Skip to content

Commit bbc09f3

Browse files
committed
Merge remote-tracking branch 'origin/main' into koesie10/unique-database-names
2 parents fe01360 + 1ab3dea commit bbc09f3

17 files changed

Lines changed: 664 additions & 397 deletions

File tree

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,32 @@ export function findCommonParentDir(...paths: string[]): string {
176176
function isTopLevelPath(path: string): boolean {
177177
return dirname(path) === path;
178178
}
179+
180+
/**
181+
* Recursively looks for a file in a directory. If the file exists, then returns the directory containing the file.
182+
*
183+
* @param dir The directory to search
184+
* @param toFind The file to recursively look for in this directory
185+
*
186+
* @returns the directory containing the file, or undefined if not found.
187+
*/
188+
export async function findDirWithFile(
189+
dir: string,
190+
...toFind: string[]
191+
): Promise<string | undefined> {
192+
if (!(await stat(dir)).isDirectory()) {
193+
return;
194+
}
195+
const files = await readdir(dir);
196+
if (toFind.some((file) => files.includes(file))) {
197+
return dir;
198+
}
199+
for (const file of files) {
200+
const newPath = join(dir, file);
201+
const result = await findDirWithFile(newPath, ...toFind);
202+
if (result) {
203+
return result;
204+
}
205+
}
206+
return;
207+
}

extensions/ql-vscode/src/common/vscode/abstract-webview-view-provider.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
import type {
2-
CancellationToken,
3-
WebviewView,
4-
WebviewViewProvider,
5-
WebviewViewResolveContext,
6-
} from "vscode";
1+
import type { WebviewView, WebviewViewProvider } from "vscode";
72
import { Uri } from "vscode";
83
import type { WebviewKind, WebviewMessage } from "./webview-html";
94
import { getHtmlForWebview } from "./webview-html";
@@ -28,11 +23,7 @@ export abstract class AbstractWebviewViewProvider<
2823
* This is called when a view first becomes visible. This may happen when the view is
2924
* first loaded or when the user hides and then shows a view again.
3025
*/
31-
public resolveWebviewView(
32-
webviewView: WebviewView,
33-
_context: WebviewViewResolveContext,
34-
_token: CancellationToken,
35-
) {
26+
public resolveWebviewView(webviewView: WebviewView) {
3627
webviewView.webview.options = {
3728
enableScripts: true,
3829
localResourceRoots: [Uri.file(this.app.extensionPath)],

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

Lines changed: 3 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
pathExists,
1111
createWriteStream,
1212
remove,
13-
stat,
1413
readdir,
1514
} from "fs-extra";
1615
import { basename, join } from "path";
@@ -36,11 +35,12 @@ import {
3635
} from "../config";
3736
import { showAndLogInformationMessage } from "../common/logging";
3837
import { AppOctokit } from "../common/octokit";
39-
import { getLanguageDisplayName } from "../common/query-language";
4038
import type { DatabaseOrigin } from "./local-databases/database-origin";
4139
import { createTimeoutSignal } from "../common/fetch-stream";
4240
import type { App } from "../common/app";
4341
import { createFilenameFromString } from "../common/filenames";
42+
import { findDirWithFile } from "../common/files";
43+
import { convertGithubNwoToDatabaseUrl } from "./github-databases/api";
4444

4545
/**
4646
* Prompts a user to fetch a database from a remote location. Database is assumed to be an archive file.
@@ -406,6 +406,7 @@ async function databaseArchiveFetcher(
406406
nameOverride,
407407
{
408408
addSourceArchiveFolder,
409+
extensionManagedLocation: unzipPath,
409410
},
410411
);
411412
return item;
@@ -617,125 +618,6 @@ function isFile(databaseUrl: string) {
617618
return Uri.parse(databaseUrl).scheme === "file";
618619
}
619620

620-
/**
621-
* Recursively looks for a file in a directory. If the file exists, then returns the directory containing the file.
622-
*
623-
* @param dir The directory to search
624-
* @param toFind The file to recursively look for in this directory
625-
*
626-
* @returns the directory containing the file, or undefined if not found.
627-
*/
628-
// exported for testing
629-
export async function findDirWithFile(
630-
dir: string,
631-
...toFind: string[]
632-
): Promise<string | undefined> {
633-
if (!(await stat(dir)).isDirectory()) {
634-
return;
635-
}
636-
const files = await readdir(dir);
637-
if (toFind.some((file) => files.includes(file))) {
638-
return dir;
639-
}
640-
for (const file of files) {
641-
const newPath = join(dir, file);
642-
const result = await findDirWithFile(newPath, ...toFind);
643-
if (result) {
644-
return result;
645-
}
646-
}
647-
return;
648-
}
649-
650-
export async function convertGithubNwoToDatabaseUrl(
651-
nwo: string,
652-
octokit: Octokit,
653-
progress: ProgressCallback,
654-
language?: string,
655-
): Promise<
656-
| {
657-
databaseUrl: string;
658-
owner: string;
659-
name: string;
660-
databaseId: number;
661-
databaseCreatedAt: string;
662-
commitOid: string | null;
663-
}
664-
| undefined
665-
> {
666-
try {
667-
const [owner, repo] = nwo.split("/");
668-
669-
const response = await octokit.rest.codeScanning.listCodeqlDatabases({
670-
owner,
671-
repo,
672-
});
673-
674-
const languages = response.data.map((db) => db.language);
675-
676-
if (!language || !languages.includes(language)) {
677-
language = await promptForLanguage(languages, progress);
678-
if (!language) {
679-
return;
680-
}
681-
}
682-
683-
const databaseForLanguage = response.data.find(
684-
(db) => db.language === language,
685-
);
686-
if (!databaseForLanguage) {
687-
throw new Error(`No database found for language '${language}'`);
688-
}
689-
690-
return {
691-
databaseUrl: databaseForLanguage.url,
692-
owner,
693-
name: repo,
694-
databaseId: databaseForLanguage.id,
695-
databaseCreatedAt: databaseForLanguage.created_at,
696-
commitOid: databaseForLanguage.commit_oid ?? null,
697-
};
698-
} catch (e) {
699-
void extLogger.log(`Error: ${getErrorMessage(e)}`);
700-
throw new Error(`Unable to get database for '${nwo}'`);
701-
}
702-
}
703-
704-
export async function promptForLanguage(
705-
languages: string[],
706-
progress: ProgressCallback | undefined,
707-
): Promise<string | undefined> {
708-
progress?.({
709-
message: "Choose language",
710-
step: 2,
711-
maxStep: 2,
712-
});
713-
if (!languages.length) {
714-
throw new Error("No databases found");
715-
}
716-
if (languages.length === 1) {
717-
return languages[0];
718-
}
719-
720-
const items = languages
721-
.map((language) => ({
722-
label: getLanguageDisplayName(language),
723-
description: language,
724-
language,
725-
}))
726-
.sort((a, b) => a.label.localeCompare(b.label));
727-
728-
const selectedItem = await window.showQuickPick(items, {
729-
placeHolder: "Select the database language to download:",
730-
ignoreFocusOut: true,
731-
});
732-
if (!selectedItem) {
733-
return undefined;
734-
}
735-
736-
return selectedItem.language;
737-
}
738-
739621
/**
740622
* Databases created by the old odasa tool will not have a zipped
741623
* source location. However, this extension works better if sources

extensions/ql-vscode/src/databases/github-databases/api.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ import { showNeverAskAgainDialog } from "../../common/vscode/dialog";
55
import type { GitHubDatabaseConfig } from "../../config";
66
import type { Credentials } from "../../common/authentication";
77
import { AppOctokit } from "../../common/octokit";
8+
import type { ProgressCallback } from "../../common/vscode/progress";
9+
import { getErrorMessage } from "../../common/helpers-pure";
10+
import { getLanguageDisplayName } from "../../common/query-language";
11+
import { window } from "vscode";
12+
import { extLogger } from "../../common/logging/vscode";
813

914
export type CodeqlDatabase =
1015
RestEndpointMethodTypes["codeScanning"]["listCodeqlDatabases"]["response"]["data"][number];
@@ -108,3 +113,92 @@ export async function listDatabases(
108113
octokit,
109114
};
110115
}
116+
117+
export async function convertGithubNwoToDatabaseUrl(
118+
nwo: string,
119+
octokit: Octokit,
120+
progress: ProgressCallback,
121+
language?: string,
122+
): Promise<
123+
| {
124+
databaseUrl: string;
125+
owner: string;
126+
name: string;
127+
databaseId: number;
128+
databaseCreatedAt: string;
129+
commitOid: string | null;
130+
}
131+
| undefined
132+
> {
133+
try {
134+
const [owner, repo] = nwo.split("/");
135+
136+
const response = await octokit.rest.codeScanning.listCodeqlDatabases({
137+
owner,
138+
repo,
139+
});
140+
141+
const languages = response.data.map((db) => db.language);
142+
143+
if (!language || !languages.includes(language)) {
144+
language = await promptForLanguage(languages, progress);
145+
if (!language) {
146+
return;
147+
}
148+
}
149+
150+
const databaseForLanguage = response.data.find(
151+
(db) => db.language === language,
152+
);
153+
if (!databaseForLanguage) {
154+
throw new Error(`No database found for language '${language}'`);
155+
}
156+
157+
return {
158+
databaseUrl: databaseForLanguage.url,
159+
owner,
160+
name: repo,
161+
databaseId: databaseForLanguage.id,
162+
databaseCreatedAt: databaseForLanguage.created_at,
163+
commitOid: databaseForLanguage.commit_oid ?? null,
164+
};
165+
} catch (e) {
166+
void extLogger.log(`Error: ${getErrorMessage(e)}`);
167+
throw new Error(`Unable to get database for '${nwo}'`);
168+
}
169+
}
170+
171+
async function promptForLanguage(
172+
languages: string[],
173+
progress: ProgressCallback | undefined,
174+
): Promise<string | undefined> {
175+
progress?.({
176+
message: "Choose language",
177+
step: 2,
178+
maxStep: 2,
179+
});
180+
if (!languages.length) {
181+
throw new Error("No databases found");
182+
}
183+
if (languages.length === 1) {
184+
return languages[0];
185+
}
186+
187+
const items = languages
188+
.map((language) => ({
189+
label: getLanguageDisplayName(language),
190+
description: language,
191+
language,
192+
}))
193+
.sort((a, b) => a.label.localeCompare(b.label));
194+
195+
const selectedItem = await window.showQuickPick(items, {
196+
placeHolder: "Select the database language to download:",
197+
ignoreFocusOut: true,
198+
});
199+
if (!selectedItem) {
200+
return undefined;
201+
}
202+
203+
return selectedItem.language;
204+
}

extensions/ql-vscode/src/databases/local-databases/database-item-impl.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ export class DatabaseItemImpl implements DatabaseItem {
6666
return this.options.origin;
6767
}
6868

69+
public get extensionManagedLocation(): string | undefined {
70+
return this.options.extensionManagedLocation;
71+
}
72+
6973
public resolveSourceFile(uriStr: string | undefined): Uri {
7074
const sourceArchive = this.sourceArchive;
7175
const uri = uriStr ? Uri.parse(uriStr, true) : undefined;

extensions/ql-vscode/src/databases/local-databases/database-item.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ export interface DatabaseItem {
3131
*/
3232
readonly origin: DatabaseOrigin | undefined;
3333

34+
/**
35+
* The location of the base storage location as managed by the extension, or undefined
36+
* if unknown or not managed by the extension.
37+
*/
38+
readonly extensionManagedLocation: string | undefined;
39+
3440
/** If the database is invalid, describes why. */
3541
readonly error: Error | undefined;
3642

0 commit comments

Comments
 (0)