Unverified Commit 2bc9c86e authored by Alexey Tsvetkov's avatar Alexey Tsvetkov Committed by GitHub

Use published build helper module for publishing (#350)

parent d9b136ef
...@@ -2,13 +2,14 @@ import de.undercouch.gradle.tasks.download.Download ...@@ -2,13 +2,14 @@ import de.undercouch.gradle.tasks.download.Download
import org.gradle.crypto.checksum.Checksum import org.gradle.crypto.checksum.Checksum
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile
import org.jetbrains.compose.internal.publishing.MavenCentralProperties
plugins { plugins {
kotlin("multiplatform") version "1.5.31" kotlin("multiplatform") version "1.5.31"
`maven-publish` `maven-publish`
signing signing
id("org.gradle.crypto.checksum") version "1.1.0" id("org.gradle.crypto.checksum") version "1.1.0"
id("de.undercouch.download") id("de.undercouch.download") version "4.1.2"
} }
val coroutinesVersion = "1.5.2" val coroutinesVersion = "1.5.2"
...@@ -1102,10 +1103,11 @@ publishing { ...@@ -1102,10 +1103,11 @@ publishing {
} }
} }
if (skiko.isCIBuild || skiko.signArtifacts) { val mavenCentral = MavenCentralProperties(project)
if (skiko.isCIBuild || mavenCentral.signArtifacts) {
signing { signing {
sign(publishing.publications) sign(publishing.publications)
useInMemoryPgpKeys(skiko.signArtifactsKey, skiko.signArtifactsPassword) useInMemoryPgpKeys(mavenCentral.signArtifactsKey.get(), mavenCentral.signArtifactsPassword.get())
} }
} }
......
...@@ -9,6 +9,4 @@ repositories { ...@@ -9,6 +9,4 @@ repositories {
dependencies { dependencies {
implementation(kotlin("stdlib")) implementation(kotlin("stdlib"))
compileOnly(gradleApi()) compileOnly(gradleApi())
api(project(":publishing"))
} }
import org.gradle.kotlin.dsl.gradleKotlinDsl
plugins {
id("org.jetbrains.kotlin.jvm")
}
repositories {
mavenCentral()
}
dependencies {
compileOnly(gradleApi())
compileOnly(gradleKotlinDsl())
compileOnly(kotlin("stdlib"))
val jacksonVersion = "2.12.5"
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml:$jacksonVersion")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion")
implementation("io.ktor:ktor-client-okhttp:1.6.2")
implementation("org.apache.tika:tika-parsers:1.24.1")
implementation("org.jsoup:jsoup:1.14.3")
implementation("de.undercouch:gradle-download-task:4.1.2")
}
package org.jetbrains.compose.internal.gradle.publishing
import de.undercouch.gradle.tasks.download.DownloadAction
import org.gradle.api.DefaultTask
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.TaskAction
import org.jsoup.Jsoup
import java.net.URL
abstract class DownloadFromSpaceMavenRepoTask : DefaultTask() {
@get:Internal
abstract val modulesToDownload: ListProperty<ModuleToUpload>
@get:Internal
abstract val spaceRepoUrl: Property<String>
@TaskAction
fun run() {
for (module in modulesToDownload.get()) {
downloadArtifactsFromComposeDev(module)
}
}
private fun downloadArtifactsFromComposeDev(module: ModuleToUpload) {
val groupUrl = module.groupId.replace(".", "/")
val filesListingDocument =
Jsoup.connect("${spaceRepoUrl.get()}/$groupUrl/${module.artifactId}/${module.version}/").get()
val downloadableFiles = HashMap<String, URL>()
for (a in filesListingDocument.select("#contents > a")) {
val href = a.attributes().get("href")
val lastPart = href.substringAfterLast("/", "")
// check if URL points to a file
if (lastPart.isNotEmpty() && lastPart.contains(".")) {
downloadableFiles[lastPart] = URL(href)
}
}
val destinationDir = module.localDir
if (destinationDir.exists()) {
if (module.version.endsWith("-SNAPSHOT")) {
destinationDir.deleteRecursively()
} else {
// delete existing files, that are not downloadable
val existingFiles = (destinationDir.list() ?: emptyArray()).toSet()
for (existingFileName in existingFiles) {
if (existingFileName !in downloadableFiles) {
destinationDir.resolve(existingFileName).delete()
}
}
// don't re-download all files for non-snapshot version
val it = downloadableFiles.entries.iterator()
while (it.hasNext()) {
val (fileName, _) = it.next()
if (fileName in existingFiles) {
it.remove()
}
}
}
}
DownloadAction(project, this).apply {
src(downloadableFiles.values)
dest(destinationDir)
}.execute()
}
}
\ No newline at end of file
package org.jetbrains.compose.internal.gradle.publishing
import java.io.File
data class ModuleToUpload(
val groupId: String,
val artifactId: String,
val version: String,
val localDir: File
) {
internal fun listFiles(): Array<File> =
localDir.listFiles() ?: emptyArray()
internal val coordinate: String
get() = "$groupId:$artifactId:$version"
}
\ No newline at end of file
package org.jetbrains.compose.internal.gradle.publishing
import org.gradle.api.DefaultTask
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.TaskAction
import org.jetbrains.compose.internal.gradle.publishing.sonatype.ModuleValidator
import org.jetbrains.compose.internal.gradle.publishing.sonatype.SonatypeApi
import org.jetbrains.compose.internal.gradle.publishing.sonatype.SonatypeRestApiClient
import org.jetbrains.compose.internal.gradle.publishing.sonatype.StagingProfile
abstract class UploadToSonatypeTask : DefaultTask() {
// the task must always re-run anyway, so all inputs can be declared Internal
@get:Internal
abstract val sonatypeServer: Property<String>
@get:Internal
abstract val user: Property<String>
@get:Internal
abstract val password: Property<String>
@get:Internal
abstract val stagingProfileName: Property<String>
@get:Internal
abstract val autoCommitOnSuccess: Property<Boolean>
@get:Internal
abstract val autoDropOnError: Property<Boolean>
@get:Internal
abstract val version: Property<String>
@get:Internal
abstract val modulesToUpload: ListProperty<ModuleToUpload>
@TaskAction
fun run() {
SonatypeRestApiClient(
sonatypeServer = sonatypeServer.get(),
user = user.get(),
password = password.get(),
logger = logger
).use { client -> run(client) }
}
private fun run(sonatype: SonatypeApi) {
val stagingProfiles = sonatype.stagingProfiles()
val stagingProfileName = stagingProfileName.get()
val stagingProfile = stagingProfiles.data.firstOrNull { it.name == stagingProfileName }
?: error(
"Cannot find staging profile '$stagingProfileName' among existing staging profiles: " +
stagingProfiles.data.joinToString { "'${it.name}'" }
)
val modules = modulesToUpload.get()
validate(stagingProfile, modules)
val stagingRepo = sonatype.createStagingRepo(
stagingProfile, "Staging repo for '${stagingProfile.name}' release '${version.get()}'"
)
try {
for (module in modules) {
sonatype.upload(stagingRepo, module)
}
if (autoCommitOnSuccess.get()) {
sonatype.closeStagingRepo(stagingRepo)
}
} catch (e: Exception) {
if (autoDropOnError.get()) {
sonatype.dropStagingRepo(stagingRepo)
}
throw e
}
}
private fun validate(stagingProfile: StagingProfile, modules: List<ModuleToUpload>) {
val validationIssues = arrayListOf<Pair<ModuleToUpload, ModuleValidator.Status.Error>>()
for (module in modules) {
val status = ModuleValidator(stagingProfile, module, version.get()).validate()
if (status is ModuleValidator.Status.Error) {
validationIssues.add(module to status)
}
}
if (validationIssues.isNotEmpty()) {
val message = buildString {
appendLine("Some modules violate Maven Central requirements:")
for ((module, status) in validationIssues) {
appendLine("* ${module.coordinate} (files: ${module.localDir})")
for (error in status.errors) {
appendLine(" * $error")
}
}
}
error(message)
}
}
}
\ No newline at end of file
package org.jetbrains.compose.internal.gradle.publishing.sonatype
import okhttp3.*
import okhttp3.internal.http.RealResponseBody
import okio.Buffer
import org.gradle.api.logging.Logger
import java.net.URL
import java.time.Duration
import java.util.concurrent.atomic.AtomicLong
internal class RestApiClient(
private val serverUrl: String,
private val user: String,
private val password: String,
private val logger: Logger,
) : AutoCloseable {
private val okClient by lazy {
OkHttpClient.Builder()
.readTimeout(Duration.ofMinutes(1))
.build()
}
fun buildRequest(urlPath: String, configure: Request.Builder.() -> Unit): Request =
Request.Builder().apply {
addHeader("Authorization", Credentials.basic(user, password))
url(URL("$serverUrl/$urlPath"))
configure()
}.build()
fun <T> execute(
request: Request,
retries: Int = 5,
delaySec: Long = 10,
processResponse: (ResponseBody) -> T
): T {
val message = "Remote request #${globalRequestCounter.incrementAndGet()}"
val startTimeNs = System.nanoTime()
logger.info("$message: ${request.method} '${request.url}'")
val delayMs = delaySec * 1000
for (i in 1..retries) {
try {
return okClient.newCall(request).execute().use { response ->
val endTimeNs = System.nanoTime()
logger.info("$message: finished in ${(endTimeNs - startTimeNs)/1_000_000} ms")
if (!response.isSuccessful)
throw RequestError(request, response)
val responseBody = response.body ?: RealResponseBody(null, 0, Buffer())
processResponse(responseBody)
}
} catch (e: Exception) {
if (i == retries) {
throw RuntimeException("$message: failed all $retries attempts, see nested exception for details", e)
}
logger.info("$message: retry #$i of $retries failed. Retrying in $delayMs ms\n${e.message}")
Thread.sleep(delayMs)
}
}
error("Unreachable")
}
override fun close() {
okClient.connectionPool.evictAll()
}
companion object {
private val globalRequestCounter = AtomicLong()
}
}
package org.jetbrains.compose.internal.gradle.publishing.sonatype
import com.fasterxml.jackson.annotation.JsonRootName
import org.jetbrains.compose.internal.gradle.publishing.ModuleToUpload
import java.io.File
internal class ModuleValidator(
private val stagingProfile: StagingProfile,
private val module: ModuleToUpload,
private val version: String
) {
private val errors = arrayListOf<String>()
private var status: Status? = null
sealed class Status {
object OK : Status()
class Error(val errors: List<String>) : Status()
}
fun validate(): Status {
if (status == null) {
validateImpl()
status = if (errors.isEmpty()) Status.OK
else Status.Error(errors)
}
return status!!
}
private fun validateImpl() {
if (!module.groupId.startsWith(stagingProfile.name)) {
errors.add("Module's group id '${module.groupId}' does not match staging repo '${stagingProfile.name}'")
}
if (module.version != version) {
errors.add("Unexpected version '${module.version}' (expected: '$version')")
}
val pomFile = artifactFile(extension = "pom")
val pom = when {
pomFile.exists() ->
try {
// todo: validate POM
Xml.deserialize<Pom>(pomFile.readText())
} catch (e: Exception) {
errors.add("Cannot deserialize $pomFile: $e")
null
}
else -> null
}
val packageFile = artifactFile(extension = pom?.packaging ?: "jar")
val sourcesJar = artifactFile(extension = "jar", classifier = "sources")
val javadocJar = artifactFile(extension = "jar", classifier = "javadoc")
val nonExistingFiles = listOf(pomFile, packageFile, sourcesJar, javadocJar)
.filter { !it.exists() }
if (nonExistingFiles.isNotEmpty()) {
errors.add("Some necessary files do not exist: [${nonExistingFiles.map { it.name }.joinToString()}]")
}
// signatures and checksums should not be signed themselves
val skipSignatureCheckExtensions = setOf("asc", "md5", "sha1", "sha256", "sha512")
val unsignedFiles = module.listFiles()
.filter {
it.extension !in skipSignatureCheckExtensions && !it.resolveSibling(it.name + ".asc").exists()
}
if (unsignedFiles.isNotEmpty()) {
errors.add("Some files are not signed: [${unsignedFiles.map { it.name }.joinToString()}]")
}
}
private fun artifactFile(extension: String, classifier: String? = null): File {
val fileName = buildString {
append("${module.artifactId}-${module.version}")
if (classifier != null)
append("-$classifier")
append(".$extension")
}
return module.localDir.resolve(fileName)
}
}
@JsonRootName("project")
private data class Pom(
var groupId: String? = null,
var artifactId: String? = null,
var packaging: String? = null,
var name: String? = null,
var description: String? = null,
var url: String? = null,
var scm: Scm? = null,
var licenses: List<License>? = null,
var developers: List<Developer>? = null,
) {
internal data class Scm(
var connection: String?,
var developerConnection: String?,
var url: String?,
)
internal data class License(
var name: String? = null,
var url: String? = null
)
internal data class Developer(
var name: String? = null,
var organization: String? = null,
var organizationUrl: String? = null
)
}
package org.jetbrains.compose.internal.gradle.publishing.sonatype
import okhttp3.Request
import okhttp3.Response
internal class RequestError(
val request: Request,
val response: Response,
responseBody: String
) : RuntimeException("${request.url}: returned ${response.code}\n${responseBody.trim()}")
internal fun RequestError(request: Request, response: Response): RequestError {
var responseBodyException: Throwable? = null
val responseBody = try {
response.body?.string() ?: ""
} catch (t: Throwable) {
responseBodyException = t
""
}
return RequestError(request, response, responseBody).apply {
if (responseBodyException != null) addSuppressed(responseBodyException)
}
}
\ No newline at end of file
package org.jetbrains.compose.internal.gradle.publishing.sonatype
import com.fasterxml.jackson.annotation.JsonRootName
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper
import org.jetbrains.compose.internal.gradle.publishing.ModuleToUpload
interface SonatypeApi {
fun upload(repo: StagingRepo, module: ModuleToUpload)
fun stagingProfiles(): StagingProfiles
fun createStagingRepo(profile: StagingProfile, description: String): StagingRepo
fun dropStagingRepo(repo: StagingRepo)
fun closeStagingRepo(repo: StagingRepo)
}
@JsonRootName("stagingProfile")
data class StagingProfile(
var id: String = "",
var name: String = "",
)
@JsonRootName("stagingProfiles")
class StagingProfiles(
@JacksonXmlElementWrapper
var data: List<StagingProfile>
)
data class StagingRepo(
val id: String,
val description: String,
val profile: StagingProfile
) {
constructor(
response: PromoteResponse,
profile: StagingProfile
) : this(
id = response.data.stagedRepositoryId!!,
description = response.data.description,
profile = profile
)
@JsonRootName("promoteRequest")
data class PromoteRequest(var data: PromoteData)
@JsonRootName("promoteResponse")
data class PromoteResponse(var data: PromoteData)
data class PromoteData(var stagedRepositoryId: String? = null, var description: String)
}
package org.jetbrains.compose.internal.gradle.publishing.sonatype
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.Request
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.ResponseBody
import org.apache.tika.Tika
import org.gradle.api.logging.Logger
import org.jetbrains.compose.internal.gradle.publishing.ModuleToUpload
import java.io.Closeable
import java.io.File
// https://support.sonatype.com/hc/en-us/articles/213465868-Uploading-to-a-Staging-Repository-via-REST-API
class SonatypeRestApiClient(
sonatypeServer: String,
user: String,
password: String,
private val logger: Logger,
) : SonatypeApi, Closeable {
private val client = RestApiClient(sonatypeServer, user, password, logger)
private fun buildRequest(urlPath: String, builder: Request.Builder.() -> Unit): Request =
client.buildRequest(urlPath, builder)
private fun <T> Request.execute(processResponse: (ResponseBody) -> T): T =
client.execute(this, processResponse = processResponse)
override fun close() {
client.close()
}
override fun upload(repo: StagingRepo, module: ModuleToUpload) {
for (file in module.localDir.listFiles()!!) {
uploadFile(repo, module, file)
}
}
private fun uploadFile(repo: StagingRepo, module: ModuleToUpload, file: File) {
val fileType = Tika().detect(file.name)
logger.info("Uploading $file (detected type='$fileType', length=${file.length()})")
val deployUrl = "service/local/staging/deployByRepositoryId/${repo.id}"
val groupUrl = module.groupId.replace(".", "/")
val coordinateUrl = "$groupUrl/${module.artifactId}/${module.version}"
val uploadUrlPath = "$deployUrl/$coordinateUrl/${file.name}"
buildRequest(uploadUrlPath) {
header("Content-type", fileType)
put(file.asRequestBody(fileType.toMediaTypeOrNull()))
}.execute { }
}
override fun stagingProfiles(): StagingProfiles =
buildRequest("service/local/staging/profiles") {
get()
}.execute { responseBody ->
Xml.deserialize(responseBody.string())
}
override fun createStagingRepo(profile: StagingProfile, description: String): StagingRepo {
logger.info("Creating sonatype staging repository for `${profile.id}` with description `$description`")
val response =
buildRequest("service/local/staging/profiles/${profile.id}/start") {
val promoteRequest = StagingRepo.PromoteRequest(
StagingRepo.PromoteData(description = description)
)
post(Xml.serialize(promoteRequest).toRequestBody(Xml.mediaType))
}.execute { responseBody ->
Xml.deserialize<StagingRepo.PromoteResponse>(responseBody.string())
}
return StagingRepo(response, profile)
}
override fun dropStagingRepo(repo: StagingRepo) {
stagingRepoAction("drop", repo)
}
override fun closeStagingRepo(repo: StagingRepo) {
stagingRepoAction("finish", repo)
}
private fun stagingRepoAction(
action: String, repo: StagingRepo
) {
val logRepoDescription = "profileId='${repo.profile.id}', repoId='${repo.id}', description='${repo.description}'"
logger.info("Starting '$action': $logRepoDescription")
buildRequest("service/local/staging/${repo.profile.id}/$action") {
val promoteRequest = StagingRepo.PromoteRequest(
StagingRepo.PromoteData(stagedRepositoryId = repo.id, description = repo.description)
)
post(Xml.serialize(promoteRequest).toRequestBody(Xml.mediaType))
}.execute { responseBody ->
logger.info("Finished '$action': $logRepoDescription")
logger.info("Response: '${responseBody.string()}'")
}
}
}
package org.jetbrains.compose.internal.gradle.publishing.sonatype
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.MapperFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.dataformat.xml.JacksonXmlModule
import com.fasterxml.jackson.dataformat.xml.XmlMapper
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import okhttp3.MediaType.Companion.toMediaType
internal object Xml {
val mediaType = "application/xml".toMediaType()
fun serialize(value: Any): String =
kotlinXmlMapper.writeValueAsString(value)
inline fun <reified T> deserialize(xml: String): T =
kotlinXmlMapper.readValue(xml, T::class.java)
private val kotlinXmlMapper: ObjectMapper =
XmlMapper(JacksonXmlModule().apply {
setDefaultUseWrapper(false)
}).registerKotlinModule()
.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
}
include(":publishing")
\ No newline at end of file
...@@ -128,17 +128,6 @@ class SkikoProperties(private val myProject: Project) { ...@@ -128,17 +128,6 @@ class SkikoProperties(private val myProject: Project) {
val isRelease: Boolean val isRelease: Boolean
get() = myProject.findProperty("deploy.release") == "true" get() = myProject.findProperty("deploy.release") == "true"
val signArtifacts: Boolean
get() = myProject.findProperty("deploy.sign") == "true"
val signArtifactsKey: String
get() = System.getenv("MAVEN_ARTIFACTS_SIGN_KEY")
?: error("Environment variable 'MAVEN_ARTIFACTS_SIGN_KEY' is not specified")
val signArtifactsPassword: String
get() = System.getenv("MAVEN_ARTIFACTS_SIGN_PASSWORD")
?: error("Environment variable 'MAVEN_ARTIFACTS_SIGN_PASSWORD' is not specified")
val buildType: SkiaBuildType val buildType: SkiaBuildType
get() = if (myProject.findProperty("skiko.debug") == "true") SkiaBuildType.DEBUG else SkiaBuildType.RELEASE get() = if (myProject.findProperty("skiko.debug") == "true") SkiaBuildType.DEBUG else SkiaBuildType.RELEASE
...@@ -189,51 +178,6 @@ class SkikoProperties(private val myProject: Project) { ...@@ -189,51 +178,6 @@ class SkikoProperties(private val myProject: Project) {
val dependenciesDir: File val dependenciesDir: File
get() = myProject.rootProject.projectDir.resolve("dependencies") get() = myProject.rootProject.projectDir.resolve("dependencies")
val mavenCentral = MavenCentralProperties(myProject)
}
class MavenCentralProperties(private val myProject: Project) {
private fun propertyProvider(
property: String,
envVar: String? = null,
defaultValue: String? = null
): Provider<String> {
val providers = myProject.providers
var result = providers.gradleProperty(property)
if (envVar != null) {
result = result.orElse(providers.environmentVariable(envVar))
}
if (defaultValue != null) {
result = result.orElse(defaultValue)
} else {
result = result.orElse(providers.provider {
val envVarMessage = if (envVar != null) " or '$envVar' environment variable" else ""
error("Provide value for '$property' Gradle property$envVarMessage")
})
}
return result
}
private fun environmentVariable(variable: String) =
myProject.providers.environmentVariable(variable)
val version: Provider<String> =
propertyProvider("maven.central.version")
val user: Provider<String> =
propertyProvider("maven.central.user", envVar = "MAVEN_CENTRAL_USER")
val password: Provider<String> =
propertyProvider("maven.central.password", envVar = "MAVEN_CENTRAL_PASSWORD")
val autoCommitOnSuccess: Provider<Boolean> =
propertyProvider("maven.central.staging.auto.commit", defaultValue = "false")
.map { it.toBoolean() }
val autoDropOnError: Provider<Boolean> =
propertyProvider("maven.central.staging.auto.drop", defaultValue = "false")
.map { it.toBoolean() }
} }
object SkikoArtifacts { object SkikoArtifacts {
......
import org.kohsuke.github.* import org.kohsuke.github.*
import org.jetbrains.compose.internal.gradle.publishing.* import org.jetbrains.compose.internal.publishing.*
val skiko = SkikoProperties(project) val skiko = SkikoProperties(project)
val GITHUB_REPO = "JetBrains/skiko" val GITHUB_REPO = "JetBrains/skiko"
...@@ -73,22 +73,22 @@ fun skikoMavenModules(version: Provider<String>): Provider<List<ModuleToUpload>> ...@@ -73,22 +73,22 @@ fun skikoMavenModules(version: Provider<String>): Provider<List<ModuleToUpload>>
} }
} }
val mavenCentral = MavenCentralProperties(project)
val downloadSkikoArtifactsFromComposeDev by tasks.registering(DownloadFromSpaceMavenRepoTask::class) { val downloadSkikoArtifactsFromComposeDev by tasks.registering(DownloadFromSpaceMavenRepoTask::class) {
modulesToDownload.set(skikoMavenModules(skiko.mavenCentral.version)) modulesToDownload.set(skikoMavenModules(mavenCentral.version))
spaceRepoUrl.set("https://maven.pkg.jetbrains.space/public/p/compose/dev") spaceRepoUrl.set("https://maven.pkg.jetbrains.space/public/p/compose/dev")
} }
val uploadSkikoArtifactsToMavenCentral by tasks.registering(UploadToSonatypeTask::class) { val uploadSkikoArtifactsToMavenCentral by tasks.registering(UploadToSonatypeTask::class) {
dependsOn(downloadSkikoArtifactsFromComposeDev) dependsOn(downloadSkikoArtifactsFromComposeDev)
val central = skiko.mavenCentral version.set(mavenCentral.version)
version.set(central.version) modulesToUpload.set(skikoMavenModules(mavenCentral.version))
modulesToUpload.set(skikoMavenModules(central.version))
sonatypeServer.set("https://oss.sonatype.org") sonatypeServer.set("https://oss.sonatype.org")
user.set(central.user) user.set(mavenCentral.user)
password.set(central.password) password.set(mavenCentral.password)
autoCommitOnSuccess.set(central.autoCommitOnSuccess) autoCommitOnSuccess.set(mavenCentral.autoCommitOnSuccess)
autoDropOnError.set(central.autoDropOnError) autoDropOnError.set(mavenCentral.autoDropOnError)
stagingProfileName.set("org.jetbrains.skiko") stagingProfileName.set("org.jetbrains.skiko")
} }
pluginManagement { pluginManagement {
repositories { repositories {
mavenCentral()
gradlePluginPortal() gradlePluginPortal()
maven { }
url = uri("https://dl.bintray.com/kotlin/kotlin-eap") buildscript {
repositories {
mavenCentral()
maven("https://maven.pkg.jetbrains.space/public/p/compose/internal")
maven("https://maven.pkg.jetbrains.space/public/p/space/maven")
}
dependencies {
classpath("org.jetbrains.compose.internal.build-helpers:publishing:0.0.2")
} }
} }
} }
rootProject.name = "skiko" rootProject.name = "skiko"
include("ci") include("ci")
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment