@boxes-dev/dvb 0.2.27 → 0.2.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/dvb.cjs +878 -688
- package/dist/bin/dvb.js +2 -1
- package/dist/bin/dvb.js.map +1 -1
- package/dist/bin/dvbd.cjs +47 -16
- package/dist/devbox/commands/destroy.d.ts.map +1 -1
- package/dist/devbox/commands/destroy.js +62 -2
- package/dist/devbox/commands/destroy.js.map +1 -1
- package/dist/devbox/commands/init/codex/local.d.ts +8 -4
- package/dist/devbox/commands/init/codex/local.d.ts.map +1 -1
- package/dist/devbox/commands/init/codex/local.js +36 -12
- package/dist/devbox/commands/init/codex/local.js.map +1 -1
- package/dist/devbox/commands/init/index.d.ts.map +1 -1
- package/dist/devbox/commands/init/index.js +169 -173
- package/dist/devbox/commands/init/index.js.map +1 -1
- package/dist/devbox/commands/init/registry.d.ts +3 -4
- package/dist/devbox/commands/init/registry.d.ts.map +1 -1
- package/dist/devbox/commands/init/registry.js +22 -25
- package/dist/devbox/commands/init/registry.js.map +1 -1
- package/dist/devbox/commands/init/repo.d.ts +4 -1
- package/dist/devbox/commands/init/repo.d.ts.map +1 -1
- package/dist/devbox/commands/init/repo.js +57 -1
- package/dist/devbox/commands/init/repo.js.map +1 -1
- package/dist/devbox/commands/init/state.d.ts +2 -2
- package/dist/devbox/commands/init/state.d.ts.map +1 -1
- package/dist/devbox/commands/init/state.js +22 -7
- package/dist/devbox/commands/init/state.js.map +1 -1
- package/dist/devbox/commands/wezterm.d.ts.map +1 -1
- package/dist/devbox/commands/wezterm.js +29 -5
- package/dist/devbox/commands/wezterm.js.map +1 -1
- package/dist/devbox/completions/index.d.ts.map +1 -1
- package/dist/devbox/completions/index.js +42 -22
- package/dist/devbox/completions/index.js.map +1 -1
- package/dist/devbox/daemonClient.js +2 -2
- package/dist/devbox/daemonClient.js.map +1 -1
- package/dist/prompts/local-scan-env-secrets.md +1 -1
- package/dist/prompts/local-scan-external.md +1 -1
- package/dist/prompts/local-scan-extra-artifacts.md +1 -1
- package/dist/prompts/local-services-scan.md +1 -1
- package/package.json +1 -1
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
1
|
import path from "node:path";
|
|
3
2
|
import fs from "node:fs/promises";
|
|
4
3
|
import os from "node:os";
|
|
5
4
|
import { cancel as clackCancel, confirm as clackConfirm, isCancel, log as clackLog, note as clackNote, select as clackSelect, selectKey as clackSelectKey, taskLog as clackTaskLog, } from "@clack/prompts";
|
|
6
5
|
import { resolveSocketInfo } from "@boxes-dev/core";
|
|
7
|
-
import { createSecretStore, loadConfig, createSpritesClient,
|
|
6
|
+
import { createSecretStore, loadConfig, createSpritesClient, normalizeGitRemoteUrl, resolveSpritesApiUrl, resolveDevboxProjectDir, SpritesApiError, slugify, } from "@boxes-dev/core";
|
|
8
7
|
import { DAEMON_TIMEOUT_MS, ensureDaemonRunning, requestJson, requireDaemonFeatures, } from "../../daemonClient.js";
|
|
9
8
|
import { ensureSpritesToken } from "../../auth.js";
|
|
10
9
|
import { fetchSpriteDaemonRelease, getConvexUrl, issueSpriteDaemonToken, } from "../../controlPlane.js";
|
|
11
10
|
import { logger } from "../../logger.js";
|
|
12
11
|
import { parseInitArgs } from "./args.js";
|
|
13
|
-
import { confirmCopyWorktree, findRepoRoot, mapGlobalGitConfigDestinations, readGlobalGitConfigFiles, readHeadState,
|
|
12
|
+
import { confirmCopyWorktree, findRepoRoot, ensureRepoProjectId, mapGlobalGitConfigDestinations, readGlobalGitConfigFiles, readHeadState, readRepoOrigin, readWorktreeState, resolveGitCommonDir, runCommand, } from "./repo.js";
|
|
14
13
|
import { createFileListArchive, createGitMetaArchive, writePatch } from "./packaging.js";
|
|
15
14
|
import { bootstrapDevbox, ensureSpriteDaemonService, expandHome, ensureWeztermMuxService, installSpriteDaemon, shellQuote, stageRemoteSetupArtifacts, writeRemoteCodexConfig, } from "./remote.js";
|
|
16
15
|
import { ensureWeztermMuxInstalled } from "../../../wezterm/ensureMux.js";
|
|
17
|
-
import {
|
|
16
|
+
import { readRepoMarker, writeRepoMarker } from "./registry.js";
|
|
18
17
|
import { INIT_STEP_KEYS, readInitState, writeInitState } from "./state.js";
|
|
19
18
|
import { ensureSshConfig, ensureSshKey, copyToClipboard, openBrowser, parseGitRemote, readRemoteOrigin, setRemoteOrigin, verifySshAuth, } from "./ssh.js";
|
|
20
19
|
import { ensureKnownHostsFile, ensureLocalMountKey, ensureRemoteMountAccess, ensureSshdService, readLocalMountPublicKey, } from "../mountSsh.js";
|
|
@@ -34,6 +33,54 @@ const mergeLocalPaths = (existing, repoRoot) => {
|
|
|
34
33
|
merged.add(repoRoot);
|
|
35
34
|
return [...merged];
|
|
36
35
|
};
|
|
36
|
+
const ensurePrivateDir = async (dir) => {
|
|
37
|
+
await fs.mkdir(dir, { recursive: true, mode: 0o700 });
|
|
38
|
+
try {
|
|
39
|
+
await fs.chmod(dir, 0o700);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// best effort on filesystems that do not support chmod
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const migrateLegacyRepoDevboxDir = async ({ repoRoot, projectDir, }) => {
|
|
46
|
+
const legacyDir = path.join(repoRoot, ".devbox");
|
|
47
|
+
try {
|
|
48
|
+
const stat = await fs.stat(legacyDir);
|
|
49
|
+
if (!stat.isDirectory())
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
await ensurePrivateDir(projectDir);
|
|
56
|
+
const entries = [
|
|
57
|
+
"box.json",
|
|
58
|
+
"init-state.json",
|
|
59
|
+
"setup.json",
|
|
60
|
+
"services.json",
|
|
61
|
+
"setup-artifacts.tgz",
|
|
62
|
+
"setup-artifacts.json",
|
|
63
|
+
"scans",
|
|
64
|
+
"logs",
|
|
65
|
+
];
|
|
66
|
+
for (const entry of entries) {
|
|
67
|
+
const src = path.join(legacyDir, entry);
|
|
68
|
+
const dest = path.join(projectDir, entry);
|
|
69
|
+
try {
|
|
70
|
+
await fs.access(dest);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// missing -> migrate
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
await fs.cp(src, dest, { recursive: true });
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// best effort
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
37
84
|
const buildServicesTomlUpdates = (services) => {
|
|
38
85
|
const updates = {};
|
|
39
86
|
for (const service of services) {
|
|
@@ -281,14 +328,14 @@ export const runInit = async (args) => {
|
|
|
281
328
|
// - `complete` must only be true when the setup is actually usable without
|
|
282
329
|
// additional `dvb init --resume` work.
|
|
283
330
|
// See: apps/cli/docs/INIT_RECOVERABILITY.md
|
|
284
|
-
const
|
|
331
|
+
const projectId = await ensureRepoProjectId(repoRoot);
|
|
332
|
+
const projectDir = resolveDevboxProjectDir(projectId, localHomeDir);
|
|
333
|
+
await migrateLegacyRepoDevboxDir({ repoRoot, projectDir });
|
|
334
|
+
const repoMarker = await readRepoMarker(projectDir);
|
|
285
335
|
const origin = await readRepoOrigin(repoRoot);
|
|
286
336
|
const normalizedOrigin = origin ? normalizeGitRemoteUrl(origin) : null;
|
|
287
|
-
const fingerprint =
|
|
288
|
-
|
|
289
|
-
: repoMarker?.fingerprint ??
|
|
290
|
-
fingerprintFromRootCommit(await readRootCommit(repoRoot), randomUUID());
|
|
291
|
-
let initState = await readInitState(repoRoot);
|
|
337
|
+
const fingerprint = projectId;
|
|
338
|
+
let initState = await readInitState(projectDir);
|
|
292
339
|
const initFingerprintMismatch = Boolean(initState?.fingerprint) &&
|
|
293
340
|
initState?.fingerprint !== fingerprint;
|
|
294
341
|
if (!initFingerprintMismatch &&
|
|
@@ -298,13 +345,15 @@ export const runInit = async (args) => {
|
|
|
298
345
|
// been applied yet. Treat this as incomplete so `dvb init --resume` can
|
|
299
346
|
// recover.
|
|
300
347
|
initState = { ...initState, complete: false };
|
|
301
|
-
await writeInitState(
|
|
348
|
+
await writeInitState(projectDir, initState);
|
|
302
349
|
}
|
|
303
350
|
return {
|
|
304
351
|
repoRoot,
|
|
305
352
|
repoName,
|
|
306
353
|
slug,
|
|
307
354
|
localHomeDir,
|
|
355
|
+
projectId,
|
|
356
|
+
projectDir,
|
|
308
357
|
repoMarker,
|
|
309
358
|
origin,
|
|
310
359
|
normalizedOrigin,
|
|
@@ -314,7 +363,7 @@ export const runInit = async (args) => {
|
|
|
314
363
|
};
|
|
315
364
|
},
|
|
316
365
|
});
|
|
317
|
-
const { repoRoot, repoName, slug, localHomeDir, repoMarker, origin, normalizedOrigin, fingerprint, } = detected;
|
|
366
|
+
const { repoRoot, repoName, slug, localHomeDir, projectId, projectDir, repoMarker, origin, normalizedOrigin, fingerprint, } = detected;
|
|
318
367
|
let initState = detected.initState;
|
|
319
368
|
const initFingerprintMismatch = detected.initFingerprintMismatch;
|
|
320
369
|
if (parsed.status) {
|
|
@@ -346,25 +395,28 @@ export const runInit = async (args) => {
|
|
|
346
395
|
(!initState || initFingerprintMismatch) &&
|
|
347
396
|
Boolean(registryProject?.initStatus && registryProject.initStatus !== "complete");
|
|
348
397
|
const lines = [];
|
|
398
|
+
const markerPath = path.join(projectDir, "box.json");
|
|
399
|
+
const checkpointPath = path.join(projectDir, "init-state.json");
|
|
349
400
|
lines.push("INIT STATUS");
|
|
350
401
|
lines.push("");
|
|
351
402
|
lines.push("Repo");
|
|
352
403
|
lines.push(` root: ${repoRoot}`);
|
|
353
404
|
lines.push(` origin: ${normalizedOrigin ?? origin ?? "(none)"}`);
|
|
354
|
-
lines.push(`
|
|
405
|
+
lines.push(` projectId (git config devbox.projectId): ${projectId}`);
|
|
406
|
+
lines.push(` local state dir: ${projectDir}`);
|
|
355
407
|
lines.push("");
|
|
356
408
|
lines.push("Local state");
|
|
357
409
|
if (repoMarker?.canonical) {
|
|
358
|
-
lines.push(` box marker (
|
|
410
|
+
lines.push(` box marker (${markerPath}): present (alias: ${repoMarker.alias ?? "(none)"}, box: ${repoMarker.canonical})`);
|
|
359
411
|
}
|
|
360
412
|
else {
|
|
361
|
-
lines.push(
|
|
413
|
+
lines.push(` box marker (${markerPath}): missing`);
|
|
362
414
|
}
|
|
363
415
|
if (initState) {
|
|
364
416
|
const completeText = initFingerprintMismatch
|
|
365
|
-
? `${String(Boolean(initState.complete))} (
|
|
417
|
+
? `${String(Boolean(initState.complete))} (projectId mismatch)`
|
|
366
418
|
: String(Boolean(initState.complete));
|
|
367
|
-
lines.push(` init checkpoint (
|
|
419
|
+
lines.push(` init checkpoint (${checkpointPath}): present (updated: ${initState.updatedAt}, complete: ${completeText})`);
|
|
368
420
|
if (initState.canonical || initState.alias || initState.workdir) {
|
|
369
421
|
lines.push(` box: ${initState.canonical ?? "(unknown)"} (alias: ${initState.alias ?? "(unknown)"})`);
|
|
370
422
|
lines.push(` workdir: ${initState.workdir ?? "(unknown)"}`);
|
|
@@ -390,7 +442,7 @@ export const runInit = async (args) => {
|
|
|
390
442
|
}
|
|
391
443
|
}
|
|
392
444
|
else {
|
|
393
|
-
lines.push(
|
|
445
|
+
lines.push(` init checkpoint (${checkpointPath}): missing`);
|
|
394
446
|
}
|
|
395
447
|
lines.push("");
|
|
396
448
|
lines.push("Registry");
|
|
@@ -462,7 +514,7 @@ export const runInit = async (args) => {
|
|
|
462
514
|
}
|
|
463
515
|
if (parsed.resume) {
|
|
464
516
|
if (initFingerprintMismatch) {
|
|
465
|
-
throw new Error(
|
|
517
|
+
throw new Error(`Init state does not match this repo. Remove ${path.join(projectDir, "init-state.json")} and run \`dvb init\` again.`);
|
|
466
518
|
}
|
|
467
519
|
if (!initState) {
|
|
468
520
|
throw new Error("No init state to resume. Run `dvb init` to start a new init.");
|
|
@@ -508,7 +560,7 @@ export const runInit = async (args) => {
|
|
|
508
560
|
},
|
|
509
561
|
updatedAt: new Date().toISOString(),
|
|
510
562
|
};
|
|
511
|
-
await writeInitState(
|
|
563
|
+
await writeInitState(projectDir, initState);
|
|
512
564
|
};
|
|
513
565
|
const recordCodexCheckpoint = async ({ client, canonical, phase, }) => {
|
|
514
566
|
const createdAt = new Date().toISOString();
|
|
@@ -571,14 +623,14 @@ export const runInit = async (args) => {
|
|
|
571
623
|
if (!repoMarker?.canonical) {
|
|
572
624
|
throw new Error("Repo is not initialized. Run `dvb init` first.");
|
|
573
625
|
}
|
|
574
|
-
const setupDir =
|
|
626
|
+
const setupDir = projectDir;
|
|
575
627
|
const setupPath = path.join(setupDir, "setup.json");
|
|
576
628
|
const servicesPath = path.join(setupDir, "services.json");
|
|
577
629
|
try {
|
|
578
630
|
await fs.access(setupPath);
|
|
579
631
|
}
|
|
580
632
|
catch {
|
|
581
|
-
throw new Error(
|
|
633
|
+
throw new Error(`Missing setup plan (${setupPath}). Run \`dvb init\` first.`);
|
|
582
634
|
}
|
|
583
635
|
const localArtifactsBundlePath = path.join(setupDir, "setup-artifacts.tgz");
|
|
584
636
|
const localArtifactsManifestPath = path.join(setupDir, "setup-artifacts.json");
|
|
@@ -1040,10 +1092,11 @@ export const runInit = async (args) => {
|
|
|
1040
1092
|
}
|
|
1041
1093
|
},
|
|
1042
1094
|
});
|
|
1043
|
-
const setupDir =
|
|
1095
|
+
const setupDir = projectDir;
|
|
1044
1096
|
const setupPath = path.join(setupDir, "setup.json");
|
|
1045
1097
|
const servicesPath = path.join(setupDir, "services.json");
|
|
1046
1098
|
const scansDir = path.join(setupDir, "scans");
|
|
1099
|
+
const logDir = path.join(setupDir, "logs");
|
|
1047
1100
|
const setupEnvSecretsScanPath = path.join(scansDir, "setup-env-secrets.json");
|
|
1048
1101
|
const setupExternalScanPath = path.join(scansDir, "setup-external.json");
|
|
1049
1102
|
const setupExtraArtifactsScanPath = path.join(scansDir, "setup-extra-artifacts.json");
|
|
@@ -1059,7 +1112,13 @@ export const runInit = async (args) => {
|
|
|
1059
1112
|
let approvedServices = null;
|
|
1060
1113
|
const setupTempDir = await fs.mkdtemp(path.join(os.tmpdir(), "devbox-setup-"));
|
|
1061
1114
|
try {
|
|
1062
|
-
await fs.mkdir(setupDir, { recursive: true });
|
|
1115
|
+
await fs.mkdir(setupDir, { recursive: true, mode: 0o700 });
|
|
1116
|
+
try {
|
|
1117
|
+
await fs.chmod(setupDir, 0o700);
|
|
1118
|
+
}
|
|
1119
|
+
catch {
|
|
1120
|
+
// best effort on filesystems that do not support chmod
|
|
1121
|
+
}
|
|
1063
1122
|
const tryReadSetupPlan = async () => {
|
|
1064
1123
|
try {
|
|
1065
1124
|
return await readSetupPlan(setupPath);
|
|
@@ -1189,6 +1248,7 @@ export const runInit = async (args) => {
|
|
|
1189
1248
|
: runCodexScanWithImmediateRetry({
|
|
1190
1249
|
run: async () => await runLocalSetupEnvSecretsScan({
|
|
1191
1250
|
cwd: repoRoot,
|
|
1251
|
+
logDir,
|
|
1192
1252
|
schemaPath: envSecretsSchemaPath,
|
|
1193
1253
|
outputPath: setupEnvSecretsScanPath,
|
|
1194
1254
|
onProgress: updateEnvSecrets,
|
|
@@ -1205,6 +1265,7 @@ export const runInit = async (args) => {
|
|
|
1205
1265
|
: runCodexScanWithImmediateRetry({
|
|
1206
1266
|
run: async () => await runLocalSetupExternalScan({
|
|
1207
1267
|
cwd: repoRoot,
|
|
1268
|
+
logDir,
|
|
1208
1269
|
schemaPath: externalSchemaPath,
|
|
1209
1270
|
outputPath: setupExternalScanPath,
|
|
1210
1271
|
homeDir: localHomeDir,
|
|
@@ -1222,6 +1283,7 @@ export const runInit = async (args) => {
|
|
|
1222
1283
|
: runCodexScanWithImmediateRetry({
|
|
1223
1284
|
run: async () => await runLocalSetupExtraArtifactsScan({
|
|
1224
1285
|
cwd: repoRoot,
|
|
1286
|
+
logDir,
|
|
1225
1287
|
schemaPath: extraArtifactsSchemaPath,
|
|
1226
1288
|
outputPath: setupExtraArtifactsScanPath,
|
|
1227
1289
|
onProgress: updateExtraArtifacts,
|
|
@@ -1236,6 +1298,7 @@ export const runInit = async (args) => {
|
|
|
1236
1298
|
: runCodexScanWithImmediateRetry({
|
|
1237
1299
|
run: async () => await runLocalServicesScan({
|
|
1238
1300
|
cwd: repoRoot,
|
|
1301
|
+
logDir,
|
|
1239
1302
|
schemaPath: servicesSchemaPath,
|
|
1240
1303
|
outputPath: servicesPath,
|
|
1241
1304
|
homeDir: localHomeDir,
|
|
@@ -1644,7 +1707,6 @@ export const runInit = async (args) => {
|
|
|
1644
1707
|
else if (!skipProvision || !skipSetupUpload) {
|
|
1645
1708
|
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "devbox-init-"));
|
|
1646
1709
|
const bundlePath = path.join(tempDir, "repo.bundle");
|
|
1647
|
-
const archivePath = path.join(tempDir, "repo.tgz");
|
|
1648
1710
|
const gitMetaPath = path.join(tempDir, "git-meta.tgz");
|
|
1649
1711
|
const gitMetaListPath = path.join(tempDir, "git-meta.list");
|
|
1650
1712
|
const stagedPatchPath = path.join(tempDir, "staged.patch");
|
|
@@ -1655,12 +1717,11 @@ export const runInit = async (args) => {
|
|
|
1655
1717
|
const globalGitConfigMappings = mapGlobalGitConfigDestinations(globalGitConfigSources, localHomeDir);
|
|
1656
1718
|
try {
|
|
1657
1719
|
const remoteBundlePath = "/home/sprite/.devbox/upload.bundle";
|
|
1658
|
-
const remoteArchivePath = "/home/sprite/.devbox/upload.tgz";
|
|
1659
1720
|
const remoteGitMetaPath = "/home/sprite/.devbox/git-meta.tgz";
|
|
1660
1721
|
const remoteStagedPatchPath = "/home/sprite/.devbox/staged.patch";
|
|
1661
1722
|
const remoteUnstagedPatchPath = "/home/sprite/.devbox/unstaged.patch";
|
|
1662
1723
|
const remoteUntrackedPath = "/home/sprite/.devbox/untracked.tgz";
|
|
1663
|
-
|
|
1724
|
+
await runInitStep({
|
|
1664
1725
|
enabled: progressEnabled,
|
|
1665
1726
|
title: "Preparing remote directories",
|
|
1666
1727
|
fn: async ({ status }) => {
|
|
@@ -1670,10 +1731,14 @@ export const runInit = async (args) => {
|
|
|
1670
1731
|
"-lc",
|
|
1671
1732
|
"git --version",
|
|
1672
1733
|
]);
|
|
1673
|
-
|
|
1734
|
+
if (gitCheck.exitCode !== 0) {
|
|
1735
|
+
const details = gitCheck.stderr || gitCheck.stdout || "";
|
|
1736
|
+
throw new Error(details
|
|
1737
|
+
? `Remote git unavailable: ${details.trim()}`
|
|
1738
|
+
: "Remote git unavailable");
|
|
1739
|
+
}
|
|
1674
1740
|
const remoteDirs = new Set();
|
|
1675
1741
|
remoteDirs.add(path.posix.dirname(remoteBundlePath));
|
|
1676
|
-
remoteDirs.add(path.posix.dirname(remoteArchivePath));
|
|
1677
1742
|
remoteDirs.add(path.posix.dirname(remoteGitMetaPath));
|
|
1678
1743
|
remoteDirs.add(path.posix.dirname(remoteStagedPatchPath));
|
|
1679
1744
|
remoteDirs.add(path.posix.dirname(remoteUnstagedPatchPath));
|
|
@@ -1692,7 +1757,6 @@ export const runInit = async (args) => {
|
|
|
1692
1757
|
throw new Error(prepResult.stderr || "Failed to prepare remote dirs");
|
|
1693
1758
|
}
|
|
1694
1759
|
}
|
|
1695
|
-
return remoteHasGit;
|
|
1696
1760
|
},
|
|
1697
1761
|
});
|
|
1698
1762
|
const { headState, gitCommonDir, worktreeState, copyWorktree } = await runInitStep({
|
|
@@ -1730,43 +1794,20 @@ export const runInit = async (args) => {
|
|
|
1730
1794
|
let stagedPatchCreated = false;
|
|
1731
1795
|
let unstagedPatchCreated = false;
|
|
1732
1796
|
let untrackedCreated = false;
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
untrackedCreated = await createFileListArchive(repoRoot, worktreeState.untracked, untrackedPath, untrackedListPath);
|
|
1748
|
-
}
|
|
1749
|
-
}
|
|
1750
|
-
else {
|
|
1751
|
-
status.stage("Packaging repo archive");
|
|
1752
|
-
if (copyWorktree) {
|
|
1753
|
-
const workingFiles = await readNullSeparatedPaths(repoRoot, [
|
|
1754
|
-
"ls-files",
|
|
1755
|
-
"--cached",
|
|
1756
|
-
"--others",
|
|
1757
|
-
"--exclude-standard",
|
|
1758
|
-
]);
|
|
1759
|
-
await createFileListArchive(repoRoot, workingFiles, archivePath, untrackedListPath);
|
|
1760
|
-
}
|
|
1761
|
-
else {
|
|
1762
|
-
await runCommand(repoRoot, "git", [
|
|
1763
|
-
"archive",
|
|
1764
|
-
"--format=tar.gz",
|
|
1765
|
-
"-o",
|
|
1766
|
-
archivePath,
|
|
1767
|
-
"HEAD",
|
|
1768
|
-
]);
|
|
1769
|
-
}
|
|
1797
|
+
status.stage("Packaging repo bundle");
|
|
1798
|
+
await runCommand(repoRoot, "git", [
|
|
1799
|
+
"bundle",
|
|
1800
|
+
"create",
|
|
1801
|
+
bundlePath,
|
|
1802
|
+
"--all",
|
|
1803
|
+
]);
|
|
1804
|
+
status.stage("Packaging git metadata");
|
|
1805
|
+
gitMetaCreated = await createGitMetaArchive(gitCommonDir, gitMetaPath, gitMetaListPath);
|
|
1806
|
+
if (copyWorktree) {
|
|
1807
|
+
status.stage("Packaging repo changes");
|
|
1808
|
+
stagedPatchCreated = await writePatch(repoRoot, ["diff", "--binary", "--cached"], stagedPatchPath);
|
|
1809
|
+
unstagedPatchCreated = await writePatch(repoRoot, ["diff", "--binary"], unstagedPatchPath);
|
|
1810
|
+
untrackedCreated = await createFileListArchive(repoRoot, worktreeState.untracked, untrackedPath, untrackedListPath);
|
|
1770
1811
|
}
|
|
1771
1812
|
return {
|
|
1772
1813
|
gitMetaCreated,
|
|
@@ -1780,31 +1821,25 @@ export const runInit = async (args) => {
|
|
|
1780
1821
|
enabled: progressEnabled,
|
|
1781
1822
|
title: "Uploading repo",
|
|
1782
1823
|
fn: async ({ status }) => {
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
await client.writeFile(canonical, remoteGitMetaPath, gitMetaData);
|
|
1791
|
-
}
|
|
1792
|
-
if (packaged.stagedPatchCreated) {
|
|
1793
|
-
status.stage("Uploading staged changes");
|
|
1794
|
-
await client.writeFile(canonical, remoteStagedPatchPath, await fs.readFile(stagedPatchPath));
|
|
1795
|
-
}
|
|
1796
|
-
if (packaged.unstagedPatchCreated) {
|
|
1797
|
-
status.stage("Uploading unstaged changes");
|
|
1798
|
-
await client.writeFile(canonical, remoteUnstagedPatchPath, await fs.readFile(unstagedPatchPath));
|
|
1799
|
-
}
|
|
1800
|
-
if (packaged.untrackedCreated) {
|
|
1801
|
-
status.stage("Uploading untracked files");
|
|
1802
|
-
await client.writeFile(canonical, remoteUntrackedPath, await fs.readFile(untrackedPath));
|
|
1803
|
-
}
|
|
1824
|
+
status.stage("Uploading repo bundle");
|
|
1825
|
+
const bundleData = await fs.readFile(bundlePath);
|
|
1826
|
+
await client.writeFile(canonical, remoteBundlePath, bundleData);
|
|
1827
|
+
if (packaged.gitMetaCreated) {
|
|
1828
|
+
status.stage("Uploading git metadata");
|
|
1829
|
+
const gitMetaData = await fs.readFile(gitMetaPath);
|
|
1830
|
+
await client.writeFile(canonical, remoteGitMetaPath, gitMetaData);
|
|
1804
1831
|
}
|
|
1805
|
-
|
|
1806
|
-
status.stage("Uploading
|
|
1807
|
-
await client.writeFile(canonical,
|
|
1832
|
+
if (packaged.stagedPatchCreated) {
|
|
1833
|
+
status.stage("Uploading staged changes");
|
|
1834
|
+
await client.writeFile(canonical, remoteStagedPatchPath, await fs.readFile(stagedPatchPath));
|
|
1835
|
+
}
|
|
1836
|
+
if (packaged.unstagedPatchCreated) {
|
|
1837
|
+
status.stage("Uploading unstaged changes");
|
|
1838
|
+
await client.writeFile(canonical, remoteUnstagedPatchPath, await fs.readFile(unstagedPatchPath));
|
|
1839
|
+
}
|
|
1840
|
+
if (packaged.untrackedCreated) {
|
|
1841
|
+
status.stage("Uploading untracked files");
|
|
1842
|
+
await client.writeFile(canonical, remoteUntrackedPath, await fs.readFile(untrackedPath));
|
|
1808
1843
|
}
|
|
1809
1844
|
if (globalGitConfigMappings.length > 0) {
|
|
1810
1845
|
status.stage("Uploading git config");
|
|
@@ -1820,77 +1855,48 @@ export const runInit = async (args) => {
|
|
|
1820
1855
|
title: "Provisioning workdir",
|
|
1821
1856
|
fn: async () => {
|
|
1822
1857
|
const backup = `${expandedWorkdir}.bak-${Date.now()}`;
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
throw new Error(execResult.stderr || "Remote init failed");
|
|
1866
|
-
}
|
|
1867
|
-
}
|
|
1868
|
-
else {
|
|
1869
|
-
const remoteCommand = [
|
|
1870
|
-
"set -euo pipefail",
|
|
1871
|
-
"unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE",
|
|
1872
|
-
`if [ -d ${shellQuote(expandedWorkdir)} ]; then`,
|
|
1873
|
-
parsed.force
|
|
1874
|
-
? ` mv ${shellQuote(expandedWorkdir)} ${shellQuote(backup)}`
|
|
1875
|
-
: ` echo "Target exists: ${expandedWorkdir}" >&2; exit 1`,
|
|
1876
|
-
"fi",
|
|
1877
|
-
`mkdir -p ${shellQuote(expandedWorkdir)}`,
|
|
1878
|
-
`tar -xzf ${shellQuote(remoteArchivePath)} -C ${shellQuote(expandedWorkdir)}`,
|
|
1879
|
-
].join("\n");
|
|
1880
|
-
const execResult = await client.exec(canonical, [
|
|
1881
|
-
"/bin/bash",
|
|
1882
|
-
"--noprofile",
|
|
1883
|
-
"--norc",
|
|
1884
|
-
"-e",
|
|
1885
|
-
"-u",
|
|
1886
|
-
"-o",
|
|
1887
|
-
"pipefail",
|
|
1888
|
-
"-c",
|
|
1889
|
-
remoteCommand,
|
|
1890
|
-
]);
|
|
1891
|
-
if (execResult.exitCode !== 0) {
|
|
1892
|
-
throw new Error(execResult.stderr || "Remote init failed");
|
|
1893
|
-
}
|
|
1858
|
+
const checkoutCommand = headState.branch
|
|
1859
|
+
? `git checkout -B ${shellQuote(headState.branch)} ${shellQuote(headState.commit)}`
|
|
1860
|
+
: `git checkout --detach ${shellQuote(headState.commit)}`;
|
|
1861
|
+
const remoteCommand = [
|
|
1862
|
+
"set -euo pipefail",
|
|
1863
|
+
"unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE",
|
|
1864
|
+
`if [ -d ${shellQuote(expandedWorkdir)} ]; then`,
|
|
1865
|
+
parsed.force
|
|
1866
|
+
? ` mv ${shellQuote(expandedWorkdir)} ${shellQuote(backup)}`
|
|
1867
|
+
: ` echo "Target exists: ${expandedWorkdir}" >&2; exit 1`,
|
|
1868
|
+
"fi",
|
|
1869
|
+
`mkdir -p ${shellQuote(path.dirname(expandedWorkdir))}`,
|
|
1870
|
+
`git init -b devbox-init ${shellQuote(expandedWorkdir)}`,
|
|
1871
|
+
`cd ${shellQuote(expandedWorkdir)}`,
|
|
1872
|
+
`git fetch ${shellQuote(remoteBundlePath)} 'refs/*:refs/*'`,
|
|
1873
|
+
`if [ -f ${shellQuote(remoteGitMetaPath)} ]; then`,
|
|
1874
|
+
` tar -xzf ${shellQuote(remoteGitMetaPath)} -C .git`,
|
|
1875
|
+
"fi",
|
|
1876
|
+
checkoutCommand,
|
|
1877
|
+
`if [ -f ${shellQuote(remoteStagedPatchPath)} ]; then`,
|
|
1878
|
+
` git apply --index ${shellQuote(remoteStagedPatchPath)}`,
|
|
1879
|
+
"fi",
|
|
1880
|
+
`if [ -f ${shellQuote(remoteUnstagedPatchPath)} ]; then`,
|
|
1881
|
+
` git apply ${shellQuote(remoteUnstagedPatchPath)}`,
|
|
1882
|
+
"fi",
|
|
1883
|
+
`if [ -f ${shellQuote(remoteUntrackedPath)} ]; then`,
|
|
1884
|
+
` tar -xzf ${shellQuote(remoteUntrackedPath)} -C .`,
|
|
1885
|
+
"fi",
|
|
1886
|
+
].join("\n");
|
|
1887
|
+
const execResult = await client.exec(canonical, [
|
|
1888
|
+
"/bin/bash",
|
|
1889
|
+
"--noprofile",
|
|
1890
|
+
"--norc",
|
|
1891
|
+
"-e",
|
|
1892
|
+
"-u",
|
|
1893
|
+
"-o",
|
|
1894
|
+
"pipefail",
|
|
1895
|
+
"-c",
|
|
1896
|
+
remoteCommand,
|
|
1897
|
+
]);
|
|
1898
|
+
if (execResult.exitCode !== 0) {
|
|
1899
|
+
throw new Error(execResult.stderr || "Remote init failed");
|
|
1894
1900
|
}
|
|
1895
1901
|
await updateInitState({ steps: { workdirProvisioned: true } });
|
|
1896
1902
|
},
|
|
@@ -2243,8 +2249,7 @@ export const runInit = async (args) => {
|
|
|
2243
2249
|
enabled: progressEnabled,
|
|
2244
2250
|
title: "Writing local metadata",
|
|
2245
2251
|
fn: async () => {
|
|
2246
|
-
await
|
|
2247
|
-
await writeRepoMarker(repoRoot, { fingerprint, canonical, alias });
|
|
2252
|
+
await writeRepoMarker(projectDir, { fingerprint, canonical, alias });
|
|
2248
2253
|
},
|
|
2249
2254
|
});
|
|
2250
2255
|
await runInitStep({
|
|
@@ -2265,15 +2270,6 @@ export const runInit = async (args) => {
|
|
|
2265
2270
|
}
|
|
2266
2271
|
},
|
|
2267
2272
|
});
|
|
2268
|
-
try {
|
|
2269
|
-
const gitignore = await fs.readFile(path.join(repoRoot, ".gitignore"), "utf8");
|
|
2270
|
-
if (!gitignore.includes(".devbox")) {
|
|
2271
|
-
console.log("Note: add .devbox/ to .gitignore to keep local marker private.");
|
|
2272
|
-
}
|
|
2273
|
-
}
|
|
2274
|
-
catch {
|
|
2275
|
-
// ignore missing .gitignore
|
|
2276
|
-
}
|
|
2277
2273
|
const skipSetupArtifactsStage = skipCodexApply ||
|
|
2278
2274
|
(shouldResume && initState?.steps.setupArtifactsStaged);
|
|
2279
2275
|
if (!skipSetupArtifactsStage) {
|