Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to notarytool for notarization #3642

Merged
merged 12 commits into from
Sep 19, 2023
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