mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 23:21:59 +01:00
Robolectric resolves android-all-instrumented itself at test time with MavenArtifactFetcher, whose default repository is repo1.maven.org. The Gradle mirror in settings.gradle.kts never applied to that fetch, so the CI egress IP still got HTTP 429 there. That fetcher also stores the jar in ~/.m2/repository, outside the ~/.gradle/caches directory CI restores, so every ephemeral runner downloaded it again. Point robolectric.dependency.repo.url at the same mirror for every Test task and cache the Robolectric Maven repository in both workflows, keyed by the Robolectric version and the configured SDK.
293 lines
10 KiB
Kotlin
293 lines
10 KiB
Kotlin
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
|
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|
|
|
plugins {
|
|
alias(libs.plugins.android.application)
|
|
alias(libs.plugins.compose.compiler)
|
|
alias(libs.plugins.roborazzi)
|
|
alias(libs.plugins.ksp)
|
|
alias(libs.plugins.ktlint)
|
|
alias(libs.plugins.aboutLibraries)
|
|
}
|
|
|
|
ktlint {
|
|
version.set(libs.versions.ktlint.get())
|
|
}
|
|
|
|
val commitHash: String =
|
|
runCatching {
|
|
providers
|
|
.exec {
|
|
commandLine("git", "rev-parse", "HEAD")
|
|
workingDir = rootProject.layout.projectDirectory.asFile
|
|
isIgnoreExitValue = true
|
|
}.standardOutput.asText
|
|
.get()
|
|
.trim()
|
|
.take(7)
|
|
}.getOrDefault("")
|
|
|
|
android {
|
|
namespace = "com.subtracks"
|
|
compileSdk = 37
|
|
buildToolsVersion = "37.0.0"
|
|
|
|
defaultConfig {
|
|
applicationId = "com.subtracks"
|
|
minSdk = 24
|
|
targetSdk = 37
|
|
versionCode = 14
|
|
versionName = "3.1.0"
|
|
buildConfigField("String", "COMMIT_HASH", "\"$commitHash\"")
|
|
}
|
|
|
|
buildFeatures {
|
|
compose = true
|
|
buildConfig = true
|
|
}
|
|
|
|
androidResources {
|
|
generateLocaleConfig = true
|
|
}
|
|
|
|
dependenciesInfo {
|
|
// The APK signing-block copy is encrypted per build, breaking APK reproducibility, and
|
|
// F-Droid rejects it; the bundle keeps its copy for Play.
|
|
includeInApk = false
|
|
}
|
|
|
|
compileOptions {
|
|
sourceCompatibility = JavaVersion.VERSION_17
|
|
targetCompatibility = JavaVersion.VERSION_17
|
|
isCoreLibraryDesugaringEnabled = true
|
|
}
|
|
|
|
lint {
|
|
checkReleaseBuilds = false
|
|
warningsAsErrors = true
|
|
disable +=
|
|
setOf(
|
|
"MissingTranslation",
|
|
"MissingQuantity",
|
|
// Version nags are handled by the dependency-update process, not the build.
|
|
"AndroidGradlePluginVersion",
|
|
"GradleDependency",
|
|
"NewerVersionAvailable",
|
|
)
|
|
}
|
|
|
|
packaging {
|
|
jniLibs {
|
|
// Prebuilt .so files with no symbol table; leaving them unstripped avoids the strip warning.
|
|
keepDebugSymbols +=
|
|
setOf(
|
|
"**/libandroidx.graphics.path.so",
|
|
"**/libdatastore_shared_counter.so",
|
|
"**/libsqliteJni.so",
|
|
)
|
|
}
|
|
}
|
|
|
|
testOptions {
|
|
unitTests {
|
|
isIncludeAndroidResources = true
|
|
}
|
|
}
|
|
|
|
sourceSets
|
|
.getByName("test")
|
|
.kotlin.directories
|
|
.add("src/integrationTest/kotlin")
|
|
|
|
signingConfigs {
|
|
// Committed on purpose: a public key for debug and nightly builds, never for distribution.
|
|
getByName("debug") {
|
|
storeFile = file("debug.keystore")
|
|
storePassword = "android"
|
|
keyAlias = "androiddebugkey"
|
|
keyPassword = "android"
|
|
}
|
|
val keystorePath = providers.environmentVariable("RELEASE_KEYSTORE").orNull
|
|
if (keystorePath != null) {
|
|
create("release") {
|
|
storeFile = file(keystorePath)
|
|
storePassword = providers.environmentVariable("RELEASE_KEYSTORE_PASSWORD").orNull
|
|
keyAlias = providers.environmentVariable("RELEASE_KEY_ALIAS").orNull
|
|
keyPassword = providers.environmentVariable("RELEASE_KEY_PASSWORD").orNull
|
|
}
|
|
}
|
|
}
|
|
|
|
sourceSets {
|
|
getByName("debug").res.directories.add("src/nightly/res")
|
|
}
|
|
|
|
buildTypes {
|
|
debug {
|
|
applicationIdSuffix = ".nightly"
|
|
}
|
|
release {
|
|
isMinifyEnabled = true
|
|
isShrinkResources = true
|
|
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
|
|
// Release signing when the keystore is supplied (CI); debug elsewhere so local build/install works.
|
|
signingConfig = signingConfigs.findByName("release") ?: signingConfigs.getByName("debug")
|
|
}
|
|
create("nightly") {
|
|
initWith(getByName("release"))
|
|
applicationIdSuffix = ".nightly"
|
|
signingConfig = signingConfigs.getByName("debug")
|
|
}
|
|
}
|
|
}
|
|
|
|
kotlin {
|
|
compilerOptions {
|
|
jvmTarget.set(JvmTarget.JVM_17)
|
|
allWarningsAsErrors.set(true)
|
|
}
|
|
}
|
|
|
|
roborazzi {
|
|
outputDir.set(file("src/test/screenshots"))
|
|
}
|
|
|
|
ksp {
|
|
arg("room.schemaLocation", "$projectDir/schemas")
|
|
}
|
|
|
|
// Resolving the variant classpaths here makes AGP warn that a configuration was resolved during
|
|
// configuration time; upstream, tracked in mikepenz/AboutLibraries#1369.
|
|
aboutLibraries {
|
|
collect {
|
|
configPath = file("config")
|
|
}
|
|
}
|
|
|
|
val integrationTestClasses =
|
|
listOf(
|
|
"com.subtracks.data.source.subsonic.SubsonicAuthIntegrationTest",
|
|
"com.subtracks.data.source.subsonic.SubsonicErrorIntegrationTest",
|
|
"com.subtracks.data.source.subsonic.SubsonicMediaIntegrationTest",
|
|
"com.subtracks.data.source.subsonic.SubsonicSourceIntegrationTest",
|
|
"com.subtracks.data.source.subsonic.SubsonicWriteIntegrationTest",
|
|
"com.subtracks.data.sync.MultiSourceSyncIntegrationTest",
|
|
"com.subtracks.data.sync.PruneSyncIntegrationTest",
|
|
"com.subtracks.data.sync.SyncServiceIntegrationTest",
|
|
)
|
|
|
|
val demoScreenshotClass = "com.subtracks.ui.DemoScreenshotTest"
|
|
|
|
val queryTimingClass = "com.subtracks.data.db.QueryTimingMeasurementTest"
|
|
|
|
tasks.withType<Test>().configureEach {
|
|
// Robolectric fetches its Android SDK jar itself, outside Gradle's repositories.
|
|
systemProperty("robolectric.dependency.repo.url", providers.gradleProperty("mavenCentralMirrorUrl").get())
|
|
if (name.endsWith("UnitTest")) {
|
|
filter {
|
|
(integrationTestClasses + demoScreenshotClass + queryTimingClass).forEach { excludeTestsMatching(it) }
|
|
}
|
|
}
|
|
testLogging {
|
|
exceptionFormat = TestExceptionFormat.FULL
|
|
}
|
|
outputs.cacheIf { false }
|
|
}
|
|
|
|
tasks.register<Test>("integrationTest") {
|
|
description = "Runs the integration tests against locally started Subsonic servers"
|
|
group = "verification"
|
|
val unitTest = tasks.named<Test>("testDebugUnitTest")
|
|
dependsOn("compileDebugUnitTestKotlin")
|
|
testClassesDirs = files(provider { unitTest.get().testClassesDirs })
|
|
classpath = files(provider { unitTest.get().classpath })
|
|
filter {
|
|
integrationTestClasses.forEach { includeTestsMatching(it) }
|
|
}
|
|
systemProperty("prune.baseUrl", providers.gradleProperty("pruneBaseUrl").getOrElse(""))
|
|
systemProperty("prune.musicDir", providers.gradleProperty("pruneMusicDir").getOrElse(""))
|
|
}
|
|
|
|
tasks.register<Test>("demoScreenshots") {
|
|
description = "Records the store screenshots from a real sync of the public Navidrome demo library"
|
|
group = "verification"
|
|
val unitTest = tasks.named<Test>("testDebugUnitTest")
|
|
dependsOn("compileDebugUnitTestKotlin")
|
|
testClassesDirs = files(provider { unitTest.get().testClassesDirs })
|
|
classpath = files(provider { unitTest.get().classpath })
|
|
filter {
|
|
includeTestsMatching(demoScreenshotClass)
|
|
}
|
|
systemProperty("roborazzi.test.record", "true")
|
|
outputs.upToDateWhen { false }
|
|
}
|
|
|
|
tasks.register<Test>("queryTiming") {
|
|
description = "Measures SQL query timings for the logging threshold (not a CI gate)"
|
|
group = "verification"
|
|
val unitTest = tasks.named<Test>("testDebugUnitTest")
|
|
dependsOn("compileDebugUnitTestKotlin")
|
|
testClassesDirs = files(provider { unitTest.get().testClassesDirs })
|
|
classpath = files(provider { unitTest.get().classpath })
|
|
filter {
|
|
includeTestsMatching(queryTimingClass)
|
|
}
|
|
providers.gradleProperty("measureSongs").orNull?.let { systemProperty("measure.songs", it) }
|
|
outputs.upToDateWhen { false }
|
|
}
|
|
|
|
dependencies {
|
|
implementation(platform(libs.androidx.compose.bom))
|
|
implementation(libs.androidx.core.ktx)
|
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
|
implementation(libs.androidx.lifecycle.runtime.compose)
|
|
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
|
implementation(libs.androidx.activity.compose)
|
|
implementation(libs.androidx.navigation.compose)
|
|
implementation(libs.androidx.compose.ui)
|
|
implementation(libs.androidx.compose.ui.graphics)
|
|
implementation(libs.androidx.compose.ui.tooling.preview)
|
|
implementation(libs.androidx.compose.material3)
|
|
implementation(libs.androidx.compose.material.icons.core)
|
|
implementation(libs.androidx.compose.material.icons.extended)
|
|
implementation(libs.kotlinx.coroutines.android)
|
|
implementation(libs.androidx.sqlite.bundled)
|
|
implementation(libs.androidx.room3.runtime)
|
|
implementation(libs.androidx.room3.sqlite.wrapper)
|
|
implementation(libs.androidx.room3.paging)
|
|
implementation(libs.androidx.paging.compose)
|
|
implementation(libs.androidx.palette)
|
|
implementation(libs.reorderable)
|
|
implementation(libs.androidx.datastore.preferences)
|
|
implementation(libs.androidx.media3.exoplayer)
|
|
implementation(libs.androidx.media3.session)
|
|
implementation(libs.androidx.media3.datasource)
|
|
implementation(libs.androidx.media3.datasource.okhttp)
|
|
implementation(libs.androidx.media3.database)
|
|
implementation(libs.okhttp)
|
|
implementation(libs.coil.compose)
|
|
implementation(libs.coil.network.okhttp)
|
|
implementation(libs.koin.androidx.compose)
|
|
implementation(libs.aboutlibraries.core)
|
|
implementation(libs.aboutlibraries.compose.m3)
|
|
ksp(libs.androidx.room3.compiler)
|
|
|
|
coreLibraryDesugaring(libs.desugar.jdk.libs)
|
|
|
|
debugImplementation(libs.androidx.compose.ui.tooling)
|
|
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
|
|
|
testImplementation(libs.junit)
|
|
testImplementation(libs.robolectric)
|
|
testImplementation(libs.roborazzi)
|
|
testImplementation(libs.roborazzi.compose)
|
|
testImplementation(libs.roborazzi.junit.rule)
|
|
testImplementation(libs.androidx.compose.ui.test.junit4)
|
|
testImplementation(libs.androidx.test.ext.junit)
|
|
testImplementation(libs.androidx.test.core)
|
|
testImplementation(libs.androidx.sqlite.bundled.jvm)
|
|
testImplementation(libs.coil.test)
|
|
testImplementation(libs.okhttp.mockwebserver)
|
|
testImplementation(libs.kotlinx.coroutines.test)
|
|
}
|