Your SlideShare is downloading. ×
Building a Continuous Delivery pipeline with Gradle and Jenkins
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Building a Continuous Delivery pipeline with Gradle and Jenkins

16,294

Published on

Speaker: Peter Niederwieser …

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
2 Comments
72 Likes
Statistics
Notes
  • 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
16,294
On Slideshare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
442
Comments
2
Likes
72
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Building  a   Con,nuous  Delivery  Pipeline  with and Peter  Niederwieser Principal  Engineer,  Gradleware @pniederw
  • 2. Releases  don’t  have  to  be  painful
  • 3. Con,nuous  Delivery Deliver  so5ware  fast  and  frequently
  • 4. #1  Every  commit  can  result  in  a  release  
  • 5. #1  Every  commit  can  result  in  a  release #2  Automate  everything!    
  • 6. #1  Every  commit  can  result  in  a  release #2  Automate  everything!   #3     Automated  tests  are  essenCal
  • 7. #1  Every  commit  can  result  in  a  release #2  Automate  everything!   #3     Automated  tests  are  essenCal #4  Done  means  released
  • 8. Build  pipeline Automated  manifestaCon  of  delivery  process
  • 9. Build  quality  in! Establish  automated  quality  gates
  • 10. Compile/Unit  Tests
  • 11. Compile/Unit  Tests IntegraCon  Tests
  • 12. Compile/Unit  Tests IntegraCon  Tests Code  Analysis
  • 13. Compile/Unit  Tests IntegraCon  Tests Code  Analysis Package/Deploy Test
  • 14. Compile/Unit  Tests IntegraCon  Tests Code  Analysis Package/Deploy Acceptance  Tests Test
  • 15. Compile/Unit  Tests IntegraCon  Tests Code  Analysis Package/Deploy Acceptance  Tests Test UAT
  • 16. Compile/Unit  Tests IntegraCon  Tests Code  Analysis Package/Deploy Acceptance  Tests Test UAT Prod
  • 17. !
  • 18. !  But   how?
  • 19. The  “revolu,onary”  sample  applica,on Writes Browser Internet To Do application Data store Reads
  • 20. Mul,-­‐project  dependencies Model depend depend Web Repository depend
  • 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. Project  hierarchy todo gradle wrapper xyz.gradle model build.gradle repository build.gradle web build.gradle build.gradle settings.gradle gradlew gradlew.bat
  • 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. 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. 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. 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. 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. Project  ar,facts root project model project JAR repository project JAR web project WAR
  • 29. Project  ar,facts root project model project JAR repository project JAR web project WAR Deployable  ar,fact
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Database  integra,on  tests ... test start Database build Schema integration Test stop Database check build
  • 43. Database  integra,on  tests ... test start Database build Schema integration Test stop Database check build
  • 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. 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. 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. Picking  the  “right”  code  coverage  tool
  • 48. Picking  the  “right”  code  coverage  tool Cobertura Offline  bytecode    instrumenta,on
  • 49. Picking  the  “right”  code  coverage  tool Cobertura Offline  bytecode    instrumenta,on Offline  bytecode    instrumenta,on
  • 50. Picking  the  “right”  code  coverage  tool Cobertura Offline  bytecode    instrumenta,on Offline  bytecode    instrumenta,on        Source  code    instrumenta,on
  • 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. On-­‐the-­‐fly  bytecode  instrumentaCon No  modificaCon  to  source  or  bytecode
  • 53. Code  coverage  with  JaCoCo apply plugin: "jacoco" task jacocoIntegrationTestReport(type: JacocoReport) { sourceSets sourceSets.main executionData integTest }
  • 54. Code  coverage  with  JaCoCo Apply  JaCoCo  plugin apply plugin: "jacoco" task jacocoIntegrationTestReport(type: JacocoReport) { sourceSets sourceSets.main executionData integTest }
  • 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. Genera,ng  coverage  reports ... test integration Test ... .exec
  • 57. Genera,ng  coverage  reports ... .exec test integration Test jacoco TestReport ... jacocoIntegration TestReport .exec .html
  • 58. Genera,ng  coverage  reports ... .exec .html test integration Test jacoco TestReport ... jacocoIntegration TestReport .exec .html
  • 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. 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. Measure  quality  over  ,me  with  Sonar Gradle Sonar database Sonar Runner publishes analyzes root model repo. web Gradle project reads / writes
  • 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. 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. Versioning  strategy …the  Maven  way 1.0-­‐SNAPSHOT 1.0 during  development when  released Change  version  with  Maven  Release  plugin
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Publishing  the  deployable  ar,fact 1.0.32 1.0.34 1.0.33 1.0.34
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Acceptance  stage:  Retrieve  binaries Retrieve Binaries Deploy Binaries Functional Tests Acceptance stage Request  versioned  ar,fact Store  in  temp.  directory
  • 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. 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. 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. 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. Deploying  to  mul,ple  environments –Penv=test –Penv=uat Test –Penv=prod UAT Prod
  • 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. 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. 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. 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. 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. 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. In-­‐container  func,onal  tests ... functional TestClasses functional JettyRun functional Test functional JettyStop check ...
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. Let’s  bring  Jenkins  into  play! Publish Trigger  Build Download Pull  Source  Code      Deployment Acceptance  Tests Test UAT Prod
  • 110. Model  pipeline  as  series  of  jobs
  • 111. Model  pipeline  as  series  of  jobs Triggered  job  when  change            to  SCM  is  detected
  • 112. Ini,al  Jenkins  Build  Job
  • 113. Build  Name  Seier  Plugin
  • 114. JaCoCo  Plugin
  • 115. Parameterized  Trigger  Plugin
  • 116. Gradle  Plugin
  • 117. Gradle  Plugin Always  use  the  Wrapper!
  • 118. Gradle  Plugin Always  use  the  Wrapper! Run  clean  task  to  remove            exis,ng  ar,facts
  • 119. Build  Name  Seier  Plugin
  • 120. Build  Name  Seier  Plugin Clearly  iden,fy  a  build through  an  expressive                  build  name
  • 121. Build  Name  Seier  Plugin Clearly  iden,fy  a  build through  an  expressive                  build  name Use  Jenkins  environment  variable
  • 122. Parameterized  Trigger  Plugin
  • 123. Parameterized  Trigger  Plugin Next  job  to  trigger  if          build  is  stable
  • 124. Parameterized  Trigger  Plugin Next  job  to  trigger  if          build  is  stable Defines  build  number parameter  provided  to   subsequent  jobs
  • 125. Clone  Workspace  SCM  Plugin
  • 126. Clone  Workspace  SCM  Plugin Archive  all  files
  • 127. Clone  Workspace  SCM  Plugin Archive  all  files Only  archive  if  build        was  successful
  • 128. JaCoCo  Plugin
  • 129. JaCoCo  Plugin Point  to  separated  test  results
  • 130. JaCoCo  Plugin Point  to  separated  test  results Point  to  JaCoCo  files  as  well      as  source  and  class  files
  • 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. Clone  Workspace  SCM  Plugin Build  Name  Seier  Plugin
  • 133. Clone  Workspace  SCM  Plugin Build  Name  Seier  Plugin Reuse  ini,al  workspace
  • 134. Clone  Workspace  SCM  Plugin Build  Name  Seier  Plugin Reuse  ini,al  workspace Reuse  ini,al  build  number
  • 135. Build  Pipeline  Plugin
  • 136. Build  Pipeline  Plugin Define  the  target  environment
  • 137. Build  Pipeline  Plugin Define  the  target  environment Downstream  job  that  requires  manual  execu,on
  • 138. Build  Pipeline  Plugin Visualiza,on  of  chained  pipeline  jobs
  • 139. > gradle qa :askQuestions BUILD SUCCESSFUL Total time: 300 secs

×