LoginSignup
10
9

More than 3 years have passed since last update.

フロント・バックエンドサービスをコンテナ化してもGitコミット時にLefthookでテストやLint実行

Posted at

TL;DR

  • フロント・バックエンドサービスをそれぞれコンテナ化、docker-composeで全てのコンテナを管理する
  • monorepoで管理した際に1リポジトリとなるので気軽にGit Hookの処理ができない
  • Lefthookを導入してpre-commit時にすべてのコンテナに対してLintツールを動作させるようにした

サンプルコード

1リポジトリで開発環境を管理したい

渋川さんの記事

マイクロサービスほどじゃないけどウェブサービスを分割開発したい人向けDocker設定を集めるスレ
https://qiita.com/shibukawa/items/fd49f98736045789ffc3

を読んでフロントエンドとAPIがごっちゃになっている開発環境ヨクナイ!ってことでサービス単位でコンテナ化してvs codeのリモートコンテナ機能を使って開発環境を再構築をしていたらGitとGItHooksの扱いで躓く。

Git Hooksの扱い

1リポジトリでサービスをコンテナ化してmonorepoを構築した際に.gitはルートディレクトリのみに存在する。
何が起こるかというと、フロントエンド開発時に「Husky+lint-stagedでコミット前にeslintやprettierを実行してコミット前にソースをチェックする」ができなくなります。これは治安が悪くなるってことで調査を進めた結果、試してみたのがLefthookです。

Lefthookを使ってみる

Lefthookとは?ということで公式の説明を引用

The fastest polyglot Git hooks manager out there

Fast and powerful Git hooks manager for Node.js, Ruby or any other type of projects.

  • Fast. It is written in Go. Can run commands in parallel.
  • Powerful. With a few lines in the config you can check only the changed files on pre-push hook.
  • Simple. It is single dependency-free binary which can work in any environment.
  • GO製。コマンドを並列実行できる
  • かんたんな設定ファイルでpre-pushのhookが使えるようになる
  • (GOによる)シングルバイナリなので、どのOSでも実行可能

今回は以下のことを実装しました。

  • 起動時にdocker-composeでインスタンス起動
  • 起動したインスタンスに対してコマンドを実施
  • 更新対象のファイルの拡張子をgrepして対象の拡張子がstageにあるときのみ実行する

※、余談ですが日本語での紹介記事は2つのみ。1つは公式の翻訳ともう一つはRubyでのHusky置き換え記事です。

Lefthook: 多機能GItフックマネージャ
https://techracho.bpsinc.jp/hachi8833/2019_10_16/79052

Git HooksマネージャーのLefthookを試してHusky(+lint-staged)と比較した結果、乗りかえました
https://blog.solunita.net/posts/change-lefthook-instead-of-lintstaged-with-husky/

Lefthookインストール

Lefthookインストールですが、公式サイトのInstallationか、リリースページから直接ダウンロードします。自分はWindows環境なのでプロジェクト内にlefthook.exeをそのまま置いて使っています。
インストール後に対象のリポジトリで以下のコマンドを実行

lefthook install #Windowsで直下に置いている場合は lefthook.exe install

インストールが完了するとリポジトリのルートに「lefthook.yml」が作成されますので、こちらに設定を書きます。

Lefthook設定ファイル解説

lefthook.yml
# EXAMPLE USAGE
# Refer for explanation to following link:
# https://github.com/Arkweid/lefthook/blob/master/docs/full_guide.md
#
# pre-push:
#   commands:
#     packages-audit:
#       tags: frontend security
#       run: yarn audit
#     gems-audit:
#       tags: backend security
#       run: bundle audit
#
# pre-commit:
#   parallel: true
#   commands:
#     eslint:
#       glob: "*.{js,ts}"
#       run: yarn eslint {staged_files}
#     rubocop:
#       tags: backend style
#       glob: "*.rb"
#       exclude: "application.rb|routes.rb"
#       run: bundle exec rubocop --force-exclusion {all_files}
#     govet:
#       tags: backend style
#       files: git ls-files -m
#       glob: "*.go"
#       run: go vet {files}
#   scripts:
#     "hello.js":
#       runner: node
#     "any.go":
#       runner: go run

pre-commit:
    piped: true
    commands:
        1_docker-compose:
            root: .
            run: docker-compose up -d
        2_eslint:
            root: "containers/frontend/"
            glob: "*.{js,jsx,ts}"
            run: docker exec -it frontend-container yarn eslint-check
        3_frontend-test:
            root: "containers/frontend/"
            glob: "*.{js,jsx,ts}"
            run: docker exec -it frontend-container yarn test
        4_api-test:
            root: "containers/api/"
            run: docker exec -it api-container go test

サンプルとインデントの数が違うのはご愛嬌。今回使っている処理は以下の通り。

  • pre-commit: コミット実施前に実行してほしい処理
  • piped:commandsを名前順に実施する。そのため頭文字に数字をつける
  • commands:実行するコマンド
  • root:どの階層でコマンドを実施するかを記載
  • glob:stagedのファイルのうち、コマンド実行対象とするファイルを選別する
  • run:実行するコマンド

コマンドの内容ですが以下の処理を実施しています。

  • docker-composeでコンテナを起動
  • docker execを使用してフロントエンドコンテナでeslint+Prettierを実施
  • docker execを使用してフロントエンドコンテナでテストを実施
  • docker execを使用してAPIコンテナでテストを実施

フォルダ・ファイル構成

以下の想定でファイル構成を行っています。
- フロントエンドとAPIサーバをそれぞれコンテナ化して管理
- docker-composeでコンテナを一元管理
- フロントエンドではeslint+Prettierでのソース整形とテストを実施
- APIサーバではテストを実施
- それぞれ正常に実行時のみコミットを実施する

ファイル構成
lefthook-docker-node-go-dev-sample
│  .gitignore
│  docker-compose.yml
│  lefthook.exe
│  lefthook.yml
│  LICENSE
│  README.md
│
└─containers
    ├─api
    │      docker-entrypoint.sh
    │      Dockerfile
    │      go.mod
    │      go.sum
    │      main.go
    │      main_test.go
    │
    └─frontend
        │  .eslintrc.json
        │  docker-entrypoint.sh
        │  Dockerfile
        │  package.json
        │  README.md
        │  yarn.lock
        │
        ├─node_modules
        ├─public
        │      favicon.ico
        │      index.html
        │      logo192.png
        │      logo512.png
        │      manifest.json
        │      robots.txt
        │
        └─src
                App.css
                App.js
                App.test.js
                index.css
                index.js
                logo.svg
                serviceWorker.js
                setupTests.js

Lefthookでpre-commit時にコンテナに対してコマンド実行

pre-commitをrunしてみる。

実行結果
.\lefthook.exe run pre-commit
RUNNING HOOKS GROUP: pre-commit

  EXECUTE > 1_docker-compose
api-container is up-to-date
frontend-container is up-to-date

  EXECUTE > 2_eslint
yarn run v1.22.4
$ eslint --print-config .eslintrc.json | eslint-config-prettier-check
No rules that are unnecessary or conflict with Prettier were found.
Done in 0.69s.

  EXECUTE > 3_frontend-test
yarn run v1.22.4
$ CI=true react-scripts test
PASS src/App.test.js
  ✓ renders learn react link (39ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.081s
Ran all test suites.
Done in 3.23s.

  EXECUTE > 4_api-test
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> github.com/MegaBlackLabel/lefthook-docker-node-go-dev-sample.setupRouter.func1 (3 handlers)
[GIN] 2020/03/15 - 03:43:28 | 200 |      44.742µs |                 | GET      /ping
PASS
ok      github.com/MegaBlackLabel/lefthook-docker-node-go-dev-sample    0.014s

SUMMARY: (done in 7.78 seconds)
✔️  1_docker-compose
✔️  2_eslint
✔️  3_frontend-test
✔️  4_api-test

コマンドが順番に実行されそれぞれの実行結果が表示。最後にサマリーとしてOK・NGが出力されます。

まとめ

  • サービス単位でコンテナ化して開発するのは便利だね。でもGit Hooksの処理ができない
  • Lefthook使えばできるよ。シングルバイナリだから導入もかんたんだよ
  • コンテナ化してもGit Hooksが使えるので複数の開発者がいても治安が維持できそう

以上

10
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
9