The end-to-end playbook for cutting a release of prosa. If you are not the maintainer cutting this release, you don't need this file.
just ci # all lights green locally
just snapshot # dry-run goreleaser into dist/
just docker-build # check the three images build for your host arch
./bin/prosa --version # sanity check
git tag v0.15.2
git push --tagsThe v* tag triggers .github/workflows/release.yml, which orchestrates
GoReleaser, the npm publish script, and the Docker push.
Run from a clean checkout of master:
-
just ci— the full local pipeline. Must be green. This covers:just tidy-check—go.mod/go.sumalready tidy.just gen-check— no diff afterbuf generate.just vet—go vetclean.just lint—golangci-lintclean.just test-race— full race-detector test suite.just build— all three binaries build.git diff --exit-code— no uncommitted regen.
-
just snapshot— local GoReleaser dry-run.- Builds the full matrix into
dist/. - Renders archives + checksums.
- Does not push to the Homebrew tap or GitHub.
- Use to confirm there are no GoReleaser config errors before the real tag.
- Builds the full matrix into
-
just docker-build— Dockerfile builds for the host arch, producingprosa:local,prosa-server:local, andprosa-panel:local.docker run --rm prosa:local --versionshould print the same version/commit metadata as./bin/prosa --version. The other two images carry their named binaries.
-
./bin/prosa --version— confirms the buildinfo ldflags injectedVersion,Commit,BuildDate. -
Cross-check
ROADMAP.md— is this the release described under Next? Either way, ROADMAP gets updated as a follow-up PR after the release. -
Look at the changelog GoReleaser will publish:
git log $(git describe --tags --abbrev=0)..HEAD --onelineGoReleaser groups by
feat:/fix:and excludesdocs:/test:/chore:/ci:and merge commits — be sure the user-facing changes are conventional-commit-shaped.
prosa follows semver. Pre-releases use -rc, -beta, etc.; GoReleaser is
configured with prerelease: auto.
git tag v0.15.2 # or v0.15.2-rc1, v0.15.2-beta1, etc.
git push --tagsThat's the only manual step.
GitHub Actions runs .github/workflows/release.yml. One job, multi-step:
goreleaser release --clean runs in the Actions runner.
- Builds the 12 binaries (3 binaries × 2 OS × 2 arch).
- Publishes one tar.gz per binary × OS × arch
(
prosa_<v>_<os>_<arch>.tar.gz,prosa-server_<v>_<os>_<arch>.tar.gz,prosa-panel_<v>_<os>_<arch>.tar.gz) = 12 total. - Computes
checksums.txtcovering every asset. - Renders the GitHub Release (title =
prosa v0.15.2, body = grouped changelog). - Pushes
Casks/prosa.rb,Casks/prosa-server.rbandCasks/prosa-panel.rbtoc3-oss/homebrew-prosausingHOMEBREW_TAP_TOKEN. Each cask is pinned to its own binary archive (ids: [prosa],ids: [prosa-server],ids: [prosa-panel]), so users install separately:brew install c3-oss/prosa/prosa,brew install c3-oss/prosa/prosa-server, andbrew install c3-oss/prosa/prosa-panel.
Secrets used: GITHUB_TOKEN (auto), HOMEBREW_TAP_TOKEN (manual,
6-month rotation).
scripts/publish-npm.sh runs after GoReleaser.
- Reads
GITHUB_REF_NAME(e.g.v0.15.2), stripsv→0.15.2. - Stamps
0.15.2into all fivenpm/*/package.jsonfiles (and into the metapackage'soptionalDependencies). - Copies binaries from
dist/prosa_<os>_<arch>/prosaintonpm/prosa-<platform>/bin/prosa. - Verifies all five versions match (aborts otherwise).
- Publishes the four sub-packages first, then
@c3-oss/prosa.
Auth: npm OIDC trusted publishing — the job's id-token: write is exchanged
for npm credentials (no stored token).
QEMU + Buildx multi-arch build and push. Three images, one per binary,
share the QEMU/Buildx setup and GHCR login. Each image is published with
the release tag and latest, for linux/amd64 and linux/arm64:
ghcr.io/c3-oss/prosa(CLI)ghcr.io/c3-oss/prosa-serverghcr.io/c3-oss/prosa-panel
The Dockerfile is multi-target (one named final stage per binary). The
shared build stage is cached by Buildx so all three targets reuse the
same compiled binaries.
Secret used: GITHUB_TOKEN (auto).
A few minutes after the workflow goes green:
# Homebrew (installs one cask per binary)
brew update && brew install c3-oss/prosa/prosa # CLI only
brew install c3-oss/prosa/prosa-server # API server only
brew install c3-oss/prosa/prosa-panel # web UI only
# install.sh — interactive prompt picks CLI / all / server / panel
PROSA_VERSION=v0.15.2 \
curl -fsSL https://raw.githubusercontent.com/c3-oss/prosa/master/install.sh | sh
~/.local/bin/prosa --version
# Direct tarball install (or use `install.sh` and set INSTALL_BINS):
mkdir -p /tmp/prosa-install && cd /tmp/prosa-install
curl -fsSL -o prosa-server_0.15.2_darwin_arm64.tar.gz \
https://github.com/c3-oss/prosa/releases/download/v0.15.2/prosa-server_0.15.2_darwin_arm64.tar.gz
tar -xzf prosa-server_0.15.2_darwin_arm64.tar.gz prosa-server
install -m 0755 prosa-server ~/.local/bin/prosa-server
# npm
npm install -g @c3-oss/prosa@0.15.2
prosa --version
# Docker — three images, one per binary
docker pull ghcr.io/c3-oss/prosa:v0.15.2
docker pull ghcr.io/c3-oss/prosa-server:v0.15.2
docker pull ghcr.io/c3-oss/prosa-panel:v0.15.2
docker run --rm ghcr.io/c3-oss/prosa:v0.15.2 --version
docker run --rm ghcr.io/c3-oss/prosa-server:v0.15.2 --version
docker run --rm ghcr.io/c3-oss/prosa-panel:v0.15.2 --versionEach should print the same version, commit, and build date as the GitHub Release.
The most common cause is an expired HOMEBREW_TAP_TOKEN. Symptom: the
GitHub Release succeeds, but the brew step errors with a 401. Fix:
rotate the token (see homebrew.md),
re-run the workflow job.
Other GoReleaser failures usually surface in the action logs as YAML
schema errors. Reproduce with just snapshot locally; iterate.
Possible causes:
- Version drift between sub-packages:
publish-npm.shaborts before any publish. Fix the script or the artifacts and re-run the job. - Trusted publisher not configured / OIDC exchange failed: confirm each
package has a trusted publisher for
c3-oss/prosa+release.yml, and the job hasid-token: writeon a GitHub-hosted runner; re-run. - One sub-package failed mid-loop: the script exits non-zero; later
sub-packages and the metapackage don't publish. Re-run the job —
npm publishrejects re-publishing the same version, but the script can detect already-published versions and skip them (or you can run the remainingnpm publishcommands manually from a checkout).
After fixing, re-run the workflow on the existing tag. GoReleaser is configured to be idempotent within reason (the GitHub Release isn't re-created if one exists for the tag).
Usually transient (GHCR rate limit, QEMU emulation flake). Re-run the job.
For a fix you want to ship immediately:
- Make the change on
master(or branch + PR + merge). - Tag
vX.Y.Z+1(semver patch). - Push tags.
There is no separate release branch in this MVP. Every release tag points
to a commit on master.
After all four channels confirm the new version is live:
- Update
../../ROADMAP.md— move what was under Next into the relevant section, queue the next thing. - Close the GitHub milestone (if you use them).
- Drink coffee.
HOMEBREW_TAP_TOKEN— rotate every 6 months. Mark it on your calendar the day you generate it.GITHUB_TOKEN— auto-rotated by Actions; no manual step.PROSA_ADMIN_TOKEN,PROSA_PANEL_COOKIE_KEY— these aren't release secrets; they live on your prod environment. Rotate on a schedule appropriate for your deployment.