Merge branch 'main' into mob/consume-update-event
This commit is contained in:
5
.github/workflows/main.yml
vendored
5
.github/workflows/main.yml
vendored
@@ -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'
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
19
extensions/ql-vscode/.storybook/tsconfig.json
Normal file
19
extensions/ql-vscode/.storybook/tsconfig.json
Normal 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"]
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -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 />,
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
export function config(entry = []) {
|
||||
return [...entry, require.resolve("./preview.ts")];
|
||||
}
|
||||
|
||||
export function managerEntries(entry = []) {
|
||||
return [...entry, require.resolve("./manager.tsx")];
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { withTheme } from './withTheme';
|
||||
import { VSCodeTheme } from './theme';
|
||||
|
||||
export const decorators = [withTheme];
|
||||
|
||||
export const globals = {
|
||||
vscodeTheme: VSCodeTheme.Dark,
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
export enum VSCodeTheme {
|
||||
Dark = 'dark',
|
||||
Light = 'light',
|
||||
}
|
||||
|
||||
export const themeNames: { [key in VSCodeTheme]: string } = {
|
||||
[VSCodeTheme.Dark]: 'Dark+',
|
||||
[VSCodeTheme.Light]: 'Light+',
|
||||
}
|
||||
@@ -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();
|
||||
};
|
||||
2547
extensions/ql-vscode/package-lock.json
generated
2547
extensions/ql-vscode/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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",
|
||||
|
||||
79
extensions/ql-vscode/scripts/lint-scenarios.ts
Normal file
79
extensions/ql-vscode/scripts/lint-scenarios.ts
Normal 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);
|
||||
});
|
||||
@@ -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>();
|
||||
}
|
||||
|
||||
@@ -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.');
|
||||
|
||||
@@ -54,7 +54,7 @@ export class QueryInProgress {
|
||||
}
|
||||
|
||||
get compiledQueryPath() {
|
||||
return path.join(this.querySaveDir, 'compiledQuery.qlo');
|
||||
return this.queryEvalInfo.compileQueryPath;
|
||||
}
|
||||
|
||||
|
||||
|
||||
76
extensions/ql-vscode/src/mocks/gh-api-request.ts
Normal file
76
extensions/ql-vscode/src/mocks/gh-api-request.ts
Normal 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;
|
||||
173
extensions/ql-vscode/src/mocks/mock-gh-api-server.ts
Normal file
173
extensions/ql-vscode/src/mocks/mock-gh-api-server.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
176
extensions/ql-vscode/src/mocks/recorder.ts
Normal file
176
extensions/ql-vscode/src/mocks/recorder.ts
Normal 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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ export async function compileAndRunQueryAgainstDatabase(
|
||||
singletonExternalInputs: templates || {},
|
||||
outputPath: query.resultsPaths.resultsPath,
|
||||
queryPath: initialInfo.queryPath,
|
||||
dilPath: query.dilPath,
|
||||
logPath,
|
||||
target,
|
||||
};
|
||||
|
||||
@@ -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)}`);
|
||||
|
||||
|
||||
@@ -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'); }
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
635
extensions/ql-vscode/src/stories/vscode-theme-light.css
Normal file
635
extensions/ql-vscode/src/stories/vscode-theme-light.css
Normal 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);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -68,7 +68,8 @@ describe('query-history', () => {
|
||||
|
||||
variantAnalysisManagerStub = {
|
||||
onVariantAnalysisAdded: sandbox.stub(),
|
||||
onVariantAnalysisStatusUpdated: sandbox.stub()
|
||||
onVariantAnalysisStatusUpdated: sandbox.stub(),
|
||||
onVariantAnalysisRemoved: sandbox.stub()
|
||||
} as any as VariantAnalysisManager;
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user