nakamura244 blog

所属団体とは関係なく、個人的なblog

Go Conference Fukuoka 2019から刺激を受けてanalysis toolを作った

はじめに

先週Go Conferenceに参加させてもらいました。そこで良い刺激をもらい、その勢いでtoolを作ったのでblogに残しておこうと思います

Go Conference'19 Summer in Fukuoka - Go Conference'19 Summer in Fukuoka

聞いたセッション

たくさんのセッションを聞かせてもらった。がタイトルの通りその中でもより良い刺激を受けたセッションが下記です

Goによる静的解析のはじめかた - Google スライド

Go Conference'19 Summer in Fukuokaで登壇してきた | おそらくはそれさえも平凡な日々

このセッションを聞いて、よし自分もanalysis tool作ろうっと思った

作ったtool

github.com

どんなtoolか?

go fileのimport Pathを抽出して、あらかじめ定義した依存ルールに違反をしていないかを静的解析するツールです

会社のプロダクトでCleanArchitectureを採用しているものがあります。そのArchitectureの一つのポイントがDIP(Dependency Inversion Principle) = 依存関係逆転の原則 というものがあります。

このルールにちゃんと則っているかのチェックが、go fileのimport部分を見れば簡単にチェックができると思ったので作りました

しかし、実はこの手のツールは既にありました。

GitHub - roblaszczak/go-cleanarch: Clean architecture validator for go, like a The Dependency Rule and interaction between packages in your Go projects.

最初はこのツールを使っていました。が、下記の不満点が自分の中で出てきました

感じた課題感

  • golang.org/x/tools/go/analysisを使ったらもっと簡単にできるなぁ
  • プロジェクトによってレイヤー名が異なるのでその辺はそれぞれにconfigで可変できるようになると使いやすいなぁ
    • このツールでは決めうちされていた
  • それぞれのレイヤーでディレクトリを掘っていくとpackage名が必ずしもレイヤー名とならない場合があって、その場合困った
├── infrastructures
│   ├── a
│   │   ├── b.go

上記の場合、bというファイルのpackage名はaとしている場合とinfrastructuresとしている場合があると思う。前者のパターンにしている場合、このツールではうまく解析ができなかった

  • 自分で作ったパッケージの依存関係はチェックできるけど、ビルドインのパッケージ、サードパーティーのパッケージの依存のチェックはできなかった。
    • CleanArchitectureだとexternalレイヤー以外では外のpackageのimportは禁止されてるけど、そのチェックができていない
    • 例えば、CleanArchitectureでいうinterfaces層からビルドインパッケージのimportの良し悪し。domain層からのビルドインパッケージのimportの良し悪し。等はおそらくチームの規約を決めて運用する必要があるが、その辺りも規約をカスタムしてチェックしたいなーと思っていた

モチベーション

上記の課題感を自分で解決してみようと思い、勉強がてら作った

話を戻して今回作ったtool(dependency-check)はどういう風に使うのか

readmeにも色々書いたが改めて。

1. まずはinstallしてください

go get github.com/nakamura244/dependency-check

2. configを配置します

pathはどこでも大丈夫ですが、外部のCIツールでも活用する場合は、該当のレポジトリ内に設置する方が良いかと思います

ex:

base:
  innerPath: &innerPath github.com/xxx/xxx  # チェックをしたいレポジトリのパスを記載

layer1:
  layerName: infrastructures     # レイヤー名を任意でつけます
  packageNames:
    - github.com/xxx/xxx/infrastructures          # layerNameに属するパッケージ名をパスから記載(複数設定可能)
  innerPath: *innerPath                           # エイリアス
  allowDependPackages:                            # 依存して良い(import可)パッケージ名をパスから記載(複数設定可能)。ただし同じリポジトリ内のパッケージに限る。nullの場合は同リポジトリ内のパッケージの依存を許さないという設定になる
    - github.com/xxx/xxx/domain
    - github.com/xxx/xxx/interfaces
    - github.com/xxx/xxx/config
    - github.com/xxx/xxx/infrastructures/iface
  allowDependBuildIn: yes                             # infrastructuresレイヤーはgoのbuildinパッケージに依存しても良い(import可)かどうかの設定。値は`yes` or `no`
  allowDependOutside: yes                             # infrastructuresレイヤーはgoの外部のパッケージに依存しても良い(import可)かどうかの設定。値は`yes` or `no`

上記はlayer1の設定ですが、layer4まで設定可能です

下記のexampleを見てもらっても良いかと思います

https://github.com/nakamura244/dependency-check/blob/master/testdata/src/valid/.dependency-check/config.yml

3. Run

実行したいリポジトリに移動して下記のコマンドを実行

dependency-check -ignoreTests=true -config=./.dependency-check/config.yml ./...
  • -ignoreTests=trueはtestファイルは無視するかどうかの設定
  • -config=./.dependency-check/config.ymlは2で作成したconfig yamlのパスを設定
  • ./...再帰的に解析したいパス

4. Result

失敗した時はexit status 1で終了します。

出力は成功の場合、失敗の場合は下記のように標準出力されます https://github.com/nakamura244/dependency-check/blob/master/README.md#run

注意!!

CircleCIで組み込んで利用する場合、environmentGOROOTを指定しないと期待値通りに動きません。自分の環境下ではそうでしたというだけかもしれないけど

https://github.com/nakamura244/dependency-check/blob/master/.circleci/config.yml#L63

https://github.com/nakamura244/dependency-check/blob/master/README.md#caution

実際に会社のプロダクトに適用してみた

CircleCIを使っていてそのJOBの一つに加えました f:id:tsuyoshi_nakamura:20190719112345p:plain

workflowの一部にdepcというjobを追加して、dependency-checkを実施しています。このjobがfaildすると次のjob(deploy)ができない感じになっています

最後に

運営の方々には良い機会を提供いただき感謝しかありません。お疲れ様でした。

やっぱり実際に足を運んで、技術に触れる事は成長につながるなーと改めて思いました。

おまけ

スペシャルゲストで福岡市長が来てくれました。政治家ってプレゼンテーションが超絶うまいんだなーと体験しました

福岡市長高島宗一郎 GO conference'19 summer in fukuokaに出席しました - YouTube