<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Ci-Cd on Soldier's 5</title><link>https://4lex.nz/tags/ci-cd/</link><description>Recent content in Ci-Cd on Soldier's 5</description><image><title>Soldier's 5</title><url>https://4lex.nz/img/404-bg.jpg</url><link>https://4lex.nz/img/404-bg.jpg</link></image><generator>Hugo</generator><language>en-us</language><lastBuildDate>Mon, 13 Jul 2020 00:00:00 +0000</lastBuildDate><atom:link href="https://4lex.nz/tags/ci-cd/index.xml" rel="self" type="application/rss+xml"/><item><title>Running Deployments with Github Actions</title><link>https://4lex.nz/posts/github-actions-heroku-sentry/</link><pubDate>Mon, 13 Jul 2020 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/github-actions-heroku-sentry/</guid><description>&lt;p&gt;In a previous article I wrote about &lt;a href="https://4lex.nz/2020/05/getting-started-with-github-actions" target="_blank" rel="noopener noreferrer"&gt;Getting Started with Github Actions&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;In it, I talk about the basics of Actions, how they work, what the language looks like and how you can get started. If you&amp;rsquo;ve never used Actions before, I highly recommend you have a quick read of that before reading this, as I build on some of the ideas presented there.&lt;/p&gt;
&lt;h2 id="continuous-integration-ci-versus-continues-deployment-cd-versus-continuous-delivery-unhelpfully-also-cd"&gt;Continuous Integration (CI) versus Continues Deployment (CD) versus Continuous Delivery (unhelpfully, also CD)&lt;/h2&gt;
&lt;p&gt;Put simply, &lt;strong&gt;continuous integration&lt;/strong&gt; is the act of continually moving your changeset into the wider codebase, as frequently as it makes sense for your use case. CI is an essential practice because it drives us as engineers to produce smaller changes more often, and these small changes tend to be less risky individually. Those small changes also lead us to deliver value to our users more rapidly, and more often.&lt;/p&gt;</description><content:encoded><![CDATA[<p>In a previous article I wrote about <a href="https://4lex.nz/2020/05/getting-started-with-github-actions" target="_blank" rel="noopener noreferrer">Getting Started with Github Actions</a>

.</p>
<p>In it, I talk about the basics of Actions, how they work, what the language looks like and how you can get started. If you&rsquo;ve never used Actions before, I highly recommend you have a quick read of that before reading this, as I build on some of the ideas presented there.</p>
<h2 id="continuous-integration-ci-versus-continues-deployment-cd-versus-continuous-delivery-unhelpfully-also-cd">Continuous Integration (CI) versus Continues Deployment (CD) versus Continuous Delivery (unhelpfully, also CD)</h2>
<p>Put simply, <strong>continuous integration</strong> is the act of continually moving your changeset into the wider codebase, as frequently as it makes sense for your use case. CI is an essential practice because it drives us as engineers to produce smaller changes more often, and these small changes tend to be less risky individually. Those small changes also lead us to deliver value to our users more rapidly, and more often.</p>
<p><strong>Continuous deployment</strong> is the engineering decision to ship validated or verified changes to production environments as often as practical. The decision to continually deploy tends to be made for the same reasons as we choose to continuously integrate. It&rsquo;s about reducing risk, making improvements available sooner, and building trust with users and with our less technology-focused colleagues.</p>
<p>The natural extension of continuous deployment is <strong>continuous delivery</strong>. While continuous delivery has become an umbrella term to describe all of the bits and pieces that contribute to it, I do see these things as separate concepts. The key difference then is that continuous deployment is an engineering decision to ship code to production. Continuous delivery is the product or broader business decision to make the features that our code provides, available to users.</p>
<p>Each concept requires the predecessor to work, but it can be a valid choice to stop at any of these points and not continue on. My own bias is to adopt continuous delivery as a fundamental practice, but your own operating context will have impacts on what parts of this you can and can&rsquo;t do.</p>
<p>Continuous Delivery is a foundational skill that your organisation needs to be good at if it is to remain responsive and &rsquo;lowercase a&rsquo; agile.</p>
<p>In the previous article, I stated:</p>
<blockquote>
<p>More concretely, a decent CI/CD solution will:</p>
</blockquote>
<ul>
<li>Build your code</li>
<li>Run the tests</li>
<li>Deploy it to an environment</li>
<li>Roll-back if there is an issue</li>
</ul>
<p>My initial implementation of Github Actions saw me using the tool primarily for continuous integration. However, I&rsquo;ve recently expanded that to include continuous deployment (and since I&rsquo;m not using feature flags yet, this technically qualifies as continuous delivery if only as a side effect).</p>
<h2 id="github-actions-now-owns-artefact-build-across-all-environments">Github Actions now owns artefact build across all environments.</h2>
<p>Previously, my <code>create-rc.yml</code> workflow built a container in dev, tested that and then told Heroku that the build is good, and to cut a release.</p>
<p>Heroku then would pull the code, build it and release it to my dev environment. I wasn&rsquo;t actually running the code I tested, I was running code built from the code I tested. This has been the source of subtle but pernicious bugs for me in the past. My rule of thumb is that you want to build your release candidate once and pass it through environments. That way you know for sure, and can prove in an auditable way, that this particular code is running in this specific place.</p>
<blockquote>
<p>Note: The Heroku CLI is bundled as standard on Github Actions runners. I couldn&rsquo;t find this documented anywhere and literally only stumbled on it in a fit of frustration at midnight last night. My new heuristic is that if you need a specific tool, assume it&rsquo;s on the runner but isn&rsquo;t necessarily up to date. If your dependency isn&rsquo;t there, then you should try to work around it.</p>
</blockquote>
<p>I assumed that Heroku CLI wouldn&rsquo;t be there and spent a long time trying to fight shadows. Don&rsquo;t be like me.</p>
<p>Here&rsquo;s a high-level summary of the work I did to make my builds work the way I&rsquo;ve described above, with one RC being deployed to many environments:</p>
<ol>
<li>Have <code>create-rc.yml</code> treat the docker image it makes like the one RC, rather than as a means of delivering integration tests.</li>
<li>Improve the tagging process so that we produce a docker image with the right naming conventions for Heroku.</li>
<li>Push the image to the Heroku docker registry, where we can persist it across builds and environments.</li>
<li>Introduce a new job to deploy the verified image to the right Heroku environment.</li>
<li>Introduce a new workflow with a different trigger, to take the already validated image and redeploy it to production.</li>
</ol>
<p>Overall the changes weren&rsquo;t massive, but I definitely cut myself on some rough edges (documentation being a big part of that).</p>
<p>These steps build my image, tag it and send it off to Heroku. That&rsquo;s step 1, 2 and 3 done in ~20 lines:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yml" data-lang="yml"><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Login to Heroku Registry</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">env</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">HEROKU_API_KEY</span>: <span style="color:#ae81ff">${{ secrets.HEROKU_TOKEN }}</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">run</span>: <span style="color:#ae81ff">heroku container:login</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Build and push</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">env</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">HEROKU_API_KEY</span>: <span style="color:#ae81ff">${{ secrets.HEROKU_TOKEN }}</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">run</span>: <span style="color:#ae81ff">heroku container:push -a dev-api-spring-boot web</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Create tarball from docker image</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># Heroku has a naming convention for images, it&#39;s important to match that.</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">run</span>: <span style="color:#ae81ff">docker save registry.heroku.com/dev-api-spring-boot/web &gt; spring-boot-api-candidate.tar</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Upload image tarball for next job</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">actions/upload-artifact@v2</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">with</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#ae81ff">spring-boot-api-candidate.tar</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">path</span>: <span style="color:#ae81ff">spring-boot-api-candidate.tar</span>
</span></span></code></pre></div><p>These steps close out the only actual building of code that I do. In every other case, I get code from one place to the other by redeploying code I already know to be good.
It&rsquo;s important to note here that I&rsquo;m trying to keep build time low by using capabilities baked into the actions runner, rather than using externally published actions.</p>
<p>It&rsquo;s also important to note that if you use Heroku commands the way I am, you lose visibility of fine-grained build actions because most of that code lives in your docker file. By that, I mean that all I can see in this code is that Heroku is pushing a container. I can&rsquo;t see how it&rsquo;s built. For that, I need to go to my Dockerfile, which in this case looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yml" data-lang="yml"><span style="display:flex;"><span><span style="color:#ae81ff">FROM gradle:6.5.1-jdk14 as build-stage</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">WORKDIR /build</span>
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">COPY src /build/src</span>
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">COPY gradlew /build/gradlew</span>
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">COPY gradle /build/gradle</span>
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">COPY build.gradle /build/build.gradle</span>
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">COPY settings.gradle /build/settings.gradle</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">RUN /build/gradlew clean build -x test</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Running container build begins</span>
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">FROM adoptopenjdk:14.0.1_7-jre-openj9-0.20.0-bionic</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># copy application JAR</span>
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">COPY --from=build-stage build/build/libs/spring-boot-api-*.jar /spring-boot-api.jar</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># specify default command</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># The command to run this in local mode would be docker run -p 8080:8080 -e JAVA_OPTS=&#34;-Dspring.profiles.active=local -Dserver.port=$PORT&#34; registry.heroku.com/dev-api-spring-boot/web</span>
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">CMD [&#34;sh&#34;, &#34;-c&#34;, &#34; java ${JAVA_OPTS} -jar /spring-boot-api.jar&#34;]</span>
</span></span></code></pre></div><p>Now I have two sets of build logic in two places, which isn&rsquo;t perfect, but given how docker works, it&rsquo;s tolerable. An example of the impact this has is that if I want to stop running tests while I build the image (because I unit test and integration test within the workflow), I need to add <code>-x test</code> to the end of my Gradle command in the Dockerfile as seen here. It adds a little bit of cognitive burden. Okay for now but not ideal.</p>
<p>Speaking of unit tests, since I want to cut down my build time, I can run them in parralel! Hooray for Github&rsquo;s <code>jobs are parralel by default</code> stance!</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yml" data-lang="yml"><span style="display:flex;"><span><span style="color:#f92672">unit-test</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">runs-on</span>: <span style="color:#ae81ff">ubuntu-latest</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">needs</span>: <span style="color:#ae81ff">build-image</span> <span style="color:#75715e"># build-image is the name of the first job in this workflow, it&#39;s where the Heroku code above lives.</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">steps</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Check out code</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">actions/checkout@v2</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Set up JDK 14</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">actions/setup-java@v1</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">with</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">java-version</span>: <span style="color:#ae81ff">14</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Make gradle wrapper executable</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">run</span>: <span style="color:#ae81ff">chmod +x gradlew</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Run Unit Tests</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">env</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">PORT</span>: <span style="color:#ae81ff">8080</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">run</span>: <span style="color:#ae81ff">./gradlew clean test</span>
</span></span></code></pre></div><h2 id="releasing-a-verified-image-to-heroku">Releasing a verified image to Heroku</h2>
<p>The great thing is that we only need to add one more step to our <code>create-rc.yml</code> (although this file should probably be renamed to <code>dev-build</code> or something simillar).
What I&rsquo;m saying below is that at the end of my build, only when I&rsquo;ve got green unit tests and green integration tests, I want that image that I&rsquo;ve already pushed, to be activated in Heroku.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yml" data-lang="yml"><span style="display:flex;"><span><span style="color:#f92672">dev-deploy</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">runs-on</span>: <span style="color:#ae81ff">ubuntu-latest</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">needs</span>: [<span style="color:#ae81ff">integration-test, unit-test]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">steps</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Login to Heroku Registry</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">env</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">HEROKU_API_KEY</span>: <span style="color:#ae81ff">${{ secrets.HEROKU_TOKEN }}</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">run</span>: <span style="color:#ae81ff">heroku container:login</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Release to Heroku dev</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">env</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">HEROKU_API_KEY</span>: <span style="color:#ae81ff">${{ secrets.HEROKU_TOKEN }}</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">run</span>: <span style="color:#ae81ff">heroku container:release -a dev-api-spring-boot web</span>
</span></span></code></pre></div><p>I don&rsquo;t have to care about artefact handling or pass in a tarball. The image is always current, and I just choose whether or not I want to make it active.
One of the drawbacks here though is that I could get an image pushed that fails unit tests or integration tests. This is mitigated by the image remaining inactive until I release it.
There is an edge case here that if I had to rollback, I might roll back to a bad image but that&rsquo;s a problem for improvement later - and I&rsquo;ll never have a broken image that won&rsquo;t start because if the image won&rsquo;t build, the build job will fail.</p>
<p>So with the above changes, we have a dev build process entirely owned by Github Actions. In theory, I could rip out Heroku and point to AWS ECS or somewhere else quickly. I&rsquo;m using my platform provider for its platform, not for the stepladder on to the platform!</p>
<h2 id="releasing-to-production-with-sentry-integration">Releasing to Production with Sentry Integration</h2>
<p>My procedure is that a release to production is triggered on a push to <code>main</code>, and the only way to push to main is via a merged commit.
This procedure needs to do a few things:</p>
<ol>
<li>Take a validated image from an upstream environment and retag it.</li>
<li>Push that retagged image back to Heroku</li>
<li>Release the image</li>
<li>Create a new release version in Sentry</li>
</ol>
<p>I have some intermediate steps here that I want to use to tie things together from an audit perspective, but I&rsquo;m waiting on some feedback from elsewhere before I can do that.
Here are the steps for my release job, note how similar it is to my dev release methodology.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yml" data-lang="yml"><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Login to Heroku Registry</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">env</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">HEROKU_API_KEY</span>: <span style="color:#ae81ff">${{ secrets.HEROKU_TOKEN }}</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">run</span>: <span style="color:#ae81ff">heroku container:login</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Pull dev image</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">run</span>: <span style="color:#ae81ff">docker pull registry.heroku.com/dev-api-spring-boot/web</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Re-tag dev as prod</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># Again, Heroku expects a naming convention here, so we just have to comply.</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">run</span>: <span style="color:#ae81ff">docker tag registry.heroku.com/dev-api-spring-boot/web registry.heroku.com/api-spring-boot/web</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Push production image</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">run</span>: <span style="color:#ae81ff">docker push registry.heroku.com/api-spring-boot/web</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Push Prod Image</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">env</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">HEROKU_API_KEY</span>: <span style="color:#ae81ff">${{ secrets.HEROKU_TOKEN }}</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">run</span>: <span style="color:#ae81ff">heroku container:release -a api-spring-boot web</span>
</span></span></code></pre></div><p>The post-processing I do on a release is much more interesting then docker jiggery-pokery:</p>
<ol>
<li>I want to grab the entire codebase because I want that delicious commit history.</li>
<li>I want to create a new tag for this production release</li>
<li>I want to get all of the commit descriptions between the new tag, and the previous tag, and then filter out some commits that aren&rsquo;t interesting for given values of interesting</li>
<li>Then cut a release for Github using the commits as a changelog</li>
<li>Finally, use Sentry.io for crash reports, and I want to tell Sentry about the new version so that I can track where issues might have been introduced.</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yml" data-lang="yml"><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Checkout code</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">actions/checkout@v2</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">with</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># Fetch depth grabs the entire history, though I&#39;m sure I could grab the commits since the last tag if I really wanted.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">fetch-depth</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># I encountered an issue here so have left this commented out. It&#39;s not a part of my workflow, but hopefully one day it&#39;ll create tags for me</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Leaving this out for now - to use the output of tag in the below release use ${{ steps.create_tag.outputs.new_tag }}</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#      - name: Bump version and push tag</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#        id: create_tag</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#        uses: mathieudutour/github-tag-action@v4.5</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#        with:</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#          github_token: ${{ secrets.GITHUB_TOKEN }}</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#          release_branches: dev</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Generate release notes</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># Important note: I use https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit for commit format. It makes it really easy to filter commits in the way I&#39;ve done below.</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">run</span>: <span style="color:#ae81ff">git log $(git describe --tags --abbrev=0)..HEAD --pretty=format:&#34;%s&#34; -i -E --grep=&#34;^(build|ci|docs|feat|fix|perf|refactor|style|test)*feat|perf|fix&#34; &gt; release-notes.txt</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># I don&#39;t want to cut releases with no info in them! That&#39;s why this too is not currently part of my workflow</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#      - name: Create Github Release</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#        id: create_release</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#        uses: actions/create-release@v1</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#        env:</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#        with:</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#          tag_name: ${{ github.sha }}</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#          release_name: Release ${{ github.sha }}</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#          body_path: release-notes.txt</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#          draft: false</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#          prerelease: false</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Create Sentry release</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">tclindner/sentry-releases-action@v1.2.0</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">env</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">SENTRY_AUTH_TOKEN</span>: <span style="color:#ae81ff">${{ secrets.SENTRY_AUTH_TOKEN }}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">SENTRY_ORG</span>: <span style="color:#ae81ff">my-sentry-org</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">SENTRY_PROJECT</span>: <span style="color:#ae81ff">spring-boot-api</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">with</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">tagName</span>: <span style="color:#ae81ff">${{ github.sha }}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">environment</span>: <span style="color:#ae81ff">heroku-prod</span>
</span></span></code></pre></div><p>The net effect of the above is that I get to take a previously validated image. When I choose (via merged PR), I can automatically redeploy quality code to prod without rebuilding.
The neat interplay here is that when I open that PR, my project will build, and use that same build to decide whether or not I can merge. As soon as I merge, the code is in prod without a rebuild. It&rsquo;s nice.</p>
<p>With a new release being cut in Sentry each time, I can resolve errors that I&rsquo;ve introduced down to at worst, a PR or at best, an individual commit. Also nice!</p>
<p><img alt="Sentry Releases" loading="lazy" src="/img/in-post/sentry-releases.png"></p>
]]></content:encoded></item><item><title>Getting Started with Github Actions</title><link>https://4lex.nz/posts/getting-started-with-github-actions/</link><pubDate>Thu, 28 May 2020 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/getting-started-with-github-actions/</guid><description>&lt;p&gt;Continuous Delivery is a foundational skill that your organisation needs to be good at if it is to remain responsive and &amp;rsquo;lowercase a&amp;rsquo; agile.&lt;/p&gt;
&lt;p&gt;A full solution for continuous delivery tends to include (but isn&amp;rsquo;t limited to):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The ability to create artefacts that we can release to production, across many products, teams, branches, environments and repositories.&lt;/li&gt;
&lt;li&gt;The ability to quickly validate an artefact as a candidate for release (through applying the testing triangle - unit, integration, acceptance, smoke and so on).&lt;/li&gt;
&lt;li&gt;The ability to release code to production without necessarily activating it for users (typically through feature flags, canary built and graduated rollouts).&lt;/li&gt;
&lt;li&gt;The ability to rapidly respond if a release candidate we later find to be problematic (again through canary builds, graduated rollouts, and by quickly rolling back software found to be problematic).&lt;/li&gt;
&lt;li&gt;The ability to audit or understand what code is where, what state that code is in, and why that code is where it is.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;More concretely, a decent CI/CD solution will:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Continuous Delivery is a foundational skill that your organisation needs to be good at if it is to remain responsive and &rsquo;lowercase a&rsquo; agile.</p>
<p>A full solution for continuous delivery tends to include (but isn&rsquo;t limited to):</p>
<ul>
<li>The ability to create artefacts that we can release to production, across many products, teams, branches, environments and repositories.</li>
<li>The ability to quickly validate an artefact as a candidate for release (through applying the testing triangle - unit, integration, acceptance, smoke and so on).</li>
<li>The ability to release code to production without necessarily activating it for users (typically through feature flags, canary built and graduated rollouts).</li>
<li>The ability to rapidly respond if a release candidate we later find to be problematic (again through canary builds, graduated rollouts, and by quickly rolling back software found to be problematic).</li>
<li>The ability to audit or understand what code is where, what state that code is in, and why that code is where it is.</li>
</ul>
<p>More concretely, a decent CI/CD solution will:</p>
<ul>
<li>Build your code</li>
<li>Run the tests</li>
<li>Deploy it to an environment</li>
<li>Roll-back if there is an issue</li>
</ul>
<p>Building an excellent build tool is no easy task, and because of the variety of use cases out there, you tend to find tools optimised for those use cases. They&rsquo;ll be passable or useful in that niche and poor outside of it.
Use-case variety and the complexity of the problem space is part of the reason why container-based builds have taken off. Where previously I&rsquo;d have precious build agent boxes sitting somewhere that I&rsquo;d need to feed and water and love and sell my children to, now I can have a dumb build box that knows about nothing. The build box just grabs a container off the internet (or I make a custom container) to fill my needs.</p>
<h2 id="github-actions">Github Actions</h2>
<p><a href="https://github.com/features/actions" target="_blank" rel="noopener noreferrer">Github Actions</a>

 is one such container based build offering, though at first they don&rsquo;t tell you that!</p>
