|
| 1 | +# Define a Standard Release Process (Branch, Tag, Docker Image, Crate) |
| 2 | + |
| 3 | +**Issue**: #448 |
| 4 | +**Parent Epic**: N/A (standalone release process task) |
| 5 | +**Related**: |
| 6 | + |
| 7 | +- `docs/contributing/roadmap-issues.md` |
| 8 | +- `docs/contributing/commit-process.md` |
| 9 | +- `docs/roadmap.md` |
| 10 | + |
| 11 | +## Overview |
| 12 | + |
| 13 | +Define and document a repeatable release process for this repository so releases are |
| 14 | +predictable, auditable, and less error-prone. |
| 15 | + |
| 16 | +This repository already has a container workflow in `.github/workflows/container.yaml` |
| 17 | +that publishes Docker images for `main` and `develop`. The new release process should |
| 18 | +extend that model to support release branches, while keeping the overall process much |
| 19 | +simpler than the Torrust Tracker release process. |
| 20 | + |
| 21 | +The initial release process should include these mandatory steps in order: |
| 22 | + |
| 23 | +1. Update the version in the relevant `Cargo.toml` files |
| 24 | +2. Commit the release version |
| 25 | +3. Push the release commit to `main` |
| 26 | +4. Create the release tag and push it |
| 27 | +5. Create the release branch and push it |
| 28 | +6. Create the GitHub release from the tag |
| 29 | +7. Let GitHub Actions publish release artifacts: |
| 30 | + - Docker image for the release branch |
| 31 | + - Crate for the release branch |
| 32 | + |
| 33 | +## Goals |
| 34 | + |
| 35 | +- [ ] Define a single documented release workflow with explicit step order |
| 36 | +- [ ] Make branch and tag conventions consistent across releases |
| 37 | +- [ ] Ensure Docker image publication is triggered from release branches |
| 38 | +- [ ] Ensure crate publication is triggered from release branches |
| 39 | +- [ ] Define validation and rollback guidance for failed release steps |
| 40 | +- [ ] Keep the first version of the process intentionally simpler than the tracker repository |
| 41 | +- [ ] Avoid duplicate Docker release tags for the same version |
| 42 | + |
| 43 | +## 🏗️ Architecture Requirements |
| 44 | + |
| 45 | +**DDD Layer**: Infrastructure (CI/CD and release automation), Documentation |
| 46 | +**Module Path**: `docs/`, `.github/workflows/`, and release-related scripts if needed |
| 47 | +**Pattern**: Release workflow and operational guide |
| 48 | + |
| 49 | +### Module Structure Requirements |
| 50 | + |
| 51 | +- [ ] Keep process documentation in `docs/` |
| 52 | +- [ ] Keep automation in `.github/workflows/` and/or `scripts/` |
| 53 | +- [ ] Keep branch and tag naming rules explicit and testable |
| 54 | +- [ ] Keep artifact version alignment across Git tag, Docker image tag, and crate version |
| 55 | + |
| 56 | +### Architectural Constraints |
| 57 | + |
| 58 | +- [ ] Release order must be deterministic and documented |
| 59 | +- [ ] Tag format must be clearly defined as `vX.Y.Z` |
| 60 | +- [ ] Release branch format must be clearly defined and compatible with workflow triggers |
| 61 | +- [ ] Docker publish step must support reproducible release tagging without overloading `main` publish behavior |
| 62 | +- [ ] Docker release tags must not include the Git tag `v` prefix |
| 63 | +- [ ] Crate publish step must define pre-checks and ownership requirements |
| 64 | +- [ ] Docker Hub credentials must separate secrets from non-sensitive variables |
| 65 | +- [ ] Workflow triggers and branch protections must align with allowed branches (`develop`, `main`, `releases/**/*`) |
| 66 | + |
| 67 | +### Anti-Patterns to Avoid |
| 68 | + |
| 69 | +- ❌ Manual ad-hoc release steps without a checklist |
| 70 | +- ❌ Tagging and artifact versions drifting from each other |
| 71 | +- ❌ Publishing the same Docker release twice with both `vX.Y.Z` and `X.Y.Z` tags |
| 72 | +- ❌ Publishing artifacts without verification or rollback notes |
| 73 | +- ❌ Coupling release steps to undocumented local machine state |
| 74 | + |
| 75 | +## Specifications |
| 76 | + |
| 77 | +### 1. Release Branch Strategy |
| 78 | + |
| 79 | +Define how release branches are created, named, and finalized. |
| 80 | + |
| 81 | +- Naming convention: `releases/vX.Y.Z` |
| 82 | +- Source branch: create the release branch from the same commit that was pushed to `main` |
| 83 | +- The release branch is a publication trigger, not a long-lived development branch |
| 84 | +- The release branch name must be parseable by GitHub Actions so release version metadata can be extracted |
| 85 | + |
| 86 | +### 2. Version Update and Release Commit |
| 87 | + |
| 88 | +Define which manifests are updated before the release commit. |
| 89 | + |
| 90 | +- Root `Cargo.toml` version must be updated from the current development version to the release version |
| 91 | +- Publishable package versions must also be updated in their own manifests |
| 92 | +- The likely first publishable crate is `packages/sdk/Cargo.toml` (`torrust-tracker-deployer-sdk`) |
| 93 | +- The release commit should be explicit and traceable, for example: `release: version vX.Y.Z` |
| 94 | +- Verify release metadata quality for publishable crates (`description`, `license`, `repository`, `readme`) before publishing |
| 95 | + |
| 96 | +### 3. Tagging Strategy |
| 97 | + |
| 98 | +Define release tag rules and when tags are created. |
| 99 | + |
| 100 | +- Tag format: `vX.Y.Z` |
| 101 | +- Annotated and signed tag requirements |
| 102 | +- Tag is created from the release commit already pushed to `main` |
| 103 | +- Tag, release branch, Docker tags, and crate versions must all refer to the same semantic version |
| 104 | +- Git tags keep the `v` prefix, but Docker release tags must use bare semver (`X.Y.Z`) |
| 105 | + |
| 106 | +### 4. Docker Image Publication |
| 107 | + |
| 108 | +Extend `.github/workflows/container.yaml` so release branches also publish Docker images. |
| 109 | + |
| 110 | +- Keep existing behavior for `main` and `develop` |
| 111 | +- Add support for `releases/**/*` branch pushes |
| 112 | +- Follow the tracker repository pattern for deriving release image tags from the release branch version |
| 113 | +- Release branch publication should push versioned tags, not `latest` |
| 114 | +- Release branch publication must publish only canonical semver Docker tags such as `1.2.3` |
| 115 | +- Do not publish duplicate release image tags with both `v1.2.3` and `1.2.3` |
| 116 | +- Verify the image can be pulled and inspected after publication |
| 117 | + |
| 118 | +Environment configuration for Docker publish: |
| 119 | + |
| 120 | +- Use GitHub Environment: `dockerhub-torrust` |
| 121 | +- Keep `DOCKER_HUB_ACCESS_TOKEN` as a secret |
| 122 | +- Keep `DOCKER_HUB_USERNAME` as a normal environment variable (already set to `torrust` in deployer) |
| 123 | +- Do not store `DOCKER_HUB_USERNAME` or `DOCKER_HUB_REPOSITORY_NAME` as secrets |
| 124 | +- Repository name can be hardcoded for this repo (`tracker-deployer`) or stored as a non-secret variable |
| 125 | + |
| 126 | +### 5. Library Crate Publication |
| 127 | + |
| 128 | +Add a dedicated workflow for publishing crates from release branches. |
| 129 | + |
| 130 | +- Preferred initial target crate: `torrust-tracker-deployer-sdk` |
| 131 | +- Trigger on push to `releases/**/*` |
| 132 | +- Run tests and release pre-checks before publication |
| 133 | +- Verify packaged contents before publishing (`cargo package --list`) to avoid shipping unintended files |
| 134 | +- `cargo publish --dry-run` before real publish |
| 135 | +- Post-publish verification (crate visible in registry and installable) |
| 136 | +- Verify docs.rs build status for the published version |
| 137 | +- Avoid mixing Docker-specific logic into the crate publication workflow |
| 138 | + |
| 139 | +Environment configuration for crate publish: |
| 140 | + |
| 141 | +- Use a dedicated GitHub Environment for crate publication (for example `deployment`) |
| 142 | +- Store cargo registry token as a secret only in that environment |
| 143 | +- Keep non-sensitive crate metadata as normal variables when needed |
| 144 | + |
| 145 | +### 6. GitHub Release Creation |
| 146 | + |
| 147 | +Define how the GitHub release is created from the pushed tag. |
| 148 | + |
| 149 | +- Keep this step simple for now: create the GitHub release manually from the tag |
| 150 | +- Attach release notes manually or with a minimal template |
| 151 | +- Do not block Docker or crate publication on a more complex release-notes automation flow |
| 152 | + |
| 153 | +Release finalization gate order: |
| 154 | + |
| 155 | +- Confirm the release commit is pushed to `main` |
| 156 | +- Confirm tag `vX.Y.Z` is pushed |
| 157 | +- Confirm branch `releases/vX.Y.Z` is pushed |
| 158 | +- Confirm Docker release workflow passed |
| 159 | +- Confirm crate release workflow passed |
| 160 | +- Create/publish GitHub release as final step |
| 161 | + |
| 162 | +### 7. Workflow Separation Strategy |
| 163 | + |
| 164 | +Prefer independent workflows instead of one workflow that publishes all release artifacts. |
| 165 | + |
| 166 | +- Keep Docker publication in `container.yaml` because it already owns Docker build/test/publish logic |
| 167 | +- Add a separate release-oriented workflow for crate publication; `deployment.yaml` is probably too vague in this repository |
| 168 | +- Prefer a name that reveals the artifact, for example `publish-crate.yaml` or `release-crate.yaml` |
| 169 | +- Keep GitHub release creation outside the artifact publication workflows for the first iteration |
| 170 | + |
| 171 | +Reasoning: |
| 172 | + |
| 173 | +- Docker and crate publishing have different credentials, failure modes, and verification steps |
| 174 | +- Separate workflows reduce accidental coupling and make reruns more targeted |
| 175 | +- The simpler process is easier to debug than one orchestrator workflow with multiple artifact paths |
| 176 | + |
| 177 | +### 8. Failure Handling and Recovery |
| 178 | + |
| 179 | +Define how to proceed when a step fails. |
| 180 | + |
| 181 | +- If Docker publication fails, the release branch can be re-pushed or the workflow can be re-run without changing the tag |
| 182 | +- If crate publication fails after tag and branch creation, document whether a version must be abandoned or publication can be retried safely |
| 183 | +- Branch/tag rollback guidance |
| 184 | +- Docker publish retry policy |
| 185 | +- Crate publish partial-failure guidance |
| 186 | +- Operator-facing troubleshooting notes |
| 187 | + |
| 188 | +Partial-failure action matrix: |
| 189 | + |
| 190 | +- Docker failed, crate not started: fix Docker workflow and re-run publication on the same release branch |
| 191 | +- Docker passed, crate failed before upload: fix issue and re-run crate workflow on the same release branch |
| 192 | +- Crate published, later step failed: do not republish same crate version; proceed with follow-up patch release if needed |
| 193 | + |
| 194 | +Idempotency and re-run rules: |
| 195 | + |
| 196 | +- Docker release publication must be safely re-runnable for the same release branch/version |
| 197 | +- Crate workflow must detect already-published versions and fail with clear guidance instead of ambiguous errors |
| 198 | +- Tag and branch creation steps must check for existing refs and stop with actionable output if refs already exist |
| 199 | + |
| 200 | +Crate rollback/yank policy: |
| 201 | + |
| 202 | +- Never delete published versions (not possible on crates.io); use `cargo yank` only when necessary |
| 203 | +- Prefer yanking only for severe release defects (broken build, critical security issue, unusable package) |
| 204 | +- After yanking, cut a patch release with a higher version and document remediation in release notes |
| 205 | + |
| 206 | +### 9. Pre-Flight Checks |
| 207 | + |
| 208 | +Define mandatory checks before starting any release actions. |
| 209 | + |
| 210 | +- Verify required GitHub environments exist (`dockerhub-torrust` and crate publish environment) |
| 211 | +- Verify required secrets and variables exist in those environments |
| 212 | +- Verify the releaser has permission to access protected environments and push required refs |
| 213 | +- Verify local workspace is clean and on the expected source branch before version bump/tagging |
| 214 | + |
| 215 | +### 10. Repository Settings Alignment |
| 216 | + |
| 217 | +Define repository settings expectations that release automation depends on. |
| 218 | + |
| 219 | +- Allowed branches for release-related workflows: `develop`, `main`, `releases/**/*` |
| 220 | +- Release workflows must be trigger-scoped to those branches; avoid broad wildcard triggers |
| 221 | +- Current tracker policy (`10` branches and `0` tags allowed) should be documented as reference, and deployer should adopt equivalent branch scoping for release workflows where applicable |
| 222 | + |
| 223 | +## Implementation Plan |
| 224 | + |
| 225 | +### Phase 1: Define the Manual Release Sequence (estimated time: 2-3 hours) |
| 226 | + |
| 227 | +- [ ] Task 1.1: Document the simplified release steps from version bump through GitHub release creation |
| 228 | +- [ ] Task 1.2: Define version, tag, and release branch naming conventions |
| 229 | +- [ ] Task 1.3: Specify which `Cargo.toml` files must be updated for each release |
| 230 | +- [ ] Task 1.4: Add a pre-flight checklist for environments, permissions, and clean git state |
| 231 | + |
| 232 | +### Phase 2: Docker Release Branch Publishing (estimated time: 1-2 hours) |
| 233 | + |
| 234 | +- [ ] Task 2.1: Extend `container.yaml` to trigger on `releases/**/*` |
| 235 | +- [ ] Task 2.2: Add release branch context detection and release image tags |
| 236 | +- [ ] Task 2.3: Define image verification, credential, and rerun requirements |
| 237 | +- [ ] Task 2.4: Ensure Docker Hub username/repository are configured as non-secret variables (token remains secret) |
| 238 | + |
| 239 | +### Phase 3: Crate Publishing Workflow (estimated time: 1-2 hours) |
| 240 | + |
| 241 | +- [ ] Task 3.1: Create a dedicated workflow for publishing the SDK crate from `releases/**/*` |
| 242 | +- [ ] Task 3.2: Define package inspection, dry-run, publish, and post-publish verification steps |
| 243 | +- [ ] Task 3.3: Define dedicated environment and document cargo registry credentials and failure recovery rules |
| 244 | +- [ ] Task 3.4: Add docs.rs post-publish verification guidance |
| 245 | + |
| 246 | +### Phase 4: Validation and Operational Guidance (estimated time: 2-4 hours) |
| 247 | + |
| 248 | +- [ ] Task 4.1: Validate the end-to-end release flow against a test version |
| 249 | +- [ ] Task 4.2: Document how maintainers verify Docker image, crate publication, and GitHub release creation |
| 250 | +- [ ] Task 4.3: Add troubleshooting notes for partial publication failures |
| 251 | +- [ ] Task 4.4: Add explicit idempotency/re-run guidance and crate yank policy |
| 252 | + |
| 253 | +## Acceptance Criteria |
| 254 | + |
| 255 | +> **Note for Contributors**: These criteria define what the PR reviewer will check. Use this as your pre-review checklist before submitting the PR to minimize back-and-forth iterations. |
| 256 | +
|
| 257 | +**Quality Checks**: |
| 258 | + |
| 259 | +- [ ] Pre-commit checks pass: `./scripts/pre-commit.sh` |
| 260 | + |
| 261 | +**Task-Specific Criteria**: |
| 262 | + |
| 263 | +- [ ] The documented release process follows this order: version update, release commit, push to `main`, tag push, release branch push, GitHub release creation, workflow-driven artifact publication |
| 264 | +- [ ] The spec defines explicit finalization gates (main push, tag push, release branch push, Docker pass, crate pass, GitHub release) |
| 265 | +- [ ] Branch naming and tag naming conventions are documented as `releases/vX.Y.Z` and `vX.Y.Z` |
| 266 | +- [ ] `container.yaml` is specified to publish Docker images for release branches in addition to existing `main` and `develop` behavior |
| 267 | +- [ ] The spec explicitly requires Docker release tags to use `X.Y.Z` and forbids `vX.Y.Z` image tags |
| 268 | +- [ ] A separate crate publication workflow is specified for the SDK crate on `releases/**/*` |
| 269 | +- [ ] The spec explicitly records the decision to keep Docker and crate publication in independent workflows |
| 270 | +- [ ] Docker Hub configuration policy is explicit: token is secret, username/repository are variables |
| 271 | +- [ ] Release workflow branch scope is explicit and aligned with `develop`, `main`, and `releases/**/*` |
| 272 | +- [ ] Docker publish procedure includes verification and failure handling |
| 273 | +- [ ] Crate publish procedure includes dry-run and post-publish verification |
| 274 | +- [ ] Crate publish procedure includes package content inspection before publish |
| 275 | +- [ ] Crate publish procedure includes docs.rs build verification after publish |
| 276 | +- [ ] Pre-flight checks are documented for environments, secrets/variables, permissions, and git state |
| 277 | +- [ ] Partial-failure and re-run rules are documented for Docker and crate workflows |
| 278 | +- [ ] Crate rollback policy includes explicit yank criteria and patch-release follow-up |
| 279 | +- [ ] Version consistency rules are documented across Git tags, Docker tags, and crate versions |
| 280 | + |
| 281 | +## Related Documentation |
| 282 | + |
| 283 | +- `docs/contributing/roadmap-issues.md` |
| 284 | +- `docs/contributing/commit-process.md` |
| 285 | +- `docs/roadmap.md` |
| 286 | +- https://raw.githubusercontent.com/torrust/torrust-linting/refs/heads/main/skills/publish-rust-crate/SKILL.md |
| 287 | + |
| 288 | +## Notes |
| 289 | + |
| 290 | +- Keep the first iteration focused on one release path that can be executed by maintainers without additional assumptions. |
| 291 | +- Start with the SDK crate only unless additional crates are explicitly marked for publication. |
| 292 | +- Do not import the tracker repository's full staging and develop branch merge-back process into this repository yet. |
| 293 | +- Guard against the tracker bug described in `torrust/torrust-tracker#1029`: Docker release tags should not be published with the `v` prefix. |
0 commit comments