SonarQubeでソースコードの静的解析とレビューを自動化してみる(後編)

ソースコードの静的解析ツールとして人気のSonarQubeを使い、プルリクへのレビューコメント投稿を自動化してみました。
2018.08.07

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

サーバーレス開発部@大阪の岩田です。 前編に引き続き、SonarQubeを用いたソースコードの静的解析について見ていきます。 今回は前回の設定を発展させて、GitHubのプルリクを検知して、ソースコードの解析とレビューコメントの投稿までを自動化してみます。

こんな感じのコメントを自動で投稿するのが目標です↓

なお、前編はこちらです↓

SonarQubeでソースコードの静的解析とレビューを自動化してみる(前編)

今回構築する環境

今回は下記のような環境を構築してみます。

大まかな流れは下記の通りです。

  1. GitHubがプルリクエストを検知し、プルリクエストの情報をSNSに通知
  2. SNSをトリガーにLambdaが起動、LambdaがCodeBuildのジョブを実行
  3. CodeBuildのジョブがSonarScannerで変更箇所をスキャン
  4. スキャン結果をプルリクエストのレビューコメントとして登録

環境構築

前回構築したSonarQubeの環境をベースに早速環境を構築していきます!!

SonarQubeにGitHubプラグインを導入

SonarQubeの Developer Edition以上ではPull Request Analysisという機能が利用できるのですが、今回はCommunity Editionでのお試しなので、GitHubプラグインを利用します。

※現在GitHubプラグインはdeprecated扱いとなっています。

SonarQubeの管理者向けメニューからプラグインをインストールし、SonarQubeを再起動しておきます。

sonar.propertyの準備

スキャン用にSonarScannerの設定ファイルを用意し、スキャン対象プロジェクトの直下に配置しておきます。 今回は下記のようなファイルを用意しました。

sonar.projectKey=<プロジェクトを一意に識別するためのキーを設定>
sonar.projectName=<>
sonar.projectBaseDir=.
sonar.sources=.
sonar.exclusions=.build/**

sonar.analysis.mode=preview
sonar.github.repository=<GitHubのユーザー名/GitHubのリポジトリ名>

GitHubプラグインでプルリクエストをスキャンするためにはsonar.analysis.modepreviewを指定する必要があるため、設定を追加します。 また、CodeBuildのジョブの中でSonnarScannerをDLするため、.buildというディレクトリ配下をスキャン対象外としています。

GitHubのトークンを発行

GitHubプラグインを使用するにはGitHubのトークンが必要になります。 GitHubの設定メニューからpublic_repoの権限を持ったトークンを発行します。 ※発行したトークンは後ほど必要になるので忘れずに控えておいて下さい。

CodeBuildのジョブを作成

ビルドプロジェクトの作成

続いてCodeBuildのジョブを作成していきます。 SonanrScannerがJavaで動くので、JDK8のコンテナをベースに使用します。

最終的な設定値は下記の通りです。

※今回はお試しなので上記のように設定しましたが、実際に利用する場合はSonnarScanner導入済みのコンテナをECR等に登録して利用する方が良いでしょう。

buildspec.ymlの準備

下記のようにbuildspec.ymlを作成し、スキャン対象プロジェクトの直下に保存しておきます。

buildspec.yml

version: 0.2
env:
  parameter-store:
    SONARQUBE_TOKEN: "SONARQUBE_TOKEN"
    SONARQUBE_ENDPOINT: "SONARQUBE_ENDPOINT"
    GITHUB_TOKEN: "GITHUB_TOKEN"
phases:
  install:
    commands:
      - wget https://sonarsource.bintray.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-3.2.0.1227-linux.zip
      - unzip sonar-scanner-cli-3.2.0.1227-linux.zip -d .build
      - export PATH=$PATH:./.build/sonar-scanner-3.2.0.1227-linux/bin
  build:
    commands:
      - export PULL_REQUEST_ID=`echo ${CODEBUILD_SOURCE_VERSION} | sed -e 's/pr\///'`
      - sonar-scanner -Dsonar.login=${SONARQUBE_TOKEN} -Dsonar.github.oauth=${GITHUB_TOKEN} -Dsonar.github.pullRequest=${PULL_REQUEST_ID} -Dsonar.host.url=${SONARQUBE_ENDPOINT}

ポイントをかいつまんで説明します。

環境変数の設定
env:
  parameter-store:
    SONARQUBE_TOKEN: "SONARQUBE_TOKEN"
    SONARQUBE_ENDPOINT: "SONARQUBE_ENDPOINT"
    GITHUB_TOKEN: "GITHUB_TOKEN"

の部分でAWS Systems Manager パラメータストアからGitHubやSonarQubeのトークンを読み出して環境変数にセットしています。 AWS Systems Manager パラメータストアを使用することでソースコード上にトークンを埋め込む必要が無くなります。

プルリクエストIDの取得
      - export PULL_REQUEST_ID=`echo ${CODEBUILD_SOURCE_VERSION} | sed -e 's/pr\///'`
      - sonar-scanner -Dsonar.login=${SONARQUBE_TOKEN} -Dsonar.github.oauth=${GITHUB_TOKEN} -Dsonar.github.pullRequest=${PULL_REQUEST_ID} -Dsonar.host.url=${SONARQUBE_ENDPOINT}

後述する設定でLambdaからジョブが起動された際に、環境変数CODEBUILD_SOURCE_VERSIONの中にpr/1のような形式でプルリクエストのIDが設定されてくるので、sedでプルリクエストのIDを取り出し、SonnarScannerの引数に渡しています。

AWS Systems Manager パラメータストアの作成

AWS Systems Manager パラメータストアを作成し、

  • SonarQubeのトークン
  • SonqrQubeのエンドポイント
  • GitHubのトークン

を保存します。

CodeBuildの実行ロールにAWS Systems Manager パラメータストアの読み取り権限を付与

先ほど保存した各パラメータをCodeBuildのジョブの中で利用できるように、CodeBuildの実行ロールにSSMパラメータストアの読み取り権限を付与します。

GitHubからSNSに通知するための設定

こちらのブログで紹介されてる手順に従って設定を行います。

GitHubのプルリクエストをAmazon SNSに通知する

SNS - Lambda - CodeBuildを連携させる設定

こちらのブログで紹介されてる手順に従って設定を行います。

CodeBuild で GitHub の pull request をビルドできるようになりました

プルリクエストを出してみる。

まずSAM CliPython3.6の雛形アプリを作成してGitHubにプッシュしておきます。 次に新規にブランチを作成し、そのブランチ上で雛形アプリに無茶苦茶な変更を加えてみます。

hello_world/app.py

import json

import requests


def lambda_handler(event, context):
    """Sample pure Lambda function
    Arguments:
        event LambdaEvent -- Lambda Event received from Invoke API
        context LambdaContext -- Lambda Context runtime methods and attributes
    Returns:
        dict -- {'statusCode': int, 'body': dict}
    """

    ip = requests.get('http://checkip.amazonaws.com/')

    try:
        if 1 == 1:
            if 2 == 2:
                if 3 == 3:
                    if 4 == 4:
                        if 5 == 5:
                            print(1)
                        else:
                            pass
                    else:
                        pass
                else:
                    pass
            else:
                pass
        else:
            pass                                
    except Exception as e:
        pass

    return {
        "statusCode": 200,
        "body": json.dumps({
            'message': 'hello world',
            'location': ip.text.replace('\n', ''),
        })
}

変更できたらブランチをプッシュしてプルリクエストを出してみます。 その後しばらく待つと・・・

無事にジョブが実行され、正常終了しました!

GitHubのプルリクエストを確認すると、SonarScannerからコメントが入っています!

まとめ

2回に分けてSonarQubeを利用したソースコードの静的解析について見てきました。 こういった静的解析ツールをうまく活用することで

  • レビュー漏れ防止による品質向上
  • レビューの省力化による生産性向上
  • 非推奨な書き方を学習することによるプログラマーのスキル向上

といった効果が見込めると思います。 開発プロセスの中にうまく組み込んでいきたいですね。

参考

CodeBuild で GitHub の pull request をビルドできるようになりました

GitHubのプルリクエストをAmazon SNSに通知する

SonarQube GitHub Plugin公式ドキュメント