Skip to content

ci: add Gradle build + unit test workflow (hard gates), advisory lint report #99

Description

@stozo04

Carved out of #98 (Phase 3, item 13) so the foundation lands as its own small, reviewable PR. Everything else in #98's Phase 3 builds on top of this.

What

Add the repo's first Gradle build/test workflow to GitHub Actions: on every PR and push to main, build the app, run the unit test suite, and produce the lint report. Build + unit tests are hard gates; lint stays advisory per the repo's documented tier design.

Why

There is currently no Gradle workflow in .github/workflows/ (only static-analysis.yml for Markdown, doc-layout.yml, and crashlytics-autotriage.yml). The unit test suite exists and is meaningful (Robolectric + JUnit under app/src/test/), but nothing runs it automatically — a PR that breaks the build or the tests shows green. Same argument as stozo04/stevengates#31: a repo whose README claims Google-grade engineering discipline needs a green check on every commit proving the basics.

Decisions already made by Steven (2026-07-07, in-session) — do not re-litigate

  • google-services.json in CI: use the REAL file via a repo secret. No dummy/placeholder config. The CI build must be valid and match what ships. Note for the implementer: the build script gracefully skips the Google Services + Crashlytics plugins when the file is absent (app/build.gradle.kts ~line 313: if (file("google-services.json").exists())), so a secretless build would technically pass — that path is NOT acceptable for CI per this decision, because it silently builds a non-production variant. The workflow must materialize the real config from the secret and fail loudly if the secret is missing/empty.
  • Related feat(quality): Make performance and accessibility a demonstrated feature — Android Vitals green, a11y enforced in CI #98 decision recorded for context: D3 (Play Developer Reporting API credentials for the vitals watchdog) is APPROVED — not part of this issue, but the same service-account setup session can cover both secrets.

How

  1. Steven (blocking prerequisite): add the repo secret — from a machine with the real config:
    gh secret set GOOGLE_SERVICES_JSON --repo stozo04/OpenLoop < app/google-services.json
    (The local working copy at the time of writing did NOT contain app/google-services.json — it may need to be re-downloaded from Firebase console → Project settings → your apps. See app/google-services.json.README.)
  2. Workflow .github/workflows/android-ci.yml:
    • Triggers: pull_request, push: branches: [main], plus workflow_dispatch (matches the repo's existing convention in static-analysis.yml).
    • actions/checkoutactions/setup-java with temurin JDK 17 (the project pins Java 17: app/build.gradle.ktsjvmToolchain(17), sourceCompatibility = JavaVersion.VERSION_17, with a comment explaining JDK 21 is deliberately avoided; do NOT use 21) → gradle/actions/setup-gradle for dependency + build caching.
    • Materialize the config: write ${{ secrets.GOOGLE_SERVICES_JSON }} to app/google-services.json; fail with a clear message if empty. Mind fork PRs: secrets are unavailable to PRs from forks — the step should fail with an explanatory message rather than silently building without Crashlytics (repo currently takes PRs from branches, so this is an edge case to document in a workflow comment, not solve).
    • ./gradlew :app:assembleDebug :app:testDebugUnitTest :app:lintDebug --stacktrace
    • Upload artifacts: unit test results (app/build/reports/tests/) and the lint report (app/build/reports/lint-results-debug.*). Lint must NOT fail the job — abortOnError = false is already set in app/build.gradle.kts with the rationale "the SKILL decides the PR verdict, not the build"; the workflow surfaces the report for the pr-reviewer skill, nothing more.
    • A concurrency group keyed on the ref with cancel-in-progress: true so superseded pushes don't queue.
  3. README badge once green, mirroring what stozo04/stevengates#12 did there.

Explicitly out of scope (stays in #98)

Verification — required

  • The workflow runs green on the PR that introduces it (this requires prerequisite 1 to be done first — if the secret is missing, STOP and ask Steven rather than shipping a fallback).
  • Deliberately breaking a unit test on the branch (then reverting) to prove the gate actually fails — include the red run's link in the PR as evidence, same spirit as stevengates#35's verification section.
  • Terminal output or screenshot of the artifact listing (test results + lint report) in the PR.
  • Cache effectiveness noted: cold vs warm run duration.

Process

Same rules as #98: read CLAUDE.md and docs/STATIC_ANALYSIS.md first, verify file/line references against current code, one branch (issue-{N}-android-ci), conventional commits, plan in the PR description, do not merge.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions