Skip to content

Commit 9eb4b2a

Browse files
committed
More fixes for extension
1 parent 159b683 commit 9eb4b2a

7 files changed

Lines changed: 97 additions & 26 deletions

File tree

extensions/vscode/src/server/pack-installer.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -274,10 +274,16 @@ export class PackInstaller extends DisposableObject {
274274
}
275275
}
276276
const attemptCount = languages.length - skippedCount;
277-
const skippedSuffix = skippedCount > 0 ? `, ${skippedCount} skipped` : '';
278-
this.logger.info(
279-
`Bundled pack install complete: ${successCount}/${attemptCount} languages succeeded${skippedSuffix}.`,
280-
);
277+
if (attemptCount === 0) {
278+
this.logger.info(
279+
`Bundled pack install complete: no bundled packs found, ${skippedCount} skipped.`,
280+
);
281+
} else {
282+
const skippedSuffix = skippedCount > 0 ? `, ${skippedCount} skipped` : '';
283+
this.logger.info(
284+
`Bundled pack install complete: ${successCount}/${attemptCount} languages succeeded${skippedSuffix}.`,
285+
);
286+
}
281287
}
282288

283289
/** Run `codeql pack install` for a single pack directory. */

extensions/vscode/test/server/pack-installer.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,5 +542,25 @@ describe('PackInstaller', () => {
542542
expect.stringContaining('1/2'),
543543
);
544544
});
545+
546+
it('should handle attemptCount === 0 when all languages are skipped', async () => {
547+
cliResolver.getCliVersion.mockReturnValue('2.25.1');
548+
serverManager.getExtensionVersion.mockReturnValue('2.25.1');
549+
550+
// All pack directories are missing → all languages skipped
551+
vi.mocked(access).mockRejectedValue(new Error('ENOENT'));
552+
553+
await installer.installAll({ languages: ['javascript', 'python', 'go'] });
554+
555+
const summaryCall = logger.info.mock.calls.find(
556+
(call: any[]) => typeof call[0] === 'string' && call[0].includes('pack install complete'),
557+
);
558+
expect(summaryCall).toBeDefined();
559+
const summaryMsg = summaryCall![0] as string;
560+
// Should NOT log "0/0 languages succeeded"
561+
expect(summaryMsg).not.toContain('0/0');
562+
// Should indicate no packs were found
563+
expect(summaryMsg).toContain('no bundled packs found');
564+
});
545565
});
546566
});

extensions/vscode/test/suite/mcp-server.integration.test.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,21 +60,25 @@ suite('MCP Server Definition Tests', () => {
6060
}
6161
});
6262

