CI/CD as code
at drivetime
A look into how and why we do builds at DriveTime
using DSL plugin and a custom build framework
What is CI/CD?
Summary:
- Code goes into SCM
- ???
- Applications run in prod
Specifics:
- Builds
- Tests (Unit, Integration, e2e)
- Reporting
- Deployments
- Preferably all automated (tiny robots)
Problems associated
with CI/CD
- Things that should be easy are hard
- No consistencies
- No disaster recovery
- No way to rollback
- No way to share work
- No way to test work
- No way to migrate
- Nice-to-have's are rarely done
CI/CD as code fixes all this
- Things that should be easy are hard
Changing from tfs to github can be 1 line of code
- No consistencies,
No way to share work
Reusable base classes, interfaces, methods and frameworks solve this
- No disaster recovery,
No way to rollback
SCM does this for free!
CI/CD as code fixes all this
We unit test code all the time
Migrating code can be done quickly: abstractions, testing, and code re-use enables this
- Nice-to-have's are rarely done
Do it once in a method or base class and
now everyone can use it regardless of how difficult it once was
How do you get CI/CD as code?
First a little bit about how Jenkins works
Next, Jenkins DSL plugin! It's all groovy, man
How does the DSL plugin work?
Jenkins job: Seed build
Comes from the DSL plugin, it runs groovy code that creates:
- Folders
- Views
- Jobs
- Other seed builds
It can be confusing
Groovy code runs from your seed job
That code creates jobs by generating XML
There isn't any code running when your builds run
Misc things that DSL does
From your seed build
From your generated build
Additional flexibility
You can ignore, disable or delete removed items
and we typically disable
This doesn't look very reusable
job("Your folder/example build name") {
scm {
git {
remote {
github repo
refspec '+refs/pull/*:refs/remotes/origin/pr/*'
}
branch '${sha1}'
} }
triggers {
githubPullRequest {
admin 'sheehan'
triggerPhrase 'OK to test'
onlyTriggerPhrase true
} }
steps {
grails {
useWrapper true
targets(['test-app', 'war'])
} } }
That's because it's not
This is all code, we can do whatever we want
new SeedBuildDefinition(
jobName: 'OnlineAutoSales Seed',
scmConnector: new GitHubConnector(repoName: 'Builds.OnlineAutoSales'),
groovyFileToRun: 'jobs.groovy'
),
new SeedBuildDefinition(
jobName: 'DevOps Seed',
scmConnector: new GitHubConnector(repoName: 'Builds.DevOps'),
groovyFileToRun: 'devops_seed.groovy'
),
new SeedBuildDefinition(
jobName: 'BPM Seed',
scmConnector: new GitHubConnector(repoName: 'Builds.BPM'),
groovyFileToRun: 'jobs.groovy'
)
Different ways of doing stuff
Generic
new NpmPackagePublisher(
jobName: 'UX/UI.Components',
descriptionText: readFileFromWorkspace('dt-components/description.html'),
scmConnector: new GitHubConnector(
repoName: 'Ui.Components',
buildPullRequests: true),
failedBuildEmailAddresses: AllYourEmailsInAVariable,
azurePublishProfiles: Arrays.asList(
'dt-components/drivetime-components-dev.PublishSettings')
),
Specific
new FinancingBuild(
jobName: rootLocation + '/Financing/Financing-Main'
)
Some additional generic stuff
Anything else I should know?
But it will get better with time!
And we'll add tests!