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:
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@v3
uses: 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 set
command:
$ pinact token set
Enter a GitHub access token: # Input GitHub Access token
or 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=true
Note 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 rm
Please run pinact run
on a Git repository root directory, then target files are fixed.
pinact run
Default 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 -u
pinact can fix example codes in documents too.
pinact run README.md
As 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 init
You 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 --check
Using 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 $?
1
If -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 -fix
And if you want to output diff, please use -diff
option.
pinact run -check -diff
Please 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=pinact
The 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 |
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: 3
In 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: main
This 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.md
This is optional. A list of ignored actions and reusable workflows.
This is required. A regular expression of ignored actions and reusable workflows.
ignored_actions:
- name: actions/.*
ref: main
Warning
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.yaml
Version: main
# yaml-language-server: $schema=https://raw.githubusercontent.com/suzuki-shunsuke/pinact/main/json-schema/pinact.json
Or pinning version:
# yaml-language-server: $schema=https://raw.githubusercontent.com/suzuki-shunsuke/pinact/v1.1.2/json-schema/pinact.json
In 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@main
We 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 # main
Tools 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.2
We 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.