Skip to content

Commit ab0ad80

Browse files
committed
ci: release automation — PR template, Prepare release workflow, RELEASE_NOTES body
- PR template: Summary, Test plan, Closes #N - Prepare release: workflow_dispatch to tag from main (version input) - Release: populate body from RELEASE_NOTES.md section via extract_release_notes.py - docs/release-process.md: release steps and automation summary - README: link release process and PR template Made-with: Cursor
1 parent 164b9dc commit ab0ad80

File tree

6 files changed

+184
-2
lines changed

6 files changed

+184
-2
lines changed

.github/pull_request_template.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
## Summary
2+
3+
<!-- What does this PR do? -->
4+
5+
## Test plan
6+
7+
- [ ] `cargo test`
8+
- [ ] `cargo clippy --all-targets`
9+
- <!-- Add any manual or integration steps -->
10+
11+
## Closes
12+
13+
<!-- Link issues this PR ships: Closes #28, #29 -->
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/usr/bin/env python3
2+
"""Extract the release notes section for a given version from RELEASE_NOTES.md.
3+
4+
Usage:
5+
python3 .github/scripts/extract_release_notes.py 0.5.27
6+
7+
Prints the section for "# Release Notes - v0.5.27" up to the next "---" or
8+
"# Release Notes - v". If no section is found, exits 1 (caller can use a fallback).
9+
"""
10+
11+
from __future__ import annotations
12+
13+
import re
14+
import sys
15+
from pathlib import Path
16+
17+
18+
def main() -> int:
19+
if len(sys.argv) != 2:
20+
print("Usage: extract_release_notes.py <version>", file=sys.stderr)
21+
return 1
22+
version = sys.argv[1].strip()
23+
if version.startswith("v"):
24+
version = version[1:]
25+
path = Path("RELEASE_NOTES.md")
26+
if not path.exists():
27+
print("RELEASE_NOTES.md not found", file=sys.stderr)
28+
return 1
29+
text = path.read_text()
30+
# Match "# Release Notes - v0.5.27" or "# Release Notes - v0.5.27 "
31+
pattern = rf"^# Release Notes - v{re.escape(version)}\s*$"
32+
match = re.search(pattern, text, re.MULTILINE)
33+
if not match:
34+
return 1
35+
start = match.end()
36+
# End at next "---" (horizontal rule) or next "# Release Notes - v"
37+
end_match = re.search(r"\n---\s*\n|^# Release Notes - v", text[start:], re.MULTILINE)
38+
end = start + end_match.start() if end_match else len(text)
39+
section = text[start:end].strip()
40+
if not section:
41+
return 1
42+
print(section)
43+
return 0
44+
45+
46+
if __name__ == "__main__":
47+
sys.exit(main())
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Manual workflow: create and push a release tag from main.
2+
# Prerequisites: Cargo.toml and charts/diffscope/Chart.yaml appVersion must already
3+
# be bumped and merged to main. Then run this workflow with the same version.
4+
# This pushes the tag and triggers the Release workflow (builds, release notes, assets).
5+
name: Prepare release
6+
7+
on:
8+
workflow_dispatch:
9+
inputs:
10+
version:
11+
description: 'Release version (e.g. 0.5.28)'
12+
required: true
13+
type: string
14+
15+
permissions:
16+
contents: write
17+
18+
jobs:
19+
tag-and-push:
20+
name: Tag and push
21+
runs-on: ubuntu-latest
22+
steps:
23+
- name: Checkout main
24+
uses: actions/checkout@v5
25+
with:
26+
ref: main
27+
fetch-depth: 0
28+
29+
- name: Normalize version input
30+
id: version
31+
run: |
32+
v="${{ inputs.version }}"
33+
v="${v#v}"
34+
echo "version=$v" >> "$GITHUB_OUTPUT"
35+
echo "tag=v$v" >> "$GITHUB_OUTPUT"
36+
37+
- name: Verify Cargo.toml version matches
38+
run: python3 .github/scripts/check_version_sync.py --tag "v${{ steps.version.outputs.version }}"
39+
40+
- name: Verify Chart appVersion matches
41+
run: |
42+
chart_version=$(python3 -c "
43+
import re
44+
from pathlib import Path
45+
c = Path('charts/diffscope/Chart.yaml').read_text()
46+
m = re.search(r\"\"\"appVersion:\s*['\"]?(.*?)['\"]?\s*$\"\"\", c, re.MULTILINE)
47+
print(m.group(1).strip()) if m else exit(1)
48+
")
49+
expected="${{ steps.version.outputs.version }}"
50+
if [ "$chart_version" != "$expected" ]; then
51+
echo "Chart appVersion ($chart_version) does not match input ($expected). Bump charts/diffscope/Chart.yaml and merge to main first."
52+
exit 1
53+
fi
54+
55+
- name: Create and push tag
56+
run: |
57+
git config user.name "github-actions[bot]"
58+
git config user.email "github-actions[bot]@users.noreply.github.com"
59+
tag="${{ steps.version.outputs.tag }}"
60+
if git rev-parse "$tag" >/dev/null 2>&1; then
61+
echo "Tag $tag already exists."
62+
exit 1
63+
fi
64+
git tag -a "$tag" -m "Release $tag"
65+
git push origin "$tag"

.github/workflows/release.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ jobs:
2929
id: get_version
3030
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
3131

32+
- name: Extract release notes from RELEASE_NOTES.md
33+
id: release_notes
34+
run: |
35+
v="${GITHUB_REF#refs/tags/v}"
36+
python3 .github/scripts/extract_release_notes.py "$v" > release_notes_section.txt || true
37+
echo "body<<RELEASE_NOTES_EOF" >> "$GITHUB_OUTPUT"
38+
cat release_notes_section.txt >> "$GITHUB_OUTPUT" || true
39+
echo "RELEASE_NOTES_EOF" >> "$GITHUB_OUTPUT"
40+
3241
- name: Verify release metadata matches tag
3342
env:
3443
TAG_NAME: ${{ steps.get_version.outputs.VERSION }}
@@ -55,7 +64,7 @@ jobs:
5564
body: |
5665
# DiffScope ${{ steps.get_version.outputs.VERSION }}
5766
58-
See [CHANGELOG.md](https://github.com/evalops/diffscope/blob/main/CHANGELOG.md) for details.
67+
${{ steps.release_notes.outputs.body }}
5968
6069
## 🚀 Installation
6170

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1002,7 +1002,7 @@ The summary includes:
10021002

10031003
Contributions are welcome! Please open an issue first to discuss what you would like to change. Enhancement backlog and triage: see [docs/ROADMAP.md](docs/ROADMAP.md) and `gh issue list --label "priority: high"`.
10041004

1005-
**PR workflow:** Open a PR → ensure CI is green (version, lint, security, test, mutation, review) → merge when ready. Use a short test plan in the PR description. Small, focused PRs are preferred.
1005+
**PR workflow:** Open a PR → ensure CI is green (version, lint, security, test, mutation, review) → merge when ready. Use a short test plan in the PR description. Small, focused PRs are preferred. Use the PR template (Summary, Test plan, **Closes #N**). **Release process:** [docs/release-process.md](docs/release-process.md) (version bump, RELEASE_NOTES, Prepare release workflow).
10061006

10071007
### Local Development Checks
10081008

docs/release-process.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Release process
2+
3+
This doc and GitHub Actions make releases repeatable with minimal manual steps.
4+
5+
## One-time per release (on main)
6+
7+
1. **Bump version** in a PR (or directly on main):
8+
- `Cargo.toml``[package].version = "0.5.28"`
9+
- `charts/diffscope/Chart.yaml``appVersion: "0.5.28"`
10+
2. **Update release notes** in the same PR (or a follow-up):
11+
- In `RELEASE_NOTES.md`, add a new section at the top:
12+
```markdown
13+
# Release Notes - v0.5.28
14+
📅 **Release Date**: YYYY-MM-DD
15+
## Summary
16+
- Bullet points for this release.
17+
## Full Changelog
18+
[v0.5.27...v0.5.28](https://github.com/evalops/diffscope/compare/v0.5.27...v0.5.28)
19+
---
20+
```
21+
- Optionally update `docs/ROADMAP.md` “Shipped” section.
22+
3. **Merge to main** (e.g. “chore: bump version to 0.5.28”).
23+
24+
## Create the release (automated)
25+
26+
4. **Run the “Prepare release” workflow**
27+
- In the repo: **Actions****Prepare release****Run workflow**.
28+
- Enter the **version** (e.g. `0.5.28`). Must match `Cargo.toml` and `Chart.yaml` on main.
29+
- The workflow creates tag `v0.5.28`, pushes it, and triggers the **Release** workflow.
30+
5. **Release workflow** (runs on tag push):
31+
- Verifies `Cargo.toml` and Chart `appVersion` match the tag.
32+
- Extracts the section for this version from `RELEASE_NOTES.md` and uses it in the GitHub Release body.
33+
- Builds binaries for Linux/macOS/Windows, builds Docker image, creates the release and uploads assets.
34+
35+
## PR workflow (reminder)
36+
37+
- Use the **PR template** (Summary, Test plan, **Closes #N**).
38+
- Linking “Closes #28” in the PR body auto-closes the issue when the PR is merged.
39+
40+
## Automation summary
41+
42+
| Step | Automation |
43+
|------|------------|
44+
| Version sync (CI) | `check_version_sync.py` fails if `Cargo.toml` is behind latest tag. |
45+
| Tag and trigger release | **Prepare release** workflow (manual run with version input). |
46+
| Release body | **Release** workflow reads `RELEASE_NOTES.md` for the tagged version. |
47+
| Binaries + Docker | **Release** workflow builds and uploads. |
48+
| Issue close | Add “Closes #N” in PR body. |

0 commit comments

Comments
 (0)