Do not use the CLI server to determine query pack language

This removes the use of the CLI `codeql resolve library-path` command to
detect the language of a query pack. Instead, it uses the `qlpack.yml`
file to determine the language. This is slightly less correct since it
only works for `codeql/${language}-all` dependencies, but it is much
faster and more reliable. It also doesn't result in the CLI server
restarting for invalid query packs (such as in the `github/codeql`
repository or in any workspaces containing it).
This commit is contained in:
Koen Vlaswinkel
2023-12-21 12:11:48 +01:00
parent 704895ba6c
commit 7554c415af
4 changed files with 53 additions and 96 deletions

View File

@@ -799,11 +799,7 @@ async function activateWithInstalledDistribution(
);
ctx.subscriptions.push(databaseUI);
const queriesModule = QueriesModule.initialize(
app,
languageContext,
cliServer,
);
const queriesModule = QueriesModule.initialize(app, languageContext);
void extLogger.log("Initializing evaluator log viewer.");
const evalLogViewer = new EvalLogViewer();

View File

@@ -1,4 +1,3 @@
import { CodeQLCliServer } from "../codeql-cli/cli";
import { extLogger } from "../common/logging/vscode";
import { App } from "../common/app";
import { DisposableObject } from "../common/disposable-object";
@@ -26,23 +25,18 @@ export class QueriesModule extends DisposableObject {
public static initialize(
app: App,
languageContext: LanguageContextStore,
cliServer: CodeQLCliServer,
): QueriesModule {
const queriesModule = new QueriesModule(app);
app.subscriptions.push(queriesModule);
queriesModule.initialize(app, languageContext, cliServer);
queriesModule.initialize(app, languageContext);
return queriesModule;
}
private initialize(
app: App,
langauageContext: LanguageContextStore,
cliServer: CodeQLCliServer,
): void {
private initialize(app: App, langauageContext: LanguageContextStore): void {
void extLogger.log("Initializing queries panel.");
const queryPackDiscovery = new QueryPackDiscovery(cliServer);
const queryPackDiscovery = new QueryPackDiscovery();
this.push(queryPackDiscovery);
void queryPackDiscovery.initialRefresh();

View File

@@ -1,14 +1,12 @@
import { basename, dirname } from "path";
import { CodeQLCliServer, QuerySetup } from "../codeql-cli/cli";
import { Event } from "vscode";
import { QueryLanguage, dbSchemeToLanguage } from "../common/query-language";
import { QueryLanguage } from "../common/query-language";
import { FALLBACK_QLPACK_FILENAME, QLPACK_FILENAMES } from "../common/ql";
import { FilePathDiscovery } from "../common/vscode/file-path-discovery";
import { getErrorMessage } from "../common/helpers-pure";
import { extLogger } from "../common/logging/vscode";
import { EOL } from "os";
import { containsPath } from "../common/files";
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
import { load } from "js-yaml";
import { readFile } from "fs-extra";
import { QlPackFile } from "../packaging/qlpack-file";
interface QueryPack {
path: string;
@@ -19,7 +17,7 @@ interface QueryPack {
* Discovers all query packs in the workspace.
*/
export class QueryPackDiscovery extends FilePathDiscovery<QueryPack> {
constructor(private readonly cliServer: CodeQLCliServer) {
constructor() {
super("Query Pack Discovery", `**/{${QLPACK_FILENAMES.join(",")}}`);
}
@@ -71,32 +69,32 @@ export class QueryPackDiscovery extends FilePathDiscovery<QueryPack> {
}
protected async getDataForPath(path: string): Promise<QueryPack> {
const language = await this.determinePackLanguage(path);
let language: QueryLanguage | undefined;
try {
language = await this.determinePackLanguage(path);
} catch (e) {
language = undefined;
}
return { path, language };
}
private async determinePackLanguage(
path: string,
): Promise<QueryLanguage | undefined> {
let packInfo: QuerySetup | undefined = undefined;
try {
packInfo = await this.cliServer.resolveLibraryPath(
getOnDiskWorkspaceFolders(),
path,
true,
);
} catch (err) {
void extLogger.log(
`Query pack discovery failed to determine language for query pack: ${path}${EOL}Reason: ${getErrorMessage(
err,
)}`,
);
const qlPack = load(await readFile(path, "utf8")) as QlPackFile | undefined;
const dependencies = qlPack?.dependencies;
if (!dependencies || typeof dependencies !== "object") {
return;
}
if (packInfo?.dbscheme === undefined) {
const matchingLanguages = Object.values(QueryLanguage).filter(
(language) => `codeql/${language}-all` in dependencies,
);
if (matchingLanguages.length !== 1) {
return undefined;
}
const dbscheme = basename(packInfo.dbscheme);
return dbSchemeToLanguage[dbscheme];
return matchingLanguages[0];
}
protected pathIsRelevant(path: string): boolean {

View File

@@ -2,9 +2,8 @@ import { Uri, workspace } from "vscode";
import { QueryPackDiscovery } from "../../../../src/queries-panel/query-pack-discovery";
import * as tmp from "tmp";
import { dirname, join } from "path";
import { CodeQLCliServer, QuerySetup } from "../../../../src/codeql-cli/cli";
import { mockedObject } from "../../utils/mocking.helpers";
import { mkdirSync, writeFileSync } from "fs";
import { ensureDir, writeJSON } from "fs-extra";
import { QueryLanguage } from "../../../../src/common/query-language";
describe("Query pack discovery", () => {
let tmpDir: string;
@@ -12,9 +11,6 @@ describe("Query pack discovery", () => {
let workspacePath: string;
let resolveLibraryPath: jest.SpiedFunction<
typeof CodeQLCliServer.prototype.resolveLibraryPath
>;
let discovery: QueryPackDiscovery;
beforeEach(() => {
@@ -34,15 +30,7 @@ describe("Query pack discovery", () => {
.spyOn(workspace, "workspaceFolders", "get")
.mockReturnValue([workspaceFolder]);
const mockResolveLibraryPathValue: QuerySetup = {
libraryPath: [],
dbscheme: "/ql/java/ql/lib/config/semmlecode.dbscheme",
};
resolveLibraryPath = jest
.fn()
.mockResolvedValue(mockResolveLibraryPathValue);
const mockCliServer = mockedObject<CodeQLCliServer>({ resolveLibraryPath });
discovery = new QueryPackDiscovery(mockCliServer);
discovery = new QueryPackDiscovery();
});
afterEach(() => {
@@ -60,7 +48,7 @@ describe("Query pack discovery", () => {
});
it("locates a query pack in the same directory", async () => {
makeTestFile(join(workspacePath, "qlpack.yml"));
await makeTestFile(join(workspacePath, "qlpack.yml"));
await discovery.initialRefresh();
@@ -70,7 +58,7 @@ describe("Query pack discovery", () => {
});
it("locates a query pack using the old pack name", async () => {
makeTestFile(join(workspacePath, "codeql-pack.yml"));
await makeTestFile(join(workspacePath, "codeql-pack.yml"));
await discovery.initialRefresh();
@@ -80,7 +68,7 @@ describe("Query pack discovery", () => {
});
it("locates a query pack in a higher directory", async () => {
makeTestFile(join(workspacePath, "qlpack.yml"));
await makeTestFile(join(workspacePath, "qlpack.yml"));
await discovery.initialRefresh();
@@ -92,7 +80,7 @@ describe("Query pack discovery", () => {
});
it("doesn't recognise a query pack in a sibling directory", async () => {
makeTestFile(join(workspacePath, "foo", "qlpack.yml"));
await makeTestFile(join(workspacePath, "foo", "qlpack.yml"));
await discovery.initialRefresh();
@@ -109,24 +97,11 @@ describe("Query pack discovery", () => {
});
it("query packs override those from parent directories", async () => {
makeTestFile(join(workspacePath, "qlpack.yml"));
makeTestFile(join(workspacePath, "foo", "qlpack.yml"));
resolveLibraryPath.mockImplementation(async (_workspaces, queryPath) => {
if (queryPath === join(workspacePath, "qlpack.yml")) {
return {
libraryPath: [],
dbscheme: "/ql/java/ql/lib/config/semmlecode.dbscheme",
};
}
if (queryPath === join(workspacePath, "foo", "qlpack.yml")) {
return {
libraryPath: [],
dbscheme: "/ql/cpp/ql/lib/semmlecode.cpp.dbscheme",
};
}
throw new Error(`Unknown query pack: ${queryPath}`);
});
await makeTestFile(join(workspacePath, "qlpack.yml"), QueryLanguage.Java);
await makeTestFile(
join(workspacePath, "foo", "qlpack.yml"),
QueryLanguage.Cpp,
);
await discovery.initialRefresh();
@@ -141,24 +116,11 @@ describe("Query pack discovery", () => {
});
it("prefers a query pack called qlpack.yml", async () => {
makeTestFile(join(workspacePath, "qlpack.yml"));
makeTestFile(join(workspacePath, "codeql-pack.yml"));
resolveLibraryPath.mockImplementation(async (_workspaces, queryPath) => {
if (queryPath === join(workspacePath, "qlpack.yml")) {
return {
libraryPath: [],
dbscheme: "/ql/cpp/ql/lib/semmlecode.cpp.dbscheme",
};
}
if (queryPath === join(workspacePath, "codeql-pack.yml")) {
return {
libraryPath: [],
dbscheme: "/ql/java/ql/lib/config/semmlecode.dbscheme",
};
}
throw new Error(`Unknown query pack: ${queryPath}`);
});
await makeTestFile(join(workspacePath, "qlpack.yml"), QueryLanguage.Cpp);
await makeTestFile(
join(workspacePath, "codeql-pack.yml"),
QueryLanguage.Java,
);
await discovery.initialRefresh();
@@ -169,7 +131,14 @@ describe("Query pack discovery", () => {
});
});
function makeTestFile(path: string) {
mkdirSync(dirname(path), { recursive: true });
writeFileSync(path, "");
async function makeTestFile(
path: string,
language: QueryLanguage = QueryLanguage.Java,
) {
await ensureDir(dirname(path));
await writeJSON(path, {
dependencies: {
[`codeql/${language}-all`]: "*",
},
});
}