Skip to content

How Queen ships

The registries a release touches, what CI publishes from verified tags, what a maintainer uploads, where each version number lives, and the one boundary a rollback cannot cross.

Updated View as Markdown

A release touches the broker image, six client SDKs and the CLI, across seven package registries plus GitHub Releases. Some channels are published by CI from a verified tag; others require a maintainer upload. Knowing which is which is the point of this page.

Where the version lives

server/server.json is the single source. Three separate things read it:

  • server/build.rs extracts version and exports it as QUEEN_VERSION, which is what GET /health, the boot log and queenctl ping report. A version bump therefore forces a rebuild of the broker.
  • build.sh reads version with jq to tag the broker image it builds or pushes locally.
  • .github/workflows/docker-build.yml reads version with jq for the same purpose in CI.

Neither reads name: the image name is queen in both, and both take the proxy image’s version from proxy/Cargo.toml instead.

Everything else carries its own version, or none at all.

Artifact Where its version lives
Broker image server/server.json
queen-engine (the broker crate) server/Cargo.toml, which must say the same string as server/server.json: the first is what crates.io serves, the second is what the built binary reports on /health
JavaScript client clients/client-js/package.json
Python client clients/client-py/pyproject.toml and clients/client-py/queen/__init__.py, whose __version__ the package exports: bumping only the first ships a package that misreports itself
PHP/Laravel client nowhere: composer.json has no version field; the split repository receives a bare vX.Y.Z tag from clients/client-php/vX.Y.Z
Go client nowhere: the git tag is the version
Rust client clients/client-rust/Cargo.toml
queen-protocol crates/queen-protocol/Cargo.toml, and the version requirement on EVERY dependency on it: both clients/client-rust/Cargo.toml and server/Cargo.toml pin it by version beside the path
queenctl nowhere: the tag is the version, injected at build time with -ldflags
C++ client nowhere: header-only, taken at whatever tag you check out
Dashboard (app/) app/package.json, private, never published separately

The versioning rule

Each published component carries its own release version. Coordinated product releases through 1.3 kept the broker, proxy and SDK minor lines aligned; from 1.4 onward, broker/proxy releases may advance without republishing SDKs when the stable /api/v1 contract remains compatible. Never infer the installed SDK version from the broker tag: read the manifest or component tag in the table above. A patch that rebuilds only one component moves only that component.

What CI publishes

The container images

.github/workflows/docker-build.yml publishes two images to GHCR, as a matrix over the two Dockerfiles:

Image Dockerfile Version from
ghcr.io/queen-mq/queen Dockerfile server/server.json
ghcr.io/queen-mq/queen-proxy proxy/Dockerfile proxy/Cargo.toml

Both build for linux/amd64 and linux/arm64 from the repository root: the proxy crate embeds ../server/webapp/dist, so its context cannot be proxy/. Each gets three tags: its own version, sha-<short>, and latest. The broker also receives QUEENCTL_VERSION and QUEENCTL_COMMIT as build arguments so that queenctl version inside the container matches what the broker reports.

Docker Hub is an opt-in mirror of the same build, not a second build: with vars.DOCKERHUB_NAMESPACE plus the DOCKER_USERNAME and DOCKER_PASSWORD secrets configured, buildx pushes the one manifest to docker.io under the same three tags, so the images are bit-identical across the two registries. Unset, the job publishes to GHCR alone, and a pull request never mirrors because secrets do not reach forked pull requests.

Authentication for GHCR is the workflow’s own GITHUB_TOKEN under permissions: packages: write, so that registry needs no secrets to rotate; the two Docker Hub secrets exist only if the mirror does. Layer caching is the GitHub Actions cache, scoped per image (scope=${{ matrix.image }}); a shared scope would make the two matrix legs evict each other’s layers and cold-build every run.

Triggers are a push to the release branch, a v* tag, or workflow_dispatch. A pull request against release builds both images without pushing.

The CLI

.github/workflows/release-cli.yml fires on a tag matching clients/client-cli/v* and runs GoReleaser v2, which cross-compiles queenctl for linux, darwin and windows on amd64 and arm64 (windows/arm64 excluded), produces tar.gz archives (zip on windows) with checksums and pre-built shell completions, and attaches them to a GitHub Release. Version, commit and build date go in through -ldflags, so queenctl version reports the tag.

The directory-prefixed tag shape is required by Go’s module proxy for a nested module, and GoReleaser wants bare semver, so the workflow strips the prefix into GORELEASER_CURRENT_TAG and runs with --skip=validate, which drops the requirement that the bare tag exist at the checkout. In practice it does exist: a release tags the broker and the CLI together, and GoReleaser publishes its GitHub Release against the bare tag, so the queenctl X.Y.Z release lands on the vX.Y.Z tag. The broker’s release notes for that tag and the CLI artifacts therefore share one GitHub Release: edit the release that exists rather than creating a second one on the same tag.

