fix(cli): report mismatched service status
This commit is contained in:
parent
0eaa75ec0f
commit
2ff3f43d59
3 changed files with 80 additions and 2 deletions
|
|
@ -8,7 +8,8 @@ import { ServiceConfig } from "../../../services/service-config"
|
||||||
export default Runtime.handler(
|
export default Runtime.handler(
|
||||||
Commands.commands.service.commands.status,
|
Commands.commands.service.commands.status,
|
||||||
Effect.fn("cli.service.status")(function* () {
|
Effect.fn("cli.service.status")(function* () {
|
||||||
const found = yield* Service.discover(yield* ServiceConfig.options())
|
const options = yield* ServiceConfig.options()
|
||||||
process.stdout.write((found ? found.url : "stopped") + EOL)
|
const found = yield* Service.status(options)
|
||||||
|
process.stdout.write(JSON.stringify({ ...found, clientVersion: options.version }, null, 2) + EOL)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { Database } from "@opencode-ai/core/database/database"
|
||||||
import { EventV2 } from "@opencode-ai/core/event"
|
import { EventV2 } from "@opencode-ai/core/event"
|
||||||
import { EventTable } from "@opencode-ai/core/event/sql"
|
import { EventTable } from "@opencode-ai/core/event/sql"
|
||||||
import { Global } from "@opencode-ai/core/global"
|
import { Global } from "@opencode-ai/core/global"
|
||||||
|
import { InstallationVersion } from "@opencode-ai/core/installation/version"
|
||||||
import { Project } from "@opencode-ai/core/project"
|
import { Project } from "@opencode-ai/core/project"
|
||||||
import { ProjectTable } from "@opencode-ai/core/project/sql"
|
import { ProjectTable } from "@opencode-ai/core/project/sql"
|
||||||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||||
|
|
@ -35,6 +36,60 @@ test("local channel stores service config with the local service filename", asyn
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("service status reports a healthy version-mismatched server", async () => {
|
||||||
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-service-status-"))
|
||||||
|
const version = "0.0.0-older"
|
||||||
|
const server = Bun.serve({
|
||||||
|
hostname: "127.0.0.1",
|
||||||
|
port: 0,
|
||||||
|
fetch(request) {
|
||||||
|
if (new URL(request.url).pathname !== "/api/health") return new Response("Not found", { status: 404 })
|
||||||
|
return Response.json({ healthy: true, version, pid: process.pid })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const env = {
|
||||||
|
...process.env,
|
||||||
|
HOME: root,
|
||||||
|
OPENCODE_TEST_HOME: root,
|
||||||
|
XDG_CACHE_HOME: path.join(root, "cache"),
|
||||||
|
XDG_CONFIG_HOME: path.join(root, "config"),
|
||||||
|
XDG_DATA_HOME: path.join(root, "data"),
|
||||||
|
XDG_STATE_HOME: path.join(root, "state"),
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const registration = path.join(root, "state", "opencode", "service-local.json")
|
||||||
|
await fs.mkdir(path.dirname(registration), { recursive: true })
|
||||||
|
await Bun.write(
|
||||||
|
registration,
|
||||||
|
JSON.stringify({ id: "older-service", version, url: server.url.toString(), pid: process.pid }),
|
||||||
|
)
|
||||||
|
const child = Bun.spawn([process.execPath, path.join(import.meta.dir, "../src/index.ts"), "service", "status"], {
|
||||||
|
env,
|
||||||
|
stdout: "pipe",
|
||||||
|
stderr: "pipe",
|
||||||
|
})
|
||||||
|
const [stdout, stderr, exitCode] = await Promise.all([
|
||||||
|
new Response(child.stdout).text(),
|
||||||
|
new Response(child.stderr).text(),
|
||||||
|
child.exited,
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(exitCode, stderr).toBe(0)
|
||||||
|
expect(JSON.parse(stdout)).toEqual({
|
||||||
|
status: "running",
|
||||||
|
url: server.url.toString(),
|
||||||
|
pid: process.pid,
|
||||||
|
version,
|
||||||
|
compatible: false,
|
||||||
|
clientVersion: InstallationVersion,
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
server.stop(true)
|
||||||
|
await fs.rm(root, { recursive: true, force: true })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
test("concurrent service processes elect one server", async () => {
|
test("concurrent service processes elect one server", async () => {
|
||||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-service-election-"))
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-service-election-"))
|
||||||
const database = path.join(root, "opencode.db")
|
const database = path.join(root, "opencode.db")
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,28 @@ export type StartOptions = Options & {
|
||||||
readonly onStart?: (reason: StartReason) => void
|
readonly onStart?: (reason: StartReason) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Status =
|
||||||
|
| { readonly status: "stopped" }
|
||||||
|
| {
|
||||||
|
readonly status: "running"
|
||||||
|
readonly url: string
|
||||||
|
readonly pid: number
|
||||||
|
readonly version?: string
|
||||||
|
readonly compatible: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export const status = Effect.fn("service.status")(function* (options: Options = {}) {
|
||||||
|
const found = yield* discoverLocal({ ...options, version: undefined })
|
||||||
|
if (found === undefined) return { status: "stopped" } as const
|
||||||
|
return {
|
||||||
|
status: "running",
|
||||||
|
url: found.endpoint.url,
|
||||||
|
pid: found.info.pid,
|
||||||
|
version: found.info.version,
|
||||||
|
compatible: options.version === undefined || found.info.version === options.version,
|
||||||
|
} satisfies Status
|
||||||
|
})
|
||||||
|
|
||||||
// Read-only lookup: registration file plus health check and version gate.
|
// Read-only lookup: registration file plus health check and version gate.
|
||||||
// Never spawns; escalation to start() is the caller's policy.
|
// Never spawns; escalation to start() is the caller's policy.
|
||||||
export const discover = Effect.fn("service.discover")(function* (options: Options = {}) {
|
export const discover = Effect.fn("service.discover")(function* (options: Options = {}) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue