Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.
Continuous Delivery with Gradle 
and Jenkins 
By Peter Niederwieser 
© 2014 SpringOne 2GX. All rights reserved. Do not dis...
Releases 
don’t 
have 
to 
be 
painful
Con4nuous 
Delivery 
Deliver 
so8ware 
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 
essen4al
#1 
Every 
commit 
can 
result 
in 
a 
release 
#2 
Automate 
everything! 
#3 
Automated 
tests 
are 
essen4al 
#4 
Done 
...
Build 
pipeline 
Automated 
manifesta4on 
of 
delivery 
process
Build 
quality 
in! 
Establish 
automated 
quality 
gates
Compile/Unit 
Tests
Compile/Unit 
Tests 
Integra4on 
Tests
Compile/Unit 
Tests 
Integra4on 
Tests 
Code 
Analysis
Test 
Compile/Unit 
Tests 
Integra4on 
Tests 
Code 
Analysis 
Package/Deploy
Test 
Compile/Unit 
Tests 
Integra4on 
Tests 
Code 
Analysis 
Package/Deploy 
Acceptance 
Tests
Test 
Compile/Unit 
Tests 
Integra4on 
Tests 
Code 
Analysis 
Package/Deploy 
UAT 
Acceptance 
Tests
Test 
Compile/Unit 
Tests 
Integra4on 
Tests 
Code 
Analysis 
Package/Deploy 
UAT Prod 
Acceptance 
Tests
!
! 
But 
how?
The 
“revolu4onary” 
sample 
applica4on 
Browser Internet 
Data 
To Do 
application 
store 
Writes 
Reads
Mul4-­‐project 
dependencies 
Model 
depend depend 
Web Repository 
depend
Project 
hierarchy 
todo 
gradle 
model 
wrapper 
xyz.gradle 
build.gradle 
repository 
web 
build.gradle 
build.gradle 
b...
Project 
hierarchy 
todo 
gradle 
model 
wrapper 
xyz.gradle 
build.gradle 
repository 
web 
build.gradle 
build.gradle 
b...
Project 
hierarchy 
todo 
gradle 
model 
wrapper 
xyz.gradle 
build.gradle 
repository 
web 
build.gradle 
build.gradle 
b...
Project 
hierarchy 
Defines 
which 
projects 
are 
taking 
part 
in 
the 
build 
settings.gradle 
include 'model' 
include...
Project 
hierarchy 
todo 
gradle 
model 
wrapper 
xyz.gradle 
build.gradle 
repository 
web 
build.gradle 
build.gradle 
b...
Project 
hierarchy 
todo 
gradle 
model 
wrapper 
xyz.gradle 
build.gradle 
repository 
web 
build.gradle 
build.gradle 
b...
Project 
hierarchy 
todo 
gradle 
model 
wrapper 
xyz.gradle 
build.gradle 
repository 
web 
build.gradle 
build.gradle 
b...
Project 
ar4facts 
root 
project 
model 
project 
repository 
project 
web 
project 
JAR 
JAR 
WAR
Project 
ar4facts 
root 
project 
model 
project 
repository 
project 
web 
project 
JAR 
JAR 
WAR 
Deployable 
ar4fact
Stages 
in 
build 
pipeline 
Functional 
tests 
Acceptance stage 
Publish 
Binaries 
Commit stage 
Deploy 
Binaries 
UAT 
...
Stages 
in 
build 
pipeline 
Functional 
tests 
Acceptance stage 
Publish 
Binaries 
Commit stage 
Deploy 
Binaries 
UAT 
...
Stages 
in 
build 
pipeline 
Functional 
tests 
Acceptance stage 
Publish 
Binaries 
Commit stage 
Deploy 
Binaries 
UAT 
...
Commit 
stage: 
Compile/unit 
tests 
Rapid 
feedback 
(< 
5 
mins) 
Run 
on 
every 
VCS 
check-­‐in 
Priority: 
fix 
broke...
Commit 
stage: 
Integra4on 
tests 
Longer 
running 
tests 
Require 
environment 
setup 
Harder 
to 
maintain 
Publish 
Bin...
Separate 
tests 
in 
project 
layout 
todo 
model 
repository 
web 
src 
integTest 
java 
main 
java 
test 
java 
Produc4o...
Separate 
tests 
in 
project 
layout 
todo 
model 
repository 
web 
src 
integTest 
java 
main 
java 
test 
java 
Integra4...
Separate 
tests 
with 
SourceSets 
sourceSets { 
integrationTest { 
java.srcDir file('src/integTest/java') 
resources.srcD...
Separate 
tests 
with 
SourceSets 
sourceSets { 
integrationTest { 
java.srcDir file('src/integTest/java') 
resources.srcD...
Separate 
tests 
with 
SourceSets 
sourceSets { 
integrationTest { 
java.srcDir file('src/integTest/java') 
resources.srcD...
Separate 
tests 
with 
SourceSets 
sourceSets { 
integrationTest { 
java.srcDir file('src/integTest/java') 
resources.srcD...
Separate 
tests 
with 
SourceSets 
sourceSets { 
integrationTest { 
java.srcDir file('src/integTest/java') 
resources.srcD...
Database 
integra4on 
tests 
... 
stop 
check build Database 
test 
start 
Database 
build 
Schema 
integration 
Test
Database 
integra4on 
tests 
... 
stop 
check build Database 
test 
start 
Database 
build 
Schema 
integration 
Test
Database 
integra4on 
tests 
... 
stop 
check build Database 
apply from: "$rootDir/gradle/databaseSetup.gradle" 
! 
integ...
Database 
integra4on 
tests 
... 
stop 
check build Database 
apply from: "$rootDir/gradle/databaseSetup.gradle" 
! 
integ...
Database 
integra4on 
tests 
... 
stop 
check build Database 
apply from: "$rootDir/gradle/databaseSetup.gradle" 
! 
integ...
Picking 
the 
“right” 
code 
coverage 
tool
Picking 
the 
“right” 
code 
coverage 
tool 
Cobertura Offline 
bytecode 
instrumenta4on
Picking 
the 
“right” 
code 
coverage 
tool 
Cobertura Offline 
bytecode 
instrumenta4on 
Offline 
bytecode 
instrumenta4o...
Picking 
the 
“right” 
code 
coverage 
tool 
Cobertura Offline 
bytecode 
instrumenta4on 
Offline 
bytecode 
instrumenta4o...
Picking 
the 
“right” 
code 
coverage 
tool 
Cobertura Offline 
bytecode 
instrumenta4on 
Offline 
bytecode 
instrumenta4o...
On-­‐the-­‐fly 
bytecode 
instrumenta4on 
No 
modifica4on 
to 
source 
or 
bytecode
Code 
coverage 
with 
JaCoCo 
apply plugin: "jacoco" 
! 
task jacocoIntegrationTestReport(type: JacocoReport) { 
sourceSet...
Code 
coverage 
with 
JaCoCo 
apply plugin: "jacoco" 
! 
task jacocoIntegrationTestReport(type: JacocoReport) { 
sourceSet...
Code 
coverage 
with 
JaCoCo 
apply plugin: "jacoco" 
! 
task jacocoIntegrationTestReport(type: JacocoReport) { 
sourceSet...
Genera4ng 
coverage 
reports 
... integration 
... test 
Test .exec
Genera4ng 
coverage 
reports 
... integration 
... test 
Test .exec 
.exec .html 
jacoco 
TestReport 
jacocoIntegration 
T...
Genera4ng 
coverage 
reports 
... integration 
... test 
Test .exec 
.exec .html 
jacoco 
TestReport 
jacocoIntegration 
T...
Commit 
stage: 
Code 
analysis 
Publish 
Binaries 
Commit stage 
Compile 
Unit Tests 
Integration 
Tests 
Code 
Analysis 
...
Sta4c 
code 
analysis 
tools 
Checkstyle 
FindBugs 
apply plugin: 'pmd' 
pmd { 
ignoreFailures = true 
} 
tasks.withType(P...
Measure 
quality 
over 
4me 
with 
Sonar 
Sonar 
database 
Gradle 
Sonar Runner 
root 
model 
repo. 
web 
analyzes 
publis...
Applying 
the 
Sonar 
Runner 
plugin 
apply plugin: "sonar-runner" 
! 
sonarRunner { 
sonarProperties { 
property "sonar.h...
Commit 
stage: 
Assemble 
distribu4on 
Exclude 
env. 
configura4on 
Include 
build 
informa4on 
Choose 
versioning 
strate...
Versioning 
strategy 
…the 
Maven 
way 
1.0-­‐SNAPSHOT 1.0 
during 
development when 
released 
Change 
version 
with 
Mav...
Versioning 
strategy 
…the 
Con4nuous 
Delivery 
way 
1.0.134 1.0.134 
during 
development when 
released 
1.0.134 
Projec...
Versioning 
strategy 
…implemented 
with 
Gradle 
versioning.gradle Contains 
allprojects { 
apply from: "$rootDir/gradle/...
Versioning 
strategy 
…implemented 
with 
Gradle 
ext.buildTimestamp = new Date().format('yyyy-MM-dd HH:mm:ss') 
version =...
Versioning 
strategy 
…implemented 
with 
Gradle 
ext.buildTimestamp = new Date().format('yyyy-MM-dd HH:mm:ss') 
version =...
Versioning 
strategy 
…implemented 
with 
Gradle 
ext.buildTimestamp = new Date().format('yyyy-MM-dd HH:mm:ss') 
version =...
Packaging 
the 
deployable 
ar4fact 
project(':web') { 
apply plugin: 'war' 
task createBuildInfoFile << { 
def buildInfoF...
Packaging 
the 
deployable 
ar4fact 
project(':web') { 
apply plugin: 'war' 
task createBuildInfoFile << { 
def buildInfoF...
Packaging 
the 
deployable 
ar4fact 
project(':web') { 
apply plugin: 'war' 
task createBuildInfoFile << { 
def buildInfoF...
Packaging 
the 
deployable 
ar4fact 
project(':web') { 
apply plugin: 'war' 
task createBuildInfoFile << { 
def buildInfoF...
Commit 
stage: 
Publish 
binaries 
Version 
ar4fact(s) 
Use 
binary 
repository 
Publish 
once, 
then 
reuse 
Publish 
Bin...
Publishing 
the 
deployable 
ar4fact 
1.0.34 
1.0.32 1.0.33 1.0.34
Defining 
build 
configura4on 
binaryRepository { 
url = 'http://mycompany.bin.repo:8081/artifactory' 
username = 'admin' ...
Defining 
build 
configura4on 
binaryRepository { 
url = 'http://mycompany.bin.repo:8081/artifactory' 
username = 'admin' ...
Defining 
build 
configura4on 
binaryRepository { 
url = 'http://mycompany.bin.repo:8081/artifactory' 
username = 'admin' ...
Defining 
build 
configura4on 
binaryRepository { 
url = 'http://mycompany.bin.repo:8081/artifactory' 
username = 'admin' ...
Reading 
build 
configura4on 
Initialization 
phase 
Conguration 
phase 
Execution 
phase 
def env = project.hasProperty(...
Reading 
build 
configura4on 
Initialization 
phase 
Conguration 
phase 
Execution 
phase 
def env = project.hasProperty(...
Reading 
build 
configura4on 
Initialization 
phase 
Conguration 
phase 
Execution 
phase 
def env = project.hasProperty(...
Using 
the 
Maven 
Publishing 
plugin 
apply plugin: 'maven-publish' 
def fullRepoUrl = "$config.binaryRepository.url/$con...
Using 
the 
Maven 
Publishing 
plugin 
apply plugin: 'maven-publish' 
def fullRepoUrl = "$config.binaryRepository.url/$con...
Using 
the 
Maven 
Publishing 
plugin 
apply plugin: 'maven-publish' 
def fullRepoUrl = "$config.binaryRepository.url/$con...
Using 
the 
Maven 
Publishing 
plugin 
apply plugin: 'maven-publish' 
def fullRepoUrl = "$config.binaryRepository.url/$con...
Acceptance 
stage: 
Retrieve 
binaries 
Request 
versioned 
ar4fact 
Store 
in 
temp. 
directory 
Acceptance stage 
Deploy...
Downloading 
the 
deployable 
ar4fact 
1.0.34 
1.0.32 1.0.33 1.0.34 
1.0.34 
Test 
UAT 
Prod
Task 
for 
downloading 
ar4fact 
repositories { 
maven { url fullRepoUrl } 
} 
! 
configurations { 
war 
} 
! 
dependencie...
Task 
for 
downloading 
ar4fact 
repositories { 
maven { url fullRepoUrl } 
} 
! 
configurations { 
war 
} 
! 
dependencie...
Acceptance 
stage: 
Deploy 
binaries 
Deployment 
on 
request 
Make 
it 
a 
reliable 
process 
Acceptance stage 
Deploy 
B...
Deploying 
to 
mul4ple 
environments 
Test 
–Penv=test 
UAT 
Prod 
–Penv=prod 
–Penv=uat
Deployment 
with 
the 
Cargo 
plugin 
cargoDeployRemote.dependsOn downloadBinaryArchive, cargoUndeployRemote 
cargoUndeplo...
Deployment 
with 
the 
Cargo 
plugin 
cargoDeployRemote.dependsOn downloadBinaryArchive, cargoUndeployRemote 
cargoUndeplo...
Deployment 
with 
the 
Cargo 
plugin 
cargoDeployRemote.dependsOn downloadBinaryArchive, cargoUndeployRemote 
cargoUndeplo...
Deployment 
with 
the 
Cargo 
plugin 
cargoDeployRemote.dependsOn downloadBinaryArchive, cargoUndeployRemote 
cargoUndeplo...
Deployment 
with 
the 
Cargo 
plugin 
cargoDeployRemote.dependsOn downloadBinaryArchive, cargoUndeployRemote 
cargoUndeplo...
Acceptance 
stage: 
Func4onal 
tests 
Test 
UI 
permuta4ons 
Test 
important 
use 
cases 
Acceptance stage 
Deploy 
Binari...
Execu4ng 
func4onal 
tests 
def functionalTestReportDir = file("$testReportDir/functional") 
def functionalTestResultsDir ...
Execu4ng 
func4onal 
tests 
def functionalTestReportDir = file("$testReportDir/functional") 
def functionalTestResultsDir ...
Execu4ng 
func4onal 
tests 
def functionalTestReportDir = file("$testReportDir/functional") 
def functionalTestResultsDir ...
Execu4ng 
func4onal 
tests 
def functionalTestReportDir = file("$testReportDir/functional") 
def functionalTestResultsDir ...
Let’s 
bring 
Jenkins 
into 
play! 
Test 
UAT 
Prod 
Deployment 
Acceptance 
Tests 
Publish 
Download 
Trigger 
Build 
Pul...
Model 
pipeline 
as 
series 
of 
jobs
Model 
pipeline 
as 
series 
of 
jobs 
Triggered 
job 
when 
change 
to 
SCM 
is 
detected
Ini4al 
Jenkins 
Build 
Job
Build 
Name 
Seker 
Plugin
JaCoCo 
Plugin
Parameterized 
Trigger 
Plugin
Gradle 
Plugin
Gradle 
Plugin 
Always 
use 
the 
Wrapper!
Gradle 
Plugin 
Always 
use 
the 
Wrapper! 
Run 
clean 
task 
to 
remove 
exis4ng 
ar4facts
Build 
Name 
Seker 
Plugin
Clearly 
iden4fy 
a 
build 
through 
an 
expressive 
build 
name 
Build 
Name 
Seker 
Plugin
Use 
Jenkins 
environment 
variable 
Clearly 
iden4fy 
a 
build 
through 
an 
expressive 
build 
name 
Build 
Name 
Seker ...
Parameterized 
Trigger 
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 
number 
parameter 
provided...
Clone 
Workspace 
SCM 
Plugin
Clone 
Workspace 
SCM 
Plugin 
Archive 
all 
files
Clone 
Workspace 
SCM 
Plugin 
Archive 
all 
files 
Only 
archive 
if 
build 
was 
successful
JaCoCo 
Plugin
JaCoCo 
Plugin 
Point 
to 
separated 
test 
results
JaCoCo 
Plugin 
Point 
to 
separated 
test 
results 
Point 
to 
JaCoCo 
files 
as 
well 
as 
source 
and 
class 
files
JaCoCo 
Plugin 
Point 
to 
separated 
test 
results 
Point 
to 
JaCoCo 
files 
as 
well 
as 
source 
and 
class 
files 
Fa...
Clone 
Workspace 
SCM 
Plugin Build 
Name 
Seker 
Plugin
Clone 
Workspace 
SCM 
Plugin Build 
Reuse 
ini4al 
workspace 
Name 
Seker 
Plugin
Clone 
Workspace 
SCM 
Plugin Build 
Reuse 
ini4al 
workspace 
Name 
Seker 
Plugin 
Reuse 
ini4al 
build 
number
Build 
Pipeline 
Plugin
Build 
Pipeline 
Plugin 
Define 
the 
target 
environment
Build 
Pipeline 
Plugin 
Define 
the 
target 
environment 
Downstream 
job 
that 
requires 
manual 
execu4on
Build 
Pipeline 
Plugin 
Visualiza4on 
of 
chained 
pipeline 
jobs
> gradle qa 
:askQuestions 
! 
BUILD SUCCESSFUL 
! 
Total time: 300 secs
Building a Continuous Delivery with Gradle and Jenkins
Building a Continuous Delivery with Gradle and Jenkins
Building a Continuous Delivery with Gradle and Jenkins
Building a Continuous Delivery with Gradle and Jenkins
Prochain SlideShare
Chargement dans…5
×

Building a Continuous Delivery with Gradle and Jenkins

28 432 vues

Publié le

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.

Publié dans : Logiciels
  • Hello! Get Your Professional Job-Winning Resume Here - Check our website! https://vk.cc/818RFv
       Répondre 
    Voulez-vous vraiment ?  Oui  Non
    Votre message apparaîtra ici

Building a Continuous Delivery with Gradle and Jenkins

  1. 1. Continuous Delivery with Gradle and Jenkins By Peter Niederwieser © 2014 SpringOne 2GX. All rights reserved. Do not distribute without permission.
  2. 2. Releases don’t have to be painful
  3. 3. Con4nuous Delivery Deliver so8ware 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 essen4al
  7. 7. #1 Every commit can result in a release #2 Automate everything! #3 Automated tests are essen4al #4 Done means released
  8. 8. Build pipeline Automated manifesta4on of delivery process
  9. 9. Build quality in! Establish automated quality gates
  10. 10. Compile/Unit Tests
  11. 11. Compile/Unit Tests Integra4on Tests
  12. 12. Compile/Unit Tests Integra4on Tests Code Analysis
  13. 13. Test Compile/Unit Tests Integra4on Tests Code Analysis Package/Deploy
  14. 14. Test Compile/Unit Tests Integra4on Tests Code Analysis Package/Deploy Acceptance Tests
  15. 15. Test Compile/Unit Tests Integra4on Tests Code Analysis Package/Deploy UAT Acceptance Tests
  16. 16. Test Compile/Unit Tests Integra4on Tests Code Analysis Package/Deploy UAT Prod Acceptance Tests
  17. 17. !
  18. 18. ! But how?
  19. 19. The “revolu4onary” sample applica4on Browser Internet Data To Do application store Writes Reads
  20. 20. Mul4-­‐project dependencies Model depend depend Web Repository depend
  21. 21. Project hierarchy todo gradle model wrapper xyz.gradle build.gradle repository web build.gradle build.gradle build.gradle settings.gradle gradlew gradlew.bat
  22. 22. Project hierarchy todo gradle model wrapper xyz.gradle build.gradle repository web build.gradle build.gradle build.gradle settings.gradle gradlew gradlew.bat
  23. 23. Project hierarchy todo gradle model wrapper xyz.gradle build.gradle repository web build.gradle build.gradle build.gradle settings.gradle gradlew gradlew.bat Define project-­‐specific behavior
  24. 24. Project hierarchy Defines which projects are taking part in the build settings.gradle include 'model' include 'repository' include 'web' todo gradle model wrapper xyz.gradle build.gradle repository web build.gradle build.gradle build.gradle settings.gradle gradlew gradlew.bat
  25. 25. Project hierarchy todo gradle model wrapper xyz.gradle build.gradle repository web build.gradle build.gradle build.gradle settings.gradle gradlew gradlew.bat Always use Wrapper to execute the build!
  26. 26. Project hierarchy todo gradle model wrapper xyz.gradle build.gradle repository web build.gradle 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 model wrapper xyz.gradle build.gradle repository web build.gradle build.gradle build.gradle settings.gradle gradlew gradlew.bat Externalize concerns into script plugins and organize them in dedicated directory Examples: ! ! Versioning strategy ! Integra4on and func4onal test setup ! Deployment func4onality ! ...
  28. 28. Project ar4facts root project model project repository project web project JAR JAR WAR
  29. 29. Project ar4facts root project model project repository project web project JAR JAR WAR Deployable ar4fact
  30. 30. Stages in build pipeline Functional tests Acceptance stage Publish Binaries Commit stage Deploy Binaries UAT Deploy Binaries Production Integration Tests Code Analysis Assemble Distribution Compile Unit Tests Retrieve Binaries Deploy Binaries Asserts that system works at a technical level
  31. 31. Stages in build pipeline Functional tests Acceptance stage Publish Binaries Commit stage Deploy Binaries UAT Deploy Binaries Production Integration Tests Code Analysis Assemble Distribution Compile Unit Tests Retrieve Binaries Deploy Binaries Asserts that system works on a func4onal/non-­‐func4onal level
  32. 32. Stages in build pipeline Functional tests Acceptance stage Publish Binaries Commit stage Deploy Binaries UAT Deploy Binaries Production Integration Tests Code Analysis Assemble Distribution Compile Unit Tests Retrieve Binaries Deploy Binaries TTrriiggggeerr mmaannuuaallllyy
  33. 33. Commit stage: Compile/unit tests Rapid feedback (< 5 mins) Run on every VCS check-­‐in Priority: fix broken build Publish Binaries Commit stage Compile Unit Tests Integration Tests Code Analysis Assemble Distribution
  34. 34. Commit stage: Integra4on tests Longer running tests Require environment setup Harder to maintain Publish Binaries Commit stage Compile Unit Tests Integration Tests Code Analysis Assemble Distribution
  35. 35. Separate tests in project layout todo model repository web src integTest java main java test java Produc4on Java sources Unit test Java sources
  36. 36. Separate tests in project layout todo model repository web src integTest java main java test java Integra4on test Java sources Produc4on Java sources Unit test Java sources
  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 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
  39. 39. 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 run4me classpath
  40. 40. 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 run4me classpath
  41. 41. 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 run4me classpath gradlew integrationTest
  42. 42. Database integra4on tests ... stop check build Database test start Database build Schema integration Test
  43. 43. Database integra4on tests ... stop check build Database test start Database build Schema integration Test
  44. 44. Database integra4on tests ... stop check build Database apply from: "$rootDir/gradle/databaseSetup.gradle" ! integrationTest.dependsOn startAndPrepareDatabase integrationTest.finalizedBy stopDatabase ! check.dependsOn integrationTest test start Database build Schema integration Test
  45. 45. Database integra4on tests ... stop check build Database apply from: "$rootDir/gradle/databaseSetup.gradle" ! integrationTest.dependsOn startAndPrepareDatabase integrationTest.finalizedBy stopDatabase ! check.dependsOn integrationTest test start Database build Schema integration Test Separate complex setup logic into script plugin
  46. 46. Database integra4on tests ... stop check build Database apply from: "$rootDir/gradle/databaseSetup.gradle" ! integrationTest.dependsOn startAndPrepareDatabase integrationTest.finalizedBy stopDatabase ! check.dependsOn integrationTest test start Database build Schema integration Test Separate complex setup logic into script plugin Integrate tasks into build lifecycle
  47. 47. Picking the “right” code coverage tool
  48. 48. Picking the “right” code coverage tool Cobertura Offline bytecode instrumenta4on
  49. 49. Picking the “right” code coverage tool Cobertura Offline bytecode instrumenta4on Offline bytecode instrumenta4on
  50. 50. Picking the “right” code coverage tool Cobertura Offline bytecode instrumenta4on Offline bytecode instrumenta4on Source code instrumenta4on
  51. 51. Picking the “right” code coverage tool Cobertura Offline bytecode instrumenta4on Offline bytecode instrumenta4on Source code instrumenta4on On-­‐the-­‐fly bytecode instrumenta4on
  52. 52. On-­‐the-­‐fly bytecode instrumenta4on No modifica4on 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 plugin: "jacoco" ! task jacocoIntegrationTestReport(type: JacocoReport) { sourceSets sourceSets.main executionData integTest } Apply JaCoCo plugin
  55. 55. Code coverage with JaCoCo apply plugin: "jacoco" ! task jacocoIntegrationTestReport(type: JacocoReport) { sourceSets sourceSets.main executionData integTest } Apply JaCoCo plugin Also report code coverage for integra4on tests
  56. 56. Genera4ng coverage reports ... integration ... test Test .exec
  57. 57. Genera4ng coverage reports ... integration ... test Test .exec .exec .html jacoco TestReport jacocoIntegration TestReport
  58. 58. Genera4ng coverage reports ... integration ... test Test .exec .exec .html jacoco TestReport jacocoIntegration TestReport .html
  59. 59. Commit stage: Code analysis Publish Binaries Commit stage Compile Unit Tests Integration Tests Code Analysis Assemble Distribution Perform code health check Fail build for low quality Record progress over 4me
  60. 60. Sta4c 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
  61. 61. Measure quality over 4me with Sonar Sonar database Gradle Sonar Runner root model repo. web analyzes publishes reads / writes Gradle project
  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 distribu4on Exclude env. configura4on Include build informa4on Choose versioning strategy Publish Binaries Commit stage Compile Unit Tests Integration Tests Code Analysis Assemble Distribution
  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 Con4nuous 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 versioning.gradle Contains allprojects { apply from: "$rootDir/gradle/versioning.gradle" } todo gradle model repository web build.gradle settings.gradle version implementa4on
  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 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
  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 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 representa4on
  70. 70. Packaging the deployable ar4fact 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 ar4fact 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 informa4on
  72. 72. Packaging the deployable ar4fact 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 informa4on Include build info file Into WAR distribu4on
  73. 73. Packaging the deployable ar4fact 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 informa4on Include build info file Into WAR distribu4on gradlew assemble
  74. 74. Commit stage: Publish binaries Version ar4fact(s) Use binary repository Publish once, then reuse Publish Binaries Commit stage Compile Unit Tests Integration Tests Code Analysis Assemble Distribution
  75. 75. Publishing the deployable ar4fact 1.0.34 1.0.32 1.0.33 1.0.34
  76. 76. Defining build configura4on 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 configura4on 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' } } ... Common configura4on
  78. 78. Defining build configura4on 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' } } ... Common configura4on Environment-­‐specific configura4on
  79. 79. Defining build configura4on 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' } } ... Common configura4on Environment-­‐specific configura4on Read creden4als from gradle.proper4es Read creden4als from gradle.proper4es Read creden4als from gradle.proper4es
  80. 80. Reading build configura4on 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()) ext.config = parsedConfig
  81. 81. Reading build configura4on 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()) ext.config = parsedConfig Assign configura4on to extra property
  82. 82. Reading build configura4on 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()) ext.config = parsedConfig extra property gradlew –Penv=uat ... Assign configura4on to
  83. 83. Using the Maven Publishing plugin apply plugin: 'maven-publish' def 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 apply plugin: 'maven-publish' def fullRepoUrl = "$config.binaryRepository.url/$config.binaryRepository.name” publishing { publications { webApp(MavenPublication) { from components.web } } repositories { maven { url fullRepoUrl credentials { username = config.binaryRepository.username password = config.binaryRepository.password } } } } Build repository URL from configura4on
  85. 85. Using the Maven Publishing plugin apply plugin: 'maven-publish' def fullRepoUrl = "$config.binaryRepository.url/$config.binaryRepository.name” publishing { publications { webApp(MavenPublication) { from components.web } } repositories { maven { url fullRepoUrl credentials { username = config.binaryRepository.username password = config.binaryRepository.password } } } } Build repository URL from configura4on Assign publica4on name and component
  86. 86. Using the Maven Publishing plugin apply plugin: 'maven-publish' def fullRepoUrl = "$config.binaryRepository.url/$config.binaryRepository.name” publishing { publications { webApp(MavenPublication) { from components.web } } repositories { maven { url fullRepoUrl credentials { Assign publica4on name and component username = config.binaryRepository.username password = config.binaryRepository.password } } } } Build repository URL from configura4on gradlew publish
  87. 87. Acceptance stage: Retrieve binaries Request versioned ar4fact Store in temp. directory Acceptance stage Deploy Binaries Functional Tests Retrieve Binaries
  88. 88. Downloading the deployable ar4fact 1.0.34 1.0.32 1.0.33 1.0.34 1.0.34 Test UAT Prod
  89. 89. Task for downloading ar4fact 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 ar4fact 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 loca4on for downloaded ar4fact gradlew downloadBinaryArchive
  91. 91. Acceptance stage: Deploy binaries Deployment on request Make it a reliable process Acceptance stage Deploy Binaries Functional Tests Retrieve Binaries Use process for all envs
  92. 92. Deploying to mul4ple environments Test –Penv=test UAT Prod –Penv=prod –Penv=uat
  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 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 ar4fact from binary repository and undeploy exis4ng one
  95. 95. 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 ar4fact from binary repository and undeploy exis4ng one Only undeploy if URL context exists
  96. 96. 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 ar4fact from binary repository and undeploy exis4ng one Only undeploy if URL context exists Use environment-­‐specific configura4on
  97. 97. 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 ar4fact from binary repository and undeploy exis4ng one Only undeploy if URL context exists Use environment-­‐specific configura4on gradlew –Penv=uat cargoDeploy
  98. 98. Acceptance stage: Func4onal tests Test UI permuta4ons Test important use cases Acceptance stage Deploy Binaries Functional Tests Retrieve Binaries Run against different envs
  99. 99. Execu4ng func4onal tests def functionalTestReportDir = file("$testReportDir/functional") def functionalTestResultsDir = file("$testResultsDir/functional") def functionalCommonSystemProperties = ['geb.env': 'firefox', 'geb.build.reportsDir': reporting.file("$name/geb")] ! task functionalTest(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/" }
  100. 100. Execu4ng func4onal tests def functionalTestReportDir = file("$testReportDir/functional") def functionalTestResultsDir = file("$testResultsDir/functional") def functionalCommonSystemProperties = ['geb.env': 'firefox', 'geb.build.reportsDir': reporting.file("$name/geb")] ! task functionalTest(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 proper4es
  101. 101. Execu4ng func4onal tests def functionalTestReportDir = file("$testReportDir/functional") def functionalTestResultsDir = file("$testResultsDir/functional") def functionalCommonSystemProperties = ['geb.env': 'firefox', 'geb.build.reportsDir': reporting.file("$name/geb")] ! task functionalTest(type: Test) { Reuse setup proper4es 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. configura4on
  102. 102. Execu4ng func4onal tests def functionalTestReportDir = file("$testReportDir/functional") def functionalTestResultsDir = file("$testResultsDir/functional") def functionalCommonSystemProperties = ['geb.env': 'firefox', 'geb.build.reportsDir': reporting.file("$name/geb")] ! task functionalTest(type: Test) { Reuse setup proper4es 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. configura4on gradlew –Penv=test remoteFunctionalTest
  103. 103. Let’s bring Jenkins into play! Test UAT Prod Deployment Acceptance Tests Publish Download Trigger Build Pull Source Code
  104. 104. Model pipeline as series of jobs
  105. 105. Model pipeline as series of jobs Triggered job when change to SCM is detected
  106. 106. Ini4al Jenkins Build Job
  107. 107. Build Name Seker Plugin
  108. 108. JaCoCo Plugin
  109. 109. Parameterized Trigger Plugin
  110. 110. Gradle Plugin
  111. 111. Gradle Plugin Always use the Wrapper!
  112. 112. Gradle Plugin Always use the Wrapper! Run clean task to remove exis4ng ar4facts
  113. 113. Build Name Seker Plugin
  114. 114. Clearly iden4fy a build through an expressive build name Build Name Seker Plugin
  115. 115. Use Jenkins environment variable Clearly iden4fy a build through an expressive build name Build Name Seker Plugin
  116. 116. Parameterized Trigger Plugin
  117. 117. Parameterized Trigger Plugin Next job to trigger if build is stable
  118. 118. Parameterized Trigger Plugin Next job to trigger if build is stable Defines build number parameter provided to subsequent jobs
  119. 119. Clone Workspace SCM Plugin
  120. 120. Clone Workspace SCM Plugin Archive all files
  121. 121. Clone Workspace SCM Plugin Archive all files Only archive if build was successful
  122. 122. JaCoCo Plugin
  123. 123. JaCoCo Plugin Point to separated test results
  124. 124. JaCoCo Plugin Point to separated test results Point to JaCoCo files as well as source and class files
  125. 125. 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
  126. 126. Clone Workspace SCM Plugin Build Name Seker Plugin
  127. 127. Clone Workspace SCM Plugin Build Reuse ini4al workspace Name Seker Plugin
  128. 128. Clone Workspace SCM Plugin Build Reuse ini4al workspace Name Seker Plugin Reuse ini4al build number
  129. 129. Build Pipeline Plugin
  130. 130. Build Pipeline Plugin Define the target environment
  131. 131. Build Pipeline Plugin Define the target environment Downstream job that requires manual execu4on
  132. 132. Build Pipeline Plugin Visualiza4on of chained pipeline jobs
  133. 133. > gradle qa :askQuestions ! BUILD SUCCESSFUL ! Total time: 300 secs

×