Skip to content

Commit

Permalink
Switch to notarytool for notarization (#3642)
Browse files Browse the repository at this point in the history
See #3642 for details

Resolves #3208
Resolves #2253

---------

Co-authored-by: Michael Rittmeister <michael@rittmeister.in>
  • Loading branch information
AlexeyTsvetkov and DRSchlaubi committed Sep 19, 2023
1 parent 93a8316 commit 50d45f3
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 217 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.provider.ProviderFactory
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Optional
import org.jetbrains.compose.desktop.application.internal.ComposeProperties
import org.jetbrains.compose.internal.utils.nullableProperty
Expand All @@ -35,7 +36,15 @@ abstract class MacOSNotarizationSettings {

@get:Input
@get:Optional
val teamID: Property<String?> = objects.nullableProperty<String>().apply {
set(ComposeProperties.macNotarizationTeamID(providers))
}

@Deprecated("This option is no longer supported and got replaced by teamID", level = DeprecationLevel.ERROR)
@get:Internal
val ascProvider: Property<String?> = objects.nullableProperty<String>().apply {
set(ComposeProperties.macNotarizationAscProvider(providers))
set(providers.provider {
throw UnsupportedOperationException("This option is not supported by notary tool and was replaced by teamID")
})
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal object ComposeProperties {
internal const val MAC_SIGN_PREFIX = "compose.desktop.mac.signing.prefix"
internal const val MAC_NOTARIZATION_APPLE_ID = "compose.desktop.mac.notarization.appleID"
internal const val MAC_NOTARIZATION_PASSWORD = "compose.desktop.mac.notarization.password"
internal const val MAC_NOTARIZATION_ASC_PROVIDER = "compose.desktop.mac.notarization.ascProvider"
internal const val MAC_NOTARIZATION_TEAM_ID_PROVIDER = "compose.desktop.mac.notarization.teamID"
internal const val CHECK_JDK_VENDOR = "compose.desktop.packaging.checkJdkVendor"

fun isVerbose(providers: ProviderFactory): Provider<Boolean> =
Expand All @@ -46,8 +46,8 @@ internal object ComposeProperties {
fun macNotarizationPassword(providers: ProviderFactory): Provider<String?> =
providers.valueOrNull(MAC_NOTARIZATION_PASSWORD)

fun macNotarizationAscProvider(providers: ProviderFactory): Provider<String?> =
providers.valueOrNull(MAC_NOTARIZATION_ASC_PROVIDER)
fun macNotarizationTeamID(providers: ProviderFactory): Provider<String?> =
providers.valueOrNull(MAC_NOTARIZATION_TEAM_ID_PROVIDER)

fun checkJdkVendor(providers: ProviderFactory): Provider<Boolean> =
providers.valueOrNull(CHECK_JDK_VENDOR).toBooleanProvider(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import org.gradle.api.provider.Provider
import org.gradle.process.ExecOperations
import org.gradle.process.ExecResult
import org.jetbrains.compose.internal.utils.ioFile
import java.io.ByteArrayInputStream
import java.io.File
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
Expand All @@ -33,7 +34,8 @@ internal class ExternalToolRunner(
workingDir: File? = null,
checkExitCodeIsNormal: Boolean = true,
processStdout: Function1<String, Unit>? = null,
logToConsole: LogToConsole = LogToConsole.OnlyWhenVerbose
logToConsole: LogToConsole = LogToConsole.OnlyWhenVerbose,
stdinStr: String? = null
): ExecResult {
val logsDir = logsDir.ioFile
logsDir.mkdirs()
Expand All @@ -52,6 +54,10 @@ internal class ExternalToolRunner(
// check exit value later
spec.isIgnoreExitValue = true

if (stdinStr != null) {
spec.standardInput = ByteArrayInputStream(stdinStr.toByteArray())
}

@Suppress("NAME_SHADOWING")
val logToConsole = when (logToConsole) {
LogToConsole.Always -> true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,23 +186,13 @@ private fun JvmApplicationContext.configurePackagingTasks(
"Unexpected target format for MacOS: $targetFormat"
}

val notarizationRequestsDir = project.layout.buildDirectory.dir("compose/notarization/$app")
tasks.register<AbstractUploadAppForNotarizationTask>(
tasks.register<AbstractNotarizationTask>(
taskNameAction = "notarize",
taskNameObject = targetFormat.name,
args = listOf(targetFormat)
) {
dependsOn(packageFormat)
inputDir.set(packageFormat.flatMap { it.destinationDir })
requestsDir.set(notarizationRequestsDir)
configureCommonNotarizationSettings(this)
}

tasks.register<AbstractCheckNotarizationStatusTask>(
taskNameAction = "check",
taskNameObject = "notarizationStatus"
) {
requestDir.set(notarizationRequestsDir)
configureCommonNotarizationSettings(this)
}
}
Expand Down Expand Up @@ -351,7 +341,6 @@ private fun JvmApplicationContext.configurePackageTask(
internal fun JvmApplicationContext.configureCommonNotarizationSettings(
notarizationTask: AbstractNotarizationTask
) {
notarizationTask.nonValidatedBundleID.set(app.nativeDistributions.macOS.bundleID)
notarizationTask.nonValidatedNotarizationSettings = app.nativeDistributions.macOS.notarization
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,30 @@

package org.jetbrains.compose.desktop.application.internal.validation

import org.gradle.api.provider.Provider
import org.jetbrains.compose.desktop.application.dsl.MacOSNotarizationSettings
import org.jetbrains.compose.desktop.application.internal.ComposeProperties

internal data class ValidatedMacOSNotarizationSettings(
val bundleID: String,
val appleID: String,
val password: String,
val ascProvider: String?
val teamID: String?
)

internal fun MacOSNotarizationSettings?.validate(
bundleIDProvider: Provider<String?>
): ValidatedMacOSNotarizationSettings {
internal fun MacOSNotarizationSettings?.validate(): ValidatedMacOSNotarizationSettings {
checkNotNull(this) {
ERR_NOTARIZATION_SETTINGS_ARE_NOT_PROVIDED
}

val bundleID = validateBundleID(bundleIDProvider)
check(!appleID.orNull.isNullOrEmpty()) {
ERR_APPLE_ID_IS_EMPTY
}
check(!password.orNull.isNullOrEmpty()) {
ERR_PASSWORD_IS_EMPTY
}
return ValidatedMacOSNotarizationSettings(
bundleID = bundleID,
appleID = appleID.orNull!!,
password = password.orNull!!,
ascProvider = ascProvider.orNull
teamID = teamID.orNull
)
}

Expand All @@ -50,4 +44,4 @@ private val ERR_PASSWORD_IS_EMPTY =
"""|$ERR_PREFIX password is null or empty. To specify:
| * Use '${ComposeProperties.MAC_NOTARIZATION_PASSWORD}' Gradle property;
| * Or use 'nativeDistributions.macOS.notarization.password' DSL property;
""".trimMargin()
""".trimMargin()

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,67 @@

package org.jetbrains.compose.desktop.application.tasks

import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Nested
import org.gradle.api.tasks.Optional
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.tasks.*
import org.jetbrains.compose.desktop.application.dsl.MacOSNotarizationSettings
import org.jetbrains.compose.internal.utils.nullableProperty
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.compose.desktop.application.internal.files.checkExistingFile
import org.jetbrains.compose.desktop.application.internal.files.findOutputFileOrDir
import org.jetbrains.compose.desktop.application.internal.validation.ValidatedMacOSNotarizationSettings
import org.jetbrains.compose.desktop.application.internal.validation.validate
import org.jetbrains.compose.desktop.tasks.AbstractComposeDesktopTask
import org.jetbrains.compose.internal.utils.MacUtils
import org.jetbrains.compose.internal.utils.ioFile
import java.io.File
import javax.inject.Inject

abstract class AbstractNotarizationTask : AbstractComposeDesktopTask() {
abstract class AbstractNotarizationTask @Inject constructor(
@get:Input
@get:Optional
internal val nonValidatedBundleID: Property<String?> = objects.nullableProperty()
val targetFormat: TargetFormat
) : AbstractComposeDesktopTask() {

@get:Nested
@get:Optional
internal var nonValidatedNotarizationSettings: MacOSNotarizationSettings? = null

internal fun validateNotarization() =
nonValidatedNotarizationSettings.validate(nonValidatedBundleID)
}
@get:InputDirectory
val inputDir: DirectoryProperty = objects.directoryProperty()

init {
check(targetFormat != TargetFormat.AppImage) { "${TargetFormat.AppImage} cannot be notarized!" }
}

@TaskAction
fun run() {
val notarization = nonValidatedNotarizationSettings.validate()
val packageFile = findOutputFileOrDir(inputDir.ioFile, targetFormat).checkExistingFile()

notarize(notarization, packageFile)
staple(packageFile)
}

private fun notarize(
notarization: ValidatedMacOSNotarizationSettings,
packageFile: File
) {
logger.info("Uploading '${packageFile.name}' for notarization")
val args = listOfNotNull(
"notarytool",
"submit",
"--wait",
"--apple-id",
notarization.appleID,
"--team-id".takeIf { notarization.teamID != null },
notarization.teamID,
packageFile.absolutePath
)
runExternalTool(tool = MacUtils.xcrun, args = args, stdinStr = notarization.password)
}

private fun staple(packageFile: File) {
runExternalTool(
tool = MacUtils.xcrun,
args = listOf("stapler", "staple", packageFile.absolutePath)
)
}
}

This file was deleted.

Loading

0 comments on commit 50d45f3

Please sign in to comment.