From 7b3927dc601512d15fff030288d99d8009e91e65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakob=20M=C3=B6ller?= Date: Tue, 26 Nov 2024 15:05:44 +0100 Subject: [PATCH] chore: rework release note handling (#1139) #### What this PR does / why we need it reworks release note management based on github release note api It does this in the following way: 1. Creates a tag on cutoff that is annotated 2. Calls the Github Release Note API together with a release note configuration file (very similar to drafter but allows us to specify a previous tag) 3. Generates the notes for the current branch based on the last found tag. This has the advantage that we can explicitly determine which release notes are generated from when to when and also use the contributors fields. TODO: 1. Test on other repo 2. Make sure that the Merge commit in main that we used for testing disappears. #### Which issue(s) this PR fixes --- .github/config/release.yml | 28 ++++++++++ .github/release-drafter.yml | 38 ------------- .github/workflows/release-branch.yaml | 14 ++++- .github/workflows/release-drafter.yaml | 74 +++++++++++++++++++++++--- .github/workflows/release.yaml | 21 +++++++- 5 files changed, 128 insertions(+), 47 deletions(-) create mode 100644 .github/config/release.yml delete mode 100644 .github/release-drafter.yml diff --git a/.github/config/release.yml b/.github/config/release.yml new file mode 100644 index 000000000..cf0852160 --- /dev/null +++ b/.github/config/release.yml @@ -0,0 +1,28 @@ +changelog: + exclude: + labels: + - 'kind/skip-release-notes' + - 'wontfix' + - 'triage/wont-fix' + - 'triage/invalid' + categories: + - title: '‼️ Breaking Changes' + labels: + - 'breaking-change' + - title: '🚀 Features' + labels: + - 'kind/enhancement' + - 'feature' + - 'enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'kind/bug' + - 'fix' + - 'bugfix' + - 'bug' + - title: '🧰 Maintenance' + labels: + - 'chore' + - title: '⬆️ Dependencies' + labels: + - 'dependencies' \ No newline at end of file diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml deleted file mode 100644 index 6827c023f..000000000 --- a/.github/release-drafter.yml +++ /dev/null @@ -1,38 +0,0 @@ -name-template: 'v$RESOLVED_VERSION' -tag-template: 'v$RESOLVED_VERSION' -version-template: '$COMPLETE' -change-template: '- $TITLE (#$NUMBER)' -change-title-escapes: '\<*_&#@`' -template: | - # Release v$RESOLVED_VERSION - - $CHANGES -exclude-labels: - - 'kind/skip-release-notes' - - 'wontfix' - - 'triage/wont-fix' - - 'triage/invalid' -categories: - - title: '‼️ Breaking Changes' - labels: - - 'breaking-change' - - title: '🚀 Features' - labels: - - 'kind/enhancement' - - 'feature' - - 'enhancement' - - title: '🐛 Bug Fixes' - collapse-after: 5 - labels: - - 'kind/bug' - - 'fix' - - 'bugfix' - - 'bug' - - title: '🧰 Maintenance' - collapse-after: 3 - labels: - - 'chore' - - title: '⬆️ Dependencies' - collapse-after: 3 - labels: - - 'dependencies' diff --git a/.github/workflows/release-branch.yaml b/.github/workflows/release-branch.yaml index f63d279c5..237c8866c 100644 --- a/.github/workflows/release-branch.yaml +++ b/.github/workflows/release-branch.yaml @@ -80,7 +80,19 @@ jobs: ref: main fetch-depth: 0 token: ${{ steps.generate_token.outputs.token }} - + - name: Setup git config + run: | + git config user.name "GitHub Actions Bot" + git config user.email "<41898282+github-actions[bot]@users.noreply.github.com>" + # tag the cutoff point in main so that it can be used for release note generation + - name: Create Cutoff Tag in Main + run: | + set -e + tag="v${{ needs.cutoff-preconditions.outputs.minor }}" + msg="Cutoff for $tag" + git tag --annotate --message "${msg}" "$tag" + git push origin "$tag" + # create a new branch - name: Create Release Branch run: | set -e diff --git a/.github/workflows/release-drafter.yaml b/.github/workflows/release-drafter.yaml index 607cf7613..e066e35b6 100644 --- a/.github/workflows/release-drafter.yaml +++ b/.github/workflows/release-drafter.yaml @@ -11,7 +11,7 @@ on: permissions: contents: read # The release-drafter action adds PR titles to the release notes once these are merged to main. - # A draft release is kept up-to-date listing the changes for the next minor release version. + # A draft release is kept up-to-date listing the changes for the next minor or patch release version for that branch. jobs: release-version: name: Release Version @@ -31,12 +31,72 @@ jobs: env: RELEASE_VERSION: ${{ needs.release-version.outputs.version }} steps: + - name: Generate token + id: generate_token + uses: tibdex/github-app-token@v2 + with: + app_id: ${{ secrets.OCMBOT_APP_ID }} + private_key: ${{ secrets.OCMBOT_PRIV_KEY }} - name: Checkout uses: actions/checkout@v4 - - name: Drafter - uses: release-drafter/release-drafter@v6 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - tag: ${{env.RELEASE_VERSION }} - version: ${{env.RELEASE_VERSION }} \ No newline at end of file + fetch-depth: 0 + fetch-tags: true + - name: Setup Release with gh + env: + REF: ${{ github.ref }} + REPO: ${{ github.repository }} + GH_TOKEN: ${{ steps.generate_token.outputs.token }} + run: | + # generate the release notes based on the last previous tag. + # match only a valid semver tag. + # also do not match for the cutoff tag for the release branch. + # Example: If we are in releases/v0.18, we don't want to match v0.18 as the cutoff tag. + # Instead we want to match the one before that, which would be v0.17. + # That would generate us the notes from + # v0.17 to HEAD, which is what we want. + # + # Implementors Note: + # ##*\/ removes everything before the last / in the ref, + # e.g. refs/heads/releases/v0.18 -> v0.18 + previous_tag=$(git describe HEAD --abbrev=0 --tags --match "v*" --exclude "${REF##*\/}") + + if [[ -z $previous_tag ]]; then + echo "No previous tag found, cannot generate release notes" + exit 1 + fi + + echo "Generating release notes for ${{env.RELEASE_VERSION}} starting from ${previous_tag} to HEAD" + + notes=$(\ + gh api \ + --method POST \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + /repos/${REPO}/releases/generate-notes \ + -f "tag_name=${{env.RELEASE_VERSION}}" \ + -f "target_commitish=${{ github.ref }}" \ + -f "previous_tag_name=${previous_tag}" \ + -f "configuration_file_path=.github/config/release.yml" \ + -q .body \ + ) + + echo "Release Notes generated for ${{env.RELEASE_VERSION}}:" + echo "${notes}" + + echo "Verifying if release ${{env.RELEASE_VERSION}} already exists" + if [[ -z $(gh release list -R ${REPO} --json name -q '.[] | select(.name == "${{env.RELEASE_VERSION}}")') ]]; then + echo "Release ${{env.RELEASE_VERSION}} does not exist yet, creating from scratch" + gh release create ${{env.RELEASE_VERSION}} \ + --title "${{env.RELEASE_VERSION}}" \ + --notes "${notes}" \ + --draft \ + --latest=false \ + --target ${{ github.ref }} \ + -R ${REPO} + else + echo "Release ${{env.RELEASE_VERSION}} already exists, updating existing..." + gh release edit ${{env.RELEASE_VERSION}} \ + --notes "${notes}" \ + -R ${REPO} + fi diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index f15bdb225..b6034f388 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -181,8 +181,27 @@ jobs: git commit -m "Release $RELEASE_VERSION" msg="Release ${{ env.RELEASE_VERSION }}" git tag --annotate --message "${msg}" ${{ env.RELEASE_VERSION }} - # push both the tag as well as a release branch with that tag. + # push the tag. git push origin ${{ env.RELEASE_VERSION }} + # If we encounter a release (i.e. NOT a candidate), we want to keep the tag in the release branch git history + # by merging it (without taking over its contents). + # This allows tools that rely on the latest tag (such as Release Note generators or git describe) + # to recognize the release as the latest version. + # We can then use this to generate release notes based on the previous tag. + # If we previously built a release candidate, the tag is not merged back. + # That results in the tag being "omitted" / not recognized while generating release notes. + # This is intended, because a candidate should never influence further release notes. + # Example: + # Branch releases/v0.19 + # - Candidate Build v0.19.0-rc.1 => no merge, release notes based on original cutoff + # - Actual Build v0.19.0 => merge, release notes based on original cutoff + # - Candidate Build v0.19.1-rc.1 => no merge, release notes based on v0.19.0 due to previous merge + - name: Merge Release Tag back into release branch if not a release candidate + if: inputs.release_candidate == false + run: | + git checkout ${{ github.ref }} + git merge --strategy ours ${{ env.RELEASE_VERSION }} + git push origin ${{ github.ref }} - name: Create GPG Token file from Secret run: |