# CI・デプロイで使う

CI は、実行時に aikagi から値を取得します。GitHub Actions は OIDC を使うため、長期間有効な token を置く必要がありません。Cloudflare Workers・Cloudflare Pages には、読み取り専用の実行時キーを 1 つ置きます。Vercel へは、aikagi が値を Vercel の環境変数として同期します。

## 実行する場所ごとの方法

| 実行する場所 | 値の取得方法 | その場所に置くもの |
| --- | --- | --- |
| 手元の PC・自分のサーバ | [`aikagi run` を前に付ける](#run) | なし（`aikagi login` のログインを使う） |
| GitHub Actions | [job の OIDC token](#github) | なし |
| Vercel | [aikagi から Vercel の環境変数へ同期](#vercel) | 同期した値（Vercel の環境変数） |
| Cloudflare Workers・Cloudflare Pages | [読み取り専用の実行時キー](#runtime-key) | 実行時キー 1 つ（`AIKAGI_KEY`） |

**Cloudflare Workers・Cloudflare Pages のアプリは、起動時に aikagi から値を取得します。**そのときに aikagi に接続できないと、アプリの起動（最初のリクエストの処理）は失敗します。値が無いまま動き続けることはありません。Vercel のアプリは、同期された環境変数を読みます。アプリの起動時に aikagi へ接続することはありません。

## コマンドの前に付ける

いつも実行しているコマンドの前に `aikagi run --` を付けます。値は環境変数として、そのコマンドにだけ渡されます。

```sh
aikagi run -- npm test
```

## GitHub Actions

GitHub Actions では、GitHub が job ごとに発行する OIDC（OpenID Connect）の token を使って値を取得します。長期間有効な token を作って GitHub の Secrets に置く方法はありません。

まず、どのリポジトリに値の取得を許可するかを設定します。プロジェクトの左メニューで「環境」を開き、「GitHub Actions」の右上の「＋」（リポジトリを許可）を押して、次の 3 つを指定して保存します。保存すると、workflow に追加する内容がその場で表示されます。

1. リポジトリ（`acme/web-app` など）。
2. 取得を許可する ref（`refs/heads/main` や `refs/tags/v*`）。
3. 読み取りを許可する環境（`production` など）。

ref は書いたとおりの文字列で照合します。`refs/heads/main` は main ブランチだけに一致し、`main-hotfix` には一致しません。末尾に `*` を付けると、その手前までが同じものすべてに一致します。たとえば `refs/tags/v*` は、`v` で始まるすべてのタグに一致します。

次に、workflow の job に次の内容を追加します。

```sh
permissions:
  id-token: write
  contents: read
steps:
  - run: |
      curl -fsSL https://aikagi.dev/install.sh | sh
      echo "$HOME/.local/bin" >> "$GITHUB_PATH"
  - run: aikagi run --workspace acme --project web-app --env production -- npm test
```

**`permissions: id-token: write` が必要です。**これが無いと job は OIDC token を受け取れず、値を取得できません。`--env` を省くと、許可した環境のうち先頭の環境の値を読みます。

OIDC token はその job の中で 1 回だけ使われ、ファイルにも残りません。長期間有効な token が無いため、token の入れ替えや失効し忘れを管理する必要もありません。

## Vercel

Vercel では、aikagi が環境の値を Vercel プロジェクトの環境変数へ同期します。ビルド時に読み込まれる値（`NEXT_PUBLIC_*` など）、ミドルウェア、関数のどこからでも、通常の環境変数として同じ値を読めます。アプリにパッケージを追加する必要はありません。

値を変更する場所は aikagi だけです。Vercel の画面で値を直接変更する必要はありません。

### 用意するもの

- **ワークスペースの管理者の権限。**同期を設定できるのは、ワークスペースの管理者だけです。メンバーは、同期の一覧を見ることだけができます。
- **Vercel のアクセストークン。**Vercel の Account Settings にある Tokens（`vercel.com/account/tokens`）で作成します。aikagi の画面でアクセストークンを貼り付ける欄にある「Vercel でトークンを作る」からも開けます。作成するときの「Scope」では、同期先のプロジェクトがある team を選びます。作成したアクセストークンは Vercel の画面に 1 回しか表示されないため、その場でコピーしてください。
- **同期先の Vercel プロジェクト。**先に Vercel で作成しておきます。

アクセストークンに有効期限を付けた場合は、期限が切れると同期が失敗します。期限が切れる前に、[新しいアクセストークンに差し替えて](#vercel-token)ください。

### 同期を設定する

1. プロジェクトの左メニューで「環境」を開き、「Vercel へ同期」の「＋」（Vercel へ同期を追加）を押します。
2. 「Vercel のアクセストークン」を貼り付けて、「登録」を押します。アクセストークンは暗号化して保存され、保存したあとは画面に表示されません。保存するのはワークスペースごとに 1 回だけです。2 つ目以降のプロジェクトでは、保存済みのアクセストークンを選ぶだけで済みます。
3. 同期先の Vercel プロジェクトを一覧から選びます。プロジェクトが多い場合は、名前で絞り込めます。
4. Production・Preview・Development のそれぞれについて、送る aikagi の環境を選びます。送らない場合は「同期しない」を選びます。最初は、名前が同じ環境が選ばれています。
5. 上書きされる環境変数の名前が表示されるので、内容を確認します。
6. 「同期を始める」を押します。すべての値が Vercel へ送られ、行の「最終同期」に時刻が表示されます。

![プロジェクトの環境の画面にある「Vercel へ同期」。同期先の Vercel プロジェクト web-app の行に、環境の対応（production から Production、staging から Preview）と最終同期の時刻が表示されている。右上に「…」と「＋」のボタンがある](https://aikagi.dev/guide/img/vercel-sync.webp)

*同期を設定すると、行に環境の対応と最終同期の時刻が表示されます。*

環境の対応は、たとえば次のようにします。

| Vercel の環境 | aikagi の環境 | 使われる場面 |
| --- | --- | --- |
| Production | `production` | 本番のデプロイ |
| Preview | `staging` | プルリクエストやブランチのデプロイ |
| Development | `development` | `vercel dev` と `vercel env pull` |

Vercel の 1 つの環境に送れる aikagi の環境は、1 つだけです。同じ aikagi の環境を、Vercel の複数の環境に送ることはできます。

### 値を反映する

設定したあとは、値を保存・削除するたびに自動で Vercel へ送られます。キーの名前を変更した場合も、Vercel の環境変数の名前が変わります。

**同期した値が使われるのは、次のデプロイからです。**これは Vercel の仕様です。動いているデプロイの値は変わりません。すぐに反映したい場合は、Vercel で再デプロイしてください。aikagi が再デプロイを実行することはありません。

値が届いたかどうかは、次の 2 か所で確認できます。

- aikagi の「Vercel へ同期」の行にある「最終同期」の時刻。
- Vercel のプロジェクトの Settings にある Environment Variables の一覧。

### 環境変数の名前

環境変数の名前は、`aikagi run` が渡す名前と同じです。フォルダを含むキーの名前の `/` と `-` を `_` に置き換え、大文字にします。

| aikagi のキー | Vercel の環境変数 |
| --- | --- |
| `DATABASE_URL` | `DATABASE_URL` |
| `openai/api_key` | `OPENAI_API_KEY` |
| `stripe/webhook-secret` | `STRIPE_WEBHOOK_SECRET` |

次のキーは送られません。送られなかったキーは、同期の行に件数が表示され、カーソルを合わせると名前を確認できます。

- 名前が数字で始まるキー（`2fa/secret` など）。Vercel の環境変数の名前にできません。
- Vercel が予約している名前（`TZ`、`AWS_SECRET_KEY` など）になるキー。
- 別のキーと同じ環境変数の名前になるキー（`openai/api-key` と `openai/api_key` など）。名前の順で先のキーだけが送られます。

送りたい場合は、aikagi でキーの名前を変更してください。

### Vercel にすでに値がある場合

- **同じ名前の環境変数は、aikagi の値で上書きされます。**上書きされる名前は、「同期を始める」を押す前に一覧で表示されます。Vercel の値を残したい場合は、先にその値を aikagi に保存してください。
- **aikagi に無い名前の環境変数は、そのまま残ります。**aikagi が削除するのは、aikagi 自身が Vercel に置いた環境変数だけです。
- 新しく作成される Production と Preview の環境変数は、Vercel の Sensitive な環境変数になります。Sensitive な環境変数の値は、Vercel の画面でも読めなくなります。値は aikagi で確認してください。Development は Vercel が Sensitive に対応していないため、通常の環境変数として作成されます。
- すでにある環境変数は、値だけが置き換わります。種類はほとんどの場合そのままですが、古い形式で暗号化されたものは Sensitive に変わり、暗号化されていないものは暗号化される種類で作り直されます。
- 1 つの環境変数を Vercel の複数の環境で共有していた場合は、同期する環境の分だけが aikagi の値に置き換わります。ほかの環境の値は変わりません。
- Preview の環境変数のうち、特定のブランチだけに設定したものは変更されません。

### 設定を変える・解除する

同期の行の「…」から操作します。

- **今すぐ同期**: すべての値を送り直します。同期に失敗したあとのやり直しにも使えます。
- **設定を変える**: 環境の対応を変更できます。「保存して同期」を押すと、新しい設定ですべての値が送り直されます。対応から外した Vercel の環境からは、aikagi が置いた環境変数が削除されます。
- **同期を解除**: aikagi が Vercel に置いた環境変数を削除して、同期をやめます。動いているデプロイはそのままで、値が無くなるのは次のデプロイからです。「aikagi で消したキーを Vercel に残す」が有効な場合は、Vercel の環境変数をすべて残したまま同期をやめます。

「設定を変える」の「詳細」を開くと、次の 2 つも変更できます。

- 「同期するフォルダ」: 選んだフォルダの中のキーだけを送ります。最初は「プロジェクト全体」です。
- 「aikagi で消したキーを Vercel に残す」: 有効にすると、aikagi でキーを削除しても Vercel の環境変数は削除されません。

aikagi で環境を削除すると、その環境から送った環境変数は Vercel からも削除されます。プロジェクトを削除した場合も同じです。

### アクセストークンを差し替える

「Vercel へ同期」の右上の「…」から「Vercel のトークン」を開きます。「差し替え」を押して新しいアクセストークンを貼り付けると、そのアクセストークンを使っているすべての同期が、新しいアクセストークンで動くようになります。同期を設定し直す必要はありません。

どの同期にも使われていないアクセストークンは、同じ画面で削除できます。

### うまくいかないとき

| 状況 | 対処 |
| --- | --- |
| アクセストークンを貼り付けても登録できない | アクセストークンをコピーし直してください。期限が切れている場合は、Vercel で作り直してください。 |
| 同期先の Vercel プロジェクトが一覧に無い | アクセストークンの Scope が、そのプロジェクトのある team になっているか確認してください。違う場合は、Scope を選び直してアクセストークンを作り直してください。 |
| 行に「同期に失敗」と表示される | カーソルを合わせると理由が表示されます。アクセストークンが原因の場合は、[差し替えて](#vercel-token)から「今すぐ同期」を押してください。Vercel 側のエラーや呼び出し制限の場合は、しばらく待ってから「今すぐ同期」を押してください。 |
| 値を変更したのに、アプリに反映されない | 行の「最終同期」の時刻が変更より後なら、値は Vercel に届いています。Vercel で再デプロイしてください。 |
| 一部のキーが Vercel に無い | [送られないキー](#vercel-names)に当てはまらないか、行の表示で確認してください。「同期するフォルダ」を選んでいる場合は、そのフォルダの外のキーは送られません。 |
| 同期を解除できない | Vercel の環境変数を削除できなかった場合は、同期は解除されません。アクセストークンを差し替えるか、しばらく待ってからやり直してください。Vercel の環境変数を残したまま解除する場合は、「設定を変える」で「aikagi で消したキーを Vercel に残す」を有効にしてから解除してください。 |

### `register()` で取得していた場合

以前のガイドでは、`instrumentation.ts` の `register()` で `loadAikagi()` を呼ぶ方法を案内していました。この方法は Vercel では動作しません（理由は[次の節](#vercel-runtime)）。次の順に同期へ切り替えてください。

1. 上の手順で同期を設定し、行の「最終同期」に時刻が表示されることを確認します。
2. `instrumentation.ts` から `loadAikagi()` の呼び出しを削除します。ほかで使っていなければ、`@aikagi/runtime` もアンインストールします。
3. デプロイします。このデプロイから、同期した環境変数が使われます。
4. 「環境」の画面の「実行時の取得元」に Vercel の行が残っている場合は、外します。

### Vercel で実行時に取得する場合

Vercel では、アプリの起動時に値を取得する方法は使えません。Vercel が関数に OIDC token を渡すのは、リクエストを処理している間だけです。`instrumentation.ts` の `register()` はリクエストより前に実行されるため、OIDC token を受け取れません。

`@aikagi/runtime` を Vercel で使えるのは、リクエストの処理中に `loadAikagi()` を呼ぶ場合だけです。この場合は、Vercel のプロジェクトとリンクしたリポジトリ（`.vercel/project.json` があるディレクトリ）で `aikagi init` を実行してから、ルートハンドラなどの先頭で呼びます。ビルド時に読み込まれる値には使えません。

```sh
// app/api/hello/route.ts
import { loadAikagi } from '@aikagi/runtime'

export async function GET() {
  await loadAikagi()
  return Response.json({ ok: true })
}
```

## Cloudflare Workers・Cloudflare Pages

Cloudflare Workers・Cloudflare Pages では OIDC を使えません。代わりに、production 環境だけを読み取れる実行時キーを 1 つ置きます。

デプロイの設定ファイルがあるリポジトリで `aikagi init` を実行し、確認されたら `Y` を押します。実行時キーが作成され、手元でデプロイに使っているログインを使って `AIKAGI_KEY` として登録されます。登録に失敗した場合は、作成した実行時キーを取り消し、そのことを 1 行で表示します。

```sh
// src/index.ts
import { withAikagi } from '@aikagi/runtime'

export default withAikagi({
  async fetch(request, env) {
    // env.OPENAI_API_KEY などが入っています
    return new Response('ok')
  },
})
```

実行時キーは、プロジェクトの左メニューの「環境」にある「実行時の取得元」で失効できます。`aikagi init --force` をもう一度実行すると、新しい実行時キーに置き換わります。

![プロジェクトの環境の画面。上に GitHub Actions に許可したリポジトリと ref・環境・最終使用、下に実行時の取得元として Vercel のプロジェクトと Cloudflare Workers 用の実行時キーが並んでいる](https://aikagi.dev/guide/img/environments.webp)

*許可したリポジトリと実行時の取得元は、同じ画面に表示されます。その下に「Vercel へ同期」が続きます。Vercel の行は、[実行時に取得する場合](#vercel-runtime)に `aikagi init` で登録したプロジェクトです。*

CI の中で `aikagi init` を実行しても、デプロイ先の設定は変わりません。AI ツールに実行させる場合は、`--yes` を付けると確認なしで最後まで進みます。

## デプロイする

Vercel や Cloudflare Workers・Cloudflare Pages へのデプロイも、GitHub Actions の中で同じように実行できます。デプロイに使う token（`VERCEL_TOKEN` や `CLOUDFLARE_API_TOKEN`）も aikagi に保存しておけば、GitHub にもデプロイ先にも token を置かずに済みます。

```sh
  - run: aikagi run --workspace acme --project web-app --env production -- npx vercel deploy --prod
```

```sh
  - run: aikagi run --workspace acme --project web-app --env production -- npx wrangler deploy
```

## 監査ログ

値を取得するたびに、[監査ログ](https://aikagi.dev/guide/audit.md)に記録されます。取得した主体は次のように表示されます。

- GitHub Actions: `github:acme/web-app#<run_id>`（リポジトリと実行番号）
- Vercel（[実行時に取得した場合](#vercel-runtime)）: `vercel:acme/web-app@production`（team・プロジェクト・Vercel の環境）
- Cloudflare Workers・Cloudflare Pages: `key:<実行時キーの名前>`

Vercel への同期は、値を変更した人の操作として記録されます。同期 1 回につき 1 行で、記録されるのは送った件数とキーの名前だけです。値そのものは記録されません。

---

出典: https://aikagi.dev/guide/ci/
