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
17 changes: 16 additions & 1 deletion .github/workflows/release-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,24 @@ jobs:
PR_BODY_FILE=$(mktemp)
cat > "$PR_BODY_FILE" <<'EOF'
<!-- release-notes -->
<!-- release-notes:todo — remove this line once the notes below are final. While this marker is present, merging falls back to GitHub's auto-generated notes. -->
## Release notes

_Replace this paragraph with a short narrative for the release. If left unchanged, GitHub's auto-generated notes will be used instead._
### Highlights

<!-- One narrative bullet per theme: what changed and why it matters to a user. Group related work; don't echo commit messages. -->

### Behavior changes to know about

<!-- Observable deltas: changed defaults, renamed output, compatibility notes. Omit the section when empty. -->

### Fixes

<!-- Bugs fixed: the observable symptom, then the cause. Omit the section when empty. -->

### Internal changes

<!-- Refactors, test and infrastructure work. Omit the section when empty. -->
EOF

PR_URL=$(gh pr create \
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
--json body \
--jq '.[0].body // ""')

if [ -n "$PR_BODY" ] && ! echo "$PR_BODY" | grep -q "Replace this paragraph"; then
if [ -n "$PR_BODY" ] && ! echo "$PR_BODY" | grep -q "release-notes:todo"; then
echo "Using release PR body for notes."
printf '%s\n' "$PR_BODY" > /tmp/release-notes.md
else
Expand Down
32 changes: 32 additions & 0 deletions tests/release-workflows.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { describe, expect, test } from "bun:test";
import fs from "node:fs";
import path from "node:path";

const REPO_ROOT = path.resolve(import.meta.dir, "..");

describe("release notes workflow contract", () => {
const releasePrWorkflow = fs.readFileSync(
path.join(REPO_ROOT, ".github/workflows/release-pr.yml"),
"utf8",
);
const releaseWorkflow = fs.readFileSync(
path.join(REPO_ROOT, ".github/workflows/release.yml"),
"utf8",
);

test("release PRs start with the structured notes skeleton", () => {
expect(releasePrWorkflow).toContain("<!-- release-notes -->");
expect(releasePrWorkflow).toContain("<!-- release-notes:todo");
expect(releasePrWorkflow).toContain("## Release notes");
expect(releasePrWorkflow).toContain("### Highlights");
expect(releasePrWorkflow).toContain("### Behavior changes to know about");
expect(releasePrWorkflow).toContain("### Fixes");
expect(releasePrWorkflow).toContain("### Internal changes");
expect(releasePrWorkflow).not.toContain("Replace this paragraph");
});

test("release creation falls back while the todo marker remains", () => {
expect(releaseWorkflow).toContain('grep -q "release-notes:todo"');
expect(releaseWorkflow).not.toContain("Replace this paragraph");
});
});
Loading