deploy-an-app

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Deploy an App

部署应用

Take an app that works on one machine and make it work for everyone. The result is a live URL the user owns, backed by real infrastructure, with an honest account of what was done for them and what is still theirs to do.
Deploying is not building. This skill provisions infrastructure, sets configuration and runs deploys. It does not add features, redesign pages, or improve code it happens to dislike. Where a change to the code is genuinely required to run in production — and there is exactly one common case, a SQLite app moving to Postgres — it goes on the sheet in Step 3 and is approved before it happens.
Most of this is automatable, and the parts that aren't are knowable in advance. The value of this skill is not that it types
vercel deploy
. It is that it asks for every human-only credential once, in one place, before anything starts — rather than discovering them one failed build at a time.
将仅在单台机器上运行的应用改造为可供所有人使用的版本。最终成果是用户拥有的一个在线URL,由真实基础设施提供支撑,同时会清晰告知用户已完成的工作和仍需他们自行处理的事项。
部署不等于构建。该技能负责配置基础设施、设置参数并执行部署操作,不会添加功能、重新设计页面或修改它认为不佳的代码。只有当代码确实需要修改才能在生产环境运行时(唯一常见的情况是将SQLite应用迁移到Postgres),才会在步骤3中列出修改内容,并在执行前获得用户批准。
大部分流程可自动化,无法自动化的部分也能提前明确。该技能的价值不在于执行
vercel deploy
命令,而在于在启动任何操作前,一次性在同一位置收集所有仅能由人工提供的凭据——而非在每次构建失败后才逐一索要。

Ground rules

基本原则

  • Explain every step like you would to a smart friend who doesn't code. Say "the address people will type" before saying "domain". Introduce each term once, briefly, then use it normally.
  • Never write or accept a version number. Not in an install command, not in prose, not a CLI version to gate on. Every invocation takes the current release, and Step 1 is what establishes what that means today. A version written into a skill file is a lie with a timestamp on it.
  • Nothing deprecated, ever. If the current release renames or supersedes something a reference file uses, use the replacement, not the old path that "still works".
  • On what the hosting CLI can do,
    --help
    outranks everything
    — this file, the reference files, the research in Step 1, and the provider's own documentation. Documentation describes the release the writer had. Ask the binary that is about to run. This is the deploy-side twin of the parent skill's rule that research wins on API detail.
  • The public URL is read back from the platform, never predicted. A name collision makes the host assign something else, and every value derived from a guessed URL — the sign-in origin, the OAuth callbacks, the token audience — is then quietly wrong.
  • Everything the build needs is set before the build runs. Not just the database. Provider clients get constructed when a module is first imported, so a missing key is a failed build, not a runtime warning. Step 7 finishes before Step 8 starts.
  • .env
    is not a deployment artefact.
    Some values in it are actively dangerous in production — a local database address, a sandbox payments flag, a switch that turns off signature checking. Each is copied, transformed, regenerated or refused deliberately.
    references/env.md
    has the table; there is no copy loop.
  • A secret goes from its source into the host and nowhere else. Never printed, never echoed into the conversation, never asked for as a chat message when a file or an environment variable would do.
  • One approval, and one announced pause. Step 3 is the only place the user decides anything. Step 6 is a scheduled hand-off where they do browser work no API can do — named on the sheet before it happens, so it is a meeting rather than an interruption.
  • Test mode unless the user says otherwise, separately. Everything else on the sheet runs under one go-ahead. Taking real money does not: it is the one action that can charge a real card, and live prices cannot be deleted afterwards.
  • Check before creating, and write down what was created. Every provider here will happily create a second one of anything. Two webhooks on one URL means every event is processed twice, with half the signature checks failing. The record is written as each thing is created, never reconstructed afterwards —
    references/recovery.md
    has the format, and it is what makes an interrupted run reportable instead of mysterious.
  • When something fails, stop and report — never tear down. A database a later step couldn't reach still holds the schema that worked.
    references/recovery.md
    says what can be undone and what cannot.
  • Never delete or overwrite something this run did not create. A name collision stops and asks. The exception is a value this run wrote itself and is correcting.
  • A check that wasn't run is named, never claimed. In this skill the specific temptations are saying the environment is verified when only names were read, that the database is connected when only a page returned 200, and that webhooks work when only an endpoint was registered. The user reads silence as success.
  • The gate is passed by fixing the deploy, never by widening the gate. No build-error suppression, no skipping validation, and above all never removing the migration step from the build script to make a red X go green.
  • Never
    drizzle-kit push
    .
    The parent skill's rule, and here the database has the user's real data in it.
  • The app is deployed from the current working directory — that folder is the project root. Never create a subfolder and never
    cd
    into one.
  • All commands, package names and config live in the reference files, never in this file. Load only the ones the detected branches need.
  • If a reference command fails because a tool changed, check that tool's official docs, use the current equivalent, finish the job, and tell the user at the end which file needs a refresh.
  • 像给不懂代码的聪明朋友讲解一样解释每一步。先讲“用户将输入的地址”,再提“域名”。每个术语只介绍一次,简单说明后就正常使用。
  • 绝不写入或接受版本号。无论是安装命令、说明文字还是CLI版本限制,都不使用版本号。每次调用都采用当前最新版本,步骤1会明确当前最新版本的定义。写入技能文件的版本号就是带有时间戳的错误信息。
  • 绝不使用已废弃的内容。如果当前版本重命名或取代了参考文件中使用的内容,使用替代方案,而非“仍能工作”的旧路径。
  • 关于托管CLI的功能,
    --help
    的优先级高于一切
    ——包括本文件、参考文件、步骤1的研究内容以及服务商自身的文档。文档描述的是作者当时使用的版本,而应询问即将运行的二进制文件。这是部署端遵循的规则,与父技能中“API细节以研究结果为准”的规则相对应。
  • 公开URL从平台读取,绝不预测。名称冲突会导致主机分配其他URL,而基于猜测URL生成的所有值——登录源、OAuth回调、令牌受众——都会悄无声息地出错。
  • 构建所需的所有内容都在构建前设置完成。不仅是数据库。提供商客户端在模块首次导入时就会初始化,因此缺失密钥会导致构建失败,而非运行时警告。步骤7完成后才会启动步骤8。
  • .env
    不是部署工件
    。其中部分值在生产环境中存在安全风险——本地数据库地址、沙箱支付标识、关闭签名校验的开关。每个值都会经过复制、转换、重新生成或主动拒绝处理。
    references/env.md
    中有对应表格,不存在直接复制的循环操作。
  • 密钥从源头直接传入主机,不经过其他任何环节。绝不打印、绝不回显到对话中、绝不以聊天消息形式索要,应使用文件或环境变量传递。
  • 仅一次批准,一次预先告知的暂停。步骤3是唯一需要用户决策的环节。步骤6是预先安排的交接环节,用户需要完成无法通过API实现的浏览器操作——提前在清单中说明,使其成为有计划的任务而非突发干扰。
  • 默认使用测试模式,除非用户单独明确说明。清单上的其他所有操作只需一次批准即可执行。处理真实支付是例外:这是唯一可能产生真实扣费的操作,且实时价格无法事后删除。
  • 创建前先检查,并记录已创建的内容。此处的每个提供商都会轻易创建第二个相同资源。同一URL上的两个Webhook会导致每个事件被处理两次,且半数签名校验会失败。创建每个资源时就记录信息,绝不事后重建——
    references/recovery.md
    有记录格式,这能让中断的运行状态可追溯,而非变得神秘。
  • 若出现失败,立即停止并报告——绝不拆除资源。后续步骤无法连接的数据库仍保留有效的架构。
    references/recovery.md
    说明了哪些内容可以撤销,哪些不能。
  • 绝不删除或覆盖本次运行未创建的内容。名称冲突时会停止并询问。例外情况是本次运行自身写入并需要修正的值。
  • 未执行的检查要明确说明,绝不谎称已执行。本技能中容易出现的误导情况包括:仅读取变量名称就声称环境已验证;仅页面返回200就声称数据库已连接;仅注册端点就声称Webhook可用。用户会将沉默视为成功。
  • 通过修复部署通过校验,绝不放宽校验标准。不抑制构建错误、不跳过验证,尤其绝不能为了消除错误提示而从构建脚本中移除迁移步骤。
  • 绝不使用
    drizzle-kit push
    。这是父技能的规则,且此处数据库中存储着用户的真实数据。
  • 应用从当前工作目录部署——该文件夹即为项目根目录。绝不创建子文件夹,绝不执行
    cd
    进入子目录。
  • 所有命令、包名和配置都存储在参考文件中,绝不写入本文件。仅加载检测到的分支所需的内容。
  • 若参考命令因工具更新而失败,检查该工具的官方文档,使用当前等效命令,完成任务后在末尾告知用户需要更新哪个文件。

Step 0 — Preflight

步骤0 — 预检

Find out what is true before promising anything.
references/preflight.md
.
Nothing here changes anything. It establishes: whether the hosting CLI is available and what it can actually do; whether the user is logged in and under which account; what this app is, which package manager it uses, and what its build command really runs; which features it has, read from the code rather than asked about; the complete list of environment variables the code reads; and whether the git tree is clean.
The branch list comes from the code. An app has payments because there is a payments client in it, not because the user remembered to mention it. This is also what lets the skill work on an app it did not build.
A dirty tree stops here. A deploy cut from uncommitted work is a build nobody can reproduce, and the two deploy mechanisms — a push and a direct upload — disagree about what it even contains.
Is there already a production deployment with users on it? If so, the posture changes for the rest of the run: nothing is created from scratch, and Step 8 stops deploying straight to production. Establish it here, not at the moment it matters.
在做出任何承诺前先了解实际情况。详情见
references/preflight.md
此步骤不会修改任何内容。它会确认:托管CLI是否可用以及实际具备哪些功能;用户是否已登录以及使用哪个账户;当前应用是什么、使用哪个包管理器、构建命令实际执行的内容;应用具备哪些功能(从代码中读取而非询问用户);代码读取的完整环境变量列表;以及git工作树是否干净。
分支列表来自代码。应用具备支付功能是因为代码中包含支付客户端,而非用户记得提及此事。这也是该技能能处理非自身构建的应用的原因。
工作树不干净则停止操作。基于未提交代码的部署是无法重现的构建,且两种部署机制——推送和直接上传——对部署内容的定义不一致。
**是否已有面向用户的生产部署?**如果有,后续操作的姿态会改变:不会从头创建任何资源,步骤8不会直接部署到生产环境。在此步骤就确认此事,而非到关键时刻才发现。

Step 1 — Check what's current

步骤1 — 检查当前最新状态

The branches are known, so find out what deploying them involves today. Same pattern as the parent skill: one research subagent per detected branch, all dispatched in a single message.
Hosting CLIs, dashboards and provider APIs move faster than application libraries, and they move in a way that breaks scripts rather than types. A flag that was renamed is a pipeline that stops halfway through, having already created things.
Each gets the standard brief — current stable release, anything deprecated or renamed, current command and endpoint shapes, any capability added since that would replace hand-written steps in the reference file. Reconcile as the parent skill does: latest stable only, take the new capability when there is one, and on how the pieces fit together this skill wins.
Say something to the user only when something changed. Narrating research that found everything fine reads as filler.
已知分支信息,因此要了解当前部署这些分支涉及的内容。与父技能模式相同:为每个检测到的分支分配一个研究子Agent,所有请求一次性发送
托管CLI、仪表板和提供商API的更新速度快于应用库,且更新方式可能导致脚本失效。例如,某个标志被重命名会导致管道中途停止,且已创建部分资源。
每个子Agent都会收到标准指令:获取当前稳定版本、已废弃或重命名的内容、当前命令和端点格式、自参考文件编写以来新增的可替代手动步骤的功能。按照父技能的方式协调:仅使用最新稳定版本,有新功能则使用新功能,关于各组件如何配合的问题以本技能的规则为准。
仅当内容发生变化时才告知用户。讲述未发现问题的研究内容会显得冗余。

Step 2 — What only the user knows

