Skip to main content

gh-action-pulse

gh-action-pulse scans a repository for GitHub Actions uses: references, checks them against the GitHub API, and rewrites them to safer or more current references when a better upstream target exists.

It is aimed at repositories that want to keep GitHub Actions dependencies understandable, current, and pinned with more confidence.

Key Features

  • Automatic scanning: Detects uses: statements across workflow and reusable action files.
  • Reference classification: Distinguishes whether an action is currently pinned to a commit SHA, tag, or branch.
  • Recommendation engine: Looks up upstream metadata and recommends an updated reference based on available SemVer tags and branch state.
  • Repository redirect handling: Rewrites moved repositories to their canonical name when GitHub reports a redirect.
  • Freshness checks: Warns or fails when the newest eligible SemVer tag is older than your configured threshold.
  • Node.js runtime check: Recursively verifies that actions, including composite and local composite dependencies, run on at least a configurable minimum Node.js version (--minimum-nodejs-version, default 24), failing with a dedicated exit code (3) when an outdated runtime is detected.
  • Comment preservation: Keeps extra trailing comments on uses: lines (for example gh-action-pulse: ignore[max-days]) when rewriting references.

How It Works

For each detected uses: line, gh-action-pulse:

  1. Finds GitHub Actions references in the configured workflow and action directories.
  2. Queries the GitHub API for the referenced repository.
  3. Detects whether the current reference is a tag, branch, or SHA.
  4. Selects the newest SemVer tag that is at least --min-age days old.
  5. Falls back to a branch recommendation when that is safer or newer than the eligible tag.
  6. Rewrites redirected repositories to their canonical upstream name.

In practice, this means the tool can:

  • convert branch or tag references into pinned SHAs annotated with the matching tag,
  • preserve branch intent when no suitable tag exists,
  • keep extra trailing comments such as ignore hints,
  • surface stale upstream action releases with a non-zero exit code.

Example

Before:

- uses: google-github-actions/auth@v2

After:

- uses: google-github-actions/auth@<commit-sha> # v2.1.10

If an action repository has moved, the repository name may also be rewritten to the canonical upstream location.

Setup

gh-action-pulse talks to the GitHub API.

Set a token explicitly:

export GITHUB_TOKEN=your_github_token_here

If GITHUB_TOKEN is not set, the tool can fall back to using the GitHub CLI authentication flow when gh is available.

The project currently requires Python >= 3.14.

Installation

Install from PyPI with uv

uv tool install gh-action-pulse

Install from PyPI with pipx

pipx install gh-action-pulse

Install the local checkout

uv tool install . --force --reinstall

CLI Usage

Run against the current repository:

gh-action-pulse

Preview changes without writing files:

gh-action-pulse --dry-run

Require action tags to be at least 14 days old before they can be selected:

gh-action-pulse --min-age 14

Fail when the newest eligible tag is older than 180 days:

gh-action-pulse --min-age 14 --max-age 180

Require actions to run on at least Node.js 20:

gh-action-pulse --minimum-nodejs-version 20

Show more detail while debugging:

gh-action-pulse --log-level DEBUG

Print the installed version:

gh-action-pulse --version

Output

The CLI uses Rich for progress and summaries on stderr:

  • a progress bar while enriching actions from the GitHub API (and while checking Node.js runtimes);
  • colored phase lines for scan, freshness, and Node.js checks;
  • a table of proposed or applied uses: rewrites (yellow header in --dry-run);
  • a closing summary panel with update counts, warnings, and the exit code.

Routine per-file and per-action chatter is logged at DEBUG. Use --log-level DEBUG (or WARNING / ERROR) when you need diagnostic detail; warnings and errors still use Rich-formatted logging without repeating the main user-facing summary.

CLI Options

  • --dry-run (GH_ACTION_PULSE_DRY_RUN): show the updates without writing files.
  • --log-level (GH_ACTION_PULSE_LOG_LEVEL): set the logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL).
  • --min-age (GH_ACTION_PULSE_MIN_AGE): require tags to be at least this many days old before recommending them.
  • --max-age (GH_ACTION_PULSE_MAX_AGE): fail when the chosen --min-age-eligible upstream tag is older than this many days. Use 0 to disable the check.
  • --minimum-nodejs-version (GH_ACTION_PULSE_MINIMUM_NODEJS_VERSION): fail when an action, or any of its composite/local dependencies, runs on a Node.js major version below this value (default 24). Use 0 to disable the check.
  • --version: print the package version and exit.

