diff --git a/service-artifact-gradle-plugin.iml b/service-artifact-gradle-plugin.iml index 9f901fd..00f339b 100644 --- a/service-artifact-gradle-plugin.iml +++ b/service-artifact-gradle-plugin.iml @@ -35,7 +35,9 @@ - + + + diff --git a/src/main/groovy/com/github/lookout/serviceartifact/AbstractServiceExtension.groovy b/src/main/groovy/com/github/lookout/serviceartifact/AbstractServiceExtension.groovy index 9d266ad..cfe0dc2 100644 --- a/src/main/groovy/com/github/lookout/serviceartifact/AbstractServiceExtension.groovy +++ b/src/main/groovy/com/github/lookout/serviceartifact/AbstractServiceExtension.groovy @@ -1,7 +1,5 @@ package com.github.lookout.serviceartifact -import com.github.lookout.serviceartifact.scm.AbstractScmHandler -import groovy.json.JsonBuilder import org.gradle.api.Project import org.gradle.api.Task @@ -15,56 +13,27 @@ abstract class AbstractServiceExtension { /** * Properly update the compressed archive tasks with the appropriate - * configurations after the serviceJar task has been set up + * configurations after a serviceJar task has been set up * * @param project - * @return */ - protected void setupCompressedArchives(Project project, AbstractScmHandler scmHandler) { + protected void setupCompressedArchives(Object serviceExtension) { + Project project = serviceExtension.project Task tar = project.tasks.findByName('serviceTar') Task zip = project.tasks.findByName('serviceZip') Task jar = project.tasks.findByName('serviceJar') - Task version = project.tasks.create('serviceVersionInfo') { - group ServiceArtifactPlugin.GROUP_NAME - description "Generate the service artifact version information" - - def versionFilePath = "${this.project.buildDir}/VERSION" - outputs.file(versionFilePath).upToDateWhen { false } - - doFirst { - JsonBuilder builder = new JsonBuilder() - builder(buildDate: new Date(), - version: project.version, - name: project.name, - revision: scmHandler?.revision, - builtOn: InetAddress.localHost.hostName) - new File(versionFilePath).write(builder.toPrettyString()) - } - } - /* Ensure our service (distribution) artifact tasks depend on this * jar task */ [tar, zip].each { - String directory = String.format("%s-%s", project.name, project.version) - it.dependsOn(jar) - it.into(directory) { from(jar.outputs.files) } - it.into("${directory}/bin") { from("${project.projectDir}/bin") } - - /* Pack a handy VERSION file containing some built metadata about - * this artifact to help trace it back to builds in the future - */ - it.into(directory) { from version.outputs.files } + it.into(serviceExtension.archiveDirName) { from(jar.outputs.files) } + it.into("${serviceExtension.archiveDirName}/bin") { from("${project.projectDir}/bin") } } } protected void disableJarTask() { - Task jarTask = this.project.tasks.findByName('jar') - - if (jarTask instanceof Task) { - jarTask.enabled = false - } + this.project.tasks.findByName('jar')?.enabled = false } } diff --git a/src/main/groovy/com/github/lookout/serviceartifact/ServiceArtifactExtension.groovy b/src/main/groovy/com/github/lookout/serviceartifact/ServiceArtifactExtension.groovy index 5c53c46..8cae9ed 100644 --- a/src/main/groovy/com/github/lookout/serviceartifact/ServiceArtifactExtension.groovy +++ b/src/main/groovy/com/github/lookout/serviceartifact/ServiceArtifactExtension.groovy @@ -1,6 +1,8 @@ package com.github.lookout.serviceartifact +import groovy.json.JsonBuilder import org.gradle.api.Project +import org.gradle.api.Task import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -22,6 +24,8 @@ class ServiceArtifactExtension { /** SCM Handler appropriate for this execution */ protected AbstractScmHandler _scmHandler + /** Map of metadata that should be written into the artifact's etc/metadata.conf */ + protected Map metadata = [:] ServiceArtifactExtension(final Project project) { this(project, [:]) @@ -33,6 +37,73 @@ class ServiceArtifactExtension { this.env = env } + /** + * Bootstrap and set up whatever internal tasks/helpers we need to set up + */ + void bootstrap() { + String versionFilePath = String.format("%s/VERSION", this.project.buildDir) + + Task version = project.tasks.create('serviceVersionInfo') { + group ServiceArtifactPlugin.GROUP_NAME + description "Generate the service artifact version information" + + outputs.file(versionFilePath).upToDateWhen { false } + + doFirst { + JsonBuilder builder = new JsonBuilder() + builder(generateVersionMap()) + new File(versionFilePath).write(builder.toPrettyString()) + } + } + + [ServiceArtifactPlugin.TAR_TASK, ServiceArtifactPlugin.ZIP_TASK].each { + Task archiveTask = this.project.tasks.findByName(it) + + if (archiveTask instanceof Task) { + archiveTask.dependsOn(version) + /* Pack the VERSION file containing some built metadata about + * this artifact to help trace it back to builds in the future + */ + archiveTask.into(this.archiveDirName) { from version.outputs.files } + } + } + } + + /** + * + * @return A map containing the necessary version information we want to drop into archives + */ + Map generateVersionMap() { + return [ + 'version' : this.project.version, + 'name' : this.project.name, + 'buildDate' : new Date(), + 'revision': scmHandler?.revision, + 'builtOn': this.hostname, + ] + } + + /** + * Return a hostname or unknown if we can't resolve our localhost (as seen on Mavericks) + * + * @return Local host's name or 'unknown' + */ + String getHostname() { + try { + return InetAddress.localHost.hostName + } + catch (UnknownHostException) { + return 'unknown' + } + } + + /** + * @return A (name)-(version) String for the directory name inside a compressed archive + */ + String getArchiveDirName() { + return String.format("%s-%s", this.project.name, this.project.version) + } + /** * Lazily look up our SCM Handler */ @@ -64,5 +135,31 @@ class ServiceArtifactExtension { return baseVersion } + + /** + * Return the computed Map<> of metadata that is to be written to the etc/metadata.conf + * inside of the service artifact + */ + Map getMetadata() { + return this.metadata + } + + void metadata(Object... arguments) { + arguments.each { + this.metadata.putAll(it as Map) + } + } + + void setMetadata(Object... arguments) { + this.metadata = [:] + this.metadata arguments + } + + /** + * return the configured project for this service{} extension + */ + Project getProject() { + return this.project + } } diff --git a/src/main/groovy/com/github/lookout/serviceartifact/ServiceArtifactPlugin.groovy b/src/main/groovy/com/github/lookout/serviceartifact/ServiceArtifactPlugin.groovy index 16d30c9..1458765 100644 --- a/src/main/groovy/com/github/lookout/serviceartifact/ServiceArtifactPlugin.groovy +++ b/src/main/groovy/com/github/lookout/serviceartifact/ServiceArtifactPlugin.groovy @@ -10,6 +10,8 @@ import org.gradle.api.tasks.bundling.Tar class ServiceArtifactPlugin implements Plugin { static final String GROUP_NAME = 'Service Artifact' static final String ARCHIVES_CONFIG = "serviceArchives" + static final String ZIP_TASK = 'serviceZip' + static final String TAR_TASK = 'serviceTar' void apply(Project project) { /* Add the asciidoctor plugin because...docs are important */ @@ -37,13 +39,13 @@ class ServiceArtifactPlugin implements Plugin { description "stub task for preparing the bin scripts for the artifact" } - Task tarTask = project.task('serviceTar', type: Tar) { + Task tarTask = project.task(TAR_TASK, type: Tar) { group GROUP_NAME description "Create a .tar.gz artifact containing the service" dependsOn prepareTask } - Task zipTask = project.task('serviceZip', type: Zip) { + Task zipTask = project.task(ZIP_TASK, type: Zip) { group GROUP_NAME description "Create a .zip artifact containing the service" dependsOn prepareTask @@ -63,5 +65,8 @@ class ServiceArtifactPlugin implements Plugin { project.artifacts.add(ARCHIVES_CONFIG, zipTask) project.artifacts.add(ARCHIVES_CONFIG, tarTask) + + /* With everything defined, let's bootstrap the service extension internals */ + service.bootstrap() } } diff --git a/src/main/groovy/com/github/lookout/serviceartifact/lang/JRuby.groovy b/src/main/groovy/com/github/lookout/serviceartifact/lang/JRuby.groovy index e0e1514..a06db0f 100644 --- a/src/main/groovy/com/github/lookout/serviceartifact/lang/JRuby.groovy +++ b/src/main/groovy/com/github/lookout/serviceartifact/lang/JRuby.groovy @@ -68,7 +68,7 @@ class JRuby extends AbstractServiceExtension { disableJarTask() setupJRubyJar() - setupCompressedArchives(this.project, serviceExtension.scmHandler) + setupCompressedArchives(serviceExtension) extraConfig.delegate = new JRubyDSL(this.project) extraConfig.call(this.project) diff --git a/src/main/groovy/com/github/lookout/serviceartifact/lang/Scala.groovy b/src/main/groovy/com/github/lookout/serviceartifact/lang/Scala.groovy index eb07d4d..3e38e8c 100644 --- a/src/main/groovy/com/github/lookout/serviceartifact/lang/Scala.groovy +++ b/src/main/groovy/com/github/lookout/serviceartifact/lang/Scala.groovy @@ -41,8 +41,10 @@ class Scala extends AbstractServiceExtension { } this.project.tasks.findByName('assemble').dependsOn(jar) this.project.artifacts.add(ServiceArtifactPlugin.ARCHIVES_CONFIG, jar) + jar.configurations.add(this.project.configurations.getByName('compile')) - setupCompressedArchives(this.project, serviceExtension.scmHandler) + + setupCompressedArchives(serviceExtension) disableJarTask() } diff --git a/src/test/groovy/com/github/lookout/serviceartifact/ServiceArtifactExtensionSpec.groovy b/src/test/groovy/com/github/lookout/serviceartifact/ServiceArtifactExtensionSpec.groovy index b932fc4..1437aec 100644 --- a/src/test/groovy/com/github/lookout/serviceartifact/ServiceArtifactExtensionSpec.groovy +++ b/src/test/groovy/com/github/lookout/serviceartifact/ServiceArtifactExtensionSpec.groovy @@ -6,6 +6,17 @@ import org.gradle.api.Project import org.gradle.api.Task import org.gradle.testfixtures.ProjectBuilder +import com.github.lookout.serviceartifact.scm.GerritHandler + +abstract class AppliedExtensionSpec extends Specification { + protected Project project + + def setup() { + this.project = ProjectBuilder.builder().build() + this.project.apply plugin: 'com.github.lookout.service-artifact' + } +} + class ServiceArtifactExtensionSpec extends Specification { protected Project project @@ -30,23 +41,15 @@ class ServiceArtifactExtensionSpec extends Specification { expect: extension instanceof ServiceArtifactExtension } -} - - -class ServiceArtifactExtensionInstanceSpec extends ServiceArtifactExtensionSpec { def "getScmHandler() instantiate a handler instance"() { given: "we're inside of a Gerrit-triggered Jenkins job" def ext = new ServiceArtifactExtension(this.project, gerritEnv()) expect: - ext.scmHandler instanceof scm.GerritHandler + ext.scmHandler instanceof GerritHandler } -} - -class ServiceArtifactExtensionVersionSpec extends ServiceArtifactExtensionSpec { - def "version() should return an unmolested string by default"() { given: def ext = Spy(ServiceArtifactExtension, constructorArgs: [this.project]) @@ -69,14 +72,95 @@ class ServiceArtifactExtensionVersionSpec extends ServiceArtifactExtensionSpec { then: version == '1.0.1.1+0xded' } + + def "bootstrap() should define a serviceVersionInfo task"() { + given: + def extension = new ServiceArtifactExtension(this.project) + + when: + extension.bootstrap() + + then: + this.project.tasks.findByName('serviceVersionInfo') + } + + @Ignore + def "bootstrap() should define serviceMetadata"() { + given: + def extension = new ServiceArtifactExtension(this.project) + + when: + extension.bootstrap() + + then: + this.project.tasks.findByName('serviceMetadata') + } + + + def "generateVersionMap()"() { + given: + def extension = new ServiceArtifactExtension(this.project) + Map versionMap = extension.generateVersionMap() + + expect: + versionMap instanceof Map + versionMap['version'] + versionMap['name'] + versionMap['buildDate'] + versionMap['revision'] + versionMap['builtOn'] + } } -class ServiceArtifactExtensionShadowSpec extends Specification { - protected Project project +class ExtensionIntegrationSpec extends AppliedExtensionSpec { + def "bootstrap() should have set up dependencies for serviceVersionInfo"() { + given: + Closure matcher = { (it instanceof Task) && (it.name == 'serviceVersionInfo') } + Task zip = this.project.tasks.findByName('serviceZip') + Task tar = this.project.tasks.findByName('serviceTar') + + expect: "the compressed archives to rely on serviceVersionInfo" + zip.dependsOn.find(matcher) + tar.dependsOn.find(matcher) + } +} + + +/** + * Test the functionality of the service { metadata [:] } property + */ +class ServiceArtifactExtensionMetadataSpec extends AppliedExtensionSpec { + def "its metadata should be a map by default"() { + expect: + this.project.service.metadata instanceof Map + } + + def "I should be able to set metadata"() { + given: + this.project.service.metadata 'success' : true + + expect: + this.project.service.metadata == ['success' : true] + } + + def "I should be able to completely overwrite it with setMetadata"() { + given: + this.project.service.metadata 'overwrite' : 1 + + when: + this.project.service.setMetadata 'success' : true + + then: + this.project.service.metadata == ['success' : true] + } +} + +/** + * Test the functionality of the service { jruby{} } closure + */ +class ServiceArtifactExtensionJRubyIntegrationSpec extends AppliedExtensionSpec { def setup() { - this.project = ProjectBuilder.builder().build() - this.project.apply plugin: 'com.github.lookout.service-artifact' this.project.service { jruby {} } } @@ -105,19 +189,6 @@ class ServiceArtifactExtensionShadowSpec extends Specification { task.manifest.attributes['Main-Class'] } -} - -/** - * Test the behaviors of the setupCompressedArchives() functionality - */ -class ServiceArtifactExtensionArchivesSpec extends Specification { - protected Project project - - def setup() { - this.project = ProjectBuilder.builder().build() - this.project.apply plugin: 'com.github.lookout.service-artifact' - this.project.service { jruby {} } - } def "the serviceTar task should depend on serviceJar"() { given: @@ -135,4 +206,3 @@ class ServiceArtifactExtensionArchivesSpec extends Specification { tar.dependsOn.find { (it instanceof Task) && (it.name == 'serviceJar') } } } -