Narrow both build modules' fetch with mesh-sdk's buildstep.Narrow, the one copy (issue 1758, ADR 0499 §4)
The local narrowing of mesh-build-go and mesh-build-node moves into mesh-sdk (feat/r4e-sdk d1af87a), with its tests; each module keeps its fetch-level test on real git. The three build modules are pinned to d1af87a.
This commit is contained in:
@@ -84,7 +84,7 @@ func (m *Module) fetchTool() StepTool {
|
||||
|
||||
func (m *Module) fetch(ctx context.Context, args map[string]any) (any, error) {
|
||||
// What the builder and the checker ask, in mesh-sdk's one form (ADR 0499 §4): with the module's directory and its
|
||||
// named paths, the workspace is narrowed to them (narrow.go).
|
||||
// named paths, the workspace is narrowed to them (buildstep.Narrow).
|
||||
req, err := buildstep.ParseFetchRequest(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -115,7 +115,7 @@ func (m *Module) fetch(ctx context.Context, args map[string]any) (any, error) {
|
||||
return nil, err
|
||||
}
|
||||
if (base == "" || res.Merge != "") && len(req.Paths) > 0 {
|
||||
if err := narrow(tree, req); err != nil {
|
||||
if err := buildstep.Narrow(tree, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Said beside the tree, so the steps that list the tree's go.mod files leave out those narrowed away.
|
||||
@@ -187,7 +187,7 @@ func (m *Module) goModDirs(ctx context.Context, tree string) ([]string, error) {
|
||||
if l = strings.TrimSpace(l); l != "" && !inTestdata(l) {
|
||||
if narrowed(tree) {
|
||||
if _, err := os.Stat(filepath.Join(tree, filepath.FromSlash(l))); err != nil {
|
||||
continue // outside a narrowed workspace's paths (narrow.go): no module of this build
|
||||
continue // outside a narrowed workspace's paths (buildstep.Narrow): no module of this build
|
||||
}
|
||||
}
|
||||
dirs = append(dirs, filepath.ToSlash(filepath.Dir(l)))
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
package main
|
||||
|
||||
// The narrowed workspace (novox/hq ADR 0499 §4; issue 1758, the lead's decision L8): a build of a module whose every
|
||||
// artifact names its paths sees only those paths, the module's manifest and its lock files. The paths come from the
|
||||
// builder's request (buildstep.FetchRequest's Dir and Paths, from the indexer's kept inputs), never from a manifest.
|
||||
// Everything else of the fetched tree is removed before any run step, so a read outside fails, naming the path.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/buildstep"
|
||||
)
|
||||
|
||||
// narrowKept are the files of the module's directory a narrowed workspace keeps beside its paths: the manifest and the
|
||||
// lock files of both languages.
|
||||
var narrowKept = []string{"module.json", "package.json", "package-lock.json", "go.mod", "go.sum"}
|
||||
|
||||
// narrow removes from tree everything but r's paths in its module directory, the manifest and the lock files (and the
|
||||
// repository's .git). A request naming no paths leaves the tree whole. A named path the tree lacks fails, naming it.
|
||||
func narrow(tree string, r buildstep.FetchRequest) error {
|
||||
if len(r.Paths) == 0 {
|
||||
return nil
|
||||
}
|
||||
dir := path.Clean(r.Dir)
|
||||
keep := map[string]bool{}
|
||||
for _, p := range r.Paths {
|
||||
rel := path.Join(dir, path.Clean(p))
|
||||
if _, err := os.Lstat(filepath.Join(tree, filepath.FromSlash(rel))); err != nil {
|
||||
return fmt.Errorf("the path %s the request names is not in the commit: %w", rel, err)
|
||||
}
|
||||
keep[rel] = true
|
||||
}
|
||||
for _, f := range narrowKept {
|
||||
keep[path.Join(dir, f)] = true
|
||||
}
|
||||
return prune(tree, "", keep)
|
||||
}
|
||||
|
||||
// prune removes under tree/rel everything that is neither kept nor above or below a kept path.
|
||||
func prune(tree, rel string, keep map[string]bool) error {
|
||||
entries, err := os.ReadDir(filepath.Join(tree, filepath.FromSlash(rel)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
names := make([]string, 0, len(entries))
|
||||
for _, e := range entries {
|
||||
names = append(names, e.Name())
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
p := path.Join(rel, name)
|
||||
if rel == "" && name == ".git" {
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case keep[p]:
|
||||
continue // a kept path, whole
|
||||
case holdsKept(p, keep):
|
||||
if err := prune(tree, p, keep); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
if err := os.RemoveAll(filepath.Join(tree, filepath.FromSlash(p))); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// holdsKept says whether a kept path lies below p.
|
||||
func holdsKept(p string, keep map[string]bool) bool {
|
||||
for k := range keep {
|
||||
if len(k) > len(p) && k[:len(p)+1] == p+"/" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/buildstep"
|
||||
)
|
||||
|
||||
func files(t *testing.T, root string) []string {
|
||||
t.Helper()
|
||||
var out []string
|
||||
filepath.WalkDir(root, func(p string, d os.DirEntry, err error) error {
|
||||
if err == nil && !d.IsDir() {
|
||||
rel, _ := filepath.Rel(root, p)
|
||||
if !strings.HasPrefix(rel, ".git/") {
|
||||
out = append(out, filepath.ToSlash(rel))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
slices.Sort(out)
|
||||
return out
|
||||
}
|
||||
|
||||
func narrowFixture(t *testing.T) string {
|
||||
t.Helper()
|
||||
tree := t.TempDir()
|
||||
for _, f := range []string{".git/HEAD", "README.md", "module.json", "package.json", "package-lock.json", "tsconfig.json",
|
||||
"src/index.ts", "src/deep/a.ts", "test/a.test.ts", "go/go.mod", "go/x.go", "other/module.json", "srcx/b.ts"} {
|
||||
p := filepath.Join(tree, filepath.FromSlash(f))
|
||||
if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(p, []byte(f), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
return tree
|
||||
}
|
||||
|
||||
// ADR 0499 §4: a request naming paths leaves only those, the manifest and the lock files (and .git); a path the commit
|
||||
// lacks fails naming it; a request naming none leaves the tree whole.
|
||||
func TestTheWorkspaceIsNarrowedToTheNamedPathsTheManifestAndTheLocks(t *testing.T) {
|
||||
tree := narrowFixture(t)
|
||||
r := buildstep.FetchRequest{Dir: ".", Paths: []string{"src", "test", "tsconfig.json"}}
|
||||
if err := narrow(tree, r); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := []string{"module.json", "package-lock.json", "package.json", "src/deep/a.ts", "src/index.ts", "test/a.test.ts", "tsconfig.json"}
|
||||
if got := files(t, tree); !slices.Equal(got, want) {
|
||||
t.Errorf("kept %v, want %v", got, want)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(tree, ".git", "HEAD")); err != nil {
|
||||
t.Errorf("the repository's .git went: %v", err)
|
||||
}
|
||||
tree = narrowFixture(t)
|
||||
if err := narrow(tree, buildstep.FetchRequest{Dir: "go", Paths: []string{"x.go"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := files(t, tree); !slices.Equal(got, []string{"go/go.mod", "go/x.go"}) {
|
||||
t.Errorf("a module in a directory kept %v", got)
|
||||
}
|
||||
tree = narrowFixture(t)
|
||||
if err := narrow(tree, buildstep.FetchRequest{Dir: ".", Paths: []string{"lib"}}); err == nil || !strings.Contains(err.Error(), "lib") {
|
||||
t.Errorf("a path the commit lacks answered %v", err)
|
||||
}
|
||||
tree = narrowFixture(t)
|
||||
before := files(t, tree)
|
||||
if err := narrow(tree, buildstep.FetchRequest{}); err != nil || !slices.Equal(files(t, tree), before) {
|
||||
t.Errorf("a request naming no paths changed the tree: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ module meshbuildgo
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261022160024-9cb4d59093a7
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261022163107-d1af87a83a52
|
||||
github.com/distribution/distribution/v3 v3.1.2
|
||||
github.com/nats-io/nats-server/v2 v2.11.17
|
||||
github.com/nats-io/nats.go v1.54.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261022160024-9cb4d59093a7 h1:wK9O1CgNk7mo3IdJqW/CCx/hebg+1F0rkvW8CvJfnRM=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261022160024-9cb4d59093a7/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261022163107-d1af87a83a52 h1:5k04XjpfYCq6j7rCM6Dy6FfrsBk5iUJlbwGf6WhUOUc=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261022163107-d1af87a83a52/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21jeqDCONI=
|
||||
github.com/alicebob/miniredis/v2 v2.35.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
|
||||
|
||||
@@ -3,7 +3,7 @@ module meshbuildimage
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261022160024-9cb4d59093a7
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261022163107-d1af87a83a52
|
||||
github.com/nats-io/nats-server/v2 v2.11.17
|
||||
github.com/nats-io/nats.go v1.54.0
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261022160024-9cb4d59093a7 h1:wK9O1CgNk7mo3IdJqW/CCx/hebg+1F0rkvW8CvJfnRM=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261022160024-9cb4d59093a7/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261022163107-d1af87a83a52 h1:5k04XjpfYCq6j7rCM6Dy6FfrsBk5iUJlbwGf6WhUOUc=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261022163107-d1af87a83a52/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
|
||||
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
|
||||
|
||||
@@ -107,7 +107,7 @@ func (m *Module) fetchTool() StepTool {
|
||||
|
||||
func (m *Module) fetch(ctx context.Context, args map[string]any) (any, error) {
|
||||
// What the builder and the checker ask, in mesh-sdk's one form (ADR 0499 §4): with the module's directory and its
|
||||
// named paths, the workspace is narrowed to them (narrow.go).
|
||||
// named paths, the workspace is narrowed to them (buildstep.Narrow).
|
||||
req, err := buildstep.ParseFetchRequest(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -142,7 +142,7 @@ func (m *Module) fetch(ctx context.Context, args map[string]any) (any, error) {
|
||||
}
|
||||
}
|
||||
if len(req.Paths) > 0 {
|
||||
if err := narrow(tree, req); err != nil {
|
||||
if err := buildstep.Narrow(tree, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Said beside the tree, so the listing of the tree's modules leaves out those narrowed away.
|
||||
@@ -208,7 +208,7 @@ func (m *Module) typeScriptModules(ctx context.Context, tree string) (dirs []str
|
||||
}
|
||||
raw, err := os.ReadFile(filepath.Join(tree, filepath.FromSlash(l)))
|
||||
if os.IsNotExist(err) && narrowed(tree) {
|
||||
continue // outside a narrowed workspace's paths (narrow.go): no module of this build
|
||||
continue // outside a narrowed workspace's paths (buildstep.Narrow): no module of this build
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
package main
|
||||
|
||||
// The narrowed workspace (novox/hq ADR 0499 §4; issue 1758, the lead's decision L8): a build of a module whose every
|
||||
// artifact names its paths sees only those paths, the module's manifest and its lock files. The paths come from the
|
||||
// builder's request (buildstep.FetchRequest's Dir and Paths, from the indexer's kept inputs), never from a manifest.
|
||||
// Everything else of the fetched tree is removed before any run step, so a read outside fails, naming the path.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/buildstep"
|
||||
)
|
||||
|
||||
// narrowKept are the files of the module's directory a narrowed workspace keeps beside its paths: the manifest and the
|
||||
// lock files of both languages.
|
||||
var narrowKept = []string{"module.json", "package.json", "package-lock.json", "go.mod", "go.sum"}
|
||||
|
||||
// narrow removes from tree everything but r's paths in its module directory, the manifest and the lock files (and the
|
||||
// repository's .git). A request naming no paths leaves the tree whole. A named path the tree lacks fails, naming it.
|
||||
func narrow(tree string, r buildstep.FetchRequest) error {
|
||||
if len(r.Paths) == 0 {
|
||||
return nil
|
||||
}
|
||||
dir := path.Clean(r.Dir)
|
||||
keep := map[string]bool{}
|
||||
for _, p := range r.Paths {
|
||||
rel := path.Join(dir, path.Clean(p))
|
||||
if _, err := os.Lstat(filepath.Join(tree, filepath.FromSlash(rel))); err != nil {
|
||||
return fmt.Errorf("the path %s the request names is not in the commit: %w", rel, err)
|
||||
}
|
||||
keep[rel] = true
|
||||
}
|
||||
for _, f := range narrowKept {
|
||||
keep[path.Join(dir, f)] = true
|
||||
}
|
||||
return prune(tree, "", keep)
|
||||
}
|
||||
|
||||
// prune removes under tree/rel everything that is neither kept nor above or below a kept path.
|
||||
func prune(tree, rel string, keep map[string]bool) error {
|
||||
entries, err := os.ReadDir(filepath.Join(tree, filepath.FromSlash(rel)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
names := make([]string, 0, len(entries))
|
||||
for _, e := range entries {
|
||||
names = append(names, e.Name())
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
p := path.Join(rel, name)
|
||||
if rel == "" && name == ".git" {
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case keep[p]:
|
||||
continue // a kept path, whole
|
||||
case holdsKept(p, keep):
|
||||
if err := prune(tree, p, keep); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
if err := os.RemoveAll(filepath.Join(tree, filepath.FromSlash(p))); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// holdsKept says whether a kept path lies below p.
|
||||
func holdsKept(p string, keep map[string]bool) bool {
|
||||
for k := range keep {
|
||||
if len(k) > len(p) && k[:len(p)+1] == p+"/" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/buildstep"
|
||||
)
|
||||
|
||||
func files(t *testing.T, root string) []string {
|
||||
t.Helper()
|
||||
var out []string
|
||||
filepath.WalkDir(root, func(p string, d os.DirEntry, err error) error {
|
||||
if err == nil && !d.IsDir() {
|
||||
rel, _ := filepath.Rel(root, p)
|
||||
if !strings.HasPrefix(rel, ".git/") {
|
||||
out = append(out, filepath.ToSlash(rel))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
slices.Sort(out)
|
||||
return out
|
||||
}
|
||||
|
||||
func narrowFixture(t *testing.T) string {
|
||||
t.Helper()
|
||||
tree := t.TempDir()
|
||||
for _, f := range []string{".git/HEAD", "README.md", "module.json", "package.json", "package-lock.json", "tsconfig.json",
|
||||
"src/index.ts", "src/deep/a.ts", "test/a.test.ts", "go/go.mod", "go/x.go", "other/module.json", "srcx/b.ts"} {
|
||||
p := filepath.Join(tree, filepath.FromSlash(f))
|
||||
if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(p, []byte(f), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
return tree
|
||||
}
|
||||
|
||||
// ADR 0499 §4: a request naming paths leaves only those, the manifest and the lock files (and .git); a path the commit
|
||||
// lacks fails naming it; a request naming none leaves the tree whole.
|
||||
func TestTheWorkspaceIsNarrowedToTheNamedPathsTheManifestAndTheLocks(t *testing.T) {
|
||||
tree := narrowFixture(t)
|
||||
r := buildstep.FetchRequest{Dir: ".", Paths: []string{"src", "test", "tsconfig.json"}}
|
||||
if err := narrow(tree, r); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := []string{"module.json", "package-lock.json", "package.json", "src/deep/a.ts", "src/index.ts", "test/a.test.ts", "tsconfig.json"}
|
||||
if got := files(t, tree); !slices.Equal(got, want) {
|
||||
t.Errorf("kept %v, want %v", got, want)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(tree, ".git", "HEAD")); err != nil {
|
||||
t.Errorf("the repository's .git went: %v", err)
|
||||
}
|
||||
tree = narrowFixture(t)
|
||||
if err := narrow(tree, buildstep.FetchRequest{Dir: "go", Paths: []string{"x.go"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := files(t, tree); !slices.Equal(got, []string{"go/go.mod", "go/x.go"}) {
|
||||
t.Errorf("a module in a directory kept %v", got)
|
||||
}
|
||||
tree = narrowFixture(t)
|
||||
if err := narrow(tree, buildstep.FetchRequest{Dir: ".", Paths: []string{"lib"}}); err == nil || !strings.Contains(err.Error(), "lib") {
|
||||
t.Errorf("a path the commit lacks answered %v", err)
|
||||
}
|
||||
tree = narrowFixture(t)
|
||||
before := files(t, tree)
|
||||
if err := narrow(tree, buildstep.FetchRequest{}); err != nil || !slices.Equal(files(t, tree), before) {
|
||||
t.Errorf("a request naming no paths changed the tree: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ module meshbuildnode
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261022160024-9cb4d59093a7
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261022163107-d1af87a83a52
|
||||
github.com/distribution/distribution/v3 v3.1.2
|
||||
github.com/nats-io/nats.go v1.54.0
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261022160024-9cb4d59093a7 h1:wK9O1CgNk7mo3IdJqW/CCx/hebg+1F0rkvW8CvJfnRM=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261022160024-9cb4d59093a7/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261022163107-d1af87a83a52 h1:5k04XjpfYCq6j7rCM6Dy6FfrsBk5iUJlbwGf6WhUOUc=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261022163107-d1af87a83a52/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21jeqDCONI=
|
||||
github.com/alicebob/miniredis/v2 v2.35.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
|
||||
|
||||
Reference in New Issue
Block a user