Motivation | Install | How to use | GitHub Actions | Configuration | Contributing | LICENSE
pinact is a CLI to edit GitHub Workflow and Composite action files and pin versions of Actions and Reusable Workflows. pinact can also update their versions, verify version annotations, and create reviews.
pinact run$ git diff
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 84bd67a..5d92e44 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -113,17 +113,17 @@ jobs:
needs: path-filter
permissions: {}
steps:
- - uses: actions/checkout@83b7061638ee4956cf7545a6f7efe594e5ad0247 # v3
- - uses: actions/setup-go@v4
+ - uses: actions/checkout@83b7061638ee4956cf7545a6f7efe594e5ad0247 # v3.5.1
+ - uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
- name: Cache Primes
id: cache-primes
- uses: actions/[email protected]
+ uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
with:
path: prime-numbers
key: ${{ runner.os }}-primes
actionlint:
- uses: suzuki-shunsuke/actionlint-workflow/.github/workflows/[email protected]
+ uses: suzuki-shunsuke/actionlint-workflow/.github/workflows/actionlint.yaml@b6a5f966d4504893b2aeb60cf2b0de8946e48504 # v0.5.0
with:
aqua_version: v2.3.4
permissions:Creating reviews:
You can ask any questions about pinact to NotebookLM.
It is a good manner to pin GitHub Actions versions by commit hash. GitHub tags are mutable so they have a substantial security and reliability risk.
See also Security hardening for GitHub Actions - GitHub Docs
Pinning an action to a full length commit SHA is currently the only way to use an action as an immutable release. Pinning to a particular SHA helps mitigate the risk of a bad actor adding a backdoor to the action's repository, as they would need to generate a SHA-1 collision for a valid Git object payload
👍
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1👎
uses: actions/cache@v3uses: actions/[email protected]The Renovate preset helpers:pinGitHubActionDigestsToSemver is useful, but pinact is still useful:
- Renovate can't pin actions in pull requests before merging them. If you use linters such as ghalint in CI, you need to pin actions before merging pull requests (ref. ghalint policy to enforce actions to be pinned)
- Even if you use Renovate, sometimes you would want to update actions manually
- pinact is useful for non Renovate users
- pinact supports verifying version annotations
pinact calls GitHub REST API to get commit hashes and tags.
You can pass GitHub Access token via environment variable GITHUB_TOKEN.
If no GitHub Access token is passed, pinact calls GitHub REST API without access token.
pinact >= v3.1.0
You can manage a GitHub Access token using secret store such as Windows Credential Manager, macOS Keychain, and GNOME Keyring.
- Configure a GitHub Access token by
pinact token setcommand:
$ pinact token set
Enter a GitHub access token: # Input GitHub Access tokenor you can also pass a GitHub Access token via standard input:
echo "<github access token>" | pinact token set -stdin- Enable the feature by setting the environment variable
PINACT_KEYRING_ENABLED:
export PINACT_KEYRING_ENABLED=trueNote that if the environment variable GITHUB_TOKEN is set, this feature gets disabled.
You can remove a GitHub Access token from keyring by pinact token rm command:
pinact token rmPlease run pinact run on a Git repository root directory, then target files are fixed.
pinact runDefault target files are:
.github/workflows/*.yml
.github/workflows/*.yaml
action.yml
action.yaml
*/action.yml
*/action.yaml
*/*/action.yml
*/*/action.yaml
*/*/*/action.yml
*/*/*/action.yaml
You can change target files by command line arguments or configuration files.
e.g.
pinact run example.yaml#663 pinact >= v1.1.0
You can update actions using the -update (-u) option:
pinact run -upinact can fix example codes in documents too.
pinact run README.mdAs of pinact v3.3.0, pinact can create reviews by GitHub API.
A GitHub access token with pull_requests:write permission is required.
pinact run \
-review \
-repo-owner <repository owner> \
-repo-name <repository name> \
-pr <pull request number> \
-sha <commit SHA to be reviewed>If pinact is run via GitHub Actions pull_request event, options are auto-completed.
Warning
GitHub can't create pull request reviews on files not changed by the pull request. When pinact fails to create reviews, pinact outputs warning and creates GitHub Actions error messages to log instead. You can ignore the warning like this:
WARN[0004] create a review comment error="create a review comment: POST https://api.github.com/repos/szksh-lab-2/test-github-action/pulls/317/comments: 422 Validation Failed [{Resource:PullRequestReviewComment Field:pull_request_review_thread.path Code:invalid Message:} {Resource:PullRequestReviewComment Field:pull_request_review_thread.diff_hunk Code:missing_field Message:}]" line=" - uses: suzuki-shunsuke/watch-star-action@feat/first-pr" line_number=14 pinact_version=3.3.0-5 program=pinact review_pr_number=317 review_repo_name=test-github-action review_repo_owner=szksh-lab-2 review_sha=92f0b04efdc10acb793e78bdd1f70958dd3fd9a3 workflow_file=.github/workflows/watch.yaml
A configuration file is optional.
You can create a configuration file .pinact.yaml by pinact init.
pinact initYou can change the output path.
pinact init '.github/pinact.yaml'About the configuration, please see Configuration.
pinact >= v1.6.0 #816
Instead of fixing files, you can validate if actions are pinned by --check option:
pinact run --checkUsing this option, pinact doesn't fix files. If actions aren't pinned, the command fails.
$ pinact run --check
ERRO[0000] parse a line action=actions/checkout@v2 error="action isn't pinned" pinact_version= program=pinact workflow_file=testdata/foo.yaml
ERRO[0000] parse a line action=actions/[email protected] error="action isn't pinned" pinact_version= program=pinact workflow_file=testdata/foo.yaml
ERRO[0000] parse a line action=rharkor/[email protected] error="action isn't pinned" pinact_version= program=pinact workflow_file=testdata/foo.yaml
ERRO[0000] parse a line action=actions/checkout@v3 error="action isn't pinned" pinact_version= program=pinact workflow_file=testdata/foo.yaml
ERRO[0000] parse a line action=actions/checkout@v3 error="action isn't pinned" pinact_version= program=pinact workflow_file=testdata/foo.yaml
ERRO[0000] parse a line action=suzuki-shunsuke/actionlint-workflow/.github/workflows/[email protected] error="action isn't pinned" pinact_version= program=pinact workflow_file=testdata/foo.yaml
$ echo $?
1If -check is set, files aren't fixed and no diff is outputted.
If you want to fix files, please use -fix option.
pinact run -check -fixAnd if you want to output diff, please use -diff option.
pinact run -check -diffPlease see the document.
$ pinact run -diff
INFO[0000] action isn't pinned
.github/workflows/test.yaml:8
- - uses: actions/checkout@v4
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
pinact_version=v3.0.0-local program=pinactThe behaviour of pinact run command is changed by command line options -diff, -check, and -fix.
This is a table how the behaviour is changed by these options.
| options | Fix files | Exit with code 1 if actions aren't pinned | Output changes |
|---|---|---|---|
| No option | o | ||
| -check | o | ||
| -diff | o | ||
| -check -diff | o | o | |
| -check -fix | o | o | o |
| -fix -diff | o | o |
#1082 pinact >= v3.4.0
You can fix only specific actions using the -include (-i) <regular expression> option.
You can also exclude only specific actions using the -exclude (-e) <regular expression> option.
e.g.
pinact run -i "actions/.*" -i "^aquaproj/aqua-installer$"pinact run -e "actions/.*" -e "^aquaproj/aqua-installer$"https://github.com/suzuki-shunsuke/pinact-action
We develop GitHub Actions to pin GitHub Actions and reusable workflows by pinact.
A configuration file is optional.
pinact supports a configuration file .pinact.yaml, .github/pinact.yaml, .pinact.yml or .github/pinact.yml.
You can also specify the configuration file path by the environment variable PINACT_CONFIG or command line option -c.
As of pinact v2.2.0, pinact configuration file has a schema version.
version: 3In general, you should use the latest schema version.
pinact v2.2.0 or later supports this version.
.pinact.yaml
e.g.
version: 3
files:
- pattern: .github/workflows/*.yml
- pattern: .github/workflows/*.yaml
- pattern: .github/actions/*/action.yml
- pattern: .github/actions/*/action.yaml
ignore_actions:
# slsa-framework/slsa-github-generator doesn't support pinning version
# > Invalid ref: 68bad40844440577b33778c9f29077a3388838e9. Expected ref of the form refs/tags/vX.Y.Z
# https://github.com/slsa-framework/slsa-github-generator/issues/722
- name: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml
ref: "v\\d+\\.\\d+\\.\\d+"
- name: suzuki-shunsuke/.*
ref: mainThis is optional. A list of target files.
This is required. A glob pattern of target files. Go's path/filepath#Glob is used. A relative path from pinact's configuration file. If files are passed via positional command line arguments, the configuration is ignored.
e.g.
files:
- pattern: .github/workflows/*.yml
- pattern: .github/workflows/*.yaml
- pattern: README.mdThis is optional. A list of ignored actions and reusable workflows.
This is required. A regular expression of ignored actions and reusable workflows.
ignore_actions:
- name: actions/.*
ref: mainWarning
Regular expressions must match with action names exactly.
For instance, name: actions/ doesn't match with actions/checkout
Regarding regular expressions, Go's regexp package is used.
This is required. A regular expression of ignored action versions (branch, tag, or commit hash).
Warning
Regular expressions must match with action names exactly.
For instance, ref: main doesn't match with malicious-main
Please see here.
- pinact.json
- https://raw.githubusercontent.com/suzuki-shunsuke/pinact/refs/heads/main/json-schema/pinact.json
If you look for a CLI tool to validate configuration with JSON Schema, ajv-cli is useful.
ajv --spec=draft2020 -s json-schema/pinact.json -d pinact.yamlVersion: main
# yaml-language-server: $schema=https://raw.githubusercontent.com/suzuki-shunsuke/pinact/main/json-schema/pinact.jsonOr pinning version:
# yaml-language-server: $schema=https://raw.githubusercontent.com/suzuki-shunsuke/pinact/v1.1.2/json-schema/pinact.jsonIn some cases pinact doesn't pin versions intentionally, which may confuse you. So we describe the reason here.
pinact doesn't pin actions whose versions aren't semver (e.g. main, master, release/v1).
This is because pinact is designed as a safe tool so that it doesn't change workflows behaviour.
pinact pins actions but doesn't change SHA of actions at the moment when pinact pins versions.
This design enables you to accept changes by pinact safely.
For instance, pinact changes the version v1 to v1.1.0 if their SHA are equivalent.
If there are no semver whose SHA is same with v1, pinact doesn't change the version.
And pinact doesn't change versions which aren't semver.
For instance, pinact doesn't change the version main.
uses: actions/checkout@mainWe don't want to pin main to full commit length SHA like the following because we can't update this following semantic versioning.
uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2 # mainTools like Renovate can update the SHA, but it's not safe at all as main branch isn't stable.
And we don't want to change main to the latest semver like the following because SHA is changed and workflows may be broken.
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2We don't want to pin branches as SHA of branches is changed.
pinact doesn't check if a version is a tag or a branch because we would like to reduce the number of API calls as much as possible. If a version isn't semver, pinact judges it may be a branch so pinact doesn't pin it.
Please see also #926.
- Renovate github-actions Manager - Additional Information
- sethvargo/ratchet is a great tool, but there are known issues.

