mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-03 20:13:02 +00:00
fix(bar): keep sticky port across ccs bar stop via launch.json fallback
`ccs bar stop` deletes bar.json, so a bar.json-only sticky port is lost on every stop/start cycle: the next launch reverted to 3000 and the probe could no longer find a server still running on the previously chosen port. resolveBarPort now falls back to the --port recorded in launch.json (written by launch, not deleted by stop), which restores both the sticky port and probe discovery after a stop. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
34608ce291
commit
d27b53f235
2 files changed
+82
-4
No files matched your search
@@ -26,8 +26,11 @@ export interface DashboardInfo {
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the port recorded in an existing bar.json.
|
||||
* Returns null when the file is absent or malformed.
|
||||
* Read the port recorded in an existing bar.json, falling back to the --port
|
||||
* in launch.json's args. `ccs bar stop` deletes bar.json but leaves
|
||||
* launch.json, so the fallback is what keeps the sticky port (and the probe's
|
||||
* ability to find a server on a non-default port) across a stop/start cycle.
|
||||
* Returns null when neither file records a port.
|
||||
*/
|
||||
|
||||
export function resolveBarPort(ccsDir: string): number | null {
|
||||
@@ -35,10 +38,25 @@ export function resolveBarPort(ccsDir: string): number | null {
|
||||
try {
|
||||
const raw = fs.readFileSync(barJsonPath, 'utf8');
|
||||
const parsed = JSON.parse(raw) as Partial<{ port: number }>;
|
||||
return typeof parsed.port === 'number' ? parsed.port : null;
|
||||
if (typeof parsed.port === 'number') return parsed.port;
|
||||
} catch {
|
||||
return null;
|
||||
/* fall through to launch.json */
|
||||
}
|
||||
|
||||
const launchJsonPath = path.join(ccsDir, 'bar', 'launch.json');
|
||||
try {
|
||||
const raw = fs.readFileSync(launchJsonPath, 'utf8');
|
||||
const parsed = JSON.parse(raw) as Partial<{ args: unknown[] }>;
|
||||
const args = Array.isArray(parsed.args) ? parsed.args : [];
|
||||
const idx = args.indexOf('--port');
|
||||
if (idx !== -1 && idx + 1 < args.length) {
|
||||
const n = parseInt(String(args[idx + 1]), 10);
|
||||
if (Number.isFinite(n) && n > 0 && n < 65536) return n;
|
||||
}
|
||||
} catch {
|
||||
/* absent or malformed -> null */
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3814,3 +3814,63 @@ describe('launch: --port selects the server port', () => {
|
||||
expect(seen.spawnPort).toBe(3777);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveBarPort: launch.json fallback survives `ccs bar stop`', () => {
|
||||
// `ccs bar stop` deletes bar.json, so bar.json alone cannot carry the sticky
|
||||
// port across a stop/start cycle. launch.json (refreshed by launch, NOT
|
||||
// deleted by stop) records the port in its args and acts as the fallback.
|
||||
it('falls back to the --port recorded in launch.json when bar.json is absent', async () => {
|
||||
const ccsDir = path.join(tempHome, '.ccs');
|
||||
fs.mkdirSync(path.join(ccsDir, 'bar'), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(ccsDir, 'bar', 'launch.json'),
|
||||
JSON.stringify({
|
||||
schema: 1,
|
||||
runtime: '/usr/bin/node',
|
||||
args: ['/x/ccs.js', 'bar', 'serve', '--port', '3456'],
|
||||
home: tempHome,
|
||||
})
|
||||
);
|
||||
|
||||
const { resolveBarPort } = await loadLaunchSubcommand();
|
||||
expect(resolveBarPort(ccsDir)).toBe(3456);
|
||||
});
|
||||
|
||||
it('bar.json port wins over launch.json when both exist', async () => {
|
||||
const ccsDir = path.join(tempHome, '.ccs');
|
||||
fs.mkdirSync(path.join(ccsDir, 'bar'), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(ccsDir, 'bar.json'),
|
||||
JSON.stringify({ baseUrl: 'http://127.0.0.1:4000', port: 4000, authMode: 'loopback' })
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(ccsDir, 'bar', 'launch.json'),
|
||||
JSON.stringify({
|
||||
schema: 1,
|
||||
runtime: '/usr/bin/node',
|
||||
args: ['/x/ccs.js', 'bar', 'serve', '--port', '3456'],
|
||||
home: tempHome,
|
||||
})
|
||||
);
|
||||
|
||||
const { resolveBarPort } = await loadLaunchSubcommand();
|
||||
expect(resolveBarPort(ccsDir)).toBe(4000);
|
||||
});
|
||||
|
||||
it('returns null when launch.json has no --port and bar.json is absent', async () => {
|
||||
const ccsDir = path.join(tempHome, '.ccs');
|
||||
fs.mkdirSync(path.join(ccsDir, 'bar'), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(ccsDir, 'bar', 'launch.json'),
|
||||
JSON.stringify({
|
||||
schema: 1,
|
||||
runtime: '/usr/bin/node',
|
||||
args: ['/x/ccs.js', 'bar', 'serve'],
|
||||
home: tempHome,
|
||||
})
|
||||
);
|
||||
|
||||
const { resolveBarPort } = await loadLaunchSubcommand();
|
||||
expect(resolveBarPort(ccsDir)).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user