From eb50bf1f48e3c4e66ca1ebd4d3a0888445b07d09 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 4 Nov 2026 16:24:06 +0100 Subject: [PATCH] Commit the build modules' publish code a bare ignore pattern hid; drop committed go.work.sum The patterns mesh-build-node and mesh-build-image matched the command directory, so publish.go and its test were never committed and the branch did not compile. Anchored to the binaries now. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig --- modules/mesh-build-go/go.work.sum | 39 ----- modules/mesh-build-image/.gitignore | 4 +- .../cmd/mesh-build-image/publish.go | 151 ++++++++++++++++++ .../cmd/mesh-build-image/publish_test.go | 124 ++++++++++++++ modules/mesh-build-node/.gitignore | 4 +- .../cmd/mesh-build-node/publish.go | 29 ++++ modules/mesh-build-node/go.work.sum | 27 ---- 7 files changed, 310 insertions(+), 68 deletions(-) delete mode 100644 modules/mesh-build-go/go.work.sum create mode 100644 modules/mesh-build-image/cmd/mesh-build-image/publish.go create mode 100644 modules/mesh-build-image/cmd/mesh-build-image/publish_test.go create mode 100644 modules/mesh-build-node/cmd/mesh-build-node/publish.go delete mode 100644 modules/mesh-build-node/go.work.sum diff --git a/modules/mesh-build-go/go.work.sum b/modules/mesh-build-go/go.work.sum deleted file mode 100644 index d8beaa0a..00000000 --- a/modules/mesh-build-go/go.work.sum +++ /dev/null @@ -1,39 +0,0 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= -github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba/go.mod h1:EFYHy8/1y2KfgTAsx7Luu7NGhoxtuVHnNo8jE7FikKc= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= -golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= -golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= -golang.org/x/mod v0.39.0/go.mod h1:bvIbwjQ0HUFFf5AKukeeYQG4ZBUG9yxQbR9aEweIwYY= -golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c= -golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o= -golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/telemetry v0.0.0-20260811182544-a038080d80e5/go.mod h1:LVehoXe41cL5SCVQilsV7Gg6BNG+Js6P9PhSbYTIUkQ= -golang.org/x/telemetry v0.0.0-20260924152758-ed294f943157/go.mod h1:i+ivNqjDnTF3WTElsdk5g9V5DTSBYgdNo7xTU9SDwYA= -golang.org/x/term v0.46.0/go.mod h1:+K02xbkittuwc0Am4abfA3Fc+XRGXkvBXNO88NCXPoc= -golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= -golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= -golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo= -golang.org/x/tools v0.51.0/go.mod h1:9eEncMayCV6zRMGhR5eZEC2iBx98qWcF1HZ9Z7wJOoA= -google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/modules/mesh-build-image/.gitignore b/modules/mesh-build-image/.gitignore index 00eb03a8..6a944359 100644 --- a/modules/mesh-build-image/.gitignore +++ b/modules/mesh-build-image/.gitignore @@ -1 +1,3 @@ -mesh-build-image +# the binary, built at the module or in its command directory; never the command directory itself +/mesh-build-image +/cmd/mesh-build-image/mesh-build-image diff --git a/modules/mesh-build-image/cmd/mesh-build-image/publish.go b/modules/mesh-build-image/cmd/mesh-build-image/publish.go new file mode 100644 index 00000000..6890142b --- /dev/null +++ b/modules/mesh-build-image/cmd/mesh-build-image/publish.go @@ -0,0 +1,151 @@ +package main + +// The publish of a trunk build (novox/hq design 51 *Publishing*, ADR 0611; CAT-BI1): after the build has ended, from +// the module's own process and not from a Dockerfile step, the image is written as an OCI layout in the step's +// workspace and published through the seat container-image-registry's verbs publish and publish-part, which the +// manifest `invokes`, with the build modules' one sender (buildstep.PublishFiles): its config, its layers and its +// manifest, by their digests, of which only the parts the holder lacks travel. This module holds no registry address and +// no credential; the holder checks the build with the builder and says published or refused on the seat's events. The +// step answers the seat's reference, container-image-registry//@sha256:. + +import ( + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + + reg "git.novox.be/novox/mesh-contracts/go/seats/containerimageregistry" + stdio "git.novox.be/novox/mesh-sdk/go" + "git.novox.be/novox/mesh-sdk/go/catalogue" + "git.novox.be/novox/mesh-sdk/go/delivery" + + "git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep" +) + +// KindImage is the kind an image is published as on its seat. +const KindImage = "image" + +// call is the seat's request: Call when a test set it, else the runtime's request over the bus (stdio.Ask). +func (m *Module) call() buildstep.SeatCall { + if m.Call != nil { + return m.Call + } + return stdio.Ask +} + +// layout is an OCI image layout one build wrote: its one manifest's digest and the files of its parts. +type layout struct { + Digest string + Files []buildstep.File +} + +type descriptor struct { + MediaType string `json:"mediaType"` + Digest string `json:"digest"` + Size int64 `json:"size"` +} + +// readLayout reads the layout in dir: index.json names exactly one manifest, which names its config and layers; every +// part is a blob of the layout, of the size its descriptor says. The manifest comes last, so a holder stores it after +// what it names. +func readLayout(dir string) (layout, error) { + var index struct { + Manifests []descriptor `json:"manifests"` + } + raw, err := os.ReadFile(filepath.Join(dir, "index.json")) + if err != nil { + return layout{}, fmt.Errorf("the image's layout has no index: %w", err) + } + if err := json.Unmarshal(raw, &index); err != nil || len(index.Manifests) != 1 { + return layout{}, fmt.Errorf("the image's layout names %d manifests, not one (%v)", len(index.Manifests), err) + } + top := index.Manifests[0] + blob := func(d descriptor) (buildstep.File, error) { + if !digestOK.MatchString(d.Digest) { + return buildstep.File{}, fmt.Errorf("%q is not a digest", d.Digest) + } + p := filepath.Join(dir, "blobs", "sha256", strings.TrimPrefix(d.Digest, "sha256:")) + info, err := os.Stat(p) + if err != nil { + return buildstep.File{}, fmt.Errorf("the layout lacks the blob %s: %w", d.Digest, err) + } + if info.Size() != d.Size { + return buildstep.File{}, fmt.Errorf("the blob %s holds %d bytes, not the %d its descriptor says", d.Digest, info.Size(), d.Size) + } + return buildstep.File{Digest: d.Digest, Size: d.Size, Path: p}, nil + } + manifest, err := blob(top) + if err != nil { + return layout{}, err + } + var body struct { + Config descriptor `json:"config"` + Layers []descriptor `json:"layers"` + } + raw, err = os.ReadFile(manifest.Path) + if err != nil || json.Unmarshal(raw, &body) != nil || body.Config.Digest == "" { + return layout{}, fmt.Errorf("the manifest %s names no config (%v)", top.Digest, err) + } + out := layout{Digest: top.Digest} + seen := map[string]bool{} + for _, d := range append([]descriptor{body.Config}, body.Layers...) { + if seen[d.Digest] { + continue + } + seen[d.Digest] = true + f, err := blob(d) + if err != nil { + return layout{}, err + } + out.Files = append(out.Files, f) + } + out.Files = append(out.Files, manifest) + return out, nil +} + +// publish writes local as an OCI layout and publishes it on the image seat as /, at the version the +// builder gave it (and the tag the call names): the step answers the layout's manifest digest, checked against the +// digest buildah wrote, and the seat's reference; a publish the holder does not take fails `not stored:`. +func (m *Module) publish(ctx context.Context, workspace, module, local string, res BuildResult, version, tag string) (any, error) { + dir := filepath.Join(m.Work, workspace, "layout."+res.Artifact) + digestFile := filepath.Join(m.Work, workspace, "pushed."+res.Artifact) + _ = os.RemoveAll(dir) + defer os.RemoveAll(dir) + argv := append(m.storeFlags(), "push", "--digestfile", digestFile, local, "oci:"+dir) + if r := m.Run(ctx, m.env(), "buildah", argv...); r.Status != 0 || r.Err != "" { + m.sayFailure("buildah push of "+res.Artifact+" to its layout", r) + return nil, fmt.Errorf("%s%s: writing its layout: %s", delivery.NotStored, res.Artifact, cause(r)) + } + img, err := readLayout(dir) + if err != nil { + return nil, fmt.Errorf("%s%s: %w", delivery.NotStored, res.Artifact, err) + } + // What buildah wrote is checked against the layout: a digest never goes out unchecked. + raw, err := os.ReadFile(digestFile) + if pushed := strings.TrimSpace(string(raw)); err != nil || pushed != img.Digest { + return nil, fmt.Errorf("%s%s: buildah wrote the digest %q (%v), but its layout's manifest is %s", delivery.NotStored, res.Artifact, pushed, err, img.Digest) + } + name := module + "/" + res.Artifact + q := KindImage + if version != "" { + q = "v" + version + } + in := reg.PublishInput{Name: name, Kind: KindImage, Version: version, Tag: tag, Digest: img.Digest, + Transfer: buildstep.TransferName(name, q, img.Digest)} + sent, err := buildstep.PublishFiles(ctx, m.call(), buildstep.ImageSeat, in, img.Files) + if err != nil { + return nil, fmt.Errorf("%s%s: %v", delivery.NotStored, res.Artifact, err) + } + m.say("published %s on %s: %d of its %d parts sent, the rest held", name, buildstep.ImageSeat, len(sent.Parts), len(img.Files)) + res.Digest, res.Pushed = img.Digest, true + res.Reference = catalogue.SeatReference(buildstep.ImageSeat, module, res.Artifact, img.Digest) + return res, nil +} + +func (m *Module) say(format string, a ...any) { + if m.Say != nil { + m.Say(format, a...) + } +} diff --git a/modules/mesh-build-image/cmd/mesh-build-image/publish_test.go b/modules/mesh-build-image/cmd/mesh-build-image/publish_test.go new file mode 100644 index 00000000..c1196cd9 --- /dev/null +++ b/modules/mesh-build-image/cmd/mesh-build-image/publish_test.go @@ -0,0 +1,124 @@ +package main + +import ( + "errors" + "os" + "path/filepath" + "slices" + "strings" + "testing" + + reg "git.novox.be/novox/mesh-contracts/go/seats/containerimageregistry" + "git.novox.be/novox/mesh-sdk/go/delivery" +) + +// 1703d/AC (ADR 0487 ยง5): a candidate's build leaves the image seat unchanged: nothing is written, nothing is asked. +func TestACandidatesBuildLeavesTheRegistryUnchanged(t *testing.T) { + m, f, states := setup(t, same) + res, st := runBuild(t, m, states, nil) // no publish + if st.State != delivery.StepPassed || res.Pushed { + t.Fatalf("step = %+v result = %+v", st, res) + } + if len(f.called("push")) != 0 || len(f.published) != 0 { + t.Errorf("a candidate's build reached the seat: published %v, calls %v", f.published, f.lines()) + } +} + +// CAT-BI1 (design 51 *Publishing*): a trunk build writes its image as an OCI layout after the build has ended and +// publishes it once through container-image-registry, as / at the ask's version, its config, layers +// and manifest by digest, sending only the parts the holder lacks; the key carries the layout's manifest digest and the +// seat's reference. A holder refusing, a digest file that disagrees and a seat other than the image seat each fail. +func TestATrunkBuildPublishesThroughTheImageSeatOnlyWhatTheHolderLacks(t *testing.T) { + for name, c := range map[string]struct { + extra map[string]any + heldLayer bool + refuse error + digestFile string + reason string + }{ + "every part sent": {extra: map[string]any{"version": "1.4.0"}}, + "the held layer is not sent": {extra: map[string]any{"version": "1.4.0"}, heldLayer: true}, + "a tag of the call's own": {extra: map[string]any{"tag": "fp-abc123"}}, + "the holder refuses": {refuse: errors.New("refused: the builder did not number it"), reason: "not stored: server: "}, + "buildah wrote another digest": {digestFile: "other", reason: "not stored: server: buildah wrote the digest"}, + "buildah wrote no digest": {digestFile: "none", reason: "not stored: server: buildah wrote the digest \"\""}, + "another seat": {extra: map[string]any{"seat": "oci-artifact-registry"}, reason: "is published through container-image-registry"}, + } { + t.Run(name, func(t *testing.T) { + m, f, states := setup(t, same) + f.refuse, f.digestFile = c.refuse, c.digestFile + args := map[string]any{"publish": true} + for k, v := range c.extra { + args[k] = v + } + if c.heldLayer { + // The layout's layer is the fake's one fixed layer: held under any name. + f.held[sha([]byte("the layer's bytes"))] = true + } + res, st := runBuild(t, m, states, args) + if c.reason != "" { + if (st.State != delivery.StepFailed && st.State != delivery.StepRefused) || !strings.Contains(st.Reason, c.reason) { + t.Fatalf("step = %+v, want failed with %q", st, c.reason) + } + return + } + if st.State != delivery.StepPassed { + t.Fatalf("step = %+v", st) + } + if !res.Pushed || res.Digest != f.pushed || res.Reference != reg.Seat+"/fixture-image/server@"+f.pushed { + t.Errorf("result = %+v, want the layout's %s", res, f.pushed) + } + if len(f.published) != 1 { + t.Fatalf("published = %+v", f.published) + } + p := f.published[0] + version, _ := c.extra["version"].(string) + tag, _ := c.extra["tag"].(string) + if p.Name != "fixture-image/server" || p.Kind != "image" || p.Digest != f.pushed || p.Version != version || p.Tag != tag || p.Transfer == "" { + t.Errorf("publish = %+v", p) + } + var parts []string + for _, x := range p.Parts { + parts = append(parts, x.Digest) + } + if !slices.Equal(parts, f.parts) { + t.Errorf("parts = %v, want config, layer, manifest %v", parts, f.parts) + } + want := f.parts + if c.heldLayer { + want = []string{f.parts[0], f.parts[2]} + } + if !slices.Equal(f.sent, want) { + t.Errorf("sent = %v, want %v", f.sent, want) + } + push := f.called("push") + if len(push) != 1 || !strings.Contains(push[0].line, " oci:"+filepath.Join(m.Work, "ws1", "layout.server")) || strings.Contains(push[0].line, "docker://") { + t.Fatalf("push = %v", push) + } + for _, bad := range []string{"--creds", "--authfile", "--sign", "--tls-verify"} { + if strings.Contains(push[0].line, bad) { + t.Errorf("the push carries %s", bad) + } + } + if _, err := os.Stat(filepath.Join(m.Work, "ws1", "layout.server")); !os.IsNotExist(err) { + t.Errorf("the layout outlived the step: %v", err) + } + // The layout is written after the build has ended: bud, then images, then push. + order := []string{} + for _, l := range f.lines() { + for _, v := range []string{" bud ", " images ", " push ", " rmi "} { + if strings.Contains(l, "buildah") && strings.Contains(l, v) { + order = append(order, strings.TrimSpace(v)) + } + } + } + if strings.Join(order, ",") != "bud,images,push,rmi" { + t.Errorf("order = %v", order) + } + }) + } + m2, f2, states2 := setup(t, same) + if _, st := runBuild(t, m2, states2, map[string]any{"publish": true, "tag": "a/b:c"}); st.State != delivery.StepFailed || len(f2.called("bud")) != 0 { + t.Errorf("a tag that is not one: step = %+v", st) + } +} diff --git a/modules/mesh-build-node/.gitignore b/modules/mesh-build-node/.gitignore index f7604aaa..ce26533a 100644 --- a/modules/mesh-build-node/.gitignore +++ b/modules/mesh-build-node/.gitignore @@ -1 +1,3 @@ -mesh-build-node +# the binary, built at the module or in its command directory; never the command directory itself +/mesh-build-node +/cmd/mesh-build-node/mesh-build-node diff --git a/modules/mesh-build-node/cmd/mesh-build-node/publish.go b/modules/mesh-build-node/cmd/mesh-build-node/publish.go new file mode 100644 index 00000000..bb3b1cec --- /dev/null +++ b/modules/mesh-build-node/cmd/mesh-build-node/publish.go @@ -0,0 +1,29 @@ +package main + +// The publish of a done build's file (novox/hq design 51 *Publishing*, ADR 0611; CAT-BN3): from this module's own +// process, after the build step ended, through a registry seat's verbs publish and publish-part, which the manifest +// `invokes`, with the build modules' one sender (buildstep.PublishOne): a bundle's archive on oci-artifact-registry, a +// trunk package on npm-package-registry under the version the builder gave it. No build step reaches a store, and this +// module holds no write credential: the holder checks the build with the builder and says published or refused. + +import ( + "context" + + stdio "git.novox.be/novox/mesh-sdk/go" + + "git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep" +) + +// publishOn publishes body, of digest, as name (at version, when it has one) of kind on seat: through Call when a +// test set it, else the runtime's request over the bus (stdio.Ask). +func (m *Module) publishOn(ctx context.Context, seat, name, kind, version, digest string, body []byte) error { + call := m.Call + if call == nil { + call = stdio.Ask + } + q := kind + if version != "" { + q = "v" + version + } + return buildstep.PublishOne(ctx, call, seat, name, kind, version, digest, buildstep.TransferName(name, q, digest), body) +} diff --git a/modules/mesh-build-node/go.work.sum b/modules/mesh-build-node/go.work.sum deleted file mode 100644 index d2da1916..00000000 --- a/modules/mesh-build-node/go.work.sum +++ /dev/null @@ -1,27 +0,0 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= -golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= -golang.org/x/mod v0.39.0/go.mod h1:bvIbwjQ0HUFFf5AKukeeYQG4ZBUG9yxQbR9aEweIwYY= -golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o= -golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/telemetry v0.0.0-20260811182544-a038080d80e5/go.mod h1:LVehoXe41cL5SCVQilsV7Gg6BNG+Js6P9PhSbYTIUkQ= -golang.org/x/term v0.46.0/go.mod h1:+K02xbkittuwc0Am4abfA3Fc+XRGXkvBXNO88NCXPoc= -golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= -golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= -golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo= -google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM=