ci-cost-diff-action 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +92 -0
- package/LICENSE +21 -0
- package/README.md +200 -0
- package/SECURITY.md +25 -0
- package/action.yml +100 -0
- package/bin/ci-cost-diff.js +297 -0
- package/docs/ARCHITECTURE.md +81 -0
- package/docs/RATE_MODEL.md +103 -0
- package/examples/baseline-jobs.json +22 -0
- package/examples/current-jobs.json +22 -0
- package/package.json +54 -0
- package/src/action.js +533 -0
- package/src/comments.js +78 -0
- package/src/cost.js +603 -0
- package/src/github.js +670 -0
- package/src/inputs.js +187 -0
- package/src/jobs.js +40 -0
- package/src/rates.js +841 -0
- package/src/report.js +258 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
- Initial implementation.
|
|
6
|
+
- GitHub Action for current-vs-baseline workflow cost diff.
|
|
7
|
+
- Dependency-free local CLI.
|
|
8
|
+
- Markdown summary and optional PR comments.
|
|
9
|
+
- Budget thresholds for total cost and cost increases.
|
|
10
|
+
- Default GitHub-hosted runner rate table with custom overrides.
|
|
11
|
+
- Official larger-runner workflow label inference for labels such as `ubuntu-24.04-16core`.
|
|
12
|
+
- Exact larger-runner and self-hosted runner precedence over broad class overrides.
|
|
13
|
+
- At-least-one-minute rounding for valid completed jobs with second-resolution timestamps.
|
|
14
|
+
- Skips malformed or negative job intervals instead of counting them as zero-cost jobs.
|
|
15
|
+
- Non-negative budget threshold validation with floating-point-safe comparisons.
|
|
16
|
+
- Rerun-aware job fetching with `filter=all`.
|
|
17
|
+
- Scoped update-mode PR comments per workflow/job.
|
|
18
|
+
- Collision-resistant scoped PR comment markers with raw-scope hash suffixes.
|
|
19
|
+
- Post-create duplicate cleanup for concurrent update-mode PR comments.
|
|
20
|
+
- Baseline, job, and issue-comment pagination continue past old fixed page caps.
|
|
21
|
+
- Malformed successful GitHub API list responses now fail closed, with a high pagination safety guard for malformed endless full pages.
|
|
22
|
+
- Generic OS-plus-architecture labels, including `linux` plus `arm`, are treated as unknown to avoid charging self-hosted runners created without default labels.
|
|
23
|
+
- Broad class rate overrides retain ambiguous-runner warnings for larger/custom-looking labels.
|
|
24
|
+
- Non-finite analyzed costs are rejected instead of bypassing USD thresholds.
|
|
25
|
+
- Update-mode PR comments require authenticated token ownership before updating or deleting existing marker comments.
|
|
26
|
+
- Baseline pagination now uses the same high-page safety guard as jobs and comments.
|
|
27
|
+
- Workflow job pagination no longer trusts `total_count` as an early stop signal; it waits for GitHub's final page.
|
|
28
|
+
- Broad runner names/groups such as `linux` no longer receive GitHub-hosted defaults without explicit overrides.
|
|
29
|
+
- Report fields are truncated defensively to keep comments and step summaries bounded.
|
|
30
|
+
- Verification now checks package dry-run contents and the exact action metadata entrypoint.
|
|
31
|
+
- Matrix-aware comment scoping when `current-job-id` identifies the reporting job.
|
|
32
|
+
- Symmetric reporting-job exclusion for in-progress `include-current-job` and `run-id` override cases.
|
|
33
|
+
- Markdown table escaping for PR-controlled job and runner fields.
|
|
34
|
+
- Best-effort PR comments for fork pull requests, with custom-token opt-in.
|
|
35
|
+
- GitHub Enterprise Server API URL support through `GITHUB_API_URL`.
|
|
36
|
+
- Ambiguous runner-rate warnings for custom-looking labels.
|
|
37
|
+
- Generic OS-plus-GPU labels are treated as unknown unless a known GPU SKU label or explicit override is provided.
|
|
38
|
+
- Percentage budget gates fail closed for non-finite derived percentage values and reject non-finite threshold inputs.
|
|
39
|
+
- Baseline lookup respects GitHub's 1,000-result cap for filtered workflow-run searches.
|
|
40
|
+
- Retry handling supports HTTP-date `Retry-After` headers and honors server-provided retry waits above the fallback retry cap.
|
|
41
|
+
- GitHub API path parameters are encoded or numerically validated before request paths are built.
|
|
42
|
+
- GitHub API list responses now reject malformed array items, not just missing arrays.
|
|
43
|
+
- Current-run baseline self-exclusion can fall back to `GITHUB_JOB` when the current jobs API has not exposed the reporting job yet.
|
|
44
|
+
- CLI boolean flags accept explicit inline `true` or `false` values.
|
|
45
|
+
- Action metadata verification validates `runs.using`, `runs.main`, and input/output names.
|
|
46
|
+
- Current official standard labels such as `windows-2025-vs2026`, `macos-15-intel`, `macos-26-intel`, `macos-26`, and `ubuntu-22.04-arm` are recognized.
|
|
47
|
+
- Split standard labels plus architecture labels such as `ubuntu-latest` plus `arm64` are treated as unknown instead of silently using x64 standard pricing.
|
|
48
|
+
- Negated modifier labels such as `not-gpu` no longer trigger ambiguous-runner warnings.
|
|
49
|
+
- Verification now runs the `npm test` script and package checks reject unexpected tarball files.
|
|
50
|
+
- `SECURITY.md` is included in the npm package.
|
|
51
|
+
- Broad class overrides no longer price generic OS-plus-architecture/GPU labels such as `linux` plus `arm` or `linux` plus `gpu`.
|
|
52
|
+
- Bare generic OS labels and combined non-official ARM aliases such as `linux-arm`, `ubuntu-arm`, or `windows-arm` are treated as unknown unless an exact override or known billing SKU is supplied.
|
|
53
|
+
- Combined OS/GPU labels such as `linux-gpu`, `gpu-linux`, and `ubuntu-gpu`, and non-official Ubuntu-looking labels such as `ubuntu-cache`, are treated as unknown unless an exact override or known billing SKU is supplied.
|
|
54
|
+
- macOS larger-runner default inference is limited to official workflow labels such as `macos-15-large` and `macos-26-xlarge`.
|
|
55
|
+
- Update-mode PR comments create a replacement when a listed marker comment disappears before update.
|
|
56
|
+
- `include-current-job` self-exclusion can fall back to `GITHUB_JOB` when the current jobs API has not exposed the reporting job yet.
|
|
57
|
+
- Release and security docs now include versioning, publish, and private-disclosure fallback guidance.
|
|
58
|
+
- CLI value parsing and package dry-run validation handle malformed edge cases more defensively.
|
|
59
|
+
- CLI inline options with empty values now fail instead of silently disabling budget or runner-rate gates.
|
|
60
|
+
- Empty job names no longer bypass invalid-interval or explicit-name exclusions.
|
|
61
|
+
- CLI jobs-file validation now reports top-level `null` cleanly and help output lists `--help`.
|
|
62
|
+
- Exact standard billing SKU overrides such as `actions-linux` now beat broad class overrides.
|
|
63
|
+
- Current workflow-run API responses now fail closed when required run identifiers are missing.
|
|
64
|
+
- Update-mode PR comments ignore malformed marker comments without numeric IDs and delete stale duplicates even while the newly created comment is not yet visible.
|
|
65
|
+
- CLI whitespace-only numeric threshold values now fail instead of becoming zero.
|
|
66
|
+
- Verification now asserts the published `ci-cost-diff` command maps to `bin/ci-cost-diff.js`.
|
|
67
|
+
- Exact Linux slim SKU/alias labels such as `actions-linux-slim` and `linux-slim` now price as `actions_linux_slim` without ambiguous-runner warnings.
|
|
68
|
+
- Reporting-job self-exclusion now removes previous same-name attempts from current-run analysis when `current-job-id` is provided and `job-filter: all` includes reruns.
|
|
69
|
+
- Baseline workflow-run list items missing required IDs now fail closed before baseline job fetching.
|
|
70
|
+
- GitHub API server retry delays are capped to avoid very long action sleeps.
|
|
71
|
+
- CLI rates files must be non-empty JSON objects.
|
|
72
|
+
- Verification now checks package-lock root metadata, exact package bin commands, CLI shebangs, and release workflow trusted-publishing guardrails.
|
|
73
|
+
- Added a trusted npm publishing release workflow using current major GitHub action tags (`actions/checkout@v6`, `actions/setup-node@v6`) and `npm publish --provenance`.
|
|
74
|
+
- Split standard labels plus GPU labels such as `ubuntu-latest` plus `gpu` are treated as unknown unless an exact GPU billing SKU or override is provided.
|
|
75
|
+
- Official workflow labels are recognized when they appear as exact runner names or runner group names.
|
|
76
|
+
- Secondary GitHub API rate-limit `403` responses are retried when the response body identifies a secondary limit.
|
|
77
|
+
- Workflow job API items missing required IDs now fail closed.
|
|
78
|
+
- Release publishing now validates that the release tag matches `package.json` before `npm publish --provenance`.
|
|
79
|
+
- Fabricated actions-prefixed hosted-looking labels such as `actions-ubuntu-arm` are treated as unknown unless they are exact known billing SKU labels.
|
|
80
|
+
- Update-mode PR comments now avoid creating another same-scope GitHub Actions comment when token identity cannot be resolved.
|
|
81
|
+
- Manual release dispatch now checks out `refs/tags/<tag>` and requires the exact `v<package.version>` tag.
|
|
82
|
+
- Documentation now clarifies older-baseline selection, bounded server retry waits, and empty `delta-percent` output for zero-cost baselines.
|
|
83
|
+
- Current workflow-run API responses now fail closed when they lack usable baseline-ordering metadata.
|
|
84
|
+
- Update-mode PR comments now avoid creating another same-scope GitHub App comment when token identity cannot be resolved.
|
|
85
|
+
- Action metadata now documents runner group overrides and current-run-only runner-rate gates more precisely.
|
|
86
|
+
- SKU overrides now apply when official workflow labels are exposed through runner names or runner groups.
|
|
87
|
+
- Baseline workflow-run list items now fail closed when required conclusion or ordering metadata is missing.
|
|
88
|
+
- Trusted publishing now validates the tag before dependency installation, disables release dependency caching, and uses `--ignore-scripts` for npm install, verify, and publish commands.
|
|
89
|
+
- CLI value options now reject following `--...` tokens as missing values instead of treating unknown options as filenames.
|
|
90
|
+
- Arbitrary `actions-linux-*`, `actions-windows-*`, or `actions-macos-*` custom labels no longer collide with non-`actions-*` custom override keys.
|
|
91
|
+
- Ambiguous runner-rate warnings now distinguish broad class overrides from default-rate fallbacks.
|
|
92
|
+
- Reports now have a final length cap in addition to field and row limits.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CI Cost Diff contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# CI Cost Diff Action
|
|
2
|
+
|
|
3
|
+
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.
|
|
4
|
+
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Pull request summary with current cost, baseline cost, and delta.
|
|
10
|
+
- Largest per-job cost delta table to show the biggest changes.
|
|
11
|
+
- Budget gates for total cost, absolute increase, and percentage increase.
|
|
12
|
+
- No runtime npm dependencies.
|
|
13
|
+
- Works as both a GitHub Action and local CLI.
|
|
14
|
+
- Supports custom runner rates through JSON overrides.
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
Add a reporting job after the CI jobs you want to measure:
|
|
19
|
+
|
|
20
|
+
```yaml
|
|
21
|
+
name: CI
|
|
22
|
+
|
|
23
|
+
on:
|
|
24
|
+
pull_request:
|
|
25
|
+
push:
|
|
26
|
+
branches: [main]
|
|
27
|
+
|
|
28
|
+
permissions:
|
|
29
|
+
contents: read
|
|
30
|
+
|
|
31
|
+
jobs:
|
|
32
|
+
test:
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@v6
|
|
36
|
+
- run: npm ci
|
|
37
|
+
- run: npm test
|
|
38
|
+
|
|
39
|
+
cost:
|
|
40
|
+
if: always()
|
|
41
|
+
needs: [test]
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
permissions:
|
|
44
|
+
actions: read
|
|
45
|
+
contents: read
|
|
46
|
+
issues: write
|
|
47
|
+
steps:
|
|
48
|
+
- uses: HupBaHa/cost-diff@<release-tag>
|
|
49
|
+
with:
|
|
50
|
+
current-job-id: ${{ job.check_run_id }}
|
|
51
|
+
fail-on-increase-percent: 25
|
|
52
|
+
fail-on-increase-usd: 1.00
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The action compares the current workflow run with the nearest older successful run of the same workflow on the pull request base branch.
|
|
56
|
+
|
|
57
|
+
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.
|
|
58
|
+
|
|
59
|
+
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.
|
|
60
|
+
|
|
61
|
+
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.
|
|
62
|
+
|
|
63
|
+
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.
|
|
64
|
+
|
|
65
|
+
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.
|
|
66
|
+
|
|
67
|
+
## Inputs
|
|
68
|
+
|
|
69
|
+
| Input | Default | Description |
|
|
70
|
+
| --- | --- | --- |
|
|
71
|
+
| `github-token` | `${{ github.token }}` | Token for reading workflow runs/jobs and writing PR comments. |
|
|
72
|
+
| `run-id` | current run | Workflow run id to analyze. |
|
|
73
|
+
| `workflow-id` | current workflow | Workflow id or workflow file name for baseline lookup. |
|
|
74
|
+
| `baseline-branch` | PR base branch, repository default branch, or current run branch | Branch used for baseline run lookup. |
|
|
75
|
+
| `baseline-event` | empty | Optional event filter such as `push`. |
|
|
76
|
+
| `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. |
|
|
77
|
+
| `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. |
|
|
78
|
+
| `exclude-jobs` | empty | Comma or newline separated job-name patterns. Supports `*`. |
|
|
79
|
+
| `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`. |
|
|
80
|
+
| `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. |
|
|
81
|
+
| `runner-rates` | `{}` | JSON object mapping runner labels/names/group names/SKUs to USD per minute. |
|
|
82
|
+
| `fail-on-unknown-runner` | `false` | Fail if a current job has an unknown runner rate. |
|
|
83
|
+
| `fail-on-ambiguous-runner-rate` | `false` | Fail if a current job uses an ambiguous standard-rate fallback. |
|
|
84
|
+
| `fail-on-increase-percent` | empty | Fail if cost increase exceeds this non-negative percentage. |
|
|
85
|
+
| `fail-on-increase-usd` | empty | Fail if cost increase exceeds this non-negative USD value. |
|
|
86
|
+
| `fail-on-total-usd` | empty | Fail if current total cost exceeds this non-negative USD value. |
|
|
87
|
+
| `comment-mode` | `update` | `off`, `update`, or `always`. |
|
|
88
|
+
| `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. |
|
|
89
|
+
|
|
90
|
+
## Outputs
|
|
91
|
+
|
|
92
|
+
| Output | Description |
|
|
93
|
+
| --- | --- |
|
|
94
|
+
| `current-cost` | Estimated current run cost in USD. |
|
|
95
|
+
| `baseline-cost` | Estimated baseline run cost in USD. |
|
|
96
|
+
| `delta-cost` | Current cost minus baseline cost in USD. |
|
|
97
|
+
| `delta-percent` | Current cost delta as percentage of baseline cost, or an empty string when baseline cost is zero. |
|
|
98
|
+
| `current-minutes` | Rounded billable minutes in the current run. |
|
|
99
|
+
| `baseline-minutes` | Rounded billable minutes in the baseline run. |
|
|
100
|
+
| `conclusion` | `pass` or `fail`. |
|
|
101
|
+
|
|
102
|
+
## Custom Runner Rates
|
|
103
|
+
|
|
104
|
+
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:
|
|
105
|
+
|
|
106
|
+
```yaml
|
|
107
|
+
- uses: HupBaHa/cost-diff@<release-tag>
|
|
108
|
+
with:
|
|
109
|
+
runner-rates: |
|
|
110
|
+
{
|
|
111
|
+
"ubuntu-8-core": 0.022,
|
|
112
|
+
"windows-16-core": 0.082,
|
|
113
|
+
"gpu-linux": 0.052
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Replace `<release-tag>` with a published tag, for example `v1` after the v1 release is created.
|
|
118
|
+
|
|
119
|
+
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.
|
|
120
|
+
|
|
121
|
+
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.
|
|
122
|
+
|
|
123
|
+
Unknown and ambiguous runner-rate warnings include both current and baseline jobs so underpriced baselines are visible in the report.
|
|
124
|
+
|
|
125
|
+
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.
|
|
126
|
+
|
|
127
|
+
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.
|
|
128
|
+
|
|
129
|
+
## Local CLI
|
|
130
|
+
|
|
131
|
+
You can compare saved GitHub jobs JSON locally from the repository checkout:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
node ./bin/ci-cost-diff.js \
|
|
135
|
+
--current examples/current-jobs.json \
|
|
136
|
+
--baseline examples/baseline-jobs.json \
|
|
137
|
+
--exclude "docs*,lint" \
|
|
138
|
+
--fail-on-increase-percent 25
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The CLI also supports `--fail-on-unknown-runner` and `--fail-on-ambiguous-runner-rate` for parity with the action's runner-rate gates.
|
|
142
|
+
|
|
143
|
+
After installing the package, run the CLI as `ci-cost-diff`:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
ci-cost-diff \
|
|
147
|
+
--current current-jobs.json \
|
|
148
|
+
--baseline baseline-jobs.json \
|
|
149
|
+
--fail-on-unknown-runner=false
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
You can also run it without installing globally:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
npx ci-cost-diff-action \
|
|
156
|
+
--current current-jobs.json \
|
|
157
|
+
--baseline baseline-jobs.json
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
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.
|
|
161
|
+
|
|
162
|
+
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.
|
|
163
|
+
|
|
164
|
+
Each JSON file can be either:
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{ "jobs": [] }
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
or a raw jobs array:
|
|
171
|
+
|
|
172
|
+
```json
|
|
173
|
+
[]
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Cost Model
|
|
177
|
+
|
|
178
|
+
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](docs/RATE_MODEL.md) for details.
|
|
179
|
+
|
|
180
|
+
## Development
|
|
181
|
+
|
|
182
|
+
This project intentionally has no runtime dependencies.
|
|
183
|
+
|
|
184
|
+
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.
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
npm ci
|
|
188
|
+
npm run lint
|
|
189
|
+
npm run verify
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Optional local pre-commit hook:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
git config core.hooksPath .githooks
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
MIT
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Reporting a Vulnerability
|
|
4
|
+
|
|
5
|
+
Please report security issues privately by opening a GitHub security advisory in the repository.
|
|
6
|
+
|
|
7
|
+
If private vulnerability reporting is unavailable to you, contact the maintainer through the private contact listed on the maintainer's GitHub or npm profile. If no private contact is available, open a public issue asking for a private disclosure channel, but do not include vulnerability details in the public issue.
|
|
8
|
+
|
|
9
|
+
Do not disclose vulnerabilities publicly until a fix is available.
|
|
10
|
+
|
|
11
|
+
## Scope
|
|
12
|
+
|
|
13
|
+
Security-sensitive areas include:
|
|
14
|
+
|
|
15
|
+
- GitHub token handling.
|
|
16
|
+
- Pull request comment writing.
|
|
17
|
+
- Runner-rate parsing.
|
|
18
|
+
- Markdown generation from job names and runner labels.
|
|
19
|
+
- GitHub API request handling.
|
|
20
|
+
|
|
21
|
+
Pull request comments are best-effort because fork pull request workflows often receive read-only tokens. The step summary remains the source of truth when commenting is not allowed.
|
|
22
|
+
|
|
23
|
+
## Runtime Dependencies
|
|
24
|
+
|
|
25
|
+
The action has no runtime npm dependencies by design.
|
package/action.yml
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
name: CI Cost Diff
|
|
2
|
+
description: Estimate GitHub Actions cost for a workflow run and report deltas against the base branch.
|
|
3
|
+
author: CI Cost Diff contributors
|
|
4
|
+
branding:
|
|
5
|
+
icon: dollar-sign
|
|
6
|
+
color: green
|
|
7
|
+
|
|
8
|
+
inputs:
|
|
9
|
+
github-token:
|
|
10
|
+
description: GitHub token used to read workflow runs/jobs and write pull request comments.
|
|
11
|
+
required: false
|
|
12
|
+
default: ${{ github.token }}
|
|
13
|
+
run-id:
|
|
14
|
+
description: Workflow run id to analyze. Defaults to the current run.
|
|
15
|
+
required: false
|
|
16
|
+
default: ""
|
|
17
|
+
workflow-id:
|
|
18
|
+
description: Workflow id or workflow file name to use for baseline lookup. Defaults to the current run workflow id.
|
|
19
|
+
required: false
|
|
20
|
+
default: ""
|
|
21
|
+
baseline-branch:
|
|
22
|
+
description: Branch used for baseline run lookup. Defaults to PR base branch, repository default branch, then current run branch.
|
|
23
|
+
required: false
|
|
24
|
+
default: ""
|
|
25
|
+
baseline-event:
|
|
26
|
+
description: Optional workflow event filter for baseline lookup, for example push. Leave empty to allow any event.
|
|
27
|
+
required: false
|
|
28
|
+
default: ""
|
|
29
|
+
lookback-runs:
|
|
30
|
+
description: Positive number of successful baseline workflow runs to inspect, capped at GitHub's 1,000 filtered-run search limit.
|
|
31
|
+
required: false
|
|
32
|
+
default: "20"
|
|
33
|
+
job-filter:
|
|
34
|
+
description: "GitHub jobs API filter: all includes old rerun attempts, latest uses only the latest attempt."
|
|
35
|
+
required: false
|
|
36
|
+
default: "all"
|
|
37
|
+
exclude-jobs:
|
|
38
|
+
description: Comma or newline separated job-name patterns to exclude. Supports * wildcards.
|
|
39
|
+
required: false
|
|
40
|
+
default: ""
|
|
41
|
+
current-job-id:
|
|
42
|
+
description: Current job check run id. Pass ${{ job.check_run_id }} from the workflow when available.
|
|
43
|
+
required: false
|
|
44
|
+
default: ""
|
|
45
|
+
include-current-job:
|
|
46
|
+
description: Include the reporting job when it is already complete. In-progress jobs are skipped and their baseline counterpart is excluded.
|
|
47
|
+
required: false
|
|
48
|
+
default: "false"
|
|
49
|
+
runner-rates:
|
|
50
|
+
description: JSON object mapping runner labels, runner names, runner group names, or billing SKUs to USD per minute.
|
|
51
|
+
required: false
|
|
52
|
+
default: "{}"
|
|
53
|
+
fail-on-unknown-runner:
|
|
54
|
+
description: Fail when a current job has an unknown runner rate.
|
|
55
|
+
required: false
|
|
56
|
+
default: "false"
|
|
57
|
+
fail-on-ambiguous-runner-rate:
|
|
58
|
+
description: Fail when a current job uses an ambiguous standard-rate fallback.
|
|
59
|
+
required: false
|
|
60
|
+
default: "false"
|
|
61
|
+
fail-on-increase-percent:
|
|
62
|
+
description: Fail if total estimated cost increases by more than this non-negative percentage.
|
|
63
|
+
required: false
|
|
64
|
+
default: ""
|
|
65
|
+
fail-on-increase-usd:
|
|
66
|
+
description: Fail if total estimated cost increases by more than this non-negative USD amount.
|
|
67
|
+
required: false
|
|
68
|
+
default: ""
|
|
69
|
+
fail-on-total-usd:
|
|
70
|
+
description: Fail if current total estimated cost is above this non-negative USD amount.
|
|
71
|
+
required: false
|
|
72
|
+
default: ""
|
|
73
|
+
comment-mode:
|
|
74
|
+
description: "Pull request comment mode: off, update, or always."
|
|
75
|
+
required: false
|
|
76
|
+
default: "update"
|
|
77
|
+
allow-fork-pr-comments:
|
|
78
|
+
description: Allow PR comments on fork pull_request events when github-token is a write-capable custom token. Does not change pull_request_target behavior.
|
|
79
|
+
required: false
|
|
80
|
+
default: "false"
|
|
81
|
+
|
|
82
|
+
outputs:
|
|
83
|
+
current-cost:
|
|
84
|
+
description: Estimated current run cost in USD.
|
|
85
|
+
baseline-cost:
|
|
86
|
+
description: Estimated baseline run cost in USD.
|
|
87
|
+
delta-cost:
|
|
88
|
+
description: Current cost minus baseline cost in USD.
|
|
89
|
+
delta-percent:
|
|
90
|
+
description: Current cost delta as a percentage of baseline cost, or empty when baseline cost is zero.
|
|
91
|
+
current-minutes:
|
|
92
|
+
description: Rounded billable minutes in the current run.
|
|
93
|
+
baseline-minutes:
|
|
94
|
+
description: Rounded billable minutes in the baseline run.
|
|
95
|
+
conclusion:
|
|
96
|
+
description: pass or fail.
|
|
97
|
+
|
|
98
|
+
runs:
|
|
99
|
+
using: node24
|
|
100
|
+
main: src/action.js
|