Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Quick and dirty Gradle task for deploying Cloud Foundry
buildscript {
repositories {
mavenCentral()
maven { url 'http://maven.springframework.org/milestone' }
}
dependencies {
classpath 'org.cloudfoundry:cloudfoundry-client-lib:0.7.1'
}
}
import org.cloudfoundry.client.lib.CloudApplication
import org.cloudfoundry.client.lib.CloudFoundryClient
task cloudFoundryDeploy {
dependsOn assemble
inputs.dir "$project.buildDir/libs"
doLast {
String target = System.properties['vcap.target']
String email = System.properties['vcap.email']
String passwd = System.properties['vcap.passwd']
if (!(target && email && passwd)) {
throw new GradleException("'vcap.target', 'vcap.email' and 'vcap.passwd' system properties are required for the cloudFoundryDeploy task")
}
String appName = project.name
String url = target.replaceFirst(/^api\./, "${appName}.")
CloudFoundryClient client = new CloudFoundryClient(email, passwd, "http://$target")
client.login()
if (client.getApplications().find { it.name == appName }) {
client.stopApplication(appName)
}
else {
client.createApplication(appName, CloudApplication.SPRING, client.getDefaultApplicationMemory(CloudApplication.SPRING), [url], null);
}
client.uploadApplication(appName, file("$project.buildDir/libs/${project.name}.war"))
client.startApplication(appName)
}
}

cbeams commented Feb 8, 2012

Consider changing System.properties[...] access to project.properties[...] or project.property(...). The latter two approaches allow users the flexibility of specifying the settings on the Gradle command line via -Pvcap.* flags or dropping them into gradle.properties in the project root or $GRADLE_HOME directories. The map-style access returns null if the given key is not present, whereas the method-style access will throw.

cbeams commented Feb 8, 2012

Use http://repo.springsource.org instead of http://maven.springframework.org . See https://github.com/SpringSource/spring-framework/wiki/SpringSource-repository-FAQ for details.

And why is this an artifact at version 0.7.1 coming out of the /milestone repository? Is it indeed a milestone, and not a GA release?

cbeams commented Feb 8, 2012

Based on the following line

client.uploadApplication(appName, file("$project.buildDir/libs/${project.name}.war"))

it appears that the project must have a war task. Therefore, instead of the following:

inputs.dir "$project.buildDir/libs"
// ...
client.uploadApplication(appName, file("$project.buildDir/libs/${project.name}.war"))

you can do the following:

inputs war.archivePath
// ...
client.uploadApplication(appName, file(war.archivePath))

where war.archivePath is the path to the actual .war file. If you do still for some reason wish to use inputs.dir over inputs, you can refer to war.destinationDir instead.

cbeams commented Feb 8, 2012

Consider doing all of the following outside of (above) the doLast { ... } block:

String target = System.properties['vcap.target']
String email = System.properties['vcap.email']
String passwd = System.properties['vcap.passwd']
if (!(target && email && passwd)) {
  throw new GradleException("'vcap.target', 'vcap.email' and 'vcap.passwd' system properties are required for the cloudFoundryDeploy task")
}
String appName = project.name
String url = target.replaceFirst(/^api\./, "${appName}.")

This allows these statements to be evaluated during the building of the task graph, allowing for fail-fast behavior if something is wrong, e.g. a property has not been specified. Otherwise the user will have to wait for all dependent tasks to execute before getting to the cloudFoundryDeploy task's doLast block, only to find it failing.

cbeams commented Feb 8, 2012

It's somewhat more idiomatic Gradle to write

task cloudFoundryDeploy(dependsOn: assemble) { ... }

than it is to write

task cloudFoundryDeploy {
    dependsOn assemble
}

They are functionally equivalent, the former just emphasizes task dependencies more prominently, and is more concise to boot.

cbeams commented Feb 8, 2012

Minor point here, but it's more Groovy-ish to drop the explicit typing, e.g.:

String appName = project.name

becomes

def appName = project.name

Note that the way this works in Gradle is that if the variable is a def, it is task-local, whereas if it is declared simply as

appName = project.name

then it becomes a publicly-visible property of the task. This can be quite useful in cases where you want to refer to this information from another task, e.g.:

task myOtherTask {
    println cloudFoundryDeploy.appName
}

cbeams commented Feb 8, 2012

Note that there is now a clear path for creating and publishing Gradle plugins to the SpringSource repository (http://repo.springsource.org). Take a look at https://github.com/springsource/gradle-plugins for examples/inspiration if you'd eventually like to turn this gist into a proper plugin.

As of this writing the gradle-plugins project is lacking documentation, but I'm happy to help in the meantime.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment