From ba3fb61123ababeefedc2ba8d578b7eaea985bd3 Mon Sep 17 00:00:00 2001 From: Ankur Datta <64993082+ankur-arch@users.noreply.github.com> Date: Wed, 29 Jul 2026 18:35:39 +0200 Subject: [PATCH 1/4] docs(compute): add Deploy Button page and generator Adds a Deploy Button reference page under Compute integrations: what the button does, supported templates, URL format with attribution parameters, troubleshooting, and copy-paste Markdown/HTML snippets. Includes an interactive generator component and a hosted "Deploy with Prisma" SVG badge for third-party READMEs to hot-link. Co-Authored-By: Claude Fable 5 --- .../content/docs/compute/deploy-button.mdx | 110 ++++++++++++ apps/docs/content/docs/compute/meta.json | 1 + apps/docs/public/img/deploy-button.svg | 8 + .../components/deploy-button-generator.tsx | 160 ++++++++++++++++++ apps/docs/src/mdx-components.tsx | 2 + 5 files changed, 281 insertions(+) create mode 100644 apps/docs/content/docs/compute/deploy-button.mdx create mode 100644 apps/docs/public/img/deploy-button.svg create mode 100644 apps/docs/src/components/deploy-button-generator.tsx diff --git a/apps/docs/content/docs/compute/deploy-button.mdx b/apps/docs/content/docs/compute/deploy-button.mdx new file mode 100644 index 0000000000..d860b7b666 --- /dev/null +++ b/apps/docs/content/docs/compute/deploy-button.mdx @@ -0,0 +1,110 @@ +--- +title: Deploy Button +description: Let anyone deploy a Prisma template to Prisma Compute in one click — the button copies the code into their GitHub account, provisions a Prisma Postgres database, and starts the first deployment. +url: /compute/deploy-button +metaTitle: Deploy Button | Prisma Compute +metaDescription: Add a Deploy with Prisma button to your README or website. One click copies a template into the user's GitHub account, provisions Prisma Postgres, and deploys it on Prisma Compute. +--- + +The Deploy Button lets anyone go from a template to a running app in one click. Clicking it opens the Prisma Console, where the user picks a GitHub account and a region — Prisma then copies the template's code into a new repository they own, creates a project with a [Prisma Postgres](/postgres) database, and starts the first deployment on [Prisma Compute](/compute). + +Add it to a README, a blog post, or your own site: + +[![Deploy with Prisma](/img/deploy-button.svg)](https://console.prisma.io/templates/hono?utm_source=docs) + +:::note +The Deploy Button is in early access. It currently supports official Prisma templates; support for your own repositories is planned. +::: + +## How it works + +1. **Click the button.** The user lands on the template's page in the Prisma Console. If they're signed out, they sign in (or sign up) first and return to the same page automatically. +2. **Choose where it goes.** They pick a GitHub account or organization, a repository name and visibility, and a deployment region. If the Prisma GitHub App isn't installed yet, the flow walks them through installing it. +3. **Authorize once.** GitHub asks the user to authorize the repository creation. This authorization is used for that single deployment and is not stored. +4. **Prisma sets everything up.** A new repository is created under the user's account with the template's code, a project with a Prisma Postgres database is provisioned, the database connection is configured automatically, and the first deployment starts. +5. **Push to deploy.** The repository is connected to the project, so every later push deploys through the normal Git workflow. + +The user owns the repository and the project. Prisma adds no credentials or platform files to the repository — database connection settings are managed in the Console, not committed to code. + +## Supported templates + +The button works with official Prisma templates: + +| Template | ID | Description | +| --- | --- | --- | +| Hono API | `hono` | A lightweight API server with Prisma ORM and Prisma Postgres | +| Next.js | `nextjs` | A full-stack Next.js app with Prisma ORM and Prisma Postgres | +| TanStack Start | `tanstack-start` | A TanStack Start app with Prisma ORM and Prisma Postgres | + +Arbitrary repositories are not supported yet — the button can only point at templates from the official catalog. + +## Requirements + +For the developer adding the button: none. The button is a link — no registration, API key, or configuration is needed. + +For the user clicking it: + +- A [Prisma Data Platform account](https://pris.ly/pdp) (free to create during the flow). +- A GitHub account. The Prisma GitHub App must be installed on the account or organization that will own the new repository — the flow offers the installation step when it's missing. +- Permission to create repositories under the selected account or organization. + +No environment variables need to be configured up front: the database connection is provisioned and wired automatically. Anything app-specific can be added in the Console after the first deployment — see [environment variables](/compute/environment-variables). + +## URL format + +A Deploy Button is a link to: + +```text +https://console.prisma.io/templates/ +``` + +| Parameter | Where | Required | Description | +| --- | --- | --- | --- | +| `` | path | Yes | The ID of an official template, for example `hono`, `nextjs`, or `tanstack-start`. | +| `utm_source` | query | No | Attribution for where the click came from, for example `github-readme`. | +| `utm_medium` | query | No | Attribution for the kind of placement, for example `button`. | +| `utm_campaign` | query | No | Attribution for a specific campaign or launch. | + +Example with attribution: + +```text +https://console.prisma.io/templates/nextjs?utm_source=github-readme&utm_campaign=launch +``` + +## Create your button + +Pick a template, optionally add attribution, and copy the generated snippet: + + + +## Snippets + +A complete Markdown example for a README: + +```md +[![Deploy with Prisma](https://www.prisma.io/docs/img/deploy-button.svg)](https://console.prisma.io/templates/hono?utm_source=github-readme) +``` + +The same button in HTML: + +```html + + Deploy with Prisma + +``` + +Both render as: [![Deploy with Prisma](/img/deploy-button.svg)](https://console.prisma.io/templates/hono?utm_source=docs) + +## Troubleshooting + +**"Template not found" or a 404 page.** Check the template ID in the URL against the [supported templates](#supported-templates) table. During early access, the templates gallery may also not be enabled for every account yet. + +**"A repository with this name already exists."** The user already has a repository with the chosen name. Pick a different repository name in the form and deploy again. + +**GitHub authorization was canceled or expired.** The authorization step must be completed within a few minutes. Returning to the template page and clicking Deploy again starts a fresh attempt. + +**The GitHub account isn't listed.** The Prisma GitHub App isn't installed on that account or organization. Use the "Install GitHub App" option in the deploy form, complete the installation on GitHub, and continue from the template page. + +**The repository was created, but the deployment didn't start.** The repository and project are preserved — nothing is deleted on a partial failure. Pushing any commit to the repository's default branch triggers a deployment through the normal Git flow. See [deployments](/compute/deployments) for how builds and deployments are surfaced. + +**The app deployed but shows an error page.** Check the build and runtime logs on the project's deployment page, and confirm the database finished provisioning in the Console. The [FAQ](/compute/faq) covers common runtime issues. diff --git a/apps/docs/content/docs/compute/meta.json b/apps/docs/content/docs/compute/meta.json index 9a606f5fb0..7aaae82a92 100644 --- a/apps/docs/content/docs/compute/meta.json +++ b/apps/docs/content/docs/compute/meta.json @@ -15,6 +15,7 @@ "domains", "---Integrations---", "github", + "deploy-button", "---Reference---", "configuration", "cli-reference", diff --git a/apps/docs/public/img/deploy-button.svg b/apps/docs/public/img/deploy-button.svg new file mode 100644 index 0000000000..1582425a94 --- /dev/null +++ b/apps/docs/public/img/deploy-button.svg @@ -0,0 +1,8 @@ + + Deploy with Prisma + + + + + Deploy with Prisma + diff --git a/apps/docs/src/components/deploy-button-generator.tsx b/apps/docs/src/components/deploy-button-generator.tsx new file mode 100644 index 0000000000..ff17b2ee18 --- /dev/null +++ b/apps/docs/src/components/deploy-button-generator.tsx @@ -0,0 +1,160 @@ +"use client"; +import { useMemo, useState } from "react"; +import { useCopyButton } from "fumadocs-ui/utils/use-copy-button"; +import { buttonVariants } from "@prisma-docs/ui/components/button"; +import { cn } from "@prisma-docs/ui/lib/cn"; +import { withDocsBasePath } from "@/lib/urls"; + +const CONSOLE_TEMPLATES_URL = "https://console.prisma.io/templates"; +const BUTTON_IMAGE_URL = "https://www.prisma.io/docs/img/deploy-button.svg"; + +const TEMPLATES = [ + { id: "hono", label: "Hono API" }, + { id: "nextjs", label: "Next.js" }, + { id: "tanstack-start", label: "TanStack Start" }, +]; + +const CUSTOM_TEMPLATE = "__custom"; + +const TEMPLATE_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/; + +function CopyButton({ value }: { value: string }) { + const [checked, onClick] = useCopyButton(() => navigator.clipboard.writeText(value)); + + return ( + + ); +} + +function Snippet({ label, value }: { label: string; value: string }) { + return ( +
+ {label} +
+
+          {value}
+        
+ +
+
+ ); +} + +const fieldClassName = + "w-full rounded-lg border bg-fd-background px-3 py-2 text-sm text-fd-foreground outline-none focus-visible:ring-2 focus-visible:ring-fd-ring"; + +export function DeployButtonGenerator() { + const [selectedTemplate, setSelectedTemplate] = useState(TEMPLATES[0].id); + const [customTemplateId, setCustomTemplateId] = useState(""); + const [utmSource, setUtmSource] = useState(""); + const [utmCampaign, setUtmCampaign] = useState(""); + + const templateId = + selectedTemplate === CUSTOM_TEMPLATE ? customTemplateId.trim() : selectedTemplate; + const templateIdValid = TEMPLATE_ID_PATTERN.test(templateId) && templateId.length <= 64; + + const url = useMemo(() => { + if (!templateIdValid) return null; + const search = new URLSearchParams(); + if (utmSource.trim()) search.set("utm_source", utmSource.trim()); + if (utmCampaign.trim()) search.set("utm_campaign", utmCampaign.trim()); + const query = search.toString(); + return `${CONSOLE_TEMPLATES_URL}/${templateId}${query ? `?${query}` : ""}`; + }, [templateIdValid, templateId, utmSource, utmCampaign]); + + return ( +
+
+ + {selectedTemplate === CUSTOM_TEMPLATE && ( + + )} + + +
+ + {url ? ( + <> + + + + Deploy with Prisma`} + /> + + ) : ( +

+ Enter a template ID using lowercase letters, numbers, and hyphens (for example{" "} + tanstack-start) to generate your button. +

+ )} +
+ ); +} diff --git a/apps/docs/src/mdx-components.tsx b/apps/docs/src/mdx-components.tsx index 5bc64070fd..52f482639f 100644 --- a/apps/docs/src/mdx-components.tsx +++ b/apps/docs/src/mdx-components.tsx @@ -2,6 +2,7 @@ import defaultMdxComponents from "fumadocs-ui/mdx"; import { Youtube } from "@prisma-docs/ui/components/youtube"; import { APIPage } from "@/components/api-page"; import { ConceptAnimation } from "@/components/concept-animation"; +import { DeployButtonGenerator } from "@/components/deploy-button-generator"; import { AgentPrompt, GetStartedTabs, @@ -121,6 +122,7 @@ export function getMDXComponents(components?: MDXComponents): MDXComponents { APIPage, AgentPrompt, ConceptAnimation, + DeployButtonGenerator, GetStartedTabs, HeroGrid, HeroPitch, From 7dc58b6bfef63511f0fde4239d1186c5346d6360 Mon Sep 17 00:00:00 2001 From: Ankur Datta <64993082+ankur-arch@users.noreply.github.com> Date: Thu, 30 Jul 2026 14:21:07 +0200 Subject: [PATCH 2/4] docs(compute): make the Deploy Button repository-first MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The button's contract is now a repository URL: any public GitHub repository with a root prisma.compute.json, a package.json, and a lockfile is deployable — no registration or catalog entry. The page documents the minimal repository contract and the /new/clone?repository-url=… format, and the generator takes a repository URL plus optional project-name and attribution. Co-Authored-By: Claude Fable 5 --- .../content/docs/compute/deploy-button.mdx | 98 +++++++++-------- .../components/deploy-button-generator.tsx | 101 ++++++++++-------- 2 files changed, 111 insertions(+), 88 deletions(-) diff --git a/apps/docs/content/docs/compute/deploy-button.mdx b/apps/docs/content/docs/compute/deploy-button.mdx index d860b7b666..8cdbe5b831 100644 --- a/apps/docs/content/docs/compute/deploy-button.mdx +++ b/apps/docs/content/docs/compute/deploy-button.mdx @@ -1,79 +1,87 @@ --- title: Deploy Button -description: Let anyone deploy a Prisma template to Prisma Compute in one click — the button copies the code into their GitHub account, provisions a Prisma Postgres database, and starts the first deployment. +description: Add a Deploy with Prisma button to your repository — one click copies your project into the user's GitHub account, provisions a Prisma Postgres database, and deploys it on Prisma Compute. url: /compute/deploy-button metaTitle: Deploy Button | Prisma Compute -metaDescription: Add a Deploy with Prisma button to your README or website. One click copies a template into the user's GitHub account, provisions Prisma Postgres, and deploys it on Prisma Compute. +metaDescription: Let anyone deploy your TypeScript project in one click. The Deploy with Prisma button clones your public GitHub repository, provisions Prisma Postgres, and deploys on Prisma Compute. --- -The Deploy Button lets anyone go from a template to a running app in one click. Clicking it opens the Prisma Console, where the user picks a GitHub account and a region — Prisma then copies the template's code into a new repository they own, creates a project with a [Prisma Postgres](/postgres) database, and starts the first deployment on [Prisma Compute](/compute). +The Deploy Button lets anyone deploy your project in one click. Clicking it opens the Prisma Console, where the user picks a GitHub account and a region — Prisma then copies your repository's code into a new repository they own, creates a project with a [Prisma Postgres](/postgres) database, and starts the first deployment on [Prisma Compute](/compute). -Add it to a README, a blog post, or your own site: +Add it to your README, a blog post, or your own site: -[![Deploy with Prisma](/img/deploy-button.svg)](https://console.prisma.io/templates/hono?utm_source=docs) +[![Deploy with Prisma](/img/deploy-button.svg)](https://console.prisma.io/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fprisma%2Fcompute-deploy-example&utm_source=docs) :::note -The Deploy Button is in early access. It currently supports official Prisma templates; support for your own repositories is planned. +The Deploy Button is in early access. It supports public GitHub repositories; support for private repositories is planned. ::: -## How it works +## Make your repository deployable -1. **Click the button.** The user lands on the template's page in the Prisma Console. If they're signed out, they sign in (or sign up) first and return to the same page automatically. -2. **Choose where it goes.** They pick a GitHub account or organization, a repository name and visibility, and a deployment region. If the Prisma GitHub App isn't installed yet, the flow walks them through installing it. -3. **Authorize once.** GitHub asks the user to authorize the repository creation. This authorization is used for that single deployment and is not stored. -4. **Prisma sets everything up.** A new repository is created under the user's account with the template's code, a project with a Prisma Postgres database is provisioned, the database connection is configured automatically, and the first deployment starts. -5. **Push to deploy.** The repository is connected to the project, so every later push deploys through the normal Git workflow. +Any public GitHub repository works with the Deploy Button when it meets this minimal contract: -The user owns the repository and the project. Prisma adds no credentials or platform files to the repository — database connection settings are managed in the Console, not committed to code. +- **Public GitHub repository.** The button deploys the repository's default branch. +- **A `prisma.compute.json` file at the repository root.** This is the same file Prisma Compute reads for any connected repository — it names the app and sets its HTTP port. Don't set a region in it; the person deploying picks the region, and it's used for both the database and the app. See [configuration](/compute/configuration). +- **A TypeScript project with a `package.json` and a lockfile** (`bun.lock`, `package-lock.json`, `pnpm-lock.yaml`, or `yarn.lock`) at the root. If it builds on Prisma Compute, it deploys. -## Supported templates +That's it — no registration, API key, or approval process. A minimal working example you can copy is [`prisma/compute-deploy-example`](https://github.com/prisma/compute-deploy-example), whose README carries its own Deploy Button. -The button works with official Prisma templates: +A minimal `prisma.compute.json`: -| Template | ID | Description | -| --- | --- | --- | -| Hono API | `hono` | A lightweight API server with Prisma ORM and Prisma Postgres | -| Next.js | `nextjs` | A full-stack Next.js app with Prisma ORM and Prisma Postgres | -| TanStack Start | `tanstack-start` | A TanStack Start app with Prisma ORM and Prisma Postgres | +```json +{ + "name": "my-app", + "port": 3000 +} +``` -Arbitrary repositories are not supported yet — the button can only point at templates from the official catalog. +## How it works + +1. **Click the button.** The user lands on the clone page in the Prisma Console, which always shows exactly what will be deployed: your repository's name and the resolved commit, linked back to the source on GitHub. If they're signed out, they sign in first and return to the same page automatically. +2. **Choose where it goes.** They pick a GitHub account or organization, a name and visibility for their new repository, and a deployment region. If the Prisma GitHub App isn't installed yet, the flow walks them through installing it. +3. **Authorize once.** GitHub asks the user to authorize the repository creation. This authorization is used for that single deployment and is not stored. +4. **Prisma sets everything up.** Your code is copied — at the exact commit shown on the clone page — into a new repository under the user's account. A project with a Prisma Postgres database is provisioned, the database connection is configured automatically, and the first deployment starts. +5. **Push to deploy.** The new repository is connected to the project, so every later push deploys through the normal Git workflow. + +The user owns the copy and the project. Prisma adds no credentials or platform files to the repository — database connection settings are managed in the Console, not committed to code. ## Requirements -For the developer adding the button: none. The button is a link — no registration, API key, or configuration is needed. +For you, the author: only the [repository contract](#make-your-repository-deployable) above. -For the user clicking it: +For the user clicking the button: - A [Prisma Data Platform account](https://pris.ly/pdp) (free to create during the flow). -- A GitHub account. The Prisma GitHub App must be installed on the account or organization that will own the new repository — the flow offers the installation step when it's missing. +- A GitHub account with the Prisma GitHub App installed on the account or organization that will own the new repository — the flow offers the installation step when it's missing. - Permission to create repositories under the selected account or organization. -No environment variables need to be configured up front: the database connection is provisioned and wired automatically. Anything app-specific can be added in the Console after the first deployment — see [environment variables](/compute/environment-variables). +No environment variables need to be configured up front: `DATABASE_URL` is provisioned and wired automatically. Anything app-specific can be added in the Console after the first deployment — see [environment variables](/compute/environment-variables). ## URL format A Deploy Button is a link to: ```text -https://console.prisma.io/templates/ +https://console.prisma.io/new/clone?repository-url= ``` -| Parameter | Where | Required | Description | -| --- | --- | --- | --- | -| `` | path | Yes | The ID of an official template, for example `hono`, `nextjs`, or `tanstack-start`. | -| `utm_source` | query | No | Attribution for where the click came from, for example `github-readme`. | -| `utm_medium` | query | No | Attribution for the kind of placement, for example `button`. | -| `utm_campaign` | query | No | Attribution for a specific campaign or launch. | +| Parameter | Required | Description | +| --- | --- | --- | +| `repository-url` | Yes | The public GitHub repository to deploy, in the form `https://github.com/owner/repo`. URL-encode it when composing the link. | +| `project-name` | No | Prefills the name of the repository the user creates (they can change it). Defaults to your repository's name. | +| `utm_source` | No | Attribution for where the click came from, for example `github-readme`. | +| `utm_medium` | No | Attribution for the kind of placement, for example `button`. | +| `utm_campaign` | No | Attribution for a specific campaign or launch. | -Example with attribution: +Example with a project name and attribution: ```text -https://console.prisma.io/templates/nextjs?utm_source=github-readme&utm_campaign=launch +https://console.prisma.io/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Facme%2Fshop-api&project-name=shop-api&utm_source=github-readme ``` ## Create your button -Pick a template, optionally add attribution, and copy the generated snippet: +Enter your repository URL and copy the generated snippet: @@ -82,29 +90,29 @@ Pick a template, optionally add attribution, and copy the generated snippet: A complete Markdown example for a README: ```md -[![Deploy with Prisma](https://www.prisma.io/docs/img/deploy-button.svg)](https://console.prisma.io/templates/hono?utm_source=github-readme) +[![Deploy with Prisma](https://www.prisma.io/docs/img/deploy-button.svg)](https://console.prisma.io/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Facme%2Fshop-api&utm_source=github-readme) ``` The same button in HTML: ```html - + Deploy with Prisma ``` -Both render as: [![Deploy with Prisma](/img/deploy-button.svg)](https://console.prisma.io/templates/hono?utm_source=docs) - ## Troubleshooting -**"Template not found" or a 404 page.** Check the template ID in the URL against the [supported templates](#supported-templates) table. During early access, the templates gallery may also not be enabled for every account yet. +**"This repository cannot be deployed" with a message about `prisma.compute.json`.** The repository is missing the root configuration file. Add a `prisma.compute.json` at the repository root — see [make your repository deployable](#make-your-repository-deployable). + +**The clone page says the repository must be public.** The button only supports public source repositories today. The user's *copy* can still be private — that's their choice in the deploy form. -**"A repository with this name already exists."** The user already has a repository with the chosen name. Pick a different repository name in the form and deploy again. +**"A repository with this name already exists."** The user already has a repository with the chosen name. They can pick a different name in the form and deploy again. -**GitHub authorization was canceled or expired.** The authorization step must be completed within a few minutes. Returning to the template page and clicking Deploy again starts a fresh attempt. +**GitHub authorization was canceled or expired.** The authorization step must be completed within a few minutes. Returning to the clone page and clicking Deploy again starts a fresh attempt. -**The GitHub account isn't listed.** The Prisma GitHub App isn't installed on that account or organization. Use the "Install GitHub App" option in the deploy form, complete the installation on GitHub, and continue from the template page. +**The GitHub account isn't listed.** The Prisma GitHub App isn't installed on that account or organization. The "Connect GitHub" option in the deploy form starts the installation; after completing it on GitHub, the user continues from the clone page. -**The repository was created, but the deployment didn't start.** The repository and project are preserved — nothing is deleted on a partial failure. Pushing any commit to the repository's default branch triggers a deployment through the normal Git flow. See [deployments](/compute/deployments) for how builds and deployments are surfaced. +**The repository was created, but the deployment didn't start.** The repository and project are preserved — nothing is deleted on a partial failure. Pushing any commit to the new repository's default branch triggers a deployment through the normal Git flow. See [deployments](/compute/deployments). -**The app deployed but shows an error page.** Check the build and runtime logs on the project's deployment page, and confirm the database finished provisioning in the Console. The [FAQ](/compute/faq) covers common runtime issues. +**The app deployed but shows an error page.** Check the build and runtime logs on the project's deployment page, confirm the database finished provisioning in the Console, and verify the app listens on the port declared in `prisma.compute.json`. The [FAQ](/compute/faq) covers common runtime issues. diff --git a/apps/docs/src/components/deploy-button-generator.tsx b/apps/docs/src/components/deploy-button-generator.tsx index ff17b2ee18..5f1e1a8fe8 100644 --- a/apps/docs/src/components/deploy-button-generator.tsx +++ b/apps/docs/src/components/deploy-button-generator.tsx @@ -5,18 +5,33 @@ import { buttonVariants } from "@prisma-docs/ui/components/button"; import { cn } from "@prisma-docs/ui/lib/cn"; import { withDocsBasePath } from "@/lib/urls"; -const CONSOLE_TEMPLATES_URL = "https://console.prisma.io/templates"; +const CONSOLE_CLONE_URL = "https://console.prisma.io/new/clone"; const BUTTON_IMAGE_URL = "https://www.prisma.io/docs/img/deploy-button.svg"; -const TEMPLATES = [ - { id: "hono", label: "Hono API" }, - { id: "nextjs", label: "Next.js" }, - { id: "tanstack-start", label: "TanStack Start" }, -]; +const OWNER_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9]|-(?=[A-Za-z0-9])){0,38}$/; +const REPO_PATTERN = /^[A-Za-z0-9._-]{1,100}$/; +const PROJECT_NAME_PATTERN = /^[A-Za-z0-9._-]{1,100}$/; -const CUSTOM_TEMPLATE = "__custom"; - -const TEMPLATE_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/; +function parseRepositoryUrl(raw: string): { owner: string; repo: string } | null { + const value = raw.trim(); + if (value.length === 0 || value.length > 300) return null; + let url: URL; + try { + url = new URL(value.includes("://") ? value : `https://${value}`); + } catch { + return null; + } + if (url.protocol !== "https:" || url.hostname !== "github.com" || url.port) return null; + if (url.username || url.password || url.search || url.hash) return null; + const segments = url.pathname.split("/").filter(Boolean); + if (segments.length !== 2) return null; + const owner = segments[0]; + let repo = segments[1]; + if (repo.endsWith(".git")) repo = repo.slice(0, -4); + if (!OWNER_PATTERN.test(owner) || !REPO_PATTERN.test(repo)) return null; + if (repo === "." || repo === "..") return null; + return { owner, repo }; +} function CopyButton({ value }: { value: string }) { const [checked, onClick] = useCopyButton(() => navigator.clipboard.writeText(value)); @@ -54,53 +69,53 @@ const fieldClassName = "w-full rounded-lg border bg-fd-background px-3 py-2 text-sm text-fd-foreground outline-none focus-visible:ring-2 focus-visible:ring-fd-ring"; export function DeployButtonGenerator() { - const [selectedTemplate, setSelectedTemplate] = useState(TEMPLATES[0].id); - const [customTemplateId, setCustomTemplateId] = useState(""); + const [repositoryUrl, setRepositoryUrl] = useState(""); + const [projectName, setProjectName] = useState(""); const [utmSource, setUtmSource] = useState(""); const [utmCampaign, setUtmCampaign] = useState(""); - const templateId = - selectedTemplate === CUSTOM_TEMPLATE ? customTemplateId.trim() : selectedTemplate; - const templateIdValid = TEMPLATE_ID_PATTERN.test(templateId) && templateId.length <= 64; + const parsed = useMemo(() => parseRepositoryUrl(repositoryUrl), [repositoryUrl]); + const projectNameValid = + projectName.trim() === "" || PROJECT_NAME_PATTERN.test(projectName.trim()); const url = useMemo(() => { - if (!templateIdValid) return null; + if (!parsed || !projectNameValid) return null; const search = new URLSearchParams(); + search.set("repository-url", `https://github.com/${parsed.owner}/${parsed.repo}`); + if (projectName.trim()) search.set("project-name", projectName.trim()); if (utmSource.trim()) search.set("utm_source", utmSource.trim()); if (utmCampaign.trim()) search.set("utm_campaign", utmCampaign.trim()); - const query = search.toString(); - return `${CONSOLE_TEMPLATES_URL}/${templateId}${query ? `?${query}` : ""}`; - }, [templateIdValid, templateId, utmSource, utmCampaign]); + return `${CONSOLE_CLONE_URL}?${search.toString()}`; + }, [parsed, projectNameValid, projectName, utmSource, utmCampaign]); return (
+ - {selectedTemplate === CUSTOM_TEMPLATE && ( - - )}
From 8619b8b9dffcb606bfce093660d80f3b99369e06 Mon Sep 17 00:00:00 2001 From: Ankur Datta <64993082+ankur-arch@users.noreply.github.com> Date: Thu, 30 Jul 2026 15:11:53 +0200 Subject: [PATCH 3/4] fix(docs): correct Deploy Button contract details from Console audit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - prisma.compute.json example uses the real schema (app.httpPort, not top-level name/port — the old example failed validation verbatim). - Document the validation limits (file count/size caps, no workflows, no symlinks/submodules, single-app, region key rejected). - Match the Console's actual copy for the name-conflict error, the 15-minute authorization window, commit display and pinning behavior, the workspace picker, and the private-by-default visibility. - Qualify push-to-recover advice by whether the project was created. - Generator: project-name field shows its own validation hint, copy buttons get distinct accessible names, HTML snippet escapes ampersands in the href. Co-Authored-By: Claude Fable 5 --- .../content/docs/compute/deploy-button.mdx | 38 +++++++++++-------- .../components/deploy-button-generator.tsx | 26 ++++++++++--- 2 files changed, 44 insertions(+), 20 deletions(-) diff --git a/apps/docs/content/docs/compute/deploy-button.mdx b/apps/docs/content/docs/compute/deploy-button.mdx index 8cdbe5b831..3925f6b761 100644 --- a/apps/docs/content/docs/compute/deploy-button.mdx +++ b/apps/docs/content/docs/compute/deploy-button.mdx @@ -21,26 +21,34 @@ The Deploy Button is in early access. It supports public GitHub repositories; su Any public GitHub repository works with the Deploy Button when it meets this minimal contract: - **Public GitHub repository.** The button deploys the repository's default branch. -- **A `prisma.compute.json` file at the repository root.** This is the same file Prisma Compute reads for any connected repository — it names the app and sets its HTTP port. Don't set a region in it; the person deploying picks the region, and it's used for both the database and the app. See [configuration](/compute/configuration). -- **A TypeScript project with a `package.json` and a lockfile** (`bun.lock`, `package-lock.json`, `pnpm-lock.yaml`, or `yarn.lock`) at the root. If it builds on Prisma Compute, it deploys. +- **A `prisma.compute.json` file at the repository root.** This is the same file Prisma Compute reads for any connected repository — it names the app and sets its HTTP port. It must describe a single app, and it must not set a `region`: the person deploying picks the region, which is used for both the database and the app. See [configuration](/compute/configuration). +- **A TypeScript project with a `package.json` and a lockfile** (`bun.lock`, `package-lock.json`, `pnpm-lock.yaml`, or `yarn.lock`) at the root. -That's it — no registration, API key, or approval process. A minimal working example you can copy is [`prisma/compute-deploy-example`](https://github.com/prisma/compute-deploy-example), whose README carries its own Deploy Button. +The repository is validated before it is copied, so a few shapes are rejected: + +- More than 100 files, a file over 5 MB, or more than 25 MB in total. +- GitHub Actions workflows (anything under `.github/workflows/`). +- Symlinks or Git submodules. + +There is no registration, API key, or approval process. A minimal working example you can copy is [`prisma/compute-deploy-example`](https://github.com/prisma/compute-deploy-example), whose README carries its own Deploy Button. A minimal `prisma.compute.json`: ```json { - "name": "my-app", - "port": 3000 + "app": { + "name": "my-app", + "httpPort": 3000 + } } ``` ## How it works -1. **Click the button.** The user lands on the clone page in the Prisma Console, which always shows exactly what will be deployed: your repository's name and the resolved commit, linked back to the source on GitHub. If they're signed out, they sign in first and return to the same page automatically. -2. **Choose where it goes.** They pick a GitHub account or organization, a name and visibility for their new repository, and a deployment region. If the Prisma GitHub App isn't installed yet, the flow walks them through installing it. +1. **Click the button.** The user lands on the clone page in the Prisma Console, which shows what will be deployed: your repository's name — and, once a GitHub account is connected, the resolved commit — linked back to the source on GitHub. If they're signed out, they sign in first and return to the same page automatically. +2. **Choose where it goes.** They pick a workspace (when they have more than one), a GitHub account or organization, a name and visibility for their new repository (private by default), and a deployment region. If the Prisma GitHub App isn't installed yet, the flow walks them through installing it. 3. **Authorize once.** GitHub asks the user to authorize the repository creation. This authorization is used for that single deployment and is not stored. -4. **Prisma sets everything up.** Your code is copied — at the exact commit shown on the clone page — into a new repository under the user's account. A project with a Prisma Postgres database is provisioned, the database connection is configured automatically, and the first deployment starts. +4. **Prisma sets everything up.** Your code is copied — pinned to the default-branch commit resolved when they click Deploy — into a new repository under the user's account. A project with a Prisma Postgres database is provisioned, the database connection is configured automatically, and the first deployment starts. 5. **Push to deploy.** The new repository is connected to the project, so every later push deploys through the normal Git workflow. The user owns the copy and the project. Prisma adds no credentials or platform files to the repository — database connection settings are managed in the Console, not committed to code. @@ -69,9 +77,9 @@ https://console.prisma.io/new/clone?repository-url= | --- | --- | --- | | `repository-url` | Yes | The public GitHub repository to deploy, in the form `https://github.com/owner/repo`. URL-encode it when composing the link. | | `project-name` | No | Prefills the name of the repository the user creates (they can change it). Defaults to your repository's name. | -| `utm_source` | No | Attribution for where the click came from, for example `github-readme`. | -| `utm_medium` | No | Attribution for the kind of placement, for example `button`. | -| `utm_campaign` | No | Attribution for a specific campaign or launch. | +| `utm_source` | No | Attribution for where the click came from, for example `github-readme`. This is the value Prisma's own funnel reporting records. | +| `utm_medium` | No | Attribution for the kind of placement, for example `button`. Carried on the link for your own analytics. | +| `utm_campaign` | No | Attribution for a specific campaign or launch. Carried on the link for your own analytics. | Example with a project name and attribution: @@ -96,7 +104,7 @@ A complete Markdown example for a README: The same button in HTML: ```html - + Deploy with Prisma ``` @@ -107,12 +115,12 @@ The same button in HTML: **The clone page says the repository must be public.** The button only supports public source repositories today. The user's *copy* can still be private — that's their choice in the deploy form. -**"A repository with this name already exists."** The user already has a repository with the chosen name. They can pick a different name in the form and deploy again. +**"The repository changed before Prisma could copy the template. Choose another repository name and try again."** The chosen name usually collides with a repository the user already has. Picking a different name in the form and deploying again resolves it. -**GitHub authorization was canceled or expired.** The authorization step must be completed within a few minutes. Returning to the clone page and clicking Deploy again starts a fresh attempt. +**GitHub authorization was canceled or expired.** The authorization step must be completed within 15 minutes. Returning to the clone page and clicking Deploy again starts a fresh attempt. **The GitHub account isn't listed.** The Prisma GitHub App isn't installed on that account or organization. The "Connect GitHub" option in the deploy form starts the installation; after completing it on GitHub, the user continues from the clone page. -**The repository was created, but the deployment didn't start.** The repository and project are preserved — nothing is deleted on a partial failure. Pushing any commit to the new repository's default branch triggers a deployment through the normal Git flow. See [deployments](/compute/deployments). +**The repository was created, but the deployment didn't start.** Nothing is deleted on a partial failure. If the Prisma project was created and connected to the repository, pushing any commit to the new repository's default branch triggers a deployment through the normal Git flow. If the failure happened before the project existed, return to the clone page and deploy again with a different repository name. See [deployments](/compute/deployments). **The app deployed but shows an error page.** Check the build and runtime logs on the project's deployment page, confirm the database finished provisioning in the Console, and verify the app listens on the port declared in `prisma.compute.json`. The [FAQ](/compute/faq) covers common runtime issues. diff --git a/apps/docs/src/components/deploy-button-generator.tsx b/apps/docs/src/components/deploy-button-generator.tsx index 5f1e1a8fe8..09afa6f432 100644 --- a/apps/docs/src/components/deploy-button-generator.tsx +++ b/apps/docs/src/components/deploy-button-generator.tsx @@ -33,13 +33,13 @@ function parseRepositoryUrl(raw: string): { owner: string; repo: string } | null return { owner, repo }; } -function CopyButton({ value }: { value: string }) { +function CopyButton({ value, label }: { value: string; label: string }) { const [checked, onClick] = useCopyButton(() => navigator.clipboard.writeText(value)); return (
); } +function escapeHtmlAttribute(value: string): string { + return value.replaceAll("&", "&").replaceAll('"', """); +} + const fieldClassName = "w-full rounded-lg border bg-fd-background px-3 py-2 text-sm text-fd-foreground outline-none focus-visible:ring-2 focus-visible:ring-fd-ring"; @@ -97,10 +101,15 @@ export function DeployButtonGenerator() { className={fieldClassName} placeholder="https://github.com/owner/repo" value={repositoryUrl} + aria-invalid={repositoryUrl.trim() !== "" && !parsed} + aria-describedby="deploy-button-repository-url-hint" onChange={(event) => setRepositoryUrl(event.target.value)} /> {repositoryUrl.trim() !== "" && !parsed ? ( - + Enter a public GitHub repository URL like https://github.com/owner/repo. ) : null} @@ -113,8 +122,15 @@ export function DeployButtonGenerator() { className={fieldClassName} placeholder="my-app" value={projectName} + aria-invalid={!projectNameValid} + aria-describedby="deploy-button-project-name-hint" onChange={(event) => setProjectName(event.target.value)} /> + {!projectNameValid ? ( + + Use up to 100 letters, numbers, dots, dashes, or underscores. + + ) : null}