MCPcopy Index your code
hub / github.com/azat-io/actions-up

github.com/azat-io/actions-up @v1.16.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release v1.16.0 ↗ · + Follow
234 symbols 654 edges 151 files 65 documented · 28% 9 cross-repo links updated 6d agov1.16.0 · 2026-07-02★ 6042 open issues
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Actions Up!

Actions Up logo

Version Code Coverage GitHub License

Actions Up scans your workflows and composite actions to discover every referenced GitHub Action, then checks for newer releases.

Interactively upgrade and pin actions to exact commit SHAs for secure, reproducible CI, or preserve tag-style references when you need to stay on tags.

Features

  • Auto-discovery: Scans all workflows (.github/workflows/*.yml) and composite actions (.github/actions/*/action.yml and root action.yml/action.yaml)
  • Reusable Workflows: Detects and updates reusable workflow calls at the job level
  • Flexible update styles: Use SHA pinning by default, or preserve tag-style references with --style preserve
  • Batch Updates: Update multiple actions at once
  • Interactive Selection: Choose which actions to update
  • Breaking Changes Detection: Warns about major version updates
  • Fast & Efficient: Optimized API usage with deduped lookups
  • CI/CD Integration: Use in GitHub Actions workflows for automated PR checks

Actions Up! interactive example

Why

Keeping GitHub Actions updated is critical and time-consuming. Actions Up scans all workflows, highlights available updates, and can pin actions to SHAs for reproducibility.

Without Actions Up With Actions Up
Check each action manually Scan all workflows in seconds
Risk using vulnerable versions SHA pinning for maximum security
30+ minutes per repository Under 1 minute total

Security Motivation

GitHub Actions run arbitrary code in your CI. If a job has secrets available, any action used in that job can read the environment and exfiltrate those secrets. A compromised action or a mutable version tag is a direct path to leakage.

Actions Up reduces risk by:

  • Pinning actions to commit SHAs to prevent tag hijacking
  • Making outdated actions visible and showing exactly what runs in CI
  • Warning about major updates so you can review changes before applying them

Note: secrets are available on push, workflow_dispatch, schedule, and pull_request_target triggers (and on fork PRs if explicitly enabled). Always scope workflow permissions to the minimum required.

Installation

Quick use (no installation)

npx actions-up

Global installation

npm install -g actions-up

Per-project

npm install --save-dev actions-up

Alternatively, you can install Actions Up with Homebrew

brew install actions-up

Usage

Interactive Mode (Default)

Run in your repository root:

npx actions-up

This will:

  1. Scan all .github/workflows/*.yml and .github/actions/*/action.yml files, plus root action.yml/action.yaml
  2. Check for available updates
  3. Show an interactive list to select updates
  4. Apply selected updates with SHA pinning by default

Auto-Update Mode

Skip all prompts and update everything:

npx actions-up --yes
# or
npx actions-up -y

Dry Run Mode

Check for updates without making any changes:

npx actions-up --dry-run

JSON Mode

Output a machine-readable JSON report instead of the interactive UI:

npx actions-up --json

--json is report-only: it never writes files, skips the interactive prompt, and cannot be combined with --yes.

Custom Directory

By default, Actions Up scans .github.

Use --dir to choose another directory, and pass it multiple times to scan several directories:

npx actions-up --dir .gitea
npx actions-up --dir .github --dir ./other/.github

Recursive Scanning

Use --recursive (-r) to scan YAML workflow/composite-action files recursively in the selected directories:

npx actions-up -r
npx actions-up --dir ./gh-repo-defaults -r

When --recursive is used without --dir, Actions Up scans from the current directory (.).

Branch References

By default, actions pinned to branch refs (e.g., @main, @release/v1) are skipped to avoid changing intentionally floating references. Skipped entries are listed in the output. To include them in update checks, pass --include-branches.

Quiet Mode

Use --quiet (-q) to hide the skipped and blocked-update warnings (for example, actions intentionally pinned to branches). Other output — results, applied updates, and errors — is unchanged.

npx actions-up --yes --quiet

Update Mode

By default, Actions Up allows major updates. Use --mode to limit updates:

npx actions-up --mode minor
npx actions-up --mode patch

In minor and patch modes, Actions Up tries to find the newest compatible tag first (for example, from @v4 in minor mode it will choose the latest v4.x.y). If no compatible version exists, that action is skipped.

Update Style

