Package management#1
Merged
Merged
Conversation
The repository field said quicknode/qn, but the git remote (and the canonical GitHub URL for releases, tap repos, etc.) is quicknode/cli. Fix the mismatch before adding release tooling that reads this field.
`dist init` generated a tag-triggered workflow that cross-compiles to seven targets (Linux gnu/musl x amd64/arm64, macOS x amd64/arm64, Windows x86_64), produces .tar.xz/.zip archives with sha256 sidecars and SLSA build attestations, and uploads everything to a GitHub Release. It also generates shell + powershell installers and a Homebrew formula that gets pushed to quicknode/homebrew-tap. Phase 0 of the packaging plan. Subsequent phases will layer crates.io, Docker/GHCR, AUR, Scoop, .deb, and COPR jobs on top of this. The HOMEBREW_TAP_TOKEN secret + tap repo are deferred to Phase 3 — the workflow will simply fail the homebrew step until then, but the rest runs.
The cargo-dist template hardcodes "axo bot" <admin+bot@axo.dev> as the git committer for Homebrew formula updates pushed to the tap repo. Use "Quicknode Release Bot" <release-bot@users.noreply.github.com> instead so the tap repo's commit history is branded correctly. The noreply.github.com domain is GitHub's standard non-routable address for bot identities, so it doesn't promise an inbox we don't own. Also lock in the Quicknode brand capitalization (capital Q, lowercase n, never "QuickNode") in CLAUDE.md so this kind of slip doesn't recur in generated config. This override will get clobbered the next time we run `dist generate`. The Justfile recipe coming in Phase 0c re-applies the override post-generation.
Mirrors the shape of qn/sdk's Justfile: local-dev recipes (test, lint,
fmt-check, fmt), a `dist-regen` recipe that re-applies our Quicknode
bot-identity override after running `dist generate`, and a Phase 1
release flow with staged confirmation checkpoints.
The release flow:
release-prepare VERSION
→ release-bump VERSION (sed-bump Cargo.toml, commit on
release/vX.Y.Z; refresh Cargo.lock)
→ release-open-pr VERSION (gh pr create)
→ release-merge-pr VERSION (gh pr merge --squash, poll for MERGED)
→ release-tag-main VERSION (tag + push — triggers release.yml)
→ release-create-tag VERSION (gh release create --generate-notes)
→ release-wait-ci VERSION (gh run watch)
Same safety checks as the SDK: clean tree, must be on main, no leading
`v`, semver-shape regex, prompts before push and before squash-merge.
Each recipe is also callable standalone for retry/recovery.
Phase 1 (crates.io publish recipes) and the eventual release-publish
orchestrator land in a follow-up commit.
The `qn` name is already claimed on crates.io by an unrelated quantum computing library (marek-miller/qn, "Non-local qubits"), so we can't publish under that name. Rename the package to `quicknode-cli` — this parallels the sister crate `quicknode-sdk` on crates.io. User-facing surface is unchanged: the installed binary stays `qn` (the `[[bin]]` section is what determines the binary name on PATH, not [package].name), and the Homebrew formula is also `qn` thanks to the new `formula = "qn"` override in dist-workspace.toml. So `cargo install quicknode-cli` installs `qn`, and `brew install quicknode/tap/qn` works as designed. Only the cargo install command changes. Also revert the cargo-dist bot identity override: cargo-dist's `plan` step does an internal "generated workflow is up to date" check and hard-fails CI on any post-generation edit. The Quicknode-branded bot strings caused exit 255 from `dist plan`. The "axo bot" attribution appears only on commits in the homebrew-tap repo's history (not on any end-user surface), so the cost of leaving it is low. Simplify the `dist-regen` Justfile recipe accordingly — there's no override to re-apply anymore.
cargo-dist doesn't ship a built-in cargo/crates publish job, so we use
its user_publish_jobs mechanism: a reusable workflow at
.github/workflows/publish-crates.yml gets called by a custom-publish-crates
job that cargo-dist generates into release.yml.
The wiring puts crates.io publish in the right place in the orchestration:
plan → build → host (create GH release + upload artifacts)
→ publish-homebrew-formula
→ custom-publish-crates ← runs cargo publish here
→ announce
Both publish jobs gate on `!is_prerelease || publish_prereleases`, so
release candidates skip crates.io until we decide to ship them there.
The reusable workflow also asserts the tag version matches the Cargo.toml
version before publishing, so a tag-without-bump (or vice versa) fails
loudly rather than uploading the wrong version to crates.io.
Justfile gains `release-cargo-publish-check` (dry-run) and
`release-cargo-publish` (manual recovery only — CI normally owns this).
Requires CARGO_REGISTRY_TOKEN repo secret before the first non-rc tag.
Adds: * Dockerfile — single-stage, FROM distroless/static-debian12:nonroot, COPYs the prebuilt musl binary at /qn. No in-image compilation; the SLSA-attested binary that ships in the GitHub release IS what ships in the image. Distroless gives us a CA bundle (for any future HTTPS-calling qn feature) without a shell or package manager. * publish-docker.yml — reusable workflow invoked by cargo-dist as a user_publish_job. Downloads both musl tarballs from the GitHub release, stages per-arch build contexts, pushes single-arch images, then stitches them into a multi-arch manifest at the version tag. Promotes to :latest only for non-prerelease releases. * dist-workspace.toml — wires ./publish-docker into publish-jobs. The image is published private — the package's visibility needs to be flipped to private once after the first push (GHCR defaults to inherit- from-repo, which is public for a public repo). Pulls then require `docker login ghcr.io` with a PAT scoped to read:packages. End-to-end-tested the Dockerfile locally with a static musl-style binary: builds clean, runs under the nonroot user, exits 0.
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.