continuous delivery with gradle
TRANSCRIPT
CONTINUOUS DELIVERYWITH GRADLE
/ / BOB PAULIN @BOBPAULIN [email protected]
ABOUT MEIndependent ConsultantBusiness EnablementWeb Centric PlatformsAutomation
I LOVE DELIVERING SOFTWARE
I WANT TO DELIVER SOFTWARE ALL THE TIME!
MULTIPLE TIMES A DAY!
WAIT PEOPLE ACTUALLY DO THAT?
SEVERAL MAJOR ECOMMERCE SHOPSETSY.COM DEPLOYS ABOUT 30 TIMES EACH DAY
MY PERSONAL RECORD 5
BENEFITS OF CONTINUOUS DELIVERYShorten the time it takes from conception to customer impactExperiment more frequentlyDo more concurrentlyDelivery becomes a non-event
DELIVERING SOFTWARE CAN BE PAINFUL!Needs to be AssembledNeeds to be DeployedNeeds to be TestedHow can I make sure I don't break existing stuff?What happens if I do break something?What about dependent systems?How do I know what changed?How do I control what goes in and what does not?
ALL THAT'S GOING TO TAKE ME AT LEAST 2WEEKS!
RELEASE MANAGEMENT 101Consistent Build ProcessFunctional/Regression TestingVersioningSource Code ManagementDeployment ProcessRollback
RELEASE MANAGEMENT 501Config SwitchesFeature BranchingModular DeploymentThrottlingAuto ProvisioningAuto-Scaling
TOOLS TO THE RESCUE!
BUILD TOOLS
Dependency ManagementPackagingPlugin ArchitectureLifecycle ManagementVersioning
A RECIPE FOR CONTINUOUS DELIVERYGradleArtifactoryGit
GRADLEBuild ToolGroovyCombines some of the best features of Ant and MavenConventionsPlugins
ARTIFACTORYBinary RepositoryJEE Web AppSupports Maven and IvyOpen Source and Pro Licensed
GITSource Control ManagementDistributedTagging/Branch/Merge
HOW DOES ALL THIS STUFF WORKTOGETHER?
STEPS
SYSTEM INTERACTION
A BRIEF ASIDE ON SEMANTIC VERSIONING
LETS LOOK AT SOME CODE!
GRADLE PLUGINSWar PluginCargo PluginMaven Plugin
GRADLE CONFIGURATIONSCONFIG + CODE!
cargo { containerId = 'tomcat7x' port = Integer.parseInt(getProperty(project.env + ".deployPort"))
deployable { context = artifactId file = project.file(artifactDownloadPath) } remote { hostname = getProperty(project.env + ".deployHostname") username = getProperty(project.env + ".deployUserName") password = getProperty(project.env + ".deployPassword") } }
DON'T EMBED ENVIRONMENT DATA INTO THEBUILD SCRIPT!
CREATE A USER PROPERTIES FILE INSTEAD.
DEPENDENCY MANAGEMENTFOR YOUR APPLICATION AND FOR YOUR BUILD
buildscript { repositories { maven { url "https://oss.sonatype.org/content/groups/public"} mavenCentral() }
dependencies { classpath "javax.servlet:servlet-api:2.5", "org.gradle.api.plugins:gradle-cargo-plugin:0.6.1", "org.ajoberstar:gradle-git:0.6.3" }}
dependencies { def cargoVersion = '1.3.3' def springVersion = '3.2.3.RELEASE' def wro4JVersion = '1.6.3' def aopAllainceVersion = '1.0' cargo "org.codehaus.cargo:cargo-core-uberjar:$cargoVersion", "org.codehaus.cargo:cargo-ant: compile "org.springframework:spring-aop:$springVersion", "org.springframework:spring-beans: "org.springframework:spring-context:$springVersion","org.springframework:spring-context-support: "org.springframework:spring-expression:$springVersion", "org.springframework:spring-tx: "org.springframework:spring-web:$springVersion", "org.springframework:spring-webmvc: "javax.servlet:jstl:1.2","org.slf4j:slf4j-api:1.5.6", "org.slf4j:jcl-over-slf4j:1.5.6" "org.codehaus.jackson:jackson-mapper-asl:1.9.3","aopalliance:aopalliance:$aopAllainceVersion compile("org.springframework.data:spring-data-mongodb:1.0.4.RELEASE"){ exclude module: "slf4j-api" } compile("org.springframework:spring-core:$springVersion"){ exclude module: 'commons-logging' } compile ("ro.isdc.wro4j:wro4j-core:$wro4JVersion") { exclude module: "slf4j-api" } compile("ro.isdc.wro4j:wro4j-extensions:$wro4JVersion") { exclude module: "slf4j-api" exclude module: "slf4j-log4j12" } providedCompile "javax.el:el-api:1.0", "javax.servlet.jsp:jsp-api:2.0", "javax.servlet:servlet-api:2.5" testCompile "junit:junit:3.8.1", "org.springframework:spring-test:$springVersion
}
GRADLE TASKS
task updateUploadedPom(dependsOn: 'getBranchName') << { def pomVersion = project.version if(!project.getGradle().getTaskGraph().hasTask(":release")) pomVersion += ".$branchName" uploadArchives.repositories.mavenDeployer.pom.version = pomVersion}
task tag(type: GitTag) { tagName = version message = "Release of ${version}"}
task pushToRemote(type: GitPush){ pushAll = true}
task qaRelease { dependsOn test, war, getBranchName, updateUploadedPom, uploadArchives, updateVersionNumber, addVersion}
GRADLE TASK DEPENDENCIES
//Task Dependencieswar.mustRunAfter testtag.mustRunAfter warupdateUploadedPom.mustRunAfter taguploadArchives.mustRunAfter updateUploadedPomupdateVersionNumber.mustRunAfter uploadArchivesaddVersionUpdate.mustRunAfter updateVersionNumbercommitVersionUpdate.mustRunAfter addVersionUpdatepushToRemote.mustRunAfter commitVersionUpdate
DEMO TIME!A SIMPLE BOOK REVIEW WEBSITE
ADDING BOOK RATINGS SCORE TO THE SITE
CREATING A FEATURE BRANCH
WRITE THE CODE... WE ALREADY KNOW HOWTO DO THIS!
BUILD PROCESSWhen you say you're code is done the fun is just beginning...
TESTING PROCESSAre you sure you're done?
RELEASE PROCESSYup we're done!
DEPLOYMENT PROCESSOh yeah now we're done
AN ASIDEWhy is deploy separate from release?
DEPLOYMENT PROCESS: TAKE 2Oh ****! What have we done?!?!
KNOW YOUR ROLLBACK STRATEGY BEFOREEVERY DEPLOYMENT
ENTERPRISE ARCHITECTURE
SOME PARTING THOUGHTS ON GRADLE ANDCONTINOUS DELIVERY
REFERENCESCODE AND PRESENTATION
GRADLE
GRADLE RELEASE PLUGIN
GIT
ARTIFACTORY
CARGO
SEMANTIC VERSIONING
ETSY'S MIK E BRITTAIN "CONTINUOUS DELIVERY: THE DIRTY DETAILS"
BOB PAULIN / / BOB PAULIN @BOBPAULIN [email protected]