diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 36e1687..64dea33 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,8 +29,27 @@ jobs: - name: Set up Android SDK uses: android-actions/setup-android@v3 - - name: Assemble debug + - name: Decode Android Keystore + if: env.HAS_KEYSTORE == 'true' + run: echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 -d > app/release.jks + env: + HAS_KEYSTORE: ${{ secrets.ANDROID_KEYSTORE_BASE64 != '' }} + + - name: Assemble release (signed) + if: env.HAS_KEYSTORE == 'true' + run: ./gradlew assembleRelease --stacktrace + env: + HAS_KEYSTORE: ${{ secrets.ANDROID_KEYSTORE_BASE64 != '' }} + KEYSTORE_PATH: release.jks + KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} + KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} + + - name: Assemble debug (fallback when keystore absent) + if: env.HAS_KEYSTORE != 'true' run: ./gradlew assembleDebug --stacktrace + env: + HAS_KEYSTORE: ${{ secrets.ANDROID_KEYSTORE_BASE64 != '' }} - name: Unit tests + UI parity suites + design-invariant audit run: ./gradlew test --stacktrace @@ -44,11 +63,19 @@ jobs: - name: Corpus tooling syntax check run: python3 -m py_compile tools/*.py + - name: Select APK artifact + run: | + if [ -f "app/build/outputs/apk/release/app-release.apk" ]; then + echo "APK_PATH=app/build/outputs/apk/release/app-release.apk" >> "$GITHUB_ENV" + else + echo "APK_PATH=app/build/outputs/apk/debug/app-debug.apk" >> "$GITHUB_ENV" + fi + - name: Upload APK uses: actions/upload-artifact@v4 with: - name: dorado-hd-debug - path: app/build/outputs/apk/debug/app-debug.apk + name: dorado-hd-apk + path: ${{ env.APK_PATH }} publish: name: Publish APK @@ -61,6 +88,8 @@ jobs: runs-on: ubuntu-latest permissions: contents: write + id-token: write + attestations: write steps: - name: Compute short SHA @@ -69,11 +98,36 @@ jobs: - name: Download APK uses: actions/download-artifact@v4 with: - name: dorado-hd-debug + name: dorado-hd-apk path: apk - name: Name APK with commit hash - run: cp apk/app-debug.apk "dorado-hd-${SHORT_SHA}.apk" + run: | + src_apk=$(find apk -name '*.apk' | head -n1) + cp "$src_apk" "dorado-hd-${SHORT_SHA}.apk" + + - name: Generate Checksums + run: sha256sum dorado-hd-${SHORT_SHA}.apk > SHA256SUMS + + - name: Import GPG Key + if: env.HAS_GPG == 'true' + uses: crazy-max/ghaction-import-gpg@v6 + with: + gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.GPG_PASSPHRASE }} + env: + HAS_GPG: ${{ secrets.GPG_PRIVATE_KEY != '' }} + + - name: Sign Checksums + if: env.HAS_GPG == 'true' + run: gpg --batch --yes --detach-sign --armor SHA256SUMS + + - name: Attest Build Provenance + uses: actions/attest-build-provenance@v2 + with: + subject-path: | + dorado-hd-*.apk + SHA256SUMS* # Each main-branch commit is published as a prerelease tagged with the # short commit hash for a stable per-commit download URL. @@ -84,7 +138,9 @@ jobs: name: dorado-hd build ${{ env.SHORT_SHA }} prerelease: true generate_release_notes: true - files: dorado-hd-${{ env.SHORT_SHA }}.apk + files: | + dorado-hd-${{ env.SHORT_SHA }}.apk + SHA256SUMS* env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d60e22e..3d987f5 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -16,6 +16,24 @@ android { versionName = "0.1.0" } + val keystorePath = System.getenv("KEYSTORE_PATH") ?: project.findProperty("KEYSTORE_PATH") as? String + val keystorePassword = System.getenv("KEYSTORE_PASSWORD") ?: project.findProperty("KEYSTORE_PASSWORD") as? String + val keyAlias = System.getenv("KEY_ALIAS") ?: project.findProperty("KEY_ALIAS") as? String + val keyPassword = System.getenv("KEY_PASSWORD") ?: project.findProperty("KEY_PASSWORD") as? String + + val releaseSigningConfig = if (keystorePath != null && file(keystorePath).exists() && keystorePassword != null && keyAlias != null && keyPassword != null) { + signingConfigs.create("release") { + storeFile = file(keystorePath) + storePassword = keystorePassword + this.keyAlias = keyAlias + this.keyPassword = keyPassword + enableV1Signing = true + enableV2Signing = true + } + } else { + null + } + buildTypes { release { isMinifyEnabled = true @@ -24,6 +42,9 @@ android { getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) + if (releaseSigningConfig != null) { + signingConfig = releaseSigningConfig + } } debug { applicationIdSuffix = ".debug"