Skip to content

ci(pricing): land pricing updates on main instead of opening PRs - #1509

Merged
ryoppippi merged 3 commits into
mainfrom
ci/pricing-immediate-updates
Jul 28, 2026
Merged

ryoppippi merged 3 commits into
mainfrom
ci/pricing-immediate-updates

Conversation

@ryoppippi

@ryoppippi ryoppippi commented Jul 28, 2026 •

Copy link
Copy Markdown
Member

Summary

update-pricing.yaml already runs hourly and already accepts workflow_dispatch, but it opened a pull request with github.token. Such a PR cannot trigger ci.yaml, so it arrived with no checks and nothing merged it — pricing data was only as fresh as the last time someone merged that PR by hand (#1355 for the flake lock sat open for a month for the same reason). Both jobs now validate in place and push straight to main, so a dispatch lands the update immediately.

What Changed

  • .github/workflows/update-pricing.yaml
    • each job runs the checks a PR would have run — nix flake check, nix build .#ccusage-tests, just test-node — in a parallel: step, then pushes to main.
    • the existing "skip lock-only update" logic now feeds a step output, so the push step is skipped instead of every later step re-testing git diff.
    • added a concurrency group: a manual dispatch would otherwise race the hourly run's push.
    • dropped pull-requests: write; the jobs only need contents: write now.
  • .github/actions/commit-and-push-main/action.yaml: new composite action shared by both jobs. It commits the given paths as github-actions[bot], rebases onto whatever main has become during validation, and fails loudly (so the next run retries) if that rebase conflicts.
  • .github/scripts/update-litellm-lock.nu and .github/scripts/update-models-dev-lock.nu: the two update steps moved out of shell into Nushell. The lock file is read with open, the LiteLLM pricing document is fetched with http get and compared as a record, so the jq/curl/cmp dance and its /tmp JSON files are gone. models.dev decides through a match over {snapshots, lock}, which names the three states the step can end in — snapshots moved, only the lock moved, nothing moved. Both carry the repository's Nix shebang, so no nushell setup step is needed and they stay runnable by hand.

Why the checks run inline

A push made with github.token cannot trigger ci.yaml either, so main would otherwise receive the pricing bump with no validation at all. Running the PR's checks inside the workflow keeps the same gate without needing a PR that nobody merges.

just gen-models-dev-pricing still runs before validation, so the committed models-dev-pricing.json and codex-auto-review-fallbacks.json snapshots never lag the locked models-dev revision.

Testing

  • Both Nushell scripts were run end to end against this checkout: LiteLLM bumped 34561482 to f2cda740, saw the pricing JSON change and reported changed=true; models.dev regenerated models-dev-pricing.json and reported changed=true. The lock and snapshot were restored afterwards.
  • nu-check passes on both scripts.
  • just fmt passes, which runs actionlint over the workflow, zizmor over both the workflow and the new composite action, and nufmt over the scripts.
  • actionlint with the repo's ignore set reports nothing for the workflow; the only diagnostic anywhere is the parallel: step syntax already ignored repo-wide for ci.yaml.

Note

The parallel: legs each start with git diff --quiet && exit 0 so they no-op when the snapshot was already current. if: is used for the ordinary steps, but its behaviour on a parallel: step is not something this repo exercises yet, so the guard stays inside the legs.

Summary by CodeRabbit

  • New Features
    • Pricing updates for LiteLLM and models.dev are now applied automatically to the main branch when relevant pricing changes are detected.
    • Workflows were restructured to use a single change-aware, matrix-driven update flow.
  • Bug Fixes
    • Reduced unnecessary lockfile/pricing commits by detecting whether the underlying pricing content actually changed.
    • Prevented lock-only churn by reverting lock changes when snapshot outputs remained the same.
    • Improved reliability by serializing runs and handling rebase conflicts to allow safe retries.

The hourly pricing refresh opened a PR with github.token, which cannot
trigger ci.yaml, so the PR arrived with no checks and nothing merged it.
Pricing data was therefore only as fresh as the last time someone merged
that PR by hand, even though the workflow already accepts a
workflow_dispatch.

Both jobs now run the checks that a pull request would have run - flake
check, Rust tests and the Node tests, in a parallel step - and push the
result straight to main. `just gen-models-dev-pricing` already runs before
validation, so the committed snapshots never lag the locked revision.

The commit and rebase-onto-main dance is shared by both jobs, so it lives in
a composite action. A concurrency group keeps a manual dispatch from racing
the hourly run's push.
Copilot AI review requested due to automatic review settings July 28, 2026 00:48
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 28, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
ccusage-guide fbdc937 Commit Preview URL

Branch Preview URL
Jul 28 2026, 01:03 AM

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026 •

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fbf9606e-945d-46e0-a2ff-87d63e79340f

📥 Commits

Reviewing files that changed from the base of the PR and between 8dddd22 and fbdc937.

📒 Files selected for processing (1)
  • .github/workflows/update-pricing.yaml

📝 Walkthrough

Walkthrough

Adds a reusable action that commits and rebases changes onto main, then updates pricing through serialized matrix jobs that validate changed artifacts and push selected files directly with scoped permissions.

Changes

Pricing automation

Layer / File(s) Summary
Commit and rebase action
.github/actions/commit-and-push-main/action.yaml
Adds required commit-message and path inputs, bot authentication, rebase conflict handling, and push logic.
Workflow coordination and permissions
.github/workflows/update-pricing.yaml
Serializes runs, scopes permissions to contents writing, runs matrix update legs, and conditionally validates and commits changes.
LiteLLM pricing update flow
.github/scripts/update-litellm-lock.nu, .github/workflows/update-pricing.yaml
Updates the LiteLLM pin, compares locked pricing data, removes lock-only changes, and exports change status for workflow control.
models.dev pricing update flow
.github/scripts/update-models-dev-lock.nu, .github/workflows/update-pricing.yaml
Regenerates pricing artifacts, removes lock-only churn, detects changed paths, and exports change status for workflow control.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PricingWorkflow
  participant UpdateScripts
  participant ValidationChecks
  participant CommitAndPushMain
  participant MainBranch
  PricingWorkflow->>UpdateScripts: update pins and detect artifact changes
  UpdateScripts-->>PricingWorkflow: changed output
  PricingWorkflow->>ValidationChecks: run checks when git changes exist
  ValidationChecks-->>PricingWorkflow: validation result
  PricingWorkflow->>CommitAndPushMain: provide commit message and paths
  CommitAndPushMain->>MainBranch: fetch, rebase, and push validated changes
Loading

Possibly related PRs

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: pricing updates now land directly on main instead of opening pull requests.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/pricing-immediate-updates

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@pkg-pr-new

pkg-pr-new Bot commented Jul 28, 2026

Copy link
Copy Markdown

Open in StackBlitz

ccusage

npx https://pkg.pr.new/ccusage@1509

@ccusage/ccusage-darwin-arm64

npx https://pkg.pr.new/@ccusage/ccusage-darwin-arm64@1509

@ccusage/ccusage-darwin-x64

npx https://pkg.pr.new/@ccusage/ccusage-darwin-x64@1509

@ccusage/ccusage-linux-arm64

npx https://pkg.pr.new/@ccusage/ccusage-linux-arm64@1509

@ccusage/ccusage-linux-x64

npx https://pkg.pr.new/@ccusage/ccusage-linux-x64@1509

@ccusage/ccusage-win32-x64

npx https://pkg.pr.new/@ccusage/ccusage-win32-x64@1509

commit: aa740d4

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The codex-auto-review-fallbacks.json path in the models-dev job is wrong — the tracked file is at rust/adapters/codex/src/codex-auto-review-fallbacks.json, not rust/crates/ccusage-adapter-codex/src/codex-auto-review-fallbacks.json. This would cause the skip step's git checkout -- to fail when the snapshots are unchanged, and the push step's git commit to fail when they changed — the models-dev update job can't succeed with either outcome.

Reviewed changes — replaces the PR-creation workflow with inline validation (nix flake check, Rust tests, JS tests in parallel) and a direct push to main via a new composite action. Adds concurrency guarding, step outputs for change detection, and timeouts.

  • New commit-and-push-main composite action — commits given paths as github-actions[bot], rebases onto origin/main, and pushes. Fails on rebase conflicts so the next run retries.
  • Inline validation — both jobs run nix flake check, nix build .#ccusage-tests, and just test-node in parallel before pushing, replacing the old single-step just check. The git diff --quiet && exit 0 no-op in each leg skips validation when unchanged.
  • Step outputs for change detection — skip steps emit changed=true/false via GITHUB_OUTPUT, gating the push action.
  • Concurrency — update-pricing group with cancel-in-progress: false prevents hourly/manual dispatch races.
  • Permissions and config — dropped pull-requests: write, added timeout-minutes: 60 and fetch-depth: 0.

Pullfrog  | ⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

Comment thread .github/workflows/update-pricing.yaml Outdated
paths: >-
flake.lock
rust/crates/ccusage-core/src/models-dev-pricing.json
rust/crates/ccusage-adapter-codex/src/codex-auto-review-fallbacks.json

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong path for codex-auto-review-fallbacks.json. The tracked file is at rust/adapters/codex/src/codex-auto-review-fallbacks.json (matching just gen-models-dev-pricing). The path used here (rust/crates/ccusage-adapter-codex/...) doesn't exist and would cause git commit to fail because the actual changed file at the correct path never gets committed.

Technical details
# Wrong file path for codex-auto-review-fallbacks.json

## Affected sites
- `.github/workflows/update-pricing.yaml:141` — `paths:` input to `commit-and-push-main` (new code)
- `.github/workflows/update-pricing.yaml:108` — `git checkout --` in the skip step's unchanged branch (pre-existing; would fail, killing the step)
- `.github/workflows/update-pricing.yaml:106` — `git diff --quiet --` check in the skip step (harmless — nonexistent pathspec returns 0, so the check silently ignores this file)

## Required outcome
- Replace `rust/crates/ccusage-adapter-codex/src/codex-auto-review-fallbacks.json` with `rust/adapters/codex/src/codex-auto-review-fallbacks.json` in all three locations above

## Suggested approach
Lines 106 and 108 are pre-existing, unchanged context lines; this PR's new code at line 141 is the trigger for fixing all three. The `git ls-files` output and `gen-models-dev-pricing` just recipe both confirm the correct path is `rust/adapters/codex/src/codex-auto-review-fallbacks.json`.

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: aa740d4160d4
Base SHA: 1dab5c2afcf3

This compares the Rust PR release binary against the configured base package on the same CI runner.

Package runtime diagnostics

Compares the PR package wrapper, the installed native optional dependency binary, and the workspace release binary on the same large fixture. This identifies whether slow package results come from JavaScript wrapper overhead, the published native binary build, or the Rust core itself.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
All rows run --offline --json, measured by hyperfine with 0 warmups and 1 runs. This isolates wrapper overhead from the installed native optional dependency and the workspace release binary built on the runner.

Command Runtime Input Median Throughput Samples
claude --offline --json Package wrapper 1.01 GiB 361.9ms 2.78 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 339.1ms 2.97 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 118.1ms 8.53 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 96.1ms 10.47 GiB/s 1

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published native ccusage binary from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 2 warmups and 7 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude daily --offline --json 0.00 MiB 33.2ms 4.9ms 6.78x 55.00 MiB 12.45 MiB 0.23x 0.05 MiB/s 0.32 MiB/s
claude session --offline --json 0.00 MiB 25.1ms 4.6ms 5.41x 54.75 MiB 12.45 MiB 0.23x 0.06 MiB/s 0.33 MiB/s
codex daily --offline --json 0.00 MiB 22.9ms 2.4ms 9.54x 54.75 MiB 10.44 MiB 0.19x 0.04 MiB/s 0.36 MiB/s
codex session --offline --json 0.00 MiB 23.0ms 2.8ms 8.32x 55.00 MiB 10.44 MiB 0.19x 0.04 MiB/s 0.31 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published native ccusage binary from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 0 warmups and 1 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude --offline --json 1.01 GiB 372.3ms 319.4ms 1.17x 940.58 MiB 940.57 MiB 1.00x 2.70 GiB/s 3.15 GiB/s
codex --offline --json 1.01 GiB 119.7ms 97.2ms 1.23x 436.65 MiB 402.65 MiB 0.92x 8.41 GiB/s 10.36 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 18.78 KiB 18.78 KiB +0.00 KiB 1.00x
installed native package binary 4156.78 KiB 4156.78 KiB +0.00 KiB 1.00x

Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: aa740d4160d4
Base SHA: 1dab5c2afcf3

This compares the PR package against the configured base package on the same CI runner.

Package runtime diagnostics

Compares the PR package wrapper, the installed native optional dependency binary, and the workspace release binary on the same large fixture. This identifies whether slow package results come from JavaScript wrapper overhead, the published native binary build, or the Rust core itself.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
All rows run --offline --json, measured by hyperfine with 0 warmups and 1 runs. This isolates wrapper overhead from the installed native optional dependency and the workspace release binary built on the runner.

Command Runtime Input Median Throughput Samples
claude --offline --json Package wrapper 1.01 GiB 339.3ms 2.97 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 314.1ms 3.21 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 119.2ms 8.44 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 94.2ms 10.69 GiB/s 1

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published ccusage package from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 2 warmups and 7 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude daily --offline --json 0.00 MiB 30.2ms 28.8ms 1.05x 55.00 MiB 55.00 MiB 1.00x 0.05 MiB/s 0.05 MiB/s
claude session --offline --json 0.00 MiB 25.5ms 25.5ms 1.00x 55.00 MiB 55.00 MiB 1.00x 0.06 MiB/s 0.06 MiB/s
codex daily --offline --json 0.00 MiB 24.5ms 24.8ms 0.99x 55.00 MiB 55.00 MiB 1.00x 0.04 MiB/s 0.03 MiB/s
codex session --offline --json 0.00 MiB 22.6ms 23.6ms 0.96x 55.25 MiB 55.00 MiB 1.00x 0.04 MiB/s 0.04 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published ccusage package from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 0 warmups and 1 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude --offline --json 1.01 GiB 360.0ms 360.7ms 1.00x 968.57 MiB 942.58 MiB 0.97x 2.80 GiB/s 2.79 GiB/s
codex --offline --json 1.01 GiB 116.6ms 120.1ms 0.97x 410.66 MiB 414.64 MiB 1.01x 8.63 GiB/s 8.38 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 18.78 KiB 18.78 KiB +0.00 KiB 1.00x
installed native package binary 4156.78 KiB 4156.78 KiB +0.00 KiB 1.00x

Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/update-pricing.yaml:
- Around line 82-93: Change the if condition for update-models-dev-pricing from
always() to !cancelled() so the dependent job still runs after success or
failure but does not start after manual workflow cancellation.
- Around line 56-75: Replace the repeated git diff --quiet guards in the
parallel checks at .github/workflows/update-pricing.yaml lines 56-75 and 117-133
with the existing changed-state outputs from steps.litellm.outputs.changed or
steps.models-dev.outputs.changed; if step-level if conditions are unavailable,
use git --no-optional-locks diff --quiet instead. Apply the same gating approach
to every affected check leg.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 627289ff-42f4-4325-9517-c7ab552c510a

📥 Commits

Reviewing files that changed from the base of the PR and between 1dab5c2 and aa740d4.

📒 Files selected for processing (2)
  • .github/actions/commit-and-push-main/action.yaml
  • .github/workflows/update-pricing.yaml

Comment thread .github/workflows/update-pricing.yaml Outdated
Comment thread .github/workflows/update-pricing.yaml Outdated
Comment on lines 82 to 93
update-models-dev-pricing:
needs: update-pricing
if: ${{ always() }}
runs-on: blacksmith-32vcpu-ubuntu-2404
timeout-minutes: 60
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate workflow =="
fd -a 'update-pricing\.yaml' .github/workflows || true

echo "== relevant workflow excerpt =="
if [ -f .github/workflows/update-pricing.yaml ]; then
  nl -ba .github/workflows/update-pricing.yaml | sed -n '1,140p'
fi

echo "== changed context if available =="
git diff -- .github/workflows/update-pricing.yaml | sed -n '1,220p' || true

echo "== search relevant expressions in workflows =="
rg -n "update-models-dev-pricing|needs: update-pricing|if:\s*\$\{\{\s*always|if:\s*\$\{\{\s*!cancelled|push|checkout@" .github/workflows || true

Repository: ccusage/ccusage

Length of output: 304


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== relevant workflow excerpt =="
sed -n '1,140p' .github/workflows/update-pricing.yaml

echo "== changed context if available =="
git diff -- .github/workflows/update-pricing.yaml | sed -n '1,220p' || true

echo "== search relevant expressions in workflows =="
rg -n "update-models-dev-pricing|needs: update-pricing|if:\s*\$\{\{\s*always|if:\s*\$\{\{\s*!cancelled|push|checkout@" .github/workflows || true

Repository: ccusage/ccusage

Length of output: 9593


🌐 Web query:

GitHub Actions always() function cancelled workflow run needs context documentation

💡 Result:

The GitHub Actions always() function causes a job or step to execute even when the workflow run is canceled [1][2]. Because always() evaluates to true even during a cancellation event, it can effectively prevent a job from being canceled when included in a job-level or step-level if condition [3][4]. Official GitHub documentation explicitly warns against using always() for tasks that could suffer from critical failures (such as fetching sources), as this may cause the workflow to hang until it times out [1][2]. For scenarios where you want a job or step to run regardless of success or failure, but you still want it to be skipped if the workflow is canceled, the recommended alternative is to use the !cancelled() expression [1][2]: if: ${{!cancelled }} This approach ensures the task executes after successful or failed steps, but respects the cancellation state of the workflow [5].

Citations:


Use !cancelled() for the dependent pricing job.

always() forces update-models-dev-pricing to start even when the workflow run is manually cancelled, then it can still push changes to main before the cancellation takes effect. Use !cancelled() to retain “run after success/failure” while honoring cancellations.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/update-pricing.yaml around lines 82 - 93, Change the if
condition for update-models-dev-pricing from always() to !cancelled() so the
dependent job still runs after success or failure but does not start after
manual workflow cancellation.

The two update steps were shell pipelines around jq, curl and cmp writing
JSON to /tmp. Nushell handles the same work as structured data: the lock
file is read with `open`, the pricing document is fetched with `http get`
and compared as a record, so no temporary files or byte-level formatting
assumptions are involved.

The scripts carry the repository's Nix shebang, so they need no extra
nushell setup step in the workflow and stay runnable by hand.

models.dev now decides through a `match` over {snapshots, lock}, which
names the three states the step can end in - snapshots moved, only the lock
moved, nothing moved - instead of inferring them from consecutive `git
diff` calls.
Copilot AI review requested due to automatic review settings July 28, 2026 00:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

The validation legs only covered flake check, the Rust tests and the Node
tests, so the Babashka harness test from ci.yaml's test job was missing. Now
that the push lands on main without any pull request checks behind it, the
workflow has to run what a PR would have run: `nix flake check` already
covers the preflight job's treefmt and gitleaks checks, and the remaining
legs match the test job leg for leg.

The two sources were separate jobs with a duplicated leg list, which is how
the Babashka leg went missing in the first place. They are a matrix now, so
the checks are written once and both sources cannot drift apart.
`max-parallel: 1` preserves the sequential push the previous `needs:`
ordering gave.
Copilot AI review requested due to automatic review settings July 28, 2026 01:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found and verified against the latest diff

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".github/actions/commit-and-push-main/action.yaml">

<violation number="1" location=".github/actions/commit-and-push-main/action.yaml:38">
P1: A manual run started from a non-main branch can publish that branch's existing commits to `main`, not just the generated pricing snapshot. Because checkout is not pinned to `main` and the action rebases/pushes the whole `HEAD`, the direct push can replay every branch commit that is not already reachable from `main`. Restricting this workflow to `refs/heads/main` or explicitly checking out `main` before generating the update would prevent that history leak.</violation>
</file>

<file name=".github/workflows/update-pricing.yaml">

<violation number="1" location=".github/workflows/update-pricing.yaml:20">
P2: Since this job now pushes directly to `main` via `commit-and-push-main` instead of opening a PR, `if: ${{ always() }}` lets the job start and push even after a manual cancellation of the workflow run, since `always()` ignores cancellation status. Consider using `!cancelled()` instead to preserve run-after-success/failure semantics while still respecting cancellation.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

echo 'main moved with a conflicting change; the next run will retry.'
exit 1
fi
git push origin HEAD:main

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: A manual run started from a non-main branch can publish that branch's existing commits to main, not just the generated pricing snapshot. Because checkout is not pinned to main and the action rebases/pushes the whole HEAD, the direct push can replay every branch commit that is not already reachable from main. Restricting this workflow to refs/heads/main or explicitly checking out main before generating the update would prevent that history leak.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/actions/commit-and-push-main/action.yaml, line 38:

<comment>A manual run started from a non-main branch can publish that branch's existing commits to `main`, not just the generated pricing snapshot. Because checkout is not pinned to `main` and the action rebases/pushes the whole `HEAD`, the direct push can replay every branch commit that is not already reachable from `main`. Restricting this workflow to `refs/heads/main` or explicitly checking out `main` before generating the update would prevent that history leak.</comment>

<file context>
@@ -0,0 +1,38 @@
+          echo 'main moved with a conflicting change; the next run will retry.'
+          exit 1
+        fi
+        git push origin HEAD:main
</file context>

update-pricing:
name: ${{ matrix.name }}
runs-on: blacksmith-32vcpu-ubuntu-2404
timeout-minutes: 60

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Since this job now pushes directly to main via commit-and-push-main instead of opening a PR, if: ${{ always() }} lets the job start and push even after a manual cancellation of the workflow run, since always() ignores cancellation status. Consider using !cancelled() instead to preserve run-after-success/failure semantics while still respecting cancellation.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/update-pricing.yaml, line 19:

<comment>Since this job now pushes directly to `main` via `commit-and-push-main` instead of opening a PR, `if: ${{ always() }}` lets the job start and push even after a manual cancellation of the workflow run, since `always()` ignores cancellation status. Consider using `!cancelled()` instead to preserve run-after-success/failure semantics while still respecting cancellation.</comment>

<file context>
@@ -7,15 +7,22 @@ on:
 jobs:
   update-pricing:
     runs-on: blacksmith-32vcpu-ubuntu-2404
+    timeout-minutes: 60
     permissions:
       contents: write
</file context>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The matrix refactor in fbdc937 merged the two jobs into one and removed the if: ${{ always() }} this targeted, so the condition no longer exists on main.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The path bug for codex-auto-review-fallbacks.json flagged in the prior review was not addressed — it was carried forward into the new update-models-dev-lock.nu script's SNAPSHOTS constant. The models-dev update job will fail on every run with either outcome.

Reviewed changes — moved the lock-update logic for both LiteLLM and models.dev from inline shell steps into two Nushell scripts, each using a Nix shebang so the workflow can call them directly without a separate Nushell setup step.

  • New update-litellm-lock.nu — fetches the pricing JSON before and after nix flake update litellm, compares the records in Nushell (replacing the curl/jq/cmp dance), and emits changed=true/false via GITHUB_OUTPUT.
  • New update-models-dev-lock.nu — runs nix flake update models-dev and just gen-models-dev-pricing, checks snapshot and lock dirtiness with a dirty helper, and emits changed=true/false with a three-way match over {snapshots, lock}.
  • Workflow call-site simplification — both jobs replace inline shell capture/update/skip/create-PR steps with single run: calls to the Nushell scripts plus the existing parallel: validation legs and commit-and-push-main.

ℹ️ Path bug carried into new update-models-dev-lock.nu SNAPSHOTS

The prior review identified that codex-auto-review-fallbacks.json lives at rust/adapters/codex/src/codex-auto-review-fallbacks.json, not rust/crates/ccusage-adapter-codex/src/codex-auto-review-fallbacks.json. This commit relocated the lock-update logic into a Nushell script without fixing the path — the SNAPSHOTS constant on line 6 uses the same wrong path, and the workflow's paths: input on line 97 was left unchanged. The models-dev pricing update job cannot succeed: git diff --quiet -- silently ignores the nonexistent path, git checkout -- fails when the snapshots haven't changed, and commit-and-push-main fails to commit the actual changed file at the correct path.

Technical details
# Path bug carried into new Nushell script

## Affected sites
- `.github/scripts/update-models-dev-lock.nu:6` — `SNAPSHOTS` constant (new in this commit)
- `.github/workflows/update-pricing.yaml:97` — `paths:` input to `commit-and-push-main` (pre-existing)

## Required outcome
- Replace `rust/crates/ccusage-adapter-codex/src/codex-auto-review-fallbacks.json` with `rust/adapters/codex/src/codex-auto-review-fallbacks.json` in both locations
- Verify `just gen-models-dev-pricing` still writes to the corrected path

## Suggested approach
`git ls-files` confirms the tracked file is at `rust/adapters/codex/src/codex-auto-review-fallbacks.json`. Fix line 6 in the script and line 97 in the workflow.

Pullfrog  | ⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏


const SNAPSHOTS = [
'rust/crates/ccusage-core/src/models-dev-pricing.json'
'rust/crates/ccusage-adapter-codex/src/codex-auto-review-fallbacks.json'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Wrong path carried forward from prior review. The tracked file is at rust/adapters/codex/src/codex-auto-review-fallbacks.json (confirmed by git ls-files and just gen-models-dev-pricing). This path doesn't exist, so git diff --quiet -- silently ignores the file and git checkout -- fails when the snapshots haven't changed.

Technical details
# Wrong path in SNAPSHOTS constant

## Affected sites
- `.github/scripts/update-models-dev-lock.nu:6` — this line
- `.github/workflows/update-pricing.yaml:97` — `paths:` input to `commit-and-push-main`

## Required outcome
- Replace `rust/crates/ccusage-adapter-codex/src/codex-auto-review-fallbacks.json` with `rust/adapters/codex/src/codex-auto-review-fallbacks.json` in both locations

## Suggested approach
`git ls-files` confirms the correct path; the two fix sites are a single string replacement each.

Comment thread .github/workflows/update-pricing.yaml Outdated
paths: >-
flake.lock
rust/crates/ccusage-core/src/models-dev-pricing.json
rust/crates/ccusage-adapter-codex/src/codex-auto-review-fallbacks.json

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Same wrong path as the script above. The tracked file is at rust/adapters/codex/src/codex-auto-review-fallbacks.json. When the snapshots changed, git commit will succeed for flake.lock and models-dev-pricing.json but the actually-changed file at the correct path will be left out of the commit.

@ryoppippi
ryoppippi merged commit b184bdc into main Jul 28, 2026
17 of 19 checks passed
@ryoppippi
ryoppippi deleted the ci/pricing-immediate-updates branch July 28, 2026 01:05

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/scripts/update-models-dev-lock.nu:
- Around line 15-27: Update the snapshot change detection around state and
SNAPSHOTS so it captures the pre-regeneration snapshots and compares their open
--raw ... | from json results rather than raw git bytes. Keep git diff-based
detection only for flake.lock, and preserve the existing lock-only rollback and
validation behavior when parsed snapshot content is unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 55762c46-7bf9-4639-bf12-aa6a038d5e81

📥 Commits

Reviewing files that changed from the base of the PR and between aa740d4 and 8dddd22.

📒 Files selected for processing (3)
  • .github/scripts/update-litellm-lock.nu
  • .github/scripts/update-models-dev-lock.nu
  • .github/workflows/update-pricing.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/update-pricing.yaml

Comment on lines +15 to +27
let state = {
snapshots: (dirty ...$SNAPSHOTS)
lock: (dirty flake.lock)
}
let changed = match $state {
{snapshots: true} => true
{snapshots: false, lock: true} => {
print 'models.dev pricing snapshots are unchanged; dropping the lock-only bump.'
^git checkout -- flake.lock ...$SNAPSHOTS
false
}
_ => false
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Compare generated snapshots as JSON, not raw Git bytes.

dirty ...$SNAPSHOTS treats whitespace or formatting-only rewrites as pricing changes, causing unnecessary validation and commits to main. Capture the snapshots before regeneration and compare open --raw ... | from json results; keep git diff only for flake.lock.

Proposed fix
 def main [] {
+    let before = ($SNAPSHOTS | each {|path| open --raw $path | from json })
     ^nix flake update models-dev
     ^nix develop --command just gen-models-dev-pricing

     let state = {
-        snapshots: (dirty ...$SNAPSHOTS)
+        snapshots: (($SNAPSHOTS
+            | each {|path| open --raw $path | from json }) != $before)
         lock: (dirty flake.lock)
     }

Also applies to: 32-35

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/scripts/update-models-dev-lock.nu around lines 15 - 27, Update the
snapshot change detection around state and SNAPSHOTS so it captures the
pre-regeneration snapshots and compares their open --raw ... | from json results
rather than raw git bytes. Keep git diff-based detection only for flake.lock,
and preserve the existing lock-only rollback and validation behavior when parsed
snapshot content is unchanged.

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: 8dddd22c4407
Base SHA: 1dab5c2afcf3

This compares the Rust PR release binary against the configured base package on the same CI runner.

Package runtime diagnostics

Compares the PR package wrapper, the installed native optional dependency binary, and the workspace release binary on the same large fixture. This identifies whether slow package results come from JavaScript wrapper overhead, the published native binary build, or the Rust core itself.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
All rows run --offline --json, measured by hyperfine with 0 warmups and 1 runs. This isolates wrapper overhead from the installed native optional dependency and the workspace release binary built on the runner.

Command Runtime Input Median Throughput Samples
claude --offline --json Package wrapper 1.01 GiB 348.1ms 2.89 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 336.0ms 3.00 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 122.2ms 8.24 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 97.2ms 10.36 GiB/s 1

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published native ccusage binary from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 2 warmups and 7 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude daily --offline --json 0.00 MiB 29.3ms 4.8ms 6.09x 55.25 MiB 12.45 MiB 0.23x 0.05 MiB/s 0.32 MiB/s
claude session --offline --json 0.00 MiB 29.2ms 3.4ms 8.49x 55.00 MiB 12.45 MiB 0.23x 0.05 MiB/s 0.45 MiB/s
codex daily --offline --json 0.00 MiB 24.8ms 2.4ms 10.52x 55.25 MiB 10.45 MiB 0.19x 0.03 MiB/s 0.36 MiB/s
codex session --offline --json 0.00 MiB 23.5ms 2.4ms 9.71x 55.00 MiB 10.45 MiB 0.19x 0.04 MiB/s 0.35 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published native ccusage binary from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 0 warmups and 1 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude --offline --json 1.01 GiB 367.1ms 336.0ms 1.09x 958.58 MiB 954.58 MiB 1.00x 2.74 GiB/s 3.00 GiB/s
codex --offline --json 1.01 GiB 123.7ms 98.1ms 1.26x 420.65 MiB 428.65 MiB 1.02x 8.14 GiB/s 10.27 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 18.78 KiB 18.78 KiB -0.00 KiB 1.00x
installed native package binary 4156.78 KiB 4156.78 KiB +0.00 KiB 1.00x

Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: 8dddd22c4407
Base SHA: 1dab5c2afcf3

This compares the PR package against the configured base package on the same CI runner.

Package runtime diagnostics

Compares the PR package wrapper, the installed native optional dependency binary, and the workspace release binary on the same large fixture. This identifies whether slow package results come from JavaScript wrapper overhead, the published native binary build, or the Rust core itself.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
All rows run --offline --json, measured by hyperfine with 0 warmups and 1 runs. This isolates wrapper overhead from the installed native optional dependency and the workspace release binary built on the runner.

Command Runtime Input Median Throughput Samples
claude --offline --json Package wrapper 1.01 GiB 356.5ms 2.82 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 315.6ms 3.19 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 110.5ms 9.11 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 93.5ms 10.76 GiB/s 1

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published ccusage package from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 2 warmups and 7 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude daily --offline --json 0.00 MiB 28.4ms 27.4ms 1.04x 55.00 MiB 55.00 MiB 1.00x 0.05 MiB/s 0.06 MiB/s
claude session --offline --json 0.00 MiB 30.6ms 26.7ms 1.15x 55.00 MiB 55.25 MiB 1.00x 0.05 MiB/s 0.06 MiB/s
codex daily --offline --json 0.00 MiB 25.0ms 24.3ms 1.03x 55.00 MiB 55.25 MiB 1.00x 0.03 MiB/s 0.04 MiB/s
codex session --offline --json 0.00 MiB 21.9ms 25.1ms 0.87x 55.00 MiB 55.00 MiB 1.00x 0.04 MiB/s 0.03 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published ccusage package from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 0 warmups and 1 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude --offline --json 1.01 GiB 371.7ms 343.5ms 1.08x 952.58 MiB 984.58 MiB 1.03x 2.71 GiB/s 2.93 GiB/s
codex --offline --json 1.01 GiB 116.4ms 118.5ms 0.98x 434.63 MiB 410.64 MiB 0.94x 8.65 GiB/s 8.50 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 18.78 KiB 18.78 KiB -0.00 KiB 1.00x
installed native package binary 4156.78 KiB 4156.78 KiB +0.00 KiB 1.00x

Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: fbdc937b4c27
Base SHA: 1dab5c2afcf3

This compares the Rust PR release binary against the configured base package on the same CI runner.

Package runtime diagnostics

Compares the PR package wrapper, the installed native optional dependency binary, and the workspace release binary on the same large fixture. This identifies whether slow package results come from JavaScript wrapper overhead, the published native binary build, or the Rust core itself.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
All rows run --offline --json, measured by hyperfine with 0 warmups and 1 runs. This isolates wrapper overhead from the installed native optional dependency and the workspace release binary built on the runner.

Command Runtime Input Median Throughput Samples
claude --offline --json Package wrapper 1.01 GiB 354.9ms 2.84 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 307.7ms 3.27 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 118.8ms 8.47 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 91.8ms 10.96 GiB/s 1

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published native ccusage binary from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 2 warmups and 7 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude daily --offline --json 0.00 MiB 29.8ms 4.9ms 6.06x 55.00 MiB 12.44 MiB 0.23x 0.05 MiB/s 0.31 MiB/s
claude session --offline --json 0.00 MiB 24.0ms 2.5ms 9.49x 55.00 MiB 12.45 MiB 0.23x 0.06 MiB/s 0.61 MiB/s
codex daily --offline --json 0.00 MiB 22.7ms 2.4ms 9.43x 55.00 MiB 10.44 MiB 0.19x 0.04 MiB/s 0.36 MiB/s
codex session --offline --json 0.00 MiB 24.0ms 2.3ms 10.42x 55.00 MiB 10.44 MiB 0.19x 0.04 MiB/s 0.37 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published native ccusage binary from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 0 warmups and 1 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude --offline --json 1.01 GiB 369.8ms 327.2ms 1.13x 948.58 MiB 940.58 MiB 0.99x 2.72 GiB/s 3.08 GiB/s
codex --offline --json 1.01 GiB 118.5ms 95.2ms 1.24x 402.64 MiB 410.65 MiB 1.02x 8.50 GiB/s 10.58 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 18.78 KiB 18.78 KiB +0.00 KiB 1.00x
installed native package binary 4156.78 KiB 4156.78 KiB +0.00 KiB 1.00x

Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: fbdc937b4c27
Base SHA: 1dab5c2afcf3

This compares the PR package against the configured base package on the same CI runner.

Package runtime diagnostics

Compares the PR package wrapper, the installed native optional dependency binary, and the workspace release binary on the same large fixture. This identifies whether slow package results come from JavaScript wrapper overhead, the published native binary build, or the Rust core itself.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
All rows run --offline --json, measured by hyperfine with 0 warmups and 1 runs. This isolates wrapper overhead from the installed native optional dependency and the workspace release binary built on the runner.

Command Runtime Input Median Throughput Samples
claude --offline --json Package wrapper 1.01 GiB 351.2ms 2.87 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 335.3ms 3.00 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 122.2ms 8.24 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 91.8ms 10.97 GiB/s 1

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published ccusage package from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 2 warmups and 7 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude daily --offline --json 0.00 MiB 27.5ms 26.6ms 1.04x 55.25 MiB 55.00 MiB 1.00x 0.06 MiB/s 0.06 MiB/s
claude session --offline --json 0.00 MiB 27.8ms 25.1ms 1.11x 55.00 MiB 55.00 MiB 1.00x 0.06 MiB/s 0.06 MiB/s
codex daily --offline --json 0.00 MiB 23.6ms 24.3ms 0.97x 55.00 MiB 55.00 MiB 1.00x 0.04 MiB/s 0.04 MiB/s
codex session --offline --json 0.00 MiB 23.2ms 22.4ms 1.04x 55.00 MiB 55.00 MiB 1.00x 0.04 MiB/s 0.04 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published ccusage package from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 0 warmups and 1 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude --offline --json 1.01 GiB 396.0ms 349.7ms 1.13x 952.58 MiB 954.58 MiB 1.00x 2.54 GiB/s 2.88 GiB/s
codex --offline --json 1.01 GiB 119.7ms 117.9ms 1.02x 424.89 MiB 412.90 MiB 0.97x 8.41 GiB/s 8.54 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 18.78 KiB 18.78 KiB +0.00 KiB 1.00x
installed native package binary 4156.78 KiB 4156.78 KiB +0.00 KiB 1.00x

Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

ryoppippi added a commit that referenced this pull request Jul 28, 2026
main has no required status checks, so GitHub auto-merge has nothing to wait
for: `gh pr merge --auto` merged #1509 while its CI was still running, and
renovate's platformAutomerge would merge dependency PRs the same way.

Requiring the existing checks directly does not work, because everything
behind `changes` is skipped on docs-only pull requests and a skipped check
never satisfies a requirement. This adds the usual aggregator instead: it
needs every job action-timeline already waits for, treats skipped as fine,
and fails only when a job failed or was cancelled. That single "ci gate"
check is what main's ruleset can require.
ryoppippi added a commit that referenced this pull request Jul 28, 2026
main has no required status checks, so GitHub's auto-merge merges the moment
it is enabled - that is how #1509 landed mid-CI. platformAutomerge hands
renovate's automerge to that same mechanism, which would merge dependency
PRs before their checks report.

Requiring the new `ci gate` check on main would fix it for every merge path
at once, but a repository ruleset cannot list the built-in GitHub Actions
integration as a bypass actor:

  Actor GitHub Actions integration must be part of the ruleset source or
  owner organization

and required status checks reject direct pushes too, so the rule would break
update-pricing.yaml's push to main. Bypassing by write role does not help
either, because renovate holds write and would bypass the gate it is meant
to obey.

With platformAutomerge off, renovate merges the branch itself once it has
seen the branch checks pass, which needs no repository rules at all.
ryoppippi added a commit that referenced this pull request Jul 28, 2026
main has no required status checks, so GitHub's auto-merge merges the moment
it is enabled - that is how #1509 landed mid-CI. platformAutomerge hands
renovate's automerge to that same mechanism, which would merge dependency
PRs before their checks report.

Requiring the new `ci gate` check on main would fix it for every merge path
at once, but a repository ruleset cannot list the built-in GitHub Actions
integration as a bypass actor:

  Actor GitHub Actions integration must be part of the ruleset source or
  owner organization

and required status checks reject direct pushes too, so the rule would break
update-pricing.yaml's push to main. Bypassing by write role does not help
either, because renovate holds write and would bypass the gate it is meant
to obey.

With platformAutomerge off, renovate merges the branch itself once it has
seen the branch checks pass, which needs no repository rules at all.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants