Skip to content

suzuki-shunsuke/pinact

Repository files navigation

pinact

Ask DeepWiki Install | How to use | Configuration

pinact is a CLI to pin GitHub Actions and Reusable Workflows. pinact can also update their versions and verify version comments.

$ pinact run
.github/workflows/test.yaml:8
-       - uses: actions/checkout@83b7061638ee4956cf7545a6f7efe594e5ad0247 # v3
+       - uses: actions/checkout@83b7061638ee4956cf7545a6f7efe594e5ad0247 # v3.5.1
.github/workflows/test.yaml:9
-       - uses: actions/setup-go@v4
+       - uses: actions/setup-go@7b8cf10d4e4a01d4992d18a89f4d7dc5a3e6d6f4 # v4.3.0
.github/workflows/test.yaml:10
-       - uses: actions/cache@v3.3.1
+       - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
.github/workflows/test.yaml:16
-     uses: suzuki-shunsuke/actionlint-workflow/.github/workflows/actionlint.yaml@v0.5.0
+     uses: suzuki-shunsuke/actionlint-workflow/.github/workflows/actionlint.yaml@b6a5f966d4504893b2aeb60cf2b0de8946e48504 # v0.5.0

Features

  1. Pin GitHub Actions and Reusable Workflows
  2. Check if actions are pinned without editing files
  3. Offline check without GitHub API
  4. Update actions with a minimum release age
  5. Verify version comments (-verify-comment)
  6. Require a version comment on SHA-pinned actions
  7. Verify if actions meet the minimum release age
  8. Pin branches
  9. Include and exclude specific actions
  10. Generate SARIF. This is useful to create reviews using reviewdog
  11. Read GitHub access token via keyrings or ghtkn
  12. Pin only changed lines
  13. Support GitHub Enterprise Server
  14. GitHub Action

Usage

pinact run [<workflow file>...]

If no file is specified, the following files are pinned:

.github/workflows/*.yml
.github/workflows/*.yaml
action.yml
action.yaml
*/action.yml
*/action.yaml
*/*/action.yml
*/*/action.yaml
*/*/*/action.yml
*/*/*/action.yaml

pinact calls GitHub API to fetch releases and tags. To avoid api rate limiting, you should pass a GitHub Access token.

Fix example codes in documents

Not only workflow files, but also text files of any formats are supported. This is useful to pin actions in text files such as README.md.

pinact run README.md

Just Validation: -check, -fix=false

By default, pinact edit files. If -check or -fix=false is specified, pinact just checks if actions are pinned without editing files.

pinact run -check

Offline Check: -no-api

For an offline check (no GitHub API call, only the 40-character SHA syntactic check), add -no-api:

pinact run -fix=false -no-api

With -no-api, pinact can't fetch action versions and SHA, so pinact can't pin actions. So it only checks if actions are pinned with full-length commit SHA.

Update Actions: -update

Update actions to latest versions:

pinact run -update

Minimum Release Age (Cooldown): -min-age, -verify-min-age

pinact supports two kinds of minimum release age checks:

  1. Verify current versions: Verify if current action versions meet the minimum release age requirement
  2. Verify new versions: Exclude versions that don't meet the minimum release age requirement when updating actions (-update)
    1. If no release meeting the given minimum age is found, pinact will exit with an error.

This helps reduce supply chain security risks.

By default, no minimum release age is set. You can set the minimum release age by some methods:

  1. -min-age <minimum release age>: Set the minimum release age in days
pinact run -min-age 7
  1. Environment variable PINACT_MIN_AGE
  2. Configuration file .pinact.yml
    1. .rules[].min_age: A rule specific minimum release age in days
    2. .min_age.value: The default minimum release age in days
min_age:
  value: 7
rules:
  - min_age: 0
    conditions:
      - expr: |
          ActionRepoOwner == "suzuki-shunsuke"

It may be wasteful to verify all current versions against the minimum release age every time pinact runs. Therefore, current versions are verified using the min_age setting in .pinact.yml and PINACT_MIN_AGE only when --verify-min-age is set or .min_age.always is true.

pinact run -verify-min-age

Or

min_age:
  value: 7
  always: true # default is false

On the other hand, when updating actions min_age setting is always used to filter new versions.

  • For GitHub Releases, the PublishedAt date is checked
  • For tags, the commit's Committer.Date is checked (requires additional API call)

Verify Version Comments: -verify-comment (-verify, -v)

Please see Verify version comments.

pinact run -verify-comment

Pin Branches: -branch-to-tag

pinact >= v3.10.0, #1529

By default, pinact doesn't pin branches such as main or master. If you want to pin specific branches, you can use the --branch-to-tag option.

pinact run --branch-to-tag '<regular expression matching branch name>'

