@ebay-dt/bonfire-node 0.2.0-alpha.10 → 0.2.0-alpha.11

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.
Files changed (45) hide show
  1. package/README.md +18 -5
  2. package/dist/deployment-provider.d.ts +1 -1
  3. package/dist/deployment-provider.d.ts.map +1 -1
  4. package/dist/deployment-provider.js +1 -1
  5. package/dist/deployment-provider.js.map +1 -1
  6. package/dist/duplicate.d.ts.map +1 -1
  7. package/dist/duplicate.js +103 -47
  8. package/dist/duplicate.js.map +1 -1
  9. package/dist/git-repository.d.ts +14 -1
  10. package/dist/git-repository.d.ts.map +1 -1
  11. package/dist/git-repository.js +99 -3
  12. package/dist/git-repository.js.map +1 -1
  13. package/dist/index.d.ts +1 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +1 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/internal/prototype-templates.d.ts +0 -1
  18. package/dist/internal/prototype-templates.d.ts.map +1 -1
  19. package/dist/internal/prototype-templates.js +0 -5
  20. package/dist/internal/prototype-templates.js.map +1 -1
  21. package/dist/lifecycle/deployment-worktree.d.ts +5 -4
  22. package/dist/lifecycle/deployment-worktree.d.ts.map +1 -1
  23. package/dist/lifecycle/deployment-worktree.js +269 -68
  24. package/dist/lifecycle/deployment-worktree.js.map +1 -1
  25. package/dist/lifecycle/local-prototype-lifecycle.d.ts +13 -1
  26. package/dist/lifecycle/local-prototype-lifecycle.d.ts.map +1 -1
  27. package/dist/lifecycle/local-prototype-lifecycle.js +634 -110
  28. package/dist/lifecycle/local-prototype-lifecycle.js.map +1 -1
  29. package/dist/lifecycle/preparation-marker.d.ts +12 -0
  30. package/dist/lifecycle/preparation-marker.d.ts.map +1 -0
  31. package/dist/lifecycle/preparation-marker.js +76 -0
  32. package/dist/lifecycle/preparation-marker.js.map +1 -0
  33. package/dist/lifecycle/prototype-path-policy.d.ts +18 -0
  34. package/dist/lifecycle/prototype-path-policy.d.ts.map +1 -0
  35. package/dist/lifecycle/prototype-path-policy.js +429 -0
  36. package/dist/lifecycle/prototype-path-policy.js.map +1 -0
  37. package/dist/lifecycle/repository-summary.d.ts +5 -1
  38. package/dist/lifecycle/repository-summary.d.ts.map +1 -1
  39. package/dist/lifecycle/repository-summary.js +6 -0
  40. package/dist/lifecycle/repository-summary.js.map +1 -1
  41. package/dist/lifecycle/repository-synchronization.d.ts +13 -9
  42. package/dist/lifecycle/repository-synchronization.d.ts.map +1 -1
  43. package/dist/lifecycle/repository-synchronization.js +670 -173
  44. package/dist/lifecycle/repository-synchronization.js.map +1 -1
  45. package/package.json +2 -2
