Skip to content

fix(generate): close four silent generated-output defects - #634

Merged
joshua-temple merged 3 commits into
mainfrom
fix/pass10-silent-emitted-output
Jul 18, 2026
Merged

fix(generate): close four silent generated-output defects#634
joshua-temple merged 3 commits into
mainfrom
fix/pass10-silent-emitted-output

Conversation

@joshua-temple

Copy link
Copy Markdown
Collaborator

Four defects that each emit valid YAML doing the wrong thing (actionlint alone will not catch them). Each has a generation-assertion test that reds on the pre-fix output and greens after. Verified by generating and reading the emitted YAML.

GB2 (blocker) - promote matrix deploys dropped environment/sha

A matrix deploy (a deploy declaring inputs) emitted a with: block carrying only declared manifest inputs, while its job name referenced ${{ matrix.environment }} - a key the matrix builder never set. The deploy ran with an empty environment.

Before:

  deploy-web:
    name: Deploy web (${{ matrix.environment }})
    with:
      region: ${{ matrix.region }}

After:

    with:
      environment: ${{ matrix.environment }}
      sha: ${{ matrix.sha }}
      region: ${{ matrix.region }}

The matrix builder now injects {environment, sha} onto every entry; environment is passed unconditionally (orchestrate's contract), sha only when the callback declares it, and neither when the manifest already wires it as an explicit input.

GM4 (major) - rollback repository_dispatch dry_run truthiness

(inputs.dry_run || client_payload.dry_run) != 'true' compared a JSON boolean true (a natural {"dry_run": true} payload) against a string; GitHub Actions coerces numerically, so the guard read true as not-a-dry-run and a dry-run rollback ran real deploys and wrote rolled-back state.

Before: ... != 'true' && (...)
After: (...) != true && (...) != 'true' && (...) - matches both the boolean and the string. Without the trigger, output is byte-identical.

GM5 (major) - dependent deploys judged the immutable base result

deploy-api depending on deploy-web gated on needs.deploy-web.result == 'success' (and emitted it twice). A GHA job result is immutable, so a base deploy failing-then-rescued by a retry shim left the dependent skipped.

Before:

    if: |
      needs.deploy-web.result == 'success' &&
      needs.deploy-web.result == 'success'

After (retries: 2):

    if: |
      (needs.deploy-web.result == 'success' || needs.deploy-web-retry-1.result == 'success' || needs.deploy-web-retry-2.result == 'success')

Reuses #627's effectiveSuccessCond (not duplicated); the duplicated clause is removed. N=0 collapses to the single bare clause.

GM6 (major) - promote native Deployment had no dry_run guard

A dry-run promote created a real GitHub Deployment, and the status expression counted a legitimately skipped deploy as failure and omitted the prod job.

Before:

      - name: Create deployment
        if: ${{ github.server_url == 'https://github.com' }}
      ...
      if [ "${{ (needs.deploy-app.result == 'success') && 'success' || 'failure' }}" = "success" ]

After:

        if: ${{ github.server_url == 'https://github.com' && github.event.inputs.dry_run != 'true' }}
      ...
      if [ "${{ ((needs.deploy-app.result == 'success' || needs.deploy-app.result == 'skipped') && (needs.deploy-app-prod.result == 'success' || needs.deploy-app-prod.result == 'skipped')) && 'success' || 'failure' }}" = "success" ]

Verification

  • go build/test/vet/-race and golangci-lint clean; cascade verify --own-repo no drift.
  • The fix(generate): enforce emitted-workflow validity; reject on_failure continue, gate hotfix inputs #631 actionlint feature-matrix guard stays green (emitted output valid).
  • e2e scenarios: 43 (GB2 with-block), 32 (GM4 dual-comparison), 31 (GM6 dry-run guard) assert the fixes structurally on act. The empty-env deploy, boolean-payload dry-run, retry-rescued dependent, and dry-run-creates-Deployment behaviors are only observable on real GitHub (the fleet).

… rollback, and deploy gating

Each emitted valid YAML that did the wrong thing, so actionlint alone did not
catch it. Regression tests assert the emitted output and red before the fix.

- promote: matrix deploys now thread the per-promotion environment and sha to
  their callback (as orchestrate does) instead of deploying to an empty
  environment while the job name references a matrix key that was never set.
- rollback: the repository_dispatch dry-run guard matches both a JSON boolean
  true (client_payload) and the string 'true' (workflow_dispatch); a bare
  "!= 'true'" read a boolean true as not-a-dry-run and ran real deploys.
- generate: a dependent deploy is gated on the base deploy's effective result
  (base or any retry shim), reusing the retry-aware helper, and no longer emits
  the dependency clause twice.
- promote: a dry-run promote no longer creates a real GitHub Deployment, and the
  terminal status counts a skipped deploy as success and includes the prod job.

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

The GB2 fix threaded sha only when the callback declares it, but declared-input
detection read the raw workflow path (baseDir/deploy.yaml) while the emitted
uses: line and the orchestrate generator resolve a bare filename to
.github/workflows/deploy.yaml. A manifest referencing the callback by bare name
therefore failed detection silently, so deployHasInput(sha) was false and the
matrix with: block dropped sha while still threading environment.

discoverDeployInputs now resolves through normalizeWorkflowPath (and skips
cross-repo refs), matching the uses: emission, so a canonically-located callback
is found and its declared sha is threaded. The regression tests reference the
callback by bare filename, the way the e2e scenarios do, so they red when
detection drops sha instead of passing against a fully-qualified shortcut.

Signed-off-by: Joshua Temple <joshua.temple@stablekernel.com>
@joshua-temple
joshua-temple force-pushed the fix/pass10-silent-emitted-output branch from 4e5e06e to c834460 Compare July 18, 2026 09:05
@joshua-temple
joshua-temple merged commit a5c2402 into main Jul 18, 2026
21 checks passed
@joshua-temple
joshua-temple deleted the fix/pass10-silent-emitted-output branch July 18, 2026 09:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant