見出し画像

Nuxt アプリ開発者のためのBitbucket Pipelines のキホン

🌱 はじめに

こんにちは、キャベたまご🥚です。
現在は、ナビタイムジャパンで Nuxt で新規Webアプリの開発を行っています。

今回は、その開発で取り入れていた、以下の3つを中心にご紹介します。

① 🏋️ Nuxt のプロダクトで導入しやすい基本的な step
② 🏎️ 実行速度改善のTips
③ ❤️ 導入に際して大事にしていた考え方

前提のお話ですが、当社ではバージョン管理ツールとして、Bitbucketを利用しています。また、アプリケーションの主要な環境は以下です。

TypeScript    : v5 系
・Node.js       : v18 系
・Nuxt          : v3

また、今回のプロダクトソースの構成は、モノリポ(Monorepo)であり、フロントエンドとバックエンドのコードを同一のGitリポジトリで管理しています。

├── propject-root
│   ├── frontend
│   └── backend

♻️ Bitbucket Pipelinesとは

Bitbucketには、CI(継続的インテグレーション:Continuous Integration)を実装するための機能として Bitbucket Pipelines が備わっています。CIはアジャイル開発手法の一つで、導入することによりソフトウェア開発プロセスを効率化し、品質を向上しやすくなります。

Bitbucketでの導入方法は至ってシンプルで、プロジェクト配下に bitbucket-pipelines.yml を追加し、適切な step を記述するだけです。(より詳細な導入方法を知りたい方はこちらからご確認ください)

├── propject-root
│   ├── bitbucket-pipelines.yml

Bitbucket Pipelines を未導入で本記事を読んで気になった場合は、ぜひ導入をご検討してみてください。

✨ 出来るようになること

これからご紹介する step を設定することで、ビルドやUnitテスト、型チェック、ライブラリ脆弱性診断のプロセスが自動化され、これらを手動で行う手間を省くことができるようになります。

また、変更により引き起こされるエラー等のFBが早まり、安心して master ブランチに変更をマージしやすくなります。

🏋️ Nuxt のプロダクトで導入しやすい基本的な step

以下は、実際にプロジェクトに導入した step を4つご紹介します。

① build
アプリケーションをビルドする step です。ビルドに失敗すると、このScriptを実行する step がfailedになります。

name: Build
caches:
  - node
script:
  - npm install
  - nuxt build

② Unit Test
Unit Testを実行する step です。1ケースでもテストに失敗すると、このScriptを実行する step がfailedになります。テストライブラリとして、⚡️Vitestでファイル内のUnitテストを実行するScriptになっています。

name: Unit Test
caches:
  - node
script:
  - npm install
  - npx vitest

③ 型チェック
TypeScriptの型チェックをアプリケーション全体に行う step です。SFCの template内も型チェックが行われ、型エラーを検知すると、このScriptを実行する step がfailedになります。型チェックには、nuxi typecheck を利用しています。

name: Typecheck
caches:
  - node
script:
  - npm install
  - npx nuxi typecheck

④ ライブラリのセキュリティ検査
利用しているライブラリの脆弱性を検知する step です。
こちらの例では、high 以上の脆弱性が検知されると、このScriptを実行する step がfailedになります。
どのLevelを検知、失敗とするかはチームで合意をとって決めていただくと良いのかと思います。検知には、npm audit を利用しています。(それぞれのLevelがどういった指標になっているかを知りたい方はこちらからご確認ください)

name: Check package vulnerability
caches:
  - node
script:
  - npm audit --audit-level=high

🏎️ 実行速度の改善のTips

これまでご紹介した4つの step は全て直列で実行すると、3分以上かかりました。そのため、ちょっとした文言の変更などを加えたい際に実行時間が多く、フロー効率を落としてしまう可能性がありました。

そこで、以下にご紹介する ①並列処理と②差分検知の対応を行うことで、実行時間を50%以上短縮することができました。

もし未導入の場合は、導入していいただけると実行時間が短縮されると思います。ぜひ、ご参考になれば幸いです。

① 並列処理
Bitbucket Pipelines には、並列で実行するための parallel オプションが用意されています。この場合、BuildとUnit Test が並列で処理されます。

parallel:
  - name: Build
    script:
      - npm install
      - nuxt build
  - name: Unit Test
    script:
      - npm install
      - npm run vitest

② 差分検知
Bitbucket Pipelines には、変更差分を検知して、変更のあった箇所に応じて、処理を実行する changesets というオプションが用意されています。

前述した通り、今回のプロダクトソースは、モノリポ(Monorepo)であるため、バックエンドの変更のみであってもフロントエンドの step が実行されます。

そのため実行する必要のない step まで実行され、余計な待ち時間が発生しておりました。

そこで、差分検知の仕組みを導入し、フロントエンドか bitbucket-pipelines.yml 自身の変更があったときのみ、 step が実行されるように対応しました。

name: Build
caches:
  - node
# 以下の 5行が該当処理箇所
condition:
  changesets:
    includePaths:
      - 'frontend/**'
      - 'bitbucket-pipelines.yml'
script:
  - npm install
  - nuxt build

📓 最終的な bitbucket-pipelines.yml

最終的には、以下のよう bitbucket-pipelines.yml になりました。
インデントが一つでもずれると、構文エラーになってしまうため、コピーされる際はご注意ください。

pipelines:
  default:
    - parallel:
        - step:
            name: Unit Test
            caches:
              - node
            condition:
              changesets:
                includePaths:
                  - 'frontend/**'
                  - 'bitbucket-pipelines.yml'
            script:
              - npm install
              - npm run vitest
        - step:
            name: Typecheck
            caches:
              - node
            condition:
              changesets:
                includePaths:
                  - 'frontend/**'
                  - 'bitbucket-pipelines.yml'
            script:
              - npm install
              - npx nuxi typecheck
        - step:
            name: Build
            caches:
              - node
            condition:
              changesets:
                includePaths:
                  - 'frontend/**'
                  - 'bitbucket-pipelines.yml'
            script:
              - npm install
              - nuxt build
        - step:
            name: Check package vulnerability
            caches:
              - node
            condition:
              changesets:
                includePaths:
                  - 'frontend/**'
                  - 'bitbucket-pipelines.yml'
            script:
              - npm audit --audit-level=high

# 本来は、バックエンドのCIのためのScriptが続きますが、ここでは省略します

🗣️ チームからの反応

フロントエンド開発チームからは導入以降「毎日、手動で実行する手間が省けてありがたい」や「Pipelineのうち一つの step でもfailedするとマージできないようになっていたので、PRに安心感が生まれた」といったポジティブな声をもらえました。

ここでは詳細を説明しませんが、Pipelineのうち一つの step でもfailedするとマージできないようにする設定も可能です。気になる方は、こちらをご覧ください。

❤️ 大事にしていたこと

今回、Bitbucket Pipelines を整備していく上で大事にしていた考え方をご紹介します。

① 小さく始める
1つ目は、「小さく始める」ということです。例えば、これまで厳格に型チェックをしていなかったが、型チェックの step を導入すると大量のエラーが発生して、CIを導入するまでに時間がかかってしまう可能性があります。

そこで、最初は導入ハードルの低い step から、少しずつイテレーティブにCIを拡充していくことでまず動くものが整備できます。

今回で言えば、一番担保すべき build ができるように step を書くだけのシンプルな Pipeline を構築するところからスタートできると良いのではないかと思います。

② トラブルは、カイゼンのチャンス!
色々な step をご紹介してきましたが、実は、開発初期からCIを整備できていたわけではなく、途中から導入しました。

きっかけとしては、開発初期にmaster ブランチに取り込んだ変更が起因でアプリがbuild できないことに気づけなかったことでした。その後、”仕組みで解決する” 方法をチームで話して、CIを Bitbucket Pipelines で構築するという考えに至りました。

問題が起こった時は、カイゼンのチャンスと前向きに捉えていけると良いのではないかと思います。

⛰️ 今後の展望

今後は、より堅牢な開発環境づくりのために E2E(End to End)テスト、VRT(Visual Regression Test)テストの導入を検討しています。

一方で、Kent C.Dodds が提唱した Testing Trophy で説明されるように、これらのテストは🏆トロフィーの頂点のE2E Testに当たるものであり壊れやすく、実行時間や運用コストもかかる傾向にありますそのため、安直に全てのパターンを網羅するようにテストを実装すべきではないと考えています。

Testing Trophy

そこで、費用対効果を念頭に置いた上で適切なテストスコープについての議論を行い、導入を進めていこうと思います。

ここまで、読んでいただきありがとうございました!

基本的な Bitbucket Pipelines の記法のご紹介でしたが、
みなさんの開発体験、生産性の向上に少しでも寄与できましたら幸いです。

#CI