scripts/publish_gh_release_notes.py

Summary

Maintainability
A
2 hrs
Test Coverage
"""
Script used to publish GitHub release notes extracted from CHANGELOG.mdx.
This script is executed by GitHub after a new release was successfully built.

Uses the following environment variables:
* GITHUB_TAG: the name of the tag of the current commit.
* GITHUB_TOKEN: a personal access token with 'repo' permissions.

The script also requires ``pandoc`` to be previously installed in the system.
Requires Python3.6+.

Based on code from pytest.
https://github.com/pytest-dev/pytest/blob/master/scripts/publish_gh_release_notes.py
Copyright Holger Krekel and others, 2004-2019.

Distributed under the terms of the MIT license, pytest is free and open source software.
"""
import os
import re
import sys
from pathlib import Path
from typing import Text

# if this needs any more dependencies, they need to be installed on github deploy stage
import github3
from pep440_version_utils import Version


def create_github_release(slug: Text, token: Text, tag_name: Text, body: Text):
    """Create a github release."""

    github = github3.login(token=token)
    owner, repo = slug.split("/")
    repo = github.repository(owner, repo)
    return repo.create_release(tag_name=tag_name, body=body)


def parse_changelog(tag_name: Text) -> Text:
    """Read the changelog and extract the most recently release entry."""

    p = Path(__file__).parent.parent / "CHANGELOG.mdx"
    changelog_lines = p.read_text(encoding="UTF-8").splitlines()

    title_regex = re.compile(r"##\s*\[(\d+\.\d+\.\d+)(\S*)\]\s*-\s*\d{4}-\d{2}-\d{2}")
    consuming_version = False
    version_lines = []
    for line in changelog_lines:
        m = title_regex.match(line)
        if m:
            # found the version we want: start to consume lines
            # until we find the next version title
            if m.group(1) == tag_name:
                consuming_version = True
            # found a new version title while parsing the version we want: break out
            elif consuming_version:
                break
        if consuming_version:
            version_lines.append(line)

    if consuming_version:
        # drop the first lines (version headline, not needed for GH)
        return "\n".join(version_lines[2:]).strip()
    else:
        return None


def main():
    tag_name = os.environ.get("GITHUB_TAG")
    if not tag_name:
        print("environment variable GITHUB_TAG not set", file=sys.stderr)
        return 1

    token = os.environ.get("GITHUB_TOKEN")
    if not token:
        print("GITHUB_TOKEN not set", file=sys.stderr)
        return 1

    slug = os.environ.get("GITHUB_REPO_SLUG")
    if not slug:
        print("GITHUB_REPO_SLUG not set", file=sys.stderr)
        return 1

    version = Version(tag_name)
    if version.pre:
        md_body = "_Pre-release version_"
    else:
        md_body = parse_changelog(tag_name)

    if md_body is None:
        print("Failed to extract changelog entries for version from changelog.")
        return 2

    if not create_github_release(slug, token, tag_name, md_body):
        print("Could not publish release notes:", file=sys.stderr)
        print(md_body, file=sys.stderr)
        return 5

    print()
    print(f"Release notes for {tag_name} published successfully:")
    print(f"https://github.com/{slug}/releases/tag/{tag_name}")
    print()
    return 0


if __name__ == "__main__":
    sys.exit(main())