From b57a034cff55de0cbad2fcc87751bb22d819d754 Mon Sep 17 00:00:00 2001 From: mosherBT Date: Fri, 30 Oct 2026 13:12:29 -0400 Subject: [PATCH 1/6] ci: add a Tag release workflow that bumps and pushes the release tag --- .github/workflows/android-sdk-ci.yml | 4 ++ .github/workflows/tag.yml | 98 ++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 .github/workflows/tag.yml diff --git a/.github/workflows/android-sdk-ci.yml b/.github/workflows/android-sdk-ci.yml index e03f825..3717e27 100644 --- a/.github/workflows/android-sdk-ci.yml +++ b/.github/workflows/android-sdk-ci.yml @@ -6,6 +6,10 @@ on: tags: - v[0-9]+.[0-9]+.[0-9]+ - v[0-9]+.[0-9]+.[0-9]+-* + # Called by tag.yml after it pushes a tag. A tag pushed with GITHUB_TOKEN does + # not start another workflow run, so the push trigger above will not fire for + # that tag and CI has to be invoked directly. + workflow_call: jobs: sdk-tests: diff --git a/.github/workflows/tag.yml b/.github/workflows/tag.yml new file mode 100644 index 0000000..116cbae --- /dev/null +++ b/.github/workflows/tag.yml @@ -0,0 +1,98 @@ +name: Tag release + +# Tags master and runs CI against the tag. Releasing is still a deliberate act -- +# this is workflow_dispatch, not a push trigger -- but it replaces doing the tag +# dance by hand, and it cannot mis-derive the version or tag the wrong branch. +# +# Pre-releases (vX.Y.Z-rc.1 and friends) are not covered here. Push those tags by +# hand; CI still picks them up on its own. + +on: + workflow_dispatch: + inputs: + bump: + description: "Which part of the version to increment" + type: choice + options: + - minor + - major + - patch + default: minor + +permissions: + contents: read + +jobs: + tag: + name: Tag master + runs-on: ubuntu-24.04 + permissions: + contents: write + outputs: + version: ${{ steps.next.outputs.version }} + steps: + - name: Refuse to release from anything but master + if: github.ref != 'refs/heads/master' + run: | + echo "::error::Release must run on master, got ${{ github.ref }}. Re-run and pick master." + exit 1 + + - name: Checkout code + uses: actions/checkout@v5 + with: + # Tags are needed to work out what the current version is. + fetch-depth: 0 + + - name: Compute next version + id: next + env: + BUMP: ${{ inputs.bump }} + run: | + set -euo pipefail + # Releases have been tagged both as X.Y.Z and vX.Y.Z, so the prefix is + # stripped before sorting. Only strict release tags seed the next version; + # pre-release tags sort above the release they precede. + # `|| true` keeps the fallback below reachable: with pipefail a grep that + # matches nothing fails the pipeline, and set -e would kill the step + # before the default could apply. + latest=$(git tag -l | grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+$' | sed 's/^v//' | sort -V | tail -n1 || true) + latest=${latest:-0.0.0} + IFS=. read -r major minor patch <<<"${latest}" + case "$BUMP" in + major) major=$((major + 1)); minor=0; patch=0 ;; + minor) minor=$((minor + 1)); patch=0 ;; + patch) patch=$((patch + 1)) ;; + esac + version="v${major}.${minor}.${patch}" + echo "${latest} -> ${version}" + echo "version=${version}" >>"$GITHUB_OUTPUT" + { + echo "### Release \`${version}\`" + echo "" + echo "Previous: \`${latest}\` · bump: \`${BUMP}\` · commit: \`${GITHUB_SHA:0:7}\`" + } >>"$GITHUB_STEP_SUMMARY" + + - name: Create and push tag + env: + VERSION: ${{ steps.next.outputs.version }} + run: | + set -euo pipefail + for tag in "${VERSION}" "${VERSION#v}"; do + if git rev-parse -q --verify "refs/tags/${tag}" >/dev/null; then + echo "::error::${tag} already exists. Someone tagged out of band." + exit 1 + fi + done + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git tag -a "${VERSION}" -m "Release ${VERSION}" + git push origin "${VERSION}" + + ci: + # A tag pushed with GITHUB_TOKEN does not start another workflow run, so the + # CI workflow's `on: push: tags` will not fire for the tag created above. CI is + # therefore called explicitly rather than left to trigger itself. It keeps its + # push trigger, so a hand-pushed tag still runs. + name: CI + needs: tag + uses: ./.github/workflows/android-sdk-ci.yml From b5c30c270e0af9b54d7cfdcbedd652caad43380b Mon Sep 17 00:00:00 2001 From: mosherBT Date: Fri, 30 Oct 2026 13:17:49 -0400 Subject: [PATCH 2/6] comments --- .github/workflows/android-sdk-ci.yml | 3 --- .github/workflows/tag.yml | 19 +------------------ 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/.github/workflows/android-sdk-ci.yml b/.github/workflows/android-sdk-ci.yml index 3717e27..bba8f1b 100644 --- a/.github/workflows/android-sdk-ci.yml +++ b/.github/workflows/android-sdk-ci.yml @@ -6,9 +6,6 @@ on: tags: - v[0-9]+.[0-9]+.[0-9]+ - v[0-9]+.[0-9]+.[0-9]+-* - # Called by tag.yml after it pushes a tag. A tag pushed with GITHUB_TOKEN does - # not start another workflow run, so the push trigger above will not fire for - # that tag and CI has to be invoked directly. workflow_call: jobs: diff --git a/.github/workflows/tag.yml b/.github/workflows/tag.yml index 116cbae..7e4e06d 100644 --- a/.github/workflows/tag.yml +++ b/.github/workflows/tag.yml @@ -1,12 +1,5 @@ name: Tag release -# Tags master and runs CI against the tag. Releasing is still a deliberate act -- -# this is workflow_dispatch, not a push trigger -- but it replaces doing the tag -# dance by hand, and it cannot mis-derive the version or tag the wrong branch. -# -# Pre-releases (vX.Y.Z-rc.1 and friends) are not covered here. Push those tags by -# hand; CI still picks them up on its own. - on: workflow_dispatch: inputs: @@ -40,7 +33,6 @@ jobs: - name: Checkout code uses: actions/checkout@v5 with: - # Tags are needed to work out what the current version is. fetch-depth: 0 - name: Compute next version @@ -49,12 +41,6 @@ jobs: BUMP: ${{ inputs.bump }} run: | set -euo pipefail - # Releases have been tagged both as X.Y.Z and vX.Y.Z, so the prefix is - # stripped before sorting. Only strict release tags seed the next version; - # pre-release tags sort above the release they precede. - # `|| true` keeps the fallback below reachable: with pipefail a grep that - # matches nothing fails the pipeline, and set -e would kill the step - # before the default could apply. latest=$(git tag -l | grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+$' | sed 's/^v//' | sort -V | tail -n1 || true) latest=${latest:-0.0.0} IFS=. read -r major minor patch <<<"${latest}" @@ -89,10 +75,7 @@ jobs: git push origin "${VERSION}" ci: - # A tag pushed with GITHUB_TOKEN does not start another workflow run, so the - # CI workflow's `on: push: tags` will not fire for the tag created above. CI is - # therefore called explicitly rather than left to trigger itself. It keeps its - # push trigger, so a hand-pushed tag still runs. + # Tags pushed with GITHUB_TOKEN don't trigger other workflows, so CI is called directly. name: CI needs: tag uses: ./.github/workflows/android-sdk-ci.yml From 035524106307dcf5ac87bce654505d42f831f6fe Mon Sep 17 00:00:00 2001 From: mosherBT Date: Tue, 10 Nov 2026 11:26:24 -0500 Subject: [PATCH 3/6] ci: fail the Tag release unless it matches optableVersionName --- .github/workflows/tag.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/tag.yml b/.github/workflows/tag.yml index 7e4e06d..eb9cdd0 100644 --- a/.github/workflows/tag.yml +++ b/.github/workflows/tag.yml @@ -58,6 +58,17 @@ jobs: echo "Previous: \`${latest}\` · bump: \`${BUMP}\` · commit: \`${GITHUB_SHA:0:7}\`" } >>"$GITHUB_STEP_SUMMARY" + - name: Check gradle.properties version + env: + VERSION: ${{ steps.next.outputs.version }} + run: | + set -euo pipefail + name=$(sed -nE 's/^optableVersionName=(.*)$/\1/p' gradle.properties) + if [[ "${name}" != "${VERSION#v}" ]]; then + echo "::error::Computed ${VERSION} but gradle.properties has optableVersionName=${name:-}. Fix the bump or the release prep PR." + exit 1 + fi + - name: Create and push tag env: VERSION: ${{ steps.next.outputs.version }} From 71f797b9c67d07c4deae99a850926746128b7a85 Mon Sep 17 00:00:00 2001 From: mosherBT Date: Tue, 10 Nov 2026 11:28:44 -0500 Subject: [PATCH 4/6] Revert "ci: fail the Tag release unless it matches optableVersionName" This reverts commit 035524106307dcf5ac87bce654505d42f831f6fe. --- .github/workflows/tag.yml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/.github/workflows/tag.yml b/.github/workflows/tag.yml index eb9cdd0..7e4e06d 100644 --- a/.github/workflows/tag.yml +++ b/.github/workflows/tag.yml @@ -58,17 +58,6 @@ jobs: echo "Previous: \`${latest}\` · bump: \`${BUMP}\` · commit: \`${GITHUB_SHA:0:7}\`" } >>"$GITHUB_STEP_SUMMARY" - - name: Check gradle.properties version - env: - VERSION: ${{ steps.next.outputs.version }} - run: | - set -euo pipefail - name=$(sed -nE 's/^optableVersionName=(.*)$/\1/p' gradle.properties) - if [[ "${name}" != "${VERSION#v}" ]]; then - echo "::error::Computed ${VERSION} but gradle.properties has optableVersionName=${name:-}. Fix the bump or the release prep PR." - exit 1 - fi - - name: Create and push tag env: VERSION: ${{ steps.next.outputs.version }} From 51e1f6ddc84a4e38fd8de737c040954bb90c1545 Mon Sep 17 00:00:00 2001 From: mosherBT Date: Wed, 11 Nov 2026 12:34:17 -0500 Subject: [PATCH 5/6] comments --- .github/workflows/tag.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/.github/workflows/tag.yml b/.github/workflows/tag.yml index 7e4e06d..4d6f9a8 100644 --- a/.github/workflows/tag.yml +++ b/.github/workflows/tag.yml @@ -58,6 +58,17 @@ jobs: echo "Previous: \`${latest}\` · bump: \`${BUMP}\` · commit: \`${GITHUB_SHA:0:7}\`" } >>"$GITHUB_STEP_SUMMARY" + - name: Check gradle.properties version + env: + VERSION: ${{ steps.next.outputs.version }} + run: | + set -euo pipefail + name=$(sed -nE 's/^optableVersionName=(.*)$/\1/p' gradle.properties) + if [[ "${name}" != "${VERSION#v}" ]]; then + echo "::error::Computed ${VERSION} but gradle.properties has optableVersionName=${name:-}. Fix the bump or the release prep PR." + exit 1 + fi + - name: Create and push tag env: VERSION: ${{ steps.next.outputs.version }} @@ -79,3 +90,16 @@ jobs: name: CI needs: tag uses: ./.github/workflows/android-sdk-ci.yml + + release: + name: GitHub release + needs: [tag, ci] + runs-on: ubuntu-24.04 + permissions: + contents: write + steps: + - name: Create release + env: + GH_TOKEN: ${{ github.token }} + VERSION: ${{ needs.tag.outputs.version }} + run: gh release create "${VERSION}" --repo "${GITHUB_REPOSITORY}" --verify-tag --title "${VERSION}" --generate-notes From a615db4747bd13ea749b363f3c7df1c1a7c28a9a Mon Sep 17 00:00:00 2001 From: mosherBT Date: Wed, 11 Nov 2026 13:35:21 -0500 Subject: [PATCH 6/6] build: take the SDK version from the JitPack tag instead of checking it in tag.yml --- .github/workflows/tag.yml | 11 ----------- android_sdk/build.gradle | 8 ++++++-- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/.github/workflows/tag.yml b/.github/workflows/tag.yml index 4d6f9a8..630cd42 100644 --- a/.github/workflows/tag.yml +++ b/.github/workflows/tag.yml @@ -58,17 +58,6 @@ jobs: echo "Previous: \`${latest}\` · bump: \`${BUMP}\` · commit: \`${GITHUB_SHA:0:7}\`" } >>"$GITHUB_STEP_SUMMARY" - - name: Check gradle.properties version - env: - VERSION: ${{ steps.next.outputs.version }} - run: | - set -euo pipefail - name=$(sed -nE 's/^optableVersionName=(.*)$/\1/p' gradle.properties) - if [[ "${name}" != "${VERSION#v}" ]]; then - echo "::error::Computed ${VERSION} but gradle.properties has optableVersionName=${name:-}. Fix the bump or the release prep PR." - exit 1 - fi - - name: Create and push tag env: VERSION: ${{ steps.next.outputs.version }} diff --git a/android_sdk/build.gradle b/android_sdk/build.gradle index e57bb55..f683e5b 100644 --- a/android_sdk/build.gradle +++ b/android_sdk/build.gradle @@ -4,6 +4,10 @@ plugins { id("org.jetbrains.kotlinx.kover") version "0.9.4" } +// JitPack builds releases from their tag and sets VERSION to it. +def jitpackVersion = System.getenv("JITPACK") == "true" ? System.getenv("VERSION")?.replaceFirst(/^v/, "") : null +def sdkVersionName = jitpackVersion ==~ /\d+\.\d+\.\d+(-.+)?/ ? jitpackVersion : optableVersionName + android { compileSdk 35 @@ -13,14 +17,14 @@ android { targetSdkVersion 35 versionCode optableVersionCode as Integer - versionName optableVersionName + versionName sdkVersionName archivesBaseName = "optable-android-sdk" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" buildConfigField "String", "VERSION_CODE", "\"${optableVersionCode}\"" - buildConfigField "String", "VERSION_NAME", "\"${optableVersionName}\"" + buildConfigField "String", "VERSION_NAME", "\"${sdkVersionName}\"" } buildTypes {