Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion extensions/ql-vscode/src/contextual/queryResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { CancellationToken, Uri } from "vscode";
import { ProgressCallback } from "../commandRunner";
import { QueryRunner } from "../queryRunner";
import { redactableError } from "../pure/errors";
import { QLPACK_FILENAMES } from "../pure/ql";

export async function qlpackOfDatabase(
cli: CodeQLCliServer,
Expand Down Expand Up @@ -113,7 +114,7 @@ async function resolveContextualQuery(
// Work out the enclosing pack.
const packContents = await cli.packPacklist(query, false);
const packFilePath = packContents.find((p) =>
["codeql-pack.yml", "qlpack.yml"].includes(basename(p)),
QLPACK_FILENAMES.includes(basename(p)),
);
if (packFilePath === undefined) {
// Should not happen; we already resolved this query.
Expand Down
1 change: 1 addition & 0 deletions extensions/ql-vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,7 @@ async function activateWithInstalledDistribution(
documentSelector: [
{ language: "ql", scheme: "file" },
{ language: "yaml", scheme: "file", pattern: "**/qlpack.yml" },
{ language: "yaml", scheme: "file", pattern: "**/codeql-pack.yml" },
],
synchronize: {
configurationSection: "codeQL",
Expand Down
26 changes: 16 additions & 10 deletions extensions/ql-vscode/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { extLogger, OutputChannelLogger } from "./common";
import { QueryMetadata } from "./pure/interface-types";
import { telemetryListener } from "./telemetry";
import { RedactableError } from "./pure/errors";
import { getQlPackPath } from "./pure/ql";

// Shared temporary folder for the extension.
export const tmpDir = dirSync({
Expand Down Expand Up @@ -387,17 +388,22 @@ async function findDbschemePack(
): Promise<{ name: string; isLibraryPack: boolean }> {
for (const { packDir, packName } of packs) {
if (packDir !== undefined) {
const qlpack = load(
await readFile(join(packDir, "qlpack.yml"), "utf8"),
) as { dbscheme?: string; library?: boolean };
if (
qlpack.dbscheme !== undefined &&
basename(qlpack.dbscheme) === basename(dbschemePath)
) {
return {
name: packName,
isLibraryPack: qlpack.library === true,
const qlpackPath = await getQlPackPath(packDir);

if (qlpackPath !== undefined) {
const qlpack = load(await readFile(qlpackPath, "utf8")) as {
dbscheme?: string;
library?: boolean;
};
if (
qlpack.dbscheme !== undefined &&
basename(qlpack.dbscheme) === basename(dbschemePath)
) {
return {
name: packName,
isLibraryPack: qlpack.library === true,
};
}
}
}
}
Expand Down
19 changes: 19 additions & 0 deletions extensions/ql-vscode/src/pure/ql.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { join } from "path";
import { pathExists } from "fs-extra";

export const QLPACK_FILENAMES = ["qlpack.yml", "codeql-pack.yml"];
export const FALLBACK_QLPACK_FILENAME = QLPACK_FILENAMES[0];

export async function getQlPackPath(
packRoot: string,
): Promise<string | undefined> {
for (const filename of QLPACK_FILENAMES) {
const path = join(packRoot, filename);

if (await pathExists(path)) {
return path;
}
}

return undefined;
}
13 changes: 10 additions & 3 deletions extensions/ql-vscode/src/quick-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
} from "./helpers";
import { ProgressCallback, UserCancellationException } from "./commandRunner";
import { getErrorMessage } from "./pure/helpers-pure";
import { FALLBACK_QLPACK_FILENAME, getQlPackPath } from "./pure/ql";

const QUICK_QUERIES_DIR_NAME = "quick-queries";
const QUICK_QUERY_QUERY_NAME = "quick-query.ql";
Expand Down Expand Up @@ -112,7 +113,7 @@ export async function displayQuickQuery(
const dbscheme = await getPrimaryDbscheme(datasetFolder);
const qlpack = (await getQlPackForDbscheme(cliServer, dbscheme))
.dbschemePack;
const qlPackFile = join(queriesDir, "qlpack.yml");
const qlPackFile = await getQlPackPath(queriesDir);
const qlFile = join(queriesDir, QUICK_QUERY_QUERY_NAME);
const shouldRewrite = await checkShouldRewrite(qlPackFile, qlpack);

Expand All @@ -126,7 +127,7 @@ export async function displayQuickQuery(
},
};
await writeFile(
qlPackFile,
qlPackFile ?? join(queriesDir, FALLBACK_QLPACK_FILENAME),
QLPACK_FILE_HEADER + dump(quickQueryQlpackYaml),
"utf8",
);
Expand Down Expand Up @@ -158,7 +159,13 @@ export async function displayQuickQuery(
}
}

async function checkShouldRewrite(qlPackFile: string, newDependency: string) {
async function checkShouldRewrite(
qlPackFile: string | undefined,
newDependency: string,
) {
if (!qlPackFile) {
return true;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see we don't have a test file for quick-query functions so this is a stretch. If the setup isn't terribly complicated, do you think it's worth adding one to test what this is doing?

I do see we have a queries.test.ts file which I guess covers some of the quick-query functionality.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could add a test for this function, but I don't really know what input values it would actually receive. I'm not quite sure when the function should be returning true or false or what it's purpose is, so I'm hesitant to add tests based purely on its implementation, rather than on its intended behaviour.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quick query is a feature where the IDE generates a bare-bones qlpack in a temporary directory that is tailored to the current language. It is meant to be semi-ephemeral.

When you call the quick query command, the extension checks if the query query pack exists already and if it is for the correct language of the current database.

if (!(await pathExists(qlPackFile))) {
return true;
}
Expand Down
32 changes: 16 additions & 16 deletions extensions/ql-vscode/src/remote-queries/run-remote-query.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CancellationToken, Uri, window } from "vscode";
import { relative, join, sep, dirname, parse, basename } from "path";
import { dump, load } from "js-yaml";
import { pathExists, copy, writeFile, readFile, mkdirp } from "fs-extra";
import { copy, writeFile, readFile, mkdirp } from "fs-extra";
import { dir, tmpName } from "tmp-promise";
import {
askForLanguage,
Expand Down Expand Up @@ -30,6 +30,11 @@ import {
} from "./repository-selection";
import { Repository } from "./shared/repository";
import { DbManager } from "../databases/db-manager";
import {
getQlPackPath,
FALLBACK_QLPACK_FILENAME,
QLPACK_FILENAMES,
} from "../pure/ql";

export interface QlPack {
name: string;
Expand Down Expand Up @@ -67,7 +72,7 @@ async function generateQueryPack(
const targetQueryFileName = join(queryPackDir, packRelativePath);

let language: string | undefined;
if (await getExistingPackFile(originalPackRoot)) {
if (await getQlPackPath(originalPackRoot)) {
// don't include ql files. We only want the queryFile to be copied.
const toCopy = await cliServer.packPacklist(originalPackRoot, false);

Expand Down Expand Up @@ -120,7 +125,10 @@ async function generateQueryPack(
},
defaultSuite: generateDefaultSuite(packRelativePath),
};
await writeFile(join(queryPackDir, "qlpack.yml"), dump(syntheticQueryPack));
await writeFile(
join(queryPackDir, FALLBACK_QLPACK_FILENAME),
dump(syntheticQueryPack),
);
}
if (!language) {
throw new UserCancellationException("Could not determine language.");
Expand Down Expand Up @@ -163,7 +171,7 @@ async function generateQueryPack(
async function findPackRoot(queryFile: string): Promise<string> {
// recursively find the directory containing qlpack.yml
let dir = dirname(queryFile);
while (!(await getExistingPackFile(dir))) {
while (!(await getQlPackPath(dir))) {
dir = dirname(dir);
if (isFileSystemRoot(dir)) {
// there is no qlpack.yml in this directory or any parent directory.
Expand All @@ -175,16 +183,6 @@ async function findPackRoot(queryFile: string): Promise<string> {
return dir;
}

async function getExistingPackFile(dir: string) {
if (await pathExists(join(dir, "qlpack.yml"))) {
return join(dir, "qlpack.yml");
}
if (await pathExists(join(dir, "codeql-pack.yml"))) {
return join(dir, "codeql-pack.yml");
}
return undefined;
}

function isFileSystemRoot(dir: string): boolean {
const pathObj = parse(dir);
return pathObj.root === dir && pathObj.base === "";
Expand Down Expand Up @@ -319,12 +317,14 @@ async function fixPackFile(
queryPackDir: string,
packRelativePath: string,
): Promise<void> {
const packPath = await getExistingPackFile(queryPackDir);
const packPath = await getQlPackPath(queryPackDir);

// This should not happen since we create the pack ourselves.
if (!packPath) {
throw new Error(
`Could not find qlpack.yml or codeql-pack.yml file in '${queryPackDir}'`,
`Could not find ${QLPACK_FILENAMES.join(
" or ",
)} file in '${queryPackDir}'`,
);
}
const qlpack = load(await readFile(packPath, "utf8")) as QlPack;
Expand Down
48 changes: 48 additions & 0 deletions extensions/ql-vscode/test/unit-tests/pure/ql.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { join } from "path";
import { dirSync } from "tmp-promise";
import { DirResult } from "tmp";
import { writeFile } from "fs-extra";
import { getQlPackPath } from "../../../src/pure/ql";

describe("getQlPackPath", () => {
let tmpDir: DirResult;

beforeEach(() => {
tmpDir = dirSync({
prefix: "queries_",
keep: false,
unsafeCleanup: true,
});
});

afterEach(() => {
tmpDir.removeCallback();
});

it("should find a qlpack.yml when it exists", async () => {
await writeFile(join(tmpDir.name, "qlpack.yml"), "name: test");

const result = await getQlPackPath(tmpDir.name);
expect(result).toEqual(join(tmpDir.name, "qlpack.yml"));
});

it("should find a codeql-pack.yml when it exists", async () => {
await writeFile(join(tmpDir.name, "codeql-pack.yml"), "name: test");

const result = await getQlPackPath(tmpDir.name);
expect(result).toEqual(join(tmpDir.name, "codeql-pack.yml"));
});

it("should find a qlpack.yml when both exist", async () => {
await writeFile(join(tmpDir.name, "qlpack.yml"), "name: test");
await writeFile(join(tmpDir.name, "codeql-pack.yml"), "name: test");

const result = await getQlPackPath(tmpDir.name);
expect(result).toEqual(join(tmpDir.name, "qlpack.yml"));
});

it("should find nothing when it doesn't exist", async () => {
const result = await getQlPackPath(tmpDir.name);
expect(result).toEqual(undefined);
});
});