サイトアイコンmaita tomoya dev io

GitHub Actions

DevOps/インフラ

GitHub Actions

CI/CDとは

ソフトウェア開発において、コードを書いてから本番環境にリリースするまでには多くの工程がある。CI/CDはこれらの工程を自動化する仕組みの総称だ。

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

開発者がコードを共有リポジトリにマージするたびに、自動でビルドとテストを実行する手法。「インテグレーション」とは「統合」を意味し、複数の開発者のコードを頻繁に統合することで、問題を早期に発見する。

CD(継続的デリバリー/デプロイ: Continuous Delivery / Continuous Deployment)

CIの次のステップとして、テストを通過したコードを自動でステージング環境や本番環境に配信する手法。

用語意味自動化の範囲
CI(継続的インテグレーション)コードの統合・ビルド・テストを自動化コード変更 → ビルド → テスト
CD(継続的デリバリー)リリース準備までを自動化(本番反映は手動承認)テスト → ステージング → 承認待ち
CD(継続的デプロイ)本番環境への反映まで全て自動化テスト → ステージング → 本番反映

身近な例で理解すると、CI/CDは「工場の品質管理ライン」のようなもの。製品(コード)が作られるたびに、自動で検査(テスト)が行われ、合格品だけが出荷(デプロイ)される仕組みだ。

なぜCI/CDが必要か

手動デプロイの問題点

CI/CDを導入していないプロジェクトでは、以下のような問題が頻繁に発生する。

問題具体的な状況影響
ヒューマンエラー手動でコマンドを打ち間違える本番環境の障害
属人化特定の人しかデプロイできないその人が休むとリリースできない
時間の浪費毎回同じ手順を手動で実行開発に使える時間が減る
テスト漏れテストを実行し忘れるバグが本番に混入
環境差異「自分の環境では動いた」本番で動かない
リリース頻度の低下デプロイが面倒で頻度が下がるフィードバックサイクルが遅くなる

自動化のメリット

  • コードをpushするだけで自動的にテストが走る
  • テストが通ったコードだけがデプロイされる
  • 誰がやっても同じ手順でデプロイされる
  • リリース頻度を上げられる(1日に何度もリリース可能)
  • 問題があった場合、どのコミットで壊れたか特定しやすい

GitHub Actionsとは

GitHub Actionsは、GitHubに組み込まれたCI/CDサービス。リポジトリ上でコードの変更が発生したとき(pushやPR作成など)、自動的にビルド、テスト、デプロイなどの処理を実行できる。

他のCI/CDサービス(Jenkins、CircleCI、Travis CIなど)と違い、GitHubと完全に統合されているため、別途サービスの設定やアカウント作成が不要。

CI/CDサービス特徴GitHubとの統合料金(個人)
GitHub ActionsGitHub組み込み、設定が簡単完全統合無料枠が充実
Jenkinsオープンソース、高い拡張性プラグインが必要無料(サーバー費用は別)
CircleCI高速、Docker対応が強力連携が必要無料枠あり
Travis CIオープンソースに強い連携が必要有料化が進んでいる
GitLab CI/CDGitLabに統合GitLabリポジトリ向け無料枠あり

基本概念

GitHub Actionsを理解するには、以下の6つの概念を把握する必要がある。

概念の関係図

[Event] → トリガー
    ↓
