こんにちは。最近CIの素晴らしさを実感し始めたkitakkunです。
今回は、Next.jsで作ったウェブサイトの更新フローをGitHub Actionsを活用して自動化してみたのですが、まあまあ詰まってしまったので記事にまとめてみました。
前提事項
- サーバー環境: さくらレンタルサーバー(FTP)
やりたいこと
以下のワークフローをGitHub Actionsで自動化していきます。
- Next.jsプロジェクトをビルド・エラーチェック
- Next.jsサイトを静的サイトとして書き出し(静的書き出しは事前に設定済みとします)
- 静的サイトデータをFTPでレンタルサーバーにアップロード
GitHub Actionsを書く
僕自身も感覚で実際に書いて理解したので、細かくは説明しません。とりあえず冒頭部はこんな感じになります。
name: "Deploy Next.js App"
on:
push:
branches: [ develop ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
# 以下に続く・・(今は省略)
developブランチへプッシュされると、最新版のUbuntu上でsteps
以下に定義されるタスクが順番に走っていきます。
リポジトリのチェックアウト
Actionが走るブランチに切り替えます。actions/*
はすでに提供されているタスクのショートカットみたいなものだと思います。
name: Checkout repository
uses: actions/checkout@v2
Node.jsのセットアップ
Node.jsに関しても使えるactionがあるらしいのでそちらを活用します。バージョンはご自身のプロジェクトに合わせて変更してください。
name: Setup Node.js
uses: actions/setupj-node@v2
with:
node-version: 18.x
依存npmパッケージのインストール
package.json
に定義されている依存モジュールをCI環境にインストールします。細かいことはよくわかりませんが、npm install
ではなくnpm ci
を使ったほうがpackage-lock.json
との整合性などにもチェックが入ってより安全なようです。
name: Install NPM packages
run: npm ci
Next.jsプロジェクトのビルド
ビルドを走らせます。プログラムにエラーとかあったらここで落ちます。
name: Build Next.js App
run: npm run build
Next.jsプロジェクトのエクスポート
アップロード用にファイルをエクスポートします。
name: Export Next.js App
run: npm run export
create-next-app
したときにexportコマンドは定義されていないと思うので、
package.json
に追記しましょう。
"scripts": {
"dev": "next dev",
"build": "next build && next export",
"start": "next start",
"export": "next export",
"lint": "next lint"
}
追記しないと落ちます。
他のブログ記事とかにexport
のステップが書いてあったので、とりあえず入れてるけど、本当に必要なのかは疑問。ログ見た感じbuild
の時点でout
に出力はされていたので、いらないような気もする。
FTPサーバーへのアップロード(ここで超躓いた)
この時点でアップロード用のファイルが、out
に入っています。これを、ウェブサイトをホストするFTPサーバーに移動します。
name: FTP Deploy
uses: SamKirkland/FTP-Deploy-Action@4.3.0
with:
server: ${{ secrets.FTP_SERVER }}
username: ${{ secrets.FTP_USERNAME }}
password: ${{ secrets.FTP_PASSWORD }}
local-dir: ./out/
server-dir: /home/${{ secrets.FTP_USERNAME }}/www/
protocol: ftps
パット見て何やってるかよくわからないと思うので要所要所で分けて説明します。
使っているActionについて
FTP接続・アップロードのタスクに関しては、こちらのアクションを使わせてもらっています。
各パラメータの補足
詳しいことはリポジトリのページを見てほしいのですが、サンプルで指定しているパラメータに関する説明は以下のとおりです。
-
server
: サーバーのドメイン・URL -
username
: 認証用ユーザ名 -
password
: 認証用パスワード -
local-dir
: リモートに反映させたいディレクトリのパス(反映元) -
server-dir
: サーバー上の対応するディレクトリのパス(反映先) -
protocol
: 接続プロトコル(デフォルトはftp
)
secrets
って何?
GitHub Actionsのファイル内に認証情報を書いても動くことには動きますが、公開すべきデータではないので隠蔽するべきです。
Secretsのパラメータとして定義してやれば、具体的な中身を隠したまま認証情報の記述ができます。
Secretsはリポジトリの「Settings -> Secrets -> Actions」から設定できます(詳細は割愛)。
例えば、
${{ secrets.FTP_SERVER }}
はCI実行時自動的にSecretsに定義されたFTP_SERVER
の値に置き換わります。
躓きポイント1: 海外IPからのサーバーアクセス
「さくらレンタルサーバー」は、(おそらくデフォルトで)海外IPからのアクセスを遮断する設定になっています。GitHub Actionsは海外サーバーから実行されますから、海外IPからのアクセスを許可しないと、FTPアクセスを拒絶されてワークフローが落ちます。
躓きポイント2: server-dir
に指定するパス
これもおそらく「さくらレンタルサーバー」特有の問題だと思うのですが、server-dir
のパスには/home/[ユーザー名]/www/
を指定しないと、更新処理時にディレクトリ削除が発生したときno such file or directory
のエラーが出てCIが落ちました。
ターミナルからサーバー接続したとき、/home/[ユーザー名]/
のパスにつながっていたので、./www/
(相対パス)で問題ないと思っていたのですが、2回目の差分アップロード時に失敗するという事象が起きてしまいました。
こちらの記事でも以下のように説明されているので、ここはレンタルサーバーによって指定の仕方に注意が必要かもしれません。
- ロリポップ→ホスト名の後にWebサイトデータの存在するディレクトリ(フォルダ)名
(例:●●.lolipop.jp/サイトのフォルダ名)- エックスサーバ→ドメイン名の後に/public_htm/ Webサイトデータの存在するディレクトリ名
- さくらインターネット→/home/アカウント名/www/ Webサイトデータの存在するディレクトリ名
出典:GitHub Actionsを使ってFTP自動デプロイ(Webサイト公開)を実現!ソフトを使った手動アップロードを卒業する
なお、./www/
で指定しても、1回目だけはちゃんと動作するので、原因特定にとても時間がかかりました。同じようなことで躓く人が減ってほしい。
完成したymlコードの全体像
完成したGitHub Actionsのコードはこんな感じです。developが更新されたとき、自動でサーバーの内容に変更が反映されるので、とても便利です。
name: Deploy Next.js App
on:
push:
branches: [ develop ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 18.x
- name: Install NPM packages
run: npm ci
- name: Build Next.js App
run: npm run build
- name: Export Next.js app
run: npm run export
- name: FTP Deploy
uses: SamKirkland/FTP-Deploy-Action@4.3.0
with:
server: ${{ secrets.FTP_SERVER }}
username: ${{ secrets.FTP_USERNAME }}
password: ${{ secrets.FTP_PASSWORD }}
local-dir: ./out/
server-dir: /home/${{ secrets.FTP_USERNAME }}/www/
protocol: ftps