Repository navigation
ci: migrate workflows to Blacksmith - #1249
Conversation
Move CI, release preparation, scheduled automation, and lightweight gate jobs onto Blacksmith runners. Keep macOS Intel and Windows ARM only in the release native package matrix because those runner families are not available on Blacksmith. Replace nix-community/cache-nix-action with a pinned useblacksmith/stickydisk mount for the Linux /nix store, using stable package/dev keys per runner architecture so Nix builds reuse persistent store state on every Blacksmith Linux job. Replace the old GitHub Actions cache cleanup workflow with weekly Blacksmith sticky disk Nix garbage collection for package and dev profiles, deleting store paths older than seven days. Split npm release publishing so Blacksmith restores native artifacts and packs npm tarballs, while the final GitHub-hosted job only runs pnpm publish with provenance for OIDC trusted publishing.
|
Caution Review failedPull request was closed or merged during review Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR migrates CI and release workflows to Blacksmith self-hosted runners, introduces platform-specific Nix caching composites (Linux sticky-disk and macOS Nix cache), extracts native package builds into per-platform composites, replaces GitHub Actions cache GC with Nix store GC, adds actionlint config for runner validation, and fixes Darwin binary distribution by rewriting libiconv dylib references to system paths. ChangesNix Infrastructure and Action Setup
Per-Platform Native Package Composites
Workflow Runner Migrations and Composite Integration
Minor Updates and Fixes
🎯 3 (Moderate) | ⏱️ ~25 minutes
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
ccusage-guide | f8a302d | Commit Preview URL Branch Preview URL |
Jun 10 2026, 08:03 PM |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
.github/actions/setup-nix/action.yaml (1)
16-18: 💤 Low valueConsider more restrictive permissions for the Nix store mount point.
Using
chmod -R 777on/nixis overly permissive. While this is a transient state before Nix takes ownership, a more restrictive permission like755would suffice for Nix installation to proceed and reduces the window for unintended writes.♻️ Suggested fix
- sudo chmod -R 777 /nix + sudo chmod -R 755 /nix🤖 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/actions/setup-nix/action.yaml around lines 16 - 18, Change the overly-permissive chmod in the action run block: instead of `chmod -R 777 /nix` use a more restrictive mode (e.g., `chmod 755 /nix`) so the created `/nix/store` still allows installation but prevents world-writable access; keep the `sudo mkdir -p /nix/store` and apply the permission change non-recursively to `/nix` as shown in the run script snippet.
🤖 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/cache-gc.yaml:
- Line 33: The checkout step currently uses
actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 and leaves Git
credentials persisted; update the checkout invocation to include
persist-credentials: false so credentials are not exposed to later steps (i.e.,
add the persist-credentials: false option to the actions/checkout step in the
workflow).
In @.github/workflows/release.yaml:
- Around line 133-135: The checkout step using
actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 currently leaves Git
credentials persisted; update the checkout action configuration (the uses:
actions/checkout step) to add persist-credentials: false under the with block so
credentials are not saved for downstream steps that only download/publish
artifacts.
- Around line 136-139: The workflow uses actions/setup-node (the step currently
referencing actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e) and
should explicitly disable the package manager cache to prevent cache poisoning;
update that setup-node step to include package-manager-cache: false in the step
inputs (alongside registry-url and node-version) so caching is disabled for
package managers when running the release workflow.
- Around line 145-150: The "Publish npm packages" step runs pnpm publish but
doesn't provide the required NODE_AUTH_TOKEN, causing auth failures; update that
step (the one named "Publish npm packages" that reads packages from
"$RUNNER_TEMP/npm-packages" and runs pnpm publish) to set the environment
variable NODE_AUTH_TOKEN from a secret (e.g. NODE_AUTH_TOKEN: ${{
secrets.NPM_TOKEN }}), or otherwise export the token to npm
(//registry.npmjs.org/:_authToken) before calling pnpm publish so the publish
commands can authenticate.
---
Nitpick comments:
In @.github/actions/setup-nix/action.yaml:
- Around line 16-18: Change the overly-permissive chmod in the action run block:
instead of `chmod -R 777 /nix` use a more restrictive mode (e.g., `chmod 755
/nix`) so the created `/nix/store` still allows installation but prevents
world-writable access; keep the `sudo mkdir -p /nix/store` and apply the
permission change non-recursively to `/nix` as shown in the run script snippet.
🪄 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: f0adc4a9-f9e9-4411-8720-107d5a778341
📒 Files selected for processing (11)
.github/actionlint.yaml.github/actions/setup-nix/action.yaml.github/workflows/approve-contributor.yaml.github/workflows/cache-gc.yaml.github/workflows/check-pr-title.yaml.github/workflows/ci.yaml.github/workflows/issue-gate.yaml.github/workflows/pr-gate.yaml.github/workflows/pullfrog.yml.github/workflows/release.yaml.github/workflows/update-pricing.yaml
Address CodeRabbit feedback by avoiding persisted checkout credentials in release/cache maintenance jobs, disabling setup-node package-manager caching in the OIDC publish job, and tightening the temporary /nix mount point permission before stickydisk mounts.
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
6 issues found across 11 files
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/workflows/release.yaml">
<violation number="1" location=".github/workflows/release.yaml:152">
P1: `pnpm publish` is executed with a pnpm version pinned to 11.1.1, which is affected by known OIDC trusted-publishing auth issues with setup-node-generated npm auth config; release publishes can fail.</violation>
</file>
Tip: instead of fixing issues one by one fix them all with cubic
Re-trigger cubic
| shell: bash | ||
| run: | | ||
| while IFS= read -r package; do | ||
| pnpm publish "$package" --provenance --no-git-checks --access public |
There was a problem hiding this comment.
P1: pnpm publish is executed with a pnpm version pinned to 11.1.1, which is affected by known OIDC trusted-publishing auth issues with setup-node-generated npm auth config; release publishes can fail.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/release.yaml, line 152:
<comment>`pnpm publish` is executed with a pnpm version pinned to 11.1.1, which is affected by known OIDC trusted-publishing auth issues with setup-node-generated npm auth config; release publishes can fail.</comment>
<file context>
@@ -114,12 +111,51 @@ jobs:
+ shell: bash
+ run: |
+ while IFS= read -r package; do
+ pnpm publish "$package" --provenance --no-git-checks --access public
+ done < <(find "$RUNNER_TEMP/npm-packages" -name '*.tgz' -type f | sort)
</file context>
| pnpm publish "$package" --provenance --no-git-checks --access public | |
| NODE_AUTH_TOKEN="" pnpm publish "$package" --provenance --no-git-checks --access public |
Remove the cache-profile input from the setup-nix action so each OS and architecture uses one shared /nix sticky disk. The Nix store is content-addressed and safe to share within the platform scope, so keeping separate package and dev profiles only fragments the cache and doubles GC work. Simplify the weekly sticky disk GC matrix to match the shared Linux platform keys. Restore the npm publish job shape to the existing GitHub-hosted OIDC publishing flow; only the native package build jobs need the Blacksmith runner migration.
|
@coderabbitai review |
✅ Action performedReview finished.
|
Switch the PR gate away from pull_request_target so the contributor gate no longer runs as a privileged target workflow. It stays on the lightweight Blacksmith runner because the job does not need a larger machine. Enumerate the Blacksmith labels in actionlint so typos are caught during workflow linting. Limit the PR preview package publish to the native packages actually built on PRs; release still builds and publishes the full platform set. Keep npm release publishing on the existing GitHub-hosted OIDC path. For sticky disk Nix setup, fix mounted /nix ownership after the disk is attached so the single-user Nix installer can write to the store without using a world-writable mount point.
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
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/release.yaml:
- Around line 99-101: In the npm job's checkout step (the actions/checkout@...
invocation that currently sets fetch-depth: 0), add persist-credentials: false
under the with: block so GitHub Actions will not persist repo credentials to the
workspace; update the with block alongside fetch-depth to include
persist-credentials: false to reduce credential exposure when only downloading
artifacts and publishing packages.
- Around line 103-106: The setup-node step currently configures registry-url and
node-version but leaves package manager caching enabled; update the
actions/setup-node step (the one containing registry-url and node-version:
lts/*) to explicitly disable package manager caching by adding
package-manager-cache: false under its with block so the action will not create
a package manager cache in this privileged workflow (which also uses id-token:
write).
🪄 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: c2924d26-7c06-4a51-b3ca-609a52f0cd73
📒 Files selected for processing (5)
.github/actions/setup-nix/action.yaml.github/workflows/cache-gc.yaml.github/workflows/ci.yaml.github/workflows/release.yaml.github/workflows/update-pricing.yaml
💤 Files with no reviewable changes (2)
- .github/workflows/update-pricing.yaml
- .github/workflows/ci.yaml
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
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/release.yaml:
- Around line 99-101: In the npm job's checkout step (the actions/checkout@...
invocation that currently sets fetch-depth: 0), add persist-credentials: false
under the with: block so GitHub Actions will not persist repo credentials to the
workspace; update the with block alongside fetch-depth to include
persist-credentials: false to reduce credential exposure when only downloading
artifacts and publishing packages.
- Around line 103-106: The setup-node step currently configures registry-url and
node-version but leaves package manager caching enabled; update the
actions/setup-node step (the one containing registry-url and node-version:
lts/*) to explicitly disable package manager caching by adding
package-manager-cache: false under its with block so the action will not create
a package manager cache in this privileged workflow (which also uses id-token:
write).
🪄 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: c2924d26-7c06-4a51-b3ca-609a52f0cd73
📒 Files selected for processing (5)
.github/actions/setup-nix/action.yaml.github/workflows/cache-gc.yaml.github/workflows/ci.yaml.github/workflows/release.yaml.github/workflows/update-pricing.yaml
💤 Files with no reviewable changes (2)
- .github/workflows/update-pricing.yaml
- .github/workflows/ci.yaml
🛑 Comments failed to post (2)
.github/workflows/release.yaml (2)
99-101:
⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd
persist-credentials: falseto the npm job checkout.The checkout action at line 99 persists Git credentials by default. Since this job only downloads artifacts and publishes packages without needing Git operations, disabling credential persistence reduces the attack surface, especially given the
id-token: writepermission.🛡️ Proposed fix
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 + persist-credentials: false📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 persist-credentials: false🧰 Tools
🪛 zizmor (1.25.2)
[warning] 99-101: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
🤖 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/release.yaml around lines 99 - 101, In the npm job's checkout step (the actions/checkout@... invocation that currently sets fetch-depth: 0), add persist-credentials: false under the with: block so GitHub Actions will not persist repo credentials to the workspace; update the with block alongside fetch-depth to include persist-credentials: false to reduce credential exposure when only downloading artifacts and publishing packages.Source: Linters/SAST tools
103-106:
⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd
package-manager-cache: falseto prevent cache poisoning.The
actions/setup-nodeaction enables caching by default whenpackage.jsoncontains apackageManagerfield, creating a cache poisoning risk in privileged workflows. Since this npm job hasid-token: writeand publishes packages, explicitly disable the package manager cache.🛡️ Proposed fix
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: registry-url: 'https://registry.npmjs.org' node-version: lts/* + package-manager-cache: false🧰 Tools
🪛 zizmor (1.25.2)
[error] 103-103: runtime artifacts potentially vulnerable to a cache poisoning attack (cache-poisoning): enables caching by default
(cache-poisoning)
🤖 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/release.yaml around lines 103 - 106, The setup-node step currently configures registry-url and node-version but leaves package manager caching enabled; update the actions/setup-node step (the one containing registry-url and node-version: lts/*) to explicitly disable package manager caching by adding package-manager-cache: false under its with block so the action will not create a package manager cache in this privileged workflow (which also uses id-token: write).Source: Linters/SAST tools
There was a problem hiding this comment.
3 issues found across 11 files
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/workflows/release.yaml">
<violation number="1" location=".github/workflows/release.yaml:152">
P1: `pnpm publish` is executed with a pnpm version pinned to 11.1.1, which is affected by known OIDC trusted-publishing auth issues with setup-node-generated npm auth config; release publishes can fail.</violation>
</file>
<file name=".github/workflows/pr-gate.yaml">
<violation number="1" location=".github/workflows/pr-gate.yaml:4">
P1: Switching this workflow to `pull_request` can break PR auto-close enforcement for forked contributions because the token is read-only there.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
|
|
||
| on: | ||
| pull_request_target: | ||
| pull_request: |
There was a problem hiding this comment.
P1: Switching this workflow to pull_request can break PR auto-close enforcement for forked contributions because the token is read-only there.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/pr-gate.yaml, line 4:
<comment>Switching this workflow to `pull_request` can break PR auto-close enforcement for forked contributions because the token is read-only there.</comment>
<file context>
@@ -1,13 +1,13 @@
on:
- pull_request_target:
+ pull_request:
types:
- opened
</file context>
| pull_request: | |
| pull_request_target: |
There was a problem hiding this comment.
pull_request_target provides security incident recently. you should not propose this dangerous comment
There was a problem hiding this comment.
Got it — I won’t suggest that change for this workflow.
Remove the unsupported commit input from the pinned useblacksmith/stickydisk action and recreate /nix/store after the sticky disk is mounted before fixing ownership. The mounted filesystem can replace the pre-created directory tree, so /nix/store must be created after mount to avoid Linux ARM setup failures. Drop the remaining actions/cache based Cargo cache from Windows native package builds. The Blacksmith sticky disk action is documented as an ext4 mounted disk and the pinned implementation shells out to mkfs.ext4 and mount, so Windows native runners cannot safely use it as a direct replacement. Restore action-timeline to the main-branch ubuntu-slim runner because that reporting job does not need Blacksmith capacity.
Restore the lightweight PR gate, issue gate, contributor approval, PR title, code-change detection, and action timeline jobs to the GitHub-hosted ubuntu-slim runner. These jobs only run small GitHub API or reporting work and do not benefit from Blacksmith capacity. Keep the actionlint Blacksmith runner allow-list limited to the heavy runners that remain in use after moving lightweight jobs back to ubuntu-slim.
|
@coderabbitai review |
✅ Action performedReview finished.
|
Disable persisted checkout credentials in the npm publish and release jobs, and disable setup-node package manager caching in the npm publish job. This keeps the existing GitHub-hosted OIDC publish flow intact while addressing CodeRabbit credential and cache-poisoning findings. Remove the global SC2016 actionlint suppression and scope the shellcheck directive to the two inline Node.js command substitutions that intentionally use single-quoted JavaScript.
|
@coderabbitai review |
✅ Action performedReview finished.
|
Restore actions/cache for Windows Cargo builds in CI and release. Blacksmith documentation says official GitHub cache actions transparently use Blacksmith colocated cache on Blacksmith runners and recommends upstream cache actions over archived useblacksmith cache forks. Do not use useblacksmith/stickydisk for Windows Cargo because the sticky disk documentation describes ext4 filesystem mounts and the pinned action implementation formats and mounts ext4 block devices. Nix store caching remains on sticky disk as requested.
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
1 issue 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/workflows/cache-gc.yaml">
<violation number="1" location=".github/workflows/cache-gc.yaml:8">
P3: Workflow token is over-permissioned with `read-all`; scope it to only required permissions.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
| workflow_dispatch: | ||
|
|
||
| permissions: {} | ||
| permissions: read-all |
There was a problem hiding this comment.
P3: Workflow token is over-permissioned with read-all; scope it to only required permissions.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/cache-gc.yaml, line 8:
<comment>Workflow token is over-permissioned with `read-all`; scope it to only required permissions.</comment>
<file context>
@@ -1,39 +1,30 @@
workflow_dispatch:
-permissions: {}
+permissions: read-all
jobs:
</file context>
| permissions: read-all | |
| permissions: | |
| contents: read |
Replace the macOS build action input switch with separate Nix and Cargo composite actions. The CI macOS ARM build continues to use the Nix action, release macOS ARM uses the same path, and release Intel macOS calls the Cargo action so it avoids slow Nix setup and cache behavior on the GitHub-hosted Intel runner.
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
.github/workflows/ci.yaml (1)
120-120: Update:nix-github-tokenis a boolean-like gating flag (not a token value).The composite action’s
nix-github-tokeninput is defined as “Whether Nix builds may use the GitHub token from Nix config” and is compared to the string"false", so passing${{ github.event_name != 'pull_request' }}is appropriate. Renaming it for clarity (e.g.,enable-nix-github-token) would be optional.🤖 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/ci.yaml at line 120, The input name `nix-github-token` is a boolean-like gating flag (not an actual token) and is being compared to the string "false", so update the workflow to make intent clear: keep passing the expression `${{ github.event_name != 'pull_request' }}` to the composite action input `nix-github-token` (or optionally rename the input to something clearer like `enable-nix-github-token` in the composite action metadata and all call sites), and ensure any string comparisons against `"false"` in the composite action logic (e.g., checks inside the composite that reference `nix-github-token`) continue to handle the boolean-string value consistently..github/actions/build-linux-native-package/action.yaml (1)
27-37: 💤 Low valueSimplify: Both branches execute identical verification logic.
The
if/elsebranches on Line 31-37 run the samecatandgrepcommands regardless ofldd's exit status. You can simplify by capturing output unconditionally:♻️ Suggested refactor
- name: Verify Linux binary is static shell: bash run: | file "${{ inputs.binary }}" - if ldd "${{ inputs.binary }}" > "$RUNNER_TEMP/ldd.txt" 2>&1; then - cat "$RUNNER_TEMP/ldd.txt" - grep -Eiq 'not a dynamic executable|statically linked' "$RUNNER_TEMP/ldd.txt" - else - cat "$RUNNER_TEMP/ldd.txt" - grep -Eiq 'not a dynamic executable|statically linked' "$RUNNER_TEMP/ldd.txt" - fi + ldd "${{ inputs.binary }}" > "$RUNNER_TEMP/ldd.txt" 2>&1 || true + cat "$RUNNER_TEMP/ldd.txt" + grep -Eiq 'not a dynamic executable|statically linked' "$RUNNER_TEMP/ldd.txt"🤖 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/actions/build-linux-native-package/action.yaml around lines 27 - 37, The step "Verify Linux binary is static" contains an if/else whose bodies are identical — both branches run `cat "$RUNNER_TEMP/ldd.txt"` and `grep -Eiq 'not a dynamic executable|statically linked' "$RUNNER_TEMP/ldd.txt"` after calling `ldd` on `${{ inputs.binary }}`; simplify by running `ldd "${{ inputs.binary }}" > "$RUNNER_TEMP/ldd.txt" 2>&1` unconditionally and then always executing the `cat` and `grep` commands (remove the duplicated if/else), keeping the use of `$RUNNER_TEMP/ldd.txt` and the same grep pattern to preserve behavior.
🤖 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/actions/build-macos-native-package/action.yaml:
- Around line 15-17: Add a post-build verification step after the "Build macOS
binary" step that inspects the built binary(s) for any remaining /nix/store
paths and fails the job if any are found: locate the produced artifact from the
nix build (the output referenced by .#ccusage), run the macOS linker inspection
(otool -L) or string search against that binary, and assert that the output
contains no "/nix/store" entries; name the step "Verify no /nix/store
references" and ensure it returns non-zero on detection so install_name_tool
rewriting is effectively validated.
In @.github/workflows/ci.yaml:
- Line 114: In the build-native-packages job's checkout step that uses
actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10, add the configuration
key persist-credentials: false under the step's with: block to prevent GitHub
Actions from persisting PATs/credentials into the workspace and potentially
leaking them into uploaded artifacts; ensure the change mirrors other checkout
steps that already use persist-credentials: false.
In @.github/workflows/release.yaml:
- Around line 9-12: The build-native-packages job currently inherits default
token permissions; add an explicit permissions block under the
build-native-packages job to harden the workflow by specifying only the minimal
scopes needed (for example add a permissions map like "contents: read" and any
other specific scopes your job requires such as "packages: write" or "id-token:
write" if you publish or authenticate). Locate the build-native-packages job and
insert the permissions: section with only the required keys and values to avoid
broad default access.
---
Nitpick comments:
In @.github/actions/build-linux-native-package/action.yaml:
- Around line 27-37: The step "Verify Linux binary is static" contains an
if/else whose bodies are identical — both branches run `cat
"$RUNNER_TEMP/ldd.txt"` and `grep -Eiq 'not a dynamic executable|statically
linked' "$RUNNER_TEMP/ldd.txt"` after calling `ldd` on `${{ inputs.binary }}`;
simplify by running `ldd "${{ inputs.binary }}" > "$RUNNER_TEMP/ldd.txt" 2>&1`
unconditionally and then always executing the `cat` and `grep` commands (remove
the duplicated if/else), keeping the use of `$RUNNER_TEMP/ldd.txt` and the same
grep pattern to preserve behavior.
In @.github/workflows/ci.yaml:
- Line 120: The input name `nix-github-token` is a boolean-like gating flag (not
an actual token) and is being compared to the string "false", so update the
workflow to make intent clear: keep passing the expression `${{
github.event_name != 'pull_request' }}` to the composite action input
`nix-github-token` (or optionally rename the input to something clearer like
`enable-nix-github-token` in the composite action metadata and all call sites),
and ensure any string comparisons against `"false"` in the composite action
logic (e.g., checks inside the composite that reference `nix-github-token`)
continue to handle the boolean-string value consistently.
🪄 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: 09ef0f2a-7e78-4d82-b68f-5c9e45a065d6
📒 Files selected for processing (9)
.github/actions/build-linux-native-package/action.yaml.github/actions/build-macos-native-package/action.yaml.github/actions/build-windows-native-package/action.yaml.github/actions/setup-linux-blacksmith-sticky-disk/action.yaml.github/actions/setup-macos-nix-cache/action.yaml.github/actions/setup-native-build-cache/action.yaml.github/actions/setup-nix/action.yaml.github/workflows/ci.yaml.github/workflows/release.yaml
✅ Files skipped from review due to trivial changes (2)
- .github/actions/setup-native-build-cache/action.yaml
- .github/actions/setup-macos-nix-cache/action.yaml
| - name: Build macOS binary | ||
| shell: bash | ||
| run: nix build .#ccusage |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Add verification step to ensure no /nix/store references in the binary.
Issue #1251 explicitly requests a CI check to verify macOS binaries are free of /nix/store paths. The Linux action includes static-linkage verification; the macOS action should similarly verify that install_name_tool successfully rewrote library paths.
✅ Suggested verification step to add after the build
- name: Build macOS binary
shell: bash
run: nix build .#ccusage
+ - name: Verify macOS binary has no /nix/store references
+ shell: bash
+ run: |
+ echo "Checking dynamic library dependencies..."
+ otool -L "${{ inputs.binary }}"
+ if otool -L "${{ inputs.binary }}" | grep -q '/nix/store'; then
+ echo "Error: Binary contains /nix/store references"
+ exit 1
+ fi
+ echo "✓ No /nix/store references found"
+
- name: Stage native package📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: Build macOS binary | |
| shell: bash | |
| run: nix build .#ccusage | |
| - name: Build macOS binary | |
| shell: bash | |
| run: nix build .#ccusage | |
| - name: Verify macOS binary has no /nix/store references | |
| shell: bash | |
| run: | | |
| echo "Checking dynamic library dependencies..." | |
| otool -L "${{ inputs.binary }}" | |
| if otool -L "${{ inputs.binary }}" | grep -q '/nix/store'; then | |
| echo "Error: Binary contains /nix/store references" | |
| exit 1 | |
| fi | |
| echo "✓ No /nix/store references found" |
🤖 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/actions/build-macos-native-package/action.yaml around lines 15 - 17,
Add a post-build verification step after the "Build macOS binary" step that
inspects the built binary(s) for any remaining /nix/store paths and fails the
job if any are found: locate the produced artifact from the nix build (the
output referenced by .#ccusage), run the macOS linker inspection (otool -L) or
string search against that binary, and assert that the output contains no
"/nix/store" entries; name the step "Verify no /nix/store references" and ensure
it returns non-zero on detection so install_name_tool rewriting is effectively
validated.
| binary: rust/target/release/ccusage.exe | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 |
There was a problem hiding this comment.
Set persist-credentials: false to prevent credential leakage.
The checkout step in the build-native-packages job does not disable credential persistence. Since this job uploads artifacts, persisted credentials in the working directory could leak into the uploaded packages. Other checkout steps in this workflow (lines 19, 48, 221, 293) correctly set persist-credentials: false.
🔒 Proposed fix
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ with:
+ persist-credentials: false
- uses: ./.github/actions/build-linux-native-package📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| with: | |
| persist-credentials: false |
🧰 Tools
🪛 zizmor (1.25.2)
[warning] 114-114: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
🤖 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/ci.yaml at line 114, In the build-native-packages job's
checkout step that uses
actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10, add the configuration
key persist-credentials: false under the step's with: block to prevent GitHub
Actions from persisting PATs/credentials into the workspace and potentially
leaking them into uploaded artifacts; ensure the change mirrors other checkout
steps that already use persist-credentials: false.
Source: Linters/SAST tools
| build-native-packages: | ||
| name: ${{ matrix.name }} | ||
| runs-on: ${{ matrix.os }} | ||
| timeout-minutes: 30 |
There was a problem hiding this comment.
Set explicit minimal permissions for security hardening.
The build-native-packages job inherits default token permissions because no explicit permissions: block is defined. This widens the attack surface unnecessarily.
🛡️ Proposed fix
jobs:
build-native-packages:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
timeout-minutes: 30
+ permissions:
+ contents: read
strategy:📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| build-native-packages: | |
| name: ${{ matrix.name }} | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 30 | |
| jobs: | |
| build-native-packages: | |
| name: ${{ matrix.name }} | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 30 | |
| permissions: | |
| contents: read | |
| strategy: |
🧰 Tools
🪛 zizmor (1.25.2)
[warning] 9-67: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block
(excessive-permissions)
🤖 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/release.yaml around lines 9 - 12, The
build-native-packages job currently inherits default token permissions; add an
explicit permissions block under the build-native-packages job to harden the
workflow by specifying only the minimal scopes needed (for example add a
permissions map like "contents: read" and any other specific scopes your job
requires such as "packages: write" or "id-token: write" if you publish or
authenticate). Locate the build-native-packages job and insert the permissions:
section with only the required keys and values to avoid broad default access.
Source: Linters/SAST tools
Pass composite action inputs through environment variables before shell execution so zizmor does not treat arch and binary values as attacker-controllable template injection.
|
@coderabbitai review |
✅ Action performedReview finished.
|
ccusage performance comparisonPR SHA: This compares the PR package against the configured base package on the same CI runner. Package runner startupExecution setup measures any pre-benchmark package materialization used by the execution benchmark. Bunx temp cache measures one
Cached bunx execution performanceRuns the same large fixture through Fixtures: Claude
Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
ccusage performance comparisonPR SHA: This compares the Rust PR release binary against the configured base package on the same CI runner. Package runner startupExecution setup measures any pre-benchmark package materialization used by the execution benchmark. Bunx temp cache measures one
Cached bunx execution performanceRuns the same large fixture through Fixtures: Claude
Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
ccusage performance comparisonPR SHA: This compares the Rust PR release binary against the configured base package on the same CI runner. Package runner startupExecution setup measures any pre-benchmark package materialization used by the execution benchmark. Bunx temp cache measures one
Cached bunx execution performanceRuns the same large fixture through Fixtures: Claude
Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
ccusage performance comparisonPR SHA: This compares the PR package against the configured base package on the same CI runner. Package runner startupExecution setup measures any pre-benchmark package materialization used by the execution benchmark. Bunx temp cache measures one
Cached bunx execution performanceRuns the same large fixture through Fixtures: Claude
Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
ccusage performance comparisonPR SHA: This compares the Rust PR release binary against the configured base package on the same CI runner. Package runner startupExecution setup measures any pre-benchmark package materialization used by the execution benchmark. Bunx temp cache measures one
Cached bunx execution performanceRuns the same large fixture through Fixtures: Claude
Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
ccusage performance comparisonPR SHA: This compares the PR package against the configured base package on the same CI runner. Package runner startupExecution setup measures any pre-benchmark package materialization used by the execution benchmark. Bunx temp cache measures one
Cached bunx execution performanceRuns the same large fixture through Fixtures: Claude
Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
* refactor(nix): merge development flake back into the root flake Fold dev/flake.nix back into flake.nix so the repository ships a single flake again: treefmt-nix, git-hooks, and agent-skills inputs return to the root flake, and the development modules (treefmt, git-hooks, dev-shell, agent-skills) are imported alongside the package modules. The dev/ flake split (#1243) existed to keep package-only CI builds from evaluating development inputs, trading a simpler setup for speed. With the Blacksmith sticky-disk Nix store cache (#1249) the extra evaluation cost no longer matters, so the simpler single-flake layout wins. The pins for the restored inputs (agent-skills, git-hooks, treefmt-nix and their transitive deps) are carried over verbatim from dev/flake.lock, so no input revisions change. The dev shell is reached with plain 'nix develop' / 'use flake' again, and all './dev#' flake references move back to the root flake. * refactor(ci): drop the ci dev shell and explicit pnpm install steps Remove devShells.ci and the just install / flake-check recipes that existed to keep CI shells free of side effects. Every workflow now enters the default dev shell with plain 'nix develop --command', whose shellHook installs pnpm dependencies, syncs agent skills, and installs git hooks. The shellHook now runs 'pnpm install --frozen-lockfile' unconditionally instead of comparing lockfile mtimes: an up-to-date install takes about a second, and the conditional occasionally kept stale node_modules around after branch switches. Also drop the warm-dev-shell input from the setup-nix action (nothing passed it any more) and restore the direct 'nix flake check --print-build-logs' step in the lint job. The separate ci shell and explicit install steps were a #1243-era optimisation to avoid paying the shellHook cost in CI; with sticky-disk caching the cost is noise and the extra indirection just made the setup harder to follow. * ci: report what the Nix and node_modules caches restored The sticky disk and cache-nix-action steps only log that a mount or restore happened, so job logs never showed whether the run started warm or cold, or what the restored data actually was. Add report steps after each restore that print the sticky disk key, the mount path, disk usage, and the number of restored Nix store paths or top-level node_modules entries, with an explicit cold-start message when the disk or cache comes back empty. * Revert "ci: report what the Nix and node_modules caches restored" This reverts commit af47cba. * Update nix/dev-shell.nix Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * chore(ci): update github actions * ci: simplify pnpm version resolution for the sticky disk key Read the packageManager pin with jq instead of a node -p invocation and a case statement. The resolved version only feeds the node_modules sticky disk cache key; the pnpm that actually runs self-resolves to the pinned version via manage-package-manager-versions regardless of which binary starts it. * ci: stop caching node_modules on a sticky disk Drop the root node_modules sticky disk and the pnpm version resolution that only existed to build its cache key. A warm disk saved roughly twenty seconds of pnpm install, while mounting cost five to forty-five seconds per job (and occasionally failed outright), every job committed a snapshot even when nothing ran pnpm, and the per-GB-billed disk grew to tens of gigabytes despite node_modules holding about 500 MB of files. Installing from the registry on each run is cheaper and simpler. The Nix store sticky disk stays; that one caches multi-gigabyte build closures that are genuinely expensive to recreate. --------- Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Blacksmith is a new sponsor of ccusage; the repository CI was migrated to Blacksmith runners in #1249. Add the Blacksmith logo to docs/public and show it alongside the CodeRabbit logo in the README sponsor section, the docs home page sponsor block, and the sponsors guide. The logo ships with its own yellow background, so a dark-mode variant is not needed.
* build(ccusage): reject non-portable staged native binaries ensure-native-binary.ts accepted any staged package binary whose version matched, including binaries dynamically linked against libraries that only exist on the build machine. A Nix-built macOS binary linking /nix/store/.../libiconv.2.dylib shipped this way and crashed for users without Nix with a missing dynamic library error. Add an isPortableBinary() gate before accepting the staged binary: Linux binaries must be fully static (ldd, mirroring the release CI check), macOS binaries may only link system dylibs under /usr/lib or /System/Library (otool -L), and Windows is not checked because MSVC builds link only system DLLs by default. A version-matched but non-portable binary now fails loudly instead of being silently accepted or silently replaced by a local cargo build. * fix(nix): fail the darwin build when non-system dylibs remain The darwin package relies on install_name_tool to rewrite the Nix store libiconv reference to /usr/lib/libiconv.2.dylib (#1249), but install_name_tool -change is a silent no-op when the old path does not match, and nothing verified the final binary. A regression (for example a new Nix-linked dylib) would ship unnoticed and crash for users without /nix/store. Assert in postInstall that every linked dylib lives under /usr/lib or /System/Library, printing the offending entries otherwise. The assertion runs on every nix build and via nix flake check, which already builds the ccusage package as a check. Verified both ways on darwin: the current build passes, and removing the install_name_tool line makes the build fail with the offending /nix/store path in the log. * fix(nix): assert the static Linux build has no dynamic loader ccusage-static targets musl specifically so the published Linux binaries run without any dynamic libraries, but the derivation never verified the result; only a separate CI action step did. Check in postInstall that the binary has no PT_INTERP program header, which would mean it requests a dynamic loader and fails on end-user machines. READELF comes from the cross bintools wrapper, with a plain readelf fallback. This complements the existing ldd verification in build-linux-native-package, moving the primary gate into nix build itself so local builds and any future workflows are covered too. * ci: verify the cargo-built macOS x64 binary links only system dylibs The darwin-x64 package is built with plain cargo on a GitHub macOS runner, so the portability assertion added to the Nix derivation does not cover it and no other gate inspects it before staging. Fail the build action if otool -L reports any dylib outside /usr/lib or /System/Library, mirroring the assertion in package.nix. The check logic was exercised locally on macOS: a /usr/lib-linked binary passes and a Nix-store-linked binary fails with the offending path printed.
The Blacksmith migration (#1249) changed the PR Gate trigger from `pull_request_target` to `pull_request`. For PRs opened by outside contributors, `pull_request` workflows are gated as `action_required` and do not run without a maintainer manually approving them, so the auto-close gate silently stopped firing (e.g. #1281 stayed open). Restore `pull_request_target` so the gate runs with the base repo's write permissions and can close unapproved PRs automatically. This workflow never checks out PR code — it only calls the GitHub API via github-script — so the usual pull_request_target injection risk does not apply. Added an inline comment documenting the requirement to prevent the same regression in future workflow edits.
Migrates the GitHub Actions fleet to Blacksmith runners, using larger Blacksmith Linux, macOS ARM, and Windows runners for CI and release preparation.
What changed:
Validation:
Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.Summary by CodeRabbit
Bug Fixes
Chores
Notes: