60d.dev ブログを Astro + MDX に移行した
AI 主体で執筆なぜ移行したか
手書き HTML ブログの index.html 競合から逃げる では「記事 30 本 / 月 2 回コンフリクト / 追加忘れ 2 回目」のどれかで踏み切る、と書きました。
2026 年 6 月時点で次が重なり、踏み切りました。
- 公開記事が 27 本超
- 並行 Cursor エージェントで 複数記事ブランチ が同時に走ることが増えた
- AI 開示バッジや文体 lint など 横断変更 をテンプレ 1 箇所で済ませたい
延期の判断は当時正しかった一方、ルール追加だけではコンフリクトは消えない段階に入っていました。
採用した構成
| 項目 | 内容 |
|---|---|
| SSG | Astro 6 + MDX |
| 記事ソース | 60d.dev/astro-blog/src/content/blog/*.mdx |
| 一覧 | Content Collections の getCollection() で日付降順自動生成 |
| AI 開示 | AiDisclosureBadge.astro + 一覧 AiNotice.astro |
| 本番配信 | Netlify(publish = "." の静的配信。生成 HTML を blog/ に commit) |
| 文体 lint | npm run lint:style(MDX ソース)+ textlint |
記事の slug は移行前と同一のため、旧 URL の 301 は不要でした。
やったこと
- Phase 0 — Astro スパイク(パイロット 2 本で build / preview 確認)
- Phase 1 — 公開 HTML から MDX へ一括移行(
scripts/migrate-from-html.mjs、30 記事) - 統合 — 別リポジトリだったスパイクを
60d.dev/astro-blog/に組み込み - 品質 — コードブロックへの言語タグ付与(Shiki ハイライト)、OGP 整備、NG 語彙修正
- 本番 —
scripts/build-blog.shでblog/を生成し、Netlify へ反映(2026-06-06)
astro-blog/ の MDX ソースは netlify.toml で /astro-blog/* を 404 にし、公開物は /blog/ 配下のみです。
移行後の執筆フロー
# 1. MDX を書く
# astro-blog/src/content/blog/<slug>.mdx
# 2. 生成 HTML を blog/ に反映
bash scripts/build-blog.sh
# 3. MDX と blog/ の生成物を同一 PR に含めて main へ
一覧への手動追記は不要です。blog/index.html への行追加コンフリクトは構造的に起きなくなりました。
移行で分かったこと
- HTML → MDX の一括変換では、表組みや一部コードブロックが欠損しやすい。公開前に目視と lint が必要だった
- 生成 HTML を commit する方式なら、Netlify 側の build コマンドは不要。ただし
netlify-should-skip.shのマージ deploy エッジケースには注意が要る - 手書き時代の「index.html 競合」記事に追記する約束は、同記事の追記 で果たした
まとめ
- 記事メタの二重管理(記事 + 一覧)がコンフリクトと追加忘れの根本だった
- Astro + MDX + Content Collections で 記事 = 1 ファイル に寄せ、一覧は自動生成
- 本番は Netlify の静的配信のまま、
blog/だけ Astro 生成物に置き換えた