Merge branch 'main' into koesie10/typed-local-query-commands
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
## [UNRELEASED]
|
||||
|
||||
- Show data flow paths of a variant analysis in a new tab
|
||||
- Show labels of entities in exported CSV results [#2170](https://github.com/github/vscode-codeql/pull/2170)
|
||||
|
||||
## 1.8.0 - 9 March 2023
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ import {
|
||||
showInformationMessageWithAction,
|
||||
tmpDir,
|
||||
tmpDirDisposal,
|
||||
prepareCodeTour,
|
||||
} from "./helpers";
|
||||
import {
|
||||
asError,
|
||||
@@ -523,6 +524,14 @@ async function installOrUpdateThenTryActivate(
|
||||
): Promise<CodeQLExtensionInterface | Record<string, never>> {
|
||||
await installOrUpdateDistribution(ctx, distributionManager, config);
|
||||
|
||||
try {
|
||||
await prepareCodeTour();
|
||||
} catch (e: unknown) {
|
||||
void extLogger.log(
|
||||
`Could not open tutorial workspace automatically: ${getErrorMessage(e)}`,
|
||||
);
|
||||
}
|
||||
|
||||
// Display the warnings even if the extension has already activated.
|
||||
const distributionResult =
|
||||
await getDistributionDisplayingDistributionWarnings(distributionManager);
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
window as Window,
|
||||
workspace,
|
||||
env,
|
||||
commands,
|
||||
} from "vscode";
|
||||
import { CodeQLCliServer, QlpacksInfo } from "./cli";
|
||||
import { UserCancellationException } from "./commandRunner";
|
||||
@@ -25,6 +26,7 @@ import { telemetryListener } from "./telemetry";
|
||||
import { RedactableError } from "./pure/errors";
|
||||
import { getQlPackPath } from "./pure/ql";
|
||||
import { dbSchemeToLanguage } from "./common/query-language";
|
||||
import { isCodespacesTemplate } from "./config";
|
||||
|
||||
// Shared temporary folder for the extension.
|
||||
export const tmpDir = dirSync({
|
||||
@@ -266,6 +268,51 @@ export function isFolderAlreadyInWorkspace(folderName: string) {
|
||||
);
|
||||
}
|
||||
|
||||
/** Check if the current workspace is the CodeTour and open the workspace folder.
|
||||
* Without this, we can't run the code tour correctly.
|
||||
**/
|
||||
export async function prepareCodeTour(): Promise<void> {
|
||||
if (workspace.workspaceFolders?.length) {
|
||||
const currentFolder = workspace.workspaceFolders[0].uri.fsPath;
|
||||
|
||||
const tutorialWorkspacePath = join(
|
||||
currentFolder,
|
||||
"tutorial.code-workspace",
|
||||
);
|
||||
const toursFolderPath = join(currentFolder, ".tours");
|
||||
|
||||
/** We're opening the tutorial workspace, if we detect it.
|
||||
* This will only happen if the following three conditions are met:
|
||||
* - the .tours folder exists
|
||||
* - the tutorial.code-workspace file exists
|
||||
* - the CODESPACES_TEMPLATE setting doesn't exist (it's only set if the user has already opened
|
||||
* the tutorial workspace so it's a good indicator that the user is in the folder but has ignored
|
||||
* the prompt to open the workspace)
|
||||
*/
|
||||
if (
|
||||
(await pathExists(tutorialWorkspacePath)) &&
|
||||
(await pathExists(toursFolderPath)) &&
|
||||
!isCodespacesTemplate()
|
||||
) {
|
||||
const answer = await showBinaryChoiceDialog(
|
||||
"We've detected you're in the CodeQL Tour repo. We will need to open the workspace file to continue. Reload?",
|
||||
);
|
||||
|
||||
if (!answer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tutorialWorkspaceUri = Uri.parse(tutorialWorkspacePath);
|
||||
|
||||
void extLogger.log(
|
||||
`In prepareCodeTour() method, going to open the tutorial workspace file: ${tutorialWorkspacePath}`,
|
||||
);
|
||||
|
||||
await commands.executeCommand("vscode.openFolder", tutorialWorkspaceUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a utility method to invoke a function only if a minimum time interval has elapsed since
|
||||
* the last invocation of that function.
|
||||
|
||||
@@ -48,7 +48,7 @@ import {
|
||||
import {
|
||||
deserializeQueryHistory,
|
||||
serializeQueryHistory,
|
||||
} from "../query-serialization";
|
||||
} from "./store/query-history-store";
|
||||
import { pathExists } from "fs-extra";
|
||||
import { CliVersionConstraint } from "../cli";
|
||||
import { HistoryItemLabelProvider } from "./history-item-label-provider";
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { pathExists, readFile, remove, mkdir, writeFile } from "fs-extra";
|
||||
import { dirname } from "path";
|
||||
|
||||
import { showAndLogExceptionWithTelemetry } from "./helpers";
|
||||
import { showAndLogExceptionWithTelemetry } from "../../helpers";
|
||||
import {
|
||||
asError,
|
||||
asyncFilter,
|
||||
getErrorMessage,
|
||||
getErrorStack,
|
||||
} from "./pure/helpers-pure";
|
||||
import { CompletedQueryInfo, LocalQueryInfo } from "./query-results";
|
||||
import { QueryHistoryInfo } from "./query-history/query-history-info";
|
||||
import { QueryEvaluationInfo } from "./run-queries-shared";
|
||||
import { QueryResultType } from "./pure/legacy-messages";
|
||||
import { redactableError } from "./pure/errors";
|
||||
} from "../../pure/helpers-pure";
|
||||
import { CompletedQueryInfo, LocalQueryInfo } from "../../query-results";
|
||||
import { QueryHistoryInfo } from "../query-history-info";
|
||||
import { QueryEvaluationInfo } from "../../run-queries-shared";
|
||||
import { QueryResultType } from "../../pure/legacy-messages";
|
||||
import { redactableError } from "../../pure/errors";
|
||||
|
||||
export async function deserializeQueryHistory(
|
||||
fsPath: string,
|
||||
@@ -30,7 +30,7 @@ import { nanoid } from "nanoid";
|
||||
import { CodeQLCliServer } from "./cli";
|
||||
import { SELECT_QUERY_NAME } from "./contextual/locationFinder";
|
||||
import { DatabaseManager } from "./local-databases";
|
||||
import { DecodedBqrsChunk } from "./pure/bqrs-cli-types";
|
||||
import { DecodedBqrsChunk, EntityValue } from "./pure/bqrs-cli-types";
|
||||
import { extLogger, Logger } from "./common";
|
||||
import { generateSummarySymbolsFile } from "./log-insights/summary-parser";
|
||||
import { getErrorMessage } from "./pure/helpers-pure";
|
||||
@@ -351,11 +351,17 @@ export class QueryEvaluationInfo {
|
||||
chunk.tuples.forEach((tuple) => {
|
||||
out.write(
|
||||
`${tuple
|
||||
.map((v, i) =>
|
||||
chunk.columns[i].kind === "String"
|
||||
? `"${typeof v === "string" ? v.replaceAll('"', '""') : v}"`
|
||||
: v,
|
||||
)
|
||||
.map((v, i) => {
|
||||
if (chunk.columns[i].kind === "String") {
|
||||
return `"${
|
||||
typeof v === "string" ? v.replaceAll('"', '""') : v
|
||||
}"`;
|
||||
} else if (chunk.columns[i].kind === "Entity") {
|
||||
return (v as EntityValue).label;
|
||||
} else {
|
||||
return v;
|
||||
}
|
||||
})
|
||||
.join(",")}\n`,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[
|
||||
"v2.12.4",
|
||||
"v2.12.5",
|
||||
"v2.11.6",
|
||||
"v2.7.6",
|
||||
"v2.8.5",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
commands,
|
||||
EnvironmentVariableCollection,
|
||||
EnvironmentVariableMutator,
|
||||
Event,
|
||||
@@ -15,7 +16,14 @@ import {
|
||||
import { dump } from "js-yaml";
|
||||
import * as tmp from "tmp";
|
||||
import { join } from "path";
|
||||
import { writeFileSync, mkdirSync, ensureDirSync, symlinkSync } from "fs-extra";
|
||||
import {
|
||||
writeFileSync,
|
||||
mkdirSync,
|
||||
ensureDirSync,
|
||||
symlinkSync,
|
||||
writeFile,
|
||||
mkdir,
|
||||
} from "fs-extra";
|
||||
import { DirResult } from "tmp";
|
||||
|
||||
import {
|
||||
@@ -24,6 +32,7 @@ import {
|
||||
isFolderAlreadyInWorkspace,
|
||||
isLikelyDatabaseRoot,
|
||||
isLikelyDbLanguageFolder,
|
||||
prepareCodeTour,
|
||||
showBinaryChoiceDialog,
|
||||
showBinaryChoiceWithUrlDialog,
|
||||
showInformationMessageWithAction,
|
||||
@@ -31,6 +40,7 @@ import {
|
||||
} from "../../../src/helpers";
|
||||
import { reportStreamProgress } from "../../../src/commandRunner";
|
||||
import { QueryLanguage } from "../../../src/common/query-language";
|
||||
import { Setting } from "../../../src/config";
|
||||
|
||||
describe("helpers", () => {
|
||||
describe("Invocation rate limiter", () => {
|
||||
@@ -559,3 +569,113 @@ describe("isFolderAlreadyInWorkspace", () => {
|
||||
expect(isFolderAlreadyInWorkspace("/third/path")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("prepareCodeTour", () => {
|
||||
let dir: tmp.DirResult;
|
||||
let showInformationMessageSpy: jest.SpiedFunction<
|
||||
typeof window.showInformationMessage
|
||||
>;
|
||||
|
||||
beforeEach(() => {
|
||||
dir = tmp.dirSync();
|
||||
|
||||
const mockWorkspaceFolders = [
|
||||
{
|
||||
uri: Uri.file(dir.name),
|
||||
name: "test",
|
||||
index: 0,
|
||||
},
|
||||
] as WorkspaceFolder[];
|
||||
|
||||
jest
|
||||
.spyOn(workspace, "workspaceFolders", "get")
|
||||
.mockReturnValue(mockWorkspaceFolders);
|
||||
|
||||
showInformationMessageSpy = jest
|
||||
.spyOn(window, "showInformationMessage")
|
||||
.mockResolvedValue({ title: "Yes" });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
dir.removeCallback();
|
||||
});
|
||||
|
||||
describe("if we're in the tour repo", () => {
|
||||
describe("if the workspace is not already open", () => {
|
||||
it("should open the tutorial workspace", async () => {
|
||||
// set up directory to have a 'tutorial.code-workspace' file
|
||||
const tutorialWorkspacePath = join(dir.name, "tutorial.code-workspace");
|
||||
await writeFile(tutorialWorkspacePath, "{}");
|
||||
|
||||
// set up a .tours directory to indicate we're in the tour codespace
|
||||
const tourDirPath = join(dir.name, ".tours");
|
||||
await mkdir(tourDirPath);
|
||||
|
||||
// spy that we open the workspace file by calling the 'vscode.openFolder' command
|
||||
const commandSpy = jest.spyOn(commands, "executeCommand");
|
||||
commandSpy.mockImplementation(() => Promise.resolve());
|
||||
|
||||
await prepareCodeTour();
|
||||
|
||||
expect(showInformationMessageSpy).toHaveBeenCalled();
|
||||
expect(commandSpy).toHaveBeenCalledWith(
|
||||
"vscode.openFolder",
|
||||
expect.objectContaining({
|
||||
path: Uri.parse(tutorialWorkspacePath).fsPath,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("if the workspace is already open", () => {
|
||||
it("should not open the tutorial workspace", async () => {
|
||||
// Set isCodespaceTemplate to true to indicate the workspace has already been opened
|
||||
jest.spyOn(Setting.prototype, "getValue").mockReturnValue(true);
|
||||
|
||||
// set up directory to have a 'tutorial.code-workspace' file
|
||||
const tutorialWorkspacePath = join(dir.name, "tutorial.code-workspace");
|
||||
await writeFile(tutorialWorkspacePath, "{}");
|
||||
|
||||
// set up a .tours directory to indicate we're in the tour codespace
|
||||
const tourDirPath = join(dir.name, ".tours");
|
||||
await mkdir(tourDirPath);
|
||||
|
||||
// spy that we open the workspace file by calling the 'vscode.openFolder' command
|
||||
const commandSpy = jest.spyOn(commands, "executeCommand");
|
||||
commandSpy.mockImplementation(() => Promise.resolve());
|
||||
|
||||
await prepareCodeTour();
|
||||
|
||||
expect(commandSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("if we're in a different tour repo", () => {
|
||||
it("should not open the tutorial workspace", async () => {
|
||||
// set up a .tours directory
|
||||
const tourDirPath = join(dir.name, ".tours");
|
||||
await mkdir(tourDirPath);
|
||||
|
||||
// spy that we open the workspace file by calling the 'vscode.openFolder' command
|
||||
const commandSpy = jest.spyOn(commands, "executeCommand");
|
||||
commandSpy.mockImplementation(() => Promise.resolve());
|
||||
|
||||
await prepareCodeTour();
|
||||
|
||||
expect(commandSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("if we're in a different repo with no tour", () => {
|
||||
it("should not open the tutorial workspace", async () => {
|
||||
// spy that we open the workspace file by calling the 'vscode.openFolder' command
|
||||
const commandSpy = jest.spyOn(commands, "executeCommand");
|
||||
commandSpy.mockImplementation(() => Promise.resolve());
|
||||
|
||||
await prepareCodeTour();
|
||||
|
||||
expect(commandSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
import {
|
||||
deserializeQueryHistory,
|
||||
serializeQueryHistory,
|
||||
} from "../../../src/query-serialization";
|
||||
} from "../../../../../src/query-history/store/query-history-store";
|
||||
import { join } from "path";
|
||||
import { writeFileSync, mkdirpSync, writeFile } from "fs-extra";
|
||||
import { LocalQueryInfo, InitialQueryInfo } from "../../../src/query-results";
|
||||
import { QueryWithResults } from "../../../src/run-queries-shared";
|
||||
import { DatabaseInfo } from "../../../src/pure/interface-types";
|
||||
import {
|
||||
LocalQueryInfo,
|
||||
InitialQueryInfo,
|
||||
} from "../../../../../src/query-results";
|
||||
import { QueryWithResults } from "../../../../../src/run-queries-shared";
|
||||
import { DatabaseInfo } from "../../../../../src/pure/interface-types";
|
||||
import { CancellationTokenSource, Uri } from "vscode";
|
||||
import { tmpDir } from "../../../src/helpers";
|
||||
import { QueryResultType } from "../../../src/pure/legacy-messages";
|
||||
import { QueryInProgress } from "../../../src/legacy-query-server/run-queries";
|
||||
import { VariantAnalysisHistoryItem } from "../../../src/query-history/variant-analysis-history-item";
|
||||
import { QueryHistoryInfo } from "../../../src/query-history/query-history-info";
|
||||
import { createMockVariantAnalysisHistoryItem } from "../../factories/query-history/variant-analysis-history-item";
|
||||
import { tmpDir } from "../../../../../src/helpers";
|
||||
import { QueryResultType } from "../../../../../src/pure/legacy-messages";
|
||||
import { QueryInProgress } from "../../../../../src/legacy-query-server/run-queries";
|
||||
import { VariantAnalysisHistoryItem } from "../../../../../src/query-history/variant-analysis-history-item";
|
||||
import { QueryHistoryInfo } from "../../../../../src/query-history/query-history-info";
|
||||
import { createMockVariantAnalysisHistoryItem } from "../../../../factories/query-history/variant-analysis-history-item";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
describe("serialize and deserialize", () => {
|
||||
Reference in New Issue
Block a user