@ebay-dt/bonfire-node 0.2.0-alpha.8 → 0.2.0-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lifecycle/deployment-worktree.d.ts +6 -1
- package/dist/lifecycle/deployment-worktree.d.ts.map +1 -1
- package/dist/lifecycle/deployment-worktree.js +97 -18
- package/dist/lifecycle/deployment-worktree.js.map +1 -1
- package/dist/lifecycle/local-prototype-lifecycle.d.ts.map +1 -1
- package/dist/lifecycle/local-prototype-lifecycle.js +19 -10
- package/dist/lifecycle/local-prototype-lifecycle.js.map +1 -1
- package/package.json +2 -2
|
@@ -6,7 +6,11 @@ export type DeploymentWorktreeOptions = {
|
|
|
6
6
|
timeoutMs?: number;
|
|
7
7
|
commitIdentity?: GitCommitIdentity;
|
|
8
8
|
};
|
|
9
|
-
export type DeploymentWorktreeErrorKind = "invalid-source" | "preflight-conflict" | "publication-conflict" | "publication-rejected" | "publication-failure" | "cleanup-failure";
|
|
9
|
+
export type DeploymentWorktreeErrorKind = "invalid-source" | "preflight-conflict" | "preflight-failure" | "publication-conflict" | "publication-rejected" | "publication-failure" | "cleanup-failure";
|
|
10
|
+
type DeploymentPhase = "preflight" | "publication";
|
|
11
|
+
type DeploymentStep = "fetch" | "directory" | "marker" | "worktree-add" | "source-verify" | "merge" | "metadata-read" | "metadata-write" | "metadata-commit" | "push" | "cleanup";
|
|
12
|
+
/** Log only finite classifications; Git/OS messages may contain paths or credentials. */
|
|
13
|
+
export declare function reportDeploymentIssue(phase: DeploymentPhase, step: DeploymentStep, error: unknown): void;
|
|
10
14
|
export declare class DeploymentWorktreeError extends Error {
|
|
11
15
|
readonly kind: DeploymentWorktreeErrorKind;
|
|
12
16
|
readonly cleanupFailed: boolean;
|
|
@@ -27,4 +31,5 @@ export declare function publishDeploymentSource(repository: GitRepository, optio
|
|
|
27
31
|
repository: GitRepository;
|
|
28
32
|
}) => Promise<void>): Promise<DeploymentPublicationResult>;
|
|
29
33
|
export declare function deploymentWorktreeRelativePath(repositoryRoot: string, workspacePath: string): string;
|
|
34
|
+
export {};
|
|
30
35
|
//# sourceMappingURL=deployment-worktree.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployment-worktree.d.ts","sourceRoot":"","sources":["../../src/lifecycle/deployment-worktree.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAK9B,MAAM,MAAM,yBAAyB,GAAG;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,iBAAiB,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GACnC,gBAAgB,GAChB,oBAAoB,GACpB,sBAAsB,GACtB,sBAAsB,GACtB,qBAAqB,GACrB,iBAAiB,CAAC;AAEtB,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,QAAQ,CAAC,IAAI,EAAE,2BAA2B,CAAC;IAC3C,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IAExB,YACE,IAAI,EAAE,2BAA2B,EACjC,OAAO,GAAE;QAAE,aAAa,CAAC,EAAE,OAAO,CAAC;QAAC,cAAc,CAAC,EAAE,OAAO,CAAA;KAAO,EAQpE;CACF;AAgLD,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,aAAa,EACzB,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,IAAI,CAAC,CAaf;AAED,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC;IAChB,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,aAAa,EACzB,OAAO,EAAE,yBAAyB,EAClC,OAAO,EAAE,CAAC,SAAS,EAAE;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,aAAa,CAAC;CAC3B,KAAK,OAAO,CAAC,IAAI,CAAC,GAClB,OAAO,CAAC,2BAA2B,CAAC,CAsDtC;AAED,wBAAgB,8BAA8B,CAC5C,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,MAAM,UAGtB","sourcesContent":["import { lstat, mkdtemp, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join, relative, sep } from \"node:path\";\nimport {\n createGitRepository,\n GitRepositoryError,\n type GitCommitIdentity,\n type GitRepository,\n} from \"../git-repository.js\";\n\nconst OWNERSHIP_MARKER = \"bonfire deployment workspace\\n\";\nconst SOURCE_SHA_PATTERN = /^[0-9a-f]{40,64}$/u;\n\nexport type DeploymentWorktreeOptions = {\n remote: string;\n primaryBranch: string;\n sourceSha: string;\n timeoutMs?: number;\n commitIdentity?: GitCommitIdentity;\n};\n\nexport type DeploymentWorktreeErrorKind =\n | \"invalid-source\"\n | \"preflight-conflict\"\n | \"publication-conflict\"\n | \"publication-rejected\"\n | \"publication-failure\"\n | \"cleanup-failure\";\n\nexport class DeploymentWorktreeError extends Error {\n readonly kind: DeploymentWorktreeErrorKind;\n readonly cleanupFailed: boolean;\n readonly operationError: unknown;\n readonly cause: unknown;\n\n constructor(\n kind: DeploymentWorktreeErrorKind,\n options: { cleanupFailed?: boolean; operationError?: unknown } = {},\n ) {\n super(`Deployment workspace operation failed: ${kind}`);\n this.name = \"DeploymentWorktreeError\";\n this.kind = kind;\n this.cleanupFailed = options.cleanupFailed ?? false;\n this.operationError = options.operationError;\n this.cause = options.operationError;\n }\n}\n\nfunction withCleanupFailure(error: unknown): DeploymentWorktreeError {\n if (error instanceof DeploymentWorktreeError) {\n return new DeploymentWorktreeError(error.kind, {\n cleanupFailed: true,\n operationError: error.operationError ?? error,\n });\n }\n return new DeploymentWorktreeError(\"cleanup-failure\", {\n cleanupFailed: true,\n operationError: error,\n });\n}\n\ntype DeploymentWorktree = {\n path: string;\n repository: GitRepository;\n cleanup(): Promise<void>;\n};\n\ntype DeploymentWorkspaceOperation<Value> = (\n workspace: DeploymentWorktree,\n) => Promise<Value>;\n\nfunction assertBranchName(value: string, option: string) {\n if (\n value.length === 0 ||\n value.startsWith(\"-\") ||\n value.includes(\"\\0\") ||\n /\\s/u.test(value) ||\n value.includes(\"@{\") ||\n value.split(/[\\\\/]/u).some((segment) => segment === \"..\")\n ) {\n throw new TypeError(`Invalid ${option}`);\n }\n}\n\nfunction assertSourceSha(sourceSha: string) {\n if (!SOURCE_SHA_PATTERN.test(sourceSha)) {\n throw new DeploymentWorktreeError(\"invalid-source\");\n }\n}\n\nfunction remotePrimaryRef(options: DeploymentWorktreeOptions) {\n return `refs/remotes/${options.remote}/${options.primaryBranch}`;\n}\n\nfunction mapMergeError(\n error: unknown,\n phase: \"preflight\" | \"publication\",\n): unknown {\n if (!(error instanceof GitRepositoryError)) return error;\n if (error.kind === \"conflict\") {\n return new DeploymentWorktreeError(`${phase}-conflict`);\n }\n if (phase === \"publication\") {\n if (error.kind === \"non-fast-forward\") {\n return new DeploymentWorktreeError(\"publication-rejected\");\n }\n return new DeploymentWorktreeError(\"publication-failure\");\n }\n return error;\n}\n\nasync function createDeploymentWorktree(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n): Promise<DeploymentWorktree> {\n assertBranchName(options.remote, \"remote\");\n assertBranchName(options.primaryBranch, \"primary branch\");\n assertSourceSha(options.sourceSha);\n\n await repository.fetch(options.remote, options.primaryBranch);\n const operationDirectory = await mkdtemp(\n join(tmpdir(), \"bonfire-deployment-\"),\n );\n const workspacePath = join(operationDirectory, \"workspace\");\n const markerPath = join(operationDirectory, \"OWNER\");\n\n let worktreeAdded = false;\n try {\n await writeFile(markerPath, OWNERSHIP_MARKER, { flag: \"wx\" });\n await repository.raw([\n \"worktree\",\n \"add\",\n \"--detach\",\n workspacePath,\n remotePrimaryRef(options),\n ]);\n worktreeAdded = true;\n\n const workspaceRepository = createGitRepository({\n repositoryRoot: workspacePath,\n timeoutMs: options.timeoutMs,\n commitIdentity: options.commitIdentity,\n });\n\n await workspaceRepository.raw([\"cat-file\", \"-e\", `${options.sourceSha}^{commit}`]);\n\n return {\n path: workspacePath,\n repository: workspaceRepository,\n async cleanup() {\n try {\n const marker = await readFile(markerPath, \"utf8\");\n const stats = await lstat(workspacePath);\n if (marker !== OWNERSHIP_MARKER || stats.isSymbolicLink()) {\n throw new DeploymentWorktreeError(\"cleanup-failure\");\n }\n await repository.raw([\"worktree\", \"remove\", \"--force\", workspacePath]);\n await rm(operationDirectory, { recursive: true, force: false });\n } catch (error) {\n if (error instanceof DeploymentWorktreeError) throw error;\n throw new DeploymentWorktreeError(\"cleanup-failure\");\n }\n },\n };\n } catch (error) {\n if (worktreeAdded) {\n await repository\n .raw([\"worktree\", \"remove\", \"--force\", workspacePath])\n .catch(() => undefined);\n }\n await rm(operationDirectory, { recursive: true, force: true }).catch(\n () => undefined,\n );\n throw error;\n }\n}\n\nasync function mergeSource(\n workspace: DeploymentWorktree,\n sourceSha: string,\n phase: \"preflight\" | \"publication\",\n) {\n try {\n await workspace.repository.merge([\n sourceSha,\n \"--no-ff\",\n \"-m\",\n `merge: deploy ${sourceSha.slice(0, 12)}`,\n ]);\n } catch (error) {\n throw mapMergeError(error, phase);\n }\n}\n\nasync function withDeploymentWorktree<Value>(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n operation: DeploymentWorkspaceOperation<Value>,\n): Promise<{ value: Value; cleanupFailed: boolean }> {\n const workspace = await createDeploymentWorktree(repository, options);\n let value: Value;\n try {\n value = await operation(workspace);\n } catch (error) {\n // Preserve the operation's failure while making cleanup attention\n // observable to callers.\n try {\n await workspace.cleanup();\n } catch {\n throw withCleanupFailure(error);\n }\n throw error;\n }\n\n try {\n await workspace.cleanup();\n return { value, cleanupFailed: false };\n } catch {\n return { value, cleanupFailed: true };\n }\n}\n\nexport async function preflightDeploymentSource(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n): Promise<void> {\n const result = await withDeploymentWorktree(\n repository,\n options,\n async (workspace) => {\n await mergeSource(workspace, options.sourceSha, \"preflight\");\n },\n );\n if (result.cleanupFailed) {\n throw new DeploymentWorktreeError(\"cleanup-failure\", {\n cleanupFailed: true,\n });\n }\n}\n\nexport type DeploymentPublicationResult = {\n attempts: 1 | 2;\n cleanupFailed: boolean;\n};\n\nexport async function publishDeploymentSource(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n prepare: (workspace: {\n path: string;\n repository: GitRepository;\n }) => Promise<void>,\n): Promise<DeploymentPublicationResult> {\n let cleanupFailed = false;\n for (const attempt of [1, 2] as const) {\n try {\n const result = await withDeploymentWorktree(\n repository,\n options,\n async (workspace) => {\n await mergeSource(workspace, options.sourceSha, \"publication\");\n await prepare({ path: workspace.path, repository: workspace.repository });\n try {\n await workspace.repository.raw([\n \"push\",\n options.remote,\n `HEAD:refs/heads/${options.primaryBranch}`,\n ]);\n } catch (error) {\n throw mapMergeError(error, \"publication\");\n }\n },\n );\n cleanupFailed ||= result.cleanupFailed;\n return { attempts: attempt, cleanupFailed };\n } catch (error) {\n cleanupFailed ||=\n error instanceof DeploymentWorktreeError && error.cleanupFailed;\n if (\n !(error instanceof DeploymentWorktreeError) ||\n error.kind !== \"publication-rejected\" ||\n attempt === 2\n ) {\n if (cleanupFailed) {\n if (error instanceof DeploymentWorktreeError) {\n if (!error.cleanupFailed) {\n throw new DeploymentWorktreeError(error.kind, {\n cleanupFailed: true,\n operationError: error.operationError ?? error,\n });\n }\n } else {\n throw new DeploymentWorktreeError(\"publication-failure\", {\n cleanupFailed: true,\n operationError: error,\n });\n }\n }\n throw error;\n }\n }\n }\n\n throw new DeploymentWorktreeError(\"publication-rejected\", {\n cleanupFailed,\n });\n}\n\nexport function deploymentWorktreeRelativePath(\n repositoryRoot: string,\n workspacePath: string,\n) {\n return relative(repositoryRoot, workspacePath).split(sep).join(\"/\");\n}\n"]}
|
|
1
|
+
{"version":3,"file":"deployment-worktree.d.ts","sourceRoot":"","sources":["../../src/lifecycle/deployment-worktree.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAK9B,MAAM,MAAM,yBAAyB,GAAG;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,iBAAiB,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GACnC,gBAAgB,GAChB,oBAAoB,GACpB,mBAAmB,GACnB,sBAAsB,GACtB,sBAAsB,GACtB,qBAAqB,GACrB,iBAAiB,CAAC;AAEtB,KAAK,eAAe,GAAG,WAAW,GAAG,aAAa,CAAC;AACnD,KAAK,cAAc,GACf,OAAO,GACP,WAAW,GACX,QAAQ,GACR,cAAc,GACd,eAAe,GACf,OAAO,GACP,eAAe,GACf,gBAAgB,GAChB,iBAAiB,GACjB,MAAM,GACN,SAAS,CAAC;AAEd,yFAAyF;AACzF,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,OAAO,QAiBf;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,QAAQ,CAAC,IAAI,EAAE,2BAA2B,CAAC;IAC3C,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IAExB,YACE,IAAI,EAAE,2BAA2B,EACjC,OAAO,GAAE;QAAE,aAAa,CAAC,EAAE,OAAO,CAAC;QAAC,cAAc,CAAC,EAAE,OAAO,CAAA;KAAO,EAQpE;CACF;AA+ND,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,aAAa,EACzB,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC;IAChB,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,aAAa,EACzB,OAAO,EAAE,yBAAyB,EAClC,OAAO,EAAE,CAAC,SAAS,EAAE;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,aAAa,CAAC;CAC3B,KAAK,OAAO,CAAC,IAAI,CAAC,GAClB,OAAO,CAAC,2BAA2B,CAAC,CAgEtC;AAED,wBAAgB,8BAA8B,CAC5C,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,MAAM,UAGtB","sourcesContent":["import { lstat, mkdtemp, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join, relative, sep } from \"node:path\";\nimport {\n createGitRepository,\n GitRepositoryError,\n type GitCommitIdentity,\n type GitRepository,\n} from \"../git-repository.js\";\n\nconst OWNERSHIP_MARKER = \"bonfire deployment workspace\\n\";\nconst SOURCE_SHA_PATTERN = /^[0-9a-f]{40,64}$/u;\n\nexport type DeploymentWorktreeOptions = {\n remote: string;\n primaryBranch: string;\n sourceSha: string;\n timeoutMs?: number;\n commitIdentity?: GitCommitIdentity;\n};\n\nexport type DeploymentWorktreeErrorKind =\n | \"invalid-source\"\n | \"preflight-conflict\"\n | \"preflight-failure\"\n | \"publication-conflict\"\n | \"publication-rejected\"\n | \"publication-failure\"\n | \"cleanup-failure\";\n\ntype DeploymentPhase = \"preflight\" | \"publication\";\ntype DeploymentStep =\n | \"fetch\"\n | \"directory\"\n | \"marker\"\n | \"worktree-add\"\n | \"source-verify\"\n | \"merge\"\n | \"metadata-read\"\n | \"metadata-write\"\n | \"metadata-commit\"\n | \"push\"\n | \"cleanup\";\n\n/** Log only finite classifications; Git/OS messages may contain paths or credentials. */\nexport function reportDeploymentIssue(\n phase: DeploymentPhase,\n step: DeploymentStep,\n error: unknown,\n) {\n const kind =\n error instanceof GitRepositoryError\n ? error.kind\n : error instanceof DeploymentWorktreeError\n ? error.kind\n : step.startsWith(\"metadata-\") ||\n step === \"directory\" ||\n step === \"marker\"\n ? \"filesystem-failure\"\n : \"unexpected\";\n console.warn(\"[bonfire] Deployment workspace attention\", {\n phase,\n step,\n kind,\n });\n}\n\nexport class DeploymentWorktreeError extends Error {\n readonly kind: DeploymentWorktreeErrorKind;\n readonly cleanupFailed: boolean;\n readonly operationError: unknown;\n readonly cause: unknown;\n\n constructor(\n kind: DeploymentWorktreeErrorKind,\n options: { cleanupFailed?: boolean; operationError?: unknown } = {},\n ) {\n super(`Deployment workspace operation failed: ${kind}`);\n this.name = \"DeploymentWorktreeError\";\n this.kind = kind;\n this.cleanupFailed = options.cleanupFailed ?? false;\n this.operationError = options.operationError;\n this.cause = options.operationError;\n }\n}\n\nfunction withCleanupFailure(error: unknown): DeploymentWorktreeError {\n if (error instanceof DeploymentWorktreeError) {\n return new DeploymentWorktreeError(error.kind, {\n cleanupFailed: true,\n operationError: error.operationError ?? error,\n });\n }\n return new DeploymentWorktreeError(\"cleanup-failure\", {\n cleanupFailed: true,\n operationError: error,\n });\n}\n\ntype DeploymentWorktree = {\n path: string;\n repository: GitRepository;\n cleanup(): Promise<void>;\n};\n\ntype DeploymentWorkspaceOperation<Value> = (\n workspace: DeploymentWorktree,\n) => Promise<Value>;\n\nfunction assertBranchName(value: string, option: string) {\n if (\n value.length === 0 ||\n value.startsWith(\"-\") ||\n value.includes(\"\\0\") ||\n /\\s/u.test(value) ||\n value.includes(\"@{\") ||\n value.split(/[\\\\/]/u).some((segment) => segment === \"..\")\n ) {\n throw new TypeError(`Invalid ${option}`);\n }\n}\n\nfunction assertSourceSha(sourceSha: string) {\n if (!SOURCE_SHA_PATTERN.test(sourceSha)) {\n throw new DeploymentWorktreeError(\"invalid-source\");\n }\n}\n\nfunction remotePrimaryRef(options: DeploymentWorktreeOptions) {\n return `refs/remotes/${options.remote}/${options.primaryBranch}`;\n}\n\nfunction mapMergeError(\n error: unknown,\n phase: \"preflight\" | \"publication\",\n): unknown {\n if (!(error instanceof GitRepositoryError)) return error;\n if (error.kind === \"conflict\") {\n return new DeploymentWorktreeError(`${phase}-conflict`);\n }\n if (phase === \"publication\") {\n if (error.kind === \"non-fast-forward\") {\n return new DeploymentWorktreeError(\"publication-rejected\");\n }\n return new DeploymentWorktreeError(\"publication-failure\");\n }\n return error;\n}\n\nasync function createDeploymentWorktree(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n phase: DeploymentPhase,\n): Promise<DeploymentWorktree> {\n assertBranchName(options.remote, \"remote\");\n assertBranchName(options.primaryBranch, \"primary branch\");\n assertSourceSha(options.sourceSha);\n\n try {\n await repository.fetch(options.remote, options.primaryBranch);\n } catch (error) {\n reportDeploymentIssue(phase, \"fetch\", error);\n throw new DeploymentWorktreeError(`${phase}-failure`, {\n operationError: error,\n });\n }\n let operationDirectory: string;\n try {\n operationDirectory = await mkdtemp(join(tmpdir(), \"bonfire-deployment-\"));\n } catch (error) {\n reportDeploymentIssue(phase, \"directory\", error);\n throw new DeploymentWorktreeError(`${phase}-failure`, {\n operationError: error,\n });\n }\n const workspacePath = join(operationDirectory, \"workspace\");\n const markerPath = join(operationDirectory, \"OWNER\");\n\n let worktreeAdded = false;\n let step: DeploymentStep = \"marker\";\n try {\n await writeFile(markerPath, OWNERSHIP_MARKER, { flag: \"wx\" });\n step = \"worktree-add\";\n await repository.raw([\n \"worktree\",\n \"add\",\n \"--detach\",\n workspacePath,\n remotePrimaryRef(options),\n ]);\n worktreeAdded = true;\n\n const workspaceRepository = createGitRepository({\n repositoryRoot: workspacePath,\n timeoutMs: options.timeoutMs,\n commitIdentity: options.commitIdentity,\n });\n\n step = \"source-verify\";\n await workspaceRepository.raw([\n \"cat-file\",\n \"-e\",\n `${options.sourceSha}^{commit}`,\n ]);\n\n return {\n path: workspacePath,\n repository: workspaceRepository,\n async cleanup() {\n try {\n const marker = await readFile(markerPath, \"utf8\");\n const stats = await lstat(workspacePath);\n if (marker !== OWNERSHIP_MARKER || stats.isSymbolicLink()) {\n throw new DeploymentWorktreeError(\"cleanup-failure\");\n }\n await repository.raw([\n \"worktree\",\n \"remove\",\n \"--force\",\n workspacePath,\n ]);\n await rm(operationDirectory, { recursive: true, force: false });\n } catch (error) {\n reportDeploymentIssue(phase, \"cleanup\", error);\n if (error instanceof DeploymentWorktreeError) throw error;\n throw new DeploymentWorktreeError(\"cleanup-failure\", {\n operationError: error,\n });\n }\n },\n };\n } catch (error) {\n reportDeploymentIssue(phase, step, error);\n let cleanupFailed = false;\n if (worktreeAdded) {\n try {\n await repository.raw([\"worktree\", \"remove\", \"--force\", workspacePath]);\n } catch (cleanupError) {\n cleanupFailed = true;\n reportDeploymentIssue(phase, \"cleanup\", cleanupError);\n }\n }\n try {\n await rm(operationDirectory, { recursive: true, force: true });\n } catch (cleanupError) {\n cleanupFailed = true;\n reportDeploymentIssue(phase, \"cleanup\", cleanupError);\n }\n throw new DeploymentWorktreeError(`${phase}-failure`, {\n cleanupFailed,\n operationError: error,\n });\n }\n}\n\nasync function mergeSource(\n workspace: DeploymentWorktree,\n sourceSha: string,\n phase: \"preflight\" | \"publication\",\n) {\n try {\n await workspace.repository.merge([\n sourceSha,\n \"--no-ff\",\n \"-m\",\n `merge: deploy ${sourceSha.slice(0, 12)}`,\n ]);\n } catch (error) {\n reportDeploymentIssue(phase, \"merge\", error);\n const mapped = mapMergeError(error, phase);\n throw mapped instanceof DeploymentWorktreeError\n ? mapped\n : new DeploymentWorktreeError(`${phase}-failure`, {\n operationError: error,\n });\n }\n}\n\nasync function withDeploymentWorktree<Value>(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n operation: DeploymentWorkspaceOperation<Value>,\n phase: DeploymentPhase,\n): Promise<{ value: Value; cleanupFailed: boolean }> {\n const workspace = await createDeploymentWorktree(repository, options, phase);\n let value: Value;\n try {\n value = await operation(workspace);\n } catch (error) {\n // Preserve the operation's failure while making cleanup attention\n // observable to callers.\n try {\n await workspace.cleanup();\n } catch {\n throw withCleanupFailure(error);\n }\n throw error;\n }\n\n try {\n await workspace.cleanup();\n return { value, cleanupFailed: false };\n } catch {\n return { value, cleanupFailed: true };\n }\n}\n\nexport async function preflightDeploymentSource(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n): Promise<void> {\n const result = await withDeploymentWorktree(\n repository,\n options,\n async (workspace) => {\n await mergeSource(workspace, options.sourceSha, \"preflight\");\n },\n \"preflight\",\n );\n if (result.cleanupFailed) {\n throw new DeploymentWorktreeError(\"cleanup-failure\", {\n cleanupFailed: true,\n });\n }\n}\n\nexport type DeploymentPublicationResult = {\n attempts: 1 | 2;\n cleanupFailed: boolean;\n};\n\nexport async function publishDeploymentSource(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n prepare: (workspace: {\n path: string;\n repository: GitRepository;\n }) => Promise<void>,\n): Promise<DeploymentPublicationResult> {\n let cleanupFailed = false;\n for (const attempt of [1, 2] as const) {\n try {\n const result = await withDeploymentWorktree(\n repository,\n options,\n async (workspace) => {\n await mergeSource(workspace, options.sourceSha, \"publication\");\n await prepare({\n path: workspace.path,\n repository: workspace.repository,\n });\n try {\n await workspace.repository.raw([\n \"push\",\n options.remote,\n `HEAD:refs/heads/${options.primaryBranch}`,\n ]);\n } catch (error) {\n reportDeploymentIssue(\"publication\", \"push\", error);\n const mapped = mapMergeError(error, \"publication\");\n throw mapped instanceof DeploymentWorktreeError\n ? mapped\n : new DeploymentWorktreeError(\"publication-failure\", {\n operationError: error,\n });\n }\n },\n \"publication\",\n );\n cleanupFailed ||= result.cleanupFailed;\n return { attempts: attempt, cleanupFailed };\n } catch (error) {\n cleanupFailed ||=\n error instanceof DeploymentWorktreeError && error.cleanupFailed;\n if (\n !(error instanceof DeploymentWorktreeError) ||\n error.kind !== \"publication-rejected\" ||\n attempt === 2\n ) {\n if (cleanupFailed) {\n if (error instanceof DeploymentWorktreeError) {\n if (!error.cleanupFailed) {\n throw new DeploymentWorktreeError(error.kind, {\n cleanupFailed: true,\n operationError: error.operationError ?? error,\n });\n }\n } else {\n throw new DeploymentWorktreeError(\"publication-failure\", {\n cleanupFailed: true,\n operationError: error,\n });\n }\n }\n throw error;\n }\n }\n }\n\n throw new DeploymentWorktreeError(\"publication-rejected\", {\n cleanupFailed,\n });\n}\n\nexport function deploymentWorktreeRelativePath(\n repositoryRoot: string,\n workspacePath: string,\n) {\n return relative(repositoryRoot, workspacePath).split(sep).join(\"/\");\n}\n"]}
|
|
@@ -4,6 +4,23 @@ import { join, relative, sep } from "node:path";
|
|
|
4
4
|
import { createGitRepository, GitRepositoryError, } from "../git-repository.js";
|
|
5
5
|
const OWNERSHIP_MARKER = "bonfire deployment workspace\n";
|
|
6
6
|
const SOURCE_SHA_PATTERN = /^[0-9a-f]{40,64}$/u;
|
|
7
|
+
/** Log only finite classifications; Git/OS messages may contain paths or credentials. */
|
|
8
|
+
export function reportDeploymentIssue(phase, step, error) {
|
|
9
|
+
const kind = error instanceof GitRepositoryError
|
|
10
|
+
? error.kind
|
|
11
|
+
: error instanceof DeploymentWorktreeError
|
|
12
|
+
? error.kind
|
|
13
|
+
: step.startsWith("metadata-") ||
|
|
14
|
+
step === "directory" ||
|
|
15
|
+
step === "marker"
|
|
16
|
+
? "filesystem-failure"
|
|
17
|
+
: "unexpected";
|
|
18
|
+
console.warn("[bonfire] Deployment workspace attention", {
|
|
19
|
+
phase,
|
|
20
|
+
step,
|
|
21
|
+
kind,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
7
24
|
export class DeploymentWorktreeError extends Error {
|
|
8
25
|
kind;
|
|
9
26
|
cleanupFailed;
|
|
@@ -62,17 +79,36 @@ function mapMergeError(error, phase) {
|
|
|
62
79
|
}
|
|
63
80
|
return error;
|
|
64
81
|
}
|
|
65
|
-
async function createDeploymentWorktree(repository, options) {
|
|
82
|
+
async function createDeploymentWorktree(repository, options, phase) {
|
|
66
83
|
assertBranchName(options.remote, "remote");
|
|
67
84
|
assertBranchName(options.primaryBranch, "primary branch");
|
|
68
85
|
assertSourceSha(options.sourceSha);
|
|
69
|
-
|
|
70
|
-
|
|
86
|
+
try {
|
|
87
|
+
await repository.fetch(options.remote, options.primaryBranch);
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
reportDeploymentIssue(phase, "fetch", error);
|
|
91
|
+
throw new DeploymentWorktreeError(`${phase}-failure`, {
|
|
92
|
+
operationError: error,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
let operationDirectory;
|
|
96
|
+
try {
|
|
97
|
+
operationDirectory = await mkdtemp(join(tmpdir(), "bonfire-deployment-"));
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
reportDeploymentIssue(phase, "directory", error);
|
|
101
|
+
throw new DeploymentWorktreeError(`${phase}-failure`, {
|
|
102
|
+
operationError: error,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
71
105
|
const workspacePath = join(operationDirectory, "workspace");
|
|
72
106
|
const markerPath = join(operationDirectory, "OWNER");
|
|
73
107
|
let worktreeAdded = false;
|
|
108
|
+
let step = "marker";
|
|
74
109
|
try {
|
|
75
110
|
await writeFile(markerPath, OWNERSHIP_MARKER, { flag: "wx" });
|
|
111
|
+
step = "worktree-add";
|
|
76
112
|
await repository.raw([
|
|
77
113
|
"worktree",
|
|
78
114
|
"add",
|
|
@@ -86,7 +122,12 @@ async function createDeploymentWorktree(repository, options) {
|
|
|
86
122
|
timeoutMs: options.timeoutMs,
|
|
87
123
|
commitIdentity: options.commitIdentity,
|
|
88
124
|
});
|
|
89
|
-
|
|
125
|
+
step = "source-verify";
|
|
126
|
+
await workspaceRepository.raw([
|
|
127
|
+
"cat-file",
|
|
128
|
+
"-e",
|
|
129
|
+
`${options.sourceSha}^{commit}`,
|
|
130
|
+
]);
|
|
90
131
|
return {
|
|
91
132
|
path: workspacePath,
|
|
92
133
|
repository: workspaceRepository,
|
|
@@ -97,25 +138,48 @@ async function createDeploymentWorktree(repository, options) {
|
|
|
97
138
|
if (marker !== OWNERSHIP_MARKER || stats.isSymbolicLink()) {
|
|
98
139
|
throw new DeploymentWorktreeError("cleanup-failure");
|
|
99
140
|
}
|
|
100
|
-
await repository.raw([
|
|
141
|
+
await repository.raw([
|
|
142
|
+
"worktree",
|
|
143
|
+
"remove",
|
|
144
|
+
"--force",
|
|
145
|
+
workspacePath,
|
|
146
|
+
]);
|
|
101
147
|
await rm(operationDirectory, { recursive: true, force: false });
|
|
102
148
|
}
|
|
103
149
|
catch (error) {
|
|
150
|
+
reportDeploymentIssue(phase, "cleanup", error);
|
|
104
151
|
if (error instanceof DeploymentWorktreeError)
|
|
105
152
|
throw error;
|
|
106
|
-
throw new DeploymentWorktreeError("cleanup-failure"
|
|
153
|
+
throw new DeploymentWorktreeError("cleanup-failure", {
|
|
154
|
+
operationError: error,
|
|
155
|
+
});
|
|
107
156
|
}
|
|
108
157
|
},
|
|
109
158
|
};
|
|
110
159
|
}
|
|
111
160
|
catch (error) {
|
|
161
|
+
reportDeploymentIssue(phase, step, error);
|
|
162
|
+
let cleanupFailed = false;
|
|
112
163
|
if (worktreeAdded) {
|
|
113
|
-
|
|
114
|
-
.raw(["worktree", "remove", "--force", workspacePath])
|
|
115
|
-
|
|
164
|
+
try {
|
|
165
|
+
await repository.raw(["worktree", "remove", "--force", workspacePath]);
|
|
166
|
+
}
|
|
167
|
+
catch (cleanupError) {
|
|
168
|
+
cleanupFailed = true;
|
|
169
|
+
reportDeploymentIssue(phase, "cleanup", cleanupError);
|
|
170
|
+
}
|
|
116
171
|
}
|
|
117
|
-
|
|
118
|
-
|
|
172
|
+
try {
|
|
173
|
+
await rm(operationDirectory, { recursive: true, force: true });
|
|
174
|
+
}
|
|
175
|
+
catch (cleanupError) {
|
|
176
|
+
cleanupFailed = true;
|
|
177
|
+
reportDeploymentIssue(phase, "cleanup", cleanupError);
|
|
178
|
+
}
|
|
179
|
+
throw new DeploymentWorktreeError(`${phase}-failure`, {
|
|
180
|
+
cleanupFailed,
|
|
181
|
+
operationError: error,
|
|
182
|
+
});
|
|
119
183
|
}
|
|
120
184
|
}
|
|
121
185
|
async function mergeSource(workspace, sourceSha, phase) {
|
|
@@ -128,11 +192,17 @@ async function mergeSource(workspace, sourceSha, phase) {
|
|
|
128
192
|
]);
|
|
129
193
|
}
|
|
130
194
|
catch (error) {
|
|
131
|
-
|
|
195
|
+
reportDeploymentIssue(phase, "merge", error);
|
|
196
|
+
const mapped = mapMergeError(error, phase);
|
|
197
|
+
throw mapped instanceof DeploymentWorktreeError
|
|
198
|
+
? mapped
|
|
199
|
+
: new DeploymentWorktreeError(`${phase}-failure`, {
|
|
200
|
+
operationError: error,
|
|
201
|
+
});
|
|
132
202
|
}
|
|
133
203
|
}
|
|
134
|
-
async function withDeploymentWorktree(repository, options, operation) {
|
|
135
|
-
const workspace = await createDeploymentWorktree(repository, options);
|
|
204
|
+
async function withDeploymentWorktree(repository, options, operation, phase) {
|
|
205
|
+
const workspace = await createDeploymentWorktree(repository, options, phase);
|
|
136
206
|
let value;
|
|
137
207
|
try {
|
|
138
208
|
value = await operation(workspace);
|
|
@@ -159,7 +229,7 @@ async function withDeploymentWorktree(repository, options, operation) {
|
|
|
159
229
|
export async function preflightDeploymentSource(repository, options) {
|
|
160
230
|
const result = await withDeploymentWorktree(repository, options, async (workspace) => {
|
|
161
231
|
await mergeSource(workspace, options.sourceSha, "preflight");
|
|
162
|
-
});
|
|
232
|
+
}, "preflight");
|
|
163
233
|
if (result.cleanupFailed) {
|
|
164
234
|
throw new DeploymentWorktreeError("cleanup-failure", {
|
|
165
235
|
cleanupFailed: true,
|
|
@@ -172,7 +242,10 @@ export async function publishDeploymentSource(repository, options, prepare) {
|
|
|
172
242
|
try {
|
|
173
243
|
const result = await withDeploymentWorktree(repository, options, async (workspace) => {
|
|
174
244
|
await mergeSource(workspace, options.sourceSha, "publication");
|
|
175
|
-
await prepare({
|
|
245
|
+
await prepare({
|
|
246
|
+
path: workspace.path,
|
|
247
|
+
repository: workspace.repository,
|
|
248
|
+
});
|
|
176
249
|
try {
|
|
177
250
|
await workspace.repository.raw([
|
|
178
251
|
"push",
|
|
@@ -181,9 +254,15 @@ export async function publishDeploymentSource(repository, options, prepare) {
|
|
|
181
254
|
]);
|
|
182
255
|
}
|
|
183
256
|
catch (error) {
|
|
184
|
-
|
|
257
|
+
reportDeploymentIssue("publication", "push", error);
|
|
258
|
+
const mapped = mapMergeError(error, "publication");
|
|
259
|
+
throw mapped instanceof DeploymentWorktreeError
|
|
260
|
+
? mapped
|
|
261
|
+
: new DeploymentWorktreeError("publication-failure", {
|
|
262
|
+
operationError: error,
|
|
263
|
+
});
|
|
185
264
|
}
|
|
186
|
-
});
|
|
265
|
+
}, "publication");
|
|
187
266
|
cleanupFailed ||= result.cleanupFailed;
|
|
188
267
|
return { attempts: attempt, cleanupFailed };
|
|
189
268
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployment-worktree.js","sourceRoot":"","sources":["../../src/lifecycle/deployment-worktree.ts"],"names":[],"mappings":"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","sourcesContent":["import { lstat, mkdtemp, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join, relative, sep } from \"node:path\";\nimport {\n createGitRepository,\n GitRepositoryError,\n type GitCommitIdentity,\n type GitRepository,\n} from \"../git-repository.js\";\n\nconst OWNERSHIP_MARKER = \"bonfire deployment workspace\\n\";\nconst SOURCE_SHA_PATTERN = /^[0-9a-f]{40,64}$/u;\n\nexport type DeploymentWorktreeOptions = {\n remote: string;\n primaryBranch: string;\n sourceSha: string;\n timeoutMs?: number;\n commitIdentity?: GitCommitIdentity;\n};\n\nexport type DeploymentWorktreeErrorKind =\n | \"invalid-source\"\n | \"preflight-conflict\"\n | \"publication-conflict\"\n | \"publication-rejected\"\n | \"publication-failure\"\n | \"cleanup-failure\";\n\nexport class DeploymentWorktreeError extends Error {\n readonly kind: DeploymentWorktreeErrorKind;\n readonly cleanupFailed: boolean;\n readonly operationError: unknown;\n readonly cause: unknown;\n\n constructor(\n kind: DeploymentWorktreeErrorKind,\n options: { cleanupFailed?: boolean; operationError?: unknown } = {},\n ) {\n super(`Deployment workspace operation failed: ${kind}`);\n this.name = \"DeploymentWorktreeError\";\n this.kind = kind;\n this.cleanupFailed = options.cleanupFailed ?? false;\n this.operationError = options.operationError;\n this.cause = options.operationError;\n }\n}\n\nfunction withCleanupFailure(error: unknown): DeploymentWorktreeError {\n if (error instanceof DeploymentWorktreeError) {\n return new DeploymentWorktreeError(error.kind, {\n cleanupFailed: true,\n operationError: error.operationError ?? error,\n });\n }\n return new DeploymentWorktreeError(\"cleanup-failure\", {\n cleanupFailed: true,\n operationError: error,\n });\n}\n\ntype DeploymentWorktree = {\n path: string;\n repository: GitRepository;\n cleanup(): Promise<void>;\n};\n\ntype DeploymentWorkspaceOperation<Value> = (\n workspace: DeploymentWorktree,\n) => Promise<Value>;\n\nfunction assertBranchName(value: string, option: string) {\n if (\n value.length === 0 ||\n value.startsWith(\"-\") ||\n value.includes(\"\\0\") ||\n /\\s/u.test(value) ||\n value.includes(\"@{\") ||\n value.split(/[\\\\/]/u).some((segment) => segment === \"..\")\n ) {\n throw new TypeError(`Invalid ${option}`);\n }\n}\n\nfunction assertSourceSha(sourceSha: string) {\n if (!SOURCE_SHA_PATTERN.test(sourceSha)) {\n throw new DeploymentWorktreeError(\"invalid-source\");\n }\n}\n\nfunction remotePrimaryRef(options: DeploymentWorktreeOptions) {\n return `refs/remotes/${options.remote}/${options.primaryBranch}`;\n}\n\nfunction mapMergeError(\n error: unknown,\n phase: \"preflight\" | \"publication\",\n): unknown {\n if (!(error instanceof GitRepositoryError)) return error;\n if (error.kind === \"conflict\") {\n return new DeploymentWorktreeError(`${phase}-conflict`);\n }\n if (phase === \"publication\") {\n if (error.kind === \"non-fast-forward\") {\n return new DeploymentWorktreeError(\"publication-rejected\");\n }\n return new DeploymentWorktreeError(\"publication-failure\");\n }\n return error;\n}\n\nasync function createDeploymentWorktree(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n): Promise<DeploymentWorktree> {\n assertBranchName(options.remote, \"remote\");\n assertBranchName(options.primaryBranch, \"primary branch\");\n assertSourceSha(options.sourceSha);\n\n await repository.fetch(options.remote, options.primaryBranch);\n const operationDirectory = await mkdtemp(\n join(tmpdir(), \"bonfire-deployment-\"),\n );\n const workspacePath = join(operationDirectory, \"workspace\");\n const markerPath = join(operationDirectory, \"OWNER\");\n\n let worktreeAdded = false;\n try {\n await writeFile(markerPath, OWNERSHIP_MARKER, { flag: \"wx\" });\n await repository.raw([\n \"worktree\",\n \"add\",\n \"--detach\",\n workspacePath,\n remotePrimaryRef(options),\n ]);\n worktreeAdded = true;\n\n const workspaceRepository = createGitRepository({\n repositoryRoot: workspacePath,\n timeoutMs: options.timeoutMs,\n commitIdentity: options.commitIdentity,\n });\n\n await workspaceRepository.raw([\"cat-file\", \"-e\", `${options.sourceSha}^{commit}`]);\n\n return {\n path: workspacePath,\n repository: workspaceRepository,\n async cleanup() {\n try {\n const marker = await readFile(markerPath, \"utf8\");\n const stats = await lstat(workspacePath);\n if (marker !== OWNERSHIP_MARKER || stats.isSymbolicLink()) {\n throw new DeploymentWorktreeError(\"cleanup-failure\");\n }\n await repository.raw([\"worktree\", \"remove\", \"--force\", workspacePath]);\n await rm(operationDirectory, { recursive: true, force: false });\n } catch (error) {\n if (error instanceof DeploymentWorktreeError) throw error;\n throw new DeploymentWorktreeError(\"cleanup-failure\");\n }\n },\n };\n } catch (error) {\n if (worktreeAdded) {\n await repository\n .raw([\"worktree\", \"remove\", \"--force\", workspacePath])\n .catch(() => undefined);\n }\n await rm(operationDirectory, { recursive: true, force: true }).catch(\n () => undefined,\n );\n throw error;\n }\n}\n\nasync function mergeSource(\n workspace: DeploymentWorktree,\n sourceSha: string,\n phase: \"preflight\" | \"publication\",\n) {\n try {\n await workspace.repository.merge([\n sourceSha,\n \"--no-ff\",\n \"-m\",\n `merge: deploy ${sourceSha.slice(0, 12)}`,\n ]);\n } catch (error) {\n throw mapMergeError(error, phase);\n }\n}\n\nasync function withDeploymentWorktree<Value>(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n operation: DeploymentWorkspaceOperation<Value>,\n): Promise<{ value: Value; cleanupFailed: boolean }> {\n const workspace = await createDeploymentWorktree(repository, options);\n let value: Value;\n try {\n value = await operation(workspace);\n } catch (error) {\n // Preserve the operation's failure while making cleanup attention\n // observable to callers.\n try {\n await workspace.cleanup();\n } catch {\n throw withCleanupFailure(error);\n }\n throw error;\n }\n\n try {\n await workspace.cleanup();\n return { value, cleanupFailed: false };\n } catch {\n return { value, cleanupFailed: true };\n }\n}\n\nexport async function preflightDeploymentSource(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n): Promise<void> {\n const result = await withDeploymentWorktree(\n repository,\n options,\n async (workspace) => {\n await mergeSource(workspace, options.sourceSha, \"preflight\");\n },\n );\n if (result.cleanupFailed) {\n throw new DeploymentWorktreeError(\"cleanup-failure\", {\n cleanupFailed: true,\n });\n }\n}\n\nexport type DeploymentPublicationResult = {\n attempts: 1 | 2;\n cleanupFailed: boolean;\n};\n\nexport async function publishDeploymentSource(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n prepare: (workspace: {\n path: string;\n repository: GitRepository;\n }) => Promise<void>,\n): Promise<DeploymentPublicationResult> {\n let cleanupFailed = false;\n for (const attempt of [1, 2] as const) {\n try {\n const result = await withDeploymentWorktree(\n repository,\n options,\n async (workspace) => {\n await mergeSource(workspace, options.sourceSha, \"publication\");\n await prepare({ path: workspace.path, repository: workspace.repository });\n try {\n await workspace.repository.raw([\n \"push\",\n options.remote,\n `HEAD:refs/heads/${options.primaryBranch}`,\n ]);\n } catch (error) {\n throw mapMergeError(error, \"publication\");\n }\n },\n );\n cleanupFailed ||= result.cleanupFailed;\n return { attempts: attempt, cleanupFailed };\n } catch (error) {\n cleanupFailed ||=\n error instanceof DeploymentWorktreeError && error.cleanupFailed;\n if (\n !(error instanceof DeploymentWorktreeError) ||\n error.kind !== \"publication-rejected\" ||\n attempt === 2\n ) {\n if (cleanupFailed) {\n if (error instanceof DeploymentWorktreeError) {\n if (!error.cleanupFailed) {\n throw new DeploymentWorktreeError(error.kind, {\n cleanupFailed: true,\n operationError: error.operationError ?? error,\n });\n }\n } else {\n throw new DeploymentWorktreeError(\"publication-failure\", {\n cleanupFailed: true,\n operationError: error,\n });\n }\n }\n throw error;\n }\n }\n }\n\n throw new DeploymentWorktreeError(\"publication-rejected\", {\n cleanupFailed,\n });\n}\n\nexport function deploymentWorktreeRelativePath(\n repositoryRoot: string,\n workspacePath: string,\n) {\n return relative(repositoryRoot, workspacePath).split(sep).join(\"/\");\n}\n"]}
|
|
1
|
+
{"version":3,"file":"deployment-worktree.js","sourceRoot":"","sources":["../../src/lifecycle/deployment-worktree.ts"],"names":[],"mappings":"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","sourcesContent":["import { lstat, mkdtemp, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join, relative, sep } from \"node:path\";\nimport {\n createGitRepository,\n GitRepositoryError,\n type GitCommitIdentity,\n type GitRepository,\n} from \"../git-repository.js\";\n\nconst OWNERSHIP_MARKER = \"bonfire deployment workspace\\n\";\nconst SOURCE_SHA_PATTERN = /^[0-9a-f]{40,64}$/u;\n\nexport type DeploymentWorktreeOptions = {\n remote: string;\n primaryBranch: string;\n sourceSha: string;\n timeoutMs?: number;\n commitIdentity?: GitCommitIdentity;\n};\n\nexport type DeploymentWorktreeErrorKind =\n | \"invalid-source\"\n | \"preflight-conflict\"\n | \"preflight-failure\"\n | \"publication-conflict\"\n | \"publication-rejected\"\n | \"publication-failure\"\n | \"cleanup-failure\";\n\ntype DeploymentPhase = \"preflight\" | \"publication\";\ntype DeploymentStep =\n | \"fetch\"\n | \"directory\"\n | \"marker\"\n | \"worktree-add\"\n | \"source-verify\"\n | \"merge\"\n | \"metadata-read\"\n | \"metadata-write\"\n | \"metadata-commit\"\n | \"push\"\n | \"cleanup\";\n\n/** Log only finite classifications; Git/OS messages may contain paths or credentials. */\nexport function reportDeploymentIssue(\n phase: DeploymentPhase,\n step: DeploymentStep,\n error: unknown,\n) {\n const kind =\n error instanceof GitRepositoryError\n ? error.kind\n : error instanceof DeploymentWorktreeError\n ? error.kind\n : step.startsWith(\"metadata-\") ||\n step === \"directory\" ||\n step === \"marker\"\n ? \"filesystem-failure\"\n : \"unexpected\";\n console.warn(\"[bonfire] Deployment workspace attention\", {\n phase,\n step,\n kind,\n });\n}\n\nexport class DeploymentWorktreeError extends Error {\n readonly kind: DeploymentWorktreeErrorKind;\n readonly cleanupFailed: boolean;\n readonly operationError: unknown;\n readonly cause: unknown;\n\n constructor(\n kind: DeploymentWorktreeErrorKind,\n options: { cleanupFailed?: boolean; operationError?: unknown } = {},\n ) {\n super(`Deployment workspace operation failed: ${kind}`);\n this.name = \"DeploymentWorktreeError\";\n this.kind = kind;\n this.cleanupFailed = options.cleanupFailed ?? false;\n this.operationError = options.operationError;\n this.cause = options.operationError;\n }\n}\n\nfunction withCleanupFailure(error: unknown): DeploymentWorktreeError {\n if (error instanceof DeploymentWorktreeError) {\n return new DeploymentWorktreeError(error.kind, {\n cleanupFailed: true,\n operationError: error.operationError ?? error,\n });\n }\n return new DeploymentWorktreeError(\"cleanup-failure\", {\n cleanupFailed: true,\n operationError: error,\n });\n}\n\ntype DeploymentWorktree = {\n path: string;\n repository: GitRepository;\n cleanup(): Promise<void>;\n};\n\ntype DeploymentWorkspaceOperation<Value> = (\n workspace: DeploymentWorktree,\n) => Promise<Value>;\n\nfunction assertBranchName(value: string, option: string) {\n if (\n value.length === 0 ||\n value.startsWith(\"-\") ||\n value.includes(\"\\0\") ||\n /\\s/u.test(value) ||\n value.includes(\"@{\") ||\n value.split(/[\\\\/]/u).some((segment) => segment === \"..\")\n ) {\n throw new TypeError(`Invalid ${option}`);\n }\n}\n\nfunction assertSourceSha(sourceSha: string) {\n if (!SOURCE_SHA_PATTERN.test(sourceSha)) {\n throw new DeploymentWorktreeError(\"invalid-source\");\n }\n}\n\nfunction remotePrimaryRef(options: DeploymentWorktreeOptions) {\n return `refs/remotes/${options.remote}/${options.primaryBranch}`;\n}\n\nfunction mapMergeError(\n error: unknown,\n phase: \"preflight\" | \"publication\",\n): unknown {\n if (!(error instanceof GitRepositoryError)) return error;\n if (error.kind === \"conflict\") {\n return new DeploymentWorktreeError(`${phase}-conflict`);\n }\n if (phase === \"publication\") {\n if (error.kind === \"non-fast-forward\") {\n return new DeploymentWorktreeError(\"publication-rejected\");\n }\n return new DeploymentWorktreeError(\"publication-failure\");\n }\n return error;\n}\n\nasync function createDeploymentWorktree(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n phase: DeploymentPhase,\n): Promise<DeploymentWorktree> {\n assertBranchName(options.remote, \"remote\");\n assertBranchName(options.primaryBranch, \"primary branch\");\n assertSourceSha(options.sourceSha);\n\n try {\n await repository.fetch(options.remote, options.primaryBranch);\n } catch (error) {\n reportDeploymentIssue(phase, \"fetch\", error);\n throw new DeploymentWorktreeError(`${phase}-failure`, {\n operationError: error,\n });\n }\n let operationDirectory: string;\n try {\n operationDirectory = await mkdtemp(join(tmpdir(), \"bonfire-deployment-\"));\n } catch (error) {\n reportDeploymentIssue(phase, \"directory\", error);\n throw new DeploymentWorktreeError(`${phase}-failure`, {\n operationError: error,\n });\n }\n const workspacePath = join(operationDirectory, \"workspace\");\n const markerPath = join(operationDirectory, \"OWNER\");\n\n let worktreeAdded = false;\n let step: DeploymentStep = \"marker\";\n try {\n await writeFile(markerPath, OWNERSHIP_MARKER, { flag: \"wx\" });\n step = \"worktree-add\";\n await repository.raw([\n \"worktree\",\n \"add\",\n \"--detach\",\n workspacePath,\n remotePrimaryRef(options),\n ]);\n worktreeAdded = true;\n\n const workspaceRepository = createGitRepository({\n repositoryRoot: workspacePath,\n timeoutMs: options.timeoutMs,\n commitIdentity: options.commitIdentity,\n });\n\n step = \"source-verify\";\n await workspaceRepository.raw([\n \"cat-file\",\n \"-e\",\n `${options.sourceSha}^{commit}`,\n ]);\n\n return {\n path: workspacePath,\n repository: workspaceRepository,\n async cleanup() {\n try {\n const marker = await readFile(markerPath, \"utf8\");\n const stats = await lstat(workspacePath);\n if (marker !== OWNERSHIP_MARKER || stats.isSymbolicLink()) {\n throw new DeploymentWorktreeError(\"cleanup-failure\");\n }\n await repository.raw([\n \"worktree\",\n \"remove\",\n \"--force\",\n workspacePath,\n ]);\n await rm(operationDirectory, { recursive: true, force: false });\n } catch (error) {\n reportDeploymentIssue(phase, \"cleanup\", error);\n if (error instanceof DeploymentWorktreeError) throw error;\n throw new DeploymentWorktreeError(\"cleanup-failure\", {\n operationError: error,\n });\n }\n },\n };\n } catch (error) {\n reportDeploymentIssue(phase, step, error);\n let cleanupFailed = false;\n if (worktreeAdded) {\n try {\n await repository.raw([\"worktree\", \"remove\", \"--force\", workspacePath]);\n } catch (cleanupError) {\n cleanupFailed = true;\n reportDeploymentIssue(phase, \"cleanup\", cleanupError);\n }\n }\n try {\n await rm(operationDirectory, { recursive: true, force: true });\n } catch (cleanupError) {\n cleanupFailed = true;\n reportDeploymentIssue(phase, \"cleanup\", cleanupError);\n }\n throw new DeploymentWorktreeError(`${phase}-failure`, {\n cleanupFailed,\n operationError: error,\n });\n }\n}\n\nasync function mergeSource(\n workspace: DeploymentWorktree,\n sourceSha: string,\n phase: \"preflight\" | \"publication\",\n) {\n try {\n await workspace.repository.merge([\n sourceSha,\n \"--no-ff\",\n \"-m\",\n `merge: deploy ${sourceSha.slice(0, 12)}`,\n ]);\n } catch (error) {\n reportDeploymentIssue(phase, \"merge\", error);\n const mapped = mapMergeError(error, phase);\n throw mapped instanceof DeploymentWorktreeError\n ? mapped\n : new DeploymentWorktreeError(`${phase}-failure`, {\n operationError: error,\n });\n }\n}\n\nasync function withDeploymentWorktree<Value>(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n operation: DeploymentWorkspaceOperation<Value>,\n phase: DeploymentPhase,\n): Promise<{ value: Value; cleanupFailed: boolean }> {\n const workspace = await createDeploymentWorktree(repository, options, phase);\n let value: Value;\n try {\n value = await operation(workspace);\n } catch (error) {\n // Preserve the operation's failure while making cleanup attention\n // observable to callers.\n try {\n await workspace.cleanup();\n } catch {\n throw withCleanupFailure(error);\n }\n throw error;\n }\n\n try {\n await workspace.cleanup();\n return { value, cleanupFailed: false };\n } catch {\n return { value, cleanupFailed: true };\n }\n}\n\nexport async function preflightDeploymentSource(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n): Promise<void> {\n const result = await withDeploymentWorktree(\n repository,\n options,\n async (workspace) => {\n await mergeSource(workspace, options.sourceSha, \"preflight\");\n },\n \"preflight\",\n );\n if (result.cleanupFailed) {\n throw new DeploymentWorktreeError(\"cleanup-failure\", {\n cleanupFailed: true,\n });\n }\n}\n\nexport type DeploymentPublicationResult = {\n attempts: 1 | 2;\n cleanupFailed: boolean;\n};\n\nexport async function publishDeploymentSource(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n prepare: (workspace: {\n path: string;\n repository: GitRepository;\n }) => Promise<void>,\n): Promise<DeploymentPublicationResult> {\n let cleanupFailed = false;\n for (const attempt of [1, 2] as const) {\n try {\n const result = await withDeploymentWorktree(\n repository,\n options,\n async (workspace) => {\n await mergeSource(workspace, options.sourceSha, \"publication\");\n await prepare({\n path: workspace.path,\n repository: workspace.repository,\n });\n try {\n await workspace.repository.raw([\n \"push\",\n options.remote,\n `HEAD:refs/heads/${options.primaryBranch}`,\n ]);\n } catch (error) {\n reportDeploymentIssue(\"publication\", \"push\", error);\n const mapped = mapMergeError(error, \"publication\");\n throw mapped instanceof DeploymentWorktreeError\n ? mapped\n : new DeploymentWorktreeError(\"publication-failure\", {\n operationError: error,\n });\n }\n },\n \"publication\",\n );\n cleanupFailed ||= result.cleanupFailed;\n return { attempts: attempt, cleanupFailed };\n } catch (error) {\n cleanupFailed ||=\n error instanceof DeploymentWorktreeError && error.cleanupFailed;\n if (\n !(error instanceof DeploymentWorktreeError) ||\n error.kind !== \"publication-rejected\" ||\n attempt === 2\n ) {\n if (cleanupFailed) {\n if (error instanceof DeploymentWorktreeError) {\n if (!error.cleanupFailed) {\n throw new DeploymentWorktreeError(error.kind, {\n cleanupFailed: true,\n operationError: error.operationError ?? error,\n });\n }\n } else {\n throw new DeploymentWorktreeError(\"publication-failure\", {\n cleanupFailed: true,\n operationError: error,\n });\n }\n }\n throw error;\n }\n }\n }\n\n throw new DeploymentWorktreeError(\"publication-rejected\", {\n cleanupFailed,\n });\n}\n\nexport function deploymentWorktreeRelativePath(\n repositoryRoot: string,\n workspacePath: string,\n) {\n return relative(repositoryRoot, workspacePath).split(sep).join(\"/\");\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-prototype-lifecycle.d.ts","sourceRoot":"","sources":["../../src/lifecycle/local-prototype-lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["import { constants } from \"node:fs\";\nimport { lstat, open, rename, rm, writeFile } from \"node:fs/promises\";\nimport { randomUUID } from \"node:crypto\";\nimport { join, relative, sep } from \"node:path\";\nimport {\n definePrototypeRef,\n getPrototypeUrl,\n LifecycleInputError,\n lifecycleFailure,\n lifecycleSuccess,\n parseCreatePrototypeInput,\n parseDuplicatePrototypeInput,\n parseEditPrototypeInput,\n parseBonfireIdentity,\n parsePrototypeCommandInput,\n parsePrototypeMetadata,\n slugifyPrototypeTitle,\n PrototypeRefError,\n type BranchPreparationValue,\n type CreatePrototypeInput,\n type CreatePrototypeValue,\n type DeletePrototypeValue,\n type DeploymentValue,\n type DeploymentWarningCode,\n type DirtyObservationValue,\n type DiscardPrototypeValue,\n type DuplicatePrototypeInput,\n type DuplicatePrototypeValue,\n type EditPrototypeInput,\n type EditPrototypeValue,\n parseGitModeSetting,\n type GitMode,\n type LifecycleErrorCode,\n type LifecycleResult,\n type BonfireIdentity,\n type PrototypeCommandInput,\n type SavePrototypeValue,\n type RepositorySummary,\n type ThemeRegistry,\n} from \"@ebay-dt/bonfire-core\";\nimport {\n commitPath,\n discardPath,\n GitRepositoryError,\n pushBranch,\n pushCommitToBranch,\n type GitRepository,\n} from \"../git-repository.js\";\nimport { duplicatePrototype, PrototypeDuplicateError } from \"../duplicate.js\";\nimport {\n PrototypeCatalog,\n type PrototypeCatalogLike,\n} from \"../prototype-catalog.js\";\nimport { PrototypeScaffoldError, scaffoldPrototype } from \"../scaffold.js\";\nimport {\n createPrototypeLifecycleRuntime,\n type PrototypeLifecycleRuntimeContext,\n} from \"./runtime-context.js\";\nimport type {\n PreparePrototypeFiles,\n PreparePrototypeFilesRequest,\n} from \"../prepare-prototype-files.js\";\nimport {\n cleanupOwnedDestination,\n validateOwnedDestination,\n type OwnedDestinationReceipt,\n} from \"../internal/owned-destination.js\";\nimport {\n RepositoryBusyError,\n withRepositoryOperation,\n} from \"./operation-gate.js\";\nimport {\n DeploymentProviderError,\n type DeploymentProvider,\n} from \"../deployment-provider.js\";\nimport {\n assertIdentityBranchReady,\n synchronizeIdentityBranch,\n} from \"./repository-synchronization.js\";\nimport { getRepositorySummary } from \"./repository-summary.js\";\nimport {\n DeploymentWorktreeError,\n preflightDeploymentSource,\n publishDeploymentSource,\n} from \"./deployment-worktree.js\";\n\ntype LifecycleMethod<Input, Value> = (\n identity: BonfireIdentity,\n input: Input,\n) => Promise<LifecycleResult<Value>>;\n\nexport type PrototypeLifecycle = {\n /** Declares whether this lifecycle enforces the selected Git mode. */\n readonly gitMode?: GitMode;\n prepareBranch(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<BranchPreparationValue>>;\n create: LifecycleMethod<CreatePrototypeInput, CreatePrototypeValue>;\n duplicate: LifecycleMethod<DuplicatePrototypeInput, DuplicatePrototypeValue>;\n edit: LifecycleMethod<EditPrototypeInput, EditPrototypeValue>;\n delete: LifecycleMethod<PrototypeCommandInput, DeletePrototypeValue>;\n getDirtyState: LifecycleMethod<PrototypeCommandInput, DirtyObservationValue>;\n getRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>>;\n checkRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>>;\n save: LifecycleMethod<PrototypeCommandInput, SavePrototypeValue>;\n discard: LifecycleMethod<PrototypeCommandInput, DiscardPrototypeValue>;\n deploy: LifecycleMethod<PrototypeCommandInput, DeploymentValue>;\n retryPublish?: LifecycleMethod<PrototypeCommandInput, DeploymentValue>;\n};\n\nexport type LocalPrototypeLifecycleOptions = {\n repositoryRoot: string;\n prototypeRoot?: string;\n remote?: string;\n primaryBranch?: string;\n automaticBranchSwitch?: \"enabled\" | \"disabled\";\n gitMode?: GitMode;\n commitIdentity?: {\n name: string;\n email: string;\n };\n catalog?: PrototypeCatalogLike;\n preparePrototypeFiles?: PreparePrototypeFiles;\n deploymentProvider?: DeploymentProvider;\n applicationId?: string;\n themeRegistry?: ThemeRegistry;\n};\n\ntype InternalLocalPrototypeLifecycleOptions = LocalPrototypeLifecycleOptions & {\n runtime?: PrototypeLifecycleRuntimeContext;\n};\n\nfunction repositoryRelativePath(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n const absolutePath = join(runtime.prototypeRoot, ref.login, ref.slug);\n return relative(runtime.repositoryRoot, absolutePath).split(sep).join(\"/\");\n}\n\nfunction destinationRef(identity: BonfireIdentity, title: string) {\n const slug = slugifyPrototypeTitle(title);\n if (slug === null)\n throw new PrototypeRefError(\"title\", \"must produce a slug\");\n return definePrototypeRef({ login: identity.login, slug });\n}\n\nclass PrototypeNotFoundError extends Error {}\nclass PrototypeOwnershipError extends Error {}\nclass PrototypeMetadataFileError extends Error {}\nclass DeploymentUnavailableError extends Error {}\nclass DeploymentSourcePushError extends Error {}\nclass DeploymentNotAcceptedError extends Error {}\n\ntype PendingPublication = {\n identity: BonfireIdentity;\n input: PrototypeCommandInput;\n ref: { login: string; slug: string };\n path: string;\n previewUrl: string;\n deploymentId: string;\n sourceSha: string;\n readyState: DeploymentValue[\"readyState\"];\n synchronizationWarning: DeploymentWarningCode | undefined;\n};\n\nfunction metadataPath(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n return join(runtime.prototypeRoot, ref.login, ref.slug, \"metadata.json\");\n}\n\nasync function assertPrototypeDirectory(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n const paths = [\n runtime.prototypeRoot,\n join(runtime.prototypeRoot, ref.login),\n join(runtime.prototypeRoot, ref.login, ref.slug),\n ];\n\n try {\n for (const path of paths) {\n const stats = await lstat(path);\n if (stats.isSymbolicLink() || !stats.isDirectory()) {\n throw new PrototypeMetadataFileError();\n }\n }\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function readMetadataDocument(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n themeRegistry?: ThemeRegistry,\n) {\n const path = metadataPath(runtime, ref);\n try {\n const file = await open(\n path,\n constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0),\n );\n try {\n const source = await file.readFile(\"utf8\");\n const document: unknown = JSON.parse(source);\n parsePrototypeMetadata(document, { themeRegistry });\n if (\n typeof document !== \"object\" ||\n document === null ||\n Array.isArray(document)\n ) {\n throw new PrototypeMetadataFileError();\n }\n return { ...(document as Record<string, unknown>) };\n } finally {\n await file.close();\n }\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function removePrototypeDirectory(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n await assertPrototypeDirectory(runtime, ref);\n try {\n await rm(join(runtime.prototypeRoot, ref.login, ref.slug), {\n force: false,\n recursive: true,\n });\n } catch {\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function writeMetadataDocument(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n document: Record<string, unknown>,\n) {\n const path = metadataPath(runtime, ref);\n const temporaryPath = `${path}.${randomUUID()}.tmp`;\n\n try {\n const stats = await lstat(path);\n if (stats.isSymbolicLink() || !stats.isFile()) {\n throw new PrototypeMetadataFileError();\n }\n await writeFile(temporaryPath, `${JSON.stringify(document, null, 2)}\\n`, {\n encoding: \"utf8\",\n flag: \"wx\",\n });\n await rename(temporaryPath, path);\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n } finally {\n await rm(temporaryPath, { force: true }).catch(() => undefined);\n }\n}\n\nfunction mapError(error: unknown): LifecycleErrorCode {\n if (error instanceof PrototypeNotFoundError) return \"not-found\";\n if (error instanceof PrototypeOwnershipError) return \"forbidden\";\n if (error instanceof PrototypeMetadataFileError) return \"filesystem-failure\";\n if (error instanceof RepositoryBusyError) return \"repository-busy\";\n if (\n error instanceof PrototypeRefError ||\n error instanceof LifecycleInputError\n ) {\n return \"invalid-input\";\n }\n if (error instanceof GitRepositoryError) {\n if (error.kind === \"timeout\") return \"timeout\";\n if (error.kind === \"conflict\") return \"working-tree-conflict\";\n if (error.kind === \"invalid-configuration\") return \"unexpected-failure\";\n return \"git-failure\";\n }\n if (error instanceof PrototypeScaffoldError) {\n if (error.kind === \"collision\") return \"already-exists\";\n if (error.kind === \"invalid-input\") return \"invalid-input\";\n return \"filesystem-failure\";\n }\n if (error instanceof PrototypeDuplicateError) {\n if (error.kind === \"source-not-found\") return \"not-found\";\n if (error.kind === \"collision\") return \"already-exists\";\n if (error.kind === \"invalid-source\") return \"invalid-input\";\n return \"filesystem-failure\";\n }\n if (error instanceof DeploymentUnavailableError) {\n return \"deployment-unavailable\";\n }\n if (\n error instanceof DeploymentSourcePushError ||\n error instanceof DeploymentNotAcceptedError ||\n error instanceof DeploymentWorktreeError\n ) {\n return \"deployment-failure\";\n }\n if (error instanceof DeploymentProviderError) {\n return error.kind === \"timeout\" ? \"timeout\" : \"deployment-failure\";\n }\n return \"unexpected-failure\";\n}\n\nfunction resultFrom<Value>(\n operation: () => Promise<Value>,\n): Promise<LifecycleResult<Value>> {\n return operation()\n .then(lifecycleSuccess)\n .catch((error: unknown) => lifecycleFailure(mapError(error)));\n}\n\nexport class LocalPrototypeLifecycle implements PrototypeLifecycle {\n readonly #runtime: PrototypeLifecycleRuntimeContext;\n readonly gitMode: GitMode;\n readonly #catalog: PrototypeCatalogLike;\n readonly #repository: GitRepository;\n readonly #preparePrototypeFiles: PreparePrototypeFiles;\n readonly #deploymentProvider?: DeploymentProvider;\n readonly #applicationId?: string;\n readonly #themeRegistry?: ThemeRegistry;\n #restartRequired = false;\n #pendingPublication?: PendingPublication;\n\n constructor(options: LocalPrototypeLifecycleOptions) {\n const internalOptions = options as InternalLocalPrototypeLifecycleOptions;\n const runtime =\n internalOptions.runtime ?? createPrototypeLifecycleRuntime(internalOptions);\n this.#runtime = {\n ...runtime,\n gitMode: parseGitModeSetting(runtime.gitMode),\n };\n this.gitMode = this.#runtime.gitMode;\n this.#themeRegistry = options.themeRegistry;\n this.#catalog =\n internalOptions.catalog ??\n new PrototypeCatalog({\n ...this.#runtime,\n themeRegistry: this.#themeRegistry,\n });\n this.#repository = this.#runtime.gitRepository;\n this.#preparePrototypeFiles =\n internalOptions.preparePrototypeFiles ?? (() => undefined);\n this.#deploymentProvider = internalOptions.deploymentProvider;\n this.#applicationId = internalOptions.applicationId;\n if (\n this.#deploymentProvider &&\n (typeof this.#applicationId !== \"string\" ||\n this.#applicationId.length === 0)\n ) {\n throw new TypeError(\n \"applicationId must be provided when deploymentProvider is configured\",\n );\n }\n }\n\n async #verifiedIdentity(identity: BonfireIdentity) {\n return parseBonfireIdentity(identity);\n }\n\n async #currentBranch() {\n const branch = await this.#repository.currentBranch();\n if (branch === \"HEAD\" || branch.length === 0) {\n throw new GitRepositoryError(\"conflict\", \"current-branch\");\n }\n return branch;\n }\n\n async #ownedPrototype(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ) {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n if (normalizedInput.ref.login !== verifiedIdentity.login) {\n throw new PrototypeOwnershipError();\n }\n const summary = await this.#catalog.getSummary(normalizedInput.ref);\n if (!summary) {\n throw new PrototypeNotFoundError();\n }\n\n return {\n ref: normalizedInput.ref,\n path: repositoryRelativePath(this.#runtime, normalizedInput.ref),\n summary,\n };\n }\n\n async #prepareMutationBranch(login: string) {\n await assertIdentityBranchReady(this.#repository, login);\n return login;\n }\n\n async #prepareDestination(\n receipt: OwnedDestinationReceipt,\n expectedFiles: readonly string[],\n request: PreparePrototypeFilesRequest,\n ) {\n try {\n const frozenRequest = Object.freeze({\n ...request,\n ref: Object.freeze({ ...request.ref }),\n identity: Object.freeze({ ...request.identity }),\n ...(request.operation === \"duplicate\"\n ? { source: Object.freeze({ ...request.source }) }\n : {}),\n }) as PreparePrototypeFilesRequest;\n await this.#preparePrototypeFiles(frozenRequest);\n await validateOwnedDestination(\n receipt,\n expectedFiles,\n request.ref,\n this.#themeRegistry,\n );\n } catch {\n try {\n await cleanupOwnedDestination(receipt);\n } catch {\n // The failure remains a finite filesystem failure without retrying cleanup.\n }\n throw new PrototypeScaffoldError(\"filesystem\");\n }\n }\n\n async prepareBranch(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<BranchPreparationValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (\n this.#runtime.gitMode === \"manual\" ||\n this.#runtime.automaticBranchSwitch === \"disabled\"\n ) {\n await this.#currentBranch();\n return { status: \"disabled\" } satisfies BranchPreparationValue;\n }\n\n const synchronization = await synchronizeIdentityBranch(\n this.#repository,\n {\n identityBranch: verifiedIdentity.login,\n primaryBranch: this.#runtime.primaryBranch,\n remote: this.#runtime.remote,\n managedPaths: this.#runtime.managedRepositoryPaths,\n },\n );\n if (synchronization.status !== \"synchronized\") {\n return {\n status: synchronization.status,\n reason: synchronization.reason,\n ...(synchronization.affectedPaths.length > 0\n ? { affectedPaths: synchronization.affectedPaths }\n : {}),\n } satisfies BranchPreparationValue;\n }\n if (\n synchronization.affectedPaths.some((path) =>\n this.#runtime.managedRepositoryPaths.includes(path),\n )\n ) {\n this.#restartRequired = true;\n }\n return {\n status: \"ready\",\n ...(synchronization.affectedPaths.length > 0\n ? { affectedPaths: synchronization.affectedPaths }\n : {}),\n } satisfies BranchPreparationValue;\n },\n );\n });\n }\n\n async create(\n identity: BonfireIdentity,\n input: CreatePrototypeInput,\n ): Promise<LifecycleResult<CreatePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseCreatePrototypeInput(\n input,\n this.#themeRegistry,\n );\n const ref = destinationRef(verifiedIdentity, normalizedInput.title);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (await this.#catalog.getSummary(ref)) {\n throw new PrototypeScaffoldError(\"collision\");\n }\n\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n const prepared = await scaffoldPrototype({\n paths: this.#runtime,\n ref,\n title: normalizedInput.title,\n description: normalizedInput.description,\n authorName: verifiedIdentity.name,\n theme: normalizedInput.theme,\n themeRegistry: this.#themeRegistry,\n });\n await this.#prepareDestination(\n prepared.receipt,\n prepared.expectedFiles,\n {\n operation: \"create\",\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n prototypePath: prepared.destination,\n ref,\n identity: verifiedIdentity,\n title: normalizedInput.title,\n description: normalizedInput.description,\n },\n );\n if (branch === undefined) {\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: \"manual\",\n } satisfies CreatePrototypeValue;\n }\n\n await commitPath(\n this.#repository,\n repositoryRelativePath(this.#runtime, ref),\n `init: ${ref.slug}`,\n );\n const push = await pushBranch(\n this.#repository,\n this.#runtime.remote,\n branch,\n );\n\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: push.remoteStatus,\n } satisfies CreatePrototypeValue;\n },\n );\n });\n }\n\n async edit(\n identity: BonfireIdentity,\n input: EditPrototypeInput,\n ): Promise<LifecycleResult<EditPrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseEditPrototypeInput(\n input,\n this.#themeRegistry,\n );\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(verifiedIdentity, {\n ref: normalizedInput.ref,\n });\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n await assertPrototypeDirectory(this.#runtime, ref);\n const document = await readMetadataDocument(\n this.#runtime,\n ref,\n this.#themeRegistry,\n );\n const metadata = parsePrototypeMetadata(document, {\n themeRegistry: this.#themeRegistry,\n });\n if (metadata.authorLogin !== ref.login) {\n throw new PrototypeNotFoundError();\n }\n\n await writeMetadataDocument(this.#runtime, ref, {\n ...document,\n title: normalizedInput.title,\n description: normalizedInput.description,\n updatedAt: new Date().toISOString(),\n ...(normalizedInput.theme === undefined\n ? {}\n : { theme: normalizedInput.theme }),\n });\n if (branch !== undefined) {\n await commitPath(this.#repository, path, `edit: ${ref.slug}`);\n await pushBranch(this.#repository, this.#runtime.remote, branch);\n }\n\n return { edited: true, ref } satisfies EditPrototypeValue;\n },\n );\n });\n }\n\n async delete(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeletePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(\n verifiedIdentity,\n normalizedInput,\n );\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n await removePrototypeDirectory(this.#runtime, ref);\n if (branch !== undefined) {\n await commitPath(this.#repository, path, `delete: ${ref.slug}`);\n await pushBranch(this.#repository, this.#runtime.remote, branch);\n }\n\n return { deleted: true, ref } satisfies DeletePrototypeValue;\n },\n );\n });\n }\n\n async getDirtyState(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DirtyObservationValue>> {\n return resultFrom(async () => {\n const { path, summary } = await this.#ownedPrototype(identity, input);\n const status = await this.#repository.status([path]);\n return {\n status: status.files.length > 0 ? \"dirty\" : \"clean\",\n ...(summary.previewUrl !== undefined\n ? { previewUrl: summary.previewUrl }\n : {}),\n } satisfies DirtyObservationValue;\n });\n }\n\n async getRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return getRepositorySummary({\n repository: this.#repository,\n catalog: this.#catalog,\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n identity: verifiedIdentity,\n mode: this.#runtime.gitMode,\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n restartRequired: this.#restartRequired,\n });\n });\n }\n\n async checkRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return getRepositorySummary({\n repository: this.#repository,\n catalog: this.#catalog,\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n identity: verifiedIdentity,\n mode: this.#runtime.gitMode,\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n restartRequired: this.#restartRequired,\n refresh: true,\n });\n });\n }\n\n async #headCommit() {\n const commit = (await this.#repository.raw([\"rev-parse\", \"HEAD\"])).trim();\n if (!/^[0-9a-f]{40,64}$/iu.test(commit)) {\n throw new GitRepositoryError(\"malformed-output\", \"head-commit\");\n }\n return commit.toLowerCase();\n }\n\n async #prepareDeploymentBranch(login: string) {\n if (this.#runtime.automaticBranchSwitch === \"disabled\") {\n await assertIdentityBranchReady(this.#repository, login);\n return \"updates-not-synchronized\" as const;\n }\n\n const synchronization = await synchronizeIdentityBranch(this.#repository, {\n identityBranch: login,\n primaryBranch: this.#runtime.primaryBranch,\n remote: this.#runtime.remote,\n managedPaths: this.#runtime.managedRepositoryPaths,\n });\n if (synchronization.status === \"unsafe\") {\n throw new GitRepositoryError(\"conflict\", \"deployment-preparation\");\n }\n await assertIdentityBranchReady(this.#repository, login);\n return synchronization.status === \"deferred\"\n ? (\"updates-not-synchronized\" as const)\n : undefined;\n }\n\n #workspaceRuntime(\n workspacePath: string,\n workspaceRepository: GitRepository,\n ): PrototypeLifecycleRuntimeContext {\n return {\n ...this.#runtime,\n repositoryRoot: workspacePath,\n repositoryRealPath: workspacePath,\n prototypeRoot: join(\n workspacePath,\n relative(this.#runtime.repositoryRoot, this.#runtime.prototypeRoot),\n ),\n gitRepository: workspaceRepository,\n };\n }\n\n async #publishDeploymentMetadata(pending: PendingPublication) {\n return publishDeploymentSource(\n this.#repository,\n {\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n sourceSha: pending.sourceSha,\n commitIdentity: this.#runtime.commitIdentity,\n },\n async ({ path, repository }) => {\n const workspaceRuntime = this.#workspaceRuntime(path, repository);\n const mergedDocument = await readMetadataDocument(\n workspaceRuntime,\n pending.ref,\n this.#themeRegistry,\n );\n await writeMetadataDocument(workspaceRuntime, pending.ref, {\n ...mergedDocument,\n previewUrl: pending.previewUrl,\n updatedAt: new Date().toISOString(),\n });\n await commitPath(\n repository,\n pending.path,\n `deploy: ${pending.ref.slug} (metadata)`,\n );\n },\n );\n }\n\n async #deploymentValue(\n pending: PendingPublication,\n publicationWarnings: readonly DeploymentWarningCode[] = [],\n ): Promise<DeploymentValue> {\n const warnings = [\n ...(pending.synchronizationWarning === undefined\n ? []\n : [pending.synchronizationWarning]),\n ...publicationWarnings,\n ];\n return {\n deployed: true,\n ref: pending.ref,\n previewUrl: pending.previewUrl,\n deploymentId: pending.deploymentId,\n sourceSha: pending.sourceSha,\n readyState: pending.readyState,\n warning: publicationWarnings[0] ?? pending.synchronizationWarning,\n ...(warnings.length === 0 ? {} : { warnings }),\n };\n }\n\n async deploy(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeploymentValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n if (!this.#deploymentProvider) {\n throw new DeploymentUnavailableError();\n }\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(\n verifiedIdentity,\n normalizedInput,\n );\n const synchronizationWarning = await this.#prepareDeploymentBranch(\n verifiedIdentity.login,\n );\n await assertPrototypeDirectory(this.#runtime, ref);\n const document = await readMetadataDocument(\n this.#runtime,\n ref,\n this.#themeRegistry,\n );\n const metadata = parsePrototypeMetadata(document, {\n themeRegistry: this.#themeRegistry,\n });\n if (metadata.authorLogin !== ref.login) {\n throw new PrototypeNotFoundError();\n }\n\n await commitPath(this.#repository, path, `deploy: ${ref.slug}`);\n const sourceSha = await this.#headCommit();\n const sourcePush = await pushCommitToBranch(\n this.#repository,\n this.#runtime.remote,\n sourceSha,\n verifiedIdentity.login,\n );\n if (sourcePush.remoteStatus !== \"pushed\") {\n throw new DeploymentSourcePushError();\n }\n\n await preflightDeploymentSource(this.#repository, {\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n sourceSha,\n commitIdentity: this.#runtime.commitIdentity,\n });\n\n const deployment = await this.#deploymentProvider!.deploy({\n applicationId: this.#applicationId!,\n login: verifiedIdentity.login,\n });\n if (\n deployment.commitSha.toLowerCase() !== sourceSha ||\n ![\"READY\", \"QUEUED\", \"INITIALIZING\", \"BUILDING\"].includes(\n deployment.readyState,\n )\n ) {\n throw new DeploymentNotAcceptedError();\n }\n const previewUrl = new URL(\n getPrototypeUrl(ref).slice(1),\n `${deployment.url.replace(/\\/+$/, \"\")}/`,\n ).toString();\n const pending: PendingPublication = {\n identity: verifiedIdentity,\n input: normalizedInput,\n ref,\n path,\n previewUrl,\n deploymentId: deployment.deploymentId,\n sourceSha,\n readyState: deployment.readyState,\n synchronizationWarning,\n };\n\n try {\n const publication = await this.#publishDeploymentMetadata(pending);\n this.#pendingPublication = undefined;\n return this.#deploymentValue(\n pending,\n publication.cleanupFailed\n ? [\"publication-cleanup-failed\"]\n : [],\n );\n } catch (error) {\n // Keep the accepted deployment retryable for every publication\n // failure, including metadata preparation failures.\n this.#pendingPublication = pending;\n return this.#deploymentValue(pending, [\n error instanceof DeploymentWorktreeError &&\n error.kind !== \"cleanup-failure\"\n ? \"primary-merge-failed\"\n : \"metadata-persistence-failed\",\n ...(error instanceof DeploymentWorktreeError && error.cleanupFailed\n ? [\"publication-cleanup-failed\" as const]\n : []),\n ]);\n }\n },\n );\n });\n }\n\n async retryPublish(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeploymentValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n const pending = this.#pendingPublication;\n if (\n pending === undefined ||\n pending.identity.login !== verifiedIdentity.login ||\n pending.ref.login !== normalizedInput.ref.login ||\n pending.ref.slug !== normalizedInput.ref.slug\n ) {\n throw new DeploymentUnavailableError();\n }\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await assertIdentityBranchReady(this.#repository, verifiedIdentity.login);\n const publication = await this.#publishDeploymentMetadata(pending);\n this.#pendingPublication = undefined;\n return this.#deploymentValue(\n pending,\n publication.cleanupFailed ? [\"publication-cleanup-failed\"] : [],\n );\n },\n );\n });\n }\n\n async save(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<SavePrototypeValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const { ref, path } = await this.#ownedPrototype(\n verifiedIdentity,\n input,\n );\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await this.#prepareMutationBranch(verifiedIdentity.login);\n await commitPath(this.#repository, path, `save: ${ref.slug}`);\n await pushBranch(\n this.#repository,\n this.#runtime.remote,\n verifiedIdentity.login,\n );\n return { saved: true } satisfies SavePrototypeValue;\n },\n );\n });\n }\n\n async discard(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DiscardPrototypeValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const { path } = await this.#ownedPrototype(\n verifiedIdentity,\n input,\n );\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await discardPath(this.#repository, path);\n return { discarded: true } satisfies DiscardPrototypeValue;\n },\n );\n });\n }\n\n async duplicate(\n identity: BonfireIdentity,\n input: DuplicatePrototypeInput,\n ): Promise<LifecycleResult<DuplicatePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseDuplicatePrototypeInput(input);\n if (!(await this.#catalog.getSummary(normalizedInput.source))) {\n throw new PrototypeDuplicateError(\"source-not-found\");\n }\n const ref = destinationRef(verifiedIdentity, normalizedInput.title);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (await this.#catalog.getSummary(ref)) {\n throw new PrototypeDuplicateError(\"collision\");\n }\n\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n const prepared = await duplicatePrototype({\n paths: this.#runtime,\n source: normalizedInput.source,\n destination: ref,\n title: normalizedInput.title,\n description: normalizedInput.description,\n authorName: verifiedIdentity.name,\n themeRegistry: this.#themeRegistry,\n });\n await this.#prepareDestination(\n prepared.receipt,\n prepared.expectedFiles,\n {\n operation: \"duplicate\",\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n prototypePath: prepared.destination,\n ref,\n source: normalizedInput.source,\n identity: verifiedIdentity,\n title: normalizedInput.title,\n description: normalizedInput.description,\n },\n );\n if (branch === undefined) {\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: \"manual\",\n } satisfies DuplicatePrototypeValue;\n }\n\n await commitPath(\n this.#repository,\n repositoryRelativePath(this.#runtime, ref),\n `init: ${ref.slug}`,\n );\n const push = await pushBranch(\n this.#repository,\n this.#runtime.remote,\n branch,\n );\n\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: push.remoteStatus,\n } satisfies DuplicatePrototypeValue;\n },\n );\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"local-prototype-lifecycle.d.ts","sourceRoot":"","sources":["../../src/lifecycle/local-prototype-lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["import { constants } from \"node:fs\";\nimport { lstat, open, rename, rm, writeFile } from \"node:fs/promises\";\nimport { randomUUID } from \"node:crypto\";\nimport { join, relative, sep } from \"node:path\";\nimport {\n definePrototypeRef,\n getPrototypeUrl,\n LifecycleInputError,\n lifecycleFailure,\n lifecycleSuccess,\n parseCreatePrototypeInput,\n parseDuplicatePrototypeInput,\n parseEditPrototypeInput,\n parseBonfireIdentity,\n parsePrototypeCommandInput,\n parsePrototypeMetadata,\n slugifyPrototypeTitle,\n PrototypeRefError,\n type BranchPreparationValue,\n type CreatePrototypeInput,\n type CreatePrototypeValue,\n type DeletePrototypeValue,\n type DeploymentValue,\n type DeploymentWarningCode,\n type DirtyObservationValue,\n type DiscardPrototypeValue,\n type DuplicatePrototypeInput,\n type DuplicatePrototypeValue,\n type EditPrototypeInput,\n type EditPrototypeValue,\n parseGitModeSetting,\n type GitMode,\n type LifecycleErrorCode,\n type LifecycleResult,\n type BonfireIdentity,\n type PrototypeCommandInput,\n type SavePrototypeValue,\n type RepositorySummary,\n type ThemeRegistry,\n} from \"@ebay-dt/bonfire-core\";\nimport {\n commitPath,\n discardPath,\n GitRepositoryError,\n pushBranch,\n pushCommitToBranch,\n type GitRepository,\n} from \"../git-repository.js\";\nimport { duplicatePrototype, PrototypeDuplicateError } from \"../duplicate.js\";\nimport {\n PrototypeCatalog,\n type PrototypeCatalogLike,\n} from \"../prototype-catalog.js\";\nimport { PrototypeScaffoldError, scaffoldPrototype } from \"../scaffold.js\";\nimport {\n createPrototypeLifecycleRuntime,\n type PrototypeLifecycleRuntimeContext,\n} from \"./runtime-context.js\";\nimport type {\n PreparePrototypeFiles,\n PreparePrototypeFilesRequest,\n} from \"../prepare-prototype-files.js\";\nimport {\n cleanupOwnedDestination,\n validateOwnedDestination,\n type OwnedDestinationReceipt,\n} from \"../internal/owned-destination.js\";\nimport {\n RepositoryBusyError,\n withRepositoryOperation,\n} from \"./operation-gate.js\";\nimport {\n DeploymentProviderError,\n type DeploymentProvider,\n} from \"../deployment-provider.js\";\nimport {\n assertIdentityBranchReady,\n synchronizeIdentityBranch,\n} from \"./repository-synchronization.js\";\nimport { getRepositorySummary } from \"./repository-summary.js\";\nimport {\n DeploymentWorktreeError,\n preflightDeploymentSource,\n publishDeploymentSource,\n reportDeploymentIssue,\n} from \"./deployment-worktree.js\";\n\ntype LifecycleMethod<Input, Value> = (\n identity: BonfireIdentity,\n input: Input,\n) => Promise<LifecycleResult<Value>>;\n\nexport type PrototypeLifecycle = {\n /** Declares whether this lifecycle enforces the selected Git mode. */\n readonly gitMode?: GitMode;\n prepareBranch(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<BranchPreparationValue>>;\n create: LifecycleMethod<CreatePrototypeInput, CreatePrototypeValue>;\n duplicate: LifecycleMethod<DuplicatePrototypeInput, DuplicatePrototypeValue>;\n edit: LifecycleMethod<EditPrototypeInput, EditPrototypeValue>;\n delete: LifecycleMethod<PrototypeCommandInput, DeletePrototypeValue>;\n getDirtyState: LifecycleMethod<PrototypeCommandInput, DirtyObservationValue>;\n getRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>>;\n checkRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>>;\n save: LifecycleMethod<PrototypeCommandInput, SavePrototypeValue>;\n discard: LifecycleMethod<PrototypeCommandInput, DiscardPrototypeValue>;\n deploy: LifecycleMethod<PrototypeCommandInput, DeploymentValue>;\n retryPublish?: LifecycleMethod<PrototypeCommandInput, DeploymentValue>;\n};\n\nexport type LocalPrototypeLifecycleOptions = {\n repositoryRoot: string;\n prototypeRoot?: string;\n remote?: string;\n primaryBranch?: string;\n automaticBranchSwitch?: \"enabled\" | \"disabled\";\n gitMode?: GitMode;\n commitIdentity?: {\n name: string;\n email: string;\n };\n catalog?: PrototypeCatalogLike;\n preparePrototypeFiles?: PreparePrototypeFiles;\n deploymentProvider?: DeploymentProvider;\n applicationId?: string;\n themeRegistry?: ThemeRegistry;\n};\n\ntype InternalLocalPrototypeLifecycleOptions = LocalPrototypeLifecycleOptions & {\n runtime?: PrototypeLifecycleRuntimeContext;\n};\n\nfunction repositoryRelativePath(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n const absolutePath = join(runtime.prototypeRoot, ref.login, ref.slug);\n return relative(runtime.repositoryRoot, absolutePath).split(sep).join(\"/\");\n}\n\nfunction destinationRef(identity: BonfireIdentity, title: string) {\n const slug = slugifyPrototypeTitle(title);\n if (slug === null)\n throw new PrototypeRefError(\"title\", \"must produce a slug\");\n return definePrototypeRef({ login: identity.login, slug });\n}\n\nclass PrototypeNotFoundError extends Error {}\nclass PrototypeOwnershipError extends Error {}\nclass PrototypeMetadataFileError extends Error {}\nclass DeploymentUnavailableError extends Error {}\nclass DeploymentSourcePushError extends Error {}\nclass DeploymentNotAcceptedError extends Error {}\n\ntype PendingPublication = {\n identity: BonfireIdentity;\n input: PrototypeCommandInput;\n ref: { login: string; slug: string };\n path: string;\n previewUrl: string;\n deploymentId: string;\n sourceSha: string;\n readyState: DeploymentValue[\"readyState\"];\n synchronizationWarning: DeploymentWarningCode | undefined;\n};\n\nfunction metadataPath(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n return join(runtime.prototypeRoot, ref.login, ref.slug, \"metadata.json\");\n}\n\nasync function assertPrototypeDirectory(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n const paths = [\n runtime.prototypeRoot,\n join(runtime.prototypeRoot, ref.login),\n join(runtime.prototypeRoot, ref.login, ref.slug),\n ];\n\n try {\n for (const path of paths) {\n const stats = await lstat(path);\n if (stats.isSymbolicLink() || !stats.isDirectory()) {\n throw new PrototypeMetadataFileError();\n }\n }\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function readMetadataDocument(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n themeRegistry?: ThemeRegistry,\n) {\n const path = metadataPath(runtime, ref);\n try {\n const file = await open(\n path,\n constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0),\n );\n try {\n const source = await file.readFile(\"utf8\");\n const document: unknown = JSON.parse(source);\n parsePrototypeMetadata(document, { themeRegistry });\n if (\n typeof document !== \"object\" ||\n document === null ||\n Array.isArray(document)\n ) {\n throw new PrototypeMetadataFileError();\n }\n return { ...(document as Record<string, unknown>) };\n } finally {\n await file.close();\n }\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function removePrototypeDirectory(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n await assertPrototypeDirectory(runtime, ref);\n try {\n await rm(join(runtime.prototypeRoot, ref.login, ref.slug), {\n force: false,\n recursive: true,\n });\n } catch {\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function writeMetadataDocument(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n document: Record<string, unknown>,\n) {\n const path = metadataPath(runtime, ref);\n const temporaryPath = `${path}.${randomUUID()}.tmp`;\n\n try {\n const stats = await lstat(path);\n if (stats.isSymbolicLink() || !stats.isFile()) {\n throw new PrototypeMetadataFileError();\n }\n await writeFile(temporaryPath, `${JSON.stringify(document, null, 2)}\\n`, {\n encoding: \"utf8\",\n flag: \"wx\",\n });\n await rename(temporaryPath, path);\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n } finally {\n await rm(temporaryPath, { force: true }).catch(() => undefined);\n }\n}\n\nfunction mapError(error: unknown): LifecycleErrorCode {\n if (error instanceof PrototypeNotFoundError) return \"not-found\";\n if (error instanceof PrototypeOwnershipError) return \"forbidden\";\n if (error instanceof PrototypeMetadataFileError) return \"filesystem-failure\";\n if (error instanceof RepositoryBusyError) return \"repository-busy\";\n if (\n error instanceof PrototypeRefError ||\n error instanceof LifecycleInputError\n ) {\n return \"invalid-input\";\n }\n if (error instanceof GitRepositoryError) {\n if (error.kind === \"timeout\") return \"timeout\";\n if (error.kind === \"conflict\") return \"working-tree-conflict\";\n if (error.kind === \"invalid-configuration\") return \"unexpected-failure\";\n return \"git-failure\";\n }\n if (error instanceof PrototypeScaffoldError) {\n if (error.kind === \"collision\") return \"already-exists\";\n if (error.kind === \"invalid-input\") return \"invalid-input\";\n return \"filesystem-failure\";\n }\n if (error instanceof PrototypeDuplicateError) {\n if (error.kind === \"source-not-found\") return \"not-found\";\n if (error.kind === \"collision\") return \"already-exists\";\n if (error.kind === \"invalid-source\") return \"invalid-input\";\n return \"filesystem-failure\";\n }\n if (error instanceof DeploymentUnavailableError) {\n return \"deployment-unavailable\";\n }\n if (\n error instanceof DeploymentSourcePushError ||\n error instanceof DeploymentNotAcceptedError ||\n error instanceof DeploymentWorktreeError\n ) {\n return \"deployment-failure\";\n }\n if (error instanceof DeploymentProviderError) {\n return error.kind === \"timeout\" ? \"timeout\" : \"deployment-failure\";\n }\n return \"unexpected-failure\";\n}\n\nfunction resultFrom<Value>(\n operation: () => Promise<Value>,\n): Promise<LifecycleResult<Value>> {\n return operation()\n .then(lifecycleSuccess)\n .catch((error: unknown) => lifecycleFailure(mapError(error)));\n}\n\nasync function diagnoseMetadataStep<Value>(\n step: \"metadata-read\" | \"metadata-write\" | \"metadata-commit\",\n operation: () => Promise<Value>,\n): Promise<Value> {\n try {\n return await operation();\n } catch (error) {\n reportDeploymentIssue(\"publication\", step, error);\n throw error;\n }\n}\n\nexport class LocalPrototypeLifecycle implements PrototypeLifecycle {\n readonly #runtime: PrototypeLifecycleRuntimeContext;\n readonly gitMode: GitMode;\n readonly #catalog: PrototypeCatalogLike;\n readonly #repository: GitRepository;\n readonly #preparePrototypeFiles: PreparePrototypeFiles;\n readonly #deploymentProvider?: DeploymentProvider;\n readonly #applicationId?: string;\n readonly #themeRegistry?: ThemeRegistry;\n #restartRequired = false;\n #pendingPublication?: PendingPublication;\n\n constructor(options: LocalPrototypeLifecycleOptions) {\n const internalOptions = options as InternalLocalPrototypeLifecycleOptions;\n const runtime =\n internalOptions.runtime ??\n createPrototypeLifecycleRuntime(internalOptions);\n this.#runtime = {\n ...runtime,\n gitMode: parseGitModeSetting(runtime.gitMode),\n };\n this.gitMode = this.#runtime.gitMode;\n this.#themeRegistry = options.themeRegistry;\n this.#catalog =\n internalOptions.catalog ??\n new PrototypeCatalog({\n ...this.#runtime,\n themeRegistry: this.#themeRegistry,\n });\n this.#repository = this.#runtime.gitRepository;\n this.#preparePrototypeFiles =\n internalOptions.preparePrototypeFiles ?? (() => undefined);\n this.#deploymentProvider = internalOptions.deploymentProvider;\n this.#applicationId = internalOptions.applicationId;\n if (\n this.#deploymentProvider &&\n (typeof this.#applicationId !== \"string\" ||\n this.#applicationId.length === 0)\n ) {\n throw new TypeError(\n \"applicationId must be provided when deploymentProvider is configured\",\n );\n }\n }\n\n async #verifiedIdentity(identity: BonfireIdentity) {\n return parseBonfireIdentity(identity);\n }\n\n async #currentBranch() {\n const branch = await this.#repository.currentBranch();\n if (branch === \"HEAD\" || branch.length === 0) {\n throw new GitRepositoryError(\"conflict\", \"current-branch\");\n }\n return branch;\n }\n\n async #ownedPrototype(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ) {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n if (normalizedInput.ref.login !== verifiedIdentity.login) {\n throw new PrototypeOwnershipError();\n }\n const summary = await this.#catalog.getSummary(normalizedInput.ref);\n if (!summary) {\n throw new PrototypeNotFoundError();\n }\n\n return {\n ref: normalizedInput.ref,\n path: repositoryRelativePath(this.#runtime, normalizedInput.ref),\n summary,\n };\n }\n\n async #prepareMutationBranch(login: string) {\n await assertIdentityBranchReady(this.#repository, login);\n return login;\n }\n\n async #prepareDestination(\n receipt: OwnedDestinationReceipt,\n expectedFiles: readonly string[],\n request: PreparePrototypeFilesRequest,\n ) {\n try {\n const frozenRequest = Object.freeze({\n ...request,\n ref: Object.freeze({ ...request.ref }),\n identity: Object.freeze({ ...request.identity }),\n ...(request.operation === \"duplicate\"\n ? { source: Object.freeze({ ...request.source }) }\n : {}),\n }) as PreparePrototypeFilesRequest;\n await this.#preparePrototypeFiles(frozenRequest);\n await validateOwnedDestination(\n receipt,\n expectedFiles,\n request.ref,\n this.#themeRegistry,\n );\n } catch {\n try {\n await cleanupOwnedDestination(receipt);\n } catch {\n // The failure remains a finite filesystem failure without retrying cleanup.\n }\n throw new PrototypeScaffoldError(\"filesystem\");\n }\n }\n\n async prepareBranch(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<BranchPreparationValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (\n this.#runtime.gitMode === \"manual\" ||\n this.#runtime.automaticBranchSwitch === \"disabled\"\n ) {\n await this.#currentBranch();\n return { status: \"disabled\" } satisfies BranchPreparationValue;\n }\n\n const synchronization = await synchronizeIdentityBranch(\n this.#repository,\n {\n identityBranch: verifiedIdentity.login,\n primaryBranch: this.#runtime.primaryBranch,\n remote: this.#runtime.remote,\n managedPaths: this.#runtime.managedRepositoryPaths,\n },\n );\n if (synchronization.status !== \"synchronized\") {\n return {\n status: synchronization.status,\n reason: synchronization.reason,\n ...(synchronization.affectedPaths.length > 0\n ? { affectedPaths: synchronization.affectedPaths }\n : {}),\n } satisfies BranchPreparationValue;\n }\n if (\n synchronization.affectedPaths.some((path) =>\n this.#runtime.managedRepositoryPaths.includes(path),\n )\n ) {\n this.#restartRequired = true;\n }\n return {\n status: \"ready\",\n ...(synchronization.affectedPaths.length > 0\n ? { affectedPaths: synchronization.affectedPaths }\n : {}),\n } satisfies BranchPreparationValue;\n },\n );\n });\n }\n\n async create(\n identity: BonfireIdentity,\n input: CreatePrototypeInput,\n ): Promise<LifecycleResult<CreatePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseCreatePrototypeInput(\n input,\n this.#themeRegistry,\n );\n const ref = destinationRef(verifiedIdentity, normalizedInput.title);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (await this.#catalog.getSummary(ref)) {\n throw new PrototypeScaffoldError(\"collision\");\n }\n\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n const prepared = await scaffoldPrototype({\n paths: this.#runtime,\n ref,\n title: normalizedInput.title,\n description: normalizedInput.description,\n authorName: verifiedIdentity.name,\n theme: normalizedInput.theme,\n themeRegistry: this.#themeRegistry,\n });\n await this.#prepareDestination(\n prepared.receipt,\n prepared.expectedFiles,\n {\n operation: \"create\",\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n prototypePath: prepared.destination,\n ref,\n identity: verifiedIdentity,\n title: normalizedInput.title,\n description: normalizedInput.description,\n },\n );\n if (branch === undefined) {\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: \"manual\",\n } satisfies CreatePrototypeValue;\n }\n\n await commitPath(\n this.#repository,\n repositoryRelativePath(this.#runtime, ref),\n `init: ${ref.slug}`,\n );\n const push = await pushBranch(\n this.#repository,\n this.#runtime.remote,\n branch,\n );\n\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: push.remoteStatus,\n } satisfies CreatePrototypeValue;\n },\n );\n });\n }\n\n async edit(\n identity: BonfireIdentity,\n input: EditPrototypeInput,\n ): Promise<LifecycleResult<EditPrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseEditPrototypeInput(\n input,\n this.#themeRegistry,\n );\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(verifiedIdentity, {\n ref: normalizedInput.ref,\n });\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n await assertPrototypeDirectory(this.#runtime, ref);\n const document = await readMetadataDocument(\n this.#runtime,\n ref,\n this.#themeRegistry,\n );\n const metadata = parsePrototypeMetadata(document, {\n themeRegistry: this.#themeRegistry,\n });\n if (metadata.authorLogin !== ref.login) {\n throw new PrototypeNotFoundError();\n }\n\n await writeMetadataDocument(this.#runtime, ref, {\n ...document,\n title: normalizedInput.title,\n description: normalizedInput.description,\n updatedAt: new Date().toISOString(),\n ...(normalizedInput.theme === undefined\n ? {}\n : { theme: normalizedInput.theme }),\n });\n if (branch !== undefined) {\n await commitPath(this.#repository, path, `edit: ${ref.slug}`);\n await pushBranch(this.#repository, this.#runtime.remote, branch);\n }\n\n return { edited: true, ref } satisfies EditPrototypeValue;\n },\n );\n });\n }\n\n async delete(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeletePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(\n verifiedIdentity,\n normalizedInput,\n );\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n await removePrototypeDirectory(this.#runtime, ref);\n if (branch !== undefined) {\n await commitPath(this.#repository, path, `delete: ${ref.slug}`);\n await pushBranch(this.#repository, this.#runtime.remote, branch);\n }\n\n return { deleted: true, ref } satisfies DeletePrototypeValue;\n },\n );\n });\n }\n\n async getDirtyState(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DirtyObservationValue>> {\n return resultFrom(async () => {\n const { path, summary } = await this.#ownedPrototype(identity, input);\n const status = await this.#repository.status([path]);\n return {\n status: status.files.length > 0 ? \"dirty\" : \"clean\",\n ...(summary.previewUrl !== undefined\n ? { previewUrl: summary.previewUrl }\n : {}),\n } satisfies DirtyObservationValue;\n });\n }\n\n async getRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return getRepositorySummary({\n repository: this.#repository,\n catalog: this.#catalog,\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n identity: verifiedIdentity,\n mode: this.#runtime.gitMode,\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n restartRequired: this.#restartRequired,\n });\n });\n }\n\n async checkRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return getRepositorySummary({\n repository: this.#repository,\n catalog: this.#catalog,\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n identity: verifiedIdentity,\n mode: this.#runtime.gitMode,\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n restartRequired: this.#restartRequired,\n refresh: true,\n });\n });\n }\n\n async #headCommit() {\n const commit = (await this.#repository.raw([\"rev-parse\", \"HEAD\"])).trim();\n if (!/^[0-9a-f]{40,64}$/iu.test(commit)) {\n throw new GitRepositoryError(\"malformed-output\", \"head-commit\");\n }\n return commit.toLowerCase();\n }\n\n async #prepareDeploymentBranch(login: string) {\n if (this.#runtime.automaticBranchSwitch === \"disabled\") {\n await assertIdentityBranchReady(this.#repository, login);\n return \"updates-not-synchronized\" as const;\n }\n\n const synchronization = await synchronizeIdentityBranch(this.#repository, {\n identityBranch: login,\n primaryBranch: this.#runtime.primaryBranch,\n remote: this.#runtime.remote,\n managedPaths: this.#runtime.managedRepositoryPaths,\n });\n if (synchronization.status === \"unsafe\") {\n throw new GitRepositoryError(\"conflict\", \"deployment-preparation\");\n }\n await assertIdentityBranchReady(this.#repository, login);\n return synchronization.status === \"deferred\"\n ? (\"updates-not-synchronized\" as const)\n : undefined;\n }\n\n #workspaceRuntime(\n workspacePath: string,\n workspaceRepository: GitRepository,\n ): PrototypeLifecycleRuntimeContext {\n return {\n ...this.#runtime,\n repositoryRoot: workspacePath,\n repositoryRealPath: workspacePath,\n prototypeRoot: join(\n workspacePath,\n relative(this.#runtime.repositoryRoot, this.#runtime.prototypeRoot),\n ),\n gitRepository: workspaceRepository,\n };\n }\n\n async #publishDeploymentMetadata(pending: PendingPublication) {\n return publishDeploymentSource(\n this.#repository,\n {\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n sourceSha: pending.sourceSha,\n commitIdentity: this.#runtime.commitIdentity,\n },\n async ({ path, repository }) => {\n const workspaceRuntime = this.#workspaceRuntime(path, repository);\n const mergedDocument = await diagnoseMetadataStep(\"metadata-read\", () =>\n readMetadataDocument(\n workspaceRuntime,\n pending.ref,\n this.#themeRegistry,\n ),\n );\n await diagnoseMetadataStep(\"metadata-write\", () =>\n writeMetadataDocument(workspaceRuntime, pending.ref, {\n ...mergedDocument,\n previewUrl: pending.previewUrl,\n updatedAt: new Date().toISOString(),\n }),\n );\n await diagnoseMetadataStep(\"metadata-commit\", () =>\n commitPath(\n repository,\n pending.path,\n `deploy: ${pending.ref.slug} (metadata)`,\n ),\n );\n },\n );\n }\n\n async #deploymentValue(\n pending: PendingPublication,\n publicationWarnings: readonly DeploymentWarningCode[] = [],\n ): Promise<DeploymentValue> {\n const warnings = [\n ...(pending.synchronizationWarning === undefined\n ? []\n : [pending.synchronizationWarning]),\n ...publicationWarnings,\n ];\n return {\n deployed: true,\n ref: pending.ref,\n previewUrl: pending.previewUrl,\n deploymentId: pending.deploymentId,\n sourceSha: pending.sourceSha,\n readyState: pending.readyState,\n warning: publicationWarnings[0] ?? pending.synchronizationWarning,\n ...(warnings.length === 0 ? {} : { warnings }),\n };\n }\n\n async deploy(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeploymentValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n if (!this.#deploymentProvider) {\n throw new DeploymentUnavailableError();\n }\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(\n verifiedIdentity,\n normalizedInput,\n );\n const synchronizationWarning = await this.#prepareDeploymentBranch(\n verifiedIdentity.login,\n );\n await assertPrototypeDirectory(this.#runtime, ref);\n const document = await readMetadataDocument(\n this.#runtime,\n ref,\n this.#themeRegistry,\n );\n const metadata = parsePrototypeMetadata(document, {\n themeRegistry: this.#themeRegistry,\n });\n if (metadata.authorLogin !== ref.login) {\n throw new PrototypeNotFoundError();\n }\n\n await commitPath(this.#repository, path, `deploy: ${ref.slug}`);\n const sourceSha = await this.#headCommit();\n const sourcePush = await pushCommitToBranch(\n this.#repository,\n this.#runtime.remote,\n sourceSha,\n verifiedIdentity.login,\n );\n if (sourcePush.remoteStatus !== \"pushed\") {\n throw new DeploymentSourcePushError();\n }\n\n await preflightDeploymentSource(this.#repository, {\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n sourceSha,\n commitIdentity: this.#runtime.commitIdentity,\n });\n\n const deployment = await this.#deploymentProvider!.deploy({\n applicationId: this.#applicationId!,\n login: verifiedIdentity.login,\n });\n if (\n deployment.commitSha.toLowerCase() !== sourceSha ||\n ![\"READY\", \"QUEUED\", \"INITIALIZING\", \"BUILDING\"].includes(\n deployment.readyState,\n )\n ) {\n throw new DeploymentNotAcceptedError();\n }\n const previewUrl = new URL(\n getPrototypeUrl(ref).slice(1),\n `${deployment.url.replace(/\\/+$/, \"\")}/`,\n ).toString();\n const pending: PendingPublication = {\n identity: verifiedIdentity,\n input: normalizedInput,\n ref,\n path,\n previewUrl,\n deploymentId: deployment.deploymentId,\n sourceSha,\n readyState: deployment.readyState,\n synchronizationWarning,\n };\n\n try {\n const publication = await this.#publishDeploymentMetadata(pending);\n this.#pendingPublication = undefined;\n return this.#deploymentValue(\n pending,\n publication.cleanupFailed ? [\"publication-cleanup-failed\"] : [],\n );\n } catch (error) {\n // Keep the accepted deployment retryable for every publication\n // failure, including metadata preparation failures.\n this.#pendingPublication = pending;\n return this.#deploymentValue(pending, [\n error instanceof DeploymentWorktreeError &&\n error.kind !== \"cleanup-failure\"\n ? \"primary-merge-failed\"\n : \"metadata-persistence-failed\",\n ...(error instanceof DeploymentWorktreeError &&\n error.cleanupFailed\n ? [\"publication-cleanup-failed\" as const]\n : []),\n ]);\n }\n },\n );\n });\n }\n\n async retryPublish(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeploymentValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n const pending = this.#pendingPublication;\n if (\n pending === undefined ||\n pending.identity.login !== verifiedIdentity.login ||\n pending.ref.login !== normalizedInput.ref.login ||\n pending.ref.slug !== normalizedInput.ref.slug\n ) {\n throw new DeploymentUnavailableError();\n }\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await assertIdentityBranchReady(\n this.#repository,\n verifiedIdentity.login,\n );\n const publication = await this.#publishDeploymentMetadata(pending);\n this.#pendingPublication = undefined;\n return this.#deploymentValue(\n pending,\n publication.cleanupFailed ? [\"publication-cleanup-failed\"] : [],\n );\n },\n );\n });\n }\n\n async save(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<SavePrototypeValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const { ref, path } = await this.#ownedPrototype(verifiedIdentity, input);\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await this.#prepareMutationBranch(verifiedIdentity.login);\n await commitPath(this.#repository, path, `save: ${ref.slug}`);\n await pushBranch(\n this.#repository,\n this.#runtime.remote,\n verifiedIdentity.login,\n );\n return { saved: true } satisfies SavePrototypeValue;\n },\n );\n });\n }\n\n async discard(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DiscardPrototypeValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const { path } = await this.#ownedPrototype(verifiedIdentity, input);\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await discardPath(this.#repository, path);\n return { discarded: true } satisfies DiscardPrototypeValue;\n },\n );\n });\n }\n\n async duplicate(\n identity: BonfireIdentity,\n input: DuplicatePrototypeInput,\n ): Promise<LifecycleResult<DuplicatePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseDuplicatePrototypeInput(input);\n if (!(await this.#catalog.getSummary(normalizedInput.source))) {\n throw new PrototypeDuplicateError(\"source-not-found\");\n }\n const ref = destinationRef(verifiedIdentity, normalizedInput.title);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (await this.#catalog.getSummary(ref)) {\n throw new PrototypeDuplicateError(\"collision\");\n }\n\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n const prepared = await duplicatePrototype({\n paths: this.#runtime,\n source: normalizedInput.source,\n destination: ref,\n title: normalizedInput.title,\n description: normalizedInput.description,\n authorName: verifiedIdentity.name,\n themeRegistry: this.#themeRegistry,\n });\n await this.#prepareDestination(\n prepared.receipt,\n prepared.expectedFiles,\n {\n operation: \"duplicate\",\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n prototypePath: prepared.destination,\n ref,\n source: normalizedInput.source,\n identity: verifiedIdentity,\n title: normalizedInput.title,\n description: normalizedInput.description,\n },\n );\n if (branch === undefined) {\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: \"manual\",\n } satisfies DuplicatePrototypeValue;\n }\n\n await commitPath(\n this.#repository,\n repositoryRelativePath(this.#runtime, ref),\n `init: ${ref.slug}`,\n );\n const push = await pushBranch(\n this.#repository,\n this.#runtime.remote,\n branch,\n );\n\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: push.remoteStatus,\n } satisfies DuplicatePrototypeValue;\n },\n );\n });\n }\n}\n"]}
|
|
@@ -13,7 +13,7 @@ import { RepositoryBusyError, withRepositoryOperation, } from "./operation-gate.
|
|
|
13
13
|
import { DeploymentProviderError, } from "../deployment-provider.js";
|
|
14
14
|
import { assertIdentityBranchReady, synchronizeIdentityBranch, } from "./repository-synchronization.js";
|
|
15
15
|
import { getRepositorySummary } from "./repository-summary.js";
|
|
16
|
-
import { DeploymentWorktreeError, preflightDeploymentSource, publishDeploymentSource, } from "./deployment-worktree.js";
|
|
16
|
+
import { DeploymentWorktreeError, preflightDeploymentSource, publishDeploymentSource, reportDeploymentIssue, } from "./deployment-worktree.js";
|
|
17
17
|
function repositoryRelativePath(runtime, ref) {
|
|
18
18
|
const absolutePath = join(runtime.prototypeRoot, ref.login, ref.slug);
|
|
19
19
|
return relative(runtime.repositoryRoot, absolutePath).split(sep).join("/");
|
|
@@ -175,6 +175,15 @@ function resultFrom(operation) {
|
|
|
175
175
|
.then(lifecycleSuccess)
|
|
176
176
|
.catch((error) => lifecycleFailure(mapError(error)));
|
|
177
177
|
}
|
|
178
|
+
async function diagnoseMetadataStep(step, operation) {
|
|
179
|
+
try {
|
|
180
|
+
return await operation();
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
reportDeploymentIssue("publication", step, error);
|
|
184
|
+
throw error;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
178
187
|
export class LocalPrototypeLifecycle {
|
|
179
188
|
#runtime;
|
|
180
189
|
gitMode;
|
|
@@ -188,7 +197,8 @@ export class LocalPrototypeLifecycle {
|
|
|
188
197
|
#pendingPublication;
|
|
189
198
|
constructor(options) {
|
|
190
199
|
const internalOptions = options;
|
|
191
|
-
const runtime = internalOptions.runtime ??
|
|
200
|
+
const runtime = internalOptions.runtime ??
|
|
201
|
+
createPrototypeLifecycleRuntime(internalOptions);
|
|
192
202
|
this.#runtime = {
|
|
193
203
|
...runtime,
|
|
194
204
|
gitMode: parseGitModeSetting(runtime.gitMode),
|
|
@@ -491,13 +501,13 @@ export class LocalPrototypeLifecycle {
|
|
|
491
501
|
commitIdentity: this.#runtime.commitIdentity,
|
|
492
502
|
}, async ({ path, repository }) => {
|
|
493
503
|
const workspaceRuntime = this.#workspaceRuntime(path, repository);
|
|
494
|
-
const mergedDocument = await readMetadataDocument(workspaceRuntime, pending.ref, this.#themeRegistry);
|
|
495
|
-
await writeMetadataDocument(workspaceRuntime, pending.ref, {
|
|
504
|
+
const mergedDocument = await diagnoseMetadataStep("metadata-read", () => readMetadataDocument(workspaceRuntime, pending.ref, this.#themeRegistry));
|
|
505
|
+
await diagnoseMetadataStep("metadata-write", () => writeMetadataDocument(workspaceRuntime, pending.ref, {
|
|
496
506
|
...mergedDocument,
|
|
497
507
|
previewUrl: pending.previewUrl,
|
|
498
508
|
updatedAt: new Date().toISOString(),
|
|
499
|
-
});
|
|
500
|
-
await commitPath(repository, pending.path, `deploy: ${pending.ref.slug} (metadata)`);
|
|
509
|
+
}));
|
|
510
|
+
await diagnoseMetadataStep("metadata-commit", () => commitPath(repository, pending.path, `deploy: ${pending.ref.slug} (metadata)`));
|
|
501
511
|
});
|
|
502
512
|
}
|
|
503
513
|
async #deploymentValue(pending, publicationWarnings = []) {
|
|
@@ -574,9 +584,7 @@ export class LocalPrototypeLifecycle {
|
|
|
574
584
|
try {
|
|
575
585
|
const publication = await this.#publishDeploymentMetadata(pending);
|
|
576
586
|
this.#pendingPublication = undefined;
|
|
577
|
-
return this.#deploymentValue(pending, publication.cleanupFailed
|
|
578
|
-
? ["publication-cleanup-failed"]
|
|
579
|
-
: []);
|
|
587
|
+
return this.#deploymentValue(pending, publication.cleanupFailed ? ["publication-cleanup-failed"] : []);
|
|
580
588
|
}
|
|
581
589
|
catch (error) {
|
|
582
590
|
// Keep the accepted deployment retryable for every publication
|
|
@@ -587,7 +595,8 @@ export class LocalPrototypeLifecycle {
|
|
|
587
595
|
error.kind !== "cleanup-failure"
|
|
588
596
|
? "primary-merge-failed"
|
|
589
597
|
: "metadata-persistence-failed",
|
|
590
|
-
...(error instanceof DeploymentWorktreeError &&
|
|
598
|
+
...(error instanceof DeploymentWorktreeError &&
|
|
599
|
+
error.cleanupFailed
|
|
591
600
|
? ["publication-cleanup-failed"]
|
|
592
601
|
: []),
|
|
593
602
|
]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-prototype-lifecycle.js","sourceRoot":"","sources":["../../src/lifecycle/local-prototype-lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["import { constants } from \"node:fs\";\nimport { lstat, open, rename, rm, writeFile } from \"node:fs/promises\";\nimport { randomUUID } from \"node:crypto\";\nimport { join, relative, sep } from \"node:path\";\nimport {\n definePrototypeRef,\n getPrototypeUrl,\n LifecycleInputError,\n lifecycleFailure,\n lifecycleSuccess,\n parseCreatePrototypeInput,\n parseDuplicatePrototypeInput,\n parseEditPrototypeInput,\n parseBonfireIdentity,\n parsePrototypeCommandInput,\n parsePrototypeMetadata,\n slugifyPrototypeTitle,\n PrototypeRefError,\n type BranchPreparationValue,\n type CreatePrototypeInput,\n type CreatePrototypeValue,\n type DeletePrototypeValue,\n type DeploymentValue,\n type DeploymentWarningCode,\n type DirtyObservationValue,\n type DiscardPrototypeValue,\n type DuplicatePrototypeInput,\n type DuplicatePrototypeValue,\n type EditPrototypeInput,\n type EditPrototypeValue,\n parseGitModeSetting,\n type GitMode,\n type LifecycleErrorCode,\n type LifecycleResult,\n type BonfireIdentity,\n type PrototypeCommandInput,\n type SavePrototypeValue,\n type RepositorySummary,\n type ThemeRegistry,\n} from \"@ebay-dt/bonfire-core\";\nimport {\n commitPath,\n discardPath,\n GitRepositoryError,\n pushBranch,\n pushCommitToBranch,\n type GitRepository,\n} from \"../git-repository.js\";\nimport { duplicatePrototype, PrototypeDuplicateError } from \"../duplicate.js\";\nimport {\n PrototypeCatalog,\n type PrototypeCatalogLike,\n} from \"../prototype-catalog.js\";\nimport { PrototypeScaffoldError, scaffoldPrototype } from \"../scaffold.js\";\nimport {\n createPrototypeLifecycleRuntime,\n type PrototypeLifecycleRuntimeContext,\n} from \"./runtime-context.js\";\nimport type {\n PreparePrototypeFiles,\n PreparePrototypeFilesRequest,\n} from \"../prepare-prototype-files.js\";\nimport {\n cleanupOwnedDestination,\n validateOwnedDestination,\n type OwnedDestinationReceipt,\n} from \"../internal/owned-destination.js\";\nimport {\n RepositoryBusyError,\n withRepositoryOperation,\n} from \"./operation-gate.js\";\nimport {\n DeploymentProviderError,\n type DeploymentProvider,\n} from \"../deployment-provider.js\";\nimport {\n assertIdentityBranchReady,\n synchronizeIdentityBranch,\n} from \"./repository-synchronization.js\";\nimport { getRepositorySummary } from \"./repository-summary.js\";\nimport {\n DeploymentWorktreeError,\n preflightDeploymentSource,\n publishDeploymentSource,\n} from \"./deployment-worktree.js\";\n\ntype LifecycleMethod<Input, Value> = (\n identity: BonfireIdentity,\n input: Input,\n) => Promise<LifecycleResult<Value>>;\n\nexport type PrototypeLifecycle = {\n /** Declares whether this lifecycle enforces the selected Git mode. */\n readonly gitMode?: GitMode;\n prepareBranch(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<BranchPreparationValue>>;\n create: LifecycleMethod<CreatePrototypeInput, CreatePrototypeValue>;\n duplicate: LifecycleMethod<DuplicatePrototypeInput, DuplicatePrototypeValue>;\n edit: LifecycleMethod<EditPrototypeInput, EditPrototypeValue>;\n delete: LifecycleMethod<PrototypeCommandInput, DeletePrototypeValue>;\n getDirtyState: LifecycleMethod<PrototypeCommandInput, DirtyObservationValue>;\n getRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>>;\n checkRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>>;\n save: LifecycleMethod<PrototypeCommandInput, SavePrototypeValue>;\n discard: LifecycleMethod<PrototypeCommandInput, DiscardPrototypeValue>;\n deploy: LifecycleMethod<PrototypeCommandInput, DeploymentValue>;\n retryPublish?: LifecycleMethod<PrototypeCommandInput, DeploymentValue>;\n};\n\nexport type LocalPrototypeLifecycleOptions = {\n repositoryRoot: string;\n prototypeRoot?: string;\n remote?: string;\n primaryBranch?: string;\n automaticBranchSwitch?: \"enabled\" | \"disabled\";\n gitMode?: GitMode;\n commitIdentity?: {\n name: string;\n email: string;\n };\n catalog?: PrototypeCatalogLike;\n preparePrototypeFiles?: PreparePrototypeFiles;\n deploymentProvider?: DeploymentProvider;\n applicationId?: string;\n themeRegistry?: ThemeRegistry;\n};\n\ntype InternalLocalPrototypeLifecycleOptions = LocalPrototypeLifecycleOptions & {\n runtime?: PrototypeLifecycleRuntimeContext;\n};\n\nfunction repositoryRelativePath(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n const absolutePath = join(runtime.prototypeRoot, ref.login, ref.slug);\n return relative(runtime.repositoryRoot, absolutePath).split(sep).join(\"/\");\n}\n\nfunction destinationRef(identity: BonfireIdentity, title: string) {\n const slug = slugifyPrototypeTitle(title);\n if (slug === null)\n throw new PrototypeRefError(\"title\", \"must produce a slug\");\n return definePrototypeRef({ login: identity.login, slug });\n}\n\nclass PrototypeNotFoundError extends Error {}\nclass PrototypeOwnershipError extends Error {}\nclass PrototypeMetadataFileError extends Error {}\nclass DeploymentUnavailableError extends Error {}\nclass DeploymentSourcePushError extends Error {}\nclass DeploymentNotAcceptedError extends Error {}\n\ntype PendingPublication = {\n identity: BonfireIdentity;\n input: PrototypeCommandInput;\n ref: { login: string; slug: string };\n path: string;\n previewUrl: string;\n deploymentId: string;\n sourceSha: string;\n readyState: DeploymentValue[\"readyState\"];\n synchronizationWarning: DeploymentWarningCode | undefined;\n};\n\nfunction metadataPath(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n return join(runtime.prototypeRoot, ref.login, ref.slug, \"metadata.json\");\n}\n\nasync function assertPrototypeDirectory(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n const paths = [\n runtime.prototypeRoot,\n join(runtime.prototypeRoot, ref.login),\n join(runtime.prototypeRoot, ref.login, ref.slug),\n ];\n\n try {\n for (const path of paths) {\n const stats = await lstat(path);\n if (stats.isSymbolicLink() || !stats.isDirectory()) {\n throw new PrototypeMetadataFileError();\n }\n }\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function readMetadataDocument(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n themeRegistry?: ThemeRegistry,\n) {\n const path = metadataPath(runtime, ref);\n try {\n const file = await open(\n path,\n constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0),\n );\n try {\n const source = await file.readFile(\"utf8\");\n const document: unknown = JSON.parse(source);\n parsePrototypeMetadata(document, { themeRegistry });\n if (\n typeof document !== \"object\" ||\n document === null ||\n Array.isArray(document)\n ) {\n throw new PrototypeMetadataFileError();\n }\n return { ...(document as Record<string, unknown>) };\n } finally {\n await file.close();\n }\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function removePrototypeDirectory(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n await assertPrototypeDirectory(runtime, ref);\n try {\n await rm(join(runtime.prototypeRoot, ref.login, ref.slug), {\n force: false,\n recursive: true,\n });\n } catch {\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function writeMetadataDocument(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n document: Record<string, unknown>,\n) {\n const path = metadataPath(runtime, ref);\n const temporaryPath = `${path}.${randomUUID()}.tmp`;\n\n try {\n const stats = await lstat(path);\n if (stats.isSymbolicLink() || !stats.isFile()) {\n throw new PrototypeMetadataFileError();\n }\n await writeFile(temporaryPath, `${JSON.stringify(document, null, 2)}\\n`, {\n encoding: \"utf8\",\n flag: \"wx\",\n });\n await rename(temporaryPath, path);\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n } finally {\n await rm(temporaryPath, { force: true }).catch(() => undefined);\n }\n}\n\nfunction mapError(error: unknown): LifecycleErrorCode {\n if (error instanceof PrototypeNotFoundError) return \"not-found\";\n if (error instanceof PrototypeOwnershipError) return \"forbidden\";\n if (error instanceof PrototypeMetadataFileError) return \"filesystem-failure\";\n if (error instanceof RepositoryBusyError) return \"repository-busy\";\n if (\n error instanceof PrototypeRefError ||\n error instanceof LifecycleInputError\n ) {\n return \"invalid-input\";\n }\n if (error instanceof GitRepositoryError) {\n if (error.kind === \"timeout\") return \"timeout\";\n if (error.kind === \"conflict\") return \"working-tree-conflict\";\n if (error.kind === \"invalid-configuration\") return \"unexpected-failure\";\n return \"git-failure\";\n }\n if (error instanceof PrototypeScaffoldError) {\n if (error.kind === \"collision\") return \"already-exists\";\n if (error.kind === \"invalid-input\") return \"invalid-input\";\n return \"filesystem-failure\";\n }\n if (error instanceof PrototypeDuplicateError) {\n if (error.kind === \"source-not-found\") return \"not-found\";\n if (error.kind === \"collision\") return \"already-exists\";\n if (error.kind === \"invalid-source\") return \"invalid-input\";\n return \"filesystem-failure\";\n }\n if (error instanceof DeploymentUnavailableError) {\n return \"deployment-unavailable\";\n }\n if (\n error instanceof DeploymentSourcePushError ||\n error instanceof DeploymentNotAcceptedError ||\n error instanceof DeploymentWorktreeError\n ) {\n return \"deployment-failure\";\n }\n if (error instanceof DeploymentProviderError) {\n return error.kind === \"timeout\" ? \"timeout\" : \"deployment-failure\";\n }\n return \"unexpected-failure\";\n}\n\nfunction resultFrom<Value>(\n operation: () => Promise<Value>,\n): Promise<LifecycleResult<Value>> {\n return operation()\n .then(lifecycleSuccess)\n .catch((error: unknown) => lifecycleFailure(mapError(error)));\n}\n\nexport class LocalPrototypeLifecycle implements PrototypeLifecycle {\n readonly #runtime: PrototypeLifecycleRuntimeContext;\n readonly gitMode: GitMode;\n readonly #catalog: PrototypeCatalogLike;\n readonly #repository: GitRepository;\n readonly #preparePrototypeFiles: PreparePrototypeFiles;\n readonly #deploymentProvider?: DeploymentProvider;\n readonly #applicationId?: string;\n readonly #themeRegistry?: ThemeRegistry;\n #restartRequired = false;\n #pendingPublication?: PendingPublication;\n\n constructor(options: LocalPrototypeLifecycleOptions) {\n const internalOptions = options as InternalLocalPrototypeLifecycleOptions;\n const runtime =\n internalOptions.runtime ?? createPrototypeLifecycleRuntime(internalOptions);\n this.#runtime = {\n ...runtime,\n gitMode: parseGitModeSetting(runtime.gitMode),\n };\n this.gitMode = this.#runtime.gitMode;\n this.#themeRegistry = options.themeRegistry;\n this.#catalog =\n internalOptions.catalog ??\n new PrototypeCatalog({\n ...this.#runtime,\n themeRegistry: this.#themeRegistry,\n });\n this.#repository = this.#runtime.gitRepository;\n this.#preparePrototypeFiles =\n internalOptions.preparePrototypeFiles ?? (() => undefined);\n this.#deploymentProvider = internalOptions.deploymentProvider;\n this.#applicationId = internalOptions.applicationId;\n if (\n this.#deploymentProvider &&\n (typeof this.#applicationId !== \"string\" ||\n this.#applicationId.length === 0)\n ) {\n throw new TypeError(\n \"applicationId must be provided when deploymentProvider is configured\",\n );\n }\n }\n\n async #verifiedIdentity(identity: BonfireIdentity) {\n return parseBonfireIdentity(identity);\n }\n\n async #currentBranch() {\n const branch = await this.#repository.currentBranch();\n if (branch === \"HEAD\" || branch.length === 0) {\n throw new GitRepositoryError(\"conflict\", \"current-branch\");\n }\n return branch;\n }\n\n async #ownedPrototype(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ) {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n if (normalizedInput.ref.login !== verifiedIdentity.login) {\n throw new PrototypeOwnershipError();\n }\n const summary = await this.#catalog.getSummary(normalizedInput.ref);\n if (!summary) {\n throw new PrototypeNotFoundError();\n }\n\n return {\n ref: normalizedInput.ref,\n path: repositoryRelativePath(this.#runtime, normalizedInput.ref),\n summary,\n };\n }\n\n async #prepareMutationBranch(login: string) {\n await assertIdentityBranchReady(this.#repository, login);\n return login;\n }\n\n async #prepareDestination(\n receipt: OwnedDestinationReceipt,\n expectedFiles: readonly string[],\n request: PreparePrototypeFilesRequest,\n ) {\n try {\n const frozenRequest = Object.freeze({\n ...request,\n ref: Object.freeze({ ...request.ref }),\n identity: Object.freeze({ ...request.identity }),\n ...(request.operation === \"duplicate\"\n ? { source: Object.freeze({ ...request.source }) }\n : {}),\n }) as PreparePrototypeFilesRequest;\n await this.#preparePrototypeFiles(frozenRequest);\n await validateOwnedDestination(\n receipt,\n expectedFiles,\n request.ref,\n this.#themeRegistry,\n );\n } catch {\n try {\n await cleanupOwnedDestination(receipt);\n } catch {\n // The failure remains a finite filesystem failure without retrying cleanup.\n }\n throw new PrototypeScaffoldError(\"filesystem\");\n }\n }\n\n async prepareBranch(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<BranchPreparationValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (\n this.#runtime.gitMode === \"manual\" ||\n this.#runtime.automaticBranchSwitch === \"disabled\"\n ) {\n await this.#currentBranch();\n return { status: \"disabled\" } satisfies BranchPreparationValue;\n }\n\n const synchronization = await synchronizeIdentityBranch(\n this.#repository,\n {\n identityBranch: verifiedIdentity.login,\n primaryBranch: this.#runtime.primaryBranch,\n remote: this.#runtime.remote,\n managedPaths: this.#runtime.managedRepositoryPaths,\n },\n );\n if (synchronization.status !== \"synchronized\") {\n return {\n status: synchronization.status,\n reason: synchronization.reason,\n ...(synchronization.affectedPaths.length > 0\n ? { affectedPaths: synchronization.affectedPaths }\n : {}),\n } satisfies BranchPreparationValue;\n }\n if (\n synchronization.affectedPaths.some((path) =>\n this.#runtime.managedRepositoryPaths.includes(path),\n )\n ) {\n this.#restartRequired = true;\n }\n return {\n status: \"ready\",\n ...(synchronization.affectedPaths.length > 0\n ? { affectedPaths: synchronization.affectedPaths }\n : {}),\n } satisfies BranchPreparationValue;\n },\n );\n });\n }\n\n async create(\n identity: BonfireIdentity,\n input: CreatePrototypeInput,\n ): Promise<LifecycleResult<CreatePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseCreatePrototypeInput(\n input,\n this.#themeRegistry,\n );\n const ref = destinationRef(verifiedIdentity, normalizedInput.title);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (await this.#catalog.getSummary(ref)) {\n throw new PrototypeScaffoldError(\"collision\");\n }\n\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n const prepared = await scaffoldPrototype({\n paths: this.#runtime,\n ref,\n title: normalizedInput.title,\n description: normalizedInput.description,\n authorName: verifiedIdentity.name,\n theme: normalizedInput.theme,\n themeRegistry: this.#themeRegistry,\n });\n await this.#prepareDestination(\n prepared.receipt,\n prepared.expectedFiles,\n {\n operation: \"create\",\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n prototypePath: prepared.destination,\n ref,\n identity: verifiedIdentity,\n title: normalizedInput.title,\n description: normalizedInput.description,\n },\n );\n if (branch === undefined) {\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: \"manual\",\n } satisfies CreatePrototypeValue;\n }\n\n await commitPath(\n this.#repository,\n repositoryRelativePath(this.#runtime, ref),\n `init: ${ref.slug}`,\n );\n const push = await pushBranch(\n this.#repository,\n this.#runtime.remote,\n branch,\n );\n\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: push.remoteStatus,\n } satisfies CreatePrototypeValue;\n },\n );\n });\n }\n\n async edit(\n identity: BonfireIdentity,\n input: EditPrototypeInput,\n ): Promise<LifecycleResult<EditPrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseEditPrototypeInput(\n input,\n this.#themeRegistry,\n );\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(verifiedIdentity, {\n ref: normalizedInput.ref,\n });\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n await assertPrototypeDirectory(this.#runtime, ref);\n const document = await readMetadataDocument(\n this.#runtime,\n ref,\n this.#themeRegistry,\n );\n const metadata = parsePrototypeMetadata(document, {\n themeRegistry: this.#themeRegistry,\n });\n if (metadata.authorLogin !== ref.login) {\n throw new PrototypeNotFoundError();\n }\n\n await writeMetadataDocument(this.#runtime, ref, {\n ...document,\n title: normalizedInput.title,\n description: normalizedInput.description,\n updatedAt: new Date().toISOString(),\n ...(normalizedInput.theme === undefined\n ? {}\n : { theme: normalizedInput.theme }),\n });\n if (branch !== undefined) {\n await commitPath(this.#repository, path, `edit: ${ref.slug}`);\n await pushBranch(this.#repository, this.#runtime.remote, branch);\n }\n\n return { edited: true, ref } satisfies EditPrototypeValue;\n },\n );\n });\n }\n\n async delete(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeletePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(\n verifiedIdentity,\n normalizedInput,\n );\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n await removePrototypeDirectory(this.#runtime, ref);\n if (branch !== undefined) {\n await commitPath(this.#repository, path, `delete: ${ref.slug}`);\n await pushBranch(this.#repository, this.#runtime.remote, branch);\n }\n\n return { deleted: true, ref } satisfies DeletePrototypeValue;\n },\n );\n });\n }\n\n async getDirtyState(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DirtyObservationValue>> {\n return resultFrom(async () => {\n const { path, summary } = await this.#ownedPrototype(identity, input);\n const status = await this.#repository.status([path]);\n return {\n status: status.files.length > 0 ? \"dirty\" : \"clean\",\n ...(summary.previewUrl !== undefined\n ? { previewUrl: summary.previewUrl }\n : {}),\n } satisfies DirtyObservationValue;\n });\n }\n\n async getRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return getRepositorySummary({\n repository: this.#repository,\n catalog: this.#catalog,\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n identity: verifiedIdentity,\n mode: this.#runtime.gitMode,\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n restartRequired: this.#restartRequired,\n });\n });\n }\n\n async checkRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return getRepositorySummary({\n repository: this.#repository,\n catalog: this.#catalog,\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n identity: verifiedIdentity,\n mode: this.#runtime.gitMode,\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n restartRequired: this.#restartRequired,\n refresh: true,\n });\n });\n }\n\n async #headCommit() {\n const commit = (await this.#repository.raw([\"rev-parse\", \"HEAD\"])).trim();\n if (!/^[0-9a-f]{40,64}$/iu.test(commit)) {\n throw new GitRepositoryError(\"malformed-output\", \"head-commit\");\n }\n return commit.toLowerCase();\n }\n\n async #prepareDeploymentBranch(login: string) {\n if (this.#runtime.automaticBranchSwitch === \"disabled\") {\n await assertIdentityBranchReady(this.#repository, login);\n return \"updates-not-synchronized\" as const;\n }\n\n const synchronization = await synchronizeIdentityBranch(this.#repository, {\n identityBranch: login,\n primaryBranch: this.#runtime.primaryBranch,\n remote: this.#runtime.remote,\n managedPaths: this.#runtime.managedRepositoryPaths,\n });\n if (synchronization.status === \"unsafe\") {\n throw new GitRepositoryError(\"conflict\", \"deployment-preparation\");\n }\n await assertIdentityBranchReady(this.#repository, login);\n return synchronization.status === \"deferred\"\n ? (\"updates-not-synchronized\" as const)\n : undefined;\n }\n\n #workspaceRuntime(\n workspacePath: string,\n workspaceRepository: GitRepository,\n ): PrototypeLifecycleRuntimeContext {\n return {\n ...this.#runtime,\n repositoryRoot: workspacePath,\n repositoryRealPath: workspacePath,\n prototypeRoot: join(\n workspacePath,\n relative(this.#runtime.repositoryRoot, this.#runtime.prototypeRoot),\n ),\n gitRepository: workspaceRepository,\n };\n }\n\n async #publishDeploymentMetadata(pending: PendingPublication) {\n return publishDeploymentSource(\n this.#repository,\n {\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n sourceSha: pending.sourceSha,\n commitIdentity: this.#runtime.commitIdentity,\n },\n async ({ path, repository }) => {\n const workspaceRuntime = this.#workspaceRuntime(path, repository);\n const mergedDocument = await readMetadataDocument(\n workspaceRuntime,\n pending.ref,\n this.#themeRegistry,\n );\n await writeMetadataDocument(workspaceRuntime, pending.ref, {\n ...mergedDocument,\n previewUrl: pending.previewUrl,\n updatedAt: new Date().toISOString(),\n });\n await commitPath(\n repository,\n pending.path,\n `deploy: ${pending.ref.slug} (metadata)`,\n );\n },\n );\n }\n\n async #deploymentValue(\n pending: PendingPublication,\n publicationWarnings: readonly DeploymentWarningCode[] = [],\n ): Promise<DeploymentValue> {\n const warnings = [\n ...(pending.synchronizationWarning === undefined\n ? []\n : [pending.synchronizationWarning]),\n ...publicationWarnings,\n ];\n return {\n deployed: true,\n ref: pending.ref,\n previewUrl: pending.previewUrl,\n deploymentId: pending.deploymentId,\n sourceSha: pending.sourceSha,\n readyState: pending.readyState,\n warning: publicationWarnings[0] ?? pending.synchronizationWarning,\n ...(warnings.length === 0 ? {} : { warnings }),\n };\n }\n\n async deploy(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeploymentValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n if (!this.#deploymentProvider) {\n throw new DeploymentUnavailableError();\n }\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(\n verifiedIdentity,\n normalizedInput,\n );\n const synchronizationWarning = await this.#prepareDeploymentBranch(\n verifiedIdentity.login,\n );\n await assertPrototypeDirectory(this.#runtime, ref);\n const document = await readMetadataDocument(\n this.#runtime,\n ref,\n this.#themeRegistry,\n );\n const metadata = parsePrototypeMetadata(document, {\n themeRegistry: this.#themeRegistry,\n });\n if (metadata.authorLogin !== ref.login) {\n throw new PrototypeNotFoundError();\n }\n\n await commitPath(this.#repository, path, `deploy: ${ref.slug}`);\n const sourceSha = await this.#headCommit();\n const sourcePush = await pushCommitToBranch(\n this.#repository,\n this.#runtime.remote,\n sourceSha,\n verifiedIdentity.login,\n );\n if (sourcePush.remoteStatus !== \"pushed\") {\n throw new DeploymentSourcePushError();\n }\n\n await preflightDeploymentSource(this.#repository, {\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n sourceSha,\n commitIdentity: this.#runtime.commitIdentity,\n });\n\n const deployment = await this.#deploymentProvider!.deploy({\n applicationId: this.#applicationId!,\n login: verifiedIdentity.login,\n });\n if (\n deployment.commitSha.toLowerCase() !== sourceSha ||\n ![\"READY\", \"QUEUED\", \"INITIALIZING\", \"BUILDING\"].includes(\n deployment.readyState,\n )\n ) {\n throw new DeploymentNotAcceptedError();\n }\n const previewUrl = new URL(\n getPrototypeUrl(ref).slice(1),\n `${deployment.url.replace(/\\/+$/, \"\")}/`,\n ).toString();\n const pending: PendingPublication = {\n identity: verifiedIdentity,\n input: normalizedInput,\n ref,\n path,\n previewUrl,\n deploymentId: deployment.deploymentId,\n sourceSha,\n readyState: deployment.readyState,\n synchronizationWarning,\n };\n\n try {\n const publication = await this.#publishDeploymentMetadata(pending);\n this.#pendingPublication = undefined;\n return this.#deploymentValue(\n pending,\n publication.cleanupFailed\n ? [\"publication-cleanup-failed\"]\n : [],\n );\n } catch (error) {\n // Keep the accepted deployment retryable for every publication\n // failure, including metadata preparation failures.\n this.#pendingPublication = pending;\n return this.#deploymentValue(pending, [\n error instanceof DeploymentWorktreeError &&\n error.kind !== \"cleanup-failure\"\n ? \"primary-merge-failed\"\n : \"metadata-persistence-failed\",\n ...(error instanceof DeploymentWorktreeError && error.cleanupFailed\n ? [\"publication-cleanup-failed\" as const]\n : []),\n ]);\n }\n },\n );\n });\n }\n\n async retryPublish(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeploymentValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n const pending = this.#pendingPublication;\n if (\n pending === undefined ||\n pending.identity.login !== verifiedIdentity.login ||\n pending.ref.login !== normalizedInput.ref.login ||\n pending.ref.slug !== normalizedInput.ref.slug\n ) {\n throw new DeploymentUnavailableError();\n }\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await assertIdentityBranchReady(this.#repository, verifiedIdentity.login);\n const publication = await this.#publishDeploymentMetadata(pending);\n this.#pendingPublication = undefined;\n return this.#deploymentValue(\n pending,\n publication.cleanupFailed ? [\"publication-cleanup-failed\"] : [],\n );\n },\n );\n });\n }\n\n async save(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<SavePrototypeValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const { ref, path } = await this.#ownedPrototype(\n verifiedIdentity,\n input,\n );\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await this.#prepareMutationBranch(verifiedIdentity.login);\n await commitPath(this.#repository, path, `save: ${ref.slug}`);\n await pushBranch(\n this.#repository,\n this.#runtime.remote,\n verifiedIdentity.login,\n );\n return { saved: true } satisfies SavePrototypeValue;\n },\n );\n });\n }\n\n async discard(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DiscardPrototypeValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const { path } = await this.#ownedPrototype(\n verifiedIdentity,\n input,\n );\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await discardPath(this.#repository, path);\n return { discarded: true } satisfies DiscardPrototypeValue;\n },\n );\n });\n }\n\n async duplicate(\n identity: BonfireIdentity,\n input: DuplicatePrototypeInput,\n ): Promise<LifecycleResult<DuplicatePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseDuplicatePrototypeInput(input);\n if (!(await this.#catalog.getSummary(normalizedInput.source))) {\n throw new PrototypeDuplicateError(\"source-not-found\");\n }\n const ref = destinationRef(verifiedIdentity, normalizedInput.title);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (await this.#catalog.getSummary(ref)) {\n throw new PrototypeDuplicateError(\"collision\");\n }\n\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n const prepared = await duplicatePrototype({\n paths: this.#runtime,\n source: normalizedInput.source,\n destination: ref,\n title: normalizedInput.title,\n description: normalizedInput.description,\n authorName: verifiedIdentity.name,\n themeRegistry: this.#themeRegistry,\n });\n await this.#prepareDestination(\n prepared.receipt,\n prepared.expectedFiles,\n {\n operation: \"duplicate\",\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n prototypePath: prepared.destination,\n ref,\n source: normalizedInput.source,\n identity: verifiedIdentity,\n title: normalizedInput.title,\n description: normalizedInput.description,\n },\n );\n if (branch === undefined) {\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: \"manual\",\n } satisfies DuplicatePrototypeValue;\n }\n\n await commitPath(\n this.#repository,\n repositoryRelativePath(this.#runtime, ref),\n `init: ${ref.slug}`,\n );\n const push = await pushBranch(\n this.#repository,\n this.#runtime.remote,\n branch,\n );\n\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: push.remoteStatus,\n } satisfies DuplicatePrototypeValue;\n },\n );\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"local-prototype-lifecycle.js","sourceRoot":"","sources":["../../src/lifecycle/local-prototype-lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["import { constants } from \"node:fs\";\nimport { lstat, open, rename, rm, writeFile } from \"node:fs/promises\";\nimport { randomUUID } from \"node:crypto\";\nimport { join, relative, sep } from \"node:path\";\nimport {\n definePrototypeRef,\n getPrototypeUrl,\n LifecycleInputError,\n lifecycleFailure,\n lifecycleSuccess,\n parseCreatePrototypeInput,\n parseDuplicatePrototypeInput,\n parseEditPrototypeInput,\n parseBonfireIdentity,\n parsePrototypeCommandInput,\n parsePrototypeMetadata,\n slugifyPrototypeTitle,\n PrototypeRefError,\n type BranchPreparationValue,\n type CreatePrototypeInput,\n type CreatePrototypeValue,\n type DeletePrototypeValue,\n type DeploymentValue,\n type DeploymentWarningCode,\n type DirtyObservationValue,\n type DiscardPrototypeValue,\n type DuplicatePrototypeInput,\n type DuplicatePrototypeValue,\n type EditPrototypeInput,\n type EditPrototypeValue,\n parseGitModeSetting,\n type GitMode,\n type LifecycleErrorCode,\n type LifecycleResult,\n type BonfireIdentity,\n type PrototypeCommandInput,\n type SavePrototypeValue,\n type RepositorySummary,\n type ThemeRegistry,\n} from \"@ebay-dt/bonfire-core\";\nimport {\n commitPath,\n discardPath,\n GitRepositoryError,\n pushBranch,\n pushCommitToBranch,\n type GitRepository,\n} from \"../git-repository.js\";\nimport { duplicatePrototype, PrototypeDuplicateError } from \"../duplicate.js\";\nimport {\n PrototypeCatalog,\n type PrototypeCatalogLike,\n} from \"../prototype-catalog.js\";\nimport { PrototypeScaffoldError, scaffoldPrototype } from \"../scaffold.js\";\nimport {\n createPrototypeLifecycleRuntime,\n type PrototypeLifecycleRuntimeContext,\n} from \"./runtime-context.js\";\nimport type {\n PreparePrototypeFiles,\n PreparePrototypeFilesRequest,\n} from \"../prepare-prototype-files.js\";\nimport {\n cleanupOwnedDestination,\n validateOwnedDestination,\n type OwnedDestinationReceipt,\n} from \"../internal/owned-destination.js\";\nimport {\n RepositoryBusyError,\n withRepositoryOperation,\n} from \"./operation-gate.js\";\nimport {\n DeploymentProviderError,\n type DeploymentProvider,\n} from \"../deployment-provider.js\";\nimport {\n assertIdentityBranchReady,\n synchronizeIdentityBranch,\n} from \"./repository-synchronization.js\";\nimport { getRepositorySummary } from \"./repository-summary.js\";\nimport {\n DeploymentWorktreeError,\n preflightDeploymentSource,\n publishDeploymentSource,\n reportDeploymentIssue,\n} from \"./deployment-worktree.js\";\n\ntype LifecycleMethod<Input, Value> = (\n identity: BonfireIdentity,\n input: Input,\n) => Promise<LifecycleResult<Value>>;\n\nexport type PrototypeLifecycle = {\n /** Declares whether this lifecycle enforces the selected Git mode. */\n readonly gitMode?: GitMode;\n prepareBranch(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<BranchPreparationValue>>;\n create: LifecycleMethod<CreatePrototypeInput, CreatePrototypeValue>;\n duplicate: LifecycleMethod<DuplicatePrototypeInput, DuplicatePrototypeValue>;\n edit: LifecycleMethod<EditPrototypeInput, EditPrototypeValue>;\n delete: LifecycleMethod<PrototypeCommandInput, DeletePrototypeValue>;\n getDirtyState: LifecycleMethod<PrototypeCommandInput, DirtyObservationValue>;\n getRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>>;\n checkRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>>;\n save: LifecycleMethod<PrototypeCommandInput, SavePrototypeValue>;\n discard: LifecycleMethod<PrototypeCommandInput, DiscardPrototypeValue>;\n deploy: LifecycleMethod<PrototypeCommandInput, DeploymentValue>;\n retryPublish?: LifecycleMethod<PrototypeCommandInput, DeploymentValue>;\n};\n\nexport type LocalPrototypeLifecycleOptions = {\n repositoryRoot: string;\n prototypeRoot?: string;\n remote?: string;\n primaryBranch?: string;\n automaticBranchSwitch?: \"enabled\" | \"disabled\";\n gitMode?: GitMode;\n commitIdentity?: {\n name: string;\n email: string;\n };\n catalog?: PrototypeCatalogLike;\n preparePrototypeFiles?: PreparePrototypeFiles;\n deploymentProvider?: DeploymentProvider;\n applicationId?: string;\n themeRegistry?: ThemeRegistry;\n};\n\ntype InternalLocalPrototypeLifecycleOptions = LocalPrototypeLifecycleOptions & {\n runtime?: PrototypeLifecycleRuntimeContext;\n};\n\nfunction repositoryRelativePath(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n const absolutePath = join(runtime.prototypeRoot, ref.login, ref.slug);\n return relative(runtime.repositoryRoot, absolutePath).split(sep).join(\"/\");\n}\n\nfunction destinationRef(identity: BonfireIdentity, title: string) {\n const slug = slugifyPrototypeTitle(title);\n if (slug === null)\n throw new PrototypeRefError(\"title\", \"must produce a slug\");\n return definePrototypeRef({ login: identity.login, slug });\n}\n\nclass PrototypeNotFoundError extends Error {}\nclass PrototypeOwnershipError extends Error {}\nclass PrototypeMetadataFileError extends Error {}\nclass DeploymentUnavailableError extends Error {}\nclass DeploymentSourcePushError extends Error {}\nclass DeploymentNotAcceptedError extends Error {}\n\ntype PendingPublication = {\n identity: BonfireIdentity;\n input: PrototypeCommandInput;\n ref: { login: string; slug: string };\n path: string;\n previewUrl: string;\n deploymentId: string;\n sourceSha: string;\n readyState: DeploymentValue[\"readyState\"];\n synchronizationWarning: DeploymentWarningCode | undefined;\n};\n\nfunction metadataPath(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n return join(runtime.prototypeRoot, ref.login, ref.slug, \"metadata.json\");\n}\n\nasync function assertPrototypeDirectory(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n const paths = [\n runtime.prototypeRoot,\n join(runtime.prototypeRoot, ref.login),\n join(runtime.prototypeRoot, ref.login, ref.slug),\n ];\n\n try {\n for (const path of paths) {\n const stats = await lstat(path);\n if (stats.isSymbolicLink() || !stats.isDirectory()) {\n throw new PrototypeMetadataFileError();\n }\n }\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function readMetadataDocument(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n themeRegistry?: ThemeRegistry,\n) {\n const path = metadataPath(runtime, ref);\n try {\n const file = await open(\n path,\n constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0),\n );\n try {\n const source = await file.readFile(\"utf8\");\n const document: unknown = JSON.parse(source);\n parsePrototypeMetadata(document, { themeRegistry });\n if (\n typeof document !== \"object\" ||\n document === null ||\n Array.isArray(document)\n ) {\n throw new PrototypeMetadataFileError();\n }\n return { ...(document as Record<string, unknown>) };\n } finally {\n await file.close();\n }\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function removePrototypeDirectory(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n await assertPrototypeDirectory(runtime, ref);\n try {\n await rm(join(runtime.prototypeRoot, ref.login, ref.slug), {\n force: false,\n recursive: true,\n });\n } catch {\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function writeMetadataDocument(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n document: Record<string, unknown>,\n) {\n const path = metadataPath(runtime, ref);\n const temporaryPath = `${path}.${randomUUID()}.tmp`;\n\n try {\n const stats = await lstat(path);\n if (stats.isSymbolicLink() || !stats.isFile()) {\n throw new PrototypeMetadataFileError();\n }\n await writeFile(temporaryPath, `${JSON.stringify(document, null, 2)}\\n`, {\n encoding: \"utf8\",\n flag: \"wx\",\n });\n await rename(temporaryPath, path);\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n } finally {\n await rm(temporaryPath, { force: true }).catch(() => undefined);\n }\n}\n\nfunction mapError(error: unknown): LifecycleErrorCode {\n if (error instanceof PrototypeNotFoundError) return \"not-found\";\n if (error instanceof PrototypeOwnershipError) return \"forbidden\";\n if (error instanceof PrototypeMetadataFileError) return \"filesystem-failure\";\n if (error instanceof RepositoryBusyError) return \"repository-busy\";\n if (\n error instanceof PrototypeRefError ||\n error instanceof LifecycleInputError\n ) {\n return \"invalid-input\";\n }\n if (error instanceof GitRepositoryError) {\n if (error.kind === \"timeout\") return \"timeout\";\n if (error.kind === \"conflict\") return \"working-tree-conflict\";\n if (error.kind === \"invalid-configuration\") return \"unexpected-failure\";\n return \"git-failure\";\n }\n if (error instanceof PrototypeScaffoldError) {\n if (error.kind === \"collision\") return \"already-exists\";\n if (error.kind === \"invalid-input\") return \"invalid-input\";\n return \"filesystem-failure\";\n }\n if (error instanceof PrototypeDuplicateError) {\n if (error.kind === \"source-not-found\") return \"not-found\";\n if (error.kind === \"collision\") return \"already-exists\";\n if (error.kind === \"invalid-source\") return \"invalid-input\";\n return \"filesystem-failure\";\n }\n if (error instanceof DeploymentUnavailableError) {\n return \"deployment-unavailable\";\n }\n if (\n error instanceof DeploymentSourcePushError ||\n error instanceof DeploymentNotAcceptedError ||\n error instanceof DeploymentWorktreeError\n ) {\n return \"deployment-failure\";\n }\n if (error instanceof DeploymentProviderError) {\n return error.kind === \"timeout\" ? \"timeout\" : \"deployment-failure\";\n }\n return \"unexpected-failure\";\n}\n\nfunction resultFrom<Value>(\n operation: () => Promise<Value>,\n): Promise<LifecycleResult<Value>> {\n return operation()\n .then(lifecycleSuccess)\n .catch((error: unknown) => lifecycleFailure(mapError(error)));\n}\n\nasync function diagnoseMetadataStep<Value>(\n step: \"metadata-read\" | \"metadata-write\" | \"metadata-commit\",\n operation: () => Promise<Value>,\n): Promise<Value> {\n try {\n return await operation();\n } catch (error) {\n reportDeploymentIssue(\"publication\", step, error);\n throw error;\n }\n}\n\nexport class LocalPrototypeLifecycle implements PrototypeLifecycle {\n readonly #runtime: PrototypeLifecycleRuntimeContext;\n readonly gitMode: GitMode;\n readonly #catalog: PrototypeCatalogLike;\n readonly #repository: GitRepository;\n readonly #preparePrototypeFiles: PreparePrototypeFiles;\n readonly #deploymentProvider?: DeploymentProvider;\n readonly #applicationId?: string;\n readonly #themeRegistry?: ThemeRegistry;\n #restartRequired = false;\n #pendingPublication?: PendingPublication;\n\n constructor(options: LocalPrototypeLifecycleOptions) {\n const internalOptions = options as InternalLocalPrototypeLifecycleOptions;\n const runtime =\n internalOptions.runtime ??\n createPrototypeLifecycleRuntime(internalOptions);\n this.#runtime = {\n ...runtime,\n gitMode: parseGitModeSetting(runtime.gitMode),\n };\n this.gitMode = this.#runtime.gitMode;\n this.#themeRegistry = options.themeRegistry;\n this.#catalog =\n internalOptions.catalog ??\n new PrototypeCatalog({\n ...this.#runtime,\n themeRegistry: this.#themeRegistry,\n });\n this.#repository = this.#runtime.gitRepository;\n this.#preparePrototypeFiles =\n internalOptions.preparePrototypeFiles ?? (() => undefined);\n this.#deploymentProvider = internalOptions.deploymentProvider;\n this.#applicationId = internalOptions.applicationId;\n if (\n this.#deploymentProvider &&\n (typeof this.#applicationId !== \"string\" ||\n this.#applicationId.length === 0)\n ) {\n throw new TypeError(\n \"applicationId must be provided when deploymentProvider is configured\",\n );\n }\n }\n\n async #verifiedIdentity(identity: BonfireIdentity) {\n return parseBonfireIdentity(identity);\n }\n\n async #currentBranch() {\n const branch = await this.#repository.currentBranch();\n if (branch === \"HEAD\" || branch.length === 0) {\n throw new GitRepositoryError(\"conflict\", \"current-branch\");\n }\n return branch;\n }\n\n async #ownedPrototype(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ) {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n if (normalizedInput.ref.login !== verifiedIdentity.login) {\n throw new PrototypeOwnershipError();\n }\n const summary = await this.#catalog.getSummary(normalizedInput.ref);\n if (!summary) {\n throw new PrototypeNotFoundError();\n }\n\n return {\n ref: normalizedInput.ref,\n path: repositoryRelativePath(this.#runtime, normalizedInput.ref),\n summary,\n };\n }\n\n async #prepareMutationBranch(login: string) {\n await assertIdentityBranchReady(this.#repository, login);\n return login;\n }\n\n async #prepareDestination(\n receipt: OwnedDestinationReceipt,\n expectedFiles: readonly string[],\n request: PreparePrototypeFilesRequest,\n ) {\n try {\n const frozenRequest = Object.freeze({\n ...request,\n ref: Object.freeze({ ...request.ref }),\n identity: Object.freeze({ ...request.identity }),\n ...(request.operation === \"duplicate\"\n ? { source: Object.freeze({ ...request.source }) }\n : {}),\n }) as PreparePrototypeFilesRequest;\n await this.#preparePrototypeFiles(frozenRequest);\n await validateOwnedDestination(\n receipt,\n expectedFiles,\n request.ref,\n this.#themeRegistry,\n );\n } catch {\n try {\n await cleanupOwnedDestination(receipt);\n } catch {\n // The failure remains a finite filesystem failure without retrying cleanup.\n }\n throw new PrototypeScaffoldError(\"filesystem\");\n }\n }\n\n async prepareBranch(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<BranchPreparationValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (\n this.#runtime.gitMode === \"manual\" ||\n this.#runtime.automaticBranchSwitch === \"disabled\"\n ) {\n await this.#currentBranch();\n return { status: \"disabled\" } satisfies BranchPreparationValue;\n }\n\n const synchronization = await synchronizeIdentityBranch(\n this.#repository,\n {\n identityBranch: verifiedIdentity.login,\n primaryBranch: this.#runtime.primaryBranch,\n remote: this.#runtime.remote,\n managedPaths: this.#runtime.managedRepositoryPaths,\n },\n );\n if (synchronization.status !== \"synchronized\") {\n return {\n status: synchronization.status,\n reason: synchronization.reason,\n ...(synchronization.affectedPaths.length > 0\n ? { affectedPaths: synchronization.affectedPaths }\n : {}),\n } satisfies BranchPreparationValue;\n }\n if (\n synchronization.affectedPaths.some((path) =>\n this.#runtime.managedRepositoryPaths.includes(path),\n )\n ) {\n this.#restartRequired = true;\n }\n return {\n status: \"ready\",\n ...(synchronization.affectedPaths.length > 0\n ? { affectedPaths: synchronization.affectedPaths }\n : {}),\n } satisfies BranchPreparationValue;\n },\n );\n });\n }\n\n async create(\n identity: BonfireIdentity,\n input: CreatePrototypeInput,\n ): Promise<LifecycleResult<CreatePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseCreatePrototypeInput(\n input,\n this.#themeRegistry,\n );\n const ref = destinationRef(verifiedIdentity, normalizedInput.title);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (await this.#catalog.getSummary(ref)) {\n throw new PrototypeScaffoldError(\"collision\");\n }\n\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n const prepared = await scaffoldPrototype({\n paths: this.#runtime,\n ref,\n title: normalizedInput.title,\n description: normalizedInput.description,\n authorName: verifiedIdentity.name,\n theme: normalizedInput.theme,\n themeRegistry: this.#themeRegistry,\n });\n await this.#prepareDestination(\n prepared.receipt,\n prepared.expectedFiles,\n {\n operation: \"create\",\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n prototypePath: prepared.destination,\n ref,\n identity: verifiedIdentity,\n title: normalizedInput.title,\n description: normalizedInput.description,\n },\n );\n if (branch === undefined) {\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: \"manual\",\n } satisfies CreatePrototypeValue;\n }\n\n await commitPath(\n this.#repository,\n repositoryRelativePath(this.#runtime, ref),\n `init: ${ref.slug}`,\n );\n const push = await pushBranch(\n this.#repository,\n this.#runtime.remote,\n branch,\n );\n\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: push.remoteStatus,\n } satisfies CreatePrototypeValue;\n },\n );\n });\n }\n\n async edit(\n identity: BonfireIdentity,\n input: EditPrototypeInput,\n ): Promise<LifecycleResult<EditPrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseEditPrototypeInput(\n input,\n this.#themeRegistry,\n );\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(verifiedIdentity, {\n ref: normalizedInput.ref,\n });\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n await assertPrototypeDirectory(this.#runtime, ref);\n const document = await readMetadataDocument(\n this.#runtime,\n ref,\n this.#themeRegistry,\n );\n const metadata = parsePrototypeMetadata(document, {\n themeRegistry: this.#themeRegistry,\n });\n if (metadata.authorLogin !== ref.login) {\n throw new PrototypeNotFoundError();\n }\n\n await writeMetadataDocument(this.#runtime, ref, {\n ...document,\n title: normalizedInput.title,\n description: normalizedInput.description,\n updatedAt: new Date().toISOString(),\n ...(normalizedInput.theme === undefined\n ? {}\n : { theme: normalizedInput.theme }),\n });\n if (branch !== undefined) {\n await commitPath(this.#repository, path, `edit: ${ref.slug}`);\n await pushBranch(this.#repository, this.#runtime.remote, branch);\n }\n\n return { edited: true, ref } satisfies EditPrototypeValue;\n },\n );\n });\n }\n\n async delete(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeletePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(\n verifiedIdentity,\n normalizedInput,\n );\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n await removePrototypeDirectory(this.#runtime, ref);\n if (branch !== undefined) {\n await commitPath(this.#repository, path, `delete: ${ref.slug}`);\n await pushBranch(this.#repository, this.#runtime.remote, branch);\n }\n\n return { deleted: true, ref } satisfies DeletePrototypeValue;\n },\n );\n });\n }\n\n async getDirtyState(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DirtyObservationValue>> {\n return resultFrom(async () => {\n const { path, summary } = await this.#ownedPrototype(identity, input);\n const status = await this.#repository.status([path]);\n return {\n status: status.files.length > 0 ? \"dirty\" : \"clean\",\n ...(summary.previewUrl !== undefined\n ? { previewUrl: summary.previewUrl }\n : {}),\n } satisfies DirtyObservationValue;\n });\n }\n\n async getRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return getRepositorySummary({\n repository: this.#repository,\n catalog: this.#catalog,\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n identity: verifiedIdentity,\n mode: this.#runtime.gitMode,\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n restartRequired: this.#restartRequired,\n });\n });\n }\n\n async checkRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return getRepositorySummary({\n repository: this.#repository,\n catalog: this.#catalog,\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n identity: verifiedIdentity,\n mode: this.#runtime.gitMode,\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n restartRequired: this.#restartRequired,\n refresh: true,\n });\n });\n }\n\n async #headCommit() {\n const commit = (await this.#repository.raw([\"rev-parse\", \"HEAD\"])).trim();\n if (!/^[0-9a-f]{40,64}$/iu.test(commit)) {\n throw new GitRepositoryError(\"malformed-output\", \"head-commit\");\n }\n return commit.toLowerCase();\n }\n\n async #prepareDeploymentBranch(login: string) {\n if (this.#runtime.automaticBranchSwitch === \"disabled\") {\n await assertIdentityBranchReady(this.#repository, login);\n return \"updates-not-synchronized\" as const;\n }\n\n const synchronization = await synchronizeIdentityBranch(this.#repository, {\n identityBranch: login,\n primaryBranch: this.#runtime.primaryBranch,\n remote: this.#runtime.remote,\n managedPaths: this.#runtime.managedRepositoryPaths,\n });\n if (synchronization.status === \"unsafe\") {\n throw new GitRepositoryError(\"conflict\", \"deployment-preparation\");\n }\n await assertIdentityBranchReady(this.#repository, login);\n return synchronization.status === \"deferred\"\n ? (\"updates-not-synchronized\" as const)\n : undefined;\n }\n\n #workspaceRuntime(\n workspacePath: string,\n workspaceRepository: GitRepository,\n ): PrototypeLifecycleRuntimeContext {\n return {\n ...this.#runtime,\n repositoryRoot: workspacePath,\n repositoryRealPath: workspacePath,\n prototypeRoot: join(\n workspacePath,\n relative(this.#runtime.repositoryRoot, this.#runtime.prototypeRoot),\n ),\n gitRepository: workspaceRepository,\n };\n }\n\n async #publishDeploymentMetadata(pending: PendingPublication) {\n return publishDeploymentSource(\n this.#repository,\n {\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n sourceSha: pending.sourceSha,\n commitIdentity: this.#runtime.commitIdentity,\n },\n async ({ path, repository }) => {\n const workspaceRuntime = this.#workspaceRuntime(path, repository);\n const mergedDocument = await diagnoseMetadataStep(\"metadata-read\", () =>\n readMetadataDocument(\n workspaceRuntime,\n pending.ref,\n this.#themeRegistry,\n ),\n );\n await diagnoseMetadataStep(\"metadata-write\", () =>\n writeMetadataDocument(workspaceRuntime, pending.ref, {\n ...mergedDocument,\n previewUrl: pending.previewUrl,\n updatedAt: new Date().toISOString(),\n }),\n );\n await diagnoseMetadataStep(\"metadata-commit\", () =>\n commitPath(\n repository,\n pending.path,\n `deploy: ${pending.ref.slug} (metadata)`,\n ),\n );\n },\n );\n }\n\n async #deploymentValue(\n pending: PendingPublication,\n publicationWarnings: readonly DeploymentWarningCode[] = [],\n ): Promise<DeploymentValue> {\n const warnings = [\n ...(pending.synchronizationWarning === undefined\n ? []\n : [pending.synchronizationWarning]),\n ...publicationWarnings,\n ];\n return {\n deployed: true,\n ref: pending.ref,\n previewUrl: pending.previewUrl,\n deploymentId: pending.deploymentId,\n sourceSha: pending.sourceSha,\n readyState: pending.readyState,\n warning: publicationWarnings[0] ?? pending.synchronizationWarning,\n ...(warnings.length === 0 ? {} : { warnings }),\n };\n }\n\n async deploy(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeploymentValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n if (!this.#deploymentProvider) {\n throw new DeploymentUnavailableError();\n }\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(\n verifiedIdentity,\n normalizedInput,\n );\n const synchronizationWarning = await this.#prepareDeploymentBranch(\n verifiedIdentity.login,\n );\n await assertPrototypeDirectory(this.#runtime, ref);\n const document = await readMetadataDocument(\n this.#runtime,\n ref,\n this.#themeRegistry,\n );\n const metadata = parsePrototypeMetadata(document, {\n themeRegistry: this.#themeRegistry,\n });\n if (metadata.authorLogin !== ref.login) {\n throw new PrototypeNotFoundError();\n }\n\n await commitPath(this.#repository, path, `deploy: ${ref.slug}`);\n const sourceSha = await this.#headCommit();\n const sourcePush = await pushCommitToBranch(\n this.#repository,\n this.#runtime.remote,\n sourceSha,\n verifiedIdentity.login,\n );\n if (sourcePush.remoteStatus !== \"pushed\") {\n throw new DeploymentSourcePushError();\n }\n\n await preflightDeploymentSource(this.#repository, {\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n sourceSha,\n commitIdentity: this.#runtime.commitIdentity,\n });\n\n const deployment = await this.#deploymentProvider!.deploy({\n applicationId: this.#applicationId!,\n login: verifiedIdentity.login,\n });\n if (\n deployment.commitSha.toLowerCase() !== sourceSha ||\n ![\"READY\", \"QUEUED\", \"INITIALIZING\", \"BUILDING\"].includes(\n deployment.readyState,\n )\n ) {\n throw new DeploymentNotAcceptedError();\n }\n const previewUrl = new URL(\n getPrototypeUrl(ref).slice(1),\n `${deployment.url.replace(/\\/+$/, \"\")}/`,\n ).toString();\n const pending: PendingPublication = {\n identity: verifiedIdentity,\n input: normalizedInput,\n ref,\n path,\n previewUrl,\n deploymentId: deployment.deploymentId,\n sourceSha,\n readyState: deployment.readyState,\n synchronizationWarning,\n };\n\n try {\n const publication = await this.#publishDeploymentMetadata(pending);\n this.#pendingPublication = undefined;\n return this.#deploymentValue(\n pending,\n publication.cleanupFailed ? [\"publication-cleanup-failed\"] : [],\n );\n } catch (error) {\n // Keep the accepted deployment retryable for every publication\n // failure, including metadata preparation failures.\n this.#pendingPublication = pending;\n return this.#deploymentValue(pending, [\n error instanceof DeploymentWorktreeError &&\n error.kind !== \"cleanup-failure\"\n ? \"primary-merge-failed\"\n : \"metadata-persistence-failed\",\n ...(error instanceof DeploymentWorktreeError &&\n error.cleanupFailed\n ? [\"publication-cleanup-failed\" as const]\n : []),\n ]);\n }\n },\n );\n });\n }\n\n async retryPublish(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeploymentValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n const pending = this.#pendingPublication;\n if (\n pending === undefined ||\n pending.identity.login !== verifiedIdentity.login ||\n pending.ref.login !== normalizedInput.ref.login ||\n pending.ref.slug !== normalizedInput.ref.slug\n ) {\n throw new DeploymentUnavailableError();\n }\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await assertIdentityBranchReady(\n this.#repository,\n verifiedIdentity.login,\n );\n const publication = await this.#publishDeploymentMetadata(pending);\n this.#pendingPublication = undefined;\n return this.#deploymentValue(\n pending,\n publication.cleanupFailed ? [\"publication-cleanup-failed\"] : [],\n );\n },\n );\n });\n }\n\n async save(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<SavePrototypeValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const { ref, path } = await this.#ownedPrototype(verifiedIdentity, input);\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await this.#prepareMutationBranch(verifiedIdentity.login);\n await commitPath(this.#repository, path, `save: ${ref.slug}`);\n await pushBranch(\n this.#repository,\n this.#runtime.remote,\n verifiedIdentity.login,\n );\n return { saved: true } satisfies SavePrototypeValue;\n },\n );\n });\n }\n\n async discard(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DiscardPrototypeValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const { path } = await this.#ownedPrototype(verifiedIdentity, input);\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await discardPath(this.#repository, path);\n return { discarded: true } satisfies DiscardPrototypeValue;\n },\n );\n });\n }\n\n async duplicate(\n identity: BonfireIdentity,\n input: DuplicatePrototypeInput,\n ): Promise<LifecycleResult<DuplicatePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseDuplicatePrototypeInput(input);\n if (!(await this.#catalog.getSummary(normalizedInput.source))) {\n throw new PrototypeDuplicateError(\"source-not-found\");\n }\n const ref = destinationRef(verifiedIdentity, normalizedInput.title);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (await this.#catalog.getSummary(ref)) {\n throw new PrototypeDuplicateError(\"collision\");\n }\n\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n const prepared = await duplicatePrototype({\n paths: this.#runtime,\n source: normalizedInput.source,\n destination: ref,\n title: normalizedInput.title,\n description: normalizedInput.description,\n authorName: verifiedIdentity.name,\n themeRegistry: this.#themeRegistry,\n });\n await this.#prepareDestination(\n prepared.receipt,\n prepared.expectedFiles,\n {\n operation: \"duplicate\",\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n prototypePath: prepared.destination,\n ref,\n source: normalizedInput.source,\n identity: verifiedIdentity,\n title: normalizedInput.title,\n description: normalizedInput.description,\n },\n );\n if (branch === undefined) {\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: \"manual\",\n } satisfies DuplicatePrototypeValue;\n }\n\n await commitPath(\n this.#repository,\n repositoryRelativePath(this.#runtime, ref),\n `init: ${ref.slug}`,\n );\n const push = await pushBranch(\n this.#repository,\n this.#runtime.remote,\n branch,\n );\n\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: push.remoteStatus,\n } satisfies DuplicatePrototypeValue;\n },\n );\n });\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ebay-dt/bonfire-node",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.9",
|
|
4
4
|
"description": "Node.js adapters for Bonfire.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"access": "public"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ebay-dt/bonfire-core": "0.2.0-alpha.
|
|
34
|
+
"@ebay-dt/bonfire-core": "0.2.0-alpha.9",
|
|
35
35
|
"chokidar": "^5.0.0",
|
|
36
36
|
"simple-git": "^3.36.0"
|
|
37
37
|
},
|