npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ci-cost-diff-action

v0.1.2

Published

GitHub Action and CLI that estimates GitHub Actions job cost and reports CI cost deltas in pull requests.

Readme

CI Cost Diff Action

Estimate the cost of a GitHub Actions workflow run, compare it with the nearest older successful run on the base branch, and publish a pull request report.

CI Cost Diff is designed for teams that want a lightweight budget guard directly inside code review. It uses rounded job minutes, GitHub-hosted runner list prices, and optional runner-rate overrides for larger or custom runners.

Features

  • Pull request summary with current cost, baseline cost, and delta.
  • Largest per-job cost delta table to show the biggest changes.
  • Budget gates for total cost, absolute increase, and percentage increase.
  • No runtime npm dependencies.
  • Works as both a GitHub Action and local CLI.
  • Supports custom runner rates through JSON overrides.

Quick Start

Add a reporting job after the CI jobs you want to measure:

name: CI

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - run: npm ci
      - run: npm test

  cost:
    if: always()
    needs: [test]
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
      issues: write
    steps:
      - uses: HupBaHa/cost-diff@<release-tag>
        with:
          current-job-id: ${{ job.check_run_id }}
          fail-on-increase-percent: 25
          fail-on-increase-usd: 1.00

The action compares the current workflow run with the nearest older successful run of the same workflow on the pull request base branch.

For pull requests from forks, GitHub gives the default GITHUB_TOKEN read-only permissions on pull_request workflows. In that case the action skips the PR comment and still writes the report to the step summary. If you intentionally pass a write-capable PAT or GitHub App token, set allow-fork-pr-comments: true to opt in. This guard applies to pull_request events only; pull_request_target uses the base repository token permissions, so use it only when you intentionally want write-token behavior and avoid checking out or running untrusted fork code before this action.

Update-mode PR comments are scoped by workflow id and reporting job, so separate workflows or reporting jobs keep separate comments on the same pull request. The marker includes a short hash of the raw scope to avoid collisions after normalization.

When current-job-id is supplied, update-mode comments use the matched GitHub job name in the scope. This keeps matrix copies such as cost (ubuntu) and cost (macos) from overwriting each other.

If concurrent update-mode runs create same-scope comments, the action keeps the newest matching comment from the authenticated token user and deletes older duplicates during update and post-create cleanup passes. If the token identity cannot be resolved, the action will not update marker comments from another bot; when an existing GitHub App marker comment is present, it also avoids creating another same-scope comment.

Report fields are truncated defensively, and the final report is capped, so unusually long job names, labels, runner names, branches, or links do not make pull request comments exceed GitHub limits.

Inputs

| Input | Default | Description | | --- | --- | --- | | github-token | ${{ github.token }} | Token for reading workflow runs/jobs and writing PR comments. | | run-id | current run | Workflow run id to analyze. | | workflow-id | current workflow | Workflow id or workflow file name for baseline lookup. | | baseline-branch | PR base branch, repository default branch, or current run branch | Branch used for baseline run lookup. | | baseline-event | empty | Optional event filter such as push. | | lookback-runs | 20 | Positive number of successful baseline runs to inspect, capped at GitHub's documented 1,000-result limit for filtered workflow-run searches. | | job-filter | all | GitHub jobs API filter. all includes old rerun attempts; latest uses only the latest attempt. Job pages are fetched until GitHub returns the final page. | | exclude-jobs | empty | Comma or newline separated job-name patterns. Supports *. | | current-job-id | empty | Check run id of the reporting job, used to exclude the action's own job from current and baseline runs. Pass ${{ job.check_run_id }} from the workflow when available; it is matched through the jobs API check_run_url. | | include-current-job | false | Include the reporting job when it is already complete. In-progress jobs are skipped and their baseline counterpart is excluded to keep comparisons symmetric. | | runner-rates | {} | JSON object mapping runner labels/names/group names/SKUs to USD per minute. | | fail-on-unknown-runner | false | Fail if a current job has an unknown runner rate. | | fail-on-ambiguous-runner-rate | false | Fail if a current job uses an ambiguous standard-rate fallback. | | fail-on-increase-percent | empty | Fail if cost increase exceeds this non-negative percentage. | | fail-on-increase-usd | empty | Fail if cost increase exceeds this non-negative USD value. | | fail-on-total-usd | empty | Fail if current total cost exceeds this non-negative USD value. | | comment-mode | update | off, update, or always. | | allow-fork-pr-comments | false | Allow PR comments on fork pull_request events when github-token is a write-capable custom token; does not change pull_request_target behavior. |

Outputs

| Output | Description | | --- | --- | | current-cost | Estimated current run cost in USD. | | baseline-cost | Estimated baseline run cost in USD. | | delta-cost | Current cost minus baseline cost in USD. | | delta-percent | Current cost delta as percentage of baseline cost, or an empty string when baseline cost is zero. | | current-minutes | Rounded billable minutes in the current run. | | baseline-minutes | Rounded billable minutes in the baseline run. | | conclusion | pass or fail. |

