fix(compose): retain an explicit zero log tail (#673)

Compose service logs now pass --tail only for finite values, so an explicit zero reaches Compose as --tail 0, while undefined and NaN omit the flag as before.
This commit is contained in:
Rudy Mizrahi Celekli authored and GitHub committed 2026-10-04 07:27:47 -07:00
1 parent c31fa2689b
commit 7fd8d3f9d3
2 files changed
+42 -1

No files matched your search

@@ -130,7 +130,7 @@ export class ComposeServicesAdapter {
}): Promise<ComposeLogsResult> {
const args = this.baseArgs(opts.composeFile, opts.projectName, opts.profiles);
const serviceArg = opts.service ? ` ${sq(opts.service)}` : "";
const tailArg = opts.tail ? ` --tail ${opts.tail}` : "";
const tailArg = Number.isFinite(opts.tail) ? ` --tail ${opts.tail}` : "";
const cmd = `docker compose ${args} logs${tailArg}${serviceArg} 2>&1`;
try {
const output = await this.exec(cmd);
@@ -0,0 +1,41 @@
import { afterEach, expect, it } from "vitest";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import { ComposeServicesAdapter } from "../src/adapters/compose-services-adapter.js";
const execute = promisify(execFile);
const dirs: string[] = [];
afterEach(() => {
for (const dir of dirs.splice(0)) {
fs.rmSync(dir, { recursive: true, force: true });
}
});
it.skipIf(process.platform === "win32").each([0, 25, undefined, NaN])("preserves explicit zero log tail in native argument dispatch: %s", async (tail) => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "compose-tail-argv-"));
dirs.push(dir);
// Controlled executable records real shell arguments; it never contacts Docker.
fs.writeFileSync(path.join(dir, "docker"), '#!/bin/sh\nprintf "%s\\n" "$@"\n', { mode: 0o700 });
const adapter = new ComposeServicesAdapter(async (command) => {
const { stdout } = await execute("/bin/sh", ["-c", command], { env: { PATH: dir } });
return stdout;
});
const result = await adapter.logs({
composeFile: path.join(dir, "services.yaml"),
projectName: "owned-fixture",
service: "api",
tail,
});
expect(result.ok).toBe(true);
const args = result.output.trim().split("\n");
if (tail === undefined || Number.isNaN(tail)) {
expect(args).not.toContain("--tail");
} else {
const tailIndex = args.indexOf("--tail");
expect(args.slice(tailIndex, tailIndex + 2)).toEqual(["--tail", String(tail)]);
}
expect(args.at(-1)).toBe("api");
});