96
Real-World Strategies for Continuous Delivery with Maven and Jenkins John Ferguson Smart

Continuous delivery-with-maven

Embed Size (px)

Citation preview

Real-World Strategies for Continuous Delivery with Maven and Jenkins

John Ferguson Smart

John Ferguson Smart

ConsultantTrainerMentorAuthorSpeakerCoder

3

Getting value to the business.Fast.

Continuous Delivery

But what is Continuous Delivery?

A build tool?

A CI tool?

Automated tests?

Automated quality?

Automated deployment?

Yes...and No

Continuous Delivery is an attitude

Deliver business value more frequently

Value can also come in the form of fast feedback

Give power back to the business

The ‘eternal beta’ strategy

Principles of Continuous Delivery

Every build is a potential release

Principle #1

Eliminate manual bottlenecks

Principle #2

Automate wherever possible

Principle #3

Have automated tests you can trust

Principle #4

The conveyer belts of Continuous Delivery

Build Pipelines

Compile and unit test

DEV

Package and deploy

QA

PROD

Acceptance tests

Deploy to QA

Deploy to PROD

Compile and unit test

DEV

Package and deploy

QA

PROD

Acceptance tests

Deploy to QA

Deploy to PROD

Each commit is a potential release

Compile and unit test

DEV

Package and deploy

QA

PROD

Acceptance tests

Deploy to QA

Deploy to PROD

We reuse the same binaries

Compile and unit test

DEV

Package and deploy

QA

PROD

Acceptance tests

Deploy to QA

Deploy to PROD

Automated tests give us confidence in our build

Acceptance tests

Compile and unit test

Package and deploy

Acceptance tests

Deploy to QA

Deploy to PROD

Automated, repeatable deployments

DEV

QA

PROD

Automated, repeatable deployments

Automated, repeatable deployments

Continuous Delivery with Maven

Why does it have to be so hard?!

compile

test-compile

test

package

integration-test

verify

install

deploy

The Maven lifecycle

The Maven lifecycle

Compile and unit test mvn test compiletest-compile

test

Integration tests mvn verify compiletest-compile

testpackage

integration-testverify

The Maven lifecycle

Compile and unit test mvn test compiletest-compiletest

Integration tests mvn verify compiletest-compiletestpackageintegration-testverify

Deploy to repository mvn deploy compiletest-compile

testpackage

integration-testverifydeploy

The Maven Release Process

1.0.0-SNAPSHOT

1.0.0-SNAPSHOT

1.0.0

mvn release

The Maven Release Process

mvn release:prepare

• Check that there are no uncommitted changes in the sources• Check that there are no SNAPSHOT dependencies• Change the version in the POMs from x-SNAPSHOT to a new version (you

will be prompted for the versions to use)• Transform the SCM information in the POM to include the final destination

of the tag• Run the project tests against the modified POMs to confirm everything is in

working order• Commit the modified POMs• Tag the code in the SCM with a version name (this will be prompted for)• Bump the version in the POMs to a new value y-SNAPSHOT (these values

will also be prompted for)• Commit the modified POMs

The Maven Release Process

mvn release:prepare

compiletest-compile

testpackage

integration-testverify

1.0.0-SNAPSHOT

1.0.0

1.0.1-SNAPSHOT

1.0.1-SNAPSHOT

The Maven Release Process

mvn release:perform

1.0.0

compiletest-compile

testpackage

integration-testverifyinstalldeploy

1.0.0

Maven: You build a new release when a snapshot build is good enough

Continuous Delivery: Any build is a potential release

The Maven Release Process

Make Maven work for you

Options?

Build and unit test

mvn test

Code coverage...

mvn site

Integration tests

mvn verify

Beware duplication!

Minimize build jobs for the main build

Default build

mvn installcompile

test

