diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 7caf9e9..ba13fac 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -13,6 +13,8 @@ on: env: # each folder under the repo root that contains one of your CLIs WORKSPACES: create-db create-pg create-postgres + CREATE_DB_WORKER_URL: ${{ secrets.CREATE_DB_WORKER_URL }} + CLAIM_DB_WORKER_URL: ${{ secrets.CLAIM_DB_WORKER_URL }} jobs: preview: @@ -71,10 +73,21 @@ jobs: run: echo "//registry.npmjs.org/:_authToken=${{ secrets.CREATE_DB_TOKEN_NPM }}" > ~/.npmrc - name: ๐Ÿš€ Bump & publish CLI previews - env: - CREATE_DB_WORKER_URL: ${{ secrets.CREATE_DB_WORKER_URL }} - CLAIM_DB_WORKER_URL: ${{ secrets.CLAIM_DB_WORKER_URL }} run: | + # Resolve URLs with fallback + CREATE_DB_WORKER_URL="${{ steps.deploy-db.outputs.deployment-url || secrets.CREATE_DB_WORKER_URL }}" + CLAIM_DB_WORKER_URL="${{ steps.deploy-claim.outputs.deployment-url || secrets.CLAIM_DB_WORKER_URL }}" + + # Persist for next steps + echo "CREATE_DB_WORKER_URL=$CREATE_DB_WORKER_URL" >> $GITHUB_ENV + echo "CLAIM_DB_WORKER_URL=$CLAIM_DB_WORKER_URL" >> $GITHUB_ENV + + export CREATE_DB_WORKER_URL + export CLAIM_DB_WORKER_URL + + echo "Using CREATE_DB_WORKER_URL=$CREATE_DB_WORKER_URL" + echo "Using CLAIM_DB_WORKER_URL=$CLAIM_DB_WORKER_URL" + for pkg in $WORKSPACES; do cd "$pkg" npm version prerelease \ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2453c08..c628e11 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,61 +1,25 @@ -# .github/workflows/release.yml -name: Release all CLIs & Deploy CF Workers +name: Release CLIs on: - workflow_dispatch: - pull_request: - branches: [main] - types: [opened, synchronize] push: - branches: [main] + branches: + - main + +concurrency: ${{ github.workflow }}-${{ github.ref }} env: WORKSPACES: create-db create-pg create-postgres jobs: - # ๐Ÿ’พ On PRs: bump package.json versions & CHANGELOGs via Changesets - version: - if: ${{ github.event_name == 'pull_request' }} + release: + name: ๐Ÿš€ Release CLIs runs-on: ubuntu-latest - steps: - - name: ๐Ÿ›Ž๏ธ Checkout full & tags - uses: actions/checkout@v3 - with: - fetch-depth: 0 - persist-credentials: true - - - name: ๐Ÿค Disable Husky - run: echo "HUSKY=0" >> $GITHUB_ENV - - - name: ๐Ÿ“ฆ Setup pnpm - uses: pnpm/action-setup@v2 - with: - version: 8 - - - name: ๐Ÿ”ง Install dependencies - run: pnpm install - - - name: ๐Ÿ”„ Version & changelogs via Changesets - uses: changesets/action@v1 - with: - version: "pnpm changeset version --yes" - commit: "chore(release): bump [skip ci]" - commitMode: "git-cli" - createGithubReleases: false - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # ๐Ÿš€ On merges to main: aggregate CHANGELOGs, publish CLIs & deploy Workers - publish: - if: ${{ github.event_name == 'push' }} - runs-on: ubuntu-latest - needs: version steps: - - name: ๐Ÿ›Ž๏ธ Checkout full & tags + - name: ๐Ÿ›Ž๏ธ Checkout Repo uses: actions/checkout@v3 with: fetch-depth: 0 - persist-credentials: true - name: ๐Ÿค Disable Husky run: echo "HUSKY=0" >> $GITHUB_ENV @@ -68,68 +32,54 @@ jobs: - name: ๐Ÿ”ง Install dependencies run: pnpm install - - name: ๐Ÿ”ง Install jq - run: | - sudo apt-get update - sudo apt-get install -y jq + - name: โŒ Disable pnpm git-checks + run: pnpm config set git-checks false + # Copy README files before any version bump or publish - name: ๐Ÿ“„ Copy README to child CLIs run: | - for pkg in $WORKSPACES; do - if [ "$pkg" != "create-db" ]; then - cp create-db/README.md "$pkg/README.md" - fi + for pkg in create-pg create-postgres; do + cp create-db/README.md "$pkg/README.md" done - - name: ๐Ÿ“‘ Aggregate changelogs - id: aggregate - run: | - NEW_VERSION=$(jq -r '.version' create-db/package.json) - echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV - echo "# Release v$NEW_VERSION" > AGGREGATED_CHANGELOG.md - for pkg in $WORKSPACES; do - echo "## $pkg" >> AGGREGATED_CHANGELOG.md - sed -n "/^## $NEW_VERSION/,/^## /p" $pkg/CHANGELOG.md \ - | sed '1d;$d' \ - >> AGGREGATED_CHANGELOG.md - echo "" >> AGGREGATED_CHANGELOG.md - done + # Configure npm for publishing + - name: ๐Ÿ”‘ Configure npm auth + run: echo "//registry.npmjs.org/:_authToken=${{ secrets.CREATE_DB_TOKEN_NPM }}" > ~/.npmrc - - name: ๐Ÿšฉ Create GitHub Release - uses: actions/create-release@v1 + # Generate version updates with changesets (PR created if branch protected) + - name: ๐Ÿ“ Changesets Versioning + uses: changesets/action@v1 with: - tag_name: v${{ env.NEW_VERSION }} - release_name: Release v${{ env.NEW_VERSION }} - body_path: AGGREGATED_CHANGELOG.md + version: pnpm changeset version env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: ๐Ÿ”‘ Configure npm auth - run: echo "//registry.npmjs.org/:_authToken=${{ secrets.CREATE_DB_TOKEN_NPM }}" > ~/.npmrc - - - name: ๐Ÿš€ Publish each CLI individually + # Publish packages to npm if changesets are applied + - name: ๐Ÿš€ Publish Each CLI Package + if: steps.changesets.outputs.hasChangesets == 'false' run: | + # Export secrets for CLI usage + export CREATE_DB_WORKER_URL="${{ secrets.CREATE_DB_WORKER_URL }}" + export CLAIM_DB_WORKER_URL="${{ secrets.CLAIM_DB_WORKER_URL }}" + + echo "Using CREATE_DB_WORKER_URL=$CREATE_DB_WORKER_URL" + echo "Using CLAIM_DB_WORKER_URL=$CLAIM_DB_WORKER_URL" + + # Publish each package for pkg in $WORKSPACES; do - echo "Publishing $pkg..." - pnpm --filter "$pkg" publish --access public - done + echo "Publishing $pkg to npm..." + cd "$pkg" + + # Stable patch bump + npm version patch --no-git-tag-version + + # Publish to npm + pnpm publish --access public - # - name: โ˜๏ธ Deploy create-db-worker (production) - # uses: cloudflare/wrangler-action@v3 - # with: - # apiToken: ${{ secrets.CF_API_TOKEN }} - # accountId: ${{ secrets.CF_ACCOUNT_ID }} - # environment: production - # workingDirectory: create-db-worker - - # - name: โ˜๏ธ Deploy claim-db-worker (production) - # uses: cloudflare/wrangler-action@v3 - # with: - # apiToken: ${{ secrets.CF_API_TOKEN }} - # accountId: ${{ secrets.CF_ACCOUNT_ID }} - # environment: production - # workingDirectory: claim-db-worker + cd - >/dev/null + done + env: + NODE_AUTH_TOKEN: ${{ secrets.CREATE_DB_TOKEN_NPM }} - name: ๐Ÿงน Cleanup npm auth - if: ${{ always() }} run: rm -f ~/.npmrc diff --git a/create-db-worker/worker-configuration.d.ts b/create-db-worker/worker-configuration.d.ts index 37df440..3d3fbb8 100644 --- a/create-db-worker/worker-configuration.d.ts +++ b/create-db-worker/worker-configuration.d.ts @@ -1,11 +1,9 @@ /* eslint-disable */ -// Generated by Wrangler by running `wrangler types` (hash: 63ac3638913b0dc240976e75200d8232) +// Generated by Wrangler by running `wrangler types` (hash: 3f56097be8c168953b53c223c74e988f) // Runtime types generated with workerd@1.20250617.0 2025-06-27 declare namespace Cloudflare { interface Env { CREATE_DB_RATE_LIMIT_KV: KVNamespace; - INTEGRATION_TOKEN: string; - CLIENT_SECRET: string; CREATE_DB_DATASET: AnalyticsEngineDataset; CREATE_DB_RATE_LIMITER: RateLimit; DELETE_DB_WORKFLOW: Workflow; diff --git a/create-db/index.js b/create-db/index.js index fdb9ca5..fd1efc6 100755 --- a/create-db/index.js +++ b/create-db/index.js @@ -15,6 +15,11 @@ import terminalLink from "terminal-link"; dotenv.config(); +const CREATE_DB_WORKER_URL = + process.env.CREATE_DB_WORKER_URL || "https://create-db-temp.prisma.io"; +const CLAIM_DB_WORKER_URL = + process.env.CLAIM_DB_WORKER_URL || "https://create-db.prisma.io"; + async function listRegions() { try { const regions = await getRegions(); @@ -29,7 +34,7 @@ async function listRegions() { } async function isOffline() { - const healthUrl = `${process.env.CREATE_DB_WORKER_URL || "https://create-db-temp.prisma.io"}/health`; + const healthUrl = `${CREATE_DB_WORKER_URL}/health`; try { const res = await fetch(healthUrl, { method: "GET" }); @@ -78,30 +83,27 @@ Usage: Options: ${chalk.yellow(`--region , -r `)} Specify the region (e.g., ${regionExamples}) - ${chalk.yellow("--list-regions, -lr")} Return the list of available regions for Prisma Postgres - ${chalk.yellow("--choose-region, -cs")} Select database region interactively + ${chalk.yellow("--interactive, -i")} Run in interactive mode to select a region and create the database ${chalk.yellow("--help, -h")} Show this help message Examples: ${chalk.gray(`npx ${CLI_NAME} --region us-east-1`)} - ${chalk.gray(`npx ${CLI_NAME} --list-regions`)} - ${chalk.gray(`npx ${CLI_NAME} --choose-region`)} - ${chalk.gray(`npx ${CLI_NAME} -cs`)} + ${chalk.gray(`npx ${CLI_NAME} -r us-east-1`)} + ${chalk.gray(`npx ${CLI_NAME} --interactive`)} + ${chalk.gray(`npx ${CLI_NAME} -i`)} `); process.exit(0); } // Parse command line arguments into flags and positional arguments -function parseArgs() { +async function parseArgs() { const args = process.argv.slice(2); const flags = {}; - const allowedFlags = ["region", "help", "list-regions", "choose-region"]; + const allowedFlags = ["region", "help", "list-regions", "interactive"]; const shorthandMap = { r: "region", - l: "list-regions", - lr: "list-regions", // multi-letter shorthand - cs: "choose-region", // multi-letter shorthand + i: "interactive", h: "help", }; @@ -117,7 +119,7 @@ function parseArgs() { // Handle long flags (--region, --help, etc.) if (arg.startsWith("--")) { const flag = arg.slice(2); - if (flag === "help") showHelp(); + if (flag === "help") await showHelp(); if (!allowedFlags.includes(flag)) exitWithError(`Invalid flag: --${flag}`); if (flag === "region") { @@ -156,7 +158,10 @@ function parseArgs() { for (const letter of short.split("")) { const mappedFlag = shorthandMap[letter]; if (!mappedFlag) exitWithError(`Invalid flag: -${letter}`); - if (mappedFlag === "help") showHelp(); + if (mappedFlag === "help") { + await showHelp(); + return; + } if (mappedFlag === "region") { const region = args[i + 1]; if (!region || region.startsWith("-")) @@ -180,7 +185,7 @@ function parseArgs() { * Fetch available regions from the API. */ export async function getRegions() { - const url = `${process.env.CREATE_DB_WORKER_URL || "https://create-db-temp.prisma.io"}/regions`; + const url = `${CREATE_DB_WORKER_URL}/regions`; const res = await fetch(url); if (!res.ok) { @@ -192,7 +197,7 @@ export async function getRegions() { try { const data = await res.json(); const regions = Array.isArray(data) ? data : data.data; - return regions.filter(region => region.status === 'available'); + return regions.filter((region) => region.status === "available"); } catch (e) { handleError("Failed to parse JSON from /regions endpoint.", e); } @@ -268,14 +273,11 @@ async function createDatabase(name, region) { const s = spinner(); s.start("Creating your database..."); - const resp = await fetch( - `${process.env.CREATE_DB_WORKER_URL || "https://create-db-temp.prisma.io"}/create`, - { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ region, name, utm_source: CLI_NAME }), - } - ); + const resp = await fetch(`${CREATE_DB_WORKER_URL}/create`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ region, name, utm_source: CLI_NAME }), + }); // Rate limit exceeded if (resp.status === 429) { @@ -303,21 +305,19 @@ async function createDatabase(name, region) { // Determine which connection string to display const database = result.data ? result.data.database : result.databases?.[0]; const prismaConn = database?.connectionString; - const directConnDetails = result.data + const directConnDetails = result.data ? database?.apiKeys?.[0]?.directConnection : result.databases?.[0]?.apiKeys?.[0]?.ppgDirectConnection; const directConn = directConnDetails ? `postgresql://${directConnDetails.user}:${directConnDetails.pass}@${directConnDetails.host}/postgres` : null; - log.info(chalk.bold("๐Ÿ”Œ You have two ways to connect to your database:")); + log.info(chalk.bold("Connect to your database โ†’")); // Show Prisma Postgres connection string if (prismaConn) { log.message( - chalk.cyan( - " Use this database connection string optimized for Prisma ORM:" - ) + chalk.magenta(" Use this connection string optimized for Prisma ORM:") ); log.message(" " + chalk.yellow(prismaConn)); log.message(""); @@ -340,20 +340,20 @@ async function createDatabase(name, region) { // Claim Database const projectId = result.data ? result.data.id : result.id; - const claimUrl = `${process.env.CLAIM_DB_WORKER_URL}?projectID=${projectId}&utm_source=${CLI_NAME}&utm_medium=cli`; + const claimUrl = `${CLAIM_DB_WORKER_URL}?projectID=${projectId}&utm_source=${CLI_NAME}&utm_medium=cli`; const clickableUrl = terminalLink(claimUrl, claimUrl, { fallback: false }); - log.info(`${chalk.white(chalk.bold("โœ… Claim your database:"))}`); - + log.success(`${chalk.bold("Claim your database โ†’")}`); log.message( - chalk.cyan(" You can make your database permanent ") + - chalk.cyan(chalk.bold("for free")) + - chalk.cyan(" by clicking the link below:") + chalk.cyan(" Want to keep your database? Claim for free via this link:") ); - log.message(" " + chalk.yellow(clickableUrl)); log.message( chalk.italic( - chalk.gray(" Your database expires at " + expiryFormatted + ".\n") + chalk.gray( + " Your database will be deleted on " + + expiryFormatted + + " if not claimed.\n" + ) ) ); } @@ -374,6 +374,10 @@ async function main() { let region = "us-east-1"; let chooseRegionPrompt = false; + if (flags.help) { + return; + } + if (flags["list-regions"]) { await listRegions(); process.exit(0); @@ -383,33 +387,26 @@ async function main() { if (flags.region) { region = flags.region; } - if (flags["choose-region"]) { + if (flags.interactive) { chooseRegionPrompt = true; } + intro(chalk.cyan.bold("๐Ÿš€ Prisma Postgres Create DB")); + log.message( + chalk.white(`Provisioning a temporary database in ${region}...`) + ); + log.message(""); + log.message( + chalk.gray( + `It will be automatically deleted in 24 hours, but you can claim it.` + ) + ); + log.message(""); + // Interactive mode prompts if (chooseRegionPrompt) { - intro(chalk.cyan.bold("๐Ÿš€ Prisma Postgres Create DB")); - - log.message( - chalk.gray( - `We'll create a temporary Prisma Postgres database (valid for 24 hours).\n` + - `You can claim it to make it permanent or use it for prototyping.` - ) - ); - log.message(""); - // Prompt for region region = await promptForRegion(region); - } else { - // Show minimal header for non-interactive mode - log.info(chalk.cyan.bold("๐Ÿš€ Prisma Postgres Create DB")); - log.message( - chalk.gray( - `Creating a temporary Prisma Postgres database in ${region}...` - ) - ); - log.message(""); } // Validate the region diff --git a/create-pg/.gitignore b/create-pg/.gitignore index 941d536..9a9c9bc 100644 --- a/create-pg/.gitignore +++ b/create-pg/.gitignore @@ -1,3 +1,4 @@ .env node_modules -package-lock.json \ No newline at end of file +package-lock.json +README.md \ No newline at end of file diff --git a/create-postgres/.gitignore b/create-postgres/.gitignore index 941d536..9a9c9bc 100644 --- a/create-postgres/.gitignore +++ b/create-postgres/.gitignore @@ -1,3 +1,4 @@ .env node_modules -package-lock.json \ No newline at end of file +package-lock.json +README.md \ No newline at end of file