Skip to content

Instantly share code, notes, and snippets.

@JLLeitschuh
Created June 13, 2019 17:48
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save JLLeitschuh/43f7a45ae9d5fe7f1f58722d265caacf to your computer and use it in GitHub Desktop.
Gradle Plugin Build logic to upload single files to JFrog Artifactory
/* ****************************************************************************** */
// MIT License
//
// Copyright (c) 2019 Hewlett Packard Enterprise Development LP
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
/* ****************************************************************************** */
import org.gradle.api.Action
import org.gradle.api.DefaultTask
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.credentials.PasswordCredentials
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.model.ObjectFactory
import org.gradle.api.publish.plugins.PublishingPlugin
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Nested
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.TaskAction
import org.gradle.kotlin.dsl.property
import org.jfrog.artifactory.client.ArtifactoryClientBuilder
import javax.inject.Inject
open class ArtifactoryExtension
@Inject
internal constructor(
project: Project
) {
val servers: NamedDomainObjectContainer<ArtifactoryServer> =
project.container(ArtifactoryServer::class.java) {
require(it.isNotBlank()) {
"Name of ${ArtifactoryServer::class.simpleName} can not be empty."
}
ArtifactoryServer(
name = it,
project = project
)
}
}
data class ArtifactoryServer
internal constructor(
val name: String,
/**
* The url of the Artifactory server.
*/
var url: String? = null,
/**
* Configures the files to be uploaded.
*/
val upload: NamedDomainObjectContainer<UploadDescriptor>,
internal val internalCredentials: CustomPasswordCredentials
) {
constructor(
name: String,
project: Project
) : this(
name = name,
upload = project.container(UploadDescriptor::class.java) {
require(it.isNotBlank()) {
"Name of ${UploadDescriptor::class.simpleName} can not be empty."
}
UploadDescriptor(
name = it,
file = project.objects.fileProperty()
)
},
internalCredentials = project.objects.newInstance<CustomPasswordCredentials>()
)
/**
* Used to set the credentials to be used to upload the file to the Artifactory server.
*/
val credentials: PasswordCredentials = internalCredentials
fun credentials(configure: Action<PasswordCredentials>) {
configure.execute(credentials)
}
}
data class UploadDescriptor(
val name: String,
/**
* The Artifactory repository the file should be uploaded to.
*/
var repository: String? = null,
/**
* The destination on the Artifactory server that this file should be uploaded to.
*/
var destinationDirectory: String? = null,
/**
* The file to be uploaded to the [destinationDirectory].
*/
val file: RegularFileProperty,
/**
* Specifies that this [file] has been built by a set of tasks.
* @see Task.dependsOn
*/
var builtBy: Iterable<Any> = emptyList()
) {
/**
* Specifies that this [file] has been built by a set of tasks
* @see Task.dependsOn
*/
fun builtBy(vararg tasks: Any) {
builtBy += listOf(*tasks)
}
}
open class ArtifactoryPlugin : Plugin<Project> {
companion object {
const val ARTIFACTORY_PUBLISH_TASK_NAME = "publishToArtifactory"
}
override fun apply(target: Project) {
target.plugins.apply(PublishingPlugin::class.java)
val extension = target.extensions
.create("artifactory", ArtifactoryExtension::class.java, target)
val publishToArtifactory = target.tasks.register(ARTIFACTORY_PUBLISH_TASK_NAME) {
group = PublishingPlugin.PUBLISH_TASK_GROUP
description = "Publishes all of the files that have been configured to Artifactory."
}
extension.servers.all {
val server = this
server.upload.all {
val descriptor = this
val publishFileToArtifactory = target.tasks
.register(
"publish${descriptor.name.capitalize()}To${server.name.capitalize()}ArtifactoryServer",
UploadToArtifactoryTask::class.java
) {
url.set(target.provider { server.url })
repository.set(target.provider { requireNotNull(descriptor.repository) { "repository" } })
credentials.set(target.provider { server.internalCredentials })
destinationDirectory.set(target.provider { requireNotNull(descriptor.destinationDirectory) { "destinationDirectory" } })
fileToUpload.set(descriptor.file)
dependsOn(target.provider { descriptor.builtBy })
}
publishToArtifactory.configure { dependsOn(publishFileToArtifactory) }
}
}
target.tasks.named(PublishingPlugin.PUBLISH_LIFECYCLE_TASK_NAME).configure {
dependsOn(publishToArtifactory)
}
}
}
open class UploadToArtifactoryTask
@Inject
constructor(
objectFactory: ObjectFactory
) : DefaultTask() {
@get:Input
val url = objectFactory.stringProperty()
@get:Input
val repository = objectFactory.stringProperty()
@get:Nested
val credentials = objectFactory.property<CustomPasswordCredentials>()
@get:Input
val destinationDirectory = objectFactory.stringProperty()
@get:InputFile
@get:PathSensitive(PathSensitivity.RELATIVE)
val fileToUpload = objectFactory.fileProperty()
@TaskAction
fun doUpload() {
val artifactory =
ArtifactoryClientBuilder
.create()
.setUrl(url.get())
.setUsername(requireNotNull(credentials.get().username) { "username" })
.setPassword(requireNotNull(credentials.get().password) { "password" })
.build()
val fileToUpload = fileToUpload.asFile.get()
// Add a trailing slash if one isn't present.
val destinationBase = destinationDirectory.get().let {
if (it.endsWith("/")) it else (it + "/")
}
val destinationPathFull = destinationBase + fileToUpload.name
val uploadedFile =
artifactory
.repository(repository.get())
.upload(destinationPathFull, fileToUpload)
.bySha1Checksum()
.doUpload()
logger.lifecycle("File uploaded to: ${uploadedFile.uri}")
}
}
open class CustomPasswordCredentials
constructor(
@field:Internal
private var user: String? = null,
@field:Internal
private var pass: String? = null
) : PasswordCredentials {
@Inject
constructor() : this(null, null)
override fun setUsername(userName: String?) {
user = userName
}
@Input
override fun getUsername(): String? {
return user
}
@Input
override fun getPassword(): String? {
return pass
}
override fun setPassword(password: String?) {
pass = password
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment