mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-03 05:27:20 +00:00
fix: refine daemon launchd prep changes
This commit is contained in:
14
AGENTS.md
14
AGENTS.md
@@ -102,20 +102,6 @@
|
||||
- Pure test additions/fixes generally do **not** need a changelog entry unless they alter user-facing behavior or the user asks for one.
|
||||
- Mobile: before using a simulator, check for connected real devices (iOS + Android) and prefer them when available.
|
||||
|
||||
### Local full-CI one-shot record (2026-02-25)
|
||||
|
||||
- Ran local parity sweep for GitHub CI workflows (`workflow-sanity.yml`, `ci.yml`, `install-smoke.yml`, `sandbox-common-smoke.yml`) via `/tmp/run-openclaw-local-ci.sh`.
|
||||
- Artifacts: `/tmp/openclaw-local-ci-20260225-221535.summary`, `/tmp/openclaw-local-ci-20260225-221535.log`.
|
||||
- Result: `PASS=15`, `FAIL=15`, `SKIP=1` (`ci:secrets-zizmor` skipped because no workflow-file diff vs `origin/main`).
|
||||
- `ci.yml` jobs with `if: false` (`deadcode`, `ios`) are disabled in GitHub CI and were not executed.
|
||||
- Main local blockers seen in this run:
|
||||
- `ci:check`: formatting failure in `src/daemon/launchd.ts`.
|
||||
- `ci:checks-node-test` and `ci:macos-ts-tests`: test failures plus Node OOM during full `pnpm test`.
|
||||
- `ci:skills-python-*` / `ci:secrets-*`: Homebrew Python PEP668 (`externally-managed-environment`) and missing `pre-commit`/`detect-secrets`.
|
||||
- `ci:macos-swift*`: missing `swiftlint`/`swiftformat` and Swift `6.2.0` required vs local `6.1.0`.
|
||||
- `ci:android-*`: Android SDK not configured (`ANDROID_HOME` / `apps/android/local.properties`).
|
||||
- `sandbox-common-smoke:common-image`: passed after running the workflow command directly (temp script had unescaped `$u` with `set -u`).
|
||||
|
||||
## Commit & Pull Request Guidelines
|
||||
|
||||
**Full maintainer PR workflow (optional):** If you want the repo's end-to-end maintainer workflow (triage order, quality bar, rebase rules, commit/changelog conventions, co-contributor policy, and the `review-pr` > `prepare-pr` > `merge-pr` pipeline), see `.agents/skills/PR_WORKFLOW.md`. Maintainers may use other workflows; when a maintainer specifies a workflow, follow that. If no workflow is specified, default to PR_WORKFLOW.
|
||||
|
||||
@@ -10,6 +10,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- Daemon/macOS launchd: forward proxy env vars into supervised service environments, switch LaunchAgent keepalive policy to crash-only with throttling, and harden restart sequencing to `print -> bootout -> wait old pid exit -> bootstrap -> kickstart`. (#27276) thanks @frankekn.
|
||||
- Android/Node invoke: remove native gateway WebSocket `Origin` header to avoid false origin rejections, unify invoke command registry/policy/error parsing paths, and keep command availability checks centralized to reduce dispatcher/advertisement drift. (#27257) Thanks @obviyus.
|
||||
- CI/Windows: shard the Windows `checks-windows` test lane into two matrix jobs and honor explicit shard index overrides in `scripts/test-parallel.mjs` to reduce CI critical-path wall time. (#27234) Thanks @joshavant.
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
const state = vi.hoisted(() => ({
|
||||
launchctlCalls: [] as string[][],
|
||||
listOutput: "",
|
||||
printOutput: "",
|
||||
bootstrapError: "",
|
||||
dirs: new Set<string>(),
|
||||
files: new Map<string, string>(),
|
||||
@@ -36,6 +37,9 @@ vi.mock("./exec-file.js", () => ({
|
||||
if (call[0] === "list") {
|
||||
return { stdout: state.listOutput, stderr: "", code: 0 };
|
||||
}
|
||||
if (call[0] === "print") {
|
||||
return { stdout: state.printOutput, stderr: "", code: 0 };
|
||||
}
|
||||
if (call[0] === "bootstrap" && state.bootstrapError) {
|
||||
return { stdout: "", stderr: state.bootstrapError, code: 1 };
|
||||
}
|
||||
@@ -72,6 +76,7 @@ vi.mock("node:fs/promises", async (importOriginal) => {
|
||||
beforeEach(() => {
|
||||
state.launchctlCalls.length = 0;
|
||||
state.listOutput = "";
|
||||
state.printOutput = "";
|
||||
state.bootstrapError = "";
|
||||
state.dirs.clear();
|
||||
state.files.clear();
|
||||
@@ -224,6 +229,42 @@ describe("launchd install", () => {
|
||||
expect(bootstrapIndex).toBeLessThan(kickstartIndex);
|
||||
});
|
||||
|
||||
it("waits for previous launchd pid to exit before bootstrapping", async () => {
|
||||
const env = createDefaultLaunchdEnv();
|
||||
state.printOutput = ["state = running", "pid = 4242"].join("\n");
|
||||
const killSpy = vi.spyOn(process, "kill");
|
||||
killSpy
|
||||
.mockImplementationOnce(() => true)
|
||||
.mockImplementationOnce(() => {
|
||||
const err = new Error("no such process") as NodeJS.ErrnoException;
|
||||
err.code = "ESRCH";
|
||||
throw err;
|
||||
});
|
||||
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
const restartPromise = restartLaunchAgent({
|
||||
env,
|
||||
stdout: new PassThrough(),
|
||||
});
|
||||
await vi.advanceTimersByTimeAsync(250);
|
||||
await restartPromise;
|
||||
expect(killSpy).toHaveBeenCalledWith(4242, 0);
|
||||
const domain = typeof process.getuid === "function" ? `gui/${process.getuid()}` : "gui/501";
|
||||
const label = "ai.openclaw.gateway";
|
||||
const bootoutIndex = state.launchctlCalls.findIndex(
|
||||
(c) => c[0] === "bootout" && c[1] === `${domain}/${label}`,
|
||||
);
|
||||
const bootstrapIndex = state.launchctlCalls.findIndex((c) => c[0] === "bootstrap");
|
||||
expect(bootoutIndex).toBeGreaterThanOrEqual(0);
|
||||
expect(bootstrapIndex).toBeGreaterThanOrEqual(0);
|
||||
expect(bootoutIndex).toBeLessThan(bootstrapIndex);
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
killSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it("shows actionable guidance when launchctl gui domain does not support bootstrap", async () => {
|
||||
state.bootstrapError = "Bootstrap failed: 125: Domain does not support specified action";
|
||||
const env = createDefaultLaunchdEnv();
|
||||
|
||||
Reference in New Issue
Block a user