By default, Actions Up writes updates as pinned SHAs:

npx actions-up --style sha

Use --style preserve to keep the current reference style:

npx actions-up --style preserve

preserve keeps tag references on tags and SHA references on SHAs. Tag refs also keep their granularity, so actions/checkout@v5 updates to actions/checkout@v6, while actions/checkout@v5.0 updates to actions/checkout@v6.0. A SHA-pinned action continues updating to the latest resolved SHA.

GitHub Actions Integration

Automated PR Checks

You can integrate Actions Up into your CI/CD pipeline to automatically check for outdated actions on every pull request. This helps maintain security and ensures your team stays aware of available updates.

Create .github/workflows/check-actions-updates.yml.

````yaml name: Check for outdated GitHub Actions on: pull_request: types: [edited, opened, synchronize, reopened]

jobs: check-actions: name: Check for GHA updates runs-on: ubuntu-latest permissions: contents: read pull-requests: write issues: write steps: - name: Checkout repository uses: actions/checkout@v4

  - name: Setup Node.js
    uses: actions/setup-node@v4
    with:
      node-version: '20'

  - name: Install actions-up
    run: npm install -g actions-up

  - name: Run actions-up check
    id: actions-check
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    run: |
      set -euo pipefail
      echo "## GitHub Actions Update Check" >> $GITHUB_STEP_SUMMARY
      echo "" >> $GITHUB_STEP_SUMMARY

      # Run actions-up and capture machine-readable output
      echo "Running actions-up to check for updates..."
      actions-up --json > actions-up-report.json

      UPDATE_COUNT=$(node -pe "JSON.parse(require('node:fs').readFileSync('actions-up-report.json', 'utf8')).summary.totalUpdates")

      # Create formatted output
      if [ "$UPDATE_COUNT" -gt 0 ]; then
        echo "Found $UPDATE_COUNT GitHub Actions with available updates" >> $GITHUB_STEP_SUMMARY
        echo "" >> $GITHUB_STEP_SUMMARY
        echo "

" >> $GITHUB_STEP_SUMMARY echo "

Click to see JSON report

" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo 'json' >> $GITHUB_STEP_SUMMARY cat actions-up-report.json >> $GITHUB_STEP_SUMMARY echo '' >> $GITHUB_STEP_SUMMARY echo "

