これはDMM.com #2 Advent Calendar 2017の24日目の記事です。
昨日は@Ecouffesさんの超マイナーなCSSヲタク知識を晒してみる:CSS displayプロパティ値の自動変換でした。
弊社のアドベントカレンダーのURLはこちら
DMM.com #1 Advent Calendar 2017
DMM.com #2 Advent Calendar 2017
追記: v7.2から無料でPR解析ができなくなったらしい
Release notesによると、Developer、Enterprise、Data Center Editionでしか使えなくなったらしいです。大変つらい。
まだどうするか考え中です・・・。
まえがき
どーも、@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を見てみると大体何ができそうかわかると思います。
こんな感じで、コードの品質に関するいろいろなメトリクスを閲覧することができます。また問題のあるコードはインラインで指摘してくれます。
この機能は、プラグインでGithubと連携することによりなんと自動コードレビューができます。すごい。WebUIによるメトリクス・issues閲覧と自動コードレビュー。この2つがメインの機能になるかと思います。
このクソ長い記事を読むと何が分かるってんだ
- SonarQubeとCircleCIを連携させることが出来るようになるぜ
- コードの品質を視覚的に捉えることが出来るようになるぜ
- 自動コードレビューが出来るようになるぜ。レビューアーがちょっと楽できるぜ
- C#で使うときはちょっと特殊だ。それもフォローするぜ
- IDEで使いたいって?あぁ任せとけ
- SonarQube自体の細かい機能や使い方はあまり分からないぜ
目指す構成
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です。
[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では動きません。sonar
やsonarqube
のようなユーザーとグループを作成し、インストールディレクトリの所有者にし、↑のsonar.sevice
でもUser
とGroup
をそれに合わせます。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権限を与えます。
CircleCIに環境変数を設定
アクセストークンをハードコーディングしないようにするため、環境変数に設定します。以下2つに取得したアクセストークンをコピペして下さい。歯車マーク > Environmental variables
からできます。
- GITHUB_TOKEN
- SONAR_TOKEN
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 - すべてのコミットに対して発火
-
circle.yml
でブランチを制限したりはできる - コミットメッセージで無視させることもできる
-
- 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
があるとその設定を読み込みます(詳しくは後述)。
##################################################################
# 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
は適当なものを。
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
をコンテナにインストールして実行する感じです。こちらを参考に、より単純にしました。
#!/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にコメントがつく。
↑同じ名前のプロジェクトが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によって閲覧することができ、担当者をアサインしたりタグを付けたり、まぁなんか色々できます。
例えばこの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もあるぽいです。
- Maven
- Gradle
例えば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を利用するにはプラグインが必要なので、マーケットプレイスからインストールします。
Build definitionを作成
あらかじめ適当なprojectも作成してください。その後Build definitionを作成。Hosted(クラウドな)agentを使用しています。
get sourceの設定
Github Enterpriseを使用するので、普通のGithubモジュールは使用できません(そのうちGHEに対応するかもしれません)。External Git
を選択して、認証情報を入力してください。
バージョン管理もVSTSにするとこの辺はラクになるかと思います。
gitでプロキシを設定
※この手順はオンプレ(VTF)なagentを使用する場合には不要です。
弊社のGithub Enterpriseは社内ネットワークからしかアクセスできないので、リポジトリをcloneするために、プロキシを経由する必要がありました。
CIコンテナのgit configでプロキシの設定を行おうと思ったのですが、Get sourceのタスクの前にbuild taskを入れることはデフォルトでは不可能(謎仕様)なので、build taskを自分で作る必要がありました。てきとーにつくりました。
https://github.com/tinoji/vsts-task-gitProxy
※設定時、アカウント名に含まれる記号は適宜エスケープする必要があります。
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をインストール。
サーバの設定
preferences > other settings > SonarQube
でまずサーバを登録。ユーザーはanonymousだと動かないので、各々のユーザー情報を入力。
次にSonarQube resources
で+
ボタンをクリック。Download resources
をクリックすると、SonarQubeサーバにアップされているプロジェクト一覧が出るので該当のプロジェクトを選択。
ここまでの設定で、サーバにあるプロジェクトのissuesが取得できます。
ローカルで実行するための設定
ローカルでpreviewモードで実行しレポートを出力することにより、サーバに上がっている情報との差分をとってくれます。ローカルで実行するのは以下のどれでもおkです。
- sonar-scanner(予めローカルにインストールする必要あり)
- mavenのパッケージ
- gradleのパッケージ
まぁsonar-scannarでいいと思います。
設定画面 > Local analysis script > Add
で実行スクリプトを書きます。sonar-scannerにはパスが通っていてもフルパスで書く必要があります。sonar.report.export.path
は.scannerworkディレクトリをルートとするので注意です。
実行
Analyze > Inspect code
で、他の解析と一緒に実行されます。ちゃんとローカルで実行されていれば、new issueというセクションが出現します。
イベントログにも吐かれます。
参考リンク
-
SonarQube と CircleCI を使って Github でのコードレビューを自動化する
- CircleCIとの連携とかはこちらを参考にしました。
- SonarQubeでプログラムの品質管理をはじめる(概要)
- SonarQubeでソースコードの品質チェック
-
SonarQubeで始める静的コード解析
- dockerでSonarQubeを立てる&CIにJenkinsを使用する&Gitlabを使う。
まとめ
SonarQubeたそ、使い始めるまでがわりと面倒でしたが、これからいい仕事をしてくれるんじゃないかと期待しています。実際に使っていくうちにまた何か知見があればどこかに書こうと思っています。
以上です〜。明日のアドベントカレンダーは@huatoさんです!