monolithic kernel

Astro で1つのコードベースから複数のサイトを生成する

Astro で複数のサイトを生成していると、コンテンツは異なるがコードはほとんど同じということがある。そのような状況で、コードベースを一本化してメンテナンスを楽にする方法を検討した。

公式に案内されているものがあればそれを採用したかったのだが、見つけられなかったので、あまり凝ったことはせずに、シンプルにシンボリックリンクを使った方法で実現してみた。

サイトの構成

以下のようなディレクトリ構造にして、content のシンボリックリンクが示す先を切り替えることで、生成するサイトを切り替える。

.
├── sites/
│ ├── site-a/ # サイトAのコンテンツ
│ │ ├── blog/
│ │ ├── public/
│ │ └── metadata.json
│ └── site-b/ # サイトBのコンテンツ
│ ├── blog/
│ ├── public/
│ └── metadata.json
├── content -> sites/site-a/ # シンボリックリンク
└── src/ # 共通コード

package.json にサイト切り替えコマンドを用意している。

package.json
{
"scripts": {
"switch:site-a": "ln -sfn sites/site-a content",
"switch:site-b": "ln -sfn sites/site-b content"
}
}

ビルド前にこのコマンドを実行することで、ビルド対象を切り替える。

Terminal window
pnpm run switch:site-a
pnpm run build

Astro 側は、content 以下を参照してビルドするように普通に設定している。サイトごとに挙動を変えたい場合は、設定ファイルも content 以下に配置して読み込めばよい。

ビルド・デプロイ

GitHub Actions の matrix 機能を使い、各サイトを並列にビルド・デプロイしている。自分の場合だと Cloudflare Workers をデプロイ先にしているので、wrangler.toml にサイトごとの環境を定義しておいて、デプロイ時に指定している。

.github/workflows/ci.yml
get-sites:
runs-on: ubuntu-latest
outputs:
sites: ${{ steps.set-matrix.outputs.sites }}
steps:
- uses: actions/checkout@v6
- id: set-matrix
run: |
SITES=$(ls -1 sites | jq -R -s -c 'split("\n")[:-1]')
echo "sites=$SITES" >> "$GITHUB_OUTPUT"
deploy:
needs: get-sites
strategy:
matrix:
site: ${{ fromJson(needs.get-sites.outputs.sites) }}
runs-on: ubuntu-latest
steps:
# ...
- run: pnpm run "switch:$SITE"
env:
SITE: ${{ matrix.site }}
- run: pnpm run build
- run: wrangler deploy --env "$SITE"
env:
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
SITE: ${{ matrix.site }}
wrangler.toml
[env.site-a]
name = "site-a"
[env.site-b]
name = "site-b"

Related articles