logo Fonnpo

お問い合わせ
2026年5月31日 4 分で読める Fonnpo

GitHub Actions 入門から実践まで

コア概念、イベント、キャッシュ高速化 — Nuxt プロジェクトを例に自動デプロイを実現。

#DevOps#CI/CD#GitHub

GitHub Actions は GitHub に組み込まれた CI/CD プラットフォームで、リポジトリの中に直接自動化フローを定義できます。Jenkins や CircleCI などの外部ツールは不要です。本記事では本サイト(Nuxt 4 + pnpm プロジェクト)を例に、概念から実践まで、プッシュ後に自動ビルド・デプロイを実現する手順を解説します。

基本概念

設定を書く前に、まず用語を整理しておきましょう。

用語説明
Workflow自動化フロー全体。.github/workflows/ 以下に YAML ファイルとして保存される
EventWorkflow を起動するトリガー。pushpull_requestschedule・手動実行など
JobWorkflow 内の実行単位。各 Job は独立した VM で動き、デフォルトは並列実行
StepJob 内の単一操作。順番に実行され、シェルスクリプトまたは Action を指定できる
ActionGitHub Marketplace や自作の再利用可能モジュール。uses で参照する
RunnerJob を実行する VM。GitHub が提供する hosted runner と自前の self-hosted がある
Secret暗号化された環境変数。リポジトリの Settings で設定し、${{ secrets.MY_KEY }} で参照する

Workflow ファイルの構造

すべての Workflow ファイルはリポジトリルートの .github/workflows/ 以下に置きます。ファイル名は自由で、拡張子は .yml でも .yaml でも構いません。

最もシンプルな例:

.github/workflows/deploy.yml
        name: Deploy        # Actions ページに表示される名前

on:                 # トリガー条件
  push:
    branches:
      - main

jobs:               # 1 つ以上の Job
  build:            # Job ID — 自由に命名
    runs-on: ubuntu-latest  # 使用する Runner
    steps:          # この Job のステップ
      - name: コードをチェックアウト
        uses: actions/checkout@v4

      - name: Node バージョンを表示
        run: node -v

    

トリガーイベント詳解

Yaml
        on:
  # 指定ブランチへのプッシュ時
  push:
    branches:
      - main
      - 'release/**'   # glob パターン対応
    paths:
      - 'app/**'       # このパスが変更されたときのみ(省略可)

  # PR の操作時
  pull_request:
    branches:
      - main
    types: [opened, synchronize, reopened]

  # スケジュール実行(cron 構文、UTC タイムゾーン)
  schedule:
    - cron: '0 2 * * *'  # 毎日 UTC 02:00

  # 手動実行(Actions ページにボタンが表示される)
  workflow_dispatch:
    inputs:
      environment:
        description: 'デプロイ環境'
        required: true
        default: 'production'
        type: choice
        options:
          - production
          - staging

    

実践:本サイトの CI/CD 構成

本サイトは Nuxt 4 プロジェクトで、pnpm で依存関係を管理し、PM2 でサーバー上で稼働しています。目標は以下の 2 つです。

  1. CI(コードチェック):PR またはプッシュのたびに ESLint を自動実行
  2. CD(自動デプロイ)main ブランチへのプッシュ時に Runner 上でビルドし、成果物を圧縮してSCP でサーバーに転送、SSH で展開して PM2 を再起動

ステップ 1:Secrets の準備

リポジトリの Settings → Secrets and variables → Actions に以下の変数を追加します。

