building a continuous delivery pipeline with gradle and jenkins

Post on 09-May-2015

16.540 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

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.

TRANSCRIPT

Building  a  Con,nuous  Delivery  Pipeline

 with

and

Peter  NiederwieserPrincipal  Engineer,  Gradleware

@pniederw

Releases  don’t  have  to  be  painful

Con,nuous  Delivery

Deliver  so5ware  fast  and  frequently

#1  Every  commit  can  result  in  a  release

 

#1  Every  commit  can  result  in  a  release

#2  Automate  everything!  

 

#1  Every  commit  can  result  in  a  release

#2  Automate  everything!  

 #3  Automated  tests  are  essenCal

#1  Every  commit  can  result  in  a  release

#2  Automate  everything!  

 #3  Automated  tests  are  essenCal

#4  Done  means  released

Build  pipeline

Automated  manifestaCon  of  delivery  process

Build  quality  in!

Establish  automated  quality  gates

Compile/Unit  Tests

Compile/Unit  Tests

IntegraCon  Tests

Compile/Unit  Tests

IntegraCon  Tests

Code  Analysis

Test

Compile/Unit  Tests

IntegraCon  Tests

Code  Analysis

Package/Deploy

Test

Compile/Unit  Tests

IntegraCon  Tests

Code  Analysis

Package/Deploy

Acceptance  Tests

Test

Compile/Unit  Tests

IntegraCon  Tests

Code  Analysis

Package/Deploy

UAT

Acceptance  Tests

Test

Compile/Unit  Tests

IntegraCon  Tests

Code  Analysis

Package/Deploy

UAT Prod

Acceptance  Tests

!

!  But  how?

The  “revolu,onary”  sample  applica,on

Internet To Do applicationBrowser Data

store

Writes

Reads

Mul,-­‐project  dependencies

Model

Web Repository

dependdepend

depend

Project  hierarchytodo

model

repository

build.gradle

settings.gradle

web

build.gradle

build.gradle

build.gradle

gradle

wrapper

xyz.gradle

gradlew

gradlew.bat

Project  hierarchytodo

model

repository

build.gradle

settings.gradle

web

build.gradle

build.gradle

build.gradle

gradle

wrapper

xyz.gradle

gradlew

gradlew.bat

Project  hierarchytodo

model

repository

build.gradle

settings.gradle

web

build.gradle

build.gradle

build.gradle

gradle

wrapper

xyz.gradle

gradlew

gradlew.bat

Define  project-­‐specific                          behavior

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

Project  hierarchytodo

model

repository

build.gradle

settings.gradle

web

build.gradle

build.gradle

build.gradle

gradle

wrapper

xyz.gradle

gradlew

gradlew.bat

Always  use  Wrapperto  execute  the  build!

Project  hierarchytodo

model

repository

build.gradle

settings.gradle

web

build.gradle

build.gradle

build.gradle

gradle

wrapper

xyz.gradle

gradlew

gradlew.bat

   Externalize  concerns  into  script  pluginsand  organize  them  in  dedicated  directory

Project  hierarchytodo

model

repository

build.gradle

settings.gradle

web

build.gradle

build.gradle

build.gradle

gradle

wrapper

xyz.gradle

gradlew

gradlew.bat

   Externalize  concerns  into  script  pluginsand  organize  them  in  dedicated  directory

Examples:

Versioning  strategy Integra,on  and  func,onal  test  setup Deployment  func,onality ...

Project  ar,facts

rootproject

modelproject

repositoryproject

webproject

JAR

JAR

WAR

Project  ar,facts

rootproject

modelproject

repositoryproject

webproject

JAR

JAR

WAR

Deployable  ar,fact

Stages  in  build  pipeline

Acceptance stage

Functional tests

PublishBinaries

Commit stage

DeployBinaries

UAT

DeployBinaries

Production

IntegrationTests

Code Analysis

AssembleDistribution

CompileUnit Tests

RetrieveBinaries

DeployBinaries

Asserts  that  system  works              at  a  technical  level

Stages  in  build  pipeline

Acceptance stage

Functional tests

