Skip to content

Commit 0454130

Browse files
Merge branch 'main' into robertbrignull/remove_withInheritedProgress
2 parents 16b8f84 + 06f9f2c commit 0454130

File tree

28 files changed

+491
-45
lines changed

28 files changed

+491
-45
lines changed

extensions/ql-vscode/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## [UNRELEASED]
44

5+
- Databases created from [CodeQL test cases](https://docs.github.com/en/code-security/codeql-cli/using-the-advanced-functionality-of-the-codeql-cli/testing-custom-queries) are now copied into a shared VS Code storage location. This avoids a bug where re-running test cases would fail if the test's database is already imported into the workspace. [#3433](https://github.com/github/vscode-codeql/pull/3433)
6+
57
## 1.12.3 - 29 February 2024
68

79
- Update variant analysis view to show when cancelation is in progress. [#3405](https://github.com/github/vscode-codeql/pull/3405)

extensions/ql-vscode/package.json

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,10 @@
738738
"command": "codeQL.setCurrentDatabase",
739739
"title": "CodeQL: Set Current Database"
740740
},
741+
{
742+
"command": "codeQL.importTestDatabase",
743+
"title": "CodeQL: (Re-)Import Test Database"
744+
},
741745
{
742746
"command": "codeQL.getCurrentDatabase",
743747
"title": "CodeQL: Get Current Database"
@@ -1322,7 +1326,12 @@
13221326
{
13231327
"command": "codeQL.setCurrentDatabase",
13241328
"group": "9_qlCommands",
1325-
"when": "resourceScheme == codeql-zip-archive || explorerResourceIsFolder || resourceExtname == .zip"
1329+
"when": "resourceExtname != .testproj && (resourceScheme == codeql-zip-archive || explorerResourceIsFolder || resourceExtname == .zipz)"
1330+
},
1331+
{
1332+
"command": "codeQL.importTestDatabase",
1333+
"group": "9_qlCommands",
1334+
"when": "explorerResourceIsFolder && resourceExtname == .testproj"
13261335
},
13271336
{
13281337
"command": "codeQL.viewAstContextExplorer",
@@ -1476,6 +1485,10 @@
14761485
"command": "codeQL.setCurrentDatabase",
14771486
"when": "false"
14781487
},
1488+
{
1489+
"command": "codeQL.importTestDatabase",
1490+
"when": "false"
1491+
},
14791492
{
14801493
"command": "codeQL.getCurrentDatabase",
14811494
"when": "false"
@@ -2018,7 +2031,6 @@
20182031
"@types/tar-stream": "^3.1.3",
20192032
"@types/through2": "^2.0.36",
20202033
"@types/tmp": "^0.2.6",
2021-
"@types/unzipper": "^0.10.1",
20222034
"@types/vscode": "^1.82.0",
20232035
"@types/yauzl": "^2.10.3",
20242036
"@typescript-eslint/eslint-plugin": "^6.19.0",

extensions/ql-vscode/src/common/commands.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ export type LocalDatabasesCommands = {
220220

221221
// Explorer context menu
222222
"codeQL.setCurrentDatabase": (uri: Uri) => Promise<void>;
223+
"codeQL.importTestDatabase": (uri: Uri) => Promise<void>;
223224

224225
// Database panel view title commands
225226
"codeQLDatabases.chooseDatabaseFolder": () => Promise<void>;

extensions/ql-vscode/src/common/interface-types.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import type { Method } from "../model-editor/method";
1414
import type { ModeledMethod } from "../model-editor/modeled-method";
1515
import type {
1616
MethodModelingPanelViewState,
17+
ModelAlertsViewState,
1718
ModelEditorViewState,
1819
} from "../model-editor/shared/view-state";
1920
import type { Mode } from "../model-editor/shared/mode";
@@ -726,10 +727,11 @@ export type ToMethodModelingMessage =
726727
| SetInProgressMessage
727728
| SetProcessedByAutoModelMessage;
728729

729-
interface SetModelAlertsMessage {
730-
t: "setModelAlerts";
730+
interface SetModelAlertsViewStateMessage {
731+
t: "setModelAlertsViewState";
732+
viewState: ModelAlertsViewState;
731733
}
732734

733-
export type ToModelAlertsMessage = SetModelAlertsMessage;
735+
export type ToModelAlertsMessage = SetModelAlertsViewStateMessage;
734736

735737
export type FromModelAlertsMessage = CommonFromViewMessages;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface ModelPackDetails {
2+
name: string;
3+
path: string;
4+
}

extensions/ql-vscode/src/databases/database-fetcher.ts

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
createWriteStream,
1212
remove,
1313
readdir,
14+
copy,
1415
} from "fs-extra";
1516
import { basename, join } from "path";
1617
import type { Octokit } from "@octokit/rest";
@@ -64,7 +65,7 @@ export async function promptImportInternetDatabase(
6465

6566
validateUrl(databaseUrl);
6667

67-
const item = await databaseArchiveFetcher(
68+
const item = await fetchDatabaseToWorkspaceStorage(
6869
databaseUrl,
6970
{},
7071
databaseManager,
@@ -258,7 +259,7 @@ export async function downloadGitHubDatabaseFromUrl(
258259
* We only need the actual token string.
259260
*/
260261
const octokitToken = ((await octokit.auth()) as { token: string })?.token;
261-
return await databaseArchiveFetcher(
262+
return await fetchDatabaseToWorkspaceStorage(
262263
databaseUrl,
263264
{
264265
Accept: "application/zip",
@@ -282,14 +283,15 @@ export async function downloadGitHubDatabaseFromUrl(
282283
}
283284

284285
/**
285-
* Imports a database from a local archive.
286+
* Imports a database from a local archive or a test database that is in a folder
287+
* ending with `.testproj`.
286288
*
287-
* @param databaseUrl the file url of the archive to import
289+
* @param databaseUrl the file url of the archive or directory to import
288290
* @param databaseManager the DatabaseManager
289291
* @param storagePath where to store the unzipped database.
290292
* @param cli the CodeQL CLI server
291293
*/
292-
export async function importArchiveDatabase(
294+
export async function importLocalDatabase(
293295
commandManager: AppCommandManager,
294296
databaseUrl: string,
295297
databaseManager: DatabaseManager,
@@ -298,24 +300,27 @@ export async function importArchiveDatabase(
298300
cli: CodeQLCliServer,
299301
): Promise<DatabaseItem | undefined> {
300302
try {
301-
const item = await databaseArchiveFetcher(
303+
const origin: DatabaseOrigin = {
304+
type: databaseUrl.endsWith(".testproj") ? "testproj" : "archive",
305+
path: Uri.parse(databaseUrl).fsPath,
306+
};
307+
const item = await fetchDatabaseToWorkspaceStorage(
302308
databaseUrl,
303309
{},
304310
databaseManager,
305311
storagePath,
306312
undefined,
307-
{
308-
type: "archive",
309-
path: databaseUrl,
310-
},
313+
origin,
311314
progress,
312315
cli,
313316
);
314317
if (item) {
315318
await commandManager.execute("codeQLDatabases.focus");
316319
void showAndLogInformationMessage(
317320
extLogger,
318-
"Database unzipped and imported successfully.",
321+
origin.type === "testproj"
322+
? "Test database imported successfully."
323+
: "Database unzipped and imported successfully.",
319324
);
320325
}
321326
return item;
@@ -332,10 +337,10 @@ export async function importArchiveDatabase(
332337
}
333338

334339
/**
335-
* Fetches an archive database. The database might be on the internet
340+
* Fetches a database into workspace storage. The database might be on the internet
336341
* or in the local filesystem.
337342
*
338-
* @param databaseUrl URL from which to grab the database
343+
* @param databaseUrl URL from which to grab the database. This could be a local archive file, a local directory, or a remote URL.
339344
* @param requestHeaders Headers to send with the request
340345
* @param databaseManager the DatabaseManager
341346
* @param storagePath where to store the unzipped database.
@@ -346,7 +351,7 @@ export async function importArchiveDatabase(
346351
* @param makeSelected make the new database selected in the databases panel (default: true)
347352
* @param addSourceArchiveFolder whether to add a workspace folder containing the source archive to the workspace
348353
*/
349-
async function databaseArchiveFetcher(
354+
async function fetchDatabaseToWorkspaceStorage(
350355
databaseUrl: string,
351356
requestHeaders: { [key: string]: string },
352357
databaseManager: DatabaseManager,
@@ -374,7 +379,11 @@ async function databaseArchiveFetcher(
374379
);
375380

376381
if (isFile(databaseUrl)) {
377-
await readAndUnzip(databaseUrl, unzipPath, cli, progress);
382+
if (origin.type === "testproj") {
383+
await copyDatabase(databaseUrl, unzipPath, progress);
384+
} else {
385+
await readAndUnzip(databaseUrl, unzipPath, cli, progress);
386+
}
378387
} else {
379388
await fetchAndUnzip(databaseUrl, requestHeaders, unzipPath, cli, progress);
380389
}
@@ -438,6 +447,8 @@ async function getStorageFolder(
438447
lastName = basename(url.path).substring(0, 250);
439448
if (lastName.endsWith(".zip")) {
440449
lastName = lastName.substring(0, lastName.length - 4);
450+
} else if (lastName.endsWith(".testproj")) {
451+
lastName = lastName.substring(0, lastName.length - 9);
441452
}
442453
}
443454

@@ -484,6 +495,26 @@ function validateUrl(databaseUrl: string) {
484495
}
485496
}
486497

498+
/**
499+
* Copies a database folder from the file system into the workspace storage.
500+
* @param scrDirURL the original location of the database as a URL string
501+
* @param destDir the location to copy the database to. This should be a folder in the workspace storage.
502+
* @param progress callback to send progress messages to
503+
*/
504+
async function copyDatabase(
505+
srcDirURL: string,
506+
destDir: string,
507+
progress?: ProgressCallback,
508+
) {
509+
progress?.({
510+
maxStep: 10,
511+
step: 9,
512+
message: `Copying database ${basename(destDir)} into the workspace`,
513+
});
514+
await ensureDir(destDir);
515+
await copy(Uri.parse(srcDirURL).fsPath, destDir);
516+
}
517+
487518
async function readAndUnzip(
488519
zipUrl: string,
489520
unzipPath: string,

extensions/ql-vscode/src/databases/local-databases-ui.ts

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import {
3838
showAndLogErrorMessage,
3939
} from "../common/logging";
4040
import {
41-
importArchiveDatabase,
41+
importLocalDatabase,
4242
promptImportGithubDatabase,
4343
promptImportInternetDatabase,
4444
} from "./database-fetcher";
@@ -140,7 +140,8 @@ class DatabaseTreeDataProvider
140140
item.iconPath = new ThemeIcon("error", new ThemeColor("errorForeground"));
141141
}
142142
item.tooltip = element.databaseUri.fsPath;
143-
item.description = element.language;
143+
item.description =
144+
element.language + (element.origin?.type === "testproj" ? " (test)" : "");
144145
return item;
145146
}
146147

@@ -276,6 +277,7 @@ export class DatabaseUI extends DisposableObject {
276277
this.handleChooseDatabaseInternet.bind(this),
277278
"codeQL.chooseDatabaseGithub": this.handleChooseDatabaseGithub.bind(this),
278279
"codeQL.setCurrentDatabase": this.handleSetCurrentDatabase.bind(this),
280+
"codeQL.importTestDatabase": this.handleImportTestDatabase.bind(this),
279281
"codeQL.setDefaultTourDatabase":
280282
this.handleSetDefaultTourDatabase.bind(this),
281283
"codeQL.upgradeCurrentDatabase":
@@ -705,7 +707,7 @@ export class DatabaseUI extends DisposableObject {
705707
try {
706708
// Assume user has selected an archive if the file has a .zip extension
707709
if (uri.path.endsWith(".zip")) {
708-
await importArchiveDatabase(
710+
await importLocalDatabase(
709711
this.app.commands,
710712
uri.toString(true),
711713
this.databaseManager,
@@ -733,6 +735,60 @@ export class DatabaseUI extends DisposableObject {
733735
);
734736
}
735737

738+
private async handleImportTestDatabase(uri: Uri): Promise<void> {
739+
return withProgress(
740+
async (progress) => {
741+
try {
742+
if (!uri.path.endsWith(".testproj")) {
743+
throw new Error(
744+
"Please select a valid test database to import. Test databases end with `.testproj`.",
745+
);
746+
}
747+
748+
// Check if the database is already in the workspace. If
749+
// so, delete it first before importing the new one.
750+
const existingItem = this.databaseManager.findTestDatabase(uri);
751+
const baseName = basename(uri.fsPath);
752+
if (existingItem !== undefined) {
753+
progress({
754+
maxStep: 9,
755+
step: 1,
756+
message: `Removing existing test database ${baseName}`,
757+
});
758+
await this.databaseManager.removeDatabaseItem(existingItem);
759+
}
760+
761+
await importLocalDatabase(
762+
this.app.commands,
763+
uri.toString(true),
764+
this.databaseManager,
765+
this.storagePath,
766+
progress,
767+
this.queryServer.cliServer,
768+
);
769+
770+
if (existingItem !== undefined) {
771+
progress({
772+
maxStep: 9,
773+
step: 9,
774+
message: `Successfully re-imported ${baseName}`,
775+
});
776+
}
777+
} catch (e) {
778+
// rethrow and let this be handled by default error handling.
779+
throw new Error(
780+
`Could not set database to ${basename(
781+
uri.fsPath,
782+
)}. Reason: ${getErrorMessage(e)}`,
783+
);
784+
}
785+
},
786+
{
787+
title: "(Re-)importing test database from directory",
788+
},
789+
);
790+
}
791+
736792
private async handleRemoveDatabase(
737793
databaseItems: DatabaseItem[],
738794
): Promise<void> {
@@ -948,7 +1004,7 @@ export class DatabaseUI extends DisposableObject {
9481004
} else {
9491005
// we are selecting a database archive. Must unzip into a workspace-controlled area
9501006
// before importing.
951-
return await importArchiveDatabase(
1007+
return await importLocalDatabase(
9521008
this.app.commands,
9531009
uri.toString(true),
9541010
this.databaseManager,

0 commit comments

Comments
 (0)