Files
bookorbit/docs/RELEASE_PROCESS.md
T
neon e89f4ce0e4 ci: enforce PR issue references and branch conventions
Validate BO-<issue>-<slug> branch names and lint PR titles and commit headers in CI and release workflows.

Require GitHub closing-keyword issue references and verify referenced issues exist. Align contributing, release, commit, testing, and policy docs with the enforced behavior and tone.
2026-05-17 13:10:32 -06:00

6.5 KiB

Release Process

This document covers how releases are created, what triggers Docker image publishing, and how to roll back if needed.


Overview

BookOrbit uses semantic-release for automated versioning and GitHub Release creation. Releases are triggered manually via workflow_dispatch and produce:

  • A Git tag (vX.Y.Z)
  • A GitHub Release with auto-generated release notes
  • A Docker image pushed to GHCR (X.Y.Z + latest)

PR Requirements

Squash merge required

All PRs to main must use squash merge. The squash commit message becomes the commit that semantic-release analyzes for version bumps.

PR titles and all commits in a PR must follow the commit guidelines:

<type>(<scope>): <summary>

Why PR title is required: with squash merges, the final commit analyzed by semantic-release is based on the PR title.

Validation in CI and release checks:

  • PR branch name must follow BO-123-fix-reader-pagination
  • PR title must match commitlint rules
    • Example: fix(reader): reduce rerendering in chapter list
  • PR commit headers must match commitlint rules
  • PR description must include at least one GitHub closing-keyword issue reference:
    • close, closes, closed, fix, fixes, fixed, resolve, resolves, resolved
    • Examples: Closes #123, Fixes: #123, RESOLVED owner/repo#123
  • Branch issue number must exist in this repository
  • PR description issue references must resolve to valid GitHub issues (same repo or owner/repo#issue)

Examples:

  • Valid:
## What does this PR do?

Improve reader performance when opening large EPUB files.

Fixes #145
  • Invalid (fails lint):
## What does this PR do?

Improve reader performance when opening large EPUB files.

fixes issue 145

Types that trigger releases

Type Release Notes
feat minor New features
fix patch Bug fixes
perf patch Performance improvements
security patch Security patches
db patch Database/schema changes
style patch Visual/CSS changes
revert none No release
refactor none No release
docs none No release
test none No release
build none No release
ci none No release
chore none No release

Breaking changes (BREAKING CHANGE: in footer) always trigger a major release regardless of type.


Manual Release Runbook

Releases are triggered manually from the GitHub Actions UI.

Prerequisites

  1. All changes are merged to main.
  2. CI has passed for the latest commit on main.

Steps

  1. Go to Actions > Release workflow.
  2. Click Run workflow.
  3. Select main branch.
  4. Click Run workflow.

The workflow will:

  1. Verify the dispatch is from main.
  2. Verify CI has passed for the current commit.
  3. Run semantic-release to determine the next version.
  4. Create a Git tag and GitHub Release with release notes.
  5. Build, scan, and push the Docker image (if a new version was published).

No release produced

If all commits since the last release are non-releasable types (chore, docs, test, build, ci, refactor), semantic-release will skip the release. The Docker publish job will also be skipped.


Docker Image Publishing

Release images

The release.yml workflow publishes Docker images after a successful release:

  • ghcr.io/bookorbit/bookorbit:<X.Y.Z> - version-specific tag
  • ghcr.io/bookorbit/bookorbit:latest - latest stable release

Images are scanned with Trivy before push. If critical or high vulnerabilities are found, the push is blocked.

Continuous validation images

The container-image.yml workflow still runs on CI success (push to main) and publishes validation images with branch and SHA tags. These are not release images.


Rollback

Rolling back to a previous release

To deploy a previous version:

# In your production docker-compose.yml or .env, set:
APP_IMAGE=ghcr.io/bookorbit/bookorbit:X.Y.Z

Replace X.Y.Z with the version you want to roll back to, then redeploy:

pnpm prod:down
pnpm prod:up

Deleting a bad release tag

If a release was created in error and needs to be removed:

# Delete tag locally and remotely
git tag -d vX.Y.Z
git push origin :refs/tags/vX.Y.Z

Then delete the corresponding GitHub Release from the Releases page.


Release Workflow Architecture

The release.yml workflow has four jobs:

┌──────────────┐     ┌──────────────────┐
│  commitlint  │     │ release-dry-run  │    (PR only)
└──────────────┘     └──────────────────┘

┌──────────────┐     ┌──────────────────┐
│   release    │────>│  docker-publish  │    (workflow_dispatch only)
└──────────────┘     └──────────────────┘
  • commitlint: Validates branch naming, PR title, commit headers, and PR description issue-link and issue-existence rules (PR only).
  • release-dry-run: Runs semantic-release in dry-run mode to validate config (PR only).
  • release: Creates the actual release (manual dispatch only). Outputs published, version, and tag.
  • docker-publish: Builds and pushes Docker image (only when a release was published).

Concurrency

  • PR checks cancel older runs for the same PR.
  • Manual release runs are serialized and never canceled mid-flight.
  • PR checks and manual releases do not block each other.

Configuration Files

File Purpose
release.config.js semantic-release config (plugins, release rules)
commitlint.config.cjs commitlint config (type/scope validation)
.github/workflows/release.yml Release and Docker publish workflow