概要
GitLab CI は大変便利。高機能。
2,000分/月もの無料枠がある。
だが、2,000分もあっても、愚直に組むと、意外とあっさり超えてしまう。
1回あたりのレスポンスを早める記事はいろいろ見かけたが、実行時間を短くする記事はあまりなかったので書くことに。
今回 GitLab CI でやること/やらないこと
やること: lint, test
やらないこと: deploy, 生成物の artifact 化
GitLab CI を使う上での注意点
- 無料枠 は アカウント単位 である (リポジトリごとじゃない)
技1: Docker Image は alpine を使う
apline は余分な機能をそぎ落とした最小の Docker Image。
たとえば、下記を
約40秒かかる
image: node:12.18.2
を下記に書き換えることで apline を使える。
約15秒かかる
image: node:12.18.2-alpine
これだけで1コンテナあたり 25 秒も節約できる!
技2: Job(コンテナ)を分けない
これは一般的なベストプラクティスとは逆行している。
Job(コンテナ)を分けるのは並列させて 待ち時間を短縮 するためだが、その分 無料枠は消費 される。
コンテナ起動してキャッシュを読み込むとそれだけで1回あたり、30秒~60秒くらい消費する。
Jobを複数並列で起動する場合は、それごとにかかる。
なので、無料枠を考えると、Job1つでやったほうが効率的なのである。
Job1つならキャッシュ保存/読込のオーバーヘッドも要らない。
技3: lint と test を両方必ず走らせたい
通常、どちらかが失敗すると、その場で Job は終了してしまう。
なので、script
に複数並べても、両方のエラー常に出力できない。
そこで下記のようにする。
script:
# && true をつけることで強制終了しなくなる。
# これは -e は条件式の場合、最後の句がエラーだった場合に強制終了する仕様だが、
# 差分アリの場合、ショートサーキットで最後(true)が実行されないためである。
# 差分ナシの場合は、true (結局何もしない) が実行される。
# &? は同じ行にないと正しく取れないので注意
- 'npm run lint && true; EXIT1=&?'
- 'npm run test && true; EXIT2=&?'
# まとめてエラー判定 (実は exit 1 は無くても同様に動くが、可読性のため)
- 'echo EXIT1=[${EXIT1}], EXIT2=[${EXIT2}]'
- '[ "${EXIT1}" -eq 0 -a "${EXIT2}" -eq 0 ] || exit 1'
.gitlab-ci 全体
image: node:12.18.2-alpine
workflow:
rules:
# マージリクエストで起動
- if: $CI_MERGE_REQUEST_ID
stages:
- test
test:
stage: test
before_script:
# package.json が変わったときに備えて ci で行う
- time npm ci
script:
# サーバ起動のオーバーヘッドを減らすため lint と test を同じインスタンスでやる
# && true をつけることで強制終了しなくなる。
# これは -e は条件式の場合、最後の句がエラーだった場合に強制終了する仕様だが、
# 差分アリの場合、ショートサーキットで最後(true)が実行されないためである。
# 差分ナシの場合は、true (結局何もしない) が実行される。
# &? は同じ行にないと正しく取れないので注意
- 'time npm run lint && true; EXIT1=&?'
- 'time npx rub test && true; EXIT2=&?'
# 条件句が偽の場合もまた終了コード1をもたらすので注意(必ずPASSを真として判定する必要あり)
- 'echo EXIT1=[${EXIT1}], EXIT2=[${EXIT2}]'
- '[ "${EXIT1}" -eq 0 -a "${EXIT2}" -eq 0 ] || exit 1'