GitLabCICD入門 ~よく使うパラメータまとめ~

今回はGitLabCIで使用されるパラメータについてまとめます。
種類が多いので、実務における使用頻度が高いもののみに絞って
詳細を整理します。


## 設定パラメータ一覧

|パラメータ|説明|よく使う|
|---------|----|-------|
|script|実行する処理|〇|
|image|使用するDockerイメージ|〇|
|services|imageと連携するDockerイメージ|-|
|before_script|scriptの前に実行するコマンド|〇|
|after_script|scriptの後に実行するコマンド|-|
|stage|ジョブのステージ|〇|
|only|ジョブを実行する条件|-|
|except|ジョブを実行しない条件|-|
|allow_failure|ジョブの失敗を許可する|〇|
|when|ジョブをいつ実行するかを決める|〇|
|rules|ジョブ実行有無の条件|〇|
|tags|Runnerを選択する際に使うタグ一覧|-|
|environment|ジョブをデプロイする環境の名前|-|
|cache|ジョブ同士でキャッシュするファイル|〇|
|artifacts|成功した場合に作成するファイル|〇|
|dependencies|アーティファクトを取得するジョブ|-|
|coverage|ジョブのコードカバレッジ|-|
|retry|失敗した時の再試行回数|-|
|timeout|ジョブのタイムアウト|-|
|parallel|ジョブを並列に実行するインスタンス数|-|
|trigger|下流パイプラインのトリガーを定義する|-|
|include|外部のymlファイル読み込みを許可する|-|
|extends|ジョブが継承する項目|-|
|pages|GitLabPagesにジョブの結果をアップロードする|-|
|variables|ジョブ単位の変数|〇|
|interruptible|重複実行されたジョブをキャンセルする|-|
|resource_group|ジョブの同時実行を制御する|-|
|release|リリースオブジェクトを生成する。|-|


### script

GitLabRunnerが実行するコマンドを定義します。ジョブ定義に必須です。

```.gitlab-ci.yml:yml

sample1:
  script: echo "Hello World"
```

配列形式で複数のコマンドを実行したり、1つのコマンドを複数行で定義する事も出来ます。

```.gitlab-ci.yml:yml

sample2:
  script:
    - echo "show files"
    - ls -la

sample3:
  # gitlabCI特有のものではなく、ymlの記法です
  script: 
    -|
     apt-get install pytest 
     pytest-mock
      
```

yamlのアンカーを利用して複数のジョブで共通のコマンドを再利用することも可能です。

```.gitlab-ci.yml:yml
## &以降がアンカー名です
.script-template: &template
  - echo "Template"

job:
  script:
  # *アンカー名でアンカーを読み込みます
  - *template
  - echo "Execute job"
```

### image

ジョブの実行環境として使用するDockerイメージを定義します。(省略した場合ruby2.5)

### services

```image```で指定したDockerイメージと連携するDockerイメージを定義します。
アプリケーションコンテナとデーターベースコンテナを接続してデータベースを用いた
統合テストを実行するという使い方が一般的です。

### before_script

ジョブの前に実行されるコマンドを定義します。
scriptで指定されたコマンドと統合されて同一のシェル内で実行されます。
(before_script内で定義した環境変数やエイリアスがscriptに引き継がれる)

```before_script```ではコマンドを配列で指定する必要があります。

```
# これはok
job:
  before_script:
    - npm install
# これはng
job2:
  before_script: npm install
```

### after_script

ジョブの実行後に実行するコマンドを定義します。
before_scriptと異なりscriptとは別のシェルで実行されます。

### stage

ジョブをグルーピングするためのパラメータです。
個々のジョブはグローバルに定義されたstagesパラメータに依存します。

ジョブはstagesで定義された順に実行されます。(下記の例の場合build→test→deployの順に実行されます。)
また、同じstageのジョブは並列実行されます。(job 1とjob 2が並列実行されます。)

実行順序を問わないジョブ(例えば静的解析とユニットテスト等)を同じステージにすれば実行時間を短縮
させることができるでしょう。


