getting_started_part_four.md 11.3 KB
Newer Older
1
---
2
last_updated: 2020-01-06
Marcia Ramos's avatar
Marcia Ramos committed
3
type: reference, howto
4
---
5

6
# Creating and Tweaking GitLab CI/CD for GitLab Pages
7

Marcia Ramos's avatar
Marcia Ramos committed
8 9 10 11 12 13 14 15 16 17 18 19 20
To [get started with GitLab Pages](index.md#getting-started), you can
use one of the project templates, a `.gitlab-ci.yml` template,
or fork an existing example project. Therefore, you don't need to
understand _all_ the ins and odds of GitLab CI/CD to get your site
deployed. Still, there are cases where you want to write your own
script or tweak an existing one. This document guides you through
this process.

This guide also provides a general overview and clear introduction
for **getting familiar with the `.gitlab-ci.yml` file and writing
one for the first time.**

[GitLab CI/CD](../../../ci/README.md) serves
21 22
numerous purposes, to build, test, and deploy your app
from GitLab through
23
[Continuous Integration, Continuous Delivery, and Continuous Deployment](../../../ci/introduction/index.md#introduction-to-cicd-methodologies)
24 25 26
methods. You will need it to build your website with GitLab Pages,
and deploy it to the Pages server.

Marcia Ramos's avatar
Marcia Ramos committed
27
To implement GitLab CI/CD, the first thing you need is a configuration
28 29
file called `.gitlab-ci.yml` placed at your website's root directory.

30 31 32
What this file actually does is telling the
[GitLab Runner](https://docs.gitlab.com/runner/) to run scripts
as you would do from the command line. The Runner acts as your
33
terminal. GitLab CI/CD tells the Runner which commands to run.
34 35 36
Both are built-in in GitLab, and you don't need to set up
anything for them to work.

Marcia Ramos's avatar
Marcia Ramos committed
37
Explaining [every detail of GitLab CI/CD](../../../ci/yaml/README.md)
38 39 40
and GitLab Runner is out of the scope of this guide, but we'll
need to understand just a few things to be able to write our own
`.gitlab-ci.yml` or tweak an existing one. It's an
41
[Yaml](https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html) file,
42
with its own syntax. You can always check your CI syntax with
Marcel Amirault's avatar
Marcel Amirault committed
43
the [GitLab CI/CD Lint Tool](https://gitlab.com/ci/lint).
44

45
## Practical example
46 47 48 49 50

Let's consider you have a [Jekyll](https://jekyllrb.com/) site.
To build it locally, you would open your terminal, and run `jekyll build`.
Of course, before building it, you had to install Jekyll in your computer.
For that, you had to open your terminal and run `gem install jekyll`.
Marcel Amirault's avatar
Marcel Amirault committed
51
Right? GitLab CI/CD + GitLab Runner do the same thing. But you need to
52
write in the `.gitlab-ci.yml` the script you want to run so
Morris-Chen's avatar
Morris-Chen committed
53
GitLab Runner will do it for you. It looks more complicated than it
54 55
is. What you need to tell the Runner:

56
```shell
57 58
gem install jekyll
jekyll build
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
```

### Script

To transpose this script to Yaml, it would be like this:

```yaml
script:
  - gem install jekyll
  - jekyll build
```

### Job

So far so good. Now, each `script`, in GitLab is organized by
a `job`, which is a bunch of scripts and settings you want to
apply to that specific task.

```yaml
job:
  script:
  - gem install jekyll
  - jekyll build
```

For GitLab Pages, this `job` has a specific name, called `pages`,
which tells the Runner you want that task to deploy your website
with GitLab Pages:

```yaml
pages:
  script:
  - gem install jekyll
  - jekyll build
```

### The `public` directory

We also need to tell Jekyll where do you want the website to build,
and GitLab Pages will only consider files in a directory called `public`.
To do that with Jekyll, we need to add a flag specifying the
[destination (`-d`)](https://jekyllrb.com/docs/usage/) of the
built website: `jekyll build -d public`. Of course, we need
to tell this to our Runner:

```yaml
pages:
  script:
  - gem install jekyll
  - jekyll build -d public
```

### Artifacts

We also need to tell the Runner that this _job_ generates
_artifacts_, which is the site built by Jekyll.
Where are these artifacts stored? In the `public` directory:

```yaml
pages:
  script:
  - gem install jekyll
  - jekyll build -d public
  artifacts:
    paths:
    - public
```

The script above would be enough to build your Jekyll
site with GitLab Pages. But, from Jekyll 3.4.0 on, its default
template originated by `jekyll new project` requires
130
[Bundler](https://bundler.io) to install Jekyll dependencies
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
and the default theme. To adjust our script to meet these new
requirements, we only need to install and build Jekyll with Bundler:

```yaml
pages:
  script:
  - bundle install
  - bundle exec jekyll build -d public
  artifacts:
    paths:
    - public
```

That's it! A `.gitlab-ci.yml` with the content above would deploy
your Jekyll 3.4.0 site with GitLab Pages. This is the minimum
configuration for our example. On the steps below, we'll refine
Marcel Amirault's avatar
Marcel Amirault committed
147
the script by adding extra options to our GitLab CI/CD.
148

149
Artifacts will be automatically deleted once GitLab Pages got deployed.
150
You can preserve artifacts for limited time by specifying the expiry time.
151

152 153 154 155 156 157 158 159 160
### Image

At this point, you probably ask yourself: "okay, but to install Jekyll
I need Ruby. Where is Ruby on that script?". The answer is simple: the
first thing GitLab Runner will look for in your `.gitlab-ci.yml` is a
[Docker](https://www.docker.com/) image specifying what do you need in
your container to run that script:

```yaml
161
image: ruby:2.7
162 163 164 165 166 167 168 169 170 171 172

pages:
  script:
  - bundle install
  - bundle exec jekyll build -d public
  artifacts:
    paths:
    - public
```

In this case, you're telling the Runner to pull this image, which
173
contains Ruby 2.7 as part of its file system. When you don't specify
174
this image in your configuration, the Runner will use a default
175
image, which is Ruby 2.6.
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200

If your SSG needs [NodeJS](https://nodejs.org/) to build, you'll
need to specify which image you want to use, and this image should
contain NodeJS as part of its file system. E.g., for a
[Hexo](https://gitlab.com/pages/hexo) site, you can use `image: node:4.2.2`.

>**Note:**
We're not trying to explain what a Docker image is,
we just need to introduce the concept with a minimum viable
explanation. To know more about Docker images, please visit
their website or take a look at a
[summarized explanation](http://paislee.io/how-to-automate-docker-deployments/) here.

Let's go a little further.

### Branching

If you use GitLab as a version control platform, you will have your
branching strategy to work on your project. Meaning, you will have
other branches in your project, but you'll want only pushes to the
default branch (usually `master`) to be deployed to your website.
To do that, we need to add another line to our CI, telling the Runner
to only perform that _job_ called `pages` on the `master` branch `only`:

```yaml
201
image: ruby:2.6
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218

pages:
  script:
  - bundle install
  - bundle exec jekyll build -d public
  artifacts:
    paths:
    - public
  only:
  - master
```

### Stages

Another interesting concept to keep in mind are build stages.
Your web app can pass through a lot of tests and other tasks
until it's deployed to staging or production environments.
Marcel Amirault's avatar
Marcel Amirault committed
219
There are three default stages on GitLab CI/CD: build, test,
220 221 222 223
and deploy. To specify which stage your _job_ is running,
simply add another line to your CI:

```yaml
224
image: ruby:2.6
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246

pages:
  stage: deploy
  script:
  - bundle install
  - bundle exec jekyll build -d public
  artifacts:
    paths:
    - public
  only:
  - master
```

You might ask yourself: "why should I bother with stages
at all?" Well, let's say you want to be able to test your
script and check the built site before deploying your site
to production. You want to run the test exactly as your
script will do when you push to `master`. It's simple,
let's add another task (_job_) to our CI, telling it to
test every push to other branches, `except` the `master` branch:

```yaml
247
image: ruby:2.6
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281

pages:
  stage: deploy
  script:
  - bundle install
  - bundle exec jekyll build -d public
  artifacts:
    paths:
    - public
  only:
  - master

test:
  stage: test
  script:
  - bundle install
  - bundle exec jekyll build -d test
  artifacts:
    paths:
    - test
  except:
  - master
```

The `test` job is running on the stage `test`, Jekyll
will build the site in a directory called `test`, and
this job will affect all the branches except `master`.

The best benefit of applying _stages_ to different
_jobs_ is that every job in the same stage builds in
parallel. So, if your web app needs more than one test
before being deployed, you can run all your test at the
same time, it's not necessary to wait one test to finish
to run the other. Of course, this is just a brief
Marcel Amirault's avatar
Marcel Amirault committed
282
introduction of GitLab CI/CD and GitLab Runner, which are
283 284 285 286 287 288 289 290 291 292 293 294 295 296
tools much more powerful than that. This is what you
need to be able to create and tweak your builds for
your GitLab Pages site.

### Before Script

To avoid running the same script multiple times across
your _jobs_, you can add the parameter `before_script`,
in which you specify which commands you want to run for
every single _job_. In our example, notice that we run
`bundle install` for both jobs, `pages` and `test`.
We don't need to repeat it:

```yaml
297
image: ruby:2.6
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331

before_script:
  - bundle install

pages:
  stage: deploy
  script:
  - bundle exec jekyll build -d public
  artifacts:
    paths:
    - public
  only:
  - master

test:
  stage: test
  script:
  - bundle exec jekyll build -d test
  artifacts:
    paths:
    - test
  except:
  - master
```

### Caching Dependencies

If you want to cache the installation files for your
projects dependencies, for building faster, you can
use the parameter `cache`. For this example, we'll
cache Jekyll dependencies in a `vendor` directory
when we run `bundle install`:

```yaml
332
image: ruby:2.6
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371

cache:
  paths:
  - vendor/

before_script:
  - bundle install --path vendor

pages:
  stage: deploy
  script:
  - bundle exec jekyll build -d public
  artifacts:
    paths:
    - public
  only:
  - master

test:
  stage: test
  script:
  - bundle exec jekyll build -d test
  artifacts:
    paths:
    - test
  except:
  - master
```

For this specific case, we need to exclude `/vendor`
from Jekyll `_config.yml` file, otherwise Jekyll will
understand it as a regular directory to build
together with the site:

```yml
exclude:
  - vendor
```

Marcel Amirault's avatar
Marcel Amirault committed
372
There we go! Now our GitLab CI/CD not only builds our website,
373 374 375 376 377 378
but also **continuously test** pushes to feature-branches,
**caches** dependencies installed with Bundler, and
**continuously deploy** every push to the `master` branch.

## Advanced GitLab CI for GitLab Pages

Marcel Amirault's avatar
Marcel Amirault committed
379
What you can do with GitLab CI/CD is pretty much up to your
380 381 382
creativity. Once you get used to it, you start creating
awesome scripts that automate most of tasks you'd do
manually in the past. Read through the
Marcel Amirault's avatar
Marcel Amirault committed
383
[documentation of GitLab CI/CD](../../../ci/yaml/README.md)
384 385 386
to understand how to go even further on your scripts.

- On this blog post, understand the concept of
Marcel Amirault's avatar
Marcel Amirault committed
387
  [using GitLab CI/CD `environments` to deploy your
388
  web app to staging and production](https://about.gitlab.com/blog/2016/08/26/ci-deployment-and-environments/).
389
- On this post, learn [how to run jobs sequentially,
390
  in parallel, or build a custom pipeline](https://about.gitlab.com/blog/2016/07/29/the-basics-of-gitlab-ci/)
391
- On this blog post, we go through the process of
392
  [pulling specific directories from different projects](https://about.gitlab.com/blog/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/)
393
  to deploy this website you're looking at, <https://docs.gitlab.com>.
394
- On this blog post, we teach you [how to use GitLab Pages to produce a code coverage report](https://about.gitlab.com/blog/2016/11/03/publish-code-coverage-report-with-gitlab-pages/).