DevOps Zone is brought to you in partnership with:

Java programmer since 1996. J2SE, J2EE Expert, Technical Leader, Team Manager. Rich experience in infrastructure and architecture of distributed applications Pavel has posted 6 posts at DZone. You can read more from them at their website. View Full User Profile

Replace your Scripts with Gradle Tasks

09.24.2013
| 12828 views |
  • submit to reddit

I really like Maven, and I really like the declarative build style, but recently I finally came to understand why Gradle is better.

For small projects that produce a common library JAR, you can still use Maven, but real-life, complex software projects always contain a lot of support scripts for deployment, copying artifacts, and so on. For some of those tasks you can find Maven plug-ins, for most of them you can write Maven plugins, but in real life you have shell scripts to do the job.

The problem with such scripts is that they contain paths and file names, which are part of the build. And if you change your pom.xml, you should change those scripts as well.

Gradle solves the problem. Your scripts are now part of the build; they can use build paths and artifact names, and they can depend on build stages.

I will show a small example: deploying WAR using SSH to a remote server. The example uses the Gradle SSH plugin.

Notice that we use the WAR name from the build and that the deployment task depends on the test, so we can't deploy if the tests fail

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.hidetake:gradle-ssh-plugin:0.1.7'
    }
}

apply plugin: 'ssh'
apply plugin: 'war'


war {
    archiveName = 'rest.war'
}

dependencies {
  //...


}


ssh {
    config(StrictHostKeyChecking: 'no')
}
remotes {
    testserver {
        host = 'myserver.com'
        user = sshuser
        identity = file("${System.properties['user.home']}/.ssh/id_dsa")
    }

  
}

task deployTestServer << {
    deploy(remotes.testserver)
}



deployTestServer.dependsOn build


def deploy(def server) {
    logger.lifecycle("Deploying to $server")
    logger.lifecycle("Copying ${war.archivePath.absolutePath} to $server ... Be patient .. takes time ...")
    sshexec {
        session(server) {
            put(war.archivePath.absolutePath, war.archiveName)
        }
    }
    sshexecute(server, '/usr/share/tomcat7/bin/tomcat7 stop')
    sshexecute(server, 'rm -rf /var/lib/tomcat7/webapps/rest*')
    sshexecute(server, "cp ${war.archiveName}  /var/lib/tomcat7/webapps")
    sshexecute(server, '/usr/share/tomcat7/bin/tomcat7 start')
    sshexecute(server, '/usr/share/tomcat7/bin/tomcat7 status')
}



def sshexecute(def server, def cmd) {
    logger.lifecycle("Executing '$cmd'  ...")
    sshexec {
        session(server) {
            execute(cmd, pty: true)
        }
    }
 

}  


Published at DZone with permission of its author, Pavel Bernshtam. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Roman Zenka replied on Thu, 2013/09/26 - 5:56pm

I understand you are trying to give a simple example, but it shows a very common pitfall that people writing build scripts suffer from.

You folks think you are special.

You think your project is one of a kind.

Nobody else in the world would POSSIBLY ever think of deploying a .war to Tomcat via ssh! Right? You're the only person in the world who has that need!

So you write your own custom script, completely ignoring a plugin that does that already. A plugin, which allows you to turn code into configuration. And configuration can be processed as data. Code cannot.

So you missed out on an opportunity to make your build more standard, instead you made it more special. And you know... this is exactly what we need. Every single tiny project with their own special way of running builds, so you can learn them one at a time, instead of using tools to automate this process.

You are not special.

Standardize.

Collaborate.

Pavel Bernshtam replied on Thu, 2013/09/26 - 11:34pm

Roman, yes, you are right, may be the example is not the best. But still I think that in any large real life project there are a lot of custom things to do.

Marcus Philip replied on Wed, 2013/10/02 - 11:31am in response to: Roman Zenka

When you start your little project maybe you are not special. But two years down the line, and some hundred thousands of lines of code later, if you survive that far, I promise you, you are special. 

http://nealford.com/memeagora/2013/01/22/why_everyone_eventually_hates_maven.html

Standardize, yes! But standardize on the right things. But more important, understand the trade offs when going with an intrusive opinionated framework like maven. 

Marcus Philip replied on Wed, 2013/10/02 - 11:40am

If you have a real-life, complex software project, you'll want to deploy to an array of servers. I wonder how gradle scales to handle that. I like the idea that build, test and deploy scripts are all one, but I wonder if not at some point gradle should call out to another tool.  I hope writing a gradle plugin has a lower cognitive load than a dito maven. If not, forget about it. 

Pavel Bernshtam replied on Wed, 2013/10/02 - 1:02pm in response to: Marcus Philip

Marcus, in gradle you can just write a groovy code (which means all abilities of Java code) that does some work instead of writing plugin.

Alexandre Drummond replied on Thu, 2013/10/03 - 6:26am

Do you all know you don't need to tie your Maven/Gradle project to specific deployment procedure?

Usually I use Jenkins plugins to deploy directly to JBoss clusters, tomcat, even HP Operations and Orchestration... I don't see any advantage doing this kind of thing using Maven or Gradle instead of Jenkins jobs.

Maybe I'm missing something.

Marcus Philip replied on Thu, 2013/10/03 - 2:41pm in response to: Alexandre Drummond

I'm afraid you are missing something.

The Jenkins deploy plugins works fine most of the time. Until it mysteriously hangs intermittently. Or it doesn't allow zero down time deployments the way you want to do it. Or some other requirement or problem arises. And debugging or extending that plugin has quite a threshold. 

Raging Infernoz replied on Sat, 2013/10/05 - 6:10am

 I saw this (http://nealford.com/memeagora/2013/01/22/why_everyone_eventually_hates_maven.html) opinion; sorry disagree, been there with Ant and lots of *nix tools (yuck!), and assembler, that's why I use Maven, component based tools, and high level languages now.

Just because a tool exists some naive people think they need to do everything with just that tool, wrong; you have to option to build layers which drive tools, which are better and simpler at some task areas, rather like the *nix tools idea, but less icky; this is where build servers like Jenkins came from.

If you can customise Gradle that much, the project will become an ugly mess, and people will leave it behind just like ugly *nix editors like emacs was dumped when better structured and simpler to use text editors and IDEs were created.  I also use NetBeans, because Eclipse is such an ugly, bolt-on, complex mess, that it is annoying noise which gets in the way of actually doing stuff; IntelliJ is nearly as bad!

Andrey Melekhovskiy replied on Sun, 2013/10/06 - 12:00pm

 I'm sure that Maven or Gradle should be used for building artifacts only. For deployment better to use Jenkins (or any other CI), or SCM (Chef, Puppet etc.). If you have ever thought about Continuous Delivery you understand why.

Jay Vee replied on Wed, 2013/10/30 - 12:56pm

What the hell is up with DZone's comment text editing area?  seriously could you have made it any harder?  What I see when entering text is not what is displayed on the screen.


If I save a code snippit and then edit and save again (without making any changes), things are garbled.  We have the technology to do this right you know.  cheez.

Jay Vee replied on Wed, 2013/10/30 - 1:00pm

When I run the above code, I get this error

build file '/home/projects/ssh.gradle':
Invalid use of declaration inside method call.
@ line 62, column 13.
task deploy(def server) {


any ideas?  This example does not work right out of the box.  Can it be modified to work?

Jay Vee replied on Wed, 2013/10/30 - 12:59pm in response to: Andrey Melekhovskiy

For deployment the best thing to do is to create an ant, maven or Gradle task to do it and then call that from Jenkins.

I really hate it when I cannot do something from the command line because our brilliant build guy decided to do everything in Jenkins.  Doing it in Jenkins does not give the developer access to this when checking out code and running from the command line.

Jenkins gives you enough rope to hang yourself with and the above advice to 'deploy from jenkins' is bad advice in my opinion. seriously.  Yes deploy from Jenkins but call ant, maven or gradle to do the work.  Not everyone likes to open a browser window and push buttons.  Deployment should be scriptable and using Jenkins does not allow for this.

Jay Vee replied on Wed, 2013/10/30 - 1:02pm

What does this section do?

sshexec {
  session(server) {
    put(war.archivePath.absolutePath, war.archiveName)
  }
}

It appears to do a ssh put, but would you not want to stop the server and cleanup the old war stuff first before doing a put?  

thanks

Raging Infernoz replied on Sat, 2013/11/02 - 11:25am

This is because HTML is being abused to try and do formatted text tasks, but does not respect well establish text formatting rules which word processors and DTP got right years ago, because the existing controls were always pathetic, so all kinds of flaky div and JavaScript hacks exist to try and do this right, but should really be done by proper controls!

An example I see too often on DZone, is inserted code with all the indenting stripped off; this makes the code effectively unreadable, and makes the author look an ass.

Pavel Bernshtam replied on Sat, 2013/11/02 - 11:38am

Jay Vee:

1. the only thing you should add - define in gradle.properties a variable sshuser

2. put will put the file into your homedir on remote machine, then we stop server, copy the war to the correct place and start server


Jay Vee replied on Mon, 2013/11/04 - 5:50pm in response to: Pavel Bernshtam

One issue we have is that the tomcat directories are owned and run by another user for which we do not have direct ssh access.

We can login to the machine using our ldap login and then we must sudo su to the other user and start tomcat.

Is it possible to ssh and then sudo su to the other user and do the things needed?

thanks


J.V.

Stan Svec replied on Mon, 2013/11/11 - 10:35am

In my latest projects I use declarative configured dev. environment created in VM with Vagrant and Puppet tools. This plugin is very handy in such setup. Thanks a lot for your post!

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.