integration-testverifyinstall

           <plugin>                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-surefire-plugin</artifactId>                <version>2.11</version>                <configuration>                    <skip>true</skip>                </configuration>                <executions>                    <execution>                        <id>unit-tests</id>                        <phase>test</phase>                        <goals>                            <goal>test</goal>                        </goals>                        <configuration>                            <skip>false</skip>                            <includes>                                <include>**/When*.*</include>                            </includes>                            <excludes>                                <exclude>%regex[.*integration.*When.*]</exclude>                            </excludes>                        </configuration>                    </execution>                    <execution>                        <id>integration-tests</id>                        <phase>integration-test</phase>                        <goals>                            <goal>test</goal>                        </goals>                        <configuration>                            <skip>false</skip>                            <includes>                                <include>%regex[.*integration.*]</include>                            </includes>                        </configuration>                    </execution>                </executions>            </plugin>

Split unit and integration tests

Using package conventions

           <plugin>                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-surefire-plugin</artifactId>                <version>2.11</version>                <configuration>                    <skip>true</skip>                </configuration>                <executions>                    <execution>                        <id>unit-tests</id>                        <phase>test</phase>                        <goals>                            <goal>test</goal>                        </goals>                        <configuration>                            <skip>false</skip>                            <includes>                                <include>**/When*.*</include>                            </includes>                            <excludes>                                <exclude>%regex[.*integration.*When.*]</exclude>                            </excludes>                        </configuration>                    </execution>                    <execution>                        <id>integration-tests</id>                        <phase>integration-test</phase>                        <goals>                            <goal>test</goal>                        </goals>                        <configuration>                            <skip>false</skip>                            <includes>                                <include>%regex[.*integration.*]</include>                            </includes>                        </configuration>                    </execution>                </executions>            </plugin>

Split unit and integration tests

Using package conventions

                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-surefire-plugin</artifactId>                <version>2.11</version>                <configuration>                    <skip>true</skip>                </configuration>

           <plugin>                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-surefire-plugin</artifactId>                <version>2.11</version>                <configuration>                    <skip>true</skip>                </configuration>                <executions>                    <execution>                        <id>unit-tests</id>                        <phase>test</phase>                        <goals>                            <goal>test</goal>                        </goals>                        <configuration>                            <skip>false</skip>                            <includes>                                <include>**/When*.*</include>                            </includes>                            <excludes>                                <exclude>%regex[.*integration.*When.*]</exclude>                            </excludes>                        </configuration>                    </execution>                    <execution>                        <id>integration-tests</id>                        <phase>integration-test</phase>                        <goals>                            <goal>test</goal>                        </goals>                        <configuration>                            <skip>false</skip>                            <includes>                                <include>%regex[.*integration.*]</include>                            </includes>                        </configuration>                    </execution>                </executions>            </plugin>

Split unit and integration tests

Using package conventions

                    <execution>                        <id>unit-tests</id>                        <phase>test</phase>                        <goals>                            <goal>test</goal>                        </goals>                        <configuration>                            <skip>false</skip>                            <includes>                                <include>**/When*.*</include>                            </includes>                            <excludes>                                <exclude>%regex[.*integration.*When.*]</exclude>                            </excludes>                        </configuration>                    </execution>

Excluding integration tests from the test phase

           <plugin>                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-surefire-plugin</artifactId>                <version>2.11</version>                <configuration>                    <skip>true</skip>                </configuration>                <executions>                    <execution>                        <id>unit-tests</id>                        <phase>test</phase>                        <goals>                            <goal>test</goal>                        </goals>                        <configuration>                            <skip>false</skip>                            <includes>                                <include>**/When*.*</include>                            </includes>                            <excludes>                                <exclude>%regex[.*integration.*When.*]</exclude>                            </excludes>                        </configuration>                    </execution>                    <execution>                        <id>integration-tests</id>                        <phase>integration-test</phase>                        <goals>                            <goal>test</goal>                        </goals>                        <configuration>                            <skip>false</skip>                            <includes>                                <include>%regex[.*integration.*]</include>                            </includes>                        </configuration>                    </execution>                </executions>            </plugin>

Split unit and integration tests

                    <execution>                        <id>integration-tests</id>                        <phase>integration-test</phase>                        <goals>                            <goal>test</goal>                        </goals>                        <configuration>                            <skip>false</skip>                            <includes>                                <include>%regex[.*integration.*]</include>                            </includes>                        </configuration>                    </execution>

Using package conventions

Run only integration tests in the integration-test phase

Default build

mvn deploy

Compile

