|
1 | | -# CI/CD release cycle (Rust crate) |
| 1 | +# CI/CD release cycle |
2 | 2 |
|
3 | | -This document describes a practical CI/CD software release cycle for a Rust library crate using GitHub Actions. It’s designed to keep PR feedback fast, keep `main` always releasable, and make releases repeatable and auditable. |
| 3 | +This document explains how CacheKit’s release cycle works in practice: what runs in CI, |
| 4 | +what makes `main` “releasable”, and how a version becomes a GitHub Release and (optionally) |
| 5 | +a crates.io publish. |
4 | 6 |
|
5 | | -## Goals |
| 7 | +For the hands-on, step-by-step release procedure, see `docs/releasing.md`. |
6 | 8 |
|
7 | | -- Catch issues early (format, lint, tests) on every PR. |
8 | | -- Keep `main` green and always releasable (protected branch + required checks). |
9 | | -- Make releases reproducible (tagged source, locked toolchain, deterministic steps). |
10 | | -- Separate “fast PR checks” from “slow/deep validation” (Miri/bench). |
| 9 | +## Mental model |
11 | 10 |
|
12 | | -## Branch + versioning model |
| 11 | +- **All change goes through PRs into `main`.** |
| 12 | +- **`main` should always be releasable.** CI is set up so that obvious breakages are caught |
| 13 | + before merge (format/lint/tests/docs/audit/MSRV). |
| 14 | +- **A “release” is signaled by a git tag.** |
| 15 | + - Stable releases: `vX.Y.Z` (example: `v0.1.0`) |
| 16 | + - Pre-releases: `vX.Y.Z-alpha` / `vX.Y.Z-rc.1`, etc. |
| 17 | +- **Crate versions do not use `v`.** In `Cargo.toml`, use `0.1.0-alpha`, not `v0.1.0-alpha`. |
13 | 18 |
|
14 | | -- **Default branch:** `main` |
15 | | -- **Feature work:** branch from `main`, merge via PR. |
16 | | -- **Versioning:** SemVer in `Cargo.toml` and `CHANGELOG.md`. |
17 | | -- **Release signal:** annotated git tag `vX.Y.Z` created from a commit on `main`. |
| 19 | +## What runs when |
18 | 20 |
|
19 | | -Recommended repo settings: |
| 21 | +### Pull requests and pushes to `main` (`.github/workflows/ci.yml`) |
20 | 22 |
|
21 | | -- Protect `main`: |
22 | | - - Require PRs |
23 | | - - Require status checks to pass (CI jobs) |
24 | | - - Require up-to-date branches before merge |
25 | | - - Require linear history (optional but helps) |
26 | | -- Use CODEOWNERS or required reviewers for release-sensitive areas (optional). |
| 23 | +CI runs on: |
27 | 24 |
|
28 | | -## Pipelines and required workflows |
29 | | - |
30 | | -### 1) PR / main CI pipeline (`.github/workflows/ci.yml`) |
31 | | - |
32 | | -Triggers: |
33 | | - |
34 | | -- `pull_request` to `main` |
| 25 | +- `pull_request` targeting `main` |
35 | 26 | - `push` to `main` |
36 | 27 |
|
37 | | -Required checks (typical): |
| 28 | +What it does (project-specific): |
38 | 29 |
|
39 | | -- **Format:** `cargo fmt --check` |
| 30 | +- **Formatting:** `cargo fmt --check` |
40 | 31 | - **Lint:** `cargo clippy --all-targets --all-features -- -D warnings` |
41 | | -- **Test:** `cargo test --all-features --all-targets` (run on Linux, optionally also macOS/Windows) |
42 | | -- **Docs:** `cargo doc --no-deps --all-features` (treat warnings as errors) |
43 | | -- **MSRV:** `cargo check --all-features` on the project’s declared MSRV toolchain |
44 | | -- **Security audit:** `cargo audit` (or `rustsec/audit-check`) |
| 32 | +- **Tests:** `cargo test --all-features --all-targets` on Linux/macOS/Windows |
| 33 | +- **Docs build:** `RUSTDOCFLAGS='-Dwarnings' cargo doc --no-deps --all-features` |
| 34 | +- **Security audit:** `rustsec/audit-check` |
| 35 | +- **Benchmarks:** `cargo bench --no-fail-fast` (only on `main`, not required for PRs) |
| 36 | +- **MSRV check:** `cargo check --all-features` on Rust `1.85.0` |
| 37 | +- **Miri:** curated subset on nightly for Linux/macOS (slower; not on Windows) |
| 38 | + |
| 39 | +Local equivalents (useful before opening a PR): |
45 | 40 |
|
46 | | -Optional (usually *not* required to merge due to slowness/flakiness): |
| 41 | +```bash |
| 42 | +cargo fmt |
| 43 | +cargo clippy --all-targets --all-features -- -D warnings |
| 44 | +cargo test --all-features --all-targets |
| 45 | +RUSTDOCFLAGS='-Dwarnings' cargo doc --no-deps --all-features |
| 46 | +``` |
47 | 47 |
|
48 | | -- **Miri:** run on Linux (and optionally macOS). Do not run on Windows. |
49 | | -- **Benchmarks:** run only on `main` or on demand (not required for PR merge). |
| 48 | +### Release automation on tags (`.github/workflows/release.yml`) |
50 | 49 |
|
51 | | -Suggested commands: |
| 50 | +The release workflow runs on: |
52 | 51 |
|
53 | | -- Tests (thorough): `cargo test --all-features --all-targets` |
54 | | -- Tests (fast PR): `cargo test` |
55 | | -- Docs: `RUSTDOCFLAGS='-Dwarnings' cargo doc --no-deps --all-features` |
| 52 | +- `push` tags matching `v*.*.*` |
56 | 53 |
|
57 | | -### 2) Release pipeline (`.github/workflows/release.yml`) |
| 54 | +Important: |
58 | 55 |
|
59 | | -Purpose: produce a GitHub Release (notes + artifacts) and publish to crates.io (optional), only from a version tag. |
| 56 | +- This tag pattern matches stable tags like `v0.1.0`. |
| 57 | +- It does **not** match pre-release tags like `v0.1.0-alpha` or `v0.2.0-rc.1` unless you |
| 58 | + expand the trigger pattern. |
60 | 59 |
|
61 | | -Triggers: |
| 60 | +What the workflow does: |
62 | 61 |
|
63 | | -- `push` tags: `v*.*.*` |
64 | | - - Note: this does not include pre-release tags like `v0.1.0-alpha` or `v0.2.0-rc.1` |
65 | | - unless you expand the trigger pattern. |
| 62 | +1. Verifies the tag commit is reachable from `origin/main`. |
| 63 | +2. Re-runs full validation: fmt, clippy, tests, docs. |
| 64 | +3. Verifies the crate packages cleanly: `cargo package` and `cargo publish --dry-run`. |
| 65 | +4. Creates a GitHub Release (currently using auto-generated release notes). |
| 66 | +5. Optionally publishes to crates.io. |
66 | 67 |
|
67 | | -Recommended release steps: |
| 68 | +crates.io publishing: |
68 | 69 |
|
69 | | -1. **Validate tag source:** ensure tag points to a commit on `main` (optional but recommended). |
70 | | -2. **Run full validation again** (or assert CI was green on the tagged commit): |
71 | | - - `cargo fmt --check` |
72 | | - - `cargo clippy --all-targets --all-features -- -D warnings` |
73 | | - - `cargo test --all-features --all-targets` |
74 | | - - `cargo doc --no-deps --all-features` |
75 | | -3. **Build artifacts** (optional, if you ship binaries or example builds): |
76 | | - - `cargo build --release` |
77 | | -4. **Package verification** before publish: |
78 | | - - `cargo package` |
79 | | - - `cargo publish --dry-run` |
80 | | -5. **Create GitHub Release**: |
81 | | - - Use generated notes or extract from `CHANGELOG.md` |
82 | | -6. **Publish to crates.io** (optional): |
83 | | - - `cargo publish` |
84 | | - - Requires `CARGO_REGISTRY_TOKEN` secret |
| 70 | +- The publish job only runs if the repository variable `CARGO_REGISTRY_TOKEN` is set. |
| 71 | +- When enabled, the job runs `cargo publish` using that token. |
85 | 72 |
|
86 | | -If you ship binaries, add a matrix to build per OS and upload artifacts to the GitHub Release. |
| 73 | +### Scheduled maintenance (`.github/workflows/maintenance.yml`) |
87 | 74 |
|
88 | | -### 3) Scheduled security + drift checks (`.github/workflows/maintenance.yml`) |
| 75 | +Maintenance runs on a schedule and manually (`workflow_dispatch`) to catch: |
89 | 76 |
|
90 | | -Purpose: catch dependency advisories and ecosystem drift even when there are no PRs. |
| 77 | +- New RustSec advisories and dependency issues |
| 78 | +- Ecosystem drift when there isn’t active development |
91 | 79 |
|
92 | | -Triggers: |
| 80 | +### Docs site publishing (`.github/workflows/jekyll-gh-pages.yml`) |
93 | 81 |
|
94 | | -- `schedule` (e.g., weekly) |
95 | | -- `workflow_dispatch` (manual) |
| 82 | +On each push to `main` (or manually), GitHub Pages builds the site from `docs/` via Jekyll. |
| 83 | +This is for the documentation site under the repo’s Pages URL (not `cargo doc` output). |
96 | 84 |
|
97 | | -Recommended jobs: |
| 85 | +## How a release happens (end-to-end) |
98 | 86 |
|
99 | | -- `cargo audit` / `rustsec/audit-check` |
100 | | -- `cargo update -w --dry-run` (optional, informational) |
101 | | -- (Optional) run CI with the latest stable toolchain if you pin a toolchain, to detect upcoming breakages. |
| 87 | +### 1) Prepare a release PR |
102 | 88 |
|
103 | | -### 4) Documentation publishing (optional) |
| 89 | +In a PR that targets `main`: |
104 | 90 |
|
105 | | -Only needed if you publish docs to GitHub Pages. |
| 91 | +- Bump `Cargo.toml` `version = "X.Y.Z(...)"` (no `v`). |
| 92 | +- Finalize `CHANGELOG.md` for that version/date. |
| 93 | +- (Optional) Update `docs/benchmarks.md` after a local run: |
| 94 | + - `cargo bench` |
| 95 | + - `scripts/update_docs_benchmarks.sh target/criterion docs/benchmarks.md` |
106 | 96 |
|
107 | | -Options: |
| 97 | +### 2) Merge to `main` |
108 | 98 |
|
109 | | -- Publish `cargo doc` output to `gh-pages` (for crate docs). |
110 | | -- Build and publish a separate `docs/` site (e.g., MkDocs / mdBook / Jekyll). |
| 99 | +Merge once CI is green. This keeps `main` always in a releasable state. |
111 | 100 |
|
112 | | -## Typical software release cycle (end-to-end) |
| 101 | +### 3) Tag the release |
113 | 102 |
|
114 | | -1. **Development** |
115 | | - - Work on a branch, run locally: |
116 | | - - `cargo fmt` |
117 | | - - `cargo clippy --all-targets --all-features -- -D warnings` |
118 | | - - `cargo test --all-features --all-targets` |
119 | | -2. **Pull request** |
120 | | - - CI runs required checks. |
121 | | - - Review and iterate until green. |
122 | | -3. **Merge to `main`** |
123 | | - - CI runs again on `main`. |
124 | | - - `main` stays green (releasable). |
125 | | -4. **Release preparation** |
126 | | - - Update `CHANGELOG.md` and bump `Cargo.toml` version. |
127 | | - - Open PR “Release vX.Y.Z”. |
128 | | -5. **Tag and release** |
129 | | - - After merge, create tag `vX.Y.Z` on `main`. |
130 | | - - Release workflow runs full validation, builds artifacts, publishes release (and crates.io if configured). |
131 | | -6. **Post-release** |
132 | | - - Verify published crate metadata and docs. |
133 | | - - Triage any newly reported issues. |
| 103 | +Tag the exact commit on `main` you want to release. |
134 | 104 |
|
135 | | -## What to require vs. what to keep optional |
| 105 | +- **Stable tags** (`vX.Y.Z`) will trigger the automated release workflow. |
| 106 | +- **Pre-release tags** (`vX.Y.Z-alpha`, etc.) currently will not trigger the release |
| 107 | + workflow with the default tag pattern. |
136 | 108 |
|
137 | | -Recommended **required for merge**: |
| 109 | +### 4) Watch the automation |
138 | 110 |
|
139 | | -- fmt, clippy, tests (at least Linux), docs build, MSRV check, audit |
| 111 | +For stable tags, GitHub Actions validates, creates the GitHub Release, and optionally |
| 112 | +publishes to crates.io if configured. |
140 | 113 |
|
141 | | -Recommended **optional / non-blocking**: |
| 114 | +### 5) After the release |
142 | 115 |
|
143 | | -- Miri (Linux/macOS only), benchmarks, extended multi-OS test matrices, stress/perf tests |
| 116 | +- Verify the GitHub Release notes/tag/version. |
| 117 | +- If published, verify the crates.io page and metadata. |
| 118 | +- Keep `[Unreleased]` in `CHANGELOG.md` ready for the next cycle. |
144 | 119 |
|
145 | | -## Commands cheat sheet (CI-friendly defaults) |
| 120 | +## Troubleshooting |
146 | 121 |
|
147 | | -- Format: `cargo fmt --check` |
148 | | -- Lint: `cargo clippy --all-targets --all-features -- -D warnings` |
149 | | -- Test (thorough): `cargo test --all-features --all-targets` |
150 | | -- Test (workspace): `cargo test --workspace --all-features --all-targets` |
151 | | -- Docs: `RUSTDOCFLAGS='-Dwarnings' cargo doc --no-deps --all-features` |
152 | | -- MSRV (example): `cargo +1.85.0 check --all-features` |
153 | | -- Audit: `cargo audit` |
| 122 | +- **Release workflow didn’t run:** the tag must match `v*.*.*` (stable) unless you change |
| 123 | + the workflow trigger. |
| 124 | +- **Publish job skipped:** ensure `CARGO_REGISTRY_TOKEN` is configured as a repository |
| 125 | + variable (or update the workflow to use secrets). |
| 126 | +- **Docs site didn’t update:** confirm the Pages workflow is enabled and the `docs/` |
| 127 | + folder builds successfully with Jekyll. |
0 commit comments