Skip to content

Commit a6d23b0

Browse files
adityamparikhclaude
andcommitted
ci: add automated changelog generation with git-cliff
Add GitHub Actions workflow and configuration for automatic CHANGELOG.md generation from conventional commits. Changes: - cliff.toml: git-cliff configuration for parsing commits - .github/workflows/changelog.yml: Automated workflow - dev-docs/WORKFLOWS.md: Documentation for changelog workflow Features: - Automatically triggers on version tags (v*) - Manual trigger with dry_run and create_pr options - Groups commits by type (Features, Bug Fixes, etc.) - Supports conventional commit format 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 8f38662 commit a6d23b0

3 files changed

Lines changed: 304 additions & 10 deletions

File tree

.github/workflows/changelog.yml

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one or more
2+
# contributor license agreements. See the NOTICE file distributed with
3+
# this work for additional information regarding copyright ownership.
4+
# The ASF licenses this file to You under the Apache License, Version 2.0
5+
# (the "License"); you may not use this file except in compliance with
6+
# the License. You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
# ╔═══════════════════════════════════════════════════════════════════════════╗
17+
# ║ CHANGELOG GENERATION WORKFLOW ║
18+
# ╚═══════════════════════════════════════════════════════════════════════════╝
19+
#
20+
# PURPOSE: Automatically generate CHANGELOG.md from conventional commits
21+
#
22+
# WHEN TO USE:
23+
# -----------
24+
# - Automatically triggered on version tags (v*)
25+
# - Manual trigger before releases to preview changelog
26+
# - After merging significant features to update changelog
27+
#
28+
# REQUIREMENTS:
29+
# -------------
30+
# - Conventional commit messages (feat:, fix:, docs:, etc.)
31+
# - cliff.toml configuration file in repository root
32+
#
33+
# OPTIONS:
34+
# --------
35+
# - dry_run: Preview changelog without committing (default: false)
36+
# - create_pr: Create a PR with changelog updates (default: false)
37+
# - tag: Specific tag to generate changelog for (optional)
38+
39+
name: Generate Changelog
40+
41+
on:
42+
push:
43+
tags:
44+
- 'v*'
45+
workflow_dispatch:
46+
inputs:
47+
dry_run:
48+
description: 'Dry run - preview changelog without committing'
49+
required: false
50+
default: 'false'
51+
type: boolean
52+
create_pr:
53+
description: 'Create a PR with changelog updates instead of direct commit'
54+
required: false
55+
default: 'false'
56+
type: boolean
57+
tag:
58+
description: 'Tag to generate changelog for (leave empty for latest)'
59+
required: false
60+
type: string
61+
62+
permissions:
63+
contents: write
64+
pull-requests: write
65+
66+
jobs:
67+
changelog:
68+
name: Generate Changelog
69+
runs-on: ubuntu-latest
70+
steps:
71+
- name: Checkout repository
72+
uses: actions/checkout@v4
73+
with:
74+
fetch-depth: 0
75+
token: ${{ secrets.GITHUB_TOKEN }}
76+
77+
- name: Generate changelog
78+
uses: orhun/git-cliff-action@v4
79+
id: git-cliff
80+
with:
81+
config: cliff.toml
82+
args: --verbose
83+
env:
84+
OUTPUT: CHANGELOG.md
85+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
86+
87+
- name: Print changelog (dry run)
88+
if: ${{ github.event.inputs.dry_run == 'true' }}
89+
run: |
90+
echo "Generated changelog preview:"
91+
echo "=============================="
92+
cat CHANGELOG.md
93+
94+
- name: Create Pull Request
95+
if: ${{ github.event.inputs.create_pr == 'true' && github.event.inputs.dry_run != 'true' }}
96+
uses: peter-evans/create-pull-request@v7
97+
with:
98+
token: ${{ secrets.GITHUB_TOKEN }}
99+
commit-message: 'docs: update CHANGELOG.md'
100+
title: 'docs: update CHANGELOG.md'
101+
body: |
102+
This PR updates the CHANGELOG.md file with the latest changes.
103+
104+
Generated by the changelog workflow using [git-cliff](https://git-cliff.org/).
105+
branch: changelog-update
106+
delete-branch: true
107+
108+
- name: Commit and push changelog
109+
if: ${{ github.event.inputs.dry_run != 'true' && github.event.inputs.create_pr != 'true' }}
110+
run: |
111+
git config user.name 'github-actions[bot]'
112+
git config user.email 'github-actions[bot]@users.noreply.github.com'
113+
114+
# Only commit if there are changes
115+
if git diff --quiet CHANGELOG.md; then
116+
echo "No changes to CHANGELOG.md"
117+
else
118+
git add CHANGELOG.md
119+
git commit -m "docs: update CHANGELOG.md for ${{ github.ref_name }}"
120+
git push
121+
fi
122+
123+
- name: Upload changelog artifact
124+
uses: actions/upload-artifact@v4
125+
with:
126+
name: changelog
127+
path: CHANGELOG.md
128+
retention-days: 30

cliff.toml

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# git-cliff configuration file
2+
# https://git-cliff.org/docs/configuration
3+
#
4+
# This configuration is used by the changelog.yml GitHub Action to automatically
5+
# generate CHANGELOG.md from conventional commits.
6+
7+
[changelog]
8+
# Changelog header
9+
header = """
10+
# Changelog
11+
12+
All notable changes to this project will be documented in this file.
13+
14+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
15+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
16+
17+
"""
18+
# Template for the changelog body
19+
body = """
20+
{% if version %}\
21+
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
22+
{% else %}\
23+
## [Unreleased]
24+
{% endif %}\
25+
{% for group, commits in commits | group_by(attribute="group") %}
26+
### {{ group | striptags | trim | upper_first }}
27+
{% for commit in commits %}
28+
- {% if commit.scope %}**{{ commit.scope }}:** {% endif %}\
29+
{% if commit.breaking %}[**breaking**] {% endif %}\
30+
{{ commit.message | upper_first }}\
31+
{% if commit.github.username %} by @{{ commit.github.username }}{% endif %}\
32+
{% if commit.github.pr_number %} in [#{{ commit.github.pr_number }}](https://github.com/apache/solr-mcp/pull/{{ commit.github.pr_number }}){% endif %}\
33+
{% endfor %}
34+
{% endfor %}
35+
"""
36+
# Template for the changelog footer
37+
footer = """
38+
<!-- generated by git-cliff -->
39+
"""
40+
# Remove leading and trailing whitespace
41+
trim = true
42+
# Postprocessors for commit messages
43+
postprocessors = []
44+
45+
[git]
46+
# Parse conventional commits
47+
conventional_commits = true
48+
# Filter out unconventional commits
49+
filter_unconventional = true
50+
# Split commits by lines
51+
split_commits = false
52+
# Regex patterns for commit preprocessing
53+
commit_preprocessors = [
54+
# Replace issue numbers with links
55+
{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](https://github.com/apache/solr-mcp/issues/${2}))" },
56+
]
57+
# Commit parsers for categorization
58+
commit_parsers = [
59+
{ message = "^feat", group = "Features" },
60+
{ message = "^fix", group = "Bug Fixes" },
61+
{ message = "^doc", group = "Documentation" },
62+
{ message = "^perf", group = "Performance" },
63+
{ message = "^refactor", group = "Refactoring" },
64+
{ message = "^style", group = "Styling" },
65+
{ message = "^test", group = "Testing" },
66+
{ message = "^build", group = "Build" },
67+
{ message = "^ci", group = "CI/CD" },
68+
{ message = "^chore\\(release\\)", skip = true },
69+
{ message = "^chore\\(deps\\)", skip = true },
70+
{ message = "^chore\\(pr\\)", skip = true },
71+
{ message = "^chore\\(pull\\)", skip = true },
72+
{ message = "^chore", group = "Miscellaneous" },
73+
{ body = ".*security", group = "Security" },
74+
{ message = "^revert", group = "Reverted Changes" },
75+
]
76+
# Protect breaking changes from being skipped
77+
protect_breaking_commits = true
78+
# Filter commits by patterns
79+
filter_commits = false
80+
# Tagging pattern
81+
tag_pattern = "v[0-9].*"
82+
# Skip tags matching these patterns
83+
skip_tags = ""
84+
# Ignore tags matching these patterns
85+
ignore_tags = ""
86+
# Sort order for tags
87+
topo_order = false
88+
# Sort commits by date
89+
sort_commits = "oldest"
90+
# Limit the number of commits
91+
# limit_commits = 42

dev-docs/WORKFLOWS.md

Lines changed: 85 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ This guide explains when and how to use each GitHub Actions workflow in the proj
99
| [build-and-publish.yml](#build-and-publishyml) | Development CI/CD | Automatic (push/PR) | ✅ Active | Daily development |
1010
| [release-publish.yml](#release-publishyml) | Official ASF releases | Manual (after vote) | ✅ Active | Production releases |
1111
| [nightly-build.yml](#nightly-buildyml) | Nightly builds | Scheduled (2 AM UTC) | ✅ Active | Latest unstable builds |
12+
| [changelog.yml](#changelogyml) | Changelog generation | Tag push / Manual | ✅ Active | Release documentation |
1213
| [atr-release-test.yml](#atr-release-testyml) | ATR testing | Manual (safe mode) | ✅ Ready | Testing ATR workflow |
1314
| [atr-release.yml](#atr-releaseyml) | ATR production | Manual (blocked) | ⚠️ Blocked | Future ATR releases |
1415

@@ -324,6 +325,80 @@ docker pull apache/solr-mcp-nightly:nightly-20250112-a1b2c3d
324325

325326
---
326327

328+
### changelog.yml
329+
330+
**Purpose**: Automatically generate CHANGELOG.md from conventional commits
331+
332+
#### When to Use
333+
334+
- ✅ Automatically triggered on version tags (v*)
335+
- ✅ Manual trigger before releases to preview changelog
336+
- ✅ After merging significant features to update changelog
337+
338+
#### When NOT to Use
339+
340+
- ❌ If not using conventional commit messages
341+
- ❌ For draft/work-in-progress releases
342+
343+
#### Triggers
344+
345+
```yaml
346+
on:
347+
push:
348+
tags:
349+
- 'v*' # Triggered on version tags
350+
workflow_dispatch:
351+
inputs:
352+
dry_run: # Preview without committing
353+
create_pr: # Create PR instead of direct commit
354+
tag: # Specific tag to generate for
355+
```
356+
357+
#### What It Does
358+
359+
1. **Parses** conventional commits (feat:, fix:, docs:, etc.)
360+
2. **Groups** changes by type (Features, Bug Fixes, Documentation, etc.)
361+
3. **Generates** CHANGELOG.md with proper formatting
362+
4. **Commits** changes directly or creates a PR
363+
364+
#### Required Files
365+
366+
- `cliff.toml` - Configuration file in repository root
367+
368+
#### How to Use
369+
370+
**Automatic (on tag)**:
371+
372+
```bash
373+
# Tag triggers changelog generation automatically
374+
git tag v1.0.0 -m "Release 1.0.0"
375+
git push origin v1.0.0
376+
```
377+
378+
**Manual Preview (Dry Run)**:
379+
380+
```bash
381+
# Via GitHub UI: Actions → Generate Changelog → Run workflow
382+
# Enable "Dry run" option to preview without committing
383+
384+
# Or via CLI:
385+
gh workflow run changelog.yml -f dry_run=true
386+
```
387+
388+
**Create PR with Changes**:
389+
390+
```bash
391+
gh workflow run changelog.yml -f create_pr=true
392+
```
393+
394+
#### Example Use Cases
395+
396+
- Generating release notes before a release
397+
- Previewing changelog updates during development
398+
- Automating documentation for version tags
399+
400+
---
401+
327402
### atr-release-test.yml
328403

329404
**Purpose**: Test Apache Trusted Releases (ATR) workflow safely
@@ -493,16 +568,16 @@ gh workflow run atr-release.yml \
493568

494569
## Workflow Comparison Matrix
495570

496-
| Feature | build-and-publish | release-publish | nightly-build | atr-release-test | atr-release |
497-
|----------------------|-------------------|-----------------|--------------------|------------------|-------------|
498-
| **Status** | ✅ Active | ✅ Active | ✅ Active | ✅ Ready | ⚠️ Blocked |
499-
| **Trigger** | Automatic | Manual | Scheduled | Manual | Manual |
500-
| **Docker Namespace** | Personal/GHCR | `apache/*` | `apache/*-nightly` | Test | `apache/*` |
501-
| **MCP Registry** | ❌ No | ✅ Yes | ❌ No | ❌ No | ✅ Yes |
502-
| **ASF Vote** | ❌ Not required | ✅ Required | ❌ Not required | ❌ Not required | ✅ Required |
503-
| **Signing** | ❌ No | ⚠️ Manual | ❌ No | ⚠️ Simulated | ✅ Automated |
504-
| **Production Ready** | ❌ No | ✅ Yes | ❌ No | ❌ No | ⚠️ Future |
505-
| **Can Test Now** | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ❌ No |
571+
| Feature | build-and-publish | release-publish | nightly-build | changelog | atr-release-test | atr-release |
572+
|----------------------|-------------------|-----------------|--------------------|--------------|------------------|-------------|
573+
| **Status** | ✅ Active | ✅ Active | ✅ Active | ✅ Active | ✅ Ready | ⚠️ Blocked |
574+
| **Trigger** | Automatic | Manual | Scheduled | Tag/Manual | Manual | Manual |
575+
| **Docker Namespace** | Personal/GHCR | `apache/*` | `apache/*-nightly` | N/A | Test | `apache/*` |
576+
| **MCP Registry** | ❌ No | ✅ Yes | ❌ No | ❌ No | ❌ No | ✅ Yes |
577+
| **ASF Vote** | ❌ Not required | ✅ Required | ❌ Not required | ❌ Not required | ❌ Not required | ✅ Required |
578+
| **Signing** | ❌ No | ⚠️ Manual | ❌ No | ❌ No | ⚠️ Simulated | ✅ Automated |
579+
| **Production Ready** | ❌ No | ✅ Yes | ❌ No | ✅ Yes | ❌ No | ⚠️ Future |
580+
| **Can Test Now** | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ❌ No |
506581

507582
---
508583

0 commit comments

Comments
 (0)