From 0b9c9dbfb7cfc9c734859b90c6d7a5345fcfad2f Mon Sep 17 00:00:00 2001 From: Lennoard Date: Mon, 17 Aug 2026 20:22:15 -0300 Subject: [PATCH] build: improve release signing and automation Signed-off-by: Lennoard --- .github/workflows/ci.yml | 2 +- .github/workflows/release-apk.yml | 69 ++++++++++++++++++++++++------- app/build.gradle.kts | 46 +++++++++++++++++---- common/design/build.gradle.kts | 11 ++--- common/utils/build.gradle.kts | 2 +- 5 files changed, 96 insertions(+), 34 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d371186..0681002 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Temurin JDK 21 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: '21' diff --git a/.github/workflows/release-apk.yml b/.github/workflows/release-apk.yml index a73bac9..00c8e9c 100644 --- a/.github/workflows/release-apk.yml +++ b/.github/workflows/release-apk.yml @@ -5,13 +5,18 @@ on: tags: - 'v*' workflow_dispatch: + inputs: + version: + description: 'Release tag name, e.g. v3.2.0' + required: true + type: string jobs: build-release: - name: Build and publish unsigned APK + name: Build and publish signed APK runs-on: ubuntu-24.04 env: - TAG: ${{ github.ref_name }} + TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.version || github.ref_name }} REPO: ${{ github.repository }} steps: - name: Checkout @@ -20,23 +25,45 @@ jobs: fetch-depth: 0 - name: Set up Temurin JDK 21 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: '21' cache: 'gradle' - - name: Set up Android SDK (platform + build-tools) - uses: r0adkll/setup-android@v2 - with: - api-level: 36 - build-tools: '36.0.0' + - name: Set up Android SDK + uses: android-actions/setup-android@v3 + + - name: Install Android platform and build-tools + run: | + yes | "$ANDROID_HOME"/cmdline-tools/latest/bin/sdkmanager --licenses + "$ANDROID_HOME"/cmdline-tools/latest/bin/sdkmanager --install "platforms;android-36" "build-tools;36.0.0" + + - name: Prepare release signing config + env: + KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }} + KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.KEY_ALIAS }} + KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} + run: | + if [ -n "$KEYSTORE_BASE64" ] && [ -n "$KEYSTORE_PASSWORD" ] && [ -n "$KEY_ALIAS" ] && [ -n "$KEY_PASSWORD" ]; then + echo "$KEYSTORE_BASE64" | base64 --decode > "$GITHUB_WORKSPACE/release-keystore.jks" + echo "KEYSTORE_PATH=$GITHUB_WORKSPACE/release-keystore.jks" >> "$GITHUB_ENV" + echo "KEYSTORE_PASSWORD=$KEYSTORE_PASSWORD" >> "$GITHUB_ENV" + echo "KEY_ALIAS=$KEY_ALIAS" >> "$GITHUB_ENV" + echo "KEY_PASSWORD=$KEY_PASSWORD" >> "$GITHUB_ENV" + fi - name: Grant execute permission for gradlew run: chmod +x ./gradlew - - name: Assemble release APK (unsigned) - run: ./gradlew :app:assembleRelease --no-daemon -x validateSigningRelease + - name: Assemble signed release APK + env: + KEYSTORE_PATH: ${{ env.KEYSTORE_PATH }} + KEYSTORE_PASSWORD: ${{ env.KEYSTORE_PASSWORD }} + KEY_ALIAS: ${{ env.KEY_ALIAS }} + KEY_PASSWORD: ${{ env.KEY_PASSWORD }} + run: ./gradlew :app:assembleRelease --no-daemon - name: Find release APK id: find_apk @@ -61,8 +88,8 @@ jobs: git fetch --tags TAG=${{ env.TAG }} REPO=${{ env.REPO }} - PREV_TAG=$(git tag --sort=-v:refname | sed -n '2p' || true) - CHANGELOG_FILE=$(ls -t fastlane/metadata/android/en-US/changelogs/*.txt 2>/dev/null | head -n1 || true) + PREV_TAG=$(git tag --sort=-v:refname | grep -v "^${TAG}$" | head -n 1 || true) + CHANGELOG_FILE=$(find fastlane/metadata/android/en-US/changelogs -maxdepth 1 -type f -name '*.txt' 2>/dev/null | sort -V | tail -n 1 || true) CHANGELOG="" if [ -n "$CHANGELOG_FILE" ]; then CHANGELOG=$(cat "$CHANGELOG_FILE"); fi if [ -n "$PREV_TAG" ]; then @@ -70,10 +97,20 @@ jobs: else COMP="" fi - BODY="[${TAG}](https://github.com/${REPO}/releases/tag/${TAG}) [Latest](https://github.com/${REPO}/releases/latest)\n\n## What's Changed\n\n${CHANGELOG}\n\n**Full Changelog**: ${COMP}" - echo "release_body<> $GITHUB_OUTPUT - echo "$BODY" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT + BODY=$(printf '%s\n' \ + "[${TAG}](https://github.com/${REPO}/releases/tag/${TAG}) [Latest](https://github.com/${REPO}/releases/latest)" \ + "" \ + "## What's Changed" \ + "" \ + "${CHANGELOG}" \ + "" \ + "**Full Changelog**: ${COMP}") + echo "release_body<> "$GITHUB_OUTPUT" + printf '%s\n' "$BODY" >> "$GITHUB_OUTPUT" + echo "EOF" >> "$GITHUB_OUTPUT" + echo "--- GENERATED RELEASE BODY ---" + printf '%s\n' "$BODY" + echo "--- END RELEASE BODY ---" - name: Create GitHub Release id: create_release diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4507185..6964a86 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -36,6 +36,19 @@ android { signingConfigs { create("release") { + val envStorePath = System.getenv("KEYSTORE_PATH") + val envStorePassword = System.getenv("KEYSTORE_PASSWORD") + val envKeyAlias = System.getenv("KEY_ALIAS") + val envKeyPassword = System.getenv("KEY_PASSWORD") + + if (!envStorePath.isNullOrBlank()) { + storeFile = file(envStorePath) + storePassword = envStorePassword ?: "" + keyAlias = envKeyAlias ?: "" + keyPassword = envKeyPassword ?: "" + return@create + } + val propFile = rootProject.file("keystore.properties") if (!propFile.exists()) { propFile.createNewFile() @@ -44,19 +57,36 @@ android { load(propFile.inputStream()) } - keyAlias = keystoreProps["keyAlias"] as? String ?: "" - keyPassword = keystoreProps["keyPassword"] as? String ?: "" - storeFile = file(keystoreProps["storeFile"] as? String ?: "/") - storePassword = keystoreProps["storePassword"] as? String ?: "" + val storeFilePath = keystoreProps["storeFile"] as? String + val keyAliasValue = keystoreProps["keyAlias"] as? String + val keyPasswordValue = keystoreProps["keyPassword"] as? String + val storePasswordValue = keystoreProps["storePassword"] as? String + + keyAlias = keyAliasValue ?: "" + keyPassword = keyPasswordValue ?: "" + storeFile = if (!storeFilePath.isNullOrBlank()) file(storeFilePath) else file("/dev/null") + storePassword = storePasswordValue ?: "" } } buildTypes { getByName("release") { - isMinifyEnabled = !AppConfig.devCycle - isShrinkResources = !AppConfig.devCycle - isDebuggable = AppConfig.devCycle - signingConfig = signingConfigs.getByName("release") + isMinifyEnabled = true + isShrinkResources = true + isDebuggable = false + val envEnabled = !System.getenv("KEYSTORE_PATH").isNullOrBlank() && + !System.getenv("KEYSTORE_PASSWORD").isNullOrBlank() && + !System.getenv("KEY_ALIAS").isNullOrBlank() && + !System.getenv("KEY_PASSWORD").isNullOrBlank() + val propFile = rootProject.file("keystore.properties") + val keyStoreExists = propFile.exists() && propFile.readText().contains("storeFile") + val hasSigningValues = keyStoreExists && propFile.readText().contains("keyAlias") && + propFile.readText().contains("storePassword") + signingConfig = if (envEnabled || hasSigningValues) { + signingConfigs.getByName("release") + } else { + signingConfigs.getByName("debug") + } proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" diff --git a/common/design/build.gradle.kts b/common/design/build.gradle.kts index e691bb8..42a89cc 100644 --- a/common/design/build.gradle.kts +++ b/common/design/build.gradle.kts @@ -16,7 +16,7 @@ android { buildTypes { release { - isMinifyEnabled = !AppConfig.devCycle + isMinifyEnabled = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" @@ -58,11 +58,6 @@ dependencies { androidTestImplementation(platform(libs.androidx.compose.bom)) - if (AppConfig.devCycle) { - api(libs.androidx.compose.ui.tooling) - api(libs.androidx.ui.test.manifest) - } else { - debugApi(libs.androidx.compose.ui.tooling) - debugApi(libs.androidx.ui.test.manifest) - } + debugApi(libs.androidx.compose.ui.tooling) + debugApi(libs.androidx.ui.test.manifest) } diff --git a/common/utils/build.gradle.kts b/common/utils/build.gradle.kts index ffe03ab..796529c 100644 --- a/common/utils/build.gradle.kts +++ b/common/utils/build.gradle.kts @@ -15,7 +15,7 @@ android { buildTypes { getByName("release") { - isMinifyEnabled = !AppConfig.devCycle + isMinifyEnabled = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"