From 3cf96eb06899e930a4e69dbb7dcda6fbc9a54784 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Tue, 20 Jan 2026 22:13:38 +0000 Subject: [PATCH] refactor: use code diffs instead of commit messages for release notes The previous approach used commit messages which could include changes that were later reverted. Now the script analyzes actual git diffs between versions to identify user-facing changes. Extracts diffs from: - API handlers (new endpoints) - Frontend components (new features) - Config options (new settings) - Alerts/notifications (webhook changes) - Agent code (host/docker features) - Install scripts Passes structured diffs to LLM with instructions to write plain, factual release notes without marketing language. --- scripts/generate-release-notes.sh | 358 +++++++++++------------------- 1 file changed, 125 insertions(+), 233 deletions(-) diff --git a/scripts/generate-release-notes.sh b/scripts/generate-release-notes.sh index 03e56309f..979a731bb 100755 --- a/scripts/generate-release-notes.sh +++ b/scripts/generate-release-notes.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Generate release notes using LLM analysis of git commits +# Generate release notes using LLM analysis of actual code diffs (not commit messages) # Usage: ./scripts/generate-release-notes.sh [previous-tag] set -euo pipefail @@ -18,139 +18,171 @@ fi if [ -z "$PREVIOUS_TAG" ]; then PREVIOUS_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") if [ -z "$PREVIOUS_TAG" ]; then - echo "No previous tag found, using all commits" - PREVIOUS_TAG=$(git rev-list --max-parents=0 HEAD) + echo "No previous tag found, cannot generate diff-based release notes" + exit 1 fi fi echo "Generating release notes for v${VERSION}..." -echo "Analyzing commits since ${PREVIOUS_TAG}..." +echo "Comparing code changes from ${PREVIOUS_TAG} to HEAD..." -# Get commit log -COMMIT_LOG=$(git log ${PREVIOUS_TAG}..HEAD --pretty=format:"%h %s" --no-merges) +# Get diff stats (excluding non-user-facing files) +DIFF_STAT=$(git diff ${PREVIOUS_TAG}..HEAD --stat \ + -- ':!*.md' ':!*.test.go' ':!*_test.go' ':!*_test.tsx' ':!*_test.ts' \ + ':!.github/*' ':!tests/*' ':!docs/*' ':!*.txt' ':!*.json' ':!go.sum' \ + ':!frontend-modern/src/**/__tests__/*' \ + | tail -20) -if [ -z "$COMMIT_LOG" ]; then - echo "No commits found since ${PREVIOUS_TAG}" - exit 1 -fi +# Get list of changed user-facing files +CHANGED_FILES=$(git diff ${PREVIOUS_TAG}..HEAD --name-only \ + -- ':!*.md' ':!*.test.go' ':!*_test.go' ':!*_test.tsx' ':!*_test.ts' \ + ':!.github/*' ':!tests/*' ':!docs/*' ':!*.txt' ':!go.sum' \ + ':!frontend-modern/src/**/__tests__/*' \ + | head -100) -# Count commits -COMMIT_COUNT=$(echo "$COMMIT_LOG" | wc -l) -echo "Found ${COMMIT_COUNT} commits" +# Get specific diffs for key user-facing areas (truncated for API limits) -# Generate release notes using LLM API -# Supports both OpenAI and Anthropic Claude -# Set either OPENAI_API_KEY or ANTHROPIC_API_KEY +# API routes/handlers - new endpoints +API_DIFF=$(git diff ${PREVIOUS_TAG}..HEAD -- 'internal/api/*.go' ':!*_test.go' \ + | grep -E '^\+.*func.*Handle|^\+.*router\.(GET|POST|PUT|DELETE|PATCH)|^\+.*\.Path\(' \ + | head -30 || echo "") +# Frontend pages and components - new features +FRONTEND_DIFF=$(git diff ${PREVIOUS_TAG}..HEAD -- 'frontend-modern/src/components/*.tsx' 'frontend-modern/src/pages/*.tsx' \ + ':!*_test.tsx' ':!*__tests__*' \ + | grep -E '^\+.*export|^\+.*function.*\(|^\+.*const.*=' \ + | head -40 || echo "") + +# Config options - new settings users can configure +CONFIG_DIFF=$(git diff ${PREVIOUS_TAG}..HEAD -- 'internal/config/*.go' ':!*_test.go' \ + | grep -E '^\+.*`json:|^\+.*`yaml:' \ + | head -20 || echo "") + +# Notifications/alerts - webhook changes, alert features +ALERT_DIFF=$(git diff ${PREVIOUS_TAG}..HEAD -- 'internal/notifications/*.go' 'internal/alerts/*.go' ':!*_test.go' \ + | grep -E '^\+' \ + | head -30 || echo "") + +# Agent changes - host/docker agent features +AGENT_DIFF=$(git diff ${PREVIOUS_TAG}..HEAD -- 'cmd/pulse-agent/*.go' 'internal/agent/*.go' ':!*_test.go' \ + | grep -E '^\+' \ + | head -20 || echo "") + +# Install script changes +INSTALL_DIFF=$(git diff ${PREVIOUS_TAG}..HEAD -- 'scripts/install.sh' 'install.sh' \ + | grep -E '^\+' \ + | head -20 || echo "") + +# Models/types - new data structures +MODELS_DIFF=$(git diff ${PREVIOUS_TAG}..HEAD -- 'internal/models/*.go' ':!*_test.go' \ + | grep -E '^\+.*type.*struct|^\+.*`json:' \ + | head -20 || echo "") + +echo "Collected diffs from key areas" + +# Check for LLM API keys if [ -n "${ANTHROPIC_API_KEY:-}" ]; then LLM_PROVIDER="anthropic" elif [ -n "${OPENAI_API_KEY:-}" ]; then LLM_PROVIDER="openai" else - echo "No LLM API keys detected – falling back to deterministic release notes." - LLM_PROVIDER="fallback" + echo "No LLM API keys detected – cannot generate diff-based notes." + echo "Set ANTHROPIC_API_KEY or OPENAI_API_KEY" + exit 1 fi echo "Using LLM provider: ${LLM_PROVIDER}" -# Prepare prompt for LLM +# Build the prompt with actual code changes read -r -d '' PROMPT <&2 - if ! RELEASE_NOTES=$(generate_with_openai); then - echo "OpenAI fallback failed; generating heuristic release notes." >&2 - RELEASE_NOTES=$(generate_fallback_release_notes) - fi - LLM_PROVIDER="openai" + RELEASE_NOTES=$(generate_with_openai) || { + echo "Both LLM providers failed" >&2 + exit 1 + } else - echo "Anthropic generation failed and no OpenAI fallback is available; generating heuristic release notes." >&2 - RELEASE_NOTES=$(generate_fallback_release_notes) + echo "Anthropic generation failed and no OpenAI fallback" >&2 + exit 1 fi fi else - if ! RELEASE_NOTES=$(generate_with_openai); then - echo "OpenAI generation failed; generating heuristic release notes." >&2 - RELEASE_NOTES=$(generate_fallback_release_notes) - fi + RELEASE_NOTES=$(generate_with_openai) || { + echo "OpenAI generation failed" >&2 + exit 1 + } fi if [ -z "$RELEASE_NOTES" ] || [ "$RELEASE_NOTES" = "null" ]; then