Add consumer-facing CHANGELOG.md maintained by release CI.
PR Title Check / check-title (pull_request) Successful in 8s
Test Python Package / unit-tests (pull_request) Successful in 12s
Code Quality Pipeline / code-quality (pull_request) Failing after 23s
Test Python Package / integration-tests (pull_request) Successful in 45s
Test Python Package / coverage-report (pull_request) Successful in 11s

Backfill version history from git tags and update CHANGELOG automatically on each release alongside Gitea release notes.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Brian Bjarke Jensen
2026-07-10 20:28:46 +02:00
co-authored by Cursor
parent 683ee332d3
commit 0a5bd39801
8 changed files with 489 additions and 5 deletions
+64
View File
@@ -0,0 +1,64 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=common.sh
source "${SCRIPT_DIR}/common.sh"
CHANGELOG="CHANGELOG.md"
# Best-effort summary for historical releases without stored PR titles.
summary_for_range() {
local from_ref="$1"
local to_ref="$2"
local subject
while IFS= read -r subject; do
[[ -z "$subject" ]] && continue
if echo "$subject" | grep -qE '^chore: release v'; then
continue
fi
if echo "$subject" | grep -qE "^Merge pull request '"; then
local title
title=$(extract_pr_title_from_merge_commit "$subject")
if [[ -n "$title" ]]; then
strip_bump_prefix "$title"
return 0
fi
fi
echo "$subject"
return 0
done < <(git log "${from_ref}..${to_ref}" --pretty=format:'%s' 2>/dev/null || true)
echo "Release ${to_ref}"
}
TAGS=()
while IFS= read -r tag; do
TAGS+=("$tag")
done < <(git tag -l 'v*' --sort=version:refname)
if [[ ${#TAGS[@]} -eq 0 ]]; then
echo "No version tags found." >&2
exit 1
fi
changelog_header > "$CHANGELOG"
for ((i = ${#TAGS[@]} - 1; i >= 0; i--)); do
TAG="${TAGS[$i]}"
VERSION="${TAG#v}"
DATE=$(git log -1 --format=%cs "$TAG" 2>/dev/null || date -u +%Y-%m-%d)
if [[ $i -eq 0 ]]; then
PREV_TAG=""
else
PREV_TAG="${TAGS[$((i - 1))]}"
fi
SUMMARY=$(summary_for_range "$PREV_TAG" "$TAG")
COMMITS=$(commits_between "$PREV_TAG" "$TAG")
SECTION=$(changelog_section "$VERSION" "$DATE" "$SUMMARY" "$COMMITS")
printf '\n%s\n' "$SECTION" >> "$CHANGELOG"
done
echo "Generated ${CHANGELOG} from ${#TAGS[@]} tags." >&2
+53
View File
@@ -61,6 +61,59 @@ set_pyproject_version() {
rm -f pyproject.toml.bak
}
# Returns markdown bullet list of commits between two refs (exclusive..inclusive).
# Optional third argument limits entries when from_ref is empty (release notes only).
commits_between() {
local from_ref="$1"
local to_ref="${2:-HEAD}"
local limit="${3:-}"
if [[ -z "$from_ref" ]]; then
if [[ -n "$limit" ]]; then
git log "${to_ref}" --pretty=format:'- %h %s' -"${limit}" 2>/dev/null || true
else
git log "${to_ref}" --pretty=format:'- %h %s' 2>/dev/null || true
fi
else
git log "${from_ref}..${to_ref}" --pretty=format:'- %h %s' 2>/dev/null || true
fi
}
commits_since() {
commits_between "$1" HEAD
}
changelog_header() {
cat <<'EOF'
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
EOF
}
changelog_section() {
local version="$1"
local date="$2"
local summary="$3"
local commits="$4"
cat <<EOF
## [${version}] - ${date}
### Summary
${summary}
### Changed
${commits:-- (no commits recorded)}
EOF
}
bump_semver() {
local current="$1"
local bump_type="$2"
+2 -2
View File
@@ -34,13 +34,13 @@ if [[ -n "$PREV_TAG" ]]; then
## Changes since ${PREV_TAG}
$(git log "${PREV_TAG}..HEAD" --pretty=format:'- %h %s' || true)"
$(commits_since "$PREV_TAG")"
else
NOTES="${NOTES}
## Changes
$(git log --pretty=format:'- %h %s' -20 || true)"
$(commits_between "" HEAD 20)"
fi
if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=common.sh
source "${SCRIPT_DIR}/common.sh"
VERSION="${1:-}"
SUMMARY="${2:-}"
PREV_TAG="${3:-}"
DATE="${4:-$(date -u +%Y-%m-%d)}"
CHANGELOG="CHANGELOG.md"
if [[ -z "$VERSION" ]]; then
echo "Usage: update-changelog.sh <version> <summary> <prev-tag> [date]" >&2
exit 1
fi
if [[ -f "$CHANGELOG" ]] && grep -qF "## [${VERSION}]" "$CHANGELOG"; then
echo "CHANGELOG already contains version ${VERSION}; skipping." >&2
exit 0
fi
if [[ -n "$SUMMARY" ]]; then
SUMMARY_TEXT=$(strip_bump_prefix "$SUMMARY")
else
SUMMARY_TEXT="Automated release v${VERSION}."
fi
COMMITS=$(commits_since "$PREV_TAG")
SECTION=$(changelog_section "$VERSION" "$DATE" "$SUMMARY_TEXT" "$COMMITS")
if [[ ! -f "$CHANGELOG" ]]; then
changelog_header > "$CHANGELOG"
printf '\n%s\n' "$SECTION" >> "$CHANGELOG"
else
UNRELEASED_MARKER='## [Unreleased]'
if ! grep -qF "$UNRELEASED_MARKER" "$CHANGELOG"; then
echo "CHANGELOG.md exists but is missing ${UNRELEASED_MARKER}" >&2
exit 1
fi
TMP=$(mktemp)
SECTION_FILE=$(mktemp)
printf '%s\n' "$SECTION" > "$SECTION_FILE"
awk -v section_file="$SECTION_FILE" '
/^## \[Unreleased\]/ {
print
print ""
while ((getline line < section_file) > 0) {
print line
}
close(section_file)
next
}
{ print }
' "$CHANGELOG" > "$TMP"
rm -f "$SECTION_FILE"
mv "$TMP" "$CHANGELOG"
fi
echo "Updated ${CHANGELOG} with version ${VERSION}." >&2