diff --git a/.github/workflows/bintray.yml b/.github/workflows/bintray.yml deleted file mode 100644 index 080f07baf..000000000 --- a/.github/workflows/bintray.yml +++ /dev/null @@ -1,123 +0,0 @@ -# This is a basic workflow to help you get started with Actions - -name: Bintray Publish - -# Controls when the action will run. Triggers the workflow on push or pull request -# events but only for the master branch -on: - release: - types: [ created ] - push: - tags: - - '*-dev*' - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - # This workflow contains a single job called "build" - publish-mirai: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Checkout submodules - run: git submodule update --init --recursive --remote - - - name: Set up JDK 1.8 - uses: actions/setup-java@v1 - with: - java-version: 1.8 - - - name: chmod -R 777 * - run: chmod -R 777 * - - - name: Init gradle project - run: ./gradlew clean --scan - - - name: Check keys - run: > - ./gradlew :mirai-core-utils:ensureBintrayAvailable - :mirai-core-api:ensureBintrayAvailable - :mirai-core:ensureBintrayAvailable - :mirai-console:ensureBintrayAvailable - -Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }} - -Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }} - - - name: fillBuildConstants - run: > - ./gradlew - fillBuildConstants --scan - -Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }} - -Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }} - - - name: Assemble - run: ./gradlew assemble --scan - - - name: Check - run: ./gradlew check --scan - - - name: Gradle :mirai-core-utils:publish - run: > - ./gradlew :mirai-core-utils:publish --scan - -Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }} - -Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }} - - - name: Gradle :mirai-core-api:publish - run: > - ./gradlew :mirai-core-api:publish --scan - -Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }} - -Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }} - - - name: Gradle :mirai-core:publish - run: > - ./gradlew :mirai-core:publish --scan - -Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }} - -Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }} - - - name: Gradle :mirai-core-all:bintrayUpload - run: > - ./gradlew :mirai-core-all:bintrayUpload --info - -Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }} - -Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }} - - - name: Gradle :mirai-console:bintrayUpload - run: > - ./gradlew - :mirai-console:bintrayUpload --info - -Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }} - -Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }} - - - name: Gradle :mirai-console-terminal:bintrayUpload - run: > - ./gradlew - :mirai-console-terminal:bintrayUpload --info - -Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }} - -Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }} - - - name: Gradle :mirai-console-compiler-common:bintrayUpload - run: > - ./gradlew - :mirai-console-compiler-common:bintrayUpload --info - -Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }} - -Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }} - - - name: Gradle :mirai-console-compiler-annotations:bintrayUpload - run: > - ./gradlew - :mirai-console-compiler-annotations:bintrayUpload --info - -Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }} - -Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }} - -# - name: Gradle :mirai-console-intellij:bintrayUpload -# run: > -# ./gradlew -# :mirai-console-intellij:bintrayUpload --info -# -Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }} -# -Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }} - - - name: Publish Gradle plugin - run: > - ./gradlew - :mirai-console-gradle:publishPlugins --scan - -Dgradle.publish.key=${{ secrets.GRADLE_PUBLISH_KEY }} -Pgradle.publish.key=${{ secrets.GRADLE_PUBLISH_KEY }} - -Dgradle.publish.secret=${{ secrets.GRADLE_PUBLISH_SECRET }} -Pgradle.publish.secret=${{ secrets.GRADLE_PUBLISH_SECRET }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..fd75d0c68 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,125 @@ +# This is a basic workflow to help you get started with Actions + +name: Release Publish + +# Controls when the action will run. Triggers the workflow on push or pull request +# events but only for the master branch +on: + release: + types: [ created ] + push: + tags: + - '*-dev*' + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + publish-mirai: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Checkout submodules + run: git submodule update --init --recursive --remote + + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + + - name: chmod -R 777 * + run: chmod -R 777 * + + - name: Keys setup + shell: bash + run: | + mkdir build-gpg-sign + echo "$GPG_PRIVATE" > build-gpg-sign/keys.gpg + echo "$GPG_PUBLIC_" > build-gpg-sign/keys.gpg.pub + mkdir build-secret-keys + echo "$SONATYPE_USER" > build-secret-keys/sonatype.key + echo "$SONATYPE_KEY" >> build-secret-keys/sonatype.key + echo "$BINTRAY_USER" > build-secret-keys/bintray.key + echo "$BINTRAY_KEY" >> build-secret-keys/bintray.key + env: + GPG_PRIVATE: ${{ secrets.GPG_PRIVATE_KEY }} + GPG_PUBLIC_: ${{ secrets.GPG_PUBLIC_KEY }} + SONATYPE_USER: ${{ secrets.SONATYPE_USER }} + SONATYPE_KEY: ${{ secrets.SONATYPE_KEY }} + BINTRAY_USER: ${{ secrets.BINTRAY_USER }} + BINTRAY_KEY: ${{ secrets.BINTRAY_KEY }} + + - name: Init gradle project + run: ./gradlew clean --scan + + - name: Check keys + run: > + ./gradlew + :mirai-core-utils:ensureBintrayAvailable + :mirai-core-api:ensureBintrayAvailable + :mirai-core:ensureBintrayAvailable + :mirai-console:ensureBintrayAvailable + :mirai-core-utils:ensureMavenCentralAvailable + :mirai-core-api:ensureMavenCentralAvailable + :mirai-core:ensureMavenCentralAvailable + :mirai-console:ensureMavenCentralAvailable + + - name: fillBuildConstants + run: > + ./gradlew + fillBuildConstants --scan + + - name: Assemble + run: ./gradlew assemble --scan + + - name: Check + run: ./gradlew check --scan + + - name: Gradle :mirai-core-utils:publish + run: > + ./gradlew :mirai-core-utils:publish --scan + + - name: Gradle :mirai-core-api:publish + run: > + ./gradlew :mirai-core-api:publish --scan + + - name: Gradle :mirai-core:publish + run: > + ./gradlew :mirai-core:publish --scan + + - name: Gradle :mirai-core-all:publish + run: > + ./gradlew :mirai-core-all:publish --info + + - name: Gradle :mirai-console:publish + run: > + ./gradlew + :mirai-console:publish --info + + - name: Gradle :mirai-console-terminal:publish + run: > + ./gradlew + :mirai-console-terminal:publish --info + + - name: Gradle :mirai-console-compiler-common:publish + run: > + ./gradlew + :mirai-console-compiler-common:publish --info + + - name: Gradle :mirai-console-compiler-annotations:publish + run: > + ./gradlew + :mirai-console-compiler-annotations:publish --info + + - name: Publish Gradle plugin + run: > + ./gradlew + :mirai-console-gradle:publishPlugins --scan + -Dgradle.publish.key=${{ secrets.GRADLE_PUBLISH_KEY }} -Pgradle.publish.key=${{ secrets.GRADLE_PUBLISH_KEY }} + -Dgradle.publish.secret=${{ secrets.GRADLE_PUBLISH_SECRET }} -Pgradle.publish.secret=${{ secrets.GRADLE_PUBLISH_SECRET }} + + - name: Gradle :ci-release-helper:closeAndReleaseRepository + run: > + ./gradlew + :ci-release-helper:closeAndReleaseRepository --info diff --git a/.gitignore b/.gitignore index 9315f8357..fb2977862 100644 --- a/.gitignore +++ b/.gitignore @@ -45,4 +45,9 @@ keys.properties token.txt bintray.user.txt -bintray.key.txt \ No newline at end of file +bintray.key.txt + +# For gpg sign +/build-gpg-sign +# Name for IDEA direction sorting +build-secret-keys/ diff --git a/build.gradle.kts b/build.gradle.kts index 0dfb3d3ad..0cae31c90 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -64,6 +64,7 @@ configure { } project.ext.set("isAndroidSDKAvailable", false) +GpgSigner.setup(project) tasks.register("publishMiraiCoreArtifactsToMavenLocal") { group = "mirai" @@ -138,9 +139,14 @@ subprojects { tasks.register("cleanExceptIntellij") { group = "build" - allprojects.forEach { - if (it.name != "mirai-console-intellij") - dependsOn(it.tasks.findByName("clean")) + allprojects.forEach { proj -> + if (proj.name != "mirai-console-intellij") { + + // Type mismatch + // proj.tasks.findByName("clean")?.let(::dependsOn) + + proj.tasks.findByName("clean")?.let { dependsOn(it) } + } } } diff --git a/buildSrc/src/main/kotlin/GpgSigner.kt b/buildSrc/src/main/kotlin/GpgSigner.kt new file mode 100644 index 000000000..8ee91e941 --- /dev/null +++ b/buildSrc/src/main/kotlin/GpgSigner.kt @@ -0,0 +1,108 @@ +/* + * 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 { + 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") + val keyFilePub = gpg.resolve("keys.gpg.pub") + if (keyFile.isFile) { + val homedir = gpg.resolve("homedir") + signer = GpgSigner(homedir.absolutePath) + if (!homedir.resolve("pubring.kbx").isFile) { + signer.importKey(keyFile) + if (keyFilePub.isFile) { + signer.importKey(keyFilePub) + } else { + println("[GPG SIGN] Missing public key storage") + println("[GPG SIGN] GPG Sign 2nd verity may failed.") + } + } + } else { + println("[GPG SIGN] GPG Key not found.") + println("[GPG SIGN] GPG Signer will not setup") + println("[GPG SIGN] Key file location: $keyFile") + } + } + } + + open fun processGpg( + vararg cmds: String + ) { + workdir.mkdirs() + verbosePrintOnce + + val response = ProcessBuilder().command(ArrayList().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", "--detach-sig", "--sign", file.toString()) + processGpg("--verify", "$file.asc", file.toString()) + } + +} diff --git a/buildSrc/src/main/kotlin/JvmPublishing.kt b/buildSrc/src/main/kotlin/JvmPublishing.kt index 52f42e365..ee95e2eea 100644 --- a/buildSrc/src/main/kotlin/JvmPublishing.kt +++ b/buildSrc/src/main/kotlin/JvmPublishing.kt @@ -13,6 +13,7 @@ ) import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin +import keys.SecretKeys import org.gradle.api.Project import org.gradle.api.publish.maven.MavenPublication import org.gradle.api.tasks.bundling.Jar @@ -21,17 +22,9 @@ import org.gradle.kotlin.dsl.get import org.gradle.kotlin.dsl.register import org.gradle.kotlin.dsl.registering -/* - * Copyright 2020 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 - */ - - -fun Project.configureBintray() { +fun Project.configureRemoteRepos( + bintrayPkgName: String = "mirai-core" +) { tasks.register("ensureBintrayAvailable") { doLast { if (!project.isBintrayAvailable()) { @@ -39,19 +32,53 @@ fun Project.configureBintray() { } } } + tasks.register("ensureMavenCentralAvailable") { + doLast { + if (GpgSigner.signer == GpgSigner.NoopSigner) { + error("GPG Signer isn't available.") + } + val keys = SecretKeys.getCache(project) + if (!keys.loadKey("sonatype").isValid) { + error("Maven Central isn't available.") + } + } + } - if (isBintrayAvailable()) { - publishing { - repositories { + publishing { + // sonatype + val keys = SecretKeys.getCache(project) + repositories { + val sonatype = keys.loadKey("sonatype") + if (sonatype.isValid) { maven { - setUrl("https://api.bintray.com/maven/him188moe/mirai/mirai-core/;publish=1;override=1") + name = "MavenCentral" + // Maven Central + setUrl("https://oss.sonatype.org/service/local/staging/deploy/maven2") + + credentials { + username = sonatype.user + password = sonatype.password + } + } + } else { + println("SonaType is not available") + } + + + if (isBintrayAvailable()) { + maven { + name = "Bintray" + setUrl("https://api.bintray.com/maven/him188moe/mirai/$bintrayPkgName/;publish=1;override=1") credentials { username = Bintray.getUser(project) password = Bintray.getKey(project) } } + } else { + println("bintray isn't available.") } + } } } @@ -63,30 +90,29 @@ inline fun Project.configurePublishing( bintrayPkgName: String = artifactId, vcs: String = "https://github.com/mamoe/mirai" ) { - configureBintray() + configureRemoteRepos( + bintrayPkgName = bintrayPkgName + ) apply() - if (!project.isBintrayAvailable()) { - println("bintray isn't available. NO PUBLICATIONS WILL BE SET") - return - } + if (project.isBintrayAvailable()) { + bintray { + user = Bintray.getUser(project) + key = Bintray.getKey(project) - bintray { - user = Bintray.getUser(project) - key = Bintray.getKey(project) + setPublications("mavenJava") + setConfigurations("archives") - setPublications("mavenJava") - setConfigurations("archives") + publish = true + override = true - publish = true - override = true - - pkg.apply { - repo = bintrayRepo - name = bintrayPkgName - setLicenses("AGPLv3") - publicDownloadNumbers = true - vcsUrl = vcs + pkg.apply { + repo = bintrayRepo + name = bintrayPkgName + setLicenses("AGPLv3") + publicDownloadNumbers = true + vcsUrl = vcs + } } } @@ -94,6 +120,10 @@ inline fun Project.configurePublishing( archiveClassifier.set("sources") from(sourceSets["main"].allSource) } + val stubJavadoc = tasks.register("javadocJar", Jar::class) { + @Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS") + archiveClassifier.set("javadoc") + } publishing { publications { @@ -104,16 +134,15 @@ inline fun Project.configurePublishing( setArtifactId(artifactId) version = project.version.toString() - pom.withXml { - val root = asNode() - root.appendNode("description", description) - root.appendNode("name", project.name) - root.appendNode("url", vcs) - root.children().last() - } + setupPom( + project = project, + vcs = vcs + ) artifact(sourcesJar.get()) + artifact(stubJavadoc.get()) } } + configGpgSign(this@configurePublishing) } } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/MppPublishing.kt b/buildSrc/src/main/kotlin/MppPublishing.kt index eff90e5ea..f93acf51a 100644 --- a/buildSrc/src/main/kotlin/MppPublishing.kt +++ b/buildSrc/src/main/kotlin/MppPublishing.kt @@ -21,7 +21,7 @@ fun logPublishing(message: String) { } fun Project.configureMppPublishing() { - configureBintray() + configureRemoteRepos() // mirai does some magic on MPP targets afterEvaluate { @@ -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)) } } } @@ -61,9 +64,10 @@ fun Project.configureMppPublishing() { logPublishing("Publications: ${publications.joinToString { it.name }}") publications.filterIsInstance().forEach { publication -> - if (publication.name != "kotlinMultiplatform") { - publication.artifact(stubJavadoc) - } + // Maven Central always require javadoc.jar + publication.artifact(stubJavadoc) + + publication.setupPom(project) logPublishing(publication.name) when (val type = publication.name) { @@ -86,6 +90,7 @@ fun Project.configureMppPublishing() { } } } + configGpgSign(this@configureMppPublishing) } } } @@ -129,6 +134,6 @@ val publishPlatformArtifactsInRootModule: Project.(MavenPublication) -> Unit = { } } -private fun MavenArtifact.smartToString(): String { +public fun MavenArtifact.smartToString(): String { return "${file.path}, classifier=${classifier}, ext=${extension}" } diff --git a/buildSrc/src/main/kotlin/PublishingGpgSign.kt b/buildSrc/src/main/kotlin/PublishingGpgSign.kt new file mode 100644 index 000000000..e0a52c3c4 --- /dev/null +++ b/buildSrc/src/main/kotlin/PublishingGpgSign.kt @@ -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 { + if (publication is DefaultMavenPublication) { + return DefaultMavenPublication::class.java.getDeclaredField("metadataArtifacts") + .also { it.isAccessible = true } + .get(publication) as Collection + } + 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, (MavenArtifact) -> Unit>> = listOf( + publication.artifacts to { publication.artifact(it) }, // main artifacts + PublishingAccess.getMetadataArtifacts(publication).let { artifacts -> // pom files + if (artifacts is MutableCollection) { + 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)) + } + } + } + } + } + +} diff --git a/buildSrc/src/main/kotlin/PublishingHelpers.kt b/buildSrc/src/main/kotlin/PublishingHelpers.kt index aaf9817b1..226b0325b 100644 --- a/buildSrc/src/main/kotlin/PublishingHelpers.kt +++ b/buildSrc/src/main/kotlin/PublishingHelpers.kt @@ -15,6 +15,7 @@ import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.publish.PublicationContainer +import org.gradle.api.publish.maven.MavenPublication import org.gradle.api.tasks.TaskContainer import org.gradle.kotlin.dsl.ExistingDomainObjectDelegate import org.gradle.kotlin.dsl.RegisteringDomainObjectDelegateProviderWithTypeAndAction @@ -77,3 +78,40 @@ val Project.publications: PublicationContainer } return ret } + +fun MavenPublication.setupPom( + project: Project, + vcs: String = "https://github.com/mamoe/mirai" +) { + pom { + scm { + url.set(vcs) + connection.set("scm:$vcs.git") + developerConnection.set("scm:${vcs.replace("https:", "git:")}.git") + } + + licenses { + license { + name.set("GNU AGPLv3") + url.set("https://github.com/mamoe/mirai/blob/master/LICENSE") + } + } + + developers { + developer { + id.set("mamoe") + name.set("Mamoe Technologies") + email.set("support@mamoe.net") + } + } + + } + + pom.withXml { + val root = asNode() + root.appendNode("description", project.description) + root.appendNode("name", project.name) + root.appendNode("url", vcs) + } +} + diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 825c130ea..52c8aa478 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -12,7 +12,7 @@ import org.gradle.api.attributes.Attribute object Versions { - const val project = "2.4-M1" + const val project = "2.4-M1-dev-publish-3" const val core = project const val console = project diff --git a/buildSrc/src/main/kotlin/keys/SecretKeys.kt b/buildSrc/src/main/kotlin/keys/SecretKeys.kt new file mode 100644 index 000000000..e849e5f99 --- /dev/null +++ b/buildSrc/src/main/kotlin/keys/SecretKeys.kt @@ -0,0 +1,123 @@ +/* + * 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 + */ + +package keys + +import org.gradle.api.Project +import java.io.BufferedReader + +open class SecretKeys( + val type: String, + val user: String, + val password: String +) { + class Invalid( + type: String, + override val isDisabled: Boolean = false + ) : SecretKeys(type, "", "") { + override val isValid: Boolean get() = false + override fun requireNotInvalid(): Nothing { + error( + """ + Key $type not found. + Please lease specify by creating a file $type.key in projectDir/build-secret-keys + or by providing JVM parameter '$type.user', `$type.password` + """.trimIndent() + ) + } + } + + companion object { + val keyCaches = mutableMapOf() + + @JvmStatic + fun getCache(project: Project): ProjectKeysCache = + keyCaches.computeIfAbsent(project, SecretKeys::ProjectKeysCache) + } + + class ProjectKeysCache(val project: Project) { + val keys = mutableMapOf() + fun loadKey(type: String) = keys.computeIfAbsent(type, this::loadKey0) + + private fun loadKey0(type: String): SecretKeys { + + project.parent?.let { parent -> + getCache(parent).loadKey(type).takeIf { + it.isValid || it.isDisabled + }?.let { return it } + } + + val secretKeys = project.projectDir.resolve("build-secret-keys") + + kotlin.run { + val secretKeyFile = secretKeys.resolve("$type.disable").takeIf { it.isFile } + ?: secretKeys.resolve("$type.disable.txt") + if (secretKeyFile.isFile) return Invalid(type, true) // Disabled + } + + // Load from secretKeys/$type.key + kotlin.run { + val secretKeyFile = secretKeys.resolve("$type.key").takeIf { it.isFile } + ?: secretKeys.resolve("$type.key.txt") + if (secretKeyFile.isFile) { + secretKeyFile.bufferedReader().use { + fun BufferedReader.readLineNonEmpty(): String { + while (true) { + val nextLine = readLine() ?: return "" + if (nextLine.isNotBlank()) { + return nextLine.trim() + } + } + } + return SecretKeys(type, it.readLineNonEmpty(), it.readLineNonEmpty()) + } + } + } + // Load from project/%type.key, user + kotlin.run { + val userFile = project.projectDir.resolve("$type.user.txt") + val keyFile = project.projectDir.resolve("$type.key.txt") + if (userFile.isFile && keyFile.isFile) { + return SecretKeys(type, userFile.readText().trim(), keyFile.readText().trim()) + } + } + + + // Load from property $type.user, $type.password + + fun findProperty(type: String): String? { + val p = project.findProperty(type) + ?: System.getProperty(type) + ?: System.getenv(type) + + return p?.toString() + } + + val tUser = findProperty("$type.user") + ?: findProperty("${type}_user") + + val tPassword = findProperty("$type.password") + ?: findProperty("$type.passwd") + ?: findProperty("$type.key") + ?: findProperty("${type}_password") + ?: findProperty("${type}_passwd") + ?: findProperty("${type}_key") + + if (tUser != null && tPassword != null) { + return SecretKeys(type, tUser, tPassword) + } + + return Invalid(type) + } + } + + open val isValid: Boolean get() = true + open val isDisabled: Boolean get() = false + open fun requireNotInvalid(): SecretKeys = this +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/upload/Bintray.kt b/buildSrc/src/main/kotlin/upload/Bintray.kt index 1041b7e03..615c330b1 100644 --- a/buildSrc/src/main/kotlin/upload/Bintray.kt +++ b/buildSrc/src/main/kotlin/upload/Bintray.kt @@ -7,18 +7,9 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ +import keys.SecretKeys import org.gradle.api.Project -import org.gradle.kotlin.dsl.provideDelegate -import java.io.File -/* - * Copyright 2019-2020 Mamoe Technologies and contributors. - * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions license that can be found via the following link. - * - * https://github.com/mamoe/mirai/blob/master/LICENSE - */ fun Project.isBintrayAvailable() = Bintray.isBintrayAvailable(project) @Suppress("DuplicatedCode") @@ -34,76 +25,18 @@ object Bintray { @JvmStatic fun getUser(project: Project): String { - kotlin.runCatching { - @Suppress("UNUSED_VARIABLE", "LocalVariableName") - val bintray_user: String by project - return bintray_user - } - - kotlin.runCatching { - @Suppress("UNUSED_VARIABLE", "LocalVariableName") - val bintray_user: String by project.rootProject - return bintray_user - } - - System.getProperty("bintray_user", null)?.let { - return it.trim() - } - - File(File(System.getProperty("user.dir")).parent, "/bintray.user.txt").let { local -> - if (local.exists()) { - return local.readText().trim() - } - } - - File(File(System.getProperty("user.dir")), "/bintray.user.txt").let { local -> - if (local.exists()) { - return local.readText().trim() - } - } - - error( - "Cannot find bintray user, " + - "please specify by creating a file bintray.user.txt in project dir, " + - "or by providing JVM parameter 'bintray_user'" - ) + return SecretKeys.getCache(project) + .loadKey("bintray") + .requireNotInvalid() + .user } @JvmStatic fun getKey(project: Project): String { - kotlin.runCatching { - @Suppress("UNUSED_VARIABLE", "LocalVariableName") - val bintray_key: String by project - return bintray_key - } - - kotlin.runCatching { - @Suppress("UNUSED_VARIABLE", "LocalVariableName") - val bintray_key: String by project.rootProject - return bintray_key - } - - System.getProperty("bintray_key", null)?.let { - return it.trim() - } - - File(File(System.getProperty("user.dir")).parent, "/bintray.key.txt").let { local -> - if (local.exists()) { - return local.readText().trim() - } - } - - File(File(System.getProperty("user.dir")), "/bintray.key.txt").let { local -> - if (local.exists()) { - return local.readText().trim() - } - } - - error( - "Cannot find bintray key, " + - "please specify by creating a file bintray.key.txt in project dir, " + - "or by providing JVM parameter 'bintray_key'" - ) + return SecretKeys.getCache(project) + .loadKey("bintray") + .requireNotInvalid() + .password } } \ No newline at end of file diff --git a/ci-release-helper/build.gradle.kts b/ci-release-helper/build.gradle.kts new file mode 100644 index 000000000..3a04e0834 --- /dev/null +++ b/ci-release-helper/build.gradle.kts @@ -0,0 +1,22 @@ +/* + * 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 keys.SecretKeys + +plugins { + id("io.codearte.nexus-staging") version "0.22.0" +} + +description = "Mirai CI Methods for Releasing" + +nexusStaging { + packageGroup = rootProject.group.toString() + val keys = SecretKeys.getCache(project).loadKey("sonatype") + username = keys.user + password = keys.password +} diff --git a/gradle.properties b/gradle.properties index 46f7e3f1c..122691870 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,11 +1,12 @@ # -# Copyright 2019-2020 Mamoe Technologies and contributors. +# 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 # + # style guide kotlin.code.style=official # config @@ -17,4 +18,5 @@ org.gradle.vfs.watch=true kotlin.mpp.enableGranularSourceSetsMetadata=true kotlin.native.enableDependencyPropagation=false #kotlin.mpp.enableGranularSourceSetsMetadata=true -systemProp.org.gradle.internal.publish.checksums.insecure=true \ No newline at end of file +systemProp.org.gradle.internal.publish.checksums.insecure=true +gnsp.disableApplyOnlyOnRootProjectEnforcement=true diff --git a/settings.gradle.kts b/settings.gradle.kts index cb6728cbf..4f6140571 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -29,6 +29,7 @@ include(":mirai-core") include(":mirai-core-all") include(":binary-compatibility-validator") +include(":ci-release-helper") fun includeConsoleProjects() {