步骤2 — 仅用户知晓的信息

Short, because Step 0 answered most of it from the code. Ask one thing at a time, with a recommendation.
  1. "What address should this live at?" — a domain they own, or the free one the host provides. Asked first because every callback URL, every webhook target and the sign-in origin derive from it, and each one that changes later is manual work done twice.
  2. The credentials the detected branches need, gathered in one block — every provider account and key, what each is for, and where to get it. Not one question per failure.
  3. Payments, if present: test mode or real money. Recommend test mode. Real money is a separate, explicit confirmation.
内容简短,因为步骤0已从代码中回答了大部分问题。每次询问一件事,并给出建议。
  1. “应用的访问地址是什么?”——用户拥有的域名,或主机提供的免费域名。优先询问此事,因为所有回调URL、Webhook目标和登录源都基于该地址,后续修改任何一项都需重复手动操作。
  2. 检测到的分支所需的凭据——集中收集所有提供商账户和密钥,说明每个凭据的用途和获取位置。而非每次失败后询问一次。
  3. 若存在支付功能:使用测试模式还是真实支付。建议使用测试模式。真实支付需要单独的明确确认。

Step 3 — Deploy sheet

步骤3 — 部署清单

Restate it in plain words and get one clear go-ahead.
start-an-app
Step 3's job, on the deploy side:
Here's what I'll set up for TrailLog:
It'll live at: traillog.com — you'll add two DNS records at your registrar, I'll tell you exactly what. Database: a hosted Postgres, free tier — sleeps when idle, so the first visit after a quiet spell takes a second. Photos: a file store connected to the project, free up to a point. Sign-in: you'll paste one callback URL into Google's console — about two minutes, and I'll give you the exact text. Payments: set up in test mode. No real cards until you say so. What it costs: nothing today. Every piece is on a free tier, and I'll tell you where each one ends. One pause: after I've set up the address, I'll stop and give you a short list of browser work — the DNS records and the Google callback. Everything else runs without interrupting you.
Sound right?
Include what will be created, what it costs, what is irreversible, and where the pause lands. For a SQLite app, the conversion appears here as a change to their code, in plain words.
用直白的语言重述内容,并获得明确的批准。对应
start-an-app
步骤3在部署端的作用:
我将为TrailLog完成以下设置:
访问地址: traillog.com — 你需要在域名注册商处添加两条DNS记录,我会告知具体内容。 数据库: 托管式Postgres,免费套餐 — 空闲时会休眠,因此长时间无人访问后的首次加载需要一秒钟。 图片存储: 与项目关联的文件存储服务,有免费额度限制。 登录功能: 你需要将一个回调URL粘贴到Google控制台 — 约两分钟,我会提供准确的文本内容。 支付功能: 配置为测试模式。获得你的明确确认前不会使用真实银行卡。 费用说明: 当前无需付费。所有组件均使用免费套餐,我会告知每个套餐的限制。 一次暂停: 设置完地址后,我会暂停操作并提供一份简短的浏览器操作清单 — DNS记录和Google回调配置。其他所有操作都会自动完成,不会打扰你。
这样可以吗?
清单需包含将创建的内容、费用、不可撤销的操作以及暂停的时机。对于SQLite应用,代码转换会作为修改内容以直白语言列出。

Step 4 — The address

步骤4 — 设置访问地址

Everything downstream needs the URL, so it is settled before anything else exists.
references/project-and-url.md
.
Create the project, read back the production URL the platform actually assigned, attach the custom domain if there is one, and produce the DNS records the user will need. Start DNS early: it is the slowest thing in the pipeline and it depends on nothing.
Where a domain is involved, decide the canonical host — bare or
www
— and redirect the other to it. A session cookie set on the wrong one is a sign-in that appears to work and then doesn't.
后续所有操作都依赖URL,因此需在创建其他资源前确定URL。详情见
references/project-and-url.md
创建项目,读取平台实际分配的生产URL,若有自定义域名则绑定,并生成用户需要添加的DNS记录。尽早启动DNS配置:这是流程中最慢的环节,且不依赖其他任何操作。
若涉及域名,确定规范主机——裸域名或
www
子域名——并将另一个重定向到规范主机。在错误域名上设置的会话Cookie会导致登录看似成功但实际无效。

Step 5 — Provision and prepare

步骤5 — 配置与准备

references/provision-database.md
, plus
references/provision-storage.md
for the uploads branch.
Create the database and the file store, and connect them to the project. Two details decide whether this works: the name the provider injects may not be the name the code reads, and migrations want the direct connection string while the app wants the pooled one.
This is also where the code changes happen, if any: the SQLite conversion, and reconciling any variable name. Commit them.
Then build it locally against the production database, before deploying anything. It runs the same migrate-then-build the platform will run, catches the conversion and every module-scope key, and costs no deployments. This is the single best-value check in the skill.
详情见
references/provision-database.md
,若存在上传分支则还需参考
references/provision-storage.md
创建数据库和文件存储服务,并将其连接到项目。有两个细节决定操作是否成功:提供商注入的名称可能与代码读取的名称不一致;迁移操作需要直接连接字符串,而应用需要池化连接字符串。
此步骤也会进行必要的代码修改(如有):SQLite转换和变量名称协调。提交这些修改。
然后在本地针对生产数据库构建应用,再进行部署。执行平台将采用的“先迁移后构建”流程,提前发现转换问题和所有模块级密钥缺失问题,且不会消耗部署次数。这是本技能中性价比最高的检查步骤。

Step 6 — The rendezvous

步骤6 — 交接环节

The one announced pause. Hand the user everything that needs a human in a browser, in a single block: the DNS records, the OAuth callback URLs, any provider account they still need. Exact values, ready to paste.
This is not a prompt for a decision — Step 3 was. It is a scheduled hand-off, promised in advance, so the user can do fifteen minutes of console work in one sitting rather than being interrupted five times.
这是预先告知的唯一暂停环节。将所有需要人工在浏览器中完成的操作集中提供给用户:DNS记录、OAuth回调URL、仍需设置的提供商账户。提供准确的可直接粘贴的值。
这不是决策请求——步骤3已经完成决策。这是预先安排的交接,用户可以一次性完成十五分钟的控制台操作,而非被打断五次。

Step 7 — The environment

步骤7 — 配置环境

references/env.md
. Write every variable the code reads, to production, before any build.
Three things make this more than a copy: values that must be transformed or refused rather than copied; secrets that are corrupted by an invisible trailing newline and cannot be read back to diagnose; and the fact that a written secret can be confirmed to exist but never to be correct.
Then wire the external systems that need only the URL, not a live site — payment webhooks, background-job keys.
references/wire-payments.md
,
references/wire-jobs.md
,
references/wire-auth.md
,
references/wire-email.md
,
references/wire-mcp.md
, each only if that branch exists.
详情见
references/env.md
。在任何构建操作前,将代码读取的所有变量写入生产环境。
与直接复制不同,此处需处理三件事:必须转换或拒绝复制的值;被不可见换行符损坏且无法回读诊断的密钥;已写入的密钥只能确认其存在,无法确认其正确性。
然后连接仅需URL即可配置的外部系统——支付Webhook、后台任务密钥。根据分支情况参考
references/wire-payments.md
references/wire-jobs.md
references/wire-auth.md
references/wire-email.md
references/wire-mcp.md

Step 8 — Deploy

步骤8 — 执行部署

references/deploy.md
. Once. Everything the build needs is already in place, so the first deploy is the first one that can succeed rather than a throwaway that fails on a missing database.
Where the user has a repository, connect it and let a push produce the deploy — so the mechanism they will use forever is the one that just got proven. A direct upload is the fallback.
Then confirm the deployment that went live is the commit you think it is. It is one command, and it catches the two most common false victories: reading the previous build's result while the new one is still going, and deploying something that was never committed.
Deploy straight to production only for a project this run created. Where there is already a live site with users, that is a different posture, and
references/deploy.md
has it.
详情见
references/deploy.md
仅执行一次。构建所需的所有内容已准备就绪,因此首次部署就是可能成功的部署,而非因缺少数据库而失败的试部署。
若用户有代码仓库,将其连接并设置为推送触发部署——这样用户今后将一直使用已验证有效的机制。直接上传作为备选方案。
然后确认上线的部署版本是预期的提交记录。只需一条命令,就能发现两种最常见的虚假成功:新构建仍在运行时读取旧构建结果,以及部署了未提交的内容。
仅当本次运行创建的新项目才直接部署到生产环境。若已有面向用户的上线站点,操作姿态不同,详情见
references/deploy.md

