Skip to content

ci(release): publish a signed packslip with each release - #1379

Merged
jdx merged 1 commit into
mainfrom
claude/packslip-release
Sep 5, 2026
Merged

ci(release): publish a signed packslip with each release#1379
jdx merged 1 commit into
mainfrom
claude/packslip-release

Conversation

@jdx

@jdx jdx commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Part of adopting packslip across the jdx.dev CLIs.

Each release now publishes packslip.sigstore.json beside the archives — one signed document listing:

  • every archive's sha256 and sha512
  • the executable inside each one (usage, usage.exe on Windows) and the usage.1 man page beside it
  • the shared objects each build needs from the host, read out of the binaries — vcruntime140.dll on Windows, nothing on Linux or macOS
  • a build-provenance attestation per file, linked by digest
  • the source repo, tag, and commit

signed keylessly with this workflow's OIDC identity, so an installer verifies a download against the identity github.com/jdx/usage instead of a signing key this project would have to hold and rotate.

usage --usage-spec is run against the freshly built Linux binary and uploaded as usage.usage.kdl, listed as a cli-spec resource. usage is the tool that reads these specs, so publishing its own is the example the format asks for.

Changes

  • A new packslip job, needing build-and-publish. It needs to be its own job: build-and-publish uploads straight to the release from each runner, so no step there sees the whole release, and one document covering all of it can only be written once every target has landed. It downloads the release's own archives back, generates the CLI spec, and writes the bundle.
  • release now also needs packslip, so the draft is complete before it is published. Both jobs carry the same startsWith(github.ref, 'refs/tags/v') guard, so the dispatch path is unchanged.

Verification

Rehearsed end to end against the real v6.6.1 release: downloaded all seven assets, ran usage --usage-spec from the linux-gnu build (456 lines), and created a bundle.

usage-aarch64-unknown-linux-gnu.tar.gz  | linux  aarch64 gnu  tar.gz | ['usage']
usage-aarch64-unknown-linux-musl.tar.gz | linux  aarch64 musl tar.gz | ['usage']
usage-universal-apple-darwin.tar.gz     | darwin  -       -   tar.gz | ['usage']
usage-x86_64-unknown-linux-gnu.tar.gz   | linux  x86_64  gnu  tar.gz | ['usage']
usage-x86_64-unknown-linux-musl.tar.gz  | linux  x86_64  musl tar.gz | ['usage']
usage-aarch64-pc-windows-msvc.zip       | windows aarch64  -  zip    | ['usage.exe']
usage-x86_64-pc-windows-msvc.zip        | windows x86_64   -  zip    | ['usage.exe']

The universal macOS build correctly comes out with no arch, so it matches either one. zizmor --offline reports no findings on the changed workflow.

🤖 Generated with Claude Code


Note

Medium Risk
Changes release gating and adds OIDC/attestation permissions plus third-party release uploads; a packslip failure would block publishing the release.

Overview
Adds a packslip job to publish-cli.yml that runs on version tags after build-and-publish, because matrix runners upload assets independently and only a follow-up job can see the full release.

That job re-downloads all usage-*.tar.gz / usage-*.zip assets, extracts the Linux GNU binary to emit usage.usage.kdl via usage --usage-spec and uploads it to the release, then runs jdx/packslip@v0.3.0 to publish a keyless OIDC-signed inventory (packslip.sigstore.json) covering archive digests, the usage binary, bundled usage.1, and the CLI spec as a cli-spec resource.

The final release job now needs: packslip as well as enhance-release, so the draft is not marked published until the packslip (and spec asset) are on the release.

Reviewed by Cursor Bugbot for commit 12793a7. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features
    • Releases now include a CLI usage specification for easier tooling and integration.
    • Published release assets now include a signed inventory covering archives, manual pages, and CLI specifications.
  • Chores
    • Release publishing now verifies that all generated release resources are available before completion.

The release now carries `packslip.sigstore.json`: one signed document
listing every archive's sha256 and sha512, the executable and man page
inside it, the shared objects it loads from the host, and the build
provenance of each file, signed keylessly with this workflow's OIDC
identity. An installer verifies a download against the identity
`github.com/jdx/usage` rather than against a signing key this project
would have to hold and rotate.