```.gitlab-ci.yml:yml

# グローバルでstagesを定義する。(数は任意)
stages:
  - build
  - test
  - deploy

job 1:
  stage: build
  script: make build dependencies

job 2:
  stage: build
  script: make build artifacts

job 3:
  stage: test
  script: make test

job 4:
  stage: deploy
  script: make deploy
```

前のステージのジョブが全て成功した場合のみそれ以降のジョブが実行されます。
例えば、```build```ステージのjob1 job2のどちらかが失敗すると```test```ステージと```deploy```ステージの
ジョブは実行されません。

stagesを定義しない場合、デフォルトでbuild・test・deployステージが定義されます。
ジョブのstageを指定しない場合、デフォルトでtsetステージが指定されます。

### allow_failure

有効化するとジョブが失敗した時に他のパイプラインが影響をされないようにすることができます。
デフォルトではfalseです。

以下の例ではjob1が失敗しますが、job2は実行されます。

```.gitlab-ci.yml:yml

job1:
  stage: test
  script:
    - scp /non/exists hoge@hoge.emptydomain
  allow_failure: true

job2:
  stage: deploy
  script: echo "にゃーん"
```

実行結果が安定しないE2Eテストや、外部要因に結果が左右されるジョブ(例：外部APIと通信するテスト)
は ```allow_failure: true```と設定されることが多いでしょう。
逆にコンテナ内で完結するユニットテストやビルドはデフォルト設定にしておくのが無難だと思います。

### when

ジョブが失敗した場合や、結果に関わらず実行されるジョブを制御するために使います。

- on_success: 前のステージのジョブが全て成功した場合のみジョブが実行されます。(デフォルト)
- on_failure: 前のステージのジョブが少なくとも1つ失敗した場合のみジョブが実行されます。
- always: 前のステージのジョブとは無関係に常にジョブが実行されます。
- manual: ジョブを手動で実行します。本番環境へのデプロイ等、実行タイミングをコントロールしたい
処理に対して設定されます。
- delayed: 一定時間後にジョブを実行します。


on_failureやdelayedはあまり使わないような気もします。


### rules

パイプラインにジョブを追加する条件を設定します。
rules句に指定された条件が上から順に評価されていき、一致した場合ジョブが実行されるかどうか決まります。

#### rules句で利用できる条件

- if: if文の評価によりジョブを追加・除外します。
- changes: ファイルの変更によりジョブを追加・除外します。
- existsファイルの存在によりジョブを追加・除外します。

#### rules句の条件で利用できる属性

- when: デフォルトではon_successです。
- allow_failure: デフォルトではfalseです。

ここでジョブパイプラインに追加されるケース・されないケースを整理します。


||rules句|when属性|追加有無|
|--|----|--------|--------|
||存在しない|on_success・delayed・always|追加される|
||存在しない|存在しない|追加される|
||マッチする|on_success・delayed・always|追加される|
||マッチしない|独立しており、on_success・delayed・alwayesのいずれか|追加される|
||存在しない|never|追加されない|
||マッチしない|独立しておらず、on_success・delayed・alwayesのいずれかがない|追加されない
||マッチする|never|追加されない|

#### rules句の注意点

rules句の条件は上から順に評価していき、最初にマッチした時点でパイプラインにジョブが追加される・されないが決まります。
すなわち、次の例では「マージリクエストが作成されたときかつfrontendディレクトリ配下のファイルが変更された時」にジョブが追加されるわけではなく
「マージリクエストが作成された時」または「frontend配下のファイルが変更されたとき」にジョブが追加されます。

```.gitlab-ci.yml:yml

job1:
  script: echo "にゃーん"
  rules:
    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
    - changes:
      - frontend/*
    - if: $CI_PIPELINE_SOURCE == 'push'
      when: never
```

「マージリクエストが作成されたときかつfrontendディレクトリ配下のファイルが変更された時」にジョブを追加したい場合、次のようにif句の
スコープ内でchanges句を記述する必要があります。

```.gitlab-ci.yml:yml

job1:
  script: echo "にゃーん"
  rules:
    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
      changes:
        - frontend/*
    - if: $CI_PIPELINE_SOURCE == 'push'
      when: never
```

また、rules句は```only/except```と組み合わせて使うことはできません。
公式ではrulesが推奨されているため、only/exceptではなくrules句を使うのが良さそうです。

