Prompt non-codespace users for storage path
Offer non-codespace users the option to configure their storage folder for skeleton packs. Suggested here: https://github.com/github/vscode-codeql/pull/2310#issuecomment-1507428217 At the moment we're choosing to create our skeleton packs in the first folder in the workspace. This is fine for the codespace template because we can control the folder structure in that repo. For users outside of this we'd like to offer them the option to choose where to save their skeleton packs.
This commit is contained in:
@@ -619,3 +619,19 @@ export const ALLOW_HTTP_SETTING = new Setting(
|
||||
export function allowHttp(): boolean {
|
||||
return ALLOW_HTTP_SETTING.getValue<boolean>() || false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the folder where we want to create skeleton wizard QL packs.
|
||||
**/
|
||||
const SKELETON_WIZARD_FOLDER = new Setting(
|
||||
"folder",
|
||||
new Setting("skeletonWizard", ROOT_SETTING),
|
||||
);
|
||||
|
||||
export function getSkeletonWizardFolder(): string | undefined {
|
||||
return SKELETON_WIZARD_FOLDER.getValue<string>() || undefined;
|
||||
}
|
||||
|
||||
export async function setSkeletonWizardFolder(folder: string | undefined) {
|
||||
await SKELETON_WIZARD_FOLDER.updateValue(folder, ConfigurationTarget.Global);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,12 @@ import { QlPackGenerator } from "./qlpack-generator";
|
||||
import { DatabaseItem, DatabaseManager } from "./local-databases";
|
||||
import { ProgressCallback, UserCancellationException } from "./progress";
|
||||
import { askForGitHubRepo, downloadGitHubDatabase } from "./databaseFetcher";
|
||||
import { existsSync } from "fs";
|
||||
import {
|
||||
getSkeletonWizardFolder,
|
||||
isCodespacesTemplate,
|
||||
setSkeletonWizardFolder,
|
||||
} from "./config";
|
||||
import { existsSync } from "fs-extra";
|
||||
|
||||
type QueryLanguagesToDatabaseMap = Record<string, string>;
|
||||
|
||||
@@ -55,7 +60,7 @@ export class SkeletonQueryWizard {
|
||||
return;
|
||||
}
|
||||
|
||||
this.qlPackStoragePath = getFirstWorkspaceFolder();
|
||||
this.qlPackStoragePath = await this.determineStoragePath();
|
||||
|
||||
const skeletonPackAlreadyExists =
|
||||
existsSync(join(this.qlPackStoragePath, this.folderName)) ||
|
||||
@@ -98,6 +103,38 @@ export class SkeletonQueryWizard {
|
||||
});
|
||||
}
|
||||
|
||||
public async determineStoragePath() {
|
||||
const firstStorageFolder = getFirstWorkspaceFolder();
|
||||
|
||||
if (isCodespacesTemplate()) {
|
||||
return firstStorageFolder;
|
||||
}
|
||||
|
||||
let storageFolder = getSkeletonWizardFolder();
|
||||
|
||||
if (storageFolder === undefined || !existsSync(storageFolder)) {
|
||||
storageFolder = await Window.showInputBox({
|
||||
title:
|
||||
"Please choose a folder in which to create your new query pack. You can change this in the extension settings.",
|
||||
value: firstStorageFolder,
|
||||
ignoreFocusOut: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (storageFolder === undefined) {
|
||||
throw new UserCancellationException("No storage folder entered.");
|
||||
}
|
||||
|
||||
if (!existsSync(storageFolder)) {
|
||||
throw new UserCancellationException(
|
||||
"Invalid folder. Must be a folder that already exists.",
|
||||
);
|
||||
}
|
||||
|
||||
await setSkeletonWizardFolder(storageFolder);
|
||||
return storageFolder;
|
||||
}
|
||||
|
||||
private async chooseLanguage() {
|
||||
this.progress({
|
||||
message: "Choose language",
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
import * as databaseFetcher from "../../../src/databaseFetcher";
|
||||
import { createMockDB } from "../../factories/databases/databases";
|
||||
import { asError } from "../../../src/pure/helpers-pure";
|
||||
import { Setting } from "../../../src/config";
|
||||
|
||||
describe("SkeletonQueryWizard", () => {
|
||||
let mockCli: CodeQLCliServer;
|
||||
@@ -29,6 +30,7 @@ describe("SkeletonQueryWizard", () => {
|
||||
let dir: tmp.DirResult;
|
||||
let storagePath: string;
|
||||
let quickPickSpy: jest.SpiedFunction<typeof window.showQuickPick>;
|
||||
let showInputBoxSpy: jest.SpiedFunction<typeof window.showInputBox>;
|
||||
let generateSpy: jest.SpiedFunction<
|
||||
typeof QlPackGenerator.prototype.generate
|
||||
>;
|
||||
@@ -93,6 +95,9 @@ describe("SkeletonQueryWizard", () => {
|
||||
quickPickSpy = jest
|
||||
.spyOn(window, "showQuickPick")
|
||||
.mockResolvedValueOnce(mockedQuickPickItem(chosenLanguage));
|
||||
showInputBoxSpy = jest
|
||||
.spyOn(window, "showInputBox")
|
||||
.mockResolvedValue(storagePath);
|
||||
generateSpy = jest
|
||||
.spyOn(QlPackGenerator.prototype, "generate")
|
||||
.mockResolvedValue(undefined);
|
||||
@@ -433,4 +438,116 @@ describe("SkeletonQueryWizard", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("determineStoragePath", () => {
|
||||
it("should prompt the user to provide a storage path", async () => {
|
||||
const chosenPath = await wizard.determineStoragePath();
|
||||
|
||||
expect(showInputBoxSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ value: storagePath }),
|
||||
);
|
||||
expect(chosenPath).toEqual(storagePath);
|
||||
});
|
||||
|
||||
it("should write the chosen folder to settings", async () => {
|
||||
const updateValueSpy = jest.spyOn(Setting.prototype, "updateValue");
|
||||
|
||||
await wizard.determineStoragePath();
|
||||
|
||||
expect(updateValueSpy).toHaveBeenCalledWith(storagePath, 1);
|
||||
});
|
||||
|
||||
describe("when the user is using the codespace template", () => {
|
||||
let originalValue: any;
|
||||
let storedPath: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
storedPath = join(dir.name, "pickles-folder");
|
||||
ensureDirSync(storedPath);
|
||||
|
||||
originalValue = workspace
|
||||
.getConfiguration("codeQL.skeletonWizard")
|
||||
.get("folder");
|
||||
|
||||
// Set isCodespacesTemplate to true to indicate we are in the codespace template
|
||||
await workspace
|
||||
.getConfiguration("codeQL")
|
||||
.update("codespacesTemplate", true);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await workspace
|
||||
.getConfiguration("codeQL")
|
||||
.update("codespacesTemplate", originalValue);
|
||||
});
|
||||
|
||||
it("should not prompt the user", async () => {
|
||||
const chosenPath = await wizard.determineStoragePath();
|
||||
|
||||
expect(showInputBoxSpy).not.toHaveBeenCalled();
|
||||
expect(chosenPath).toEqual(storagePath);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when there is already a saved storage path in settings", () => {
|
||||
describe("when the saved storage path exists", () => {
|
||||
let originalValue: any;
|
||||
let storedPath: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
storedPath = join(dir.name, "pickles-folder");
|
||||
ensureDirSync(storedPath);
|
||||
|
||||
originalValue = workspace
|
||||
.getConfiguration("codeQL.skeletonWizard")
|
||||
.get("folder");
|
||||
await workspace
|
||||
.getConfiguration("codeQL.skeletonWizard")
|
||||
.update("folder", storedPath);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await workspace
|
||||
.getConfiguration("codeQL.skeletonWizard")
|
||||
.update("folder", originalValue);
|
||||
});
|
||||
|
||||
it("should return it and not prompt the user", async () => {
|
||||
const chosenPath = await wizard.determineStoragePath();
|
||||
|
||||
expect(showInputBoxSpy).not.toHaveBeenCalled();
|
||||
expect(chosenPath).toEqual(storedPath);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the saved storage path does not exist", () => {
|
||||
let originalValue: any;
|
||||
let storedPath: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
storedPath = join(dir.name, "this-folder-does-not-exist");
|
||||
|
||||
originalValue = workspace
|
||||
.getConfiguration("codeQL.skeletonWizard")
|
||||
.get("folder");
|
||||
await workspace
|
||||
.getConfiguration("codeQL.skeletonWizard")
|
||||
.update("folder", storedPath);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await workspace
|
||||
.getConfiguration("codeQL.skeletonWizard")
|
||||
.update("folder", originalValue);
|
||||
});
|
||||
|
||||
it("should prompt the user for to provide a new folder name", async () => {
|
||||
const chosenPath = await wizard.determineStoragePath();
|
||||
|
||||
expect(showInputBoxSpy).toHaveBeenCalled();
|
||||
expect(chosenPath).toEqual(storagePath);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user