diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 83789234..be5cfa43 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -60,9 +60,35 @@ jobs: ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + # Job outputs propagate the computed release version to image-release + # so the bundled sidecar file matches what's baked into the APK — + # otherwise the server would report a different version string than + # the installed client and the update banner could thrash. + outputs: + version_name: ${{ steps.ver.outputs.name }} + version_code: ${{ steps.ver.outputs.code }} + steps: - name: Checkout uses: actions/checkout@v4 + with: + # fetch-depth: 0 retrieves full history; default shallow clone + # would return 1 for `git rev-list --count HEAD`, breaking the + # iteration suffix. + fetch-depth: 0 + + - name: Compute release version + id: ver + shell: bash + working-directory: ${{ github.workspace }} + run: | + set -euo pipefail + TAG="${GITHUB_REF#refs/tags/v}" + COMMIT_COUNT=$(git rev-list --count HEAD) + VERSION_NAME="${TAG}.${COMMIT_COUNT}" + echo "name=${VERSION_NAME}" >> "$GITHUB_OUTPUT" + echo "code=${COMMIT_COUNT}" >> "$GITHUB_OUTPUT" + echo "::notice::APK version: ${VERSION_NAME} (code=${COMMIT_COUNT})" - name: Cache Gradle dirs uses: actions/cache@v4 @@ -91,7 +117,10 @@ jobs: echo "ANDROID_KEYSTORE_PATH=${KEYSTORE_PATH}" >> "${GITHUB_ENV}" - name: Build release APK - run: ./gradlew assembleRelease + run: | + ./gradlew assembleRelease \ + -PMINSTREL_VERSION_NAME=${{ steps.ver.outputs.name }} \ + -PMINSTREL_VERSION_CODE=${{ steps.ver.outputs.code }} - name: Upload APK as workflow artifact # @v3 because Gitea Actions emulates GHES and the v2 artifact @@ -204,14 +233,18 @@ jobs: - name: Stage bundled APK + version sidecar if: steps.guard.outputs.ready == 'true' && startsWith(github.ref, 'refs/tags/v') shell: bash + env: + # Pulled from android-release.outputs.version_name so the + # sidecar string the server hands clients matches the + # versionName baked into the APK they're comparing against. + APK_VERSION_NAME: ${{ needs.android-release.outputs.version_name }} run: | set -euxo pipefail - TAG="${GITHUB_REF#refs/tags/}" # The artifact lands as `app-release.apk` (the original Gradle # output name). The Dockerfile COPYs client/* into /app/client/ # and the server reads minstrel.apk + minstrel.apk.version. mv client/app-release.apk client/minstrel.apk - echo "${TAG}" > client/minstrel.apk.version + echo "${APK_VERSION_NAME}" > client/minstrel.apk.version ls -lh client/ - name: Build and push diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 2535edf2..4e72d441 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -21,8 +21,19 @@ android { applicationId = "com.fabledsword.minstrel" minSdk = 26 targetSdk = 36 - versionCode = 1 - versionName = "0.1.0-native" + // versionName / versionCode are released-build values injected by + // CI from the git tag + commit count. Local / debug builds fall + // back to "dev" so the About card reads honestly. Releases ship + // versionName="YYYY.MM.DD." (e.g. "2026.06.02.142") and + // versionCode=, which is monotonic forever and lets the + // shared isVersionNewer comparator distinguish two same-day + // re-cuts (the iteration suffix differs). + val versionNameOverride = + (project.findProperty("MINSTREL_VERSION_NAME") as String?)?.takeIf { it.isNotBlank() } + val versionCodeOverride = + (project.findProperty("MINSTREL_VERSION_CODE") as String?)?.toIntOrNull() + versionCode = versionCodeOverride ?: 1 + versionName = versionNameOverride ?: "dev" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { useSupportLibrary = true } }