Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
831f34e59a | ||
|
|
86be014907 | ||
|
|
06358f94bd |
@@ -10,9 +10,6 @@
|
|||||||
// (`mesh-controller.<name>`, `seat:mesh-controller.<verb>`, `mesh-controller_<bucket>`), anywhere in the manifest;
|
// (`mesh-controller.<name>`, `seat:mesh-controller.<verb>`, `mesh-controller_<bucket>`), anywhere in the manifest;
|
||||||
// - a manifest string that is the bare module name `mesh-controller` outside the module's own `config`, where a value
|
// - a manifest string that is the bare module name `mesh-controller` outside the module's own `config`, where a value
|
||||||
// is the module's data (an issue owner naming the controller's repository is no dependency), not an address;
|
// is the module's data (an issue owner naming the controller's repository is no dependency), not an address;
|
||||||
// - a go.mod of a module or of a check requiring the controller's repository (its module path, github.com/novox/mesh-controller,
|
|
||||||
// or the forge's git.novox.be/novox/mesh-controller): the repository is archived once its last module is delivered
|
|
||||||
// elsewhere (novox/hq ADR 0593 §11), so nothing here may be built from it;
|
|
||||||
// - a line of any module's file naming one of the controller's buckets (`mesh-controller_`): its buckets are removed
|
// - a line of any module's file naming one of the controller's buckets (`mesh-controller_`): its buckets are removed
|
||||||
// after the controller's end (ADR 0590 §6), so a reader of one reads nothing. A test (`_test.go`, `.test.ts`) reads
|
// after the controller's end (ADR 0590 §6), so a reader of one reads nothing. A test (`_test.go`, `.test.ts`) reads
|
||||||
// no live bucket, its names are its own data, and a `testdata` directory is a copy of text from elsewhere (a design,
|
// no live bucket, its names are its own data, and a `testdata` directory is a copy of text from elsewhere (a design,
|
||||||
@@ -43,9 +40,6 @@ const (
|
|||||||
// address is a string naming the controller's seat, one of its states, verbs or events, or one of its buckets.
|
// address is a string naming the controller's seat, one of its states, verbs or events, or one of its buckets.
|
||||||
var address = regexp.MustCompile(`(^|[^A-Za-z0-9_-])(seat:)?mesh-controller([._][A-Za-z0-9])`)
|
var address = regexp.MustCompile(`(^|[^A-Za-z0-9_-])(seat:)?mesh-controller([._][A-Za-z0-9])`)
|
||||||
|
|
||||||
// repository is a go.mod line naming the controller's repository by either of its module paths.
|
|
||||||
var repository = regexp.MustCompile(`(github\.com|git\.novox\.be)/novox/mesh-controller(/|\s|$)`)
|
|
||||||
|
|
||||||
// Refusals is each reason the check gives over the tree at root, sorted; none when it passes.
|
// Refusals is each reason the check gives over the tree at root, sorted; none when it passes.
|
||||||
func Refusals(root string) ([]string, error) {
|
func Refusals(root string) ([]string, error) {
|
||||||
var out []string
|
var out []string
|
||||||
@@ -71,26 +65,6 @@ func Refusals(root string) ([]string, error) {
|
|||||||
"consumes, waits on or uses it; name the seat that holds the job now (novox/hq ADR 0590 §5)", module, s))
|
"consumes, waits on or uses it; name the seat that holds the job now (novox/hq ADR 0590 §5)", module, s))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mods, err := filepath.Glob(filepath.Join(root, "*", "*", "go.mod"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
sort.Strings(mods)
|
|
||||||
for _, f := range mods {
|
|
||||||
if rel, _ := filepath.Rel(root, f); !strings.HasPrefix(rel, "modules"+string(filepath.Separator)) && !strings.HasPrefix(rel, "checks"+string(filepath.Separator)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
raw, err := os.ReadFile(f)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for n, l := range strings.Split(string(raw), "\n") {
|
|
||||||
if repository.MatchString(l) {
|
|
||||||
rel, _ := filepath.Rel(root, f)
|
|
||||||
out = append(out, fmt.Sprintf("%s:%d: requires the controller's repository: it is archived once its last module is built elsewhere, so nothing here is built from it (novox/hq ADR 0593 §11)", filepath.ToSlash(rel), n+1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buckets, err := bucketLines(root)
|
buckets, err := bucketLines(root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -33,9 +33,6 @@ func TestRefusalsJudgeATree(t *testing.T) {
|
|||||||
{"a requirement", map[string]string{"modules/d/module.json": `{"module":"d","requires":[{"name":"x","from":"mesh-controller"}]}`}, `d: names "mesh-controller"`},
|
{"a requirement", map[string]string{"modules/d/module.json": `{"module":"d","requires":[{"name":"x","from":"mesh-controller"}]}`}, `d: names "mesh-controller"`},
|
||||||
{"the controller's own module", map[string]string{"modules/mesh-controller/module.json": `{"module":"mesh-controller"}`}, "mesh-controller: the controller is no module of the catalogue"},
|
{"the controller's own module", map[string]string{"modules/mesh-controller/module.json": `{"module":"mesh-controller"}`}, "mesh-controller: the controller is no module of the catalogue"},
|
||||||
{"a bucket in code", map[string]string{"modules/e/cmd/e/main.go": "package main\nconst b = \"mesh-controller_assignments\"\n"}, "modules/e/cmd/e/main.go:2: names the controller's bucket"},
|
{"a bucket in code", map[string]string{"modules/e/cmd/e/main.go": "package main\nconst b = \"mesh-controller_assignments\"\n"}, "modules/e/cmd/e/main.go:2: names the controller's bucket"},
|
||||||
{"a requirement of the controller's repository", map[string]string{"modules/h/go.mod": "module h\n\nrequire github.com/novox/mesh-controller v0.0.0\n"}, "modules/h/go.mod:3: requires the controller's repository"},
|
|
||||||
{"its forge path in a check", map[string]string{"checks/i/go.mod": "module i\n\nrequire (\n\tgit.novox.be/novox/mesh-controller/modules/route-proxy v0.1.0\n)\n"}, "checks/i/go.mod:4"},
|
|
||||||
{"another repository of the forge passes", map[string]string{"modules/j/go.mod": "module j\n\nrequire git.novox.be/novox/mesh-sdk/go v0.1.0\n"}, ""},
|
|
||||||
{"a bucket in a test is its data", map[string]string{"modules/e/cmd/e/main_test.go": "package main\n// mesh-controller_lease\n"}, ""},
|
{"a bucket in a test is its data", map[string]string{"modules/e/cmd/e/main_test.go": "package main\n// mesh-controller_lease\n"}, ""},
|
||||||
{"a fixture copied from elsewhere", map[string]string{"modules/e/cmd/e/testdata/x.md": "mesh-controller_rollouts\n"}, ""},
|
{"a fixture copied from elsewhere", map[string]string{"modules/e/cmd/e/testdata/x.md": "mesh-controller_rollouts\n"}, ""},
|
||||||
{"an owner and prose pass", map[string]string{"modules/f/module.json": `{"module":"f","config":{"owners":{"default":["mesh-controller","mesh-controller modules/route-proxy"]}},"why":"the mesh-controller module is gone"}`}, ""},
|
{"an owner and prose pass", map[string]string{"modules/f/module.json": `{"module":"f","config":{"owners":{"default":["mesh-controller","mesh-controller modules/route-proxy"]}},"why":"the mesh-controller module is gone"}`}, ""},
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ module checks/credentialform
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
|
|||||||
+2
-13
@@ -111,24 +111,13 @@ func forgeRequirements(dir string) ([]requirement, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("its go.mod cannot be read: %s", lastLine(out))
|
return nil, fmt.Errorf("its go.mod cannot be read: %s", lastLine(out))
|
||||||
}
|
}
|
||||||
var mod struct {
|
var mod struct{ Require []requirement }
|
||||||
Require []requirement
|
|
||||||
Replace []struct{ Old, New requirement }
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(out, &mod); err != nil {
|
if err := json.Unmarshal(out, &mod); err != nil {
|
||||||
return nil, fmt.Errorf("its go.mod cannot be read: %w", err)
|
return nil, fmt.Errorf("its go.mod cannot be read: %w", err)
|
||||||
}
|
}
|
||||||
// A requirement the go.mod replaces to a directory is a package of the module's own repository (novox/hq ADR 0593 §1): it
|
|
||||||
// is in the tree under check, so it is fetched from nowhere.
|
|
||||||
local := map[string]bool{}
|
|
||||||
for _, r := range mod.Replace {
|
|
||||||
if r.New.Version == "" {
|
|
||||||
local[r.Old.Path] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var on []requirement
|
var on []requirement
|
||||||
for _, r := range mod.Require {
|
for _, r := range mod.Require {
|
||||||
if strings.HasPrefix(r.Path, forge) && !local[r.Path] {
|
if strings.HasPrefix(r.Path, forge) {
|
||||||
on = append(on, r)
|
on = append(on, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -175,28 +175,3 @@ func TestTheRefOfAVersion(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ADR 0593 §1: a module requires a sibling package of its own repository and replaces it to the directory beside it; that
|
|
||||||
// package is no fetch from the forge (it is in the tree under check), so it is never looked for in a clone beside the check,
|
|
||||||
// and the module builds with the SDK taken from there.
|
|
||||||
func TestASiblingPackageReplacedToItsDirectoryIsNotFetched(t *testing.T) {
|
|
||||||
offline(t)
|
|
||||||
beside, commit := sdkBeside(t)
|
|
||||||
version := "v0.1.14-0.20261012082113-" + commit[:12]
|
|
||||||
root := t.TempDir()
|
|
||||||
sibling := "git.novox.be/novox/mesh-catalog/modules/builder/runner"
|
|
||||||
write(t, filepath.Join(root, "builder", "runner", "go.mod"), "module "+sibling+"\n\ngo 1.22\n\nrequire git.novox.be/novox/mesh-sdk/go "+version+"\n")
|
|
||||||
write(t, filepath.Join(root, "builder", "runner", "run.go"), "package runner\n\nimport \"git.novox.be/novox/mesh-sdk/go/sdk\"\n\nfunc Run() string { return sdk.Hello() }\n")
|
|
||||||
dir := filepath.Join(root, "user")
|
|
||||||
write(t, filepath.Join(dir, "go.mod"), "module git.novox.be/novox/mesh-catalog/modules/user\n\ngo 1.22\n\nrequire (\n\t"+sibling+" v0.0.0\n"+
|
|
||||||
"\tgit.novox.be/novox/mesh-sdk/go "+version+"\n)\n\nreplace "+sibling+" => ../builder/runner\n")
|
|
||||||
write(t, filepath.Join(dir, "main.go"), "package main\n\nimport \""+sibling+"\"\n\nfunc main() { println(runner.Run()) }\n")
|
|
||||||
modfile, err := resolve(dir, beside, t.TempDir())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("resolve: %v", err)
|
|
||||||
}
|
|
||||||
if modfile == "" {
|
|
||||||
t.Fatal("resolve said the module fetches its dependencies, with nothing to fetch the SDK from")
|
|
||||||
}
|
|
||||||
builds(t, dir, modfile)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Package failurekit refuses a build module whose tests do not run the build runner's conformance kit (issue 1901, its thorough
|
// Package failurekit refuses a build module whose tests do not run mesh-sdk's conformance kit (issue 1901, its thorough
|
||||||
// fix): a module that claims a node-build-* seat and serves a test step must hold a test that calls conformance.Run, so
|
// fix): a module that claims a node-build-* seat and serves a test step must hold a test that calls conformance.Run, so
|
||||||
// its parse of the test runner's output is held to the failure contract (a failing test named with its cause, never a
|
// its parse of the test runner's output is held to the failure contract (a failing test named with its cause, never a
|
||||||
// passing line in its place). A build module with no test step (mesh-build-image builds images and runs no tests) is
|
// passing line in its place). A build module with no test step (mesh-build-image builds images and runs no tests) is
|
||||||
@@ -71,7 +71,7 @@ func Judge(root string) ([]Judged, error) {
|
|||||||
}
|
}
|
||||||
if !runs {
|
if !runs {
|
||||||
refused = append(refused, fmt.Sprintf("it claims %s and serves the test step %s, and no test of it calls conformance.Run "+
|
refused = append(refused, fmt.Sprintf("it claims %s and serves the test step %s, and no test of it calls conformance.Run "+
|
||||||
"(mesh-build-go buildstep/conformance): run the kit with the module's own parse path (issue 1901)", seat, test))
|
"(mesh-sdk go/buildstep/conformance): run the kit with the module's own parse path (issue 1901)", seat, test))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Every build module keeps steps, so every one runs the left-running case, test step or not (issue 1890).
|
// Every build module keeps steps, so every one runs the left-running case, test step or not (issue 1890).
|
||||||
@@ -81,7 +81,7 @@ func Judge(root string) ([]Judged, error) {
|
|||||||
}
|
}
|
||||||
if !sweeps {
|
if !sweeps {
|
||||||
refused = append(refused, fmt.Sprintf("it claims %s, and no test of it calls conformance.LeftRunning "+
|
refused = append(refused, fmt.Sprintf("it claims %s, and no test of it calls conformance.LeftRunning "+
|
||||||
"(mesh-build-go buildstep/conformance): run the kit's left-running case with the module's own start sweep (issue 1890)", seat))
|
"(mesh-sdk go/buildstep/conformance): run the kit's left-running case with the module's own start sweep (issue 1890)", seat))
|
||||||
}
|
}
|
||||||
j.Refused = strings.Join(refused, "; ")
|
j.Refused = strings.Join(refused, "; ")
|
||||||
out = append(out, j)
|
out = append(out, j)
|
||||||
@@ -121,7 +121,7 @@ func runsTheKit(dir, call string) (bool, error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
found = strings.Contains(string(raw), call) &&
|
found = strings.Contains(string(raw), call) &&
|
||||||
strings.Contains(string(raw), `"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep/conformance"`)
|
strings.Contains(string(raw), `"git.novox.be/novox/mesh-sdk/go/buildstep/conformance"`)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
return found, err
|
return found, err
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
// cases is refused, and a module that claims no node-build-* seat is not judged.
|
// cases is refused, and a module that claims no node-build-* seat is not judged.
|
||||||
func TestABuildModuleWithATestStepMustRunTheKit(t *testing.T) {
|
func TestABuildModuleWithATestStepMustRunTheKit(t *testing.T) {
|
||||||
root := t.TempDir()
|
root := t.TempDir()
|
||||||
const imp = "package main\n\nimport \"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep/conformance\"\n\n"
|
const imp = "package main\n\nimport \"git.novox.be/novox/mesh-sdk/go/buildstep/conformance\"\n\n"
|
||||||
const sweep = "func TestSweep(t *testing.T) { conformance.LeftRunning(t, nil) }\n"
|
const sweep = "func TestSweep(t *testing.T) { conformance.LeftRunning(t, nil) }\n"
|
||||||
kit := imp + "func TestKit(t *testing.T) { conformance.Run(t, conformance.GoTest, nil) }\n" + sweep
|
kit := imp + "func TestKit(t *testing.T) { conformance.Run(t, conformance.GoTest, nil) }\n" + sweep
|
||||||
for path, body := range map[string]string{
|
for path, body := range map[string]string{
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ module checks/health
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ module checks/manifests
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ module checks/readable
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
module git.novox.be/novox/mesh-catalog/checks/removedpackages
|
|
||||||
|
|
||||||
go 1.22
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
// Command removedpackages refuses a Go source of the catalogue that imports what mesh-sdk gave up in the release that
|
|
||||||
// narrowed it (novox/hq ADR 0593 §1): a package that left for its owner, and the build runner's names that left
|
|
||||||
// mesh-sdk's buildstep for mesh-build-go's package.
|
|
||||||
//
|
|
||||||
// A module compiles against the mesh-sdk version it pins, so a source that still names a removed package builds until the
|
|
||||||
// pin moves, and then breaks in a delivery of its own; and a later change could bring the import back with the old pin.
|
|
||||||
// This check refuses it at once, from the source alone:
|
|
||||||
//
|
|
||||||
// - an import of a package in Removed (or one under it);
|
|
||||||
// - in a file importing mesh-sdk's buildstep (under any name), a selector naming anything that is not in
|
|
||||||
// BuildstepContract: what is left of that package is what the delivery parts read, and the runner is
|
|
||||||
// mesh-build-go's package (git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep).
|
|
||||||
//
|
|
||||||
// It reads the non-test and test Go files under modules/ and checks/ (testdata, vendor and node_modules left out).
|
|
||||||
//
|
|
||||||
// go run . <catalogue root>
|
|
||||||
//
|
|
||||||
// Exits 1 on a finding. The lists are the release's: a package that goes later is added here with the change.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"go/ast"
|
|
||||||
"go/parser"
|
|
||||||
"go/token"
|
|
||||||
"io/fs"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const sdk = "git.novox.be/novox/mesh-sdk/go"
|
|
||||||
|
|
||||||
// Removed are the packages of mesh-sdk that left it, each with where its logic is now.
|
|
||||||
var Removed = map[string]string{
|
|
||||||
"buildcpu": "the build runner's package, git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep/buildcpu",
|
|
||||||
"buildstep/conformance": "the build runner's package, git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep/conformance",
|
|
||||||
"delivery/buildrun": "the builder's package (mesh-delivery-builder), imported by its repository's modules",
|
|
||||||
"compose": "the node-engine, which alone composes (ADR 0593 §6)",
|
|
||||||
"compose/frombus": "the node-engine, which alone composes (ADR 0593 §6)",
|
|
||||||
"compose/fromruntime": "the node-engine, which alone composes (ADR 0593 §6)",
|
|
||||||
"compose/judge": "the assigner's own package",
|
|
||||||
"takein": "the node-engine, which alone composes (ADR 0593 §6)",
|
|
||||||
"validate": "the node-engine, which alone composes (ADR 0593 §6)",
|
|
||||||
"facts": "nothing: the controller they served is ended (ADR 0590)",
|
|
||||||
"example": "nothing: no module imports it (ADR 0593 §12)",
|
|
||||||
"packageguard": "nothing: a test helper of mesh-sdk's own",
|
|
||||||
}
|
|
||||||
|
|
||||||
// BuildstepContract are the names mesh-sdk's buildstep keeps: the failure contract, the fetch request, the stale-step sweep,
|
|
||||||
// the step id and the module tree shape the delivery parts read.
|
|
||||||
var BuildstepContract = map[string]bool{
|
|
||||||
"AskWhat": true, "Boot": true, "By": true, "CapacitySetting": true, "Clip": true, "ConsoleFailure": true, "CutLine": true,
|
|
||||||
"EndOne": true, "EndStale": true, "EndStaleAtStart": true, "EndStaleRound": true, "Failure": true, "FetchRequest": true,
|
|
||||||
"FetchRequestProperties": true, "Hidden": true, "Launchers": true, "LauncherSuffix": true, "LineBytes": true,
|
|
||||||
"ModuleTree": true, "NewID": true, "OutputBytes": true, "Owner": true, "PackagesArg": true, "ParseFetchRequest": true,
|
|
||||||
"PassingLine": true, "RestartReason": true, "Scan": true, "ScanOf": true, "StaleAttempts": true, "StaleRetry": true,
|
|
||||||
"StaleServed": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Findings is every refusal in the Go sources under root, sorted.
|
|
||||||
func Findings(root string) ([]string, error) {
|
|
||||||
var out []string
|
|
||||||
for _, top := range []string{"modules", "checks"} {
|
|
||||||
err := filepath.WalkDir(filepath.Join(root, top), func(path string, d fs.DirEntry, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if d.IsDir() {
|
|
||||||
switch d.Name() {
|
|
||||||
case "testdata", "vendor", "node_modules", ".mesh-build":
|
|
||||||
return filepath.SkipDir
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if !strings.HasSuffix(path, ".go") {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
rel, _ := filepath.Rel(root, path)
|
|
||||||
found, err := fileFindings(path, filepath.ToSlash(rel))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
out = append(out, found...)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Strings(out)
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func fileFindings(path, rel string) ([]string, error) {
|
|
||||||
file, err := parser.ParseFile(token.NewFileSet(), path, nil, parser.SkipObjectResolution)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("%s: %w", rel, err)
|
|
||||||
}
|
|
||||||
var out []string
|
|
||||||
buildstep := ""
|
|
||||||
for _, imp := range file.Imports {
|
|
||||||
p, err := strconv.Unquote(imp.Path.Value)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
sub, ok := strings.CutPrefix(p, sdk+"/")
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for pkg, to := range Removed {
|
|
||||||
if sub == pkg || strings.HasPrefix(sub, pkg+"/") {
|
|
||||||
out = append(out, fmt.Sprintf("%s: imports %s, which left mesh-sdk; it is %s (novox/hq ADR 0593 §1)", rel, p, to))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if sub == "buildstep" {
|
|
||||||
buildstep = "buildstep"
|
|
||||||
if imp.Name != nil {
|
|
||||||
buildstep = imp.Name.Name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if buildstep == "" || buildstep == "_" || buildstep == "." {
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
seen := map[string]bool{}
|
|
||||||
ast.Inspect(file, func(n ast.Node) bool {
|
|
||||||
sel, ok := n.(*ast.SelectorExpr)
|
|
||||||
if !ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
id, ok := sel.X.(*ast.Ident)
|
|
||||||
if !ok || id.Name != buildstep || !sel.Sel.IsExported() || BuildstepContract[sel.Sel.Name] || seen[sel.Sel.Name] {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
seen[sel.Sel.Name] = true
|
|
||||||
out = append(out, fmt.Sprintf("%s: uses %s.%s, which is the build runner's, no longer mesh-sdk's buildstep; import git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep (novox/hq ADR 0593 §1)", rel, buildstep, sel.Sel.Name))
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
if len(os.Args) != 2 {
|
|
||||||
fmt.Fprintln(os.Stderr, "usage: removedpackages <catalogue root>")
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
found, err := Findings(os.Args[1])
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
for _, f := range found {
|
|
||||||
fmt.Println(f)
|
|
||||||
}
|
|
||||||
if len(found) > 0 {
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
fmt.Println("no source imports a package mesh-sdk gave up")
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ADR 0593 §1: a source that imports a package mesh-sdk gave up, or names the build runner through mesh-sdk's buildstep, is
|
|
||||||
// refused naming where it went; the contract that stays, the runner's own package and a testdata copy are not.
|
|
||||||
func TestFindingsRefuseWhatMeshSDKGaveUp(t *testing.T) {
|
|
||||||
const head = "package x\n\nimport (\n"
|
|
||||||
for _, row := range []struct {
|
|
||||||
name, file, src string
|
|
||||||
want string // empty: clean
|
|
||||||
}{
|
|
||||||
{"clean", "modules/a/x.go", head + "\t\"git.novox.be/novox/mesh-sdk/go/delivery\"\n)\n\nvar _ = delivery.StepsState\n", ""},
|
|
||||||
{"a removed package", "modules/a/x.go", head + "\t\"git.novox.be/novox/mesh-sdk/go/compose\"\n)\n\nvar _ = compose.Resolve\n", "imports git.novox.be/novox/mesh-sdk/go/compose, which left mesh-sdk; it is the node-engine"},
|
|
||||||
{"under a removed package", "modules/a/x_test.go", head + "\t\"git.novox.be/novox/mesh-sdk/go/compose/frombus\"\n)\n", "the node-engine"},
|
|
||||||
{"buildcpu", "checks/c/x.go", head + "\t\"git.novox.be/novox/mesh-sdk/go/buildcpu\"\n)\n", "buildstep/buildcpu"},
|
|
||||||
{"the runner through mesh-sdk's buildstep", "modules/a/x.go", head + "\t\"git.novox.be/novox/mesh-sdk/go/buildstep\"\n)\n\nvar _ = buildstep.RunAsk\n", "uses buildstep.RunAsk, which is the build runner's"},
|
|
||||||
{"the runner through an alias", "modules/a/x.go", head + "\tsdkstep \"git.novox.be/novox/mesh-sdk/go/buildstep\"\n)\n\nvar _ = sdkstep.Puller{}\n", "uses sdkstep.Puller"},
|
|
||||||
{"the contract of buildstep", "modules/a/x.go", head + "\t\"git.novox.be/novox/mesh-sdk/go/buildstep\"\n)\n\nvar _ = buildstep.NewID\nvar _ buildstep.Failure\n", ""},
|
|
||||||
{"the runner's own package", "modules/a/x.go", head + "\t\"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep\"\n)\n\nvar _ = buildstep.RunAsk\n", ""},
|
|
||||||
{"a copy in testdata", "modules/a/testdata/x.go", head + "\t\"git.novox.be/novox/mesh-sdk/go/compose\"\n)\n", ""},
|
|
||||||
} {
|
|
||||||
t.Run(row.name, func(t *testing.T) {
|
|
||||||
root := t.TempDir()
|
|
||||||
p := filepath.Join(root, row.file)
|
|
||||||
if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := os.WriteFile(p, []byte(row.src), 0o644); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
got, err := Findings(root)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if all := strings.Join(got, "\n"); (row.want == "") != (all == "") || !strings.Contains(all, row.want) {
|
|
||||||
t.Fatalf("findings = %q, want one containing %q", all, row.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The script's run of this check over the real tree (novox/hq ADR 0529 clause 2).
|
|
||||||
func TestTheCatalogueImportsNothingMeshSDKGaveUp(t *testing.T) {
|
|
||||||
got, err := Findings(filepath.Join("..", ".."))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
for _, f := range got {
|
|
||||||
t.Error(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
# What these tests read outside their package, as paths from the repository's root (a glob, or a directory read whole;
|
|
||||||
# ? before a path whose absence is read). mesh-build-go hashes them into the package's digest, so its verdict is kept
|
|
||||||
# until one of them changes (issue 1936). An empty list, or a line matching nothing, keeps nothing.
|
|
||||||
# every Go source of the modules and of the checks, whose imports are read.
|
|
||||||
modules
|
|
||||||
checks
|
|
||||||
@@ -36,7 +36,13 @@ import (
|
|||||||
// composition is every function or table the check lets stay in mesh-sdk, as "path-from-go/:name", and why. It names no
|
// composition is every function or table the check lets stay in mesh-sdk, as "path-from-go/:name", and why. It names no
|
||||||
// manifest judgement: each is the composer's refusal of composition itself (ADR 0578 clause 5).
|
// manifest judgement: each is the composer's refusal of composition itself (ADR 0578 clause 5).
|
||||||
var composition = map[string]string{
|
var composition = map[string]string{
|
||||||
"catalogue/collections.go:itemTemplateProblem": "composition: filling an item's template",
|
"catalogue/consumers.go:consumersPlaceholderProblems": "composition: rendering a consumer's template",
|
||||||
|
"catalogue/collections.go:collectedPlaceholderProblems": "composition: placeholders a contribution renders",
|
||||||
|
"catalogue/environment_into.go:placeholderProblems": "composition: placeholders a contribution renders",
|
||||||
|
"catalogue/seat_contributions.go:seatPlaceholderProblems": "composition: placeholders a contribution renders",
|
||||||
|
"catalogue/collections.go:itemTemplateProblem": "composition: filling an item's template",
|
||||||
|
"catalogue/setting_takes.go:takenSettingsProblems": "composition: whether stored settings still compose",
|
||||||
|
"catalogue/template.go:HeldTemplateProblem": "composition: the take-in refusal of a resolved build",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Problems is what mesh-sdk's Go tree under root (its go/ directory) holds of a manifest judge, one line each, but for
|
// Problems is what mesh-sdk's Go tree under root (its go/ directory) holds of a manifest judge, one line each, but for
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module adwaita
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ module artifactstoretools
|
|||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
github.com/nats-io/nats.go v1.54.0
|
github.com/nats-io/nats.go v1.54.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/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 h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
|
||||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
|
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=
|
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module asuszephyrusg14
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module avahi
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module blueman
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module bluetooth
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ module brother
|
|||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
github.com/nats-io/nats-server/v2 v2.11.17
|
github.com/nats-io/nats-server/v2 v2.11.17
|
||||||
github.com/nats-io/nats.go v1.54.0
|
github.com/nats-io/nats.go v1.54.0
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/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 h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
|
||||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
|
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=
|
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module claude-code
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require gopkg.in/yaml.v3 v3.0.1
|
require gopkg.in/yaml.v3 v3.0.1
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ module claude-licence-manager
|
|||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
github.com/jackc/pgx/v5 v5.11.0
|
github.com/jackc/pgx/v5 v5.11.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module clipmenu
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ module cups
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ module dbus
|
|||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
github.com/godbus/dbus/v5 v5.1.0
|
github.com/godbus/dbus/v5 v5.1.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module deskchannel
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ module diskload
|
|||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
github.com/nats-io/nats.go v1.54.0
|
github.com/nats-io/nats.go v1.54.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/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 h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
|
||||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
|
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=
|
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module dmenu
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module dnsmasq
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module docker-compose
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module docker
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module dunst
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module fail2ban
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ module feh
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module flatpak
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module fonts
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module forticlient
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
+4
-25
@@ -559,14 +559,8 @@ export class GiteaClient {
|
|||||||
*
|
*
|
||||||
* **A listing the forge cut is no listing** (it says `truncated` on a page with nothing more to read, or the pages
|
* **A listing the forge cut is no listing** (it says `truncated` on a page with nothing more to read, or the pages
|
||||||
* run past a bound): the call fails naming the repository and the commit, never answering the directories it did
|
* run past a bound): the call fails naming the repository and the commit, never answering the directories it did
|
||||||
* see, since a directory left out would be taken for one without a module. With `files`, a list of directories (the
|
* see, since a directory left out would be taken for one without a module. Reads only. */
|
||||||
* root as `""`), the answer also carries, of each, every blob under it, recursively, as its path relative to the
|
async commitTrees(owner: string, repo: string, sha: string, paths?: Record<string, string[]>): Promise<{ root: string; trees: Record<string, string>; paths?: Record<string, Record<string, string>> }> {
|
||||||
* directory with its git object id (the indexer digests the files of a mesh package from it, novox/hq ADR 0593 §2); a
|
|
||||||
* directory the commit lacks fails naming it. Reads only. */
|
|
||||||
async commitTrees(owner: string, repo: string, sha: string, paths?: Record<string, string[]>, files?: string[]): Promise<{ root: string; trees: Record<string, string>; paths?: Record<string, Record<string, string>>; files?: Record<string, Record<string, string>> }> {
|
|
||||||
for (const dir of files ?? []) {
|
|
||||||
if (dir !== "" && dir.split("/").some((seg) => seg === "" || seg === "." || seg === "..")) throw new Error(`files: ${JSON.stringify(dir)} is not a plain relative directory`);
|
|
||||||
}
|
|
||||||
const base = `${repoPath(owner, repo)}/git/trees/${commitSegment(sha)}?recursive=true`;
|
const base = `${repoPath(owner, repo)}/git/trees/${commitSegment(sha)}?recursive=true`;
|
||||||
const cut = (why: string) => new Error(`the forge's tree listing of ${owner}/${repo} at ${sha} was cut (${why}); no trees are answered from a part`);
|
const cut = (why: string) => new Error(`the forge's tree listing of ${owner}/${repo} at ${sha} was cut (${why}); no trees are answered from a part`);
|
||||||
const entries: any[] = [];
|
const entries: any[] = [];
|
||||||
@@ -593,22 +587,7 @@ export class GiteaClient {
|
|||||||
if (!tree) throw new Error(`the forge's tree listing of ${owner}/${repo} at ${sha} holds ${e.path} but not the tree of ${dir}`);
|
if (!tree) throw new Error(`the forge's tree listing of ${owner}/${repo} at ${sha} holds ${e.path} but not the tree of ${dir}`);
|
||||||
trees[dir] = tree;
|
trees[dir] = tree;
|
||||||
}
|
}
|
||||||
let filesOf: Record<string, Record<string, string>> | undefined;
|
if (paths === undefined) return { root, trees };
|
||||||
if (files !== undefined) {
|
|
||||||
// **The files of whole directories** (novox/hq ADR 0593 §2): every blob under each, from the same listing. The listing
|
|
||||||
// is whole (a cut one failed above), so a directory absent from it is absent at the commit.
|
|
||||||
filesOf = {};
|
|
||||||
for (const dir of files) {
|
|
||||||
if (dir !== "" && dirOf.get(dir) === undefined) throw new Error(`the directory ${dir} is not in ${owner}/${repo} at ${sha}`);
|
|
||||||
const prefix = dir === "" ? "" : `${dir}/`;
|
|
||||||
const blobs: Record<string, string> = {};
|
|
||||||
for (const e of entries) {
|
|
||||||
if (e?.type === "blob" && typeof e.path === "string" && e.path.startsWith(prefix)) blobs[e.path.slice(prefix.length)] = String(e.sha);
|
|
||||||
}
|
|
||||||
filesOf[dir] = blobs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (paths === undefined) return filesOf === undefined ? { root, trees } : { root, trees, files: filesOf };
|
|
||||||
// **Named paths** (novox/hq ADR 0499 §3, issue 1758): each asked path's git object id at the commit, a file's or a
|
// **Named paths** (novox/hq ADR 0499 §3, issue 1758): each asked path's git object id at the commit, a file's or a
|
||||||
// directory's, from the same listing, by directory and by path relative to it. A path the listing lacks fails
|
// directory's, from the same listing, by directory and by path relative to it. A path the listing lacks fails
|
||||||
// naming both, and nothing is answered in part.
|
// naming both, and nothing is answered in part.
|
||||||
@@ -623,7 +602,7 @@ export class GiteaClient {
|
|||||||
named[dir][p] = id;
|
named[dir][p] = id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return filesOf === undefined ? { root, trees, paths: named } : { root, trees, paths: named, files: filesOf };
|
return { root, trees, paths: named };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Whether a repository holds a file at a commit: true, false for a 404, thrown when the forge cannot say. */
|
/** Whether a repository holds a file at a commit: true, false for a 404, thrown when the forge cannot say. */
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ module gitea
|
|||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
github.com/nats-io/nats-server/v2 v2.11.17
|
github.com/nats-io/nats-server/v2 v2.11.17
|
||||||
github.com/nats-io/nats.go v1.54.0
|
github.com/nats-io/nats.go v1.54.0
|
||||||
golang.org/x/crypto v0.57.0
|
golang.org/x/crypto v0.57.0
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/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 h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
|
||||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
|
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=
|
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
|
||||||
|
|||||||
@@ -221,58 +221,3 @@ test("gitea_commit_trees takes paths as {directory: [paths]} and refuses another
|
|||||||
server.close();
|
server.close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test("files: every blob under each asked directory, recursively, as its path relative to it with the blob's object id (ADR 0593 §2)", async () => {
|
|
||||||
const { client, server } = await serve([{ sha: COMMIT, url: "u", tree: LISTING, truncated: false, page: 1, total_count: LISTING.length }]);
|
|
||||||
try {
|
|
||||||
const got = await client.commitTrees("novox", "hq", COMMIT, undefined, ["modules/a", "modules/b", ""]);
|
|
||||||
assert.deepEqual(got.files, {
|
|
||||||
"modules/a": { "main.go": h("6"), "module.json": h("5") },
|
|
||||||
"modules/b": { "c/module.json": h("9") },
|
|
||||||
"": { "README.md": h("1"), "module.json": h("2"), "modules/a/module.json": h("5"), "modules/a/main.go": h("6"), "modules/b/c/module.json": h("9"), "modules/d/notes.md": h("b") },
|
|
||||||
});
|
|
||||||
assert.equal((await client.commitTrees("novox", "hq", COMMIT)).files, undefined, "no files asked, none answered");
|
|
||||||
} finally {
|
|
||||||
server.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test("a directory of files the commit lacks, or spelled badly, or a listing the forge cut, fails and answers nothing", async () => {
|
|
||||||
const { client, server } = await serve([{ sha: COMMIT, url: "u", tree: LISTING, truncated: false, page: 1, total_count: LISTING.length }]);
|
|
||||||
try {
|
|
||||||
await assert.rejects(client.commitTrees("novox", "hq", COMMIT, undefined, ["modules/a", "modules/gone"]), (err: Error) => {
|
|
||||||
assert.match(err.message, /modules\/gone/);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
await assert.rejects(client.commitTrees("novox", "hq", COMMIT, undefined, ["README.md"]), /README\.md/, "a file is no directory");
|
|
||||||
for (const bad of ["../x", "/abs", "a//b", "modules/a/", "."]) {
|
|
||||||
await assert.rejects(client.commitTrees("novox", "hq", COMMIT, undefined, [bad]), /files/, bad);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
server.close();
|
|
||||||
}
|
|
||||||
const cut = await serve([{ sha: COMMIT, tree: LISTING, truncated: true, page: 1, total_count: LISTING.length }]);
|
|
||||||
try {
|
|
||||||
await assert.rejects(cut.client.commitTrees("novox", "hq", COMMIT, undefined, ["modules/a"]), /cut/);
|
|
||||||
} finally {
|
|
||||||
cut.server.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test("gitea_commit_trees takes files as a list of directories and refuses another shape before the forge is asked", async () => {
|
|
||||||
const { getGiteaTools } = await import("../tools/index.ts");
|
|
||||||
const { client, server, asked } = await serve([{ sha: COMMIT, url: "u", tree: LISTING, truncated: false, page: 1, total_count: LISTING.length }]);
|
|
||||||
try {
|
|
||||||
const tool = getGiteaTools(client).find((t) => t.name === "gitea_commit_trees")!;
|
|
||||||
assert.ok("files" in tool.input, "the tool names the argument");
|
|
||||||
const got: any = await tool.run({ owner: "novox", repo: "hq", sha: COMMIT, files: ["modules/a"] } as any);
|
|
||||||
assert.deepEqual(got.files, { "modules/a": { "main.go": h("6"), "module.json": h("5") } });
|
|
||||||
const before = asked.length;
|
|
||||||
for (const files of ["modules/a", { "modules/a": [] }, [1], ["../x"]]) {
|
|
||||||
await assert.rejects(tool.run({ owner: "novox", repo: "hq", sha: COMMIT, files } as any), /files/, JSON.stringify(files));
|
|
||||||
}
|
|
||||||
assert.equal(asked.length, before, "the forge was asked for a refused call");
|
|
||||||
} finally {
|
|
||||||
server.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -79,17 +79,6 @@ function namedPaths(raw: unknown): Record<string, string[]> | undefined {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The `files` argument of gitea_commit_trees: a list of directories, each plain ("" is the root; no empty, `.` or `..`
|
|
||||||
* segment, not absolute, no trailing slash); anything else is refused before the forge is asked. Absent is absent. */
|
|
||||||
function filesDirs(raw: unknown): string[] | undefined {
|
|
||||||
if (raw === undefined || raw === null) return undefined;
|
|
||||||
if (!Array.isArray(raw) || raw.some((d) => typeof d !== "string")) throw new Error("files is a list of directories");
|
|
||||||
for (const d of raw as string[]) {
|
|
||||||
if (d !== "" && d.split("/").some((seg) => seg === "" || seg === "." || seg === "..")) throw new Error(`files: ${JSON.stringify(d)} is not a plain relative directory`);
|
|
||||||
}
|
|
||||||
return raw as string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getGiteaTools(gitea: GiteaClient, admin?: () => GiteaAdmin, source: SourceRead = sourceFromBus(state)): ToolDefinition[] {
|
export function getGiteaTools(gitea: GiteaClient, admin?: () => GiteaAdmin, source: SourceRead = sourceFromBus(state)): ToolDefinition[] {
|
||||||
return namesChecked(allGiteaTools(gitea, admin, source));
|
return namesChecked(allGiteaTools(gitea, admin, source));
|
||||||
}
|
}
|
||||||
@@ -733,20 +722,19 @@ function allGiteaTools(gitea: GiteaClient, admin: (() => GiteaAdmin) | undefined
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "gitea_commit_trees",
|
name: "gitea_commit_trees",
|
||||||
description: "A commit's trees from the forge's one recursive listing: the root tree, and the tree of every directory that holds a module.json (the root as an empty name), each by its git hash. What the forge bridge puts in a merge's message (novox/hq ADR 0437 §5). With files, a list of directories, the answer also carries files, {directory: {path relative to it: git blob id}}, every blob under each directory, recursively, from the same listing (novox/hq ADR 0593 §2); a directory the commit lacks fails naming it. With paths, {directory: [path relative to it]}, the answer also carries paths, {directory: {path: git object id}} of each named file or directory at the commit (novox/hq ADR 0499 §3); a path the commit lacks fails naming the directory and the path. The commit is a full 40-hex sha. A listing the forge cut is refused, never answered in part. Reads only.",
|
description: "A commit's trees from the forge's one recursive listing: the root tree, and the tree of every directory that holds a module.json (the root as an empty name), each by its git hash. What the forge bridge puts in a merge's message (novox/hq ADR 0437 §5). With paths, {directory: [path relative to it]}, the answer also carries paths, {directory: {path: git object id}} of each named file or directory at the commit (novox/hq ADR 0499 §3); a path the commit lacks fails naming the directory and the path. The commit is a full 40-hex sha. A listing the forge cut is refused, never answered in part. Reads only.",
|
||||||
input: {
|
input: {
|
||||||
owner: { type: "string", description: "the repository owner" },
|
owner: { type: "string", description: "the repository owner" },
|
||||||
repo: { type: "string", description: "the repository name" },
|
repo: { type: "string", description: "the repository name" },
|
||||||
sha: { type: "string", description: "the commit, its full 40-hex sha" },
|
sha: { type: "string", description: "the commit, its full 40-hex sha" },
|
||||||
paths: { type: "object", description: "optional, {directory: [path relative to it]}: the git object id of each named file or directory is answered under paths; the root directory is the empty name" },
|
paths: { type: "object", description: "optional, {directory: [path relative to it]}: the git object id of each named file or directory is answered under paths; the root directory is the empty name" },
|
||||||
files: { type: "array", description: "optional, a list of directories (the root is the empty name): every blob under each, recursively, is answered under files as {directory: {path relative to it: git blob id}}" },
|
|
||||||
},
|
},
|
||||||
run: async (args) => {
|
run: async (args) => {
|
||||||
const owner = String(args.owner ?? "").trim(), repo = String(args.repo ?? "").trim();
|
const owner = String(args.owner ?? "").trim(), repo = String(args.repo ?? "").trim();
|
||||||
const sha = String(args.sha ?? "").trim();
|
const sha = String(args.sha ?? "").trim();
|
||||||
if (!owner || !repo) throw new Error("name the repository: owner and repo");
|
if (!owner || !repo) throw new Error("name the repository: owner and repo");
|
||||||
if (!/^[0-9a-f]{40}$/i.test(sha)) throw new Error("sha is a commit's full 40-hex sha");
|
if (!/^[0-9a-f]{40}$/i.test(sha)) throw new Error("sha is a commit's full 40-hex sha");
|
||||||
return gitea.commitTrees(owner, repo, sha, namedPaths(args.paths), filesDirs(args.files));
|
return gitea.commitTrees(owner, repo, sha, namedPaths(args.paths));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module gnomekeyring
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module hostname
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ module i3
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module i3statusrust
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module kernel
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module keycloak
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module lemurs
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module localization
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module logrotate
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module memorypressure
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/compress v1.20.0 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||||
|
|||||||
@@ -133,10 +133,10 @@ agent's recipe (mesh-controller's `toolchain.go`, the Go entry, copied in `testd
|
|||||||
`builder.go`): `CGO_ENABLED=0`, `GOFLAGS=-trimpath -buildvcs=false` (with `-mod=mod`, or `-mod=vendor` for a directory
|
`builder.go`): `CGO_ENABLED=0`, `GOFLAGS=-trimpath -buildvcs=false` (with `-mod=mod`, or `-mod=vendor` for a directory
|
||||||
that vendors), `go build -ldflags "-s -w -X main.builtFor=<system>" -o .mesh-build/<artifact>/<binary> ./<from>` in the
|
that vendors), `go build -ldflags "-s -w -X main.builtFor=<system>" -o .mesh-build/<artifact>/<binary> ./<from>` in the
|
||||||
module's directory, by the installed go (`GOTOOLCHAIN=local`) from the module cache the fetch filled and no network
|
module's directory, by the installed go (`GOTOOLCHAIN=local`) from the module cache the fetch filled and no network
|
||||||
(`GOPROXY=off`). The binary is named as the build agent names it. The output directory is packed with the build runner's `Pack`,
|
(`GOPROXY=off`). The binary is named as the build agent names it. The output directory is packed with mesh-sdk's `Pack`,
|
||||||
as the build agent packs; two build nodes on the same toolchain give the same digest for the same tree (ADR 0477 §6). The step ends with the artifact, the binary, the digest, the size and
|
as the build agent packs; two build nodes on the same toolchain give the same digest for the same tree (ADR 0477 §6). The step ends with the artifact, the binary, the digest, the size and
|
||||||
the reference of the archive: once the build ended, this process pushes it to the artifact store (novox/hq ADR 0498 §1 §2,
|
the reference of the archive: once the build ended, this process pushes it to the artifact store (novox/hq ADR 0498 §1 §2,
|
||||||
the build runner's `buildstep.PushBlob`) under `<module>/<artifact>` by its digest, a candidate's as a trunk build's, and answers
|
mesh-sdk's `buildstep.PushBlob`) under `<module>/<artifact>` by its digest, a candidate's as a trunk build's, and answers
|
||||||
`artifact-store://<module>/<artifact>/blobs/<digest>`; a push that fails fails the step `not stored: <artifact>: <the
|
`artifact-store://<module>/<artifact>/blobs/<digest>`; a push that fails fails the step `not stored: <artifact>: <the
|
||||||
store's words>`. The packed bytes never stay on the node. The store's address is `${dir:state}/registry`, placed by the
|
store's words>`. The packed bytes never stay on the node. The store's address is `${dir:state}/registry`, placed by the
|
||||||
manifest, which requires `artifact-store`. The go build runs shut off in bubblewrap (ADR 0448, `isolate.go`): no
|
manifest, which requires `artifact-store`. The go build runs shut off in bubblewrap (ADR 0448, `isolate.go`): no
|
||||||
@@ -148,20 +148,3 @@ directory named by `from`, or a file pinned by `url` and `sha256`, packed with t
|
|||||||
(`testdata/captured/README` says how its digest was captured). A pinned file is fetched, checked against its sha256 (a
|
(`testdata/captured/README` says how its digest was captured). A pinned file is fetched, checked against its sha256 (a
|
||||||
mismatch fails the step naming both digests) and laid out as the artifact says (`file`, or unpacked from `within`; gzip, bzip2
|
mismatch fails the step naming both digests) and laid out as the artifact says (`file`, or unpacked from `within`; gzip, bzip2
|
||||||
and xz, read in Go as the build agent reads it, issue 1590); it runs no command and needs no Go.
|
and xz, read in Go as the build agent reads it, issue 1590); it runs no command and needs no Go.
|
||||||
|
|
||||||
## The runner's package, and what a build says at its end (ADR 0593)
|
|
||||||
|
|
||||||
- The build runner (the pull of asks, the step lifecycle, the toolchain guard, the fetch, the parsers of `go test -json` and
|
|
||||||
`node --test`, `Pack`, the conformance kit and the `mesh-pack` command) is this module's package
|
|
||||||
`git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep`, a Go module of its own in `buildstep/`. mesh-build-node
|
|
||||||
and mesh-build-image require it and replace it to the sibling directory, since it is the same repository. What the delivery
|
|
||||||
parts read of a step (the failure contract, the fetch request, the stale-step sweep, the step id) stays in mesh-sdk's
|
|
||||||
`buildstep`.
|
|
||||||
- `build_go_imports` (a tool, and the last step of a build ask that built an artifact) answers the mesh packages the module's
|
|
||||||
Go programs import: `go list -deps` of each Go bundle's program, tests left out, the packages of the forge outside the
|
|
||||||
module's own directory (a sibling package of its own repository counts), sorted and each once. A build ask carries them as
|
|
||||||
`BuildAnswer.Imports`; the indexer takes the module's fingerprint over those packages' content.
|
|
||||||
- A merge preview (`build_go_fetch` with a merge) answers raw trees: each module directory's git tree, its manifest and lock
|
|
||||||
digests and the objects of the paths its artifacts name. The indexer computes the named tree.
|
|
||||||
- A package of the mesh's forge counts in a test digest by the content of the files the tests reach, not by its version, so a
|
|
||||||
merge of a package whose reached files are unchanged keeps every kept verdict.
|
|
||||||
|
|||||||
@@ -1,497 +0,0 @@
|
|||||||
package buildstep
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"maps"
|
|
||||||
"path"
|
|
||||||
"slices"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
||||||
)
|
|
||||||
|
|
||||||
// **One ask, one whole job** (novox/hq ADR 0525 §3). A build module runs a pulled ask with its own step tools' bodies,
|
|
||||||
// the same ones its request and reply tools run (ADR 0525 §8), in the order the job names: a build ask fetches, then
|
|
||||||
// builds or copies each artifact; a check ask fetches, digests the level, takes the kept verdicts and tests the rest,
|
|
||||||
// the packages whose tests the change touches first. The answers are the tools' own, whole, so the asker judges them as
|
|
||||||
// it judged the tools' answers.
|
|
||||||
|
|
||||||
// StepFunc is one step tool's body: its arguments in, its answer out.
|
|
||||||
type StepFunc func(ctx context.Context, args map[string]any) (any, error)
|
|
||||||
|
|
||||||
// AskSteps are the step tools a build module runs an ask with. Copy is the image module's alone; Digest the Go module's.
|
|
||||||
type AskSteps struct {
|
|
||||||
Fetch, Build, Copy, Digest, Test StepFunc
|
|
||||||
// Imports reads the mesh packages the built programs import (the Go module's alone, ADR 0593 §2): it answers
|
|
||||||
// {"imports": [...]}, sorted, from the workspace the build ran in. A build that built an artifact asks it once, at
|
|
||||||
// its end; nil reports none.
|
|
||||||
Imports StepFunc
|
|
||||||
// Toolchain is the toolchain the module builds with now; a build ask naming another is declined (Declining). Nil checks nothing
|
|
||||||
// (an image or an archive names none).
|
|
||||||
Toolchain func(ctx context.Context) (delivery.ToolchainUsed, error)
|
|
||||||
// Kept looks up a kept verdict by level and digest (the checker's kept bucket, delivery.KeptKey): whether one is
|
|
||||||
// kept and how many tests it counted. Nil takes none.
|
|
||||||
Kept func(ctx context.Context, level, digest string) (tests int, found bool, err error)
|
|
||||||
// Release removes a workspace once the ask's steps on it ended (the module's release tool's body); nil leaves it to
|
|
||||||
// the module's sweep.
|
|
||||||
Release func(workspace string) error
|
|
||||||
Logf func(format string, args ...any)
|
|
||||||
// Has says whether this node has a need a package's tests declare (delivery.AskNeeds, issue 890); a package needing
|
|
||||||
// what it lacks is not tested here but named in the level's Unmet, for the asker to place. Nil has nothing.
|
|
||||||
Has func(need string) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunAsk runs ask's job with steps and answers a delivery.BuildAnswer or a delivery.CheckAnswer.
|
|
||||||
func RunAsk(ctx context.Context, steps AskSteps, ask delivery.StepAsk) (any, error) {
|
|
||||||
if ask.Build != nil {
|
|
||||||
job := *ask.Build
|
|
||||||
job.Fetch = withPackages(job.Fetch, ask.Packages)
|
|
||||||
return steps.build(ctx, job)
|
|
||||||
}
|
|
||||||
if ask.Check != nil {
|
|
||||||
job := *ask.Check
|
|
||||||
job.Fetch = withPackages(job.Fetch, ask.Packages)
|
|
||||||
return steps.check(ctx, job)
|
|
||||||
}
|
|
||||||
return nil, Refuse("the ask %s holds no job", ask.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// withPackages is the fetch's arguments with the ask's package versions (novox/hq ADR 0573 §2), which the build module
|
|
||||||
// writes into the workspace it fetches; a copy, so the ask is left as it was. An ask with none, or a copy alone that
|
|
||||||
// fetches nothing, is left as it is.
|
|
||||||
func withPackages(fetch map[string]any, packages delivery.PackageVersions) map[string]any {
|
|
||||||
if len(packages) == 0 || fetch == nil {
|
|
||||||
return fetch
|
|
||||||
}
|
|
||||||
out := maps.Clone(fetch)
|
|
||||||
out["packages"] = maps.Clone(packages)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
func raw(v any) (json.RawMessage, error) {
|
|
||||||
if r, ok := v.(json.RawMessage); ok {
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
return json.Marshal(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// fetched is what an ask reads of a fetch's answer.
|
|
||||||
type fetched struct {
|
|
||||||
Workspace string `json:"workspace"`
|
|
||||||
Merge string `json:"merge"`
|
|
||||||
Conflicts []string `json:"conflicts"`
|
|
||||||
Changed []string `json:"changed"`
|
|
||||||
Modules []string `json:"modules"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s AskSteps) fetch(ctx context.Context, args map[string]any) (json.RawMessage, fetched, error) {
|
|
||||||
if s.Fetch == nil {
|
|
||||||
return nil, fetched{}, Refuse("this build module fetches nothing")
|
|
||||||
}
|
|
||||||
if _, merges := args["base"]; merges {
|
|
||||||
Phase(ctx, "fetch and merge")
|
|
||||||
} else {
|
|
||||||
Phase(ctx, "fetch")
|
|
||||||
}
|
|
||||||
out, err := s.Fetch(ctx, args)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fetched{}, err
|
|
||||||
}
|
|
||||||
r, err := raw(out)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fetched{}, err
|
|
||||||
}
|
|
||||||
var f fetched
|
|
||||||
if err := json.Unmarshal(r, &f); err != nil || f.Workspace == "" {
|
|
||||||
return r, f, fmt.Errorf("the fetch answered no workspace: %s", r)
|
|
||||||
}
|
|
||||||
return r, f, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// onToolchain is the toolchain this node builds with when the ask names one and this module reports its own, nil when
|
|
||||||
// either is absent. A node on another toolchain than the ask names answers a Declining: it is not on the agreed one and
|
|
||||||
// sits out, for a node that is to take the ask (novox/hq ADR 0584 clause 2).
|
|
||||||
func (s AskSteps) onToolchain(ctx context.Context, want *delivery.ToolchainUsed) (*delivery.ToolchainUsed, error) {
|
|
||||||
if want == nil || s.Toolchain == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
tc, err := s.Toolchain(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if tc.Version != want.Version || tc.Recipe != want.Recipe {
|
|
||||||
return nil, &Declining{Lacks: []string{fmt.Sprintf("the agreed toolchain %s (recipe %s)", want.Version, want.Recipe)},
|
|
||||||
Text: fmt.Sprintf("the ask is for %s (recipe %s) and this node builds with %s (recipe %s): it sits out, and the ask is left to a node on the agreed toolchain",
|
|
||||||
want.Version, want.Recipe, tc.Version, tc.Recipe)}
|
|
||||||
}
|
|
||||||
return &tc, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s AskSteps) build(ctx context.Context, job delivery.BuildJob) (any, error) {
|
|
||||||
answer := delivery.BuildAnswer{}
|
|
||||||
tc, err := s.onToolchain(ctx, job.Toolchain)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
answer.Toolchain = tc
|
|
||||||
var f fetched
|
|
||||||
defer func() { s.release(f.Workspace) }()
|
|
||||||
for _, a := range job.Artifacts {
|
|
||||||
if a.From == "" && f.Workspace == "" {
|
|
||||||
r, got, err := s.fetch(ctx, job.Fetch)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if _, candidate := job.Fetch["base"]; candidate {
|
|
||||||
// A candidate is built from its head merged onto the trunk; a head that does not merge leaves the tree at the
|
|
||||||
// trunk, and building that would store the trunk's build under the candidate's fingerprint.
|
|
||||||
if len(got.Conflicts) > 0 {
|
|
||||||
return nil, fmt.Errorf("the head does not merge into the trunk: files in conflict: %s", strings.Join(got.Conflicts, ", "))
|
|
||||||
}
|
|
||||||
if got.Merge == "" {
|
|
||||||
return nil, fmt.Errorf("the fetch with a base answered no merge: %s", r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f = got
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
out any
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
if a.From != "" {
|
|
||||||
if s.Copy == nil {
|
|
||||||
return nil, Refuse("this build module copies no artifact (%s)", a.Artifact)
|
|
||||||
}
|
|
||||||
Phase(ctx, "copy "+a.Artifact)
|
|
||||||
out, err = s.Copy(ctx, map[string]any{"module": job.Module, "artifact": a.Artifact, "from": a.From})
|
|
||||||
} else {
|
|
||||||
if s.Build == nil {
|
|
||||||
return nil, Refuse("this build module builds no artifact (%s)", a.Artifact)
|
|
||||||
}
|
|
||||||
args := map[string]any{"workspace": f.Workspace, "module": job.Path, "artifact": a.Artifact}
|
|
||||||
if job.Publish {
|
|
||||||
args["publish"] = true
|
|
||||||
}
|
|
||||||
Phase(ctx, "build "+a.Artifact)
|
|
||||||
out, err = s.Build(ctx, args)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("%s: %w", a.Artifact, err)
|
|
||||||
}
|
|
||||||
r, err := raw(out)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var built struct {
|
|
||||||
Digest string `json:"digest"`
|
|
||||||
Reference string `json:"reference"`
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(r, &built); err != nil || built.Digest == "" {
|
|
||||||
return nil, fmt.Errorf("the build of %s answered no digest: %s", a.Artifact, r)
|
|
||||||
}
|
|
||||||
answer.Artifacts = append(answer.Artifacts, delivery.AskBuilt{Artifact: a.Artifact, Digest: built.Digest, Reference: built.Reference})
|
|
||||||
}
|
|
||||||
if s.Imports != nil && f.Workspace != "" {
|
|
||||||
out, err := s.Imports(ctx, map[string]any{"workspace": f.Workspace, "module": job.Path})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("the imports of %s: %w", job.Module, err)
|
|
||||||
}
|
|
||||||
r, err := raw(out)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var read struct {
|
|
||||||
Imports []string `json:"imports"`
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(r, &read); err != nil {
|
|
||||||
return nil, fmt.Errorf("the imports of %s answered what is not a list: %s", job.Module, r)
|
|
||||||
}
|
|
||||||
if err := delivery.CheckImports(read.Imports); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
answer.Imports = read.Imports
|
|
||||||
}
|
|
||||||
return answer, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s AskSteps) check(ctx context.Context, job delivery.CheckJob) (any, error) {
|
|
||||||
if _, err := s.onToolchain(ctx, job.Toolchain); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
r, f, err := s.fetch(ctx, job.Fetch)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer s.release(f.Workspace)
|
|
||||||
answer := delivery.CheckAnswer{Fetch: r}
|
|
||||||
if len(f.Conflicts) > 0 {
|
|
||||||
return answer, nil // the asker reads the conflict from the fetch's answer: a verdict, not an error
|
|
||||||
}
|
|
||||||
if s.Test == nil {
|
|
||||||
return nil, Refuse("this build module tests nothing")
|
|
||||||
}
|
|
||||||
runner := ""
|
|
||||||
if job.Runner != "" && (job.RunnerWhen == "" || changesUnder(f.Changed, job.RunnerWhen)) {
|
|
||||||
runner = job.Runner
|
|
||||||
}
|
|
||||||
// Every level runs, also after one that did not pass: one check names every failure, not only the first (the operator,
|
|
||||||
// 2026-11-02: "A failed check shows only its first fault").
|
|
||||||
for _, level := range job.Levels {
|
|
||||||
la := delivery.LevelAnswer{Level: level}
|
|
||||||
_, err := s.level(ctx, job, level, f, runner, &la)
|
|
||||||
answer.Levels = append(answer.Levels, la)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return answer, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// level runs one level of a check ask and says whether it passed.
|
|
||||||
func (s AskSteps) level(ctx context.Context, job delivery.CheckJob, level string, f fetched, runner string, la *delivery.LevelAnswer) (bool, error) {
|
|
||||||
withRunner := func(args map[string]any) map[string]any {
|
|
||||||
if runner != "" {
|
|
||||||
args["runner"] = runner
|
|
||||||
}
|
|
||||||
if len(job.NodeNames) > 0 {
|
|
||||||
args[delivery.NodeNamesArg] = job.NodeNames // the names check's (ADR 0567 §12)
|
|
||||||
}
|
|
||||||
return args
|
|
||||||
}
|
|
||||||
test := func(args map[string]any) (bool, error) {
|
|
||||||
what := "the whole level"
|
|
||||||
if dir, _ := args["module"].(string); dir != "" {
|
|
||||||
what = dir
|
|
||||||
} else if pkgs, _ := args["packages"].([]string); len(pkgs) > 0 {
|
|
||||||
what = fmt.Sprintf("%d packages", len(pkgs))
|
|
||||||
}
|
|
||||||
Phase(ctx, fmt.Sprintf("%s: tests of %s", level, what))
|
|
||||||
out, err := s.Test(ctx, args)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
r, err := raw(out)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
la.Tests = append(la.Tests, r)
|
|
||||||
var t struct {
|
|
||||||
Passed bool `json:"passed"`
|
|
||||||
}
|
|
||||||
_ = json.Unmarshal(r, &t)
|
|
||||||
return t.Passed, nil
|
|
||||||
}
|
|
||||||
// The packages (or Node directories) whose tests the change touches run first (issue 1289; ADR 0444), and then every
|
|
||||||
// other test step of the level, also after one that did not pass, so one check names every failing test of the level
|
|
||||||
// and not only the first (issue 1921; the operator, 2026-11-02); the level passes only when each of its steps did.
|
|
||||||
all := true
|
|
||||||
if s.Digest == nil {
|
|
||||||
// A module with no digest (Node) tests each module directory of the tree, those whose tests changed first.
|
|
||||||
for _, priority := range []bool{true, false} {
|
|
||||||
for _, dir := range f.Modules {
|
|
||||||
if changesTestIn(f.Changed, dir) != priority {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
passed, err := test(withRunner(map[string]any{"workspace": f.Workspace, "module": dir, "level": level}))
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("the %s level of %s: %w", level, dir, err)
|
|
||||||
}
|
|
||||||
all = all && passed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return all, nil
|
|
||||||
}
|
|
||||||
todo, whole, keepRunner := s.plan(ctx, job, level, f, runner, withRunner, la)
|
|
||||||
if !keepRunner {
|
|
||||||
runner = "" // an installed module that ignores the runner judges the level as it judges any other (ADR 0535 §2)
|
|
||||||
}
|
|
||||||
if whole && len(job.Packages) > 0 {
|
|
||||||
todo, whole = job.Packages, false // a placed ask tests its packages alone, whatever the digest said (ADR 0552 §4)
|
|
||||||
}
|
|
||||||
for _, priority := range []bool{true, false} {
|
|
||||||
args := withRunner(map[string]any{"workspace": f.Workspace, "level": level})
|
|
||||||
if job.Run != "" {
|
|
||||||
args["run"] = job.Run
|
|
||||||
}
|
|
||||||
if whole {
|
|
||||||
if priority {
|
|
||||||
continue // nothing to put first: the level runs whole after
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var run []string
|
|
||||||
for _, p := range todo {
|
|
||||||
if changesTestOf(f.Changed, p) == priority {
|
|
||||||
run = append(run, p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(run) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
args["packages"] = run
|
|
||||||
}
|
|
||||||
passed, err := test(args)
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("the %s level: %w", level, err)
|
|
||||||
}
|
|
||||||
all = all && passed // the others still run after a failure (issue 1921)
|
|
||||||
}
|
|
||||||
return all, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// release removes an ask's workspace, saying a failure: the module's sweep removes it later.
|
|
||||||
func (s AskSteps) release(workspace string) {
|
|
||||||
if workspace == "" || s.Release == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := s.Release(workspace); err != nil && s.Logf != nil {
|
|
||||||
s.Logf("the workspace %s is left to the sweep: %v", workspace, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// plan digests the level and takes the kept verdicts: the packages left to test, or all when the level runs whole, and
|
|
||||||
// whether the runner stays. A try of some tests runs whole, digesting nothing (a partial run proves no package). Under the
|
|
||||||
// candidate's runner the digest is asked as the candidate: when the candidate answers it, every package runs and nothing
|
|
||||||
// is digested for keeping (ADR 0535 §4); when the installed module answers it (it does not take the runner), the level is
|
|
||||||
// planned as any other and the runner is dropped. The level runs whole and takes nothing when the digest names no package
|
|
||||||
// or does not cover the siblings the fetch placed (ADR 0444, L5).
|
|
||||||
func (s AskSteps) plan(ctx context.Context, job delivery.CheckJob, level string, f fetched, runner string, withRunner func(map[string]any) map[string]any, la *delivery.LevelAnswer) ([]string, bool, bool) {
|
|
||||||
if job.Run != "" {
|
|
||||||
return nil, true, true
|
|
||||||
}
|
|
||||||
Phase(ctx, level+": digest")
|
|
||||||
out, err := s.Digest(ctx, withRunner(map[string]any{"workspace": f.Workspace, "level": level}))
|
|
||||||
if err != nil {
|
|
||||||
if s.Logf != nil {
|
|
||||||
s.Logf("no digest of the %s level, so it runs whole: %v", level, err)
|
|
||||||
}
|
|
||||||
return nil, true, true
|
|
||||||
}
|
|
||||||
r, err := raw(out)
|
|
||||||
if err != nil {
|
|
||||||
return nil, true, true
|
|
||||||
}
|
|
||||||
var d delivery.LevelDigest // packages kept as a sorted list, never keyed by a package path (issue 1930)
|
|
||||||
if err := json.Unmarshal(r, &d); err != nil {
|
|
||||||
return nil, true, true
|
|
||||||
}
|
|
||||||
if runner != "" {
|
|
||||||
if strings.HasPrefix(d.JudgedBy, "candidate ") {
|
|
||||||
la.Judged = d.JudgedBy
|
|
||||||
return nil, true, true // the candidate decides which verdicts would be taken: all run, nothing kept
|
|
||||||
}
|
|
||||||
runner = ""
|
|
||||||
}
|
|
||||||
if len(d.Packages) == 0 || !slices.Equal(d.Beside, besideOf(job.Fetch)) {
|
|
||||||
return nil, true, runner != "" // a digest that names nothing or not the siblings keys no verdict: the level runs whole
|
|
||||||
}
|
|
||||||
la.Digest = &d
|
|
||||||
only := map[string]bool{}
|
|
||||||
for _, p := range job.Packages {
|
|
||||||
only[p] = true
|
|
||||||
}
|
|
||||||
var todo []string
|
|
||||||
for _, pd := range d.Packages {
|
|
||||||
p := pd.Package
|
|
||||||
if len(only) > 0 && !only[p] {
|
|
||||||
continue // a placed ask's other packages were judged by the first ask (ADR 0552 §4)
|
|
||||||
}
|
|
||||||
if job.Kept && s.Kept != nil {
|
|
||||||
tests, found, err := s.Kept(ctx, level, pd.Digest)
|
|
||||||
if err != nil && s.Logf != nil {
|
|
||||||
s.Logf("the kept verdict of %s at %s cannot be read, so it runs: %v", p, level, err)
|
|
||||||
}
|
|
||||||
if err == nil && found {
|
|
||||||
la.Kept = append(la.Kept, delivery.KeptTaken{Package: p, Digest: pd.Digest, Tests: tests})
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if needs := d.NeedsOf(p); len(delivery.Lacks(needs, s.Has)) > 0 {
|
|
||||||
// Its tests would only skip here (issue 890): it is left for a node that has what they need.
|
|
||||||
la.Unmet = append(la.Unmet, delivery.PackageNeeds{Package: p, Needs: needs})
|
|
||||||
if s.Logf != nil {
|
|
||||||
s.Logf("the %s level of %s is left to a node with %s: this node lacks %s", level, p,
|
|
||||||
strings.Join(needs, ", "), strings.Join(delivery.Lacks(needs, s.Has), ", "))
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
todo = append(todo, p)
|
|
||||||
}
|
|
||||||
return todo, false, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// besideOf are the siblings a fetch's arguments place, as a digest echoes them: `<owner/repo> <commit>`, sorted.
|
|
||||||
func besideOf(fetch map[string]any) []string {
|
|
||||||
raw, err := json.Marshal(fetch["beside"])
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var refs []struct {
|
|
||||||
Repository string `json:"repository"`
|
|
||||||
Commit string `json:"commit"`
|
|
||||||
}
|
|
||||||
_ = json.Unmarshal(raw, &refs)
|
|
||||||
var out []string
|
|
||||||
for _, r := range refs {
|
|
||||||
out = append(out, r.Repository+" "+r.Commit)
|
|
||||||
}
|
|
||||||
sort.Strings(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// changesUnder says whether a path the merge changes is under prefix.
|
|
||||||
func changesUnder(changed []string, prefix string) bool {
|
|
||||||
for _, p := range changed {
|
|
||||||
if strings.HasPrefix(p, prefix) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// isTestFile says whether a repository-relative path is a test file: Go's _test.go, and a TypeScript module's .test.
|
|
||||||
// and .spec. files and test directories.
|
|
||||||
func isTestFile(p string) bool {
|
|
||||||
switch {
|
|
||||||
case strings.HasSuffix(p, "_test.go"):
|
|
||||||
return true
|
|
||||||
case strings.Contains(p, ".test.") || strings.Contains(p, ".spec."):
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
for _, seg := range strings.Split(path.Dir(p), "/") {
|
|
||||||
if seg == "__tests__" || seg == "test" || seg == "tests" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// changesTestOf says whether the merge changes a test file of the Go package with the import path pkg: the file's
|
|
||||||
// directory and the import path agree on their last segments.
|
|
||||||
func changesTestOf(changed []string, pkg string) bool {
|
|
||||||
for _, f := range changed {
|
|
||||||
if !strings.HasSuffix(f, "_test.go") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dir := path.Dir(f)
|
|
||||||
if dir == pkg || strings.HasSuffix(pkg, "/"+dir) || strings.HasSuffix(dir, "/"+pkg) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// changesTestIn says whether the merge changes a test file inside the directory of a Node module.
|
|
||||||
func changesTestIn(changed []string, dir string) bool {
|
|
||||||
for _, f := range changed {
|
|
||||||
if !isTestFile(f) || strings.HasSuffix(f, "_test.go") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if dir == "." || dir == "" || strings.HasPrefix(f, strings.TrimSuffix(dir, "/")+"/") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
@@ -1,336 +0,0 @@
|
|||||||
package buildstep
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
sdkstep "git.novox.be/novox/mesh-sdk/go/buildstep"
|
|
||||||
"slices"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
||||||
)
|
|
||||||
|
|
||||||
const runCommit = "0123456789abcdef0123456789abcdef01234567"
|
|
||||||
|
|
||||||
// recorder is a build module's step tools that record each call.
|
|
||||||
type recorder struct {
|
|
||||||
calls []string
|
|
||||||
fetch map[string]any
|
|
||||||
tests map[string]bool // package or module directory -> passed
|
|
||||||
digests map[string]string
|
|
||||||
needs []delivery.PackageNeeds
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *recorder) steps() AskSteps {
|
|
||||||
call := func(name string, answer func(args map[string]any) any) StepFunc {
|
|
||||||
return func(ctx context.Context, args map[string]any) (any, error) {
|
|
||||||
b, _ := json.Marshal(args)
|
|
||||||
r.calls = append(r.calls, name+" "+string(b))
|
|
||||||
return answer(args), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return AskSteps{
|
|
||||||
Fetch: call("fetch", func(map[string]any) any { return r.fetch }),
|
|
||||||
Build: call("build", func(args map[string]any) any {
|
|
||||||
return map[string]any{"digest": "sha256:" + args["artifact"].(string), "reference": "artifact-store://" + args["artifact"].(string)}
|
|
||||||
}),
|
|
||||||
Copy: call("copy", func(args map[string]any) any { return map[string]any{"digest": "sha256:copied"} }),
|
|
||||||
Digest: call("digest", func(map[string]any) any {
|
|
||||||
return map[string]any{"level": "unit", "packages": r.digests, "needs": r.needs}
|
|
||||||
}),
|
|
||||||
Test: call("test", func(args map[string]any) any {
|
|
||||||
passed := true
|
|
||||||
if pkgs, ok := args["packages"].([]string); ok {
|
|
||||||
for _, p := range pkgs {
|
|
||||||
passed = passed && r.tests[p]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if dir, ok := args["module"].(string); ok {
|
|
||||||
passed = r.tests[dir]
|
|
||||||
}
|
|
||||||
return map[string]any{"passed": passed, "tests": 1}
|
|
||||||
}),
|
|
||||||
Kept: func(ctx context.Context, level, digest string) (int, bool, error) { return 7, digest == "d-kept", nil },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ask(job any) delivery.StepAsk {
|
|
||||||
a := delivery.StepAsk{ID: sdkstep.NewID(), Kind: delivery.AskCheck, Language: delivery.AskGo, Asker: "t", At: time.Now()}
|
|
||||||
switch j := job.(type) {
|
|
||||||
case delivery.BuildJob:
|
|
||||||
a.Build = &j
|
|
||||||
case delivery.CheckJob:
|
|
||||||
a.Check = &j
|
|
||||||
}
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1887/A3: a build ask fetches once, builds each artifact from the workspace with publish, copies a pinned one without a
|
|
||||||
// fetch, and answers each artifact's digest and reference.
|
|
||||||
func TestRunAskBuilds(t *testing.T) {
|
|
||||||
r := &recorder{fetch: map[string]any{"workspace": "/w/1"}}
|
|
||||||
steps := r.steps()
|
|
||||||
var released []string
|
|
||||||
steps.Release = func(ws string) error { released = append(released, ws); return nil }
|
|
||||||
// ADR 0593 §2: a Go module's build ends by saying which mesh packages its programs import.
|
|
||||||
steps.Imports = func(ctx context.Context, args map[string]any) (any, error) {
|
|
||||||
b, _ := json.Marshal(args)
|
|
||||||
r.calls = append(r.calls, "imports "+string(b))
|
|
||||||
return map[string]any{"imports": []string{"git.novox.be/novox/mesh-sdk/go/announce", "git.novox.be/novox/mesh-sdk/go/delivery"}}, nil
|
|
||||||
}
|
|
||||||
// Issue 1585: the ask's package versions reach the fetch, and the ask's own fetch arguments stay as they were.
|
|
||||||
a := ask(delivery.BuildJob{Module: "m", Path: "modules/m", Publish: true,
|
|
||||||
Fetch: map[string]any{"repository": "novox/x", "commit": runCommit},
|
|
||||||
Artifacts: []delivery.AskArtifact{{Artifact: "base", From: "registry/b@sha256:00"}, {Artifact: "bundle"}, {Artifact: "lib"}}})
|
|
||||||
a.Packages = delivery.PackageVersions{"git.novox.be/novox/mesh-sdk/go": runCommit}
|
|
||||||
out, err := RunAsk(context.Background(), steps, a)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if _, put := a.Build.Fetch["packages"]; put {
|
|
||||||
t.Error("the ask's fetch arguments were changed")
|
|
||||||
}
|
|
||||||
got := strings.Join(r.calls, "\n")
|
|
||||||
want := `copy {"artifact":"base","from":"registry/b@sha256:00","module":"m"}
|
|
||||||
fetch {"commit":"` + runCommit + `","packages":{"git.novox.be/novox/mesh-sdk/go":"` + runCommit + `"},"repository":"novox/x"}
|
|
||||||
build {"artifact":"bundle","module":"modules/m","publish":true,"workspace":"/w/1"}
|
|
||||||
build {"artifact":"lib","module":"modules/m","publish":true,"workspace":"/w/1"}
|
|
||||||
imports {"module":"modules/m","workspace":"/w/1"}`
|
|
||||||
if got != want {
|
|
||||||
t.Fatalf("calls:\n%s\nwant:\n%s", got, want)
|
|
||||||
}
|
|
||||||
if len(released) != 1 || released[0] != "/w/1" {
|
|
||||||
t.Errorf("released %v, want the one workspace", released)
|
|
||||||
}
|
|
||||||
answer := out.(delivery.BuildAnswer)
|
|
||||||
if len(answer.Artifacts) != 3 || answer.Artifacts[1].Reference != "artifact-store://bundle" {
|
|
||||||
t.Fatalf("answer %+v", answer)
|
|
||||||
}
|
|
||||||
if !slices.Equal(answer.Imports, []string{"git.novox.be/novox/mesh-sdk/go/announce", "git.novox.be/novox/mesh-sdk/go/delivery"}) {
|
|
||||||
t.Errorf("the answer's imports are %v", answer.Imports)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A build ask for another toolchain is declined (ADR 0584 clause 2), so the node sits out and the asker puts it again.
|
|
||||||
func TestRunAskRefusesAnotherToolchain(t *testing.T) {
|
|
||||||
r := &recorder{fetch: map[string]any{"workspace": "/w"}}
|
|
||||||
s := r.steps()
|
|
||||||
s.Toolchain = func(context.Context) (delivery.ToolchainUsed, error) {
|
|
||||||
return delivery.ToolchainUsed{Version: "go1.27", Recipe: "3"}, nil
|
|
||||||
}
|
|
||||||
_, err := RunAsk(context.Background(), s, ask(delivery.BuildJob{Module: "m", Path: ".", Fetch: map[string]any{"repository": "novox/x", "commit": runCommit},
|
|
||||||
Artifacts: []delivery.AskArtifact{{Artifact: "bundle"}}, Toolchain: &delivery.ToolchainUsed{Version: "go1.26", Recipe: "3"}}))
|
|
||||||
var dec *Declining
|
|
||||||
if !errors.As(err, &dec) || len(dec.Lacks) != 1 || len(r.calls) != 0 {
|
|
||||||
t.Fatalf("err %v, calls %v", err, r.calls)
|
|
||||||
}
|
|
||||||
// A check ask names the toolchain too (ADR 0584), and a node on another declines it before it fetches.
|
|
||||||
_, err = RunAsk(context.Background(), s, ask(delivery.CheckJob{Fetch: map[string]any{"repository": "novox/x", "commit": runCommit}, Levels: []string{delivery.LevelUnit},
|
|
||||||
Toolchain: &delivery.ToolchainUsed{Version: "go1.26", Recipe: "3"}}))
|
|
||||||
if !errors.As(err, &dec) || len(r.calls) != 0 {
|
|
||||||
t.Fatalf("a check ask: err %v, calls %v", err, r.calls)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1887/A3, A5a: a check ask runs the levels of one check after one fetch: it digests the unit level, takes the kept
|
|
||||||
// verdicts, tests the packages whose tests changed first and then the others, also after one of them failed, and runs
|
|
||||||
// every level, also after one that did not pass, so one check names every failure (the operator, 2026-11-02).
|
|
||||||
func TestRunAskChecksTheLevelsOfOneCheck(t *testing.T) {
|
|
||||||
r := &recorder{
|
|
||||||
fetch: map[string]any{"workspace": "/w", "changed": []string{"pkg/b/b_test.go"}},
|
|
||||||
digests: map[string]string{"x/pkg/a": "d-a", "x/pkg/b": "d-b", "x/pkg/k": "d-kept"},
|
|
||||||
tests: map[string]bool{"x/pkg/a": true, "x/pkg/b": false},
|
|
||||||
}
|
|
||||||
out, err := RunAsk(context.Background(), r.steps(), ask(delivery.CheckJob{Levels: []string{delivery.LevelUnit, delivery.LevelIntegration}, Kept: true,
|
|
||||||
Runner: "workspace", RunnerWhen: "modules/mesh-build-go/", Fetch: map[string]any{"repository": "novox/x", "commit": runCommit, "base": runCommit}}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
got := strings.Join(r.calls, "\n")
|
|
||||||
want := `fetch {"base":"` + runCommit + `","commit":"` + runCommit + `","repository":"novox/x"}
|
|
||||||
digest {"level":"unit","workspace":"/w"}
|
|
||||||
test {"level":"unit","packages":["x/pkg/b"],"workspace":"/w"}
|
|
||||||
test {"level":"unit","packages":["x/pkg/a"],"workspace":"/w"}
|
|
||||||
digest {"level":"integration","workspace":"/w"}
|
|
||||||
test {"level":"integration","packages":["x/pkg/b"],"workspace":"/w"}
|
|
||||||
test {"level":"integration","packages":["x/pkg/a"],"workspace":"/w"}`
|
|
||||||
if got != want {
|
|
||||||
t.Fatalf("calls:\n%s\nwant:\n%s", got, want)
|
|
||||||
}
|
|
||||||
a := out.(delivery.CheckAnswer)
|
|
||||||
if len(a.Levels) != 2 {
|
|
||||||
t.Fatalf("levels %+v, want both levels", a.Levels)
|
|
||||||
}
|
|
||||||
u := a.Levels[0]
|
|
||||||
if u.Level != delivery.LevelUnit || len(u.Kept) != 1 || u.Kept[0].Package != "x/pkg/k" || u.Kept[0].Tests != 7 || len(u.Tests) != 2 || u.Digest == nil {
|
|
||||||
t.Fatalf("the unit level %+v", u)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ADR 0567 §12: a check's node names reach the digest and test tools, for the names check (a kept verdict misses when
|
|
||||||
// they change).
|
|
||||||
r.calls = nil
|
|
||||||
if _, err := RunAsk(context.Background(), r.steps(), ask(delivery.CheckJob{Levels: []string{delivery.LevelUnit}, NodeNames: []string{"anchor"},
|
|
||||||
Fetch: map[string]any{"repository": "novox/x", "commit": runCommit}})); err != nil || !strings.Contains(strings.Join(r.calls, "\n"), `digest {"level":"unit","node-names":["anchor"]`) ||
|
|
||||||
!strings.Contains(strings.Join(r.calls, "\n"), `test {"level":"unit","node-names":["anchor"]`) {
|
|
||||||
t.Fatalf("the node names are not handed to the digest and test tools: %v, %v", r.calls, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Issue 890/AC2: a package declaring node-build-node is not tested on a node that lacks it but named unmet, and is
|
|
||||||
// tested on a node that has it.
|
|
||||||
for _, has := range []bool{false, true} {
|
|
||||||
r.calls, r.tests["x/pkg/b"] = nil, true
|
|
||||||
r.needs = []delivery.PackageNeeds{{Package: "x/pkg/a", Needs: []string{delivery.NeedNodeBuildNode}}}
|
|
||||||
steps := r.steps()
|
|
||||||
if has {
|
|
||||||
steps.Has = func(n string) bool { return n == delivery.NeedNodeBuildNode }
|
|
||||||
}
|
|
||||||
out, err := RunAsk(context.Background(), steps, ask(delivery.CheckJob{Levels: []string{delivery.LevelUnit}, Kept: true,
|
|
||||||
Fetch: map[string]any{"repository": "novox/x", "commit": runCommit}}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
u := out.(delivery.CheckAnswer).Levels[0]
|
|
||||||
tested := strings.Contains(strings.Join(r.calls, "\n"), `"x/pkg/a"`)
|
|
||||||
if tested != has || (len(u.Unmet) == 0) == !has || (!has && (u.Unmet[0].Package != "x/pkg/a" || delivery.UnmetNeeds([]delivery.LevelAnswer{u})[0] != delivery.NeedNodeBuildNode)) {
|
|
||||||
t.Fatalf("has %v: tested x/pkg/a %v, unmet %+v, calls %v", has, tested, u.Unmet, r.calls)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// A placed ask naming its packages tests those alone and takes no other's kept verdict, also when the level would
|
|
||||||
// run whole (ADR 0552 §4).
|
|
||||||
for _, digests := range []map[string]string{r.digests, nil} {
|
|
||||||
r.calls, r.digests = nil, digests
|
|
||||||
steps := r.steps()
|
|
||||||
steps.Has = func(string) bool { return true }
|
|
||||||
out, err := RunAsk(context.Background(), steps, ask(delivery.CheckJob{Levels: []string{delivery.LevelUnit}, Kept: true, Packages: []string{"x/pkg/a"},
|
|
||||||
Fetch: map[string]any{"repository": "novox/x", "commit": runCommit}}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
u := out.(delivery.CheckAnswer).Levels[0]
|
|
||||||
if last := r.calls[len(r.calls)-1]; !strings.HasSuffix(last, `"packages":["x/pkg/a"],"workspace":"/w"}`) || len(u.Kept) != 0 || len(u.Tests) != 1 {
|
|
||||||
t.Fatalf("digests %v: calls %v, kept %+v: want x/pkg/a alone tested, nothing taken kept", digests != nil, r.calls, u.Kept)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ADR 0535 §2, §4: a merge that changes the build module's own code is judged by the candidate's runner: its digest says
|
|
||||||
// the candidate ran, and every package runs with nothing kept; an installed module that ignores the runner is planned as
|
|
||||||
// any other, the runner dropped. A digest that does not cover the fetch's siblings runs the level whole too.
|
|
||||||
func TestRunAskRunsWholeUnderTheRunnerOrWithoutTheSiblings(t *testing.T) {
|
|
||||||
for _, tc := range []struct{ judged, want string }{
|
|
||||||
{"candidate 999", `digest {"level":"unit","runner":"workspace","workspace":"/w"}
|
|
||||||
test {"level":"unit","runner":"workspace","workspace":"/w"}`},
|
|
||||||
{"installed", `digest {"level":"unit","runner":"workspace","workspace":"/w"}`},
|
|
||||||
} {
|
|
||||||
r := &recorder{fetch: map[string]any{"workspace": "/w", "changed": []string{"modules/mesh-build-go/x.go"}},
|
|
||||||
digests: map[string]string{"x/pkg/a": "d-kept"}, tests: map[string]bool{}}
|
|
||||||
steps := r.steps()
|
|
||||||
digest := steps.Digest
|
|
||||||
steps.Digest = func(ctx context.Context, args map[string]any) (any, error) {
|
|
||||||
_, _ = digest(ctx, args)
|
|
||||||
return map[string]any{"level": "unit", "packages": r.digests, "judged_by": tc.judged}, nil
|
|
||||||
}
|
|
||||||
out, err := RunAsk(context.Background(), steps, ask(delivery.CheckJob{Levels: []string{delivery.LevelUnit}, Kept: true,
|
|
||||||
Runner: "workspace", RunnerWhen: "modules/mesh-build-go/", Fetch: map[string]any{"repository": "novox/x", "commit": runCommit}}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if got := strings.Join(r.calls[1:], "\n"); got != tc.want {
|
|
||||||
t.Errorf("judged by %s: calls\n%s\nwant\n%s", tc.judged, got, tc.want)
|
|
||||||
}
|
|
||||||
l := out.(delivery.CheckAnswer).Levels[0]
|
|
||||||
if tc.judged == "installed" && (len(l.Kept) != 1 || l.Judged != "") {
|
|
||||||
t.Errorf("an installed module's level %+v, want the kept verdict taken", l)
|
|
||||||
}
|
|
||||||
if tc.judged != "installed" && (len(l.Kept) != 0 || l.Digest != nil || l.Judged != tc.judged) {
|
|
||||||
t.Errorf("the candidate's level %+v, want nothing kept and the program named", l)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
r := &recorder{fetch: map[string]any{"workspace": "/w"}, digests: map[string]string{"x/pkg/a": "d-kept"}, tests: map[string]bool{}}
|
|
||||||
out, err := RunAsk(context.Background(), r.steps(), ask(delivery.CheckJob{Levels: []string{delivery.LevelUnit}, Kept: true,
|
|
||||||
Fetch: map[string]any{"repository": "novox/x", "commit": runCommit, "beside": []any{map[string]any{"repository": "novox/mesh-sdk", "commit": runCommit}}}}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if l := out.(delivery.CheckAnswer).Levels[0]; len(l.Kept) != 0 || l.Digest != nil {
|
|
||||||
t.Fatalf("a digest without the siblings kept %+v", l)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Node check tests each module directory, the one whose tests changed first; a conflict ends the ask with the fetch
|
|
||||||
// alone, a verdict for the asker.
|
|
||||||
func TestRunAskChecksNodeModulesAndConflicts(t *testing.T) {
|
|
||||||
r := &recorder{fetch: map[string]any{"workspace": "/w", "modules": []string{"a", "b"}, "changed": []string{"b/test/x.test.ts"}},
|
|
||||||
tests: map[string]bool{"a": true, "b": true}}
|
|
||||||
s := r.steps()
|
|
||||||
s.Digest = nil
|
|
||||||
if _, err := RunAsk(context.Background(), s, ask(delivery.CheckJob{Levels: []string{delivery.LevelUnit}, Fetch: map[string]any{"repository": "novox/x", "commit": runCommit}})); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if fmt.Sprint(r.calls[1:]) != `[test {"level":"unit","module":"b","workspace":"/w"} test {"level":"unit","module":"a","workspace":"/w"}]` {
|
|
||||||
t.Fatalf("calls %v", r.calls)
|
|
||||||
}
|
|
||||||
r = &recorder{fetch: map[string]any{"workspace": "/w", "conflicts": []string{"go.mod"}}}
|
|
||||||
out, err := RunAsk(context.Background(), r.steps(), ask(delivery.CheckJob{Levels: []string{delivery.LevelUnit}, Fetch: map[string]any{"repository": "novox/x", "commit": runCommit}}))
|
|
||||||
if err != nil || len(r.calls) != 1 || len(out.(delivery.CheckAnswer).Levels) != 0 {
|
|
||||||
t.Fatalf("a conflict: %v %v", err, r.calls)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Issue 1289: the packages whose tests changed run first. Since 2026-11-02 (the operator: "A failed check shows only its
|
|
||||||
// first fault") a failure among them ends nothing: every package of the level and every level still runs, so one check
|
|
||||||
// names every failing test (issue 1921). A Node check does the same with its module directories.
|
|
||||||
func TestRunAskRunsEveryPackageAndLevelAfterAFailure(t *testing.T) {
|
|
||||||
for _, row := range []struct {
|
|
||||||
name string
|
|
||||||
changed []string
|
|
||||||
tests map[string]bool
|
|
||||||
want []string // the packages tested at each level, in order
|
|
||||||
}{
|
|
||||||
{"the change's own test fails: the rest still runs", []string{"pkg/b/b_test.go"}, map[string]bool{"x/pkg/a": false, "x/pkg/b": false}, []string{"x/pkg/b", "x/pkg/a"}},
|
|
||||||
{"the change's own test passes: the rest runs, also after a failure", []string{"pkg/b/b_test.go"}, map[string]bool{"x/pkg/a": false, "x/pkg/b": true, "x/pkg/c": false}, []string{"x/pkg/b", "x/pkg/a x/pkg/c"}},
|
|
||||||
{"no test changed: every failure is named", nil, map[string]bool{"x/pkg/a": false, "x/pkg/b": false}, []string{"x/pkg/a x/pkg/b"}},
|
|
||||||
} {
|
|
||||||
r := &recorder{fetch: map[string]any{"workspace": "/w", "changed": row.changed}, tests: row.tests,
|
|
||||||
digests: map[string]string{"x/pkg/a": "d-a", "x/pkg/b": "d-b"}}
|
|
||||||
if _, ok := row.tests["x/pkg/c"]; ok {
|
|
||||||
r.digests["x/pkg/c"] = "d-c"
|
|
||||||
}
|
|
||||||
out, err := RunAsk(context.Background(), r.steps(), ask(delivery.CheckJob{Levels: []string{delivery.LevelUnit, delivery.LevelIntegration},
|
|
||||||
Fetch: map[string]any{"repository": "novox/x", "commit": runCommit}}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
var got []string
|
|
||||||
for _, c := range r.calls[1:] {
|
|
||||||
if strings.HasPrefix(c, "test ") {
|
|
||||||
var a struct{ Packages []string }
|
|
||||||
_ = json.Unmarshal([]byte(strings.TrimPrefix(c, "test ")), &a)
|
|
||||||
got = append(got, strings.Join(a.Packages, " "))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if want := append(slices.Clone(row.want), row.want...); fmt.Sprint(got) != fmt.Sprint(want) {
|
|
||||||
t.Errorf("%s: tested %v, want %v", row.name, got, want)
|
|
||||||
}
|
|
||||||
if a := out.(delivery.CheckAnswer); len(a.Levels) != 2 || len(a.Levels[0].Tests) != len(row.want) {
|
|
||||||
t.Errorf("%s: levels %+v, want both, each with every test result", row.name, a.Levels)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r := &recorder{fetch: map[string]any{"workspace": "/w", "modules": []string{"a", "b"}, "changed": []string{"b/test/x.test.ts"}},
|
|
||||||
tests: map[string]bool{"a": true, "b": false}}
|
|
||||||
s := r.steps()
|
|
||||||
s.Digest = nil
|
|
||||||
out, err := RunAsk(context.Background(), s, ask(delivery.CheckJob{Levels: []string{delivery.LevelUnit, delivery.LevelIntegration},
|
|
||||||
Fetch: map[string]any{"repository": "novox/x", "commit": runCommit}}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if a := out.(delivery.CheckAnswer); len(r.calls) != 5 || len(a.Levels) != 2 || len(a.Levels[0].Tests) != 2 {
|
|
||||||
t.Fatalf("calls %v, levels %+v: want b then a at each of the two levels", r.calls, a.Levels)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
package buildstep
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RegistryWait is how long a push waits for an artifact store that refuses connections, longer than its nightly
|
|
||||||
// collection holds it still (about a minute and a half, issue 457); RegistryFirstPause is the first pause, doubling
|
|
||||||
// to RegistryMostPause. Variables so a test shortens them.
|
|
||||||
var (
|
|
||||||
RegistryWait = 5 * time.Minute
|
|
||||||
RegistryFirstPause = time.Second
|
|
||||||
RegistryMostPause = 10 * time.Second
|
|
||||||
)
|
|
||||||
|
|
||||||
// BlobReference is where a pushed bundle or archive is, as a done key names it and the controller delivers from
|
|
||||||
// (novox/hq ADR 0498 clause 1): artifact-store://<module>/<artifact>/blobs/<digest>, the store's address left out
|
|
||||||
// (issue 102).
|
|
||||||
func BlobReference(module, artifact, digest string) string {
|
|
||||||
return "artifact-store://" + module + "/" + artifact + "/blobs/" + digest
|
|
||||||
}
|
|
||||||
|
|
||||||
// PushBlob puts body into the artifact store at registry (host:port) as a blob of repository (<module>/<artifact>) by
|
|
||||||
// its digest (`sha256:` and hex), from the build module's own process, after the build step ended (novox/hq ADR 0498
|
|
||||||
// clauses 1 and 2): a blob the store already holds is done without an upload; otherwise it is uploaded (POST
|
|
||||||
// blobs/uploads/, then PUT with the digest). A store refusing connections is waited out up to RegistryWait; any other
|
|
||||||
// refusal fails, naming the store's status and its own words, for the caller's `not stored:` error. Bytes that are not
|
|
||||||
// their digest are refused before anything is sent.
|
|
||||||
func PushBlob(ctx context.Context, client *http.Client, registry, repository, digest string, body []byte) error {
|
|
||||||
sum := sha256.Sum256(body)
|
|
||||||
if got := "sha256:" + hex.EncodeToString(sum[:]); got != digest {
|
|
||||||
return fmt.Errorf("the blob is %s, not the digest %s it was to be pushed as", got, digest)
|
|
||||||
}
|
|
||||||
if client == nil {
|
|
||||||
client = http.DefaultClient
|
|
||||||
}
|
|
||||||
base := "http://" + registry + "/v2/" + repository + "/blobs/"
|
|
||||||
var present bool
|
|
||||||
if err := waitForStore(ctx, registry, func() error {
|
|
||||||
var err error
|
|
||||||
present, err = blobPresent(ctx, client, base+digest)
|
|
||||||
return err
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if present {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var location string
|
|
||||||
if err := waitForStore(ctx, registry, func() error {
|
|
||||||
var err error
|
|
||||||
location, err = startUpload(ctx, client, base+"uploads/")
|
|
||||||
return err
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
target, err := url.Parse(base)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
loc, err := target.Parse(location)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("the store answered the upload's location %q, which is not a URL: %w", location, err)
|
|
||||||
}
|
|
||||||
q := loc.Query()
|
|
||||||
q.Set("digest", digest)
|
|
||||||
loc.RawQuery = q.Encode()
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPut, loc.String(), bytes.NewReader(body))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
req.Header.Set("Content-Type", "application/octet-stream")
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("putting %s of %s: %w", digest, repository, err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
if resp.StatusCode != http.StatusCreated {
|
|
||||||
return storeSaid(resp, "putting "+digest+" of "+repository)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func blobPresent(ctx context.Context, client *http.Client, at string) (bool, error) {
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodHead, at, nil)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
resp.Body.Close()
|
|
||||||
switch resp.StatusCode {
|
|
||||||
case http.StatusOK:
|
|
||||||
return true, nil
|
|
||||||
case http.StatusNotFound:
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
return false, fmt.Errorf("asking the store for %s: it answered %s", at, resp.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
func startUpload(ctx context.Context, client *http.Client, at string) (string, error) {
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, at, nil)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
if resp.StatusCode != http.StatusAccepted {
|
|
||||||
return "", storeSaid(resp, "starting an upload at "+at)
|
|
||||||
}
|
|
||||||
location := resp.Header.Get("Location")
|
|
||||||
if location == "" {
|
|
||||||
return "", fmt.Errorf("starting an upload at %s: the store named no location", at)
|
|
||||||
}
|
|
||||||
return location, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// storeSaid is the store's refusal: its status and the first of its own words.
|
|
||||||
func storeSaid(resp *http.Response, what string) error {
|
|
||||||
words, _ := io.ReadAll(io.LimitReader(resp.Body, 2048))
|
|
||||||
return fmt.Errorf("%s: the store answered %s: %s", what, resp.Status, strings.TrimSpace(string(words)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// refused is whether an error is a connection refused.
|
|
||||||
func refused(err error) bool {
|
|
||||||
return err != nil && (errors.Is(err, syscall.ECONNREFUSED) || strings.Contains(err.Error(), "connection refused"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// waitForStore runs try, and while it fails because the store refuses connections, tries again with a growing pause
|
|
||||||
// until RegistryWait has passed. A refusal is the one error waited for: nothing was sent, so trying again does nothing
|
|
||||||
// twice.
|
|
||||||
func waitForStore(ctx context.Context, registry string, try func() error) error {
|
|
||||||
err := try()
|
|
||||||
if !refused(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
started, pause := time.Now(), RegistryFirstPause
|
|
||||||
for {
|
|
||||||
left := RegistryWait - time.Since(started)
|
|
||||||
if left <= 0 {
|
|
||||||
return fmt.Errorf("the artifact store at %s refused every connection for %s, longer than its nightly collection holds it still, so it is down, not paused: %w",
|
|
||||||
registry, time.Since(started).Round(time.Millisecond), err)
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return fmt.Errorf("stopped while waiting for the artifact store at %s: %w (last: %v)", registry, ctx.Err(), err)
|
|
||||||
case <-time.After(min(pause, left)):
|
|
||||||
}
|
|
||||||
pause = min(pause*2, RegistryMostPause)
|
|
||||||
if err = try(); !refused(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
package buildstep
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
||||||
)
|
|
||||||
|
|
||||||
func digestOfBytes(b []byte) string {
|
|
||||||
h := sha256.Sum256(b)
|
|
||||||
return "sha256:" + hex.EncodeToString(h[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// registry is an artifact store as far as a blob push needs one: HEAD a blob, start an upload, PUT it with its digest.
|
|
||||||
type registry struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
blobs map[string][]byte
|
|
||||||
calls []string
|
|
||||||
put int // the status a PUT answers, 201 when 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *registry) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|
||||||
r.mu.Lock()
|
|
||||||
defer r.mu.Unlock()
|
|
||||||
r.calls = append(r.calls, req.Method+" "+req.URL.Path)
|
|
||||||
switch {
|
|
||||||
case req.Method == http.MethodHead && strings.Contains(req.URL.Path, "/blobs/sha256:"):
|
|
||||||
if _, ok := r.blobs[req.URL.Path[strings.LastIndex(req.URL.Path, "/")+1:]]; ok {
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
case req.Method == http.MethodPost && strings.HasSuffix(req.URL.Path, "/blobs/uploads/"):
|
|
||||||
w.Header().Set("Location", strings.TrimSuffix(req.URL.Path, "/")+"/u1?state=x")
|
|
||||||
w.WriteHeader(http.StatusAccepted)
|
|
||||||
case req.Method == http.MethodPut && strings.Contains(req.URL.Path, "/blobs/uploads/u1"):
|
|
||||||
if r.put != 0 {
|
|
||||||
w.WriteHeader(r.put)
|
|
||||||
_, _ = io.WriteString(w, `{"errors":[{"code":"DIGEST_INVALID","message":"provided digest did not match uploaded content"}]}`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
body, _ := io.ReadAll(req.Body)
|
|
||||||
d := req.URL.Query().Get("digest")
|
|
||||||
if req.URL.Query().Get("state") != "x" || d != digestOfBytes(body) {
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r.blobs[d] = body
|
|
||||||
w.WriteHeader(http.StatusCreated)
|
|
||||||
default:
|
|
||||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A done build's bundle or archive is pushed to the artifact store by its build module (novox/hq ADR 0498 clauses 1
|
|
||||||
// and 2): a blob the store already holds is done with no upload; one it lacks is uploaded by digest; its reference is
|
|
||||||
// the artifact-store form the builder's key and the controller check.
|
|
||||||
func TestABlobIsPushedOnceByItsDigest(t *testing.T) {
|
|
||||||
reg := ®istry{blobs: map[string][]byte{}}
|
|
||||||
srv := httptest.NewServer(reg)
|
|
||||||
defer srv.Close()
|
|
||||||
addr := strings.TrimPrefix(srv.URL, "http://")
|
|
||||||
body := []byte("the packed bundle")
|
|
||||||
d := digestOfBytes(body)
|
|
||||||
if err := PushBlob(context.Background(), srv.Client(), addr, "mail/code", d, body); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if string(reg.blobs[d]) != string(body) {
|
|
||||||
t.Fatalf("the store holds %q", reg.blobs[d])
|
|
||||||
}
|
|
||||||
reg.calls = nil
|
|
||||||
if err := PushBlob(context.Background(), srv.Client(), addr, "mail/code", d, body); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(reg.calls) != 1 || !strings.HasPrefix(reg.calls[0], "HEAD ") {
|
|
||||||
t.Fatalf("a blob the store holds was uploaded again: %v", reg.calls)
|
|
||||||
}
|
|
||||||
ref := BlobReference("mail", "code", d)
|
|
||||||
if ref != "artifact-store://mail/code/blobs/"+d {
|
|
||||||
t.Fatalf("BlobReference = %q", ref)
|
|
||||||
}
|
|
||||||
b := delivery.BuilderBuild{State: delivery.BuildDone, Manifest: strings.Repeat("4", 64),
|
|
||||||
Artifacts: []delivery.BuiltArtifact{{Name: "code", Kind: "bundle", Digest: d, Reference: ref}}}
|
|
||||||
if _, err := delivery.EncodeBuilderBuild(b); err != nil {
|
|
||||||
t.Fatalf("the key refuses the reference BlobReference gives: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A store that refuses the upload fails the push naming the store's own words; bytes that are not the digest are
|
|
||||||
// refused before anything is sent.
|
|
||||||
func TestAPushTheStoreRefusesSaysTheStoresWords(t *testing.T) {
|
|
||||||
reg := ®istry{blobs: map[string][]byte{}, put: http.StatusBadRequest}
|
|
||||||
srv := httptest.NewServer(reg)
|
|
||||||
defer srv.Close()
|
|
||||||
addr := strings.TrimPrefix(srv.URL, "http://")
|
|
||||||
body := []byte("x")
|
|
||||||
err := PushBlob(context.Background(), srv.Client(), addr, "mail/code", digestOfBytes(body), body)
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "provided digest did not match") || !strings.Contains(err.Error(), "400") {
|
|
||||||
t.Fatalf("a refused upload said %v", err)
|
|
||||||
}
|
|
||||||
reg.calls = nil
|
|
||||||
err = PushBlob(context.Background(), srv.Client(), addr, "mail/code", digestOfBytes([]byte("other")), body)
|
|
||||||
if err == nil || len(reg.calls) != 0 {
|
|
||||||
t.Fatalf("bytes that are not their digest: %v, calls %v", err, reg.calls)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A store refusing connections (its nightly collection holds it still) is waited out, up to RegistryWait (issue 457).
|
|
||||||
func TestAPushWaitsOutAStoreRefusingConnections(t *testing.T) {
|
|
||||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
addr := l.Addr().String()
|
|
||||||
l.Close() // refusing now
|
|
||||||
reg := ®istry{blobs: map[string][]byte{}}
|
|
||||||
go func() {
|
|
||||||
time.Sleep(300 * time.Millisecond)
|
|
||||||
l2, err := net.Listen("tcp", addr)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_ = http.Serve(l2, reg)
|
|
||||||
}()
|
|
||||||
beforeWait, beforePause := RegistryWait, RegistryFirstPause
|
|
||||||
RegistryWait, RegistryFirstPause = 3*time.Second, 100*time.Millisecond
|
|
||||||
defer func() { RegistryWait, RegistryFirstPause = beforeWait, beforePause }()
|
|
||||||
body := []byte("late")
|
|
||||||
if err := PushBlob(context.Background(), http.DefaultClient, addr, "mail/code", digestOfBytes(body), body); err != nil {
|
|
||||||
t.Fatalf("a store that came back was not waited for: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l3, _ := net.Listen("tcp", "127.0.0.1:0")
|
|
||||||
gone := l3.Addr().String()
|
|
||||||
l3.Close()
|
|
||||||
RegistryWait = 300 * time.Millisecond
|
|
||||||
err = PushBlob(context.Background(), http.DefaultClient, gone, "mail/code", digestOfBytes(body), body)
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "refused every connection") {
|
|
||||||
t.Fatalf("a store that stayed down said %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,215 +0,0 @@
|
|||||||
// Package buildcpu is how much of a build node's processors its builds may take, and how they yield to everything else
|
|
||||||
// there (novox/hq ADR 0553, issue 1806; the operator, 2026-10-26: "limit CPU usage on our build nodes so the total cpu
|
|
||||||
// usage never impacts the normal workings of our nodes ... We have 3 build agents so they should share the cpu power").
|
|
||||||
//
|
|
||||||
// Three parts, each with one owner:
|
|
||||||
//
|
|
||||||
// - **Priority.** A build seat's holder (catalogue.Seat.Yields) runs at the lowest priority there is: the composer
|
|
||||||
// gives its runtime process declaration.Yields, and the node-engine places every yielding process of the node in
|
|
||||||
// one slice (declaration.YieldSlice) whose CPU weight is idle, with Nice=19 for every process it starts.
|
|
||||||
// Interactive and service work always comes first; builds take the cycles nothing else wants.
|
|
||||||
// - **One cap per node, shared.** The node's share of its processors is one value, the node-engine's own config
|
|
||||||
// `build-cpu-share` (ShareSetting), set per node through the node's settings layer. The node-engine writes it as the
|
|
||||||
// slice's CPUQuota: one bound the kernel holds for all the node's build modules together, never one per module.
|
|
||||||
// - **Threads.** The quota bounds the total; threads only size each step. A step gets the share divided by its own
|
|
||||||
// module's capacity (Threads), from the share the composer hands as a word (ShareWord): GOMAXPROCS sets every go
|
|
||||||
// command's -p, the compiler's concurrency, a test binary's processors and -parallel. A module given no word sets
|
|
||||||
// no thread limit at all: without the slice there is nothing to size a step to, and a limit then would only slow
|
|
||||||
// builds.
|
|
||||||
package buildcpu
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ShareSetting is the node-engine's config value of the node's processor share for its builds, in percent of the
|
|
||||||
// machine's processors, 1 to 100: one value per node, set through the node's settings layer (novox/hq ADR 0553 §3).
|
|
||||||
const ShareSetting = "build-cpu-share"
|
|
||||||
|
|
||||||
// DefaultPercent is the share where nothing sets one: half the machine, so a desktop keeps half its processors for
|
|
||||||
// itself whatever builds.
|
|
||||||
const DefaultPercent = 50
|
|
||||||
|
|
||||||
// ShareWord is the word the composer gives every yielding module (novox/hq ADR 0553 §4): the node's share, in percent.
|
|
||||||
const ShareWord = "MESH_BUILD_CPU_SHARE"
|
|
||||||
|
|
||||||
// Quota is a CPUQuota for a share of a machine with cores processors: systemd counts a quota in percent of one
|
|
||||||
// processor, so half of 24 is 1200%. At least 100%, one processor, so a share on a small machine never stops a step.
|
|
||||||
func Quota(percent, cores int) int {
|
|
||||||
q := percent * cores
|
|
||||||
if q < 100 {
|
|
||||||
q = 100
|
|
||||||
}
|
|
||||||
return q
|
|
||||||
}
|
|
||||||
|
|
||||||
// Threads is how many threads one step may run at once: its part of the share when its own module runs slots steps at
|
|
||||||
// once, at least one. The slice's quota bounds the steps of all the node's build modules together; this only sizes one.
|
|
||||||
func Threads(percent, cores, slots int) int {
|
|
||||||
if slots < 1 {
|
|
||||||
slots = 1
|
|
||||||
}
|
|
||||||
if percent < 1 || percent > 100 {
|
|
||||||
percent = DefaultPercent
|
|
||||||
}
|
|
||||||
t := cores * percent / 100 / slots
|
|
||||||
if t < 1 {
|
|
||||||
t = 1
|
|
||||||
}
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
// Limits is what one step is held to on this node, as a status tool says it.
|
|
||||||
type Limits struct {
|
|
||||||
// Percent is the node's share in percent of its processors, and From where it was read: ShareWord, or "none" when
|
|
||||||
// the composer gave no share and a step runs without a thread limit.
|
|
||||||
Percent int `json:"share-percent,omitempty"`
|
|
||||||
From string `json:"share-from"`
|
|
||||||
// Cores is the machine's processor count.
|
|
||||||
Cores int `json:"cores"`
|
|
||||||
// Slots is this module's own capacity on the node: steps at once.
|
|
||||||
Slots int `json:"slots"`
|
|
||||||
// Threads is what each step is given (Threads); 0 is no limit.
|
|
||||||
Threads int `json:"threads-per-step"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromWords is the limits of a step from the share the composer gave this process (ShareWord) and the module's own
|
|
||||||
// capacity. Without a usable share there is no limit: Threads is 0.
|
|
||||||
func FromWords(getenv func(string) string, own int) Limits {
|
|
||||||
if own < 1 {
|
|
||||||
own = 1
|
|
||||||
}
|
|
||||||
l := Limits{From: "none", Cores: runtime.NumCPU(), Slots: own}
|
|
||||||
if n, err := strconv.Atoi(strings.TrimSpace(getenv(ShareWord))); err == nil && n >= 1 && n <= 100 {
|
|
||||||
l.Percent, l.From = n, ShareWord
|
|
||||||
l.Threads = Threads(n, l.Cores, own)
|
|
||||||
}
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
|
|
||||||
// Env is the environment a step's commands are given so they start no more threads than they may: GOMAXPROCS for every
|
|
||||||
// Go program (the compiler, a test binary, esbuild, buildah), UV_THREADPOOL_SIZE for Node's worker pool. Returned as
|
|
||||||
// KEY=value words to append to a command's environment; nothing for 0 threads, no limit.
|
|
||||||
func Env(threads int) []string {
|
|
||||||
if threads < 1 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
t := strconv.Itoa(threads)
|
|
||||||
return []string{"GOMAXPROCS=" + t, "UV_THREADPOOL_SIZE=" + t}
|
|
||||||
}
|
|
||||||
|
|
||||||
// InForce is what the kernel holds this process to, read rather than assumed (evidence over assertion): its nice
|
|
||||||
// level, its cgroup (the unit) and the cgroup's parent (the slice the node-engine placed it in), each one's CPU weight,
|
|
||||||
// idle mark and quota. A field the machine does not answer is left empty and Unread says which.
|
|
||||||
//
|
|
||||||
// The IO weight is read but binds only where the disk's scheduler honours it (bfq, or the io.cost controller); under
|
|
||||||
// the scheduler `none`, the usual one for NVMe, it does nothing, and the CPU bound is what holds.
|
|
||||||
type InForce struct {
|
|
||||||
Nice int `json:"nice"`
|
|
||||||
Cgroup string `json:"cgroup,omitempty"`
|
|
||||||
Unit Group `json:"unit"`
|
|
||||||
Slice Group `json:"slice"`
|
|
||||||
// Quota is the tighter of the two groups' cpu.max as a percent of one processor, 0 with none.
|
|
||||||
Quota int `json:"quota-percent,omitempty"`
|
|
||||||
Unread []string `json:"unread,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Group is one cgroup's controls.
|
|
||||||
type Group struct {
|
|
||||||
Path string `json:"path,omitempty"`
|
|
||||||
CPUIdle string `json:"cpu-idle,omitempty"`
|
|
||||||
CPUWeight string `json:"cpu-weight,omitempty"`
|
|
||||||
// CPUMax is the cgroup's cpu.max: "<quota µs> <period µs>", or "max" with no quota.
|
|
||||||
CPUMax string `json:"cpu-max,omitempty"`
|
|
||||||
IOWeight string `json:"io-weight,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// cgroupRoot is where the unified hierarchy is mounted. A variable so a test can point it elsewhere.
|
|
||||||
var cgroupRoot = "/sys/fs/cgroup"
|
|
||||||
|
|
||||||
// procSelfCgroup is this process's cgroup file. A variable so a test can point it elsewhere.
|
|
||||||
var procSelfCgroup = "/proc/self/cgroup"
|
|
||||||
|
|
||||||
// procSelfStat is this process's stat file. A variable so a test can point it elsewhere.
|
|
||||||
var procSelfStat = "/proc/self/stat"
|
|
||||||
|
|
||||||
// ReadInForce reads what the kernel holds this process to.
|
|
||||||
func ReadInForce() InForce {
|
|
||||||
var f InForce
|
|
||||||
f.Nice = nice()
|
|
||||||
raw, err := os.ReadFile(procSelfCgroup)
|
|
||||||
if err != nil {
|
|
||||||
f.Unread = append(f.Unread, "cgroup: "+err.Error())
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
for _, line := range strings.Split(strings.TrimSpace(string(raw)), "\n") {
|
|
||||||
if strings.HasPrefix(line, "0::") {
|
|
||||||
f.Cgroup = strings.TrimPrefix(line, "0::")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if f.Cgroup == "" {
|
|
||||||
f.Unread = append(f.Unread, "cgroup: not on the unified hierarchy")
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
read := func(path string) Group {
|
|
||||||
g := Group{Path: path}
|
|
||||||
value := func(name string) string {
|
|
||||||
b, err := os.ReadFile(cgroupRoot + path + "/" + name)
|
|
||||||
if err != nil {
|
|
||||||
f.Unread = append(f.Unread, path+"/"+name+": "+err.Error())
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(string(b))
|
|
||||||
}
|
|
||||||
g.CPUIdle, g.CPUWeight, g.CPUMax, g.IOWeight = value("cpu.idle"), value("cpu.weight"), value("cpu.max"), value("io.weight")
|
|
||||||
return g
|
|
||||||
}
|
|
||||||
f.Unit = read(f.Cgroup)
|
|
||||||
if i := strings.LastIndex(f.Cgroup, "/"); i > 0 {
|
|
||||||
f.Slice = read(f.Cgroup[:i])
|
|
||||||
}
|
|
||||||
for _, q := range []int{quotaOf(f.Unit.CPUMax), quotaOf(f.Slice.CPUMax)} {
|
|
||||||
if q > 0 && (f.Quota == 0 || q < f.Quota) {
|
|
||||||
f.Quota = q
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// quotaOf is a cpu.max value as a percent of one processor: "1200000 100000" is 1200; "max ..." or nothing is 0.
|
|
||||||
func quotaOf(cpuMax string) int {
|
|
||||||
fields := strings.Fields(cpuMax)
|
|
||||||
if len(fields) != 2 || fields[0] == "max" {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
q, err1 := strconv.Atoi(fields[0])
|
|
||||||
p, err2 := strconv.Atoi(fields[1])
|
|
||||||
if err1 != nil || err2 != nil || p <= 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return q * 100 / p
|
|
||||||
}
|
|
||||||
|
|
||||||
// nice is this process's nice level from its stat file (the 19th field, counted after the command's closing
|
|
||||||
// parenthesis, which may itself hold spaces); 0 when it cannot be read.
|
|
||||||
func nice() int {
|
|
||||||
raw, err := os.ReadFile(procSelfStat)
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
s := string(raw)
|
|
||||||
i := strings.LastIndex(s, ")")
|
|
||||||
if i < 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
fields := strings.Fields(s[i+1:])
|
|
||||||
// After the command: state is field 3, so nice (field 19) is index 16.
|
|
||||||
if len(fields) < 17 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
n, _ := strconv.Atoi(fields[16])
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package buildcpu
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The share's names the composer gives are the build modules' (catalogue writes them too, so it does not import this).
|
|
||||||
func TestTheShareNamesAgreeWithTheCatalogues(t *testing.T) {
|
|
||||||
if ShareSetting != catalogue.BuildCPUShareSetting || ShareWord != catalogue.BuildCPUShareWord || DefaultPercent != catalogue.BuildCPUDefaultPercent {
|
|
||||||
t.Fatal("buildcpu's share names differ from catalogue's")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
package buildcpu
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"reflect"
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The node's share as a quota, the threads one step is given for its own module's capacity, and the word the composer
|
|
||||||
// hands, in one table (ADR 0553, issue 1806).
|
|
||||||
func TestShareQuotaAndThreads(t *testing.T) {
|
|
||||||
for _, c := range []struct {
|
|
||||||
name string
|
|
||||||
percent, cores, slots int
|
|
||||||
quota, threads int
|
|
||||||
}{
|
|
||||||
{"shanks: half of 24, three slots", 50, 24, 3, 1200, 4},
|
|
||||||
{"ace: three quarters of 24, two slots", 75, 24, 2, 1800, 9},
|
|
||||||
{"g14: half of 16, one slot", 50, 16, 1, 800, 8},
|
|
||||||
{"one processor at least", 10, 2, 4, 100, 1},
|
|
||||||
{"no slots counts as one", 100, 4, 0, 400, 4},
|
|
||||||
{"a share out of range is the default", 0, 8, 1, 0, 4},
|
|
||||||
} {
|
|
||||||
if c.quota != 0 {
|
|
||||||
if q := Quota(c.percent, c.cores); q != c.quota {
|
|
||||||
t.Errorf("%s: Quota = %d, want %d", c.name, q, c.quota)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if th := Threads(c.percent, c.cores, c.slots); th != c.threads {
|
|
||||||
t.Errorf("%s: Threads = %d, want %d", c.name, th, c.threads)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
l := FromWords(func(k string) string { return map[string]string{ShareWord: "75"}[k] }, 2)
|
|
||||||
if l.Percent != 75 || l.From != ShareWord || l.Slots != 2 || l.Threads != Threads(75, runtime.NumCPU(), 2) {
|
|
||||||
t.Errorf("from the word: %+v", l)
|
|
||||||
}
|
|
||||||
// Told nothing usable, a step has no thread limit: nothing in its environment.
|
|
||||||
for _, word := range []string{"", "250", "lots"} {
|
|
||||||
l := FromWords(func(string) string { return word }, 3)
|
|
||||||
if l.Threads != 0 || l.From != "none" || Env(l.Threads) != nil {
|
|
||||||
t.Errorf("share %q: %+v, env %v", word, l, Env(l.Threads))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if got := Env(3); !reflect.DeepEqual(got, []string{"GOMAXPROCS=3", "UV_THREADPOOL_SIZE=3"}) {
|
|
||||||
t.Errorf("Env = %q", got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// What the kernel holds a process to is read from its unit's and its slice's cgroup and its stat file, not assumed.
|
|
||||||
func TestReadInForce(t *testing.T) {
|
|
||||||
dir := t.TempDir()
|
|
||||||
cg := filepath.Join(dir, "cg")
|
|
||||||
slice := filepath.Join(cg, "meshyield.slice")
|
|
||||||
unit := filepath.Join(slice, "mesh-build-go-runtime.service")
|
|
||||||
if err := os.MkdirAll(unit, 0o755); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
for at, files := range map[string]map[string]string{
|
|
||||||
slice: {"cpu.idle": "1\n", "cpu.weight": "0\n", "cpu.max": "1200000 100000\n", "io.weight": "default 1\n"},
|
|
||||||
unit: {"cpu.idle": "0\n", "cpu.weight": "100\n", "cpu.max": "max 100000\n", "io.weight": "default 100\n"},
|
|
||||||
} {
|
|
||||||
for name, v := range files {
|
|
||||||
if err := os.WriteFile(filepath.Join(at, name), []byte(v), 0o644); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self := filepath.Join(dir, "cgroup")
|
|
||||||
stat := filepath.Join(dir, "stat")
|
|
||||||
_ = os.WriteFile(self, []byte("0::/meshyield.slice/mesh-build-go-runtime.service\n"), 0o644)
|
|
||||||
// A command with a space and a parenthesis in it, then nice 19 in field 19.
|
|
||||||
_ = os.WriteFile(stat, []byte("42 (mesh (build) go) S 1 42 42 0 -1 4194560 1 0 0 0 0 0 0 0 39 19 8 0 1 2 3\n"), 0o644)
|
|
||||||
cgroupRoot, procSelfCgroup, procSelfStat = cg, self, stat
|
|
||||||
t.Cleanup(func() {
|
|
||||||
cgroupRoot, procSelfCgroup, procSelfStat = "/sys/fs/cgroup", "/proc/self/cgroup", "/proc/self/stat"
|
|
||||||
})
|
|
||||||
|
|
||||||
got := ReadInForce()
|
|
||||||
want := InForce{Nice: 19, Cgroup: "/meshyield.slice/mesh-build-go-runtime.service",
|
|
||||||
Unit: Group{Path: "/meshyield.slice/mesh-build-go-runtime.service", CPUIdle: "0", CPUWeight: "100",
|
|
||||||
CPUMax: "max 100000", IOWeight: "default 100"},
|
|
||||||
Slice: Group{Path: "/meshyield.slice", CPUIdle: "1", CPUWeight: "0", CPUMax: "1200000 100000",
|
|
||||||
IOWeight: "default 1"},
|
|
||||||
Quota: 1200}
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
|
||||||
t.Fatalf("ReadInForce = %+v\nwant %+v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user