Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:46:13 +08:00
commit 6005591708
7 changed files with 1125 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
{
"name": "docs-and-changelogs",
"description": "Generates comprehensive changelogs from Conventional Commits, maintains CHANGELOG.md files, and scaffolds project documentation like PRD.md or ADR.md. This skill should be used when creating changelogs, generating release notes, maintaining version history, documenting architectural decisions, or scaffolding project requirements documentation. Use for changelog generation, release notes, version documentation, ADR, PRD, or technical documentation.",
"version": "1.0.0",
"author": {
"name": "Hope Overture",
"email": "support@worldbuilding-app-skills.dev"
},
"skills": [
"./skills"
]
}

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# docs-and-changelogs
Generates comprehensive changelogs from Conventional Commits, maintains CHANGELOG.md files, and scaffolds project documentation like PRD.md or ADR.md. This skill should be used when creating changelogs, generating release notes, maintaining version history, documenting architectural decisions, or scaffolding project requirements documentation. Use for changelog generation, release notes, version documentation, ADR, PRD, or technical documentation.

57
plugin.lock.json Normal file
View File

@@ -0,0 +1,57 @@
{
"$schema": "internal://schemas/plugin.lock.v1.json",
"pluginId": "gh:hopeoverture/worldbuilding-app-skills:plugins/docs-and-changelogs",
"normalized": {
"repo": null,
"ref": "refs/tags/v20251128.0",
"commit": "cfb63484fdfc5263340df4c49b4c99ee1df70eaa",
"treeHash": "f5a41674605bfcb425fff6e526042b8b766bc84f6c95f272e3d6db0dbb382da7",
"generatedAt": "2025-11-28T10:17:34.957922Z",
"toolVersion": "publish_plugins.py@0.2.0"
},
"origin": {
"remote": "git@github.com:zhongweili/42plugin-data.git",
"branch": "master",
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
},
"manifest": {
"name": "docs-and-changelogs",
"description": "Generates comprehensive changelogs from Conventional Commits, maintains CHANGELOG.md files, and scaffolds project documentation like PRD.md or ADR.md. This skill should be used when creating changelogs, generating release notes, maintaining version history, documenting architectural decisions, or scaffolding project requirements documentation. Use for changelog generation, release notes, version documentation, ADR, PRD, or technical documentation.",
"version": "1.0.0"
},
"content": {
"files": [
{
"path": "README.md",
"sha256": "05ec3485cd559235539ce17ff177c84f3bfb6cccde6741a35e08057a36264063"
},
{
"path": ".claude-plugin/plugin.json",
"sha256": "dc1e37d07897a72add6305a6876c9accafb4d6217098566d2aad3671ca9161d2"
},
{
"path": "skills/docs-and-changelogs/SKILL.md",
"sha256": "d1009762952d079aac28e310e02e9aae4c6d9e223cae011c06f363853ed723a7"
},
{
"path": "skills/docs-and-changelogs/scripts/create_prd.py",
"sha256": "7ac9fb4c4e7f0ac50a41083a60c3d362ce947f69fc6b6e577d3522041e9add13"
},
{
"path": "skills/docs-and-changelogs/scripts/generate_changelog.py",
"sha256": "331826193a5e77b3f7d34cc5b61f1f766b13547fdd26a30926bc59f77f04667f"
},
{
"path": "skills/docs-and-changelogs/scripts/create_adr.py",
"sha256": "b24c6f9bd74883213e2b09c2fded60bb85c7acecf711e651a46ac61ad4cd9399"
}
],
"dirSha256": "f5a41674605bfcb425fff6e526042b8b766bc84f6c95f272e3d6db0dbb382da7"
},
"security": {
"scannedAt": null,
"scannerVersion": null,
"flags": []
}
}

View File

@@ -0,0 +1,285 @@
---
name: docs-and-changelogs
description: Generates comprehensive changelogs from Conventional Commits, maintains CHANGELOG.md files, and scaffolds project documentation like PRD.md or ADR.md. This skill should be used when creating changelogs, generating release notes, maintaining version history, documenting architectural decisions, or scaffolding project requirements documentation. Use for changelog generation, release notes, version documentation, ADR, PRD, or technical documentation.
---
# Documentation and Changelogs
Generate and maintain project documentation including changelogs, architectural decision records, and product requirement documents.
## Overview
To manage project documentation effectively:
1. Generate changelogs from git commit history using Conventional Commits
2. Maintain CHANGELOG.md with semantic versioning
3. Create architectural decision records (ADR) for significant decisions
4. Scaffold product requirement documents (PRD) for new features
5. Automate documentation updates as part of release process
## Changelog Generation
To generate changelogs from Conventional Commits:
1. Parse git commit history for conventional commit messages
2. Categorize commits by type (feat, fix, chore, docs, etc.)
3. Group by version/release using git tags
4. Format according to Keep a Changelog standards
5. Append to existing CHANGELOG.md or create new file
Use `scripts/generate_changelog.py` to automate changelog generation from commit history.
### Conventional Commits Format
Follow this commit message structure:
```
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
```
**Types:**
- `feat`: New feature
- `fix`: Bug fix
- `docs`: Documentation changes
- `style`: Code style changes (formatting, semicolons, etc.)
- `refactor`: Code refactoring without feature changes
- `perf`: Performance improvements
- `test`: Adding or updating tests
- `chore`: Maintenance tasks
- `ci`: CI/CD changes
**Breaking Changes:**
- Add `BREAKING CHANGE:` in footer or `!` after type
- Example: `feat!: redesign entity schema structure`
## CHANGELOG.md Maintenance
To maintain changelog file:
1. Structure with sections: Unreleased, versioned releases
2. Use semantic versioning (MAJOR.MINOR.PATCH)
3. Group changes by category (Added, Changed, Deprecated, Removed, Fixed, Security)
4. Include links to commits and pull requests
5. Add release dates in ISO format (YYYY-MM-DD)
Consult `references/changelog-format.md` for detailed formatting guidelines and examples.
## Architectural Decision Records (ADR)
To create architectural decision records:
1. Use `scripts/create_adr.py` to scaffold new ADR file
2. Number ADRs sequentially (0001-title.md, 0002-title.md)
3. Include standard sections: Context, Decision, Consequences
4. Document alternatives considered
5. Reference related ADRs
Use `assets/adr-template.md` as starting point for new ADRs.
### ADR Structure
Standard ADR sections:
- **Title**: Short, descriptive name
- **Status**: Proposed, Accepted, Deprecated, Superseded
- **Context**: What problem are we solving?
- **Decision**: What did we decide to do?
- **Consequences**: What are the tradeoffs and impacts?
- **Alternatives Considered**: What other options were evaluated?
## Product Requirement Documents (PRD)
To scaffold product requirement documents:
1. Use `scripts/create_prd.py` to generate PRD template
2. Define problem statement and goals
3. List functional and non-functional requirements
4. Include user stories and acceptance criteria
5. Document technical constraints and dependencies
Reference `assets/prd-template.md` for comprehensive PRD structure.
### PRD Sections
Standard PRD components:
- **Overview**: High-level description
- **Problem Statement**: What problem are we solving?
- **Goals and Non-Goals**: Scope definition
- **User Stories**: Who, what, why format
- **Requirements**: Functional and non-functional
- **Design Considerations**: UI/UX, architecture
- **Success Metrics**: How to measure success
- **Timeline**: Development phases
## Implementation Steps
### Generate Changelog from Commits
To generate changelog:
1. **Collect Commits**
```bash
python scripts/generate_changelog.py --since v1.0.0
```
2. **Categorize Changes**
- Parse commit messages for conventional commit types
- Extract breaking changes
- Group by scope if present
3. **Format Output**
- Generate markdown with appropriate headings
- Link to commits and PRs
- Add version header with date
4. **Update CHANGELOG.md**
- Prepend new version section
- Maintain existing content
- Update "Unreleased" section
### Create New ADR
To document architectural decision:
1. **Generate ADR File**
```bash
python scripts/create_adr.py "use postgresql for entity storage"
```
2. **Fill Template**
- Document context and constraints
- Explain decision rationale
- List consequences and tradeoffs
3. **Review and Commit**
- Get team feedback
- Update status to "Accepted"
- Link from main architecture docs
### Scaffold New PRD
To create product requirements:
1. **Generate PRD Template**
```bash
python scripts/create_prd.py "timeline visualization feature"
```
2. **Complete Sections**
- Define problem and goals
- Write user stories
- List requirements
3. **Review with Stakeholders**
- Get product team input
- Validate technical feasibility
- Refine scope and requirements
## Automation
To automate documentation updates:
### Release Workflow Integration
Add to `.github/workflows/release.yml`:
```yaml
- name: Generate changelog
run: python scripts/generate_changelog.py --output CHANGELOG.md
- name: Commit changelog
run: |
git config user.name github-actions
git config user.email github-actions@github.com
git add CHANGELOG.md
git commit -m "docs: update changelog for ${{ github.ref_name }}"
```
### Pre-commit Hook
Add to `.git/hooks/commit-msg`:
```bash
#!/bin/bash
# Validate conventional commit format
python scripts/validate_commit_msg.py "$1"
```
## Documentation Structure
Organize project documentation:
```
docs/
├── CHANGELOG.md # Version history
├── ADR/ # Architectural decisions
│ ├── 0001-use-nextjs.md
│ └── 0002-database-choice.md
├── PRD/ # Product requirements
│ ├── timeline-feature.md
│ └── entity-relationships.md
└── api/ # API documentation
└── endpoints.md
```
## Best Practices
### Changelog
- Write for users, not developers
- Use present tense ("Add" not "Added")
- Link to relevant issues/PRs
- Highlight breaking changes prominently
- Keep entries concise and clear
### ADR
- Write when decision is made, not after
- Document alternatives considered
- Be honest about tradeoffs
- Update status if decision changes
- Link related ADRs
### PRD
- Start with user needs, not solutions
- Include success metrics
- Define scope clearly (goals and non-goals)
- Get stakeholder buy-in early
- Update as requirements evolve
## Version Management
To manage semantic versioning:
1. **MAJOR**: Breaking changes (incompatible API changes)
2. **MINOR**: New features (backward-compatible)
3. **PATCH**: Bug fixes (backward-compatible)
Use `scripts/bump_version.py` to update version across package.json, changelog, and tags.
## Release Notes
To generate release notes:
1. Extract relevant changelog section
2. Add highlights and notable changes
3. Include upgrade instructions if needed
4. Link to full changelog
5. Publish to GitHub releases
Use `scripts/generate_release_notes.py` to create formatted release notes from changelog.
## Troubleshooting
Common issues:
- **Commits Not Categorized**: Ensure commits follow Conventional Commits format
- **Missing Version**: Tag releases in git with semantic version numbers
- **Duplicate Entries**: Check for merge conflicts in CHANGELOG.md
- **Broken Links**: Verify commit SHAs and PR numbers are correct

