From e644ef0481b64518b4466eb7d6e76bf263f5a350 Mon Sep 17 00:00:00 2001 From: Matthew Watkins Date: Wed, 8 Oct 2025 12:25:45 +0100 Subject: [PATCH] CI: Add release-drafter workflow/config - Automatically creates GitHub draft releases - Amend release.yaml to promote draft release on tag push Change-Id: Ia8d95253aa5a03bf59ccb7b45d7dfea28e47c16e Signed-off-by: Matthew Watkins --- .github/release-drafter.yml | 70 +++++++++++++++++++++++++ .github/workflows/release-drafter.yaml | 71 +++++++++++++++++++++++++ .github/workflows/release.yaml | 94 ++++++++++++++++++++++++++++++++-- 3 files changed, 232 insertions(+), 3 deletions(-) create mode 100644 .github/release-drafter.yml create mode 100644 .github/workflows/release-drafter.yaml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 00000000..7bbe8b18 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,70 @@ +--- +# SPDX-FileCopyrightText: 2021 Andrew Grimberg +# SPDX-License-Identifier: Apache-2.0 + +name-template: "v$RESOLVED_VERSION" +tag-template: "v$RESOLVED_VERSION" +change-template: "- $TITLE @$AUTHOR (#$NUMBER)" +sort-direction: ascending +categories: + - title: ":boom: Breaking Change :boom:" + labels: + - "breaking-change" + - title: ":zap: Enhancements :zap:" + labels: + - "enhancement" + - title: ":sparkles: New Features :sparkles:" + labels: + - "feature" + - title: ":bug: Bug Fixes :bug:" + labels: + - "fix" + - "bugfix" + - "bug" + - title: ":wrench: Maintenance :wrench:" + labels: + - "chore" + - "documentation" + - "maintenance" + - "repo" + - "dependencies" + - "github_actions" + - "refactor" + - title: ":mortar_board: Code Quality :mortar_board:" + labels: + - "code-quality" + - "CI" + - "test" +autolabeler: + - label: "breaking-change" + title: + - "/!:/i" + - label: "feature" + title: + - "/feat:/i" + - label: "bug" + title: + - "/fix:/i" + - label: "refactor" + title: + - "/refactor:/i" + - label: "code-quality" + title: + - "/test:/i" + - label: "CI" + title: + - "/ci:/i" + - label: "chore" + title: + - "/chore:/i" + - label: "documentation" + title: + - "/docs:/i" +# yamllint disable rule:line-length +template: | + [![Downloads for this release](https://img.shields.io/github/downloads/os-climate/osc-github-devops/v$RESOLVED_VERSION/total.svg)](https://github.com/os-climate/osc-github-devops/releases/v$RESOLVED_VERSION) + + $CHANGES + + ## Links + - [Submit bugs/feature requests](https://github.com/os-climate/osc-github-devops/issues) diff --git a/.github/workflows/release-drafter.yaml b/.github/workflows/release-drafter.yaml new file mode 100644 index 00000000..33c0d607 --- /dev/null +++ b/.github/workflows/release-drafter.yaml @@ -0,0 +1,71 @@ +--- +# SPDX-FileCopyrightText: 2025 The Linux Foundation +# SPDX-License-Identifier: Apache-2.0 + +name: 'Release Drafter' + +"on": + push: + branches: + - main + - master + # pull_request is required for autolabeler + pull_request: + types: + - opened + - synchronize + - reopened + # pull_request_target is required for autolabeler on PRs from forks + pull_request_target: + types: + - opened + - synchronize + - reopened + +permissions: {} + +concurrency: + # yamllint disable-line rule:line-length + group: >- + rd-${{ github.event_name }}-${{ github.event.number }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + show_concurrency_group: + name: 'Concurrency Group' + runs-on: 'ubuntu-latest' + timeout-minutes: 2 + steps: + - name: 'Concurrency Group' + shell: bash + # yamllint disable rule:line-length + run: | + # Concurrency Group + echo "Concurrency group: rd-${{ github.event_name }}-${{ github.event.number }}-${{ github.ref }}" + # shellcheck disable=SC2129 + echo "## Release Drafter" \ + >> "$GITHUB_STEP_SUMMARY" + echo "Concurrency group: rd-${{ github.event_name }}-${{ github.event.number }}-${{ github.ref }}" \ + >> "$GITHUB_STEP_SUMMARY" + + # yamllint enable rule:line-length + update_release_draft: + name: 'Update Release Draft' + permissions: + # write permission is required to create releases + contents: write + # write permission is required for autolabeler + pull-requests: write + runs-on: 'ubuntu-latest' + timeout-minutes: 3 + steps: + # Harden the runner used by this workflow + # yamllint disable-line rule:line-length + - uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 + with: + egress-policy: 'audit' + + # yamllint disable-line rule:line-length + - uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 # v6.1.0 + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 02d18fb3..9c665eba 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -23,6 +23,7 @@ jobs: timeout-minutes: 1 outputs: tag: "${{ steps.tag-validate.outputs.tag }}" + should_promote: "${{ steps.check-release.outputs.should_promote }}" steps: # Harden the runner used by this workflow # yamllint disable-line rule:line-length @@ -47,6 +48,32 @@ jobs: >> "$GITHUB_STEP_SUMMARY" exit 1 + - name: 'Check if release exists' + id: 'check-release' + shell: bash + env: + GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + run: | + TAG="${{ steps.tag-validate.outputs.tag }}" + + # Check if release exists and get its draft status + if RELEASE_INFO=$(gh release view "$TAG" --json isDraft \ + 2>/dev/null); then + IS_DRAFT=$(echo "$RELEASE_INFO" | jq -r '.isDraft') + if [ "$IS_DRAFT" = "false" ]; then + echo "should_promote=false" >> "$GITHUB_OUTPUT" + echo "Published release already exists for tag $TAG, " \ + "skipping promotion" + else + echo "should_promote=true" >> "$GITHUB_OUTPUT" + echo "Draft release exists for tag $TAG, " \ + "will proceed with promotion" + fi + else + echo "should_promote=true" >> "$GITHUB_OUTPUT" + echo "No release found for tag $TAG, will proceed with promotion" + fi + python-build: name: 'Python Build' needs: 'tag-validate' @@ -133,7 +160,6 @@ jobs: uses: lfreleng-actions/python-audit-action@bab5316468c108870eb759ef0de622bae9239aad # v0.2.2 with: python_version: "${{ matrix.python-version }}" - permit_fail: 'true' test-pypi: name: 'Test PyPI Publishing' @@ -197,6 +223,67 @@ jobs: tag: "${{ needs.tag-validate.outputs.tag }}" pypi_credential: "${{ secrets.PYPI_CREDENTIAL }}" + + promote-release: + name: 'Promote Draft Release' + # yamllint disable-line rule:line-length + if: needs.tag-validate.outputs.should_promote == 'true' + needs: + - 'tag-validate' + - 'pypi' + runs-on: 'ubuntu-latest' + permissions: + contents: write # IMPORTANT: needed to edit a draft release and promote it + timeout-minutes: 2 + outputs: + release_url: "${{ steps.promote-release.outputs.release_url }}" + steps: + # Harden the runner used by this workflow + # yamllint disable-line rule:line-length + - uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 + with: + egress-policy: 'audit' + + # yamllint disable-line rule:line-length + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + + - name: 'Check if release is already promoted' + id: 'check-promoted' + shell: bash + env: + GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + run: | + TAG="${{ needs.tag-validate.outputs.tag }}" + if gh release view "$TAG" --json isDraft --jq '.isDraft' \ + 2>/dev/null | grep -q "false"; then + echo "Release $TAG is already promoted, skipping promotion" + echo "already_promoted=true" >> "$GITHUB_OUTPUT" + else + echo "Release $TAG is draft or doesn't exist, " \ + "proceeding with promotion" + echo "already_promoted=false" >> "$GITHUB_OUTPUT" + fi + + - name: 'Promote draft release' + id: 'promote-release' + if: steps.check-promoted.outputs.already_promoted == 'false' + # yamllint disable-line rule:line-length + uses: lfreleng-actions/draft-release-promote-action@d7e7df12e32fa26b28dbc2f18a12766482785399 # v0.1.2 + with: + token: "${{ secrets.GITHUB_TOKEN }}" + tag: "${{ needs.tag-validate.outputs.tag }}" + latest: true + + - name: 'Set release URL for already promoted release' + if: steps.check-promoted.outputs.already_promoted == 'true' + shell: bash + env: + GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + run: | + TAG="${{ needs.tag-validate.outputs.tag }}" + RELEASE_URL=$(gh release view "$TAG" --json url --jq '.url') + echo "release_url=$RELEASE_URL" >> "$GITHUB_OUTPUT" + # Need to attach build artefacts to the release # This step could potentially be moved # (May be better to when/where the release is still in draft state) @@ -206,8 +293,9 @@ jobs: needs: - 'tag-validate' - 'python-build' - - 'test-pypi' - - 'pypi' + - 'promote-release' + # yamllint disable-line rule:line-length + if: always() && (needs.promote-release.result == 'success' || needs.promote-release.result == 'skipped') permissions: contents: write # IMPORTANT: needed to edit release, attach artefacts timeout-minutes: 5 -- 2.16.6