Allow download from lgtm
Adds a new command to download databases from lgtm. It's working, though need to create better error messages on failure.
This commit is contained in:
committed by
Andrew Eisenberg
parent
dd9fafc27c
commit
1c1dbc95c7
5
extensions/ql-vscode/media/dark/lgtm-plus.svg
Normal file
5
extensions/ql-vscode/media/dark/lgtm-plus.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
|
||||
<path d="M16.010 6.49c-3.885 0-7.167 0.906-9.328 2.813-0.063-0.12-0.109-0.219-0.188-0.339-0.224-0.365-0.438-0.776-1.104-1.188-0.411-0.26-0.87-0.438-1.349-0.516-0.208-0.021-0.422-0.021-0.63 0l0.135-0.016c-1.214 0-1.922 0.724-2.385 1.354-0.458 0.625-0.755 1.328-0.948 2.099-0.38 1.542-0.385 3.536 1.083 5.026 0.766 0.781 1.667 1.151 2.484 1.37 0.156 0.042 0.297 0.052 0.448 0.083 0.531 2.521 2.104 4.656 4.208 5.839v0.005c1.24 0.693 2.417 1.010 3.297 1.349 1.234 0.479 2.536 1 4.052 1.135l0.078 0.005h0.198c1.745 0 3.063-0.703 4.203-1.141 0.875-0.333 2.052-0.641 3.302-1.344 0.578-0.323 1.115-0.719 1.594-1.172 1.318-1.234 2.229-2.839 2.625-4.599 1.115-0.182 2.141-0.719 2.922-1.536 1.464-1.484 1.458-3.479 1.078-5.021-0.193-0.771-0.49-1.474-0.948-2.099-0.458-0.63-1.172-1.354-2.385-1.354l0.135 0.016c-0.208-0.021-0.422-0.021-0.63 0-0.479 0.078-0.938 0.255-1.344 0.516-0.667 0.411-0.88 0.823-1.104 1.182-0.073 0.12-0.12 0.219-0.188 0.333-2.156-1.901-5.432-2.802-9.313-2.802zM16.042 8.313c4.745 0 8.016 1.422 9.411 3.964 0.839-0.323 1.453-2.521 2.146-2.948 0.563-0.344 0.885-0.26 0.885-0.26 1.271 0 2.578 3.729 0.953 5.38-0.859 0.875-2.443 1.12-3.229 1.057-0.063 2.542-1.542 4.833-3.5 5.932-1 0.563-2.068 0.854-3.063 1.234-1.229 0.469-2.38 1.016-3.547 1.016h-0.125c-1.161-0.099-2.318-0.542-3.547-1.016-0.995-0.38-2.068-0.682-3.063-1.24-1.948-1.099-3.427-3.391-3.49-5.927-0.781 0.068-2.385-0.177-3.245-1.057-1.625-1.651-0.318-5.38 0.948-5.38 0 0 0.328-0.083 0.885 0.26 0.698 0.427 1.318 2.646 2.161 2.953 1.391-2.547 4.667-3.969 9.417-3.969zM10.875 11.422c-2.276-0.042-4.146 1.792-4.146 4.068 0 2.281 1.87 4.115 4.146 4.073 5.328-0.099 5.328-8.047 0-8.141zM21.208 11.422c-5.427 0-5.427 8.141 0 8.141s5.427-8.141 0-8.141zM11.453 13.708c2.349 0.063 2.349 3.552 0 3.615-1.182 0-2.042-1.115-1.75-2.255 0.318 0.771 1.469 0.547 1.464-0.292 0-0.406-0.318-0.745-0.729-0.76 0.302-0.203 0.656-0.313 1.016-0.307zM20.641 13.708c2.344 0.063 2.344 3.552 0 3.615-1.182 0-2.047-1.115-1.755-2.255 0.229 0.552 0.979 0.641 1.328 0.146 0.344-0.49 0.010-1.167-0.589-1.193 0.297-0.208 0.651-0.313 1.016-0.313zM15.359 19.906c-0.318 0.026-0.5 0.193-0.5 0.635 0 0.281 0.182 0.484 0.5 0.484 0.229 0 0.266-0.323 0.047-0.375-0.031-0.005-0.172-0.057-0.172-0.182 0-0.12 0-0.167 0.24-0.198 0.104-0.016 0.156-0.141 0.125-0.24s-0.125-0.135-0.24-0.125zM16.724 19.906c-0.115-0.005-0.208 0.026-0.24 0.125s0.021 0.224 0.125 0.24c0.24 0.031 0.24 0.078 0.24 0.198 0 0.125-0.141 0.177-0.172 0.182-0.219 0.052-0.182 0.375 0.042 0.375 0.323 0 0.51-0.203 0.51-0.484 0-0.443-0.188-0.609-0.505-0.635z" fill="#C5C5C5"/>
|
||||
<line y2="24" x2="16" y1="26" x1="32" stroke-width="2" stroke="green" fill="none"/>
|
||||
<line y2="16" x2="24" y1="32" x1="24" stroke-width="1" stroke="green" fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
5
extensions/ql-vscode/media/light/lgtm-plus.svg
Normal file
5
extensions/ql-vscode/media/light/lgtm-plus.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
|
||||
<path d="M16.010 6.49c-3.885 0-7.167 0.906-9.328 2.813-0.063-0.12-0.109-0.219-0.188-0.339-0.224-0.365-0.438-0.776-1.104-1.188-0.411-0.26-0.87-0.438-1.349-0.516-0.208-0.021-0.422-0.021-0.63 0l0.135-0.016c-1.214 0-1.922 0.724-2.385 1.354-0.458 0.625-0.755 1.328-0.948 2.099-0.38 1.542-0.385 3.536 1.083 5.026 0.766 0.781 1.667 1.151 2.484 1.37 0.156 0.042 0.297 0.052 0.448 0.083 0.531 2.521 2.104 4.656 4.208 5.839v0.005c1.24 0.693 2.417 1.010 3.297 1.349 1.234 0.479 2.536 1 4.052 1.135l0.078 0.005h0.198c1.745 0 3.063-0.703 4.203-1.141 0.875-0.333 2.052-0.641 3.302-1.344 0.578-0.323 1.115-0.719 1.594-1.172 1.318-1.234 2.229-2.839 2.625-4.599 1.115-0.182 2.141-0.719 2.922-1.536 1.464-1.484 1.458-3.479 1.078-5.021-0.193-0.771-0.49-1.474-0.948-2.099-0.458-0.63-1.172-1.354-2.385-1.354l0.135 0.016c-0.208-0.021-0.422-0.021-0.63 0-0.479 0.078-0.938 0.255-1.344 0.516-0.667 0.411-0.88 0.823-1.104 1.182-0.073 0.12-0.12 0.219-0.188 0.333-2.156-1.901-5.432-2.802-9.313-2.802zM16.042 8.313c4.745 0 8.016 1.422 9.411 3.964 0.839-0.323 1.453-2.521 2.146-2.948 0.563-0.344 0.885-0.26 0.885-0.26 1.271 0 2.578 3.729 0.953 5.38-0.859 0.875-2.443 1.12-3.229 1.057-0.063 2.542-1.542 4.833-3.5 5.932-1 0.563-2.068 0.854-3.063 1.234-1.229 0.469-2.38 1.016-3.547 1.016h-0.125c-1.161-0.099-2.318-0.542-3.547-1.016-0.995-0.38-2.068-0.682-3.063-1.24-1.948-1.099-3.427-3.391-3.49-5.927-0.781 0.068-2.385-0.177-3.245-1.057-1.625-1.651-0.318-5.38 0.948-5.38 0 0 0.328-0.083 0.885 0.26 0.698 0.427 1.318 2.646 2.161 2.953 1.391-2.547 4.667-3.969 9.417-3.969zM10.875 11.422c-2.276-0.042-4.146 1.792-4.146 4.068 0 2.281 1.87 4.115 4.146 4.073 5.328-0.099 5.328-8.047 0-8.141zM21.208 11.422c-5.427 0-5.427 8.141 0 8.141s5.427-8.141 0-8.141zM11.453 13.708c2.349 0.063 2.349 3.552 0 3.615-1.182 0-2.042-1.115-1.75-2.255 0.318 0.771 1.469 0.547 1.464-0.292 0-0.406-0.318-0.745-0.729-0.76 0.302-0.203 0.656-0.313 1.016-0.307zM20.641 13.708c2.344 0.063 2.344 3.552 0 3.615-1.182 0-2.047-1.115-1.755-2.255 0.229 0.552 0.979 0.641 1.328 0.146 0.344-0.49 0.010-1.167-0.589-1.193 0.297-0.208 0.651-0.313 1.016-0.313zM15.359 19.906c-0.318 0.026-0.5 0.193-0.5 0.635 0 0.281 0.182 0.484 0.5 0.484 0.229 0 0.266-0.323 0.047-0.375-0.031-0.005-0.172-0.057-0.172-0.182 0-0.12 0-0.167 0.24-0.198 0.104-0.016 0.156-0.141 0.125-0.24s-0.125-0.135-0.24-0.125zM16.724 19.906c-0.115-0.005-0.208 0.026-0.24 0.125s0.021 0.224 0.125 0.24c0.24 0.031 0.24 0.078 0.24 0.198 0 0.125-0.141 0.177-0.172 0.182-0.219 0.052-0.182 0.375 0.042 0.375 0.323 0 0.51-0.203 0.51-0.484 0-0.443-0.188-0.609-0.505-0.635z" fill="#424242"/>
|
||||
<line y2="24" x2="16" y1="26" x1="32" stroke-width="2" stroke="green" fill="none"/>
|
||||
<line y2="16" x2="24" y1="32" x1="24" stroke-width="1" stroke="green" fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
@@ -30,10 +30,12 @@
|
||||
"onCommand:codeQLDatabases.chooseDatabaseFolder",
|
||||
"onCommand:codeQLDatabases.chooseDatabaseArchive",
|
||||
"onCommand:codeQLDatabases.chooseDatabaseInternet",
|
||||
"onCommand:codeQLDatabases.chooseDatabaseLgtm",
|
||||
"onCommand:codeQL.setCurrentDatabase",
|
||||
"onCommand:codeQL.chooseDatabaseFolder",
|
||||
"onCommand:codeQL.chooseDatabaseArchive",
|
||||
"onCommand:codeQL.chooseDatabaseInternet",
|
||||
"onCommand:codeQL.chooseDatabaseLgtm",
|
||||
"onCommand:codeQLDatabases.chooseDatabase",
|
||||
"onCommand:codeQLDatabases.setCurrentDatabase",
|
||||
"onCommand:codeQL.quickQuery",
|
||||
@@ -200,6 +202,14 @@
|
||||
"dark": "media/dark/cloud-download.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "codeQLDatabases.chooseDatabaseLgtm",
|
||||
"title": "Download from LGTM",
|
||||
"icon": {
|
||||
"light": "media/light/lgtm-plus.svg",
|
||||
"dark": "media/dark/lgtm-plus.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "codeQL.setCurrentDatabase",
|
||||
"title": "CodeQL: Set Current Database"
|
||||
@@ -244,6 +254,10 @@
|
||||
"command": "codeQL.chooseDatabaseInternet",
|
||||
"title": "CodeQL: Download Database"
|
||||
},
|
||||
{
|
||||
"command": "codeQL.chooseDatabaseLgtm",
|
||||
"title": "CodeQL: Download Database from LGTM"
|
||||
},
|
||||
{
|
||||
"command": "codeQLDatabases.sortByName",
|
||||
"title": "Sort by Name",
|
||||
@@ -339,6 +353,11 @@
|
||||
"command": "codeQLDatabases.chooseDatabaseInternet",
|
||||
"when": "view == codeQLDatabases",
|
||||
"group": "navigation"
|
||||
},
|
||||
{
|
||||
"command": "codeQLDatabases.chooseDatabaseLgtm",
|
||||
"when": "view == codeQLDatabases",
|
||||
"group": "navigation"
|
||||
}
|
||||
],
|
||||
"view/item/context": [
|
||||
@@ -469,6 +488,10 @@
|
||||
"command": "codeQLDatabases.chooseDatabaseInternet",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "codeQLDatabases.chooseDatabaseLgtm",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "codeQLDatabases.upgradeDatabase",
|
||||
"when": "false"
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
import * as fetch from "node-fetch";
|
||||
import fetch from "node-fetch";
|
||||
import * as unzipper from "unzipper";
|
||||
import { Uri, ProgressOptions, ProgressLocation, commands, window } from "vscode";
|
||||
import {
|
||||
Uri,
|
||||
ProgressOptions,
|
||||
ProgressLocation,
|
||||
commands,
|
||||
window,
|
||||
} from "vscode";
|
||||
import * as fs from "fs-extra";
|
||||
import * as path from "path";
|
||||
import { DatabaseManager, DatabaseItem } from "./databases";
|
||||
import { ProgressCallback, showAndLogErrorMessage, withProgress, showAndLogInformationMessage } from "./helpers";
|
||||
import {
|
||||
ProgressCallback,
|
||||
showAndLogErrorMessage,
|
||||
withProgress,
|
||||
showAndLogInformationMessage,
|
||||
} from "./helpers";
|
||||
|
||||
/**
|
||||
* Prompts a user to fetch a database from a remote location. Database is assumed to be an archive file.
|
||||
@@ -12,25 +23,39 @@ import { ProgressCallback, showAndLogErrorMessage, withProgress, showAndLogInfor
|
||||
* @param databasesManager the DatabaseManager
|
||||
* @param storagePath where to store the unzipped database.
|
||||
*/
|
||||
export async function promptImportInternetDatabase(databasesManager: DatabaseManager, storagePath: string): Promise<DatabaseItem | undefined> {
|
||||
export async function promptImportInternetDatabase(
|
||||
databasesManager: DatabaseManager,
|
||||
storagePath: string
|
||||
): Promise<DatabaseItem | undefined> {
|
||||
let item: DatabaseItem | undefined = undefined;
|
||||
|
||||
try {
|
||||
const databaseUrl = await window.showInputBox({
|
||||
prompt: 'Enter URL of zipfile of database to download'
|
||||
prompt: "Enter URL of zipfile of database to download",
|
||||
});
|
||||
if (databaseUrl) {
|
||||
validateHttpsUrl(databaseUrl);
|
||||
|
||||
const progressOptions: ProgressOptions = {
|
||||
location: ProgressLocation.Notification,
|
||||
title: 'Adding database from URL',
|
||||
title: "Adding database from URL",
|
||||
cancellable: false,
|
||||
};
|
||||
await withProgress(progressOptions, async progress => (item = await databaseArchiveFetcher(databaseUrl, databasesManager, storagePath, progress)));
|
||||
commands.executeCommand('codeQLDatabases.focus');
|
||||
await withProgress(
|
||||
progressOptions,
|
||||
async (progress) =>
|
||||
(item = await databaseArchiveFetcher(
|
||||
databaseUrl,
|
||||
databasesManager,
|
||||
storagePath,
|
||||
progress
|
||||
))
|
||||
);
|
||||
commands.executeCommand("codeQLDatabases.focus");
|
||||
}
|
||||
showAndLogInformationMessage('Database downloaded and imported successfully.');
|
||||
showAndLogInformationMessage(
|
||||
"Database downloaded and imported successfully."
|
||||
);
|
||||
} catch (e) {
|
||||
showAndLogErrorMessage(e.message);
|
||||
}
|
||||
@@ -38,6 +63,56 @@ export async function promptImportInternetDatabase(databasesManager: DatabaseMan
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompts a user to fetch a database from lgtm.
|
||||
* User enters a project url and then the user is asked which language
|
||||
* to download (if there is more than one)
|
||||
*
|
||||
* @param databasesManager the DatabaseManager
|
||||
* @param storagePath where to store the unzipped database.
|
||||
*/
|
||||
export async function promptImportLgtmDatabase(
|
||||
databasesManager: DatabaseManager,
|
||||
storagePath: string
|
||||
): Promise<DatabaseItem | undefined> {
|
||||
let item: DatabaseItem | undefined = undefined;
|
||||
|
||||
try {
|
||||
const lgtmUrl = await window.showInputBox({
|
||||
prompt:
|
||||
"Enter the project URL on LGTM (e.g., https://lgtm.com/projects/g/github/codeql)",
|
||||
});
|
||||
if (looksLikeLgtmUrl(lgtmUrl)) {
|
||||
const databaseUrl = await convertToDatabaseUrl(lgtmUrl);
|
||||
if (!databaseUrl) {
|
||||
return item;
|
||||
}
|
||||
const progressOptions: ProgressOptions = {
|
||||
location: ProgressLocation.Notification,
|
||||
title: "Adding database from LGTM",
|
||||
cancellable: false,
|
||||
};
|
||||
await withProgress(
|
||||
progressOptions,
|
||||
async (progress) =>
|
||||
(item = await databaseArchiveFetcher(
|
||||
databaseUrl,
|
||||
databasesManager,
|
||||
storagePath,
|
||||
progress
|
||||
))
|
||||
);
|
||||
commands.executeCommand("codeQLDatabases.focus");
|
||||
}
|
||||
showAndLogInformationMessage(
|
||||
"Database downloaded and imported successfully."
|
||||
);
|
||||
} catch (e) {
|
||||
showAndLogErrorMessage(e.message);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports a database from a local archive.
|
||||
@@ -46,25 +121,39 @@ export async function promptImportInternetDatabase(databasesManager: DatabaseMan
|
||||
* @param databasesManager the DatabaseManager
|
||||
* @param storagePath where to store the unzipped database.
|
||||
*/
|
||||
export async function importArchiveDatabase(databaseUrl: string, databasesManager: DatabaseManager, storagePath: string): Promise<DatabaseItem | undefined> {
|
||||
export async function importArchiveDatabase(
|
||||
databaseUrl: string,
|
||||
databasesManager: DatabaseManager,
|
||||
storagePath: string
|
||||
): Promise<DatabaseItem | undefined> {
|
||||
let item: DatabaseItem | undefined = undefined;
|
||||
try {
|
||||
const progressOptions: ProgressOptions = {
|
||||
location: ProgressLocation.Notification,
|
||||
title: 'Importing database from archive',
|
||||
title: "Importing database from archive",
|
||||
cancellable: false,
|
||||
};
|
||||
await withProgress(progressOptions, async progress => (item = await databaseArchiveFetcher(databaseUrl, databasesManager, storagePath, progress)));
|
||||
commands.executeCommand('codeQLDatabases.focus');
|
||||
await withProgress(
|
||||
progressOptions,
|
||||
async (progress) =>
|
||||
(item = await databaseArchiveFetcher(
|
||||
databaseUrl,
|
||||
databasesManager,
|
||||
storagePath,
|
||||
progress
|
||||
))
|
||||
);
|
||||
commands.executeCommand("codeQLDatabases.focus");
|
||||
|
||||
showAndLogInformationMessage('Database unzipped and imported successfully.');
|
||||
showAndLogInformationMessage(
|
||||
"Database unzipped and imported successfully."
|
||||
);
|
||||
} catch (e) {
|
||||
showAndLogErrorMessage(e.message);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetches an archive database. The database might be on the internet
|
||||
* or in the local filesystem.
|
||||
@@ -82,8 +171,8 @@ async function databaseArchiveFetcher(
|
||||
): Promise<DatabaseItem> {
|
||||
progressCallback?.({
|
||||
maxStep: 3,
|
||||
message: 'Getting database',
|
||||
step: 1
|
||||
message: "Getting database",
|
||||
step: 1,
|
||||
});
|
||||
if (!storagePath) {
|
||||
throw new Error("No storage path specified.");
|
||||
@@ -99,18 +188,22 @@ async function databaseArchiveFetcher(
|
||||
|
||||
progressCallback?.({
|
||||
maxStep: 3,
|
||||
message: 'Opening database',
|
||||
step: 3
|
||||
message: "Opening database",
|
||||
step: 3,
|
||||
});
|
||||
|
||||
// find the path to the database. The actual database might be in a sub-folder
|
||||
const dbPath = await findDirWithFile(unzipPath, '.dbinfo', 'codeql-database.yml');
|
||||
const dbPath = await findDirWithFile(
|
||||
unzipPath,
|
||||
".dbinfo",
|
||||
"codeql-database.yml"
|
||||
);
|
||||
if (dbPath) {
|
||||
const item = await databasesManager.openDatabase(Uri.file(dbPath));
|
||||
databasesManager.setCurrentDatabaseItem(item);
|
||||
return item;
|
||||
} else {
|
||||
throw new Error('Database not found in archive.');
|
||||
throw new Error("Database not found in archive.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +234,6 @@ async function getStorageFolder(storagePath: string, urlStr: string) {
|
||||
return folderName;
|
||||
}
|
||||
|
||||
|
||||
function validateHttpsUrl(databaseUrl: string) {
|
||||
let uri;
|
||||
try {
|
||||
@@ -150,47 +242,51 @@ function validateHttpsUrl(databaseUrl: string) {
|
||||
throw new Error(`Invalid url: ${databaseUrl}`);
|
||||
}
|
||||
|
||||
if (uri.scheme !== 'https') {
|
||||
throw new Error('Must use https for downloading a database.');
|
||||
if (uri.scheme !== "https") {
|
||||
throw new Error("Must use https for downloading a database.");
|
||||
}
|
||||
}
|
||||
|
||||
async function readAndUnzip(databaseUrl: string, unzipPath: string) {
|
||||
const unzipStream = unzipper.Extract({
|
||||
path: unzipPath
|
||||
path: unzipPath,
|
||||
});
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
// we already know this is a file scheme
|
||||
const databaseFile = Uri.parse(databaseUrl).fsPath;
|
||||
const stream = fs.createReadStream(databaseFile);
|
||||
stream.on('error', reject);
|
||||
unzipStream.on('error', reject);
|
||||
unzipStream.on('close', resolve);
|
||||
stream.on("error", reject);
|
||||
unzipStream.on("error", reject);
|
||||
unzipStream.on("close", resolve);
|
||||
stream.pipe(unzipStream);
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchAndUnzip(databaseUrl: string, unzipPath: string, progressCallback?: ProgressCallback) {
|
||||
const response = await fetch.default(databaseUrl);
|
||||
async function fetchAndUnzip(
|
||||
databaseUrl: string,
|
||||
unzipPath: string,
|
||||
progressCallback?: ProgressCallback
|
||||
) {
|
||||
const response = await fetch(databaseUrl);
|
||||
const unzipStream = unzipper.Extract({
|
||||
path: unzipPath
|
||||
path: unzipPath,
|
||||
});
|
||||
progressCallback?.({
|
||||
maxStep: 3,
|
||||
message: 'Unzipping database',
|
||||
step: 2
|
||||
message: "Unzipping database",
|
||||
step: 2,
|
||||
});
|
||||
await new Promise((resolve, reject) => {
|
||||
response.body.on('error', reject);
|
||||
unzipStream.on('error', reject);
|
||||
unzipStream.on('close', resolve);
|
||||
response.body.on("error", reject);
|
||||
unzipStream.on("error", reject);
|
||||
unzipStream.on("close", resolve);
|
||||
response.body.pipe(unzipStream);
|
||||
});
|
||||
}
|
||||
|
||||
function isFile(databaseUrl: string) {
|
||||
return Uri.parse(databaseUrl).scheme === 'file';
|
||||
return Uri.parse(databaseUrl).scheme === "file";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -201,12 +297,15 @@ function isFile(databaseUrl: string) {
|
||||
*
|
||||
* @returns the directory containing the file, or undefined if not found.
|
||||
*/
|
||||
async function findDirWithFile(dir: string, ...toFind: string[]): Promise<string | undefined> {
|
||||
async function findDirWithFile(
|
||||
dir: string,
|
||||
...toFind: string[]
|
||||
): Promise<string | undefined> {
|
||||
if (!(await fs.stat(dir)).isDirectory()) {
|
||||
return;
|
||||
}
|
||||
const files = await fs.readdir(dir);
|
||||
if (toFind.some(file => files.includes(file))) {
|
||||
if (toFind.some((file) => files.includes(file))) {
|
||||
return dir;
|
||||
}
|
||||
for (const file of files) {
|
||||
@@ -218,3 +317,60 @@ async function findDirWithFile(dir: string, ...toFind: string[]): Promise<string
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
function looksLikeLgtmUrl(lgtmUrl: string | undefined): lgtmUrl is string {
|
||||
if (!lgtmUrl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uri = Uri.parse(lgtmUrl, true);
|
||||
if (uri.scheme !== "https") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (uri.authority !== "lgtm.com" && uri.authority !== "www.lgtm.com") {
|
||||
return false;
|
||||
}
|
||||
|
||||
const paths = uri.path.split("/").filter((segment) => segment);
|
||||
return paths.length === 4 && paths[0] === "projects" && paths[1] === "g";
|
||||
}
|
||||
|
||||
async function convertToDatabaseUrl(lgtmUrl: string) {
|
||||
const uri = Uri.parse(lgtmUrl, true);
|
||||
const paths = ["api", "v1.0"].concat(
|
||||
uri.path.split("/").filter((segment) => segment)
|
||||
);
|
||||
const projectUrl = `https://lgtm.com/${paths.join("/")}`;
|
||||
const projectResponse = await fetch(projectUrl);
|
||||
const projectJson = await projectResponse.json();
|
||||
|
||||
const language = await promptForLanguage(projectJson);
|
||||
if (!language) {
|
||||
return;
|
||||
}
|
||||
return `https://lgtm.com/${[
|
||||
"api",
|
||||
"v1.0",
|
||||
"snapshots",
|
||||
projectJson.id,
|
||||
language,
|
||||
].join("/")}`;
|
||||
}
|
||||
|
||||
async function promptForLanguage(
|
||||
projectJson: any
|
||||
): Promise<string | undefined> {
|
||||
if (!projectJson?.languages?.length) {
|
||||
return;
|
||||
}
|
||||
if (projectJson.languages.length === 1) {
|
||||
return projectJson.languages[0].language;
|
||||
}
|
||||
|
||||
return await window.showQuickPick(
|
||||
projectJson.languages.map((lang: { language: string }) => lang.language), {
|
||||
placeHolder: "Select the database language to download:"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { logger } from './logging';
|
||||
import { clearCacheInDatabase, UserCancellationException } from './run-queries';
|
||||
import * as qsClient from './queryserver-client';
|
||||
import { upgradeDatabase } from './upgrades';
|
||||
import { importArchiveDatabase, promptImportInternetDatabase } from './databaseFetcher';
|
||||
import { importArchiveDatabase, promptImportInternetDatabase, promptImportLgtmDatabase } from './databaseFetcher';
|
||||
import * as fs from 'fs-extra';
|
||||
|
||||
type ThemableIconPath = { light: string; dark: string } | string;
|
||||
@@ -179,6 +179,7 @@ export class DatabaseUI extends DisposableObject {
|
||||
ctx.subscriptions.push(commands.registerCommand('codeQLDatabases.chooseDatabaseFolder', this.handleChooseDatabaseFolder));
|
||||
ctx.subscriptions.push(commands.registerCommand('codeQLDatabases.chooseDatabaseArchive', this.handleChooseDatabaseArchive));
|
||||
ctx.subscriptions.push(commands.registerCommand('codeQLDatabases.chooseDatabaseInternet', this.handleChooseDatabaseInternet));
|
||||
ctx.subscriptions.push(commands.registerCommand('codeQLDatabases.chooseDatabaseLgtm', this.handleChooseDatabaseLgtm));
|
||||
ctx.subscriptions.push(commands.registerCommand('codeQL.setCurrentDatabase', this.handleSetCurrentDatabase));
|
||||
ctx.subscriptions.push(commands.registerCommand('codeQL.upgradeCurrentDatabase', this.handleUpgradeCurrentDatabase));
|
||||
ctx.subscriptions.push(commands.registerCommand('codeQL.clearCache', this.handleClearCache));
|
||||
@@ -217,6 +218,10 @@ export class DatabaseUI extends DisposableObject {
|
||||
return await promptImportInternetDatabase(this.databaseManager, this.storagePath);
|
||||
}
|
||||
|
||||
handleChooseDatabaseLgtm = async (): Promise<DatabaseItem | undefined> => {
|
||||
return await promptImportLgtmDatabase(this.databaseManager, this.storagePath);
|
||||
}
|
||||
|
||||
private handleSortByName = async () => {
|
||||
if (this.treeDataProvider.sortOrder === SortOrder.NameAsc) {
|
||||
this.treeDataProvider.sortOrder = SortOrder.NameDesc;
|
||||
|
||||
@@ -349,6 +349,7 @@ async function activateWithInstalledDistribution(ctx: ExtensionContext, distribu
|
||||
}));
|
||||
ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabaseFolder', () => databaseUI.handleChooseDatabaseFolder()));
|
||||
ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabaseArchive', () => databaseUI.handleChooseDatabaseArchive()));
|
||||
ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabaseLgtm', () => databaseUI.handleChooseDatabaseLgtm()));
|
||||
ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabaseInternet', () => databaseUI.handleChooseDatabaseInternet()));
|
||||
|
||||
logger.log('Starting language server.');
|
||||
|
||||
Reference in New Issue
Block a user