Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
package xyz.block.artifactswap

import xyz.block.gradle.services.SharedServiceKey
import xyz.block.gradle.services.SharedServices
import xyz.block.artifactswap.ArtifactSwapBomService.Parameters
import groovy.xml.XmlSlurper
import groovy.xml.slurpersupport.GPathResult
import java.nio.file.Path
import kotlin.io.path.Path
import kotlin.io.path.exists
import kotlin.io.path.inputStream
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import org.gradle.api.provider.Property
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
import java.nio.file.Path
import kotlin.io.path.Path
import kotlin.io.path.exists
import kotlin.io.path.inputStream

import xyz.block.artifactswap.ArtifactSwapBomService.Parameters
import xyz.block.gradle.services.SharedServiceKey
import xyz.block.gradle.services.SharedServices

// Service to parse local BOM file once per sync
abstract class ArtifactSwapBomService : BuildService<Parameters> {
Expand Down Expand Up @@ -44,13 +43,15 @@ abstract class ArtifactSwapBomService : BuildService<Parameters> {
val dependencyManagement = pom.getProperty("dependencyManagement") as GPathResult
val dependencies = dependencyManagement.getProperty("dependencies") as GPathResult
val dependencySequence = dependencies.children().asSequence().filterIsInstance<GPathResult>()
dependencySequence
.associate { it.getProperty("artifactId").toString() to it.getProperty("version").toString() }
dependencySequence.associate {
it.getProperty("artifactId").toString() to it.getProperty("version").toString()
}
} else {
logger.error("Artifact sync bom does not exist: {}", bomFile)
emptyMap()
}
}
}

internal val SharedServices.artifactSyncBomService get() = get(ArtifactSwapBomService.KEY)
internal val SharedServices.artifactSyncBomService
get() = get(ArtifactSwapBomService.KEY)
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package xyz.block.artifactswap

import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
import xyz.block.gradle.services.SharedServiceKey
import xyz.block.gradle.services.SharedServices
import java.io.File

/**
* Build service that holds artifact swap publishing configuration. This allows the configuration to
* be set at settings time and accessed in projects in a configuration-cache compatible way.
*/
abstract class ArtifactSwapConfigService : BuildService<ArtifactSwapConfigService.Params> {

interface Params : BuildServiceParameters {
val artifactHashFile: RegularFileProperty
val repoUrl: Property<String>
val repoUsername: Property<String>
val repoPassword: Property<String>
}

internal object KEY : SharedServiceKey<ArtifactSwapConfigService, Params>("artifactSwapConfig")
}

internal val SharedServices.artifactSwapConfigService
get() = get(ArtifactSwapConfigService.KEY)
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

package xyz.block.artifactswap

import xyz.block.gradle.generatedProtosVersion
import xyz.block.gradle.protosSchemaVersion
import xyz.block.gradle.services.services
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.DependencySubstitution
import org.gradle.api.artifacts.component.ModuleComponentSelector
import org.jetbrains.kotlin.util.prefixIfNot
import xyz.block.gradle.generatedProtosVersion
import xyz.block.gradle.protosSchemaVersion
import xyz.block.gradle.services.services

/**
* Artifact Sync project sub-plugin. This plugin is responsible for performing dependency substitution
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,34 +13,37 @@ import org.gradle.api.publish.maven.MavenPom
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.maven.tasks.PublishToMavenRepository
import org.gradle.api.tasks.bundling.Jar
import xyz.block.gradle.artifactVersion
import xyz.block.gradle.isAndroid
import xyz.block.gradle.isKotlin
import xyz.block.gradle.sandbagVersion
import xyz.block.gradle.toSandbagArtifact
import xyz.block.gradle.services.services
import xyz.block.gradle.toProjectArtifactName

/**
* Artifact Swap project publish plugin for sandbags. This plugin is responsible for configuring
* Maven publishing with sandbag-specific settings when sandbag publishing is enabled.
*
* This plugin extracts the sandbag publishing logic from PublishPlugin and AndroidLibJavaPlugin
* to centralize artifact swap publishing concerns.
* Artifact Swap project publish plugin for project artifacts. This plugin is responsible for
* configuring Maven publishing with artifact-swap-specific settings when artifact publishing is
* enabled.
*
* For reference and searchability, the ID of this plugin is `xyz.block.artifactswap.publish`.
*/
@Suppress("unused")
class ArtifactSwapProjectPublishPlugin : Plugin<Project> {

private lateinit var configService: ArtifactSwapConfigService

override fun apply(target: Project): Unit = target.run {
val version = sandbagVersion ?: return@run
val version = artifactVersion ?: return@run

configService = gradle.services.artifactSwapConfigService

pluginManager.apply("maven-publish")
extensions.getByType(PublishingExtension::class.java).also { mavenPublishing ->
val repo = configureSandbagRepository(mavenPublishing)
val repo = configureArtifactRepository(mavenPublishing)

// Other plugins configure the components to be published, so we have to configure them after
// those plugins run
// Other plugins configure the components to be published, so we have to configure them
// after those plugins run
afterEvaluate {
val publication = configureSandbagPublication(mavenPublishing, version)
val publication = configureArtifactPublication(mavenPublishing, version)
createPublishAliasTask(repo, publication)
}
}
Expand All @@ -50,33 +53,35 @@ class ArtifactSwapProjectPublishPlugin : Plugin<Project> {
}
}

private fun Project.configureSandbagRepository(
private fun Project.configureArtifactRepository(
mavenPublishing: PublishingExtension,
): MavenArtifactRepository = with(mavenPublishing) {
val sandbagsUrl = providers.gradleProperty("square.sandbagsUrl").get()
return repositories.maven { repo ->
repo.name = "artifactSwap"
repo.url = uri(sandbagsUrl)

getSandbagCredentials()?.apply {
repo.credentials(PasswordCredentials::class.java) { creds ->
creds.username = username
creds.password = password
): MavenArtifactRepository =
with(mavenPublishing) {
val repoUrl = configService.parameters.repoUrl.get()
return repositories.maven { repo ->
repo.name = "artifactSwap"
repo.url = uri(repoUrl)

val username = configService.parameters.repoUsername.orNull
val password = configService.parameters.repoPassword.orNull
if (username != null && password != null) {
repo.credentials(PasswordCredentials::class.java) { creds ->
creds.username = username
creds.password = password
}
}
}
}
}

private fun Project.configureSandbagPublication(
private fun Project.configureArtifactPublication(
mavenPublishing: PublishingExtension,
version: String
): MavenPublication {
val publication = mavenPublishing.publications
.maybeCreate("projectArtifact", MavenPublication::class.java)
val publication = mavenPublishing.publications.maybeCreate("projectArtifact", MavenPublication::class.java)

// Automatically configure maven coordinates for sandbag
// Automatically configure maven coordinates for artifact
publication.groupId = ARTIFACT_SWAP_MAVEN_GROUP
publication.artifactId = path.toSandbagArtifact
publication.artifactId = path.toProjectArtifactName
publication.version = version

// For non-Android projects, automatically configure the java component and sources
Expand All @@ -85,7 +90,7 @@ class ArtifactSwapProjectPublishPlugin : Plugin<Project> {
addSourcesArtifact(publication)
}

configureSandbagPom(publication.pom)
configureArtifactPom(publication.pom)

return publication
}
Expand Down Expand Up @@ -113,26 +118,11 @@ class ArtifactSwapProjectPublishPlugin : Plugin<Project> {
}
}

private fun Project.configureSandbagPom(pom: MavenPom) {
private fun Project.configureArtifactPom(pom: MavenPom) {
with(pom) {
name.set(project.name)
description.set("Sandbag for ${project.name} in build ${project.isolated.rootProject.name}")
url.set(providers.gradleProperty("square.repoUrl"))
scm { scm ->
scm.connection.set(providers.gradleProperty("square.scmConnectionUrl"))
scm.developerConnection.set(providers.gradleProperty("square.scmDeveloperConnectionUrl"))
scm.url.set(providers.gradleProperty("square.repoUrl"))
}
}
}

private fun Project.getSandbagCredentials(): SandbagCredentials? {
val username = providers.gradleProperty("square.artifactory.username").orNull
val password = providers.gradleProperty("square.artifactory.password").orNull
return if (username != null && password != null) {
SandbagCredentials(username, password)
} else {
null
description.set("Artifact for ${project.name} in build ${project.isolated.rootProject.name}")
url.set(configService.parameters.repoUrl)
}
}

Expand All @@ -145,9 +135,4 @@ class ArtifactSwapProjectPublishPlugin : Plugin<Project> {
it.dependsOn(tasks.named(publishTaskName))
}
}

private data class SandbagCredentials(
val username: String,
val password: String
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@

package xyz.block.artifactswap

import java.io.File
import org.gradle.api.Plugin
import org.gradle.api.initialization.Settings
import org.gradle.api.initialization.resolve.DependencyResolutionManagement
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import xyz.block.artifactswap.dsl.ArtifactSwapExtension
import xyz.block.gradle.LOCAL_PROTOS_ARTIFACTS
import xyz.block.gradle.bomVersion
import xyz.block.gradle.isArtifactPublishingEnabled
import xyz.block.gradle.services.services
import xyz.block.gradle.useArtifactSync
import xyz.block.gradle.useLocalProtos
import xyz.block.ide.forceSettingsModulesOverride
import xyz.block.ide.isIdeSync
import java.io.File

/**
* Main Artifact Sync settings plugin. This plugin is responsible for:
Expand All @@ -31,7 +31,11 @@ import java.io.File
*/
@Suppress("unused")
class ArtifactSwapSettingsPlugin : Plugin<Settings> {
private lateinit var extension: ArtifactSwapExtension

override fun apply(target: Settings) = target.run {
extension = ArtifactSwapExtension.of(target)

applyProjectIncludes()
maybeApplyArtifactSync()
maybeApplyPublishPlugin()
Expand Down Expand Up @@ -130,13 +134,20 @@ class ArtifactSwapSettingsPlugin : Plugin<Settings> {
}
}

/**
* Applies the publish plugin to all projects when sandbag publishing is enabled.
*/
/** Applies the publish plugin to all projects when artifact publishing is enabled. */
private fun Settings.maybeApplyPublishPlugin() {
if (isArtifactPublishingEnabled) {
gradle.lifecycle.beforeProject { project ->
project.plugins.apply(ArtifactSwapProjectPublishPlugin::class.java)
gradle.settingsEvaluated {
if (extension.publishing.enabled.get()) {
Comment on lines +139 to +140

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it important to evaluate that property here? (I'm just always suspicious of things that "look like" afterEvaluate. Is this preferable to checking the final value of enabled in each project context?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the values from extensions are never available in the initial plugin application, you can only read them in afterSettings (or afterEvaluate for plugins)

gradle.services.register(ArtifactSwapConfigService.KEY) { spec ->
spec.parameters.artifactHashFile.set(extension.publishing.artifactHashFile)
spec.parameters.repoUrl.set(extension.publishing.repo.url)
spec.parameters.repoUsername.set(extension.publishing.repo.username)
spec.parameters.repoPassword.set(extension.publishing.repo.password)
}

gradle.lifecycle.beforeProject { project ->
project.plugins.apply(ArtifactSwapProjectPublishPlugin::class.java)
}
}
}
}
Expand Down
Loading