Skip to content

Commit b926687

Browse files
committed
fix(cli): name the docker endpoint by scheme in probe debug output
1 parent 0fbf888 commit b926687

2 files changed

Lines changed: 43 additions & 5 deletions

File tree

apps/cli/src/command-internal/db-bootstrap/local-db-running.integration.test.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,10 +303,21 @@ describe("LocalDockerEngine (direct Engine-API transport)", () => {
303303
l.includes("/containers/supabase_db_engine-probe/json"),
304304
),
305305
).toBe(true);
306-
yield* withDockerHost("ssh://user@remote-host", traced);
306+
// Plainly, then with a password a URL parser mis-locates, then with
307+
// no scheme for one to anchor on at all.
308+
yield* withDockerHost("ssh://user:hunter2@remote-host", traced);
307309
expect(
308-
lines.some((l) => l.startsWith("debug:") && l.includes("not directly addressable")),
310+
lines.some(
311+
(l) => l.startsWith("debug:") && l.includes("not directly addressable (ssh)"),
312+
),
309313
).toBe(true);
314+
expect(lines.some((l) => l.includes("hunter2") || l.includes("remote-host"))).toBe(false);
315+
yield* withDockerHost("tcp://user:#hunter3@10.0.0.5:2376", traced);
316+
expect(lines.some((l) => l.includes("not directly addressable (tcp)"))).toBe(true);
317+
expect(lines.some((l) => l.includes("hunter3") || l.includes("10.0.0.5"))).toBe(false);
318+
yield* withDockerHost("deploy:hunter4@10.0.0.5:2376", traced);
319+
expect(lines.some((l) => l.includes("not directly addressable (no scheme)"))).toBe(true);
320+
expect(lines.some((l) => l.includes("hunter4") || l.includes("deploy"))).toBe(false);
310321
const missingDir = mkdtempSync(join(tmpdir(), "ldbgone-"));
311322
yield* withDockerHost(`unix://${join(missingDir, "never-created.sock")}`, traced).pipe(
312323
Effect.ensuring(

apps/cli/src/command-internal/db-bootstrap/local-db-running.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,32 @@ export function dockerEndpointSocketPath(endpoint: string): string | undefined {
7272
return undefined;
7373
}
7474

75+
/**
76+
* How the endpoint is named in {@link localDockerEngineLayer}'s decline line:
77+
* by its scheme alone.
78+
*
79+
* That is the whole reason the probe declined — `tcp`, `ssh` and `fd` are not
80+
* local sockets — and it is the one part of a `DOCKER_HOST` that cannot carry
81+
* a credential. The rest can: `ssh://user:secret@host` is a supported spelling,
82+
* and once a password holds an unencoded `@`, `/`, `?` or `#` there is no
83+
* telling it apart from an ordinary host and path, so none of it is printed.
84+
*/
85+
function describeEndpoint(endpoint: string | undefined): string {
86+
if (endpoint === undefined) {
87+
return "unresolved context";
88+
}
89+
// Anchored on `://`, so a value with no scheme cannot report its own first
90+
// segment as one — that segment is the username in `user:secret@host`.
91+
const scheme = /^[a-zA-Z][a-zA-Z0-9+.-]*(?=:\/\/)/.exec(endpoint);
92+
if (scheme === null) {
93+
return "no scheme";
94+
}
95+
const name = scheme[0].toLowerCase();
96+
// A socket scheme only reaches the decline branch with nothing after it, so
97+
// naming it alone would read as a contradiction.
98+
return name === "unix" || name === "npipe" ? `${name}, no socket path` : name;
99+
}
100+
75101
/**
76102
* Socket-inactivity deadline: a connected-but-silent endpoint degrades to the
77103
* container-CLI fallback instead of parking the command (#6110's hang shape).
@@ -206,8 +232,9 @@ const inspectContainerOverSocket = (
206232
* `docker` CLI itself would (`DOCKER_HOST` -> context store -> platform
207233
* default), inside `Effect.suspend` so every execution sees the current
208234
* environment. With `DebugLogger` provided (the db families provide it), the
209-
* probe's endpoint and fallback decisions surface under `--debug` — otherwise
210-
* this is the one HTTP call the debug side channel cannot see.
235+
* probe's socket and fallback decisions surface under `--debug` — otherwise
236+
* this is the one HTTP call the debug side channel cannot see. An endpoint it
237+
* cannot address is named by {@link describeEndpoint}, never printed.
211238
*/
212239
export const localDockerEngineLayer: Layer.Layer<LocalDockerEngine> = Layer.effect(
213240
LocalDockerEngine,
@@ -225,7 +252,7 @@ export const localDockerEngineLayer: Layer.Layer<LocalDockerEngine> = Layer.effe
225252
endpoint === undefined ? undefined : dockerEndpointSocketPath(endpoint);
226253
if (socketPath === undefined) {
227254
return debug(
228-
`local db engine probe: endpoint not directly addressable (${endpoint === undefined ? "unresolved context" : redactHttpUrl(endpoint)}) — using the container CLI`,
255+
`local db engine probe: endpoint not directly addressable (${describeEndpoint(endpoint)}) — using the container CLI`,
229256
).pipe(Effect.as(Option.none()));
230257
}
231258
return httpLine(`${endpoint}/containers/${containerId}/json`).pipe(

0 commit comments

Comments
 (0)