tech · · siraken · 9 min read

Astro v6 移行後に Cloudflare Workers のデプロイ先がおかしくなった話

どうも白澤です。ここ最近ブログを書けていて良い感じです。

Astro v6 が 3 月 10 日にリリースされました。Vite の Environment API による開発体験の刷新や、Built-in Fonts API、Live Content Collections など盛りだくさん。@astrojs/cloudflare アダプターも v13 に上がり、開発・ビルド・本番すべてで workerd ランタイムが動くようになったのも大きな変更点です。Astro チームが Cloudflare に買収されたこともあって、Cloudflare Workers への対応がかなり強化されてる感じしますね。

このブログも早速 v6 にアップグレードしたのですが、その結果 Cloudflare Workers へのデプロイが意図しない挙動になる、という状況に遭遇したのでその記録を残しておきます。

何が起きたか

Novalumo ではこのブログを含め、クライアントのサイトも Astro + Cloudflare Workers で実装することがほとんどで、普通に Web サイトを作成するだけなのに Next.js にすると運用面で比較的重い(機能が過剰、フレームワーク特有のコスト等)ということもあって、結構今はこの構成が鉄板になっています。GitHub Actions で main に push すると staging と production の 2 環境に並行デプロイされる構成で、Wrangler の設定(wrangler.jsonc)で env セクションを使って環境ごとのワーカー名やバインディングを分けています。

{
  "name": "blog-hogehoge",
  // ...
  "env": {
    "production": {
      "name": "blog-hogehoge-production",
      // ...
    },
    "staging": {
      "name": "blog-hogehoge-staging",
      // ...
    },
  },
}

CI ではこんな感じでそれぞれにデプロイしていました。

wrangler deploy --env=staging
wrangler deploy --env=production

Astro v5 の頃はこれで問題なく動いていたのですが、v6 に上げた後からデプロイが staging にも production にも飛ばず、ルート名の blog-hogehoge という単一のワーカーにしかデプロイされなくなっていました。

CI 自体は成功しているし、最初はデプロイしても変更が反映されないのでおかしいなと。多分 Astro 側というより Cloudflare 側に関連する何かしらの仕様変更があったのかなと思って調査を始めました。

CI ログを確認する

デプロイジョブのログを確認すると、こんな感じになっていました。

deploy (staging):   Uploaded blog-hogehoge (14.21 sec)
deploy (staging):   Deployed blog-hogehoge triggers (4.07 sec)

deploy (production): Uploaded blog-hogehoge (6.51 sec)
deploy (production): Deployed blog-hogehoge triggers (1.18 sec)

本来なら blog-hogehoge-stagingblog-hogehoge-production にそれぞれデプロイされるべきなのに、両方とも blog-hogehoge になっています。--env=staging を指定しているのに完全に無視されている。

で、ログをもう少し上の方まで見ていくと、気になる出力がありました。

Using redirected Wrangler configuration.
 - Configuration being used: "dist/server/wrangler.json"
 - Original user's configuration: "wrangler.jsonc"
 - Deploy configuration file: ".wrangler/deploy/config.json"

Wrangler が wrangler.jsonc ではなく dist/server/wrangler.json を使っている。見つけた時「絶対これじゃん」ってなりましたが、結論としてはやはりこれが原因でした。

何が変わったのか

Astro v6 対応の @astrojs/cloudflare v13 は、内部的に @cloudflare/vite-plugin を使うようになりました。今回の問題の本質はここです。 このプラグインはビルド時に dist/server/wrangler.json というフラット化された Wrangler 設定ファイルを生成して、.wrangler/deploy/config.json でそのファイルにリダイレクトする仕組みになっています。

で、この生成された dist/server/wrangler.json の中身を見てみると、こうなっていました。

{
  "name": "blog-hogehoge",
  "main": "entry.mjs",
  "r2_buckets": [],
  "definedEnvironments": ["production", "staging"],
  ...
}

env セクションが丸ごと消えています。name はトップレベルの blog-hogehoge のまま、r2_buckets も空。definedEnvironments というメタデータには “production” と “staging” が残っているけど、実際の環境設定は何もない。

つまり wrangler deploy --env=staging を実行しても、参照先の設定ファイルに env がないので、トップレベルの blog-hogehoge にフォールバックしてしまう、というわけです。

Cloudflare Vite Plugin の仕様

これは @astrojs/cloudflare v13 から内部で使うようになった Cloudflare Vite Plugin の仕様で、公式ドキュメントにも明記されています。

As Cloudflare environments are applied at dev and build time, specifying CLOUDFLARE_ENV when running vite preview or wrangler deploy will have no effect.

つまり環境の選択はビルド時に完結していて、デプロイ時にはもう変更できないということです。--env を渡しても、参照先のフラット化された設定に env セクションが存在しないので意味がありません。CLOUDFLARE_ENV=production を指定してビルドすると、wrangler.jsoncenv.production セクションがトップレベルにマージ・フラット化された dist/server/wrangler.json が生成されるので、あとは wrangler deploy--env なし)でそのまま正しいワーカーにデプロイされます。

wrangler.jsonc 自体は env セクションも含めてそのまま維持して大丈夫で、ビルド時にアダプターが CLOUDFLARE_ENV をもとに適切にフラット化してくれます。

CI の修正

行った修正は結構シンプルで、GitHub Actions のワークフローを 2 箇所変えるだけでした。

修正前:

- name: Build
  run: pnpm run build

- name: Deploy to ${{ matrix.environment }}
  uses: cloudflare/wrangler-action@...
  with:
    apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
    accountId: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
    command: deploy --env=${{ matrix.environment }}

修正後:

- name: Build
  run: pnpm run build
  env:
    CLOUDFLARE_ENV: ${{ matrix.environment }}

- name: Deploy to ${{ matrix.environment }}
  uses: cloudflare/wrangler-action@...
  with:
    apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
    accountId: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
    command: deploy

Build ステップに CLOUDFLARE_ENV を追加して、Deploy ステップから --env を削除。これだけです。

修正後の確認

修正を push して CI が回った結果がこちら。

deploy (staging):   Uploaded blog-hogehoge-staging (11.21 sec)
deploy (production): Uploaded blog-hogehoge-production (14.65 sec)

ちゃんと blog-hogehoge-stagingblog-hogehoge-production にそれぞれデプロイされるようになりました。R2 バインディングや routes の設定も正しく反映されています。

ちなみにルート名で誤って作成されていた blog-hogehoge ワーカーは wrangler delete --name blog-hogehoge で削除しておきました。

まとめ

ということで、Astro v6(@astrojs/cloudflare v13)に移行した際に Wrangler の環境デプロイが壊れた話でした。

原因をまとめると、@astrojs/cloudflare v13 が内部で使うようになった @cloudflare/vite-plugin がビルド時に Wrangler 設定をフラット化して env セクションを除去するため、wrangler deploy --env が効かなくなった、というものです。対処法は CLOUDFLARE_ENV をビルド時に指定する方式に切り替えるだけ。

CI が成功しているのにデプロイ先が間違っている、というのが結構厄介で気づきにくいパターンでした。Astro v6 に移行して Wrangler の env を使っている人は一度デプロイログを確認してみると良いかもしれません。