Browse Source

Check notarization status without triggering new notarization upload

pull/387/head
Alexey Tsvetkov 3 years ago committed by Alexey Tsvetkov
parent
commit
f7feef4cc6
  1. 35
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/NotarizationRequestInfo.kt
  2. 16
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureApplication.kt
  3. 50
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractCheckNotarizationStatusTask.kt
  4. 26
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractUploadAppForNotarizationTask.kt
  5. 7
      tutorials/Signing_and_notarization_on_macOS/README.md

35
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/NotarizationRequestInfo.kt

@ -0,0 +1,35 @@
package org.jetbrains.compose.desktop.application.internal
import java.io.File
import java.util.*
internal const val NOTARIZATION_REQUEST_INFO_FILE_NAME = "notarization-request.properties"
internal data class NotarizationRequestInfo(
var uuid: String = "",
var uploadTime: String = ""
) {
fun loadFrom(file: File) {
val properties = Properties().apply {
file.inputStream().buffered().use { input ->
load(input)
}
}
uuid = properties.getProperty(UUID) ?: uuid
uploadTime = properties.getProperty(UPLOAD_TIME) ?: uploadTime
}
fun saveTo(file: File) {
val properties = Properties()
properties[UUID] = uuid
properties[UPLOAD_TIME] = uploadTime
file.outputStream().buffered().use { output ->
properties.store(output, null)
}
}
companion object {
private const val UUID = "uuid"
private const val UPLOAD_TIME = "upload.time"
}
}

16
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureApplication.kt

