はじめに
本記事はSWAを通じ、モノレポ構成に準じたGitHubActionでのCICD実装を行います。
初めて触るGithubActionコードと、モノレポ構成独特のCICD構成を考えてみましたので、よければご参考ください。では〜!
環境設定
環境・アプリケーション構成
- GitHub: ソースコード管理とCI/CDパイプラインの実行環境
- Azure: デプロイ先のクラウドプラットフォーム(Azure Static Web Apps)
- モノレポアプリケーション: Turborepoなどで管理されたモノレポ構成の複数フロントエンドアプリケーション
今回、すでにTurborepoを用いたモノレポアプリケーションを前提に、GitHubActionを記載していきます。
フロントエンドの環境構築についての記事もあるのでぜひ〜!
→React+Vite+Turborepo+TanstackRouterを用いた爆速フロントエンド開発のお話
Static Web Apps
概要
Azure Static Web Apps(以降略、SWA)とは、Azure上でWebアプリケーションを簡単にデプロイするサービスです。
主な特徴として、
- 静的コンテンツのWebホスティング:インターネット上でWebサイトを公開・アクセス可能にします
- GitHub統合による自動ビルドとデプロイ:コード変更からデプロイまでを一連の流れで自動化します
- プレビュー環境の自動作成:プルリクエストごとに独立した環境が自動的に構築されます
- 様々なフレームワーク対応:React、Vue、Angularなどの主要JavaScriptフレームワークから、Gatsby、VuePressなどの静的サイトジェネレーターまで幅広くサポートしています
- グローバルホスティング:CDNを活用し、ユーザーに近い場所からコンテンツを配信することで高速なアクセスを実現します
などが挙げられます。
SWAの作成手順
今回作成するアプリケーションのデプロイ先としてSWAを利用します。
作成方法は様々ですが、今回はAzure Portal上より作成する一例をご紹介します。
-
Azure Portalにアクセスし、Static Web Appsを作成します。
-
GitHubリポジトリからソースを取得します。
GitHubアカウントと連携し、デプロイするアプリケーションのあるレポジトリと接続します。
デプロイには数分かかる場合があります。デプロイ後、任意のURLが発行されます。発行されたURLにアクセスして動作確認を行います。
SWAは、Free、Standard、Dedicated (プレビュー) の3プランから選択できます。
それぞれアプリサイズや制限などが異なります。詳細はこちら→Azure Static Web Apps ホスティング プラン
初期GitHubActionファイル
SWAはGitHubとの連携が完了すると、GitHubActionを制御するためのyamlファイルが自動的に生成されます。作成されたファイルは自動的にコミットし、GitHub管理されます。
また必要なSecrets値は自動的に作成、保管されます。
ファイル格納先は以下のような構成です。
.github
└workflows
└hoge.yml
ここで作成されたワークフローのYAMLファイル名でのみ、本番環境へビルドされます。ファイル名を変更すると正しくデプロイされないのでご注意を!!
こちらの内容をもとにGitHubActionの構成について説明していきます。
初期作成yaml 全量
name: Azure Static Web Apps CI/CD
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened, closed]
branches:
- main
jobs:
build_and_deploy_job:
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
runs-on: ubuntu-latest
name: Build and Deploy Job
steps:
- uses: actions/checkout@v3
with:
submodules: true
lfs: false
- name: Build And Deploy
id: builddeploy
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_HOGE }}
repo_token: ${{ secrets.GITHUB_TOKEN }}
action: "upload"
app_location: "./apps"
api_location: ""
output_location: "build"
close_pull_request_job:
if: github.event_name == 'pull_request' && github.event.action == 'closed'
runs-on: ubuntu-latest
name: Close Pull Request Job
steps:
- name: Close Pull Request
id: closepullrequest
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_HOGE }}
action: "close"
GitHub Actions設定の詳細
1. ワークフロー名
name: Azure Static Web Apps CI/CD #ワークフロー名
GitHubのAction名として表示されます。
GitHub上ではこのように表示されます。

2. トリガー設定
on:
push: # pushをトリガーとする
branches:
- main # メインブランチ
pull_request: # PR作成をトリガーとする
types: [
opened, # 新規PR作成
synchronize, # PR更新(新しいコミットが追加された時など)
reopened, # PR再オープン
closed # PRクローズ
]
branches:
- main # メインブランチ
ワークフローを実行するトリガーを定義します。
この例では「メインブランチへのPush」または「メインブランチへのPRを作成、更新、再オープン、クローズ時」に実行されるワークフローとなります。
GitHubActionでは様々なトリガーが設定可能です。またPRトリガーの中のアクテビティもたくさんあるので、以下公式サイトをご確認ください。
3. ジョブ設定
jobs:
build_and_deploy_job: # 任意のジョブID
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') # 実行条件
runs-on: ubuntu-latest # 実行環境
name: Build and Deploy Job # 任意のジョブ名
steps:
〜略〜
ワークフローの処理を定義します。
ここでは任意のジョブIDを指定して、そのIDに紐づくジョブを作成します。この例ではbuild_and_deploy_job
というIDを使用しており、このIDはのちのジョブの参照や依存関係の設定に利用できます。
その下には条件や、実行環境、ジョブ名などが記載されます。
ここでは、条件として「Pushイベントが発生した場合」または「PRイベントが発生し、かつそのアクションがクローズでない場合」という条件を設定しています。この条件により、PRがクローズされた時にはこのジョブは実行されません。
また、実行環境としてはUbuntu Linuxの最新バージョンを使用するよう指定しています。
ジョブは複数作成することができ、デフォルトでは並列で実行されます。しかし完了したら実施する、など依存関係を持つジョブを作成することも可能です。
jobs:
test: # 任意のジョブIDA
if: github.event_name == 'pull_request' && github.event.action != 'closed' # 実行条件
build: # 任意のジョブIDB
needs: test # 依存関係ジョブID(ここでは上記ジョブID)
if: ${{ !cancelled() && !failure() }} # 実行条件
上記の例では、testジョブに依存しbuildジョブが設定されており、testジョブ→buildジョブの順で実行されます。
GitHub上では以下のように表示され、ジョブ関係が線で繋がって表示されます。
またif
に実行条件が記載されており、この例でのbuildジョブの条件は、testジョブに成功した場合、またはtestジョブがスキップされた場合で実行されます。
条件についてわかりやすかった記事はこちら↓
4. ステップ設定
ジョブ内には、ステップという一連のタスクが連なっています。
jobs:
build_and_deploy_job:
〜略〜
steps:
- uses: actions/checkout@v3 #Github提供の固定アクション
with:
submodules: true # サブモジュールを含める
lfs: false #Git Large File Storageファイルをダウンロードしない
- name: Build And Deploy # 任意のステップ名
id: builddeploy # 任意のステップID
uses: Azure/static-web-apps-deploy@v1 #Github提供の固定アクション(SWA専用)
with:
〜略〜
ジョブ内の具体的な処理内容をステップとして定義します。
各ステップは-
で始まる行で表され、それぞれが独立した処理単位となります。
ここでは条件や、実行コマンド、ステップ名などが記載されます。
特にuses
には様々な便利なアクションが提供されています。@
の後にはバージョン番号を指定できます。
以下は代表的なアクションの例です:
- actions/checkout: リポジトリのコードをチェックアウトするアクション
- actions/setup-node: Node.jsの環境をセットアップするアクション
- pnpm/action-setup: pnpmパッケージマネージャーをセットアップするアクション
GitHub上ではこのように表示されます。

SWA専用Action
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_HOGE }} # SWAトークン
repo_token: ${{ secrets.GITHUB_TOKEN }} # GitHubトークン
action: "upload" # アップロード
app_location: "./apps" # フロントエンドアプリケーションのソースコードパス
api_location: "" # APIアプリケーションのソースコードパス
output_location: "build" # ビルドファイル生成パス
SWAの大きな特徴の一つは、ビルドとデプロイのプロセスが統合されていることです。上記の設定では、アプリケーションのソースコード場所(app_location
)、APIの場所(api_location
)、ビルド出力先(output_location
)などを指定することで、GitHub Actionsが自動的にアプリケーションをビルドし、そのままAzureへデプロイします。
その他の詳細な設定オプションについては、以下公式サイトをご確認ください。
また、もう一つのSWAの特徴である、PR作成時のプレビュー環境についても上記 build_and_deploy_job
ジョブ内で実施されます。
mainブランチへのデプロイの場合は本番デプロイ、それ以外のデプロイの場合は一時的なプレビュー環境へのデプロイがされます。
このプレビュー環境はPRがクローズ時と一緒にクローズするため、以下の様なアクションが必要となります
close_pull_request_job:
if: github.event_name == 'pull_request' && github.event.action == 'closed' # 実行条件
runs-on: ubuntu-latest # 実行環境
name: Close Pull Request Job # 任意のジョブ名
steps:
- name: Close Pull Request # 任意のステップ名
id: closepullrequest # 任意のステップID
uses: Azure/static-web-apps-deploy@v1 #Github提供の固定アクション(SWA専用)
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_HOGE }} # SWAトークン
action: "close" # クローズ
プレビュー環境の上限はSWAのプランによって異なります。
Freeプランの場合は3環境、Standardプランの場合は10環境、Dedicatedプランの場合は20環境が上限となっています。
モノレポ環境でのCICD実装
モノレポ環境でのCICD実装には複数のアプローチが考えられます。ここでは、私が検討した2つの方法について説明します。
1. パスベースの分岐方式
トリガー箇所にターゲットとなるパスを指定し、1つのアプリケーションを監視するように指定します。
on:
push: # pushをトリガーとする
branches:
- main # メインブランチ
paths:
- apps/management/** # Managementアプリケーションのファイル
- packages/** # 共通UIパッケージのファイル
- .github/workflows/azure-static-web-apps-hoge.yml # ワークフローのファイル
- package.json # パッケージマネージャーのファイル
- turbo.json # ビルドツールのファイル
このように指定したフォルダ配下やファイルにのみ加えられた変更がトリガーとなり、実行されます。
全量例
name: App CI/CD
on:
push: # プッシュ時の処理
branches:
- main
paths:
- apps/management/** # Managementアプリケーションのファイル
- packages/** # 共通UIパッケージのファイル
- .github/workflows/azure-static-web-apps-hoge.yml # ワークフローのファイル
- package.json # パッケージマネージャーのファイル
- turbo.json # ビルドツールのファイル
pull_request: # PR時の処理
types: [
opened, # 新規PR作成
synchronize, # PR更新
reopened, # PR再オープン
closed # PRクローズ
]
branches:
- main
- develop
paths:
- apps/management/** # Managementアプリケーションのファイル
- packages/** # 共通UIパッケージのファイル
- .github/workflows/azure-static-web-apps-management-kind-mud-049051900.yml # ワークフローのファイル
- package.json # パッケージマネージャーのファイル
- turbo.json # ビルドツールのファイル
jobs:
# フロントエンドアプリケーションのテスト
test-management:
if: github.event_name == 'pull_request' && github.event.action != 'closed'
runs-on: ubuntu-latest
name: Test Management
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Run Test Management
run: |
# テスト作成時に記述する example(pnpm --filter management test)
echo "Management App Tested"
# フロントエンドアプリケーションのビルド
build-management:
needs: test-management
if: ${{ !cancelled() && !failure() && (github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')) }}
runs-on: ubuntu-latest
name: Build Management
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
lfs: false
- name: Build Management
run: |
npm install -g pnpm@10.0.0
pnpm install
pnpm --filter management build
echo "Management App Built"
- name: Upload Management Build Artifact
uses: actions/upload-artifact@v4
with:
name: management-build
path: apps/management/dist
retention-days: 1 # ビルド成果物の保存期間(日)
# フロントエンドアプリケーションのデプロイ
deploy-management:
needs: build-management
if: ${{ !cancelled() && !failure() && (github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')) }}
runs-on: ubuntu-latest
name: Deploy Management
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
lfs: false
- name: Download Management Build Artifact
uses: actions/download-artifact@v4
with:
name: management-build
path: apps/management/dist
- name: Deploy Management
id: deploymanagement
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_HOGE }}
repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
action: "upload" # デプロイアクション
app_location: "./apps/management/dist" # アプリケーションの場所
output_location: "" # ビルド出力先
skip_app_build: true # ビルドをスキップ
# PRクローズ時の処理
# Azure Static Web Appsのプレビュー機能を使用するため、PRクローズ時の処理を記述
close_pull_request_job_management:
if: github.event_name == 'pull_request' && github.event.action == 'closed'
runs-on: ubuntu-latest
name: Close Management App PR Environment
steps:
- name: Close Pull Request (Management)
id: closepullrequestmanagement
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_HOGE }}
action: "close"
2. Git差分フラグ分岐方式
モノレポ構成では、Turborepoを利用することで効率的なビルド管理が可能です。Turborepoが提供するFilter機能を活用し、変更があったパッケージのみをビルド対象とするフラグ管理を実装します。
- name: Check App Changed
id: check-changes
run: |
# Git履歴に差分があるか確認
GIT_CHANGES=$(git diff --name-only HEAD^1 HEAD || echo "")
# アプリケーションごとに変更を検出
FRONTEND_CHANGED=$(pnpm turbo run build --dry-run --filter="frontend[HEAD^1]" | grep frontend || echo "")
MANAGEMENT_CHANGED=$(pnpm turbo run build --dry-run --filter="management[HEAD^1]" | grep management || echo "")
# FrontendとManagementに変更がなく、Git履歴に差分があれば、共通リソースへの変更として扱う
if [ -z "$FRONTEND_CHANGED" ] && [ -z "$MANAGEMENT_CHANGED" ] && [ -n "$GIT_CHANGES" ]; then
echo "frontend-changed=true" >> "$GITHUB_OUTPUT"
echo "management-changed=true" >> "$GITHUB_OUTPUT"
else
echo "frontend-changed=$([ -n "$FRONTEND_CHANGED" ] && echo "true" || echo "false")" >> "$GITHUB_OUTPUT"
echo "management-changed=$([ -n "$MANAGEMENT_CHANGED" ] && echo "true" || echo "false")" >> "$GITHUB_OUTPUT"
fi
TurborepoコマンドおよびGitコマンドを用いて、コードベースの差分を検出します。
Turborepoのフィルター機能(--filter
オプション)を使用することで、変更があったパッケージのみを特定できます。
この例では差分検出のみを行うジョブを作成し、その結果を出力変数(outputs)として後続のジョブに渡します。後続のジョブではこの変数を条件として使用し、必要なジョブのみを実行する仕組みになっています。
全量例
# アプリケーションの変更を検知
detect-changes:
runs-on: ubuntu-latest # 実行環境
outputs: # 変数
frontend-changed: ${{ steps.check-changes.outputs.frontend-changed }}
management-changed: ${{ steps.check-changes.outputs.management-changed }}
name: Detect Changes # 任意のジョブ名
steps:
- name: Checkout # 任意のステップ名
uses: actions/checkout@v4 #Github提供の固定アクション
with:
# ブランチの履歴を全て取得
fetch-depth: 0
- name: Setup Node # 任意のステップ名
uses: actions/setup-node@v4 #Github提供の固定アクション
with:
# package.jsonのenginesに記載されているバージョンを使用
node-version: '18'
- name: Setup pnpm # 任意のステップ名
uses: pnpm/action-setup@v4 #Github提供の固定アクション
with:
# package.jsonのpackageManagerに記載されているバージョンを自動使用
# pnpm installを実行
run_install: true
- name: Check App Changed # 任意のステップ名
id: check-changes # 任意のステップID
run: | #実行コマンド
# Git履歴に差分があるか確認
GIT_CHANGES=$(git diff --name-only HEAD^1 HEAD || echo "")
# アプリケーションごとに変更を検出
FRONTEND_CHANGED=$(pnpm turbo run build --dry-run --filter="frontend[HEAD^1]" | grep frontend || echo "")
MANAGEMENT_CHANGED=$(pnpm turbo run build --dry-run --filter="management[HEAD^1]" | grep management || echo "")
# FrontendとManagementに変更がなく、Git履歴に差分があれば、共通リソースへの変更として扱う
if [ -z "$FRONTEND_CHANGED" ] && [ -z "$MANAGEMENT_CHANGED" ] && [ -n "$GIT_CHANGES" ]; then
echo "frontend-changed=true" >> "$GITHUB_OUTPUT"
echo "management-changed=true" >> "$GITHUB_OUTPUT"
else
echo "frontend-changed=$([ -n "$FRONTEND_CHANGED" ] && echo "true" || echo "false")" >> "$GITHUB_OUTPUT"
echo "management-changed=$([ -n "$MANAGEMENT_CHANGED" ] && echo "true" || echo "false")" >> "$GITHUB_OUTPUT"
fi
test-build-deploy-frontend:
needs: detect-changes
if: ${{needs.detect-changes.outputs.frontend-changed == 'true' && (github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')) }} # 実行条件
runs-on: ubuntu-latest # 実行環境
name: Frontend # 任意のジョブ名
steps:
- name: Checkout # 任意のステップ名
uses: actions/checkout@v4 #Github提供の固定アクション
- name: Run Test Frontend # 任意のステップ名
run: | #実行コマンド
# テスト(仮)
echo "Frontend App Tested"
- name: Build Frontend # 任意のステップ名
run: | #実行コマンド
# ビルド(仮)
echo "Frontend App Built"
GitHubActionのデバッグログを出力することも可能です。
ランナーログ(ACTIONS_RUNNER_DEBUG
)とステップログ(ACTIONS_STEP_DEBUG
)の2種類があります。
ランナーログはワークフロー全体の詳細情報を、ステップログは各ステップの実行過程で[debug]
というプレフィックス付きの詳細情報を表示します。
これらを有効にするには、GitHubのリポジトリ設定でSecret値として登録し、値をtrue
に設定するだけです。
ログはGithub上または、ログをダウンロードすることで確認できます。
詳細はこちら→デバッグ ログを有効にする
SWAコンフィグファイル
SWAの構成をstaticwebapp.config.json
ファイルにて定義を行います。
実際の具体的エラーに基づきながら説明します。
シングルページアプリケーション(SPA)構成の場合、直パスやリロード時パス通りアクセスした場合存在しないhtmlへアクセスしにいくため404エラーとなります。

この原因を深掘りしていくために新ためてSPAについて理解を深めていくとします。
シングルページアプリケーション(SPA)は文字どおり1つのhtmlをベースとし、変動するコンテンツ情報を取得しクライアント側でhtmlを生成、描写をします。
具体的なコードを用いると、index.html
のroot
IDのあるdivタグに描写する情報をmain.tsx
で定義しています。
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div> # ここに変動的にコンテンツが描写される
<script type="module" src="/src/main.tsx"></script> # Reactアプリケーションを初期化・実行するJavaScriptを読み込む
</body>
</html>
const rootElement = document.getElementById("root")!;
if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement);
root.render(
<StrictMode>
<RouterProvider router={router} />
</StrictMode>,
);
}
従来のマルチページアプリケーション(MPA)の場合、画面ごとにサーバーで作成されたhtmlを呼び出しますが、
シングルページアプリケーション(SPA)の場合、htmlは1つしかないため、存在しない画面のhtmlを呼び出す形となり404エラー、となるのです。
これを解消するために、SWA設定にてリソースがないリクエストの場合に指定のhtmlを返すよう設定します。
シングルページアプリケーション(SPA)の場合はindex.html
となります。
{
"navigationFallback": {
"rewrite": "/index.html"
}
}
作成されたstaticwebapp.config.json
は/public
に格納し、ビルド時にはビルドファイルへコピーされます。
デプロイの際、GithubActionのyamlファイル上で定義しているapp_location
の位置にstaticwebapp.config.json
が配置されるようにしてください。
便利ツール
最後に!GitHubActionを作成に当たり、便利ツールをご紹介!
YAMLチェックツール
GitHub Actionsのワークフローファイル(YAML)の構文チェックができるツールです。
https://rhysd.github.io/actionlint/
ローカル実行環境 - act
GitHub Actionsをローカル環境で実行・テストできるツールです。
https://qiita.com/kaminuma/items/37838dd31aca91944453
- 個人環境ではRancher Desktopを利用しています
- Docker CLIが有効になっている必要があります
まとめ
今回はモノレポ構成アプリケーションへのGithubAction+Azure StaticWebAppsを用いたCICD構成を考え実装してみました!
はじめて触るGithubActionだったり、SWA独自の設定方法などで悩む部分が多くありましたが、とても勉強になりました。またまだキャッシュやデプロイ構成など改良余地ありだと思うのでブラッシュアップしていければと思います。
引き続き頑張っていきますのでよろしくお願いします〜!