If you’re using GitHub Actions in your projects, you should be pinning your actions to specific commit SHAs instead of using tags or branches.

The problem with tags

Most GitHub Actions workflows look something like this:

- uses: actions/checkout@v4
- uses: actions/setup-go@v5

Simple, clean, easy to read, and also a potential security risk.

When you reference an action by tag (like @v4), you’re essentially trusting that:

  1. The tag won’t be moved to point to a different commit
  2. The repository won’t be compromised
  3. The maintainer’s account won’t be hijacked

And here’s the thing: tags are mutable. They can be deleted and recreated pointing to completely different code. If someone compromises a popular action’s repository or the maintainer’s account, they could push malicious code under an existing tag, and every workflow using that tag would automatically pull and run it.

This isn’t theoretical - supply chain attacks through dependencies (including CI/CD) are becoming increasingly common. You probably wouldn’t run curl | bash in production, so why trust mutable references in your CI pipeline?

Pinning to commit SHAs

The solution is to pin actions to their full commit SHA instead:

- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2

This ensures:

  • Immutability: Commit SHAs cannot be changed or moved. What you pin is what you get, forever.
  • Security: You know exactly what code is running in your workflows, and it can’t be swapped out from under you.
  • Auditability: When updating, you can easily review the diff between the old SHA and the new one to see exactly what changed.

The comment at the end (# v4.1.1) is optional but highly recommended - it helps you remember which version the SHA corresponds to, and it also allows Dependabot to recognize the version and create pull requests to update your actions automatically.

The manual approach is tedious

Now, you could manually pin all your actions. Look up each action, find the latest release, copy the commit SHA, paste it in… and then do it again for every action in every workflow file. And then do it all over again when you want to update them.

So. Boring.

Enter Piñata

This is why I built caarlos0/pinata.

Piñata automatically pins GitHub Actions in your workflow files to their commit SHAs, and adds helpful comments with the corresponding tag or version.

You can run it like so:

pinata

Piñata will:

  • Pin all actions to commit SHAs
  • Add version comments for readability
  • Create a pull request with the changes
  • Let you review exactly what’s being updated

That’s it

Pinning GitHub Actions to commit SHAs is one of those simple security practices that takes five minutes to set up and might save you from a really bad day down the line.

If you’re not doing it yet, give pinata a try. It’s open source, free, and makes the whole process painless.