<p>The highest order concept in Actions is the Workflow. A workflow consists of several jobs, and each job can have several steps. Here&rsquo;s an example we&rsquo;ll become a bit more familiar with later:</p>
<p><img alt="Github Actions Example Workflow" loading="lazy" src="/img/in-post/example-github-actions-workflow.jpg"></p>
<p>As you can see, A workflow, is a set of jobs (that we run in series or parallel), with each job containing several steps.
Jobs describe collections of steps that when together, perform some action that is useful to the business. Model your jobs in terms of things that are useful for you - it&rsquo;s these aggregates that you&rsquo;ll be watching most.</p>
<p>Steps are concrete actions that we perform, things like building, unit testing, renaming files and so on. A step is the smallest unit of work you can do in a workflow, and they don&rsquo;t need to be useful in and of themselves.</p>
<blockquote>
<p>The reason I think that jobs should be useful units of work for your business, is because if you plan them that way, it becomes trivial to re-use jobs to achieve new functionality quickly</p>
</blockquote>
<h3 id="pipelines-as-code-with-yaml">Pipelines as Code with&hellip; YAML</h3>
<p>Workflows use YAML as the format of choice, and I YAML in Github Actions as much as I hate any other white spaced language in a place it doesn&rsquo;t belong. I&rsquo;m sure Github chose YAML for a good reason, I&rsquo;ve struggled to find out what those reasons were or are. Work continues in that area.</p>
<p><img alt="I WILL RUN INTO THE WOODS DON&rsquo;T TEMPT ME" loading="lazy" src="/img/in-post/YML.png"></p>
<p>As with most things that involve YAML, the workflow file is easy to quickly grok while it&rsquo;s small to medium length but challenging to troubleshoot or do anything with once it gets a bit bigger.
Github Actions is still new, so I can understand why it might be a little rough around the edges. Perhaps there is tooling around the workflow file I&rsquo;m ignorant of, but an equivalent to Bitbucket Pipelines&rsquo;s <a href="https://web.archive.org/web/20240105172732/https://bitbucket-pipelines.prod.public.atl-paas.net/validator" target="_blank" rel="noopener noreferrer">pipeline validator</a>

 would be an excellent addition.</p>
