Skip to content
Draft
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
2 changes: 2 additions & 0 deletions extensions/ql-vscode/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## [UNRELEASED]

- Avoid reinstalling dependencies for standard queries when their query pack already has a lock file. [#4471](https://github.com/github/vscode-codeql/issues/4471)

## 1.17.8 - 17 July 2026

- Fix a bug where installing or updating the CodeQL CLI could hang indefinitely while extracting the downloaded archive. Extraction now reports an error if a file cannot be written, and aborts with a clear message if no progress is made within the download timeout (for example due to slow or networked storage, or security software). [#4455](https://github.com/github/vscode-codeql/pull/4455)
Expand Down
13 changes: 9 additions & 4 deletions extensions/ql-vscode/src/local-queries/standard-queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { CodeQLCliServer } from "../codeql-cli/cli";
import { QLPACK_FILENAMES, QLPACK_LOCK_FILENAMES } from "../common/ql";
import { basename, dirname, resolve } from "path";
import { extLogger } from "../common/logging/vscode";
import { promises } from "fs-extra";
import { pathExists, promises } from "fs-extra";
import type { BaseLogger } from "../common/logging";

type LockFileForStandardQueryResult = {
Expand Down Expand Up @@ -36,9 +36,14 @@ export async function createLockFileForStandardQuery(
);
}
const packPath = dirname(packFilePath);
const lockFilePath = packContents.find((p) =>
QLPACK_LOCK_FILENAMES.includes(basename(p)),
);
let lockFilePath: string | undefined;
for (const lockFileName of QLPACK_LOCK_FILENAMES) {
const candidateLockFilePath = resolve(packPath, lockFileName);
if (await pathExists(candidateLockFilePath)) {
lockFilePath = candidateLockFilePath;
break;
}
}

let cleanup: (() => Promise<void>) | undefined = undefined;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { DirectoryResult } from "tmp-promise";
import { dir } from "tmp-promise";
import { join } from "path";
import { createLockFileForStandardQuery } from "../../../../src/local-queries/standard-queries";
import { outputFile, pathExists } from "fs-extra";
import { outputFile, pathExists, readFile } from "fs-extra";

describe("createLockFileForStandardQuery", () => {
let tmpDir: DirectoryResult;
Expand Down Expand Up @@ -41,37 +41,34 @@ describe("createLockFileForStandardQuery", () => {
});

describe("when the lock file exists", () => {
let lockfilePath: string;

beforeEach(async () => {
lockfilePath = join(packPath, "qlpack.lock.yml");

packPacklist.mockResolvedValue([qlpackPath, lockfilePath, queryPath]);
});

it("does not resolve or install dependencies", async () => {
expect(await createLockFileForStandardQuery(mockCli, queryPath)).toEqual({
cleanup: undefined,
});

expect(packResolveDependencies).not.toHaveBeenCalled();
expect(clearCache).not.toHaveBeenCalled();
expect(packInstall).not.toHaveBeenCalled();
});

it("does not resolve or install dependencies with a codeql-pack.lock.yml", async () => {
lockfilePath = join(packPath, "codeql-pack.lock.yml");

packPacklist.mockResolvedValue([qlpackPath, lockfilePath, queryPath]);

expect(await createLockFileForStandardQuery(mockCli, queryPath)).toEqual({
cleanup: undefined,
});

expect(packResolveDependencies).not.toHaveBeenCalled();
expect(clearCache).not.toHaveBeenCalled();
expect(packInstall).not.toHaveBeenCalled();
});
it.each(["qlpack.lock.yml", "codeql-pack.lock.yml"])(
"does not resolve or install dependencies with %s",
async (lockFileName) => {
const lockFilePath = join(packPath, lockFileName);
const lockFileContents = `${lockFileName} contents`;
await outputFile(lockFilePath, lockFileContents);

const { cleanup } = await createLockFileForStandardQuery(
mockCli,
queryPath,
);

expect({
cleanup,
packResolveDependenciesCallCount:
packResolveDependencies.mock.calls.length,
clearCacheCallCount: clearCache.mock.calls.length,
packInstallCallCount: packInstall.mock.calls.length,
lockFileContents: await readFile(lockFilePath, "utf8"),
}).toEqual({
cleanup: undefined,
packResolveDependenciesCallCount: 0,
clearCacheCallCount: 0,
packInstallCallCount: 0,
lockFileContents,
});
},
);
});

describe("when the lock file does not exist", () => {
Expand Down