Use manual trigger to prepare release PRs (#8150)

Co-authored-by: Ran Benita <ran@unusedvar.com>
This commit is contained in:
Bruno Oliveira 2020-12-16 07:50:02 -03:00 committed by GitHub
parent 950fbbc326
commit f1a1de2257
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 200 additions and 0 deletions

View File

@ -0,0 +1,42 @@
name: prepare release pr
on:
workflow_dispatch:
inputs:
branch:
description: 'Branch to base the release from'
required: true
default: ''
major:
description: 'Major release? (yes/no)'
required: true
default: 'no'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: "3.8"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install --upgrade setuptools tox
- name: Prepare release PR (minor/patch release)
if: github.event.inputs.branch.major == 'no'
run: |
tox -e prepare-release-pr -- ${{ github.event.inputs.branch }} ${{ secrets.chatops }}
- name: Prepare release PR (major release)
if: github.event.inputs.branch.major == 'yes'
run: |
tox -e prepare-release-pr -- ${{ github.event.inputs.branch }} ${{ secrets.chatops }} --major

View File

@ -0,0 +1,151 @@
"""
This script is part of the pytest release process which is triggered manually in the Actions
tab of the repository.
The user will need to enter the base branch to start the release from (for example
``6.1.x`` or ``master``) and if it should be a major release.
The appropriate version will be obtained based on the given branch automatically.
After that, it will create a release using the `release` tox environment, and push a new PR.
**Secret**: currently the secret is defined in the @pytestbot account,
which the core maintainers have access to. There we created a new secret named `chatops`
with write access to the repository.
"""
import argparse
import re
from pathlib import Path
from subprocess import check_call
from subprocess import check_output
from subprocess import run
from colorama import Fore
from colorama import init
from github3.repos import Repository
class InvalidFeatureRelease(Exception):
pass
SLUG = "pytest-dev/pytest"
PR_BODY = """\
Created automatically from manual trigger.
Once all builds pass and it has been **approved** by one or more maintainers, the build
can be released by pushing a tag `{version}` to this repository.
"""
def login(token: str) -> Repository:
import github3
github = github3.login(token=token)
owner, repo = SLUG.split("/")
return github.repository(owner, repo)
def prepare_release_pr(base_branch: str, is_major: bool, token: str) -> None:
print()
print(f"Processing release for branch {Fore.CYAN}{base_branch}")
check_call(["git", "checkout", f"origin/{base_branch}"])
try:
version = find_next_version(base_branch, is_major)
except InvalidFeatureRelease as e:
print(f"{Fore.RED}{e}")
raise SystemExit(1)
print(f"Version: {Fore.CYAN}{version}")
release_branch = f"release-{version}"
run(
["git", "config", "user.name", "pytest bot"],
text=True,
check=True,
capture_output=True,
)
run(
["git", "config", "user.email", "pytestbot@gmail.com"],
text=True,
check=True,
capture_output=True,
)
run(
["git", "checkout", "-b", release_branch, f"origin/{base_branch}"],
text=True,
check=True,
capture_output=True,
)
print(f"Branch {Fore.CYAN}{release_branch}{Fore.RESET} created.")
# important to use tox here because we have changed branches, so dependencies
# might have changed as well
cmdline = ["tox", "-e", "release", "--", version, "--skip-check-links"]
print("Running", " ".join(cmdline))
run(
cmdline, text=True, check=True, capture_output=True,
)
oauth_url = f"https://{token}:x-oauth-basic@github.com/{SLUG}.git"
run(
["git", "push", oauth_url, f"HEAD:{release_branch}", "--force"],
text=True,
check=True,
capture_output=True,
)
print(f"Branch {Fore.CYAN}{release_branch}{Fore.RESET} pushed.")
body = PR_BODY.format(version=version)
repo = login(token)
pr = repo.create_pull(
f"Prepare release {version}", base=base_branch, head=release_branch, body=body,
)
print(f"Pull request {Fore.CYAN}{pr.url}{Fore.RESET} created.")
def find_next_version(base_branch: str, is_major: bool) -> str:
output = check_output(["git", "tag"], encoding="UTF-8")
valid_versions = []
for v in output.splitlines():
m = re.match(r"\d.\d.\d+$", v.strip())
if m:
valid_versions.append(tuple(int(x) for x in v.split(".")))
valid_versions.sort()
last_version = valid_versions[-1]
changelog = Path("changelog")
features = list(changelog.glob("*.feature.rst"))
breaking = list(changelog.glob("*.breaking.rst"))
is_feature_release = features or breaking
if is_major:
return f"{last_version[0]+1}.0.0"
elif is_feature_release:
return f"{last_version[0]}.{last_version[1] + 1}.0"
else:
return f"{last_version[0]}.{last_version[1]}.{last_version[2] + 1}"
def main() -> None:
init(autoreset=True)
parser = argparse.ArgumentParser()
parser.add_argument("base_branch")
parser.add_argument("token")
parser.add_argument("--major", action="store_true", default=False)
options = parser.parse_args()
prepare_release_pr(
base_branch=options.base_branch, is_major=options.major, token=options.token
)
if __name__ == "__main__":
main()

View File

@ -157,6 +157,13 @@ passenv = {[testenv:release]passenv}
deps = {[testenv:release]deps}
commands = python scripts/release-on-comment.py {posargs}
[testenv:prepare-release-pr]
decription = prepare a release PR from a manual trigger in GitHub actions
usedevelop = {[testenv:release]usedevelop}
passenv = {[testenv:release]passenv}
deps = {[testenv:release]deps}
commands = python scripts/prepare-release-pr.py {posargs}
[testenv:publish-gh-release-notes]
description = create GitHub release after deployment
basepython = python3