Skip to content

Commit ad18484

Browse files
mabry1985Josh Mabryclaude
authored
ci: release-notes + Discord release cadence via protoLabsAI/release-tools (#44)
Onboards the plugin onto the fleet release ritual like the rest of the fleet: a .github/workflows/release.yml that, on a `chore: release vX.Y.Z` commit to main (or manual dispatch), tags the current version (canonical = protoagent.plugin.yaml, mirrored by pyproject), generates LLM-themed release notes for the range since the previous tag via protoLabsAI/release-tools@v1, posts a Discord embed, and sets the GitHub release body. Idempotent (skips an already-tagged version); runs on the owned Namespace runner (workspace-config conformant). Needs org secrets GATEWAY_API_KEY + DISCORD_RELEASE_WEBHOOK. The plugin's last tag is v0.4.1; the first automated cut (v0.18.0) batches everything since — incl. the CI-fix budget, pre-PR gate, auto-rebase, and the self-improving loop-retro flywheel. README gains a Releasing section + retro.py row. Co-authored-by: Josh Mabry <artificialcitizens@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 3354bca commit ad18484

2 files changed

Lines changed: 107 additions & 1 deletion

File tree

.github/workflows/release.yml

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
name: Release
2+
3+
# The fleet release ritual via protoLabsAI/release-tools: tag → LLM-themed release
4+
# notes → Discord embed → GitHub release body. Fires on a deliberate
5+
# `chore: release vX.Y.Z` commit to main (the version in protoagent.plugin.yaml /
6+
# pyproject.toml is bumped in lockstep by feature PRs; the release commit is the cut
7+
# that batches them into one release) or a manual dispatch. Idempotent: a re-run on a
8+
# version whose tag already exists skips the tag + release-creation steps.
9+
#
10+
# Requires two secrets (org-level across the fleet): GATEWAY_API_KEY (protoLabs LLM
11+
# gateway, for the notes) and DISCORD_RELEASE_WEBHOOK (the release channel).
12+
13+
on:
14+
push:
15+
branches: [main]
16+
workflow_dispatch:
17+
18+
permissions:
19+
contents: write # create the tag + the GitHub release
20+
21+
jobs:
22+
release:
23+
name: Tag + release notes + Discord
24+
runs-on: ${{ vars.NSC_RUNNER || 'namespace-profile-protolabs-linux' }}
25+
timeout-minutes: 10
26+
if: >-
27+
github.repository == 'protoLabsAI/projectBoard-plugin' &&
28+
(github.event_name == 'workflow_dispatch' ||
29+
startsWith(github.event.head_commit.message, 'chore: release v'))
30+
steps:
31+
- uses: actions/checkout@v4
32+
with:
33+
fetch-depth: 0 # full history: the notes diff range + previous-tag detection
34+
token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
35+
36+
- name: Resolve version + previous tag
37+
id: v
38+
run: |
39+
# The manifest version is canonical (pyproject mirrors it via the lockstep test).
40+
VERSION=$(grep -E '^version:' protoagent.plugin.yaml | head -1 | sed -E 's/^version:[[:space:]]*//')
41+
TAG="v$VERSION"
42+
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
43+
# Previous release = the latest existing vX.Y.Z tag (this version isn't tagged
44+
# yet); fall back to the root commit for the very first automated release.
45+
PREV=$(git tag --sort=-v:refname | grep -E '^v[0-9]+\.' | head -1)
46+
[ -z "$PREV" ] && PREV=$(git rev-list --max-parents=0 HEAD | head -1)
47+
echo "prev=$PREV" >> "$GITHUB_OUTPUT"
48+
if git rev-parse "$TAG" >/dev/null 2>&1; then
49+
echo "exists=true" >> "$GITHUB_OUTPUT"
50+
echo "::notice::$TAG already tagged — skipping (idempotent re-run)."
51+
else
52+
echo "exists=false" >> "$GITHUB_OUTPUT"
53+
fi
54+
echo "Releasing $TAG (notes from $PREV)"
55+
56+
- name: Create tag + GitHub release
57+
if: steps.v.outputs.exists == 'false'
58+
env:
59+
GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
60+
run: |
61+
git tag "${{ steps.v.outputs.tag }}"
62+
git push origin "${{ steps.v.outputs.tag }}"
63+
gh release create "${{ steps.v.outputs.tag }}" \
64+
--title "${{ steps.v.outputs.tag }}" \
65+
--notes "Generating release notes…"
66+
67+
- name: Generate notes + post to Discord
68+
if: steps.v.outputs.exists == 'false'
69+
uses: protoLabsAI/release-tools@v1
70+
with:
71+
version: ${{ steps.v.outputs.tag }}
72+
previous-version: ${{ steps.v.outputs.prev }}
73+
out-file: .release-notes.md
74+
env:
75+
GATEWAY_API_KEY: ${{ secrets.GATEWAY_API_KEY }}
76+
DISCORD_RELEASE_WEBHOOK: ${{ secrets.DISCORD_RELEASE_WEBHOOK }}
77+
78+
- name: Set the GitHub release body to the generated notes
79+
if: steps.v.outputs.exists == 'false'
80+
env:
81+
GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
82+
run: |
83+
if [ -s .release-notes.md ]; then
84+
gh release edit "${{ steps.v.outputs.tag }}" --notes-file .release-notes.md
85+
else
86+
echo "::warning::no notes file produced — leaving the placeholder release body."
87+
fi

README.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,26 @@ project_board:
123123
| `worktree.py` | per-feature worktree lifecycle, scoped coder dispatch, `open_pr` |
124124
| `api.py` | the HTTP API + the `/webhook/pr` Done edge (HMAC-verified) |
125125
| `board_view.py` | the Kanban/list console view |
126-
| `subagents.py` + `skills/` | the decompose/antagonist planning layer |
126+
| `retro.py` | loop-retro mining: bead attempt/outcome history → recurring failure classes (the self-improving flywheel) |
127+
| `subagents.py` + `skills/` | the `decompose`/`antagonist` planning layer + the `loop-retro` distill skill |
127128
| `__init__.py` | `register()` — wires it all |
128129

129130
Ships **disabled**; nothing runs until you enable it and declare a coder.
131+
132+
## Releasing
133+
134+
Releases follow the fleet cadence via [`protoLabsAI/release-tools`](https://github.com/protoLabsAI/release-tools):
135+
**tag → LLM-themed release notes → Discord embed → GitHub release body**, wired in
136+
`.github/workflows/release.yml`.
137+
138+
The version lives in `protoagent.plugin.yaml` + `pyproject.toml` (kept in lockstep by a
139+
test) and is bumped per feature PR. To **cut a release** that batches the bumped changes
140+
since the last tag, either:
141+
142+
- push a `chore: release vX.Y.Z` commit to `main`, or
143+
- run the **Release** workflow manually — `gh workflow run release.yml` (or the Actions tab).
144+
145+
It tags the current version, generates notes for the range since the previous tag, posts
146+
them to the release Discord channel, and sets the GitHub release body — idempotent
147+
(a re-run on an already-tagged version is a no-op). Requires the org secrets
148+
`GATEWAY_API_KEY` + `DISCORD_RELEASE_WEBHOOK`.

0 commit comments

Comments
 (0)