Custom Runner Rates

Known GitHub billing SKU labels, official standard workflow labels such as ubuntu-slim, macos-26, and windows-2025-vs2026, and official larger-runner workflow labels such as ubuntu-24.04-16core, windows-2022-16core, macos-15-large, and macos-26-xlarge are priced by default. GitHub larger runners often use custom labels, so add runner-rates when your larger runners or private runner names do not expose a known SKU:

- uses: HupBaHa/cost-diff@<release-tag>
  with:
    runner-rates: |
      {
        "ubuntu-8-core": 0.022,
        "windows-16-core": 0.082,
        "gpu-linux": 0.052
      }

Replace <release-tag> with a published tag, for example v1 after the v1 release is created.

Keys are matched against runner labels, runner names, runner group names, and known GitHub billing SKUs after normalization. Exact runner labels, names, groups, and known billing SKUs take precedence over class keys. Class keys such as linux, windows, and macos apply only to jobs inferred to that standard runner SKU; they do not price self-hosted jobs, generic OS-plus-architecture labels, or exact larger-runner labels.

Generic labels such as bare linux, windows, or macos, linux plus arm, linux plus x64, linux plus gpu, windows plus gpu, or combined labels such as linux-gpu, gpu-linux, or ubuntu-gpu are not priced as GitHub-hosted runners by default because self-hosted runners can use custom labels and can omit GitHub's default self-hosted label. Combined non-official ARM aliases such as linux-arm, ubuntu-arm, windows-arm, or actions-prefixed hosted-looking aliases such as actions-ubuntu-arm are also treated as unknown. Non-official Ubuntu-looking labels without a custom/larger-runner signal, such as ubuntu-cache, are treated as unknown rather than silently using a standard Linux hosted rate. Split standard labels plus architecture or GPU labels, such as ubuntu-latest plus arm64 or ubuntu-latest plus gpu, are also treated as unknown; use an official hosted ARM label such as ubuntu-24.04-arm or ubuntu-22.04-arm, a known GPU billing SKU such as linux-4-core-gpu, or provide a runner-rates override.

Unknown and ambiguous runner-rate warnings include both current and baseline jobs so underpriced baselines are visible in the report.

If labels look custom or larger-runner-like but map to a standard GitHub-hosted default or broad class override, the report shows an "Ambiguous Runner Rates" warning. For example, generic split labels such as linux plus 8-core are treated as ambiguous unless you provide a specific override or use a known larger-runner label. Use fail-on-ambiguous-runner-rate: true when you want those cases to block the PR.

Note: job.check_run_id is not available on GitHub Enterprise Server. In GHES workflows, or when you do not pass current-job-id, use exclude-jobs when the reporting job has a custom display name.

Local CLI

You can compare saved GitHub jobs JSON locally from the repository checkout:

node ./bin/ci-cost-diff.js \
  --current examples/current-jobs.json \
  --baseline examples/baseline-jobs.json \
  --exclude "docs*,lint" \
  --fail-on-increase-percent 25

The CLI also supports --fail-on-unknown-runner and --fail-on-ambiguous-runner-rate for parity with the action's runner-rate gates.

After installing the package, run the CLI as ci-cost-diff:

ci-cost-diff \
  --current current-jobs.json \
  --baseline baseline-jobs.json \
  --fail-on-unknown-runner=false

You can also run it without installing globally:

npx ci-cost-diff-action \
  --current current-jobs.json \
  --baseline baseline-jobs.json

Boolean flags accept either the bare flag form, such as --fail-on-unknown-runner, or an explicit inline value, such as --fail-on-unknown-runner=false. Empty inline values such as --fail-on-unknown-runner= are rejected.

Use --rates runner-rates.json to apply the same JSON object format as the action's runner-rates input. The file must contain a JSON object; empty files are rejected so a truncated rates file does not silently disable custom pricing.

Each JSON file can be either:

{ "jobs": [] }

or a raw jobs array:

[]

Cost Model

The action estimates compute cost only. It does not query account billing, included minutes, private contracts, cache/storage billing, or public-repository free usage. By default it asks the GitHub jobs API for filter=all, so rerun attempts are included in the estimate. Percentage gates fail on any nonzero current cost when the baseline cost is zero. See docs/RATE_MODEL.md for details.

Development

This project intentionally has no runtime dependencies.

Use Node 20.9.0 or newer for local verification. The runtime source remains compatible with Node >=20, but the locked ESLint dev tooling requires Node 20.9.0+ on the Node 20 line.

npm ci
npm run lint
npm run verify

Optional local pre-commit hook:

git config core.hooksPath .githooks

License

MIT