メインコンテンツへスキップ
ブログ一覧に戻る
Web制作・運用

CI/CD入門:テストの自動化から公開条件の設計まで

2024年7月27日
最終更新: 2026年10月8日
8分で読めます

この記事でわかること

CI、継続的デリバリー、継続的デプロイメントの違いを整理し、小さなNode.jsの例から検査と公開の境界を確かめます。

2026年10月8日に、actions/checkoutの現行READMEとactions/setup-nodeの現行READMEを再読しました。checkout@v7は既定ではイベントのref/SHAに対する1コミットだけを取得し、全履歴やタグが必要ならfetch-depth: 0などを明示します。setup-node@v7はNodeの版を明示する例を示し、lockfileを前提にnpm・yarn・pnpmの依存キャッシュを設定できます。これはActionの設定範囲であり、CI成功、キャッシュの安全性、公開承認、復旧可能性を保証するものではありません。

手元ではテストが通るのに、別の人の環境では失敗する。公開のたびに同じ手順を思い出している。CI/CDは、こうした確認を繰り返せる形にする取り組みです。ただし、検査の成功と、本番へ出してよいという判断には、それぞれ条件があります。

30秒で要点

  • CIは変更を頻繁に統合し、自動のビルドやテストで問題を早く見つける実践です。
  • 継続的デリバリーは公開できる状態を保つこと。継続的デプロイメントは、決めた条件を満たす変更を本番へ自動反映することです。
  • 最初は短い検査から始めます。公開の自動化には、権限、対象の成果物、確認条件、監視と復旧手順も必要です。

二つのCDを区別すると、責任の境界が見える

CIはContinuous Integration(継続的インテグレーション)の略です。変更を小さく統合し、ビルドやテストを繰り返します。CIツールを入れても、長期間統合せず、失敗を放置する運用では問題の発見が遅れます。

CDには二つの意味があります。GitLabの概念説明でも、DeliveryとDeploymentを分けています。

表が収まらない場合は、横にスクロールできます

実践保ちたい状態本番へ進む判断
CI変更を継続して統合・検証できるこの段階だけでは公開を決めない
Continuous Delivery検証した変更を公開できるよう準備する公開する時点を人が選べる
Continuous Deployment事前に決めた条件で公開まで進める毎回の手動承認を挟まず自動で進む

ステージングは、本番に近い条件で確かめる検証環境です。そこへの自動配置はDeliveryの一例ですが、「ステージングに置けた」という事実だけで公開準備が整ったとは限りません。外部サービスの設定、権限、データ移行など、何を同じ条件にして確認したかが重要です。

変更をCIで検査し、識別できる成果物を作る。その後は公開条件を確認し、デリバリーでは人が公開時点を選び、デプロイメントでは事前ルールで進む。本番反映後は監視と復旧へつなぐ。
図1:検査から公開後までの流れの例。この記事のコード例はCIの入口を扱います。公開条件や本番反映は、別途設計して設定する部分です。

小さなNode.jsの例で、検査の成功と失敗を見る

まずは外部サービスに接続しない例を作ります。必要なのはNode.js 24系とnpmです。2026年10月4日に確認したNode.jsの公式一覧では24系がLTS(長期サポート)です。実務ではアプリが対応するサポート中の版を選び、開発環境とCIでそろえます。

例の仕様は「商品小計が5,000円以上なら送料0円、それ未満なら500円」。税や地域別送料は扱いません。空の作業フォルダに次の3ファイルを作ります。

package.jsonはテストの入口です。外部パッケージは使いません。

{
  "name": "shipping-ci-example",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "scripts": {
    "check": "node --check shipping.mjs",
    "test": "node --test shipping.test.mjs"
  }
}

shipping.mjsには、送料の条件と入力の範囲を書きます。

export function shippingFee(subtotalYen) {
  if (!Number.isSafeInteger(subtotalYen) || subtotalYen < 0) {
    throw new RangeError("小計は0以上の安全な整数で指定します");
  }
  return subtotalYen >= 5000 ? 0 : 500;
}

shipping.test.mjsでは、条件の境目と受け付けない入力を調べます。

import test from "node:test";
import assert from "node:assert/strict";
import { shippingFee } from "./shipping.mjs";

test("無料になる金額の境界を確認する", () => {
  assert.equal(shippingFee(0), 500);
  assert.equal(shippingFee(4999), 500);
  assert.equal(shippingFee(5000), 0);
  assert.equal(shippingFee(5001), 0);
});

test("不正な小計を受け付けない", () => {
  for (const input of [-1, 0.5, "5000", NaN, Infinity]) {
    assert.throws(() => shippingFee(input), RangeError);
  }
});

