Proud Blog を Middleman から Astro へ移行した話

はじめに

このブログ(Proud Blog)はもともと Middleman で構築・運用していました。記事は Markdown で書かれており、GitHub リポジトリで管理されていましたが、ビルド環境の老朽化・デザインの陳腐化・デプロイフローの煩雑さが課題でした。

今回 Astro へ全面移行し、GitHub Actions による自動デプロイ環境を整備しました。作業のほぼ全工程を Claude Code CLI との対話で進めています。


移行前の構成

項目内容
SSGMiddleman
記事形式.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 スクリプトで一括変換しました。

変換内容:

MiddlemanAstro
date:pubDate:
ogp.og.descriptiondescription:
tags: AWS,EC2tags: ["AWS", "EC2"]
ファイル名 *.html.md*.md

authortags はカスタムフィールドとして 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;
}

移行後の構成

項目内容
SSGAstro v6
ホスティングS3 + CloudFront
デプロイGitHub Actions(push 自動)
認証OIDC(長期クレデンシャル不要)
URL リライトCloudFront Function

まとめ

158記事・620枚の画像を含む大規模な移行でしたが、Claude Code CLI との対話で1日以内に完了しました。手作業は GitHub リポジトリと AWS 側の初期設定のみで、変換スクリプト・デザイン実装・ワークフロー構築・CloudFront 設定はすべてチャットで指示するだけで進みました。

移行によって得られたもの:

  • デプロイ自動化: git push だけで公開完了
  • モダンなデザイン: タグ検索・ライトボックス・日本語フォント対応
  • 運用コスト削減: サーバーレス構成でほぼゼロコスト
  • セキュリティ向上: 静的ファイルのため攻撃面が大幅に減少

このブログ自体が Claude Code CLI 活用の実例になっています。