From 183ca2088a38f11e811697e733be5037bcba7f7e Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Thu, 18 Nov 2021 09:36:42 +0000 Subject: [PATCH] Switch template to calendar versioning & automate releases (#3415) --- .github/changelog-template.md | 1 - .github/release-drafter.yml | 29 ------- .github/workflows/draft-release.yml | 14 ---- .github/workflows/update-changelog.yml | 15 ++-- requirements.txt | 1 + scripts/update_changelog.py | 108 ++++++++++++++++++++----- setup.py | 14 +--- 7 files changed, 100 insertions(+), 82 deletions(-) delete mode 100644 .github/release-drafter.yml delete mode 100644 .github/workflows/draft-release.yml diff --git a/.github/changelog-template.md b/.github/changelog-template.md index 2c0c512f..50aab38e 100644 --- a/.github/changelog-template.md +++ b/.github/changelog-template.md @@ -1,4 +1,3 @@ -## [{{merge_date.strftime('%Y-%m-%d')}}] {%- for change_type, pulls in grouped_pulls.items() %} {%- if pulls %} ### {{ change_type }} diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml deleted file mode 100644 index 734a541a..00000000 --- a/.github/release-drafter.yml +++ /dev/null @@ -1,29 +0,0 @@ -categories: - - title: 'Breaking Changes' - labels: - - 'breaking' - - title: 'Major Changes' - labels: - - 'major' - - title: 'Minor Changes' - labels: - - 'enhancement' - - title: 'Bugfixes' - labels: - - 'bug' - - title: 'Removals' - labels: - - 'removed' - - title: 'Documentation updates' - labels: - - 'docs' - -exclude-labels: - - 'skip-changelog' - - 'update' - - 'project infrastructure' - -template: | - ## Changes - - $CHANGES diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml deleted file mode 100644 index 6c2d6620..00000000 --- a/.github/workflows/draft-release.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: Release Drafter - -on: - push: - branches: - - master - -jobs: - release_notes: - runs-on: ubuntu-latest - steps: - - uses: release-drafter/release-drafter@v5 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/update-changelog.yml b/.github/workflows/update-changelog.yml index 9b611142..5fa53cea 100644 --- a/.github/workflows/update-changelog.yml +++ b/.github/workflows/update-changelog.yml @@ -8,7 +8,10 @@ on: workflow_dispatch: jobs: - build: + release: + # Disables this workflow from running in a repository that is not part of the indicated organization/user + if: github.repository_owner == 'cookiecutter' + runs-on: ubuntu-latest steps: @@ -22,13 +25,11 @@ jobs: run: | python -m pip install --upgrade pip pip install -r requirements.txt + - name: Set git details + run: | + git config --global user.name "github-actions" + git config --global user.email "action@github.com" - name: Update list run: python scripts/update_changelog.py env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Commit changes - uses: stefanzweifel/git-auto-commit-action@v4.12.0 - with: - commit_message: Update Changelog - file_pattern: CHANGELOG.md diff --git a/requirements.txt b/requirements.txt index 2ad130df..4e7e9bfd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,5 +21,6 @@ pyyaml==6.0 # Scripting # ------------------------------------------------------------------------------ PyGithub==1.55 +gitpython==3.1.24 jinja2==3.0.3 requests==2.26.0 diff --git a/scripts/update_changelog.py b/scripts/update_changelog.py index 5ec5d25a..1ec9bde9 100644 --- a/scripts/update_changelog.py +++ b/scripts/update_changelog.py @@ -1,26 +1,31 @@ import datetime as dt import os +import re from pathlib import Path from typing import Iterable -from github import Github +import git import github.PullRequest +import github.Repository +from github import Github from jinja2 import Template CURRENT_FILE = Path(__file__) ROOT = CURRENT_FILE.parents[1] -GITHUB_TOKEN = os.getenv("GITHUB_TOKEN", None) -GITHUB_REPO = os.getenv("GITHUB_REPOSITORY", None) - -# Generate changelog for PRs merged yesterday -MERGED_DATE = dt.date.today() - dt.timedelta(days=1) +GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") +GITHUB_REPO = os.getenv("GITHUB_REPOSITORY") +GIT_BRANCH = os.getenv("GITHUB_REF_NAME") def main() -> None: """ Script entry point. """ - merged_pulls = list(iter_pulls()) + # Generate changelog for PRs merged yesterday + merged_date = dt.date.today() - dt.timedelta(days=1) + repo = Github(login_or_token=GITHUB_TOKEN).get_repo(GITHUB_REPO) + merged_pulls = list(iter_pulls(repo, merged_date)) + print(f"Merged pull requests: {merged_pulls}") if not merged_pulls: print("Nothing was merged, existing.") return @@ -29,26 +34,44 @@ def main() -> None: grouped_pulls = group_pulls_by_change_type(merged_pulls) # Generate portion of markdown - rendered_content = generate_md(grouped_pulls) + release_changes_summary = generate_md(grouped_pulls) + print(f"Summary of changes: {release_changes_summary}") # Update CHANGELOG.md file - file_path = ROOT / "CHANGELOG.md" - old_content = file_path.read_text() - updated_content = old_content.replace( - "", - f"\n\n{rendered_content}", + release = f"{merged_date:%Y.%m.%d}" + changelog_path = ROOT / "CHANGELOG.md" + write_changelog(changelog_path, release, release_changes_summary) + print(f"Wrote {changelog_path}") + + # Update version + setup_py_path = ROOT / "setup.py" + update_version(setup_py_path, release) + print(f"Updated version in {setup_py_path}") + + # Commit changes, create tag and push + update_git_repo([changelog_path, setup_py_path], release) + + # Create GitHub release + github_release = repo.create_git_release( + tag=release, + name=release, + message=release_changes_summary, ) - file_path.write_text(updated_content) + print(f"Created release on GitHub {github_release}") -def iter_pulls() -> Iterable[github.PullRequest.PullRequest]: +def iter_pulls( + repo: github.Repository.Repository, + merged_date: dt.date, +) -> Iterable[github.PullRequest.PullRequest]: """Fetch merged pull requests at the date we're interested in.""" - repo = Github(login_or_token=GITHUB_TOKEN).get_repo(GITHUB_REPO) recent_pulls = repo.get_pulls( - state="closed", sort="updated", direction="desc" + state="closed", + sort="updated", + direction="desc", ).get_page(0) for pull in recent_pulls: - if pull.merged and pull.merged_at.date() == MERGED_DATE: + if pull.merged and pull.merged_at.date() == merged_date: yield pull @@ -77,7 +100,50 @@ def generate_md(grouped_pulls: dict[str, list[github.PullRequest.PullRequest]]) """Generate markdown file from Jinja template.""" changelog_template = ROOT / ".github" / "changelog-template.md" template = Template(changelog_template.read_text(), autoescape=True) - return template.render(merge_date=MERGED_DATE, grouped_pulls=grouped_pulls) + return template.render(grouped_pulls=grouped_pulls) + + +def write_changelog(file_path: Path, release: str, content: str) -> None: + """Write Release details to the changelog file.""" + content = f"## {release}\n{content}" + old_content = file_path.read_text() + updated_content = old_content.replace( + "", + f"\n\n{content}", + ) + file_path.write_text(updated_content) + + +def update_version(file_path: Path, release: str) -> None: + """Update template version in setup.py.""" + old_content = file_path.read_text() + updated_content = re.sub( + r'\nversion = "\d+\.\d+\.\d+"\n', + f'\nversion = "{release}"\n', + old_content, + ) + file_path.write_text(updated_content) + + +def update_git_repo(paths: list[Path], release: str) -> None: + """Commit, tag changes in git repo and push to origin.""" + repo = git.Repo(ROOT) + for path in paths: + repo.git.add(path) + message = f"Release {release}" + + user = repo.git.config("--get", "user.name") + email = repo.git.config("--get", "user.email") + + repo.git.commit( + m=message, + author=f"{user} <{email}>", + ) + repo.git.tag("-a", release, m=message) + server = f"https://{GITHUB_TOKEN}@github.com/{GITHUB_REPO}.git" + print(f"Pushing changes to {GIT_BRANCH} branch of {GITHUB_REPO}") + repo.git.push(server, GIT_BRANCH) + repo.git.push("--tags", server, GIT_BRANCH) if __name__ == "__main__": @@ -85,4 +151,8 @@ if __name__ == "__main__": raise RuntimeError( "No github repo, please set the environment variable GITHUB_REPOSITORY" ) + if GIT_BRANCH is None: + raise RuntimeError( + "No git branch set, please set the GITHUB_REF_NAME environment variable" + ) main() diff --git a/setup.py b/setup.py index b1be8f7a..9b85150f 100644 --- a/setup.py +++ b/setup.py @@ -1,21 +1,11 @@ #!/usr/bin/env python - -import os -import sys - try: from setuptools import setup except ImportError: from distutils.core import setup -# Our version ALWAYS matches the version of Django we support -# If Django has a new release, we branch, tag, then update this setting after the tag. -version = "3.1.13-01" - -if sys.argv[-1] == "tag": - os.system(f'git tag -a {version} -m "version {version}"') - os.system("git push --tags") - sys.exit() +# We use calendar versioning +version = "2021.11.17" with open("README.rst") as readme_file: long_description = readme_file.read()