feat: [FEEDS-1965] regenerate OpenAPI client (#112) #98
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - '*.x' | |
| workflow_dispatch: | |
| inputs: | |
| publish_tag: | |
| description: 'Existing tag to (re)publish to Maven Central, e.g. 10.1.2. Leave empty for a normal release run.' | |
| required: false | |
| default: '' | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: release-${{ github.ref_name }} | |
| cancel-in-progress: false | |
| jobs: | |
| # Reversible half: keep the Release PR current. Stands down only while a release is | |
| # already pending, because until that one is tagged there is no release commit to stop | |
| # the walk at and it would propose the same commits again in a second Release PR. | |
| release-pr: | |
| name: Release PR | |
| needs: detect | |
| # Gate on the explicit value. If detect fails or is skipped the output is empty, and | |
| # anything short of a definite "nothing pending" has to hold this job back, or it | |
| # proposes a second Release PR on top of one that may still be untagged. | |
| if: needs.detect.outputs.pending == 'false' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| permissions: | |
| contents: write | |
| issues: write | |
| pull-requests: write | |
| steps: | |
| - uses: googleapis/release-please-action@v4 | |
| with: | |
| config-file: release-please-config.json | |
| manifest-file: .release-please-manifest.json | |
| target-branch: ${{ github.ref_name }} | |
| skip-github-release: true | |
| # The tag, the GitHub Release and the Maven Central push are irreversible, so they run only on the push that merged the Release PR: `ready` requires the pending release's merge commit to be this run's commit. A release from the default branch has no test run, because the Release PR adds only the version bump and changelog to already-tested code; a hotfix release from `N.x` runs the unit lane first, because hotfix commits are pushed without a PR. | |
| detect: | |
| name: Detect pending release | |
| if: >- | |
| (github.event_name == 'push' || inputs.publish_tag == '') && | |
| (github.ref_name == 'main' || endsWith(github.ref_name, '.x')) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| permissions: | |
| contents: read | |
| # write, not read: a stuck release is announced on its own Release PR, because | |
| # nothing else reaches a person without them opening the run first. | |
| pull-requests: write | |
| issues: read | |
| outputs: | |
| pending: ${{ steps.find.outputs.pending }} | |
| ready: ${{ steps.find.outputs.ready }} | |
| steps: | |
| - name: Find a merged Release PR waiting to be tagged | |
| id: find | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| BASE: ${{ github.ref_name }} | |
| HEAD_SHA: ${{ github.sha }} | |
| run: | | |
| pending=false | |
| ready=false | |
| lookup_failed=false | |
| # Query the label directly, and page: release-please applies the label when it | |
| # opens the Release PR, not when it merges, so every Release PR closed without | |
| # merging keeps it forever and holds a slot in this listing. One page would | |
| # eventually stop containing the genuinely pending release, which reads as | |
| # "nothing to release" and passes. merged_at comes back in the listing, so | |
| # filtering on it here keeps the per-PR lookups below to real candidates. | |
| # --paginate makes this N requests, so guard it too, and remember that a failure | |
| # here means "unknown", never "nothing to release". | |
| if ! nums="$(gh api --paginate "repos/${GITHUB_REPOSITORY}/issues" \ | |
| -X GET -f state=closed -f labels='autorelease: pending' -f per_page=100 \ | |
| --jq '.[] | select(.pull_request.merged_at != null) | .number')"; then | |
| echo "::warning::Could not list pending releases." | |
| nums="" | |
| lookup_failed=true | |
| fi | |
| # The base branch is not in that listing, so each candidate still needs a | |
| # lookup: a hotfix branch can hold its own pending release, and taking the | |
| # newest label match would drop this branch's release until the other clears. | |
| num="" | |
| sha="" | |
| for n in $nums; do | |
| # Under `bash -e` an unguarded assignment from a non-2xx would abort the | |
| # step, which would fail detect and skip release-pr with it. | |
| if ! sha="$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${n}" \ | |
| --jq 'select(.merged_at != null and .base.ref == env.BASE) | .merge_commit_sha // empty')"; then | |
| echo "::warning::Could not read PR #${n}; skipping it." | |
| sha="" | |
| lookup_failed=true | |
| continue | |
| fi | |
| if [ -n "$sha" ]; then | |
| num="$n" | |
| break | |
| fi | |
| done | |
| if [ -z "$sha" ] && [ "$lookup_failed" = true ]; then | |
| # Unknown is not the same as nothing. Releasing on a guess is how a second | |
| # Release PR lands on top of one that was never tagged. | |
| pending=true | |
| echo "::warning::Could not determine whether a release is pending on ${BASE}; standing down." | |
| elif [ -z "$sha" ]; then | |
| echo "No pending release on ${BASE}." | |
| elif [ "$sha" != "$HEAD_SHA" ]; then | |
| # Reached when an earlier release run failed after the Release PR merged. | |
| # Finishing it from a later push would retry a deterministic failure (a refused major tag, a broken build) on every push, so stand down and say why. | |
| pending=true | |
| echo "::warning::Release PR #${num} is still pending at ${sha}, which is not this run's commit ${HEAD_SHA}. Re-run the workflow run for ${sha} to finish that release." | |
| { | |
| echo "### Release stuck" | |
| echo | |
| echo "Release PR #${num} merged at \`${sha}\` and was never tagged, so no Release PR will be opened or refreshed until it clears." | |
| echo | |
| echo "Re-run the \`Release\` run for \`${sha}\`. If that commit is genuinely broken, remove the \`autorelease: pending\` label from #${num} by hand and release forward." | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| # A warning annotation and a step summary are both only visible to someone who | |
| # already opened the run. Tell the Release PR's subscribers once per stuck sha. | |
| marker="<!-- release-stuck:${sha} -->" | |
| seen="$(gh api "repos/${GITHUB_REPOSITORY}/issues/${num}/comments" --paginate --jq '.[].body' || echo "")" | |
| if ! printf '%s' "$seen" | grep -qF "$marker"; then | |
| { | |
| echo "$marker" | |
| echo "This release is stuck: #${num} merged at \`${sha}\` and was never tagged, so no Release PR is opened or refreshed until it clears." | |
| echo | |
| echo "Re-run the \`Release\` run for \`${sha}\`. If that commit is genuinely broken, remove the \`autorelease: pending\` label here by hand and release forward." | |
| } > "${RUNNER_TEMP}/release-stuck.md" | |
| gh pr comment "$num" --repo "$GITHUB_REPOSITORY" --body-file "${RUNNER_TEMP}/release-stuck.md" \ | |
| || echo "::warning::Could not comment on #${num}." | |
| fi | |
| else | |
| pending=true | |
| ready=true | |
| echo "Pending release #${num} will be tagged at ${sha}." | |
| fi | |
| { | |
| echo "pending=${pending}" | |
| echo "ready=${ready}" | |
| } >> "$GITHUB_OUTPUT" | |
| tests: | |
| name: Tests (hotfix only) | |
| needs: detect | |
| if: needs.detect.outputs.ready == 'true' && github.ref_name != github.event.repository.default_branch | |
| uses: ./.github/workflows/run_tests.yml | |
| # Irreversible half. | |
| release: | |
| name: 🚀 Tag and release | |
| needs: [detect, tests] | |
| if: >- | |
| ${{ !cancelled() && needs.detect.result == 'success' && needs.detect.outputs.ready == 'true' | |
| && (needs.tests.result == 'success' || needs.tests.result == 'skipped') }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| permissions: | |
| contents: write | |
| issues: write | |
| pull-requests: write | |
| outputs: | |
| release_created: ${{ steps.release.outputs.release_created }} | |
| tag_name: ${{ steps.release.outputs.tag_name }} | |
| steps: | |
| - uses: googleapis/release-please-action@v4 | |
| id: release | |
| with: | |
| config-file: release-please-config.json | |
| manifest-file: .release-please-manifest.json | |
| target-branch: ${{ github.ref_name }} | |
| skip-github-pull-request: true | |
| # Chained rather than triggered on the release event, because a GitHub Release created | |
| # with GITHUB_TOKEN starts no new workflow run. Also reachable on its own through | |
| # workflow_dispatch with publish_tag, which is the recovery path once GitHub has | |
| # retired the original run and "Re-run failed jobs" is gone. | |
| publish: | |
| name: 📦 Publish to Maven Central | |
| needs: release | |
| if: >- | |
| !cancelled() && | |
| (inputs.publish_tag != '' || needs.release.outputs.release_created == 'true') | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Resolve tag | |
| id: target | |
| env: | |
| PUBLISH_TAG: ${{ inputs.publish_tag }} | |
| RELEASE_TAG: ${{ needs.release.outputs.tag_name }} | |
| run: | | |
| tag="${PUBLISH_TAG:-$RELEASE_TAG}" | |
| echo "tag=${tag}" >> "$GITHUB_OUTPUT" | |
| - uses: actions/checkout@v5.0.1 | |
| with: | |
| ref: ${{ steps.target.outputs.tag }} | |
| # The Gradle build runs project code, and nothing here writes to the | |
| # repository, so do not leave GITHUB_TOKEN in .git/config. | |
| persist-credentials: false | |
| - name: Setup JDK 17 | |
| uses: actions/setup-java@v5.1.0 | |
| with: | |
| distribution: 'corretto' | |
| java-version: '17' | |
| - name: Setup Gradle | |
| uses: gradle/actions/setup-gradle@v4 | |
| - name: Publish to Maven Central | |
| env: | |
| STREAM_API_KEY: ${{ vars.STREAM_API_KEY }} | |
| STREAM_API_SECRET: ${{ secrets.STREAM_API_SECRET }} | |
| GPG_KEY_CONTENTS: ${{ secrets.GPG_KEY_CONTENTS }} | |
| OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} | |
| OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} | |
| SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} | |
| SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} | |
| SIGNING_SECRET_KEY_RING_FILE: ${{ secrets.SIGNING_SECRET_KEY_RING_FILE }} | |
| SONATYPE_STAGING_PROFILE_ID: ${{ secrets.SONATYPE_STAGING_PROFILE_ID }} | |
| run: | | |
| ./gradlew publishToSonatype --no-daemon --max-workers 1 closeAndReleaseSonatypeStagingRepository |