Skip to content
74 changes: 17 additions & 57 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,18 @@ jobs:
run: ../gradlew test

# Android lint is per-variant
lint:
ci:
permissions:
# Required to upload SARIF files
security-events: write
strategy:
matrix:
color: ["orange"]
store: [ "Fdroid", "Github", "Google" ]
type: [ "Debug", "Release" ]
name: Android Lint
type: [ "Debug" ]
api-level: [ 31 ]
target: [ default ]
name: Full CI
runs-on: ubuntu-latest

steps:
Expand All @@ -86,11 +88,11 @@ jobs:

# Run lint for the app module. This is configured to also lint the
# dependent modules and produce a single report.
- name: Regular lint ${{ matrix.color }}${{ matrix.store }}${{ matrix.type }}
id: runlint
- name: Lint, test, build ${{ matrix.color }}${{ matrix.store }}${{ matrix.type }}
id: runbuild
run: |
set +e
./gradlew app:lint${{ matrix.color }}${{ matrix.store }}${{ matrix.type }}
./gradlew :app:lint${{ matrix.color }}${{ matrix.store }}${{ matrix.type }} test${{ matrix.color }}${{ matrix.store }}${{ matrix.type }} assemble${{ matrix.color }}${{ matrix.store }}${{ matrix.type }}
echo "exitcode=$?" >> $GITHUB_OUTPUT

- name: Upload SARIF file
Expand All @@ -99,64 +101,21 @@ jobs:
sarif_file: app/build/reports/lint-results-${{ matrix.color }}${{ matrix.store }}${{ matrix.type }}.sarif
category: lint-${{ matrix.color }}${{ matrix.store }}${{ matrix.type }}


# Exit with whatever exit code the original lint run exited with, to
# ensure this job fails if lint fails, *but* the lint reports are still
# uploaded.
- name: Fail if lint failed
run: exit ${{ steps.runlint.outputs.exitcode }}

# Android tests are per variant
test:
strategy:
matrix:
color: ["orange"]
store: [ "Fdroid", "Github", "Google" ]
type: [ "Debug", "Release" ]
name: Android Test
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- uses: ./.github/actions/setup-build-env
with:
gradle-cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}

- name: test ${{ matrix.color }}${{ matrix.store }}${{ matrix.type }}
run: ./gradlew test${{ matrix.color }}${{ matrix.store }}${{ matrix.type }}

# Android assemble is per variant
assemble:
strategy:
matrix:
color: ["orange"]
store: [ "Fdroid", "Github", "Google" ]
type: [ "Debug", "Release" ]
name: Android Assemble
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- uses: ./.github/actions/setup-build-env
with:
gradle-cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}

- name: assemble ${{ matrix.color }}${{ matrix.store }}${{ matrix.type }}
run: ./gradlew assemble${{ matrix.color }}${{ matrix.store }}${{ matrix.type }}
- name: Fail if build failed
run: exit ${{ steps.runbuild.outputs.exitcode }}

# Connected tests are per-store-variant, debug builds only.
connected:
strategy:
matrix:
color: ["Orange"]
store: ["Fdroid", "Github", "Google"]
type: ["Debug"]
api-level: [31]
target: [default]
color: [ "Orange" ]
store: [ "Fdroid", "Github", "Google" ]
type: [ "Debug" ]
api-level: [ 31 ]
target: [ default ]
name: Android Emulator Tests
runs-on: ubuntu-latest

Expand Down Expand Up @@ -194,7 +153,8 @@ jobs:
disable-animations: false
script: echo "Generated AVD snapshot for caching."

- name: Run tests
# Connected tests only run on Debug variant (release variant task doesn't exist).
- name: Run emulator tests
uses: reactivecircus/android-emulator-runner@e89f39f1abbbd05b1113a29cf4db69e7540cae5a # v2
with:
arch: x86_64
Expand Down
12 changes: 12 additions & 0 deletions build-logic/convention/src/main/kotlin/app/pachli/KotlinAndroid.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.kotlin.dsl.assign
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.invoke
import org.gradle.kotlin.dsl.provideDelegate
import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
Expand Down Expand Up @@ -55,6 +56,17 @@ internal fun Project.configureKotlinAndroid(
// "Method myLooper in android.os.Looper not mocked"
isReturnDefaultValues = true
}

// https://developer.android.com/studio/test/managed-devices
managedDevices {
localDevices {
create("pixel9api31") {
device = "Pixel 9 Pro XL"
apiLevel = 31
systemImageSource = "aosp-atd"
}
}
}
}

buildFeatures {
Expand Down
31 changes: 31 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,34 @@ subprojects {
tasks.register<Delete>("clean") {
delete(layout.buildDirectory)
}

// Create a "precommit" lifecycle task that depends on other tasks that
// give reasonable confidence the change will pass CI. The tasks are
// limited to the "orangeGoogleDebug" flavour/variant. While problems
// might affect other combinations (and CI will check all of them), this
// combination passing gives high confidence for the amount of time it
// takes to run.
//
// - :app:lintOrangeGoogleDebug
// - *:testOrangeGoogleDebugUnitTest
// - :app:assembleOrangeDebug
// - *:pixel9api31orangegoogledebugAndroidTest
tasks.register("precommit") {
group = "Verification"
description = "Runs the precommit tests."
dependsOn(":app:lintOrangeGoogleDebug")
allprojects
.flatMap { it.tasks }
.filter { it.name.equals("testOrangeGoogleDebugUnitTest", ignoreCase = true) }
.forEach {
dependsOn(it.path)
}
dependsOn(":app:assembleOrangeGoogleDebug")
allprojects
.flatMap { it.tasks }
.filter { it.name.equals("pixel9api31OrangeGoogleDebugAndroidTest", ignoreCase = true) }
.forEach {
println("dep: $it.name")
dependsOn(it.path)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class ContextCompatGetDrawableDetector : Detector(), SourceCodeScanner {
issue = ISSUE,
scope = node,
location = context.getCallLocation(node, includeReceiver = true, includeArguments = true),
message = "Use AppCompatResources.getDrawable",
message = "Use `AppCompatResources`.getDrawable",
quickfixData = fix,
)
}
Expand All @@ -75,7 +75,7 @@ class ContextCompatGetDrawableDetector : Detector(), SourceCodeScanner {
id = "ContextCompatGetDrawableDetector",
briefDescription = "Don't use `ContextCompat.getDrawable()`, use `AppCompatResources.getDrawable()`",
explanation = """
AppCompatResources().getDrawable() backports features and bug fixes,
`AppCompatResources().getDrawable()` backports features and bug fixes,
ContextCompat.getDrawable() does not.
See https://medium.com/@crafty/yes-contextcompat-just-saves-you-the-api-level-check-it-doesnt-back-port-and-features-or-bug-9cd7d5f09be4
""",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ class StringResourceEntityDetector : Detector(), SourceCodeScanner, OtherFileSca
val ISSUE = Issue.create(
id = "StringResourceEntityDetector",
briefDescription = "`&lt;` and `&gt;` in string resources should use angle brackets",
explanation = "This string resource is passed to getText(), which expects HTML.",
explanation = "This string resource is passed to `getText()`, which expects HTML.",
category = Category.CORRECTNESS,
priority = 10,
severity = Severity.WARNING,
Expand Down
Loading