Closed8

Remix+CloudflareでWebサイトを作る 20(DependabotのSecrets・Hello, Prisma!・Prismaのシード挿入時エラー・Staging環境のURLを固定)

saneatsusaneatsu

【2024-03-25】手動で作ったPRではGitHub Actionが正常に動くが、Dependabotが作ったPRではsecretsが読み込めなくて落ちる

問題

staging-deploy-yml
      - 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へのアクセス権限がない?

https://zenn.dev/thaim/articles/2022-04-allow-dependabot-access-secrets

pull_request_target を利用すれば secrets へのアクセスも許可される

と書いてある。
Viteの方ではエラーがでないのでそんなこともなさそうに思うが試しに以下のようにしてみるがうまくいかず。

on:
  pull_request_target:
    types: [opened, reopened, synchronize]

Dependabot secretsを設定する

https://knmts.com/as-a-engineer-140/

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に設定しなくてもエラーがでない。なんでだ...?読み取れねえぞってエラーだしてくれよ。

saneatsusaneatsu

【2024-03-26】Prisma入れようとしたらエラー [ERR_MODULE_NOT_FOUND]: Cannot find package '~' imported from vite.config.ts.timestamp-

https://zenn.dev/chimame/articles/d3e7af9a612038

待ってました〜。
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 }) にするとこのエラーが発生する。

vite.config.ts
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 にコードを少しずつ移植しながら原因を特定しようとしたもののエラーが再現できず動いてしまった。

う〜ん、もやもやするけど先へ進む。

saneatsusaneatsu

【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

schema.prisma
datasource db {
  provider = "sqlite" // provider = "postgresql" にするとエラーが消える
  url      = "file:./dev.db"
}

Issueはある?

4年前の2020-04-18からOpenになっている。

「Prisma辞めます!」「早く実装してくれ!」(これが多い)というコメントが。
必要とされているのに長く実装されていないからか結構過激なコメント多くておもろい。

https://github.com/prisma/prisma/issues/2219

Enumの値を入れたDBを作るパターンか、String型のカラムにするがDB以外でバリデーションしておくか、の2つが候補として挙げられているが後者を選択しようかなと思う。

saneatsusaneatsu

【2024-03-28】Prismaでシードデータを入れる際にエラー発生: TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /prisma/seed.ts

エラー内容

https://www.prisma.io/docs/orm/prisma-migrate/workflows/seeding

公式を参考に以下のようにしてシードデータを入れてみる。
シードファイルはprisma/seed.tsに作成した。

package.json
{
  "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

解決

https://github.com/prisma/prisma/discussions/20369#discussioncomment-7637038

ts-node ではなく tsx を使って解決。

$ npm install -D tsx
package.json
{
  "prisma": {
    "seed": "tsx ./prisma/seed.ts"
  },
  "scripts": {
    "db:seed": "prisma db seed",
  }
$ npm run db:seed
saneatsusaneatsu

【2024-03-29】prisma db seed でD1にシードデータ入れるのどうやるんだ?

問題点

https://www.prisma.io/docs/orm/prisma-migrate/workflows/seeding

Prismaのシードデータ作成の方法を調べると prisma/seed.ts 作って以下のように初期化している。

seed.ts
const client = new PrismaClient();

けど、D1との接続のためにはPrismaClient() の引数にRemixのloader()などで引数に取るcontext.db: D1Databaseの値を使う必要がある。

app/database/client.ts
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" とか指定できた気がするけど。

https://github.com/prisma/studio/issues/1133

Issueは立ってる。早く来るといいな。

saneatsusaneatsu

【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

こんな感じで引数を渡したときに環境ごとにデプロイコマンドを分ける。

prisma/d1-migration.mjs
#!/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が標準搭載だったので無駄に使いたかった。

saneatsusaneatsu

【2024-03-30】npx wrangler dev実行時に[ERROR] Top-level await is currently not supported with the "iife" output format

【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:
      1export default (await import('./query_engine_bg.wasm')).default
        ╵                 ~~~~~
saneatsusaneatsu

【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 になる。

staging-deploy.yml
- 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を使ってブランチ名を指定する。

staging-deploy.yml
- 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認証時に正しくリダイレクトされることを確認して終わり。

このスクラップは2024/03/31にクローズされました