Slide 1

Slide 1 text

Building  a  Con,nuous  Delivery  pipeline    with  Gradle  and  Jenkins   Benjamin  Muschko  

Slide 2

Slide 2 text

bmuschko   bmuschko   [email protected]   Gradle  in  Ac,on   Java/Groovy  developer   Gradle  contributor   Open  Source  enthusiast   Benjamin  Muschko   Presenta,on  will  be  available  on   hFps://speakerdeck.com/bmuschko  

Slide 3

Slide 3 text

Releases  don’t  have  to  be  painful  

Slide 4

Slide 4 text

Con,nuous  Delivery   Deliver  soKware  fast  and  frequently  

Slide 5

Slide 5 text

#1  Every  commit  can  result  in  a  release   #2  Automate  everything!         #3  Automated  tests  are  essen,al   #4  Done  means  released  

Slide 6

Slide 6 text

Build  pipeline   Automated  manifesta,on  of  delivery  process  

Slide 7

Slide 7 text

Build  quality  in!   Establish  automated  quality  gates  

Slide 8

Slide 8 text

Test   Compile/Unit  Tests   Integra,on  Tests   Code  Analysis   Package/Deploy   UAT   Prod   Acceptance  Tests  

Slide 9

Slide 9 text

!    But     how?  

Slide 10

Slide 10 text

The  “revolu,onary”  sample  applica,on   Internet To Do application Browser Data store Writes Reads

Slide 11

Slide 11 text

Mul,-­‐project  dependencies   Model Web Repository depend depend depend

Slide 12

Slide 12 text

Project  hierarchy   todo model repository build.gradle settings.gradle web build.gradle build.gradle build.gradle gradle wrapper xyz.gradle gradlew gradlew.bat

Slide 13

Slide 13 text

Project  hierarchy   todo model repository build.gradle settings.gradle web build.gradle build.gradle build.gradle gradle wrapper xyz.gradle gradlew gradlew.bat

Slide 14

Slide 14 text

Project  hierarchy   todo model repository build.gradle settings.gradle web build.gradle build.gradle build.gradle gradle wrapper xyz.gradle gradlew gradlew.bat Define  project-­‐specific                            behavior  

Slide 15

Slide 15 text

Project  hierarchy   include 'model' include 'repository' include 'web' settings.gradle Defines  which  projects  are        taking  part  in  the  build   todo model repository build.gradle settings.gradle web build.gradle build.gradle build.gradle gradle wrapper xyz.gradle gradlew gradlew.bat

Slide 16

Slide 16 text

Project  hierarchy   todo model repository build.gradle settings.gradle web build.gradle build.gradle build.gradle gradle wrapper xyz.gradle gradlew gradlew.bat Always  use  Wrapper   to  execute  the  build!  

Slide 17

Slide 17 text

Project  hierarchy   todo model repository build.gradle settings.gradle web build.gradle build.gradle build.gradle gradle wrapper xyz.gradle gradlew gradlew.bat    Externalize  concerns  into  script  plugins   and  organize  them  in  dedicated  directory   Examples:     ü  Versioning  strategy   ü  Integra,on  and  func,onal  test  setup   ü  Deployment  func,onality   ü  ...        

Slide 18

Slide 18 text

Project  ar,facts   root project model project repository project web project JAR   JAR   WAR   Deployable  ar,fact  

Slide 19

Slide 19 text

Stages  in  build  pipeline   Acceptance stage Functional tests Publish Binaries Commit stage Deploy Binaries UAT Deploy Binaries Production Integration Tests Code Analysis Assemble Distribution Compile Unit Tests Retrieve Binaries Deploy Binaries Asserts  that  system  works                at  a  technical  level  

Slide 20

Slide 20 text

Stages  in  build  pipeline   Acceptance stage Functional tests Publish Binaries Commit stage Deploy Binaries UAT Deploy Binaries Production Integration Tests Code Analysis Assemble Distribution Compile Unit Tests Retrieve Binaries Deploy Binaries Asserts  that  system  works  on  a   func,onal/non-­‐func,onal  level  

Slide 21

Slide 21 text

Stages  in  build  pipeline   Acceptance stage Functional tests Publish Binaries Commit stage Deploy Binaries UAT Deploy Binaries Production Integration Tests Code Analysis Assemble Distribution Compile Unit Tests Retrieve Binaries Deploy Binaries Trigger  manually   Trigger  manually  

