Remix+CloudflareでWebサイトを作る 20(DependabotのSecrets・Hello, Prisma!・Prismaのシード挿入時エラー・Staging環境のURLを固定)
【2024-03-25】手動で作ったPRではGitHub Actionが正常に動くが、Dependabotが作ったPRではsecretsが読み込めなくて落ちる
問題
- name: Create .env.staging
env:
VITE_CLIENT_URL: ${{ vars.VITE_CLIENT_URL }}
VITE_SESSION_SECRET: ${{ secrets.VITE_SESSION_SECRET }}
VITE_GOOGLE_CLIENT_ID: ${{ secrets.VITE_GOOGLE_CLIENT_ID }}
VITE_GOOGLE_CLIENT_SECRET: ${{ secrets.VITE_GOOGLE_CLIENT_SECRET }}
run: |
echo NODE_ENV=staging >> .env.staging
echo VITE_CLIENT_URL=$VITE_CLIENT_URL >> .env.staging
echo VITE_SESSION_SECRET=$VITE_SESSION_SECRET >> .env.staging
echo VITE_GOOGLE_CLIENT_ID=$VITE_GOOGLE_CLIENT_ID >> .env.staging
echo VITE_GOOGLE_CLIENT_SECRET=$VITE_GOOGLE_CLIENT_SECRET >> .env.staging
- name: Deploy
uses: cloudflare/wrangler-action@v3
with:
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} # ここで落ちる
command: pages deploy ./build/client --project-name=projectName --branch=${{ needs.setup.outputs.BRANCH }} --env staging
エラー内容
✘ [ERROR] In a non-interactive environment, it's necessary to set a CLOUDFLARE_API_TOKEN environment variable for wrangler to work. Please go to https://developers.cloudflare.com/fundamentals/api/get-started/create-token/ for instructions on how to create an api token, and assign its value to CLOUDFLARE_API_TOKEN.
手動で作ったときは動いている、かつViteのSecretsは読み込めているのでなおさらわからない。
secretsへのアクセス権限がない?
pull_request_target を利用すれば secrets へのアクセスも許可される
と書いてある。
Viteの方ではエラーがでないのでそんなこともなさそうに思うが試しに以下のようにしてみるがうまくいかず。
on:
pull_request_target:
types: [opened, reopened, synchronize]
Dependabot secretsを設定する
GitHub の Settings > Secrets のところを見ると以下の2つがあります。
・Actions secrets
・Dependabot secrets
このうちの Dependabot secrets が Dependabot がアクセスできるシークレットです。
そうだ。言われてみればそんなんあったわ...。
「Actions」の方にはこんな感じでSecretsを設定していたが、「Dependabot」の方は何も設定してなかった。
ここでGitHub Actionsで.envファイルを作るようにするまではCloudflare側のデプロイ機能使ってたし気付かなかった。
エラーがでているCLOUDFLARE_ACCOUNT_ID
だけ設定すると以下のようなエラーがでる。
✘ [ERROR] A request to the Cloudflare API (/memberships) failed.
Authentication error [code: 10000]
CLOUDFLARE_API_TOKEN
も設定するとうまくいった!
pull_request_target
はセキュリティリスクもあるということだったので良かった。
ただ、${{ secrets.VITE_SESSION_SECRET }}
をDependabot Secretsに設定しなくてもエラーがでない。なんでだ...?読み取れねえぞってエラーだしてくれよ。
[ERR_MODULE_NOT_FOUND]: Cannot find package '~' imported from vite.config.ts.timestamp-
【2024-03-26】Prisma入れようとしたらエラー
待ってました〜。
Early Accessだけど入れてしまいます〜〜。
セットアップ
$ npm i @prisma/client @prisma/adapter-d1
$ npm i -D prisma
$ npx prisma init --datasource-provider sqlite
Connection URLs (Reference) | Prisma Docs
PotgreSQLで作ったときはURLを書いたがSQLではファイル指定なのか。
適当なモデルをschema.prisma
に書いてマイグレーション。
$ npx prisma migrate dev --name add_user_model
ログに表示されている通りdev.db
というファイルがprisma/
に作成されている
vite.config.tsでエラー
あとは元記事にあるとおりzxを使ってマイグレーションしていざ立ち上げようとしたら以下のエラーが発生
↪ npm run dev
> dev
> remix vite:dev
failed to load config from /vite.config.ts
Error [ERR_MODULE_NOT_FOUND]: Cannot find package '~' imported from vite.config.ts.timestamp-1711462399629-641d95f804b69.mjs
at new NodeError (node:internal/errors:406:5)
at packageResolve (node:internal/modules/esm/resolve:789:9)
at moduleResolve (node:internal/modules/esm/resolve:838:20)
at defaultResolve (node:internal/modules/esm/resolve:1043:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:383:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:352:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:228:38)
at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:85:39)
at link (node:internal/modules/esm/module_job:84:36) {
code: 'ERR_MODULE_NOT_FOUND'
}
remixCloudflareDevProxy()
だと発生しないがremixCloudflareDevProxy({ getLoadContext })
にするとこのエラーが発生する。
export default defineConfig({
plugins: [
remixCloudflareDevProxy({ getLoadContext }), // ここで発生する
remix(),
tsconfigPaths(),
],
});
記事内のリポジトリのコードをCloneして中身見比べたけど差分がわからない。
記事内のリポジトリに今のコードを少しずつ移植していっても、全くエラーが出てくれない。
このエラーで調べると出てくるtsconfig.json
の設定の問題でもなさそう。
原因わからないしもういっそ https://github.com/chimame/remix-prisma-d1-on-cloudflare-pages に全部コピペしていったら動くんじゃね...?
結果
https://github.com/chimame/remix-prisma-d1-on-cloudflare-pages にコードを少しずつ移植しながら原因を特定しようとしたもののエラーが再現できず動いてしまった。
う〜ん、もやもやするけど先へ進む。
【2024-03-28】SQliteでEnum使えない
問題
前にRemix + Prisma(PostgreSQL)のときはenum使えていたのに以下のエラーがでる
error: Error validating: You defined the enum `<EnumName>`. But the current connector does not support enums.
SQLiteなので
SQLiteだもんね。忘れてた。
ref: Prisma Schema API | Prisma Docs
datasource db {
provider = "sqlite" // provider = "postgresql" にするとエラーが消える
url = "file:./dev.db"
}
Issueはある?
4年前の2020-04-18からOpenになっている。
「Prisma辞めます!」「早く実装してくれ!」(これが多い)というコメントが。
必要とされているのに長く実装されていないからか結構過激なコメント多くておもろい。
Enumの値を入れたDBを作るパターンか、String型のカラムにするがDB以外でバリデーションしておくか、の2つが候補として挙げられているが後者を選択しようかなと思う。
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /prisma/seed.ts
【2024-03-28】Prismaでシードデータを入れる際にエラー発生: エラー内容
公式を参考に以下のようにしてシードデータを入れてみる。
シードファイルはprisma/seed.ts
に作成した。
{
"prisma": {
"seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts"
},
}
$ npx prisma db seed
> db:dev:seed
> npx prisma db seed
Running seed command `ts-node ./prisma/seed.ts` ...
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /prisma/seed.ts
解決
ts-node
ではなく tsx
を使って解決。
$ npm install -D tsx
{
"prisma": {
"seed": "tsx ./prisma/seed.ts"
},
"scripts": {
"db:seed": "prisma db seed",
}
$ npm run db:seed
prisma db seed
でD1にシードデータ入れるのどうやるんだ?
【2024-03-29】問題点
Prismaのシードデータ作成の方法を調べると prisma/seed.ts
作って以下のように初期化している。
const client = new PrismaClient();
けど、D1との接続のためにはPrismaClient()
の引数にRemixのloader()などで引数に取るcontext.db: D1Database
の値を使う必要がある。
import { PrismaD1 } from "@prisma/adapter-d1";
import { PrismaClient } from "@prisma/client";
export const connection = async (db: D1Database) => {
const adapter = new PrismaD1(db);
return new PrismaClient({ adapter });
};
adapter
を作るために db の値が欲しいが、これをどっから持ってきたらいいのかわからない。
Prisma StudioはD1で使えない
Drizzle StudioだとConfigに driver: "d1"
とか指定できた気がするけど。
Issueは立ってる。早く来るといいな。
【2024-03-29】zxで引数を受け取る
背景
zxで本番環境にマイグレーションを実行した時に以下のエラーが出た。
結果を言うとwrangler.toml
に書いているdatabase_id
が間違っていたんだけど、エラー文わかりにくいからどうにかしたさある。
あと引数渡してapplyする環境を動的に変えられるようにしたい。
$ npm exec -- zx prisma/d1-migration.mjs
$ mkdir -p ./migrations
$ npx wrangler d1 migrations apply DB_NAME --remote
⛅️ wrangler 3.37.0 (update available 3.39.0)
---------------------------------------------
Error:
at file:///prisma/d1-migration.mjs:20:8
exit code: 1
実装
$ npm exec -- zx prisma/d1-migration.mjs a b c --env=development
こんな感じで引数を渡すと、mjsファイル内のargv
では以下のように値が入る。
console.log(argv)
# { _: [ 'a', 'b', 'c' ], env: 'development' }
ということを考慮して実装。
$ npm exec -- zx prisma/d1-migration.mjs --env=development
$ npm exec -- zx prisma/d1-migration.mjs --env=staging
$ npm exec -- zx prisma/d1-migration.mjs --env=production
こんな感じで引数を渡したときに環境ごとにデプロイコマンドを分ける。
#!/usr/bin/env zx
+ // zxが呼び出したShellコマンドの標準出力を表示しないようにする。
+ // $.verbose = false;
await $`mkdir -p ./migrations`;
const packages = await glob(["prisma/migrations/*/migration.sql"]);
for (let i = 0; i < packages.length; i++) {
const migrationName = packages[i]
.replace("prisma/migrations/", "")
.split("/")[0];
if (!fs.existsSync(`migrations/${migrationName}.sql`)) {
await $`cp ${packages[i]} migrations/${migrationName}.sql`;
}
}
+ switch (argv.env) {
+ case "development":
+ const development = chalk.bgHex("#00bfff").hex("#000000");
+ echo(development("Apply to development."));
+ await $`npx wrangler d1 migrations apply DB_NAME --local`;
+ break;
+ case "staging":
+ const staging = chalk.bgHex("#ffa500").hex("#000000");
+ echo(staging("Apply to staging."));
+ await $`npx wrangler d1 migrations apply DB_NAME --remote --preview`;
+ break;
+ case "production":
+ const production = chalk.bgHex("#ff0000").hex("#000000");
+ echo(production("Apply to production."));
+ await $`npx wrangler d1 migrations apply DB_NAME --remote`;
+ break;
+ default:
+ console.log("⚠️ Please select environment");
+ break;
+ }
zxではTerminalをスタイリングするchalkが標準搭載だったので無駄に使いたかった。
npx wrangler dev
実行時に[ERROR] Top-level await is currently not supported with the "iife" output format
【2024-03-30】【2024-03-01】ローカルでR2にアップロードした画像を取得する方法がわからん でnpx wrangler dev
実行時にエラーでてたけど、Prismaを導入したらまた違うエラーで落ちていた。
Prisma内部で発生しているけどEarly Accessだしと考えて一旦無視して進む。
$ npx wrangler dev
⛅️ wrangler 3.39.0 (update available 3.41.0)
-------------------------------------------------------
...(略)
[wrangler:inf] Ready on http://localhost:51645
✘ [ERROR] Top-level await is currently not supported with the "iife" output format
node_modules/.prisma/client/wasm-worker-loader.js:1:16:
1 │ export default (await import('./query_engine_bg.wasm')).default
╵ ~~~~~
【2024-03-31】CloudflareのPreview環境のデプロイURLを固定したい
背景
【2024-03-22】remix-google-authでGoogleAuthログインにするでGoogle認証でログインするようにした。
ログインするためにはGoogle Cloud側で「承認済のリダイレクトURI」を設定しているが、現在はStaging環境ではうまく動いていない。
というのも、CloudflareのPreview(= Staging)環境では毎回異なるデプロイ先のURLが生成されるのでGoogle Cloud側に固定のものを設定できていないのだ。
あと毎回デプロイURLが変わるのでBasic認証のUsernameとPassword入力を要求されてめんどい。
ref: Preview deployments · Cloudflare Pages docs
色々試す
Add a custom domain to a branch · Cloudflare Pages docs
Cloudflare Pages でステージング環境を構築する #cloudflare - Qiita
ここらへんの記事を見る限り、CloudflareはPreview環境のデプロイ先のURLにブランチ名を含むので、staging
という名前のブランチにデプロイすることでStaging環境のデプロイURLを固定するということが実現できそう。
公式通りにカスタムドメインをPagesに設定して https://AppName.pages.dev/ にアクセスしてみる。
ん?なにかミスっている。
デプロイログ見てみると、stagingブランチの方がデプロイされていない。
エイリアスがrefs/pull/53/merge
となっているのは、GitHub Actionでデプロイする際に以下のように --branch=${{ needs.setup.outputs.BRANCH }}
を指定していたから。
因みに--branch
が指定されていない場合はhead
になる。
- name: Deploy
id: cloudflare-wrangler
uses: cloudflare/wrangler-action@v3
with:
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
command: pages deploy ./build/client --project-name=<AppName> --branch=${{ needs.setup.outputs.BRANCH }} --env staging
元々Cloudflare側のデプロイはOFFにしていたが、stagingブランチにpushされた時だけCloudflareで自動デプロイするように設定すればエイリアスstaging
になるのでは?と思ったがこれではエラーになってしまう。
というのも、現在はGitHub Actionを使ってGitHub Secretsを見て動的に.env.stagingを作成してデプロイしている。
Cloudflareの自動デプロイ機能を使っても.env.stagingファイルが作成されないためエラーになってしまう...。完全に忘れてた。
ということでGitHub Actions側で--branch
を指定することでエイリアスをstaging
に固定する。
実装
deploy
コマンドのオプションで--branch
を使ってブランチ名を指定する。
- name: Deploy
id: cloudflare-wrangler
uses: cloudflare/wrangler-action@v3
with:
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
command: pages deploy ./build/client --project-name=<AppName> --branch=staging --env staging
ランダムなURLも作成されるが、エイリアスは最初に設定した固定のURLになった🎉
GitHub Actionsでこんな感じで指定しているのでどこのブランチからでもStaging環境のURLにデプロイされる。
めっちゃ快適だ〜〜〜〜。
GoogleAuthログインが正しくリダイレクトすることを確認
最後にGoogle Cloudの「承認済みのリダイレクト URI」にStaging環境のURLを追加する。
Google認証時に正しくリダイレクトされることを確認して終わり。