Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ go run ./tools/numericsurface --rbgo /tmp/rbgo --root content/en
go run ./tools/upstreamlinks --root content/en
```

A fifth directory, `tools/wired/`, is not a tool but the guard over the four
above: its test fails when a tool under `tools/` is invoked by no workflow.
It exists because the MkDocs-to-Hugo move ([#28](https://github.com/go-embedded-ruby/docs/pull/28))
rewrote the workflows and dropped two of these guards while leaving them in the
tree — **a guard nothing calls is indistinguishable from a guard that passes,
since both are silent**, and the pages could have said anything for two
releases. It looks for the path `tools/<name>`, not the bare name: `docs.yml`
mentions `docexample` in a comment as well as in its step, so against the bare
name, deleting that step would have left the tool reported as wired.

The authority column is the point. None of these holds the right answer itself:
a table of right answers kept in **this** repository would repeat the page's
mistake and agree with it. `upstreamlinks` asks `go.mod` precisely because a
Expand Down
27 changes: 25 additions & 2 deletions tools/wired/wired.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,34 @@ func WorkflowText(dir string) (string, error) {
return b.String(), nil
}

// Unwired returns the commands no workflow mentions.
// Unwired returns the commands no workflow invokes.
//
// ⛔ It looks for `tools/<name>`, the PATH, not for the bare name.
//
// Measured 2026-10-31: `.github/workflows/docs.yml` names `docexample` twice —
// once on the `run:` line that invokes it, and once in a comment on line 108
// explaining what it judges. Against the bare name, deleting the invocation and
// leaving the comment would have kept that tool reported as wired. The one
// defect this package exists to catch would have been invisible for the one
// tool whose step carries prose about it.
//
// This stays deliberately crude — it still does not check for a well-formed
// step, because the failure it guards is not subtle (a tool nobody calls) and a
// crude test that would have failed #28 beats a precise one nobody wrote. But
// "appears in a sentence" and "appears in a command" are not the same claim, and
// `tools/` is what every spelling of the invocation has in common:
//
// go run ./tools/upstreamlinks --root content/en
// go run github.com/go-embedded-ruby/docs/tools/[email protected]
//
// while a prose comment does not. A workflow that invoked a tool some other way
// — through a variable, a matrix — would now read as unwired, which fails loudly
// rather than quietly, and that is the right direction for this guard to be
// wrong in.
func Unwired(commands []string, workflows string) []string {
var out []string
for _, c := range commands {
if !strings.Contains(workflows, c) {
if !strings.Contains(workflows, "tools/"+c) {
out = append(out, c)
}
}
Expand Down
42 changes: 40 additions & 2 deletions tools/wired/wired_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,15 @@ func TestEveryToolIsWiredIntoAWorkflow(t *testing.T) {

bad := wired.Unwired(cmds, wf)
if len(bad) > 0 {
t.Errorf("no workflow mentions %v.\n"+
t.Errorf("no workflow invokes %v (looked for `tools/<name>`, not the bare name:\n"+
"a tool named in a comment is documented, not called).\n"+
"A tool nothing calls is indistinguishable from a tool that passes: both are "+
"silent. Either wire it into .github/workflows or delete it; leaving it is the "+
"state that let the MkDocs-to-Hugo move (#28) drop both documentation guards "+
"without a red build.", bad)
return
}
t.Logf("%d tool(s) under tools/, all named by a workflow: %v", len(cmds), cmds)
t.Logf("%d tool(s) under tools/, all invoked by a workflow: %v", len(cmds), cmds)
}

// TestUnwiredNamesOnlyWhatIsMissing checks the rule itself against literals, so
Expand All @@ -64,6 +65,43 @@ func TestUnwiredNamesOnlyWhatIsMissing(t *testing.T) {
}
}

// ⛔ A NAME IN A SENTENCE IS NOT AN INVOCATION, and this repository already
// contains the sentence: docs.yml:108 reads
//
// # docexample judges the examples that claim an answer -- 11 of them. The
//
// so against the bare name, deleting the `go run ./tools/docexample` step and
// leaving that comment kept the tool reported as wired — the exact blindness
// this package exists to remove, for the one tool whose step is documented.
//
// The two halves are asserted together: the comment alone must NOT count, and
// the invocation must still count, so a rule tightened into matching nothing
// cannot pass this.
func TestAMentionInProseIsNotAnInvocation(t *testing.T) {
const comment = " # docexample judges the examples that claim an answer -- 11 of them. The\n"
if got := wired.Unwired([]string{"docexample"}, comment); len(got) != 1 {
t.Errorf("a tool named only in a comment was reported as wired (Unwired = %v)", got)
}

// The real spellings of an invocation, each of which must count.
for _, yaml := range []string{
" run: go run ./tools/docexample --rbgo /tmp/rbgo --root content/en\n",
" run: go run github.com/go-embedded-ruby/docs/tools/[email protected]\n",
" run: cd tools/docexample && go run .\n",
} {
if got := wired.Unwired([]string{"docexample"}, yaml); len(got) != 0 {
t.Errorf("an invoked tool was reported as unwired (%v) for:\n%s", got, yaml)
}
}

// And the comment beside a real invocation is the repository's actual state:
// wired because of the step, not because of the prose.
both := " run: go run ./tools/docexample --root content/en\n" + comment
if got := wired.Unwired([]string{"docexample"}, both); len(got) != 0 {
t.Errorf("Unwired = %v, want the step to count", got)
}
}

// repoRoot walks up to the directory holding go.mod, so the test runs from
// wherever `go test ./...` places it.
func repoRoot(t *testing.T) string {
Expand Down
Loading