Release Candidate Verification & Quality Gates¶
Every release of UltimaScraper passes through strict automated gates before publishing container images or distribution wheels.
Release Check Pipeline¶
The primary release validation script is located at scripts/release/check.sh.
# Run local release verification suite
./scripts/release/check.sh
# Include isolated Docker acceptance test
./scripts/release/check.sh --docker
Automated Stages¶
- Backend Test Suite:
- Executes
pytestwith coverage thresholds across all core modules, write queue handlers, and job execution engines. - WebUI Build & Test:
npm ciclean package installation.npm audit --omit=dev --audit-level=highsecurity vulnerability scanning.npm run lintESLint code style and hygiene validation.npm run typecheck:testsTypeScript strict type checking.npm run test:unitVitest component and logic tests.npm run build:productionVite static production asset bundle generation.npm run test:smokeBrowser smoke verification.- Documentation Strictness Gate:
zensical build --strict: Builds the entire documentation site with strict error enforcement. The build fails if any dead links, syntax anomalies, or missing pages are detected.- Distribution Artifacts:
- Builds distribution wheels and packages for release staging.
- Isolated Docker Acceptance (Optional
--docker): - Spins up ephemeral test containers, initializes a temporary PostgreSQL and Redis instance, exercises the setup wizard, verifies API health, and tears down cleanly.
Tagging & Image Publishing¶
- Version Alignment: Ensure
pyproject.tomlandpackage.jsonreflect the target version (e.g.0.1.0). - Git Tagging: Create and push an annotated git tag matching the version:
- Automated CI Build: The GitHub Actions release workflow (
.github/workflows/release.yml) builds multi-architecture images for bothlinux/amd64andlinux/arm64and publishes them toghcr.io/ultimahoarder/ultimascraper/{ultimascraper,backend,worker,webui}, the images Docker Compose and the setup wizard use. - WebUI package: The GitHub Actions workflow
.github/workflows/webui-package.ymlpublishesultima-scraper-webui-serverto PyPI, which WebUIs installed withuv tool installupdate from. See Updating the WebUI.
Image tags¶
| Trigger | Tags pushed | Notes |
|---|---|---|
Tag vX.Y.Z (must match pyproject.toml) |
X.Y.Z, X.Y, latest, sha-<commit> |
A release version is never overwritten; the run fails if X.Y.Z already exists. |
Push to main (or a manual run) |
X.Y.Z-edge.<commit>, edge, sha-<commit> |
A prerelease of the coming version. It never writes X.Y.Z or latest. |
Pin a release (0.1.0) for real deployments. Use edge to try main on a test host, for example with Upgrade under Backends → Deployments.
The workflow runs on GitHub's own runners and pushes with the run's own token, so each ghcr package must give the UltimaScraper repository write access (package settings → Manage Actions access). It needs no secrets.
The WebUI package workflow publishes through PyPI's trusted publishing: the ultima-scraper-webui-server project on PyPI trusts the UltimaHoarder/UltimaScraper repository's webui-package.yml workflow in the pypi environment, so it needs no token.
origin fetches from GitHub and pushes to both GitHub and the Forgejo backup mirror, so git push origin (or the push button in VS Code) updates both. Forgejo runs no workflows.
Build cache¶
The workflow keeps BuildKit's cache in ghcr.io/ultimahoarder/ultimascraper/buildcache, one tag per image. Every build reads it and every push writes it back, so the next run reuses each unchanged layer.
The Dockerfiles are ordered for it:
- the system packages and the third-party dependencies are rebuilt only when the base image,
docker/requirements.lockordocker/webui-requirements.lockchange; - a build's own code (the first-party wheels,
main.py, the WebUI server) is pure Python, installed once on the build host's own platform and copied into every image as a small layer, so arm64 does not build it under QEMU; - version and revision build arguments are declared after the heavy steps, because a build argument used by a
RUNstep makes that step miss the cache whenever its value changes.
So a typical main build redoes only the first-party layers. The first run, or a run after a lock file changes, builds everything.
Images are pushed one at a time: the full, backend and worker images share most layers, so each later image reuses what the first one uploaded.