It needs a job of its own. build-and-publish uploads straight to the
release from each runner, so no step there sees the whole release; one
document covering all of it can only be written once every target has
landed. `release` waits on it, so the draft is complete before it is
published.

`usage --usage-spec` runs against the freshly built Linux binary and its
output is uploaded as `usage.usage.kdl`, listed as a `cli-spec`
resource: usage is the tool that reads these specs, so publishing its
own is the example the format asks for.

See https://packslip.dev.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Team

Run ID: a63d658a-5312-4ee8-bc5f-6d8a6d13c78e

📥 Commits

Reviewing files that changed from the base of the PR and between 5301982 and 12793a7.

📒 Files selected for processing (1)
  • .github/workflows/publish-cli.yml

Included review availability: Your plan provides up to 4 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

The workflow adds a tag-only packslip job. It generates and uploads the CLI specification, publishes signed release resources, and makes the final release job wait for packslip.

Changes

CLI release publishing

Layer / File(s) Summary
Packslip resource generation
.github/workflows/publish-cli.yml
The tag-only packslip job downloads release archives, extracts the Linux binary, generates usage.usage.kdl, uploads the specification, and publishes signed archive, man-page, and CLI-spec resources.
Release publication gate
.github/workflows/publish-cli.yml
The release job now depends on packslip, build-and-publish, and enhance-release.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 12793

The release workflow now publishes signed inventory and CLI-spec assets before publication, with no substantiated merge-blocking issue remaining.

Sequence Diagram(s)

sequenceDiagram
  participant GitHubActions
  participant ReleaseArchives
  participant GitHubRelease
  participant Packslip
  GitHubActions->>ReleaseArchives: Download release archives
  GitHubActions->>GitHubRelease: Upload generated CLI specification
  GitHubActions->>Packslip: Publish signed release resources
  Packslip-->>GitHubActions: Complete packslip job
  GitHubActions->>GitHubRelease: Run final release publication
Loading

Poem

A rabbit reads each line,
The patch grows clear beneath the moon,
Small changes hop in place,
Tests guard the garden path,
Reviews bloom before the dawn.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: publishing a signed packslip with each release.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
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.

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.

@jdx
jdx merged commit c2f1306 into main Sep 5, 2026
15 of 18 checks passed
@jdx
jdx deleted the claude/packslip-release branch September 5, 2026 02:57
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Instruction counts

benchmark trend instructions Δ wall (min) Δ
markdown ▁▁▁███ 386,439,542 → 386,425,738 -0.00% 64.43 → 64.62ms +0.31%
startup ▁▁▁▇▃█ 952,527 → 959,539 +0.74% 1.17 → 1.17ms +0.58%

No instruction-count regression above 1%.

Only instruction counts gate. Wall clock is shown for context — on identical hardware it moves 4-20% run to run.

Measured by tak — instruction-counted CLI benchmarks, stored in this repository's git notes.

Shadow comparison

Parsing mise use -g node@20 against a shadow of mise's committed spec.
Reported, not gated: the shadow grows as the derive learns to express more, so
what to watch is the ratio rather than either column.

framework stripped binary, bytes
usage 1344744
bpaf 2493216
clap 3101656
framework instructions, cold parse vs usage
usage 8374
clap 6314655 754x
bpaf 21909025 2616x
                                              min       p01       p10    median
usage-rs: argv -> struct                     1193      1223      1241      1251  ns
clap: build tree + parse -> struct        1791610   1794625   1802149   1813534  ns
bpaf: build parser + parse -> struct      5713564   5713564   5739848   5769016  ns

usage: argv -> struct                            1170 ns      1.17 µs
clap: build tree + parse -> struct            1821005 ns   1821.00 µs
clap: parse -> struct, tree reused              72043 ns     72.04 µs
clap: build tree only                         1050492 ns   1050.49 µs

12793a712d6f vs 5301982371ba · measured on the runner, not pushed to the history.

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.

1 participant