独自フレームワークをVercel for Gitに対応する

# はじめに
[Vercel for Git](https://vercel.com/docs/concepts/git)を使うとpull request発行時にプレビュー環境へのデプロイを行ったりmainへのマージに連動した本番デプロイが出来ます。Next.jsのような有名なフレームワークであったり、標準的な手順でビルド・デプロイが可能なものであればサクッとレポジトリをインポートしてやれば使用可能になるこの便利機能ですが、そうすんなり行かないプロジェクトでこの機能を使うための対応作業を行ったので本記事で内容を紹介します。

# 対応内容
今回はruby.wasmを使ってSPAを構築するパッケージ"[Bormaŝino](https://github.com/keyasuda/bormashino)"を使用した[プロジェクト](https://github.com/keyasuda/bormashino-app-template)をVercel for Gitに対応させました。
このプロジェクトをビルドするには以下の環境が必要となります。
- rbenvが導入済みである、もしくはruby 3.2.0-preview1以降がインストール済みである
- npmが導入済みである

上記の環境にて以下のコマンドを投入すると`dist/`以下に成果物が生成されます。
```bash
cat .ruby-version | rbenv install -
npm i
bundle
bundle exec rake bormashino:download
(cd src && bundle)
npm run build
```

## ビルド環境を理解する
Vercel for Gitのインテグレーションにてトリガーされたビルドは以下の環境で実行されます。
- [Amazon Linux 2](https://vercel.com/docs/concepts/deployments/troubleshoot-a-build#build-image)
- [Dockerは使用できない](https://vercel.com/guides/does-vercel-support-docker-deployments)

この環境上にて以下のような流れで処理が行われます。
1. プロジェクトがgit cloneされる
1. INSTALL COMMANDが実行される(Vercel上Project Settingsの該当項目、もしくはvercel.jsonの`installCommand`)
1. BUILD COMMANDが実行される(Vercel上Project Settingsの該当項目、もしくはvercel.jsonの`buildCommand`)
1. OUTPUT DIRECTORY中の生成物がデプロイされる(Vercel上Project Settingsの該当項目、もしくはvercel.jsonの`outputDirectory`)

## ローカルにて再現
[トラブルシューティングガイド](https://vercel.com/docs/concepts/deployments/troubleshoot-a-build#build-image)に記載のとおり、

```bash
docker run --rm -it amazonlinux:2.0.20191217.0 sh
```
で類似環境を起動できます。
今回のプロジェクトではこのコンテナ中で以下のコマンドを投入するとビルド可能になります。
### 必要なパッケージをインストール
```bash
yum install -y gcc openssl-devel readline-devel zlib-devel make tar libyaml-devel xz curl
```
### rbenv&ruby-buildをインストール
```bash
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
(cd ~/.rbenv && src/configure && make -C src)
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
~/.rbenv/bin/rbenv init
echo 'eval "$(rbenv init - bash)"' >> ~/.bash_profile
source ~/.bash_profile

mkdir -p "$(rbenv root)"/plugins
git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build
```

### プロジェクトをビルド
```bash
cat .ruby-version | rbenv install -
npm i
bundle
bundle exec rake bormashino:download
(cd src && bundle)
npm run build
```

## Vercel for Gitで使えるように実装する
ローカルのコンテナで検証したビルド手順をスクリプトに落とし込みます。
```bash:bin/vercel-git
#!/usr/bin/bash

# rbenv install
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
(cd ~/.rbenv && src/configure && make -C src)
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
~/.rbenv/bin/rbenv init
echo 'eval "$(rbenv init - bash)"' >> ~/.bash_profile
source ~/.bash_profile

mkdir -p "$(rbenv root)"/plugins
git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build

# required ruby install
rbenv rehash
cat .ruby-version | rbenv install -

# project build
npm i
bundle
bundle exec rake bormashino:download
(cd src && bundle)
npm run build
```
各コマンド・ディレクトリ指定を設定ファイルで定義します。
```json:vercel.json
{
  "buildCommand": "bin/vercel-git",
  "outputDirectory": "dist",
  "installCommand": "yum install -y gcc openssl-devel readline-devel zlib-devel make tar libyaml-devel xz curl"
}
```
予めレポジトリをインポートしておきこれらのファイルを追加してPRを発行してしばらく待つと、Vercelがそのブランチをプレビュー用にデプロイした旨のコメントがPRに付与されます。
![Screenshot from 2022-09-18 20-29-29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2650925/1e0ea368-35a1-d830-dff9-3d878cce310f.png)


## 高速化
ここまでで今回のプロジェクトはVercel for Gitでデプロイ出来るようになりましたが、一回のデプロイに5分強掛かっていました。大半はruby-buildにてrubyをビルドする時間に費やされています。
この問題を解消するためには予めビルドされたrubyを用意すると効果的でしょう。それを送り込むための手法としては、
- rpmパッケージを用意する
- make済み(make installでインストール可能な)なtarballを用意する
- ruby-build後の`~/.rbenv/versions`をtarballとして用意する

と言ったものが考えられます。今回は3番目の手法を取りました。

https://github.com/keyasuda/bormashino-vercel-for-git-prebuilt-ruby
このレポジトリのactionsで必要なrubyをビルドし、`~/.rbenv/versions`ディレクトリをtarballに固めてGitHub Releasesへ投入するようにしました。

このtarballをビルドスクリプト中でダウンロード・展開するように改修すると5分掛かっていたデプロイが1分程度に短縮されました。

# おわりに
- Amazon Linux 2で走るビルドスクリプトを実装
- `installCommand`, `buildCommand`, `outputDirectory`を定義

これらの手順を踏むことで独自フレームワークを使用したプロジェクトをVercel for Gitに対応させられました。