継続的インテグレーション(CI)

GitBeginner
オンラインで実践に進む

はじめに

大きなチームで本を書いていると想像してください。誰かが新しい章を提出するたびに、スペルミスや筋書きの矛盾を確認する必要があります。これを手作業で行うのは大変です。

ソフトウェア開発では、この問題を 継続的インテグレーション(CI)で解決します。CI は、通常、依存関係のインストール、テストの実行、プロジェクトの検証を更新のたびに行い、変更を早い段階で自動的にチェックします。

重要なのは、すぐにフィードバックを得ることです。変更の数日後ではなく、変更直後に問題を見つけたほうが、簡単に修正できます。

この速いフィードバックがあるため、CI は通常、チームが最初に構築する自動化レイヤーです。すべての push を自動的な品質チェックに変えることで、main ブランチを保護します。

この実験では、シンプルな Node.js アプリケーション用の安全網を構築します。次の作業を行います。

  • 基本的な計算を行う小さなアプリケーションを作成します。
  • 計算が正しいことを確認するテストを記述します。
  • コードを push するたびに、このテストを自動実行するよう GitHub Actions を設定します。
  • 意図的にコードを壊し、安全網がエラーを検出する様子を確認します。

前提条件

この実験は前の実験を前提としています。前の実験で作成した github-actions-basics リポジトリを引き続き使用します。

Node.js プロジェクトを準備する

まず、テスト対象のコードが必要です。小さな Node.js アプリケーションを作成します。プログラミング経験がなくても心配ありません。必要なコードはすべて用意されています。

プロジェクトディレクトリへ移動します。新しい実験環境なので、リポジトリをもう一度ダウンロードします。

cd ~/project
git clone https://github.com/your-username/github-actions-basics.git
cd github-actions-basics

注:your-username を自分の GitHub ユーザー名に置き換えてください。

実際のプロジェクトを再現するために、3 つのファイルを作成します。ファイルの内容をコマンドラインから直接指定してすばやく作成できる cat コマンドを使用します。

1. 「レシピ」(package.json)

このファイルには、プロジェクトの種類と必要なツールを記述します。ここでは、プロジェクトが jest というテストツールに依存していることを指定します。

cat > package.json << EOF
{
  "name": "ci-demo",
  "version": "1.0.0",
  "scripts": {
    "test": "jest"
  },
  "dependencies": {
    "jest": "^29.7.0"
  }
}
EOF

2. 「料理」(index.js)

これが実際のアプリケーションコードです。2 つの数値を足し合わせるシンプルな関数です。

cat > index.js << EOF
function sum(a, b) {
  return a + b;
}
module.exports = sum;
EOF

3. 「味見テスト」(index.test.js)

これが自動テストです。1 と 2 を足した結果が 必ず 3 になることを確認します。

cat > index.test.js << EOF
const sum = require('./index');

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});
EOF

ファイルを作成したので、GitHub に保存します。標準的な Git コマンドを実行して、作業内容をアップロードします。

git add .
git commit -m "Add Node.js project files"
git push

GitHub への再ログインを求められる場合があります。前回と同じように、ユーザー名と Personal Access Token(PAT)を使用してください。

CI ワークフローを作成する

次に、このコードをテストする方法をロボットアシスタントに伝えます。標準の .github/workflows フォルダーに、ci.yml という新しいワークフローファイルを作成します。

touch .github/workflows/ci.yml

このファイルを nano エディターで開きます。

nano .github/workflows/ci.yml

次の設定を追加します。新しく登場する部分については、この後で説明します。

name: Node.js CI

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 16

ここでは、強力な新しいキーワード uses を使用しています。

これは GitHub Actions の重要なパターンです。すべてのシェル操作を手作業で記述する代わりに、ワークフローでは、チェックアウト、言語環境のセットアップ、アーティファクトのアップロードなど、よく使う処理をまとめた再利用可能なアクションを組み合わせることがよくあります。

uses は、ロボットの頭脳にスキルをダウンロードするものだと考えてください。複雑なスクリプトを最初から記述する代わりに、他の人が作成した既成のアクションを使用できます。

  • actions/checkout@v4: この標準アクションは、ロボットに「コードをダウンロードする」よう指示します。これがないと、ロボットのワークスペースは空のままです。
  • actions/setup-node@v3: runner に Node.js をインストールします。また、一貫性を保つために特定のバージョン(16)を渡しています。

エディターで保存して終了します(Ctrl+X、Y、Enter)。

インストールとテストのステップを追加する

コードのチェックアウトとツール(Node.js)の準備ができました。次に、実際にテストを実行します。

Node.js では、これに使用する標準コマンドが 2 つあります。

  1. npm install: package.json に記載されたすべての依存関係(jest テストツールなど)をダウンロードします。
  2. npm test: package.json に定義されたテストスクリプトを実行します。

これらのステップを組み合わせることで、CI の中心的な流れを再現できます。環境を準備し、依存関係をインストールし、クリーンな runner 上でコードを検証してから、変更を問題ないものとして受け入れます。

これらのステップをワークフローに追加します。もう一度 .github/workflows/ci.yml を開きます。

nano .github/workflows/ci.yml

ファイルの末尾に、次の 2 つのステップを追加します。インデントが前のステップとそろっていることを確認してください。

- name: Install dependencies
  run: npm install

- name: Run tests
  run: npm test

完成したファイルは、次のようになります。

name: Node.js CI

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 16

      - name: Install dependencies
        run: npm install

      - name: Run tests
        run: npm test

エディターで保存して終了します。

次に、このファイルを GitHub に push して安全網を有効にします。

git add .
git commit -m "Add CI pipeline"
git push

GitHub でリポジトリを開き、Actions タブをクリックします。「Node.js CI」という新しいワークフローが実行されているはずです。完了すると、緑色のチェックマークが表示されます。これはコードが正常に動作したことを示します。

CI Pipeline

ビルドを壊す

安全網は、転んだときに受け止めてくれて初めて役に立ちます。CI システムが実際に動作する様子を見るために、意図的にコードを「壊して」みましょう。

テストファイルを編集し、間違った内容にします。1 + 2 の答えが 4 になると主張します。

nano で index.test.js を開きます。

nano index.test.js

テストの期待値を 3 から 4 に変更します。

const sum = require("./index");

test("adds 1 + 2 to equal 3", () => {
  expect(sum(1, 2)).toBe(4); // CHANGED 3 to 4
});

保存して終了します。次に、この「バグ」を commit して push します。

git add .
git commit -m "Intentionally break the test"
git push

すぐに GitHub の Actions タブへ戻ります。新しいワークフローの実行を確認してください。

しばらくすると、実行が失敗するはずです。commit の横に 赤い X アイコンが表示されます。これは CI システムが「停止してください。何か問題があります」と知らせている状態です。

失敗した実行をクリックし、次に build ジョブをクリックします。Run tests ステップを展開すると、次のエラーログを確認できます。

Error: expect(received).toBe(expected) // Object.is equality

Expected: 4
Received: 3

このシンプルな赤い X により、バグが発生してから数日または数週間後ではなく、発生した瞬間に検出できるため、開発者は数え切れないほどの時間を節約できます。

CI Pipeline Failed

まとめ

この実験では、本格的な継続的インテグレーションパイプラインを作成しました。

次のことを学びました。

  • package.json を使って、プロジェクトの「レシピ」を定義する方法。
  • uses キーワードを使って、actions/checkout や actions/setup-node などの強力なツールを導入する方法。
  • コードを変更するたびにロボットが検証できるよう、テストを自動化する方法。
  • コードの安全網として「赤い X」が果たす重要な役割。

次は、動作するコードをパッケージ化して、世界に公開する方法を学びます。