Skip to content

chore: Run PR verification (unit tests, coverage, static analysis) on… #6

chore: Run PR verification (unit tests, coverage, static analysis) on…

chore: Run PR verification (unit tests, coverage, static analysis) on… #6

# Runs the unit-test gate on GitHub Actions, replacing the monolithic
# CodeBuild `./gradlew build`. The unit-test suite runs exactly once (via Kover,
# which also emits the coverage report), and static analysis runs in parallel so
# cheap failures surface fast. Coverage upload is observability only and never
# gates the merge.
name: PR Verification
on:
pull_request:
branches:
- 'main'
push:
branches:
- 'main'
workflow_dispatch:
permissions:
contents: read
# Required for the test-report step to attach annotations to the run's check.
# Note: fork PRs get a read-only token, so annotations are skipped there — the
# step is continue-on-error so this never fails the required job, and the
# actual test pass/fail is still determined by the Gradle exit code.
checks: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
# Only cancel superseded runs on PRs. Runs on `main` are never cancelled —
# cancelling one would drop that commit's Codecov upload and leave a gap in
# main's coverage history.
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
# REQUIRED. Runs the full unit-test suite once and uploads coverage to Codecov
# as a soft-fail (observability-only) step.
unit-tests:
runs-on: ubuntu-latest
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Java
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '17'
distribution: 'corretto'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0
# `koverXmlReport` runs the unit-test suite once and emits the coverage
# report as a byproduct. Note: Kover only aggregates modules that apply the
# Kover plugin, which is applied via PublishingConventionPlugin — so only
# *published* modules have their tests run here. Every module with test
# sources is currently also published, so this is at parity with the old
# `./gradlew build` gate. A future non-published module with unit tests
# would silently drop out of this gate (see design doc; follow-up: a CI
# guard that fails if a module has test sources but no Kover plugin).
- name: Run tests and generate Kover report
run: ./gradlew koverXmlReport
# Surfaces failing tests as inline annotations on this job's check (no
# separate check run — annotate_only). Runs even when the test step failed
# (that's when it's most useful) and is soft-fail so a read-only token on
# fork PRs never fails the job.
- name: Publish test report annotations
uses: mikepenz/action-junit-report@d9f48fc87bc235f7e214acf696ca5abc0a986f16 # v6.4.2
if: ${{ !cancelled() }}
continue-on-error: true
with:
report_paths: '**/build/test-results/**/*.xml'
annotate_only: true
# Soft-fail: Codecov upload is observability only and must never fail this
# required job. Skipped on fork PRs where the token is absent.
- name: Upload coverage to Codecov
if: ${{ !cancelled() && env.CODECOV_TOKEN != '' }}
continue-on-error: true
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v6
with:
name: report
files: build/reports/kover/report.xml
token: ${{ env.CODECOV_TOKEN }}
- name: Upload test reports
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: failure()
with:
name: test-reports
path: '**/reports/tests/**'
retention-days: 7
# REQUIRED. Runs in parallel with unit-tests so lint/format/API-compat breaks
# surface fast. `lint` is included because the old `./gradlew build` gate ran
# Android lint with abortOnError/warningsAsErrors — dropping it would silently
# remove a gating check.
static-analysis:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Java
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '17'
distribution: 'corretto'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3
- name: Run static analysis
run: ./gradlew ktlintCheck checkstyle apiCheck lint
# REQUIRED. Verifies that every external type in each published module's public
# API surface is resolvable on the compile classpath a downstream consumer
# assembles from the published artifact. A failure means a dependency supplying
# a public-API type is scoped `implementation` (runtime) but must be `api`
# (compile). Runs in parallel with the other gates.
scope-check:
runs-on: ubuntu-latest
# Only needs read access; the workflow-level `checks: write` (for the
# test-report annotator) is not required here.
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Java
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
java-version: '17'
distribution: 'corretto'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3
- name: Verify API scopes
run: scripts/verify_api_scopes.sh