Slide 22

Slide 22 text

Commit  stage:  Compile/unit  tests   Rapid  feedback  (<  5  mins)   Run  on  every  VCS  check-­‐in   Priority:  fix  broken  build   Publish Binaries Commit stage Compile Unit Tests Integration Tests Code Analysis Assemble Distribution

Slide 23

Slide 23 text

Commit  stage:  Integra,on  tests   Long  running  tests   Require  environment  setup   Hard  to  maintain   Publish Binaries Commit stage Compile Unit Tests Integration Tests Code Analysis Assemble Distribution

Slide 24

Slide 24 text

Separate  tests  in  project  layout   todo model repository web src integTest java main java test java Integra,on  test  Java  sources   Produc,on  Java  sources   Unit  test  Java  sources  

Slide 25

Slide 25 text

Separate  tests  with  SourceSets   
   sourceSets {
 integrationTest {
 java.srcDir file('src/integTest/java')
 resources.srcDir file('src/integTest/resources')
 compileClasspath = sourceSets.main.output + configurations.testRuntime
 runtimeClasspath = output + compileClasspath
 }
 } task integrationTest(type: Test) {
 description = 'Runs the integration tests.'
 group = 'verification'
 testClassesDir = sourceSets.integrationTest.output.classesDir
 classpath = sourceSets.integrationTest.runtimeClasspath
 testResultsDir = file("$testResultsDir/integration")
 }
          Custom  test     results  directory   Set  source  and  resources  directory    Set  compile  and     run,me  classpath   gradlew integrationTest

Slide 26

Slide 26 text

Database  integra,on  tests   stop Database test start Database build Schema integration Test check build ...

Slide 27

Slide 27 text

Database  integra,on  tests   
   apply from: "$rootDir/gradle/databaseSetup.gradle" integrationTest.dependsOn startAndPrepareDatabase
 stopDatabase.dependsOn integrationTest
 task databaseIntegrationTest(dependsOn: stopDatabase) check.dependsOn databaseIntegrationTests   stop Database test start Database build Schema integration Test check build ... Separate  complex  setup  logic  into  script  plugin   Integrate  tasks  into  build  lifecycle  

Slide 28

Slide 28 text

Picking  the  “right”  code  coverage  tool   Cobertura   Offline  bytecode      instrumenta,on          Source  code      instrumenta,on   Offline  bytecode      instrumenta,on   On-­‐the-­‐fly  bytecode            instrumenta,on  

Slide 29

Slide 29 text

On-­‐the-­‐fly  bytecode  instrumenta,on   No  modifica,on  to  source  or  bytecode  

Slide 30

Slide 30 text

Code  coverage  with  JaCoCo   
   buildscript {
 repositories {
 mavenCentral()
 }
 
 dependencies {
 classpath 'org.ajoberstar:gradle-jacoco:0.3.0'
 }
 }
 
 apply plugin: org.ajoberstar.gradle.jacoco.plugins.JacocoPlugin
 
 jacoco {
 integrationTestTaskName = 'integrationTest'
 }   Configures  instrumenta,on     for  integra,on  tests  as  well        Add  plugin  to  the   buildscript’s  classpath   Gradle  1.6  includes  this  plugin  as  incuba,ng  feature  

Slide 31

Slide 31 text

Genera,ng  coverage  reports   test integration Test ... ... .exec   jacoco TestReport jacocoIntegration TestReport .exec   .html   .html  

Slide 32

Slide 32 text

Commit  stage:  Code  analysis   Publish Binaries Commit stage Compile Unit Tests Integration Tests Code Analysis Assemble Distribution Perform  code  health  check   Fail  build  for  low  quality   Record  progress  over  ,me  

Slide 33

Slide 33 text

Sta,c  code  analysis  tools   Checkstyle   FindBugs   apply plugin: 'pmd'
 
 pmd {
 ignoreFailures = true
 }
 
 tasks.withType(Pmd) {
 reports {
 xml.enabled = false
 html.enabled = true
 }
 }   apply plugin: 'jdepend’
 
 jdepend {
 toolVersion = '2.9.1'
 ignoreFailures = true
 }   gradlew check

Slide 34

Slide 34 text

Measure  quality  over  ,me  with  Sonar   Sonar database Gradle Sonar Runner root model repo. web analyzes publishes reads / writes Gradle project

