PONOS Advent Calendar 2022の7日目の記事です。
昨日は@nissy_gpさんの「エンジニアの仕事を不確実性という原点から考えてみる」でした。
はじめに
先日の記事「Unityエディタ上で循環的複雑度(CCN)を計測する」ではlizardというプログラムを用いて、Unityエディタ上でソースコード中の循環的複雑度(以下、CCN)を計測する方法を紹介しました。
この記事では、また少し違ったCCN計測の手法をご紹介します。
今回は、2つのブランチの差分を見て、実際に変更のあったソースコードのCCN値を取得する方法をご紹介します。
コードの細部は異なりますが現在参加しているプロジェクトにも導入している仕組みであり、プルリクエストが出されたタイミングでCCNの計測を行ってソースコード毎の複雑度をチェックすることで、保守性の低いソースコードや変更に気づかずにマージされてしまう事態を防いでいます。
前提
以下の2ファイルを管理しているプロジェクトがあるとします。
using UnityEngine;
public class Sample1 : MonoBehaviour
{
void Start()
{
}
}
using UnityEngine;
public class Sample2 : MonoBehaviour
{
void Start()
{
}
}
これらのファイルは同じフォルダに置かれ、main
という名前のブランチの中で管理されています。
main
ブランチは開発の本流のブランチを指しています。
次に、main
ブランチから分岐したwork
ブランチを作成します。
このwork
ブランチは実際の変更をコミットするブランチを指しています。
次に、work
ブランチ上で`Sample1.csだけを以下のように変更しコミットします。
(CCN値の変化を確認するために分岐を適当に増やしています)
using UnityEngine;
public class Sample1 : MonoBehaviour
{
void Start()
{
if(true) // まったく意味のない分岐。
{
}
}
}
main
ブランチとwork
ブランチは、以下のような運用をイメージしています。
1. mainブランチから作業用のブランチ(work)を作成
↓
2. workブランチ上で変更をコミット
↓
3. mainブランチに向けて、workブランチのプルリクエストを作成する
(このタイミングでCCNの高い変更があれば検知したい)
上記3でCCNの計測を行いってその結果を出力させることができれば、要件は達成できそうです。
しかし、そのまま何も考えずにlizardでプロジェクト内を計測してしまうと、「プルリクエストに含まれていない(=変更されていない)ソースコード」に対しても計測が実施され、計測結果のノイズとなってしまいます。
そこで、ここでは、プルリクエストで変化のあったソースコードに限定してCCNを計測する具体的なコマンドを紹介していきます。
gitで差分のあったファイルに限定してCCN計測するコマンド
コマンドはかなりシンプルです。
$ git checkout work
$ TARGETS=`git diff main...work --name-only`
$ lizard --csv $TARGETS
分解して順番に見ていきます。
まずは、git checkout
コマンドを使って、「変更をコミットしたブランチ(work
)」に切り替えています。
今回の変更で複雑度の高いソースコードが無いかを判定するために、変更後のソースコードに対してlizardを実行する必要があるためです。
$ git checkout work
Switched to branch 'work'
続いて、git diff
コマンドを使い、特定の2つのブランチの差分情報を取得し、変数$TARGETS
に格納しています。
変数$TARGETS
には変更のあったソースコードのファイル名が格納され、この変数をlizardに渡すことで計測対象を限定させます。
$ TARGETS=`git diff source...destination --name-only`
git diff
は--name-only
オプションを追加することで、差分のあるファイル名だけを出力させることができます。
$ TARGETS=`git diff source...destination`
$ echo $TARGETS
diff --git a/Sample1.cs b/Sample1.cs index 60aa9dc..073da88 100644 --- a/Sample1.cs +++ b/Sample1.cs @@ -4,5 +4,8 @@ public class Sample1 : MonoBehaviour { void Start() { + if(true) // 意味のない分岐 + { + } } }
$ TARGETS=`git diff source...destination --name-only`
$ echo $TARGETS
Sample1.cs
今回はlizardに計測対象のファイル名を渡すことが目的ですので、--name-only
オプションを指定しています。
最後に、変数$TARGETSをlizardへ渡して計測を実行し、今回変更されたファイルのCCN値を確認します。
lizardはオプションでファイル名 / フォルダ名を渡すことで、計測の範囲を限定することができます。
$ lizard --csv $TARGETS
ここで指定している--csv
オプションは、lizardの実行結果の出力形式です。
得られる結果は同じですが、csv形式で出力されているほうが後の処理でデータを利用しやすいためオススメです。
$ lizard $TARGETS
================================================
NLOC CCN token PARAM length location
------------------------------------------------
6 2 11 0 6 Sample1::Start@5-10@Sample1.cs
1 file analyzed.
==============================================================
NLOC Avg.NLOC AvgCCN Avg.token function_cnt file
--------------------------------------------------------------
10 6.0 2.0 11.0 1 Sample1.cs
===============================================================================================================
No thresholds exceeded (cyclomatic_complexity > 15 or length > 1000 or nloc > 1000000 or parameter_count > 100)
==========================================================================================
Total nloc Avg.NLOC AvgCCN Avg.token Fun Cnt Warning cnt Fun Rt nloc Rt
------------------------------------------------------------------------------------------
10 6.0 2.0 11.0 1 0 0.00 0.00
$ lizard --csv $TARGETS
6,2,11,0,6,"Sample1::Start@5-10@Sample1.cs","Sample1.cs","Sample1::Start","Sample1::Start()",5,10
この計測結果を参考に、プルリクエストをそのままマージしてもよいのか判断しましょう。
まとめ
前回に引き続き、CCNを計測する方法について紹介しました。
皆様のソースコードの保守性を保つためのヒントになれば幸いです。
明日は@ackylaさんです!