Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Package management#1

Merged
johnpmitsch merged 7 commits into
mainquicknode/cli:mainfrom
package_managementquicknode/cli:package_managementCopy head branch name to clipboard
Jun 4, 2026
Merged

Package management#1
johnpmitsch merged 7 commits into
mainquicknode/cli:mainfrom
package_managementquicknode/cli:package_managementCopy head branch name to clipboard

Conversation

@johnpmitsch
Copy link
Copy Markdown
Collaborator

No description provided.

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.
@johnpmitsch johnpmitsch merged commit c7b3726 into main Jun 4, 2026
1 check passed
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

Morty Proxy This is a proxified and sanitized view of the page, visit original site.