The value is evaluated as a regular expression with partial match, just like --include / --exclude. Anchor with ^...$ for an exact match - for short branch names like main this is recommended to avoid matching mainline etc. Versions that don't match any of the supplied regexps continue to error out as before.

The branch is converted to the latest stable tag of the action. Pre-releases are used only when no stable tag exists.

--min-age is honored: when set, tags released within the cooldown window are skipped.

--branch-to-tag can be specified multiple times.

e.g.

pinact run --branch-to-tag '^main$' --branch-to-tag '^release/.*$'

Include and exclude specific actions

#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$"

SARIF

pinact >= v3.7.0 #1294

pinact can output the result in the SARIF format.

pinact run --format sarif

This format is useful to integration tools like reviewdog and GitHub SARIF Code Scanning.

-format sarif implies -fix=false, so files are not modified. If you want to fix files, use -fix.

pinact run --format sarif -fix

Reviewdog

pinact run -format sarif |
  reviewdog -f sarif -name pinact -reporter github-pr-review

GitHub SARIF Code Scanning

- run: pinact run -format sarif > sarif.json || true
- name: Upload SARIF file
  uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9
  with:
    sarif_file: sarif.json
    category: pinact

Pin Only Changed Lines

pinact >= v4.0.0

pinact supports pinning only changed lines using the -diff-file option. This is useful to introduce pinact gradually.

pinact run -diff-file diff.txt 

diff.txt must be the unified diff format.

example

diff --git a/testdata/diff-file/action.yaml b/testdata/diff-file/action.yaml
index 0000001..0000002 100644
--- a/testdata/diff-file/action.yaml
+++ b/testdata/diff-file/action.yaml
@@ -7,5 +7,5 @@ jobs:
     permissions: {}
     steps:
       - uses: actions/checkout@v3.6.0
-      - uses: actions/setup-go@v3.5.0
+      - uses: actions/setup-go@v4.0.0
       - uses: actions/cache@v3.3.1

-diff-file - means reading from stdin:

cat diff.txt | pinact run -diff-file -

You can generate a diff file via GitHub Actions using pr-unified-diff-action.

- uses: suzuki-shunsuke/pr-unified-diff-action@c932c1df5f577028d8ca05d2d3c0c059072d8821 # v0.0.1
  id: diff
- run: pinact run -diff-file "$DIFF_FILE"
  env:
    DIFF_FILE: ${{ steps.diff.outputs.diff_path }}

GitHub Access token

pinact calls GitHub REST API to get commit hashes and tags. You can pass GitHub Access token via environment variable PINACT_GITHUB_TOKEN or GITHUB_TOKEN. If no GitHub Access token is passed, pinact calls GitHub REST API without access token. About GitHub Enterprise Server, see also GitHub Access Token for GHES.

Manage GitHub Access token using ghtkn

pinact >= v3.8.0

You can create a GitHub App User Access Token by ghtkn integration. About ghtkn, please see the document of ghtkn. You need to set up ghtkn first.

export PINACT_GHTKN=true

Manage GitHub Access token using Keyring

pinact >= v3.1.0

You can manage a GitHub Access token using secret store such as Windows Credential Manager, macOS Keychain, and GNOME Keyring.

  1. 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
  1. 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

Configuration Priority

  1. Command line options
  2. Environment variables
  3. Local configuration file
  4. Global configuration file

Configuration File

JSON Schema

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. You can generate a configuration file by pinact init.

pinact init [<configuration file path>]

Furthermore, pinact supports a global configuration file for user-wide defaults.

For more details, see Configuration File.

Exit codes

Code Meaning
0 Everything is pinned, or pinact fixed it
1 -fix=false was set and something needs pinning
2 An action cannot be auto-fixed (branch reference, missing version comment on a SHA pin, -verify-comment mismatch, or -min-age violation)
3 GitHub API error, invalid CLI flag combination, or other unexpected error

GitHub Actions

https://github.com/suzuki-shunsuke/pinact-action

We develop GitHub Actions to pin GitHub Actions and reusable workflows by pinact.

Q. Why doesn't pinact pin some actions?

Tip

Since v3.10.0, the --branch-to-tag option lets you opt-in to pinning specific branches to the latest stable tag of an action.

In some cases pinact doesn't pin versions intentionally, which may confuse you. For instance, pinact doesn't pin branches like main and master by default. For more details, please see here?.

Motivation

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/cache@v3.3.1

Why not using Renovate's helpers:pinGitHubActionDigestsToSemver preset?

The Renovate preset helpers:pinGitHubActionDigestsToSemver is useful, but pinact is still useful: You can use both the preset and pinact together.

  1. 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)
  2. Even if you use Renovate, sometimes you would want to update actions manually
  3. pinact is useful for non Renovate users
  4. pinact supports verifying version annotations

See also

About

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 and verify version annotations.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Sponsor this project

 

Packages

 
 
 

Contributors

Languages