技術資料

GitHub ActionsでPHP・LaravelのCIを始める:テスト・Lint・ビルドを自動化するWorkflowの基本

作成日:2026.09.13

PHP・Laravelプロジェクトを対象に、GitHub ActionsのWorkflow、イベント、ジョブ、ステップの基本を説明します。Composerのテスト、npm ci、フロントエンドビルド、Laravel PintをCIへ組み込む方法に加え、featureブランチやPull Requestで実行される条件、失敗時のログ確認、Secrets・権限・Actionのバージョン管理についても整理します。

PHPやLaravelを使ったアプリケーションでは、コードを変更するたびにテスト、Lint、JavaScriptのビルドなどを確認します。

手元で確認するだけでもよいのですが、実行を忘れたり、開発者ごとに環境や手順が違ったりすると、問題に気づくタイミングが遅くなります。そこで、GitHub Actionsを使って、リポジトリへ変更を反映したときに検証処理を自動実行してみます。

今回は、実際のPHP・Laravelプロジェクトで使われていたCI設定をもとに、特定のプロジェクト名や機能へ依存しない形で、Workflowの基本と検証コマンドの組み立て方を整理します。対象はテスト、Lint、ビルドまでとし、デプロイや本番環境への反映は扱いません。

GitHubのリポジトリ、ブランチ、commit、Pull Requestの基本については、以前のGitHub入門の記事で説明しています。この記事では、それらを使って変更を管理した後に、検証を自動化する方法へ焦点を当てます。

今回の前提

この記事では、次のようなプロジェクトを想定します。

  • GitHub上にリポジトリがある
  • PHPとLaravelを使っている
  • ComposerでPHPの依存関係を管理している
  • Node.jsとnpmでフロントエンドの依存関係を管理している
  • PHPUnitなどのテストを実行できる
  • GitHub Actionsをリポジトリで利用できる

PHP、Laravel、Node.jsのバージョンは、プロジェクトのcomposer.jsonpackage.json、ローカル開発環境などに合わせて決めます。この記事に登場するバージョン番号は、特定の環境をそのまま推奨するものではありません。

CIとGitHub Actionsの役割

CIは、Continuous Integrationの略です。ソースコードの変更をリポジトリへ反映したときに、テストやコード検査などを自動実行し、問題を早めに見つけるための考え方です。

GitHub Actionsは、GitHubのリポジトリに置いたWorkflowを、PushやPull Requestなどのイベントをきっかけに実行できます。GitHub公式ドキュメントでは、Workflowは1つ以上のジョブから構成され、ジョブの中でActionやスクリプトをステップとして実行すると説明されています。詳しくはGitHub ActionsのWorkflow公式ドキュメントを参照してください。

CIは、しばしばCI/CDという形で、リリースやデプロイの自動化とセットで言及されます。CIで変更を検証した後、その変更をリリース可能な状態に整えたり、環境へ反映したりする流れまで続くためです。

ここでいうCDは、Continuous Delivery(継続的デリバリー)またはContinuous Deployment(継続的デプロイメント)の略です。CIで検証した変更を、リリース可能な状態に整えたり、本番環境へ自動的に反映したりする工程を指します。

CIとCDは、どちらも自動化の仕組みですが、この記事では役割を分けて考えます。

区分 主な目的 この記事の対象
CI テスト、Lint、ビルドなどで変更を検証する 扱う
CD 検証済みの成果物を配布・デプロイする 扱わない

最初からデプロイまで自動化すると、失敗したときに原因を追いにくくなります。まずは、変更を受け入れてよいか判断するための検証に絞る方が、導入後の確認を進めやすいと思います。

Workflowファイルを作成する

GitHub ActionsのWorkflowは、リポジトリの.github/workflowsディレクトリへ、.ymlまたは.yamlファイルとして保存します。

例えば、次のようなファイルを.github/workflows/ci.ymlとして作成します。これは、PHP・Laravelプロジェクトで実行する検証処理を、実際の構成に合わせて一般化した例です。

name: CI

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

permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          extensions: mbstring, dom, fileinfo, sqlite, pdo_sqlite, zip
          coverage: none

      - name: Setup Node.js
        uses: actions/setup-node@v6
        with:
          node-version: 24
          cache: npm

      - name: Install PHP dependencies
        run: composer install --no-interaction --prefer-dist --no-progress

      - name: Prepare application environment
        run: |
          cp .env.example .env
          php artisan key:generate --ansi

      - name: Run PHP tests
        run: composer test

      - name: Install JavaScript dependencies
        run: npm ci

      - name: Build frontend assets
        run: npm run build

      - name: Check PHP code style
        run: ./vendor/bin/pint --test

Workflowの基本的な要素は、次のように読むことができます。

  • name: Actions画面に表示するWorkflow名
  • on: Workflowを実行するイベント
  • permissions: Workflowへ与える権限
  • jobs: 実行する処理のまとまり
  • runs-on: ジョブを実行するRunnerの種類
  • steps: ジョブの中で順番に実行する処理
  • uses: 既存のActionを利用する処理
  • run: シェルコマンドを実行する処理

GitHub公式のGitHub Actions Quickstartでも、Workflowを.github/workflowsへ保存し、イベント、ジョブ、ステップを定義する流れが紹介されています。

Push、Pull Request、手動実行

今回の例では、onへ3種類のイベントを指定しています。

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

pushbranches: [main]は、mainブランチへPushされたときにWorkflowを実行する指定です。pull_requestを指定すると、Pull Requestに関係するイベントでも実行されます。

この例では、pushpull_requestが別々のイベントとして設定されています。push.branches: [main]は、main以外のブランチへのPushを対象にしません。

そのため、featureブランチへPushしたときはpushイベント自体は発生していても、ブランチの条件に一致せず、このWorkflowは実行されません。また、Pull Requestを作成する前にはpull_requestイベントも発生しません。Pull Requestを作成すると、その作成イベントでWorkflowが実行され、以後そのPull Requestへコミットを追加したときにも再実行されます。

Pull Requestを作成する前からfeatureブランチを検証したい場合は、pushの対象へfeatureブランチを追加するか、ブランチによる絞り込みを外します。

on:
  push:
    branches:
      - main
      - 'feature/**'
  pull_request:

ブランチ名の規則が異なる場合は、実際の命名に合わせてパターンを変更します。Pull Request作成後の検証だけでよいのか、作業中のPushも検証するのかを、実行時間や利用量と合わせて決めるとよいでしょう。

workflow_dispatchは、GitHubのActions画面から手動で実行するためのイベントです。Workflowを作成した直後の確認や、定期的な再検証に使えます。

イベントの種類やブランチ・パスの絞り込みは、プロジェクトの運用に合わせて決めます。イベントを増やすほど実行回数も増えるため、すべての変更で実行する必要があるかは考えておく必要があります。

Runnerとシェル

runs-on: ubuntu-latestは、GitHubが用意するUbuntuのRunnerでジョブを実行する指定です。今回の例で使っているcpなどのコマンドは、Ubuntu上のシェルで実行されます。

ローカルの開発環境がWindowsでも、WorkflowのRunnerがUbuntuなら、CIのコマンドはUbuntu上で動きます。Windows PowerShellで動くコマンドをそのままWorkflowへ書けるとは限らないため、runs-onrunで使うシェルを確認してください。

権限を明示する

サンプルでは、Workflowの権限としてcontents: readだけを指定しています。テストやビルドだけが目的なら、リポジトリへ書き込む権限は必要ありません。

権限を省略した場合の既定値は、リポジトリや組織の設定に影響される可能性があります。Workflowが必要とする権限を確認し、不要な書き込み権限を与えないようにします。

PHPとComposerの環境を準備する

PHPのテストを実行する前に、RunnerへPHPと必要な拡張を用意します。

- name: Setup PHP
  uses: shivammathur/setup-php@v2
  with:
    php-version: '8.2'
    extensions: mbstring, dom, fileinfo, sqlite, pdo_sqlite, zip
    coverage: none

ここで指定するPHPのバージョンは、composer.jsonrequire.php、本番環境、開発環境と整合させます。CIだけ別のバージョンになると、ローカルでは成功する処理がCIで失敗したり、その逆が起きたりします。

必要な拡張もプロジェクトによって異なります。Composerの依存関係や、テストで利用する機能を確認してから指定します。拡張名を増やせばよいというものではなく、不要な環境差を増やさないことも大切です。

PHPを用意したら、Composerの依存関係をインストールします。

composer install --no-interaction --prefer-dist --no-progress

composer installは、通常composer.lockに記録された依存関係をインストールします。CIで実行する場合は、開発環境と同じ依存関係を再現できるよう、composer.lockをリポジトリへ含めておくことが重要です。

composer updateをCIで実行すると、実行時点の条件によって依存関係が変わる可能性があります。依存関係の更新は別の変更として行い、通常のCIではcomposer installを使う方が結果を確認しやすいと思います。

テスト用のアプリケーション環境を用意する

Laravelアプリケーションのテストでは、.envやアプリケーションキーが必要になる場合があります。例えば次のように.env.exampleをコピーしてからキーを生成するなどします。

cp .env.example .env
php artisan key:generate --ansi

ここで使うのはCI専用の環境です。開発環境や本番環境の.envをWorkflowへコピーしたり、実際のAPIキーやデータベースのパスワードを登録したりしないでください。

テストでデータベースを使う場合も、開発用データベースとは分けます。例えば、PHPUnitの設定でSQLiteのインメモリデータベースを指定すると、テストのたびに専用のデータベースを使えます。

<phpunit>
    <php>
        <env name="DB_CONNECTION" value="sqlite" />
        <env name="DB_DATABASE" value=":memory:" />
    </php>
</phpunit>

実際の設定方法は、Laravelのバージョンやプロジェクトのphpunit.xmlに合わせます。テスト開始時にMigrationやテーブル作成を行う構成であっても、開発用のデータベースへ接続しないことを先に確認します。

Composer ScriptからPHPUnitを実行する

Workflowへ直接php artisan testを書く方法もありますが、プロジェクトのcomposer.jsonにテスト用Scriptが定義されているなら、それを使う方がローカルとCIの手順をそろえやすくなります。

例えば、composer testで次の処理を呼び出す構成にします。

{
    "scripts": {
        "test": [
            "@php artisan config:clear --ansi",
            "@php artisan test"
        ]
    }
}

このようにScriptへまとめておくと、ローカルではcomposer test、CIでもcomposer testを実行できます。CI専用のコマンドを別に増やす前に、既存のScriptで必要な確認ができるか調べます。

composer test

ただし、composer testという名前がすべてのプロジェクトに存在するわけではありません。composer.jsonscriptsを確認し、実際に定義されているコマンドを使います。

Node.jsとフロントエンドを検証する

LaravelのBladeへVue.jsを組み込んでいる場合や、Viteでアセットをビルドしている場合は、PHPのテストだけでなくJavaScript側の依存関係とビルドも確認します。

- name: Setup Node.js
  uses: actions/setup-node@v6
  with:
    node-version: 24
    cache: npm

- name: Install JavaScript dependencies
  run: npm ci

- name: Build frontend assets
  run: npm run build
npm cinpm installの違い

CIでは、package-lock.jsonなどのロックファイルをもとに依存関係をインストールするnpm ciを使う構成がよくあります。

npm ciはロックファイルがない場合や、package.jsonとロックファイルの内容が一致しない場合に失敗します。これはCIにとって不便な制約ではなく、依存関係が意図せず変わっていないことを検出するための条件として利用できます。

ローカルでnpm installを実行した後は、package.jsonとロックファイルの差分を確認し、意図した変更だけをコミットします。

npm run buildはScriptの内容を確認する

npm run buildが実行する内容は、package.jsonscripts.buildで決まります。Viteを使うプロジェクトであれば、ビルドによってJavaScriptやCSSの生成物を作成します。

{
    "scripts": {
        "build": "vite build"
    }
}

プロジェクトによっては、TypeScriptの型チェック、Lint、テストなどを別のScriptへ分けています。CIで何を検証したいかを整理し、存在しないコマンドを推測で追加しないようにします。

また、ビルドが成功したことと、画面が期待どおりに表示されることは別です。CIではコマンドの成功を確認し、画面の見た目や操作は必要に応じて人が確認します。

Laravel PintでPHPコードを検査する

PHPコードの書式をそろえる場合は、Laravel Pintを利用できます。Laravelの公式ドキュメントでは、PintはLaravelの書式に沿ってPHPコードを整えるツールで、新しいLaravelアプリケーションには自動的に含まれると説明されています。

ローカルで書式を修正する場合は、次のコマンドを実行します。

./vendor/bin/pint

CIでは、ファイルを書き換えずに書式違反を検査する--testを使います。

./vendor/bin/pint --test

--testで問題が見つかると、終了コードが成功にならないため、Workflowも失敗します。ローカルで./vendor/bin/pintを実行して差分を確認し、修正後にもう一度--testを実行します。

Pintは書式を整えるツールであり、アプリケーションの動作や要件を確認するテストの代わりにはなりません。Pint、PHPUnit、ビルドはそれぞれ確認する対象が違うため、どれか一つが成功しただけで問題がないとは判断しません。

なお、Pintは標準ではBladeテンプレートを整形しません。Bladeも対象にする場合の設定や、Node.jsなど追加の依存関係が必要になる場合があるため、Laravel Pintの公式ドキュメントで対象バージョンの仕様を確認してください。

検証コマンドの順番を考える

Workflowのステップは、基本的に上から順番に実行されます。私がよく使う構成では、環境準備の後にPHPテスト、JavaScript依存関係のインストール、フロントエンドのビルド、Pintの検査を行っています。

リポジトリを取得
  ↓
PHPとNode.jsを準備
  ↓
Composer依存関係をインストール
  ↓
テスト用環境を準備
  ↓
PHPテストを実行
  ↓
JavaScript依存関係をインストール
  ↓
フロントエンドをビルド
  ↓
PHPコードの書式を検査

この順番が唯一の正解というわけではありません。例えば、Pintを早く実行して書式の問題を先に検出する構成も考えられます。大切なのは、前のステップに必要な環境が用意されていることと、失敗時に確認範囲を絞りやすいことです。

コマンドを一つの長いrunへまとめると、どの処理で失敗したか分かりにくくなります。環境準備、PHPテスト、JavaScriptビルド、Lintのようにステップを分けると、Actions画面のログから原因を追いやすくなります。

CIが失敗したときの確認方法

Workflowが失敗したら、最初からすべてのコードを疑うのではなく、Actions画面で失敗したジョブとステップを確認します。GitHubのQuickstartでも、Workflowの実行結果からジョブを開き、各ステップのログを確認する流れが案内されています。

Workflowが実行されない
  • Workflowファイルが.github/workflowsにあるか
  • ファイルの拡張子が.ymlまたは.yaml
  • 変更したブランチがonの条件に一致しているか
  • リポジトリまたは組織でActionsが無効になっていないか
  • YAMLのインデントやキー名に誤りがないか
環境準備のステップで失敗する

PHPやNode.jsのセットアップで失敗する場合は、指定したバージョンが利用可能か、必要な拡張名が正しいか、RunnerのOSとコマンドが合っているかを確認します。

Composerやnpmのインストールで失敗する場合は、ロックファイルとマニフェストの不整合、依存パッケージのPHP・Node.js要件、ネットワークやレジストリのエラーを確認します。

テストで失敗する

テストで失敗した場合は、テストコードの失敗だけでなく、環境変数、PHP拡張、データベース接続、Migration、タイムゾーンなども確認します。

特に、ローカルでは接続できる開発用データベースへ、CIからも接続できると考えてはいけません。CIではCI専用のSQLiteやテスト用データベースを使用し、実データを扱う環境へ接続しない構成にします。

ビルドやPintで失敗する

ビルドの失敗では、Node.jsのバージョン、ロックファイル、環境変数、Viteの設定、読み込んでいるファイルの大文字・小文字を確認します。UbuntuのRunnerでは、Windowsのファイルシステムと大文字・小文字の扱いが異なる場合があります。

Pintの失敗では、まずローカルで./vendor/bin/pintを実行し、変更されたファイルを確認します。書式修正による差分を人が確認してからコミットし、CIでは--testで検査だけを行う流れが分かりやすいと思います。

Workflowを安全に運用する

秘密情報をWorkflowへ直書きしない

APIキー、パスワード、秘密鍵、本番環境の接続情報を、Workflowファイルや.env.exampleへ直接記述しないでください。テストで秘密情報が必要な場合は、GitHubのSecretsなどを使い、ログへ値を出力しないようにします。

ただし、Pull Requestの送信元がForkの場合など、イベントによってSecretsの扱いが変わります。外部から変更を受け取るWorkflowでは、Secretsを使う処理や書き込み権限を安易に組み合わせないようにします。

Actionの参照先とバージョンを確認する

usesで利用するActionは、誰が管理しているものか、どのリポジトリを参照しているかを確認します。GitHubのWorkflow syntax公式ドキュメントでは、ActionのバージョンとしてGitの参照名、タグ、コミットSHAなどを指定でき、コミットSHAによる固定が安全性の面で有効だと説明されています。

一方で、コミットSHAへ固定すると、更新や脆弱性修正を自動的には取り込めません。メジャーバージョンタグを使う場合も含め、Actionの更新を確認する担当やタイミングを決めておく必要があります。

検証用Workflowから書き込み処理を分ける

テスト、Lint、ビルドだけを行うWorkflowでは、リポジトリへ書き込む処理を追加しない方が安全です。書式を自動修正してコミットする処理や、デプロイを行う処理は、権限や実行対象を分けて設計します。

CIが成功したことをきっかけに本番へ反映する仕組みは便利ですが、対象ブランチ、承認、Secrets、環境保護、ロールバックまで考える必要があります。この記事ではそこまで扱わず、まずは検証結果を確認できる状態を作ります。

Actionの成功とアプリケーションの品質を混同しない

CIが成功するのは、Workflowに書かれた処理が成功したという意味です。テストが不足していれば、未検証の問題は残ります。画面の表示、仕様との一致、運用上の判断など、CIで確認していない内容は人が確認します。

まとめ

GitHub ActionsでCIを始めるときは、まず.github/workflowsへWorkflowを置き、PushやPull Requestをきっかけに検証処理を実行します。

PHP・Laravelプロジェクトでは、PHPと必要な拡張、Composer依存関係、テスト用環境を準備してから、composer testなどのPHPテストを実行します。フロントエンドを使っている場合は、Node.js、npm cinpm run buildも確認します。PHPコードの書式は、ローカルではPintで修正し、CIでは./vendor/bin/pint --testで検査する構成にできます。

Workflowを作るときは、プロジェクトのcomposer.jsonpackage.jsonphpunit.xmlを確認し、実際にローカルで実行できるコマンドを組み込みます。CI専用の別手順を増やしすぎず、環境準備と検証処理をステップへ分けておくと、失敗した場所を追いやすくなります。

最初からテスト、ビルド、デプロイのすべてを自動化する必要はありません。まずは、変更を反映したら同じ検証が繰り返し実行され、その結果をPull Requestで確認できるところまで作るのがよいかと思います。

参考資料

この記事を書いた人

※上が私です。

奈良市を拠点に、27年以上の経験を持つフリーランスWebエンジニア、阿部辰也です。

これまで、ECサイトのバックエンド開発や業務効率化システム、公共施設の予約システムなど、多彩なプロジェクトを手がけ、企業様や制作会社様のパートナーとして信頼を築いてまいりました。

【制作会社・企業様向けサポート】
  • 専任エンジニアのいない企業様に対するシステム面の不安を解消
  • 柔軟な契約形態や短納期での対応により、急なニーズにも迅速にサポート
  • システムの企画段階から運用まで、ワンストップでのサービスを提供

Webシステムの開発やサイト改善でお困りの際は、どうぞお気軽にご相談ください。小さな疑問から大規模プロジェクトまで、最適なご提案を心を込めてさせていただきます。

ぜひ、プロフィールWeb制作会社様向け業務案内一般企業様向け業務案内もご覧くださいね。

Laravel 13でArtisanコマンドを作成する:引数・オプション・dry-runの基本

2026.09.10

Laravel 13でArtisanコマンドを作成する方法を、引数・オプション・コンソール出力・終了コードの基本から解説します。日時指定した記事を定期的に公開するDB更新処理を例に、--dry-runで対象を確認してから安全に実行する方法や、スケジューラー・cronでの定期実行についても紹介します。

Laravel PHP

LaravelのFeature Testで開発用DBを初期化してしまった:Codex運用で学ぶテストDBの多層防御

2026.09.07

CodexにLaravelのFeature Test実行を依頼した際、テスト用DBではなくローカルの開発用DBへ接続した状態でRefreshDatabaseが実行され、データが失われる事故が発生しました。この記事では、PHPUnitのforce="true"による環境変数固定、DB_URLの無効化、config:clearのComposer Script化、Laravel起動後のDB接続先ガード、通常テストとIntegration Testの分離など、テストDBの誤接続を防ぐ多層防御を紹介します。

Codex Laravel PHP

Laravel 13で既存のテーブルを使ってログイン機能を実装する

2026.09.02

Laravel 13の既存連載で作成したmembersテーブルを認証用ユーザーとして流用し、Memberモデルと認証プロバイダーを設定します。SQLiteに保存したユーザー情報を使ったメールアドレス・パスワード認証、ログイン済み画面の表示、データベースセッション、ログアウト時のセッション破棄までを実装します。

Laravel PHP

Laravel 13+Vue.jsで画像・CSVを非同期アップロードする:FormData・ファイル検証・非公開保存

2026.08.31

Laravel 13のBladeに組み込んだVue.jsから、FormDataを使って画像またはCSVを非同期で送信する方法を解説します。JPEG・PNGの画像検証、CSVの文字コード変換と簡易的な内容確認、20MBのサイズ制限、非公開ディスクへの保存、CSRF・バリデーションエラー処理を実装し、fetchを基本にaxiosとの違いも整理します。

JavaScript Laravel PHP Vue.js

阿部辰也へのお仕事の依頼・お問い合わせ

軽いご相談もお気軽にどうぞ!

個人情報の取り扱いについて *必須 プライバシーポリシーをご確認いただき、同意いただける場合は「同意する」にチェックをしてください。

keyboard_double_arrow_up
TOP