Jenkinsとwebpack bundle analyzerでフロントエンドプロジェクトの合計サイズを定常的に計測

今回はフロントエンド側の成果物がどの程度容量で出力されているか計測するための Tips をご紹介したいと思います。

そもそも SEROKU ではバックエンドとフロントエンドにプロジェクトが分かれており、フロントエンドプロジェクトは Angular を利用しています。実装のモチベーションとなったのは、「プロジェクト内で minify されていない画像や動画がプロジェクトに紛れ込んだ時にすぐに気づける仕組みを構築したかった」ためです。

SEROKU の開発では CI を積極的に採用しており、Jenkins の Multibranch pipeline を活用して、ブランチにマージする前にテストを自動的に行うようになっています。この既存の Multibranch pipeline を使っているジョブに対して、プロジェクトの成果物の総容量の遷移をグラフで出力する機能を付け加えました。

Webpack Bundle Analyzerを見てみよう

どのような感じで表示されるのかは下記の画像の通りです。

X軸がビルド番号とビルド実施の時期で、Y軸がプロジェクトの成果物の容量となっています。

ブランチのアップデートを起点に自動的にビルドが走るため、成果物の容量を都度都度積算することでグラフが描画できるようになっています。

それでは導入手順を紹介していきます。

前提条件

  • Jenkins Multibranch pipeline が導入済みであること

    • Jenkins ジョブの設定が完了している
    • プロジェクトディレクトリに Jenkinsfile が配置されている

webpack bundle analyzer のインストール

まずはビルド時に成果物のサイズを計測してしてくれる webpack bundle analyzer をプロジェクト内にインストールします

# NPM
npm install --save-dev webpack-bundle-analyzer
# Yarn
yarn add -D webpack-bundle-analyzer

Angular のプロジェクトに導入する場合にはこれだけで完了です

Jenkins に Plot plugin を導入する

[Jenkinsの管理] -> [プラグインの管理] -> [利用可能タブ] を開き、「Plot Plugin」と検索してプラグインをインストールします

Jenkinsfile に plot を行う処理を追記する

こちらがメインの処理です。こちらは記述量が多いため、先にスクリプトを提示します。

// webpack bundle analyzer で取得した bundle のサイズを保持しておくための変数
def totalBundleSize = 0

pipeline {
  agent any
  // (省略)
  stage('Webpack Bundle Analyzer') {
    agent {
      dockerfile {
        filename 'Dockerfile.jenkins'
      }
    }

    steps {
      unstash 'built'

      script {
        // ファイル容量の一覧を出力
        sh 'jq -r "[.assets[] | {name: .name, size: .size}]" dist/browser/stats.json | jq "sort_by(.size) | reverse"'

        // 総量を各々で出力(MB 単位)
        totalBundleSize = sh(
          returnStdout: true,
          script: 'jq -r "[.assets[] | .size] | add" dist/browser/stats.json | awk \'{ byte = $1 / 1024 / 1024; print byte }\''
        ).trim()

        writeFile file: "bundle_total_size.csv", text: "SEROKU\n${totalBundleSize}\n"
        // use Plot plugin
        plot csvFileName: "plot-${TOPIC_NAME}.csv", csvSeries: [[displayTableFlag: false, exclusionValues: '', file: 'bundle_total_size.csv', inclusionFlag: 'OFF', url: '']], group: 'plot', numBuilds: '50', style: 'line', title: 'Webpack Bundle Analyzer', yaxis: 'サイズ(MB)', exclZero: true
      }
    }

    post {
      always {
        // (省略)
      }
    }
  }
}

各行に対してコメントをしていきます。

        sh 'jq -r "[.assets[] | {name: .name, size: .size}]" dist/browser/stats.json | jq "sort_by(.size) | reverse"'

こちらは、webpack bundle analyzer で出力された json ファイルの一部を読むために、jq コマンドを利用して、サイズ順にソートしています。

ジョブのデバッグや、特定のビルドのみ詳細を見たいときのために、Jenkins ジョブのコンソール出力に残しておいています。

        // 総量を各々で出力(MB 単位)
        serokuTotalBundleSize = sh(
          returnStdout: true,
          script: 'jq -r "[.assets[] | .size] | add" dist/browser/stats.json | awk \'{ byte = $1 / 1024 / 1024; print byte }\''
        ).trim()

こちらが実際のビルド成果物の総容量を取得している個所です。 bytes 単位で出力すると細かい値になってしまうため、総容量を MB 単位に直して出力するように sed コマンドを利用しています。

        writeFile file: "bundle_total_size.csv", text: "SEROKU\n${totalBundleSize}\n"
        // use Plot plugin
        plot csvFileName: "plot-${TOPIC_NAME}.csv", csvSeries: [[displayTableFlag: false, exclusionValues: '', file: 'bundle_total_size.csv', inclusionFlag: 'OFF', url: '']], group: 'plot', numBuilds: '50', style: 'line', title: 'Webpack Bundle Analyzer', yaxis: 'サイズ(MB)', exclZero: true

writeFile を使って、現在の値を csv としてファイル出力しています。

writeFile で出力したファイルは一時的なものであり、グラフ化するための蓄積先は後述の plot-${TOPIC_NAME}.csv に溜まっていきます。

各種オプションは Plot Plugin のドキュメントを参照してください

最後に

以上で Jenkins の Multibranch pipeline 配下でのグラフプロットが可能になります。

この仕組みを導入したことによる劇的な改善はありませんが、開発の際に容量の大きすぎるファイルを発見することができ、多少は改善に繋がったと思います。

Plot Plugin と Jenkinsfile を組み合わせは他のことに応用できそうなので、今後もこういった改善活動を続けていきたいと思います。