Skip to content

ci: add a signed release-APK workflow#5

Merged
1a1a11a merged 2 commits into
mainfrom
claude/gallant-fermat-xau2al
Jun 17, 2026
Merged

ci: add a signed release-APK workflow#5
1a1a11a merged 2 commits into
mainfrom
claude/gallant-fermat-xau2al

Conversation

@1a1a11a

@1a1a11a 1a1a11a commented Jun 17, 2026

Copy link
Copy Markdown
Member

Summary

Adds a CI pipeline that builds a signed release APK and publishes it, so a release artifact can be produced on demand. (The APK is built by CI — it needs the Android SDK, which isn't in the dev sandbox.)

Changes

  • .github/workflows/release.yml — builds :app:assembleRelease, signs it, and:
    • on a v* tag push → publishes a GitHub Release with the APK attached;
    • via manual dispatch → uploads the signed APK as a workflow artifact.
  • app/build.gradle.kts — release buildType now uses an env-driven signing config (KEYSTORE_FILE/KEYSTORE_PASSWORD/KEY_ALIAS/KEY_PASSWORD). When those are unset (e.g. a local assembleDebug), no signing config is attached, so debug builds are unaffected.
  • docs/ANDROID_APP.md — a "Releases" section documenting the tag/dispatch flow, keystore-secret setup, and a local signed-build command.

Signing strategy

  • If the repo defines KEYSTORE_BASE64 + KEYSTORE_PASSWORD + KEY_ALIAS + KEY_PASSWORD secrets → signs with that keystore (stable signature; updates install in place).
  • Otherwise → generates an ephemeral keystore so the build still yields an installable, signed APK (signature changes per run, so those builds can't update a prior install — reinstall instead).

Notes

  • Release builds use the same code as debug (isMinifyEnabled = false); only signing differs. The app still requires root at runtime.
  • The existing Android CI will run on this PR (build.gradle changed) and validates that the signing-config change doesn't break the debug build.

Producing the APK

After merge, push a tag (e.g. git tag v0.1.0 && git push origin v0.1.0) to generate the signed APK as a GitHub Release — I can do that step on your go-ahead.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VbHpkJ1MdVfGz9bi6Ekb1i


Generated by Claude Code

Add .github/workflows/release.yml that builds a signed release APK and attaches
it to a GitHub Release on a v* tag (or uploads it as an artifact via manual
dispatch). Signing uses the KEYSTORE_BASE64/PASSWORD/ALIAS secrets when present
(stable signature for in-place updates), else generates an ephemeral keystore so
the build still yields an installable, signed APK.

Wire app/build.gradle.kts release buildType to an env-driven signing config
(KEYSTORE_FILE/PASSWORD, KEY_ALIAS/PASSWORD); when unset (e.g. local
assembleDebug) no signing config is attached. Document the release flow in
docs/ANDROID_APP.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VbHpkJ1MdVfGz9bi6Ekb1i
Copilot AI review requested due to automatic review settings June 17, 2026 02:45

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces release signing configuration to the Android application build setup, driven by environment variables, and adds comprehensive documentation on how to build and sign release APKs. The review feedback suggests improving the robustness of the Gradle build script by validating all required signing environment variables and resolving the keystore path relative to the root project. Additionally, it recommends using a portable base64 command in the documentation to ensure compatibility across both Linux and macOS environments.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread app/build.gradle.kts Outdated
Comment on lines +21 to +42
val keystoreFile = System.getenv("KEYSTORE_FILE")
signingConfigs {
create("release") {
if (keystoreFile != null) {
storeFile = file(keystoreFile)
storePassword = System.getenv("KEYSTORE_PASSWORD")
keyAlias = System.getenv("KEY_ALIAS")
keyPassword = System.getenv("KEY_PASSWORD")
}
}
}

buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
if (keystoreFile != null) {
signingConfig = signingConfigs.getByName("release")
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using file(keystoreFile) resolves relative paths relative to the subproject directory (app/), which can cause build failures if the keystore is placed in the root directory (a common pattern in CI workflows and local builds). Additionally, checking only keystoreFile != null can lead to cryptic build failures if other required environment variables (like KEYSTORE_PASSWORD) are missing or empty.

We can make this robust by:

  1. Validating that all required environment variables are present and non-empty.
  2. Resolving the keystore path relative to the root project using rootProject.file(...) so both relative and absolute paths work seamlessly.
  3. Conditionally creating and assigning the release signing configuration only when all signing environment variables are fully configured.
    val envKeystoreFile = System.getenv("KEYSTORE_FILE")
    val envKeystorePassword = System.getenv("KEYSTORE_PASSWORD")
    val envKeyAlias = System.getenv("KEY_ALIAS")
    val envKeyPassword = System.getenv("KEY_PASSWORD")

    val hasSigningEnv = !envKeystoreFile.isNullOrEmpty() &&
            !envKeystorePassword.isNullOrEmpty() &&
            !envKeyAlias.isNullOrEmpty() &&
            !envKeyPassword.isNullOrEmpty()

    if (hasSigningEnv) {
        signingConfigs {
            create("release") {
                storeFile = rootProject.file(envKeystoreFile)
                storePassword = envKeystorePassword
                keyAlias = envKeyAlias
                keyPassword = envKeyPassword
            }
        }
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro",
            )
            if (hasSigningEnv) {
                signingConfig = signingConfigs.getByName("release")
            }

Comment thread docs/ANDROID_APP.md Outdated
```bash
keytool -genkeypair -v -keystore release.jks -alias iotracer \
-keyalg RSA -keysize 2048 -validity 10000
base64 -w0 release.jks # paste into the KEYSTORE_BASE64 repo secret

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The -w0 flag is specific to GNU base64 (Linux) and will fail on macOS with base64: invalid option -- w.

Using base64 release.jks | tr -d '\n' is fully portable and works seamlessly on both Linux and macOS to produce a single-line base64 string.

Suggested change
base64 -w0 release.jks # paste into the KEYSTORE_BASE64 repo secret
base64 release.jks | tr -d '\n' # paste into the KEYSTORE_BASE64 repo secret

Address PR #5 review:
- Gate release signing on all four env vars being present and non-empty
  (isNullOrEmpty, not just != null), and resolve the keystore path with
  rootProject.file so relative paths work from the repo root, not app/.
- Keep a loud failure when KEYSTORE_FILE is set but the rest are missing, so a
  half-configured keystore errors instead of silently shipping an unsigned
  release.
- Docs: use `base64 release.jks | tr -d '\n'` (portable; macOS base64 has no -w0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VbHpkJ1MdVfGz9bi6Ekb1i
@1a1a11a 1a1a11a merged commit 1e4b3e6 into main Jun 17, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants