Skip to content

Commit

Permalink
Add a GitHub workflow for finalizing a release
Browse files Browse the repository at this point in the history
This automates the second half of the release process (the first being
automated by `prepare-release.yml`). After this workflow completes, the only
action that should remain for the releaser to do is to merge the
`dev-release-*` pull request. We can't do that as part of this workflow,
because CI has to finish first, and it seems pointless to create another
workflow just to merge 1 PR.

Apply some of the aspects of this pipeline to `prepare-release.yml` as well:

* Make the release notes extraction process more sophisticated to work
  around GitHub's frustrating handling of line breaks in PR and release
  descriptions.

* Use GitHub app credentials in order to be able to trigger other pipelines.
  • Loading branch information
SpecLad committed Oct 13, 2023
1 parent 4d9af86 commit 55f23cc
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 6 deletions.
109 changes: 109 additions & 0 deletions .github/workflows/finalize-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
name: Finalize release
on:
workflow_dispatch:
jobs:
main:
runs-on: ubuntu-latest
steps:
- name: Discover pending release
id: discover
env:
GH_TOKEN: "${{ github.token }}"
run: |
gh --repo="${{ github.repository }}" \
pr list --base master --state open --json number,headRefName \
--jq 'map(select(.headRefName | startswith("release-")))' \
> /tmp/release-prs.json
if jq -e 'length < 1' /tmp/release-prs.json > /dev/null; then
echo "No open release pull requests found."
exit 1
elif jq -e 'length > 1' /tmp/release-prs.json > /dev/null; then
echo "Multiple open release pull requests found:"
jq -r '.[] | "https://github.com/${{ github.repository }}/pull/\(.number)"' /tmp/release-prs.json
exit 1
fi
jq -r '.[] | "prNumber=\(.number)", "version=\(.headRefName | ltrimstr("release-"))"' \
/tmp/release-prs.json >> "$GITHUB_OUTPUT"
# When you use the default github.token to make changes in the repository,
# it does not trigger further GitHub pipelines. We want to trigger CI for
# the pull request and artifact building for the release, so we have to use
# an app token.
- name: Generate authentication token
id: gen-token
uses: actions/create-github-app-token@v1
with:
app-id: "${{ secrets.CVAT_BOT_APP_ID }}"
private-key: "${{ secrets.CVAT_BOT_PRIVATE_KEY }}"

- name: Install dependencies
run:
sudo apt-get install -y pandoc

- uses: actions/checkout@v4
with:
ref: "release-${{ steps.discover.outputs.version }}"

- name: Verify that the release is new
env:
NEW_VERSION: "${{ steps.discover.outputs.version }}"
run: |
if git ls-remote --exit-code origin "refs/tags/v$NEW_VERSION" > /dev/null; then
echo "Release v$NEW_VERSION already exists"
exit 1
fi
# Do post-release tasks before publishing the release. If anything goes wrong,
# the dev-release-* branch can be deleted, and the whole process restarted again;
# whereas we can't unmerge the release PR.

- name: Create post-release branch
run:
git checkout -b "dev-release-${{ steps.discover.outputs.version }}"

- name: Bump version
run:
./dev/update_version.py --minor

- name: Commit post-release changes
run: |
git -c user.name='cvat-bot[bot]' -c user.email='147643061+cvat-bot[bot]@users.noreply.github.com' \
commit -a -m "Update ${{ github.ref_name }} after v${{ steps.discover.outputs.version }}"
- name: Push post-release branch
run:
git push -u origin "dev-release-${{ steps.discover.outputs.version }}"

- name: Create post-release pull request
env:
GH_TOKEN: "${{ steps.gen-token.outputs.token }}"
run: |
gh pr create \
--base="${{ github.ref_name }}" \
--title="Update ${{ github.ref_name }} after v${{ steps.discover.outputs.version }}" \
--body=""
# Now publish the release.

- name: Merge release pull request
env:
GH_TOKEN: "${{ steps.gen-token.outputs.token }}"
run:
gh pr merge --merge "${{ steps.discover.outputs.prNumber }}" --delete-branch

- name: Create release
env:
GH_TOKEN: "${{ steps.gen-token.outputs.token }}"
NEW_VERSION: "${{ steps.discover.outputs.version }}"
run: |
# We could grab the release notes from the PR description, but it could
# be outdated if any changes were made on the release branch. So instead,
# just re-extract them from the changelog again.
./dev/gh_release_notes.sh \
| gh release create "v$NEW_VERSION" \
--target=master \
--title="v$NEW_VERSION" \
--notes-file=-
27 changes: 21 additions & 6 deletions .github/workflows/prepare-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ jobs:
exit 1
fi
# When you use the default github.token to make changes in the repository,
# it does not trigger further GitHub pipelines. We want to trigger CI for
# the pull request, so we have to use an app token.
- name: Generate authentication token
id: gen-token
uses: actions/create-github-app-token@v1
with:
app-id: "${{ secrets.CVAT_BOT_APP_ID }}"
private-key: "${{ secrets.CVAT_BOT_PRIVATE_KEY }}"

- name: Install dependencies
run:
sudo apt-get install -y pandoc

- uses: actions/checkout@v4

- name: Verify that the release is new
Expand All @@ -42,7 +56,7 @@ jobs:

- name: Commit release preparation changes
run: |
git -c user.name='github-actions[bot]' -c user.email='github-actions[bot]@users.noreply.github.com' \
git -c user.name='cvat-bot[bot]' -c user.email='147643061+cvat-bot[bot]@users.noreply.github.com' \
commit -a -m "Prepare release v${{ inputs.newVersion }}"
- name: Push release branch
Expand All @@ -51,9 +65,10 @@ jobs:

- name: Create release pull request
env:
GH_TOKEN: "${{ github.token }}"
GH_TOKEN: "${{ steps.gen-token.outputs.token }}"
run: |
gh pr create \
--base=master \
--title="Release v${{ inputs.newVersion }}" \
--body="$(awk '/^## / { hn += 1; next } hn == 1 && !/^</' CHANGELOG.md)"
./dev/gh_release_notes.sh \
| gh pr create \
--base=master \
--title="Release v${{ inputs.newVersion }}" \
--body-file=-
14 changes: 14 additions & 0 deletions dev/gh_release_notes.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/sh

# This script takes the release notes for the most recent release
# and reformats them for use in GitHub.

# In GitHub PR and release descriptions, a single line break is
# equivalent to <br>, so we pipe the text through pandoc to unwrap all lines.

set -eu

repo_root="$(dirname "$0")/.."

awk '/^## / { hn += 1; next } hn == 1 && !/^</' "$repo_root/CHANGELOG.md" \
| pandoc -f gfm -t gfm --wrap=none

0 comments on commit 55f23cc

Please sign in to comment.