Publish Kotlin Multiplatform Applications with Conveyor

This guide outlines a real-world configuration for publishing a Kotlin Multiplatform application with Hydraulic Conveyor.

For this tutorial, I am using maxDEV — a cross-platform (supports Mac, Windows, Linux) developer productivity app as an example.

Conveyor configuration

conveyor.conf:

include required("https://raw.githubusercontent.com/hydraulic-software/conveyor/master/configs/jvm/extract-native-libraries.conf")
include "/stdlib/jdk/17/amazon.conf"

app {
  fsname = maxdev
  display-name = maxDEV
  updates = aggressive

  icons = "icons/icon.png"
  windows.icons = "icons/icon.ico"

  mac.notarization {
	app-specific-password = "!!!!-!!!!-!!!!-!!!"
  }

  jvm.extract-native-libraries = true

  site.base-url = "https://kenanbek.github.io/maxDEV/download.html"
  site.github.oauth-token = "!!!"
  site.github.pages-branch = "gh-pages"
}

conveyor.compatibility-level = 17
conveyor.billing-email = "!!!"
conveyor.license-key = "!!!"

For Apple notarization, check this link.

For the Conveyor license key and billing, you need a Hydraulic subscription.

Gradle configuraitons

libs.versions:

[versions]
kotlin = "2.1.10"
kotlinx-coroutines = "1.10.1"
compose-multiplatform = "1.7.0"
androidx-lifecycle = "2.8.4"
junit = "4.13.2"
ktor = "3.1.1"
logback = "1.5.17"
ktlint-gradle = "11.6.1"
ktlint-ruleset-compose = "0.4.22"
conveyor = "1.12"

[plugins]
conveyor = { id = "dev.hydraulic.conveyor", version.ref = "conveyor" }

build.gradle.kts (root):

plugins {
    alias(libs.plugins.conveyor) apply false
}

build.gradle.kts (desktop app):

plugins {
    alias(libs.plugins.conveyor)
}

compose.desktop {
    application {
        mainClass = "com.appbaza.maxdev.MainKt"

        nativeDistributions {
            targetFormats(TargetFormat.Dmg, TargetFormat.Exe, TargetFormat.Msi, TargetFormat.Deb, TargetFormat.Rpm)
            packageName = "com.appbaza.maxdev"
            packageVersion = "1.0.0"

            macOS {
                bundleID = "com.appbaza.maxdev"
                signing {
                    sign.set(true)
                    identity.set("!!!")
                }
                notarization {
                    appleID.set("!!!")
                    password.set("!!!")
                    teamID.set("!!!")
                }
            }
        }
    }
}

Updates

I configured the application to GitHub Pages website for distributing updates. For manual updates, I am including a static page on the application's website.

Conveyor also supports other options.

At the current state of the project, I am using the aggressive update mode. Once the app reaches a more stable phase, I might consider the background update mode.

You can read more about update modes here.

Configuring the update mode is as easy as the following configuration in your conveyor.conf:

app {
  fsname = maxdev
  display-name = maxDEV
  updates = aggressive
}

This is another excellent documentation from Hydraulic about updates in general:

Understanding updates - Hydraulic Conveyor
User guide for the Conveyor packaging tool.

Release

I use the following procedure for preparing a new release for maxDEV:

  1. Finalize code changes.
  2. Bump the version number in build.gradle.kts (:composeApp)
  3. Prepare package (./gradlew clean package)
  4. Prepare download assets (conveyor make site)
  5. Upload site (conveyor make copied-site)


You can reach out to me if you need consultancy and development support for your Kotlin cross-platform desktop projects: send an email to KEN at AppBaza.com.