-
Notifications
You must be signed in to change notification settings - Fork 3
Replace the changelog action with a maintained one #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
shuo-zhou
wants to merge
2
commits into
main
Choose a base branch
from
ci/changelog-automation
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| { | ||
| "template": "#{{CHANGELOG}}\n\n#### Other Changes\n\n#{{UNCATEGORIZED}}", | ||
| "pr_template": "- #{{TITLE}} ([##{{NUMBER}}](#{{URL}}))", | ||
| "empty_template": "- No pull requests were merged since the last release.", | ||
| "ignore_labels": ["dependabot", "bot", "ci"], | ||
| "categories": [ | ||
| { | ||
| "title": "#### New Features", | ||
| "labels": ["feature", "enhancement"] | ||
| }, | ||
| { | ||
| "title": "#### Bug Fixes", | ||
| "labels": ["bug", "bugfix"] | ||
| }, | ||
| { | ||
| "title": "#### Code Improvements", | ||
| "labels": ["improvements", "refactor", "refactoring"] | ||
| }, | ||
| { | ||
| "title": "#### Documentation", | ||
| "labels": ["documentation", "docs", "doc"] | ||
| }, | ||
| { | ||
| "title": "#### Tests", | ||
| "labels": ["tests", "testing", "test"] | ||
| } | ||
| ] | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| #!/usr/bin/env python3 | ||
| """Insert a generated release section into .github/CHANGELOG.md. | ||
|
|
||
| Usage: update_changelog.py <version> <generated-file> <changelog-file> | ||
|
|
||
| The generated file holds the label-grouped list of pull requests produced by | ||
| release-changelog-builder-action. It is turned into a section | ||
|
|
||
| # Version <version> | ||
|
|
||
| #### <group> | ||
|
|
||
| - <pull request> | ||
|
|
||
| which is written to the top of the changelog. Group headings without any | ||
| entries below them are dropped, so an empty "Other Changes" group does not | ||
| leave a dangling heading behind. | ||
|
|
||
| The generated file is rewritten to hold the final section, so the same text | ||
| can be reused as the pull request comment body. | ||
|
|
||
| Running the workflow more than once for the same version is safe: a section | ||
| that is already present is replaced in place instead of being duplicated, and | ||
| identical content leaves the file untouched. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import argparse | ||
| import re | ||
| import sys | ||
| from pathlib import Path | ||
|
|
||
| SECTION_RE = re.compile(r"^#\s+Version\s+(?P<version>\S+)\s*$") | ||
| GROUP_RE = re.compile(r"^#{2,6}\s") | ||
|
|
||
|
|
||
| def normalise_version(version: str) -> str: | ||
| """Drop a leading ``v`` so tag names and headers compare equal.""" | ||
| return version.strip().lstrip("vV") | ||
|
|
||
|
|
||
| def tidy(text: str) -> str: | ||
| """Trim trailing whitespace and collapse runs of blank lines.""" | ||
| lines = [line.rstrip() for line in text.splitlines()] | ||
| collapsed: list[str] = [] | ||
| for line in lines: | ||
| if not line and collapsed and not collapsed[-1]: | ||
| continue | ||
| collapsed.append(line) | ||
| while collapsed and not collapsed[0]: | ||
| collapsed.pop(0) | ||
| while collapsed and not collapsed[-1]: | ||
| collapsed.pop() | ||
| return "\n".join(collapsed) | ||
|
|
||
|
|
||
| def drop_empty_groups(text: str) -> str: | ||
| """Remove group headings that have no entries below them.""" | ||
| lines = text.splitlines() | ||
| kept: list[str] = [] | ||
| index = 0 | ||
| while index < len(lines): | ||
| if GROUP_RE.match(lines[index]): | ||
| end = index + 1 | ||
| while end < len(lines) and not GROUP_RE.match(lines[end]): | ||
| end += 1 | ||
| if any(line.strip() for line in lines[index + 1 : end]): | ||
| kept.extend(lines[index:end]) | ||
| index = end | ||
| else: | ||
| kept.append(lines[index]) | ||
| index += 1 | ||
| return tidy("\n".join(kept)) | ||
|
|
||
|
|
||
| def replace_section(changelog: str, version: str, section: str) -> str: | ||
| """Replace the section of ``version``, or prepend it when absent.""" | ||
| lines = changelog.splitlines() | ||
| start = None | ||
| for index, line in enumerate(lines): | ||
| match = SECTION_RE.match(line) | ||
| if match and normalise_version(match.group("version")) == version: | ||
| start = index | ||
| break | ||
|
|
||
| if start is None: | ||
| return tidy(f"{section}\n\n{changelog}") + "\n" | ||
|
|
||
| end = len(lines) | ||
| for index in range(start + 1, len(lines)): | ||
| if SECTION_RE.match(lines[index]): | ||
| end = index | ||
| break | ||
|
|
||
| merged = "\n\n".join(["\n".join(lines[:start]), section, "\n".join(lines[end:])]) | ||
| return tidy(merged) + "\n" | ||
|
|
||
|
|
||
| def main() -> int: | ||
| parser = argparse.ArgumentParser(description=__doc__) | ||
| parser.add_argument("version", help="release version, e.g. 0.2.0b1") | ||
| parser.add_argument("generated", type=Path, help="changelog built by the action") | ||
| parser.add_argument("changelog", type=Path, help="changelog file to update") | ||
| args = parser.parse_args() | ||
|
|
||
| if not args.generated.is_file(): | ||
| sys.stderr.write(f"error: {args.generated} was not generated\n") | ||
| return 1 | ||
|
|
||
| body = drop_empty_groups(args.generated.read_text(encoding="utf-8")) | ||
| if not body: | ||
| sys.stderr.write(f"error: {args.generated} is empty\n") | ||
| return 1 | ||
|
|
||
| version = normalise_version(args.version) | ||
| section = f"# Version {version}\n\n{body}" | ||
|
|
||
| existing = args.changelog.read_text(encoding="utf-8") if args.changelog.is_file() else "" | ||
| args.changelog.write_text(replace_section(existing, version, section), encoding="utf-8") | ||
| # Reuse the section as the pull request comment body. | ||
| args.generated.write_text(section + "\n", encoding="utf-8") | ||
|
|
||
| sys.stdout.write(f"updated {args.changelog} for version {version}\n") | ||
| return 0 | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| raise SystemExit(main()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,31 +1,122 @@ | ||
| # This workflow will generate a log of changes automatically upon a new release. | ||
| # See https://github.com/marketplace/actions/changelog-ci | ||
| # Generates the changelog for release pull requests. | ||
| # | ||
| # A pull request whose title mentions a release and carries a version, e.g. | ||
| # "Prepare the 0.2.0b1 release of kalelinear", gets a label-grouped changelog | ||
| # of every pull request merged since the previous tag. The section is written | ||
| # to .github/CHANGELOG.md, committed to the pull request branch, and posted as | ||
| # a pull request comment. | ||
| # | ||
| # See https://github.com/mikepenz/release-changelog-builder-action | ||
|
|
||
| name: changelog | ||
|
|
||
| on: | ||
| pull_request: | ||
| types: [opened] | ||
| # "opened"/"reopened" cover the usual flow, and "edited" picks up a pull | ||
| # request that is retitled into a release. "synchronize" is deliberately | ||
| # left out: the changelog commit below would trigger it again. | ||
| types: [opened, reopened, edited] | ||
|
|
||
| # Writing the changelog commit and the pull request comment needs more than | ||
| # the default read-only token. Without these permissions both operations fail | ||
| # and the action only reports a warning, leaving a green but empty run. | ||
| permissions: | ||
| contents: write | ||
| pull-requests: write | ||
|
|
||
| # A newer run of the same pull request supersedes the one in flight. | ||
| concurrency: | ||
| group: changelog-${{ github.event.pull_request.number }} | ||
| cancel-in-progress: true | ||
|
|
||
| jobs: | ||
| log-changes: | ||
| name: Log changes | ||
| runs-on: ubuntu-latest | ||
| # Pull requests from forks receive a read-only token, which can neither | ||
| # push nor comment. Only branches of this repository are supported. | ||
| if: github.event.pull_request.head.repo.full_name == github.repository | ||
| steps: | ||
| - name: Checkout code | ||
| - name: Checkout pull request branch | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| repository: ${{ github.event.pull_request.head.repo.full_name }} | ||
| ref: ${{ github.event.pull_request.head.ref }} | ||
| # Keep checkout shallow; changelog-ci handles unshallow internally. | ||
| # Using fetch-depth: 0 causes changelog-ci to fail with: | ||
| # "fatal: --unshallow on a complete repository does not make sense" | ||
| fetch-depth: 1 | ||
| # The full history is needed to locate the previous release tag. | ||
| fetch-depth: 0 | ||
| token: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Run changelog | ||
| uses: saadmk11/changelog-ci@v1.2.0 | ||
|
|
||
| - name: Get release version from pull request title | ||
| id: version | ||
| env: | ||
| PR_TITLE: ${{ github.event.pull_request.title }} | ||
| run: | | ||
| version="" | ||
| if printf '%s' "$PR_TITLE" | grep -qi 'release'; then | ||
| version=$(printf '%s' "$PR_TITLE" \ | ||
| | grep -oEi 'v?[0-9]+\.[0-9]+\.[0-9]+([ab][0-9]+)?' \ | ||
| | head -n1 || true) | ||
|
shuo-zhou marked this conversation as resolved.
|
||
| fi | ||
| if [ -z "$version" ]; then | ||
| echo "::notice::Not a release pull request, skipping changelog generation." | ||
| fi | ||
| echo "version=$version" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Find previous release tag | ||
| id: previous | ||
| if: steps.version.outputs.version != '' | ||
| run: | | ||
| tag=$(git describe --tags --abbrev=0 2>/dev/null || true) | ||
| echo "tag=$tag" >> "$GITHUB_OUTPUT" | ||
| echo "Previous release tag: ${tag:-none}" | ||
|
|
||
| - name: Build changelog | ||
| if: steps.version.outputs.version != '' | ||
| # v6.3.0, pinned to a commit so an upstream tag move cannot change the | ||
| # code that runs here. Dependabot can bump this. | ||
| uses: mikepenz/release-changelog-builder-action@cb021f9b36a51a7c6f18e4679b6fa2cb77a1260c | ||
| with: | ||
| changelog_filename: .github/CHANGELOG.md | ||
| config_file: .github/changelog-ci-config.json | ||
| configuration: .github/changelog-config.json | ||
| fromTag: ${{ steps.previous.outputs.tag }} | ||
| toTag: ${{ github.event.pull_request.head.sha }} | ||
| outputFile: .github/changelog.generated.md | ||
| token: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
|
shuo-zhou marked this conversation as resolved.
|
||
| - name: Update changelog | ||
| if: steps.version.outputs.version != '' | ||
| env: | ||
| VERSION: ${{ steps.version.outputs.version }} | ||
| run: | | ||
| git show "${{ github.event.pull_request.base.sha }}:.github/scripts/update_changelog.py" > "$RUNNER_TEMP/update_changelog.py" | ||
| python3 "$RUNNER_TEMP/update_changelog.py" \ | ||
| "$VERSION" \ | ||
| .github/changelog.generated.md \ | ||
| .github/CHANGELOG.md | ||
|
|
||
| - name: Commit changelog | ||
| id: commit | ||
| if: steps.version.outputs.version != '' | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| VERSION: ${{ steps.version.outputs.version }} | ||
| run: | | ||
| if git diff --quiet -- .github/CHANGELOG.md; then | ||
| echo "changed=false" >> "$GITHUB_OUTPUT" | ||
| echo "::notice::CHANGELOG.md already lists this release, nothing to commit." | ||
| exit 0 | ||
| fi | ||
| git config user.name "github-actions[bot]" | ||
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | ||
| git add .github/CHANGELOG.md | ||
| git commit -m "Update CHANGELOG.md for version ${VERSION#v}" | ||
| git push | ||
| echo "changed=true" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Comment changelog on pull request | ||
| if: steps.version.outputs.version != '' && steps.commit.outputs.changed == 'true' | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| PR_NUMBER: ${{ github.event.pull_request.number }} | ||
| run: gh pr comment "$PR_NUMBER" --body-file .github/changelog.generated.md | ||
|
|
||
|
Comment on lines
+113
to
+119
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the push succeeds but commenting fails or the run is cancelled between them, the next run finds identical changelog content, sets |
||
| - name: Remove generated file | ||
| if: always() | ||
| run: rm -f .github/changelog.generated.md | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.