Slide 35

Slide 35 text

Applying  the  Sonar  Runner  plugin   apply plugin: 'sonar-runner'
 
 sonarRunner {
 sonarProperties {
 property 'sonar.projectName', 'todo'
 property 'sonar.projectDescription', 'A task management application'
 }
 }
 
 subprojects {
 sonarRunner {
 sonarProperties {
 property 'sonar.sourceEncoding', 'UTF-8'
 }
 }
 }   gradlew sonarRunner

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

Commit  stage:  Assemble  distribu,on   Exclude  env.  configura,on   Include  build  informa,on   Choose  versioning  strategy   Publish Binaries Commit stage Compile Unit Tests Integration Tests Code Analysis Assemble Distribution

Slide 38

Slide 38 text

Versioning  strategy   1.0-­‐SNAPSHOT   1.0   during  development   when  released   …the  Maven  way   Change  version  with  Maven  Release  plugin  

Slide 39

Slide 39 text

Versioning  strategy   1.0.134   1.0.134   during  development   when  released   …the  Con,nuous  Delivery  way   1.0.134   Jenkins  build  number   Project  version  number  

Slide 40

Slide 40 text

Versioning  strategy   …implemented  with  Gradle   allprojects {
 apply from: "$rootDir/gradle/versioning.gradle"
 }   todo model repository build.gradle settings.gradle web gradle versioning.gradle Contains  version  implementa,on  

Slide 41

Slide 41 text

Versioning  strategy   …implemented  with  Gradle   ext.buildTimestamp = new Date().format('yyyy-MM-dd HH:mm:ss')
 
 version = new ProjectVersion(1, 0, System.env.SOURCE_BUILD_NUMBER)
 
 class ProjectVersion {
 Integer major
 Integer minor
 String build
 
 ProjectVersion(Integer major, Integer minor, String build) {
 this.major = major
 this.minor = minor
 this.build = build
 }
 
 @Override
 String toString() { String fullVersion = "$major.$minor"
 if(build) {
 fullVersion += ".$build" }
 fullVersion
 } }   Jenkins  Build  Number                Builds  version     String  representa,on  

Slide 42

Slide 42 text

Packaging  the  deployable  ar,fact   project(':web') { apply plugin: 'war' 
 task createBuildInfoFile << {
 def buildInfoFile = new File("$buildDir/build-info.properties")
 Properties props = new Properties()
 props.setProperty('version', project.version.toString())
 props.setProperty('timestamp', project.buildTimestamp)
 props.store(buildInfoFile.newWriter(), null)
 }
 
 war {
 dependsOn createBuildInfoFile
 baseName = 'todo'
 
 from(buildDir) {
 include 'build-info.properties'
 into('WEB-INF/classes')
 }
 } }   Creates  file  containing   build  informa,on   Include  build  info  file   Into  WAR  distribu,on   gradlew assemble

Slide 43

Slide 43 text

Commit  stage:  Publish  binaries   Version  ar,fact(s)   Use  binary  repository   Publish  once,  then  reuse   Publish Binaries Commit stage Compile Unit Tests Integration Tests Code Analysis Assemble Distribution

Slide 44

Slide 44 text

Publishing  the  deployable  ar,fact   1.0.34   1.0.32   1.0.33   1.0.34  

Slide 45

Slide 45 text

Defining  build  configura,on   binaryRepository {
 url = 'http://mycompany.bin.repo:8081/artifactory'
 username = 'admin'
 password = 'password'
 name = 'libs-release-local'
 }
 
 environments {
 test {
 server {
 hostname = 'mycompany.test'
 port = 8099
 context = 'todo'
 username = 'manager'
 password = 'manager'
 }
 }
 
 uat {
 server {
 hostname = 'mycompany.uat'
 port = 8199
 context = 'todo'
 username = 'manager'
 password = 'manager'
 }
 } 
 ...
 }   Environment-­‐specific        configura,on        Common    configura,on            Read  creden,als    from  gradle.proper,es            Read  creden,als    from  gradle.proper,es            Read  creden,als    from  gradle.proper,es  

Slide 46

Slide 46 text

