Files
gstack/test/cso-docker-socket-realpath.test.ts
50d020ad9d v1.91.85.0 fix(cso,deploy): /cso on Windows Git 2.56 and macOS Docker, zsh-safe loops, honest deploy stops (Wave 9 Lane D) (#3134)
/cso sets GIT_CONFIG_GLOBAL=/dev/null on every platform (Git for Windows 2.56
rejects NUL), shared by childEnvironment and the eval and producer Git
environments, with a Windows-safe test that `git config --global --list` is
empty. On macOS the local Docker socket resolves without opening it when Bun's
realpathSync throws EOPNOTSUPP; Linux admission and the local-Unix-only rule
are unchanged. /test-audit and /ship loop over file lists zsh does not
word-split, with a static test against `for x in $VAR` in generated bash.
/setup-deploy stops and writes nothing when nothing is deployed yet;
/land-and-deploy runs a recorded command trigger only with explicit approval
before claiming revision proof. TODOS.md records the open P2/P3 bugs by area,
the Wave 9 parked items and the feature themes.

Closes #3082. Adopts #3094, #3095, #3096 and #3109.

Co-authored-by: marcode <244097767+vilano93@users.noreply.github.com>
Co-authored-by: Nathan <196341+nathanshox@users.noreply.github.com>
Co-authored-by: mikehasa <91774998+mikehasa@users.noreply.github.com>
Co-authored-by: Andrey <95643518+Alfabit7@users.noreply.github.com>
2026-10-11 15:43:33 +00:00

119 lines
4.6 KiB
TypeScript

import { afterAll, afterEach, beforeAll, describe, expect, spyOn, test } from 'bun:test';
import * as fs from 'node:fs';
import * as net from 'node:net';
import * as os from 'node:os';
import { join } from 'node:path';
import { realpathSocket } from '../lib/cso/docker';
describe.skipIf(process.platform === 'win32')('CSO Docker socket path resolution', () => {
let root = '';
let socketPath = '';
let server: net.Server;
beforeAll(async () => {
root = fs.mkdtempSync(join(os.tmpdir(), 'cso-sock-'));
socketPath = join(root, 'docker.sock');
server = net.createServer();
await new Promise<void>((resolve, reject) => {
server.once('error', reject);
server.listen(socketPath, () => resolve());
});
});
afterAll(async () => {
await new Promise<void>((resolve) => server.close(() => resolve()));
fs.rmSync(root, { recursive: true, force: true });
});
test('resolves a Unix socket that realpath cannot open', () => {
const real = realpathSocket(socketPath);
expect(fs.statSync(real).isSocket()).toBe(true);
expect(real).toBe(join(fs.realpathSync(root), 'docker.sock'));
});
test('follows absolute and relative symbolic links down to the socket', () => {
const absoluteLink = join(root, 'absolute.sock');
fs.symlinkSync(socketPath, absoluteLink);
const relativeLink = join(root, 'relative.sock');
fs.symlinkSync('absolute.sock', relativeLink);
const expected = realpathSocket(socketPath);
expect(realpathSocket(absoluteLink)).toBe(expected);
expect(realpathSocket(relativeLink)).toBe(expected);
});
test('resolves a socket through a symlinked parent directory', () => {
const linkedDirectory = join(root, 'run');
fs.symlinkSync(root, linkedDirectory);
expect(realpathSocket(join(linkedDirectory, 'docker.sock'))).toBe(realpathSocket(socketPath));
});
test('still reports a missing socket as an error', () => {
expect(() => realpathSocket(join(root, 'missing.sock'))).toThrow();
});
test('matches realpath for ordinary files', () => {
const file = join(root, 'plain');
fs.writeFileSync(file, 'x');
expect(realpathSocket(file)).toBe(fs.realpathSync(file));
});
});
// On macOS Bun's own realpath fails on a socket, so the suite above already
// exercises the fallback there. Linux resolves sockets natively, so this block
// forces the failure to keep the fallback covered on every CI platform.
describe.skipIf(process.platform === 'win32')(
'CSO Docker socket path resolution when realpath refuses sockets',
() => {
let root = '';
let socketPath = '';
let server: net.Server;
const realpath = fs.realpathSync;
const refusing = (code: string) =>
((path: fs.PathLike, ...rest: any[]) => {
if (fs.statSync(path).isSocket()) {
const error = new Error(`${code}: operation not supported on socket`) as NodeJS.ErrnoException;
error.code = code;
throw error;
}
return realpath(path, ...rest);
}) as typeof fs.realpathSync;
beforeAll(async () => {
root = fs.mkdtempSync(join(os.tmpdir(), 'cso-sock-stub-'));
socketPath = join(root, 'docker.sock');
server = net.createServer();
await new Promise<void>((resolve, reject) => {
server.once('error', reject);
server.listen(socketPath, () => resolve());
});
});
afterAll(async () => {
await new Promise<void>((resolve) => server.close(() => resolve()));
fs.rmSync(root, { recursive: true, force: true });
});
afterEach(() => {
(fs.realpathSync as any).mockRestore?.();
});
test('falls back to resolving the leaf by hand on EOPNOTSUPP', () => {
const stub = spyOn(fs, 'realpathSync').mockImplementation(refusing('EOPNOTSUPP'));
expect(realpathSocket(socketPath)).toBe(join(realpath(root), 'docker.sock'));
expect(stub).toHaveBeenCalledWith(socketPath);
expect(stub.mock.calls.length).toBeGreaterThan(1);
});
test('still follows symbolic links to the socket on EOPNOTSUPP', () => {
spyOn(fs, 'realpathSync').mockImplementation(refusing('EOPNOTSUPP'));
const link = join(root, 'link.sock');
fs.symlinkSync('docker.sock', link);
const directory = join(root, 'run');
fs.symlinkSync(root, directory);
const expected = join(realpath(root), 'docker.sock');
expect(realpathSocket(link)).toBe(expected);
expect(realpathSocket(join(directory, 'docker.sock'))).toBe(expected);
});
test('propagates any other realpath error unchanged', () => {
spyOn(fs, 'realpathSync').mockImplementation(refusing('EACCES'));
expect(() => realpathSocket(socketPath)).toThrow(/EACCES/);
});
},
);