GitをCMS代わりにブログを構築してみた話:リポジトリ構成とデプロイ

8分で読了

前回の記事で「WordPressからAstroに移行した」という話をしましたが、今回はその裏側。 「で、実際Gitリポジトリはどうなってるの?」というエンジニア向けのディープな話です。

結論から言うと、「コンテンツ(Markdown)」と「ガワ(Astro)」を別々のリポジトリに分け、Git Submoduleで合体させるという構成を取りました。 一見面倒くさいこの構成をなぜ選んだのか、その設計思想と現実のトレードオフについて語ります。

リポジトリ構成の全体像

百聞は一見に如かず。今の構成はこんな感じです。

graph LR
  • Content Repository: 記事のMarkdownファイルと画像のみを管理。Astroの設定ファイルなどは一切なし。
  • System Repository: Astro本体、コンポーネント、CSSなどを管理。Content Repositoryを src/contents 配下にサブモジュールとしてマウント。

なぜ分けたのか?(推したいメリット)

普通に考えれば、1つのリポジトリに全部突っ込む(Monorepo)のが一番ラクです。 でも、あえて分けました。そこにはエンジニア独自の「こだわり」と、ソフトウェア設計的な「正当性」があります。

1. システムは変わるが、コンテンツは資産である

これが最大の理由です。 今の流行りはAstroですが、3年後には「SuperUltraNext.js」が覇権を握っているかもしれません。 その時、システムとコンテンツが密結合していると、「移行作業=リポジトリの解体と再構築」になります。

リポジトリを分けておけば、将来Astroを捨てて別のフレームワークに乗り換える際、System Repositoryを作り直して、Content Repositoryをサブモジュールとして読み込むだけで済みます(理想論)。 コンテンツという「資産」を、移ろいやすい「技術トレンド」から守るための防衛策です。

2. DDD(ドメイン駆動設計)的な役割分担

ちょっとこじつけてみると、これはDDD的なアーキテクチャの適用とも言えます。

  • コンテンツ = ドメイン層:
    • ブログの本質的な価値。技術選定に依存しない、純粋な情報。
    • 「記事の書きやすさ」や「データの整合性」だけに集中する場所。
  • システム = プレゼンテーション/インフラ層:
    • コンテンツをどう見せるか(HTML化、スタイリング)を担当。
    • ドメイン(コンテンツ)を参照するが、ドメインはシステムを知らなくていい。

この分離により、「記事を書くときは技術のことを考えなくていい」「デザインをいじるときは記事を壊す心配をしなくていい」という、責務の分離が実現できます。 コンテンツリポジトリには package.json すらありません。あるのは純粋なテキストと一部画像のみ。この潔さが心地いいのです。

3. デプロイのトリガー制御

コンテンツの修正(誤字訂正など)と、システムの修正(機能追加)はライフサイクルが違います。 リポジトリを分けておけば、コンテンツ更新のPushをトリガーにするワークフロー、システム更新をトリガーにするワークフローを明確に分離できます。 「CSSを1行直しただけなのに、全記事の再処理が走る」みたいな無駄を防ぎやすくもなります。

現実の厳しさ(トレードオフとデメリット)

と、良いことばかり書きましたが、現実は甘くありません。「分離の代償」は確実に存在します。

1. Git操作がシンプルに面倒くさい

これが最大のデメリットです。 記事を書いて公開するフローが、単一リポジトリなら git push 一発ですが、この構成だと…

  1. Content Repo で記事を書いて Commit & Push。
  2. System Repo に移動して、サブモジュールの参照を更新 (git submodule update --remote)。
  3. System Repo で変更を Commit & Push。

2回Pushしないとデプロイされないわけです。 「記事書いたのに反映されてない!」→「あ、サブモジュールのポインタ更新忘れてた」という事故が多発します。 (GitHub Actionsで自動化もできますが、それはそれで構築コストがかかる…)

2. ローカル開発環境のセットアップが複雑

git clone しただけでは記事データが空っぽです。 git submodule init して update して…という手順を、cloneするたびにやる必要があります。 あと両方のリポジトリを意識しながら作業するのも、地味に面倒です。

Q. そもそもHeadless CMSを使えばいいのでは?

もっともなツッコミです。 microCMSやContentfulを使えば、非エンジニアでも更新できますし、画像管理も楽でしょう。 「コンテンツとシステムの分離」もAPIベースで綺麗に実現できます。

それでもあえてGitベース(Markdown管理)を選んだ理由は、以下のメリット・デメリットを天秤にかけた結果です。

Headless CMS vs Git管理

特徴Headless CMS (microCMS, Contentful)Git管理 (Markdown)
執筆体験Webブラウザ上のエディタVS Code / Vim (ローカル環境)
オフライン不可(ネット必須)可能(飛行機でも書ける)
画像管理GUIでドラッグ&ドロップ (楽)ファイルパスを手書き (苦)
バージョン管理サービスの履歴機能依存Gitの強力な機能がフルに使える
ベンダーロックインあり (サービス終了/値上げのリスク)なし (テキストファイルは永遠)
運用コストプランによる (無料枠制限あり)無料

要するに、「利便性(CMS)」よりも「自由と支配権(Git)」を取ったということです。 「サービスがサ終したので記事をエクスポートして…」という作業は二度としたくない。 Markdownファイルとして手元にあれば、GitHubがなくなってもGitLabに行けばいいとか思ってます。

Cloudflare Pagesへのデプロイ方法

Cloudflare PagesはGit Submoduleに対応しているので、基本的には設定さえ間違えなければ動きます。

1. .gitmodules の設定

System Repository にある .gitmodules ファイルで、コンテンツリポジトリのパスを指定します。 ここで注意なのが、公開リポジトリ(Public)ならHTTPSのURLで書くこと。SSH(git@github.com:...)だと、Cloudflareのビルド環境で権限エラーになります。

[submodule "src/contents"]
path = src/contents
url = https://github.com/your-name/your-content-repo.git

相対パスでも大丈夫だったりします。

[submodule "src/contents"]
path = src/contents
url = ../your-content-repo.git

もしコンテンツリポジトリがPrivateだとしてもCloudflare側で認証していれば動くので、わざわざPublicにする必要はありません。(ブログ記事なら公開されて困るものもないでしょうが…)。

2. ビルドコマンド

git submodule update --init --recursive はCloudflare Pagesがビルド前に勝手にやってくれます(神)。 なので、通常のAstro同様、ビルドコマンドは npm run build、出力ディレクトリは dist を指定するだけでOKです。

まとめ

  • メリット: コンテンツを技術的負債から切り離せる。DDD的にクリーンな構成。将来の移行が楽。
  • デメリット: 日々の運用(Git操作)の手数が増える。

正直、「個人ブログでここまでやる必要ある?」と聞かれれば、「ない」と答えるでしょう。 ただ、エンジニアたるもの、「ロマンある構成」で飯を食いたい時があるのです。 「ただのブログ」を「設計されたコンテンツ配信システム」に昇華させたい方は、ぜひこの茨の道へ足を踏み入れてみてください。