Unit Test

Integration Test

Package

Default build

mvn install

Compile

Unit Test

Integration Test

Package

Jenkins Artifactory Plugin

Default build

mvn install

Compile

Unit Test

mvn deploy:deploy

Integration Test

Package

Eliminate waste

Run code coverage with the integration tests

Integration tests mvn verify compiletest-compiletestpackageintegration-testverify

packageintegration-test

Test coverage mvn cobertura

compiletest-compiletestcobertura

Duplication!

Creates its own instrumented JAR files

JACOCOJava Code Coverage

Code coverage on the fly

Doesn’t mess with your JAR files

Run code coverage with the integration tests

<plugin>    <groupId>org.jacoco</groupId>    <artifactId>jacoco-maven-plugin</artifactId>    <version>0.5.10.201208310627</version>    <executions>        <execution>            <id>jacoco-initialize</id>            <goals>                <goal>prepare-agent</goal>            </goals>        </execution>        <execution>            <id>jacoco-site</id>            <phase>post-integration-test</phase>            <goals>                <goal>report</goal>            </goals>        </execution>    </executions></plugin>

Measure code coverage after the integration tests

           <plugin>                <groupId>org.jacoco</groupId>                <artifactId>jacoco-­‐maven-­‐plugin</artifactId>                <executions>                    <execution>                        <goals>                            <goal>prepare-­‐agent</goal>                        </goals>                    </execution>                    <execution>                        <id>check</id>                        <goals>                            <goal>check</goal>                        </goals>                        <phase>post-­‐integration-­‐test</phase>                        <configuration>                            <check>                                <classRate>95</classRate>                                <instructionRate>95</instructionRate>                                <methodRate>90</methodRate>                                <branchRate>95</branchRate>                                <complexityRate>95</complexityRate>                                <lineRate>95</lineRate>                            </check>                        </configuration>                    </execution>                </executions>            </plugin>

Run code coverage with the integration tests

Define minimum coverage

Run code coverage with the integration tests

Integration tests/test coverage

mvn install compiletest-compiletestpackageintegration-testverify

Coverage checks and report

Uninstrumented JAR

Minimal overhead

Normal build With JaCoCo

Build

Tim

e

Reuse binaries wherever possible

Default build

1.0.203

1.0.201

1.0.202

1.0.203

Deploy to TEST

Acceptance Tests

Deploy to UAT

Have a separate acceptance test suite

Acceptance Tests

TEST

UAT

PROD

Take code quality seriously

Checkstyle

Findbugs

Code quality metrics

mvn site

Take code quality seriously

Code quality metrics

Take code quality seriously

mvn pmd:check

mvn findbugs:check

mvn checkstye:checkstyle

Pick and choose your metrics

Fail the build if there are violations

Don’t sweat the version numbers

Avoid the Maven Release Plugin

1.0-SNAPSHOT

Developers always work on a SNAPSHOT version

This number is pretty arbitrary

1.0-SNAPSHOT

1.0.203

This number is a build counter

Jenkins sets a release candidate version at the start of the build pipeline

1.0.203

This version goes through the build pipeline

Unit tests

Acceptance tests

Code quality metrics

Deploy to TEST

1.0.203

This version goes through the build pipeline

Unit testsAcceptance testsCode quality metricsDeploy to TESTAcceptance Tests

1.0.2041.0.205

1.0.203

1.0.204

1.0.205

Create a new release branch

Update the version number

Build and test release candidate

Did it work?

Push release branch

Deploy release candidate

Yes

Drop release branch

No

Notify everyone

Create a new release branch

Update the version number

Build and test release candidate

Push release branch

Did it work?

Drop release branch

Deploy release candidate

No Yes

Notify everyone

1.0.203

Create a new release branch

Update the version number

Build and test release candidate

Push release branch

Did it work?

Drop release branch

Deploy release candidate

No Yes

Notify everyone

mvn versions:set -DnewVersion=1.0.203

Versions Plugin

Create a new release branch

Update the version number

Build and test release candidate

Push release branch

Did it work?

Drop release branch

Deploy release candidate

No Yes

Notify everyone

mvn verify ...

And make it visible

The Build Pipeline plugin

