Raise your hand if you've ever manually copied code from one server to another.
Yeah, we've all been there. It's how we start as developers. Learning to code comes first, and everything else comes later. There's a lot of information to take in, so we take baby steps, learning a little bit at a time until we're comfortable enough with the basics to start internalizing best practices.
As we progress in our education, we begin to understand the meaning behind terms like "version control" and "git flow" (if you’re not yet familiar with these terms, you can check out Codeship’s ebook). Once you get the hang of a version control system like Git or Mercurial, the benefits are pretty self-evident.
Beyond the obvious productivity and security boosts, the true power of version control systems comes from cloud-hosted VCS hosts like GitHub and Bitbucket. From facilitating code reviews to integrating with countless third-party add-ons, these services make being a developer that much more efficient and exciting.
While both of these services allow you to easily manage project contributors and provide great visual tools for viewing and managing a repository’s commit history, they also integrate directly with popular applications like Slack and Trello, which increases visibility into your project's health. In turn, they make the entire development team more productive by improving communication and facilitating collaboration.
In a Git-backed workflow, facilitating deployments to a production environment is significantly safer (and easier) than the copy-paste alternative. From doing a simple git pull on the server to automatically triggering atomic deployments on controller branch updates, delivering code can be done in any number of ways (as Codeship’s ebook also explains), each having its own benefits and drawbacks.
So, how do we take a powerful system like Git and use it to automate complex deployment workflows? The answer is Continuous Integration (CI) and Continuous Delivery (CD). With a CI/CD workflow, your code is automatically and continuously built, tested, and deployed to production whenever you update it.
With that automation comes simplicity. CI/CD workflows are surprisingly easy to set up and configure, and the small amount of time required to create them pays huge dividends in improving efficiency later.
To demonstrate how easy it is to establish a CI/CD delivery chain, let's take a look at what you need to do to integrate Codeship into an existing project. We’ll discuss deploying to Heroku using Codeship’s Classic infrastructure, then move onto more complicated deploys using their Docker infrastructure at the end of the article.
For the purposes of this demonstration, I spun up a vanilla Laravel application, but these steps can be easily applied to any new or existing project.
In a typical project, it isn't unheard of to automatically trigger production deployments whenever the
controllerbranch is updated. While this isn't the proper place to address this subject in depth, I should note that automating production deployments can be incredibly dangerous without a proper test suite and development workflow in place. If this is something you're considering, I recommend using a formal CI/CD platform like Codeship. That way you can configure proper atomic deployments and automated rollbacks in the event of any issues.
After validating our project's test suite, getting it running comfortably on Codeship, and ensuring that we have an appropriate development workflow to properly manage automated production deployments, it's time to work out our deployment pipelines.
This is important because by creating branches, we can configure and control code promotion to ensure that the right code ends up in the right place. At minimum in most cases, we’ll want to set up distinct development, staging, and controller branches. Builds and reviews of each branch can then be done independently, assuring that each part of the software delivery team is able to work with the right version of the code.
To create a branch, follow these steps:
Head over to the Deployment section of the Project Settings and click on + Add new deployment pipeline. This will pull up a page that looks like the following screenshot.
To trigger automatic deployments of the controller branch, select Branch is exactly from the dropdown and enter
controllerin the textbox.
When you click on Save pipeline settings, you'll be directed to a new page that asks for a deployment destination. To keep things simple, let's go with Heroku. (We’ll get to Docker below.)
For a simple production environment, simply fill out your Heroku application name and API key, and on the next build of the
controllerbranch, your application will be deployed to Heroku.
Deploying a single-server pipeline is a pretty straightforward process, but what about more complex deployment pipelines?
In many projects, acceptance testing is handled on staging servers with multiple load-balanced production servers, all of which require individual provisioning and deployment workflows. If this is something you've ever had to manage, then you know how much of a time suck it can be. Coordinating which code goes onto which servers and communicating that information to stakeholders can feel like a full-time job when the development team is being productive. Fortunately, this is a challenge that Codeship solves as well.
Let's assume, for the sake of this demonstration, that we are using the git flow workflow. Git flow is a popular development workflow, but due to its nature, it utilizes a lot of branches. What makes automating staging deploys with git flow a little more complicated is that the release branches are prefixed with the word
release, which means that we can't simply push a
release branch to Heroku as we did our
Automatically deploying new git flow releases to a staging environment is just as straightforward as the previous deployment process with a few tweaks:
Create a new deployment pipeline, but instead of selecting Branch is exactly from the dropdown, select Branch starts with.
Set the textbox to
release/. What this means is that any time a push happens on a new branch that starts with
release/, it will be automatically deployed to our designated staging environment. This is a good start, but we're not quite there yet.
Configure the Heroku application. The only different step here is that you need to enable force pushes, as you are pushing a non-controller branch up to Heroku.
As you can see, managing automated deployment workflows in a Git-backed workflow can be a relatively straightforward process, but what if we need… more?
Say, for example, our release branches don't start with release, but in fact end with it. Or perhaps we need to be able to automatically spin up more than one staging server at a time. This is where Codeship's Docker support comes into play. Getting your project set up to run Docker-based builds is beyond the scope of this guide, so I recommend reading Codeship's documentation if this is something your project requires.
By switching a project over to Docker-based builds (which requires simply requesting activation of a Docker trial in your Codeship account), we can take total control over what gets deployed and how.
Let's consider the first issue mentioned earlier. If the established naming conventions for your release branches don't follow a prefix-based system, then you can use Codeship's Docker-based system to limit deployments to Git tags or branches that match defined regular expressions.
Assuming you've already established your
codeship-steps.yml file, add
tag: staging$ to one of your steps. This tells Codeship to only run the defined step when that branch ends in the word "staging." While this is a fairly simple example, the possibilities here are endless. By establishing a more complicated naming convention, we can use git tags and branches to provide total, automated control over our build, test, and deployment workflows with minimal overhead.
Taking It Further
But what about the other scenario we mentioned? What if we need to spin up more than one staging environment at a time?
No problem. Docker can do that. By taking the example mentioned above, we should first limit a step to branches prefixed with the word "staging." This would be written as:
The next step is entirely dependent on your deployment destination, but let's assume we are still using Heroku (here's the exact configuration of this type of deploy through Docker). If we take a look at Heroku's documentation, we see that we are able to create applications via the command line using the following command:
heroku create <app-name>
If we combine these two pieces of information, we can automatically create a new Heroku application based on the current branch name. The step to accomplish this would look something like this:
- service: herokudeployment tag: ^staging command: heroku create `git branch 2>/dev/null| sed -n /^\*/s/^\* //p'`
It's important to note that this is simply a proof of concept. The command to pull the name of the git branch does not take into account illegal characters or taken names. That being said, the true power of Docker-based builds should be pretty self-evident in this simple example.
The power of repository-driven software development workflows is clear. It's essential for working effectively within a complex software delivery framework. For example, repository-driven development allows software delivery teams to maintain different branches for development, staging, and production. That ensures that each segment of the delivery pipeline can be managed efficiently and without interfering with other segments.
Of course, just how much power you get from your repositories or branches is ultimately dependent on your implementation. But whether you're managing complex multi-server deployment pipelines or a simple one-branch-one-server personal project, integrations between tools like GitHub and Codeship will take your productivity to the next level.