Tags are mutable. SHAs aren't. Pinning actions to commit SHAs protects your CI from supply-chain attacks.
Install
Download a prebuilt binary
Grab the latest release for your platform from GitHub Releases.
macOS (Apple Silicon)
curl -sL https://github.com/TheDen/ghapin/releases/latest/download/ghapin_darwin_arm64 -o ghapin chmod +x ghapin sudo mv ghapin /usr/local/bin/
macOS (Intel)
curl -sL https://github.com/TheDen/ghapin/releases/latest/download/ghapin_darwin_amd64 -o ghapin chmod +x ghapin sudo mv ghapin /usr/local/bin/
Linux (x86_64)
curl -sL https://github.com/TheDen/ghapin/releases/latest/download/ghapin_linux_amd64 -o ghapin chmod +x ghapin sudo mv ghapin /usr/local/bin/
Linux (ARM64)
curl -sL https://github.com/TheDen/ghapin/releases/latest/download/ghapin_linux_arm64 -o ghapin chmod +x ghapin sudo mv ghapin /usr/local/bin/
Windows
Download ghapin_windows_amd64,
rename it to ghapin.exe, and add it to a directory in your PATH.
Verify
Go install
go install github.com/TheDen/ghapin@latest
Build from source
git clone https://github.com/TheDen/ghapin.git cd ghapin go build -o ghapin .
Quick start
# See what would change ghapin .github/workflows/*.yaml # Happy with the output? Write it back ghapin -w .github/workflows/*.yaml
Usage
ghapin [flags] <files...>
ghapin [command]
Running ghapin with no arguments prints help.
Commands
| Command | Description |
|---|---|
list |
Discover and list workflow files under .github/workflows/ |
help |
Help about any command |
Flags
| Flag | Short | Description |
|---|---|---|
--write |
-w |
Write pinned output back to the file in-place |
--dry-run |
-n |
Show what would change, don't produce output |
--comment |
-c |
Keep the original ref as an inline comment (# v1.29.5) |
--verbose |
-v |
Log skipped and already-pinned actions |
--quiet |
-q |
Suppress all progress output on stderr |
--include-first-party |
Pin first-party actions too (actions/*) |
|
--skip-org |
Skip all actions from this org (repeatable) | |
--skip-action |
Skip a specific action by owner/repo (repeatable) |
|
--version |
Print version | |
--help |
-h |
Print help |
-w and -n are mutually exclusive. So are -v and -q.
Examples
Preview before writing
By default ghapin prints the pinned YAML to stdout so you can inspect it,
pipe it, or diff it before committing to anything:
ghapin .github/workflows/ci.yaml | diff .github/workflows/ci.yaml -Write in-place
ghapin -w .github/workflows/*.yamlDry-run
See which actions would be pinned without producing any file output:
ghapin -n .github/workflows/*.yaml .github/workflows/ci.yaml: crate-ci/typos@v1.29.5 -> 11ca4583f2f3
.github/workflows/ci.yaml: docker/build-push-action@v5 -> ca052bb54ab0
Keep the original ref as a comment
ghapin -wc .github/workflows/*.yaml- uses: crate-ci/typos@11ca4583f2f3f74c7e7785c0ecb20fe2c99a4308 # v1.29.5
Comments are off by default. This avoids stale comments if a tool like Dependabot later bumps the SHA without updating the comment.
Discover workflow files
.github/workflows/ci.yaml
.github/workflows/release.yml
Useful for scripting:
Pin first-party actions
By default actions/* (checkout, setup-node, cache, etc.) are skipped.
To pin them too:
ghapin -w --include-first-party .github/workflows/*.yamlSkip specific orgs or actions
ghapin -w --skip-org docker --skip-org google .github/workflows/*.yaml ghapin -w --skip-action hashicorp/setup-terraform .github/workflows/*.yaml
Verbose output
See everything ghapin considers, including what it skips and why:
ghapin -nv .github/workflows/ci.yaml
ci.yaml: skip actions/checkout (org "actions" excluded)
ci.yaml: crate-ci/typos@v1.29.5 -> 11ca4583f2f3
ci.yaml: skip docker/build-push-action (already pinned)
Testing
License
GNU General Public License v2.0 (GPL-2.0).
See LICENSE.