From e24fe1d010549a4e174605e70c394bfeac3777ea Mon Sep 17 00:00:00 2001 From: Sean Proctor Date: Fri, 29 May 2026 10:08:26 -0400 Subject: [PATCH 1/2] feat(plugin): add per-OS jpackageVersion for jpackage app-image jpackage rejects SemVer pre-release/build metadata (e.g. "2.3.5-beta.7" fails with "invalid component [5-beta.7]"), but it is the only backend that builds the app-image (RawAppImage); all installer formats route through electron-builder, which accepts full SemVer. Add a per-OS `jpackageVersion` (windows/macOS/linux) used only for the jpackage app-image, falling back to `packageVersion` when unset. The value is passed to jpackage as-is. electron-builder formats continue to use `packageVersion` with the SemVer suffix intact. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../application/dsl/PlatformSettings.kt | 13 +++++++++ .../internal/configureJvmApplication.kt | 4 ++- .../application/internal/packageVersions.kt | 28 +++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/dsl/PlatformSettings.kt b/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/dsl/PlatformSettings.kt index e2b4af265..5160df1e7 100644 --- a/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/dsl/PlatformSettings.kt +++ b/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/dsl/PlatformSettings.kt @@ -19,6 +19,19 @@ abstract class AbstractPlatformSettings { val iconFile: RegularFileProperty = objects.fileProperty() var packageVersion: String? = null + /** + * Version passed to jpackage when it builds the app-image (`--app-version`). + * + * jpackage enforces strict, platform-specific version rules — notably Windows requires + * `MAJOR.MINOR.BUILD` and rejects SemVer pre-release/build metadata such as `2.3.5-beta.7`. + * [packageVersion] is forwarded unchanged to electron-builder (which accepts full SemVer), so + * use this to give jpackage a compatible version without altering [packageVersion]. + * + * When unset, it defaults to [packageVersion]; the value is passed to jpackage as-is and + * jpackage reports an error if it is not compatible. + */ + var jpackageVersion: String? = null + internal val fileAssociations: MutableSet = mutableSetOf() @JvmOverloads diff --git a/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/internal/configureJvmApplication.kt b/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/internal/configureJvmApplication.kt index 724914f86..f885fa16a 100644 --- a/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/internal/configureJvmApplication.kt +++ b/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/internal/configureJvmApplication.kt @@ -708,7 +708,9 @@ private fun JvmApplicationContext.configurePackageTask( packageTask.packageDescription.set(executables.description) packageTask.packageCopyright.set(executables.copyright) packageTask.packageVendor.set(executables.vendor) - packageTask.packageVersion.set(packageVersionFor(packageTask.targetFormat)) + // jpackage app-image: use the jpackage-safe version. electron-builder formats keep the full + // SemVer (see configureElectronBuilderPackageTask, which uses packageVersionFor). + packageTask.packageVersion.set(jpackageVersionFor(packageTask.targetFormat)) } val dirSuffix = if (sandboxed) "-sandboxed" else "" diff --git a/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/internal/packageVersions.kt b/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/internal/packageVersions.kt index e100fbfda..22114fe0f 100644 --- a/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/internal/packageVersions.kt +++ b/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/internal/packageVersions.kt @@ -17,6 +17,34 @@ internal fun JvmApplicationContext.packageVersionFor(targetFormat: TargetFormat) ?: "1.0.0" } +/** + * Version used when jpackage builds the app-image (`--app-version`). + * + * jpackage is the only [io.github.kdroidfilter.nucleus.desktop.application.dsl.PackagingBackend.JPACKAGE] + * step ([TargetFormat.RawAppImage]) and enforces strict platform version rules — notably Windows + * rejects SemVer pre-release/build metadata such as `2.3.5-beta.7`. All real installer formats run + * through electron-builder and keep the full SemVer via [packageVersionFor]. + * + * Resolves the platform's explicit + * [io.github.kdroidfilter.nucleus.desktop.application.dsl.AbstractPlatformSettings.jpackageVersion], + * falling back to the normal package version. The value is passed to jpackage as-is; jpackage + * reports an error if it is not compatible. + */ +internal fun JvmApplicationContext.jpackageVersionFor(targetFormat: TargetFormat): Provider = + project.provider { + app.nativeDistributions.jpackageVersionFor(targetFormat) + ?: app.nativeDistributions.packageVersionFor(targetFormat) + ?: project.version.toString().takeIf { it != "unspecified" } + ?: "1.0.0" + } + +private fun JvmApplicationDistributions.jpackageVersionFor(targetFormat: TargetFormat): String? = + when (targetFormat.targetOS) { + OS.Linux -> linux.jpackageVersion + OS.MacOS -> macOS.jpackageVersion + OS.Windows -> windows.jpackageVersion + } + @Suppress("CyclomaticComplexMethod") // Exhaustive when on TargetFormat enum private fun JvmApplicationDistributions.packageVersionFor(targetFormat: TargetFormat): String? { val formatSpecificVersion: String? = From 2b2b5dd9ebc47d1339ae78f16c2993de6508a03d Mon Sep 17 00:00:00 2001 From: Sean Proctor Date: Sun, 31 May 2026 05:41:30 -0400 Subject: [PATCH 2/2] remove jpackage configuration, just simplify --- .../application/dsl/PlatformSettings.kt | 13 ------------ .../internal/configureJvmApplication.kt | 3 +-- .../application/internal/packageVersions.kt | 21 ++++--------------- 3 files changed, 5 insertions(+), 32 deletions(-) diff --git a/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/dsl/PlatformSettings.kt b/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/dsl/PlatformSettings.kt index 5160df1e7..e2b4af265 100644 --- a/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/dsl/PlatformSettings.kt +++ b/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/dsl/PlatformSettings.kt @@ -19,19 +19,6 @@ abstract class AbstractPlatformSettings { val iconFile: RegularFileProperty = objects.fileProperty() var packageVersion: String? = null - /** - * Version passed to jpackage when it builds the app-image (`--app-version`). - * - * jpackage enforces strict, platform-specific version rules — notably Windows requires - * `MAJOR.MINOR.BUILD` and rejects SemVer pre-release/build metadata such as `2.3.5-beta.7`. - * [packageVersion] is forwarded unchanged to electron-builder (which accepts full SemVer), so - * use this to give jpackage a compatible version without altering [packageVersion]. - * - * When unset, it defaults to [packageVersion]; the value is passed to jpackage as-is and - * jpackage reports an error if it is not compatible. - */ - var jpackageVersion: String? = null - internal val fileAssociations: MutableSet = mutableSetOf() @JvmOverloads diff --git a/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/internal/configureJvmApplication.kt b/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/internal/configureJvmApplication.kt index f885fa16a..0543cfbda 100644 --- a/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/internal/configureJvmApplication.kt +++ b/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/internal/configureJvmApplication.kt @@ -708,8 +708,7 @@ private fun JvmApplicationContext.configurePackageTask( packageTask.packageDescription.set(executables.description) packageTask.packageCopyright.set(executables.copyright) packageTask.packageVendor.set(executables.vendor) - // jpackage app-image: use the jpackage-safe version. electron-builder formats keep the full - // SemVer (see configureElectronBuilderPackageTask, which uses packageVersionFor). + // jpackage app-image: use the jpackage-safe version. packageTask.packageVersion.set(jpackageVersionFor(packageTask.targetFormat)) } diff --git a/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/internal/packageVersions.kt b/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/internal/packageVersions.kt index 22114fe0f..d373b4063 100644 --- a/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/internal/packageVersions.kt +++ b/plugin-build/plugin/src/main/kotlin/io/github/kdroidfilter/nucleus/desktop/application/internal/packageVersions.kt @@ -24,26 +24,13 @@ internal fun JvmApplicationContext.packageVersionFor(targetFormat: TargetFormat) * step ([TargetFormat.RawAppImage]) and enforces strict platform version rules — notably Windows * rejects SemVer pre-release/build metadata such as `2.3.5-beta.7`. All real installer formats run * through electron-builder and keep the full SemVer via [packageVersionFor]. - * - * Resolves the platform's explicit - * [io.github.kdroidfilter.nucleus.desktop.application.dsl.AbstractPlatformSettings.jpackageVersion], - * falling back to the normal package version. The value is passed to jpackage as-is; jpackage - * reports an error if it is not compatible. */ internal fun JvmApplicationContext.jpackageVersionFor(targetFormat: TargetFormat): Provider = - project.provider { - app.nativeDistributions.jpackageVersionFor(targetFormat) - ?: app.nativeDistributions.packageVersionFor(targetFormat) - ?: project.version.toString().takeIf { it != "unspecified" } - ?: "1.0.0" - } + packageVersionFor(targetFormat).map { it.toJpackageVersion() } -private fun JvmApplicationDistributions.jpackageVersionFor(targetFormat: TargetFormat): String? = - when (targetFormat.targetOS) { - OS.Linux -> linux.jpackageVersion - OS.MacOS -> macOS.jpackageVersion - OS.Windows -> windows.jpackageVersion - } +// jpackage rejects SemVer pre-release/build metadata; keep only the MAJOR.MINOR.PATCH core. +private fun String.toJpackageVersion(): String = + substringBefore('-').substringBefore('+') @Suppress("CyclomaticComplexMethod") // Exhaustive when on TargetFormat enum private fun JvmApplicationDistributions.packageVersionFor(targetFormat: TargetFormat): String? {