Go back

redamoon.net を Astro 7 に上げた — 壊さない移行と Cloudflare Pages CI の落とし穴

Posted on:2026-06-24

2026年6月、Astro 7 がリリースされました。自分のブログ redamoon.net は Astro 5 系で動いていたので、ブランチ feature/astro-7-upgrade を切ってアップグレードに着手し、マージまで届けました。

ここでは移行前に立てた計画、実際に手を入れた箇所、CI でハマった点をまとめます。

方針(確定事項)

今回の制約と方針は次のとおりです。

  • 必ず Astro 7 にする@astrojs/*wrangler など周辺ライブラリも合わせて更新する。
  • Markdown は新パイプライン Sätteri に寄せる。ただし記事本文は実質 zenn-markdown-html 系で描画されるため、Astro 標準パイプライン切替の影響は小さい。移行後は全記事をビルドして目視確認する。
  • 対応範囲は Astro 本体アプリ(サイト + scrap ページ)のみworkers/scrap-api(Hono / Cloudflare Worker)はコード変更せず、ビルド・デプロイ・API 疎通の互換確認だけ行う。src/fetch.ts(astro/hono)の新規導入はスコープ外。

「壊さない」を最優先にした結果、新機能の導入や大きな構成変更は今回のブランチから外し、あとで別タスクに回す判断をしました(後述)。

現状把握(移行前に確認したこと)

記事レンダリングは Astro 標準 Markdown と独立

最初に確認したのは「記事本文が Astro の Markdown パイプラインに依存しているか」です。結論は 依存していない でした。

コンテンツ レンダリング経路
log 記事 zenn-markdown-html / markdown-itsrc/pages/log/post/[...slug].astrosrc/utils/logMarkdownToHtml.ts
slides Marp(src/pages/slides/[slug].astro
scrap 詳細 client:only="react"src/pages/scraps/[slug].astro

そのため astro.config.mjs にあった remark-gfm / remark-math / remark-toc / remark-collapse / rehype-katex は、実出力への寄与がほぼありません。KaTeX 数式は Marp(slides)側で完結しています。

Markdown を Sätteri に寄せても記事本文が壊れるリスクは小さい一方、レガシー API の移行が本丸になります。

主リスクはレガシー API

  • **Astro.glob(...)** — index.astrosearch.astrotags/ 配下、log/post/ 配下などで広く使われていた
  • レガシー Content Collections — loader なしの defineCollectionentry.slug / entry.body 参照(src/content/config.tssrc/utils/log.ts、slides ページ)
  • scrap 機能 — 一覧/編集は client:load の React + SCRAP_API_BASE、詳細は SSR(prerender = false)+ client:only。サイト本体の Cloudflare デプロイは wrangler.site.toml 経由

作業手順と実施内容

計画どおり、次の順で進めました。

1. ブランチ作成とベースライン

feature/astro-7-upgrade を作成し、移行前に pnpm build を実行して dist の生成物と警告を控えました。

2. Astro 7 へアップグレード

pnpm dlx @astrojs/upgradeastro@astrojs/* を一括更新しました。おおむね次の構成です。

パッケージ 移行前 移行後
astro 5.18 系 7.0.0
@astrojs/cloudflare 旧系 14.0.0
vite 6 系 8.0.16
@astrojs/react 旧系 6.0.0

react / react-dom は 18 系のまま @astrojs/react の peer を満たすことを確認しました。

@astrojs/tailwind は Astro 7 向けに非対応だったため、Tailwind v3 を維持したまま PostCSS 構成postcss.config.cjs)へ切り替えました。

3. Markdown パイプラインの Sätteri 化

astro.config.mjs から remarkPlugins / rehypePlugins / shikiConfig を撤去し、Astro 7 のデフォルト(Sätteri)に任せました。

記事本文は zenn-markdown-html 経由のため、標準パイプラインへの依存はほぼありません。もし標準 Markdown 出力に差分が出た場合は、@astrojs/markdown-remarkunified() へ退避する保険も計画に含めていましたが、今回は Sätteri のままビルドが通りました。

4. レガシー API の移行(主作業)

  • **Astro.glob** → import.meta.glob(..., { eager: true })src/utils/contentPosts.ts に集約。rss.xml.ts[ogTitle].svg.ts の既存パターンに合わせた)
  • Content Collections → Content Layer API(glob loader を src/content.config.ts に追加)
  • **entry.slug / entry.body** → entry.id ベースの参照へ変更(src/utils/log.ts、slides ページなど)
  • **ViewTransitions**ClientRouter

5. Rust コンパイラ厳格化への対応

Astro 7 の新コンパイラは HTML 自動補正をしない・未閉じタグでエラー・JSX 空白の畳み込みなど、挙動が厳しくなります。今回のビルドでは大きなテンプレート修正は不要でしたが、表示崩れが出た場合は {' '} で空白を明示する、という方針を計画に入れていました。

6. ビルドと静的出力の検証

pnpm buildastro build && jampack ./dist/client)を通し、次を確認しました。

  • log 記事 HTML、CSS/スタイル
  • public/ 配下の画像・SVG・favicon.svg、OG 画像
  • RSS、sitemap

jampack も「No issues」で完了しています。

7. scrap 機能の互換確認

workers/scrap-api(Hono)はコード変更なし。wrangler deploy --dry-run で D1/R2 バインディングが維持されることを確認しました。

scrap 詳細(SSR + client:only)が Worker ランタイムで例外を出さないことも、デプロイ dry-run とビルド成果物の構成で確認しています。

なお zenn-markdown-html の dependencies 版数(0.1.150)と pnpm.patchedDependencies の対象版数(0.1.162)の不整合は、今回のスコープ外として残しています(脚注の docId 生成パッチが正しく当たるかの整備は別タスク)。

8. Cloudflare デプロイ確認

@astrojs/cloudflare v14 では、従来の dist/_worker.js ではなく次の構成に変わります。

  • 静的アセット: dist/client/
  • Worker エントリ: dist/server/entry.mjs
  • デプロイ用設定: **dist/server/wrangler.json**(アダプタが生成)

deploy:site は次の流れです。

pnpm build && wrangler deploy --config ./dist/server/wrangler.json

ルートの wrangler.toml は scrap-api(Hono)専用のままです。Astro サイト側は wrangler.site.toml をアダプタの configPath で参照し、KV(SESSION)やゾーンルート(redamoon.net/*)を引き継ぎます。

prerenderEnvironment: "node" も指定しています。workerd 上の prerender はグローバルスコープでの非同期 I/O 制約があり、OG 画像生成(フォント取得)や getStaticPaths 内の fetch が動かなくなるためです。

9. 仕上げ

README.md のバージョン・コマンド記述を更新しました。pnpm lint はリポジトリに ESLint 設定がなく、今回は触れていません。

今回のスコープ外にしたこと

アップグレード本体から外し、あとで別タスクに回す項目です。リポジトリ内では issue として起票していますが、ここでは内容だけ書きます。

  • Advanced Routing — Astro 7 の src/fetch.tsastro/hono)でリクエストパイプライン(i18n / auth / middleware / pages)を明示的に合成する導入検討
  • Route CachingAstro.cache / routeRules による SSR 応答のキャッシュ方針(scrap 詳細ページなど)
  • Cloudflare CDN Cache ProvidercacheCloudflare() によるエッジキャッシュ委譲(Workers Cache は private beta のため待ち)
  • AI Enhancementsastro dev --background や JSON logging など、エージェント/CI 向けの運用改善
  • Tailwind v4 + @tailwindcss/vite への移行 — 今回は v3 + PostCSS で表示崩れリスクを避けた
  • **zenn-markdown-html の版数整合** — dependencies と patch 対象版数の不一致解消、ブラウザでの脚注描画確認

いずれも「まず Astro 7 で壊さず動かす」ことを優先して、今回のブランチには入れませんでした。

Cloudflare Pages CI でハマった Node.js バージョン

マージ後、Cloudflare Pages のビルドで2段階ハマりました。計画には含まれていませんでしたが、実運用で必須の修正です。

1. Astro 7 の最低要件(>= 22.12.0)

Node.js v20.10.0 is not supported by Astro!
Please upgrade Node.js to a supported version: ">=22.12.0"

.nvmrc20.10.0 のままだったのが原因です。22.12.0 に上げて解消しました。

2. Vite 8 の registerHooks(>= 22.15.0、実運用は 22.16.0 推奨)

The requested module 'node:module' does not provide an export named 'registerHooks'

Astro 7 の起動チェックは >=22.12.0 ですが、Vite 8 が使う node:module.registerHooks は Node 22.15.0 以降で提供されます。22.12.0 では Astro のバージョンチェックは通っても、設定読み込みで落ちます。

最終的に .nvmrc / .node-version22.16.0Cloudflare Pages v3 ビルドイメージのデフォルト)に揃え、package.jsonengines.node>=22.15.0 にしました。

CI で Node を上げるときは「フレームワークの最低要件」と「依存ツールが使う Node API」の両方を見る必要がある、という教訓です。

リスクと留意点(振り返り)

計画時に挙げていた不確実性は、おおむね次のとおりでした。

  • レガシー Content Collections / Astro.glob — ビルドで早期にエラーが出たため、手順4の移行が必須だった
  • **@astrojs/tailwind** — Astro 7 非対応のため PostCSS 構成へ分岐した
  • Sätteri 化 — 実記事は zenn/markdown-it 経由のため影響は限定的。unified 退避は不要だった
  • Node.js バージョン — 計画外だったが、Astro の要件と Vite 8 の隠れ要件の差が CI で顕在化した

まとめ

Astro 7 への移行は「パッケージを上げる」だけでは終わりません。レガシー API の置き換え、Cloudflare アダプタの出力構成変更、Node.js のバージョン要件までセットで見る必要がありました。

記事本文は zenn-markdown-html 経由だったので Markdown パイプライン切替のリスクは小さかった一方、壊れやすいのはビルド・デプロイ・CI の周辺でした。次は Advanced Routing や Tailwind v4 化など、スコープ外にした項目を別ブランチで進めたいと思います。

参考リンク