Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/scripts/extract-changelog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/usr/bin/env python3
"""Extract the latest version's release notes from docs/changelog/index.md.

Outputs JSON with "version", "date", and "body" fields for use in GitHub Actions.
"""

import json
import re
import sys
from pathlib import Path

CHANGELOG_PATH = Path("docs/changelog/index.md")


def extract_latest() -> dict | None:
"""Extract the latest release entry from the changelog."""
text = CHANGELOG_PATH.read_text(encoding="utf-8")

# Match the first version heading: ### VERSION <small>DATE</small> { id="..." }
pattern = re.compile(
r'^###\s+(\S+)\s+<small>(.*?)</small>\s*\{[^}]*\}\s*$',
re.MULTILINE,
)

match = pattern.search(text)
if not match:
return None

version = match.group(1)
date = match.group(2).strip()
start = match.end()

# Find the next version heading or the "---" separator
next_pattern = re.compile(
r'^###\s+\S+\s+<small>|^---\s*$',
re.MULTILINE,
)
next_match = next_pattern.search(text, start)
end = next_match.start() if next_match else len(text)

body = text[start:end].strip()

return {"version": version, "date": date, "body": body}


def main() -> None:
entry = extract_latest()
if not entry:
print("No version entry found in changelog", file=sys.stderr)
sys.exit(1)

json.dump(entry, sys.stdout, ensure_ascii=False)
print() # trailing newline


if __name__ == "__main__":
main()
87 changes: 87 additions & 0 deletions .github/workflows/draft-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: Draft Release Notes

on:
push:
branches:
- "main"
paths:
- "docs/changelog/index.md"
tags:
- "[0-9]+.[0-9]+.[0-9]+*"
workflow_dispatch:

permissions:
contents: write

jobs:
sync-release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Extract latest changelog entry
id: changelog
run: |
python3 .github/scripts/extract-changelog.py > /tmp/changelog.json

- name: Parse version and date
id: meta
run: |
VERSION=$(python3 -c "import json; print(json.load(open('/tmp/changelog.json'))['version'])")
DATE=$(python3 -c "import json; print(json.load(open('/tmp/changelog.json'))['date'])")
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "date=$DATE" >> "$GITHUB_OUTPUT"

- name: Set tag and draft flag
id: flags
run: |
if [[ "${{ github.ref_type }}" == "tag" ]]; then
echo "tag=${{ github.ref_name }}" >> "$GITHUB_OUTPUT"
echo "draft=false" >> "$GITHUB_OUTPUT"
echo "make_latest=true" >> "$GITHUB_OUTPUT"
else
echo "tag=${{ steps.meta.outputs.version }}" >> "$GITHUB_OUTPUT"
echo "draft=true" >> "$GITHUB_OUTPUT"
echo "make_latest=false" >> "$GITHUB_OUTPUT"
fi

- name: Write release notes to file
run: |
python3 -c "
import json
with open('/tmp/changelog.json') as f:
data = json.load(f)
with open('/tmp/release-body.md', 'w') as f:
f.write(data['body'])
"

- name: Create or update GitHub Release
env:
GH_TOKEN: ${{ github.token }}
run: |
TAG="${{ steps.flags.outputs.tag }}"
TITLE="${{ steps.meta.outputs.version }} – ${{ steps.meta.outputs.date }}"
NOTES_FILE="/tmp/release-body.md"
DRAFT="${{ steps.flags.outputs.draft }}"
MAKE_LATEST="${{ steps.flags.outputs.make_latest }}"

ARGS=(
--title "$TITLE"
--notes-file "$NOTES_FILE"
)

if [[ "$DRAFT" == "true" ]]; then
ARGS+=(--draft)
fi

if [[ "$MAKE_LATEST" == "true" ]]; then
ARGS+=(--latest)
fi

if gh release view "$TAG" &>/dev/null; then
echo "Release $TAG exists – editing"
gh release edit "$TAG" "${ARGS[@]}"
else
echo "Release $TAG does not exist – creating"
gh release create "$TAG" "${ARGS[@]}"
fi
16 changes: 0 additions & 16 deletions .github/workflows/release-drafter.yml

This file was deleted.

Loading