Please note the “Update” section below.
I haven’t had much success with finding useful deployment strategies and/or scripts for Grails anywhere. The extent of the documentation I’ve been able locate for deployment simply tells you to create a WAR and upload it to the servlet container.
Not terribly helpful if you want to run a formal process.
So, for my Grails webapps, I came up with this. I create a file in the “gradle” directory named “deploy.gradle” containing the following:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'org.hidetake:gradle-ssh-plugin:2.9.0'
}
}
apply plugin: 'org.hidetake.ssh'
// the below is in it's own ext block because it can potentially be
// overridden by .deployrc.gradle in the user's home directory, and the scmUser
// might be required in the following ext block (where the repository is
// set up)
ext {
scmUser = project.hasProperty('user') ? project.getProperty('user') : System.properties['user.name']
}
// please note that the file must end in ".gradle" or Gradle won't know how
// to process it, and you'll get a "String index out of bounds: 0" error
def rcFile = new File("${System.properties['user.home']}/.deployrc.gradle")
if (rcFile.exists()) {
apply from: rcFile.absolutePath
}
ext {
timeNow = new Date().format('yyyyMMddHHmmss')
// TODO: the below should use a repository directly associated with the individual user
scmRepository = "${project.scmUser}@my.site:/var/repos/${project.name}.git"
runEnvironment = project.hasProperty('env') ? project.getProperty('env') : 'production'
scmBranch = project.hasProperty('branch') ? project.getProperty('branch') : 'master'
tmpDir = System.getProperty('java.io.tmpdir') + "${project.name}/${timeNow}"
}
apply from: "${project.projectDir}/gradle/deploy/${project.runEnvironment}.gradle"
task deploy {
group 'Remote Deployment'
description 'Deploys a branch from your SCM to a target environment on a remote server'
doLast {
println "Deploying from branch ${project.scmBranch} to environment ${project.runEnvironment}"
ssh.run {
session(remotes.role('webserver')) {
// upload the WAR to the target server(s)
println 'Uploading WAR file'
put from: "${project.buildDir}/libs/mercury.war", into: "mercury/mercury-${timeNow}.war"
// stop Tomcat so we can get this party started
println 'Stopping Tomcat'
execute 'sudo service tomcat8 stop'
// if a pre-existing link exists to "current", remove it
println 'Symlinking'
execute 'if [ -f mercury/mercury-current.war ]; then rm mercury/mercury-current.war; fi'
// link our new WAR to the name "current"
execute "cd mercury; ln -s mercury-${timeNow}.war mercury-current.war"
// Tomcat doesn't clean up extracted WAR file directories, so we must flush out the /var/lib/tomcat8/webapps directory
execute 'if [ -d /var/lib/tomcat8/mercury ]; then sudo rm -rf /var/lib/tomcat8/mercury; fi'
// Fire tomcat back up
println 'Starting Tomcat'
execute 'sudo service tomcat8 start'
}
}
}
}
task deployBuild << {
println "Building WAR for ${project.runEnvironment} environment"
exec {
workingDir project.tmpDir
environment 'TERM', 'dumb' // this prevents grails+gradle for displaying a second progress bar during the following invocation
commandLine 'grails', "-Dgrails.env=${project.runEnvironment}", 'war', 'mercury.war'
}
}
task deployCheckout << {
println "Cloning ${project.scmRepository}#${project.scmBranch} for user ${project.scmUser} into ${project.tmpDir}"
exec {
commandLine 'git', 'clone', '-b', project.scmBranch, project.scmRepository, project.tmpDir
}
}
task deployCleanup << {
println "Removing temporary directory ${project.tmpDir}"
exec {
commandLine 'rm', '-rf', project.tmpDir
}
}
deployBuild.dependsOn deployCheckout
deploy.dependsOn deployBuild
deploy.finalizedBy deployCleanup
Also in the “gradle” directory is a subdirectory named “deploy” where I have the files specific to the environments to which I can deploy, such as “staging.gradle”:
remotes {
web {
role 'webserver'
host = 'my.site'
user = 'webapps'
identity = file("${System.properties['user.home']}/.ssh/webapps")
}
}
Now, you will need to make some modifications to your main “build.gradle” script. First, add in the SSH plugin dependency, immediately after the “buildscript” section:
plugins {
id 'org.hidetake.ssh' version '2.10.1'
}
And then in the area where you have all the “apply” instructions:
apply from: "${project.projectDir}/gradle/deploy.gradle"
Using the script above, I can deploy a particular branch from within my git repository to a specific environment thus:
gradle -Penv=staging -Pbranch=hotfix/1.2.3.001 deploy
It’s probably not perfect, but since I’m new to Gradle and Grails, I think it’s a pretty good start!
Update!
Alas the “org.hidetake.ssh” plugin has not kept up to date with the latest updates to SSH, and as such the above scripts no longer work, and I have abandoned any attempts to use Gradle to effect deployments. Instead, I have switched over to using Ansible to deal with all of this.