GitHub ActionsでMSBuildのGoogleTestとCppcheckを自動化する

記事
この記事は約11分で読めます。
※記事内には広告を含む場合がございます

結論

以下のことを開発に取り入れると、コード品質の一定化、
ビルドが動くことの保証を得ることができます。
GitHub Actionsでプッシュ時に、GoogleTestとCppcheckを自動で実行する

流れは以下のとおりです。

  • 開発者がGithubにPushする
  • GoogleTest && Cppcheck 自動実行
  • 自動実行が成功すれば、マージ可とする

本記事の前提条件

  • VisualStudio 2022 C++ プロジェクト
  • Msbuild と nuget
  • GoogleTestCppcheck

GitHub Actionsを使い、自動で単体テストや静的解析するメリット

テストと静的解析

テストとは、様々な手法があります。
今回は単体テストをメインで取り上げます

静的解析は静的に(実行せずに)コードを文法解析して、問題点を検出するものです。
この記事ではテストと静的解析の詳細は解説しません。

テストと静的解析を自動で実行すると以下のメリットがあります。

  • ビルドの動作の保証
  • コード品質の一定化
  • 環境構築の負荷軽減

GitHub Actions

開発フローにGitHubを使用している場合、
テストを自動化することのメリットが特に多いです。

なぜならば、不特定多数の開発者の環境構築の手間を減らせるからです。
GitHub Actionsはクラウド上で全てのテストが実行されるため、
テスト用の環境を用意する必要がありません。

また無料枠もかなりあるため、個人開発でも手軽に導入できます。

製品Storage分 (月あたり)
GitHub Free500 MB2,000
GitHub Pro1 GB3,000

具体例

では実際にやってみましょう。
まずは説明と練習のためMSBuildにてC++プロジェクトをビルドしてみます。

MSBuildでC++プロジェクトをビルドする

GitHub Actionの作成ページにて用意されている、
公式テンプレートをカスタマイズしたものです。
これから解説していきます。

name: MSBuild

on:
  pull_request:
    branches: [ "master" ]
  workflow_dispatch:

env:
  SOLUTION_FILE_PATH: .

  BUILD_CONFIGURATION: Release

jobs:
  build:
    name: msbuild 
    runs-on: windows-latest

    steps:
    
    - uses: actions/checkout@v3

    - name: Add MSBuild to PATH
      uses: microsoft/[email protected]
          
    - name: Restore NuGet packages
      working-directory: ${{env.GITHUB_WORKSPACE}}
      run: nuget restore ${{env.SOLUTION_FILE_PATH}}

    - name: Build
      working-directory: ${{env.GITHUB_WORKSPACE}}

      run: msbuild /m /p:Configuration=${{env.BUILD_CONFIGURATION}} ${{env.SOLUTION_FILE_PATH}}
      
    - name: Upload artifacts
      uses: actions/upload-artifact@v1
      with:
          name: artifacts_win-x64
          path: x64/${{env.BUILD_CONFIGURATION}}/

(見やすくするため、自動生成されたコメントは省略しています。)

on:
  pull_request:
    branches: [ "master" ]
  workflow_dispatch:

masterブランチにプルリクエストされた際に、このアクションを実行します。
workflow_dispatchとはGitHubのGUI上から任意のタイミングで
アクションを実行できるようにするオプションです。

env:
  SOLUTION_FILE_PATH: .

  BUILD_CONFIGURATION: Release

このアクション内で使える変数を定義しています。
BUILD_CONFIGURATIONでは、DebugやReleaseなどコンフィグレーションを切り替えられます。

例えばBUILD_CONFIGURATIONを使いたい場合
以下の記法で変数を展開できます。

${{env.BUILD_CONFIGURATION}}

- name: Restore NuGet packages
      working-directory: ${{env.GITHUB_WORKSPACE}}
      run: nuget restore ${{env.SOLUTION_FILE_PATH}}

Nugetのパッケージを復元します。

- name: Build
      working-directory: ${{env.GITHUB_WORKSPACE}}

      run: msbuild /m /p:Configuration=${{env.BUILD_CONFIGURATION}} ${{env.SOLUTION_FILE_PATH}}

run: msbuild /m /p:Configuration=${{env.BUILD_CONFIGURATION}} ${{env.SOLUTION_FILE_PATH}}
msbuildのコマンドで実際にビルドしています。

- name: Upload artifacts
      uses: actions/upload-artifact@v1
      with:
          name: artifacts_win-x64
          path: x64/${{env.BUILD_CONFIGURATION}}/

ビルドした成果物をGithubにアップロードします。
path:にてビルド後のフォルダを指定しています。
つまり、プロジェクトの設定を変更して出力先を変更している場合は対応が必要です。

Google Testをビルドする

GoogleTestはヘッダーオンリーライブラリのため、
ヘッダーをインクルードした上で、通常通りビルド→実行
すればよいです。

つまり上記で説明した、MSbuildビルドを行う際に、
Google Testのプロジェクトも一緒にビルドすればよいです。

複数方法はありますが、

- RootProject.sln
   - Library.sln
   - Test_Library.sln

のようなプロジェクト構造にし、
RootProject.slnに子のプロジェクトを追加します。
その状態でビルドする時、以下のような構造にします。

- RootProject.sln
   - Library.sln
   - Test_Library.sln
- x64
   - Debug
       - Library.exe | Library.dll
       - Test_Library.exe

このような状態なら、アクションのymlの最後
以下のような文を追加すればよいです。
(パスは適時変更してください)

- name: Run Unit Test
      run: |
        ./x64/${{env.BUILD_CONFIGURATION}}/Test_Library.exe
      shell: powershell

もしテストが失敗すると
正常失敗の終了コードが返ってくるため、このアクションは失敗します。

アクションが成功しないとマージできないようにする

  • プロジェクト → Settingを開く
  • Branchesを開く
  • Add ruleを開く

まずはBranch name patternに保護したいブランチ名を入力します。
patternなため、完全一致ではありません。
master*などで、一致する名前全てに適用することもできます。

そしてRequire status checks to pass before merging
マージする前にステータスチェックに合格する必要があります
ONにします。

この状態でプルリクエストを出してみると・・・

チェックが完了するまでマージボタンが存在せず、押せません。
※ 管理者に限り、強制的にマージすることは可能です。

Cppcheck

name: cppcheck-action
on:
  pull_request:
    branches: [ "master" ]
  workflow_dispatch:


jobs:
  build:
    name: cppcheck
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: cppcheck
        run : |
              sudo apt install cppcheck
              cppcheck --max-ctu-depth=2 --output-file=cppcheck_report.txt  --enable=all .

      - name: print output
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          URL: ${{ github.event.pull_request.html_url }}
        run: |
               sudo apt-get install nkf
               REPORTFILE=./cppcheck_report.txt
               WARNINGSFILE=./warnings.txt
               if test -f "$REPORTFILE"; then
                  # Filter innocuous warnings and write the remaining ones to another file.
                  # Note that you can add more warnings by adding it in the parenthesis,
                  # with "\|" in front of it. For example, "(missingIncludeSystem\|useStlAlgorithm\)"
                  sed 's/\[\(missingIncludeSystem\|internalAstError\|missingInclude\|ConfigurationNotChecked\|toomanyconfigs\)\]//g' "$REPORTFILE" > "$WARNINGSFILE"
                  # are there any remaining warnings?
                  if grep -qP '\[[a-zA-Z0-9]{5,}\]' "$WARNINGSFILE"; then
                      # print the remaining warnings
                      echo Warnings detected:
                      echo ==================
                      cat "$WARNINGSFILE" | grep -P '\[[a-zA-Z0-9]{5,}\]'
                      
                      # fail the job
                      exit 0
                  else
                      echo No warnings detected
                  fi
               else
                  echo "$REPORTFILE" not found
               fi

CppcheckはC++言語の静的解析ツールです。
ローカルに導入する場合GUI版もあります。

これをGithub Actionで自動実行する際は以下の方法で行います。

  • apt installでインストール
  • 実行
  • 結果から終了コードを求める

apt installでインストール

実はcppcheckをgithub acitonで行うサンプルは、マーケットプレイスにも存在するのですが、
コマンドがラップされてしまい、融通がききませんでした。

Ubuntuランナーならaptが普通に使えるため、
apt install cppcheckでサクッと導入します。

実行

これは通常通りCppcheckをCLIで実行しているだけです。

結果から終了コードを求める。

- name: print output
    以下が判定コード

これはくせ者です。
Cppcheckは結果をレポート(テキスト)として出力されるため、
結果がレポートを開いてみないとわかりません。

そのため、ファイルを開いてからロジックで終了コードを求めます。
これは以下のPRを参考にさせていただきました!

Document how to set up a pass/fail configuration by Blake-Madden · Pull Request #55 · deep5050/cppcheck-action
Instead of uploading the results to your repo, you can also parse them during the run and then show the action as passin...

結論

テストを自動化することで、コードの品質を一定にできるだけでなく、
ビルドの正常性を保証できたり、
テスト環境の構築の統一や簡略化が期待できます。

これはOSSとして多数の開発者が関わる際に、多大なメリットとなります。

こういった技術をDevopsと言ったりします。
近年では、ビルドエンジニアという専門職もあったりしますので、
興味を持った方は、ぜひ極めてみてください!


コメント

タイトルとURLをコピーしました