Vercelで環境変数を変えて複数デプロイする方法

# 前提
- github
  - リポジトリはprivate
  - main ブランチ = master ブランチ
- vercel
- next.jsは使っていてもいなくてもいい

# やりたいこと
例えば、github上でmainへのPRをマージしたときに、vercel上では **そのコードで** 環境変数とドメインだけが異なる`本番環境`,`デモ環境`,`開発環境`の3つの環境がデプロイされるようにしたい

- 本番環境(prod)
  - 本番用の環境変数を使い、実際に本番APIと疎通する
  - ドメインは a.com とする
- デモ環境(demo)
  - デモ環境用の環境変数を使い、デモ環境用APIと疎通する
  - ドメインは a.demo.com とする
  - to Bの顧客が試す環境というイメージ
- 開発環境(dev)
  - 開発用の環境変数を使い、開発環境用APIと疎通する環境
  - ドメインは a.dev.com とする
  - 開発者が試す環境

AWS Amplifyは1ブランチに対して環境変数などを変えた複数ビルドを構築することができる
しかしVercelは `production` `preview` `develop` deployの概念が思想としてあり、ビルドがどのdeploy typeになるかルールがあるので、単純にはそのようなことができない

解決までの流れとしては、 

1. `main` と自動で同期される `demo`, `dev` ブランチをつくる
2. demo, devブランチとドメインを紐付ける
3. 環境変数を変えたビルドを作る
4. vercelのドメインURLがproduction deployになっているので、demo, devが検索にヒットしうる問題を解決する

となる

# 1 ブランチ同期
## 1.1 方法
https://github.com/marketplace/actions/github-repo-sync

を使う
sshとhttpsどちらでもcloneできるが、楽なhttpsを使う

```yaml:.github/workflows/sync_branches.yml
# @see https://github.com/marketplace/actions/github-repo-sync

name: Sync branches

on:
  push:
    branches:
      - main

jobs:
  sync_branches:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
      with:
        persist-credentials: false
    - name: "force push main to demo"
      uses: repo-sync/github-sync@v2
      with:
        source_repo: https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/[organization name]/[repository name].git
        source_branch: "main"
        destination_branch: "demo"
        github_token: ${{ secrets.GITHUB_TOKEN }}
    - name: "force push main to dev"
      uses: repo-sync/github-sync@v2
      with:
        source_repo: https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/[organization name]/[repository name].git
        source_branch: "main"
        destination_branch: "dev"
        github_token: ${{ secrets.GITHUB_TOKEN }}

```

`[organization name]/[repository name]` のところは適宜置き換える

### 1.1.1 github token をURLに入れても安全なのか

https://docs.github.com/en/actions/reference/authentication-in-a-workflow

には

> Before each job begins, GitHub fetches an installation access token for the job. The token expires when the job is finished.

とあるので大丈夫そう

## 1.2 protected branchにpushしたい場合
GitHubの`branch protection` に設定しているとGitHub Actionsもpush不能になる

### 1.2.1 回避方法1: Personal access tokenを使う

https://github.community/t/allowing-github-actions-bot-to-push-to-protected-branch/16536

によると

```
    - uses: actions/checkout@v2
      with:
        persist-credentials: false
```

にしておき、 `github.token` ではなく personal access token を使えばよいらしい
デプロイに個人のpersonal access tokenを使うのは退職などを考えるとアンチパターンなので、CI用に共有するbotユーザーアカウントを作って使うとよさそう
（試してはいない）

### 1.2.2 回避方法2: git hooks の pre-pushで防ぐ
GitHubが公式に誤爆push防止できるようになったことで廃れた印象があるが、今回はGitHubに頼らない手段として利用できる

https://www.granfairs.com/blog/cto/prevent-pushing-master

「人間が開発するときにだけpre-pushが入っており誤爆pushを防げて、GitHub Actionsはpushできる」という状態にしたい
これはpackage.jsonのpostinstallで生成すれば雑だが達成できる

