fix(cli): detect symlinked entrypoints (#652)
This commit is contained in:
@@ -0,0 +1,38 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { fileURLToPath, pathToFileURL } from 'url';
|
||||||
|
|
||||||
|
function normalizeCliEntryPath(filePath, realpath = fs.realpathSync) {
|
||||||
|
if (typeof filePath !== 'string' || filePath.trim().length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolvedPath = path.resolve(filePath);
|
||||||
|
try {
|
||||||
|
return realpath(resolvedPath);
|
||||||
|
} catch {
|
||||||
|
return resolvedPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isModuleCliExecution(entryPath = process.argv[1], moduleUrl = import.meta.url, realpath = fs.realpathSync) {
|
||||||
|
if (typeof entryPath !== 'string' || entryPath.trim().length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const normalizedEntryPath = normalizeCliEntryPath(entryPath, realpath);
|
||||||
|
const normalizedModulePath = normalizeCliEntryPath(fileURLToPath(moduleUrl), realpath);
|
||||||
|
if (!normalizedEntryPath || !normalizedModulePath) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return pathToFileURL(normalizedEntryPath).href === pathToFileURL(normalizedModulePath).href;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
normalizeCliEntryPath,
|
||||||
|
isModuleCliExecution,
|
||||||
|
};
|
||||||
+2
-11
@@ -7,6 +7,7 @@ import path from 'path';
|
|||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import { spawn, spawnSync } from 'child_process';
|
import { spawn, spawnSync } from 'child_process';
|
||||||
import { fileURLToPath, pathToFileURL } from 'url';
|
import { fileURLToPath, pathToFileURL } from 'url';
|
||||||
|
import { isModuleCliExecution } from './cli-entry.js';
|
||||||
import { cloudflareTunnelProviderCapabilities } from '../server/lib/tunnels/providers/cloudflare.js';
|
import { cloudflareTunnelProviderCapabilities } from '../server/lib/tunnels/providers/cloudflare.js';
|
||||||
import {
|
import {
|
||||||
intro as clackIntro, outro as clackOutro, log as clackLog,
|
intro as clackIntro, outro as clackOutro, log as clackLog,
|
||||||
@@ -4614,17 +4615,7 @@ async function main() {
|
|||||||
await commands[command](options);
|
await commands[command](options);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isCliExecution = (() => {
|
const isCliExecution = isModuleCliExecution();
|
||||||
const entry = process.argv[1];
|
|
||||||
if (typeof entry !== 'string' || entry.length === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return pathToFileURL(path.resolve(entry)).href === import.meta.url;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
if (isCliExecution) {
|
if (isCliExecution) {
|
||||||
let isHandlingSigint = false;
|
let isHandlingSigint = false;
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { describe, expect, it } from 'bun:test';
|
||||||
|
import path from 'path';
|
||||||
|
import { pathToFileURL } from 'url';
|
||||||
|
|
||||||
|
import { isModuleCliExecution, normalizeCliEntryPath } from './cli-entry.js';
|
||||||
|
|
||||||
|
describe('cli entry detection', () => {
|
||||||
|
const modulePath = '/tmp/openchamber/bin/cli.js';
|
||||||
|
const moduleUrl = pathToFileURL(modulePath).href;
|
||||||
|
|
||||||
|
it('resolves symlinked entry paths before comparing', () => {
|
||||||
|
const symlinkPath = '/usr/local/bin/openchamber';
|
||||||
|
const realpath = (filePath) => {
|
||||||
|
if (filePath === path.resolve(symlinkPath)) {
|
||||||
|
return modulePath;
|
||||||
|
}
|
||||||
|
return filePath;
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(isModuleCliExecution(symlinkPath, moduleUrl, realpath)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('falls back to resolved paths when realpath fails', () => {
|
||||||
|
const realpath = () => {
|
||||||
|
throw new Error('realpath unavailable');
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(isModuleCliExecution(modulePath, moduleUrl, realpath)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false for non-matching entry path', () => {
|
||||||
|
expect(isModuleCliExecution('/tmp/other-cli.js', moduleUrl)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false for empty entry path', () => {
|
||||||
|
expect(isModuleCliExecution('', moduleUrl)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('normalizes direct paths when realpath fails', () => {
|
||||||
|
const unresolvedPath = './packages/web/bin/cli.js';
|
||||||
|
const realpath = () => {
|
||||||
|
throw new Error('no symlink resolution');
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(normalizeCliEntryPath(unresolvedPath, realpath)).toBe(path.resolve(unresolvedPath));
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user