@ebay-dt/bonfire-node 0.2.0-alpha.0 → 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 (52) hide show
  1. package/README.md +18 -5
  2. package/dist/deployment-provider.d.ts +2 -1
  3. package/dist/deployment-provider.d.ts.map +1 -1
  4. package/dist/deployment-provider.js +2 -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 +16 -1
  10. package/dist/git-repository.d.ts.map +1 -1
  11. package/dist/git-repository.js +127 -1
  12. package/dist/git-repository.js.map +1 -1
  13. package/dist/github-cli.d.ts.map +1 -1
  14. package/dist/github-cli.js +3 -1
  15. package/dist/github-cli.js.map +1 -1
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +1 -0
  19. package/dist/index.js.map +1 -1
  20. package/dist/internal/prototype-templates.d.ts +0 -1
  21. package/dist/internal/prototype-templates.d.ts.map +1 -1
  22. package/dist/internal/prototype-templates.js +0 -5
  23. package/dist/internal/prototype-templates.js.map +1 -1
  24. package/dist/lifecycle/deployment-worktree.d.ts +36 -0
  25. package/dist/lifecycle/deployment-worktree.d.ts.map +1 -0
  26. package/dist/lifecycle/deployment-worktree.js +503 -0
  27. package/dist/lifecycle/deployment-worktree.js.map +1 -0
  28. package/dist/lifecycle/local-prototype-lifecycle.d.ts +15 -1
  29. package/dist/lifecycle/local-prototype-lifecycle.d.ts.map +1 -1
  30. package/dist/lifecycle/local-prototype-lifecycle.js +738 -98
  31. package/dist/lifecycle/local-prototype-lifecycle.js.map +1 -1
  32. package/dist/lifecycle/preparation-marker.d.ts +12 -0
  33. package/dist/lifecycle/preparation-marker.d.ts.map +1 -0
  34. package/dist/lifecycle/preparation-marker.js +76 -0
  35. package/dist/lifecycle/preparation-marker.js.map +1 -0
  36. package/dist/lifecycle/prototype-path-policy.d.ts +18 -0
  37. package/dist/lifecycle/prototype-path-policy.d.ts.map +1 -0
  38. package/dist/lifecycle/prototype-path-policy.js +429 -0
  39. package/dist/lifecycle/prototype-path-policy.js.map +1 -0
  40. package/dist/lifecycle/repository-summary.d.ts +7 -1
  41. package/dist/lifecycle/repository-summary.d.ts.map +1 -1
  42. package/dist/lifecycle/repository-summary.js +20 -4
  43. package/dist/lifecycle/repository-summary.js.map +1 -1
  44. package/dist/lifecycle/repository-synchronization.d.ts +13 -8
  45. package/dist/lifecycle/repository-synchronization.d.ts.map +1 -1
  46. package/dist/lifecycle/repository-synchronization.js +680 -139
  47. package/dist/lifecycle/repository-synchronization.js.map +1 -1
  48. package/dist/lifecycle/runtime-context.d.ts +2 -0
  49. package/dist/lifecycle/runtime-context.d.ts.map +1 -1
  50. package/dist/lifecycle/runtime-context.js +5 -1
  51. package/dist/lifecycle/runtime-context.js.map +1 -1
  52. package/package.json +2 -2
@@ -0,0 +1,503 @@
1
+ import { lstat, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { isAbsolute, join, relative, sep } from "node:path";
4
+ import { createGitRepository, GitRepositoryError, } from "../git-repository.js";
5
+ import { isProtectedPrototypePath, isRegularPrototypeMode, } from "./prototype-path-policy.js";
6
+ const OWNERSHIP_MARKER = "bonfire deployment workspace\n";
7
+ const SOURCE_SHA_PATTERN = /^[0-9a-f]{40,64}$/u;
8
+ /** Log only finite classifications; Git/OS messages may contain paths or credentials. */
9
+ export function reportDeploymentIssue(phase, step, error) {
10
+ const kind = step.startsWith("metadata-") ||
11
+ step.startsWith("namespace-") ||
12
+ step === "directory" ||
13
+ step === "marker"
14
+ ? "filesystem-failure"
15
+ : error instanceof GitRepositoryError
16
+ ? error.kind
17
+ : error instanceof DeploymentWorktreeError
18
+ ? error.kind
19
+ : "unexpected";
20
+ console.warn("[bonfire] Deployment workspace attention", {
21
+ phase,
22
+ step,
23
+ kind,
24
+ });
25
+ }
26
+ export class DeploymentWorktreeError extends Error {
27
+ kind;
28
+ cleanupFailed;
29
+ operationError;
30
+ cause;
31
+ constructor(kind, options = {}) {
32
+ super(`Deployment workspace operation failed: ${kind}`);
33
+ this.name = "DeploymentWorktreeError";
34
+ this.kind = kind;
35
+ this.cleanupFailed = options.cleanupFailed ?? false;
36
+ this.operationError = options.operationError;
37
+ this.cause = options.operationError;
38
+ }
39
+ }
40
+ function withCleanupFailure(error) {
41
+ if (error instanceof DeploymentWorktreeError) {
42
+ return new DeploymentWorktreeError(error.kind, {
43
+ cleanupFailed: true,
44
+ operationError: error.operationError ?? error,
45
+ });
46
+ }
47
+ return new DeploymentWorktreeError("cleanup-failure", {
48
+ cleanupFailed: true,
49
+ operationError: error,
50
+ });
51
+ }
52
+ function assertBranchName(value, option) {
53
+ if (value.length === 0 ||
54
+ value.startsWith("-") ||
55
+ value.includes("\0") ||
56
+ /\s/u.test(value) ||
57
+ value.includes("@{") ||
58
+ value.split(/[\\/]/u).some((segment) => segment === "..")) {
59
+ throw new TypeError(`Invalid ${option}`);
60
+ }
61
+ }
62
+ function assertSourceSha(sourceSha) {
63
+ if (!SOURCE_SHA_PATTERN.test(sourceSha)) {
64
+ throw new DeploymentWorktreeError("invalid-source");
65
+ }
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
+ }
82
+ function remotePrimaryRef(options) {
83
+ return `refs/remotes/${options.remote}/${options.primaryBranch}`;
84
+ }
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
+ });
214
+ }
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);
244
+ }
245
+ }
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
+ }
254
+ }
255
+ async function createDeploymentWorktree(repository, options) {
256
+ assertBranchName(options.remote, "remote");
257
+ assertBranchName(options.primaryBranch, "primary branch");
258
+ assertSourceSha(options.sourceSha);
259
+ assertNamespacePath(options.namespacePath);
260
+ try {
261
+ await repository.fetch(options.remote, options.primaryBranch);
262
+ }
263
+ catch (error) {
264
+ reportDeploymentIssue("publication", "fetch", error);
265
+ throw new DeploymentWorktreeError("publication-failure", {
266
+ operationError: error,
267
+ });
268
+ }
269
+ let operationDirectory;
270
+ try {
271
+ operationDirectory = await mkdtemp(join(tmpdir(), "bonfire-deployment-"));
272
+ }
273
+ catch (error) {
274
+ reportDeploymentIssue("publication", "directory", error);
275
+ throw new DeploymentWorktreeError("publication-failure", {
276
+ operationError: error,
277
+ });
278
+ }
279
+ const workspacePath = join(operationDirectory, "workspace");
280
+ const markerPath = join(operationDirectory, "OWNER");
281
+ let worktreeAdded = false;
282
+ let step = "marker";
283
+ try {
284
+ await writeFile(markerPath, OWNERSHIP_MARKER, { flag: "wx" });
285
+ step = "worktree-add";
286
+ await repository.raw([
287
+ "worktree",
288
+ "add",
289
+ "--detach",
290
+ workspacePath,
291
+ remotePrimaryRef(options),
292
+ ]);
293
+ worktreeAdded = true;
294
+ const workspaceRepository = createGitRepository({
295
+ repositoryRoot: workspacePath,
296
+ timeoutMs: options.timeoutMs,
297
+ commitIdentity: options.commitIdentity,
298
+ });
299
+ step = "source-verify";
300
+ await workspaceRepository.raw([
301
+ "cat-file",
302
+ "-e",
303
+ `${options.sourceSha}^{commit}`,
304
+ ]);
305
+ return {
306
+ path: workspacePath,
307
+ repository: workspaceRepository,
308
+ async cleanup() {
309
+ try {
310
+ const marker = await readFile(markerPath, "utf8");
311
+ const stats = await lstat(workspacePath);
312
+ if (marker !== OWNERSHIP_MARKER || stats.isSymbolicLink()) {
313
+ throw new DeploymentWorktreeError("cleanup-failure");
314
+ }
315
+ await repository.raw([
316
+ "worktree",
317
+ "remove",
318
+ "--force",
319
+ workspacePath,
320
+ ]);
321
+ await rm(operationDirectory, { recursive: true, force: false });
322
+ }
323
+ catch (error) {
324
+ reportDeploymentIssue("publication", "cleanup", error);
325
+ if (error instanceof DeploymentWorktreeError)
326
+ throw error;
327
+ throw new DeploymentWorktreeError("cleanup-failure", {
328
+ operationError: error,
329
+ });
330
+ }
331
+ },
332
+ };
333
+ }
334
+ catch (error) {
335
+ reportDeploymentIssue("publication", step, error);
336
+ let cleanupFailed = false;
337
+ if (worktreeAdded) {
338
+ try {
339
+ await repository.raw(["worktree", "remove", "--force", workspacePath]);
340
+ }
341
+ catch (cleanupError) {
342
+ cleanupFailed = true;
343
+ reportDeploymentIssue("publication", "cleanup", cleanupError);
344
+ }
345
+ }
346
+ try {
347
+ await rm(operationDirectory, { recursive: true, force: true });
348
+ }
349
+ catch (cleanupError) {
350
+ cleanupFailed = true;
351
+ reportDeploymentIssue("publication", "cleanup", cleanupError);
352
+ }
353
+ throw new DeploymentWorktreeError("publication-failure", {
354
+ cleanupFailed,
355
+ operationError: error,
356
+ });
357
+ }
358
+ }
359
+ async function withDeploymentWorktree(repository, options, operation) {
360
+ const workspace = await createDeploymentWorktree(repository, options);
361
+ let value;
362
+ try {
363
+ value = await operation(workspace);
364
+ }
365
+ catch (error) {
366
+ try {
367
+ await workspace.cleanup();
368
+ }
369
+ catch {
370
+ throw withCleanupFailure(error);
371
+ }
372
+ throw error;
373
+ }
374
+ try {
375
+ await workspace.cleanup();
376
+ return { value, cleanupFailed: false };
377
+ }
378
+ catch {
379
+ return { value, cleanupFailed: true };
380
+ }
381
+ }
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;
420
+ }
421
+ }
422
+ export async function publishDeploymentSource(repository, options, prepare) {
423
+ let cleanupFailed = false;
424
+ for (const attempt of [1, 2]) {
425
+ try {
426
+ const result = await withDeploymentWorktree(repository, options, async (workspace) => {
427
+ await composeOwnedNamespace(workspace, options);
428
+ await prepare({
429
+ path: workspace.path,
430
+ repository: workspace.repository,
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();
442
+ try {
443
+ await workspace.repository.raw([
444
+ "push",
445
+ options.remote,
446
+ `HEAD:refs/heads/${options.primaryBranch}`,
447
+ ]);
448
+ }
449
+ catch (error) {
450
+ reportDeploymentIssue("publication", "push", error);
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);
460
+ }
461
+ return { publicationSha };
462
+ });
463
+ cleanupFailed ||= result.cleanupFailed;
464
+ return {
465
+ attempts: attempt,
466
+ cleanupFailed,
467
+ publicationSha: result.value.publicationSha,
468
+ };
469
+ }
470
+ catch (error) {
471
+ cleanupFailed ||=
472
+ error instanceof DeploymentWorktreeError && error.cleanupFailed;
473
+ if (!(error instanceof DeploymentWorktreeError) ||
474
+ error.kind !== "publication-rejected" ||
475
+ attempt === 2) {
476
+ if (cleanupFailed) {
477
+ if (error instanceof DeploymentWorktreeError) {
478
+ if (!error.cleanupFailed) {
479
+ throw new DeploymentWorktreeError(error.kind, {
480
+ cleanupFailed: true,
481
+ operationError: error.operationError ?? error,
482
+ });
483
+ }
484
+ }
485
+ else {
486
+ throw new DeploymentWorktreeError("publication-failure", {
487
+ cleanupFailed: true,
488
+ operationError: error,
489
+ });
490
+ }
491
+ }
492
+ throw error;
493
+ }
494
+ }
495
+ }
496
+ throw new DeploymentWorktreeError("publication-rejected", {
497
+ cleanupFailed,
498
+ });
499
+ }
500
+ export function deploymentWorktreeRelativePath(repositoryRoot, workspacePath) {
501
+ return relative(repositoryRoot, workspacePath).split(sep).join("/");
502
+ }
503
+ //# sourceMappingURL=deployment-worktree.js.map
@@ -0,0 +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 { 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>;
@@ -17,6 +20,9 @@ export type PrototypeLifecycle = {
17
20
  save: LifecycleMethod<PrototypeCommandInput, SavePrototypeValue>;
18
21
  discard: LifecycleMethod<PrototypeCommandInput, DiscardPrototypeValue>;
19
22
  deploy: LifecycleMethod<PrototypeCommandInput, DeploymentValue>;
23
+ retryPublish?: LifecycleMethod<PrototypeCommandInput, DeploymentValue>;
24
+ getPendingRecovery?(identity: BonfireIdentity): Promise<LifecycleResult<LifecycleRecoveryValue | null>>;
25
+ retryPendingPublication?(identity: BonfireIdentity, recoveryId: string): Promise<LifecycleResult<LifecycleRecoveryValue>>;
20
26
  };
21
27
  export type LocalPrototypeLifecycleOptions = {
22
28
  repositoryRoot: string;
@@ -34,12 +40,16 @@ export type LocalPrototypeLifecycleOptions = {
34
40
  deploymentProvider?: DeploymentProvider;
35
41
  applicationId?: string;
36
42
  themeRegistry?: ThemeRegistry;
43
+ publishDeploymentSource?: typeof publishDeploymentSource | null;
44
+ /** Test/host seam for the existing package-manager installation step. */
45
+ installDependencies?: DependencyInstaller;
37
46
  };
38
47
  export declare class LocalPrototypeLifecycle implements PrototypeLifecycle {
39
48
  #private;
40
49
  readonly gitMode: GitMode;
41
50
  constructor(options: LocalPrototypeLifecycleOptions);
42
51
  prepareBranch(identity: BonfireIdentity): Promise<LifecycleResult<BranchPreparationValue>>;
52
+ prepareDependencies(dependencyPaths?: readonly string[], installer?: DependencyInstaller): Promise<LifecycleResult<DependencyPreparationValue>>;
43
53
  create(identity: BonfireIdentity, input: CreatePrototypeInput): Promise<LifecycleResult<CreatePrototypeValue>>;
44
54
  edit(identity: BonfireIdentity, input: EditPrototypeInput): Promise<LifecycleResult<EditPrototypeValue>>;
45
55
  delete(identity: BonfireIdentity, input: PrototypeCommandInput): Promise<LifecycleResult<DeletePrototypeValue>>;
@@ -47,6 +57,10 @@ export declare class LocalPrototypeLifecycle implements PrototypeLifecycle {
47
57
  getRepositorySummary(identity: BonfireIdentity): Promise<LifecycleResult<RepositorySummary>>;
48
58
  checkRepositorySummary(identity: BonfireIdentity): Promise<LifecycleResult<RepositorySummary>>;
49
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. */
63
+ retryPublish(identity: BonfireIdentity, input: PrototypeCommandInput): Promise<LifecycleResult<DeploymentValue>>;
50
64
  save(identity: BonfireIdentity, input: PrototypeCommandInput): Promise<LifecycleResult<SavePrototypeValue>>;
51
65
  discard(identity: BonfireIdentity, input: PrototypeCommandInput): Promise<LifecycleResult<DiscardPrototypeValue>>;
52
66
  duplicate(identity: BonfireIdentity, input: DuplicatePrototypeInput): Promise<LifecycleResult<DuplicatePrototypeValue>>;