[{"data":1,"prerenderedAt":1988},["ShallowReactive",2],{"navigation:en":3,"\u002Fblog\u002Fgit-ramas-stage-production-primer-lanzamiento":311},[4],{"title":5,"path":6,"stem":7,"children":8,"icon":10},"Nuxfire Docs","\u002Fdocs","docs\u002F0.index",[9,11,152],{"title":5,"path":6,"stem":7,"icon":10},"i-lucide-layout-grid",{"title":12,"path":13,"stem":14,"children":15,"icon":151},"SaaS Starter Kit","\u002Fdocs\u002Fsaas-starter-kit","docs\u002F1.saas-starter-kit\u002F0.index",[16,19,63,129],{"title":17,"path":13,"stem":14,"icon":18},"What is the SaaS Starter Kit?","i-lucide-info",{"title":20,"icon":21,"hide":22,"path":23,"stem":24,"children":25,"page":62},"Getting Started","i-lucide-play",true,"\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started","docs\u002F1.saas-starter-kit\u002F1.getting-started",[26,30,34,38,42,46,50,54,58],{"title":27,"path":28,"stem":29},"Quick Start","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fquick-start","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F0.quick-start",{"title":31,"path":32,"stem":33},"Installation","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Finstallation","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F1.installation",{"title":35,"path":36,"stem":37},"Cloudflare Access","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fcloudflare-access","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F2.cloudflare-access",{"title":39,"path":40,"stem":41},"Configuration","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fconfiguration","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F3.configuration",{"title":43,"path":44,"stem":45},"Secrets","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fsecrets","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F4.secrets",{"title":47,"path":48,"stem":49},"Environment Variables","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fenvironment-variables","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F5.environment-variables",{"title":51,"path":52,"stem":53},"Deploy","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fdeploy","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F6.deploy",{"title":55,"path":56,"stem":57},"Local Development","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Flocal-development","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F7.local-development",{"title":59,"path":60,"stem":61},"Project Structure","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fproject-structure","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F8.project-structure",false,{"title":64,"icon":65,"hide":22,"path":66,"stem":67,"children":68,"page":62},"Features","i-lucide-box","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures","docs\u002F1.saas-starter-kit\u002F2.features",[69,73,77,81,85,89,93,97,101,105,109,113,117,121,125],{"title":70,"path":71,"stem":72},"Authentication","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fauth","docs\u002F1.saas-starter-kit\u002F2.features\u002F1.auth",{"title":74,"path":75,"stem":76},"Rate Limiting","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Frate-limiting","docs\u002F1.saas-starter-kit\u002F2.features\u002F10.rate-limiting",{"title":78,"path":79,"stem":80},"WebSockets","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fwebsockets","docs\u002F1.saas-starter-kit\u002F2.features\u002F11.websockets",{"title":82,"path":83,"stem":84},"Blog","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fblog","docs\u002F1.saas-starter-kit\u002F2.features\u002F14.blog",{"title":86,"path":87,"stem":88},"Documentation","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fdocumentation","docs\u002F1.saas-starter-kit\u002F2.features\u002F15.documentation",{"title":90,"path":91,"stem":92},"Analytics","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fanalytics","docs\u002F1.saas-starter-kit\u002F2.features\u002F16.analytics",{"title":94,"path":95,"stem":96},"Platform Admin Console","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fplatform-admin","docs\u002F1.saas-starter-kit\u002F2.features\u002F17.platform-admin",{"title":98,"path":99,"stem":100},"Database","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fdatabase","docs\u002F1.saas-starter-kit\u002F2.features\u002F2.database",{"title":102,"path":103,"stem":104},"Payments & Billing","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fpayments","docs\u002F1.saas-starter-kit\u002F2.features\u002F3.payments",{"title":106,"path":107,"stem":108},"Emails","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Femails","docs\u002F1.saas-starter-kit\u002F2.features\u002F4.emails",{"title":110,"path":111,"stem":112},"Notifications","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fnotifications","docs\u002F1.saas-starter-kit\u002F2.features\u002F5.notifications",{"title":114,"path":115,"stem":116},"Role-Based Access Control","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Frbac","docs\u002F1.saas-starter-kit\u002F2.features\u002F6.rbac",{"title":118,"path":119,"stem":120},"Async Jobs","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fasync-jobs","docs\u002F1.saas-starter-kit\u002F2.features\u002F7.async-jobs",{"title":122,"path":123,"stem":124},"Storage","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fstorage","docs\u002F1.saas-starter-kit\u002F2.features\u002F8.storage",{"title":126,"path":127,"stem":128},"Internationalization","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Finternationalization","docs\u002F1.saas-starter-kit\u002F2.features\u002F9.internationalization",{"title":130,"icon":131,"path":132,"stem":133,"children":134,"page":62},"Concepts","i-lucide-graduation-cap","\u002Fdocs\u002Fsaas-starter-kit\u002Fconcepts","docs\u002F1.saas-starter-kit\u002F3.concepts",[135,139,143,147],{"title":136,"path":137,"stem":138},"Technology Stack","\u002Fdocs\u002Fsaas-starter-kit\u002Fconcepts\u002Ftech-stack","docs\u002F1.saas-starter-kit\u002F3.concepts\u002F0.tech-stack",{"title":140,"path":141,"stem":142},"SST.dev","\u002Fdocs\u002Fsaas-starter-kit\u002Fconcepts\u002Fsst","docs\u002F1.saas-starter-kit\u002F3.concepts\u002F1.sst",{"title":144,"path":145,"stem":146},"NuxtHub","\u002Fdocs\u002Fsaas-starter-kit\u002Fconcepts\u002Fnuxt-hub","docs\u002F1.saas-starter-kit\u002F3.concepts\u002F2.nuxt-hub",{"title":148,"path":149,"stem":150},"Design System & Branding","\u002Fdocs\u002Fsaas-starter-kit\u002Fconcepts\u002Fdesign-system","docs\u002F1.saas-starter-kit\u002F3.concepts\u002F3.design-system","i-lucide-zap",{"title":153,"path":154,"stem":155,"children":156,"icon":159},"Fireskills","\u002Fdocs\u002Ffireskills","docs\u002F2.fireskills\u002F0.index",[157,160,219,249,275,293],{"title":158,"path":154,"stem":155,"icon":159},"What is Fireskills?","i-lucide-flame",{"title":20,"icon":21,"hide":22,"path":161,"stem":162,"children":163,"page":62},"\u002Fdocs\u002Ffireskills\u002Fgetting-started","docs\u002F2.fireskills\u002F1.getting-started",[164,167,171,175,179,183,187,191,195,199,203,207,211,215],{"title":27,"path":165,"stem":166},"\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fquick-start","docs\u002F2.fireskills\u002F1.getting-started\u002F00.quick-start",{"title":168,"path":169,"stem":170},"Workflow & Governance","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fworkflow","docs\u002F2.fireskills\u002F1.getting-started\u002F01.workflow",{"title":172,"path":173,"stem":174},"Project Context","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fproject-context","docs\u002F2.fireskills\u002F1.getting-started\u002F02.project-context",{"title":176,"path":177,"stem":178},"Step 0: Setup","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-setup","docs\u002F2.fireskills\u002F1.getting-started\u002F03.step-setup",{"title":180,"path":181,"stem":182},"Step 1: Inbox","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-inbox","docs\u002F2.fireskills\u002F1.getting-started\u002F04.step-inbox",{"title":184,"path":185,"stem":186},"Step 2: Backlog","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-backlog","docs\u002F2.fireskills\u002F1.getting-started\u002F05.step-backlog",{"title":188,"path":189,"stem":190},"Step 3: Specify","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-specify","docs\u002F2.fireskills\u002F1.getting-started\u002F06.step-specify",{"title":192,"path":193,"stem":194},"Step 4: Validate","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-validate","docs\u002F2.fireskills\u002F1.getting-started\u002F07.step-validate",{"title":196,"path":197,"stem":198},"Step 5: Tasks","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-tasks","docs\u002F2.fireskills\u002F1.getting-started\u002F08.step-tasks",{"title":200,"path":201,"stem":202},"Step 6: TDD\u002FBDD","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-tdd-bdd","docs\u002F2.fireskills\u002F1.getting-started\u002F09.step-tdd-bdd",{"title":204,"path":205,"stem":206},"Step 7: Implement","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-implement","docs\u002F2.fireskills\u002F1.getting-started\u002F10.step-implement",{"title":208,"path":209,"stem":210},"Step 8: Documentator","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-documentator","docs\u002F2.fireskills\u002F1.getting-started\u002F11.step-documentator",{"title":212,"path":213,"stem":214},"Late Changes","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Flate-changes","docs\u002F2.fireskills\u002F1.getting-started\u002F12.late-changes",{"title":216,"path":217,"stem":218},"Other Entry Points","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fother-entry-points","docs\u002F2.fireskills\u002F1.getting-started\u002F13.other-entry-points",{"title":220,"icon":221,"hide":22,"path":222,"stem":223,"children":224,"page":62},"Methodology","i-lucide-git-branch","\u002Fdocs\u002Ffireskills\u002Fmethodology","docs\u002F2.fireskills\u002F2.methodology",[225,229,233,237,241,245],{"title":226,"path":227,"stem":228},"The Three Acts","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Fthree-acts","docs\u002F2.fireskills\u002F2.methodology\u002F0.three-acts",{"title":230,"path":231,"stem":232},"The Three Gates","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Fthree-gates","docs\u002F2.fireskills\u002F2.methodology\u002F1.three-gates",{"title":234,"path":235,"stem":236},"The Canonical Spec","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Fcanonical-spec","docs\u002F2.fireskills\u002F2.methodology\u002F2.canonical-spec",{"title":238,"path":239,"stem":240},"Lifecycle States","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Flifecycle-states","docs\u002F2.fireskills\u002F2.methodology\u002F3.lifecycle-states",{"title":242,"path":243,"stem":244},"Effort Scale","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Feffort-scale","docs\u002F2.fireskills\u002F2.methodology\u002F4.effort-scale",{"title":246,"path":247,"stem":248},"Milestones & Governance","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Fgovernance-and-milestones","docs\u002F2.fireskills\u002F2.methodology\u002F5.governance-and-milestones",{"title":250,"icon":251,"hide":22,"path":252,"stem":253,"children":254,"page":62},"Skills","i-lucide-layers","\u002Fdocs\u002Ffireskills\u002Fskills","docs\u002F2.fireskills\u002F3.skills",[255,259,263,267,271],{"title":256,"path":257,"stem":258},"Catalog","\u002Fdocs\u002Ffireskills\u002Fskills\u002Fcatalog","docs\u002F2.fireskills\u002F3.skills\u002F0.catalog",{"title":260,"path":261,"stem":262},"Act I: Define","\u002Fdocs\u002Ffireskills\u002Fskills\u002Fact-1-define","docs\u002F2.fireskills\u002F3.skills\u002F1.act-1-define",{"title":264,"path":265,"stem":266},"Act II: Design & Prove","\u002Fdocs\u002Ffireskills\u002Fskills\u002Fact-2-design-prove","docs\u002F2.fireskills\u002F3.skills\u002F2.act-2-design-prove",{"title":268,"path":269,"stem":270},"Act III: Deliver","\u002Fdocs\u002Ffireskills\u002Fskills\u002Fact-3-deliver","docs\u002F2.fireskills\u002F3.skills\u002F3.act-3-deliver",{"title":272,"path":273,"stem":274},"Governance & Decisions","\u002Fdocs\u002Ffireskills\u002Fskills\u002Fgovernance-and-decisions","docs\u002F2.fireskills\u002F3.skills\u002F4.governance-and-decisions",{"title":276,"icon":277,"hide":22,"path":278,"stem":279,"children":280,"page":62},"Specialists","i-lucide-sparkles","\u002Fdocs\u002Ffireskills\u002Fspecialists","docs\u002F2.fireskills\u002F4.specialists",[281,285,289],{"title":282,"path":283,"stem":284},"Overview","\u002Fdocs\u002Ffireskills\u002Fspecialists\u002Foverview","docs\u002F2.fireskills\u002F4.specialists\u002F0.overview",{"title":286,"path":287,"stem":288},"Nuxfire Stack","\u002Fdocs\u002Ffireskills\u002Fspecialists\u002Fnuxfire-stack","docs\u002F2.fireskills\u002F4.specialists\u002F1.nuxfire-stack",{"title":290,"path":291,"stem":292},"Engineering Specialists","\u002Fdocs\u002Ffireskills\u002Fspecialists\u002Fengineering-specialists","docs\u002F2.fireskills\u002F4.specialists\u002F2.engineering-specialists",{"title":294,"icon":295,"hide":22,"path":296,"stem":297,"children":298,"page":62},"CLI & Tooling","i-lucide-terminal","\u002Fdocs\u002Ffireskills\u002Fcli","docs\u002F2.fireskills\u002F5.cli",[299,303,307],{"title":300,"path":301,"stem":302},"CLI Overview","\u002Fdocs\u002Ffireskills\u002Fcli\u002Foverview","docs\u002F2.fireskills\u002F5.cli\u002F0.overview",{"title":304,"path":305,"stem":306},"Commands","\u002Fdocs\u002Ffireskills\u002Fcli\u002Fcommands","docs\u002F2.fireskills\u002F5.cli\u002F1.commands",{"title":308,"path":309,"stem":310},"Safety Scripts","\u002Fdocs\u002Ffireskills\u002Fcli\u002Fgovernance-scripts","docs\u002F2.fireskills\u002F5.cli\u002F2.governance-scripts",{"id":312,"title":313,"author":314,"authorType":315,"body":316,"category":1965,"date":1966,"description":1967,"extension":1968,"image":1969,"lang":1970,"meta":1971,"navigation":22,"ogImage":1972,"path":1973,"readTime":1974,"seo":1975,"stem":1976,"tags":1977,"translations":1983,"updated":315,"__hash__":1987},"blog\u002Fblog\u002Fgit-ramas-stage-production-primer-lanzamiento.md","Git para su Primer Lanzamiento: Ramas stage y production, del Repositorio Vacío a Producción","Equipo Nuxfire",null,{"type":317,"value":318,"toc":1945},"minimark",[319,333,336,341,351,354,404,417,421,456,460,539,551,556,562,566,588,594,600,676,697,719,725,729,735,741,759,786,793,813,819,838,842,848,854,860,898,904,921,925,931,940,953,967,986,996,1014,1033,1036,1055,1064,1068,1074,1078,1088,1092,1098,1141,1147,1175,1197,1201,1207,1247,1257,1267,1287,1314,1318,1324,1328,1334,1356,1367,1383,1389,1435,1441,1474,1477,1523,1532,1536,1542,1546,1555,1581,1590,1632,1639,1643,1649,1653,1659,1706,1716,1720,1726,1730,1849,1853,1931,1941],[320,321,322,323,327,328,332],"p",{},"Ha extraído el código fuente de Nuxfire y está a punto de desplegar su primer producto. Antes de ejecutar un solo comando ",[324,325,326],"code",{},"sst",", dedique veinte minutos a lo que decide si los próximos cien despliegues serán tranquilos o estresantes: ",[329,330,331],"strong",{},"cómo se relacionan sus ramas de Git con lo que está en producción",".",[320,334,335],{},"Este manual construye un modelo pequeño y estricto. Cada comando de Git que aparece aquí se ejecutó de principio a fin en un repositorio de prueba antes de publicarlo; las opciones de GitHub CLI provienen de su manual oficial. Cada paso incluye los comandos manuales y un prompt corto que puede pegar en un agente de IA de su IDE (Cursor, Claude Code, Copilot y similares).",[337,338,340],"h2",{"id":339},"el-modelo-en-un-minuto","El modelo en un minuto",[342,343,349],"pre",{"className":344,"code":346,"language":347,"meta":348},[345],"language-text","feature\u002Fx ──▶ stage ──(validated)──▶ production\n              default branch          moves ONLY by fast-forward\n              you work here           = what you approved for live\n","text","",[324,350,346],{"__ignoreMap":348},[320,352,353],{},"Dos ramas de larga duración y cinco reglas:",[355,356,357,367,378,386,394],"ol",{},[358,359,360,366],"li",{},[329,361,362,363,332],{},"Todo el trabajo ocurre en ",[324,364,365],{},"stage"," Es la rama predeterminada, así que los pull requests y los clones llegan a ella.",[358,368,369,377],{},[329,370,371,374,375,332],{},[324,372,373],{},"production"," solo avanza por fast-forward desde ",[324,376,365],{}," Sin commits de merge y sin commits directos.",[358,379,380,385],{},[329,381,382,383,332],{},"Nunca haga force-push sobre ",[324,384,373],{}," Si un push es rechazado, es la red de seguridad funcionando.",[358,387,388,393],{},[329,389,390,391,332],{},"Despliegue producción desde la rama ",[324,392,373],{}," Un despliegue publica lo que tenga en checkout en su máquina, así que la rama en la que está es la verdad.",[358,395,396,399,400,403],{},[329,397,398],{},"Etiquete con un tag cada release que salió a producción"," (",[324,401,402],{},"v1.0.0","). La rama dice \"aprobado\", el tag dice \"confirmado en producción\".",[320,405,406,407,410,411,413,414,416],{},"¿Por qué no conservar también ",[324,408,409],{},"main","? Una tercera rama de larga duración es una tercera respuesta a \"¿cuál es el código más reciente?\". Nuxfire ya nombra sus etapas de despliegue ",[324,412,365],{}," y ",[324,415,373],{},", así que las ramas replican esos nombres y no hay nada que traducir mentalmente.",[337,418,420],{"id":419},"qué-necesita","Qué necesita",[422,423,424,438,445],"ul",{},[358,425,426,427,430,431,434,435,332],{},"Git ",[329,428,429],{},"2.28 o superior"," (para ",[324,432,433],{},"git init -b","). Compruébelo con ",[324,436,437],{},"git --version",[358,439,440,441,444],{},"Una cuenta de GitHub (personal u organización). GitHub CLI (",[324,442,443],{},"gh",") es opcional; cada paso muestra también el camino por la interfaz web.",[358,446,447,448,451,452,332],{},"La carpeta de Nuxfire extraída y ",[324,449,450],{},"bun install"," ejecutado, como en ",[453,454,455],"a",{"href":32},"Instalación",[337,457,459],{"id":458},"paso-1-dígale-a-git-quién-es-usted","Paso 1: dígale a Git quién es usted",[342,461,465],{"className":462,"code":463,"language":464,"meta":348,"style":348},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark monokai","git --version\ngit config --global user.name \"Your Name\"\ngit config --global user.email \"you@example.com\"\ngit config --global init.defaultBranch stage\n","bash",[324,466,467,480,505,524],{"__ignoreMap":348},[468,469,472,476],"span",{"class":470,"line":471},"line",1,[468,473,475],{"class":474},"sR7ES","git",[468,477,479],{"class":478},"sFhLe"," --version\n",[468,481,483,485,489,492,495,499,502],{"class":470,"line":482},2,[468,484,475],{"class":474},[468,486,488],{"class":487},"sLACW"," config",[468,490,491],{"class":478}," --global",[468,493,494],{"class":487}," user.name",[468,496,498],{"class":497},"siCPE"," \"",[468,500,501],{"class":487},"Your Name",[468,503,504],{"class":497},"\"\n",[468,506,508,510,512,514,517,519,522],{"class":470,"line":507},3,[468,509,475],{"class":474},[468,511,488],{"class":487},[468,513,491],{"class":478},[468,515,516],{"class":487}," user.email",[468,518,498],{"class":497},[468,520,521],{"class":487},"you@example.com",[468,523,504],{"class":497},[468,525,527,529,531,533,536],{"class":470,"line":526},4,[468,528,475],{"class":474},[468,530,488],{"class":487},[468,532,491],{"class":478},[468,534,535],{"class":487}," init.defaultBranch",[468,537,538],{"class":487}," stage\n",[320,540,541,542,544,545,548,549,332],{},"La última línea hace que todo repositorio nuevo que cree empiece en ",[324,543,365],{}," en lugar de ",[324,546,547],{},"master"," o ",[324,550,409],{},[320,552,553],{},[329,554,555],{},"Prompt para agentes de IA:",[342,557,560],{"className":558,"code":559,"language":347,"meta":348},[345],"Check `git --version` (2.28 or newer is required). Show my global git\nuser.name and user.email. If either is empty, ask me for the values\nbefore setting them. Do not change anything else.\n",[324,561,559],{"__ignoreMap":348},[337,563,565],{"id":564},"paso-2-proteja-sus-secretos-y-haga-el-primer-commit","Paso 2: proteja sus secretos y haga el primer commit",[320,567,568,569,572,573,413,576,579,580,583,584,587],{},"Su carpeta ya contiene archivos que ",[329,570,571],{},"nunca"," deben llegar a Git: ",[324,574,575],{},".env.production",[324,577,578],{},".env.stage"," guardan credenciales reales (la comprobación previa del despliegue incluso verifica que los valores de la base de datos estén rellenos), y ",[324,581,582],{},".sst"," es el estado local del despliegue. El ",[324,585,586],{},".gitignore"," de Nuxfire los cubre:",[342,589,592],{"className":590,"code":591,"language":347,"meta":348},[345],".env\n.env.*\n!.env.example\n.sst\n",[324,593,591],{"__ignoreMap":348},[320,595,596,597,599],{},"Cree el repositorio en ",[324,598,365],{},", demuestre que las reglas funcionan y solo entonces añada todo:",[342,601,603],{"className":462,"code":602,"language":464,"meta":348,"style":348},"git init -b stage\ngit check-ignore -v .env.production .env.stage .sst\ngit add -A\ngit diff --cached --name-only | grep -E \"(^|\u002F)\\.env\"\n",[324,604,605,617,636,646],{"__ignoreMap":348},[468,606,607,609,612,615],{"class":470,"line":471},[468,608,475],{"class":474},[468,610,611],{"class":487}," init",[468,613,614],{"class":478}," -b",[468,616,538],{"class":487},[468,618,619,621,624,627,630,633],{"class":470,"line":482},[468,620,475],{"class":474},[468,622,623],{"class":487}," check-ignore",[468,625,626],{"class":478}," -v",[468,628,629],{"class":487}," .env.production",[468,631,632],{"class":487}," .env.stage",[468,634,635],{"class":487}," .sst\n",[468,637,638,640,643],{"class":470,"line":507},[468,639,475],{"class":474},[468,641,642],{"class":487}," add",[468,644,645],{"class":478}," -A\n",[468,647,648,650,653,656,659,663,666,669,671,674],{"class":470,"line":526},[468,649,475],{"class":474},[468,651,652],{"class":487}," diff",[468,654,655],{"class":478}," --cached",[468,657,658],{"class":478}," --name-only",[468,660,662],{"class":661},"sGXK2"," |",[468,664,665],{"class":474}," grep",[468,667,668],{"class":478}," -E",[468,670,498],{"class":497},[468,672,673],{"class":487},"(^|\u002F)\\.env",[468,675,504],{"class":497},[320,677,678,681,682,685,686,689,690,693,694,696],{},[324,679,680],{},"check-ignore"," debe imprimir la regla que coincide con cada archivo. El último comando debe imprimir ",[329,683,684],{},"únicamente"," ",[324,687,688],{},".env.example",". Si aparece cualquier otro archivo ",[324,691,692],{},".env*",", deténgase y corrija ",[324,695,586],{}," antes de hacer el commit.",[342,698,700],{"className":462,"code":699,"language":464,"meta":348,"style":348},"git commit -m \"chore: initial commit from Nuxfire\"\n",[324,701,702],{"__ignoreMap":348},[468,703,704,706,709,712,714,717],{"class":470,"line":471},[468,705,475],{"class":474},[468,707,708],{"class":487}," commit",[468,710,711],{"class":478}," -m",[468,713,498],{"class":497},[468,715,716],{"class":487},"chore: initial commit from Nuxfire",[468,718,504],{"class":497},[720,721,722],"blockquote",{},[320,723,724],{},"Si alguna vez se sube un secreto en un commit, borrar el archivo no basta: sigue en el historial. Trate la credencial como comprometida, rótela primero y solo después limpie el historial.",[320,726,727],{},[329,728,555],{},[342,730,733],{"className":731,"code":732,"language":347,"meta":348},[345],"In the project root, run `git init -b stage` only if there is no .git\nfolder. With `git check-ignore -v`, verify that .env.production,\n.env.stage and .sst are ignored and that .env.example is not. Run\n`git add -A` and print `git diff --cached --name-only`. STOP and warn me\nif any .env* file other than .env.example is listed. If it is clean,\ncommit with the message \"chore: initial commit from Nuxfire\".\n",[324,734,732],{"__ignoreMap":348},[337,736,738,739],{"id":737},"paso-3-cree-el-repositorio-en-github-y-suba-stage","Paso 3: cree el repositorio en GitHub y suba ",[324,740,365],{},[320,742,743,744,747,748,750,751,754,755,758],{},"Cree un repositorio ",[329,745,746],{},"privado y vacío",": sin README, sin ",[324,749,586],{},", sin licencia. En el navegador use ",[329,752,753],{},"New repository",", elija ",[329,756,757],{},"Private"," y deje sin marcar las opciones de inicialización. O con la CLI, desde la carpeta del proyecto:",[342,760,762],{"className":462,"code":761,"language":464,"meta":348,"style":348},"gh repo create my-product --private --source=. --remote=origin\n",[324,763,764],{"__ignoreMap":348},[468,765,766,768,771,774,777,780,783],{"class":470,"line":471},[468,767,443],{"class":474},[468,769,770],{"class":487}," repo",[468,772,773],{"class":487}," create",[468,775,776],{"class":487}," my-product",[468,778,779],{"class":478}," --private",[468,781,782],{"class":478}," --source=.",[468,784,785],{"class":478}," --remote=origin\n",[320,787,788,789,792],{},"La CLI añade el remoto ",[324,790,791],{},"origin"," por usted. Sin ella, añádalo manualmente con la URL que le muestra GitHub:",[342,794,796],{"className":462,"code":795,"language":464,"meta":348,"style":348},"git remote add origin git@github.com:YOUR-USER\u002Fmy-product.git\n",[324,797,798],{"__ignoreMap":348},[468,799,800,802,805,807,810],{"class":470,"line":471},[468,801,475],{"class":474},[468,803,804],{"class":487}," remote",[468,806,642],{"class":487},[468,808,809],{"class":487}," origin",[468,811,812],{"class":487}," git@github.com:YOUR-USER\u002Fmy-product.git\n",[320,814,815,816,818],{},"En ambos casos, publique ",[324,817,365],{}," y configure el seguimiento del remoto:",[342,820,822],{"className":462,"code":821,"language":464,"meta":348,"style":348},"git push -u origin stage\n",[324,823,824],{"__ignoreMap":348},[468,825,826,828,831,834,836],{"class":470,"line":471},[468,827,475],{"class":474},[468,829,830],{"class":487}," push",[468,832,833],{"class":478}," -u",[468,835,809],{"class":487},[468,837,538],{"class":487},[320,839,840],{},[329,841,555],{},[342,843,846],{"className":844,"code":845,"language":347,"meta":348},[345],"Create a PRIVATE, empty GitHub repository named \u003Cname> under \u003Cowner>\n(no README, no .gitignore, no license), add it as `origin`, and push the\n`stage` branch with upstream tracking. Use `gh` if it is installed;\notherwise print the exact commands for me to run. Never use --force.\n",[324,847,845],{"__ignoreMap":348},[337,849,851,852],{"id":850},"paso-4-cree-production","Paso 4: cree ",[324,853,373],{},[320,855,856,857,859],{},"Cree la rama a partir de ",[324,858,365],{},", en el mismo commit, publíquela y vuelva:",[342,861,863],{"className":462,"code":862,"language":464,"meta":348,"style":348},"git switch -c production\ngit push -u origin production\ngit switch stage\n",[324,864,865,878,890],{"__ignoreMap":348},[468,866,867,869,872,875],{"class":470,"line":471},[468,868,475],{"class":474},[468,870,871],{"class":487}," switch",[468,873,874],{"class":478}," -c",[468,876,877],{"class":487}," production\n",[468,879,880,882,884,886,888],{"class":470,"line":482},[468,881,475],{"class":474},[468,883,830],{"class":487},[468,885,833],{"class":478},[468,887,809],{"class":487},[468,889,877],{"class":487},[468,891,892,894,896],{"class":470,"line":507},[468,893,475],{"class":474},[468,895,871],{"class":487},[468,897,538],{"class":487},[320,899,900,901,903],{},"Ahora añada una protección local para que ",[324,902,373],{}," solo pueda avanzar por fast-forward. Git rechazará cualquier merge que no sea un fast-forward:",[342,905,907],{"className":462,"code":906,"language":464,"meta":348,"style":348},"git config branch.production.mergeOptions --ff-only\n",[324,908,909],{"__ignoreMap":348},[468,910,911,913,915,918],{"class":470,"line":471},[468,912,475],{"class":474},[468,914,488],{"class":487},[468,916,917],{"class":487}," branch.production.mergeOptions",[468,919,920],{"class":478}," --ff-only\n",[320,922,923],{},[329,924,555],{},[342,926,929],{"className":927,"code":928,"language":347,"meta":348},[345],"Create branch `production` from `stage`, push it with upstream tracking,\nswitch back to `stage`, then run\n`git config branch.production.mergeOptions --ff-only`.\nConfirm the result with `git branch -vv`.\n",[324,930,928],{"__ignoreMap":348},[337,932,934,935,937,938],{"id":933},"paso-5-haga-de-stage-la-rama-predeterminada-y-elimine-main","Paso 5: haga de ",[324,936,365],{}," la rama predeterminada y elimine ",[324,939,409],{},[320,941,942,943,945,946,949,950,952],{},"Quien decide la rama predeterminada es GitHub, no su Git local. Incluso en un repositorio vacío puede seguir siendo ",[324,944,409],{},", así que compruébelo de forma explícita. GitHub exige ",[329,947,948],{},"más de una rama"," para cambiar la predeterminada, y por eso ",[324,951,373],{}," tenía que existir antes.",[320,954,955,956,959,960,962,963,966],{},"En el navegador: ",[329,957,958],{},"Settings → Branches → Default branch",", pulse el icono de cambio, elija ",[324,961,365],{},", ",[329,964,965],{},"Update"," y confirme. Con la CLI:",[342,968,970],{"className":462,"code":969,"language":464,"meta":348,"style":348},"gh repo edit --default-branch stage\n",[324,971,972],{"__ignoreMap":348},[468,973,974,976,978,981,984],{"class":470,"line":471},[468,975,443],{"class":474},[468,977,770],{"class":487},[468,979,980],{"class":487}," edit",[468,982,983],{"class":478}," --default-branch",[468,985,538],{"class":487},[320,987,988,989,991,992,995],{},"Después mire qué existe en el remoto y, si ",[324,990,409],{}," está ahí (por ejemplo, porque el repositorio se creó con un README), elimínela. Hágalo ",[329,993,994],{},"después"," de cambiar la predeterminada, nunca antes:",[342,997,999],{"className":462,"code":998,"language":464,"meta":348,"style":348},"git ls-remote --heads origin\n",[324,1000,1001],{"__ignoreMap":348},[468,1002,1003,1005,1008,1011],{"class":470,"line":471},[468,1004,475],{"class":474},[468,1006,1007],{"class":487}," ls-remote",[468,1009,1010],{"class":478}," --heads",[468,1012,1013],{"class":487}," origin\n",[342,1015,1017],{"className":462,"code":1016,"language":464,"meta":348,"style":348},"git push origin --delete main\n",[324,1018,1019],{"__ignoreMap":348},[468,1020,1021,1023,1025,1027,1030],{"class":470,"line":471},[468,1022,475],{"class":474},[468,1024,830],{"class":487},[468,1026,809],{"class":487},[468,1028,1029],{"class":478}," --delete",[468,1031,1032],{"class":487}," main\n",[320,1034,1035],{},"Por último, actualice su puntero local a la rama predeterminada del remoto:",[342,1037,1039],{"className":462,"code":1038,"language":464,"meta":348,"style":348},"git remote set-head origin --auto\n",[324,1040,1041],{"__ignoreMap":348},[468,1042,1043,1045,1047,1050,1052],{"class":470,"line":471},[468,1044,475],{"class":474},[468,1046,804],{"class":487},[468,1048,1049],{"class":487}," set-head",[468,1051,809],{"class":487},[468,1053,1054],{"class":478}," --auto\n",[320,1056,1057,1058,1060,1061,1063],{},"Si ",[324,1059,409],{}," se creó con un README, tiene un historial sin relación con el suyo. No la fusione en ",[324,1062,365],{},"; simplemente elimínela.",[320,1065,1066],{},[329,1067,555],{},[342,1069,1072],{"className":1070,"code":1071,"language":347,"meta":348},[345],"Check which branch is the GitHub default with\n`gh repo view --json defaultBranchRef`. If it is not `stage`, run\n`gh repo edit --default-branch stage`. Then run\n`git ls-remote --heads origin`. If `main` exists, tell me and WAIT for my\nconfirmation before running `git push origin --delete main`. Finish with\n`git remote set-head origin --auto`.\n",[324,1073,1071],{"__ignoreMap":348},[337,1075,1077],{"id":1076},"paso-6-protecciones-en-github-si-su-plan-las-incluye","Paso 6: protecciones en GitHub (si su plan las incluye)",[320,1079,1080,1081,1084,1085,1087],{},"La protección local ",[324,1082,1083],{},"--ff-only"," no cuesta nada y no depende de ningún plan. En GitHub, la protección de ramas y los rulesets dependen de su plan y de la visibilidad del repositorio (los rulesets están documentados para los planes GitHub Team y GitHub Enterprise). Si el suyo los ofrece, proteja ",[324,1086,373],{}," bloqueando los force pushes y restringiendo las eliminaciones. Si no, el proceso y la protección local bastan siempre que nunca haga force-push.",[337,1089,1091],{"id":1090},"paso-7-el-día-a-día","Paso 7: el día a día",[320,1093,1094,1095,1097],{},"Si trabaja solo, haga commit en ",[324,1096,365],{}," y súbalo:",[342,1099,1101],{"className":462,"code":1100,"language":464,"meta":348,"style":348},"git switch stage\ngit add -A\ngit commit -m \"feat: describe the change\"\ngit push\n",[324,1102,1103,1111,1119,1134],{"__ignoreMap":348},[468,1104,1105,1107,1109],{"class":470,"line":471},[468,1106,475],{"class":474},[468,1108,871],{"class":487},[468,1110,538],{"class":487},[468,1112,1113,1115,1117],{"class":470,"line":482},[468,1114,475],{"class":474},[468,1116,642],{"class":487},[468,1118,645],{"class":478},[468,1120,1121,1123,1125,1127,1129,1132],{"class":470,"line":507},[468,1122,475],{"class":474},[468,1124,708],{"class":487},[468,1126,711],{"class":478},[468,1128,498],{"class":497},[468,1130,1131],{"class":487},"feat: describe the change",[468,1133,504],{"class":497},[468,1135,1136,1138],{"class":470,"line":526},[468,1137,475],{"class":474},[468,1139,1140],{"class":487}," push\n",[320,1142,1143,1144,1146],{},"Si trabaja en equipo, use ramas cortas y pull requests hacia ",[324,1145,365],{},":",[342,1148,1150],{"className":462,"code":1149,"language":464,"meta":348,"style":348},"git switch -c feat\u002Fbilling-copy\ngit push -u origin feat\u002Fbilling-copy\n",[324,1151,1152,1163],{"__ignoreMap":348},[468,1153,1154,1156,1158,1160],{"class":470,"line":471},[468,1155,475],{"class":474},[468,1157,871],{"class":487},[468,1159,874],{"class":478},[468,1161,1162],{"class":487}," feat\u002Fbilling-copy\n",[468,1164,1165,1167,1169,1171,1173],{"class":470,"line":482},[468,1166,475],{"class":474},[468,1168,830],{"class":487},[468,1170,833],{"class":478},[468,1172,809],{"class":487},[468,1174,1162],{"class":487},[320,1176,1177,1178,399,1184,962,1187,962,1190,962,1193,1196],{},"Use ",[453,1179,1183],{"href":1180,"rel":1181},"https:\u002F\u002Fwww.conventionalcommits.org\u002F",[1182],"nofollow","Conventional Commits",[324,1185,1186],{},"feat:",[324,1188,1189],{},"fix:",[324,1191,1192],{},"docs:",[324,1194,1195],{},"chore:","). El historial del propio Nuxfire los sigue, lo que deja las notas de release casi gratis.",[337,1198,1200],{"id":1199},"paso-8-la-puerta-antes-de-su-primer-lanzamiento","Paso 8: la puerta antes de su primer lanzamiento",[320,1202,1203,1204,1206],{},"Nada llega a ",[324,1205,373],{}," sin pasar siempre por las mismas comprobaciones:",[342,1208,1210],{"className":462,"code":1209,"language":464,"meta":348,"style":348},"bun install\nbun run test\nbun run doctor -- --stage production\n",[324,1211,1212,1220,1230],{"__ignoreMap":348},[468,1213,1214,1217],{"class":470,"line":471},[468,1215,1216],{"class":474},"bun",[468,1218,1219],{"class":487}," install\n",[468,1221,1222,1224,1227],{"class":470,"line":482},[468,1223,1216],{"class":474},[468,1225,1226],{"class":487}," run",[468,1228,1229],{"class":487}," test\n",[468,1231,1232,1234,1236,1239,1242,1245],{"class":470,"line":507},[468,1233,1216],{"class":474},[468,1235,1226],{"class":487},[468,1237,1238],{"class":487}," doctor",[468,1240,1241],{"class":478}," --",[468,1243,1244],{"class":478}," --stage",[468,1246,877],{"class":487},[320,1248,1249,1252,1253,1256],{},[324,1250,1251],{},"doctor"," es la comprobación previa del despliegue: valida su token de Cloudflare, sus valores ",[324,1254,1255],{},"BRAND_*",", los secretos de la base de datos y la zona del dominio, e indica la corrección exacta de cada problema antes de que pierda minutos en un despliegue que va a fallar.",[320,1258,1259,1260,1263,1264,1146],{},"Para un primer producto, despliegue una ",[329,1261,1262],{},"etapa de validación"," antes de producción. Una etapa que no es de producción obtiene sus propios Workers, conexión de base de datos y secretos, y responde en ",[324,1265,1266],{},"\u003Cstage>.dev.\u003Csu-dominio>",[342,1268,1270],{"className":462,"code":1269,"language":464,"meta":348,"style":348},"bun run deploy -- --stage stage\n",[324,1271,1272],{"__ignoreMap":348},[468,1273,1274,1276,1278,1281,1283,1285],{"class":470,"line":471},[468,1275,1216],{"class":474},[468,1277,1226],{"class":487},[468,1279,1280],{"class":487}," deploy",[468,1282,1241],{"class":478},[468,1284,1244],{"class":478},[468,1286,538],{"class":487},[320,1288,1289,1290,1293,1294,962,1297,413,1299,1302,1303,685,1306,1309,1310,1313],{},"El ",[324,1291,1292],{},"--"," es necesario para que la opción llegue al script. El token y los secretos se explican en ",[453,1295,1296],{"href":36},"Acceso a Cloudflare",[453,1298,43],{"href":44},[453,1300,1301],{"href":52},"Despliegue",". Conviene conocer desde ya un requisito exclusivo de producción: el dominio raíz y ",[324,1304,1305],{},"www",[329,1307,1308],{},"no"," pueden tener ningún registro A, AAAA o CNAME en su DNS; de lo contrario, Cloudflare rechaza el dominio personalizado (error ",[324,1311,1312],{},"100117",").",[320,1315,1316],{},[329,1317,555],{},[342,1319,1322],{"className":1320,"code":1321,"language":347,"meta":348},[345],"Run `bun install`, `bun run test` and\n`bun run doctor -- --stage production`. Report every failure with its\nexact message and the fix. Do NOT deploy anything.\n",[324,1323,1321],{"__ignoreMap":348},[337,1325,1327],{"id":1326},"paso-9-el-primer-lanzamiento","Paso 9: el primer lanzamiento",[320,1329,1330,1331,1333],{},"Todo está en verde en ",[324,1332,365],{},". Promuévala:",[342,1335,1337],{"className":462,"code":1336,"language":464,"meta":348,"style":348},"git status\ngit push origin stage\n",[324,1338,1339,1346],{"__ignoreMap":348},[468,1340,1341,1343],{"class":470,"line":471},[468,1342,475],{"class":474},[468,1344,1345],{"class":487}," status\n",[468,1347,1348,1350,1352,1354],{"class":470,"line":482},[468,1349,475],{"class":474},[468,1351,830],{"class":487},[468,1353,809],{"class":487},[468,1355,538],{"class":487},[320,1357,1358,1361,1362,1364,1365,1146],{},[324,1359,1360],{},"git status"," debe indicar que el árbol de trabajo está limpio. Ahora avance ",[324,1363,373],{}," por fast-forward exactamente al mismo commit, sin salir de ",[324,1366,365],{},[342,1368,1370],{"className":462,"code":1369,"language":464,"meta":348,"style":348},"git push origin stage:production\n",[324,1371,1372],{"__ignoreMap":348},[468,1373,1374,1376,1378,1380],{"class":470,"line":471},[468,1375,475],{"class":474},[468,1377,830],{"class":487},[468,1379,809],{"class":487},[468,1381,1382],{"class":487}," stage:production\n",[320,1384,1385,1386,1388],{},"Ese push solo se acepta si es un fast-forward puro. Compruebe que las dos ramas son idénticas y cambie a ",[324,1387,373],{},", porque es la rama desde la que despliega:",[342,1390,1392],{"className":462,"code":1391,"language":464,"meta":348,"style":348},"git fetch origin\ngit diff --stat stage origin\u002Fproduction\ngit switch production\ngit pull --ff-only\n",[324,1393,1394,1403,1418,1426],{"__ignoreMap":348},[468,1395,1396,1398,1401],{"class":470,"line":471},[468,1397,475],{"class":474},[468,1399,1400],{"class":487}," fetch",[468,1402,1013],{"class":487},[468,1404,1405,1407,1409,1412,1415],{"class":470,"line":482},[468,1406,475],{"class":474},[468,1408,652],{"class":487},[468,1410,1411],{"class":478}," --stat",[468,1413,1414],{"class":487}," stage",[468,1416,1417],{"class":487}," origin\u002Fproduction\n",[468,1419,1420,1422,1424],{"class":470,"line":507},[468,1421,475],{"class":474},[468,1423,871],{"class":487},[468,1425,877],{"class":487},[468,1427,1428,1430,1433],{"class":470,"line":526},[468,1429,475],{"class":474},[468,1431,1432],{"class":487}," pull",[468,1434,920],{"class":478},[320,1436,1289,1437,1440],{},[324,1438,1439],{},"diff"," no debe imprimir nada. Ejecute la comprobación previa una vez más y despliegue:",[342,1442,1444],{"className":462,"code":1443,"language":464,"meta":348,"style":348},"bun run doctor -- --stage production\nbun run deploy -- --stage production\n",[324,1445,1446,1460],{"__ignoreMap":348},[468,1447,1448,1450,1452,1454,1456,1458],{"class":470,"line":471},[468,1449,1216],{"class":474},[468,1451,1226],{"class":487},[468,1453,1238],{"class":487},[468,1455,1241],{"class":478},[468,1457,1244],{"class":478},[468,1459,877],{"class":487},[468,1461,1462,1464,1466,1468,1470,1472],{"class":470,"line":482},[468,1463,1216],{"class":474},[468,1465,1226],{"class":487},[468,1467,1280],{"class":487},[468,1469,1241],{"class":478},[468,1471,1244],{"class":478},[468,1473,877],{"class":487},[320,1475,1476],{},"Solo cuando el despliegue termine bien, etiquete el release y vuelva al trabajo:",[342,1478,1480],{"className":462,"code":1479,"language":464,"meta":348,"style":348},"git tag -a v1.0.0 -m \"First production release\"\ngit push origin v1.0.0\ngit switch stage\n",[324,1481,1482,1504,1515],{"__ignoreMap":348},[468,1483,1484,1486,1489,1492,1495,1497,1499,1502],{"class":470,"line":471},[468,1485,475],{"class":474},[468,1487,1488],{"class":487}," tag",[468,1490,1491],{"class":478}," -a",[468,1493,1494],{"class":487}," v1.0.0",[468,1496,711],{"class":478},[468,1498,498],{"class":497},[468,1500,1501],{"class":487},"First production release",[468,1503,504],{"class":497},[468,1505,1506,1508,1510,1512],{"class":470,"line":482},[468,1507,475],{"class":474},[468,1509,830],{"class":487},[468,1511,809],{"class":487},[468,1513,1514],{"class":487}," v1.0.0\n",[468,1516,1517,1519,1521],{"class":470,"line":507},[468,1518,475],{"class":474},[468,1520,871],{"class":487},[468,1522,538],{"class":487},[320,1524,1525,1526,1528,1529,1531],{},"Si el despliegue falla, no edite ",[324,1527,373],{},". Corrija hacia adelante en ",[324,1530,365],{},", vuelva a pasar la puerta y repita el fast-forward. La rama puede quedar brevemente por delante de lo que está en producción, y la ausencia del tag se lo indica exactamente.",[320,1533,1534],{},[329,1535,555],{},[342,1537,1540],{"className":1538,"code":1539,"language":347,"meta":348},[345],"Release checklist for the first release. Stop at the first failure:\n1. `git status` must be clean and on `stage`.\n2. `git push origin stage`.\n3. `git push origin stage:production`. If it is rejected, STOP and show\n   me `git log --oneline stage..production`. Never force.\n4. `git switch production && git pull --ff-only`.\n5. `bun run doctor -- --stage production`.\n6. Ask me for explicit confirmation, then run\n   `bun run deploy -- --stage production`.\n7. Only after a successful deploy: `git tag -a v1.0.0 -m \"First\n   production release\"` and `git push origin v1.0.0`.\n8. `git switch stage`.\n",[324,1541,1539],{"__ignoreMap":348},[337,1543,1545],{"id":1544},"paso-10-evite-que-las-dos-ramas-se-separen","Paso 10: evite que las dos ramas se separen",[320,1547,1548,1549,1551,1552,1554],{},"El modo de fallo que le ocurre a todo el mundo una vez: una corrección cae en ",[324,1550,373],{}," (o un despliegue sale de ",[324,1553,365],{},") y las ramas dejan de describir el mismo código sin que nadie lo note. La comprobación es una línea y siempre debe imprimir nada:",[342,1556,1558],{"className":462,"code":1557,"language":464,"meta":348,"style":348},"git fetch origin\ngit log --oneline stage..origin\u002Fproduction\n",[324,1559,1560,1568],{"__ignoreMap":348},[468,1561,1562,1564,1566],{"class":470,"line":471},[468,1563,475],{"class":474},[468,1565,1400],{"class":487},[468,1567,1013],{"class":487},[468,1569,1570,1572,1575,1578],{"class":470,"line":482},[468,1571,475],{"class":474},[468,1573,1574],{"class":487}," log",[468,1576,1577],{"class":478}," --oneline",[468,1579,1580],{"class":487}," stage..origin\u002Fproduction\n",[320,1582,1583,1584,1586,1587,1589],{},"Si imprime commits, ",[324,1585,373],{}," tiene trabajo que ",[324,1588,365],{}," no tiene. Una promoción será rechazada, y eso es correcto. Traiga el trabajo de vuelta, publíquelo y avance de nuevo:",[342,1591,1593],{"className":462,"code":1592,"language":464,"meta":348,"style":348},"git switch stage\ngit merge origin\u002Fproduction\ngit push origin stage\ngit push origin stage:production\n",[324,1594,1595,1603,1612,1622],{"__ignoreMap":348},[468,1596,1597,1599,1601],{"class":470,"line":471},[468,1598,475],{"class":474},[468,1600,871],{"class":487},[468,1602,538],{"class":487},[468,1604,1605,1607,1610],{"class":470,"line":482},[468,1606,475],{"class":474},[468,1608,1609],{"class":487}," merge",[468,1611,1417],{"class":487},[468,1613,1614,1616,1618,1620],{"class":470,"line":507},[468,1615,475],{"class":474},[468,1617,830],{"class":487},[468,1619,809],{"class":487},[468,1621,538],{"class":487},[468,1623,1624,1626,1628,1630],{"class":470,"line":526},[468,1625,475],{"class":474},[468,1627,830],{"class":487},[468,1629,809],{"class":487},[468,1631,1382],{"class":487},[320,1633,1634,1635,1638],{},"Si el merge informa de conflictos, resuélvalos, vuelva a pasar la puerta y haga el commit antes de subir. Nunca lo resuelva con ",[324,1636,1637],{},"--force",": borraría justamente los commits que quiere conservar.",[320,1640,1641],{},[329,1642,555],{},[342,1644,1647],{"className":1645,"code":1646,"language":347,"meta":348},[345],"Run `git fetch origin`, then `git log --oneline stage..origin\u002Fproduction`.\nIf it prints anything: merge origin\u002Fproduction into stage, resolve any\nconflicts together with me, run `bun run test`, push stage, then\nfast-forward production with `git push origin stage:production`.\nNever use --force.\n",[324,1648,1646],{"__ignoreMap":348},[337,1650,1652],{"id":1651},"volver-atrás","Volver atrás",[320,1654,1655,1656,1658],{},"No reescriba ",[324,1657,373],{},". Redespliegue el código de un tag anterior:",[342,1660,1662],{"className":462,"code":1661,"language":464,"meta":348,"style":348},"git tag --sort=-creatordate\ngit switch --detach v1.0.0\nbun run deploy -- --stage production\ngit switch stage\n",[324,1663,1664,1673,1684,1698],{"__ignoreMap":348},[468,1665,1666,1668,1670],{"class":470,"line":471},[468,1667,475],{"class":474},[468,1669,1488],{"class":487},[468,1671,1672],{"class":478}," --sort=-creatordate\n",[468,1674,1675,1677,1679,1682],{"class":470,"line":482},[468,1676,475],{"class":474},[468,1678,871],{"class":487},[468,1680,1681],{"class":478}," --detach",[468,1683,1514],{"class":487},[468,1685,1686,1688,1690,1692,1694,1696],{"class":470,"line":507},[468,1687,1216],{"class":474},[468,1689,1226],{"class":487},[468,1691,1280],{"class":487},[468,1693,1241],{"class":478},[468,1695,1244],{"class":478},[468,1697,877],{"class":487},[468,1699,1700,1702,1704],{"class":470,"line":526},[468,1701,475],{"class":474},[468,1703,871],{"class":487},[468,1705,538],{"class":487},[320,1707,1708,1709,1712,1713,1715],{},"Esto restaura el ",[329,1710,1711],{},"código",". Las migraciones de la base de datos se aplican en cada despliegue y no se revierten, así que planifique los cambios de esquema primero como aditivos (añada una columna, publique el código que la usa, elimine la antigua más tarde). Después corrija hacia adelante en ",[324,1714,365],{}," y promueva un nuevo release.",[320,1717,1718],{},[329,1719,555],{},[342,1721,1724],{"className":1722,"code":1723,"language":347,"meta":348},[345],"Do NOT touch the `production` branch. Show me\n`git tag --sort=-creatordate | head`, ask which tag to restore, run\n`git switch --detach \u003Ctag>`, and ask for my explicit confirmation before\n`bun run deploy -- --stage production`. Afterwards run `git switch stage`.\n",[324,1725,1723],{"__ignoreMap":348},[337,1727,1729],{"id":1728},"cuando-algo-sale-mal","Cuando algo sale mal",[1731,1732,1733,1749],"table",{},[1734,1735,1736],"thead",{},[1737,1738,1739,1743,1746],"tr",{},[1740,1741,1742],"th",{},"Lo que ve",[1740,1744,1745],{},"Qué significa",[1740,1747,1748],{},"Qué hacer",[1750,1751,1752,1771,1786,1802,1821,1835],"tbody",{},[1737,1753,1754,1760,1768],{},[1755,1756,1757],"td",{},[324,1758,1759],{},"[rejected] stage -> production (non-fast-forward)",[1755,1761,1762,1764,1765,1767],{},[324,1763,373],{}," tiene commits que ",[324,1766,365],{}," no tiene",[1755,1769,1770],{},"Paso 10. Nunca use force.",[1737,1772,1773,1778,1783],{},[1755,1774,1775],{},[324,1776,1777],{},"fatal: Not possible to fast-forward, aborting.",[1755,1779,1780,1781],{},"La protección local detuvo un merge en ",[324,1782,373],{},[1755,1784,1785],{},"Misma causa y misma solución que arriba",[1737,1787,1788,1793,1796],{},[1755,1789,1790,1791],{},"GitHub no le deja eliminar ",[324,1792,409],{},[1755,1794,1795],{},"Normalmente sigue siendo la rama predeterminada",[1755,1797,1798,1799,1801],{},"Cambie primero la predeterminada a ",[324,1800,365],{}," (Paso 5)",[1737,1803,1804,1813,1818],{},[1755,1805,1806,1807,1809,1810],{},"Un archivo ",[324,1808,692],{}," aparece en ",[324,1811,1812],{},"git diff --cached",[1755,1814,1815,1816],{},"Falta una regla en ",[324,1817,586],{},[1755,1819,1820],{},"Corríjalo antes del commit, nunca después",[1737,1822,1823,1826,1829],{},[1755,1824,1825],{},"Desplegó desde la rama equivocada",[1755,1827,1828],{},"El despliegue publica el árbol en checkout",[1755,1830,1831,1834],{},[324,1832,1833],{},"git branch --show-current"," antes de cada despliegue",[1737,1836,1837,1843,1846],{},[1755,1838,1839,1840,1842],{},"Error de dominio ",[324,1841,1312],{}," en el primer despliegue de producción",[1755,1844,1845],{},"El hostname ya tiene un registro DNS",[1755,1847,1848],{},"Elimine el registro A, AAAA o CNAME y despliegue de nuevo",[337,1850,1852],{"id":1851},"lista-de-verificación","Lista de verificación",[422,1854,1857,1870,1881,1892,1904,1916,1925],{"className":1855},[1856],"contains-task-list",[358,1858,1861,1865,1866,413,1868],{"className":1859},[1860],"task-list-item",[1862,1863],"input",{"disabled":22,"type":1864},"checkbox"," El repositorio es privado y en el remoto solo existen ",[324,1867,365],{},[324,1869,373],{},[358,1871,1873,685,1875,1877,1878,1880],{"className":1872},[1860],[1862,1874],{"disabled":22,"type":1864},[324,1876,365],{}," es la rama predeterminada y ",[324,1879,409],{}," ya no existe",[358,1882,1884,685,1886,1889,1890],{"className":1883},[1860],[1862,1885],{"disabled":22,"type":1864},[324,1887,1888],{},"git config branch.production.mergeOptions"," imprime ",[324,1891,1083],{},[358,1893,1895,1897,1898,1900,1901,1903],{"className":1894},[1860],[1862,1896],{"disabled":22,"type":1864}," Ningún archivo ",[324,1899,692],{},", salvo ",[324,1902,688],{},", está versionado",[358,1905,1907,685,1909,413,1912,1915],{"className":1906},[1860],[1862,1908],{"disabled":22,"type":1864},[324,1910,1911],{},"bun run test",[324,1913,1914],{},"bun run doctor -- --stage production"," están en verde",[358,1917,1919,685,1921,1924],{"className":1918},[1860],[1862,1920],{"disabled":22,"type":1864},[324,1922,1923],{},"git diff --stat stage origin\u002Fproduction"," no imprime nada antes de desplegar",[358,1926,1928,1930],{"className":1927},[1860],[1862,1929],{"disabled":22,"type":1864}," El release solo se etiqueta después de que el despliegue termine bien",[320,1932,1933,1934,1936,1937,413,1939,332],{},"De vuelta a la configuración: continúe con ",[453,1935,1296],{"href":36}," para crear el token de API, después ",[453,1938,43],{"href":44},[453,1940,1301],{"href":52},[1942,1943,1944],"style",{},"html pre.shiki code .sR7ES, html code.shiki .sR7ES{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#A6E22E}html pre.shiki code .sFhLe, html code.shiki .sFhLe{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#AE81FF}html pre.shiki code .sLACW, html code.shiki .sLACW{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#E6DB74}html pre.shiki code .siCPE, html code.shiki .siCPE{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#E6DB74}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sGXK2, html code.shiki .sGXK2{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#F92672}",{"title":348,"searchDepth":482,"depth":482,"links":1946},[1947,1948,1949,1950,1951,1953,1955,1957,1958,1959,1960,1961,1962,1963,1964],{"id":339,"depth":482,"text":340},{"id":419,"depth":482,"text":420},{"id":458,"depth":482,"text":459},{"id":564,"depth":482,"text":565},{"id":737,"depth":482,"text":1952},"Paso 3: cree el repositorio en GitHub y suba stage",{"id":850,"depth":482,"text":1954},"Paso 4: cree production",{"id":933,"depth":482,"text":1956},"Paso 5: haga de stage la rama predeterminada y elimine main",{"id":1076,"depth":482,"text":1077},{"id":1090,"depth":482,"text":1091},{"id":1199,"depth":482,"text":1200},{"id":1326,"depth":482,"text":1327},{"id":1544,"depth":482,"text":1545},{"id":1651,"depth":482,"text":1652},{"id":1728,"depth":482,"text":1729},{"id":1851,"depth":482,"text":1852},"DevOps","2026-09-25","Un manual de nivel sénior para quien acaba de adquirir Nuxfire: cree el repositorio, haga de stage la rama predeterminada, elimine main, promueva a production con fast-forward y publique su primera versión de forma segura.","md","\u002Fimages\u002Fblog\u002Fgit-branching-stage-production.svg","es",{},"\u002Fimages\u002Fblog\u002Fgit-branching-stage-production.png","\u002Fblog\u002Fgit-ramas-stage-production-primer-lanzamiento","14 min",{"title":313,"description":1967},"blog\u002Fgit-ramas-stage-production-primer-lanzamiento",[1978,1979,1980,1981,51,1982],"Git","GitHub","Branching","Release","SST",{"en":1984,"pt-br":1985,"es":1986},"git-branching-stage-production-first-release","git-branches-stage-production-primeiro-lancamento","git-ramas-stage-production-primer-lanzamiento","kFgSXbdgvKLfNJ0h-Dr_ifjZNpygdBR-M903HvaAoLk",1790707502335]