@ricsam/r5d-worker 0.0.123 → 0.0.125
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 +1 -1
- package/dist/cjs/command-launcher.cjs +6 -2
- package/dist/cjs/main.cjs +496 -238
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/project-checkout-garbage.cjs +32 -3
- package/dist/cjs/project-workspace-state.cjs +51 -35
- package/dist/cjs/project-worktrees.cjs +79 -34
- package/dist/cjs/recovery-journal-protocol.cjs +56 -0
- package/dist/cjs/recovery-journal-runtime.cjs +133 -0
- package/dist/cjs/recovery-journal-thread.cjs +9 -0
- package/dist/cjs/recovery-journal.cjs +735 -0
- package/dist/cjs/recovery-store.cjs +8 -0
- package/dist/cjs/session-file-mutations.cjs +61 -0
- package/dist/cjs/working-tree-mirror.cjs +1 -0
- package/dist/cjs/workspace-command-sync-policy.cjs +37 -8
- package/dist/cjs/workspace-filesystem-executor-thread.cjs +36 -0
- package/dist/cjs/workspace-filesystem-executor.cjs +327 -0
- package/dist/cjs/workspace-filesystem-job-types.cjs +134 -0
- package/dist/cjs/workspace-filesystem-jobs.cjs +57 -0
- package/dist/cjs/workspace-git-sync.cjs +275 -201
- package/dist/cjs/workspace-mount-hold-fence.cjs +120 -0
- package/dist/mjs/command-launcher.mjs +6 -2
- package/dist/mjs/main.mjs +499 -244
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/project-checkout-garbage.mjs +30 -2
- package/dist/mjs/project-workspace-state.mjs +51 -35
- package/dist/mjs/project-worktrees.mjs +74 -34
- package/dist/mjs/recovery-journal-protocol.mjs +30 -0
- package/dist/mjs/recovery-journal-runtime.mjs +112 -0
- package/dist/mjs/recovery-journal-thread.mjs +8 -0
- package/dist/mjs/recovery-journal.mjs +687 -0
- package/dist/mjs/recovery-store.mjs +8 -0
- package/dist/mjs/session-file-mutations.mjs +37 -0
- package/dist/mjs/working-tree-mirror.mjs +1 -0
- package/dist/mjs/workspace-command-sync-policy.mjs +37 -8
- package/dist/mjs/workspace-filesystem-executor-thread.mjs +38 -0
- package/dist/mjs/workspace-filesystem-executor.mjs +287 -0
- package/dist/mjs/workspace-filesystem-job-types.mjs +106 -0
- package/dist/mjs/workspace-filesystem-jobs.mjs +51 -0
- package/dist/mjs/workspace-git-sync.mjs +264 -202
- package/dist/mjs/workspace-mount-hold-fence.mjs +95 -0
- package/dist/types/command-launcher.d.ts +2 -1
- package/dist/types/main.d.ts +30 -19
- package/dist/types/project-checkout-garbage.d.ts +19 -2
- package/dist/types/project-workspace-state.d.ts +9 -9
- package/dist/types/project-worktrees.d.ts +49 -5
- package/dist/types/recovery-journal-protocol.d.ts +35 -0
- package/dist/types/recovery-journal-runtime.d.ts +12 -0
- package/dist/types/recovery-journal-stall-fixture.d.ts +1 -0
- package/dist/types/recovery-journal-thread.d.ts +1 -0
- package/dist/types/recovery-journal.d.ts +246 -0
- package/dist/types/recovery-store.d.ts +6 -0
- package/dist/types/session-file-mutations.d.ts +22 -0
- package/dist/types/workspace-command-sync-policy.d.ts +22 -7
- package/dist/types/workspace-filesystem-executor-thread.d.ts +1 -0
- package/dist/types/workspace-filesystem-executor.d.ts +123 -0
- package/dist/types/workspace-filesystem-job-types.d.ts +246 -0
- package/dist/types/workspace-filesystem-jobs.d.ts +7 -0
- package/dist/types/workspace-git-sync.d.ts +113 -7
- package/dist/types/workspace-mount-hold-fence.d.ts +42 -0
- package/package.json +1 -1
- package/dist/cjs/project-snapshot-recovery-runner.cjs +0 -171
- package/dist/mjs/project-snapshot-recovery-runner.mjs +0 -135
- package/dist/types/project-snapshot-recovery-runner.d.ts +0 -10
|
@@ -40,9 +40,20 @@ __export(workspace_git_sync_exports, {
|
|
|
40
40
|
hydrateWorkspaceGitMounts: () => hydrateWorkspaceGitMounts,
|
|
41
41
|
recoverWorkspaceGitHydration: () => recoverWorkspaceGitHydration,
|
|
42
42
|
resetWorkspaceGit: () => resetWorkspaceGit,
|
|
43
|
+
runCheckoutDurabilityRecoveryJob: () => runCheckoutDurabilityRecoveryJob,
|
|
44
|
+
runCheckoutTransitionBeginJob: () => runCheckoutTransitionBeginJob,
|
|
45
|
+
runCheckoutTransitionCompleteJob: () => runCheckoutTransitionCompleteJob,
|
|
46
|
+
runHydrationRawJob: () => runHydrationRawJob,
|
|
47
|
+
runHydrationRecoveryJob: () => runHydrationRecoveryJob,
|
|
48
|
+
runHydrationTransactionJob: () => runHydrationTransactionJob,
|
|
49
|
+
runOuterCheckoutFsyncJob: () => runOuterCheckoutFsyncJob,
|
|
50
|
+
runProjectionMirrorJob: () => runProjectionMirrorJob,
|
|
51
|
+
runProjectionOuterApplyJob: () => runProjectionOuterApplyJob,
|
|
52
|
+
runProjectionReceiptJob: () => runProjectionReceiptJob,
|
|
43
53
|
synchronizeWorkspaceGit: () => synchronizeWorkspaceGit,
|
|
44
54
|
workspaceGitHydrationIsCurrent: () => workspaceGitHydrationIsCurrent,
|
|
45
55
|
workspaceGitHydrationIsRecoverable: () => workspaceGitHydrationIsRecoverable,
|
|
56
|
+
workspaceGitMountData: () => workspaceGitMountData,
|
|
46
57
|
workspaceGitMountsForRemediation: () => workspaceGitMountsForRemediation,
|
|
47
58
|
workspaceGitSyncTestHarness: () => workspaceGitSyncTestHarness
|
|
48
59
|
});
|
|
@@ -51,6 +62,7 @@ var import_node_fs = __toESM(require("node:fs"), 1);
|
|
|
51
62
|
var import_node_path = __toESM(require("node:path"), 1);
|
|
52
63
|
var import_git_process_environment = require("./git-process-environment.cjs");
|
|
53
64
|
var import_workspace_merge_projection = require("./workspace-merge-projection.cjs");
|
|
65
|
+
var import_workspace_filesystem_executor = require("./workspace-filesystem-executor.cjs");
|
|
54
66
|
var import_working_tree_mirror = require("./working-tree-mirror.cjs");
|
|
55
67
|
var import_workspace_mount_boundary = require("./workspace-mount-boundary.cjs");
|
|
56
68
|
const WORKSPACE_GIT_BRANCH = "main";
|
|
@@ -60,6 +72,22 @@ const WORKSPACE_GIT_INTEGRATED_REF = "refs/r5d/workspace-local/integrated";
|
|
|
60
72
|
const WORKSPACE_GIT_HYDRATED_RECEIPT = "r5d/workspace-hydrated-head";
|
|
61
73
|
const WORKSPACE_GIT_HYDRATION_TRANSACTION = "r5d/workspace-hydration-transaction";
|
|
62
74
|
const WORKSPACE_GIT_CHECKOUT_DURABILITY = "r5d/workspace-checkout-durability";
|
|
75
|
+
function workspaceGitMountData(mount) {
|
|
76
|
+
return {
|
|
77
|
+
id: mount.id,
|
|
78
|
+
hydrationIncarnationKey: mount.hydrationIncarnationKey,
|
|
79
|
+
sourcePath: mount.sourcePath,
|
|
80
|
+
durabilityRootPath: mount.durabilityRootPath,
|
|
81
|
+
workspaceRelativePath: mount.workspaceRelativePath,
|
|
82
|
+
sourceMode: mount.sourceMode,
|
|
83
|
+
hydrateDeletionMode: mount.hydrateDeletionMode,
|
|
84
|
+
...mount.preserveLocalOnInitialOuterAbsence === void 0 ? {} : { preserveLocalOnInitialOuterAbsence: mount.preserveLocalOnInitialOuterAbsence },
|
|
85
|
+
...mount.preserveLocalOnHydrationBasisChange === void 0 ? {} : { preserveLocalOnHydrationBasisChange: mount.preserveLocalOnHydrationBasisChange },
|
|
86
|
+
...mount.initialPublicationSourceRelativePath === void 0 ? {} : { initialPublicationSourceRelativePath: mount.initialPublicationSourceRelativePath },
|
|
87
|
+
...mount.deleteWhenSourceMissing === void 0 ? {} : { deleteWhenSourceMissing: mount.deleteWhenSourceMissing },
|
|
88
|
+
...mount.lastProjectedMutationToken === void 0 ? {} : { lastProjectedMutationToken: mount.lastProjectedMutationToken }
|
|
89
|
+
};
|
|
90
|
+
}
|
|
63
91
|
function workspaceGitMountsForRemediation(mounts) {
|
|
64
92
|
return mounts.map((mount) => ({ ...mount, busy: mount.busyForRecovery ?? mount.busy }));
|
|
65
93
|
}
|
|
@@ -151,9 +179,6 @@ async function revParseAsync(workspacePath, revision) {
|
|
|
151
179
|
const result = await gitResultAsync(workspacePath, ["rev-parse", "--verify", revision]);
|
|
152
180
|
return result.exitCode === 0 ? result.stdout : null;
|
|
153
181
|
}
|
|
154
|
-
function yieldToWorkerEventLoop() {
|
|
155
|
-
return new Promise((resolve) => setImmediate(resolve));
|
|
156
|
-
}
|
|
157
182
|
function workspaceCommitsExist(workspacePath, heads) {
|
|
158
183
|
const uniqueHeads = [...new Set(heads)];
|
|
159
184
|
if (uniqueHeads.length === 0) return true;
|
|
@@ -418,20 +443,35 @@ function readHydratedWorkspaceReceipt(workspacePath) {
|
|
|
418
443
|
}
|
|
419
444
|
return unverified.receipt;
|
|
420
445
|
}
|
|
446
|
+
async function readHydratedWorkspaceReceiptUnverifiedAsync(workspacePath) {
|
|
447
|
+
const receiptPath = hydratedWorkspaceReceiptPath(workspacePath);
|
|
448
|
+
let status;
|
|
449
|
+
try {
|
|
450
|
+
status = await import_node_fs.default.promises.lstat(receiptPath);
|
|
451
|
+
} catch (error) {
|
|
452
|
+
if (error.code === "ENOENT") return null;
|
|
453
|
+
throw error;
|
|
454
|
+
}
|
|
455
|
+
if (!status.isFile() || status.isSymbolicLink()) {
|
|
456
|
+
throw new Error(`Workspace hydration receipt is not a regular file: ${receiptPath}`);
|
|
457
|
+
}
|
|
458
|
+
const content = await import_node_fs.default.promises.readFile(receiptPath, "utf8");
|
|
459
|
+
return { ...parseWorkspaceHydrationReceiptContentUnverified(receiptPath, content), receiptPath };
|
|
460
|
+
}
|
|
421
461
|
async function readHydratedWorkspaceReceiptAsync(workspacePath) {
|
|
422
|
-
const unverified =
|
|
462
|
+
const unverified = await readHydratedWorkspaceReceiptUnverifiedAsync(workspacePath);
|
|
423
463
|
if (!unverified) return null;
|
|
424
464
|
if (!await workspaceCommitsExistAsync(workspacePath, unverified.headsToVerify)) {
|
|
425
465
|
throw new Error(`Workspace hydration receipt does not resolve to a local commit: ${unverified.receiptPath}`);
|
|
426
466
|
}
|
|
427
467
|
return unverified.receipt;
|
|
428
468
|
}
|
|
429
|
-
function hydratedWorkspaceReceiptFileIsV3(workspacePath) {
|
|
469
|
+
async function hydratedWorkspaceReceiptFileIsV3(workspacePath) {
|
|
430
470
|
const receiptPath = hydratedWorkspaceReceiptPath(workspacePath);
|
|
431
|
-
const status = lstatIfExists(receiptPath);
|
|
432
|
-
if (!status?.isFile() || status.isSymbolicLink()) return false;
|
|
433
471
|
try {
|
|
434
|
-
|
|
472
|
+
const status = await import_node_fs.default.promises.lstat(receiptPath);
|
|
473
|
+
if (!status.isFile() || status.isSymbolicLink()) return false;
|
|
474
|
+
return JSON.parse(await import_node_fs.default.promises.readFile(receiptPath, "utf8")).version === 3;
|
|
435
475
|
} catch {
|
|
436
476
|
return false;
|
|
437
477
|
}
|
|
@@ -528,24 +568,6 @@ function runWorkspaceRefUpdateBatch(workspacePath, instructions, action) {
|
|
|
528
568
|
throw new Error(`${action}: ${updated.stderr.toString().trim() || `git exited ${updated.exitCode}`}`);
|
|
529
569
|
}
|
|
530
570
|
}
|
|
531
|
-
async function runWorkspaceRefUpdateBatchAsync(workspacePath, instructions, action) {
|
|
532
|
-
if (instructions.length === 0) return;
|
|
533
|
-
const subprocess = Bun.spawn(gitCommandArgs(["update-ref", "--stdin"]), {
|
|
534
|
-
cwd: workspacePath,
|
|
535
|
-
stdin: Buffer.from([...instructions, ""].join("\n")),
|
|
536
|
-
stdout: "pipe",
|
|
537
|
-
stderr: "pipe",
|
|
538
|
-
env: (0, import_git_process_environment.workerGitProcessEnvironment)()
|
|
539
|
-
});
|
|
540
|
-
const [, stderr, exitCode] = await Promise.all([
|
|
541
|
-
new Response(subprocess.stdout).text(),
|
|
542
|
-
new Response(subprocess.stderr).text(),
|
|
543
|
-
subprocess.exited
|
|
544
|
-
]);
|
|
545
|
-
if (exitCode !== 0) {
|
|
546
|
-
throw new Error(`${action}: ${stderr.trim() || `git exited ${exitCode}`}`);
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
571
|
function workspaceBasisRefTransaction(receipt, stableRefs) {
|
|
550
572
|
const desired = desiredWorkspaceBasisRefs(receipt);
|
|
551
573
|
const transaction = ["start"];
|
|
@@ -568,24 +590,6 @@ function updateWorkspaceBasisRefs(workspacePath, receipt) {
|
|
|
568
590
|
"remove staged workspace hydration basis refs"
|
|
569
591
|
);
|
|
570
592
|
}
|
|
571
|
-
async function updateWorkspaceBasisRefsAsync(workspacePath, receipt) {
|
|
572
|
-
const stableRefs = (await gitAsync(workspacePath, ["for-each-ref", "--format=%(refname)", WORKSPACE_GIT_BASIS_REF_PREFIX], "list workspace basis refs")).split("\n").filter(Boolean).sort();
|
|
573
|
-
await runWorkspaceRefUpdateBatchAsync(
|
|
574
|
-
workspacePath,
|
|
575
|
-
workspaceBasisRefTransaction(receipt, stableRefs),
|
|
576
|
-
"Synchronize workspace hydration basis refs"
|
|
577
|
-
);
|
|
578
|
-
const stagedRefs = (await gitAsync(
|
|
579
|
-
workspacePath,
|
|
580
|
-
["for-each-ref", "--format=%(refname)", WORKSPACE_GIT_BASIS_STAGING_REF_PREFIX],
|
|
581
|
-
"list staged workspace basis refs"
|
|
582
|
-
)).split("\n").filter(Boolean).sort();
|
|
583
|
-
await runWorkspaceRefUpdateBatchAsync(
|
|
584
|
-
workspacePath,
|
|
585
|
-
stagedRefs.map((ref) => `delete ${ref}`),
|
|
586
|
-
"remove staged workspace hydration basis refs"
|
|
587
|
-
);
|
|
588
|
-
}
|
|
589
593
|
function assertCanonicalHydrationReceiptHeads(receipt) {
|
|
590
594
|
for (const mount of receipt.mounts) {
|
|
591
595
|
if (!/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(mount.head)) {
|
|
@@ -599,20 +603,15 @@ function throwMissingHydrationReceiptCommit(workspacePath, receipt) {
|
|
|
599
603
|
}
|
|
600
604
|
function updateHydratedWorkspaceReceipt(workspacePath, receipt) {
|
|
601
605
|
assertCanonicalHydrationReceiptHeads(receipt);
|
|
602
|
-
if (!workspaceCommitsExist(
|
|
606
|
+
if (!workspaceCommitsExist(
|
|
607
|
+
workspacePath,
|
|
608
|
+
receipt.mounts.map(({ head }) => head)
|
|
609
|
+
)) {
|
|
603
610
|
throwMissingHydrationReceiptCommit(workspacePath, receipt);
|
|
604
611
|
}
|
|
605
612
|
readHydratedWorkspaceReceipt(workspacePath);
|
|
606
613
|
writeHydratedWorkspaceReceiptCore(workspacePath, receipt);
|
|
607
614
|
}
|
|
608
|
-
async function updateHydratedWorkspaceReceiptAsync(workspacePath, receipt) {
|
|
609
|
-
assertCanonicalHydrationReceiptHeads(receipt);
|
|
610
|
-
if (!await workspaceCommitsExistAsync(workspacePath, receipt.mounts.map(({ head }) => head))) {
|
|
611
|
-
throwMissingHydrationReceiptCommit(workspacePath, receipt);
|
|
612
|
-
}
|
|
613
|
-
await readHydratedWorkspaceReceiptAsync(workspacePath);
|
|
614
|
-
await writeHydratedWorkspaceReceiptCoreAsync(workspacePath, receipt);
|
|
615
|
-
}
|
|
616
615
|
function prepareHydratedWorkspaceReceiptWrite(workspacePath) {
|
|
617
616
|
const gitDirectory = import_node_path.default.join(workspacePath, ".git");
|
|
618
617
|
const gitDirectoryStatus = import_node_fs.default.lstatSync(gitDirectory);
|
|
@@ -667,17 +666,6 @@ function writeHydratedWorkspaceReceiptCore(workspacePath, receipt) {
|
|
|
667
666
|
publishHydratedWorkspaceReceiptFile(workspacePath, receipt, paths);
|
|
668
667
|
updateWorkspaceBasisRefs(workspacePath, receipt);
|
|
669
668
|
}
|
|
670
|
-
async function writeHydratedWorkspaceReceiptCoreAsync(workspacePath, receipt) {
|
|
671
|
-
const paths = prepareHydratedWorkspaceReceiptWrite(workspacePath);
|
|
672
|
-
const stagingPrefix = `${WORKSPACE_GIT_BASIS_STAGING_REF_PREFIX}${crypto.randomUUID()}/`;
|
|
673
|
-
await runWorkspaceRefUpdateBatchAsync(
|
|
674
|
-
workspacePath,
|
|
675
|
-
stagedWorkspaceBasisRefInstructions(receipt, stagingPrefix),
|
|
676
|
-
"stage workspace hydration basis refs"
|
|
677
|
-
);
|
|
678
|
-
publishHydratedWorkspaceReceiptFile(workspacePath, receipt, paths);
|
|
679
|
-
await updateWorkspaceBasisRefsAsync(workspacePath, receipt);
|
|
680
|
-
}
|
|
681
669
|
function hydrationTransactionPath(workspacePath) {
|
|
682
670
|
return import_node_path.default.join(workspacePath, ".git", ...WORKSPACE_GIT_HYDRATION_TRANSACTION.split("/"));
|
|
683
671
|
}
|
|
@@ -772,8 +760,21 @@ function readWorkspaceCheckoutDurabilityRecord(workspacePath) {
|
|
|
772
760
|
return unverified.record;
|
|
773
761
|
}
|
|
774
762
|
async function readWorkspaceCheckoutDurabilityRecordAsync(workspacePath) {
|
|
775
|
-
const
|
|
776
|
-
|
|
763
|
+
const recordPath = workspaceCheckoutDurabilityPath(workspacePath);
|
|
764
|
+
let status;
|
|
765
|
+
try {
|
|
766
|
+
status = await import_node_fs.default.promises.lstat(recordPath);
|
|
767
|
+
} catch (error) {
|
|
768
|
+
if (error.code === "ENOENT") return null;
|
|
769
|
+
throw error;
|
|
770
|
+
}
|
|
771
|
+
if (!status.isFile() || status.isSymbolicLink()) {
|
|
772
|
+
throw new Error(`Workspace checkout durability record is not a regular file: ${recordPath}`);
|
|
773
|
+
}
|
|
774
|
+
const unverified = {
|
|
775
|
+
record: parseWorkspaceCheckoutDurabilityRecordUnverified(recordPath, await import_node_fs.default.promises.readFile(recordPath, "utf8")),
|
|
776
|
+
recordPath
|
|
777
|
+
};
|
|
777
778
|
if (unverified.record.head && !await tryGitAsync(workspacePath, ["cat-file", "-e", `${unverified.record.head}^{commit}`])) {
|
|
778
779
|
throw new Error(`Workspace checkout durability record names a missing commit: ${unverified.recordPath}`);
|
|
779
780
|
}
|
|
@@ -800,13 +801,6 @@ function updateWorkspaceCheckoutDurabilityRecord(workspacePath, record) {
|
|
|
800
801
|
readWorkspaceCheckoutDurabilityRecord(workspacePath);
|
|
801
802
|
writeWorkspaceCheckoutDurabilityRecordFile(workspacePath, record);
|
|
802
803
|
}
|
|
803
|
-
async function updateWorkspaceCheckoutDurabilityRecordAsync(workspacePath, record) {
|
|
804
|
-
if (record.head && !await tryGitAsync(workspacePath, ["cat-file", "-e", `${record.head}^{commit}`])) {
|
|
805
|
-
throw new Error(`Cannot record workspace checkout durability for a missing commit: ${record.head}`);
|
|
806
|
-
}
|
|
807
|
-
await readWorkspaceCheckoutDurabilityRecordAsync(workspacePath);
|
|
808
|
-
writeWorkspaceCheckoutDurabilityRecordFile(workspacePath, record);
|
|
809
|
-
}
|
|
810
804
|
function fsyncWorkspaceCheckoutTree(workspacePath) {
|
|
811
805
|
const status = import_node_fs.default.lstatSync(workspacePath);
|
|
812
806
|
if (!status.isDirectory() || status.isSymbolicLink()) {
|
|
@@ -818,40 +812,6 @@ function fsyncWorkspaceCheckoutTree(workspacePath) {
|
|
|
818
812
|
}
|
|
819
813
|
fsyncDirectory(workspacePath);
|
|
820
814
|
}
|
|
821
|
-
const WORKSPACE_FSYNC_YIELD_INTERVAL = 64;
|
|
822
|
-
async function fsyncWorkspaceCheckoutTreeYielding(workspacePath) {
|
|
823
|
-
const status = import_node_fs.default.lstatSync(workspacePath);
|
|
824
|
-
if (!status.isDirectory() || status.isSymbolicLink()) {
|
|
825
|
-
throw new Error(`Workspace clone root is not a regular directory: ${workspacePath}`);
|
|
826
|
-
}
|
|
827
|
-
let entriesSinceYield = 0;
|
|
828
|
-
const syncEntry = async (entryPath) => {
|
|
829
|
-
const entryStatus = import_node_fs.default.lstatSync(entryPath);
|
|
830
|
-
if (entryStatus.isSymbolicLink()) return;
|
|
831
|
-
if (entryStatus.isDirectory()) {
|
|
832
|
-
for (const name of import_node_fs.default.readdirSync(entryPath)) await syncEntry(import_node_path.default.join(entryPath, name));
|
|
833
|
-
fsyncDirectory(entryPath);
|
|
834
|
-
return;
|
|
835
|
-
}
|
|
836
|
-
if (!entryStatus.isFile()) return;
|
|
837
|
-
const descriptor = import_node_fs.default.openSync(entryPath, import_node_fs.default.constants.O_RDONLY | import_node_fs.default.constants.O_NOFOLLOW);
|
|
838
|
-
try {
|
|
839
|
-
import_node_fs.default.fsyncSync(descriptor);
|
|
840
|
-
} finally {
|
|
841
|
-
import_node_fs.default.closeSync(descriptor);
|
|
842
|
-
}
|
|
843
|
-
entriesSinceYield += 1;
|
|
844
|
-
if (entriesSinceYield >= WORKSPACE_FSYNC_YIELD_INTERVAL) {
|
|
845
|
-
entriesSinceYield = 0;
|
|
846
|
-
await yieldToWorkerEventLoop();
|
|
847
|
-
}
|
|
848
|
-
};
|
|
849
|
-
for (const name of import_node_fs.default.readdirSync(workspacePath)) {
|
|
850
|
-
if (name === ".git") continue;
|
|
851
|
-
await syncEntry(import_node_path.default.join(workspacePath, name));
|
|
852
|
-
}
|
|
853
|
-
fsyncDirectory(workspacePath);
|
|
854
|
-
}
|
|
855
815
|
function workspaceRebaseInProgress(workspacePath) {
|
|
856
816
|
const gitDirectory = import_node_path.default.join(workspacePath, ".git");
|
|
857
817
|
return import_node_fs.default.existsSync(import_node_path.default.join(gitDirectory, "rebase-merge")) || import_node_fs.default.existsSync(import_node_path.default.join(gitDirectory, "rebase-apply"));
|
|
@@ -920,22 +880,36 @@ async function workspaceCheckoutDurabilityIsCurrentAsync(workspacePath) {
|
|
|
920
880
|
const record = await readWorkspaceCheckoutDurabilityRecordAsync(workspacePath);
|
|
921
881
|
return Boolean(record?.state === "durable" && record.head === await revParseAsync(workspacePath, "HEAD"));
|
|
922
882
|
}
|
|
923
|
-
function
|
|
883
|
+
function runCheckoutTransitionBeginJob(input) {
|
|
884
|
+
const workspacePath = import_node_path.default.resolve(input.workspacePath);
|
|
924
885
|
recoverWorkspaceCheckoutDurability(workspacePath);
|
|
925
886
|
const head = revParse(workspacePath, "HEAD");
|
|
926
887
|
updateWorkspaceCheckoutDurabilityRecord(workspacePath, { version: 1, state: "transition", head });
|
|
888
|
+
return null;
|
|
889
|
+
}
|
|
890
|
+
function runCheckoutTransitionCompleteJob(input) {
|
|
891
|
+
const workspacePath = import_node_path.default.resolve(input.workspacePath);
|
|
892
|
+
fsyncWorkspaceCheckoutTree(workspacePath);
|
|
893
|
+
updateWorkspaceCheckoutDurabilityRecord(workspacePath, { version: 1, state: "durable", head: revParse(workspacePath, "HEAD") });
|
|
894
|
+
return null;
|
|
895
|
+
}
|
|
896
|
+
function runCheckoutDurabilityRecoveryJob(input) {
|
|
897
|
+
recoverWorkspaceCheckoutDurability(import_node_path.default.resolve(input.workspacePath), input.preserveResolutionInProgress);
|
|
898
|
+
return null;
|
|
899
|
+
}
|
|
900
|
+
function runOuterCheckoutFsyncJob(input) {
|
|
901
|
+
fsyncWorkspaceCheckoutTree(import_node_path.default.resolve(input.workspacePath));
|
|
902
|
+
return null;
|
|
903
|
+
}
|
|
904
|
+
async function beginWorkspaceCheckoutTransition(workspacePath) {
|
|
905
|
+
await (0, import_workspace_filesystem_executor.workspaceFilesystemExecutor)().run("checkout_transition_begin", { workspacePath });
|
|
927
906
|
}
|
|
928
907
|
async function completeWorkspaceCheckoutTransition(workspacePath) {
|
|
929
|
-
await
|
|
930
|
-
await updateWorkspaceCheckoutDurabilityRecordAsync(workspacePath, {
|
|
931
|
-
version: 1,
|
|
932
|
-
state: "durable",
|
|
933
|
-
head: await revParseAsync(workspacePath, "HEAD")
|
|
934
|
-
});
|
|
908
|
+
await (0, import_workspace_filesystem_executor.workspaceFilesystemExecutor)().run("checkout_transition_complete", { workspacePath });
|
|
935
909
|
}
|
|
936
|
-
function throwAfterWorkspaceCheckoutRecovery(workspacePath, error) {
|
|
910
|
+
async function throwAfterWorkspaceCheckoutRecovery(workspacePath, error) {
|
|
937
911
|
try {
|
|
938
|
-
|
|
912
|
+
await (0, import_workspace_filesystem_executor.workspaceFilesystemExecutor)().run("checkout_durability_recovery", { workspacePath, preserveResolutionInProgress: false });
|
|
939
913
|
} catch (recoveryError) {
|
|
940
914
|
throw new AggregateError(
|
|
941
915
|
[error, recoveryError],
|
|
@@ -945,12 +919,12 @@ function throwAfterWorkspaceCheckoutRecovery(workspacePath, error) {
|
|
|
945
919
|
throw error;
|
|
946
920
|
}
|
|
947
921
|
async function runWorkspaceCheckoutTransition(workspacePath, mutate) {
|
|
948
|
-
beginWorkspaceCheckoutTransition(workspacePath);
|
|
922
|
+
await beginWorkspaceCheckoutTransition(workspacePath);
|
|
949
923
|
try {
|
|
950
924
|
await mutate();
|
|
951
925
|
await completeWorkspaceCheckoutTransition(workspacePath);
|
|
952
926
|
} catch (error) {
|
|
953
|
-
throwAfterWorkspaceCheckoutRecovery(workspacePath, error);
|
|
927
|
+
await throwAfterWorkspaceCheckoutRecovery(workspacePath, error);
|
|
954
928
|
}
|
|
955
929
|
}
|
|
956
930
|
function parseHydrationTransactionManifest(transactionPath) {
|
|
@@ -1332,8 +1306,16 @@ async function configureWorkspaceRepository(input) {
|
|
|
1332
1306
|
await gitAsync(input.workspacePath, ["remote", "add", "origin", input.remoteUrl], "configure workspace origin");
|
|
1333
1307
|
}
|
|
1334
1308
|
await tryGitAsync(input.workspacePath, ["config", "--local", "--unset-all", "remote.origin.pushurl"]);
|
|
1335
|
-
await gitAsync(
|
|
1336
|
-
|
|
1309
|
+
await gitAsync(
|
|
1310
|
+
input.workspacePath,
|
|
1311
|
+
["config", "--local", "--replace-all", "credential.helper", ""],
|
|
1312
|
+
"reset workspace credential helpers"
|
|
1313
|
+
);
|
|
1314
|
+
await gitAsync(
|
|
1315
|
+
input.workspacePath,
|
|
1316
|
+
["config", "--local", "credential.useHttpPath", "false"],
|
|
1317
|
+
"configure workspace credential path matching"
|
|
1318
|
+
);
|
|
1337
1319
|
if (input.credentialUsername) {
|
|
1338
1320
|
await gitAsync(
|
|
1339
1321
|
input.workspacePath,
|
|
@@ -1431,19 +1413,24 @@ async function ensureWorkspaceGitClone(input) {
|
|
|
1431
1413
|
input.credentialHelper,
|
|
1432
1414
|
input.credentialUsername
|
|
1433
1415
|
);
|
|
1434
|
-
const clone = Bun.
|
|
1416
|
+
const clone = Bun.spawn(cloneCommand, {
|
|
1417
|
+
stdin: "ignore",
|
|
1435
1418
|
stdout: "pipe",
|
|
1436
1419
|
stderr: "pipe",
|
|
1437
1420
|
env: (0, import_git_process_environment.workerGitProcessEnvironment)()
|
|
1438
1421
|
});
|
|
1439
|
-
|
|
1422
|
+
const [, , cloneExitCode] = await Promise.all([new Response(clone.stdout).text(), new Response(clone.stderr).text(), clone.exited]);
|
|
1423
|
+
if (cloneExitCode !== 0) {
|
|
1440
1424
|
import_node_fs.default.rmSync(workspacePath, { recursive: true, force: true });
|
|
1441
1425
|
import_node_fs.default.mkdirSync(workspacePath, { recursive: true });
|
|
1442
1426
|
git(workspacePath, ["init", `--initial-branch=${WORKSPACE_GIT_BRANCH}`], "initialize workspace clone");
|
|
1443
1427
|
}
|
|
1444
1428
|
}
|
|
1445
1429
|
await configureWorkspaceRepository({ ...input, workspacePath });
|
|
1446
|
-
|
|
1430
|
+
await (0, import_workspace_filesystem_executor.workspaceFilesystemExecutor)().run("checkout_durability_recovery", {
|
|
1431
|
+
workspacePath,
|
|
1432
|
+
preserveResolutionInProgress: input.preserveResolutionInProgress === true
|
|
1433
|
+
});
|
|
1447
1434
|
const previousRemoteHead = await revParseAsync(workspacePath, `refs/remotes/origin/${WORKSPACE_GIT_BRANCH}`);
|
|
1448
1435
|
const localHeadBeforeFetch = await revParseAsync(workspacePath, "HEAD");
|
|
1449
1436
|
if (!await revParseAsync(workspacePath, WORKSPACE_GIT_INTEGRATED_REF) && previousRemoteHead && localHeadBeforeFetch && await tryGitAsync(workspacePath, ["merge-base", "--is-ancestor", previousRemoteHead, localHeadBeforeFetch])) {
|
|
@@ -1722,7 +1709,7 @@ async function hydrateWorkspaceGitMountsTransactionally(input) {
|
|
|
1722
1709
|
};
|
|
1723
1710
|
};
|
|
1724
1711
|
const noopCandidate = computeSelection();
|
|
1725
|
-
if (noopCandidate.hydrationMounts.length === 0 && noopCandidate.advancedDurabilityMounts.length === 0 && noopCandidate.targetReceipt && lstatIfExists(hydrationTransactionPath(workspacePath)) === null && workspaceHydrationReceiptsEqual(receiptBeforeTransaction, noopCandidate.targetReceipt) && hydratedWorkspaceReceiptFileIsV3(workspacePath) && await workspaceBasisRefsAreCurrentAsync(workspacePath, noopCandidate.targetReceipt)) {
|
|
1712
|
+
if (noopCandidate.hydrationMounts.length === 0 && noopCandidate.advancedDurabilityMounts.length === 0 && noopCandidate.targetReceipt && lstatIfExists(hydrationTransactionPath(workspacePath)) === null && workspaceHydrationReceiptsEqual(receiptBeforeTransaction, noopCandidate.targetReceipt) && await hydratedWorkspaceReceiptFileIsV3(workspacePath) && await workspaceBasisRefsAreCurrentAsync(workspacePath, noopCandidate.targetReceipt)) {
|
|
1726
1713
|
return { hydratedMountIds: [], skippedMountIds: noopCandidate.skippedMountIds };
|
|
1727
1714
|
}
|
|
1728
1715
|
const hooks = input.hydrationHooks;
|
|
@@ -1735,47 +1722,92 @@ async function hydrateWorkspaceGitMountsTransactionally(input) {
|
|
|
1735
1722
|
try {
|
|
1736
1723
|
const selection = computeSelection();
|
|
1737
1724
|
const { hydrationMounts, advancedDurabilityMounts, targetReceipt } = selection;
|
|
1738
|
-
const
|
|
1725
|
+
const heldMountIds = [...new Set([...hydrationMounts, ...advancedDurabilityMounts].map(({ id }) => id))];
|
|
1726
|
+
const releaseHold = hooks?.holdMounts(heldMountIds, `hydration transaction ${context.transactionId}`);
|
|
1739
1727
|
try {
|
|
1740
|
-
const hydration =
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
return {
|
|
1750
|
-
hydratedMountIds: hydration.hydratedMountIds,
|
|
1751
|
-
skippedMountIds: selection.skippedMountIds
|
|
1752
|
-
};
|
|
1753
|
-
} catch (error) {
|
|
1754
|
-
const transactionPath = hydrationTransactionPath(workspacePath);
|
|
1755
|
-
if (lstatIfExists(transactionPath) && hydrationTransactionCommitted(transactionPath)) {
|
|
1756
|
-
throw error;
|
|
1757
|
-
}
|
|
1758
|
-
try {
|
|
1759
|
-
restoreHydrationTransaction(workspacePath, manifest);
|
|
1760
|
-
removeHydrationTransaction(workspacePath);
|
|
1761
|
-
} catch (restoreError) {
|
|
1762
|
-
throw new AggregateError(
|
|
1763
|
-
[error, restoreError],
|
|
1764
|
-
`Workspace hydration failed and its durable snapshot could not be restored: ${error instanceof Error ? error.message : String(error)}`
|
|
1765
|
-
);
|
|
1766
|
-
}
|
|
1767
|
-
throw error;
|
|
1728
|
+
const hydration = await (0, import_workspace_filesystem_executor.workspaceFilesystemExecutor)().run("hydration_transaction", {
|
|
1729
|
+
workspacePath,
|
|
1730
|
+
hydrationMounts: hydrationMounts.map(workspaceGitMountData),
|
|
1731
|
+
advancedDurabilityMounts: advancedDurabilityMounts.map(workspaceGitMountData),
|
|
1732
|
+
targetReceipt
|
|
1733
|
+
});
|
|
1734
|
+
return { hydratedMountIds: hydration.hydratedMountIds, skippedMountIds: selection.skippedMountIds };
|
|
1735
|
+
} finally {
|
|
1736
|
+
releaseHold?.();
|
|
1768
1737
|
}
|
|
1769
1738
|
} finally {
|
|
1770
1739
|
hooks?.afterTransaction(context);
|
|
1771
1740
|
}
|
|
1772
1741
|
}
|
|
1742
|
+
function runHydrationTransactionJob(input) {
|
|
1743
|
+
const workspacePath = import_node_path.default.resolve(input.workspacePath);
|
|
1744
|
+
const { hydrationMounts, advancedDurabilityMounts, targetReceipt } = input;
|
|
1745
|
+
const { manifest, durabilityScopes } = beginHydrationTransaction(workspacePath, hydrationMounts, targetReceipt);
|
|
1746
|
+
try {
|
|
1747
|
+
const hydration = hydrateWorkspaceGitMountsRaw(workspacePath, hydrationMounts, { ignoreBusy: true });
|
|
1748
|
+
const expectedHydratedIds = hydrationMounts.map(({ id }) => id).sort();
|
|
1749
|
+
if (hydration.skippedMountIds.length > 0 || hydration.hydratedMountIds.length !== expectedHydratedIds.length || hydration.hydratedMountIds.some((id, index) => id !== expectedHydratedIds[index])) {
|
|
1750
|
+
throw new Error("Workspace hydration did not include every required mount");
|
|
1751
|
+
}
|
|
1752
|
+
fsyncHydratedWorkspaceMounts(advancedDurabilityMounts, durabilityScopes);
|
|
1753
|
+
markHydrationTransactionDurable(workspacePath);
|
|
1754
|
+
if (targetReceipt) updateHydratedWorkspaceReceipt(workspacePath, targetReceipt);
|
|
1755
|
+
removeHydrationTransaction(workspacePath);
|
|
1756
|
+
return { hydratedMountIds: hydration.hydratedMountIds };
|
|
1757
|
+
} catch (error) {
|
|
1758
|
+
const transactionPath = hydrationTransactionPath(workspacePath);
|
|
1759
|
+
if (lstatIfExists(transactionPath) && hydrationTransactionCommitted(transactionPath)) {
|
|
1760
|
+
throw error;
|
|
1761
|
+
}
|
|
1762
|
+
try {
|
|
1763
|
+
restoreHydrationTransaction(workspacePath, manifest);
|
|
1764
|
+
removeHydrationTransaction(workspacePath);
|
|
1765
|
+
} catch (restoreError) {
|
|
1766
|
+
throw new AggregateError(
|
|
1767
|
+
[error, restoreError],
|
|
1768
|
+
`Workspace hydration failed and its durable snapshot could not be restored: ${error instanceof Error ? error.message : String(error)}`
|
|
1769
|
+
);
|
|
1770
|
+
}
|
|
1771
|
+
throw error;
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
function runHydrationRawJob(input) {
|
|
1775
|
+
return {
|
|
1776
|
+
hydratedMountIds: hydrateWorkspaceGitMountsRaw(import_node_path.default.resolve(input.workspacePath), input.mounts, { ignoreBusy: true }).hydratedMountIds
|
|
1777
|
+
};
|
|
1778
|
+
}
|
|
1779
|
+
function runHydrationRecoveryJob(input) {
|
|
1780
|
+
const workspacePath = import_node_path.default.resolve(input.workspacePath);
|
|
1781
|
+
recoverWorkspaceCheckoutDurability(workspacePath, input.preserveResolutionInProgress);
|
|
1782
|
+
recoverPendingHydrationTransaction(workspacePath);
|
|
1783
|
+
repairWorkspaceBasisRefs(workspacePath, readHydratedWorkspaceReceipt(workspacePath));
|
|
1784
|
+
return null;
|
|
1785
|
+
}
|
|
1773
1786
|
async function hydrateWorkspaceGitMounts(workspacePath, mounts, options = {}) {
|
|
1774
|
-
const
|
|
1787
|
+
const resolvedWorkspacePath = import_node_path.default.resolve(workspacePath);
|
|
1788
|
+
const gitDirectory = import_node_path.default.join(resolvedWorkspacePath, ".git");
|
|
1775
1789
|
if (!import_node_fs.default.existsSync(gitDirectory)) {
|
|
1776
|
-
|
|
1790
|
+
const skippedMountIds = options.ignoreBusy ? [] : mounts.filter((mount) => mount.busy?.()).map(({ id }) => id).sort();
|
|
1791
|
+
const skipped = new Set(skippedMountIds);
|
|
1792
|
+
const selected = mounts.filter(({ id }) => !skipped.has(id));
|
|
1793
|
+
const releaseHold = options.hydrationHooks?.holdMounts(
|
|
1794
|
+
selected.map(({ id }) => id),
|
|
1795
|
+
"pre-clone hydration"
|
|
1796
|
+
);
|
|
1797
|
+
try {
|
|
1798
|
+
const hydration = await (0, import_workspace_filesystem_executor.workspaceFilesystemExecutor)().run("hydration_raw", {
|
|
1799
|
+
workspacePath: resolvedWorkspacePath,
|
|
1800
|
+
mounts: selected.map(workspaceGitMountData)
|
|
1801
|
+
});
|
|
1802
|
+
return { hydratedMountIds: hydration.hydratedMountIds, skippedMountIds };
|
|
1803
|
+
} finally {
|
|
1804
|
+
releaseHold?.();
|
|
1805
|
+
}
|
|
1777
1806
|
}
|
|
1778
|
-
|
|
1807
|
+
await (0, import_workspace_filesystem_executor.workspaceFilesystemExecutor)().run("checkout_durability_recovery", {
|
|
1808
|
+
workspacePath: resolvedWorkspacePath,
|
|
1809
|
+
preserveResolutionInProgress: false
|
|
1810
|
+
});
|
|
1779
1811
|
return hydrateWorkspaceGitMountsTransactionally({
|
|
1780
1812
|
workspacePath,
|
|
1781
1813
|
mounts,
|
|
@@ -1786,13 +1818,12 @@ async function hydrateWorkspaceGitMounts(workspacePath, mounts, options = {}) {
|
|
|
1786
1818
|
async function recoverWorkspaceGitHydration(workspacePath, mounts, options = {}) {
|
|
1787
1819
|
workspacePath = import_node_path.default.resolve(workspacePath);
|
|
1788
1820
|
validateMounts(workspacePath, mounts);
|
|
1789
|
-
|
|
1790
|
-
|
|
1821
|
+
await (0, import_workspace_filesystem_executor.workspaceFilesystemExecutor)().run("hydration_recovery", {
|
|
1822
|
+
workspacePath,
|
|
1823
|
+
preserveResolutionInProgress: options.preserveResolutionInProgress === true
|
|
1824
|
+
});
|
|
1791
1825
|
const currentHead = await revParseAsync(workspacePath, "HEAD");
|
|
1792
1826
|
const currentReceipt = await readHydratedWorkspaceReceiptAsync(workspacePath);
|
|
1793
|
-
if (!await workspaceBasisRefsAreCurrentAsync(workspacePath, currentReceipt ?? { version: 3, mounts: [] })) {
|
|
1794
|
-
repairWorkspaceBasisRefs(workspacePath, currentReceipt);
|
|
1795
|
-
}
|
|
1796
1827
|
if (!currentHead) return;
|
|
1797
1828
|
const outerHeadContainsMount = (mount) => tryGitAsync(workspacePath, ["cat-file", "-e", `HEAD:${normalizedWorkspaceMountPath(mount.workspaceRelativePath)}`]);
|
|
1798
1829
|
const configuredBasisMounts = configuredWorkspaceHydrationBasisMounts(workspacePath, mounts, options.deferMountIds);
|
|
@@ -1833,13 +1864,17 @@ async function resetWorkspaceGit(input) {
|
|
|
1833
1864
|
throw new Error(`Workspace reset cannot run while mounts are busy: ${busyResetMountIds.sort().join(", ")}`);
|
|
1834
1865
|
}
|
|
1835
1866
|
const initial = await ensureWorkspaceGitClone({ ...input, workspacePath });
|
|
1836
|
-
|
|
1867
|
+
await (0, import_workspace_filesystem_executor.workspaceFilesystemExecutor)().run("hydration_recovery", { workspacePath, preserveResolutionInProgress: false });
|
|
1837
1868
|
const status = gitResult(workspacePath, ["status", "--porcelain=v1", "-z"]);
|
|
1838
1869
|
const discardedPaths = status.exitCode === 0 ? status.stdout.split("\0").filter(Boolean).map((entry) => entry.slice(3)).sort() : [];
|
|
1839
1870
|
const remoteHead = await fetchWorkspaceHead(workspacePath, input.remoteUrl, input.credentialHelper, input.credentialUsername);
|
|
1840
1871
|
if (remoteHead) {
|
|
1841
1872
|
await runWorkspaceCheckoutTransition(workspacePath, async () => {
|
|
1842
|
-
await gitAsync(
|
|
1873
|
+
await gitAsync(
|
|
1874
|
+
workspacePath,
|
|
1875
|
+
["checkout", "--no-recurse-submodules", "-B", WORKSPACE_GIT_BRANCH, remoteHead],
|
|
1876
|
+
"reset workspace main"
|
|
1877
|
+
);
|
|
1843
1878
|
await gitAsync(workspacePath, ["clean", "-fd", "--", "."], "remove untracked workspace changes");
|
|
1844
1879
|
});
|
|
1845
1880
|
} else if (revParse(workspacePath, "HEAD")) {
|
|
@@ -2050,7 +2085,7 @@ function snapshotConflict(input) {
|
|
|
2050
2085
|
}
|
|
2051
2086
|
async function rebaseWorkspace(input) {
|
|
2052
2087
|
const remoteRevision = `refs/remotes/origin/${WORKSPACE_GIT_BRANCH}`;
|
|
2053
|
-
beginWorkspaceCheckoutTransition(input.workspacePath);
|
|
2088
|
+
await beginWorkspaceCheckoutTransition(input.workspacePath);
|
|
2054
2089
|
const rebase = await gitResultAsync(
|
|
2055
2090
|
input.workspacePath,
|
|
2056
2091
|
input.truncatedHistoryBase ? ["rebase", "--onto", remoteRevision, input.truncatedHistoryBase] : ["rebase", remoteRevision]
|
|
@@ -2220,18 +2255,24 @@ async function runWorkspaceGitSynchronization(input, remoteHeadFetches) {
|
|
|
2220
2255
|
cycleSkippedMountIds.add(mount.id);
|
|
2221
2256
|
continue;
|
|
2222
2257
|
}
|
|
2223
|
-
const
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2258
|
+
const releaseMergeHold = input.hydrationHooks?.holdMounts([mount.id], `merge projection of mount ${mount.id}`);
|
|
2259
|
+
let merged;
|
|
2260
|
+
try {
|
|
2261
|
+
merged = await (0, import_workspace_filesystem_executor.workspaceFilesystemExecutor)().run("projection_merge", {
|
|
2262
|
+
workspacePath,
|
|
2263
|
+
mount: {
|
|
2264
|
+
id: mount.id,
|
|
2265
|
+
sourcePath: mount.sourcePath,
|
|
2266
|
+
workspaceRelativePath: normalizedWorkspaceMountPath(mount.workspaceRelativePath),
|
|
2267
|
+
sourceMode: mount.sourceMode
|
|
2268
|
+
},
|
|
2269
|
+
basisHead,
|
|
2270
|
+
currentHead: projectionBasisHead,
|
|
2271
|
+
attemptId
|
|
2272
|
+
});
|
|
2273
|
+
} finally {
|
|
2274
|
+
releaseMergeHold?.();
|
|
2275
|
+
}
|
|
2235
2276
|
if (merged.kind === "conflict") {
|
|
2236
2277
|
const refs = snapshotConflict({ workspacePath, attemptId, localHead: merged.oursCommit, remoteHead: projectionBasisHead });
|
|
2237
2278
|
return {
|
|
@@ -2254,7 +2295,6 @@ async function runWorkspaceGitSynchronization(input, remoteHeadFetches) {
|
|
|
2254
2295
|
}
|
|
2255
2296
|
mergedProjectionTrees.set(mount.id, merged.resultTree);
|
|
2256
2297
|
mergedProjectionMounts.push(mount);
|
|
2257
|
-
await yieldToWorkerEventLoop();
|
|
2258
2298
|
}
|
|
2259
2299
|
}
|
|
2260
2300
|
if (projectionWarning) process.stderr.write(`[r5d-worker] ${projectionWarning}
|
|
@@ -2337,7 +2377,7 @@ async function runWorkspaceGitSynchronization(input, remoteHeadFetches) {
|
|
|
2337
2377
|
const projectedFastMounts = [];
|
|
2338
2378
|
const currentProjectionMounts = [];
|
|
2339
2379
|
if (!input.skipMountMirror) {
|
|
2340
|
-
beginWorkspaceCheckoutTransition(workspacePath);
|
|
2380
|
+
await beginWorkspaceCheckoutTransition(workspacePath);
|
|
2341
2381
|
try {
|
|
2342
2382
|
const selectedActiveIds = new Set(selected.active.map(({ id }) => id));
|
|
2343
2383
|
const fastActiveMounts = fastProjectionMounts.filter(({ id }) => selectedActiveIds.has(id));
|
|
@@ -2351,30 +2391,34 @@ async function runWorkspaceGitSynchronization(input, remoteHeadFetches) {
|
|
|
2351
2391
|
currentProjectionMounts.push(mount);
|
|
2352
2392
|
continue;
|
|
2353
2393
|
}
|
|
2354
|
-
|
|
2394
|
+
const releaseMirrorHold = input.hydrationHooks?.holdMounts([mount.id], `projection of mount ${mount.id}`);
|
|
2395
|
+
try {
|
|
2396
|
+
const mirrored = await (0, import_workspace_filesystem_executor.workspaceFilesystemExecutor)().run("projection_mirror", {
|
|
2397
|
+
workspacePath,
|
|
2398
|
+
mount: workspaceGitMountData(mount)
|
|
2399
|
+
});
|
|
2400
|
+
projectedGitlinks.push({ mount, gitlinks: mirrored.gitlinks });
|
|
2401
|
+
} finally {
|
|
2402
|
+
releaseMirrorHold?.();
|
|
2403
|
+
}
|
|
2355
2404
|
projectedFastMounts.push(mount);
|
|
2356
|
-
await yieldToWorkerEventLoop();
|
|
2357
2405
|
}
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
workspacePath,
|
|
2406
|
+
await (0, import_workspace_filesystem_executor.workspaceFilesystemExecutor)().run("projection_outer_apply", {
|
|
2407
|
+
workspacePath,
|
|
2408
|
+
removeMounts: fastTombstones.map(workspaceGitMountData),
|
|
2409
|
+
materialize: mergedProjectionMounts.map((mount) => ({
|
|
2363
2410
|
workspaceRelativePath: normalizedWorkspaceMountPath(mount.workspaceRelativePath),
|
|
2364
2411
|
resultTree: mergedProjectionTrees.get(mount.id)
|
|
2365
|
-
})
|
|
2366
|
-
|
|
2367
|
-
}
|
|
2368
|
-
restoreDeferredWorkspaceMountsFromHead(
|
|
2369
|
-
workspacePath,
|
|
2370
|
-
input.mounts.filter(
|
|
2412
|
+
})),
|
|
2413
|
+
restoreFromHead: input.mounts.filter(
|
|
2371
2414
|
(mount) => deferredMountIds.has(mount.id) || mount.preserveLocalOnHydrationBasisChange === true && cycleSkippedMountIds.has(mount.id)
|
|
2372
|
-
)
|
|
2373
|
-
);
|
|
2415
|
+
).map(workspaceGitMountData)
|
|
2416
|
+
});
|
|
2417
|
+
projectedFastMounts.push(...fastTombstones);
|
|
2374
2418
|
await stageAndCommitWorkspace();
|
|
2375
|
-
await
|
|
2419
|
+
await (0, import_workspace_filesystem_executor.workspaceFilesystemExecutor)().run("outer_checkout_fsync", { workspacePath });
|
|
2376
2420
|
} catch (error) {
|
|
2377
|
-
throwAfterWorkspaceCheckoutRecovery(workspacePath, error);
|
|
2421
|
+
await throwAfterWorkspaceCheckoutRecovery(workspacePath, error);
|
|
2378
2422
|
}
|
|
2379
2423
|
} else {
|
|
2380
2424
|
await stageAndCommitWorkspace();
|
|
@@ -2390,12 +2434,12 @@ async function runWorkspaceGitSynchronization(input, remoteHeadFetches) {
|
|
|
2390
2434
|
allProjectionReceiptMounts
|
|
2391
2435
|
);
|
|
2392
2436
|
const currentProjectionReceipt = await readHydratedWorkspaceReceiptAsync(workspacePath);
|
|
2393
|
-
if (!workspaceHydrationReceiptsEqual(currentProjectionReceipt, targetProjectionReceipt) || !hydratedWorkspaceReceiptFileIsV3(workspacePath) || !await workspaceBasisRefsAreCurrentAsync(workspacePath, targetProjectionReceipt)) {
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2437
|
+
if (!workspaceHydrationReceiptsEqual(currentProjectionReceipt, targetProjectionReceipt) || !await hydratedWorkspaceReceiptFileIsV3(workspacePath) || !await workspaceBasisRefsAreCurrentAsync(workspacePath, targetProjectionReceipt)) {
|
|
2438
|
+
await (0, import_workspace_filesystem_executor.workspaceFilesystemExecutor)().run("projection_receipt", {
|
|
2439
|
+
workspacePath,
|
|
2440
|
+
fsyncMounts: projectedFastMounts.map(workspaceGitMountData),
|
|
2441
|
+
receipt: targetProjectionReceipt
|
|
2442
|
+
});
|
|
2399
2443
|
}
|
|
2400
2444
|
await completeWorkspaceCheckoutTransition(workspacePath);
|
|
2401
2445
|
}
|
|
@@ -2562,6 +2606,25 @@ ${push.stdout}`)) {
|
|
|
2562
2606
|
}
|
|
2563
2607
|
throw new Error(`Workspace push did not converge after ${maxPushAttempts} attempts`);
|
|
2564
2608
|
}
|
|
2609
|
+
function runProjectionMirrorJob(input) {
|
|
2610
|
+
const projected = mirrorMountsToWorkspace(import_node_path.default.resolve(input.workspacePath), [input.mount]);
|
|
2611
|
+
return { gitlinks: projected[0]?.gitlinks ?? [] };
|
|
2612
|
+
}
|
|
2613
|
+
function runProjectionOuterApplyJob(input) {
|
|
2614
|
+
const workspacePath = import_node_path.default.resolve(input.workspacePath);
|
|
2615
|
+
removeWorkspaceMounts(workspacePath, input.removeMounts);
|
|
2616
|
+
for (const { workspaceRelativePath, resultTree } of input.materialize) {
|
|
2617
|
+
(0, import_workspace_merge_projection.materializeWorkspaceProjectionTree)({ workspacePath, workspaceRelativePath, resultTree });
|
|
2618
|
+
}
|
|
2619
|
+
restoreDeferredWorkspaceMountsFromHead(workspacePath, input.restoreFromHead);
|
|
2620
|
+
return null;
|
|
2621
|
+
}
|
|
2622
|
+
function runProjectionReceiptJob(input) {
|
|
2623
|
+
const workspacePath = import_node_path.default.resolve(input.workspacePath);
|
|
2624
|
+
for (const mount of input.fsyncMounts) fsyncHydratedWorkspaceMount(mount);
|
|
2625
|
+
updateHydratedWorkspaceReceipt(workspacePath, input.receipt);
|
|
2626
|
+
return null;
|
|
2627
|
+
}
|
|
2565
2628
|
const workspaceGitSyncTestHarness = {
|
|
2566
2629
|
commandArgs: gitCommandArgs,
|
|
2567
2630
|
workspaceCloneCommandArgs,
|
|
@@ -2583,9 +2646,20 @@ const workspaceGitSyncTestHarness = {
|
|
|
2583
2646
|
hydrateWorkspaceGitMounts,
|
|
2584
2647
|
recoverWorkspaceGitHydration,
|
|
2585
2648
|
resetWorkspaceGit,
|
|
2649
|
+
runCheckoutDurabilityRecoveryJob,
|
|
2650
|
+
runCheckoutTransitionBeginJob,
|
|
2651
|
+
runCheckoutTransitionCompleteJob,
|
|
2652
|
+
runHydrationRawJob,
|
|
2653
|
+
runHydrationRecoveryJob,
|
|
2654
|
+
runHydrationTransactionJob,
|
|
2655
|
+
runOuterCheckoutFsyncJob,
|
|
2656
|
+
runProjectionMirrorJob,
|
|
2657
|
+
runProjectionOuterApplyJob,
|
|
2658
|
+
runProjectionReceiptJob,
|
|
2586
2659
|
synchronizeWorkspaceGit,
|
|
2587
2660
|
workspaceGitHydrationIsCurrent,
|
|
2588
2661
|
workspaceGitHydrationIsRecoverable,
|
|
2662
|
+
workspaceGitMountData,
|
|
2589
2663
|
workspaceGitMountsForRemediation,
|
|
2590
2664
|
workspaceGitSyncTestHarness
|
|
2591
2665
|
});
|