diff --git a/src/commands/provision.ts b/src/commands/provision.ts index 3f29055..6cb6659 100644 --- a/src/commands/provision.ts +++ b/src/commands/provision.ts @@ -28,6 +28,7 @@ type Derived = { templateOwner: string; templateRepo: string; cloneUrl: string; + templateCloneUrl: string; }; type ProvisionReport = { @@ -70,8 +71,8 @@ export async function provision( throw new Error(`Local root path does not exist: ${env.WP_LOCAL_ROOT_PATH}`); } - await ensureGiteaRepo(context, env, slug, derived, report); - await ensureSiteDirectory(context, env, slug, derived, report); + const repoAvailable = await ensureGiteaRepo(context, env, slug, derived, report); + await ensureSiteDirectory(context, env, derived, repoAvailable, report); await checkGitFreshness(context, derived.sitePath); await ensureDatabase(context, derived.dbName, report); await ensureCore(context, env, derived.sitePath, report); @@ -102,6 +103,7 @@ function resolveDerived(env: ProvisionEnv, slug: string): Derived { templateOwner, templateRepo, cloneUrl: `ssh://${env.WPOP_GITEA_SSH_USER}@${host}:${env.WPOP_GITEA_SSH_PORT}/${env.WPOP_GITEA_OWNER}/${slug}.git`, + templateCloneUrl: `ssh://${env.WPOP_GITEA_SSH_USER}@${host}:${env.WPOP_GITEA_SSH_PORT}/${templateOwner}/${templateRepo}.git`, }; } @@ -144,24 +146,30 @@ async function confirm(context: WPopContext, slug: string, derived: Derived): Pr return Boolean(response.confirmed); } +/** + * Ensures the per-site Gitea repo exists. Returns `true` when the repo is + * available to clone from (it already existed or was just created), `false` + * when the user declined creation — in which case the caller falls back to + * cloning the template directly so the local site is still provisioned. + */ async function ensureGiteaRepo( context: WPopContext, env: ProvisionEnv, slug: string, derived: Derived, report: ProvisionReport, -): Promise { +): Promise { if (context.dryRun) { consola.info( `[dry-run] Would ensure Gitea repo ${derived.owner}/${slug} exists (create from ${env.WPOP_GITEA_TEMPLATE} if missing)`, ); - return; + return true; } const exists = await giteaRepoExists(env.giteaBaseUrl, env.giteaToken, derived.owner, slug); if (exists) { consola.info(`Gitea repo ${derived.owner}/${slug} already exists`); - return; + return true; } if (!context.yes) { @@ -172,7 +180,11 @@ async function ensureGiteaRepo( initial: true, }); if (!response.confirmed) { - throw new Error(`Gitea repo ${derived.owner}/${slug} is required but was not created`); + consola.warn( + `Skipping Gitea repo ${derived.owner}/${slug}; provisioning local site from template ${env.WPOP_GITEA_TEMPLATE}`, + ); + report.steps.push("gitea:skip"); + return false; } } @@ -184,13 +196,14 @@ async function ensureGiteaRepo( name: slug, }); report.steps.push("gitea:create"); + return true; } async function ensureSiteDirectory( context: WPopContext, env: ProvisionEnv, - slug: string, derived: Derived, + repoAvailable: boolean, report: ProvisionReport, ): Promise { if (!context.dryRun && existsSync(derived.sitePath)) { @@ -202,11 +215,22 @@ async function ensureSiteDirectory( return; } - consola.info(`Cloning ${derived.cloneUrl} into ${derived.sitePath}`); - await run(context, "git", ["clone", derived.cloneUrl, derived.sitePath], { + const cloneUrl = repoAvailable ? derived.cloneUrl : derived.templateCloneUrl; + consola.info(`Cloning ${cloneUrl} into ${derived.sitePath}`); + await run(context, "git", ["clone", cloneUrl, derived.sitePath], { cwd: env.WP_LOCAL_ROOT_PATH, }); report.steps.push("git:clone"); + + // When provisioning from the template (the per-site repo was not created), + // drop the origin remote so the clone is not tied to the template repo. + if (!repoAvailable) { + await run(context, "git", ["remote", "remove", "origin"], { + cwd: derived.sitePath, + reject: false, + }); + report.steps.push("git:detach-template"); + } } async function checkGitFreshness(context: WPopContext, sitePath: string): Promise {