鳥頭

Next.jsで作ったサイトをGitHub Actionsでビルドして、Netlifyにデプロイする

に投稿

このサイトをVercelからNetlifyへ変更するにあたって、色々ぶち当たった問題を元に作成した記事になります。 ちなみに本当はCloudflareにデプロイしたかったけど、動かなくて諦めたのでNetlifyにしました。

OpenNext

つい先日、OpenNextというものを知り、Next.jsをVercel以外のプラットフォームでも動かせるアダプターがあるのを知りました。 OpenNextの概要ページでも述べられている通り、Next.jsはNode.jsで動かすことはできるものの、Vercelほどのレベルで動作はしません。 OpenNextはそこを助けてくれるもののようです。実際使ってみるとお手軽ですし、結構いい感じに動いてくれます。

サポートされているプラットフォーム

  • AWS
    • 使っていないのでどこまで動くか分かりません
    • SSTと呼ばれるコミュニティがメンテナンスしている
  • Cloudflare
    • まだ開発中ということもあり、試しにこのサイトをデプロイしようとしてみましたが、一部のページが動きませんでした
    • Cloudflareのチームによってメンテナンスされています
  • Netlify
    • Next.js v15対応済みで、このサイトをデプロイしても問題なく動きました
    • Netlifyのチームによってメンテナンスされています

ということで、Netlifyを採用してみました。

ここから本題

OpenNextも軽く紹介したので、さっそく本題に入ります。

pnpmを使用している場合

OpenNextのドキュメントによると、Next.jsとpnpmを使用している場合はshamefully-hoistオプションを有効にしなければならないようです。

.npmrcファイルをプロジェクトルート上に配置して、public-hoist-pattern[]=*またはshamefully-hoist=trueを追記します。 その後は、pnpm installをしてpnpm-lock.yamlnode_modulesをアップデートしておきましょう。

Vercelでしか使えない機能は削除する

当然ですが、Vercel AnalyticsやVercel Speed Insightsは使用できませんので、削除しましょう。 Vercel CLIも不要なので削除します。

GitHub Actionsを使わない場合はここまで

GitHub Actionsなんて使わないという方は、後はNetlifyに丸投げするだけで、勝手にNext.jsで作られたアプリケーションをNetlifyで使える形にビルドして、デプロイまでやってくれます。 後は動かない箇所をNetlifyで動かせるように修正して、再度Netlifyに投げておしまいです。お疲れ様でした。

GitHub Actionsを使う場合

なぜGitHub Actionsを使うのか先に理由を述べておきます。

  • 公開リポジトリならGitHub Actionsを無制限に使える
    • プライベートリポジトリでも、月2000分は動かせる
      • Proプラン加入なら月3000分は使える(Netlifyの10倍)
  • Netlifyは月300分しかビルドできない
    • 開発が活発だとちょっと厳しいし、メインブランチ以外のブランチやPRごとにビルドしてプレビューURLを発行しているとあっという間に消費する

最初はnwtgck/actions-netlifyを使おうとした

ありがたいことに、Netlifyへのデプロイをビルド時間0で行うためのGitHub Actionsという記事を書いた方がnwtgck/actions-netlifyというこのようなワークフローを書くだけで、お手軽にデプロイできるものを公開していましたので、最初はこれを使うつもりでした。

結論から言うと無理でした。色々試してみたものの、2年前のIssueについたコメントにもある通り、このActionに適したものを作るのは厳しいようです。

Netlify CLIを使う

ローカルビルドに対応しているので、これを使う以外に手段はなさそうです。

加えて、.gitignore.netlify/を追加することをオススメします。

インストールするパッケージ

  • Netlify用のNext.jsランタイムを生成するのに、@netlify/plugin-nextjsが必要
  • netlify-cliが必要
$ pnpm i -D @netlify/plugin-nextjs netlify-cli

netlify.tomlを作成

  1. @netlify/plugin-nextjsを使う設定を入れる
  2. build.commandpnpm buildを設定 (next buildするものならなんでも)

そして、以下の内容でnetlify.tomlを作成します。

[build]
command = "pnpm build"

[[plugins]]
package = "@netlify/plugin-nextjs"

これで準備は完了です。

試しにデプロイする

ここまで正しく設定し、ソースコードに誤りもなければ正常にデプロイが完了するはずです。

pnpm netlify deploy --build

次はいよいよワークフローを書いていきます。

ワークフローを作成する

nwtgck/actions-netlifyに近いことがしたいので、以下のような仕組みでワークフローを組んでいきます。

  • ブランチごとにpushイベントが起きたら、プレビューを作成する
    • コミットに対して、コメントでプレビューURLを伝える
  • プルリクエストでopened, synchronize, reopenedいずれかのイベントが起きたらプレビューを作成する
    • コメントでプレビューURLを伝える
  • mainブランチにプッシュされたら本番環境にデプロイする
    • コミットに対して、コメントでプレビューURLを伝える
name: Netlify

on:
  push:
  pull_request:
    types: [opened, synchronize, reopened]

concurrency:
  cancel-in-progress: true
  group: ${{ github.workflow }}-${{ github.ref }}

jobs:
  # プレビュー用
  deploy-preview:
    name: Preview
    runs-on: ubuntu-latest
    if: github.ref != 'refs/heads/main'
    permissions:
      contents: write
      pull-requests: write
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v4

      - uses: actions/setup-node@v4
        with:
          cache: pnpm

      - name: Install dependencies
        run: pnpm install

      - name: Deploy to preview
        id: deploy-result
        # 参考: https://github.com/netlify/actions/blob/3eff4d5cd9bf9f7ba528c1f1bbb94a37c3a3201d/cli/entrypoint.sh
        run: |
          OUTPUT=$(sh -c "echo && pnpm netlify deploy --build")
          NETLIFY_URL=$(echo "$OUTPUT" | grep -Eo '(http|https)://[a-zA-Z0-9./?=_-]*(--)[a-zA-Z0-9./?=_-]*')

          echo "url=$NETLIFY_URL" >> $GITHUB_OUTPUT
          echo "$OUTPUT"
        env:
          NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
          NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}

      - name: Comment to commit
        uses: actions/github-script@v7
        if: github.event_name == 'push'
        with:
          script: |
            const netlifyUrl = process.env.NETLIFY_URL;

            await github.rest.repos.createCommitComment({
              ...context.repo,
              commit_sha: context.sha,
              body: `Deployed to ${netlifyUrl}`,
            });
        env:
          NETLIFY_URL: ${{ steps.deploy-result.outputs.url }}

      - name: Comment to Pull Request
        if: github.event_name == 'pull_request'
        uses: thollander/actions-comment-pull-request@v3
        with:
          comment-tag: netlify-deploy-preview
          mode: recreate
          message: Deployed to ${{ steps.deploy-result.outputs.url }}

  # 本番環境用
  deploy-prod:
    name: Production
    runs-on: ubuntu-latest
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v4

      - uses: actions/setup-node@v4
        with:
          cache: pnpm

      - name: Install dependencies
        run: pnpm install

      - name: Deploy to production
        id: deploy-result
        # 参考: https://github.com/netlify/actions/blob/3eff4d5cd9bf9f7ba528c1f1bbb94a37c3a3201d/cli/entrypoint.sh
        run: |
          OUTPUT=$(sh -c "echo && pnpm netlify deploy --prod --build")
          NETLIFY_URL=$(echo "$OUTPUT" | grep 'Website URL:' | grep -Eo '(http|https)://[a-zA-Z0-9./?=_-]*')

          echo "url=$NETLIFY_URL" >> $GITHUB_OUTPUT
          echo "$OUTPUT"
        env:
          NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
          NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}

      - name: Comment to commit
        uses: actions/github-script@v7
        with:
          script: |
            const netlifyUrl = process.env.NETLIFY_URL;

            await github.rest.repos.createCommitComment({
              ...context.repo,
              commit_sha: context.sha,
              body: `Deployed to ${netlifyUrl}`,
            });
        env:
          NETLIFY_URL: ${{ steps.deploy-result.outputs.url }}

NetlifyのアクセストークンとサイトID

ワークフローの内容を見てもらうと分かる通り、Netlify CLIを実行する際にNETLIFY_AUTH_TOKENNETLIFY_SITE_IDを渡していますが、この2つがデプロイの際に必要になります。

シークレットの作成方法は https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-an-environment に従ってください。

おわり

ワークフローを動かしてみると、何も問題がなければうまく動くはずです。inkohx.dev用に書いたものを記事用に少し加工したものなので、もし動かなかったらそれを参考にするか、私に教えてください。

実際に動かすとこんな感じでコミットにコメントして、プルリクエストが作成されるとコメントを使って通知もしてくれます。