View File

@@ -0,0 +1,172 @@
#!/usr/bin/env python3
"""
Create a new Architectural Decision Record (ADR).
"""
import argparse
import re
from datetime import datetime
from pathlib import Path
def slugify(text):
"""Convert text to lowercase slug format."""
text = text.lower()
text = re.sub(r'[^\w\s-]', '', text)
text = re.sub(r'[-\s]+', '-', text)
return text.strip('-')
def get_next_adr_number(adr_dir):
"""Get the next ADR number in sequence."""
if not adr_dir.exists():
return 1
existing = list(adr_dir.glob('*.md'))
if not existing:
return 1
numbers = []
for path in existing:
match = re.match(r'^(\d+)', path.name)
if match:
numbers.append(int(match.group(1)))
return max(numbers) + 1 if numbers else 1
def create_adr(title, adr_dir='docs/ADR', status='Proposed'):
"""Create a new ADR file."""
adr_dir = Path(adr_dir)
adr_dir.mkdir(parents=True, exist_ok=True)
number = get_next_adr_number(adr_dir)
slug = slugify(title)
filename = f"{number:04d}-{slug}.md"
filepath = adr_dir / filename
date = datetime.now().strftime('%Y-%m-%d')
content = f"""# ADR {number:04d}: {title}
**Status:** {status}
**Date:** {date}
**Deciders:** [List key decision makers]
## Context
[Describe the context and problem statement. What forces are at play? What are the constraints?]
### Problem
[What specific problem are we trying to solve?]
### Constraints
- [Constraint 1]
- [Constraint 2]
## Decision
[Describe the decision we made and why we made it.]
### Rationale
[Explain the reasoning behind the decision. What factors influenced this choice?]
## Consequences
### Positive
- [Positive consequence 1]
- [Positive consequence 2]
### Negative
- [Negative consequence 1]
- [Negative consequence 2]
### Neutral
- [Neutral consequence 1]
## Alternatives Considered
### Alternative 1: [Name]
**Description:** [Brief description]
**Pros:**
- [Pro 1]
**Cons:**
- [Con 1]
**Decision:** Rejected because [reason]
### Alternative 2: [Name]
**Description:** [Brief description]
**Pros:**
- [Pro 1]
**Cons:**
- [Con 1]
**Decision:** Rejected because [reason]
## Implementation
[How will this decision be implemented? What are the next steps?]
- [ ] Task 1
- [ ] Task 2
## Related Decisions
- [Link to related ADR if applicable]
## References
- [Link to relevant documentation]
- [Link to discussions or RFCs]
## Notes
[Any additional notes or context]
"""
with open(filepath, 'w') as f:
f.write(content)
print(f"Created ADR: {filepath}")
print(f"Number: {number:04d}")
print(f"Status: {status}")
return filepath
def main():
parser = argparse.ArgumentParser(description='Create a new Architectural Decision Record')
parser.add_argument('title', help='Title of the ADR')
parser.add_argument('--dir', default='docs/ADR', help='ADR directory path')
parser.add_argument('--status', default='Proposed',
choices=['Proposed', 'Accepted', 'Rejected', 'Deprecated', 'Superseded'],
help='Initial status of the ADR')
args = parser.parse_args()
filepath = create_adr(args.title, args.dir, args.status)
print("\nNext steps:")
print(f"1. Edit {filepath} to complete all sections")
print("2. Review with the team")
print("3. Update status to 'Accepted' when finalized")
print("4. Link from architecture documentation")
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,353 @@
#!/usr/bin/env python3
"""
Create a new Product Requirement Document (PRD).
"""
import argparse
import re
from datetime import datetime
from pathlib import Path
def slugify(text):
"""Convert text to lowercase slug format."""
text = text.lower()
text = re.sub(r'[^\w\s-]', '', text)
text = re.sub(r'[-\s]+', '-', text)
return text.strip('-')
def create_prd(title, prd_dir='docs/PRD', author=''):
"""Create a new PRD file."""
prd_dir = Path(prd_dir)
prd_dir.mkdir(parents=True, exist_ok=True)
slug = slugify(title)
filename = f"{slug}.md"
filepath = prd_dir / filename
date = datetime.now().strftime('%Y-%m-%d')
content = f"""# Product Requirement Document: {title}
**Author:** {author or '[Your Name]'}
**Date:** {date}
**Status:** Draft
**Last Updated:** {date}
## Executive Summary
[Brief 2-3 sentence overview of what this feature is and why it matters]
## Problem Statement
### Current Situation
[Describe the current state and what pain points exist]
### User Impact
[Who is affected by this problem and how?]
### Business Impact
[What is the business cost of not solving this problem?]
## Goals and Objectives
### Primary Goals
1. [Goal 1]
2. [Goal 2]
3. [Goal 3]
### Success Metrics
- [Metric 1]: [Target]
- [Metric 2]: [Target]
- [Metric 3]: [Target]
### Non-Goals
[What is explicitly out of scope for this feature?]
- [Non-goal 1]
- [Non-goal 2]
## User Stories
### Story 1: [User Type]
**As a** [user type]
**I want to** [action]
**So that** [benefit]
**Acceptance Criteria:**
- [ ] [Criterion 1]
- [ ] [Criterion 2]
- [ ] [Criterion 3]
### Story 2: [User Type]
**As a** [user type]
**I want to** [action]
**So that** [benefit]
**Acceptance Criteria:**
- [ ] [Criterion 1]
- [ ] [Criterion 2]
## Requirements
### Functional Requirements
#### Must Have (P0)
1. **[Requirement 1]**
- Description: [Details]
- User Impact: [High/Medium/Low]
2. **[Requirement 2]**
- Description: [Details]
- User Impact: [High/Medium/Low]
#### Should Have (P1)
1. **[Requirement 3]**
- Description: [Details]
- User Impact: [High/Medium/Low]
#### Nice to Have (P2)
1. **[Requirement 4]**
- Description: [Details]
- User Impact: [High/Medium/Low]
### Non-Functional Requirements
#### Performance
- [Performance requirement 1]
- [Performance requirement 2]
#### Security
- [Security requirement 1]
- [Security requirement 2]
#### Accessibility
- [Accessibility requirement 1]
- [Accessibility requirement 2]
#### Scalability
- [Scalability requirement 1]
- [Scalability requirement 2]
## Design Considerations
### User Experience
[Describe the intended user experience]
#### User Flow
1. [Step 1]
2. [Step 2]
3. [Step 3]
#### UI Components
- [Component 1]: [Description]
- [Component 2]: [Description]
### Technical Architecture
[High-level technical approach]
#### Components
- **[Component 1]**: [Purpose and responsibilities]
- **[Component 2]**: [Purpose and responsibilities]
#### Data Model
[Overview of data structures and relationships]
#### APIs
- **[Endpoint 1]**: [Purpose]
- **[Endpoint 2]**: [Purpose]
### Integration Points
- [System/Service 1]: [How it integrates]
- [System/Service 2]: [How it integrates]
## Dependencies
### Internal Dependencies
- [Dependency 1]: [Why needed]
- [Dependency 2]: [Why needed]
### External Dependencies
- [Third-party service 1]: [Purpose]
- [Third-party service 2]: [Purpose]
### Blocking Issues
- [Issue 1]: [Resolution plan]
- [Issue 2]: [Resolution plan]
## Risks and Mitigation
| Risk | Impact | Likelihood | Mitigation Strategy |
|------|--------|------------|---------------------|
| [Risk 1] | High/Medium/Low | High/Medium/Low | [Strategy] |
| [Risk 2] | High/Medium/Low | High/Medium/Low | [Strategy] |
## Timeline and Milestones
### Phase 1: [Name] ([Duration])
- [ ] [Milestone 1]
- [ ] [Milestone 2]
### Phase 2: [Name] ([Duration])
- [ ] [Milestone 3]
- [ ] [Milestone 4]
### Phase 3: [Name] ([Duration])
- [ ] [Milestone 5]
- [ ] [Milestone 6]
### Target Launch Date
[Date or timeframe]
## Testing Strategy
### Unit Testing
[Approach to unit testing]
### Integration Testing
[Approach to integration testing]
### User Acceptance Testing
[UAT plan and criteria]
### Performance Testing
[Performance testing approach]
## Launch Plan
### Pre-Launch Checklist
- [ ] [Item 1]
- [ ] [Item 2]
- [ ] [Item 3]
### Rollout Strategy
[Describe how the feature will be rolled out]
- **Audience**: [Who gets access first]
- **Timeline**: [Phased rollout schedule]
- **Monitoring**: [What metrics to watch]
### Communication Plan
- **Internal**: [How to communicate to team]
- **External**: [How to communicate to users]
### Rollback Plan
[How to rollback if issues arise]
## Post-Launch
### Monitoring
- [Metric to monitor 1]
- [Metric to monitor 2]
### Iteration Plan
[How we'll iterate based on feedback]
### Success Evaluation
[How and when we'll evaluate if we met our goals]
## Open Questions
- [ ] [Question 1]
- [ ] [Question 2]
- [ ] [Question 3]
## Stakeholders
| Name | Role | Responsibility |
|------|------|----------------|
| [Name] | [Role] | [What they're responsible for] |
| [Name] | [Role] | [What they're responsible for] |
## References
- [Link to related PRDs]
- [Link to design mockups]
- [Link to technical specs]
- [Link to user research]
## Appendix
### Terminology
- **[Term 1]**: [Definition]
- **[Term 2]**: [Definition]
### Additional Resources
- [Resource 1]
- [Resource 2]
"""
with open(filepath, 'w') as f:
f.write(content)
print(f"Created PRD: {filepath}")
return filepath
def main():
parser = argparse.ArgumentParser(description='Create a new Product Requirement Document')
parser.add_argument('title', help='Title of the PRD')
parser.add_argument('--dir', default='docs/PRD', help='PRD directory path')
parser.add_argument('--author', help='Document author name')
args = parser.parse_args()
filepath = create_prd(args.title, args.dir, args.author or '')
print("\nNext steps:")
print(f"1. Edit {filepath} to complete all sections")
print("2. Share with stakeholders for review")
print("3. Refine based on feedback")
print("4. Get final approval before implementation")
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,243 @@
#!/usr/bin/env python3
"""
Generate CHANGELOG.md from Conventional Commits in git history.
"""
import argparse
import re
import subprocess
from collections import defaultdict
from datetime import datetime
from pathlib import Path
COMMIT_PATTERN = re.compile(
r'^(?P<type>\w+)(?:\((?P<scope>[\w-]+)\))?(?P<breaking>!)?: (?P<description>.+)$'
)
TYPE_HEADERS = {
'feat': 'Added',
'fix': 'Fixed',
'docs': 'Documentation',
'style': 'Style',
'refactor': 'Changed',
'perf': 'Performance',
'test': 'Tests',
'chore': 'Maintenance',
'ci': 'CI/CD',
'build': 'Build',
'revert': 'Reverted',
}
def get_commits(since=None, until='HEAD'):
"""Get git commits in range."""
cmd = ['git', 'log', '--pretty=format:%H|%s|%b|%an|%ae|%ad', '--date=short']
if since:
cmd.append(f'{since}..{until}')
else:
cmd.append(until)
result = subprocess.run(cmd, capture_output=True, text=True)
commits = []
for line in result.stdout.strip().split('\n'):
if not line:
continue
parts = line.split('|')
if len(parts) >= 6:
commits.append({
'hash': parts[0],
'subject': parts[1],
'body': parts[2],
'author': parts[3],
'email': parts[4],
'date': parts[5],
})
return commits
def parse_commit(commit):
"""Parse conventional commit message."""
match = COMMIT_PATTERN.match(commit['subject'])
if not match:
return None
parsed = {
'type': match.group('type'),
'scope': match.group('scope'),
'breaking': bool(match.group('breaking')),
'description': match.group('description'),
'hash': commit['hash'][:7],
'full_hash': commit['hash'],
'body': commit['body'],
}
# Check for BREAKING CHANGE in body
if 'BREAKING CHANGE' in commit['body']:
parsed['breaking'] = True
# Extract breaking change description
breaking_match = re.search(r'BREAKING CHANGE:\s*(.+)', commit['body'])
if breaking_match:
parsed['breaking_description'] = breaking_match.group(1)
return parsed
def group_commits(commits):
"""Group commits by type and breaking changes."""
groups = defaultdict(list)
breaking = []
for commit in commits:
parsed = parse_commit(commit)
if not parsed:
continue
if parsed['breaking']:
breaking.append(parsed)
commit_type = parsed['type']
groups[commit_type].append(parsed)
return groups, breaking
def format_commit(commit):
"""Format a single commit for changelog."""
scope = f"**{commit['scope']}**: " if commit['scope'] else ""
return f"- {scope}{commit['description']} ([{commit['hash']}](../../commit/{commit['full_hash']}))"
def generate_changelog_section(version, date, groups, breaking):
"""Generate a changelog section for a version."""
lines = [
f"## [{version}] - {date}",
""
]
# Breaking changes first
if breaking:
lines.append("### BREAKING CHANGES")
lines.append("")
for commit in breaking:
if 'breaking_description' in commit:
lines.append(f"- {commit['breaking_description']}")
else:
lines.append(format_commit(commit))
lines.append("")
# Regular changes by type
for commit_type, header in TYPE_HEADERS.items():
if commit_type in groups and groups[commit_type]:
lines.append(f"### {header}")
lines.append("")
for commit in groups[commit_type]:
lines.append(format_commit(commit))
lines.append("")
return '\n'.join(lines)
def get_current_version():
"""Get current version from package.json."""
try:
import json
with open('package.json') as f:
data = json.load(f)
return data.get('version', '0.0.0')
except:
return '0.0.0'
def get_last_tag():
"""Get the most recent git tag."""
result = subprocess.run(
['git', 'describe', '--tags', '--abbrev=0'],
capture_output=True,
text=True
)
if result.returncode == 0:
return result.stdout.strip()
return None
def update_changelog(content, output_path):
"""Update or create CHANGELOG.md file."""
changelog_path = Path(output_path)
if changelog_path.exists():
with open(changelog_path) as f:
existing = f.read()
# Insert new content after header
if '## [' in existing:
parts = existing.split('## [', 1)
updated = parts[0] + content + '\n## [' + parts[1]
else:
updated = existing + '\n\n' + content
else:
# Create new changelog with header
header = """# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
"""
updated = header + content
with open(changelog_path, 'w') as f:
f.write(updated)
print(f"Updated: {changelog_path}")
def main():
parser = argparse.ArgumentParser(description='Generate changelog from git commits')
parser.add_argument('--since', help='Start from this tag/commit')
parser.add_argument('--until', default='HEAD', help='End at this tag/commit')
parser.add_argument('--version', help='Version number for this release')
parser.add_argument('--output', default='CHANGELOG.md', help='Output file path')
parser.add_argument('--date', help='Release date (YYYY-MM-DD)')
args = parser.parse_args()
# Determine version and since
version = args.version or get_current_version()
since = args.since or get_last_tag()
if not since:
print("Warning: No previous tag found. Generating changelog for all commits.")
# Get and parse commits
commits = get_commits(since=since, until=args.until)
if not commits:
print("No commits found in range.")
return
groups, breaking = group_commits(commits)
# Generate changelog section
date = args.date or datetime.now().strftime('%Y-%m-%d')
changelog_content = generate_changelog_section(version, date, groups, breaking)
# Update changelog file
update_changelog(changelog_content, args.output)
# Print summary
print(f"\nGenerated changelog for version {version}")
print(f" Commits processed: {len(commits)}")
print(f" Breaking changes: {len(breaking)}")
print(f" Features: {len(groups.get('feat', []))}")
print(f" Bug fixes: {len(groups.get('fix', []))}")
if __name__ == '__main__':
main()