Build Definitions As Code
What is a build definition as code?
The concept of a build definition as code as it relates to a build server for continuous integration means using an actual declarative language to define how your build should works instead of using a web user interface to define your build steps. You can use a general purpose programming language, a domain specific language or some other declarative construct to indicate how your build your system.
The beauty of this approach is that it is just text. No complicated UI to learn, no hundreds of clicks just to get a build to run. You type in the commands in a text file, commit that file with the rest of your application and the build server has access to it for executing your build process.
We layer on the things required to get our CI build working. The Continuous Integration Process is your selected vendor where you then use their UI to create your Source Code Build Configuration which when ran will bring in your Source Code assets to ultimately create Software Artifacts.
Why Define Builds As Code?
To a certain degree we have had this ability for years in the from of a local build scripts. I've long been a proponent of using your build script tasks verbatim on your developer machines as well as in your continuous integration environment. This was probably a 80:20 ratio of actually working in practice. There were always a number of task processes, server specific implementations or configurations that you had to buy into. Many times it didn't make sense to wrap these steps into your task runners. It was just something that had to be done. I think that with the pervasiveness of cloud solutions that that high of a ratio is even lower as it just isn't as practical to keep them the same.
You might have a few reason that you might want to keep your build configuration with you code:
- versioned build process along side your source code so you can go back and inspect changes and see reason or context for those changes
- it will be subject to your normal development and release process including pull requests and code reviews
- it is the single source of truth for your builds
- if any standardization should even start to develop between vendor you might start to see a low degree of portability
One of the drawbacks with this approach is that you'll have to adopt a particular schema for your build definition. Depending on the how your build server actually implements the definition you might even need to implement it in a language your organization doesn't have specific skills in.
Many of the build tools and services already support this approach:
- AppVeyor has the ability to define a build configuration or definition via a YAML file that's kept in your project source repository
- TeamCity provides a Kotlin DSL that can be used for build definitions in the JetBrains tool chain
- Atlassian also has YAML definitions for Bitbucket Pipelines
- A JenkinsFile is what you can use to declare your steps for the Jenkins platform
Again we layer on the things required to get our CI build working. The Continuous Integration Process is your selected vendor with a subtle but significant change is now your Source Code Build Configuration is defined in a text based declarative language that we are brining in from source control which when ran will bring in your Source Code assets to ultimately create Software Artifacts.
Infrastructure as Code
We also have the ability to declare our your systems infrastructure in a vary similar manner.
A Infrastructure Process choice can be a one or a combination of a few options. On-Prem or Cloud, if you're using a cloud then, again which vendor (unless you are going to implement multi-cloud infrastructure) such as AWS or Azure. With the Infrastructure Build Configuration you are going to decide how you are going to deploy your infrastructure. You can do it manually through your respective vendors portal or you can choose your vendors automation platform to procure your infrastructure - for example, AWS Cloud Formation or Azure Resource Manager. Next is your template to define your environment. This Infrastructure Definition is what you are brining to the workflow. When this stack is executed your output are not application binaries but running Infrastructure Artifacts.
Outside of of the production environment benefits of infrastructure as code being versioned other benefits include:
- disposable sandbox environments
- temporary environments for your CI builds
- test systems for your UI Automation and Load Testing environments
- disposable demonstration environments for sales engineering and customers
Now we two automated stacks for applications and the environments they run in. This is a positive step and we're bringing our infrastructure into our software development process.
The Continuous Integration Process has both the Source Code Build Configuration and the Source Code itself committed to the repository. The Infrastructure Process also has the Infrastructure Definition in the repo the but Infrastructure Build Configuration is still living with the platform itself.
Merging the Processes
Let's see if we can better coordinate the release of software artifacts with our infrastructure changes by merging our infrastructure process with our Continuous Integration Process
Here we have a centralized process that is orchestrating our Continuous Integration Continuous Delivery with out repo holding the Source Code Configuration, Source Code and Infrastructure Definition and we still getting our binaries for our Software Artifacts and are running Infrastructure Artifacts but we are still relying on the Infrastructure Build Configuration to drive the infrastructure portion.
Specifically relating to Azure I wrote previously about using ARM templates in your build and deployment process which a major piece of this puzzle. Being able to put your infrastructure definitions for your system in a versioned state with your source code is useful on many fronts.
If your deployment tool chain supports a declarative for setting up your Infrastructure Build Configuration your releases then you can get your system to a state you have continuous integration of your Source Code, continuous integration for your Infrastructure Definition and all driven by your Continuous Integration Continuous Delivery Process.
Next up is an actual implemention of this approach using VSTS Yaml Build Definitions