GitHub Actions で .NETプロジェクトの静的コード解析を行う
GitHubにコードをpushすると静的コード解析を走らせて、問題を指摘してくれるようなCI (GitHub Actionsのワークフロー)を作ります。
完成図
静的コード解析について
本記事ではMicrosoftが用意している解析機を使います。従来はFxCop analyzersと呼ばれていたもので、今は .NET analyzers というそうです。
- https://docs.microsoft.com/en-us/visualstudio/code-quality/migrate-from-fxcop-analyzers-to-net-analyzers?view=vs-2019
- https://docs.microsoft.com/en-us/visualstudio/code-quality/net-analyzers-faq?view=vs-2019
使用する技術
使うのは dotnet build
だけです。その他特別な第三者のツールは不要です。
言い換えると、本記事で対象にできるのは dotnet build
でビルドできるプロジェクトです。古い .NET Framework のプロジェクト等ではひと手間必要かと思います。
類似の技術
本記事では使いませんが、ちなみにそのほかの類似の技術として以下のようなものがあります。
-
https://github.com/JosefPihrt/Roslynator
恐らく競合するもので、CLIもあります。 -
https://github.com/dotnet/format/
スペースや改行といったコードスタイルの指摘・修正等が可能です。
手順
ワークフロー定義
.github/workflows/<好きな名前>.yml
というファイルを作成します。内容は素朴にビルドしているだけです。発動するのはpull requestを開いたときや、その後pushしたときになっています。
actions/setup-dotnet のREADMEにある registering problem matchers for error output
のところが今回の肝になっているようです。
詳細:https://github.com/actions/toolkit/blob/master/docs/problem-matchers.md
name: .NET
on:
pull_request:
types: [synchronize, opened]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: 5.0.101
- name: Restore
run: |
dotnet restore
- name: Build
run: |
dotnet build --no-restore -warnaserror -clp:NoSummary
[追記] dotnet build
のパラメータに-clp:NoSummary
を付け加えることをお勧めします。 https://zenn.dev/shimat/articles/e6af698fca3fba
ソースコード
上記ワークフローの効きを確かめるため、適当な.NETプロジェクトを作ります。
雛形ほぼそのままの.NET5のコンソールアプリケーションです。警告を見たいので、ツッコミどころがあるクラスを入れています (こちらから参照)。
using System;
namespace ConsoleApp1
{
public class demo : Exception
{
public static void Initialize(int size) { }
protected static readonly int _item;
public static int item { get { return _item; } }
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<AnalysisMode>All</AnalysisMode>
</PropertyGroup>
</Project>
この状態で、筆者の手元 (Visual Studio Professional 2019 Version 16.8.4) では、以下のような警告が出ています。これがGitHub上でも出てくれることを期待します。AnalysisMode: All
により、素の状態よりもかなり込み入った警告が出ていることを確認しましょう。
フレームワークやVisual Studioが古い場合
このページを参考にします。 https://docs.microsoft.com/ja-jp/visualstudio/code-quality/install-net-analyzers?view=vs-2019
Microsoft.CodeAnalysis.FxCopAnalyzers
のNuGetパッケージをインストールするのが早いと思います。
動作確認
ワークフローのYAMLを設定したリポジトリで、pull requestを作りましょう。以下は私が試した例です。
- https://github.com/shimat/csharp_actions_test/pull/1
- https://github.com/shimat/csharp_actions_test/pull/1/files
dotnet build -warnaserror
のように -warnaserror
オプションを付けたので、警告がエラーとして扱われています (参考)。
エラーではなく警告にする場合
ビルドのオプションから /warnaserror
を取り除いた場合は警告となります。
- https://github.com/shimat/csharp_actions_test/pull/2
- https://github.com/shimat/csharp_actions_test/pull/2/files
GitHubのステータスは ✅ になるので、一見すると問題があるかどうか伝わりにくいです。厳格に警告0を目指すか、さりげないチェックにとどめるか、姿勢次第で考えてよいと思います。
もし指摘は警告で、かつGitHubの方のステータスは❌にしたいなら、dotnet build
からの出力に警告があるかどうかを自前で読み取って exit 1
すれば実現できそうです。それ以外にもpull requestのフィードへコメントしたり、Slack等へ通知したりしてもいいですし、いかようにも調整できます。
Windowsのワークフローの場合
runs-on: windows-latest
としてWindows仮想環境でワークフローを記述した場合、同じYAMLでほとんど完全に動きますが、-warnaserror
が効かないようです。
正確には、コマンドとしては意図通りエラーになるのですが、GitHub Actionsのワークフローはエラーにならず正常終了し、ステータスが ✅ になります。dotnetコマンドのステータスコードが0になっている模様?
ubuntu-latest
と挙動が違うのがよくわかっていません。
課題
- 今後pull requestを出したとき、そのときの差分ではない箇所にも指摘されてしまいます。従って、一度出た警告は全て対策していく姿勢が必要だと思います。意図したコードであって修正したくない場合は、例えば
#pragma warning disable CA1032
のようにして抑止することができます。ビルドコマンドや.csprojへの設定等でも可能でしょう。なおGitHubはbeta機能として、今回の差分ではない箇所へのannotationも表示してくれます。
- 警告の数が多いと、全部はdiffの中に表示されないようです。diffへのコメントはGitHub Checks API のannotation機能によって行われていますが、数に上限があるようです。たくさん出たなあと思ったら、持ち帰って手元で一括対策しましょう。
- iOSやAndroidのGitHubアプリでは、annotationが表示されないようです。
まとめ
- ごく簡単なGitHub Actionsワークフローによって、静的コード解析とコード挿入型の指摘が実現できます。
- 一般には.NETのワークフローではテスト・NuGetパッケージの作成・何らかのデプロイなどを目指すと思います。その過程で
dotnet build
はだいたい走らせることになりますから、本記事の内容は無意識のうちに実現できます。CI/CDはGitHub Actionsを使いましょうという結論です。 - .NET界隈は、他言語と比べてチームメンバ各々の開発環境が均一である可能性が高い (だいたいVisual Studio) と考えられますが、それでも案外みな違うものです。コードレビューすると「これ警告出るはずなのに、どうしてそのままにしてるんだろう」と思うことはしばしばで、そのような環境でのコード品質改善に役立つかもしれません。
Discussion