generate changelog json files on tag

This commit is contained in:
ArabCoders
2025-02-03 22:09:19 +03:00
parent 821e61103a
commit a5cf50c47b
4 changed files with 177 additions and 103 deletions

117
.github/scripts/generate_changelog.py vendored Normal file
View File

@@ -0,0 +1,117 @@
#!/usr/bin/env python3
import git
import argparse
import json
from datetime import datetime, timezone
def get_tags(repo, branch_name):
"""Returns sorted tags by date, filtered by branch name."""
tags = [tag for tag in repo.tags if tag.name.startswith(branch_name)]
# Sort tags in reverse chronological order based on the tag commit's date.
tags = sorted(tags, key=lambda t: t.commit.committed_datetime, reverse=True)
return tags
def get_commits_between(repo, start_commit, end_commit):
"""Get commit objects between two commits (excluding merges)."""
commits = list(repo.iter_commits(f"{start_commit}..{end_commit}", no_merges=True))
return commits
def format_tag(tag, branch_name):
"""Formats the tag as 'branch-YYYYMMDD-shortsha'."""
commit_date = datetime.fromtimestamp(
tag.commit.committed_date, timezone.utc
).strftime("%Y%m%d")
commit_hash = tag.commit.hexsha[:7] # Short commit hash
return f"{branch_name}-{commit_date}-{commit_hash}"
def generate_changelog(repo_path, changelog_path, branch_name):
repo = git.Repo(repo_path)
tags = get_tags(repo, branch_name)
changelog_data = []
# No tags: output an "Initial Release" entry covering the entire history.
if not tags:
start_commit = repo.commit(repo.git.rev_list("--max-parents=0", "HEAD"))
commits = get_commits_between(repo, start_commit.hexsha, "HEAD")
date_str = datetime.now(timezone.utc).strftime("%Y-%m-%d")
release_entry = {
"tag": "Initial Release",
"date": date_str,
"commits": []
}
for commit in commits:
commit_entry = {
"sha": commit.hexsha[:7],
"message": commit.message.strip(),
"author": commit.author.name,
"date": commit.committed_datetime.strftime("%Y-%m-%d")
}
release_entry["commits"].append(commit_entry)
changelog_data.append(release_entry)
else:
# Process each pair of tags (newest tag down to the second oldest tag).
# This will output a changelog entry for each tag that has commits
# between it and the next tag.
for i in range(len(tags) - 1):
tag = tags[i] # Newest tag in the pair.
next_tag = tags[i + 1] # Older tag
# Retrieve commits between the older and newer tag.
commits = get_commits_between(repo, next_tag.commit.hexsha, tag.commit.hexsha)
if not commits:
continue
# Use the commit date of the tag for the release entry.
date_str = tag.commit.committed_datetime.strftime("%Y-%m-%d")
formatted_tag = format_tag(tag, branch_name)
release_entry = {
"tag": formatted_tag,
"date": date_str,
"commits": []
}
for commit in commits:
commit_entry = {
"sha": commit.hexsha[:7],
"message": commit.message.strip(),
"author": commit.author.name,
"date": commit.committed_datetime.strftime("%Y-%m-%d")
}
release_entry["commits"].append(commit_entry)
changelog_data.append(release_entry)
# Write the changelog data as a JSON file.
with open(changelog_path, "w", encoding="utf-8") as f:
json.dump(changelog_data, f, indent=4)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Generate a changelog from git history and output JSON."
)
parser.add_argument(
"--repo_path", "-p", type=str, help="Path to the git repository", default="."
)
parser.add_argument(
"--changelog_path",
"-f",
type=str,
default="./CHANGELOG.json",
help="Path to the output JSON file (default: ./CHANGELOG.json)",
)
parser.add_argument(
"--branch_name",
"-b",
type=str,
default="master",
help="Branch name to filter tags (default: master)",
)
args = parser.parse_args()
generate_changelog(args.repo_path, args.changelog_path, args.branch_name)

View File

@@ -55,7 +55,7 @@ jobs:
strategy:
fail-fast: true
matrix:
php: [8.4]
php: [ 8.4 ]
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -144,8 +144,6 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # Ensure all commit history is available
- uses: bahmutov/npm-install@v1
with:
@@ -165,36 +163,6 @@ jobs:
with_date: "true"
with_branch: "true"
- name: Version tag
uses: arabcoders/action-python-autotagger@master
with:
token: ${{ secrets.GITHUB_TOKEN }}
repo_name: arabcoders/watchstate
path: config/config.php
regex: "'version'\\s\\=\\>\\s\\'(.+?)\\'\\,"
- name: Determine current branch
id: branch
run: |
echo "BRANCH_NAME=${GITHUB_REF_NAME}" >> $GITHUB_ENV
- name: Fetch all tags
run: git fetch --tags
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.x"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install gitpython
- name: Generate Changelog
run: |
python container/make_changelog.py --repo_path . --changelog_path ./CHANGELOG.md --branch_name $BRANCH_NAME
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
@@ -240,6 +208,14 @@ jobs:
cache-from: type=gha, scope=${{ github.workflow }}
cache-to: type=gha, scope=${{ github.workflow }}
- name: Version tag
uses: arabcoders/action-python-autotagger@master
with:
token: ${{ secrets.GITHUB_TOKEN }}
repo_name: arabcoders/watchstate
path: config/config.php
regex: "'version'\\s\\=\\>\\s\\'(.+?)\\'\\,"
dockerhub-sync-readme:
runs-on: ubuntu-latest
if: (github.event_name == 'push' && endsWith(github.ref, github.event.repository.default_branch)) || (github.event_name == 'workflow_dispatch' && github.event.inputs.update_readme == 'true')
@@ -266,7 +242,7 @@ jobs:
- name: Check out code
uses: actions/checkout@v4
with:
fetch-depth: 0 # so we can see all tags + full history
fetch-depth: 0 # so we can see all tags + full history
- name: Determine current branch
id: branch

