Skip to content

Commit f9a19b6

Browse files
committed
AST viewer: Support running when the library pack is in the package cache
If the library pack containing the AST query does not have a lock file, it is likely to be in the package cache, not a checkout of the CodeQL repo. In this case, use `codeql pack resolve-dependencies` to create a temporary lock file, and `codeql pack install` to install the dependencies of this library pack. This allows the CLI to resolve the library path and dependencies for the AST query before running it.
1 parent b526ff4 commit f9a19b6

File tree

2 files changed

+52
-8
lines changed

2 files changed

+52
-8
lines changed

extensions/ql-vscode/src/cli.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,12 @@ export class CodeQLCliServer implements Disposable {
970970
}
971971
}
972972

973+
async packResolveDependencies(dir: string, mode: 'use-lock'): Promise<{ [pack: string]: string }> {
974+
const args = ['--mode', mode, dir];
975+
const results: { [pack: string]: string } = await this.runJsonCodeQlCliCommand(['pack', 'resolve-dependencies'], args, 'Resolving pack dependencies');
976+
return results;
977+
}
978+
973979
async generateDil(qloFile: string, outFile: string): Promise<void> {
974980
const extraArgs = await this.cliConstraints.supportsDecompileDil()
975981
? ['--kind', 'dil', '-o', outFile, qloFile]

extensions/ql-vscode/src/contextual/templateProvider.ts

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import * as fs from 'fs-extra';
2+
import * as path from 'path';
13
import {
24
CancellationToken,
35
DefinitionProvider,
@@ -14,6 +16,7 @@ import {
1416
import { decodeSourceArchiveUri, encodeArchiveBasePath, zipArchiveScheme } from '../archive-filesystem-provider';
1517
import { CodeQLCliServer } from '../cli';
1618
import { DatabaseManager } from '../databases';
19+
import { logger } from '../logging';
1720
import { CachedOperation } from '../helpers';
1821
import { ProgressCallback, withProgress } from '../commandRunner';
1922
import AstBuilder from './astBuilder';
@@ -208,6 +211,31 @@ export class TemplatePrintAstProvider {
208211
zippedArchive.pathWithinSourceArchive
209212
};
210213

214+
// The AST viewer queries now live within the standard library packs.
215+
// This simplifies distribution (you don't need the standard query pack to use the AST viewer),
216+
// but if the library pack doesn't have a lockfile, we won't be able to find
217+
// other pack dependencies of the library pack.
218+
219+
// Work out the enclosing pack.
220+
const packContents = await this.cli.packPacklist(query, false);
221+
const packFilePath = packContents.find((p) => ['codeql-pack.yml', 'qlpack.yml'].includes(path.basename(p)));
222+
if (packFilePath === undefined) {
223+
// Should not happen; we already resolved this query.
224+
throw new Error(`Could not find a CodeQL pack file for the pack enclosing the query ${query}`);
225+
}
226+
const packPath = path.dirname(packFilePath);
227+
const lockFilePath = packContents.find((p) => ['codeql-pack.lock.yml', 'qlpack.lock.yml'].includes(path.basename(p)));
228+
if (!lockFilePath) {
229+
// No lock file, likely because this library pack is in the package cache.
230+
// Create a lock file so that we can resolve dependencies and library path
231+
// for the AST query.
232+
void logger.log(`Library pack ${packPath} is missing a lock file; creating a temporary lock file`);
233+
await this.cli.packResolveDependencies(packPath, 'use-lock');
234+
// Install dependencies.
235+
void logger.log(`Installing package dependencies for library pack ${packPath}`);
236+
await this.cli.packInstall(packPath);
237+
}
238+
211239
const initialInfo = await createInitialQueryInfo(
212240
Uri.file(query),
213241
{
@@ -217,15 +245,25 @@ export class TemplatePrintAstProvider {
217245
false
218246
);
219247

248+
void logger.log(`Running AST query ${query}; results will be stored in ${this.queryStorageDir}`);
249+
const queryResult = await this.qs.compileAndRunQueryAgainstDatabase(
250+
db,
251+
initialInfo,
252+
this.queryStorageDir,
253+
progress,
254+
token,
255+
templates
256+
);
257+
258+
if (!lockFilePath) {
259+
// Clean up the temporary lock file we created.
260+
const tempLockFilePath = path.resolve(packPath, 'codeql-pack.lock.yml');
261+
void logger.log(`Deleting temporary package lock file at ${tempLockFilePath}`);
262+
// It's fine if the file doesn't exist.
263+
fs.rmSync(path.resolve(packPath, 'codeql-pack.lock.yml'), { force: true });
264+
}
220265
return {
221-
query: await this.qs.compileAndRunQueryAgainstDatabase(
222-
db,
223-
initialInfo,
224-
this.queryStorageDir,
225-
progress,
226-
token,
227-
templates
228-
),
266+
query: queryResult,
229267
dbUri: db.databaseUri
230268
};
231269
}

0 commit comments

Comments
 (0)