Skip to content

promote: retries and on_failure are not emitted into promote.yaml #626

Description

@joshua-temple

Problem

run_policy, on_failure, and retries are documented as applying to validate, each builds entry, and each deploys entry. The promote generator references none of them:

$ grep -nE "Retries|OnFailure|RunPolicy" internal/generate/promote.go
(no matches)

A promotion therefore runs each deploy exactly once, with no retry and no on_failure handling, while a trunk run honors all three. Promote is the production path, so a transient deploy failure is costliest exactly where the retry is missing.

Why it was not simply wired up

The orchestrate retry ladder re-invokes the whole job. That does not transfer to promote as-is: a promote deploy declaring inputs compiles to a matrix job fanned across environments (writeDeployJobs), and a matrix job exposes a single aggregate result, failure if any leg failed. A caller-side shim gated on that aggregate would re-invoke the callback for every environment, redeploying the ones that already succeeded. GitHub Actions has no caller-side way to re-run only the failed legs of a dependency's matrix.

Emitting a ladder only on the non-matrix path was also rejected: it would make retries silently work or not work depending on whether the deploy happened to declare inputs.

Scope taken instead

The documented claim was scoped to trunk runs so the docs are true today (see docs/src/content/docs/reference/manifest.md, Policy fields).

What a real fix needs

  • Retry scoped per environment rather than per job, so a healthy environment is never redeployed because a different one failed.
  • An effective-result design for promote's own result sinks, which have the same immutable-result hazard as orchestrate: the rollback gate (needs.deploy-<name>.result == 'success' / == 'failure') and DEPLOY_RESULT_<NAME>.
  • The rollback interaction settled: rollback must fire only when a ladder is exhausted, not when a first attempt failed and a retry rescued it.
  • on_failure and run_policy decided at the same time; they share the gap.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions