Skip to content

feat: action ref pinning policy for generated workflows #28

Description

@joshua-temple

Problem

The cascade generator hardcodes mutable action tags in every emitted workflow:

  • setup-cli@latestgenerator.go:444,1026
  • actions/checkout@v4generator.go:438
  • actions/github-script@v7generator.go:935
  • actions/download-artifact@v4generator.go:1097

@latest is a supply-chain and reproducibility hole: a compromised or unintended latest tag is silently adopted by every downstream run. @v4-style tags are better but still mutable — an enterprise org enforcing allowed_actions: sha-pinned policy cannot adopt cascade's generated output at all today.

What to add

Introduce a manifest-level action_pins: map (or a pin_mode field) that controls how action refs are emitted.

Schema sketch:

# manifest.yaml (top-level)
action_pins:
  # explicit SHA overrides for any action the generator uses
  setup-cli: "sha256:abcdef..."     # optional; disables auto-resolution
  actions/checkout: "sha256:..."
  # ... etc

pin_mode: tag   # tag (default) | sha
                # sha = generator resolves each ref to its current commit SHA
                #       at generation time and pins it

When pin_mode: sha, the generator resolves each action ref via the GitHub API (or a lockfile) at cascade generate time and emits the pinned SHA with a # @vX trailing comment for readability. The resolved SHAs are persisted in a lockfile (cascade.lock) so the workflow is reproducible without an API call on every generate.

When pin_mode: tag (default), the generator emits the explicit version tags already known (e.g. @v4) rather than @latest, eliminating the mutable-tag problem without requiring SHA resolution.

The action_pins: map allows individual overrides regardless of pin_mode — useful when an org maintains a fork of an action.

Acceptance criteria

  • Generator never emits @latest for any action ref (tag mode emits explicit tags).
  • pin_mode: sha resolves and emits full commit SHAs at generation time with a trailing comment.
  • A cascade.lock file records resolved SHAs so re-generation is deterministic without network access.
  • action_pins: map entries override the resolved ref for a named action.
  • Invalid/unknown pin_mode values produce a clear validation error.
  • e2e scenario: a manifest with pin_mode: sha generates a workflow where every uses: line under cascade-controlled steps contains a 40-char hex SHA; running cascade generate a second time against the lockfile produces byte-identical output.
  • Existing manifests without action_pins continue to generate valid workflows (non-breaking default is explicit tag, not SHA).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions