Step 1 of M8 (docs/android/PLAN.md item 9).
- compileSdk/targetSdk 36 (Play's floor since 2026-08-31). AGP 8.6.1
cannot build against 36, so AGP goes to 8.13.2 (the last 8.x) with the
Gradle 8.14.3 wrapper. Kotlin, KSP and Hilt are unchanged. All three
workflows install platforms;android-36; AGP 8.13 still defaults to
build-tools 35.0.0.
- release.yml also runs bundleRelease, signed with the same keystore,
and attaches the .aab to the Gitea release beside the APK (the first
bundle has to be uploaded by hand, and this is where it comes from).
- A last step uploads the bundle to Play's internal track through the
Play Developer API v3 edit flow. The OAuth token comes from a
service-account JWT signed with openssl; the key is the new
PLAY_SERVICE_ACCOUNT_JSON secret. While that secret is unset, the
step warns and skips. The step runs after the Gitea release, so a
Play failure never holds back the APK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
**Unrelated to this PR's feature work**, and fixed here because it blocks
verifying it (org lead, 2026-09-17). PR #46 passed on this workflow yesterday;
every Android PR fails now.
`android-actions/setup-android@v3` is a floating tag and the action's `packages`
input defaults to `tools` — an obsolete package Google has since removed from the
SDK repository. So the step runs `sdkmanager tools`, gets `Warning: Failed to
find package 'tools'`, exits 1, and CI fails in **Set up Android SDK**, before a
line of this repo is compiled.
`packages: ''` turns that install off. It was always redundant here: the very
next step installs exactly what the build targets — `platform-tools`,
`platforms;android-35`, `build-tools;35.0.0` — precisely so the build never
depends on what some action decided to fetch.
Not addressed here, and worth its own decision: `@v3` is a floating major tag, so
the next upstream change can break CI the same way without warning.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
ENGAGEMENT.md §7.1 Q8. `pr-checks.yml` triggered only on PRs into `main`, so a
workstream that lands its phases on `edge` before one cutover PR got no CI at
all until the cutover — all nine M12 phase PRs merged without a single run, and
engagement Phase 8 was about to do the same. A phase should fail on its own PR.
Sonar is untouched: `sonarqube.yml` is a push-on-`main` analysis, not a PR gate,
so no phase PR was ever expected to run it.
Co-Authored-By: Claude <noreply@anthropic.com>
The M1 PR-checks run hung ~16 min in `lintReportDebug` and was killed by
the runner (33m35s, marked failure) — every compile/test/assemble task
completed first; no task FAILED. Android lint's report phase needs more
than the 2 GB heap and GC-thrashes to a hang below it on the full app
codebase (it passed at 2 GB only while the M0 scaffold was trivial).
- gradle.properties: -Xmx2048m → -Xmx3g, cap MaxMetaspaceSize=1g so the
larger heap doesn't crowd container RAM.
- pr-checks.yml: run `testDebugUnitTest lintDebug assembleDebug` instead
of the aggregate `test lint assembleDebug`, so the release variant
isn't compiled+linted in parallel — halving peak memory and build time
while keeping the same coverage (unit tests are variant-agnostic).
Verified locally with the exact command (`--no-daemon`); lintReportDebug
+ lintDebug run and pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NgyHnrNa8WwG3doxvxjuCr
The SDK install succeeds (reaches 100% unzip) but the step reported exit
141: with -o pipefail, 'yes' dying of SIGPIPE (128+13) once sdkmanager
closes the pipe poisons the pipeline status. Disable pipefail for that line
so only sdkmanager's exit gates success.
Co-Authored-By: Claude <noreply@anthropic.com>
PR #2 merged the M0 scaffold with the initial CI, which fails on this
self-hosted runner. Fix forward:
- JDK 17 via apt (openjdk-17-jdk-headless) + JAVA_HOME, replacing
actions/setup-java: the runner can't resolve api.adoptium.net (EAI_AGAIN).
- Explicit `sdkmanager` install of platform-tools + platforms;android-35 +
build-tools;35.0.0 so the build never relies on AGP auto-download.
- `chmod +x ./gradlew` in the run step (and set the 100755 git bit): this
runner's checkout drops file modes, so `./gradlew` alone hit
"Permission denied" (exit 126).
- ScaffoldSanityTest: drop the BuildConfig.DEBUG assertion, which fails under
testReleaseUnitTest (the `test` task runs both variants); assert VERSION_NAME.
- Remove the redundant android:label on MainActivity (RedundantLabel lint).
Verified locally against the Android Studio SDK (platform 35 + build-tools
35.0.0): `./gradlew lint test assembleDebug` passes and builds a debug APK.
Co-Authored-By: Claude <noreply@anthropic.com>
Stand up the Android-app repo per docs/android/PLAN.md M0: a buildable
Kotlin + Jetpack Compose (Material 3) single-activity skeleton wired for
Hilt, ready for the M1-M4 functional pass.
- Gradle 8.7 wrapper; AGP 8.6.1 / Kotlin 2.0.20, JDK 17, minSdk 29, target 35.
- Version catalog (gradle/libs.versions.toml) pins the full planned stack
(Compose, Hilt, Retrofit/OkHttp + kotlinx.serialization, DataStore,
security-crypto, Coil, Navigation) so later milestones reference by alias.
- RunicGatewayApp (@HiltAndroidApp) + MainActivity (Compose) + ui/theme/*.
- Strings externalized from day one; adaptive launcher icon; backup rules
exclude the token store / DataStore (no session material off-device).
- CI: .gitea/workflows/pr-checks.yml gates PRs with lint + test + assembleDebug
(JDK 17 + Android SDK on the self-hosted runner; debug builds auto-signed,
no secrets). Placeholder JVM unit test so the test gate runs.
- .gitattributes forces LF on gradlew so the wrapper runs on the Linux runner.
Verified locally: `gradle help`/`projects` configure the :app module and
resolve all six plugins cleanly (full assemble needs the Android SDK, done in CI).
app id: com.runicgateway.app (PLAN.md §13, pending runicgateway.app domain).
Co-Authored-By: Claude <noreply@anthropic.com>