Skip to content

Releasing semula

A release is a tag. Pushing vX.Y.Z to a commit that already passed CI on main creates a GitHub Release from the CHANGELOG and publishes the container image to ghcr.io/leejianrong/semula. Nothing is built by hand.

Cut a release

  1. Release PR. Off fresh main:
  2. set version in pyproject.toml;
  3. run uv lock (the lockfile records the version, and CI installs it frozen);
  4. in CHANGELOG.md, rename ## [Unreleased] to ## [X.Y.Z] - YYYY-MM-DD and add a new empty ## [Unreleased] above it;
  5. uv sync --extra dev && make check, then open the PR and merge it when CI is green.
  6. Check it without publishing: Actions → Release → Run workflow (a dry run) builds the four CLI binaries and the image, runs the smoke tests and publishes nothing.
  7. Tag the merge commit and push the tag:
    git checkout main && git pull
    git tag -a vX.Y.Z -m "semula X.Y.Z" && git push origin vX.Y.Z
    
  8. Watch the Release run. It fails, and publishes nothing, unless:
  9. the tag is vMAJOR.MINOR.PATCH and the commit is on main;
  10. that commit has a green CI run;
  11. the tag, pyproject.toml, uv.lock and the CHANGELOG all say the same version (scripts/release.py check vX.Y.Z runs the same test locally).
  12. After it: pull the image on a clean machine and run it (docker run --rm -p 127.0.0.1:8080:8080 ghcr.io/leejianrong/semula:X.Y.Z).

What the run publishes

  • A GitHub Release named semula X.Y.Z with the CHANGELOG section as its notes. A 0.x release is marked pre-release.
  • The CLI as one file per platform, attached to the Release with a SHA256SUMS file: semula-X.Y.Z-linux-amd64, -linux-arm64, -macos-arm64, -macos-amd64. It has the HTTP commands only (no serve, mcp or embeddings fetch, which say so and point to the image). Each is built on its own runner (Linux on Ubuntu 22.04, for older glibc), smoke-tested against a running server (scripts/smoke_cli_binary.sh), and the release job fails, creating nothing, if any of the four is missing. Build one locally with make cli-binary.
  • The hosted image, ghcr.io/leejianrong/semula:X.Y.Z-hosted (and X.Y-hosted, latest-hosted): the same Dockerfile with UV_EXTRAS="--extra hosted --extra postgres", smoke-tested by scripts/smoke_image_hosted.sh (needs a minted token to be accepted). Build and check it locally with docker build --build-arg UV_EXTRAS="--extra hosted --extra postgres" -t semula:hosted-check . and make image-smoke-hosted.
  • The image ghcr.io/leejianrong/semula for linux/amd64 and linux/arm64, tagged X.Y.Z, X.Y and latest (and X from 1.0), with build provenance. It is smoke-tested after the push: started, /health and /ui answered.

If it goes wrong

  • A check failed before anything was published: fix it in a PR, delete the tag (git push origin :refs/tags/vX.Y.Z and git tag -d vX.Y.Z), and tag the new merge commit.
  • The release was created but the image failed: re-run the failed job; the image job is safe to repeat. Do not move a published tag.
  • A bad release is out: publish a fixed patch version. Mark the bad GitHub Release as a draft or add a note; do not delete image tags people may have pulled.

Visibility

The package is linked to this repository through the org.opencontainers.image.source label. If the first image shows as private on the package page, set it to public once (package settings → Danger zone → Change visibility); later pushes keep it.