Skip to content

fix: three generator-validity hazards (timeout on reusable callbacks, env-less rollback needs, unsafe job-ID names) - #141

Merged
joshua-temple merged 3 commits into
mainfrom
fix/generator-validity-batch-a
Jun 13, 2026
Merged

joshua-temple merged 3 commits into
mainfrom
fix/generator-validity-batch-a

Conversation

@joshua-temple

Copy link
Copy Markdown
Collaborator

Fixes three generator-validity hazards confirmed against actionlint 1.7.12. Each is a separate commit with its own failing-test-first change.

F2 - timeout-minutes on reusable-workflow callbacks (GitHub rejects at parse)

GitHub forbids timeout-minutes on a job that calls a reusable workflow (allowed caller keys: name, uses, with, secrets, needs, if, permissions, strategy, concurrency). The generator emitted job-level timeout-minutes whenever timeout_minutes > 0, including on uses: callbacks.

actionlint before: when a reusable workflow is called with "uses", "timeout-minutes" is not available ...

Fix: gate the explicit-timeout emission on the callback being inline run:, mirroring the existing environment: gate. For uses: callbacks the timeout must live inside the called workflow.

F3 - rollback/finalize needs nonexistent deploy jobs when environments is empty (GitHub rejects at parse)

Deploy jobs in promote.yaml are only written when there is at least one environment, but rollback jobs and the finalize job referenced deploy jobs unconditionally when deploys: was non-empty. With environments: [] plus a configured deploys:, the emitted needs: lists (and DEPLOY_RESULT_* env vars) pointed at jobs that never existed.

actionlint before: job "rollback-..." needs job "deploy-..." which does not exist in this workflow and the same for finalize.

Fix: skip rollback generation and the deploy-job references in finalize when there are no environments.

F4/F5 - callback/env names with invalid job-ID characters (GitHub rejects at parse)

Names with characters outside the GitHub job-ID grammar (such as ., spaces, or /) produced invalid job IDs (build-app.web, build-my app) and broke the ${{ }} dereferences that read their outputs.

actionlint before: invalid job ID "build-app.web" ... and receiver of object dereference ... must be type of object but got "string".

Fix: reject such names at config validation with a clear message (builds[i].name "<name>" must contain only letters, digits, hyphens, and underscores) for build, deploy, external-deploy, and environment names. Names are rejected rather than sanitized so two distinct names cannot collapse to one job ID. Hyphens, uppercase, leading digits, and underscores remain valid since the name is a suffix after the build-/deploy- prefix.

Verification

  • go build ./..., go test ./... (1211 passing), golangci-lint run on internal/generate and internal/config: all green.
  • For F2 and F3, generated sample workflows and confirmed with actionlint that the specific parse errors are gone.
  • Each fix has a test that fails before the change. Golden tests and e2e scenarios that asserted the buggy output (callback-timeout) were updated to use inline run: so they still verify the underlying feature.

GitHub forbids timeout-minutes on a job that calls a reusable workflow
(allowed caller keys: name, uses, with, secrets, needs, if, permissions,
strategy, concurrency) and rejects the workflow at parse time. The
generator emitted job-level timeout-minutes whenever timeout_minutes > 0,
including on uses: callbacks. Gate the explicit-timeout emission on the
callback being an inline run: job, mirroring the environment: gate. For
reusable callbacks the timeout must live inside the called workflow.

Signed-off-by: Joshua Temple <joshua.temple@stablekernel.com>
…onments

Deploy jobs in promote.yaml are only written when len(Environments) > 0,
but rollback jobs and the finalize job referenced deploy jobs
unconditionally whenever deploys: was non-empty. With environments: [] and
a configured deploys:, the emitted needs: lists (and DEPLOY_RESULT_* env
vars) pointed at deploy jobs that never existed, which GitHub rejects at
parse (needs job X which does not exist). Gate rollback generation and the
finalize deploy-job references on there being at least one environment.

Signed-off-by: Joshua Temple <joshua.temple@stablekernel.com>
cascade derives job IDs as build-<name>/deploy-<name> and keys identifiers
and ${{ }} expression references off environment names, with no
sanitization. A name containing a character outside the GitHub job-ID
grammar (such as a dot, space, or slash) produced an invalid job ID like
build-app.web and broke the expression dereferences that read its outputs,
which GitHub rejects at parse time. Reject such names at config validation
with a clear message. Names are rejected rather than sanitized so two
distinct names cannot collapse to one job ID. Leading digits, uppercase,
hyphens, and underscores remain valid since the name is a suffix after the
build-/deploy- prefix.

Signed-off-by: Joshua Temple <joshua.temple@stablekernel.com>
@joshua-temple
joshua-temple merged commit 11f87b9 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.

1 participant