Building a Continuous Delivery pipeline with Gradle and Jenkins

47,911 views

Published on

Speaker: Peter Niederwieser
G&G Special Topics

Getting software from a developer's machine to a production environment without a fully automated process is time-consuming and error-prone. Continuous Delivery enables building, testing and deploying of software through build pipelines with well-defined quality gates. In this session, we will discuss how to build such a pipeline with the help of Gradle and Jenkins.

With Jenkins as the centerpiece of our build pipeline, we will model our way from build to deployment. We will start by introducing an examplary application and learn how to build it with Gradle. Step by step, we will touch on topics like automating unit, integration and functional tests, incorporating popular code quality tools, as well as packaging, publishing and deploying the deliverable.

Published in: Technology
5 Comments
152 Likes
Statistics
Notes
  • @Bartłomiej Mocior http://gradle.org/videos/continuous-delivery-build-pipeline-jenkins/
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Where I can find code samples from this presentation, is available @GitHub? I couldn't find it.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • If I add testResultsDir as suggested in slide 41 it produces the following error : No such property: testResultsDir for class: org.gradle.api.tasks.testing.Test_Decorated
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • The reason to use the Gradle wrapper is to always ensure reproducible, stable, and self-contained builds. Build scripts evolve over time, and newer Gradle versions might deprecate or remove functionality you are using in the scripts. Since the wrapper is under source control, it always matches the build scripts at a specific point in time (state of the repo). You would be able to checkout a 2-year old commit and have a fully working build, even if the current system-installed Gradle version is incompatible.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Hello! Why did the author advice to "Always use the Wrapper!" (c) on no. 120 slide? What is the reason not to use "Invoke Gradle" option?
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total views
47,911
On SlideShare
0
From Embeds
0
Number of Embeds
600
Actions
Shares
0
Downloads
902
Comments
5
Likes
152
Embeds 0
No embeds

No notes for slide

Building a Continuous Delivery pipeline with Gradle and Jenkins

  1. 1. Building  a   Con,nuous  Delivery  Pipeline  with and Peter  Niederwieser Principal  Engineer,  Gradleware @pniederw
  2. 2. Releases  don’t  have  to  be  painful
  3. 3. Con,nuous  Delivery Deliver  so5ware  fast  and  frequently
  4. 4. #1  Every  commit  can  result  in  a  release  
  5. 5. #1  Every  commit  can  result  in  a  release #2  Automate  everything!    
  6. 6. #1  Every  commit  can  result  in  a  release #2  Automate  everything!   #3     Automated  tests  are  essenCal
  7. 7. #1  Every  commit  can  result  in  a  release #2  Automate  everything!   #3     Automated  tests  are  essenCal #4  Done  means  released
  8. 8. Build  pipeline Automated  manifestaCon  of  delivery  process
  9. 9. Build  quality  in! Establish  automated  quality  gates
  10. 10. Compile/Unit  Tests
  11. 11. Compile/Unit  Tests IntegraCon  Tests
  12. 12. Compile/Unit  Tests IntegraCon  Tests Code  Analysis
  13. 13. Compile/Unit  Tests IntegraCon  Tests Code  Analysis Package/Deploy Test
  14. 14. Compile/Unit  Tests IntegraCon  Tests Code  Analysis Package/Deploy Acceptance  Tests Test
  15. 15. Compile/Unit  Tests IntegraCon  Tests Code  Analysis Package/Deploy Acceptance  Tests Test UAT
  16. 16. Compile/Unit  Tests IntegraCon  Tests Code  Analysis Package/Deploy Acceptance  Tests Test UAT Prod
  17. 17. !
  18. 18. !  But   how?
  19. 19. The  “revolu,onary”  sample  applica,on Writes Browser Internet To Do application Data store Reads
  20. 20. Mul,-­‐project  dependencies Model depend depend Web Repository depend
  21. 21. Project  hierarchy todo gradle wrapper xyz.gradle model build.gradle repository build.gradle web build.gradle build.gradle settings.gradle gradlew gradlew.bat
  22. 22. Project  hierarchy todo gradle wrapper xyz.gradle model build.gradle repository build.gradle web build.gradle build.gradle settings.gradle gradlew gradlew.bat
  23. 23. Project  hierarchy todo gradle wrapper xyz.gradle model build.gradle repository build.gradle web build.gradle build.gradle settings.gradle gradlew gradlew.bat Define  project-­‐specific                          behavior
  24. 24. Project  hierarchy todo gradle wrapper xyz.gradle model build.gradle Defines  which  projects  are      taking  part  in  the  build repository build.gradle settings.gradle web build.gradle build.gradle settings.gradle gradlew gradlew.bat include 'model' include 'repository' include 'web'
  25. 25. Project  hierarchy todo gradle wrapper xyz.gradle model build.gradle repository build.gradle web build.gradle build.gradle settings.gradle gradlew gradlew.bat Always  use  Wrapper to  execute  the  build!
  26. 26. Project  hierarchy todo gradle wrapper xyz.gradle model build.gradle repository build.gradle web build.gradle build.gradle settings.gradle gradlew gradlew.bat    Externalize  concerns  into  script  plugins and  organize  them  in  dedicated  directory
  27. 27. Project  hierarchy todo gradle wrapper xyz.gradle    Externalize  concerns  into  script  plugins and  organize  them  in  dedicated  directory model build.gradle repository Examples: build.gradle web build.gradle build.gradle settings.gradle gradlew gradlew.bat     Versioning  strategy Integra,on  and  func,onal  test  setup Deployment  func,onality ...
  28. 28. Project  ar,facts root project model project JAR repository project JAR web project WAR
  29. 29. Project  ar,facts root project model project JAR repository project JAR web project WAR Deployable  ar,fact
  30. 30. Stages  in  build  pipeline Compile Unit Tests Integration Tests Code Analysis Assemble Distribution Publish Binaries Commit stage Retrieve Binaries Deploy Binaries Functional tests Acceptance stage Asserts  that  system  works Deploy              at  a  technical  level Binaries UAT Deploy Binaries Production
  31. 31. Stages  in  build  pipeline Compile Unit Tests Integration Tests Code Analysis Assemble Distribution Publish Binaries Commit stage Retrieve Binaries Deploy Binaries Functional tests Acceptance stage Deploy Binaries UAT Asserts  that  system  works  on  a   func,onal/non-­‐func,onal  level Deploy Binaries Production
  32. 32. Stages  in  build  pipeline Compile Unit Tests Integration Tests Code Analysis Assemble Distribution Publish Binaries Commit stage Retrieve Binaries Deploy Binaries Functional tests Acceptance stage Deploy Binaries UAT Deploy Binaries Production Trigger  manually
  33. 33. Commit  stage:  Compile/unit  tests Compile Unit Tests Integration Tests Code Analysis Assemble Distribution Publish Binaries Commit stage Rapid  feedback  (<  5  mins) Run  on  every  VCS  check-­‐in Priority:  fix  broken  build
  34. 34. Commit  stage:  Integra,on  tests Compile Unit Tests Integration Tests Code Analysis Assemble Distribution Publish Binaries Commit stage Long  running  tests Require  environment  setup Hard  to  maintain
  35. 35. Separate  tests  in  project  layout todo model repository src integTest java main java Produc,on  Java  sources java Unit  test  Java  sources test web
  36. 36. Separate  tests  in  project  layout todo model repository src integTest java Integra,on  test  Java  sources java Produc,on  Java  sources java Unit  test  Java  sources main test web
  37. 37. 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") }
  38. 38. Separate  tests  with  SourceSets Set  source  and  resources  directory 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") }
  39. 39. Separate  tests  with  SourceSets Set  source  and  resources  directory 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") }  Set  compile  and   run,me  classpath
  40. 40. Separate  tests  with  SourceSets Set  source  and  resources  directory 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  compile  and   run,me  classpath
  41. 41. Separate  tests  with  SourceSets Set  source  and  resources  directory 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  compile  and   run,me  classpath gradlew integrationTest
  42. 42. Database  integra,on  tests ... test start Database build Schema integration Test stop Database check build
  43. 43. Database  integra,on  tests ... test start Database build Schema integration Test stop Database check build
  44. 44. Database  integra,on  tests ... test start Database build Schema integration Test stop Database apply from: "$rootDir/gradle/databaseSetup.gradle" integrationTest.dependsOn startAndPrepareDatabase integrationTest.finalizedBy stopDatabase check.dependsOn integrationTest check build
  45. 45. Database  integra,on  tests ... test start Database build Schema integration Test stop Database check build Separate  complex  setup  logic  into  script  plugin apply from: "$rootDir/gradle/databaseSetup.gradle" integrationTest.dependsOn startAndPrepareDatabase integrationTest.finalizedBy stopDatabase check.dependsOn integrationTest
  46. 46. Database  integra,on  tests ... test start Database build Schema integration Test stop Database check build Separate  complex  setup  logic  into  script  plugin apply from: "$rootDir/gradle/databaseSetup.gradle" integrationTest.dependsOn startAndPrepareDatabase integrationTest.finalizedBy stopDatabase check.dependsOn integrationTest Integrate  tasks  into  build  lifecycle
  47. 47. Picking  the  “right”  code  coverage  tool
  48. 48. Picking  the  “right”  code  coverage  tool Cobertura Offline  bytecode    instrumenta,on
  49. 49. Picking  the  “right”  code  coverage  tool Cobertura Offline  bytecode    instrumenta,on Offline  bytecode    instrumenta,on
  50. 50. Picking  the  “right”  code  coverage  tool Cobertura Offline  bytecode    instrumenta,on Offline  bytecode    instrumenta,on        Source  code    instrumenta,on
  51. 51. Picking  the  “right”  code  coverage  tool Cobertura Offline  bytecode    instrumenta,on Offline  bytecode    instrumenta,on        Source  code    instrumenta,on On-­‐the-­‐fly  bytecode          instrumenta,on
  52. 52. On-­‐the-­‐fly  bytecode  instrumentaCon No  modificaCon  to  source  or  bytecode
  53. 53. Code  coverage  with  JaCoCo apply plugin: "jacoco" task jacocoIntegrationTestReport(type: JacocoReport) { sourceSets sourceSets.main executionData integTest }
  54. 54. Code  coverage  with  JaCoCo Apply  JaCoCo  plugin apply plugin: "jacoco" task jacocoIntegrationTestReport(type: JacocoReport) { sourceSets sourceSets.main executionData integTest }
  55. 55. Code  coverage  with  JaCoCo Apply  JaCoCo  plugin apply plugin: "jacoco" task jacocoIntegrationTestReport(type: JacocoReport) { sourceSets sourceSets.main executionData integTest } Also  report  code  coverage   for  integra,on  tests
  56. 56. Genera,ng  coverage  reports ... test integration Test ... .exec
  57. 57. Genera,ng  coverage  reports ... .exec test integration Test jacoco TestReport ... jacocoIntegration TestReport .exec .html
  58. 58. Genera,ng  coverage  reports ... .exec .html test integration Test jacoco TestReport ... jacocoIntegration TestReport .exec .html
  59. 59. Commit  stage:  Code  analysis Compile Unit Tests Integration Tests Code Analysis Assemble Distribution Publish Binaries Commit stage Perform  code  health  check Fail  build  for  low  quality Record  progress  over  ,me
  60. 60. Sta,c  code  analysis  tools apply plugin: 'pmd' pmd { ignoreFailures = true } FindBugs tasks.withType(Pmd) { reports { xml.enabled = false html.enabled = true } } apply plugin: 'jdepend’ jdepend { toolVersion = '2.9.1' ignoreFailures = true } Checkstyle gradlew check
  61. 61. Measure  quality  over  ,me  with  Sonar Gradle Sonar database Sonar Runner publishes analyzes root model repo. web Gradle project reads / writes
  62. 62. Applying  the  Sonar  Runner  plugin apply plugin: "sonar-runner" sonarRunner { sonarProperties { property "sonar.host.url", "http://my.server.com" property "sonar.jdbc.url", "jdbc:mysql://my.server.com/sonar" property "sonar.jdbc.driverClassName", "com.mysql.jdbc.Driver" property "sonar.jdbc.username", "Fred Flintstone" property "sonar.jdbc.password", "very clever" } } subprojects { sonarRunner { sonarProperties { property "sonar.sourceEncoding", "UTF-8" } } } gradlew sonarRunner
  63. 63. Commit  stage:  Assemble  distribu,on Compile Unit Tests Integration Tests Code Analysis Assemble Distribution Publish Binaries Commit stage Exclude  env.  configura,on Include  build  informa,on Choose  versioning  strategy
  64. 64. Versioning  strategy …the  Maven  way 1.0-­‐SNAPSHOT 1.0 during  development when  released Change  version  with  Maven  Release  plugin
  65. 65. Versioning  strategy …the  Con,nuous  Delivery  way 1.0.134 1.0.134 during  development when  released 1.0.134 Project  version  number Jenkins  build  number
  66. 66. Versioning  strategy …implemented  with  Gradle todo gradle versioning.gradle Contains  version  implementa,on model repository web build.gradle settings.gradle allprojects { apply from: "$rootDir/gradle/versioning.gradle" }
  67. 67. 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 } }
  68. 68. 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 Jenkins  Build  Number 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 } }
  69. 69. 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 Jenkins  Build  Number 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 } }              Builds  version   String  representa,on
  70. 70. 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') } } }
  71. 71. Packaging  the  deployable  ar,fact project(':web') { apply plugin: 'war' Creates  file  containing            build  informa,on 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') } } }
  72. 72. Packaging  the  deployable  ar,fact project(':web') { apply plugin: 'war' Creates  file  containing            build  informa,on 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') } } } Include  build  info  file Into  WAR  distribu,on
  73. 73. Packaging  the  deployable  ar,fact Creates  file  containing            build  informa,on 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') } Include  build  info  file Into  WAR  distribu,on } } gradlew assemble
  74. 74. Commit  stage:  Publish  binaries Compile Unit Tests Integration Tests Code Analysis Assemble Distribution Publish Binaries Commit stage Version  ar,fact(s) Use  binary  repository Publish  once,  then  reuse
  75. 75. Publishing  the  deployable  ar,fact 1.0.32 1.0.34 1.0.33 1.0.34
  76. 76. 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' } }
  77. 77. Defining  build  configura,on        Common  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' } }
  78. 78. Defining  build  configura,on        Common  configura,on Environment-­‐specific 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' } }
  79. 79. Defining  build  configura,on        Common  configura,on Environment-­‐specific configura,on binaryRepository { url = 'http://mycompany.bin.repo:8081/artifactory' username = 'admin' password = 'password' name = 'libs-release-local' }          Read  creden,als  from  gradle.proper,es 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' } }          Read  creden,als  from  gradle.proper,es          Read  creden,als  from  gradle.proper,es
  80. 80. Reading  build  configura,on Initialization phase Conguration phase Execution phase 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 }
  81. 81. Reading  build  configura,on Initialization phase Conguration phase Execution phase 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 } Assign  configura,on  to                extra  property
  82. 82. Reading  build  configura,on Initialization phase Conguration phase Execution phase 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 } Assign  configura,on  to                extra  property gradlew –Penv=uat ...
  83. 83. 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 } } } }
  84. 84. Using  the  Maven  Publishing  plugin Build  repository  URL from  configura,on 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 } } } }
  85. 85. Using  the  Maven  Publishing  plugin Build  repository  URL from  configura,on apply plugin: 'maven-publish' ext.fullRepoUrl = "$config.binaryRepository.url/$config.binaryRepository.name” publishing { publications { webApp(MavenPublication) { from components.web } } Assign  publica,on  name                and  component repositories { maven { url fullRepoUrl credentials { username = config.binaryRepository.username password = config.binaryRepository.password } } } }
  86. 86. Using  the  Maven  Publishing  plugin Build  repository  URL from  configura,on apply plugin: 'maven-publish' ext.fullRepoUrl = "$config.binaryRepository.url/$config.binaryRepository.name” publishing { publications { webApp(MavenPublication) { from components.web } } Assign  publica,on  name                and  component repositories { maven { url fullRepoUrl credentials { username = config.binaryRepository.username password = config.binaryRepository.password } } } } gradlew publish
  87. 87. Acceptance  stage:  Retrieve  binaries Retrieve Binaries Deploy Binaries Functional Tests Acceptance stage Request  versioned  ar,fact Store  in  temp.  directory
  88. 88. Downloading  the  deployable  ar,fact 1.0.32 1.0.34 1.0.34 Test UAT Prod 1.0.33 1.0.34
  89. 89. Task  for  downloading  ar,fact repositories { maven { url fullRepoUrl } } configurations { war } dependencies { war "$project.group:$project.name:$project.version" } task downloadBinaryArchive(type: Copy) { from configurations.war into "$buildDir/download" }
  90. 90. Task  for  downloading  ar,fact Target  loca,on  for downloaded  ar,fact repositories { maven { url fullRepoUrl } } configurations { war } dependencies { war "$project.group:$project.name:$project.version" } task downloadBinaryArchive(type: Copy) { from configurations.war into "$buildDir/download" } gradlew downloadBinaryArchive
  91. 91. Acceptance  stage:  Deploy  binaries Retrieve Binaries Deploy Binaries Functional Tests Acceptance stage Deployment  on  request Make  it  a  reliable  process Use  process  for  all  envs
  92. 92. Deploying  to  mul,ple  environments –Penv=test –Penv=uat Test –Penv=prod UAT Prod
  93. 93. 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 } }
  94. 94. Deployment  with  the  Cargo  plugin            Download  ar,fact  from  binary   repository  and  undeploy  exis,ng  one 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 } }
  95. 95. Deployment  with  the  Cargo  plugin            Download  ar,fact  from  binary   repository  and  undeploy  exis,ng  one cargoDeployRemote.dependsOn downloadBinaryArchive, cargoUndeployRemote cargoUndeployRemote { onlyIf appContextStatus } cargo { containerId = 'tomcat7x' port = config.server.port  Only  undeploy  if URL  context  exists deployable { file = downloadedArtifact context = config.server.context } remote { hostname = config.server.hostname username = config.server.username password = config.server.password } }
  96. 96. Deployment  with  the  Cargo  plugin            Download  ar,fact  from  binary   repository  and  undeploy  exis,ng  one cargoDeployRemote.dependsOn downloadBinaryArchive, cargoUndeployRemote cargoUndeployRemote { onlyIf appContextStatus } cargo { containerId = 'tomcat7x' port = config.server.port  Only  undeploy  if URL  context  exists deployable { file = downloadedArtifact context = config.server.context } remote { hostname = config.server.hostname username = config.server.username password = config.server.password } } Use  environment-­‐specific                          configura,on
  97. 97. Deployment  with  the  Cargo  plugin            Download  ar,fact  from  binary   repository  and  undeploy  exis,ng  one cargoDeployRemote.dependsOn downloadBinaryArchive, cargoUndeployRemote cargoUndeployRemote { onlyIf appContextStatus } cargo { containerId = 'tomcat7x' port = config.server.port  Only  undeploy  if URL  context  exists deployable { file = downloadedArtifact context = config.server.context } remote { hostname = config.server.hostname username = config.server.username password = config.server.password } Use  environment-­‐specific                          configura,on } gradlew –Penv=uat cargoDeploy
  98. 98. Acceptance  stage:  Func,onal  tests Retrieve Binaries Deploy Binaries Functional Tests Acceptance stage Test  all  UI  permuta,ons Test  important  use  cases Run  against  different  envs
  99. 99. In-­‐container  func,onal  tests ... functional TestClasses functional JettyRun functional Test functional JettyStop check ...
  100. 100. In-­‐container  func,onal  tests ... functional TestClasses functional JettyRun functional Test functional JettyStop 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 } functionalTest.dependsOn functionalJettyRun functionalTest.finalizedBy functionalJettyStop task inContainerFunctionalTest(dependsOn: functionalJettyStop) ...
  101. 101. In-­‐container  func,onal  tests ... functional TestClasses functional JettyRun task functionalTest(type: Test) { ... } functional Test functional JettyStop check Func,onal  test  task 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 } functionalTest.dependsOn functionalJettyRun functionalTest.finalizedBy functionalJettyStop task inContainerFunctionalTest(dependsOn: functionalJettyStop) ...
  102. 102. In-­‐container  func,onal  tests ... functional TestClasses functional JettyRun task functionalTest(type: Test) { ... } functional Test functional JettyStop check ... Func,onal  test  task task functionalJettyRun(type: org.gradle.api.plugins.jetty.JettyRun) { httpPort = functionalJettyHttpPort stopPort = functionalJettyStopPort stopKey = functionalJettyStopKey contextPath = functionalJettyContextPath daemon = true } Custom  Jeiy  Run  task task functionalJettyStop(type: org.gradle.api.plugins.jetty.JettyStop) { stopPort = functionalJettyStopPort stopKey = functionalJettyStopKey } functionalTest.dependsOn functionalJettyRun functionalTest.finalizedBy functionalJettyStop task inContainerFunctionalTest(dependsOn: functionalJettyStop)
  103. 103. In-­‐container  func,onal  tests ... functional TestClasses functional JettyRun task functionalTest(type: Test) { ... } functional Test functional JettyStop check ... Func,onal  test  task task functionalJettyRun(type: org.gradle.api.plugins.jetty.JettyRun) { httpPort = functionalJettyHttpPort stopPort = functionalJettyStopPort stopKey = functionalJettyStopKey contextPath = functionalJettyContextPath daemon = true } Custom  Jeiy  Run  task task functionalJettyStop(type: org.gradle.api.plugins.jetty.JettyStop) { stopPort = functionalJettyStopPort stopKey = functionalJettyStopKey } Custom  Jeiy  Stop  task functionalTest.dependsOn functionalJettyRun functionalTest.finalizedBy functionalJettyStop task inContainerFunctionalTest(dependsOn: functionalJettyStop)
  104. 104. 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/" }
  105. 105. 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")] } Reuse  setup        proper,es 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/" }
  106. 106. 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")] } Reuse  setup        proper,es 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/" } Build  URL  from   env.  configura,on
  107. 107. 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")] } Reuse  setup        proper,es 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/" } Build  URL  from   env.  configura,on gradlew –Penv=test remoteFunctionalTest
  108. 108. 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
  109. 109. Let’s  bring  Jenkins  into  play! Publish Trigger  Build Download Pull  Source  Code      Deployment Acceptance  Tests Test UAT Prod
  110. 110. Model  pipeline  as  series  of  jobs
  111. 111. Model  pipeline  as  series  of  jobs Triggered  job  when  change            to  SCM  is  detected
  112. 112. Ini,al  Jenkins  Build  Job
  113. 113. Build  Name  Seier  Plugin
  114. 114. JaCoCo  Plugin
  115. 115. Parameterized  Trigger  Plugin
  116. 116. Gradle  Plugin
  117. 117. Gradle  Plugin Always  use  the  Wrapper!
  118. 118. Gradle  Plugin Always  use  the  Wrapper! Run  clean  task  to  remove            exis,ng  ar,facts
  119. 119. Build  Name  Seier  Plugin
  120. 120. Build  Name  Seier  Plugin Clearly  iden,fy  a  build through  an  expressive                  build  name
  121. 121. Build  Name  Seier  Plugin Clearly  iden,fy  a  build through  an  expressive                  build  name Use  Jenkins  environment  variable
  122. 122. Parameterized  Trigger  Plugin
  123. 123. Parameterized  Trigger  Plugin Next  job  to  trigger  if          build  is  stable
  124. 124. Parameterized  Trigger  Plugin Next  job  to  trigger  if          build  is  stable Defines  build  number parameter  provided  to   subsequent  jobs
  125. 125. Clone  Workspace  SCM  Plugin
  126. 126. Clone  Workspace  SCM  Plugin Archive  all  files
  127. 127. Clone  Workspace  SCM  Plugin Archive  all  files Only  archive  if  build        was  successful
  128. 128. JaCoCo  Plugin
  129. 129. JaCoCo  Plugin Point  to  separated  test  results
  130. 130. JaCoCo  Plugin Point  to  separated  test  results Point  to  JaCoCo  files  as  well      as  source  and  class  files
  131. 131. JaCoCo  Plugin Point  to  separated  test  results Point  to  JaCoCo  files  as  well      as  source  and  class  files Fail  build  of  quality  gate        criteria  are  not  met
  132. 132. Clone  Workspace  SCM  Plugin Build  Name  Seier  Plugin
  133. 133. Clone  Workspace  SCM  Plugin Build  Name  Seier  Plugin Reuse  ini,al  workspace
  134. 134. Clone  Workspace  SCM  Plugin Build  Name  Seier  Plugin Reuse  ini,al  workspace Reuse  ini,al  build  number
  135. 135. Build  Pipeline  Plugin
  136. 136. Build  Pipeline  Plugin Define  the  target  environment
  137. 137. Build  Pipeline  Plugin Define  the  target  environment Downstream  job  that  requires  manual  execu,on
  138. 138. Build  Pipeline  Plugin Visualiza,on  of  chained  pipeline  jobs
  139. 139. > gradle qa :askQuestions BUILD SUCCESSFUL Total time: 300 secs

×