.gitlab-ci.yml を書くときに知っておくといいことあるかも

#

## グローバル設定
グローバル設定として、`image` や `before_script` などが書けたのですが、[将来廃止](https://docs.gitlab.com/ee/ci/yaml/#globally-defined-image-services-cache-before_script-after_script)されます。

代わりに `default` キーワードが追加されました。

```yaml
image: ruby:3.0
```

ではなく、

```yaml
default:
  image: ruby:3.0
```

にします。

`default` キーワードにはもともとグローバルには設定できなかった `tags` などが書けるようになりました。ちょっとうれしい。

まだどのバージョンでというのはありませんが、今から書くものは `default` キーワードを使ったほうがよいでしょう。


## 変数の設定
変数の設定は `variables` キーワードを利用しますが、グローバルとジョブ個別の両方に設定できます。(グローバルは `default` キーワードではなく、今まで通り直下に書きます)

この `variables` キーワードは、グローバル設定とジョブ個別の設定がマージされます。

```yaml
variables:
  VAR1: global1
  VAR2: global2

job1:
  variables:
    VAR1: job1
  script:
    - echo $VAR1 $VAR2 # "job1 global2"
```

グローバル設定を無視したいときは、ジョブ設定に `inherit:variables: false` を追加します。

変数は他の変数の展開ができます。

```yaml
variables:
  WORKING: $CI_PROJECT_DIR/.working
  TEMPORARY: ${WORKING}/temporary
```

展開できる変数は次の通りです。

* [定義済みの変数](https://docs.gitlab.com/ee/ci/variables/predefined_variables.html)
* [他の変数](https://docs.gitlab.com/ee/ci/variables/#use-variables-in-other-variables)
* [プロジェクトの CI/CD 変数](https://docs.gitlab.com/ee/ci/variables/#predefined-cicd-variables)
* [グループの CI/CD 変数](https://docs.gitlab.com/ee/ci/variables/#add-a-cicd-variable-to-a-group)
* [インスタンスレベルの CI/CD 変数](https://docs.gitlab.com/ee/ci/variables/#add-a-cicd-variable-to-an-instance)

一般的な Linux 環境の環境変数 (Ex: HOSTNAME) は展開できません。


## `before_script`
[before_script](https://docs.gitlab.com/ee/ci/yaml/#before_script) キーワードに書かれた処理は `script` キーワードのものよりも前に動作します。

基本的に `script` に書いても `before_script` に書いても変わりはないようです。

ただし、`default` キーワードに書けますので、共通の初期化処理がある場合は活用できます。

初期化処理がジョブごとにばらばらでも次のようにごまかす方法もあります。

```yaml
default:
  before_script:
    - which dotnet && dotnet restore
    - which npm && npm install
    - which docker && docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
```

ジョブでデフォルトの `before_script` を使いたくないときは `[]` を設定します。

```yaml
job:
  before_script: []
```

デフォルト設定とジョブ設定の両方を設定した場合、`variables` キーワードとは異なり、ジョブ設定の方だけが使われます。(どちらかというと、`variables` キーワードが特殊)

```yaml
default:
  before_script:
    # これは実行されません
    - echo don't execute

job:
  before_script:
    - echo execute
  script:
    - echo done
```

もし、デフォルトの `before_script` の後に処理を実行したい場合は、YAML アンカーが使えます。

```yaml
default:
  before_script: &before_script
    - echo default

job:
  before_script:
    - *before_script
    - echo job
  script:
    - echo done
```

ですが、こういった小細工を覚えなくてもいい、シェルスクリプトの活用をお勧めします。


```yaml
default:
  before_script:
    - ./ci/setup-default.sh

job:
  before_script:
    - ./ci/setup-default.sh
    - ./ci/setup-job.sh
  script:
    - echo done
```


## `script`
`before_script` で実行した結果の環境変数・シェル変数・エイリアスはそのまま引き継がれます。

```yaml
job:
  before_script:
    - export VAR1=var1
  script:
    - echo $VAR1 # "var1"
```

エイリアスを使う [nvm](https://github.com/nvm-sh/nvm) も使うことができます。

```yaml
job:
  before_script:
    - nvm install 12
  script:
    - node --version
```

複数行を書きたいときはいろいろ方法がありますが `- |` がよさげです。

```yaml
job:
  script:
    - |
      echo first line
      echo second line
```

CI ジョブのデバッグは結構大変です。`.gitlab-ci.yml` を修正して、プッシュして下手したら 10 分以上待ってバグに気が付きます。長い処理を書くのを避け、シェルスクリプトやビルドツールを使ったほうがよいでしょう。

```yaml
build:
  script:
    - echo first process
    - echo second process
    - echo third process
    - ...
```

と書くよりは、

```yaml
build:
  script: ./ci/build.sh
```

や

```yaml
build:
  before_script: npm install
  script: npm run build
```

のようにすることをお勧めします。

このやり方だと、パソコン側で動作確認がやりやすいので、CI ジョブのデバッグ時間を節約できます。


## `after_script`
ジョブの後始末は [after_script](https://docs.gitlab.com/ee/ci/yaml/#after_script) キーワードに書くことができます。`before_script` キーワードと同様に、デフォルト設定とジョブ設定の両方に書くことができます。

ただし、結構注意が必要です。

* `before_script` もしくは `script` が失敗しても、`after_script` は必ず実行されます
* `after_script` でエラーが発生しても、ジョブの成否には影響しません
* 必ず 5 分でタイムアウトします。タイムアウトしてもジョブの成否には影響しません

完全に後始末用ですね。

もう一つはまりやすい仕様があります。

* `before_script` や `script` で設定された環境変数・シェル変数・エイリアスは引き継がれません

```yaml
job:
  before_script:
    - export VAR1=before
  script:
    - echo $VAR1 # "before"
    - export VAR2=script
  after_script:
    - echo $VAR1 # 何も表示されない
    - echo $VAR2 # 何も表示されない
```

* `nvm` などのエイリアスを使う仕組みは `after_script` に引き継がれません
* プロセス番号を環境変数に設定して、`after_script` で `kill` しようとしてもできません
* 値を引き継ぎたいときはファイルに書き出して、`after_script` で読み込んでください

私はこれを忘れてしょっちゅうはまってます。

`after_script` は挙動が少し特殊なのではまりやすいです。(私はしょっちゅう忘れてはまります)


## 最後に
公式ドキュメントに書いてあることばかりですが、あらかじめ知っておくといいかなと思います。