summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Bruhin <me@the-compiler.org>2023-08-16 11:59:46 +0200
committerFlorian Bruhin <me@the-compiler.org>2023-08-17 12:35:53 +0200
commit950d06ad5b54abfc3f0d4dffb6b4b890b029e9e8 (patch)
treec2ecd154b61fbbd89a2210c5de25c357070efe45
parentd7b33759e537a759fb4cf90adfcc98f1a3ec7626 (diff)
downloadqutebrowser-950d06ad5b54abfc3f0d4dffb6b4b890b029e9e8.tar.gz
qutebrowser-950d06ad5b54abfc3f0d4dffb6b4b890b029e9e8.zip
ci: Initial automatic release support
See #3725
-rw-r--r--.github/workflows/release.yml137
-rwxr-xr-xscripts/dev/build_release.py29
-rw-r--r--scripts/dev/update_version.py64
-rw-r--r--tox.ini8
4 files changed, 214 insertions, 24 deletions
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 000000000..213078730
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -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 }}"
diff --git a/scripts/dev/build_release.py b/scripts/dev/build_release.py
index 55b3f5f1c..2038a7f67 100755
--- a/scripts/dev/build_release.py
+++ b/scripts/dev/build_release.py
@@ -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")
diff --git a/scripts/dev/update_version.py b/scripts/dev/update_version.py
index ec1550414..c67873496 100644
--- a/scripts/dev/update_version.py
+++ b/scripts/dev/update_version.py
@@ -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))
diff --git a/tox.ini b/tox.ini
index 060dccaaa..06c96cdf1 100644
--- a/tox.ini
+++ b/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 = *