Skip to content

Pin GHA actions to hashes and add dependabot #259

@jaimergp

Description

@jaimergp

We are using floating tags in most of our workflows, we should harden this with hash-pins to strengthen our supply chain.

List of PRs


Method used

I usually apply https://www.npmjs.com/package/pin-github-action for a couple of workflows, but this tool has an issue: it won't pin to the strictest version available (e.g. it will stay in v4 instead of choosing v4.1.3, which will configure Dependabot differently). For more info, see this issue: mheap/pin-github-action#192.

So instead I asked Gemini to write a script to do the same in Python, with the strictest-pin-available approach. After a couple of prompts, something usable was available. I fixed the logic for the comments. The script is available at https://gist.github.com/jaimergp/7e657e86a5c1a491905d37bcef6d3e55.

The script, however, is not perfect. It uses ruamel.yaml to parse YAML and its comments, and the roundtrip is not perfect. In some cases, whitespace differences were introduced, so I had to clean those up manually. However, this SO answer plus a little bit of regex-y search+replace ((.* uses: .*) -> $1) in my IDE automated most of it. After all, I did want to review all the modified workflows, so I coupled this cleaning-up to the review.

So, the overall script looked something like this (this is not how I ran it, but this is how I would have run it if I knew about the shortcomings after the first attempts). Click to unfold!

Bash script
#!/bin/bash
set -ex

# 1. (Fork and) clone all repos in the napari org

# napari-repos.txt is obtained with 'gh repo list napari', removed the napari/ part so we only get the repo name;
# I omitted napari.github.io and the archived ones too; this could have been done with a CLI flag, but well...
# Oh, and place 'packaging' the first so we can rely on its dependabot.yml file
for repo in $(cat napari-repos.txt); do 
    echo napari/$repo
    if [[ -d $repo ]]; then
        continue
    fi
    gh repo fork --clone --default-branch-only napari/${repo}
    cd $repo
    git checkout main
    git pull upstream main
    if [[ ! -f .github/dependabot.yml ]];
        cp ../packaging/.github/dependabot.yml .github/dependabot.yml
    fi
    cd ..
done

# 2. Run the Python script that will process the tags into commented hashes
# We do this for ALL workflows at once (across all repos) to maximize the
# cache hits (many workflows share many actions like actions/checkout)
GITHUB_TOKEN=XXXXX uv run pinner.py */.github/workflows/*.yml

# Steps 3 and 3b could have been avoided with a better YAML roundtrip logic in pinner.py
# like a regex-y search replace for ` uses: +\S+`, but well :shrug:
# 3. Clean up the whitespace diffs
for repo in $(cat napari-repos.txt); do
    cd $repo
    git diff -U0 -w --no-color --ignore-blank-lines | git apply --cached --ignore-whitespace --unidiff-zero - && git checkout .
    cd ..
done

# 3b. Go to your IDE and review all the diffs, fixing issues as they appear
code .

# 4. Commit all the changes and send the PRs
for repo in $(cat napari-repos.txt); do
    cd $repo
    if env PAGER=cat git diff --check; then
        git checkout -b pin-gha
        git add .
        git commit -m "Pin Github Actions actions to their hashes"
        git push
    fi
    gh repo set-default napari/$repo
    gh pr create --title "Pin Github Actions actions to their hashes" --body "Part of https://github.com/napari/packaging/issues/259" | tee -a ../prs.txt
    cd ..
done

Metadata

Metadata

Assignees

No one assigned

    Labels

    ciContinuous infrastructure

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions