summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2023-01-16 13:48:00 -0500
committerNed Batchelder <ned@nedbatchelder.com>2023-01-16 13:52:37 -0500
commitbe1e1ae8ca619c92ed84ab0a01c95f71a48e5284 (patch)
treeefebd4e82093ef582f2601260494be38894b8922
parentec4701f937d2b91fcd61491812595939caf6bbe2 (diff)
downloadpython-coveragepy-git-be1e1ae8ca619c92ed84ab0a01c95f71a48e5284.tar.gz
build: use scriv to create GitHub releases
-rw-r--r--CHANGES.rst3
-rw-r--r--Makefile4
-rw-r--r--ci/ghrel_template.md.j25
-rw-r--r--ci/github_releases.py146
-rw-r--r--doc/changes.rst2
-rw-r--r--doc/requirements.in1
-rw-r--r--pyproject.toml8
7 files changed, 19 insertions, 150 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index dc05aa19..d845d884 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -22,6 +22,7 @@ Unreleased
- Typing: all product and test code has type annotations.
+.. scriv-start-here
.. _changes_7-0-5:
@@ -965,7 +966,7 @@ Version 5.3 — 2020-09-13
.. _issue 1011: https://github.com/nedbat/coveragepy/issues/1011
-.. endchangesinclude
+.. scriv-end-here
Older changes
-------------
diff --git a/Makefile b/Makefile
index 50d0a507..a3028b8b 100644
--- a/Makefile
+++ b/Makefile
@@ -253,8 +253,8 @@ relnotes_json: $(RELNOTES_JSON) ## Convert changelog to JSON for further parsin
$(RELNOTES_JSON): $(CHANGES_MD)
$(DOCBIN)/python ci/parse_relnotes.py tmp/rst_rst/changes.md $(RELNOTES_JSON)
-github_releases: $(RELNOTES_JSON) ## Update GitHub releases.
- $(DOCBIN)/python ci/github_releases.py $(RELNOTES_JSON) $(REPO_OWNER)
+github_releases: $(DOCBIN) ## Update GitHub releases.
+ $(DOCBIN)/python -m scriv github-release
comment_on_fixes: $(RELNOTES_JSON) ## Add a comment to issues that were fixed.
python ci/comment_on_fixes.py $(REPO_OWNER)
diff --git a/ci/ghrel_template.md.j2 b/ci/ghrel_template.md.j2
new file mode 100644
index 00000000..9d626bca
--- /dev/null
+++ b/ci/ghrel_template.md.j2
@@ -0,0 +1,5 @@
+<!-- for use with scriv to create GitHub releases. -->
+{{body}}
+
+:arrow_right:&nbsp; PyPI page: [coverage {{version}}](https://pypi.org/project/coverage/{{version}}).
+:arrow_right:&nbsp; To install: `python3 -m pip install coverage=={{version}}`
diff --git a/ci/github_releases.py b/ci/github_releases.py
deleted file mode 100644
index 5ba3d522..00000000
--- a/ci/github_releases.py
+++ /dev/null
@@ -1,146 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Upload release notes into GitHub releases."""
-
-import json
-import shlex
-import subprocess
-import sys
-
-import pkg_resources
-import requests
-
-
-RELEASES_URL = "https://api.github.com/repos/{repo}/releases"
-
-def run_command(cmd):
- """
- Run a command line (with no shell).
-
- Returns a tuple:
- bool: true if the command succeeded.
- str: the output of the command.
-
- """
- proc = subprocess.run(
- shlex.split(cmd),
- shell=False,
- check=False,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT,
- )
- output = proc.stdout.decode("utf-8")
- succeeded = proc.returncode == 0
- return succeeded, output
-
-def does_tag_exist(tag_name):
- """
- Does `tag_name` exist as a tag in git?
- """
- return run_command(f"git rev-parse --verify {tag_name}")[0]
-
-def check_ok(resp):
- """
- Check that the Requests response object was successful.
-
- Raise an exception if not.
- """
- if not resp:
- print(f"text: {resp.text!r}")
- resp.raise_for_status()
-
-def github_paginated(session, url):
- """
- Get all the results from a paginated GitHub url.
- """
- while True:
- resp = session.get(url)
- check_ok(resp)
- yield from resp.json()
- next_link = resp.links.get("next", None)
- if not next_link:
- break
- url = next_link["url"]
-
-def get_releases(session, repo):
- """
- Get all the releases from a name/project repo.
-
- Returns:
- A dict mapping tag names to release dictionaries.
- """
- url = RELEASES_URL.format(repo=repo)
- releases = { r['tag_name']: r for r in github_paginated(session, url) }
- return releases
-
-RELEASE_BODY_FMT = """\
-{relnote_text}
-
-:arrow_right:\xa0 PyPI page: [coverage {version}](https://pypi.org/project/coverage/{version}).
-:arrow_right:\xa0 To install: `python3 -m pip install coverage=={version}`
-"""
-
-def release_for_relnote(relnote):
- """
- Turn a release note dict into the data needed by GitHub for a release.
- """
- relnote_text = relnote["text"]
- tag = version = relnote["version"]
- body = RELEASE_BODY_FMT.format(relnote_text=relnote_text, version=version)
- return {
- "tag_name": tag,
- "name": version,
- "body": body,
- "draft": False,
- "prerelease": relnote["prerelease"],
- }
-
-def create_release(session, repo, release_data):
- """
- Create a new GitHub release.
- """
- print(f"Creating {release_data['name']}")
- resp = session.post(RELEASES_URL.format(repo=repo), json=release_data)
- check_ok(resp)
-
-def update_release(session, url, release_data):
- """
- Update an existing GitHub release.
- """
- print(f"Updating {release_data['name']}")
- resp = session.patch(url, json=release_data)
- check_ok(resp)
-
-def update_github_releases(json_filename, repo):
- """
- Read the json file, and create or update releases in GitHub.
- """
- gh_session = requests.Session()
- releases = get_releases(gh_session, repo)
- if 0: # if you need to delete all the releases!
- for release in releases.values():
- print(release["tag_name"])
- resp = gh_session.delete(release["url"])
- check_ok(resp)
- return
-
- with open(json_filename) as jf:
- relnotes = json.load(jf)
- relnotes.sort(key=lambda rel: pkg_resources.parse_version(rel["version"]))
- for relnote in relnotes:
- tag = relnote["version"]
- if not does_tag_exist(tag):
- continue
- release_data = release_for_relnote(relnote)
- exists = tag in releases
- if not exists:
- create_release(gh_session, repo, release_data)
- else:
- release = releases[tag]
- if release["body"] != release_data["body"]:
- url = release["url"]
- update_release(gh_session, url, release_data)
-
-if __name__ == "__main__":
- update_github_releases(*sys.argv[1:3])
diff --git a/doc/changes.rst b/doc/changes.rst
index da0f45ae..7f2df618 100644
--- a/doc/changes.rst
+++ b/doc/changes.rst
@@ -6,7 +6,7 @@
.. The recent changes from the top-level file:
.. include:: ../CHANGES.rst
- :end-before: endchangesinclude
+ :end-before: scriv-end-here
.. Older changes here:
diff --git a/doc/requirements.in b/doc/requirements.in
index 12aba6cc..42eca405 100644
--- a/doc/requirements.in
+++ b/doc/requirements.in
@@ -9,6 +9,7 @@
cogapp
#doc8
pyenchant
+scriv # for writing GitHub releases
sphinx
sphinx-autobuild
sphinx_rtd_theme
diff --git a/pyproject.toml b/pyproject.toml
index b21ba3b7..e11a5af1 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -27,3 +27,11 @@ exclude = """(?x)(
^coverage/fullcoverage/encodings\\.py$ # can't import things into it.
| ^tests/balance_xdist_plugin\\.py$ # not part of our test suite.
)"""
+
+[tool.scriv]
+# Changelog management: https://pypi.org/project/scriv/
+format = "rst"
+output_file = "CHANGES.rst"
+insert_marker = "scriv-start-here"
+end_marker = "scriv-end-here"
+ghrel_template = "file: ci/ghrel_template.md.j2"