こんにちはjunです。皆さんはVueやReact、Nuxtなどなどフロントエンドフレームワークやライブラリを使っていますでしょうか?これらのライブラリはフロントの構築が楽になりますが、一度Node.jsを用いてビルドする必要があります。そしてビルドしたファイルを読み込ませることで動作します。
ローカルの環境ではNode.jsがあるので問題ないですが、デプロイする本番環境になくて困ったということはありませんでしょうか?サーバー要件などで本番環境にNode.jsを入れられない、無い場合は予めどこかでビルドして転送する必要があります。
一番手っ取り早いのはローカルでビルドしてFTPなりrsyncすることです。しかし何人もプロジェクトに関わっていたり、属人化したり、環境が統一されていないなど色々とデメリットがあります。
私はLaravel+Nuxt.jsなプロジェクトを開発し、Xserver(レンタルサーバー)に配置したことがあります。Laravelのアセットファイルと、Nuxt.jsはNode.jsでビルドする必要がありますが、レンタルサーバー にはNode.jsがありません。(Xserverには気合で入れらるらしいですが、、パフォーマンスなどの都合でやめました。)
そこで前から気になっていたGithub Actionsを用いてGithub上でビルドを行い、本番環境でrsyncすることでなんとかアセットファイル系がデプロイできました。今回はこの方法について解説します。前提やGithub Actionsの説明から行いますで、さっさと内部コードを知りたい方は「ワークフローを書いていく」へ移動してください。
GitHub Actionsを使用すると、ワールドクラスのCI / CDですべてのソフトウェアワークフローを簡単に自動化できます。 GitHubから直接コードをビルド、テスト、デプロイでき、コードレビュー、ブランチ管理、問題のトリアージを希望どおりに機能させます。(https://github.co.jp/features/actions)
Github ActionsはGithubでビルド、テスト、デプロイ作業ができる環境です。Github上のコードを用いてすぐにビルドなどができます。例えばLaravelのVueファイルをビルドする際にはLaravelのプロジェクトルートで
npm run prod
とnode.jsのコマンドを打って、node.jsを動かしてビルドファイルを作成します。そしてビルド後にはcss,jsディレクトリが作成されます。このビルド操作をGithubの環境でも行えるようにします。
以下のようなymlファイルをGithub上で作成して、ビルド・やりたいコマンドを記述します。
name: Deploy
on: [ workflow_dispatch ]
jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v2
      with:
        ref: release
    - uses: actions/setup-node@v2
      with:
        node-version: '14'
        cache: 'npm'
     - name: public build
      run: |
        npm install
        npm run prod
        rsync -av --delete \
        ./public/css ./public/js ./public/images \
        deploy:$LARAVEL_PATH/public/
        scp ./public/mix-manifest.json deploy:$LARAVEL_PATH/public/mix-manifest.json
      env:
        LARAVEL_PATH : ${{ secrets.LARAVEL_PATH }}
Dokcerファイルやバッチファイルと似た感じです。Node.jsの環境などはGithubが用意してくれます。
結構なんでもいけます。Node.jsはしかり、PHP/Python/Ruby/Java/Power Shellなど色々あります。
パブリックリポジトリの場合は何分使おうが無料です。しかしプライベートの場合は無料枠があり、フリーは毎月2000分までとなっています。(2022年1月時点)
参照:https://github.co.jp/features/actions
私のプロジェクトの場合、大体3分ぐらいで終わりますし、頻繁にビルドしないので基本無料で使えそうです。
今回ビルドするプロジェクトはLaravelとNuxtが連携されたプロジェクトです。
css/,js/に吐き出されるdist/というディレクトリが生成される。.
├── README.md
├── app
├── artisan
├── bootstrap
├── composer.json
├── composer.lock
├── config
├── database
├── nuxt # nuxtはここ
├── package-lock.json
├── package.json
├── phpunit.xml
├── public # ここにLaravelのcss,jsが吐き出される
├── resources
├── routes
├── server.php
├── storage
├── tests
├── tinker_test.php
└── webpack.mix.js
まあとりあえず、Laravelルートでnpm run prodそして、nuxt/にてnpm run generateしてnode.jsを動かす必要があります。
そして生成されてたcss/,js/,images/,mix-manifest.json,dist/ディレクトリ・ファイルを本番サーバーに転送します。
転送の際にはrsyncを使用し、SSHの鍵認証で接続します。またこのアクションは手動で行うようにします。一応、masterブランチにプッシュされた時に自動で実行なんてこともできます。ただし今回はビルドする環境が欲しいだけなので、処理の実行は手動で行います。以上がActionの概要です。
まずSSHやビルドで使用する.envなど環境変数を定義しておきます。Github Actionsでも環境変数を定義できます。
リポジトリのSettingsからSecretsの画面で定義できます。
 
「New repository secret」をクリックしてキー名と値を入力します。
SSH_KEY:SSHに使用する鍵ファイルの中身です。.pem、.keyなどを開いて記述された文字をすべてコピペしてください。
SSH_HOST:接続先のホスト名です。
SSH_PORT:接続先のポートです。
SSH_USER:接続先のユーザーです。
LARAEL_PATH:本番サーバーでのLaravelが置かれている絶対パスです。
GOOGLE_MAP_API_KEY:NuxtでGoogleMapのAPI(フロントに出してもOKなやつ)を使っているので定義。あとで使います。
以上のSSHに関連する値と、.envの記述で必要であれば書いておきます。それではアクションの内容を書いていきましょう。
メニューの「Actions」をクリックしたのち、「New Workflow」をクリックします。

するとworkflowを選択する画面にて、テンプレートをいくつか用意してくれています。

今回はNode.jsのビルドしかないので「Node.js」を選択します。するとある程度の記述が書かれた状態で、yamlが表示されます。
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Node.js CI
on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]
jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [12.x, 14.x, 16.x]
        # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
    steps:
    - uses: actions/checkout@v2
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v2
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'
    - run: npm ci
    - run: npm run build --if-present
    - run: npm test