Reading  build  configura,on   task loadConfiguration {
 def env = project.hasProperty('env') ? project.getProperty('env') : 'test'
 logger.quiet "Loading configuration for environment '$env’." def configFile = file("$rootDir/gradle/config/buildConfig.groovy")
 def parsedConfig = new ConfigSlurper(env).parse(configFile.toURL())
 
 allprojects {
 ext.config = parsedConfig
 }
 }   Initialization phase Conguration phase Execution phase gradlew –Penv=uat ... Assign  configura,on  to                  extra  property  

Slide 47

Slide 47 text

Using  the  Maven  Publishing  plugin   apply plugin: 'maven-publish'
 ext.fullRepoUrl = "$config.binaryRepository.url/$config.binaryRepository.name”
 
 publishing {
 publications {
 webApp(MavenPublication) {
 from components.web }
 }
 
 repositories {
 maven {
 url fullRepoUrl
 
 credentials {
 username = config.binaryRepository.username
 password = config.binaryRepository.password
 }
 }
 }
 }   Build  repository  URL   from  configura,on   gradlew publish Assign  publica,on  name              and  component  

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

Acceptance  stage:  Retrieve  binaries   Request  versioned  ar,fact   Store  in  temp.  directory   Acceptance stage Deploy Binaries Functional Tests Retrieve Binaries

Slide 50

Slide 50 text

Downloading  the  deployable  ar,fact   1.0.34   1.0.32   1.0.33   1.0.34   1.0.34   Test   UAT   Prod  

Slide 51

Slide 51 text

Task  for  downloading  ar,fact   ext.downloadedArtifact = file("$buildDir/download/$war.archiveName") 
 task downloadBinaryArchive(type: com.manning.gia.BinaryDownload) {
 ext {
 repoPath = project.group.replaceAll('\\.', '/')
 repoBaseArtifactName = war.baseName
 repoVersion = project.version.toString()
 repoArtifact = war.archiveName
 binaryUrl = "${config.binaryRepository.url}/simple/ ${config.binaryRepository.name}/${repoPath}/ ${repoBaseArtifactName}/${repoVersion}/${repoArtifact}"
 }
 
 sourceUrl = binaryUrl
 targetBinary = downloadedArtifact
 }   Target  loca,on  for   downloaded  ar,fact   gradlew downloadBinaryArchive Build  exposed     binary  URL  from   configura,on  

Slide 52

Slide 52 text

Acceptance  stage:  Deploy  binaries   Deployment  on  request   Make  it  a  reliable  process   Acceptance stage Deploy Binaries Functional Tests Retrieve Binaries Use  process  for  all  envs.  

Slide 53

Slide 53 text

Deploying  to  mul,ple  environments   Test   UAT   Prod   –Penv=prod –Penv=uat –Penv=test

Slide 54

Slide 54 text

Deployment  with  the  Cargo  plugin   cargoDeployRemote.dependsOn downloadBinaryArchive, cargoUndeployRemote
 cargoUndeployRemote {
 onlyIf appContextStatus
 } 
 cargo {
 containerId = 'tomcat7x'
 port = config.server.port
 
 deployable {
 file = downloadedArtifact
 context = config.server.context
 }
 
 remote {
 hostname = config.server.hostname
 username = config.server.username
 password = config.server.password
 }
 }              Download  ar,fact  from  binary   repository  and  undeploy  exis,ng  one    Only  undeploy  if   URL  context  exists   Use  environment-­‐ specific  configura,on   gradlew –Penv=uat cargoDeploy

Slide 55

Slide 55 text

No content

Slide 56

Slide 56 text

Acceptance  stage:  Func,onal  tests   Test  all  UI  permuta,ons   Test  important  use  cases   Acceptance stage Deploy Binaries Functional Tests Retrieve Binaries Run  against  different  envs.  

Slide 57

Slide 57 text

In-­‐container  func,onal  tests   functional JettyStop functional TestClasses functional JettyRun functional Test check ... ... 
   task functionalTest(type: Test) {
 ...
 }
 
 task functionalJettyRun(type: org.gradle.api.plugins.jetty.JettyRun) {
 httpPort = functionalJettyHttpPort
 stopPort = functionalJettyStopPort
 stopKey = functionalJettyStopKey
 contextPath = functionalJettyContextPath
 daemon = true
 }
 
 task functionalJettyStop(type: org.gradle.api.plugins.jetty.JettyStop) {
 stopPort = functionalJettyStopPort
 stopKey = functionalJettyStopKey
 }
 
 functionalJettyRun.dependsOn functionalTestClasses
 functionalTest.dependsOn functionalJettyRun
 functionalJettyStop.dependsOn functionalTest
 task inContainerFunctionalTest(dependsOn: functionalJettyStop)   Func,onal  test  task   Custom  JeFy  Run  task   Custom  JeFy  Stop  task  

Slide 58

Slide 58 text

Execu,ng  remote  func,onal  tests   ext {
 functionalTestReportDir = file("$testReportDir/functional")
 functionalTestResultsDir = file("$testResultsDir/functional")
 functionalCommonSystemProperties = ['geb.env': 'firefox', 'geb.build.reportsDir': reporting.file("$name/geb")]
 } task remoteFunctionalTest(type: Test) {
 testClassesDir = sourceSets.functionalTest.output.classesDir
 classpath = sourceSets.functionalTest.runtimeClasspath
 testReportDir = functionalTestReportDir
 testResultsDir = functionalTestResultsDir
 systemProperties functionalCommonSystemProperties
 systemProperty 'geb.build.baseUrl', "http://$config.server.hostname:$config.server.port/$config.server.context/"
 }
   gradlew –Penv=test remoteFunctionalTest Build  URL  from     env.  configura,on   Reuse  setup   proper,es  

Slide 59

Slide 59 text

Going  further:  Capacity  tes,ng   buildscript {
 repositories {
 mavenCentral()
 } 
 dependencies {
 classpath "com.github.kulya:jmeter-gradle-plugin:1.3.1-2.9"
 }
 }
 
 apply plugin: com.github.kulya.gradle.plugins.jmeter.JmeterPlugin   ext.loadTestResources = "$projectDir/src/loadTest/resources"
 
 jmeterRun.configure {
 jmeterTestFiles = [file("$loadTestResources/todo-test-plan.jmx")]
 jmeterPropertyFile = file("$loadTestResources/jmeter.properties")
 jmeterUserProperties = ["hostname=${config.server.hostname}, "port=${config.server.port}", "context=${config.server.context}"]
 logging.captureStandardError LogLevel.INFO }   gradlew –Penv=uat jmeterRun

Slide 60

Slide 60 text

Let’s  bring  Jenkins  into  play!   Test   UAT   Prod        Deployment   Acceptance  Tests   Publish   Download   Trigger  Build   Pull  Source  Code  

Slide 61

Slide 61 text

Model  pipeline  as  series  of  jobs   Triggered  job  when  change              to  SCM  is  detected  

Slide 62

Slide 62 text

Ini,al  Jenkins  Build  Job  

Slide 63

Slide 63 text

Build  Name  SeFer  Plugin  

Slide 64

Slide 64 text

JaCoCo  Plugin  

Slide 65

Slide 65 text

Parameterized  Trigger  Plugin  

Slide 66

Slide 66 text

Always  use  the  Wrapper!   Gradle  Plugin   Run  clean  task  to  remove              exis,ng  ar,facts  

Slide 67

Slide 67 text

Use  Jenkins  environment  variable   Clearly  iden,fy  a  build   through  an  expressive                    build  name   Build  Name  SeFer  Plugin  

Slide 68

Slide 68 text

Next  job  to  trigger  if            build  is  stable   Defines  build  number   parameter  provided  to   subsequent  jobs   Parameterized  Trigger  Plugin  

Slide 69

Slide 69 text

Archive  all  files   Only  archive  if  build          was  successful   Clone  Workspace  SCM  Plugin  

Slide 70

Slide 70 text

Point  to  separated  test  results   Fail  build  of  quality  gate          criteria  are  not  met   JaCoCo  Plugin   Point  to  JaCoCo  files  as  well        as  source  and  class  files  

Slide 71

Slide 71 text

Reuse  ini,al  workspace   Reuse  ini,al  build  number   Clone  Workspace  SCM  Plugin   Build  Name  SeFer  Plugin  

Slide 72

Slide 72 text

Define  the  target  environment   Downstream  job  that  requires  manual  execu,on   Build  Pipeline  Plugin  

Slide 73

Slide 73 text

Build  Pipeline  Plugin   Visualiza,on  of  chained  pipeline  jobs  

Slide 74

Slide 74 text

No content

Slide 75

Slide 75 text

> gradle qa! :askQuestions ! ! BUILD SUCCESSFUL! ! Total time: 300 secs