Vueプロジェクトの簡単なGitLab CI/CDフロー
-
2021年6月9日
こんにちは。ウェブ開発室のコンテンツチームのソーントンです。
皆さん、CI/CDでテストを自動的に実行していますか?そのテストを通った場合、自動的にデプロイしていますか?もし答えがノーでしたら、ご安心ください。本記事ではウェブプロジェクトで使える簡単なGitLab CI/CDパイプラインをゼロから作っていきます。
事前準備
GitLab CI/CDパイプラインの作成にフォーカスしたいので、チュートリアルについていきたい方は下記のサービス登録やプログラムのインストールなどを事前に行ってください。
・GitLab への登録
・GitLabで新規プロジェクトの作成(空)
・Node.js のインストール
(バージョンは2021/06時点LTSのv14.17.0ですが、最新LTSでもいいかと思います)
・静的ファイルをデプロイできるサービスの準備(本記事でAWSのS3を使います)
目標
一般的なフロントエンドのプロジェクトには次のスクリプトが入っているかと思います。
(カッコ内はこちらのチュートリアルで使っているツール名)
・静的解析(ESLint)
・単体テスト(Jest)
・E2Eテスト(Cypress)
・静的ファイルのビルド(Webpack)
・静的ファイルのデプロイ(S3)
こちら5つの処理をGitLab CI/CDで実行できるパイプラインを作りたいと思います。が、まずはプロジェクトが必要なのでVueのCLIツールで簡単に立ち上げます。
Vue CLIでプロジェクトを立ち上げましょう
まずはVue CLIのインストールを行います。
$ npm install -g @vue/cli
$ vue --version でインストールの確認ができます。
$ vue --version @vue/cli 4.5.13
最後は $ vue create <プロジェクト名> で新規のVueプロジェクトを作成しましょう。プロジェクト名はお任せしますが、オプションは次の通りにしてください。
$ vue create gitlab-cicd-test Vue CLI v4.5.13 ? Please pick a preset: Manually select features ? Check the features needed for your project: Choose Vue version, Linter, Unit, E2E ? Choose a version of Vue.js that you want to start the project with 2.x ? Pick a linter / formatter config: Prettier ? Pick additional lint features: Lint on save, Lint and fix on commit ? Pick a unit testing solution: Jest ? Pick an E2E testing solution: Cypress ? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files ? Save this as a preset for future projects? No
NPMのパッケージのインストールなどに少し時間がかかります。できたら、プロジェクトディレクトリに移動します。
$ cd gitlab-cicd-test
一旦、ローカルでテスト・ビルドのスクリプトを実行できるかを確認します。
# 静的解析 $ npm run lint # 単体テスト $ npm run test:unit # E2Eテスト(--headlessオプションでCLIのみで回す) $ npm run test:e2e -- --headless # ビルド $ npm run build
問題がなければおめでとうございます。簡単なフロントエンドのプロジェクトができたので、「事前準備」で作成したGitLabのリポジトリに反映しましょう。
# 実際のGitLabのプロジェクトURLで書き換えてください $ git remote add origin "https://gitlab.com/USERNAME/PROJECT" $ git push origin master
.gitlab-ci.ymlを作りましょう
お疲れ様です。プロジェクトを立ち上げてGitLabまでプッシュしましたが、残念ながらCI/CDがまだ実行されていません。
幸運な事に使い始めるための手順はとてもシンプルで、プロジェクトのルートに .gitlab-ci.yml というファイルを追加するだけとなります。プロジェクトのCI/CDコンフィグがすべてこちらのファイルに入ります。
これはプログラミング系のチュートリアルなので、最初はHello Worldを作らないといけませんね。GitLab上のEditor機能で作成しましょう。
まずプロジェクト画面の左メニューから CI/CD > Editor を選択してください。

(「Editor」ってまだ翻訳されてないんですね)
次の画面で「Create new CI/CD Pipeline」を選択すれば、エディター画面が表示されます。

ようやくCI/CDパイプラインの作成を始められます!簡単なHello Worldを作りましょう。最小限のコンフィグになります。
yml
hello_world:
script:
- echo "Hello, world!"
日本語に翻訳すれば、hello_world という「ジョブ」で、 echo コマンドを実行する、という意味合いです。
「Commit changes」ボタンで一旦コミットすれば、パイプラインが自動的に始まります。左メニューの CI/CD > ジョブ を選択すれば、ジョブ一覧に hello_world が追加されていることを確認できます。

