GAミント至上主義

Web Monomaniacal Developer.

NestJS + CircleCIで各種チェックやテストを一通り実行する

NestJS + TypeORMを使った新規開発で開発がちょっと進み、テストコマンドなどもできてきたのでCircleCIで自動実行できるようにしたのでメモ。

前提としてローカルではテストなどが動いていること。


実行したのは下記。まだデプロイなどは行っていない。

  • lintの実行
  • NestJSのbuild
  • マイグレーション漏れが無いか確認
  • 普通のテスト実行 (テスト用DB使用)
  • E2Eテストの実行(テスト用DB使用)

NestJS系のバージョン

"dependencies": {
    "@nestjs/common": "^8.0.9",
    "@nestjs/config": "^1.0.1",
    "@nestjs/core": "^8.0.9",
    "@nestjs/platform-express": "^8.0.0",
    "@nestjs/typeorm": "^8.0.2",

CircleCIの設定ファイル(.circleci/config.yml)

いろいろなところからのコピペしたままもあり、ツギハギ感あるけど一旦動いたもの。

テスト用DBの接続情報、パスワードとかも直書きだけどCircleCI内部で使われるだけなので問題なさそうなのでそのまま。

version: 2.1

jobs:
  test-server:
    resource_class: medium
    docker:
      - image: circleci/node:14
        environment:
          TEST_DB_HOST: localhost
          TEST_DB_PORT: 3306
          TEST_DB_USERNAME: test_search_user
          TEST_DB_PASSWORD: test_search_password
          TEST_DB_DATABASE: test_search_db

      - image: mysql:8.0
        command:
          - --sql-mode=NO_ENGINE_SUBSTITUTION
        environment:
          MYSQL_DATABASE: test_search_db
          MYSQL_USER: test_search_user
          MYSQL_PASSWORD: test_search_password
          MYSQL_ROOT_PASSWORD: test_search_root_password
          MYSQL_HOST: localhost

    steps:
      - checkout

      - restore_cache:
          name: Restore Yarn Package Cache
          keys:
            - yarn-packages-{{ checksum "./server/yarn.lock" }}
      - run:
          # フロント、サーバーを同一レポジトリでやる予定のため、./serverにNestJSを入れてあるので全体的にそのフォルダを指定してます。
          working_directory: ./server
          name: Install Dependencies
          command: yarn install --immutable
      - save_cache:
          name: Save Yarn Package Cache
          key: yarn-packages-{{ checksum "./server/yarn.lock" }}
          paths:
            - ~/server/.cache/yarn
      - run:
          working_directory: ./server
          name: lint
          command: yarn lint:ci
      - run:
          working_directory: ./server
          name: nest build
          command: yarn nest build
      - run:
          name: dockerize のインストール
          command: wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz
          environment:
            DOCKERIZE_VERSION: v0.3.0
      - run:
          name: db の待機
          command: dockerize -wait tcp://localhost:3306 -timeout 1m
      - run:
          working_directory: ./server
          name: migration run
          command: yarn typeorm migration:run -c test
          no_output_timeout: 1m
      - run:
          working_directory: ./server
          name: Verifies that the current database is up to date.
          command: yarn typeorm migration:generate -c test --ch -n Test
          no_output_timeout: 1m
      - run:
          working_directory: ./server
          name: app test
          command: yarn test
          no_output_timeout: 1m
      - run:
          working_directory: ./server
          name: e2e test
          command: yarn test:e2e
          no_output_timeout: 3m

workflows:
  server:
    jobs:
      - test-server

接続設定関係

接続情報にはormconfig.tsを用いておりこんな感じ。

default を環境変数で切り替えでもいいけど、もしもの事故を防ぐ意味も兼ねて明示的にbtestを使うようにしている。
マスタデータ系の投入にもmigrationを用いているため、synchronizeは使わず、migrationsRunでマイグレーションを実行している。
マイグレーションファイルも、DB名を変えたり、不要なデータを省いたり専用に用意している。


ormconfig.ts

// https://github.com/typeorm/typeorm/blob/master/docs/connection-options.md
module.exports = [
  {
    name: 'default',
    type: 'mysql',
    // 省略
  },
  {
    name: 'test',
    type: 'mysql',
    host: process.env.TEST_DB_HOST,
    port: process.env.TEST_DB_PORT,
    username: process.env.TEST_DB_USERNAME,
    password: process.env.TEST_DB_PASSWORD,
    database: process.env.TEST_DB_DATABASE,
    synchronize: false,
    dropSchema: true,
    entities: ['src/**/*.entity.ts'],
    migrationsRun: true,
    migrations: ['src/migration_test/*.ts'],
    logging: false,
  },
]

circleci/mysqlイメージを使ったら固まったので普通のMySQLイメージを使った


CircleCIが用意したMySQLイメージがあり、インメモリ設定も簡単にできるので使っていたが、実行すると途中で固まってしまうため、通常のMySQLイメージを使用した。

https://hub.docker.com/r/circleci/mysql

こっち使ってる
https://hub.docker.com/_/mysql


接続もクエリ発行も問題ないけど下記の箇所で何も返さずに固まってタイムアウトするので困った。

yarn run v1.22.5
$ ts-node $(yarn bin typeorm) migration:show -c test
query: SELECT * FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = 'test_search_db' AND `TABLE_NAME` = 'migrations'
query: CREATE TABLE `test_search_db`.`migrations` (`id` int NOT NULL AUTO_INCREMENT, `timestamp` bigint NOT NULL, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB
query: SELECT * FROM `test_search_db`.`migrations` `migrations` ORDER BY `id` DESC
# このまま音信不通となりタイムアウト・・・

Experimental の記載もあるし、実行速度も問題ないので一旦このまま。将来的にまた試して見ようと思う。

CI用のlintコマンドを追加する

レビューのときもちょくちょく忘れがあったので欲しかったやつ。
yarn lintで実行できるコマンドはあるが--fixが付いていて直せるところは直したあとの結果が返ってしまうので、fixなしのコマンドをつくって実行するようにした。

package.json

  "scripts": {
    "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
    "lint:ci": "eslint \"{src,apps,libs,test}/**/*.ts\"",

テスト用DBを使ったテスト実行

AWS で専用に立てなきゃ・・・と思ってたら簡単にDockerで動かせるので超便利だった。時代に追いつけてない。

マイグレーション漏れが無いか確認

現状のマイグレーションを実行後のテーブルと、Entityに差異が無いかをチェックする。
これもレビュー時に手動で確認してて、ちょくちょく問題になったので欲しかった。
typeormのCLIで下記のように--ch をつければ確認できる。なければ0, あれば1でエラー。

  • nは使われることはないが必要なので適当に。

事前にtypeorm migration:runが必要なのでCI側で直前に実行するようにしている。

typeorm migration:generate -c test --ch -n Test

できた!

プッシュすると一通りのテストがCIで動くやつ、5年以上前から憧れてた気がするけどようやく形にできた気がする。
新規のタイミングでないとなかなか難しい。
これを維持できるようにがんばりたい。

f:id:uyamazak:20211004154841p:plain