Secret 名内容
SSH_HOSTサーバーの IP またはホスト名(例:123.45.67.89
SSH_PORTSSH ポート。デフォルトは 22
SSH_USERログインユーザー名(例:root または deploy
SSH_KEY秘密鍵の内容(cat ~/.ssh/id_ed25519 の出力)
SSH_PATHサーバー上のデプロイディレクトリ(例:/www/wwwroot/fonnpo-web

秘密鍵の設定~/.ssh/id_ed25519 の内容(-----BEGIN...-----END... 行を含む全文)を SSH_KEY に貼り付けます。対応する公開鍵 (~/.ssh/id_ed25519.pub) はサーバーの ~/.ssh/authorized_keys に追記してください。

ステップ 2:CI Workflow(コードチェック)

.github/workflows/ci.yml
        name: CI

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

jobs:
  lint:
    name: ESLint チェック
    runs-on: ubuntu-latest

    steps:
      - name: コードをチェックアウト
        uses: actions/checkout@v4

      - name: pnpm をセットアップ
        uses: pnpm/action-setup@v4
        with:
          version: 10

      - name: Node.js をセットアップ
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm          # pnpm store をキャッシュしてインストールを高速化

      - name: 依存関係のインストール
        run: pnpm install --frozen-lockfile

      - name: ESLint を実行
        run: pnpm eslint .

    
折りたたむ

--frozen-lockfile により、CI 環境は常に pnpm-lock.yaml の通りにインストールされ、バージョンが勝手に上がることがありません。

ステップ 3:CD Workflow(自動デプロイ)

本サイトはRunner 上でビルドして成果物をサーバーに転送する方針を採用しています。サーバー上で git pull + build する方式と比べ、サーバーに Node.js ビルド環境が不要、ビルド失敗が本番に影響しない、サーバー負荷が低いといったメリットがあります。

.github/workflows/deploy.yml
        name: Deploy to Self-hosted Server

on:
  push:
    branches: [main]
  workflow_dispatch:     # Actions ページからの手動実行を有効化

jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true  # Actions ランタイムを Node 24 に強制
      NODE_OPTIONS: --max-old-space-size=4096   # 大規模ビルドの OOM を防止

    steps:
      - name: コードをチェックアウト
        uses: actions/checkout@v4

      - name: pnpm をセットアップ
        uses: pnpm/action-setup@v4

      - name: Node.js をセットアップ
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm

      - name: 依存関係のインストール
        run: pnpm install --frozen-lockfile

      - name: ビルド
        run: pnpm build

      - name: 成果物を圧縮
        run: |
          tar -czf release.tgz .output ecosystem.config.cjs package.json pnpm-lock.yaml

      - name: サーバーにアップロード(SCP)
        uses: appleboy/scp-action@v0.1.7
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_KEY }}
          port: ${{ secrets.SSH_PORT }}
          source: release.tgz
          target: ${{ secrets.SSH_PATH }}

      - name: サーバーで展開・再起動
        uses: appleboy/ssh-action@v1.2.0
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_KEY }}
          port: ${{ secrets.SSH_PORT }}
          script: |
            set -e
            cd ${{ secrets.SSH_PATH }}
            tar -xzf release.tgz
            rm -f release.tgz

            corepack enable
            pnpm install --prod --frozen-lockfile

            pm2 startOrReload ecosystem.config.cjs --update-env
            pm2 save

    
折りたたむ

ポイント:

  • env ブロックFORCE_JAVASCRIPT_ACTIONS_TO_NODE24 で Actions 内部ランタイムを Node 24 に統一。NODE_OPTIONS でヒープ上限を拡大し、Nuxt の大規模ビルドでの OOM を防止
  • tar 圧縮.output(Nuxt ビルド成果物)、ecosystem.config.cjspackage.jsonpnpm-lock.yaml のみを含める。ソースコードは転送しない
  • appleboy/scp-action:SCP プロトコルでアーカイブをサーバーの SSH_PATH に転送
  • pnpm install --prod:本番依存関係のみインストール。ビルドツールは不要でサイズも抑えられる
  • pm2 startOrReloadpm2 reload より堅牢。プロセスが存在しなければ起動、存在すればダウンタイムなしでリロード。pm2 save でプロセス一覧を永続化し、サーバー再起動後も自動復元できる

ステップ 4:1 ファイルにまとめる(CI → CD)

CI が通った場合のみデプロイしたい場合は、needs で Job を直列化します。

.github/workflows/ci-cd.yml
        name: CI / CD

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

jobs:
  lint:
    name: ESLint チェック
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
        with:
          version: 10
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm eslint .

  deploy:
    name: 本番サーバーへデプロイ
    runs-on: ubuntu-latest
    needs: lint                              # ← lint が成功するまで待機
    if: github.ref == 'refs/heads/main'     # ← PR はデプロイしない。main へのプッシュのみ
    env:
      FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
      NODE_OPTIONS: --max-old-space-size=4096

    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm build

      - name: 成果物を圧縮
        run: tar -czf release.tgz .output ecosystem.config.cjs package.json pnpm-lock.yaml

      - name: サーバーにアップロード(SCP)
        uses: appleboy/scp-action@v0.1.7
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_KEY }}
          port: ${{ secrets.SSH_PORT }}
          source: release.tgz
          target: ${{ secrets.SSH_PATH }}

      - name: サーバーで展開・再起動
        uses: appleboy/ssh-action@v1.2.0
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_KEY }}
          port: ${{ secrets.SSH_PORT }}
          script: |
            set -e
            cd ${{ secrets.SSH_PATH }}
            tar -xzf release.tgz && rm -f release.tgz
            corepack enable
            pnpm install --prod --frozen-lockfile
            pm2 startOrReload ecosystem.config.cjs --update-env
            pm2 save

    
折りたたむ

PR では lint のみ実行されます。main へのマージ後は lintdeploy の順に実行され、両方が成功して初めて完了となります。

Tips

依存関係のキャッシュでビルドを高速化

actions/setup-nodecache: 'pnpm' を使うと pnpm store が自動キャッシュされ、pnpm install が大幅に短縮されます。

Yaml
        - uses: actions/setup-node@v4
  with:
    node-version: 22
    cache: pnpm       # pnpm-lock.yaml を元にキャッシュキーを自動生成

    

環境変数と Secrets の違い

Yaml
        env:
  NODE_ENV: production       # 平文 — ログに表示される

steps:
  - run: echo ${{ secrets.TOKEN }}  # Secret — ログでは *** にマスクされる

    

機密情報(パスワード、トークン、秘密鍵)は必ず Secrets を使ってください。

手動デプロイのトリガー

コードをプッシュせずに再デプロイしたい場合(サーバー再起動後など)は workflow_dispatch を追加します。

Yaml
        on:
  workflow_dispatch:    # Actions ページに "Run workflow" ボタンが表示される

  push:
    branches: [main]

    

Job 間のデータ共有

Job 間は分離されています。outputs を使ってデータを受け渡します。

Yaml
        jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.get-version.outputs.version }}
    steps:
      - id: get-version
        run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - run: echo "バージョン ${{ needs.build.outputs.version }} をデプロイ"

    

特定パスの変更時のみトリガー

フロントエンドとバックエンドが同一リポジトリにある場合、関連ファイルが変更されたときのみフローを起動できます。

Yaml
        on:
  push:
    paths:
      - 'app/**'
      - nuxt.config.ts
      - package.json
      - pnpm-lock.yaml
    branches: [main]

    

実行結果の確認

プッシュ後は GitHub リポジトリの Actions タブですべての実行履歴を確認できます。クリックすると各ステップのログが展開され、失敗時は赤くハイライトされるので原因を素早く特定できます。

設定後の完全な Workflow のイメージ:

Text
        Actions
└── Deploy to Self-hosted Server
    ├── 🟢 lint (20s)
    │   ├── コードをチェックアウト
    │   ├── pnpm / Node.js をセットアップ
    │   ├── pnpm install
    │   └── ESLint チェック
    └── 🟢 deploy (3min)
        ├── コードをチェックアウト
        ├── pnpm / Node.js をセットアップ
        ├── pnpm install
        ├── pnpm build
        ├── tar で圧縮
        ├── SCP でサーバーにアップロード
        └── SSH で展開・PM2 再起動
            ├── tar -xzf release.tgz
            ├── pnpm install --prod
            ├── pm2 startOrReload
            └── pm2 save

    

一度設定してしまえば、あとは git push だけで全自動。手動 SSH でのデプロイ作業が不要になります。

Fonnpo