diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 8d0e303a4..57e1f1c2e 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -167,41 +167,10 @@ jobs: run: pnpm install --frozen-lockfile working-directory: . - - name: Setup pscale - uses: planetscale/setup-pscale-action@v1 - - - name: Generate PlanetScale branch password - env: - PLANETSCALE_SERVICE_TOKEN_ID: ${{ secrets.PLANETSCALE_SERVICE_TOKEN_ID }} - PLANETSCALE_SERVICE_TOKEN: ${{ secrets.PLANETSCALE_SERVICE_TOKEN }} - run: | - branch="${STAGE}" - if [ "$branch" = "prod" ]; then - branch="main" - fi - - response=$(pscale password create ${{ secrets.PLANETSCALE_DATABASE_NAME }} "$branch" "" -f json --org ${{ secrets.PLANETSCALE_ORGANIZATION }}) - - id=$(echo "$response" | jq -r '.id') - host=$(echo "$response" | jq -r '.access_host_url') - username=$(echo "$response" | jq -r '.username') - password=$(echo "$response" | jq -r '.plain_text') - echo "PASSWORD_ID=$id" >> $GITHUB_ENV - database_url="mysql://$username:$password@$host/${{ secrets.PLANETSCALE_DATABASE_NAME }}" - echo "DATABASE_URL=$database_url" >> $GITHUB_ENV - echo "::add-mask::$database_url" - - - name: Push schema to PlanetScale branch - run: pnpm --filter wodsmith-start exec drizzle-kit push - working-directory: . - env: - DATABASE_URL: ${{ env.DATABASE_URL }} - - name: Deploy with Alchemy run: pnpm run deploy env: STAGE: ${{ env.STAGE }} - DATABASE_URL: ${{ env.DATABASE_URL }} CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }} CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} ALCHEMY_PASSWORD: ${{ secrets.ALCHEMY_PASSWORD }} @@ -218,6 +187,7 @@ jobs: # AI configuration (optional) OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} BRAINTRUST_API_KEY: ${{ secrets.BRAINTRUST_API_KEY }} + CF_AIG_TOKEN: ${{ secrets.CF_AIG_TOKEN }} # Stripe secrets - prod and demo get their respective keys STRIPE_API_KEY: ${{ env.STAGE == 'prod' && secrets.STRIPE_SECRET_KEY || (env.STAGE == 'demo' && secrets.STRIPE_SECRET_KEY_DEMO || '') }} STRIPE_SECRET_KEY: ${{ env.STAGE == 'prod' && secrets.STRIPE_SECRET_KEY || (env.STAGE == 'demo' && secrets.STRIPE_SECRET_KEY_DEMO || '') }} @@ -264,32 +234,6 @@ jobs: run: pnpm install --frozen-lockfile working-directory: . - - name: Setup pscale - uses: planetscale/setup-pscale-action@v1 - - - name: Generate PlanetScale demo branch password - env: - PLANETSCALE_SERVICE_TOKEN_ID: ${{ secrets.PLANETSCALE_SERVICE_TOKEN_ID }} - PLANETSCALE_SERVICE_TOKEN: ${{ secrets.PLANETSCALE_SERVICE_TOKEN }} - run: | - response=$(pscale password create ${{ secrets.PLANETSCALE_DATABASE_NAME }} demo "" -f json --org ${{ secrets.PLANETSCALE_ORGANIZATION }}) - - id=$(echo "$response" | jq -r '.id') - host=$(echo "$response" | jq -r '.access_host_url') - username=$(echo "$response" | jq -r '.username') - password=$(echo "$response" | jq -r '.plain_text') - echo "PASSWORD_ID=$id" >> $GITHUB_ENV - database_url="mysql://$username:$password@$host/${{ secrets.PLANETSCALE_DATABASE_NAME }}" - echo "DATABASE_URL=$database_url" >> $GITHUB_ENV - echo "::add-mask::$database_url" - - - name: Push schema to PlanetScale demo branch - run: pnpm --filter wodsmith-start exec drizzle-kit push - working-directory: . - env: - DATABASE_URL: ${{ env.DATABASE_URL }} - - - name: Deploy with Alchemy run: pnpm run deploy env: @@ -310,6 +254,7 @@ jobs: # AI configuration (optional) OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} BRAINTRUST_API_KEY: ${{ secrets.BRAINTRUST_API_KEY }} + CF_AIG_TOKEN: ${{ secrets.CF_AIG_TOKEN }} # Both point to same secret - STRIPE_API_KEY is for Alchemy webhook creation, # STRIPE_SECRET_KEY is for runtime API calls STRIPE_API_KEY: ${{ secrets.STRIPE_SECRET_KEY_DEMO }} diff --git a/apps/wodsmith-start/alchemy.run.ts b/apps/wodsmith-start/alchemy.run.ts index 6c44362bc..ba645cc20 100644 --- a/apps/wodsmith-start/alchemy.run.ts +++ b/apps/wodsmith-start/alchemy.run.ts @@ -102,6 +102,7 @@ import { Branch as PlanetScaleBranch, Password as PlanetScalePassword, } from "alchemy/planetscale" +import { Exec } from "alchemy/os" import { CloudflareStateStore } from "alchemy/state" import { WebhookEndpoint } from "alchemy/stripe" @@ -247,33 +248,25 @@ const psBranch = adopt: true, }) -const psPassword = await PlanetScalePassword(`ps-password-${stage}`, { +const psPassword = await PlanetScalePassword(`ps-deploy-password-${stage}`, { organization: psOrg, database: psDbName, branch: psBranch ?? psBranchName, role: "admin", }) -const ciDatabaseUrl = process.env.DATABASE_URL - ? new URL(process.env.DATABASE_URL) - : undefined -const hyperdriveOrigin = ciDatabaseUrl - ? { - host: ciDatabaseUrl.hostname, - database: ciDatabaseUrl.pathname.slice(1), - user: decodeURIComponent(ciDatabaseUrl.username), - password: decodeURIComponent(ciDatabaseUrl.password), - port: ciDatabaseUrl.port ? Number(ciDatabaseUrl.port) : 3306, - scheme: "mysql" as const, - } - : { - host: psPassword.host, - database: psDbName, - user: psPassword.username, - password: psPassword.password.unencrypted, - port: 3306, - scheme: "mysql" as const, - } +const databaseUrl = alchemy.secret( + `mysql://${encodeURIComponent(psPassword.username)}:${encodeURIComponent( + psPassword.password.unencrypted, + )}@${psPassword.host}/${psDbName}`, +) + +await Exec(`push-schema-${stage}`, { + command: "pnpm exec drizzle-kit push", + env: { + DATABASE_URL: databaseUrl, + }, +}) /** * Cloudflare Hyperdrive for PlanetScale connection pooling and caching. @@ -287,10 +280,14 @@ const hyperdriveOrigin = ciDatabaseUrl * which exposes a `connectionString` for use with standard MySQL drivers (mysql2). */ const hyperdrive = await Hyperdrive(`hyperdrive-${stage}`, { - // CI creates a fresh PlanetScale branch password before deploy and verifies it - // with drizzle-kit. Prefer that URL so stale Alchemy password state cannot - // make Cloudflare reject Hyperdrive updates during credential validation. - origin: hyperdriveOrigin, + origin: { + host: psPassword.host, + database: psDbName, + user: psPassword.username, + password: psPassword.password.unencrypted, + port: 3306, + scheme: "mysql", + }, caching: { disabled: true, },