<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>GitLab</title>
  <id>https://about.gitlab.com/blog/</id>
  <link href="https://about.gitlab.com/blog/"/>
  <updated>2016-12-22T00:00:00+00:00</updated>
  <author>
    <name>Blog Author</name>
  </author>
  <entry>
    <title>GitLab 8.15 Released with Auto Deploy and Web Terminal</title>
    <link rel="alternate" href="https://about.gitlab.com/2016/12/22/gitlab-8-15-released/"/>
    <id>https://about.gitlab.com/2016/12/22/gitlab-8-15-released/</id>
    <published>2016-12-22T00:00:00+00:00</published>
    <updated>2016-12-22T00:00:00+00:00</updated>
    <author>
      <name>Job van der Voort</name>
    </author>
    <content type="html">
&lt;p&gt;With this last release of the year, we&#x27;re not only completing our &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;2016&#x2F;09&#x2F;14&#x2F;gitlab-live-event-recap&#x2F;&quot;&gt;Master Plan&lt;&#x2F;a&gt;, but we
want to show you something cool we&#x27;ve been working on:&lt;&#x2F;p&gt;

&lt;figure class=&quot;video_container&quot;&gt;
  &lt;iframe src=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;embed&#x2F;m0nYHPue5RU&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot;&gt; &lt;&#x2F;iframe&gt;
&lt;&#x2F;figure&gt;



&lt;p&gt;&lt;br &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;p&gt;With GitLab 8.15, we&#x27;re introducing Auto Deploy (shown in the video at
&lt;a href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;m0nYHPue5RU?t=102&quot;&gt;1:42&lt;&#x2F;a&gt;), which automates setting up deploys and review apps. For a
Ruby on Rails project, this brings setup time to under a minute.&lt;&#x2F;p&gt;

&lt;p&gt;Plus, to make accessing your environments easier and faster, you can now
access them directly through a terminal in GitLab (shown in the video at &lt;a href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;m0nYHPue5RU?t=318&quot;&gt;5:18&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;

&lt;p&gt;We want to give the power of containers, continuous integration and deployment,
review apps and container schedulers to everyone. With GitLab 8.15, we
take away all the hard work without hiding anything. In the demonstration we
setup and deploy a Ruby application with review apps, multiple environments,
chatops to a Kubernetes cluster in about 12 minutes. This typically takes days,
if not weeks to setup and use without GitLab.&lt;&#x2F;p&gt;

&lt;p&gt;For many people, December is a month of celebration and gift giving.
This month, GitLab was again so lucky to receive many great contributions.&lt;&#x2F;p&gt;

&lt;p&gt;The MVP of this month is &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;Munken&quot;&gt;Michael Munch&lt;&#x2F;a&gt; for bringing beautifully-typeset math
(LaTeX) to GitLab. Michael worked over a period of 6 months in merge requests
with in total more than three hundred comments to bring this to GitLab.&lt;&#x2F;p&gt;

&lt;p&gt;We&#x27;d also like to thank &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;warren.postma&quot;&gt;Warren Postma&lt;&#x2F;a&gt; for his contributions on the issue trackers, and the &lt;a href=&quot;https:&#x2F;&#x2F;forum.gitlab.com&#x2F;users&#x2F;warren.postma&#x2F;activity&quot;&gt;community forum&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;warren.postma&#x2F;gitlab-ce&#x2F;wikis&#x2F;home&quot;&gt;generally being a great advocate of the product and the company&lt;&#x2F;a&gt;.
And lastly, we want to thank &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;glensc&quot;&gt;Elan Ruusamäe&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;dirker&quot;&gt;Dirk Hörner&lt;&#x2F;a&gt; for their
contributions in technical design and implementation that have given super
powers to Git Hooks.&lt;&#x2F;p&gt;

&lt;p&gt;Thanks Warren, Michael, Elan, and Dirk!&lt;&#x2F;p&gt;

&lt;h2 id=&quot;auto-deploy&quot;&gt;Auto Deploy&lt;&#x2F;h2&gt;

&lt;p&gt;We want everyone to quickly get a fully functioning CI&#x2F;CD pipeline that deploys
to a container scheduler. It shouldn&#x27;t require any effort to get started,
but should also be scalable and not hide any of the magic.&lt;&#x2F;p&gt;

&lt;p&gt;Auto Deploy does this. Auto Deploy adds a single button to your project,
that when clicked, will create a merge request with a template that will
automatically deploy your application using Docker to your container scheduler.
The cool thing about this is that this immediately leverages Review Apps,
meaning you can see it working before even merging the merge request!&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;8_15&#x2F;auto_deploy.png&quot; alt=&quot;Auto deploy in GitLab 8.15&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;p&gt;This is as close as you can get to one-click deploys, while exposing what
is happening and having all this version-controlled, ready
to collaborate and iterate on.&lt;&#x2F;p&gt;

&lt;p&gt;See &lt;a href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;m0nYHPue5RU?t=102&quot;&gt;1:42&lt;&#x2F;a&gt; in the video for a quick demo of
Auto Deploy, as it is available in GitLab 8.15.&lt;&#x2F;p&gt;

&lt;p&gt;For this first iteration, we ship Auto Deploy with a template for deploying to an external OpenShift cluster. We use &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gliderlabs&#x2F;herokuish&quot;&gt;Herokuish&lt;&#x2F;a&gt; and Heroku Buildpacks to
package your application into a Docker Image that then is deployed to
Kubernetes on Openshift. We want to add support for more container
schedulers and cloud platforms later (vanilla Kubernetes cluster, Mesos, Docker
Swarm). Contributions are very welcome in &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ci-yml&quot;&gt;our template repository&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;blockquote&gt;
  &lt;p&gt;See &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;autodeploy&#x2F;index.html&quot;&gt;the Autodeploy documentation&lt;&#x2F;a&gt; for more information.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;h2 id=&quot;web-terminal&quot;&gt;Web Terminal&lt;&#x2F;h2&gt;

&lt;p&gt;Working together with your container scheduler, GitLab happily spins up several
(dynamic) environments on request for your projects. Be that for review apps
or a staging or production environment.
Traditionally, getting direct access to these environments has been a little
painful. And that&#x27;s a shame: it&#x27;s very useful to quickly try something in a
live environment to debug a problem, or just to experiment.&lt;&#x2F;p&gt;

&lt;p&gt;With the web terminal, this has become extremely easy. Just visit the
environments page in your project and click on the terminal button.
GitLab will SSH into the instance for you and allow you to do anything
you would be able to do from your local instance.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;8_15&#x2F;terminal_gif.gif&quot; alt=&quot;Web Terminal in GitLab 8.15&quot; class=&quot;shadow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;p&gt;In the demonstration at &lt;a href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;m0nYHPue5RU?t=318&quot;&gt;5:18&lt;&#x2F;a&gt; we show you
give you a quick peek at the web terminal. We can&#x27;t wait to see how you&#x27;ll
use it to speed up your workflow.&lt;&#x2F;p&gt;

&lt;blockquote&gt;
  &lt;p&gt;See the &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;administration&#x2F;integration&#x2F;terminal.html&quot;&gt;administrator&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;environments.html#terminal-support&quot;&gt;environments&lt;&#x2F;a&gt; documentation on the web terminal&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;h2 id=&quot;improved-bitbucket-importer&quot;&gt;Improved Bitbucket Importer&lt;&#x2F;h2&gt;

&lt;p&gt;Importing from Bitbucket has become even more powerful. With GitLab 8.15 we&#x27;ll
also import all pull requests as individual merge requests plus pull
request comments, milestones and the wiki from Bitbucket.
That makes the things we import from any Bitbucket project:&lt;&#x2F;p&gt;

&lt;ul&gt;
  &lt;li&gt;Repository description&lt;&#x2F;li&gt;
  &lt;li&gt;Git repository&lt;&#x2F;li&gt;
  &lt;li&gt;Issues and their comments&lt;&#x2F;li&gt;
  &lt;li&gt;Pull Requests and their comments&lt;&#x2F;li&gt;
  &lt;li&gt;Milestones&lt;&#x2F;li&gt;
  &lt;li&gt;Wiki&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;p&gt;When importing a project all references to pull requests and issues are
preserved and so is the access level (public&#x2F;private).&lt;&#x2F;p&gt;

&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;workflow&#x2F;importing&#x2F;import_projects_from_bitbucket.html&quot;&gt;Read the docs on the Bitbucket importer&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;

&lt;h2 id=&quot;global-git-hooks&quot;&gt;Global Git Hooks&lt;&#x2F;h2&gt;

&lt;p&gt;If you want to enforce rules and triggers based on Git pushes and their
contents, you have been able to use custom Git hooks in GitLab. But if you
want to standardize on these rules, you&#x27;d have to copy and paste them
to every new project.&lt;&#x2F;p&gt;

&lt;p&gt;With Global Git Hooks you can now create Git hooks that will be run
for each project on the GitLab instance. This should make it much easier
to set up rules that you want all incoming code to comply with.&lt;&#x2F;p&gt;

&lt;p&gt;Create the hooks in &lt;code&gt;hooks&#x2F;&amp;lt;hook_name&amp;gt;.d&#x2F;&lt;&#x2F;code&gt; directory or tell GitLab Shell where the directories are.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;administration&#x2F;custom_hooks.html&quot;&gt;See the documentation on Custom Hooks&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;

&lt;h2 id=&quot;chained-custom-git-hooks&quot;&gt;Chained Custom Git Hooks&lt;&#x2F;h2&gt;

&lt;p&gt;If you have set up custom Git hooks, the order might matter: when an initial
hook fails, there is no point in moving forward. With Chained Custom Hooks
hooks will execute in lexical order, failing on the first failing script.&lt;&#x2F;p&gt;

&lt;p&gt;This means you can create hooks &lt;code&gt;1-hook.sh&lt;&#x2F;code&gt; and &lt;code&gt;2-hook.sh&lt;&#x2F;code&gt; and you&#x27;ll know that
&lt;code&gt;1&lt;&#x2F;code&gt; will execute before &lt;code&gt;2&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;This gives a lot of power to your hooks and allows endless customization of
what happens when a commit is about to land in your GitLab project.&lt;&#x2F;p&gt;

&lt;p&gt;For more information, see the &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;administration&#x2F;custom_hooks.html#chained-hooks-support&quot;&gt;custom git hooks documentation&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;Thanks to both Elan Ruusamäe and Dirk Hörner for both helping to define Global Git Hooks and this feature, and providing the implementation!&lt;&#x2F;p&gt;

&lt;h2 id=&quot;ldap-group-membership-overrides-ee&quot;&gt;LDAP Group membership overrides (EE)&lt;&#x2F;h2&gt;

&lt;p&gt;In GitLab EE, you can sync any LDAP group with any GitLab group and give
everyone automatically a certain permission. For instance, you
can give everyone in the LDAP group &lt;code&gt;developers&lt;&#x2F;code&gt; the &lt;code&gt;Developer&lt;&#x2F;code&gt;
permission level. New developers just have to be added to the LDAP
group and GitLab will take care of giving them access automatically.&lt;&#x2F;p&gt;

&lt;p&gt;With GitLab 8.15, this has become even more powerful. In addition to the
automatically synced permissions, you can now override this permission
per user. This should make it easier to manage complex permissions across
groups and projects.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;8_15&#x2F;ldap_overrides.gif&quot; alt=&quot;Override LDAP permissions in GitLab 8.15 EE&quot; class=&quot;shadow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;h2 id=&quot;slack-chatops&quot;&gt;Slack Chatops&lt;&#x2F;h2&gt;

&lt;p&gt;After bringing Chatops to GitLab with &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;2016&#x2F;11&#x2F;22&#x2F;gitlab-8-14-released&#x2F;#chat-commands-experimental&quot;&gt;our Mattermost integration&lt;&#x2F;a&gt;,
we&#x27;re now doing the same for everyone using Slack! This
means you can create, show and search for issues straight from Slack. Super
convenient to bridge that gap from a casual conversation to an actual issue.&lt;&#x2F;p&gt;

&lt;p&gt;In addition, you can actually deploy from and to any environment.
For instance, you can do&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;&#x2F;awesome-website deploy from staging to production
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And GitLab will deploy the latest commit from staging onto master.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;8_15&#x2F;slack.png&quot; alt=&quot;Slack Chatops in GitLab 8.15&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;p&gt;Configure Chatops for Slack in your project services. As always,
we&#x27;re looking forward to see contributions to help expand the power of
chat integration with GitLab!&lt;&#x2F;p&gt;

&lt;h2 id=&quot;one-click-mattermost-configuration&quot;&gt;One-click Mattermost configuration&lt;&#x2F;h2&gt;

&lt;p&gt;Setting up the &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;2016&#x2F;11&#x2F;22&#x2F;gitlab-8-14-released&#x2F;#chat-commands-experimental&quot;&gt;awesome Mattermost integration&lt;&#x2F;a&gt; with GitLab has been made
much easier. It&#x27;s a matter of a single click now, as you can see &lt;a href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;m0nYHPue5RU?t=196&quot;&gt;in the video at 3:16&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;8_15&#x2F;mattermost.png&quot; alt=&quot;Super easy Mattermost configuration in GitLab 8.15&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;p&gt;The Mattermost and Slack integrations allow you to do the same things:
create, show and search for issues and deploy to any environment.&lt;&#x2F;p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;help&#x2F;project_services&#x2F;mattermost_slash_commands.md&quot;&gt;Read the docs on the Mattermost Chatops integration&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;h2 id=&quot;diff-in-notification-emails&quot;&gt;Diff in Notification Emails&lt;&#x2F;h2&gt;

&lt;p&gt;When you get a notification email from a comment on a diff, GitLab will now
send along a bit of the diff, so you have the correct context immediately
available.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;8_15&#x2F;diff_emails.png&quot; alt=&quot;Diffs in Notification emails in GitLab 8.15&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;h2 id=&quot;interface-improvements&quot;&gt;Interface improvements&lt;&#x2F;h2&gt;

&lt;p&gt;Our amazing usability team has been working hard on making GitLab easier to use,
enhancing GitLab&#x27;s personality and improving readability. This release contains
various changes that will make using GitLab more enjoyable!&lt;&#x2F;p&gt;

&lt;h3 id=&quot;fresh-typefaces&quot;&gt;Fresh typefaces!&lt;&#x2F;h3&gt;

&lt;p&gt;To improve readability and cross-OS&#x2F;browser support in GitLab, we have
changed to using system fonts. These fonts are optimized for your platform
and should therefore provide a better experience, independent of where you&#x27;re
viewing GitLab from.&lt;&#x2F;p&gt;

&lt;p&gt;If you want to get a feel of GitLab across platforms, &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7545&quot;&gt;check out the original merge request&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;h3 id=&quot;slimmed-down-widths&quot;&gt;Slimmed down widths&lt;&#x2F;h3&gt;

&lt;p&gt;We&#x27;ve reduced the max-width for issues and merge requests container in order to
provide a more readable line length. This is the first step for us to correct
the huge line lengths you see throughout GitLab.
Follow our &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;issues&#x2F;13680&quot;&gt;meta issue&lt;&#x2F;a&gt;
as we continue to improve GitLab&#x27;s line length.&lt;&#x2F;p&gt;

&lt;h3 id=&quot;unique-labels&quot;&gt;Unique Labels&lt;&#x2F;h3&gt;

&lt;p&gt;We&#x27;ve given labels a unique look in order to differentiate them from buttons.
We are continuing work on this to make our
&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;issues&#x2F;25518&quot;&gt;labels&lt;&#x2F;a&gt; and
&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;issues&#x2F;25564&quot;&gt;status badges&lt;&#x2F;a&gt;
even more consistent in further iterations.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;8_15&#x2F;labels.png&quot; alt=&quot;Unique Labels in GitLab 8.15&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;h3 id=&quot;improved-build-scrolling-and-loading&quot;&gt;Improved Build Scrolling and Loading&lt;&#x2F;h3&gt;

&lt;p&gt;Build scrolling and loading works and looks quite a bit better now:&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;8_15&#x2F;build_scroll.gif&quot; alt=&quot;Improved Build Scrolling and Loading in GitLab 8.15&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;h3 id=&quot;smaller-page-size&quot;&gt;Smaller page size&lt;&#x2F;h3&gt;

&lt;p&gt;Between system fonts and improvements to autocomplete, we&#x27;ve reduced the
average page size of any page in GitLab significantly. A given merge request
in the GitLab CE project went from 1800kb to now 718kb!&lt;&#x2F;p&gt;

&lt;h3 id=&quot;improved-empty-states&quot;&gt;Improved empty states&lt;&#x2F;h3&gt;

&lt;p&gt;In order to improve our experience for users who are just getting started with
GitLab, we&#x27;ve added informative and fun empty states to many of our pages
across the application!
View our &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;issues&#x2F;15632&quot;&gt;meta issue&lt;&#x2F;a&gt; to
see where we&#x27;ve placed new empty states and feel free to suggest new ones!&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;8_15&#x2F;empty.png&quot; alt=&quot;Even empty is beautiful in GitLab 8.15&quot; class=&quot;shadow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;h3 id=&quot;other-changes-and-tweaks&quot;&gt;Other changes and tweaks&lt;&#x2F;h3&gt;

&lt;ul&gt;
  &lt;li&gt;Improved accessibility by adding a focus state to dropdown options&lt;&#x2F;li&gt;
  &lt;li&gt;Added hover states to our primary navigation and tabs throughout the site.&lt;&#x2F;li&gt;
  &lt;li&gt;Improved hover, focus, and active states for buttons &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7797&quot;&gt;!7797&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;Added hover states to collapsed items with the issues&#x2F;mr&#x27;s sidebar &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7777&quot;&gt;!7777&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h2 id=&quot;mathematics-support-for-markdown-and-asciidoc-using-katex&quot;&gt;Mathematics support for Markdown and AsciiDoc, using KaTeX&lt;&#x2F;h2&gt;

&lt;p&gt;Comments and repository files can now contain beautifully-typeset mathematics, using the &lt;a href=&quot;https:&#x2F;&#x2F;khan.github.io&#x2F;KaTeX&#x2F;&quot;&gt;KaTeX&lt;&#x2F;a&gt; library from Khan Academy.&lt;&#x2F;p&gt;

&lt;p&gt;To render inline mathematics, use dollar signs around inline code: &lt;code&gt;$`a^2+b^2=c^2`$&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;

&lt;p&gt;To render multiline mathematics, use the &lt;code&gt;math&lt;&#x2F;code&gt; language for the code block:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;```math
a^2+b^2=c^2
```
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In addition to working for Markdown, this is also available for AsciiDoc documents. &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;user&#x2F;markdown.html#math&quot;&gt;Read the documentation on mathematics support.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;8_15&#x2F;math.png&quot; alt=&quot;Beautifully rendered math in GitLab 8.15&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;p&gt;Thanks to Michael Munch for this feature!&lt;&#x2F;p&gt;

&lt;h2 id=&quot;cleaner-merge-commit-messages&quot;&gt;Cleaner merge commit messages&lt;&#x2F;h2&gt;

&lt;p&gt;Previously, merge commit messages included the title and description of the
merge request, and a reference to the merge request. This didn&#x27;t read well when
using &lt;code&gt;git log&lt;&#x2F;code&gt; and similar tools, because merge request descriptions often
contain requests for review, screenshots, and other details incidental to the
code change.&lt;&#x2F;p&gt;

&lt;p&gt;Now, the default merge commit message is in the following format:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;Merge branch &#x27;$SOURCE_BRANCH&#x27; into &#x27;$TARGET_BRANCH&#x27;

$TITLE

Closes $CLOSING_ISSUE_REFERENCES # only present if the MR closes issues

See merge request $MERGE_REQUEST_REFERENCE
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The previous default message is available as an option when customizing the
merge commit message.&lt;&#x2F;p&gt;

&lt;p&gt;Thanks to Gabriel Gizotti!&lt;&#x2F;p&gt;

&lt;h2 id=&quot;shorthand-cross-project-references-in-gitlab-flavored-markdown&quot;&gt;Shorthand cross-project references in GitLab Flavored Markdown&lt;&#x2F;h2&gt;

&lt;p&gt;Previously, a reference to something in another project always included the
namespace, even if the project was in the same namespace.&lt;&#x2F;p&gt;

&lt;p&gt;Now, shorthand references are available. So from within the
&lt;code&gt;gitlab-org&#x2F;gitlab-ce&lt;&#x2F;code&gt; project, you can refer to issue #1 in GitLab Workhorse
by writing &lt;code&gt;gitlab-workhorse#1&lt;&#x2F;code&gt; instead of &lt;code&gt;gitlab-org&#x2F;gitlab-workhorse#1&lt;&#x2F;code&gt;,
saving precious keystrokes!&lt;&#x2F;p&gt;

&lt;p&gt;For more information, see the &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;user&#x2F;markdown.html#special-gitlab-references&quot;&gt;special GitLab references&lt;&#x2F;a&gt; section of our Markdown documentation.&lt;&#x2F;p&gt;

&lt;p&gt;Thanks to Oswaldo Ferreira!&lt;&#x2F;p&gt;

&lt;h2 id=&quot;create-an-issue-with-unresolved-discussion-from-a-merge-request&quot;&gt;Create an issue with unresolved discussion from a merge request&lt;&#x2F;h2&gt;

&lt;p&gt;In 8.14, we &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;2016&#x2F;11&#x2F;22&#x2F;gitlab-8-14-released&#x2F;#prevent-merge-until-review-is-done&quot;&gt;added the ability to block a merge when there are unresolved discussions&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;Now, we have added an option to &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;user&#x2F;project&#x2F;merge_requests&#x2F;merge_request_discussion_resolution.html#move-all-unresolved-discussions-in-a-merge-request-to-an-issue&quot;&gt;create a new issue from the unresolved discussions in a merge request&lt;&#x2F;a&gt;, and resolve those discussions at the same time! This is perfect for those cases where you need to merge something now, but don&#x27;t want to forget about the code review comments.&lt;&#x2F;p&gt;

&lt;p&gt;Thanks to Bob van Landuyt!&lt;&#x2F;p&gt;

&lt;h2 id=&quot;manual-actions-from-the-pipeline-graph&quot;&gt;Manual Actions from the Pipeline Graph&lt;&#x2F;h2&gt;

&lt;p&gt;Manual actions allow you to require manual interaction before moving
forward with a particular job in CI. Your entire pipeline can run
automatically, but the actual deploy to production will require a click.&lt;&#x2F;p&gt;

&lt;p&gt;You can do this straight from the pipeline graph. Just click on the play
button to execute that particular job.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;8_15&#x2F;manual_actions_graph.png&quot; alt=&quot;Manual actions on the pipeline graph&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;h2 id=&quot;user-activities-api&quot;&gt;User Activities API&lt;&#x2F;h2&gt;

&lt;p&gt;To quickly get an idea of when a user last interacted with GitLab,
we&#x27;ve added a special admin-only API to GitLab that allows you to get
the last activity timestamp of every user on the instance.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ee&#x2F;api&#x2F;users.html#get-user-activities-admin-only&quot;&gt;Find the details in the docs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;

&lt;h2 id=&quot;sort-project-and-group-members&quot;&gt;Sort Project and Group members&lt;&#x2F;h2&gt;

&lt;p&gt;You can now find people in projects and groups more easily by sorting them
by name, access level and date of joining.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;8_15&#x2F;sort_members.png&quot; alt=&quot;Easily find people in projects and groups in GitLab 8.15&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;h2 id=&quot;api-changes&quot;&gt;API Changes&lt;&#x2F;h2&gt;

&lt;p&gt;Every month we make many additions to our API. Here are the highlights
for GitLab 8.15:&lt;&#x2F;p&gt;

&lt;ul&gt;
  &lt;li&gt;Allow some Project API GET endpoints to be requested anonymously&lt;&#x2F;li&gt;
  &lt;li&gt;Allow Repositories &amp;amp; Files API GET endpoints to be requested anonymously&lt;&#x2F;li&gt;
  &lt;li&gt;Allow some Tag API GET endpoints to be requested anonymously&lt;&#x2F;li&gt;
  &lt;li&gt;Add scopes for personal access tokens and OAuth tokens&lt;&#x2F;li&gt;
  &lt;li&gt;Add ability to cherry pick a commit (community contribution)&lt;&#x2F;li&gt;
  &lt;li&gt;Add ability to unshare a project from a group (community contribution)&lt;&#x2F;li&gt;
  &lt;li&gt;Add ability to set &lt;code&gt;should_remove_source_branch&lt;&#x2F;code&gt; on merge requests (community contribution)&lt;&#x2F;li&gt;
  &lt;li&gt;Add simple representation of group&#x27;s projects (community contribution)&lt;&#x2F;li&gt;
  &lt;li&gt;Expose committer details for commits (community contribution)&lt;&#x2F;li&gt;
  &lt;li&gt;Expose merge status for branch API (community contribution)&lt;&#x2F;li&gt;
  &lt;li&gt;Expose personal snippets as &#x2F;snippets (community contribution)&lt;&#x2F;li&gt;
  &lt;li&gt;Expose pipeline coverage&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h2 id=&quot;omnibus-gitlab-package-changes&quot;&gt;Omnibus GitLab package changes&lt;&#x2F;h2&gt;

&lt;h3 id=&quot;postgresql-version-upgrade&quot;&gt;PostgreSQL version upgrade&lt;&#x2F;h3&gt;

&lt;p&gt;Starting last month with GitLab 8.14 omnibus-gitlab package, we are providing a
way to upgrade the PostgreSQL database version.&lt;&#x2F;p&gt;

&lt;p&gt;The current version of PostgreSQL we are packaging (9.2.18) is slowly
approaching its EOL. Due to the &lt;a href=&quot;https:&#x2F;&#x2F;www.postgresql.org&#x2F;support&#x2F;versioning&#x2F;&quot;&gt;PostgreSQL versioning policy&lt;&#x2F;a&gt;,
upgrades between major releases require downtime and the use of the &lt;code&gt;pg_upgrade&lt;&#x2F;code&gt; tool.&lt;&#x2F;p&gt;

&lt;p&gt;For this purpose, we are packaging the newest available PostgreSQL version (9.6.1).
We are also introducing &lt;code&gt;gitlab-ctl pg-upgrade&lt;&#x2F;code&gt; tool which should make this
transition as painless as possible.
When upgrading to GitLab 8.15, this &lt;em&gt;action will not be run automatically&lt;&#x2F;em&gt;.
This will allow you to plan the database upgrade downtime.&lt;&#x2F;p&gt;

&lt;p&gt;After version 9.0 is released, we plan on setting the PostgreSQL version 9.6 as
default so please make sure that you plan your upgrade before that release.
We&#x27;re expecting to ship GitLab 9.0 on or after February 22nd.&lt;&#x2F;p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;omnibus&#x2F;settings&#x2F;database.html#upgrade-packaged-postgresql-server&quot;&gt;Read more about database upgrade in our docs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Ran into issues? Create an issue at the &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;omnibus-gitlab&#x2F;issues&quot;&gt;omnibus-gitlab issue tracker&lt;&#x2F;a&gt;,
and reference it in the &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;omnibus-gitlab&#x2F;issues&#x2F;1783&quot;&gt;upgrade problems meta issue.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;h2 id=&quot;performance-improvements&quot;&gt;Performance Improvements&lt;&#x2F;h2&gt;

&lt;p&gt;GitLab CE:&lt;&#x2F;p&gt;

&lt;ul&gt;
  &lt;li&gt;Retrieving commit counts has been improved for certain cases: &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7668&quot;&gt;!7668&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;Polling intervals have been adjusted to reduce system load: &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7762&quot;&gt;!7762&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;Refreshing authorized projects is done in a smarter way to reduce database load: &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7956&quot;&gt;!7956&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;The most recent commit ID for a path is now cached: &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;8098&quot;&gt;!8098&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;p&gt;GitLab EE:&lt;&#x2F;p&gt;

&lt;p&gt;GitLab EE now ships with a command called &lt;code&gt;sidekiq-cluster&lt;&#x2F;code&gt;. This command can be used to start extra Sidekiq workers that process only a limited number of queues. This feature can be used to process queues that receive a lot of jobs, without it affecting other parts of Sidekiq. This was added in &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ee&#x2F;merge_requests&#x2F;922&quot;&gt;922&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;This command is not yet used by Omnibus, our goal is to add this in 8.16.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;gitlab-runner-19&quot;&gt;GitLab Runner 1.9&lt;&#x2F;h2&gt;

&lt;p&gt;We are also releasing GitLab Runner 1.9 today. Some highlights:&lt;&#x2F;p&gt;

&lt;ul&gt;
  &lt;li&gt;Add a retry mechanism to prevent failed clones in builds &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ci-multi-runner&#x2F;merge_requests&#x2F;399&quot;&gt;!399&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;Add Kubernete Node Selector &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ci-multi-runner&#x2F;merge_requests&#x2F;328&quot;&gt;!328&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;Push prebuilt images to dockerhub &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ci-multi-runner&#x2F;merge_requests&#x2F;420&quot;&gt;!420&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;Use prebuilt containers with Kubernetes executor &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ci-multi-runner&#x2F;merge_requests&#x2F;425&quot;&gt;!425&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;Add path and share cache settings for S3 cache &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ci-multi-runner&#x2F;merge_requests&#x2F;423&quot;&gt;!423&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;Split prepare stage to be: prepare, git_clone, restore_cache, download_artifacts &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ci-multi-runner&#x2F;merge_requests&#x2F;406&quot;&gt;!406&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;Introduce docker.Client timeouts &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ci-multi-runner&#x2F;merge_requests&#x2F;411&quot;&gt;!411&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;Allow network-sourced variables to specify that they should be files &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ci-multi-runner&#x2F;merge_requests&#x2F;413&quot;&gt;!413&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;Fix docker hanging for docker-engine 1.12.4 &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ci-multi-runner&#x2F;merge_requests&#x2F;415&quot;&gt;!415&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;Add pprof HTTP endpoints to metrics server &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ci-multi-runner&#x2F;merge_requests&#x2F;398&quot;&gt;!398&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;Add a multiple prometheus metrics: &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ci-multi-runner&#x2F;merge_requests&#x2F;401&quot;&gt;!401&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;p&gt;To see the full list of all changes please read &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ci-multi-runner&#x2F;blob&#x2F;v1.9.0&#x2F;CHANGELOG.md&quot;&gt;the Runner&#x27;s CHANGELOG file&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;gitlab-mattermost-351&quot;&gt;GitLab Mattermost 3.5.1&lt;&#x2F;h2&gt;

&lt;p&gt;GitLab 8.15 includes &lt;a href=&quot;https:&#x2F;&#x2F;about.mattermost.com&#x2F;&quot;&gt;Mattermost 3.5.1&lt;&#x2F;a&gt;, an open
source Slack-alternative providing workplace messaging for web, PC and phone
with archiving and search. Improvements this month include new &lt;a href=&quot;https:&#x2F;&#x2F;about.mattermost.com&#x2F;mattermost-december-2016-update&#x2F;&quot;&gt;Xen Orchestra and Homebrew integrations, plus upgraded Desktop Apps for Windows, Mac and Linux&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;This version includes &lt;a href=&quot;http:&#x2F;&#x2F;about.mattermost.com&#x2F;security-updates&#x2F;&quot;&gt;security updates&lt;&#x2F;a&gt; and upgrade from earlier versions is recommended.&lt;&#x2F;p&gt;

&lt;p&gt;Mattermost 3.5.1 was included in GitLab 8.14.1, so anyone on GitLab 8.14.1 or
later should have the patch already.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;other-changes&quot;&gt;Other changes&lt;&#x2F;h2&gt;

&lt;p&gt;This release has more improvements. Please check out
&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;blob&#x2F;master&#x2F;CHANGELOG.md&quot;&gt;the changelog&lt;&#x2F;a&gt; to see all the named changes.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;upgrade-barometer&quot;&gt;Upgrade barometer&lt;&#x2F;h2&gt;

&lt;p&gt;To upgrade to GitLab 8.15, downtime is required. Larger instances (&amp;gt;1000 users)
should expect about 15 minutes of downtime.&lt;&#x2F;p&gt;

&lt;p&gt;The specific migrations requiring downtime are described below.&lt;&#x2F;p&gt;

&lt;ul&gt;
  &lt;li&gt;Columns with default values are added, this process may take some time depending on the number of rows in these tables&lt;&#x2F;li&gt;
  &lt;li&gt;Some indexes are added concurrently, which does not require downtime but may take some time to complete&lt;&#x2F;li&gt;
  &lt;li&gt;The environments table is cleaned up, and a new column is added with a generated default value (this could take some time depending on the number of environments)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h3 id=&quot;note&quot;&gt;Note&lt;&#x2F;h3&gt;

&lt;p&gt;We assume you are upgrading from the latest version. If not, then also consult the upgrade barometers of any intermediate versions you are skipping.
If you are upgrading from a GitLab version prior to 8.0 &lt;em&gt;and&lt;&#x2F;em&gt; you have CI enabled, you have to upgrade to GitLab 8.0 &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;2015&#x2F;09&#x2F;22&#x2F;gitlab-8-0-released&#x2F;&quot;&gt;first&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;Please be aware that by default the Omnibus packages will stop, run migrations,
and start again, no matter how “big” or “small” the upgrade is. This behavior
can be changed by adding a &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;omnibus&#x2F;update&#x2F;README.html&quot;&gt;&lt;code&gt;&#x2F;etc&#x2F;gitlab&#x2F;skip-auto-migrations&lt;&#x2F;code&gt;
file&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;hr &#x2F;&gt;

&lt;h2 id=&quot;installation&quot;&gt;Installation&lt;&#x2F;h2&gt;

&lt;p&gt;If you are setting up a new GitLab installation please see the
&lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;installation&#x2F;&quot;&gt;download GitLab page&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;updating&quot;&gt;Updating&lt;&#x2F;h2&gt;

&lt;p&gt;Check out our &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;update&#x2F;&quot;&gt;update page&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;enterprise-edition&quot;&gt;Enterprise Edition&lt;&#x2F;h2&gt;

&lt;p&gt;The mentioned EE only features and things like LDAP group support can be found in GitLab Enterprise Edition.
For a complete overview please have a look at the &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;features&#x2F;#enterprise&quot;&gt;feature list of GitLab EE&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;Access to GitLab Enterprise Edition is included with a
&lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;pricing&#x2F;&quot;&gt;subscription&lt;&#x2F;a&gt;.
No time to upgrade GitLab yourself?
A subscription also entitles you to our upgrade and installation services.&lt;&#x2F;p&gt;

&lt;hr &#x2F;&gt;
&lt;img src=&#x27;https:&#x2F;&#x2F;about.gitlab.com&#x2F;images&#x2F;8_15&#x2F;pic.jpg&#x27; class=&#x27;webfeedsFeaturedVisual&#x27; style=&#x27;display: none;&#x27; &#x2F;&gt;    </content>
  </entry>
  <entry>
    <title>How to Keep Remote (Volunteer) Teams Engaged</title>
    <link rel="alternate" href="https://about.gitlab.com/2016/12/21/how-to-keep-remote-teams-engaged/"/>
    <id>https://about.gitlab.com/2016/12/21/how-to-keep-remote-teams-engaged/</id>
    <published>2016-12-21T00:00:00+00:00</published>
    <updated>2016-12-21T00:00:00+00:00</updated>
    <author>
      <name>Emily von Hoffmann</name>
    </author>
    <content type="html">
&lt;p&gt;We love hearing when people outside of GitLab read our &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;handbook&#x2F;&quot;&gt;Handbook&lt;&#x2F;a&gt; - it&#x27;s totally public, after all, and it&#x27;s pretty comprehensive (or behemoth, depending on your frame of mind). It was even more exciting to hear from James Telfer, of &lt;a href=&quot;http:&#x2F;&#x2F;ukseds.org&#x2F;&quot;&gt;UK Students for the Exploration and Development of Space&lt;&#x2F;a&gt; (UKSEDS) that he read the handbook and had further questions that we&#x27;d left unanswered. In particular - what to do when you have neither carrot nor stick when managing remote volunteers?&lt;&#x2F;p&gt;



&lt;p&gt;UKSEDS is a charity run by students that operates around the UK offering career advice and skills support to students interested in the space industry and borrowed a lot from our handbook when making their constitution.&lt;&#x2F;p&gt;

&lt;p&gt;The internal engagement problem was exacerbated after UKSEDS experienced a growth surge and a structural change earlier this year. Specifically, James said:&lt;&#x2F;p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Project teams are interacting okay with their direct managers but interaction with the wider organization is little to none. While this doesn&#x27;t directly impact work, we&#x27;re concerned about the longer term impact on volunteer enjoyment and retention, both of which are really important to us.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;p&gt;Eliran Mesika, our Director of Strategic Partnerships, agreed to jump on the phone with James to learn more about UKSEDS&#x27; particular case and the challenges they faced.&lt;&#x2F;p&gt;

&lt;p&gt;Here are some highlights:&lt;&#x2F;p&gt;

&lt;ul&gt;
  &lt;li&gt;Having individual responsibilities somewhere public (even internally) is a useful accountability tool, and it helps direct team members to the correct person when they have a question.&lt;&#x2F;li&gt;
  &lt;li&gt;Functional group updates presented to the whole company make teams talk more, which results in more organic collaboration.&lt;&#x2F;li&gt;
  &lt;li&gt;Keeping communication on a specific topic within the right issue is essential for keeping everyone on the same page - especially when working asynchronously.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;p&gt;Read on for Eliran&#x27;s tips.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;James:&lt;&#x2F;strong&gt; For some background, our work involves hosting skills workshops and conducting outreach, which means traveling to schools and science fairs to tell kids that space is great and they should get involved. We recently restructured by creating a small executive group that meets every week, and allowing volunteer teams to grow larger and meet less frequently. We found that destroyed our ability to connect with the teams and we&#x27;ve started losing people; we&#x27;ve got quite a high attrition rate, which is normal for a charity. But I wondered how you approached this problem? Some of those things like the random Hangouts and the 1:1 chats work great as long as you can pay people for their work time. But with volunteers it&#x27;s a different problem entirely.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;strong&gt;Eliran:&lt;&#x2F;strong&gt; I&#x27;m actually working with Drupal, and I think your problem is more similar to an open source project, or an open source community behind a project. Because everyone is volunteering, and there is a major difference to be taken into account between volunteers and people who are being paid. Once you&#x27;re paying someone, you have their attention. You still have to think about keeping them productive, but you already have their time which is the most important asset. Perhaps learning what open source projects do to engage their contributors may be helpful in your case. For now, I can share a bit about what we&#x27;re doing to keep people engaged and connected to the mission.&lt;&#x2F;p&gt;

&lt;p&gt;We&#x27;re an open core product, and since its inception the whole behavior and culture of the company has also been open. This extends to everything that the company does. Everything from the very technical details of a new feature that is being repaired or created, all the way to our high priorities and strategy, and the actual procedures the company follows, is documented and publicly shared. It is shared within the whole company, and is available all the time.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;James:&lt;&#x2F;strong&gt; How does that culture extend to individual responsibilities?&lt;&#x2F;p&gt;

&lt;p&gt;&lt;strong&gt;Eliran:&lt;&#x2F;strong&gt; 
Expectations and responsibilities are also public. I handle strategic partnerships, and it&#x27;s pretty much just me that&#x27;s doing this, but I can look at what anyone on our whole team is working on, without even having to ask them directly, which would be the usual scenario I think. On our team page, we have everyone&#x27;s job description linked, along with any specific responsibilities. So if you have frontend developers, maybe one is responsible for the website, and others are responsible for a particular set of features of the community edition, for example. So you can understand at a glance who the right person is for what you&#x27;re interested in, and you can go into any repo and see what they&#x27;re working on. For the most part when we&#x27;re talking about procedures, we have the Handbook which describes in detail everything about the administration and culture of the company. It goes into detail even about how to write a shared document that you work on. It also talks about what we do on a regular basis.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;James:&lt;&#x2F;strong&gt; Can you elaborate a bit on those routine things, like the daily calls?**&lt;&#x2F;p&gt;

&lt;p&gt;&lt;strong&gt;Eliran:&lt;&#x2F;strong&gt; Our daily calls have worked really well for us. They&#x27;re optional, you don&#x27;t have to join, but we usually get maybe two-thirds of the company joining each day. You also have to take into account the fact that, because it&#x27;s a set time maybe some people can&#x27;t come because it&#x27;s night for them or too early, but for the most part people join. Those calls are two-fold. One thing, which I think relates to what you&#x27;re trying to achieve, is that we have functional updates. We&#x27;ve divided teams into the various days of the week, and they give an update on what they&#x27;ve completed over the past few weeks. So that gives the whole company insight into what the marketing team is doing, what the CI team is doing, and what they&#x27;re working on next. It really connects you with the whole scope, rather than just seeing your team&#x27;s goals and your individual goals in a vacuum. That helps I think to orient everyone to what the company is doing as a whole, and how it&#x27;s moving forward.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;James:&lt;&#x2F;strong&gt; So that&#x27;s using what other people are doing to remind an individual person or team that the greater company exists - it&#x27;s not so much that they&#x27;d be interested in the technical details, but it reminds them they&#x27;re part of something bigger? Is that the key there?&lt;&#x2F;p&gt;

&lt;p&gt;&lt;strong&gt;Eliran:&lt;&#x2F;strong&gt; Yeah I think it&#x27;s very important to give people insight into what other teams are doing, because it&#x27;s remote. That&#x27;s really key. Otherwise, it seems like when people are working remotely it&#x27;s very easy to feel isolated. Having those functional updates forces you out of isolation and connects you with the bigger goal. And on a personal level, it connects you with other people&#x27;s work. As you said, even if you&#x27;re not a technical person, you&#x27;ll still get a high level understanding of the product or what the objective was. That&#x27;s great for creating a more cohesive environment, and it&#x27;s remained the same since I joined. When I joined there were about 70 employees, and it&#x27;s still the same culture and process. We also have a second part of the call where people shared what they did last weekend. So once a week you have an opportunity to talk about your personal life, we rotate so everyone shares their experiences. I think that&#x27;s a very powerful social tool to also connect on a social level, even if you&#x27;re not talking with people on a regular basis.&lt;&#x2F;p&gt;

&lt;p&gt;Typically you&#x27;re working with a team of 5-10 people, and you&#x27;ll be part of a group that&#x27;s maybe 30 people. So at best you&#x27;ll know like 30 people, and you won&#x27;t talk to them on a regular basis. You know the regular few people that you talk to, and I think that&#x27;s very narrow if you&#x27;re part of a bigger group. But the way that we do these individual stories helps make everyone feel closer.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;James&lt;&#x2F;strong&gt;: I can see one definite scope for improvement for us, because we haven&#x27;t been very good at pulling the teams together. They&#x27;re sort of sandboxed at the moment.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;strong&gt;Eliran;&lt;&#x2F;strong&gt; I think you&#x27;ll start noticing that once teams start talking about what they&#x27;re doing, obviously all teams have touch points that they&#x27;ve been speaking with others about, but you&#x27;ll have more organic collaboration. Just by talking about what they&#x27;re thinking about doing, or trying to do. That&#x27;s the best case scenario. At the most basic level, you&#x27;ll get people to be aware of what&#x27;s going on.&lt;&#x2F;p&gt;

&lt;p&gt;We also have a second type of meeting, which is an open meeting focused on a certain department or area. We have a kick-off meeting for a product, or for example we have a monthly release cycle, and anyone can come to those meetings to learn more about what&#x27;s going on. Our marketing team also has those meetings for example, to talk about new efforts and past performance.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;James:&lt;&#x2F;strong&gt; How else do people reach out to work together?&lt;&#x2F;p&gt;

&lt;p&gt;&lt;strong&gt;Eliran:&lt;&#x2F;strong&gt; We use Slack for communication, and I&#x27;m not sure how that would work in an environment like yours, without volunteers dedicating a certain amount of time every day. But we have channels for everything, and the whole team is distributed across channels. We have #general, which is the channel Emily used to ask &quot;Hey is there anyone relevant who&#x27;d like to help James and talk about remote work at his organization?&quot; We also have a #questions channel, where anyone can ask anything from the most sophisticated to the stupidest. They just throw their question to the channel and people try to help them or steer them in the right direction for who to talk to. Another channel that&#x27;s really important is the #thanks channel, and I feel that&#x27;s an important part to offer gratitude to someone for helping you, and also receive that when you&#x27;ve helped someone. Because of the remote environment it&#x27;s very powerful. Because GitLab&#x27;s culture is built on asynchronous work - people in different time zones - you&#x27;re not on Slack all the time because others will pick up your question whenever they become available.&lt;&#x2F;p&gt;

&lt;p&gt;One other thing that&#x27;s working well is centralizing a process for working asynchronously. So, we try to make all the work and communication and discussion on GitLab by using issues. Someone will create an issue with a particular task or mission, and then the whole communication is available there. So people have access to information or decisions or a process on a specific area. Even if you&#x27;re not involved, you can comment and say, &quot;Hey I&#x27;d like to suggest a, b, c.&quot; So by virtue of being in different time zones, we were forced to use asynchronous methods of work, and issues work very well for that. Having the discussion tools integrated into that, and using it to keep everything in one work space dedicated to a specific topic, is great for remote teams. If you can&#x27;t always set a time for everyone to sit down together, using issues is crucial to making work happen.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;James:&lt;&#x2F;strong&gt; We do use Slack, because, as you may recall as a student you may as well be in different time zones, waking up at 9 am one day and 1 pm the next. Are there decisions made on Slack, or do you tell people, &quot;No this is a discussion, take it to x issue&quot;?&lt;&#x2F;p&gt;

&lt;p&gt;&lt;strong&gt;Eliran:&lt;&#x2F;strong&gt; We may have those specific discussions on Slack, but for the most part, maybe 95% of the decisions are happening within the issue. But we don&#x27;t discourage people from talking, so there could be times when you have to talk to someone or a group of people, because there&#x27;s only so much you can do over text. So you may make a decision over Hangout, but that is communicated back through the issue. Someone will go back and say &quot;I had a discussion with Mark, and we decided the best way to move forward is x.&quot; That way everyone has the opportunity to get the takeaways from that meeting and give input. So some decisions happen away from the issues. But it&#x27;s important to reinforce that we try to keep communication on a specific topic within an issue. We really push that, and it&#x27;s part of the culture at GitLab. As you keep growing, the sooner you adopt changes in a working culture, the better it is for people to learn them later on.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;em&gt;Tweet us &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;gitlab&quot;&gt;@GitLab&lt;&#x2F;a&gt; and check out our &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;jobs&#x2F;&quot;&gt;job openings&lt;&#x2F;a&gt;.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;

&lt;!-- cover image: https:&#x2F;&#x2F;www.pexels.com&#x2F;photo&#x2F;people-coffee-meeting-team-7096&#x2F; --&gt;

&lt;!-- cover image license: CC0: https:&#x2F;&#x2F;www.pexels.com&#x2F;photo-license&#x2F; --&gt;
&lt;img src=&#x27;https:&#x2F;&#x2F;about.gitlab.com&#x2F;images&#x2F;blogimages&#x2F;how-to-keep-remote-teams-engaged-cover.jpg&#x27; class=&#x27;webfeedsFeaturedVisual&#x27; style=&#x27;display: none;&#x27; &#x2F;&gt;    </content>
  </entry>
  <entry>
    <title>A creative agency's GitLab wishlist</title>
    <link rel="alternate" href="https://about.gitlab.com/2016/12/15/a-creative-agencys-gitlab-wishlist/"/>
    <id>https://about.gitlab.com/2016/12/15/a-creative-agencys-gitlab-wishlist/</id>
    <published>2016-12-15T00:00:00+00:00</published>
    <updated>2016-12-15T00:00:00+00:00</updated>
    <author>
      <name>Emily von Hoffmann</name>
    </author>
    <content type="html">
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;wvkuipers&quot;&gt;Wouter van Kuipers&lt;&#x2F;a&gt; is an engineer at &lt;a href=&quot;https:&#x2F;&#x2F;www.lukkien.com&#x2F;en&#x2F;&quot;&gt;Lukkien&lt;&#x2F;a&gt;, a creative agency that produces online media, photography, film, apps, CGI, and graphic design. His team currently works on a platform aimed at parents and healthcare professionals. They&#x27;ve used a combination of Jenkins and GitLab, although they are switching to GitLab CI for testing. He told me his team tends to use the collaboration tools of GitLab the most. Before GitLab, they used SVN, and ultimately decided on GitLab instead of a competitor because they needed to host on-premises for security reasons. Our service engineer &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;leematos&quot;&gt;Lee Matos&lt;&#x2F;a&gt; sat down with Wouter to learn about how GitLab can help.&lt;&#x2F;p&gt;



&lt;p&gt;Here are some items discussed below and requested by the Lukkien team:&lt;&#x2F;p&gt;

&lt;ul&gt;
  &lt;li&gt;A view that will let you see changes over builds, and how builds are affected over time.&lt;&#x2F;li&gt;
  &lt;li&gt;Notifications around CI builds, so if there are any related tickets, those get updated as well.&lt;&#x2F;li&gt;
  &lt;li&gt;Versioning for Photoshop and InDesign files.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;Wouter:&lt;&#x2F;strong&gt; We struggle as a team and company to find a good versioning system for our (UX) designs. Right now we create separate folders and label the versions of our InDesign and Photoshop files. We want to know the latest version, but also want to have a clear visual representation of the changes between versions. Is there any planning for tooling like that in GitLab in the (near) feature?&lt;&#x2F;p&gt;

&lt;p&gt;&lt;strong&gt;Lee:&lt;&#x2F;strong&gt; Frankly, this is on our dream feature list. I think everybody on our team wants to be able to version Photoshop and InDesign documents, but we don’t have a good solution for those files right now that&#x27;s going to work smoothly. It looks like Adobe is getting into the versioning space for files like these, so there’s a silver lining here in that once Adobe solves that problem, we’ll probably do something similar quickly thereafter.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;Wouter:&lt;&#x2F;strong&gt; My next question is that we are hosting GitLab using a Docker setup, this works quite well but we are not sure if this will create limitations in the long run, for example if we want to use Mattermost in the future? My team has 8-10 developers, but we use the setup for all our teams, we’re all in the same GitLab instance. So that’s 100-120 developers.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;strong&gt;Lee:&lt;&#x2F;strong&gt; That&#x27;s what I would call GitLab Small&#x2F;Medium-sized. As it stands, we think that will be fine, there are no limitations even with Mattermost and we don’t expect there to be any problems. Obviously if there are, we’ll explore that with you and figure it out. Our product team leads aren&#x27;t aware of running GitLab in Docker at a big scale (1000+) – most of those clients are running it directly in a VM or Bare Metal. We feel that obviously Docker is the future so we need to find the answer to these questions. If you run into anything, please bring it up in an issue. And the same goes for Docker in Mattermost, we don’t expect anything different.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;Wouter:&lt;&#x2F;strong&gt; Finally, in what way does GitLab want to position itself in the long run compared to GitHub?&lt;&#x2F;p&gt;

&lt;p&gt;&lt;strong&gt;Lee:&lt;&#x2F;strong&gt; The best way to think about it, for me, is we are actually more like Atlassian. Our end goal is to build what Atlassian ended up with by acquiring the little pieces, by instead building those parts and making them 100 percent cohesive. So it’s more about building an end-to-end development tool that allows your team to work together and converse and go. Our buzz phrase at this point is &quot;From idea to production&quot;, so we want to cover everything over that process, and make it faster, whatever you’re using GitLab for. That’s even our goal internally as well, so it excites me because we’re actually using GitLab to build it.&lt;&#x2F;p&gt;

&lt;p&gt;I think GitHub is positioning itself as more of a core component, they see Git and code as the core thing that needs to be solved, and are leaving integrations up to the third parties. We have integrations and we see the value in them, but we want to build something that allows you to start making things work out of the box. Instead of saying &quot;You need to go buy Drone CI, you need to use Waffle.io, and need to wire them all up and read 10 different documentations to figure it out.&quot; We want that process to be as easy as possible.&lt;&#x2F;p&gt;

&lt;p&gt;Image &lt;a href=&quot;https:&#x2F;&#x2F;www.flickr.com&#x2F;photos&#x2F;lennykphotography&#x2F;26687024535&#x2F;in&#x2F;photolist-GEeYCF-hUAGKL-nQCwXy-Emhqdz-HRUzeG-EeGxU4-p2KCQa-Eroe6z-e4BpVm-dcZWfj-mQnNTJ-atd2f5-DSYEyA-DSqqGk-DFXwUA-aHPQVk-GucZJZ-EDGjje-CS8FYi-rymZ62-EBjtSY-DSfzQT-avJQMx-aYtqkR-CztMC7-dTRM3q-EPK3hD-DpeasQ-f2hdPB-eRwBGC-EoaxPD-b18F74-9sd1No-bkNuRx-byvPzZ-hxRZyb-D7F1xM-EVqmsh-CVBJBa-9pnw9W-eBWbNx-ftZrun-DXtJuT-p8As5e-DWQhdR-bkNdg7-oQCcaJ-b3JagT-8VoF1U-cgzLCU&quot;&gt;&quot;Lightroom Preset Balloon Release&quot;&lt;&#x2F;a&gt; by &lt;a href=&quot;https:&#x2F;&#x2F;www.flickr.com&#x2F;photos&#x2F;lennykphotography&#x2F;&quot;&gt;Lenny K Photography&lt;&#x2F;a&gt; is licensed under &lt;a href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by&#x2F;4.0&#x2F;legalcode&quot;&gt;CC BY 4.0&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;em&gt;We want you to ask us anything! If you&#x27;re a user interested in sharing your story on our blog, please fill out this &lt;a href=&quot;https:&#x2F;&#x2F;docs.google.com&#x2F;a&#x2F;gitlab.com&#x2F;forms&#x2F;d&#x2F;1K8ZTS1QvSSPos6mVh1ol8ZyagInYctX3fb9eglzeK70&#x2F;edit&quot;&gt;form&lt;&#x2F;a&gt;  and we’ll get in touch!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;

&lt;p&gt;&lt;em&gt;Tweet us &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;gitlab&quot;&gt;@GitLab&lt;&#x2F;a&gt; and check out our &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;jobs&#x2F;&quot;&gt;job openings&lt;&#x2F;a&gt;.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;img src=&#x27;https:&#x2F;&#x2F;about.gitlab.com&#x2F;images&#x2F;blogimages&#x2F;a-creative-agencys-gitlab-wishlist.jpg&#x27; class=&#x27;webfeedsFeaturedVisual&#x27; style=&#x27;display: none;&#x27; &#x2F;&gt;    </content>
  </entry>
  <entry>
    <title>GitLab 8.14.5, 8.13.10, and 8.12.13 Released</title>
    <link rel="alternate" href="https://about.gitlab.com/2016/12/14/gitlab-8-dot-14-dot-5-released/"/>
    <id>https://about.gitlab.com/2016/12/14/gitlab-8-dot-14-dot-5-released/</id>
    <published>2016-12-14T19:00:00+00:00</published>
    <updated>2016-12-14T19:00:00+00:00</updated>
    <author>
      <name>GitLab</name>
    </author>
    <content type="html">
&lt;p&gt;Today we are releasing versions 8.14.5, 8.13.10, and 8.12.13 for GitLab Community
Edition (CE) and Enterprise Edition (EE).&lt;&#x2F;p&gt;

&lt;p&gt;These versions contain important security fixes, and we recommend that all
affected GitLab installations be upgraded to one of these versions.&lt;&#x2F;p&gt;

&lt;p&gt;Please read on for more details.&lt;&#x2F;p&gt;



&lt;h2 id=&quot;security-fixes-in-8145-81310-and-81213&quot;&gt;Security fixes in 8.14.5, 8.13.10 and 8.12.13&lt;&#x2F;h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Fix missing Note access checks in by moving Note#search to updated NoteFinder (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;issues&#x2F;23867&quot;&gt;#23867&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Filter &lt;code&gt;incoming_email_token&lt;&#x2F;code&gt; and &lt;code&gt;runners_token&lt;&#x2F;code&gt; parameters (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;issues&#x2F;25687&quot;&gt;#25687&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h2 id=&quot;security-fixes-in-8145-and-81310&quot;&gt;Security fixes in 8.14.5 and 8.13.10&lt;&#x2F;h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Issue#visible_to_user moved to IssuesFinder (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;issues&#x2F;24637&quot;&gt;#24637&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h3 id=&quot;other-fixes-in-8145-81213-and-81310&quot;&gt;Other fixes in 8.14.5, 8.12.13 and 8.13.10&lt;&#x2F;h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; API: Memoize the current_user so that the sudo can work properly. (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;8017&quot;&gt;!8017&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h3 id=&quot;other-fixes-in-8145&quot;&gt;Other fixes in 8.14.5&lt;&#x2F;h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Omnibus GitLab&lt;&#x2F;strong&gt;: Add attribute client_output_buffer_limit_slave for redis (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;omnibus-gitlab&#x2F;merge_requests&#x2F;1147&quot;&gt;!1147&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
  &lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Remove &#x27;Leave Project&#x27; and &#x27;Leave Group&#x27; from settings dropdowns (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7600&quot;&gt;!7600&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Fix display hook error message (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7775&quot;&gt;!7775&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Shows group members in the project members list (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7899&quot;&gt;!7899&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Correct autocomplete for values with special characters (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7910&quot;&gt;!7910&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Remove wrong &#x27;.builds-feature&#x27; class from the MR settings fieldset (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7930&quot;&gt;!7930&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Avoid escaping relative links in Markdown twice (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7940&quot;&gt;!7940&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Allow branch names with dots on API endpoint (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7963&quot;&gt;!7963&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Fixed timeago re-rendering every element (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7969&quot;&gt;!7969&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Use a single query in Projects::ProjectMembersController to fetch members (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7997&quot;&gt;!7997&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Displays milestone remaining days only when it&#x27;s present (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7998&quot;&gt;!7998&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Fix Crontab typo for PruneOldEventsWorker to run 4x&#x2F;day instead of 60x&#x2F;hour (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;8051&quot;&gt;!8051&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Encode when migrating ProcessCommitWorker jobs (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;8064&quot;&gt;!8064&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Updates the docs to require GitLab Shell 4.0.3 (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;8050&quot;&gt;!8050&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
  &lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;EE:&lt;&#x2F;strong&gt; Fix milestone total weight is missing on the milestone page (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ee&#x2F;merge_requests&#x2F;944&quot;&gt;!944&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;EE:&lt;&#x2F;strong&gt; Remove wrong &#x27;.builds-feature&#x27; class from the MR settings fieldset (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ee&#x2F;merge_requests&#x2F;947&quot;&gt;!947&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;EE:&lt;&#x2F;strong&gt; Group members in project members view (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ee&#x2F;merge_requests&#x2F;958&quot;&gt;!958&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h2 id=&quot;upgrade-barometer&quot;&gt;Upgrade barometer&lt;&#x2F;h2&gt;

&lt;p&gt;These versions do include a single migration, and will require brief
downtime of typically less than one minute.&lt;&#x2F;p&gt;

&lt;p&gt;Please be aware that by default the Omnibus packages will stop, run migrations,
and start again, no matter how “big” or “small” the upgrade is. This behavior
can be changed by adding a &lt;a href=&quot;http:&#x2F;&#x2F;doc.gitlab.com&#x2F;omnibus&#x2F;update&#x2F;README.html&quot;&gt;&lt;code&gt;&#x2F;etc&#x2F;gitlab&#x2F;skip-auto-migrations&lt;&#x2F;code&gt;
file&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;updating&quot;&gt;Updating&lt;&#x2F;h2&gt;

&lt;p&gt;To update, check out our &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;update&quot;&gt;update page&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;enterprise-edition&quot;&gt;Enterprise Edition&lt;&#x2F;h2&gt;

&lt;p&gt;Interested in GitLab Enterprise Edition? Check out the &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;features&#x2F;#enterprise&quot;&gt;features exclusive to
EE&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;Access to GitLab Enterprise Edition is included with a
&lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;pricing&#x2F;&quot;&gt;subscription&lt;&#x2F;a&gt;. No time to upgrade GitLab
yourself? Subscribers receive upgrade and installation services.&lt;&#x2F;p&gt;
&lt;img src=&#x27;https:&#x2F;&#x2F;about.gitlab.com&#x2F;images&#x2F;default-blog-image.png&#x27; class=&#x27;webfeedsFeaturedVisual&#x27; style=&#x27;display: none;&#x27; &#x2F;&gt;    </content>
  </entry>
  <entry>
    <title>Continuous Delivery of a Spring Boot application with GitLab CI and Kubernetes</title>
    <link rel="alternate" href="https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/"/>
    <id>https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/</id>
    <published>2016-12-14T00:00:00+00:00</published>
    <updated>2016-12-14T00:00:00+00:00</updated>
    <author>
      <name>Marco Lenzo</name>
    </author>
    <content type="html">
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;2016&#x2F;08&#x2F;05&#x2F;continuous-integration-delivery-and-deployment-with-gitlab&#x2F;&quot;&gt;Continuous Integration, Continuous Deployment and Continuous Delivery&lt;&#x2F;a&gt; are increasingly popular topics among modern development teams. Together they enable a team to safely build, test and deploy the code virtually at any commit. The main benefit of these approaches is the ability to release more frequently quality code through means of automated pipelines. The tough part is building such pipelines. There is a myriad of tools available which we would need to choose, learn, install, integrate, and maintain.&lt;&#x2F;p&gt;

&lt;p&gt;Recently, I literally fell in love with &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;&quot;&gt;GitLab&lt;&#x2F;a&gt;! It offers a fully-featured ecosystem of tools which enable us to create an automated pipeline in minutes! From source control to issue tracking and CI, we find everything under one roof fully integrated and ready to use.&lt;&#x2F;p&gt;



&lt;p&gt;In this tutorial, we will create a &lt;a href=&quot;https:&#x2F;&#x2F;projects.spring.io&#x2F;spring-boot&#x2F;&quot;&gt;Spring Boot&lt;&#x2F;a&gt; application built, tested, and deployed with &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;gitlab-ci&#x2F;&quot;&gt;GitLab CI&lt;&#x2F;a&gt; on a &lt;a href=&quot;http:&#x2F;&#x2F;kubernetes.io&#x2F;&quot;&gt;Kubernetes&lt;&#x2F;a&gt; cluster. Spring Boot is the leading &lt;a href=&quot;http:&#x2F;&#x2F;microservices.io&#x2F;patterns&#x2F;microservice-chassis.html&quot;&gt;microservice chassis&lt;&#x2F;a&gt; for Java. It allows a developer to build a production-grade stand-alone application, like a typical &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Create,_read,_update_and_delete&quot;&gt;CRUD&lt;&#x2F;a&gt; application exposing a &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Representational_state_transfer&quot;&gt;RESTful API&lt;&#x2F;a&gt;,  with minimal configuration, reducing drastically the learning curve required for using the &lt;a href=&quot;https:&#x2F;&#x2F;spring.io&#x2F;&quot;&gt;Spring Framework&lt;&#x2F;a&gt;. Kubernetes is an open-source container orchestrator inspired by &lt;a href=&quot;http:&#x2F;&#x2F;static.googleusercontent.com&#x2F;media&#x2F;research.google.com&#x2F;en&#x2F;&#x2F;pubs&#x2F;archive&#x2F;43438.pdf&quot;&gt;Google Borg&lt;&#x2F;a&gt; that schedules, scales and manages containerized applications.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;create-a-gitlab-project&quot;&gt;Create a GitLab Project&lt;&#x2F;h2&gt;

&lt;p&gt;Let&#x27;s start by &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;projects&#x2F;new&quot;&gt;creating a new project&lt;&#x2F;a&gt; in GitLab named &lt;code&gt;actuator-sample&lt;&#x2F;code&gt;. Then we follow the command line instructions displayed in the project&#x27;s home page to clone the repository on our machine and perform the first commit.&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;git clone git@gitlab.com:marcolenzo&#x2F;actuator-sample.git
&lt;span class=&quot;nb&quot;&gt;cd &lt;&#x2F;span&gt;actuator-sample
touch README.md
git add README.md
git commit -m &lt;span class=&quot;s2&quot;&gt;&quot;add README&quot;&lt;&#x2F;span&gt;
git push -u origin master
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p class=&quot;alert alert-info&quot;&gt;Always replace &lt;code&gt;marcolenzo&lt;&#x2F;code&gt; with your own GitLab username whenever copying a snippet of code from this tutorial.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;create-a-spring-boot-application&quot;&gt;Create a Spring Boot application&lt;&#x2F;h2&gt;

&lt;p&gt;To bootstrap the Spring Boot application we navigate to the &lt;a href=&quot;https:&#x2F;&#x2F;start.spring.io&quot;&gt;Spring Initializr&lt;&#x2F;a&gt; web page and generate a &lt;strong&gt;Maven Project&lt;&#x2F;strong&gt; with the pre-selected Spring Boot &lt;strong&gt;Version&lt;&#x2F;strong&gt;. &lt;a href=&quot;https:&#x2F;&#x2F;maven.apache.org&#x2F;index.html&quot;&gt;Maven&lt;&#x2F;a&gt; is a project management tool commonly used in Java projects to define dependencies and the build lifecycle. We leave &lt;code&gt;com.example&lt;&#x2F;code&gt; as &lt;strong&gt;Group&lt;&#x2F;strong&gt; and set &lt;code&gt;actuator-sample&lt;&#x2F;code&gt; as the &lt;strong&gt;Artifact&lt;&#x2F;strong&gt; name. We select the &lt;code&gt;Web&lt;&#x2F;code&gt; dependency, which supports full stack web development with &lt;a href=&quot;http:&#x2F;&#x2F;tomcat.apache.org&#x2F;&quot;&gt;Tomcat&lt;&#x2F;a&gt; and &lt;a href=&quot;http:&#x2F;&#x2F;docs.spring.io&#x2F;spring&#x2F;docs&#x2F;current&#x2F;spring-framework-reference&#x2F;html&#x2F;mvc.html&quot;&gt;Spring MVC&lt;&#x2F;a&gt;, and the &lt;code&gt;Actuator&lt;&#x2F;code&gt; dependency which implements some production grade features useful for monitoring and managing our application like health-checks and HTTP requests traces.&lt;&#x2F;p&gt;

&lt;p&gt;Finally, we generate the project and a Zip file named &lt;code&gt;actuator-sample.zip&lt;&#x2F;code&gt; will be downloaded to our machine.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blogimages&#x2F;continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes&#x2F;initializr.png&quot; alt=&quot;Spring Initializr&quot; class=&quot;shadow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;p&gt;We can now unzip the archive and launch the application immediately. Spring Initializr has already created everything for us. We just need to have a &lt;a href=&quot;http:&#x2F;&#x2F;openjdk.java.net&#x2F;install&#x2F;&quot;&gt;Java JDK&lt;&#x2F;a&gt; 1.7 or later installed on our machine and the &lt;code&gt;JAVA_HOME&lt;&#x2F;code&gt; environment variable set accordingly. &lt;a href=&quot;http:&#x2F;&#x2F;openjdk.java.net&#x2F;&quot;&gt;OpenJDK&lt;&#x2F;a&gt; is the preferred option for most Linux distributions since it is readily available on their repositories. You can alternatively install &lt;a href=&quot;http:&#x2F;&#x2F;www.oracle.com&#x2F;technetwork&#x2F;java&#x2F;javase&#x2F;downloads&#x2F;index.html&quot;&gt;Oracle JDK&lt;&#x2F;a&gt; if it is a strict requirement for your team.&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;### Installing OpenJDK 8 on Debian, Ubuntu, etc.&lt;&#x2F;span&gt;

sudo apt-get install openjdk-8-jre

&lt;span class=&quot;c&quot;&gt;### Installing OpenJDK 8 on Fedora, Oracle Linux, Red Hat Enteprise, CentOS, etc.&lt;&#x2F;span&gt;

su -c &lt;span class=&quot;s2&quot;&gt;&quot;yum install java-1.8.0-openjdk&quot;&lt;&#x2F;span&gt;

&lt;span class=&quot;c&quot;&gt;### Setting the JAVA_HOME environment variable&lt;&#x2F;span&gt;

&lt;span class=&quot;nb&quot;&gt;export &lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt;JAVA_HOME&lt;&#x2F;span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt;&#x2F;path&#x2F;to&#x2F;your&#x2F;java&#x2F;home &lt;span class=&quot;c&quot;&gt;# e.g. &#x2F;usr&#x2F;lib&#x2F;jvm&#x2F;java-8-openjdk-amd64&#x2F;&lt;&#x2F;span&gt;

&lt;span class=&quot;c&quot;&gt;### Extracting and Launching the application&lt;&#x2F;span&gt;

&lt;span class=&quot;gp&quot;&gt;~&#x2F;git&#x2F;actuator-sample$ &lt;&#x2F;span&gt;unzip ~&#x2F;Downloads&#x2F;actuator-sample.zip -d ..&#x2F;
&lt;span class=&quot;gp&quot;&gt;~&#x2F;git&#x2F;actuator-sample$ &lt;&#x2F;span&gt;.&#x2F;mvnw spring-boot:run

&lt;span class=&quot;o&quot;&gt;[&lt;&#x2F;span&gt;...]

2016-12-02 22:41:14.376  INFO 10882 --- &lt;span class=&quot;o&quot;&gt;[&lt;&#x2F;span&gt;           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port&lt;span class=&quot;o&quot;&gt;(&lt;&#x2F;span&gt;s&lt;span class=&quot;o&quot;&gt;)&lt;&#x2F;span&gt;: 8080 &lt;span class=&quot;o&quot;&gt;(&lt;&#x2F;span&gt;http&lt;span class=&quot;o&quot;&gt;)&lt;&#x2F;span&gt;
2016-12-02 22:41:14.420  INFO 10882 --- &lt;span class=&quot;o&quot;&gt;[&lt;&#x2F;span&gt;           main] com.example.ActuatorSampleApplication    : Started ActuatorSampleApplication &lt;span class=&quot;k&quot;&gt;in &lt;&#x2F;span&gt;17.924 seconds &lt;span class=&quot;o&quot;&gt;(&lt;&#x2F;span&gt;JVM running &lt;span class=&quot;k&quot;&gt;for &lt;&#x2F;span&gt;87.495&lt;span class=&quot;o&quot;&gt;)&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The application is up and running and we did not write one line of code! Spring Boot is opinionated and auto-configures the application with sane default values and beans. It also scans the classpath for known dependencies and initializes them. In our case, we enjoy immediately all the production grade services offered by &lt;a href=&quot;http:&#x2F;&#x2F;docs.spring.io&#x2F;spring-boot&#x2F;docs&#x2F;current&#x2F;reference&#x2F;html&#x2F;production-ready-endpoints.html&quot;&gt;Spring Actuator&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;~$ &lt;&#x2F;span&gt;curl http:&#x2F;&#x2F;localhost:8080&#x2F;health
&lt;span class=&quot;o&quot;&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;status&quot;&lt;&#x2F;span&gt;:&lt;span class=&quot;s2&quot;&gt;&quot;UP&quot;&lt;&#x2F;span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;diskSpace&quot;&lt;&#x2F;span&gt;:&lt;span class=&quot;o&quot;&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;status&quot;&lt;&#x2F;span&gt;:&lt;span class=&quot;s2&quot;&gt;&quot;UP&quot;&lt;&#x2F;span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;total&quot;&lt;&#x2F;span&gt;:981190307840,&lt;span class=&quot;s2&quot;&gt;&quot;free&quot;&lt;&#x2F;span&gt;:744776503296,&lt;span class=&quot;s2&quot;&gt;&quot;threshold&quot;&lt;&#x2F;span&gt;:10485760&lt;span class=&quot;o&quot;&gt;}}&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p class=&quot;alert alert-info&quot;&gt;If you wish to learn Spring Boot in greater detail, have a look at their &lt;a href=&quot;http:&#x2F;&#x2F;docs.spring.io&#x2F;spring-boot&#x2F;docs&#x2F;current&#x2F;reference&#x2F;htmlsingle&#x2F;&quot;&gt;reference documentation&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;spring.io&#x2F;guides&quot;&gt;guides&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;It is time to commit our changes and push them to &lt;code&gt;origin&lt;&#x2F;code&gt;. To simplify things a bit, we commit directly on &lt;code&gt;master&lt;&#x2F;code&gt; without using &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ee&#x2F;workflow&#x2F;gitlab_flow.html#github-flow-as-a-simpler-alternative&quot;&gt;feature branches&lt;&#x2F;a&gt; since collaboration is not the focus of this tutorial. Later, we will use &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ee&#x2F;workflow&#x2F;gitlab_flow.html#environment-branches-with-gitlab-flow&quot;&gt;environment branches&lt;&#x2F;a&gt; as specified in the &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ee&#x2F;workflow&#x2F;gitlab_flow.html&quot;&gt;GitLab Flow&lt;&#x2F;a&gt; to selectively deploy to different environments, e.g. staging and production. If you are not familiar with the &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;2014&#x2F;09&#x2F;29&#x2F;gitlab-flow&#x2F;&quot;&gt;GitLab Flow&lt;&#x2F;a&gt;, I strongly recommend you to read its documentation.&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;git add --all
git commit -m &lt;span class=&quot;s2&quot;&gt;&quot;Creates actuator-example application&quot;&lt;&#x2F;span&gt;
git push origin master
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;creating-a-continuous-delivery-pipeline-with-gitlab-ci&quot;&gt;Creating a Continuous Delivery Pipeline with GitLab CI&lt;&#x2F;h2&gt;

&lt;p&gt;While our code is now safe on GitLab, we still need to automate its integration and deployment. We need to verify each commit with an automated build and set of tests in order to discover issues as early as possible and, if the build is successful, deploy to a target environment. A few years ago, our only option was to install, configure and maintain a CI Server like &lt;a href=&quot;https:&#x2F;&#x2F;jenkins.io&#x2F;&quot;&gt;Jenkins&lt;&#x2F;a&gt; and possibly automate our deployment with a set of bash scripts. While the number of options has grown significantly, whether hosted or on the cloud, we still need to find a way to integrate our source control system with the CI Server of our choice.&lt;&#x2F;p&gt;

&lt;p&gt;Not anymore though! GitLab has fully integrated CI and CD Pipelines in its offering allowing us to &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;2016&#x2F;10&#x2F;25&#x2F;gitlab-workflow-an-overview&#x2F;#build-test-and-deploy&quot;&gt;build, test and deploy&lt;&#x2F;a&gt; our code with ease.&lt;&#x2F;p&gt;

&lt;p&gt;For the purpose of this tutorial we will deploy to the &lt;a href=&quot;https:&#x2F;&#x2F;cloud.google.com&#x2F;container-engine&#x2F;&quot;&gt;Google Cloud Container Engine&lt;&#x2F;a&gt; which is a cluster management and orchestration system built on the open source &lt;a href=&quot;http:&#x2F;&#x2F;kubernetes.io&#x2F;&quot;&gt;Kubernetes&lt;&#x2F;a&gt;. Kubernetes is supported by all main cloud providers and can be &lt;a href=&quot;http:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;getting-started-guides&#x2F;kubeadm&#x2F;&quot;&gt;easily installed on any Linux server&lt;&#x2F;a&gt; in minutes. That said, we will be able to re-use this configuration virtually on any environment running Kubernetes.&lt;&#x2F;p&gt;

&lt;p&gt;Before we can proceed to the creation of the pipeline, we need to add a couple of files to our repository to package our application as a Docker container and to describe the target deployment in Kubernetes terms.&lt;&#x2F;p&gt;

&lt;h3 id=&quot;packaging-a-spring-boot-application-as-a-docker-container&quot;&gt;Packaging a Spring Boot application as a Docker container&lt;&#x2F;h3&gt;

&lt;p&gt;Let&#x27;s start by creating the &lt;code&gt;Dockerfile&lt;&#x2F;code&gt; in the root directory of our project.&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;FROM openjdk:8u111-jdk-alpine
VOLUME &#x2F;tmp
ADD &#x2F;target&#x2F;actuator-sample-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT &lt;span class=&quot;o&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;java&quot;&lt;&#x2F;span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;-Djava.security.egd=file:&#x2F;dev&#x2F;.&#x2F;urandom&quot;&lt;&#x2F;span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;-jar&quot;&lt;&#x2F;span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;&#x2F;app.jar&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;FROM&lt;&#x2F;code&gt; keyword defines the base Docker image of our container. We chose &lt;a href=&quot;http:&#x2F;&#x2F;openjdk.java.net&#x2F;&quot;&gt;OpenJDK&lt;&#x2F;a&gt; installed on &lt;a href=&quot;https:&#x2F;&#x2F;alpinelinux.org&#x2F;&quot;&gt;Alpine Linux&lt;&#x2F;a&gt; which is a lightweight Linux distribution. The &lt;code&gt;VOLUME&lt;&#x2F;code&gt; instruction creates a mount point with the specified name and marks it as holding externally mounted volumes from the native host or other containers. &lt;code&gt;ADD&lt;&#x2F;code&gt; copies the executable JAR generated during the build to the container root directory. Finally &lt;code&gt;ENTRYPOINT&lt;&#x2F;code&gt; defines the command to execute when the container is started. Since Spring Boot produces an executable JAR with embedded Tomcat, the command to execute is simply &lt;code&gt;java -jar app.jar&lt;&#x2F;code&gt;. The additiona flag &lt;code&gt;java.security.edg=file:&#x2F;dev&#x2F;.&#x2F;urandom&lt;&#x2F;code&gt; is used to speed up the application start-up and avoid possible freezes. By default, Java uses &lt;code&gt;&#x2F;dev&#x2F;random&lt;&#x2F;code&gt; to seed its &lt;code&gt;SecureRandom&lt;&#x2F;code&gt; class which is known to block if its entropy pool is empty.&lt;&#x2F;p&gt;

&lt;p&gt;Time to commit.&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;git add Dockerfile
git commit -m &lt;span class=&quot;s2&quot;&gt;&quot;Adds Dockerfile&quot;&lt;&#x2F;span&gt;
git push origin master
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;define-the-kubernetes-deployment&quot;&gt;Define the Kubernetes Deployment&lt;&#x2F;h3&gt;

&lt;p&gt;Let&#x27;s create a file named &lt;code&gt;deployment.yml&lt;&#x2F;code&gt; in the root directory of our project.&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;extensions&#x2F;v1beta1&lt;&#x2F;span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;Deployment&lt;&#x2F;span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;actuator-sample&lt;&#x2F;span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;replicas&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;2&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;metadata&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
      &lt;span class=&quot;na&quot;&gt;labels&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
        &lt;span class=&quot;na&quot;&gt;app&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;actuator-sample&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;spec&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
      &lt;span class=&quot;na&quot;&gt;containers&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;actuator-sample&lt;&#x2F;span&gt;
        &lt;span class=&quot;na&quot;&gt;image&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;registry.gitlab.com&#x2F;marcolenzo&#x2F;actuator-sample&lt;&#x2F;span&gt;
        &lt;span class=&quot;na&quot;&gt;imagePullPolicy&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;Always&lt;&#x2F;span&gt;
        &lt;span class=&quot;na&quot;&gt;ports&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;na&quot;&gt;containerPort&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;8080&lt;&#x2F;span&gt;
      &lt;span class=&quot;na&quot;&gt;imagePullSecrets&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;registry.gitlab.com&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is the definition of a Kubernetes &lt;a href=&quot;http:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;user-guide&#x2F;deployments&#x2F;&quot;&gt;&lt;code&gt;Deployment&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; named &lt;code&gt;actuator-sample&lt;&#x2F;code&gt;. The &lt;code&gt;replicas&lt;&#x2F;code&gt; element defines the target number of &lt;a href=&quot;http:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;user-guide&#x2F;pods&#x2F;&quot;&gt;&lt;code&gt;Pods&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;. Kubernetes performs automated binpacking and self-healing of the system to comply with the deployment specifications while achieving optimal utilization of compute resources. A Pod can be composed of multiple containers. In this scenario, we only include the &lt;code&gt;actuator-sample&lt;&#x2F;code&gt; image stored on our private &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;2016&#x2F;05&#x2F;23&#x2F;gitlab-container-registry&#x2F;&quot;&gt;GitLab Container Registry&lt;&#x2F;a&gt;. For this reason, we need to set an entry under the &lt;code&gt;imagePullSecrets&lt;&#x2F;code&gt; which is used to authenticate to the GitLab Container Registry.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;For a detailed explanation of Kubernetes resources and concepts refer to the &lt;a href=&quot;http:&#x2F;&#x2F;kubernetes.io&#x2F;&quot;&gt;official documentation&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;Time to commit again and we are ready to define our GitLab CI pipeline.&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;git add deployment.yml
git commit -m &lt;span class=&quot;s2&quot;&gt;&quot;Adds Kubernetes Deployment definition&quot;&lt;&#x2F;span&gt;
git push origin master
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;creating-the-gitlab-ci-pipeline&quot;&gt;Creating the GitLab CI Pipeline&lt;&#x2F;h3&gt;

&lt;p&gt;In order to make use of &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;gitlab-ci&#x2F;&quot;&gt;GitLab CI&lt;&#x2F;a&gt; we need to add the &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;&quot;&gt;&lt;code&gt;.gitlab-ci.yml&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; configuration file to the root directory of our repository. This file is used by &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ee&#x2F;ci&#x2F;runners&#x2F;README.html&quot;&gt;GitLab Runners&lt;&#x2F;a&gt; to manage our project&#x27;s builds and deployments. Therein we can define an unlimited number of &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;#jobs&quot;&gt;Jobs&lt;&#x2F;a&gt; and their role in the whole build lifecycle.&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;image&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;docker:latest&lt;&#x2F;span&gt;
&lt;span class=&quot;na&quot;&gt;services&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;docker:dind&lt;&#x2F;span&gt;

&lt;span class=&quot;na&quot;&gt;variables&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;DOCKER_DRIVER&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;overlay&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;SPRING_PROFILES_ACTIVE&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;gitlab-ci&lt;&#x2F;span&gt;

&lt;span class=&quot;na&quot;&gt;stages&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;build&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;package&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;deploy&lt;&#x2F;span&gt;

&lt;span class=&quot;na&quot;&gt;maven-build&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;image&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;maven:3-jdk-8&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;build&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;mvn&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;package&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;-B&quot;&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;artifacts&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;paths&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;target&#x2F;*.jar&lt;&#x2F;span&gt;

&lt;span class=&quot;na&quot;&gt;docker-build&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;package&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;docker build -t registry.gitlab.com&#x2F;marcolenzo&#x2F;actuator-sample .&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN registry.gitlab.com&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;docker push registry.gitlab.com&#x2F;marcolenzo&#x2F;actuator-sample&lt;&#x2F;span&gt;

&lt;span class=&quot;na&quot;&gt;k8s-deploy&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;image&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;google&#x2F;cloud-sdk&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;deploy&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;echo &quot;$GOOGLE_KEY&quot; &amp;gt; key.json&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;gcloud auth activate-service-account --key-file key.json&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;gcloud config set compute&#x2F;zone europe-west1-c&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;gcloud config set project actuator-sample&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;gcloud config set container&#x2F;use_client_certificate True&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;gcloud container clusters get-credentials actuator-sample&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;kubectl delete secret registry.gitlab.com&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;kubectl create secret docker-registry registry.gitlab.com --docker-server=https:&#x2F;&#x2F;registry.gitlab.com --docker-username=marcolenzo --docker-password=$REGISTRY_PASSWD --docker-email=lenzo.marco@gmail.com&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;kubectl apply -f deployment.yml&lt;&#x2F;span&gt; 
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let&#x27;s break the file in pieces to understand what is going on.&lt;&#x2F;p&gt;

&lt;h4 id=&quot;image-and-services&quot;&gt;Image and Services&lt;&#x2F;h4&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;image&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;docker:latest&lt;&#x2F;span&gt;
&lt;span class=&quot;na&quot;&gt;services&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;docker:dind&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ee&#x2F;ci&#x2F;runners&#x2F;README.html&quot;&gt;GitLab Runner&lt;&#x2F;a&gt; can &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;docker&#x2F;using_docker_images.html&quot;&gt;use Docker images&lt;&#x2F;a&gt; to support our pipelines. The &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;#image-and-services&quot;&gt;&lt;code&gt;image&lt;&#x2F;code&gt; element&lt;&#x2F;a&gt; defines the name of the Docker image we want to use. Valid images are those hosted in the local Docker Engine or on &lt;a href=&quot;https:&#x2F;&#x2F;hub.docker.com&#x2F;&quot;&gt;Docker Hub&lt;&#x2F;a&gt;. The &lt;code&gt;services&lt;&#x2F;code&gt; element defines additional Docker images which are linked to the main container. In our case the main container is a plain Docker image while the linked container is enabled for running Docker in Docker.&lt;&#x2F;p&gt;

&lt;h4 id=&quot;variables&quot;&gt;Variables&lt;&#x2F;h4&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;variables&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;DOCKER_DRIVER&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;overlay&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;SPRING_PROFILES_ACTIVE&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;gitlab-ci&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is the definition of &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;#variables&quot;&gt;&lt;code&gt;variables&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; to be set on our build environment. The &lt;code&gt;DOCKER_DRIVER&lt;&#x2F;code&gt; signals the Docker Engine which storage driver to use. We use &lt;code&gt;overlay&lt;&#x2F;code&gt; for &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;docker&#x2F;using_docker_build.html#using-the-overlayfs-driver&quot;&gt;performance reasons&lt;&#x2F;a&gt;. The &lt;code&gt;SPRING_PROFILES_ACTIVE&lt;&#x2F;code&gt; is very useful when dealing with Spring Boot applications. It activates &lt;a href=&quot;http:&#x2F;&#x2F;docs.spring.io&#x2F;autorepo&#x2F;docs&#x2F;spring-boot&#x2F;current&#x2F;reference&#x2F;html&#x2F;boot-features-profiles.html&quot;&gt;Spring Profiles&lt;&#x2F;a&gt;, which provide a way to segregate parts of our application configuration and make it available only in certain environments. For instance, we can define different database URIs per environment, e.g. &lt;code&gt;localhost&lt;&#x2F;code&gt; when running on the developer machine and &lt;code&gt;mongo&lt;&#x2F;code&gt; when running within GitLab CI.&lt;&#x2F;p&gt;

&lt;h4 id=&quot;stages&quot;&gt;Stages&lt;&#x2F;h4&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;stages&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;build&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;package&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;deploy&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;#stages&quot;&gt;&lt;code&gt;stages&lt;&#x2F;code&gt; element&lt;&#x2F;a&gt; defines the lifecycle of our build. We associate each &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;#jobs&quot;&gt;job&lt;&#x2F;a&gt; to one stage. All jobs within a stage are run in parallel and stages are triggered sequentially in the order we define them, i.e. the next stage is initiated only when the previous one is complete.&lt;&#x2F;p&gt;

&lt;h4 id=&quot;the--job&quot;&gt;The &lt;code&gt;maven-build&lt;&#x2F;code&gt; job&lt;&#x2F;h4&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;maven-build&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;image&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;maven:3-jdk-8&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;build&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;mvn&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;package&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;-B&quot;&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;artifacts&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;paths&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;target&#x2F;*.jar&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is a job definition. Jobs can have any name except keywords. Have a look at the &lt;code&gt;.gitlab-ci.yml&lt;&#x2F;code&gt; &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;&quot;&gt;documentation&lt;&#x2F;a&gt; for the complete list of keywords.&lt;&#x2F;p&gt;

&lt;p&gt;The scope of this job is to perform a &lt;a href=&quot;https:&#x2F;&#x2F;maven.apache.org&#x2F;index.html&quot;&gt;Maven&lt;&#x2F;a&gt; build. For this reason, we define the &lt;code&gt;maven:3-jdk-8&lt;&#x2F;code&gt; as the Docker image on which this job should execute. This image comes with Maven 3 and the Java JDK 8 pre-installed for us.&lt;&#x2F;p&gt;

&lt;p&gt;We then specify &lt;code&gt;build&lt;&#x2F;code&gt; as the &lt;code&gt;stage&lt;&#x2F;code&gt; of this job. Jobs associated with the same stage run concurrently. This is extremely useful if you need to cross-compile your application. For instance, if we wanted to compile and test our application also on Java JDK 7, we could simply create another job with a different name and use the image &lt;code&gt;maven:3-jdk-7&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;maven-test-jdk-7&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;image&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;maven:3-jdk-7&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;build&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;mvn&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;package&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;-B&quot;&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;artifacts&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;paths&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;target&#x2F;*.jar&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As previously said, the &lt;code&gt;maven-test-jdk-7&lt;&#x2F;code&gt; job runs in parallel with the &lt;code&gt;maven-build&lt;&#x2F;code&gt;. Hence, it does not have an impact on the pipeline execution time.&lt;&#x2F;p&gt;

&lt;p&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;#script&quot;&gt;&lt;code&gt;script&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is a shell command to be executed by the GitLab Runner. The &lt;code&gt;mvn package -B&lt;&#x2F;code&gt; triggers a non-interactive Maven build up to the &lt;code&gt;package&lt;&#x2F;code&gt; phase. This phase is specific to the &lt;a href=&quot;https:&#x2F;&#x2F;maven.apache.org&#x2F;guides&#x2F;introduction&#x2F;introduction-to-the-lifecycle.html&quot;&gt;Maven build lifecycle&lt;&#x2F;a&gt; and it includes also the &lt;code&gt;validate&lt;&#x2F;code&gt;, &lt;code&gt;compile&lt;&#x2F;code&gt; and &lt;code&gt;test&lt;&#x2F;code&gt; phases. That means that our Maven project will be validated, compiled and (unit) tested as well. Tests are to be included in the &lt;code&gt;src&#x2F;test&#x2F;java&lt;&#x2F;code&gt; folder. In our specific case, Spring Initializr has already created a unit test which verifies that the application context loads without errors. We are free to add as many unit tests as we like. Finally, the &lt;code&gt;package&lt;&#x2F;code&gt; phase creates the executable JAR.&lt;&#x2F;p&gt;

&lt;p&gt;To persist the executable JAR and share it across jobs, we specify job &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;#artifacts&quot;&gt;&lt;code&gt;artifacts&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;. These are files or directories that are attached to the build after success and made downloadable from the UI in the Pipelines screen.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blogimages&#x2F;continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes&#x2F;artifacts.png&quot; alt=&quot;Downloading Artifacts from Pipelines&quot; class=&quot;shadow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;h4 id=&quot;the--job-1&quot;&gt;The &lt;code&gt;docker-build&lt;&#x2F;code&gt; job&lt;&#x2F;h4&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;docker-build&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;package&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;docker build -t registry.gitlab.com&#x2F;marcolenzo&#x2F;actuator-sample .&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN registry.gitlab.com&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;docker push registry.gitlab.com&#x2F;marcolenzo&#x2F;actuator-sample&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;docker-build&lt;&#x2F;code&gt; job packages the application into a Docker container. We define &lt;code&gt;package&lt;&#x2F;code&gt; as the build &lt;code&gt;stage&lt;&#x2F;code&gt; since we need the &lt;code&gt;maven-build&lt;&#x2F;code&gt; job to produce the executable JAR beforehand.&lt;&#x2F;p&gt;

&lt;p&gt;The scripts are a typical sequence of &lt;code&gt;docker&lt;&#x2F;code&gt; commands used to build an image, login to a private registry and push the image to it. We will be pushing images to the &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;2016&#x2F;05&#x2F;23&#x2F;gitlab-container-registry&#x2F;&quot;&gt;GitLab Container Registry&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;user&#x2F;project&#x2F;new_ci_build_permissions_model.html#container-registry&quot;&gt;&lt;code&gt;$CI_BUILD_TOKEN&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is a pre-defined variable which is injected by GitLab CI in our build environment automatically. It is used to login to the GitLab Container Registry.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;For a complete list of pre-defined variables, have a look at the &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;variables&#x2F;README.html#variables&quot;&gt;variables documentation&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;h4 id=&quot;the--job-2&quot;&gt;The &lt;code&gt;k8s-deploy&lt;&#x2F;code&gt; job&lt;&#x2F;h4&gt;

&lt;p&gt;This job is responsible for deploying our application to the &lt;a href=&quot;https:&#x2F;&#x2F;cloud.google.com&#x2F;container-engine&#x2F;&quot;&gt;Google Container Engine&lt;&#x2F;a&gt;. I purposely decided to make use of the &lt;a href=&quot;https:&#x2F;&#x2F;cloud.google.com&#x2F;sdk&#x2F;gcloud&#x2F;&quot;&gt;Google Cloud SDK&lt;&#x2F;a&gt; (&lt;code&gt;gcloud&lt;&#x2F;code&gt;) because it gives us the possibility to programmatically create and manage Google Container Engine clusters and other products of the Google Cloud ecosystem. In this tutorial, we will simplify things a bit by creating the Google Container Engine cluster beforehand through the GUI.&lt;&#x2F;p&gt;

&lt;p&gt;First, we create a Google Cloud Project named &lt;code&gt;actuator-sample&lt;&#x2F;code&gt;. Take note of the &lt;code&gt;Project ID&lt;&#x2F;code&gt; since it sometimes differs from the project name we specify. Then we create a Google Container Engine cluster named &lt;code&gt;actuator-sample&lt;&#x2F;code&gt; as well. We can choose any machine type and any number of nodes. For the purpose of this tutorial is sufficient one node and a small machine. Let&#x27;s take note of the &lt;code&gt;zone&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blogimages&#x2F;continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes&#x2F;create-gce-cluster.png&quot; alt=&quot;Create a container cluster&quot; class=&quot;shadow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;p&gt;Finally we need to create a service account which is necessary to perform a non-interactive login with &lt;code&gt;gcloud&lt;&#x2F;code&gt;. Navigate to Google Cloud &lt;strong&gt;API Manager&lt;&#x2F;strong&gt; &amp;gt; &lt;strong&gt;Credentials&lt;&#x2F;strong&gt; &amp;gt; &lt;strong&gt;Create Credentials&lt;&#x2F;strong&gt; and create a JSON key for the &lt;code&gt;Compute Engine default service account&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;We can now analyze the configuration.&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;k8s-deploy&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;image&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;google&#x2F;cloud-sdk&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;deploy&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;echo &quot;$GOOGLE_KEY&quot; &amp;gt; key.json&lt;&#x2F;span&gt; &lt;span class=&quot;c1&quot;&gt;# Google Cloud service account key&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;gcloud auth activate-service-account --key-file key.json&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;gcloud config set compute&#x2F;zone europe-west1-c&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;gcloud config set project actuator-sample&lt;&#x2F;span&gt; 
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;gcloud config set container&#x2F;use_client_certificate True&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;gcloud container clusters get-credentials actuator-example&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;kubectl delete secret registry.gitlab.com&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;kubectl create secret docker-registry registry.gitlab.com --docker-server=https:&#x2F;&#x2F;registry.gitlab.com --docker-username=marcolenzo --docker-password=$REGISTRY_PASSWD --docker-email=lenzo.marco@gmail.com&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;kubectl apply -f deployment.yml&lt;&#x2F;span&gt; 
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We use the &lt;code&gt;google&#x2F;cloud-sdk&lt;&#x2F;code&gt; image for this process since it comes preloaded with &lt;code&gt;gcloud&lt;&#x2F;code&gt; and all components and dependencies of the Google Cloud SDK including alpha and beta components. We obviously chose &lt;code&gt;deploy&lt;&#x2F;code&gt; as the &lt;code&gt;stage&lt;&#x2F;code&gt; since we want that our application is packaged beforehand and its container and pushed to the GitLab Container Registry. Then we execute a set of scripts.&lt;&#x2F;p&gt;

&lt;p&gt;The &lt;code&gt;echo &quot;$GOOGLE_KEY&quot; &amp;gt; key.json&lt;&#x2F;code&gt; script injects the Google Cloud service account key in the container. &lt;code&gt;$GOOGLE_KEY&lt;&#x2F;code&gt; is a Secure Variable having as value the content of the Google Cloud service account key. &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;variables&#x2F;#user-defined-variables-secure-variables&quot;&gt;Secure Variables&lt;&#x2F;a&gt; are user defined variables that should not be shown in the &lt;code&gt;.gitlab-ci.yml&lt;&#x2F;code&gt;. They are set per project by navigating to &lt;strong&gt;Project&lt;&#x2F;strong&gt; &amp;gt; &lt;strong&gt;Variables&lt;&#x2F;strong&gt; &amp;gt; &lt;strong&gt;Add Variable&lt;&#x2F;strong&gt; in GitLab.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blogimages&#x2F;continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes&#x2F;secure-variables.png&quot; alt=&quot;Secure Variables&quot; class=&quot;shadow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;p&gt;The &lt;code&gt;gcloud auth activate-service-account --key-file key.json&lt;&#x2F;code&gt; script performs the non-interactive authentication process. The &lt;code&gt;gcloud config set ...&lt;&#x2F;code&gt; scripts are selecting the target project, zone and cluster. Make sure these values correspond to those you jotted down before. The &lt;code&gt;gcloud container clusters get-credentials actuator-example&lt;&#x2F;code&gt; scripts downloads the &lt;code&gt;kubectl&lt;&#x2F;code&gt; configuration file. If we wanted to use Kubernetes on another cloud provider or custom installation, we would source the &lt;code&gt;kubectl&lt;&#x2F;code&gt; configuration &lt;code&gt;~&#x2F;.kube&#x2F;config&lt;&#x2F;code&gt; without the need of interacting with &lt;code&gt;gcloud&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;The &lt;code&gt;kubectl create secret docker-registry ...&lt;&#x2F;code&gt; script creates the &lt;code&gt;imagePullSecret&lt;&#x2F;code&gt; we had defined in the &lt;code&gt;deployment.yml&lt;&#x2F;code&gt;. This is used by Kubernetes to authenticate with our private GitLab Container Registry and download the container images. The &lt;code&gt;kubectl delete secret&lt;&#x2F;code&gt; is necessary because the Kubernetes API is lacking the &lt;code&gt;replace&lt;&#x2F;code&gt; operation for &lt;code&gt;docker-registry&lt;&#x2F;code&gt; secrets. In a real world scenario, I would suggest to handle &lt;a href=&quot;http:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;user-guide&#x2F;secrets&#x2F;&quot;&gt;Kubernetes secrets&lt;&#x2F;a&gt; that can affect multiple pipelines, like the password for a private Docker registry, in a separate pipeline or through configuration management tools like &lt;a href=&quot;https:&#x2F;&#x2F;www.ansible.com&#x2F;&quot;&gt;Ansible&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;saltstack.com&#x2F;&quot;&gt;Salt&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;puppet.com&#x2F;&quot;&gt;Puppet&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;www.chef.io&#x2F;&quot;&gt;Chef&lt;&#x2F;a&gt;. The reason is that such secrets should be rotated periodically for security reasons and updated in each GitLab project using them. There is also the risk of interference between pipelines because of the &lt;code&gt;kubectl delete&lt;&#x2F;code&gt; command. Note that &lt;code&gt;$REGISTRY_PASSWD&lt;&#x2F;code&gt; is another Secure Variable.&lt;&#x2F;p&gt;

&lt;p&gt;Time to check if everything is in order on our cluster.&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;&#x2F;span&gt;kubectl get deployments
NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
actuator-sample   2         2         2            2           2m
&lt;span class=&quot;gp&quot;&gt;$ &lt;&#x2F;span&gt;kubectl get pods
NAME                               READY     STATUS    RESTARTS   AGE
actuator-sample-3641958612-3e5xy   1&#x2F;1       Running   0          2m
actuator-sample-5542343546-fr4gh   1&#x2F;1       Running   0          2m
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blogimages&#x2F;continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes&#x2F;kubernetes.png&quot; alt=&quot;Kubernetes&quot; class=&quot;shadow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;p&gt;Deployed!&lt;&#x2F;p&gt;

&lt;h4 id=&quot;gitlab-environments&quot;&gt;GitLab Environments&lt;&#x2F;h4&gt;

&lt;p&gt;Before concluding the tutorial, we will learn about &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;environments.html&quot;&gt;GitLab Environments&lt;&#x2F;a&gt; which enable us to track environments and deployments.&lt;&#x2F;p&gt;

&lt;p&gt;Let&#x27;s refactor the &lt;code&gt;k8s-deploy&lt;&#x2F;code&gt; job and split it in two. One job will target the staging environment and the other production.&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;k8s-deploy-staging&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;image&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;google&#x2F;cloud-sdk&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;deploy&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;echo &quot;$GOOGLE_KEY&quot; &amp;gt; key.json&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;gcloud auth activate-service-account --key-file key.json&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;gcloud config set compute&#x2F;zone europe-west1-c&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;gcloud config set project actuator-sample&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;gcloud config set container&#x2F;use_client_certificate True&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;gcloud container clusters get-credentials actuator-example&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;kubectl delete secret registry.gitlab.com&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;kubectl create secret docker-registry registry.gitlab.com --docker-server=https:&#x2F;&#x2F;registry.gitlab.com --docker-username=marcolenzo --docker-password=$REGISTRY_PASSWD --docker-email=lenzo.marco@gmail.com&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;kubectl apply -f deployment.yml --namespace=staging&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;environment&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;staging&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;url&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;https:&#x2F;&#x2F;example.staging.com&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;only&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;master&lt;&#x2F;span&gt;

&lt;span class=&quot;na&quot;&gt;k8s-deploy-production&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;image&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;google&#x2F;cloud-sdk&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;deploy&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;echo &quot;$GOOGLE_KEY&quot; &amp;gt; key.json&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;gcloud auth activate-service-account --key-file key.json&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;gcloud config set compute&#x2F;zone europe-west1-c&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;gcloud config set project actuator-sample&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;gcloud config set container&#x2F;use_client_certificate True&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;gcloud container clusters get-credentials actuator-example&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;kubectl delete secret registry.gitlab.com&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;kubectl create secret docker-registry registry.gitlab.com --docker-server=https:&#x2F;&#x2F;registry.gitlab.com --docker-username=marcolenzo --docker-password=$REGISTRY_PASSWD --docker-email=lenzo.marco@gmail.com&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;kubectl apply -f deployment.yml --namespace=production&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;environment&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;production&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;url&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;https:&#x2F;&#x2F;example.production.com&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;when&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;manual&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;only&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;production&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;environment&lt;&#x2F;code&gt; keyword associates the job with a specific environment while the &lt;code&gt;url&lt;&#x2F;code&gt; element is used to generate a handy hyperlink to our application on the GitLab Environments page (found under your project&#x27;s &lt;code&gt;Pipelines &amp;gt; Environments&lt;&#x2F;code&gt;). The &lt;code&gt;only&lt;&#x2F;code&gt; keyword signals to GitLab CI that the job should be executed only when the pipeline is building the listed branches. Finally, &lt;code&gt;when: manual&lt;&#x2F;code&gt; is used to turn the job execution from automatic to manual. Turning the execution of this job to &lt;code&gt;automatic&lt;&#x2F;code&gt; would project us in the world of &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;2016&#x2F;08&#x2F;05&#x2F;continuous-integration-delivery-and-deployment-with-gitlab&#x2F;#continuous-deployment&quot;&gt;Continuous Deployment&lt;&#x2F;a&gt; rather than &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;2016&#x2F;08&#x2F;05&#x2F;continuous-integration-delivery-and-deployment-with-gitlab&#x2F;#continuous-delivery&quot;&gt;Continuous Delivery&lt;&#x2F;a&gt;. From a Kubernetes perspective, we are making use of &lt;code&gt;namespaces&lt;&#x2F;code&gt; to segregate the different environments.&lt;&#x2F;p&gt;

&lt;p&gt;By committing on &lt;code&gt;master&lt;&#x2F;code&gt; and &lt;code&gt;production&lt;&#x2F;code&gt; we &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;2016&#x2F;08&#x2F;26&#x2F;ci-deployment-and-environments&#x2F;&quot;&gt;trigger a pipeline per environment&lt;&#x2F;a&gt;. As previously said, we are not making use of any collaboration tool because it is out of the scope of this tutorial. In real world scenarios, we would use &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;2016&#x2F;10&#x2F;25&#x2F;gitlab-workflow-an-overview&#x2F;#merge-request&quot;&gt;Merge Requests&lt;&#x2F;a&gt; with &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;features&#x2F;review-apps&#x2F;&quot;&gt;Review Apps&lt;&#x2F;a&gt; to move code across branches. Merge Requests allow the team to review and discuss the changes before they get merged into the target branch. &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;features&#x2F;review-apps&#x2F;&quot;&gt;Review Apps&lt;&#x2F;a&gt; take that one step further by spinning up dynamic environments for our merge requests offering the team access to a deployed instance of our application without the need of checking out the branch. This is extremely useful not only for non-technical members of the team, but also to collaborators and project managers to preview the changes without having to clone and install the app and its dependencies when evaluating a proposal.&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;git commit -am &lt;span class=&quot;s2&quot;&gt;&quot;Showcasing Pipelines&quot;&lt;&#x2F;span&gt;
git push origin master
git checkout -b production
git push origin production
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blogimages&#x2F;continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes&#x2F;pipelines.png&quot; alt=&quot;Pipelines&quot; class=&quot;shadow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;p&gt;The Pipelines screen details all pipeline executions. We can gather information about the branch and the individual result of each stage. In the case of the &lt;code&gt;production&lt;&#x2F;code&gt; pipeline the &lt;code&gt;k8s-deploy-production&lt;&#x2F;code&gt; is not executed automatically as expected but can be triggered from the GUI from where we can also download the build artifacts.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blogimages&#x2F;continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes&#x2F;environments.png&quot; alt=&quot;Environments&quot; class=&quot;shadow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;p&gt;Environments are listed in a separate page, from which it is possible to redeploy the latest version of an environment or to rollback to a particular version of the environment by accessing the relative details page.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blogimages&#x2F;continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes&#x2F;rollbacks.png&quot; alt=&quot;Rollbacks&quot; class=&quot;shadow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;

&lt;p&gt;In this tutorial, we were able to create a &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Continuous_delivery&quot;&gt;Continuous Delivery&lt;&#x2F;a&gt; pipeline with ease thanks to the suite of &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;&quot;&gt;GitLab&lt;&#x2F;a&gt; products that supported us at every stage. &lt;a href=&quot;https:&#x2F;&#x2F;projects.spring.io&#x2F;spring-boot&#x2F;&quot;&gt;Spring Boot&lt;&#x2F;a&gt; gave us agility by auto-configuring the application context and offering production grade services out of the box. &lt;a href=&quot;http:&#x2F;&#x2F;kubernetes.io&#x2F;&quot;&gt;Kubernetes&lt;&#x2F;a&gt; abstracted us from the compute resources and orchestration duties allowing us to define only the desired deployment state. &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;gitlab-ci&#x2F;&quot;&gt;GitLab CI&lt;&#x2F;a&gt; was the core engine of our pipeline. Its declarative &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;&quot;&gt;&lt;code&gt;.gitlab-ci.yml&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; file allowed us to define, version and manage our pipelines while the GUI gave us full visibility and control.&lt;&#x2F;p&gt;

&lt;p&gt;While this is a basic example, it shows clearly the immense benefits any team or company can gain by using the unified GUI of GitLab for issues, code review, CI and CD.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;about-guest-author&quot;&gt;About Guest Author&lt;&#x2F;h2&gt;

&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;marco_lenzo&quot;&gt;Marco Lenzo&lt;&#x2F;a&gt; is a Software Architect always up for a challenge. He has expertise in transaction processing and platform as a service (PaaS). Java, Spring, Go and Kubernetes are currently his bread and butter.&lt;&#x2F;p&gt;

&lt;!-- closes https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-com&#x2F;blog-posts&#x2F;issues&#x2F;309 --&gt;
&lt;!-- cover image: https:&#x2F;&#x2F;unsplash.com&#x2F;photos&#x2F;G86MS2ZsiJA --&gt;

&lt;style&gt;
  .h4 {
    font-weight: bold;
  }
&lt;&#x2F;style&gt;

&lt;img src=&#x27;https:&#x2F;&#x2F;about.gitlab.com&#x2F;images&#x2F;unsplash&#x2F;dew-leaf.jpg&#x27; class=&#x27;webfeedsFeaturedVisual&#x27; style=&#x27;display: none;&#x27; &#x2F;&gt;    </content>
  </entry>
  <entry>
    <title>GitLab and Gravitational discuss Kubernetes</title>
    <link rel="alternate" href="https://about.gitlab.com/2016/12/12/gitlab-joins-forces-with-gravitational/"/>
    <id>https://about.gitlab.com/2016/12/12/gitlab-joins-forces-with-gravitational/</id>
    <published>2016-12-12T00:00:00+00:00</published>
    <updated>2016-12-12T00:00:00+00:00</updated>
    <author>
      <name>Rebecca Dodd</name>
    </author>
    <content type="html">
&lt;p&gt;You never know where a conversation on Hacker News might take you. That&#x27;s what Ev Kontsevoy learned when he left a comment on a post, sparking a conversation with another reader who turned out to be GitLab CEO, Sid Sijbrandij. Later that day, at a party in San Francisco, Ev spotted another party guest wearing a GitLab T-shirt and approached him to chat. &quot;Turns out it was Sid, again!&quot; Ev says, laughing. &quot;It&#x27;s like he&#x27;s everywhere.&quot;&lt;&#x2F;p&gt;



&lt;p&gt;&quot;So we bumped into each other twice in the same day and when we started chatting. Sid mentioned that right now GitLab is trying to figure out the Kubernetes story: how to work with companies that rely heavily on Kubernetes, and frankly also how to use it internally.&quot; &lt;a href=&quot;https:&#x2F;&#x2F;gravitational.com&#x2F;&quot;&gt;Gravitational&lt;&#x2F;a&gt; is a Kubernetes company, so Ev was quick to offer their services, which is how GitLab ended up collaborating with Gravitational.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;what-did-we-work-on-together&quot;&gt;What did we work on together?&lt;&#x2F;h2&gt;

&lt;p&gt;&quot;One area that we were looking at together is how to run PostgreSQL on Kubernetes really well. It’s definitely a problem that most users will be trying to solve and it’s just one of the things that Gravitational is good at: we help our companies migrate their existing applications to Kubernetes, including databases. We&#x27;ve also been working on a GitLab path towards adopting Kubernetes for SaaS internally, after I bumped into some of your team members at a Kubernetes conference in Seattle.&quot;&lt;&#x2F;p&gt;

&lt;h2 id=&quot;why-kubernetes&quot;&gt;Why Kubernetes?&lt;&#x2F;h2&gt;

&lt;p&gt;Jacob Vosmaer, GitLab Senior Developer, had some questions for Ev about the partnership: &quot;What does a tool like Kubernetes make easier for an application like GitLab?&quot;&lt;&#x2F;p&gt;

&lt;p&gt;Ev: &quot;You mean, &#x27;Why even bother with Kubernetes at all?&#x27;&quot;&lt;&#x2F;p&gt;

&lt;p&gt;Jacob: &quot;I was being polite!&quot;&lt;&#x2F;p&gt;

&lt;p&gt;Ev: &quot;Server costs are a major line item on every company’s budget. When you are a certain size it can actually become more expensive than your engineering salaries. Developing software that utilizes servers effectively is difficult. This is where Kubernetes comes in. Unlike the old technology like virtualisation, which would statically partition your servers into smaller VMs, Kubernetes allows you to do this partitioning on the fly, which means that if your application needs more or less of particular resources as it runs, Kubernetes will be dynamically growing and shrinking different components of your application across the infrastructure that’s available to you. So that&#x27;s really the benefit of Kubernetes, you save a lot of money on hosting if you utilize it.&quot;&lt;&#x2F;p&gt;

&lt;h2 id=&quot;will-kubernetes-become-the-industry-standard&quot;&gt;Will Kubernetes become the industry standard?&lt;&#x2F;h2&gt;

&lt;p&gt;Ev: &quot;We&#x27;re probably going to witness something similar to Windows vs Linux, where there will probably be No. 1 and No. 2, and perhaps a very small No. 3. It feels to me that No. 1 and 2 are going to be Kubernetes and DC&#x2F;OS from Mesosphere, for two reasons: DC&#x2F;OS is an older product, it’s fairly mature, there are now Fortune 500 companies publicly using it. This kind of success doesn’t appear overnight. Even though technically Mesosphere technology is competing with Kubernetes and with what we do, I wish them the best and I do believe they are going to be noticeable.&quot;&lt;&#x2F;p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&quot;Kubernetes is an unstoppable force right now&quot;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;p&gt;&quot;In my line, Kubernetes is an unstoppable force right now, simply because there are so many companies with a proven track record of popularizing open source who are behind it. Companies like IBM, Red Hat, Google themselves – with so many backers pushing Kubernetes forward, it’s hard for me to imagine it not leading the platform. Look at what happened with Linux: it’s the exact same companies who keep pushing Linux Kernel forward. They&#x27;re now joining forces again to promote Kubernetes and to evolve and invest money into it.&quot;&lt;&#x2F;p&gt;

&lt;p&gt;&quot;Finally, there&#x27;s container pioneering company Docker, with their own technology called Docker Swarm, so Docker has enormous mind share with developers. Docker Swarm itself is kind of late out of the gate compared to Kubernetes and DC&#x2F;OS, and I’m very curious to see what’s going to happen. But those are the three players that I think will be carving out this pie, I don’t really see anyone else challenging that.&quot;&lt;&#x2F;p&gt;

&lt;p&gt;In addition to their work with GitLab on Kubernetes, Gravitational also helped us &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;issues&#x2F;22864&quot;&gt;build a Terminal into GitLab&lt;&#x2F;a&gt;, Ev explains, &quot;to allow developers or any GitLab users to easily jump inside the containers that are running on Kubernetes without leaving the GitLab environment&quot;. You can &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;2016&#x2F;11&#x2F;14&#x2F;idea-to-production&#x2F;&quot;&gt;find out how to use GitLab&#x27;s built-in terminal here&lt;&#x2F;a&gt;. Good news for open source! Thanks for working with us, Gravitational.&lt;&#x2F;p&gt;

&lt;p&gt;Image: &quot;&lt;a href=&quot;https:&#x2F;&#x2F;www.flickr.com&#x2F;photos&#x2F;rachaelvoorhees&#x2F;828353700&#x2F;&quot;&gt;Take the wheel and drive&lt;&#x2F;a&gt;&quot; by &lt;a href=&quot;https:&#x2F;&#x2F;www.flickr.com&#x2F;photos&#x2F;rachaelvoorhees&#x2F;&quot;&gt;rachaelvoorhees&lt;&#x2F;a&gt; is licensed under &lt;a href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by&#x2F;2.0&#x2F;&quot;&gt;CC BY 2.0&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;img src=&#x27;https:&#x2F;&#x2F;about.gitlab.com&#x2F;images&#x2F;blogimages&#x2F;ship-steering-wheel-kubernetes.jpg&#x27; class=&#x27;webfeedsFeaturedVisual&#x27; style=&#x27;display: none;&#x27; &#x2F;&gt;    </content>
  </entry>
  <entry>
    <title>Proposed server purchase for GitLab.com</title>
    <link rel="alternate" href="https://about.gitlab.com/2016/12/11/proposed-server-purchase-for-gitlab-com/"/>
    <id>https://about.gitlab.com/2016/12/11/proposed-server-purchase-for-gitlab-com/</id>
    <published>2016-12-11T00:00:00+00:00</published>
    <updated>2016-12-11T00:00:00+00:00</updated>
    <author>
      <name>Sid Sijbrandij</name>
    </author>
    <content type="html">
&lt;p&gt;We want to make GitLab.com fast and we &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;2016&#x2F;11&#x2F;10&#x2F;why-choose-bare-metal&#x2F;&quot;&gt;knew it was time to leave the cloud&lt;&#x2F;a&gt; and purchase our own servers.
In this post is our thinking about what chassis, rack, memory, CPU, network, power, and hosting to buy.
We wanted to share what we learned and get your feedback on our proposal and questions.
When you reply to a question in the comments on our blog or Hacker News please reference it with the letter and number: &#x27;Regarding R1&#x27;.
We&#x27;ll try to update the questions with preliminary answers as we learn more.&lt;&#x2F;p&gt;



&lt;h1 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h1&gt;

&lt;p&gt;Today, GitLab.com hosts 96TB of data, and that number is growing rapidly. We
are attempting to build a fault-tolerant and performant CephFS cluster. We are
also attempting to move GitLab application servers and supporting services
(e.g. PostgreSQL) to bare metal.&lt;&#x2F;p&gt;

&lt;p&gt;Note that for now our CI Runners will stay in the cloud. Not only are they are
much less sensitive to latency, but autoscaling is easier with a cloud service.&lt;&#x2F;p&gt;

&lt;h1 id=&quot;chassis&quot;&gt;Chassis&lt;&#x2F;h1&gt;

&lt;p&gt;One of the team members that will join GitLab in 2017 recommended using a &lt;a href=&quot;https:&#x2F;&#x2F;www.supermicro.nl&#x2F;products&#x2F;system&#x2F;2U&#x2F;6028&#x2F;SYS-6028TP-HTTR.cfm&quot;&gt;6028TP-HTTR SuperMicro 2U Twin2 server&lt;&#x2F;a&gt; chassis that has 4 dual processor nodes and is 2 &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Rack_unit&quot;&gt;rack units&lt;&#x2F;a&gt; (U) high. The advantages are:&lt;&#x2F;p&gt;

&lt;ol&gt;
  &lt;li&gt;Great density, 0.5U per dual processor server&lt;&#x2F;li&gt;
  &lt;li&gt;You have one common form factor&lt;&#x2F;li&gt;
  &lt;li&gt;Power supplies are shared for great efficiency similar to &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Blade_server&quot;&gt;blade servers&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;The network is per node for more bandwidth and reliability (like individual server)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;

&lt;p&gt;We use the &lt;a href=&quot;https:&#x2F;&#x2F;www.supermicro.com&#x2F;products&#x2F;nfo&#x2F;2UTwin2.cfm&quot;&gt;2U Twin2&lt;&#x2F;a&gt; instead of the &lt;a href=&quot;https:&#x2F;&#x2F;www.supermicro.com&#x2F;products&#x2F;nfo&#x2F;1UTwin.cfm&quot;&gt;1U Twin&lt;&#x2F;a&gt; because it fits one more 3.5&quot; hard drive (3 per node instead of 2).&lt;&#x2F;p&gt;

&lt;p&gt;This server is on the list of global SKU&#x27;s for SuperMicro.
We&#x27;ll also ask for quotes from other vendors to see if they have a competitive alternative.
For example HPE has the &lt;a href=&quot;https:&#x2F;&#x2F;www.hpe.com&#x2F;h20195&#x2F;v2&#x2F;getpdf.aspx&#x2F;c04542552.pdf?ver=7&quot;&gt;Apollo 2000 series&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;C1 Should we use another version of the chassis than HTTR?&lt;&#x2F;p&gt;

&lt;p&gt;C2 What is the best Dell equivalent? =&amp;gt; &lt;a href=&quot;http:&#x2F;&#x2F;www.dell.com&#x2F;us&#x2F;business&#x2F;p&#x2F;poweredge-c6320&#x2F;pd&quot;&gt;C6320&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;

&lt;h1 id=&quot;servers&quot;&gt;Servers&lt;&#x2F;h1&gt;

&lt;p&gt;We need the following servers:&lt;&#x2F;p&gt;

&lt;ol&gt;
  &lt;li&gt;32x File storage (CephFS OSD)&lt;&#x2F;li&gt;
  &lt;li&gt;3x File Monitoring (CephFS MON)&lt;&#x2F;li&gt;
  &lt;li&gt;8x Application server (&lt;a href=&quot;https:&#x2F;&#x2F;bogomips.org&#x2F;unicorn&#x2F;&quot;&gt;Unicorn&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;7x Background jobs (&lt;a href=&quot;http:&#x2F;&#x2F;sidekiq.org&#x2F;&quot;&gt;Sidekiq&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;5x Key value store (&lt;a href=&quot;https:&#x2F;&#x2F;redis.io&#x2F;topics&#x2F;sentinel&quot;&gt;Redis Sentinel&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;4x Database (PostgreSQL)&lt;&#x2F;li&gt;
  &lt;li&gt;3x Load balancers (HAproxy)&lt;&#x2F;li&gt;
  &lt;li&gt;1x Staging&lt;&#x2F;li&gt;
  &lt;li&gt;1x Spare&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;

&lt;p&gt;For a total of 64 nodes.&lt;&#x2F;p&gt;

&lt;p&gt;We would like to have one common node so that they are interchangable.
This would mean installing only a few disks per node instead of having large fileservers.
This would distribute failures and IO.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blogimages&#x2F;write_iops.png&quot; alt=&quot;IOPS on GitLab.com&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;p&gt;The above picture shows the currently number of Input&#x2F;output Operations Per
Second (IOPS) on GitLab.com. On our current NFS servers, our peak write IOPS
often hit close to 500K, and our peak read IOPS reach 200K. These numbers
suggest that using spinning disks alone may not be enough; we need to use
high-performance SSDs judiciously.&lt;&#x2F;p&gt;

&lt;p&gt;One task that we could not fit on the common nodes was PostgreSQL.
Our current plan is to make PostgreSQL distributed in 2017 with the help of &lt;a href=&quot;https:&#x2F;&#x2F;www.citusdata.com&#x2F;&quot;&gt;Citus&lt;&#x2F;a&gt;.
But for now, we need to scale vertically so we need a lot of memory and CPU.
We need at least a primary and secondary database.
We wanted to add a second pair for testing and to ensure spares in case of failure.
Details about this are in the following sections.&lt;&#x2F;p&gt;

&lt;p&gt;Choosing a common node will mean that file storage servers will have too much CPU and that application servers will have too much disk space.
We plan to remedy that by running everything on Kubernetes.
This allows us to have a blended workload using all CPU and disk.
For example we can combine file storage and background jobs on the same server since one is disk heavy and one is CPU heavy.
We will start by having one workload per server to reduce complexity.
This means that when we need to grow we can still unlock almost twice as much disk space and CPU by blending the workloads.
Please note that this will be container based, to get maximum IO performance we won&#x27;t virtualize our workload.&lt;&#x2F;p&gt;

&lt;p&gt;S1 Shall we spread the database servers among different chassis to make sure they don&#x27;t all fail when one chassis fails?&lt;&#x2F;p&gt;

&lt;p&gt;S2 Does Ceph handle running 60 OSD nodes well or can this cause problems?&lt;&#x2F;p&gt;

&lt;h1 id=&quot;cpu&quot;&gt;CPU&lt;&#x2F;h1&gt;

&lt;p&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;www.supermicro.nl&#x2F;products&#x2F;system&#x2F;2U&#x2F;6028&#x2F;SYS-6028TP-HTTR.cfm&quot;&gt;SuperServer 6028TP-HTTR&lt;&#x2F;a&gt; supports dual E5-2600v4 processors per node.
We think the &lt;a href=&quot;http:&#x2F;&#x2F;ark.intel.com&#x2F;products&#x2F;92981&#x2F;Intel-Xeon-Processor-E5-2630-v4-25M-Cache-2_20-GHz&quot;&gt;E5-2630v4&lt;&#x2F;a&gt; is a good blend of power and cost.
It has 20 virtual cores at 2.20Ghz, 25MB cache, and costs about $669 per processor.
Every physical core is two virtual cores due to &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Hyper-threading&quot;&gt;hyperthreading&lt;&#x2F;a&gt;.
A slightly more powerful processor is the &lt;a href=&quot;https:&#x2F;&#x2F;ark.intel.com&#x2F;products&#x2F;92984&#x2F;Intel-Xeon-Processor-E5-2640-v4-25M-Cache-2_40-GHz&quot;&gt;E5-2640v4&lt;&#x2F;a&gt; but while the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;SPECint&quot;&gt;SPECint score&lt;&#x2F;a&gt; increases from 845 to 887 the costs increase from $669 to $939.
You can find the scores by entering a &lt;a href=&quot;https:&#x2F;&#x2F;www.spec.org&#x2F;cgi-bin&#x2F;osgresults?conf=rint2006&quot;&gt;search on spec.org&lt;&#x2F;a&gt; with &#x27;Hewlett Packard Enterprise&#x27; as the hardware vendor and looking for ProLiant DL360 Gen9 as the platform.&lt;&#x2F;p&gt;

&lt;p&gt;Our current SQL server has one E5-2698B v3 with 32 virtual cores.
PostgreSQL commonly uses about 20-25 virtual cores.
Moving to dual processors should already help a lot.
To give us more months to grow before having to distribute the database we want to purchase some headroom.
That is why we&#x27;re getting a &lt;a href=&quot;https:&#x2F;&#x2F;ark.intel.com&#x2F;products&#x2F;91750&#x2F;Intel-Xeon-Processor-E5-2687W-v4-30M-Cache-3_00-GHz&quot;&gt;E5-2687Wv4&lt;&#x2F;a&gt; for the database servers.
This processor costs $2100 instead of $670 but has 4 extra virtual cores and runs continuously on 3 Ghz instead of 2.2 Ghz.
Comprated to the E5-2630v4 that leads to a SPEC score or 1230 instead of 845 and 51.3 SPEC per virtual core instead of 42.3.
For the 4 dual processor database servers this upgrade will cost $11k.
We think it is worth it since the 20-40% of extra performence will buy us the month or two of extra time to distribute the database that we need.&lt;&#x2F;p&gt;

&lt;h1 id=&quot;disk&quot;&gt;Disk&lt;&#x2F;h1&gt;

&lt;p&gt;Every node can fit 3 larger (3.5&quot;) harddrives.
We plan to purchase the largest one available, a 8TB Seagate with 6Gb&#x2F;s SATA and 7.2K RPM.
At 60 nodes this will give us 1.4PB of raw storage.
At a replication factor of 3 for Ceph this is 480TB of usable storage.
Right now GitLab.com uses 96TB (54TB for repo&#x27;s, 21TB for uploads, 21TB for LFS and build artifacts) so we can grow by a factor of almost 5.&lt;&#x2F;p&gt;

&lt;p&gt;Disks can be slow so we looked at improving latency.
Higher RPM hard drives typically come in &lt;a href=&quot;http:&#x2F;&#x2F;www.seagate.com&#x2F;enterprise-storage&#x2F;hard-disk-drives&#x2F;enterprise-performance-15k-hdd&#x2F;&quot;&gt;GB instead of TB sizes&lt;&#x2F;a&gt;.
Going all SSD is too expensive.
To improve latency we plan to fit every server with an SSD card.
On the fileservers this will be used as a cache.
We&#x27;re thinking about using &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Bcache&quot;&gt;Bcache&lt;&#x2F;a&gt; for this.&lt;&#x2F;p&gt;

&lt;p&gt;We plan to use &lt;a href=&quot;http:&#x2F;&#x2F;www.intel.com&#x2F;content&#x2F;www&#x2F;us&#x2F;en&#x2F;solid-state-drives&#x2F;ssd-dc-p3700-spec.html&quot;&gt;Intel DC P3700 series&lt;&#x2F;a&gt; or slight less powerful &lt;a href=&quot;http:&#x2F;&#x2F;www.intel.com&#x2F;content&#x2F;www&#x2F;us&#x2F;en&#x2F;solid-state-drives&#x2F;ssd-dc-p3600-spec.html&quot;&gt;P3600 series&lt;&#x2F;a&gt; of SSD&#x27;s because they are recommended by the CephFS experts we hired.
For most servers it will be the &lt;a href=&quot;http:&#x2F;&#x2F;www.supermicro.com&#x2F;products&#x2F;nfo&#x2F;PCI-E_SSD.cfm?show=Intel&quot;&gt;800GB SSDPEDMD800G4&lt;&#x2F;a&gt;.
For the database servers we plan to use the the 1.6TB variant to have more headroom.
The endurance we need for the database server is 90TB&#x2F;year, the 3600 series is already above 4PB of endurance.&lt;&#x2F;p&gt;

&lt;p&gt;We plan to add a 64GB &lt;a href=&quot;https:&#x2F;&#x2F;www.supermicro.com&#x2F;products&#x2F;nfo&#x2F;SATADOM.cfm&quot;&gt;SSD SATADOM boot drive&lt;&#x2F;a&gt; to the servers to boot from.
This way we can keep the large SSD as a separate volume.&lt;&#x2F;p&gt;

&lt;p&gt;D1 We plan to configure the disks as just a bunch of disks (JBOD) but heard that this caused performance problems with some controllers. Is this likely to impact us?&lt;&#x2F;p&gt;

&lt;p&gt;D2 Should we use Bcache to improve latency on on the Ceph OSD servers with SSD? =&amp;gt; Make sure you&#x27;re using a kernel &amp;gt;= 4.5, since that&#x27;s when a bunch of stability patches landed (https:&#x2F;&#x2F;lkml.org&#x2F;lkml&#x2F;2015&#x2F;12&#x2F;5&#x2F;38).&lt;&#x2F;p&gt;

&lt;p&gt;D3 We heard concerns about fitting the PCIe 3.0 x 4 SSD card into &lt;a href=&quot;https:&#x2F;&#x2F;www.supermicro.nl&#x2F;products&#x2F;system&#x2F;2U&#x2F;6028&#x2F;SYS-6028TP-HTTR.cfm&quot;&gt;our chassis&lt;&#x2F;a&gt; that supports a PCI-E 3.0 x16 Low-profile slot. Will this fit? =&amp;gt; &lt;a href=&quot;http:&#x2F;&#x2F;disq.us&#x2F;p&#x2F;1eedj2n&quot;&gt;Florian Heigl&lt;&#x2F;a&gt;: &quot;Somewhat unlikely you will be able to fit a P3700. I have a Twin^2 too and the only SSD I could fit there was a consumer NVME with a PCIe adapter board.&quot;&lt;&#x2F;p&gt;

&lt;p&gt;D4 Should we ask for 8TB HGST drives instead of Seagate since they seem &lt;a href=&quot;https:&#x2F;&#x2F;www.backblaze.com&#x2F;blog&#x2F;hard-drive-reliability-stats-q1-2016&#x2F;&quot;&gt;more reliable&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;D5 Is it a good idea to have a boot drive or should we use &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Preboot_Execution_Environment&quot;&gt;PXE boot&lt;&#x2F;a&gt; every time it starts? =&amp;gt; &lt;a href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=13153336&quot;&gt;dsr_&lt;&#x2F;a&gt;: You want a local boot drive, and you want it to fall back to PXE booting if the local drive is unavailable. Your PXE image should default to the last known working image, and have a boot-time menu with options for a rescue image and an installer for your distribution of choice.&lt;&#x2F;p&gt;

&lt;p&gt;D6 Should we go for the 3700 series SSD or save some money and go for the 3600 series? Both for the normal and the SQL servers?&lt;&#x2F;p&gt;

&lt;p&gt;D7 We&#x27;re planning on one SSD per node. For the OSD nodes (file server) that would mean having the Ceph journal and bcache on the same SSD. Is this a good idea?&lt;&#x2F;p&gt;

&lt;h1 id=&quot;memory&quot;&gt;Memory&lt;&#x2F;h1&gt;

&lt;p&gt;Suppose one node runs both as application server and fileserver.
We recommend virtual cores + 1 instances of Unicorn of about 0.5GB each, for a total of 21GB per node (2 processors * 21 unicorns per processor * 0.5GB).
Ceph recommends about 1GB per TB of data which comes out to 24 per node.
So theoretically we can fit everything in 45GB so 64GB should be enough.&lt;&#x2F;p&gt;

&lt;p&gt;But in practice we&#x27;ve seen 24TB OSD nodes use 79GB of memory.
And the rule of thumb is have about 2GB per virtual core for background jobs available (40GB).
So in order not to be to low we&#x27;ll spend the extra $30k to have 128GB of ECC memory per node instead of 64GB.&lt;&#x2F;p&gt;

&lt;p&gt;For the SQL nodes we&#x27;ll need much more memory, we currently give it 440GB and it uses all of that.
The database is about 250GB in size and growing with 40GB per month.
At 250GB of server memory we redlined the server, probably because it no longer fits into memory.
Theoretically the server supports 2TB of memory but it needs to fit in 16 memory slots per node.
We wanted to start with 1TB per server but we&#x27;re not sure if we should go from a 64GB DIMM to 128GB to be able to expand later.
By having only half of the memory banks full you get half the bandwidth.
And 64GB DIMMs already cost twice as much per GB as 32GB DIMMs, let alone 128GB ones.
At a price of about $940 per 64 DIMM the cost for 1TB of memory already is $15k per server.&lt;&#x2F;p&gt;

&lt;p&gt;Note that larger sizes such as 64GB come in the form of LRDIMM that has a &lt;a href=&quot;https:&#x2F;&#x2F;www.microway.com&#x2F;hpc-tech-tips&#x2F;ddr4-rdimm-lrdimm-performance-comparison&#x2F;&quot;&gt;small performance penalty&lt;&#x2F;a&gt; but this looks acceptable.&lt;&#x2F;p&gt;

&lt;p&gt;M1. Should we use 128GB DIMMS to be able to expand the database server later even though the will double the cost and half the bandwidth?&lt;&#x2F;p&gt;

&lt;h1 id=&quot;network&quot;&gt;Network&lt;&#x2F;h1&gt;

&lt;p&gt;The servers come with 2x 10Gbps RJ45 by default (Intel X540 Dual port 10GBase-T).
We want to &lt;a href=&quot;https:&#x2F;&#x2F;docs.oracle.com&#x2F;cd&#x2F;E37670_01&#x2F;E41138&#x2F;html&#x2F;ch11s05.html&quot;&gt;dual bound&lt;&#x2F;a&gt; the network connections to increase performance and reliability.
This will allow us to take routers out of service during low traffic times, for example to restart them after a software upgrade.
We think that 20Gbps is enough bandwidth to handle our data access and replication needs, right now our higest peaks are 1 Gbps.
This is important because we want to have minimal latency between the Ceph servers so network congestion would be a problem.&lt;&#x2F;p&gt;

&lt;p&gt;Ceph reference designs recommend a seperated front and back network with the back network reserved for Ceph traffic.
We think that this is not needed as long as there is enough capacity.
We do want to have user request termination in a DMZ, so our HA proxy servers will be the only ones with a public IP.&lt;&#x2F;p&gt;

&lt;p&gt;Each of the two physical network connections will connect to a different top of rack router.
We want to get a Software Defined Networking (SDN) compatible router so we have flexibility there.
We&#x27;re considering the &lt;a href=&quot;https:&#x2F;&#x2F;www.supermicro.com&#x2F;products&#x2F;accessories&#x2F;Networking&#x2F;SSE-X3648S.cfm&quot;&gt;10&#x2F;40GbE SDN SuperSwitch (SSE-X3648S&#x2F;SSE-X3648SR)&lt;&#x2F;a&gt; that can switch 1440 Gbps.&lt;&#x2F;p&gt;

&lt;p&gt;Apart from those routers we&#x27;ll have a separate router for a 1Gbps management network.
For example to make &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;STONITH&quot;&gt;STONITH&lt;&#x2F;a&gt; reliable when there is a lot of traffic on the normal network.
Each node already has a separate 1Gbps connection for this.&lt;&#x2F;p&gt;

&lt;p&gt;We have 64+1 nodes (1 for backup) and most routers seem to have 48 ports.
Every node has 2 network ports so that is a need for 130 ports in total.
We&#x27;re not use if we can use 3 routers with 48 ports each (144 in total) to cover that.&lt;&#x2F;p&gt;

&lt;p&gt;N1 Which router should we purchase?&lt;&#x2F;p&gt;

&lt;p&gt;N2 How do we interconnect the routers while keeping the network simple and fast?&lt;&#x2F;p&gt;

&lt;p&gt;N3 Should we have a separate network for Ceph traffic?&lt;&#x2F;p&gt;

&lt;p&gt;N4 Do we need an SDN compatible router or can we purchase something more affordable?&lt;&#x2F;p&gt;

&lt;p&gt;N5 What router should we use for the management network?&lt;&#x2F;p&gt;

&lt;h1 id=&quot;backup&quot;&gt;Backup&lt;&#x2F;h1&gt;

&lt;p&gt;We&#x27;re still early in figuring out the backup solution so there are still lots of questions.&lt;&#x2F;p&gt;

&lt;p&gt;Backing up 480TB of data (expected size in 2017) is pretty hard.
We thought about using &lt;a href=&quot;https:&#x2F;&#x2F;cloud.google.com&#x2F;storage-nearline&#x2F;&quot;&gt;Google Nearline&lt;&#x2F;a&gt; because with a price of $0.01 per GB per month means that for $4800 we don&#x27;t have to worry about much.
But restoring that over a 1Gbps connection takes 44 days, way too long.&lt;&#x2F;p&gt;

&lt;p&gt;We mainly want our backup to protect us against human and software errors.
Because all the files are already replicated 3 times hardware errors are unlikely to affect us.
Of course we should have a good &lt;a href=&quot;http:&#x2F;&#x2F;docs.ceph.com&#x2F;docs&#x2F;jewel&#x2F;rados&#x2F;operations&#x2F;crush-map&#x2F;&quot;&gt;Ceph CRUSH map&lt;&#x2F;a&gt; to prevent storing multiple copies on the same chassis.&lt;&#x2F;p&gt;

&lt;p&gt;We&#x27;re most afraid of human error or Ceph corruption. For that reason we don&#x27;t want to replicate on the Ceph level but on the file level.&lt;&#x2F;p&gt;

&lt;p&gt;We&#x27;re thinking about using &lt;a href=&quot;https:&#x2F;&#x2F;www.bareos.org&#x2F;en&#x2F;&quot;&gt;Bareos backup software&lt;&#x2F;a&gt; to replicate to a huge fileserver.
We&#x27;re inspired by the posts about the &lt;a href=&quot;https:&#x2F;&#x2F;www.backblaze.com&#x2F;blog&#x2F;open-source-data-storage-server&#x2F;&quot;&gt;latest 480TB Backblaze storage pod 6.0&lt;&#x2F;a&gt; and these are available for $6k without drives from &lt;a href=&quot;https:&#x2F;&#x2F;www.backuppods.com&#x2F;&quot;&gt;Backuppods&lt;&#x2F;a&gt;.
But SuperMicro offers a &lt;a href=&quot;https:&#x2F;&#x2F;www.supermicro.com&#x2F;products&#x2F;chassis&#x2F;4U&#x2F;946&#x2F;SC946ED-R2KJBOD&quot;&gt;comparable solution in the form of a SuperChassis that can hold 90 drives&lt;&#x2F;a&gt;.
At 8TB per drive that is 720TB of raw storage.
Even with RAID overhead it should be possible to have 480TB of usable storage (66%).&lt;&#x2F;p&gt;

&lt;p&gt;The SuperChassis is only hard drives, it still needs a controller. In a &lt;a href=&quot;https:&#x2F;&#x2F;nexenta.com&#x2F;sites&#x2F;default&#x2F;files&#x2F;docs&#x2F;Nexenta_SMC_RA_DataSheet.pdf&quot;&gt;reference architecture by Nexenta (PDF download)&lt;&#x2F;a&gt; two &lt;a href=&quot;https:&#x2F;&#x2F;www.supermicro.com&#x2F;products&#x2F;system&#x2F;2u&#x2F;6028&#x2F;sys-6028u-tr4_.cfm&quot;&gt;SYS6028U&lt;&#x2F;a&gt; with E5-2643v3 processors and 256GB of RAM is recommended. Unlike smaller configurations this one doesn&#x27;t come with an SSD for &lt;a href=&quot;https:&#x2F;&#x2F;blogs.oracle.com&#x2F;brendan&#x2F;entry&#x2F;test&quot;&gt;ZFS L2ARC&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;Since backups are mostly linear we don&#x27;t need an SSD for caching. In general 1GB of memory per TB of raw ZFS disk space is recommended. That would mean getting 512GB of RAM, 16x 32GB. Unlike the reference architecture we&#x27;ll go with one controller. We&#x27;re considering the &lt;a href=&quot;https:&#x2F;&#x2F;www.supermicro.com&#x2F;products&#x2F;system&#x2F;1U&#x2F;1028&#x2F;SYS-1028R-WC1RT.cfm&quot;&gt;SuperServer 1028R-WC1RT&lt;&#x2F;a&gt; since it is similar to our other servers, 1U, has 2x 10Gbps, 16 DIMM slots, and has 2 PCI slots. We&#x27;ll use our regular &lt;a href=&quot;http:&#x2F;&#x2F;ark.intel.com&#x2F;products&#x2F;92981&#x2F;Intel-Xeon-Processor-E5-2630-v4-25M-Cache-2_20-GHz&quot;&gt;E5-2630v4&lt;&#x2F;a&gt; processor.&lt;&#x2F;p&gt;

&lt;p&gt;The question is if this controller can saturate the 20 Gbps uplink.
For this it needs to use both 12 Gbps SAS buses.
And each drive has to do at least 30 MBps which seems reasonable for a continuous read.&lt;&#x2F;p&gt;

&lt;p&gt;The problem is that even at 20Gbps a full restore takes 2 days.
Of course many times you need to restore only part of the files (uploads).
And most of the time it won&#x27;t contain 480TB (we&#x27;ll start at about 100TB).
The question is if we can accept this worst case scenario for GitLab.com.&lt;&#x2F;p&gt;

&lt;p&gt;An alternative would be to use multiple controllers.
But you can&#x27;t aggregate ZFS pools over multiple servers.
Another option would be to have one controller with more IO.
We can use multiple disk enclosures and multiple SAS buses.
And we can add more network ports and&#x2F;or switch to 40Gbps.
But this all seems pretty complicated.&lt;&#x2F;p&gt;

&lt;p&gt;B0 Are we on the right track here or is 20 Gbps of restore speed not OK?&lt;&#x2F;p&gt;

&lt;p&gt;B1 Should we go for the &lt;a href=&quot;https:&#x2F;&#x2F;www.supermicro.com&#x2F;products&#x2F;chassis&#x2F;4U&#x2F;?chs=946&quot;&gt;90 or 60 drive SuperChassis&lt;&#x2F;a&gt;? It looks like 60 drive one has more peak power (1600W vs. 800W) to start the drives.&lt;&#x2F;p&gt;

&lt;p&gt;B2 How should we configure the SuperChassis? &lt;a href=&quot;http:&#x2F;&#x2F;zfsonlinux.org&#x2F;&quot;&gt;ZFS on Linux&lt;&#x2F;a&gt; with &lt;a href=&quot;https:&#x2F;&#x2F;icesquare.com&#x2F;wordpress&#x2F;zfs-performance-mirror-vs-raidz-vs-raidz2-vs-raidz3-vs-striped&#x2F;&quot;&gt;RAIDZ3&lt;&#x2F;a&gt;?&lt;&#x2F;p&gt;

&lt;p&gt;B3 Will the SuperChassis be able to saturate the 20Gbsp connection?&lt;&#x2F;p&gt;

&lt;p&gt;B4 Should we upgrade the networking on the SuperChassis to be able to restore even faster?&lt;&#x2F;p&gt;

&lt;p&gt;B5 Is Bareos the right software to use?&lt;&#x2F;p&gt;

&lt;p&gt;B6 How should we configure the backup software?  Should we use incremental backups with parallel jobs to speed things up?&lt;&#x2F;p&gt;

&lt;p&gt;B7 Should we use the live filesystem or &lt;a href=&quot;http:&#x2F;&#x2F;docs.ceph.com&#x2F;docs&#x2F;master&#x2F;dev&#x2F;cephfs-snapshots&#x2F;&quot;&gt;CephFS snapshots&lt;&#x2F;a&gt; to back up from?&lt;&#x2F;p&gt;

&lt;p&gt;B8 How common is it to have a tape or cloud backup in addition to the above?&lt;&#x2F;p&gt;

&lt;p&gt;B9 Should we pick the top load model or &lt;a href=&quot;https:&#x2F;&#x2F;www.supermicro.com&#x2F;products&#x2F;chassis&#x2F;JBOD&#x2F;index.cfm?show=SELECT&amp;amp;storage=90&quot;&gt;one of the front and rear access models&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;B10 Can we connect two SAS cables to get 2x 12 Gbps?&lt;&#x2F;p&gt;

&lt;p&gt;B11 What &lt;a href=&quot;https:&#x2F;&#x2F;www.supermicro.com&#x2F;products&#x2F;nfo&#x2F;storage_cards.cfm&quot;&gt;HBA card&lt;&#x2F;a&gt; should be added to the controller or does it come with an LSI 3108?&lt;&#x2F;p&gt;

&lt;p&gt;B12 Is it smart to make the controller a seperate 1U box or should we repurpose some of our normal nodes for this?&lt;&#x2F;p&gt;

&lt;p&gt;B13 Any hints on how to test the backup restore (on AWS or our hardware, how often, etc.)?&lt;&#x2F;p&gt;

&lt;h1 id=&quot;rack&quot;&gt;Rack&lt;&#x2F;h1&gt;

&lt;p&gt;The default rack height seems to be 45U nowadays (42U used to be the standard).&lt;&#x2F;p&gt;

&lt;p&gt;It is used as follows:&lt;&#x2F;p&gt;

&lt;ul&gt;
  &lt;li&gt;32U for 16 chassis with 64 nodes&lt;&#x2F;li&gt;
  &lt;li&gt;3U for three network routers&lt;&#x2F;li&gt;
  &lt;li&gt;1U for the management network&lt;&#x2F;li&gt;
  &lt;li&gt;4U for the disk enclosure&lt;&#x2F;li&gt;
  &lt;li&gt;1U for the disk controller&lt;&#x2F;li&gt;
  &lt;li&gt;4U spare for 2 new chassis (maybe distributed PostgreSQL servers)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h1 id=&quot;power&quot;&gt;Power&lt;&#x2F;h1&gt;

&lt;p&gt;Each chassis has a 2000 watt power supply (comes to 1kW per U), 32kW in total.
Normal usage is guessed at 60% of the rated capacity, about 19kW.
That doesn&#x27;t account for the routers and backup.
Both hosting providers quoted 4 x 208v 30A power supplies (2 for redundancy).&lt;&#x2F;p&gt;

&lt;p&gt;P1 Does the quoted supply seem adequate for our needs?&lt;&#x2F;p&gt;

&lt;h1 id=&quot;hosting&quot;&gt;Hosting&lt;&#x2F;h1&gt;

&lt;p&gt;We&#x27;ve worked in &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-com&#x2F;infrastructure&#x2F;issues&#x2F;732&quot;&gt;an issue&lt;&#x2F;a&gt; to see where we should host.&lt;&#x2F;p&gt;

&lt;p&gt;Apart from the obvious (reliable, affordable) we had the following needs:&lt;&#x2F;p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;aws.amazon.com&#x2F;directconnect&#x2F;details&#x2F;&quot;&gt;AWS Direct connect&lt;&#x2F;a&gt; so we can use the cloud for temporary application server needs&lt;&#x2F;li&gt;
  &lt;li&gt;Based on the east coast of the USA since it provides the best latency tradeoff for most of our users&lt;&#x2F;li&gt;
  &lt;li&gt;Advanced remote hands service so we don&#x27;t have to station people near the datacenter at all times&lt;&#x2F;li&gt;
  &lt;li&gt;Ability to upgrade from one rack to a private cage&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;p&gt;The following networking options are a plus:&lt;&#x2F;p&gt;

&lt;ul&gt;
  &lt;li&gt;Carrier neutral (all major global network providers in its meet-me facility)&lt;&#x2F;li&gt;
  &lt;li&gt;Backbones to other locations to provide cheap 2nd site transit&lt;&#x2F;li&gt;
  &lt;li&gt;CDN services to reduce origin bandwidth costs&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;p&gt;So far we&#x27;ve gotten quotes from &lt;a href=&quot;http:&#x2F;&#x2F;www.qtsdatacenters.com&#x2F;data-centers&#x2F;ashburn&quot;&gt;QTS in Ashburn, VA&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;www.nyi.net&#x2F;datacenters&#x2F;new-jersey&#x2F;&quot;&gt;NYI in Bridgewater, NJ&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;H1 Any watchouts when selecting hosting providers?&lt;&#x2F;p&gt;

&lt;p&gt;H2 Should we install the servers ourselves or is it OK to let the hosting provider do that?&lt;&#x2F;p&gt;

&lt;p&gt;H3 How can we minimize installation costs? Should we ask to configure the servers to PXE boot?&lt;&#x2F;p&gt;

&lt;p&gt;H4 Is there an Azure equivalent for AWS Direct Connect? =&amp;gt; Azure will let you work with a provider to &quot;peer into&quot; the Azure network at a data center of your choice. So for example we could pay to have a circuit established in a data center that was linked into the Azure &#x27;US East 2&#x27; data center (where we currently host out of) for direct connectivity needs.&lt;&#x2F;p&gt;

&lt;h1 id=&quot;expense&quot;&gt;Expense&lt;&#x2F;h1&gt;

&lt;p&gt;We can&#x27;t give cost details since all the quotes we receive are confidential.
The cloud hosting for GitLab.com excluding GitLab CI is currently costing us about $200k per month.
The capital needed for going to metal would be less than we pay for 1 quarter of hosting.
The hosting facility costs look to be less than $10k per month.
If you spread the capital costs over 2.5 years (10 quarters) it is 10x cheaper to host your own.&lt;&#x2F;p&gt;

&lt;p&gt;Of course the growth of GitLab.com will soon force us to buy additional hardware.
But we would also have to pay extra for additional cloud capacity.
Our proposed buying plan is about 5x the capacity we need now.
Having your own hardware means you&#x27;re always overprovisioned.
And we could probably have reduced the cost of cloud hosting by focussing on it.&lt;&#x2F;p&gt;

&lt;p&gt;The bigger expense will be hiring more people to deal with the additional complexity.
We&#x27;ll probably need to hire a couple of people more to deal with this.
We&#x27;re hiring &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;jobs&#x2F;production-engineer&#x2F;&quot;&gt;production engineers&lt;&#x2F;a&gt; and if you&#x27;re spotting mistakes in this post we would love to talk to you (and if you didn&#x27;t spot many mistakes but think you can help us we also want to talk to you).&lt;&#x2F;p&gt;

&lt;p&gt;We looked into initially having disks in only half the servers but that saves only $20k ($225 per disk) and it would create a lot of work when we eventually have to install them.&lt;&#x2F;p&gt;

&lt;p&gt;E1 If we want to look at leasing should we do that through SuperMicro or third party?&lt;&#x2F;p&gt;

&lt;p&gt;E2 Are there ways we can save money?&lt;&#x2F;p&gt;

&lt;h1 id=&quot;details&quot;&gt;Details&lt;&#x2F;h1&gt;

&lt;p&gt;Our detailed calculations and notes can be found in a &lt;a href=&quot;https:&#x2F;&#x2F;docs.google.com&#x2F;spreadsheets&#x2F;d&#x2F;1XG9VXdDxNd8ipgPlEr7Nb7Eg22twXPuzgDwsOhtdYKQ&#x2F;edit#gid=894825456&quot;&gt;public Google sheet&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;img src=&#x27;https:&#x2F;&#x2F;about.gitlab.com&#x2F;images&#x2F;default-blog-image.png&#x27; class=&#x27;webfeedsFeaturedVisual&#x27; style=&#x27;display: none;&#x27; &#x2F;&gt;    </content>
  </entry>
  <entry>
    <title>How to evaluate GitLab during an EE trial</title>
    <link rel="alternate" href="https://about.gitlab.com/2016/12/09/how-to-evaluate-gitlab-during-ee-trial/"/>
    <id>https://about.gitlab.com/2016/12/09/how-to-evaluate-gitlab-during-ee-trial/</id>
    <published>2016-12-09T00:00:00+00:00</published>
    <updated>2016-12-09T00:00:00+00:00</updated>
    <author>
      <name>Rebecca Dodd</name>
    </author>
    <content type="html">
&lt;p&gt;Ever reach the end of a free trial and wonder if you even learned anything from the experience? We want you to get the most out of your time using GitLab Enterprise Edition, so here&#x27;s a spreadsheet you can use to evaluate the product.&lt;&#x2F;p&gt;



&lt;p&gt;Sometimes when you start a free trial you don&#x27;t have any particular criteria in mind and aren&#x27;t sure what exactly you&#x27;re looking for from GitLab EE, so you don&#x27;t get much out of your time using it. The spreadsheet below was created by one of our biggest EE customers to use when they were evaluating GitLab, and they&#x27;ve kindly shared it with us to help more users discover whether EE is the best choice for them.&lt;&#x2F;p&gt;

&lt;p&gt;We&#x27;d love for you to contribute to it and make it work for you so we can also learn more about what you&#x27;re looking for in EE. Please share your feedback in the comments!&lt;&#x2F;p&gt;

&lt;p&gt;Follow the instructions below to make an informed decision about whether GitLab EE is right for your company.&lt;&#x2F;p&gt;

&lt;ol&gt;
  &lt;li&gt;To get started, sign up for your &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;free-trial&#x2F;&quot;&gt;free 30-day trial of GitLab EE&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
  &lt;li&gt;Make a copy of our &lt;a href=&quot;https:&#x2F;&#x2F;docs.google.com&#x2F;spreadsheets&#x2F;d&#x2F;1gUzEoiJqbkE35lCmpk8UqFP1bMfzG3SjmstFQKaVxFM&#x2F;edit?usp=sharing&quot;&gt;GitLab EE trial evaluation spreadsheet&lt;&#x2F;a&gt; and save it to your own Google Drive.&lt;&#x2F;li&gt;
  &lt;li&gt;Customize the spreadsheet to suit your needs: add other products you currently use or are considering and will be comparing to GitLab EE, and include additional tests that you would run to assess the trial.&lt;&#x2F;li&gt;
  &lt;li&gt;Create an evaluation timeline for your 30-day trial, and divide tasks between departments and teams if you like.&lt;&#x2F;li&gt;
  &lt;li&gt;At the end of the trial period, total the scores.&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;sales&#x2F;&quot;&gt;Contact our sales team&lt;&#x2F;a&gt; with any questions or feedback.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;

&lt;p&gt;Image: &quot;&lt;a href=&quot;https:&#x2F;&#x2F;www.flickr.com&#x2F;photos&#x2F;craigmoulding&#x2F;8399214678&#x2F;&quot;&gt;I Love Spreadsheets&lt;&#x2F;a&gt;&quot; by &lt;a href=&quot;https:&#x2F;&#x2F;www.flickr.com&#x2F;photos&#x2F;craigmoulding&#x2F;&quot;&gt;Craig Chew-Moulding&lt;&#x2F;a&gt; is licensed under &lt;a href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;2.0&#x2F;&quot;&gt;CC BY-SA 2.0&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;img src=&#x27;https:&#x2F;&#x2F;about.gitlab.com&#x2F;images&#x2F;blogimages&#x2F;i-love-spreadsheets-trial-evaluation.jpg&#x27; class=&#x27;webfeedsFeaturedVisual&#x27; style=&#x27;display: none;&#x27; &#x2F;&gt;    </content>
  </entry>
  <entry>
    <title>GitLab 8.14.4, 8.13.9, and 8.12.12 Released</title>
    <link rel="alternate" href="https://about.gitlab.com/2016/12/08/gitlab-8-dot-14-dot-4-released/"/>
    <id>https://about.gitlab.com/2016/12/08/gitlab-8-dot-14-dot-4-released/</id>
    <published>2016-12-08T19:00:00+00:00</published>
    <updated>2016-12-08T19:00:00+00:00</updated>
    <author>
      <name>GitLab</name>
    </author>
    <content type="html">
&lt;p&gt;Today we are releasing versions 8.14.4, 8.13.9, and 8.12.12 for GitLab Community
Edition (CE) and Enterprise Edition (EE).&lt;&#x2F;p&gt;

&lt;p&gt;These versions contain important security fixes, and we &lt;strong&gt;strongly
recommend&lt;&#x2F;strong&gt; that all affected GitLab installations be upgraded to one of these
versions &lt;strong&gt;immediately&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;Please read on for more details.&lt;&#x2F;p&gt;



&lt;h2 id=&quot;security-fixes-in-8144-8139-and-81212&quot;&gt;Security fixes in 8.14.4, 8.13.9 and 8.12.12&lt;&#x2F;h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Replace MR access checks with use of &lt;code&gt;MergeRequestsFinder&lt;&#x2F;code&gt; (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;issues&#x2F;23867&quot;&gt;#23867&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h2 id=&quot;security-fixes-in-8144&quot;&gt;Security fixes in 8.14.4&lt;&#x2F;h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Destroy a user&#x27;s session when they delete their account. (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;issues&#x2F;25015&quot;&gt;#25015&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Filter authentication tokens from Sentry output.&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; XSS when &lt;code&gt;LegacyDiffNote&lt;&#x2F;code&gt; is created on a merge request diff containing HTML (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;issues&#x2F;25249&quot;&gt;#25249&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;Thanks to Kristiyan Bogdanov via HackerOne.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h2 id=&quot;other-fixes-in-8144&quot;&gt;Other fixes in 8.14.4&lt;&#x2F;h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Fix pipeline author for Slack and use pipeline id for pipeline link (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7506&quot;&gt;!7506&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Resolve &quot;Highlighting lines is broken&quot; (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7090&quot;&gt;!7090&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Fix pipelines tabs (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7709&quot;&gt;!7709&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Fix compatibility with Internet Explorer 11 for merge requests (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7525&quot;&gt;!7525&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Authorize users into imported GitLab project (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7936&quot;&gt;!7936&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Remove caching of Repository#has_visible_content? (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7947&quot;&gt;!7947&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Bump gitlab-shell version to 4.0.3 (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7953&quot;&gt;!7953&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
  &lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;EE:&lt;&#x2F;strong&gt; Prevent remote mirrors from failing when project is in pending_delete (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ee&#x2F;merge_requests&#x2F;938&quot;&gt;!938&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h2 id=&quot;upgrade-barometer&quot;&gt;Upgrade barometer&lt;&#x2F;h2&gt;

&lt;p&gt;These versions do include a single migration, and will require brief
downtime of typically less than one minute.&lt;&#x2F;p&gt;

&lt;p&gt;Please be aware that by default the Omnibus packages will stop, run migrations,
and start again, no matter how “big” or “small” the upgrade is. This behavior
can be changed by adding a &lt;a href=&quot;http:&#x2F;&#x2F;doc.gitlab.com&#x2F;omnibus&#x2F;update&#x2F;README.html&quot;&gt;&lt;code&gt;&#x2F;etc&#x2F;gitlab&#x2F;skip-auto-migrations&lt;&#x2F;code&gt;
file&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;updating&quot;&gt;Updating&lt;&#x2F;h2&gt;

&lt;p&gt;To update, check out our &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;update&quot;&gt;update page&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;enterprise-edition&quot;&gt;Enterprise Edition&lt;&#x2F;h2&gt;

&lt;p&gt;Interested in GitLab Enterprise Edition? Check out the &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;features&#x2F;#enterprise&quot;&gt;features exclusive to
EE&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;Access to GitLab Enterprise Edition is included with a
&lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;pricing&#x2F;&quot;&gt;subscription&lt;&#x2F;a&gt;. No time to upgrade GitLab
yourself? Subscribers receive upgrade and installation services.&lt;&#x2F;p&gt;
&lt;img src=&#x27;https:&#x2F;&#x2F;about.gitlab.com&#x2F;images&#x2F;default-blog-image.png&#x27; class=&#x27;webfeedsFeaturedVisual&#x27; style=&#x27;display: none;&#x27; &#x2F;&gt;    </content>
  </entry>
  <entry>
    <title>Expanding Our Enterprise Offering: Announcing GitLab Enterprise Edition Premium</title>
    <link rel="alternate" href="https://about.gitlab.com/2016/12/08/announcing-gitlab-enterprise-edition-premium/"/>
    <id>https://about.gitlab.com/2016/12/08/announcing-gitlab-enterprise-edition-premium/</id>
    <published>2016-12-08T10:35:00+00:00</published>
    <updated>2016-12-08T10:35:00+00:00</updated>
    <author>
      <name>Amara Nwaigwe</name>
    </author>
    <content type="html">
&lt;p&gt;Today we are announcing the addition of a new enterprise plan, GitLab Enterprise Edition Premium. GitLab Enterprise Edition (EE) Premium was built for global organizations who need a solution for remote teams, premium support, High Availability, and advanced workflow controls. GitLab EE Premium builds on top of our base enterprise plan and offers the following additional features:&lt;&#x2F;p&gt;



&lt;ul&gt;
  &lt;li&gt;Premium Support: 4-hour response times, 24&#x2F;7 emergency support, training, live upgrade assistance, and support for High Availability ensure that your team can focus on building great products.&lt;&#x2F;li&gt;
  &lt;li&gt;GitLab Geo: Make your remote teams more productive with read-only mirrors of your GitLab instance that significantly reduce the time it takes to clone and fetch large repos.&lt;&#x2F;li&gt;
  &lt;li&gt;File Locking: Improve how you manage your binary and text files with the ability to lock any file or directory.&lt;&#x2F;li&gt;
  &lt;li&gt;Pivotal Tile: Easily deploy GitLab as a pre-configured appliance using Ops Manager (BOSH) for Pivotal Cloud Foundry.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;p&gt;GitLab EE Premium ensures your team has the tools and support they need today
as well as in the future. Premium features are features that
replace other products typically used within organizations. Take a look at some
of the premium features we are considering on &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;direction&#x2F;#new-products&quot;&gt;our direction page&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;GitLab EE Premium is now &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;products&#x2F;&quot;&gt;available for purchase&lt;&#x2F;a&gt; for $199 per user per year.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;what-has-changed&quot;&gt;What has changed?&lt;&#x2F;h2&gt;

&lt;p&gt;Previously, we offered one enterprise plan for $39 per user&#x2F;per year and a number of additional enterprise products you could purchase for $99 per product&#x2F;per user&#x2F;per year. Now, the products and services we used to offer as additional purchases will be bundled into GitLab EE Premium. The only change to the former GitLab Enterprise Edition is the name. Its features and price will be exactly the same. This plan has simply been renamed to GitLab Enterprise Edition Starter.&lt;&#x2F;p&gt;

&lt;p&gt;We are also improving our Premium Support offering to speed up our non-emergency SLA from next-day responses to a 4-hour response time. This faster response time means that we will no longer offer a dedicated service engineer. While dedicated resources can potentially offer more personalized service, they limit our ability to get back to you as quickly as possible. We know your team’s time is sensitive so we chose to optimize for faster response times.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;why-one-subscription-instead-of-multiple-products&quot;&gt;Why one subscription instead of multiple products?&lt;&#x2F;h2&gt;

&lt;p&gt;There are a few reasons:&lt;&#x2F;p&gt;

&lt;ol&gt;
  &lt;li&gt;The per product purchasing process slowed customers down. We&#x27;d release a new product that our customers needed but in order for customers to actually start using it they would have to start a new internal purchasing process.&lt;&#x2F;li&gt;
  &lt;li&gt;Unlocking certain features&#x2F;experiences on a per product reduces the usage, so we were not getting enough feedback from our customers on how we could improve.&lt;&#x2F;li&gt;
  &lt;li&gt;We think this will make ordering and upsell conversations less complex.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;

&lt;h2 id=&quot;what-does-this-mean-for-existing-enterprise-customers&quot;&gt;What does this mean for existing enterprise customers?&lt;&#x2F;h2&gt;

&lt;ul&gt;
  &lt;li&gt;For customers who did not purchase an EE product, there is no change.&lt;&#x2F;li&gt;
  &lt;li&gt;For customers with one EE Product, you were paying $138 per user&#x2F;per year for one product. With the new EE Premium plan you&#x27;ll have quicker support response times and access to the additional products for $199 per user&#x2F;per year.&lt;&#x2F;li&gt;
  &lt;li&gt;For customers with two or more EE Product, you will save money and enjoy quicker support response times.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;p&gt;We want all of our customers to have the best experience with our products so we are offering all enterprise customers a 25% discount for early renewal until January 31st, 2017. If you&#x27;ve purchased an EE product we&#x27;ll upgrade your plan to EE Premium so you can try the additional products and services for FREE until your contract renewal. For customers with two or more of the EE products, we will include the additional products and services, update your contract to reflect the new EE Premium price of $199&#x2F;per user, and give you money back (to refund the difference between your bill with multiple EE products and the new EE Premium price).&lt;&#x2F;p&gt;
&lt;img src=&#x27;https:&#x2F;&#x2F;about.gitlab.com&#x2F;images&#x2F;blogimages&#x2F;announcing-ee-premium-cover.jpg&#x27; class=&#x27;webfeedsFeaturedVisual&#x27; style=&#x27;display: none;&#x27; &#x2F;&gt;    </content>
  </entry>
  <entry>
    <title>Git Tips &amp; Tricks</title>
    <link rel="alternate" href="https://about.gitlab.com/2016/12/08/git-tips-and-tricks/"/>
    <id>https://about.gitlab.com/2016/12/08/git-tips-and-tricks/</id>
    <published>2016-12-08T10:34:00+00:00</published>
    <updated>2016-12-08T10:34:00+00:00</updated>
    <author>
      <name>Achilleas Pipinellis</name>
    </author>
    <content type="html">
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;git-scm.com&quot;&gt;Git&lt;&#x2F;a&gt; comes with a ton of commands, and that&#x27;s probably an understatement.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;a href=&quot;&#x2F;images&#x2F;theinternet.png&quot;&gt;The internet&lt;&#x2F;a&gt; is full of Git tips and it&#x27;s hard if not impossible to know
them all, but sometimes you stumble upon an aha! moment that changes your
whole workflow.&lt;&#x2F;p&gt;

&lt;p&gt;In this post, we gathered some Git tips and tricks we use at GitLab everyday.
Hopefully they will add up to your aha! moment.&lt;&#x2F;p&gt;



&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#intro&quot; id=&quot;markdown-toc-intro&quot;&gt;Intro&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;a href=&quot;#gits-built-in-help&quot; id=&quot;markdown-toc-gits-built-in-help&quot;&gt;Git&#x27;s built-in help&lt;&#x2F;a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#the-most-common-commands&quot; id=&quot;markdown-toc-the-most-common-commands&quot;&gt;The most common commands&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
      &lt;li&gt;&lt;a href=&quot;#a-help-page-for-every-command&quot; id=&quot;markdown-toc-a-help-page-for-every-command&quot;&gt;A help page for every command&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
      &lt;li&gt;&lt;a href=&quot;#git-guides&quot; id=&quot;markdown-toc-git-guides&quot;&gt;Git guides&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
    &lt;&#x2F;ul&gt;
  &lt;&#x2F;li&gt;
  &lt;li&gt;&lt;a href=&quot;#see-the-repository-status-in-your-terminals-prompt&quot; id=&quot;markdown-toc-see-the-repository-status-in-your-terminals-prompt&quot;&gt;See the repository status in your terminal&#x27;s prompt&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;a href=&quot;#autocompletion-for-git-commands&quot; id=&quot;markdown-toc-autocompletion-for-git-commands&quot;&gt;Autocompletion for Git commands&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;a href=&quot;#git-plugins&quot; id=&quot;markdown-toc-git-plugins&quot;&gt;Git plugins&lt;&#x2F;a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#the--plugin&quot; id=&quot;markdown-toc-the--plugin&quot;&gt;The &lt;code&gt;git-extras&lt;&#x2F;code&gt; plugin&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
      &lt;li&gt;&lt;a href=&quot;#the--plugin-1&quot; id=&quot;markdown-toc-the--plugin-1&quot;&gt;The &lt;code&gt;git-open&lt;&#x2F;code&gt; plugin&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
    &lt;&#x2F;ul&gt;
  &lt;&#x2F;li&gt;
  &lt;li&gt;&lt;a href=&quot;#-on-steroids&quot; id=&quot;markdown-toc--on-steroids&quot;&gt;&lt;code&gt;.gitconfig&lt;&#x2F;code&gt; on steroids&lt;&#x2F;a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#set-a-global-&quot; id=&quot;markdown-toc-set-a-global-&quot;&gt;Set a global &lt;code&gt;.gitignore&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
      &lt;li&gt;&lt;a href=&quot;#delete-local-branches-that-have-been-removed-from-remote-on-fetchpull&quot; id=&quot;markdown-toc-delete-local-branches-that-have-been-removed-from-remote-on-fetchpull&quot;&gt;Delete local branches that have been removed from remote on fetch&#x2F;pull&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
      &lt;li&gt;&lt;a href=&quot;#enable-gits-autosquash-feature-by-default&quot; id=&quot;markdown-toc-enable-gits-autosquash-feature-by-default&quot;&gt;Enable Git&#x27;s autosquash feature by default&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
      &lt;li&gt;&lt;a href=&quot;#extra-info-when-using-git-submodules&quot; id=&quot;markdown-toc-extra-info-when-using-git-submodules&quot;&gt;Extra info when using Git submodules&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
      &lt;li&gt;&lt;a href=&quot;#change-the-editor-of-gits-messages&quot; id=&quot;markdown-toc-change-the-editor-of-gits-messages&quot;&gt;Change the editor of Git&#x27;s messages&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
      &lt;li&gt;&lt;a href=&quot;#change-the-tool-with-which-diffs-are-shown&quot; id=&quot;markdown-toc-change-the-tool-with-which-diffs-are-shown&quot;&gt;Change the tool with which diffs are shown&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
    &lt;&#x2F;ul&gt;
  &lt;&#x2F;li&gt;
  &lt;li&gt;&lt;a href=&quot;#aliases&quot; id=&quot;markdown-toc-aliases&quot;&gt;Aliases&lt;&#x2F;a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#add-an-alias-to-pretty-log-graphs&quot; id=&quot;markdown-toc-add-an-alias-to-pretty-log-graphs&quot;&gt;Add an alias to pretty log graphs&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
      &lt;li&gt;&lt;a href=&quot;#add-an-alias-to-checkout-merge-requests-locally&quot; id=&quot;markdown-toc-add-an-alias-to-checkout-merge-requests-locally&quot;&gt;Add an alias to checkout merge requests locally&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-oh-my-zsh-git-aliases-plugin&quot; id=&quot;markdown-toc-the-oh-my-zsh-git-aliases-plugin&quot;&gt;The Oh-my-zsh Git aliases plugin&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
    &lt;&#x2F;ul&gt;
  &lt;&#x2F;li&gt;
  &lt;li&gt;&lt;a href=&quot;#git-command-line-tips&quot; id=&quot;markdown-toc-git-command-line-tips&quot;&gt;Git command line tips&lt;&#x2F;a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#an-alias-of-&quot; id=&quot;markdown-toc-an-alias-of-&quot;&gt;An alias of &lt;code&gt;HEAD&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
      &lt;li&gt;&lt;a href=&quot;#quickly-checkout-the-previous-branch-you-were-on&quot; id=&quot;markdown-toc-quickly-checkout-the-previous-branch-you-were-on&quot;&gt;Quickly checkout the previous branch you were on&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
      &lt;li&gt;&lt;a href=&quot;#delete-local-branches-which-have-already-been-merged-into-master&quot; id=&quot;markdown-toc-delete-local-branches-which-have-already-been-merged-into-master&quot;&gt;Delete local branches which have already been merged into master&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
      &lt;li&gt;&lt;a href=&quot;#delete-local-branches-that-no-longer-exist-in-the-remote-repo&quot; id=&quot;markdown-toc-delete-local-branches-that-no-longer-exist-in-the-remote-repo&quot;&gt;Delete local branches that no longer exist in the remote repo&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
      &lt;li&gt;&lt;a href=&quot;#checking-out-a-new-branch-from-a-base-branch&quot; id=&quot;markdown-toc-checking-out-a-new-branch-from-a-base-branch&quot;&gt;Checking out a new branch from a base branch&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
    &lt;&#x2F;ul&gt;
  &lt;&#x2F;li&gt;
  &lt;li&gt;&lt;a href=&quot;#references&quot; id=&quot;markdown-toc-references&quot;&gt;References&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;a href=&quot;#conclusion&quot; id=&quot;markdown-toc-conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h2 id=&quot;intro&quot;&gt;Intro&lt;&#x2F;h2&gt;

&lt;p&gt;Almost everybody at GitLab will need to use Git at some point. For newcomers
who know nothing about Git that can be a fearsome experience. We have a
&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-com&#x2F;marketing&#x2F;raw&#x2F;master&#x2F;design&#x2F;print&#x2F;git-cheatsheet&#x2F;print-pdf&#x2F;git-cheatsheet.pdf&quot;&gt;Git cheatsheet&lt;&#x2F;a&gt; and a &lt;code&gt;#git-help&lt;&#x2F;code&gt; chat channel where we ask questions and
provide help if some of us get stuck. That&#x27;s a quick way to provide help, and
if something is complicated or someone has messed up their local repository and
needs immediate help, there&#x27;s always a person to jump on a quick call.&lt;&#x2F;p&gt;

&lt;p&gt;Here&#x27;s a pack of Git tricks that will leverage your Git-fu and you&#x27;ll hopefully
find useful. Remember, the list is far from exhaustive :)&lt;&#x2F;p&gt;

&lt;h2 id=&quot;gits-built-in-help&quot;&gt;Git&#x27;s built-in help&lt;&#x2F;h2&gt;

&lt;p&gt;The majority of users rely on sites like &lt;a href=&quot;https:&#x2F;&#x2F;stackoverflow.com&quot;&gt;StackOverflow&lt;&#x2F;a&gt; to find answers to their
Git problems, but how often do you use Git&#x27;s built-in help to find more about a
command you are struggling with?&lt;&#x2F;p&gt;

&lt;h3 id=&quot;the-most-common-commands&quot;&gt;The most common commands&lt;&#x2F;h3&gt;

&lt;p&gt;Run &lt;code&gt;git help&lt;&#x2F;code&gt; to print a list of the most common commands. You&#x27;ll probably
notice you&#x27;ve used most of them, but how well do you really know them?
Thankfully, there is a help page for every command!&lt;&#x2F;p&gt;

&lt;h3 id=&quot;a-help-page-for-every-command&quot;&gt;A help page for every command&lt;&#x2F;h3&gt;

&lt;p&gt;Git&#x27;s documentation is comprehensive and is automatically installed with Git.
Run &lt;code&gt;git help &amp;lt;command&amp;gt;&lt;&#x2F;code&gt; to find out all about a command&#x27;s behavior and what
options it can take.&lt;&#x2F;p&gt;

&lt;h3 id=&quot;git-guides&quot;&gt;Git guides&lt;&#x2F;h3&gt;

&lt;p&gt;Git comes with a handful of guides ready for you to explore. Run &lt;code&gt;git help -g&lt;&#x2F;code&gt;
to see what&#x27;s available:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;The common Git guides are:

   attributes   Defining attributes per path
   everyday     Everyday Git With 20 Commands Or So
   glossary     A Git glossary
   ignore       Specifies intentionally untracked files to ignore
   modules      Defining submodule properties
   revisions    Specifying revisions and ranges for Git
   tutorial     A tutorial introduction to Git (for version 1.5.1 or newer)
   workflows    An overview of recommended workflows with Git
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Jump to a Git tutorial with &lt;code&gt;git help tutorial&lt;&#x2F;code&gt;, go through the glossary with
&lt;code&gt;git help glossary&lt;&#x2F;code&gt; or learn about the most common commands with
&lt;code&gt;git help everyday&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;see-the-repository-status-in-your-terminals-prompt&quot;&gt;See the repository status in your terminal&#x27;s prompt&lt;&#x2F;h2&gt;

&lt;p&gt;It&#x27;s very useful to be able to visualize the status of your repository at any
given time. While there are 3rd party tools that include this information
(&lt;a href=&quot;http:&#x2F;&#x2F;ohmyz.sh&#x2F;&quot;&gt;oh-my-zsh&lt;&#x2F;a&gt; anyone?), Git itself provides a script named &lt;code&gt;git-prompt.sh&lt;&#x2F;code&gt;
that does exactly that. You can &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;git&#x2F;git&#x2F;blob&#x2F;master&#x2F;contrib&#x2F;completion&#x2F;git-prompt.sh&quot;&gt;download it&lt;&#x2F;a&gt; and follow the
instructions in it to install and use it in your system. If you&#x27;re using Linux
and have installed Git with your package manager, it may already be
present on your system, usually under &lt;code&gt;&#x2F;etc&#x2F;bash_completion.d&#x2F;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;Go ahead and replace your boring shell prompt with something like this:&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blogimages&#x2F;git-tricks&#x2F;git-shell-info.png&quot; alt=&quot;Git shell prompt&quot; class=&quot;shadow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;p&gt;&lt;em&gt;Taken from oh-my-zsh&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;robbyrussell&#x2F;oh-my-zsh&#x2F;wiki&#x2F;Themes#kafeitu&quot;&gt;themes wiki&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;

&lt;h2 id=&quot;autocompletion-for-git-commands&quot;&gt;Autocompletion for Git commands&lt;&#x2F;h2&gt;

&lt;p&gt;You may also find it useful to use the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;git&#x2F;git&#x2F;tree&#x2F;master&#x2F;contrib&#x2F;completion&quot;&gt;completion scripts&lt;&#x2F;a&gt; that provide Git
command completion for &lt;code&gt;bash&lt;&#x2F;code&gt;, &lt;code&gt;tcsh&lt;&#x2F;code&gt; and &lt;code&gt;zsh&lt;&#x2F;code&gt;. Again, follow the instructions
inside the scripts to learn how to install them. Once done, you can try out
typing a command.&lt;&#x2F;p&gt;

&lt;p&gt;Let&#x27;s say you want to type &lt;code&gt;git pull&lt;&#x2F;code&gt;. If Git completion is enabled, typing
just the first letter with &lt;code&gt;git p&lt;&#x2F;code&gt; followed by &lt;kbd&gt;Tab&lt;&#x2F;kbd&gt; will show the
following:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;pack-objects   -- create packed archive of objects
pack-redundant -- find redundant pack files
pack-refs      -- pack heads and tags for efficient repository access
parse-remote   -- routines to help parsing remote repository access parameters
patch-id       -- compute unique ID for a patch
prune          -- prune all unreachable objects from the object database
prune-packed   -- remove extra objects that are already in pack files
pull           -- fetch from and merge with another repository or local branch
push           -- update remote refs along with associated objects
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To show all available commands, type &lt;code&gt;git&lt;&#x2F;code&gt; in your terminal followed by
&lt;kbd&gt;Tab&lt;&#x2F;kbd&gt;+ &lt;kbd&gt;Tab&lt;&#x2F;kbd&gt;, and see the magic happening.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;media.giphy.com&#x2F;media&#x2F;12NUbkX6p4xOO4&#x2F;giphy.gif&quot; alt=&quot;It&#x27;s a kind of magic&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;h2 id=&quot;git-plugins&quot;&gt;Git plugins&lt;&#x2F;h2&gt;

&lt;p&gt;Since Git is free software, it&#x27;s easy for people to write scripts that extend
its functionality. Let&#x27;s see some of the most common ones.&lt;&#x2F;p&gt;

&lt;h3 id=&quot;the--plugin&quot;&gt;The &lt;code&gt;git-extras&lt;&#x2F;code&gt; plugin&lt;&#x2F;h3&gt;

&lt;p&gt;If you want to enhance Git with more commands, you&#x27;ll want to try out the
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tj&#x2F;git-extras&quot;&gt;&lt;code&gt;git-extras&lt;&#x2F;code&gt; plugin&lt;&#x2F;a&gt;. It includes commands like &lt;code&gt;git info&lt;&#x2F;code&gt; (show
information about the repository), &lt;code&gt;git effort&lt;&#x2F;code&gt; (number of commits per file),
and the list goes on. After you &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tj&#x2F;git-extras&#x2F;blob&#x2F;master&#x2F;Installation.md&quot;&gt;install&lt;&#x2F;a&gt; it, make sure to visit
the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tj&#x2F;git-extras&#x2F;blob&#x2F;master&#x2F;Commands.md&quot;&gt;documentation on the provided commands&lt;&#x2F;a&gt; in order to understand
what each one does before using it.&lt;&#x2F;p&gt;

&lt;h3 id=&quot;the--plugin-1&quot;&gt;The &lt;code&gt;git-open&lt;&#x2F;code&gt; plugin&lt;&#x2F;h3&gt;

&lt;p&gt;If you want to quickly visit the website on which the repository you&#x27;re on is
hosted, &lt;code&gt;git-open&lt;&#x2F;code&gt; is for you. All major providers are supported (GitLab, GitHub,
Bitbucket) and you can even use them all at the same time if you set
them as different remotes.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;paulirish&#x2F;git-open#installation&quot;&gt;Install it&lt;&#x2F;a&gt;, and try it out by cloning a repository from
&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;explore&quot;&gt;GitLab.com&lt;&#x2F;a&gt;. From your terminal navigate to that
repository and run &lt;code&gt;git open&lt;&#x2F;code&gt; to be transferred to the project&#x27;s page on
GitLab.com.&lt;&#x2F;p&gt;

&lt;p&gt;It works by default for projects hosted on GitLab.com, but you can also use it
with your own GitLab instances. In that case, make sure to set up the domain
name with:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;git config gitopen.gitlab.domain git.example.com
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can even open different remotes and branches if they have been set up.
Read more in the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;paulirish&#x2F;git-open#examples&quot;&gt;examples section&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;-on-steroids&quot;&gt;&lt;code&gt;.gitconfig&lt;&#x2F;code&gt; on steroids&lt;&#x2F;h2&gt;

&lt;p&gt;The &lt;code&gt;.gitconfig&lt;&#x2F;code&gt; file contains information on how you want Git to behave on
certain circumstances. There are options you can set at a repository level,
but you can also set them in a global &lt;code&gt;.gitconfig&lt;&#x2F;code&gt; so that all local config
will inherit its values. This file usually resides in your home directory.
If not, either you&#x27;ll have to create it manually or it will be automatically
be created when you issue a command starting with &lt;code&gt;git config --global&lt;&#x2F;code&gt; as
we&#x27;ll see below.&lt;&#x2F;p&gt;

&lt;p&gt;The very first encounter with &lt;code&gt;.gitconfig&lt;&#x2F;code&gt; was probably when you set your
name and email address for Git to know who you are.
To know more about the options &lt;code&gt;.gitconfig&lt;&#x2F;code&gt; can take, see the &lt;a href=&quot;https:&#x2F;&#x2F;git-scm.com&#x2F;docs&#x2F;git-config&quot;&gt;Git documentation
on &lt;code&gt;.gitconfig&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;If you are using macOS or Linux, &lt;code&gt;.gitconfig&lt;&#x2F;code&gt; will probably be hidden if you are
trying to open it from a file manager. Either make sure the hidden files are
shown or open it using a command in the terminal: &lt;code&gt;atom ~&#x2F;.gitconfig&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;Let&#x27;s explore some of the most useful config options.&lt;&#x2F;p&gt;

&lt;h3 id=&quot;set-a-global-&quot;&gt;Set a global &lt;code&gt;.gitignore&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;

&lt;p&gt;If you want to avoid committing files like &lt;code&gt;.DS_Store&lt;&#x2F;code&gt;, Vim &lt;code&gt;swp&lt;&#x2F;code&gt; files, etc.,
you can set up a global &lt;code&gt;.gitignore&lt;&#x2F;code&gt; file.&lt;&#x2F;p&gt;

&lt;p&gt;First create the file:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;touch ~&#x2F;.gitignore
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then run:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;git config --global core.excludesFile ~&#x2F;.gitignore
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or manually add the following to your &lt;code&gt;~&#x2F;.gitconfig&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight ini&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[core]&lt;&#x2F;span&gt;
  &lt;span class=&quot;py&quot;&gt;excludesFile&lt;&#x2F;span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;~&#x2F;.gitignore&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Gradually build up your own useful list of things you want Git to ignore. Read
the &lt;a href=&quot;https:&#x2F;&#x2F;git-scm.com&#x2F;docs&#x2F;gitignore&quot;&gt;gitignore documentation&lt;&#x2F;a&gt; to find out
more.&lt;&#x2F;p&gt;

&lt;hr &#x2F;&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https:&#x2F;&#x2F;git-scm.com&#x2F;docs&#x2F;git-config#git-config-coreexcludesFile&quot;&gt;Git docs source&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;

&lt;h3 id=&quot;delete-local-branches-that-have-been-removed-from-remote-on-fetchpull&quot;&gt;Delete local branches that have been removed from remote on fetch&#x2F;pull&lt;&#x2F;h3&gt;

&lt;p&gt;You might already have a bunch of stale branches in your local repository that
no longer exist in the remote one. To delete them in each fetch&#x2F;pull, run:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;git config --global fetch.prune &lt;span class=&quot;nb&quot;&gt;true&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or manually add the following to your &lt;code&gt;~&#x2F;.gitconfig&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight ini&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[fetch]&lt;&#x2F;span&gt;
  &lt;span class=&quot;py&quot;&gt;prune&lt;&#x2F;span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;hr &#x2F;&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https:&#x2F;&#x2F;git-scm.com&#x2F;docs&#x2F;git-config#git-config-fetchprune&quot;&gt;Git docs source&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;

&lt;h3 id=&quot;enable-gits-autosquash-feature-by-default&quot;&gt;Enable Git&#x27;s autosquash feature by default&lt;&#x2F;h3&gt;

&lt;p&gt;Autosquash makes it quicker and easier to squash or fixup commits during an
interactive rebase. It can be enabled for each rebase using
&lt;code&gt;git rebase -i --autosquash&lt;&#x2F;code&gt;, but it&#x27;s easier to turn it on by default.&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;git config --global rebase.autosquash &lt;span class=&quot;nb&quot;&gt;true&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or manually add the following to your &lt;code&gt;~&#x2F;.gitconfig&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight ini&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[rebase]&lt;&#x2F;span&gt;
  &lt;span class=&quot;py&quot;&gt;autosquash&lt;&#x2F;span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p class=&quot;alert alert-info&quot;&gt;At this point, let us remind you of &lt;a href=&quot;https:&#x2F;&#x2F;git-scm.com&#x2F;book&#x2F;en&#x2F;v2&#x2F;Git-Branching-Rebasing#The-Perils-of-Rebasing&quot;&gt;the perils of rebasing&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;hr &#x2F;&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https:&#x2F;&#x2F;git-scm.com&#x2F;docs&#x2F;git-config#git-config-rebaseautoSquash&quot;&gt;Git docs source&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;
&lt;em&gt;(&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;thoughtbot&#x2F;dotfiles&#x2F;pull&#x2F;377&quot;&gt;tip taken from thoughbot&lt;&#x2F;a&gt;)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;

&lt;h3 id=&quot;extra-info-when-using-git-submodules&quot;&gt;Extra info when using Git submodules&lt;&#x2F;h3&gt;

&lt;p&gt;If you are using &lt;a href=&quot;https:&#x2F;&#x2F;git-scm.com&#x2F;book&#x2F;en&#x2F;v2&#x2F;Git-Tools-Submodules&quot;&gt;submodules&lt;&#x2F;a&gt;, it might be useful to turn on the submodule summary.
From your terminal run:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;git config --global status.submoduleSummary &lt;span class=&quot;nb&quot;&gt;true&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or manually add the following to your &lt;code&gt;~&#x2F;.gitconfig&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight ini&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[status]&lt;&#x2F;span&gt;
  &lt;span class=&quot;py&quot;&gt;submoduleSummary&lt;&#x2F;span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;hr &#x2F;&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https:&#x2F;&#x2F;git-scm.com&#x2F;docs&#x2F;git-config#git-config-statussubmoduleSummary&quot;&gt;Git docs source&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;

&lt;h3 id=&quot;change-the-editor-of-gits-messages&quot;&gt;Change the editor of Git&#x27;s messages&lt;&#x2F;h3&gt;

&lt;p&gt;You can change the default text editor for use by Git commands.&lt;&#x2F;p&gt;

&lt;p&gt;From &lt;code&gt;git help var&lt;&#x2F;code&gt;:
the order of preference is the &lt;code&gt;$GIT_EDITOR&lt;&#x2F;code&gt; environment variable, then
&lt;code&gt;core.editor&lt;&#x2F;code&gt; configuration, then &lt;code&gt;$VISUAL&lt;&#x2F;code&gt;, then &lt;code&gt;$EDITOR&lt;&#x2F;code&gt;, and then the
default chosen at compile time, which is usually &lt;code&gt;vi&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;Running &lt;code&gt;git config --show-origin core.editor&lt;&#x2F;code&gt; will tell you if &lt;code&gt;core.editor&lt;&#x2F;code&gt;
is set and from which file. This needs at least Git 2.8.&lt;&#x2F;p&gt;

&lt;p&gt;To change it to your favor editor (&lt;code&gt;vim&lt;&#x2F;code&gt;, &lt;code&gt;emacs&lt;&#x2F;code&gt;, &lt;code&gt;atom&lt;&#x2F;code&gt;, etc.), run:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;git config --global core.editor vim
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or manually add the following to your &lt;code&gt;~&#x2F;.gitconfig&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight ini&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[core]&lt;&#x2F;span&gt;
  &lt;span class=&quot;py&quot;&gt;editor&lt;&#x2F;span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;vim&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;hr &#x2F;&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https:&#x2F;&#x2F;git-scm.com&#x2F;docs&#x2F;git-config.html#git-config-coreeditor&quot;&gt;Git docs source&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;

&lt;h3 id=&quot;change-the-tool-with-which-diffs-are-shown&quot;&gt;Change the tool with which diffs are shown&lt;&#x2F;h3&gt;

&lt;p&gt;&lt;code&gt;git diff&lt;&#x2F;code&gt; is useful as it shows the changes that are not currently staged.
When running this command Git usually uses its internal tool and displays
the changes in your terminal.&lt;&#x2F;p&gt;

&lt;p&gt;If you don&#x27;t like the default difftool there are a couple of others to choose
from:&lt;&#x2F;p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code&gt;vimdiff&lt;&#x2F;code&gt; - &lt;a href=&quot;http:&#x2F;&#x2F;vimdoc.sourceforge.net&#x2F;htmldoc&#x2F;diff.html&quot;&gt;Vim&#x27;s built-in vimdiff&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;code&gt;magit&lt;&#x2F;code&gt; - &lt;a href=&quot;https:&#x2F;&#x2F;www.emacswiki.org&#x2F;emacs&#x2F;Magit&quot;&gt;Emacs most popular tool is Magit&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;code&gt;meld&lt;&#x2F;code&gt; - &lt;a href=&quot;http:&#x2F;&#x2F;meldmerge.org&#x2F;&quot;&gt;A visual diff and merge tool written in Python&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;code&gt;kdiff3&lt;&#x2F;code&gt; - &lt;a href=&quot;http:&#x2F;&#x2F;kdiff3.sourceforge.net&#x2F;&quot;&gt;A diff and merge program written in Qt&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;p&gt;To change the default tool for watching diffs run the following:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;git config --global diff.tool vimdiff
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or manually add the following to your &lt;code&gt;~&#x2F;.gitconfig&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight ini&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[diff]&lt;&#x2F;span&gt;
  &lt;span class=&quot;py&quot;&gt;tool&lt;&#x2F;span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;vimdiff&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Also related is the &lt;code&gt;merge.tool&lt;&#x2F;code&gt; setting which can be set to a tool to be used
as the merge resolution program. Similarly:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;git config --global merge.tool vimdiff
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or manually add the following to your &lt;code&gt;~&#x2F;.gitconfig&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight ini&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[merge]&lt;&#x2F;span&gt;
  &lt;span class=&quot;py&quot;&gt;tool&lt;&#x2F;span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;vimdiff&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;hr &#x2F;&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https:&#x2F;&#x2F;git-scm.com&#x2F;docs&#x2F;git-difftool&quot;&gt;Git docs source&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;

&lt;h2 id=&quot;aliases&quot;&gt;Aliases&lt;&#x2F;h2&gt;

&lt;p&gt;Git commands can take a lot of flags at a time. For example, for a log graph
you can use the following command:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;git log --graph --pretty&lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt;format:&lt;span class=&quot;s1&quot;&gt;&#x27;%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset&#x27;&lt;&#x2F;span&gt; --abbrev-commit --date&lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt;relative
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You sure don&#x27;t want to type this every time you need to run it. For that purpose,
Git supports aliases, which are custom user-defined commands that build on top
of the core ones. They are defined in &lt;code&gt;~&#x2F;.gitconfig&lt;&#x2F;code&gt; under the &lt;code&gt;[alias]&lt;&#x2F;code&gt; group.&lt;&#x2F;p&gt;

&lt;p&gt;Open &lt;code&gt;~&#x2F;.gitconfig&lt;&#x2F;code&gt; with your editor and start adding stuff.&lt;&#x2F;p&gt;

&lt;h3 id=&quot;add-an-alias-to-pretty-log-graphs&quot;&gt;Add an alias to pretty log graphs&lt;&#x2F;h3&gt;

&lt;p&gt;In your &lt;code&gt;~&#x2F;.gitconfig&lt;&#x2F;code&gt; add:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight ini&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[alias]&lt;&#x2F;span&gt;
  &lt;span class=&quot;py&quot;&gt;lg&lt;&#x2F;span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;log --graph --pretty=format:&#x27;%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset&#x27; --abbrev-commit --date=relative&lt;&#x2F;span&gt;
  &lt;span class=&quot;py&quot;&gt;lol&lt;&#x2F;span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;log --graph --decorate --pretty=oneline --abbrev-commit&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Next time you want the pretty log to appear, run: &lt;code&gt;git lg&lt;&#x2F;code&gt; or &lt;code&gt;git lol&lt;&#x2F;code&gt; for
some pretty log graphs.&lt;&#x2F;p&gt;

&lt;h3 id=&quot;add-an-alias-to-checkout-merge-requests-locally&quot;&gt;Add an alias to checkout merge requests locally&lt;&#x2F;h3&gt;

&lt;p&gt;A merge request contains all the history from a repository, plus the additional
commits added to the branch associated with the merge request. Note that you
can checkout a public merge request locally even if the source project is a fork
(even a private fork) of the target project.&lt;&#x2F;p&gt;

&lt;p&gt;To checkout a merge request locally, add the following alias to your &lt;code&gt;~&#x2F;.gitconfig&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;[alias]
  mr = !sh -c &#x27;git fetch $1 merge-requests&#x2F;$2&#x2F;head:mr-$1-$2 &amp;amp;&amp;amp; git checkout mr-$1-$2&#x27; -
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now you can check out a particular merge request from any repository and any
remote. For example, to check out the merge request with ID 5 as shown in GitLab
from the &lt;code&gt;upstream&lt;&#x2F;code&gt; remote, run:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;git mr upstream 5
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This will fetch the merge request into a local &lt;code&gt;mr-upstream-5&lt;&#x2F;code&gt; branch and check
it out. In the above example, &lt;code&gt;upstream&lt;&#x2F;code&gt; is the remote that points to GitLab
which you can find out by running &lt;code&gt;git remote -v&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;

&lt;h3 id=&quot;the-oh-my-zsh-git-aliases-plugin&quot;&gt;The Oh-my-zsh Git aliases plugin&lt;&#x2F;h3&gt;

&lt;p&gt;If you are an &lt;a href=&quot;http:&#x2F;&#x2F;ohmyz.sh&#x2F;&quot;&gt;Oh My Zsh&lt;&#x2F;a&gt; user you&#x27;ll probably know this already.
Learn how you can &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;robbyrussell&#x2F;oh-my-zsh&#x2F;wiki&#x2F;Plugin:git&quot;&gt;enable the Git plugin&lt;&#x2F;a&gt; provided with Oh My Zsh and
start using the short commands to save time. Some examples are:&lt;&#x2F;p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code&gt;gl&lt;&#x2F;code&gt;  instead of &lt;code&gt;git pull&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;code&gt;gp&lt;&#x2F;code&gt;  instead of &lt;code&gt;git push&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;code&gt;gco&lt;&#x2F;code&gt; instead of &lt;code&gt;git checkout&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h2 id=&quot;git-command-line-tips&quot;&gt;Git command line tips&lt;&#x2F;h2&gt;

&lt;p&gt;Here&#x27;s a list of Git tips we gathered.&lt;&#x2F;p&gt;

&lt;h3 id=&quot;an-alias-of-&quot;&gt;An alias of &lt;code&gt;HEAD&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;

&lt;p&gt;Did you know &lt;code&gt;@&lt;&#x2F;code&gt; is the same as &lt;code&gt;HEAD&lt;&#x2F;code&gt;? Using it during a rebase is a life saver:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;git rebase -i @~2
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;quickly-checkout-the-previous-branch-you-were-on&quot;&gt;Quickly checkout the previous branch you were on&lt;&#x2F;h3&gt;

&lt;p&gt;A dash (&lt;code&gt;-&lt;&#x2F;code&gt;) refers to the branch you were on before the current one.
Use it to checkout the previous branch (&lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;holman&#x2F;status&#x2F;530490167522779137&quot;&gt;source&lt;&#x2F;a&gt;):&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Checkout master&lt;&#x2F;span&gt;
git checkout master

&lt;span class=&quot;c&quot;&gt;# Create and checkout to a new branch&lt;&#x2F;span&gt;
git checkout -b git-tips

&lt;span class=&quot;c&quot;&gt;# Checkout master&lt;&#x2F;span&gt;
git checkout master

&lt;span class=&quot;c&quot;&gt;# Checkout to the previous branch (git-tips)&lt;&#x2F;span&gt;
git checkout -
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;delete-local-branches-which-have-already-been-merged-into-master&quot;&gt;Delete local branches which have already been merged into master&lt;&#x2F;h3&gt;

&lt;p&gt;If you are working everyday on a project that gets contributions all the time,
the local branches number increases without noticing it. Run the following
command to delete all local branches that are already merged into master
(&lt;a href=&quot;http:&#x2F;&#x2F;stevenharman.net&#x2F;git-clean-delete-already-merged-branches&quot;&gt;source&lt;&#x2F;a&gt;):&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Make sure you have checked out master first&lt;&#x2F;span&gt;
git checkout master

&lt;span class=&quot;c&quot;&gt;# Delete merged branches to master except master&lt;&#x2F;span&gt;
git branch --merged master | grep -v &lt;span class=&quot;s2&quot;&gt;&quot;master&quot;&lt;&#x2F;span&gt; | xargs -n 1 git branch -d
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In the event that you accidentally delete master (💩 happens),  get it back with:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;git checkout -b master origin&#x2F;master
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;delete-local-branches-that-no-longer-exist-in-the-remote-repo&quot;&gt;Delete local branches that no longer exist in the remote repo&lt;&#x2F;h3&gt;

&lt;p&gt;To remove all tracking branches that you have locally but are no more present in
the remote repository (&lt;code&gt;origin&lt;&#x2F;code&gt;):&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;git remote prune origin
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Use the &lt;code&gt;--dry-run&lt;&#x2F;code&gt; flag to only see what branches will be pruned, but not
actually prune them:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;git remote prune origin --dry-run
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you want this to be run automatically every time you fetch&#x2F;pull, see
&lt;a href=&quot;#delete-local-branches-that-have-been-removed-from-remote-on-fetchpull&quot;&gt;how to add it to your &lt;code&gt;.gitconfig&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;h3 id=&quot;checking-out-a-new-branch-from-a-base-branch&quot;&gt;Checking out a new branch from a base branch&lt;&#x2F;h3&gt;

&lt;p&gt;You can checkout a new branch from a base branch without first checking out
the base branch. Confusing? Here&#x27;s an example.&lt;&#x2F;p&gt;

&lt;p&gt;If you are on a branch named &lt;code&gt;old-branch&lt;&#x2F;code&gt; and you want to
checkout &lt;code&gt;new-branch&lt;&#x2F;code&gt; based off &lt;code&gt;master&lt;&#x2F;code&gt;, you&#x27;d normally do:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;git checkout master
git checkout -b new-branch
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There&#x27;s a quicker way though. While still on the &lt;code&gt;old-branch&lt;&#x2F;code&gt;, run:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;git checkout -b new-branch master
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The pattern is the following:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;git checkout -b new_branch base_branch
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;&#x2F;h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;thoughtbot&#x2F;dotfiles&#x2F;blob&#x2F;master&#x2F;gitconfig&quot;&gt;Thoughbot&#x27;s gitconfig file&lt;&#x2F;a&gt;
contains useful tips some of which are also present in this post&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;git-tips&#x2F;tips&#x2F;blob&#x2F;master&#x2F;README.md&quot;&gt;A collection of Git tips&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;usevim.com&#x2F;2012&#x2F;03&#x2F;21&#x2F;git-and-vimdiff&#x2F;&quot;&gt;Git and Vimdiff&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;git-scm.com&#x2F;&quot;&gt;Git&#x27;s official site&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;

&lt;p&gt;As always, writing something about Git, only scratches the surface. While some
of the tips included in this post might come in handy, there are sure a lot
of other stuff we&#x27;re not familiar with.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blogimages&#x2F;git-tricks&#x2F;uncle-sam-wants-git.jpg&quot; alt=&quot;Uncle Sam wants you to tell your trick&quot; class=&quot;shadow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;!-- Links --&gt;

&lt;img src=&#x27;https:&#x2F;&#x2F;about.gitlab.com&#x2F;images&#x2F;blogimages&#x2F;git-tricks&#x2F;git-tricks-cover-image.png&#x27; class=&#x27;webfeedsFeaturedVisual&#x27; style=&#x27;display: none;&#x27; &#x2F;&gt;    </content>
  </entry>
  <entry>
    <title>GitLab 8.14 webcast recording &amp; highlights</title>
    <link rel="alternate" href="https://about.gitlab.com/2016/12/08/gitlab-8-14-webcast/"/>
    <id>https://about.gitlab.com/2016/12/08/gitlab-8-14-webcast/</id>
    <published>2016-12-08T10:33:00+00:00</published>
    <updated>2016-12-08T10:33:00+00:00</updated>
    <author>
      <name>Erica Lindberg</name>
    </author>
    <content type="html">
&lt;p&gt;On 22 October we released &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;2016&#x2F;11&#x2F;22&#x2F;gitlab-8-14-released&#x2F;&quot;&gt;GitLab 8.14&lt;&#x2F;a&gt;, our 60th consecutive monthly release. In this webcast, GitLab&#x27;s Head of Product &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;MarkPundsack&quot;&gt;Mark Pundsack&lt;&#x2F;a&gt; and Product Manager &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;djaiss&quot;&gt;Régis Freyd&lt;&#x2F;a&gt; show off what&#x27;s new, including live demos of Time Tracking Beta (EE), Mattermost Chat
Commands, and &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;2016&#x2F;11&#x2F;22&#x2F;introducing-review-apps&#x2F;&quot;&gt;Review Apps&lt;&#x2F;a&gt;. Watch the recording and get the highlights below.&lt;&#x2F;p&gt;

&lt;figure class=&quot;video_container&quot;&gt;
&lt;iframe src=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;embed&#x2F;CteZol_7pxo&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot;&gt; &lt;&#x2F;iframe&gt;
&lt;&#x2F;figure&gt;

&lt;h2 id=&quot;highlights&quot;&gt;Highlights&lt;&#x2F;h2&gt;

&lt;h3 id=&quot;0121-introduction-to-git-and-gitlab&quot;&gt;[01:21] Introduction to Git and GitLab&lt;&#x2F;h3&gt;
&lt;h3 id=&quot;0407-gitlabs-idea-to-production-vision&quot;&gt;[04:07] GitLab&#x27;s Idea to Production Vision&lt;&#x2F;h3&gt;
&lt;h3 id=&quot;0610-time-tracking-beta-ee&quot;&gt;[06:10] Time Tracking Beta (EE)&lt;&#x2F;h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;Recording time has always been painful. You have to use many different systems, and even then, you don&#x27;t get a complete picture because your work is in one place, and your time tracking data in another. This is why we&#x27;ve decided to release Time Tracking in Beta in 8.14.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;h3 id=&quot;1218-mattermost-chat-commands&quot;&gt;[12:18] Mattermost Chat Commands&lt;&#x2F;h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;Team communication has changed, more and more ideas are first discussed in chat. GitLab wants to make it easier for you to get your ideas from chat into GitLab issues where they can be acted on.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;h3 id=&quot;1540-review-apps&quot;&gt;[15:40] Review Apps&lt;&#x2F;h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;Review Apps are the future of reviewing work! Rather than just looking at code, Review Apps serve up a fully functioning environment where your app is running, so you get a live preview of every branch and merge request. It’s perfect for testing complex changes where looking at the code isn’t enough.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;h3 id=&quot;3350-other-improvements--whats-next&quot;&gt;[33:50] Other improvements &amp;amp; what&#x27;s next&lt;&#x2F;h3&gt;

&lt;h2 id=&quot;upcoming-live-streams&quot;&gt;Upcoming Live Streams&lt;&#x2F;h2&gt;

&lt;h2 id=&quot;1-monitoring-distributed-systems-with-prometheus&quot;&gt;1. Monitoring Distributed Systems with Prometheus&lt;&#x2F;h2&gt;

&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=WzAzm0C15W8&quot;&gt;Watch live&lt;&#x2F;a&gt; on December 14 at 9am PT&#x2F;5pm GMT. &lt;a href=&quot;https:&#x2F;&#x2F;page.gitlab.com&#x2F;20161207_PrometheusWebcast_LandingPage.html&quot;&gt;Sign up to receive a reminder and the recording&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;Infrastructure Lead Pablo Carranza will give a behind-the-scenes look at GitLab&#x27;s Prometheus set up, explain how we plan to ship Prometheus with GitLab CE, and give a tutorial on how you can set up your own dashboard. A live chat Q&amp;amp;A will follow the presentation. 
For more information on the topic, read Pablo&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;2016&#x2F;11&#x2F;10&#x2F;why-choose-bare-metal&#x2F;&quot;&gt;blog post on how we knew it was time to leave the cloud&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;2-introducing-the-codeship-gitlab-integration&quot;&gt;2. Introducing the Codeship GitLab Integration&lt;&#x2F;h2&gt;

&lt;p&gt;Hosted by &lt;a href=&quot;http:&#x2F;&#x2F;bit.ly&#x2F;2g2jFjG&quot;&gt;Codeship&lt;&#x2F;a&gt; on December 15 at 9am PT&#x2F;6pm GMT. &lt;a href=&quot;http:&#x2F;&#x2F;bit.ly&#x2F;2g2jFjG&quot;&gt;Register here&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;

&lt;p&gt;Following &lt;a href=&quot;https:&#x2F;&#x2F;blog.codeship.com&#x2F;codeship-launches-gitlab-support&#x2F;&quot;&gt;Codeship&#x27;s announcement&lt;&#x2F;a&gt;, GitLab&#x27;s Mark Pundsack will join Codeship Senior Software Engineer Kyle Rames for a discussion on why Codeship built a GitLab integration and showcase a demo alongside Codeship&#x27;s Docker Platform.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;3-designing-gitlabs-user-experience-with-ux-lead-allison-whilden&quot;&gt;3. Designing GitLab&#x27;s User Experience with UX Lead Allison Whilden&lt;&#x2F;h2&gt;

&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=Lxy1jET5pww&quot;&gt;Watch live&lt;&#x2F;a&gt; on December 15 at 10am PT&#x2F;6pm GMT. &lt;a href=&quot;https:&#x2F;&#x2F;page.gitlab.com&#x2F;UXLiveStream_LandingPage.html&quot;&gt;Sign up to receive a reminder and the recording&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;User experience (UX) affects every interaction a user has with a product. Because of this, it can make or break the adoption of a website, or application. UX designers have to do dig into the who, the what, the why, and the how of everything that happens within a platform.
How does GitLab&#x27;s UX address the needs of many different types of users in a product that expands every month? Join GitLab&#x27;s UX Lead, Allison Whilden, and her team, as they discuss their process, the big challenges they face and how they solve them.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;4-prometheus-and-gitlab-completing-the-application-lifecycle-with-monitoring&quot;&gt;4. Prometheus and GitLab: Completing the application lifecycle with monitoring&lt;&#x2F;h2&gt;

&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=rT92jecagQo&quot;&gt;Watch live&lt;&#x2F;a&gt; on December 15 at 1:30pm PT&#x2F;9:30pm GMT.&lt;&#x2F;p&gt;

&lt;p&gt;Tune in for this live broadcast with GitLab CEO &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;sytses&quot;&gt;Sid Sijbrandij&lt;&#x2F;a&gt; and Head of Product Mark Pundsack as 
they share our future plans for GitLab and Prometheus.&lt;&#x2F;p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Prometheus is not just for monitoring GitLab. It is not an optional feature. It is an essential part of deploying applications with GitLab. All GitLab users should have access to it to monitor the impact of their deployments to business&#x2F;application&#x2F;system metrics and do feature flags&#x2F;ab-testing&#x2F;etc.&lt;&#x2F;em&gt; - Sid Sijbrandij&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;

&lt;!-- identifiers --&gt;
&lt;img src=&#x27;https:&#x2F;&#x2F;about.gitlab.com&#x2F;images&#x2F;default-blog-image.png&#x27; class=&#x27;webfeedsFeaturedVisual&#x27; style=&#x27;display: none;&#x27; &#x2F;&gt;    </content>
  </entry>
  <entry>
    <title>Building a new GitLab Docs site with Nanoc, GitLab CI, and GitLab Pages</title>
    <link rel="alternate" href="https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/"/>
    <id>https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/</id>
    <published>2016-12-07T00:00:00+00:00</published>
    <updated>2016-12-07T00:00:00+00:00</updated>
    <author>
      <name>Connor Shea</name>
    </author>
    <content type="html">
&lt;p&gt;We recently rebuilt &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&quot;&gt;docs.gitlab.com&lt;&#x2F;a&gt; from scratch. Where previously the site was generated with a simple Ruby script, we now use a proper static site generator.&lt;&#x2F;p&gt;

&lt;p&gt;Check out the improvements we made, the structure we now use to deploy from specific directories in multiple repositories to a single website, build with &lt;a href=&quot;&#x2F;gitlab-ci&#x2F;&quot;&gt;GitLab CI&lt;&#x2F;a&gt; and deployed with &lt;a href=&quot;https:&#x2F;&#x2F;pages.gitlab.io&quot;&gt;GitLab Pages&lt;&#x2F;a&gt;. Now our documentation has a nicer look and feel, is more pleasant to read through, and simpler and quicker to maintain.&lt;&#x2F;p&gt;



&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#improvements&quot; id=&quot;markdown-toc-improvements&quot;&gt;Improvements&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;a href=&quot;#requirements&quot; id=&quot;markdown-toc-requirements&quot;&gt;Requirements&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-build-process&quot; id=&quot;markdown-toc-the-build-process&quot;&gt;The build process&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;a href=&quot;#taking-advantage-of-gitlab-to-put-everything-together&quot; id=&quot;markdown-toc-taking-advantage-of-gitlab-to-put-everything-together&quot;&gt;Taking advantage of GitLab to put everything together&lt;&#x2F;a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#gitlab-ci&quot; id=&quot;markdown-toc-gitlab-ci&quot;&gt;GitLab CI&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
      &lt;li&gt;&lt;a href=&quot;#review-apps&quot; id=&quot;markdown-toc-review-apps&quot;&gt;Review Apps&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
      &lt;li&gt;&lt;a href=&quot;#gitlab-pages&quot; id=&quot;markdown-toc-gitlab-pages&quot;&gt;GitLab Pages&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
    &lt;&#x2F;ul&gt;
  &lt;&#x2F;li&gt;
  &lt;li&gt;&lt;a href=&quot;#conclusion&quot; id=&quot;markdown-toc-conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h2 id=&quot;improvements&quot;&gt;Improvements&lt;&#x2F;h2&gt;

&lt;p&gt;The old documentation website was pretty much just an HTML file, a stylesheet, and a &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-com&#x2F;doc-gitlab-com&#x2F;blob&#x2F;master&#x2F;generate.rb&quot;&gt;Ruby script&lt;&#x2F;a&gt; called &lt;code&gt;generate.rb&lt;&#x2F;code&gt;. While it worked, it was hard to update and not very flexible. It mostly laid dormant, only occasionally being touched by developers. The docs team really wanted to update the site to use a &lt;a href=&quot;&#x2F;2016&#x2F;06&#x2F;17&#x2F;ssg-overview-gitlab-pages-part-3-examples-ci&#x2F;&quot;&gt;static site generator&lt;&#x2F;a&gt; and take better advantage of &lt;a href=&quot;https:&#x2F;&#x2F;pages.gitlab.io&quot;&gt;GitLab Pages&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;We chose &lt;a href=&quot;http:&#x2F;&#x2F;nanoc.ws&#x2F;&quot;&gt;Nanoc&lt;&#x2F;a&gt; because it’s fast, it comes with a number of built-in helpers and filters (as well as the ability to create custom ones), and it’s built with Ruby. Overall, we think this was definitely the right choice. The author was very responsive and addressed anything we brought up. Kudos to him on the great project!&lt;&#x2F;p&gt;

&lt;p&gt;Other improvements include syntax highlighting with &lt;a href=&quot;http:&#x2F;&#x2F;rouge.jneen.net&#x2F;&quot;&gt;Rouge&lt;&#x2F;a&gt; (no syntax highlighting was used at all on the old site), breadcrumbs for navigating between pages, and an improved overall design – especially on mobile.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;requirements&quot;&gt;Requirements&lt;&#x2F;h2&gt;

&lt;p&gt;Our documentation site has some unique requirements that I haven’t seen mentioned or solved in any other companies’ blog posts. We have a few products with documentation we want to include in the site: Community Edition, Enterprise Edition, Omnibus GitLab, and GitLab Runner. In the future we’ll likely add more.&lt;&#x2F;p&gt;

&lt;p&gt;Each product has it own repository with its own documentation directory. This allows developers to add documentation in the same merge request they add a new feature or change some behavior, which prevents documentation from becoming outdated.&lt;&#x2F;p&gt;

&lt;p&gt;The site also needed to be flexible enough that we could add versioning to it in the future. Eventually, our goal is to replace the Help section in CE&#x2F;EE with this Docs site, so we need to maintain older versions of the documentation on the Docs site for users on older versions of GitLab.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;the-build-process&quot;&gt;The build process&lt;&#x2F;h2&gt;

&lt;p&gt;Given the requirements and separate repositories, we decided we’d just need to clone the repositories as part of the build process.&lt;&#x2F;p&gt;

&lt;p&gt;Inside Nanoc&#x27;s config file (&lt;code&gt;nanoc.yml&lt;&#x2F;code&gt;), we &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-com&#x2F;gitlab-docs&#x2F;blob&#x2F;30f13e6a81bf9baeda95204b5524c6abf980b1e5&#x2F;nanoc.yaml#L101-149&quot;&gt;have defined&lt;&#x2F;a&gt; a hash of each of our products containing all the data we need. Here&#x27;s an excerpt:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;products&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;ce&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;full_name&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s1&quot;&gt;&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;GitLab&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;Community&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;Edition&#x27;&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;short_name&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s1&quot;&gt;&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;Community&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;Edition&#x27;&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;abbreviation&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s1&quot;&gt;&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;CE&#x27;&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;slug&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s1&quot;&gt;&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;ce&#x27;&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;index_file&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s1&quot;&gt;&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;README.*&#x27;&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;description&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s1&quot;&gt;&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;Browse&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;user&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;and&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;administration&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;documentation&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;and&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;guides&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;for&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;GitLab&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;Community&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;Edition.&#x27;&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;repo&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s1&quot;&gt;&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce.git&#x27;&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;dirs&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
      &lt;span class=&quot;na&quot;&gt;temp_dir&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s1&quot;&gt;&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;tmp&#x2F;ce&#x2F;&#x27;&lt;&#x2F;span&gt;
      &lt;span class=&quot;na&quot;&gt;dest_dir&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s1&quot;&gt;&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;content&#x2F;ce&#x27;&lt;&#x2F;span&gt;
      &lt;span class=&quot;na&quot;&gt;doc_dir&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;  &lt;span class=&quot;s1&quot;&gt;&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;doc&#x27;&lt;&#x2F;span&gt;

&lt;span class=&quot;nn&quot;&gt;...&lt;&#x2F;span&gt;

  &lt;span class=&quot;na&quot;&gt;runner&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;full_name&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s1&quot;&gt;&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;GitLab&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;Runner&#x27;&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;short_name&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s1&quot;&gt;&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;Runner&#x27;&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;abbreviation&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s1&quot;&gt;&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;RU&#x27;&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;slug&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s1&quot;&gt;&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;runner&#x27;&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;index_file&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s1&quot;&gt;&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;index.*&#x27;&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;description&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s1&quot;&gt;&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;Browse&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;installation,&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;configuration,&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;maintenance,&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;and&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;troubleshooting&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;documentation&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;for&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;GitLab&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;Runner.&#x27;&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;repo&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s1&quot;&gt;&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ci-multi-runner.git&#x27;&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;dirs&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
      &lt;span class=&quot;na&quot;&gt;temp_dir&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s1&quot;&gt;&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;tmp&#x2F;runner&#x2F;&#x27;&lt;&#x2F;span&gt;
      &lt;span class=&quot;na&quot;&gt;dest_dir&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s1&quot;&gt;&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;content&#x2F;runner&#x27;&lt;&#x2F;span&gt;
      &lt;span class=&quot;na&quot;&gt;doc_dir&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;  &lt;span class=&quot;s1&quot;&gt;&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;docs&#x27;&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We then have the &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-com&#x2F;gitlab-docs&#x2F;blob&#x2F;30f13e6a81bf9baeda95204b5524c6abf980b1e5&#x2F;Rakefile&quot;&gt;Rakefile&lt;&#x2F;a&gt; where the repos are cloned and the directories that
Nanoc needs are created:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;desc&lt;&#x2F;span&gt; &lt;span class=&quot;s1&quot;&gt;&#x27;Pulls down the CE, EE, Omnibus and Runner git repos and merges the content of their doc directories into the nanoc site&#x27;&lt;&#x2F;span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;&#x2F;span&gt; &lt;span class=&quot;ss&quot;&gt;:pull_repos&lt;&#x2F;span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;&#x2F;span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;&#x2F;span&gt; &lt;span class=&quot;s1&quot;&gt;&#x27;yaml&#x27;&lt;&#x2F;span&gt;

  &lt;span class=&quot;c1&quot;&gt;# By default won&#x27;t delete any directories, requires all relevant directories&lt;&#x2F;span&gt;
  &lt;span class=&quot;c1&quot;&gt;# be empty. Run `RAKE_FORCE_DELETE=true rake pull_repos` to have directories&lt;&#x2F;span&gt;
  &lt;span class=&quot;c1&quot;&gt;# deleted.&lt;&#x2F;span&gt;
  &lt;span class=&quot;n&quot;&gt;force_delete&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;no&quot;&gt;ENV&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;RAKE_FORCE_DELETE&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;&#x2F;span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Parse the config file and create a hash.&lt;&#x2F;span&gt;
  &lt;span class=&quot;n&quot;&gt;config&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;no&quot;&gt;YAML&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;load_file&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;.&#x2F;nanoc.yaml&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Pull products data from the config.&lt;&#x2F;span&gt;
  &lt;span class=&quot;n&quot;&gt;ce&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;products&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ce&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;&#x2F;span&gt;
  &lt;span class=&quot;n&quot;&gt;ee&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;products&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ee&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;&#x2F;span&gt;
  &lt;span class=&quot;n&quot;&gt;omnibus&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;products&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;omnibus&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;&#x2F;span&gt;
  &lt;span class=&quot;n&quot;&gt;runner&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;products&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;runner&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;&#x2F;span&gt;

  &lt;span class=&quot;n&quot;&gt;products&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;ce&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;ee&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;omnibus&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;runner&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;&#x2F;span&gt;
  &lt;span class=&quot;n&quot;&gt;dirs&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;&#x2F;span&gt;
  &lt;span class=&quot;n&quot;&gt;products&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;&#x2F;span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;&#x2F;span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;&#x2F;span&gt;
    &lt;span class=&quot;n&quot;&gt;dirs&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;push&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;dirs&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;temp_dir&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;&#x2F;span&gt;
    &lt;span class=&quot;n&quot;&gt;dirs&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;push&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;dirs&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;dest_dir&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;&#x2F;span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;&#x2F;span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;force_delete&lt;&#x2F;span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;&#x2F;span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;WARNING: Are you sure you want to remove &lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;dirs&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;, &#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;? [y&#x2F;n]&quot;&lt;&#x2F;span&gt;
    &lt;span class=&quot;nb&quot;&gt;exit&lt;&#x2F;span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;&#x2F;span&gt; &lt;span class=&quot;no&quot;&gt;STDIN&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;gets&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;index&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;sr&quot;&gt;&#x2F;y&#x2F;i&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;&#x2F;span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;&#x2F;span&gt;

    &lt;span class=&quot;n&quot;&gt;dirs&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;&#x2F;span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;&#x2F;span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;&#x2F;span&gt;
      &lt;span class=&quot;nb&quot;&gt;puts&lt;&#x2F;span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;=&amp;gt; Deleting &lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt; if it exists&lt;&#x2F;span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;
      &lt;span class=&quot;no&quot;&gt;FileUtils&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;rm_r&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;&#x2F;span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;&#x2F;span&gt;
  &lt;span class=&quot;k&quot;&gt;else&lt;&#x2F;span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;&#x2F;span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;NOTE: The following directories must be empty otherwise this task &quot;&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;&#x2F;span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;will fail:&lt;&#x2F;span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;dirs&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;, &#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;&#x2F;span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;If you want to force-delete the `tmp&#x2F;` and `content&#x2F;` folders so &lt;&#x2F;span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;&#x2F;span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;the task will run without manual intervention, run &lt;&#x2F;span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;&#x2F;span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;`RAKE_FORCE_DELETE=true rake pull_repos`.&quot;&lt;&#x2F;span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;&#x2F;span&gt;

  &lt;span class=&quot;n&quot;&gt;dirs&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;&#x2F;span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;&#x2F;span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;&#x2F;span&gt;
    &lt;span class=&quot;k&quot;&gt;unless&lt;&#x2F;span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;start_with?&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;tmp&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;

      &lt;span class=&quot;nb&quot;&gt;puts&lt;&#x2F;span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;=&amp;gt; Making an empty &lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;
      &lt;span class=&quot;no&quot;&gt;FileUtils&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;mkdir&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;&#x2F;span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;&#x2F;span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;&#x2F;span&gt;

  &lt;span class=&quot;n&quot;&gt;products&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;&#x2F;span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;&#x2F;span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;&#x2F;span&gt;
    &lt;span class=&quot;n&quot;&gt;temp_dir&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;dirs&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;temp_dir&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;&#x2F;span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;&#x2F;span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;=&amp;gt; Cloning &lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;repo&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt; into &lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;temp_dir&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;

    &lt;span class=&quot;sb&quot;&gt;`git clone &lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;repo&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;sb&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;temp_dir&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;sb&quot;&gt; --depth 1 --branch master`&lt;&#x2F;span&gt;

    &lt;span class=&quot;n&quot;&gt;temp_doc_dir&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;dirs&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;temp_dir&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;product&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;dirs&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;doc_dir&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;&#x2F;span&gt; &lt;span class=&quot;s1&quot;&gt;&#x27;.&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;
    &lt;span class=&quot;n&quot;&gt;destination_dir&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;dirs&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;dest_dir&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;&#x2F;span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;&#x2F;span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;=&amp;gt; Copying &lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;temp_doc_dir&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt; into &lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;destination_dir&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;
    &lt;span class=&quot;no&quot;&gt;FileUtils&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;cp_r&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;temp_doc_dir&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;destination_dir&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;&#x2F;span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;pull_repos&lt;&#x2F;code&gt; task inside the Rakefile is pretty self-explanatory if you know
some Ruby, but here&#x27;s what it does:&lt;&#x2F;p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code&gt;nanoc.yml&lt;&#x2F;code&gt; is loaded since it contains the information we need for the
various products:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt; &lt;span class=&quot;n&quot;&gt;config&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;no&quot;&gt;YAML&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;load_file&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;.&#x2F;nanoc.yaml&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;  &lt;&#x2F;li&gt;
  &lt;li&gt;
    &lt;p&gt;The products data are pulled from the config:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt; &lt;span class=&quot;n&quot;&gt;ce&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;products&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ce&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;&#x2F;span&gt;
 &lt;span class=&quot;n&quot;&gt;ee&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;products&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ee&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;&#x2F;span&gt;
 &lt;span class=&quot;n&quot;&gt;omnibus&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;products&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;omnibus&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;&#x2F;span&gt;
 &lt;span class=&quot;n&quot;&gt;runner&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;products&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;runner&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;  &lt;&#x2F;li&gt;
  &lt;li&gt;
    &lt;p&gt;The needed directories to be created (or deleted) are populated in an array:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt; &lt;span class=&quot;n&quot;&gt;products&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;ce&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;ee&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;omnibus&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;runner&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;&#x2F;span&gt;
 &lt;span class=&quot;n&quot;&gt;dirs&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;&#x2F;span&gt;
 &lt;span class=&quot;n&quot;&gt;products&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;&#x2F;span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;&#x2F;span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;&#x2F;span&gt;
   &lt;span class=&quot;n&quot;&gt;dirs&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;push&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;dirs&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;temp_dir&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;&#x2F;span&gt;
   &lt;span class=&quot;n&quot;&gt;dirs&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;push&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;dirs&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;dest_dir&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;&#x2F;span&gt;
 &lt;span class=&quot;k&quot;&gt;end&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;  &lt;&#x2F;li&gt;
  &lt;li&gt;
    &lt;p&gt;The empty directories are created:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt; &lt;span class=&quot;n&quot;&gt;dirs&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;&#x2F;span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;&#x2F;span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;&#x2F;span&gt;
   &lt;span class=&quot;k&quot;&gt;unless&lt;&#x2F;span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;start_with?&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;tmp&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;

     &lt;span class=&quot;nb&quot;&gt;puts&lt;&#x2F;span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;=&amp;gt; Making an empty &lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;
     &lt;span class=&quot;no&quot;&gt;FileUtils&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;mkdir&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;&#x2F;span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;
   &lt;span class=&quot;k&quot;&gt;end&lt;&#x2F;span&gt;
 &lt;span class=&quot;k&quot;&gt;end&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;  &lt;&#x2F;li&gt;
  &lt;li&gt;
    &lt;p&gt;We finally copy the contents of the documentation directory (defined by
&lt;code&gt;doc_dir&lt;&#x2F;code&gt;) for each product from &lt;code&gt;tmp&#x2F;&lt;&#x2F;code&gt; to &lt;code&gt;content&#x2F;&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt; &lt;span class=&quot;n&quot;&gt;products&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;&#x2F;span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;&#x2F;span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;&#x2F;span&gt;
   &lt;span class=&quot;n&quot;&gt;temp_dir&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;dirs&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;temp_dir&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;&#x2F;span&gt;
   &lt;span class=&quot;nb&quot;&gt;puts&lt;&#x2F;span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;=&amp;gt; Cloning &lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;repo&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt; into &lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;temp_dir&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;

   &lt;span class=&quot;sb&quot;&gt;`git clone &lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;repo&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;sb&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;temp_dir&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;sb&quot;&gt; --depth 1 --branch master`&lt;&#x2F;span&gt;

   &lt;span class=&quot;n&quot;&gt;temp_doc_dir&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;dirs&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;temp_dir&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;product&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;dirs&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;doc_dir&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;&#x2F;span&gt; &lt;span class=&quot;s1&quot;&gt;&#x27;.&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;
   &lt;span class=&quot;n&quot;&gt;destination_dir&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;dirs&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;dest_dir&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;&#x2F;span&gt;
   &lt;span class=&quot;nb&quot;&gt;puts&lt;&#x2F;span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;=&amp;gt; Copying &lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;temp_doc_dir&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt; into &lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;destination_dir&lt;&#x2F;span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;
   &lt;span class=&quot;no&quot;&gt;FileUtils&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;nf&quot;&gt;cp_r&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;temp_doc_dir&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;destination_dir&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;
 &lt;span class=&quot;k&quot;&gt;end&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
    &lt;p&gt;&lt;code&gt;content&#x2F;&lt;&#x2F;code&gt; is where Nanoc looks for the actual site’s Markdown files. To prevent the &lt;code&gt;tmp&#x2F;&lt;&#x2F;code&gt; and &lt;code&gt;content&#x2F;&lt;&#x2F;code&gt; subdirectories from being pushed after testing the site locally, they’re excluded by &lt;code&gt;.gitignore&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
  &lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;

&lt;p&gt;In the future we may speed this up further by caching the &lt;code&gt;tmp&lt;&#x2F;code&gt; folder in CI. The task would need to be updated to check if the local repository is up-to-date with the remote, only cloning if they differ.&lt;&#x2F;p&gt;

&lt;p&gt;Now that all the needed files are in order, we run &lt;code&gt;nanoc&lt;&#x2F;code&gt; to build the static sire. Nanoc runs each Markdown file through a series of &lt;a href=&quot;http:&#x2F;&#x2F;nanoc.ws&#x2F;doc&#x2F;reference&#x2F;filters&#x2F;&quot;&gt;filters&lt;&#x2F;a&gt; defined by rules in the &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-com&#x2F;gitlab-docs&#x2F;blob&#x2F;30f13e6a81bf9baeda95204b5524c6abf980b1e5&#x2F;Rules&quot;&gt;&lt;code&gt;Rules&lt;&#x2F;code&gt; file&lt;&#x2F;a&gt;. We currently use &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;vmg&#x2F;redcarpet&quot;&gt;Redcarpet&lt;&#x2F;a&gt; as the Markdown parser along with Rouge for syntax highlighting, as well as some custom filters. We plan on &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-com&#x2F;gitlab-docs&#x2F;issues&#x2F;50&quot;&gt;moving to Kramdown as our Markdown parser in the future&lt;&#x2F;a&gt; as it provides some nice stuff like user-defined Table of Contents, etc.&lt;&#x2F;p&gt;

&lt;p&gt;We also define some filters inside the &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-com&#x2F;gitlab-docs&#x2F;tree&#x2F;30f13e6a81bf9baeda95204b5524c6abf980b1e5&#x2F;lib&#x2F;filters&quot;&gt;&lt;code&gt;lib&#x2F;filters&#x2F;&lt;&#x2F;code&gt; directory&lt;&#x2F;a&gt;,
including one that &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-com&#x2F;gitlab-docs&#x2F;blob&#x2F;30f13e6a81bf9baeda95204b5524c6abf980b1e5&#x2F;lib&#x2F;filters&#x2F;markdown_to_html_ext.rb&quot;&gt;replaces any &lt;code&gt;.md&lt;&#x2F;code&gt; extension with &lt;code&gt;.html&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;The Table of Contents (ToC) is generated for each page except when it&#x27;s named &lt;code&gt;index.md&lt;&#x2F;code&gt;
or &lt;code&gt;README.md&lt;&#x2F;code&gt; as we usually use these as landing pages to index other
documentation files and we don&#x27;t want them to have a ToC. All this and some
other options that Redcarpet provides &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-com&#x2F;gitlab-docs&#x2F;blob&#x2F;30f13e6a81bf9baeda95204b5524c6abf980b1e5&#x2F;Rules#L33-51&quot;&gt;are defined in the &lt;code&gt;Rules&lt;&#x2F;code&gt; file&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;For more on the specifics of building a site with Nanoc, see &lt;a href=&quot;http:&#x2F;&#x2F;nanoc.ws&#x2F;doc&#x2F;tutorial&#x2F;&quot;&gt;the Nanoc tutorial&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;taking-advantage-of-gitlab-to-put-everything-together&quot;&gt;Taking advantage of GitLab to put everything together&lt;&#x2F;h2&gt;

&lt;p&gt;The new docs portal is hosted on GitLab.com at &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-com&#x2F;gitlab-docs&quot;&gt;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-com&#x2F;gitlab-docs&lt;&#x2F;a&gt;.
In that project we create issues, discuss things, open merge requests in feature
branches, iterate on feedback and finally merge things in the &lt;code&gt;master&lt;&#x2F;code&gt; branch.
Again, the documentation source files are not stored in this repository, if
you want to contribute, you&#x27;d have to open a merge request to the respective
project.&lt;&#x2F;p&gt;

&lt;p&gt;There are 3 key things we use to test, build, deploy and host the Nanoc site
all built into GitLab: &lt;a href=&quot;&#x2F;gitlab-ci&quot;&gt;GitLab CI&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;features&#x2F;review-apps&quot;&gt;Review Apps&lt;&#x2F;a&gt;
and &lt;a href=&quot;https:&#x2F;&#x2F;pages.gitlab.io&quot;&gt;GitLab Pages&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;Let&#x27;s break it down to pieces.&lt;&#x2F;p&gt;

&lt;h3 id=&quot;gitlab-ci&quot;&gt;GitLab CI&lt;&#x2F;h3&gt;

&lt;p&gt;GitLab CI is responsible of all the stages that we go through to publish
new documentation: test, build and deploy.&lt;&#x2F;p&gt;

&lt;p&gt;Nanoc has a built-in system of &lt;a href=&quot;http:&#x2F;&#x2F;nanoc.ws&#x2F;doc&#x2F;testing&#x2F;&quot;&gt;Checks&lt;&#x2F;a&gt;, including HTML&#x2F;CSS and internal&#x2F;external link validation. With GitLab CI we test with the internal link checker (set to &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;#allow_failure&quot;&gt;&lt;code&gt;allow failure&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;) and also verify that the site compiles without errors. We also run a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;brigade&#x2F;scss-lint&quot;&gt;SCSS Linter&lt;&#x2F;a&gt; to make sure our SCSS looks uniform.&lt;&#x2F;p&gt;

&lt;p&gt;Our full &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-com&#x2F;gitlab-docs&#x2F;blob&#x2F;master&#x2F;.gitlab-ci.yml&quot;&gt;&lt;code&gt;.gitlab-ci.yml&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; file looks like this. We&#x27;ll break it down to make it clear what it is doing:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;image&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;ruby:2.3&lt;&#x2F;span&gt;

&lt;span class=&quot;c1&quot;&gt;## Cache the vendor&#x2F;ruby directory&lt;&#x2F;span&gt;
&lt;span class=&quot;na&quot;&gt;cache&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;key&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;ruby-231&quot;&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;paths&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;vendor&#x2F;ruby&lt;&#x2F;span&gt;

&lt;span class=&quot;c1&quot;&gt;## Define the stages&lt;&#x2F;span&gt;
&lt;span class=&quot;na&quot;&gt;stages&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;test&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;deploy&lt;&#x2F;span&gt;

&lt;span class=&quot;c1&quot;&gt;## Before each job&#x27;s script is run, run the commands below&lt;&#x2F;span&gt;
&lt;span class=&quot;na&quot;&gt;before_script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;ruby -v&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;bundle install --jobs 4 --path vendor&lt;&#x2F;span&gt;

&lt;span class=&quot;c1&quot;&gt;## Make sure the site builds successfully&lt;&#x2F;span&gt;
&lt;span class=&quot;na&quot;&gt;verify_compile&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;test&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;rake pull_repos&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;nanoc&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;artifacts&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;paths&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;public&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;expire_in&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;1w&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;except&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;master&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;tags&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;docker&lt;&#x2F;span&gt;

&lt;span class=&quot;c1&quot;&gt;## Check for dead internal links using Nanoc&#x27;s built-in tool&lt;&#x2F;span&gt;
&lt;span class=&quot;na&quot;&gt;internal_links&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;test&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;rake pull_repos&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;nanoc&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;nanoc check internal_links&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;allow_failure&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;tags&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;docker&lt;&#x2F;span&gt;

&lt;span class=&quot;c1&quot;&gt;## Make sure our SCSS stylesheets are correctly defined&lt;&#x2F;span&gt;
&lt;span class=&quot;na&quot;&gt;scss_lint&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;test&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;bundle exec scss-lint&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;tags&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;docker&lt;&#x2F;span&gt;

&lt;span class=&quot;c1&quot;&gt;## A job that deploys a review app to a dedicated server running Nginx.&lt;&#x2F;span&gt;
&lt;span class=&quot;na&quot;&gt;review&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;deploy&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;variables&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;GIT_STRATEGY&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;none&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;before_script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;pi&quot;&gt;[]&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;cache&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;pi&quot;&gt;{}&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;rsync -av --delete public &#x2F;srv&#x2F;nginx&#x2F;pages&#x2F;$CI_BUILD_REF_NAME&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;environment&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;review&#x2F;$CI_BUILD_REF_NAME&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;url&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;http:&#x2F;&#x2F;$CI_BUILD_REF_NAME.$APPS_DOMAIN&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;on_stop&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;review_stop&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;only&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;branches@gitlab-com&#x2F;gitlab-docs&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;except&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;master&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;tags&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;nginx&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;review-apps&lt;&#x2F;span&gt;

&lt;span class=&quot;c1&quot;&gt;## Stop the review app&lt;&#x2F;span&gt;
&lt;span class=&quot;na&quot;&gt;review_stop&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;deploy&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;variables&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;GIT_STRATEGY&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;none&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;before_script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;pi&quot;&gt;[]&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;artifacts&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;pi&quot;&gt;{}&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;cache&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;pi&quot;&gt;{}&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;dependencies&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;pi&quot;&gt;[]&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;rm -rf public &#x2F;srv&#x2F;nginx&#x2F;pages&#x2F;$CI_BUILD_REF_NAME&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;when&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;manual&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;environment&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;review&#x2F;$CI_BUILD_REF_NAME&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;action&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;stop&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;only&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;branches@gitlab-com&#x2F;gitlab-docs&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;except&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;master&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;tags&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;nginx&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;review-apps&lt;&#x2F;span&gt;

&lt;span class=&quot;c1&quot;&gt;## Deploy the static site to GitLab Pages&lt;&#x2F;span&gt;
&lt;span class=&quot;na&quot;&gt;pages&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;deploy&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;environment&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;production&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;url&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;https:&#x2F;&#x2F;docs.gitlab.com&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;rake pull_repos&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;nanoc&lt;&#x2F;span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Symlink all README.html to index.html&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;for i in `find public -name README.html`; do ln -sf README.html $(dirname $i)&#x2F;index.html; done&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;artifacts&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;paths&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;public&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;expire_in&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;1h&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;only&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;master@gitlab-com&#x2F;gitlab-docs&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;tags&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;docker&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To better visualize how the jobs are run, take a look at how the pipeline
graph looks like for &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-com&#x2F;gitlab-docs&#x2F;pipelines&#x2F;5266794&quot;&gt;one of the pipelines&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blogimages&#x2F;new-gitlab-docs-site&#x2F;pipeline-graph.png&quot; alt=&quot;Pipeline graph example&quot; class=&quot;shadow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;p&gt;Let&#x27;s see what all these settings mean.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;For more information, you can read the &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;&quot;&gt;documentation on &lt;code&gt;.gitlab-ci.yml&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;hr &#x2F;&gt;

&lt;p&gt;Define the Docker image to be used:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;image&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;ruby:2.3&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;#cache&quot;&gt;Cache&lt;&#x2F;a&gt; the vendor&#x2F;ruby directory so that we don&#x27;t have to install the
gems for each job&#x2F;pipeline:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;cache&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;key&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;&lt;span class=&quot;s&quot;&gt;ruby-231&quot;&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;paths&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;vendor&#x2F;ruby&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Define the &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;#stages&quot;&gt;stages&lt;&#x2F;a&gt; the jobs will run:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;stages&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;test&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;deploy&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Before each job&#x27;s script is run, run the commands that are defined in the
&lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;#before_script&quot;&gt;&lt;code&gt;before_script&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;. Display the Ruby version and install
the needed gems:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;before_script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;ruby -v&lt;&#x2F;span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;bundle install --jobs 4 --path vendor&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In the &lt;code&gt;verify_compile&lt;&#x2F;code&gt; job we make sure the site builds successfully.
It first pulls the repos locally, then runs &lt;code&gt;nanoc&lt;&#x2F;code&gt; to compile the site.
The &lt;code&gt;public&#x2F;&lt;&#x2F;code&gt; directory where the static site is built, is uploaded as
an artifact so that it can pass between stages. We define an expire date of
one week. The job runs on all refs except master. The &lt;code&gt;docker&lt;&#x2F;code&gt; tag ensures that
this job is picked by the shared Runners on GitLab.com:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;verify_compile&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;test&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;rake pull_repos&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;nanoc&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;artifacts&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;paths&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;public&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;expire_in&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;1w&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;except&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;master&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;tags&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;docker&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In the &lt;code&gt;internal_links&lt;&#x2F;code&gt; job we check for dead internal links using Nanoc&#x27;s
built-in functionality. We first need to pull the repos and compile the static
site. We allow it to fail since the source of the dead links are in a
different repository, not much related with the current one.
The &lt;code&gt;docker&lt;&#x2F;code&gt; tag ensures that this job is picked by the shared Runners
on GitLab.com:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;internal_links&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;test&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;rake pull_repos&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;nanoc&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;nanoc check internal_links&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;allow_failure&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;tags&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;docker&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;scss_lint&lt;&#x2F;code&gt; job makes sure our SCSS stylesheets are correctly defined by
running a linter on them. The &lt;code&gt;docker&lt;&#x2F;code&gt; tag ensures that this job is picked by
the shared Runners on GitLab.com:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;scss_lint&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;test&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;bundle exec scss-lint&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;tags&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;docker&lt;&#x2F;span&gt;

&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Next, we define the Review Apps.&lt;&#x2F;p&gt;

&lt;h3 id=&quot;review-apps&quot;&gt;Review Apps&lt;&#x2F;h3&gt;

&lt;p&gt;When opening a merge request for the docs site we use a new feature called &lt;a href=&quot;&#x2F;features&#x2F;review-apps&#x2F;&quot;&gt;Review Apps&lt;&#x2F;a&gt; to test changes. This lets us test new features, style changes, new sections, etc., by deploying the updated static site to a test domain. On every merge request that all jobs finished successfully, we can see a link with the URL to the temporary deployed docs site.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blogimages&#x2F;gitlab-docs-review-apps-screenshot.png&quot; alt=&quot;Review apps&quot; class=&quot;shadow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;p&gt;We define two additional jobs for that purpose in &lt;code&gt;.gitlab-ci.yml&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;review&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;deploy&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;variables&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;GIT_STRATEGY&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;none&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;before_script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;pi&quot;&gt;[]&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;cache&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;pi&quot;&gt;{}&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;rsync -av --delete public &#x2F;srv&#x2F;nginx&#x2F;pages&#x2F;$CI_BUILD_REF_NAME&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;environment&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;review&#x2F;$CI_BUILD_REF_NAME&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;url&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;http:&#x2F;&#x2F;$CI_BUILD_REF_NAME.$APPS_DOMAIN&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;on_stop&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;review_stop&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;only&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;branches@gitlab-com&#x2F;gitlab-docs&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;except&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;master&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;tags&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;nginx&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;review-apps&lt;&#x2F;span&gt;

&lt;span class=&quot;na&quot;&gt;review_stop&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;deploy&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;variables&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;GIT_STRATEGY&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;none&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;before_script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;pi&quot;&gt;[]&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;artifacts&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;pi&quot;&gt;{}&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;cache&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;pi&quot;&gt;{}&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;dependencies&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;pi&quot;&gt;[]&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;rm -rf public &#x2F;srv&#x2F;nginx&#x2F;pages&#x2F;$CI_BUILD_REF_NAME&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;when&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;manual&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;environment&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;review&#x2F;$CI_BUILD_REF_NAME&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;action&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;stop&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;only&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;branches@gitlab-com&#x2F;gitlab-docs&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;except&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;master&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;tags&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;nginx&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;review-apps&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;They both run on all branches except &lt;code&gt;master&lt;&#x2F;code&gt; since &lt;code&gt;master&lt;&#x2F;code&gt; is deployed straight
to production. Once someone with write access to the repository pushes a branch
and creates a merge request, if the jobs in the &lt;code&gt;test&lt;&#x2F;code&gt; stage finish successfully,
the &lt;code&gt;review&lt;&#x2F;code&gt; job deploys the code of that particular branch to a server. The
server is set up to &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-examples&#x2F;review-apps-nginx&quot;&gt;use Nginx with Review Apps&lt;&#x2F;a&gt;, and it uses
the artifacts from the previously &lt;code&gt;verify_compile&lt;&#x2F;code&gt; job which contain the
&lt;code&gt;public&#x2F;&lt;&#x2F;code&gt; directory with the HTML files Nanoc compiled.&lt;&#x2F;p&gt;

&lt;p&gt;Notice that both jobs rely on &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;environments.html#dynamic-environments&quot;&gt;dynamic environments&lt;&#x2F;a&gt; and with
the &lt;code&gt;review&#x2F;&lt;&#x2F;code&gt; prefix we can group them under the &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-com&#x2F;gitlab-docs&#x2F;environments&quot;&gt;Environments page&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;The &lt;code&gt;review_stop&lt;&#x2F;code&gt; job depends on the &lt;code&gt;review&lt;&#x2F;code&gt; one and is called whenever we
want to clear up the review app. By default it is called every time the related
branch is deleted, but you can also manually call it with the buttons that can
be found in GitLab.&lt;&#x2F;p&gt;

&lt;p&gt;The trick of this particular set up is that we use the shared Runners provided
in GitLab.com to test and build the docs site (using Docker containers) whereas
we use a specific Runner that is set up in the server that hosts the Review Apps
and is configured with the &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;runner&#x2F;executors&#x2F;shell.html&quot;&gt;shell executor&lt;&#x2F;a&gt;. GitLab CI knows what Runner to use
each time from the &lt;code&gt;tags&lt;&#x2F;code&gt; we provide each job with.&lt;&#x2F;p&gt;

&lt;p&gt;The &lt;code&gt;review&lt;&#x2F;code&gt; job has also some other things specified:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;variables&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;GIT_STRATEGY&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;none&lt;&#x2F;span&gt;
&lt;span class=&quot;na&quot;&gt;before_script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;pi&quot;&gt;[]&lt;&#x2F;span&gt;
&lt;span class=&quot;na&quot;&gt;cache&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;pi&quot;&gt;{}&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In this case, &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;#git-strategy&quot;&gt;&lt;code&gt;GIT_STRATEGY&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is set up to &lt;code&gt;none&lt;&#x2F;code&gt; since we don&#x27;t need to
checkout the repository for this job. We only use &lt;code&gt;rsync&lt;&#x2F;code&gt; to copy over the
artifacts that were passed from the previous job to the server where Review
Apps are deployed. We also turn off the &lt;code&gt;before_script&lt;&#x2F;code&gt; since we don&#x27;t need it
to run, same for &lt;code&gt;cache&lt;&#x2F;code&gt;. They both are defined globally, so you need to pass
an empty array and hash respectively to disable them in a job level.&lt;&#x2F;p&gt;

&lt;p&gt;On the other hand, setting the &lt;code&gt;GIT_STRATEGY&lt;&#x2F;code&gt; to &lt;code&gt;none&lt;&#x2F;code&gt; is necessary on the
&lt;code&gt;review_stop&lt;&#x2F;code&gt; job so that the GitLab Runner won&#x27;t try to checkout the code after
the branch is deleted. We also define one additional thing in it:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;dependencies&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;pi&quot;&gt;[]&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Since this is the last job that is performed in the lifecycle of a merge request
(after it&#x27;s merged and the branch deleted), we opt to not download any artifacts
from the previous stage with passing an empty array in &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;#dependencies&quot;&gt;&lt;code&gt;dependencies&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;hr &#x2F;&gt;

&lt;p&gt;See &lt;a href=&quot;&#x2F;2016&#x2F;11&#x2F;22&#x2F;introducing-review-apps&#x2F;&quot;&gt;our blog post on Review Apps&lt;&#x2F;a&gt; for
more information about how they work and their purpose. Be sure to also check
the &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;review_apps&#x2F;index.html&quot;&gt;Review Apps documentation&lt;&#x2F;a&gt; as well as &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;environments.html#dynamic-environments&quot;&gt;how dynamic environments work&lt;&#x2F;a&gt;
since they are the basis of the Review Apps.&lt;&#x2F;p&gt;

&lt;p&gt;The final step after the site gets successfully built is to deploy to
production which is under the URL everybody knows: &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&quot;&gt;https:&#x2F;&#x2F;docs.gitlab.com&lt;&#x2F;a&gt;.
For that purpose, we use &lt;a href=&quot;https:&#x2F;&#x2F;pages.gitlab.io&quot;&gt;GitLab Pages&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;h3 id=&quot;gitlab-pages&quot;&gt;GitLab Pages&lt;&#x2F;h3&gt;

&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;pages.gitlab.io&#x2F;&quot;&gt;GitLab Pages&lt;&#x2F;a&gt; hosts &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Static_web_page&quot;&gt;static websites&lt;&#x2F;a&gt; and can be used with any Static Site Generator, including &lt;a href=&quot;https:&#x2F;&#x2F;jekyllrb.com&#x2F;&quot;&gt;Jekyll&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;gohugo.io&#x2F;&quot;&gt;Hugo&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;middlemanapp.com&#x2F;&quot;&gt;Middleman&lt;&#x2F;a&gt;, &lt;a href=&quot;http:&#x2F;&#x2F;blog.getpelican.com&#x2F;&quot;&gt;Pelican&lt;&#x2F;a&gt;, and of course Nanoc.&lt;&#x2F;p&gt;

&lt;p&gt;GitLab Pages allows us to create the static site dynamically since it just deploys the &lt;code&gt;public&lt;&#x2F;code&gt; directory after the GitLab CI task is done. The job responsible for this is named &lt;code&gt;pages&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;A production environment is set with a url to the of the docs portal.
The script pulls the repos, runs &lt;code&gt;nanoc&lt;&#x2F;code&gt; to compile the static site.
The &lt;code&gt;public&#x2F;&lt;&#x2F;code&gt; directory where the static site is built, is uploaded as
an artifact so that it can be deployed to GitLab Pages. We define an expire
date of one hour and the job runs only on the master branch.
The &lt;code&gt;docker&lt;&#x2F;code&gt; tag ensures that this job is picked by the shared Runners
on GitLab.com.&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;pages&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;deploy&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;environment&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;production&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;url&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;https:&#x2F;&#x2F;docs.gitlab.com&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;rake pull_repos&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;nanoc&lt;&#x2F;span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Symlink all README.html to index.html&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;for i in `find public -name README.html`; do ln -sf README.html $(dirname $i)&#x2F;index.html; done&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;artifacts&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;paths&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;public&lt;&#x2F;span&gt;
    &lt;span class=&quot;na&quot;&gt;expire_in&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;1h&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;only&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;master@gitlab-com&#x2F;gitlab-docs&lt;&#x2F;span&gt;
  &lt;span class=&quot;na&quot;&gt;tags&lt;&#x2F;span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;&#x2F;span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;docker&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;GitLab Pages deploys our documentation site whenever a commit is made to the master branch of the gitlab-docs repository and is run only on the &lt;code&gt;master&lt;&#x2F;code&gt; branch of the gitlab-docs project.&lt;&#x2F;p&gt;

&lt;p&gt;Since the documentation content itself is not hosted under the gitlab-docs repository, we rely to a CI job under all the products we build the docs site from. We specifically &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;triggers&#x2F;&quot;&gt;make use of triggers&lt;&#x2F;a&gt; where a build for the docs site is triggered whenever CI runs successfully on the master branches of CE, EE, Omnibus GitLab, or Runner. If you go to the &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-com&#x2F;gitlab-docs&#x2F;pipelines&quot;&gt;pipelines page of the gitlab-docs project&lt;&#x2F;a&gt;, you can notice the &lt;strong&gt;triggered&lt;&#x2F;strong&gt; word next to the pipelines that are re-run because a trigger was initiated.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blogimages&#x2F;new-gitlab-docs-site&#x2F;pipelines-triggers.png&quot; alt=&quot;Pipeline triggers&quot; class=&quot;shadow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;p&gt;How we specifically use triggers for gitlab-docs is briefly described in the
&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-com&#x2F;gitlab-docs&#x2F;blob&#x2F;master&#x2F;README.md#deployment-process&quot;&gt;project&#x27;s readme&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;We also use a hack to symlink all &lt;code&gt;README.html&lt;&#x2F;code&gt; files into &lt;code&gt;index.html&lt;&#x2F;code&gt; so that
they can be viewed without the extension. Notice how the following links point
to the same document:&lt;&#x2F;p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;README.html&quot;&gt;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;README.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;index.html&quot;&gt;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;index.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;&quot;&gt;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;p&gt;The line responsible for this is:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for &lt;&#x2F;span&gt;i &lt;span class=&quot;k&quot;&gt;in&lt;&#x2F;span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;&#x2F;span&gt;find public -name README.html&lt;span class=&quot;sb&quot;&gt;`&lt;&#x2F;span&gt;; &lt;span class=&quot;k&quot;&gt;do &lt;&#x2F;span&gt;ln -sf README.html &lt;span class=&quot;k&quot;&gt;$(&lt;&#x2F;span&gt;dirname &lt;span class=&quot;nv&quot;&gt;$i&lt;&#x2F;span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;&#x2F;span&gt;&#x2F;index.html; &lt;span class=&quot;k&quot;&gt;done&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The artifacts are made to &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;yaml&#x2F;#artifacts-expire_in&quot;&gt;expire in&lt;&#x2F;a&gt; an hour since they are deployed to the
GitLab Pages server, we don&#x27;t need them lingering in GitLab forever.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;It’s worth noting that GitLab Pages is a &lt;a href=&quot;&#x2F;products&quot;&gt;GitLab Enterprise Edition&lt;&#x2F;a&gt;-only feature, but it’s also available for free on GitLab.com.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;

&lt;p&gt;Hopefully this shows some GitLab&#x27;s power and how having everything integrated into one cohesive product simplifies one&#x27;s workflow. If you have a complex documentation site you’d like to put together from specific directories in multiple Git repositories, the process described above is the best we&#x27;ve been able to come up with. If you have any ideas to make this system better, let us know!&lt;&#x2F;p&gt;

&lt;p&gt;The documentation website is &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-com&#x2F;gitlab-docs&quot;&gt;open source&lt;&#x2F;a&gt;, available under the MIT License. You’re welcome to take a look at it, submit a merge request, or even fork it to use it with your own project.&lt;&#x2F;p&gt;

&lt;p&gt;Thanks for reading, if you have any questions we’d be happy to answer them in the comments!&lt;&#x2F;p&gt;

&lt;!-- Cover image: https:&#x2F;&#x2F;unsplash.com&#x2F;photos&#x2F;G6G93jtU1vE --&gt;

&lt;img src=&#x27;https:&#x2F;&#x2F;about.gitlab.com&#x2F;images&#x2F;blogimages&#x2F;book.jpg&#x27; class=&#x27;webfeedsFeaturedVisual&#x27; style=&#x27;display: none;&#x27; &#x2F;&gt;    </content>
  </entry>
  <entry>
    <title>Here's how new programmers can learn by contributing to GitLab</title>
    <link rel="alternate" href="https://about.gitlab.com/2016/12/07/heres-how-new-programmers-can-learn-by-contributing-to-gitlab/"/>
    <id>https://about.gitlab.com/2016/12/07/heres-how-new-programmers-can-learn-by-contributing-to-gitlab/</id>
    <published>2016-12-07T00:00:00+00:00</published>
    <updated>2016-12-07T00:00:00+00:00</updated>
    <author>
      <name>Emily von Hoffmann</name>
    </author>
    <content type="html">
&lt;p&gt;Hello, relative newcomers. There’s room for you to contribute, too. You can start by finding other programmers, making a plan before you code, documenting properly, and poking around on GitLab so you&#x27;re never ever learning in a vacuum. Teaching yourself is hard, so here are a few tips.&lt;&#x2F;p&gt;



&lt;h2 id=&quot;find-other-programmers-near-you&quot;&gt;Find other programmers near you.&lt;&#x2F;h2&gt;

&lt;p&gt;Software development is not something you can learn by yourself. You have to incorporate other perspectives, and bounce ideas off of people. Go to meet-ups, conferences and any events that will facilitate meeting with other developers. Try to work on projects with other deveopers, because most of your professional work will involve you and others collaborating on a project - it is very difficult to design and maintain complex software on your own! This can also help you avoid developing bad habits in the first place - it&#x27;s much more difficult to un-learn them down the road. Even making small contributions to GitLab will help you get involved and practice communicating with others working on the same project.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;plan-before-you-start-coding&quot;&gt;Plan before you start coding.&lt;&#x2F;h2&gt;

&lt;p&gt;This is an important part of software development that lots of new programmers tend to ignore. Often they want to start writing code as soon as possible, but skipping the planning stage has a negative effect in the long term. You’ll likely end up with an unmaintainable code base and a poorly designed application. You need to embrace the &quot;boring parts&quot; of software development. These help remind you to start small by mastering the basics before you try to build the next Facebook. &lt;a href=&quot;https:&#x2F;&#x2F;pragprog.com&#x2F;book&#x2F;tpp&#x2F;the-pragmatic-programmer&quot;&gt;&lt;em&gt;The Pragmatic Programmer&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; by Andrew Hunt and David Thomas is an excellent resource. GitLab&#x27;s issue board is a useful tool for thinking through potential challenges and creating detailed checklists.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;follow-conventions-and-standards&quot;&gt;Follow conventions and standards.&lt;&#x2F;h2&gt;

&lt;p&gt;There are some generally accepted coding conventions and standards you will come across when you start out. The advantage of following these norms is that most of them were created or developed by really smart people who discovered ways of making it easier to develop and maintain software. This will save you from falling into most mistakes others have found solutions to. That’s why it is really important to try and contribute to open source projects; they tend to expose you to these standards. GitLab has a &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;blob&#x2F;master&#x2F;CONTRIBUTING.md&quot;&gt;contribution guide&lt;&#x2F;a&gt; to help you get started.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;document-properly&quot;&gt;Document properly.&lt;&#x2F;h2&gt;

&lt;p&gt;This might seem trivial, but it is actually one of the most difficult aspects of software development. There is a recurring joke of how naming things can be really difficult. It is important to document properly to ensure others can understand your code and make it possible for you to return to your program after some time away from it without pulling your hair out. Young programmers tend to focus more on making their code run, ignoring their code readability. To avoid other people rewriting your code, ensure you document it properly, comment on your code and use proper naming conventions. This blog &lt;a href=&quot;http:&#x2F;&#x2F;blog.codinghorror.com&#x2F;coding-for-violent-psychopaths&#x2F;&quot;&gt;article&lt;&#x2F;a&gt; illustrates how important it is to document and follow best practices: “Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.” Especially since your work on GitLab will likely be public, you should assume that at some point in the future, someone will look through your code and perhaps even borrow from it.&lt;&#x2F;p&gt;

&lt;p&gt;Almost anyone can learn how to code, but it takes tremendous effort to learn how to build complex software that can be maintained for years. It will be great if more experienced developers reading this article give feedback or add to the points given above in the comment section.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;em&gt;If you have some Ruby&#x2F;JavaScript experience and you’re looking for a place to get started, check out our &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;blob&#x2F;master&#x2F;CONTRIBUTING.md&quot;&gt;Contribution Guide&lt;&#x2F;a&gt; and the “&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;blob&#x2F;master&#x2F;CONTRIBUTING.md#i-want-to-contribute&quot;&gt;up for grabs&lt;&#x2F;a&gt;” label.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;

&lt;p&gt;&lt;em&gt;Tweet &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;gitlab&quot;&gt;@GitLab&lt;&#x2F;a&gt; and check out our &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;jobs&#x2F;&quot;&gt;job openings&lt;&#x2F;a&gt;.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;img src=&#x27;https:&#x2F;&#x2F;about.gitlab.com&#x2F;images&#x2F;blogimages&#x2F;heres-how-new-programmers-can-learn-by-contributing-to-gitlab.png&#x27; class=&#x27;webfeedsFeaturedVisual&#x27; style=&#x27;display: none;&#x27; &#x2F;&gt;    </content>
  </entry>
  <entry>
    <title>GitLab at DOES San Francisco 2016</title>
    <link rel="alternate" href="https://about.gitlab.com/2016/12/06/gitlab-at-does-2016/"/>
    <id>https://about.gitlab.com/2016/12/06/gitlab-at-does-2016/</id>
    <published>2016-12-06T00:00:00+00:00</published>
    <updated>2016-12-06T00:00:00+00:00</updated>
    <author>
      <name>Emily von Hoffmann</name>
    </author>
    <content type="html">
&lt;p&gt;Our U.S. Sales Director, Haydn Mackay, joined Matt Hines on &lt;a href=&quot;https:&#x2F;&#x2F;devops.com&#x2F;&quot;&gt;DevOpsTV&lt;&#x2F;a&gt; at &lt;a href=&quot;http:&#x2F;&#x2F;events.itrevolution.com&#x2F;us&#x2F;&quot;&gt;DOES 2016&lt;&#x2F;a&gt; in San Francisco last month. They chat about some trends they observed at the event, such as companies&#x27; increasing willingness to talk about tooling, and a warmer embrace of open source. Haydn explains what we do, how we got started, and mentions a few of our clients with names you might recognize. Watch their conversation or skim the transcript below, and tweet us your thoughts @GitLab.&lt;&#x2F;p&gt;



&lt;h2 id=&quot;watch&quot;&gt;Watch:&lt;&#x2F;h2&gt;

&lt;figure class=&quot;video_container&quot;&gt;
  &lt;iframe src=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;embed&#x2F;KooCVhZ7nHk&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot;&gt; &lt;&#x2F;iframe&gt;
&lt;&#x2F;figure&gt;

&lt;h2 id=&quot;read&quot;&gt;Read:&lt;&#x2F;h2&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;Matt:&lt;&#x2F;strong&gt; GitHub we&#x27;re very familiar with, but GitLab is an emerging presence here so can you tell us about what it is you guys do?&lt;&#x2F;p&gt;

&lt;p&gt;&lt;strong&gt;Haydn:&lt;&#x2F;strong&gt; We&#x27;re the new kids on the block in the Git space; we do get mistaken for GitHub all the time. We&#x27;re an open source code collaboration platform, been around since 2013 when the project was started by our CTO. We incorporated in the States in 2014. head office here in San Francisco. We&#x27;re open source, but we also sell an Enterprise Edition. Version control, issue tracking, code review, and CI&#x2F;CD all baked into a single application.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;Matt:&lt;&#x2F;strong&gt; Who are your customers who are here this week? Are there any onsite talking about what they&#x27;re doing?&lt;&#x2F;p&gt;

&lt;p&gt;&lt;strong&gt;Haydn:&lt;&#x2F;strong&gt; There are the two sides of the business - the community edition, our open source product. There are a lot of people who come up and see my shirt and say, &quot;hey, we love GitLab, we use you.&quot; That&#x27;s great to hear because we don&#x27;t know they&#x27;re using it. Then we have our customer base who subscribe to the enterprise edition. There&#x27;s a lot of them here, Disney&#x27;s presenting, Akamai&#x27;s presenting, IBM&#x27;s presenting, they&#x27;re on the enterprise side. A bunch on the community side like Capital One that&#x27;s got some GitLab, there are quite a few here.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;Matt:&lt;&#x2F;strong&gt; Those are some great names.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;strong&gt;Haydn:&lt;&#x2F;strong&gt; Yeah and one message that has become clear this week is that everyone is really embracing open source. It made sense that the tools that developers use, git obviously being open source, but that the platform on top of Git makes sense to be open source as well so they can actually contribute to the tool they use everyday.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;Matt:&lt;&#x2F;strong&gt; There&#x27;s been a lot of talk about best practices this week, it used to be &quot;emerging practices&quot; but now we&#x27;re driving towards best practices. Nearly every conversation that I&#x27;ve had this week has elicited the fact that people more and more are willing to talk about tools. Before people said, &quot;listen I want to talk about culture, not tools,&quot; but now people are talking more and more about best practices for tooling. Are you seeing that with your business as well?&lt;&#x2F;p&gt;

&lt;p&gt;&lt;strong&gt;Haydn:&lt;&#x2F;strong&gt; There&#x27;s no doubt it&#x27;s a competitive advantage. With the advent of continuous integration, and tying that to a development platform, and now continuous delivery and deployment, it&#x27;s all becoming tightly integrated and I think there&#x27;s two ROIs for that. One is you attract the right talent. The modern developer today grew up on GitHub, there&#x27;s no doubt about it. So they go to a company and they expect to see that kind of workflow and tooling. It becomes a recruitment and retention strategy. And you can also improve the performance of your development team, getting stuff done and out the door faster, increasing your deployment velocity and things like that. Two-fold.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;Matt:&lt;&#x2F;strong&gt; I&#x27;m going to put you on the spot here, continuous people say &quot;DevOps means something different to everybody.&quot; I think continuous delivery suffers the same malady. What does continuous delivery mean to you and to GitLab?&lt;&#x2F;p&gt;

&lt;p&gt;&lt;strong&gt;Haydn:&lt;&#x2F;strong&gt; Continuous delivery to me means that you&#x27;re in a state where you can release at any given moment. It&#x27;s not so much an end result but a process. The outcome of that process is being able to ship something at any given moment, if you had to. If you wanted to ship in the next five minutes, then you could. You may not, you may decide to ship once a month like we do. We put out a major release on the 22nd of every month, that&#x27;s just our regular drum beat cadence for releasing. But, if we had to put out an emergency patch for a security hole tomorrow or in the next five minutes, we could do that. To me that&#x27;s what continuous delivery is.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;Matt:&lt;&#x2F;strong&gt; We&#x27;ve also talked about snowflakes here a lot. &quot;Everybody&#x27;s a snowflake,&quot; or &quot;people aren&#x27;t such a snowflake,&quot; others say. Definitions aside, you talk about bringing to market an integrated single platform, and it&#x27;s got various components to it. I imagine different organizations have different ideas about what they need from a platform. How has Gitlab arrived at what needs to be in the platform it&#x27;s providing and how is that changing?&lt;&#x2F;p&gt;

&lt;p&gt;&lt;strong&gt;Haydn:&lt;&#x2F;strong&gt; That&#x27;s a really good question. Our CEO has really defined our scope of what we&#x27;ll include in the platform and what we won&#x27;t go after. The core developer tools have always been version control, code review, issue tracking, and now we&#x27;re seeing continuous integration also becoming part of that dev stack, along with deployment to some degree. But things like security scanning, or scanning for vulnerabilities, is probably something that we wouldn&#x27;t say is part of our scope. We try to be really clear about what will be part of our platform and which areas we&#x27;ll look to integrate with or open up to others.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;Matt:&lt;&#x2F;strong&gt; The beauty of being open source is that you&#x27;re 100% engaged with the community, and seeing how they&#x27;re advancing their use of the tooling, must drive a lot of what you do there.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;strong&gt;Haydn:&lt;&#x2F;strong&gt; Yes and in fact GitLab CI, and the Runner used in CI, was contributed by someone in the community. It was something that developers wanted inside GitLab and they just went ahead and did it. So it&#x27;s a prime example of that.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;Matt:&lt;&#x2F;strong&gt; I&#x27;ll ask you one last question before we let you go. One of the presentations here yesterday talked about the top ten ways to fail at DevOps. One of those was too much open source, because of the notion of reliability, support, etc. Listen, we&#x27;ve been hearing this since the genesis of the open source movement. Yet at the same time, this is being exacerbated by DevOps, the cloud, there&#x27;s all sorts of confluences that continue to bring this subject to light. As a provider that is driven by an open source community, how do you encourage people to use as much of the open source tooling as possible while balancing that?&lt;&#x2F;p&gt;

&lt;p&gt;&lt;strong&gt;Haydn:&lt;&#x2F;strong&gt; Yeah, it&#x27;s always tricky. A lot of companies are embedding open source components into the produce they&#x27;re releasing, and also using open source tools as part of that development platform. What we&#x27;ve seen is they&#x27;re kind of straddling the fence of how much to expose outside, and how much to keep on-premise. We&#x27;ve always been focused on-premise. That&#x27;s how GitLab started, the CTO wanted a collaboration platform that he could put on his own servers for his consulting clients, that&#x27;s how he started. He was importing things from open source, contributing to open source projects, but keeping that delineation between the two. Having an easy way to do it is the challenge.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;Matt:&lt;&#x2F;strong&gt; It&#x27;s an interesting conversation and it probably always will be. Haydn thanks for joining us, GitLab clearly a very interesting company to watch within this space. I&#x27;m Matt Hines with DevOps TV for DevOps.com at DOES 2016, thanks for joining us.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;chat&quot;&gt;Chat:&lt;&#x2F;h2&gt;

&lt;p&gt;Tweet us &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;gitlab&quot;&gt;@GitLab&lt;&#x2F;a&gt;, check out our &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;jobs&#x2F;&quot;&gt;job openings&lt;&#x2F;a&gt;, or add your questions and suggestions to our &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;issues&quot;&gt;issue tracker&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;img src=&#x27;https:&#x2F;&#x2F;about.gitlab.com&#x2F;images&#x2F;unsplash&#x2F;party.jpg&#x27; class=&#x27;webfeedsFeaturedVisual&#x27; style=&#x27;display: none;&#x27; &#x2F;&gt;    </content>
  </entry>
  <entry>
    <title>GitLab 8.14.3, 8.13.8, and 8.12.11 Released</title>
    <link rel="alternate" href="https://about.gitlab.com/2016/12/05/cve-2016-9469/"/>
    <id>https://about.gitlab.com/2016/12/05/cve-2016-9469/</id>
    <published>2016-12-05T23:59:00+00:00</published>
    <updated>2016-12-05T23:59:00+00:00</updated>
    <author>
      <name>GitLab</name>
    </author>
    <content type="html">
&lt;p&gt;Today we are releasing versions 8.14.3, 8.13.8, and 8.12.11 for GitLab Community 
Edition (CE) and Enterprise Edition (EE).&lt;&#x2F;p&gt;

&lt;p&gt;These versions contain an important security fix for a critical 
denial-of-service and data corruption vulnerability, and we &lt;strong&gt;strongly 
recommend&lt;&#x2F;strong&gt; that all affected GitLab installations be upgraded to one of these 
versions &lt;strong&gt;immediately&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;Please read on for more details.&lt;&#x2F;p&gt;



&lt;h2 id=&quot;denial-of-service-and-data-corruption-vulnerability-in-issue-and-merge-request-trackers&quot;&gt;Denial-of-Service and Data Corruption Vulnerability in Issue and Merge Request Trackers&lt;&#x2F;h2&gt;

&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;jobertabma&quot;&gt;Jobert Abma&lt;&#x2F;a&gt; of &lt;a href=&quot;https:&#x2F;&#x2F;hackerone.com&#x2F;&quot;&gt;HackerOne&lt;&#x2F;a&gt; reported a critical vulnerability 
in the GitLab Issue and Merge Request trackers that could allow a user with
access to any project to delete all issues and merge requests from all GitLab 
projects. For GitLab instances with publicly available projects this vulnerability
could be exploited by an unauthenticated user.&lt;&#x2F;p&gt;

&lt;p&gt;This issue is the result of un-sanitized user input being passed to an internal
function that expects only trusted data. This code was introduced in GitLab 
8.13.0. Please see &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;issues&#x2F;25064&quot;&gt;the issue&lt;&#x2F;a&gt; for more details.&lt;&#x2F;p&gt;

&lt;p&gt;This issue has been assigned &lt;a href=&quot;http:&#x2F;&#x2F;cve.mitre.org&#x2F;cgi-bin&#x2F;cvename.cgi?name=CVE-2016-9469&quot;&gt;CVE-2016-9469&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;h3 id=&quot;versions-affected&quot;&gt;Versions affected&lt;&#x2F;h3&gt;

&lt;ul&gt;
  &lt;li&gt;8.14.0 through 8.14.2&lt;&#x2F;li&gt;
  &lt;li&gt;8.13.0 through 8.13.7&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;p&gt;We &lt;strong&gt;strongly recommend&lt;&#x2F;strong&gt; that all installations running a version mentioned
above be upgraded as soon as possible.&lt;&#x2F;p&gt;

&lt;h3 id=&quot;workarounds&quot;&gt;Workarounds&lt;&#x2F;h3&gt;

&lt;p&gt;If you&#x27;re unable to upgrade right away, you can secure your GitLab installation
against this vulnerability using one of the workarounds outlined below until you
have time to upgrade.&lt;&#x2F;p&gt;

&lt;p&gt;You only need to apply &lt;em&gt;one&lt;&#x2F;em&gt; of these workarounds.&lt;&#x2F;p&gt;

&lt;h3 id=&quot;securing-via-omnibus-configuration&quot;&gt;Securing via Omnibus configuration&lt;&#x2F;h3&gt;

&lt;p&gt;For Omnibus installations using the bundled Nginx web server, edit
&lt;code&gt;&#x2F;etc&#x2F;gitlab&#x2F;gitlab.rb&lt;&#x2F;code&gt; and add the following line:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;nginx&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;s1&quot;&gt;&#x27;custom_gitlab_server_config&#x27;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;if ($args ~* &#x27;state=delete|state=destroy&#x27;) { return 404; }&lt;&#x2F;span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then run &lt;code&gt;sudo gitlab-ctl reconfigure&lt;&#x2F;code&gt; for the changes to take effect.&lt;&#x2F;p&gt;

&lt;h3 id=&quot;securing-via-web-server-configuration&quot;&gt;Securing via web server configuration&lt;&#x2F;h3&gt;

&lt;p&gt;If you are using an external web server with Omnibus or have installed GitLab
from source, add the following lines to your web server&#x27;s configuration file.&lt;&#x2F;p&gt;

&lt;p&gt;For Nginx:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight nginx&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;nv&quot;&gt;$args&lt;&#x2F;span&gt; &lt;span class=&quot;p&quot;&gt;~&lt;&#x2F;span&gt;&lt;span class=&quot;sr&quot;&gt;*&lt;&#x2F;span&gt; &lt;span class=&quot;s&quot;&gt;&#x27;state=delete|state=destroy&#x27;)&lt;&#x2F;span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;&#x2F;span&gt; &lt;span class=&quot;kn&quot;&gt;return&lt;&#x2F;span&gt; &lt;span class=&quot;mi&quot;&gt;403&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;&#x2F;span&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For Apache with mod_rewrite:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight apache&quot;&gt;&lt;code&gt; &lt;span class=&quot;nc&quot;&gt;RewriteEngine&lt;&#x2F;span&gt; On
 &lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;&#x2F;span&gt; %{QUERY_STRING} ^.*(state=destroy).* [NC,OR]
 &lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;&#x2F;span&gt; %{QUERY_STRING} ^.*(state=delete).* [NC]
 &lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;&#x2F;span&gt; ^(.*)$ - [F,L]
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then reload the server for changes to take effect.&lt;&#x2F;p&gt;

&lt;h3 id=&quot;securing-via-haproxy-configuration&quot;&gt;Securing via HAProxy configuration&lt;&#x2F;h3&gt;

&lt;p&gt;Add the following lines to your configuration and restart the service:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;acl bad_filter_uri path_sub,url_dec -i state=destroy state=delete
http-request deny if bad_filter_uri
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;securing-via-patch&quot;&gt;Securing via patch&lt;&#x2F;h3&gt;

&lt;p&gt;To temporarily patch just the critical vulnerability, apply the following diff:&lt;&#x2F;p&gt;

&lt;pre class=&quot;highlight diff&quot;&gt;&lt;code&gt;&lt;span class=&quot;gh&quot;&gt;diff --git a&#x2F;app&#x2F;finders&#x2F;issuable_finder.rb b&#x2F;app&#x2F;finders&#x2F;issuable_finder.rb
index e42d5af..2c9412b 100644
&lt;&#x2F;span&gt;&lt;span class=&quot;gd&quot;&gt;--- a&#x2F;app&#x2F;finders&#x2F;issuable_finder.rb
&lt;&#x2F;span&gt;&lt;span class=&quot;gi&quot;&gt;+++ b&#x2F;app&#x2F;finders&#x2F;issuable_finder.rb
&lt;&#x2F;span&gt;&lt;span class=&quot;gu&quot;&gt;@@ -7,7 +7,7 @@
&lt;&#x2F;span&gt; #   current_user - which user use
 #   params:
 #     scope: &#x27;created-by-me&#x27; or &#x27;assigned-to-me&#x27; or &#x27;all&#x27;
&lt;span class=&quot;gd&quot;&gt;-#     state: &#x27;open&#x27; or &#x27;closed&#x27; or &#x27;all&#x27;
&lt;&#x2F;span&gt;&lt;span class=&quot;gi&quot;&gt;+#     state: &#x27;opened&#x27; or &#x27;closed&#x27; or &#x27;all&#x27;
&lt;&#x2F;span&gt; #     group_id: integer
 #     project_id: integer
 #     milestone_title: string
&lt;span class=&quot;gu&quot;&gt;@@ -183,10 +183,13 @@ class IssuableFinder
&lt;&#x2F;span&gt;   end
 
   def by_state(items)
&lt;span class=&quot;gd&quot;&gt;-    params[:state] ||= &#x27;all&#x27;
-
-    if items.respond_to?(params[:state])
-      items.public_send(params[:state])
&lt;&#x2F;span&gt;&lt;span class=&quot;gi&quot;&gt;+    case params[:state].to_s
+    when &#x27;closed&#x27;
+      items.closed
+    when &#x27;merged&#x27;
+      items.respond_to?(:merged) ? items.merged : items.closed
+    when &#x27;opened&#x27;
+      items.opened
&lt;&#x2F;span&gt;     else
       items
     end

&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;verifying-the-workaround&quot;&gt;Verifying the workaround&lt;&#x2F;h3&gt;

&lt;ol&gt;
  &lt;li&gt;Browse to a project&lt;&#x2F;li&gt;
  &lt;li&gt;Open the project&#x27;s issue tracker&lt;&#x2F;li&gt;
  &lt;li&gt;Choose the &quot;closed&quot; tab&lt;&#x2F;li&gt;
  &lt;li&gt;Adjust the &quot;state&quot; field in your browser&#x27;s address bar to &quot;deleteme&quot;&lt;&#x2F;li&gt;
  &lt;li&gt;Verify you receive a &lt;code&gt;403 Forbidden&lt;&#x2F;code&gt; error&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;

&lt;p&gt;Note: If you only applied the patch you will receive no errors here.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;git-security-patch&quot;&gt;Git Security Patch&lt;&#x2F;h2&gt;

&lt;p&gt;Omnibus packages for these versions contain a security patch for git 2.7.4 that
prevents malicious repositories from using HTTP redirects to steal or corrupt 
data. More information on this patch can be found &lt;a href=&quot;http:&#x2F;&#x2F;public-inbox.org&#x2F;git&#x2F;20161201090336.xjbb47bublfcpglo@sigill.intra.peff.net&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;other-fixes-in-8143&quot;&gt;Other fixes in 8.14.3&lt;&#x2F;h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Revert signin tab order fix. (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7538&quot;&gt;!7538&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Allow dots in group names to pass validation for Create Group and Edit Group forms (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7723&quot;&gt;!7723&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Pass commit data to ProcessCommitWorker (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7744&quot;&gt;!7744&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Resolve &quot;Merge request dashboard page takes over a minute to load&quot; (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7760&quot;&gt;!7760&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Fix GitHub importer to import PR where source repo&#x2F;fork was renamed&#x2F;deleted (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7865&quot;&gt;!7865&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Fix URL rewritting in the Help section (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7875&quot;&gt;!7875&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Fixes ActionView::Template::Error: undefined method &lt;code&gt;text?&lt;&#x2F;code&gt; for nil:NilClass (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7893&quot;&gt;!7893&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
  &lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;EE:&lt;&#x2F;strong&gt; Save some queries on issuable dashboard. (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ee&#x2F;merge_requests&#x2F;935&quot;&gt;!935&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;EE:&lt;&#x2F;strong&gt; Expose add-ons associated to the license in &#x2F;license endpoint. (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ee&#x2F;merge_requests&#x2F;907&quot;&gt;!907&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h2 id=&quot;upgrade-barometer&quot;&gt;Upgrade barometer&lt;&#x2F;h2&gt;

&lt;p&gt;These versions do include a single migration, and will require brief
downtime of typically less than one minute.&lt;&#x2F;p&gt;

&lt;p&gt;Please be aware that by default the Omnibus packages will stop, run migrations,
and start again, no matter how “big” or “small” the upgrade is. This behavior
can be changed by adding a &lt;a href=&quot;http:&#x2F;&#x2F;doc.gitlab.com&#x2F;omnibus&#x2F;update&#x2F;README.html&quot;&gt;&lt;code&gt;&#x2F;etc&#x2F;gitlab&#x2F;skip-auto-migrations&lt;&#x2F;code&gt;
file&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;updating&quot;&gt;Updating&lt;&#x2F;h2&gt;

&lt;p&gt;To update, check out our &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;update&quot;&gt;update page&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;enterprise-edition&quot;&gt;Enterprise Edition&lt;&#x2F;h2&gt;

&lt;p&gt;Interested in GitLab Enterprise Edition? Check out the &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;features&#x2F;#enterprise&quot;&gt;features exclusive to
EE&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;Access to GitLab Enterprise Edition is included with a
&lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;pricing&#x2F;&quot;&gt;subscription&lt;&#x2F;a&gt;. No time to upgrade GitLab
yourself? Subscribers receive upgrade and installation services.&lt;&#x2F;p&gt;

&lt;img src=&#x27;https:&#x2F;&#x2F;about.gitlab.com&#x2F;images&#x2F;default-blog-image.png&#x27; class=&#x27;webfeedsFeaturedVisual&#x27; style=&#x27;display: none;&#x27; &#x2F;&gt;    </content>
  </entry>
  <entry>
    <title>Why Vaadin chose GitLab</title>
    <link rel="alternate" href="https://about.gitlab.com/2016/12/05/why-vaadin-chose-gitlab/"/>
    <id>https://about.gitlab.com/2016/12/05/why-vaadin-chose-gitlab/</id>
    <published>2016-12-05T00:00:00+00:00</published>
    <updated>2016-12-05T00:00:00+00:00</updated>
    <author>
      <name>Emily von Hoffmann</name>
    </author>
    <content type="html">
&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https:&#x2F;&#x2F;vaadin.com&quot;&gt;Vaadin&lt;&#x2F;a&gt; is an open source UI product development company that builds components and tools for developers to create beautiful web applications. The company has more than 170 employees, based in Finland, Helsinki, Berlin, and San Jose. Today more than 150,000 developers are active users of Vaadin.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;



&lt;p&gt;Vaadin wanted a tool to centralize their internal projects, while maintaining the commitment to simple UI&#x2F;UX that they prioritize in their own product. Although they had developed a workflow using &lt;a href=&quot;http:&#x2F;&#x2F;gitolite.com&#x2F;gitolite&#x2F;index.html&quot;&gt;Gitolite&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;trac.edgewall.org&#x2F;&quot;&gt;Trac&lt;&#x2F;a&gt;, and several other platforms, the administrative overhead began costing developers and admins valuable time. After following GitLab since its earliest iterations, Vaadin’s IT manager, Mikael Vappula, decided that GitLab was the right platform for them. We sat down with Mikael to learn more.&lt;&#x2F;p&gt;

&lt;p&gt;Here are some tips from Mikael&#x27;s experience:&lt;&#x2F;p&gt;
&lt;ul&gt;
  &lt;li&gt;Centralizing projects reduces the overhead of both IT admins and developers.&lt;&#x2F;li&gt;
  &lt;li&gt;Issue trackers and code review make a potent combination, especially when you can easily cross-link relevant issues and MRs.&lt;&#x2F;li&gt;
  &lt;li&gt;Make the best tools available to your team, but let them decide what to use.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;What were you using for your previous VCS?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;

&lt;p&gt;Before GitLab, we used Gitolite for git repository management, Trac for issue tracking, and a variety of other platforms. We had a range of tools with separate instances provisioned for specific purposes within each project.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;What were the challenges with it? Was there a specific moment when you knew it was time to try something else?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;

&lt;p&gt;As the team grew, the administrative overhead grew with it. For me, the work of tracking which repos individual projects were in, or who had access to what, was starting to burden the team. The development team had problems finding relevant projects, linking to the right issue tracker, and managing the needed credentials. They lost valuable time searching for the right version control system and the correct set of credentials for it.&lt;&#x2F;p&gt;

&lt;p&gt;As the IT Manager, I had to set up new repos and credentials for the growing number of projects, tools, and employees. In doing so, I noticed that our tooling was leading developers to use their time inefficiently and become frustrated. Like most companies, our engineering teams play a critical role in the success of our business, and we couldn’t afford the loss of developers’ time. It was clear the team needed one platform where they could pull everything together under a single entry point.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;Once you decided on us, how did you find the migration process?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;

&lt;p&gt;The prospect of a migration is intimidating, but &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;omnibus-gitlab&quot;&gt;Omnibus&lt;&#x2F;a&gt; made the installation and migration process easy, and we began using it quickly. I ran the setup procedure to get the new system provisioned on a new virtual machine in Vaadin&#x27;s on-premise servers. I was impressed by the Omnibus distribution, and once I set up the basic settings, it was pretty much self-service. The only thing we had to pay a little more attention to was how to integrate GitLab into our authentication system (powered by CAS).&lt;&#x2F;p&gt;

&lt;p&gt;I&#x27;ve also been impressed in terms of maintenance and updates. Rather than a multitude of servers and configuration files, the only resources needed for the 200 internal projects that Vaadin runs using GitLab are just one server and one configuration file. You know there’s always that software where you try to postpone updates, but with GitLab it’s pretty safe to take the latest version. We’ve done the updates 20 times now.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;How have developers on your team responded to the switch? How did you announce the change?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;

&lt;p&gt;We really emphasize open-mindedness and choice, so I couldn’t mandate a tool switch. I&#x27;ve heard from team members who appreciate that our culture allows people to choose their own hardware and discuss challenges openly. So after I set up GitLab I wrote a post to all of our employees. I tried to just explain what GitLab is, what different people should use it for, and generally how GitLab as a tool fits with our culture. I was pleased to see that after they heard GitLab was available to them, people generally adopted it quickly.&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;Can you share any details about how using GitLab has improved your team&#x27;s performance or general experience?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;

&lt;p&gt;It made a big difference for us to have a central point for software projects that we could manage by ourselves and have all the tooling in one place. I think using GitLab has also led to improvements in our workflow and collaboration. Using cross-linking, you can link to issues and merges, the mark-up system is great, and you can select a project’s activity to see all the members in one place. Integration, workflow, and communication have all been vastly improved.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;em&gt;If your team uses GitLab and is interested in sharing your story, please fill out this &lt;a href=&quot;https:&#x2F;&#x2F;docs.google.com&#x2F;a&#x2F;gitlab.com&#x2F;forms&#x2F;d&#x2F;1K8ZTS1QvSSPos6mVh1ol8ZyagInYctX3fb9eglzeK70&#x2F;edit&quot;&gt;form&lt;&#x2F;a&gt; and we’ll get in touch!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;

&lt;p&gt;&lt;em&gt;Tweet us &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;gitlab&quot;&gt;@GitLab&lt;&#x2F;a&gt;, and check out our &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;jobs&#x2F;&quot;&gt;job openings&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;

&lt;p&gt;&lt;em&gt;Follow &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;vaadin?lang=en&quot;&gt;@Vaadin&lt;&#x2F;a&gt; on Twitter&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;

&lt;img src=&#x27;https:&#x2F;&#x2F;about.gitlab.com&#x2F;images&#x2F;blogimages&#x2F;why-vaadin-chose-gitlab-cover.png&#x27; class=&#x27;webfeedsFeaturedVisual&#x27; style=&#x27;display: none;&#x27; &#x2F;&gt;    </content>
  </entry>
  <entry>
    <title>Building GitLab Recap &amp; Recording</title>
    <link rel="alternate" href="https://about.gitlab.com/2016/12/05/building-gitlab-office-hours/"/>
    <id>https://about.gitlab.com/2016/12/05/building-gitlab-office-hours/</id>
    <published>2016-12-05T00:00:00+00:00</published>
    <updated>2016-12-05T00:00:00+00:00</updated>
    <author>
      <name>Erica Lindberg</name>
    </author>
    <content type="html">

&lt;p&gt;Following the release of &lt;a href=&quot;&#x2F;2016&#x2F;11&#x2F;22&#x2F;gitlab-8-14-released&#x2F;&quot;&gt;GitLab 8.14&lt;&#x2F;a&gt;, VP of Product &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;Jobvo&quot;&gt;Job van der Voort&lt;&#x2F;a&gt;
hosted a YouTube live stream to discuss how our team is working to realize GitLab&#x27;s vision and
some of the awesome features we just released. Check out the video and highlights below.&lt;&#x2F;p&gt;

&lt;figure class=&quot;video_container&quot;&gt;
  &lt;iframe src=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;embed&#x2F;njP8Wvp45o0&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;&#x2F;figure&gt;

&lt;h2 id=&quot;highlights&quot;&gt;Highlights&lt;&#x2F;h2&gt;

&lt;h3 id=&quot;107-gitlabs-vision&quot;&gt;1:07 GitLab&#x27;s Vision&lt;&#x2F;h3&gt;

&lt;p&gt;&quot;When we &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;2016&#x2F;09&#x2F;13&#x2F;gitlab-master-plan&#x2F;&quot;&gt;announced our series B round in Septmeber&lt;&#x2F;a&gt;, we said, &quot;Ok, what we&#x27;re going to do is we&#x27;re going to try
and ship the whole &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;university&#x2F;training&#x2F;topics&#x2F;gitlab_flow.html&quot;&gt;GitLab flow&lt;&#x2F;a&gt;, going from idea all the way to production and we&#x27;re going to ship it by the end of the year.&quot;&lt;&#x2F;p&gt;

&lt;h3 id=&quot;242-what-is-review-apps&quot;&gt;2:42 What is Review Apps?&lt;&#x2F;h3&gt;

&lt;p&gt;&quot;When you do a merge request, let&#x27;s say you have some change you want to make on the website.
In the past, you would create a merge request and then somebody would check out the code; maybe
you have &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;gitlab-ci&#x2F;&quot;&gt;continuous integration&lt;&#x2F;a&gt; running automatically to see if those tests past.Now, if
you wanted to actually &lt;em&gt;see&lt;&#x2F;em&gt; the changes, and play around with them, you would still have to
checkout the branch for the merge request locally and run it to your local development environment.&lt;&#x2F;p&gt;

&lt;p&gt;With &lt;a href=&quot;&#x2F;2016&#x2F;11&#x2F;22&#x2F;introducing-review-apps&#x2F;&quot;&gt;Review Apps&lt;&#x2F;a&gt;, what happens is that rather than you having to do anything,
on every single merge request we automatically create a new envionment. In that environment,
we run a full, live environment.&quot;&lt;&#x2F;p&gt;

&lt;h3 id=&quot;457-time-tracking-beta-released-for-gitlab-enterprise-edition&quot;&gt;4:57 Time Tracking Beta released for GitLab Enterprise Edition&lt;&#x2F;h3&gt;

&lt;p&gt;&quot;What you can do with GitLab 8.14 Enterprise Edition is you can set - for each issue
that you&#x27;re working on - an estimate and you can set how you are spent your time.
You don&#x27;t even need to do this all at once. You can just set an estimate at the beginning,
and as you spend time, you can just type a comment with a slash command for time spent.&quot;&lt;&#x2F;p&gt;

&lt;h3 id=&quot;704-mattmost-chat-commands&quot;&gt;7:04 Mattmost Chat Commands&lt;&#x2F;h3&gt;

&lt;p&gt;&quot;As the whole world started hanging out in chat, we wanted to bridge the gap
from chat to GitLab. In GitLab 8.14, for the first time we released chat slash
commands.&quot;&lt;&#x2F;p&gt;

&lt;h3 id=&quot;850-but-wait-theres-more&quot;&gt;8:50 But wait, there&#x27;s more&lt;&#x2F;h3&gt;

&lt;p&gt;&quot;There&#x27;s a few small feautures that we added that I think are extremely cool.&quot;
(Seriously, go find out!)&lt;&#x2F;p&gt;

&lt;h3 id=&quot;1023-ask-me-anything&quot;&gt;10:23 Ask me anything&lt;&#x2F;h3&gt;

&lt;p&gt;10:38 Is there a mobile handbook of GitLab for the Kindle?&lt;&#x2F;p&gt;

&lt;p&gt;11:02 What are your thoughts on the short term possiblities of NLP, chat,
Conversational Development, and local development data sets?&lt;&#x2F;p&gt;

&lt;p&gt;12:38 How do you decide what to work on each release?&lt;&#x2F;p&gt;

&lt;p&gt;15:29 Feature proposal to improve labels and issues&lt;&#x2F;p&gt;

&lt;p&gt;18:40 What kind of data do you receive from on-premise GitLab instances? What kind of
data insights can you obtain from this group of users?&lt;&#x2F;p&gt;

&lt;p&gt;21:22 Are there any downsides of only planning a few months ahead?&lt;&#x2F;p&gt;

&lt;h2 id=&quot;upcoming-live-streams&quot;&gt;Upcoming Live Streams&lt;&#x2F;h2&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blogimages&#x2F;facebook-inside-gitlab-webcast-ad.png&quot; alt=&quot;Inside GitLab&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;h2 id=&quot;1-why-we-chose-vuejs&quot;&gt;1. Why We Chose Vue.js&lt;&#x2F;h2&gt;

&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=ioogrvs2Ejc&quot;&gt;Watch the recording&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;

&lt;p&gt;A couple of months ago, &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;jakecodes&quot;&gt;Jacob Schatz&lt;&#x2F;a&gt;, Front End Lead at GitLab, published a post
detailing &lt;a href=&quot;&#x2F;2016&#x2F;10&#x2F;20&#x2F;why-we-chose-vue&#x2F;&quot;&gt;why we chose Vue.js&lt;&#x2F;a&gt; as our JavaScript framework. Since,
this post has spurred tons of conversation on the topic. To faciliate the conversation
further, GitLab Front End engineer Phil Hughes presented a front end update
and host a Q&amp;amp;A session along with Jacob.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;2-monitoring-distributed-systems-with-prometheus&quot;&gt;2. Monitoring Distributed Systems with Prometheus&lt;&#x2F;h2&gt;

&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=WzAzm0C15W8&quot;&gt;Watch live&lt;&#x2F;a&gt; on &lt;strong&gt;December 14 at 9am PT&#x2F;5pm GMT.&lt;&#x2F;strong&gt; 
&lt;a href=&quot;https:&#x2F;&#x2F;page.gitlab.com&#x2F;20161207_PrometheusWebcast_LandingPage.html&quot;&gt;Sign up to receive a reminder and the recording.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;

&lt;p&gt;Infrastructure Lead &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;psczg&quot;&gt;Pablo Carranza&lt;&#x2F;a&gt; will give a behind-the-scenes
look at GitLab&#x27;s Prometheus set up, explain how we plan to ship Prometheus with
GitLab CE, and give a tutorial on how you can set up your own dashboard.
A live chat Q&amp;amp;A will follow the presentation. See more details and sign up for
a reminder &lt;a href=&quot;https:&#x2F;&#x2F;page.gitlab.com&#x2F;20161207_PrometheusWebcast_LandingPage.html&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;For more information on the topic, read Pablo&#x27;s blog post on &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;2016&#x2F;11&#x2F;10&#x2F;why-choose-bare-metal&#x2F;&quot;&gt;how we knew it was time to leave the cloud&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;3-designing-gitlabs-user-experience-with-ux-lead-allison-whilden&quot;&gt;3. Designing GitLab&#x27;s User Experience with UX Lead Allison Whilden&lt;&#x2F;h2&gt;

&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=Lxy1jET5pww&quot;&gt;Watch live&lt;&#x2F;a&gt; on &lt;strong&gt;December 15 at 10am PT&#x2F;6pm GMT.&lt;&#x2F;strong&gt; &lt;a href=&quot;https:&#x2F;&#x2F;page.gitlab.com&#x2F;UXLiveStream_LandingPage.html&quot;&gt;Sign up to receive
a reminder and the recording.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;

&lt;p&gt;User experience (UX) affects every interaction a user has with a product. Because
of this, it can literally make or break the adoption of a website, or application.
UX designers have a really big job to do as they dig into the who, the what,
the why, and the how of essentially everything that happens within a platform.&lt;&#x2F;p&gt;

&lt;p&gt;How does GitLab&#x27;s UX team work to solve the challenge of creating an application
that has so many different types of users and releases a new version of its
product every month? Join GitLab&#x27;s UX Lead, Allison Whilden, and her team, as
they discuss their process, the big challenges they face and how they look to solve them.&lt;&#x2F;p&gt;

&lt;!-- identifiers --&gt;

&lt;img src=&#x27;https:&#x2F;&#x2F;about.gitlab.com&#x2F;images&#x2F;blogimages&#x2F;facebook-open-office-hours-webcast.png&#x27; class=&#x27;webfeedsFeaturedVisual&#x27; style=&#x27;display: none;&#x27; &#x2F;&gt;    </content>
  </entry>
  <entry>
    <title>How we stay connected as a remote company</title>
    <link rel="alternate" href="https://about.gitlab.com/2016/12/05/how-we-stay-connected-as-a-remote-company/"/>
    <id>https://about.gitlab.com/2016/12/05/how-we-stay-connected-as-a-remote-company/</id>
    <published>2016-12-05T00:00:00+00:00</published>
    <updated>2016-12-05T00:00:00+00:00</updated>
    <author>
      <name>Rebecca Dodd</name>
    </author>
    <content type="html">
&lt;p&gt;GitLab is a remote-only company, which means we have the freedom to live where we choose and work from anywhere. This flexibility is one of the reasons why it’s great to work at GitLab, but working remotely can be isolating. So how do we build a company culture when we’re all so scattered?&lt;&#x2F;p&gt;



&lt;h2 id=&quot;team-calls&quot;&gt;Team calls&lt;&#x2F;h2&gt;

&lt;p&gt;Almost every day we have a &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;handbook&#x2F;#team-call&quot;&gt;team call&lt;&#x2F;a&gt;, not just for company updates but to take it in turns filling in the rest of the team about what we’ve been up to that weekend. I’ve been given a tour of team members’ new houses, admired their Christmas decorations, squealed when their pets and kids make an appearance and watched them preparing dinner – glimpses into the personal lives of my colleagues that I’ve never had in any office job. I’ve worked at companies where I didn’t know the names of anyone who worked on a different floor. &quot;There are no floors at GitLab,&quot; Douwe said to me on a call, and it&#x27;s true both literally and metaphorically. Because you’ve chatted with everyone on team calls, everyone feels approachable.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;coffee-break-calls&quot;&gt;Coffee break calls&lt;&#x2F;h2&gt;

&lt;p&gt;We also have regular one-on-one chats that can be about anything (as long as it’s not work!). Acknowledging that we don’t have a water cooler around which to gather, we make time for shooting the breeze online instead. And it’s kind of cool to have my &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;handbook&#x2F;#coffee-break-calls&quot;&gt;coffee break&lt;&#x2F;a&gt; with someone on the other side of the world, in an entirely different time zone or season.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;summits&quot;&gt;Summits&lt;&#x2F;h2&gt;

&lt;p&gt;Of course, nothing beats getting to know your team in person, which is why we get together every nine months for the &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;culture&#x2F;&quot;&gt;GitLab summit&lt;&#x2F;a&gt;. So far we’ve met up in Amsterdam and Austin, and in January we’ll be heading to sunny Cancun for a week to work and play together.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blogimages&#x2F;Gitlab-summit-Austin.jpeg&quot; alt=&quot;GitLab Austin summit&quot; &#x2F;&gt;&lt;em&gt;The GitLab summit in Austin, May 2016&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;

&lt;h2 id=&quot;secret-santa&quot;&gt;Secret Santa&lt;&#x2F;h2&gt;

&lt;p&gt;Because holiday cheer knows no borders, this year for the first time we’re doing a company-wide &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;handbook&#x2F;miscellaneous&#x2F;&quot;&gt;Secret Santa&lt;&#x2F;a&gt;. Because it’s a bit much to expect Santa to deliver to 33 different countries before he even starts his Christmas rounds, we’ll be shipping to our recipients directly or exchanging gifts at the Cancun Summit. Watch this space for the highlights!&lt;&#x2F;p&gt;

&lt;h2 id=&quot;meet-ups&quot;&gt;Meet-ups&lt;&#x2F;h2&gt;

&lt;p&gt;Working remotely means you can take your office anywhere – and that’s exactly what some of the team has done. Douwe and Robert are currently on an epic 6-month World Tour, working from 13 different countries and meeting up with other team members in Mexico City, Edinburgh, Warsaw, Tel Aviv and more. Working together even for just short bursts builds our relationships so that even when we’re working independently, we feel more connected and in tune with each other. Douwe and Robert’s adventure has even inspired others to travel while working, joining team members along the way.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blogimages&#x2F;gitlab-edinburgh.jpg&quot; alt=&quot;GitLab Edinburgh&quot; &#x2F;&gt;&lt;em&gt;The unofficial GitLab Edinburgh office&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;

&lt;p&gt;Like the sound of how we work together at GitLab? Come work with us! Check out our &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;jobs&#x2F;&quot;&gt;job openings&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;img src=&#x27;https:&#x2F;&#x2F;about.gitlab.com&#x2F;images&#x2F;blogimages&#x2F;how-we-stay-connected-as-a-remote-company-globe.jpg&#x27; class=&#x27;webfeedsFeaturedVisual&#x27; style=&#x27;display: none;&#x27; &#x2F;&gt;    </content>
  </entry>
  <entry>
    <title>How to Keep your Fork Up-To-Date with its Origin</title>
    <link rel="alternate" href="https://about.gitlab.com/2016/12/01/how-to-keep-your-fork-up-to-date-with-its-origin/"/>
    <id>https://about.gitlab.com/2016/12/01/how-to-keep-your-fork-up-to-date-with-its-origin/</id>
    <published>2016-12-01T00:00:00+00:00</published>
    <updated>2016-12-01T00:00:00+00:00</updated>
    <author>
      <name>Marcia Ramos</name>
    </author>
    <content type="html">
&lt;p&gt;Have you ever wondered how to keep a fork automatically up-to-date with its origin (&lt;strong&gt;upstream&lt;&#x2F;strong&gt;)?&lt;&#x2F;p&gt;

&lt;p&gt;In this post we&#x27;ll show you how to do that on GitLab!&lt;&#x2F;p&gt;



&lt;h2 id=&quot;fork&quot;&gt;Fork&lt;&#x2F;h2&gt;

&lt;p&gt;As you most likely know, a &lt;strong&gt;fork&lt;&#x2F;strong&gt; is a copy of a Git repository &quot;connected&quot; with the project you forked from (upstream). When you collaborate on code, it&#x27;s pretty common forking a project, cloning to your local machine, making the changes you&#x27;re up to, pushing to your fork, and submitting a merge request (MR) to merge your code into the original project.&lt;&#x2F;p&gt;

&lt;p&gt;You fork a repository whenever you want to contribute to a project which you don&#x27;t have access to, as it&#x27;s not your own or your team&#x27;s. This is how open source projects hosted by GitLab get so much collaboration from the community.&lt;&#x2F;p&gt;

&lt;p&gt;When you are a member of a project (or a group), it&#x27;s easier using the &lt;a href=&quot;&#x2F;2014&#x2F;09&#x2F;29&#x2F;gitlab-flow&#x2F;&quot;&gt;GitLab Flow&lt;&#x2F;a&gt;, or &lt;em&gt;branching strategy&lt;&#x2F;em&gt;, since anyone in your team can pull your branch and preview your changes locally. By the way, with our brand-new solution for this, called &lt;a href=&quot;&#x2F;features&#x2F;review-apps&quot;&gt;Review Apps&lt;&#x2F;a&gt;, you can preview your apps in a per-branch basis, for each MR submitted to GitLab, directly from a link generated by GitLab. Review Apps came to bring the &lt;a href=&quot;&#x2F;2016&#x2F;10&#x2F;25&#x2F;gitlab-workflow-an-overview&#x2F;&quot;&gt;GitLab Workflow&lt;&#x2F;a&gt; up to the next level! 🙌&lt;&#x2F;p&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;&#x2F;h2&gt;

&lt;p&gt;When you fork a project and clone it to your local computer, you&#x27;ll eventually need to update it with the &lt;code&gt;master&lt;&#x2F;code&gt; branch of the upstream project (or whatever the default branch is set to). That way you will be able to checkout an up-to-date branch to work from. If you do not do that, you are very likely to stumble upon merge conflicts in the end, since the copy of the code you&#x27;re working could be out-of-date.&lt;&#x2F;p&gt;

&lt;p&gt;To prevent this, first you&#x27;d need to pull the upstream, push to the remote, checkout a new branch from the &lt;code&gt;master&lt;&#x2F;code&gt; branch up-to-date, then finally start working on your changes. This takes time, and if you forget to do it, can cause a lot of pain. This process takes some time to complete, and turns out to be annoying if you need to do this multiple times a day. That&#x27;s why we could use a better solution.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;the-solution&quot;&gt;The Solution&lt;&#x2F;h2&gt;

&lt;p&gt;GitLab can do that for you with no pain! Yay! What you need to do is very simple: enable &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ee&#x2F;workflow&#x2F;repository_mirroring.html&quot;&gt;GitLab Repository Mirroring&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;

&lt;p&gt;&lt;i class=&quot;fa fa-arrow-circle-o-right gitlab-purple&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;i&gt;
&lt;strong&gt;First&lt;&#x2F;strong&gt;. Mirror your fork:&lt;&#x2F;p&gt;

&lt;p&gt;Under your forked project&#x27;s &lt;strong&gt;Settings&lt;&#x2F;strong&gt;, navigate to &lt;strong&gt;Mirror Repository&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blogimages&#x2F;how-to-keep-your-fork-up-to-date-with-its-origin&#x2F;mirror-repository-settings.png&quot; alt=&quot;settings - mirror repository&quot; class=&quot;shadow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;p&gt;&lt;i class=&quot;fa fa-arrow-circle-o-right gitlab-purple&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;i&gt;
&lt;strong&gt;Second&lt;&#x2F;strong&gt;. Add the upstream&#x27;s path to the field &lt;strong&gt;Git repository URL&lt;&#x2F;strong&gt;, then enable automatic mirroring:&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blogimages&#x2F;how-to-keep-your-fork-up-to-date-with-its-origin&#x2F;setup-automatic-mirror.png&quot; alt=&quot;fork - enable automatic repository update&quot; class=&quot;shadow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;p&gt;&lt;i class=&quot;fa fa-arrow-circle-o-right gitlab-purple&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;i&gt;
&lt;strong&gt;Third&lt;&#x2F;strong&gt;. Setup the &lt;strong&gt;mirror user&lt;&#x2F;strong&gt;: it&#x27;s the user whose new commits to the upstream project will be attributed to in your fork.&lt;&#x2F;p&gt;

&lt;p&gt;&lt;i class=&quot;fa fa-check-circle-o&quot; aria-hidden=&quot;true&quot; style=&quot;color: green&quot;&gt;&lt;&#x2F;i&gt;
&lt;strong&gt;Done&lt;&#x2F;strong&gt;! Once an hour, GitLab will pull the upstream for you, and keep your fork up-to-date!&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blogimages&#x2F;how-to-keep-your-fork-up-to-date-with-its-origin&#x2F;repository-mirrored.png&quot; alt=&quot;fork - repo successfully mirrored&quot; class=&quot;shadow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;p&gt;By doing so, you only need to proceed as you usually do for your own projects (pull, checkout a new branch, and push your commits). All the rest GitLab does for you!&lt;&#x2F;p&gt;

&lt;p&gt;Simple, isn&#x27;t it?&lt;&#x2F;p&gt;

&lt;p class=&quot;alert alert-success text-center&quot;&gt;Repository Mirroring is available for free at &lt;strong&gt;GitLab.com&lt;&#x2F;strong&gt; and to all &lt;strong&gt;GitLab Enterprise Edition&lt;&#x2F;strong&gt; users.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;the-secret&quot;&gt;The Secret&lt;&#x2F;h2&gt;

&lt;p&gt;In order to pull without authentication from an upstream repository you are contributing to, the project should be public in the first place. Also, the &lt;strong&gt;Repository&lt;&#x2F;strong&gt;&#x27;s &lt;strong&gt;Feature Visibility&lt;&#x2F;strong&gt; settings of the upstream project needs to be set to &lt;strong&gt;Everyone with access&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;

&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blogimages&#x2F;how-to-keep-your-fork-up-to-date-with-its-origin&#x2F;feature-visibility-settings.png&quot; alt=&quot;feature visibility settings&quot; class=&quot;shadow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

&lt;p&gt;You can find them in your project&#x27;s &lt;strong&gt;Settings&lt;&#x2F;strong&gt; (&lt;i class=&quot;fa fa-cog&quot; aria-hidden=&quot;true&quot; style=&quot;color: grey&quot;&gt;&lt;&#x2F;i&gt;) &amp;gt; &lt;strong&gt;Edit Project&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;Note that you can mirror private projects as well, but you&#x27;d need to be a member of the project or the group it belongs.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;what-is-your-solution&quot;&gt;What is Your Solution?&lt;&#x2F;h2&gt;

&lt;p&gt;We would love to know how you do that! Do you have a different solution? You can certainly help others. Please share them in the comments, so everyone from the community can decide which solution is the best for them. Thank you!&lt;&#x2F;p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;

&lt;p&gt;Hopefully, we provided you with an easy solution for keeping your fork up-to-date. Remember, you can even mirror repositories hosted in other Git platforms!&lt;&#x2F;p&gt;

&lt;p&gt;Cool! I&#x27;m looking forward to hearing from you: feedback, questions, and suggestions are very welcome! Leave your comment below, and&#x2F;or tweet at us &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;gitlab&quot;&gt;@GitLab&lt;&#x2F;a&gt;! We &lt;i class=&quot;fa fa-heart gitlab-purple&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;i&gt; our community!&lt;&#x2F;p&gt;

&lt;!-- cover image: https:&#x2F;&#x2F;unsplash.com&#x2F;photos&#x2F;8yqds_91OLw --&gt;
&lt;img src=&#x27;https:&#x2F;&#x2F;about.gitlab.com&#x2F;images&#x2F;blogimages&#x2F;how-to-keep-your-fork-up-to-date-with-its-origin&#x2F;fork.png&#x27; class=&#x27;webfeedsFeaturedVisual&#x27; style=&#x27;display: none;&#x27; &#x2F;&gt;    </content>
  </entry>
  <entry>
    <title>GitLab 8.14.2 released</title>
    <link rel="alternate" href="https://about.gitlab.com/2016/12/01/gitlab-8-dot-14-dot-2-released/"/>
    <id>https://about.gitlab.com/2016/12/01/gitlab-8-dot-14-dot-2-released/</id>
    <published>2016-12-01T00:00:00+00:00</published>
    <updated>2016-12-01T00:00:00+00:00</updated>
    <author>
      <name>Alejandro Rodríguez</name>
    </author>
    <content type="html">
&lt;p&gt;Today we are releasing version 8.14.2 for GitLab Community Edition (CE) and
Enterprise Edition (EE).&lt;&#x2F;p&gt;

&lt;p&gt;This version resolves a number of regressions and bugs in the &lt;a href=&quot;&#x2F;2016&#x2F;11&#x2F;22&#x2F;gitlab-8-14-released&quot;&gt;recent 8.14
release&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;Please read on for more details.&lt;&#x2F;p&gt;



&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Rephrase some system notes to be compatible with new system note style (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7692&quot;&gt;!7692&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Create tag after running pre-hooks and pass updated SHA to post-hooks (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7700&quot;&gt;!7700&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Fixed commit time not rendering after initial page load (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7704&quot;&gt;!7704&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Prevent error when submitting a merge request and pipeline is not defined (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7707&quot;&gt;!7707&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Fix: Timeout creating and viewing merge request for binary file (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7713&quot;&gt;!7713&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; New system note design for commit discussion (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7721&quot;&gt;!7721&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Refresh project authorizations using a Redis lease (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7733&quot;&gt;!7733&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Fixed issue boards issue sorting when dragging issue into list (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7734&quot;&gt;!7734&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Fix for builds with no start date throwing an error in cycle analytics events (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7738&quot;&gt;!7738&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Clean up JiraService (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7756&quot;&gt;!7756&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Update GitLab Workhorse to v1.0.1 (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7759&quot;&gt;!7759&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Fix pipelines info being hidden in merge request widget (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7808&quot;&gt;!7808&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Update Sidekiq-cron to fix compatibility issues with Sidekiq 4.2.1 (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7815&quot;&gt;!7815&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Fix a transient spec failure (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7825&quot;&gt;!7825&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Fixes access to the wiki code with git when repository feature disabled (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7832&quot;&gt;!7832&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Revert bump in rufus-scheduler (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7844&quot;&gt;!7844&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Make deleting with optimistic locking respect NULL (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7867&quot;&gt;!7867&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Remove caching of events data (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;6578&quot;&gt;!6578&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Fix broken external links in help&#x2F;index.html (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7582&quot;&gt;!7582&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Evalute time_ago method instead of printing it (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7634&quot;&gt;!7634&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Resolves updated and resolved status is not showing (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7655&quot;&gt;!7655&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Fixed resolved discussion timeago not rendering (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7656&quot;&gt;!7656&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;CE&#x2F;EE:&lt;&#x2F;strong&gt; Pick valid event objects for the events list (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ce&#x2F;merge_requests&#x2F;7689&quot;&gt;!7689&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
  &lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;EE:&lt;&#x2F;strong&gt; Port of rephrase-system-notes to EE (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ee&#x2F;merge_requests&#x2F;913&quot;&gt;!913&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;EE:&lt;&#x2F;strong&gt; Get rid of user activites table and replace it with redis (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ee&#x2F;merge_requests&#x2F;915&quot;&gt;!915&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;EE:&lt;&#x2F;strong&gt; Geo: Display Custom Avatars in secondary nodes (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;gitlab-ee&#x2F;merge_requests&#x2F;904&quot;&gt;!904&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
  &lt;&#x2F;li&gt;
  &lt;li&gt;&lt;strong&gt;Omnibus GitLab:&lt;&#x2F;strong&gt; Revert default IPv6 configuration for NGINX (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;gitlab-org&#x2F;omnibus-gitlab&#x2F;merge_requests&#x2F;1133&quot;&gt;!1133&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h2 id=&quot;upgrade-barometer&quot;&gt;Upgrade barometer&lt;&#x2F;h2&gt;

&lt;p&gt;This version has no migrations and should not require any downtime.&lt;&#x2F;p&gt;

&lt;p&gt;Please be aware that by default the Omnibus packages will stop, run migrations,
and start again, no matter how “big” or “small” the upgrade is. This behavior
can be changed by adding a &lt;a href=&quot;http:&#x2F;&#x2F;doc.gitlab.com&#x2F;omnibus&#x2F;update&#x2F;README.html&quot;&gt;&lt;code&gt;&#x2F;etc&#x2F;gitlab&#x2F;skip-auto-migrations&lt;&#x2F;code&gt;
file&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;updating&quot;&gt;Updating&lt;&#x2F;h2&gt;

&lt;p&gt;To update, check out our &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;update&#x2F;&quot;&gt;update page&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;h2 id=&quot;enterprise-edition&quot;&gt;Enterprise Edition&lt;&#x2F;h2&gt;

&lt;p&gt;Interested in GitLab Enterprise Edition? Check out the &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;features&#x2F;#enterprise&quot;&gt;features exclusive to
EE&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;Access to GitLab Enterprise Edition is included with a &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;pricing&#x2F;&quot;&gt;subscription&lt;&#x2F;a&gt;.
No time to upgrade GitLab yourself? Subscribers receive upgrade and installation
services.&lt;&#x2F;p&gt;
&lt;img src=&#x27;https:&#x2F;&#x2F;about.gitlab.com&#x2F;images&#x2F;default-blog-image.png&#x27; class=&#x27;webfeedsFeaturedVisual&#x27; style=&#x27;display: none;&#x27; &#x2F;&gt;    </content>
  </entry>
</feed>
