💈

Cypressでe2eをCloudBuild上で実行する方法

2021/10/17に公開

Cloud Build: サーバーレス CI / CD プラットフォーム  |  Google Cloud

CI/CD で Github Actions を使うことは多いですが、 GCP の Cloud Build を使って Cypress を実行する方法をメモしておきます。

今回の成果物リポジトリ: hisasann/nuxt-lint-ts-msw: ESlint、TypeScript、Mock Service Worker(msw)でのモックサーバーを使ったリポジトリです

Cloud SDK のインストール

圧縮ファイルをダウンロードしてきて sh ファイルを実行していきます。

$ ./google-cloud-sdk/install.sh
$ ./google-cloud-sdk/bin/gcloud init

サンプルコードの実行

Google Cloud Platform

$ git clone https://github.com/GoogleCloudBuild/cloud-console-sample-build && \
  cd cloud-console-sample-build && \
  gcloud builds submit --config cloudbuild.yaml

ローカルのプロジェクトを設定する

$ gcloud auth list
$ gcloud config set project プロジェクト名(aliasではない)
$ gcloud config configurations list

gcloud でプロジェクトの切り替え設定 - Qiita

はじめ、これからを何もせずに gcloud builds submit してみたらエラーで進みませんでした。

プロジェクトをブラウザの GCP の画面から作り、その名前を指定してから、

$ gcloud builds submit --config cloudbuild.yaml

をしたらうまくいきました。

調子に乗って、 今回使っている nuxt のリポジトリで hisasann/nuxt-lint-ts-msw 実行してみたら、

$ gcloud builds submit --config ./cloudbuild/lint_and_test.yaml

.eslintignoreを作るの巻

こんなエラーが出ました。

Step #1: Oops! Something went wrong! :(
Step #1:
Step #1: ESLint: 7.32.0
Step #1:
Step #1: Error: Cannot read .eslintignore file: /workspace/.gitignore
Step #1: Error: ENOENT: no such file or directory, open '/workspace/.gitignore'
Step #1:     at Object.openSync (fs.js:497:3)
Step #1:     at Object.readFileSync (fs.js:393:35)
Step #1:     at readFile (/workspace/node_modules/@eslint/eslintrc/lib/config-array-factory.js:142:15)
Step #1:     at loadESLintIgnoreFile (/workspace/node_modules/@eslint/eslintrc/lib/config-array-factory.js:270:16)
Step #1:     at ConfigArrayFactory.loadESLintIgnore (/workspace/node_modules/@eslint/eslintrc/lib/config-array-factory.js:559:32)
Step #1:     at createCLIConfigArray (/workspace/node_modules/@eslint/eslintrc/lib/cascading-config-array-factory.js:165:34)
Step #1:     at new CascadingConfigArrayFactory (/workspace/node_modules/@eslint/eslintrc/lib/cascading-config-array-factory.js:243:29)
Step #1:     at new CLIEngine (/workspace/node_modules/eslint/lib/cli-engine/cli-engine.js:569:36)
Step #1:     at new ESLint (/workspace/node_modules/eslint/lib/eslint/eslint.js:432:27)
Step #1:     at Object.execute (/workspace/node_modules/eslint/lib/cli.js:292:24)
Step #1: npm ERR! code ELIFECYCLE
Step #1: npm ERR! errno 2
Step #1: npm ERR! nuxt-lint-ts-msw@1.0.0 lint:js: `eslint --ext .js,.ts,.vue --ignore-path .gitignore .`
Step #1: npm ERR! Exit status 2
S

.eslintignore の配置場所は気をつけた方がいい - Qiita

eslint --ext .js,.ts,.vue --ignore-path .gitignore .

このあたりを読んで、 --ignore-path .gitignore するのやめて .eslintignore を配置してみよう。

あんまり普段意識したことなかったが、 .gitignore ファイルを eslint に食べさせる横着は良くなかったのだろう。

$ gcloud builds submit --config ./cloudbuild/lint_and_test.yaml

エラーが解消されました!

cloudbuild.yamlをはじめから作ればよかった

