ai-project-manage-cli 8.1.27 → 8.1.28
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/index.js
CHANGED
|
@@ -1436,7 +1436,7 @@ function resolveHomeRepositoryAbsolutePaths(workdirInput, entries) {
|
|
|
1436
1436
|
}
|
|
1437
1437
|
|
|
1438
1438
|
// src/utils/git.ts
|
|
1439
|
-
import { execFile } from "child_process";
|
|
1439
|
+
import { execFile, spawn } from "child_process";
|
|
1440
1440
|
import { dirname as dirname3, join as join8 } from "path";
|
|
1441
1441
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1442
1442
|
import { promisify } from "util";
|
|
@@ -1505,7 +1505,9 @@ async function execGit(cwd, args, quietOrOptions = false) {
|
|
|
1505
1505
|
const e = err;
|
|
1506
1506
|
if (e.killed || e.signal === "SIGTERM" || e.code === "ETIMEDOUT") {
|
|
1507
1507
|
throw new Error(
|
|
1508
|
-
`[apm] git ${args.join(
|
|
1508
|
+
`[apm] git ${args.join(
|
|
1509
|
+
" "
|
|
1510
|
+
)} \u8D85\u65F6\uFF08>${timeoutMs}ms\uFF09\uFF0C\u8BF7\u68C0\u67E5\u5230\u8FDC\u7A0B\u7684\u7F51\u7EDC/SSH\uFF1B\u53EF\u7A0D\u540E\u91CD\u8BD5`
|
|
1509
1511
|
);
|
|
1510
1512
|
}
|
|
1511
1513
|
const detail = (e.stderr ?? e.message ?? String(err)).trim();
|
|
@@ -1528,33 +1530,67 @@ async function runGitScript(name, options) {
|
|
|
1528
1530
|
const timeoutMs = options.timeoutMs ?? GIT_NETWORK_TIMEOUT_MS;
|
|
1529
1531
|
const script = resolveGitScript(name);
|
|
1530
1532
|
const args = [script, ...options.args ?? []];
|
|
1531
|
-
|
|
1532
|
-
const
|
|
1533
|
+
return new Promise((resolve5, reject) => {
|
|
1534
|
+
const child = spawn("bash", args, {
|
|
1533
1535
|
cwd: options.cwd,
|
|
1534
1536
|
env: gitExecEnv(),
|
|
1535
|
-
|
|
1536
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
1537
|
-
timeout: timeoutMs,
|
|
1538
|
-
killSignal: "SIGTERM"
|
|
1537
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1539
1538
|
});
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1539
|
+
let stderrBuf = "";
|
|
1540
|
+
let settled = false;
|
|
1541
|
+
let timedOut = false;
|
|
1542
|
+
const timer = timeoutMs > 0 ? setTimeout(() => {
|
|
1543
|
+
timedOut = true;
|
|
1544
|
+
child.kill("SIGTERM");
|
|
1545
|
+
const forceKill = setTimeout(() => {
|
|
1546
|
+
child.kill("SIGKILL");
|
|
1547
|
+
}, 2e3);
|
|
1548
|
+
forceKill.unref();
|
|
1549
|
+
}, timeoutMs) : null;
|
|
1550
|
+
const finish = (fn) => {
|
|
1551
|
+
if (settled) return;
|
|
1552
|
+
settled = true;
|
|
1553
|
+
if (timer) clearTimeout(timer);
|
|
1554
|
+
fn();
|
|
1555
|
+
};
|
|
1556
|
+
child.stdout?.on("data", (chunk) => {
|
|
1557
|
+
process.stdout.write(chunk);
|
|
1558
|
+
});
|
|
1559
|
+
child.stderr?.on("data", (chunk) => {
|
|
1560
|
+
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
1561
|
+
stderrBuf += text;
|
|
1562
|
+
process.stderr.write(chunk);
|
|
1563
|
+
});
|
|
1564
|
+
child.on("error", (err) => {
|
|
1565
|
+
finish(() => reject(new Error(`[apm] ${name} \u542F\u52A8\u5931\u8D25: ${err.message}`)));
|
|
1566
|
+
});
|
|
1567
|
+
child.on("close", (code, signal) => {
|
|
1568
|
+
finish(() => {
|
|
1569
|
+
if (timedOut || signal === "SIGTERM" || signal === "SIGKILL") {
|
|
1570
|
+
reject(
|
|
1571
|
+
new Error(
|
|
1572
|
+
`[apm] ${name} \u8D85\u65F6\uFF08>${timeoutMs}ms\uFF09\uFF0C\u8BF7\u770B\u4E0A\u65B9\u6700\u540E\u4E00\u6B65\u65E5\u5FD7\u5B9A\u4F4D\u5361\u70B9\uFF08\u591A\u4E3A ls-remote/fetch/push\uFF09\uFF1B\u68C0\u67E5\u5230\u8FDC\u7A0B\u7684\u7F51\u7EDC/SSH \u540E\u53EF\u7A0D\u540E\u91CD\u8BD5`
|
|
1573
|
+
)
|
|
1574
|
+
);
|
|
1575
|
+
return;
|
|
1576
|
+
}
|
|
1577
|
+
if (code === skipCode) {
|
|
1578
|
+
resolve5(false);
|
|
1579
|
+
return;
|
|
1580
|
+
}
|
|
1581
|
+
if (code === 0) {
|
|
1582
|
+
resolve5(true);
|
|
1583
|
+
return;
|
|
1584
|
+
}
|
|
1585
|
+
const detail = stderrBuf.trim();
|
|
1586
|
+
reject(
|
|
1587
|
+
new Error(
|
|
1588
|
+
`[apm] ${name} \u5931\u8D25${detail ? `: ${detail}` : code != null ? ` (exit ${code})` : ""}`
|
|
1589
|
+
)
|
|
1590
|
+
);
|
|
1591
|
+
});
|
|
1592
|
+
});
|
|
1593
|
+
});
|
|
1558
1594
|
}
|
|
1559
1595
|
async function commitWorkingTreeIfDirty(cwd, message) {
|
|
1560
1596
|
await ensureGitRepo(cwd);
|
|
@@ -6,8 +6,11 @@ set -euo pipefail
|
|
|
6
6
|
|
|
7
7
|
baseline="${1:?用法: ensure_baseline.sh <baseline-branch>}"
|
|
8
8
|
|
|
9
|
+
echo "[apm] ensure_baseline: fetch origin ${baseline} …"
|
|
9
10
|
git fetch origin "$baseline"
|
|
11
|
+
echo "[apm] ensure_baseline: ls-remote origin ${baseline} …"
|
|
10
12
|
if git ls-remote --heads origin "$baseline" | grep -q .; then
|
|
13
|
+
echo "[apm] ensure_baseline: origin/${baseline} 已确认"
|
|
11
14
|
exit 0
|
|
12
15
|
fi
|
|
13
16
|
|
|
@@ -9,9 +9,12 @@ baseline="${2:?用法: ensure_feature_branch.sh <branch> <baseline-branch>}"
|
|
|
9
9
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
10
10
|
|
|
11
11
|
git rev-parse --git-dir >/dev/null
|
|
12
|
+
origin_url="$(git config --get remote.origin.url 2>/dev/null || true)"
|
|
13
|
+
echo "[apm] ensure_feature_branch: branch=${branch} baseline=${baseline}${origin_url:+ origin=${origin_url}}"
|
|
12
14
|
|
|
13
15
|
# 有未提交改动时先 stash(含 untracked),再切/建分支,避免污染目标分支工作区
|
|
14
16
|
if [[ -n "$(git status --porcelain)" ]]; then
|
|
17
|
+
echo "[apm] ensure_feature_branch: stash 未提交改动 …"
|
|
15
18
|
git stash push -u -m "apm: ensure ${branch}"
|
|
16
19
|
echo "[apm] 已 stash 未提交改动,再确认分支 ${branch}"
|
|
17
20
|
fi
|
|
@@ -20,16 +23,26 @@ current="$(git rev-parse --abbrev-ref HEAD)"
|
|
|
20
23
|
if [[ "$current" == "$branch" ]]; then
|
|
21
24
|
echo "[apm] 已在分支 ${branch}"
|
|
22
25
|
else
|
|
26
|
+
echo "[apm] ensure_feature_branch: 当前 ${current} → 准备 ${branch}"
|
|
23
27
|
bash "$SCRIPT_DIR/ensure_baseline.sh" "$baseline"
|
|
24
28
|
|
|
29
|
+
echo "[apm] ensure_feature_branch: ls-remote origin ${branch} …"
|
|
25
30
|
if git ls-remote --heads origin "$branch" | grep -q .; then
|
|
31
|
+
echo "[apm] ensure_feature_branch: 远程已有 ${branch},fetch + checkout …"
|
|
26
32
|
git fetch origin "$branch"
|
|
27
33
|
git checkout -B "$branch" "origin/${branch}"
|
|
28
34
|
elif git show-ref --verify --quiet "refs/heads/${branch}"; then
|
|
35
|
+
echo "[apm] ensure_feature_branch: 本地已有 ${branch},checkout …"
|
|
29
36
|
git checkout "$branch"
|
|
30
37
|
echo "[apm] 分支 ${branch} 已存在,跳过创建"
|
|
31
38
|
else
|
|
39
|
+
# 先切到远程基线(便于 post-checkout 等 hook 装依赖),再基于 origin/<baseline> 建特性分支(不基于本地基线)
|
|
40
|
+
echo "[apm] ensure_feature_branch: 本地/远程均无 ${branch},先切基线 origin/${baseline} …"
|
|
41
|
+
echo "[apm] ensure_feature_branch: fetch origin ${baseline} …"
|
|
32
42
|
git fetch origin "$baseline"
|
|
43
|
+
echo "[apm] ensure_feature_branch: checkout -B ${baseline} origin/${baseline} …"
|
|
44
|
+
git checkout -B "$baseline" "origin/${baseline}"
|
|
45
|
+
echo "[apm] ensure_feature_branch: 基于 origin/${baseline} 创建 ${branch} …"
|
|
33
46
|
set +e
|
|
34
47
|
git checkout --no-track -b "$branch" "origin/${baseline}"
|
|
35
48
|
create_status=$?
|
|
@@ -44,14 +57,19 @@ else
|
|
|
44
57
|
else
|
|
45
58
|
exit "$create_status"
|
|
46
59
|
fi
|
|
60
|
+
else
|
|
61
|
+
echo "[apm] ensure_feature_branch: 已创建本地分支 ${branch}"
|
|
47
62
|
fi
|
|
48
63
|
fi
|
|
49
64
|
fi
|
|
50
65
|
|
|
51
66
|
# 本地已在目标分支时也要补推:上次 push 挂死/超时后常见「本地有、远端无」
|
|
67
|
+
echo "[apm] ensure_feature_branch: 再次 ls-remote 确认远程是否已有 ${branch} …"
|
|
52
68
|
if ! git ls-remote --heads origin "$branch" | grep -q .; then
|
|
53
69
|
echo "[apm] 远程尚无 ${branch},正在 push …"
|
|
54
70
|
bash "$SCRIPT_DIR/push_code.sh" "$branch"
|
|
71
|
+
else
|
|
72
|
+
echo "[apm] ensure_feature_branch: 远程已有 ${branch},跳过 push"
|
|
55
73
|
fi
|
|
56
74
|
|
|
57
75
|
echo "[apm] 已就绪分支 ${branch}"
|
|
@@ -17,5 +17,8 @@ if ! git config --get remote.origin.url >/dev/null 2>&1; then
|
|
|
17
17
|
exit 2
|
|
18
18
|
fi
|
|
19
19
|
|
|
20
|
+
origin_url="$(git config --get remote.origin.url)"
|
|
21
|
+
echo "[apm] push: git push -u origin ${branch}(${origin_url})…"
|
|
20
22
|
# 显式 origin + -u:避免裸 git push 因 upstream 与当前分支名不一致失败
|
|
21
23
|
git push -u origin "$branch"
|
|
24
|
+
echo "[apm] push: 完成 ${branch}"
|
package/dist/worker-script.js
CHANGED
|
@@ -359,7 +359,7 @@ async function ensureWorkspaceApmDirForInit(cwd = resolveWorkdirPath()) {
|
|
|
359
359
|
}
|
|
360
360
|
|
|
361
361
|
// src/utils/git.ts
|
|
362
|
-
import { execFile } from "child_process";
|
|
362
|
+
import { execFile, spawn } from "child_process";
|
|
363
363
|
import { dirname, join as join3 } from "path";
|
|
364
364
|
import { fileURLToPath } from "url";
|
|
365
365
|
import { promisify } from "util";
|
|
@@ -428,7 +428,9 @@ async function execGit(cwd, args, quietOrOptions = false) {
|
|
|
428
428
|
const e = err;
|
|
429
429
|
if (e.killed || e.signal === "SIGTERM" || e.code === "ETIMEDOUT") {
|
|
430
430
|
throw new Error(
|
|
431
|
-
`[apm] git ${args.join(
|
|
431
|
+
`[apm] git ${args.join(
|
|
432
|
+
" "
|
|
433
|
+
)} \u8D85\u65F6\uFF08>${timeoutMs}ms\uFF09\uFF0C\u8BF7\u68C0\u67E5\u5230\u8FDC\u7A0B\u7684\u7F51\u7EDC/SSH\uFF1B\u53EF\u7A0D\u540E\u91CD\u8BD5`
|
|
432
434
|
);
|
|
433
435
|
}
|
|
434
436
|
const detail = (e.stderr ?? e.message ?? String(err)).trim();
|
|
@@ -458,33 +460,67 @@ async function runGitScript(name, options) {
|
|
|
458
460
|
const timeoutMs = options.timeoutMs ?? GIT_NETWORK_TIMEOUT_MS;
|
|
459
461
|
const script = resolveGitScript(name);
|
|
460
462
|
const args = [script, ...options.args ?? []];
|
|
461
|
-
|
|
462
|
-
const
|
|
463
|
+
return new Promise((resolve7, reject) => {
|
|
464
|
+
const child = spawn("bash", args, {
|
|
463
465
|
cwd: options.cwd,
|
|
464
466
|
env: gitExecEnv(),
|
|
465
|
-
|
|
466
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
467
|
-
timeout: timeoutMs,
|
|
468
|
-
killSignal: "SIGTERM"
|
|
467
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
469
468
|
});
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
469
|
+
let stderrBuf = "";
|
|
470
|
+
let settled = false;
|
|
471
|
+
let timedOut = false;
|
|
472
|
+
const timer = timeoutMs > 0 ? setTimeout(() => {
|
|
473
|
+
timedOut = true;
|
|
474
|
+
child.kill("SIGTERM");
|
|
475
|
+
const forceKill = setTimeout(() => {
|
|
476
|
+
child.kill("SIGKILL");
|
|
477
|
+
}, 2e3);
|
|
478
|
+
forceKill.unref();
|
|
479
|
+
}, timeoutMs) : null;
|
|
480
|
+
const finish = (fn) => {
|
|
481
|
+
if (settled) return;
|
|
482
|
+
settled = true;
|
|
483
|
+
if (timer) clearTimeout(timer);
|
|
484
|
+
fn();
|
|
485
|
+
};
|
|
486
|
+
child.stdout?.on("data", (chunk) => {
|
|
487
|
+
process.stdout.write(chunk);
|
|
488
|
+
});
|
|
489
|
+
child.stderr?.on("data", (chunk) => {
|
|
490
|
+
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
491
|
+
stderrBuf += text;
|
|
492
|
+
process.stderr.write(chunk);
|
|
493
|
+
});
|
|
494
|
+
child.on("error", (err) => {
|
|
495
|
+
finish(() => reject(new Error(`[apm] ${name} \u542F\u52A8\u5931\u8D25: ${err.message}`)));
|
|
496
|
+
});
|
|
497
|
+
child.on("close", (code, signal) => {
|
|
498
|
+
finish(() => {
|
|
499
|
+
if (timedOut || signal === "SIGTERM" || signal === "SIGKILL") {
|
|
500
|
+
reject(
|
|
501
|
+
new Error(
|
|
502
|
+
`[apm] ${name} \u8D85\u65F6\uFF08>${timeoutMs}ms\uFF09\uFF0C\u8BF7\u770B\u4E0A\u65B9\u6700\u540E\u4E00\u6B65\u65E5\u5FD7\u5B9A\u4F4D\u5361\u70B9\uFF08\u591A\u4E3A ls-remote/fetch/push\uFF09\uFF1B\u68C0\u67E5\u5230\u8FDC\u7A0B\u7684\u7F51\u7EDC/SSH \u540E\u53EF\u7A0D\u540E\u91CD\u8BD5`
|
|
503
|
+
)
|
|
504
|
+
);
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
if (code === skipCode) {
|
|
508
|
+
resolve7(false);
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
if (code === 0) {
|
|
512
|
+
resolve7(true);
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
const detail = stderrBuf.trim();
|
|
516
|
+
reject(
|
|
517
|
+
new Error(
|
|
518
|
+
`[apm] ${name} \u5931\u8D25${detail ? `: ${detail}` : code != null ? ` (exit ${code})` : ""}`
|
|
519
|
+
)
|
|
520
|
+
);
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
});
|
|
488
524
|
}
|
|
489
525
|
async function branchHasChangesAgainstBase(cwd, baseBranch, headBranch) {
|
|
490
526
|
return runGitScript("branch_has_changes.sh", {
|