Proud Blog を Middleman から Astro へ移行した話
はじめに
このブログ(Proud Blog)はもともと Middleman で構築・運用していました。記事は Markdown で書かれており、GitHub リポジトリで管理されていましたが、ビルド環境の老朽化・デザインの陳腐化・デプロイフローの煩雑さが課題でした。
今回 Astro へ全面移行し、GitHub Actions による自動デプロイ環境を整備しました。作業のほぼ全工程を Claude Code CLI との対話で進めています。
移行前の構成
| 項目 | 内容 |
|---|---|
| SSG | Middleman |
| 記事形式 | .html.md(YAML フロントマター) |
| 記事数 | 158件 |
| ホスティング | 旧サーバー(blog.proudit.jp) |
| デプロイ | 手動 |
移行作業の全工程
1. Astro プロジェクトのセットアップ
npm create astro@latest astro-blog -- --template blog --no-install --no-git
Astro の公式ブログテンプレートを起点に、astro-blog ディレクトリを作成しました。
2. ソースリポジトリのクローン
元ブログのリポジトリを git clone で取得。source/posts/ 以下に 158件の .html.md ファイルが格納されていることを確認しました。
3. 158記事の一括変換
Middleman のフロントマターと Astro のスキーマには差異があります。Python スクリプトで一括変換しました。
変換内容:
| Middleman | Astro |
|---|---|
date: | pubDate: |
ogp.og.description | description: |
tags: AWS,EC2 | tags: ["AWS", "EC2"] |
ファイル名 *.html.md | *.md |
author と tags はカスタムフィールドとして Astro のコンテンツスキーマに追加しています。
4. 画像の移設
記事内の画像参照パターンは3種類ありました。
| パターン | 件数 | 対応 |
|---|---|---|
外部 URL(https://...) | 627枚 | ダウンロードして public/images/posts/ に保存・URL 置換 |
相対パス(../images/...) | 135枚 | 元リポジトリからコピー、/images/ に置換 |
同階層(./images/...) | 数枚 | 同上 |
外部 URL は 620枚のダウンロードに成功。7枚はリンク切れのため、記事内でも非表示のままとなっています。
5. Markdown の修正
元記事では #見出し(# 直後にスペースなし)が Middleman の拡張で許容されていましたが、CommonMark 準拠の Astro では正しく解釈されません。64ファイル・244箇所を正規表現で一括修正しました。
re.sub(r'^(#{1,6})([^\s#\n])', r'\1 \2', body, flags=re.MULTILINE)
また、見出し直後の ---(Middleman 慣習のデコレーション)も同様に削除しました。
6. デザイン刷新
デフォルトテンプレートから以下の点を全面刷新しました。
- フォント: Noto Sans JP(日本語)+ JetBrains Mono(コード)
- ヘッダー: スティッキー・backdrop-filter blur 付きモダンナビ
- 記事一覧: カード型リスト(日付・著者・タグ表示)
- 記事ページ: カード型レイアウト・戻るボタン
- ライトボックス: 記事内画像クリックで拡大表示(ESC / 背景クリックで閉じる)
- タグページ:
/blog/tags/でタグ一覧、/blog/tags/AWS/でタグ絞り込み
7. GitHub Actions によるデプロイ自動化
main ブランチへの push をトリガーに、ビルド → S3 同期 → CloudFront キャッシュクリアを自動実行するワークフローを構築しました。
- name: Deploy to S3
run: |
aws s3 sync dist/ s3://proudblod-static-site \
--delete \
--cache-control "public, max-age=31536000, immutable" \
--exclude "*.html"
aws s3 sync dist/ s3://proudblod-static-site \
--cache-control "public, max-age=0, must-revalidate" \
--include "*.html"
- name: Invalidate CloudFront cache
run: |
aws cloudfront create-invalidation \
--distribution-id E2DVKEB6G2KQ47 \
--paths "/*"
HTML と静的アセットでキャッシュ戦略を分けています。HTML は must-revalidate(常に最新)、画像・CSS・JS は immutable(1年間キャッシュ)です。
AWS 認証は OIDC を使い、長期クレデンシャルを使用しない構成にしています。
8. CloudFront URL リライト
Astro の静的出力は /blog/xxx/index.html の形式で生成されますが、CloudFront + S3 REST API エンドポイントの構成では /blog/xxx/ へのアクセスが index.html を返しません。
CloudFront Function を作成して Viewer Request でパスをリライトすることで解決しました。
function handler(event) {
var request = event.request;
var uri = request.uri;
if (uri.endsWith('/')) {
request.uri += 'index.html';
} else if (!uri.split('/').pop().includes('.')) {
request.uri += '/index.html';
}
return request;
}
移行後の構成
| 項目 | 内容 |
|---|---|
| SSG | Astro v6 |
| ホスティング | S3 + CloudFront |
| デプロイ | GitHub Actions(push 自動) |
| 認証 | OIDC(長期クレデンシャル不要) |
| URL リライト | CloudFront Function |
まとめ
158記事・620枚の画像を含む大規模な移行でしたが、Claude Code CLI との対話で1日以内に完了しました。手作業は GitHub リポジトリと AWS 側の初期設定のみで、変換スクリプト・デザイン実装・ワークフロー構築・CloudFront 設定はすべてチャットで指示するだけで進みました。
移行によって得られたもの:
- デプロイ自動化:
git pushだけで公開完了 - モダンなデザイン: タグ検索・ライトボックス・日本語フォント対応
- 運用コスト削減: サーバーレス構成でほぼゼロコスト
- セキュリティ向上: 静的ファイルのため攻撃面が大幅に減少
このブログ自体が Claude Code CLI 活用の実例になっています。