@solaqua/gji 0.12.2 → 0.12.3
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/bootstrap-output.d.ts +3 -2
- package/dist/bootstrap-output.js +1 -16
- package/dist/bootstrap-preview.d.ts +2 -3
- package/dist/bootstrap-preview.js +1 -11
- package/dist/cli.js +5 -1
- package/dist/config.d.ts +3 -13
- package/dist/config.js +12 -59
- package/dist/dependency-bootstrap.d.ts +11 -45
- package/dist/dependency-bootstrap.js +142 -608
- package/dist/gji-bundle.mjs +567 -2367
- package/dist/new.d.ts +4 -5
- package/dist/new.js +10 -45
- package/dist/pr.d.ts +4 -5
- package/dist/pr.js +9 -69
- package/dist/shell-completion.js +7 -5
- package/dist/uv-validation.d.ts +4 -0
- package/dist/uv-validation.js +210 -0
- package/dist/worktree-bootstrap.d.ts +5 -16
- package/dist/worktree-bootstrap.js +1 -24
- package/man/man1/gji-back.1 +1 -1
- package/man/man1/gji-clean.1 +1 -1
- package/man/man1/gji-completion.1 +1 -1
- package/man/man1/gji-config.1 +1 -1
- package/man/man1/gji-doctor.1 +1 -1
- package/man/man1/gji-done.1 +1 -1
- package/man/man1/gji-go.1 +1 -1
- package/man/man1/gji-history.1 +1 -1
- package/man/man1/gji-init.1 +1 -1
- package/man/man1/gji-ls.1 +1 -1
- package/man/man1/gji-new.1 +8 -5
- package/man/man1/gji-open.1 +1 -1
- package/man/man1/gji-pr.1 +6 -3
- package/man/man1/gji-remove.1 +1 -1
- package/man/man1/gji-root.1 +1 -1
- package/man/man1/gji-run-hook.1 +1 -1
- package/man/man1/gji-status.1 +1 -1
- package/man/man1/gji-sync-files.1 +1 -1
- package/man/man1/gji-sync.1 +1 -1
- package/man/man1/gji-task.1 +1 -1
- package/man/man1/gji-undo.1 +1 -1
- package/man/man1/gji-warp.1 +1 -1
- package/man/man1/gji.1 +2 -2
- package/package.json +1 -1
- package/dist/dependency-bootstrap-prompt.d.ts +0 -24
- package/dist/dependency-bootstrap-prompt.js +0 -103
- package/dist/dir-clone.d.ts +0 -32
- package/dist/dir-clone.js +0 -641
- package/dist/install-prompt.d.ts +0 -12
- package/dist/install-prompt.js +0 -127
- package/dist/package-manager.d.ts +0 -5
- package/dist/package-manager.js +0 -159
- package/dist/sync-directories.d.ts +0 -29
- package/dist/sync-directories.js +0 -77
- package/dist/sync-plan.d.ts +0 -16
- package/dist/sync-plan.js +0 -128
package/dist/gji-bundle.mjs
CHANGED
|
@@ -3462,7 +3462,7 @@ var require_commander = __commonJS({
|
|
|
3462
3462
|
// node_modules/.pnpm/graceful-fs@4.2.11/node_modules/graceful-fs/polyfills.js
|
|
3463
3463
|
var require_polyfills = __commonJS({
|
|
3464
3464
|
"node_modules/.pnpm/graceful-fs@4.2.11/node_modules/graceful-fs/polyfills.js"(exports, module) {
|
|
3465
|
-
var
|
|
3465
|
+
var constants9 = __require("constants");
|
|
3466
3466
|
var origCwd = process.cwd;
|
|
3467
3467
|
var cwd = null;
|
|
3468
3468
|
var platform3 = process.env.GRACEFUL_FS_PLATFORM || process.platform;
|
|
@@ -3486,7 +3486,7 @@ var require_polyfills = __commonJS({
|
|
|
3486
3486
|
var chdir;
|
|
3487
3487
|
module.exports = patch;
|
|
3488
3488
|
function patch(fs5) {
|
|
3489
|
-
if (
|
|
3489
|
+
if (constants9.hasOwnProperty("O_SYMLINK") && process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
|
|
3490
3490
|
patchLchmod(fs5);
|
|
3491
3491
|
}
|
|
3492
3492
|
if (!fs5.lutimes) {
|
|
@@ -3526,7 +3526,7 @@ var require_polyfills = __commonJS({
|
|
|
3526
3526
|
}
|
|
3527
3527
|
if (platform3 === "win32") {
|
|
3528
3528
|
fs5.rename = typeof fs5.rename !== "function" ? fs5.rename : (function(fs$rename) {
|
|
3529
|
-
function
|
|
3529
|
+
function rename3(from, to, cb) {
|
|
3530
3530
|
var start = Date.now();
|
|
3531
3531
|
var backoff = 0;
|
|
3532
3532
|
fs$rename(from, to, function CB(er) {
|
|
@@ -3546,8 +3546,8 @@ var require_polyfills = __commonJS({
|
|
|
3546
3546
|
if (cb) cb(er);
|
|
3547
3547
|
});
|
|
3548
3548
|
}
|
|
3549
|
-
if (Object.setPrototypeOf) Object.setPrototypeOf(
|
|
3550
|
-
return
|
|
3549
|
+
if (Object.setPrototypeOf) Object.setPrototypeOf(rename3, fs$rename);
|
|
3550
|
+
return rename3;
|
|
3551
3551
|
})(fs5.rename);
|
|
3552
3552
|
}
|
|
3553
3553
|
fs5.read = typeof fs5.read !== "function" ? fs5.read : (function(fs$read) {
|
|
@@ -3588,7 +3588,7 @@ var require_polyfills = __commonJS({
|
|
|
3588
3588
|
fs6.lchmod = function(path9, mode, callback) {
|
|
3589
3589
|
fs6.open(
|
|
3590
3590
|
path9,
|
|
3591
|
-
|
|
3591
|
+
constants9.O_WRONLY | constants9.O_SYMLINK,
|
|
3592
3592
|
mode,
|
|
3593
3593
|
function(err, fd) {
|
|
3594
3594
|
if (err) {
|
|
@@ -3604,7 +3604,7 @@ var require_polyfills = __commonJS({
|
|
|
3604
3604
|
);
|
|
3605
3605
|
};
|
|
3606
3606
|
fs6.lchmodSync = function(path9, mode) {
|
|
3607
|
-
var fd = fs6.openSync(path9,
|
|
3607
|
+
var fd = fs6.openSync(path9, constants9.O_WRONLY | constants9.O_SYMLINK, mode);
|
|
3608
3608
|
var threw = true;
|
|
3609
3609
|
var ret;
|
|
3610
3610
|
try {
|
|
@@ -3624,9 +3624,9 @@ var require_polyfills = __commonJS({
|
|
|
3624
3624
|
};
|
|
3625
3625
|
}
|
|
3626
3626
|
function patchLutimes(fs6) {
|
|
3627
|
-
if (
|
|
3627
|
+
if (constants9.hasOwnProperty("O_SYMLINK") && fs6.futimes) {
|
|
3628
3628
|
fs6.lutimes = function(path9, at, mt, cb) {
|
|
3629
|
-
fs6.open(path9,
|
|
3629
|
+
fs6.open(path9, constants9.O_SYMLINK, function(er, fd) {
|
|
3630
3630
|
if (er) {
|
|
3631
3631
|
if (cb) cb(er);
|
|
3632
3632
|
return;
|
|
@@ -3639,7 +3639,7 @@ var require_polyfills = __commonJS({
|
|
|
3639
3639
|
});
|
|
3640
3640
|
};
|
|
3641
3641
|
fs6.lutimesSync = function(path9, at, mt) {
|
|
3642
|
-
var fd = fs6.openSync(path9,
|
|
3642
|
+
var fd = fs6.openSync(path9, constants9.O_SYMLINK);
|
|
3643
3643
|
var ret;
|
|
3644
3644
|
var threw = true;
|
|
3645
3645
|
try {
|
|
@@ -3951,8 +3951,8 @@ var require_graceful_fs = __commonJS({
|
|
|
3951
3951
|
fs6.createReadStream = createReadStream;
|
|
3952
3952
|
fs6.createWriteStream = createWriteStream;
|
|
3953
3953
|
var fs$readFile = fs6.readFile;
|
|
3954
|
-
fs6.readFile =
|
|
3955
|
-
function
|
|
3954
|
+
fs6.readFile = readFile10;
|
|
3955
|
+
function readFile10(path9, options, cb) {
|
|
3956
3956
|
if (typeof options === "function")
|
|
3957
3957
|
cb = options, options = null;
|
|
3958
3958
|
return go$readFile(path9, options, cb);
|
|
@@ -3968,8 +3968,8 @@ var require_graceful_fs = __commonJS({
|
|
|
3968
3968
|
}
|
|
3969
3969
|
}
|
|
3970
3970
|
var fs$writeFile = fs6.writeFile;
|
|
3971
|
-
fs6.writeFile =
|
|
3972
|
-
function
|
|
3971
|
+
fs6.writeFile = writeFile10;
|
|
3972
|
+
function writeFile10(path9, data, options, cb) {
|
|
3973
3973
|
if (typeof options === "function")
|
|
3974
3974
|
cb = options, options = null;
|
|
3975
3975
|
return go$writeFile(path9, data, options, cb);
|
|
@@ -4023,9 +4023,9 @@ var require_graceful_fs = __commonJS({
|
|
|
4023
4023
|
}
|
|
4024
4024
|
}
|
|
4025
4025
|
var fs$readdir = fs6.readdir;
|
|
4026
|
-
fs6.readdir =
|
|
4026
|
+
fs6.readdir = readdir2;
|
|
4027
4027
|
var noReaddirOptionVersions = /^v[0-5]\./;
|
|
4028
|
-
function
|
|
4028
|
+
function readdir2(path9, options, cb) {
|
|
4029
4029
|
if (typeof options === "function")
|
|
4030
4030
|
cb = options, options = null;
|
|
4031
4031
|
var go$readdir = noReaddirOptionVersions.test(process.version) ? function go$readdir2(path10, options2, cb2, startTime) {
|
|
@@ -5386,7 +5386,7 @@ var require_minimist = __commonJS({
|
|
|
5386
5386
|
var require_rc = __commonJS({
|
|
5387
5387
|
"node_modules/.pnpm/rc@1.2.8/node_modules/rc/index.js"(exports, module) {
|
|
5388
5388
|
var cc = require_utils();
|
|
5389
|
-
var
|
|
5389
|
+
var join16 = __require("path").join;
|
|
5390
5390
|
var deepExtend = require_deep_extend();
|
|
5391
5391
|
var etc = "/etc";
|
|
5392
5392
|
var win = process.platform === "win32";
|
|
@@ -5411,15 +5411,15 @@ var require_rc = __commonJS({
|
|
|
5411
5411
|
}
|
|
5412
5412
|
if (!win)
|
|
5413
5413
|
[
|
|
5414
|
-
|
|
5415
|
-
|
|
5414
|
+
join16(etc, name, "config"),
|
|
5415
|
+
join16(etc, name + "rc")
|
|
5416
5416
|
].forEach(addConfigFile);
|
|
5417
5417
|
if (home)
|
|
5418
5418
|
[
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5419
|
+
join16(home, ".config", name, "config"),
|
|
5420
|
+
join16(home, ".config", name),
|
|
5421
|
+
join16(home, "." + name, "config"),
|
|
5422
|
+
join16(home, "." + name + "rc")
|
|
5423
5423
|
].forEach(addConfigFile);
|
|
5424
5424
|
addConfigFile(cc.find("." + name + "rc"));
|
|
5425
5425
|
if (env5.config) addConfigFile(env5.config);
|
|
@@ -5436,7 +5436,7 @@ var require_rc = __commonJS({
|
|
|
5436
5436
|
// node_modules/.pnpm/graceful-fs@4.2.10/node_modules/graceful-fs/polyfills.js
|
|
5437
5437
|
var require_polyfills2 = __commonJS({
|
|
5438
5438
|
"node_modules/.pnpm/graceful-fs@4.2.10/node_modules/graceful-fs/polyfills.js"(exports, module) {
|
|
5439
|
-
var
|
|
5439
|
+
var constants9 = __require("constants");
|
|
5440
5440
|
var origCwd = process.cwd;
|
|
5441
5441
|
var cwd = null;
|
|
5442
5442
|
var platform3 = process.env.GRACEFUL_FS_PLATFORM || process.platform;
|
|
@@ -5460,7 +5460,7 @@ var require_polyfills2 = __commonJS({
|
|
|
5460
5460
|
var chdir;
|
|
5461
5461
|
module.exports = patch;
|
|
5462
5462
|
function patch(fs5) {
|
|
5463
|
-
if (
|
|
5463
|
+
if (constants9.hasOwnProperty("O_SYMLINK") && process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
|
|
5464
5464
|
patchLchmod(fs5);
|
|
5465
5465
|
}
|
|
5466
5466
|
if (!fs5.lutimes) {
|
|
@@ -5500,7 +5500,7 @@ var require_polyfills2 = __commonJS({
|
|
|
5500
5500
|
}
|
|
5501
5501
|
if (platform3 === "win32") {
|
|
5502
5502
|
fs5.rename = typeof fs5.rename !== "function" ? fs5.rename : (function(fs$rename) {
|
|
5503
|
-
function
|
|
5503
|
+
function rename3(from, to, cb) {
|
|
5504
5504
|
var start = Date.now();
|
|
5505
5505
|
var backoff = 0;
|
|
5506
5506
|
fs$rename(from, to, function CB(er) {
|
|
@@ -5520,8 +5520,8 @@ var require_polyfills2 = __commonJS({
|
|
|
5520
5520
|
if (cb) cb(er);
|
|
5521
5521
|
});
|
|
5522
5522
|
}
|
|
5523
|
-
if (Object.setPrototypeOf) Object.setPrototypeOf(
|
|
5524
|
-
return
|
|
5523
|
+
if (Object.setPrototypeOf) Object.setPrototypeOf(rename3, fs$rename);
|
|
5524
|
+
return rename3;
|
|
5525
5525
|
})(fs5.rename);
|
|
5526
5526
|
}
|
|
5527
5527
|
fs5.read = typeof fs5.read !== "function" ? fs5.read : (function(fs$read) {
|
|
@@ -5562,7 +5562,7 @@ var require_polyfills2 = __commonJS({
|
|
|
5562
5562
|
fs6.lchmod = function(path9, mode, callback) {
|
|
5563
5563
|
fs6.open(
|
|
5564
5564
|
path9,
|
|
5565
|
-
|
|
5565
|
+
constants9.O_WRONLY | constants9.O_SYMLINK,
|
|
5566
5566
|
mode,
|
|
5567
5567
|
function(err, fd) {
|
|
5568
5568
|
if (err) {
|
|
@@ -5578,7 +5578,7 @@ var require_polyfills2 = __commonJS({
|
|
|
5578
5578
|
);
|
|
5579
5579
|
};
|
|
5580
5580
|
fs6.lchmodSync = function(path9, mode) {
|
|
5581
|
-
var fd = fs6.openSync(path9,
|
|
5581
|
+
var fd = fs6.openSync(path9, constants9.O_WRONLY | constants9.O_SYMLINK, mode);
|
|
5582
5582
|
var threw = true;
|
|
5583
5583
|
var ret;
|
|
5584
5584
|
try {
|
|
@@ -5598,9 +5598,9 @@ var require_polyfills2 = __commonJS({
|
|
|
5598
5598
|
};
|
|
5599
5599
|
}
|
|
5600
5600
|
function patchLutimes(fs6) {
|
|
5601
|
-
if (
|
|
5601
|
+
if (constants9.hasOwnProperty("O_SYMLINK") && fs6.futimes) {
|
|
5602
5602
|
fs6.lutimes = function(path9, at, mt, cb) {
|
|
5603
|
-
fs6.open(path9,
|
|
5603
|
+
fs6.open(path9, constants9.O_SYMLINK, function(er, fd) {
|
|
5604
5604
|
if (er) {
|
|
5605
5605
|
if (cb) cb(er);
|
|
5606
5606
|
return;
|
|
@@ -5613,7 +5613,7 @@ var require_polyfills2 = __commonJS({
|
|
|
5613
5613
|
});
|
|
5614
5614
|
};
|
|
5615
5615
|
fs6.lutimesSync = function(path9, at, mt) {
|
|
5616
|
-
var fd = fs6.openSync(path9,
|
|
5616
|
+
var fd = fs6.openSync(path9, constants9.O_SYMLINK);
|
|
5617
5617
|
var ret;
|
|
5618
5618
|
var threw = true;
|
|
5619
5619
|
try {
|
|
@@ -5925,8 +5925,8 @@ var require_graceful_fs2 = __commonJS({
|
|
|
5925
5925
|
fs6.createReadStream = createReadStream;
|
|
5926
5926
|
fs6.createWriteStream = createWriteStream;
|
|
5927
5927
|
var fs$readFile = fs6.readFile;
|
|
5928
|
-
fs6.readFile =
|
|
5929
|
-
function
|
|
5928
|
+
fs6.readFile = readFile10;
|
|
5929
|
+
function readFile10(path9, options, cb) {
|
|
5930
5930
|
if (typeof options === "function")
|
|
5931
5931
|
cb = options, options = null;
|
|
5932
5932
|
return go$readFile(path9, options, cb);
|
|
@@ -5942,8 +5942,8 @@ var require_graceful_fs2 = __commonJS({
|
|
|
5942
5942
|
}
|
|
5943
5943
|
}
|
|
5944
5944
|
var fs$writeFile = fs6.writeFile;
|
|
5945
|
-
fs6.writeFile =
|
|
5946
|
-
function
|
|
5945
|
+
fs6.writeFile = writeFile10;
|
|
5946
|
+
function writeFile10(path9, data, options, cb) {
|
|
5947
5947
|
if (typeof options === "function")
|
|
5948
5948
|
cb = options, options = null;
|
|
5949
5949
|
return go$writeFile(path9, data, options, cb);
|
|
@@ -5997,9 +5997,9 @@ var require_graceful_fs2 = __commonJS({
|
|
|
5997
5997
|
}
|
|
5998
5998
|
}
|
|
5999
5999
|
var fs$readdir = fs6.readdir;
|
|
6000
|
-
fs6.readdir =
|
|
6000
|
+
fs6.readdir = readdir2;
|
|
6001
6001
|
var noReaddirOptionVersions = /^v[0-5]\./;
|
|
6002
|
-
function
|
|
6002
|
+
function readdir2(path9, options, cb) {
|
|
6003
6003
|
if (typeof options === "function")
|
|
6004
6004
|
cb = options, options = null;
|
|
6005
6005
|
var go$readdir = noReaddirOptionVersions.test(process.version) ? function go$readdir2(path10, options2, cb2, startTime) {
|
|
@@ -6605,13 +6605,13 @@ var require_envKeyToSetting = __commonJS({
|
|
|
6605
6605
|
module.exports = function(x2) {
|
|
6606
6606
|
const colonIndex = x2.indexOf(":");
|
|
6607
6607
|
if (colonIndex === -1) {
|
|
6608
|
-
return
|
|
6608
|
+
return normalize2(x2);
|
|
6609
6609
|
}
|
|
6610
6610
|
const firstPart = x2.substr(0, colonIndex);
|
|
6611
6611
|
const secondPart = x2.substr(colonIndex + 1);
|
|
6612
|
-
return `${firstPart}:${
|
|
6612
|
+
return `${firstPart}:${normalize2(secondPart)}`;
|
|
6613
6613
|
};
|
|
6614
|
-
function
|
|
6614
|
+
function normalize2(s) {
|
|
6615
6615
|
s = s.toLowerCase();
|
|
6616
6616
|
if (s === "_authtoken") return "_authToken";
|
|
6617
6617
|
let r2 = s[0];
|
|
@@ -6757,11 +6757,11 @@ var require_util = __commonJS({
|
|
|
6757
6757
|
if (files.includes("node_modules") || files.includes("package.json") || files.includes("package.json5") || files.includes("package.yaml") || files.includes("pnpm-workspace.yaml")) {
|
|
6758
6758
|
return name2;
|
|
6759
6759
|
}
|
|
6760
|
-
const
|
|
6761
|
-
if (
|
|
6760
|
+
const dirname13 = path9.dirname(name2);
|
|
6761
|
+
if (dirname13 === name2) {
|
|
6762
6762
|
return original;
|
|
6763
6763
|
}
|
|
6764
|
-
return find(
|
|
6764
|
+
return find(dirname13, original);
|
|
6765
6765
|
} catch (error) {
|
|
6766
6766
|
if (name2 === original) {
|
|
6767
6767
|
if (error.code === "ENOENT") {
|
|
@@ -8729,7 +8729,7 @@ var require_semver2 = __commonJS({
|
|
|
8729
8729
|
"node_modules/.pnpm/semver@7.7.4/node_modules/semver/index.js"(exports, module) {
|
|
8730
8730
|
"use strict";
|
|
8731
8731
|
var internalRe = require_re();
|
|
8732
|
-
var
|
|
8732
|
+
var constants9 = require_constants();
|
|
8733
8733
|
var SemVer = require_semver();
|
|
8734
8734
|
var identifiers = require_identifiers();
|
|
8735
8735
|
var parse = require_parse();
|
|
@@ -8811,8 +8811,8 @@ var require_semver2 = __commonJS({
|
|
|
8811
8811
|
re: internalRe.re,
|
|
8812
8812
|
src: internalRe.src,
|
|
8813
8813
|
tokens: internalRe.t,
|
|
8814
|
-
SEMVER_SPEC_VERSION:
|
|
8815
|
-
RELEASE_TYPES:
|
|
8814
|
+
SEMVER_SPEC_VERSION: constants9.SEMVER_SPEC_VERSION,
|
|
8815
|
+
RELEASE_TYPES: constants9.RELEASE_TYPES,
|
|
8816
8816
|
compareIdentifiers: identifiers.compareIdentifiers,
|
|
8817
8817
|
rcompareIdentifiers: identifiers.rcompareIdentifiers
|
|
8818
8818
|
};
|
|
@@ -9522,7 +9522,7 @@ var retryifyAsync = (fn, options) => {
|
|
|
9522
9522
|
throw error;
|
|
9523
9523
|
const delay2 = Math.round(interval * Math.random());
|
|
9524
9524
|
if (delay2 > 0) {
|
|
9525
|
-
const delayPromise = new Promise((
|
|
9525
|
+
const delayPromise = new Promise((resolve12) => setTimeout(resolve12, delay2));
|
|
9526
9526
|
return delayPromise.then(() => attempt.apply(void 0, args));
|
|
9527
9527
|
} else {
|
|
9528
9528
|
return attempt.apply(void 0, args);
|
|
@@ -9781,14 +9781,14 @@ var Temp = {
|
|
|
9781
9781
|
}
|
|
9782
9782
|
},
|
|
9783
9783
|
truncate: (filePath) => {
|
|
9784
|
-
const
|
|
9785
|
-
if (
|
|
9784
|
+
const basename12 = path2.basename(filePath);
|
|
9785
|
+
if (basename12.length <= LIMIT_BASENAME_LENGTH)
|
|
9786
9786
|
return filePath;
|
|
9787
|
-
const truncable = /^(\.?)(.*?)((?:\.[^.]+)?(?:\.tmp-\d{10}[a-f0-9]{6})?)$/.exec(
|
|
9787
|
+
const truncable = /^(\.?)(.*?)((?:\.[^.]+)?(?:\.tmp-\d{10}[a-f0-9]{6})?)$/.exec(basename12);
|
|
9788
9788
|
if (!truncable)
|
|
9789
9789
|
return filePath;
|
|
9790
|
-
const truncationLength =
|
|
9791
|
-
return `${filePath.slice(0, -
|
|
9790
|
+
const truncationLength = basename12.length - LIMIT_BASENAME_LENGTH;
|
|
9791
|
+
return `${filePath.slice(0, -basename12.length)}${truncable[1]}${truncable[2].slice(0, -truncationLength)}${truncable[3]}`;
|
|
9792
9792
|
}
|
|
9793
9793
|
};
|
|
9794
9794
|
node_default(Temp.purgeSyncAll);
|
|
@@ -11088,14 +11088,14 @@ var TimeoutError = class extends Error {
|
|
|
11088
11088
|
|
|
11089
11089
|
// node_modules/.pnpm/ky@1.14.3/node_modules/ky/distribution/utils/timeout.js
|
|
11090
11090
|
async function timeout(request, init, abortController, options) {
|
|
11091
|
-
return new Promise((
|
|
11091
|
+
return new Promise((resolve12, reject) => {
|
|
11092
11092
|
const timeoutId = setTimeout(() => {
|
|
11093
11093
|
if (abortController) {
|
|
11094
11094
|
abortController.abort();
|
|
11095
11095
|
}
|
|
11096
11096
|
reject(new TimeoutError(request));
|
|
11097
11097
|
}, options.timeout);
|
|
11098
|
-
void options.fetch(request, init).then(
|
|
11098
|
+
void options.fetch(request, init).then(resolve12).catch(reject).then(() => {
|
|
11099
11099
|
clearTimeout(timeoutId);
|
|
11100
11100
|
});
|
|
11101
11101
|
});
|
|
@@ -11103,7 +11103,7 @@ async function timeout(request, init, abortController, options) {
|
|
|
11103
11103
|
|
|
11104
11104
|
// node_modules/.pnpm/ky@1.14.3/node_modules/ky/distribution/utils/delay.js
|
|
11105
11105
|
async function delay(ms, { signal }) {
|
|
11106
|
-
return new Promise((
|
|
11106
|
+
return new Promise((resolve12, reject) => {
|
|
11107
11107
|
if (signal) {
|
|
11108
11108
|
signal.throwIfAborted();
|
|
11109
11109
|
signal.addEventListener("abort", abortHandler, { once: true });
|
|
@@ -11114,7 +11114,7 @@ async function delay(ms, { signal }) {
|
|
|
11114
11114
|
}
|
|
11115
11115
|
const timeoutId = setTimeout(() => {
|
|
11116
11116
|
signal?.removeEventListener("abort", abortHandler);
|
|
11117
|
-
|
|
11117
|
+
resolve12();
|
|
11118
11118
|
}, ms);
|
|
11119
11119
|
});
|
|
11120
11120
|
}
|
|
@@ -12817,14 +12817,7 @@ import { basename as basename2 } from "node:path";
|
|
|
12817
12817
|
// src/config.ts
|
|
12818
12818
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
12819
12819
|
import { homedir } from "node:os";
|
|
12820
|
-
import {
|
|
12821
|
-
dirname,
|
|
12822
|
-
isAbsolute,
|
|
12823
|
-
join,
|
|
12824
|
-
normalize,
|
|
12825
|
-
resolve,
|
|
12826
|
-
win32
|
|
12827
|
-
} from "node:path";
|
|
12820
|
+
import { dirname, join, resolve } from "node:path";
|
|
12828
12821
|
var CONFIG_FILE_NAME = ".gji.json";
|
|
12829
12822
|
var GLOBAL_CONFIG_DIRECTORY = ".config/gji";
|
|
12830
12823
|
var GLOBAL_CONFIG_NAME = "config.json";
|
|
@@ -12836,8 +12829,6 @@ var KNOWN_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
12836
12829
|
"hooks",
|
|
12837
12830
|
"installSaveTarget",
|
|
12838
12831
|
"shellIntegration",
|
|
12839
|
-
"skipInstallPrompt",
|
|
12840
|
-
"syncDirs",
|
|
12841
12832
|
"syncDefaultBranch",
|
|
12842
12833
|
"syncFiles",
|
|
12843
12834
|
"syncRemote",
|
|
@@ -12847,22 +12838,19 @@ var KNOWN_GLOBAL_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
12847
12838
|
...KNOWN_CONFIG_KEYS,
|
|
12848
12839
|
"repos"
|
|
12849
12840
|
]);
|
|
12841
|
+
var REMOVED_CONFIG_KEYS = /* @__PURE__ */ new Set(["syncDirs"]);
|
|
12850
12842
|
var DEFAULT_CONFIG = Object.freeze({});
|
|
12851
12843
|
async function loadConfig(root) {
|
|
12852
12844
|
const path9 = join(root, CONFIG_FILE_NAME);
|
|
12853
12845
|
return loadConfigFile(path9);
|
|
12854
12846
|
}
|
|
12855
12847
|
async function loadEffectiveConfig(root, home = homedir(), onWarning) {
|
|
12856
|
-
return (await loadEffectiveConfigResult(root, home, onWarning)).config;
|
|
12857
|
-
}
|
|
12858
|
-
async function loadEffectiveConfigResult(root, home = homedir(), onWarning) {
|
|
12859
12848
|
const [globalConfig, localConfig] = await Promise.all([
|
|
12860
12849
|
loadGlobalConfig(home),
|
|
12861
12850
|
loadConfig(root)
|
|
12862
12851
|
]);
|
|
12863
12852
|
const repos = globalConfig.config.repos;
|
|
12864
12853
|
const perRepoConfig = isPlainObject(repos) ? findPerRepoConfig(repos, root, home) : {};
|
|
12865
|
-
validateSyncDirsConfig(perRepoConfig.syncDirs);
|
|
12866
12854
|
const globalBase = { ...globalConfig.config };
|
|
12867
12855
|
delete globalBase.repos;
|
|
12868
12856
|
if (onWarning) {
|
|
@@ -12905,10 +12893,7 @@ async function loadEffectiveConfigResult(root, home = homedir(), onWarning) {
|
|
|
12905
12893
|
if (Object.keys(globalHooks).length > 0 || Object.keys(perRepoHooks).length > 0 || Object.keys(localHooks).length > 0) {
|
|
12906
12894
|
merged.hooks = { ...globalHooks, ...perRepoHooks, ...localHooks };
|
|
12907
12895
|
}
|
|
12908
|
-
return
|
|
12909
|
-
config: toEffectiveConfig(merged),
|
|
12910
|
-
dependencyBootstrapExplicit: Object.hasOwn(globalBase, "dependencyBootstrap") || Object.hasOwn(perRepoConfig, "dependencyBootstrap") || Object.hasOwn(localConfig.config, "dependencyBootstrap")
|
|
12911
|
-
};
|
|
12896
|
+
return toEffectiveConfig(merged);
|
|
12912
12897
|
}
|
|
12913
12898
|
async function loadGlobalConfig(home = homedir()) {
|
|
12914
12899
|
return loadConfigFile(GLOBAL_CONFIG_FILE_PATH(home));
|
|
@@ -12920,15 +12905,6 @@ async function saveLocalConfig(root, config) {
|
|
|
12920
12905
|
`, "utf8");
|
|
12921
12906
|
return path9;
|
|
12922
12907
|
}
|
|
12923
|
-
async function updateLocalConfigKey(root, key, value) {
|
|
12924
|
-
const loaded = await loadConfig(root);
|
|
12925
|
-
const nextConfig = {
|
|
12926
|
-
...loaded.config,
|
|
12927
|
-
[key]: value
|
|
12928
|
-
};
|
|
12929
|
-
await saveLocalConfig(root, nextConfig);
|
|
12930
|
-
return nextConfig;
|
|
12931
|
-
}
|
|
12932
12908
|
async function saveGlobalConfig(config, home = homedir()) {
|
|
12933
12909
|
validateConfigValues(config);
|
|
12934
12910
|
const path9 = GLOBAL_CONFIG_FILE_PATH(home);
|
|
@@ -12945,6 +12921,7 @@ async function unsetGlobalConfigKey(key, home = homedir()) {
|
|
|
12945
12921
|
return nextConfig;
|
|
12946
12922
|
}
|
|
12947
12923
|
async function updateGlobalConfigKey(key, value, home = homedir()) {
|
|
12924
|
+
assertConfigKeySupported(key);
|
|
12948
12925
|
const loaded = await loadGlobalConfig(home);
|
|
12949
12926
|
const nextConfig = {
|
|
12950
12927
|
...loaded.config,
|
|
@@ -12953,15 +12930,6 @@ async function updateGlobalConfigKey(key, value, home = homedir()) {
|
|
|
12953
12930
|
await saveGlobalConfig(nextConfig, home);
|
|
12954
12931
|
return nextConfig;
|
|
12955
12932
|
}
|
|
12956
|
-
async function updateGlobalRepoConfigKey(repoRoot, key, value, home = homedir()) {
|
|
12957
|
-
const loaded = await loadGlobalConfig(home);
|
|
12958
|
-
const repos = isPlainObject(loaded.config.repos) ? { ...loaded.config.repos } : {};
|
|
12959
|
-
const existing = isPlainObject(repos[repoRoot]) ? repos[repoRoot] : {};
|
|
12960
|
-
repos[repoRoot] = { ...existing, [key]: value };
|
|
12961
|
-
const nextConfig = { ...loaded.config, repos };
|
|
12962
|
-
await saveGlobalConfig(nextConfig, home);
|
|
12963
|
-
return nextConfig;
|
|
12964
|
-
}
|
|
12965
12933
|
function GLOBAL_CONFIG_FILE_PATH(home = homedir()) {
|
|
12966
12934
|
const configDir = process.env.GJI_CONFIG_DIR;
|
|
12967
12935
|
if (configDir) {
|
|
@@ -12980,32 +12948,19 @@ function resolveConfigString(config, key) {
|
|
|
12980
12948
|
const value = config[key];
|
|
12981
12949
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
12982
12950
|
}
|
|
12983
|
-
function
|
|
12984
|
-
|
|
12951
|
+
function assertConfigKeySupported(key) {
|
|
12952
|
+
if (REMOVED_CONFIG_KEYS.has(key)) {
|
|
12953
|
+
throw new Error(`configuration key "${key}" is no longer supported`);
|
|
12954
|
+
}
|
|
12985
12955
|
}
|
|
12986
12956
|
function normalizeDependencyBootstrap(value) {
|
|
12987
12957
|
if (value === void 0) return;
|
|
12988
|
-
if (value !== "off"
|
|
12989
|
-
throw new Error(
|
|
12990
|
-
'dependencyBootstrap: expected "off", "cow-then-repair", or "install-only"'
|
|
12991
|
-
);
|
|
12958
|
+
if (value !== "off") {
|
|
12959
|
+
throw new Error('dependencyBootstrap: expected "off"');
|
|
12992
12960
|
}
|
|
12993
12961
|
return value;
|
|
12994
12962
|
}
|
|
12995
|
-
function normalizeSyncDirsConfig(value) {
|
|
12996
|
-
if (value === void 0) return;
|
|
12997
|
-
if (!Array.isArray(value)) {
|
|
12998
|
-
throw new Error("syncDirs: expected an array of relative directory paths");
|
|
12999
|
-
}
|
|
13000
|
-
return value.map((pattern) => {
|
|
13001
|
-
if (typeof pattern !== "string") {
|
|
13002
|
-
throw new Error("syncDirs: every entry must be a string");
|
|
13003
|
-
}
|
|
13004
|
-
return validateSyncDirPattern(pattern);
|
|
13005
|
-
});
|
|
13006
|
-
}
|
|
13007
12963
|
function validateConfigValues(config) {
|
|
13008
|
-
validateSyncDirsConfig(config.syncDirs);
|
|
13009
12964
|
normalizeDependencyBootstrap(config.dependencyBootstrap);
|
|
13010
12965
|
const repos = config.repos;
|
|
13011
12966
|
if (!isPlainObject(repos)) return;
|
|
@@ -13015,29 +12970,6 @@ function validateConfigValues(config) {
|
|
|
13015
12970
|
}
|
|
13016
12971
|
}
|
|
13017
12972
|
}
|
|
13018
|
-
function validateSyncDirPattern(pattern) {
|
|
13019
|
-
if (pattern.length === 0 || isAbsolute(pattern) || win32.isAbsolute(pattern)) {
|
|
13020
|
-
throw new Error(
|
|
13021
|
-
`syncDirs: pattern must be a relative path, got: ${pattern}`
|
|
13022
|
-
);
|
|
13023
|
-
}
|
|
13024
|
-
const segments = pattern.split(/[\\/]+/u);
|
|
13025
|
-
if (segments.includes("..")) {
|
|
13026
|
-
throw new Error(
|
|
13027
|
-
`syncDirs: pattern must not contain '..' segments, got: ${pattern}`
|
|
13028
|
-
);
|
|
13029
|
-
}
|
|
13030
|
-
if (segments.some((segment) => segment.toLowerCase() === ".git")) {
|
|
13031
|
-
throw new Error(`syncDirs: pattern must not include .git, got: ${pattern}`);
|
|
13032
|
-
}
|
|
13033
|
-
const normalized = normalize(pattern);
|
|
13034
|
-
if (normalized === ".") {
|
|
13035
|
-
throw new Error(
|
|
13036
|
-
`syncDirs: pattern must name a directory below the repository root, got: ${pattern}`
|
|
13037
|
-
);
|
|
13038
|
-
}
|
|
13039
|
-
return normalized;
|
|
13040
|
-
}
|
|
13041
12973
|
async function loadConfigFile(path9) {
|
|
13042
12974
|
try {
|
|
13043
12975
|
const rawConfig = await readFile(path9, "utf8");
|
|
@@ -13086,12 +13018,6 @@ function toEffectiveConfig(config) {
|
|
|
13086
13018
|
delete effective[key];
|
|
13087
13019
|
}
|
|
13088
13020
|
}
|
|
13089
|
-
if (effective.skipInstallPrompt !== void 0 && typeof effective.skipInstallPrompt !== "boolean") {
|
|
13090
|
-
delete effective.skipInstallPrompt;
|
|
13091
|
-
}
|
|
13092
|
-
if (config.syncDirs !== void 0) {
|
|
13093
|
-
effective.syncDirs = normalizeSyncDirsConfig(config.syncDirs);
|
|
13094
|
-
}
|
|
13095
13021
|
if (Array.isArray(config.syncFiles)) {
|
|
13096
13022
|
effective.syncFiles = config.syncFiles.filter(
|
|
13097
13023
|
(value) => typeof value === "string"
|
|
@@ -13219,7 +13145,7 @@ async function runArgvHook(hookCmd, cwd, context, stderr, stdout3) {
|
|
|
13219
13145
|
stderr("gji: hook argv command must include a non-empty command\n");
|
|
13220
13146
|
return;
|
|
13221
13147
|
}
|
|
13222
|
-
await new Promise((
|
|
13148
|
+
await new Promise((resolve12) => {
|
|
13223
13149
|
const child = spawn2(command, args, {
|
|
13224
13150
|
cwd,
|
|
13225
13151
|
shell: false,
|
|
@@ -13239,18 +13165,18 @@ async function runArgvHook(hookCmd, cwd, context, stderr, stdout3) {
|
|
|
13239
13165
|
`
|
|
13240
13166
|
);
|
|
13241
13167
|
}
|
|
13242
|
-
|
|
13168
|
+
resolve12();
|
|
13243
13169
|
});
|
|
13244
13170
|
child.on("error", (err) => {
|
|
13245
13171
|
stderr(`gji: hook failed to start: ${err.message}
|
|
13246
13172
|
`);
|
|
13247
|
-
|
|
13173
|
+
resolve12();
|
|
13248
13174
|
});
|
|
13249
13175
|
});
|
|
13250
13176
|
}
|
|
13251
13177
|
async function runShellHook(hookCmd, cwd, context, stderr, stdout3) {
|
|
13252
13178
|
const interpolated = interpolate(hookCmd, context);
|
|
13253
|
-
await new Promise((
|
|
13179
|
+
await new Promise((resolve12) => {
|
|
13254
13180
|
const child = spawn2(interpolated, {
|
|
13255
13181
|
cwd,
|
|
13256
13182
|
shell: true,
|
|
@@ -13268,12 +13194,12 @@ async function runShellHook(hookCmd, cwd, context, stderr, stdout3) {
|
|
|
13268
13194
|
stderr(`gji: hook exited with code ${code}: ${interpolated}
|
|
13269
13195
|
`);
|
|
13270
13196
|
}
|
|
13271
|
-
|
|
13197
|
+
resolve12();
|
|
13272
13198
|
});
|
|
13273
13199
|
child.on("error", (err) => {
|
|
13274
13200
|
stderr(`gji: hook failed to start: ${err.message}
|
|
13275
13201
|
`);
|
|
13276
|
-
|
|
13202
|
+
resolve12();
|
|
13277
13203
|
});
|
|
13278
13204
|
});
|
|
13279
13205
|
}
|
|
@@ -13330,7 +13256,7 @@ function createNavigationRepository(name, root) {
|
|
|
13330
13256
|
// src/repo.ts
|
|
13331
13257
|
import { access } from "node:fs/promises";
|
|
13332
13258
|
import { homedir as homedir3 } from "node:os";
|
|
13333
|
-
import { basename, dirname as dirname3, isAbsolute
|
|
13259
|
+
import { basename, dirname as dirname3, isAbsolute, join as join3, resolve as resolve3 } from "node:path";
|
|
13334
13260
|
|
|
13335
13261
|
// src/git.ts
|
|
13336
13262
|
import { execFile } from "node:child_process";
|
|
@@ -13480,7 +13406,7 @@ function hasExitCode(error, code) {
|
|
|
13480
13406
|
async function detectRepository(cwd) {
|
|
13481
13407
|
const currentRoot = await runGit(cwd, ["rev-parse", "--show-toplevel"]);
|
|
13482
13408
|
const rawCommonDir = await runGit(cwd, ["rev-parse", "--git-common-dir"]);
|
|
13483
|
-
const gitCommonDir =
|
|
13409
|
+
const gitCommonDir = isAbsolute(rawCommonDir) ? rawCommonDir : resolve3(currentRoot, rawCommonDir);
|
|
13484
13410
|
const repoRoot = dirname3(gitCommonDir);
|
|
13485
13411
|
return {
|
|
13486
13412
|
currentRoot,
|
|
@@ -13492,7 +13418,7 @@ async function detectRepository(cwd) {
|
|
|
13492
13418
|
}
|
|
13493
13419
|
async function worktreeGitDir(worktreePath) {
|
|
13494
13420
|
const rawGitDir = await runGit(worktreePath, ["rev-parse", "--git-dir"]);
|
|
13495
|
-
return
|
|
13421
|
+
return isAbsolute(rawGitDir) ? rawGitDir : resolve3(worktreePath, rawGitDir);
|
|
13496
13422
|
}
|
|
13497
13423
|
function resolveWorktreePath(repoRoot, branch, basePath) {
|
|
13498
13424
|
const segments = branch.split("/").filter(Boolean);
|
|
@@ -14743,7 +14669,7 @@ async function acquireUndoLogLock(path9) {
|
|
|
14743
14669
|
const remainingMs = deadline - Date.now();
|
|
14744
14670
|
if (remainingMs > 0)
|
|
14745
14671
|
await new Promise(
|
|
14746
|
-
(
|
|
14672
|
+
(resolve12) => setTimeout(resolve12, Math.min(UNDO_LOCK_RETRY_DELAY_MS, remainingMs))
|
|
14747
14673
|
);
|
|
14748
14674
|
}
|
|
14749
14675
|
}
|
|
@@ -16935,7 +16861,7 @@ function normalizeShell(value) {
|
|
|
16935
16861
|
var TOP_LEVEL_COMMANDS = [
|
|
16936
16862
|
{
|
|
16937
16863
|
name: "new",
|
|
16938
|
-
description: "create a new branch or detached linked worktree
|
|
16864
|
+
description: "create a new branch or detached linked worktree"
|
|
16939
16865
|
},
|
|
16940
16866
|
{
|
|
16941
16867
|
name: "done",
|
|
@@ -17087,7 +17013,7 @@ _gji_completion() {
|
|
|
17087
17013
|
|
|
17088
17014
|
case "$command_name" in
|
|
17089
17015
|
new)
|
|
17090
|
-
COMPREPLY=( $(compgen -W "--detached --from-current --no-fetch --take --copy --task --force --open --editor --dry-run --json --help" -- "$cur") )
|
|
17016
|
+
COMPREPLY=( $(compgen -W "--detached --from-current --no-fetch --take --copy --task --force --open --editor --dry-run --no-install --json --help" -- "$cur") )
|
|
17091
17017
|
;;
|
|
17092
17018
|
done)
|
|
17093
17019
|
COMPREPLY=( $(compgen -W "--force --keep-branch --json --help" -- "$cur") )
|
|
@@ -17125,7 +17051,7 @@ _gji_completion() {
|
|
|
17125
17051
|
COMPREPLY=( $(compgen -W "$(__gji_pr_targets) --select --help" -- "$cur") )
|
|
17126
17052
|
fi
|
|
17127
17053
|
else
|
|
17128
|
-
COMPREPLY=( $(compgen -W "--dry-run --json --help" -- "$cur") )
|
|
17054
|
+
COMPREPLY=( $(compgen -W "--dry-run --no-install --json --help" -- "$cur") )
|
|
17129
17055
|
fi
|
|
17130
17056
|
;;
|
|
17131
17057
|
back)
|
|
@@ -17343,6 +17269,7 @@ complete -c gji -n '__fish_seen_subcommand_from new' -l force -d 'remove and rec
|
|
|
17343
17269
|
complete -c gji -n '__fish_seen_subcommand_from new' -l open -d 'open the new worktree in an editor after creation'
|
|
17344
17270
|
complete -c gji -n '__fish_seen_subcommand_from new' -l editor -r -d 'editor CLI to use with --open (code, cursor, zed, \u2026)'
|
|
17345
17271
|
complete -c gji -n '__fish_seen_subcommand_from new' -l dry-run -d 'show what would be created without executing any git commands or writing files'
|
|
17272
|
+
complete -c gji -n '__fish_seen_subcommand_from new' -l no-install -d 'skip automatic dependency setup in the new worktree'
|
|
17346
17273
|
complete -c gji -n '__fish_seen_subcommand_from new' -l json -d 'emit JSON on success or error instead of human-readable output'
|
|
17347
17274
|
|
|
17348
17275
|
complete -c gji -n '__fish_seen_subcommand_from init' -l write -d 'write the integration to the shell config file'
|
|
@@ -17358,6 +17285,7 @@ complete -c gji -n '__fish_seen_subcommand_from completion' -a 'fish' -d 'shell'
|
|
|
17358
17285
|
complete -c gji -n '__fish_seen_subcommand_from completion' -a 'zsh' -d 'shell'
|
|
17359
17286
|
|
|
17360
17287
|
complete -c gji -n '__fish_seen_subcommand_from pr; and test (commandline -opc)[3] != open' -l dry-run -d 'show what would be created without executing any git commands or writing files'
|
|
17288
|
+
complete -c gji -n '__fish_seen_subcommand_from pr; and test (commandline -opc)[3] != open' -l no-install -d 'skip automatic dependency setup in the PR worktree'
|
|
17361
17289
|
complete -c gji -n '__fish_seen_subcommand_from pr; and test (commandline -opc)[3] != open' -l json -d 'emit JSON on success or error instead of human-readable output'
|
|
17362
17290
|
complete -c gji -n '__fish_seen_subcommand_from pr' -a 'open' -d 'open a pull request in the default browser'
|
|
17363
17291
|
complete -c gji -n '__gji_should_complete_pr_select' -l select -d 'choose a pull request from any linked worktree'
|
|
@@ -17501,7 +17429,7 @@ fi
|
|
|
17501
17429
|
|
|
17502
17430
|
case "\${words[2]}" in
|
|
17503
17431
|
new)
|
|
17504
|
-
_arguments '--detached[create a detached worktree without a branch]' '--from-current[base the new branch on the current worktree instead of the main worktree]' '--no-fetch[skip refreshing the remote default branch before creating the new branch]' '--take[move current uncommitted changes into the new worktree]' '--copy[copy current uncommitted changes instead of moving them (requires --take)]' '--task[record the purpose of the new worktree]:description:' '--force[remove and recreate the worktree if the target path already exists]' '--open[open the new worktree in an editor after creation]' '--editor[editor CLI to use with --open (code, cursor, zed, \u2026)]:editor:' '--dry-run[show what would be created without executing any git commands or writing files]' '--json[emit JSON on success or error instead of human-readable output]' '2:branch: '
|
|
17432
|
+
_arguments '--detached[create a detached worktree without a branch]' '--from-current[base the new branch on the current worktree instead of the main worktree]' '--no-fetch[skip refreshing the remote default branch before creating the new branch]' '--take[move current uncommitted changes into the new worktree]' '--copy[copy current uncommitted changes instead of moving them (requires --take)]' '--task[record the purpose of the new worktree]:description:' '--force[remove and recreate the worktree if the target path already exists]' '--open[open the new worktree in an editor after creation]' '--editor[editor CLI to use with --open (code, cursor, zed, \u2026)]:editor:' '--dry-run[show what would be created without executing any git commands or writing files]' '--no-install[skip automatic dependency setup in the new worktree]' '--json[emit JSON on success or error instead of human-readable output]' '2:branch: '
|
|
17505
17433
|
;;
|
|
17506
17434
|
done)
|
|
17507
17435
|
_arguments '--force[remove dirty or unmerged worktrees without prompting]' '--keep-branch[remove the worktree but preserve its branch]' '--json[emit JSON on success or error instead of human-readable output]' '2:branch: '
|
|
@@ -17528,7 +17456,7 @@ case "\${words[2]}" in
|
|
|
17528
17456
|
_arguments '--select[choose a pull request from any linked worktree]' '4:branch or PR number:->pr_targets'
|
|
17529
17457
|
fi
|
|
17530
17458
|
else
|
|
17531
|
-
_arguments '--dry-run[show what would be created without executing any git commands or writing files]' '--json[emit JSON on success or error instead of human-readable output]' '2:ref:(open)'
|
|
17459
|
+
_arguments '--dry-run[show what would be created without executing any git commands or writing files]' '--no-install[skip automatic dependency setup in the PR worktree]' '--json[emit JSON on success or error instead of human-readable output]' '2:ref:(open)'
|
|
17532
17460
|
fi
|
|
17533
17461
|
;;
|
|
17534
17462
|
back)
|
|
@@ -17706,9 +17634,9 @@ var EDITORS = [
|
|
|
17706
17634
|
];
|
|
17707
17635
|
async function defaultSpawnEditor(cli, args) {
|
|
17708
17636
|
const child = spawn3(cli, args, { detached: true, stdio: "ignore" });
|
|
17709
|
-
await new Promise((
|
|
17637
|
+
await new Promise((resolve12, reject) => {
|
|
17710
17638
|
child.once("error", reject);
|
|
17711
|
-
child.once("spawn",
|
|
17639
|
+
child.once("spawn", resolve12);
|
|
17712
17640
|
});
|
|
17713
17641
|
child.unref();
|
|
17714
17642
|
}
|
|
@@ -17797,8 +17725,8 @@ async function removeMissingRegistryEntries(paths, home = homedir6()) {
|
|
|
17797
17725
|
async function withRegistryLock(registryPath, operation) {
|
|
17798
17726
|
const previous = registryLocks.get(registryPath);
|
|
17799
17727
|
let release;
|
|
17800
|
-
const current = new Promise((
|
|
17801
|
-
release =
|
|
17728
|
+
const current = new Promise((resolve12) => {
|
|
17729
|
+
release = resolve12;
|
|
17802
17730
|
});
|
|
17803
17731
|
registryLocks.set(registryPath, current);
|
|
17804
17732
|
if (previous) await previous;
|
|
@@ -18685,7 +18613,7 @@ function toMessage3(error) {
|
|
|
18685
18613
|
}
|
|
18686
18614
|
|
|
18687
18615
|
// src/go.ts
|
|
18688
|
-
import { basename as
|
|
18616
|
+
import { basename as basename9 } from "node:path";
|
|
18689
18617
|
|
|
18690
18618
|
// src/context-card.ts
|
|
18691
18619
|
async function renderContextCard(worktreePath) {
|
|
@@ -18712,42 +18640,15 @@ function formatLastCommit2(info) {
|
|
|
18712
18640
|
}
|
|
18713
18641
|
|
|
18714
18642
|
// src/pr.ts
|
|
18715
|
-
import { execFile as
|
|
18716
|
-
import { mkdir as
|
|
18717
|
-
import { dirname as
|
|
18718
|
-
import { promisify as
|
|
18719
|
-
|
|
18720
|
-
// src/format-bytes.ts
|
|
18721
|
-
function formatBytes(bytes) {
|
|
18722
|
-
if (bytes === void 0) return "size unavailable";
|
|
18723
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
18724
|
-
const units = ["KB", "MB", "GB", "TB"];
|
|
18725
|
-
let value = bytes;
|
|
18726
|
-
let unit = -1;
|
|
18727
|
-
while (value >= 1024 && unit < units.length - 1) {
|
|
18728
|
-
value /= 1024;
|
|
18729
|
-
unit += 1;
|
|
18730
|
-
}
|
|
18731
|
-
return `${value.toFixed(value >= 10 ? 0 : 1)} ${units[unit]}`;
|
|
18732
|
-
}
|
|
18643
|
+
import { execFile as execFile6 } from "node:child_process";
|
|
18644
|
+
import { mkdir as mkdir7 } from "node:fs/promises";
|
|
18645
|
+
import { dirname as dirname10 } from "node:path";
|
|
18646
|
+
import { promisify as promisify7 } from "node:util";
|
|
18733
18647
|
|
|
18734
18648
|
// src/bootstrap-output.ts
|
|
18735
|
-
function createBootstrapReporter(write, json
|
|
18649
|
+
function createBootstrapReporter(write, json) {
|
|
18736
18650
|
return {
|
|
18737
|
-
measureCloneSize: measureCloneSize && !json,
|
|
18738
18651
|
write,
|
|
18739
|
-
cloned: (directory) => {
|
|
18740
|
-
if (json) return;
|
|
18741
|
-
write(
|
|
18742
|
-
`\u26A1 cloned ${directory.dir} (${formatBytes(directory.bytes)} \u2192 ${formatDuration(directory.ms)})
|
|
18743
|
-
`
|
|
18744
|
-
);
|
|
18745
|
-
},
|
|
18746
|
-
skipped: (directory) => {
|
|
18747
|
-
if (json) return;
|
|
18748
|
-
write(`gji: skipped ${directory.dir} \u2014 ${directory.reason}
|
|
18749
|
-
`);
|
|
18750
|
-
},
|
|
18751
18652
|
dependency: (event) => {
|
|
18752
18653
|
if (json) return;
|
|
18753
18654
|
const target = event.target ? ` ${event.target}` : "";
|
|
@@ -18756,29 +18657,15 @@ function createBootstrapReporter(write, json, measureCloneSize = false) {
|
|
|
18756
18657
|
}
|
|
18757
18658
|
};
|
|
18758
18659
|
}
|
|
18759
|
-
function formatDuration(ms) {
|
|
18760
|
-
return `${(ms / 1e3).toFixed(1)}s`;
|
|
18761
|
-
}
|
|
18762
18660
|
|
|
18763
18661
|
// src/dependency-bootstrap.ts
|
|
18764
|
-
import {
|
|
18765
|
-
import {
|
|
18766
|
-
import { lstat as lstat4, open as open3, readdir as readdir2, readFile as readFile9, realpath as realpath3, rm as rm4 } from "node:fs/promises";
|
|
18767
|
-
import {
|
|
18768
|
-
basename as basename7,
|
|
18769
|
-
dirname as dirname9,
|
|
18770
|
-
isAbsolute as isAbsolute4,
|
|
18771
|
-
join as join12,
|
|
18772
|
-
relative as relative3,
|
|
18773
|
-
resolve as resolve9,
|
|
18774
|
-
sep as sep3
|
|
18775
|
-
} from "node:path";
|
|
18776
|
-
import { promisify as promisify7 } from "node:util";
|
|
18662
|
+
import { readFile as readFile8, realpath as realpath3 } from "node:fs/promises";
|
|
18663
|
+
import { join as join12, relative as relative2, resolve as resolve9, sep as sep3 } from "node:path";
|
|
18777
18664
|
|
|
18778
18665
|
// src/command-runner.ts
|
|
18779
18666
|
import { spawn as spawn4 } from "node:child_process";
|
|
18780
18667
|
var runCommand = async (command, cwd, stderr, stdout3 = (chunk) => process.stdout.write(chunk), options) => {
|
|
18781
|
-
await new Promise((
|
|
18668
|
+
await new Promise((resolve12, reject) => {
|
|
18782
18669
|
const shell = options?.shell ?? true;
|
|
18783
18670
|
const [executable, args] = shell ? [command, []] : splitCommand(command);
|
|
18784
18671
|
const child = spawn4(executable, args, {
|
|
@@ -18797,7 +18684,7 @@ var runCommand = async (command, cwd, stderr, stdout3 = (chunk) => process.stdou
|
|
|
18797
18684
|
if (code !== 0) {
|
|
18798
18685
|
reject(new Error(`exited with code ${code}`));
|
|
18799
18686
|
} else {
|
|
18800
|
-
|
|
18687
|
+
resolve12();
|
|
18801
18688
|
}
|
|
18802
18689
|
});
|
|
18803
18690
|
child.on("error", reject);
|
|
@@ -18813,32 +18700,6 @@ function splitCommand(command) {
|
|
|
18813
18700
|
];
|
|
18814
18701
|
}
|
|
18815
18702
|
|
|
18816
|
-
// src/dir-clone.ts
|
|
18817
|
-
import { execFile as execFile5 } from "node:child_process";
|
|
18818
|
-
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
18819
|
-
import { constants as constants5 } from "node:fs";
|
|
18820
|
-
import {
|
|
18821
|
-
chmod,
|
|
18822
|
-
cp,
|
|
18823
|
-
lstat as lstat3,
|
|
18824
|
-
mkdir as mkdir7,
|
|
18825
|
-
mkdtemp,
|
|
18826
|
-
opendir,
|
|
18827
|
-
readdir,
|
|
18828
|
-
readFile as readFile8,
|
|
18829
|
-
readlink,
|
|
18830
|
-
realpath as realpath2,
|
|
18831
|
-
rename as rename3,
|
|
18832
|
-
rm as rm3,
|
|
18833
|
-
rmdir,
|
|
18834
|
-
symlink,
|
|
18835
|
-
unlink as unlink3,
|
|
18836
|
-
utimes,
|
|
18837
|
-
writeFile as writeFile8
|
|
18838
|
-
} from "node:fs/promises";
|
|
18839
|
-
import { basename as basename6, dirname as dirname8, join as join11, relative as relative2, resolve as resolve8, sep as sep2 } from "node:path";
|
|
18840
|
-
import { promisify as promisify6 } from "node:util";
|
|
18841
|
-
|
|
18842
18703
|
// src/fs-utils.ts
|
|
18843
18704
|
import { lstat } from "node:fs/promises";
|
|
18844
18705
|
async function pathExists2(path9) {
|
|
@@ -18866,7 +18727,7 @@ function hasErrorCode(error, code) {
|
|
|
18866
18727
|
// src/safe-destination.ts
|
|
18867
18728
|
import { constants as constants4 } from "node:fs";
|
|
18868
18729
|
import { lstat as lstat2, mkdir as mkdir6, open as open2 } from "node:fs/promises";
|
|
18869
|
-
import { isAbsolute as
|
|
18730
|
+
import { isAbsolute as isAbsolute2, join as join10, relative, resolve as resolve7, sep } from "node:path";
|
|
18870
18731
|
async function openDestinationDirectory(root, path9) {
|
|
18871
18732
|
const segments = destinationSegments(root, path9);
|
|
18872
18733
|
if (process.platform === "darwin") {
|
|
@@ -18909,7 +18770,7 @@ async function inspectDestination(root, path9) {
|
|
|
18909
18770
|
const resolvedPath = resolve7(path9);
|
|
18910
18771
|
const resolvedRoot = resolve7(root);
|
|
18911
18772
|
const distance = relative(resolvedRoot, resolvedPath);
|
|
18912
|
-
if (
|
|
18773
|
+
if (isAbsolute2(distance) || distance === ".." || distance.startsWith(`..${sep}`)) {
|
|
18913
18774
|
return { kind: "unsafe", reason: "destination escapes the worktree" };
|
|
18914
18775
|
}
|
|
18915
18776
|
let current = resolvedRoot;
|
|
@@ -18968,7 +18829,7 @@ function destinationSegments(root, path9) {
|
|
|
18968
18829
|
const resolvedRoot = resolve7(root);
|
|
18969
18830
|
const resolvedPath = resolve7(path9);
|
|
18970
18831
|
const distance = relative(resolvedRoot, resolvedPath);
|
|
18971
|
-
if (
|
|
18832
|
+
if (isAbsolute2(distance) || distance === ".." || distance.startsWith(`..${sep}`)) {
|
|
18972
18833
|
throw new Error("destination escapes the worktree");
|
|
18973
18834
|
}
|
|
18974
18835
|
return distance.split(sep).filter(Boolean);
|
|
@@ -18987,7 +18848,7 @@ async function ensureDestinationDirectory(root, path9) {
|
|
|
18987
18848
|
const resolvedRoot = resolve7(root);
|
|
18988
18849
|
const resolvedPath = resolve7(path9);
|
|
18989
18850
|
const distance = relative(resolvedRoot, resolvedPath);
|
|
18990
|
-
if (
|
|
18851
|
+
if (isAbsolute2(distance) || distance === ".." || distance.startsWith(`..${sep}`)) {
|
|
18991
18852
|
throw new Error("destination escapes the worktree");
|
|
18992
18853
|
}
|
|
18993
18854
|
let current = resolvedRoot;
|
|
@@ -19017,625 +18878,232 @@ async function ensureDestinationDirectory(root, path9) {
|
|
|
19017
18878
|
}
|
|
19018
18879
|
}
|
|
19019
18880
|
|
|
19020
|
-
// src/
|
|
18881
|
+
// src/uv-validation.ts
|
|
18882
|
+
import { execFile as execFile5 } from "node:child_process";
|
|
18883
|
+
import { constants as constants5 } from "node:fs";
|
|
18884
|
+
import { lstat as lstat3, open as open3, readdir, realpath as realpath2 } from "node:fs/promises";
|
|
18885
|
+
import { basename as basename6, dirname as dirname8, isAbsolute as isAbsolute3, join as join11, resolve as resolve8, sep as sep2 } from "node:path";
|
|
18886
|
+
import { promisify as promisify6 } from "node:util";
|
|
19021
18887
|
var execFileAsync5 = promisify6(execFile5);
|
|
19022
|
-
var
|
|
19023
|
-
var
|
|
19024
|
-
var
|
|
19025
|
-
|
|
19026
|
-
|
|
19027
|
-
|
|
19028
|
-
|
|
19029
|
-
|
|
19030
|
-
|
|
19031
|
-
}
|
|
19032
|
-
const sourcePath = await realpath2(source);
|
|
19033
|
-
const sourceStats = await lstat3(sourcePath);
|
|
19034
|
-
if (!sourceStats.isDirectory()) {
|
|
19035
|
-
throw new Error("source is not a directory");
|
|
19036
|
-
}
|
|
19037
|
-
const destinationPath = await resolveProspectivePath(destination);
|
|
19038
|
-
const destinationDistance = relative2(sourcePath, destinationPath);
|
|
19039
|
-
if (destinationDistance === "" || destinationDistance !== ".." && !destinationDistance.startsWith(`..${sep2}`)) {
|
|
19040
|
-
throw new Error("clone destination must not be inside its source");
|
|
19041
|
-
}
|
|
19042
|
-
const startedAt = Date.now();
|
|
19043
|
-
const parent = dirname8(destination);
|
|
19044
|
-
let safeParent;
|
|
19045
|
-
try {
|
|
19046
|
-
if (options.destinationRoot) {
|
|
19047
|
-
const parentInspection = await inspectDestination(
|
|
19048
|
-
options.destinationRoot,
|
|
19049
|
-
parent
|
|
19050
|
-
);
|
|
19051
|
-
if (parentInspection.kind === "unsafe") {
|
|
19052
|
-
throw new Error(parentInspection.reason);
|
|
19053
|
-
}
|
|
19054
|
-
safeParent = await openDestinationDirectory(
|
|
19055
|
-
options.destinationRoot,
|
|
19056
|
-
parent
|
|
19057
|
-
);
|
|
19058
|
-
} else {
|
|
19059
|
-
await mkdir7(parent, { recursive: true });
|
|
19060
|
-
}
|
|
19061
|
-
const operationParent = safeParent?.path ?? parent;
|
|
19062
|
-
const operationDestination = join11(operationParent, basename6(destination));
|
|
19063
|
-
const cloneLock = await acquireCloneLock(
|
|
19064
|
-
`${operationDestination}${CLONE_LOCK_SUFFIX}`,
|
|
19065
|
-
destination
|
|
18888
|
+
var UV_VALIDATION_MAX_ENTRIES = 1e4;
|
|
18889
|
+
var UV_VALIDATION_MAX_FILE_BYTES = 1024 * 1024;
|
|
18890
|
+
var UV_VALIDATION_MAX_TOTAL_BYTES = 16 * 1024 * 1024;
|
|
18891
|
+
function virtualEnvironmentPrefixes(text) {
|
|
18892
|
+
const prefixes = [];
|
|
18893
|
+
for (const line of text.split(/\r?\n/u)) {
|
|
18894
|
+
if (!/\bVIRTUAL_ENV\b/u.test(line)) continue;
|
|
18895
|
+
const quotedAssignment = line.match(
|
|
18896
|
+
/\bVIRTUAL_ENV(?:\s*=|\s+)\s*(?:"([^"]+)"|'([^']+)')/u
|
|
19066
18897
|
);
|
|
19067
|
-
const
|
|
19068
|
-
|
|
19069
|
-
|
|
19070
|
-
|
|
19071
|
-
try {
|
|
19072
|
-
if (await destinationExists(operationDestination)) {
|
|
19073
|
-
throw new CloneDestinationExistsError(destination);
|
|
19074
|
-
}
|
|
19075
|
-
reservation = await reserveDestination(operationDestination);
|
|
19076
|
-
temporaryRoot = await mkdtemp(
|
|
19077
|
-
join11(operationParent, `.${basename6(destination)}.gji-clone-`)
|
|
19078
|
-
);
|
|
19079
|
-
const temporaryDestination = join11(temporaryRoot, basename6(destination));
|
|
19080
|
-
try {
|
|
19081
|
-
await (options.copyDirectory ?? ((sourcePath2, targetPath) => copyDirectoryWithCow(
|
|
19082
|
-
sourcePath2,
|
|
19083
|
-
targetPath,
|
|
19084
|
-
platform3,
|
|
19085
|
-
options.runLinuxCommand
|
|
19086
|
-
)))(sourcePath, temporaryDestination);
|
|
19087
|
-
} catch (error) {
|
|
19088
|
-
if (error instanceof CloneUnsupportedError) throw error;
|
|
19089
|
-
if (isUnsupportedCloneError(error)) {
|
|
19090
|
-
throw new CloneUnsupportedError(toErrorMessage(error));
|
|
19091
|
-
}
|
|
19092
|
-
throw error;
|
|
19093
|
-
}
|
|
19094
|
-
await publishCloneContents(
|
|
19095
|
-
temporaryDestination,
|
|
19096
|
-
operationDestination,
|
|
19097
|
-
reservation.path,
|
|
19098
|
-
(entry) => publishedEntries.push(entry),
|
|
19099
|
-
options.copyFile ?? runForcedCloneFileCopy
|
|
19100
|
-
);
|
|
19101
|
-
reservation = void 0;
|
|
19102
|
-
} finally {
|
|
19103
|
-
stopLockHeartbeat();
|
|
19104
|
-
if (reservation) {
|
|
19105
|
-
await cleanupReservedDestination(
|
|
19106
|
-
operationDestination,
|
|
19107
|
-
reservation,
|
|
19108
|
-
publishedEntries
|
|
19109
|
-
);
|
|
19110
|
-
}
|
|
19111
|
-
if (temporaryRoot) {
|
|
19112
|
-
await rm3(temporaryRoot, { force: true, recursive: true }).catch(
|
|
19113
|
-
() => void 0
|
|
19114
|
-
);
|
|
19115
|
-
}
|
|
19116
|
-
await releaseCloneLock(cloneLock).catch(() => void 0);
|
|
19117
|
-
}
|
|
19118
|
-
return cloneResult(sourcePath, startedAt, options.measureBytes);
|
|
19119
|
-
} finally {
|
|
19120
|
-
await safeParent?.close().catch(() => void 0);
|
|
18898
|
+
const quotedPrefix = quotedAssignment?.[1] ?? quotedAssignment?.[2];
|
|
18899
|
+
if (quotedPrefix && isAbsolute3(quotedPrefix)) prefixes.push(quotedPrefix);
|
|
18900
|
+
const unquoted = line.match(/\bVIRTUAL_ENV(?:\s*=|\s+)\s*([^\s"']+)/u)?.[1];
|
|
18901
|
+
if (unquoted && isAbsolute3(unquoted)) prefixes.push(unquoted);
|
|
19121
18902
|
}
|
|
18903
|
+
return uniquePaths(prefixes);
|
|
19122
18904
|
}
|
|
19123
|
-
|
|
19124
|
-
|
|
19125
|
-
|
|
19126
|
-
|
|
19127
|
-
|
|
19128
|
-
|
|
19129
|
-
|
|
19130
|
-
|
|
19131
|
-
|
|
19132
|
-
|
|
18905
|
+
function pythonPrefixFromShebang(text) {
|
|
18906
|
+
const command = text.match(/^#!\s*([^\r\n]+)/u)?.[1]?.trim().split(/\s+/u)[0];
|
|
18907
|
+
if (!command || !isAbsolute3(command)) return void 0;
|
|
18908
|
+
if (!/^python(?:\d+(?:\.\d+)?)?(?:\.exe)?$/iu.test(
|
|
18909
|
+
command.split(sep2).at(-1) ?? ""
|
|
18910
|
+
)) {
|
|
18911
|
+
return void 0;
|
|
18912
|
+
}
|
|
18913
|
+
return dirname8(dirname8(command));
|
|
18914
|
+
}
|
|
18915
|
+
function pythonPrefixesFromText(text) {
|
|
18916
|
+
const interpreters = [];
|
|
18917
|
+
for (const match of text.matchAll(/["'](\/[^"']+)["']/gu)) {
|
|
18918
|
+
if (match[1]) interpreters.push(match[1]);
|
|
19133
18919
|
}
|
|
19134
|
-
const
|
|
19135
|
-
|
|
19136
|
-
throw new CloneUnsupportedError(`platform ${platform3} has no CoW strategy`);
|
|
18920
|
+
for (const match of text.matchAll(/(?:^|\s)(\/[^\s"']+)/gmu)) {
|
|
18921
|
+
if (match[1]) interpreters.push(match[1]);
|
|
19137
18922
|
}
|
|
19138
|
-
|
|
19139
|
-
|
|
19140
|
-
|
|
18923
|
+
return uniquePaths(
|
|
18924
|
+
interpreters.flatMap((interpreter) => {
|
|
18925
|
+
const name = interpreter.split(sep2).at(-1) ?? "";
|
|
18926
|
+
return /^python(?:\d+(?:\.\d+)?)?(?:\.exe)?$/iu.test(name) ? [dirname8(dirname8(interpreter))] : [];
|
|
18927
|
+
})
|
|
19141
18928
|
);
|
|
19142
18929
|
}
|
|
19143
|
-
async function
|
|
19144
|
-
|
|
19145
|
-
|
|
19146
|
-
|
|
19147
|
-
|
|
19148
|
-
|
|
19149
|
-
|
|
19150
|
-
|
|
19151
|
-
|
|
19152
|
-
|
|
19153
|
-
|
|
19154
|
-
|
|
18930
|
+
async function validateUvRelocation(target, context) {
|
|
18931
|
+
if (context.input === "preserve") return;
|
|
18932
|
+
if (!target.targetPath || !target.sourcePath) {
|
|
18933
|
+
throw new Error("uv environment target is not project-local");
|
|
18934
|
+
}
|
|
18935
|
+
const scriptsDirectory = join11(
|
|
18936
|
+
target.targetPath,
|
|
18937
|
+
process.platform === "win32" ? "Scripts" : "bin"
|
|
18938
|
+
);
|
|
18939
|
+
const interpreter = join11(
|
|
18940
|
+
scriptsDirectory,
|
|
18941
|
+
process.platform === "win32" ? "python.exe" : "python"
|
|
18942
|
+
);
|
|
18943
|
+
const { stdout: stdout3 } = await execFileAsync5(interpreter, [
|
|
18944
|
+
"-c",
|
|
18945
|
+
"import os, sys; print(os.path.realpath(sys.prefix))"
|
|
18946
|
+
]);
|
|
18947
|
+
const [actualPrefix, expectedPrefix] = await Promise.all([
|
|
18948
|
+
realpath2(stdout3.trim()),
|
|
18949
|
+
realpath2(target.targetPath)
|
|
18950
|
+
]);
|
|
18951
|
+
if (actualPrefix !== expectedPrefix) {
|
|
18952
|
+
throw new Error(
|
|
18953
|
+
`uv environment still points to its source prefix: ${actualPrefix}`
|
|
18954
|
+
);
|
|
18955
|
+
}
|
|
18956
|
+
const sourcePaths = uniquePaths([
|
|
18957
|
+
target.sourcePath,
|
|
18958
|
+
await realpath2(target.sourcePath).catch(() => void 0) ?? ""
|
|
18959
|
+
]).filter(Boolean);
|
|
18960
|
+
const entries = await readdir(scriptsDirectory, { withFileTypes: true });
|
|
18961
|
+
assertSafeUvScriptEntries(entries);
|
|
18962
|
+
const environmentName = basename6(target.targetPath);
|
|
18963
|
+
const acceptedPrefixes = /* @__PURE__ */ new Set([
|
|
18964
|
+
resolve8(target.targetPath),
|
|
18965
|
+
await realpath2(target.targetPath)
|
|
18966
|
+
]);
|
|
18967
|
+
let validatedBytes = 0;
|
|
18968
|
+
for (const path9 of [
|
|
18969
|
+
join11(target.targetPath, "pyvenv.cfg"),
|
|
18970
|
+
...entries.filter((entry) => entry.isFile()).map((entry) => join11(scriptsDirectory, entry.name))
|
|
18971
|
+
]) {
|
|
18972
|
+
const validated = await readBoundedUvTextFile(
|
|
18973
|
+
path9,
|
|
18974
|
+
UV_VALIDATION_MAX_TOTAL_BYTES - validatedBytes
|
|
18975
|
+
);
|
|
18976
|
+
if (!validated) continue;
|
|
18977
|
+
validatedBytes += validated.bytes;
|
|
18978
|
+
if (validated.text === void 0) continue;
|
|
18979
|
+
const { text } = validated;
|
|
18980
|
+
if (sourcePaths.some((sourcePath) => text.includes(sourcePath))) {
|
|
18981
|
+
throw new Error(`uv environment contains a stale source path: ${path9}`);
|
|
18982
|
+
}
|
|
18983
|
+
const shebangPrefix = pythonPrefixFromShebang(text);
|
|
18984
|
+
if (shebangPrefix && basename6(shebangPrefix) === environmentName && !acceptedPrefixes.has(resolve8(shebangPrefix))) {
|
|
18985
|
+
throw new Error(`uv launcher points outside its environment: ${path9}`);
|
|
18986
|
+
}
|
|
18987
|
+
if (pythonPrefixesFromText(text).filter((prefix) => basename6(prefix) === environmentName).some((prefix) => !acceptedPrefixes.has(resolve8(prefix)))) {
|
|
18988
|
+
throw new Error(`uv launcher points outside its environment: ${path9}`);
|
|
18989
|
+
}
|
|
18990
|
+
if (virtualEnvironmentPrefixes(text).some(
|
|
18991
|
+
(prefix) => !acceptedPrefixes.has(resolve8(prefix))
|
|
18992
|
+
)) {
|
|
18993
|
+
throw new Error(
|
|
18994
|
+
`uv activation script points outside its environment: ${path9}`
|
|
18995
|
+
);
|
|
19155
18996
|
}
|
|
19156
18997
|
}
|
|
19157
18998
|
}
|
|
19158
|
-
async function
|
|
19159
|
-
|
|
19160
|
-
|
|
18999
|
+
async function validateUvInstallation(target, context) {
|
|
19000
|
+
await validateUvStructure(target, context);
|
|
19001
|
+
await validateUvRelocation(target, context);
|
|
19161
19002
|
}
|
|
19162
|
-
|
|
19163
|
-
|
|
19164
|
-
|
|
19165
|
-
|
|
19166
|
-
|
|
19167
|
-
|
|
19168
|
-
|
|
19169
|
-
|
|
19170
|
-
code = "GJI_CLONE_UNSUPPORTED";
|
|
19171
|
-
constructor(reason) {
|
|
19172
|
-
super(`copy-on-write cloning is not supported: ${reason}`);
|
|
19173
|
-
this.name = "CloneUnsupportedError";
|
|
19003
|
+
async function validateUvStructure(target, context) {
|
|
19004
|
+
if (context.input === "preserve") return;
|
|
19005
|
+
if (!target.targetPath)
|
|
19006
|
+
throw new Error("uv environment target is not project-local");
|
|
19007
|
+
const targetStats = await lstat3(target.targetPath).catch(() => void 0);
|
|
19008
|
+
if (!targetStats) return;
|
|
19009
|
+
if (!targetStats.isDirectory() || targetStats.isSymbolicLink()) {
|
|
19010
|
+
throw new Error("uv environment must be a real directory");
|
|
19174
19011
|
}
|
|
19175
|
-
|
|
19176
|
-
|
|
19177
|
-
|
|
19178
|
-
|
|
19179
|
-
|
|
19180
|
-
this.name = "CloneInProgressError";
|
|
19012
|
+
const configStats = await lstat3(join11(target.targetPath, "pyvenv.cfg")).catch(
|
|
19013
|
+
() => void 0
|
|
19014
|
+
);
|
|
19015
|
+
if (configStats && (!configStats.isFile() || configStats.isSymbolicLink())) {
|
|
19016
|
+
throw new Error("uv pyvenv.cfg must be a regular file");
|
|
19181
19017
|
}
|
|
19182
|
-
|
|
19183
|
-
|
|
19184
|
-
|
|
19185
|
-
|
|
19186
|
-
|
|
19187
|
-
|
|
19188
|
-
|
|
19189
|
-
|
|
19190
|
-
const stats = await lstat3(path9);
|
|
19191
|
-
if (!stats.isDirectory()) return stats.size;
|
|
19192
|
-
const pending = [path9];
|
|
19193
|
-
let total = 0;
|
|
19194
|
-
let entryCount = 0;
|
|
19195
|
-
const startedAt = Date.now();
|
|
19196
|
-
while (pending.length > 0) {
|
|
19197
|
-
const current = pending.pop();
|
|
19198
|
-
if (!current) continue;
|
|
19199
|
-
const directory = await opendir(current);
|
|
19200
|
-
try {
|
|
19201
|
-
for await (const entry of directory) {
|
|
19202
|
-
entryCount += 1;
|
|
19203
|
-
if (entryCount > SIZE_ESTIMATE_MAX_ENTRIES || Date.now() - startedAt > SIZE_ESTIMATE_MAX_MS) {
|
|
19204
|
-
throw new Error("directory size estimate exceeded its safety limit");
|
|
19205
|
-
}
|
|
19206
|
-
const entryPath = join11(current, entry.name);
|
|
19207
|
-
if (entry.isDirectory()) pending.push(entryPath);
|
|
19208
|
-
else total += (await lstat3(entryPath)).size;
|
|
19209
|
-
}
|
|
19210
|
-
} finally {
|
|
19211
|
-
await directory.close().catch(() => void 0);
|
|
19212
|
-
}
|
|
19018
|
+
const scriptsDirectory = join11(
|
|
19019
|
+
target.targetPath,
|
|
19020
|
+
process.platform === "win32" ? "Scripts" : "bin"
|
|
19021
|
+
);
|
|
19022
|
+
const scriptsStats = await lstat3(scriptsDirectory).catch(() => void 0);
|
|
19023
|
+
if (!scriptsStats) return;
|
|
19024
|
+
if (!scriptsStats.isDirectory() || scriptsStats.isSymbolicLink()) {
|
|
19025
|
+
throw new Error("uv scripts path must be a real directory");
|
|
19213
19026
|
}
|
|
19214
|
-
|
|
19027
|
+
assertSafeUvScriptEntries(
|
|
19028
|
+
await readdir(scriptsDirectory, { withFileTypes: true })
|
|
19029
|
+
);
|
|
19215
19030
|
}
|
|
19216
|
-
function
|
|
19217
|
-
if (
|
|
19218
|
-
|
|
19219
|
-
"-a",
|
|
19220
|
-
"--reflink=always",
|
|
19221
|
-
source,
|
|
19222
|
-
destination
|
|
19223
|
-
];
|
|
19031
|
+
function assertSafeUvScriptEntries(entries) {
|
|
19032
|
+
if (entries.length > UV_VALIDATION_MAX_ENTRIES) {
|
|
19033
|
+
throw new Error("uv scripts directory exceeds the validation entry limit");
|
|
19224
19034
|
}
|
|
19225
|
-
|
|
19226
|
-
|
|
19227
|
-
|
|
19228
|
-
|
|
19229
|
-
}
|
|
19230
|
-
async function estimateCloneBytes(path9) {
|
|
19231
|
-
try {
|
|
19232
|
-
return await directorySize(path9);
|
|
19233
|
-
} catch {
|
|
19234
|
-
return void 0;
|
|
19035
|
+
for (const entry of entries) {
|
|
19036
|
+
if (entry.isSymbolicLink() && !isUvInterpreterName(entry.name)) {
|
|
19037
|
+
throw new Error(`uv script must not be a symbolic link: ${entry.name}`);
|
|
19038
|
+
}
|
|
19235
19039
|
}
|
|
19236
19040
|
}
|
|
19237
|
-
async function
|
|
19041
|
+
async function readBoundedUvTextFile(path9, remainingBytes) {
|
|
19042
|
+
let handle;
|
|
19238
19043
|
try {
|
|
19239
|
-
await
|
|
19044
|
+
handle = await open3(
|
|
19045
|
+
path9,
|
|
19046
|
+
constants5.O_RDONLY | (process.platform === "win32" ? 0 : constants5.O_NOFOLLOW)
|
|
19047
|
+
);
|
|
19240
19048
|
} catch (error) {
|
|
19241
|
-
if (
|
|
19242
|
-
throw new CloneUnsupportedError(toErrorMessage(error));
|
|
19243
|
-
}
|
|
19049
|
+
if (isNotFoundError(error)) return void 0;
|
|
19244
19050
|
throw error;
|
|
19245
19051
|
}
|
|
19246
|
-
}
|
|
19247
|
-
async function acquireCloneLock(lockPath, destination) {
|
|
19248
|
-
const releaseGuard = await acquireCloneLockGuard(lockPath, destination);
|
|
19249
19052
|
try {
|
|
19250
|
-
|
|
19251
|
-
|
|
19252
|
-
|
|
19253
|
-
|
|
19254
|
-
}
|
|
19255
|
-
async function acquireCloneLockWithGuard(lockPath, destination) {
|
|
19256
|
-
const lockToken = randomUUID2();
|
|
19257
|
-
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
19258
|
-
const published = await publishCloneLock(lockPath, lockToken);
|
|
19259
|
-
if (published) return published;
|
|
19260
|
-
let staleLock;
|
|
19261
|
-
try {
|
|
19262
|
-
staleLock = await inspectCloneLock(lockPath);
|
|
19263
|
-
} catch (error) {
|
|
19264
|
-
if (isNotFoundError(error)) continue;
|
|
19265
|
-
throw error;
|
|
19053
|
+
const stats = await handle.stat();
|
|
19054
|
+
if (!stats.isFile()) return void 0;
|
|
19055
|
+
if (stats.size > UV_VALIDATION_MAX_FILE_BYTES) {
|
|
19056
|
+
throw new Error(`uv launcher exceeds the validation size limit: ${path9}`);
|
|
19266
19057
|
}
|
|
19267
|
-
if (
|
|
19268
|
-
throw new
|
|
19058
|
+
if (stats.size > remainingBytes) {
|
|
19059
|
+
throw new Error("uv launchers exceed the total validation size limit");
|
|
19269
19060
|
}
|
|
19270
|
-
const
|
|
19271
|
-
|
|
19272
|
-
|
|
19273
|
-
|
|
19274
|
-
if (isNotFoundError(error)) continue;
|
|
19275
|
-
throw error;
|
|
19061
|
+
const readLimit = Math.min(UV_VALIDATION_MAX_FILE_BYTES, remainingBytes);
|
|
19062
|
+
const contents = await readFilePrefix(handle, readLimit + 1);
|
|
19063
|
+
if (contents.byteLength > UV_VALIDATION_MAX_FILE_BYTES) {
|
|
19064
|
+
throw new Error(`uv launcher exceeds the validation size limit: ${path9}`);
|
|
19276
19065
|
}
|
|
19277
|
-
|
|
19278
|
-
|
|
19279
|
-
if (!replacement) {
|
|
19280
|
-
await cleanupStaleCloneLock(stalePath, staleLock);
|
|
19281
|
-
continue;
|
|
19282
|
-
}
|
|
19283
|
-
try {
|
|
19284
|
-
await cleanupStaleCloneLock(stalePath, staleLock);
|
|
19285
|
-
} catch (cleanupError) {
|
|
19286
|
-
await releaseCloneLockWithGuard(replacement).catch(() => void 0);
|
|
19287
|
-
throw cleanupError;
|
|
19288
|
-
}
|
|
19289
|
-
return replacement;
|
|
19290
|
-
} catch (error) {
|
|
19291
|
-
await cleanupStaleCloneLock(stalePath, staleLock).catch(() => void 0);
|
|
19292
|
-
if (isAlreadyExistsError(error)) continue;
|
|
19293
|
-
throw error;
|
|
19066
|
+
if (contents.byteLength > remainingBytes) {
|
|
19067
|
+
throw new Error("uv launchers exceed the total validation size limit");
|
|
19294
19068
|
}
|
|
19295
|
-
|
|
19296
|
-
|
|
19297
|
-
}
|
|
19298
|
-
async function acquireCloneLockGuard(lockPath, destination) {
|
|
19299
|
-
const guardPath = `${lockPath}.guard`;
|
|
19300
|
-
const deadline = Date.now() + 5e3;
|
|
19301
|
-
while (true) {
|
|
19302
|
-
try {
|
|
19303
|
-
await mkdir7(guardPath, { mode: 448 });
|
|
19304
|
-
const markerName = `${process.pid}-${randomUUID2()}`;
|
|
19305
|
-
const markerPath = join11(guardPath, markerName);
|
|
19306
|
-
try {
|
|
19307
|
-
await writeFile8(markerPath, `${markerName}
|
|
19308
|
-
`, {
|
|
19309
|
-
flag: "wx",
|
|
19310
|
-
mode: 384
|
|
19311
|
-
});
|
|
19312
|
-
} catch (error) {
|
|
19313
|
-
await rmdir(guardPath).catch(() => void 0);
|
|
19314
|
-
throw error;
|
|
19315
|
-
}
|
|
19316
|
-
return async () => {
|
|
19317
|
-
await unlink3(markerPath).catch((error) => {
|
|
19318
|
-
if (!isNotFoundError(error)) throw error;
|
|
19319
|
-
});
|
|
19320
|
-
await rmdir(guardPath).catch((error) => {
|
|
19321
|
-
if (!isNotFoundError(error)) throw error;
|
|
19322
|
-
});
|
|
19323
|
-
};
|
|
19324
|
-
} catch (error) {
|
|
19325
|
-
if (!isAlreadyExistsError(error)) throw error;
|
|
19326
|
-
if (await reclaimAbandonedCloneGuard(guardPath)) continue;
|
|
19327
|
-
if (Date.now() >= deadline) {
|
|
19328
|
-
throw new CloneInProgressError(destination);
|
|
19329
|
-
}
|
|
19330
|
-
await new Promise((resolve13) => setTimeout(resolve13, 10));
|
|
19331
|
-
}
|
|
19332
|
-
}
|
|
19333
|
-
}
|
|
19334
|
-
async function reclaimAbandonedCloneGuard(guardPath) {
|
|
19335
|
-
let expectedMarker;
|
|
19336
|
-
try {
|
|
19337
|
-
const entries = await readdir(guardPath);
|
|
19338
|
-
if (entries.length > 1) return false;
|
|
19339
|
-
expectedMarker = entries[0];
|
|
19340
|
-
const freshnessPath = expectedMarker ? join11(guardPath, expectedMarker) : guardPath;
|
|
19341
|
-
const stats = await lstat3(freshnessPath);
|
|
19342
|
-
if (Date.now() - stats.mtimeMs < CLONE_GUARD_TTL_MS) return false;
|
|
19343
|
-
if (expectedMarker) {
|
|
19344
|
-
const ownerPid = Number(expectedMarker.split("-", 1)[0]);
|
|
19345
|
-
if (!Number.isSafeInteger(ownerPid) || ownerPid <= 0) return false;
|
|
19346
|
-
if (processIsAlive(ownerPid)) return false;
|
|
19347
|
-
}
|
|
19348
|
-
} catch (error) {
|
|
19349
|
-
return isNotFoundError(error);
|
|
19350
|
-
}
|
|
19351
|
-
const stalePath = `${guardPath}.stale-${randomUUID2()}`;
|
|
19352
|
-
try {
|
|
19353
|
-
await rename3(guardPath, stalePath);
|
|
19354
|
-
} catch (error) {
|
|
19355
|
-
if (isNotFoundError(error)) return true;
|
|
19356
|
-
throw error;
|
|
19357
|
-
}
|
|
19358
|
-
const movedEntries = await readdir(stalePath).catch(() => []);
|
|
19359
|
-
if (movedEntries.length !== (expectedMarker ? 1 : 0) || expectedMarker && movedEntries[0] !== expectedMarker) {
|
|
19360
|
-
await rename3(stalePath, guardPath).catch(() => void 0);
|
|
19361
|
-
return false;
|
|
19362
|
-
}
|
|
19363
|
-
if (expectedMarker) await unlink3(join11(stalePath, expectedMarker));
|
|
19364
|
-
await rmdir(stalePath);
|
|
19365
|
-
return true;
|
|
19366
|
-
}
|
|
19367
|
-
function processIsAlive(pid) {
|
|
19368
|
-
try {
|
|
19369
|
-
process.kill(pid, 0);
|
|
19370
|
-
return true;
|
|
19371
|
-
} catch (error) {
|
|
19372
|
-
return error.code === "EPERM";
|
|
19373
|
-
}
|
|
19374
|
-
}
|
|
19375
|
-
async function publishCloneLock(lockPath, lockToken) {
|
|
19376
|
-
try {
|
|
19377
|
-
await mkdir7(lockPath, { mode: 448 });
|
|
19378
|
-
} catch (error) {
|
|
19379
|
-
if (isAlreadyExistsError(error)) return void 0;
|
|
19380
|
-
throw error;
|
|
19381
|
-
}
|
|
19382
|
-
const markerPath = join11(lockPath, lockToken);
|
|
19383
|
-
try {
|
|
19384
|
-
await writeFile8(markerPath, `${lockToken}
|
|
19385
|
-
`, { flag: "wx", mode: 384 });
|
|
19386
|
-
return {
|
|
19387
|
-
lockPath,
|
|
19388
|
-
markerName: lockToken,
|
|
19389
|
-
markerPath,
|
|
19390
|
-
token: lockToken
|
|
19391
|
-
};
|
|
19392
|
-
} catch (error) {
|
|
19393
|
-
await rmdir(lockPath).catch(() => void 0);
|
|
19394
|
-
throw error;
|
|
19395
|
-
}
|
|
19396
|
-
}
|
|
19397
|
-
async function inspectCloneLock(lockPath) {
|
|
19398
|
-
try {
|
|
19399
|
-
const stats = await lstat3(lockPath);
|
|
19400
|
-
if (!stats.isDirectory() || stats.isSymbolicLink()) {
|
|
19401
|
-
return { kind: "invalid", mtimeMs: stats.mtimeMs };
|
|
19402
|
-
}
|
|
19403
|
-
const entries = await readdir(lockPath);
|
|
19404
|
-
if (entries.length === 0) return { kind: "empty", mtimeMs: stats.mtimeMs };
|
|
19405
|
-
if (entries.length !== 1) {
|
|
19406
|
-
return { kind: "invalid", mtimeMs: stats.mtimeMs };
|
|
19407
|
-
}
|
|
19408
|
-
const markerName = entries[0];
|
|
19409
|
-
const token = markerName?.startsWith("owner-") ? markerName.slice("owner-".length) : markerName;
|
|
19410
|
-
if (!token || !/^[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}$/iu.test(token)) {
|
|
19411
|
-
return { kind: "invalid", mtimeMs: stats.mtimeMs };
|
|
19412
|
-
}
|
|
19413
|
-
const markerPath = join11(lockPath, markerName);
|
|
19414
|
-
const markerStats = await lstat3(markerPath);
|
|
19415
|
-
if (!markerStats.isFile() || markerStats.isSymbolicLink()) {
|
|
19416
|
-
return { kind: "invalid", mtimeMs: markerStats.mtimeMs };
|
|
19417
|
-
}
|
|
19418
|
-
if ((await readFile8(markerPath, "utf8")).trim() !== token) {
|
|
19419
|
-
return { kind: "invalid", mtimeMs: markerStats.mtimeMs };
|
|
19420
|
-
}
|
|
19421
|
-
return {
|
|
19422
|
-
kind: "owned",
|
|
19423
|
-
lockPath,
|
|
19424
|
-
markerName,
|
|
19425
|
-
markerPath,
|
|
19426
|
-
mtimeMs: markerStats.mtimeMs,
|
|
19427
|
-
token
|
|
19428
|
-
};
|
|
19429
|
-
} catch (error) {
|
|
19430
|
-
if (isNotFoundError(error)) throw error;
|
|
19431
|
-
return { kind: "invalid", mtimeMs: Date.now() };
|
|
19432
|
-
}
|
|
19433
|
-
}
|
|
19434
|
-
async function cleanupStaleCloneLock(lockPath, inspection) {
|
|
19435
|
-
if (inspection.kind === "owned") {
|
|
19436
|
-
await unlink3(join11(lockPath, inspection.markerName));
|
|
19437
|
-
}
|
|
19438
|
-
await rmdir(lockPath);
|
|
19439
|
-
}
|
|
19440
|
-
async function publishCloneContents(temporaryDestination, destination, reservationPath, onEntryPublished, copyFileEntry) {
|
|
19441
|
-
const reservationName = basename6(reservationPath);
|
|
19442
|
-
const destinationEntries = await readdir(destination);
|
|
19443
|
-
if (destinationEntries.length !== 1 || destinationEntries[0] !== reservationName) {
|
|
19444
|
-
throw new CloneDestinationExistsError(destination);
|
|
19445
|
-
}
|
|
19446
|
-
const temporaryEntries = await readdir(temporaryDestination);
|
|
19447
|
-
for (const entry of temporaryEntries) {
|
|
19448
|
-
await publishCloneEntry(
|
|
19449
|
-
join11(temporaryDestination, entry),
|
|
19450
|
-
join11(destination, entry),
|
|
19451
|
-
(entry2) => onEntryPublished(entry2),
|
|
19452
|
-
copyFileEntry
|
|
19453
|
-
);
|
|
19454
|
-
}
|
|
19455
|
-
await unlink3(reservationPath);
|
|
19456
|
-
}
|
|
19457
|
-
async function publishCloneEntry(source, destination, onCreated, copyFileEntry) {
|
|
19458
|
-
const sourceStats = await lstat3(source);
|
|
19459
|
-
if (sourceStats.isDirectory()) {
|
|
19460
|
-
try {
|
|
19461
|
-
await mkdir7(destination);
|
|
19462
|
-
} catch (error) {
|
|
19463
|
-
if (isAlreadyExistsError(error)) {
|
|
19464
|
-
throw new CloneDestinationExistsError(destination);
|
|
19465
|
-
}
|
|
19466
|
-
throw error;
|
|
19467
|
-
}
|
|
19468
|
-
onCreated(await inspectOwnedCloneEntry(destination, "directory"));
|
|
19469
|
-
for (const entry of await readdir(source)) {
|
|
19470
|
-
await publishCloneEntry(
|
|
19471
|
-
join11(source, entry),
|
|
19472
|
-
join11(destination, entry),
|
|
19473
|
-
() => void 0,
|
|
19474
|
-
copyFileEntry
|
|
19475
|
-
);
|
|
19476
|
-
}
|
|
19477
|
-
await copyCloneMetadata(sourceStats, destination);
|
|
19478
|
-
return;
|
|
19479
|
-
}
|
|
19480
|
-
if (sourceStats.isSymbolicLink()) {
|
|
19481
|
-
try {
|
|
19482
|
-
await symlink(await readlink(source), destination);
|
|
19483
|
-
} catch (error) {
|
|
19484
|
-
if (isAlreadyExistsError(error)) {
|
|
19485
|
-
throw new CloneDestinationExistsError(destination);
|
|
19486
|
-
}
|
|
19487
|
-
throw error;
|
|
19488
|
-
}
|
|
19489
|
-
onCreated(await inspectOwnedCloneEntry(destination, "symlink"));
|
|
19490
|
-
return;
|
|
19491
|
-
}
|
|
19492
|
-
if (!sourceStats.isFile()) {
|
|
19493
|
-
throw new Error(`unsupported clone entry type: ${source}`);
|
|
19494
|
-
}
|
|
19495
|
-
try {
|
|
19496
|
-
await copyFileEntry(source, destination);
|
|
19497
|
-
} catch (error) {
|
|
19498
|
-
if (isAlreadyExistsError(error)) {
|
|
19499
|
-
throw new CloneDestinationExistsError(destination);
|
|
19500
|
-
}
|
|
19501
|
-
if (isUnsupportedCloneError(error)) {
|
|
19502
|
-
throw new CloneUnsupportedError(toErrorMessage(error));
|
|
19503
|
-
}
|
|
19504
|
-
throw error;
|
|
19505
|
-
}
|
|
19506
|
-
onCreated(await inspectOwnedCloneEntry(destination, "file"));
|
|
19507
|
-
await copyCloneMetadata(sourceStats, destination);
|
|
19508
|
-
}
|
|
19509
|
-
async function runForcedCloneFileCopy(source, destination) {
|
|
19510
|
-
await cp(source, destination, {
|
|
19511
|
-
errorOnExist: true,
|
|
19512
|
-
force: false,
|
|
19513
|
-
mode: constants5.COPYFILE_FICLONE_FORCE,
|
|
19514
|
-
preserveTimestamps: true
|
|
19515
|
-
});
|
|
19516
|
-
}
|
|
19517
|
-
async function copyCloneMetadata(sourceStats, destination) {
|
|
19518
|
-
await chmod(destination, Number(sourceStats.mode) & 4095);
|
|
19519
|
-
await utimes(destination, sourceStats.atime, sourceStats.mtime);
|
|
19520
|
-
}
|
|
19521
|
-
async function reserveDestination(destination) {
|
|
19522
|
-
try {
|
|
19523
|
-
await mkdir7(destination);
|
|
19524
|
-
} catch (error) {
|
|
19525
|
-
if (isAlreadyExistsError(error)) {
|
|
19526
|
-
throw new CloneDestinationExistsError(destination);
|
|
19527
|
-
}
|
|
19528
|
-
throw error;
|
|
19529
|
-
}
|
|
19530
|
-
const reservationPath = join11(
|
|
19531
|
-
destination,
|
|
19532
|
-
`.gji-clone-reservation-${randomUUID2()}`
|
|
19533
|
-
);
|
|
19534
|
-
try {
|
|
19535
|
-
await writeFile8(reservationPath, "gji clone reservation\n", {
|
|
19536
|
-
flag: "wx"
|
|
19537
|
-
});
|
|
19538
|
-
return await inspectOwnedCloneEntry(reservationPath, "file");
|
|
19539
|
-
} catch (error) {
|
|
19540
|
-
await rmdir(destination).catch(() => void 0);
|
|
19541
|
-
throw error;
|
|
19542
|
-
}
|
|
19543
|
-
}
|
|
19544
|
-
async function cleanupReservedDestination(destination, reservation, publishedEntries) {
|
|
19545
|
-
for (const entry of [...publishedEntries, reservation].reverse()) {
|
|
19546
|
-
await removeOwnedCloneEntry(entry);
|
|
19547
|
-
}
|
|
19548
|
-
await rmdir(destination).catch(() => void 0);
|
|
19549
|
-
}
|
|
19550
|
-
async function inspectOwnedCloneEntry(path9, kind) {
|
|
19551
|
-
const stats = await lstat3(path9, { bigint: true });
|
|
19552
|
-
return { dev: stats.dev, ino: stats.ino, kind, path: path9 };
|
|
19553
|
-
}
|
|
19554
|
-
async function removeOwnedCloneEntry(entry) {
|
|
19555
|
-
try {
|
|
19556
|
-
const current = await lstat3(entry.path, { bigint: true });
|
|
19557
|
-
if (current.dev !== entry.dev || current.ino !== entry.ino) return;
|
|
19558
|
-
if (entry.kind === "directory") await rmdir(entry.path);
|
|
19559
|
-
else await unlink3(entry.path);
|
|
19560
|
-
} catch {
|
|
19561
|
-
}
|
|
19562
|
-
}
|
|
19563
|
-
async function destinationExists(path9) {
|
|
19564
|
-
try {
|
|
19565
|
-
await lstat3(path9);
|
|
19566
|
-
return true;
|
|
19567
|
-
} catch (error) {
|
|
19568
|
-
if (isNotFoundError(error)) return false;
|
|
19569
|
-
throw error;
|
|
19570
|
-
}
|
|
19571
|
-
}
|
|
19572
|
-
function startLockHeartbeat(lock) {
|
|
19573
|
-
const timer = setInterval(() => {
|
|
19574
|
-
void refreshCloneLock(lock);
|
|
19575
|
-
}, CLONE_LOCK_TTL_MS / 3);
|
|
19576
|
-
timer.unref?.();
|
|
19577
|
-
return () => clearInterval(timer);
|
|
19578
|
-
}
|
|
19579
|
-
async function refreshCloneLock(lock) {
|
|
19580
|
-
try {
|
|
19581
|
-
const now = /* @__PURE__ */ new Date();
|
|
19582
|
-
await utimes(lock.markerPath, now, now);
|
|
19583
|
-
} catch {
|
|
19584
|
-
}
|
|
19585
|
-
}
|
|
19586
|
-
async function releaseCloneLock(lock) {
|
|
19587
|
-
const releaseGuard = await acquireCloneLockGuard(
|
|
19588
|
-
lock.lockPath,
|
|
19589
|
-
lock.lockPath
|
|
19590
|
-
);
|
|
19591
|
-
try {
|
|
19592
|
-
await releaseCloneLockWithGuard(lock);
|
|
19069
|
+
const text = contents.toString("utf8");
|
|
19070
|
+
return Buffer.from(text, "utf8").equals(contents) ? { bytes: contents.byteLength, text } : { bytes: contents.byteLength };
|
|
19593
19071
|
} finally {
|
|
19594
|
-
await
|
|
19072
|
+
await handle.close();
|
|
19595
19073
|
}
|
|
19596
19074
|
}
|
|
19597
|
-
async function
|
|
19598
|
-
|
|
19599
|
-
|
|
19600
|
-
|
|
19601
|
-
|
|
19602
|
-
|
|
19603
|
-
|
|
19604
|
-
|
|
19605
|
-
|
|
19606
|
-
|
|
19607
|
-
if (
|
|
19608
|
-
|
|
19609
|
-
}
|
|
19075
|
+
async function readFilePrefix(handle, maxBytes) {
|
|
19076
|
+
const contents = Buffer.allocUnsafe(maxBytes);
|
|
19077
|
+
let offset = 0;
|
|
19078
|
+
while (offset < contents.byteLength) {
|
|
19079
|
+
const { bytesRead } = await handle.read(
|
|
19080
|
+
contents,
|
|
19081
|
+
offset,
|
|
19082
|
+
contents.byteLength - offset,
|
|
19083
|
+
offset
|
|
19084
|
+
);
|
|
19085
|
+
if (bytesRead === 0) break;
|
|
19086
|
+
offset += bytesRead;
|
|
19610
19087
|
}
|
|
19088
|
+
return contents.subarray(0, offset);
|
|
19611
19089
|
}
|
|
19612
|
-
function
|
|
19613
|
-
|
|
19614
|
-
const code = "code" in error ? error.code : "";
|
|
19615
|
-
if (["EINVAL", "ENOSYS", "ENOTSUP", "EOPNOTSUPP", "EXDEV"].includes(code ?? "")) {
|
|
19616
|
-
return true;
|
|
19617
|
-
}
|
|
19618
|
-
return /reflink|unsupported|operation not supported|not supported|invalid cross-device|cross-device/iu.test(
|
|
19619
|
-
error.message
|
|
19620
|
-
);
|
|
19090
|
+
function isUvInterpreterName(name) {
|
|
19091
|
+
return /^python(?:\d+(?:\.\d+)?)?(?:\.exe)?$/iu.test(name);
|
|
19621
19092
|
}
|
|
19622
|
-
function
|
|
19623
|
-
return
|
|
19093
|
+
function uniquePaths(paths) {
|
|
19094
|
+
return [...new Set(paths.filter((path9) => Boolean(path9)))];
|
|
19624
19095
|
}
|
|
19625
19096
|
|
|
19626
19097
|
// src/dependency-bootstrap.ts
|
|
19627
|
-
|
|
19628
|
-
|
|
19629
|
-
|
|
19630
|
-
|
|
19098
|
+
function resolveDependencyBootstrapMode(dependencyBootstrap, noInstall = false) {
|
|
19099
|
+
if (noInstall || dependencyBootstrap === "off") return "off";
|
|
19100
|
+
return "install";
|
|
19101
|
+
}
|
|
19631
19102
|
async function prepareDependencyBootstrap(mode, context) {
|
|
19632
19103
|
if (mode === "off") return { mode, targets: [] };
|
|
19633
|
-
const adapters = createBootstrapAdapters(
|
|
19634
|
-
context.checkUvRuntime ?? defaultCheckUvRuntime,
|
|
19635
|
-
context.cargoBuildCommand
|
|
19636
|
-
);
|
|
19104
|
+
const adapters = createBootstrapAdapters(context.cargoBuildCommand);
|
|
19637
19105
|
const sourceRoots = [];
|
|
19638
|
-
for (const sourceRoot of
|
|
19106
|
+
for (const sourceRoot of uniquePaths2([
|
|
19639
19107
|
context.repoRoot,
|
|
19640
19108
|
context.currentRoot
|
|
19641
19109
|
])) {
|
|
@@ -19647,8 +19115,6 @@ async function prepareDependencyBootstrap(mode, context) {
|
|
|
19647
19115
|
const plannedRelativePaths = /* @__PURE__ */ new Set();
|
|
19648
19116
|
for (const adapter of adapters) {
|
|
19649
19117
|
if (plannedRelativePaths.has(adapter.relativePath)) continue;
|
|
19650
|
-
let firstDetected;
|
|
19651
|
-
let selected;
|
|
19652
19118
|
for (const sourceRoot of sourceRoots) {
|
|
19653
19119
|
const target = await adapter.detect({
|
|
19654
19120
|
detectionRoot: context.detectionRoot,
|
|
@@ -19656,82 +19122,37 @@ async function prepareDependencyBootstrap(mode, context) {
|
|
|
19656
19122
|
worktreePath: context.worktreePath
|
|
19657
19123
|
});
|
|
19658
19124
|
if (!target) continue;
|
|
19659
|
-
|
|
19660
|
-
const candidate = { target, seedable };
|
|
19661
|
-
firstDetected ??= candidate;
|
|
19662
|
-
if (mode === "install-only" || seedable) {
|
|
19663
|
-
selected = candidate;
|
|
19664
|
-
break;
|
|
19665
|
-
}
|
|
19666
|
-
}
|
|
19667
|
-
const plannedTarget = selected ?? firstDetected;
|
|
19668
|
-
if (plannedTarget) {
|
|
19669
|
-
targets.push(plannedTarget);
|
|
19125
|
+
targets.push({ adapter, target });
|
|
19670
19126
|
plannedRelativePaths.add(adapter.relativePath);
|
|
19127
|
+
break;
|
|
19671
19128
|
}
|
|
19672
19129
|
}
|
|
19673
19130
|
return { mode, targets };
|
|
19674
19131
|
}
|
|
19675
|
-
async function detectDependencyBootstrapCandidate(context) {
|
|
19676
|
-
const plan = await prepareDependencyBootstrap("cow-then-repair", context);
|
|
19677
|
-
const planned = plan.targets[0];
|
|
19678
|
-
if (!planned) return null;
|
|
19679
|
-
return {
|
|
19680
|
-
adapter: planned.target.adapter,
|
|
19681
|
-
kind: planned.target.kind,
|
|
19682
|
-
lockfile: adapterForTarget(planned.target).lockfile,
|
|
19683
|
-
target: planned.target.relativePath,
|
|
19684
|
-
repairCommand: planned.target.repairCommand,
|
|
19685
|
-
seedable: planned.seedable
|
|
19686
|
-
};
|
|
19687
|
-
}
|
|
19688
19132
|
function previewDependencyBootstrap(plan) {
|
|
19689
19133
|
return {
|
|
19690
19134
|
mode: plan.mode,
|
|
19691
|
-
targets: plan.targets.map(({
|
|
19692
|
-
adapter:
|
|
19693
|
-
kind:
|
|
19135
|
+
targets: plan.targets.map(({ adapter, target }) => ({
|
|
19136
|
+
adapter: adapter.name,
|
|
19137
|
+
kind: adapter.kind,
|
|
19694
19138
|
target: target.relativePath,
|
|
19695
|
-
|
|
19696
|
-
seedable,
|
|
19697
|
-
strategy: bootstrapStrategy(plan.mode, target, seedable)
|
|
19139
|
+
command: target.installCommand
|
|
19698
19140
|
}))
|
|
19699
19141
|
};
|
|
19700
19142
|
}
|
|
19701
19143
|
async function executeDependencyBootstrap(plan, options) {
|
|
19702
19144
|
if (plan.mode === "off") return { mode: plan.mode, ready: true, events: [] };
|
|
19703
19145
|
const events = [];
|
|
19704
|
-
const cloneDirectory = options.cloneDirectory ?? cloneDir;
|
|
19705
19146
|
const execution = {
|
|
19706
19147
|
runCommand: options.runCommand ?? runCommand,
|
|
19707
19148
|
stderr: options.stderr ?? (() => void 0),
|
|
19708
19149
|
stdout: options.stdout ?? ((chunk) => process.stdout.write(chunk))
|
|
19709
19150
|
};
|
|
19710
|
-
const seededDirectories = new Set(options.seededDirectories ?? []);
|
|
19711
19151
|
if (plan.targets.length === 0) {
|
|
19712
|
-
recordBootstrapEvent(events, options.reporter, {
|
|
19713
|
-
adapter: "none",
|
|
19714
|
-
kind: "dependency",
|
|
19715
|
-
reason: "no-lockfile",
|
|
19716
|
-
state: "skipped",
|
|
19717
|
-
target: "",
|
|
19718
|
-
message: "no supported dependency or build-state lockfile was detected"
|
|
19719
|
-
});
|
|
19720
19152
|
return { mode: plan.mode, ready: true, events };
|
|
19721
19153
|
}
|
|
19722
|
-
for (const {
|
|
19723
|
-
|
|
19724
|
-
await executeBootstrapTarget(
|
|
19725
|
-
adapter,
|
|
19726
|
-
target,
|
|
19727
|
-
seedable,
|
|
19728
|
-
plan.mode,
|
|
19729
|
-
cloneDirectory,
|
|
19730
|
-
execution,
|
|
19731
|
-
seededDirectories.has(target.relativePath),
|
|
19732
|
-
events,
|
|
19733
|
-
options.reporter
|
|
19734
|
-
);
|
|
19154
|
+
for (const { adapter, target } of plan.targets) {
|
|
19155
|
+
await installTarget(adapter, target, execution, events, options.reporter);
|
|
19735
19156
|
}
|
|
19736
19157
|
return {
|
|
19737
19158
|
mode: plan.mode,
|
|
@@ -19739,263 +19160,43 @@ async function executeDependencyBootstrap(plan, options) {
|
|
|
19739
19160
|
events
|
|
19740
19161
|
};
|
|
19741
19162
|
}
|
|
19742
|
-
async function
|
|
19743
|
-
const
|
|
19744
|
-
|
|
19745
|
-
target.targetPath
|
|
19746
|
-
);
|
|
19747
|
-
if (destinationInspection.kind === "unsafe") {
|
|
19163
|
+
async function installTarget(adapter, target, execution, events, reporter) {
|
|
19164
|
+
const beforeInstallInspection = target.targetPath ? await inspectDestination(target.worktreePath, target.targetPath) : void 0;
|
|
19165
|
+
if (beforeInstallInspection?.kind === "unsafe") {
|
|
19748
19166
|
recordBootstrapFailure(
|
|
19749
19167
|
events,
|
|
19750
19168
|
reporter,
|
|
19751
19169
|
adapter,
|
|
19752
19170
|
target,
|
|
19753
|
-
|
|
19171
|
+
beforeInstallInspection.reason,
|
|
19754
19172
|
"destination-unsafe"
|
|
19755
19173
|
);
|
|
19756
19174
|
return;
|
|
19757
19175
|
}
|
|
19758
|
-
|
|
19759
|
-
|
|
19760
|
-
|
|
19761
|
-
target,
|
|
19762
|
-
execution,
|
|
19763
|
-
destinationInspection.kind === "exists" ? "preserve" : "clean",
|
|
19764
|
-
events,
|
|
19765
|
-
reporter
|
|
19766
|
-
);
|
|
19767
|
-
return;
|
|
19768
|
-
}
|
|
19769
|
-
let input = destinationInspection.kind === "exists" ? "preserve" : "clean";
|
|
19770
|
-
const destinationExistsNow = destinationInspection.kind === "exists";
|
|
19771
|
-
if (target.initialInput === "preserve" && destinationExistsNow) {
|
|
19772
|
-
recordBootstrapEvent(events, reporter, {
|
|
19773
|
-
adapter: adapter.name,
|
|
19774
|
-
kind: adapter.kind,
|
|
19775
|
-
reason: "target-exists",
|
|
19776
|
-
state: "skipped",
|
|
19777
|
-
target: target.relativePath,
|
|
19778
|
-
message: "target already existed; using it as the repair input"
|
|
19779
|
-
});
|
|
19780
|
-
} else if (seededBySyncDirs && destinationExistsNow) {
|
|
19781
|
-
if (seedable) {
|
|
19782
|
-
input = "seed";
|
|
19783
|
-
recordBootstrapEvent(events, reporter, {
|
|
19784
|
-
adapter: adapter.name,
|
|
19785
|
-
kind: adapter.kind,
|
|
19786
|
-
reason: "generic-seed",
|
|
19787
|
-
state: "seeded",
|
|
19788
|
-
target: target.relativePath,
|
|
19789
|
-
message: "reusing a seed created by syncDirs"
|
|
19790
|
-
});
|
|
19791
|
-
} else {
|
|
19792
|
-
input = "preserve";
|
|
19793
|
-
recordBootstrapEvent(events, reporter, {
|
|
19794
|
-
adapter: adapter.name,
|
|
19795
|
-
kind: adapter.kind,
|
|
19796
|
-
reason: "generic-seed",
|
|
19797
|
-
state: "repair-only",
|
|
19798
|
-
target: target.relativePath,
|
|
19799
|
-
message: "syncDirs created a generic target; this adapter uses repair without CoW"
|
|
19800
|
-
});
|
|
19801
|
-
}
|
|
19802
|
-
} else if (destinationExistsNow) {
|
|
19803
|
-
input = "preserve";
|
|
19804
|
-
recordBootstrapEvent(events, reporter, {
|
|
19805
|
-
adapter: adapter.name,
|
|
19806
|
-
kind: adapter.kind,
|
|
19807
|
-
reason: "destination-race",
|
|
19808
|
-
state: "skipped",
|
|
19809
|
-
target: target.relativePath,
|
|
19810
|
-
message: "target appeared during bootstrap; preserving it as the repair input"
|
|
19811
|
-
});
|
|
19812
|
-
} else if (seedable) {
|
|
19813
|
-
try {
|
|
19814
|
-
await cloneDirectory(adapter.seedPath(target), target.targetPath, {
|
|
19815
|
-
destinationRoot: target.worktreePath,
|
|
19816
|
-
measureBytes: reporter.measureCloneSize
|
|
19817
|
-
});
|
|
19818
|
-
input = "seed";
|
|
19819
|
-
recordBootstrapEvent(events, reporter, {
|
|
19820
|
-
adapter: adapter.name,
|
|
19821
|
-
kind: adapter.kind,
|
|
19822
|
-
state: "seeded",
|
|
19823
|
-
target: target.relativePath,
|
|
19824
|
-
message: "seeded with copy-on-write"
|
|
19825
|
-
});
|
|
19826
|
-
} catch (error) {
|
|
19827
|
-
if (isCloneDestinationExistsError(error)) {
|
|
19828
|
-
input = "preserve";
|
|
19829
|
-
recordBootstrapEvent(events, reporter, {
|
|
19830
|
-
adapter: adapter.name,
|
|
19831
|
-
kind: adapter.kind,
|
|
19832
|
-
state: "skipped",
|
|
19833
|
-
target: target.relativePath,
|
|
19834
|
-
message: "target appeared during CoW seeding; preserving it as the repair input"
|
|
19835
|
-
});
|
|
19836
|
-
await repairTarget(adapter, target, execution, input, events, reporter);
|
|
19837
|
-
return;
|
|
19838
|
-
}
|
|
19839
|
-
if (isCloneInProgressError(error)) {
|
|
19840
|
-
recordBootstrapFailure(
|
|
19841
|
-
events,
|
|
19842
|
-
reporter,
|
|
19843
|
-
adapter,
|
|
19844
|
-
target,
|
|
19845
|
-
`CoW seed is already in progress: ${toErrorMessage2(error)}`,
|
|
19846
|
-
"clone-in-progress"
|
|
19847
|
-
);
|
|
19848
|
-
return;
|
|
19849
|
-
}
|
|
19850
|
-
const failedCloneInspection = await inspectDestination(
|
|
19851
|
-
target.worktreePath,
|
|
19852
|
-
target.targetPath
|
|
19853
|
-
);
|
|
19854
|
-
if (failedCloneInspection.kind === "unsafe") {
|
|
19855
|
-
recordBootstrapFailure(
|
|
19856
|
-
events,
|
|
19857
|
-
reporter,
|
|
19858
|
-
adapter,
|
|
19859
|
-
target,
|
|
19860
|
-
failedCloneInspection.reason,
|
|
19861
|
-
"destination-unsafe"
|
|
19862
|
-
);
|
|
19863
|
-
return;
|
|
19864
|
-
}
|
|
19865
|
-
input = failedCloneInspection.kind === "exists" ? "preserve" : "clean";
|
|
19866
|
-
const repairInput = input === "preserve" ? "the preserved target" : "an empty target";
|
|
19867
|
-
recordBootstrapEvent(events, reporter, {
|
|
19868
|
-
adapter: adapter.name,
|
|
19869
|
-
kind: adapter.kind,
|
|
19870
|
-
reason: "cow-seed-failed",
|
|
19871
|
-
state: "repair-only",
|
|
19872
|
-
target: target.relativePath,
|
|
19873
|
-
message: `CoW seed failed; repairing from ${repairInput} (${toErrorMessage2(error)})`
|
|
19874
|
-
});
|
|
19875
|
-
}
|
|
19876
|
-
} else {
|
|
19176
|
+
const effectiveInput = beforeInstallInspection?.kind === "exists" ? "preserve" : "clean";
|
|
19177
|
+
try {
|
|
19178
|
+
await adapter.install(target, { ...execution, input: effectiveInput });
|
|
19877
19179
|
recordBootstrapEvent(events, reporter, {
|
|
19878
19180
|
adapter: adapter.name,
|
|
19879
19181
|
kind: adapter.kind,
|
|
19880
|
-
|
|
19881
|
-
state: "repair-only",
|
|
19182
|
+
state: "installed",
|
|
19882
19183
|
target: target.relativePath,
|
|
19883
|
-
message:
|
|
19184
|
+
message: installSuccessMessage(target, effectiveInput)
|
|
19884
19185
|
});
|
|
19885
|
-
}
|
|
19886
|
-
await repairTarget(adapter, target, execution, input, events, reporter);
|
|
19887
|
-
}
|
|
19888
|
-
async function repairTarget(adapter, target, execution, input, events, reporter) {
|
|
19889
|
-
const beforeRepairInspection = await inspectDestination(
|
|
19890
|
-
target.worktreePath,
|
|
19891
|
-
target.targetPath
|
|
19892
|
-
);
|
|
19893
|
-
if (beforeRepairInspection.kind === "unsafe") {
|
|
19186
|
+
} catch (error) {
|
|
19894
19187
|
recordBootstrapFailure(
|
|
19895
19188
|
events,
|
|
19896
19189
|
reporter,
|
|
19897
19190
|
adapter,
|
|
19898
19191
|
target,
|
|
19899
|
-
|
|
19900
|
-
"
|
|
19901
|
-
);
|
|
19902
|
-
return;
|
|
19903
|
-
}
|
|
19904
|
-
const effectiveInput = beforeRepairInspection.kind === "exists" ? input === "seed" ? "seed" : "preserve" : "clean";
|
|
19905
|
-
try {
|
|
19906
|
-
await adapter.repair(target, { ...execution, input: effectiveInput });
|
|
19907
|
-
recordBootstrapEvent(events, reporter, {
|
|
19908
|
-
adapter: adapter.name,
|
|
19909
|
-
kind: adapter.kind,
|
|
19910
|
-
state: target.repairState,
|
|
19911
|
-
target: target.relativePath,
|
|
19912
|
-
message: repairSuccessMessage(target, effectiveInput)
|
|
19913
|
-
});
|
|
19914
|
-
} catch (firstError) {
|
|
19915
|
-
const firstFailureContext = {
|
|
19916
|
-
input: effectiveInput,
|
|
19917
|
-
target
|
|
19918
|
-
};
|
|
19919
|
-
const cleanupError = await adapter.cleanupAfterRepairFailure(firstFailureContext).then(() => void 0).catch((error) => toErrorMessage2(error));
|
|
19920
|
-
if (!adapter.shouldRetryAfterRepairFailure(firstFailureContext)) {
|
|
19921
|
-
recordBootstrapFailure(
|
|
19922
|
-
events,
|
|
19923
|
-
reporter,
|
|
19924
|
-
adapter,
|
|
19925
|
-
target,
|
|
19926
|
-
formatRepairFailure(
|
|
19927
|
-
firstError,
|
|
19928
|
-
cleanupError === void 0 ? void 0 : cleanupError
|
|
19929
|
-
),
|
|
19930
|
-
"repair-failed"
|
|
19931
|
-
);
|
|
19932
|
-
return;
|
|
19933
|
-
}
|
|
19934
|
-
if (cleanupError) {
|
|
19935
|
-
recordBootstrapFailure(
|
|
19936
|
-
events,
|
|
19937
|
-
reporter,
|
|
19938
|
-
adapter,
|
|
19939
|
-
target,
|
|
19940
|
-
formatRepairFailure(firstError, cleanupError),
|
|
19941
|
-
"repair-cleanup-failed"
|
|
19942
|
-
);
|
|
19943
|
-
return;
|
|
19944
|
-
}
|
|
19945
|
-
recordBootstrapEvent(events, reporter, {
|
|
19946
|
-
adapter: adapter.name,
|
|
19947
|
-
kind: adapter.kind,
|
|
19948
|
-
reason: "seed-repair-failed",
|
|
19949
|
-
state: "repair-only",
|
|
19950
|
-
target: target.relativePath,
|
|
19951
|
-
message: `seed repair failed; removed the seed and retrying clean (${toErrorMessage2(firstError)})`
|
|
19952
|
-
});
|
|
19953
|
-
const beforeRetryInspection = await inspectDestination(
|
|
19954
|
-
target.worktreePath,
|
|
19955
|
-
target.targetPath
|
|
19192
|
+
formatInstallFailure(error),
|
|
19193
|
+
"install-failed"
|
|
19956
19194
|
);
|
|
19957
|
-
if (beforeRetryInspection.kind === "unsafe") {
|
|
19958
|
-
recordBootstrapFailure(
|
|
19959
|
-
events,
|
|
19960
|
-
reporter,
|
|
19961
|
-
adapter,
|
|
19962
|
-
target,
|
|
19963
|
-
beforeRetryInspection.reason,
|
|
19964
|
-
"destination-unsafe"
|
|
19965
|
-
);
|
|
19966
|
-
return;
|
|
19967
|
-
}
|
|
19968
|
-
const retryInput = beforeRetryInspection.kind === "exists" ? "preserve" : "clean";
|
|
19969
|
-
try {
|
|
19970
|
-
await adapter.repair(target, { ...execution, input: retryInput });
|
|
19971
|
-
recordBootstrapEvent(events, reporter, {
|
|
19972
|
-
adapter: adapter.name,
|
|
19973
|
-
kind: adapter.kind,
|
|
19974
|
-
reason: "repair-retry",
|
|
19975
|
-
state: target.repairState,
|
|
19976
|
-
target: target.relativePath,
|
|
19977
|
-
message: repairSuccessMessage(target, retryInput)
|
|
19978
|
-
});
|
|
19979
|
-
} catch (secondError) {
|
|
19980
|
-
const cleanupError2 = await adapter.cleanupAfterRepairFailure({
|
|
19981
|
-
input: retryInput,
|
|
19982
|
-
target
|
|
19983
|
-
}).then(() => void 0).catch((error) => toErrorMessage2(error));
|
|
19984
|
-
recordBootstrapFailure(
|
|
19985
|
-
events,
|
|
19986
|
-
reporter,
|
|
19987
|
-
adapter,
|
|
19988
|
-
target,
|
|
19989
|
-
formatRepairFailure(secondError, cleanupError2),
|
|
19990
|
-
"repair-failed"
|
|
19991
|
-
);
|
|
19992
|
-
}
|
|
19993
19195
|
}
|
|
19994
19196
|
}
|
|
19995
|
-
function
|
|
19996
|
-
if (input === "
|
|
19997
|
-
|
|
19998
|
-
return target.repairState === "installed" ? "installed from a clean target" : "repaired from a clean target";
|
|
19197
|
+
function installSuccessMessage(_target, input) {
|
|
19198
|
+
if (input === "preserve") return "installed into the existing target";
|
|
19199
|
+
return "installed into a clean target";
|
|
19999
19200
|
}
|
|
20000
19201
|
function recordBootstrapFailure(events, reporter, adapter, target, message, reason) {
|
|
20001
19202
|
recordBootstrapEvent(events, reporter, {
|
|
@@ -20011,169 +19212,156 @@ function recordBootstrapEvent(events, reporter, event) {
|
|
|
20011
19212
|
events.push(event);
|
|
20012
19213
|
reporter.dependency(event);
|
|
20013
19214
|
}
|
|
20014
|
-
function
|
|
20015
|
-
|
|
20016
|
-
return cleanupError ? `${message}; cleanup failed: ${cleanupError}` : message;
|
|
20017
|
-
}
|
|
20018
|
-
function bootstrapStrategy(mode, target, seedable) {
|
|
20019
|
-
if (mode === "install-only" || target.repairState === "installed") {
|
|
20020
|
-
return "install-only";
|
|
20021
|
-
}
|
|
20022
|
-
return seedable ? "cow-then-repair" : "repair-only";
|
|
19215
|
+
function formatInstallFailure(error) {
|
|
19216
|
+
return `install failed: ${toErrorMessage(error)}`;
|
|
20023
19217
|
}
|
|
20024
|
-
function
|
|
20025
|
-
const adapter = createBootstrapAdapters(defaultCheckUvRuntime).find(
|
|
20026
|
-
(candidate) => candidate.name === target.adapter
|
|
20027
|
-
);
|
|
20028
|
-
if (!adapter)
|
|
20029
|
-
throw new Error(`unsupported bootstrap adapter: ${target.adapter}`);
|
|
20030
|
-
return adapter;
|
|
20031
|
-
}
|
|
20032
|
-
function createBootstrapAdapters(checkUvRuntime, cargoBuildCommand) {
|
|
19218
|
+
function createBootstrapAdapters(cargoBuildCommand) {
|
|
20033
19219
|
return [
|
|
20034
19220
|
new LockfileBootstrapAdapter({
|
|
20035
19221
|
name: "pnpm",
|
|
20036
19222
|
kind: "dependency",
|
|
20037
|
-
|
|
19223
|
+
lockfiles: ["pnpm-lock.yaml"],
|
|
20038
19224
|
relativePath: "node_modules",
|
|
20039
|
-
|
|
20040
|
-
shell: false,
|
|
20041
|
-
beforeRepair: async (target, context) => {
|
|
20042
|
-
if (context.input === "seed") {
|
|
20043
|
-
await rm4(join12(target.targetPath, ".modules.yaml"), {
|
|
20044
|
-
force: true
|
|
20045
|
-
});
|
|
20046
|
-
}
|
|
20047
|
-
}
|
|
19225
|
+
installCommand: "pnpm install --frozen-lockfile"
|
|
20048
19226
|
}),
|
|
20049
19227
|
new LockfileBootstrapAdapter({
|
|
20050
19228
|
name: "yarn",
|
|
20051
19229
|
kind: "dependency",
|
|
20052
|
-
|
|
19230
|
+
lockfiles: ["yarn.lock"],
|
|
20053
19231
|
relativePath: "node_modules",
|
|
20054
|
-
|
|
20055
|
-
|
|
20056
|
-
|
|
20057
|
-
|
|
20058
|
-
|
|
20059
|
-
|
|
20060
|
-
|
|
20061
|
-
|
|
20062
|
-
|
|
19232
|
+
installCommand: "yarn install --frozen-lockfile",
|
|
19233
|
+
selectInstallCommand: async (_target, _input) => await isYarnBerry(_target.detectionRoot ?? _target.sourceRoot) ? "yarn install --immutable" : "yarn install --frozen-lockfile"
|
|
19234
|
+
}),
|
|
19235
|
+
new LockfileBootstrapAdapter({
|
|
19236
|
+
name: "bun",
|
|
19237
|
+
kind: "dependency",
|
|
19238
|
+
lockfiles: ["bun.lock", "bun.lockb"],
|
|
19239
|
+
relativePath: "node_modules",
|
|
19240
|
+
installCommand: "bun install --frozen-lockfile"
|
|
20063
19241
|
}),
|
|
20064
19242
|
new LockfileBootstrapAdapter({
|
|
20065
19243
|
name: "npm",
|
|
20066
19244
|
kind: "dependency",
|
|
20067
|
-
|
|
19245
|
+
lockfiles: ["package-lock.json"],
|
|
20068
19246
|
relativePath: "node_modules",
|
|
20069
|
-
|
|
20070
|
-
|
|
20071
|
-
seedPolicy: "never",
|
|
20072
|
-
selectRepairCommand: (_target, input) => input === "preserve" ? "npm install" : "npm ci",
|
|
20073
|
-
repairState: "installed"
|
|
19247
|
+
installCommand: "npm ci",
|
|
19248
|
+
selectInstallCommand: (_target, input) => input === "preserve" ? "npm install" : "npm ci"
|
|
20074
19249
|
}),
|
|
20075
19250
|
new LockfileBootstrapAdapter({
|
|
20076
19251
|
name: "bundler",
|
|
20077
19252
|
kind: "dependency",
|
|
20078
|
-
|
|
19253
|
+
lockfiles: ["Gemfile.lock"],
|
|
20079
19254
|
relativePath: "vendor/bundle",
|
|
20080
|
-
|
|
20081
|
-
shell: false,
|
|
19255
|
+
installCommand: "bundle install",
|
|
20082
19256
|
commandOptions: () => ({
|
|
20083
19257
|
env: { BUNDLE_PATH: "vendor/bundle" }
|
|
20084
|
-
})
|
|
20085
|
-
|
|
20086
|
-
|
|
20087
|
-
|
|
20088
|
-
|
|
20089
|
-
|
|
19258
|
+
})
|
|
19259
|
+
}),
|
|
19260
|
+
new LockfileBootstrapAdapter({
|
|
19261
|
+
name: "poetry",
|
|
19262
|
+
kind: "dependency",
|
|
19263
|
+
lockfiles: ["poetry.lock"],
|
|
19264
|
+
relativePath: ".venv",
|
|
19265
|
+
installCommand: "poetry install --no-interaction",
|
|
19266
|
+
commandOptions: () => ({
|
|
19267
|
+
env: { POETRY_VIRTUALENVS_IN_PROJECT: "true" }
|
|
19268
|
+
})
|
|
20090
19269
|
}),
|
|
20091
19270
|
new LockfileBootstrapAdapter({
|
|
20092
19271
|
name: "uv",
|
|
20093
19272
|
kind: "dependency",
|
|
20094
|
-
|
|
19273
|
+
lockfiles: ["uv.lock"],
|
|
20095
19274
|
relativePath: ".venv",
|
|
20096
|
-
|
|
20097
|
-
|
|
20098
|
-
|
|
20099
|
-
|
|
20100
|
-
|
|
19275
|
+
installCommand: "uv sync --locked",
|
|
19276
|
+
beforeInstall: validateUvStructure,
|
|
19277
|
+
afterInstall: validateUvInstallation
|
|
19278
|
+
}),
|
|
19279
|
+
new LockfileBootstrapAdapter({
|
|
19280
|
+
name: "pipenv",
|
|
19281
|
+
kind: "dependency",
|
|
19282
|
+
lockfiles: ["Pipfile.lock"],
|
|
19283
|
+
relativePath: ".venv",
|
|
19284
|
+
installCommand: "pipenv sync",
|
|
19285
|
+
commandOptions: () => ({
|
|
19286
|
+
env: { PIPENV_VENV_IN_PROJECT: "1" }
|
|
19287
|
+
})
|
|
19288
|
+
}),
|
|
19289
|
+
new LockfileBootstrapAdapter({
|
|
19290
|
+
name: "go",
|
|
19291
|
+
kind: "dependency",
|
|
19292
|
+
lockfiles: ["go.mod"],
|
|
19293
|
+
relativePath: "",
|
|
19294
|
+
installCommand: "go mod download"
|
|
19295
|
+
}),
|
|
19296
|
+
new LockfileBootstrapAdapter({
|
|
19297
|
+
name: "composer",
|
|
19298
|
+
kind: "dependency",
|
|
19299
|
+
lockfiles: ["composer.lock"],
|
|
19300
|
+
relativePath: "vendor",
|
|
19301
|
+
installCommand: "composer install --no-interaction --prefer-dist"
|
|
20101
19302
|
}),
|
|
20102
19303
|
new LockfileBootstrapAdapter({
|
|
20103
19304
|
name: "cargo",
|
|
20104
19305
|
kind: "build-cache",
|
|
20105
|
-
|
|
19306
|
+
lockfiles: ["Cargo.lock"],
|
|
20106
19307
|
relativePath: "target",
|
|
20107
|
-
|
|
19308
|
+
installCommand: cargoBuildCommand?.trim() || "cargo check",
|
|
20108
19309
|
shell: Boolean(cargoBuildCommand)
|
|
20109
19310
|
})
|
|
20110
19311
|
];
|
|
20111
19312
|
}
|
|
20112
19313
|
var LockfileBootstrapAdapter = class {
|
|
20113
19314
|
kind;
|
|
20114
|
-
lockfile;
|
|
20115
19315
|
name;
|
|
20116
19316
|
relativePath;
|
|
20117
|
-
|
|
19317
|
+
defaultInstallCommand;
|
|
19318
|
+
lockfiles;
|
|
20118
19319
|
shell;
|
|
20119
|
-
|
|
20120
|
-
|
|
20121
|
-
beforeRepair;
|
|
20122
|
-
afterRepair;
|
|
19320
|
+
beforeInstall;
|
|
19321
|
+
afterInstall;
|
|
20123
19322
|
commandOptions;
|
|
20124
|
-
|
|
20125
|
-
repairState;
|
|
19323
|
+
selectInstallCommand;
|
|
20126
19324
|
constructor(spec) {
|
|
20127
19325
|
this.name = spec.name;
|
|
20128
19326
|
this.kind = spec.kind;
|
|
20129
|
-
this.
|
|
19327
|
+
this.lockfiles = spec.lockfiles;
|
|
20130
19328
|
this.relativePath = spec.relativePath;
|
|
20131
|
-
this.
|
|
20132
|
-
this.shell = spec.shell;
|
|
20133
|
-
this.
|
|
20134
|
-
this.
|
|
20135
|
-
this.beforeRepair = spec.beforeRepair;
|
|
20136
|
-
this.afterRepair = spec.afterRepair;
|
|
19329
|
+
this.defaultInstallCommand = spec.installCommand;
|
|
19330
|
+
this.shell = spec.shell ?? false;
|
|
19331
|
+
this.beforeInstall = spec.beforeInstall;
|
|
19332
|
+
this.afterInstall = spec.afterInstall;
|
|
20137
19333
|
this.commandOptions = spec.commandOptions;
|
|
20138
|
-
this.
|
|
20139
|
-
this.repairState = spec.repairState ?? "repaired";
|
|
19334
|
+
this.selectInstallCommand = spec.selectInstallCommand;
|
|
20140
19335
|
}
|
|
20141
19336
|
async detect(context) {
|
|
20142
19337
|
const detectionRoot = context.detectionRoot ?? context.sourceRoot;
|
|
20143
|
-
|
|
20144
|
-
|
|
20145
|
-
const
|
|
20146
|
-
const
|
|
20147
|
-
|
|
20148
|
-
|
|
20149
|
-
);
|
|
20150
|
-
const initialInput = destinationInspection.kind === "exists" ? "preserve" : "clean";
|
|
19338
|
+
const hasLockfile = await hasExistingPath(detectionRoot, this.lockfiles);
|
|
19339
|
+
if (!hasLockfile) return null;
|
|
19340
|
+
const sourcePath = this.relativePath ? join12(context.sourceRoot, this.relativePath) : void 0;
|
|
19341
|
+
const targetPath = this.relativePath ? join12(context.worktreePath, this.relativePath) : void 0;
|
|
19342
|
+
const destinationInspection = targetPath ? await inspectDestination(context.worktreePath, targetPath) : void 0;
|
|
19343
|
+
const initialInput = destinationInspection?.kind === "exists" ? "preserve" : "clean";
|
|
20151
19344
|
const target = {
|
|
20152
|
-
adapter: this.name,
|
|
20153
19345
|
kind: this.kind,
|
|
20154
19346
|
relativePath: this.relativePath,
|
|
20155
19347
|
sourceRoot: context.sourceRoot,
|
|
19348
|
+
detectionRoot,
|
|
20156
19349
|
worktreePath: context.worktreePath,
|
|
20157
19350
|
sourcePath,
|
|
20158
19351
|
targetPath,
|
|
20159
|
-
|
|
20160
|
-
|
|
20161
|
-
shell: this.shell,
|
|
20162
|
-
initialInput
|
|
19352
|
+
installCommand: this.defaultInstallCommand,
|
|
19353
|
+
shell: this.shell
|
|
20163
19354
|
};
|
|
20164
19355
|
return {
|
|
20165
19356
|
...target,
|
|
20166
|
-
|
|
19357
|
+
installCommand: await this.selectCommand(target, initialInput)
|
|
20167
19358
|
};
|
|
20168
19359
|
}
|
|
20169
|
-
|
|
20170
|
-
|
|
20171
|
-
|
|
20172
|
-
async repair(target, context) {
|
|
20173
|
-
await this.beforeRepair?.(target, context);
|
|
20174
|
-
const repairCommand = this.selectRepairCommand?.(target, context.input) ?? target.repairCommand;
|
|
19360
|
+
async install(target, context) {
|
|
19361
|
+
await this.beforeInstall?.(target, context);
|
|
19362
|
+
const installCommand = await this.selectCommand(target, context.input);
|
|
20175
19363
|
await context.runCommand(
|
|
20176
|
-
|
|
19364
|
+
installCommand,
|
|
20177
19365
|
target.worktreePath,
|
|
20178
19366
|
context.stderr,
|
|
20179
19367
|
context.stdout,
|
|
@@ -20182,277 +19370,32 @@ var LockfileBootstrapAdapter = class {
|
|
|
20182
19370
|
shell: target.shell
|
|
20183
19371
|
}
|
|
20184
19372
|
);
|
|
20185
|
-
await this.
|
|
20186
|
-
}
|
|
20187
|
-
async canSeed(target) {
|
|
20188
|
-
if (this.seedPolicy === "never") return false;
|
|
20189
|
-
if (!await safeSourceDirectory(target)) return false;
|
|
20190
|
-
return await this.canSeedOverride?.(target) ?? true;
|
|
19373
|
+
await this.afterInstall?.(target, context);
|
|
20191
19374
|
}
|
|
20192
|
-
|
|
20193
|
-
return
|
|
20194
|
-
}
|
|
20195
|
-
async cleanupAfterRepairFailure(context) {
|
|
20196
|
-
if (context.input !== "seed") return;
|
|
20197
|
-
await rm4(context.target.targetPath, { force: true, recursive: true });
|
|
19375
|
+
async selectCommand(target, input) {
|
|
19376
|
+
return await this.selectInstallCommand?.(target, input) ?? target.installCommand;
|
|
20198
19377
|
}
|
|
20199
19378
|
};
|
|
20200
|
-
async function
|
|
20201
|
-
|
|
20202
|
-
|
|
20203
|
-
realpath3(target.sourceRoot),
|
|
20204
|
-
realpath3(target.sourcePath)
|
|
20205
|
-
]);
|
|
20206
|
-
const sourceStats = await lstat4(source);
|
|
20207
|
-
return sourceStats.isDirectory() && isWithin(root, source);
|
|
20208
|
-
} catch {
|
|
20209
|
-
return false;
|
|
19379
|
+
async function hasExistingPath(root, paths) {
|
|
19380
|
+
for (const path9 of paths) {
|
|
19381
|
+
if (await pathExists2(join12(root, path9))) return true;
|
|
20210
19382
|
}
|
|
19383
|
+
return false;
|
|
20211
19384
|
}
|
|
20212
|
-
async function
|
|
19385
|
+
async function isYarnBerry(root) {
|
|
19386
|
+
if (await pathExists2(join12(root, ".yarnrc.yml"))) return true;
|
|
20213
19387
|
try {
|
|
20214
|
-
const
|
|
20215
|
-
join12(
|
|
20216
|
-
"utf8"
|
|
20217
|
-
);
|
|
20218
|
-
const expected = config.match(/^version(?:_info)?\s*=\s*(\d+\.\d+)/mu)?.[1];
|
|
20219
|
-
if (!expected) return false;
|
|
20220
|
-
const expectedImplementation = config.match(/^implementation\s*=\s*([^\s#]+)/imu)?.[1]?.toLowerCase();
|
|
20221
|
-
const sourceInterpreter = join12(
|
|
20222
|
-
target.sourcePath,
|
|
20223
|
-
process.platform === "win32" ? "Scripts/python.exe" : "bin/python"
|
|
19388
|
+
const packageJson3 = JSON.parse(
|
|
19389
|
+
await readFile8(join12(root, "package.json"), "utf8")
|
|
20224
19390
|
);
|
|
20225
|
-
const
|
|
20226
|
-
const
|
|
20227
|
-
|
|
20228
|
-
fingerprintScript
|
|
20229
|
-
]);
|
|
20230
|
-
const sourceFingerprint = source.stdout.trim();
|
|
20231
|
-
const [sourceVersion, sourceMachine, sourceImplementation, extra] = sourceFingerprint.split("|");
|
|
20232
|
-
return extra === void 0 && sourceVersion === expected && Boolean(sourceMachine) && Boolean(sourceImplementation) && (expectedImplementation === void 0 || sourceImplementation.toLowerCase() === expectedImplementation);
|
|
19391
|
+
const packageManager = packageJson3.packageManager;
|
|
19392
|
+
const match = typeof packageManager === "string" && packageManager.match(/^yarn@(\d+)/u);
|
|
19393
|
+
return match ? Number(match[1]) >= 2 : false;
|
|
20233
19394
|
} catch {
|
|
20234
19395
|
return false;
|
|
20235
19396
|
}
|
|
20236
19397
|
}
|
|
20237
|
-
function
|
|
20238
|
-
const prefixes = [];
|
|
20239
|
-
for (const line of text.split(/\r?\n/u)) {
|
|
20240
|
-
if (!/\bVIRTUAL_ENV\b/u.test(line)) continue;
|
|
20241
|
-
const quotedAssignment = line.match(
|
|
20242
|
-
/\bVIRTUAL_ENV(?:\s*=|\s+)\s*(?:"([^"]+)"|'([^']+)')/u
|
|
20243
|
-
);
|
|
20244
|
-
const quotedPrefix = quotedAssignment?.[1] ?? quotedAssignment?.[2];
|
|
20245
|
-
if (quotedPrefix && isAbsolute4(quotedPrefix)) prefixes.push(quotedPrefix);
|
|
20246
|
-
const unquoted = line.match(/\bVIRTUAL_ENV(?:\s*=|\s+)\s*([^\s"']+)/u)?.[1];
|
|
20247
|
-
if (unquoted && isAbsolute4(unquoted)) prefixes.push(unquoted);
|
|
20248
|
-
}
|
|
20249
|
-
return uniquePaths(prefixes);
|
|
20250
|
-
}
|
|
20251
|
-
function pythonPrefixFromShebang(text) {
|
|
20252
|
-
const command = text.match(/^#!\s*([^\r\n]+)/u)?.[1]?.trim().split(/\s+/u)[0];
|
|
20253
|
-
if (!command || !isAbsolute4(command)) return void 0;
|
|
20254
|
-
if (!/^python(?:\d+(?:\.\d+)?)?(?:\.exe)?$/iu.test(
|
|
20255
|
-
command.split(sep3).at(-1) ?? ""
|
|
20256
|
-
)) {
|
|
20257
|
-
return void 0;
|
|
20258
|
-
}
|
|
20259
|
-
return dirname9(dirname9(command));
|
|
20260
|
-
}
|
|
20261
|
-
function pythonPrefixesFromText(text) {
|
|
20262
|
-
const interpreters = [];
|
|
20263
|
-
for (const match of text.matchAll(/["'](\/[^"']+)["']/gu)) {
|
|
20264
|
-
if (match[1]) interpreters.push(match[1]);
|
|
20265
|
-
}
|
|
20266
|
-
for (const match of text.matchAll(/(?:^|\s)(\/[^\s"']+)/gmu)) {
|
|
20267
|
-
if (match[1]) interpreters.push(match[1]);
|
|
20268
|
-
}
|
|
20269
|
-
return uniquePaths(
|
|
20270
|
-
interpreters.flatMap((interpreter) => {
|
|
20271
|
-
const name = interpreter.split(sep3).at(-1) ?? "";
|
|
20272
|
-
return /^python(?:\d+(?:\.\d+)?)?(?:\.exe)?$/iu.test(name) ? [dirname9(dirname9(interpreter))] : [];
|
|
20273
|
-
})
|
|
20274
|
-
);
|
|
20275
|
-
}
|
|
20276
|
-
async function validateUvRelocation(target, context) {
|
|
20277
|
-
if (context.input === "preserve") return;
|
|
20278
|
-
const scriptsDirectory = join12(
|
|
20279
|
-
target.targetPath,
|
|
20280
|
-
process.platform === "win32" ? "Scripts" : "bin"
|
|
20281
|
-
);
|
|
20282
|
-
const interpreter = join12(
|
|
20283
|
-
scriptsDirectory,
|
|
20284
|
-
process.platform === "win32" ? "python.exe" : "python"
|
|
20285
|
-
);
|
|
20286
|
-
const { stdout: stdout3 } = await execFileAsync6(interpreter, [
|
|
20287
|
-
"-c",
|
|
20288
|
-
"import os, sys; print(os.path.realpath(sys.prefix))"
|
|
20289
|
-
]);
|
|
20290
|
-
const [actualPrefix, expectedPrefix] = await Promise.all([
|
|
20291
|
-
realpath3(stdout3.trim()),
|
|
20292
|
-
realpath3(target.targetPath)
|
|
20293
|
-
]);
|
|
20294
|
-
if (actualPrefix !== expectedPrefix) {
|
|
20295
|
-
throw new Error(
|
|
20296
|
-
`uv environment still points to its source prefix: ${actualPrefix}`
|
|
20297
|
-
);
|
|
20298
|
-
}
|
|
20299
|
-
const sourcePaths = uniquePaths([
|
|
20300
|
-
target.sourcePath,
|
|
20301
|
-
await realpath3(target.sourcePath).catch(() => void 0)
|
|
20302
|
-
]);
|
|
20303
|
-
const entries = await readdir2(scriptsDirectory, { withFileTypes: true });
|
|
20304
|
-
assertSafeUvScriptEntries(entries);
|
|
20305
|
-
const environmentName = basename7(target.targetPath);
|
|
20306
|
-
const acceptedPrefixes = /* @__PURE__ */ new Set([
|
|
20307
|
-
resolve9(target.targetPath),
|
|
20308
|
-
await realpath3(target.targetPath)
|
|
20309
|
-
]);
|
|
20310
|
-
let validatedBytes = 0;
|
|
20311
|
-
for (const path9 of [
|
|
20312
|
-
join12(target.targetPath, "pyvenv.cfg"),
|
|
20313
|
-
...entries.filter((entry) => entry.isFile()).map((entry) => join12(scriptsDirectory, entry.name))
|
|
20314
|
-
]) {
|
|
20315
|
-
const validated = await readBoundedUvTextFile(
|
|
20316
|
-
path9,
|
|
20317
|
-
UV_VALIDATION_MAX_TOTAL_BYTES - validatedBytes
|
|
20318
|
-
);
|
|
20319
|
-
if (!validated) continue;
|
|
20320
|
-
validatedBytes += validated.bytes;
|
|
20321
|
-
if (validated.text === void 0) continue;
|
|
20322
|
-
const { text } = validated;
|
|
20323
|
-
if (sourcePaths.some((sourcePath) => text.includes(sourcePath))) {
|
|
20324
|
-
throw new Error(`uv environment contains a stale source path: ${path9}`);
|
|
20325
|
-
}
|
|
20326
|
-
const shebangPrefix = pythonPrefixFromShebang(text);
|
|
20327
|
-
if (shebangPrefix && basename7(shebangPrefix) === environmentName && !acceptedPrefixes.has(resolve9(shebangPrefix))) {
|
|
20328
|
-
throw new Error(`uv launcher points outside its environment: ${path9}`);
|
|
20329
|
-
}
|
|
20330
|
-
if (pythonPrefixesFromText(text).filter((prefix) => basename7(prefix) === environmentName).some((prefix) => !acceptedPrefixes.has(resolve9(prefix)))) {
|
|
20331
|
-
throw new Error(`uv launcher points outside its environment: ${path9}`);
|
|
20332
|
-
}
|
|
20333
|
-
if (virtualEnvironmentPrefixes(text).some(
|
|
20334
|
-
(prefix) => !acceptedPrefixes.has(resolve9(prefix))
|
|
20335
|
-
)) {
|
|
20336
|
-
throw new Error(
|
|
20337
|
-
`uv activation script points outside its environment: ${path9}`
|
|
20338
|
-
);
|
|
20339
|
-
}
|
|
20340
|
-
}
|
|
20341
|
-
}
|
|
20342
|
-
async function validateUvStructure(target, context) {
|
|
20343
|
-
if (context.input === "preserve") return;
|
|
20344
|
-
const targetStats = await lstat4(target.targetPath).catch(() => void 0);
|
|
20345
|
-
if (!targetStats) return;
|
|
20346
|
-
if (!targetStats.isDirectory() || targetStats.isSymbolicLink()) {
|
|
20347
|
-
throw new Error("uv environment must be a real directory");
|
|
20348
|
-
}
|
|
20349
|
-
const configStats = await lstat4(join12(target.targetPath, "pyvenv.cfg")).catch(
|
|
20350
|
-
() => void 0
|
|
20351
|
-
);
|
|
20352
|
-
if (configStats && (!configStats.isFile() || configStats.isSymbolicLink())) {
|
|
20353
|
-
throw new Error("uv pyvenv.cfg must be a regular file");
|
|
20354
|
-
}
|
|
20355
|
-
const scriptsDirectory = join12(
|
|
20356
|
-
target.targetPath,
|
|
20357
|
-
process.platform === "win32" ? "Scripts" : "bin"
|
|
20358
|
-
);
|
|
20359
|
-
const scriptsStats = await lstat4(scriptsDirectory).catch(() => void 0);
|
|
20360
|
-
if (!scriptsStats) return;
|
|
20361
|
-
if (!scriptsStats.isDirectory() || scriptsStats.isSymbolicLink()) {
|
|
20362
|
-
throw new Error("uv scripts path must be a real directory");
|
|
20363
|
-
}
|
|
20364
|
-
assertSafeUvScriptEntries(
|
|
20365
|
-
await readdir2(scriptsDirectory, { withFileTypes: true })
|
|
20366
|
-
);
|
|
20367
|
-
}
|
|
20368
|
-
function assertSafeUvScriptEntries(entries) {
|
|
20369
|
-
if (entries.length > UV_VALIDATION_MAX_ENTRIES) {
|
|
20370
|
-
throw new Error("uv scripts directory exceeds the validation entry limit");
|
|
20371
|
-
}
|
|
20372
|
-
for (const entry of entries) {
|
|
20373
|
-
if (entry.isSymbolicLink() && !isUvInterpreterName(entry.name)) {
|
|
20374
|
-
throw new Error(`uv script must not be a symbolic link: ${entry.name}`);
|
|
20375
|
-
}
|
|
20376
|
-
}
|
|
20377
|
-
}
|
|
20378
|
-
async function readBoundedUvTextFile(path9, remainingBytes) {
|
|
20379
|
-
let handle;
|
|
20380
|
-
try {
|
|
20381
|
-
handle = await open3(
|
|
20382
|
-
path9,
|
|
20383
|
-
constants6.O_RDONLY | (process.platform === "win32" ? 0 : constants6.O_NOFOLLOW)
|
|
20384
|
-
);
|
|
20385
|
-
} catch (error) {
|
|
20386
|
-
if (isNotFoundError(error)) return void 0;
|
|
20387
|
-
throw error;
|
|
20388
|
-
}
|
|
20389
|
-
try {
|
|
20390
|
-
const stats = await handle.stat();
|
|
20391
|
-
if (!stats.isFile()) return void 0;
|
|
20392
|
-
if (stats.size > UV_VALIDATION_MAX_FILE_BYTES) {
|
|
20393
|
-
throw new Error(`uv launcher exceeds the validation size limit: ${path9}`);
|
|
20394
|
-
}
|
|
20395
|
-
if (stats.size > remainingBytes) {
|
|
20396
|
-
throw new Error("uv launchers exceed the total validation size limit");
|
|
20397
|
-
}
|
|
20398
|
-
const readLimit = Math.min(UV_VALIDATION_MAX_FILE_BYTES, remainingBytes);
|
|
20399
|
-
const contents = await readFilePrefix(handle, readLimit + 1);
|
|
20400
|
-
if (contents.byteLength > UV_VALIDATION_MAX_FILE_BYTES) {
|
|
20401
|
-
throw new Error(`uv launcher exceeds the validation size limit: ${path9}`);
|
|
20402
|
-
}
|
|
20403
|
-
if (contents.byteLength > remainingBytes) {
|
|
20404
|
-
throw new Error("uv launchers exceed the total validation size limit");
|
|
20405
|
-
}
|
|
20406
|
-
const text = contents.toString("utf8");
|
|
20407
|
-
return Buffer.from(text, "utf8").equals(contents) ? { bytes: contents.byteLength, text } : { bytes: contents.byteLength };
|
|
20408
|
-
} finally {
|
|
20409
|
-
await handle.close();
|
|
20410
|
-
}
|
|
20411
|
-
}
|
|
20412
|
-
async function readFilePrefix(handle, maxBytes) {
|
|
20413
|
-
const contents = Buffer.allocUnsafe(maxBytes);
|
|
20414
|
-
let offset = 0;
|
|
20415
|
-
while (offset < contents.byteLength) {
|
|
20416
|
-
const { bytesRead } = await handle.read(
|
|
20417
|
-
contents,
|
|
20418
|
-
offset,
|
|
20419
|
-
contents.byteLength - offset,
|
|
20420
|
-
offset
|
|
20421
|
-
);
|
|
20422
|
-
if (bytesRead === 0) break;
|
|
20423
|
-
offset += bytesRead;
|
|
20424
|
-
}
|
|
20425
|
-
return contents.subarray(0, offset);
|
|
20426
|
-
}
|
|
20427
|
-
function isUvInterpreterName(name) {
|
|
20428
|
-
return /^python(?:\d+(?:\.\d+)?)?(?:\.exe)?$/iu.test(name);
|
|
20429
|
-
}
|
|
20430
|
-
async function removeBundlerExtensionMarkers(root) {
|
|
20431
|
-
const pending = [root];
|
|
20432
|
-
while (pending.length > 0) {
|
|
20433
|
-
const current = pending.pop();
|
|
20434
|
-
if (!current) continue;
|
|
20435
|
-
let entries;
|
|
20436
|
-
try {
|
|
20437
|
-
entries = await readdir2(current, { withFileTypes: true });
|
|
20438
|
-
} catch (error) {
|
|
20439
|
-
if (isNotFoundError(error)) continue;
|
|
20440
|
-
throw error;
|
|
20441
|
-
}
|
|
20442
|
-
for (const entry of entries) {
|
|
20443
|
-
const path9 = join12(current, entry.name);
|
|
20444
|
-
if (entry.isDirectory()) pending.push(path9);
|
|
20445
|
-
else if (entry.isFile() && isBundlerExtensionMarker(root, path9)) {
|
|
20446
|
-
await rm4(path9, { force: true });
|
|
20447
|
-
}
|
|
20448
|
-
}
|
|
20449
|
-
}
|
|
20450
|
-
}
|
|
20451
|
-
function isBundlerExtensionMarker(root, path9) {
|
|
20452
|
-
const segments = relative3(root, path9).split(sep3);
|
|
20453
|
-
return segments.length >= 7 && segments[0] === "ruby" && segments[2] === "extensions" && segments.at(-1) === "gem.build_complete";
|
|
20454
|
-
}
|
|
20455
|
-
function uniquePaths(paths) {
|
|
19398
|
+
function uniquePaths2(paths) {
|
|
20456
19399
|
return [...new Set(paths.filter((path9) => Boolean(path9)))];
|
|
20457
19400
|
}
|
|
20458
19401
|
async function isAllowedSourceRoot(repoRoot, sourceRoot) {
|
|
@@ -20462,7 +19405,7 @@ async function isAllowedSourceRoot(repoRoot, sourceRoot) {
|
|
|
20462
19405
|
realpath3(sourceRoot)
|
|
20463
19406
|
]);
|
|
20464
19407
|
if (resolvedRepoRoot === resolvedSourceRoot) return true;
|
|
20465
|
-
const gitFile = await
|
|
19408
|
+
const gitFile = await readFile8(join12(resolvedSourceRoot, ".git"), "utf8");
|
|
20466
19409
|
const gitDirectory = gitFile.match(/^gitdir:\s*(.+)$/mu)?.[1];
|
|
20467
19410
|
if (!gitDirectory) return false;
|
|
20468
19411
|
return isWithin(
|
|
@@ -20474,10 +19417,10 @@ async function isAllowedSourceRoot(repoRoot, sourceRoot) {
|
|
|
20474
19417
|
}
|
|
20475
19418
|
}
|
|
20476
19419
|
function isWithin(root, candidate) {
|
|
20477
|
-
const distance =
|
|
19420
|
+
const distance = relative2(root, candidate);
|
|
20478
19421
|
return distance === "" || distance !== ".." && !distance.startsWith(`..${sep3}`);
|
|
20479
19422
|
}
|
|
20480
|
-
function
|
|
19423
|
+
function toErrorMessage(error) {
|
|
20481
19424
|
return error instanceof Error ? error.message : String(error);
|
|
20482
19425
|
}
|
|
20483
19426
|
|
|
@@ -20490,27 +19433,17 @@ async function createDependencyBootstrapPreview(mode, context) {
|
|
|
20490
19433
|
function formatDependencyBootstrapPreview(preview) {
|
|
20491
19434
|
if (!preview) return "";
|
|
20492
19435
|
return preview.targets.map(
|
|
20493
|
-
({ adapter, target,
|
|
19436
|
+
({ adapter, target, command }) => `Would install ${target || "dependencies"} with ${adapter}: ${command}
|
|
20494
19437
|
`
|
|
20495
19438
|
).join("");
|
|
20496
19439
|
}
|
|
20497
|
-
function formatBootstrapStrategy(strategy) {
|
|
20498
|
-
switch (strategy) {
|
|
20499
|
-
case "cow-then-repair":
|
|
20500
|
-
return "seed and repair";
|
|
20501
|
-
case "repair-only":
|
|
20502
|
-
return "repair";
|
|
20503
|
-
case "install-only":
|
|
20504
|
-
return "install";
|
|
20505
|
-
}
|
|
20506
|
-
}
|
|
20507
19440
|
|
|
20508
19441
|
// src/conflict.ts
|
|
20509
|
-
import { constants as
|
|
19442
|
+
import { constants as constants6 } from "node:fs";
|
|
20510
19443
|
import { access as access7 } from "node:fs/promises";
|
|
20511
19444
|
async function pathExists3(path9) {
|
|
20512
19445
|
try {
|
|
20513
|
-
await access7(path9,
|
|
19446
|
+
await access7(path9, constants6.F_OK);
|
|
20514
19447
|
return true;
|
|
20515
19448
|
} catch {
|
|
20516
19449
|
return false;
|
|
@@ -20525,257 +19458,41 @@ async function promptForPathConflict(path9) {
|
|
|
20525
19458
|
label: "Abort",
|
|
20526
19459
|
hint: "Keep the existing directory untouched"
|
|
20527
19460
|
},
|
|
20528
|
-
{
|
|
20529
|
-
value: "reuse",
|
|
20530
|
-
label: "Reuse path",
|
|
20531
|
-
hint: "Print the existing path and stop"
|
|
20532
|
-
}
|
|
20533
|
-
]
|
|
20534
|
-
});
|
|
20535
|
-
if (pD(choice)) {
|
|
20536
|
-
return "abort";
|
|
20537
|
-
}
|
|
20538
|
-
return choice;
|
|
20539
|
-
}
|
|
20540
|
-
|
|
20541
|
-
// src/dependency-bootstrap-prompt.ts
|
|
20542
|
-
async function resolveDependencyBootstrapPolicy(context, config, dependencyBootstrapExplicit, options) {
|
|
20543
|
-
if (dependencyBootstrapExplicit) {
|
|
20544
|
-
return {
|
|
20545
|
-
mode: config.dependencyBootstrap ?? "off",
|
|
20546
|
-
prompted: false,
|
|
20547
|
-
source: "explicit"
|
|
20548
|
-
};
|
|
20549
|
-
}
|
|
20550
|
-
if (options.dryRun || options.nonInteractive || isHeadless()) {
|
|
20551
|
-
return { mode: "off", prompted: false, source: "default" };
|
|
20552
|
-
}
|
|
20553
|
-
if (options.legacyInstallPromptConfigured && !options.dependencies?.promptForDependencyBootstrap) {
|
|
20554
|
-
return { mode: "off", prompted: false, source: "legacy" };
|
|
20555
|
-
}
|
|
20556
|
-
let candidate;
|
|
20557
|
-
try {
|
|
20558
|
-
candidate = await detectDependencyBootstrapCandidate(context);
|
|
20559
|
-
} catch (error) {
|
|
20560
|
-
options.stderr(
|
|
20561
|
-
`gji: dependency setup detection failed: ${toErrorMessage3(error)}
|
|
20562
|
-
`
|
|
20563
|
-
);
|
|
20564
|
-
return { mode: "off", prompted: false, source: "default" };
|
|
20565
|
-
}
|
|
20566
|
-
if (!candidate) return { mode: "off", prompted: false, source: "default" };
|
|
20567
|
-
const prompt = options.dependencies?.promptForDependencyBootstrap ?? defaultPromptForDependencyBootstrap;
|
|
20568
|
-
const choice = await prompt(candidate);
|
|
20569
|
-
if (choice === null)
|
|
20570
|
-
return { mode: "off", prompted: true, source: "prompted" };
|
|
20571
|
-
const mode = choice;
|
|
20572
|
-
await persistDependencyBootstrapPolicy(
|
|
20573
|
-
context.repoRoot,
|
|
20574
|
-
config,
|
|
20575
|
-
mode,
|
|
20576
|
-
options.dependencies,
|
|
20577
|
-
options.stderr
|
|
20578
|
-
);
|
|
20579
|
-
return { mode, prompted: true, source: "prompted" };
|
|
20580
|
-
}
|
|
20581
|
-
async function persistDependencyBootstrapPolicy(repoRoot, config, mode, dependencies, stderr) {
|
|
20582
|
-
const writeLocal = dependencies?.writeConfigKey ?? (async (root, key, value) => {
|
|
20583
|
-
await updateLocalConfigKey(root, key, value);
|
|
20584
|
-
});
|
|
20585
|
-
const writeGlobal = dependencies?.writeGlobalRepoConfigKey ?? (async (root, key, value) => {
|
|
20586
|
-
await updateGlobalRepoConfigKey(root, key, value);
|
|
20587
|
-
});
|
|
20588
|
-
try {
|
|
20589
|
-
if (config.installSaveTarget === "global") {
|
|
20590
|
-
await writeGlobal(repoRoot, "dependencyBootstrap", mode);
|
|
20591
|
-
} else {
|
|
20592
|
-
await writeLocal(repoRoot, "dependencyBootstrap", mode);
|
|
20593
|
-
}
|
|
20594
|
-
} catch (error) {
|
|
20595
|
-
stderr(
|
|
20596
|
-
`gji: failed to save dependencyBootstrap: ${toErrorMessage3(error)}
|
|
20597
|
-
`
|
|
20598
|
-
);
|
|
20599
|
-
}
|
|
20600
|
-
}
|
|
20601
|
-
async function defaultPromptForDependencyBootstrap(candidate) {
|
|
20602
|
-
const reuseHint = formatReuseHint(candidate);
|
|
20603
|
-
const subject = candidate.kind === "build-cache" ? "dependencies and build state" : "dependencies";
|
|
20604
|
-
const choice = await ve({
|
|
20605
|
-
message: `Set up ${candidate.adapter} ${subject} for new worktrees? (${candidate.lockfile})`,
|
|
20606
|
-
options: [
|
|
20607
|
-
{
|
|
20608
|
-
value: "cow-then-repair",
|
|
20609
|
-
label: "Reuse and repair (recommended)",
|
|
20610
|
-
hint: reuseHint
|
|
20611
|
-
},
|
|
20612
|
-
{
|
|
20613
|
-
value: "install-only",
|
|
20614
|
-
label: "Install fresh each time",
|
|
20615
|
-
hint: candidate.repairCommand
|
|
20616
|
-
},
|
|
20617
|
-
{
|
|
20618
|
-
value: "off",
|
|
20619
|
-
label: "Skip dependency setup",
|
|
20620
|
-
hint: "leave dependencies to project hooks or manual setup"
|
|
20621
|
-
}
|
|
20622
|
-
]
|
|
20623
|
-
});
|
|
20624
|
-
if (pD(choice)) return null;
|
|
20625
|
-
return choice;
|
|
20626
|
-
}
|
|
20627
|
-
function formatReuseHint(candidate) {
|
|
20628
|
-
if (candidate.adapter === "pnpm") {
|
|
20629
|
-
return "reuse local node_modules through CoW, then run pnpm install --frozen-lockfile";
|
|
20630
|
-
}
|
|
20631
|
-
if (candidate.adapter === "npm") {
|
|
20632
|
-
return "npm uses a clean install because node_modules is not seeded";
|
|
20633
|
-
}
|
|
20634
|
-
if (!candidate.seedable) {
|
|
20635
|
-
return `repair ${candidate.target} with ${candidate.repairCommand}`;
|
|
20636
|
-
}
|
|
20637
|
-
return `reuse ${candidate.target} through CoW, then run ${candidate.repairCommand}`;
|
|
20638
|
-
}
|
|
20639
|
-
function toErrorMessage3(error) {
|
|
20640
|
-
return error instanceof Error ? error.message : String(error);
|
|
20641
|
-
}
|
|
20642
|
-
|
|
20643
|
-
// src/sync-plan.ts
|
|
20644
|
-
import { lstat as lstat5, realpath as realpath4 } from "node:fs/promises";
|
|
20645
|
-
import { isAbsolute as isAbsolute5, join as join13, relative as relative4, resolve as resolve10, sep as sep4 } from "node:path";
|
|
20646
|
-
async function prepareSyncDirectoryPlan(repoRoot, worktreePath, directories) {
|
|
20647
|
-
const normalizedDirectories = directories.map(validateSyncDirPattern).sort(directoryDepthAscending);
|
|
20648
|
-
const plan = [];
|
|
20649
|
-
for (const directory of normalizedDirectories) {
|
|
20650
|
-
const destination = join13(worktreePath, directory);
|
|
20651
|
-
const destinationState = await inspectDestination(
|
|
20652
|
-
worktreePath,
|
|
20653
|
-
destination
|
|
20654
|
-
);
|
|
20655
|
-
const destinationWasPresent = destinationState.kind === "exists";
|
|
20656
|
-
const destinationWarning = destinationState.kind === "unsafe" ? destinationState.reason : void 0;
|
|
20657
|
-
try {
|
|
20658
|
-
const source = await resolveSyncDirectorySource(repoRoot, directory);
|
|
20659
|
-
if (!source) {
|
|
20660
|
-
plan.push({
|
|
20661
|
-
directory,
|
|
20662
|
-
destination,
|
|
20663
|
-
worktreePath,
|
|
20664
|
-
destinationWasPresent,
|
|
20665
|
-
destinationWarning
|
|
20666
|
-
});
|
|
20667
|
-
} else if ("warning" in source) {
|
|
20668
|
-
plan.push({
|
|
20669
|
-
directory,
|
|
20670
|
-
destination,
|
|
20671
|
-
worktreePath,
|
|
20672
|
-
destinationWasPresent,
|
|
20673
|
-
destinationWarning,
|
|
20674
|
-
warning: source.warning
|
|
20675
|
-
});
|
|
20676
|
-
} else {
|
|
20677
|
-
plan.push({
|
|
20678
|
-
directory,
|
|
20679
|
-
destination,
|
|
20680
|
-
worktreePath,
|
|
20681
|
-
destinationWasPresent,
|
|
20682
|
-
destinationWarning,
|
|
20683
|
-
source: source.path
|
|
20684
|
-
});
|
|
20685
|
-
}
|
|
20686
|
-
} catch (error) {
|
|
20687
|
-
plan.push({
|
|
20688
|
-
directory,
|
|
20689
|
-
destination,
|
|
20690
|
-
worktreePath,
|
|
20691
|
-
destinationWasPresent,
|
|
20692
|
-
destinationWarning,
|
|
20693
|
-
warning: `could not inspect ${directory}: ${toErrorMessage4(error)}`
|
|
20694
|
-
});
|
|
20695
|
-
}
|
|
20696
|
-
}
|
|
20697
|
-
return plan;
|
|
20698
|
-
}
|
|
20699
|
-
async function estimateSyncDirectoryPlan(plan) {
|
|
20700
|
-
const estimates = [];
|
|
20701
|
-
for (const entry of plan) {
|
|
20702
|
-
if (entry.destinationWasPresent || entry.destinationWarning || !entry.source || entry.warning || isCoveredByCloneAncestor(entry, plan)) {
|
|
20703
|
-
continue;
|
|
20704
|
-
}
|
|
20705
|
-
try {
|
|
20706
|
-
estimates.push({
|
|
20707
|
-
bytes: await directorySize(entry.source),
|
|
20708
|
-
dir: entry.directory
|
|
20709
|
-
});
|
|
20710
|
-
} catch {
|
|
20711
|
-
}
|
|
20712
|
-
}
|
|
20713
|
-
return estimates;
|
|
20714
|
-
}
|
|
20715
|
-
async function estimateSyncDirectories(repoRoot, worktreePath, directories) {
|
|
20716
|
-
const plan = await prepareSyncDirectoryPlan(
|
|
20717
|
-
repoRoot,
|
|
20718
|
-
worktreePath,
|
|
20719
|
-
directories
|
|
20720
|
-
);
|
|
20721
|
-
return estimateSyncDirectoryPlan(plan);
|
|
20722
|
-
}
|
|
20723
|
-
async function resolveSyncDirectorySource(repoRoot, directory) {
|
|
20724
|
-
const source = join13(repoRoot, directory);
|
|
20725
|
-
let resolvedSource;
|
|
20726
|
-
try {
|
|
20727
|
-
resolvedSource = await realpath4(source);
|
|
20728
|
-
} catch (error) {
|
|
20729
|
-
if (isNotFoundError(error)) return null;
|
|
20730
|
-
throw error;
|
|
20731
|
-
}
|
|
20732
|
-
if (!isPathInside2(repoRoot, resolvedSource)) {
|
|
20733
|
-
return {
|
|
20734
|
-
warning: `source symlink resolves outside the repository (${resolvedSource})`
|
|
20735
|
-
};
|
|
19461
|
+
{
|
|
19462
|
+
value: "reuse",
|
|
19463
|
+
label: "Reuse path",
|
|
19464
|
+
hint: "Print the existing path and stop"
|
|
19465
|
+
}
|
|
19466
|
+
]
|
|
19467
|
+
});
|
|
19468
|
+
if (pD(choice)) {
|
|
19469
|
+
return "abort";
|
|
20736
19470
|
}
|
|
20737
|
-
|
|
20738
|
-
if (!stats.isDirectory()) return { warning: "source is not a directory" };
|
|
20739
|
-
return { path: resolvedSource };
|
|
20740
|
-
}
|
|
20741
|
-
function isCoveredByCloneAncestor(entry, plan) {
|
|
20742
|
-
return plan.some(
|
|
20743
|
-
(candidate) => candidate !== entry && !candidate.destinationWasPresent && !candidate.destinationWarning && !!candidate.source && isPathInside2(candidate.directory, entry.directory)
|
|
20744
|
-
);
|
|
20745
|
-
}
|
|
20746
|
-
function directoryDepthAscending(left, right) {
|
|
20747
|
-
return left.split(/[\\/]+/u).length - right.split(/[\\/]+/u).length;
|
|
20748
|
-
}
|
|
20749
|
-
function isPathInside2(parent, child) {
|
|
20750
|
-
const relativePath = relative4(resolve10(parent), resolve10(child));
|
|
20751
|
-
return relativePath !== "" && !isAbsolute5(relativePath) && relativePath !== ".." && !relativePath.startsWith(`..${sep4}`);
|
|
20752
|
-
}
|
|
20753
|
-
function toErrorMessage4(error) {
|
|
20754
|
-
return error instanceof Error ? error.message : String(error);
|
|
19471
|
+
return choice;
|
|
20755
19472
|
}
|
|
20756
19473
|
|
|
20757
19474
|
// src/worktree-bootstrap.ts
|
|
20758
|
-
import { basename as
|
|
19475
|
+
import { basename as basename8 } from "node:path";
|
|
20759
19476
|
|
|
20760
19477
|
// src/file-sync.ts
|
|
20761
|
-
import { constants as
|
|
20762
|
-
import { copyFile, lstat as
|
|
19478
|
+
import { constants as constants7 } from "node:fs";
|
|
19479
|
+
import { copyFile, lstat as lstat4, realpath as realpath4 } from "node:fs/promises";
|
|
20763
19480
|
import {
|
|
20764
|
-
basename as
|
|
20765
|
-
dirname as
|
|
20766
|
-
isAbsolute as
|
|
20767
|
-
join as
|
|
20768
|
-
normalize
|
|
20769
|
-
relative as
|
|
20770
|
-
resolve as
|
|
20771
|
-
sep as
|
|
19481
|
+
basename as basename7,
|
|
19482
|
+
dirname as dirname9,
|
|
19483
|
+
isAbsolute as isAbsolute4,
|
|
19484
|
+
join as join13,
|
|
19485
|
+
normalize,
|
|
19486
|
+
relative as relative3,
|
|
19487
|
+
resolve as resolve10,
|
|
19488
|
+
sep as sep4
|
|
20772
19489
|
} from "node:path";
|
|
20773
19490
|
async function syncFiles(mainRoot, targetPath, patterns) {
|
|
20774
19491
|
for (const pattern of patterns) {
|
|
20775
19492
|
const normalized = validateSyncFilePattern(pattern);
|
|
20776
19493
|
const sourcePath = await resolveSyncFileSource(mainRoot, normalized);
|
|
20777
19494
|
if (!sourcePath) continue;
|
|
20778
|
-
const destPath =
|
|
19495
|
+
const destPath = join13(targetPath, normalized);
|
|
20779
19496
|
const existingDestination = await readDestinationEntry(destPath);
|
|
20780
19497
|
if (existingDestination?.isSymbolicLink()) {
|
|
20781
19498
|
throw new Error(`destination is a symbolic link: ${destPath}`);
|
|
@@ -20783,7 +19500,7 @@ async function syncFiles(mainRoot, targetPath, patterns) {
|
|
|
20783
19500
|
if (existingDestination) {
|
|
20784
19501
|
continue;
|
|
20785
19502
|
}
|
|
20786
|
-
const destinationParent =
|
|
19503
|
+
const destinationParent = dirname9(destPath);
|
|
20787
19504
|
const beforeCreate = await inspectDestination(
|
|
20788
19505
|
targetPath,
|
|
20789
19506
|
destinationParent
|
|
@@ -20796,14 +19513,14 @@ async function syncFiles(mainRoot, targetPath, patterns) {
|
|
|
20796
19513
|
destinationParent
|
|
20797
19514
|
);
|
|
20798
19515
|
try {
|
|
20799
|
-
const safeDestination =
|
|
19516
|
+
const safeDestination = join13(safeParent.path, basename7(destPath));
|
|
20800
19517
|
const safeExistingDestination = await readDestinationEntry(safeDestination);
|
|
20801
19518
|
if (safeExistingDestination?.isSymbolicLink()) {
|
|
20802
19519
|
throw new Error(`destination is a symbolic link: ${destPath}`);
|
|
20803
19520
|
}
|
|
20804
19521
|
if (safeExistingDestination) continue;
|
|
20805
19522
|
try {
|
|
20806
|
-
await copyFile(sourcePath, safeDestination,
|
|
19523
|
+
await copyFile(sourcePath, safeDestination, constants7.COPYFILE_EXCL);
|
|
20807
19524
|
} catch (error) {
|
|
20808
19525
|
if (!isAlreadyExistsError(error)) throw error;
|
|
20809
19526
|
}
|
|
@@ -20813,12 +19530,12 @@ async function syncFiles(mainRoot, targetPath, patterns) {
|
|
|
20813
19530
|
}
|
|
20814
19531
|
}
|
|
20815
19532
|
function validateSyncFilePattern(pattern) {
|
|
20816
|
-
if (
|
|
19533
|
+
if (isAbsolute4(pattern)) {
|
|
20817
19534
|
throw new Error(
|
|
20818
19535
|
`syncFiles: pattern must be a relative path, got: ${pattern}`
|
|
20819
19536
|
);
|
|
20820
19537
|
}
|
|
20821
|
-
const normalized =
|
|
19538
|
+
const normalized = normalize(pattern);
|
|
20822
19539
|
if (normalized.startsWith("..")) {
|
|
20823
19540
|
throw new Error(
|
|
20824
19541
|
`syncFiles: pattern must not contain '..' segments, got: ${pattern}`
|
|
@@ -20831,424 +19548,42 @@ async function resolveSyncFileSource(mainRoot, pattern) {
|
|
|
20831
19548
|
let resolvedSource;
|
|
20832
19549
|
try {
|
|
20833
19550
|
[resolvedRoot, resolvedSource] = await Promise.all([
|
|
20834
|
-
|
|
20835
|
-
|
|
19551
|
+
realpath4(mainRoot),
|
|
19552
|
+
realpath4(join13(mainRoot, pattern))
|
|
20836
19553
|
]);
|
|
20837
19554
|
} catch (error) {
|
|
20838
19555
|
if (isNotFoundError(error)) return void 0;
|
|
20839
19556
|
throw error;
|
|
20840
19557
|
}
|
|
20841
|
-
if (!
|
|
19558
|
+
if (!isPathInside2(resolvedRoot, resolvedSource)) {
|
|
20842
19559
|
throw new Error(
|
|
20843
19560
|
`syncFiles: source symlink resolves outside the repository: ${resolvedSource}`
|
|
20844
19561
|
);
|
|
20845
19562
|
}
|
|
20846
|
-
const sourceStats = await
|
|
19563
|
+
const sourceStats = await lstat4(resolvedSource);
|
|
20847
19564
|
if (!sourceStats.isFile()) {
|
|
20848
19565
|
throw new Error(
|
|
20849
|
-
`syncFiles: source is not a file: ${
|
|
19566
|
+
`syncFiles: source is not a file: ${join13(mainRoot, pattern)}`
|
|
20850
19567
|
);
|
|
20851
19568
|
}
|
|
20852
19569
|
return resolvedSource;
|
|
20853
19570
|
}
|
|
20854
19571
|
async function readDestinationEntry(path9) {
|
|
20855
19572
|
try {
|
|
20856
|
-
return await
|
|
19573
|
+
return await lstat4(path9);
|
|
20857
19574
|
} catch (error) {
|
|
20858
19575
|
if (isNotFoundError(error)) return void 0;
|
|
20859
19576
|
throw error;
|
|
20860
19577
|
}
|
|
20861
19578
|
}
|
|
20862
|
-
function
|
|
20863
|
-
const distance =
|
|
20864
|
-
return distance === "" || !
|
|
20865
|
-
}
|
|
20866
|
-
|
|
20867
|
-
// src/package-manager.ts
|
|
20868
|
-
import { access as access8, readdir as readdir3 } from "node:fs/promises";
|
|
20869
|
-
import { join as join15 } from "node:path";
|
|
20870
|
-
var ENTRIES = [
|
|
20871
|
-
// JavaScript / TypeScript
|
|
20872
|
-
{ name: "pnpm", signals: ["pnpm-lock.yaml"], command: "pnpm install" },
|
|
20873
|
-
{ name: "yarn", signals: ["yarn.lock"], command: "yarn install" },
|
|
20874
|
-
{ name: "bun", signals: ["bun.lockb"], command: "bun install" },
|
|
20875
|
-
{ name: "npm", signals: ["package-lock.json"], command: "npm install" },
|
|
20876
|
-
{ name: "deno", signals: ["deno.json", "deno.jsonc"], command: "deno cache" },
|
|
20877
|
-
// Python
|
|
20878
|
-
{ name: "poetry", signals: ["poetry.lock"], command: "poetry install" },
|
|
20879
|
-
{ name: "uv", signals: ["uv.lock"], command: "uv sync" },
|
|
20880
|
-
{ name: "pipenv", signals: ["Pipfile.lock"], command: "pipenv install" },
|
|
20881
|
-
{ name: "pdm", signals: ["pdm.lock"], command: "pdm install" },
|
|
20882
|
-
{
|
|
20883
|
-
name: "conda-lock",
|
|
20884
|
-
signals: ["conda-lock.yml"],
|
|
20885
|
-
command: "conda-lock install"
|
|
20886
|
-
},
|
|
20887
|
-
{
|
|
20888
|
-
name: "conda",
|
|
20889
|
-
signals: ["environment.yml"],
|
|
20890
|
-
command: "conda env update --file environment.yml"
|
|
20891
|
-
},
|
|
20892
|
-
// R
|
|
20893
|
-
{
|
|
20894
|
-
name: "renv",
|
|
20895
|
-
signals: ["renv.lock"],
|
|
20896
|
-
command: "Rscript -e 'renv::restore()'"
|
|
20897
|
-
},
|
|
20898
|
-
// Rust
|
|
20899
|
-
{ name: "cargo", signals: ["Cargo.lock"], command: "cargo build" },
|
|
20900
|
-
// Go
|
|
20901
|
-
{ name: "go", signals: ["go.sum"], command: "go mod download" },
|
|
20902
|
-
// Ruby
|
|
20903
|
-
{ name: "bundler", signals: ["Gemfile.lock"], command: "bundle install" },
|
|
20904
|
-
// PHP
|
|
20905
|
-
{ name: "composer", signals: ["composer.lock"], command: "composer install" },
|
|
20906
|
-
// Elixir / Erlang
|
|
20907
|
-
{ name: "mix", signals: ["mix.lock"], command: "mix deps.get" },
|
|
20908
|
-
{ name: "rebar3", signals: ["rebar.lock"], command: "rebar3 deps" },
|
|
20909
|
-
// Dart / Flutter
|
|
20910
|
-
{ name: "dart", signals: ["pubspec.lock"], command: "dart pub get" },
|
|
20911
|
-
// Java / Kotlin / Scala
|
|
20912
|
-
{ name: "maven", signals: ["pom.xml"], command: "mvn install" },
|
|
20913
|
-
{ name: "gradle", signals: ["gradlew"], command: "./gradlew build" },
|
|
20914
|
-
{
|
|
20915
|
-
name: "gradle",
|
|
20916
|
-
signals: ["build.gradle", "build.gradle.kts"],
|
|
20917
|
-
command: "gradle build"
|
|
20918
|
-
},
|
|
20919
|
-
{ name: "sbt", signals: ["build.sbt"], command: "sbt compile" },
|
|
20920
|
-
// .NET (C# / F# / VB)
|
|
20921
|
-
{
|
|
20922
|
-
name: "dotnet",
|
|
20923
|
-
signals: ["*.sln", "*.csproj", "*.fsproj", "*.vbproj"],
|
|
20924
|
-
command: "dotnet restore",
|
|
20925
|
-
glob: true
|
|
20926
|
-
},
|
|
20927
|
-
// Swift
|
|
20928
|
-
{
|
|
20929
|
-
name: "swift",
|
|
20930
|
-
signals: ["Package.swift"],
|
|
20931
|
-
command: "swift package resolve"
|
|
20932
|
-
},
|
|
20933
|
-
// Haskell
|
|
20934
|
-
{ name: "stack", signals: ["stack.yaml"], command: "stack build" },
|
|
20935
|
-
{
|
|
20936
|
-
name: "cabal",
|
|
20937
|
-
signals: ["cabal.project"],
|
|
20938
|
-
command: "cabal install --only-dependencies"
|
|
20939
|
-
},
|
|
20940
|
-
{
|
|
20941
|
-
name: "cabal",
|
|
20942
|
-
signals: ["*.cabal"],
|
|
20943
|
-
command: "cabal install --only-dependencies",
|
|
20944
|
-
glob: true
|
|
20945
|
-
},
|
|
20946
|
-
// Clojure
|
|
20947
|
-
{ name: "clojure", signals: ["deps.edn"], command: "clojure -P" },
|
|
20948
|
-
{ name: "leiningen", signals: ["project.clj"], command: "lein deps" },
|
|
20949
|
-
// OCaml
|
|
20950
|
-
{ name: "dune", signals: ["dune-project"], command: "dune build" },
|
|
20951
|
-
// Julia
|
|
20952
|
-
{
|
|
20953
|
-
name: "julia",
|
|
20954
|
-
signals: ["Manifest.toml"],
|
|
20955
|
-
command: "julia --project -e 'using Pkg; Pkg.instantiate()'"
|
|
20956
|
-
},
|
|
20957
|
-
// Nim
|
|
20958
|
-
{
|
|
20959
|
-
name: "nimble",
|
|
20960
|
-
signals: ["*.nimble"],
|
|
20961
|
-
command: "nimble install",
|
|
20962
|
-
glob: true
|
|
20963
|
-
},
|
|
20964
|
-
// Crystal
|
|
20965
|
-
{ name: "shards", signals: ["shard.yml"], command: "shards install" },
|
|
20966
|
-
// Perl
|
|
20967
|
-
{ name: "cpanm", signals: ["cpanfile"], command: "cpanm --installdeps ." },
|
|
20968
|
-
// Zig
|
|
20969
|
-
{ name: "zig", signals: ["build.zig.zon"], command: "zig build" },
|
|
20970
|
-
// C / C++
|
|
20971
|
-
{ name: "vcpkg", signals: ["vcpkg.json"], command: "vcpkg install" },
|
|
20972
|
-
{
|
|
20973
|
-
name: "conan",
|
|
20974
|
-
signals: ["conanfile.py", "conanfile.txt"],
|
|
20975
|
-
command: "conan install ."
|
|
20976
|
-
},
|
|
20977
|
-
// Nix
|
|
20978
|
-
{ name: "nix", signals: ["flake.nix"], command: "nix develop" },
|
|
20979
|
-
{ name: "nix-shell", signals: ["shell.nix"], command: "nix-shell" },
|
|
20980
|
-
// Terraform / OpenTofu
|
|
20981
|
-
{
|
|
20982
|
-
name: "terraform",
|
|
20983
|
-
signals: ["terraform.lock.hcl"],
|
|
20984
|
-
command: "terraform init"
|
|
20985
|
-
}
|
|
20986
|
-
];
|
|
20987
|
-
async function detectPackageManager(repoRoot) {
|
|
20988
|
-
for (const entry of ENTRIES) {
|
|
20989
|
-
const matched = entry.glob ? await matchesGlob(repoRoot, entry.signals) : await matchesExact(repoRoot, entry.signals);
|
|
20990
|
-
if (matched) {
|
|
20991
|
-
return { name: entry.name, installCommand: entry.command };
|
|
20992
|
-
}
|
|
20993
|
-
}
|
|
20994
|
-
return null;
|
|
20995
|
-
}
|
|
20996
|
-
async function matchesExact(repoRoot, signals) {
|
|
20997
|
-
for (const signal of signals) {
|
|
20998
|
-
try {
|
|
20999
|
-
await access8(join15(repoRoot, signal));
|
|
21000
|
-
return true;
|
|
21001
|
-
} catch {
|
|
21002
|
-
}
|
|
21003
|
-
}
|
|
21004
|
-
return false;
|
|
21005
|
-
}
|
|
21006
|
-
async function matchesGlob(repoRoot, patterns) {
|
|
21007
|
-
let files;
|
|
21008
|
-
try {
|
|
21009
|
-
files = await readdir3(repoRoot);
|
|
21010
|
-
} catch {
|
|
21011
|
-
return false;
|
|
21012
|
-
}
|
|
21013
|
-
const regexes = patterns.map(patternToRegex);
|
|
21014
|
-
return files.some((file) => regexes.some((re) => re.test(file)));
|
|
21015
|
-
}
|
|
21016
|
-
function patternToRegex(pattern) {
|
|
21017
|
-
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, "[^/]*");
|
|
21018
|
-
return new RegExp(`^${escaped}$`);
|
|
21019
|
-
}
|
|
21020
|
-
|
|
21021
|
-
// src/install-prompt.ts
|
|
21022
|
-
async function maybeRunInstallPrompt(worktreePath, repoRoot, config, stderr, dependencies = {}, nonInteractive = false) {
|
|
21023
|
-
if (isHeadless() || nonInteractive) {
|
|
21024
|
-
return;
|
|
21025
|
-
}
|
|
21026
|
-
if (extractHooks(config)["after-create"]) {
|
|
21027
|
-
return;
|
|
21028
|
-
}
|
|
21029
|
-
if (config.skipInstallPrompt === true) {
|
|
21030
|
-
return;
|
|
21031
|
-
}
|
|
21032
|
-
const detect = dependencies.detectInstallPackageManager ?? detectPackageManager;
|
|
21033
|
-
const pm = await detect(worktreePath);
|
|
21034
|
-
if (!pm) {
|
|
21035
|
-
return;
|
|
21036
|
-
}
|
|
21037
|
-
const prompt = dependencies.promptForInstallChoice ?? defaultPromptForInstallChoice;
|
|
21038
|
-
const choice = await prompt(pm);
|
|
21039
|
-
if (!choice || choice === "no") {
|
|
21040
|
-
return;
|
|
21041
|
-
}
|
|
21042
|
-
if (choice === "yes" || choice === "always") {
|
|
21043
|
-
const runner = dependencies.runInstallCommand ?? runInstallCommand;
|
|
21044
|
-
try {
|
|
21045
|
-
await runner(pm.installCommand, worktreePath, stderr);
|
|
21046
|
-
} catch (error) {
|
|
21047
|
-
stderr(
|
|
21048
|
-
`gji: install command failed: ${error instanceof Error ? error.message : String(error)}
|
|
21049
|
-
`
|
|
21050
|
-
);
|
|
21051
|
-
}
|
|
21052
|
-
}
|
|
21053
|
-
const saveGlobal = config.installSaveTarget === "global";
|
|
21054
|
-
const writeKey = dependencies.writeConfigKey ?? defaultWriteConfigKey;
|
|
21055
|
-
const writeGlobalKey = dependencies.writeGlobalRepoConfigKey ?? defaultWriteGlobalRepoConfigKey;
|
|
21056
|
-
if (choice === "always") {
|
|
21057
|
-
try {
|
|
21058
|
-
if (saveGlobal) {
|
|
21059
|
-
const existingRaw = await loadExistingGlobalRepoHooks(repoRoot);
|
|
21060
|
-
const existing = extractHooks({ hooks: existingRaw });
|
|
21061
|
-
await writeGlobalKey(repoRoot, "hooks", {
|
|
21062
|
-
...existing["after-enter"] !== void 0 && {
|
|
21063
|
-
"after-enter": existing["after-enter"]
|
|
21064
|
-
},
|
|
21065
|
-
...existing["before-remove"] !== void 0 && {
|
|
21066
|
-
"before-remove": existing["before-remove"]
|
|
21067
|
-
},
|
|
21068
|
-
"after-create": pm.installCommand
|
|
21069
|
-
});
|
|
21070
|
-
} else {
|
|
21071
|
-
const { config: localConfig } = await loadConfig(repoRoot);
|
|
21072
|
-
const existing = extractHooks(localConfig);
|
|
21073
|
-
await writeKey(repoRoot, "hooks", {
|
|
21074
|
-
...existing["after-enter"] !== void 0 && {
|
|
21075
|
-
"after-enter": existing["after-enter"]
|
|
21076
|
-
},
|
|
21077
|
-
...existing["before-remove"] !== void 0 && {
|
|
21078
|
-
"before-remove": existing["before-remove"]
|
|
21079
|
-
},
|
|
21080
|
-
"after-create": pm.installCommand
|
|
21081
|
-
});
|
|
21082
|
-
}
|
|
21083
|
-
} catch (error) {
|
|
21084
|
-
stderr(
|
|
21085
|
-
`gji: failed to save config: ${error instanceof Error ? error.message : String(error)}
|
|
21086
|
-
`
|
|
21087
|
-
);
|
|
21088
|
-
}
|
|
21089
|
-
}
|
|
21090
|
-
if (choice === "never") {
|
|
21091
|
-
try {
|
|
21092
|
-
if (saveGlobal) {
|
|
21093
|
-
await writeGlobalKey(repoRoot, "skipInstallPrompt", true);
|
|
21094
|
-
} else {
|
|
21095
|
-
await writeKey(repoRoot, "skipInstallPrompt", true);
|
|
21096
|
-
}
|
|
21097
|
-
} catch (error) {
|
|
21098
|
-
stderr(
|
|
21099
|
-
`gji: failed to save config: ${error instanceof Error ? error.message : String(error)}
|
|
21100
|
-
`
|
|
21101
|
-
);
|
|
21102
|
-
}
|
|
21103
|
-
}
|
|
21104
|
-
}
|
|
21105
|
-
var runInstallCommand = runCommand;
|
|
21106
|
-
async function defaultWriteConfigKey(root, key, value) {
|
|
21107
|
-
await updateLocalConfigKey(root, key, value);
|
|
21108
|
-
}
|
|
21109
|
-
async function defaultWriteGlobalRepoConfigKey(repoRoot, key, value) {
|
|
21110
|
-
await updateGlobalRepoConfigKey(repoRoot, key, value);
|
|
21111
|
-
}
|
|
21112
|
-
async function loadExistingGlobalRepoHooks(repoRoot) {
|
|
21113
|
-
const { config: globalConfig } = await loadGlobalConfig();
|
|
21114
|
-
const repos = isPlainObject2(globalConfig.repos) ? globalConfig.repos : {};
|
|
21115
|
-
const perRepo = isPlainObject2(repos[repoRoot]) ? repos[repoRoot] : {};
|
|
21116
|
-
return isPlainObject2(perRepo.hooks) ? perRepo.hooks : {};
|
|
21117
|
-
}
|
|
21118
|
-
async function defaultPromptForInstallChoice(pm) {
|
|
21119
|
-
const choice = await ve({
|
|
21120
|
-
message: `Run \`${pm.installCommand}\` in the new worktree?`,
|
|
21121
|
-
options: [
|
|
21122
|
-
{ value: "yes", label: "Yes", hint: "run once" },
|
|
21123
|
-
{ value: "no", label: "No", hint: "skip this time" },
|
|
21124
|
-
{ value: "always", label: "Always", hint: "save as after-create hook" },
|
|
21125
|
-
{
|
|
21126
|
-
value: "never",
|
|
21127
|
-
label: "Never",
|
|
21128
|
-
hint: "disable this prompt for this repo"
|
|
21129
|
-
}
|
|
21130
|
-
]
|
|
21131
|
-
});
|
|
21132
|
-
if (pD(choice)) {
|
|
21133
|
-
return null;
|
|
21134
|
-
}
|
|
21135
|
-
return choice;
|
|
21136
|
-
}
|
|
21137
|
-
function isPlainObject2(value) {
|
|
21138
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21139
|
-
}
|
|
21140
|
-
|
|
21141
|
-
// src/sync-directories.ts
|
|
21142
|
-
async function executeSyncDirectoryPlan(plan, options) {
|
|
21143
|
-
const outcomes = [];
|
|
21144
|
-
for (const entry of plan) {
|
|
21145
|
-
if (entry.destinationWarning) {
|
|
21146
|
-
recordSkipped(
|
|
21147
|
-
outcomes,
|
|
21148
|
-
options.reporter,
|
|
21149
|
-
entry.directory,
|
|
21150
|
-
entry.destinationWarning
|
|
21151
|
-
);
|
|
21152
|
-
continue;
|
|
21153
|
-
}
|
|
21154
|
-
const destinationState = await inspectDestination(
|
|
21155
|
-
entry.worktreePath,
|
|
21156
|
-
entry.destination
|
|
21157
|
-
);
|
|
21158
|
-
if (destinationState.kind === "exists") {
|
|
21159
|
-
recordSkipped(
|
|
21160
|
-
outcomes,
|
|
21161
|
-
options.reporter,
|
|
21162
|
-
entry.directory,
|
|
21163
|
-
"destination already exists"
|
|
21164
|
-
);
|
|
21165
|
-
continue;
|
|
21166
|
-
}
|
|
21167
|
-
if (destinationState.kind === "unsafe") {
|
|
21168
|
-
const reason = destinationState.reason;
|
|
21169
|
-
recordSkipped(outcomes, options.reporter, entry.directory, reason);
|
|
21170
|
-
continue;
|
|
21171
|
-
}
|
|
21172
|
-
if (entry.warning) {
|
|
21173
|
-
recordSkipped(outcomes, options.reporter, entry.directory, entry.warning);
|
|
21174
|
-
continue;
|
|
21175
|
-
}
|
|
21176
|
-
if (!entry.source) {
|
|
21177
|
-
recordSkipped(
|
|
21178
|
-
outcomes,
|
|
21179
|
-
options.reporter,
|
|
21180
|
-
entry.directory,
|
|
21181
|
-
"source does not exist"
|
|
21182
|
-
);
|
|
21183
|
-
continue;
|
|
21184
|
-
}
|
|
21185
|
-
const refreshedDestinationState = await inspectDestination(
|
|
21186
|
-
entry.worktreePath,
|
|
21187
|
-
entry.destination
|
|
21188
|
-
);
|
|
21189
|
-
if (refreshedDestinationState.kind !== "missing") {
|
|
21190
|
-
recordSkipped(
|
|
21191
|
-
outcomes,
|
|
21192
|
-
options.reporter,
|
|
21193
|
-
entry.directory,
|
|
21194
|
-
refreshedDestinationState.kind === "unsafe" ? refreshedDestinationState.reason : "destination already exists"
|
|
21195
|
-
);
|
|
21196
|
-
continue;
|
|
21197
|
-
}
|
|
21198
|
-
let result;
|
|
21199
|
-
try {
|
|
21200
|
-
const cloneOptions = {
|
|
21201
|
-
destinationRoot: entry.worktreePath,
|
|
21202
|
-
measureBytes: options.reporter.measureCloneSize
|
|
21203
|
-
};
|
|
21204
|
-
result = await options.cloneDirectory(
|
|
21205
|
-
entry.source,
|
|
21206
|
-
entry.destination,
|
|
21207
|
-
cloneOptions
|
|
21208
|
-
);
|
|
21209
|
-
} catch (error) {
|
|
21210
|
-
if (isCloneDestinationExistsError(error)) {
|
|
21211
|
-
recordSkipped(
|
|
21212
|
-
outcomes,
|
|
21213
|
-
options.reporter,
|
|
21214
|
-
entry.directory,
|
|
21215
|
-
"destination already exists"
|
|
21216
|
-
);
|
|
21217
|
-
continue;
|
|
21218
|
-
}
|
|
21219
|
-
if (isCloneInProgressError(error)) {
|
|
21220
|
-
const reason2 = "copy-on-write clone already in progress";
|
|
21221
|
-
recordSkipped(outcomes, options.reporter, entry.directory, reason2);
|
|
21222
|
-
continue;
|
|
21223
|
-
}
|
|
21224
|
-
const reason = toErrorMessage5(error);
|
|
21225
|
-
recordSkipped(outcomes, options.reporter, entry.directory, reason);
|
|
21226
|
-
continue;
|
|
21227
|
-
}
|
|
21228
|
-
const clonedDirectory = {
|
|
21229
|
-
bytes: result.bytes,
|
|
21230
|
-
dir: entry.directory,
|
|
21231
|
-
ms: result.ms
|
|
21232
|
-
};
|
|
21233
|
-
const outcome = { kind: "cloned", directory: clonedDirectory };
|
|
21234
|
-
outcomes.push(outcome);
|
|
21235
|
-
options.reporter.cloned(clonedDirectory);
|
|
21236
|
-
}
|
|
21237
|
-
return outcomes;
|
|
21238
|
-
}
|
|
21239
|
-
function recordSkipped(outcomes, reporter, dir, reason) {
|
|
21240
|
-
outcomes.push({ kind: "skipped", dir, reason });
|
|
21241
|
-
if (reporter.skipped) reporter.skipped({ dir, reason });
|
|
21242
|
-
else reporter.write(`syncDirs: ${reason}, skipped ${dir}
|
|
21243
|
-
`);
|
|
21244
|
-
}
|
|
21245
|
-
function toErrorMessage5(error) {
|
|
21246
|
-
return error instanceof Error ? error.message : String(error);
|
|
19579
|
+
function isPathInside2(root, candidate) {
|
|
19580
|
+
const distance = relative3(resolve10(root), resolve10(candidate));
|
|
19581
|
+
return distance === "" || !isAbsolute4(distance) && distance !== ".." && !distance.startsWith(`..${sep4}`);
|
|
21247
19582
|
}
|
|
21248
19583
|
|
|
21249
19584
|
// src/worktree-bootstrap.ts
|
|
21250
19585
|
async function bootstrapWorktree(options) {
|
|
21251
|
-
const dependencyMode = options.
|
|
19586
|
+
const dependencyMode = options.dependencyMode;
|
|
21252
19587
|
const dependencyPlan = await prepareDependencyBootstrap(dependencyMode, {
|
|
21253
19588
|
currentRoot: options.currentRoot,
|
|
21254
19589
|
detectionRoot: options.dependencyDetectionRoot,
|
|
@@ -21256,27 +19591,12 @@ async function bootstrapWorktree(options) {
|
|
|
21256
19591
|
cargoBuildCommand: options.config.dependencyBuildCommand,
|
|
21257
19592
|
worktreePath: options.worktreePath
|
|
21258
19593
|
});
|
|
21259
|
-
const syncPlan = await prepareSyncDirectoryPlan(
|
|
21260
|
-
options.repoRoot,
|
|
21261
|
-
options.worktreePath,
|
|
21262
|
-
options.config.syncDirs ?? []
|
|
21263
|
-
);
|
|
21264
|
-
const outcomes = await executeSyncDirectoryPlan(syncPlan, {
|
|
21265
|
-
cloneDirectory: options.cloneDirectory ?? cloneDir,
|
|
21266
|
-
reporter: options.reporter
|
|
21267
|
-
});
|
|
21268
|
-
const clonedDirs = outcomes.flatMap(
|
|
21269
|
-
(outcome) => outcome.kind === "cloned" ? [outcome.directory] : []
|
|
21270
|
-
);
|
|
21271
|
-
const skippedDirs = outcomes.flatMap(
|
|
21272
|
-
(outcome) => outcome.kind === "skipped" ? [{ dir: outcome.dir, reason: outcome.reason }] : []
|
|
21273
|
-
);
|
|
21274
19594
|
const syncFileFailures = [];
|
|
21275
19595
|
for (const pattern of options.config.syncFiles ?? []) {
|
|
21276
19596
|
try {
|
|
21277
19597
|
await syncFiles(options.repoRoot, options.worktreePath, [pattern]);
|
|
21278
19598
|
} catch (error) {
|
|
21279
|
-
const message = `failed to sync file "${pattern}": ${
|
|
19599
|
+
const message = `failed to sync file "${pattern}": ${toErrorMessage2(error)}`;
|
|
21280
19600
|
if (!options.json) options.reporter.write(`Warning: ${message}
|
|
21281
19601
|
`);
|
|
21282
19602
|
syncFileFailures.push({
|
|
@@ -21293,34 +19613,18 @@ async function bootstrapWorktree(options) {
|
|
|
21293
19613
|
for (const event of syncFileFailures) options.reporter.dependency(event);
|
|
21294
19614
|
}
|
|
21295
19615
|
const dependencyBootstrap = syncFileFailures.length > 0 ? { mode: dependencyMode, ready: false, events: [] } : await executeDependencyBootstrap(dependencyPlan, {
|
|
21296
|
-
cloneDirectory: options.cloneDirectory,
|
|
21297
19616
|
reporter: options.reporter,
|
|
21298
19617
|
stderr: options.commandStderr ?? options.reporter.write,
|
|
21299
19618
|
stdout: options.commandStdout,
|
|
21300
|
-
seededDirectories: clonedDirs.map(({ dir }) => dir),
|
|
21301
19619
|
runCommand: options.runCommand
|
|
21302
19620
|
});
|
|
21303
19621
|
if (!dependencyBootstrap.ready) {
|
|
21304
19622
|
return {
|
|
21305
|
-
clonedDirs,
|
|
21306
19623
|
dependencyBootstrap,
|
|
21307
19624
|
ready: false,
|
|
21308
|
-
syncFileFailures
|
|
21309
|
-
skippedDirs
|
|
19625
|
+
syncFileFailures
|
|
21310
19626
|
};
|
|
21311
19627
|
}
|
|
21312
|
-
if (dependencyMode === "off" && ["default", "legacy"].includes(
|
|
21313
|
-
options.dependencyBootstrapPolicy?.source ?? "default"
|
|
21314
|
-
)) {
|
|
21315
|
-
await maybeRunInstallPrompt(
|
|
21316
|
-
options.worktreePath,
|
|
21317
|
-
options.repoRoot,
|
|
21318
|
-
options.config,
|
|
21319
|
-
options.reporter.write,
|
|
21320
|
-
options.installDependencies,
|
|
21321
|
-
options.nonInteractive
|
|
21322
|
-
);
|
|
21323
|
-
}
|
|
21324
19628
|
const hooks = extractHooks(options.config);
|
|
21325
19629
|
const slot = await assignWorktreeSlot(options.repoRoot, options.worktreePath);
|
|
21326
19630
|
await runHook(
|
|
@@ -21329,26 +19633,24 @@ async function bootstrapWorktree(options) {
|
|
|
21329
19633
|
{
|
|
21330
19634
|
branch: options.branch,
|
|
21331
19635
|
path: options.worktreePath,
|
|
21332
|
-
repo:
|
|
19636
|
+
repo: basename8(options.repoRoot),
|
|
21333
19637
|
slot
|
|
21334
19638
|
},
|
|
21335
19639
|
options.reporter.write,
|
|
21336
19640
|
options.json ? () => void 0 : options.commandStdout ?? ((chunk) => process.stdout.write(chunk))
|
|
21337
19641
|
);
|
|
21338
19642
|
return {
|
|
21339
|
-
clonedDirs,
|
|
21340
19643
|
dependencyBootstrap,
|
|
21341
19644
|
ready: true,
|
|
21342
|
-
syncFileFailures: []
|
|
21343
|
-
skippedDirs
|
|
19645
|
+
syncFileFailures: []
|
|
21344
19646
|
};
|
|
21345
19647
|
}
|
|
21346
|
-
function
|
|
19648
|
+
function toErrorMessage2(error) {
|
|
21347
19649
|
return error instanceof Error ? error.message : String(error);
|
|
21348
19650
|
}
|
|
21349
19651
|
|
|
21350
19652
|
// src/pr.ts
|
|
21351
|
-
var
|
|
19653
|
+
var execFileAsync6 = promisify7(execFile6);
|
|
21352
19654
|
var PR_OUTPUT_FILE_ENV = "GJI_PR_OUTPUT_FILE";
|
|
21353
19655
|
function parsePrInput(input) {
|
|
21354
19656
|
if (/^\d+$/.test(input)) return input;
|
|
@@ -21377,15 +19679,12 @@ function createPrCommand(dependencies = {}) {
|
|
|
21377
19679
|
}
|
|
21378
19680
|
const repository = await detectRepository(options.cwd);
|
|
21379
19681
|
let config;
|
|
21380
|
-
let dependencyBootstrapExplicit;
|
|
21381
19682
|
try {
|
|
21382
|
-
|
|
19683
|
+
config = await loadEffectiveConfig(
|
|
21383
19684
|
repository.repoRoot,
|
|
21384
19685
|
void 0,
|
|
21385
19686
|
options.json ? void 0 : options.stderr
|
|
21386
19687
|
);
|
|
21387
|
-
config = loaded.config;
|
|
21388
|
-
dependencyBootstrapExplicit = loaded.dependencyBootstrapExplicit;
|
|
21389
19688
|
} catch (error) {
|
|
21390
19689
|
const message = error instanceof Error ? error.message : String(error);
|
|
21391
19690
|
if (options.json) {
|
|
@@ -21436,47 +19735,16 @@ function createPrCommand(dependencies = {}) {
|
|
|
21436
19735
|
);
|
|
21437
19736
|
return 1;
|
|
21438
19737
|
}
|
|
21439
|
-
|
|
21440
|
-
|
|
21441
|
-
|
|
21442
|
-
|
|
21443
|
-
|
|
21444
|
-
|
|
21445
|
-
|
|
21446
|
-
|
|
21447
|
-
|
|
21448
|
-
|
|
21449
|
-
worktreePath
|
|
21450
|
-
},
|
|
21451
|
-
config,
|
|
21452
|
-
dependencyBootstrapExplicit,
|
|
21453
|
-
{
|
|
21454
|
-
dependencies,
|
|
21455
|
-
dryRun: true,
|
|
21456
|
-
legacyInstallPromptConfigured: dependencies.promptForInstallChoice !== void 0,
|
|
21457
|
-
nonInteractive: !!options.json,
|
|
21458
|
-
stderr: options.stderr
|
|
21459
|
-
}
|
|
21460
|
-
);
|
|
21461
|
-
config = {
|
|
21462
|
-
...config,
|
|
21463
|
-
dependencyBootstrap: dependencyPolicy.mode
|
|
21464
|
-
};
|
|
21465
|
-
}
|
|
21466
|
-
const dryRunSyncDirs = options.dryRun ? await estimateSyncDirectories(
|
|
21467
|
-
repository.repoRoot,
|
|
21468
|
-
worktreePath,
|
|
21469
|
-
config.syncDirs ?? []
|
|
21470
|
-
) : [];
|
|
21471
|
-
const dryRunDependencyBootstrap = options.dryRun ? await createDependencyBootstrapPreview(
|
|
21472
|
-
config.dependencyBootstrap ?? "off",
|
|
21473
|
-
{
|
|
21474
|
-
currentRoot: repository.currentRoot,
|
|
21475
|
-
repoRoot: repository.repoRoot,
|
|
21476
|
-
cargoBuildCommand: config.dependencyBuildCommand,
|
|
21477
|
-
worktreePath
|
|
21478
|
-
}
|
|
21479
|
-
) : void 0;
|
|
19738
|
+
const dependencyMode = resolveDependencyBootstrapMode(
|
|
19739
|
+
config.dependencyBootstrap,
|
|
19740
|
+
options.noInstall
|
|
19741
|
+
);
|
|
19742
|
+
const dryRunDependencyBootstrap = options.dryRun ? await createDependencyBootstrapPreview(dependencyMode, {
|
|
19743
|
+
currentRoot: repository.currentRoot,
|
|
19744
|
+
repoRoot: repository.repoRoot,
|
|
19745
|
+
cargoBuildCommand: config.dependencyBuildCommand,
|
|
19746
|
+
worktreePath
|
|
19747
|
+
}) : void 0;
|
|
21480
19748
|
if (options.dryRun) {
|
|
21481
19749
|
if (options.json) {
|
|
21482
19750
|
const output = {
|
|
@@ -21490,7 +19758,6 @@ function createPrCommand(dependencies = {}) {
|
|
|
21490
19758
|
),
|
|
21491
19759
|
dryRun: true
|
|
21492
19760
|
};
|
|
21493
|
-
if (dryRunSyncDirs.length > 0) output.syncDirs = dryRunSyncDirs;
|
|
21494
19761
|
if (dryRunDependencyBootstrap?.targets.length)
|
|
21495
19762
|
output.dependencyBootstrap = dryRunDependencyBootstrap;
|
|
21496
19763
|
options.stdout(`${JSON.stringify(output, null, 2)}
|
|
@@ -21498,8 +19765,7 @@ function createPrCommand(dependencies = {}) {
|
|
|
21498
19765
|
} else {
|
|
21499
19766
|
options.stdout(
|
|
21500
19767
|
`Would create worktree at ${worktreePath} (branch: ${branchName})
|
|
21501
|
-
${
|
|
21502
|
-
`).join("")}${formatDependencyBootstrapPreview(dryRunDependencyBootstrap)}`
|
|
19768
|
+
${formatDependencyBootstrapPreview(dryRunDependencyBootstrap)}`
|
|
21503
19769
|
);
|
|
21504
19770
|
}
|
|
21505
19771
|
return 0;
|
|
@@ -21526,57 +19792,31 @@ ${dryRunSyncDirs.map(({ dir, bytes }) => `Would clone ${dir} (${formatBytes(byte
|
|
|
21526
19792
|
}
|
|
21527
19793
|
return 1;
|
|
21528
19794
|
}
|
|
21529
|
-
await
|
|
19795
|
+
await mkdir7(dirname10(worktreePath), { recursive: true });
|
|
21530
19796
|
const branchAlreadyExists = await localBranchExists(
|
|
21531
19797
|
repository.repoRoot,
|
|
21532
19798
|
branchName
|
|
21533
19799
|
);
|
|
21534
19800
|
const worktreeArgs = branchAlreadyExists ? ["worktree", "add", worktreePath, branchName] : ["worktree", "add", "-b", branchName, worktreePath, remoteRef];
|
|
21535
|
-
await
|
|
21536
|
-
if (!dependencyBootstrapExplicit) {
|
|
21537
|
-
dependencyPolicy = await resolveDependencyBootstrapPolicy(
|
|
21538
|
-
{
|
|
21539
|
-
currentRoot: repository.currentRoot,
|
|
21540
|
-
detectionRoot: worktreePath,
|
|
21541
|
-
repoRoot: repository.repoRoot,
|
|
21542
|
-
worktreePath
|
|
21543
|
-
},
|
|
21544
|
-
config,
|
|
21545
|
-
false,
|
|
21546
|
-
{
|
|
21547
|
-
dependencies,
|
|
21548
|
-
legacyInstallPromptConfigured: dependencies.promptForInstallChoice !== void 0,
|
|
21549
|
-
nonInteractive: !!options.json,
|
|
21550
|
-
stderr: options.stderr
|
|
21551
|
-
}
|
|
21552
|
-
);
|
|
21553
|
-
config = {
|
|
21554
|
-
...config,
|
|
21555
|
-
dependencyBootstrap: dependencyPolicy.mode
|
|
21556
|
-
};
|
|
21557
|
-
}
|
|
19801
|
+
await execFileAsync6("git", worktreeArgs, { cwd: repository.repoRoot });
|
|
21558
19802
|
const bootstrap = await bootstrapWorktree({
|
|
21559
19803
|
branch: branchName,
|
|
21560
|
-
cloneDirectory: dependencies.cloneDir,
|
|
21561
19804
|
config,
|
|
21562
19805
|
currentRoot: repository.currentRoot,
|
|
21563
19806
|
dependencyDetectionRoot: worktreePath,
|
|
21564
|
-
|
|
19807
|
+
dependencyMode,
|
|
21565
19808
|
repoRoot: repository.repoRoot,
|
|
21566
19809
|
reporter: createBootstrapReporter(options.stderr, !!options.json),
|
|
21567
|
-
runCommand: dependencies.
|
|
19810
|
+
runCommand: dependencies.runCommand,
|
|
21568
19811
|
commandStdout: options.json ? () => void 0 : options.stdout,
|
|
21569
19812
|
commandStderr: options.json ? () => void 0 : options.stderr,
|
|
21570
19813
|
json: options.json,
|
|
21571
|
-
worktreePath
|
|
21572
|
-
installDependencies: dependencies,
|
|
21573
|
-
dependencyBootstrapPolicy: dependencyPolicy
|
|
19814
|
+
worktreePath
|
|
21574
19815
|
});
|
|
21575
19816
|
if (!bootstrap.ready) {
|
|
21576
19817
|
const details = {
|
|
21577
19818
|
dependencyBootstrap: bootstrap.dependencyBootstrap,
|
|
21578
19819
|
path: worktreePath,
|
|
21579
|
-
skipped: bootstrap.skippedDirs,
|
|
21580
19820
|
syncFiles: bootstrap.syncFileFailures
|
|
21581
19821
|
};
|
|
21582
19822
|
if (options.json) {
|
|
@@ -21604,15 +19844,7 @@ ${dryRunSyncDirs.map(({ dir, bytes }) => `Would clone ${dir} (${formatBytes(byte
|
|
|
21604
19844
|
branchName
|
|
21605
19845
|
)
|
|
21606
19846
|
};
|
|
21607
|
-
if (bootstrap.
|
|
21608
|
-
output.cloned = bootstrap.clonedDirs.map(({ dir, ms }) => ({
|
|
21609
|
-
dir,
|
|
21610
|
-
ms
|
|
21611
|
-
}));
|
|
21612
|
-
}
|
|
21613
|
-
if (bootstrap.skippedDirs.length > 0)
|
|
21614
|
-
output.skipped = bootstrap.skippedDirs;
|
|
21615
|
-
if (bootstrap.dependencyBootstrap.mode !== "off")
|
|
19847
|
+
if (bootstrap.dependencyBootstrap.events.length > 0)
|
|
21616
19848
|
output.dependencyBootstrap = bootstrap.dependencyBootstrap;
|
|
21617
19849
|
options.stdout(`${JSON.stringify(output, null, 2)}
|
|
21618
19850
|
`);
|
|
@@ -21625,7 +19857,7 @@ ${dryRunSyncDirs.map(({ dir, bytes }) => `Would clone ${dir} (${formatBytes(byte
|
|
|
21625
19857
|
}
|
|
21626
19858
|
async function localBranchExists(repoRoot, branchName) {
|
|
21627
19859
|
try {
|
|
21628
|
-
await
|
|
19860
|
+
await execFileAsync6(
|
|
21629
19861
|
"git",
|
|
21630
19862
|
["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`],
|
|
21631
19863
|
{ cwd: repoRoot }
|
|
@@ -21639,7 +19871,7 @@ var runPrCommand = createPrCommand();
|
|
|
21639
19871
|
async function fetchPullRequestRef(repoRoot, input, prNumber, remoteRef) {
|
|
21640
19872
|
for (const sourceRef of listPullRequestSourceRefs(input, prNumber)) {
|
|
21641
19873
|
try {
|
|
21642
|
-
await
|
|
19874
|
+
await execFileAsync6(
|
|
21643
19875
|
"git",
|
|
21644
19876
|
["fetch", "origin", `${sourceRef}:${remoteRef}`],
|
|
21645
19877
|
{ cwd: repoRoot }
|
|
@@ -21942,15 +20174,14 @@ function normalizeRemoteUrl(value) {
|
|
|
21942
20174
|
}
|
|
21943
20175
|
|
|
21944
20176
|
// src/new.ts
|
|
21945
|
-
import { execFile as
|
|
21946
|
-
import { access as
|
|
21947
|
-
import { dirname as
|
|
21948
|
-
import { promisify as
|
|
21949
|
-
var
|
|
20177
|
+
import { execFile as execFile7 } from "node:child_process";
|
|
20178
|
+
import { access as access8, mkdir as mkdir8 } from "node:fs/promises";
|
|
20179
|
+
import { dirname as dirname11, resolve as resolve11 } from "node:path";
|
|
20180
|
+
import { promisify as promisify8 } from "node:util";
|
|
20181
|
+
var execFileAsync7 = promisify8(execFile7);
|
|
21950
20182
|
var NEW_OUTPUT_FILE_ENV = "GJI_NEW_OUTPUT_FILE";
|
|
21951
20183
|
function createNewCommand(dependencies = {}) {
|
|
21952
20184
|
const createBranchPlaceholder = dependencies.createBranchPlaceholder ?? generateBranchPlaceholder;
|
|
21953
|
-
const cloneDirectory = dependencies.cloneDir ?? cloneDir;
|
|
21954
20185
|
const promptForBranch = dependencies.promptForBranch ?? defaultPromptForBranch;
|
|
21955
20186
|
const promptForFetchFailure = dependencies.promptForFetchFailure ?? defaultPromptForFetchFailure;
|
|
21956
20187
|
const prompt = dependencies.promptForPathConflict ?? promptForPathConflict;
|
|
@@ -21971,15 +20202,12 @@ function createNewCommand(dependencies = {}) {
|
|
|
21971
20202
|
}
|
|
21972
20203
|
const repository = await detectRepository(options.cwd);
|
|
21973
20204
|
let config;
|
|
21974
|
-
let dependencyBootstrapExplicit;
|
|
21975
20205
|
try {
|
|
21976
|
-
|
|
20206
|
+
config = await loadEffectiveConfig(
|
|
21977
20207
|
repository.repoRoot,
|
|
21978
20208
|
void 0,
|
|
21979
20209
|
options.json ? void 0 : options.stderr
|
|
21980
20210
|
);
|
|
21981
|
-
config = loaded.config;
|
|
21982
|
-
dependencyBootstrapExplicit = loaded.dependencyBootstrapExplicit;
|
|
21983
20211
|
} catch (error) {
|
|
21984
20212
|
return emitNewError(
|
|
21985
20213
|
options,
|
|
@@ -22044,7 +20272,7 @@ function createNewCommand(dependencies = {}) {
|
|
|
22044
20272
|
if (options.force) {
|
|
22045
20273
|
if (!options.dryRun) {
|
|
22046
20274
|
try {
|
|
22047
|
-
await
|
|
20275
|
+
await execFileAsync7(
|
|
22048
20276
|
"git",
|
|
22049
20277
|
["worktree", "remove", "--force", worktreePath],
|
|
22050
20278
|
{ cwd: repository.repoRoot }
|
|
@@ -22065,7 +20293,7 @@ function createNewCommand(dependencies = {}) {
|
|
|
22065
20293
|
}
|
|
22066
20294
|
if (!options.detached) {
|
|
22067
20295
|
try {
|
|
22068
|
-
await
|
|
20296
|
+
await execFileAsync7("git", ["branch", "-D", worktreeName], {
|
|
22069
20297
|
cwd: repository.repoRoot
|
|
22070
20298
|
});
|
|
22071
20299
|
} catch {
|
|
@@ -22106,26 +20334,10 @@ function createNewCommand(dependencies = {}) {
|
|
|
22106
20334
|
return 1;
|
|
22107
20335
|
}
|
|
22108
20336
|
}
|
|
22109
|
-
const
|
|
22110
|
-
|
|
22111
|
-
|
|
22112
|
-
repoRoot: repository.repoRoot,
|
|
22113
|
-
worktreePath
|
|
22114
|
-
},
|
|
22115
|
-
config,
|
|
22116
|
-
dependencyBootstrapExplicit,
|
|
22117
|
-
{
|
|
22118
|
-
dependencies,
|
|
22119
|
-
dryRun: options.dryRun,
|
|
22120
|
-
legacyInstallPromptConfigured: dependencies.promptForInstallChoice !== void 0,
|
|
22121
|
-
nonInteractive: !!options.json,
|
|
22122
|
-
stderr: options.stderr
|
|
22123
|
-
}
|
|
20337
|
+
const dependencyMode = resolveDependencyBootstrapMode(
|
|
20338
|
+
config.dependencyBootstrap,
|
|
20339
|
+
options.noInstall
|
|
22124
20340
|
);
|
|
22125
|
-
config = {
|
|
22126
|
-
...config,
|
|
22127
|
-
dependencyBootstrap: dependencyPolicy.mode
|
|
22128
|
-
};
|
|
22129
20341
|
if (options.dryRun) {
|
|
22130
20342
|
if (options.take) {
|
|
22131
20343
|
const changedFiles = await listTakeFiles(options.cwd);
|
|
@@ -22156,13 +20368,8 @@ ${transferableFiles.map((file) => ` ${file}
|
|
|
22156
20368
|
}
|
|
22157
20369
|
return 0;
|
|
22158
20370
|
}
|
|
22159
|
-
const dryRunSyncDirs = await estimateSyncDirectories(
|
|
22160
|
-
repository.repoRoot,
|
|
22161
|
-
worktreePath,
|
|
22162
|
-
config.syncDirs ?? []
|
|
22163
|
-
);
|
|
22164
20371
|
const dryRunDependencyBootstrap = await createDependencyBootstrapPreview(
|
|
22165
|
-
|
|
20372
|
+
dependencyMode,
|
|
22166
20373
|
{
|
|
22167
20374
|
currentRoot: repository.currentRoot,
|
|
22168
20375
|
repoRoot: repository.repoRoot,
|
|
@@ -22182,7 +20389,6 @@ ${transferableFiles.map((file) => ` ${file}
|
|
|
22182
20389
|
),
|
|
22183
20390
|
dryRun: true
|
|
22184
20391
|
};
|
|
22185
|
-
if (dryRunSyncDirs.length > 0) output.syncDirs = dryRunSyncDirs;
|
|
22186
20392
|
if (dryRunDependencyBootstrap.targets.length > 0)
|
|
22187
20393
|
output.dependencyBootstrap = dryRunDependencyBootstrap;
|
|
22188
20394
|
options.stdout(`${JSON.stringify(output, null, 2)}
|
|
@@ -22192,13 +20398,12 @@ ${transferableFiles.map((file) => ` ${file}
|
|
|
22192
20398
|
const openNote = resolvedEditor ? `, then open in ${resolvedEditor}` : "";
|
|
22193
20399
|
options.stdout(
|
|
22194
20400
|
`Would create worktree at ${worktreePath} (branch: ${worktreeName}${openNote})
|
|
22195
|
-
${
|
|
22196
|
-
`).join("")}${formatDependencyBootstrapPreview(dryRunDependencyBootstrap)}`
|
|
20401
|
+
${formatDependencyBootstrapPreview(dryRunDependencyBootstrap)}`
|
|
22197
20402
|
);
|
|
22198
20403
|
}
|
|
22199
20404
|
return 0;
|
|
22200
20405
|
}
|
|
22201
|
-
await
|
|
20406
|
+
await mkdir8(dirname11(worktreePath), { recursive: true });
|
|
22202
20407
|
const startPoint = options.fromCurrent && !options.detached ? await resolveCurrentWorktreeHead(options.cwd) : void 0;
|
|
22203
20408
|
const takeStartPoint = options.take ? await resolveCurrentWorktreeHead(options.cwd) : void 0;
|
|
22204
20409
|
const changedTakeFiles = options.take ? await listTakeFiles(options.cwd) : [];
|
|
@@ -22269,7 +20474,7 @@ ${dryRunSyncDirs.map(({ dir, bytes }) => `Would clone ${dir} (${formatBytes(byte
|
|
|
22269
20474
|
...takeStartPoint ? [takeStartPoint] : startPoint ? [startPoint] : freshBaseRef ? [freshBaseRef] : []
|
|
22270
20475
|
];
|
|
22271
20476
|
try {
|
|
22272
|
-
await
|
|
20477
|
+
await execFileAsync7("git", gitArgs, { cwd: repository.repoRoot });
|
|
22273
20478
|
} catch (error) {
|
|
22274
20479
|
if (stashSha)
|
|
22275
20480
|
await restoreTakeStash(options.cwd, stashSha, options.stderr);
|
|
@@ -22286,7 +20491,7 @@ ${dryRunSyncDirs.map(({ dir, bytes }) => `Would clone ${dir} (${formatBytes(byte
|
|
|
22286
20491
|
options.stderr
|
|
22287
20492
|
)) {
|
|
22288
20493
|
try {
|
|
22289
|
-
await
|
|
20494
|
+
await execFileAsync7(
|
|
22290
20495
|
"git",
|
|
22291
20496
|
["worktree", "remove", "--force", worktreePath],
|
|
22292
20497
|
{ cwd: repository.repoRoot }
|
|
@@ -22310,25 +20515,22 @@ ${dryRunSyncDirs.map(({ dir, bytes }) => `Would clone ${dir} (${formatBytes(byte
|
|
|
22310
20515
|
}
|
|
22311
20516
|
const bootstrap = await bootstrapWorktree({
|
|
22312
20517
|
branch: worktreeName,
|
|
22313
|
-
cloneDirectory,
|
|
22314
20518
|
config,
|
|
22315
20519
|
currentRoot: repository.currentRoot,
|
|
22316
|
-
|
|
20520
|
+
dependencyDetectionRoot: worktreePath,
|
|
20521
|
+
dependencyMode,
|
|
22317
20522
|
repoRoot: repository.repoRoot,
|
|
22318
20523
|
reporter: createBootstrapReporter(options.stderr, !!options.json),
|
|
22319
|
-
runCommand: dependencies.
|
|
20524
|
+
runCommand: dependencies.runCommand,
|
|
22320
20525
|
commandStdout: options.json ? () => void 0 : options.stdout,
|
|
22321
20526
|
commandStderr: options.json ? () => void 0 : options.stderr,
|
|
22322
20527
|
json: options.json,
|
|
22323
|
-
worktreePath
|
|
22324
|
-
installDependencies: dependencies,
|
|
22325
|
-
dependencyBootstrapPolicy: dependencyPolicy
|
|
20528
|
+
worktreePath
|
|
22326
20529
|
});
|
|
22327
20530
|
if (!bootstrap.ready) {
|
|
22328
20531
|
return emitNewError(options, "worktree bootstrap failed", {
|
|
22329
20532
|
dependencyBootstrap: bootstrap.dependencyBootstrap,
|
|
22330
20533
|
path: worktreePath,
|
|
22331
|
-
skipped: bootstrap.skippedDirs,
|
|
22332
20534
|
syncFiles: bootstrap.syncFileFailures
|
|
22333
20535
|
});
|
|
22334
20536
|
}
|
|
@@ -22346,15 +20548,7 @@ ${dryRunSyncDirs.map(({ dir, bytes }) => `Would clone ${dir} (${formatBytes(byte
|
|
|
22346
20548
|
submodules: submoduleFiles
|
|
22347
20549
|
}
|
|
22348
20550
|
} : { ...navigation };
|
|
22349
|
-
if (bootstrap.
|
|
22350
|
-
output.cloned = bootstrap.clonedDirs.map(({ dir, ms }) => ({
|
|
22351
|
-
dir,
|
|
22352
|
-
ms
|
|
22353
|
-
}));
|
|
22354
|
-
}
|
|
22355
|
-
if (bootstrap.skippedDirs.length > 0)
|
|
22356
|
-
output.skipped = bootstrap.skippedDirs;
|
|
22357
|
-
if (bootstrap.dependencyBootstrap.mode !== "off")
|
|
20551
|
+
if (bootstrap.dependencyBootstrap.events.length > 0)
|
|
22358
20552
|
output.dependencyBootstrap = bootstrap.dependencyBootstrap;
|
|
22359
20553
|
options.stdout(`${JSON.stringify(output, null, 2)}
|
|
22360
20554
|
`);
|
|
@@ -22583,7 +20777,7 @@ async function defaultPromptForBranch(placeholder) {
|
|
|
22583
20777
|
}
|
|
22584
20778
|
async function localBranchExists2(repoRoot, branchName) {
|
|
22585
20779
|
try {
|
|
22586
|
-
await
|
|
20780
|
+
await execFileAsync7(
|
|
22587
20781
|
"git",
|
|
22588
20782
|
["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`],
|
|
22589
20783
|
{ cwd: repoRoot }
|
|
@@ -22594,18 +20788,18 @@ async function localBranchExists2(repoRoot, branchName) {
|
|
|
22594
20788
|
}
|
|
22595
20789
|
}
|
|
22596
20790
|
async function resolveCurrentWorktreeHead(cwd) {
|
|
22597
|
-
return
|
|
20791
|
+
return execFileAsync7("git", ["rev-parse", "--verify", "HEAD"], { cwd }).then(
|
|
22598
20792
|
({ stdout: stdout3 }) => stdout3.trim()
|
|
22599
20793
|
);
|
|
22600
20794
|
}
|
|
22601
20795
|
async function createTakeStash(cwd, worktreeName) {
|
|
22602
20796
|
const message = `gji-take: ${worktreeName}:${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
22603
|
-
await
|
|
20797
|
+
await execFileAsync7(
|
|
22604
20798
|
"git",
|
|
22605
20799
|
["stash", "push", "--include-untracked", "-m", message],
|
|
22606
20800
|
{ cwd }
|
|
22607
20801
|
);
|
|
22608
|
-
const { stdout: stdout3 } = await
|
|
20802
|
+
const { stdout: stdout3 } = await execFileAsync7(
|
|
22609
20803
|
"git",
|
|
22610
20804
|
["stash", "list", "--format=%H%x00%s"],
|
|
22611
20805
|
{ cwd }
|
|
@@ -22626,12 +20820,12 @@ async function detectInProgressGitState(cwd) {
|
|
|
22626
20820
|
];
|
|
22627
20821
|
for (const [marker, name] of markers) {
|
|
22628
20822
|
try {
|
|
22629
|
-
const { stdout: stdout3 } = await
|
|
20823
|
+
const { stdout: stdout3 } = await execFileAsync7(
|
|
22630
20824
|
"git",
|
|
22631
20825
|
["rev-parse", "--git-path", marker],
|
|
22632
20826
|
{ cwd }
|
|
22633
20827
|
);
|
|
22634
|
-
await
|
|
20828
|
+
await access8(resolve11(cwd, stdout3.trim()));
|
|
22635
20829
|
return name;
|
|
22636
20830
|
} catch {
|
|
22637
20831
|
}
|
|
@@ -22678,7 +20872,7 @@ async function openWorktree(worktreePath, editorCli, spawnFn, stderr) {
|
|
|
22678
20872
|
}
|
|
22679
20873
|
async function listTakeFiles(cwd) {
|
|
22680
20874
|
try {
|
|
22681
|
-
const { stdout: stdout3 } = await
|
|
20875
|
+
const { stdout: stdout3 } = await execFileAsync7(
|
|
22682
20876
|
"git",
|
|
22683
20877
|
["status", "--porcelain=v1"],
|
|
22684
20878
|
{ cwd }
|
|
@@ -22690,7 +20884,7 @@ async function listTakeFiles(cwd) {
|
|
|
22690
20884
|
}
|
|
22691
20885
|
async function listIgnoredFiles(cwd) {
|
|
22692
20886
|
try {
|
|
22693
|
-
const { stdout: stdout3 } = await
|
|
20887
|
+
const { stdout: stdout3 } = await execFileAsync7(
|
|
22694
20888
|
"git",
|
|
22695
20889
|
["status", "--porcelain=v1", "--ignored", "--untracked-files=all"],
|
|
22696
20890
|
{ cwd }
|
|
@@ -22703,7 +20897,7 @@ async function listIgnoredFiles(cwd) {
|
|
|
22703
20897
|
async function listSubmoduleFiles(cwd, changedFiles) {
|
|
22704
20898
|
if (changedFiles.length === 0) return [];
|
|
22705
20899
|
try {
|
|
22706
|
-
const { stdout: stdout3 } = await
|
|
20900
|
+
const { stdout: stdout3 } = await execFileAsync7("git", ["ls-files", "--stage"], {
|
|
22707
20901
|
cwd
|
|
22708
20902
|
});
|
|
22709
20903
|
const submodules = new Set(
|
|
@@ -22716,7 +20910,7 @@ async function listSubmoduleFiles(cwd, changedFiles) {
|
|
|
22716
20910
|
}
|
|
22717
20911
|
async function countUntrackedFiles(cwd) {
|
|
22718
20912
|
try {
|
|
22719
|
-
const { stdout: stdout3 } = await
|
|
20913
|
+
const { stdout: stdout3 } = await execFileAsync7(
|
|
22720
20914
|
"git",
|
|
22721
20915
|
["status", "--porcelain=v1", "--untracked-files=all"],
|
|
22722
20916
|
{ cwd }
|
|
@@ -22728,7 +20922,7 @@ async function countUntrackedFiles(cwd) {
|
|
|
22728
20922
|
}
|
|
22729
20923
|
async function applyTakeStash(worktreePath, sourcePath, stashSha, copy, stderr) {
|
|
22730
20924
|
try {
|
|
22731
|
-
await
|
|
20925
|
+
await execFileAsync7("git", ["stash", "apply", stashSha], {
|
|
22732
20926
|
cwd: worktreePath
|
|
22733
20927
|
});
|
|
22734
20928
|
} catch (error) {
|
|
@@ -22739,7 +20933,7 @@ async function applyTakeStash(worktreePath, sourcePath, stashSha, copy, stderr)
|
|
|
22739
20933
|
}
|
|
22740
20934
|
if (copy) {
|
|
22741
20935
|
try {
|
|
22742
|
-
await
|
|
20936
|
+
await execFileAsync7("git", ["stash", "apply", stashSha], {
|
|
22743
20937
|
cwd: sourcePath
|
|
22744
20938
|
});
|
|
22745
20939
|
} catch (error) {
|
|
@@ -22751,7 +20945,7 @@ async function applyTakeStash(worktreePath, sourcePath, stashSha, copy, stderr)
|
|
|
22751
20945
|
}
|
|
22752
20946
|
}
|
|
22753
20947
|
try {
|
|
22754
|
-
const { stdout: stdout3 } = await
|
|
20948
|
+
const { stdout: stdout3 } = await execFileAsync7(
|
|
22755
20949
|
"git",
|
|
22756
20950
|
["stash", "list", "--format=%H %gd"],
|
|
22757
20951
|
{ cwd: sourcePath }
|
|
@@ -22759,14 +20953,14 @@ async function applyTakeStash(worktreePath, sourcePath, stashSha, copy, stderr)
|
|
|
22759
20953
|
const line = stdout3.split("\n").find((candidate) => candidate.startsWith(stashSha));
|
|
22760
20954
|
const ref = line?.trim().split(/\s+/).at(1);
|
|
22761
20955
|
if (ref)
|
|
22762
|
-
await
|
|
20956
|
+
await execFileAsync7("git", ["stash", "drop", ref], { cwd: sourcePath });
|
|
22763
20957
|
} catch {
|
|
22764
20958
|
}
|
|
22765
20959
|
return true;
|
|
22766
20960
|
}
|
|
22767
20961
|
async function restoreTakeStash(sourcePath, stashSha, stderr) {
|
|
22768
20962
|
try {
|
|
22769
|
-
await
|
|
20963
|
+
await execFileAsync7("git", ["stash", "apply", stashSha], {
|
|
22770
20964
|
cwd: sourcePath
|
|
22771
20965
|
});
|
|
22772
20966
|
} catch {
|
|
@@ -23045,7 +21239,7 @@ async function navigateToExistingWorktree(options, path9, worktree) {
|
|
|
23045
21239
|
{
|
|
23046
21240
|
branch: worktree?.branch ?? void 0,
|
|
23047
21241
|
path: path9,
|
|
23048
|
-
repo:
|
|
21242
|
+
repo: basename9(repository.repoRoot),
|
|
23049
21243
|
slot: await getWorktreeSlot(path9)
|
|
23050
21244
|
},
|
|
23051
21245
|
options.stderr
|
|
@@ -23183,7 +21377,7 @@ async function runHistoryCommand(options) {
|
|
|
23183
21377
|
}
|
|
23184
21378
|
|
|
23185
21379
|
// src/hub.ts
|
|
23186
|
-
import { basename as
|
|
21380
|
+
import { basename as basename10 } from "node:path";
|
|
23187
21381
|
import { stdin as stdin2, stdout as stdout2 } from "node:process";
|
|
23188
21382
|
var MAX_HUB_REPOSITORY_CONCURRENCY = 4;
|
|
23189
21383
|
var defaultDependencies = {
|
|
@@ -23206,7 +21400,7 @@ async function buildHubData(cwd, dependencies = {}) {
|
|
|
23206
21400
|
existing.sources.push(source);
|
|
23207
21401
|
} else {
|
|
23208
21402
|
groups.set(repoRoot, {
|
|
23209
|
-
name: source.repoName ||
|
|
21403
|
+
name: source.repoName || basename10(repoRoot),
|
|
23210
21404
|
sources: [source]
|
|
23211
21405
|
});
|
|
23212
21406
|
}
|
|
@@ -23468,10 +21662,10 @@ async function mapWithConcurrency4(items, limit, mapper) {
|
|
|
23468
21662
|
}
|
|
23469
21663
|
|
|
23470
21664
|
// src/init.ts
|
|
23471
|
-
import { constants as
|
|
23472
|
-
import { access as
|
|
21665
|
+
import { constants as constants8 } from "node:fs";
|
|
21666
|
+
import { access as access9, mkdir as mkdir9, readFile as readFile9, writeFile as writeFile8 } from "node:fs/promises";
|
|
23473
21667
|
import { homedir as homedir8 } from "node:os";
|
|
23474
|
-
import { dirname as
|
|
21668
|
+
import { dirname as dirname12 } from "node:path";
|
|
23475
21669
|
var ZSH_COMPLETION_PATH_LINE = "fpath=(~/.zsh/completions $fpath)";
|
|
23476
21670
|
var SHELL_WRAPPED_COMMANDS = [
|
|
23477
21671
|
{
|
|
@@ -23595,8 +21789,8 @@ async function runDefaultOnboarding(context) {
|
|
|
23595
21789
|
const installCompletion = await promptForCompletion(shell, completionPath);
|
|
23596
21790
|
if (installCompletion === null) return abortDefaultOnboarding();
|
|
23597
21791
|
if (installCompletion) {
|
|
23598
|
-
await
|
|
23599
|
-
await
|
|
21792
|
+
await mkdir9(dirname12(completionPath), { recursive: true });
|
|
21793
|
+
await writeFile8(completionPath, renderShellCompletion(shell), "utf8");
|
|
23600
21794
|
}
|
|
23601
21795
|
if (shell === "zsh" && (completionAlreadyInstalled || installCompletion)) {
|
|
23602
21796
|
await ensureZshCompletionPath(rcPath);
|
|
@@ -23688,8 +21882,8 @@ async function applyOnboardingResult(result, home) {
|
|
|
23688
21882
|
const completionAlreadyInstalled = await fileExists(completionPath);
|
|
23689
21883
|
await applyShellIntegration(result.shell, result.shellIntegration, home);
|
|
23690
21884
|
if (result.installCompletion) {
|
|
23691
|
-
await
|
|
23692
|
-
await
|
|
21885
|
+
await mkdir9(dirname12(completionPath), { recursive: true });
|
|
21886
|
+
await writeFile8(
|
|
23693
21887
|
completionPath,
|
|
23694
21888
|
renderShellCompletion(result.shell),
|
|
23695
21889
|
"utf8"
|
|
@@ -23704,17 +21898,17 @@ async function applyOnboardingResult(result, home) {
|
|
|
23704
21898
|
}
|
|
23705
21899
|
async function installOnboardingShellIntegration(shell, home) {
|
|
23706
21900
|
const rcPath = resolveShellConfigPath(shell, home);
|
|
23707
|
-
await
|
|
21901
|
+
await mkdir9(dirname12(rcPath), { recursive: true });
|
|
23708
21902
|
const current = await readExistingConfig(rcPath);
|
|
23709
21903
|
if (shell === "fish") {
|
|
23710
|
-
await
|
|
21904
|
+
await writeFile8(
|
|
23711
21905
|
rcPath,
|
|
23712
21906
|
upsertShellIntegration(current, renderShellIntegration(shell)),
|
|
23713
21907
|
"utf8"
|
|
23714
21908
|
);
|
|
23715
21909
|
return;
|
|
23716
21910
|
}
|
|
23717
|
-
await
|
|
21911
|
+
await writeFile8(
|
|
23718
21912
|
rcPath,
|
|
23719
21913
|
upsertOnboardingShellIntegration(current, shell),
|
|
23720
21914
|
"utf8"
|
|
@@ -23748,7 +21942,7 @@ async function ensureZshCompletionPath(rcPath) {
|
|
|
23748
21942
|
const current = await readExistingConfig(rcPath);
|
|
23749
21943
|
const next = upsertZshCompletionPath(current);
|
|
23750
21944
|
if (next !== current) {
|
|
23751
|
-
await
|
|
21945
|
+
await writeFile8(rcPath, next, "utf8");
|
|
23752
21946
|
}
|
|
23753
21947
|
}
|
|
23754
21948
|
function upsertZshCompletionPath(existingConfig) {
|
|
@@ -23771,7 +21965,7 @@ ${suffix}`
|
|
|
23771
21965
|
}
|
|
23772
21966
|
async function fileExists(path9) {
|
|
23773
21967
|
try {
|
|
23774
|
-
await
|
|
21968
|
+
await access9(path9, constants8.F_OK);
|
|
23775
21969
|
return true;
|
|
23776
21970
|
} catch {
|
|
23777
21971
|
return false;
|
|
@@ -23792,10 +21986,10 @@ async function runLegacyInitCommand(options) {
|
|
|
23792
21986
|
return 0;
|
|
23793
21987
|
}
|
|
23794
21988
|
const rcPath = resolveShellConfigPath(shell, home);
|
|
23795
|
-
await
|
|
21989
|
+
await mkdir9(dirname12(rcPath), { recursive: true });
|
|
23796
21990
|
const current = await readExistingConfig(rcPath);
|
|
23797
21991
|
const next = upsertShellIntegration(current, script);
|
|
23798
|
-
await
|
|
21992
|
+
await writeFile8(rcPath, next, "utf8");
|
|
23799
21993
|
options.stdout(`${rcPath}
|
|
23800
21994
|
`);
|
|
23801
21995
|
const { config: globalConfig } = await loadGlobalConfig(home);
|
|
@@ -23886,7 +22080,7 @@ async function saveWizardConfig(result, cwd, home) {
|
|
|
23886
22080
|
}
|
|
23887
22081
|
async function readExistingConfig(path9) {
|
|
23888
22082
|
try {
|
|
23889
|
-
return await
|
|
22083
|
+
return await readFile9(path9, "utf8");
|
|
23890
22084
|
} catch (error) {
|
|
23891
22085
|
if (isMissingFileError3(error)) {
|
|
23892
22086
|
return "";
|
|
@@ -24135,11 +22329,11 @@ function sortWorktrees(worktrees) {
|
|
|
24135
22329
|
}
|
|
24136
22330
|
|
|
24137
22331
|
// src/open.ts
|
|
24138
|
-
import { execFile as
|
|
24139
|
-
import { access as
|
|
24140
|
-
import { join as
|
|
24141
|
-
import { promisify as
|
|
24142
|
-
var
|
|
22332
|
+
import { execFile as execFile8 } from "node:child_process";
|
|
22333
|
+
import { access as access10, writeFile as writeFile9 } from "node:fs/promises";
|
|
22334
|
+
import { join as join14 } from "node:path";
|
|
22335
|
+
import { promisify as promisify9 } from "node:util";
|
|
22336
|
+
var execFileAsync8 = promisify9(execFile8);
|
|
24143
22337
|
function createOpenCommand(dependencies = {}) {
|
|
24144
22338
|
const detectEditors = dependencies.detectEditors ?? detectInstalledEditors;
|
|
24145
22339
|
const promptForEditor2 = dependencies.promptForEditor ?? defaultPromptForEditor;
|
|
@@ -24286,7 +22480,7 @@ async function detectInstalledEditors() {
|
|
|
24286
22480
|
}
|
|
24287
22481
|
async function isCommandAvailable(command) {
|
|
24288
22482
|
try {
|
|
24289
|
-
await
|
|
22483
|
+
await execFileAsync8("which", [command]);
|
|
24290
22484
|
return true;
|
|
24291
22485
|
} catch {
|
|
24292
22486
|
return false;
|
|
@@ -24304,14 +22498,14 @@ async function defaultPromptForEditor(editors) {
|
|
|
24304
22498
|
return choice;
|
|
24305
22499
|
}
|
|
24306
22500
|
async function ensureWorkspaceFile(worktreePath, repoName) {
|
|
24307
|
-
const workspacePath =
|
|
22501
|
+
const workspacePath = join14(worktreePath, `${repoName}.code-workspace`);
|
|
24308
22502
|
try {
|
|
24309
|
-
await
|
|
22503
|
+
await access10(workspacePath);
|
|
24310
22504
|
return workspacePath;
|
|
24311
22505
|
} catch {
|
|
24312
22506
|
}
|
|
24313
22507
|
const workspace = { folders: [{ path: "." }], settings: {} };
|
|
24314
|
-
await
|
|
22508
|
+
await writeFile9(
|
|
24315
22509
|
workspacePath,
|
|
24316
22510
|
`${JSON.stringify(workspace, null, 2)}
|
|
24317
22511
|
`,
|
|
@@ -24321,10 +22515,10 @@ async function ensureWorkspaceFile(worktreePath, repoName) {
|
|
|
24321
22515
|
}
|
|
24322
22516
|
|
|
24323
22517
|
// src/browser.ts
|
|
24324
|
-
import { execFile as
|
|
22518
|
+
import { execFile as execFile9 } from "node:child_process";
|
|
24325
22519
|
import { platform as platform2 } from "node:process";
|
|
24326
|
-
import { promisify as
|
|
24327
|
-
var
|
|
22520
|
+
import { promisify as promisify10 } from "node:util";
|
|
22521
|
+
var execFileAsync9 = promisify10(execFile9);
|
|
24328
22522
|
function createBrowserOpener(dependencies = {}) {
|
|
24329
22523
|
const currentPlatform = dependencies.platform ?? platform2;
|
|
24330
22524
|
const runCommand2 = dependencies.runCommand ?? defaultRunCommand2;
|
|
@@ -24354,7 +22548,7 @@ function validateBrowserUrl(url) {
|
|
|
24354
22548
|
}
|
|
24355
22549
|
}
|
|
24356
22550
|
async function defaultRunCommand2(command, args) {
|
|
24357
|
-
await
|
|
22551
|
+
await execFileAsync9(command, args, { timeout: 1e4 });
|
|
24358
22552
|
}
|
|
24359
22553
|
|
|
24360
22554
|
// src/pr-open.ts
|
|
@@ -24719,7 +22913,7 @@ function formatError(error) {
|
|
|
24719
22913
|
}
|
|
24720
22914
|
|
|
24721
22915
|
// src/remove.ts
|
|
24722
|
-
import { basename as
|
|
22916
|
+
import { basename as basename11 } from "node:path";
|
|
24723
22917
|
var REMOVE_OUTPUT_FILE_ENV = "GJI_REMOVE_OUTPUT_FILE";
|
|
24724
22918
|
function createRemoveCommand(dependencies = {}) {
|
|
24725
22919
|
const promptForWorktree2 = dependencies.promptForWorktree ?? defaultPromptForWorktree3;
|
|
@@ -24825,7 +23019,7 @@ function createRemoveCommand(dependencies = {}) {
|
|
|
24825
23019
|
{
|
|
24826
23020
|
branch: worktree.branch ?? void 0,
|
|
24827
23021
|
path: worktree.path,
|
|
24828
|
-
repo:
|
|
23022
|
+
repo: basename11(repository.repoRoot)
|
|
24829
23023
|
},
|
|
24830
23024
|
options.stderr
|
|
24831
23025
|
);
|
|
@@ -25224,7 +23418,7 @@ function resolveConfiguredString2(value) {
|
|
|
25224
23418
|
|
|
25225
23419
|
// src/sync-files-command.ts
|
|
25226
23420
|
import { homedir as homedir9 } from "node:os";
|
|
25227
|
-
import { join as
|
|
23421
|
+
import { join as join15 } from "node:path";
|
|
25228
23422
|
async function runSyncFilesCommand(options) {
|
|
25229
23423
|
const repository = await detectRepository(options.cwd);
|
|
25230
23424
|
const home = options.home ?? homedir9();
|
|
@@ -25271,9 +23465,9 @@ async function runSyncFilesCommand(options) {
|
|
|
25271
23465
|
}
|
|
25272
23466
|
function findRepoConfigEntry(config, repoRoot, home) {
|
|
25273
23467
|
const repos = config.repos;
|
|
25274
|
-
if (!
|
|
23468
|
+
if (!isPlainObject2(repos)) return null;
|
|
25275
23469
|
for (const [key, value] of Object.entries(repos)) {
|
|
25276
|
-
if (expandTilde3(key, home) === repoRoot &&
|
|
23470
|
+
if (expandTilde3(key, home) === repoRoot && isPlainObject2(value)) {
|
|
25277
23471
|
return { config: value, key };
|
|
25278
23472
|
}
|
|
25279
23473
|
}
|
|
@@ -25330,8 +23524,8 @@ function removeSyncFiles(existing, removals) {
|
|
|
25330
23524
|
return existing.filter((path9) => !removalSet.has(path9));
|
|
25331
23525
|
}
|
|
25332
23526
|
async function saveRepoSyncFiles(config, repoKey, syncFiles2, home) {
|
|
25333
|
-
const repos =
|
|
25334
|
-
const repoConfig =
|
|
23527
|
+
const repos = isPlainObject2(config.repos) ? { ...config.repos } : {};
|
|
23528
|
+
const repoConfig = isPlainObject2(repos[repoKey]) ? repos[repoKey] : {};
|
|
25335
23529
|
const nextRepoConfig = { ...repoConfig };
|
|
25336
23530
|
if (syncFiles2.length > 0) {
|
|
25337
23531
|
nextRepoConfig.syncFiles = syncFiles2;
|
|
@@ -25345,7 +23539,7 @@ async function saveRepoSyncFiles(config, repoKey, syncFiles2, home) {
|
|
|
25345
23539
|
}
|
|
25346
23540
|
await saveGlobalConfig({ ...config, repos }, home);
|
|
25347
23541
|
}
|
|
25348
|
-
function
|
|
23542
|
+
function isPlainObject2(value) {
|
|
25349
23543
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25350
23544
|
}
|
|
25351
23545
|
function writeError(options, message) {
|
|
@@ -25359,7 +23553,7 @@ function writeError(options, message) {
|
|
|
25359
23553
|
}
|
|
25360
23554
|
function expandTilde3(value, home) {
|
|
25361
23555
|
if (value === "~") return home;
|
|
25362
|
-
if (value.startsWith("~/")) return
|
|
23556
|
+
if (value.startsWith("~/")) return join15(home, value.slice(2));
|
|
25363
23557
|
return value;
|
|
25364
23558
|
}
|
|
25365
23559
|
|
|
@@ -25624,9 +23818,7 @@ async function maybeRegisterCurrentRepo(cwd) {
|
|
|
25624
23818
|
}
|
|
25625
23819
|
}
|
|
25626
23820
|
function registerCommands(program2) {
|
|
25627
|
-
program2.command("new [branch]").description(
|
|
25628
|
-
"create a new branch or detached linked worktree and CoW-bootstrap configured directories"
|
|
25629
|
-
).option(
|
|
23821
|
+
program2.command("new [branch]").description("create a new branch or detached linked worktree").option(
|
|
25630
23822
|
"-f, --force",
|
|
25631
23823
|
"remove and recreate the worktree if the target path already exists"
|
|
25632
23824
|
).option("--detached", "create a detached worktree without a branch").option(
|
|
@@ -25644,6 +23836,9 @@ function registerCommands(program2) {
|
|
|
25644
23836
|
).option(
|
|
25645
23837
|
"--dry-run",
|
|
25646
23838
|
"show what would be created without executing any git commands or writing files"
|
|
23839
|
+
).option(
|
|
23840
|
+
"--no-install",
|
|
23841
|
+
"skip automatic dependency setup in the new worktree"
|
|
25647
23842
|
).option(
|
|
25648
23843
|
"--json",
|
|
25649
23844
|
"emit JSON on success or error instead of human-readable output"
|
|
@@ -25666,6 +23861,9 @@ function registerCommands(program2) {
|
|
|
25666
23861
|
).option(
|
|
25667
23862
|
"--dry-run",
|
|
25668
23863
|
"show what would be created without executing any git commands or writing files"
|
|
23864
|
+
).option(
|
|
23865
|
+
"--no-install",
|
|
23866
|
+
"skip automatic dependency setup in the PR worktree"
|
|
25669
23867
|
).option(
|
|
25670
23868
|
"--json",
|
|
25671
23869
|
"emit JSON on success or error instead of human-readable output"
|
|
@@ -25743,6 +23941,7 @@ function attachCommandActions(program2, options) {
|
|
|
25743
23941
|
force: commandOptions2.force,
|
|
25744
23942
|
noFetch: commandOptions2.fetch === false,
|
|
25745
23943
|
json: commandOptions2.json,
|
|
23944
|
+
noInstall: commandOptions2.install === false,
|
|
25746
23945
|
open: commandOptions2.open,
|
|
25747
23946
|
take: commandOptions2.take,
|
|
25748
23947
|
task: commandOptions2.task
|
|
@@ -25825,6 +24024,7 @@ function attachCommandActions(program2, options) {
|
|
|
25825
24024
|
cwd: options.cwd,
|
|
25826
24025
|
dryRun: commandOptions2.dryRun,
|
|
25827
24026
|
json: commandOptions2.json,
|
|
24027
|
+
noInstall: commandOptions2.install === false,
|
|
25828
24028
|
number,
|
|
25829
24029
|
stderr: options.stderr,
|
|
25830
24030
|
stdout: options.stdout
|