Last Friday, I shipped v2.4.0. I spent 45 minutes reading the git log. I scanned 38 commits. I guessed which were fixes and which were features. I missed a breaking change. Users reported it within 30 minutes.
Release notes are a hidden tax on maintainers. You write code. You test code. Then you summarize code by hand. AI can do this for you. This tutorial builds a pipeline that generates release notes from git history. It runs on a free server. It runs as a cron job. You never hand-write a changelog again.
Why manual release notes fail
Manual notes have two problems. The first is memory. You remember big features. You forget small fixes. Those small fixes might be what users waited for. The second is wording. You write "update config". Users need to know "the config format changed and old files will fail".
An automated pipeline does not skip commits. It reads every commit. It classifies each one. It produces consistent formatting. It gives you a draft before you publish.
Architecture
The pipeline has three parts:
- A git log extractor
- A prompt template
- A release notes generator
MonkeyCode is an open-source project with two useful defaults. It offers free model access and a free server option. Disclosure: This article was prepared as part of MonkeyCode's product outreach. The free tier advertises 10 million tokens as of this writing. Quotas change. Check the project README before relying on them.
Step 1: Provision the free server
Provision the free server from the MonkeyCode dashboard. Keep the SSH command.
ssh root@<your-free-server-ip>
git --version && python3 --version
Both commands must succeed. If git is missing, install it.
sudo apt-get update && sudo apt-get install -y git python3-pip
Step 2: Write the git log extractor
Create a project directory. Add a script that pulls commits since the last tag.
mkdir -p /opt/release-notes && cd /opt/release-notes
Save this as extract_log.sh. It fetches commits since the last tag. It outputs a clean list.
#!/usr/bin/env bash
set -euo pipefail
REPO_PATH="${1:-.}"
cd "$REPO_PATH"
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || git rev-list --max-parents=0 HEAD)
echo "Commits since $LAST_TAG:"
git log "$LAST_TAG..HEAD" --pretty=format:"%h|%s" --no-merges
Make it executable.
chmod +x extract_log.sh
./extract_log.sh /path/to/your/repo
The output looks like this:
a1b2c3d|add retry logic to http client
e4f5g6h7|fix timeout parsing bug
i8j9k0l1|update config schema for v3
One commit per line. Hash and message separated by a pipe.
Step 3: Write the prompt template
Raw commit messages are messy. They say "fix stuff" and "wip". The model needs guidance. This prompt template turns commits into classified release notes.
Save this as prompt.txt.
You are a release notes writer. Convert the following git commits into structured release notes.
Rules:
- Classify each commit as Feature, Fix, Breaking, or Chore.
- Merge related commits into one bullet point.
- Use plain language. No jargon.
- Flag any commit that might break existing behavior.
- Output in Markdown with three sections: Features, Fixes, Breaking Changes.
Commits:
{{COMMITS}}
The pipeline replaces {{COMMITS}} with actual commits.
Step 4: Build the pipeline
Save this as generate_notes.sh. It connects all parts.
#!/usr/bin/env bash
set -euo pipefail
REPO_PATH="${1:-.}"
OUTPUT_FILE="${2:-RELEASE_NOTES.md}"
COMMITS=$(./extract_log.sh "$REPO_PATH")
PROMPT=$(sed "s|{{COMMITS}}|$COMMITS|g" prompt.txt)
curl -s -X POST "$MODEL_ENDPOINT" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg p "$PROMPT" '{prompt: $p}')" \
| jq -r '.output' > "$OUTPUT_FILE"
echo "Release notes written to $OUTPUT_FILE"
Note: the exact API format depends on your model endpoint. Check the MonkeyCode docs for the request format. Adjust the curl call to match.
Step 5: Inspect the sample output
Run the pipeline. You get Markdown like this:
## Features
- Added retry logic to the HTTP client
- New CLI flag for verbose logging
## Fixes
- Corrected timeout handling in config parsing
- Fixed race condition in task queue
## Breaking Changes
- Config format changed: `retry_timeout` is now `retry_timeout_seconds`
That looks good. Do not trust it blindly. Verification is required.
Step 6: Verify the output
Check three things before publishing.
First, check the classification. Does each commit land in the right section?
grep -c "^- " RELEASE_NOTES.md
Second, check the breaking changes. Is your Breaking Changes section empty? That might be wrong. The model may have missed a breaking change.
Third, check for hallucination. The model may add things that are not in the commits. Cross-check against the git log.
./extract_log.sh "$REPO_PATH" | wc -l
grep -c "^- " RELEASE_NOTES.md
If the release notes have more lines than commits, the model is inventing. Trim it.
Step 7: Run it as a cron job
Manual runs are fine. Cron is better. Every time you tag a release, the pipeline runs itself.
Add a cron job that checks for new tags daily.
crontab -e
Add this line:
0 9 * * * cd /opt/release-notes && ./generate_notes.sh /path/to/repo /var/www/RELEASE_NOTES.md
Every morning at 9 AM, the pipeline runs. If there are no new commits since the last tag, the output is empty. If there is a new tag, you get a draft release notes file.
Where this pipeline breaks
The model misses context. It only sees commit messages, not code. A commit that says "update config" could be breaking. The model cannot know. You need human review.
The free tier may route to different models. Output style will vary. Your release notes format may shift slightly each run. Fixing the format in the prompt helps.
Commit message quality matters. If your team commits say "stuff", the output says "stuff". Garbage in, garbage out.
Who should not use this
This pipeline suits internal projects, open-source libraries, and small teams. It is not for:
- Regulated products that need legal review
- Customer-facing docs that need perfect wording
- Teams with chaotic commit message conventions
For those cases, use the pipeline as a draft. Human editing is still required. The pipeline saves drafting time, not review time.
Try it
The free tier is enough to run this pipeline for weeks. Check the current quota before you start. Then let your git log write your release notes. Your Friday afternoon will thank you.
Top comments (0)