DEV Community

Cover image for I added AI-generated release notes to my CI/CD pipeline using Claude and GitHub Actions
Aftab Bashir
Aftab Bashir

Posted on

I added AI-generated release notes to my CI/CD pipeline using Claude and GitHub Actions

Every time I merged a PR I had to write a changelog entry manually. It took two minutes but I kept forgetting to do it. So I automated it with Claude.

When a PR merges to main, a GitHub Actions workflow reads the PR title, description, and changed files, sends them to the Claude API, and gets back a structured changelog entry. That entry gets committed to CHANGELOG.md automatically. The whole thing runs in about 10 seconds.

What MCP has to do with this

Nothing, directly. But this pipeline lives inside my MCP Kubernetes Manager project, which already lets Claude manage Kubernetes clusters through natural language. Adding AI-generated release notes felt like a natural fit. The project now manages its own changelog the same way it manages deployments.

If you want context on the MCP server itself, I wrote about it here: https://dev.to/aftabkh4n/i-built-an-mcp-server-in-net-that-lets-claude-manage-my-kubernetes-cluster-through-natural-language-3cji

How the pipeline works

A GitHub Actions workflow triggers on pull_request with type closed. The first thing it checks is whether the PR was actually merged, not just closed:

if: github.event.pull_request.merged == true
Enter fullscreen mode Exit fullscreen mode

That condition is important. Without it the workflow runs on every closed PR including ones that were rejected.

Then it collects the PR metadata:

env:
  PR_TITLE: ${{ github.event.pull_request.title }}
  PR_BODY: ${{ github.event.pull_request.body }}
  PR_NUMBER: ${{ github.event.pull_request.number }}
  PR_AUTHOR: ${{ github.event.pull_request.user.login }}
  PR_MERGED_AT: ${{ github.event.pull_request.merged_at }}
Enter fullscreen mode Exit fullscreen mode

And gets the list of changed files from git:

CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD | head -20 | tr '\n' ', ')
Enter fullscreen mode Exit fullscreen mode

Calling the Claude API

The API call is a standard POST to the Anthropic messages endpoint. The key part is building the payload with jq so special characters in PR titles and descriptions do not break the JSON:

PAYLOAD=$(jq -n \
  --arg model "claude-haiku-4-5-20251001" \
  --arg pr_num "$PR_NUMBER" \
  --arg pr_title "$PR_TITLE" \
  --arg pr_body "$PR_BODY_SAFE" \
  --arg files "$CHANGED_FILES" \
  '{
    model: $model,
    max_tokens: 1024,
    messages: [{
      role: "user",
      content: ("Generate a changelog entry for PR #" + $pr_num + ": " + $pr_title)
    }]
  }')

RESPONSE=$(curl -s https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d "$PAYLOAD")

ENTRY=$(echo "$RESPONSE" | jq -r '.content[0].text')
Enter fullscreen mode Exit fullscreen mode

The model is claude-haiku-4-5-20251001 which is the fastest and cheapest Claude model. For a changelog entry it produces the same quality as larger models at a fraction of the cost.

The API key is stored as a GitHub Actions secret called ANTHROPIC_API_KEY. Never hardcode it.

Writing the changelog entry back

Once Claude returns the entry, the workflow prepends it to CHANGELOG.md so the newest entry is always at the top:

TEMP=$(mktemp)
head -4 CHANGELOG.md > "$TEMP"
echo "$ENTRY" >> "$TEMP"
tail -n +5 CHANGELOG.md >> "$TEMP"
mv "$TEMP" CHANGELOG.md
Enter fullscreen mode Exit fullscreen mode

Then commits and pushes as github-actions[bot]:

git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git pull --rebase origin main
git add CHANGELOG.md
git commit -m "docs: AI-generated changelog for PR #${{ github.event.pull_request.number }}"
git push
Enter fullscreen mode Exit fullscreen mode

The git pull --rebase before the push is important. The PR merge itself updates main, so without pulling first the push gets rejected.

What the output looks like

After merging a PR that adds Kubernetes tools documentation, Claude wrote this:

## [PR #5] docs: add tools list to README
*2026-04-16T05:13:52Z by @aftabkh4n*

### Changes
- Added documentation describing the AI-powered changelog automation workflow
- Documented how Claude generates structured changelog entries automatically when PRs merge to main
- Explained the process of reading PR metadata to produce changelog entries

### Files changed
- README.md
Enter fullscreen mode Exit fullscreen mode

It reads the PR description and infers what actually changed. The better your PR description, the better the changelog entry.

What it costs

The Anthropic API is not free but it is very cheap for this use case. Haiku costs $0.80 per million input tokens. A typical PR payload is around 500 tokens. That works out to roughly $0.0004 per changelog entry, less than a cent per merge.

Setting it up in your repo

  1. Get an API key from console.anthropic.com
  2. Add it as a secret in your repo: Settings, Secrets and variables, Actions, New repository secret, name it ANTHROPIC_API_KEY
  3. Create .github/workflows/release-notes.yml with the full workflow

Full source code: https://github.com/aftabkh4n/mcp-kubernetes-manager

What I would add next

A way to skip the changelog for trivial PRs would be useful. Something like checking whether the PR title starts with chore: or wip: and skipping the API call entirely. That keeps the changelog clean without adding friction to the merge process.

Top comments (0)