Compare commits
59
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
237a534ca4 | ||
|
|
dd458cb155 | ||
|
|
6c09be4a70 | ||
|
|
448f1ef481 | ||
|
|
093967af9b | ||
|
|
be749c9513 | ||
|
|
4026392a6e | ||
|
|
d632071317 | ||
|
|
5fe7e3e6ba | ||
|
|
be24f5ac38 | ||
|
|
f2eb548204 | ||
|
|
e6de426303 | ||
|
|
5fab8f118e | ||
|
|
d5237091a6 | ||
|
|
1a35e5cf4d | ||
|
|
e2fad6a50a | ||
|
|
04ab9bc603 | ||
|
|
333ab9de22 | ||
|
|
e84b9d13c4 | ||
|
|
9c36d4edd4 | ||
|
|
4c137795a8 | ||
|
|
281f586621 | ||
|
|
843da56d09 | ||
|
|
2d34ff468b | ||
|
|
230230ce0e | ||
|
|
34ce2895dd | ||
|
|
0aa319154e | ||
|
|
90ff598207 | ||
|
|
1d1d5ec443 | ||
|
|
3112692def | ||
|
|
4915ff5d29 | ||
|
|
dd4c53eefb | ||
|
|
32dc74788e | ||
|
|
1fb32928ae | ||
|
|
0b2d393b11 | ||
|
|
134baf0e3b | ||
|
|
7c78fd2e0c | ||
|
|
9239177efd | ||
|
|
a1d98c2438 | ||
|
|
f469eb387a | ||
|
|
d221c63379 | ||
|
|
68e7a2ddbf | ||
|
|
ee75d4327c | ||
|
|
640455999e | ||
|
|
45084af849 | ||
|
|
7f4cc9e2ec | ||
|
|
06963203ff | ||
|
|
0697b2994e | ||
|
|
308d052ff6 | ||
|
|
1885f176f1 | ||
|
|
6bdf958cef | ||
|
|
4797790959 | ||
|
|
7aad491321 | ||
|
|
6118c790e6 | ||
|
|
6f781abb57 | ||
|
|
350aca05fb | ||
|
|
b83a1abe5f | ||
|
|
9c57f7afb3 | ||
|
|
2d2eb5dffb |
@@ -10,6 +10,9 @@
|
||||
// (`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
|
||||
// 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
|
||||
// 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,
|
||||
@@ -40,6 +43,9 @@ const (
|
||||
// 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])`)
|
||||
|
||||
// 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.
|
||||
func Refusals(root string) ([]string, error) {
|
||||
var out []string
|
||||
@@ -65,6 +71,26 @@ 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))
|
||||
}
|
||||
}
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -33,6 +33,9 @@ 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"`},
|
||||
{"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 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 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"}`}, ""},
|
||||
|
||||
@@ -2,4 +2,4 @@ module checks/credentialform
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
|
||||
+13
-2
@@ -111,13 +111,24 @@ func forgeRequirements(dir string) ([]requirement, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("its go.mod cannot be read: %s", lastLine(out))
|
||||
}
|
||||
var mod struct{ Require []requirement }
|
||||
var mod struct {
|
||||
Require []requirement
|
||||
Replace []struct{ Old, New requirement }
|
||||
}
|
||||
if err := json.Unmarshal(out, &mod); err != nil {
|
||||
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
|
||||
for _, r := range mod.Require {
|
||||
if strings.HasPrefix(r.Path, forge) {
|
||||
if strings.HasPrefix(r.Path, forge) && !local[r.Path] {
|
||||
on = append(on, r)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,3 +175,28 @@ 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 mesh-sdk's conformance kit (issue 1901, its thorough
|
||||
// Package failurekit refuses a build module whose tests do not run the build runner'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
|
||||
// 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
|
||||
@@ -71,7 +71,7 @@ func Judge(root string) ([]Judged, error) {
|
||||
}
|
||||
if !runs {
|
||||
refused = append(refused, fmt.Sprintf("it claims %s and serves the test step %s, and no test of it calls conformance.Run "+
|
||||
"(mesh-sdk go/buildstep/conformance): run the kit with the module's own parse path (issue 1901)", seat, test))
|
||||
"(mesh-build-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).
|
||||
@@ -81,7 +81,7 @@ func Judge(root string) ([]Judged, error) {
|
||||
}
|
||||
if !sweeps {
|
||||
refused = append(refused, fmt.Sprintf("it claims %s, and no test of it calls conformance.LeftRunning "+
|
||||
"(mesh-sdk go/buildstep/conformance): run the kit's left-running case with the module's own start sweep (issue 1890)", seat))
|
||||
"(mesh-build-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, "; ")
|
||||
out = append(out, j)
|
||||
@@ -121,7 +121,7 @@ func runsTheKit(dir, call string) (bool, error) {
|
||||
return err
|
||||
}
|
||||
found = strings.Contains(string(raw), call) &&
|
||||
strings.Contains(string(raw), `"git.novox.be/novox/mesh-sdk/go/buildstep/conformance"`)
|
||||
strings.Contains(string(raw), `"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep/conformance"`)
|
||||
return nil
|
||||
})
|
||||
return found, err
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
// cases is refused, and a module that claims no node-build-* seat is not judged.
|
||||
func TestABuildModuleWithATestStepMustRunTheKit(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
const imp = "package main\n\nimport \"git.novox.be/novox/mesh-sdk/go/buildstep/conformance\"\n\n"
|
||||
const imp = "package main\n\nimport \"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep/conformance\"\n\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
|
||||
for path, body := range map[string]string{
|
||||
|
||||
@@ -2,4 +2,4 @@ module checks/health
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
|
||||
@@ -2,4 +2,4 @@ module checks/manifests
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
)
|
||||
|
||||
// The tree's manifests are ones a judge can read, and no module is named twice. A row is a catalogue of its own: a
|
||||
@@ -48,3 +50,45 @@ func TestEveryManifestDecodesAndNamesOneModule(t *testing.T) {
|
||||
t.Fatalf("the catalogue holds manifests no judge can read:\n%s", strings.Join(real, "\n"))
|
||||
}
|
||||
}
|
||||
|
||||
// No manifest of this catalogue claims the seat mesh-catalog: the indexer of the delivery holds it, and the module that
|
||||
// held it before has dropped its claim (ADR 0604 §6). A row is a tree of its own: a manifest claiming it is named.
|
||||
func TestNoManifestClaimsTheSeatMeshCatalog(t *testing.T) {
|
||||
claimants := func(root string) []string {
|
||||
files, err := filepath.Glob(filepath.Join(root, "modules", "*", "module.json"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var out []string
|
||||
for _, f := range files {
|
||||
raw, err := os.ReadFile(f)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m, err := catalogue.Decode(raw)
|
||||
if err != nil {
|
||||
continue // Problems names it
|
||||
}
|
||||
for _, c := range m.Claims {
|
||||
if c.Name == "mesh-catalog" {
|
||||
out = append(out, m.Module)
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
row := t.TempDir()
|
||||
if err := os.MkdirAll(filepath.Join(row, "modules", "a"), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(row, "modules", "a", "module.json"),
|
||||
[]byte(`{"module":"a","version":"1","claims":[{"name":"mesh-catalog","scope":"mesh"}]}`), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := claimants(row); len(got) != 1 || got[0] != "a" {
|
||||
t.Fatalf("a manifest claiming the seat is named, got %v", got)
|
||||
}
|
||||
if got := claimants(filepath.Join("..", "..")); len(got) != 0 {
|
||||
t.Errorf("the catalogue's manifests %v claim the seat mesh-catalog: the indexer of the delivery holds it", got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,4 +2,4 @@ module checks/readable
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
module git.novox.be/novox/mesh-catalog/checks/removedpackages
|
||||
|
||||
go 1.22
|
||||
@@ -0,0 +1,166 @@
|
||||
// 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")
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# 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,13 +36,7 @@ import (
|
||||
// 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).
|
||||
var composition = map[string]string{
|
||||
"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",
|
||||
"catalogue/collections.go:itemTemplateProblem": "composition: filling an item's template",
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
@@ -61,7 +61,7 @@ record names leaves its platform manifests unrecorded with it.
|
||||
**What the definitions pin** (hq issue 323). A definition may pin an image of the store the mesh did not
|
||||
make: pushed before the mesh built it, and pinned under the store's current name or a name it had before.
|
||||
No record of the controller names it, yet a machine runs it, so `artifact_store_references` reads every
|
||||
definition the catalogue holds (`mesh-catalog.catalog_modules`, then `catalog_module` for each, eight at
|
||||
definition the catalogue holds (the seat mesh-catalog's verbs `modules`, then `module` for each, eight at
|
||||
a time and within twenty seconds) and finds every `<name>/<repository>@sha256:…` in any of its strings,
|
||||
under each of the store's names: `artifact-store://`, the store's address as the controller answers it,
|
||||
and the names it had before. Those are the mesh's setting `older-store-names` (space-separated, `none` by
|
||||
|
||||
@@ -23,10 +23,11 @@ import (
|
||||
// manifest's.
|
||||
const NamesFileEnv = "MESH_ARTIFACT_STORE_NAMES_FILE"
|
||||
|
||||
// CatalogueModules and CatalogueModule are the catalog's tools these definitions are read through.
|
||||
// CatalogueModules and CatalogueModule are the verbs of the seat mesh-catalog these definitions are read through
|
||||
// (ADR 0604 §3); the answers are catalogue.CatalogModules and catalogue.CatalogOne.
|
||||
const (
|
||||
CatalogueModules = "mesh-catalog.catalog_modules"
|
||||
CatalogueModule = "mesh-catalog.catalog_module"
|
||||
CatalogueModules = "seat:mesh-catalog.modules"
|
||||
CatalogueModule = "seat:mesh-catalog.module"
|
||||
)
|
||||
|
||||
// DefinitionsTimeout bounds reading every definition, below the runtime's thirty-second call limit; what
|
||||
@@ -279,11 +280,8 @@ func (c *Catalogue) definition(m catalogueEntry, names StoreNames, nameKey strin
|
||||
return pins, ""
|
||||
}
|
||||
}
|
||||
args := map[string]any{"module": m.Module}
|
||||
if m.Commit != "" {
|
||||
args["commit"] = m.Commit
|
||||
}
|
||||
raw, err := c.Ask(CatalogueModule, args)
|
||||
// The verb takes no commit: it answers the trunk's module, and the answer names the commit it is at.
|
||||
raw, err := c.Ask(CatalogueModule, map[string]any{"module": m.Module})
|
||||
if err != nil {
|
||||
return nil, "could not be asked: " + err.Error()
|
||||
}
|
||||
@@ -293,6 +291,7 @@ func (c *Catalogue) definition(m catalogueEntry, names StoreNames, nameKey strin
|
||||
}
|
||||
var one struct {
|
||||
Module *struct {
|
||||
Commit string `json:"commit"`
|
||||
Manifest json.RawMessage `json:"manifest"`
|
||||
} `json:"module"`
|
||||
Error string `json:"error"`
|
||||
@@ -301,9 +300,11 @@ func (c *Catalogue) definition(m catalogueEntry, names StoreNames, nameKey strin
|
||||
return nil, "not readable: " + firstOf(one.Error, errText(err), "no manifest in the answer")
|
||||
}
|
||||
pins := PinsIn(one.Module.Manifest, names)
|
||||
if m.Commit != "" {
|
||||
// Kept by the commit the answer names, which may be newer than the list's: a definition and its commit never
|
||||
// change together.
|
||||
if at := firstOf(one.Module.Commit, m.Commit); at != "" {
|
||||
c.mu.Lock()
|
||||
c.kept[key] = pins
|
||||
c.kept[m.Module+"@"+at+"|"+nameKey] = pins
|
||||
c.mu.Unlock()
|
||||
}
|
||||
return pins, ""
|
||||
|
||||
@@ -211,7 +211,7 @@ func TestReferencesReadTheDefinitionsUnderTheStoresOlderNames(t *testing.T) {
|
||||
}
|
||||
}
|
||||
if before != 2 || after != 2 {
|
||||
t.Errorf("asked catalog_module %d times, then %d: each definition at its commit is read once", before, after)
|
||||
t.Errorf("asked the seat's module %d times, then %d: each definition at its commit is read once", before, after)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -366,8 +366,8 @@ func (a *asked) ask(key string, body any) (json.RawMessage, error) {
|
||||
if a.failing[name] {
|
||||
return nil, fmt.Errorf("no answer")
|
||||
}
|
||||
if args["commit"] != "c-"+name {
|
||||
return nil, fmt.Errorf("asked %s at %v", name, args["commit"])
|
||||
if _, has := args["commit"]; has {
|
||||
return nil, fmt.Errorf("asked %s at %v: the seat's verb takes no commit", name, args["commit"])
|
||||
}
|
||||
manifest, has := a.definitions[name]
|
||||
if !has {
|
||||
@@ -375,7 +375,8 @@ func (a *asked) ask(key string, body any) (json.RawMessage, error) {
|
||||
break
|
||||
}
|
||||
// The catalogue's tool answers through the runtime as the protocol's content list.
|
||||
raw, _ := json.Marshal(map[string]any{"module": map[string]any{"module": name, "manifest": json.RawMessage(manifest)}})
|
||||
raw, _ := json.Marshal(catalogue.CatalogOne{Module: catalogue.CatalogModule{
|
||||
CatalogEntry: catalogue.CatalogEntry{Module: name, Commit: "c-" + name}, Manifest: json.RawMessage(manifest)}})
|
||||
return json.Marshal(map[string]any{"content": []map[string]any{{"type": "text", "text": string(raw)}}})
|
||||
case "collect":
|
||||
if args["confirm"] == "true" {
|
||||
|
||||
@@ -3,7 +3,7 @@ module artifactstoretools
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103221822-85ba0a9a70d8
|
||||
github.com/nats-io/nats.go v1.54.0
|
||||
)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103221822-85ba0a9a70d8 h1:XEbIVImp6yGjA91rp0iZBoDdL5KfamjOuDLwWvqh0hc=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103221822-85ba0a9a70d8/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
|
||||
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
],
|
||||
"invokes": [
|
||||
"seat:mesh-hand-acts.hand-act",
|
||||
"mesh-catalog.catalog_modules",
|
||||
"mesh-catalog.catalog_module"
|
||||
"seat:mesh-catalog.modules",
|
||||
"seat:mesh-catalog.module"
|
||||
],
|
||||
"reads": [
|
||||
"mesh-delivery-assigner.wants",
|
||||
|
||||
@@ -2,7 +2,7 @@ module asuszephyrusg14
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
@@ -2,7 +2,7 @@ module avahi
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
@@ -2,7 +2,7 @@ module blueman
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
@@ -2,7 +2,7 @@ module bluetooth
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
@@ -3,7 +3,7 @@ module brother
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
github.com/nats-io/nats-server/v2 v2.11.17
|
||||
github.com/nats-io/nats.go v1.54.0
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
|
||||
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
|
||||
|
||||
@@ -2,7 +2,7 @@ module claude-code
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require gopkg.in/yaml.v3 v3.0.1
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
@@ -3,7 +3,7 @@ module claude-licence-manager
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
github.com/jackc/pgx/v5 v5.11.0
|
||||
)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
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
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ module cups
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
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.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ module dbus
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
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.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
|
||||
@@ -2,7 +2,7 @@ module deskchannel
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
@@ -33,7 +33,7 @@ All six only read.
|
||||
| `disk_filesystems` | Each filesystem that stores data, said once however many places it is mounted: size, used and free GiB, used % as `df` counts it, inodes used % | `df -h`, `df -i` |
|
||||
| `disk_devices` | Each disk without sampling: model, size, whether it spins, scheduler, queue limit, what it is built on, and since boot the GiB read and written, the busy share and the wait per read and per write | `lsblk`, `cat /proc/diskstats` |
|
||||
| `disk_folders` | Which folders fill a disk, like `du -x`: the largest folders under `path` (default `/`) to `depth` levels (default 2, at most 3), `limit` of them (default 20, at most 100), with bytes and GiB. Sizes are allocated bytes, a hard link once. It stays on the path's one filesystem: a mount below it is named, never entered. It reads every folder the tool runner can read (docker's included) and no content; folders it cannot read are counted and named. After 25 s (the tool runner gives a call 30 s) it answers what it has measured, marked `partial`, with `stopped_at` (novox/hq issue 1804) | `du -x`, `ncdu` |
|
||||
| `cpu_top` | Samples a window (`seconds`, default 3, at most 10). It answers the machine's CPU in use (% of all CPUs and as CPUs busy, from `/proc/stat`), then `by=unit` (the default) ranks systemd units and containers by CPU, from their cgroups' cpu.stat, busiest first (`limit`, default 15), with the busy time no unit accounts for; `by=container` ranks containers by name | `top`, `systemd-cgtop` |
|
||||
| `cpu_top` | Samples a window (`seconds`, default 3, at most 10). It answers the machine's CPU in use (% of all CPUs and as CPUs busy, from `/proc/stat`), then `by=unit` (the default) ranks systemd units and containers by CPU, from their cgroups' cpu.stat, busiest first (`limit`, default 15), with the busy time no unit accounts for; `by=container` ranks containers by name With `unit` (a unit name such as `session-c1.scope`, or a cgroup path) it ranks that unit's processes instead (pid, command, cpu_pct, share_pct of the unit's busy time, threads), from `/proc/<pid>/stat` | `top`, `systemd-cgtop` |
|
||||
|
||||
### How each number is read
|
||||
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// clockTicks is USER_HZ, the unit of utime and stime in /proc/<pid>/stat: 100 on every Linux the mesh runs.
|
||||
const clockTicks = 100.0
|
||||
|
||||
// ProcessCPU is one process of a unit and its share of CPU over the window (cpu_top with unit set).
|
||||
type ProcessCPU struct {
|
||||
PID int `json:"pid"`
|
||||
Command string `json:"command"` // comm, then the first 120 characters of the command line
|
||||
CPUPct float64 `json:"cpu_pct"` // % of one CPU: 100 = one CPU busy the whole window
|
||||
SharePct float64 `json:"share_pct"` // this process's share of the unit's busy time
|
||||
Threads int `json:"threads"`
|
||||
raw float64
|
||||
}
|
||||
|
||||
// procTicks is a process's utime+stime ticks and thread count from its /proc/<pid>/stat, whose comm
|
||||
// field may hold spaces and parentheses, so the fields are counted from the last ")".
|
||||
func parseProcStat(s string) (comm string, ticks uint64, threads int, start string, ok bool) {
|
||||
l, r := strings.Index(s, "("), strings.LastIndex(s, ")")
|
||||
if l < 0 || r < l {
|
||||
return "", 0, 0, "", false
|
||||
}
|
||||
f := strings.Fields(s[r+1:])
|
||||
if len(f) < 20 {
|
||||
return "", 0, 0, "", false
|
||||
}
|
||||
ut, e1 := strconv.ParseUint(f[11], 10, 64)
|
||||
st, e2 := strconv.ParseUint(f[12], 10, 64)
|
||||
th, e3 := strconv.Atoi(f[17])
|
||||
if e1 != nil || e2 != nil || e3 != nil {
|
||||
return "", 0, 0, "", false
|
||||
}
|
||||
return s[l+1 : r], ut + st, th, f[19], true
|
||||
}
|
||||
|
||||
// cgroupOfUnit is the cgroup path (relative to /sys/fs/cgroup) a unit name or a path names.
|
||||
func (m *Machine) cgroupOfUnit(unit string) (string, error) {
|
||||
base := m.path("/sys/fs/cgroup")
|
||||
if strings.Contains(unit, "/") {
|
||||
rel := strings.Trim(strings.TrimPrefix(unit, "/sys/fs/cgroup"), "/")
|
||||
if strings.Contains(rel, "..") || filepath.Clean("/"+rel) != "/"+rel {
|
||||
return "", fmt.Errorf("cgroup %q is not a path under /sys/fs/cgroup", unit)
|
||||
}
|
||||
if rel == "" || !m.exists("/sys/fs/cgroup/"+rel) {
|
||||
return "", fmt.Errorf("no cgroup %q", unit)
|
||||
}
|
||||
return rel, nil
|
||||
}
|
||||
var found []string
|
||||
filepath.WalkDir(base, func(p string, d fs.DirEntry, err error) error {
|
||||
if err == nil && d.IsDir() && d.Name() == unit {
|
||||
rel, _ := filepath.Rel(base, p)
|
||||
found = append(found, filepath.ToSlash(rel))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
switch len(found) {
|
||||
case 0:
|
||||
return "", fmt.Errorf("no unit %q in the cgroup tree", unit)
|
||||
case 1:
|
||||
return found[0], nil
|
||||
}
|
||||
return "", fmt.Errorf("unit %q is %d cgroups (%s): give the cgroup path", unit, len(found), strings.Join(found, ", "))
|
||||
}
|
||||
|
||||
type procReading struct {
|
||||
ticks uint64
|
||||
comm string
|
||||
threads int
|
||||
pid int
|
||||
start string // starttime, to tell a reused pid
|
||||
}
|
||||
|
||||
// unitProcs reads every process in a cgroup and its child cgroups: its ticks, name and threads.
|
||||
func (m *Machine) unitProcs(cgroup string) map[int]procReading {
|
||||
out := map[int]procReading{}
|
||||
base := m.path("/sys/fs/cgroup/" + cgroup)
|
||||
filepath.WalkDir(base, func(p string, d fs.DirEntry, err error) error {
|
||||
if err != nil || d.IsDir() || d.Name() != "cgroup.procs" {
|
||||
return nil
|
||||
}
|
||||
for _, f := range strings.Fields(m.read(strings.TrimPrefix(p, filepath.Clean(m.Root)))) {
|
||||
pid, err := strconv.Atoi(f)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
comm, ticks, th, start, ok := parseProcStat(m.read("/proc/" + f + "/stat"))
|
||||
if ok {
|
||||
out[pid] = procReading{ticks: ticks, comm: comm, threads: th, pid: pid, start: start}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return out
|
||||
}
|
||||
|
||||
// commandLine is a process's program path (argv[0], cut to 120 characters), never its arguments:
|
||||
// they can carry a password.
|
||||
func (m *Machine) commandLine(pid int) string {
|
||||
s, _, _ := strings.Cut(m.read("/proc/"+strconv.Itoa(pid)+"/cmdline"), "\x00")
|
||||
s, _, _ = strings.Cut(s, " ") // a process that rewrites its title puts its whole command line here
|
||||
s, _, _ = strings.Cut(s, "\t")
|
||||
s = strings.TrimSpace(s)
|
||||
if r := []rune(s); len(r) > 120 {
|
||||
s = string(r[:120])
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// topProcessCPU ranks a unit's processes by CPU between two readings; a process missing from either
|
||||
// reading (started or ended in the window) is skipped.
|
||||
func (m *Machine) topProcessCPU(a, b map[int]procReading, window time.Duration, limit int) []ProcessCPU {
|
||||
var out []ProcessCPU
|
||||
var sum float64
|
||||
for pid, end := range b {
|
||||
start, ok := a[pid]
|
||||
if !ok || end.start != start.start || end.ticks <= start.ticks {
|
||||
continue
|
||||
}
|
||||
pct := float64(end.ticks-start.ticks) / clockTicks / window.Seconds() * 100
|
||||
sum += pct
|
||||
out = append(out, ProcessCPU{PID: pid, Command: end.comm, Threads: end.threads, raw: pct})
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool {
|
||||
if out[i].raw != out[j].raw {
|
||||
return out[i].raw > out[j].raw
|
||||
}
|
||||
return out[i].PID < out[j].PID
|
||||
})
|
||||
if len(out) > limit {
|
||||
out = out[:limit]
|
||||
}
|
||||
for i := range out {
|
||||
out[i].CPUPct = round1(out[i].raw)
|
||||
out[i].SharePct = round1(out[i].raw / sum * 100)
|
||||
if cl := m.commandLine(out[i].PID); cl != "" {
|
||||
out[i].Command += ": " + cl
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -180,3 +180,52 @@ func TestCPUTopKeepsCgroupsWithTheSameUnitNameAsSeparateRows(t *testing.T) {
|
||||
t.Errorf("the user session's init.scope: %+v", byCgroup)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCPUTopRanksAUnitsProcesses is the unit argument: a session scope with a child cgroup, ranked by
|
||||
// the utime+stime each process used over a 2 s window; one process ends, one starts, one comm holds ")".
|
||||
func TestCPUTopRanksAUnitsProcesses(t *testing.T) {
|
||||
f := aCPUHog(t)
|
||||
f.file("/sys/fs/cgroup/user.slice/session-c1.scope/cgroup.procs", "10\n11\n12\n")
|
||||
f.file("/sys/fs/cgroup/user.slice/session-c1.scope/app/cgroup.procs", "13\n")
|
||||
st := func(pid int, comm string, ticks, threads int) {
|
||||
f.then("/proc/"+itoa(pid)+"/stat", itoa(pid)+" ("+comm+") S 1 1 1 0 -1 0 0 0 0 0 "+itoa(ticks)+" 0 0 0 20 0 "+itoa(threads)+" 0 1 1 1\n")
|
||||
}
|
||||
for pid, c := range map[int][2]string{10: {"firefox", "1"}, 11: {"sh (x)", "1"}, 12: {"gone", "1"}} {
|
||||
f.file("/proc/"+itoa(pid)+"/stat", itoa(pid)+" ("+c[0]+") S 1 1 1 0 -1 0 0 0 0 0 0 0 0 0 20 0 "+c[1]+" 0 1 1 1\n")
|
||||
}
|
||||
f.file("/proc/13/stat", "13 (deep) S 1 1 1 0 -1 0 0 0 0 0 0 0 0 0 20 0 4 0 1 1 1\n")
|
||||
f.file("/proc/10/cmdline", "/usr/bin/firefox\x00--new-window\x00")
|
||||
f.file("/proc/11/cmdline", "mysql\x00--password\x00hunter2\x00")
|
||||
f.file("/proc/13/cmdline", "/usr/bin/tool --password hunter2 -x") // a rewritten title: one string, spaces, no NUL
|
||||
st(10, "firefox", 150, 40) // 75 % of one CPU over 2 s
|
||||
st(11, "sh (x)", 40, 1) // 20 %
|
||||
st(13, "deep", 10, 4) // 5 %, in the child cgroup
|
||||
f.then("/sys/fs/cgroup/user.slice/session-c1.scope/cgroup.procs", "10\n11\n14\n")
|
||||
f.file("/sys/fs/cgroup/user.slice/session-c1.scope/app/cgroup.procs", "13\n15\n")
|
||||
f.file("/proc/15/stat", "15 (old) S 1 1 1 0 -1 0 0 0 0 0 0 0 0 0 20 0 1 0 1 1 1\n")
|
||||
f.then("/proc/15/stat", "15 (reused) S 1 1 1 0 -1 0 0 0 0 0 900 0 0 0 20 0 1 0 77 1 1\n") // new starttime: another process
|
||||
st(14, "newborn", 500, 1) // not in the first reading: skipped
|
||||
|
||||
out, err := tool(t, f.machine(), "cpu_top").Run(map[string]any{"unit": "session-c1.scope", "seconds": 2.0, "limit": 3.0})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
top := out.(map[string]any)["top"].([]ProcessCPU)
|
||||
if len(top) != 3 || top[2].PID != 13 || top[2].CPUPct != 5 || top[0].PID != 10 || top[0].CPUPct != 75 || top[0].SharePct != 75 || top[0].Threads != 40 ||
|
||||
top[0].Command != "firefox: /usr/bin/firefox" || top[1].PID != 11 || top[1].CPUPct != 20 || top[1].Command != "sh (x): mysql" || top[2].Command != "deep: /usr/bin/tool" {
|
||||
t.Errorf("processes of the unit: %+v", top)
|
||||
}
|
||||
for _, p := range top {
|
||||
if p.PID == 15 || strings.Contains(p.Command, "hunter2") {
|
||||
t.Errorf("a reused pid was counted, or an argument leaked: %+v", p)
|
||||
}
|
||||
}
|
||||
for _, u := range []string{"../../etc", "/sys/fs/cgroup/../../proc", "user.slice/../.."} {
|
||||
if _, err := tool(t, f.machine(), "cpu_top").Run(map[string]any{"unit": u}); err == nil {
|
||||
t.Errorf("cgroup path %q escaping the root was accepted", u)
|
||||
}
|
||||
}
|
||||
if _, err := tool(t, f.machine(), "cpu_top").Run(map[string]any{"unit": "nothing.scope"}); err == nil {
|
||||
t.Error("an unknown unit was accepted")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,11 +90,14 @@ func Tools(m *Machine) []stdio.Tool {
|
||||
Description: "What used the most CPU over a few seconds, from each unit's cgroup cpu.stat, which needs no root: the machine's " +
|
||||
"CPU in use (as % of all CPUs and as CPUs busy, from /proc/stat), then by=unit (the default) each systemd unit and " +
|
||||
"container ranked by CPU, busiest first, with the busy time no unit accounts for; by=container only the containers, " +
|
||||
"by name. Replaces top, systemd-cgtop. (r)",
|
||||
"by name. With unit (a systemd unit such as session-c1.scope, or its cgroup path) it ranks that unit's processes instead, CPU by process, the top process: " +
|
||||
"pid, command, cpu_pct, share_pct of the unit's busy time and threads, from /proc/<pid>/stat, needing no root; a desktop session is one unit, " +
|
||||
"and this names the program in it. Replaces top, systemd-cgtop. (r)",
|
||||
Input: map[string]any{
|
||||
"seconds": map[string]any{"type": "integer", "description": "how long to sample, in seconds (default 3, at most 10)"},
|
||||
"limit": map[string]any{"type": "integer", "description": "how many units or containers to name (default 15, at most 50)"},
|
||||
"by": map[string]any{"type": "string", "enum": []string{"unit", "container"}, "description": "what to rank (default unit)"},
|
||||
"unit": map[string]any{"type": "string", "description": "a systemd unit name (session-c1.scope) or cgroup path: rank that unit's processes by CPU instead"},
|
||||
},
|
||||
Run: func(args map[string]any) (any, error) {
|
||||
n, err := count(args, "seconds", 3, 1, 10)
|
||||
@@ -118,6 +121,25 @@ func Tools(m *Machine) []stdio.Tool {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if unit := text(args, "unit"); unit != "" {
|
||||
cg, err := m.cgroupOfUnit(unit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pa := m.unitProcs(cg)
|
||||
m.Wait(w)
|
||||
bc, err := parseCPU(m.read("/proc/stat"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return map[string]any{
|
||||
"window_s": w.Seconds(),
|
||||
"by": "process",
|
||||
"cgroup": cg,
|
||||
"cpu": machineCPUOver(ac, bc),
|
||||
"top": m.topProcessCPU(pa, m.unitProcs(cg), w, limit),
|
||||
}, nil
|
||||
}
|
||||
a := m.unitCPU()
|
||||
m.Wait(w)
|
||||
bc, err := parseCPU(m.read("/proc/stat"))
|
||||
|
||||
@@ -3,7 +3,7 @@ module diskload
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
github.com/nats-io/nats.go v1.54.0
|
||||
)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
|
||||
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
|
||||
|
||||
@@ -2,7 +2,7 @@ module dmenu
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
@@ -2,7 +2,7 @@ module dnsmasq
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
@@ -2,7 +2,7 @@ module docker-compose
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
@@ -2,7 +2,7 @@ module docker
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
@@ -2,7 +2,7 @@ module dunst
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
@@ -2,7 +2,7 @@ module fail2ban
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ module feh
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
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.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
@@ -2,7 +2,7 @@ module flatpak
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
@@ -2,7 +2,7 @@ module fonts
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
@@ -2,7 +2,7 @@ module forticlient
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
+25
-4
@@ -559,8 +559,14 @@ 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
|
||||
* 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. Reads only. */
|
||||
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>> }> {
|
||||
* see, since a directory left out would be taken for one without a module. With `files`, a list of directories (the
|
||||
* root as `""`), the answer also carries, of each, every blob under it, recursively, as its path relative to the
|
||||
* 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 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[] = [];
|
||||
@@ -587,7 +593,22 @@ 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}`);
|
||||
trees[dir] = tree;
|
||||
}
|
||||
if (paths === undefined) return { root, trees };
|
||||
let filesOf: Record<string, Record<string, string>> | undefined;
|
||||
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
|
||||
// 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.
|
||||
@@ -602,7 +623,7 @@ export class GiteaClient {
|
||||
named[dir][p] = id;
|
||||
}
|
||||
}
|
||||
return { root, trees, paths: named };
|
||||
return filesOf === undefined ? { root, trees, paths: named } : { root, trees, paths: named, files: filesOf };
|
||||
}
|
||||
|
||||
/** 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
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
github.com/nats-io/nats-server/v2 v2.11.17
|
||||
github.com/nats-io/nats.go v1.54.0
|
||||
golang.org/x/crypto v0.57.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
|
||||
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
|
||||
|
||||
@@ -221,3 +221,58 @@ test("gitea_commit_trees takes paths as {directory: [paths]} and refuses another
|
||||
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,6 +79,17 @@ function namedPaths(raw: unknown): Record<string, string[]> | undefined {
|
||||
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[] {
|
||||
return namesChecked(allGiteaTools(gitea, admin, source));
|
||||
}
|
||||
@@ -722,19 +733,20 @@ function allGiteaTools(gitea: GiteaClient, admin: (() => GiteaAdmin) | undefined
|
||||
},
|
||||
{
|
||||
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 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 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.",
|
||||
input: {
|
||||
owner: { type: "string", description: "the repository owner" },
|
||||
repo: { type: "string", description: "the repository name" },
|
||||
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" },
|
||||
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) => {
|
||||
const owner = String(args.owner ?? "").trim(), repo = String(args.repo ?? "").trim();
|
||||
const sha = String(args.sha ?? "").trim();
|
||||
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");
|
||||
return gitea.commitTrees(owner, repo, sha, namedPaths(args.paths));
|
||||
return gitea.commitTrees(owner, repo, sha, namedPaths(args.paths), filesDirs(args.files));
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@ module gnomekeyring
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
@@ -2,7 +2,7 @@ module hostname
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ module i3
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
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.20261102112556-abf5eace7791 h1:7SYIDPMxbPBr8dMB6k9fYsgVt4ZyWVL41q4LMFsfAvs=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
@@ -2,7 +2,7 @@ module i3statusrust
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
@@ -2,7 +2,7 @@ module kernel
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
@@ -2,7 +2,7 @@ module keycloak
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
@@ -2,7 +2,7 @@ module lemurs
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
@@ -2,7 +2,7 @@ module localization
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
@@ -2,7 +2,7 @@ module logrotate
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102112556-abf5eace7791
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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.20261102112556-abf5eace7791/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
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.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
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=
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user