Skip to content
Open
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
gh aw compile drafter review fix queue-triage ci-triage --approve
gh aw compile drafter review fix release queue-triage ci-triage --approve

- name: Check for lock drift
run: |
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/install-labels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ jobs:
{ name: 'agent/fix-working', description: 'The fix agent is actively working on this PR', color: 'FBCA04' },
{ name: 'agent/workflow-edits-allowed', description: 'Pre-authorizes agent runs to edit protected files without the request_review gate', color: '5319E7' },
{ name: 'agent/flake-tracker', description: 'Marks the CI flake tracker issue the merge queue analyzer maintains', color: '1D76DB' },
{ name: 'release', description: 'Release PR created by the release agent', color: '0075CA' },
];

const { owner, repo } = context.repo;
Expand Down
39 changes: 39 additions & 0 deletions .github/workflows/publish-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Publish Release
on:
pull_request:
types: [closed]

# When a release PR is merged, create the corresponding GitHub release
# with the release notes from the PR body.
jobs:
publish:
if: |
github.event.pull_request.merged == true &&
contains(github.event.pull_request.labels.*.name, 'release') &&
startsWith(github.event.pull_request.head.ref, 'release/')
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- name: Extract version from branch name
id: version
env:
HEAD_REF: ${{ github.event.pull_request.head.ref }}
run: |
version="${HEAD_REF#release/}"
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "Publishing release: $version"

- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ steps.version.outputs.version }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
run: |
pr_body=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json body --jq .body)

gh release create "$VERSION" \
--repo "$REPO" \
--title "Release $VERSION" \
--notes "$pr_body"
1,870 changes: 1,870 additions & 0 deletions .github/workflows/release.lock.yml

Large diffs are not rendered by default.

89 changes: 89 additions & 0 deletions .github/workflows/release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
description: |
Automated release agent for this repository. Runs weekly to create a
release PR with release notes from git history since the last release.
The PR requires human review and approval before merge. When merged,
a separate workflow creates the GitHub release with the tag.

on:
schedule: weekly on monday
workflow_dispatch:

permissions:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Do we need this stuff btw? doesn't the MCP server allow reads)

contents: read
issues: read
pull-requests: read

model: claude-sonnet-4-5-20250929
engine:
id: claude
tools:
bash: ["*"]
github:
toolsets: [default]

safe-outputs:
github-app:
client-id: ${{ vars.GH_AW_APP_CLIENT_ID }}
private-key: ${{ secrets.GH_AW_APP_PRIVATE_KEY }}
create-pull-request:
max: 1
branch-prefix: release/
Comment on lines +29 to +31
allowed-files:
- RELEASE-NOTES.md
noop:
missing-data:
---

# Release Agent

This workflow runs weekly to create a release PR.

## Your task

1. **Check for existing release PRs**: Before doing anything else, run
`gh pr list --label release --state open --json number --jq length`
to check if a release PR is already open. If the count is non-zero,
use `noop` to report that a release PR is already open.

2. **Fetch full history**: The checkout is shallow by default. Run

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh, really? Kind of weird, can we just configure that in GH-AW deterministically?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked; there's no frontmatter option for fetch depth. The compiler hardcodes fetch-depth: 1 in all compiled workflows (drafter, review, fix all have it too). The git fetch --unshallow --tags in the agent instructions is the workaround for now.

`git fetch --unshallow --tags` so that all tags and commit history
are available.

3. **Determine the next version**: Run `scripts/next-version.sh` to get
the next version string (e.g., `v0.3.0`).

4. **Generate release notes**: Use GitHub's release notes generation API
as a starting point:
```bash
gh api repos/$GITHUB_REPOSITORY/releases/generate-notes \
-f tag_name="<next-version>" \
-f previous_tag_name="<last-tag>" \
--jq .body
```
Then refine the output: group changes by category (Features, Bug Fixes,
Documentation, Dependencies, Other), add a brief summary at the top,
and highlight any breaking changes.

5. **Write RELEASE-NOTES.md**: Write the final release notes to
`RELEASE-NOTES.md` at the repository root. This file is overwritten
each release.

6. **Create the release PR**: Use the `create-pull-request` safe-output
with:
- **Title**: `Release <version>` (e.g., "Release v0.3.0")
- **Branch**: `<version>` (e.g., "v0.3.0") — the `release/` prefix
is added automatically by the branch-prefix setting
- **Body**: The release notes from RELEASE-NOTES.md
- **Labels**: `release`

7. **Handle edge cases**:
- If there are no changes since the last release, use `noop`
- If you cannot determine the version or history, use `missing-data`

## Constraints

- Never push directly to the default branch
- Only modify RELEASE-NOTES.md — do not touch other files
- Always include "Generated-by: AI" in the PR body
- Require human review for all releases
1 change: 1 addition & 0 deletions aw.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ includes:
- .github/workflows/drafter.md
- .github/workflows/review.md
- .github/workflows/fix.md
- .github/workflows/release.md
- .github/workflows/merge.yml
- .github/workflows/install-labels.yml
- .github/workflows/upgrade.yml
2 changes: 1 addition & 1 deletion justfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ setup:

# Compile all gh-aw workflow .md sources to .lock.yml (run `just setup` first).
compile:
gh aw compile drafter review fix queue-triage ci-triage --approve
gh aw compile drafter review fix release queue-triage ci-triage --approve

# Setup + compile in one step.
all: setup compile
5 changes: 5 additions & 0 deletions scripts/install-labels.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ const LABELS = [
description: 'Marks the CI flake tracker issue the merge queue analyzer maintains',
color: '1D76DB', // blue
},
{
name: 'release',
description: 'Release PR created by the release agent',
color: '0075CA', // blue
},
];

/**
Expand Down
15 changes: 15 additions & 0 deletions scripts/next-version.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/bash
# Determine the next release version by reading git tags.
# This project uses v0.x.0 minor releases.
set -euo pipefail

latest=$(git tag --list 'v*' --sort=-v:refname | head -1)

if [ -z "$latest" ]; then
echo "v0.1.0"
exit 0
fi

# Strip leading 'v', split on '.', bump minor, reset patch
IFS='.' read -r major minor _patch <<< "${latest#v}"
echo "v${major}.$((minor + 1)).0"
Loading