最初は上記の構成となっています。nameはワークフローの名前になります。わかりやすいものに変えておきましょう。
まず最初にLaravelのアセットファイルを作成してrsyncするとこまで記述します。なお登場する記述に関してはこちらの公式ドキュメントを参考にしてください。ワークフロー構文
最初は実行条件などを変更します。
name: Deploy
on: [ workflow_dispatch ]
on:はこのワークフローの実行条件です。デフォルトではmasterブランチにpushされることが条件ですが、ここではworkflow_dispatchとして、手動で実行できるようにします。
jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v2
      with:
        ref: release
    - uses: actions/setup-node@v2
      with:
        node-version: '14'
        cache: 'npm'
このリポジトリの場合、本番環境は常にreleaseブランチをプルするのでビルド対象ファイルもreleaseのものを使用します。そのため- uses: actions/checkout@v2というブランチのチェックアウトが処理を行う記述をします。
    - uses: actions/checkout@v2
      with:
        ref: release
これでリリースブランチにチェックアウトしてビルドできるようになります。
- uses: actions/setup-node@v2
      with:
        node-version: '14'
        cache: 'npm'
これでNode.js14の環境を使用できるようになり、npmコマンドも使用できるようになります。それではstep:配下に実行するコマンドを書いていきましょう。
実行コードはname:を用いて区分けできます。長いと何がなんだか分からなくなるので、書いておくといいです。
そして実行内容はrun:に記述します。
    - run: npm ci
    - run: npm run build --if-present
    - run: npm test
のように1つづつ書いてもいいですが、ここでは以下のようにまとめて書くことにします。
    - name: ssh key generate
      run: |
        echo "$SSH_KEY" > id_rsa
        mkdir ~/.ssh
        chmod 700 ~/.ssh
そんでは最初にSSHの設定を行います。毎回、鍵のパスとかを記述するのは面倒なのでSSHのconfigファイルを作ってエイリアスで呼べるようにしましょう。
    - name: ssh key generate
      run: |
        echo "$SSH_KEY" > id_rsa
        mkdir ~/.ssh
        chmod 700 ~/.ssh
        mv id_rsa ~/.ssh/
        chmod 600 ~/.ssh/id_rsa
        echo "HOST deploy" >> ~/.ssh/config
        echo "HostName $SSH_HOST" >> ~/.ssh/config
        echo "user $SSH_USER" >> ~/.ssh/config
        echo "Port $SSH_PORT" >> ~/.ssh/config
        echo "IdentityFile $HOME/.ssh/id_rsa" >> ~/.ssh/config
        echo "StrictHostKeyChecking no" >> ~/.ssh/config
        echo "UserKnownHostsFile=/dev/null" >> ~/.ssh/config
        chmod 644 ~/.ssh/config
      env:
        SSH_KEY: ${{ secrets.SSH_KEY }}
        SSH_HOST: ${{ secrets.SSH_HOST }}
        SSH_USER: ${{ secrets.SSH_USER }}
        SSH_PORT: ${{ secrets.SSH_PORT }}
まずenv:にて使用する環境変数をコマンド変数に渡します。こうすることでコマンド内で$SSH_KEYとして値を使用できます。キー、ホスト、ユーザー、ポートを出します。
ちなみに${{ secrets.SSH_KEY }}の記述はコンテキストと呼ばれています。ワークフローに関する情報やsecretsに保存した値にアクセスできます。
echo "$SSH_KEY" > id_rsa
mkdir ~/.ssh
chmod 700 ~/.ssh
mv id_rsa ~/.ssh/
chmod 600 ~/.ssh/id_rsa
まず$SSH_KEYをid_rsaとしてファイルに出力。そして~/.sshディレクトリを作成してその配下に置いておきます。鍵と.sshディレクトリの権限変更を忘れないようにしてください。
echo "HOST deploy" >> ~/.ssh/config
echo "HostName $SSH_HOST" >> ~/.ssh/config
echo "user $SSH_USER" >> ~/.ssh/config
echo "Port $SSH_PORT" >> ~/.ssh/config
echo "IdentityFile $HOME/.ssh/id_rsa" >> ~/.ssh/config
echo "StrictHostKeyChecking no" >> ~/.ssh/config
echo "UserKnownHostsFile=/dev/null" >> ~/.ssh/config
chmod 644 ~/.ssh/config
~/.ssh/configファイルにエイリアスの記述を書いておきます。SSHは初回接続時に警告をだすのでStrictHostKeyChecking noを設定し、known_hostsを出さないようにUserKnownHostsFile=/dev/nullとしておきます。
最後に権限をきちんと設定すれば、deployというSSHエイリアスが使える用意なります。手動で.ssh/configの設定をしていたのを自動化した感じです。
SSHの設定はできたのでLaravelのアセットビルドをするコードを書きます。
- name: public build
  run: |
    npm install
    npm run prod
    rsync -av --delete \
    ./public/css ./public/js ./public/images \
    deploy:$LARAVEL_PATH/public/
    scp ./public/mix-manifest.json deploy:$LARAVEL_PATH/public/mix-manifest.json
  env:
    LARAVEL_PATH : ${{ secrets.LARAVEL_PATH }}
ワークフローの初期位置はリポジトリルートと対応しています。最初にライブラリなどをインストールしてからビルドします。
終わったらrsyncを行います。エイリアスを定義しておいたのでdeploy:$LARAVEL_PATH/public/と簡単な記述で済みます。
次にnuxt/に移動してnuxt.jsのビルド処理を記述します。
    - name: nuxt build
      run: |
        cd nuxt
        touch .env
        echo "GOOGLE_MAP_API_KEY=${{ secrets.GOOGLE_MAP_API_KEY }}" >> .env
        npm install
        npm run generate
        rsync -av --delete ./dist deploy:$LARAVEL_PATH/nuxt/
      env:
        LARAVEL_PATH : ${{ secrets.LARAVEL_PATH }}
プロジェクトによっては.envファイルを使用して端末ごとに環境変数を定義していると思います。.envは大体gitignoreされてバージョン管理されていないので、ワークフローないで作成します。
      run: |
        cd nuxt
        touch .env
        echo "GOOGLE_MAP_API_KEY=${{ secrets.GOOGLE_MAP_API_KEY }}" >> .env
        npm install
        npm run generate
        rsync -av --delete ./dist deploy:$LARAVEL_PATH/nuxt/
      env:
        LARAVEL_PATH : ${{ secrets.LARAVEL_PATH }}
と必要な箇所で.envファイルを作って、echo "GOOGLE_MAP_API_KEY=${{ secrets.GOOGLE_MAP_API_KEY }}" >> .envとGithubの環境変数内容を出力します。これでOKです。
nuxtは静的書出するとdistが作成されるので、rsyncで転送します。
記述が終わったら画面右上の「Start Commit」をおして内容をコミットします。ちなみにワークフローファイルはリポジトリの.github/workflowsというディレクトリが作られ、そこに保存されます。
ではビルドしましょう。Actionsで対象のデプロイ名を選択して「Run Workflow」を押して、対象ブランチを選択して実行します。
 
ビルド中の様子やログは随時確認することができます。ビルドが無事に終了すると以下のように全てチェックマークとなり、処理が終了します。どこかエラーが起きた場合はそこで処理が中止されます。
 
終わったら本番サーバーで対象のファイルが作られているかなどを確認してみましょう。
少人数でこれぐらいであればローカルPCで誰でもできそうですが、ボタンひとつでビルドできるようになることや手違いもなくなるのでとてもおすすめです。色々自動化できるようにGithub Actionsを色々探ってみます。