feat(desktop): version source-of-truth + RPM + AppImage packaging
Phase 1+2 of multi-platform distribution plan:
- gradle/libs.versions.toml: add `app = "1.08.0"` as single source of truth
- Root build.gradle: allprojects { version = libs.versions.app.get() }
- amethyst/build.gradle: versionName from catalog (versionCode stays local)
- desktopApp/build.gradle.kts: drop hardcoded "1.0.0"; inherit project.version;
add TargetFormat.Rpm; add linux DSL (menuGroup, appCategory, debMaintainer,
rpmLicenseType, rpmPackageVersion with dashes stripped)
- desktopApp/build.gradle.kts: new createReleaseAppImage task wrapping
createReleaseDistributable with linuxdeploy (TargetFormat.AppImage is
broken in Compose 1.10.x — CMP-7101)
- packaging/appimage/: AppRun launcher (sets LD_LIBRARY_PATH for bundled VLC),
amethyst.desktop XDG entry, 512x512 icon extracted from icon.icns
- scripts/asset-name.sh: single source for release asset naming contract
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.jetbrainsKotlinJvm)
|
||||
@@ -7,6 +9,11 @@ plugins {
|
||||
id("ir.mahozad.vlc-setup") version "0.1.0"
|
||||
}
|
||||
|
||||
// RPM rejects dashes in version strings — strip prerelease suffix for Linux RPM only.
|
||||
// Other formats accept full semver (DEB uses ~rc1, DMG/MSI accept bare versions).
|
||||
val appVersion: String = project.version.toString()
|
||||
val appVersionRpm: String = appVersion.substringBefore("-")
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
kotlin.srcDir("src/jvmMain/kotlin")
|
||||
@@ -88,11 +95,11 @@ compose.desktop {
|
||||
|
||||
nativeDistributions {
|
||||
appResourcesRootDir.set(project.layout.projectDirectory.dir("src/jvmMain/appResources"))
|
||||
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
|
||||
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb, TargetFormat.Rpm)
|
||||
modules("java.management") // Required by kmp-tor TorRuntime
|
||||
|
||||
packageName = "Amethyst"
|
||||
packageVersion = "1.0.0"
|
||||
packageVersion = appVersion
|
||||
description = "Nostr client for desktop"
|
||||
vendor = "Amethyst Contributors"
|
||||
|
||||
@@ -109,6 +116,12 @@ compose.desktop {
|
||||
|
||||
linux {
|
||||
iconFile.set(project.file("src/jvmMain/resources/icon.png"))
|
||||
menuGroup = "Network"
|
||||
appCategory = "Network"
|
||||
debMaintainer = "vitor@vitorpamplona.com"
|
||||
rpmLicenseType = "MIT"
|
||||
// RPM version field rejects dashes; strip prerelease suffix for RPM builds.
|
||||
rpmPackageVersion = appVersionRpm
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,3 +139,68 @@ vlcSetup {
|
||||
tasks.named("spotlessKotlin") {
|
||||
mustRunAfter("vlcSetup")
|
||||
}
|
||||
|
||||
// --- AppImage packaging (Linux) ---
|
||||
//
|
||||
// Compose Multiplatform's TargetFormat.AppImage is known-broken in 1.10.x (CMP-7101).
|
||||
// Instead: wrap `createReleaseDistributable` output with `linuxdeploy` (which
|
||||
// auto-bundles libraries, handles rpath, and calls appimagetool internally).
|
||||
//
|
||||
// Build inputs live in packaging/appimage/:
|
||||
// - AppRun shell launcher (sets LD_LIBRARY_PATH including bundled VLC)
|
||||
// - amethyst.desktop XDG desktop entry
|
||||
// - amethyst.png 512x512 icon
|
||||
//
|
||||
// linuxdeploy binary is fetched by CI (SHA-verified) into packaging/appimage/
|
||||
// as linuxdeploy-x86_64.AppImage. BUILDING.md documents local-dev fetch.
|
||||
val createReleaseAppImage by tasks.registering(Exec::class) {
|
||||
group = "compose desktop"
|
||||
description = "Bundle createReleaseDistributable output into a Linux AppImage via linuxdeploy."
|
||||
dependsOn("createReleaseDistributable")
|
||||
|
||||
val distDir = layout.buildDirectory.dir("compose/binaries/main-release/app/Amethyst")
|
||||
val appDir = layout.buildDirectory.dir("appimage/Amethyst.AppDir")
|
||||
val outFile = layout.buildDirectory.file("appimage/Amethyst-$appVersion-x86_64.AppImage")
|
||||
val toolRoot = layout.projectDirectory.dir("../packaging/appimage")
|
||||
val linuxdeployTool = toolRoot.file("linuxdeploy-x86_64.AppImage")
|
||||
|
||||
inputs.dir(distDir)
|
||||
inputs.dir(toolRoot)
|
||||
outputs.file(outFile)
|
||||
|
||||
doFirst {
|
||||
val dir = appDir.get().asFile
|
||||
dir.deleteRecursively()
|
||||
dir.mkdirs()
|
||||
copy {
|
||||
from(distDir) { into("usr") }
|
||||
from(toolRoot.file("AppRun")) {
|
||||
rename { "AppRun" }
|
||||
filePermissions { unix("0755") }
|
||||
}
|
||||
from(toolRoot.file("amethyst.desktop"))
|
||||
from(toolRoot.file("amethyst.png"))
|
||||
into(dir)
|
||||
}
|
||||
// DirIcon is used by desktop integrations (file managers, AppImageLauncher)
|
||||
val dirIcon = File(dir, ".DirIcon")
|
||||
if (dirIcon.exists()) dirIcon.delete()
|
||||
Files.createSymbolicLink(dirIcon.toPath(), File("amethyst.png").toPath())
|
||||
|
||||
if (!linuxdeployTool.asFile.canExecute()) {
|
||||
linuxdeployTool.asFile.setExecutable(true)
|
||||
}
|
||||
}
|
||||
|
||||
commandLine(
|
||||
linuxdeployTool.asFile.absolutePath,
|
||||
"--appdir", appDir.get().asFile.absolutePath,
|
||||
"--output", "appimage",
|
||||
"--desktop-file", "${appDir.get().asFile}/amethyst.desktop",
|
||||
"--icon-file", "${appDir.get().asFile}/amethyst.png",
|
||||
)
|
||||
environment("OUTPUT", outFile.get().asFile.absolutePath)
|
||||
environment("ARCH", "x86_64")
|
||||
// Suppress linuxdeploy's verbose library-scanner output; keep errors.
|
||||
environment("LINUXDEPLOY_OUTPUT_VERSION", appVersion)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user