やりたいこと
世の中にはソースコードの行数を知りたがる人がいます。
Gitリポジトリから詳細なステップ数を取得するツールとしてclocがあり、これだけで十分便利なのですが、一部のファイルはカウントから除外するとか、全体のステップ数と改修分のステップ数を出すとか、コメントを含めないのと含めたのとそれぞれ合算するとか、細かい集計を毎回手作業でやるのは地味に面倒です。
というわけで、シェルでJSONをいじる練習も兼ねて自動化してみました。
アウトプットイメージ
最終的にこんなものを作ります。
■ロジックのみ
・追加:1000
・修正:1000
・流用:1000
---
・計 :3000
■コメント・空行を含む
・追加:2000
・修正:2000
・流用:2000
---
・計 :6000
行単位で、追加されたものを「追加」、変更されたものを「修正」、変更のないものを「流用」としてカウントします。
実行環境
- Windows 10 PC
- 基本的にはLinuxでもできることしかやらないので、CI環境にもっていくこともできると思います
- git
- git bash(シェルの実行環境)
- cloc
- clocコマンドが使えるようにPATHを通しておきます
- Windows用のexeもありますが、npmからグローバルインストールすることも可能
-
jq
- BashでJSONを扱うため
- Windowsではscoopなどからインストールできます
- PATHを通しておきます
概要
以下のようなことをやります。
- Gitリポジトリからクローン
- 全ステップ数、差分ステップ数をclocで取得し、json形式で出力
- jsonをjqに渡し、必要な情報だけ取り出して集計
- テキストファイルとして保存
事前準備
ベースコミットのハッシュ値確認
改修のBeforeとなるgitコミットのハッシュ値を調べておきます。
このコミットと最新コミット(HEAD)までの差分ステップ数から、追加/修正/流用を判定します。
言語識別キーの確認
対象のソースが配置されているルートディレクトリで clocコマンドを打ってみます。
例としてPrismのソースであれば、以下のような結果が出力されます。
$ cloc ./
895 text files.
883 unique files.
278 files ignored.
github.com/AlDanial/cloc v 1.78 T=3.01 s (278.4 files/s, 25393.2 lines/s)
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
C# 743 13034 11411 47593
MSBuild script 39 143 106 2043
JSON 4 0 0 623
XAML 27 39 4 459
Markdown 6 130 0 333
YAML 8 40 0 182
XML 10 49 128 64
PowerShell 1 11 2 29
-------------------------------------------------------------------------------
SUM: 838 13446 11651 51326
-------------------------------------------------------------------------------
左端の「Language」の中で、カウントの対象とする言語の表記を確認しておきます。
ここでは「XML」「C#」「XAML」を対象とすることにします。
カウントしたい言語が結果に出てこない場合、言語定義を自分で作成する必要があります。
詳細はCreate Custom Language Definitionsを参照。
無視リスト作成
一部のファイル/フォルダをカウント対象から除外したい場合、無視リストを作っておいて指定することができます。
ここでは stepignore.txt という名前で以下のようなファイルを作りました。
Sandbox/Windows10/SampleData
Sandbox/Xamarin
その他いろいろオプションがあるので詳しくはREADMEを。
シェルスクリプト
はじめに完成品を載せます。
#!/bin/env bash
# Gitリポジトリ
readonly GIT_REPO=https://github.com/PrismLibrary/Prism.git
# 差分比較元のコミットハッシュ
readonly BASE_COMMIT=2a89100f
# 収集対象言語
readonly LANGS='["XAML","C#","XML"]'
# リポジトリから一時フォルダへclone
git clone $GIT_REPO ./_work
cd _work
# clocでステップ数を取得
step_total=`cloc --vcs=git --json --exclude-list-file=../stepignore.txt`
step_diff=`cloc --json --exclude-list-file=../stepignore.txt --diff $BASE_COMMIT HEAD`
# 一時フォルダの削除
cd ..
rm -rf ./_work
# 全体のステップ数(ロジック、コメント)
logic_total=`echo $step_total | jq '[ .'$LANGS'.code ] | add'`
comment_total=`echo $step_total | jq '[ .'$LANGS'.comment,.'$LANGS'.blank ] | add'`
# 追加・修正分のステップ数(ロジック、コメント)
logic_added=`echo $step_diff | jq '.added | [ .'$LANGS'.code ] | add'`
logic_modified=`echo $step_diff | jq '.modified | [ .'$LANGS'.code ] | add'`
comment_added=`echo $step_diff | jq '.added | [ .'$LANGS'.comment,.'$LANGS'.blank ] | add'`
comment_modified=`echo $step_diff | jq '.modified | [ .'$LANGS'.comment,.'$LANGS'.blank ] | add'`
# 流用分を算出
logic_same=$((logic_total-logic_added-logic_modified))
comment_same=$((comment_total-comment_added-comment_modified))
# 出力
{
echo "■ロジックのみ"
echo "・追加:"$logic_added
echo "・修正:"$logic_modified
echo "・流用:"$logic_same
echo "---"
echo "・計 :"$logic_total
echo ""
echo "■コメント・空行を含む"
echo "・追加:"$((logic_added+comment_added))
echo "・修正:"$((logic_modified+comment_modified))
echo "・流用:"$((logic_same+comment_same))
echo "---"
echo "・計 :"$((logic_total+comment_total))
}>./step-result.txt
以下で解説します。
定数
以下は環境に合わせて書き換えます。
コミットハッシュと対象言語は事前準備で調べたものを使用してください。
ハッシュは頭4ケタもあれば普通は十分らしいです。念のため8ケタ書きました。
# Gitリポジトリ
readonly GIT_REPO=https://github.com/PrismLibrary/Prism.git
# 差分比較元のコミットハッシュ
readonly BASE_COMMIT=2a89100f
# 収集対象言語
readonly LANGS='["XAML","C#","XML"]'
LANGSはカンマ区切りの部分にスペースを入れるとダメっぽいので注意。
clocでステップ数を取得
全体のステップ数と、ベースコミットからの差分とを順に取得しています。
step_total=`cloc --vcs=git --json --exclude-list-file=../stepignore.txt`
step_diff=`cloc --json --exclude-list-file=../stepignore.txt --diff $BASE_COMMIT HEAD`
先ほど作った無視リストを --exclude-list-file
オプションで指定しています。
totalのほうで --vcs=git
を指定する代わりに cloc ./
という感じでディレクトリを指定することもできるのですが、その場合は .gitignore
を考慮してくれないので注意が必要です。
それと、 --diff
で実行すれば same, modified, added(, removed) が出力されるので、これらを合算すればトータルステップ数になりそうなものなんですが、このsameの判定がちょっと怪しくて、合算しても行数が一致しません。
なので、トータルステップ数は別で取得し、そこからadded,modifedを引いたものを流用分とカウントすることにします。
全体のステップ数を取り出す
全体ステップ数を取得するclocコマンドの戻り値は以下のような形になります。
{
"header": {
"cloc_url": "github.com/AlDanial/cloc",
"cloc_version": "1.78",
"elapsed_seconds": 2.93183207511902,
"n_files": 838,
"n_lines": 76423,
"files_per_second": 285.828102882046,
"lines_per_second": 26066.6361653396
},
"C#": {
"nFiles": 743,
"blank": 13034,
"comment": 11411,
"code": 47593
},
"MSBuild script": {
"nFiles": 39,
"blank": 143,
"comment": 106,
"code": 2043
},
"JSON": {
"nFiles": 4,
"blank": 0,
"comment": 0,
"code": 623
},
"XAML": {
"nFiles": 27,
"blank": 39,
"comment": 4,
"code": 459
},
"Markdown": {
"nFiles": 6,
"blank": 130,
"comment": 0,
"code": 333
},
"YAML": {
"nFiles": 8,
"blank": 40,
"comment": 0,
"code": 182
},
"XML": {
"nFiles": 10,
"blank": 49,
"comment": 128,
"code": 64
},
"PowerShell": {
"nFiles": 1,
"blank": 11,
"comment": 2,
"code": 29
},
"SUM": {
"blank": 13446,
"comment": 11651,
"code": 51326,
"nFiles": 838
}
}
これをjqコマンドに渡して、対象言語のステップ数を集計します。
logic_total=`echo $step_total | jq '[ .'$LANGS'.code ] | add'`
comment_total=`echo $step_total | jq '[ .'$LANGS'.comment,.'$LANGS'.blank ] | add'`
jqの構文はちょっとクセがありますが、 logic_total
は C#,XML,XAMLの code
の値を取ってきて合算したもの、 comment_total
は comment
とblank
の値を取ってきて合算したものになっています。
追加・修正のステップ数を取り出す
差分のclocコマンドの戻り値は以下のような形になります。
{
"header": {
"cloc_url": "github.com/AlDanial/cloc",
"cloc_version": "1.78",
"elapsed_seconds": 5.76067495346069,
"n_files": 322,
"n_lines": 31889,
"files_per_second": 55.8962278901989,
"lines_per_second": 5535.63605959799
},
"added": {
"XAML": {
"blank": 6,
"code": 95,
"nFiles": 2,
"comment": 0
},
"JSON": {
"blank": 0,
"code": 498,
"nFiles": 1,
"comment": 0
},
"XML": {
"blank": 23,
"code": 15,
"nFiles": 3,
"comment": 59
},
"Markdown": {
"comment": 0,
"nFiles": 1,
"blank": 24,
"code": 118
},
"YAML": {
"comment": 0,
"nFiles": 0,
"blank": 4,
"code": 14
},
"MSBuild script": {
"comment": 19,
"nFiles": 2,
"blank": 11,
"code": 324
},
"C#": {
"comment": 455,
"code": 3365,
"blank": 657,
"nFiles": 67
}
},
"same": {
"XAML": {
"blank": 0,
"code": 43,
"nFiles": 0,
"comment": 0
},
"JSON": {
"blank": 0,
"code": 0,
"nFiles": 0,
"comment": 0
},
"XML": {
"blank": 0,
"code": 9,
"nFiles": 0,
"comment": 40
},
"Markdown": {
"comment": 0,
"nFiles": 0,
"blank": 0,
"code": 130
},
"YAML": {
"comment": 0,
"nFiles": 0,
"blank": 0,
"code": 106
},
"MSBuild script": {
"comment": 60,
"nFiles": 0,
"blank": 0,
"code": 1138
},
"C#": {
"comment": 1177,
"code": 13850,
"blank": 0,
"nFiles": 0
}
},
"modified": {
(略)
},
"removed": {
(略)
},
"SUM": {
"added": {
"nFiles": 76,
"code": 4429,
"blank": 725,
"comment": 533
},
"same": {
"nFiles": 0,
"code": 15276,
"blank": 0,
"comment": 1277
},
"modified": {
"nFiles": 178,
"code": 2822,
"blank": 0,
"comment": 17
},
"removed": {
"nFiles": 68,
"code": 4184,
"blank": 832,
"comment": 1794
}
}
}
これをjqに渡し、addedとmodifiedをそれぞれ取り出します。
logic_added=`echo $step_diff | jq '.added | [ .'$LANGS'.code ] | add'`
logic_modified=`echo $step_diff | jq '.modified | [ .'$LANGS'.code ] | add'`
comment_added=`echo $step_diff | jq '.added | [ .'$LANGS'.comment,.'$LANGS'.blank ] | add'`
comment_modified=`echo $step_diff | jq '.modified | [ .'$LANGS'.comment,.'$LANGS'.blank ] | add'`
出力
書式はお好みで。
{
echo "■ロジックのみ"
echo "・追加:"$logic_added
echo "・修正:"$logic_modified
echo "・流用:"$logic_same
echo "---"
echo "・計 :"$logic_total
echo ""
echo "■コメント・空行を含む"
echo "・追加:"$((logic_added+comment_added))
echo "・修正:"$((logic_modified+comment_modified))
echo "・流用:"$((logic_same+comment_same))
echo "---"
echo "・計 :"$((logic_total+comment_total))
}>./step-result.txt
以上です。