The PHP client mirror

queen-mq/queen is the only source of truth. The queen-mq/php-client repository is a read-only clients/client-php subtree, created from master and tagged only by .github/workflows/release-php-client.yml. A source tag such as clients/client-php/v1.2.3 is verified on PHP 8.3 and 8.4, exercises the Redis WithoutOverlapping integration, and checks that the matching native supervisor release exists before it can create v1.2.3 in the mirror. Packagist reads that bare mirror tag.

Before enabling the mirror, install an organization-owned GitHub App on only queen-mq/php-client with repository permission Contents: Read and write. Store its App ID in the PHP_CLIENT_MIRROR_APP_ID Actions variable and its private key in the PHP_CLIENT_MIRROR_APP_PRIVATE_KEY Actions secret of queen-mq/queen. The workflow asks the App for a short-lived token scoped to that one repository; it does not use a personal access token or a deploy key. Protect clients/client-php/v* in the source and v* in the mirror from updates and deletion. The first Packagist submission is manual; enable the Packagist GitHub hook on the mirror afterwards.

What a maintainer publishes

The package registries below either need a direct upload or a source tag created by a maintainer. The PHP tag is then verified and mirrored by CI; no one commits to its split repository.

Channel Package How
npm queen-mq npm publish from clients/client-js
PyPI queen-mq clients/client-py/publish.sh, which builds the wheel and sdist and uploads with twine
Go modules github.com/smartpricing/queen/clients/client-go push a clients/client-go/vX.Y.Z tag; the module proxy picks it up
Packagist queen-mq/php-client push clients/client-php/vX.Y.Z; the verified release workflow mirrors it as vX.Y.Z, then the Packagist webhook indexes it
crates.io queen-protocol, then queen-mq and queen-engine cargo publish from crates/queen-protocol, then from clients/client-rust and from server/ (the broker package; its library is the embedded engine)

The C++ client needs nothing published: consumers take clients/client-cpp/queen_client.hpp at the tag.

Three tag shapes are load-bearing and easy to get wrong. Both nested Go modules need the directory-prefixed form (clients/client-go/v1.0.0 and clients/client-cli/v1.0.0) because that is how Go’s module proxy resolves a module that is not at the repository root. The PHP client likewise uses clients/client-php/v1.0.0; the release workflow maps it to the bare v1.0.0 tag required by Packagist in queen-mq/php-client. Without the Go prefix, go get reports no matching versions.

Order

The broker tag and image go first, because every SDK is released against a broker; then npm, because most downstream consumers wait on it; then PyPI; then the three crates, protocol before client and engine; then the Go module tag. Publish the supervisor release before creating the PHP source tag: the PHP release workflow verifies the pinned supervisor archives before it mirrors clients/client-php/vX.Y.Z as Packagist vX.Y.Z. Finally create the CLI tag, which triggers its own workflow.

Rollback, and the one place additivity stops

Schema application is idempotent by construction: server/src/schema.rs re-applies schema.sql and the 33 stored-procedure files at every boot (the schema catalogue has the mechanism), so re-applying is always safe, and rolling the broker image back to a previous 1.x is safe at the database level. Note that the procedures are embedded in the binary, so the binary and the procedures roll together: you cannot roll one back independently.

The boundary into 1.0 is the exception, and it is a wall rather than a teardown. The boot apply only creates: the teardown and migration files that once dropped the retired engines were themselves deleted from the tree (see Retired objects), so nothing removes, converts or migrates an older database’s objects at boot.

Stale release material

The pre-1.0 release checklist, developer/14-release.md, is stale like the rest of developer/; for what each release actually changed, read CHANGELOG.md at the repository root.

The CI that was stale the same way is gone: cpp-server-build.yml (C++ make targets that no longer existed) and cli.yml (an end-to-end suite pinned to a pre-log-engine 0.14.3 broker image) have both been deleted. The active release workflows are docker-build.yml, release-cli.yml, release-supervisor.yml and release-php-client.yml; the CLI’s end-to-end suite runs in the cli cell of the tests matrix, against the broker built from the same tree.

Post-release

  • Smoke-test the published packages from a clean directory rather than a local link: install queen-mq from npm and from PyPI, and go get the client module at the new version.
  • Write release notes covering schema changes, new environment variables and anything removed. A removed route or environment variable is a major-version change.
  • Re-check that the published image tag in these docs matches what was pushed. It appears in Start here and Self-hosting.
Navigation

Type to search…

↑↓ navigate↵ selectEsc close