Skip to content

feat: [FEEDS-1965] regenerate OpenAPI client (#112) #98

feat: [FEEDS-1965] regenerate OpenAPI client (#112)

feat: [FEEDS-1965] regenerate OpenAPI client (#112) #98

Workflow file for this run

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