Skip to content

Commit d3519ce

Browse files
committed
fix: keep configured plugin repair scoped
1 parent 1885f05 commit d3519ce

3 files changed

Lines changed: 84 additions & 1 deletion

File tree

scripts/e2e/lib/upgrade-survivor/run.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ FAILURE_PHASE=""
4141
FAILURE_MESSAGE=""
4242
gateway_pid=""
4343
clawhub_fixture_pid=""
44+
configured_plugin_installs_clawhub_fixture_owned=""
4445
baseline_spec=""
4546
baseline_version=""
4647
baseline_version_expected="0"
@@ -288,6 +289,7 @@ configured_plugin_installs_enabled() {
288289

289290
start_configured_plugin_installs_clawhub_fixture() {
290291
configured_plugin_installs_enabled || return 0
292+
configured_plugin_installs_clawhub_fixture_owned=""
291293
if [ -n "${OPENCLAW_CLAWHUB_URL:-}" ] || [ -n "${CLAWHUB_URL:-}" ]; then
292294
return 0
293295
fi
@@ -318,6 +320,7 @@ NODE
318320
for _ in $(seq 1 100); do
319321
if [ -s "$port_file" ]; then
320322
export OPENCLAW_CLAWHUB_URL="http://127.0.0.1:$(cat "$port_file")"
323+
configured_plugin_installs_clawhub_fixture_owned="1"
321324
echo "Configured plugin install scenario using ClawHub 404 fixture: $OPENCLAW_CLAWHUB_URL"
322325
return 0
323326
fi
@@ -329,6 +332,9 @@ NODE
329332

330333
assert_configured_plugin_installs_clawhub_attempted() {
331334
configured_plugin_installs_enabled || return 0
335+
if [ "${configured_plugin_installs_clawhub_fixture_owned:-}" != "1" ]; then
336+
return 0
337+
fi
332338
local requests_file="$ARTIFACT_ROOT/clawhub-not-found-requests.jsonl"
333339
if ! grep -q '/api/v1/packages/%40openclaw%2Fmatrix' "$requests_file" 2>/dev/null; then
334340
echo "configured plugin install scenario did not attempt ClawHub for @openclaw/matrix" >&2

src/flows/doctor-health-contributions.test.ts

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,32 @@
1-
import { describe, expect, it } from "vitest";
1+
import { beforeEach, describe, expect, it, vi } from "vitest";
22
import {
33
resolveDoctorHealthContributions,
44
shouldSkipLegacyUpdateDoctorConfigWrite,
55
} from "./doctor-health-contributions.js";
66

7+
const mocks = vi.hoisted(() => ({
8+
maybeRunConfiguredPluginInstallReleaseStep: vi.fn(),
9+
note: vi.fn(),
10+
}));
11+
12+
vi.mock("../commands/doctor/shared/release-configured-plugin-installs.js", () => ({
13+
maybeRunConfiguredPluginInstallReleaseStep: mocks.maybeRunConfiguredPluginInstallReleaseStep,
14+
}));
15+
16+
vi.mock("../terminal/note.js", () => ({
17+
note: mocks.note,
18+
}));
19+
20+
vi.mock("../version.js", () => ({
21+
VERSION: "2026.5.2-test",
22+
}));
23+
724
describe("doctor health contributions", () => {
25+
beforeEach(() => {
26+
mocks.maybeRunConfiguredPluginInstallReleaseStep.mockReset();
27+
mocks.note.mockReset();
28+
});
29+
830
it("runs release configured plugin install repair before plugin registry and final config writes", () => {
931
const ids = resolveDoctorHealthContributions().map((entry) => entry.id);
1032

@@ -15,6 +37,58 @@ describe("doctor health contributions", () => {
1537
);
1638
expect(ids.indexOf("doctor:plugin-registry")).toBeLessThan(ids.indexOf("doctor:write-config"));
1739
});
40+
41+
it("keeps release configured plugin installs repair-only", async () => {
42+
const contribution = resolveDoctorHealthContributions().find(
43+
(entry) => entry.id === "doctor:release-configured-plugin-installs",
44+
);
45+
expect(contribution).toBeDefined();
46+
const ctx = {
47+
cfg: {},
48+
configResult: { cfg: {}, sourceLastTouchedVersion: "2026.4.29" },
49+
sourceConfigValid: true,
50+
prompter: { shouldRepair: false },
51+
env: {},
52+
} as Parameters<NonNullable<typeof contribution>["run"]>[0];
53+
54+
await contribution?.run(ctx);
55+
56+
expect(mocks.maybeRunConfiguredPluginInstallReleaseStep).not.toHaveBeenCalled();
57+
expect(mocks.note).not.toHaveBeenCalled();
58+
});
59+
60+
it("stamps release configured plugin installs after repair changes", async () => {
61+
mocks.maybeRunConfiguredPluginInstallReleaseStep.mockResolvedValue({
62+
changes: ["Installed configured plugin matrix."],
63+
warnings: [],
64+
touchedConfig: true,
65+
});
66+
const contribution = resolveDoctorHealthContributions().find(
67+
(entry) => entry.id === "doctor:release-configured-plugin-installs",
68+
);
69+
expect(contribution).toBeDefined();
70+
const ctx = {
71+
cfg: {},
72+
configResult: { cfg: {}, sourceLastTouchedVersion: "2026.4.29" },
73+
sourceConfigValid: true,
74+
prompter: { shouldRepair: true },
75+
env: {},
76+
} as Parameters<NonNullable<typeof contribution>["run"]>[0];
77+
78+
await contribution?.run(ctx);
79+
80+
expect(mocks.maybeRunConfiguredPluginInstallReleaseStep).toHaveBeenCalledWith({
81+
cfg: {},
82+
env: {},
83+
touchedVersion: "2026.4.29",
84+
});
85+
expect(mocks.note).toHaveBeenCalledWith(
86+
"Installed configured plugin matrix.",
87+
"Doctor changes",
88+
);
89+
expect(ctx.cfg.meta?.lastTouchedVersion).toBe("2026.5.2-test");
90+
});
91+
1892
it("checks command owner configuration before final config writes", () => {
1993
const ids = resolveDoctorHealthContributions().map((entry) => entry.id);
2094

src/flows/doctor-health-contributions.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,9 @@ async function runReleaseConfiguredPluginInstallsHealth(
275275
if (!ctx.sourceConfigValid) {
276276
return;
277277
}
278+
if (!ctx.prompter.shouldRepair) {
279+
return;
280+
}
278281
const { maybeRunConfiguredPluginInstallReleaseStep } =
279282
await import("../commands/doctor/shared/release-configured-plugin-installs.js");
280283
const { note } = await import("../terminal/note.js");

0 commit comments

Comments
 (0)