[{"data":1,"prerenderedAt":942},["ShallowReactive",2],{"header-counts":3,"playbook-deploy\u002Fai-auto-deploy":6,"footer-counts":941},{"tools":4,"reviews":5},65,7,{"id":7,"title":8,"body":9,"category":921,"cover":922,"description":923,"extension":924,"meta":925,"navigation":116,"path":926,"published":927,"relatedTools":928,"seo":932,"stem":933,"tags":934,"updated":927,"__hash__":940},"playbook\u002Fplaybook\u002Fdeploy\u002Fai-auto-deploy.md","用 AI Agent 全自动部署：从 Git Push 到生产环境零手工",{"type":10,"value":11,"toc":910},"minimark",[12,16,29,32,43,47,54,368,372,375,467,471,572,576,692,695,791,794,797,858,861,906],[13,14,15],"h2",{"id":15},"适用场景",[17,18,19,23,26],"ul",{},[20,21,22],"li",{},"个人项目 \u002F 小团队，不想手动部署",[20,24,25],{},"想让 AI 做部署决策（是否回滚、是否发公告）",[20,27,28],{},"需要部署后自动验证的",[13,30,31],{"id":31},"架构",[33,34,39],"pre",{"className":35,"code":37,"language":38},[36],"language-text","git push main\n  → GitHub Actions 触发\n    → Step 1: 跑测试（pnpm test）\n    → Step 2: Claude Code 审查改动\n    → Step 3: 构建（pnpm build）\n    → Step 4: 部署到 Vercel\n    → Step 5: Claude Code 验证生产环境\n    → Step 6: 失败则自动回滚 + 通知\n","text",[40,41,37],"code",{"__ignoreMap":42},"",[13,44,46],{"id":45},"第一步基础-ci","第一步：基础 CI",[48,49,50,53],"p",{},[40,51,52],{},".github\u002Fworkflows\u002Fdeploy.yml","：",[33,55,59],{"className":56,"code":57,"language":58,"meta":42,"style":42},"language-yaml shiki shiki-themes github-light github-dark","name: Deploy\non:\n  push:\n    branches: [main]\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n      deployments: write\n    steps:\n      - uses: actions\u002Fcheckout@v4\n      - uses: pnpm\u002Faction-setup@v2\n        with: { version: 9 }\n      - uses: actions\u002Fsetup-node@v4\n        with: { node-version: 22, cache: pnpm }\n\n      - name: Install\n        run: pnpm install --frozen-lockfile\n\n      - name: Test\n        run: pnpm test\n\n      - name: Build\n        run: pnpm build\n        env:\n          VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}\n","yaml",[40,60,61,78,88,96,111,118,126,133,144,152,163,173,181,195,207,227,239,267,272,284,295,300,312,322,327,339,349,357],{"__ignoreMap":42},[62,63,66,70,74],"span",{"class":64,"line":65},"line",1,[62,67,69],{"class":68},"s9eBZ","name",[62,71,73],{"class":72},"sVt8B",": ",[62,75,77],{"class":76},"sZZnC","Deploy\n",[62,79,81,85],{"class":64,"line":80},2,[62,82,84],{"class":83},"sj4cs","on",[62,86,87],{"class":72},":\n",[62,89,91,94],{"class":64,"line":90},3,[62,92,93],{"class":68},"  push",[62,95,87],{"class":72},[62,97,99,102,105,108],{"class":64,"line":98},4,[62,100,101],{"class":68},"    branches",[62,103,104],{"class":72},": [",[62,106,107],{"class":76},"main",[62,109,110],{"class":72},"]\n",[62,112,114],{"class":64,"line":113},5,[62,115,117],{"emptyLinePlaceholder":116},true,"\n",[62,119,121,124],{"class":64,"line":120},6,[62,122,123],{"class":68},"jobs",[62,125,87],{"class":72},[62,127,128,131],{"class":64,"line":5},[62,129,130],{"class":68},"  deploy",[62,132,87],{"class":72},[62,134,136,139,141],{"class":64,"line":135},8,[62,137,138],{"class":68},"    runs-on",[62,140,73],{"class":72},[62,142,143],{"class":76},"ubuntu-latest\n",[62,145,147,150],{"class":64,"line":146},9,[62,148,149],{"class":68},"    permissions",[62,151,87],{"class":72},[62,153,155,158,160],{"class":64,"line":154},10,[62,156,157],{"class":68},"      contents",[62,159,73],{"class":72},[62,161,162],{"class":76},"write\n",[62,164,166,169,171],{"class":64,"line":165},11,[62,167,168],{"class":68},"      deployments",[62,170,73],{"class":72},[62,172,162],{"class":76},[62,174,176,179],{"class":64,"line":175},12,[62,177,178],{"class":68},"    steps",[62,180,87],{"class":72},[62,182,184,187,190,192],{"class":64,"line":183},13,[62,185,186],{"class":72},"      - ",[62,188,189],{"class":68},"uses",[62,191,73],{"class":72},[62,193,194],{"class":76},"actions\u002Fcheckout@v4\n",[62,196,198,200,202,204],{"class":64,"line":197},14,[62,199,186],{"class":72},[62,201,189],{"class":68},[62,203,73],{"class":72},[62,205,206],{"class":76},"pnpm\u002Faction-setup@v2\n",[62,208,210,213,216,219,221,224],{"class":64,"line":209},15,[62,211,212],{"class":68},"        with",[62,214,215],{"class":72},": { ",[62,217,218],{"class":68},"version",[62,220,73],{"class":72},[62,222,223],{"class":83},"9",[62,225,226],{"class":72}," }\n",[62,228,230,232,234,236],{"class":64,"line":229},16,[62,231,186],{"class":72},[62,233,189],{"class":68},[62,235,73],{"class":72},[62,237,238],{"class":76},"actions\u002Fsetup-node@v4\n",[62,240,242,244,246,249,251,254,257,260,262,265],{"class":64,"line":241},17,[62,243,212],{"class":68},[62,245,215],{"class":72},[62,247,248],{"class":68},"node-version",[62,250,73],{"class":72},[62,252,253],{"class":83},"22",[62,255,256],{"class":72},", ",[62,258,259],{"class":68},"cache",[62,261,73],{"class":72},[62,263,264],{"class":76},"pnpm",[62,266,226],{"class":72},[62,268,270],{"class":64,"line":269},18,[62,271,117],{"emptyLinePlaceholder":116},[62,273,275,277,279,281],{"class":64,"line":274},19,[62,276,186],{"class":72},[62,278,69],{"class":68},[62,280,73],{"class":72},[62,282,283],{"class":76},"Install\n",[62,285,287,290,292],{"class":64,"line":286},20,[62,288,289],{"class":68},"        run",[62,291,73],{"class":72},[62,293,294],{"class":76},"pnpm install --frozen-lockfile\n",[62,296,298],{"class":64,"line":297},21,[62,299,117],{"emptyLinePlaceholder":116},[62,301,303,305,307,309],{"class":64,"line":302},22,[62,304,186],{"class":72},[62,306,69],{"class":68},[62,308,73],{"class":72},[62,310,311],{"class":76},"Test\n",[62,313,315,317,319],{"class":64,"line":314},23,[62,316,289],{"class":68},[62,318,73],{"class":72},[62,320,321],{"class":76},"pnpm test\n",[62,323,325],{"class":64,"line":324},24,[62,326,117],{"emptyLinePlaceholder":116},[62,328,330,332,334,336],{"class":64,"line":329},25,[62,331,186],{"class":72},[62,333,69],{"class":68},[62,335,73],{"class":72},[62,337,338],{"class":76},"Build\n",[62,340,342,344,346],{"class":64,"line":341},26,[62,343,289],{"class":68},[62,345,73],{"class":72},[62,347,348],{"class":76},"pnpm build\n",[62,350,352,355],{"class":64,"line":351},27,[62,353,354],{"class":68},"        env",[62,356,87],{"class":72},[62,358,360,363,365],{"class":64,"line":359},28,[62,361,362],{"class":68},"          VERCEL_TOKEN",[62,364,73],{"class":72},[62,366,367],{"class":76},"${{ secrets.VERCEL_TOKEN }}\n",[13,369,371],{"id":370},"第二步ai-审查改动","第二步：AI 审查改动",[48,373,374],{},"在 build 前加 Claude Code 审查：",[33,376,378],{"className":56,"code":377,"language":58,"meta":42,"style":42},"      - name: AI Review Changes\n        uses: anthropics\u002Fclaude-code-action@v1\n        with:\n          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}\n          prompt: |\n            检查这次改动是否有部署风险：\n            1. 是否修改了环境变量配置\n            2. 是否修改了数据库 schema\n            3. 是否有 breaking change\n            4. 是否需要发迁移公告\n\n            如果有风险，输出 \"BLOCK_DEPLOY: \u003C原因>\"\n            如果没风险，输出 \"DEPLOY_OK\"\n",[40,379,380,391,401,407,417,428,433,438,443,448,453,457,462],{"__ignoreMap":42},[62,381,382,384,386,388],{"class":64,"line":65},[62,383,186],{"class":72},[62,385,69],{"class":68},[62,387,73],{"class":72},[62,389,390],{"class":76},"AI Review Changes\n",[62,392,393,396,398],{"class":64,"line":80},[62,394,395],{"class":68},"        uses",[62,397,73],{"class":72},[62,399,400],{"class":76},"anthropics\u002Fclaude-code-action@v1\n",[62,402,403,405],{"class":64,"line":90},[62,404,212],{"class":68},[62,406,87],{"class":72},[62,408,409,412,414],{"class":64,"line":98},[62,410,411],{"class":68},"          anthropic_api_key",[62,413,73],{"class":72},[62,415,416],{"class":76},"${{ secrets.ANTHROPIC_API_KEY }}\n",[62,418,419,422,424],{"class":64,"line":113},[62,420,421],{"class":68},"          prompt",[62,423,73],{"class":72},[62,425,427],{"class":426},"szBVR","|\n",[62,429,430],{"class":64,"line":120},[62,431,432],{"class":76},"            检查这次改动是否有部署风险：\n",[62,434,435],{"class":64,"line":5},[62,436,437],{"class":76},"            1. 是否修改了环境变量配置\n",[62,439,440],{"class":64,"line":135},[62,441,442],{"class":76},"            2. 是否修改了数据库 schema\n",[62,444,445],{"class":64,"line":146},[62,446,447],{"class":76},"            3. 是否有 breaking change\n",[62,449,450],{"class":64,"line":154},[62,451,452],{"class":76},"            4. 是否需要发迁移公告\n",[62,454,455],{"class":64,"line":165},[62,456,117],{"emptyLinePlaceholder":116},[62,458,459],{"class":64,"line":175},[62,460,461],{"class":76},"            如果有风险，输出 \"BLOCK_DEPLOY: \u003C原因>\"\n",[62,463,464],{"class":64,"line":183},[62,465,466],{"class":76},"            如果没风险，输出 \"DEPLOY_OK\"\n",[13,468,470],{"id":469},"第三步ai-部署","第三步：AI 部署",[33,472,474],{"className":56,"code":473,"language":58,"meta":42,"style":42},"      - name: Deploy with Claude\n        if: success()\n        uses: anthropics\u002Fclaude-code-action@v1\n        with:\n          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}\n          prompt: |\n            执行部署：\n            1. 运行 vercel --prod --yes\n            2. 等待部署完成，获取 URL\n            3. curl 测试首页是否 200\n            4. curl 测试 \u002Fapi\u002Fhealth 是否返回 ok\n            5. 如果验证失败，运行 vercel rollback\n            6. 输出部署结果\n          allowed_tools: \"Bash\"\n",[40,475,476,487,497,505,511,519,527,532,537,542,547,552,557,562],{"__ignoreMap":42},[62,477,478,480,482,484],{"class":64,"line":65},[62,479,186],{"class":72},[62,481,69],{"class":68},[62,483,73],{"class":72},[62,485,486],{"class":76},"Deploy with Claude\n",[62,488,489,492,494],{"class":64,"line":80},[62,490,491],{"class":68},"        if",[62,493,73],{"class":72},[62,495,496],{"class":76},"success()\n",[62,498,499,501,503],{"class":64,"line":90},[62,500,395],{"class":68},[62,502,73],{"class":72},[62,504,400],{"class":76},[62,506,507,509],{"class":64,"line":98},[62,508,212],{"class":68},[62,510,87],{"class":72},[62,512,513,515,517],{"class":64,"line":113},[62,514,411],{"class":68},[62,516,73],{"class":72},[62,518,416],{"class":76},[62,520,521,523,525],{"class":64,"line":120},[62,522,421],{"class":68},[62,524,73],{"class":72},[62,526,427],{"class":426},[62,528,529],{"class":64,"line":5},[62,530,531],{"class":76},"            执行部署：\n",[62,533,534],{"class":64,"line":135},[62,535,536],{"class":76},"            1. 运行 vercel --prod --yes\n",[62,538,539],{"class":64,"line":146},[62,540,541],{"class":76},"            2. 等待部署完成，获取 URL\n",[62,543,544],{"class":64,"line":154},[62,545,546],{"class":76},"            3. curl 测试首页是否 200\n",[62,548,549],{"class":64,"line":165},[62,550,551],{"class":76},"            4. curl 测试 \u002Fapi\u002Fhealth 是否返回 ok\n",[62,553,554],{"class":64,"line":175},[62,555,556],{"class":76},"            5. 如果验证失败，运行 vercel rollback\n",[62,558,559],{"class":64,"line":183},[62,560,561],{"class":76},"            6. 输出部署结果\n",[62,563,564,567,569],{"class":64,"line":197},[62,565,566],{"class":68},"          allowed_tools",[62,568,73],{"class":72},[62,570,571],{"class":76},"\"Bash\"\n",[13,573,575],{"id":574},"第四步自动回滚","第四步：自动回滚",[33,577,579],{"className":56,"code":578,"language":58,"meta":42,"style":42},"      - name: Rollback on failure\n        if: failure()\n        run: |\n          # 获取上一个稳定部署\n          PREV=$(vercel ls --yes | head -5 | tail -1 | awk '{print $1}')\n          echo \"Rolling back to $PREV\"\n          vercel promote $PREV --yes\n\n      - name: Notify\n        if: always()\n        uses: slackapi\u002Fslack-github-action@v1\n        with:\n          slack-message: |\n            ${{ job.status == 'success' && '✅' || '❌' }} 部署 ${{ job.status }}\n            仓库: ${{ github.repository }}\n            提交: ${{ github.event.head_commit.message }}\n",[40,580,581,592,601,609,614,619,624,629,633,644,653,662,668,677,682,687],{"__ignoreMap":42},[62,582,583,585,587,589],{"class":64,"line":65},[62,584,186],{"class":72},[62,586,69],{"class":68},[62,588,73],{"class":72},[62,590,591],{"class":76},"Rollback on failure\n",[62,593,594,596,598],{"class":64,"line":80},[62,595,491],{"class":68},[62,597,73],{"class":72},[62,599,600],{"class":76},"failure()\n",[62,602,603,605,607],{"class":64,"line":90},[62,604,289],{"class":68},[62,606,73],{"class":72},[62,608,427],{"class":426},[62,610,611],{"class":64,"line":98},[62,612,613],{"class":76},"          # 获取上一个稳定部署\n",[62,615,616],{"class":64,"line":113},[62,617,618],{"class":76},"          PREV=$(vercel ls --yes | head -5 | tail -1 | awk '{print $1}')\n",[62,620,621],{"class":64,"line":120},[62,622,623],{"class":76},"          echo \"Rolling back to $PREV\"\n",[62,625,626],{"class":64,"line":5},[62,627,628],{"class":76},"          vercel promote $PREV --yes\n",[62,630,631],{"class":64,"line":135},[62,632,117],{"emptyLinePlaceholder":116},[62,634,635,637,639,641],{"class":64,"line":146},[62,636,186],{"class":72},[62,638,69],{"class":68},[62,640,73],{"class":72},[62,642,643],{"class":76},"Notify\n",[62,645,646,648,650],{"class":64,"line":154},[62,647,491],{"class":68},[62,649,73],{"class":72},[62,651,652],{"class":76},"always()\n",[62,654,655,657,659],{"class":64,"line":165},[62,656,395],{"class":68},[62,658,73],{"class":72},[62,660,661],{"class":76},"slackapi\u002Fslack-github-action@v1\n",[62,663,664,666],{"class":64,"line":175},[62,665,212],{"class":68},[62,667,87],{"class":72},[62,669,670,673,675],{"class":64,"line":183},[62,671,672],{"class":68},"          slack-message",[62,674,73],{"class":72},[62,676,427],{"class":426},[62,678,679],{"class":64,"line":197},[62,680,681],{"class":76},"            ${{ job.status == 'success' && '✅' || '❌' }} 部署 ${{ job.status }}\n",[62,683,684],{"class":64,"line":209},[62,685,686],{"class":76},"            仓库: ${{ github.repository }}\n",[62,688,689],{"class":64,"line":229},[62,690,691],{"class":76},"            提交: ${{ github.event.head_commit.message }}\n",[13,693,694],{"id":694},"安全边界",[696,697,698,714],"table",{},[699,700,701],"thead",{},[702,703,704,708,711],"tr",{},[705,706,707],"th",{},"操作",[705,709,710],{},"AI 可执行",[705,712,713],{},"需人工确认",[715,716,717,728,737,746,755,764,773,782],"tbody",{},[702,718,719,723,726],{},[720,721,722],"td",{},"跑测试",[720,724,725],{},"✅",[720,727],{},[702,729,730,733,735],{},[720,731,732],{},"构建项目",[720,734,725],{},[720,736],{},[702,738,739,742,744],{},[720,740,741],{},"部署到 preview",[720,743,725],{},[720,745],{},[702,747,748,751,753],{},[720,749,750],{},"部署到 production",[720,752,725],{},[720,754],{},[702,756,757,760,762],{},[720,758,759],{},"回滚",[720,761,725],{},[720,763],{},[702,765,766,769,771],{},[720,767,768],{},"修改环境变量",[720,770],{},[720,772,725],{},[702,774,775,778,780],{},[720,776,777],{},"数据库迁移",[720,779],{},[720,781,725],{},[702,783,784,787,789],{},[720,785,786],{},"删除资源",[720,788],{},[720,790,725],{},[13,792,793],{"id":793},"效果",[48,795,796],{},"上线 1 个月后：",[696,798,799,812],{},[699,800,801],{},[702,802,803,806,809],{},[705,804,805],{},"指标",[705,807,808],{},"之前（手动）",[705,810,811],{},"之后（AI 自动）",[715,813,814,825,836,847],{},[702,815,816,819,822],{},[720,817,818],{},"部署频率",[720,820,821],{},"2 次\u002F周",[720,823,824],{},"5 次\u002F天",[702,826,827,830,833],{},[720,828,829],{},"部署耗时",[720,831,832],{},"15 分钟",[720,834,835],{},"3 分钟",[702,837,838,841,844],{},[720,839,840],{},"部署失败率",[720,842,843],{},"10%",[720,845,846],{},"3%（自动回滚）",[702,848,849,852,855],{},[720,850,851],{},"人工介入",[720,853,854],{},"每次",[720,856,857],{},"5% 的部署",[13,859,860],{"id":860},"踩坑记录",[862,863,864,878,884,890,896],"ol",{},[20,865,866,873,874,877],{},[867,868,869,870],"strong",{},"Claude Code Action 的 ",[40,871,872],{},"allowed_tools","——只给 ",[40,875,876],{},"Bash","，别给文件编辑权限，否则 AI 可能改代码。",[20,879,880,883],{},[867,881,882],{},"Vercel rollback 需要 production deployment 历史","——首次部署没有回滚目标，先手动部署一次。",[20,885,886,889],{},[867,887,888],{},"Claude API 成本","——每次部署约 $0.2-0.5（审查 + 部署 + 验证），月费 $30-50。",[20,891,892,895],{},[867,893,894],{},"preview 环境先验证","——加一步部署到 preview 验证通过再 promote 到 production。",[20,897,898,901,902,905],{},[867,899,900],{},"GitHub Actions 超时","——Claude Code 调用有时慢，设置 ",[40,903,904],{},"timeout-minutes: 15","。",[907,908,909],"style",{},"html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":42,"searchDepth":90,"depth":90,"links":911},[912,913,914,915,916,917,918,919,920],{"id":15,"depth":80,"text":15},{"id":31,"depth":80,"text":31},{"id":45,"depth":80,"text":46},{"id":370,"depth":80,"text":371},{"id":469,"depth":80,"text":470},{"id":574,"depth":80,"text":575},{"id":694,"depth":80,"text":694},{"id":793,"depth":80,"text":793},{"id":860,"depth":80,"text":860},"deploy","\u002Fog\u002Fplaybook\u002Fai-auto-deploy.png","用 Claude Code + GitHub Actions + Vercel\u002FCfly 搭一条全自动部署流水线——push 到 main → AI 跑测试 → AI 审查 → AI 部署 → 验证 → 通知。零手工操作，含回滚机制。","md",{},"\u002Fplaybook\u002Fdeploy\u002Fai-auto-deploy","2026-06-21",[929,930,931],"coding\u002Fcli\u002Fclaude-code","coding\u002Fbuilder\u002Fv0","agent\u002Fskills\u002Fclaude-skills",{"title":8,"description":923},"playbook\u002Fdeploy\u002Fai-auto-deploy",[935,936,937,938,939],"部署","CI\u002FCD","Claude Code","GitHub Actions","自动化","0C0tbe1lwERKuzXgnhYWUeLJ7FFDITUdnTFvc0MVs-E",{"tools":4,"reviews":5,"playbooks":154,"news":135},1782316489335]