Files
1jehuang--jcode/scripts/generate_release_notes.sh
wehub-resource-sync a789495a98
CI / Quality Guardrails (push) Waiting to run
CI / Build & Test (macos-latest) (push) Waiting to run
CI / Build & Test (ubuntu-latest) (push) Waiting to run
CI / Build & Test (windows-latest) (push) Waiting to run
CI / Format (push) Waiting to run
CI / PowerShell Syntax (push) Waiting to run
CI / Windows Cross-Target Check (Linux) (push) Waiting to run
FreeBSD Smoke / FreeBSD Smoke (x86_64) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:34 +08:00

160 lines
4.1 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# Generate a human-readable release notes body for a jcode release (issue #435).
#
# Usage:
# scripts/generate_release_notes.sh <tag> [previous-tag]
#
# Behavior:
# 1. If changelog/v<version>.json exists (schema in changelog/README.md),
# render it into Highlights / Improvements / Fixes markdown sections.
# 2. Otherwise, group commit subjects between the previous tag and <tag>
# by conventional-commit prefix (feat/fix/perf/docs/other).
# Always appends the GitHub compare link at the bottom.
TAG="${1:?Usage: scripts/generate_release_notes.sh <tag> [previous-tag]}"
PREV_TAG="${2:-}"
cd "$(git rev-parse --show-toplevel)"
VERSION_NUM="${TAG#v}"
CHANGELOG_FILE="changelog/v${VERSION_NUM}.json"
# Determine the owner/repo slug for the compare link.
repo_slug() {
if [[ -n "${GITHUB_REPOSITORY:-}" ]]; then
echo "$GITHUB_REPOSITORY"
return
fi
local url
url="$(git remote get-url origin 2>/dev/null || true)"
if [[ "$url" =~ github\.com[:/]+([^/]+/[^/ ]+) ]]; then
echo "${BASH_REMATCH[1]%.git}"
return
fi
echo "1jehuang/jcode"
}
REPO="$(repo_slug)"
# Determine the previous tag if not supplied: nearest ancestor tag first,
# falling back to the next entry in version-sorted tag order.
if [[ -z "$PREV_TAG" ]]; then
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
PREV_TAG="$(git describe --tags --abbrev=0 "$TAG^" 2>/dev/null || true)"
fi
if [[ -z "$PREV_TAG" ]]; then
PREV_TAG="$(git tag -l 'v*' --sort=-v:refname \
| grep -A1 -Fx "$TAG" | tail -n +2 | head -1 || true)"
fi
fi
# Render changelog/v<version>.json into markdown sections.
render_changelog_json() {
python3 - "$CHANGELOG_FILE" << 'PY'
import json
import sys
with open(sys.argv[1], encoding="utf-8") as f:
entry = json.load(f)
out = []
title = entry.get("title")
if title:
out.append(f"**{title}**")
out.append("")
for key, heading in (
("highlights", "Highlights"),
("improvements", "Improvements"),
("fixes", "Fixes"),
):
items = entry.get(key) or []
if not items:
continue
out.append(f"### {heading}")
out.append("")
out.extend(f"- {item}" for item in items)
out.append("")
print("\n".join(out).rstrip())
PY
}
# Fallback: group commit subjects since the previous tag by
# conventional-commit prefix.
render_commit_fallback() {
local range
local end_ref="$TAG"
# Support pre-tag dry runs where the tag does not exist yet.
if ! git rev-parse -q --verify "$TAG^{commit}" >/dev/null; then
end_ref="HEAD"
fi
if [[ -n "$PREV_TAG" ]]; then
range="$PREV_TAG..$end_ref"
else
range="$end_ref"
fi
python3 - "$range" << 'PY'
import re
import subprocess
import sys
subjects = subprocess.run(
["git", "log", "--no-merges", "--pretty=format:%s", sys.argv[1]],
check=True,
capture_output=True,
text=True,
).stdout.splitlines()
groups = {"feat": [], "fix": [], "perf": [], "docs": [], "other": []}
pattern = re.compile(r"^(feat|fix|perf|docs)(\([^)]*\))?!?:\s*(.+)$", re.IGNORECASE)
for subject in subjects:
subject = subject.strip()
if not subject:
continue
match = pattern.match(subject)
if match:
groups[match.group(1).lower()].append(match.group(3).strip())
else:
groups["other"].append(subject)
out = []
for key, heading in (
("feat", "Features"),
("fix", "Fixes"),
("perf", "Performance"),
("docs", "Documentation"),
("other", "Other changes"),
):
items = groups[key]
if not items:
continue
out.append(f"### {heading}")
out.append("")
out.extend(f"- {item}" for item in items)
out.append("")
print("\n".join(out).rstrip())
PY
}
if [[ -f "$CHANGELOG_FILE" ]]; then
BODY="$(render_changelog_json)"
else
BODY="$(render_commit_fallback)"
fi
if [[ -n "$BODY" ]]; then
printf '%s\n\n' "$BODY"
fi
if [[ -n "$PREV_TAG" ]]; then
printf '**Full changelog**: https://github.com/%s/compare/%s...%s\n' \
"$REPO" "$PREV_TAG" "$TAG"
else
printf '**Full changelog**: https://github.com/%s/commits/%s\n' "$REPO" "$TAG"
fi