diff --git a/.github/workflows/android-release.yml b/.github/workflows/android-release.yml index bd6eebb..c84d392 100644 --- a/.github/workflows/android-release.yml +++ b/.github/workflows/android-release.yml @@ -29,11 +29,24 @@ jobs: run: | set -euo pipefail if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - echo "value=${{ github.event.inputs.version }}" >> "$GITHUB_OUTPUT" + VERSION="${{ github.event.inputs.version }}" else - echo "value=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT" + VERSION="${GITHUB_REF_NAME}" fi + if ! [[ "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "::error::Release version must look like v0.1.0; got '$VERSION'." + exit 1 + fi + + VERSION_NAME="${VERSION#v}" + IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION_NAME" + VERSION_CODE=$((MAJOR * 10000 + MINOR * 100 + PATCH)) + + echo "value=$VERSION" >> "$GITHUB_OUTPUT" + echo "name=$VERSION_NAME" >> "$GITHUB_OUTPUT" + echo "code=$VERSION_CODE" >> "$GITHUB_OUTPUT" + - name: Create and push release tag if: github.event_name == 'workflow_dispatch' env: @@ -62,22 +75,39 @@ jobs: - name: Decode release keystore env: RELEASE_KEYSTORE_BASE64: ${{ secrets.RELEASE_KEYSTORE_BASE64 }} + RELEASE_KEYSTORE_PASSWORD: ${{ secrets.RELEASE_KEYSTORE_PASSWORD }} + RELEASE_KEY_ALIAS: ${{ secrets.RELEASE_KEY_ALIAS }} + RELEASE_KEY_PASSWORD: ${{ secrets.RELEASE_KEY_PASSWORD }} run: | set -euo pipefail - if [ -n "${RELEASE_KEYSTORE_BASE64:-}" ]; then - echo "$RELEASE_KEYSTORE_BASE64" | base64 -d > "$RUNNER_TEMP/release.keystore" - echo "RELEASE_KEYSTORE_PATH=$RUNNER_TEMP/release.keystore" >> "$GITHUB_ENV" - else - echo "::warning::RELEASE_KEYSTORE_BASE64 secret not set; release APK will be debug-signed." + missing=0 + for name in RELEASE_KEYSTORE_BASE64 RELEASE_KEYSTORE_PASSWORD RELEASE_KEY_ALIAS RELEASE_KEY_PASSWORD; do + if [ -z "${!name:-}" ]; then + echo "::error::$name is required for installable release APKs." + missing=1 + fi + done + if [ "$missing" -ne 0 ]; then + exit 1 fi + echo "$RELEASE_KEYSTORE_BASE64" | base64 -d > "$RUNNER_TEMP/release.keystore" + echo "RELEASE_KEYSTORE_PATH=$RUNNER_TEMP/release.keystore" >> "$GITHUB_ENV" - name: Build release APK env: RELEASE_KEYSTORE_PASSWORD: ${{ secrets.RELEASE_KEYSTORE_PASSWORD }} RELEASE_KEY_ALIAS: ${{ secrets.RELEASE_KEY_ALIAS }} RELEASE_KEY_PASSWORD: ${{ secrets.RELEASE_KEY_PASSWORD }} + RELEASE_VERSION_NAME: ${{ steps.version.outputs.name }} + RELEASE_VERSION_CODE: ${{ steps.version.outputs.code }} run: ./gradlew :app:assembleRelease --stacktrace + - name: Verify release APK signing + run: | + set -euo pipefail + APK_PATH="$(find app/build/outputs/apk/release -name '*.apk' | head -n 1)" + apksigner verify --verbose --print-certs "$APK_PATH" + - name: Prepare release APK env: VERSION: ${{ steps.version.outputs.value }} diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7f834b6..bdb37ad 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -6,6 +6,24 @@ plugins { alias(libs.plugins.ksp) } +fun resolvedVersionName(): String = + providers.environmentVariable("RELEASE_VERSION_NAME") + .orElse(providers.environmentVariable("GITHUB_REF_NAME").map { it.removePrefix("v") }) + .orElse("0.1.0") + .get() + +fun resolvedVersionCode(): Int = + providers.environmentVariable("RELEASE_VERSION_CODE") + .map(String::toInt) + .orElse( + providers.environmentVariable("GITHUB_REF_NAME").map { tag -> + val parts = tag.removePrefix("v").split(".").mapNotNull(String::toIntOrNull) + if (parts.size == 3) parts[0] * 10_000 + parts[1] * 100 + parts[2] else 1 + } + ) + .orElse(1) + .get() + android { namespace = "com.sploot.app" compileSdk = 36 @@ -14,8 +32,8 @@ android { applicationId = "com.sploot.app" minSdk = 26 targetSdk = 35 - versionCode = 1 - versionName = "0.1.0" + versionCode = resolvedVersionCode() + versionName = resolvedVersionName() testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } @@ -38,8 +56,6 @@ android { getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) - // Falls back to debug signing for local builds where the release - // keystore env vars aren't set, e.g. outside of the release CI job. signingConfig = signingConfigs.findByName("release") ?: signingConfigs.getByName("debug") }