Cloud Build 上で yaml ファイルを 自動検出 ではなく指定で /cloudbuild としていたらどうやらパス指定は Invalid Argument のようだ。

特にここが悪いという内容のエラーが一切出てくれなかったので、かなりハマってしまった。

なので cloudbuild.yaml にリネームした。

こうしたらビルドが走るようになった。

Node.js アプリケーションのビルド | Cloud Build のドキュメント | Google Cloud

Server起動しつつe2eの実行をコマンド一発でやる

bahmutov/start-server-and-test: Starts server, waits for URL, then runs test command; when the tests end, shuts down server

よくある需要だと思いますが、 e2e の場合は、

  1. ソースコードをビルドする
  2. サーバーを起動する
  3. サーバーが起動したままでe2eテストを実行する
  4. サーバーを停止する

とサーバーが起動したままテストを実行したいので、このモジュールを使わせていただきました。

cypress:run でコケた

> nuxt-lint-ts-msw@1.0.0 cypress:run /workspace
> cypress run

It looks like this is your first time using Cypress: 8.4.1

[STARTED] Task without title.
[FAILED] Your system is missing the dependency: Xvfb
[FAILED]
[FAILED] Install Xvfb and run Cypress again.
[FAILED]
[FAILED] Read our documentation on dependencies for more information:
[FAILED]
[FAILED] https://on.cypress.io/required-dependencies
[FAILED]
[FAILED] If you are using Docker, we provide containers with all required dependencies installed.
[FAILED]
[FAILED] ----------
[FAILED]
[FAILED] Error: spawn Xvfb ENOENT
[FAILED]
[FAILED] ----------
[FAILED]
[FAILED] Platform: linux (Debian - 9.13)
[FAILED] Cypress Version: 8.4.1
Your system is missing the dependency: Xvfb

Install Xvfb and run Cypress again.

Read our documentation on dependencies for more information:

https://on.cypress.io/required-dependencies

If you are using Docker, we provide containers with all required dependencies installed.

どうやら結構依存してるモジュールがあるようです。

Introduction | Cypress Documentation

cypress/included Tags | Docker Hub

$ docker pull cypress/included:8.5.0

bahmutov/demo-docker-cypress-included: Demo running the complete Docker image cypress/included

node:14イメージでは厳しかった

こんな感じで、 test までする cloudbuild.yaml があったとして、ここに cypress run する script 実行を追加しましたが、 node:14 イメージだと Cypress で使う依存モジュールが入っていないのエラーになりました。

chore(cloudbuild): add cloudbuild/lint_and_test.yaml · hisasann/nuxt-lint-ts-msw@6fbcf10

steps:
- name: node:14
  entrypoint: yarn
  args: ['install']
- name: node:14
  entrypoint: yarn
  args: ['run', 'lint']
- name: node:14
  entrypoint: yarn
  args: ['test']
- name: node:14
  entrypoint: yarn
  args: ['cypress']

cypress/includedを使ってCypressを実行する

今の最終形態はこちらです。

nuxt-lint-ts-msw/cloudbuild.yaml at main · hisasann/nuxt-lint-ts-msw

steps:
# install dependencies
- id: install-dependencies
  name: node:14
  entrypoint: npm
  args: ['install']

# lint and test
- name: node:14
  entrypoint: npm
  args: ['run', 'lint']
- name: node:14
  entrypoint: npm
  args: ['test']

# run cypress
- name: cypress/included:8.5.0
  entrypoint: 'npm'
  args: ['run', 'ci']

options:
  machineType: 'E2_HIGHCPU_8'

npm install を一番はじめに一回だけしていますが、これを Cypress を実行する前でもう一度実行してしまい、 Cypress の npm 環境が壊れて実行ができなくなりました。

また、 CYPRESS_CACHE_FOLDER という環境変数を使う記事が多々ありましたが、今の Cloud Build 上だと逆にそれがあることで Cypress がうまく動きませんでした。

こちらのリポジトリには大変助けられました。

bahmutov/demo-docker-cypress-included: Demo running the complete Docker image cypress/included

via:

Running Cypress in Google Cloud Build - Stack Overflow

【Cypress】Cloud Buildでdocker runを使ってCypressを実行する - UGA Boxxx

【Cypress】ローカルのCloud BuildでCypressを実行する - UGA Boxxx

【Cypress】Cloud BuildでCypressを実行したら"out of memory" エラーが発生した - UGA Boxxx

Running Cypress in Google Cloud Build - Stack Overflow

Docker | Cypress Documentation

GithubActionsでCypressを実行するyaml

Github Actions のほうはサンプルも多いので簡単です。

nuxt-lint-ts-msw/e2e.yml at main · hisasann/nuxt-lint-ts-msw

name: Node.js E2E
on: push

jobs:
  build:
    name: Node.js ${{ matrix.os }} ${{ matrix.node }}
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest]
        node: [ '14' ]
    steps:
      - uses: actions/checkout@v2
      - name: Setup node
        uses: actions/setup-node@v2
        with:
          node-version: ${{ matrix.node }}

      - name: Display version of Node.js, npm, Yarn
        run: |
          node -v
          npm -v
          yarn --version

      - name: Get yarn cache
        id: yarn-cache
        run: echo "::set-output name=dir::$(yarn cache dir)"
      - uses: actions/cache@v2
        with:
          path: ${{ steps.yarn-cache.outputs.dir }}
          key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-yarn-
            - uses: actions/cache@v2
              with:
                path: node_modules
                key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}
      - run: yarn
      - name: Cypress run
        uses: cypress-io/github-action@v2
        with:
          build: yarn build
          start: yarn start

今回の成果物リポジトリ: hisasann/nuxt-lint-ts-msw: ESlint、TypeScript、Mock Service Worker(msw)でのモックサーバーを使ったリポジトリです

記事を書いてる最中に動かなくなった

こんなエラーが出るようになって、どうやら cypress:8.6.0 のイメージが出ていて、 cypress:8.5.0 を使っているはずがなぜか 8.6.0 が使われてしまい、 cypress がインストールされていない的なエラーが出た。

Error: Command failed with exit code 1: npm run cypress:run
Step #3:     at makeError (/workspace/node_modules/execa/lib/error.js:60:11)
Step #3:     at handlePromise (/workspace/node_modules/execa/index.js:118:26)
Step #3:     at processTicksAndRejections (internal/process/task_queues.js:95:5) {
Step #3:   shortMessage: 'Command failed with exit code 1: npm run cypress:run',
Step #3:   command: 'npm run cypress:run',
Step #3:   escapedCommand: '"npm run cypress:run"',
Step #3:   exitCode: 1,
Step #3:   signal: undefined,
Step #3:   signalDescription: undefined,
Step #3:   stdout: undefined,
Step #3:   stderr: undefined,
Step #3:   failed: true,
Step #3:   timedOut: false,
Step #3:   isCanceled: false,
Step #3:   killed: false
Step #3: }

Image Layer Details - cypress/included:8.6.0 - sha256:021f2017118ded2210359e2355f1c38ccaa0942d524b9bfac172df4bf315aa58 | Docker Hub

とりあえず、 8.6.0 に cloudbuild.yaml を書き換えてみたら、 cypress が run できるようになった。

うーん、 latest みたいな感じで使われてしまっているのだろうか。

cypress/included のバージョンを固定して使いたいがちょっとここはまた別のお話。

追記

普通に凡ミスしていることを教えていただいた。

yarn.lock ファイルでバージョンを固定していたのに npm install してしまっていた。

これにより、 Cypress がバージョンを指定しているはずなのに最新がインストールされてしまっていた。

chore(cloudbuild): change entrypoint from npm to yarn · hisasann/nuxt-lint-ts-msw@c78f433

エラーがでなくなり、しっかりと cypress/included のバージョンを固定できた。

参考記事

ビルド構成ファイルのスキーマ  |  Cloud Build のドキュメント  |  Google Cloud

コンテナ イメージのビルド  |  Cloud Build のドキュメント  |  Google Cloud

cypress/included Tags | Docker Hub

Cloud Build を知ってみよう | フューチャー技術ブログ

[GCP] Cloud RunにNuxt.jsをデプロイするまで - Qiita

Cypress - TypeScript Deep Dive 日本語版

Discussion