Releasing HMS¶
One tag produces every artefact, a provenance attestation and an SBOM for each of them, and (after approval) the publishes. Nothing publishes from a branch, a pull request or a manual run.
| Tag | Workflow | Produces |
|---|---|---|
vX.Y.Z | .github/workflows/release.yml | the crate, 8 npm native packages and the root npm package, Python wheels and sdist (as release assets), SBOMs, SLSA provenance, a draft GitHub release; then publishes the crate and the npm packages |
py-vX.Y.Z | .github/workflows/publish-pypi.yml | the same wheels and sdist plus holographic-sdk, published to PyPI through Trusted Publishing |
Push both tags for a full release (the wheels build twice; PyPI trusted publishing is bound to publish-pypi.yml, so it cannot move into release.yml).
What is built¶
- Python (
python-wheels.yml): abi3 (cp39-abi3) wheels for manylinux2014 x86_64 and aarch64, musllinux_1_2 x86_64 and aarch64, macOS arm64 and x86_64, Windows x64, and the sdist. Each wheel is installed into an empty virtualenv with no source tree and no network, then a real associative search and a resonator factorization run through it (scripts/ci/smoke_python.py). Linux wheels are additionally installed in a Debian 11 (glibc 2.31, Python 3.9) or Alpine container. - npm (
npm-packages.yml):holographic-memory-{darwin-arm64, darwin-x64, linux-arm64-gnu, linux-arm64-musl, linux-x64-gnu, linux-x64-musl, win32-arm64-msvc, win32-x64-msvc}plus the JavaScript-only rootholographic-memory, whoseoptionalDependenciesselect the binding by os, cpu and libc. glibc bindings are linked against glibc 2.17, musl bindings with zig. The tarballs are installed into an empty project on a machine of every target (Alpine containers for musl, Debian 11 for glibc) with the root's optional dependencies pointed at the local tarballs, so npm itself must pick exactly one package, and a document search must succeed (scripts/ci/smoke-node.cjs). The rootpackage.json(napi.targets) is the single list of platforms andscripts/ci/npm-platform-packages.cjsrefuses to pack if it disagrees withoptionalDependenciesor any binding is missing. - Crate:
cargo package --locked, which also builds the packaged tarball. - SBOM: a CycloneDX 1.5 JSON per artefact (
cargo-cyclonedx,npm sbom), named<artefact>.cdx.jsonand paired byscripts/ci/pair-sboms.py, which refuses a release where any artefact lacks one. - Provenance:
actions/attest-build-provenancesigns a SLSA provenance statement for every artefact and SBOM, andactions/attest-sbombinds each SBOM to its artefact. The bundle is also a release asset (provenance.intoto.jsonl).
One-time setup (repository settings, by the maintainer)¶
These cannot be committed; the publish jobs wait on them.
- Environments (Settings, Environments), each with required reviewers:
release,crates-io,npm,pypi. - crates.io: add this repository's
release.ymland environmentcrates-ioas a Trusted Publisher forholographic-memory. The job usesrust-lang/crates-io-auth-actionand holds no token. - npm: put an automation token for the
holographic-memoryandholographic-memory-*names in thenpmenvironment asNPM_TOKEN. The first publish of the seven new platform package names needs a token; afterwards configure Trusted Publishing for every package on npmjs.com and delete the secret. Publishing uses--provenance. - PyPI: the trusted publisher is bound to
publish-pypi.ymland environmentpypi. Keep both.
Cut a release¶
- Version.
uv run python scripts/ci/bump-version.py X.Y.Zsets Cargo.toml, Cargo.lock, package.json with itsoptionalDependencies, and package-lock.json;scripts/ci/check-versions.pyruns in every artefact job and again against the tag, so a mismatch fails the build. - Changelog.
git cliff -c cliff.toml -o CHANGELOG.md, committed aschore: update changelog. Tag the commit before any[skip ci]commit (those run no workflows). - Dry run. Merge to
main, then run Actions, Release, Run workflow onmain, and runpublish-pypithe same way. Both build, smoke-test, SBOM and attest everything and skip the publish jobs. Do not tag until both are green. TheReproducible buildworkflow must also be green on that commit. - Tag and push (signed):
git tag -s vX.Y.Z -m vX.Y.Z && git push origin vX.Y.Z, and the same forpy-vX.Y.Z. Approve therelease,crates-io,npmandpypienvironments when asked. - GitHub release.
release.ymlleaves a draft release holding every artefact, SBOM,SHA256SUMSand the provenance bundle. Write the notes, then publish it. The data releases (evidence-v*) use other tags and never trigger these workflows. - npm un-deprecation (required while the package carries the old deprecation; the previous
0.3.0was deprecated):
npm view holographic-memory deprecated # the message, if any, that npm shows today
npm deprecate "holographic-memory@*" "" # an empty message removes the deprecation
npm view holographic-memory@X.Y.Z deprecated # must print nothing
npm dist-tag ls holographic-memory # latest must be X.Y.Z
Run it after the npm publish job is green, as an owner of the package (npm login, 2FA on). Then confirm the registry page shows no deprecation banner and npm install holographic-memory resolves to X.Y.Z. The verify-published job already installs the published packages and runs a search; this step is only about the deprecation flag. 7. Verify what was shipped (see below).
Verify an artefact¶
gh attestation verify holographic_vsa-X.Y.Z-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl \
--repo writerslogic/holographic-memory
gh attestation verify holographic-memory-linux-x64-gnu-X.Y.Z.tgz --repo writerslogic/holographic-memory
npm audit signatures # in a project that depends on holographic-memory
sha256sum -c SHA256SUMS # next to the downloaded release assets
Reproducible builds¶
.github/workflows/reproducible.yml builds hms-eval, hms-server, the napi cdylib, the Python wheel and the crate on two independent runners from different checkout paths and fails unless the SHA-256 of every artefact is identical (scripts/ci/repro-build.sh, scripts/ci/compare-hashes.py). Inputs that are pinned: the committed Cargo.lock (--locked), SOURCE_DATE_EPOCH (the commit time), maturin, no incremental compilation, and --remap-path-prefix for the checkout, cargo home and rustup home. The Rust toolchain is the runner's current stable, so both legs must run in the same workflow run.
Result of the first runs (two runners, different checkout directories): hms-eval, hms-server, the napi cdylib and the crate are bit-for-bit identical. The Python wheel is identical member for member except *.dist-info/sboms/* (and RECORD, which lists its hash), the SBOM maturin embeds, whose bom-refs contain the absolute checkout directory; the workflow therefore allows the wheel hash to differ and fails if any other member differs.
| Artefact | Reproducible | Reason |
|---|---|---|
hms-eval, hms-server, napi cdylib, .crate | yes | |
| Python wheel (manylinux x86_64, built natively) | all members except the embedded SBOM | absolute checkout path in the SBOM bom-refs |
Not covered: the per-platform npm bindings and the cross-compiled wheels, which depend on the zig and napi cross toolchains downloaded at build time (their SHA-256 are in SHA256SUMS and the attestations, not compared).
If something goes wrong¶
- A publish job failed before publishing: fix and re-run the failed job; every publish is idempotent (
cargo publishandnpm publishrefuse a version that exists, PyPI usesskip-existing). - Published a bad version: crates.io
cargo yank --version X.Y.Z; npmnpm deprecate holographic-memory@X.Y.Z "reason"(unpublish only within 72 hours); PyPI yank in the web UI (no API). Then release X.Y.(Z+1); never re-tag. - A tag pushed by mistake: delete the draft release first, then the tag, before approving any environment.