Skip to content

Commit 025737a

Browse files
authored
Merge pull request #3797 from reitowo/main-multiple-folder
Allow import all database subfolders by selecting a folder
2 parents 9a0bff6 + e7e95e2 commit 025737a

File tree

4 files changed

+121
-10
lines changed

4 files changed

+121
-10
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+
- Add a palette command that allows importing all databases directly inside of a parent folder. [3797](https://github.com/github/vscode-codeql/pull/3797)
6+
57
## 1.16.1 - 6 November 2024
68

79
- Support result columns of type `QlBuiltins::BigInt` in quick evaluations. [#3647](https://github.com/github/vscode-codeql/pull/3647)

extensions/ql-vscode/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,10 @@
839839
"command": "codeQL.chooseDatabaseFolder",
840840
"title": "CodeQL: Choose Database from Folder"
841841
},
842+
{
843+
"command": "codeQL.chooseDatabaseFoldersParent",
844+
"title": "CodeQL: Import All Databases Directly Contained in a Parent Folder"
845+
},
842846
{
843847
"command": "codeQL.chooseDatabaseArchive",
844848
"title": "CodeQL: Choose Database from Archive"

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ export type LanguageSelectionCommands = {
211211
export type LocalDatabasesCommands = {
212212
// Command palette commands
213213
"codeQL.chooseDatabaseFolder": () => Promise<void>;
214+
"codeQL.chooseDatabaseFoldersParent": () => Promise<void>;
214215
"codeQL.chooseDatabaseArchive": () => Promise<void>;
215216
"codeQL.chooseDatabaseInternet": () => Promise<void>;
216217
"codeQL.chooseDatabaseGithub": () => Promise<void>;

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

Lines changed: 114 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
ThemeIcon,
1717
ThemeColor,
1818
workspace,
19+
FileType,
1920
} from "vscode";
2021
import { pathExists, stat, readdir, remove } from "fs-extra";
2122

@@ -36,6 +37,7 @@ import {
3637
import {
3738
showAndLogExceptionWithTelemetry,
3839
showAndLogErrorMessage,
40+
showAndLogInformationMessage,
3941
} from "../common/logging";
4042
import type { DatabaseFetcher } from "./database-fetcher";
4143
import { asError, asyncFilter, getErrorMessage } from "../common/helpers-pure";
@@ -267,6 +269,8 @@ export class DatabaseUI extends DisposableObject {
267269
"codeQL.getCurrentDatabase": this.handleGetCurrentDatabase.bind(this),
268270
"codeQL.chooseDatabaseFolder":
269271
this.handleChooseDatabaseFolderFromPalette.bind(this),
272+
"codeQL.chooseDatabaseFoldersParent":
273+
this.handleChooseDatabaseFoldersParentFromPalette.bind(this),
270274
"codeQL.chooseDatabaseArchive":
271275
this.handleChooseDatabaseArchiveFromPalette.bind(this),
272276
"codeQL.chooseDatabaseInternet":
@@ -359,6 +363,12 @@ export class DatabaseUI extends DisposableObject {
359363
);
360364
}
361365

366+
private async handleChooseDatabaseFoldersParentFromPalette(): Promise<void> {
367+
return withProgress(async (progress) => {
368+
await this.chooseDatabasesParentFolder(progress);
369+
});
370+
}
371+
362372
private async handleSetDefaultTourDatabase(): Promise<void> {
363373
return withProgress(
364374
async () => {
@@ -957,26 +967,22 @@ export class DatabaseUI extends DisposableObject {
957967
}
958968

959969
/**
960-
* Ask the user for a database directory. Returns the chosen database, or `undefined` if the
961-
* operation was canceled.
970+
* Import database from uri. Returns the imported database, or `undefined` if the
971+
* operation was unsuccessful or canceled.
962972
*/
963-
private async chooseAndSetDatabase(
973+
private async importDatabase(
974+
uri: Uri,
964975
byFolder: boolean,
965976
progress: ProgressCallback,
966977
): Promise<DatabaseItem | undefined> {
967-
const uri = await chooseDatabaseDir(byFolder);
968-
if (!uri) {
969-
return undefined;
970-
}
971-
972-
if (byFolder && !uri.fsPath.endsWith("testproj")) {
978+
if (byFolder && !uri.fsPath.endsWith(".testproj")) {
973979
const fixedUri = await this.fixDbUri(uri);
974980
// we are selecting a database folder
975981
return await this.databaseManager.openDatabase(fixedUri, {
976982
type: "folder",
977983
});
978984
} else {
979-
// we are selecting a database archive or a testproj.
985+
// we are selecting a database archive or a .testproj.
980986
// Unzip archives (if an archive) and copy into a workspace-controlled area
981987
// before importing.
982988
return await this.databaseFetcher.importLocalDatabase(
@@ -986,6 +992,104 @@ export class DatabaseUI extends DisposableObject {
986992
}
987993
}
988994

995+
/**
996+
* Ask the user for a database directory. Returns the chosen database, or `undefined` if the
997+
* operation was canceled.
998+
*/
999+
private async chooseAndSetDatabase(
1000+
byFolder: boolean,
1001+
progress: ProgressCallback,
1002+
): Promise<DatabaseItem | undefined> {
1003+
const uri = await chooseDatabaseDir(byFolder);
1004+
if (!uri) {
1005+
return undefined;
1006+
}
1007+
1008+
return await this.importDatabase(uri, byFolder, progress);
1009+
}
1010+
1011+
/**
1012+
* Ask the user for a parent directory that contains all databases.
1013+
* Returns all valid databases, or `undefined` if the operation was canceled.
1014+
*/
1015+
private async chooseDatabasesParentFolder(
1016+
progress: ProgressCallback,
1017+
): Promise<DatabaseItem[] | undefined> {
1018+
const uri = await chooseDatabaseDir(true);
1019+
if (!uri) {
1020+
return undefined;
1021+
}
1022+
1023+
const databases: DatabaseItem[] = [];
1024+
const failures: string[] = [];
1025+
const entries = await workspace.fs.readDirectory(uri);
1026+
const validFileTypes = [FileType.File, FileType.Directory];
1027+
1028+
for (const [index, entry] of entries.entries()) {
1029+
progress({
1030+
step: index + 1,
1031+
maxStep: entries.length,
1032+
message: `Importing '${entry[0]}'`,
1033+
});
1034+
1035+
const subProgress: ProgressCallback = (p) => {
1036+
progress({
1037+
step: index + 1,
1038+
maxStep: entries.length,
1039+
message: `Importing '${entry[0]}': (${p.step}/${p.maxStep}) ${p.message}`,
1040+
});
1041+
};
1042+
1043+
if (!validFileTypes.includes(entry[1])) {
1044+
void this.app.logger.log(
1045+
`Skipping import for '${entry}', invalid file type: ${entry[1]}`,
1046+
);
1047+
continue;
1048+
}
1049+
1050+
try {
1051+
const databaseUri = Uri.joinPath(uri, entry[0]);
1052+
void this.app.logger.log(`Importing from ${databaseUri}`);
1053+
1054+
const database = await this.importDatabase(
1055+
databaseUri,
1056+
entry[1] === FileType.Directory,
1057+
subProgress,
1058+
);
1059+
if (database) {
1060+
databases.push(database);
1061+
} else {
1062+
failures.push(entry[0]);
1063+
}
1064+
} catch (e) {
1065+
failures.push(`${entry[0]}: ${getErrorMessage(e)}`.trim());
1066+
}
1067+
}
1068+
1069+
if (failures.length) {
1070+
void showAndLogErrorMessage(
1071+
this.app.logger,
1072+
`Failed to import ${failures.length} database(s), successfully imported ${databases.length} database(s).`,
1073+
{
1074+
fullMessage: `Failed to import ${failures.length} database(s), successfully imported ${databases.length} database(s).\nFailed databases:\n - ${failures.join("\n - ")}`,
1075+
},
1076+
);
1077+
} else if (databases.length === 0) {
1078+
void showAndLogErrorMessage(
1079+
this.app.logger,
1080+
`No database folder to import.`,
1081+
);
1082+
return undefined;
1083+
} else {
1084+
void showAndLogInformationMessage(
1085+
this.app.logger,
1086+
`Successfully imported ${databases.length} database(s).`,
1087+
);
1088+
}
1089+
1090+
return databases;
1091+
}
1092+
9891093
/**
9901094
* Perform some heuristics to ensure a proper database location is chosen.
9911095
*

0 commit comments

Comments
 (0)