" >> $GITHUB_STEP_SUMMARY

        # Create detailed markdown report with better formatting
        node --input-type=module <<'EOF'
        import { readFileSync, writeFileSync } from 'node:fs'

        let report = JSON.parse(readFileSync('actions-up-report.json', 'utf8'))
        let lines = [
          '## GitHub Actions Update Report',
          '',
          '### Summary',
          `- **Updates available:** ${report.summary.totalUpdates}`,
          '',
          '### Updates',
          '',
        ]

        for (let update of report.updates) {
          let file = update.action.file ?? 'unknown'
          let currentVersion = update.currentVersion ?? 'unknown'
          let latestVersion = update.latestVersion ?? 'unknown'
          lines.push(
            `- \`${update.action.name}\` in \`${file}\`: \`${currentVersion}\` → \`${latestVersion}\``,
          )
        }

        lines.push('')
        lines.push('Run `npx actions-up` locally to review and apply updates.')

        writeFileSync('actions-up-report.md', lines.join('\n'))
      EOF

        echo "has-updates=true" >> $GITHUB_OUTPUT
        echo "update-count=$UPDATE_COUNT" >> $GITHUB_OUTPUT
      else
        echo "All GitHub Actions are up to date!" >> $GITHUB_STEP_SUMMARY

        {
          echo "## GitHub Actions Update Report"
          echo ""
          echo "### All GitHub Actions in this repository are up to date!"
          echo ""
          echo "No action required. Your workflows are using the latest versions of all GitHub Actions."
        } > actions-up-report.md

        echo "has-updates=false" >> $GITHUB_OUTPUT
        echo "update-count=0" >> $GITHUB_OUTPUT
      fi

  - name: Comment PR with updates
    if:
      github.event_name == 'pull_request' &&
      github.event.pull_request.head.repo.full_name == github.repository
    uses: actions/github-script@v7
    with:
      script: |
        const fs = require('fs');
        const report = fs.readFileSync('actions-up-report.md', 'utf8');
        const hasUpdates = '${{ steps.actions-check.outputs.has-updates }}' === 'true';

        // Check if we already commented
        const comments = await github.paginate(github.rest.issues.listComments, {
          owner: context.repo.owner,
          repo: context.repo.repo,
          issue_number: context.issue.number
        });

        const botComment = comments.find(comment =>
          comment.user.type === 'Bot' &&
          comment.body.includes('GitHub Actions Update Report')
        );

        const commentBody = `${report}

        ---
        *Generated by [actions-up](https://github.com/azat-io/actions-up) | Last check: ${new Date().toISOString()}*`;

        // Only comment if there are updates or if we previously commented
        if (hasUpdates || botComment) {
          if (botComment) {
            // Update existing comment
            await github.rest.issues.updateComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              comment_id: botComment.id,
              body: commentBody
            });
            console.log('Updated existing comment');
          } else {
            // Create new comment only if there are updates
            await github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              body: commentBody
            });
            console.log('Created new comment');
          }
        } else {
          console.log('No updates found and no previous comment exists - skipping comment');
        }

        // Add or update PR labels based on status
        const labels = await github.rest.issues.listLabelsOnIssue({
          owner: context.repo.owner,
          repo: context.repo.repo,
          issue_number: context.issue.number
        });

        const hasOutdatedLabel = labels.data.some(label => label.name === 'outdated-actions');

        if (hasUpdates && !hasOutdatedLabel) {
          // Add label if updates are found
          try {
            await github.rest.issues.addLabels({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              labels: ['outdated-actions']
            });
            console.log('Added outdated-actions label');
          } catch (error) {
            console.log('Could not add label (might not exist in repo):', error.message);
          }
        } else if (!hasUpdates && hasOutdatedLabel) {
          // Remove label if no updates
          try {
            await github.rest.issues.removeLabel({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              name: 'outdated-actions'
            });
            console.log('Removed outdated-actions label');
          } catch (error) {

Extension points exported contracts — how you extend this code

MatchGroups (Interface)
* Regex capture groups for parsing `uses:` lines in YAML files.
core/ast/update/apply-updates.ts
PromptOptionsLike (Interface)
* Minimal prompt options shape we use to avoid Enquirer union pitfalls.
core/interactive/prompt-update-selection.ts
FetchResponseLike (Interface)
* Minimal subset of the Fetch API Response interface used by this module. * Implementations (node, polyfills, test doub
core/api/make-request.ts
ResolveScanDirectoriesOptions (Interface)
* Options for resolving scan directories from CLI flags.
cli/resolve-scan-directories.ts
WriteJsonReportOptions (Interface)
* Payload used by the local JSON writer helper in the CLI.
cli/index.ts
BuildJsonReportOptions (Interface)
* Options used to build a JSON report from the current CLI state.
cli/build-json-report.ts
AnchorDirectoryInputsOptions (Interface)
* Options for anchoring directory inputs at the repository root.
cli/anchor-directory-inputs.ts
ValidateCliOptionsInput (Interface)
* Minimal subset of CLI flags that require cross-option validation.
cli/validate-cli-options.ts

Core symbols most depended-on inside this repo

checkUpdates
called by 60
core/api/check-updates.ts
scanGitHubActions
called by 34
core/scan-github-actions.ts
promptUpdateSelection
called by 32
core/interactive/prompt-update-selection.ts
stripAnsi
called by 27
core/interactive/strip-ansi.ts
getTagInfo
called by 26
core/api/get-tag-info.ts
isYAMLMap
called by 24
core/ast/guards/is-yaml-map.ts
applyUpdates
called by 21
core/ast/update/apply-updates.ts
scanActionFile
called by 20
core/scan-action-file.ts

Shape

Function 133
Interface 58
Method 37
Class 6

Languages

TypeScript100%

Modules by API surface

core/interactive/prompt-update-selection.ts32 symbols
test/interactive/prompt-update-selection.test.ts15 symbols
core/api/check-updates.ts13 symbols
test/scan-github-actions.test.ts11 symbols
cli/build-json-report.ts10 symbols
types/github-client.d.ts9 symbols
test/scan-workflow-file.test.ts7 symbols
test/scan-recursive.test.ts7 symbols
test/scan-action-file.test.ts7 symbols
core/scan-github-actions.ts6 symbols
test/api/check-updates.test.ts5 symbols
core/ast/update/apply-updates.ts4 symbols

For agents

$ claude mcp add actions-up \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page