CLI flags override the matching environment variables when both are set.

Exit Codes

gh-action-pulse uses its exit code to signal the outcome of a run:

  • 0: the run completed and no failing condition was detected.
  • 2: GitHub authentication failed (GITHUB_TOKEN could not be resolved).
  • 3: a Node.js runtime problem was detected in the repository. When an action, or any of its composite/local composite dependencies, runs on a Node.js major version below --minimum-nodejs-version (default 24), the tool logs an error and exits with status 3. Set --minimum-nodejs-version 0 to disable this check.
  • 4: a referenced upstream action repository is archived.
  • 5: a --max-age staleness failure occurred.

When multiple failing conditions are detected in the same run, the exit code with the lowest number is returned: authentication (2) and archived repositories (4) stop the run early, and among end-of-run checks the Node.js exit code (3) takes precedence over stale tags (5).

Limitations

  • Local actions such as ./.github/actions/my-action are not part of the GitHub API lookup flow.
  • Recommendations depend on repositories exposing usable SemVer tags.
  • The tool needs GitHub API access, so rate limits and authentication still apply.

Node.js version check (--minimum-nodejs-version)

The Node.js runtime check does not inspect every uses: line in the repository. In practice it:

  • only starts from remote GitHub Actions referenced as owner/repo@ref (or owner/repo/path@ref) in .github/workflows and .github/actions;
  • skips local actions such as uses: ./.github/actions/my-action because they do not match the name@reference pattern used during scanning;
  • inspects the recommended upstream reference (the one the tool would update to), not the currently pinned reference when a recommendation exists;
  • only flags JavaScript actions whose manifest declares runs.using: nodeXX (for example node20, node24);
  • skips Docker actions (docker://), unresolvable references, missing manifests, and other non-nodeXX runtimes;
  • walks composite actions recursively and checks nested uses: dependencies, including relative ./path steps inside a remote composite action (resolved within that upstream repository);
  • does not meaningfully check reusable workflows referenced as uses: org/repo/.github/workflows/foo.yml@ref, because it looks for action.yml/action.yaml manifests rather than workflow files.

Set --minimum-nodejs-version 0 to disable this check entirely.

Roadmap

Possible future improvements:

  • Maybe Separate unit tests with appropriate workflow (pytest) if checks takes times
  • Add E2E tests with appropriate workflow (pytest and/or bats)
  • Change to versioned version of tools in mise.toml when near stable version (could depend on tools)

Potential future CLI options (to get the idea):

  • --config-file: load configuration, including ignore parameters or specific rules for some workflows (needs thinking).
  • --only-workflow: restrict scanning to a specific workflow.
  • --workflow-omit: exclude specific workflows from scanning.
  • --github-action-omit: exclude specific GitHub Actions from the checks.

Contributing

Contributions are welcome. See CONTRIBUTING.md for the code layout, local setup, and the full list of linters and checks run in CI.

Release files for gh-action-pulse 1.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for gh-action-pulse 1.2.0
File Interpreter ABI Platform
gh_action_pulse-1.2.0-py3-none-any.whl Python 3 none any Details

Release files / gh_action_pulse-1.2.0-py3-none-any.whl

Download URL gh_action_pulse-1.2.0-py3-none-any.whl
Size 40.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
59618a207e71e575bbfe361aa2dd4f64cd0987e9c2a8f403c0e851472cfcfcb4
BLAKE2b-256 checksum
How to use checksums
85e1722fba708e235c2b23480d355e7d93b5c7987803bad0ef825cb6d05bf237
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 16, 2026.

Transparency log

Release history Release notifications | RSS feed

1.5.1

1 release file

1.5.0

1 release file

1.4.1

1 release file

1.4.0

1 release file

1.3.0

1 release file

This release

1.2.0 This release

1 release file

1.1.0

1 release file

1.0.0

1 release file

0.5.2

1 release file

0.5.1

1 release file

0.2.0

1 release file

0.1.1

1 release file

0.1.0

1 release file

0.0.13

1 release file

0.0.12

1 release file

0.0.11

1 release file

0.0.10

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page