
























@@ -148,6 +148,27 @@ describe("runGatewayUpdate", () => {
148148return await setupUiIndex();
149149}
150150151+async function writePreflightPackageManagerFixture(root: string, packageManager = "pnpm@8.0.0") {
152+await fs.mkdir(root, { recursive: true });
153+await fs.writeFile(
154+path.join(root, "package.json"),
155+JSON.stringify({ name: "openclaw", version: "1.0.0", packageManager }),
156+"utf-8",
157+);
158+}
159+160+async function writePreflightPackageManagerFixtureFromWorktreeAdd(
161+key: string,
162+packageManager = "pnpm@8.0.0",
163+) {
164+const match = /\sworktree add --detach (?<root>\S+) /u.exec(key);
165+const root = match?.groups?.root;
166+if (!root) {
167+throw new Error(`expected preflight worktree path in command: ${key}`);
168+}
169+await writePreflightPackageManagerFixture(root, packageManager);
170+}
171+151172function buildStableTagResponses(
152173stableTag: string,
153174options?: { additionalTags?: string[] },
@@ -729,6 +750,7 @@ describe("runGatewayUpdate", () => {
729750key.endsWith(` ${upstreamSha}`) &&
730751preflightPrefixPattern.test(key)
731752) {
753+await writePreflightPackageManagerFixtureFromWorktreeAdd(key);
732754return { stdout: `HEAD is now at ${upstreamSha}`, stderr: "", code: 0 };
733755}
734756if (
@@ -777,6 +799,95 @@ describe("runGatewayUpdate", () => {
777799}
778800});
779801802+it("resolves the dev preflight package manager from the checked-out candidate worktree", async () => {
803+await setupGitCheckout({ packageManager: "npm@10.0.0" });
804+await setupUiIndex();
805+const upstreamSha = "upstream123";
806+const preflightInstallCommands: string[] = [];
807+const doctorNodePath = await resolveStableNodePath(process.execPath);
808+const doctorCommand = `${doctorNodePath} ${path.join(tempDir, "openclaw.mjs")} doctor --non-interactive --fix`;
809+810+const runCommand = async (
811+argv: string[],
812+options?: { env?: NodeJS.ProcessEnv; cwd?: string; timeoutMs?: number },
813+) => {
814+const key = argv.join(" ");
815+if (key === `git -C ${tempDir} rev-parse --show-toplevel`) {
816+return { stdout: tempDir, stderr: "", code: 0 };
817+}
818+if (key === `git -C ${tempDir} rev-parse HEAD`) {
819+return { stdout: "abc123", stderr: "", code: 0 };
820+}
821+if (key === `git -C ${tempDir} rev-parse --abbrev-ref HEAD`) {
822+return { stdout: "main", stderr: "", code: 0 };
823+}
824+if (key === `git -C ${tempDir} status --porcelain -- :!dist/control-ui/`) {
825+return { stdout: "", stderr: "", code: 0 };
826+}
827+if (key === `git -C ${tempDir} rev-parse --abbrev-ref --symbolic-full-name @{upstream}`) {
828+return { stdout: "origin/main", stderr: "", code: 0 };
829+}
830+if (key === `git -C ${tempDir} fetch --all --prune --no-tags`) {
831+return { stdout: "", stderr: "", code: 0 };
832+}
833+if (key === `git -C ${tempDir} rev-parse @{upstream}`) {
834+return { stdout: upstreamSha, stderr: "", code: 0 };
835+}
836+if (key === `git -C ${tempDir} rev-list --max-count=10 ${upstreamSha}`) {
837+return { stdout: `${upstreamSha}\n`, stderr: "", code: 0 };
838+}
839+if (key === "pnpm --version" || key === "npm --version") {
840+return { stdout: "10.0.0", stderr: "", code: 0 };
841+}
842+if (
843+key.startsWith(`git -C ${tempDir} worktree add --detach /tmp/`) &&
844+key.endsWith(` ${upstreamSha}`) &&
845+preflightPrefixPattern.test(key)
846+) {
847+await writePreflightPackageManagerFixtureFromWorktreeAdd(key, "pnpm@8.0.0");
848+return { stdout: `HEAD is now at ${upstreamSha}`, stderr: "", code: 0 };
849+}
850+if (
851+key.startsWith("git -C /tmp/") &&
852+preflightPrefixPattern.test(key) &&
853+key.includes(" checkout --detach ") &&
854+key.endsWith(upstreamSha)
855+) {
856+return { stdout: "", stderr: "", code: 0 };
857+}
858+if (key === "pnpm install" || key === "npm install") {
859+if (options?.cwd && preflightPrefixPattern.test(options.cwd)) {
860+preflightInstallCommands.push(key);
861+}
862+return { stdout: "", stderr: "", code: 0 };
863+}
864+if (key === "pnpm build" || key === "npm run build" || key === "npm run ui:build") {
865+return { stdout: "", stderr: "", code: 0 };
866+}
867+if (
868+key.startsWith(`git -C ${tempDir} worktree remove --force /tmp/`) &&
869+preflightPrefixPattern.test(key)
870+) {
871+return { stdout: "", stderr: "", code: 0 };
872+}
873+if (key === `git -C ${tempDir} worktree prune`) {
874+return { stdout: "", stderr: "", code: 0 };
875+}
876+if (key === `git -C ${tempDir} rebase ${upstreamSha}`) {
877+return { stdout: "", stderr: "", code: 0 };
878+}
879+if (key === doctorCommand) {
880+return { stdout: "", stderr: "", code: 0 };
881+}
882+return { stdout: "", stderr: "", code: 0 };
883+};
884+885+const result = await runWithCommand(runCommand, { channel: "dev" });
886+887+expect(result.status).toBe("ok");
888+expect(preflightInstallCommands).toEqual(["pnpm install"]);
889+});
890+780891it("returns error and stops early when build fails", async () => {
781892await setupGitCheckout({ packageManager: "pnpm@8.0.0" });
782893const stableTag = "v1.0.1-1";
@@ -988,6 +1099,7 @@ describe("runGatewayUpdate", () => {
9881099key.endsWith(` ${upstreamSha}`) &&
9891100preflightPrefixPattern.test(key)
9901101) {
1102+await writePreflightPackageManagerFixtureFromWorktreeAdd(key);
9911103return { stdout: `HEAD is now at ${upstreamSha}`, stderr: "", code: 0 };
9921104}
9931105if (
@@ -1089,6 +1201,7 @@ describe("runGatewayUpdate", () => {
10891201key.endsWith(` ${upstreamSha}`) &&
10901202preflightPrefixPattern.test(key)
10911203) {
1204+await writePreflightPackageManagerFixtureFromWorktreeAdd(key);
10921205return { stdout: `HEAD is now at ${upstreamSha}`, stderr: "", code: 0 };
10931206}
10941207if (
@@ -1186,6 +1299,7 @@ describe("runGatewayUpdate", () => {
11861299key.endsWith(` ${upstreamSha}`) &&
11871300preflightPrefixPattern.test(key)
11881301) {
1302+await writePreflightPackageManagerFixtureFromWorktreeAdd(key);
11891303return { stdout: `HEAD is now at ${upstreamSha}`, stderr: "", code: 0 };
11901304}
11911305if (
@@ -1300,6 +1414,7 @@ describe("runGatewayUpdate", () => {
13001414key.endsWith(` ${upstreamSha}`) &&
13011415preflightPrefixPattern.test(key)
13021416) {
1417+await writePreflightPackageManagerFixtureFromWorktreeAdd(key);
13031418return { stdout: `HEAD is now at ${upstreamSha}`, stderr: "", code: 0 };
13041419}
13051420if (
@@ -1398,6 +1513,7 @@ describe("runGatewayUpdate", () => {
13981513key.endsWith(` ${upstreamSha}`) &&
13991514preflightPrefixPattern.test(key)
14001515) {
1516+await writePreflightPackageManagerFixtureFromWorktreeAdd(key);
14011517return { stdout: `HEAD is now at ${upstreamSha}`, stderr: "", code: 0 };
14021518}
14031519if (
@@ -1491,6 +1607,7 @@ describe("runGatewayUpdate", () => {
14911607key.endsWith(` ${upstreamSha}`) &&
14921608preflightPrefixPattern.test(key)
14931609) {
1610+await writePreflightPackageManagerFixtureFromWorktreeAdd(key);
14941611return { stdout: `HEAD is now at ${upstreamSha}`, stderr: "", code: 0 };
14951612}
14961613if (
@@ -1577,6 +1694,7 @@ describe("runGatewayUpdate", () => {
15771694key.endsWith(` ${targetSha}`) &&
15781695preflightPrefixPattern.test(key)
15791696) {
1697+await writePreflightPackageManagerFixtureFromWorktreeAdd(key);
15801698return { stdout: `HEAD is now at ${targetSha}`, stderr: "", code: 0 };
15811699}
15821700if (
@@ -1657,6 +1775,7 @@ describe("runGatewayUpdate", () => {
16571775key.endsWith(` ${targetSha}`) &&
16581776preflightPrefixPattern.test(key)
16591777) {
1778+await writePreflightPackageManagerFixtureFromWorktreeAdd(key);
16601779return { stdout: `HEAD is now at ${targetSha}`, stderr: "", code: 0 };
16611780}
16621781if (
@@ -1740,6 +1859,7 @@ describe("runGatewayUpdate", () => {
17401859key.endsWith(` ${targetSha}`) &&
17411860preflightPrefixPattern.test(key)
17421861) {
1862+await writePreflightPackageManagerFixtureFromWorktreeAdd(key);
17431863return { stdout: `HEAD is now at ${targetSha}`, stderr: "", code: 0 };
17441864}
17451865if (
@@ -1814,6 +1934,31 @@ describe("runGatewayUpdate", () => {
18141934if (key === `git -C ${tempDir} rev-list --max-count=10 ${upstreamSha}`) {
18151935return { stdout: `${upstreamSha}\n`, stderr: "", code: 0 };
18161936}
1937+if (
1938+key.startsWith(`git -C ${tempDir} worktree add --detach /tmp/`) &&
1939+key.endsWith(` ${upstreamSha}`) &&
1940+preflightPrefixPattern.test(key)
1941+) {
1942+await writePreflightPackageManagerFixtureFromWorktreeAdd(key);
1943+return { stdout: `HEAD is now at ${upstreamSha}`, stderr: "", code: 0 };
1944+}
1945+if (
1946+key.startsWith("git -C /tmp/") &&
1947+preflightPrefixPattern.test(key) &&
1948+key.includes(" checkout --detach ") &&
1949+key.endsWith(upstreamSha)
1950+) {
1951+return { stdout: "", stderr: "", code: 0 };
1952+}
1953+if (
1954+key.startsWith(`git -C ${tempDir} worktree remove --force /tmp/`) &&
1955+preflightPrefixPattern.test(key)
1956+) {
1957+return { stdout: "", stderr: "", code: 0 };
1958+}
1959+if (key === `git -C ${tempDir} worktree prune`) {
1960+return { stdout: "", stderr: "", code: 0 };
1961+}
18171962if (key === "pnpm --version") {
18181963throw new Error("spawn pnpm ENOENT");
18191964}
@@ -1835,8 +1980,8 @@ describe("runGatewayUpdate", () => {
18351980expect(result.reason).toBe("pnpm-npm-bootstrap-failed");
18361981expect(calls).not.toContain("npm run build");
18371982expect(calls).not.toContain("npm run lint");
1838-const preflightCalls = calls.filter((call) => preflightPrefixPattern.test(call));
1839-expect(preflightCalls).toStrictEqual([]);
1983+expect(calls).not.toContain("npm install");
1984+expect(calls).not.toContain("pnpm install");
18401985});
1841198618421987it("skips update when no git root", async () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。