Skip to content

Commit 9a82608

Browse files
committed
Fix this repository's own install, and find the caller by content
v0.3.0 requires appName and changed the handle, and I updated the template without updating this repository's own install. Its config had no appName, so preflight failed, and its caller still triggered on the old prefix, so no comment could fire. The repository the agent actually works in was broken by the release. The drift check then found a flaw in itself. It looked for the caller at .github/workflows/simplycubed.yml, which is what init writes, but here that name belongs to the reusable workflow and the caller is simplycubed-caller.yml. It now finds the caller by the reusable-workflow call it makes, so a renamed caller is checked and a same-named non-caller is ignored. Only two repositories in the org have an install: this one and corp. An earlier scan reported all fifteen, which was wrong: gh api on a 404 returns a JSON body, so --jq '.sha' yielded the string "null" and every repo looked present.
1 parent ba2b3a5 commit 9a82608

4 files changed

Lines changed: 66 additions & 7 deletions

File tree

‎.github/simplycubed.yml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
# decides whether a change is good.
66
labelPrefix: sc
77

8+
# The App this repository installed. Comment commands address it:
9+
# "@simplycubed-code go" on an issue starts work. The caller workflow triggers
10+
# on this same handle, and preflight fails if the two ever disagree.
11+
appName: simplycubed-code
12+
813
gate: make check
914

1015
prDescription: rich

‎.github/workflows/simplycubed-caller.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ jobs:
6060
if: >-
6161
github.event_name == 'issue_comment' &&
6262
github.event.comment.user.type != 'Bot' &&
63-
startsWith(github.event.comment.body, '/simplycubed') &&
63+
startsWith(github.event.comment.body, '@simplycubed-code') &&
6464
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)
6565
uses: simplycubed/code/.github/workflows/simplycubed.yml@main
6666
with:

‎cmd/simplycubed/main.go‎

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -459,18 +459,38 @@ func reply(argv []string, body string, stdout io.Writer) error {
459459
// way — comments simply stop working, with no error anywhere — so it is checked
460460
// on every run rather than left to be discovered.
461461
func checkMentionAgreement(repoDir, appName string) error {
462-
path := filepath.Join(repoDir, ".github", "workflows", "simplycubed.yml")
463-
b, err := os.ReadFile(path)
462+
// Find the caller by what it does, not by what it is called. init writes
463+
// .github/workflows/simplycubed.yml, but an adopter can rename it, and in
464+
// this repository that name belongs to the reusable workflow itself. A check
465+
// keyed on the filename would pass on the wrong file, or fail on a valid
466+
// install, which is worse than not checking.
467+
dir := filepath.Join(repoDir, ".github", "workflows")
468+
entries, err := os.ReadDir(dir)
464469
if err != nil {
465-
// No caller workflow is a valid local setup, not a misconfiguration.
470+
// No workflows at all is a valid local setup, not a misconfiguration.
466471
return nil
467472
}
468473
want := "'" + command.MentionFor(appName) + "'"
469-
if strings.Contains(string(b), want) {
474+
var callers []string
475+
for _, e := range entries {
476+
if e.IsDir() {
477+
continue
478+
}
479+
path := filepath.Join(dir, e.Name())
480+
b, err := os.ReadFile(path)
481+
if err != nil || !strings.Contains(string(b), "simplycubed/code/.github/workflows/simplycubed.yml@") {
482+
continue
483+
}
484+
callers = append(callers, path)
485+
if strings.Contains(string(b), want) {
486+
return nil
487+
}
488+
}
489+
if len(callers) == 0 {
470490
return nil
471491
}
472492
return fmt.Errorf("%w: .github/simplycubed.yml sets appName %q, but %s does not trigger on %s. Comment commands will never fire. Re-run \"simplycubed init --workflow\" to rewrite the trigger from the config",
473-
ErrConfigMissing, appName, path, want)
493+
ErrConfigMissing, appName, strings.Join(callers, ", "), want)
474494
}
475495

476496
func preflightCmd(argv []string, stdout io.Writer) error {

‎cmd/simplycubed/main_test.go‎

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1290,7 +1290,11 @@ func TestPreflightCatchesHandleDrift(t *testing.T) {
12901290
if err := os.MkdirAll(filepath.Dir(wf), 0o755); err != nil {
12911291
t.Fatal(err)
12921292
}
1293-
body := "on: [issue_comment]\njobs:\n comment:\n if: startsWith(github.event.comment.body, '" + trigger + "')\n"
1293+
// Must look like a caller: the check finds it by the reusable-workflow
1294+
// call, not by filename.
1295+
body := "on: [issue_comment]\njobs:\n comment:\n" +
1296+
" uses: simplycubed/code/.github/workflows/simplycubed.yml@v0.3.0\n" +
1297+
" if: startsWith(github.event.comment.body, '" + trigger + "')\n"
12941298
if err := os.WriteFile(wf, []byte(body), 0o644); err != nil {
12951299
t.Fatal(err)
12961300
}
@@ -1417,3 +1421,33 @@ func TestCommandCmdSurfacesConfigProblems(t *testing.T) {
14171421
t.Fatal("--body with no value must be an error")
14181422
}
14191423
}
1424+
1425+
// init writes the caller as simplycubed.yml, but an adopter can rename it, and
1426+
// in this repository that name belongs to the reusable workflow itself. The
1427+
// drift check has to find the caller by what it calls, or it passes on the
1428+
// wrong file and fails on a valid install.
1429+
func TestDriftCheckFindsTheCallerByContentNotFilename(t *testing.T) {
1430+
t.Setenv("SIMPLYCUBED_AZURE_OPENAI_ENDPOINT", "https://r.openai.azure.com")
1431+
t.Setenv("SIMPLYCUBED_AZURE_OPENAI_API_KEY", "k")
1432+
1433+
dir := repoWithConfigBody(t, "gate: make check\nappName: acme-code\n")
1434+
wfDir := filepath.Join(dir, ".github", "workflows")
1435+
if err := os.MkdirAll(wfDir, 0o755); err != nil {
1436+
t.Fatal(err)
1437+
}
1438+
// A workflow with the canonical name that is not a caller must be ignored.
1439+
if err := os.WriteFile(filepath.Join(wfDir, "simplycubed.yml"), []byte("on: workflow_call\njobs:\n run:\n runs-on: ubuntu-latest\n"), 0o644); err != nil {
1440+
t.Fatal(err)
1441+
}
1442+
// The real caller, under a different name.
1443+
caller := "on: [issue_comment]\njobs:\n comment:\n" +
1444+
" uses: simplycubed/code/.github/workflows/simplycubed.yml@v0.3.0\n" +
1445+
" if: startsWith(github.event.comment.body, '@acme-code')\n"
1446+
if err := os.WriteFile(filepath.Join(wfDir, "simplycubed-caller.yml"), []byte(caller), 0o644); err != nil {
1447+
t.Fatal(err)
1448+
}
1449+
1450+
if err := preflightCmd([]string{"--repo-dir", dir}, io.Discard); err != nil {
1451+
t.Fatalf("a renamed caller that agrees must pass: %v", err)
1452+
}
1453+
}

0 commit comments

Comments
 (0)