From 0bdbdd877295059722b00ad794dde907b83d3f63 Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 22 Oct 2026 21:22:01 +0200 Subject: [PATCH] Cover every module in the particulars check, and tell the hardware model and the software from a node (issue 1838) A module is covered from its first commit, so no list of covered modules can lag behind the catalogue (1838/AC1). asus-zephyrus-g14, ROG Zephyrus G14, Novox Mesh and novox-mesh hold a node name and are not one (1838/AC2). --- checks/particulars/covered | 3 -- checks/particulars/main.go | 57 ++++++++++++++++++++++--------- checks/particulars/main_test.go | 59 +++++++++++++++++++++++++++++++-- 3 files changed, 98 insertions(+), 21 deletions(-) delete mode 100644 checks/particulars/covered diff --git a/checks/particulars/covered b/checks/particulars/covered deleted file mode 100644 index 9ded26c5..00000000 --- a/checks/particulars/covered +++ /dev/null @@ -1,3 +0,0 @@ -# The modules the check covers (novox/hq ADR 0507 §9, issue 1838), one a line; issue 1838 widens it. -pacman -kernel diff --git a/checks/particulars/main.go b/checks/particulars/main.go index 5203f1f6..48b75108 100644 --- a/checks/particulars/main.go +++ b/checks/particulars/main.go @@ -1,12 +1,14 @@ // Command particulars keeps this mesh's node names out of the modules it covers (novox/hq ADR 0507 §9, // issue 1838): a module is for any mesh, so no manifest, file, code string, identifier, comment, test name or -// fixture of a covered module names a node of this one. +// fixture of any module names a node of this one. // // The names are in the file `particulars` at the catalogue's root, one a line, `#` comments — the one place -// they may be written. The modules covered are in `covered` at checks/particulars/covered, one a line. A name is matched -// whole and in any case; a hyphen, an underscore or a CamelCase boundary parts one, so a longer word holding a name is another word. -// The forge is not a node name: its host (git.novox.be) and the organisation path (novox/) are blanked -// before a line is read. +// they may be written. Every directory under modules/ is read, with no list of covered modules, so a new module +// is covered from its first commit. A name is matched whole and in any case; a hyphen, an underscore or a +// CamelCase boundary parts one, so a longer word holding a name is another word. +// Three names hold a node name and are not one, so they are blanked before a line is read: the forge's host +// (git.novox.be) and organisation path (novox/), the hardware model (asus-zephyrus-g14, ROG Zephyrus G14) +// and the software's own name (Novox Mesh, novox-mesh). // // go run . // @@ -29,6 +31,11 @@ import ( // (testdata/novox/x, /srv/novox/data) is not blanked. var forge = regexp.MustCompile(`(?i)git\.novox\.be(?:/novox/[\w.-]+)?|(?:^|[^/\w.-])novox/[\w.-]+`) +// ownNames are the hardware model's and the software's own names, blanked before a line is read: the model +// in any separator and case (asus-zephyrus-g14, ASUS_ZEPHYRUS_G14, ROG Zephyrus G14), the software as two +// words or hyphened, whole (Novox Mesh, novox mesh, novox-mesh), so novox-mesh-vault stays a node name joined. +var ownNames = regexp.MustCompile(`(?i)(?:asus[-_ ])?zephyrus[-_ ]g14|(?:^|[^\w-])novox[- ]mesh(?:$|[^\w-])`) + func main() { root := "." if len(os.Args) > 1 { @@ -43,18 +50,18 @@ func check(root string) int { if err == nil && len(names) == 0 { err = fmt.Errorf("particulars lists no name — the check would pass on anything") } - var covered []string + var modules []string if err == nil { - covered, err = readList(filepath.Join(root, "checks/particulars/covered")) - if err == nil && len(covered) == 0 { - err = fmt.Errorf("covered lists no module — the check would pass on anything") + modules, err = moduleDirs(root) + if err == nil && len(modules) == 0 { + err = fmt.Errorf("modules/ holds no module — the check would pass on anything") } } if err != nil { fmt.Fprintln(os.Stderr, "particulars:", err) return 2 } - findings, err := run(root, names, covered) + findings, err := run(root, names, modules) if err != nil { fmt.Fprintln(os.Stderr, "particulars:", err) return 2 @@ -66,7 +73,7 @@ func check(root string) int { fmt.Printf("particulars: %d node name(s) in a module for any mesh (novox/hq ADR 0507 §9) — say what it shows, not where it was seen\n", len(findings)) return 1 } - fmt.Printf("particulars: none of %d node names in the %d covered modules\n", len(names), len(covered)) + fmt.Printf("particulars: none of %d node names in the %d modules\n", len(names), len(modules)) return 0 } @@ -89,10 +96,25 @@ func readList(path string) ([]string, error) { return out, sc.Err() } -// run reads every text file of each covered module and returns the findings, sorted. -func run(root string, names, covered []string) ([]string, error) { +// moduleDirs returns every directory under the catalogue's modules/, sorted. +func moduleDirs(root string) ([]string, error) { + entries, err := os.ReadDir(filepath.Join(root, "modules")) + if err != nil { + return nil, err + } var out []string - for _, m := range covered { + for _, e := range entries { + if e.IsDir() { + out = append(out, e.Name()) + } + } + return out, nil +} + +// run reads every text file of each module named and returns the findings, sorted. +func run(root string, names, modules []string) ([]string, error) { + var out []string + for _, m := range modules { dir := filepath.Join(root, "modules", m) err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { if err != nil { @@ -143,9 +165,12 @@ func scan(file, src string, names []string) []string { } // hits returns the node names one line holds: as a word (a hyphen or underscore parts it), as a part of a -// CamelCase identifier (TestAce, novoxTree), the forge's host and organisation path blanked first. +// CamelCase identifier (TestAce, novoxTree), the forge's host and organisation path and the hardware's and the +// software's own names blanked first. func hits(line string, names []string) []string { - line = forge.ReplaceAllStringFunc(line, func(s string) string { return strings.Repeat(" ", len(s)) }) + blank := func(s string) string { return strings.Repeat(" ", len(s)) } + line = forge.ReplaceAllStringFunc(line, blank) + line = ownNames.ReplaceAllStringFunc(line, blank) var out []string for _, n := range names { q := regexp.QuoteMeta(n) diff --git a/checks/particulars/main_test.go b/checks/particulars/main_test.go index 9522c00f..ac1c970c 100644 --- a/checks/particulars/main_test.go +++ b/checks/particulars/main_test.go @@ -82,7 +82,6 @@ func TestCheckReturnsOneOnAFindingAndTwoOnAnUnreadableList(t *testing.T) { os.WriteFile(p, []byte(s), 0o644) } write("particulars", "ace\n") - write("checks/particulars/covered", "kernel\n") write("modules/kernel/a", "clean\n") if got := check(root); got != 0 { t.Errorf("clean = %d, want 0", got) @@ -97,7 +96,7 @@ func TestCheckReturnsOneOnAFindingAndTwoOnAnUnreadableList(t *testing.T) { } } -func TestOnlyCoveredModulesAreRead(t *testing.T) { +func TestRunReadsOnlyTheModulesItIsGiven(t *testing.T) { root := t.TempDir() write := func(p, s string) { p = filepath.Join(root, p) @@ -114,3 +113,59 @@ func TestOnlyCoveredModulesAreRead(t *testing.T) { t.Errorf("got %v", got) } } + +// 1838/AC1: the check reads every directory under modules/ and no list of covered modules, so a module is +// covered from its first commit. +func TestEveryModuleIsReadWithoutAList(t *testing.T) { + root := t.TempDir() + write := func(p, s string) { + p = filepath.Join(root, p) + os.MkdirAll(filepath.Dir(p), 0o755) + os.WriteFile(p, []byte(s), 0o644) + } + write("particulars", "novox\n") + write("modules/kernel/files/a", "clean\n") + if got := check(root); got != 0 { + t.Errorf("clean, no covered list = %d, want 0", got) + } + write("modules/new-module/files/a", "on novox\n") + if got := check(root); got != 1 { + t.Errorf("a finding in a module no list names = %d, want 1", got) + } +} + +// 1838/AC2: the hardware model's own name and the software's own name are not node names. +func TestTheHardwareModelAndTheSoftwareAreNotNodeNames(t *testing.T) { + for _, s := range []string{ + "modules/asus-zephyrus-g14/README.md", + "the asus-zephyrus-g14 module", + "ASUS_ZEPHYRUS_G14=1", + "product_family: ROG Zephyrus G14", + "# This machine is a node of a Novox mesh", + "Novox Mesh turns a handful of personal machines into one system", + "