mirror of
https://github.com/amir1376/ab-download-manager.git
synced 2025-02-20 11:43:24 +08:00
add nsis installer
This commit is contained in:
parent
a1c8f54bc7
commit
2788b6cc76
@ -17,4 +17,5 @@ dependencies{
|
|||||||
implementation(libs.semver)
|
implementation(libs.semver)
|
||||||
implementation("ir.amirab.util:platform:1")
|
implementation("ir.amirab.util:platform:1")
|
||||||
implementation("ir.amirab.plugin:git-version-plugin:1")
|
implementation("ir.amirab.plugin:git-version-plugin:1")
|
||||||
|
implementation("ir.amirab.plugin:installer-plugin:1")
|
||||||
}
|
}
|
@ -1,21 +1,20 @@
|
|||||||
package buildlogic
|
package buildlogic
|
||||||
|
|
||||||
import io.github.z4kn4fein.semver.Version
|
import io.github.z4kn4fein.semver.Version
|
||||||
|
import ir.amirab.installer.InstallerTargetFormat
|
||||||
import ir.amirab.util.platform.Platform
|
import ir.amirab.util.platform.Platform
|
||||||
import org.jetbrains.compose.desktop.application.dsl.JvmApplicationDistributions
|
|
||||||
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
|
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
|
||||||
object CiUtils {
|
object CiUtils {
|
||||||
fun getTargetFileName(
|
fun getTargetFileName(
|
||||||
packageName: String,
|
packageName: String,
|
||||||
appVersion: Version,
|
appVersion: Version,
|
||||||
target: TargetFormat,
|
target: InstallerTargetFormat?,
|
||||||
): String {
|
): String {
|
||||||
val fileExtension = when (target) {
|
val fileExtension = when (target) {
|
||||||
// we use archived for app image distribution
|
// we use archived for app image distribution ( app image is a folder actually so there is no installer so we zip it instead)
|
||||||
TargetFormat.AppImage -> {
|
null -> {
|
||||||
when (Platform.getCurrentPlatform()) {
|
when (Platform.getCurrentPlatform()) {
|
||||||
Platform.Desktop.Linux -> "tar.gz"
|
Platform.Desktop.Linux -> "tar.gz"
|
||||||
Platform.Desktop.MacOS -> "tar.gz"
|
Platform.Desktop.MacOS -> "tar.gz"
|
||||||
@ -28,7 +27,7 @@ object CiUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val platformName = when (target) {
|
val platformName = when (target) {
|
||||||
TargetFormat.AppImage -> Platform.getCurrentPlatform()
|
null -> Platform.getCurrentPlatform()
|
||||||
else -> {
|
else -> {
|
||||||
val packageFileExt = target.fileExtensionWithoutDot()
|
val packageFileExt = target.fileExtensionWithoutDot()
|
||||||
requireNotNull(Platform.fromExecutableFileExtension(packageFileExt)) {
|
requireNotNull(Platform.fromExecutableFileExtension(packageFileExt)) {
|
||||||
@ -41,9 +40,10 @@ object CiUtils {
|
|||||||
|
|
||||||
fun getFileOfPackagedTarget(
|
fun getFileOfPackagedTarget(
|
||||||
baseOutputDir: File,
|
baseOutputDir: File,
|
||||||
target: TargetFormat,
|
target: InstallerTargetFormat,
|
||||||
): File {
|
): File {
|
||||||
val folder = baseOutputDir.resolve(target.outputDirName)
|
val folder = baseOutputDir
|
||||||
|
// val folder = baseOutputDir.resolve(target.outputDirName)
|
||||||
val exeFile = kotlin.runCatching {
|
val exeFile = kotlin.runCatching {
|
||||||
folder.walk().first {
|
folder.walk().first {
|
||||||
it.name.endsWith(target.fileExt)
|
it.name.endsWith(target.fileExt)
|
||||||
@ -89,7 +89,7 @@ object CiUtils {
|
|||||||
fun movePackagedAndCreateSignature(
|
fun movePackagedAndCreateSignature(
|
||||||
appVersion: Version,
|
appVersion: Version,
|
||||||
packageName: String,
|
packageName: String,
|
||||||
target: TargetFormat,
|
target: InstallerTargetFormat,
|
||||||
basePackagedAppsDir: File,
|
basePackagedAppsDir: File,
|
||||||
outputDir: File,
|
outputDir: File,
|
||||||
) {
|
) {
|
||||||
@ -148,4 +148,4 @@ object CiUtils {
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TargetFormat.fileExtensionWithoutDot() = fileExt.substring(".".length)
|
private fun InstallerTargetFormat.fileExtensionWithoutDot() = fileExt.substring(".".length)
|
20
compositeBuilds/plugins/installer-plugin/build.gradle.kts
Normal file
20
compositeBuilds/plugins/installer-plugin/build.gradle.kts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
plugins {
|
||||||
|
`kotlin-dsl`
|
||||||
|
}
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
version = 1
|
||||||
|
group = "ir.amirab.plugin"
|
||||||
|
dependencies {
|
||||||
|
implementation("ir.amirab.util:platform:1")
|
||||||
|
implementation(libs.handlebarsJava)
|
||||||
|
}
|
||||||
|
gradlePlugin {
|
||||||
|
plugins {
|
||||||
|
create("installer-plugin") {
|
||||||
|
id = "ir.amirab.installer-plugin"
|
||||||
|
implementationClass = "ir.amirab.installer.InstallerPlugin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
package ir.amirab.installer
|
||||||
|
|
||||||
|
import ir.amirab.installer.extensiion.InstallerPluginExtension
|
||||||
|
import ir.amirab.installer.tasks.windows.NsisTask
|
||||||
|
import ir.amirab.installer.utils.Constants
|
||||||
|
import ir.amirab.util.platform.Platform
|
||||||
|
import org.gradle.api.Plugin
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.kotlin.dsl.create
|
||||||
|
import org.gradle.kotlin.dsl.register
|
||||||
|
|
||||||
|
class InstallerPlugin : Plugin<Project> {
|
||||||
|
override fun apply(target: Project) {
|
||||||
|
val extension = target.extensions.create("installerPlugin", InstallerPluginExtension::class)
|
||||||
|
target.afterEvaluate {
|
||||||
|
registerTasks(target, extension)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerTasks(
|
||||||
|
project: Project,
|
||||||
|
extension: InstallerPluginExtension
|
||||||
|
) {
|
||||||
|
val windowConfig = extension.windowsConfig
|
||||||
|
val createInstallerTaskName = Constants.CREATE_INSTALLER_TASK_NAME
|
||||||
|
val createInstallerNsisTaskName = "${createInstallerTaskName}Nsis"
|
||||||
|
if (windowConfig != null) {
|
||||||
|
project.tasks
|
||||||
|
.register<NsisTask>(createInstallerNsisTaskName)
|
||||||
|
.configure {
|
||||||
|
dependsOn(extension.taskDependencies.toTypedArray())
|
||||||
|
this.nsisTemplate.set(requireNotNull(windowConfig.nsisTemplate) { "Nsis Template not provided" })
|
||||||
|
this.commonParams.set(windowConfig)
|
||||||
|
this.extraParams.set(windowConfig.extraParams)
|
||||||
|
this.destFolder.set(extension.outputFolder.get().asFile)
|
||||||
|
this.outputFileName.set(requireNotNull(windowConfig.outputFileName) { " outputFileName not provided " })
|
||||||
|
this.sourceFolder.set(requireNotNull(windowConfig.inputDir) { "inputDir not provided" })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
project.tasks.register(createInstallerTaskName) {
|
||||||
|
// when we want to create installer we need to prepare its input first!
|
||||||
|
when (val platform = Platform.getCurrentPlatform()) {
|
||||||
|
Platform.Desktop.Linux -> {
|
||||||
|
// nothing yet
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.Desktop.MacOS -> {
|
||||||
|
// nothing yet
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.Desktop.Windows -> {
|
||||||
|
if (windowConfig != null) {
|
||||||
|
dependsOn(createInstallerNsisTaskName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> error("unsupported platform: $platform")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package ir.amirab.installer
|
||||||
|
|
||||||
|
import ir.amirab.util.platform.Platform
|
||||||
|
|
||||||
|
|
||||||
|
enum class InstallerTargetFormat(
|
||||||
|
val id: String,
|
||||||
|
val targetOS: Platform,
|
||||||
|
) {
|
||||||
|
Deb("deb", Platform.Desktop.Linux),
|
||||||
|
Rpm("rpm", Platform.Desktop.Linux),
|
||||||
|
Dmg("dmg", Platform.Desktop.MacOS),
|
||||||
|
Pkg("pkg", Platform.Desktop.MacOS),
|
||||||
|
Exe("exe", Platform.Desktop.Windows),
|
||||||
|
Msi("msi", Platform.Desktop.Windows);
|
||||||
|
|
||||||
|
val isCompatibleWithCurrentOS: Boolean by lazy { isCompatibleWith(Platform.getCurrentPlatform()) }
|
||||||
|
|
||||||
|
fun isCompatibleWith(os: Platform): Boolean = os == targetOS
|
||||||
|
|
||||||
|
val outputDirName: String
|
||||||
|
get() = id
|
||||||
|
|
||||||
|
val fileExt: String
|
||||||
|
get() {
|
||||||
|
return ".$id"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
package ir.amirab.installer.extensiion
|
||||||
|
|
||||||
|
import ir.amirab.installer.InstallerTargetFormat
|
||||||
|
import ir.amirab.installer.utils.Constants
|
||||||
|
import ir.amirab.util.platform.Platform
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.Task
|
||||||
|
import org.gradle.api.file.DirectoryProperty
|
||||||
|
import org.gradle.api.tasks.TaskProvider
|
||||||
|
import java.io.File
|
||||||
|
import java.io.Serializable
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
abstract class InstallerPluginExtension {
|
||||||
|
@get:Inject
|
||||||
|
internal abstract val project: Project
|
||||||
|
|
||||||
|
abstract val outputFolder: DirectoryProperty
|
||||||
|
|
||||||
|
internal val taskDependencies = mutableListOf<Any>()
|
||||||
|
|
||||||
|
fun dependsOn(vararg tasks: Any) {
|
||||||
|
taskDependencies.addAll(tasks)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal var windowsConfig: WindowsConfig? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun windows(
|
||||||
|
config: WindowsConfig.() -> Unit
|
||||||
|
) {
|
||||||
|
if (Platform.getCurrentPlatform() != Platform.Desktop.Windows) return
|
||||||
|
val windowsConfig = if (this.windowsConfig == null) {
|
||||||
|
WindowsConfig().also {
|
||||||
|
this.windowsConfig = it
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.windowsConfig!!
|
||||||
|
}
|
||||||
|
windowsConfig.config()
|
||||||
|
}
|
||||||
|
|
||||||
|
val createInstallerTask: TaskProvider<Task> by lazy {
|
||||||
|
project.tasks.named(Constants.CREATE_INSTALLER_TASK_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isThisPlatformSupported() = when (Platform.getCurrentPlatform()) {
|
||||||
|
Platform.Desktop.Windows -> windowsConfig != null
|
||||||
|
else -> {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCreatedInstallerTargetFormats(): List<InstallerTargetFormat> {
|
||||||
|
return buildList<InstallerTargetFormat> {
|
||||||
|
when (Platform.getCurrentPlatform()) {
|
||||||
|
Platform.Desktop.Windows -> {
|
||||||
|
if (windowsConfig != null) {
|
||||||
|
add(InstallerTargetFormat.Exe)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class WindowsConfig(
|
||||||
|
var appName: String? = null,
|
||||||
|
var appDisplayName: String? = null,
|
||||||
|
var appVersion: String? = null,
|
||||||
|
var appDisplayVersion: String? = null,
|
||||||
|
var iconFile: File? = null,
|
||||||
|
var licenceFile: File? = null,
|
||||||
|
|
||||||
|
var outputFileName: String? = null,
|
||||||
|
|
||||||
|
var inputDir: File? = null,
|
||||||
|
|
||||||
|
var nsisTemplate: File? = null,
|
||||||
|
|
||||||
|
var extraParams: Map<String, Any> = emptyMap()
|
||||||
|
) : Serializable
|
||||||
|
|
@ -0,0 +1,91 @@
|
|||||||
|
package ir.amirab.installer.tasks.windows
|
||||||
|
|
||||||
|
import com.github.jknack.handlebars.Context
|
||||||
|
import com.github.jknack.handlebars.Handlebars
|
||||||
|
import ir.amirab.installer.extensiion.WindowsConfig
|
||||||
|
import org.gradle.api.DefaultTask
|
||||||
|
import org.gradle.api.file.DirectoryProperty
|
||||||
|
import org.gradle.api.provider.MapProperty
|
||||||
|
import org.gradle.api.provider.Property
|
||||||
|
import org.gradle.api.tasks.Input
|
||||||
|
import org.gradle.api.tasks.InputDirectory
|
||||||
|
import org.gradle.api.tasks.InputFile
|
||||||
|
import org.gradle.api.tasks.Internal
|
||||||
|
import org.gradle.api.tasks.OutputDirectory
|
||||||
|
import org.gradle.api.tasks.TaskAction
|
||||||
|
import org.gradle.kotlin.dsl.mapProperty
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
abstract class NsisTask : DefaultTask() {
|
||||||
|
|
||||||
|
@get:InputDirectory
|
||||||
|
abstract val sourceFolder: DirectoryProperty
|
||||||
|
|
||||||
|
@get:OutputDirectory
|
||||||
|
abstract val destFolder: DirectoryProperty
|
||||||
|
|
||||||
|
@get:Input
|
||||||
|
abstract val outputFileName: Property<String>
|
||||||
|
|
||||||
|
@get:InputFile
|
||||||
|
abstract val nsisTemplate: Property<File>
|
||||||
|
|
||||||
|
@get:Input
|
||||||
|
abstract val commonParams: Property<WindowsConfig>
|
||||||
|
|
||||||
|
@get:Input
|
||||||
|
val extraParams: MapProperty<String, Any> = project.objects.mapProperty<String, Any>()
|
||||||
|
|
||||||
|
@get:Internal
|
||||||
|
abstract val nsisExecutable: Property<File>
|
||||||
|
|
||||||
|
init {
|
||||||
|
nsisExecutable.convention(
|
||||||
|
project.provider { File("C:\\Program Files (x86)\\NSIS\\makensis.exe") }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createHandleBarContext(): Context {
|
||||||
|
val commonParams = commonParams.get()
|
||||||
|
val common = mapOf(
|
||||||
|
"app_name" to commonParams.appName!!,
|
||||||
|
"app_display_name" to commonParams.appDisplayName!!,
|
||||||
|
"app_version" to commonParams.appVersion!!,
|
||||||
|
"app_display_version" to commonParams.appDisplayVersion!!,
|
||||||
|
"license_file" to commonParams.licenceFile!!,
|
||||||
|
"icon_file" to commonParams.iconFile!!,
|
||||||
|
)
|
||||||
|
val overrides = mapOf(
|
||||||
|
"input_dir" to sourceFolder.get().asFile.absolutePath,
|
||||||
|
"output_file" to "${destFolder.file(outputFileName).get().asFile.path}.exe",
|
||||||
|
)
|
||||||
|
return Context.newContext(
|
||||||
|
extraParams
|
||||||
|
.get()
|
||||||
|
.plus(common)
|
||||||
|
.plus(overrides)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
fun run() {
|
||||||
|
val executable = nsisExecutable.get()
|
||||||
|
val scriptTemplate = nsisTemplate.get()
|
||||||
|
val handlebars = Handlebars()
|
||||||
|
val context = createHandleBarContext()
|
||||||
|
val script = handlebars.compileInline(
|
||||||
|
scriptTemplate.readText()
|
||||||
|
).apply(context)
|
||||||
|
logger.debug("NSIS Script:")
|
||||||
|
logger.debug(script)
|
||||||
|
project.exec {
|
||||||
|
executable(
|
||||||
|
executable,
|
||||||
|
)
|
||||||
|
args("-")
|
||||||
|
standardInput = ByteArrayInputStream(script.toByteArray())
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package ir.amirab.installer.utils
|
||||||
|
|
||||||
|
internal object Constants {
|
||||||
|
const val CREATE_INSTALLER_TASK_NAME = "createInstaller"
|
||||||
|
}
|
@ -6,3 +6,4 @@ dependencyResolutionManagement{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
include("git-version-plugin")
|
include("git-version-plugin")
|
||||||
|
include("installer-plugin")
|
@ -1,8 +1,10 @@
|
|||||||
import buildlogic.*
|
import buildlogic.*
|
||||||
import buildlogic.versioning.*
|
import buildlogic.versioning.*
|
||||||
|
import ir.amirab.installer.InstallerTargetFormat
|
||||||
import org.jetbrains.changelog.Changelog
|
import org.jetbrains.changelog.Changelog
|
||||||
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
|
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
|
||||||
import ir.amirab.util.platform.Platform
|
import ir.amirab.util.platform.Platform
|
||||||
|
import org.jetbrains.compose.desktop.application.dsl.TargetFormat.*
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id(MyPlugins.kotlin)
|
id(MyPlugins.kotlin)
|
||||||
@ -12,9 +14,9 @@ plugins {
|
|||||||
id(Plugins.changeLog)
|
id(Plugins.changeLog)
|
||||||
id(Plugins.ksp)
|
id(Plugins.ksp)
|
||||||
id(Plugins.aboutLibraries)
|
id(Plugins.aboutLibraries)
|
||||||
|
id("ir.amirab.installer-plugin")
|
||||||
// id(MyPlugins.proguardDesktop)
|
// id(MyPlugins.proguardDesktop)
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(libs.decompose)
|
implementation(libs.decompose)
|
||||||
implementation(libs.decompose.jbCompose)
|
implementation(libs.decompose.jbCompose)
|
||||||
@ -102,7 +104,7 @@ compose {
|
|||||||
mainClass = "$desktopPackageName.AppKt"
|
mainClass = "$desktopPackageName.AppKt"
|
||||||
nativeDistributions {
|
nativeDistributions {
|
||||||
modules("java.instrument", "jdk.unsupported")
|
modules("java.instrument", "jdk.unsupported")
|
||||||
targetFormats(TargetFormat.Msi, TargetFormat.Deb)
|
targetFormats(Msi, Deb)
|
||||||
if (Platform.getCurrentPlatform() == Platform.Desktop.Linux) {
|
if (Platform.getCurrentPlatform() == Platform.Desktop.Linux) {
|
||||||
// filekit library requires this module in linux.
|
// filekit library requires this module in linux.
|
||||||
modules("jdk.security.auth")
|
modules("jdk.security.auth")
|
||||||
@ -114,21 +116,21 @@ compose {
|
|||||||
val menuGroupName = getPrettifiedAppName()
|
val menuGroupName = getPrettifiedAppName()
|
||||||
licenseFile.set(rootProject.file("LICENSE"))
|
licenseFile.set(rootProject.file("LICENSE"))
|
||||||
linux {
|
linux {
|
||||||
debPackageVersion = getAppVersionStringForPackaging(TargetFormat.Deb)
|
debPackageVersion = getAppVersionStringForPackaging(Deb)
|
||||||
rpmPackageVersion = getAppVersionStringForPackaging(TargetFormat.Rpm)
|
rpmPackageVersion = getAppVersionStringForPackaging(Rpm)
|
||||||
appCategory = "Network"
|
appCategory = "Network"
|
||||||
iconFile = project.file("icons/icon.png")
|
iconFile = project.file("icons/icon.png")
|
||||||
menuGroup = menuGroupName
|
menuGroup = menuGroupName
|
||||||
shortcut = true
|
shortcut = true
|
||||||
}
|
}
|
||||||
macOS {
|
macOS {
|
||||||
pkgPackageVersion = getAppVersionStringForPackaging(TargetFormat.Pkg)
|
pkgPackageVersion = getAppVersionStringForPackaging(Pkg)
|
||||||
dmgPackageVersion = getAppVersionStringForPackaging(TargetFormat.Dmg)
|
dmgPackageVersion = getAppVersionStringForPackaging(Dmg)
|
||||||
iconFile = project.file("icons/icon.icns")
|
iconFile = project.file("icons/icon.icns")
|
||||||
}
|
}
|
||||||
windows {
|
windows {
|
||||||
exePackageVersion = getAppVersionStringForPackaging(TargetFormat.Exe)
|
exePackageVersion = getAppVersionStringForPackaging(Exe)
|
||||||
msiPackageVersion = getAppVersionStringForPackaging(TargetFormat.Msi)
|
msiPackageVersion = getAppVersionStringForPackaging(Msi)
|
||||||
upgradeUuid = properties["INSTALLER.WINDOWS.UPGRADE_UUID"]?.toString()
|
upgradeUuid = properties["INSTALLER.WINDOWS.UPGRADE_UUID"]?.toString()
|
||||||
iconFile = project.file("icons/icon.ico")
|
iconFile = project.file("icons/icon.ico")
|
||||||
console = false
|
console = false
|
||||||
@ -142,6 +144,32 @@ compose {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
installerPlugin {
|
||||||
|
dependsOn("createReleaseDistributable")
|
||||||
|
outputFolder.set(layout.buildDirectory.dir("custom-installer"))
|
||||||
|
windows {
|
||||||
|
appName = getAppName()
|
||||||
|
appDisplayName = getPrettifiedAppName()
|
||||||
|
appVersion = getAppVersionStringForPackaging(Exe)
|
||||||
|
appDisplayVersion = getAppVersionString()
|
||||||
|
inputDir = project.file("build/compose/binaries/main-release/app/${getAppName()}")
|
||||||
|
outputFileName = getAppName()
|
||||||
|
licenceFile = rootProject.file("LICENSE")
|
||||||
|
iconFile = project.file("icons/icon.ico")
|
||||||
|
nsisTemplate = project.file("resources/installer/nsis-script-template.nsi")
|
||||||
|
extraParams = mapOf(
|
||||||
|
"app_publisher" to "abdownloadmanager.com",
|
||||||
|
"app_version_with_build" to "${getAppVersionStringForPackaging(Exe)}.0",
|
||||||
|
"source_code_url" to "https://github.com/amir1376/ab-download-manager",
|
||||||
|
"project_website" to "www.abdownloadmanager.com",
|
||||||
|
"copyright" to "© 2024-present AB Download Manager App",
|
||||||
|
"header_image_file" to project.file("resources/installer/abdm-header-image.bmp"),
|
||||||
|
"sidebar_image_file" to project.file("resources/installer/abdm-sidebar-image.bmp")
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// generate a file with these constants
|
// generate a file with these constants
|
||||||
buildConfig {
|
buildConfig {
|
||||||
@ -240,27 +268,51 @@ val createDistributableAppArchive by tasks.registering {
|
|||||||
val createBinariesForCi by tasks.registering {
|
val createBinariesForCi by tasks.registering {
|
||||||
val nativeDistributions = compose.desktop.application.nativeDistributions
|
val nativeDistributions = compose.desktop.application.nativeDistributions
|
||||||
val mainRelease = nativeDistributions.outputBaseDir.dir("main-release")
|
val mainRelease = nativeDistributions.outputBaseDir.dir("main-release")
|
||||||
dependsOn("packageReleaseDistributionForCurrentOS")
|
if (installerPlugin.isThisPlatformSupported()) {
|
||||||
|
dependsOn(installerPlugin.createInstallerTask)
|
||||||
|
inputs.dir(installerPlugin.outputFolder)
|
||||||
|
} else {
|
||||||
|
dependsOn("packageReleaseDistributionForCurrentOS")
|
||||||
|
inputs.dir(mainRelease)
|
||||||
|
}
|
||||||
dependsOn(createDistributableAppArchive)
|
dependsOn(createDistributableAppArchive)
|
||||||
inputs.property("appVersion", getAppVersionString())
|
inputs.property("appVersion", getAppVersionString())
|
||||||
inputs.dir(mainRelease)
|
|
||||||
inputs.dir(distributableAppArchiveDir)
|
inputs.dir(distributableAppArchiveDir)
|
||||||
outputs.dir(ciDir.binariesDir)
|
outputs.dir(ciDir.binariesDir)
|
||||||
doLast {
|
doLast {
|
||||||
val output = ciDir.binariesDir.get().asFile
|
val output = ciDir.binariesDir.get().asFile
|
||||||
val packageName = appPackageNameByComposePlugin
|
val packageName = appPackageNameByComposePlugin
|
||||||
output.deleteRecursively()
|
output.deleteRecursively()
|
||||||
val allowedTarget = nativeDistributions.targetFormats.filter { it.isCompatibleWithCurrentOS }
|
|
||||||
for (target in allowedTarget) {
|
if (installerPlugin.isThisPlatformSupported()) {
|
||||||
CiUtils.movePackagedAndCreateSignature(
|
val targets = installerPlugin.getCreatedInstallerTargetFormats()
|
||||||
getAppVersion(),
|
for (target in targets) {
|
||||||
packageName,
|
CiUtils.movePackagedAndCreateSignature(
|
||||||
target,
|
getAppVersion(),
|
||||||
mainRelease.get().asFile,
|
packageName,
|
||||||
output,
|
target,
|
||||||
)
|
installerPlugin.outputFolder.get().asFile,
|
||||||
|
output,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
logger.lifecycle("app packages for '${targets.joinToString(", ") { it.name }}' written in $output using the installer plugin")
|
||||||
|
} else {
|
||||||
|
val allowedTargets = nativeDistributions
|
||||||
|
.targetFormats.filter { it.isCompatibleWithCurrentOS }
|
||||||
|
.map {
|
||||||
|
it.toInstallerTargetFormat()
|
||||||
|
}
|
||||||
|
for (target in allowedTargets) {
|
||||||
|
CiUtils.movePackagedAndCreateSignature(
|
||||||
|
getAppVersion(),
|
||||||
|
packageName,
|
||||||
|
target,
|
||||||
|
mainRelease.get().asFile.resolve(target.outputDirName),
|
||||||
|
output,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
logger.lifecycle("app packages for '${allowedTargets.joinToString(", ") { it.name }}' written in $output using compose packager tool")
|
||||||
}
|
}
|
||||||
logger.lifecycle("app packages for '${allowedTarget.joinToString(", ") { it.name }}' written in $output")
|
|
||||||
val appArchiveDistributableDir = distributableAppArchiveDir.get().asFile
|
val appArchiveDistributableDir = distributableAppArchiveDir.get().asFile
|
||||||
CiUtils.copyAndHashToDestination(
|
CiUtils.copyAndHashToDestination(
|
||||||
distributableAppArchiveDir.get().asFile.resolve(
|
distributableAppArchiveDir.get().asFile.resolve(
|
||||||
@ -272,7 +324,7 @@ val createBinariesForCi by tasks.registering {
|
|||||||
CiUtils.getTargetFileName(
|
CiUtils.getTargetFileName(
|
||||||
packageName,
|
packageName,
|
||||||
getAppVersion(),
|
getAppVersion(),
|
||||||
TargetFormat.AppImage,
|
null, // this is not an installer (it will be automatically converted to current os name
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
logger.lifecycle("distributable app archive written in ${output}")
|
logger.lifecycle("distributable app archive written in ${output}")
|
||||||
@ -299,3 +351,15 @@ val createReleaseFolderForCi by tasks.registering {
|
|||||||
dependsOn(createBinariesForCi, createChangeNoteForCi)
|
dependsOn(createBinariesForCi, createChangeNoteForCi)
|
||||||
}
|
}
|
||||||
// ======= end of GitHub action stuff
|
// ======= end of GitHub action stuff
|
||||||
|
|
||||||
|
fun TargetFormat.toInstallerTargetFormat(): InstallerTargetFormat {
|
||||||
|
return when (this) {
|
||||||
|
AppImage -> error("$this is not recognized as installer")
|
||||||
|
Deb -> InstallerTargetFormat.Deb
|
||||||
|
Rpm -> InstallerTargetFormat.Rpm
|
||||||
|
Dmg -> InstallerTargetFormat.Dmg
|
||||||
|
Pkg -> InstallerTargetFormat.Pkg
|
||||||
|
Exe -> InstallerTargetFormat.Exe
|
||||||
|
Msi -> InstallerTargetFormat.Msi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BIN
desktop/app/resources/installer/abdm-header-image.bmp
Normal file
BIN
desktop/app/resources/installer/abdm-header-image.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
BIN
desktop/app/resources/installer/abdm-sidebar-image.bmp
Normal file
BIN
desktop/app/resources/installer/abdm-sidebar-image.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 151 KiB |
212
desktop/app/resources/installer/nsis-script-template.nsi
Normal file
212
desktop/app/resources/installer/nsis-script-template.nsi
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
Unicode True
|
||||||
|
RequestExecutionLevel user
|
||||||
|
SetCompressor /SOLID lzma
|
||||||
|
!include "LogicLib.nsh"
|
||||||
|
!include "MUI2.nsh"
|
||||||
|
|
||||||
|
|
||||||
|
!define APP_PUBLISHER "{{ app_publisher }}"
|
||||||
|
!define APP_NAME "{{ app_name }}"
|
||||||
|
!define APP_DISPLAY_NAME "{{ app_display_name }}"
|
||||||
|
!define APP_VERSION "{{ app_version }}"
|
||||||
|
!define APP_VERSION_WITH_BUILD "{{ app_version_with_build }}"
|
||||||
|
!define APP_DISPLAY_VERSION "{{ app_display_version }}"
|
||||||
|
!define SOURCE_CODE_URL "{{ source_code_url }}"
|
||||||
|
!define PROJECT_WEBSITE "{{ project_website }}"
|
||||||
|
!define COPYRIGHT "{{ copyright }}"
|
||||||
|
|
||||||
|
!define INPUT_DIR "{{ input_dir }}"
|
||||||
|
!define LICENSE_FILE "{{ license_file }}"
|
||||||
|
!define MAIN_BINARY_NAME "${APP_NAME}"
|
||||||
|
|
||||||
|
!define SIDEBAR_IMAGE "{{ sidebar_image_file }}"
|
||||||
|
!define HEADER_IMAGE "{{ header_image_file }}"
|
||||||
|
!define ICON_FILE "{{ icon_file }}"
|
||||||
|
|
||||||
|
!define REG_UNINSTALL_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}"
|
||||||
|
!define REG_RUN_KEY "Software\Microsoft\Windows\CurrentVersion\Run\${APP_NAME}"
|
||||||
|
!define REG_APP_KEY "Software\${APP_NAME}"
|
||||||
|
|
||||||
|
; icon for this installer!
|
||||||
|
|
||||||
|
Icon "${ICON_FILE}"
|
||||||
|
!define MUI_ICON "${ICON_FILE}"
|
||||||
|
!define MUI_UNICON "${ICON_FILE}"
|
||||||
|
|
||||||
|
!if "${SIDEBAR_IMAGE}" != ""
|
||||||
|
!define MUI_WELCOMEFINISHPAGE_BITMAP "${SIDEBAR_IMAGE}"
|
||||||
|
|
||||||
|
!define MUI_UNWELCOMEFINISHPAGE_BITMAP "${SIDEBAR_IMAGE}"
|
||||||
|
!endif
|
||||||
|
|
||||||
|
!if "${HEADER_IMAGE}" != ""
|
||||||
|
!define MUI_HEADERIMAGE
|
||||||
|
!define MUI_HEADERIMAGE_BITMAP "${HEADER_IMAGE}"
|
||||||
|
|
||||||
|
!define MUI_UNHEADERIMAGE
|
||||||
|
!define MUI_UNHEADERIMAGE_BITMAP "${HEADER_IMAGE}"
|
||||||
|
!endif
|
||||||
|
|
||||||
|
VIProductVersion "${APP_VERSION_WITH_BUILD}"
|
||||||
|
VIAddVersionKey "ProductName" "${APP_DISPLAY_NAME}"
|
||||||
|
VIAddVersionKey "FileDescription" "${APP_DISPLAY_NAME}"
|
||||||
|
VIAddVersionKey "LegalCopyright" "${COPYRIGHT}"
|
||||||
|
VIAddVersionKey "FileVersion" "${APP_VERSION_WITH_BUILD}"
|
||||||
|
VIAddVersionKey "ProductVersion" "${APP_VERSION_WITH_BUILD}"
|
||||||
|
|
||||||
|
Name "${APP_DISPLAY_NAME}"
|
||||||
|
OutFile "{{ output_file }}"
|
||||||
|
|
||||||
|
InstallDir "$LOCALAPPDATA\${APP_NAME}"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
!define INSTALL_DIR `$INSTDIR`
|
||||||
|
Function .onInit
|
||||||
|
|
||||||
|
; Call RestorePreviousInstallLocation
|
||||||
|
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
|
; configure instfiles page
|
||||||
|
!define MUI_FINISHPAGE_NOAUTOCLOSE
|
||||||
|
!define MUI_INSTFILESPAGE_NOAUTOCLOSE
|
||||||
|
|
||||||
|
; configure finish page
|
||||||
|
!define MUI_FINISHPAGE_LINK "Open project in GitHub"
|
||||||
|
!define MUI_FINISHPAGE_LINK_LOCATION "${SOURCE_CODE_URL}"
|
||||||
|
!define MUI_FINISHPAGE_RUN
|
||||||
|
!define MUI_FINISHPAGE_RUN_FUNCTION RunMainBinary
|
||||||
|
|
||||||
|
;Installation Pages
|
||||||
|
!insertmacro MUI_PAGE_WELCOME
|
||||||
|
!insertmacro MUI_PAGE_LICENSE "${LICENSE_FILE}"
|
||||||
|
!insertmacro MUI_PAGE_COMPONENTS
|
||||||
|
!insertmacro MUI_PAGE_INSTFILES
|
||||||
|
!insertmacro MUI_PAGE_FINISH
|
||||||
|
|
||||||
|
;Uninstallation Pages
|
||||||
|
!insertmacro MUI_UNPAGE_WELCOME
|
||||||
|
!insertmacro MUI_UNPAGE_CONFIRM
|
||||||
|
!insertmacro MUI_UNPAGE_INSTFILES
|
||||||
|
!insertmacro MUI_UNPAGE_FINISH
|
||||||
|
|
||||||
|
; set language
|
||||||
|
!insertmacro MUI_LANGUAGE "English"
|
||||||
|
|
||||||
|
; a macro clear files to cleanup installation folder
|
||||||
|
!macro clearFiles
|
||||||
|
RmDir /r "${INSTALL_DIR}\app"
|
||||||
|
RmDir /r "${INSTALL_DIR}\runtime"
|
||||||
|
Delete "${INSTALL_DIR}\${MAIN_BINARY_NAME}.exe"
|
||||||
|
Delete "${INSTALL_DIR}\${MAIN_BINARY_NAME}.ico"
|
||||||
|
Delete "${INSTALL_DIR}\uninstall.exe"
|
||||||
|
RmDir "${INSTALL_DIR}"
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
Function RunMainBinary
|
||||||
|
Exec "${INSTALL_DIR}\${MAIN_BINARY_NAME}.exe"
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
|
!macro GetBestExecutableName result
|
||||||
|
StrCpy ${result} "${MAIN_BINARY_NAME}.exe"
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
; Function RestorePreviousInstallLocation
|
||||||
|
; ReadRegStr $4 SHCTX "${REG_APP_KEY}" "InstallPath"
|
||||||
|
; ${if} $4 != ""
|
||||||
|
; StrCpy $INSTDIR $4
|
||||||
|
; ${endif}
|
||||||
|
; FunctionEnd
|
||||||
|
|
||||||
|
; I should improve this.
|
||||||
|
!macro closeApp
|
||||||
|
!insertmacro GetBestExecutableName $1
|
||||||
|
DetailPrint "Stopping Executable $1"
|
||||||
|
; I don't wanna kill myself!
|
||||||
|
${If} "$EXEFILE" != "$1"
|
||||||
|
ExecWait 'taskkill /F /IM "$1"' $0
|
||||||
|
${Else}
|
||||||
|
DetailPrint "It seems that installer file name is same as app executable name"
|
||||||
|
DetailPrint "Please close app manually"
|
||||||
|
; don't sleep the script for nothing.
|
||||||
|
StrCpy $0 "1"
|
||||||
|
${EndIf}
|
||||||
|
${If} $0 == "0"
|
||||||
|
Sleep 500
|
||||||
|
BringToFront ; when we sleep it seems that window goes down
|
||||||
|
DetailPrint "Current app stopped successfully"
|
||||||
|
${Endif}
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
!macro CreateStartMenu
|
||||||
|
createDirectory "$SMPROGRAMS\${APP_DISPLAY_NAME}"
|
||||||
|
createShortCut "$SMPROGRAMS\${APP_DISPLAY_NAME}\${APP_DISPLAY_NAME}.lnk" "${INSTALL_DIR}\${MAIN_BINARY_NAME}.exe" "" "${INSTALL_DIR}\${MAIN_BINARY_NAME}.ico"
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
!macro RemoveStartMenu
|
||||||
|
RmDir /r "$SMPROGRAMS\${APP_DISPLAY_NAME}"
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
!macro CreateDesktopShortcut
|
||||||
|
CreateShortcut "$DESKTOP\${APP_DISPLAY_NAME}.lnk" "${INSTALL_DIR}\${MAIN_BINARY_NAME}.exe" "" "${INSTALL_DIR}\${MAIN_BINARY_NAME}.ico"
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
!macro RemoveDesktopShortCut
|
||||||
|
Delete "$DESKTOP\${APP_DISPLAY_NAME}.lnk"
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Section "${APP_DISPLAY_NAME}"
|
||||||
|
SectionInstType RO
|
||||||
|
|
||||||
|
DetailPrint "Closing app (if any)"
|
||||||
|
!insertmacro closeApp
|
||||||
|
DetailPrint "clearing old app (if any)"
|
||||||
|
!insertmacro clearFiles
|
||||||
|
DetailPrint "writing new data"
|
||||||
|
SetOutPath "${INSTALL_DIR}"
|
||||||
|
CreateDirectory "${INSTALL_DIR}"
|
||||||
|
|
||||||
|
WriteUninstaller "${INSTALL_DIR}\uninstall.exe"
|
||||||
|
|
||||||
|
File /nonfatal /r "${INPUT_DIR}\"
|
||||||
|
|
||||||
|
|
||||||
|
; Registry information for add/remove programs
|
||||||
|
WriteRegStr SHCTX "${REG_UNINSTALL_KEY}" "DisplayName" "${APP_DISPLAY_NAME}"
|
||||||
|
WriteRegStr SHCTX "${REG_UNINSTALL_KEY}" "DisplayIcon" "$\"${INSTALL_DIR}\${MAIN_BINARY_NAME}.exe$\""
|
||||||
|
WriteRegStr SHCTX "${REG_UNINSTALL_KEY}" "DisplayVersion" "${APP_VERSION}"
|
||||||
|
WriteRegStr SHCTX "${REG_UNINSTALL_KEY}" "Publisher" "${APP_PUBLISHER}"
|
||||||
|
WriteRegStr SHCTX "${REG_UNINSTALL_KEY}" "InstallLocation" "$\"${INSTALL_DIR}$\""
|
||||||
|
WriteRegStr SHCTX "${REG_UNINSTALL_KEY}" "UninstallString" "$\"${INSTALL_DIR}\uninstall.exe$\""
|
||||||
|
WriteRegDWORD SHCTX "${REG_UNINSTALL_KEY}" "NoModify" "1"
|
||||||
|
WriteRegDWORD SHCTX "${REG_UNINSTALL_KEY}" "NoRepair" "1"
|
||||||
|
|
||||||
|
; Registry keys for app installation path and version
|
||||||
|
WriteRegStr SHCTX "${REG_APP_KEY}" "InstallPath" "${INSTALL_DIR}"
|
||||||
|
WriteRegStr SHCTX "${REG_APP_KEY}" "Version" "${APP_VERSION}"
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
Section "Start Menu"
|
||||||
|
!insertmacro CreateStartMenu
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
Section "Desktop Shortcut"
|
||||||
|
!insertmacro CreateDesktopShortcut
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
Section "Uninstall"
|
||||||
|
!insertmacro closeApp
|
||||||
|
!insertmacro clearFiles
|
||||||
|
|
||||||
|
!insertmacro RemoveStartMenu
|
||||||
|
!insertmacro RemoveDesktopShortCut
|
||||||
|
|
||||||
|
DeleteRegKey SHCTX "${REG_UNINSTALL_KEY}"
|
||||||
|
DeleteRegKey SHCTX "${REG_APP_KEY}"
|
||||||
|
|
||||||
|
; remove auto start on boot registry
|
||||||
|
DeleteRegValue SHCTX "${REG_RUN_KEY}" "${APP_NAME}"
|
||||||
|
SectionEnd
|
@ -117,6 +117,8 @@ arrow-opticKsp = { module = "io.arrow-kt:arrow-optics-ksp-plugin", version.ref =
|
|||||||
androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "datastore" }
|
androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "datastore" }
|
||||||
osThemeDetector = { module = "com.github.Dansoftowner:jSystemThemeDetector", version.ref = "osThemeDetector" }
|
osThemeDetector = { module = "com.github.Dansoftowner:jSystemThemeDetector", version.ref = "osThemeDetector" }
|
||||||
|
|
||||||
|
handlebarsJava = "com.github.jknack:handlebars:4.4.0"
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||||
compose = { id = "org.jetbrains.compose", version.ref = "compose" }
|
compose = { id = "org.jetbrains.compose", version.ref = "compose" }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user