UNPKG

@lunora/cli

Version:

The Lunora CLI: init, dev, deploy, codegen, migrate, seed, doctor, insights, logs, registry, and the rest of the project commands

8 lines (7 loc) 21.9 kB
import{readFileSync as p,writeFileSync as T,existsSync as v}from"node:fs";import{runCodegen as B,discoverMigrations as V}from"@lunora/codegen";import{readLinkedProject as L,writeLinkedProject as K,execArgsFor as k,detectPackageManager as b,discoverContainerInfo as I,DEV_VARS_FILE as y,parseDevVariableEntries as U,isMintableSecretKey as P,runPostCodegenHook as q,resolveDeployDriver as R,inferLunoraBindings as F,packageNamesFromBindings as Y,requiredSecrets as G,generateSecretValue as z,upsertDevVariableLine as J,writeDevVariablesFileAtomically as Q,COMPOSED_WORKER_ENTRY as _,discoverSchemaInfo as X}from"@lunora/config";import{findWranglerFile as W,validateWranglerProject as Z,reconcileWranglerBindings as ee,reconcileWranglerCompatibilityDate as re,reconcileWranglerCrons as te,describePreservedCrons as ne,readWranglerJsonc as ae,mergeWranglerEnvironment as oe}from"@lunora/config/cloudflare";import{join as h}from"@visulima/path";import{Spinner as ie}from"@visulima/spinner";import{Project as se}from"ts-morph";import{i as ce,m as le,e as de}from"../packem_shared/vectorize-metadata-dXpl2_Ar.mjs";import{r as ue,a as ge,e as me}from"../packem_shared/platform-diagnostics-Cn2g6Jh-.mjs";import{p as fe,a as he}from"../packem_shared/deploy-target-B0Xcb2jV.mjs";import{r as E}from"../packem_shared/wrangler-name-Dsk5K1f-.mjs";import{w as ve}from"../packem_shared/binding-manifest-file-CQ1eQYy1.mjs";import{d as ye}from"../packem_shared/command-DwC11Cwe.mjs";import{a as pe,i as we}from"../packem_shared/docker-DpVxvYpL.mjs";import{r as ke,p as be,H as $e,a as Se}from"../packem_shared/schema-drift-gate-BDCkQ1S6.mjs";import{v as De,i as $,p as Re,l as Ee}from"../packem_shared/output-format-DfogXx0p.mjs";import{containerBuildTag as Ae}from"@lunora/container";import{defaultSpawner as S}from"../packem_shared/createRecordingSpawner-DJlNacZ6.mjs";import{a as O}from"../packem_shared/resolve-target-BRvw8Pf7.mjs";import{c as Ce}from"../packem_shared/tui-prompts-DzYd2fL3.mjs";import{l as Pe}from"../packem_shared/wrangler-secrets-CCdt0mUl.mjs";import{runMigrateDataCommand as _e}from"./runMigrateGenerateCommand.mjs";const xe=/https?:\/\/[^\s"'<>]+\.workers\.dev[^\s"'<>]*/u,je=/https:\/\/[^\s"'<>]+/u,Ne=e=>{if(e===void 0)return;const r=xe.exec(e);return r?r[0]:je.exec(e)?.[0]},x=e=>e===void 0?"":` (--env ${e})`,Te=({cwd:e,env:r,logger:n,now:t,url:a})=>{if(a!==void 0)try{const o=L(e);if(o!==void 0){if(o.workerUrl===a&&o.env===r)return;n.warn(`link: .lunora/project.json records ${o.workerUrl??"(no url)"}${x(o.env)}, but this deploy published ${a}${x(r)}. Keeping the recorded value — run \`lunora link --url ${a}${r===void 0?"":` --env ${r}`}\` to update it.`);return}const i=(t??(()=>new Date().toISOString()))();K(e,{env:r,linkedAt:i,workerName:E(e),workerUrl:a}),n.success(`link: recorded ${a} in .lunora/project.json (run \`lunora link\` to change)`)}catch{}},Le=e=>{const{cwd:r,env:n,logger:t,mintedSecretsFile:a}=e;try{const o=L(r),i=o?.workerName??E(r),s=e.url??o?.workerUrl;t.success("deploy complete"),t.info(` worker: ${i??"(see wrangler output above)"}`),n!==void 0&&t.info(` env: ${n}`),s===void 0?t.info(" url: run `lunora link --url <https://your-worker>` to record it"):t.info(` url: ${s}`),a!==void 0&&t.info(` secrets: generated value(s) recorded in ${a}`),t.info(" logs: lunora logs")}catch{}},Ie=async e=>{if(e.targets.length===0)return{builtTags:[],code:0};if(!(e.railpackAvailable??pe)()){const t="deploy blocked: a container uses `image: { build }` (Railpack), but Railpack isn't ready. Install the `railpack` CLI and start a BuildKit instance, e.g. `docker run --rm --privileged -d --name buildkit moby/buildkit` then `export BUILDKIT_HOST=docker-container://buildkit`. Alternatively switch the container's `image` to a Dockerfile path or a pre-built registry reference.";return e.logger.error(t),{builtTags:[],code:1,error:t}}const r=e.spawner??S,n=[];for(const t of e.targets){const a=Ae(t.exportName),o={args:["build",t.buildDir,"--name",a],command:"railpack",cwd:e.cwd},i=k(b(e.cwd),"wrangler",["containers","push",a]),s={args:i.args,command:i.command,cwd:e.cwd};e.logger.info(`railpack: building "${t.exportName}" → ${a} from ${t.buildDir}`);const c=await r(o);if(c.code!==0){const d=`railpack build failed for container "${t.exportName}" (${t.buildDir})`;return e.logger.error(d),{builtTags:n,code:c.code,error:d}}e.logger.info(`railpack: pushing ${a} to the Cloudflare Registry`);const l=await r(s);if(l.code!==0){const d=`wrangler containers push failed for "${t.exportName}" (${a})`;return e.logger.error(d),{builtTags:n,code:l.code,error:d}}n.push(a)}return{builtTags:n,code:0}},Ue=e=>{const r=W(e),n=r===void 0?void 0:p(r,"utf8");return()=>{r!==void 0&&n!==void 0&&T(r,n,"utf8")}},H="<replace-with-d1-create-id>",j=e=>Array.isArray(e)?e.filter(r=>r!=null):void 0,A=(e,r)=>{const n=W(e);if(!n)return;const{parsed:t}=ae(n);if(t===void 0)return;const{error:a,merged:o}=oe(t,r),i=a===void 0?o:t,{containers:s,d1_databases:c,vars:l}=i;return{containers:j(s),d1_databases:j(c),vars:typeof l=="object"&&l!==null&&!Array.isArray(l)?l:void 0}},Fe=["LUNORA_ORIGIN_URL","AUTH_URL"],We=e=>{try{const{hostname:r}=new URL(e);return r==="localhost"||r==="127.0.0.1"||r==="::1"||r==="[::1]"}catch{return!1}},Oe=e=>e.startsWith("./")||e.startsWith("../")||e.startsWith("/")||e.includes("Dockerfile"),He=(e,r,n,t="deploy",a)=>{const o=(A(e,a)?.containers??[]).filter(s=>typeof s?.image=="string"&&Oe(s.image));if(o.length===0||n())return;const i=`${t} blocked: wrangler.jsonc declares ${String(o.length)} container(s) built from a local Dockerfile, but no Docker-compatible engine is available. Start Docker (or Colima), or point the container's \`image\` at a pre-built registry reference. Note: container images must target linux/amd64.`;return r.error(i),i},Me=e=>v(h(e,_))?_:void 0,Be=(e,r,n="deploy")=>{for(const t of I(e,"lunora").containers){const{image:a}=t;if(a.kind==="dockerfile"&&!v(h(e,a.dockerfilePath))){const o=`${n} blocked: container "${t.exportName}" references a Dockerfile at "${a.dockerfilePath}" that does not exist. Create it or fix the \`image\` path in lunora/containers.ts.`;return r.error(o),o}if(a.kind==="build"&&!v(h(e,a.buildDir))){const o=`${n} blocked: container "${t.exportName}" references a Railpack build directory "${a.buildDir}" that does not exist. Create it or fix the \`image.build\` path in lunora/containers.ts.`;return r.error(o),o}}},M=e=>$(e.format)?!1:e.interactive!==void 0?e.interactive:process.stdout.isTTY&&!process.env.CI,Ve=(e,r)=>(A(e,r)?.d1_databases??[]).find(n=>n.database_id===H)?.binding,Ke=async(e,r)=>{if(r.dryRun===!0)return;const n=I(e,"lunora").containers.filter(a=>a.image.kind==="build").map(a=>({buildDir:a.image.buildDir,exportName:a.exportName}));if(n.length===0)return;const t=await Ie({cwd:e,logger:r.logger,railpackAvailable:r.railpackAvailable,spawner:r.spawner,targets:n});return t.code===0?void 0:t.error??"railpack build failed"},qe=(e,r,n)=>{if(n!==void 0)try{const t=te(e,n);t.changed&&r.success(`synced ${String(n.length)} cron trigger(s) → ${t.wranglerPath??"wrangler.jsonc"}`);for(const o of t.warnings)r.warn(o);const a=ne(t.preserved);a!==void 0&&r.info(a)}catch(t){const a=t instanceof Error?t.message:String(t);r.warn(`cron trigger sync skipped: ${a}`)}},Ye=async(e,r,n,t,a)=>{try{R(t);const o=await F({projectRoot:e}),i=ee(e,o,a);i.changed&&r.success(`provisioned bindings: ${i.added.join(", ")}${i.wranglerPath??"wrangler.jsonc"}`);for(const s of i.warnings)r.warn(s)}catch(o){const i=o instanceof Error?o.message:String(o);r.warn(`binding inference skipped: ${i}`)}try{const o=re(e);o.changed&&r.success(`bumped compatibility_date to ${o.date??"unknown"} (Workers Cache enabled) → ${o.wranglerPath??"wrangler.jsonc"}`)}catch(o){const i=o instanceof Error?o.message:String(o);r.warn(`compatibility date sync skipped: ${i}`)}qe(e,r,n)},Ge=(e,r)=>{const n=h(e,y);if(!v(n))return;let t;try{t=U(p(n,"utf8")).length}catch{return}t!==0&&r.warn(`Note: \`lunora deploy\` does not push secrets. ${y} has ${String(t)} key(s); if you changed them, run \`lunora env push --yes\` to update the deployed secrets.`)},ze=async e=>{let r=[];try{r=Y(await F({projectRoot:e}))}catch{}const n=G(r).map(a=>a.key);let t=[];try{const a=h(e,y);v(a)&&(t=U(p(a,"utf8")).map(o=>o.key).filter(o=>ce(o)))}catch{}return[...new Set([...n,...t])]},Je=async(e,r,n,t)=>{const{logger:a}=r,o=r.spawner??S,i=b(e),s=r.env===void 0?"":` --env ${r.env}`,{toolchain:c}=R(t);if(c===void 0)return a.error("deploy target has no command-line toolchain; cannot push secrets"),{minted:[],ok:!1};const l=[];for(const d of n){const g=z(),u=c.secretPut({environment:r.env,key:d,temporary:r.temporary}),m=k(i,u.tool,u.args),f=await o({args:m.args,command:m.command,cwd:e,input:g});if(f.code!==0)return a.error(`failed to push secret ${d} (exit ${String(f.code)}); set it manually with \`wrangler secret put ${d}${s}\`.`),{minted:l,ok:!1};l.push({key:d,value:g}),a.success(`generated + pushed ${d}`)}return{minted:l,ok:!0}},Qe=/^[\w-]+$/u,D=[".dev.vars",".dev.vars.*","!.dev.vars.example"],Xe=/\r?\n/u,Ze=(e,r)=>{const n=h(e,".gitignore");try{const t=v(n)?p(n,"utf8"):"",a=new Set(t.split(Xe).map(c=>c.trim())),o=D.filter(c=>!a.has(c));if(o.length===0)return;const i=o.some(c=>!c.startsWith("!"))?[...o.filter(c=>!c.startsWith("!")),...D.filter(c=>c.startsWith("!"))]:o,s=t.length>0&&!t.endsWith(` `)?` `:"";T(n,`${t}${s} # Lunora — never commit a minted secret ${i.join(` `)} `,"utf8"),r.info(`.gitignore: added ${o.join(", ")} so the recorded secret cannot be committed`)}catch(t){r.warn(`could not update .gitignore (${t instanceof Error?t.message:String(t)}) — add \`${D.join("` and `")}\` by hand before committing.`)}},er=(e,r,n)=>{if(n.length===0)return;const t=n.map(s=>s.key).join(", ");if(r.env!==void 0&&!Qe.test(r.env)){r.logger.warn(`${t}: minted and pushed for --env ${r.env}, but "${r.env}" isn't a safe filename fragment — the value could not be recorded. Capture it manually if you need it again.`);return}const a=r.env===void 0?y:`${y}.${r.env}`;Ze(e,r.logger);const o=h(e,a);let i=v(o)?p(o,"utf8"):"";for(const s of n)i=J(i,s.key,s.value);return Q(o,i),r.logger.success(r.env===void 0?`${t}: value(s) recorded in ${a}`:`${t}: value(s) recorded in ${a} (kept separate from ${y} so \`lunora dev\` and a plain \`lunora env push\` don't inherit the --env ${r.env} value)`),a},rr=async(e,r,n,t)=>{if(r.dryRun===!0||r.preview===!0)return{};const{logger:a}=r,o=r.env===void 0?"":` --env ${r.env}`;let i;try{i=await(r.secretLister??Pe)({cwd:e,env:r.env,temporary:r.temporary})}catch{return{}}if(!i.ok)return{};const s=new Set(i.names),l=(await ze(e)).filter(u=>!s.has(u));if(l.length===0)return{};if(!n)return{error:`missing required secret(s) on the deploy target: ${l.join(", ")}. Set them with \`wrangler secret put <KEY>${o}\` (or \`lunora env generate --set\` then \`lunora env push --yes${o}\`), then re-deploy.`};for(const u of l.filter(m=>!P(m)))a.warn(`required secret ${u} is not set on the target — set it with: wrangler secret put ${u}${o}`);const d=l.filter(u=>P(u));if(d.length===0)return{};if(await(r.secretConfirm??Ce())(`${String(d.length)} required secret(s) not set on the target (${d.join(", ")}). Generate strong values and push them now?`)){const{minted:u,ok:m}=await Je(e,r,d,t),f=er(e,r,u);return m?{mintedSecretsFile:f}:{error:"failed to push required secret(s) — set them manually and re-deploy",mintedSecretsFile:f}}return a.warn(`${String(d.length)} required secret(s) not set on the target: ${d.join(", ")}. Generate + push with \`lunora env generate --set\` then \`lunora env push --yes${o}\`.`),{}},tr=async(e,r)=>{const n=new se({skipAddingFilesFromTsConfig:!0}),t=h(r,"lunora");let a;try{a=V(n,t)}catch(o){const i=o instanceof Error?o.message:String(o);return e.logger.warn(`--migrate: could not discover migrations (${i}); skipping`),0}if(a.length===0)return e.logger.info("--migrate: no data migrations declared in lunora/"),0;e.logger.info(`--migrate: running ${String(a.length)} migration(s) against deployed worker`);for(const o of a){e.logger.info(`--migrate: up "${o.id}" (table "${o.table}")`);const i={cwd:r,fetchImpl:e.fetchImpl,id:o.id,logger:e.logger,prod:!0,subcommand:"up",token:e.migrateToken,url:e.migrateUrl,yes:e.migrateYes===!0},s=await _e(i);if(s.code!==0)return e.logger.error(`--migrate: migration "${o.id}" failed — see output above`),s.code;e.logger.success(`--migrate: "${o.id}" applied`)}return 0},nr=e=>{if(!e.migrate||e.dryRun||e.preview)return;if(e.migrateUrl===void 0){const n="--migrate requires --migrate-url <https://your-worker> — the deploy target URL is only known after wrangler runs, and this gate runs before it; refusing to default to localhost";return e.logger.error(n),n}if(e.migrateYes!==!0){const n="--migrate runs production data migrations after deploy. Re-run with --migrate-yes to confirm.";return e.logger.error(n),n}const r=e.migrateToken??process.env.LUNORA_ADMIN_TOKEN;if(r===void 0||r===""){const n="admin token required for --migrate — pass --migrate-token or set LUNORA_ADMIN_TOKEN";return e.logger.error(n),n}},ar=async(e,r,n,t,a,o,i,s)=>{let c;r?(c=new ie({name:"dots"}),c.start("running codegen")):n.info("running codegen");try{const l=B({apiSpec:t,projectRoot:e,target:a});c?.succeed("codegen complete"),c||n.success("codegen complete");const d=ge(l.platformDiagnostics,n);if(d.errors.length>0)return{error:d.errors.join("; ")};const{errorAdvisories:g,names:u,shouldBlock:m}=me(l.advisories,s);if(m){const C=`${g.length.toString()} ERROR-level ${g.length===1?"advisory":"advisories"} (${u.join(", ")}). Pass --no-strict-advisories to downgrade this to a warning and continue.`;return n.error(C),{error:C}}const f=await q({cwd:e,logger:n,spawner:o,stdoutToStderr:i});return f.error!==void 0?{error:f.error}:{result:l}}catch(l){c?.failed("codegen failed");const d=l instanceof Error?l.message:String(l);return n.error(`codegen failed: ${d}`),{error:`codegen failed: ${d}`}}},or=(e,r,n="deploy",t)=>{const a=Ve(e,t);if(a===void 0)return;const o=`${n} blocked: the "${a}" D1 binding has a placeholder database_id ("${H}"). Run \`wrangler d1 create <name>\` to create the database, then replace the placeholder in wrangler.jsonc with the real id before deploying.`;return r.error(o),o},ir=(e,r,n="deploy",t)=>{const a=A(e,t)?.vars;if(!a)return;const o=Fe.filter(s=>typeof a[s]=="string"&&We(a[s]));if(o.length===0)return;const i=`${n} blocked: ${o.join(", ")} in wrangler.jsonc point at localhost. A deployed Worker can't reach a loopback address, so this silently breaks scheduled-job dispatch, reverse cross-shard relations, and auth callbacks. Set each to the deployed worker's public URL (or move it to a secret with \`wrangler secret put\`) before deploying.`;return r.error(i),i},sr=async(e,r)=>{const{info:n}=X(r,"lunora"),t=n?.vectorMetadata??[];if(t.length===0)return;const a=[];for(const s of t){const c=le(s.kind);if(c===void 0){e.logger.warn(`vector index "${s.index}" declares metadata "${s.property}", whose column type cannot be filtered on in Vectorize — it is stored with each vector but no filter will match it.`);continue}a.push({index:s.index,property:s.property,type:c})}if(a.length===0)return;const i=(await de({cwd:r,entries:a,exec:k(b(r),"wrangler",[]),logger:e.logger,spawner:e.spawner??S})).filter(s=>s.status!=="failed").length;i>0&&e.logger.success(`vectorize metadata indexes ready: ${String(i)}/${String(a.length)}`)},N=5,cr=2e3,lr=async(e,r,n)=>{if(e.healthCheck!==!0)return;const t=n??O({cwd:r,env:e.env});if(t===void 0){const o="--health-check: the deploy succeeded, but no URL to probe could be resolved — wrangler's output carried none and this checkout has no link for this environment. Run `lunora link --url <https://your-worker>` and re-deploy, or drop --health-check.";return e.logger.error(o),{error:o,ok:!1,url:""}}const a=await be({attempts:N,baseUrl:t,delayMs:cr,fetchImpl:e.healthFetch,paths:[$e,Se],sleep:e.healthSleep});return a.error===void 0?(e.logger.success(`health check ok (${a.url})`),{ok:!0,url:a.url}):(e.logger.error(`--health-check: the deploy succeeded, but the new version did not answer after ${String(N)} attempt(s) — ${a.error}`),{error:a.error,ok:!1,url:a.url})},dr=async(e,r,n,t,a,o)=>{await sr(e,r);const i=e.migrate?await tr(e,r):0;return i===0&&a?.(),{code:i,descriptor:n,mintedSecretsFile:o,validation:t}},ur=(e,r,n)=>{const t=or(e,r.logger,n,r.env);if(t!==void 0)return t;const a=ir(e,r.logger,n,r.env);if(a!==void 0)return a;const o=Be(e,r.logger,n);return o!==void 0?o:He(e,r.logger,r.dockerAvailable??we,n,r.env)},gr=(e,r,n)=>{const t=Me(e);t!==void 0&&r.logger.info(`class-B composition: deploying ${t} (overrides wrangler main)`),r.temporary&&r.logger.info("temporary account: deploying to a short-lived Cloudflare account (~60min); wrangler will print a claim URL"),r.dryRun&&r.logger.info("dry run: validating + bundling without publishing"),r.outDir!==void 0&&r.logger.info(`build artifact: emitting bundle to ${r.outDir}`);const a=R(n),o={dryRun:r.dryRun,entry:t,environment:r.env,outDir:r.outDir,preview:r.preview,temporary:r.temporary};if(a.toolchain===void 0)throw new Error(`deploy target "${a.id}" has no command-line toolchain`);return a.toolchain.deploy(o)},w=(e,r)=>({code:1,descriptor:void 0,error:e,validation:{problems:[],wranglerPath:void 0},...r}),mr=(e,r)=>{for(const n of e.report?.warnings??[])r.warn(`wrangler.jsonc: ${n}`);if(e.problems.length===0)return!1;r.error("wrangler.jsonc validation failed:");for(const n of e.problems)r.error(` - ${n}`);return!0},fr=(e,r,n)=>{const t=$(r.format),a=r.dryRun!==!0,o=gr(e,r,n),i=k(b(e),o.tool,o.args);return{args:i.args,captureStdout:a&&!t,captureStdoutSilently:a&&t,command:i.command,cwd:e,stdoutToStderr:t&&!a}},hr=async({cwd:e,descriptor:r,mintedSecretsFile:n,options:t,reblessSchemaBaseline:a,stdout:o,validation:i})=>{const s={deployedAt:new Date().toISOString(),dryRun:t.dryRun===!0,env:t.env,preview:t.preview===!0,url:t.dryRun===!0?void 0:Ne(o),workerName:E(e)};if(t.dryRun===!0||t.preview===!0)return t.healthCheck===!0&&t.logger.warn(`--health-check skipped: ${t.dryRun===!0?"a dry run publishes nothing":"a preview version serves no live traffic"}`),{code:0,deployment:s,descriptor:r,mintedSecretsFile:n,validation:i};t.temporary!==!0&&Te({cwd:e,env:t.env,logger:t.logger,url:s.url});const c=await lr(t,e,s.url);return c?.error!==void 0?{code:1,deployment:s,descriptor:r,healthCheck:c,mintedSecretsFile:n,validation:i}:{...await dr(t,e,r,i,a,n),deployment:s,healthCheck:c}},vr=async(e,r)=>{const n=e.cwd??process.cwd(),t=M(e),a=ue(e),o={problems:[],wranglerPath:void 0},i=he(n,e.target);if(i.target===void 0){const u=i.error??"unknown deploy target";return e.logger.error(u),{error:u,validation:o}}const{target:s}=i;let c;if(!e.skipCodegen){const u=await ar(n,t,e.logger,e.apiSpec,s,e.spawner,$(e.format),a);if(u.error!==void 0)return{error:u.error,target:s,validation:o};c=u.result}let l;if(c!==void 0){const u=ke({allowDrift:e.allowSchemaDrift===!0,codegen:c,command:r,logger:e.logger,updateBaseline:e.updateSchemaBaseline===!0});if(u.blocked)return{error:`schema drift gate blocked ${r}`,schemaDrift:{blocked:!0,reason:u.reason},target:s,validation:o};l=u.rebless}await Ye(n,e.logger,c?.cronTriggers,s,e.env);const d=ur(n,e,r);if(d!==void 0)return{error:d,target:s,validation:o};const g=Z({environment:e.env,projectRoot:n});return mr(g,e.logger)?{error:"wrangler validation failed",target:s,validation:g}:{codegen:c,reblessSchemaBaseline:l,target:s,validation:g}},yr=async e=>{const r=e.cwd??process.cwd(),n=M(e),t=await vr(e,e.commandName??"deploy");if(t.error!==void 0){if(t.validation.problems.length>0)return{code:1,descriptor:void 0,error:t.error,validation:t.validation};const f=t.schemaDrift===void 0?void 0:{schemaDrift:t.schemaDrift};return w(t.error,f)}const{reblessSchemaBaseline:a,validation:o}=t,i=t.target,s=nr(e);if(s!==void 0)return w(s);const c=await Ke(r,e);if(c!==void 0)return w(c);Ge(r,e.logger);const{error:l,mintedSecretsFile:d}=await rr(r,e,n,i);if(l!==void 0)return e.logger.error(l),{code:1,descriptor:void 0,error:l,mintedSecretsFile:d,validation:o};const g=fr(r,e,i);e.logger.info(`deploying via ${g.command} ${g.args.join(" ")}`);const m=await(e.spawner??S)(g);return g.captureStdoutSilently===!0&&m.stdout!==void 0&&m.stdout!==""&&process.stderr.write(m.stdout),m.code!==0?{code:m.code,descriptor:g,mintedSecretsFile:d,validation:o}:hr({cwd:r,descriptor:g,mintedSecretsFile:d,options:e,reblessSchemaBaseline:a,stdout:m.stdout,validation:o})},pr=async e=>{const r=De("deploy",e.format);if(r!==void 0)return e.logger.error(r),w(r);const n=Ee(e.format,e.logger),t=e.dryRun===!0?Ue(e.cwd??process.cwd()):void 0;let a;try{if(a=await yr({...e,logger:n}),a.code===0&&e.emitBindings!==void 0){const{error:o}=ve({destination:e.emitBindings,logger:n,projectRoot:e.cwd??process.cwd()});o!==void 0&&(n.error(o),a={...a,code:1})}}finally{t?.()}if($(e.format))return Re(a),a;if(a.code===0&&e.dryRun!==!0&&e.preview!==!0)Le({cwd:e.cwd??process.cwd(),env:e.env,logger:e.logger,mintedSecretsFile:a.mintedSecretsFile,url:a.deployment?.url});else if(a.code===0&&e.preview===!0){const o=a.deployment?.url;e.logger.success(o===void 0?"preview version uploaded":`preview version uploaded — ${o}`)}return a},Hr=ye(async({cwd:e,logger:r,options:n})=>({code:(await pr({allowSchemaDrift:n.allowSchemaDrift===!0,apiSpec:fe(n.apiSpec),cwd:e,dryRun:n.dryRun===!0,env:n.env,format:n.format,healthCheck:n.healthCheck===!0,logger:r,migrate:n.migrate===!0,migrateToken:n.migrateToken,migrateUrl:O({cwd:e,env:n.env,url:n.migrateUrl}),migrateYes:n.migrateYes===!0,preview:n.preview===!0,skipCodegen:n.prebuilt===!0,strictAdvisories:n.strictAdvisories,target:n.target,temporary:n.temporary===!0,updateSchemaBaseline:n.updateSchemaBaseline===!0})).code}));export{Hr as execute,Ye as provisionBindings,pr as runDeployCommand,vr as runPreDeployPipeline};