node:testはNode.jsに含まれるテスト機能です。このフォルダで次を実行します。

npm install --package-lock-only --ignore-scripts --no-audit --no-fund
npm ci
npm run check
npm test

最初のコマンドでpackage-lock.jsonを作ります。このファイルもソースと一緒に管理します。npm ciの仕様では、lockが存在し、package.jsonと整合している必要があります。内容が食い違う場合、CIの中で勝手に直して成功させず、失敗します。

次に実装の>= 5000を一時的に> 5000へ変え、npm testが失敗することを確かめます。元へ戻すと成功します。これで、テストが実行されているだけでなく、仕様を壊す変更を検出できることを確認できます。この小例にコンパイル工程はないため、架空のbuildコマンドは置いていません。

GitHub Actionsで同じ検査を実行する

GitHubに検証用リポジトリを用意し、上の4ファイルと.github/workflows/ci.ymlを管理する場合の設定です。mainへの変更と、mainに向けたプルリクエスト(変更内容のレビュー依頼)で検査します。

name: Shipping CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

permissions:
  contents: read

jobs:
  check:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
        with:
          node-version: "24"
          package-manager-cache: false
      - run: npm ci
      - run: npm run check
      - run: npm test

checkoutはソースを取得し、setup-nodeは実行するNode.jsを用意します。長い文字列はActionのコードの版を示すコミットSHAです。ここでは公式リポジトリのv7.0.0タグとの対応を確認したSHAを使っています。GitHubの指針に沿って版を固定し、更新時には変更内容も確認します。

これはGitHubが用意するランナー向けのCI例で、デプロイ用の資格情報や公開ジョブを含みません。ローカルでの検査と、GitHub上の実行は別です。設定を追加したら、Actionsのログで実際のNode版、対象コミット、各コマンドの終了結果を確認します。セルフホストのランナーやGitHub Enterpriseでは、Actionの対応条件もcheckoutとsetup-nodeで確認してください。

実際のWebアプリでは、型検査、意味のある単体・統合テスト、本番向けビルドを追加します。公式のNode.js向けガイドと、自分のpackage.jsonのコマンドを照合してください。必須の検査を--if-presentなどで省略可能にすると、定義漏れを成功と見誤る場合があります。

公開の自動化へ進む前に、四つの条件をそろえる

検査が安定したら、成果物と公開の条件を設計します。成果物は、実行ファイルやWebアプリのビルド結果など、配置する対象です。同じコミットから作ったつもりでも、外部依存やビルド時の設定が変われば内容が変わることがあります。

表が収まらない場合は、横にスクロールできます

条件確認する内容
何を出すか検査した成果物のID・内容のハッシュ・対象コミットを特定できる
どこへ出すか検証環境と本番を分け、必要な設定・データ移行を確認する
誰が許可するか手動判断か自動条件かを決め、必要な保護ルールと最小権限を設定する
失敗したらどうするか公開後の確認項目、停止条件、復旧担当、戻せる範囲を決める

ジョブを分けると、前のジョブで作ったファイルや依存関係が自動で残るとは限りません。成果物の保存・取得、各ジョブの依存復元を明示します。キャッシュは時間短縮の補助であり、公開対象そのものの受け渡しと混同しません。

GitHubのenvironmentでは、設定した保護ルールをジョブ実行前に評価できます。ただし、productionと命名するだけでは承認待ちになりません。利用できる機能と設定方法を確認し、必要なレビューとブランチ制限を実際に設定します。

プルリクエストの検査に本番の秘密情報を渡す必要もありません。特にpull_request_targetなどの強い権限を持つ文脈で、信頼していない変更を取得して実行する構成は、GitHubのセキュリティ上の注意点を確認する必要があります。

導入効果は、待ち時間と失敗の内訳で見る

ツールを選ぶときは、今のソース管理との接続、実行環境、機密情報の扱い、保守する担当者を比較します。GitHubやGitLabに組み込まれた仕組みでも、自社でランナーを管理する構成でも、検査の内容と失敗時の対応は自分たちで決めます。

効果を測るなら、同じ対象範囲・期間で「変更を送ってから最初の結果までの時間」「本番への反映回数」「公開後に修正や復旧が必要になった変更件数/本番へ反映した変更件数」を記録します。速さだけでなく、失敗の原因がコード、環境、テストの不安定さのどこにあるかを分けます。導入だけで一定の改善率が得られるとは言えません。

最初の到達点は、壊した検査が確実に止まり、理由を読み取って直せることです。その状態から、検証した成果物を本番へ届け、問題があれば止められる手順へ少しずつつなげます。変更を統合する運用は、Gitのブランチ戦略もあわせて整理すると判断しやすくなります。

よくある質問(FAQ)