whoobe docsv0.3.3

Release process#

How a Whoobe release is made. Decisions taken on 2026-10-05: one aligned product version, first release 0.1.0, container images published on ghcr.io, security fixes for the latest two minor versions.

Status of the automation: the gates and the preparation step (scripts/release.sh) exist and are described below. Publishing the container images and the draft GitHub Release from a tag is written (.github/workflows/release.yml) and ran for v0.1.1 (2026-10-18). If a package already exists on ghcr.io (made by hand or by another account), the repository must be added under the package's Package settings > Manage Actions access with the role Write, or the push fails with permission_denied: write_package; that is what happened at the first attempt. Steps 5 to 7 by hand remain the fallback.

1. Versions#

  • One version for the whole product, in the form MAJOR.MINOR.PATCH. The root package.json and every package under apps/, packages/ and packages/sdk/ carry the same number (scripts/security is a test package and is not part of it). The SDK packages (@whoobe-cms/*) are versioned with the product, not on their own.
  • While the version starts with 0.: a minor release may change behaviour or break compatibility, a patch release contains only fixes (including security fixes and base-image updates).
  • A release is the git tag vMAJOR.MINOR.PATCH on a commit of master, or of a release/MAJOR.MINOR branch for a patch of an older line. The tag is annotated and never moved.

2. Support#

Security fixes are made for the latest two minor versions (for example, with 0.3.x current: 0.3.x and 0.2.x). Anything older gets no fix: upgrade. A fix lands on master first, then is cherry-picked to the release/ branch of the other supported line and released as a patch of both.

3. What a release contains#

Artifact Where
Source the tag, and the GitHub Release page
Container images, one per service, referenced by digest ghcr.io (see step 5)
Documentation docs/release/ at the tag; docs/release/README.md states the release it is aligned to
API description docs/release/api/openapi.yaml, attached to the GitHub Release
Security evidence reports of the CI run on the release commit, attached to the GitHub Release
Release notes draft from CHANGES.md, edited by hand

4. The gates#

./scripts/release.sh check runs them on the current commit and changes nothing.

Gate Automatic What it checks
Git yes clean working tree; on master or release/*; HEAD is pushed, so the CI result belongs to it
CI yes the security workflow succeeded on exactly this commit, and its DAST job (OWASP ZAP) succeeded on it. DAST does not run on push: start the workflow by hand with Also run the ZAP DAST scan ticked, on the release commit
License yes LICENSE.md (MIT) exists and every product package.json declares "license": "MIT"
Documentation yes every core-api route is in the API route table; api-reference.md and openapi.yaml equal what the generator produces; no broken link (links written as examples inside backticks do not count), no docs/... path or quoted repository path that does not exist; every environment variable read by the services or listed in .env.example is in operations/configuration-reference.md; the alignment line is present
Upgrade test no install the previous release, load data, upgrade to this commit, check the migrations and a login. Not automated yet - the most valuable test still missing
Fresh install no follow docs/release/operations/creating-workspaces.md on an empty host
Behaviour changes no operators are told about new or changed environment variables and defaults, in the release notes
Results published no the record of the version (a file named after the version, in the security/results folder) is written and committed: the run on the release commit (id, commit, every job and its result), the runs that failed before the green one, what differs between the tested commit and the tag, and what the run did not cover (policy)
Security summary no docs/release/security/status-summary.md is current and its open items are acknowledged; new findings of the documentation review (security/findings.md) are fixed or accepted in the release notes
Documentation review no the table "What a documentation release still needs" in docs/release/README.md has no unowned row; the technical set (architecture/, operations/, security/) was read by someone other than its author since the last change of behaviour

The non-automatic gates are printed as a reminder every time; they are the release manager's responsibility.

5. Procedure#

  1. Freeze. Merge what goes in; wait for the security workflow on the last commit; start the DAST run on it.

  2. Gates. ./scripts/release.sh check - fix what fails, do the manual gates.

  3. Prepare. ./scripts/release.sh prepare 0.1.0. After the gates pass, it

    • sets the version in the root and in every product package (a version must be higher than the latest v* tag);
    • adds a # Release vX.Y.Z - date marker and a release entry at the top of CHANGES.md. Everything under a marker, down to the next one, belongs to that release; entries above the newest marker are unreleased;
    • writes the alignment line of docs/release/README.md;
    • drafts .release/notes-vX.Y.Z.md (git-ignored) from the entry titles. Edit it: highlights, behaviour changes.

    --dry-run shows the plan without writing. The script does not commit, tag or push.

  4. Commit and tag, by hand, as the script prints: git commit -m "Release vX.Y.Z", git tag -s vX.Y.Z -m "Whoobe vX.Y.Z" (signed: a GPG or SSH key registered on GitHub as a signing key; git config gpg.format ssh and user.signingkey for an SSH key). release.yml refuses a lightweight or unverified tag before building anything, git push origin <branch> --follow-tags.

  5. SBOM: scripts/release/sbom.sh --images (CycloneDX for the source tree and the service images, written to .release/sbom/); attach to the GitHub Release.

  6. Publish images (release.yml, on the tag): core-api, asset-processor, gateway, publisher, admin-dashboard and whoobe-one-spike are built for amd64 and arm64, pushed to ghcr.io/<owner>/whoobe-<service>:X.Y.Z (plus X.Y), signed by digest with cosign (keyless), given a SLSA build provenance attestation (stored with the image; check with (GitHub CLI 2.49 or later) gh attestation verify oci://ghcr.io/<owner>/whoobe-<service>@<digest> -R <owner>/<repo>), and the digests are written into the release notes. Verify a signature with cosign verify --certificate-identity-regexp '^https://github.com/<owner>/<repo>/\.github/workflows/release\.yml@refs/tags/vX\.Y\.Z$' --certificate-oidc-issuer https://token.actions.githubusercontent.com ghcr.io/<owner>/whoobe-<service>@<digest> (tried on 2026-10-23 with a trial tag, all six images). admin-dashboard and whoobe-one-spike read their addresses, fonts and placeholder mode from the container's environment at start (/config.js), so one image serves every workspace. The job stops if the tag differs from package.json.

  7. Publish the release: release.yml creates a draft GitHub Release with the release notes file of the version (release-notes-<version>.md in docs/release/, or a one-line note if the file is missing), the image digests, openapi.yaml and the source SBOM. Add the image SBOMs (step 5) and the CI security reports by hand, then publish the draft after review. Read the reports first: one that describes a High or Critical finding that is not fixed yet is not attached (disclosure policy).

6. Patches and security updates#

  • A patch release follows the same steps from the relevant branch. Typical triggers: a CVE in a dependency or in the pinned base image (as with libpcre2 and perl-base in the Debian runtime image on 2026-10-05), a vulnerability found by the security suites, a regression.
  • For a security fix, the release notes say what was fixed and which versions are affected; supported versions get the patch at the same time.
  • When a finding becomes public follows the disclosure policy: a High or Critical finding found by the project is entered in the register, and its test joins the public suite, with the release that fixes it; the release notes get a Security section.

7. The documentation as a release artifact#

docs/release/ at the tag is the documentation of that release; docs/release/README.md states the release it is aligned to (written by release.sh prepare) and carries the table of what is still missing. To change the documentation after a release, a patch release is not needed for typos, but any change of behaviour, configuration or security posture must update the configuration reference, the release notes and the security status summary in the same commit as the code (the gate catches the variables and the routes, not the prose). A rendered copy for readers can be produced with the product's own documentation-site feature (point it at docs/release); not automated.

8. Not done yet#

  • Image SBOMs and the CI security reports are still attached to the draft release by hand; the cosign signature has not been verified by a consumer yet (the signing step succeeded).
  • Pinning the base images' freshness: the pinned digests lag behind Debian's security updates (see CHANGES.md, 2026-10-05), which today costs a patch release or a manual apt-get upgrade line in the Dockerfile.

Last updated