無秩序な既存プロジェクトのコーディングスタイルをチームで無理なく揃えていく


ZOZOテクノロジーズのむーさん@murs313です。
WEARが大好きで入社し、今はWEARのwebアプリの開発をしています。

WEARは今年のハロウィンで7才になりました。
歳を重ね、様々な人がコードを書き、コーディング規約もなく、WEARのコードはこげな状態になっておりました…。

ほげえええええええ!! インデント!!!インデントォォ!!!
ファイルによって改行コードも違うやないか…!!なんてこった\(^o^)/
このままではくしゃみと鼻水でコードが書けない…。私はコーディングスタイルを統一する決意をしたのでした。

プロジェクトのコーディングスタイルなので、ひとりでやっても意味がありません。チームでやっていきたいです!!と話したら、チームメンバーも賛成してくれました。

まずは書き方の振れ幅が大きいJavaScriptをESLintでチェックすることにしました。

やること

  • CircleCIでESLintによるJSファイルのコーディングスタイルのチェックを行う。ただし無理がないように、そのプルリクエストで触ったファイルだけをチェックする。
  • 運用が大変にならないように、errorにするルールは少なめにする。
  • errorが出ても面倒くさくならないように、一発でfixできるshellを作っておく。

手順

1. コーディング規約を決める

コーディング規約がない場合、まずはチームでコーディング規約を決めることから始めます。
細かいところはまずはスタンダードに乗っかると楽なので、「みんなが常日頃気になっているところ」だけ話しておくのが良いと思います。

  • インデント(ソフトインデント / ハードインデント)
  • 改行コード(LF / CRLF)
  • 括弧の位置 等々…

話し合いの段階でESLintの推奨設定やJavaScript Standard Styleを見ておくと、「推奨や標準はこうなんだ」と参考になります。

2. ESLintの導入

前述の通りこの記事では解説を割愛しますが、こちらの記事がとても素敵です。(npmの基礎から解説されていて、パッケージもグローバルインストールしていません)
以下淡々と導入手順を記していくので、よく分からなかったら上記の記事を参照してみてください。

npmが動く環境であることを前提とします。
package.jsonがないプロジェクトなら、下記コマンドでpackage.jsonを作ります。

$ npm init -y

ESLintをインストール。

$ npm install --save-dev eslint

ESLintを簡単に実行できるように、スクリプトを定義しておきましょう。package.json"scripts"を下記のように書き換えます。

package.json
  "scripts": {
    "lint": "eslint",
    "fix": "eslint --fix"
  },

3. ESLintのルールを整備する

いよいよコーディングルールを設定していきましょう。
プロジェクトフォルダの直下に.eslintrc.jsonというファイルを作ります。

.eslintrc.json
{
  "extends": "eslint:recommended"
}

"eslint:recommended"はESLintの推奨設定に則ることを意味します。
ESLintのコーディングルールは恐ろしいほどたくさんあり、ひとつずつチェックするのは無謀であるため、ここから調整していくのがおすすめです。(参考:ESLintのルールを全部手動で設定するのは大変だからやめておけ

先ほどスクリプト定義したので、下記のようにコマンドを打つとESLintが動きます。

$ npm run lint [jsファイルのパス]

errorやwarnがたくさん出るかもしれませんが、先ほどの素敵記事を参考に、jQueryのプロジェクトなら"env""jquery"を追加したり、"globals"に変数を設定したり、みんなで決めたルールに沿って"rules"を追加したりして調整していきます。

eslintrc.json
{
  "env": {
    "browser": true,
    "jquery": true
  },
  "extends": "eslint:recommended",
  "rules": {
    "no-redeclare": "warn",
    "no-unused-vars": "warn",
    "camelcase": "warn",
    "linebreak-style": ["error", "unix"],
    "indent": ["error", "tab", { "SwitchCase": 1 }],
    "eol-last": ["error", "always"],
    "no-trailing-spaces": "error",
    "no-multiple-empty-lines": ["error", { "max": 1 }],
    "space-before-blocks": ["error", "always"],
    "quotes": ["error", "single"],
    "semi": ["error", "always"],
    "comma-style": ["error", "last"],
    "space-before-function-paren": ["error", "never"],
    "func-call-spacing": ["error", "never"],
    "block-spacing": ["error", "always"],
    "array-bracket-spacing": ["error", "never"],
    "no-array-constructor": "error",
    "spaced-comment": ["error", "always"]
  }
}

今回はESLintでfixできるものは遠慮なくerrorにし、簡単にfixできないものはwarnにしました。CircleCIが落ちても、直せば通ることを簡単に体感してもらうためです。

4. 差分ファイルだけlintとfixができるshellを書く

2つのshellを作って、今回はプロジェクトディレクトリ直下に起きます。

  • lint.sh
    masterとの差分があるjsファイルだけlintします。
    CircleCIに載せる用。ローカルでも動きます。
  • lint-fix.sh
    masterとの差分があるjsファイルだけfixします。
    ローカルでESLintが直せるerrorを直します。
lint.sh
#!/bin/bash

echo 'CIRCLE_BRANCH: ' ${CIRCLE_BRANCH}
TARGET_BRANCH=${CIRCLE_BRANCH}

# masterブランチへのmerge時はチェックしない
if [ "$TARGET_BRANCH" = 'master' ]; then
  echo 'SKIP on merge into master'
  exit 0
fi

# ローカルでの実行用にカレントブランチをセットする
if [ "$TARGET_BRANCH" = '' ]; then
  TARGET_BRANCH=$(git rev-parse --abbrev-ref HEAD)
fi
echo 'TARGET_BRANCH: ' $TARGET_BRANCH

BASE_BRANCH=origin/master
echo 'BASE_BRANCH: ' $BASE_BRANCH

files=$(git diff --name-only $TARGET_BRANCH $BASE_BRANCH | grep -E '.js$')

error=false
for file in ${files}; do
  npm run lint -s -- ${file}
  result=$?
  if [ $result -ne 0 ]; then
    error=true
  fi
done

if $error; then
  exit 1
fi

exit 0

lint.shはこちらの記事から拝借しました。

lint-fix.sh
#!/bin/bash

# origin/masterをベースブランチにセット
BASE_BRANCH=origin/master
echo 'BASE_BRANCH: ' $BASE_BRANCH

files=$(git diff --name-only $BASE_BRANCH | grep -E '.js$')

error=false
for file in ${files}; do
  npm run fix -s -- ${file}
  result=$?
  if [ $result -ne 0 ]; then
    error=true
  fi
done

if $error; then
  exit 1
fi

exit 0

ファイルができたら、適当なjsファイルに差分を作ってから、下記のように実行して試してみましょう。

$ ./lint.sh
$ ./lint-fix.sh

5. CircleCIに導入

CircleCI側のリポジトリの追加ができている前提で、.circleci/config.ymlに下記を記述します。

.circleci/config.yml
version: 2
jobs:
  build:
    docker:
      - image: circleci/node:10.16.0
    steps:
      - checkout
      - run: npm install
      - run: ./lint.sh

nodeが使えるimageを用意して、./lint.shしています。
ここでは当時最新のLTS(long term support)だったnode v10系を使っています。

これをプッシュすれば、CircleCIによるlintが走るはずです!
プルリクの最後の方にこんな感じで表示されると思います。
CircleCIが落ちたバージョン

CircleCIが通ったバージョン

6. チームメンバーに説明・各々の環境やエディタで使えるように設定

1番大事です。下記のようなことを伝えましょう。

  • プルリクにこんなの付くよ
  • エラーメッセージはこうやって見るよ
  • レビュワーになったら、CircleCIが落ちていたら伝えてね

また、チームメンバー各々の環境やエディタで使えるように、nodeの環境構築やプラグインを入れる時間を取ると良いと思います。

最後に

この記事に書いたシステムの導入は今年7月に行われた開発合宿で行いました。足湯コーディングめっちゃ良かった〜〜〜!
足湯コーディングしたい方はJOIN US!!!

スペシャルサンクス

  • 弊社の素敵文化・フロントエンド共有会で相談に乗ってくれた@AmatsukiKuさん
  • 合宿で罵詈雑言と共にCircleCIのことを教えてくれた@inductor(呼び捨て)