mirror of
https://github.com/praktimarc/kst4contest.git
synced 2026-10-18 21:34:03 +02:00
* build: configure the Windows MSI installer in the Compose windows block (#61) Add the jpackage/WiX options for a per-user MSI next to the existing icon: a fixed upgradeUuid so Windows Installer replaces an installed KST4Contest instead of installing side by side, a Start menu group and desktop shortcut, no UAC prompt, and a directory chooser. The portable ZIP (createDistributable) is unaffected; the MSI is produced only by the tagged-release workflow. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * ci: build and attach a Windows MSI installer for tagged releases (#61) The Windows job of tagged-release.yml now runs packageMsi after the portable ZIP, renames the result to KST4Contest-<tag>-windows-x64.msi (consistent with the other platforms; the ZIP keeps its praktiKST name) and uploads it as the windows-msi artifact, which the release job downloads and attaches next to the ZIP. The nightly workflow stays ZIP-only because a nightly MSI would always carry the same version. The website download data (downloads.js, beta.js) gets an MSI card that is shown only when the release actually has that asset, so the page never links to a file that does not exist for an older release. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs: describe the Windows MSI installer next to the portable ZIP (#61) The German and English installation manuals list the MSI in the package table, describe both Windows forms (installer: per user, Start menu entry, desktop shortcut, chosen directory, in-place upgrade, removal via the Windows apps list; ZIP: portable) and the shared data directory %USERPROFILE%\.praktiKST, and add the MSI path to the Windows update section. Changelog entries for v1.50 and a note in PROJECT_CONTEXT.md record the fixed upgrade UUID and the tags-only decision. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * ci: build the Windows MSI for stable tags only (#61) A beta MSI would carry the same composePackageVersion as the following stable release. jpackage's MSI upgrade table only accepts strictly higher versions, so Windows Installer would refuse the stable over an installed beta and the tester would have to uninstall first. Betas therefore stay ZIP-only like nightlies; the build, upload and download steps are skipped for beta- tags, and the release action tolerates the missing asset path. Manual, changelog and project context say so. Raised by the Codex review on #111. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
176 lines
7.9 KiB
Kotlin
176 lines
7.9 KiB
Kotlin
plugins {
|
|
java
|
|
alias(libs.plugins.kotlinJvm)
|
|
alias(libs.plugins.composeCompiler)
|
|
alias(libs.plugins.composeMultiplatform)
|
|
}
|
|
|
|
kotlin {
|
|
jvmToolchain(21)
|
|
}
|
|
|
|
dependencies {
|
|
// JUnit and Mockito are test-only. Until #62 a few test classes sat under src/main/java
|
|
// and dragged both -- plus byte-buddy and objenesis -- into lib/app of every app image.
|
|
implementation(libs.jetbrains.annotations)
|
|
implementation(project(":core"))
|
|
implementation(compose.desktop.currentOs)
|
|
implementation(compose.material3)
|
|
implementation(libs.sqlite.jdbc)
|
|
implementation(libs.jlayer)
|
|
/*
|
|
* The Compose UI test harness, used through runComposeUiTest { } from ordinary Jupiter
|
|
* tests. Deliberately NOT ui-test-junit4: that one brings the vintage engine, and two
|
|
* engines on one classpath is how half a suite silently stops being run. Checked after
|
|
* adding this: the only engine resolved is junit-jupiter-engine. JUnit 4 classes are on
|
|
* the test classpath, but they were before this too — something else pulls them
|
|
* transitively — and with no vintage engine nothing executes them.
|
|
*
|
|
* The accessor is marked experimental by the Compose Gradle plugin, so the opt-in sits
|
|
* here — one place, for one dependency. What it buys is the only mechanism that can
|
|
* answer "did the value reach the screen": the state-level tests and the two settings
|
|
* coverage nets check that a setter is called, which is a different question.
|
|
*/
|
|
@OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)
|
|
testImplementation(compose.uiTest)
|
|
testImplementation(libs.junit.jupiter)
|
|
testImplementation(libs.mockito.core)
|
|
testImplementation(libs.mockito.junit.jupiter)
|
|
// Gradle 9 no longer adds the launcher implicitly.
|
|
testRuntimeOnly(libs.junit.platform.launcher)
|
|
}
|
|
|
|
/*
|
|
* The translation files are turned into Kotlin before anything compiles, by the
|
|
* :i18n-generator module. Two consequences worth naming:
|
|
*
|
|
* - A broken translation fails `./gradlew classes`, which is what the PR workflow already
|
|
* runs -- so a contributor's mistake is reported in their pull request without a new
|
|
* workflow existing for it.
|
|
* - The generator is reached through its own configuration, not through `implementation`, so
|
|
* it stays out of the application's runtime classpath and out of the jar.
|
|
*/
|
|
val i18nGenerator: Configuration by configurations.creating
|
|
|
|
dependencies {
|
|
i18nGenerator(project(":i18n-generator"))
|
|
}
|
|
|
|
val generatedI18nDirectory = layout.buildDirectory.dir("generated/i18n")
|
|
|
|
val generateStrings by tasks.registering(JavaExec::class) {
|
|
description = "Generates Strings.kt from app-desktop/src/main/i18n"
|
|
group = "build"
|
|
|
|
classpath = i18nGenerator
|
|
mainClass.set("kst4contest.i18n.GeneratorMain")
|
|
|
|
val input = layout.projectDirectory.dir("src/main/i18n")
|
|
inputs.dir(input)
|
|
outputs.dir(generatedI18nDirectory)
|
|
|
|
argumentProviders.add(CommandLineArgumentProvider {
|
|
listOf(input.asFile.absolutePath, generatedI18nDirectory.get().asFile.absolutePath)
|
|
})
|
|
}
|
|
|
|
kotlin.sourceSets["main"].kotlin.srcDir(generatedI18nDirectory)
|
|
|
|
tasks.named("compileKotlin") { dependsOn(generateStrings) }
|
|
|
|
compose.desktop {
|
|
application {
|
|
// Main forwards to Kst4ContestApplication.main. It was a workaround for
|
|
// launching a JavaFX Application subclass from the classpath; it stays because
|
|
// the packaged launchers and the AUR/Flatpak wrappers name this class.
|
|
mainClass = "kst4contest.view.Main"
|
|
|
|
nativeDistributions {
|
|
/*
|
|
* Only the formats the building machine can actually produce. Compose rejects a
|
|
* foreign one while CONFIGURING the project, not while packaging it, so a list
|
|
* naming all five made every Gradle task fail on macOS and Windows — including
|
|
* `run` and `test`, which package nothing. The build was Linux-only without
|
|
* anyone having decided that.
|
|
*/
|
|
val host = org.gradle.internal.os.OperatingSystem.current()
|
|
targetFormats(
|
|
*when {
|
|
host.isMacOsX -> arrayOf(
|
|
org.jetbrains.compose.desktop.application.dsl.TargetFormat.Dmg,
|
|
)
|
|
host.isWindows -> arrayOf(
|
|
org.jetbrains.compose.desktop.application.dsl.TargetFormat.Msi,
|
|
)
|
|
else -> arrayOf(
|
|
org.jetbrains.compose.desktop.application.dsl.TargetFormat.AppImage,
|
|
org.jetbrains.compose.desktop.application.dsl.TargetFormat.Deb,
|
|
org.jetbrains.compose.desktop.application.dsl.TargetFormat.Rpm,
|
|
)
|
|
}
|
|
)
|
|
/*
|
|
* jlink must come from JDK 21, not from whatever the Gradle JVM is.
|
|
* jdk.jsobject was removed after 21, and this machine has 26 on PATH;
|
|
* Etappe 1 hit the same wall with jpackage.
|
|
*/
|
|
javaHome = javaToolchains.launcherFor {
|
|
languageVersion.set(JavaLanguageVersion.of(21))
|
|
}.get().metadata.installationPath.asFile.absolutePath
|
|
|
|
/*
|
|
* KST4Contest and not praktiKST: this one name drives the app-image
|
|
* directory, the launcher, the .app bundle, the bundled icon file
|
|
* and -- lowercased -- the deb/rpm package name. Every packaging
|
|
* consumer already uses KST4Contest there (AppRun, the Arch and
|
|
* Flatpak wrappers, the AUR PKGBUILDs, build-signed-dmg.sh), and the
|
|
* shipped deb/rpm must keep the package identity "kst4contest" so an
|
|
* installed copy still upgrades.
|
|
*/
|
|
packageName = "KST4Contest"
|
|
packageVersion = providers.gradleProperty("composePackageVersion").get()
|
|
|
|
/*
|
|
* Kept from the former module-info.java. jdk.jsobject went with the map
|
|
* bridge: it carried netscape.javascript, and nothing imports that any
|
|
* more. jdk.unsupported stays — its justification used to be the JavaFX
|
|
* Marlin renderer, but sun.misc.Unsafe is reached by other dependencies
|
|
* too, and dropping it is a separate decision from removing JavaFX.
|
|
*/
|
|
modules(
|
|
"java.desktop", "java.net.http", "java.sql",
|
|
"jdk.crypto.ec", "jdk.net",
|
|
"jdk.xml.dom", "jdk.unsupported",
|
|
)
|
|
|
|
linux { iconFile.set(rootProject.file("packaging/icons/kst4contest.png")) }
|
|
windows {
|
|
iconFile.set(rootProject.file("packaging/icons/kst4contest.ico"))
|
|
/*
|
|
* The MSI installer is built for tagged releases only; nightlies ship
|
|
* the portable ZIP alone, because an MSI always carries the plain
|
|
* composePackageVersion and two nightly MSIs could not be told apart.
|
|
* It installs per user, so it needs neither administrator rights nor
|
|
* a UAC prompt, and it adds a Start menu group and a desktop shortcut.
|
|
*
|
|
* upgradeUuid must NEVER change: Windows Installer uses it to
|
|
* recognise an installed KST4Contest and replace it in place when a
|
|
* newer MSI runs. A different value would install the new release
|
|
* side by side with the old one instead of upgrading it.
|
|
*/
|
|
upgradeUuid = "3375071a-25ba-468e-9ae5-8296bc535462"
|
|
menu = true
|
|
menuGroup = "KST4Contest"
|
|
shortcut = true
|
|
perUserInstall = true
|
|
dirChooser = true
|
|
}
|
|
macOS {
|
|
iconFile.set(rootProject.file("packaging/icons/kst4contest.icns"))
|
|
// Must stay de.x08.KST4Contest: build-signed-dmg.sh signs against it.
|
|
bundleID = "de.x08.KST4Contest"
|
|
}
|
|
}
|
|
}
|
|
}
|