<p>Even better still (and this would be a feature far down the track), give me a fluent API akin to <a href="https://confluence.atlassian.com/bamboo/tutorial-create-a-simple-plan-with-bamboo-specs-894743911.html" target="_blank" rel="noopener noreferrer">Bamboo's Java Spec Concept</a>

.</p>
<p>To save you a click, 90% of my warm fuzzies for Atlassian Bamboo come from being able to do this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#66d9ef">private</span> Plan <span style="color:#a6e22e">createPlan</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">new</span> Plan(
</span></span><span style="display:flex;"><span>            project(),
</span></span><span style="display:flex;"><span>            <span style="color:#e6db74">&#34;Plan Name&#34;</span>, <span style="color:#e6db74">&#34;PLANKEY&#34;</span>)
</span></span><span style="display:flex;"><span>            .<span style="color:#a6e22e">description</span>(<span style="color:#e6db74">&#34;Plan created from (enter repository url of your plan)&#34;</span>)
</span></span><span style="display:flex;"><span>            .<span style="color:#a6e22e">stages</span>(
</span></span><span style="display:flex;"><span>                    <span style="color:#66d9ef">new</span> Stage(<span style="color:#e6db74">&#34;Stage 1&#34;</span>)
</span></span><span style="display:flex;"><span>                            .<span style="color:#a6e22e">jobs</span>(<span style="color:#66d9ef">new</span> Job(<span style="color:#e6db74">&#34;Build &amp; run&#34;</span>, <span style="color:#e6db74">&#34;RUN&#34;</span>)
</span></span><span style="display:flex;"><span>                                    .<span style="color:#a6e22e">tasks</span>(
</span></span><span style="display:flex;"><span>                                            <span style="color:#66d9ef">new</span> ScriptTask().<span style="color:#a6e22e">inlineBody</span>(<span style="color:#e6db74">&#34;echo Hello world!&#34;</span>))));
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Getting access to this kind of API, and provide auto-completion, intellisense and everything that comes with that ecosystem is war-winning feature for pipeline builders.</p>
<p>I would prefer XML to YAML. There. I said it. We can move on now.</p>
<h2 id="workflows">Workflows</h2>
<p>What does a workflow file look like? Here&rsquo;s the file for a Spring Boot application I&rsquo;m currently working on.
I was very focused on getting a minimal workflow up and running, so there are things to be added and things to be optimised.</p>
<p>Have a big read, and I&rsquo;ll break it down below:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yml" data-lang="yml"><span style="display:flex;"><span><span style="color:#f92672">name</span>: <span style="color:#ae81ff">Create Release Candidate</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">on</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">push</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">branches</span>: [ <span style="color:#ae81ff">dev ]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">pull_request</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">branches</span>: [ <span style="color:#ae81ff">dev ]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">jobs</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">create-rc</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">runs-on</span>: <span style="color:#ae81ff">ubuntu-latest</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">steps</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Check out code</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">actions/checkout@v2</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Build and tag release candidate</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">docker/build-push-action@v1.1.0</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">with</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">username</span>: <span style="color:#ae81ff">${{ secrets.HEROKU_USER }}</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">password</span>: <span style="color:#ae81ff">${{ secrets.HEROKU_TOKEN }}</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">registry</span>: <span style="color:#ae81ff">registry.heroku.com</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">repository</span>: <span style="color:#ae81ff">image-repository/heroku-process</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">tags</span>: <span style="color:#ae81ff">latest</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">push</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Create tarball from docker image</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">run</span>: <span style="color:#ae81ff">docker save registry.heroku.com/image-repository/heroku-process:latest &gt; heroku-process-candidate.tar</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Upload image tarball for next job</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">actions/upload-artifact@v2</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">with</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">heroku-process-candidate.tar</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">path</span>: <span style="color:#ae81ff">heroku-process-candidate.tar</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">integration-test</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">runs-on</span>: <span style="color:#ae81ff">ubuntu-latest</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">needs</span>: <span style="color:#ae81ff">create-rc</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">steps</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Check out code</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">actions/checkout@v2</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Download tarball</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">actions/download-artifact@v2</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">with</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">heroku-process-candidate.tar</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Load image from tarball</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">run</span>: <span style="color:#ae81ff">docker load &lt; heroku-process-candidate.tar</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># github doesn&#39;t allow us to gracefully expire artifacts (e.g. keep last 10 build artifacts and no more)</span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># if we don&#39;t delete these, we&#39;ll hit the storage limit for actions quickly.</span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># persisting docker images outside of builds should be done by pushing the image to a registry</span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># we should rm the tarball as soon as we&#39;ve loaded it as a docker image.</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Tidy up artifact to avoid storage limit</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">geekyeggo/delete-artifact@v1</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">with</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">heroku-process-candidate.tar</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Run application server</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">run</span>: <span style="color:#ae81ff">docker run -d -p 8080:8080 -e JAVA_OPTS=-Dspring.profiles.active=dev registry.heroku.com/image-repository/heroku-process:latest</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Make server watch script executable</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">run</span>: <span style="color:#ae81ff">chmod +x wait-for-heroku-process.sh</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Wait for server to start</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">run</span>: <span style="color:#ae81ff">./wait-for-heroku-process.sh</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Set up JDK 14</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">actions/setup-java@v1</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">with</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">java-version</span>: <span style="color:#ae81ff">14</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Make gradle wrapper executable</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">run</span>: <span style="color:#ae81ff">chmod +x gradlew</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Run Integration Tests</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">run</span>: <span style="color:#ae81ff">./gradlew integrationTest</span>
</span></span></code></pre></div><p>The above file is just under 100 lines of YAML to describe the first two jobs in the image above (Package for release and Run Integration tests).
Now that I&rsquo;ve finalised everything, it looks pretty clean and tidy, the process to get there was anything but. Again this is something that I&rsquo;d attribute to new-ness. Still, Github Actions does an excellent job at giving you the low-level details about how a thing works, but a lousy job at describing how you can use what Actions provides you to create higher-order pipelines.</p>
<p>For example, the Gradle starter <a href="https://github.com/actions/starter-workflows/blob/master/ci/gradle.yml" target="_blank" rel="noopener noreferrer">here</a>

 does a good job of telling me how to declare jobs, steps and make them dependent on pull requests or branches. It also leaves me with questions. I want to package my application, and then run it, and then only when it&rsquo;s running, run unit tests. How do I do that? I want to publish to my registry, but only if the unit and integration tests have passed, except for in dev, we can publish there with some failing tests. How do I do that?</p>
<p>The basic building blocks are there, but more complex pipeline logic is missing.</p>
<p>Compare this with the <strong>excellent</strong> documentation produced by VMWare and Pivotal for <a href="https://github.com/vmwarepivotallabs/concourse-pipeline-samples" target="_blank" rel="noopener noreferrer">Concourse CI</a>

 that gets much more into higher-order combinations of those basic building blocks.</p>
<p>Compare <a href="https://github.com/vmwarepivotallabs/concourse-pipeline-samples/tree/master/concourse-pipeline-patterns/gated-pipelines" target="_blank" rel="noopener noreferrer">Gated CI Pipelines</a>

 and <a href="https://github.com/vmwarepivotallabs/concourse-pipeline-samples/tree/master/pipelines/appdev/blue-green-app-deployment" target="_blank" rel="noopener noreferrer">Blue-Green Deployments</a>

 with Concourse, to anything in the only workflow template repository I could find, <a href="https://github.com/actions/starter-workflows/tree/master/ci" target="_blank" rel="noopener noreferrer">Starter Workflows</a>

 to see that high level / low-level split.</p>
<p>Concourse is a more mature product and more fully-featured, where perhaps Github Actions is still more of a CI tool rather than a CD tool. I&rsquo;m sure it will get there in time. The portability and reusability that Github Actions provides is a significant advantage over other build tools.</p>
<blockquote>
<p>I love how sturdy the modelling and pipelining features are for Concourse. It&rsquo;s ironic though that at my current workplace we&rsquo;ve just decided to get off Concourse CI in favour of Github Actions.</p>
</blockquote>
<h2 id="jobs-in-detail">Jobs in detail</h2>
<p>Here&rsquo;s the same workflow as above, trimmed for easier reading.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yml" data-lang="yml"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">name</span>: <span style="color:#ae81ff">Create Release Candidate</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">on</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">push</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">branches</span>: [ <span style="color:#ae81ff">dev ]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">pull_request</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">branches</span>: [ <span style="color:#ae81ff">dev ]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">jobs</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">create-rc</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">runs-on</span>: <span style="color:#ae81ff">ubuntu-latest</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">steps</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Arbitrary Step 1</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">run</span>: <span style="color:#ae81ff">echo &#34;Hello Muddah&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Arbitrary Step 2</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">run</span>: <span style="color:#ae81ff">echo &#34;Hello Fadduh&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">integration-test</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">runs-on</span>: <span style="color:#ae81ff">ubuntu-latest</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">needs</span>: <span style="color:#ae81ff">create-rc</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">steps</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Arbitrary Step 1</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">run</span>: <span style="color:#ae81ff">echo &#34;Here I am at&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Arbitrary Step 2</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">run</span>: <span style="color:#ae81ff">echo &#34;Camp Granada.&#34;</span>
</span></span></code></pre></div><h3 id="important-concepts-jobs-and-steps">Important Concepts: Jobs and Steps</h3>
<ul>
<li>A workflow has one set of jobs</li>
<li>A job has one set of steps.</li>
<li>Both of these sets can have one to many items in them. You can have ten jobs, each with 20 steps, or one job with 1 step.</li>
<li>To create a sequential pipeline, add a <code>needs</code> statement in every job after the first one, and in that needs statement, refer to the previous job. For example, I have two jobs, <code>create-rc</code> and <code>integration-test</code>. In the second job, I have this statement: <code>needs: create-rc</code>.</li>
<li><strong>Importantly</strong>, Jobs run in parallel by default. A sequential run like the above is something you have to do specifically. Parallel by default creates advantages if you can decouple your build process.</li>
<li>Right now, I have to wait for Actions to build my RC and image before I can integration test. I believe in pushing one artefact through the entire pipeline rather than rebuilding it each from source each time - but if I didn&rsquo;t, I might be able to get super parallelisation on my build pipelines. I.e. rather than Checkout Source &gt; Build &gt; Test &gt; Deploy with one artefact the whole way through, I could Checkout Source &gt; Build, Checkout Source &gt; Test, Checkout Source &gt; Package &gt; Deploy all at the same time. This means the time the build takes is the <code>max()</code> of the longest job, rather than <code>sum(job_1 + job_2 ... job_n)</code>.</li>
<li>I&rsquo;m an absolute savage though and believe that the best approach is to checkout and built once, and then pass an artefact all the way through. The tradeoff for that certainty is a longer build time (about 5 minutes in this case, but for longer build processes, it probably stacks up quick).</li>
<li>There is a whole ecosystem of Actions available on the Github Marketplace, but every action you use will cost you in build time. Actions are distributed as Docker images, and when your build runs, before you can build your build, you have to build all of your actions.</li>
<li>Steps all run sequentially, and an action can be a step, but not all steps have to be actions. For example:</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yml" data-lang="yml"><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Arbitrary Step 1</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">run</span>: <span style="color:#ae81ff">echo &#34;Hello Mother&#34;</span>
</span></span></code></pre></div><p>The above is a step that is not an action; the below is a step that is action. All actions are steps, but a step does not have to be an action.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yml" data-lang="yml"><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Build and tag release candidate</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">docker/build-push-action@v1.1.0</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">with</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">username</span>: <span style="color:#ae81ff">${{ secrets.HEROKU_USER }}</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">password</span>: <span style="color:#ae81ff">${{ secrets.HEROKU_TOKEN }}</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">registry</span>: <span style="color:#ae81ff">registry.heroku.com</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">repository</span>: <span style="color:#ae81ff">image-repository/heroku-process</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">tags</span>: <span style="color:#ae81ff">latest</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">push</span>: <span style="color:#66d9ef">false</span>
</span></span></code></pre></div><h2 id="sharing-and-storing-artifacts">Sharing and Storing Artifacts</h2>
<p>Each job runs in parallel and runs in a separate container.
If you have a job that builds, and you have a job that tests, you have to figure out a way to get the build output into the testing task.
Github&rsquo;s solution to this seems inefficient, but I can&rsquo;t see any better way of doing it.</p>
<p>Your workflow could happen anywhere, multiple anywhere even. Because jobs are independent, in theory, you could have your build happening over in the US East region and your tests happening in Europe.
You can&rsquo;t rely on them being on the same machine or even in the same DC, so sharing artefacts via cloud storage is the only choice you have.</p>
<p>It gives rise to these two actions:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yml" data-lang="yml"><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Upload image tarball for next job</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">actions/upload-artifact@v2</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">with</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">heroku-process-candidate.tar</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">path</span>: <span style="color:#ae81ff">heroku-process-candidate.tar</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Download tarball</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">actions/download-artifact@v2</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">with</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">heroku-process-candidate.tar</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Load image from tarball</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">run</span>: <span style="color:#ae81ff">docker load &lt; heroku-process-candidate.tar</span>
</span></span></code></pre></div><p>The way we share artefacts between jobs (or persist them outside of builds) is essentially by uploading and downloading from S3 or an equivalent (I assume they use S3 but haven&rsquo;t done the research to confirm).
I have a small workflow right now, but what this means is that passing my artefact from my first job to the second, accounts for a whopping <strong>21%</strong> of the run time of the build. This behaviour pushes me to a perverse outcome where I&rsquo;m better off starting from scratch in every single job because of the time it takes to trade artefacts! An alternative to that would be to have one enormous job, but that also flies in the face of the whole idea of Github Actions, which is that concept of composable, reusable workflows.</p>
<p>Further, you need to be careful with your usage of artefacts.</p>
<p>On a team or pro plan, you get 2GB of storage for actions. Artefacts associated with builds older than 90 days get deleted. To naturally stay at or under the 2GB limit, your builds need to persist fewer than <strong>22MB per day - not per build, per day</strong> worth of artefacts. Github Packages, and storage for Actions, get very spendy, very quickly.</p>
<p>This behaviour gives rise to actions like this one. I consider this 100% required for anybody who has Workflows with more than one job, where you need to pass an artefact between them.
Remember, every job that needs to share an artefact will incur the wrath of Github Packages.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yml" data-lang="yml"><span style="display:flex;"><span>      <span style="color:#75715e"># github doesn&#39;t allow us to gracefully expire artifacts (e.g. keep last 10 build artifacts and no more)</span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># if we don&#39;t delete these, we&#39;ll hit the storage limit for actions quickly.</span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># persisting docker images outside of builds should be done by pushing the image to a registry</span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># we should rm the tarball as soon as we&#39;ve loaded it as a docker image.</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Tidy up artifact to avoid storage limit</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">geekyeggo/delete-artifact@v1</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">with</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">heroku-process-candidate.tar</span>
</span></span></code></pre></div><p>As per the comment, I&rsquo;d recommend you persist artefacts during builds and between jobs, delete them afterwards, and for audit, viewing test reports and things like that, find somewhere else to put them. Put containers into your registry, put test reports into S3 or similar. For more detail on how this all gets billed, see <a href="https://help.github.com/en/github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-github-actions" target="_blank" rel="noopener noreferrer">this</a>

</p>
<h2 id="in-closing-why-use-actions">In closing, why use Actions?</h2>
<p>I think for a lot of people (myself included), the main draw for Github Actions is that it&rsquo;s conceptually quite simple (though as with anything there are gotchas), and tightly woven in with Github itself.
I like that the build lives close to the code and everything that comes with it. Github Actions is &lsquo;pipeline-as-code&rsquo;, that isn&rsquo;t that much of a burden to learn and use, and even having been newly released, it&rsquo;s as capable as any other build tool I&rsquo;ve used.</p>
<p>Where it starts to get powerful is the open-source ecosystem of Actions and the tie into wider Github. Be that the code repository or other apps I might have added to my repo or org. I can get this without having to deal with another tool, another login, or the overhead of another new thing. Thinking on it, maybe that&rsquo;s why they chose YAML.</p>
<p>While I&rsquo;ve spent a lot of time bagging it, I quite like it. It&rsquo;s a good example of delivering just enough capability at the right time, and I&rsquo;m sure Actions will continue to grow, if not via first-party development, certainly through that action ecosystem.</p>
<p>Yes, I&rsquo;ve got gripes about YAML, but I have that most places I go. You can work around the storage issues, and it&rsquo;s not unreasonable for Github to try and make money given everything else they provide for free or for cheap.
Actions is a fully managed service, no servers to feed and water, no breaking updates so far. With versionable pipelines, there are very few moving parts that we the end-users have to care about. That&rsquo;s worth more than it&rsquo;s equivalent weight in gold.</p>
<p>If you&rsquo;ve got a pet project or something new at work, I recommend you give it a try.</p>
]]></content:encoded></item><item><title>Building Continuous Delivery for Healthlink</title><link>https://4lex.nz/posts/building-ci-cd-for-healthlink/</link><pubDate>Wed, 05 Oct 2016 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/building-ci-cd-for-healthlink/</guid><description>&lt;p&gt;My team at Healthlink have just finished a proof of concept around Continuous Delivery. Here’s the writeup, sanitised for the internet.&lt;/p&gt;
&lt;h2 id="context"&gt;Context&lt;/h2&gt;
&lt;p&gt;We’re a Health IT company based out of New Zealand, and run an set of products
that provide messaging integrations between Healthcare organisations across New Zealand
and Australia. Our applications are a mixture of desktop and web service based, and we’re
currently undergoing a massive change process from Waterfall to Agile and from a bespoke
services company to a truly product focused company.&lt;/p&gt;</description><content:encoded><![CDATA[<p>My team at Healthlink have just finished a proof of concept around Continuous Delivery. Here’s the writeup, sanitised for the internet.</p>
<h2 id="context">Context</h2>
<p>We’re a Health IT company based out of New Zealand, and run an set of products
that provide messaging integrations between Healthcare organisations across New Zealand
and Australia. Our applications are a mixture of desktop and web service based, and we’re
currently undergoing a massive change process from Waterfall to Agile and from a bespoke
services company to a truly product focused company.</p>
<h2 id="the-problem">The Problem</h2>
<p>Our time to market is poor, we want to shorten our release cycle and provide
and awesome service to our customers (above and beyond the services that basically
enabled us to become a monopoly in New Zealand).</p>
<h2 id="the-solution">The Solution</h2>
<p>To enable rapid iteration, we needed to shorten our feedback loops, and the best way
of doing that is by implementing continuous delivery.</p>
<h2 id="the-principles-of-continuous-delivery">The Principles of Continuous Delivery</h2>
<p>There was quite a but of work to do to enable our solution, continuous delivery has
a bunch of prerequisites and we wanted to abide by a set of core principles that would
guide the technology and process decisions we made.</p>
<h3 id="the-principles-that-guided-us-are">The principles that guided us are:</h3>
<ol>
<li>
<p><strong>Processes MUST be repeatable and reliable.</strong></p>
<ul>
<li>The process by which we deploy our software has been automated, but often full automation of a release can make the business uncomfortable.
To help the business get used to this, we’ve introduced some decision points in our release flow that require manual intervention.</li>
</ul>
</li>
<li>
<p><strong>EVERYTHING has to be automated.</strong></p>
<ul>
<li>The manual decision points we’ve introduced don’t violate this because no process is actually being done manually, we’re just providing business
the opportunity to control the process in more discrete steps. All of our build, test and deployment steps are automated though, that in itself has
reduced our time to any arbitrary environment to about a minute, with full unit, integration and system tests conducted every time we build and deploy.</li>
</ul>
</li>
<li>
<p><strong>You must be able to guarantee quality.</strong></p>
<ul>
<li>The bedrock of any automated process is quality. We guarantee the quality of our code by monitoring our code metrics in Sonarqube - things like
test coverage, technical debt and best practice conformance. Sonarqube breaks builds if they introduce issues or lack test coverage on new code.
Further, in tracking unit test coverage, we also ensure that those tests are run and pass - if you don’t use them, there’s little point spending time
and money creating and maintaining them.</li>
</ul>
</li>
<li>
<p><strong>If something is difficult or painful, it must be done more often. You can’t build muscle without exercise.</strong></p>
<ul>
<li>In forcing ourselves to do painful things often, we worked out the issues in those processes and were able to mitigate or resolve them entirely,
leading to a much more robust process.</li>
</ul>
</li>
<li>
<p><strong>Everything must be expressed as code and versioned.</strong></p>
<ul>
<li>Obviously our application code is all safely in git, versioned and lovely, but it’s important to version and code our build steps and infrastructure as well.
We achieved this using Puppet with Hieradata within a git repository managed with Puppets’ Code Manager, and docker allows us to store our image creation steps in a Dockerfile,
which is stored side by side with the application code itself.</li>
</ul>
</li>
<li>
<p><strong>Things are only considered DONE when they are released to Production.</strong></p>
<ul>
<li>Setting up triggers such that successful deploys off a dev branch trigger builds and deploys on our master branch enabled us to smoothly flow code through our environments in
and automated way. Bamboo gives great deployment functionality that we’ve made good use of.</li>
</ul>
</li>
<li>
<p><strong>Measure quality throughout the process.</strong></p>
<ul>
<li>Tying all of our builds back to Sonarqube enabled us to keep track of quality, we also track metrics around the amount of time it takes to complete the process, ratios of successfull builds
and deploys to failures, and a bunch of other stuff.</li>
</ul>
</li>
<li>
<p><strong>The Continuous Deployment process needs to be continuously improved.</strong></p>
<ul>
<li>We’re still working on the process. It’s important to not forget that there is always another issue we could be improving.</li>
</ul>
</li>
</ol>
<h3 id="the-tools-we-chose">The Tools we chose</h3>
<ul>
<li>
<p><strong>Docker</strong></p>
<ul>
<li>Docker enables us to have superfast builds by building our applications in layers. By building these layers separately, when we build the application, we only have to build the application, rather than
also having to build all of our dependencies and other bits and bobs.</li>
</ul>
</li>
<li>
<p><strong>Rancher</strong></p>
<ul>
<li>We use Rancher for container orchestration and management, it enables us to keep a high level picture of what our applications look like somewhere easy to reach. It enables us to do a whole bunch of really
cool things like load balancing, health checks and the bread and butter of container orchestration. Further, it also has a great repository of applications, stuff like load balancers, CTF stacks, container
discovery tools, even a containerised version of the famous ELK stack. The capability to create your own catalog also exists, so once we’ve got all of our tools and services in to containers, we can spin up
production ready software in no time flat, with all of the quality guarantees built in to our building and deployment pipelines. This is the part of our solution that I’m most excited about and I would recommend
Rancher to anyone interested in container orchestration and management.</li>
</ul>
</li>
<li>
<p><strong>Nexus</strong></p>
<ul>
<li>We use Sonatype Nexus 3 for a bunch of stuff. We have two instances of Nexus, one for integration ready blobs and one for deployment ready blobs. That means that all of our base docker images, sub-images,
and layers we don’t want to send to prod live in our CI Nexus, and the only images that live in our CD instance of Nexus are images that are ready for deployment at a moments notice. We also use
Nexus to mirror Maven Central and the NPM Registry in order to reduce our build times by removing as much network variance as possible. We also keep build dependencies in our CI Nexus, blobs like Servicemix
archives, SoapUI archives and others.</li>
</ul>
</li>
<li>
<p><strong>Bamboo</strong></p>
<ul>
<li>We use Bamboo to wire up our builds, tests and deploys. One thing that irks me about Bamboo is it’s lack of ability to verify a deployment and conduct system and smoke tests. That being said,
it’s got an awesome build system and the a strong deploy system that allow us to tie any image running in production all the way back through the pipe, to the commit that triggered the build.
That provides us with a great deal more visibility as to what code is where when compared with our old process.</li>
</ul>
</li>
<li>
<p><strong>Bitbucket</strong></p>
<ul>
<li>Git and Bitbucket are the solid but silent performers. We use a Feature branching workflow with pull requests - we require that the only change to master is a pull request from dev, and code only gets in to
dev via pull requests. Each request must have a minimum of two reviewers, no issues or tasks against them, and a passing bamboo build.</li>
</ul>
</li>
<li>
<p><strong>SonarQube</strong></p>
<ul>
<li>Sonarqube enables us to track issues with our code, and break builds if quality gates aren’t met. It’s also a great tool to communicate the health of our code base to the rest of the business.</li>
</ul>
</li>
</ul>
<h2 id="system-map">System Map</h2>
<p>Here’s the system we built, end to end. It’s a bit busy but it is quite compact, and looking at it, I can’t see anything I would take away to make it simpler, which I think is a good thing. It’s as simple as it can
be while still being powerful enough to deal with any deployment requirement we might have.</p>
<p><img alt="Healthlink System Map" loading="lazy" src="/img/in-post/cd-system-map.png"></p>
<p>What value has the business derived from this project?</p>
<p>Applications that use our CD Pipeline gain the following benefits:</p>
<ol>
<li>The application can be built and deployed to any arbitrary environment in less then two minutes in most cases, and we’ve yet to see a build and deployment take longer than four.</li>
<li>Batch sizes are smaller, so deploys are safer</li>
<li>Deploys can be rolled back in a single click, in the unlikely event of a failure</li>
</ol>
<p>The business benefits derived from this then are:</p>
<ul>
<li>Vastly cheaper deployments, we’re saving about $800 every deployment on every project that uses this flow.</li>
<li>Our time to production, where customers use our software, is under half an hour. The benefits of being to get feedback and iterate quickly provide us with an unfair advantage over the competition. I like unfair advantages.</li>
<li>Software Quality is way up, which means less issues in production, less time spent supporting that software and less time spent maintaining that software.</li>
</ul>
<p>I feel like that’s some pretty compelling stuff.</p>
]]></content:encoded></item><item><title>DevOps Days NZ 2016</title><link>https://4lex.nz/posts/dev-ops-days-wellington/</link><pubDate>Wed, 05 Oct 2016 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/dev-ops-days-wellington/</guid><description>&lt;p&gt;On Friday 30th September, a colleague and I spoke at Devops Days 2016 in Wellington.&lt;/p&gt;
&lt;p&gt;It was an awesome experience, and the first time I&amp;rsquo;ve spoken on technical and non-technical topics at an industry conference.&lt;/p&gt;
&lt;p&gt;The talk I gave is available on &lt;a href="https://speakerdeck.com/alexandernz/health-is-hard" target="_blank" rel="noopener noreferrer"&gt;Speakerdeck&lt;/a&gt;
. I&amp;rsquo;ve also embedded it below:&lt;/p&gt;
&lt;div class="speakerdeck-embed" style="position: relative; padding-bottom: 56.2500%; height: 0; overflow: hidden;"&gt;
&lt;iframe
src="https://speakerdeck.com/player/bf1d128fcc4741efbe9b2313be9ecfdf"
title="SpeakerDeck presentation bf1d128fcc4741efbe9b2313be9ecfdf"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen
loading="lazy"
referrerpolicy="strict-origin-when-cross-origin"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"
&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;I think that the one thing that I took away from &lt;a href="https://www.devopsdays.org/events/2016-newzealand/welcome/" target="_blank" rel="noopener noreferrer"&gt;Devops Days NZ 2016&lt;/a&gt;
was this.&lt;/p&gt;</description><content:encoded><![CDATA[<p>On Friday 30th September, a colleague and I spoke at Devops Days 2016 in Wellington.</p>
<p>It was an awesome experience, and the first time I&rsquo;ve spoken on technical and non-technical topics at an industry conference.</p>
<p>The talk I gave is available on <a href="https://speakerdeck.com/alexandernz/health-is-hard" target="_blank" rel="noopener noreferrer">Speakerdeck</a>

. I&rsquo;ve also embedded it below:</p>
<div class="speakerdeck-embed" style="position: relative; padding-bottom: 56.2500%; height: 0; overflow: hidden;">
  <iframe
    src="https://speakerdeck.com/player/bf1d128fcc4741efbe9b2313be9ecfdf"
    title="SpeakerDeck presentation bf1d128fcc4741efbe9b2313be9ecfdf"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    allowfullscreen
    loading="lazy"
    referrerpolicy="strict-origin-when-cross-origin"
    style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"
  ></iframe>
</div>


<p>I think that the one thing that I took away from <a href="https://www.devopsdays.org/events/2016-newzealand/welcome/" target="_blank" rel="noopener noreferrer">Devops Days NZ 2016</a>

 was this.</p>
<p><em>It&rsquo;s very easy to get wrapped up in your own problems in this industry.
It&rsquo;s very easy to feel like your problems are unique, new or otherwise unreproducible. Unless you&rsquo;re Google, Netflix or of that ilk, this is very unlikely.
You and I, we&rsquo;re not so different. Maybe it&rsquo;s health, maybe it&rsquo;s media, maybe it&rsquo;s any other industry. We&rsquo;re all going through the same things, at different times.
It&rsquo;s important that we continue to work through these diverse issues as best we can, while knowing that someone out there has felt, is now feeling or will feel your pain.</em></p>
<p><em>You are not alone!</em></p>
<p><em>In the spirit of DevOps and all the baggage that that word contains and frees us from, let&rsquo;s keep helping each other, let&rsquo;s keep sharing, we&rsquo;re a community of like minded people.</em></p>
]]></content:encoded></item><item><title>Overtime's Tech Stack</title><link>https://4lex.nz/posts/overtime-tech/</link><pubDate>Thu, 14 Jan 2016 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/overtime-tech/</guid><description>&lt;p&gt;Overtime New Zealand is a social video game league for corporates. Companies get together and play games against each other
for bragging rights, e-peen and other critically important reasons, like proving that you&amp;rsquo;re better at Dota 2 then X_MAIN_RIVAL_FIRM.&lt;/p&gt;
&lt;p&gt;So without Further ado, here&amp;rsquo;s what we&amp;rsquo;re running at the moment.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Technology Overview" loading="lazy" src="https://4lex.nz/img/in-post/overtime-stack.png"&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll go through our components technology by technology:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Most importantly, Docker&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We couldn&amp;rsquo;t do what we wanted to do with Overtime&amp;rsquo;s CI / CD without docker.
Initially I created docker-compose containers for instances of Docker Registry, Nginx, and MongoDB and our Web App. The plan was to containerise
those components because it would allow us to scale up instances of our web app and MongoDB in case our load got crazy high. An ambitious design requirement,
but much better for the long run. In fact nearly every design decision we&amp;rsquo;ve made technologically has been focused on &amp;ldquo;What will make our lives easier in the long term&amp;rdquo;.
We do this voluntarily so making money immediately and as quickly as possible isn&amp;rsquo;t such a huge issue for us! It&amp;rsquo;s nice.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Overtime New Zealand is a social video game league for corporates. Companies get together and play games against each other
for bragging rights, e-peen and other critically important reasons, like proving that you&rsquo;re better at Dota 2 then X_MAIN_RIVAL_FIRM.</p>
<p>So without Further ado, here&rsquo;s what we&rsquo;re running at the moment.</p>
<p><img alt="Technology Overview" loading="lazy" src="/img/in-post/overtime-stack.png"></p>
<p>I&rsquo;ll go through our components technology by technology:</p>
<p><strong>Most importantly, Docker</strong></p>
<p>We couldn&rsquo;t do what we wanted to do with Overtime&rsquo;s CI / CD without docker.
Initially I created docker-compose containers for instances of Docker Registry, Nginx, and MongoDB and our Web App. The plan was to containerise
those components because it would allow us to scale up instances of our web app and MongoDB in case our load got crazy high. An ambitious design requirement,
but much better for the long run. In fact nearly every design decision we&rsquo;ve made technologically has been focused on &ldquo;What will make our lives easier in the long term&rdquo;.
We do this voluntarily so making money immediately and as quickly as possible isn&rsquo;t such a huge issue for us! It&rsquo;s nice.</p>
<p>We had a bunch of technical issues with Nginx not liking being containerised and not linking to our other containers properly.
That resulted in us shifting it to the VPS itself and using it for reverse proxy and routing, it&rsquo;s working pretty nicely so far.</p>
<p>We run separate production and test environments. Local builds are automatically built and dockerised on commit,
are automatically deployed to our test environment with 0 loss of service. When we&rsquo;re ready to go to prod, we just retag
the docker image in question as prod rather than test. Docker picks up that there is a new image available and redeploys
in the same way as our test environment, meaning we also get 0 down time. We still plan our deployment windows for early
in the morning just in case something breaks but the process seems pretty solid so far.</p>
<p><strong>Our Web App</strong></p>
<p>Overtime uses a Java 8 back end with an Angular front end. Once the application is complete, players will be able to
register themselves and their teams, sign up for tournaments, track points, rankings tables identities, and communicate with
organisers and other teams. We have a dedicated slack team for overtime and we&rsquo;re planning on our players being automatically enrolled into slack
on sign up. I also want the enrollment process to add them in to relevant channels (they might get a private team channel
and enrollment into open game and interest channels) - but we&rsquo;re still thinking about how that would work!</p>
<p>We&rsquo;re using MongoDB because we really like it&rsquo;s object paradigm, it&rsquo;ll make it easier for us to manage our data long term.</p>
<p><strong>Speaking of building:</strong></p>
<p>We use Bamboo for our CI/CD work, initially we used Jenkins but found it unreliable, prone to random failures and difficult
to set up, maintain and extend. Bamboo is a much more user friendly solution. We had set up our build server as an additional
docker container on the first VPS but thinking about it, we wanted to ensure that development work could never impact production
environments. Because of the size of our VPS it was possible that our build container could cause production issues by hogging
up resources while building things. An edge case but with the focus on the long term we decided on separating that out on to it&rsquo;s
own VPS with its own resources.</p>
<p><strong>Project Management</strong></p>
<p>We use Taiga.io for project management. It has the option for sprint or kanban set ups, it&rsquo;s a hosted solution and it&rsquo;s free, which fits our budget!
Taiga is also open source which means that we can take it, self-host and extend it should we ever need to provided we comply with the licence, which is another bonus.</p>
<p><strong>Wrap Up</strong></p>
<p>That&rsquo;s all the detail I&rsquo;ll go in to for now. I hope this helped you, if you want any more detail or you&rsquo;d like help
with your own projects, let me know, I&rsquo;d love to see how I can help!</p>
]]></content:encoded></item><item><title>Continuous Delivery</title><link>https://4lex.nz/posts/ci-cd/</link><pubDate>Wed, 28 Oct 2015 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/ci-cd/</guid><description>&lt;p&gt;The work that I&amp;rsquo;ve been doing for the past few months has been pretty interesting. At the moment it takes developers working
on one of our ventures about an hour and a half to two hours to deploy code to a test or prod machine. It used to take
a second venture a similar amount of time to do the same task, but the process we&amp;rsquo;ve implemented for them has reduced that
significantly. Once the process is completed across both ventures, our team will have reduced that deployment time enough,
that the product owners for those ventures will effectively gain 20% more points for work per sprint.&lt;/p&gt;</description><content:encoded><![CDATA[<p>The work that I&rsquo;ve been doing for the past few months has been pretty interesting. At the moment it takes developers working
on one of our ventures about an hour and a half to two hours to deploy code to a test or prod machine. It used to take
a second venture a similar amount of time to do the same task, but the process we&rsquo;ve implemented for them has reduced that
significantly. Once the process is completed across both ventures, our team will have reduced that deployment time enough,
that the product owners for those ventures will effectively gain 20% more points for work per sprint.</p>
<p><em><em>That&rsquo;s a pretty huge productivity increase</em></em></p>
<p>Especially given that it was two juniors and two seniors across three months that did this work.</p>
<p>We&rsquo;re not done yet, we&rsquo;ve still got some UAT and final bits and pieces to deal with, but I&rsquo;m proud to say that we&rsquo;re mostly there.</p>
<p><strong>Here&rsquo;s a basic run down of how we did it:</strong></p>
<p>We chose Atlassian&rsquo;s Bamboo as our general purpose task runner. It&rsquo;s the backbone of our process. We chose it because
it&rsquo;s got a nice interface, awesome integration with Bitbucket (which all of our repos are hosted on) as well as Jira and Confluence.</p>
<p>It can provide us with automatically generated information about the issues addressed by a deployment, link back to repos if we need
to sanity check source code and a bunch of other cool stuff. We&rsquo;ve also added webhooks to our deployment channel within Slack, so
Bamboo posts alerts to that channel when a deployment completes.</p>
<p>We&rsquo;re stuck with a bunch of technologies that aren&rsquo;t being used correctly (Like using Puppet to manage individual machines
rather than types of machines) and Bamboo is going to give us freedom to swap out discrete pieces of the process as and when we need.</p>
<p>We&rsquo;ve got a bunch of scripts and SSH tasks that reach out to our repos, check file versions, run puppet agents (yes, manually running rather than every n minutes),
stop queues, dump databases for backup purposes, start queues and all sorts of other nebulous tasks.</p>
<p>The end result is that to build a venture project, all a developer needs to do is commit code. To cut a release they need
to select a branch to build. To deploy to a dev, test or staging environment they need to select a build result and click deploy, same deal for pre-prod and prod (with a few additional caveats).</p>
<p><strong>The great thing is, the teams are onboard with it.</strong></p>
<p>This is a huge amount of disruption to the way 80% of our team does business. It affects developers, testers, BAs and product owners.
Without exception, everyone that I&rsquo;ve talked to is pumped. It&rsquo;s a credit to the guys here that they aren&rsquo;t stuck in a process but are always looking
to improve. It makes me happy.</p>
]]></content:encoded></item></channel></rss>