GitHub
devops
CircleCI
SonarQube
技術的負債

SonarQubeとCircleCIで技術的負債を駆逐せよ!

More than 1 year has passed since last update.

これはDMM.com #2 Advent Calendar 2017の24日目の記事です。

昨日は@Ecouffesさんの超マイナーなCSSヲタク知識を晒してみる:CSS displayプロパティ値の自動変換でした。

弊社のアドベントカレンダーのURLはこちら

DMM.com #1 Advent Calendar 2017

DMM.com #2 Advent Calendar 2017


まえがき

どーも、@tinojiです。DMM.comラボの動画配信なチームでピチピチ新卒エンジニア(26)をやっとります。配属から5ヶ月ぐらい経ったような気がします。業務では色々やらせてもらってますが、DevOps・開発効率化な基盤をつくる、みたいなことをわりとメインでやっています。

その中で、SonarQubeというツールを使うためにサーバ構築したりドキュメントをしこしこ書いたりしました。実際に運用し始められるかな〜程度の知見が貯まってきたような気がするので、その辺のことを書きたいと思います。昔からある格式高きツールなので今更感は否めませんがご容赦を。


SonarQube is 何

SonarQubeとは簡単に言うと静的コード解析ツール、そしてそれをいい感じに閲覧するWebビューアーです。ひと目でコードの品質(Bug, Code smell, Vulnerability, Coverage, Duplication, Technical Deptなどなど)が分かり、あれ?弊社のコード臭すぎ?と気づくことができます。

公式いわく、


Continuous Code Quality


今流行りのContinuous兄弟のひとりですかね。Continuous弟のCircleCIとタッグを組むことでとってもContinuousになります。SonarQubeのUIを見てみると大体何ができそうかわかると思います。

スクリーンショット 2017-12-23 12.59.20.png

スクリーンショット 2017-12-23 13.02.14.png

こんな感じで、コードの品質に関するいろいろなメトリクスを閲覧することができます。また問題のあるコードはインラインで指摘してくれます。

スクリーンショット 2017-12-23 13.10.54.png

この機能は、プラグインでGithubと連携することによりなんと自動コードレビューができます。すごい。WebUIによるメトリクス・issues閲覧と自動コードレビュー。この2つがメインの機能になるかと思います。

pr_demo.png


このクソ長い記事を読むと何が分かるってんだ


  • SonarQubeとCircleCIを連携させることが出来るようになるぜ

  • コードの品質を視覚的に捉えることが出来るようになるぜ

  • 自動コードレビューが出来るようになるぜ。レビューアーがちょっと楽できるぜ

  • C#で使うときはちょっと特殊だ。それもフォローするぜ

  • IDEで使いたいって?あぁ任せとけ

  • SonarQube自体の細かい機能や使い方はあまり分からないぜ


目指す構成

スクリーンショット 2017-12-23 16.03.33.png

master/stagingブランチが更新されたときの解析結果はSonarQubeサーバーに保存されます。それ以外のブランチの解析結果はサーバーには保存されず、プルリク時に自動コードレビューが行われます。

動作確認環境は以下。


  • SonarQube 6.7 (on CentOS 7)

  • CircleCI Enterprise 2.0

  • Github Enterprise 2.10


インストール

私はインストール版を使用しましたが、クラウド版のSonarCloudもあります。インストールはそこまで大変ではありませんが、クラウド版だとサクッと試せるのでまずそれで使ってみるのもいいかもしれません。インストール版にもいくつかエディションがあります。私は今のところ無料のCommunity Editionを使っています。無料版だと使えないプラグインがあったりします。例えば今のところSwiftを解析するためのプラグインが使えません、、、5.6では使えたのですが、、、

また、dockerイメージもあります。ラクそうです。

インストールは基本的には公式通りでおkです。DBはMySQL5.7にしました。私は最初SonarQube5.6をインストールしたのですが、そのときはDBのインストールが必須でした。今はH2DBがビルトインで使えて動作確認程度ならそれでいけるようです。


サービス化

サービス化の方法は公式だとinitdの場合しか記載がないので、systemdの場合は適当に書き換える必要があります。以下のようなファイルを/etc/system/systemd/に配置すればおkです。


sonar.service

[Unit]

Description=SonarQube service
After=syslog.target network.target

[Service]
Type=forking

ExecStart=/usr/local/sonarqube/sonar/bin/linux-x86-64/sonar.sh start
ExecStop=/usr/local/sonarqube/sonar/bin/linux-x86-64/sonar.sh stop

User=sonarqube
Group=sonarqube
Restart=always

[Install]
WantedBy=multi-user.target


ちなみに/usr/local/sonarqube/sonar/usr/local/sonarqube/sonarqube-6.7にシンボリックリンクしてあります。アップグレードするとき便利なので。


実行ユーザーに注意

Elasticsearchが使われている関係でサービスの実行ユーザーはrootでは動きませんsonarsonarqubeのようなユーザーとグループを作成し、インストールディレクトリの所有者にし、↑のsonar.seviceでもUserGroupをそれに合わせます。SonarQube5.6をインストールしたときはrootで動かしていたのでアップグレードの際にハマりました、、、


プラグインのインストール

adminユーザーでログインして、Administration > Marketplaceから以下のプラグインをインストールします。


  • Github

  • Github Authentication for SonarQube

その他、使用したい言語のプラグインを入れておきましょう。


HTTPS化

私はリバースプロキシでHTTPS化しました。オレオレ証明書では動かなかったので注意です。


アカウント等の準備


各種アカウントとアクセストークンの用意


  • CircleCIのアカウント


    • Githubと連携させておきます。

    • 弊社の場合、Organizationに対応したアカウントを1つ作成してbot用として使うことになっています。



  • SonarQubeのアカウント


    • adminでログインしてアカウントを作成します。ひとまずそのままadminを使ってもおkです。



  • Githubアクセストークン



    • ユーザーアイコン > Settings > Personal access tokenから発行します。パーミッションはrepoだけでおkです。



  • SonarQubeアクセストークン



    • ユーザーアイコン > My account > Securityから発行します。



注)SonarQubeアクセストークンは理想的には、各コミッターがそれぞれ使い分けるべきかもしれませんが、面倒なのでリポジトリの作成者などで統一しておけばいいかなーと思ってます。


GithubのCollaboratorsにCircleCIアカウントを追加

リポジトリのSettingsからCollaboratorsにCircleCIアカウントが入ったチームをjoinさせ、Admin権限を与えます。

スクリーンショット 2017-12-23 13.59.08.png


CircleCIに環境変数を設定

アクセストークンをハードコーディングしないようにするため、環境変数に設定します。以下2つに取得したアクセストークンをコピペして下さい。歯車マーク > Environmental variablesからできます。


  • GITHUB_TOKEN

  • SONAR_TOKEN

access_token.png


CCIのビルドトリガー

CCIでは何をトリガーにしてビルドするかを設定することができます。Settings > Advanced Settingsらへんです。主に以下の2つの方法がありますが、SonarQubeを使うに当たってはどちらも一長一短あるので、少し運用でカバーする必要があります。

※このへんぶっちゃけ検証不足です。もっといい方法があるに違いない、、、


PRにだけビルド



  • Only build pull requestsをON

  • PRが作成されたとき及びそのPR中のブランチのコミットがトリガー

  • SonarQubeのGithub Pluginを使用している場合、PRを作成したときに自動コメントがつくのでgood

  • マージされたときにはビルドされないので、SonarQubeサーバーにmasterブランチの解析結果を反映したいときは、CCIから手動でビルドを実行する必要がある


    • つらい


    • NOT RUNになっているのでRebuildをポチる




すべてのコミットに対してビルド



  • Only build pull requestsをOFF

  • すべてのコミットに対して発火



  • PRを作成したタイミングでは発火しないので、自動コメントつけてほしいときはCCIから手動でビルドする必要あり


    • つらい

    • 該当ブランチの直前のビルドをRebuildすればよい




設定ファイルとシェルスクリプトを書く

次に、リポジトリに置くファイルを用意します。


ディレクトリ構成

src以下に解析したいコードが入る想定です。解析対象にするディレクトリは次で説明するsonar-project.propertiesで設定できます。

.

├── .circleci
│   ├── analyze-sonarqube.sh
│   └── config.yml
├── sonar-project.properties
└── src
└── ...


sonar-project.properties

SonarQubeを実行するときはsonr-scannerというコマンドをインストールして実行します。sonar-scannerは実行したディレクトリにsonar-project.propertiesがあるとその設定を読み込みます(詳しくは後述)。


sonar-project.properties

##################################################################

# Properties file for sonar-scanner
# See https://docs.sonarqube.org/display/SONAR/Analysis+Parameters
##################################################################

# Server
sonar.host.url=http://your.host.name

# Project identification (LOCAL ANALYSIS ONLY)
#sonar.projectName=test-repo
#sonar.projectKey=com.dmm:test-repo:feature/hoge
#sonar.projectVersion=1.0

# Comma-separated paths to directories with sources (required)
sonar.sources=src
#sonar.exclusions=

# Language (If not set, a multi-language analysis will be triggered)
sonar.language=

# Default source code encoding
sonar.sourceEncoding=UTF-8



.circleci/config.yml

次で説明するanalyze-sonarqube.shを実行するだけです。imageは適当なものを。


config.yml

version: 2

jobs:
code_analyze:
docker:
- image: circleci/openjdk:8
steps:
- checkout
- run:
name: analyze by SonarQube
command: bash ./.circleci/analyze-sonarqube.sh

workflows:
version: 2
test:
jobs:
- code_analyze



.circleci/analyze-sonarqube.sh

sonar-scannerをコンテナにインストールして実行する感じです。こちらを参考に、より単純にしました。


analyze-sonarqube.sh

#!/bin/bash

#################################################
# based on https://github.com/Sagacify/ci-tools
#################################################

SONAR_VERSION="sonar-scanner-cli-3.0.3.778"
SONAR_DIR="sonar-scanner-3.0.3.778"

wget -P $HOME -N "https://sonarsource.bintray.com/Distribution/sonar-scanner-cli/${SONAR_VERSION}.zip"
unzip -d $HOME $HOME/$SONAR_VERSION.zip

DEFAULT_SONAR_PARAMS="-Dsonar.login=$SONAR_TOKEN \
-Dsonar.projectName=
$CIRCLE_PROJECT_REPONAME \
-Dsonar.projectVersion=
$CIRCLE_BUILD_NUM"

if [ -n "$CI_PULL_REQUEST" ]; then
if
[ "$CIRCLE_BRANCH" != "staging" ] & [ "$STAGING_EXISTS" ]; then
SONAR_PROJECT_KEY=$CIRCLE_PROJECT_USERNAME:$CIRCLE_PROJECT_REPONAME:staging
else
SONAR_PROJECT_KEY=$CIRCLE_PROJECT_USERNAME:$CIRCLE_PROJECT_REPONAME
fi

echo "Preview analyzing ${CI_PULL_REQUEST} by SonarQube Github Plugin"
$HOME/$SONAR_DIR/bin/sonar-scanner $DEFAULT_SONAR_PARAMS \
-Dsonar.projectKey=$SONAR_PROJECT_KEY \
-Dsonar.github.repository=$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME \
-Dsonar.github.pullRequest=${CI_PULL_REQUEST##*/} \
-Dsonar.github.oauth=$GITHUB_TOKEN \
-Dsonar.github.endpoint="https://git.your.domain/api/v3" \
-Dsonar.analysis.mode=preview;
fi

if [ "$CIRCLE_BRANCH" == "master" ]; then
echo "Analyzing ${CIRCLE_BRANCH} branch to push issues to SonarQube server"
$HOME/$SONAR_DIR/bin/sonar-scanner $DEFAULT_SONAR_PARAMS \
-Dsonar.projectKey=$CIRCLE_PROJECT_USERNAME:$CIRCLE_PROJECT_REPONAME;
elif [ "$CIRCLE_BRANCH" == "staging" ]; then
echo "Analyzing ${CIRCLE_BRANCH} branch to push issues to SonarQube server"
$HOME/$SONAR_DIR/bin/sonar-scanner $DEFAULT_SONAR_PARAMS \
-Dsonar.projectKey=$CIRCLE_PROJECT_USERNAME:$CIRCLE_PROJECT_REPONAME:staging;
fi



  • masterブランチ


    • masterにpushされたときはサーバへ反映。



  • stagingブランチ


    • stagingにpushされたときもサーバへ反映。


    • projectKeyには:stagingが付与されるので、プロジェクト名はmasterと同じだがWebUIでは異なるメトリクスが閲覧できる。



  • それ以外のブランチ


    • pushしても解析は走らない。

    • PRの場合のみGithub Pluginが実行され、PRにコメントがつく。



image.png

↑同じ名前のプロジェクトが2つ出来ていますが、片方はprojectKeyに:stagingが付与されています。この辺は運用フローによって適宜変える感じになるかと思います。


実行

あとはpushするなりPR出すなりすれば動くはずです。


master/staging以外のブランチでもサーバに反映する方法

作業中のブランチのコード量が多いのでPRを出す前にも解析したい、ということもあるかもしれません。ローカルにsonar-scannerをインストールすれば、ローカルから任意のタイミングでサーバに反映することができます。

sonar-project.propertiesでコメントアウトしてあるプロジェクト名などをコメントインし、それぞれ入力してください。projectNameは同一でもprojectKeyが異なれば違うプロジェクトと認識されるので、master/stagingとは違うprojectKeyを入力すればおkです。

例えばsonar-project.propertiesを以下のように編集して、

# Project identification (LOCAL ANALYSIS ONLY)

sonar.projectName=test-repo
sonar.projectKey=com.dmm:test-repo:feature/hoge
sonar.projectVersion=1.0

ローカルにインストールしたsonar-scannerをプロジェクトのルートディレクトリで実行します。このとき、SonarQubeのアクセストークンはオプションで渡してやる必要があります。

$ sonar-scanner -Dsonar.login=[your token]

PRがマージされたタイミングで該当ブランチのプロジェクトは使用しなくなると思うので削除推奨です。


Issueの解決

SonarQubeによってレポートされたissue(Bug, Code Smell, Vulnerability)はissue viewerによって閲覧することができ、担当者をアサインしたりタグを付けたり、まぁなんか色々できます。

image.png

例えばこのissueはコーディング規約に反していないから無視する、とかだったらステータスをResoleved as won't fixにする〜みたいな感じですかね?まぁこの辺はGithubとの使い分けが難しいのであれですが、、、

自動コードレビューされた点についてはそのPR中にできるだけ直し、直さなくていいものはSonarQubeから適宜ステータスを変更する、とかですかね、、、?

詳しい使い方はドキュメントを参照のこと。

ここから下は補足的な感じです。


SonarQubeのプロパティについて

sonar-scannerは、実行したディレクトリのルートにあるsonar-project.propertiesというファイルからプロパティを読み込みます。

また、sonar-scannerのオプションで-Dsonar.hoge=fooのようにプロパティを指定することもできます。つまり、sonar-project.properties

sonar.host.url=http:sonar.host:9000

と書いて実行するのと

$ sonar-scanner -Dsonar.host.url=http:sonar.host:9000

とオプションをつけて実行するのは同じ意味になります。

各パラメータの説明はこちらを見ればおk。

https://docs.sonarqube.org/display/SONAR/Analysis+Parameters

基本的な項目は以下のとおり。

プロパティ
説明

sonar.host.url
SonarQubeサーバのURL

sonar.projectName
SonarQubeのUIに表示されるプロジェクト名

sonar.projectKey
SonarQubeにおけるプロジェクトの識別子。 projectNameが同一でもこれが異なれば違うプロジェクトと判断される

sonar.projectVersion
プロジェクトのバージョン

sonar.sources
解析を行いたいソースのファイルorディレクトリを指定(カンマ区切り)

sonar.exclusions

sourcesから省きたいファイルorディレクトリを指定

sonar.language
解析したい言語。指定しないとmulti-language analysisになるのでそれでおk

sonar.sourceEncoding
UTF-8でいいと思いますが、ver6.4から勝手にやってくれるようです

sonar.login
SonarQubeのアクセストークン


Github Pluginで使用するプロパティ。これらをセットすると勝手にプラグインが走るという仕様ぽいです。

プロパティ
説明

sonar.github.repository
リポジトリ。organization/repository

sonar.github.pullRequest
PR名

sonar.github.oauth
Githubのアクセストークン

sonar.github.endpoint
Github APIエンドポイント。Enterpriseではgithub.comの場合とは違うので注意

sonar.analysis.mode

previewにするとサーバにpushされない(ver6.6.から非推奨)。


Maven&Gradleのプラグイン

JavaなプロジェクトでMavenやGradleを使用している場合は、それらのSonarQubeパッケージを使うことができます。Antもあるぽいです。

例えばGradleの設定はこんな感じ。


https://github.com/SonarSource/sonar-scanner-gradle

gradleスクリプトを書きたい、とかでもない限りsonar-scannerに比べてとくに有用性はないですし、バージョンが結構ややこしいので使う必要はないような気がします、、、ぶっちゃけよくわかりません。


VSTSを利用してC#を解析する

(※注意:自分がC#の解析をやっていたときはCircleCIが1.0だったため、Ubuntuコンテナでのビルドが前提でした。CircleCI2.0ではdockerを利用してWindowsコンテナが使用できると思いますので、それでサクッとできる気がします。以下はCircleCI1.0を前提にした説明となっています。あしからず、、、)

C#の場合、解析の際にMSBuildでビルドすることが必須になるので他の言語とは少し異なる手順になります。

https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner+for+MSBuild

基本的にはWindows環境が必要(exeファイルを実行する必要がある)なので、CircleCIのUbuntuコンテナでビルドすることは多分困難です。ということでC#のCI環境はVSTS(Visual Studio Team Services)で構築してみました。SonarQube公式のプラグインがあります。また、VSTSでバージョン管理も出来るのですがGHEを使いたかったのでそこらへんもゴニョってみました。


アカウント作成

まずVSTSのアカウントを作成。

https://www.visualstudio.com/ja/team-services/?rr=https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fvsts%2F

Visual Studio Pro with MSDNのアカウントを持っていれば基本的には無料で無制限に利用可能だったと思います。そうでなくても、現在1チーム5人まで無料で使用可能です。


プラグインのインストール

SonarQubeを利用するにはプラグインが必要なので、マーケットプレイスからインストールします。

スクリーンショット 2017-12-23 14.15.38.png


Build definitionを作成

あらかじめ適当なprojectも作成してください。その後Build definitionを作成。Hosted(クラウドな)agentを使用しています。


get sourceの設定

Github Enterpriseを使用するので、普通のGithubモジュールは使用できません(そのうちGHEに対応するかもしれません)。External Gitを選択して、認証情報を入力してください。

スクリーンショット 2017-12-23 14.23.33.png

バージョン管理もVSTSにするとこの辺はラクになるかと思います。


gitでプロキシを設定

※この手順はオンプレ(VTF)なagentを使用する場合には不要です。

弊社のGithub Enterpriseは社内ネットワークからしかアクセスできないので、リポジトリをcloneするために、プロキシを経由する必要がありました。

CIコンテナのgit configでプロキシの設定を行おうと思ったのですが、Get sourceのタスクの前にbuild taskを入れることはデフォルトでは不可能(謎仕様)なので、build taskを自分で作る必要がありました。てきとーにつくりました。

https://github.com/tinoji/vsts-task-gitProxy

image.png

※設定時、アカウント名に含まれる記号は適宜エスケープする必要があります。


SonarQubeの設定

以下を参照のこと。

https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Extension+for+VSTS-TFS


実行

Save&queueをすると実行されます。SonarQubeのUIに該当のリポジトリが出現したら成功です。Build definitionのtriggerタブから設定すると、どのブランチのpushで発火させるかなどが設定できます。


IDEでSonarQubeを使う


SonarLint

IntelliJやVSCode、AtomなどのIDEではSonarLintというLintプラグインが使えます。ただし、言語のサポートが少なかったりします。たとえばAndroidStudioでは2017/12時点でKotlinに対応していません。

が、非公式のプラグインではできたりします。例えばSonarQube Community Plugin というのが使えます。その例を紹介しておきます。


SonarQube Community Plugin@AndroidStudio


Pluginのインストール

preferences > pluginsから検索して、SonarQube Community Pluginをインストール。

image.png


サーバの設定

preferences > other settings > SonarQubeでまずサーバを登録。ユーザーはanonymousだと動かないので、各々のユーザー情報を入力。

次にSonarQube resources+ボタンをクリック。Download resourcesをクリックすると、SonarQubeサーバにアップされているプロジェクト一覧が出るので該当のプロジェクトを選択。

ここまでの設定で、サーバにあるプロジェクトのissuesが取得できます。


ローカルで実行するための設定

ローカルでpreviewモードで実行しレポートを出力することにより、サーバに上がっている情報との差分をとってくれます。ローカルで実行するのは以下のどれでもおkです。

まぁsonar-scannarでいいと思います。

設定画面 > Local analysis script > Addで実行スクリプトを書きます。sonar-scannerにはパスが通っていてもフルパスで書く必要があります。sonar.report.export.pathは.scannerworkディレクトリをルートとするので注意です。

android_local_config.png


実行

Analyze > Inspect codeで、他の解析と一緒に実行されます。ちゃんとローカルで実行されていれば、new issueというセクションが出現します。

image.png

イベントログにも吐かれます。

image.png


参考リンク


まとめ

SonarQubeたそ、使い始めるまでがわりと面倒でしたが、これからいい仕事をしてくれるんじゃないかと期待しています。実際に使っていくうちにまた何か知見があればどこかに書こうと思っています。

以上です〜。明日のアドベントカレンダーは@huatoさんです!