@@ -1,19 +1,21 @@
1
1
  import { lstat, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
2
2
  import { tmpdir } from "node:os";
3
- import { join, relative, sep } from "node:path";
3
+ import { isAbsolute, join, relative, sep } from "node:path";
4
4
  import { createGitRepository, GitRepositoryError, } from "../git-repository.js";
5
+ import { isProtectedPrototypePath, isRegularPrototypeMode, } from "./prototype-path-policy.js";
5
6
  const OWNERSHIP_MARKER = "bonfire deployment workspace\n";
6
7
  const SOURCE_SHA_PATTERN = /^[0-9a-f]{40,64}$/u;
7
8
  /** Log only finite classifications; Git/OS messages may contain paths or credentials. */
8
9
  export function reportDeploymentIssue(phase, step, error) {
9
- const kind = error instanceof GitRepositoryError
10
- ? error.kind
11
- : error instanceof DeploymentWorktreeError
10
+ const kind = step.startsWith("metadata-") ||
11
+ step.startsWith("namespace-") ||
12
+ step === "directory" ||
13
+ step === "marker"
14
+ ? "filesystem-failure"
15
+ : error instanceof GitRepositoryError
12
16
  ? error.kind
13
- : step.startsWith("metadata-") ||
14
- step === "directory" ||
15
- step === "marker"
16
- ? "filesystem-failure"
17
+ : error instanceof DeploymentWorktreeError
18
+ ? error.kind
17
19
  : "unexpected";
18
20
  console.warn("[bonfire] Deployment workspace attention", {
19
21
  phase,
@@ -62,33 +64,205 @@ function assertSourceSha(sourceSha) {
62
64
  throw new DeploymentWorktreeError("invalid-source");
63
65
  }
64
66
  }
67
+ function assertNamespacePath(namespacePath) {
68
+ if (namespacePath.length === 0 ||
69
+ namespacePath === "." ||
70
+ isAbsolute(namespacePath) ||
71
+ namespacePath.startsWith("-") ||
72
+ namespacePath.includes("\0") ||
73
+ namespacePath.includes("\\") ||
74
+ namespacePath.includes(":") ||
75
+ /[*?\[\]]/u.test(namespacePath) ||
76
+ namespacePath
77
+ .split("/")
78
+ .some((segment) => segment.length === 0 || segment === "..")) {
79
+ throw new DeploymentWorktreeError("protected-path");
80
+ }
81
+ }
65
82
  function remotePrimaryRef(options) {
66
83
  return `refs/remotes/${options.remote}/${options.primaryBranch}`;
67
84
  }
68
- function mapMergeError(error, phase) {
69
- if (!(error instanceof GitRepositoryError))
70
- return error;
71
- if (error.kind === "conflict") {
72
- return new DeploymentWorktreeError(`${phase}-conflict`);
85
+ function parseTreeEntries(output) {
86
+ return output
87
+ .split("\0")
88
+ .filter(Boolean)
89
+ .flatMap((entry) => {
90
+ const tab = entry.indexOf("\t");
91
+ if (tab < 0)
92
+ return [];
93
+ const header = entry.slice(0, tab).split(/\s+/u);
94
+ const mode = header[0];
95
+ const path = entry.slice(tab + 1).replaceAll("\\", "/");
96
+ if (mode === undefined || path.length === 0)
97
+ return [];
98
+ return [{ mode, path }];
99
+ });
100
+ }
101
+ async function readTreeEntries(repository, ref, namespacePath) {
102
+ const output = await repository.raw([
103
+ "ls-tree",
104
+ "-r",
105
+ "-z",
106
+ "--full-tree",
107
+ ref,
108
+ "--",
109
+ namespacePath,
110
+ ]);
111
+ return parseTreeEntries(output);
112
+ }
113
+ function pathIsWithin(path, parent) {
114
+ return path === parent || path.startsWith(`${parent}/`);
115
+ }
116
+ function isPrototypeRootMetadataPath(path, namespacePath) {
117
+ if (!path.endsWith("/metadata.json") || !pathIsWithin(path, namespacePath)) {
118
+ return false;
119
+ }
120
+ const relativePath = path.slice(namespacePath.length + 1);
121
+ return relativePath.split("/").length === 2;
122
+ }
123
+ function assertSafeTree(entries, namespacePath) {
124
+ for (const entry of entries) {
125
+ if (!pathIsWithin(entry.path, namespacePath) ||
126
+ isProtectedPrototypePath(entry.path) ||
127
+ !isRegularPrototypeMode(entry.mode) ||
128
+ entry.mode.startsWith("120000")) {
129
+ throw new DeploymentWorktreeError("protected-path");
130
+ }
131
+ }
132
+ }
133
+ function parseObject(source) {
134
+ try {
135
+ const value = JSON.parse(source);
136
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
137
+ return null;
138
+ }
139
+ return { ...value };
140
+ }
141
+ catch {
142
+ return null;
143
+ }
144
+ }
145
+ function safePreviewValue(document) {
146
+ if (!Object.hasOwn(document, "previewUrl"))
147
+ return null;
148
+ const value = document.previewUrl;
149
+ return value === null || typeof value === "string" ? value : undefined;
150
+ }
151
+ async function capturePreviewOverlays(repository, sourceSha, namespacePath, sourceEntries, primaryEntries) {
152
+ const primaryPaths = new Set(primaryEntries.map((entry) => entry.path));
153
+ const overlays = [];
154
+ for (const entry of sourceEntries) {
155
+ if (!isPrototypeRootMetadataPath(entry.path, namespacePath) ||
156
+ !primaryPaths.has(entry.path)) {
157
+ continue;
158
+ }
159
+ const primarySource = await repository.raw(["show", `HEAD:${entry.path}`]);
160
+ const primaryDocument = parseObject(primarySource);
161
+ if (primaryDocument === null) {
162
+ throw new DeploymentWorktreeError("publication-failure");
163
+ }
164
+ const preview = safePreviewValue(primaryDocument);
165
+ if (preview === undefined) {
166
+ throw new DeploymentWorktreeError("publication-failure");
167
+ }
168
+ const source = parseObject(await repository.raw(["show", `${sourceSha}:${entry.path}`]));
169
+ if (source === null) {
170
+ throw new DeploymentWorktreeError("publication-failure");
171
+ }
172
+ if (!pathIsWithin(entry.path, namespacePath)) {
173
+ throw new DeploymentWorktreeError("protected-path");
174
+ }
175
+ overlays.push({ path: entry.path, source, preview });
176
+ }
177
+ return overlays;
178
+ }
179
+ async function composeOwnedNamespace(workspace, options) {
180
+ const step = "namespace-read";
181
+ let sourceEntries;
182
+ let primaryEntries;
183
+ try {
184
+ sourceEntries = await readTreeEntries(workspace.repository, options.sourceSha, options.namespacePath);
185
+ primaryEntries = await readTreeEntries(workspace.repository, "HEAD", options.namespacePath);
186
+ assertSafeTree(sourceEntries, options.namespacePath);
187
+ assertSafeTree(primaryEntries, options.namespacePath);
188
+ }
189
+ catch (error) {
190
+ reportDeploymentIssue("publication", step, error);
191
+ if (error instanceof DeploymentWorktreeError)
192
+ throw error;
193
+ throw new DeploymentWorktreeError("publication-failure", {
194
+ operationError: error,
195
+ });
196
+ }
197
+ let overlays;
198
+ try {
199
+ overlays = await capturePreviewOverlays(workspace.repository, options.sourceSha, options.namespacePath, sourceEntries, primaryEntries);
200
+ }
201
+ catch (error) {
202
+ reportDeploymentIssue("publication", "metadata-read", error);
203
+ if (error instanceof DeploymentWorktreeError) {
204
+ if (error.kind === "publication-failure") {
205
+ throw new DeploymentWorktreeError("metadata-failure", {
206
+ operationError: error,
207
+ });
208
+ }
209
+ throw error;
210
+ }
211
+ throw new DeploymentWorktreeError("metadata-failure", {
212
+ operationError: error,
213
+ });
73
214
  }
74
- if (phase === "publication") {
75
- if (error.kind === "non-fast-forward") {
76
- return new DeploymentWorktreeError("publication-rejected");
215
+ try {
216
+ await workspace.repository.raw([
217
+ "rm",
218
+ "-r",
219
+ "--ignore-unmatch",
220
+ "--",
221
+ options.namespacePath,
222
+ ]);
223
+ if (sourceEntries.length > 0) {
224
+ await workspace.repository.raw([
225
+ "checkout",
226
+ options.sourceSha,
227
+ "--",
228
+ options.namespacePath,
229
+ ]);
230
+ }
231
+ const overlayPaths = [];
232
+ for (const overlay of overlays) {
233
+ const absolutePath = join(workspace.path, overlay.path);
234
+ const currentSource = await readFile(absolutePath, "utf8");
235
+ const currentDocument = parseObject(currentSource);
236
+ if (currentDocument === null) {
237
+ throw new DeploymentWorktreeError("publication-failure");
238
+ }
239
+ await writeFile(absolutePath, `${JSON.stringify({ ...currentDocument, previewUrl: overlay.preview }, null, 2)}\n`);
240
+ overlayPaths.push(overlay.path);
241
+ }
242
+ if (overlayPaths.length > 0) {
243
+ await workspace.repository.add(overlayPaths);
77
244
  }
78
- return new DeploymentWorktreeError("publication-failure");
79
245
  }
80
- return error;
246
+ catch (error) {
247
+ reportDeploymentIssue("publication", "namespace-write", error);
248
+ if (error instanceof DeploymentWorktreeError)
249
+ throw error;
250
+ throw new DeploymentWorktreeError("publication-failure", {
251
+ operationError: error,
252
+ });
253
+ }
81
254
  }
82
- async function createDeploymentWorktree(repository, options, phase) {
255
+ async function createDeploymentWorktree(repository, options) {
83
256
  assertBranchName(options.remote, "remote");
84
257
  assertBranchName(options.primaryBranch, "primary branch");
85
258
  assertSourceSha(options.sourceSha);
259
+ assertNamespacePath(options.namespacePath);
86
260
  try {
87
261
  await repository.fetch(options.remote, options.primaryBranch);
88
262
  }
89
263
  catch (error) {
90
- reportDeploymentIssue(phase, "fetch", error);
91
- throw new DeploymentWorktreeError(`${phase}-failure`, {
264
+ reportDeploymentIssue("publication", "fetch", error);
265
+ throw new DeploymentWorktreeError("publication-failure", {
92
266
  operationError: error,
93
267
  });
94
268
  }
@@ -97,8 +271,8 @@ async function createDeploymentWorktree(repository, options, phase) {
97
271
  operationDirectory = await mkdtemp(join(tmpdir(), "bonfire-deployment-"));
98
272
  }
99
273
  catch (error) {
100
- reportDeploymentIssue(phase, "directory", error);
101
- throw new DeploymentWorktreeError(`${phase}-failure`, {
274
+ reportDeploymentIssue("publication", "directory", error);
275
+ throw new DeploymentWorktreeError("publication-failure", {
102
276
  operationError: error,
103
277
  });
104
278
  }
@@ -147,7 +321,7 @@ async function createDeploymentWorktree(repository, options, phase) {
147
321
  await rm(operationDirectory, { recursive: true, force: false });
148
322
  }
149
323
  catch (error) {
150
- reportDeploymentIssue(phase, "cleanup", error);
324
+ reportDeploymentIssue("publication", "cleanup", error);
151
325
  if (error instanceof DeploymentWorktreeError)
152
326
  throw error;
153
327
  throw new DeploymentWorktreeError("cleanup-failure", {
@@ -158,7 +332,7 @@ async function createDeploymentWorktree(repository, options, phase) {
158
332
  };
159
333
  }
160
334
  catch (error) {
161
- reportDeploymentIssue(phase, step, error);
335
+ reportDeploymentIssue("publication", step, error);
162
336
  let cleanupFailed = false;
163
337
  if (worktreeAdded) {
164
338
  try {
@@ -166,7 +340,7 @@ async function createDeploymentWorktree(repository, options, phase) {
166
340
  }
167
341
  catch (cleanupError) {
168
342
  cleanupFailed = true;
169
- reportDeploymentIssue(phase, "cleanup", cleanupError);
343
+ reportDeploymentIssue("publication", "cleanup", cleanupError);
170
344
  }
171
345
  }
172
346
  try {
@@ -174,42 +348,21 @@ async function createDeploymentWorktree(repository, options, phase) {
174
348
  }
175
349
  catch (cleanupError) {
176
350
  cleanupFailed = true;
177
- reportDeploymentIssue(phase, "cleanup", cleanupError);
351
+ reportDeploymentIssue("publication", "cleanup", cleanupError);
178
352
  }
179
- throw new DeploymentWorktreeError(`${phase}-failure`, {
353
+ throw new DeploymentWorktreeError("publication-failure", {
180
354
  cleanupFailed,
181
355
  operationError: error,
182
356
  });
183
357
  }
184
358
  }
185
- async function mergeSource(workspace, sourceSha, phase) {
186
- try {
187
- await workspace.repository.merge([
188
- sourceSha,
189
- "--no-ff",
190
- "-m",
191
- `merge: deploy ${sourceSha.slice(0, 12)}`,
192
- ]);
193
- }
194
- catch (error) {
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
- });
202
- }
203
- }
204
- async function withDeploymentWorktree(repository, options, operation, phase) {
205
- const workspace = await createDeploymentWorktree(repository, options, phase);
359
+ async function withDeploymentWorktree(repository, options, operation) {
360
+ const workspace = await createDeploymentWorktree(repository, options);
206
361
  let value;
207
362
  try {
208
363
  value = await operation(workspace);
209
364
  }
210
365
  catch (error) {
211
- // Preserve the operation's failure while making cleanup attention
212
- // observable to callers.
213
366
  try {
214
367
  await workspace.cleanup();
215
368
  }
@@ -226,14 +379,44 @@ async function withDeploymentWorktree(repository, options, operation, phase) {
226
379
  return { value, cleanupFailed: true };
227
380
  }
228
381
  }
229
- export async function preflightDeploymentSource(repository, options) {
230
- const result = await withDeploymentWorktree(repository, options, async (workspace) => {
231
- await mergeSource(workspace, options.sourceSha, "preflight");
232
- }, "preflight");
233
- if (result.cleanupFailed) {
234
- throw new DeploymentWorktreeError("cleanup-failure", {
235
- cleanupFailed: true,
236
- });
382
+ function mapPushError(error) {
383
+ if (error instanceof DeploymentWorktreeError)
384
+ return error;
385
+ if (error instanceof GitRepositoryError &&
386
+ error.kind === "non-fast-forward") {
387
+ return new DeploymentWorktreeError("publication-rejected");
388
+ }
389
+ return new DeploymentWorktreeError("publication-failure", {
390
+ operationError: error,
391
+ });
392
+ }
393
+ async function remoteCommit(repository, options) {
394
+ const output = await repository.raw([
395
+ "ls-remote",
396
+ options.remote,
397
+ `refs/heads/${options.primaryBranch}`,
398
+ ]);
399
+ const commit = output.trim().split(/\s+/u)[0];
400
+ return commit !== undefined && SOURCE_SHA_PATTERN.test(commit)
401
+ ? commit.toLowerCase()
402
+ : null;
403
+ }
404
+ async function remoteContainsPublication(repository, options, publicationSha) {
405
+ const remoteSha = await remoteCommit(repository, options);
406
+ if (remoteSha === publicationSha)
407
+ return true;
408
+ if (remoteSha === null)
409
+ return false;
410
+ try {
411
+ // Fetch only updates the repository's remote-tracking ref; it does not
412
+ // modify the active checkout. This recognizes an accepted push followed
413
+ // by a primary descendant without repeating publication work.
414
+ await repository.fetch(options.remote, options.primaryBranch);
415
+ const mergeBase = (await repository.raw(["merge-base", publicationSha, remoteSha])).trim();
416
+ return mergeBase === publicationSha;
417
+ }
418
+ catch {
419
+ return false;
237
420
  }
238
421
  }
239
422
  export async function publishDeploymentSource(repository, options, prepare) {
@@ -241,11 +424,21 @@ export async function publishDeploymentSource(repository, options, prepare) {
241
424
  for (const attempt of [1, 2]) {
242
425
  try {
243
426
  const result = await withDeploymentWorktree(repository, options, async (workspace) => {
244
- await mergeSource(workspace, options.sourceSha, "publication");
427
+ await composeOwnedNamespace(workspace, options);
245
428
  await prepare({
246
429
  path: workspace.path,
247
430
  repository: workspace.repository,
248
431
  });
432
+ try {
433
+ await workspace.repository.commit(`deploy: publish ${options.sourceSha.slice(0, 12)}`);
434
+ }
435
+ catch (error) {
436
+ reportDeploymentIssue("publication", "namespace-commit", error);
437
+ throw new DeploymentWorktreeError("publication-failure", {
438
+ operationError: error,
439
+ });
440
+ }
441
+ const publicationSha = (await workspace.repository.raw(["rev-parse", "HEAD"])).trim();
249
442
  try {
250
443
  await workspace.repository.raw([
251
444
  "push",
@@ -255,16 +448,24 @@ export async function publishDeploymentSource(repository, options, prepare) {
255
448
  }
256
449
  catch (error) {
257
450
  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
- });
451
+ try {
452
+ if (await remoteContainsPublication(repository, options, publicationSha)) {
453
+ return { publicationSha };
454
+ }
455
+ }
456
+ catch (confirmationError) {
457
+ reportDeploymentIssue("publication", "remote-confirm", confirmationError);
458
+ }
459
+ throw mapPushError(error);
264
460
  }
265
- }, "publication");
461
+ return { publicationSha };
462
+ });
266
463
  cleanupFailed ||= result.cleanupFailed;
267
- return { attempts: attempt, cleanupFailed };
464
+ return {
465
+ attempts: attempt,
466
+ cleanupFailed,
467
+ publicationSha: result.value.publicationSha,
468
+ };
268
469
  }
269
470
  catch (error) {
270
471
  cleanupFailed ||=
@@ -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 | \"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
+ {"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 { isAbsolute, join, relative, sep } from \"node:path\";\nimport {\n createGitRepository,\n GitRepositoryError,\n type GitCommitIdentity,\n type GitRepository,\n} from \"../git-repository.js\";\nimport {\n isProtectedPrototypePath,\n isRegularPrototypeMode,\n} from \"./prototype-path-policy.js\";\n\nconst OWNERSHIP_MARKER = \"bonfire deployment workspace\\n\";\nconst SOURCE_SHA_PATTERN = /^[0-9a-f]{40,64}$/u;\n\ntype DeploymentPhase = \"publication\";\ntype DeploymentStep =\n | \"fetch\"\n | \"directory\"\n | \"marker\"\n | \"worktree-add\"\n | \"source-verify\"\n | \"namespace-read\"\n | \"namespace-write\"\n | \"namespace-commit\"\n | \"metadata-read\"\n | \"metadata-write\"\n | \"metadata-commit\"\n | \"push\"\n | \"remote-confirm\"\n | \"cleanup\";\n\nexport type DeploymentWorktreeOptions = {\n remote: string;\n primaryBranch: string;\n sourceSha: string;\n namespacePath: string;\n timeoutMs?: number;\n commitIdentity?: GitCommitIdentity;\n};\n\nexport type DeploymentWorktreeErrorKind =\n | \"invalid-source\"\n | \"protected-path\"\n | \"metadata-failure\"\n | \"publication-rejected\"\n | \"publication-failure\"\n | \"cleanup-failure\";\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 step.startsWith(\"metadata-\") ||\n step.startsWith(\"namespace-\") ||\n step === \"directory\" ||\n step === \"marker\"\n ? \"filesystem-failure\"\n : error instanceof GitRepositoryError\n ? error.kind\n : error instanceof DeploymentWorktreeError\n ? error.kind\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\ntype TreeEntry = {\n path: string;\n mode: string;\n};\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 assertNamespacePath(namespacePath: string) {\n if (\n namespacePath.length === 0 ||\n namespacePath === \".\" ||\n isAbsolute(namespacePath) ||\n namespacePath.startsWith(\"-\") ||\n namespacePath.includes(\"\\0\") ||\n namespacePath.includes(\"\\\\\") ||\n namespacePath.includes(\":\") ||\n /[*?\\[\\]]/u.test(namespacePath) ||\n namespacePath\n .split(\"/\")\n .some((segment) => segment.length === 0 || segment === \"..\")\n ) {\n throw new DeploymentWorktreeError(\"protected-path\");\n }\n}\n\nfunction remotePrimaryRef(options: DeploymentWorktreeOptions) {\n return `refs/remotes/${options.remote}/${options.primaryBranch}`;\n}\n\nfunction parseTreeEntries(output: string): TreeEntry[] {\n return output\n .split(\"\\0\")\n .filter(Boolean)\n .flatMap((entry) => {\n const tab = entry.indexOf(\"\\t\");\n if (tab < 0) return [];\n const header = entry.slice(0, tab).split(/\\s+/u);\n const mode = header[0];\n const path = entry.slice(tab + 1).replaceAll(\"\\\\\", \"/\");\n if (mode === undefined || path.length === 0) return [];\n return [{ mode, path }];\n });\n}\n\nasync function readTreeEntries(\n repository: GitRepository,\n ref: string,\n namespacePath: string,\n) {\n const output = await repository.raw([\n \"ls-tree\",\n \"-r\",\n \"-z\",\n \"--full-tree\",\n ref,\n \"--\",\n namespacePath,\n ]);\n return parseTreeEntries(output);\n}\n\nfunction pathIsWithin(path: string, parent: string) {\n return path === parent || path.startsWith(`${parent}/`);\n}\n\nfunction isPrototypeRootMetadataPath(path: string, namespacePath: string) {\n if (!path.endsWith(\"/metadata.json\") || !pathIsWithin(path, namespacePath)) {\n return false;\n }\n const relativePath = path.slice(namespacePath.length + 1);\n return relativePath.split(\"/\").length === 2;\n}\n\nfunction assertSafeTree(entries: readonly TreeEntry[], namespacePath: string) {\n for (const entry of entries) {\n if (\n !pathIsWithin(entry.path, namespacePath) ||\n isProtectedPrototypePath(entry.path) ||\n !isRegularPrototypeMode(entry.mode) ||\n entry.mode.startsWith(\"120000\")\n ) {\n throw new DeploymentWorktreeError(\"protected-path\");\n }\n }\n}\n\nfunction parseObject(source: string): Record<string, unknown> | null {\n try {\n const value: unknown = JSON.parse(source);\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return null;\n }\n return { ...(value as Record<string, unknown>) };\n } catch {\n return null;\n }\n}\n\nfunction safePreviewValue(document: Record<string, unknown>) {\n if (!Object.hasOwn(document, \"previewUrl\")) return null;\n const value = document.previewUrl;\n return value === null || typeof value === \"string\" ? value : undefined;\n}\n\nasync function capturePreviewOverlays(\n repository: GitRepository,\n sourceSha: string,\n namespacePath: string,\n sourceEntries: readonly TreeEntry[],\n primaryEntries: readonly TreeEntry[],\n) {\n const primaryPaths = new Set(primaryEntries.map((entry) => entry.path));\n const overlays: Array<{\n path: string;\n source: Record<string, unknown>;\n preview: string | null;\n }> = [];\n for (const entry of sourceEntries) {\n if (\n !isPrototypeRootMetadataPath(entry.path, namespacePath) ||\n !primaryPaths.has(entry.path)\n ) {\n continue;\n }\n const primarySource = await repository.raw([\"show\", `HEAD:${entry.path}`]);\n const primaryDocument = parseObject(primarySource);\n if (primaryDocument === null) {\n throw new DeploymentWorktreeError(\"publication-failure\");\n }\n const preview = safePreviewValue(primaryDocument);\n if (preview === undefined) {\n throw new DeploymentWorktreeError(\"publication-failure\");\n }\n const source = parseObject(\n await repository.raw([\"show\", `${sourceSha}:${entry.path}`]),\n );\n if (source === null) {\n throw new DeploymentWorktreeError(\"publication-failure\");\n }\n if (!pathIsWithin(entry.path, namespacePath)) {\n throw new DeploymentWorktreeError(\"protected-path\");\n }\n overlays.push({ path: entry.path, source, preview });\n }\n return overlays;\n}\n\nasync function composeOwnedNamespace(\n workspace: DeploymentWorktree,\n options: DeploymentWorktreeOptions,\n) {\n const step = \"namespace-read\" as const;\n let sourceEntries: TreeEntry[];\n let primaryEntries: TreeEntry[];\n try {\n sourceEntries = await readTreeEntries(\n workspace.repository,\n options.sourceSha,\n options.namespacePath,\n );\n primaryEntries = await readTreeEntries(\n workspace.repository,\n \"HEAD\",\n options.namespacePath,\n );\n assertSafeTree(sourceEntries, options.namespacePath);\n assertSafeTree(primaryEntries, options.namespacePath);\n } catch (error) {\n reportDeploymentIssue(\"publication\", step, error);\n if (error instanceof DeploymentWorktreeError) throw error;\n throw new DeploymentWorktreeError(\"publication-failure\", {\n operationError: error,\n });\n }\n\n let overlays: Awaited<ReturnType<typeof capturePreviewOverlays>>;\n try {\n overlays = await capturePreviewOverlays(\n workspace.repository,\n options.sourceSha,\n options.namespacePath,\n sourceEntries,\n primaryEntries,\n );\n } catch (error) {\n reportDeploymentIssue(\"publication\", \"metadata-read\", error);\n if (error instanceof DeploymentWorktreeError) {\n if (error.kind === \"publication-failure\") {\n throw new DeploymentWorktreeError(\"metadata-failure\", {\n operationError: error,\n });\n }\n throw error;\n }\n throw new DeploymentWorktreeError(\"metadata-failure\", {\n operationError: error,\n });\n }\n\n try {\n await workspace.repository.raw([\n \"rm\",\n \"-r\",\n \"--ignore-unmatch\",\n \"--\",\n options.namespacePath,\n ]);\n if (sourceEntries.length > 0) {\n await workspace.repository.raw([\n \"checkout\",\n options.sourceSha,\n \"--\",\n options.namespacePath,\n ]);\n }\n\n const overlayPaths: string[] = [];\n for (const overlay of overlays) {\n const absolutePath = join(workspace.path, overlay.path);\n const currentSource = await readFile(absolutePath, \"utf8\");\n const currentDocument = parseObject(currentSource);\n if (currentDocument === null) {\n throw new DeploymentWorktreeError(\"publication-failure\");\n }\n await writeFile(\n absolutePath,\n `${JSON.stringify({ ...currentDocument, previewUrl: overlay.preview }, null, 2)}\\n`,\n );\n overlayPaths.push(overlay.path);\n }\n if (overlayPaths.length > 0) {\n await workspace.repository.add(overlayPaths);\n }\n } catch (error) {\n reportDeploymentIssue(\"publication\", \"namespace-write\", error);\n if (error instanceof DeploymentWorktreeError) throw error;\n throw new DeploymentWorktreeError(\"publication-failure\", {\n operationError: error,\n });\n }\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 assertNamespacePath(options.namespacePath);\n\n try {\n await repository.fetch(options.remote, options.primaryBranch);\n } catch (error) {\n reportDeploymentIssue(\"publication\", \"fetch\", error);\n throw new DeploymentWorktreeError(\"publication-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(\"publication\", \"directory\", error);\n throw new DeploymentWorktreeError(\"publication-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(\"publication\", \"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(\"publication\", 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(\"publication\", \"cleanup\", cleanupError);\n }\n }\n try {\n await rm(operationDirectory, { recursive: true, force: true });\n } catch (cleanupError) {\n cleanupFailed = true;\n reportDeploymentIssue(\"publication\", \"cleanup\", cleanupError);\n }\n throw new DeploymentWorktreeError(\"publication-failure\", {\n cleanupFailed,\n operationError: error,\n });\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 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\nfunction mapPushError(error: unknown) {\n if (error instanceof DeploymentWorktreeError) return error;\n if (\n error instanceof GitRepositoryError &&\n error.kind === \"non-fast-forward\"\n ) {\n return new DeploymentWorktreeError(\"publication-rejected\");\n }\n return new DeploymentWorktreeError(\"publication-failure\", {\n operationError: error,\n });\n}\n\nasync function remoteCommit(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n) {\n const output = await repository.raw([\n \"ls-remote\",\n options.remote,\n `refs/heads/${options.primaryBranch}`,\n ]);\n const commit = output.trim().split(/\\s+/u)[0];\n return commit !== undefined && SOURCE_SHA_PATTERN.test(commit)\n ? commit.toLowerCase()\n : null;\n}\n\nasync function remoteContainsPublication(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n publicationSha: string,\n) {\n const remoteSha = await remoteCommit(repository, options);\n if (remoteSha === publicationSha) return true;\n if (remoteSha === null) return false;\n try {\n // Fetch only updates the repository's remote-tracking ref; it does not\n // modify the active checkout. This recognizes an accepted push followed\n // by a primary descendant without repeating publication work.\n await repository.fetch(options.remote, options.primaryBranch);\n const mergeBase = (\n await repository.raw([\"merge-base\", publicationSha, remoteSha])\n ).trim();\n return mergeBase === publicationSha;\n } catch {\n return false;\n }\n}\n\nexport type DeploymentPublicationResult = {\n attempts: 1 | 2;\n cleanupFailed: boolean;\n publicationSha: string;\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 composeOwnedNamespace(workspace, options);\n await prepare({\n path: workspace.path,\n repository: workspace.repository,\n });\n try {\n await workspace.repository.commit(\n `deploy: publish ${options.sourceSha.slice(0, 12)}`,\n );\n } catch (error) {\n reportDeploymentIssue(\"publication\", \"namespace-commit\", error);\n throw new DeploymentWorktreeError(\"publication-failure\", {\n operationError: error,\n });\n }\n\n const publicationSha = (\n await workspace.repository.raw([\"rev-parse\", \"HEAD\"])\n ).trim();\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 try {\n if (\n await remoteContainsPublication(\n repository,\n options,\n publicationSha,\n )\n ) {\n return { publicationSha };\n }\n } catch (confirmationError) {\n reportDeploymentIssue(\n \"publication\",\n \"remote-confirm\",\n confirmationError,\n );\n }\n throw mapPushError(error);\n }\n return { publicationSha };\n },\n );\n cleanupFailed ||= result.cleanupFailed;\n return {\n attempts: attempt,\n cleanupFailed,\n publicationSha: result.value.publicationSha,\n };\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,12 +1,15 @@
1
- import { type BranchPreparationValue, type CreatePrototypeInput, type CreatePrototypeValue, type DeletePrototypeValue, type DeploymentValue, type DirtyObservationValue, type DiscardPrototypeValue, type DuplicatePrototypeInput, type DuplicatePrototypeValue, type EditPrototypeInput, type EditPrototypeValue, type GitMode, type LifecycleResult, type BonfireIdentity, type PrototypeCommandInput, type SavePrototypeValue, type RepositorySummary, type ThemeRegistry } from "@ebay-dt/bonfire-core";
1
+ import { type BranchPreparationValue, type CreatePrototypeInput, type DependencyPreparationValue, type CreatePrototypeValue, type DeletePrototypeValue, type DeploymentValue, type LifecycleRecoveryValue, type DirtyObservationValue, type DiscardPrototypeValue, type DuplicatePrototypeInput, type DuplicatePrototypeValue, type EditPrototypeInput, type EditPrototypeValue, type GitMode, type LifecycleResult, type BonfireIdentity, type PrototypeCommandInput, type SavePrototypeValue, type RepositorySummary, type ThemeRegistry } from "@ebay-dt/bonfire-core";
2
2
  import { type PrototypeCatalogLike } from "../prototype-catalog.js";
3
3
  import type { PreparePrototypeFiles } from "../prepare-prototype-files.js";
4
4
  import { type DeploymentProvider } from "../deployment-provider.js";
5
+ import { publishDeploymentSource } from "./deployment-worktree.js";
6
+ import { type DependencyInstaller } from "./preparation-marker.js";
5
7
  type LifecycleMethod<Input, Value> = (identity: BonfireIdentity, input: Input) => Promise<LifecycleResult<Value>>;
6
8
  export type PrototypeLifecycle = {
7
9
  /** Declares whether this lifecycle enforces the selected Git mode. */
8
10
  readonly gitMode?: GitMode;
9
11
  prepareBranch(identity: BonfireIdentity): Promise<LifecycleResult<BranchPreparationValue>>;
12
+ prepareDependencies?(dependencyPaths?: readonly string[], installer?: DependencyInstaller): Promise<LifecycleResult<DependencyPreparationValue>>;
10
13
  create: LifecycleMethod<CreatePrototypeInput, CreatePrototypeValue>;
11
14
  duplicate: LifecycleMethod<DuplicatePrototypeInput, DuplicatePrototypeValue>;
12
15
  edit: LifecycleMethod<EditPrototypeInput, EditPrototypeValue>;
@@ -18,6 +21,8 @@ export type PrototypeLifecycle = {
18
21
  discard: LifecycleMethod<PrototypeCommandInput, DiscardPrototypeValue>;
19
22
  deploy: LifecycleMethod<PrototypeCommandInput, DeploymentValue>;
20
23
  retryPublish?: LifecycleMethod<PrototypeCommandInput, DeploymentValue>;
24
+ getPendingRecovery?(identity: BonfireIdentity): Promise<LifecycleResult<LifecycleRecoveryValue | null>>;
25
+ retryPendingPublication?(identity: BonfireIdentity, recoveryId: string): Promise<LifecycleResult<LifecycleRecoveryValue>>;
21
26
  };
22
27
  export type LocalPrototypeLifecycleOptions = {
23
28
  repositoryRoot: string;
@@ -35,12 +40,16 @@ export type LocalPrototypeLifecycleOptions = {
35
40
  deploymentProvider?: DeploymentProvider;
36
41
  applicationId?: string;
37
42
  themeRegistry?: ThemeRegistry;
43
+ publishDeploymentSource?: typeof publishDeploymentSource | null;
44
+ /** Test/host seam for the existing package-manager installation step. */
45
+ installDependencies?: DependencyInstaller;
38
46
  };
39
47
  export declare class LocalPrototypeLifecycle implements PrototypeLifecycle {
40
48
  #private;
41
49
  readonly gitMode: GitMode;
42
50
  constructor(options: LocalPrototypeLifecycleOptions);
43
51
  prepareBranch(identity: BonfireIdentity): Promise<LifecycleResult<BranchPreparationValue>>;
52
+ prepareDependencies(dependencyPaths?: readonly string[], installer?: DependencyInstaller): Promise<LifecycleResult<DependencyPreparationValue>>;
44
53
  create(identity: BonfireIdentity, input: CreatePrototypeInput): Promise<LifecycleResult<CreatePrototypeValue>>;
45
54
  edit(identity: BonfireIdentity, input: EditPrototypeInput): Promise<LifecycleResult<EditPrototypeValue>>;
46
55
  delete(identity: BonfireIdentity, input: PrototypeCommandInput): Promise<LifecycleResult<DeletePrototypeValue>>;
@@ -48,6 +57,9 @@ export declare class LocalPrototypeLifecycle implements PrototypeLifecycle {
48
57
  getRepositorySummary(identity: BonfireIdentity): Promise<LifecycleResult<RepositorySummary>>;
49
58
  checkRepositorySummary(identity: BonfireIdentity): Promise<LifecycleResult<RepositorySummary>>;
50
59
  deploy(identity: BonfireIdentity, input: PrototypeCommandInput): Promise<LifecycleResult<DeploymentValue>>;
60
+ getPendingRecovery(identity: BonfireIdentity): Promise<LifecycleResult<LifecycleRecoveryValue | null>>;
61
+ retryPendingPublication(identity: BonfireIdentity, recoveryId: string): Promise<LifecycleResult<LifecycleRecoveryValue>>;
62
+ /** Compatibility wrapper; recovery-ID retry is the implementation. */
51
63
  retryPublish(identity: BonfireIdentity, input: PrototypeCommandInput): Promise<LifecycleResult<DeploymentValue>>;
52
64
  save(identity: BonfireIdentity, input: PrototypeCommandInput): Promise<LifecycleResult<SavePrototypeValue>>;
53
65
  discard(identity: BonfireIdentity, input: PrototypeCommandInput): Promise<LifecycleResult<DiscardPrototypeValue>>;