diff --git a/.github/ISSUE_TEMPLATE/release.md b/.github/ISSUE_TEMPLATE/release.md new file mode 100644 index 0000000..3386f44 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/release.md @@ -0,0 +1,36 @@ +--- +name: Release +about: Track a versioned release from prep to publish +title: "Release vX.Y.Z" +labels: documentation +assignees: "" +--- + +## Target version +- [ ] Version: `vX.Y.Z` +- [ ] Release date confirmed + +## Pre-release prep +- [ ] Create branch: `chore/-release-vX-Y-Z` +- [ ] Ensure `Cargo.toml` version is `X.Y.Z` +- [ ] Move `Unreleased` entries into `## [X.Y.Z] - YYYY-MM-DD` in `CHANGELOG.md` +- [ ] Include issue/PR references in changelog entries where possible +- [ ] Open release PR linked to this issue + +## Validation +- [ ] `cargo fmt --all -- --check` +- [ ] `cargo clippy --all-features -- -D warnings` +- [ ] `cargo test --all` +- [ ] `cargo test --no-default-features` +- [ ] `./scripts/release-check.sh` + +## Publish +- [ ] Merge release PR to `main` +- [ ] Create and push annotated tag: `vX.Y.Z` +- [ ] Confirm GitHub `Release` workflow passes on tag +- [ ] Confirm crate published to crates.io +- [ ] Confirm GitHub Release notes match changelog section + +## Post-release +- [ ] Restore/keep `## [Unreleased]` section in changelog +- [ ] Announce release notes diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..bdc846d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,99 @@ +name: Release + +on: + push: + tags: + - "v*" + +permissions: + contents: write + +jobs: + validate: + name: Validate release tag + runs-on: ubuntu-latest + + steps: + - name: Checkout sources + uses: actions/checkout@v4 + + - name: Install Rust (stable) + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + profile: minimal + components: clippy, rustfmt + + - name: Cache cargo registry + build + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-release-${{ hashFiles('**/Cargo.lock') }} + + - name: Run release preflight + run: ./scripts/release-check.sh + + publish: + name: Publish crate and GitHub release + runs-on: ubuntu-latest + needs: validate + + steps: + - name: Checkout sources + uses: actions/checkout@v4 + + - name: Install Rust (stable) + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + profile: minimal + + - name: Verify tag matches crate version + shell: bash + run: | + set -euo pipefail + tag="${GITHUB_REF_NAME}" + crate_version=$(cargo pkgid | sed -E 's|.*@([^ ]+)$|\1|') + + if [[ "${tag}" != "v${crate_version}" ]]; then + echo "Tag ${tag} does not match crate version v${crate_version}" >&2 + exit 1 + fi + + - name: Cargo publish (dry-run) + run: cargo publish --dry-run + + - name: Cargo publish + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: cargo publish + + - name: Build release notes from changelog + shell: bash + run: | + set -euo pipefail + tag="${GITHUB_REF_NAME#v}" + awk -v version="${tag}" ' + BEGIN { in_section=0 } + $0 ~ "^## \\[" version "\\]" { in_section=1; next } + /^## \[/ && in_section { exit } + in_section { print } + ' CHANGELOG.md > release-notes.md + + if [[ ! -s release-notes.md ]]; then + echo "No changelog section found for ${tag}" >&2 + exit 1 + fi + + - name: Create GitHub release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ github.ref_name }} + body_path: release-notes.md + draft: false + prerelease: false diff --git a/CHANGELOG.md b/CHANGELOG.md index 93242e8..7232e95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,8 @@ and its version numbers follow [Semantic Versioning](https://semver.org/). control/ticker context without carrying a `RuntimeGuard` field (`#21`). - New `axum` example showing how to run a web server under `ProcessManager` with graceful shutdown and reload handling (`#11`). +- Release process documentation and release issue template for standardized + versioned publish workflows (`#47`). ### Changed - `process_handle()` now returns `Arc` (cheap cloning, @@ -53,6 +55,8 @@ and its version numbers follow [Semantic Versioning](https://semver.org/). context-based runnable API covered (`#21`). - CI now executes the `axum` example to keep web-framework integration sample code validated (`#11`). +- Added tag-driven `Release` workflow to validate, publish to crates.io, and + create GitHub releases from changelog sections (`#47`). - Added `RestartSupervisor` with configurable exponential backoff to automatically restart failed child runnables (`#19`). diff --git a/docs/release-process.md b/docs/release-process.md new file mode 100644 index 0000000..8cd6411 --- /dev/null +++ b/docs/release-process.md @@ -0,0 +1,65 @@ +# Release Process + +This document defines the canonical release flow for `mrcrgl/processmanager-rs`. + +## Prerequisites +- GitHub access with tag/release permissions +- crates.io publish token configured as `CARGO_REGISTRY_TOKEN` in repository secrets +- Clean working tree + +## 1. Open a tracked release issue +Use the `Release` issue template and fill target version/date. + +## 2. Prepare release branch +Follow branch policy from `AGENTS.md`: + +```bash +git checkout main +git pull --ff-only origin main +git checkout -b chore/-release-vX-Y-Z +``` + +## 3. Prepare release PR +- Set `Cargo.toml` package version to the target `X.Y.Z` +- Move `CHANGELOG.md` entries from `## [Unreleased]` into: + - `## [X.Y.Z] - YYYY-MM-DD` +- Open PR with issue link (`Closes #`) + +## 4. Validate before merge +Run the required checks locally: + +```bash +./scripts/release-check.sh +``` + +This script enforces: +- `cargo fmt --all -- --check` +- `cargo clippy --all-features -- -D warnings` +- `cargo test --all` +- `cargo test --no-default-features` +- all maintained examples with bounded runtimes + +## 5. Merge and tag +After release PR is merged to `main`, create and push an annotated tag: + +```bash +git checkout main +git pull --ff-only origin main +git tag -a vX.Y.Z -m "vX.Y.Z" +git push origin vX.Y.Z +``` + +## 6. Automated publish + GitHub release +Tag push triggers `.github/workflows/release.yml`. + +The workflow: +1. re-runs release preflight checks +2. verifies tag `vX.Y.Z` matches crate version `X.Y.Z` +3. runs `cargo publish --dry-run` +4. publishes crate via `cargo publish` +5. creates GitHub Release notes from the matching `CHANGELOG.md` section + +## 7. Post-release checks +- Verify crates.io package page/version +- Verify GitHub Release body content +- Confirm `## [Unreleased]` remains ready for next cycle diff --git a/scripts/release-check.sh b/scripts/release-check.sh new file mode 100755 index 0000000..d0e1695 --- /dev/null +++ b/scripts/release-check.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -euo pipefail + +cargo fmt --all -- --check +cargo clippy --all-features -- -D warnings +cargo test --all +cargo test --no-default-features + +# Keep examples under CI-appropriate upper bounds. +timeout 30s cargo run --example simple +timeout 45s cargo run --example dynamic_add +timeout 30s cargo run --example restart_supervisor +timeout 30s cargo run --example runtime_context +timeout 30s cargo run --example axum