CIでPrettier/ESLintを実行して違反時にWorkflowを失敗させる

2022.04.26

こんにちは、CX事業本部 IoT事業部の若槻です。

PrettierとESLintは、JavaScript系のプロジェクトでよく使われる構文チェックツールです。

Prettierは主にコードのスタイルを整形するフォーマッターです。ESLintはコード内の不要な記述の削除を促すリンターです。両者は役割が異なる(重複する部分もありますが)ため一緒に使われることが多いです。

今回は、PrettierおよびESLintをCI上で実行して、違反時にWorkflowを失敗させる方法を確認してみました。

環境準備

今回はAWS CDKプロジェクト(TypeScript)で試してみます。

プロジェクトを作成します。

$ npx cdk init --language typescript

PrettierとESLintおよび周辺ライブラリをインストールします。

$ npm install -D \
  prettier \
  eslint \
  eslint-config-prettier \
  eslint-config-standard \
  @typescript-eslint/eslint-plugin

PrettierとESLintがインストールできました。

$ npm ls --depth=0
├── eslint@8.13.0
├── prettier@2.6.2

その他ライブラリは次のような用途で入れています。

.eslintrc.jsonファイルを作成して、ESLintのPrettierとの併用およびTypeScriptで利用するための設定を記述します。

.eslintrc.json

{
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "prettier"
  ],
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "project": "./tsconfig.json"
  },
  "plugins": [
    "@typescript-eslint"
  ]
}

また.prettierrc.jsonファイルを作成して、Prettierのフォーマッティングルールを定義します。

.prettierrc.json

{
  "singleQuote": true,
  "trailingComma": "all",
  "tabWidth": 2
}

周辺ライブラリとしては他にもeslint-plugin-importeslint-plugin-nodeeslint-plugin-promiseなどをよく導入しますが、今回は一旦ここまでとします。

CLI実行時のExit code

CI/CDツールではExit codeが0以外の時にWorkflowの実行が失敗となります。まずはPrettierおよびESLintのCLI実行時のExit codeの仕様について確認してみます。

Prettier

Prettierでは--checkフラグを使用すれば、CLIコマンドの実行でファイルのフォーマットチェックができます。

フォーマットの違反が無ければExit codeは0となります。

$ npx prettier --check lib
Checking formatting...
All matched files use Prettier code style!

$ echo $?
0

違反があれば1となります。

$ npx prettier --check lib
Checking formatting...
[warn] lib/aws-cdk-v2-project-stack.ts
[warn] Code style issues found in the above file(s). Forgot to run Prettier?

$ echo $?
1

ESLint

ESLintが検知する違反にはerrorswarningの2種類があり、warningのみ検知時にExit codeを0とするには--max-warningsの設定が必要です。

違反が無ければExit codeは0となります。

$ npx eslint src --max-warnings=0
$ echo $?
0

違反があれば1となります。warningが一つ検知されています。

$ npx eslint lib --max-warnings=0

/Users/wakatsuki.ryuta/projects/cm-rwakatsuki/aws-cdk-v2-project/lib/aws-cdk-v2-project-stack.ts
  87:7  warning  'aaa' is assigned a value but never used  @typescript-eslint/no-unused-vars

✖ 1 problem (0 errors, 1 warning)

ESLint found too many warnings (maximum: 0).
$ echo $?
1

CI上で実行してみる

prettiereslintの実行コマンドをpackage.jsonのscriptsに設定してプロジェクト上でコマンド実行できるようにします。

package.json

{
  "scripts": {
    "format": "prettier --check './{bin,lib,src,tests}/**/*.{ts,tsx}'",
    "lint": "eslint './{bin,lib,src,tests}/**/*.{ts,tsx}' --max-warnings=0"
  }
}

上述のコマンドをCI/CDツール(今回はGitHub Actions)のWorkflow fileに記述して実行されるようにします。

.github/workflows/cicd.yaml

on: 
  push:
    paths-ignore:
      - '**/*.md'

jobs:
  integrate:
    runs-on: ubuntu-latest
    steps: 
      - name: Checkout
        uses: actions/checkout@v3

      - name: Cache CDK Dependency
        uses: actions/cache@v3
        id: cache_cdk_dependency_id
        env:
          cache-name: cache-cdk-dependency
        with:
          path: node_modules
          key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('package-lock.json') }}
          restore-keys: ${{ runner.os }}-build-${{ env.cache-name }}-

      - name: Install Dependency
        if: ${{ steps.cache_cdk_dependency_id.outputs.cache-hit != 'true' }}
        run: npm ci --no-audit --progress=false --silent

      - name: Lint & Format
        run: |
          npm run lint
          npm run format

Workflowを実行します。ESLintでリント違反となり、Workflowが失敗しました。

リント違反部分を修正し再度Workflowを実行します。次はPrettierでフォーマット違反となり、Workflowが失敗しました。

フォーマット違反を修正します。

$ npx prettier --write './{bin,lib,src,tests}/**/*.{ts,tsx}'
bin/aws-cdk-v2-project.ts 294ms
lib/aws-cdk-v2-project-stack.ts 31ms

再度Workflowを実行すると、今度はリントもフォーマットも違反無くパスし、Workflowが成功しました!

おわりに

PrettierおよびESLintをCI上で実行して、違反時にWorkflowを失敗させる方法を確認してみました。

huskyを使用してpre-commit時にフォーマット/リントを強制する宗派もありますが、私は今回のようなCI/CDツール上にオフロードする方が好みです。

参考

以上