GPG sign for artifacts

This commit is contained in:
Karlatemp 2021-02-05 17:38:27 +08:00
parent 3a8003057a
commit d37675f5c2
No known key found for this signature in database
GPG Key ID: 21FBDDF664FF06F8
7 changed files with 215 additions and 4 deletions

5
.gitignore vendored
View File

@ -45,4 +45,7 @@ keys.properties
token.txt
bintray.user.txt
bintray.key.txt
bintray.key.txt
# For gpg sign
/build-gpg-sign

View File

@ -64,6 +64,7 @@ configure<kotlinx.validation.ApiValidationExtension> {
}
project.ext.set("isAndroidSDKAvailable", false)
GpgSigner.setup(project)
tasks.register("publishMiraiCoreArtifactsToMavenLocal") {
group = "mirai"

View File

@ -0,0 +1,100 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
import org.gradle.api.Project
import java.io.File
open class GpgSigner(private val workdir: File) {
private val workdirParent by lazy { workdir.parentFile ?: error("Assertion error: No parent file of $workdir") }
private val workdirName by lazy { workdir.name }
fun verbose(msg: String) {
println("[GPG SIGN] [Verbose] $msg")
}
@Suppress("RemoveExplicitTypeArguments")
private val verbosePrintOnce by lazy<Unit> {
verbose("GPG Signer working dir: $workdir")
verbose("GPG command working dir: $workdirParent")
}
constructor(workdir: String) : this(File(workdir))
object NoopSigner : GpgSigner("build/gpg-noop") {
override fun processGpg(vararg cmds: String) {
}
override fun importKey(file: File) {
}
override fun doSign(file: File) {
}
}
companion object {
private var initialized: Boolean = false
var signer: GpgSigner = NoopSigner
fun setup(project: Project) {
if (initialized) return
initialized = true
val rootProject = project.rootProject
val gpg = rootProject.projectDir.resolve("build-gpg-sign")
gpg.mkdirs()
val keyFile = gpg.resolve("keys.gpg")
if (keyFile.isFile) {
val homedir = gpg.resolve("homedir")
signer = GpgSigner(homedir.absolutePath)
if (!homedir.resolve("pubring.kbx").isFile) {
signer.importKey(keyFile)
}
} else {
rootProject.logger.warn("GPG Key not found.")
rootProject.logger.warn("GPG Signer will not setup")
rootProject.logger.warn("Key file location: $keyFile")
}
}
}
open fun processGpg(
vararg cmds: String
) {
workdir.mkdirs()
verbosePrintOnce
val response = ProcessBuilder().command(ArrayList<String>().apply {
add("gpg")
add("--homedir"); add(workdirName)
addAll(cmds)
}.also {
verbose("Processing " + it.joinToString(" "))
}).directory(workdirParent)
.inheritIO()
.start()
.waitFor()
if (response != 0) {
error("Exit Response $response")
}
}
open fun importKey(file: File) {
processGpg("--batch", "--import", file.toString())
}
open fun doSign(file: File) {
if (!file.isFile) {
println("[GPG SIGN] $file not a file")
return
}
println("[GPG SIGN] Signing $file")
File("${file.path}.asc").delete()
processGpg("-a", "--batch", "--no-tty", "--sign", file.toString())
}
}

View File

@ -115,5 +115,6 @@ inline fun Project.configurePublishing(
artifact(sourcesJar.get())
}
}
configGpgSign(this@configurePublishing)
}
}

View File