Step 9 — Prove it

步骤9 — 验证可用性

references/gate.md
. The app is deployed. Nothing yet establishes that it works.
Commands against the live URL, whose output is read. What is genuinely provable from outside without a browser is more than it looks: the deployed commit, that migrations ran, that the database answers, every route, the certificate and canonical host, cookie flags, that a webhook endpoint verifies signatures rather than accepting anything, that discovery documents name the real domain.
Distinguish failed from blocked from not attempted. If DNS has not landed yet, every check behind the domain is red for one reason, and reporting fifteen failures for one cause teaches the user to ignore the gate.
Where a browser is available, use it — a real sign-in on the live domain is the one check that proves the URL, the database and the session cookie are all correct together. Everything still out of reach is named as unperformed.
详情见
references/gate.md
。应用已部署,但尚未证明其可用。
针对在线URL执行命令并读取输出结果。无需浏览器即可从外部验证的内容比想象中多:部署的提交记录、迁移是否执行、数据库是否响应、所有路由、证书和规范主机、Cookie标志、Webhook端点是否验证签名而非接受任何请求、发现文档是否指向真实域名。
区分失败、阻塞和未执行。若DNS尚未生效,域名后的所有检查失败都是同一原因,报告十五次失败会让用户忽略校验结果。
若有浏览器可用,使用浏览器验证——在在线域名上完成真实登录是唯一能同时验证URL、数据库和会话Cookie正确性的检查。所有仍无法验证的内容都需明确说明未执行。

Step 10 — Fresh eyes

步骤10 — 复查

The gate proves the site answers. It cannot tell whether the deploy did what was agreed, or left something behind. Dispatch the critics in a single message, read-only, evidence not access, two rounds then stop. Briefs are in
references/gate.md
.
The lenses differ from the parent skill's, because there is no new app to review — sheet against reality, claim against evidence, secret exposure, and what got left behind.
校验环节证明站点可响应,但无法确认部署是否完成了约定的工作或遗漏了内容。一次性发送复查请求,仅读取数据,不获取访问权限,两轮后停止。详情见
references/gate.md
中的指令。
复查视角与父技能不同,因为无需评审新应用——而是对照清单检查实际情况、对照声明检查证据、检查密钥是否泄露以及是否有遗漏内容。

Step 11 — Hand off

步骤11 — 交接

  • What exists now and what it costs, from the record kept while creating it — every resource, its free-tier limit, and how to remove it.
  • What the next
    git push
    does.
    If push-to-deploy is wired, this is the biggest change to how they work, and it deserves one plain sentence: pushing to the main branch puts it live, and a bad migration fails the build.
  • How to deploy again, and how to roll back.
  • Every check that could not be run, with what it would need.
  • The manual steps still outstanding, if the rendezvous left any.
  • If agent access was built, the connector URL, and where it goes in Claude — they will not find it on their own.
  • Anything Step 1's research contradicted, named, so this skill can be corrected.
  • 当前已创建的资源及费用,基于创建时记录的信息——每个资源、其免费套餐限制以及删除方式。
  • 下次
    git push
    的作用
    。若已配置推送触发部署,这是用户工作方式的最大变化,需用直白的语言说明:推送到主分支会直接上线,错误的迁移会导致构建失败。
  • 如何再次部署与回滚
  • 所有未执行的检查,以及执行所需的条件。
  • 交接环节未完成的手动步骤(如有)。
  • 若配置了Agent访问,提供连接器URL及在Claude中的配置位置——用户无法自行找到这些信息。
  • 步骤1的研究中与参考文件矛盾的内容,明确说明以便更新本技能。