Templates for publishing knit to package managers. The canonical release flow:
# 0. Everything you want in the release is landed on main; local main is current.
# 1. Bump `version` in Cargo.toml (package `knit-cli`), land that change.
# 2. Tag and push — triggers .github/workflows/release.yml, which builds
# macOS (x64/arm64; both pinned to MACOSX_DEPLOYMENT_TARGET=11.0),
# Linux (x64/arm64 musl), and Windows (x64) binaries and uploads them
# (plus .sha256 files) to a GitHub release. A tag containing `-`
# (e.g. v0.1.0-alpha.14) is marked as a pre-release.
git tag v0.1.0-alpha.14
git push origin v0.1.0-alpha.14
# 3. Select the Release run for this tag and wait for its
# verified-homebrew-tap artifact, NOT for the workflow to finish.
# The release explicitly uses manual tap publication. Download that artifact,
# copy knit.rb unchanged into the tap's Formula/knit.rb in a Knit checkout,
# and publish + land the tap PR through Knit after tap CI passes.
# Complete this while the release waits (30 minutes maximum).
# 4. Watch that same Release run to completion. It succeeds only after the live
# tap's default-branch formula matches the verified bytes exactly and all
# other release jobs pass. An open tap PR is not completion.
gh run watch --repo knit-cli/knit RELEASE_RUN_ID --exit-status
# crates.io is deliberately not part of this flow: `knit-cli` there stops at
# the earliest alphas, and Homebrew plus source are the supported paths.Release runs serialize per tag without cancelling an active run. Before building
any matrix target, scripts/release_assets.py downloads its existing archive and
checksum. A complete, checksum-valid pair is reused without rebuilding. An
inconsistent complete pair fails immediately.
The Rust binary action runs in its supported dry-run mode (build and compress
only). Its normal upload path uses --clobber and is deliberately not used.
Our publisher validates the local archive/checksum pair, compares every existing
member's SHA-256 before uploading anything, and uploads only missing members,
without overwrite. It downloads the completed pair again to verify exact bytes.
A competing upload fails safely rather than replacing another publisher's bytes.
An interrupted upload may leave a partial pair. Recovery accepts only the same
bytes for every already-published member; if a rebuild differs (including archive
metadata), the run fails without replacing assets. Retry publication with the
original archive/checksum from the failing attempt's
raw-release-<target>-<attempt> Actions artifact using
scripts/release_assets.py publish --repo knit-cli/knit --tag <tag> --target <target> --directory <download-directory>, or
investigate the partial state; do not delete assets, use --clobber, or move the
tag to make a rerun green. Completed targets are never rebuilt on rerun.
The action's official metadata
documents build-only dry-run; its upload implementation
explains why publication is handled separately.
Users install with brew install knit-cli/tap/knit and get a real bottle
(cellar: :any_skip_relocation; macOS tagged arm64_big_sur/big_sur,
Linux tagged arm64_linux/x86_64_linux) instead of a source build that
triggers Xcode checks.
The formula is generated, never hand-edited. scripts/homebrew_bottles.py
reads the four raw release archives, verifies each .sha256 sidecar before
extracting anything, and writes bottles/*.bottle.tar.gz, the complete tap
formula knit.rb, SHA256SUMS, and manifest.json.
The tag release explicitly selects manual tap publication; no cross-repository write credential is needed. While the workflow is waiting, download the verified handoff from the exact release run:
gh run download RELEASE_RUN_ID --repo knit-cli/knit \
--name verified-homebrew-tap --dir verified-homebrew-tapCheck verification.json for the intended release tag, source SHA, run ID/attempt,
and formula SHA-256. Copy knit.rb unchanged to Formula/knit.rb in the tap's Knit
checkout, publish the PR through Knit, and land after tap CI passes. Do this
before waiting for the release to finish: the workflow polls for the exact
live formula every 20 seconds and fails after 30 minutes without a match.
Standalone/backfill calls default to automatic mode: they require
HOMEBREW_TAP_TOKEN before uploading bottles unless the exact formula is already
live. Automatic mode opens a PR and still waits for its merge; it never silently
falls back to manual. The tag release also checks credentials before creating
release assets if its explicit policy is changed to automatic.
To backfill or recover a timeout without rebuilding binaries or moving tags, select manual publication explicitly and follow the same handoff/merge sequence:
gh workflow run "Homebrew bottles" --repo knit-cli/knit \
-f release_tag=v0.1.0-alpha.21 -f tap_publish_mode=manualUse the same packaging implementation when recovering an existing release. Reruns replace only the run's Actions artifacts; existing GitHub release assets must have identical bytes and are never clobbered. A completed live tap can be verified again without a publishing token. See Homebrew release completion for mode contracts, handoff metadata, and recovery details.
| File | Destination | How |
|---|---|---|
generated homebrew-dist/knit.rb |
knit-cli/homebrew-tap repo as Formula/knit.rb |
Verified handoff via Knit PR (automatic mode opens a PR); users run brew install knit-cli/tap/knit |
scoop/knit.json |
marc-merino/scoop-knit repo as bucket/knit.json |
Push to the bucket repo, users run scoop bucket add marc-merino/knit <url> && scoop install knit |
winget/marc-merino.knit.yaml |
PR to microsoft/winget-pkgs as manifests/m/marc-merino/knit/<version>/marc-merino.knit.yaml |
Submit PR, Microsoft reviews and merges |
- Bump
versioninCargo.toml,crates/knit-runtime/Cargo.toml, and theknit-runtimedependency entry; land it - Tag
v<version>and push; wait for the verified handoff artifact - Homebrew: publish and land the verified tap formula through Knit while the Release workflow waits; then confirm the full Release run succeeds
- Scoop: bump version + hash (
autoupdatehandles URLs) - Winget: submit a new manifest for the new version
There is deliberately no cargo publish step; see the note in the release flow
above.
.github/workflows/linux-packages.yml repackages the verified Linux release
binaries into .deb and .rpm files using nFPM 2.47.0. No Rust build is needed.
The packages install /usr/bin/knit and depend on Git 2.31+ and CA certificates.
Prerelease versions sort before their eventual stable versions. Both amd64 and
arm64 installs are tested through signed APT on Ubuntu 22.04 and 24.04 before
publication. A changed repository index must fail authentication.
The release workflow calls this automatically after the binaries are uploaded. To backfill an existing release after this workflow is on the default branch:
gh workflow run linux-packages.yml --repo knit-cli/knit -f release_tag=v0.1.0-alpha.22Each run uploads immutable .deb, .rpm, and Linux-specific checksum assets
to the existing GitHub release. Repeating a run accepts identical bytes and
refuses replacements. It then adds packages to the existing gh-pages branch,
regenerates and signs APT metadata, pushes without force, and explicitly requests
a Pages build. Old packages remain available; backfilling an older version does
not downgrade the latest version APT selects. Concurrent publications serialize.
GitHub Pages serves https://knit-cli.github.io/knit/; this branch is reserved
for the package site. RPM files support local dnf install; no DNF repository
is configured by this workflow.
- Generate a dedicated OpenPGP signing key in a private directory. Keep an offline backup. Use an unencrypted automation key; GitHub encrypts the secret at rest and only the publication job imports it into an ephemeral keyring.
- Save its armored private export as the repository Actions secret
APT_SIGNING_KEY. Never commit this export or place it in a release artifact. - Bootstrap
gh-pageswith the signed site generated below, commit and push it, and enable GitHub Pages using Deploy from a branch → gh-pages → / (root). The workflow'sGITHUB_TOKENneedscontents: writeandpages: write. - Confirm the live signing key and install using the commands in the root README.
The initial public signing-key fingerprint is
873E5910AB55CBA214E5F7F2258C3BB163EC0B9A (expires September 21, 2029).
The builder refuses accidental signing-key changes once a public key exists.
Key rotation requires a deliberate client migration, not replacing the secret.
GitHub Pages is dedicated to packages for this repository; do not point the
publisher at an unrelated documentation site.
Install nFPM 2.47.0, Python 3, dpkg-dev, apt-utils, and GnuPG on Linux.
Download the four Linux musl archives/checksum sidecars from the chosen release:
gh release download v0.1.0-alpha.22 --repo knit-cli/knit --dir assets \
--pattern '*unknown-linux-musl.tar.gz' --pattern '*unknown-linux-musl.sha256'
python3 scripts/linux_packages.py --version v0.1.0-alpha.22 --assets-dir assets --output-dir linux-dist
python3 -m unittest discover -s scripts -p '*_test.py' -v
docker run --rm -v "$PWD:/src:ro" ubuntu:22.04 \
bash /src/scripts/linux_install_smoke.sh /src/linux-distTo prepare the public site with your existing signing key:
bash scripts/build_apt_repository.sh linux-dist site YOUR_FULL_SIGNING_FINGERPRINT
cp dist/linux-index.html site/index.htmlGNUPGHOME must point to the private signing keyring. site may be a checkout
of the existing gh-pages branch, which retains previously published packages.
The smoke script generates a disposable test key and modifies APT configuration:
run it only in a disposable container, never directly on your workstation.