Release SDK #11
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 SDK | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: SDK version to release (SemVer, for example 1.2.3) | |
| required: true | |
| type: string | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: release-sdk | |
| cancel-in-progress: false | |
| jobs: | |
| authorize: | |
| name: Authorize release actor | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Ensure actor is maintainer or admin on main | |
| uses: actions/github-script@v7 | |
| env: | |
| TRIGGERING_ACTOR: ${{ github.triggering_actor }} | |
| with: | |
| script: | | |
| const owner = context.repo.owner | |
| const repo = context.repo.repo | |
| const actor = process.env.TRIGGERING_ACTOR || context.actor | |
| const refName = context.ref.replace("refs/heads/", "") | |
| if (refName !== "main") { | |
| core.setFailed(`Release workflow must be run from 'main'. Current ref is '${refName}'.`) | |
| return | |
| } | |
| const response = await github.rest.repos.getCollaboratorPermissionLevel({ | |
| owner, | |
| repo, | |
| username: actor | |
| }) | |
| const permission = String(response.data.permission || "").toLowerCase() | |
| const role = String(response.data.role_name || permission).toLowerCase() | |
| const allowed = new Set(["maintain", "admin"]) | |
| core.info(`Actor '${actor}' has role='${role}', permission='${permission}'`) | |
| if (!allowed.has(role)) { | |
| core.setFailed( | |
| `Only maintainers/admins can run this workflow. Actor '${actor}' has role '${role}' (permission '${permission}').` | |
| ) | |
| } | |
| checks: | |
| name: Shared checks (with integration tests) | |
| needs: authorize | |
| uses: ./.github/workflows/shared-checks.yml | |
| with: | |
| run_integration_tests: true | |
| secrets: inherit | |
| publish: | |
| name: Bump, tag, and publish release | |
| needs: checks | |
| runs-on: macos-latest | |
| env: | |
| RELEASE_VERSION: ${{ inputs.version }} | |
| steps: | |
| - name: Checkout triggering commit | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.sha }} | |
| fetch-depth: 0 | |
| token: ${{ secrets.IOS_RELEASE_PUSH_TOKEN }} | |
| - name: Validate version input and detect rerun state | |
| id: release_state | |
| run: | | |
| set -euo pipefail | |
| version_file="Sources/ClerkKit/Utils/Version.swift" | |
| if [[ ! "$RELEASE_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z]+(\.[0-9A-Za-z]+)*)?$ ]]; then | |
| echo "❌ Invalid version: '$RELEASE_VERSION'. Expected SemVer (for example 1.2.3 or 1.2.3-rc.1)." | |
| exit 1 | |
| fi | |
| current_version="$(sed -n 's/.*sdkVersion: String = "\([^"]*\)".*/\1/p' "$version_file")" | |
| if [ -z "$current_version" ]; then | |
| echo "❌ Could not read current SDK version from $version_file" | |
| exit 1 | |
| fi | |
| version_already_set="false" | |
| if [ "$current_version" = "$RELEASE_VERSION" ]; then | |
| version_already_set="true" | |
| echo "ℹ️ $version_file already contains sdkVersion: String = \"$RELEASE_VERSION\"." | |
| fi | |
| release_tag="$RELEASE_VERSION" | |
| tag_already_exists="false" | |
| if git rev-parse --verify "refs/tags/$release_tag" >/dev/null 2>&1; then | |
| tag_already_exists="true" | |
| echo "ℹ️ Tag '$release_tag' already exists locally; skipping git tag \"$release_tag\"." | |
| fi | |
| if git ls-remote --tags origin "refs/tags/$release_tag" | grep -q "$release_tag"; then | |
| tag_already_exists="true" | |
| echo "ℹ️ Tag '$release_tag' already exists on origin; skipping git push origin \"$release_tag\"." | |
| fi | |
| echo "version_already_set=$version_already_set" >> "$GITHUB_OUTPUT" | |
| echo "tag_already_exists=$tag_already_exists" >> "$GITHUB_OUTPUT" | |
| - name: Update SDK version constant | |
| if: steps.release_state.outputs.version_already_set != 'true' | |
| run: | | |
| set -euo pipefail | |
| version_file="Sources/ClerkKit/Utils/Version.swift" | |
| sed -i '' -E "s/(sdkVersion: String = \").*(\")/\\1$RELEASE_VERSION\\2/" "$version_file" | |
| echo "Updated version file:" | |
| grep -n "sdkVersion" "$version_file" | |
| - name: Verify version change is present | |
| run: | | |
| set -euo pipefail | |
| version_file="Sources/ClerkKit/Utils/Version.swift" | |
| if ! grep -q "sdkVersion: String = \"$RELEASE_VERSION\"" "$version_file"; then | |
| echo "❌ Failed to apply SDK version update in $version_file" | |
| exit 1 | |
| fi | |
| - name: Commit release version bump to main | |
| run: | | |
| set -euo pipefail | |
| if [ "${{ steps.release_state.outputs.version_already_set }}" = "true" ]; then | |
| echo "ℹ️ Skipping commit because Sources/ClerkKit/Utils/Version.swift already matches $RELEASE_VERSION." | |
| exit 0 | |
| fi | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add Sources/ClerkKit/Utils/Version.swift | |
| if git diff --cached --quiet; then | |
| echo "❌ No changes to commit for release version bump" | |
| exit 1 | |
| fi | |
| git commit -m "chore(release): $RELEASE_VERSION" | |
| git push origin HEAD:main | |
| - name: Create and push release tag | |
| run: | | |
| set -euo pipefail | |
| release_tag="$RELEASE_VERSION" | |
| if [ "${{ steps.release_state.outputs.tag_already_exists }}" = "true" ]; then | |
| echo "ℹ️ Skipping git tag \"$release_tag\" and push because tag already exists." | |
| exit 0 | |
| fi | |
| git tag "$release_tag" | |
| git push origin "$release_tag" | |
| - name: Determine prerelease flag | |
| id: prerelease | |
| run: | | |
| if [[ "$RELEASE_VERSION" == *-* ]]; then | |
| echo "value=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "value=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Publish GitHub Release with generated notes | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ inputs.version }} | |
| name: ${{ inputs.version }} | |
| generate_release_notes: true | |
| prerelease: ${{ steps.prerelease.outputs.value }} | |
| token: ${{ secrets.IOS_RELEASE_PUSH_TOKEN }} |