ZOZOTOWN AndroidチームにおけるQodanaを活用したビルドワーニングへの取り組み

ogp

はじめまして、ZOZOTOWNアプリ部Android1ブロックの池田一成です。普段はZOZOTOWN Androidアプリ開発を担当しています。

ZOZOTOWNアプリは歴史の長いアプリのため、レガシーなコードがいくつか残っています。そのため、Android Lintで検出されるビルドワーニングが複数放置されたままの状態になっていました。これらのビルドワーニングは潜在的なバグを生み出す可能性やメンテナンスコストを増加させる可能性があります。ZOZOTOWNアプリにおいても機能改修をした際に新たに発生したビルドワーニングを検知できず、リリース後不具合に繋がってしまったことがありました。本記事では、JetBrains製のQodanaという静的解析ツールを用いた既存のビルドワーニングの可視化と新規のビルドワーニングを発生させない仕組みづくりについての取り組みをご紹介します。

Qodanaとは

Qodanaは各種CIツールと連携可能なコード品質プラットフォームで、Java、JavaScript、PHP、Kotlin、Go、C# など60以上の言語で記述されたコードの問題を検出、解析、および解決できます。公式ブログより抜粋したQodanaに委任できるタスクの一覧を以下に引用します。

  • コードの問題を早期にキャッチ。コードが実際に本番環境にプッシュされる前に問題を解決できます。 後で見つかった問題を解決するのはコストが高くつきます。
  • 異常なコードを検出。プロジェクトには一般的でない方法で記述されたコード箇所は、プロジェクトのセキュリティリスクになる可能性があります。
  • コードレビューを自動化。未使用のインポート、複製、スペルや書式の問題など、複数のチェックを自動化し、フィードバックループにかかる時間を短縮できます。
  • デッドコードを除去。これにより、無関係な処理の実行をなくし、プログラムの実行時間を短縮できます。
  • コンプライアンスリスクを低減。Qodana のライセンス監査でプロジェクトが使用している依存関係を追跡できます。 追跡することで、ライセンス要件の準拠を維持しやすくなります。
  • コード構造を改善。コードを読みやすく、メンテナンス性の高いものにします。 Qodana なら、インデント、名前付けスキーム、行の長さ制限など、コードの一貫性を確保できます。
  • コーディングのベストプラクティスを導入。プロジェクトやビジネスの要件に基づき、コードを独自のコードポリシー(特定のプログラミングスタイルガイドなど)に準拠させることができます。

導入背景

ZOZOTOWN Androidアプリには既にたくさんのビルドワーニングが存在しています。そのため、Android Lintではアプリの改修等で新しく発生した問題の検知が難しく、アプリ全体のビルドワーニングの可視化と新規のビルドワーニングを検知する仕組みが必要でした。Qodanaの機能の1つにビルドワーニングの種別1をサンバースト図として表示する機能があります。この機能を活用することで、前述した課題を解決し、下記の目的を達成できると判断したためQodanaを導入することにしました。

  • アプリ全体のビルドワーニングの数や種類の可視化
  • 新規のビルドワーニングの発生を検知

サンバースト図

また、ZOZOTOWN Androidチームでは静的解析ツールにktlintAndroid Lintを導入しています。ktlintはKotlinコードのスタイルガイドに従ってコードを検証し、一貫性のあるスタイルを保つのに役立ちます。主にコードフォーマットやスタイルに関連する問題を検出するために導入しています。Android LintはAndroidプロジェクト内で一般的なバグや問題を検出するためのツールとして、非推奨なAPIの使用、リソースの問題、潜在的なメモリリークなどを検出するために導入しています。ktlintとAndroid Lintは、それぞれコードのスタイルやAndroidアプリの品質向上のための優れたツールですが、下記の観点から並行してQodanaを導入することにしました。

  • JetBrainsが設計した独自の検査により総合的な静的解析を提供するため、Android Lintとktlintでは見逃されるかもしれない幅広い問題やバグを検出できる
  • Qodanaはカスタマイズ性が豊富であり、プロジェクトのニーズに合わせてチェックを調整できる。そのため既存のktlintやAndroid Lintルールと組み合わせて、さらに厳格な検証ができる

Qodanaを導入する

ZOZOTOWN AndroidチームではGitHub Actionsを利用しているため、その導入手順を紹介します。公式ドキュメントが非常に整っていたため、簡単に導入できました。

構成ファイルの準備

プロジェクトのroot配下にqodana.ymlを作成します。このファイルをカスタマイズすることで解析の条件や使用するリンターを設定できます。

version: "1.0"
linter: jetbrains/qodana-jvm-android:2023.2
profile:
  name: qodana.recommended
projectJDK: 11

今回はAndroidのプロジェクトを解析するため、リンターにはqodana-jvm-androidを設定します。このリンターを設定することでJavaやKotlin、Gradleなど複数の言語の解析ができます。その他にはPHPやPython、Goなどのリンターが用意されています。リンターの詳細な内容については公式ドキュメントの「Linters」ページをご参照ください。

profileにはstarterrecommendedを設定できます。starterは重要なチェックのみ使用され、プロジェクトの初回スキャンに最適です。recommendedはほとんどのプロジェクトに幅広く適した事前選択済みのインスペクション一式を有効にする設定と説明されています。ZOZOTOWNではより詳細な解析をするためrecommendedを利用しています。profileの詳細な内容については公式ドキュメントの「Inspection profiles」ページをご参照ください。

GitHub Actionsへの導入

Qodanaには過去の結果と比較できるベースライン機能があります。これにより、新しい問題、変更されていない問題、解決された問題を確認できます。ZOZOTOWN Androidチームではこのベースラインを利用して運用しています。そのJobは下記のようになります。

  qodana:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: calculate previous run number
        env:
          NUM: ${{ github.run_number }}
        run: |
          echo "GITHUB_PREVIOUS_RUN_NUMBER=$(($NUM - 1))" >> $GITHUB_ENV
      - name: Download artifact
        id: download-artifact
        uses: dawidd6/action-download-artifact@v2
        with:
          run_number: ${{ env.GITHUB_PREVIOUS_RUN_NUMBER }}
          if_no_artifact_found: ignore
      - uses: JetBrains/qodana-action@v2023.2
        with:
          args: --baseline,./qodana-report/results/qodana.sarif.json
          upload-result: true
      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ${{ runner.temp }}/qodana/results/report
          destination_dir: ./docs/qodana

本来、ベースライン機能を利用する場合、比較対象となるqodana.sarif.json2をプロジェクトのroot配下に置いてQodanaを実行する必要があります。しかし、そのような運用をGitHub Actionsで行うとGitのリポジトリサイズが増加したり、定期的なjsonファイルの更新が必要になります。そのため、他のWorkflowやJobのArtifactsの取得を可能にするaction-download-artifactというライブラリを利用することにしました。これにより、CI上でArtifactsにアップロードされたqodana.sarif.jsonを参照できるようにしました。

このJobでは現在実行しているrun_numberから前回の実行時のrun_numberを計算し、前回のArtifactsを取得することでベースライン機能を利用しています。これにより前回実行時との差分を新規のビルドワーニングと既存のビルドワーニングとしてダッシュボードに出力できます。ダッシュボードについては後述します。

計測結果の可視化

Qodanaで計測されたデータをGitHub ActionsのArtifactsに保存し、GitHub Pagesにホストすることで計測結果を可視化しました。サンプルプロジェクトでの計測結果を用いて簡単にダッシュボードの使い方を紹介します。

Qodanaのダッシュボード

  • ACTUAL PROBLEMS:この計測でQodanaが検出したビルドワーニングを表示します
  • BASELINE:以前の実行からそのまま残っているビルドワーニングを表示します

タブを切り替えることで、この計測において新たに検知したビルドワーニングと前回の計測からそのまま残っているビルドワーニングを分けて確認できます。また、ビルドワーニングの種類や言語、重大度によってビルドワーニングをフィルターして表示できます。

Qodanaのダッシュボード

画像のように、ビルドワーニングの発生しているファイル名や内容、行数を確認できるためプロジェクト全体のビルドワーニングの把握がしやすくなり、スムーズに修正に取り掛かることができます。

Qodanaを活用した取り組み

ここからは、ZOZOTOWN Androidチームにおけるビルドワーニングへの取り組みを紹介します。

重大度の高いビルドワーニングへの対応

プロジェクト内に存在する多くのビルドワーニングに対応するため、QodanaのSeverity(重大度)による分類をもとに優先順位をつけて修正しました。

具体的には、優先度の高いビルドワーニング対応のチケットをファイル単位で作成し、そのファイルに存在するビルドワーニングを優先度に関わらずすべて修正する方針で進めました。このように進めることで修正による影響範囲を抑えつつ、優先度の高いビルドワーニングを着実に減らしながら、全体のビルドワーニング数も効率的に減らせるようにしました。

バックログのチケット一覧

新規のビルドワーニングの検知

ZOZOTOWN Androidチームでは2つの方法でQodanaを実行するように仕組み化しています。

1つ目は機能実装するfeatureブランチのベースブランチであるdevelopブランチに変更がプッシュされたタイミングで実行しています。これは、機能実装の間で発生したビルドワーニングの変化を確認したいためです。これをしておくことで、後述するfeatureブランチで実行する仕組みと合わせて差分が確認できるようになります。

2つ目は、Pull Requestに特定のラベルを貼ったタイミングで実行しています。これはfeatureブランチでQodanaを実行したい時に行います。このJobでは1つ目で実行されたQodanaの計測結果を取得しベースライン機能を活用して、developブランチとfeatureブランチ間の計測結果の差分を表示できるようにしています。こうすることにより、featureブランチで発生したビルドワーニングをダッシュボードで確認できるようになります。また、Qodana実行の際の引数にpost-pr-commentを渡すことで下記のような計測結果のサマリーをコメントしてくれるためレビュー時の見落としが減るかと思います。PRが作成されたタイミングやプッシュされたタイミングでQodanaを実行する方法も検討しましたが、実行時間が長いため、ラベルで実行を選択できるようにしました。

Qodanaのサマリーコメント

  MeasureWarning:
    name: Measure the warning
    if: github.event.label.name == 'ワーニング計測'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Download artifact
        id: download-artifact
        uses: dawidd6/action-download-artifact@v2
        with:
          workflow: code_quality.yml
      - name: Qodana
        uses: JetBrains/qodana-action@v2023.2
        with:
          args: --baseline,./qodana-report/results/qodana.sarif.json
          upload-result: true
      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ${{ runner.temp }}/qodana/results/report
          destination_dir: ./docs/qodana

まとめ

本記事では、ZOZOTOWN AndroidにおけるQodanaを用いたビルドワーニングへの取り組みを紹介しました。Qodanaの導入によりビルドワーニングの可視化と定期的な計測ができるようになりました。ZOZOTOWNアプリ内のビルドワーニングの数は導入前と比較すると減少しているものの、まだまだたくさん残っています。そのため、今後はビルドワーニングを0件にする運用の検討を進めていきたいと考えています。Qodanaの導入を検討している方がいれば、ぜひ参考にしてみてください。

ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。

hrmos.co


  1. 本記事のビルドワーニングはAndroid Studio内の問題のあるコードが黄色でハイライト、重大な問題の場合は、コードに赤色の下線が引かれるポップアップテキストを示します。
  2. Qodana実行時に出力されるSARIF仕様に従ってフォーマットされたJSONファイル形式のレポート
カテゴリー