Skip to content

fix: normalize local callback workflow paths to .github/workflows - #140

Merged
joshua-temple merged 1 commit into
mainfrom
fix/normalize-local-callback-paths
Jun 13, 2026
Merged

joshua-temple merged 1 commit into
mainfrom
fix/normalize-local-callback-paths

Conversation

@joshua-temple

Copy link
Copy Markdown
Collaborator

Problem

normalizeWorkflowPath only prepended ./ when a callback workflow path already started with .github/. A callback workflow: that was a bare filename (build.yaml) was emitted verbatim as uses: build.yaml, which GitHub rejects at parse:

reusable workflow call "build.yaml" at "uses" is not following the format
"owner/repo/path/to/workflow.yml@ref" nor "./path/to/workflow.yml" [workflow-call]

Nearly every shipped e2e/scenarios/*.yaml uses bare callback paths, so the act/gitea harness had been tolerating invalid YAML that real GitHub would reject. Confirmed with actionlint 1.7.12.

Fix

GitHub requires local reusable workflows to live in .github/workflows/. normalizeWorkflowPath now routes every local callback to the valid ./.github/workflows/<file> form:

  • Cross-repo external ref (contains @, e.g. owner/repo/.github/workflows/x.yml@ref): unchanged.
  • Already ./.github/workflows/...: unchanged.
  • .github/workflows/x.yaml: gets the ./ prefix.
  • Bare filename (build.yaml) or any other local path: routed to ./.github/workflows/<basename>.

A non-.github/workflows local subdir path (e.g. ci/build.yaml) is not a valid local reusable-workflow location, so it is now rejected at config validation with:

<prefix>: local callback workflow must be a .github/workflows/... path or a bare filename, got "ci/build.yaml"

wired into the build, deploy, and validate callback validation in internal/config/parse.go. All build/deploy/publish local callback uses: emission sites already route through normalizeWorkflowPath (generator.go:863, generator.go:1323, promote.go:798/846/895/1060/1312, hotfix.go:405). The custom-changelog callback is a separate structural issue and is out of scope here.

Scenario / harness lockstep

The fix changes generated output (uses: ./.github/workflows/build.yaml), so the e2e harness now places each callback stub at the matching normalized path. A normalizeCallbackStubPath helper in e2e/harness/harness.go mirrors the generator rule (skip @ external refs; else .github/workflows/ + basename) so the stub lands where the generated uses: points and the .github/workflows/*.yaml git-add glob picks it up. No scenario YAML changes were needed: all scenarios use bare filenames, which are valid input and normalize correctly.

The eight cascade-example-* repos should be regenerated with the fixed generator when that work resumes; they are separate repos and out of scope here.

Verification

  • go build ./..., full go test ./... (1202 tests) pass.
  • golangci-lint run ./internal/generate/... ./internal/config/... clean.
  • e2e module go build ./... && go vet ./... clean.
  • actionlint integration test: a bare-path build callback now generates uses: ./.github/workflows/build.yaml with no workflow-call format error.
  • Live Docker-backed e2e run was blocked: the local Docker daemon was returning HTTP 500 on /version and /info and did not recover after a Docker Desktop restart. The harness/scenario reconciliation is covered by the unit and actionlint tests and the matched stub-placement path; the Docker e2e scenario should be run once the daemon is healthy.

Closes #139

Signed-off-by: Joshua Temple <joshua.temple@stablekernel.com>
@joshua-temple
joshua-temple merged commit f1d4852 into main Jun 13, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generator emits invalid bare 'uses:' for local callback workflow paths not under .github/

1 participant