@ -1,6 +1,7 @@
package org.jetbrains.compose.desktop.application.internal
import org.gradle.api.*
import org.gradle.api.file.Directory
import org.gradle.api.file.DuplicatesStrategy
import org.gradle.api.file.FileCollection
import org.gradle.api.plugins.JavaPluginConvention
@ -72,17 +73,18 @@ internal fun Project.configurePackagingTasks(apps: Collection<Application>) {
"Unexpected target format for MacOS: $targetFormat"
}
val notarizationRequestsDir = project.layout.buildDirectory.dir("compose/notarization/${app.name}")
val upload = tasks.composeTask<AbstractUploadAppForNotarizationTask>(
taskName("notarize", app, targetFormat.name),
args = listOf(targetFormat)
) {
configureUploadForNotarizationTask(app, packageFormat, targetFormat)
configureUploadForNotarizationTask(app, packageFormat, notarizationRequestsDir)
}
tasks.composeTask<AbstractCheckNotarizationStatusTask>(
taskName("checkNotarizationStatus", app, targetFormat.name)
taskName("checkNotarizationStatus", app)
) {
configureCheckNotarizationStatusTask(app, upload)
configureCheckNotarizationStatusTask(app, notarizationRequestsDir)
}
}
@ -163,19 +165,19 @@ internal fun AbstractJPackageTask.configurePackagingTask(
internal fun AbstractUploadAppForNotarizationTask.configureUploadForNotarizationTask(
app: Application,
packageFormat: TaskProvider<AbstractJPackageTask>,
targetFormat: TargetFormat
requestsDir: Provider<Directory>
) {
dependsOn(packageFormat)
inputDir.set(packageFormat.flatMap { it.destinationDir })
requestIDFile.set(project.layout.buildDirectory.file("compose/notarization/${app.name}-${targetFormat.id}-request-id.txt"))
this.requestsDir.set(requestsDir)
configureCommonNotarizationSettings(app)
}
internal fun AbstractCheckNotarizationStatusTask.configureCheckNotarizationStatusTask(
app: Application,
uploadTask: Provider<AbstractUploadAppForNotarizationTask>
requestsDir: Provider<Directory>
) {
requestIDFile.set(uploadTask.flatMap { it.requestIDFile })
requestDir.set(requestsDir)
configureCommonNotarizationSettings(app)
}

50
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractCheckNotarizationStatusTask.kt

@ -1,27 +1,53 @@
package org.jetbrains.compose.desktop.application.tasks
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.tasks.*
import org.jetbrains.compose.desktop.application.internal.MacUtils
import org.jetbrains.compose.desktop.application.internal.NOTARIZATION_REQUEST_INFO_FILE_NAME
import org.jetbrains.compose.desktop.application.internal.NotarizationRequestInfo
import org.jetbrains.compose.desktop.application.internal.ioFile
abstract class AbstractCheckNotarizationStatusTask : AbstractNotarizationTask() {
@get:InputFile
val requestIDFile: RegularFileProperty = objects.fileProperty()
@get:Internal
val requestDir: DirectoryProperty = objects.directoryProperty()
@TaskAction
fun run() {
val notarization = validateNotarization()
val requestId = requestIDFile.ioFile.readText()
execOperations.exec { exec ->
exec.executable = MacUtils.xcrun.absolutePath
exec.args(
"altool",
"--notarization-info", requestId,
"--username", notarization.appleID,
"--password", notarization.password
)
val requests = HashSet<NotarizationRequestInfo>()
for (file in requestDir.ioFile.walk()) {
if (file.isFile && file.name == NOTARIZATION_REQUEST_INFO_FILE_NAME) {
try {
val status = NotarizationRequestInfo()
status.loadFrom(file)
requests.add(status)
} catch (e: Exception) {
logger.error("Invalid notarization request status file: $file", e)
}
}
}
if (requests.isEmpty()) {
logger.quiet("No existing notarization requests")
return
}
for (request in requests.sortedBy { it.uploadTime }) {
try {
logger.quiet("Checking status of notarization request '${request.uuid}'")
execOperations.exec { exec ->
exec.executable = MacUtils.xcrun.absolutePath
exec.args(
"altool",
"--notarization-info", request.uuid,
"--username", notarization.appleID,
"--password", notarization.password
)
}
} catch (e: Exception) {
logger.error("Could not check notarization request '${request.uuid}'", e)
}
}
}
}

26
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractUploadAppForNotarizationTask.kt

@ -1,23 +1,24 @@
package org.jetbrains.compose.desktop.application.tasks
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.*
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.compose.desktop.application.internal.*
import java.io.ByteArrayOutputStream
import java.io.PrintStream
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import javax.inject.Inject
abstract class AbstractUploadAppForNotarizationTask @Inject constructor(
@get:Input
val targetFormat: TargetFormat,
val targetFormat: TargetFormat
) : AbstractNotarizationTask() {
@get:InputDirectory
val inputDir: DirectoryProperty = objects.directoryProperty()
@get:OutputFile
val requestIDFile: RegularFileProperty = objects.fileProperty()
@get:Internal
val requestsDir: DirectoryProperty = objects.directoryProperty()
init {
check(targetFormat != TargetFormat.AppImage) { "${TargetFormat.AppImage} cannot be notarized!" }
@ -26,7 +27,6 @@ abstract class AbstractUploadAppForNotarizationTask @Inject constructor(
@TaskAction
fun run() {
val notarization = validateNotarization()
val inputFile = findOutputFileOrDir(inputDir.ioFile, targetFormat)
val file = inputFile.checkExistingFile()
@ -57,12 +57,18 @@ abstract class AbstractUploadAppForNotarizationTask @Inject constructor(
?: error("Could not determine RequestUUID from output: $output")
val requestId = m.groupValues[1]
requestIDFile.ioFile.apply {
parentFile.mkdirs()
writeText(requestId)
}
val uploadTime = LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss"))
val requestDir = requestsDir.ioFile.resolve("$uploadTime-${targetFormat.id}")
val packageCopy = requestDir.resolve(inputFile.name)
inputFile.copyTo(packageCopy)
val requestInfo = NotarizationRequestInfo(uuid = requestId, uploadTime = uploadTime)
val requestInfoFile = requestDir.resolve(NOTARIZATION_REQUEST_INFO_FILE_NAME)
requestInfo.saveTo(requestInfoFile)
logger.quiet("Request UUID: $requestId")
logger.quiet("Request UUID is saved to ${requestIDFile.ioFile.absolutePath}")
logger.quiet("Request UUID is saved to ${requestInfoFile.absolutePath}")
logger.quiet("Uploaded file is saved to ${packageCopy.absolutePath}")
}
}

7
tutorials/Signing_and_notarization_on_macOS/README.md

@ -215,12 +215,13 @@ macOS {
The following tasks are available:
* Use `createDistributable` or `packageDmg` to get a signed application
(no separate step is required).
* Use `notarizeDmg` to upload an application for notarization.
* Use `notarize<PACKAGING_FORMAT>` (e.g. `notarizeDmg`) to upload an application for notarization.
Once the upload finishes, a `RequestUUID` will be printed.
The notarization process takes some time.
Once the notarization process finishes, an email will be sent to you.
* Use `checkNotarizationStatus<PACKAGING_FORMAT>` to check a status of
the last notarization request. You can also use a command-line command directly:
Uploaded file is saved to `<BUILD_DIR>/compose/notarization/main/<UPLOAD_DATE>-<PACKAGING_FORMAT>`
* Use `checkNotarizationStatus` to check a status of
last notarization requests. You can also use a command-line command to check any notarization request:
```
xcrun altool --notarization-info <RequestUUID>
--username <Apple_ID>

Loading…
Cancel
Save