Merge branch 'main' into mob/consume-update-event

This commit is contained in:
Elena Tanasoiu
2022-10-24 11:13:41 +01:00
34 changed files with 4018 additions and 258 deletions

View File

@@ -103,6 +103,11 @@ jobs:
run: |
npm run lint
- name: Lint scenarios
working-directory: extensions/ql-vscode
run: |
npm run lint:scenarios
- name: Run unit tests (Linux)
working-directory: extensions/ql-vscode
if: matrix.os == 'ubuntu-latest'

View File

@@ -91,29 +91,58 @@ Alternatively, you can start Storybook inside of VSCode. There is a VSCode launc
More information about Storybook can be found inside the **Overview** page once you have launched Storybook.
### Running the unit tests and integration tests that do not require a CLI instance
### Testing
Unit tests and many integration tests do not require a copy of the CodeQL CLI.
We have several types of tests:
Outside of vscode, in the `extensions/ql-vscode` directory, run:
* Unit tests: these live in the `tests/pure-tests/` directory
* View tests: these live in `src/view/variant-analysis/__tests__/`
* VSCode integration tests: these live in `src/vscode-tests/no-workspace` and `src/vscode-tests/minimal-workspace`
* CLI integration tests: these live in `src/vscode-tests/cli-integration`
```shell
npm run test && npm run integration
```
The CLI integration tests require an instance of the CodeQL CLI to run so they will require some extra setup steps. When adding new tests to our test suite, please be mindful of whether they need to be in the cli-integration folder. If the tests don't depend on the CLI, they are better suited to being a VSCode integration test.
Alternatively, you can run the tests inside of vscode. There are several vscode launch configurations defined that run the unit and integration tests. They can all be found in the debug view.
#### Running the tests
Only the _With CLI_ tests require a CLI instance to run. See below on how to do that.
##### 1. From the terminal
Running from a terminal, you _must_ set the `TEST_CODEQL_PATH` variable to point to a checkout of the `github/codeql` repository. The appropriate CLI version will be downloaded as part of the test.
First move into the `extensions/ql-vscode` directory. Then, depending on which tests you want to run, use the appropriate command to run the tests:
### Running the integration tests
* Unit tests: `npm run test:unit`
* View Tests: `npm test:view`
* VSCode integration tests: `npm run integration`
You will need to run CLI tests using a task from inside of VS Code called _Launch Integration Tests - With CLI_.
###### CLI integration tests
The CLI integration tests require the CodeQL standard libraries in order to run so you will need to clone a local copy of the `github/codeql` repository.
From inside of VSCode, open the `launch.json` file and in the _Launch Integration Tests - With CLI_ task, uncomment the `"${workspaceRoot}/../codeql"` line. If necessary, replace value with a path to your checkout, and then run the task.
1. Set the `TEST_CODEQL_PATH` environment variable: running from a terminal, you _must_ set the `TEST_CODEQL_PATH` variable to point to a checkout of the `github/codeql` repository. The appropriate CLI version will be downloaded as part of the test.
2. Run your test command:
```shell
cd extensions/ql-vscode && npm run cli-integration
```
##### 2. From VSCode
Alternatively, you can run the tests inside of VSCode. There are several VSCode launch configurations defined that run the unit and integration tests.
You will need to run tests using a task from inside of VS Code, under the "Run and Debug" view:
* Unit tests: run the _Launch Unit Tests - React_ task
* View Tests: run the _Launch Unit Tests_ task
* VSCode integration tests: run the _Launch Unit Tests - No Workspace_ and _Launch Unit Tests - Minimal Workspace_ tasks
###### CLI integration tests
The CLI integration tests require the CodeQL standard libraries in order to run so you will need to clone a local copy of the `github/codeql` repository.
1. Set the `TEST_CODEQL_PATH` environment variable: running from a terminal, you _must_ set the `TEST_CODEQL_PATH` variable to point to a checkout of the `github/codeql` repository. The appropriate CLI version will be downloaded as part of the test.
2. Set the codeql path in VSCode's launch configuration: open `launch.json` and under the _Launch Integration Tests - With CLI_ section, uncomment the `"${workspaceRoot}/../codeql"` line. If you've cloned the `github/codeql` repo to a different path, replace the value with the correct path.
3. Run the VSCode task from the "Run and Debug" view called _Launch Integration Tests - With CLI_.
## Releasing (write access required)
@@ -137,7 +166,7 @@ From inside of VSCode, open the `launch.json` file and in the _Launch Integratio
git tag v1.3.6
```
If you've accidentally created a badly named tag, you can delete it via
If you've accidentally created a badly named tag, you can delete it via
```bash
git tag -d badly-named-tag
```
@@ -148,13 +177,13 @@ From inside of VSCode, open the `launch.json` file and in the _Launch Integratio
```bash
git push upstream refs/tags/v1.3.6
```
b. If you're working straight in this repo:
```bash
git push origin refs/tags/v1.3.6
```
```
This will trigger [a release build](https://github.com/github/vscode-codeql/releases) on Actions.
* **IMPORTANT** Make sure you are on the `main` branch and your local checkout is fully updated when you add the tag.

View File

@@ -8,7 +8,8 @@ const config: StorybookConfig = {
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions'
'@storybook/addon-interactions',
'./vscode-theme-addon/preset.ts',
],
framework: '@storybook/react',
core: {

View File

@@ -4,8 +4,6 @@ import { action } from '@storybook/addon-actions';
// Allow all stories/components to use Codicons
import '@vscode/codicons/dist/codicon.css';
import '../src/stories/vscode-theme.css';
// https://storybook.js.org/docs/react/configure/overview#configure-story-rendering
export const parameters = {
// All props starting with `on` will automatically receive an action as a prop
@@ -22,13 +20,8 @@ export const parameters = {
theme: themes.dark,
},
backgrounds: {
default: 'dark',
values: [
{
name: 'dark',
value: '#1e1e1e',
},
],
// The background is injected by our theme CSS files
disable: true,
}
};

View File

@@ -0,0 +1,19 @@
{
"compilerOptions": {
"module": "esnext",
"moduleResolution": "node",
"target": "es6",
"outDir": "out",
"lib": ["ES2021", "dom"],
"jsx": "react",
"sourceMap": true,
"rootDir": "..",
"strict": true,
"noUnusedLocals": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"experimentalDecorators": true,
"skipLibCheck": true
},
"exclude": ["node_modules"]
}

View File

@@ -0,0 +1,49 @@
import * as React from 'react';
import { FunctionComponent, useCallback } from 'react';
import { useGlobals } from '@storybook/api';
import { IconButton, Icons, WithTooltip, TooltipLinkList, Link, WithHideFn } from '@storybook/components';
import { themeNames, VSCodeTheme } from './theme';
export const ThemeSelector: FunctionComponent = () => {
const [{ vscodeTheme }, updateGlobals] = useGlobals();
const changeTheme = useCallback((theme: VSCodeTheme) => {
updateGlobals({
vscodeTheme: theme,
});
}, [updateGlobals]);
const createLinks = useCallback((onHide: () => void): Link[] => Object.values(VSCodeTheme).map((theme) => ({
id: theme,
onClick() {
changeTheme(theme);
onHide();
},
title: themeNames[theme],
value: theme,
active: vscodeTheme === theme,
})), [vscodeTheme, changeTheme]);
return (
<WithTooltip
placement="top"
trigger="click"
closeOnClick
tooltip={({ onHide }: WithHideFn) => (
<TooltipLinkList
links={createLinks(onHide)}
/>
)}
>
<IconButton
key="theme"
title="Change the theme of the preview"
active={vscodeTheme !== VSCodeTheme.Dark}
>
<Icons icon="dashboard" />
</IconButton>
</WithTooltip>
);
};

View File

@@ -0,0 +1,14 @@
import * as React from 'react';
import { addons, types } from '@storybook/addons';
import { ThemeSelector } from './ThemeSelector';
const ADDON_ID = 'vscode-theme-addon';
addons.register(ADDON_ID, () => {
addons.add(ADDON_ID, {
title: 'VSCode Themes',
type: types.TOOL,
match: ({ viewMode }) => !!(viewMode && viewMode.match(/^(story|docs)$/)),
render: () => <ThemeSelector />,
});
});

View File

@@ -0,0 +1,7 @@
export function config(entry = []) {
return [...entry, require.resolve("./preview.ts")];
}
export function managerEntries(entry = []) {
return [...entry, require.resolve("./manager.tsx")];
}

View File

@@ -0,0 +1,8 @@
import { withTheme } from './withTheme';
import { VSCodeTheme } from './theme';
export const decorators = [withTheme];
export const globals = {
vscodeTheme: VSCodeTheme.Dark,
};

View File

@@ -0,0 +1,9 @@
export enum VSCodeTheme {
Dark = 'dark',
Light = 'light',
}
export const themeNames: { [key in VSCodeTheme]: string } = {
[VSCodeTheme.Dark]: 'Dark+',
[VSCodeTheme.Light]: 'Light+',
}

View File

@@ -0,0 +1,36 @@
import { useEffect, useGlobals } from '@storybook/addons';
import type { AnyFramework, PartialStoryFn as StoryFunction, StoryContext } from '@storybook/csf';
import { VSCodeTheme } from './theme';
const themeFiles: { [key in VSCodeTheme]: string } = {
[VSCodeTheme.Dark]: require('!file-loader?modules!../../src/stories/vscode-theme-dark.css').default,
[VSCodeTheme.Light]: require('!file-loader?modules!../../src/stories/vscode-theme-light.css').default,
}
export const withTheme = (
StoryFn: StoryFunction<AnyFramework>,
context: StoryContext<AnyFramework>
) => {
const [{ vscodeTheme }] = useGlobals();
useEffect(() => {
const styleSelectorId =
context.viewMode === 'docs'
? `addon-vscode-theme-docs-${context.id}`
: `addon-vscode-theme-theme`;
const theme = Object.values(VSCodeTheme).includes(vscodeTheme) ? vscodeTheme as VSCodeTheme : VSCodeTheme.Dark;
document.getElementById(styleSelectorId)?.remove();
const link = document.createElement('link');
link.id = styleSelectorId;
link.href = themeFiles[theme];
link.rel = 'stylesheet';
document.head.appendChild(link);
}, [vscodeTheme]);
return StoryFn();
};

File diff suppressed because it is too large Load Diff

View File

@@ -645,6 +645,18 @@
"command": "codeQL.gotoQL",
"title": "CodeQL: Go to QL Code",
"enablement": "codeql.hasQLSource"
},
{
"command": "codeQL.mockGitHubApiServer.startRecording",
"title": "CodeQL: Mock GitHub API Server: Start Scenario Recording"
},
{
"command": "codeQL.mockGitHubApiServer.saveScenario",
"title": "CodeQL: Mock GitHub API Server: Save Scenario"
},
{
"command": "codeQL.mockGitHubApiServer.cancelRecording",
"title": "CodeQL: Mock GitHub API Server: Cancel Scenario Recording"
}
],
"menus": {
@@ -1104,6 +1116,18 @@
{
"command": "codeQLTests.showOutputDifferences",
"when": "false"
},
{
"command": "codeQL.mockGitHubApiServer.startRecording",
"when": "config.codeQL.variantAnalysis.mockGitHubApiServer && !codeQL.mockGitHubApiServer.recording"
},
{
"command": "codeQL.mockGitHubApiServer.saveScenario",
"when": "config.codeQL.variantAnalysis.mockGitHubApiServer && codeQL.mockGitHubApiServer.recording"
},
{
"command": "codeQL.mockGitHubApiServer.cancelRecording",
"when": "config.codeQL.variantAnalysis.mockGitHubApiServer && codeQL.mockGitHubApiServer.recording"
}
],
"editor/context": [
@@ -1210,7 +1234,8 @@
"lint": "eslint src test --ext .ts,.tsx --max-warnings=0",
"format-staged": "lint-staged",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
"build-storybook": "build-storybook",
"lint:scenarios": "ts-node scripts/lint-scenarios.ts"
},
"dependencies": {
"@octokit/plugin-retry": "^3.0.9",
@@ -1305,6 +1330,7 @@
"@types/xml2js": "~0.4.4",
"@typescript-eslint/eslint-plugin": "^4.26.0",
"@typescript-eslint/parser": "^4.26.0",
"ajv": "^8.11.0",
"ansi-colors": "^4.1.1",
"applicationinsights": "^2.3.5",
"babel-loader": "^8.2.5",
@@ -1330,6 +1356,7 @@
"mini-css-extract-plugin": "^2.6.1",
"mocha": "^10.0.0",
"mocha-sinon": "~2.1.2",
"msw": "^0.47.4",
"npm-run-all": "^4.1.5",
"prettier": "~2.0.5",
"proxyquire": "~2.1.3",
@@ -1337,6 +1364,7 @@
"sinon-chai": "~3.5.0",
"through2": "^4.0.2",
"ts-jest": "^29.0.1",
"ts-json-schema-generator": "^1.1.2",
"ts-loader": "^8.1.0",
"ts-node": "^10.7.0",
"ts-protoc-gen": "^0.9.0",

View File

@@ -0,0 +1,79 @@
import * as fs from 'fs-extra';
import * as path from 'path';
import Ajv from 'ajv';
import * as tsj from 'ts-json-schema-generator';
const extensionDirectory = path.resolve(__dirname, '..');
const rootDirectory = path.resolve(extensionDirectory, '../..');
const scenariosDirectory = path.resolve(extensionDirectory, 'src/mocks/scenarios');
const debug = process.env.RUNNER_DEBUG || process.argv.includes('--debug');
async function lintScenarios() {
const schema = tsj.createGenerator({
path: path.resolve(extensionDirectory, 'src/mocks/gh-api-request.ts'),
tsconfig: path.resolve(extensionDirectory, 'tsconfig.json'),
type: 'GitHubApiRequest',
skipTypeCheck: true,
topRef: true,
additionalProperties: true,
}).createSchema('GitHubApiRequest');
const ajv = new Ajv();
if (!ajv.validateSchema(schema)) {
throw new Error('Invalid schema: ' + ajv.errorsText());
}
const validate = await ajv.compile(schema);
let invalidFiles = 0;
if (!(await fs.pathExists(scenariosDirectory))) {
console.error('Scenarios directory does not exist: ' + scenariosDirectory);
// Do not exit with a non-zero status code, as this is not a fatal error.
return;
}
for await (const file of getFiles(scenariosDirectory)) {
if (!file.endsWith('.json')) {
continue;
}
const contents = await fs.readFile(file, 'utf8');
const data = JSON.parse(contents);
if (!validate(data)) {
validate.errors?.forEach(error => {
// https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-error-message
console.log(`::error file=${path.relative(rootDirectory, file)}::${error.instancePath}: ${error.message}`);
});
invalidFiles++;
} else if (debug) {
console.log(`File '${path.relative(rootDirectory, file)}' is valid`);
}
}
if (invalidFiles > 0) {
process.exit(1);
}
}
// https://stackoverflow.com/a/45130990
async function* getFiles(dir: string): AsyncGenerator<string> {
const dirents = await fs.readdir(dir, { withFileTypes: true });
for (const dirent of dirents) {
const res = path.resolve(dir, dirent.name);
if (dirent.isDirectory()) {
yield* getFiles(res);
} else {
yield res;
}
}
}
lintScenarios().catch(e => {
console.error(e);
process.exit(2);
});

View File

@@ -429,3 +429,42 @@ const LIVE_RESULTS = new Setting('liveResults', REMOTE_QUERIES_SETTING);
export function isVariantAnalysisLiveResultsEnabled(): boolean {
return !!LIVE_RESULTS.getValue<boolean>();
}
// Settings for mocking the GitHub API.
const MOCK_GH_API_SERVER = new Setting('mockGitHubApiServer', ROOT_SETTING);
/**
* A flag indicating whether to enable a mock GitHub API server.
*/
const MOCK_GH_API_SERVER_ENABLED = new Setting('enabled', MOCK_GH_API_SERVER);
/**
* A path to a directory containing test scenarios. If this setting is not set,
* the mock server will a default location for test scenarios in dev mode, and
* will show a menu to select a directory in production mode.
*/
const MOCK_GH_API_SERVER_SCENARIOS_PATH = new Setting('scenariosPath', MOCK_GH_API_SERVER);
export interface MockGitHubApiConfig {
mockServerEnabled: boolean;
mockScenariosPath: string;
onDidChangeConfiguration: Event<void>;
}
export class MockGitHubApiConfigListener extends ConfigListener implements MockGitHubApiConfig {
protected handleDidChangeConfiguration(e: ConfigurationChangeEvent): void {
this.handleDidChangeConfigurationForRelevantSettings([MOCK_GH_API_SERVER], e);
}
public get mockServerEnabled(): boolean {
return !!MOCK_GH_API_SERVER_ENABLED.getValue<boolean>();
}
public get mockScenariosPath(): string {
return MOCK_GH_API_SERVER_SCENARIOS_PATH.getValue<string>();
}
}
export function getMockGitHubApiServerScenariosPath(): string | undefined {
return MOCK_GH_API_SERVER_SCENARIOS_PATH.getValue<string>();
}

View File

@@ -116,6 +116,7 @@ import {
} from './remote-queries/gh-api/variant-analysis';
import { VariantAnalysisManager } from './remote-queries/variant-analysis-manager';
import { createVariantAnalysisContentProvider } from './remote-queries/variant-analysis-content-provider';
import { MockGitHubApiServer } from './mocks/mock-gh-api-server';
/**
* extension.ts
@@ -1190,6 +1191,27 @@ async function activateWithInstalledDistribution(
)
);
const mockServer = new MockGitHubApiServer(ctx);
ctx.subscriptions.push(mockServer);
ctx.subscriptions.push(
commandRunner(
'codeQL.mockGitHubApiServer.startRecording',
async () => await mockServer.startRecording(),
)
);
ctx.subscriptions.push(
commandRunner(
'codeQL.mockGitHubApiServer.saveScenario',
async () => await mockServer.saveScenario(),
)
);
ctx.subscriptions.push(
commandRunner(
'codeQL.mockGitHubApiServer.cancelRecording',
async () => await mockServer.cancelRecording(),
)
);
await commands.executeCommand('codeQLDatabases.removeOrphanedDatabases');
void logger.log('Successfully finished extension initialization.');

View File

@@ -54,7 +54,7 @@ export class QueryInProgress {
}
get compiledQueryPath() {
return path.join(this.querySaveDir, 'compiledQuery.qlo');
return this.queryEvalInfo.compileQueryPath;
}

View File

@@ -0,0 +1,76 @@
import { Repository } from '../remote-queries/gh-api/repository';
import { VariantAnalysis, VariantAnalysisRepoTask } from '../remote-queries/gh-api/variant-analysis';
// Types that represent requests/responses from the GitHub API
// that we need to mock.
export enum RequestKind {
GetRepo = 'getRepo',
SubmitVariantAnalysis = 'submitVariantAnalysis',
GetVariantAnalysis = 'getVariantAnalysis',
GetVariantAnalysisRepo = 'getVariantAnalysisRepo',
GetVariantAnalysisRepoResult = 'getVariantAnalysisRepoResult',
}
export interface BasicErorResponse {
message: string;
}
export interface GetRepoRequest {
request: {
kind: RequestKind.GetRepo
},
response: {
status: number,
body: Repository | BasicErorResponse | undefined
}
}
export interface SubmitVariantAnalysisRequest {
request: {
kind: RequestKind.SubmitVariantAnalysis
},
response: {
status: number,
body?: VariantAnalysis | BasicErorResponse
}
}
export interface GetVariantAnalysisRequest {
request: {
kind: RequestKind.GetVariantAnalysis
},
response: {
status: number,
body?: VariantAnalysis | BasicErorResponse
}
}
export interface GetVariantAnalysisRepoRequest {
request: {
kind: RequestKind.GetVariantAnalysisRepo,
repositoryId: number
},
response: {
status: number,
body?: VariantAnalysisRepoTask | BasicErorResponse
}
}
export interface GetVariantAnalysisRepoResultRequest {
request: {
kind: RequestKind.GetVariantAnalysisRepoResult,
repositoryId: number
},
response: {
status: number,
body?: ArrayBuffer
}
}
export type GitHubApiRequest =
| GetRepoRequest
| SubmitVariantAnalysisRequest
| GetVariantAnalysisRequest
| GetVariantAnalysisRepoRequest
| GetVariantAnalysisRepoResultRequest;

View File

@@ -0,0 +1,173 @@
import * as fs from 'fs-extra';
import { commands, env, ExtensionContext, ExtensionMode, Uri, window } from 'vscode';
import { setupServer, SetupServerApi } from 'msw/node';
import { getMockGitHubApiServerScenariosPath, MockGitHubApiConfigListener } from '../config';
import { DisposableObject } from '../pure/disposable-object';
import { Recorder } from './recorder';
/**
* Enables mocking of the GitHub API server via HTTP interception, using msw.
*/
export class MockGitHubApiServer extends DisposableObject {
private isListening: boolean;
private config: MockGitHubApiConfigListener;
private readonly server: SetupServerApi;
private readonly recorder: Recorder;
constructor(
private readonly ctx: ExtensionContext,
) {
super();
this.isListening = false;
this.config = new MockGitHubApiConfigListener();
this.server = setupServer();
this.recorder = this.push(new Recorder(this.server));
this.setupConfigListener();
}
public startServer(): void {
if (this.isListening) {
return;
}
this.server.listen();
this.isListening = true;
}
public stopServer(): void {
this.server.close();
this.isListening = false;
}
public loadScenario(): void {
// TODO: Implement logic to load a scenario from a directory.
}
public listScenarios(): void {
// TODO: Implement logic to list all available scenarios.
}
public async startRecording(): Promise<void> {
if (this.recorder.isRecording) {
void window.showErrorMessage('A scenario is already being recorded. Use the "Save Scenario" or "Cancel Scenario" commands to finish recording.');
return;
}
this.recorder.start();
// Set a value in the context to track whether we are recording. This allows us to use this to show/hide commands (see package.json)
await commands.executeCommand('setContext', 'codeQL.mockGitHubApiServer.recording', true);
await window.showInformationMessage('Recording scenario. To save the scenario, use the "CodeQL Mock GitHub API Server: Save Scenario" command.');
}
public async saveScenario(): Promise<void> {
const scenariosPath = await this.getScenariosPath();
if (!scenariosPath) {
return;
}
// Set a value in the context to track whether we are recording. This allows us to use this to show/hide commands (see package.json)
await commands.executeCommand('setContext', 'codeQL.mockGitHubApiServer.recording', false);
if (!this.recorder.isRecording) {
void window.showErrorMessage('No scenario is currently being recorded.');
return;
}
if (!this.recorder.anyRequestsRecorded) {
void window.showWarningMessage('No requests were recorded. Cancelling scenario.');
await this.stopRecording();
return;
}
const name = await window.showInputBox({
title: 'Save scenario',
prompt: 'Enter a name for the scenario.',
placeHolder: 'successful-run',
});
if (!name) {
return;
}
const filePath = await this.recorder.save(scenariosPath, name);
await this.stopRecording();
const action = await window.showInformationMessage(`Scenario saved to ${filePath}`, 'Open directory');
if (action === 'Open directory') {
await env.openExternal(Uri.file(filePath));
}
}
public async cancelRecording(): Promise<void> {
if (!this.recorder.isRecording) {
void window.showErrorMessage('No scenario is currently being recorded.');
return;
}
await this.stopRecording();
void window.showInformationMessage('Recording cancelled.');
}
private async stopRecording(): Promise<void> {
// Set a value in the context to track whether we are recording. This allows us to use this to show/hide commands (see package.json)
await commands.executeCommand('setContext', 'codeQL.mockGitHubApiServer.recording', false);
await this.recorder.stop();
await this.recorder.clear();
}
private async getScenariosPath(): Promise<string | undefined> {
const scenariosPath = getMockGitHubApiServerScenariosPath();
if (scenariosPath) {
return scenariosPath;
}
if (this.ctx.extensionMode === ExtensionMode.Development) {
const developmentScenariosPath = Uri.joinPath(this.ctx.extensionUri, 'src/mocks/scenarios').fsPath.toString();
if (await fs.pathExists(developmentScenariosPath)) {
return developmentScenariosPath;
}
}
const directories = await window.showOpenDialog({
canSelectFolders: true,
canSelectFiles: false,
canSelectMany: false,
openLabel: 'Select scenarios directory',
title: 'Select scenarios directory',
});
if (directories === undefined || directories.length === 0) {
void window.showErrorMessage('No scenarios directory selected.');
return undefined;
}
// Unfortunately, we cannot save the directory in the configuration because that requires
// the configuration to be registered. If we do that, it would be visible to all users; there
// is no "when" clause that would allow us to only show it to users who have enabled the feature flag.
return directories[0].fsPath;
}
private setupConfigListener(): void {
// The config "changes" from the default at startup, so we need to call onConfigChange() to ensure the server is
// started if required.
this.onConfigChange();
this.config.onDidChangeConfiguration(() => this.onConfigChange());
}
private onConfigChange(): void {
if (this.config.mockServerEnabled && !this.isListening) {
this.startServer();
} else if (!this.config.mockServerEnabled && this.isListening) {
this.stopServer();
}
}
}

View File

@@ -0,0 +1,176 @@
import * as fs from 'fs-extra';
import * as path from 'path';
import { MockedRequest } from 'msw';
import { SetupServerApi } from 'msw/node';
import { IsomorphicResponse } from '@mswjs/interceptors';
import { DisposableObject } from '../pure/disposable-object';
import { GitHubApiRequest, RequestKind } from './gh-api-request';
export class Recorder extends DisposableObject {
private readonly allRequests = new Map<string, MockedRequest>();
private currentRecordedScenario: GitHubApiRequest[] = [];
private _isRecording = false;
constructor(
private readonly server: SetupServerApi,
) {
super();
this.onRequestStart = this.onRequestStart.bind(this);
this.onResponseBypass = this.onResponseBypass.bind(this);
}
public get isRecording(): boolean {
return this._isRecording;
}
public get anyRequestsRecorded(): boolean {
return this.currentRecordedScenario.length > 0;
}
public start(): void {
if (this._isRecording) {
return;
}
this._isRecording = true;
this.clear();
this.server.events.on('request:start', this.onRequestStart);
this.server.events.on('response:bypass', this.onResponseBypass);
}
public stop(): void {
if (!this._isRecording) {
return;
}
this._isRecording = false;
this.server.events.removeListener('request:start', this.onRequestStart);
this.server.events.removeListener('response:bypass', this.onResponseBypass);
}
public clear() {
this.currentRecordedScenario = [];
this.allRequests.clear();
}
public async save(scenariosPath: string, name: string): Promise<string> {
const scenarioDirectory = path.join(scenariosPath, name);
await fs.ensureDir(scenarioDirectory);
for (let i = 0; i < this.currentRecordedScenario.length; i++) {
const request = this.currentRecordedScenario[i];
const fileName = `${i}-${request.request.kind}.json`;
const filePath = path.join(scenarioDirectory, fileName);
await fs.writeFile(filePath, JSON.stringify(request, null, 2));
}
this.stop();
return scenarioDirectory;
}
private onRequestStart(request: MockedRequest): void {
this.allRequests.set(request.id, request);
}
private onResponseBypass(response: IsomorphicResponse, requestId: string): void {
const request = this.allRequests.get(requestId);
this.allRequests.delete(requestId);
if (!request) {
return;
}
if (response.body === undefined) {
return;
}
const gitHubApiRequest = createGitHubApiRequest(request.url.toString(), response.status, response.body);
if (!gitHubApiRequest) {
return;
}
this.currentRecordedScenario.push(gitHubApiRequest);
}
}
function createGitHubApiRequest(url: string, status: number, body: string): GitHubApiRequest | undefined {
if (!url) {
return undefined;
}
if (url.match(/\/repos\/[a-zA-Z0-9-_.]+\/[a-zA-Z0-9-_.]+$/)) {
return {
request: {
kind: RequestKind.GetRepo,
},
response: {
status,
body: JSON.parse(body),
},
};
}
if (url.match(/\/repositories\/\d+\/code-scanning\/codeql\/variant-analyses$/)) {
return {
request: {
kind: RequestKind.SubmitVariantAnalysis,
},
response: {
status,
body: JSON.parse(body),
},
};
}
if (url.match(/\/repositories\/\d+\/code-scanning\/codeql\/variant-analyses\/\d+$/)) {
return {
request: {
kind: RequestKind.GetVariantAnalysis,
},
response: {
status,
body: JSON.parse(body),
},
};
}
const repoTaskMatch = url.match(/\/repositories\/\d+\/code-scanning\/codeql\/variant-analyses\/\d+\/repositories\/(?<repositoryId>\d+)$/);
if (repoTaskMatch?.groups?.repositoryId) {
return {
request: {
kind: RequestKind.GetVariantAnalysisRepo,
repositoryId: parseInt(repoTaskMatch.groups.repositoryId, 10),
},
response: {
status,
body: JSON.parse(body),
},
};
}
// if url is a download URL for a variant analysis result, then it's a get-variant-analysis-repoResult.
const repoDownloadMatch = url.match(/objects-origin\.githubusercontent\.com\/codeql-query-console\/codeql-variant-analysis-repo-tasks\/\d+\/(?<repositoryId>\d+)/);
if (repoDownloadMatch?.groups?.repositoryId) {
return {
request: {
kind: RequestKind.GetVariantAnalysisRepoResult,
repositoryId: parseInt(repoDownloadMatch.groups.repositoryId, 10),
},
response: {
status,
body: body as unknown as ArrayBuffer,
}
};
}
return undefined;
}

View File

@@ -30,3 +30,16 @@ export function getQueryHistoryItemId(item: QueryHistoryInfo): string {
assertNever(item);
}
}
export function getQueryText(item: QueryHistoryInfo): string {
switch (item.t) {
case 'local':
return item.initialInfo.queryText;
case 'remote':
return item.remoteQuery.queryText;
case 'variant-analysis':
return item.variantAnalysis.query.text;
default:
assertNever(item);
}
}

