From daad50325b2ca267294164ae271de83d66bddc23 Mon Sep 17 00:00:00 2001 From: archipelago Date: Sun, 17 May 2026 18:59:12 -0400 Subject: [PATCH] chore(release): require curated release notes --- docs/RELEASE_NOTES_BACKLOG.md | 15 +++++++++++ scripts/check-release-manifest.sh | 44 ++++++++++++++++++++++++++----- scripts/create-release.sh | 38 ++++++++------------------ 3 files changed, 63 insertions(+), 34 deletions(-) create mode 100644 docs/RELEASE_NOTES_BACKLOG.md diff --git a/docs/RELEASE_NOTES_BACKLOG.md b/docs/RELEASE_NOTES_BACKLOG.md new file mode 100644 index 00000000..554fb54f --- /dev/null +++ b/docs/RELEASE_NOTES_BACKLOG.md @@ -0,0 +1,15 @@ +# Release Notes Backlog + +## Next Release Required Work + +- Backfill missing or thin historical release notes before cutting the next release. +- Audit every `CHANGELOG.md` section from `v1.7.44-alpha` through the current release. +- Replace raw commit-hash entries with user/operator-facing bullets that explain behavior changes, operational impact, validation, and known limitations. +- Ensure `releases/manifest.json` changelog entries come from curated `CHANGELOG.md` notes only. + +## Release Note Policy + +- Every release must have at least three curated bullets. +- Raw `git log --oneline` output is not acceptable release documentation. +- Notes should answer what changed, why it matters, what operators should expect, and any known limitations. +- `scripts/check-release-manifest.sh` is the enforcement gate before publishing artifacts. diff --git a/scripts/check-release-manifest.sh b/scripts/check-release-manifest.sh index d7463def..f5f657f1 100755 --- a/scripts/check-release-manifest.sh +++ b/scripts/check-release-manifest.sh @@ -1,7 +1,7 @@ #!/bin/bash # Validate releases/manifest.json: # - version matches core/archipelago/Cargo.toml -# - changelog is non-empty (release notes are mandatory per product policy) +# - changelog contains curated release notes, not raw git log output # - every component's download_url exists on disk and matches sha256/size # # Run on every push from CI, and also locally before publishing a release: @@ -30,12 +30,42 @@ if [ "$MANIFEST_VERSION" != "$CARGO_VERSION" ]; then fi ok "version matches: $MANIFEST_VERSION" -# Release notes mandatory — ships stuff nobody can read otherwise. -CHANGELOG_COUNT=$(python3 -c "import json; print(len(json.load(open('$MANIFEST'))['changelog']))") -if [ "$CHANGELOG_COUNT" -eq 0 ]; then - fail "changelog is empty — every release MUST have release notes" -fi -ok "changelog has $CHANGELOG_COUNT lines" +# Release notes mandatory — ships stuff nobody can read otherwise. Require +# curated user/operator-facing notes and reject raw `git log --oneline` output. +NOTES_CHECK=$(python3 - "$MANIFEST" <<'PY' +import json +import re +import sys + +manifest = sys.argv[1] +notes = json.load(open(manifest)).get("changelog", []) +if len(notes) < 3: + print(f"FAIL: changelog has {len(notes)} lines; need at least 3 curated release-note bullets") + sys.exit(0) + +bad = [] +for note in notes: + text = str(note).strip() + if not text: + bad.append("empty release-note entry") + if len(text) < 40: + bad.append(f"too short: {text!r}") + if re.match(r"^[0-9a-f]{7,40}\s+", text): + bad.append(f"raw commit hash entry: {text!r}") + if re.match(r"^(feat|fix|chore|docs|test|refactor|build|ci|perf)(\([^)]+\))?:\s", text): + bad.append(f"raw conventional-commit entry: {text!r}") + +if bad: + print("FAIL: release notes must be curated user/operator-facing bullets, not raw git log lines:\n" + "\n".join(bad)) +else: + print(f"OK: changelog has {len(notes)} curated lines") +PY +) +case "$NOTES_CHECK" in + OK:*) ok "${NOTES_CHECK#OK: }" ;; + FAIL:*) fail "${NOTES_CHECK#FAIL: }" ;; + *) fail "unexpected release-note validation output: $NOTES_CHECK" ;; +esac # Each component: the artifact on disk under releases/v/ must match # the declared sha256 and size_bytes. diff --git a/scripts/create-release.sh b/scripts/create-release.sh index 0d177b0a..86ab0f7f 100755 --- a/scripts/create-release.sh +++ b/scripts/create-release.sh @@ -128,36 +128,20 @@ cd "$PROJECT_ROOT/neode-ui" npm run build 2>&1 | tail -3 cd "$PROJECT_ROOT" -echo "[4/7] Generating changelog..." -# Get commits since last tag (or last 50 if no tags) -LAST_TAG=$(git -C "$PROJECT_ROOT" describe --tags --abbrev=0 2>/dev/null || echo "") -if [ -n "$LAST_TAG" ]; then - GIT_LOG=$(git -C "$PROJECT_ROOT" log "$LAST_TAG"..HEAD --oneline --no-merges 2>/dev/null || echo "") -else - GIT_LOG=$(git -C "$PROJECT_ROOT" log --oneline --no-merges -50 2>/dev/null || echo "") -fi +echo "[4/7] Validating curated changelog..." -# Create/update CHANGELOG.md entry CHANGELOG_FILE="$PROJECT_ROOT/CHANGELOG.md" RELEASE_DATE=$(date +%Y-%m-%d) -CHANGELOG_ENTRY="## v${VERSION} (${RELEASE_DATE}) - -$GIT_LOG -" - -if [ -f "$CHANGELOG_FILE" ]; then - # Prepend new entry after the first line (title) - EXISTING=$(cat "$CHANGELOG_FILE") - FIRST_LINE=$(head -1 "$CHANGELOG_FILE") - REST=$(tail -n +2 "$CHANGELOG_FILE") - echo "$FIRST_LINE" > "$CHANGELOG_FILE" - echo "" >> "$CHANGELOG_FILE" - echo "$CHANGELOG_ENTRY" >> "$CHANGELOG_FILE" - echo "$REST" >> "$CHANGELOG_FILE" -else - echo "# Changelog" > "$CHANGELOG_FILE" - echo "" >> "$CHANGELOG_FILE" - echo "$CHANGELOG_ENTRY" >> "$CHANGELOG_FILE" +if [ ! -f "$CHANGELOG_FILE" ] || ! grep -q "^## v${VERSION} (" "$CHANGELOG_FILE"; then + echo "Error: CHANGELOG.md must already contain curated notes for v${VERSION}." + echo "Add a section like:" + echo "" + echo "## v${VERSION} (${RELEASE_DATE})" + echo "" + echo "- User/operator-facing change ..." + echo "- Another concrete change ..." + echo "- Validation or operational note ..." + exit 1 fi echo "[5/7] Creating release manifest..."