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
16 changes: 13 additions & 3 deletions .github/workflows/release-notes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ on:
tag:
description: 'Release tag (e.g., v0.3.3)'
required: true
target_ref:
description: 'Branch to check out and update when writing changelog changes'
default: main
required: true

permissions:
contents: write
Expand All @@ -18,15 +22,20 @@ jobs:
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event_name == 'release' && github.event.release.target_commitish || inputs.target_ref }}
fetch-depth: 0 # full history for tag detection and diff stats

- name: Resolve tag
id: tag
run: |
if [[ "${{ github.event_name }}" == "release" ]]; then
echo "tag=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
echo "target_ref=${{ github.event.release.target_commitish }}" >> "$GITHUB_OUTPUT"
echo "markdown_post_to=step-summary" >> "$GITHUB_OUTPUT"
else
echo "tag=${{ inputs.tag }}" >> "$GITHUB_OUTPUT"
echo "target_ref=${{ inputs.target_ref }}" >> "$GITHUB_OUTPUT"
echo "markdown_post_to=release,step-summary" >> "$GITHUB_OUTPUT"
fi

# ── Surface 1: GitHub Release body + step summary ──────────
Expand All @@ -39,7 +48,7 @@ jobs:
data-format: github-prs
head-ref: ${{ steps.tag.outputs.tag }}
release-tag: ${{ steps.tag.outputs.tag }}
post-to: release,step-summary
post-to: ${{ steps.tag.outputs.markdown_post_to }}
token: ${{ secrets.GITHUB_TOKEN }}

# ── Surface 2: Terminal-formatted release asset ────────────
Expand All @@ -59,6 +68,7 @@ jobs:

# ── Surface 3: Compact notes for changelog ─────────────────
- name: Generate release notes (compact)
if: github.event_name == 'workflow_dispatch'
uses: ./
id: compact
with:
Expand All @@ -72,7 +82,7 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}

- name: Commit changelog updates
if: steps.compact.outcome == 'success'
if: github.event_name == 'workflow_dispatch' && steps.compact.outcome == 'success'
run: |
if git diff --quiet CHANGELOG.md 2>/dev/null; then
echo "No changelog changes to commit."
Expand All @@ -81,5 +91,5 @@ jobs:
git config user.email "github-actions[bot]@users.noreply.github.com"
git add CHANGELOG.md
git commit -m "docs: update CHANGELOG for ${{ steps.tag.outputs.tag }}"
git push
git push origin "HEAD:${{ steps.tag.outputs.target_ref }}"
fi
29 changes: 24 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -144,15 +144,34 @@ release: build publish
gh-release:
@VERSION=$$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/'); \
PROJECT=$$(grep -m1 '^name = ' pyproject.toml | sed 's/name = "\(.*\)"/\1/'); \
TAG="v$$VERSION"; \
NOTES="site/content/releases/$$VERSION.md"; \
if [ -n "$$(git status --porcelain)" ]; then echo "Error: working tree is not clean."; exit 1; fi; \
if [ ! -f "$$NOTES" ]; then echo "Error: $$NOTES not found"; exit 1; fi; \
echo "Creating release v$$VERSION for $$PROJECT..."; \
git push origin main 2>/dev/null || true; \
git push origin v$$VERSION 2>/dev/null || true; \
awk '/^---$$/{c++;next}c>=2' "$$NOTES" | gh release create v$$VERSION \
if gh release view "$$TAG" >/dev/null 2>&1; then echo "Error: release $$TAG already exists"; exit 1; fi; \
git fetch origin main --tags; \
LOCAL=$$(git rev-parse HEAD); \
REMOTE=$$(git rev-parse origin/main); \
if [ "$$LOCAL" != "$$REMOTE" ]; then \
echo "Error: HEAD must match origin/main before releasing."; \
echo "HEAD=$$LOCAL"; \
echo "origin/main=$$REMOTE"; \
exit 1; \
fi; \
REMOTE_TAG=$$(git ls-remote origin "refs/tags/$$TAG" | awk '{print $$1}'); \
if [ -n "$$REMOTE_TAG" ] && [ "$$REMOTE_TAG" != "$$LOCAL" ]; then \
echo "Error: remote $$TAG points at $$REMOTE_TAG, not $$LOCAL"; \
exit 1; \
fi; \
git tag -f "$$TAG" HEAD; \
if [ -z "$$REMOTE_TAG" ]; then git push origin "$$TAG"; fi; \
echo "Creating release $$TAG for $$PROJECT..."; \
awk '/^---$$/{c++;next}c>=2' "$$NOTES" | gh release create "$$TAG" \
--verify-tag \
--target main \
--title "$$PROJECT $$VERSION" \
-F -; \
echo "✓ GitHub release v$$VERSION created (PyPI publish will run via workflow)"; \
echo "✓ GitHub release $$TAG created (PyPI publish will run via workflow)"; \
$(MAKE) action-tag

# Move the floating major action tag so `uses: lbliii/kida@v0` tracks the latest release.
Expand Down
35 changes: 35 additions & 0 deletions docs/stability-gate.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,41 @@ The package smoke test verifies:
- component metadata
- sandbox denial for blocked reflection attributes

## Release Automation Gate

Before running the release target, merge the release-prep PR and work from a
clean checkout whose `HEAD` matches `origin/main`. The release target expects
the version in `pyproject.toml` to match a source page at
`site/content/releases/<version>.md` and creates `v<version>` from the merged
main commit:

```bash
make gh-release
```

The target fails if the worktree is dirty, if `HEAD` differs from `origin/main`,
if the GitHub release already exists, or if an existing remote version tag points
at a different commit. It pushes the version tag, creates the GitHub release from
the curated site release notes, and then moves the floating major action tag
with `make action-tag`.

After `make gh-release`, verify:

- `refs/heads/main`, `refs/tags/v<version>`, and `refs/tags/v<major>` point at
the same release commit
- the GitHub release body still contains the curated release notes
- the `Upload Python Package` release workflow succeeded
- `https://pypi.org/pypi/kida-templates/<version>/json` returns the released
wheel and sdist metadata
- the docs release page is reachable under
`https://lbliii.github.io/kida/releases/<version>/`

The `Release Notes` workflow dogfoods Kida's release-note templates on release
events, but release events do not rewrite the curated GitHub release body or
commit changelog changes. Use its manual `workflow_dispatch` mode when a
maintainer explicitly wants to regenerate release-note/changelog output for a
tag and target branch.

## Benchmark Evidence

Linux 3.14t benchmark baselines are the performance comparison baseline. Darwin
Expand Down
Loading