update the toolchain, devshell, and build configuration

AGP 9.4.1 / Gradle 9.7.1 / Kotlin 2.3.10 (AGP built-in Kotlin), compileSdk and targetSdk 37, Room 3 with KSP schema export, and nushell/navidrome/gonic in the devshell plus a direnv .envrc. Adds a dedicated :app:integrationTest task and excludes the integration class from the unit test tasks.
This commit is contained in:
austinried
2026-10-04 00:29:51 +00:00
parent 6b6758a9b0
commit 967b733925
11 changed files with 111 additions and 98 deletions
+1
View File
@@ -0,0 +1 @@
use flake
+2
View File
@@ -12,6 +12,8 @@ captures/
.vscode/
.kotlin/
.direnv/
.preview/
.integration/
.DS_Store
+48 -7
View File
@@ -1,19 +1,21 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.roborazzi)
alias(libs.plugins.ksp)
}
android {
namespace = "com.subtracks"
compileSdk = 35
compileSdk = 37
buildToolsVersion = "37.0.0"
defaultConfig {
applicationId = "com.subtracks.next"
minSdk = 24
targetSdk = 35
targetSdk = 37
versionCode = 1
versionName = "0.1.0"
}
@@ -27,16 +29,14 @@ android {
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
testOptions {
unitTests {
isIncludeAndroidResources = true
}
}
sourceSets.getByName("test").kotlin.directories.add("src/integrationTest/kotlin")
buildTypes {
release {
isMinifyEnabled = false
@@ -44,10 +44,42 @@ android {
}
}
kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
}
}
roborazzi {
outputDir.set(file("src/test/screenshots"))
}
ksp {
arg("room.schemaLocation", "$projectDir/schemas")
}
val integrationTestClassName = "com.subtracks.data.source.subsonic.SubsonicSourceIntegrationTest"
tasks.withType<Test>().configureEach {
if (name.endsWith("UnitTest")) {
filter {
excludeTestsMatching(integrationTestClassName)
}
}
}
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 {
includeTestsMatching(integrationTestClassName)
}
}
dependencies {
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.core.ktx)
@@ -58,6 +90,12 @@ dependencies {
implementation(libs.androidx.compose.ui.graphics)
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.compose.material3)
implementation(libs.kotlinx.coroutines.android)
implementation(libs.androidx.sqlite.bundled)
implementation(libs.androidx.room3.runtime)
implementation(libs.androidx.room3.sqlite.wrapper)
implementation(libs.okhttp)
ksp(libs.androidx.room3.compiler)
debugImplementation(libs.androidx.compose.ui.tooling)
debugImplementation(libs.androidx.compose.ui.test.manifest)
@@ -70,4 +108,7 @@ dependencies {
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.okhttp.mockwebserver)
testImplementation(libs.kotlinx.coroutines.test)
}
+1 -2
View File
@@ -1,7 +1,6 @@
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.kotlin.serialization) apply false
alias(libs.plugins.compose.compiler) apply false
alias(libs.plugins.roborazzi) apply false
alias(libs.plugins.ksp) apply false
}
Generated
+4 -4
View File
@@ -2,17 +2,17 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1780453794,
"narHash": "sha256-bXMRa9VTsHSPXL4Cw8R6JJLQeY3Y/IP4+YJCYVmQ7FY=",
"lastModified": 1789724158,
"narHash": "sha256-nlKgrm0dsVhOSopKheVBCcOpiIticufPPVLdVOI0euA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "6b316287bae2ee04c9b93c8c858d930fd07d7338",
"rev": "0a3468a402c449992505b6a9fc5b06580141b750",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"rev": "6b316287bae2ee04c9b93c8c858d930fd07d7338",
"type": "github"
}
},
+9 -4
View File
@@ -2,7 +2,7 @@
description = "Subtracks - Kotlin/Jetpack Compose Android development shell";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/6b316287bae2ee04c9b93c8c858d930fd07d7338";
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
};
outputs =
@@ -23,9 +23,9 @@
};
};
android = pkgs.androidenv.composeAndroidPackages {
platformVersions = [ "35" ];
platformVersions = [ "37" ];
buildToolsVersions = [ "37.0.0" ];
platformToolsVersion = "37.0.0";
platformToolsVersion = "37.0.1";
includeEmulator = false;
includeSystemImages = false;
includeNDK = false;
@@ -38,8 +38,13 @@
default = pkgs.mkShell {
packages = [
pkgs.jdk21
pkgs.gradle
pkgs.gradle_9
pkgs.git
pkgs.curl
pkgs.unzip
pkgs.nushell
pkgs.navidrome
pkgs.gonic
androidSdk
android.platform-tools
];
+23 -44
View File
@@ -1,28 +1,20 @@
[versions]
agp = "8.7.3"
kotlin = "2.1.20"
coreKtx = "1.15.0"
lifecycle = "2.8.7"
activityCompose = "1.9.3"
composeBom = "2024.12.01"
navigation = "2.8.5"
paging = "3.3.5"
koin = "4.0.0"
coroutines = "1.9.0"
serialization = "1.7.3"
sqldelight = "2.0.2"
okhttp = "4.12.0"
retrofit = "2.11.0"
coil = "3.0.4"
media3 = "1.5.0"
datastore = "1.1.1"
material = "1.12.0"
roborazzi = "1.32.2"
robolectric = "4.14.1"
agp = "9.4.1"
kotlin = "2.3.10"
ksp = "2.3.10"
coreKtx = "1.19.0"
lifecycle = "2.11.0"
activityCompose = "1.13.0"
composeBom = "2026.09.00"
coroutines = "1.11.0"
room3 = "3.0.3"
sqliteBundled = "2.7.1"
okhttp = "5.5.0"
roborazzi = "1.74.0"
robolectric = "4.17"
junit = "4.13.2"
androidxTestExtJunit = "1.2.1"
androidxTestCore = "1.6.1"
androidxTestRunner = "1.6.2"
androidxTestExtJunit = "1.3.0"
androidxTestCore = "1.7.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@@ -37,25 +29,15 @@ androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "u
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" }
androidx-paging-runtime = { group = "androidx.paging", name = "paging-runtime", version.ref = "paging" }
androidx-paging-compose = { group = "androidx.paging", name = "paging-compose", version.ref = "paging" }
androidx-datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastore" }
koin-android = { group = "io.insert-koin", name = "koin-android", version.ref = "koin" }
koin-androidx-compose = { group = "io.insert-koin", name = "koin-androidx-compose", version.ref = "koin" }
androidx-sqlite-bundled = { group = "androidx.sqlite", name = "sqlite-bundled", version.ref = "sqliteBundled" }
androidx-sqlite-bundled-jvm = { group = "androidx.sqlite", name = "sqlite-bundled-jvm", version.ref = "sqliteBundled" }
androidx-room3-runtime = { group = "androidx.room3", name = "room3-runtime", version.ref = "room3" }
androidx-room3-compiler = { group = "androidx.room3", name = "room3-compiler", version.ref = "room3" }
androidx-room3-sqlite-wrapper = { group = "androidx.room3", name = "room3-sqlite-wrapper", version.ref = "room3" }
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" }
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "serialization" }
sqldelight-android-driver = { group = "app.cash.sqldelight", name = "android-driver", version.ref = "sqldelight" }
sqldelight-sqlite-driver = { group = "app.cash.sqldelight", name = "sqlite-driver", version.ref = "sqldelight" }
kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutines" }
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" }
retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
coil-compose = { group = "io.coil-kt.coil3", name = "coil-compose", version.ref = "coil" }
coil-network-okhttp = { group = "io.coil-kt.coil3", name = "coil-network-okhttp", version.ref = "coil" }
media3-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media3" }
media3-session = { group = "androidx.media3", name = "media3-session", version.ref = "media3" }
media3-ui = { group = "androidx.media3", name = "media3-ui", version.ref = "media3" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
okhttp-mockwebserver = { group = "com.squareup.okhttp3", name = "mockwebserver", version.ref = "okhttp" }
roborazzi = { group = "io.github.takahirom.roborazzi", name = "roborazzi", version.ref = "roborazzi" }
roborazzi-compose = { group = "io.github.takahirom.roborazzi", name = "roborazzi-compose", version.ref = "roborazzi" }
roborazzi-junit-rule = { group = "io.github.takahirom.roborazzi", name = "roborazzi-junit-rule", version.ref = "roborazzi" }
@@ -63,12 +45,9 @@ robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidxTestExtJunit" }
androidx-test-core = { group = "androidx.test", name = "core", version.ref = "androidxTestCore" }
androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = "androidxTestRunner" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" }
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
Binary file not shown.
+4 -2
View File
@@ -1,8 +1,10 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
distributionSha256Sum=f397b287023acdba1e9f6fc5ea72d22dd63669d59ed4a289a29b1a76eee151c6
distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip
distributionSha256Sum=acd53f1edaf02f1a8ff99879f8a34b302661a057d9b063ae9e35b552f804d20a
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+7 -11
View File
@@ -1,7 +1,7 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
# gradlew start up script for POSIX generated by Gradle.
#
# Important for running:
#
@@ -29,7 +29,7 @@
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
# ksh gradlew
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
@@ -57,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -86,8 +86,7 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -115,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -173,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -206,15 +203,14 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
Vendored
+12 -24
View File
@@ -19,12 +19,12 @@
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem gradlew startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Set local scope for the variables, and ensure extensions are enabled
setlocal EnableExtensions
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@@ -51,7 +51,7 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
"%COMSPEC%" /c exit 1
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
@@ -65,30 +65,18 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
"%COMSPEC%" /c exit 1
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
@rem Execute gradlew
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
:exitWithErrorLevel
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
"%COMSPEC%" /c exit %ERRORLEVEL%