252 lines
10 KiB
YAML
252 lines
10 KiB
YAML
name: Release
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
release_type:
|
|
description: 'Release type'
|
|
required: true
|
|
default: 'patch'
|
|
type: choice
|
|
options:
|
|
- 'patch'
|
|
- 'minor'
|
|
- 'major'
|
|
- 'reupload' # reupload last release
|
|
# FIXME do we want a possibility to do prereleases here?
|
|
python_version:
|
|
description: 'Python version'
|
|
required: true
|
|
default: '3.14'
|
|
type: choice
|
|
options:
|
|
- '3.9'
|
|
- '3.10'
|
|
- '3.11'
|
|
- '3.12'
|
|
- '3.13'
|
|
- '3.14'
|
|
jobs:
|
|
prepare:
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 5
|
|
outputs:
|
|
version: ${{ steps.bump.outputs.version }}
|
|
version_x: ${{ steps.bump.outputs.version_x }}
|
|
release_id: ${{ inputs.release_type == 'reupload' && steps.find-release.outputs.result || steps.create-release.outputs.id }}
|
|
permissions:
|
|
contents: write # To push release commit/tag
|
|
steps:
|
|
- name: Find release branch
|
|
uses: actions/github-script@v8
|
|
id: find-branch
|
|
with:
|
|
script: |
|
|
if (context.payload.inputs.release_type != 'patch') {
|
|
return 'main';
|
|
}
|
|
const branches = await github.paginate(github.rest.repos.listBranches, {
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
});
|
|
const branch_names = branches.map(branch => branch.name);
|
|
console.log(`branches: ${branch_names}`);
|
|
const release_branches = branch_names.filter(branch => branch.match(/^v\d+\.\d+\.x$/));
|
|
if (release_branches.length === 0) {
|
|
core.setFailed('No release branch found!');
|
|
return '';
|
|
}
|
|
console.log(`release_branches: ${release_branches}`);
|
|
// Get newest release branch (biggest version number)
|
|
const sorted = release_branches.sort((a, b) => a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' }));
|
|
console.log(`sorted: ${sorted}`);
|
|
return sorted.at(-1);
|
|
result-encoding: string
|
|
- uses: actions/checkout@v5
|
|
- name: Set up Python
|
|
uses: actions/setup-python@v6
|
|
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
|
|
uses: actions/checkout@v5
|
|
with:
|
|
ref: ${{ steps.find-branch.outputs.result }}
|
|
- name: Import GPG Key
|
|
run: |
|
|
gpg --import <<< "${{ secrets.QUTEBROWSER_BOT_GPGKEY }}"
|
|
- name: Bump version
|
|
id: bump
|
|
run: "tox -e update-version -- ${{ inputs.release_type }}"
|
|
- name: Check milestone
|
|
uses: actions/github-script@v8
|
|
with:
|
|
script: |
|
|
const milestones = await github.paginate(github.rest.issues.listMilestones, {
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
});
|
|
const names = milestones.map(milestone => milestone.title);
|
|
console.log(`milestones: ${names}`);
|
|
|
|
const milestone = milestones.find(milestone => milestone.title === "v${{ steps.bump.outputs.version }}");
|
|
if (milestone !== undefined) {
|
|
core.setFailed(`Found open milestone ${milestone.title} with ${milestone.open_issues} open and ${milestone.closed_issues} closed issues!`);
|
|
}
|
|
- name: Push release commit/tag
|
|
if: ${{ inputs.release_type != 'reupload' }}
|
|
run: |
|
|
git push origin ${{ steps.find-branch.outputs.result }}
|
|
git push origin v${{ steps.bump.outputs.version }}
|
|
- name: Cherry-pick release commit
|
|
if: ${{ inputs.release_type == 'patch' }}
|
|
run: |
|
|
git checkout main
|
|
git cherry-pick -x v${{ steps.bump.outputs.version }}
|
|
git push origin main
|
|
git checkout v${{ steps.bump.outputs.version_x }}
|
|
- name: Create release branch
|
|
if: ${{ inputs.release_type == 'minor' || inputs.release_type == 'major' }}
|
|
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
|
|
if: ${{ inputs.release_type != 'reupload' }}
|
|
id: create-release
|
|
uses: softprops/action-gh-release@v2
|
|
with:
|
|
tag_name: v${{ steps.bump.outputs.version }}
|
|
draft: true
|
|
body: "*Release artifacts for this release are currently being uploaded...*"
|
|
- name: Find GitHub draft release
|
|
if: ${{ inputs.release_type == 'reupload' }}
|
|
id: find-release
|
|
uses: actions/github-script@v8
|
|
with:
|
|
script: |
|
|
const releases = await github.paginate(github.rest.repos.listReleases, {
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
});
|
|
const names = releases.map(release => release.name);
|
|
console.log(`releases: ${names}`);
|
|
|
|
const release = releases.find(release => release.tag_name === "v${{ steps.bump.outputs.version }}");
|
|
if (release === undefined) {
|
|
core.setFailed(`No release found with tag v${{ steps.bump.outputs.version }}!`);
|
|
}
|
|
if (!release.draft) {
|
|
core.setFailed(`Release ${release.tag_name} is not a draft release!`);
|
|
}
|
|
return release.id;
|
|
result-encoding: string
|
|
release:
|
|
strategy:
|
|
matrix:
|
|
include:
|
|
- os: macos-14-large # Intel
|
|
- os: macos-14 # Apple Silicon
|
|
- os: windows-2022
|
|
- os: ubuntu-24.04
|
|
runs-on: "${{ matrix.os }}"
|
|
timeout-minutes: 45
|
|
needs: [prepare]
|
|
permissions:
|
|
contents: write # To upload release artifacts
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
with:
|
|
ref: v${{ inputs.release_type == 'reupload' && needs.prepare.outputs.version_x || needs.prepare.outputs.version }}
|
|
- name: Set up Python
|
|
uses: actions/setup-python@v6
|
|
with:
|
|
python-version: ${{ inputs.python_version }}
|
|
- name: Import GPG Key
|
|
if: ${{ startsWith(matrix.os, 'ubuntu-') }}
|
|
run: |
|
|
gpg --import <<< "${{ secrets.QUTEBROWSER_BOT_GPGKEY }}"
|
|
# Needed because of the following import chain:
|
|
# - scripts/dev/build_release.py
|
|
# - scripts/dev/update_3rdparty.py
|
|
# - scripts/dictcli.py
|
|
# - qutebrowser/browser/webengine/spell.py
|
|
# - utils.message -> utils.usertypes -> utils.qtutils -> qt.gui
|
|
# - PyQt6.QtGui
|
|
# Some additional packages are needed for a2x to build manpage
|
|
- name: Install apt dependencies
|
|
if: ${{ startsWith(matrix.os, 'ubuntu-') }}
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install --no-install-recommends libegl1 libxml2-utils docbook-xml xsltproc docbook-xsl
|
|
- name: Install dependencies
|
|
run: |
|
|
python -m pip install -U pip
|
|
python -m pip install -U -r misc/requirements/requirements-tox.txt
|
|
# FIXME consider switching to trusted publishers:
|
|
# https://blog.pypi.org/posts/2023-04-20-introducing-trusted-publishers/
|
|
- name: Build and upload release
|
|
run: "tox -e build-release -- --upload --no-confirm ${{ inputs.release_type == 'reupload' && '--reupload' || '' }}"
|
|
env:
|
|
TWINE_USERNAME: __token__
|
|
TWINE_PASSWORD: ${{ secrets.QUTEBROWSER_BOT_PYPI_TOKEN }}
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
finalize:
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 5
|
|
needs: [prepare, release]
|
|
permissions:
|
|
contents: write # To change release
|
|
steps:
|
|
- name: Publish final release
|
|
uses: actions/github-script@v8
|
|
with:
|
|
script: |
|
|
await github.rest.repos.updateRelease({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
release_id: "${{ needs.prepare.outputs.release_id }}",
|
|
draft: false,
|
|
body: "Check the [changelog](https://github.com/qutebrowser/qutebrowser/blob/main/doc/changelog.asciidoc) for changes in this release.",
|
|
})
|
|
irc:
|
|
timeout-minutes: 2
|
|
continue-on-error: true
|
|
runs-on: ubuntu-24.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 main channel IRC notification
|
|
uses: Gottox/irc-message-action@v2
|
|
if: "${{ needs.finalize.result == 'success' && github.repository == 'qutebrowser/qutebrowser' }}"
|
|
with:
|
|
server: irc.libera.chat
|
|
channel: '#qutebrowser'
|
|
nickname: qutebrowser-bot
|
|
message: "qutebrowser v${{ needs.prepare.outputs.version }} has been released! https://github.com/${{ github.repository }}/releases/tag/v${{ needs.prepare.outputs.version }}"
|
|
- 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 }}"
|