025 · Release
Continuous integration and releases (spec_release_025)
| Status | Implemented; on Bazel since 2026-09-27 (spec_monorepo_028) |
| Source | .github/workflows/ci.yml, .github/workflows/release.yml, .github/release-footer.md, .github/dependabot.yml, release/BUILD.bazel, apps/electron/packaging/, build/, tools/ |
| Depends on | spec_setup_001, spec_monorepo_028, all feature specs |
Every change is checked on macOS and Linux, including the sandboxes; releases are reproducible, signed and come with SBOMs.
- REL-01 Triggered on push to
mainand pull requests, except documentation-only changes. The runners’ Bazelisk runs the Bazel in.bazelversion;bazel-contrib/setup-bazelcaches Bazelisk, the repository cache and a disk cache per OS, saved only frommain(another branch’s would serve only it, and could evict main’s from GitHub’s 10 GB).release.ymlrestores main’s disk caches (ci-ubuntu-latest,ci-macos-latest) without saving, so a release builds again only what the version stamp changes; ARM Linux, which CI doesn’t build, has none. No other toolchain is installed: Bazel brings Go, Node and the tools. - REL-02
buildjob onubuntu-latestandmacos-latest, every step named:tools/check_format.sh(gofmt, BUILD files current, license headers),bazel test --config=race //...(vet and staticcheck run in every compile, MR-16; on Linux throughtools/coverage.sh, which fails below the coverage floor, spec_release_readiness_030 RR-43),bazel run //tools:check_deps(MR-02), the third-party notices, specs and code owners checks, govulncheck on the builtblitzandblitzd(fails only on a vulnerability the code reaches). - REL-03 Linux installs bubblewrap (since v0.7.0 only that: GTK, WebKitGTK and Xvfb went with the Wails app, and with them the WebKitGTK printing test) and a pinned gVisor (
runsc, checksum-verified). The OS-sandbox enforcement test (TestOSSandboxEnforcement) and gVisor tests (TestGVisor*) must not be skipped — the job fails if their verbose output contains--- SKIP. - REL-04 Protos (Linux): lint is a test (
blitzv1_proto_lint);bazel run //tools:buf -- format --exit-code -d; andbuf breakingagainst the previous commit (full history fetched), unless the commit message declaresBreaking-API: <why>. - REL-05 The desktop app and its page are built and tested by
bazel test //...like everything else. - REL-06
reproduciblejob: the release archives’ checksums (//release:archives) from the Linux and macOS runners must be identical (MR-25). - REL-07 Actions are pinned by commit SHA; Dependabot keeps them current.
-
REL-10 Tagging
v*runs the release on Ubuntu:bazel build --config=release //release:archives(stamped with the tag, MR-20). -
REL-11 Binaries:
blitzandblitzd, pure Go, for darwin/linux × amd64/arm64 and windows/amd64 (MR-21), built on one machine. -
REL-12 Archives
blitz_<version>_<os>_<arch>.tar.gz(.zipon Windows), each holding the folderblitz_<os>_<arch>/withblitz,blitzd,blz(not on Windows), README, LICENSE and NOTICE (MR-22), and on Windowsblitz-tray.exe(spec_windows_036 WIN-35); an SPDX SBOM per archive (syft, pinned);checksums.txt. -
REL-13 The checksum file is signed keylessly with cosign via GitHub OIDC (
id-token: write), bundlechecksums.txt.sigstore.json: since v0.7.0 Sigstore’s standard bundle (--new-bundle-format), whichpkg/selfupdatechecks without cosign; cosign’s older bundle before. Users verify against this repository’s release workflow identity (^https://github.com/retail-cortex/blitz/\.github/workflows/release\.yml@refs/tags/v, issuerhttps://token.actions.githubusercontent.com), thensha256sum --ignore-missing -c checksums.txt. -
REL-14 The release is a draft (
gh release create --draft --verify-tag), with GitHub’s generated notes and the footer in.github/release-footer.md(verifying, the desktop packages, clearing macOS quarantine since the CLI binaries aren’t notarised). -
REL-15 Installing and updating (PAR-MOD-06).
release/install.shis attached to each release (releases/latest/download/install.sh): on macOS and Linux (amd64, arm64) it downloads the archive for the machine,checksums.txtand its bundle for the latest release (BLITZ_VERSIONanother), verifies the bundle with cosign against REL-13’s identity when cosign is installed (a failure installs nothing;BLITZ_REQUIRE_SIGNATURE=1makes cosign a must), checks the archive’s SHA-256, and putsblitzandblitzdinBLITZ_BIN(~/.local/bin) by renaming over them, withblzlinked and macOS’s quarantine flag cleared, then says whether the folder is onPATH.//release:install_testruns it against a fake release. -
REL-16 Homebrew: when a release is published (not a pre-release; or by hand for a tag),
homebrew.ymldownloads itschecksums.txt, verifies it with cosign, writesFormula/blitz.rbwithrelease/homebrew_formula.sh(the four macOS and Linux archives with their checksums; installsblitz,blitzd,blzand the license files; testsblitz --version) and pushes it toretail-cortex/homebrew-tapwith theHOMEBREW_TAP_TOKENsecret, doing nothing without one.//release:homebrew_formula_testchecks the formula. -
REL-17
blitz update(--check,--version vX,--skip-signature): the latest release from GitHub’s API; a newer one (semantic versions; a pre-release is older than its release) is downloaded for the machine,checksums.txt’s signature checked against REL-13’s identity, since v0.7.0 in-process (pkg/selfupdate: sigstore-go, Sigstore’s trust root through its TUF repository, cached in~/.blitz/sigstore; the transparency log entry, an observed time and the certificate’s SCTs required), so cosign isn’t needed; a release before v0.7.0 (cosign’s older bundle,ErrOldBundle) is still checked withcosign verify-blob(cosign not installed: a usage error unless--skip-signature, which still checks the checksum), the archive’s checksum checked, andblitz(the running executable, links resolved) and theblitzdbeside it replaced by renaming new files over them (on Windows the old ones are moved aside). A copy under Homebrew’sCellaror/usris refused with how to update it; a development build needs--version. Afterwards it says to restart the service if it runs.
- REL-20 After the release job, a matrix on
macos-latest,ubuntu-24.04andubuntu-24.04-armbuilds the desktop package with--config=releaseand attaches it to the draft release. - REL-21 macOS: since v0.7.0 the Electron app (spec_electron_038 EL-80):
bazel build //apps/electron/packaging:Blitz_<arm64|x64>.app, thenapps/electron/packaging/sign_macos.shmakesBlitz_<version>_macos_<arm64|x64>.dmg(one per Mac: Electron’s runtimes are separate) with the app and a link to/Applications, signing inside out (Electron’s libraries and helpers, its frameworks, its helper apps withentitlements.plist: JIT, unsigned executable memory, library validation off; the Go programs with their identifiers; the bundle). Before v0.7.0 the Wails app, universal://apps/desktop/packaging:Blitz.appand itssign_macos.sh,Blitz_<version>_macos_universal.dmg. With a signing identity,blitzandblitzdand then the app are signed with the hardened runtime and a secure timestamp (no entitlements), the image is signed, notarised withnotarytool(App Store Connect API key, waiting up to 30 min), stapled and validated, andspctlmust accept it. Without one, the app is signed ad hoc and the job warns. - REL-22 Signing secrets:
MACOS_CERTIFICATE(the Developer ID Application certificate and key as a base64.p12),MACOS_CERTIFICATE_PASSWORD,DESKTOP_SIGN_IDENTITY(Developer ID Application: <name> (<team>)),NOTARY_KEY(the.p8text),NOTARY_KEY_ID,NOTARY_ISSUER. The certificate goes into a throwaway keychain, deleted at the end of the job; withoutMACOS_CERTIFICATEnothing is signed. - REL-23 Linux: since v0.7.0
bazel build //apps/electron/packaging:deb(EL-80; the Wails app’s//apps/desktop/packaging:debbefore), namedblitz-desktop_<version>_<amd64|arm64>.deb: Electron in/usr/lib/blitz-desktop(its program asblitz-desktop,chrome-sandboxsetuid root), the app in itsresources/appwithblitz,blitzdandblitz-tray;Depends: bubblewrap, Electron’s libraries (GTK 3, NSS, ALSA, GBM, libnotify, libsecret, XSS, XTST, xdg-utils, with Ubuntu 24.04’s t64 names). Alsoblitz-desktop_<version>_linux_<arch>.tar.gz, the same files to unpack anywhere, which updates itself. Before v0.7.0:Depends: bubblewrap, libgtk-3-0t64 | libgtk-3-0, libwebkit2gtk-4.1-0. The job installs the package with apt and fails if a library isn’t found,blitzorblitzddoesn’t run, or the link is wrong. The CLI isn’t put onPATH(the CLI archives do that).Maintainer: Retail Cortex <https://github.com/retail-cortex/blitz/issues>, and AppStream metainfo in/usr/share/metainfo/dev.blitz.desktop.metainfo.xml(linux/), whose<developer>(and<developer_name>, for AppStream before 1.0) is Retail Cortex: software centres such as GNOME Software and Ubuntu’s App Center take the author from it, and show “Unknown Author” without it. - REL-24 Each package is signed keylessly with cosign like the checksum file (REL-13), with its own bundle
<file>.sigstore.json(Sigstore’s standard bundle since v0.7.0), verified against the same workflow identity. The packages aren’t inchecksums.txt: the desktop app’s updater checks a package’s own bundle. - REL-25 Windows: since v0.7.0 the desktop app,
Blitz_<version>_windows_x64.zip(//apps/electron/packaging:windows_zip, cross-built in the release job, signed with its own bundle):Blitz.exe(Electron’s program with Blitz’s icon and details, EL-06), the app inresources\app, the licenses. Not signed with Authenticode. The service installs as a login item only on macOS and Linux (launchd, systemd); on Windowsblitz-tray.exestarts the service, starts at login itself (spec_windows_036), and the CLI archive’s tray still serves the page to a browser. - REL-26 Arch Linux:
bazel build //apps/electron/packaging:pacman(since v0.7.0; the Wails app’s//apps/desktop/packaging:pacmanbefore), a.pkg.tar.zstwith the.deb’s programs, link, launcher and icons, and the notices in/usr/share/licenses/blitz-desktop(license = Apache-2.0). rules_pkg has no pacman rule:tools/pacmanwrites the.PKGINFOand.MTREEmakepkg would, owns every entry by root and adds the parent directories, and bazel_lib’s zstd compresses it.depend:bubblewrapand Electron’s libraries (alsa-lib,gtk3,libnotify,libsecret,libxss,libxtst,mesa,nss,xdg-utils; before v0.7.0 the Wails app’sgtk3,webkit2gtk-4.1>=2.40).packageris Retail Cortex, and it carries the.deb’s AppStream metainfo. It’s namedblitz-desktop-<pkgver>-1-<x86_64|aarch64>.pkg.tar.zst, wherepkgveris the version without a pre-release’s-(0.4.0rc1, as Arch writes them, whichvercmporders before0.4.0; with a separator it would come after) and with anything else pacman refuses made.. On x86_64 the job installs it withpacman -Uin anarchlinuxcontainer and fails if a dependency or library isn’t found, a program doesn’t run, the link is wrong,pacman -Qkkfinds a file unlike its.MTREE, or removing it leaves/usr/lib/blitz-desktop. The aarch64 package (Arch Linux ARM) is built but not installed: there’s no arm64archlinuximage.
- The
retail-cortex/homebrew-taprepository and itsHOMEBREW_TAP_TOKENsecret don’t exist yet: until they do, the Homebrew job does nothing. - The first release from
retail-cortex/blitzstill needs verifying against the new identity (MANUAL_VERIFICATION section 18). Releases made before the move (e.g.v0.1.0) and thepython-finaltag live in the former repository,rmcguinness/code_puppy, whosego-release.ymlidentity they verify against. - macOS notarisation of the CLI archives. The desktop image is notarised once the signing secrets (REL-22) are set; the first signed release needs checking (MANUAL_VERIFICATION §18).
- The Arch package (REL-26) on a real Arch desktop: the launcher, the icon and the tray, which the container smoke test can’t show.
- Running opt-in real Python install tests (
BLITZ_PYENV_TESTS=1) in CI. - Neither workflow has run on GitHub since the move to Bazel: the first push and the first tag need watching (MANUAL_VERIFICATION §18).