@solaqua/gji 0.12.2 → 0.12.4
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 +580 -2360
- 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 +232 -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,252 @@ 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, TextDecoder } from "node:util";
|
|
19021
18887
|
var execFileAsync5 = promisify6(execFile5);
|
|
19022
|
-
var
|
|
19023
|
-
var
|
|
19024
|
-
var
|
|
19025
|
-
var
|
|
19026
|
-
|
|
19027
|
-
|
|
19028
|
-
const
|
|
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
|
+
var UV_VALIDATION_TEXT_PROBE_BYTES = 4 * 1024;
|
|
18892
|
+
function virtualEnvironmentPrefixes(text) {
|
|
18893
|
+
const prefixes = [];
|
|
18894
|
+
for (const line of text.split(/\r?\n/u)) {
|
|
18895
|
+
if (!/\bVIRTUAL_ENV\b/u.test(line)) continue;
|
|
18896
|
+
const quotedAssignment = line.match(
|
|
18897
|
+
/\bVIRTUAL_ENV(?:\s*=|\s+)\s*(?:"([^"]+)"|'([^']+)')/u
|
|
19066
18898
|
);
|
|
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);
|
|
18899
|
+
const quotedPrefix = quotedAssignment?.[1] ?? quotedAssignment?.[2];
|
|
18900
|
+
if (quotedPrefix && isAbsolute3(quotedPrefix)) prefixes.push(quotedPrefix);
|
|
18901
|
+
const unquoted = line.match(/\bVIRTUAL_ENV(?:\s*=|\s+)\s*([^\s"']+)/u)?.[1];
|
|
18902
|
+
if (unquoted && isAbsolute3(unquoted)) prefixes.push(unquoted);
|
|
19121
18903
|
}
|
|
18904
|
+
return uniquePaths(prefixes);
|
|
19122
18905
|
}
|
|
19123
|
-
|
|
19124
|
-
|
|
19125
|
-
|
|
19126
|
-
|
|
19127
|
-
|
|
19128
|
-
|
|
19129
|
-
|
|
19130
|
-
|
|
19131
|
-
|
|
19132
|
-
|
|
18906
|
+
function pythonPrefixFromShebang(text) {
|
|
18907
|
+
const command = text.match(/^#!\s*([^\r\n]+)/u)?.[1]?.trim().split(/\s+/u)[0];
|
|
18908
|
+
if (!command || !isAbsolute3(command)) return void 0;
|
|
18909
|
+
if (!/^python(?:\d+(?:\.\d+)?)?(?:\.exe)?$/iu.test(
|
|
18910
|
+
command.split(sep2).at(-1) ?? ""
|
|
18911
|
+
)) {
|
|
18912
|
+
return void 0;
|
|
18913
|
+
}
|
|
18914
|
+
return dirname8(dirname8(command));
|
|
18915
|
+
}
|
|
18916
|
+
function pythonPrefixesFromText(text) {
|
|
18917
|
+
const interpreters = [];
|
|
18918
|
+
for (const match of text.matchAll(/["'](\/[^"']+)["']/gu)) {
|
|
18919
|
+
if (match[1]) interpreters.push(match[1]);
|
|
19133
18920
|
}
|
|
19134
|
-
const
|
|
19135
|
-
|
|
19136
|
-
throw new CloneUnsupportedError(`platform ${platform3} has no CoW strategy`);
|
|
18921
|
+
for (const match of text.matchAll(/(?:^|\s)(\/[^\s"']+)/gmu)) {
|
|
18922
|
+
if (match[1]) interpreters.push(match[1]);
|
|
19137
18923
|
}
|
|
19138
|
-
|
|
19139
|
-
|
|
19140
|
-
|
|
18924
|
+
return uniquePaths(
|
|
18925
|
+
interpreters.flatMap((interpreter) => {
|
|
18926
|
+
const name = interpreter.split(sep2).at(-1) ?? "";
|
|
18927
|
+
return /^python(?:\d+(?:\.\d+)?)?(?:\.exe)?$/iu.test(name) ? [dirname8(dirname8(interpreter))] : [];
|
|
18928
|
+
})
|
|
19141
18929
|
);
|
|
19142
18930
|
}
|
|
19143
|
-
async function
|
|
19144
|
-
|
|
19145
|
-
|
|
19146
|
-
|
|
19147
|
-
|
|
19148
|
-
|
|
19149
|
-
|
|
19150
|
-
|
|
19151
|
-
|
|
19152
|
-
|
|
19153
|
-
|
|
19154
|
-
|
|
18931
|
+
async function validateUvRelocation(target, context) {
|
|
18932
|
+
if (context.input === "preserve") return;
|
|
18933
|
+
if (!target.targetPath || !target.sourcePath) {
|
|
18934
|
+
throw new Error("uv environment target is not project-local");
|
|
18935
|
+
}
|
|
18936
|
+
const scriptsDirectory = join11(
|
|
18937
|
+
target.targetPath,
|
|
18938
|
+
process.platform === "win32" ? "Scripts" : "bin"
|
|
18939
|
+
);
|
|
18940
|
+
const interpreter = join11(
|
|
18941
|
+
scriptsDirectory,
|
|
18942
|
+
process.platform === "win32" ? "python.exe" : "python"
|
|
18943
|
+
);
|
|
18944
|
+
const { stdout: stdout3 } = await execFileAsync5(interpreter, [
|
|
18945
|
+
"-c",
|
|
18946
|
+
"import os, sys; print(os.path.realpath(sys.prefix))"
|
|
18947
|
+
]);
|
|
18948
|
+
const [actualPrefix, expectedPrefix] = await Promise.all([
|
|
18949
|
+
realpath2(stdout3.trim()),
|
|
18950
|
+
realpath2(target.targetPath)
|
|
18951
|
+
]);
|
|
18952
|
+
if (actualPrefix !== expectedPrefix) {
|
|
18953
|
+
throw new Error(
|
|
18954
|
+
`uv environment still points to its source prefix: ${actualPrefix}`
|
|
18955
|
+
);
|
|
18956
|
+
}
|
|
18957
|
+
const sourcePaths = uniquePaths([
|
|
18958
|
+
target.sourcePath,
|
|
18959
|
+
await realpath2(target.sourcePath).catch(() => void 0) ?? ""
|
|
18960
|
+
]).filter(Boolean);
|
|
18961
|
+
const entries = await readdir(scriptsDirectory, { withFileTypes: true });
|
|
18962
|
+
assertSafeUvScriptEntries(entries);
|
|
18963
|
+
const environmentName = basename6(target.targetPath);
|
|
18964
|
+
const acceptedPrefixes = /* @__PURE__ */ new Set([
|
|
18965
|
+
resolve8(target.targetPath),
|
|
18966
|
+
await realpath2(target.targetPath)
|
|
18967
|
+
]);
|
|
18968
|
+
let validatedBytes = 0;
|
|
18969
|
+
for (const path9 of [
|
|
18970
|
+
join11(target.targetPath, "pyvenv.cfg"),
|
|
18971
|
+
...entries.filter((entry) => entry.isFile()).map((entry) => join11(scriptsDirectory, entry.name))
|
|
18972
|
+
]) {
|
|
18973
|
+
const validated = await readBoundedUvTextFile(
|
|
18974
|
+
path9,
|
|
18975
|
+
UV_VALIDATION_MAX_TOTAL_BYTES - validatedBytes
|
|
18976
|
+
);
|
|
18977
|
+
if (!validated) continue;
|
|
18978
|
+
validatedBytes += validated.bytes;
|
|
18979
|
+
if (validated.text === void 0) continue;
|
|
18980
|
+
const { text } = validated;
|
|
18981
|
+
if (sourcePaths.some((sourcePath) => text.includes(sourcePath))) {
|
|
18982
|
+
throw new Error(`uv environment contains a stale source path: ${path9}`);
|
|
18983
|
+
}
|
|
18984
|
+
const shebangPrefix = pythonPrefixFromShebang(text);
|
|
18985
|
+
if (shebangPrefix && basename6(shebangPrefix) === environmentName && !acceptedPrefixes.has(resolve8(shebangPrefix))) {
|
|
18986
|
+
throw new Error(`uv launcher points outside its environment: ${path9}`);
|
|
18987
|
+
}
|
|
18988
|
+
if (pythonPrefixesFromText(text).filter((prefix) => basename6(prefix) === environmentName).some((prefix) => !acceptedPrefixes.has(resolve8(prefix)))) {
|
|
18989
|
+
throw new Error(`uv launcher points outside its environment: ${path9}`);
|
|
18990
|
+
}
|
|
18991
|
+
if (virtualEnvironmentPrefixes(text).some(
|
|
18992
|
+
(prefix) => !acceptedPrefixes.has(resolve8(prefix))
|
|
18993
|
+
)) {
|
|
18994
|
+
throw new Error(
|
|
18995
|
+
`uv activation script points outside its environment: ${path9}`
|
|
18996
|
+
);
|
|
19155
18997
|
}
|
|
19156
18998
|
}
|
|
19157
18999
|
}
|
|
19158
|
-
async function
|
|
19159
|
-
|
|
19160
|
-
|
|
19000
|
+
async function validateUvInstallation(target, context) {
|
|
19001
|
+
await validateUvStructure(target, context);
|
|
19002
|
+
await validateUvRelocation(target, context);
|
|
19161
19003
|
}
|
|
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";
|
|
19004
|
+
async function validateUvStructure(target, context) {
|
|
19005
|
+
if (context.input === "preserve") return;
|
|
19006
|
+
if (!target.targetPath)
|
|
19007
|
+
throw new Error("uv environment target is not project-local");
|
|
19008
|
+
const targetStats = await lstat3(target.targetPath).catch(() => void 0);
|
|
19009
|
+
if (!targetStats) return;
|
|
19010
|
+
if (!targetStats.isDirectory() || targetStats.isSymbolicLink()) {
|
|
19011
|
+
throw new Error("uv environment must be a real directory");
|
|
19174
19012
|
}
|
|
19175
|
-
|
|
19176
|
-
|
|
19177
|
-
|
|
19178
|
-
|
|
19179
|
-
|
|
19180
|
-
this.name = "CloneInProgressError";
|
|
19013
|
+
const configStats = await lstat3(join11(target.targetPath, "pyvenv.cfg")).catch(
|
|
19014
|
+
() => void 0
|
|
19015
|
+
);
|
|
19016
|
+
if (configStats && (!configStats.isFile() || configStats.isSymbolicLink())) {
|
|
19017
|
+
throw new Error("uv pyvenv.cfg must be a regular file");
|
|
19181
19018
|
}
|
|
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
|
-
}
|
|
19019
|
+
const scriptsDirectory = join11(
|
|
19020
|
+
target.targetPath,
|
|
19021
|
+
process.platform === "win32" ? "Scripts" : "bin"
|
|
19022
|
+
);
|
|
19023
|
+
const scriptsStats = await lstat3(scriptsDirectory).catch(() => void 0);
|
|
19024
|
+
if (!scriptsStats) return;
|
|
19025
|
+
if (!scriptsStats.isDirectory() || scriptsStats.isSymbolicLink()) {
|
|
19026
|
+
throw new Error("uv scripts path must be a real directory");
|
|
19213
19027
|
}
|
|
19214
|
-
|
|
19028
|
+
assertSafeUvScriptEntries(
|
|
19029
|
+
await readdir(scriptsDirectory, { withFileTypes: true })
|
|
19030
|
+
);
|
|
19215
19031
|
}
|
|
19216
|
-
function
|
|
19217
|
-
if (
|
|
19218
|
-
|
|
19219
|
-
"-a",
|
|
19220
|
-
"--reflink=always",
|
|
19221
|
-
source,
|
|
19222
|
-
destination
|
|
19223
|
-
];
|
|
19032
|
+
function assertSafeUvScriptEntries(entries) {
|
|
19033
|
+
if (entries.length > UV_VALIDATION_MAX_ENTRIES) {
|
|
19034
|
+
throw new Error("uv scripts directory exceeds the validation entry limit");
|
|
19224
19035
|
}
|
|
19225
|
-
|
|
19226
|
-
|
|
19227
|
-
|
|
19228
|
-
|
|
19229
|
-
}
|
|
19230
|
-
async function estimateCloneBytes(path9) {
|
|
19231
|
-
try {
|
|
19232
|
-
return await directorySize(path9);
|
|
19233
|
-
} catch {
|
|
19234
|
-
return void 0;
|
|
19036
|
+
for (const entry of entries) {
|
|
19037
|
+
if (entry.isSymbolicLink() && !isUvInterpreterName(entry.name)) {
|
|
19038
|
+
throw new Error(`uv script must not be a symbolic link: ${entry.name}`);
|
|
19039
|
+
}
|
|
19235
19040
|
}
|
|
19236
19041
|
}
|
|
19237
|
-
async function
|
|
19042
|
+
async function readBoundedUvTextFile(path9, remainingBytes) {
|
|
19043
|
+
let handle;
|
|
19238
19044
|
try {
|
|
19239
|
-
await
|
|
19045
|
+
handle = await open3(
|
|
19046
|
+
path9,
|
|
19047
|
+
constants5.O_RDONLY | (process.platform === "win32" ? 0 : constants5.O_NOFOLLOW)
|
|
19048
|
+
);
|
|
19240
19049
|
} catch (error) {
|
|
19241
|
-
if (
|
|
19242
|
-
throw new CloneUnsupportedError(toErrorMessage(error));
|
|
19243
|
-
}
|
|
19050
|
+
if (isNotFoundError(error)) return void 0;
|
|
19244
19051
|
throw error;
|
|
19245
19052
|
}
|
|
19246
|
-
}
|
|
19247
|
-
async function acquireCloneLock(lockPath, destination) {
|
|
19248
|
-
const releaseGuard = await acquireCloneLockGuard(lockPath, destination);
|
|
19249
19053
|
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;
|
|
19054
|
+
const stats = await handle.stat();
|
|
19055
|
+
if (!stats.isFile()) return void 0;
|
|
19056
|
+
const probeBytes = Math.min(UV_VALIDATION_TEXT_PROBE_BYTES, stats.size);
|
|
19057
|
+
if (probeBytes > remainingBytes) {
|
|
19058
|
+
throw new Error("uv launchers exceed the total validation size limit");
|
|
19266
19059
|
}
|
|
19267
|
-
|
|
19268
|
-
|
|
19060
|
+
const probe = await readFilePrefix(handle, probeBytes);
|
|
19061
|
+
if (!looksLikeUtf8Text(probe, probe.byteLength === stats.size)) {
|
|
19062
|
+
return { bytes: probe.byteLength };
|
|
19269
19063
|
}
|
|
19270
|
-
|
|
19271
|
-
|
|
19272
|
-
await rename3(lockPath, stalePath);
|
|
19273
|
-
} catch (error) {
|
|
19274
|
-
if (isNotFoundError(error)) continue;
|
|
19275
|
-
throw error;
|
|
19064
|
+
if (stats.size > UV_VALIDATION_MAX_FILE_BYTES) {
|
|
19065
|
+
throw new Error(`uv launcher exceeds the validation size limit: ${path9}`);
|
|
19276
19066
|
}
|
|
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;
|
|
19067
|
+
if (stats.size > remainingBytes) {
|
|
19068
|
+
throw new Error("uv launchers exceed the total validation size limit");
|
|
19294
19069
|
}
|
|
19295
|
-
|
|
19296
|
-
|
|
19297
|
-
|
|
19298
|
-
|
|
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);
|
|
19070
|
+
const readLimit = Math.min(UV_VALIDATION_MAX_FILE_BYTES, remainingBytes);
|
|
19071
|
+
const contents = await readFilePrefix(handle, readLimit + 1);
|
|
19072
|
+
if (contents.byteLength > UV_VALIDATION_MAX_FILE_BYTES) {
|
|
19073
|
+
throw new Error(`uv launcher exceeds the validation size limit: ${path9}`);
|
|
19500
19074
|
}
|
|
19501
|
-
if (
|
|
19502
|
-
throw new
|
|
19075
|
+
if (contents.byteLength > remainingBytes) {
|
|
19076
|
+
throw new Error("uv launchers exceed the total validation size limit");
|
|
19503
19077
|
}
|
|
19504
|
-
|
|
19078
|
+
const text = contents.toString("utf8");
|
|
19079
|
+
return looksLikeUtf8Text(contents, true) ? { bytes: contents.byteLength, text } : { bytes: contents.byteLength };
|
|
19080
|
+
} finally {
|
|
19081
|
+
await handle.close();
|
|
19505
19082
|
}
|
|
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
19083
|
}
|
|
19517
|
-
|
|
19518
|
-
|
|
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
|
-
);
|
|
19084
|
+
function looksLikeUtf8Text(contents, complete) {
|
|
19085
|
+
if (contents.includes(0)) return false;
|
|
19534
19086
|
try {
|
|
19535
|
-
|
|
19536
|
-
|
|
19087
|
+
new TextDecoder("utf-8", { fatal: true }).decode(contents, {
|
|
19088
|
+
stream: !complete
|
|
19537
19089
|
});
|
|
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
19090
|
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
19091
|
} catch {
|
|
19092
|
+
return false;
|
|
19584
19093
|
}
|
|
19585
19094
|
}
|
|
19586
|
-
async function
|
|
19587
|
-
const
|
|
19588
|
-
|
|
19589
|
-
|
|
19590
|
-
|
|
19591
|
-
|
|
19592
|
-
|
|
19593
|
-
|
|
19594
|
-
|
|
19595
|
-
|
|
19596
|
-
|
|
19597
|
-
|
|
19598
|
-
try {
|
|
19599
|
-
await unlink3(join11(lock.lockPath, lock.token));
|
|
19600
|
-
} catch (error) {
|
|
19601
|
-
if (!isNotFoundError(error)) throw error;
|
|
19602
|
-
}
|
|
19603
|
-
try {
|
|
19604
|
-
await rmdir(lock.lockPath);
|
|
19605
|
-
} catch (error) {
|
|
19606
|
-
const code = "code" in error ? error.code : void 0;
|
|
19607
|
-
if (!isNotFoundError(error) && code !== "ENOTEMPTY" && code !== "EEXIST") {
|
|
19608
|
-
throw error;
|
|
19609
|
-
}
|
|
19095
|
+
async function readFilePrefix(handle, maxBytes) {
|
|
19096
|
+
const contents = Buffer.allocUnsafe(maxBytes);
|
|
19097
|
+
let offset = 0;
|
|
19098
|
+
while (offset < contents.byteLength) {
|
|
19099
|
+
const { bytesRead } = await handle.read(
|
|
19100
|
+
contents,
|
|
19101
|
+
offset,
|
|
19102
|
+
contents.byteLength - offset,
|
|
19103
|
+
offset
|
|
19104
|
+
);
|
|
19105
|
+
if (bytesRead === 0) break;
|
|
19106
|
+
offset += bytesRead;
|
|
19610
19107
|
}
|
|
19108
|
+
return contents.subarray(0, offset);
|
|
19611
19109
|
}
|
|
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
|
-
);
|
|
19110
|
+
function isUvInterpreterName(name) {
|
|
19111
|
+
return /^python(?:\d+(?:\.\d+)?)?(?:\.exe)?$/iu.test(name);
|
|
19621
19112
|
}
|
|
19622
|
-
function
|
|
19623
|
-
return
|
|
19113
|
+
function uniquePaths(paths) {
|
|
19114
|
+
return [...new Set(paths.filter((path9) => Boolean(path9)))];
|
|
19624
19115
|
}
|
|
19625
19116
|
|
|
19626
19117
|
// src/dependency-bootstrap.ts
|
|
19627
|
-
|
|
19628
|
-
|
|
19629
|
-
|
|
19630
|
-
|
|
19118
|
+
function resolveDependencyBootstrapMode(dependencyBootstrap, noInstall = false) {
|
|
19119
|
+
if (noInstall || dependencyBootstrap === "off") return "off";
|
|
19120
|
+
return "install";
|
|
19121
|
+
}
|
|
19631
19122
|
async function prepareDependencyBootstrap(mode, context) {
|
|
19632
19123
|
if (mode === "off") return { mode, targets: [] };
|
|
19633
|
-
const adapters = createBootstrapAdapters(
|
|
19634
|
-
context.checkUvRuntime ?? defaultCheckUvRuntime,
|
|
19635
|
-
context.cargoBuildCommand
|
|
19636
|
-
);
|
|
19124
|
+
const adapters = createBootstrapAdapters(context.cargoBuildCommand);
|
|
19637
19125
|
const sourceRoots = [];
|
|
19638
|
-
for (const sourceRoot of
|
|
19126
|
+
for (const sourceRoot of uniquePaths2([
|
|
19639
19127
|
context.repoRoot,
|
|
19640
19128
|
context.currentRoot
|
|
19641
19129
|
])) {
|
|
@@ -19647,8 +19135,6 @@ async function prepareDependencyBootstrap(mode, context) {
|
|
|
19647
19135
|
const plannedRelativePaths = /* @__PURE__ */ new Set();
|
|
19648
19136
|
for (const adapter of adapters) {
|
|
19649
19137
|
if (plannedRelativePaths.has(adapter.relativePath)) continue;
|
|
19650
|
-
let firstDetected;
|
|
19651
|
-
let selected;
|
|
19652
19138
|
for (const sourceRoot of sourceRoots) {
|
|
19653
19139
|
const target = await adapter.detect({
|
|
19654
19140
|
detectionRoot: context.detectionRoot,
|
|
@@ -19656,82 +19142,37 @@ async function prepareDependencyBootstrap(mode, context) {
|
|
|
19656
19142
|
worktreePath: context.worktreePath
|
|
19657
19143
|
});
|
|
19658
19144
|
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);
|
|
19145
|
+
targets.push({ adapter, target });
|
|
19670
19146
|
plannedRelativePaths.add(adapter.relativePath);
|
|
19147
|
+
break;
|
|
19671
19148
|
}
|
|
19672
19149
|
}
|
|
19673
19150
|
return { mode, targets };
|
|
19674
19151
|
}
|
|
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
19152
|
function previewDependencyBootstrap(plan) {
|
|
19689
19153
|
return {
|
|
19690
19154
|
mode: plan.mode,
|
|
19691
|
-
targets: plan.targets.map(({
|
|
19692
|
-
adapter:
|
|
19693
|
-
kind:
|
|
19155
|
+
targets: plan.targets.map(({ adapter, target }) => ({
|
|
19156
|
+
adapter: adapter.name,
|
|
19157
|
+
kind: adapter.kind,
|
|
19694
19158
|
target: target.relativePath,
|
|
19695
|
-
|
|
19696
|
-
seedable,
|
|
19697
|
-
strategy: bootstrapStrategy(plan.mode, target, seedable)
|
|
19159
|
+
command: target.installCommand
|
|
19698
19160
|
}))
|
|
19699
19161
|
};
|
|
19700
19162
|
}
|
|
19701
19163
|
async function executeDependencyBootstrap(plan, options) {
|
|
19702
19164
|
if (plan.mode === "off") return { mode: plan.mode, ready: true, events: [] };
|
|
19703
19165
|
const events = [];
|
|
19704
|
-
const cloneDirectory = options.cloneDirectory ?? cloneDir;
|
|
19705
19166
|
const execution = {
|
|
19706
19167
|
runCommand: options.runCommand ?? runCommand,
|
|
19707
19168
|
stderr: options.stderr ?? (() => void 0),
|
|
19708
19169
|
stdout: options.stdout ?? ((chunk) => process.stdout.write(chunk))
|
|
19709
19170
|
};
|
|
19710
|
-
const seededDirectories = new Set(options.seededDirectories ?? []);
|
|
19711
19171
|
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
19172
|
return { mode: plan.mode, ready: true, events };
|
|
19721
19173
|
}
|
|
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
|
-
);
|
|
19174
|
+
for (const { adapter, target } of plan.targets) {
|
|
19175
|
+
await installTarget(adapter, target, execution, events, options.reporter);
|
|
19735
19176
|
}
|
|
19736
19177
|
return {
|
|
19737
19178
|
mode: plan.mode,
|
|
@@ -19739,263 +19180,43 @@ async function executeDependencyBootstrap(plan, options) {
|
|
|
19739
19180
|
events
|
|
19740
19181
|
};
|
|
19741
19182
|
}
|
|
19742
|
-
async function
|
|
19743
|
-
const
|
|
19744
|
-
|
|
19745
|
-
target.targetPath
|
|
19746
|
-
);
|
|
19747
|
-
if (destinationInspection.kind === "unsafe") {
|
|
19183
|
+
async function installTarget(adapter, target, execution, events, reporter) {
|
|
19184
|
+
const beforeInstallInspection = target.targetPath ? await inspectDestination(target.worktreePath, target.targetPath) : void 0;
|
|
19185
|
+
if (beforeInstallInspection?.kind === "unsafe") {
|
|
19748
19186
|
recordBootstrapFailure(
|
|
19749
19187
|
events,
|
|
19750
19188
|
reporter,
|
|
19751
19189
|
adapter,
|
|
19752
19190
|
target,
|
|
19753
|
-
|
|
19191
|
+
beforeInstallInspection.reason,
|
|
19754
19192
|
"destination-unsafe"
|
|
19755
19193
|
);
|
|
19756
19194
|
return;
|
|
19757
19195
|
}
|
|
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 {
|
|
19196
|
+
const effectiveInput = beforeInstallInspection?.kind === "exists" ? "preserve" : "clean";
|
|
19197
|
+
try {
|
|
19198
|
+
await adapter.install(target, { ...execution, input: effectiveInput });
|
|
19877
19199
|
recordBootstrapEvent(events, reporter, {
|
|
19878
19200
|
adapter: adapter.name,
|
|
19879
19201
|
kind: adapter.kind,
|
|
19880
|
-
|
|
19881
|
-
state: "repair-only",
|
|
19202
|
+
state: "installed",
|
|
19882
19203
|
target: target.relativePath,
|
|
19883
|
-
message:
|
|
19204
|
+
message: installSuccessMessage(target, effectiveInput)
|
|
19884
19205
|
});
|
|
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") {
|
|
19206
|
+
} catch (error) {
|
|
19894
19207
|
recordBootstrapFailure(
|
|
19895
19208
|
events,
|
|
19896
19209
|
reporter,
|
|
19897
19210
|
adapter,
|
|
19898
19211
|
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
|
|
19212
|
+
formatInstallFailure(error),
|
|
19213
|
+
"install-failed"
|
|
19956
19214
|
);
|
|
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
19215
|
}
|
|
19994
19216
|
}
|
|
19995
|
-
function
|
|
19996
|
-
if (input === "
|
|
19997
|
-
|
|
19998
|
-
return target.repairState === "installed" ? "installed from a clean target" : "repaired from a clean target";
|
|
19217
|
+
function installSuccessMessage(_target, input) {
|
|
19218
|
+
if (input === "preserve") return "installed into the existing target";
|
|
19219
|
+
return "installed into a clean target";
|
|
19999
19220
|
}
|
|
20000
19221
|
function recordBootstrapFailure(events, reporter, adapter, target, message, reason) {
|
|
20001
19222
|
recordBootstrapEvent(events, reporter, {
|
|
@@ -20011,169 +19232,156 @@ function recordBootstrapEvent(events, reporter, event) {
|
|
|
20011
19232
|
events.push(event);
|
|
20012
19233
|
reporter.dependency(event);
|
|
20013
19234
|
}
|
|
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";
|
|
20023
|
-
}
|
|
20024
|
-
function adapterForTarget(target) {
|
|
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;
|
|
19235
|
+
function formatInstallFailure(error) {
|
|
19236
|
+
return `install failed: ${toErrorMessage(error)}`;
|
|
20031
19237
|
}
|
|
20032
|
-
function createBootstrapAdapters(
|
|
19238
|
+
function createBootstrapAdapters(cargoBuildCommand) {
|
|
20033
19239
|
return [
|
|
20034
19240
|
new LockfileBootstrapAdapter({
|
|
20035
19241
|
name: "pnpm",
|
|
20036
19242
|
kind: "dependency",
|
|
20037
|
-
|
|
19243
|
+
lockfiles: ["pnpm-lock.yaml"],
|
|
20038
19244
|
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
|
-
}
|
|
19245
|
+
installCommand: "pnpm install --frozen-lockfile"
|
|
20048
19246
|
}),
|
|
20049
19247
|
new LockfileBootstrapAdapter({
|
|
20050
19248
|
name: "yarn",
|
|
20051
19249
|
kind: "dependency",
|
|
20052
|
-
|
|
19250
|
+
lockfiles: ["yarn.lock"],
|
|
20053
19251
|
relativePath: "node_modules",
|
|
20054
|
-
|
|
20055
|
-
|
|
20056
|
-
|
|
20057
|
-
|
|
20058
|
-
|
|
20059
|
-
|
|
20060
|
-
|
|
20061
|
-
|
|
20062
|
-
|
|
19252
|
+
installCommand: "yarn install --frozen-lockfile",
|
|
19253
|
+
selectInstallCommand: async (_target, _input) => await isYarnBerry(_target.detectionRoot ?? _target.sourceRoot) ? "yarn install --immutable" : "yarn install --frozen-lockfile"
|
|
19254
|
+
}),
|
|
19255
|
+
new LockfileBootstrapAdapter({
|
|
19256
|
+
name: "bun",
|
|
19257
|
+
kind: "dependency",
|
|
19258
|
+
lockfiles: ["bun.lock", "bun.lockb"],
|
|
19259
|
+
relativePath: "node_modules",
|
|
19260
|
+
installCommand: "bun install --frozen-lockfile"
|
|
20063
19261
|
}),
|
|
20064
19262
|
new LockfileBootstrapAdapter({
|
|
20065
19263
|
name: "npm",
|
|
20066
19264
|
kind: "dependency",
|
|
20067
|
-
|
|
19265
|
+
lockfiles: ["package-lock.json"],
|
|
20068
19266
|
relativePath: "node_modules",
|
|
20069
|
-
|
|
20070
|
-
|
|
20071
|
-
seedPolicy: "never",
|
|
20072
|
-
selectRepairCommand: (_target, input) => input === "preserve" ? "npm install" : "npm ci",
|
|
20073
|
-
repairState: "installed"
|
|
19267
|
+
installCommand: "npm ci",
|
|
19268
|
+
selectInstallCommand: (_target, input) => input === "preserve" ? "npm install" : "npm ci"
|
|
20074
19269
|
}),
|
|
20075
19270
|
new LockfileBootstrapAdapter({
|
|
20076
19271
|
name: "bundler",
|
|
20077
19272
|
kind: "dependency",
|
|
20078
|
-
|
|
19273
|
+
lockfiles: ["Gemfile.lock"],
|
|
20079
19274
|
relativePath: "vendor/bundle",
|
|
20080
|
-
|
|
20081
|
-
shell: false,
|
|
19275
|
+
installCommand: "bundle install",
|
|
20082
19276
|
commandOptions: () => ({
|
|
20083
19277
|
env: { BUNDLE_PATH: "vendor/bundle" }
|
|
20084
|
-
})
|
|
20085
|
-
|
|
20086
|
-
|
|
20087
|
-
|
|
20088
|
-
|
|
20089
|
-
|
|
19278
|
+
})
|
|
19279
|
+
}),
|
|
19280
|
+
new LockfileBootstrapAdapter({
|
|
19281
|
+
name: "poetry",
|
|
19282
|
+
kind: "dependency",
|
|
19283
|
+
lockfiles: ["poetry.lock"],
|
|
19284
|
+
relativePath: ".venv",
|
|
19285
|
+
installCommand: "poetry install --no-interaction",
|
|
19286
|
+
commandOptions: () => ({
|
|
19287
|
+
env: { POETRY_VIRTUALENVS_IN_PROJECT: "true" }
|
|
19288
|
+
})
|
|
20090
19289
|
}),
|
|
20091
19290
|
new LockfileBootstrapAdapter({
|
|
20092
19291
|
name: "uv",
|
|
20093
19292
|
kind: "dependency",
|
|
20094
|
-
|
|
19293
|
+
lockfiles: ["uv.lock"],
|
|
20095
19294
|
relativePath: ".venv",
|
|
20096
|
-
|
|
20097
|
-
|
|
20098
|
-
|
|
20099
|
-
|
|
20100
|
-
|
|
19295
|
+
installCommand: "uv sync --locked",
|
|
19296
|
+
beforeInstall: validateUvStructure,
|
|
19297
|
+
afterInstall: validateUvInstallation
|
|
19298
|
+
}),
|
|
19299
|
+
new LockfileBootstrapAdapter({
|
|
19300
|
+
name: "pipenv",
|
|
19301
|
+
kind: "dependency",
|
|
19302
|
+
lockfiles: ["Pipfile.lock"],
|
|
19303
|
+
relativePath: ".venv",
|
|
19304
|
+
installCommand: "pipenv sync",
|
|
19305
|
+
commandOptions: () => ({
|
|
19306
|
+
env: { PIPENV_VENV_IN_PROJECT: "1" }
|
|
19307
|
+
})
|
|
19308
|
+
}),
|
|
19309
|
+
new LockfileBootstrapAdapter({
|
|
19310
|
+
name: "go",
|
|
19311
|
+
kind: "dependency",
|
|
19312
|
+
lockfiles: ["go.mod"],
|
|
19313
|
+
relativePath: "",
|
|
19314
|
+
installCommand: "go mod download"
|
|
19315
|
+
}),
|
|
19316
|
+
new LockfileBootstrapAdapter({
|
|
19317
|
+
name: "composer",
|
|
19318
|
+
kind: "dependency",
|
|
19319
|
+
lockfiles: ["composer.lock"],
|
|
19320
|
+
relativePath: "vendor",
|
|
19321
|
+
installCommand: "composer install --no-interaction --prefer-dist"
|
|
20101
19322
|
}),
|
|
20102
19323
|
new LockfileBootstrapAdapter({
|
|
20103
19324
|
name: "cargo",
|
|
20104
19325
|
kind: "build-cache",
|
|
20105
|
-
|
|
19326
|
+
lockfiles: ["Cargo.lock"],
|
|
20106
19327
|
relativePath: "target",
|
|
20107
|
-
|
|
19328
|
+
installCommand: cargoBuildCommand?.trim() || "cargo check",
|
|
20108
19329
|
shell: Boolean(cargoBuildCommand)
|
|
20109
19330
|
})
|
|
20110
19331
|
];
|
|
20111
19332
|
}
|
|
20112
19333
|
var LockfileBootstrapAdapter = class {
|
|
20113
19334
|
kind;
|
|
20114
|
-
lockfile;
|
|
20115
19335
|
name;
|
|
20116
19336
|
relativePath;
|
|
20117
|
-
|
|
19337
|
+
defaultInstallCommand;
|
|
19338
|
+
lockfiles;
|
|
20118
19339
|
shell;
|
|
20119
|
-
|
|
20120
|
-
|
|
20121
|
-
beforeRepair;
|
|
20122
|
-
afterRepair;
|
|
19340
|
+
beforeInstall;
|
|
19341
|
+
afterInstall;
|
|
20123
19342
|
commandOptions;
|
|
20124
|
-
|
|
20125
|
-
repairState;
|
|
19343
|
+
selectInstallCommand;
|
|
20126
19344
|
constructor(spec) {
|
|
20127
19345
|
this.name = spec.name;
|
|
20128
19346
|
this.kind = spec.kind;
|
|
20129
|
-
this.
|
|
19347
|
+
this.lockfiles = spec.lockfiles;
|
|
20130
19348
|
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;
|
|
19349
|
+
this.defaultInstallCommand = spec.installCommand;
|
|
19350
|
+
this.shell = spec.shell ?? false;
|
|
19351
|
+
this.beforeInstall = spec.beforeInstall;
|
|
19352
|
+
this.afterInstall = spec.afterInstall;
|
|
20137
19353
|
this.commandOptions = spec.commandOptions;
|
|
20138
|
-
this.
|
|
20139
|
-
this.repairState = spec.repairState ?? "repaired";
|
|
19354
|
+
this.selectInstallCommand = spec.selectInstallCommand;
|
|
20140
19355
|
}
|
|
20141
19356
|
async detect(context) {
|
|
20142
19357
|
const detectionRoot = context.detectionRoot ?? context.sourceRoot;
|
|
20143
|
-
|
|
20144
|
-
|
|
20145
|
-
const
|
|
20146
|
-
const
|
|
20147
|
-
|
|
20148
|
-
|
|
20149
|
-
);
|
|
20150
|
-
const initialInput = destinationInspection.kind === "exists" ? "preserve" : "clean";
|
|
19358
|
+
const hasLockfile = await hasExistingPath(detectionRoot, this.lockfiles);
|
|
19359
|
+
if (!hasLockfile) return null;
|
|
19360
|
+
const sourcePath = this.relativePath ? join12(context.sourceRoot, this.relativePath) : void 0;
|
|
19361
|
+
const targetPath = this.relativePath ? join12(context.worktreePath, this.relativePath) : void 0;
|
|
19362
|
+
const destinationInspection = targetPath ? await inspectDestination(context.worktreePath, targetPath) : void 0;
|
|
19363
|
+
const initialInput = destinationInspection?.kind === "exists" ? "preserve" : "clean";
|
|
20151
19364
|
const target = {
|
|
20152
|
-
adapter: this.name,
|
|
20153
19365
|
kind: this.kind,
|
|
20154
19366
|
relativePath: this.relativePath,
|
|
20155
19367
|
sourceRoot: context.sourceRoot,
|
|
19368
|
+
detectionRoot,
|
|
20156
19369
|
worktreePath: context.worktreePath,
|
|
20157
19370
|
sourcePath,
|
|
20158
19371
|
targetPath,
|
|
20159
|
-
|
|
20160
|
-
|
|
20161
|
-
shell: this.shell,
|
|
20162
|
-
initialInput
|
|
19372
|
+
installCommand: this.defaultInstallCommand,
|
|
19373
|
+
shell: this.shell
|
|
20163
19374
|
};
|
|
20164
19375
|
return {
|
|
20165
19376
|
...target,
|
|
20166
|
-
|
|
19377
|
+
installCommand: await this.selectCommand(target, initialInput)
|
|
20167
19378
|
};
|
|
20168
19379
|
}
|
|
20169
|
-
|
|
20170
|
-
|
|
20171
|
-
|
|
20172
|
-
async repair(target, context) {
|
|
20173
|
-
await this.beforeRepair?.(target, context);
|
|
20174
|
-
const repairCommand = this.selectRepairCommand?.(target, context.input) ?? target.repairCommand;
|
|
19380
|
+
async install(target, context) {
|
|
19381
|
+
await this.beforeInstall?.(target, context);
|
|
19382
|
+
const installCommand = await this.selectCommand(target, context.input);
|
|
20175
19383
|
await context.runCommand(
|
|
20176
|
-
|
|
19384
|
+
installCommand,
|
|
20177
19385
|
target.worktreePath,
|
|
20178
19386
|
context.stderr,
|
|
20179
19387
|
context.stdout,
|
|
@@ -20182,277 +19390,32 @@ var LockfileBootstrapAdapter = class {
|
|
|
20182
19390
|
shell: target.shell
|
|
20183
19391
|
}
|
|
20184
19392
|
);
|
|
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;
|
|
20191
|
-
}
|
|
20192
|
-
shouldRetryAfterRepairFailure(context) {
|
|
20193
|
-
return context.input === "seed";
|
|
19393
|
+
await this.afterInstall?.(target, context);
|
|
20194
19394
|
}
|
|
20195
|
-
async
|
|
20196
|
-
|
|
20197
|
-
await rm4(context.target.targetPath, { force: true, recursive: true });
|
|
19395
|
+
async selectCommand(target, input) {
|
|
19396
|
+
return await this.selectInstallCommand?.(target, input) ?? target.installCommand;
|
|
20198
19397
|
}
|
|
20199
19398
|
};
|
|
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;
|
|
19399
|
+
async function hasExistingPath(root, paths) {
|
|
19400
|
+
for (const path9 of paths) {
|
|
19401
|
+
if (await pathExists2(join12(root, path9))) return true;
|
|
20210
19402
|
}
|
|
19403
|
+
return false;
|
|
20211
19404
|
}
|
|
20212
|
-
async function
|
|
19405
|
+
async function isYarnBerry(root) {
|
|
19406
|
+
if (await pathExists2(join12(root, ".yarnrc.yml"))) return true;
|
|
20213
19407
|
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"
|
|
19408
|
+
const packageJson3 = JSON.parse(
|
|
19409
|
+
await readFile8(join12(root, "package.json"), "utf8")
|
|
20224
19410
|
);
|
|
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);
|
|
19411
|
+
const packageManager = packageJson3.packageManager;
|
|
19412
|
+
const match = typeof packageManager === "string" && packageManager.match(/^yarn@(\d+)/u);
|
|
19413
|
+
return match ? Number(match[1]) >= 2 : false;
|
|
20233
19414
|
} catch {
|
|
20234
19415
|
return false;
|
|
20235
19416
|
}
|
|
20236
19417
|
}
|
|
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) {
|
|
19418
|
+
function uniquePaths2(paths) {
|
|
20456
19419
|
return [...new Set(paths.filter((path9) => Boolean(path9)))];
|
|
20457
19420
|
}
|
|
20458
19421
|
async function isAllowedSourceRoot(repoRoot, sourceRoot) {
|
|
@@ -20462,7 +19425,7 @@ async function isAllowedSourceRoot(repoRoot, sourceRoot) {
|
|
|
20462
19425
|
realpath3(sourceRoot)
|
|
20463
19426
|
]);
|
|
20464
19427
|
if (resolvedRepoRoot === resolvedSourceRoot) return true;
|
|
20465
|
-
const gitFile = await
|
|
19428
|
+
const gitFile = await readFile8(join12(resolvedSourceRoot, ".git"), "utf8");
|
|
20466
19429
|
const gitDirectory = gitFile.match(/^gitdir:\s*(.+)$/mu)?.[1];
|
|
20467
19430
|
if (!gitDirectory) return false;
|
|
20468
19431
|
return isWithin(
|
|
@@ -20474,10 +19437,10 @@ async function isAllowedSourceRoot(repoRoot, sourceRoot) {
|
|
|
20474
19437
|
}
|
|
20475
19438
|
}
|
|
20476
19439
|
function isWithin(root, candidate) {
|
|
20477
|
-
const distance =
|
|
19440
|
+
const distance = relative2(root, candidate);
|
|
20478
19441
|
return distance === "" || distance !== ".." && !distance.startsWith(`..${sep3}`);
|
|
20479
19442
|
}
|
|
20480
|
-
function
|
|
19443
|
+
function toErrorMessage(error) {
|
|
20481
19444
|
return error instanceof Error ? error.message : String(error);
|
|
20482
19445
|
}
|
|
20483
19446
|
|
|
@@ -20490,27 +19453,17 @@ async function createDependencyBootstrapPreview(mode, context) {
|
|
|
20490
19453
|
function formatDependencyBootstrapPreview(preview) {
|
|
20491
19454
|
if (!preview) return "";
|
|
20492
19455
|
return preview.targets.map(
|
|
20493
|
-
({ adapter, target,
|
|
19456
|
+
({ adapter, target, command }) => `Would install ${target || "dependencies"} with ${adapter}: ${command}
|
|
20494
19457
|
`
|
|
20495
19458
|
).join("");
|
|
20496
19459
|
}
|
|
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
19460
|
|
|
20508
19461
|
// src/conflict.ts
|
|
20509
|
-
import { constants as
|
|
19462
|
+
import { constants as constants6 } from "node:fs";
|
|
20510
19463
|
import { access as access7 } from "node:fs/promises";
|
|
20511
19464
|
async function pathExists3(path9) {
|
|
20512
19465
|
try {
|
|
20513
|
-
await access7(path9,
|
|
19466
|
+
await access7(path9, constants6.F_OK);
|
|
20514
19467
|
return true;
|
|
20515
19468
|
} catch {
|
|
20516
19469
|
return false;
|
|
@@ -20525,257 +19478,41 @@ async function promptForPathConflict(path9) {
|
|
|
20525
19478
|
label: "Abort",
|
|
20526
19479
|
hint: "Keep the existing directory untouched"
|
|
20527
19480
|
},
|
|
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
|
-
};
|
|
19481
|
+
{
|
|
19482
|
+
value: "reuse",
|
|
19483
|
+
label: "Reuse path",
|
|
19484
|
+
hint: "Print the existing path and stop"
|
|
19485
|
+
}
|
|
19486
|
+
]
|
|
19487
|
+
});
|
|
19488
|
+
if (pD(choice)) {
|
|
19489
|
+
return "abort";
|
|
20736
19490
|
}
|
|
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);
|
|
19491
|
+
return choice;
|
|
20755
19492
|
}
|
|
20756
19493
|
|
|
20757
19494
|
// src/worktree-bootstrap.ts
|
|
20758
|
-
import { basename as
|
|
19495
|
+
import { basename as basename8 } from "node:path";
|
|
20759
19496
|
|
|
20760
19497
|
// src/file-sync.ts
|
|
20761
|
-
import { constants as
|
|
20762
|
-
import { copyFile, lstat as
|
|
19498
|
+
import { constants as constants7 } from "node:fs";
|
|
19499
|
+
import { copyFile, lstat as lstat4, realpath as realpath4 } from "node:fs/promises";
|
|
20763
19500
|
import {
|
|
20764
|
-
basename as
|
|
20765
|
-
dirname as
|
|
20766
|
-
isAbsolute as
|
|
20767
|
-
join as
|
|
20768
|
-
normalize
|
|
20769
|
-
relative as
|
|
20770
|
-
resolve as
|
|
20771
|
-
sep as
|
|
19501
|
+
basename as basename7,
|
|
19502
|
+
dirname as dirname9,
|
|
19503
|
+
isAbsolute as isAbsolute4,
|
|
19504
|
+
join as join13,
|
|
19505
|
+
normalize,
|
|
19506
|
+
relative as relative3,
|
|
19507
|
+
resolve as resolve10,
|
|
19508
|
+
sep as sep4
|
|
20772
19509
|
} from "node:path";
|
|
20773
19510
|
async function syncFiles(mainRoot, targetPath, patterns) {
|
|
20774
19511
|
for (const pattern of patterns) {
|
|
20775
19512
|
const normalized = validateSyncFilePattern(pattern);
|
|
20776
19513
|
const sourcePath = await resolveSyncFileSource(mainRoot, normalized);
|
|
20777
19514
|
if (!sourcePath) continue;
|
|
20778
|
-
const destPath =
|
|
19515
|
+
const destPath = join13(targetPath, normalized);
|
|
20779
19516
|
const existingDestination = await readDestinationEntry(destPath);
|
|
20780
19517
|
if (existingDestination?.isSymbolicLink()) {
|
|
20781
19518
|
throw new Error(`destination is a symbolic link: ${destPath}`);
|
|
@@ -20783,7 +19520,7 @@ async function syncFiles(mainRoot, targetPath, patterns) {
|
|
|
20783
19520
|
if (existingDestination) {
|
|
20784
19521
|
continue;
|
|
20785
19522
|
}
|
|
20786
|
-
const destinationParent =
|
|
19523
|
+
const destinationParent = dirname9(destPath);
|
|
20787
19524
|
const beforeCreate = await inspectDestination(
|
|
20788
19525
|
targetPath,
|
|
20789
19526
|
destinationParent
|
|
@@ -20796,14 +19533,14 @@ async function syncFiles(mainRoot, targetPath, patterns) {
|
|
|
20796
19533
|
destinationParent
|
|
20797
19534
|
);
|
|
20798
19535
|
try {
|
|
20799
|
-
const safeDestination =
|
|
19536
|
+
const safeDestination = join13(safeParent.path, basename7(destPath));
|
|
20800
19537
|
const safeExistingDestination = await readDestinationEntry(safeDestination);
|
|
20801
19538
|
if (safeExistingDestination?.isSymbolicLink()) {
|
|
20802
19539
|
throw new Error(`destination is a symbolic link: ${destPath}`);
|
|
20803
19540
|
}
|
|
20804
19541
|
if (safeExistingDestination) continue;
|
|
20805
19542
|
try {
|
|
20806
|
-
await copyFile(sourcePath, safeDestination,
|
|
19543
|
+
await copyFile(sourcePath, safeDestination, constants7.COPYFILE_EXCL);
|
|
20807
19544
|
} catch (error) {
|
|
20808
19545
|
if (!isAlreadyExistsError(error)) throw error;
|
|
20809
19546
|
}
|
|
@@ -20813,12 +19550,12 @@ async function syncFiles(mainRoot, targetPath, patterns) {
|
|
|
20813
19550
|
}
|
|
20814
19551
|
}
|
|
20815
19552
|
function validateSyncFilePattern(pattern) {
|
|
20816
|
-
if (
|
|
19553
|
+
if (isAbsolute4(pattern)) {
|
|
20817
19554
|
throw new Error(
|
|
20818
19555
|
`syncFiles: pattern must be a relative path, got: ${pattern}`
|
|
20819
19556
|
);
|
|
20820
19557
|
}
|
|
20821
|
-
const normalized =
|
|
19558
|
+
const normalized = normalize(pattern);
|
|
20822
19559
|
if (normalized.startsWith("..")) {
|
|
20823
19560
|
throw new Error(
|
|
20824
19561
|
`syncFiles: pattern must not contain '..' segments, got: ${pattern}`
|
|
@@ -20831,424 +19568,42 @@ async function resolveSyncFileSource(mainRoot, pattern) {
|
|
|
20831
19568
|
let resolvedSource;
|
|
20832
19569
|
try {
|
|
20833
19570
|
[resolvedRoot, resolvedSource] = await Promise.all([
|
|
20834
|
-
|
|
20835
|
-
|
|
19571
|
+
realpath4(mainRoot),
|
|
19572
|
+
realpath4(join13(mainRoot, pattern))
|
|
20836
19573
|
]);
|
|
20837
19574
|
} catch (error) {
|
|
20838
19575
|
if (isNotFoundError(error)) return void 0;
|
|
20839
19576
|
throw error;
|
|
20840
19577
|
}
|
|
20841
|
-
if (!
|
|
19578
|
+
if (!isPathInside2(resolvedRoot, resolvedSource)) {
|
|
20842
19579
|
throw new Error(
|
|
20843
19580
|
`syncFiles: source symlink resolves outside the repository: ${resolvedSource}`
|
|
20844
19581
|
);
|
|
20845
19582
|
}
|
|
20846
|
-
const sourceStats = await
|
|
19583
|
+
const sourceStats = await lstat4(resolvedSource);
|
|
20847
19584
|
if (!sourceStats.isFile()) {
|
|
20848
19585
|
throw new Error(
|
|
20849
|
-
`syncFiles: source is not a file: ${
|
|
19586
|
+
`syncFiles: source is not a file: ${join13(mainRoot, pattern)}`
|
|
20850
19587
|
);
|
|
20851
19588
|
}
|
|
20852
19589
|
return resolvedSource;
|
|
20853
19590
|
}
|
|
20854
19591
|
async function readDestinationEntry(path9) {
|
|
20855
19592
|
try {
|
|
20856
|
-
return await
|
|
19593
|
+
return await lstat4(path9);
|
|
20857
19594
|
} catch (error) {
|
|
20858
19595
|
if (isNotFoundError(error)) return void 0;
|
|
20859
19596
|
throw error;
|
|
20860
19597
|
}
|
|
20861
19598
|
}
|
|
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);
|
|
19599
|
+
function isPathInside2(root, candidate) {
|
|
19600
|
+
const distance = relative3(resolve10(root), resolve10(candidate));
|
|
19601
|
+
return distance === "" || !isAbsolute4(distance) && distance !== ".." && !distance.startsWith(`..${sep4}`);
|
|
21247
19602
|
}
|
|
21248
19603
|
|
|
21249
19604
|
// src/worktree-bootstrap.ts
|
|
21250
19605
|
async function bootstrapWorktree(options) {
|
|
21251
|
-
const dependencyMode = options.
|
|
19606
|
+
const dependencyMode = options.dependencyMode;
|
|
21252
19607
|
const dependencyPlan = await prepareDependencyBootstrap(dependencyMode, {
|
|
21253
19608
|
currentRoot: options.currentRoot,
|
|
21254
19609
|
detectionRoot: options.dependencyDetectionRoot,
|
|
@@ -21256,27 +19611,12 @@ async function bootstrapWorktree(options) {
|
|
|
21256
19611
|
cargoBuildCommand: options.config.dependencyBuildCommand,
|
|
21257
19612
|
worktreePath: options.worktreePath
|
|
21258
19613
|
});
|
|
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
19614
|
const syncFileFailures = [];
|
|
21275
19615
|
for (const pattern of options.config.syncFiles ?? []) {
|
|
21276
19616
|
try {
|
|
21277
19617
|
await syncFiles(options.repoRoot, options.worktreePath, [pattern]);
|
|
21278
19618
|
} catch (error) {
|
|
21279
|
-
const message = `failed to sync file "${pattern}": ${
|
|
19619
|
+
const message = `failed to sync file "${pattern}": ${toErrorMessage2(error)}`;
|
|
21280
19620
|
if (!options.json) options.reporter.write(`Warning: ${message}
|
|
21281
19621
|
`);
|
|
21282
19622
|
syncFileFailures.push({
|
|
@@ -21293,34 +19633,18 @@ async function bootstrapWorktree(options) {
|
|
|
21293
19633
|
for (const event of syncFileFailures) options.reporter.dependency(event);
|
|
21294
19634
|
}
|
|
21295
19635
|
const dependencyBootstrap = syncFileFailures.length > 0 ? { mode: dependencyMode, ready: false, events: [] } : await executeDependencyBootstrap(dependencyPlan, {
|
|
21296
|
-
cloneDirectory: options.cloneDirectory,
|
|
21297
19636
|
reporter: options.reporter,
|
|
21298
19637
|
stderr: options.commandStderr ?? options.reporter.write,
|
|
21299
19638
|
stdout: options.commandStdout,
|
|
21300
|
-
seededDirectories: clonedDirs.map(({ dir }) => dir),
|
|
21301
19639
|
runCommand: options.runCommand
|
|
21302
19640
|
});
|
|
21303
19641
|
if (!dependencyBootstrap.ready) {
|
|
21304
19642
|
return {
|
|
21305
|
-
clonedDirs,
|
|
21306
19643
|
dependencyBootstrap,
|
|
21307
19644
|
ready: false,
|
|
21308
|
-
syncFileFailures
|
|
21309
|
-
skippedDirs
|
|
19645
|
+
syncFileFailures
|
|
21310
19646
|
};
|
|
21311
19647
|
}
|
|
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
19648
|
const hooks = extractHooks(options.config);
|
|
21325
19649
|
const slot = await assignWorktreeSlot(options.repoRoot, options.worktreePath);
|
|
21326
19650
|
await runHook(
|
|
@@ -21329,26 +19653,24 @@ async function bootstrapWorktree(options) {
|
|
|
21329
19653
|
{
|
|
21330
19654
|
branch: options.branch,
|
|
21331
19655
|
path: options.worktreePath,
|
|
21332
|
-
repo:
|
|
19656
|
+
repo: basename8(options.repoRoot),
|
|
21333
19657
|
slot
|
|
21334
19658
|
},
|
|
21335
19659
|
options.reporter.write,
|
|
21336
19660
|
options.json ? () => void 0 : options.commandStdout ?? ((chunk) => process.stdout.write(chunk))
|
|
21337
19661
|
);
|
|
21338
19662
|
return {
|
|
21339
|
-
clonedDirs,
|
|
21340
19663
|
dependencyBootstrap,
|
|
21341
19664
|
ready: true,
|
|
21342
|
-
syncFileFailures: []
|
|
21343
|
-
skippedDirs
|
|
19665
|
+
syncFileFailures: []
|
|
21344
19666
|
};
|
|
21345
19667
|
}
|
|
21346
|
-
function
|
|
19668
|
+
function toErrorMessage2(error) {
|
|
21347
19669
|
return error instanceof Error ? error.message : String(error);
|
|
21348
19670
|
}
|
|
21349
19671
|
|
|
21350
19672
|
// src/pr.ts
|
|
21351
|
-
var
|
|
19673
|
+
var execFileAsync6 = promisify7(execFile6);
|
|
21352
19674
|
var PR_OUTPUT_FILE_ENV = "GJI_PR_OUTPUT_FILE";
|
|
21353
19675
|
function parsePrInput(input) {
|
|
21354
19676
|
if (/^\d+$/.test(input)) return input;
|
|
@@ -21377,15 +19699,12 @@ function createPrCommand(dependencies = {}) {
|
|
|
21377
19699
|
}
|
|
21378
19700
|
const repository = await detectRepository(options.cwd);
|
|
21379
19701
|
let config;
|
|
21380
|
-
let dependencyBootstrapExplicit;
|
|
21381
19702
|
try {
|
|
21382
|
-
|
|
19703
|
+
config = await loadEffectiveConfig(
|
|
21383
19704
|
repository.repoRoot,
|
|
21384
19705
|
void 0,
|
|
21385
19706
|
options.json ? void 0 : options.stderr
|
|
21386
19707
|
);
|
|
21387
|
-
config = loaded.config;
|
|
21388
|
-
dependencyBootstrapExplicit = loaded.dependencyBootstrapExplicit;
|
|
21389
19708
|
} catch (error) {
|
|
21390
19709
|
const message = error instanceof Error ? error.message : String(error);
|
|
21391
19710
|
if (options.json) {
|
|
@@ -21436,47 +19755,16 @@ function createPrCommand(dependencies = {}) {
|
|
|
21436
19755
|
);
|
|
21437
19756
|
return 1;
|
|
21438
19757
|
}
|
|
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;
|
|
19758
|
+
const dependencyMode = resolveDependencyBootstrapMode(
|
|
19759
|
+
config.dependencyBootstrap,
|
|
19760
|
+
options.noInstall
|
|
19761
|
+
);
|
|
19762
|
+
const dryRunDependencyBootstrap = options.dryRun ? await createDependencyBootstrapPreview(dependencyMode, {
|
|
19763
|
+
currentRoot: repository.currentRoot,
|
|
19764
|
+
repoRoot: repository.repoRoot,
|
|
19765
|
+
cargoBuildCommand: config.dependencyBuildCommand,
|
|
19766
|
+
worktreePath
|
|
19767
|
+
}) : void 0;
|
|
21480
19768
|
if (options.dryRun) {
|
|
21481
19769
|
if (options.json) {
|
|
21482
19770
|
const output = {
|
|
@@ -21490,7 +19778,6 @@ function createPrCommand(dependencies = {}) {
|
|
|
21490
19778
|
),
|
|
21491
19779
|
dryRun: true
|
|
21492
19780
|
};
|
|
21493
|
-
if (dryRunSyncDirs.length > 0) output.syncDirs = dryRunSyncDirs;
|
|
21494
19781
|
if (dryRunDependencyBootstrap?.targets.length)
|
|
21495
19782
|
output.dependencyBootstrap = dryRunDependencyBootstrap;
|
|
21496
19783
|
options.stdout(`${JSON.stringify(output, null, 2)}
|
|
@@ -21498,8 +19785,7 @@ function createPrCommand(dependencies = {}) {
|
|
|
21498
19785
|
} else {
|
|
21499
19786
|
options.stdout(
|
|
21500
19787
|
`Would create worktree at ${worktreePath} (branch: ${branchName})
|
|
21501
|
-
${
|
|
21502
|
-
`).join("")}${formatDependencyBootstrapPreview(dryRunDependencyBootstrap)}`
|
|
19788
|
+
${formatDependencyBootstrapPreview(dryRunDependencyBootstrap)}`
|
|
21503
19789
|
);
|
|
21504
19790
|
}
|
|
21505
19791
|
return 0;
|
|
@@ -21526,57 +19812,31 @@ ${dryRunSyncDirs.map(({ dir, bytes }) => `Would clone ${dir} (${formatBytes(byte
|
|
|
21526
19812
|
}
|
|
21527
19813
|
return 1;
|
|
21528
19814
|
}
|
|
21529
|
-
await
|
|
19815
|
+
await mkdir7(dirname10(worktreePath), { recursive: true });
|
|
21530
19816
|
const branchAlreadyExists = await localBranchExists(
|
|
21531
19817
|
repository.repoRoot,
|
|
21532
19818
|
branchName
|
|
21533
19819
|
);
|
|
21534
19820
|
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
|
-
}
|
|
19821
|
+
await execFileAsync6("git", worktreeArgs, { cwd: repository.repoRoot });
|
|
21558
19822
|
const bootstrap = await bootstrapWorktree({
|
|
21559
19823
|
branch: branchName,
|
|
21560
|
-
cloneDirectory: dependencies.cloneDir,
|
|
21561
19824
|
config,
|
|
21562
19825
|
currentRoot: repository.currentRoot,
|
|
21563
19826
|
dependencyDetectionRoot: worktreePath,
|
|
21564
|
-
|
|
19827
|
+
dependencyMode,
|
|
21565
19828
|
repoRoot: repository.repoRoot,
|
|
21566
19829
|
reporter: createBootstrapReporter(options.stderr, !!options.json),
|
|
21567
|
-
runCommand: dependencies.
|
|
19830
|
+
runCommand: dependencies.runCommand,
|
|
21568
19831
|
commandStdout: options.json ? () => void 0 : options.stdout,
|
|
21569
19832
|
commandStderr: options.json ? () => void 0 : options.stderr,
|
|
21570
19833
|
json: options.json,
|
|
21571
|
-
worktreePath
|
|
21572
|
-
installDependencies: dependencies,
|
|
21573
|
-
dependencyBootstrapPolicy: dependencyPolicy
|
|
19834
|
+
worktreePath
|
|
21574
19835
|
});
|
|
21575
19836
|
if (!bootstrap.ready) {
|
|
21576
19837
|
const details = {
|
|
21577
19838
|
dependencyBootstrap: bootstrap.dependencyBootstrap,
|
|
21578
19839
|
path: worktreePath,
|
|
21579
|
-
skipped: bootstrap.skippedDirs,
|
|
21580
19840
|
syncFiles: bootstrap.syncFileFailures
|
|
21581
19841
|
};
|
|
21582
19842
|
if (options.json) {
|
|
@@ -21604,15 +19864,7 @@ ${dryRunSyncDirs.map(({ dir, bytes }) => `Would clone ${dir} (${formatBytes(byte
|
|
|
21604
19864
|
branchName
|
|
21605
19865
|
)
|
|
21606
19866
|
};
|
|
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")
|
|
19867
|
+
if (bootstrap.dependencyBootstrap.events.length > 0)
|
|
21616
19868
|
output.dependencyBootstrap = bootstrap.dependencyBootstrap;
|
|
21617
19869
|
options.stdout(`${JSON.stringify(output, null, 2)}
|
|
21618
19870
|
`);
|
|
@@ -21625,7 +19877,7 @@ ${dryRunSyncDirs.map(({ dir, bytes }) => `Would clone ${dir} (${formatBytes(byte
|
|
|
21625
19877
|
}
|
|
21626
19878
|
async function localBranchExists(repoRoot, branchName) {
|
|
21627
19879
|
try {
|
|
21628
|
-
await
|
|
19880
|
+
await execFileAsync6(
|
|
21629
19881
|
"git",
|
|
21630
19882
|
["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`],
|
|
21631
19883
|
{ cwd: repoRoot }
|
|
@@ -21639,7 +19891,7 @@ var runPrCommand = createPrCommand();
|
|
|
21639
19891
|
async function fetchPullRequestRef(repoRoot, input, prNumber, remoteRef) {
|
|
21640
19892
|
for (const sourceRef of listPullRequestSourceRefs(input, prNumber)) {
|
|
21641
19893
|
try {
|
|
21642
|
-
await
|
|
19894
|
+
await execFileAsync6(
|
|
21643
19895
|
"git",
|
|
21644
19896
|
["fetch", "origin", `${sourceRef}:${remoteRef}`],
|
|
21645
19897
|
{ cwd: repoRoot }
|
|
@@ -21942,15 +20194,14 @@ function normalizeRemoteUrl(value) {
|
|
|
21942
20194
|
}
|
|
21943
20195
|
|
|
21944
20196
|
// src/new.ts
|
|
21945
|
-
import { execFile as
|
|
21946
|
-
import { access as
|
|
21947
|
-
import { dirname as
|
|
21948
|
-
import { promisify as
|
|
21949
|
-
var
|
|
20197
|
+
import { execFile as execFile7 } from "node:child_process";
|
|
20198
|
+
import { access as access8, mkdir as mkdir8 } from "node:fs/promises";
|
|
20199
|
+
import { dirname as dirname11, resolve as resolve11 } from "node:path";
|
|
20200
|
+
import { promisify as promisify8 } from "node:util";
|
|
20201
|
+
var execFileAsync7 = promisify8(execFile7);
|
|
21950
20202
|
var NEW_OUTPUT_FILE_ENV = "GJI_NEW_OUTPUT_FILE";
|
|
21951
20203
|
function createNewCommand(dependencies = {}) {
|
|
21952
20204
|
const createBranchPlaceholder = dependencies.createBranchPlaceholder ?? generateBranchPlaceholder;
|
|
21953
|
-
const cloneDirectory = dependencies.cloneDir ?? cloneDir;
|
|
21954
20205
|
const promptForBranch = dependencies.promptForBranch ?? defaultPromptForBranch;
|
|
21955
20206
|
const promptForFetchFailure = dependencies.promptForFetchFailure ?? defaultPromptForFetchFailure;
|
|
21956
20207
|
const prompt = dependencies.promptForPathConflict ?? promptForPathConflict;
|
|
@@ -21971,15 +20222,12 @@ function createNewCommand(dependencies = {}) {
|
|
|
21971
20222
|
}
|
|
21972
20223
|
const repository = await detectRepository(options.cwd);
|
|
21973
20224
|
let config;
|
|
21974
|
-
let dependencyBootstrapExplicit;
|
|
21975
20225
|
try {
|
|
21976
|
-
|
|
20226
|
+
config = await loadEffectiveConfig(
|
|
21977
20227
|
repository.repoRoot,
|
|
21978
20228
|
void 0,
|
|
21979
20229
|
options.json ? void 0 : options.stderr
|
|
21980
20230
|
);
|
|
21981
|
-
config = loaded.config;
|
|
21982
|
-
dependencyBootstrapExplicit = loaded.dependencyBootstrapExplicit;
|
|
21983
20231
|
} catch (error) {
|
|
21984
20232
|
return emitNewError(
|
|
21985
20233
|
options,
|
|
@@ -22044,7 +20292,7 @@ function createNewCommand(dependencies = {}) {
|
|
|
22044
20292
|
if (options.force) {
|
|
22045
20293
|
if (!options.dryRun) {
|
|
22046
20294
|
try {
|
|
22047
|
-
await
|
|
20295
|
+
await execFileAsync7(
|
|
22048
20296
|
"git",
|
|
22049
20297
|
["worktree", "remove", "--force", worktreePath],
|
|
22050
20298
|
{ cwd: repository.repoRoot }
|
|
@@ -22065,7 +20313,7 @@ function createNewCommand(dependencies = {}) {
|
|
|
22065
20313
|
}
|
|
22066
20314
|
if (!options.detached) {
|
|
22067
20315
|
try {
|
|
22068
|
-
await
|
|
20316
|
+
await execFileAsync7("git", ["branch", "-D", worktreeName], {
|
|
22069
20317
|
cwd: repository.repoRoot
|
|
22070
20318
|
});
|
|
22071
20319
|
} catch {
|
|
@@ -22106,26 +20354,10 @@ function createNewCommand(dependencies = {}) {
|
|
|
22106
20354
|
return 1;
|
|
22107
20355
|
}
|
|
22108
20356
|
}
|
|
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
|
-
}
|
|
20357
|
+
const dependencyMode = resolveDependencyBootstrapMode(
|
|
20358
|
+
config.dependencyBootstrap,
|
|
20359
|
+
options.noInstall
|
|
22124
20360
|
);
|
|
22125
|
-
config = {
|
|
22126
|
-
...config,
|
|
22127
|
-
dependencyBootstrap: dependencyPolicy.mode
|
|
22128
|
-
};
|
|
22129
20361
|
if (options.dryRun) {
|
|
22130
20362
|
if (options.take) {
|
|
22131
20363
|
const changedFiles = await listTakeFiles(options.cwd);
|
|
@@ -22156,13 +20388,8 @@ ${transferableFiles.map((file) => ` ${file}
|
|
|
22156
20388
|
}
|
|
22157
20389
|
return 0;
|
|
22158
20390
|
}
|
|
22159
|
-
const dryRunSyncDirs = await estimateSyncDirectories(
|
|
22160
|
-
repository.repoRoot,
|
|
22161
|
-
worktreePath,
|
|
22162
|
-
config.syncDirs ?? []
|
|
22163
|
-
);
|
|
22164
20391
|
const dryRunDependencyBootstrap = await createDependencyBootstrapPreview(
|
|
22165
|
-
|
|
20392
|
+
dependencyMode,
|
|
22166
20393
|
{
|
|
22167
20394
|
currentRoot: repository.currentRoot,
|
|
22168
20395
|
repoRoot: repository.repoRoot,
|
|
@@ -22182,7 +20409,6 @@ ${transferableFiles.map((file) => ` ${file}
|
|
|
22182
20409
|
),
|
|
22183
20410
|
dryRun: true
|
|
22184
20411
|
};
|
|
22185
|
-
if (dryRunSyncDirs.length > 0) output.syncDirs = dryRunSyncDirs;
|
|
22186
20412
|
if (dryRunDependencyBootstrap.targets.length > 0)
|
|
22187
20413
|
output.dependencyBootstrap = dryRunDependencyBootstrap;
|
|
22188
20414
|
options.stdout(`${JSON.stringify(output, null, 2)}
|
|
@@ -22192,13 +20418,12 @@ ${transferableFiles.map((file) => ` ${file}
|
|
|
22192
20418
|
const openNote = resolvedEditor ? `, then open in ${resolvedEditor}` : "";
|
|
22193
20419
|
options.stdout(
|
|
22194
20420
|
`Would create worktree at ${worktreePath} (branch: ${worktreeName}${openNote})
|
|
22195
|
-
${
|
|
22196
|
-
`).join("")}${formatDependencyBootstrapPreview(dryRunDependencyBootstrap)}`
|
|
20421
|
+
${formatDependencyBootstrapPreview(dryRunDependencyBootstrap)}`
|
|
22197
20422
|
);
|
|
22198
20423
|
}
|
|
22199
20424
|
return 0;
|
|
22200
20425
|
}
|
|
22201
|
-
await
|
|
20426
|
+
await mkdir8(dirname11(worktreePath), { recursive: true });
|
|
22202
20427
|
const startPoint = options.fromCurrent && !options.detached ? await resolveCurrentWorktreeHead(options.cwd) : void 0;
|
|
22203
20428
|
const takeStartPoint = options.take ? await resolveCurrentWorktreeHead(options.cwd) : void 0;
|
|
22204
20429
|
const changedTakeFiles = options.take ? await listTakeFiles(options.cwd) : [];
|
|
@@ -22269,7 +20494,7 @@ ${dryRunSyncDirs.map(({ dir, bytes }) => `Would clone ${dir} (${formatBytes(byte
|
|
|
22269
20494
|
...takeStartPoint ? [takeStartPoint] : startPoint ? [startPoint] : freshBaseRef ? [freshBaseRef] : []
|
|
22270
20495
|
];
|
|
22271
20496
|
try {
|
|
22272
|
-
await
|
|
20497
|
+
await execFileAsync7("git", gitArgs, { cwd: repository.repoRoot });
|
|
22273
20498
|
} catch (error) {
|
|
22274
20499
|
if (stashSha)
|
|
22275
20500
|
await restoreTakeStash(options.cwd, stashSha, options.stderr);
|
|
@@ -22286,7 +20511,7 @@ ${dryRunSyncDirs.map(({ dir, bytes }) => `Would clone ${dir} (${formatBytes(byte
|
|
|
22286
20511
|
options.stderr
|
|
22287
20512
|
)) {
|
|
22288
20513
|
try {
|
|
22289
|
-
await
|
|
20514
|
+
await execFileAsync7(
|
|
22290
20515
|
"git",
|
|
22291
20516
|
["worktree", "remove", "--force", worktreePath],
|
|
22292
20517
|
{ cwd: repository.repoRoot }
|
|
@@ -22310,25 +20535,22 @@ ${dryRunSyncDirs.map(({ dir, bytes }) => `Would clone ${dir} (${formatBytes(byte
|
|
|
22310
20535
|
}
|
|
22311
20536
|
const bootstrap = await bootstrapWorktree({
|
|
22312
20537
|
branch: worktreeName,
|
|
22313
|
-
cloneDirectory,
|
|
22314
20538
|
config,
|
|
22315
20539
|
currentRoot: repository.currentRoot,
|
|
22316
|
-
|
|
20540
|
+
dependencyDetectionRoot: worktreePath,
|
|
20541
|
+
dependencyMode,
|
|
22317
20542
|
repoRoot: repository.repoRoot,
|
|
22318
20543
|
reporter: createBootstrapReporter(options.stderr, !!options.json),
|
|
22319
|
-
runCommand: dependencies.
|
|
20544
|
+
runCommand: dependencies.runCommand,
|
|
22320
20545
|
commandStdout: options.json ? () => void 0 : options.stdout,
|
|
22321
20546
|
commandStderr: options.json ? () => void 0 : options.stderr,
|
|
22322
20547
|
json: options.json,
|
|
22323
|
-
worktreePath
|
|
22324
|
-
installDependencies: dependencies,
|
|
22325
|
-
dependencyBootstrapPolicy: dependencyPolicy
|
|
20548
|
+
worktreePath
|
|
22326
20549
|
});
|
|
22327
20550
|
if (!bootstrap.ready) {
|
|
22328
20551
|
return emitNewError(options, "worktree bootstrap failed", {
|
|
22329
20552
|
dependencyBootstrap: bootstrap.dependencyBootstrap,
|
|
22330
20553
|
path: worktreePath,
|
|
22331
|
-
skipped: bootstrap.skippedDirs,
|
|
22332
20554
|
syncFiles: bootstrap.syncFileFailures
|
|
22333
20555
|
});
|
|
22334
20556
|
}
|
|
@@ -22346,15 +20568,7 @@ ${dryRunSyncDirs.map(({ dir, bytes }) => `Would clone ${dir} (${formatBytes(byte
|
|
|
22346
20568
|
submodules: submoduleFiles
|
|
22347
20569
|
}
|
|
22348
20570
|
} : { ...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")
|
|
20571
|
+
if (bootstrap.dependencyBootstrap.events.length > 0)
|
|
22358
20572
|
output.dependencyBootstrap = bootstrap.dependencyBootstrap;
|
|
22359
20573
|
options.stdout(`${JSON.stringify(output, null, 2)}
|
|
22360
20574
|
`);
|
|
@@ -22583,7 +20797,7 @@ async function defaultPromptForBranch(placeholder) {
|
|
|
22583
20797
|
}
|
|
22584
20798
|
async function localBranchExists2(repoRoot, branchName) {
|
|
22585
20799
|
try {
|
|
22586
|
-
await
|
|
20800
|
+
await execFileAsync7(
|
|
22587
20801
|
"git",
|
|
22588
20802
|
["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`],
|
|
22589
20803
|
{ cwd: repoRoot }
|
|
@@ -22594,18 +20808,18 @@ async function localBranchExists2(repoRoot, branchName) {
|
|
|
22594
20808
|
}
|
|
22595
20809
|
}
|
|
22596
20810
|
async function resolveCurrentWorktreeHead(cwd) {
|
|
22597
|
-
return
|
|
20811
|
+
return execFileAsync7("git", ["rev-parse", "--verify", "HEAD"], { cwd }).then(
|
|
22598
20812
|
({ stdout: stdout3 }) => stdout3.trim()
|
|
22599
20813
|
);
|
|
22600
20814
|
}
|
|
22601
20815
|
async function createTakeStash(cwd, worktreeName) {
|
|
22602
20816
|
const message = `gji-take: ${worktreeName}:${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
22603
|
-
await
|
|
20817
|
+
await execFileAsync7(
|
|
22604
20818
|
"git",
|
|
22605
20819
|
["stash", "push", "--include-untracked", "-m", message],
|
|
22606
20820
|
{ cwd }
|
|
22607
20821
|
);
|
|
22608
|
-
const { stdout: stdout3 } = await
|
|
20822
|
+
const { stdout: stdout3 } = await execFileAsync7(
|
|
22609
20823
|
"git",
|
|
22610
20824
|
["stash", "list", "--format=%H%x00%s"],
|
|
22611
20825
|
{ cwd }
|
|
@@ -22626,12 +20840,12 @@ async function detectInProgressGitState(cwd) {
|
|
|
22626
20840
|
];
|
|
22627
20841
|
for (const [marker, name] of markers) {
|
|
22628
20842
|
try {
|
|
22629
|
-
const { stdout: stdout3 } = await
|
|
20843
|
+
const { stdout: stdout3 } = await execFileAsync7(
|
|
22630
20844
|
"git",
|
|
22631
20845
|
["rev-parse", "--git-path", marker],
|
|
22632
20846
|
{ cwd }
|
|
22633
20847
|
);
|
|
22634
|
-
await
|
|
20848
|
+
await access8(resolve11(cwd, stdout3.trim()));
|
|
22635
20849
|
return name;
|
|
22636
20850
|
} catch {
|
|
22637
20851
|
}
|
|
@@ -22678,7 +20892,7 @@ async function openWorktree(worktreePath, editorCli, spawnFn, stderr) {
|
|
|
22678
20892
|
}
|
|
22679
20893
|
async function listTakeFiles(cwd) {
|
|
22680
20894
|
try {
|
|
22681
|
-
const { stdout: stdout3 } = await
|
|
20895
|
+
const { stdout: stdout3 } = await execFileAsync7(
|
|
22682
20896
|
"git",
|
|
22683
20897
|
["status", "--porcelain=v1"],
|
|
22684
20898
|
{ cwd }
|
|
@@ -22690,7 +20904,7 @@ async function listTakeFiles(cwd) {
|
|
|
22690
20904
|
}
|
|
22691
20905
|
async function listIgnoredFiles(cwd) {
|
|
22692
20906
|
try {
|
|
22693
|
-
const { stdout: stdout3 } = await
|
|
20907
|
+
const { stdout: stdout3 } = await execFileAsync7(
|
|
22694
20908
|
"git",
|
|
22695
20909
|
["status", "--porcelain=v1", "--ignored", "--untracked-files=all"],
|
|
22696
20910
|
{ cwd }
|
|
@@ -22703,7 +20917,7 @@ async function listIgnoredFiles(cwd) {
|
|
|
22703
20917
|
async function listSubmoduleFiles(cwd, changedFiles) {
|
|
22704
20918
|
if (changedFiles.length === 0) return [];
|
|
22705
20919
|
try {
|
|
22706
|
-
const { stdout: stdout3 } = await
|
|
20920
|
+
const { stdout: stdout3 } = await execFileAsync7("git", ["ls-files", "--stage"], {
|
|
22707
20921
|
cwd
|
|
22708
20922
|
});
|
|
22709
20923
|
const submodules = new Set(
|
|
@@ -22716,7 +20930,7 @@ async function listSubmoduleFiles(cwd, changedFiles) {
|
|
|
22716
20930
|
}
|
|
22717
20931
|
async function countUntrackedFiles(cwd) {
|
|
22718
20932
|
try {
|
|
22719
|
-
const { stdout: stdout3 } = await
|
|
20933
|
+
const { stdout: stdout3 } = await execFileAsync7(
|
|
22720
20934
|
"git",
|
|
22721
20935
|
["status", "--porcelain=v1", "--untracked-files=all"],
|
|
22722
20936
|
{ cwd }
|
|
@@ -22728,7 +20942,7 @@ async function countUntrackedFiles(cwd) {
|
|
|
22728
20942
|
}
|
|
22729
20943
|
async function applyTakeStash(worktreePath, sourcePath, stashSha, copy, stderr) {
|
|
22730
20944
|
try {
|
|
22731
|
-
await
|
|
20945
|
+
await execFileAsync7("git", ["stash", "apply", stashSha], {
|
|
22732
20946
|
cwd: worktreePath
|
|
22733
20947
|
});
|
|
22734
20948
|
} catch (error) {
|
|
@@ -22739,7 +20953,7 @@ async function applyTakeStash(worktreePath, sourcePath, stashSha, copy, stderr)
|
|
|
22739
20953
|
}
|
|
22740
20954
|
if (copy) {
|
|
22741
20955
|
try {
|
|
22742
|
-
await
|
|
20956
|
+
await execFileAsync7("git", ["stash", "apply", stashSha], {
|
|
22743
20957
|
cwd: sourcePath
|
|
22744
20958
|
});
|
|
22745
20959
|
} catch (error) {
|
|
@@ -22751,7 +20965,7 @@ async function applyTakeStash(worktreePath, sourcePath, stashSha, copy, stderr)
|
|
|
22751
20965
|
}
|
|
22752
20966
|
}
|
|
22753
20967
|
try {
|
|
22754
|
-
const { stdout: stdout3 } = await
|
|
20968
|
+
const { stdout: stdout3 } = await execFileAsync7(
|
|
22755
20969
|
"git",
|
|
22756
20970
|
["stash", "list", "--format=%H %gd"],
|
|
22757
20971
|
{ cwd: sourcePath }
|
|
@@ -22759,14 +20973,14 @@ async function applyTakeStash(worktreePath, sourcePath, stashSha, copy, stderr)
|
|
|
22759
20973
|
const line = stdout3.split("\n").find((candidate) => candidate.startsWith(stashSha));
|
|
22760
20974
|
const ref = line?.trim().split(/\s+/).at(1);
|
|
22761
20975
|
if (ref)
|
|
22762
|
-
await
|
|
20976
|
+
await execFileAsync7("git", ["stash", "drop", ref], { cwd: sourcePath });
|
|
22763
20977
|
} catch {
|
|
22764
20978
|
}
|
|
22765
20979
|
return true;
|
|
22766
20980
|
}
|
|
22767
20981
|
async function restoreTakeStash(sourcePath, stashSha, stderr) {
|
|
22768
20982
|
try {
|
|
22769
|
-
await
|
|
20983
|
+
await execFileAsync7("git", ["stash", "apply", stashSha], {
|
|
22770
20984
|
cwd: sourcePath
|
|
22771
20985
|
});
|
|
22772
20986
|
} catch {
|
|
@@ -23045,7 +21259,7 @@ async function navigateToExistingWorktree(options, path9, worktree) {
|
|
|
23045
21259
|
{
|
|
23046
21260
|
branch: worktree?.branch ?? void 0,
|
|
23047
21261
|
path: path9,
|
|
23048
|
-
repo:
|
|
21262
|
+
repo: basename9(repository.repoRoot),
|
|
23049
21263
|
slot: await getWorktreeSlot(path9)
|
|
23050
21264
|
},
|
|
23051
21265
|
options.stderr
|
|
@@ -23183,7 +21397,7 @@ async function runHistoryCommand(options) {
|
|
|
23183
21397
|
}
|
|
23184
21398
|
|
|
23185
21399
|
// src/hub.ts
|
|
23186
|
-
import { basename as
|
|
21400
|
+
import { basename as basename10 } from "node:path";
|
|
23187
21401
|
import { stdin as stdin2, stdout as stdout2 } from "node:process";
|
|
23188
21402
|
var MAX_HUB_REPOSITORY_CONCURRENCY = 4;
|
|
23189
21403
|
var defaultDependencies = {
|
|
@@ -23206,7 +21420,7 @@ async function buildHubData(cwd, dependencies = {}) {
|
|
|
23206
21420
|
existing.sources.push(source);
|
|
23207
21421
|
} else {
|
|
23208
21422
|
groups.set(repoRoot, {
|
|
23209
|
-
name: source.repoName ||
|
|
21423
|
+
name: source.repoName || basename10(repoRoot),
|
|
23210
21424
|
sources: [source]
|
|
23211
21425
|
});
|
|
23212
21426
|
}
|
|
@@ -23468,10 +21682,10 @@ async function mapWithConcurrency4(items, limit, mapper) {
|
|
|
23468
21682
|
}
|
|
23469
21683
|
|
|
23470
21684
|
// src/init.ts
|
|
23471
|
-
import { constants as
|
|
23472
|
-
import { access as
|
|
21685
|
+
import { constants as constants8 } from "node:fs";
|
|
21686
|
+
import { access as access9, mkdir as mkdir9, readFile as readFile9, writeFile as writeFile8 } from "node:fs/promises";
|
|
23473
21687
|
import { homedir as homedir8 } from "node:os";
|
|
23474
|
-
import { dirname as
|
|
21688
|
+
import { dirname as dirname12 } from "node:path";
|
|
23475
21689
|
var ZSH_COMPLETION_PATH_LINE = "fpath=(~/.zsh/completions $fpath)";
|
|
23476
21690
|
var SHELL_WRAPPED_COMMANDS = [
|
|
23477
21691
|
{
|
|
@@ -23595,8 +21809,8 @@ async function runDefaultOnboarding(context) {
|
|
|
23595
21809
|
const installCompletion = await promptForCompletion(shell, completionPath);
|
|
23596
21810
|
if (installCompletion === null) return abortDefaultOnboarding();
|
|
23597
21811
|
if (installCompletion) {
|
|
23598
|
-
await
|
|
23599
|
-
await
|
|
21812
|
+
await mkdir9(dirname12(completionPath), { recursive: true });
|
|
21813
|
+
await writeFile8(completionPath, renderShellCompletion(shell), "utf8");
|
|
23600
21814
|
}
|
|
23601
21815
|
if (shell === "zsh" && (completionAlreadyInstalled || installCompletion)) {
|
|
23602
21816
|
await ensureZshCompletionPath(rcPath);
|
|
@@ -23688,8 +21902,8 @@ async function applyOnboardingResult(result, home) {
|
|
|
23688
21902
|
const completionAlreadyInstalled = await fileExists(completionPath);
|
|
23689
21903
|
await applyShellIntegration(result.shell, result.shellIntegration, home);
|
|
23690
21904
|
if (result.installCompletion) {
|
|
23691
|
-
await
|
|
23692
|
-
await
|
|
21905
|
+
await mkdir9(dirname12(completionPath), { recursive: true });
|
|
21906
|
+
await writeFile8(
|
|
23693
21907
|
completionPath,
|
|
23694
21908
|
renderShellCompletion(result.shell),
|
|
23695
21909
|
"utf8"
|
|
@@ -23704,17 +21918,17 @@ async function applyOnboardingResult(result, home) {
|
|
|
23704
21918
|
}
|
|
23705
21919
|
async function installOnboardingShellIntegration(shell, home) {
|
|
23706
21920
|
const rcPath = resolveShellConfigPath(shell, home);
|
|
23707
|
-
await
|
|
21921
|
+
await mkdir9(dirname12(rcPath), { recursive: true });
|
|
23708
21922
|
const current = await readExistingConfig(rcPath);
|
|
23709
21923
|
if (shell === "fish") {
|
|
23710
|
-
await
|
|
21924
|
+
await writeFile8(
|
|
23711
21925
|
rcPath,
|
|
23712
21926
|
upsertShellIntegration(current, renderShellIntegration(shell)),
|
|
23713
21927
|
"utf8"
|
|
23714
21928
|
);
|
|
23715
21929
|
return;
|
|
23716
21930
|
}
|
|
23717
|
-
await
|
|
21931
|
+
await writeFile8(
|
|
23718
21932
|
rcPath,
|
|
23719
21933
|
upsertOnboardingShellIntegration(current, shell),
|
|
23720
21934
|
"utf8"
|
|
@@ -23748,7 +21962,7 @@ async function ensureZshCompletionPath(rcPath) {
|
|
|
23748
21962
|
const current = await readExistingConfig(rcPath);
|
|
23749
21963
|
const next = upsertZshCompletionPath(current);
|
|
23750
21964
|
if (next !== current) {
|
|
23751
|
-
await
|
|
21965
|
+
await writeFile8(rcPath, next, "utf8");
|
|
23752
21966
|
}
|
|
23753
21967
|
}
|
|
23754
21968
|
function upsertZshCompletionPath(existingConfig) {
|
|
@@ -23771,7 +21985,7 @@ ${suffix}`
|
|
|
23771
21985
|
}
|
|
23772
21986
|
async function fileExists(path9) {
|
|
23773
21987
|
try {
|
|
23774
|
-
await
|
|
21988
|
+
await access9(path9, constants8.F_OK);
|
|
23775
21989
|
return true;
|
|
23776
21990
|
} catch {
|
|
23777
21991
|
return false;
|
|
@@ -23792,10 +22006,10 @@ async function runLegacyInitCommand(options) {
|
|
|
23792
22006
|
return 0;
|
|
23793
22007
|
}
|
|
23794
22008
|
const rcPath = resolveShellConfigPath(shell, home);
|
|
23795
|
-
await
|
|
22009
|
+
await mkdir9(dirname12(rcPath), { recursive: true });
|
|
23796
22010
|
const current = await readExistingConfig(rcPath);
|
|
23797
22011
|
const next = upsertShellIntegration(current, script);
|
|
23798
|
-
await
|
|
22012
|
+
await writeFile8(rcPath, next, "utf8");
|
|
23799
22013
|
options.stdout(`${rcPath}
|
|
23800
22014
|
`);
|
|
23801
22015
|
const { config: globalConfig } = await loadGlobalConfig(home);
|
|
@@ -23886,7 +22100,7 @@ async function saveWizardConfig(result, cwd, home) {
|
|
|
23886
22100
|
}
|
|
23887
22101
|
async function readExistingConfig(path9) {
|
|
23888
22102
|
try {
|
|
23889
|
-
return await
|
|
22103
|
+
return await readFile9(path9, "utf8");
|
|
23890
22104
|
} catch (error) {
|
|
23891
22105
|
if (isMissingFileError3(error)) {
|
|
23892
22106
|
return "";
|
|
@@ -24135,11 +22349,11 @@ function sortWorktrees(worktrees) {
|
|
|
24135
22349
|
}
|
|
24136
22350
|
|
|
24137
22351
|
// src/open.ts
|
|
24138
|
-
import { execFile as
|
|
24139
|
-
import { access as
|
|
24140
|
-
import { join as
|
|
24141
|
-
import { promisify as
|
|
24142
|
-
var
|
|
22352
|
+
import { execFile as execFile8 } from "node:child_process";
|
|
22353
|
+
import { access as access10, writeFile as writeFile9 } from "node:fs/promises";
|
|
22354
|
+
import { join as join14 } from "node:path";
|
|
22355
|
+
import { promisify as promisify9 } from "node:util";
|
|
22356
|
+
var execFileAsync8 = promisify9(execFile8);
|
|
24143
22357
|
function createOpenCommand(dependencies = {}) {
|
|
24144
22358
|
const detectEditors = dependencies.detectEditors ?? detectInstalledEditors;
|
|
24145
22359
|
const promptForEditor2 = dependencies.promptForEditor ?? defaultPromptForEditor;
|
|
@@ -24286,7 +22500,7 @@ async function detectInstalledEditors() {
|
|
|
24286
22500
|
}
|
|
24287
22501
|
async function isCommandAvailable(command) {
|
|
24288
22502
|
try {
|
|
24289
|
-
await
|
|
22503
|
+
await execFileAsync8("which", [command]);
|
|
24290
22504
|
return true;
|
|
24291
22505
|
} catch {
|
|
24292
22506
|
return false;
|
|
@@ -24304,14 +22518,14 @@ async function defaultPromptForEditor(editors) {
|
|
|
24304
22518
|
return choice;
|
|
24305
22519
|
}
|
|
24306
22520
|
async function ensureWorkspaceFile(worktreePath, repoName) {
|
|
24307
|
-
const workspacePath =
|
|
22521
|
+
const workspacePath = join14(worktreePath, `${repoName}.code-workspace`);
|
|
24308
22522
|
try {
|
|
24309
|
-
await
|
|
22523
|
+
await access10(workspacePath);
|
|
24310
22524
|
return workspacePath;
|
|
24311
22525
|
} catch {
|
|
24312
22526
|
}
|
|
24313
22527
|
const workspace = { folders: [{ path: "." }], settings: {} };
|
|
24314
|
-
await
|
|
22528
|
+
await writeFile9(
|
|
24315
22529
|
workspacePath,
|
|
24316
22530
|
`${JSON.stringify(workspace, null, 2)}
|
|
24317
22531
|
`,
|
|
@@ -24321,10 +22535,10 @@ async function ensureWorkspaceFile(worktreePath, repoName) {
|
|
|
24321
22535
|
}
|
|
24322
22536
|
|
|
24323
22537
|
// src/browser.ts
|
|
24324
|
-
import { execFile as
|
|
22538
|
+
import { execFile as execFile9 } from "node:child_process";
|
|
24325
22539
|
import { platform as platform2 } from "node:process";
|
|
24326
|
-
import { promisify as
|
|
24327
|
-
var
|
|
22540
|
+
import { promisify as promisify10 } from "node:util";
|
|
22541
|
+
var execFileAsync9 = promisify10(execFile9);
|
|
24328
22542
|
function createBrowserOpener(dependencies = {}) {
|
|
24329
22543
|
const currentPlatform = dependencies.platform ?? platform2;
|
|
24330
22544
|
const runCommand2 = dependencies.runCommand ?? defaultRunCommand2;
|
|
@@ -24354,7 +22568,7 @@ function validateBrowserUrl(url) {
|
|
|
24354
22568
|
}
|
|
24355
22569
|
}
|
|
24356
22570
|
async function defaultRunCommand2(command, args) {
|
|
24357
|
-
await
|
|
22571
|
+
await execFileAsync9(command, args, { timeout: 1e4 });
|
|
24358
22572
|
}
|
|
24359
22573
|
|
|
24360
22574
|
// src/pr-open.ts
|
|
@@ -24719,7 +22933,7 @@ function formatError(error) {
|
|
|
24719
22933
|
}
|
|
24720
22934
|
|
|
24721
22935
|
// src/remove.ts
|
|
24722
|
-
import { basename as
|
|
22936
|
+
import { basename as basename11 } from "node:path";
|
|
24723
22937
|
var REMOVE_OUTPUT_FILE_ENV = "GJI_REMOVE_OUTPUT_FILE";
|
|
24724
22938
|
function createRemoveCommand(dependencies = {}) {
|
|
24725
22939
|
const promptForWorktree2 = dependencies.promptForWorktree ?? defaultPromptForWorktree3;
|
|
@@ -24825,7 +23039,7 @@ function createRemoveCommand(dependencies = {}) {
|
|
|
24825
23039
|
{
|
|
24826
23040
|
branch: worktree.branch ?? void 0,
|
|
24827
23041
|
path: worktree.path,
|
|
24828
|
-
repo:
|
|
23042
|
+
repo: basename11(repository.repoRoot)
|
|
24829
23043
|
},
|
|
24830
23044
|
options.stderr
|
|
24831
23045
|
);
|
|
@@ -25224,7 +23438,7 @@ function resolveConfiguredString2(value) {
|
|
|
25224
23438
|
|
|
25225
23439
|
// src/sync-files-command.ts
|
|
25226
23440
|
import { homedir as homedir9 } from "node:os";
|
|
25227
|
-
import { join as
|
|
23441
|
+
import { join as join15 } from "node:path";
|
|
25228
23442
|
async function runSyncFilesCommand(options) {
|
|
25229
23443
|
const repository = await detectRepository(options.cwd);
|
|
25230
23444
|
const home = options.home ?? homedir9();
|
|
@@ -25271,9 +23485,9 @@ async function runSyncFilesCommand(options) {
|
|
|
25271
23485
|
}
|
|
25272
23486
|
function findRepoConfigEntry(config, repoRoot, home) {
|
|
25273
23487
|
const repos = config.repos;
|
|
25274
|
-
if (!
|
|
23488
|
+
if (!isPlainObject2(repos)) return null;
|
|
25275
23489
|
for (const [key, value] of Object.entries(repos)) {
|
|
25276
|
-
if (expandTilde3(key, home) === repoRoot &&
|
|
23490
|
+
if (expandTilde3(key, home) === repoRoot && isPlainObject2(value)) {
|
|
25277
23491
|
return { config: value, key };
|
|
25278
23492
|
}
|
|
25279
23493
|
}
|
|
@@ -25330,8 +23544,8 @@ function removeSyncFiles(existing, removals) {
|
|
|
25330
23544
|
return existing.filter((path9) => !removalSet.has(path9));
|
|
25331
23545
|
}
|
|
25332
23546
|
async function saveRepoSyncFiles(config, repoKey, syncFiles2, home) {
|
|
25333
|
-
const repos =
|
|
25334
|
-
const repoConfig =
|
|
23547
|
+
const repos = isPlainObject2(config.repos) ? { ...config.repos } : {};
|
|
23548
|
+
const repoConfig = isPlainObject2(repos[repoKey]) ? repos[repoKey] : {};
|
|
25335
23549
|
const nextRepoConfig = { ...repoConfig };
|
|
25336
23550
|
if (syncFiles2.length > 0) {
|
|
25337
23551
|
nextRepoConfig.syncFiles = syncFiles2;
|
|
@@ -25345,7 +23559,7 @@ async function saveRepoSyncFiles(config, repoKey, syncFiles2, home) {
|
|
|
25345
23559
|
}
|
|
25346
23560
|
await saveGlobalConfig({ ...config, repos }, home);
|
|
25347
23561
|
}
|
|
25348
|
-
function
|
|
23562
|
+
function isPlainObject2(value) {
|
|
25349
23563
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25350
23564
|
}
|
|
25351
23565
|
function writeError(options, message) {
|
|
@@ -25359,7 +23573,7 @@ function writeError(options, message) {
|
|
|
25359
23573
|
}
|
|
25360
23574
|
function expandTilde3(value, home) {
|
|
25361
23575
|
if (value === "~") return home;
|
|
25362
|
-
if (value.startsWith("~/")) return
|
|
23576
|
+
if (value.startsWith("~/")) return join15(home, value.slice(2));
|
|
25363
23577
|
return value;
|
|
25364
23578
|
}
|
|
25365
23579
|
|
|
@@ -25624,9 +23838,7 @@ async function maybeRegisterCurrentRepo(cwd) {
|
|
|
25624
23838
|
}
|
|
25625
23839
|
}
|
|
25626
23840
|
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(
|
|
23841
|
+
program2.command("new [branch]").description("create a new branch or detached linked worktree").option(
|
|
25630
23842
|
"-f, --force",
|
|
25631
23843
|
"remove and recreate the worktree if the target path already exists"
|
|
25632
23844
|
).option("--detached", "create a detached worktree without a branch").option(
|
|
@@ -25644,6 +23856,9 @@ function registerCommands(program2) {
|
|
|
25644
23856
|
).option(
|
|
25645
23857
|
"--dry-run",
|
|
25646
23858
|
"show what would be created without executing any git commands or writing files"
|
|
23859
|
+
).option(
|
|
23860
|
+
"--no-install",
|
|
23861
|
+
"skip automatic dependency setup in the new worktree"
|
|
25647
23862
|
).option(
|
|
25648
23863
|
"--json",
|
|
25649
23864
|
"emit JSON on success or error instead of human-readable output"
|
|
@@ -25666,6 +23881,9 @@ function registerCommands(program2) {
|
|
|
25666
23881
|
).option(
|
|
25667
23882
|
"--dry-run",
|
|
25668
23883
|
"show what would be created without executing any git commands or writing files"
|
|
23884
|
+
).option(
|
|
23885
|
+
"--no-install",
|
|
23886
|
+
"skip automatic dependency setup in the PR worktree"
|
|
25669
23887
|
).option(
|
|
25670
23888
|
"--json",
|
|
25671
23889
|
"emit JSON on success or error instead of human-readable output"
|
|
@@ -25743,6 +23961,7 @@ function attachCommandActions(program2, options) {
|
|
|
25743
23961
|
force: commandOptions2.force,
|
|
25744
23962
|
noFetch: commandOptions2.fetch === false,
|
|
25745
23963
|
json: commandOptions2.json,
|
|
23964
|
+
noInstall: commandOptions2.install === false,
|
|
25746
23965
|
open: commandOptions2.open,
|
|
25747
23966
|
take: commandOptions2.take,
|
|
25748
23967
|
task: commandOptions2.task
|
|
@@ -25825,6 +24044,7 @@ function attachCommandActions(program2, options) {
|
|
|
25825
24044
|
cwd: options.cwd,
|
|
25826
24045
|
dryRun: commandOptions2.dryRun,
|
|
25827
24046
|
json: commandOptions2.json,
|
|
24047
|
+
noInstall: commandOptions2.install === false,
|
|
25828
24048
|
number,
|
|
25829
24049
|
stderr: options.stderr,
|
|
25830
24050
|
stdout: options.stdout
|