[Workflow] (.github/workflows/*.yml)
    ↓
[Job 1]  ←→  [Job 2]  (並列実行 or 直列実行)
  ↓               ↓
[Step 1]        [Step 1]
[Step 2]        [Step 2]
  ↓               ↓
[Action]        [Action]   ←  再利用可能な処理単位
    ↓
[Runner] (実行環境: ubuntu-latest等)

各概念の詳細

概念説明例え
Workflow自動化プロセス全体の定義。YAMLファイルで記述。レシピ全体
EventWorkflowを起動するきっかけ。「注文が入ったら」というトリガー
JobWorkflow内の実行単位。1つ以上のStepで構成。レシピの「下ごしらえ」「調理」「盛り付け」
StepJob内の個別の処理。コマンドまたはActionを実行。「野菜を切る」「炒める」などの手順
Action再利用可能な処理の部品。Marketplaceで共有。「皮むき器」「フードプロセッサー」などの道具
RunnerWorkflowを実行するサーバー環境。調理場(キッチン)

ワークフローファイルの構造

ワークフローはリポジトリの .github/workflows/ ディレクトリにYAMLファイルとして配置する。

最小構成のワークフロー

# .github/workflows/hello.yml
name: Hello World # ワークフロー名(GitHub UIに表示される)

on: push # トリガー: pushされたら実行

jobs:
  greet: # ジョブID(任意の名前)
    runs-on: ubuntu-latest # 実行環境
    steps:
      - name: Say Hello # ステップ名
        run: echo "Hello, World!" # 実行するコマンド

詳細な構文解説

# ワークフロー名
name: CI Pipeline

# トリガー設定
on:
  push:
    branches: [main, develop] # mainとdevelopへのpush時に実行
    paths:
      - 'src/**' # srcディレクトリ内の変更時のみ
      - '!src/**/*.md' # ただしmdファイルは除外
  pull_request:
    branches: [main]
    types: [opened, synchronize, reopened]

# 環境変数(ワークフロー全体で使用可能)
env:
  NODE_VERSION: '20'
  CI: true

# ジョブ定義
jobs:
  # 1つ目のジョブ: テスト
  test:
    name: Run Tests # UIに表示されるジョブ名
    runs-on: ubuntu-latest
    timeout-minutes: 10 # タイムアウト設定

    steps:
      # リポジトリのコードをチェックアウト
      - name: Checkout code
        uses: actions/checkout@v4 # 公式Actionを使用

      # Node.jsのセットアップ
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm' # npm依存関係のキャッシュ

      # 依存関係のインストール
      - name: Install dependencies
        run: npm ci # ci はクリーンインストール

      # テスト実行
      - name: Run tests
        run: npm test

  # 2つ目のジョブ: ビルド
  build:
    name: Build
    runs-on: ubuntu-latest
    needs: test # testジョブの成功後に実行
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
      - run: npm ci
      - run: npm run build

各セクションの役割をまとめると以下の通り。

セクション役割必須
nameワークフロー名。GitHub UIに表示。いいえ
onトリガーイベントの設定。はい
env環境変数の定義。いいえ
jobs実行するジョブの定義。はい
runs-on実行環境(ランナー)の指定。はい
stepsジョブ内の処理手順。はい
uses外部のActionを利用。いいえ
runシェルコマンドを直接実行。いいえ
withActionに渡すパラメータ。いいえ
needs依存するジョブの指定。いいえ

トリガーイベント

Workflowをいつ実行するかを決める設定。よく使うものを紹介する。

push / pull_request

最も基本的なトリガー。

on:
  push:
    branches:
      - main
      - 'release/**' # release/で始まるブランチ全て
    tags:
      - 'v*' # vで始まるタグ(v1.0.0など)
    paths:
      - 'src/**'
      - 'package.json'
    paths-ignore:
      - '**.md'
      - 'docs/**'

  pull_request:
    branches: [main]
    types: [opened, synchronize, reopened, ready_for_review]

schedule(定期実行)

cron式でスケジュール実行。UTCで指定する点に注意。

on:
  schedule:
    # 毎日午前9時(JST)= UTC 0:00 に実行
    - cron: '0 0 * * *'
    # 毎週月曜日の午前9時(JST)に実行
    - cron: '0 0 * * 1'

cron式の読み方:

┌───────────── 分 (0 - 59)
│ ┌───────────── 時 (0 - 23)
│ │ ┌───────────── 日 (1 - 31)
│ │ │ ┌───────────── 月 (1 - 12)
│ │ │ │ ┌───────────── 曜日 (0 - 6、0=日曜)
│ │ │ │ │
* * * * *

workflow_dispatch(手動実行)

GitHub UIから手動でワークフローを実行できる。入力パラメータも設定可能。

on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'デプロイ先環境'
        required: true
        default: 'staging'
        type: choice
        options:
          - staging
          - production
      dry_run:
        description: 'ドライラン(実際にはデプロイしない)'
        required: false
        type: boolean
        default: false

release(リリース時)

on:
  release:
    types: [published] # リリースが公開されたとき

トリガーイベント一覧

イベント発火タイミング主な用途
pushブランチへのpushCI(テスト、ビルド)
pull_requestPRの作成・更新PRのチェック
schedulecron式で定期実行定期メンテナンス
workflow_dispatch手動実行オンデマンドデプロイ
releaseリリースの作成リリースパイプライン
issuesIssueの作成・変更Issue自動処理
workflow_call他のワークフローから呼び出し再利用可能なワークフロー

ランナー(Runner)

ランナーはワークフローを実行するサーバー環境のこと。

ランナーOS用途
ubuntu-latestUbuntu Linux最も一般的。ほとんどの用途に対応。
ubuntu-22.04Ubuntu 22.04特定バージョンを指定したい場合。
windows-latestWindows ServerWindows固有のビルド・テスト。
macos-latestmacOSiOS/macOSアプリのビルド。
self-hosted自前サーバー特殊な要件がある場合。

料金面で注意が必要。Linux以外は実行時間の消費が大きい。

ランナー1分あたりの消費(無料枠)
Linux1分
Windows2分
macOS10分

よく使うアクション

GitHub Actionsには公式やコミュニティが提供する再利用可能なアクションが数多く存在する。

actions/checkout

リポジトリのコードを取得する。ほぼ全てのワークフローで使用する。

- name: Checkout
  uses: actions/checkout@v4
  with:
    fetch-depth: 0 # 全履歴を取得(デフォルトは1=最新コミットのみ)
    ref: develop # 特定のブランチを指定

actions/setup-node

Node.jsの環境をセットアップする。

- name: Setup Node.js
  uses: actions/setup-node@v4
  with:
    node-version: '20'
    # node-version-file: '.node-version'  # ファイルからバージョンを読み取り
    cache: 'npm' # 依存関係のキャッシュ有効化
    registry-url: 'https://npm.pkg.github.com' # npm公開時

actions/cache

ビルド時間を短縮するためのキャッシュ機能。

- name: Cache node_modules
  uses: actions/cache@v4
  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-

actions/upload-artifact / actions/download-artifact

ジョブ間でファイルを受け渡すために使用する。

# ビルド成果物のアップロード
- name: Upload build artifacts
  uses: actions/upload-artifact@v4
  with:
    name: build-output
    path: dist/
    retention-days: 7 # 保持日数

# 別のジョブでダウンロード
- name: Download build artifacts
  uses: actions/download-artifact@v4
  with:
    name: build-output
    path: dist/

よく使うアクション一覧

アクション用途
actions/checkout@v4リポジトリのチェックアウト
actions/setup-node@v4Node.js環境のセットアップ
actions/setup-python@v5Python環境のセットアップ
actions/cache@v4依存関係のキャッシュ
actions/upload-artifact@v4ビルド成果物のアップロード
actions/download-artifact@v4ビルド成果物のダウンロード
actions/github-script@v7JavaScript でGitHub APIを操作
peter-evans/create-pull-request@v6PR自動作成
slackapi/slack-github-action@v1Slack通知

環境変数とシークレット

環境変数

# ワークフローレベル
env:
  NODE_ENV: production

jobs:
  build:
    runs-on: ubuntu-latest
    # ジョブレベル
    env:
      DATABASE_URL: localhost:5432
    steps:
      - name: Build
        # ステップレベル
        env:
          API_KEY: ${{ secrets.API_KEY }}
        run: npm run build

デフォルト環境変数

GitHub Actionsが自動で設定する環境変数がある。

変数名内容
GITHUB_SHAトリガーしたコミットのSHA
GITHUB_REFトリガーしたブランチやタグの参照
GITHUB_REPOSITORYリポジトリ名(owner/repo)
GITHUB_ACTORワークフローをトリガーしたユーザー
GITHUB_RUN_IDワークフロー実行のユニークID
GITHUB_RUN_NUMBERワークフロー実行の番号(インクリメント)
RUNNER_OSランナーのOS名

シークレット(Secrets)

APIキーやパスワードなどの機密情報は、リポジトリの Settings → Secrets and variables → Actions で登録する。

steps:
  - name: Deploy
    env:
      AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
      AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    run: aws s3 sync dist/ s3://my-bucket

  - name: Use GITHUB_TOKEN
    env:
      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # 自動生成される
    run: gh pr comment --body "Build succeeded!"

GITHUB_TOKENはGitHub Actionsが自動で生成するトークン。リポジトリに対する操作(PR作成、コメント投稿など)に使用できる。

シークレットの注意点

  • ログに出力されると自動でマスク(***)される
  • フォークからのPRではシークレットにアクセスできない(セキュリティ対策)
  • Organization全体で共有するシークレットも設定可能
  • Environments(環境)ごとに異なるシークレットを設定可能

マトリックスビルド

複数のバージョンや環境の組み合わせでテストを実行する仕組み。

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node-version: [18, 20, 22]
        # 3 OS x 3 バージョン = 9パターンが並列実行される
      fail-fast: false # 1つ失敗しても残りは継続

    steps:
      - uses: actions/checkout@v4
      - name: Setup Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm ci
      - run: npm test

マトリックスの除外と追加

strategy:
  matrix:
    os: [ubuntu-latest, windows-latest]
    node-version: [18, 20]
    exclude:
      # Windows + Node 18 の組み合わせを除外
      - os: windows-latest
        node-version: 18
    include:
      # 追加の組み合わせ
      - os: ubuntu-latest
        node-version: 22
        experimental: true # カスタム変数も設定可能

キャッシュ

依存関係のキャッシュによりビルド時間を大幅に短縮できる。

npmのキャッシュ

- name: Cache npm dependencies
  uses: actions/cache@v4
  id: npm-cache
  with:
    path: ~/.npm
    key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-npm-

- name: Install dependencies
  if: steps.npm-cache.outputs.cache-hit != 'true'
  run: npm ci

setup-nodeの組み込みキャッシュ

actions/setup-nodeにはキャッシュ機能が組み込まれている。上記のように手動でキャッシュを設定する代わりに、以下のように1行で設定できる。

- uses: actions/setup-node@v4
  with:
    node-version: '20'
    cache: 'npm' # これだけでnpm依存関係がキャッシュされる

キャッシュの仕組み

要素説明
pathキャッシュするディレクトリ
keyキャッシュのキー。一致するキャッシュがあれば復元。
restore-keyskeyに完全一致しない場合、前方一致でフォールバック。
cache-hitキャッシュが見つかったかどうかの出力。

キャッシュなしの場合、npm ciに毎回30秒以上かかることがある。キャッシュを使うと数秒で完了する。

条件分岐

if文を使って、条件に応じてステップやジョブの実行を制御できる。

jobs:
  deploy:
    runs-on: ubuntu-latest
    # mainブランチへのpush時のみ実行
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    steps:
      - uses: actions/checkout@v4

      - name: Deploy to production
        run: npm run deploy

      # 前のステップが失敗しても実行
      - name: Notify on failure
        if: failure()
        run: echo "Deployment failed!"

      # 常に実行(成功・失敗に関わらず)
      - name: Cleanup
        if: always()
        run: npm run cleanup

      # 特定の条件
      - name: Only on tag push
        if: startsWith(github.ref, 'refs/tags/')
        run: echo "This is a tag push"

よく使う条件式

条件式意味
success()前のステップが成功した場合(デフォルト)
failure()前のステップが失敗した場合
always()常に実行
cancelled()ワークフローがキャンセルされた場合
github.ref == 'refs/heads/main'mainブランチの場合
github.event_name == 'push'pushイベントの場合
contains(github.event.head_commit.message, '[skip ci]')コミットメッセージに[skip ci]が含まれる場合

依存ジョブ(needs)

ジョブ間の実行順序を制御する。デフォルトではジョブは並列実行される。

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run lint

  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm test

  # lint と test の両方が成功した後に実行
  build:
    runs-on: ubuntu-latest
    needs: [lint, test] # 並列に実行されるlintとtestの後に実行
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run build

  # buildが成功した後に実行
  deploy:
    runs-on: ubuntu-latest
    needs: build
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      - run: npm run deploy

上記の実行フローを図で表すと:

lint ─────┐
          ├──→ build ──→ deploy
test ─────┘
(並列実行)    (直列実行)

自作アクション(Composite Action)

よく使う処理をまとめて再利用可能なアクションとして定義できる。

ディレクトリ構成

.github/
  actions/
    setup-project/
      action.yml
  workflows/
    ci.yml

Composite Actionの定義

# .github/actions/setup-project/action.yml
name: 'Setup Project'
description: 'プロジェクトのセットアップ(Node.js + 依存関係インストール)'

inputs:
  node-version:
    description: 'Node.jsのバージョン'
    required: false
    default: '20'

runs:
  using: 'composite'
  steps:
    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: ${{ inputs.node-version }}
        cache: 'npm'

    - name: Install dependencies
      shell: bash
      run: npm ci

    - name: Show versions
      shell: bash
      run: |
        echo "Node: $(node --version)"
        echo "npm: $(npm --version)"

Composite Actionの使用

# .github/workflows/ci.yml
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      # 自作アクションを使用
      - name: Setup
        uses: ./.github/actions/setup-project
        with:
          node-version: '20'

      - run: npm test

Composite Actionを使うメリットは、複数のワークフローで共通のセットアップ処理を使い回せること。修正が1箇所で済むようになる。

実践例1: Node.jsプロジェクトのCI

実際のプロジェクトで使えるCIパイプラインの完全なコード。

# .github/workflows/ci.yml
name: CI

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

env:
  NODE_VERSION: '20'

# 同じブランチで新しいpushがあったら、前の実行をキャンセル
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  # ===== リント =====
  lint:
    name: Lint
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - run: npm ci

      - name: Run ESLint
        run: npm run lint

      - name: Check formatting (Prettier)
        run: npx prettier --check .

      - name: Type check
        run: npx tsc --noEmit

  # ===== テスト =====
  test:
    name: Test
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - run: npm ci

      - name: Run unit tests
        run: npm test -- --coverage

      - name: Upload coverage report
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report
          path: coverage/
          retention-days: 7

  # ===== ビルド =====
  build:
    name: Build
    runs-on: ubuntu-latest
    needs: [lint, test]
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - run: npm ci

      - name: Build
        run: npm run build

      - name: Upload build artifacts
        uses: actions/upload-artifact@v4
        with:
          name: build-output
          path: dist/
          retention-days: 3

実践例2: 自動デプロイ(Vercelへのデプロイ)

# .github/workflows/deploy.yml
name: Deploy to Vercel

on:
  push:
    branches: [main]

env:
  VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
  VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

jobs:
  deploy:
    name: Deploy Production
    runs-on: ubuntu-latest
    timeout-minutes: 15

    # デプロイ環境の定義(GitHub UIでデプロイ履歴を確認可能)
    environment:
      name: production
      url: ${{ steps.deploy.outputs.url }}

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - run: npm ci

      - name: Install Vercel CLI
        run: npm install -g vercel

      - name: Pull Vercel Environment Information
        run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}

      - name: Build Project
        run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}

      - name: Deploy to Vercel
        id: deploy
        run: |
          url=$(vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }})
          echo "url=$url" >> $GITHUB_OUTPUT

      - name: Comment deploy URL on commit
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.repos.createCommitComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              commit_sha: context.sha,
              body: `Deployed to production: ${{ steps.deploy.outputs.url }}`
            })

