⚒️
submoduleで複数ブランドサイトを展開し、ブランドごとに Favicon を差し替える手順
はじめに
複数ブランド向けに同じサイト基盤(base)を Git Submodule で共有しつつ、ブランド独自の favicon を簡単に差し替える方法をまとめました。
この記事では以下をゴールとします。
- リポジトリ構成:共通コードとブランド別リポジトリの設計
- サブモジュール運用フロー:追加・更新・トラブルシューティング
- Favicon のブランド別上書き:Next.js / Nuxt 3 両対応の汎用パターン
- 自動デプロイ:GitHub Actions でサブモジュール更新を検知 → 静的ホスティングへ CI/CD
1. リポジトリ構成
└ brand_alpha/ # ブランド側リポジトリ
├ base/ # 共有コード(submodule)
├ .gitmodules
├ apps/web/ # ブランド固有追加コード (任意)
└ public/
└ favicon.ico # ← ブランド専用ファビコン
- base : すべてのブランドが共通で参照する UI / API / utility 群
- brand_* : ブランド専用リポジトリ。base/ をサブモジュールとして配置し、必要に応じて独自の UI・設定・画像を上書きします。
Tips:
サブモジュールは「特定のコミットを固定で参照」する仕組みです。base を更新したら、各ブランド側で git submodule update ➜ コミット ➜ PR する必要があります。
2. サブモジュールの追加
# brand_<name> 側で実行
git submodule add https://github.com/your‑org/brand_base.git base
git commit -m "Add base as submodule"
ハマりポイント
エラー | 原因 | 解決策 |
---|---|---|
fatal: ... did not contain <commit> | base側の対象コミットがGithubから消えた | brand側の.gitmodules を修正し、git submodule sync
|
submodule が古いまま |
git pull では submodule が更新されない |
git submodule update --remote --merge や Renovate Bot の導入 |
3. Favicon をブランドごとに差し替える
3‑1. Next.js の場合
1. base 側の app/layout.tsx(または pages/_document.tsx)で、favicon へのパスを 相対パス で記述します。
<link rel="icon" href="/favicon.ico" sizes="any" />
2. brand 側の public/ に favicon.ico を置く
- サブモジュール内のファイルは 優先度が低い ため、ルートの public/favicon.ico が勝ちます。
- PNG / SVG マルチサイズ対応なら <link rel="icon" type="image/png" …> を複数置いて OK。
3‑2. Nuxt 3 の場合
1. base 側の nuxt.config.ts
export default defineNuxtConfig({
app: {
head: {
link: [{ rel: 'icon', href: '/favicon.ico' }],
},
},
})
2. brand 側で上書きしたい場合は nuxt.config.ts を extends して favicon だけ変更。
// brand/nuxt.config.ts
import base from './base/nuxt.config'
export default defineNuxtConfig({
extends: base,
app: { head: { link: [{ rel: 'icon', href: '/favicon.ico' }] } },
})
ポイント:
サブモジュール配下より上位階層のファイルが優先されるので、ビルド設定はなるべく base に集約し、静的アセットでブランド差異を出すと管理が楽です。
4. GitHub Actions で自動デプロイ
.github/workflows/deploy.yml
name: Deploy Brand Alpha
on:
push:
branches: [ main ]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive # ★ サブモジュールも一緒に取得
- uses: pnpm/action-setup@v2
with:
version: 8
- run: pnpm install --frozen-lockfile
- run: pnpm run build
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: dist
path: .output # Nuxt3 の例
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: dist
path: ./dist
- name: Deploy to Hosting
run: |
curl -X POST -F "file=@dist.zip" "$WEBHOOK_URL"
- submodules: recursive を忘れると、CI 上で base ディレクトリが空のままビルド失敗 します。
- workflow_dispatch を残しておくと、「base 更新→brand 側 submodule 追従コミットなし」でも手動デプロイ可。
5. 運用ベストプラクティス
シーン | ベストプラクティス |
---|---|
base 更新を各ブランドへ即時反映したい | Renovate などで submodule bump PR を自動生成 → マージで自動デプロイ |
ブランド固有のビルド設定が増えてきた |
base 側の Monorepo(例: pnpm-workspace.yaml )にブランド用パッケージを定義して一元管理 |
ファビコン以外にも差異がある |
public/assets/brand/** 配下に静的アセットをまとめ、ブランド側で上書きできる構成にする |
まとめ
- Git Submodule を使えば、共通コードを中央管理しつつブランドサイトを軽量に保守できます。
- 静的アセット(favicon など)は ブランドリポジトリ 直下で上書きすると衝突せず手軽。
- GitHub Actions の submodules: recursive を忘れないことが最大の落とし穴。
- 運用効率を上げるには、自動バージョンアップ Bot・Monorepo 化・ブランチ戦略の整備が鍵です。
Discussion