PublishBinaries

Commit stage

DeployBinaries

UAT

DeployBinaries

Production

IntegrationTests

Code Analysis

AssembleDistribution

CompileUnit Tests

RetrieveBinaries

DeployBinaries

Asserts  that  system  works  on  a  func,onal/non-­‐func,onal  level

Stages  in  build  pipeline

Acceptance stage

Functional tests

PublishBinaries

Commit stage

DeployBinaries

UAT

DeployBinaries

Production

IntegrationTests

Code Analysis

AssembleDistribution

CompileUnit Tests

RetrieveBinaries

DeployBinaries

Trigger  manuallyTrigger  manually

Commit  stage:  Compile/unit  tests

Rapid  feedback  (<  5  mins)

Run  on  every  VCS  check-­‐in

Priority:  fix  broken  build

PublishBinaries

Commit stage

CompileUnit Tests

IntegrationTests

Code Analysis

AssembleDistribution

Commit  stage:  Integra,on  tests

Long  running  tests

Require  environment  setup

Hard  to  maintain

PublishBinaries

Commit stage

CompileUnit Tests

IntegrationTests

Code Analysis

AssembleDistribution

Separate  tests  in  project  layout

todo

model

repository

web

src

integTest

java

main

java

test

java

Produc,on  Java  sources

Unit  test  Java  sources

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

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")}

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")}

Set  source  and  resources  directory

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")}

Set  source  and  resources  directory

 Set  compile  and  run,me  classpath

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

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

Database  integra,on  tests

stopDatabasetest start

Databasebuild

Schemaintegration

Test check build...

Database  integra,on  tests

stopDatabasetest start

Databasebuild

Schemaintegration

Test check build...

Database  integra,on  tests

apply from: "$rootDir/gradle/databaseSetup.gradle"

integrationTest.dependsOn startAndPrepareDatabaseintegrationTest.finalizedBy stopDatabase

check.dependsOn integrationTest

stopDatabasetest start

Databasebuild

Schemaintegration

Test check build...

Database  integra,on  tests

apply from: "$rootDir/gradle/databaseSetup.gradle"

integrationTest.dependsOn startAndPrepareDatabaseintegrationTest.finalizedBy stopDatabase

check.dependsOn integrationTest

stopDatabasetest start

Databasebuild

Schemaintegration

Test check build...

Separate  complex  setup  logic  into  script  plugin

Database  integra,on  tests

apply from: "$rootDir/gradle/databaseSetup.gradle"

integrationTest.dependsOn startAndPrepareDatabaseintegrationTest.finalizedBy stopDatabase

check.dependsOn integrationTest

stopDatabasetest start

Databasebuild

Schemaintegration

Test check build...

Separate  complex  setup  logic  into  script  plugin

Integrate  tasks  into  build  lifecycle

Picking  the  “right”  code  coverage  tool

Picking  the  “right”  code  coverage  tool

Cobertura Offline  bytecode    instrumenta,on

Picking  the  “right”  code  coverage  tool

Cobertura Offline  bytecode    instrumenta,on

Offline  bytecode    instrumenta,on

Picking  the  “right”  code  coverage  tool

Cobertura Offline  bytecode    instrumenta,on

       Source  code    instrumenta,on

Offline  bytecode    instrumenta,on

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

On-­‐the-­‐fly  bytecode  instrumentaCon

No  modificaCon  to  source  or  bytecode

Code  coverage  with  JaCoCo

apply plugin: "jacoco"

task jacocoIntegrationTestReport(type: JacocoReport) { sourceSets sourceSets.main executionData integTest}

Code  coverage  with  JaCoCo

apply plugin: "jacoco"

task jacocoIntegrationTestReport(type: JacocoReport) { sourceSets sourceSets.main executionData integTest}

Apply  JaCoCo  plugin

Code  coverage  with  JaCoCo

apply plugin: "jacoco"

task jacocoIntegrationTestReport(type: JacocoReport) { sourceSets sourceSets.main executionData integTest}

Apply  JaCoCo  plugin

Also  report  code  coverage  for  integra,on  tests

Genera,ng  coverage  reports

test integrationTest

... ... .exec

Genera,ng  coverage  reports

test integrationTest

... ... .exec

jacocoTestReport

jacocoIntegrationTestReport

.exec .html

Genera,ng  coverage  reports

test integrationTest

... ... .exec

jacocoTestReport

jacocoIntegrationTestReport

.exec .html

.html

Commit  stage:  Code  analysis

PublishBinaries

Commit stage

CompileUnit Tests

IntegrationTests

Code Analysis

AssembleDistribution

Perform  code  health  check

Fail  build  for  low  quality

Record  progress  over  ,me

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

Measure  quality  over  ,me  with  Sonar

Sonardatabase

Gradle

Sonar Runner

root

model

repo.

web

analyzes

publishes reads / writes

Gradle project

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

Commit  stage:  Assemble  distribu,on

Exclude  env.  configura,on

Include  build  informa,on

Choose  versioning  strategy

PublishBinaries

Commit stage

CompileUnit Tests

IntegrationTests

Code Analysis

AssembleDistribution

Versioning  strategy

1.0-­‐SNAPSHOT 1.0

during  development when  released

…the  Maven  way

Change  version  with  Maven  Release  plugin

Versioning  strategy

1.0.134 1.0.134

during  development when  released

…the  Con,nuous  Delivery  way

1.0.134Jenkins  build  numberProject  version  number

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

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 }}

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

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

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') } }}

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

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  fileInto  WAR  distribu,on

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  fileInto  WAR  distribu,on

gradlew assemble

Commit  stage:  Publish  binaries

Version  ar,fact(s)

Use  binary  repository

Publish  once,  then  reuse

PublishBinaries

Commit stage

CompileUnit Tests

IntegrationTests

Code Analysis

AssembleDistribution

Publishing  the  deployable  ar,fact

1.0.34

1.0.32 1.0.33 1.0.34

Defining  build  configura,onbinaryRepository { 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' } }

...}

Defining  build  configura,onbinaryRepository { 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' } }

...}

       Common  configura,on

Defining  build  configura,onbinaryRepository { 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-­‐specificconfigura,on

       Common  configura,on

Defining  build  configura,onbinaryRepository { 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-­‐specificconfigura,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

Reading  build  configura,on

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}

Initializationphase

Congurationphase

Executionphase

Reading  build  configura,on

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}

Initializationphase

Congurationphase

Executionphase

Assign  configura,on  to                extra  property

Reading  build  configura,on

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}

Initializationphase

Congurationphase

Executionphase

Assign  configura,on  to                extra  property

gradlew –Penv=uat ...

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 } } }}

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  URLfrom  configura,on

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  URLfrom  configura,on

Assign  publica,on  name                and  component

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  URLfrom  configura,on

gradlew publish

Assign  publica,on  name                and  component

Acceptance  stage:  Retrieve  binaries

Request  versioned  ar,fact

Store  in  temp.  directory

Acceptance stage

DeployBinaries

Functional Tests

RetrieveBinaries

Downloading  the  deployable  ar,fact

1.0.34

1.0.32 1.0.33 1.0.34

1.0.34

Test

UAT

Prod

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" }

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" }

Target  loca,on  fordownloaded  ar,fact

gradlew downloadBinaryArchive

Acceptance  stage:  Deploy  binaries

Deployment  on  request

Make  it  a  reliable  process

Acceptance stage

DeployBinaries

Functional Tests

RetrieveBinaries

Use  process  for  all  envs

Deploying  to  mul,ple  environments

Test

UAT

Prod

–Penv=prod

–Penv=uat

–Penv=test

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 }}

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

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  ifURL  context  exists

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  ifURL  context  exists

Use  environment-­‐specific                                configura,on

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  ifURL  context  exists

Use  environment-­‐specific                                configura,on

gradlew –Penv=uat cargoDeploy

Acceptance  stage:  Func,onal  tests

Test  all  UI  permuta,ons

Test  important  use  cases

Acceptance stage

DeployBinaries

Functional Tests

RetrieveBinaries

Run  against  different  envs

In-­‐container  func,onal  tests

functionalJettyStop

functionalTestClasses

functionalJettyRun

functionalTest check... ...

In-­‐container  func,onal  tests

functionalJettyStop

functionalTestClasses

functionalJettyRun

functionalTest 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 functionalJettyRunfunctionalTest.finalizedBy functionalJettyStoptask inContainerFunctionalTest(dependsOn: functionalJettyStop)

In-­‐container  func,onal  tests

functionalJettyStop

functionalTestClasses

functionalJettyRun

functionalTest 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 functionalJettyRunfunctionalTest.finalizedBy functionalJettyStoptask inContainerFunctionalTest(dependsOn: functionalJettyStop)

Func,onal  test  task

In-­‐container  func,onal  tests

functionalJettyStop

functionalTestClasses

functionalJettyRun

functionalTest 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 functionalJettyRunfunctionalTest.finalizedBy functionalJettyStoptask inContainerFunctionalTest(dependsOn: functionalJettyStop)

Func,onal  test  task

Custom  Jeiy  Run  task

In-­‐container  func,onal  tests

functionalJettyStop

functionalTestClasses

functionalJettyRun

functionalTest 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 functionalJettyRunfunctionalTest.finalizedBy functionalJettyStoptask inContainerFunctionalTest(dependsOn: functionalJettyStop)

Func,onal  test  task

Custom  Jeiy  Run  task

Custom  Jeiy  Stop  task

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/"}

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/"}

Reuse  setup        proper,es

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/"}

Build  URL  from  env.  configura,on

Reuse  setup        proper,es

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

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

Let’s  bring  Jenkins  into  play!

Test

UAT

Prod

     DeploymentAcceptance  Tests

Publish

Download

Trigger  Build

Pull  Source  Code

Model  pipeline  as  series  of  jobs

Model  pipeline  as  series  of  jobs

Triggered  job  when  change            to  SCM  is  detected

Ini,al  Jenkins  Build  Job

Build  Name  Seier  Plugin

JaCoCo  Plugin

Parameterized  Trigger  Plugin

Gradle  Plugin

Always  use  the  Wrapper!

Gradle  Plugin

Always  use  the  Wrapper!

Gradle  Plugin

Run  clean  task  to  remove            exis,ng  ar,facts

Build  Name  Seier  Plugin

Clearly  iden,fy  a  buildthrough  an  expressive                  build  name

Build  Name  Seier  Plugin

Use  Jenkins  environment  variable

Clearly  iden,fy  a  buildthrough  an  expressive                  build  name

Build  Name  Seier  Plugin

Parameterized  Trigger  Plugin

Next  job  to  trigger  if          build  is  stable

Parameterized  Trigger  Plugin

Next  job  to  trigger  if          build  is  stable

Defines  build  numberparameter  provided  to  subsequent  jobs

Parameterized  Trigger  Plugin

Clone  Workspace  SCM  Plugin

Archive  all  files

Clone  Workspace  SCM  Plugin

Archive  all  files

Only  archive  if  build        was  successful

Clone  Workspace  SCM  Plugin

JaCoCo  Plugin

Point  to  separated  test  results

JaCoCo  Plugin

Point  to  separated  test  results

JaCoCo  Plugin

Point  to  JaCoCo  files  as  well      as  source  and  class  files

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

Clone  Workspace  SCM  Plugin Build  Name  Seier  Plugin

Reuse  ini,al  workspace

Clone  Workspace  SCM  Plugin Build  Name  Seier  Plugin

Reuse  ini,al  workspace

Reuse  ini,al  build  number

Clone  Workspace  SCM  Plugin Build  Name  Seier  Plugin

Build  Pipeline  Plugin

Define  the  target  environment

Build  Pipeline  Plugin

Define  the  target  environment

Downstream  job  that  requires  manual  execu,on

Build  Pipeline  Plugin

Build  Pipeline  Plugin

Visualiza,on  of  chained  pipeline  jobs

> gradle qa:askQuestions

BUILD SUCCESSFUL

Total time: 300 secs

top related