63-
test('Environment should include a valid CODEQL_PATH when CLI is available', async () => {
63+
test('Environment should include a valid CODEQL_PATH when CLI is available', async function () {
6464
const envBuilder = api.environmentBuilder;
6565
if (!envBuilder) {
6666
// environmentBuilder may not be exported — skip gracefully
67+
this.skip();
6768
return;
6869
}
6970
const env = await envBuilder.build();
70-
if (env.CODEQL_PATH) {
71-
// The CODEQL_PATH should resolve to an existing binary.
72-
// In CI or the extension dev host, the CLI is expected to exist.
73-
const basename = path.basename(env.CODEQL_PATH).toLowerCase();
74-
assert.ok(
75-
basename === 'codeql' || basename === 'codeql.exe',
76-
`CODEQL_PATH basename is not 'codeql' or 'codeql.exe': ${env.CODEQL_PATH}`,
77-
);
71+
if (!env.CODEQL_PATH) {
72+
// Skip explicitly when the CLI is unavailable instead of passing silently.
73+
this.skip();
74+
return;
7875
}
76+
// The CODEQL_PATH should resolve to an existing binary.
77+
// In CI or the extension dev host, the CLI is expected to exist.
78+
const basename = path.basename(env.CODEQL_PATH).toLowerCase();
79+
assert.ok(
80+
basename === 'codeql' || basename === 'codeql.exe',
81+
`CODEQL_PATH basename is not 'codeql' or 'codeql.exe': ${env.CODEQL_PATH}`,
82+
);
7983
});
8084
});

server/dist/codeql-development-mcp-server.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -198452,8 +198452,9 @@ async function completeDatabasePath(value) {
198452198452
}).sort();
198453198453
return filtered.slice(0, MAX_FILE_COMPLETIONS);
198454198454
}
198455-
async function findDatabaseDirs(dir, _baseDir, maxDepth, results) {
198455+
async function findDatabaseDirs(dir, _baseDir, maxDepth, results, skipDirs) {
198456198456
if (maxDepth <= 0 || results.length >= MAX_FILE_COMPLETIONS) return;
198457+
const excluded = skipDirs ?? getSkipDirs2();
198457198458
let entries;
198458198459
try {
198459198460
entries = await readdir(dir, { withFileTypes: true });
@@ -198469,8 +198470,8 @@ async function findDatabaseDirs(dir, _baseDir, maxDepth, results) {
198469198470
}
198470198471
for (const entry of entries) {
198471198472
if (results.length >= MAX_FILE_COMPLETIONS) break;
198472-
if (entry.isDirectory() && !getSkipDirs2().has(entry.name)) {
198473-
await findDatabaseDirs(join22(dir, entry.name), _baseDir, maxDepth - 1, results);
198473+
if (entry.isDirectory() && !excluded.has(entry.name)) {
198474+
await findDatabaseDirs(join22(dir, entry.name), _baseDir, maxDepth - 1, results, excluded);
198474198475
}
198475198476
}
198476198477
}
@@ -198480,6 +198481,7 @@ async function completePackRoot(value) {
198480198481
let allResults = getCachedResults(cacheKey2);
198481198482
if (!allResults) {
198482198483
const results = [];
198484+
const excluded = getSkipDirs2();
198483198485
async function scan(dir, depth) {
198484198486
if (depth <= 0 || results.length >= MAX_FILE_COMPLETIONS) return;
198485198487
let entries;
@@ -198496,7 +198498,7 @@ async function completePackRoot(value) {
198496198498
}
198497198499
for (const entry of entries) {
198498198500
if (results.length >= MAX_FILE_COMPLETIONS) break;
198499-
if (entry.isDirectory() && !getSkipDirs2().has(entry.name)) {
198501+
if (entry.isDirectory() && !excluded.has(entry.name)) {
198500198502
await scan(join22(dir, entry.name), depth - 1);
198501198503
}
198502198504
}
@@ -198516,7 +198518,6 @@ async function completePackRoot(value) {
198516198518
var CODEQL_LANG_DEP_RE = /codeql\/([a-z]+)-(all|queries)/;
198517198519
async function resolveLanguageFromPack(queryFilePath) {
198518198520
let dir = dirname9(queryFilePath);
198519-
const root = dirname9(dir) === dir ? dir : void 0;
198520198521
for (let i = 0; i < 20; i++) {
198521198522
const packPath = join22(dir, "codeql-pack.yml");
198522198523
try {
@@ -198531,7 +198532,7 @@ async function resolveLanguageFromPack(queryFilePath) {
198531198532
} catch {
198532198533
}
198533198534
const parent = dirname9(dir);
198534-
if (parent === dir || parent === root) break;
198535+
if (parent === dir) break;
198535198536
dir = parent;
198536198537
}
198537198538
return void 0;

server/dist/codeql-development-mcp-server.js.map

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/src/prompts/prompt-completions.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -268,9 +268,12 @@ async function findDatabaseDirs(
268268
_baseDir: string,
269269
maxDepth: number,
270270
results: string[],
271+
skipDirs?: Set<string>,
271272
): Promise<void> {
272273
if (maxDepth <= 0 || results.length >= MAX_FILE_COMPLETIONS) return;
273274

275+
const excluded = skipDirs ?? getSkipDirs();
276+
274277
let entries;
275278
try {
276279
entries = await readdir(dir, { withFileTypes: true });
@@ -289,8 +292,8 @@ async function findDatabaseDirs(
289292

290293
for (const entry of entries) {
291294
if (results.length >= MAX_FILE_COMPLETIONS) break;
292-
if (entry.isDirectory() && !getSkipDirs().has(entry.name)) {
293-
await findDatabaseDirs(join(dir, entry.name), _baseDir, maxDepth - 1, results);
295+
if (entry.isDirectory() && !excluded.has(entry.name)) {
296+
await findDatabaseDirs(join(dir, entry.name), _baseDir, maxDepth - 1, results, excluded);
294297
}
295298
}
296299
}
@@ -307,6 +310,8 @@ export async function completePackRoot(value: string): Promise<string[]> {
307310
if (!allResults) {
308311
const results: string[] = [];
309312

313+
const excluded = getSkipDirs();
314+
310315
async function scan(dir: string, depth: number): Promise<void> {
311316
if (depth <= 0 || results.length >= MAX_FILE_COMPLETIONS) return;
312317

@@ -327,7 +332,7 @@ export async function completePackRoot(value: string): Promise<string[]> {
327332

328333
for (const entry of entries) {
329334
if (results.length >= MAX_FILE_COMPLETIONS) break;
330-
if (entry.isDirectory() && !getSkipDirs().has(entry.name)) {
335+
if (entry.isDirectory() && !excluded.has(entry.name)) {
331336
await scan(join(dir, entry.name), depth - 1);
332337
}
333338
}
@@ -377,7 +382,6 @@ export async function resolveLanguageFromPack(
377382
queryFilePath: string,
378383
): Promise<string | undefined> {
379384
let dir = dirname(queryFilePath);
380-
const root = dirname(dir) === dir ? dir : undefined; // filesystem root guard
381385

382386
// Walk up at most 20 levels (safety limit)
383387
for (let i = 0; i < 20; i++) {
@@ -397,7 +401,7 @@ export async function resolveLanguageFromPack(
397401
}
398402

399403
const parent = dirname(dir);
400-
if (parent === dir || parent === root) break;
404+
if (parent === dir) break; // reached filesystem root
401405
dir = parent;
402406
}
403407

server/test/src/prompts/prompt-completions.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,33 @@ describe('completeDatabasePath', () => {
269269
const result = await completeDatabasePath('');
270270
expect(result).toContain(join(tmpDir, 'project-db'));
271271
});
272+
273+
it('should respect CODEQL_MCP_SCAN_EXCLUDE_DIRS for database scanning', async () => {
274+
// Create a database inside an excluded directory
275+
const excludedDir = join(tmpDir, 'custom-excluded');
276+
const dbInExcluded = join(excludedDir, 'my-db');
277+
mkdirSync(dbInExcluded, { recursive: true });
278+
writeFileSync(join(dbInExcluded, 'codeql-database.yml'), 'primaryLanguage: javascript');
279+
280+
// Create a database outside excluded directories
281+
const normalDb = join(tmpDir, 'normal-db');
282+
mkdirSync(normalDb, { recursive: true });
283+
writeFileSync(join(normalDb, 'codeql-database.yml'), 'primaryLanguage: python');
284+
285+
// Without exclusion: both should be found
286+
vi.stubEnv('CODEQL_MCP_SCAN_EXCLUDE_DIRS', '');
287+
clearCompletionCache();
288+
const before = await completeDatabasePath('');
289+
expect(before).toContain(join(tmpDir, 'normal-db'));
290+
expect(before).toContain(dbInExcluded);
291+
292+
// Now exclude custom-excluded — database inside should not be found
293+
vi.stubEnv('CODEQL_MCP_SCAN_EXCLUDE_DIRS', 'custom-excluded');
294+
clearCompletionCache();
295+
const after = await completeDatabasePath('');
296+
expect(after).toContain(join(tmpDir, 'normal-db'));
297+
expect(after).not.toContain(dbInExcluded);
298+
});
272299
});
273300

274301
// ─────────────────────────────────────────────────────────────────────────────
@@ -783,6 +810,15 @@ describe('resolveLanguageFromPack', () => {
783810
const lang = await resolveLanguageFromPack(join(packDir, 'Query.ql'));
784811
expect(lang).toBe('java');
785812
});
813+
814+
it('should terminate when reaching filesystem root without finding pack', async () => {
815+
// Use the real filesystem root — no codeql-pack.yml should exist there
816+
const rootDir = '/';
817+
const deepPath = join(rootDir, 'nonexistent-dir', 'Query.ql');
818+
819+
const lang = await resolveLanguageFromPack(deepPath);
820+
expect(lang).toBeUndefined();
821+
});
786822
});
787823

788824
// ─────────────────────────────────────────────────────────────────────────────

0 commit comments

Comments
 (0)