> rules構文は、ジョブがいつ実行されるべきか、あるいは実行されないべきかを定義するための改良された、より強力な解決方法です。 パイプラインを最大限に活用するために、only/exceptではなく、rulesを使用することを検討してください。

### cache

ジョブ間でキャッシュするファイルとディレクトリの配列を指定します。

cacheがグローバルに定義されている場合、全てのジョブでキャッシュを利用する事が出来ます。

#### cache句で使用できるパラメータ

- paths: キャッシュするファイルやディレクトリを指定します。パスはプロジェクトのルートディレクトリからの相対パスである必要があります。
- key: キャッシュのキー(IDのようなもの)を指定します。ブランチやジョブ毎に異なるキャッシュを使う・全ブランチ全ジョブでキャッシュを共有する等設定可能です。
- untracked: 有効化するとGit管理対象外のファイルが全てキャッシュされます。
- policy: キャッシュの処理方法を指定します。
  - pull-push: ジョブの開始時にファイルをダウンロードし、終了時に再アップロードします。デフォルトの設定です。
  - pull: アップロードをスキップします。ジョブでキャッシュファイルが変更されない場合に利用できます。
  - push: ダウンロードをスキップします。前のジョブと無関係にキャッシュを再作成する場合に利用できます。

#### cache句の使用例

cache句は複数のジョブで共通して使われるライブラリやパッケージを保存するという
用途で利用されます。
(同じパッケージを何度もダウンロードせずに済むのでジョブの実行速度が向上します。)

公式ドキュメントのサンプルコードを見てみましょう。

```.gitlab-ci.yml:yml

#
# https://gitlab.com/gitlab-org/gitlab/tree/master/lib/gitlab/ci/templates/PHP.gitlab-ci.yml
#
image: php:7.2

# Cache libraries in between jobs
cache:
  key: ${CI_COMMIT_REF_SLUG}
  paths:
    - vendor/

before_script:
  # Install and run Composer
  - curl --show-error --silent https://getcomposer.org/installer | php
  - php composer.phar install

test:
  script:
    - vendor/bin/phpunit --configuration phpunit.xml --coverage-text --colors=never
```

グローバルの```cache```で全てのジョブでキャッシュを利用するようにしています。(キャッシュのパスはvendor/)
同じくグローバルの```before_script```でcomposerを用いてPHPのパッケージをインストールしています。

```before_script```でvendorディレクトリに依存パッケージをインストールしているため、testジョブでは
改めてパッケージをインストールする必要がなくなっています。

### artifacts

ジョブの成果物としてファイルやディレクトリを指定します。
アーティファクトはGitLabにアップロードされ、ダウンロードできます。
(zip形式でダウンロードされるため、ダウンロードした後解凍する必要があります。)

主にビルドされたモジュールや、テストの実行結果がアーティファクトとして指定されます。

#### artifactsで使用できるパラメータ

- paths: アーティファクトとして残すファイルのパスをプロジェクトのルートディレクトリからの相対パスとして指定します。
- exclude: アーティファクトに追加しないファイルのパスをプロジェクトのルートディレクトリからの相対パスとして指定します。
- expose_as: ジョブのアーティファクトをマージリクエスト画面で公開します。
- name: アーティファクトに名前をつけることができます。デフォルトでは```artifacts```となります。
- untracked: pathsで指定したファイルに加えてGit管理対象外の全ファイルをアーティファクトに指定します。
- when: ジョブが失敗した時にアーティファクトを制御するために使います。
  - on_success: 成功時のみアーティファクトをアップロードします。(デフォルト)
  - on_failure: 失敗時のみアーティファクトをアップロードします。
  - always: 常にアップロードします。
- expires_in: アーティファクトが削除されるまでの日数を指定します。デフォルトでは30日間です。

### variables

グローバルまたはジョブレベルで変数を定義することができます。


### 参考資料

[GitLab日本語ドキュメント](https://gitlab-docs.creationline.com/)
[GitLab CIを四倍速にした話](https://lab.astamuse.co.jp/entry/gitlab-ci-kaizen)
[GitLabのCI/CDで超重要なrulesの全てを理解する](https://dev.classmethod.jp/articles/sample-to-understand-rules/)