ci(android): add PR-time validation path for the Android Gradle build #280

Open
opened 2026-06-24 13:50:55 +00:00 by james · 0 comments
Owner

Problem

.forgejo/workflows/release-android.yml runs only on tag push (v*.*.*). That means any change to the Android toolchain surface — gradle wrapper, apps/client/plugins/with-release-signing.js, apps/client/package.json, patches/ against react-native or expo-modules, app.json config plugins, Expo SDK bumps, JDK pin in the workflow — ships unverified by CI until somebody cuts a release tag.

We just hit this in #278: foojay-resolver 0.5.0 vs Gradle 9 was a tag-only failure that nobody saw on the merge commit, only when the release pipeline ran.

Proposal

Add a PR-time job that exercises at least the Gradle configuration phase for the Android build, scoped so it only runs when something Android-shaped changes.

Shape sketch:

# .forgejo/workflows/pr.yml (or a new pr-android.yml)
on:
  pull_request:
    paths:
      - 'apps/client/package.json'
      - 'apps/client/app.json'
      - 'apps/client/plugins/**'
      - 'apps/client/scripts/build-android.sh'
      - 'patches/**'
      - '.forgejo/workflows/release-android.yml'
      - '.tool-versions'
      - 'pnpm-lock.yaml'

jobs:
  android-config:
    # Same container + JDK + Android SDK install as release-android.yml.
    # Run `pnpm -F @carol/client prebuild:android` then
    # `cd apps/client/android && ./gradlew --no-daemon help` —
    # `help` is the cheapest task that still forces full config-phase
    # evaluation (settings.gradle, includeBuilds, all build.gradle
    # files). No assembleDebug — that pulls in NDK + Hermes + the
    # full dependency graph and would push the PR job past 10 minutes.

What this catches:

  • foojay/IBM_SEMERU-class config-phase blowups (#278)
  • broken with-release-signing.js plugin output
  • app.json plugin config that breaks prebuild
  • patch files that no longer apply after a transitive bump
  • workflow file edits to release-android.yml itself (steps, env vars)

What this doesn't catch (and is fine to skip):

  • NDK / Hermes compile errors — those only show up in assembleDebug/Release and are uncommon. Real release tag still validates.
  • Signing — keystore secrets aren't available on PR runs, so we'd stay in the signed != 'true' fallback branch anyway.

Considerations

  • Path filter is load-bearing. The full job is 5-10 minutes on a self-hosted runner (cmdline-tools download dominates). Don't run it on every PR — gate via paths: so the API-only PRs still see fast CI.
  • Cache the Android SDK + Gradle dists. First run will be slow; subsequent runs should hit the cache. Same cache shape release-android.yml could be refactored to share.
  • Refactor release-android.yml to share the install steps. Either a composite action under .forgejo/actions/setup-android or a reusable workflow workflow_call. Avoids drift between the PR job and the release job.
  • Pin actions by SHA with trailing tag comment, matching repo convention.

Acceptance

  • A PR that breaks the Android Gradle configuration phase (e.g. revert of #278) fails CI before merge.
  • A PR that only touches apps/api/ or apps/client/app/ (non-Android paths) does not trigger the Android job — fast PR remains fast.
  • The PR job and the release job share their setup steps (no copy-paste drift).
## Problem `.forgejo/workflows/release-android.yml` runs **only on tag push** (`v*.*.*`). That means any change to the Android toolchain surface — gradle wrapper, `apps/client/plugins/with-release-signing.js`, `apps/client/package.json`, `patches/` against react-native or expo-modules, `app.json` config plugins, Expo SDK bumps, JDK pin in the workflow — ships unverified by CI until somebody cuts a release tag. We just hit this in #278: foojay-resolver 0.5.0 vs Gradle 9 was a tag-only failure that nobody saw on the merge commit, only when the release pipeline ran. ## Proposal Add a PR-time job that exercises **at least the Gradle configuration phase** for the Android build, scoped so it only runs when something Android-shaped changes. Shape sketch: ```yaml # .forgejo/workflows/pr.yml (or a new pr-android.yml) on: pull_request: paths: - 'apps/client/package.json' - 'apps/client/app.json' - 'apps/client/plugins/**' - 'apps/client/scripts/build-android.sh' - 'patches/**' - '.forgejo/workflows/release-android.yml' - '.tool-versions' - 'pnpm-lock.yaml' jobs: android-config: # Same container + JDK + Android SDK install as release-android.yml. # Run `pnpm -F @carol/client prebuild:android` then # `cd apps/client/android && ./gradlew --no-daemon help` — # `help` is the cheapest task that still forces full config-phase # evaluation (settings.gradle, includeBuilds, all build.gradle # files). No assembleDebug — that pulls in NDK + Hermes + the # full dependency graph and would push the PR job past 10 minutes. ``` What this catches: - foojay/IBM_SEMERU-class config-phase blowups (#278) - broken `with-release-signing.js` plugin output - `app.json` plugin config that breaks prebuild - patch files that no longer apply after a transitive bump - workflow file edits to `release-android.yml` itself (steps, env vars) What this **doesn't** catch (and is fine to skip): - NDK / Hermes compile errors — those only show up in `assembleDebug`/`Release` and are uncommon. Real release tag still validates. - Signing — keystore secrets aren't available on PR runs, so we'd stay in the `signed != 'true'` fallback branch anyway. ## Considerations - **Path filter is load-bearing.** The full job is 5-10 minutes on a self-hosted runner (cmdline-tools download dominates). Don't run it on every PR — gate via `paths:` so the API-only PRs still see fast CI. - **Cache the Android SDK + Gradle dists.** First run will be slow; subsequent runs should hit the cache. Same cache shape `release-android.yml` could be refactored to share. - **Refactor release-android.yml to share the install steps.** Either a composite action under `.forgejo/actions/setup-android` or a reusable workflow `workflow_call`. Avoids drift between the PR job and the release job. - **Pin actions by SHA** with trailing tag comment, matching repo convention. ## Acceptance - A PR that breaks the Android Gradle configuration phase (e.g. revert of #278) fails CI before merge. - A PR that only touches `apps/api/` or `apps/client/app/` (non-Android paths) does not trigger the Android job — fast PR remains fast. - The PR job and the release job share their setup steps (no copy-paste drift).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
james/carol#280
No description provided.