50
.github/workflows/changelog.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
name: Generate Changelog
on:
push:
tags:
- "*" # Trigger on any tag push
jobs:
generate-changelog:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Ensure we get all tags and history
- name: Set Up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install gitpython
- name: Get Branch Name from Tag
id: branch_name
run: |
TAG_NAME=$(git describe --tags --abbrev=0)
BRANCH_NAME=$(echo "$TAG_NAME" | cut -d '-' -f1)
echo "TAG_NAME=$TAG_NAME" >> $GITHUB_ENV
echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV
echo "Detected Tag: $TAG_NAME on Branch: $BRANCH_NAME"
- name: Generate Changelog JSON
run: |
python .github/scripts/generate_changelog.py --repo_path . --changelog_path ./CHANGELOG-${BRANCH_NAME}.json --branch_name $BRANCH_NAME
- name: Commit and Push to GitHub Pages
run: |
git config --global user.name "github-actions"
git config --global user.email "github-actions@github.com"
git checkout gh-pages || git checkout --orphan gh-pages
git pull origin gh-pages || true
mv CHANGELOG-*.json .
git add CHANGELOG-*.json
git commit -m "Update Changelog for $TAG_NAME"
git push origin gh-pages

View File

@@ -1,69 +0,0 @@
#!/usr/bin/env python3
import git
import argparse
from datetime import datetime, timezone
def get_tags(repo, branch_name):
"""Returns sorted tags by date, filtered by branch name."""
tags = [tag for tag in repo.tags if tag.name.startswith(branch_name)]
tags = sorted(tags, key=lambda t: t.commit.committed_datetime, reverse=True)
return tags
def get_commits_between(repo, start_commit, end_commit):
"""Get commit messages between two commits (excluding merges)."""
commits = list(repo.iter_commits(f"{start_commit}..{end_commit}", no_merges=True))
return commits
def format_tag(tag, branch_name):
"""Formats the tag as 'branch-latest-tag-date-hash'."""
commit_date = datetime.fromtimestamp(tag.commit.committed_date, timezone.utc).strftime("%Y%m%d")
commit_hash = tag.commit.hexsha[:7] # Short commit hash
return f"{branch_name}-{commit_date}-{commit_hash}"
def generate_changelog(repo_path, changelog_path, branch_name):
repo = git.Repo(repo_path)
tags = get_tags(repo, branch_name)
changelog_entries = []
if not tags:
start_commit = repo.commit(repo.git.rev_list("--max-parents=0", "HEAD"))
commits = get_commits_between(repo, start_commit.hexsha, "HEAD")
date_str = datetime.now(timezone.utc).strftime("%Y-%m-%d")
changelog_entries.append(f"# {date_str} - Initial Release\n\n")
for commit in commits:
changelog_entries.append(f"* {commit.summary}\n")
else:
for i in range(len(tags) - 1):
tag = tags[i] # Newest tag
next_tag = tags[i + 1] # Older tag
tag_commit = tag.commit.hexsha
next_tag_commit = next_tag.commit.hexsha
commits = get_commits_between(repo, next_tag_commit, tag_commit)
if not commits:
continue
date_str = datetime.now(timezone.utc).strftime("%Y-%m-%d")
formatted_tag = format_tag(tag, branch_name)
changelog_entries.append(f"# {date_str} - {formatted_tag}\n\n")
for commit in commits:
changelog_entries.append(f"* {commit.summary}\n")
changelog_entries.append("\n")
with open(changelog_path, "w", encoding="utf-8") as f:
f.writelines(changelog_entries)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate a changelog from git history.")
parser.add_argument("--repo_path", "-p", type=str, help="Path to the git repository", default=".")
parser.add_argument("--changelog_path", "-f", type=str, default="./CHANGELOG.md", help="Path to the output CHANGELOG.md file (default: ./CHANGELOG.md)")
parser.add_argument("--branch_name", "-b", type=str, default="master", help="Branch name to filter tags (default: master)")
args = parser.parse_args()
generate_changelog(args.repo_path, args.changelog_path, args.branch_name)