Skip to content

Parse the DR rebuild publisher step instead of scanning workflow text #2941

Description

@devantler

🤖 Generated by the Agentic Engineer

Evidence. validateDRWorkflow establishes the DR rebuild's publication wiring by scanning workflow text, not by parsing it. containsLine(job, "uses: ./.github/actions/deploy-prod/publish-platform-manifests") is satisfied by that string appearing anywhere in the job — including inside a comment or a block scalar. Reported by CodeRabbit as a P1 during review of #2939, and verified here: the symbols carrying this (validateDRWorkflow, extractJob, containsExactLine, containsLine) are untouched by that PR, so this is pre-existing rather than a regression it introduced.

Impact. The disaster-recovery route can be pointed at a different publisher while the validator still finds the expected text and reports success — so DR delivery could publish through logic this contract never checks, which is the exact drift the shared-action requirement exists to prevent. The same reasoning applies to the credential assertions (ghcr-token, hcloud-token), which are matched the same way and so are not proven to sit on the publishing step at all.

Why this is its own issue. #2939 converted the direct-push and merge-queue routes from text scanning to parsing and enforced-step checks. This is the same class at the third route, outside that PR's diff, and #2939 is already at review round 10 on a single concern.

Expected behaviour. Parse dr-rebuild.yaml, locate jobs.rebuild.steps, and require an enforced step (neither skipped nor failure-suppressed) whose uses value equals nestedPublisherAction — reusing requireEnforcedStep/usesAction, which #2939 already introduced for exactly this. Assert with.ghcr-token and with.hcloud-token on that same step rather than anywhere in the job.

Acceptance criteria.

  • validateDRWorkflow resolves the rebuild job and its publisher step through the YAML parser, not string containment.
  • The publisher step's uses is matched by equality, and the two credentials are asserted on that step.
  • The step is proven enforced (no if: skip, no continue-on-error).
  • Ablations covering: the real uses replaced while the expected text survives in a comment; the same in a block scalar; credentials moved to a different step; the publisher step skipped/failure-suppressed. Each must be refused.
  • The permission assertions (id-token/packages/attestations: write) move to parsed lookups too, or the issue records why they stay textual.

Size. Small — the parsing helpers and requireEnforcedStep already exist from #2939; this is applying them to the third route.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

Status
✅ Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions