コピペコード検出で定番のPMDはdockerから使うと楽

PMDをつかうとコピペコードの検出ができる

ある程度プロジェクトが進捗して運用フェーズに入る頃、コードの要所要所にコピペの跡が垣間見えた、なんて経験があるエンジニア諸氏は結構いるのではないでしょうか。

こういった重複コード(いわゆるコピペコード)を検出するのに便利なツールとして、比較的枯れたものの一つにPMDというものがあります。

このPMDはもともとJavaのソースコードについて、以下のような、リファクタリングに役立つ解析を行ってくれるものです。Javaには詳しくありませんが、どうやらJavaでは定番のツールかと思われます。

  • 予測可能なバグの検出(空のtry/catchなど)
  • 未使用のコード(Dead code)の検出
  • 最適ではないコードの検出(無駄なString/StringBufferの使用など)
  • 複雑すぎるコードの検出(不要なif, whichに置き換え可能なforなど)
  • 重複したコードの検出

そしてPMDは今ではプラグインを通じ、Javaの他に以下の言語に対応しています。

  • C++
  • C#
  • Fortran
  • Go
  • JavaScript
  • JSP
  • Matlab
  • Objective-C
  • PHP
  • PL/SQL
  • Python
  • Ruby
  • Velocity
  • XMLおよびXSL
  • Scala

残念ながらElixirには未対応のようです。またPerlにはpmd-perlというプラグインがありましたので、もしかすると対応しているのかもしれません。

Docker Imageを使うと楽

さて、このPMDですが、Docker Hubにいくつかビルド済みイメージがあります。私のお気に入りは rawdee/pmd:6.19.0-alpine です。

では早速、コピペコードの検出を試してみましょう。

今回解析の対象としたのは frankrap/bybit-api です。これは暗号通貨取引所「ByBit」のGo向けAPIライブラリです。

これを対象として選んだ理由は、手元に既にあり、ざっとコードを読んでそこそこ重複するコードがありそうだと思ったのと、一般公開済みのコードだからです。

あなたの環境がdockerを利用可能な状態であれば、プロジェクトルート直下で以下のコマンドを実行するだけで、コピペコードの検出が可能となります。

$ docker run --rm -v $(pwd):/src rawdee/pmd:6.19.0-alpine \
cpd --language go \
--files ./**/*.go \
--minimum-tokens 100 \
--exclude ./**/*_test.go

実際に bybit-api に対して実行した結果が以下の通りとなります。

$ docker run --rm -v $(pwd):/src rawdee/pmd:6.19.0-alpine cpd --language go --files ./**/*.go --minimum-tokens 100 --exclude ./**/*_test.go
Found a 25 line (108 tokens) duplication in the following files:
Starting at line 197 of /src/./rest/api.go
Starting at line 251 of /src/./rest/api.go

log.Printf("PublicRequest: %v", fullURL)
}
var binBody = bytes.NewReader(make([]byte, 0))

// get a http request
var request *http.Request
request, err = http.NewRequest(method, fullURL, binBody)
if err != nil {
return
}

var response *http.Response
response, err = b.client.Do(request)
if err != nil {
return
}
defer response.Body.Close()

resp, err = ioutil.ReadAll(response.Body)
if err != nil {
return
}

if b.debugMode {
log.Printf("PublicRequest: %v", string(resp))

…Wordpressではネストが崩れてしまいましたが、上記のように、コピペ箇所の検出ができていることがわかるかと思います。ここでは ./rest/api.go の197行目から25行ぶんのコードについて、251行目にも同一のコードが登場していることを表しています。

それにしても、この長いdockerコマンドを毎回入力するのは非常に面倒なことです。もしご自身のプロジェクトでコピペ検出をちょいちょいやっていきたいと思っても、こんなに長いコマンドを入力するだけでやる気が萎えてしまうというものです。

ですので、Makefileに以下のようなタスクを追加しましょう(go言語のプロジェクトの場合です)。

cpd:
docker run --rm -v $(pwd):/src rawdee/pmd:6.19.0-alpine cpd --language go --files ./**/*.go --minimum-tokens 100 --exclude ./**/*_test.go

…また例によってネストが崩れていますが、docker の前にはTABを入れることをお忘れなく。ともあれ、このようにすることで make cpd とするだけでプロジェクト配下のコードについてコピペ検出が可能となります。

なお、上記のタスクはコピペコードが存在するとmakeに失敗しますので、このことを利用して各種CIに組み込むなどの継続的な措置を取ることも可能です。

そのほか

wordpressやめてrijiに戻ろうかなぁ…

Generating Kotlin code from Protocol Buffers (without JVN on your host)

Gradle? JVM? Looks as chainsaw in this case.

Protocol Buffers is famous IDL(Interface Description Language) in these days. And the uber/prototool is needful tool for generating interface codes in any languages.

However information about generating kotlin code is quiet few in today.

Basically I was knowledgeless about kotlin before generating kotlin codes. And the our project is written in golang.

Okay, then I found an entry that telling as “JVM and gradle are needed when generating kotlin code through protoc”.

Hmm, “place build.gradle will be in our project”? Sounds not so good. Because our project is written in golang. Don’t forget, I just want “generating kotlin codes from protocol buffers“. This method looks as cutting sashimi with chainsaw.

Conclusion: Docker solves as like sashimi knife

In this case, requirement to environment for solving the issue is so huge. “Huge requirement to environment… I’ve got it!” yes, it is the Docker Hub. So I found the container image that fits this case.

It is the moia/prototool-docker (codes on GitHub).

Usage is followings.

### prototool.dockerfile
FROM moia/prototool-docker

RUN mkdir /proj
WORKDIR /proj

CMD protoc --kotlin_out=./proto/kotlin ./proto/definition/*.proto

### In your terminal
$ docker build -f ./prototool.dockerfile -t prototool .
$ docker run --rm -v /path/to/myproj:/proj -t prototool

Finally I got succeed to generate kotlin code from protocol buffers, with moia/prototool-docker that is instead of JVM on my host.

YAPC::Fukuoka 2017 HAKATAでトークしました

YAPC::Fukuoka 2017 HAKATAにてトークしてきました。

PaaSをつかって怠惰・短気・傲慢にシステムを作るためのマインドセットについての話です。当日は徳丸さんの裏番組ということもあり、人が来てくれるのかと心配しておりましたが、思いの外結構人がいらっしゃったので、話した甲斐があったと思っております。

これを機に「Azureってすごいじゃん!」とか「Azure PaaSで作ってみようかな」と思ってもらえれば本望です。

スタッフの皆様、会場提供してくださったLINE様、その他YAPC::Fukuoka成功に向けて尽力された皆様に、改めて御礼申し上げます。ありがとうございました。

Tumblrに引越しました

横着しに来ました。

今まで技術系エントリをRiji + Docker Cloud + Digital Ocean と言う構成でちょいちょい頑張ってきました。

技術的には結構チャレンジングで面白い構成でしたし、何だかんだでそこそこ安定稼働していた構成だったのですが、後述する様な問題があって、ちょっとそのまま継続するのもなんか無駄があるなぁと思い立ち、この度Tumblrにメインの技術系ブログをお引越しすることに決めました。

そもそもブログのためにコンテナを起動するのは大鉈

「ブログサービスがいっぱいある世の中ですし、それらのうちのどれかを使えばいいじゃないの」と言うのが正直な感想です。

ただ、そこそこの期間Dockerで安定稼働させたという実績は作れたのかなぁ・・・

まあ今となってはどうでもいいですね。

更新のたびにリロード作業を行うのが面倒

Riji自体がそもそも多分Staticなコンテンツを生成して使ってくださいね、という代物だと思うんですが、それをわざわざ riji serve して頑張ってたのはちょっと無駄が過ぎましたね。

そんなもんで、何かエントリを書いた後はコンテナのリロードが必要だったりしたんです。まぁ本当に無駄な労力ですね。我ながらご苦労さんなこった、と思います。

とはいえこちとら趣味でやってるだけなんで、無駄だろうがなんだろうが知ったこっちゃないんですがねw

Digital Oceanに微量ながらお金がかかる

Digital Oceanは確かにやっすいんですが、いちいち利用料をチャージしておくのがだるいし、1ヶ月で牛丼並盛りが1杯食えるくらいのお金を持って行かれるので、じゃあその分牛丼に回したほうがQoL高いんじゃね?と思ったわけです。

Dockerコンテナのご機嫌を見るのがだるい

そんなにコンテナがご機嫌を悪くすることってないんですけど、高々ブログの運営に、何らかの技術的問題を自力でどうにかするのってちょっと省エネではないなぁって思いました。

時折あったのが、haproxy:latestがなぜか固まってしまって、全くブログが見れなくなってしまうという現象。多分これはdigital oceanのdroplets(kvm?)との食い合わせの問題なのかなぁ?と考えてますが、もう使わないしいいや・・・

まとめ

技術的な趣味というか実験というか、そういうのを兼ねてDockerを使ってブログを公開してきましたが、億劫になってしまったのでTumblr寄せすることにしました。

なお、過去のブログは新しい順に以下の通りとなっております(引越し2回目)。

それでは今後もよろしくお願いします。