Let’s see it in action!

Jenkins

TESTUAT PROD

Let’s see it in action!

initial-build

code-quality

deploy-to-test

acceptance-tests

deploy-to-uat

deploy-to-prod

acceptance-tests

initial-build

code-quality

deploy-to-test

acceptance-tests

deploy-to-uat

deploy-to-prod

acceptance-tests

initial-build

Release candidate numberLinks to binary artifacts in Artifactory

initial-build

Links to binary artifacts in Artifactory

Users can override the default major version

initial-build

Use a parameterized build to configure the major build version

Update the version in the pom files

Build the binaries

initial-build

Create a new branch

Commit the changes and push the branch to the repository

If the build succeeds...

initial-build

Conditional build step plugin

If the build fails...

Delete the branch

initial-build

initial-build

Deploy the release candidate to Artifactory

Target platform

initial-build

Deploy to the test environment

initial-build

<plugin> <groupId>com.jelastic</groupId> <artifactId>jelastic-maven-plugin</artifactId> <version>1.6</version> <configuration> <email>${jelastic.username}</email> <password>${jelastic.password}</password> <context>${jelastic.context}</context> <environment>${jelastic.environment}</environment> <api_hoster>app.jelastic.servint.net</api_hoster> </configuration></plugin>

initial-build

Archive binaryUpdate the build description

initial-build

Archive binary

Pass the release branch to the next build job

initial-build

Don’t start a new build pipeline if one is currently running.

initial-build

code-quality

deploy-to-test

acceptance-tests

deploy-to-uat

deploy-to-prod

acceptance-tests

initial-build

code-quality

deploy-to-test

acceptance-tests

deploy-to-uat

deploy-to-prod

acceptance-tests

Pass in the release we want to build against

initial-build

code-quality

deploy-to-test

acceptance-tests

deploy-to-uat

deploy-to-prod

acceptance-tests

Build from the release candidate branch

initial-build

code-quality

deploy-to-test

acceptance-tests

deploy-to-uat

deploy-to-prod

acceptance-tests

initial-build

code-quality

deploy-to-test

acceptance-tests

deploy-to-uat

deploy-to-prod

acceptance-tests

Copy the WAR file from the initial build

initial-build

code-quality

deploy-to-test

acceptance-tests

deploy-to-uat

deploy-to-prod

acceptance-tests...deploy this WAR file

initial-build

code-quality

deploy-to-test

acceptance-tests

deploy-to-uat

deploy-to-prod

acceptance-tests

...and run the acceptance tests

initial-build

code-quality

deploy-to-test

acceptance-tests

deploy-to-uat

deploy-to-prod

acceptance-tests

What platform do we run the acceptance test against

initial-build

code-quality

deploy-to-test

acceptance-tests

deploy-to-uat

deploy-to-prod

acceptance-tests

What platform do we run the acceptance test against

initial-build

code-quality

deploy-to-test

acceptance-tests

deploy-to-uat

deploy-to-prod

acceptance-tests

initial-build

code-quality

deploy-to-test

acceptance-tests

deploy-to-uat

deploy-to-prod

acceptance-tests

What platform do we run the acceptance test against

initial-build

code-quality

deploy-to-test

acceptance-tests

deploy-to-uat

deploy-to-prod

acceptance-tests

What platform do we run the acceptance test against

initial-build

code-quality

deploy-to-test

acceptance-tests

deploy-to-uat

deploy-to-prod

acceptance-testsBuild promotion status

initial-build

code-quality

deploy-to-test

acceptance-tests

deploy-to-uat

deploy-to-prod

acceptance-tests

Copy the WAR file from the initial build...

...and deploy it to UAT

initial-build

code-quality

deploy-to-test

acceptance-tests

deploy-to-uat

deploy-to-prod

acceptance-tests

A build promotion process to deploy to production

initial-build

code-quality

deploy-to-test

acceptance-tests

deploy-to-uat

deploy-to-prod

acceptance-tests

A build promotion process to deploy to production

Build pipeline for an overview of the process

John  Ferguson  SmartEmail:  [email protected]:  hHp://www.wakaleo.com

TwiHer:  wakaleo

Thank You