@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.
- package/README.md +18 -5
- package/dist/deployment-provider.d.ts +1 -1
- package/dist/deployment-provider.d.ts.map +1 -1
- package/dist/deployment-provider.js +1 -1
- package/dist/deployment-provider.js.map +1 -1
- package/dist/duplicate.d.ts.map +1 -1
- package/dist/duplicate.js +103 -47
- package/dist/duplicate.js.map +1 -1
- package/dist/git-repository.d.ts +14 -1
- package/dist/git-repository.d.ts.map +1 -1
- package/dist/git-repository.js +99 -3
- package/dist/git-repository.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/prototype-templates.d.ts +0 -1
- package/dist/internal/prototype-templates.d.ts.map +1 -1
- package/dist/internal/prototype-templates.js +0 -5
- package/dist/internal/prototype-templates.js.map +1 -1
- package/dist/lifecycle/deployment-worktree.d.ts +5 -4
- package/dist/lifecycle/deployment-worktree.d.ts.map +1 -1
- package/dist/lifecycle/deployment-worktree.js +269 -68
- package/dist/lifecycle/deployment-worktree.js.map +1 -1
- package/dist/lifecycle/local-prototype-lifecycle.d.ts +13 -1
- package/dist/lifecycle/local-prototype-lifecycle.d.ts.map +1 -1
- package/dist/lifecycle/local-prototype-lifecycle.js +634 -110
- package/dist/lifecycle/local-prototype-lifecycle.js.map +1 -1
- package/dist/lifecycle/preparation-marker.d.ts +12 -0
- package/dist/lifecycle/preparation-marker.d.ts.map +1 -0
- package/dist/lifecycle/preparation-marker.js +76 -0
- package/dist/lifecycle/preparation-marker.js.map +1 -0
- package/dist/lifecycle/prototype-path-policy.d.ts +18 -0
- package/dist/lifecycle/prototype-path-policy.d.ts.map +1 -0
- package/dist/lifecycle/prototype-path-policy.js +429 -0
- package/dist/lifecycle/prototype-path-policy.js.map +1 -0
- package/dist/lifecycle/repository-summary.d.ts +5 -1
- package/dist/lifecycle/repository-summary.d.ts.map +1 -1
- package/dist/lifecycle/repository-summary.js +6 -0
- package/dist/lifecycle/repository-summary.js.map +1 -1
- package/dist/lifecycle/repository-synchronization.d.ts +13 -9
- package/dist/lifecycle/repository-synchronization.d.ts.map +1 -1
- package/dist/lifecycle/repository-synchronization.js +670 -173
- package/dist/lifecycle/repository-synchronization.js.map +1 -1
- 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 =
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
:
|
|
14
|
-
|
|
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
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
91
|
-
throw new DeploymentWorktreeError(
|
|
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(
|
|
101
|
-
throw new DeploymentWorktreeError(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
351
|
+
reportDeploymentIssue("publication", "cleanup", cleanupError);
|
|
178
352
|
}
|
|
179
|
-
throw new DeploymentWorktreeError(
|
|
353
|
+
throw new DeploymentWorktreeError("publication-failure", {
|
|
180
354
|
cleanupFailed,
|
|
181
355
|
operationError: error,
|
|
182
356
|
});
|
|
183
357
|
}
|
|
184
358
|
}
|
|
185
|
-
async function
|
|
186
|
-
|
|
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
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|
|
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
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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
|
-
|
|
461
|
+
return { publicationSha };
|
|
462
|
+
});
|
|
266
463
|
cleanupFailed ||= result.cleanupFailed;
|
|
267
|
-
return {
|
|
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>>;
|