### 1.2.3 その他の回避方法
https://github.com/marketplace/actions/git-sync-action でもできるかも
https://github.com/marketplace/actions/sync-branches はPRを自動生成してくれるaction。PRで同期したい派なら使える
[Vercel CLI](https://vercel.com/docs/cli) でやるのは茨の道





# 2 Custom Domainの設定
https://vercel.com/docs/custom-domains

にあるように設定すると、demoブランチに対して `a.demo.com` のようなURLでアクセスできるようにビルドが紐づく



# 3 環境変数の異なるビルドを作る
https://vercel.com/support/articles/set-up-a-staging-environment-on-vercel

が公式の説明
`process.env.VERCEL_GIT_COMMIT_REF` を使う

next.jsを使っている場合はnext.config.jsのenvを経由して切り替えて渡す必要がある

また、余談として

https://nextjs.org/docs/basic-features/environment-variables#default-environment-variables

の `.env.production` `.env.development` は性質が違うので使えない

# 4 Custom Domainを検索避けしなければいけない場合の対策
Custom Domainを設定すると、

https://vercel.com/docs/platform/deployments#production

> The production domain(s) are defined from the Domains tab of a Project on the Vercel Dashboard.
> To add a production domain to a Project, visit Domains from the Project > Overview page, you can read more about this in the custom domains documentation.

とあるように、 **production deploy扱い** となる

productionとpreviewの違いはレスポンスヘッダーに[ `X-Robots-Tag: no-index` がついて検索避けされるかどうか](https://vercel.com/support/articles/are-vercel-preview-deployment-indexed-by-search-engines) である
Domain設定したvercelのビルド結果を参照できる3つのURLを見ると実際に以下の違いがあった

- `a.demo.com` みたいに自分でdomain設定したURL
    - `x-robots-tag: no-index` なし
- `リポジトリ名-git-demo-団体名.vercel.app` みたいなvercelが勝手に作るURL
    - `x-robots-tag: no-index` あり
- `リポジトリ名-ハッシュ値-団体名.vercel.app` みたいなvercelが勝手に作るURL
    - `x-robots-tag: no-index` あり

大まかに考えて、回避策としては 

- main 起因のビルドでないなら `x-robots-tag: no-index`をつける
- main 起因のビルドでないなら `<meta name="robots" content="noindex, nofollow">` をつける
- DNS設定でpreview buildのdomainに向ける

が考えられる

## 4.1 next.jsを使っている場合の回避方法
```js:next.config.js
  async headers() {
    return process.env.VERCEL_GIT_COMMIT_REF === 'main'
      ? []
      : [
          {
            source: '/',
            headers: [
              {
                key: 'X-Robots-Tag',
                value: 'no-index',
              },
            ],
          },
          {
            source: '/:any*',
            headers: [
              {
                key: 'X-Robots-Tag',
                value: 'no-index',
              },
            ],
          },
        ];
  },
```

こういうのを書くとうまく設定された

## 4.2 DNS設定だけでなんとかする場合（失敗）
https://vercel.com/docs/custom-domains#dns-records
やRoute53などの各々が利用しているところで

```
a.demo.com
CNAME リポジトリ名-git-demo-団体名.vercel.app
```

みたいなのを設定すればCustom Domainの設定も要らず、Custom Domainがproduction deployになる問題も楽に回避できるかと思ったが、何故か以下のような画面になって失敗した

![スクリーンショット 2021-03-22 13.23.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50442/d6d129d9-3adc-78d3-63ca-1348b25afaf1.png)

# 感想
- vercelにCustom Domainにもpreview modeを指定する設定がないのが不便に感じる
  - 要望出そうかな→feedbackに書いて送ってみた
- vercelのpassword protectionの柔軟性がないこと等も含めてどうしてもうまく運用にマッチしない場合は、amplifyとか別のサービスをdev,demo作成に使うのが良いと思う
  - serverless functionに依存していたら使えない


# 参考資料
- vercelで使える環境変数
  - https://vercel.com/docs/environment-variables#system-environment-variables
- vercelの定義する環境概念
  - https://vercel.com/docs/environment-variables#environments
  - https://vercel.com/docs/platform/deployments#deployment-types
- github actionsで使える環境変数
  - https://docs.github.com/ja/actions/reference/context-and-expression-syntax-for-github-actions