-
Notifications
You must be signed in to change notification settings - Fork 975
docs(compute): Deploy Button page and generator (repository-url) #8116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
ankur-arch
wants to merge
5
commits into
main
Choose a base branch
from
feat/compute-deploy-button-docs
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 2 commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
ba3fb61
docs(compute): add Deploy Button page and generator
ankur-arch 7dc58b6
docs(compute): make the Deploy Button repository-first
ankur-arch 8619b8b
fix(docs): correct Deploy Button contract details from Console audit
ankur-arch 23cf20d
Merge branch 'main' into feat/compute-deploy-button-docs
ankur-arch 0625347
docs(compute): env-var support in the Deploy Button page and generator
ankur-arch File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| --- | ||
| title: Deploy Button | ||
| 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: 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 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 your README, a blog post, or your own site: | ||
|
|
||
| [](https://console.prisma.io/new/clone?repository-url=https%3A%2F%2Fgithub.qkg1.top%2Fprisma%2Fcompute-deploy-example&utm_source=docs) | ||
|
|
||
| :::note | ||
| The Deploy Button is in early access. It supports public GitHub repositories; support for private repositories is planned. | ||
| ::: | ||
|
|
||
| ## Make your repository deployable | ||
|
|
||
| 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. | ||
|
|
||
| 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. | ||
|
|
||
| A minimal `prisma.compute.json`: | ||
|
|
||
| ```json | ||
| { | ||
| "name": "my-app", | ||
| "port": 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. | ||
| 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 you, the author: only the [repository contract](#make-your-repository-deployable) above. | ||
|
|
||
| For the user clicking the button: | ||
|
|
||
| - A [Prisma Data Platform account](https://pris.ly/pdp) (free to create during the flow). | ||
| - 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: `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/new/clone?repository-url=<your repository URL> | ||
| ``` | ||
|
|
||
| | 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 a project name and attribution: | ||
|
|
||
| ```text | ||
| https://console.prisma.io/new/clone?repository-url=https%3A%2F%2Fgithub.qkg1.top%2Facme%2Fshop-api&project-name=shop-api&utm_source=github-readme | ||
| ``` | ||
|
|
||
| ## Create your button | ||
|
|
||
| Enter your repository URL and copy the generated snippet: | ||
|
|
||
| <DeployButtonGenerator /> | ||
|
|
||
| ## Snippets | ||
|
|
||
| A complete Markdown example for a README: | ||
|
|
||
| ```md | ||
| [](https://console.prisma.io/new/clone?repository-url=https%3A%2F%2Fgithub.qkg1.top%2Facme%2Fshop-api&utm_source=github-readme) | ||
| ``` | ||
|
|
||
| The same button in HTML: | ||
|
|
||
| ```html | ||
| <a href="https://console.prisma.io/new/clone?repository-url=https%3A%2F%2Fgithub.qkg1.top%2Facme%2Fshop-api&utm_source=github-readme"> | ||
| <img src="https://www.prisma.io/docs/img/deploy-button.svg" alt="Deploy with Prisma" width="172" height="36" /> | ||
| </a> | ||
| ``` | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| **"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. 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 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 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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,175 @@ | ||
| "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_CLONE_URL = "https://console.prisma.io/new/clone"; | ||
| const BUTTON_IMAGE_URL = "https://www.prisma.io/docs/img/deploy-button.svg"; | ||
|
|
||
| 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}$/; | ||
|
|
||
| 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)); | ||
|
|
||
| return ( | ||
| <button | ||
| type="button" | ||
| aria-label="Copy to clipboard" | ||
| className={cn( | ||
| buttonVariants({ color: "secondary", size: "sm", className: "shrink-0 gap-2" }), | ||
| )} | ||
| onClick={onClick} | ||
| > | ||
| {checked ? <i className="fa-regular fa-check" /> : <i className="fa-regular fa-copy" />} | ||
| Copy | ||
| </button> | ||
| ); | ||
| } | ||
|
|
||
| function Snippet({ label, value }: { label: string; value: string }) { | ||
| return ( | ||
| <div className="flex flex-col gap-1.5"> | ||
| <span className="text-sm font-medium text-fd-muted-foreground">{label}</span> | ||
| <div className="flex items-start gap-2"> | ||
| <pre className="min-w-0 flex-1 overflow-x-auto rounded-lg border bg-fd-secondary/50 p-3 text-xs leading-relaxed"> | ||
| <code>{value}</code> | ||
| </pre> | ||
| <CopyButton value={value} /> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| 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 [repositoryUrl, setRepositoryUrl] = useState(""); | ||
| const [projectName, setProjectName] = useState(""); | ||
| const [utmSource, setUtmSource] = useState(""); | ||
| const [utmCampaign, setUtmCampaign] = useState(""); | ||
|
|
||
| const parsed = useMemo(() => parseRepositoryUrl(repositoryUrl), [repositoryUrl]); | ||
| const projectNameValid = | ||
| projectName.trim() === "" || PROJECT_NAME_PATTERN.test(projectName.trim()); | ||
|
|
||
| const url = useMemo(() => { | ||
| 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()); | ||
| return `${CONSOLE_CLONE_URL}?${search.toString()}`; | ||
| }, [parsed, projectNameValid, projectName, utmSource, utmCampaign]); | ||
|
|
||
| return ( | ||
| <div className="not-prose flex flex-col gap-5 rounded-xl border p-5"> | ||
| <div className="grid gap-4 sm:grid-cols-2"> | ||
| <label className="flex flex-col gap-1.5 sm:col-span-2"> | ||
| <span className="text-sm font-medium">Repository URL</span> | ||
| <input | ||
| className={fieldClassName} | ||
| placeholder="https://github.com/owner/repo" | ||
| value={repositoryUrl} | ||
| onChange={(event) => setRepositoryUrl(event.target.value)} | ||
| /> | ||
| {repositoryUrl.trim() !== "" && !parsed ? ( | ||
| <span className="text-xs text-fd-muted-foreground"> | ||
| Enter a public GitHub repository URL like https://github.com/owner/repo. | ||
| </span> | ||
| ) : null} | ||
| </label> | ||
| <label className="flex flex-col gap-1.5"> | ||
| <span className="text-sm font-medium"> | ||
| Project name <span className="font-normal text-fd-muted-foreground">(optional)</span> | ||
| </span> | ||
| <input | ||
| className={fieldClassName} | ||
| placeholder="my-app" | ||
| value={projectName} | ||
| onChange={(event) => setProjectName(event.target.value)} | ||
| /> | ||
| </label> | ||
| <label className="flex flex-col gap-1.5"> | ||
| <span className="text-sm font-medium"> | ||
| Source <span className="font-normal text-fd-muted-foreground">(optional)</span> | ||
| </span> | ||
| <input | ||
| className={fieldClassName} | ||
| placeholder="github-readme" | ||
| value={utmSource} | ||
| onChange={(event) => setUtmSource(event.target.value)} | ||
| /> | ||
| </label> | ||
| <label className="flex flex-col gap-1.5"> | ||
| <span className="text-sm font-medium"> | ||
| Campaign <span className="font-normal text-fd-muted-foreground">(optional)</span> | ||
| </span> | ||
| <input | ||
| className={fieldClassName} | ||
| placeholder="launch-2026" | ||
| value={utmCampaign} | ||
| onChange={(event) => setUtmCampaign(event.target.value)} | ||
| /> | ||
| </label> | ||
| </div> | ||
|
|
||
| {url ? ( | ||
| <> | ||
| <div className="flex items-center gap-3 rounded-lg border border-dashed p-4"> | ||
| <span className="text-sm text-fd-muted-foreground">Preview:</span> | ||
| <a href={url} target="_blank" rel="noreferrer"> | ||
| {/* eslint-disable-next-line @next/next/no-img-element */} | ||
| <img | ||
| src={withDocsBasePath("/img/deploy-button.svg")} | ||
| alt="Deploy with Prisma" | ||
| width={172} | ||
| height={36} | ||
| /> | ||
| </a> | ||
| </div> | ||
| <Snippet label="URL" value={url} /> | ||
| <Snippet | ||
| label="Markdown" | ||
| value={`[](${url})`} | ||
| /> | ||
| <Snippet | ||
| label="HTML" | ||
| value={`<a href="${url}"><img src="${BUTTON_IMAGE_URL}" alt="Deploy with Prisma" width="172" height="36" /></a>`} | ||
| /> | ||
| </> | ||
| ) : ( | ||
| <p className="text-sm text-fd-muted-foreground"> | ||
| Enter your repository's GitHub URL to generate the button. The repository must be public | ||
| and contain a <code>prisma.compute.json</code> file at its root. | ||
| </p> | ||
| )} | ||
| </div> | ||
| ); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.