parent
d7b33759e5
commit
950d06ad5b
|
|
@ -0,0 +1,137 @@
|
|||
name: Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release_type:
|
||||
description: 'Release type'
|
||||
required: true
|
||||
default: 'patch'
|
||||
type: choice
|
||||
options:
|
||||
- 'patch'
|
||||
- 'minor'
|
||||
- 'major'
|
||||
# FIXME do we want a possibility to do prereleases here?
|
||||
python_version:
|
||||
description: 'Python version'
|
||||
required: true
|
||||
default: '3.11'
|
||||
type: choice
|
||||
options:
|
||||
- '3.8'
|
||||
- '3.9'
|
||||
- '3.10'
|
||||
- '3.11'
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
version: ${{ steps.bump.outputs.version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
# Doesn't really matter what we prepare the release with, but let's
|
||||
# use the same version for consistency.
|
||||
python-version: ${{ github.event.inputs.python_version }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install -U pip
|
||||
python -m pip install -U -r misc/requirements/requirements-tox.txt
|
||||
- name: Configure git
|
||||
run: |
|
||||
git config --global user.name "qutebrowser bot"
|
||||
git config --global user.email "bot@qutebrowser.org"
|
||||
- name: Switch to release branch
|
||||
if: "${{ github.event.inputs.release_type }} == 'patch'"
|
||||
run: |
|
||||
git checkout "$(git branch --format='%(refname:short)' --list 'v*.*.x' | sort -V | tail -n1)"
|
||||
# FIXME set up GPG for signed tag
|
||||
- name: Bump version
|
||||
id: bump
|
||||
run: "tox -e update-version -- ${{ github.event.inputs.release_type }}"
|
||||
- name: Push release commit/tag
|
||||
run: |
|
||||
git push origin main
|
||||
git push origin v${{ steps.bump.outputs.version }}
|
||||
- name: Cherry-pick release commit
|
||||
if: "${{ github.event.inputs.release_type }} == 'patch'"
|
||||
run: |
|
||||
git checkout main
|
||||
git cherry-pick v${{ steps.bump.outputs.version }}
|
||||
git push origin main
|
||||
git checkout v${{ steps.bump.outputs.version_x }}
|
||||
- name: Create release branch
|
||||
if: "${{ github.event.inputs.release_type }} != 'patch'"
|
||||
run: |
|
||||
git checkout -b v${{ steps.bump.outputs.version_x }}
|
||||
git push --set-upstream origin v${{ steps.bump.outputs.version_x }}
|
||||
- name: Create GitHub draft release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: v${{ steps.bump.outputs.version }}
|
||||
draft: true
|
||||
body: "*Release artifacts for this release are currently being uploaded...*"
|
||||
release:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: macos-11
|
||||
- os: windows-2019
|
||||
- os: ubuntu-20.04
|
||||
runs-on: "${{ matrix.os }}"
|
||||
timeout-minutes: 45
|
||||
needs: [prepare]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ github.event.inputs.python_version }}
|
||||
# FIXME set up GPG for signed releases (at least on Ubuntu)
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install -U pip
|
||||
python -m pip install -U -r misc/requirements/requirements-tox.txt
|
||||
- name: Build and upload release
|
||||
run: "tox -e build-release -- --upload --no-confirm --experimental --gh-token ${{ secrets.GITHUB_TOKEN }}"
|
||||
finalize:
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 5
|
||||
needs: [prepare, release]
|
||||
steps:
|
||||
- name: Publish final release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: v${{ needs.prepare.outputs.version }}
|
||||
draft: false
|
||||
# FIXME automatically cut relevant changes from changelog and add them here?
|
||||
body: |
|
||||
Check the [changelog](https://github.com/qutebrowser/qutebrowser/blob/master/doc/changelog.asciidoc) for changes in this release.
|
||||
irc:
|
||||
timeout-minutes: 2
|
||||
continue-on-error: true
|
||||
runs-on: ubuntu-20.04
|
||||
needs: [prepare, release, finalize]
|
||||
if: "${{ always() }}"
|
||||
steps:
|
||||
- name: Send success IRC notification
|
||||
uses: Gottox/irc-message-action@v2
|
||||
if: "${{ needs.finalize.result == 'success' }}"
|
||||
with:
|
||||
server: irc.libera.chat
|
||||
channel: '#qutebrowser-bots'
|
||||
nickname: qutebrowser-bot
|
||||
message: "[${{ github.workflow }}] \u00033Success:\u0003 ${{ github.ref }} https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} (@${{ github.actor }})"
|
||||
- name: Send non-success IRC notification
|
||||
uses: Gottox/irc-message-action@v2
|
||||
if: "${{ needs.finalize.result != 'success' }}"
|
||||
with:
|
||||
server: irc.libera.chat
|
||||
channel: '#qutebrowser-bots'
|
||||
nickname: qutebrowser-bot
|
||||
message: "[${{ github.workflow }}] \u00034FAIL:\u0003 ${{ github.ref }} https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} (@${{ github.actor }})\n
|
||||
prepare: ${{ needs.prepare.result }}, release: ${{ needs.release.result}}, finalize: ${{ needs.finalize.result }}"
|
||||
|
|
@ -544,13 +544,19 @@ def read_github_token(
|
|||
return token
|
||||
|
||||
|
||||
def github_upload(artifacts: List[Artifact], tag: str, gh_token: str) -> None:
|
||||
def github_upload(
|
||||
artifacts: List[Artifact],
|
||||
tag: str,
|
||||
gh_token: str,
|
||||
experimental: bool,
|
||||
) -> None:
|
||||
"""Upload the given artifacts to GitHub.
|
||||
|
||||
Args:
|
||||
artifacts: A list of Artifacts to upload.
|
||||
tag: The name of the release tag
|
||||
gh_token: The GitHub token to use
|
||||
experimental: Upload to the experiments repo
|
||||
"""
|
||||
# pylint: disable=broad-exception-raised
|
||||
import github3
|
||||
|
|
@ -558,7 +564,11 @@ def github_upload(artifacts: List[Artifact], tag: str, gh_token: str) -> None:
|
|||
utils.print_title("Uploading to github...")
|
||||
|
||||
gh = github3.login(token=gh_token)
|
||||
repo = gh.repository('qutebrowser', 'qutebrowser')
|
||||
|
||||
if experimental:
|
||||
repo = gh.repository('qutebrowser', 'experiments')
|
||||
else:
|
||||
repo = gh.repository('qutebrowser', 'qutebrowser')
|
||||
|
||||
release = None # to satisfy pylint
|
||||
for release in repo.releases():
|
||||
|
|
@ -602,10 +612,13 @@ def github_upload(artifacts: List[Artifact], tag: str, gh_token: str) -> None:
|
|||
break
|
||||
|
||||
|
||||
def pypi_upload(artifacts: List[Artifact]) -> None:
|
||||
def pypi_upload(artifacts: List[Artifact], experimental: bool) -> None:
|
||||
"""Upload the given artifacts to PyPI using twine."""
|
||||
utils.print_title("Uploading to PyPI...")
|
||||
run_twine('upload', artifacts)
|
||||
if experimental:
|
||||
run_twine('upload', artifacts, "-r", "testpypi")
|
||||
else:
|
||||
run_twine('upload', artifacts)
|
||||
|
||||
|
||||
def twine_check(artifacts: List[Artifact]) -> None:
|
||||
|
|
@ -635,6 +648,8 @@ def main() -> None:
|
|||
help="Build a debug build.")
|
||||
parser.add_argument('--qt5', action='store_true', required=False,
|
||||
help="Build against PyQt5")
|
||||
parser.add_argument('--experimental', action='store_true', required=False,
|
||||
help="Upload to experiments repo and test PyPI")
|
||||
args = parser.parse_args()
|
||||
utils.change_cwd()
|
||||
|
||||
|
|
@ -647,6 +662,7 @@ def main() -> None:
|
|||
gh_token = read_github_token(args.gh_token)
|
||||
else:
|
||||
gh_token = read_github_token(args.gh_token, optional=True)
|
||||
assert not args.experimental # makes no sense without upload
|
||||
|
||||
if not misc_checks.check_git():
|
||||
utils.print_error("Refusing to do a release with a dirty git tree")
|
||||
|
|
@ -685,9 +701,10 @@ def main() -> None:
|
|||
input()
|
||||
|
||||
assert gh_token is not None
|
||||
github_upload(artifacts, version_tag, gh_token=gh_token)
|
||||
github_upload(
|
||||
artifacts, version_tag, gh_token=gh_token, experimental=args.experimental)
|
||||
if upload_to_pypi:
|
||||
pypi_upload(artifacts)
|
||||
pypi_upload(artifacts, experimental=args.experimental)
|
||||
else:
|
||||
print()
|
||||
utils.print_title("Artifacts")
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
"""Update version numbers using bump2version."""
|
||||
|
||||
import re
|
||||
import sys
|
||||
import argparse
|
||||
import os.path
|
||||
|
|
@ -19,6 +20,24 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir,
|
|||
from scripts import utils
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
"""Base class for exceptions in this module."""
|
||||
|
||||
|
||||
def verify_branch(version_leap):
|
||||
"""Check that we're on the correct git branch."""
|
||||
proc = subprocess.run(
|
||||
['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
|
||||
check=True, capture_output=True, text=True)
|
||||
branch = proc.stdout.strip()
|
||||
|
||||
if (
|
||||
version_leap == 'patch' and not re.fullmatch(r'v\d+\.\d+\.\*', branch) or
|
||||
version_leap != 'patch' and branch != 'main'
|
||||
):
|
||||
raise Error(f"Invalid branch for {version_leap} release: {branch}")
|
||||
|
||||
|
||||
def bump_version(version_leap="patch"):
|
||||
"""Update qutebrowser release version.
|
||||
|
||||
|
|
@ -46,6 +65,7 @@ if __name__ == "__main__":
|
|||
utils.change_cwd()
|
||||
|
||||
if not args.commands:
|
||||
verify_branch(args.bump)
|
||||
bump_version(args.bump)
|
||||
show_commit()
|
||||
|
||||
|
|
@ -54,22 +74,30 @@ if __name__ == "__main__":
|
|||
x_version = '.'.join([str(p) for p in qutebrowser.__version_info__[:-1]] +
|
||||
['x'])
|
||||
|
||||
print("Run the following commands to create a new release:")
|
||||
print("* git push origin; git push origin v{v}".format(v=version))
|
||||
if args.bump == 'patch':
|
||||
print("* git checkout main && git cherry-pick v{v} && "
|
||||
"git push origin".format(v=version))
|
||||
if utils.ON_CI:
|
||||
output_file = os.environ["GITHUB_OUTPUT"]
|
||||
with open(output_file, "w", encoding="ascii") as f:
|
||||
f.write(f"version={version}\n")
|
||||
f.write(f"x_version={x_version}\n")
|
||||
|
||||
print(f"Outputs for {version} written to GitHub Actions output file")
|
||||
else:
|
||||
print("* git branch v{x} v{v} && git push --set-upstream origin v{x}"
|
||||
.format(v=version, x=x_version))
|
||||
print("* Create new release via GitHub (required to upload release "
|
||||
"artifacts)")
|
||||
print("* Linux: git fetch && git checkout v{v} && "
|
||||
"tox -e build-release -- --upload"
|
||||
.format(v=version))
|
||||
print("* Windows: git fetch; git checkout v{v}; "
|
||||
"py -3.9 -m tox -e build-release -- --upload"
|
||||
.format(v=version))
|
||||
print("* macOS: git fetch && git checkout v{v} && "
|
||||
"tox -e build-release -- --upload"
|
||||
.format(v=version))
|
||||
print("Run the following commands to create a new release:")
|
||||
print("* git push origin; git push origin v{v}".format(v=version))
|
||||
if args.bump == 'patch':
|
||||
print("* git checkout main && git cherry-pick v{v} && "
|
||||
"git push origin".format(v=version))
|
||||
else:
|
||||
print("* git branch v{x} v{v} && git push --set-upstream origin v{x}"
|
||||
.format(v=version, x=x_version))
|
||||
print("* Create new release via GitHub (required to upload release "
|
||||
"artifacts)")
|
||||
print("* Linux: git fetch && git checkout v{v} && "
|
||||
"tox -e build-release -- --upload"
|
||||
.format(v=version))
|
||||
print("* Windows: git fetch; git checkout v{v}; "
|
||||
"py -3.9 -m tox -e build-release -- --upload"
|
||||
.format(v=version))
|
||||
print("* macOS: git fetch && git checkout v{v} && "
|
||||
"tox -e build-release -- --upload"
|
||||
.format(v=version))
|
||||
|
|
|
|||
8
tox.ini
8
tox.ini
|
|
@ -267,6 +267,14 @@ deps =
|
|||
commands =
|
||||
{envpython} -m sphinx -jauto -W --color {posargs} {toxinidir}/doc/extapi/ {toxinidir}/doc/extapi/_build/
|
||||
|
||||
[testenv:update-version]
|
||||
basepython = {env:PYTHON:python3}
|
||||
passenv =
|
||||
GITHUB_OUTPUT
|
||||
CI
|
||||
deps = -r{toxinidir}/misc/requirements/requirements-dev.txt
|
||||
commands = {envpython} scripts/dev/update_version.py {posargs}
|
||||
|
||||
[testenv:build-release{,-qt5}]
|
||||
basepython = {env:PYTHON:python3}
|
||||
passenv = *
|
||||
|
|
|
|||
Loading…
Reference in New Issue