@ -42,10 +42,13 @@ fun Project.configureMppPublishing() {
.forEach { publication ->
val moduleFile = buildDir.resolve("publications/${publication.name}/module.json")
if (moduleFile.exists()) {
publication.artifact(object :
val artifact = (object :
org.gradle.api.publish.maven.internal.artifact.FileBasedMavenArtifact(moduleFile) {
override fun getDefaultExtension() = "module"
})
publication.artifact(artifact)
GpgSigner.signer.doSign(moduleFile)
publication.artifact(GPGSignMavenArtifact(artifact))
}
}
}
@ -86,6 +89,7 @@ fun Project.configureMppPublishing() {
}
}
}
configGpgSign(this@configureMppPublishing)
}
}
}
@ -129,6 +133,6 @@ val publishPlatformArtifactsInRootModule: Project.(MavenPublication) -> Unit = {
}
}
private fun MavenArtifact.smartToString(): String {
public fun MavenArtifact.smartToString(): String {
return "${file.path}, classifier=${classifier}, ext=${extension}"
}

View File

@ -0,0 +1,102 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
import org.gradle.api.Project
import org.gradle.api.internal.tasks.DefaultTaskDependency
import org.gradle.api.internal.tasks.TaskDependencyInternal
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenArtifact
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.maven.internal.artifact.AbstractMavenArtifact
import org.gradle.api.publish.maven.internal.publication.DefaultMavenPublication
import java.io.File
open class GPGSignMavenArtifact(
private val delegate: MavenArtifact,
private val tasks: TaskDependencyInternal = TaskDependencyInternal.EMPTY
) : AbstractMavenArtifact() {
override fun getFile(): File {
return File(delegate.file.path + ".asc")
}
override fun shouldBePublished(): Boolean = (delegate as? AbstractMavenArtifact)?.shouldBePublished() ?: true
override fun getDefaultExtension(): String = delegate.extension + ".asc"
override fun getDefaultClassifier(): String = delegate.classifier ?: ""
override fun getDefaultBuildDependencies(): TaskDependencyInternal = tasks
}
class NameCounter(val name: String) {
var counter = 0
val nextName: String
get() = name + if (counter == 0) {
counter = 1; ""
} else {
counter++; counter
}
}
object PublishingAccess {
fun getMetadataArtifacts(publication: MavenPublication): Collection<MavenArtifact> {
if (publication is DefaultMavenPublication) {
return DefaultMavenPublication::class.java.getDeclaredField("metadataArtifacts")
.also { it.isAccessible = true }
.get(publication) as Collection<MavenArtifact>
}
return emptyList()
}
}
fun PublishingExtension.configGpgSign(project: Project) {
if (GpgSigner.signer === GpgSigner.NoopSigner) {
return
}
val tasks = DefaultTaskDependency()
val signArtifactsGPG = NameCounter("signArtifactsGPG")
publications.forEach { publication ->
if (publication is MavenPublication) {
val artifacts0: Collection<Pair<Collection<MavenArtifact>, (MavenArtifact) -> Unit>> = listOf(
publication.artifacts to { publication.artifact(it) }, // main artifacts
PublishingAccess.getMetadataArtifacts(publication).let { artifacts -> // pom files
if (artifacts is MutableCollection<MavenArtifact>) {
artifacts to { artifacts.add(it) }
} else {
artifacts to { publication.artifact(it) }
}
}
)
val allArtifacts = artifacts0.flatMap { it.first }.toList()
if (allArtifacts.isNotEmpty()) {
tasks.add(project.tasks.create(signArtifactsGPG.nextName) {
group = "publishing"
doLast {
allArtifacts.forEach { artifact ->
if ((artifact as? AbstractMavenArtifact)?.shouldBePublished() != false) {
GpgSigner.signer.doSign(artifact.file)
}
}
}
allArtifacts.forEach {
dependsOn(it.buildDependencies)
}
})
artifacts0.forEach { (artifacts, artifactsRegister) ->
artifacts.toList().forEach { artifact ->
logPublishing("gpg sign for artifact ${artifact.smartToString()}")
artifactsRegister(GPGSignMavenArtifact(artifact, tasks))
}
}
}
}
}
}

View File

@ -12,7 +12,7 @@
import org.gradle.api.attributes.Attribute
object Versions {
const val project = "2.3.2"
const val project = "2.3.2-dev-publish-1"
const val core = project
const val console = project