mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-05 02:43:32 +00:00
fix(ci-setup): probe the resolved --port for availability
detectEnvironment ran checkPortAvailable(4747) eagerly, before options were parsed, and the result was printed as a hardcoded "Port 4747: available" line — wrong/irrelevant whenever the user passed a different --port. Export checkPortAvailable, drop the eager hardcoded probe (and the now-unused portAvailable field from DetectResult), and probe resolved.port after option resolution, reporting the actual port. This also removes a latent real-socket bind on 4747 from the default unit-test path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e87e3a4122
commit
847e901e39
5 changed files with 39 additions and 12 deletions
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { detectEnvironment } from './ci-setup/detect.js';
|
||||
import { detectEnvironment, checkPortAvailable } from './ci-setup/detect.js';
|
||||
import { resolveOptions } from './ci-setup/prompts.js';
|
||||
import { generateFiles } from './ci-setup/templates.js';
|
||||
import type { CiSetupOptions, CiSetupResult, GeneratedFile } from './ci-setup/types.js';
|
||||
|
|
@ -68,9 +68,6 @@ export const ciSetupCommand = async (options?: {
|
|||
if (detect.hasDocker) {
|
||||
console.log(' ✓ Docker: docker-compose or Dockerfile found');
|
||||
}
|
||||
console.log(
|
||||
` ${detect.portAvailable ? '✓' : '⚠'} Port 4747: ${detect.portAvailable ? 'available' : 'in use (serve may already be running)'}`,
|
||||
);
|
||||
console.log(' ⚠ License: PolyForm-Noncommercial — confirm non-commercial use\n');
|
||||
|
||||
// Parse and validate options from commander flags. An explicitly-passed but
|
||||
|
|
@ -105,6 +102,14 @@ export const ciSetupCommand = async (options?: {
|
|||
}
|
||||
|
||||
const resolved = await resolveOptions(detect, partial);
|
||||
|
||||
// Probe the *resolved* port (now that --port / prompts are settled) rather
|
||||
// than a hardcoded default at detection time.
|
||||
const portAvailable = await checkPortAvailable(resolved.port);
|
||||
console.log(
|
||||
` ${portAvailable ? '✓' : '⚠'} Port ${resolved.port}: ${portAvailable ? 'available' : 'in use (serve may already be running)'}`,
|
||||
);
|
||||
|
||||
const files = generateFiles(resolved, detect);
|
||||
|
||||
const result: CiSetupResult = { generated: [], skipped: [], errors: [] };
|
||||
|
|
|
|||
|
|
@ -105,7 +105,12 @@ async function detectPrimaryLanguage(repoRoot: string): Promise<string> {
|
|||
return top;
|
||||
}
|
||||
|
||||
function checkPortAvailable(port: number): Promise<boolean> {
|
||||
/**
|
||||
* Whether `port` can be bound on the loopback interface. Exported so the caller
|
||||
* can probe the *resolved* --port (known only after option parsing) rather than
|
||||
* a hardcoded default at detection time.
|
||||
*/
|
||||
export function checkPortAvailable(port: number): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
const server = net.createServer();
|
||||
server.once('error', () => resolve(false));
|
||||
|
|
@ -124,17 +129,15 @@ export async function detectEnvironment(cwd: string): Promise<DetectResult> {
|
|||
gitRoot: null,
|
||||
detectedCi: null,
|
||||
hasDocker: false,
|
||||
portAvailable: false,
|
||||
primaryLanguage: 'Unknown',
|
||||
};
|
||||
}
|
||||
|
||||
const [detectedCi, hasDocker, portAvailable, primaryLanguage] = await Promise.all([
|
||||
const [detectedCi, hasDocker, primaryLanguage] = await Promise.all([
|
||||
detectCiSystem(gitRoot),
|
||||
detectDocker(gitRoot),
|
||||
checkPortAvailable(4747),
|
||||
detectPrimaryLanguage(gitRoot),
|
||||
]);
|
||||
|
||||
return { gitRoot, detectedCi, hasDocker, portAvailable, primaryLanguage };
|
||||
return { gitRoot, detectedCi, hasDocker, primaryLanguage };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ export interface DetectResult {
|
|||
gitRoot: string | null;
|
||||
detectedCi: CiSystem | null;
|
||||
hasDocker: boolean;
|
||||
portAvailable: boolean;
|
||||
primaryLanguage: string;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ const DEFAULT_DETECT: DetectResult = {
|
|||
gitRoot: '/repo',
|
||||
detectedCi: 'github-actions',
|
||||
hasDocker: true,
|
||||
portAvailable: true,
|
||||
primaryLanguage: 'TypeScript',
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -231,6 +231,28 @@ describe('ciSetupCommand', () => {
|
|||
const entries = await fs.readdir(tempDir);
|
||||
expect(entries).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('probes the resolved --port for availability (not a hardcoded 4747)', async () => {
|
||||
vi.resetModules();
|
||||
vi.doMock('../../src/cli/ci-setup/detect.js', async () => {
|
||||
const actual = await vi.importActual<typeof import('../../src/cli/ci-setup/detect.js')>(
|
||||
'../../src/cli/ci-setup/detect.js',
|
||||
);
|
||||
return { ...actual, checkPortAvailable: vi.fn(() => Promise.resolve(true)) };
|
||||
});
|
||||
const detectMod = await import('../../src/cli/ci-setup/detect.js');
|
||||
const { ciSetupCommand } = await import('../../src/cli/ci-setup.js');
|
||||
await ciSetupCommand({
|
||||
ci: 'github-actions',
|
||||
deploy: 'docker',
|
||||
auth: 'token',
|
||||
port: '4748',
|
||||
dryRun: true,
|
||||
outputDir: tempDir,
|
||||
});
|
||||
expect(vi.mocked(detectMod.checkPortAvailable)).toHaveBeenCalledWith(4748);
|
||||
vi.doUnmock('../../src/cli/ci-setup/detect.js');
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveOptions (U1: interactive prompts reachable)', () => {
|
||||
|
|
@ -238,7 +260,6 @@ describe('resolveOptions (U1: interactive prompts reachable)', () => {
|
|||
gitRoot: '/tmp/repo',
|
||||
detectedCi: null,
|
||||
hasDocker: false,
|
||||
portAvailable: true,
|
||||
primaryLanguage: 'TypeScript',
|
||||
};
|
||||
let originalTTY: boolean | undefined;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue