Skip to content
Merged
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
1 change: 1 addition & 0 deletions extensions/ql-vscode/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module.exports = {
projects: [
"<rootDir>/src/view",
"<rootDir>/test",
"<rootDir>/out/vscode-tests/cli-integration",
"<rootDir>/out/vscode-tests/no-workspace",
"<rootDir>/out/vscode-tests/minimal-workspace",
],
Expand Down
2 changes: 1 addition & 1 deletion extensions/ql-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1275,7 +1275,7 @@
"integration": "npm-run-all integration:*",
"integration:no-workspace": "jest --projects out/vscode-tests/no-workspace",
"integration:minimal-workspace": "jest --projects out/vscode-tests/minimal-workspace",
"cli-integration": "node ./out/vscode-tests/run-integration-tests.js cli-integration",
"cli-integration": "jest --projects out/vscode-tests/cli-integration",
"update-vscode": "node ./node_modules/vscode/bin/install",
"format": "prettier --write **/*.{ts,tsx} && eslint . --ext .ts,.tsx --fix",
"lint": "eslint . --ext .ts,.tsx --max-warnings=0",
Expand Down
60 changes: 8 additions & 52 deletions extensions/ql-vscode/src/distribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import * as fs from "fs-extra";
import * as os from "os";
import * as path from "path";
import * as semver from "semver";
import * as unzipper from "unzipper";
import * as url from "url";
import { ExtensionContext, Event } from "vscode";
import { DistributionConfig } from "./config";
Expand All @@ -16,6 +15,12 @@ import {
import { logger } from "./logging";
import { getCodeQlCliVersion } from "./cli-version";
import { ProgressCallback, reportStreamProgress } from "./commandRunner";
import {
codeQlLauncherName,
deprecatedCodeQlLauncherName,
extractZipArchive,
getRequiredAssetName,
} from "./pure/distribution";

/**
* distribution.ts
Expand Down Expand Up @@ -55,22 +60,6 @@ export interface DistributionProvider {
}

export class DistributionManager implements DistributionProvider {
/**
* Get the name of the codeql cli installation we prefer to install, based on our current platform.
*/
public static getRequiredAssetName(): string {
switch (os.platform()) {
case "linux":
return "codeql-linux64.zip";
case "darwin":
return "codeql-osx64.zip";
case "win32":
return "codeql-win64.zip";
default:
return "codeql.zip";
}
}

constructor(
public readonly config: DistributionConfig,
private readonly versionRange: semver.Range,
Expand Down Expand Up @@ -361,7 +350,7 @@ class ExtensionSpecificDistributionManager {
}

// Filter assets to the unique one that we require.
const requiredAssetName = DistributionManager.getRequiredAssetName();
const requiredAssetName = getRequiredAssetName();
const assets = release.assets.filter(
(asset) => asset.name === requiredAssetName,
);
Expand Down Expand Up @@ -431,7 +420,7 @@ class ExtensionSpecificDistributionManager {
}

private async getLatestRelease(): Promise<Release> {
const requiredAssetName = DistributionManager.getRequiredAssetName();
const requiredAssetName = getRequiredAssetName();
void logger.log(
`Searching for latest release including ${requiredAssetName}.`,
);
Expand Down Expand Up @@ -683,39 +672,6 @@ export class ReleasesApiConsumer {
private static readonly _maxRedirects = 20;
}

export async function extractZipArchive(
archivePath: string,
outPath: string,
): Promise<void> {
const archive = await unzipper.Open.file(archivePath);
await archive.extract({
concurrency: 4,
path: outPath,
});
// Set file permissions for extracted files
await Promise.all(
archive.files.map(async (file) => {
// Only change file permissions if within outPath (path.join normalises the path)
const extractedPath = path.join(outPath, file.path);
if (
extractedPath.indexOf(outPath) !== 0 ||
!(await fs.pathExists(extractedPath))
) {
return Promise.resolve();
}
return fs.chmod(extractedPath, file.externalFileAttributes >>> 16);
}),
);
}

export function codeQlLauncherName(): string {
return os.platform() === "win32" ? "codeql.exe" : "codeql";
}

function deprecatedCodeQlLauncherName(): string | undefined {
return os.platform() === "win32" ? "codeql.cmd" : undefined;
}

function isRedirectStatusCode(statusCode: number): boolean {
return (
statusCode === 301 ||
Expand Down
53 changes: 53 additions & 0 deletions extensions/ql-vscode/src/pure/distribution.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import * as os from "os";
import * as unzipper from "unzipper";
import * as path from "path";
import * as fs from "fs-extra";

/**
* Get the name of the codeql cli installation we prefer to install, based on our current platform.
*/
export function getRequiredAssetName(): string {
switch (os.platform()) {
case "linux":
return "codeql-linux64.zip";
case "darwin":
return "codeql-osx64.zip";
case "win32":
return "codeql-win64.zip";
default:
return "codeql.zip";
}
}

export async function extractZipArchive(
archivePath: string,
outPath: string,
): Promise<void> {
const archive = await unzipper.Open.file(archivePath);
await archive.extract({
concurrency: 4,
path: outPath,
});
// Set file permissions for extracted files
await Promise.all(
archive.files.map(async (file) => {
// Only change file permissions if within outPath (path.join normalises the path)
const extractedPath = path.join(outPath, file.path);
if (
extractedPath.indexOf(outPath) !== 0 ||
!(await fs.pathExists(extractedPath))
) {
return Promise.resolve();
}
return fs.chmod(extractedPath, file.externalFileAttributes >>> 16);
}),
);
}

export function codeQlLauncherName(): string {
return os.platform() === "win32" ? "codeql.exe" : "codeql";
}

export function deprecatedCodeQlLauncherName(): string | undefined {
return os.platform() === "win32" ? "codeql.cmd" : undefined;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import * as sinon from "sinon";
import * as path from "path";
import { fail } from "assert";
import { expect } from "chai";
import { extensions, CancellationToken, Uri, window } from "vscode";

import { CodeQLExtensionInterface } from "../../extension";
Expand All @@ -12,64 +9,51 @@ import {
importArchiveDatabase,
promptImportInternetDatabase,
} from "../../databaseFetcher";
import { ProgressCallback } from "../../commandRunner";
import { cleanDatabases, dbLoc, DB_URL, storagePath } from "./global.helper";

jest.setTimeout(60_000);

/**
* Run various integration tests for databases
*/
describe("Databases", function () {
this.timeout(60000);

describe("Databases", () => {
const LGTM_URL =
"https://lgtm.com/projects/g/aeisenberg/angular-bind-notifier/";

let databaseManager: DatabaseManager;
let sandbox: sinon.SinonSandbox;
let inputBoxStub: sinon.SinonStub;
let inputBoxStub: jest.SpiedFunction<typeof window.showInputBox>;
let cli: CodeQLCliServer;
let progressCallback: ProgressCallback;
const progressCallback = jest.fn();

beforeEach(async () => {
try {
sandbox = sinon.createSandbox();
// the uri.fsPath function on windows returns a lowercase drive letter
// so, force the storage path string to be lowercase, too.
progressCallback = sandbox.spy();
inputBoxStub = sandbox.stub(window, "showInputBox");
sandbox.stub(window, "showErrorMessage");
sandbox.stub(window, "showInformationMessage");
inputBoxStub = jest
.spyOn(window, "showInputBox")
.mockResolvedValue(undefined);

const extension = await extensions
.getExtension<CodeQLExtensionInterface | Record<string, never>>(
"GitHub.vscode-codeql",
)!
.activate();
if ("databaseManager" in extension) {
databaseManager = extension.databaseManager;
} else {
throw new Error(
"Extension not initialized. Make sure cli is downloaded and installed properly.",
);
}
jest.spyOn(window, "showErrorMessage").mockResolvedValue(undefined);
jest.spyOn(window, "showInformationMessage").mockResolvedValue(undefined);

await cleanDatabases(databaseManager);
} catch (e) {
fail(e as Error);
const extension = await extensions
.getExtension<CodeQLExtensionInterface | Record<string, never>>(
"GitHub.vscode-codeql",
)!
.activate();
if ("databaseManager" in extension) {
databaseManager = extension.databaseManager;
} else {
throw new Error(
"Extension not initialized. Make sure cli is downloaded and installed properly.",
);
}

await cleanDatabases(databaseManager);
});

afterEach(async () => {
try {
sandbox.restore();
await cleanDatabases(databaseManager);
} catch (e) {
fail(e as Error);
}
await cleanDatabases(databaseManager);
});

it("should add a database from a folder", async () => {
const progressCallback = sandbox.spy() as ProgressCallback;
const uri = Uri.file(dbLoc);
let dbItem = await importArchiveDatabase(
uri.toString(true),
Expand All @@ -79,27 +63,27 @@ describe("Databases", function () {
{} as CancellationToken,
cli,
);
expect(dbItem).to.be.eq(databaseManager.currentDatabaseItem);
expect(dbItem).to.be.eq(databaseManager.databaseItems[0]);
expect(dbItem).not.to.be.undefined;
expect(dbItem).toBe(databaseManager.currentDatabaseItem);
expect(dbItem).toBe(databaseManager.databaseItems[0]);
expect(dbItem).toBeDefined();
dbItem = dbItem!;
expect(dbItem.name).to.eq("db");
expect(dbItem.databaseUri.fsPath).to.eq(path.join(storagePath, "db", "db"));
expect(dbItem.name).toBe("db");
expect(dbItem.databaseUri.fsPath).toBe(path.join(storagePath, "db", "db"));
});

it("should add a database from lgtm with only one language", async () => {
inputBoxStub.resolves(LGTM_URL);
inputBoxStub.mockResolvedValue(LGTM_URL);
let dbItem = await promptImportLgtmDatabase(
databaseManager,
storagePath,
progressCallback,
{} as CancellationToken,
cli,
);
expect(dbItem).not.to.be.undefined;
expect(dbItem).toBeDefined();
dbItem = dbItem!;
expect(dbItem.name).to.eq("aeisenberg_angular-bind-notifier_106179a");
expect(dbItem.databaseUri.fsPath).to.eq(
expect(dbItem.name).toBe("aeisenberg_angular-bind-notifier_106179a");
expect(dbItem.databaseUri.fsPath).toBe(
path.join(
storagePath,
"javascript",
Expand All @@ -109,7 +93,7 @@ describe("Databases", function () {
});

it("should add a database from a url", async () => {
inputBoxStub.resolves(DB_URL);
inputBoxStub.mockResolvedValue(DB_URL);

let dbItem = await promptImportInternetDatabase(
databaseManager,
Expand All @@ -118,10 +102,10 @@ describe("Databases", function () {
{} as CancellationToken,
cli,
);
expect(dbItem).not.to.be.undefined;
expect(dbItem).toBeDefined();
dbItem = dbItem!;
expect(dbItem.name).to.eq("db");
expect(dbItem.databaseUri.fsPath).to.eq(
expect(dbItem.name).toBe("db");
expect(dbItem.databaseUri.fsPath).toBe(
path.join(storagePath, "simple-db", "db"),
);
});
Expand Down
Loading