ジョブの実行が完了したら、ステータスの「成功」をクリックして出力を見ることができます。

下の方で見れるかと思いますが、script で指定した echo コマンドがちゃんと実行されましたね!これで基本の基本ができましたが、Hello Worldではなく、lintスクリプトを実行したいですね。早速ですが、lintジョブを作成しましょう。
lintジョブ
左メニューの CI/CD > Editor からエディター画面に戻ります。 hello_world を削除し、 lint というジョブを作ります。その script はVueプロジェクトの package.json に入っているスクリプトを使います。
yml
lint:
script:
- npm run lint
でも実はこれでは上手くいきません。 hello_world ジョブの出力で気づいたかもしれませんが、Dockerのイメージは ruby:2.5 でしたね。Nodeもnpm入っていないイメージなので、Nodeのイメージを使う必要があります。 更にプロジェクトの依存性パッケージをインストールしないとスクリプトを実行できません。
yml
lint:
image: node:lts # Dockerイメージの指定
script:
- npm ci # npmパッケージのインストール
- npm run lint
「Commit changes」でコミットし、問題がなければジョブが成功します!
![]()
DRYなコンフィグ
lintジョブが無事に通れば、次は単体テストを実行したいです。依存性は lint ジョブと同じで、ほとんどコピペで追加できます。
yml
lint:
image: node:lts
script:
- npm ci
- npm run lint
"test:unit":
image: node:lts
script:
- npm ci
- npm run test:unit
これでコミットしたら確かに成功しますが、プログラマーのあなたは「DRYじゃないよ」と突っ込みたいですよね。その通りです。 両方のジョブの image が同じだし、 script の最初に npm ci を実行します。幸運にもGitLabさんはDRYの書き方ができるように作ってくれています。 default と before_script を使って、重複を削減しましょう。
yml
# グローバルの設定。全部のジョブに付与されます。
default:
image: node:lts
# scriptの前に実行されるスクリプト
before_script:
- npm ci
lint:
script:
- npm run lint
"test:unit":
script:
- npm run test:unit
すっきりしましたね!これで default 文に設定されているオプションは全部のジョブに付与されるようになりました。コミットしてジョブの実行を確認しましょう!

