NRIネットコム社員が様々な視点で、日々の気づきやナレッジを発信するメディアです

注目のタグ

    【Git】huskyを導入してソースコードの品質管理をしてもらう

    本記事は  【いまさら聞けない○○ウィーク~Git編~】  2日目の記事です。
    🍦  1日目  ▶▶ 本記事 ▶▶  3日目  💻

    はじめに

    入社7年目の小林です。今日はhuskyのことをお話していきたいと思います。 2年くらい前の案件で、パートナーのエンジニアさんがサラリと導入してくれてからずっと便利に使わせてもらっています。 huskyは、ソースコードの品質管理や、レビューの効率化を図ることができるありがたい仕組みです。まだ導入していないよという方は是非ご一読ください。 huskyの導入に関する記事は既に多く上がっていますが、自分なりの見解を含めた形でお伝えできればと思います。

    huskyとは

    typicode.github.io huskyとは、Git hooksをトリガーに、タスクの実行をプログラムできるnpmライブラリです。
    具体的には、ソースをコミットする前にLintやFormatterのタスクを実行させたり、リモートにプッシュする前にTestのタスクを実行させたりすることができます。タスク実行中にエラーが検知された場合は、コミットやプッシュに至らない、というような動きになります。(例えば、プッシュ前のタスクとしてTestを組み込んだ場合、Testが通過可能なソースコードになるまでプッシュが成功しません。)

    もちろんhuskyが無くともこれらのタスクは実行できますが、タスクの実行漏れを確実に防ぐことができる(タスクが機械的に実行される)ので、ソースコードの品質が担保されやすいというメリットがあります。 複数人で開発する場合は、レビュワーと開発者の間でやりとりが発生しますが、 レビュワーとしては、huskyで検知している部分は注視しなくて良くなるので、レビューの効率化を図ることができます。 開発者としても、レビューに出す前にケアレスミスに気付くことができるので、安心して開発することができます。

    タスクを機械的に実行するという点ではCI(※)も同じですが、 CIはプッシュ後に初めて動作するのに比べて、huskyはローカル環境でのGit Hooksを検知して動作します。より早い段階で検知することができるという点で、CIを導入しているプロジェクトにおいてもhuskyの導入は有用だと思います。

    ※ CI … 継続的インテグレーション(Continuous Integration)。TestやBuildなどのタスク実行を自動化し、ソースコードの品質管理を行う手法。

    huskyを導入する

    実案件での実装をベースに導入手順を簡単に紹介していきます。pre-commit(コミット前に実行されるhook)、pre-push(プッシュ前に実行されるhook)に対して、それぞれ実行したいタスクを定義していきます。 huskyの公式のスタートガイド も合わせてご参照ください。

    開発環境(ざっくり)

    • React(TypeScript, scss)
    • Node v14.16.0
    • Formatter:Prettier
    • Lint:ESLint, StyleLint, CSpell
    • Test:Jest, Testing Libarary

    huskyによる処理

    • pre-commit:Formatter, Lintの実行
    • pre-push:Testの実行など(詳細は割愛)

    1. 必要なライブラリをインストールする

    以下コマンドでhusky, lint-stagedをインストールします。

    npx husky-init && npm install && npm install --save-dev lint-staged
    

    lint-stagedはGit上でステージングされた変更に対してLintを実行できるnpmライブラリです。 lint-stagedをpre-commitに組み込むことで、Lintを通過した場合にのみコミットが成功するような処理を実行できます。

    github.com

    2. lint-stagedで実行するタスクを定義する

    lint-stagedで実行したいタスクをpackage.jsonに定義します。 今回はpre-commitで実行したいタスクをlint-stagedタスクとして定義していきます。

    {...
      "lint-staged": {
        "src/**/*.{ts,tsx}": [
          "cspell",
          "prettier --write",
          "eslint --fix"
        ],
        "src/**/*.scss": [
          "cspell",
          "prettier --write",
          "stylelint --fix"
        ]
      }
    }
    

    3. Git hooksの設定ファイルを用意する

    huskyのインストールが完了すると、プロジェクトのルートに.huskyというディレクトリが生成されます。huskyに関する設定・処理はこのディレクトリ配下で定義していきます。 pre-commitの設定ファイル(.husky/pre-commit)はデフォルトで用意されているので、実行するタスクをセットするのみでOKです。pre-pushについては、設定ファイルの作成 & 実行するタスクのセットを行います。

    // pre-commitで実行するタスクをセット
    npx husky set .husky/pre-commit "npx lint-staged"
    
    // pre-pushの設定ファイルを作成 & 実行するタスクをセット
    npx husky add .husky/pre-push "npm run tsm && npm run tsc && npm run test"
    

    4. huskyを動かしてみる

    実際にコミット・プッシュの操作を行い、pre-commitやpre-pushで設定したタスクが実行されているかを確認してください。 エラーが検知された場合、コミット・プッシュの操作は実行されずに終了します。 エラーが検知されなかった場合は、コミット・プッシュの操作がそのまま実行されます。

    エラーが検知され、コミット・プッシュの操作が実行されずに終了した場合の出力例

    $ git commit
    husky > pre-commit (node v14.16.0)
    ✔ Preparing...
    ⚠ Running tasks...
      ↓ No staged files match src/**/*.{ts,tsx} [SKIPPED]
      ❯ Running tasks for src/**/*.scss
        ✖ cspell [FAILED]
        ◼ prettier --write
        ◼ stylelint --fix
    ↓ Skipped because of errors from tasks. [SKIPPED]
    ✔ Reverting to original state because of errors...
    ✔ Cleaning up...
    
    ✖ cspell:
    1/1 ./src/styles/variables.scss 563.86ms X
    CSpell: Files checked: 1, Issues found: 1 in 1 files
    /Users/y4-kobayashi/Documents/git/sample-app/src/styles/variables.scss:80:2 - Unknown word (samplee)
    husky > pre-commit hook failed (add --no-verify to bypass)
    
    $ git push origin feature/husky_test
    husky > pre-push (node v14.16.0)
    ...
    
    Test Suites: 1 failed, 159 passed, 160 total
    Tests:       1 failed, 1531 passed, 1532 total
    Snapshots:   3 passed, 3 total
    Time:        7.546 s
    Ran all test suites.
    ...
    
    husky > pre-push hook failed (add --no-verify to bypass)
    error: failed to push some refs to 'https://xxxx/xxx/sample-app.git'
    

    エラーは検知されず、コミット・プッシュの操作がそのまま実行された場合の出力例

    $ git commit
    husky > pre-commit (node v14.16.0)
    ✔ Preparing...
    ✔ Running tasks...
    ✔ Applying modifications...
    ✔ Cleaning up...
    [feature/husky_test 81de7363a] test
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    $ git push origin feature/husky_test
    husky > pre-push (node v14.16.0)
    ...
    
    Test Suites: 160 passed, 160 total
    Tests:       1532 passed, 1532 total
    Snapshots:   3 passed, 3 total
    Time:        6.076 s
    Ran all test suites.
    ...
    
    remote: 
    To https://xxxx/xxx/sample-app.git
       d64efa449..1018c6cd7  feature/husky_test -> feature/husky_test
    

    補足:SourceTree(4.2.3)でGit hooksのスキップ問題が解消

    以前、SourceTreeのようなGitクライアントを使用している場合には、デフォルトでGit hooksがスキップされてしまう事象があり、各自.huskyrcでパスを通す手順を踏む必要がありました。この事象は2023年5月8日にリリースされたSourceTree(4.2.3)で解消されたようです。

    product-downloads.atlassian.com

    おわりに

    pre-pushにTestのタスクを組み込んでいたこともあり、husky導入当初は中々リモートにプッシュできない状況にやりにくさを感じることもありました(エラーは残っていてもざっくり他人にソースを見てほしい時など)。ただ、慣れてしまえば、開発者は細かい部分を気にせず開発に集中できますし、レビュワーもLintやTest実行に関する些細な指摘をわざわざしなくてよくなります。ソースコードの品質管理はhusky導入による大きなメリットですが、個人的にはレビュワーと開発者の関係性を保つ上でも重要なものだと感じています。導入してくれたパートナーのエンジニアさん、ありがとうございました。

    執筆者 : 小林優莉

    飲み会は最後までビールなフロントエンドエンジニア🍺