# how to pin GitHub Actions to SHAs

## TL;DR
Replace version tags like `actions/checkout@v4` with the full commit SHA plus a comment noting the version, for example `actions/checkout@[the 40 char sha] # v4`. Tags are mutable and a compromised tag can silently change what your pipeline runs. Use Dependabot or Renovate to open PRs when pinned SHAs go stale, so you stay current without floating tags.

## The query
```text
how to pin GitHub Actions to SHAs
```

## Use this when
- you want supply-chain safety for CI and are done trusting mutable tags
- someone asks "how do I pin actions to SHAs" or "is @v4 safe"
- you are writing org policy for workflow files
- you are auditing which third-party code your pipelines execute

## Not for this skill when
- the action is maintained in-house and published from your own repo (still fine to pin, but the risk is lower)
- you are debugging a workflow failure (pinning is hygiene, not a debugger)
- you need reproducible builds of app dependencies (that is lockfiles, a separate topic)

## Steps
1. Pick a workflow and list every third-party `uses:` line.
   Expected output: a complete list of external actions and the tags they float on.
2. Resolve each tag to its commit SHA: check the action repo's releases or tags page for the commit behind the tag.
   Expected output: you have a 40-character SHA for each action.
3. Rewrite each line as `uses: actions/checkout@[the 40 char sha] # v4`, keeping the version in a trailing comment so humans can read it.
   Expected output: the workflow file has no bare version tags left on third-party actions.
4. Push to a branch and run the workflow.
   Expected output: the run succeeds and the logs show the action checking out at the pinned SHA.
5. Enable Dependabot or Renovate for GitHub Actions so stale pins get update PRs with the new SHA and version comment.
   Expected output: you receive PRs bumping the SHA and version comment on a schedule.
6. Add a CI lint check that fails if a `uses:` line references a third-party action without a 40-char SHA.
   Expected output: new PRs that float a tag fail the lint check.

### Variant: pin-github-action tool
The `pin-github-action` CLI rewrites workflow files to SHAs automatically. Run it, review the diff, and commit. Good for converting a whole org at once.

### Variant: internal reusable workflows
Pin those too. Internal does not mean immutable; tags move there as well.

### Variant: Docker-based actions
Pin the image digest in addition to the action SHA when the action pulls containers, so both layers are fixed.

## Why this happens
A tag like `v4` is a pointer the maintainer can move, and a compromised maintainer account or repo can repoint it at malicious code that your pipeline then runs with your secrets. A SHA is content-addressed: it can only ever mean that exact commit.

## Edge cases and pitfalls
- SHA pins go stale. Without Dependabot or Renovate you will run ancient actions with known bugs, so the updater is part of the fix, not optional.
- The trailing version comment is a convention, not enforcement. Your lint check should verify the SHA, not the comment.
- Forked or vendored actions need the same treatment; a fork is still third-party code.
- Major-version bumps can break workflows. Let the updater PRs run the full pipeline before merging.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_cLGzhWMFRUybzBevovrVkA