次はE2Eテストのスクリプトを実行しましょう。残念ながら、Cypressにはブラウザーが必要なので、 node:lts のイメージでは実行できません。Cypressさんが提供しているDockerイメージはおすすめです。 (https://github.com/cypress-io/cypress-docker-images)
yml
default:
image: node:lts
# 省略
# 省略
"test:e2e":
image: cypress/base:14.16.0
script:
# --headlessとはCLIのみで実行するオプション
- npm run test:e2e -- --headless
あれ、先ほど default で image を設定したのにまたジョブのほうで image を設定しているの?と思ったらご安心ください。ジョブのほうの設定は優先度が高く、好きに default の設定を上書きすることができます。ここでコミットして、3つのジョブを確認しましょう。

ステージの紹介
テスト系のジョブが揃いました。次にビルドとデプロイを追加したいのですが、ちょっとした整理が必要になります。ビルドとデプロイをこのまま追加すれば5つのジョブが並行で実行されるので、テストが通らなくてもデプロイされてしまう恐れがあるのです!では、この問題を解決してくれる「ステージ」を紹介したいのですが、それは公式ドキュメントにお任せします。
パイプラインは、継続的なインテグレーション、デリバリ、デプロイメントのトップレベルのコンポーネントです。
パイプラインの構成は、以下のとおりです。・ジョブとは、何をするかを定義するものです。例えば、コードをコンパイルしたり、テストしたりするジョブがあげられます。
・ステージとは、ジョブを実行するタイミングを定義するものです。例えば、テストを実行するステージは、コードをコンパイルするステージの後に実行するなどです。ジョブはRunnerによって実行されます。同じステージ内の複数のジョブは、同時に動作可能なRunnerがあれば並列に実行されます
ステージ内のすべてのジョブが成功すれば、パイプラインは次のステージに進みます。
あるステージのジョブがどこかで失敗すると、一般的に次のステージは実行されず、パイプラインはそこで終了します。
通常パイプラインは自動的に実行され、一度作成されたパイプラインへ介入する必要はありません。しかし、手動でパイプラインとやり取りする場合もあります。
https://gitlab-docs.creationline.com/ee/ci/pipelines/index.html
つまり、テスト系のジョブは並行で実行されてもいいので、1つのステージに入れてもいいのです。テストを通れば、ビルド・デプロイに進みたいのでテストのステージ移行にステージに入れればいいとのことです。テストの中のいずれかのジョブが失敗したら、ビルドのステージに進まないので安心ですね。
まずは、 test ステージを明示的にしましょう。
yml
stages:
- test
# 省略
lint:
stage: test
# 省略
"test:unit":
stage: test
# 省略
"test:e2e":
stage: test
# 省略
そしてビルドのジョブを新たな build ステージで作ります。
yml
stages:
- test
- build
# 省略
build:
stage: build
script:
- npm run build
これでテストが通ったらビルドに進むようになりました。一旦コミットして、パイプラインの結果を確認しましょう。

環境変数&artifacts
build ジョブで dist/ ディレクトリが生成されるので、次の deploy ジョブにそのディレクトリをS3に上げたいです。AWSのCLIが必要なので、またDockerイメージを変えることになります。幸運な事に、GitLabさんが提供してくれています。
(https://docs.gitlab.com/ee/ci/cloud_deployment/#run-aws-commands-from-gitlab-cicd)
.gitlab-ci.yml のエディター画面に戻り、 deploy ステージを stages の最後に入れて、 deploy ジョブも追加します。検証のためにまずは ls dist/ を実行します。
yml
stages:
- test
- build
- deploy
# 省略
deploy:
stage: deploy
# GitLab提供のAWSイメージ
image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
# npmインストールの必要がないため、デフォルトを上書きします
before_script: []
script:
- ls dist/
ここで一旦コミットしてCI/CDの結果を見れば、下記のエラーがデプロイジョブで出てきました。
$ ls dist/ ls: cannot access 'dist/': No such file or directory
build ジョブで生成された dist/ ディレクトリは deploy ジョブに渡されていないようですね。これはなぜかというと、ジョブの最初に git clean が実行されて、gitで管理していないファイルは全て削除されてしまいます。この場合は artifacts を設定すれば解決できます。あるジョブの `artifacts` 設定に入っているファイル・ディレクトリは、それ移行のステージに渡されます。 build ジョブのほうで追加します。
yml
# 省略
build:
stage: build
script:
- npm run build
artifacts:
paths:
- dist/
# 省略
ここでコミットし、もう一度デプロイジョブの出力を見てみましょう。
$ ls dist/ css favicon.ico img index.html js
成功です!最後にAWSのCLIが使えるように、認証情報を設定します。
AWSの認識情報を環境変数として設定すれば、スクリプトのほうでログイン処理を行う必要がなくなるのでそうします。
左メニューの 設定 > CI/CD を選択し、「変数」のセクションを展開します。「変数の追加」ボタンから、次の3つを設定します。
| 変数名 | 説明 |
|---|---|
| AWS_ACCESS_KEY_ID | アクセスキー |
| AWS_SECRET_ACCESS_KEY | シークレットキー |
| AWS_DEFAULT_REGION | リージョンコード (us-east-2など) |

環境変数の設定ができたら、CI/CDのエディター画面に戻ります。デプロイジョブのスクリプトに aws s3 sync コマンドを記入します。S3のURIは書き換えてください。
yml
# 省略
deploy:
# 省略
script:
- aws s3 sync dist/ "s3://example.com/path/to/your/instance"
コミットしてパイプラインが終わったら、デプロイ先を確認します。

上記のページが表示されていればおめでとうございます!GitLab CI/CDでのデプロイが無事できました。
チュートリアルはこれで以上となります。ありがとうございました!
※.gitlab-ci.ymlは最終的にこちらになります:
yml
stages:
- test
- build
- deploy
default:
image: node:lts
before_script:
- npm ci
lint:
stage: test
script:
- npm run lint
"test:unit":
stage: test
script:
- npm run test:unit
"test:e2e":
stage: test
image: cypress/base:14.16.0
script:
- npm run test:e2e -- --headless
build:
stage: build
script:
- npm run build
artifacts:
paths:
- dist/
deploy:
stage: deploy
image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
before_script: []
script:
- aws s3 sync dist/ "s3://example.com/path/to/your/instance"
※こちらのチュートリアルで触れていないオプションが多く、もっと知りたい方は公式のリファレンスを参考にしてください。
ココネでは一緒に働く仲間を募集中です。
ご興味のある方は、以下のリンクから是非ご応募ください。