AWSへのデプロイ(S3 + CloudFront)

# .github/workflows/deploy-aws.yml
name: Deploy to AWS

on:
  push:
    branches: [main]

permissions:
  id-token: write # OIDC認証に必要
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - run: npm ci
      - run: npm run build

      # OIDC認証でAWSにアクセス(シークレットキー不要)
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-actions-role # 自分のAWSアカウントIDに置き換える
          aws-region: ap-northeast-1

      - name: Deploy to S3
        run: aws s3 sync dist/ s3://my-bucket --delete

      - name: Invalidate CloudFront cache
        run: |
          aws cloudfront create-invalidation \
            --distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} \
            --paths "/*"

実践例3: PRへの自動コメント

PRに自動でコメントを追加するワークフロー。コードレビューの補助やビルド結果の報告に使える。

# .github/workflows/pr-comment.yml
name: PR Comment

on:
  pull_request:
    types: [opened, synchronize]

permissions:
  pull-requests: write

jobs:
  comment:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - run: npm ci

      # バンドルサイズを計測
      - name: Build and measure bundle size
        id: bundle
        run: |
          npm run build
          size=$(du -sh dist/ | cut -f1)
          echo "size=$size" >> $GITHUB_OUTPUT

      # テスト実行とカバレッジ取得
      - name: Run tests with coverage
        id: test
        run: |
          npm test -- --coverage --coverageReporters=text-summary 2>&1 | tee test-output.txt
          coverage=$(grep "Statements" test-output.txt | awk '{print $3}')
          echo "coverage=$coverage" >> $GITHUB_OUTPUT

      # PRにコメント
      - name: Comment on PR
        uses: actions/github-script@v7
        with:
          script: |
            const body = `## Build Report

            | Item | Value |
            | --- | --- |
            | Bundle Size | ${{ steps.bundle.outputs.size }} |
            | Test Coverage | ${{ steps.test.outputs.coverage }} |
            | Node.js | 20 |
            | Status | Passed |

            *Automatically generated by GitHub Actions*`;

            // 既存のコメントを探す
            const { data: comments } = await github.rest.issues.listComments({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
            });

            const botComment = comments.find(c =>
              c.user.type === 'Bot' && c.body.includes('## Build Report')
            );

            if (botComment) {
              // 既存コメントを更新
              await github.rest.issues.updateComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                comment_id: botComment.id,
                body: body,
              });
            } else {
              // 新規コメントを作成
              await github.rest.issues.createComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.issue.number,
                body: body,
              });
            }

セキュリティベストプラクティス

GitHub Actionsを安全に運用するための重要なポイント。

1. サードパーティアクションのバージョン固定

# 悪い例: タグの指定(書き換えられる可能性がある)
- uses: actions/checkout@v4

# 良い例: コミットSHAで固定(改ざんを防止)
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

Dependabotを設定すると、アクションの更新を自動でPRとして提案してくれる。

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: 'github-actions'
    directory: '/'
    schedule:
      interval: 'weekly'

2. 最小権限の原則

# ワークフローレベルで権限を最小化
permissions:
  contents: read # リポジトリの読み取りのみ

jobs:
  deploy:
    permissions:
      contents: read
      deployments: write # デプロイに必要な権限のみ追加

3. シークレットの安全な取り扱い

# 悪い例: シークレットを直接コマンドに渡す(ログに残る可能性)
- run: curl -H "Authorization: Bearer ${{ secrets.API_TOKEN }}" https://api.example.com

# 良い例: 環境変数経由で渡す
- name: Call API
  env:
    API_TOKEN: ${{ secrets.API_TOKEN }}
  run: curl -H "Authorization: Bearer $API_TOKEN" https://api.example.com

4. フォークからのPRの注意

フォークされたリポジトリからのPRは、シークレットにアクセスできない。これはセキュリティ上の重要な仕様。悪意のあるコードがシークレットを盗むことを防いでいる。

料金体系

無料枠(2025年時点)

プラン無料の実行時間(月あたり)ストレージ
Free2,000分500MB
Team3,000分2GB
Enterprise50,000分50GB

注意点

  • パブリックリポジトリは無料(無制限)
  • プライベートリポジトリのみ無料枠が適用される
  • macOSランナーは1分あたり10分として計算される(高コスト)
  • 不要な実行を防ぐために、pathsフィルタやconcurrencyを活用する

デバッグ方法

ランナー診断ログの有効化

リポジトリの Settings → Secrets and variables → Actions で以下のシークレットを追加する。

シークレット名効果
ACTIONS_RUNNER_DEBUGtrueランナーの詳細ログを有効化
ACTIONS_STEP_DEBUGtrueステップの詳細ログを有効化

ワークフロー内でのデバッグ

steps:
  - name: Debug information
    run: |
      echo "Event name: ${{ github.event_name }}"
      echo "Ref: ${{ github.ref }}"
      echo "SHA: ${{ github.sha }}"
      echo "Actor: ${{ github.actor }}"
      echo "Repository: ${{ github.repository }}"

  - name: Debug context
    env:
      GITHUB_CONTEXT: ${{ toJson(github) }}
    run: echo "$GITHUB_CONTEXT"

actを使ったローカルテスト

act はGitHub Actionsをローカルで実行できるツール。pushしなくてもワークフローをテストできる。

# インストール(macOS)
brew install act

# ワークフローを実行
act push

# 特定のジョブを実行
act -j test

# ドライラン(実行せずに内容を確認)
act -n

まとめ

GitHub Actionsの学習ステップ:

  1. まずは簡単な「push時にHello Worldを出力する」ワークフローから始める
  2. lint、test、buildの基本的なCIパイプラインを構築する
  3. キャッシュやマトリックスビルドで最適化する
  4. デプロイの自動化に挑戦する
  5. セキュリティベストプラクティスを適用する
学習段階習得すべき内容
初級ワークフローの作成、push/PRトリガー、基本的なステップ
中級キャッシュ、マトリックス、シークレット、アーティファクト
上級Composite Action、OIDC認証、セキュリティ対策、ワークフロー最適化

参考リンク