- Posted on
- • Artificial Intelligence
Artificial Intelligence Changelog Generation
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Stop Writing Changelogs by Hand: AI-Powered Release Notes from Your Git History
If you’ve ever shipped a release at 5 p.m. on a Friday, you know the pain: “Who’s writing the changelog?” Silence. Then someone cobbles together a bullet list from memory. Users miss important details. Security fixes get buried. And next week you repeat the ritual.
There’s a better way. With a few Bash commands and either a local or cloud LLM, you can turn raw Git history into structured, readable, and consistent release notes. This post shows you how to build a reliable, automatable AI changelog generator that runs on Linux using standard tools.
Why AI-generated changelogs make sense
Developers already write commit messages; AI can summarize them into user-friendly language without extra human toil.
Good changelogs are essential for users, support, and compliance (especially security and breaking changes).
Conventional Commits help, but in the real world messages vary; AI can normalize tone, categorize changes, and surface highlights.
With careful prompting and guardrails, you can get predictable, auditable output you can review and edit in seconds.
What you’ll build
A single Bash script that:
- Extracts commits from a range (e.g., since last tag).
- Sends them to an LLM (OpenAI API or a local Ollama model).
- Outputs clean Markdown with sections like Added, Changed, Fixed, Security, and Breaking Changes.
Optional GitHub Actions job to auto-generate and publish notes on every tag.
Prerequisites and installation
We’ll use git, curl, and jq. Install them with your distro’s package manager:
Debian/Ubuntu (apt):
sudo apt-get update sudo apt-get install -y git curl jqFedora/RHEL/CentOS (dnf):
sudo dnf install -y git curl jqopenSUSE/SLE (zypper):
sudo zypper refresh sudo zypper install -y git curl jq
Choose your AI backend:
Option A: Cloud (OpenAI)
Set your key (replace with your real key):
export OPENAI_API_KEY="sk-..."We’ll use curl; no extra install needed beyond jq and curl.
Option B: Local (Ollama)
Install Ollama:
curl -fsSL https://ollama.com/install.sh | shStart the service and pull a model (example: Llama 3):
sudo systemctl enable --now ollama ollama pull llama3:8b
Note: If you choose Ollama, no API keys leave your machine.
Step 1: Extract structured commits with Git
Let’s grab commits since the last tag (or use a custom range). We’ll exclude merge commits and bot users to reduce noise.
# Since last tag
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
RANGE="${1:-${LAST_TAG:+${LAST_TAG}..HEAD}}"
RANGE="${RANGE:-HEAD}"
# Produce a compact, parseable stream with separators
git log --no-merges --pretty=format:'%H%n%an <%ae>%n%ad%n%s%n%b%n----' --date=short "$RANGE" \
| sed '/\[bot\]@/I,/----/d'
This outputs blocks like:
<hash>
<author name> <author email>
<date>
<subject>
<body possibly multiline>
----
We’ll feed these to the model with instructions on how to treat “----” as an entry separator.
Step 2: Use a prompt that enforces structure
LLMs are flexible, which is great—until they aren’t. Use a clear, strict prompt that requests deterministic Markdown sections and no extra commentary.
read -r -d '' SYSTEM_PROMPT <<'EOF'
You are a release-notes generator. Convert Git commits into a concise, factual, user-facing changelog.
Rules:
- Output valid Markdown only. No preambles or explanations.
- Organize under these headings (omit empty ones):
- Highlights
- Added
- Changed
- Fixed
- Security
- Performance
- Breaking Changes
- Migration
- Use short bullet points. Prefer imperative mood.
- Aggregate duplicate or related commits.
- If Conventional Commits prefixes exist (feat, fix, perf, refactor, chore, docs), map them to the right section.
- Include PR numbers if present (e.g., #123), but do not invent IDs.
- Never fabricate features or dates. If unsure, omit.
EOF
read -r -d '' USER_INSTRUCTIONS <<'EOF'
You will receive commit entries separated by lines containing only "----".
Each entry is:
<hash>
<author name> <author email>
<YYYY-MM-DD>
<subject>
<body...>
----
Summarize these into a release changelog as per the rules.
EOF
Step 3: The one-file Bash script
Save as ai-changelog.sh and make it executable.
#!/usr/bin/env bash
set -euo pipefail
# Dependencies: git, curl, jq
# Backend: OpenAI (default) or Ollama local
# Usage:
# BACKEND=openai OPENAI_API_KEY=... MODEL=gpt-4o-mini ./ai-changelog.sh [RANGE] [VERSION]
# BACKEND=ollama MODEL=llama3:8b ./ai-changelog.sh [RANGE] [VERSION]
#
# RANGE defaults to <last_tag>..HEAD if a tag exists, else HEAD
# VERSION is used in the Markdown title (optional)
BACKEND="${BACKEND:-openai}"
MODEL="${MODEL:-gpt-4o-mini}"
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || true)
DEFAULT_RANGE="${LAST_TAG:+${LAST_TAG}..HEAD}"
RANGE="${1:-${DEFAULT_RANGE:-HEAD}}"
VERSION="${2:-}"
read -r -d '' SYSTEM_PROMPT <<'EOF'
You are a release-notes generator. Convert Git commits into a concise, factual, user-facing changelog.
Rules:
- Output valid Markdown only. No preambles or explanations.
- Organize under these headings (omit empty ones):
- Highlights
- Added
- Changed
- Fixed
- Security
- Performance
- Breaking Changes
- Migration
- Use short bullet points. Prefer imperative mood.
- Aggregate duplicate or related commits.
- If Conventional Commits prefixes exist (feat, fix, perf, refactor, chore, docs), map them to the right section.
- Include PR numbers if present (e.g., #123), but do not invent IDs.
- Never fabricate features or dates. If unsure, omit.
EOF
read -r -d '' USER_INSTRUCTIONS <<'EOF'
You will receive commit entries separated by lines containing only "----".
Each entry is:
<hash>
<author name> <author email>
<YYYY-MM-DD>
<subject>
<body...>
----
Summarize these into a release changelog as per the rules.
EOF
# Collect commits
COMMITS=$(git log --no-merges --pretty=format:'%H%n%an <%ae>%n%ad%n%s%n%b%n----' --date=short "$RANGE" \
| sed '/\[bot\]@/I,/----/d')
if [[ -z "$COMMITS" ]]; then
echo "No commits found in range: $RANGE" >&2
exit 0
fi
# Truncate to avoid huge payloads; refine as needed
MAX_CHARS=${MAX_CHARS:-180000}
COMMITS_TRUNC="${COMMITS:0:$MAX_CHARS}"
TITLE=""
if [[ -n "$VERSION" ]]; then
TITLE="# Changelog for ${VERSION}\n\n"
else
TITLE="# Changelog\n\n"
fi
generate_openai() {
: "${OPENAI_API_KEY:?OPENAI_API_KEY is required for BACKEND=openai}"
local body
body=$(jq -n \
--arg model "$MODEL" \
--arg sys "$SYSTEM_PROMPT" \
--arg user "$USER_INSTRUCTIONS"$'\n\n'"<COMMITS>\n$COMMITS_TRUNC\n</COMMITS>" \
'{
model: $model,
temperature: 0.2,
messages: [
{role:"system", content:$sys},
{role:"user", content:$user}
]
}')
curl -fsSL https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer ${OPENAI_API_KEY}" \
-H "Content-Type: application/json" \
-d "$body" \
| jq -r '.choices[0].message.content'
}
generate_ollama() {
local prompt
prompt="$SYSTEM_PROMPT"$'\n\n'"$USER_INSTRUCTIONS"$'\n\n'"<COMMITS>\n$COMMITS_TRUNC\n</COMMITS>"
curl -fsSL http://localhost:11434/api/generate \
-H "Content-Type: application/json" \
-d "$(jq -n --arg model "$MODEL" --arg prompt "$prompt" --argjson temp 0.2 '{model:$model,prompt:$prompt,temperature:$temp}')" \
| jq -r --slurp 'map(.response) | join("")'
}
CONTENT=""
case "$BACKEND" in
openai) CONTENT=$(generate_openai) ;;
ollama) CONTENT=$(generate_ollama) ;;
*) echo "Unknown BACKEND: $BACKEND (use openai or ollama)" >&2; exit 1 ;;
esac
# Print final Markdown
printf "%b%s\n" "$TITLE" "$CONTENT"
Make it executable:
chmod +x ai-changelog.sh
Step 4: Run it locally
Generate notes since the last tag (auto-detected):
BACKEND=openai MODEL=gpt-4o-mini OPENAI_API_KEY="$OPENAI_API_KEY" ./ai-changelog.shSpecify a range and a version label (e.g., when tagging v1.4.0):
git tag v1.4.0 BACKEND=ollama MODEL=llama3:8b ./ai-changelog.sh v1.3.0..v1.4.0 v1.4.0 | tee -a CHANGELOG.mdPreview for unreleased changes:
./ai-changelog.sh HEAD "Unreleased"
Tip: Always skim the output. It should be high-quality already, but a 30-second review keeps things accurate and on-brand.
Step 5: Automate in CI (GitHub Actions example)
This job runs on a new tag, generates notes with OpenAI, and publishes a GitHub release.
name: Release Notes
on:
push:
tags:
- 'v*.*.*'
jobs:
changelog:
runs-on: ubuntu-latest
permissions:
contents: write
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
BACKEND: openai
MODEL: gpt-4o-mini
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install deps
run: |
sudo apt-get update
sudo apt-get install -y git curl jq
- name: Generate changelog
run: |
TAG="${GITHUB_REF_NAME}"
PREV="$(git describe --tags --abbrev=0 "${TAG}^" 2>/dev/null || echo "")"
RANGE="${PREV:+${PREV}..}${TAG}"
curl -fsSL https://raw.githubusercontent.com/your-org/your-repo/main/ai-changelog.sh -o ai-changelog.sh
chmod +x ai-changelog.sh
./ai-changelog.sh "$RANGE" "$TAG" > RELEASE_NOTES.md
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
body_path: RELEASE_NOTES.md
If you prefer local-only generation in CI (no external API), run Ollama on your build agent and set BACKEND=ollama.
Real-world tips for reliable AI changelogs
Guardrails and structure:
- Keep the headings fixed and ask for Markdown only. This minimizes drift.
Keep inputs lean:
- Exclude merges and bot commits. Consider filtering “chore:” and “refactor:” unless you want them.
- For huge histories, chunk commit text and summarize in stages.
Reproducibility:
- Store the model name, temperature, and the prompts used in your repo to make outputs consistent across runs.
Security:
- Avoid sending full diffs or sensitive strings to cloud APIs. Local models via Ollama are great when confidentiality matters.
Better commit hygiene:
- Conventional Commits or including “[skip changelog]” in noise commits boosts quality dramatically.
Conclusion and next steps
You don’t need another Friday fire drill. Let an LLM do the heavy lifting—your team just reviews and tweaks. Start by dropping the ai-changelog.sh script into your repo, pick your backend (OpenAI or local Ollama), and wire it into your release workflow.
Your next step:
Install git, curl, jq (via apt, dnf, or zypper).
Choose your backend:
- OpenAI: export OPENAI_API_KEY and run.
- Ollama: install, pull a model, and run locally.
Commit the script, generate notes for your latest release, and ship with confidence.
Happy automating!