Skip to content

Commit

Permalink
add auto update changelong
Browse files Browse the repository at this point in the history
  • Loading branch information
Blaizzy committed Jul 16, 2024
1 parent 74fbd35 commit c6bcbf6
Show file tree
Hide file tree
Showing 2 changed files with 243 additions and 0 deletions.
60 changes: 60 additions & 0 deletions .github/workflows/update_changelog.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Update Changelog

on:
release:
types: [published]
push:
branches:
- main # or your default branch name
pull_request:
branches:
- main # or your default branch name

jobs:
test-changelog-update:
if: github.event_name == 'push' || github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests python-dotenv semver
- name: Test Changelog Update
env:
GITHUB_TOKEN: ${{ secrets.UPDATE_CHANGELOG_TOKEN }}
run: |
python update_changelog.py
if [[ -n $(git status --porcelain) ]]; then
echo "Changelog was updated. Script is working correctly."
else
echo "No changes to changelog. Please check if this is expected."
fi
update-changelog:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests python-dotenv
- name: Update Changelog
env:
GITHUB_TOKEN: ${{ secrets.UPDATE_CHANGELOG_TOKEN }}
run: python update_changelog.py
- name: Commit changes
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add docs/changelog.md
git commit -m "Update changelog for latest release" || echo "No changes to commit"
git push
183 changes: 183 additions & 0 deletions update_changelog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
"""
Script to automatically update the CHANGELOG.md file based on GitHub releases.
This script fetches release information from the GitHub API and updates
the CHANGELOG.md file with the latest release notes.
Usage:
python update_changelog.py
Requirements:
- requests
- python-dotenv
Make sure to set up a .env file with your GitHub token:
GITHUB_TOKEN=your_token_here
"""

import os
import re
from datetime import datetime

import requests
import semver
from dotenv import load_dotenv

# Load environment variables
load_dotenv()

# GitHub repository details
REPO_OWNER = "Blaizzy"
REPO_NAME = "fastmlx"
GITHUB_TOKEN = os.getenv("UPDATE_CHANGELOG_TOKEN")


# File paths
CHANGELOG_PATH = "docs/changelog.md"


def get_releases():
"""Fetch all releases information from GitHub API."""
url = f"https://api.github.com/repos/{REPO_OWNER}/{REPO_NAME}/releases"
headers = {
"Authorization": f"token {GITHUB_TOKEN}",
"Accept": "application/vnd.github.v3+json",
}
response = requests.get(url, headers=headers)
response.raise_for_status()
return response.json()


def parse_version(version_string):
"""Parse version string to semver object, handling 'v' prefix."""
return semver.VersionInfo.parse(version_string.lstrip("v"))


def create_issue_link(issue_number):
"""Create a clickable link for an issue."""
return f"[#{issue_number}](https://github.com/{REPO_OWNER}/{REPO_NAME}/issues/{issue_number})"


def create_contributor_link(username):
"""Create a clickable link for a contributor."""
return f"[@{username}](https://github.com/{username})"


def format_release_notes(body):
"""Format the release notes in a cleaner structure."""
formatted_notes = []
current_section = None

for line in body.split("\n"):
line = line.strip()

if not line:
continue

if line.startswith("##"):
current_section = line.lstrip("#").strip()
formatted_notes.append(f"\n**{current_section}**\n")
elif line.startswith("**Full Changelog**"):
# Skip the full changelog link
continue
elif line.startswith("*"):
if line.startswith("* @"):
# Handle new contributors
match = re.match(
r"\* @(\w+) made their first contribution in (https://.*)", line
)
if match:
username, url = match.groups()
formatted_notes.append(
f"- {create_contributor_link(username)} made their first contribution in [{url}]({url})"
)
else:
# Clean up bullet points
cleaned_line = re.sub(
r"by @(\w+) in (https://.*)",
lambda m: f"by {create_contributor_link(m.group(1))}",
line,
)
cleaned_line = cleaned_line.replace("* ", "- ")
formatted_notes.append(cleaned_line)
else:

formatted_notes.append(line)

# Replace issue numbers with clickable links
formatted_notes = [
re.sub(r"#(\d+)", lambda m: create_issue_link(m.group(1)), item)
for item in formatted_notes
]

return "\n".join(formatted_notes)


def update_changelog(releases):
"""Update the CHANGELOG.md file with the release information."""
with open(CHANGELOG_PATH, "r") as file:
content = file.read()

# Extract existing versions from changelog
existing_versions = re.findall(r"##\s*\[?v?([\d.]+)", content)
existing_versions = [parse_version(v) for v in existing_versions]

# Sort releases by version (newest first)
releases.sort(key=lambda r: parse_version(r["tag_name"]), reverse=True)

new_content = ""
added_versions = []

for release in releases:
version = parse_version(release["tag_name"])

# Skip if this version is already in the changelog
if version in existing_versions:
continue

print(f"Adding new version: {version}") # Debug print

release_date = datetime.strptime(
release["published_at"], "%Y-%m-%dT%H:%M:%SZ"
).strftime("%d %B %Y")
new_content += f"## [{release['tag_name']}] - {release_date}\n\n"
new_content += format_release_notes(release["body"])
new_content += "\n\n"
added_versions.append(version)

if new_content:
# Find the position to insert new content (after the header and introduction)
header_end = content.find("\n\n", content.find("# Changelog"))
if header_end == -1:
header_end = content.find("\n", content.find("# Changelog"))

if header_end != -1:
updated_content = (
content[: header_end + 2] + new_content + content[header_end + 2 :]
)
else:
# If we can't find the proper position, just prepend the new content
updated_content = "# Changelog\n\n" + new_content + content

# Write updated changelog
with open(CHANGELOG_PATH, "w") as file:
file.write(updated_content)
else:
print("No new versions to add")


def main():
try:
releases = get_releases()
update_changelog(releases)
print("Changelog updated and formatted successfully")
except requests.RequestException as e:
print(f"Error fetching release information: {e}")
except IOError as e:
print(f"Error updating changelog file: {e}")
except Exception as e:
print(f"An unexpected error occurred: {e}")


if __name__ == "__main__":
main()

0 comments on commit c6bcbf6

Please sign in to comment.