はじめに
Turso + Drizzle のマイグレーション運用環境を構築する過程で、Vercel CLI を使用したデプロイ時に環境変数が適用されないという問題に遭遇しました。本記事では、この問題の根本原因と解決策を詳しく解説します。
構成の概要
目標
issue-*ブランチの自動デプロイを無効化- GitHub Actions 経由で DB 作成 → マイグレーション → 環境変数設定 → デプロイの順序を制御
- ブランチごとに異なる Turso データベースに接続
採用したアプローチ
遭遇した問題
エラー内容
デプロイは成功するが、ランタイムで以下のエラーが発生:
❌ Invalid environment variables: [
{
expected: 'string',
code: 'invalid_type',
path: [ 'TURSO_DATABASE_URL' ],
message: 'Invalid input: expected string, received undefined'
}
]調査結果
- Vercel の環境変数は設定されていた
$ vercel env ls
TURSO_DATABASE_URL Encrypted Preview (issue-27) 30m ago- ブランチ固有の環境変数も正しく取得できた
$ vercel env pull --environment=preview --git-branch=issue-27 .env.local
TURSO_DATABASE_URL="libsql://linto-issue-27-naokiyazawa.aws-ap-northeast-1.turso.io"- デプロイメントにはブランチ情報がなかった
$ vercel inspect https://linto-xxx.vercel.app
target preview
# ブランチ情報が表示されない根本原因
原因 1: vercel.json の読み込みタイミング
vercel.json の git.deploymentEnabled 設定は、Vercel プロジェクトのデフォルトブランチ(main)から読み込まれます。
{
"git": {
"deploymentEnabled": {
"main": true,
"develop": true,
"issue-*": false
}
}
}この設定は issue-27 ブランチにあるだけでは機能せず、main または develop にマージされるまで Vercel に認識されません。
原因 2: Vercel CLI デプロイとブランチ固有環境変数
Vercel 公式ドキュメントによると、環境変数の gitBranch パラメータは特定のブランチに紐づけるためのものです:
{
"key": "TURSO_DATABASE_URL",
"value": "libsql://...",
"target": ["preview"],
"gitBranch": "issue-27"
}しかし、この仕組みは Git 連携(自動デプロイ)経由のデプロイにのみ適用されることが判明しました。
vercel deploy --prebuilt は Git 連携を使用しないため、gitBranch で設定した環境変数が適用されません。
検証: --meta gitBranch の効果
vercel deploy --prebuilt --meta gitBranch=issue-27 --token=***--meta gitBranch を追加してもメタデータとして設定されるだけで、環境変数の選択には影響しません:
$ vercel ls --meta gitBranch=issue-27
# デプロイメントは表示される(メタデータは設定されている)
# しかしランタイムで環境変数は undefined解決策
--env オプションで直接環境変数を渡す
vercel deploy コマンドには --env オプションがあり、デプロイ時にランタイム環境変数を直接設定できます。
Vercel CLI ドキュメントより:
-e, --env <KEY=VALUE> Specify environment variables during run-time
(e.g. `-e KEY1=value1 -e KEY2=value2`)修正後のワークフロー
- name: Deploy to Vercel
id: deploy
run: |
DEPLOY_URL=$(vercel deploy --prebuilt \
--env TURSO_DATABASE_URL=${{ steps.db_url.outputs.url }} \
--env TURSO_AUTH_TOKEN=${{ secrets.TURSO_GROUP_TOKEN }} \
--meta gitBranch=${{ github.head_ref }} \
--token=${{ secrets.VERCEL_TOKEN }})
echo "url=$DEPLOY_URL" >> $GITHUB_OUTPUTなぜこれが機能するのか
| 方法 | 動作 | 結果 |
|---|---|---|
Vercel API で gitBranch 設定 | Git 連携デプロイにのみ適用 | CLI デプロイでは ❌ |
--meta gitBranch | メタデータとして保存 | 環境変数には影響なし ❌ |
--env KEY=VALUE | デプロイ時に直接設定 | 確実に適用 ✅ |
その他の問題と対処
問題: staging-migration の paths 設定
元の設定:
on:
push:
branches: [develop]
paths:
- "src/db/schema/**"
- "drizzle/**"問題点:
src/db/schema/** を含めると、スキーマを変更しただけで(マイグレーションファイルを生成せずに)ワークフローが実行されてしまいます。
Drizzle のマイグレーションワークフロー:
src/db/schema/** 変更
↓
drizzle-kit generate
↓
drizzle/** にマイグレーションファイル生成
↓
git commit & push
↓
drizzle-kit migrate(DB に適用)drizzle-kit migrate はマイグレーションファイルを読み取って DB に適用するため、drizzle/** の変更のみをトリガーにすれば十分です。
修正後:
on:
push:
branches: [develop]
paths:
- "drizzle/**"最終的なワークフロー構成
preview-deploy.yml(完全版)
name: Preview Deploy
on:
pull_request:
types: [opened, reopened, synchronize]
branches: [develop]
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
jobs:
deploy-preview:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: "24"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Install Turso CLI
run: curl -sSfL https://get.tur.so/install.sh | bash
- name: Install Vercel CLI
run: npm install -g vercel@latest
# Step 1: データベースの作成(初回のみ)
- name: Generate DB name from branch
id: db_name
run: |
BRANCH="${{ github.head_ref }}"
DB_NAME="linto-$(echo "$BRANCH" | tr '[:upper:]' '[:lower:]' | tr -cd '[:alnum:]-' | cut -c 1-32)"
echo "name=$DB_NAME" >> $GITHUB_OUTPUT
- name: Check if database exists
id: db_check
env:
TURSO_API_TOKEN: ${{ secrets.TURSO_API_TOKEN }}
run: |
if ~/.turso/turso db show ${{ steps.db_name.outputs.name }} > /dev/null 2>&1; then
echo "exists=true" >> $GITHUB_OUTPUT
else
echo "exists=false" >> $GITHUB_OUTPUT
fi
- name: Create Preview Database (branched from staging)
if: steps.db_check.outputs.exists == 'false'
env:
TURSO_API_TOKEN: ${{ secrets.TURSO_API_TOKEN }}
run: |
~/.turso/turso db create ${{ steps.db_name.outputs.name }} \
--from-db linto-staging \
--group preview
- name: Get Preview Database URL
id: db_url
env:
TURSO_API_TOKEN: ${{ secrets.TURSO_API_TOKEN }}
run: |
DB_URL=$(~/.turso/turso db show ${{ steps.db_name.outputs.name }} --url)
echo "url=$DB_URL" >> $GITHUB_OUTPUT
# Step 2: マイグレーションの適用
- name: Apply migrations
env:
TURSO_DATABASE_URL: ${{ steps.db_url.outputs.url }}
TURSO_AUTH_TOKEN: ${{ secrets.TURSO_GROUP_TOKEN }}
BETTER_AUTH_SECRET: ${{ secrets.BETTER_AUTH_SECRET }}
BETTER_AUTH_URL: "http://localhost:3000"
GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }}
GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }}
NEXT_PUBLIC_SITE_URL: "http://localhost:3000"
run: npm run db:migrate
# Step 3: Vercel 環境変数の設定(将来の自動デプロイ用)
- name: Set Vercel environment variable for branch
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
run: |
curl -X POST "https://api.vercel.com/v10/projects/$VERCEL_PROJECT_ID/env?upsert=true" \
-H "Authorization: Bearer $VERCEL_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"key": "TURSO_DATABASE_URL",
"value": "${{ steps.db_url.outputs.url }}",
"type": "encrypted",
"target": ["preview"],
"gitBranch": "${{ github.head_ref }}"
}'
# Step 4: Vercel CLI でビルド & デプロイ
- name: Pull Vercel Environment
run: vercel pull --yes --environment=preview --git-branch=${{ github.head_ref }} --token=${{ secrets.VERCEL_TOKEN }}
- name: Build Project
run: vercel build --token=${{ secrets.VERCEL_TOKEN }}
env:
TURSO_DATABASE_URL: ${{ steps.db_url.outputs.url }}
TURSO_AUTH_TOKEN: ${{ secrets.TURSO_GROUP_TOKEN }}
- name: Deploy to Vercel
id: deploy
run: |
DEPLOY_URL=$(vercel deploy --prebuilt \
--env TURSO_DATABASE_URL=${{ steps.db_url.outputs.url }} \
--env TURSO_AUTH_TOKEN=${{ secrets.TURSO_GROUP_TOKEN }} \
--meta gitBranch=${{ github.head_ref }} \
--token=${{ secrets.VERCEL_TOKEN }})
echo "url=$DEPLOY_URL" >> $GITHUB_OUTPUT
# Step 5: PR にコメント
- name: Comment PR with deployment info
if: github.event.action == 'opened'
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `## Preview Deployed
| Item | Value |
|------|-------|
| Preview URL | ${{ steps.deploy.outputs.url }} |
| Database | \`${{ steps.db_name.outputs.name }}\` |
| Branch | \`${{ github.head_ref }}\` |
Database created from \`linto-staging\`
Migrations applied
Deployed with correct environment variables
**Note**: Database will be deleted when PR is closed.
`
})学んだこと
1. Vercel CLI と Git 連携の違い
| 機能 | Git 連携(自動デプロイ) | Vercel CLI |
|---|---|---|
| ブランチ検出 | 自動 | 手動(--meta) |
| ブランチ固有環境変数 | 自動適用 | 適用されない |
| 環境変数設定 | ダッシュボード/API | --env オプション |
2. vercel.json の読み込みタイミング
vercel.json はデフォルトブランチから読み込まれるため、feature ブランチにあるだけでは設定が反映されません。
3. --env オプションの重要性
CLI デプロイでは、--env オプションを使用して直接環境変数を渡すのが最も確実な方法です。
まとめ
Vercel CLI を使用して GitHub Actions からデプロイする場合、ブランチ固有の環境変数は自動的には適用されません。--env オプションを使用して、デプロイ時に直接環境変数を渡すことで、この問題を解決できます。
vercel deploy --prebuilt \
--env TURSO_DATABASE_URL=$DB_URL \
--env TURSO_AUTH_TOKEN=$TOKEN \
--token=$VERCEL_TOKENこの方法により、Vercel プロジェクトの環境変数設定に依存せず、確実に正しい環境変数でデプロイできます。