View File

@@ -31,7 +31,7 @@ import { commandRunner } from './commandRunner';
import { ONE_HOUR_IN_MS, TWO_HOURS_IN_MS } from './pure/time';
import { assertNever, getErrorMessage, getErrorStack } from './pure/helpers-pure';
import { CompletedLocalQueryInfo, LocalQueryInfo } from './query-results';
import { getQueryHistoryItemId, QueryHistoryInfo } from './query-history-info';
import { getQueryHistoryItemId, getQueryText, QueryHistoryInfo } from './query-history-info';
import { DatabaseManager } from './databases';
import { registerQueryHistoryScrubber } from './query-history-scrubber';
import { QueryStatus, variantAnalysisStatusToQueryStatus } from './query-status';
@@ -634,8 +634,16 @@ export class QueryHistoryManager extends DisposableObject {
}
});
const variantAnalysisRemovedSubscription = this.variantAnalysisManager.onVariantAnalysisRemoved(async (variantAnalysis) => {
const item = this.treeDataProvider.allHistory.find(i => i.t === 'variant-analysis' && i.variantAnalysis.id === variantAnalysis.id);
if (item) {
await this.removeRemoteQuery(item as RemoteQueryHistoryItem);
}
});
this.push(variantAnalysisAddedSubscription);
this.push(variantAnalysisStatusUpdateSubscription);
this.push(variantAnalysisRemovedSubscription);
}
private registerToRemoteQueriesEvents() {
@@ -687,6 +695,9 @@ export class QueryHistoryManager extends DisposableObject {
if (item.t === 'remote') {
await this.remoteQueriesManager.rehydrateRemoteQuery(item.queryId, item.remoteQuery, item.status);
}
if (item.t === 'variant-analysis') {
await this.variantAnalysisManager.rehydrateVariantAnalysis(item.variantAnalysis, item.status);
}
});
}
@@ -935,6 +946,8 @@ export class QueryHistoryManager extends DisposableObject {
}
} else if (queryHistoryItem.t === 'remote') {
return path.join(this.queryStorageDir, queryHistoryItem.queryId);
} else if (queryHistoryItem.t === 'variant-analysis') {
return this.variantAnalysisManager.getVariantAnalysisStorageLocation(queryHistoryItem.variantAnalysis.id);
}
throw new Error('Unable to get query directory');
@@ -957,6 +970,8 @@ export class QueryHistoryManager extends DisposableObject {
}
} else if (finalSingleItem.t === 'remote') {
externalFilePath = path.join(this.queryStorageDir, finalSingleItem.queryId, 'timestamp');
} else if (finalSingleItem.t === 'variant-analysis') {
externalFilePath = path.join(this.variantAnalysisManager.getVariantAnalysisStorageLocation(finalSingleItem.variantAnalysis.id), 'timestamp');
}
if (externalFilePath) {
@@ -1091,13 +1106,13 @@ export class QueryHistoryManager extends DisposableObject {
const params = new URLSearchParams({
isQuickEval: String(!!(finalSingleItem.t === 'local' && finalSingleItem.initialInfo.quickEvalPosition)),
queryText: encodeURIComponent(await this.getQueryText(finalSingleItem)),
queryText: encodeURIComponent(getQueryText(finalSingleItem)),
});
const queryId = getQueryHistoryItemId(finalSingleItem);
const uri = Uri.parse(
`codeql:${queryId}?${params.toString()}`, true
`codeql:${queryId}.ql?${params.toString()}`, true
);
const doc = await workspace.openTextDocument(uri);
await window.showTextDocument(doc, { preview: false });
@@ -1215,19 +1230,6 @@ export class QueryHistoryManager extends DisposableObject {
await commands.executeCommand('codeQL.copyRepoList', finalSingleItem.queryId);
}
async getQueryText(item: QueryHistoryInfo): Promise<string> {
switch (item.t) {
case 'local':
return item.initialInfo.queryText;
case 'remote':
return item.remoteQuery.queryText;
case 'variant-analysis':
return 'TODO';
default:
assertNever(item);
}
}
async handleExportResults(): Promise<void> {
await commands.executeCommand('codeQL.exportVariantAnalysisResults');
}

View File

@@ -78,6 +78,7 @@ export async function compileAndRunQueryAgainstDatabase(
singletonExternalInputs: templates || {},
outputPath: query.resultsPaths.resultsPath,
queryPath: initialInfo.queryPath,
dilPath: query.dilPath,
logPath,
target,
};

View File

@@ -278,7 +278,7 @@ export async function runRemoteQuery(
const processedVariantAnalysis = processVariantAnalysis(variantAnalysisSubmission, variantAnalysisResponse);
variantAnalysisManager.onVariantAnalysisSubmitted(processedVariantAnalysis);
await variantAnalysisManager.onVariantAnalysisSubmitted(processedVariantAnalysis);
void logger.log(`Variant analysis:\n${JSON.stringify(processedVariantAnalysis, null, 2)}`);

View File

@@ -1,3 +1,5 @@
import * as path from 'path';
import * as ghApiClient from './gh-api/gh-api-client';
import { CancellationToken, commands, EventEmitter, ExtensionContext, window } from 'vscode';
import { DisposableObject } from '../pure/disposable-object';
@@ -23,6 +25,10 @@ import { CodeQLCliServer } from '../cli';
import { getControllerRepo } from './run-remote-query';
import { processUpdatedVariantAnalysis } from './variant-analysis-processor';
import PQueue from 'p-queue';
import { createTimestampFile } from '../helpers';
import { QueryStatus } from '../query-status';
import * as fs from 'fs-extra';
export class VariantAnalysisManager extends DisposableObject implements VariantAnalysisViewManager<VariantAnalysisView> {
private readonly _onVariantAnalysisAdded = this.push(new EventEmitter<VariantAnalysis>());
@@ -30,6 +36,9 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
private readonly _onVariantAnalysisStatusUpdated = this.push(new EventEmitter<VariantAnalysis>());
public readonly onVariantAnalysisStatusUpdated = this._onVariantAnalysisStatusUpdated.event;
private readonly _onVariantAnalysisRemoved = this.push(new EventEmitter<VariantAnalysis>());
public readonly onVariantAnalysisRemoved = this._onVariantAnalysisRemoved.event;
private readonly variantAnalysisMonitor: VariantAnalysisMonitor;
private readonly variantAnalysisResultsManager: VariantAnalysisResultsManager;
private readonly variantAnalyses = new Map<number, VariantAnalysis>();
@@ -40,17 +49,29 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
constructor(
private readonly ctx: ExtensionContext,
cliServer: CodeQLCliServer,
storagePath: string,
private readonly storagePath: string,
logger: Logger,
) {
super();
this.variantAnalysisMonitor = this.push(new VariantAnalysisMonitor(ctx));
this.variantAnalysisMonitor.onVariantAnalysisChange(this.onVariantAnalysisUpdated.bind(this));
this.variantAnalysisResultsManager = this.push(new VariantAnalysisResultsManager(cliServer, storagePath, logger));
this.variantAnalysisResultsManager = this.push(new VariantAnalysisResultsManager(cliServer, logger));
this.variantAnalysisResultsManager.onResultLoaded(this.onRepoResultLoaded.bind(this));
}
public async rehydrateVariantAnalysis(variantAnalysis: VariantAnalysis, status: QueryStatus) {
if (!(await this.variantAnalysisRecordExists(variantAnalysis.id))) {
// In this case, the variant analysis was deleted from disk, most likely because
// it was purged by another workspace.
this._onVariantAnalysisRemoved.fire(variantAnalysis);
} else if (status === QueryStatus.InProgress) {
// In this case, last time we checked, the query was still in progress.
// We need to setup the monitor to check for completion.
await commands.executeCommand('codeQL.monitorVariantAnalysis', variantAnalysis);
}
}
public async showView(variantAnalysisId: number): Promise<void> {
if (!this.views.has(variantAnalysisId)) {
// The view will register itself with the manager, so we don't need to do anything here.
@@ -88,7 +109,12 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
throw new Error(`No variant analysis with id: ${variantAnalysisId}`);
}
await this.variantAnalysisResultsManager.loadResults(variantAnalysisId, repositoryFullName);
await this.variantAnalysisResultsManager.loadResults(variantAnalysisId, this.getVariantAnalysisStorageLocation(variantAnalysisId), repositoryFullName);
}
private async variantAnalysisRecordExists(variantAnalysisId: number): Promise<boolean> {
const filePath = this.getVariantAnalysisStorageLocation(variantAnalysisId);
return await fs.pathExists(filePath);
}
private async onVariantAnalysisUpdated(variantAnalysis: VariantAnalysis | undefined): Promise<void> {
@@ -102,7 +128,9 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
this._onVariantAnalysisStatusUpdated.fire(variantAnalysis);
}
public onVariantAnalysisSubmitted(variantAnalysis: VariantAnalysis): void {
public async onVariantAnalysisSubmitted(variantAnalysis: VariantAnalysis): Promise<void> {
await this.prepareStorageDirectory(variantAnalysis.id);
this._onVariantAnalysisAdded.fire(variantAnalysis);
}
@@ -160,7 +188,7 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
repoState.downloadStatus = VariantAnalysisScannedRepositoryDownloadStatus.InProgress;
await this.onRepoStateUpdated(variantAnalysisSummary.id, repoState);
await this.variantAnalysisResultsManager.download(credentials, variantAnalysisSummary.id, repoTask);
await this.variantAnalysisResultsManager.download(credentials, variantAnalysisSummary.id, repoTask, this.getVariantAnalysisStorageLocation(variantAnalysisSummary.id));
}
repoState.downloadStatus = VariantAnalysisScannedRepositoryDownloadStatus.Succeeded;
@@ -179,6 +207,23 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
return this.queue.pending;
}
public getVariantAnalysisStorageLocation(variantAnalysisId: number): string {
return path.join(
this.storagePath,
`${variantAnalysisId}`
);
}
/**
* Prepares a directory for storing results for a variant analysis.
* This directory contains a timestamp file, which will be
* used by the query history manager to determine when the directory
* should be deleted.
*/
private async prepareStorageDirectory(variantAnalysisId: number): Promise<void> {
await createTimestampFile(this.getVariantAnalysisStorageLocation(variantAnalysisId));
}
public async promptOpenVariantAnalysis() {
const credentials = await Credentials.initialize(this.ctx);
if (!credentials) { throw Error('Error authenticating with GitHub'); }

View File

@@ -39,7 +39,6 @@ export class VariantAnalysisResultsManager extends DisposableObject {
constructor(
private readonly cliServer: CodeQLCliServer,
private readonly storagePath: string,
private readonly logger: Logger,
) {
super();
@@ -50,12 +49,13 @@ export class VariantAnalysisResultsManager extends DisposableObject {
credentials: Credentials,
variantAnalysisId: number,
repoTask: VariantAnalysisRepoTask,
variantAnalysisStoragePath: string,
): Promise<void> {
if (!repoTask.artifact_url) {
throw new Error('Missing artifact URL');
}
const resultDirectory = this.getRepoStorageDirectory(variantAnalysisId, repoTask.repository.full_name);
const resultDirectory = this.getRepoStorageDirectory(variantAnalysisStoragePath, repoTask.repository.full_name);
const result = await ghApiClient.getVariantAnalysisRepoResult(
credentials,
@@ -82,18 +82,20 @@ export class VariantAnalysisResultsManager extends DisposableObject {
public async loadResults(
variantAnalysisId: number,
variantAnalysisStoragePath: string,
repositoryFullName: string
): Promise<VariantAnalysisScannedRepositoryResult> {
const result = this.cachedResults.get(createCacheKey(variantAnalysisId, repositoryFullName));
return result ?? await this.loadResultsIntoMemory(variantAnalysisId, repositoryFullName);
return result ?? await this.loadResultsIntoMemory(variantAnalysisId, variantAnalysisStoragePath, repositoryFullName);
}
private async loadResultsIntoMemory(
variantAnalysisId: number,
variantAnalysisStoragePath: string,
repositoryFullName: string,
): Promise<VariantAnalysisScannedRepositoryResult> {
const result = await this.loadResultsFromStorage(variantAnalysisId, repositoryFullName);
const result = await this.loadResultsFromStorage(variantAnalysisId, variantAnalysisStoragePath, repositoryFullName);
this.cachedResults.set(createCacheKey(variantAnalysisId, repositoryFullName), result);
this._onResultLoaded.fire(result);
return result;
@@ -101,13 +103,14 @@ export class VariantAnalysisResultsManager extends DisposableObject {
private async loadResultsFromStorage(
variantAnalysisId: number,
variantAnalysisStoragePath: string,
repositoryFullName: string,
): Promise<VariantAnalysisScannedRepositoryResult> {
if (!(await this.isVariantAnalysisRepoDownloaded(variantAnalysisId, repositoryFullName))) {
if (!(await this.isVariantAnalysisRepoDownloaded(variantAnalysisStoragePath, repositoryFullName))) {
throw new Error('Variant analysis results not downloaded');
}
const storageDirectory = this.getRepoStorageDirectory(variantAnalysisId, repositoryFullName);
const storageDirectory = this.getRepoStorageDirectory(variantAnalysisStoragePath, repositoryFullName);
const repoTask: VariantAnalysisRepoTask = await fs.readJson(path.join(storageDirectory, VariantAnalysisResultsManager.REPO_TASK_FILENAME));
@@ -144,10 +147,10 @@ export class VariantAnalysisResultsManager extends DisposableObject {
}
private async isVariantAnalysisRepoDownloaded(
variantAnalysisId: number,
variantAnalysisStoragePath: string,
repositoryFullName: string,
): Promise<boolean> {
return await fs.pathExists(this.getRepoStorageDirectory(variantAnalysisId, repositoryFullName));
return await fs.pathExists(this.getRepoStorageDirectory(variantAnalysisStoragePath, repositoryFullName));
}
private async readBqrsResults(filePath: string, fileLinkPrefix: string, sourceLocationPrefix: string): Promise<AnalysisRawResults> {
@@ -165,16 +168,9 @@ export class VariantAnalysisResultsManager extends DisposableObject {
return processedSarif.alerts;
}
private getStorageDirectory(variantAnalysisId: number): string {
public getRepoStorageDirectory(variantAnalysisStoragePath: string, fullName: string): string {
return path.join(
this.storagePath,
`${variantAnalysisId}`
);
}
public getRepoStorageDirectory(variantAnalysisId: number, fullName: string): string {
return path.join(
this.getStorageDirectory(variantAnalysisId),
variantAnalysisStoragePath,
fullName
);
}

View File

@@ -192,13 +192,13 @@ export class QueryEvaluationInfo {
if (await this.hasDil()) {
return this.dilPath;
}
const compiledQuery = path.join(this.querySaveDir, 'compiledQuery.qlo');
const compiledQuery = this.compileQueryPath;
if (!(await fs.pathExists(compiledQuery))) {
if (await cliServer.cliConstraints.supportsNewQueryServer()) {
// This could be from the new query server
// in which case we expect the qlo to be missing so we should ignore it
throw new Error(
`DIL was not found. ${compiledQuery}`
`DIL was not found. Expected location: '${this.dilPath}'`
);
} else {
throw new Error(

View File

@@ -10,6 +10,11 @@ import bodyImage from './images/update-css-variables-body.png';
Welcome to the Storybook for **CodeQL for Visual Studio Code**! This Storybook contains stories for components and pages in the extension.
### Switching themes
To switch between VSCode Dark+ and Light+ themes, use the button in the toolbar. This will not work on this document, so you'll only see
the changes applied to a different story.
### Writing stories
To create new stories, copy an existing story in the `src/stories` directory and modify it to use your component or page. Please note that
@@ -29,7 +34,7 @@ for the WebView UI Toolkit can be found [here](https://microsoft.github.io/vscod
### Updating VSCode CSS variables
The VSCode CSS variables that are injected into the Storybook preview are defined in the `src/stories/vscode-theme.css` file. They need to be
The VSCode CSS variables that are injected into the Storybook preview are defined in the `src/stories/vscode-theme-dark.css` file. They need to be
updated manually if new variables are added to VSCode. It can also be updated if you would like to manually preview a different theme. To update
these variables, follow these steps:
@@ -47,9 +52,11 @@ expand all CSS variables.
<img src={stylesImage} />
7. Copy all variables to the `src/stories/vscode-theme.css` file.
7. Copy all variables to the `src/stories/vscode-theme-dark.css` file.
8. Now, select the `<body>` element which is a direct child of the `<html>` element.
9. This time, you do not need to copy the variables. Instead, copy the styles on the `<body>` element to the `src/stories/vscode-theme.css` file.
9. This time, you do not need to copy the variables. Instead, copy the styles on the `<body>` element to the `src/stories/vscode-theme-dark.css` file.
See the image below for which styles need to be copied.
<img src={bodyImage} />
The same process can also be followed for updating the `src/stories/vscode-theme-light.css` file, but make sure to select the **Light+** theme.

View File

@@ -628,3 +628,10 @@ body {
margin: 0;
padding: 0 20px;
}
/**
* This is used for setting the background on the Storybook preview.
*/
body {
background-color: var(--vscode-editor-background);
}

View File

@@ -0,0 +1,635 @@
/*
* These were copied from VSCode Light+ theme.
*
* To update these, open a webview in VSCode, open the webview developer tools and find the
* iframe hosting the webview. The <html> element will have a style attribute that contains
* the CSS variables. Copy these to this file.
*/
:root {
--vscode-font-family: -apple-system, BlinkMacSystemFont, sans-serif;
--vscode-font-weight: normal;
--vscode-font-size: 13px;
--vscode-editor-font-family: Menlo, Monaco, "Courier New", monospace;
--vscode-editor-font-weight: normal;
--vscode-editor-font-size: 12px;
--vscode-foreground: #616161;
--vscode-disabledForeground: rgba(97, 97, 97, 0.5);
--vscode-errorForeground: #a1260d;
--vscode-descriptionForeground: #717171;
--vscode-icon-foreground: #424242;
--vscode-focusBorder: #0090f1;
--vscode-textSeparator-foreground: rgba(0, 0, 0, 0.18);
--vscode-textLink-foreground: #006ab1;
--vscode-textLink-activeForeground: #006ab1;
--vscode-textPreformat-foreground: #a31515;
--vscode-textBlockQuote-background: rgba(127, 127, 127, 0.1);
--vscode-textBlockQuote-border: rgba(0, 122, 204, 0.5);
--vscode-textCodeBlock-background: rgba(220, 220, 220, 0.4);
--vscode-widget-shadow: rgba(0, 0, 0, 0.16);
--vscode-input-background: #ffffff;
--vscode-input-foreground: #616161;
--vscode-inputOption-activeBorder: rgba(0, 122, 204, 0);
--vscode-inputOption-hoverBackground: rgba(184, 184, 184, 0.31);
--vscode-inputOption-activeBackground: rgba(0, 144, 241, 0.2);
--vscode-inputOption-activeForeground: #000000;
--vscode-input-placeholderForeground: #767676;
--vscode-inputValidation-infoBackground: #d6ecf2;
--vscode-inputValidation-infoBorder: #007acc;
--vscode-inputValidation-warningBackground: #f6f5d2;
--vscode-inputValidation-warningBorder: #b89500;
--vscode-inputValidation-errorBackground: #f2dede;
--vscode-inputValidation-errorBorder: #be1100;
--vscode-dropdown-background: #ffffff;
--vscode-dropdown-border: #cecece;
--vscode-checkbox-background: #ffffff;
--vscode-checkbox-border: #cecece;
--vscode-button-foreground: #ffffff;
--vscode-button-separator: rgba(255, 255, 255, 0.4);
--vscode-button-background: #007acc;
--vscode-button-hoverBackground: #0062a3;
--vscode-button-secondaryForeground: #ffffff;
--vscode-button-secondaryBackground: #5f6a79;
--vscode-button-secondaryHoverBackground: #4c5561;
--vscode-badge-background: #c4c4c4;
--vscode-badge-foreground: #333333;
--vscode-scrollbar-shadow: #dddddd;
--vscode-scrollbarSlider-background: rgba(100, 100, 100, 0.4);
--vscode-scrollbarSlider-hoverBackground: rgba(100, 100, 100, 0.7);
--vscode-scrollbarSlider-activeBackground: rgba(0, 0, 0, 0.6);
--vscode-progressBar-background: #0e70c0;
--vscode-editorError-foreground: #e51400;
--vscode-editorWarning-foreground: #bf8803;
--vscode-editorInfo-foreground: #1a85ff;
--vscode-editorHint-foreground: #6c6c6c;
--vscode-sash-hoverBorder: #0090f1;
--vscode-editor-background: #ffffff;
--vscode-editor-foreground: #000000;
--vscode-editorStickyScroll-background: #ffffff;
--vscode-editorStickyScrollHover-background: #f0f0f0;
--vscode-editorWidget-background: #f3f3f3;
--vscode-editorWidget-foreground: #616161;
--vscode-editorWidget-border: #c8c8c8;
--vscode-quickInput-background: #f3f3f3;
--vscode-quickInput-foreground: #616161;
--vscode-quickInputTitle-background: rgba(0, 0, 0, 0.06);
--vscode-pickerGroup-foreground: #0066bf;
--vscode-pickerGroup-border: #cccedb;
--vscode-keybindingLabel-background: rgba(221, 221, 221, 0.4);
--vscode-keybindingLabel-foreground: #555555;
--vscode-keybindingLabel-border: rgba(204, 204, 204, 0.4);
--vscode-keybindingLabel-bottomBorder: rgba(187, 187, 187, 0.4);
--vscode-editor-selectionBackground: #add6ff;
--vscode-editor-inactiveSelectionBackground: #e5ebf1;
--vscode-editor-selectionHighlightBackground: rgba(173, 214, 255, 0.5);
--vscode-editor-findMatchBackground: #a8ac94;
--vscode-editor-findMatchHighlightBackground: rgba(234, 92, 0, 0.33);
--vscode-editor-findRangeHighlightBackground: rgba(180, 180, 180, 0.3);
--vscode-searchEditor-findMatchBackground: rgba(234, 92, 0, 0.22);
--vscode-editor-hoverHighlightBackground: rgba(173, 214, 255, 0.15);
--vscode-editorHoverWidget-background: #f3f3f3;
--vscode-editorHoverWidget-foreground: #616161;
--vscode-editorHoverWidget-border: #c8c8c8;
--vscode-editorHoverWidget-statusBarBackground: #e7e7e7;
--vscode-editorLink-activeForeground: #0000ff;
--vscode-editorInlayHint-foreground: #333333;
--vscode-editorInlayHint-background: rgba(196, 196, 196, 0.6);
--vscode-editorInlayHint-typeForeground: #333333;
--vscode-editorInlayHint-typeBackground: rgba(196, 196, 196, 0.6);
--vscode-editorInlayHint-parameterForeground: #333333;
--vscode-editorInlayHint-parameterBackground: rgba(196, 196, 196, 0.6);
--vscode-editorLightBulb-foreground: #ddb100;
--vscode-editorLightBulbAutoFix-foreground: #007acc;
--vscode-diffEditor-insertedTextBackground: rgba(156, 204, 44, 0.25);
--vscode-diffEditor-removedTextBackground: rgba(255, 0, 0, 0.2);
--vscode-diffEditor-insertedLineBackground: rgba(155, 185, 85, 0.2);
--vscode-diffEditor-removedLineBackground: rgba(255, 0, 0, 0.2);
--vscode-diffEditor-diagonalFill: rgba(34, 34, 34, 0.2);
--vscode-list-focusOutline: #0090f1;
--vscode-list-focusAndSelectionOutline: #90c2f9;
--vscode-list-activeSelectionBackground: #0060c0;
--vscode-list-activeSelectionForeground: #ffffff;
--vscode-list-activeSelectionIconForeground: #ffffff;
--vscode-list-inactiveSelectionBackground: #e4e6f1;
--vscode-list-hoverBackground: #e8e8e8;
--vscode-list-dropBackground: #d6ebff;
--vscode-list-highlightForeground: #0066bf;
--vscode-list-focusHighlightForeground: #bbe7ff;
--vscode-list-invalidItemForeground: #b89500;
--vscode-list-errorForeground: #b01011;
--vscode-list-warningForeground: #855f00;
--vscode-listFilterWidget-background: #f3f3f3;
--vscode-listFilterWidget-outline: rgba(0, 0, 0, 0);
--vscode-listFilterWidget-noMatchesOutline: #be1100;
--vscode-listFilterWidget-shadow: rgba(0, 0, 0, 0.16);
--vscode-list-filterMatchBackground: rgba(234, 92, 0, 0.33);
--vscode-tree-indentGuidesStroke: #a9a9a9;
--vscode-tree-tableColumnsBorder: rgba(97, 97, 97, 0.13);
--vscode-tree-tableOddRowsBackground: rgba(97, 97, 97, 0.04);
--vscode-list-deemphasizedForeground: #8e8e90;
--vscode-quickInputList-focusForeground: #ffffff;
--vscode-quickInputList-focusIconForeground: #ffffff;
--vscode-quickInputList-focusBackground: #0060c0;
--vscode-menu-foreground: #616161;
--vscode-menu-background: #ffffff;
--vscode-menu-selectionForeground: #ffffff;
--vscode-menu-selectionBackground: #0060c0;
--vscode-menu-separatorBackground: #d4d4d4;
--vscode-toolbar-hoverBackground: rgba(184, 184, 184, 0.31);
--vscode-toolbar-activeBackground: rgba(166, 166, 166, 0.31);
--vscode-editor-snippetTabstopHighlightBackground: rgba(10, 50, 100, 0.2);
--vscode-editor-snippetFinalTabstopHighlightBorder: rgba(10, 50, 100, 0.5);
--vscode-breadcrumb-foreground: rgba(97, 97, 97, 0.8);
--vscode-breadcrumb-background: #ffffff;
--vscode-breadcrumb-focusForeground: #4e4e4e;
--vscode-breadcrumb-activeSelectionForeground: #4e4e4e;
--vscode-breadcrumbPicker-background: #f3f3f3;
--vscode-merge-currentHeaderBackground: rgba(64, 200, 174, 0.5);
--vscode-merge-currentContentBackground: rgba(64, 200, 174, 0.2);
--vscode-merge-incomingHeaderBackground: rgba(64, 166, 255, 0.5);
--vscode-merge-incomingContentBackground: rgba(64, 166, 255, 0.2);
--vscode-merge-commonHeaderBackground: rgba(96, 96, 96, 0.4);
--vscode-merge-commonContentBackground: rgba(96, 96, 96, 0.16);
--vscode-editorOverviewRuler-currentContentForeground: rgba(
64,
200,
174,
0.5
);
--vscode-editorOverviewRuler-incomingContentForeground: rgba(
64,
166,
255,
0.5
);
--vscode-editorOverviewRuler-commonContentForeground: rgba(96, 96, 96, 0.4);
--vscode-editorOverviewRuler-findMatchForeground: rgba(209, 134, 22, 0.49);
--vscode-editorOverviewRuler-selectionHighlightForeground: rgba(
160,
160,
160,
0.8
);
--vscode-minimap-findMatchHighlight: #d18616;
--vscode-minimap-selectionOccurrenceHighlight: #c9c9c9;
--vscode-minimap-selectionHighlight: #add6ff;
--vscode-minimap-errorHighlight: rgba(255, 18, 18, 0.7);
--vscode-minimap-warningHighlight: #bf8803;
--vscode-minimap-foregroundOpacity: #000000;
--vscode-minimapSlider-background: rgba(100, 100, 100, 0.2);
--vscode-minimapSlider-hoverBackground: rgba(100, 100, 100, 0.35);
--vscode-minimapSlider-activeBackground: rgba(0, 0, 0, 0.3);
--vscode-problemsErrorIcon-foreground: #e51400;
--vscode-problemsWarningIcon-foreground: #bf8803;
--vscode-problemsInfoIcon-foreground: #1a85ff;
--vscode-charts-foreground: #616161;
--vscode-charts-lines: rgba(97, 97, 97, 0.5);
--vscode-charts-red: #e51400;
--vscode-charts-blue: #1a85ff;
--vscode-charts-yellow: #bf8803;
--vscode-charts-orange: #d18616;
--vscode-charts-green: #388a34;
--vscode-charts-purple: #652d90;
--vscode-editor-lineHighlightBorder: #eeeeee;
--vscode-editor-rangeHighlightBackground: rgba(253, 255, 0, 0.2);
--vscode-editor-symbolHighlightBackground: rgba(234, 92, 0, 0.33);
--vscode-editorCursor-foreground: #000000;
--vscode-editorWhitespace-foreground: rgba(51, 51, 51, 0.2);
--vscode-editorIndentGuide-background: #d3d3d3;
--vscode-editorIndentGuide-activeBackground: #939393;
--vscode-editorLineNumber-foreground: #237893;
--vscode-editorActiveLineNumber-foreground: #0b216f;
--vscode-editorLineNumber-activeForeground: #0b216f;
--vscode-editorRuler-foreground: #d3d3d3;
--vscode-editorCodeLens-foreground: #919191;
--vscode-editorBracketMatch-background: rgba(0, 100, 0, 0.1);
--vscode-editorBracketMatch-border: #b9b9b9;
--vscode-editorOverviewRuler-border: rgba(127, 127, 127, 0.3);
--vscode-editorGutter-background: #ffffff;
--vscode-editorUnnecessaryCode-opacity: rgba(0, 0, 0, 0.47);
--vscode-editorGhostText-foreground: rgba(0, 0, 0, 0.47);
--vscode-editorOverviewRuler-rangeHighlightForeground: rgba(0, 122, 204, 0.6);
--vscode-editorOverviewRuler-errorForeground: rgba(255, 18, 18, 0.7);
--vscode-editorOverviewRuler-warningForeground: #bf8803;
--vscode-editorOverviewRuler-infoForeground: #1a85ff;
--vscode-editorBracketHighlight-foreground1: #0431fa;
--vscode-editorBracketHighlight-foreground2: #319331;
--vscode-editorBracketHighlight-foreground3: #7b3814;
--vscode-editorBracketHighlight-foreground4: rgba(0, 0, 0, 0);
--vscode-editorBracketHighlight-foreground5: rgba(0, 0, 0, 0);
--vscode-editorBracketHighlight-foreground6: rgba(0, 0, 0, 0);
--vscode-editorBracketHighlight-unexpectedBracket\.foreground: rgba(
255,
18,
18,
0.8
);
--vscode-editorBracketPairGuide-background1: rgba(0, 0, 0, 0);
--vscode-editorBracketPairGuide-background2: rgba(0, 0, 0, 0);
--vscode-editorBracketPairGuide-background3: rgba(0, 0, 0, 0);
--vscode-editorBracketPairGuide-background4: rgba(0, 0, 0, 0);
--vscode-editorBracketPairGuide-background5: rgba(0, 0, 0, 0);
--vscode-editorBracketPairGuide-background6: rgba(0, 0, 0, 0);
--vscode-editorBracketPairGuide-activeBackground1: rgba(0, 0, 0, 0);
--vscode-editorBracketPairGuide-activeBackground2: rgba(0, 0, 0, 0);
--vscode-editorBracketPairGuide-activeBackground3: rgba(0, 0, 0, 0);
--vscode-editorBracketPairGuide-activeBackground4: rgba(0, 0, 0, 0);
--vscode-editorBracketPairGuide-activeBackground5: rgba(0, 0, 0, 0);
--vscode-editorBracketPairGuide-activeBackground6: rgba(0, 0, 0, 0);
--vscode-editorUnicodeHighlight-border: #cea33d;
--vscode-editorUnicodeHighlight-background: rgba(206, 163, 61, 0.08);
--vscode-symbolIcon-arrayForeground: #616161;
--vscode-symbolIcon-booleanForeground: #616161;
--vscode-symbolIcon-classForeground: #d67e00;
--vscode-symbolIcon-colorForeground: #616161;
--vscode-symbolIcon-constantForeground: #616161;
--vscode-symbolIcon-constructorForeground: #652d90;
--vscode-symbolIcon-enumeratorForeground: #d67e00;
--vscode-symbolIcon-enumeratorMemberForeground: #007acc;
--vscode-symbolIcon-eventForeground: #d67e00;
--vscode-symbolIcon-fieldForeground: #007acc;
--vscode-symbolIcon-fileForeground: #616161;
--vscode-symbolIcon-folderForeground: #616161;
--vscode-symbolIcon-functionForeground: #652d90;
--vscode-symbolIcon-interfaceForeground: #007acc;
--vscode-symbolIcon-keyForeground: #616161;
--vscode-symbolIcon-keywordForeground: #616161;
--vscode-symbolIcon-methodForeground: #652d90;
--vscode-symbolIcon-moduleForeground: #616161;
--vscode-symbolIcon-namespaceForeground: #616161;
--vscode-symbolIcon-nullForeground: #616161;
--vscode-symbolIcon-numberForeground: #616161;
--vscode-symbolIcon-objectForeground: #616161;
--vscode-symbolIcon-operatorForeground: #616161;
--vscode-symbolIcon-packageForeground: #616161;
--vscode-symbolIcon-propertyForeground: #616161;
--vscode-symbolIcon-referenceForeground: #616161;
--vscode-symbolIcon-snippetForeground: #616161;
--vscode-symbolIcon-stringForeground: #616161;
--vscode-symbolIcon-structForeground: #616161;
--vscode-symbolIcon-textForeground: #616161;
--vscode-symbolIcon-typeParameterForeground: #616161;
--vscode-symbolIcon-unitForeground: #616161;
--vscode-symbolIcon-variableForeground: #007acc;
--vscode-editorHoverWidget-highlightForeground: #0066bf;
--vscode-editorOverviewRuler-bracketMatchForeground: #a0a0a0;
--vscode-editor-foldBackground: rgba(173, 214, 255, 0.3);
--vscode-editorGutter-foldingControlForeground: #424242;
--vscode-editor-linkedEditingBackground: rgba(255, 0, 0, 0.3);
--vscode-editor-wordHighlightBackground: rgba(87, 87, 87, 0.25);
--vscode-editor-wordHighlightStrongBackground: rgba(14, 99, 156, 0.25);
--vscode-editorOverviewRuler-wordHighlightForeground: rgba(
160,
160,
160,
0.8
);
--vscode-editorOverviewRuler-wordHighlightStrongForeground: rgba(
192,
160,
192,
0.8
);
--vscode-peekViewTitle-background: rgba(26, 133, 255, 0.1);
--vscode-peekViewTitleLabel-foreground: #000000;
--vscode-peekViewTitleDescription-foreground: #616161;
--vscode-peekView-border: #1a85ff;
--vscode-peekViewResult-background: #f3f3f3;
--vscode-peekViewResult-lineForeground: #646465;
--vscode-peekViewResult-fileForeground: #1e1e1e;
--vscode-peekViewResult-selectionBackground: rgba(51, 153, 255, 0.2);
--vscode-peekViewResult-selectionForeground: #6c6c6c;
--vscode-peekViewEditor-background: #f2f8fc;
--vscode-peekViewEditorGutter-background: #f2f8fc;
--vscode-peekViewResult-matchHighlightBackground: rgba(234, 92, 0, 0.3);
--vscode-peekViewEditor-matchHighlightBackground: rgba(245, 216, 2, 0.87);
--vscode-editorMarkerNavigationError-background: #e51400;
--vscode-editorMarkerNavigationError-headerBackground: rgba(229, 20, 0, 0.1);
--vscode-editorMarkerNavigationWarning-background: #bf8803;
--vscode-editorMarkerNavigationWarning-headerBackground: rgba(
191,
136,
3,
0.1
);
--vscode-editorMarkerNavigationInfo-background: #1a85ff;
--vscode-editorMarkerNavigationInfo-headerBackground: rgba(26, 133, 255, 0.1);
--vscode-editorMarkerNavigation-background: #ffffff;
--vscode-editorSuggestWidget-background: #f3f3f3;
--vscode-editorSuggestWidget-border: #c8c8c8;
--vscode-editorSuggestWidget-foreground: #000000;
--vscode-editorSuggestWidget-selectedForeground: #ffffff;
--vscode-editorSuggestWidget-selectedIconForeground: #ffffff;
--vscode-editorSuggestWidget-selectedBackground: #0060c0;
--vscode-editorSuggestWidget-highlightForeground: #0066bf;
--vscode-editorSuggestWidget-focusHighlightForeground: #bbe7ff;
--vscode-editorSuggestWidgetStatus-foreground: rgba(0, 0, 0, 0.5);
--vscode-tab-activeBackground: #ffffff;
--vscode-tab-unfocusedActiveBackground: #ffffff;
--vscode-tab-inactiveBackground: #ececec;
--vscode-tab-unfocusedInactiveBackground: #ececec;
--vscode-tab-activeForeground: #333333;
--vscode-tab-inactiveForeground: rgba(51, 51, 51, 0.7);
--vscode-tab-unfocusedActiveForeground: rgba(51, 51, 51, 0.7);
--vscode-tab-unfocusedInactiveForeground: rgba(51, 51, 51, 0.35);
--vscode-tab-border: #f3f3f3;
--vscode-tab-lastPinnedBorder: rgba(97, 97, 97, 0.19);
--vscode-tab-activeModifiedBorder: #33aaee;
--vscode-tab-inactiveModifiedBorder: rgba(51, 170, 238, 0.5);
--vscode-tab-unfocusedActiveModifiedBorder: rgba(51, 170, 238, 0.7);
--vscode-tab-unfocusedInactiveModifiedBorder: rgba(51, 170, 238, 0.25);
--vscode-editorPane-background: #ffffff;
--vscode-editorGroupHeader-tabsBackground: #f3f3f3;
--vscode-editorGroupHeader-noTabsBackground: #ffffff;
--vscode-editorGroup-border: #e7e7e7;
--vscode-editorGroup-dropBackground: rgba(38, 119, 203, 0.18);
--vscode-editorGroup-dropIntoPromptForeground: #616161;
--vscode-editorGroup-dropIntoPromptBackground: #f3f3f3;
--vscode-sideBySideEditor-horizontalBorder: #e7e7e7;
--vscode-sideBySideEditor-verticalBorder: #e7e7e7;
--vscode-panel-background: #ffffff;
--vscode-panel-border: rgba(128, 128, 128, 0.35);
--vscode-panelTitle-activeForeground: #424242;
--vscode-panelTitle-inactiveForeground: rgba(66, 66, 66, 0.75);
--vscode-panelTitle-activeBorder: #424242;
--vscode-panelInput-border: #dddddd;
--vscode-panel-dropBorder: #424242;
--vscode-panelSection-dropBackground: rgba(38, 119, 203, 0.18);
--vscode-panelSectionHeader-background: rgba(128, 128, 128, 0.2);
--vscode-panelSection-border: rgba(128, 128, 128, 0.35);
--vscode-banner-background: #004386;
--vscode-banner-foreground: #ffffff;
--vscode-banner-iconForeground: #1a85ff;
--vscode-statusBar-foreground: #ffffff;
--vscode-statusBar-noFolderForeground: #ffffff;
--vscode-statusBar-background: #007acc;
--vscode-statusBar-noFolderBackground: #68217a;
--vscode-statusBar-focusBorder: #ffffff;
--vscode-statusBarItem-activeBackground: rgba(255, 255, 255, 0.18);
--vscode-statusBarItem-focusBorder: #ffffff;
--vscode-statusBarItem-hoverBackground: rgba(255, 255, 255, 0.12);
--vscode-statusBarItem-compactHoverBackground: rgba(255, 255, 255, 0.2);
--vscode-statusBarItem-prominentForeground: #ffffff;
--vscode-statusBarItem-prominentBackground: rgba(0, 0, 0, 0.5);
--vscode-statusBarItem-prominentHoverBackground: rgba(0, 0, 0, 0.3);
--vscode-statusBarItem-errorBackground: #c72e0f;
--vscode-statusBarItem-errorForeground: #ffffff;
--vscode-statusBarItem-warningBackground: #725102;
--vscode-statusBarItem-warningForeground: #ffffff;
--vscode-activityBar-background: #2c2c2c;
--vscode-activityBar-foreground: #ffffff;
--vscode-activityBar-inactiveForeground: rgba(255, 255, 255, 0.4);
--vscode-activityBar-activeBorder: #ffffff;
--vscode-activityBar-dropBorder: #ffffff;
--vscode-activityBarBadge-background: #007acc;
--vscode-activityBarBadge-foreground: #ffffff;
--vscode-activityBarItem-settingsProfilesForeground: rgba(255, 255, 255, 0.4);
--vscode-activityBarItem-settingsProfilesHoverForeground: #ffffff;
--vscode-activityBarItem-settingsProfilesBackground: #4d4d4d;
--vscode-statusBarItem-remoteBackground: #16825d;
--vscode-statusBarItem-remoteForeground: #ffffff;
--vscode-extensionBadge-remoteBackground: #007acc;
--vscode-extensionBadge-remoteForeground: #ffffff;
--vscode-sideBar-background: #f3f3f3;
--vscode-sideBarTitle-foreground: #6f6f6f;
--vscode-sideBar-dropBackground: rgba(38, 119, 203, 0.18);
--vscode-sideBarSectionHeader-background: rgba(0, 0, 0, 0);
--vscode-sideBarSectionHeader-border: rgba(97, 97, 97, 0.19);
--vscode-titleBar-activeForeground: #333333;
--vscode-titleBar-inactiveForeground: rgba(51, 51, 51, 0.6);
--vscode-titleBar-activeBackground: #dddddd;
--vscode-titleBar-inactiveBackground: rgba(221, 221, 221, 0.6);
--vscode-menubar-selectionForeground: #333333;
--vscode-menubar-selectionBackground: rgba(184, 184, 184, 0.31);
--vscode-notifications-foreground: #616161;
--vscode-notifications-background: #f3f3f3;
--vscode-notificationLink-foreground: #006ab1;
--vscode-notificationCenterHeader-background: #e7e7e7;
--vscode-notifications-border: #e7e7e7;
--vscode-notificationsErrorIcon-foreground: #e51400;
--vscode-notificationsWarningIcon-foreground: #bf8803;
--vscode-notificationsInfoIcon-foreground: #1a85ff;
--vscode-commandCenter-foreground: #333333;
--vscode-commandCenter-activeForeground: #333333;
--vscode-commandCenter-activeBackground: rgba(184, 184, 184, 0.31);
--vscode-commandCenter-border: rgba(128, 128, 128, 0.35);
--vscode-editorCommentsWidget-resolvedBorder: rgba(97, 97, 97, 0.5);
--vscode-editorCommentsWidget-unresolvedBorder: #1a85ff;
--vscode-editorCommentsWidget-rangeBackground: rgba(26, 133, 255, 0.1);
--vscode-editorCommentsWidget-rangeBorder: rgba(26, 133, 255, 0.4);
--vscode-editorCommentsWidget-rangeActiveBackground: rgba(26, 133, 255, 0.1);
--vscode-editorCommentsWidget-rangeActiveBorder: rgba(26, 133, 255, 0.4);
--vscode-editorGutter-commentRangeForeground: #d5d8e9;
--vscode-debugToolBar-background: #f3f3f3;
--vscode-debugIcon-startForeground: #388a34;
--vscode-editor-stackFrameHighlightBackground: rgba(255, 255, 102, 0.45);
--vscode-editor-focusedStackFrameHighlightBackground: rgba(
206,
231,
206,
0.45
);
--vscode-mergeEditor-change\.background: rgba(155, 185, 85, 0.2);
--vscode-mergeEditor-change\.word\.background: rgba(156, 204, 44, 0.4);
--vscode-mergeEditor-conflict\.unhandledUnfocused\.border: rgba(
255,
166,
0,
0.48
);
--vscode-mergeEditor-conflict\.unhandledFocused\.border: #ffa600;
--vscode-mergeEditor-conflict\.handledUnfocused\.border: rgba(
134,
134,
134,
0.29
);
--vscode-mergeEditor-conflict\.handledFocused\.border: rgba(
193,
193,
193,
0.8
);
--vscode-mergeEditor-conflict\.handled\.minimapOverViewRuler: rgba(
173,
172,
168,
0.93
);
--vscode-mergeEditor-conflict\.unhandled\.minimapOverViewRuler: #fcba03;
--vscode-mergeEditor-conflictingLines\.background: rgba(255, 234, 0, 0.28);
--vscode-settings-headerForeground: #444444;
--vscode-settings-modifiedItemIndicator: #66afe0;
--vscode-settings-headerBorder: rgba(128, 128, 128, 0.35);
--vscode-settings-sashBorder: rgba(128, 128, 128, 0.35);
--vscode-settings-dropdownBackground: #ffffff;
--vscode-settings-dropdownBorder: #cecece;
--vscode-settings-dropdownListBorder: #c8c8c8;
--vscode-settings-checkboxBackground: #ffffff;
--vscode-settings-checkboxBorder: #cecece;
--vscode-settings-textInputBackground: #ffffff;
--vscode-settings-textInputForeground: #616161;
--vscode-settings-textInputBorder: #cecece;
--vscode-settings-numberInputBackground: #ffffff;
--vscode-settings-numberInputForeground: #616161;
--vscode-settings-numberInputBorder: #cecece;
--vscode-settings-focusedRowBackground: rgba(232, 232, 232, 0.6);
--vscode-settings-rowHoverBackground: rgba(232, 232, 232, 0.3);
--vscode-settings-focusedRowBorder: rgba(0, 0, 0, 0.12);
--vscode-terminal-foreground: #333333;
--vscode-terminal-selectionBackground: #add6ff;
--vscode-terminal-inactiveSelectionBackground: #e5ebf1;
--vscode-terminalCommandDecoration-defaultBackground: rgba(0, 0, 0, 0.25);
--vscode-terminalCommandDecoration-successBackground: #2090d3;
--vscode-terminalCommandDecoration-errorBackground: #e51400;
--vscode-terminalOverviewRuler-cursorForeground: rgba(160, 160, 160, 0.8);
--vscode-terminal-border: rgba(128, 128, 128, 0.35);
--vscode-terminal-findMatchBackground: #a8ac94;
--vscode-terminal-findMatchHighlightBackground: rgba(234, 92, 0, 0.33);
--vscode-terminalOverviewRuler-findMatchForeground: rgba(209, 134, 22, 0.49);
--vscode-terminal-dropBackground: rgba(38, 119, 203, 0.18);
--vscode-testing-iconFailed: #f14c4c;
--vscode-testing-iconErrored: #f14c4c;
--vscode-testing-iconPassed: #73c991;
--vscode-testing-runAction: #73c991;
--vscode-testing-iconQueued: #cca700;
--vscode-testing-iconUnset: #848484;
--vscode-testing-iconSkipped: #848484;
--vscode-testing-peekBorder: #e51400;
--vscode-testing-peekHeaderBackground: rgba(229, 20, 0, 0.1);
--vscode-testing-message\.error\.decorationForeground: #e51400;
--vscode-testing-message\.error\.lineBackground: rgba(255, 0, 0, 0.2);
--vscode-testing-message\.info\.decorationForeground: rgba(0, 0, 0, 0.5);
--vscode-welcomePage-tileBackground: #f3f3f3;
--vscode-welcomePage-tileHoverBackground: #dbdbdb;
--vscode-welcomePage-tileShadow: rgba(0, 0, 0, 0.16);
--vscode-welcomePage-progress\.background: #ffffff;
--vscode-welcomePage-progress\.foreground: #006ab1;
--vscode-debugExceptionWidget-border: #a31515;
--vscode-debugExceptionWidget-background: #f1dfde;
--vscode-ports-iconRunningProcessForeground: #369432;
--vscode-statusBar-debuggingBackground: #cc6633;
--vscode-statusBar-debuggingForeground: #ffffff;
--vscode-editor-inlineValuesForeground: rgba(0, 0, 0, 0.5);
--vscode-editor-inlineValuesBackground: rgba(255, 200, 0, 0.2);
--vscode-editorGutter-modifiedBackground: #2090d3;
--vscode-editorGutter-addedBackground: #48985d;
--vscode-editorGutter-deletedBackground: #e51400;
--vscode-minimapGutter-modifiedBackground: #2090d3;
--vscode-minimapGutter-addedBackground: #48985d;
--vscode-minimapGutter-deletedBackground: #e51400;
--vscode-editorOverviewRuler-modifiedForeground: rgba(32, 144, 211, 0.6);
--vscode-editorOverviewRuler-addedForeground: rgba(72, 152, 93, 0.6);
--vscode-editorOverviewRuler-deletedForeground: rgba(229, 20, 0, 0.6);
--vscode-debugIcon-breakpointForeground: #e51400;
--vscode-debugIcon-breakpointDisabledForeground: #848484;
--vscode-debugIcon-breakpointUnverifiedForeground: #848484;
--vscode-debugIcon-breakpointCurrentStackframeForeground: #be8700;
--vscode-debugIcon-breakpointStackframeForeground: #89d185;
--vscode-notebook-cellBorderColor: #e8e8e8;
--vscode-notebook-focusedEditorBorder: #0090f1;
--vscode-notebookStatusSuccessIcon-foreground: #388a34;
--vscode-notebookStatusErrorIcon-foreground: #a1260d;
--vscode-notebookStatusRunningIcon-foreground: #616161;
--vscode-notebook-cellToolbarSeparator: rgba(128, 128, 128, 0.35);
--vscode-notebook-selectedCellBackground: rgba(200, 221, 241, 0.31);
--vscode-notebook-selectedCellBorder: #e8e8e8;
--vscode-notebook-focusedCellBorder: #0090f1;
--vscode-notebook-inactiveFocusedCellBorder: #e8e8e8;
--vscode-notebook-cellStatusBarItemHoverBackground: rgba(0, 0, 0, 0.08);
--vscode-notebook-cellInsertionIndicator: #0090f1;
--vscode-notebookScrollbarSlider-background: rgba(100, 100, 100, 0.4);
--vscode-notebookScrollbarSlider-hoverBackground: rgba(100, 100, 100, 0.7);
--vscode-notebookScrollbarSlider-activeBackground: rgba(0, 0, 0, 0.6);
--vscode-notebook-symbolHighlightBackground: rgba(253, 255, 0, 0.2);
--vscode-notebook-cellEditorBackground: #f3f3f3;
--vscode-notebook-editorBackground: #ffffff;
--vscode-keybindingTable-headerBackground: rgba(97, 97, 97, 0.04);
--vscode-keybindingTable-rowsBackground: rgba(97, 97, 97, 0.04);
--vscode-scm-providerBorder: #c8c8c8;
--vscode-searchEditor-textInputBorder: #cecece;
--vscode-debugTokenExpression-name: #9b46b0;
--vscode-debugTokenExpression-value: rgba(108, 108, 108, 0.8);
--vscode-debugTokenExpression-string: #a31515;
--vscode-debugTokenExpression-boolean: #0000ff;
--vscode-debugTokenExpression-number: #098658;
--vscode-debugTokenExpression-error: #e51400;
--vscode-debugView-exceptionLabelForeground: #ffffff;
--vscode-debugView-exceptionLabelBackground: #a31515;
--vscode-debugView-stateLabelForeground: #616161;
--vscode-debugView-stateLabelBackground: rgba(136, 136, 136, 0.27);
--vscode-debugView-valueChangedHighlight: #569cd6;
--vscode-debugConsole-infoForeground: #1a85ff;
--vscode-debugConsole-warningForeground: #bf8803;
--vscode-debugConsole-errorForeground: #a1260d;
--vscode-debugConsole-sourceForeground: #616161;
--vscode-debugConsoleInputIcon-foreground: #616161;
--vscode-debugIcon-pauseForeground: #007acc;
--vscode-debugIcon-stopForeground: #a1260d;
--vscode-debugIcon-disconnectForeground: #a1260d;
--vscode-debugIcon-restartForeground: #388a34;
--vscode-debugIcon-stepOverForeground: #007acc;
--vscode-debugIcon-stepIntoForeground: #007acc;
--vscode-debugIcon-stepOutForeground: #007acc;
--vscode-debugIcon-continueForeground: #007acc;
--vscode-debugIcon-stepBackForeground: #007acc;
--vscode-extensionButton-prominentBackground: #007acc;
--vscode-extensionButton-prominentForeground: #ffffff;
--vscode-extensionButton-prominentHoverBackground: #0062a3;
--vscode-extensionIcon-starForeground: #df6100;
--vscode-extensionIcon-verifiedForeground: #006ab1;
--vscode-extensionIcon-preReleaseForeground: #1d9271;
--vscode-extensionIcon-sponsorForeground: #b51e78;
--vscode-terminal-ansiBlack: #000000;
--vscode-terminal-ansiRed: #cd3131;
--vscode-terminal-ansiGreen: #00bc00;
--vscode-terminal-ansiYellow: #949800;
--vscode-terminal-ansiBlue: #0451a5;
--vscode-terminal-ansiMagenta: #bc05bc;
--vscode-terminal-ansiCyan: #0598bc;
--vscode-terminal-ansiWhite: #555555;
--vscode-terminal-ansiBrightBlack: #666666;
--vscode-terminal-ansiBrightRed: #cd3131;
--vscode-terminal-ansiBrightGreen: #14ce14;
--vscode-terminal-ansiBrightYellow: #b5ba00;
--vscode-terminal-ansiBrightBlue: #0451a5;
--vscode-terminal-ansiBrightMagenta: #bc05bc;
--vscode-terminal-ansiBrightCyan: #0598bc;
--vscode-terminal-ansiBrightWhite: #a5a5a5;
--vscode-interactive-activeCodeBorder: #1a85ff;
--vscode-interactive-inactiveCodeBorder: #e4e6f1;
--vscode-gitDecoration-addedResourceForeground: #587c0c;
--vscode-gitDecoration-modifiedResourceForeground: #895503;
--vscode-gitDecoration-deletedResourceForeground: #ad0707;
--vscode-gitDecoration-renamedResourceForeground: #007100;
--vscode-gitDecoration-untrackedResourceForeground: #007100;
--vscode-gitDecoration-ignoredResourceForeground: #8e8e90;
--vscode-gitDecoration-stageModifiedResourceForeground: #895503;
--vscode-gitDecoration-stageDeletedResourceForeground: #ad0707;
--vscode-gitDecoration-conflictingResourceForeground: #ad0707;
--vscode-gitDecoration-submoduleResourceForeground: #1258a7;
--vscode-issues-newIssueDecoration: rgba(0, 0, 0, 0.28);
--vscode-issues-open: #22863a;
--vscode-issues-closed: #cb2431;
--vscode-pullRequests-notification: #1a85ff;
--vscode-testExplorer-errorDecorationBackground: #f2dede;
}
/**
* This is copied in the same way, but from the <body> element
*/
body {
background-color: transparent;
color: var(--vscode-editor-foreground);
font-family: var(--vscode-font-family);
font-weight: var(--vscode-font-weight);
font-size: var(--vscode-font-size);
margin: 0;
padding: 0 20px;
}
/**
* This is used for setting the background on the Storybook preview.
*/
body {
background-color: var(--vscode-editor-background);
}

View File

@@ -15,7 +15,9 @@ import { faker } from '@faker-js/faker';
import * as ghApiClient from '../../../remote-queries/gh-api/gh-api-client';
import { VariantAnalysisRepoTask } from '../../../remote-queries/gh-api/variant-analysis';
describe(VariantAnalysisResultsManager.name, () => {
describe(VariantAnalysisResultsManager.name, function() {
this.timeout(10000);
let sandbox: sinon.SinonSandbox;
let cli: CodeQLCliServer;
let variantAnalysisId: number;
@@ -33,7 +35,7 @@ describe(VariantAnalysisResultsManager.name, () => {
try {
const extension = await extensions.getExtension<CodeQLExtensionInterface | Record<string, never>>('GitHub.vscode-codeql')!.activate();
cli = extension.cliServer;
variantAnalysisResultsManager = new VariantAnalysisResultsManager(cli, storagePath, logger);
variantAnalysisResultsManager = new VariantAnalysisResultsManager(cli, logger);
} catch (e) {
fail(e as Error);
}
@@ -45,12 +47,17 @@ describe(VariantAnalysisResultsManager.name, () => {
describe('download', () => {
let getOctokitStub: sinon.SinonStub;
let variantAnalysisStoragePath: string;
const mockCredentials = {
getOctokit: () => Promise.resolve({
request: getOctokitStub
})
} as unknown as Credentials;
beforeEach(async () => {
variantAnalysisStoragePath = path.join(storagePath, variantAnalysisId.toString());
});
describe('when the artifact_url is missing', async () => {
it('should not try to download the result', async () => {
const dummyRepoTask = createMockVariantAnalysisRepoTask();
@@ -60,7 +67,8 @@ describe(VariantAnalysisResultsManager.name, () => {
await variantAnalysisResultsManager.download(
mockCredentials,
variantAnalysisId,
dummyRepoTask
dummyRepoTask,
variantAnalysisStoragePath
);
expect.fail('Expected an error to be thrown');
@@ -78,7 +86,7 @@ describe(VariantAnalysisResultsManager.name, () => {
beforeEach(async () => {
dummyRepoTask = createMockVariantAnalysisRepoTask();
storageDirectory = variantAnalysisResultsManager.getRepoStorageDirectory(variantAnalysisId, dummyRepoTask.repository.full_name);
storageDirectory = variantAnalysisResultsManager.getRepoStorageDirectory(variantAnalysisStoragePath, dummyRepoTask.repository.full_name);
const sourceFilePath = path.join(__dirname, '../../../../src/vscode-tests/cli-integration/data/variant-analysis-results.zip');
arrayBuffer = fs.readFileSync(sourceFilePath).buffer;
@@ -97,7 +105,8 @@ describe(VariantAnalysisResultsManager.name, () => {
await variantAnalysisResultsManager.download(
mockCredentials,
variantAnalysisId,
dummyRepoTask
dummyRepoTask,
variantAnalysisStoragePath
);
expect(getVariantAnalysisRepoResultStub.calledOnce).to.be.true;
@@ -107,7 +116,8 @@ describe(VariantAnalysisResultsManager.name, () => {
await variantAnalysisResultsManager.download(
mockCredentials,
variantAnalysisId,
dummyRepoTask
dummyRepoTask,
variantAnalysisStoragePath
);
expect(fs.existsSync(`${storageDirectory}/results.zip`)).to.be.true;
@@ -117,7 +127,8 @@ describe(VariantAnalysisResultsManager.name, () => {
await variantAnalysisResultsManager.download(
mockCredentials,
variantAnalysisId,
dummyRepoTask
dummyRepoTask,
variantAnalysisStoragePath
);
expect(fs.existsSync(`${storageDirectory}/results/results.sarif`)).to.be.true;

View File

@@ -68,7 +68,8 @@ describe('query-history', () => {
variantAnalysisManagerStub = {
onVariantAnalysisAdded: sandbox.stub(),
onVariantAnalysisStatusUpdated: sandbox.stub()
onVariantAnalysisStatusUpdated: sandbox.stub(),
onVariantAnalysisRemoved: sandbox.stub()
} as any as VariantAnalysisManager;
});

View File

@@ -78,7 +78,8 @@ describe('Remote queries and query history manager', function() {
variantAnalysisManagerStub = {
onVariantAnalysisAdded: sandbox.stub(),
onVariantAnalysisStatusUpdated: sandbox.stub()
onVariantAnalysisStatusUpdated: sandbox.stub(),
onVariantAnalysisRemoved: sandbox.stub()
} as any as VariantAnalysisManager;
});

View File

@@ -1,78 +1,83 @@
import { expect } from 'chai';
import { QueryStatus } from '../../src/query-status';
import { getQueryHistoryItemId, getRawQueryName } from '../../src/query-history-info';
import { getQueryHistoryItemId, getQueryText, getRawQueryName } from '../../src/query-history-info';
import { VariantAnalysisHistoryItem } from '../../src/remote-queries/variant-analysis-history-item';
import { createMockVariantAnalysis } from '../../src/vscode-tests/factories/remote-queries/shared/variant-analysis';
import { createMockLocalQueryInfo } from '../../src/vscode-tests/factories/local-queries/local-query-history-item';
import { createMockRemoteQueryHistoryItem } from '../../src/vscode-tests/factories/remote-queries/remote-query-history-item';
describe('Query history info', () => {
const date = new Date('2022-01-01T00:00:00.000Z');
const dateStr = date.toLocaleString();
const localQueryHistoryItem = createMockLocalQueryInfo(dateStr);
const remoteQueryHistoryItem = createMockRemoteQueryHistoryItem({});
const variantAnalysisHistoryItem: VariantAnalysisHistoryItem = {
t: 'variant-analysis',
status: QueryStatus.InProgress,
completed: false,
historyItemId: 'abc123',
variantAnalysis: createMockVariantAnalysis()
};
describe('getRawQueryName', () => {
it('should get the name for local history items', () => {
const date = new Date('2022-01-01T00:00:00.000Z');
const dateStr = date.toLocaleString();
const queryName = getRawQueryName(localQueryHistoryItem);
const queryHistoryItem = createMockLocalQueryInfo(dateStr);
const queryName = getRawQueryName(queryHistoryItem);
expect(queryName).to.equal(queryHistoryItem.getQueryName());
expect(queryName).to.equal(localQueryHistoryItem.getQueryName());
});
it('should get the name for remote query history items', () => {
const queryHistoryItem = createMockRemoteQueryHistoryItem({});
const queryName = getRawQueryName(queryHistoryItem);
const queryName = getRawQueryName(remoteQueryHistoryItem);
expect(queryName).to.equal(queryHistoryItem.remoteQuery.queryName);
expect(queryName).to.equal(remoteQueryHistoryItem.remoteQuery.queryName);
});
it('should get the name for variant analysis history items', () => {
const queryHistoryItem: VariantAnalysisHistoryItem = {
t: 'variant-analysis',
status: QueryStatus.InProgress,
completed: false,
historyItemId: 'abc123',
variantAnalysis: createMockVariantAnalysis()
};
const queryName = getRawQueryName(variantAnalysisHistoryItem);
const queryName = getRawQueryName(queryHistoryItem);
expect(queryName).to.equal(queryHistoryItem.variantAnalysis.query.name);
expect(queryName).to.equal(variantAnalysisHistoryItem.variantAnalysis.query.name);
});
});
describe('getQueryHistoryItemId', () => {
it('should get the ID for local history items', () => {
const date = new Date('2022-01-01T00:00:00.000Z');
const dateStr = date.toLocaleString();
const historyItemId = getQueryHistoryItemId(localQueryHistoryItem);
const queryHistoryItem = createMockLocalQueryInfo(dateStr);
const historyItemId = getQueryHistoryItemId(queryHistoryItem);
expect(historyItemId).to.equal(queryHistoryItem.initialInfo.id);
expect(historyItemId).to.equal(localQueryHistoryItem.initialInfo.id);
});
it('should get the ID for remote query history items', () => {
const queryHistoryItem = createMockRemoteQueryHistoryItem({});
const historyItemId = getQueryHistoryItemId(queryHistoryItem);
const historyItemId = getQueryHistoryItemId(remoteQueryHistoryItem);
expect(historyItemId).to.equal(queryHistoryItem.queryId);
expect(historyItemId).to.equal(remoteQueryHistoryItem.queryId);
});
it('should get the ID for variant analysis history items', () => {
const queryHistoryItem: VariantAnalysisHistoryItem = {
t: 'variant-analysis',
status: QueryStatus.InProgress,
completed: false,
historyItemId: 'abc123',
variantAnalysis: createMockVariantAnalysis()
};
const historyItemId = getQueryHistoryItemId(variantAnalysisHistoryItem);
const historyItemId = getQueryHistoryItemId(queryHistoryItem);
expect(historyItemId).to.equal(variantAnalysisHistoryItem.historyItemId);
});
});
expect(historyItemId).to.equal(queryHistoryItem.historyItemId);
describe('getQueryText', () => {
it('should get the query text for local history items', () => {
const queryText = getQueryText(localQueryHistoryItem);
expect(queryText).to.equal(localQueryHistoryItem.initialInfo.queryText);
});
it('should get the query text for remote query history items', () => {
const queryText = getQueryText(remoteQueryHistoryItem);
expect(queryText).to.equal(remoteQueryHistoryItem.remoteQuery.queryText);
});
it('should get the query text for variant analysis history items', () => {
const queryText = getQueryText(variantAnalysisHistoryItem);
expect(queryText).to.equal(variantAnalysisHistoryItem.variantAnalysis.query.text);
});
});
});