commit 5d73568ffb852dea9749c23a75816d5adb932831
parent e9f598dd293e01d92e3ac5b8be39de33c18082fa
Author: triesap <tyson@radroots.org>
Date: Mon, 10 Aug 2026 01:44:33 +0000
build: establish the KMP shared and desktop boundary
- add a one-target Kotlin Multiplatform shared module named desktop
- retain the JVM 21 desktop host and consume shared code as a project dependency
- move neutral recovery state and tests into common source sets
- verify discovery, compilation, lint, tests, and checks in both build lanes
Diffstat:
10 files changed, 75 insertions(+), 25 deletions(-)
diff --git a/Makefile b/Makefile
@@ -24,22 +24,22 @@ metadata: doctor
format: doctor
$(EXTBUILD) $(CARGO) fmt --manifest-path $(CARGO_MANIFEST) --all -- --check
- $(EXTBUILD) $(GRADLE) --no-daemon :app:desktop:ktlintCheck
+ $(EXTBUILD) $(GRADLE) --no-daemon :app:shared:ktlintCheck :app:desktop:ktlintCheck
format-fix: doctor
$(EXTBUILD) $(CARGO) fmt --manifest-path $(CARGO_MANIFEST) --all
- $(EXTBUILD) $(GRADLE) --no-daemon :app:desktop:ktlintFormat
+ $(EXTBUILD) $(GRADLE) --no-daemon :app:shared:ktlintFormat :app:desktop:ktlintFormat
lint: doctor
$(EXTBUILD) $(CARGO) clippy --manifest-path $(CARGO_MANIFEST) --workspace --all-targets --locked -- -D warnings
- $(EXTBUILD) $(GRADLE) --no-daemon :app:desktop:detekt
+ $(EXTBUILD) $(GRADLE) --no-daemon :app:shared:detektCommonMainSourceSet :app:shared:detektCommonTestSourceSet :app:desktop:detekt
test: doctor
$(EXTBUILD) $(CARGO) test --manifest-path $(CARGO_MANIFEST) --workspace --locked
- $(EXTBUILD) $(GRADLE) --no-daemon :app:desktop:test
+ $(EXTBUILD) $(GRADLE) --no-daemon :app:shared:desktopTest :app:desktop:test
check: format lint test
- $(EXTBUILD) $(GRADLE) --no-daemon :app:desktop:check
+ $(EXTBUILD) $(GRADLE) --no-daemon :app:shared:check :app:desktop:check
build: doctor
$(EXTBUILD) $(CARGO) build --manifest-path $(CARGO_MANIFEST) --workspace --locked
diff --git a/app/desktop/build.gradle.kts b/app/desktop/build.gradle.kts
@@ -612,6 +612,7 @@ abstract class VerifyMacOsNotarization : DefaultTask() {
}
dependencies {
+ implementation(projects.app.shared)
implementation(compose.desktop.currentOs)
implementation(libs.compose.foundation)
implementation(libs.jna)
diff --git a/app/desktop/src/main/kotlin/org/harvestcircle/application/GeneratedKeyBackup.kt b/app/desktop/src/main/kotlin/org/harvestcircle/application/GeneratedKeyBackup.kt
@@ -1,19 +0,0 @@
-package org.harvestcircle.application
-
-class GeneratedKeyBackup internal constructor(
- val npub: String,
- nsec: String,
-) {
- private var recoveryText: String? = nsec
-
- internal fun revealNsec(): String =
- checkNotNull(recoveryText) {
- "Generated recovery material is no longer available"
- }
-
- internal fun clear() {
- recoveryText = null
- }
-
- override fun toString(): String = "GeneratedKeyBackup(npub=$npub, nsec=[REDACTED])"
-}
diff --git a/app/shared/build.gradle.kts b/app/shared/build.gradle.kts
@@ -0,0 +1,36 @@
+import org.jetbrains.kotlin.gradle.dsl.JvmTarget
+import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
+
+plugins {
+ alias(libs.plugins.kotlin.multiplatform)
+ alias(libs.plugins.detekt)
+ alias(libs.plugins.ktlint)
+}
+
+providers.environmentVariable("EXT_BUILD_GRADLE_BUILD_DIR").orNull?.let { extBuildGradleRoot ->
+ layout.buildDirectory.set(file(extBuildGradleRoot).resolve("app-shared"))
+}
+
+kotlin {
+ jvm("desktop") {
+ compilerOptions {
+ jvmTarget.set(JvmTarget.JVM_21)
+ }
+ }
+
+ sourceSets {
+ commonTest.dependencies {
+ implementation(kotlin("test"))
+ }
+ }
+
+ jvmToolchain(21)
+
+ check(
+ targets
+ .filter { it.platformType != KotlinPlatformType.common }
+ .map { it.name } == listOf("desktop"),
+ ) {
+ "HarvestCircle shared must declare exactly one KMP platform target named desktop"
+ }
+}
diff --git a/app/shared/src/commonMain/kotlin/org/harvestcircle/application/GeneratedKeyBackup.kt b/app/shared/src/commonMain/kotlin/org/harvestcircle/application/GeneratedKeyBackup.kt
@@ -0,0 +1,19 @@
+package org.harvestcircle.application
+
+class GeneratedKeyBackup(
+ val npub: String,
+ nsec: String,
+) {
+ private var recoveryText: String? = nsec
+
+ fun revealNsec(): String =
+ checkNotNull(recoveryText) {
+ "Generated recovery material is no longer available"
+ }
+
+ fun clear() {
+ recoveryText = null
+ }
+
+ override fun toString(): String = "GeneratedKeyBackup(npub=$npub, nsec=[REDACTED])"
+}
diff --git a/app/desktop/src/test/kotlin/org/harvestcircle/application/GeneratedKeyBackupTest.kt b/app/shared/src/commonTest/kotlin/org/harvestcircle/application/GeneratedKeyBackupTest.kt
diff --git a/build.gradle.kts b/build.gradle.kts
@@ -1,6 +1,7 @@
import org.harvestcircle.gradle.VerifyProductCoordinates
plugins {
+ alias(libs.plugins.kotlin.multiplatform) apply false
alias(libs.plugins.kotlin.jvm) apply false
alias(libs.plugins.compose.multiplatform) apply false
alias(libs.plugins.compose.compiler) apply false
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
@@ -16,6 +16,7 @@ kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-c
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
[plugins]
+kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
compose-multiplatform = { id = "org.jetbrains.compose", version.ref = "compose" }
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
@@ -2616,6 +2616,9 @@
</artifact>
</component>
<component group="org.jetbrains.kotlin" name="kotlin-stdlib" version="2.4.10">
+ <artifact name="kotlin-stdlib-2.4.10-all.jar">
+ <sha256 value="d0e49d626d43c6677c9ddb9396e88b48afee4b21b4082b0fb71d894b2fa06c0f" origin="Generated by Gradle"/>
+ </artifact>
<artifact name="kotlin-stdlib-2.4.10.jar">
<sha256 value="4ec0293bc3751423b203f1d8493251c57c42e73eb6377a6b8560d0974ff0a6df" origin="Generated by Gradle"/>
</artifact>
@@ -2666,6 +2669,9 @@
</artifact>
</component>
<component group="org.jetbrains.kotlin" name="kotlin-test" version="2.4.10">
+ <artifact name="kotlin-test-2.4.10-all.jar">
+ <sha256 value="5760d52ee7696419c0fd9f73aebb3867bc862cca716ed920b470b27767cbc784" origin="Generated by Gradle"/>
+ </artifact>
<artifact name="kotlin-test-2.4.10.jar">
<sha256 value="5d334a7b17ce9b223e4db35e355f0082fb0551d36c0424a599bf35686ba2ab7b" origin="Generated by Gradle"/>
</artifact>
@@ -2750,6 +2756,11 @@
<sha256 value="5b762d1fbe93d3cd5c8cdb8548dd9593dbc41e0d6d9d9dff36c9252678226992" origin="Generated by Gradle"/>
</artifact>
</component>
+ <component group="org.jetbrains.kotlin.multiplatform" name="org.jetbrains.kotlin.multiplatform.gradle.plugin" version="2.4.10">
+ <artifact name="org.jetbrains.kotlin.multiplatform.gradle.plugin-2.4.10.pom">
+ <sha256 value="2045cd0178f1d3b7046e83cdf4a0bda5180b76b3bcc2b0f929d7fc50a12eaf55" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
<component group="org.jetbrains.kotlin.plugin.compose" name="org.jetbrains.kotlin.plugin.compose.gradle.plugin" version="2.4.10">
<artifact name="org.jetbrains.kotlin.plugin.compose.gradle.plugin-2.4.10.pom">
<sha256 value="0c101ab316081659050fe9c0144b1b4bf725ebf9aa9943113bc221e923473d04" origin="Generated by Gradle"/>
diff --git a/settings.gradle.kts b/settings.gradle.kts
@@ -41,4 +41,4 @@ rootProject.name = "harvestcircle"
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
-include(":app:desktop")
+include(":app:desktop", ":app:shared")