Pinning GHA actions/shared workflows to commits

There’s currently a push to pin GHA actions and shared workflows to commit hashes (to thwart certain types of supply chain attacks). While doing that for several repos, I found several (potential) problems and solutions.

First, here’s a collection of commands that pin certain actions:

for i in $(git grep -l ' uses:'); do sed -i $i -e 's/ uses: actions\/checkout@.*/ uses: actions\/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1  # v7.0.1/g'; done
for i in $(git grep -l ' uses:'); do sed -i $i -e 's/ uses: actions\/configure-pages@.*/ uses: actions\/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d  # v6.0.0/g'; done
for i in $(git grep -l ' uses:'); do sed -i $i -e 's/ uses: actions\/deploy-pages@.*/ uses: actions\/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128  # v5.0.0/g'; done
for i in $(git grep -l ' uses:'); do sed -i $i -e 's/ uses: actions\/download-artifact@.*/ uses: actions\/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c  # v8.0.1/g'; done
for i in $(git grep -l ' uses:'); do sed -i $i -e 's/ uses: actions\/github-script@.*/ uses: actions\/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3  # v9.0.0/g'; done
for i in $(git grep -l ' uses:'); do sed -i $i -e 's/ uses: actions\/upload-artifact@.*/ uses: actions\/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a  # v7.0.1/g'; done
for i in $(git grep -l ' uses:'); do sed -i $i -e 's/ uses: actions\/upload-pages-artifact@.*/ uses: actions\/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9  # v5.0.0/g'; done
for i in $(git grep -l ' uses:'); do sed -i $i -e 's/ uses: actions\/setup-node@.*/ uses: actions\/setup-node@820762786026740c76f36085b0efc47a31fe5020  # v7.0.0/g'; done
for i in $(git grep -l ' uses:'); do sed -i $i -e 's/ uses: actions\/setup-python@.*/ uses: actions\/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97  # v7.0.0/g'; done
for i in $(git grep -l ' uses:'); do sed -i $i -e 's/ uses: codecov\/codecov-action@.*/ uses: codecov\/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f  # v7.0.0/g'; done
for i in $(git grep -l ' uses:'); do sed -i $i -e 's/ uses: peter-evans\/create-or-update-comment@.*/ uses: peter-evans\/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9  # v5.0.0/g'; done
for i in $(git grep -l ' uses:'); do sed -i $i -e 's/ uses: peter-evans\/find-comment@.*/ uses: peter-evans\/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad  # v4.0.0/g'; done
for i in $(git grep -l ' uses:'); do sed -i $i -e 's/ uses: wntrblm\/nox@.*/ uses: wntrblm\/nox@3284968a16fa5bd7fc0cc64a69a531404ccb4756  # 2026.07.11/g'; done

This could be useful for some to quickly pin some action uses. Obviously the versions used in these commands will be outdated soon, but at least for now they are a helpful starting point :slight_smile:

Then there’s a new GitHub Actions feature that’s very useful for (shared) workflows that want to reference actions or other shared workflows in the same repository. Using the new uses: $/... syntax, it is now possible to make this work with pinning without having to use ugly workarounds. Using this syntax makes sure that the referenced action/workflow uses the same commit hash as the caller. I’ve implemented this in antsibull-nox and github-docs-build, where this caused problems especially with proper testing for a long time.

1 Like

There’s also a problem, which I wanted to describe in its own comment. Namely, we have a set of small actions:

that don’t have proper releases (we use them from their v1 or simply main branch), and for these pinning doesn’t work well, since Dependabot won’t update the pin since there are no releases. (At least to my knowledge, but maybe I’m wrong?)

As far as I know, the only way to be able to pin these would be to regularly create releases. But creating releases means more effort, also since now there will be more frequent updates (due to pinned versions of their dependencies; so far bumps only happened when actions released new major versions, now also for minor and bugfix releases), and thus we should also release these actions and shared workflows more frequently. This creates some extra work (and policy decisions, like how often should these be released?).

What do people think about this?

And a third problem are shared workflows / actions such as antsibull-nox: if ansible-ocre devel / milestone changes for example their supported Python versions, antsibull-nox gets a little update so that collections using it don’t suddenly have a broken CI when trying to run tests for no longer supported Python versions. If you now pin your antsibull-nox action or shared workflow to a specific version, we now have to do two things:

  1. Make sure to release a new version of antsibull-nox as soon as possible;
  2. Update the pin as soon as possible.

Until now, when sticking to @main or @stable, only required a new commit to antsibull-nox (for @main) or a new release (for @stable), and not updating the collections’ pin as well. This generates more friction for collection development.

Obviously we can work around this by making antsibull-nox in CI fetch some data from the repository, and lets that data overwrite certain aspects in the release that is now used. But that goes kind of against pinning.

Alternatively, antsibull-nox could stop using the HEAD of branches of ansible-core (stable-2.x, milestone, and devel branches). But that also has some disadvantages.

So this is also something that we - as a community - need to look at and discuss.