Open sebkur opened 1 year ago
Here's an official MSIX overview page
It might be possible to use microsoft/msix-packaging, an official cross-platform C library for packaging MSIX packages. There are samples within that repository. The API seems to be documented here.
We use MSIXHero if Windows-only would be okay. I can post our gradle config, which builds and signs a msix file via MSIXHero. It also creates an .appinstaller
file (basically just XML) for auto-update functionality.
Windows-only is not optimal, ideally we find a cross-platform solution. Still curious to see your configuration file.
val appName = "ABC"
val appDescription = "Lorem Ipsum"
val appPackage = "org.example.abc"
val appVersion = "1.2.3"
val msixFileName = "$appName-${appVersion}.msix"
val publisherName = "example Company"
val publisherCN = "CN=xample Company, O=xample Company, L=Bla, S=Bla, C=US"
val urlSchema = "abc"
val logoFileName = "logo.png"
val logo44FileName = "logo_44.png"
val logo155FileName = "logo_155.png"
val distributionDir: Provider<Directory> = compose.desktop.nativeApplication.distributions.outputBaseDir
.map { it.dir("main").dir("app") }
val msixBuildDir: File = layout.buildDirectory.dir("msix").get().asFile.apply { createDirectory() }
val createMsixManifest by tasks.registering {
doLast {
distributionDir.get().dir(appName).file("AppxManifest.xml").asFile.apply {
createNewFile()
writeText(
"""
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap10 rescap">
<Identity Name="$appPackage" Publisher="$publisherCN" Version="${appVersion}.0" ProcessorArchitecture="x64" />
<Properties>
<DisplayName>$appName</DisplayName>
<PublisherDisplayName>$publisherName</PublisherDisplayName>
<Description>$appDescription</Description>
<Logo>$logoFileName</Logo>
<uap10:PackageIntegrity>
<uap10:Content Enforcement="on" />
</uap10:PackageIntegrity>
</Properties>
<Resources>
<Resource Language="de-de" />
</Resources>
<Dependencies>
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.22000.1" />
</Dependencies>
<Capabilities>
<rescap:Capability Name="runFullTrust" />
</Capabilities>
<Applications>
<Application
Id="$appPackage"
Executable="$appName.exe"
EntryPoint="Windows.FullTrustApplication">
<uap:VisualElements DisplayName="$appName" Description="$appDescription" Square150x150Logo="$logo155FileName"
Square44x44Logo="$logo44FileName" BackgroundColor="white" />
<Extensions>
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="$urlSchema" />
</uap:Extension>
</Extensions>
</Application>
</Applications>
</Package>
""".trimIndent()
)
}
}
dependsOn(tasks.getByName("createDistributable"))
onlyIf { isWindows }
}
val createMsixAppinstaller by tasks.registering {
doLast {
val msixBaseUrl = requireNotNull(System.getenv("MSIX_BASE_URL"))
val appinstallerFileName = "$appName.appinstaller"
msixBuildDir.resolve(appinstallerFileName).apply {
createNewFile()
writeText(
"""
<?xml version="1.0" encoding="utf-8"?>
<AppInstaller
xmlns="http://schemas.microsoft.com/appx/appinstaller/2018"
Version="${appVersion}.0"
Uri="$msixBaseUrl/$appinstallerFileName">
<MainPackage
Name="$appPackage"
Publisher="$publisherCN"
Version="${appVersion}.0"
ProcessorArchitecture="x64"
Uri="$msixBaseUrl/$msixFileName" />
<UpdateSettings>
<OnLaunch
HoursBetweenUpdateChecks="12"
UpdateBlocksActivation="true"
ShowPrompt="true" />
<ForceUpdateFromAnyVersion>false</ForceUpdateFromAnyVersion>
<AutomaticBackgroundTask />
</UpdateSettings>
</AppInstaller>
""".trimIndent()
)
}
}
onlyIf { isWindows && isCI }
}
val copyMsixLogos by tasks.registering(Copy::class) {
from(projectDir.resolve("src").resolve("desktopMain").resolve("resources")) {
include(logoFileName, logo44FileName, logo155FileName)
}
into(distributionDir.get().dir(appName).asFile)
dependsOn(tasks.getByName("createDistributable"))
onlyIf { isWindows }
}
val msixHeroPack by tasks.registering(Exec::class) {
workingDir(msixBuildDir)
executable = "MSIXHeroCLI.exe"
args(
"pack",
"--directory", distributionDir.get().dir(appName).asFile.absolutePath,
"--package", msixFileName,
)
dependsOn(tasks.getByName("createDistributable"), createMsixManifest, copyMsixLogos)
onlyIf { isWindows }
}
val msixHeroSign by tasks.registering(Exec::class) {
workingDir(msixBuildDir)
executable = "MSIXHeroCLI.exe"
args(
"sign",
"--file", System.getenv("CODE_SIGNING_CERT_FILE")?.let { rootDir.resolve(it).absolutePath } ?: "",
"--password", System.getenv("CODE_SIGNING_CERT_FILE_PASSWORD") ?: "",
"--timestamp", System.getenv("CODE_SIGNING_TIMESTAMP_SERVER") ?: "",
msixFileName
)
dependsOn(msixHeroPack)
onlyIf { isWindows }
}
val packageMsix by tasks.registering {
group = "compose desktop"
dependsOn(msixHeroPack, msixHeroSign, createMsixAppinstaller)
onlyIf { isWindows }
}
It works very good including auto update :)
Thanks. I guess it's worth a try to see if this maybe even works via wine on Linux.
We might be able to use MSIX-Hero (Github, Website)