Compare commits

...

31 Commits

Author SHA1 Message Date
Dave Bartolomeo
d4673d9ca0 Merge pull request #1493 from dbartol/v1.16.12
Some checks failed
Release / Release (push) Has been cancelled
Release / Publish to VS Code Marketplace (push) Has been cancelled
Release / Publish to Open VSX Registry (push) Has been cancelled
v1.16.12
2022-09-01 16:38:14 -04:00
Dave Bartolomeo
87f45a7739 v1.16.12 2022-09-01 16:25:04 -04:00
Koen Vlaswinkel
0c89df9a80 Merge pull request #1482 from github/koesie10/bundle-codicons
Bundle Codicons using Webpack
2022-09-01 16:12:55 +02:00
Koen Vlaswinkel
ba8b32078d Simplify and clarify Webpack font config 2022-09-01 11:21:06 +02:00
Koen Vlaswinkel
fa4dd087e5 Remove Codicons references from webview 2022-09-01 09:39:19 +02:00
Dave Bartolomeo
ac74b967b3 Merge pull request #1490 from dbartol/dbartol/log-version/work 2022-08-31 21:01:36 -04:00
Dave Bartolomeo
c349c6a048 Fix race condition when generating evaluator log summaries
The original code that logged the human-readable log summary generated the log asynchronously, which was a reasonable choice. When I added support for viewing and scanning logs, I didn't notice that the summary was being generated asynchronously, and wrote my code assuming that the summary was already on disk when I opened it to find where each relation's log started. The effect was that, depending on timing, the evaluation sometimes failed with an error popup complaining about not being able to open the log summary file.

The fix is to _generate_ the log summary synchronously, but continue to _log_ it asynchronously.
2022-08-31 18:17:45 -04:00
Dave Bartolomeo
234b05994c Guard --sourcemap option based on CLI version 2022-08-31 18:08:21 -04:00
Koen Vlaswinkel
af8f0231c0 Merge pull request #1485 from github/koesie10/add-github-download-button
Remove canary requirement for GitHub database download
2022-08-31 16:57:12 +02:00
Edoardo Pirovano
84bd029749 Restart CLI server too when restarting query server 2022-08-31 14:39:44 +01:00
shati-patel
7d2e4b6de4 Bump CLI version to 2.10.4 for integration tests 2022-08-31 13:52:40 +01:00
Koen Vlaswinkel
23a0e03cef Completely remove using credentials in non-canary mode
This does not remove the previously added mechanism of not requesting
credentials, but using them when they are available. I expect this to be
used in the future.
2022-08-31 14:22:17 +02:00
Koen Vlaswinkel
21c5ed01ad Fix typo in getOctokit JSDoc
Co-authored-by: Andrew Eisenberg <aeisenberg@github.com>
2022-08-31 11:48:27 +02:00
Koen Vlaswinkel
d2af550bcc Merge remote-tracking branch 'origin/main' into koesie10/bundle-codicons 2022-08-31 09:51:46 +02:00
Koen Vlaswinkel
cf36a52762 Merge pull request #1478 from github/koesie10/abstract-interface-manager
Add abstract interface manager
2022-08-31 09:48:05 +02:00
Koen Vlaswinkel
ac1a97efa0 Refactor databaseFetcher tests to not use proxyquire 2022-08-30 15:32:08 +02:00
Koen Vlaswinkel
8d5067f622 Update CHANGELOG 2022-08-30 15:09:16 +02:00
Koen Vlaswinkel
fe5f1c417d Remove authentication requirement for download GitHub databases
This makes authentication for download GitHub CodeQL databases optional.
If you are already authenticated, your token will be used. If you are
not authenticated, an anonymous request will be made.

If the canary flag is enabled, you will be prompted for credentials when
downloading a database and you are not yet logged in.
2022-08-30 15:05:15 +02:00
Koen Vlaswinkel
95438bb7e3 Remove canary requirement for GitHub database download 2022-08-30 14:33:48 +02:00
Koen Vlaswinkel
6d7d0ca41a Merge pull request #1477 from github/koesie10/unified-webpack-bundle
Unify the Webpack bundle
2022-08-30 11:29:45 +02:00
Koen Vlaswinkel
3749e17769 Bundle Codicons using Webpack
This will include the Codicons inside the webview bundle, reducing the
number of files that need to be loaded and the resource roots that need
to be included.
2022-08-29 14:31:29 +02:00
Koen Vlaswinkel
ee49fb5070 Merge branch 'koesie10/unified-webpack-bundle' into koesie10/abstract-interface-manager 2022-08-29 14:12:20 +02:00
Koen Vlaswinkel
de6c523bad Merge remote-tracking branch 'origin/main' into koesie10/unified-webpack-bundle 2022-08-29 13:57:23 +02:00
Koen Vlaswinkel
6612c279ae Merge pull request #1479 from github/koesie10/improve-controller-repo-prompt
Improve prompot for controller repo
2022-08-29 09:53:26 +02:00
Koen Vlaswinkel
2dfa0e8b52 Simplify interface manager and types 2022-08-29 09:51:49 +02:00
Koen Vlaswinkel
0197306713 Remove unnecessary top-level package-lock.json 2022-08-29 09:47:24 +02:00
Dave Bartolomeo
269165eaa3 Merge pull request #1476 from github/version/bump-to-v1.6.12
Bump version to v1.6.12
2022-08-26 10:38:05 -04:00
Koen Vlaswinkel
14c736d72e Improve prompot for controller repo
This will improve the prompt for the controller repo by making clear
that the GitHub Actions workflow will be run in the specified repo.
2022-08-26 13:58:11 +02:00
Koen Vlaswinkel
b8898b939c Add abstract interface manager
This will add a new abstract class that implements the creation of the
panel and webview to reduce duplication across the different interface
managers.
2022-08-26 12:34:28 +02:00
Koen Vlaswinkel
45da1e0f1f Unify the Webpack bundle
This will move all webviews into a single Webpack bundle. This will make
it easier to add new webviews since we don't need to add a new bundle,
but just need to add a new directory with an `index.tsx` file.

It also moves the CSS processing to Webpack so that we don't need to
specify the CSS files to use separately, but can simply do so in the
TypeScript files.
2022-08-26 11:15:24 +02:00
dbartol
88c990c6ae Bump version to v1.6.12 2022-08-25 20:46:21 +00:00
68 changed files with 873 additions and 596 deletions

View File

@@ -139,7 +139,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, windows-latest] os: [ubuntu-latest, windows-latest]
version: ['v2.6.3', 'v2.7.6', 'v2.8.5', 'v2.9.4', 'v2.10.3', 'nightly'] version: ['v2.6.3', 'v2.7.6', 'v2.8.5', 'v2.9.4', 'v2.10.4', 'nightly']
env: env:
CLI_VERSION: ${{ matrix.version }} CLI_VERSION: ${{ matrix.version }}
NIGHTLY_URL: ${{ needs.find-nightly.outputs.url }} NIGHTLY_URL: ${{ needs.find-nightly.outputs.url }}

View File

@@ -1,5 +1,11 @@
# CodeQL for Visual Studio Code: Changelog # CodeQL for Visual Studio Code: Changelog
## 1.6.12 - 1 September 2022
- Add ability for users to download databases directly from GitHub. [#1485](https://github.com/github/vscode-codeql/pull/1485)
- Fix a race condition that could cause a failure to open the evaluator log when running a query. [#1490](https://github.com/github/vscode-codeql/pull/1490)
- Fix an error when running a query with an older version of the CodeQL CLI. [#1490](https://github.com/github/vscode-codeql/pull/1490)
## 1.6.11 - 25 August 2022 ## 1.6.11 - 25 August 2022
No user facing changes. No user facing changes.

View File

@@ -1,5 +1,5 @@
import * as gulp from 'gulp'; import * as gulp from 'gulp';
import { compileTypeScript, watchTypeScript, copyViewCss, cleanOutput, watchCss } from './typescript'; import { compileTypeScript, watchTypeScript, cleanOutput } from './typescript';
import { compileTextMateGrammar } from './textmate'; import { compileTextMateGrammar } from './textmate';
import { copyTestData } from './tests'; import { copyTestData } from './tests';
import { compileView, watchView } from './webpack'; import { compileView, watchView } from './webpack';
@@ -10,7 +10,7 @@ export const buildWithoutPackage =
gulp.series( gulp.series(
cleanOutput, cleanOutput,
gulp.parallel( gulp.parallel(
compileTypeScript, compileTextMateGrammar, compileView, copyTestData, copyViewCss compileTypeScript, compileTextMateGrammar, compileView, copyTestData
) )
); );
@@ -23,6 +23,5 @@ export {
copyTestData, copyTestData,
injectAppInsightsKey, injectAppInsightsKey,
compileView, compileView,
watchCss
}; };
export default gulp.series(buildWithoutPackage, injectAppInsightsKey, packageExtension); export default gulp.series(buildWithoutPackage, injectAppInsightsKey, packageExtension);

View File

@@ -39,13 +39,3 @@ export function compileTypeScript() {
export function watchTypeScript() { export function watchTypeScript() {
gulp.watch('src/**/*.ts', compileTypeScript); gulp.watch('src/**/*.ts', compileTypeScript);
} }
export function watchCss() {
gulp.watch('src/**/*.css', copyViewCss);
}
/** Copy CSS files for the results view into the output directory. */
export function copyViewCss() {
return gulp.src('src/**/view/*.css')
.pipe(gulp.dest('out'));
}

View File

@@ -1,12 +1,11 @@
import * as path from 'path'; import * as path from 'path';
import * as webpack from 'webpack'; import * as webpack from 'webpack';
import * as MiniCssExtractPlugin from 'mini-css-extract-plugin';
export const config: webpack.Configuration = { export const config: webpack.Configuration = {
mode: 'development', mode: 'development',
entry: { entry: {
resultsView: './src/view/results.tsx', webview: './src/view/webview.tsx'
compareView: './src/compare/view/Compare.tsx',
remoteQueriesView: './src/remote-queries/view/RemoteQueries.tsx',
}, },
output: { output: {
path: path.resolve(__dirname, '..', 'out'), path: path.resolve(__dirname, '..', 'out'),
@@ -31,9 +30,7 @@ export const config: webpack.Configuration = {
{ {
test: /\.less$/, test: /\.less$/,
use: [ use: [
{ MiniCssExtractPlugin.loader,
loader: 'style-loader'
},
{ {
loader: 'css-loader', loader: 'css-loader',
options: { options: {
@@ -53,17 +50,31 @@ export const config: webpack.Configuration = {
{ {
test: /\.css$/, test: /\.css$/,
use: [ use: [
{ MiniCssExtractPlugin.loader,
loader: 'style-loader'
},
{ {
loader: 'css-loader' loader: 'css-loader'
} }
] ]
},
{
test: /\.(woff(2)?|ttf|eot)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/',
// We need this to make Webpack use the correct path for the fonts.
// Without this, the CSS file will use `url([object Module])`
esModule: false
}
},
],
} }
] ]
}, },
performance: { performance: {
hints: false hints: false
} },
plugins: [new MiniCssExtractPlugin()],
}; };

View File

@@ -1,12 +1,12 @@
{ {
"name": "vscode-codeql", "name": "vscode-codeql",
"version": "1.6.11", "version": "1.6.12",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "vscode-codeql", "name": "vscode-codeql",
"version": "1.6.11", "version": "1.6.12",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@octokit/plugin-retry": "^3.0.9", "@octokit/plugin-retry": "^3.0.9",
@@ -81,6 +81,7 @@
"@types/unzipper": "~0.10.1", "@types/unzipper": "~0.10.1",
"@types/vscode": "^1.59.0", "@types/vscode": "^1.59.0",
"@types/webpack": "^5.28.0", "@types/webpack": "^5.28.0",
"@types/webpack-env": "^1.18.0",
"@types/xml2js": "~0.4.4", "@types/xml2js": "~0.4.4",
"@typescript-eslint/eslint-plugin": "^4.26.0", "@typescript-eslint/eslint-plugin": "^4.26.0",
"@typescript-eslint/parser": "^4.26.0", "@typescript-eslint/parser": "^4.26.0",
@@ -92,6 +93,7 @@
"del": "^6.0.0", "del": "^6.0.0",
"eslint": "~6.8.0", "eslint": "~6.8.0",
"eslint-plugin-react": "~7.19.0", "eslint-plugin-react": "~7.19.0",
"file-loader": "^6.2.0",
"glob": "^7.1.4", "glob": "^7.1.4",
"gulp": "^4.0.2", "gulp": "^4.0.2",
"gulp-replace": "^1.1.3", "gulp-replace": "^1.1.3",
@@ -99,6 +101,7 @@
"gulp-typescript": "^5.0.1", "gulp-typescript": "^5.0.1",
"husky": "~4.3.8", "husky": "~4.3.8",
"lint-staged": "~10.2.2", "lint-staged": "~10.2.2",
"mini-css-extract-plugin": "^2.6.1",
"mocha": "^10.0.0", "mocha": "^10.0.0",
"mocha-sinon": "~2.1.2", "mocha-sinon": "~2.1.2",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
@@ -106,7 +109,6 @@
"proxyquire": "~2.1.3", "proxyquire": "~2.1.3",
"sinon": "~13.0.1", "sinon": "~13.0.1",
"sinon-chai": "~3.5.0", "sinon-chai": "~3.5.0",
"style-loader": "~3.3.1",
"through2": "^4.0.2", "through2": "^4.0.2",
"ts-loader": "^8.1.0", "ts-loader": "^8.1.0",
"ts-node": "^10.7.0", "ts-node": "^10.7.0",
@@ -1880,6 +1882,12 @@
"webpack": "^5" "webpack": "^5"
} }
}, },
"node_modules/@types/webpack-env": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.18.0.tgz",
"integrity": "sha512-56/MAlX5WMsPVbOg7tAxnYvNYMMWr/QJiIp6BxVSW3JJXUVzzOn64qW8TzQyMSqSUFM2+PVI4aUHcHOzIz/1tg==",
"dev": true
},
"node_modules/@types/webpack/node_modules/tapable": { "node_modules/@types/webpack/node_modules/tapable": {
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
@@ -2478,6 +2486,45 @@
"url": "https://github.com/sponsors/epoberezkin" "url": "https://github.com/sponsors/epoberezkin"
} }
}, },
"node_modules/ajv-formats": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
"dev": true,
"dependencies": {
"ajv": "^8.0.0"
},
"peerDependencies": {
"ajv": "^8.0.0"
},
"peerDependenciesMeta": {
"ajv": {
"optional": true
}
}
},
"node_modules/ajv-formats/node_modules/ajv": {
"version": "8.11.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
"integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
"dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ajv-formats/node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true
},
"node_modules/ajv-keywords": { "node_modules/ajv-keywords": {
"version": "3.5.2", "version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
@@ -6205,6 +6252,70 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/file-loader": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz",
"integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==",
"dev": true,
"dependencies": {
"loader-utils": "^2.0.0",
"schema-utils": "^3.0.0"
},
"engines": {
"node": ">= 10.13.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
},
"peerDependencies": {
"webpack": "^4.0.0 || ^5.0.0"
}
},
"node_modules/file-loader/node_modules/json5": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
"dev": true,
"bin": {
"json5": "lib/cli.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/file-loader/node_modules/loader-utils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
"dev": true,
"dependencies": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
},
"engines": {
"node": ">=8.9.0"
}
},
"node_modules/file-loader/node_modules/schema-utils": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
"integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
"dev": true,
"dependencies": {
"@types/json-schema": "^7.0.8",
"ajv": "^6.12.5",
"ajv-keywords": "^3.5.2"
},
"engines": {
"node": ">= 10.13.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
}
},
"node_modules/file-uri-to-path": { "node_modules/file-uri-to-path": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
@@ -9191,6 +9302,78 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/mini-css-extract-plugin": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz",
"integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==",
"dev": true,
"dependencies": {
"schema-utils": "^4.0.0"
},
"engines": {
"node": ">= 12.13.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
},
"peerDependencies": {
"webpack": "^5.0.0"
}
},
"node_modules/mini-css-extract-plugin/node_modules/ajv": {
"version": "8.11.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
"integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
"dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
"integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
"dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.3"
},
"peerDependencies": {
"ajv": "^8.8.2"
}
},
"node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true
},
"node_modules/mini-css-extract-plugin/node_modules/schema-utils": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
"integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==",
"dev": true,
"dependencies": {
"@types/json-schema": "^7.0.9",
"ajv": "^8.8.0",
"ajv-formats": "^2.1.1",
"ajv-keywords": "^5.0.0"
},
"engines": {
"node": ">= 12.13.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
}
},
"node_modules/minimatch": { "node_modules/minimatch": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -11314,6 +11497,15 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/require-main-filename": { "node_modules/require-main-filename": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
@@ -12327,22 +12519,6 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/style-loader": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz",
"integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==",
"dev": true,
"engines": {
"node": ">= 12.13.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
},
"peerDependencies": {
"webpack": "^5.0.0"
}
},
"node_modules/styled-components": { "node_modules/styled-components": {
"version": "5.3.3", "version": "5.3.3",
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.3.tgz", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.3.tgz",
@@ -16142,6 +16318,12 @@
} }
} }
}, },
"@types/webpack-env": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.18.0.tgz",
"integrity": "sha512-56/MAlX5WMsPVbOg7tAxnYvNYMMWr/QJiIp6BxVSW3JJXUVzzOn64qW8TzQyMSqSUFM2+PVI4aUHcHOzIz/1tg==",
"dev": true
},
"@types/xml2js": { "@types/xml2js": {
"version": "0.4.5", "version": "0.4.5",
"resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.5.tgz", "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.5.tgz",
@@ -16589,6 +16771,35 @@
"uri-js": "^4.2.2" "uri-js": "^4.2.2"
} }
}, },
"ajv-formats": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
"dev": true,
"requires": {
"ajv": "^8.0.0"
},
"dependencies": {
"ajv": {
"version": "8.11.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
"integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
}
},
"json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true
}
}
},
"ajv-keywords": { "ajv-keywords": {
"version": "3.5.2", "version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
@@ -19626,6 +19837,46 @@
"flat-cache": "^2.0.1" "flat-cache": "^2.0.1"
} }
}, },
"file-loader": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz",
"integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==",
"dev": true,
"requires": {
"loader-utils": "^2.0.0",
"schema-utils": "^3.0.0"
},
"dependencies": {
"json5": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
"dev": true
},
"loader-utils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
"dev": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"schema-utils": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
"integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.8",
"ajv": "^6.12.5",
"ajv-keywords": "^3.5.2"
}
}
}
},
"file-uri-to-path": { "file-uri-to-path": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
@@ -21980,6 +22231,56 @@
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
"dev": true "dev": true
}, },
"mini-css-extract-plugin": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz",
"integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==",
"dev": true,
"requires": {
"schema-utils": "^4.0.0"
},
"dependencies": {
"ajv": {
"version": "8.11.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
"integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
}
},
"ajv-keywords": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
"integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.3"
}
},
"json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true
},
"schema-utils": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
"integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.9",
"ajv": "^8.8.0",
"ajv-formats": "^2.1.1",
"ajv-keywords": "^5.0.0"
}
}
}
},
"minimatch": { "minimatch": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -23626,6 +23927,12 @@
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
"dev": true "dev": true
}, },
"require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"dev": true
},
"require-main-filename": { "require-main-filename": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
@@ -24439,13 +24746,6 @@
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"dev": true "dev": true
}, },
"style-loader": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz",
"integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==",
"dev": true,
"requires": {}
},
"styled-components": { "styled-components": {
"version": "5.3.3", "version": "5.3.3",
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.3.tgz", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.3.tgz",

View File

@@ -4,7 +4,7 @@
"description": "CodeQL for Visual Studio Code", "description": "CodeQL for Visual Studio Code",
"author": "GitHub", "author": "GitHub",
"private": true, "private": true,
"version": "1.6.11", "version": "1.6.12",
"publisher": "GitHub", "publisher": "GitHub",
"license": "MIT", "license": "MIT",
"icon": "media/VS-marketplace-CodeQL-icon.png", "icon": "media/VS-marketplace-CodeQL-icon.png",
@@ -288,7 +288,7 @@
"default": "", "default": "",
"pattern": "^$|^(?:[a-zA-Z0-9]+-)*[a-zA-Z0-9]+/[a-zA-Z0-9-_]+$", "pattern": "^$|^(?:[a-zA-Z0-9]+-)*[a-zA-Z0-9]+/[a-zA-Z0-9-_]+$",
"patternErrorMessage": "Please enter a valid GitHub repository", "patternErrorMessage": "Please enter a valid GitHub repository",
"markdownDescription": "[For internal use only] The name of the GitHub repository where you can view the progress and results of the \"Run Variant Analysis\" command. The repository should be of the form `<owner>/<repo>`)." "markdownDescription": "[For internal use only] The name of the GitHub repository in which the GitHub Actions workflow is run when using the \"Run Variant Analysis\" command. The repository should be of the form `<owner>/<repo>`)."
} }
} }
}, },
@@ -664,7 +664,7 @@
}, },
{ {
"command": "codeQLDatabases.chooseDatabaseGithub", "command": "codeQLDatabases.chooseDatabaseGithub",
"when": "config.codeQL.canary && view == codeQLDatabases", "when": "view == codeQLDatabases",
"group": "navigation" "group": "navigation"
}, },
{ {
@@ -926,10 +926,6 @@
"command": "codeQL.viewCfg", "command": "codeQL.viewCfg",
"when": "resourceScheme == codeql-zip-archive && config.codeQL.canary" "when": "resourceScheme == codeql-zip-archive && config.codeQL.canary"
}, },
{
"command": "codeQL.chooseDatabaseGithub",
"when": "config.codeQL.canary"
},
{ {
"command": "codeQLDatabases.setCurrentDatabase", "command": "codeQLDatabases.setCurrentDatabase",
"when": "false" "when": "false"
@@ -1175,7 +1171,7 @@
}, },
{ {
"view": "codeQLDatabases", "view": "codeQLDatabases",
"contents": "Add a CodeQL database:\n[From a folder](command:codeQLDatabases.chooseDatabaseFolder)\n[From an archive](command:codeQLDatabases.chooseDatabaseArchive)\n[From a URL (as a zip file)](command:codeQLDatabases.chooseDatabaseInternet)\n[From LGTM](command:codeQLDatabases.chooseDatabaseLgtm)" "contents": "Add a CodeQL database:\n[From a folder](command:codeQLDatabases.chooseDatabaseFolder)\n[From an archive](command:codeQLDatabases.chooseDatabaseArchive)\n[From a URL (as a zip file)](command:codeQLDatabases.chooseDatabaseInternet)\n[From GitHub](command:codeQLDatabases.chooseDatabaseGithub)\n[From LGTM](command:codeQLDatabases.chooseDatabaseLgtm)"
}, },
{ {
"view": "codeQLEvalLogViewer", "view": "codeQLEvalLogViewer",
@@ -1188,7 +1184,6 @@
"watch": "npm-run-all -p watch:*", "watch": "npm-run-all -p watch:*",
"watch:extension": "tsc --watch", "watch:extension": "tsc --watch",
"watch:webpack": "gulp watchView", "watch:webpack": "gulp watchView",
"watch:css": "gulp watchCss",
"test": "mocha --exit -r ts-node/register test/pure-tests/**/*.ts", "test": "mocha --exit -r ts-node/register test/pure-tests/**/*.ts",
"preintegration": "rm -rf ./out/vscode-tests && gulp", "preintegration": "rm -rf ./out/vscode-tests && gulp",
"integration": "node ./out/vscode-tests/run-integration-tests.js no-workspace,minimal-workspace", "integration": "node ./out/vscode-tests/run-integration-tests.js no-workspace,minimal-workspace",
@@ -1199,8 +1194,8 @@
"format-staged": "lint-staged" "format-staged": "lint-staged"
}, },
"dependencies": { "dependencies": {
"@octokit/rest": "^18.5.6",
"@octokit/plugin-retry": "^3.0.9", "@octokit/plugin-retry": "^3.0.9",
"@octokit/rest": "^18.5.6",
"@primer/octicons-react": "^16.3.0", "@primer/octicons-react": "^16.3.0",
"@primer/react": "^35.0.0", "@primer/react": "^35.0.0",
"@vscode/codicons": "^0.0.31", "@vscode/codicons": "^0.0.31",
@@ -1271,6 +1266,7 @@
"@types/unzipper": "~0.10.1", "@types/unzipper": "~0.10.1",
"@types/vscode": "^1.59.0", "@types/vscode": "^1.59.0",
"@types/webpack": "^5.28.0", "@types/webpack": "^5.28.0",
"@types/webpack-env": "^1.18.0",
"@types/xml2js": "~0.4.4", "@types/xml2js": "~0.4.4",
"@typescript-eslint/eslint-plugin": "^4.26.0", "@typescript-eslint/eslint-plugin": "^4.26.0",
"@typescript-eslint/parser": "^4.26.0", "@typescript-eslint/parser": "^4.26.0",
@@ -1282,6 +1278,7 @@
"del": "^6.0.0", "del": "^6.0.0",
"eslint": "~6.8.0", "eslint": "~6.8.0",
"eslint-plugin-react": "~7.19.0", "eslint-plugin-react": "~7.19.0",
"file-loader": "^6.2.0",
"glob": "^7.1.4", "glob": "^7.1.4",
"gulp": "^4.0.2", "gulp": "^4.0.2",
"gulp-replace": "^1.1.3", "gulp-replace": "^1.1.3",
@@ -1289,6 +1286,7 @@
"gulp-typescript": "^5.0.1", "gulp-typescript": "^5.0.1",
"husky": "~4.3.8", "husky": "~4.3.8",
"lint-staged": "~10.2.2", "lint-staged": "~10.2.2",
"mini-css-extract-plugin": "^2.6.1",
"mocha": "^10.0.0", "mocha": "^10.0.0",
"mocha-sinon": "~2.1.2", "mocha-sinon": "~2.1.2",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
@@ -1296,7 +1294,6 @@
"proxyquire": "~2.1.3", "proxyquire": "~2.1.3",
"sinon": "~13.0.1", "sinon": "~13.0.1",
"sinon-chai": "~3.5.0", "sinon-chai": "~3.5.0",
"style-loader": "~3.3.1",
"through2": "^4.0.2", "through2": "^4.0.2",
"ts-loader": "^8.1.0", "ts-loader": "^8.1.0",
"ts-node": "^10.7.0", "ts-node": "^10.7.0",

View File

@@ -0,0 +1,118 @@
import {
WebviewPanel,
ExtensionContext,
window as Window,
ViewColumn,
Uri,
WebviewPanelOptions,
WebviewOptions
} from 'vscode';
import * as path from 'path';
import { DisposableObject } from './pure/disposable-object';
import { tmpDir } from './helpers';
import { getHtmlForWebview, WebviewMessage, WebviewView } from './interface-utils';
export type InterfacePanelConfig = {
viewId: string;
title: string;
viewColumn: ViewColumn;
view: WebviewView;
preserveFocus?: boolean;
additionalOptions?: WebviewPanelOptions & WebviewOptions;
}
export abstract class AbstractInterfaceManager<ToMessage extends WebviewMessage, FromMessage extends WebviewMessage> extends DisposableObject {
protected panel: WebviewPanel | undefined;
protected panelLoaded = false;
protected panelLoadedCallBacks: (() => void)[] = [];
constructor(
protected readonly ctx: ExtensionContext
) {
super();
}
protected get isShowingPanel() {
return !!this.panel;
}
protected getPanel(): WebviewPanel {
if (this.panel == undefined) {
const { ctx } = this;
const config = this.getPanelConfig();
this.panel = Window.createWebviewPanel(
config.viewId,
config.title,
{ viewColumn: ViewColumn.Active, preserveFocus: true },
{
enableScripts: true,
enableFindWidget: true,
retainContextWhenHidden: true,
...config.additionalOptions,
localResourceRoots: [
...(config.additionalOptions?.localResourceRoots ?? []),
Uri.file(tmpDir.name),
Uri.file(path.join(ctx.extensionPath, 'out'))
],
}
);
this.push(
this.panel.onDidDispose(
() => {
this.panel = undefined;
this.panelLoaded = false;
this.onPanelDispose();
},
null,
ctx.subscriptions
)
);
this.panel.webview.html = getHtmlForWebview(
ctx,
this.panel.webview,
config.view,
{
allowInlineStyles: true,
}
);
this.push(
this.panel.webview.onDidReceiveMessage(
async (e) => this.onMessage(e),
undefined,
ctx.subscriptions
)
);
}
return this.panel;
}
protected abstract getPanelConfig(): InterfacePanelConfig;
protected abstract onPanelDispose(): void;
protected abstract onMessage(msg: FromMessage): Promise<void>;
protected waitForPanelLoaded(): Promise<void> {
return new Promise((resolve) => {
if (this.panelLoaded) {
resolve();
} else {
this.panelLoadedCallBacks.push(resolve);
}
});
}
protected onWebViewLoaded(): void {
this.panelLoaded = true;
this.panelLoadedCallBacks.forEach((cb) => cb());
this.panelLoadedCallBacks = [];
}
protected postMessage(msg: ToMessage): Thenable<boolean> {
return this.getPanel().webview.postMessage(msg);
}
}

View File

@@ -76,16 +76,27 @@ export class Credentials {
})); }));
} }
async getOctokit(): Promise<Octokit.Octokit> { /**
* Creates or returns an instance of Octokit.
*
* @param requireAuthentication Whether the Octokit instance needs to be authenticated as user.
* @returns An instance of Octokit.
*/
async getOctokit(requireAuthentication = true): Promise<Octokit.Octokit> {
if (this.octokit) { if (this.octokit) {
return this.octokit; return this.octokit;
} }
this.octokit = await this.createOctokit(true); this.octokit = await this.createOctokit(requireAuthentication);
// octokit shouldn't be undefined, since we've set "createIfNone: true".
// The following block is mainly here to prevent a compiler error.
if (!this.octokit) { if (!this.octokit) {
throw new Error('Did not initialize Octokit.'); if (requireAuthentication) {
throw new Error('Did not initialize Octokit.');
}
// We don't want to set this in this.octokit because that would prevent
// authenticating when requireCredentials is true.
return new Octokit.Octokit({ retry });
} }
return this.octokit; return this.octokit;
} }

View File

@@ -240,7 +240,7 @@ export class CodeQLCliServer implements Disposable {
/** /**
* Restart the server when the current command terminates * Restart the server when the current command terminates
*/ */
private restartCliServer(): void { restartCliServer(): void {
const callback = (): void => { const callback = (): void => {
try { try {
this.killProcessIfRunning(); this.killProcessIfRunning();
@@ -683,7 +683,7 @@ export class CodeQLCliServer implements Disposable {
const subcommandArgs = [ const subcommandArgs = [
'--format=text', '--format=text',
`--end-summary=${endSummaryPath}`, `--end-summary=${endSummaryPath}`,
'--sourcemap', ...(await this.cliConstraints.supportsSourceMap() ? ['--sourcemap'] : []),
inputPath, inputPath,
outputPath outputPath
]; ];
@@ -1322,6 +1322,11 @@ export class CliVersionConstraint {
*/ */
public static CLI_VERSION_WITH_PER_QUERY_EVAL_LOG = new SemVer('2.9.0'); public static CLI_VERSION_WITH_PER_QUERY_EVAL_LOG = new SemVer('2.9.0');
/**
* CLI version that supports the `--sourcemap` option for log generation.
*/
public static CLI_VERSION_WITH_SOURCEMAP = new SemVer('2.10.3');
constructor(private readonly cli: CodeQLCliServer) { constructor(private readonly cli: CodeQLCliServer) {
/**/ /**/
} }
@@ -1389,4 +1394,8 @@ export class CliVersionConstraint {
async supportsPerQueryEvalLog() { async supportsPerQueryEvalLog() {
return this.isVersionAtLeast(CliVersionConstraint.CLI_VERSION_WITH_PER_QUERY_EVAL_LOG); return this.isVersionAtLeast(CliVersionConstraint.CLI_VERSION_WITH_PER_QUERY_EVAL_LOG);
} }
async supportsSourceMap() {
return this.isVersionAtLeast(CliVersionConstraint.CLI_VERSION_WITH_SOURCEMAP);
}
} }

View File

@@ -1,14 +1,8 @@
import { DisposableObject } from '../pure/disposable-object';
import { import {
WebviewPanel,
ExtensionContext, ExtensionContext,
window as Window,
ViewColumn, ViewColumn,
Uri,
} from 'vscode'; } from 'vscode';
import * as path from 'path';
import { tmpDir } from '../helpers';
import { import {
FromCompareViewMessage, FromCompareViewMessage,
ToCompareViewMessage, ToCompareViewMessage,
@@ -17,26 +11,24 @@ import {
import { Logger } from '../logging'; import { Logger } from '../logging';
import { CodeQLCliServer } from '../cli'; import { CodeQLCliServer } from '../cli';
import { DatabaseManager } from '../databases'; import { DatabaseManager } from '../databases';
import { getHtmlForWebview, jumpToLocation } from '../interface-utils'; import { jumpToLocation } from '../interface-utils';
import { transformBqrsResultSet, RawResultSet, BQRSInfo } from '../pure/bqrs-cli-types'; import { transformBqrsResultSet, RawResultSet, BQRSInfo } from '../pure/bqrs-cli-types';
import resultsDiff from './resultsDiff'; import resultsDiff from './resultsDiff';
import { CompletedLocalQueryInfo } from '../query-results'; import { CompletedLocalQueryInfo } from '../query-results';
import { getErrorMessage } from '../pure/helpers-pure'; import { getErrorMessage } from '../pure/helpers-pure';
import { HistoryItemLabelProvider } from '../history-item-label-provider'; import { HistoryItemLabelProvider } from '../history-item-label-provider';
import { AbstractInterfaceManager, InterfacePanelConfig } from '../abstract-interface-manager';
interface ComparePair { interface ComparePair {
from: CompletedLocalQueryInfo; from: CompletedLocalQueryInfo;
to: CompletedLocalQueryInfo; to: CompletedLocalQueryInfo;
} }
export class CompareInterfaceManager extends DisposableObject { export class CompareInterfaceManager extends AbstractInterfaceManager<ToCompareViewMessage, FromCompareViewMessage> {
private comparePair: ComparePair | undefined; private comparePair: ComparePair | undefined;
private panel: WebviewPanel | undefined;
private panelLoaded = false;
private panelLoadedCallBacks: (() => void)[] = [];
constructor( constructor(
private ctx: ExtensionContext, ctx: ExtensionContext,
private databaseManager: DatabaseManager, private databaseManager: DatabaseManager,
private cliServer: CodeQLCliServer, private cliServer: CodeQLCliServer,
private logger: Logger, private logger: Logger,
@@ -45,7 +37,7 @@ export class CompareInterfaceManager extends DisposableObject {
item: CompletedLocalQueryInfo item: CompletedLocalQueryInfo
) => Promise<void> ) => Promise<void>
) { ) {
super(); super(ctx);
} }
async showResults( async showResults(
@@ -103,73 +95,24 @@ export class CompareInterfaceManager extends DisposableObject {
} }
} }
getPanel(): WebviewPanel { protected getPanelConfig(): InterfacePanelConfig {
if (this.panel == undefined) { return {
const { ctx } = this; viewId: 'compareView',
const panel = (this.panel = Window.createWebviewPanel( title: 'Compare CodeQL Query Results',
'compareView', viewColumn: ViewColumn.Active,
'Compare CodeQL Query Results', preserveFocus: true,
{ viewColumn: ViewColumn.Active, preserveFocus: true }, view: 'compare',
{ };
enableScripts: true,
enableFindWidget: true,
retainContextWhenHidden: true,
localResourceRoots: [
Uri.file(tmpDir.name),
Uri.file(path.join(this.ctx.extensionPath, 'out')),
],
}
));
this.push(this.panel.onDidDispose(
() => {
this.panel = undefined;
this.comparePair = undefined;
},
null,
ctx.subscriptions
));
const scriptPathOnDisk = Uri.file(
ctx.asAbsolutePath('out/compareView.js')
);
const stylesheetPathOnDisk = Uri.file(
ctx.asAbsolutePath('out/view/resultsView.css')
);
panel.webview.html = getHtmlForWebview(
panel.webview,
scriptPathOnDisk,
[stylesheetPathOnDisk],
false
);
this.push(panel.webview.onDidReceiveMessage(
async (e) => this.handleMsgFromView(e),
undefined,
ctx.subscriptions
));
}
return this.panel;
} }
private waitForPanelLoaded(): Promise<void> { protected onPanelDispose(): void {
return new Promise((resolve) => { this.comparePair = undefined;
if (this.panelLoaded) {
resolve();
} else {
this.panelLoadedCallBacks.push(resolve);
}
});
} }
private async handleMsgFromView( protected async onMessage(msg: FromCompareViewMessage): Promise<void> {
msg: FromCompareViewMessage
): Promise<void> {
switch (msg.t) { switch (msg.t) {
case 'compareViewLoaded': case 'compareViewLoaded':
this.panelLoaded = true; this.onWebViewLoaded();
this.panelLoadedCallBacks.forEach((cb) => cb());
this.panelLoadedCallBacks = [];
break; break;
case 'changeCompare': case 'changeCompare':
@@ -186,10 +129,6 @@ export class CompareInterfaceManager extends DisposableObject {
} }
} }
private postMessage(msg: ToCompareViewMessage): Thenable<boolean> {
return this.getPanel().webview.postMessage(msg);
}
private async findCommonResultSetNames( private async findCommonResultSetNames(
from: CompletedLocalQueryInfo, from: CompletedLocalQueryInfo,
to: CompletedLocalQueryInfo, to: CompletedLocalQueryInfo,

View File

@@ -1,13 +0,0 @@
module.exports = {
env: {
browser: true
},
extends: [
"plugin:react/recommended"
],
settings: {
react: {
version: 'detect'
}
}
}

View File

@@ -1,18 +0,0 @@
{
"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
},
"exclude": ["node_modules"]
}

View File

@@ -10,6 +10,8 @@ import {
import { CodeQLCliServer } from './cli'; import { CodeQLCliServer } from './cli';
import * as fs from 'fs-extra'; import * as fs from 'fs-extra';
import * as path from 'path'; import * as path from 'path';
import * as Octokit from '@octokit/rest';
import { retry } from '@octokit/plugin-retry';
import { DatabaseManager, DatabaseItem } from './databases'; import { DatabaseManager, DatabaseItem } from './databases';
import { import {
@@ -76,7 +78,7 @@ export async function promptImportInternetDatabase(
export async function promptImportGithubDatabase( export async function promptImportGithubDatabase(
databaseManager: DatabaseManager, databaseManager: DatabaseManager,
storagePath: string, storagePath: string,
credentials: Credentials, credentials: Credentials | undefined,
progress: ProgressCallback, progress: ProgressCallback,
token: CancellationToken, token: CancellationToken,
cli?: CodeQLCliServer cli?: CodeQLCliServer
@@ -99,14 +101,15 @@ export async function promptImportGithubDatabase(
throw new Error(`Invalid GitHub repository: ${githubRepo}`); throw new Error(`Invalid GitHub repository: ${githubRepo}`);
} }
const result = await convertGithubNwoToDatabaseUrl(githubRepo, credentials, progress); const octokit = credentials ? await credentials.getOctokit(true) : new Octokit.Octokit({ retry });
const result = await convertGithubNwoToDatabaseUrl(githubRepo, octokit, progress);
if (!result) { if (!result) {
return; return;
} }
const { databaseUrl, name, owner } = result; const { databaseUrl, name, owner } = result;
const octokit = await credentials.getOctokit();
/** /**
* The 'token' property of the token object returned by `octokit.auth()`. * The 'token' property of the token object returned by `octokit.auth()`.
* The object is undocumented, but looks something like this: * The object is undocumented, but looks something like this:
@@ -118,14 +121,9 @@ export async function promptImportGithubDatabase(
* We only need the actual token string. * We only need the actual token string.
*/ */
const octokitToken = (await octokit.auth() as { token: string })?.token; const octokitToken = (await octokit.auth() as { token: string })?.token;
if (!octokitToken) {
// Just print a generic error message for now. Ideally we could show more debugging info, like the
// octokit object, but that would expose a user token.
throw new Error('Unable to get GitHub token.');
}
const item = await databaseArchiveFetcher( const item = await databaseArchiveFetcher(
databaseUrl, databaseUrl,
{ 'Accept': 'application/zip', 'Authorization': `Bearer ${octokitToken}` }, { 'Accept': 'application/zip', 'Authorization': octokitToken ? `Bearer ${octokitToken}` : '' },
databaseManager, databaseManager,
storagePath, storagePath,
`${owner}/${name}`, `${owner}/${name}`,
@@ -523,7 +521,7 @@ function convertGitHubUrlToNwo(githubUrl: string): string | undefined {
export async function convertGithubNwoToDatabaseUrl( export async function convertGithubNwoToDatabaseUrl(
githubRepo: string, githubRepo: string,
credentials: Credentials, octokit: Octokit.Octokit,
progress: ProgressCallback): Promise<{ progress: ProgressCallback): Promise<{
databaseUrl: string, databaseUrl: string,
owner: string, owner: string,
@@ -533,7 +531,6 @@ export async function convertGithubNwoToDatabaseUrl(
const nwo = convertGitHubUrlToNwo(githubRepo) || githubRepo; const nwo = convertGitHubUrlToNwo(githubRepo) || githubRepo;
const [owner, repo] = nwo.split('/'); const [owner, repo] = nwo.split('/');
const octokit = await credentials.getOctokit();
const response = await octokit.request('GET /repos/:owner/:repo/code-scanning/codeql/databases', { owner, repo }); const response = await octokit.request('GET /repos/:owner/:repo/code-scanning/codeql/databases', { owner, repo });
const languages = response.data.map((db: any) => db.language); const languages = response.data.map((db: any) => db.language);

View File

@@ -40,6 +40,7 @@ import {
import { CancellationToken } from 'vscode'; import { CancellationToken } from 'vscode';
import { asyncFilter, getErrorMessage } from './pure/helpers-pure'; import { asyncFilter, getErrorMessage } from './pure/helpers-pure';
import { Credentials } from './authentication'; import { Credentials } from './authentication';
import { isCanary } from './config';
type ThemableIconPath = { light: string; dark: string } | string; type ThemableIconPath = { light: string; dark: string } | string;
@@ -301,7 +302,7 @@ export class DatabaseUI extends DisposableObject {
progress: ProgressCallback, progress: ProgressCallback,
token: CancellationToken token: CancellationToken
) => { ) => {
const credentials = await this.getCredentials(); const credentials = isCanary() ? await this.getCredentials() : undefined;
await this.handleChooseDatabaseGithub(credentials, progress, token); await this.handleChooseDatabaseGithub(credentials, progress, token);
}, },
{ {
@@ -480,7 +481,7 @@ export class DatabaseUI extends DisposableObject {
}; };
handleChooseDatabaseGithub = async ( handleChooseDatabaseGithub = async (
credentials: Credentials, credentials: Credentials | undefined,
progress: ProgressCallback, progress: ProgressCallback,
token: CancellationToken token: CancellationToken
): Promise<DatabaseItem | undefined> => { ): Promise<DatabaseItem | undefined> => {

View File

@@ -938,6 +938,8 @@ async function activateWithInstalledDistribution(
progress: ProgressCallback, progress: ProgressCallback,
token: CancellationToken token: CancellationToken
) => { ) => {
// We restart the CLI server too, to ensure they are the same version
cliServer.restartCliServer();
await qs.restartQueryServer(progress, token); await qs.restartQueryServer(progress, token);
void showAndLogInformationMessage('CodeQL Query Server restarted.', { void showAndLogInformationMessage('CodeQL Query Server restarted.', {
outputLogger: queryServerLogger, outputLogger: queryServerLogger,
@@ -970,7 +972,7 @@ async function activateWithInstalledDistribution(
progress: ProgressCallback, progress: ProgressCallback,
token: CancellationToken token: CancellationToken
) => { ) => {
const credentials = await Credentials.initialize(ctx); const credentials = isCanary() ? await Credentials.initialize(ctx) : undefined;
await databaseUI.handleChooseDatabaseGithub(credentials, progress, token); await databaseUI.handleChooseDatabaseGithub(credentials, progress, token);
}, },
{ {
@@ -1018,19 +1020,16 @@ async function activateWithInstalledDistribution(
} }
}; };
// The "authenticateToGitHub" command is internal-only.
ctx.subscriptions.push( ctx.subscriptions.push(
commandRunner('codeQL.authenticateToGitHub', async () => { commandRunner('codeQL.authenticateToGitHub', async () => {
if (isCanary()) { /**
/** * Credentials for authenticating to GitHub.
* Credentials for authenticating to GitHub. * These are used when making API calls.
* These are used when making API calls. */
*/ const credentials = await Credentials.initialize(ctx);
const credentials = await Credentials.initialize(ctx); const octokit = await credentials.getOctokit();
const octokit = await credentials.getOctokit(); const userInfo = await octokit.users.getAuthenticated();
const userInfo = await octokit.users.getAuthenticated(); void showAndLogInformationMessage(`Authenticated to GitHub as user: ${userInfo.data.login}`);
void showAndLogInformationMessage(`Authenticated to GitHub as user: ${userInfo.data.login}`);
}
})); }));
ctx.subscriptions.push( ctx.subscriptions.push(

View File

@@ -4,6 +4,7 @@ import {
Uri, Uri,
Location, Location,
Range, Range,
ExtensionContext,
WebviewPanel, WebviewPanel,
Webview, Webview,
workspace, workspace,
@@ -111,16 +112,36 @@ export function tryResolveLocation(
} }
} }
export type WebviewView = 'results' | 'compare' | 'remote-queries';
export interface WebviewMessage {
t: string;
}
/** /**
* Returns HTML to populate the given webview. * Returns HTML to populate the given webview.
* Uses a content security policy that only loads the given script. * Uses a content security policy that only loads the given script.
*/ */
export function getHtmlForWebview( export function getHtmlForWebview(
ctx: ExtensionContext,
webview: Webview, webview: Webview,
scriptUriOnDisk: Uri, view: WebviewView,
stylesheetUrisOnDisk: Uri[], {
allowInlineStyles: boolean allowInlineStyles,
}: {
allowInlineStyles?: boolean;
} = {
allowInlineStyles: false,
}
): string { ): string {
const scriptUriOnDisk = Uri.file(
ctx.asAbsolutePath('out/webview.js')
);
const stylesheetUrisOnDisk = [
Uri.file(ctx.asAbsolutePath('out/webview.css'))
];
// Convert the on-disk URIs into webview URIs. // Convert the on-disk URIs into webview URIs.
const scriptWebviewUri = webview.asWebviewUri(scriptUriOnDisk); const scriptWebviewUri = webview.asWebviewUri(scriptUriOnDisk);
const stylesheetWebviewUris = stylesheetUrisOnDisk.map(stylesheetUriOnDisk => const stylesheetWebviewUris = stylesheetUrisOnDisk.map(stylesheetUriOnDisk =>
@@ -155,7 +176,7 @@ export function getHtmlForWebview(
${stylesheetsHtmlLines.join(` ${os.EOL}`)} ${stylesheetsHtmlLines.join(` ${os.EOL}`)}
</head> </head>
<body> <body>
<div id=root> <div id=root data-view="${view}">
</div> </div>
<script nonce="${nonce}" src="${scriptWebviewUri}"> <script nonce="${nonce}" src="${scriptWebviewUri}">
</script> </script>

View File

@@ -1,6 +1,4 @@
import * as path from 'path';
import * as Sarif from 'sarif'; import * as Sarif from 'sarif';
import { DisposableObject } from './pure/disposable-object';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { import {
Diagnostic, Diagnostic,
@@ -14,7 +12,7 @@ import {
import * as cli from './cli'; import * as cli from './cli';
import { CodeQLCliServer } from './cli'; import { CodeQLCliServer } from './cli';
import { DatabaseEventKind, DatabaseItem, DatabaseManager } from './databases'; import { DatabaseEventKind, DatabaseItem, DatabaseManager } from './databases';
import { showAndLogErrorMessage, tmpDir } from './helpers'; import { showAndLogErrorMessage } from './helpers';
import { assertNever, getErrorMessage, getErrorStack } from './pure/helpers-pure'; import { assertNever, getErrorMessage, getErrorStack } from './pure/helpers-pure';
import { import {
FromResultsViewMsg, FromResultsViewMsg,
@@ -40,13 +38,13 @@ import {
WebviewReveal, WebviewReveal,
fileUriToWebviewUri, fileUriToWebviewUri,
tryResolveLocation, tryResolveLocation,
getHtmlForWebview,
shownLocationDecoration, shownLocationDecoration,
shownLocationLineDecoration, shownLocationLineDecoration,
jumpToLocation, jumpToLocation,
} from './interface-utils'; } from './interface-utils';
import { getDefaultResultSetName, ParsedResultSets } from './pure/interface-types'; import { getDefaultResultSetName, ParsedResultSets } from './pure/interface-types';
import { RawResultSet, transformBqrsResultSet, ResultSetSchema } from './pure/bqrs-cli-types'; import { RawResultSet, transformBqrsResultSet, ResultSetSchema } from './pure/bqrs-cli-types';
import { AbstractInterfaceManager, InterfacePanelConfig } from './abstract-interface-manager';
import { PAGE_SIZE } from './config'; import { PAGE_SIZE } from './config';
import { CompletedLocalQueryInfo } from './query-results'; import { CompletedLocalQueryInfo } from './query-results';
import { HistoryItemLabelProvider } from './history-item-label-provider'; import { HistoryItemLabelProvider } from './history-item-label-provider';
@@ -122,12 +120,9 @@ function numInterpretedPages(interpretation: Interpretation | undefined): number
return Math.ceil(n / pageSize); return Math.ceil(n / pageSize);
} }
export class InterfaceManager extends DisposableObject { export class InterfaceManager extends AbstractInterfaceManager<IntoResultsViewMsg, FromResultsViewMsg> {
private _displayedQuery?: CompletedLocalQueryInfo; private _displayedQuery?: CompletedLocalQueryInfo;
private _interpretation?: Interpretation; private _interpretation?: Interpretation;
private _panel: vscode.WebviewPanel | undefined;
private _panelLoaded = false;
private _panelLoadedCallBacks: (() => void)[] = [];
private readonly _diagnosticCollection = languages.createDiagnosticCollection( private readonly _diagnosticCollection = languages.createDiagnosticCollection(
'codeql-query-results' 'codeql-query-results'
@@ -140,7 +135,7 @@ export class InterfaceManager extends DisposableObject {
public logger: Logger, public logger: Logger,
private labelProvider: HistoryItemLabelProvider private labelProvider: HistoryItemLabelProvider
) { ) {
super(); super(ctx);
this.push(this._diagnosticCollection); this.push(this._diagnosticCollection);
this.push( this.push(
vscode.window.onDidChangeTextEditorSelection( vscode.window.onDidChangeTextEditorSelection(
@@ -165,7 +160,7 @@ export class InterfaceManager extends DisposableObject {
this.databaseManager.onDidChangeDatabaseItem(({ kind }) => { this.databaseManager.onDidChangeDatabaseItem(({ kind }) => {
if (kind === DatabaseEventKind.Remove) { if (kind === DatabaseEventKind.Remove) {
this._diagnosticCollection.clear(); this._diagnosticCollection.clear();
if (this.isShowingPanel()) { if (this.isShowingPanel) {
void this.postMessage({ void this.postMessage({
t: 'untoggleShowProblems' t: 'untoggleShowProblems'
}); });
@@ -179,59 +174,81 @@ export class InterfaceManager extends DisposableObject {
await this.postMessage({ t: 'navigatePath', direction }); await this.postMessage({ t: 'navigatePath', direction });
} }
private isShowingPanel() { protected getPanelConfig(): InterfacePanelConfig {
return !!this._panel; return {
viewId: 'resultsView',
title: 'CodeQL Query Results',
viewColumn: this.chooseColumnForWebview(),
preserveFocus: true,
view: 'results',
};
} }
// Returns the webview panel, creating it if it doesn't already protected onPanelDispose(): void {
// exist. this._displayedQuery = undefined;
getPanel(): vscode.WebviewPanel { }
if (this._panel == undefined) {
const { ctx } = this;
const webViewColumn = this.chooseColumnForWebview();
const panel = (this._panel = Window.createWebviewPanel(
'resultsView', // internal name
'CodeQL Query Results', // user-visible name
{ viewColumn: webViewColumn, preserveFocus: true },
{
enableScripts: true,
enableFindWidget: true,
retainContextWhenHidden: true,
localResourceRoots: [
vscode.Uri.file(tmpDir.name),
vscode.Uri.file(path.join(this.ctx.extensionPath, 'out'))
]
}
));
this.push(this._panel.onDidDispose( protected async onMessage(msg: FromResultsViewMsg): Promise<void> {
() => { try {
this._panel = undefined; switch (msg.t) {
this._displayedQuery = undefined; case 'resultViewLoaded':
this._panelLoaded = false; this.onWebViewLoaded();
}, break;
null, case 'viewSourceFile': {
ctx.subscriptions await jumpToLocation(msg, this.databaseManager, this.logger);
)); break;
const scriptPathOnDisk = vscode.Uri.file( }
ctx.asAbsolutePath('out/resultsView.js') case 'toggleDiagnostics': {
); if (msg.visible) {
const stylesheetPathOnDisk = vscode.Uri.file( const databaseItem = this.databaseManager.findDatabaseItem(
ctx.asAbsolutePath('out/view/resultsView.css') Uri.parse(msg.databaseUri)
); );
panel.webview.html = getHtmlForWebview( if (databaseItem !== undefined) {
panel.webview, await this.showResultsAsDiagnostics(
scriptPathOnDisk, msg.origResultsPaths,
[stylesheetPathOnDisk], msg.metadata,
false databaseItem
); );
this.push(panel.webview.onDidReceiveMessage( }
async (e) => this.handleMsgFromView(e), } else {
undefined, // TODO: Only clear diagnostics on the same database.
ctx.subscriptions this._diagnosticCollection.clear();
)); }
break;
}
case 'changeSort':
await this.changeRawSortState(msg.resultSetName, msg.sortState);
break;
case 'changeInterpretedSort':
await this.changeInterpretedSortState(msg.sortState);
break;
case 'changePage':
if (msg.selectedTable === ALERTS_TABLE_NAME || msg.selectedTable === GRAPH_TABLE_NAME) {
await this.showPageOfInterpretedResults(msg.pageNumber);
}
else {
await this.showPageOfRawResults(
msg.selectedTable,
msg.pageNumber,
// When we are in an unsorted state, we guarantee that
// sortedResultsInfo doesn't have an entry for the current
// result set. Use this to determine whether or not we use
// the sorted bqrs file.
!!this._displayedQuery?.completedQuery.sortedResultsInfo[msg.selectedTable]
);
}
break;
case 'openFile':
await this.openFile(msg.filePath);
break;
default:
assertNever(msg);
}
} catch (e) {
void showAndLogErrorMessage(getErrorMessage(e), {
fullMessage: getErrorStack(e)
});
} }
return this._panel;
} }
/** /**
@@ -296,85 +313,6 @@ export class InterfaceManager extends DisposableObject {
await this.showPageOfRawResults(resultSetName, 0, true); await this.showPageOfRawResults(resultSetName, 0, true);
} }
private async handleMsgFromView(msg: FromResultsViewMsg): Promise<void> {
try {
switch (msg.t) {
case 'viewSourceFile': {
await jumpToLocation(msg, this.databaseManager, this.logger);
break;
}
case 'toggleDiagnostics': {
if (msg.visible) {
const databaseItem = this.databaseManager.findDatabaseItem(
Uri.parse(msg.databaseUri)
);
if (databaseItem !== undefined) {
await this.showResultsAsDiagnostics(
msg.origResultsPaths,
msg.metadata,
databaseItem
);
}
} else {
// TODO: Only clear diagnostics on the same database.
this._diagnosticCollection.clear();
}
break;
}
case 'resultViewLoaded':
this._panelLoaded = true;
this._panelLoadedCallBacks.forEach((cb) => cb());
this._panelLoadedCallBacks = [];
break;
case 'changeSort':
await this.changeRawSortState(msg.resultSetName, msg.sortState);
break;
case 'changeInterpretedSort':
await this.changeInterpretedSortState(msg.sortState);
break;
case 'changePage':
if (msg.selectedTable === ALERTS_TABLE_NAME || msg.selectedTable === GRAPH_TABLE_NAME) {
await this.showPageOfInterpretedResults(msg.pageNumber);
}
else {
await this.showPageOfRawResults(
msg.selectedTable,
msg.pageNumber,
// When we are in an unsorted state, we guarantee that
// sortedResultsInfo doesn't have an entry for the current
// result set. Use this to determine whether or not we use
// the sorted bqrs file.
!!this._displayedQuery?.completedQuery.sortedResultsInfo[msg.selectedTable]
);
}
break;
case 'openFile':
await this.openFile(msg.filePath);
break;
default:
assertNever(msg);
}
} catch (e) {
void showAndLogErrorMessage(getErrorMessage(e), {
fullMessage: getErrorStack(e)
});
}
}
postMessage(msg: IntoResultsViewMsg): Thenable<boolean> {
return this.getPanel().webview.postMessage(msg);
}
private waitForPanelLoaded(): Promise<void> {
return new Promise((resolve) => {
if (this._panelLoaded) {
resolve();
} else {
this._panelLoadedCallBacks.push(resolve);
}
});
}
/** /**
* Show query results in webview panel. * Show query results in webview panel.
* @param fullQuery Evaluation info for the executed query. * @param fullQuery Evaluation info for the executed query.

View File

@@ -1,11 +1,10 @@
import { import {
WebviewPanel,
ExtensionContext, ExtensionContext,
window as Window, window as Window,
ViewColumn, ViewColumn,
Uri, Uri,
workspace, workspace,
commands commands,
} from 'vscode'; } from 'vscode';
import * as path from 'path'; import * as path from 'path';
@@ -16,7 +15,6 @@ import {
RemoteQueryDownloadAllAnalysesResultsMessage RemoteQueryDownloadAllAnalysesResultsMessage
} from '../pure/interface-types'; } from '../pure/interface-types';
import { Logger } from '../logging'; import { Logger } from '../logging';
import { getHtmlForWebview } from '../interface-utils';
import { assertNever } from '../pure/helpers-pure'; import { assertNever } from '../pure/helpers-pure';
import { import {
AnalysisSummary, AnalysisSummary,
@@ -34,18 +32,17 @@ import { SHOW_QUERY_TEXT_MSG } from '../query-history';
import { AnalysesResultsManager } from './analyses-results-manager'; import { AnalysesResultsManager } from './analyses-results-manager';
import { AnalysisResults } from './shared/analysis-result'; import { AnalysisResults } from './shared/analysis-result';
import { humanizeUnit } from '../pure/time'; import { humanizeUnit } from '../pure/time';
import { AbstractInterfaceManager, InterfacePanelConfig } from '../abstract-interface-manager';
export class RemoteQueriesInterfaceManager { export class RemoteQueriesInterfaceManager extends AbstractInterfaceManager<ToRemoteQueriesMessage, FromRemoteQueriesMessage> {
private panel: WebviewPanel | undefined;
private panelLoaded = false;
private currentQueryId: string | undefined; private currentQueryId: string | undefined;
private panelLoadedCallBacks: (() => void)[] = [];
constructor( constructor(
private readonly ctx: ExtensionContext, ctx: ExtensionContext,
private readonly logger: Logger, private readonly logger: Logger,
private readonly analysesResultsManager: AnalysesResultsManager private readonly analysesResultsManager: AnalysesResultsManager
) { ) {
super(ctx);
this.panelLoadedCallBacks.push(() => { this.panelLoadedCallBacks.push(() => {
void logger.log('Variant analysis results view loaded'); void logger.log('Variant analysis results view loaded');
}); });
@@ -103,112 +100,29 @@ export class RemoteQueriesInterfaceManager {
}; };
} }
getPanel(): WebviewPanel { protected getPanelConfig(): InterfacePanelConfig {
if (this.panel == undefined) { return {
const { ctx } = this; viewId: 'remoteQueriesView',
const panel = (this.panel = Window.createWebviewPanel( title: 'CodeQL Query Results',
'remoteQueriesView', viewColumn: ViewColumn.Active,
'CodeQL Query Results', preserveFocus: true,
{ viewColumn: ViewColumn.Active, preserveFocus: true }, view: 'remote-queries',
{ additionalOptions: {
enableScripts: true, localResourceRoots: [
enableFindWidget: true, Uri.file(this.analysesResultsManager.storagePath)
retainContextWhenHidden: true, ]
localResourceRoots: [
Uri.file(this.analysesResultsManager.storagePath),
Uri.file(path.join(this.ctx.extensionPath, 'out')),
Uri.file(path.join(this.ctx.extensionPath, 'node_modules/@vscode/codicons/dist')),
],
}
));
this.panel.onDidDispose(
() => {
this.panel = undefined;
this.currentQueryId = undefined;
this.panelLoaded = false;
},
null,
ctx.subscriptions
);
const scriptPathOnDisk = Uri.file(
ctx.asAbsolutePath('out/remoteQueriesView.js')
);
const baseStylesheetUriOnDisk = Uri.file(
ctx.asAbsolutePath('out/remote-queries/view/baseStyles.css')
);
const stylesheetPathOnDisk = Uri.file(
ctx.asAbsolutePath('out/remote-queries/view/remoteQueries.css')
);
// Allows use of the VS Code "codicons" icon set.
// See https://github.com/microsoft/vscode-codicons
const codiconsPathOnDisk = Uri.file(
ctx.asAbsolutePath('node_modules/@vscode/codicons/dist/codicon.css')
);
panel.webview.html = getHtmlForWebview(
panel.webview,
scriptPathOnDisk,
[baseStylesheetUriOnDisk, stylesheetPathOnDisk, codiconsPathOnDisk],
true
);
ctx.subscriptions.push(
panel.webview.onDidReceiveMessage(
async (e) => this.handleMsgFromView(e),
undefined,
ctx.subscriptions
)
);
}
return this.panel;
}
private waitForPanelLoaded(): Promise<void> {
return new Promise((resolve) => {
if (this.panelLoaded) {
resolve();
} else {
this.panelLoadedCallBacks.push(resolve);
} }
}); };
} }
private async openFile(filePath: string) { protected onPanelDispose(): void {
try { this.currentQueryId = undefined;
const textDocument = await workspace.openTextDocument(filePath);
await Window.showTextDocument(textDocument, ViewColumn.One);
} catch (error) {
void showAndLogWarningMessage(`Could not open file: ${filePath}`);
}
} }
private async openVirtualFile(text: string) { protected async onMessage(msg: FromRemoteQueriesMessage): Promise<void> {
try {
const params = new URLSearchParams({
queryText: encodeURIComponent(SHOW_QUERY_TEXT_MSG + text)
});
const uri = Uri.parse(
`remote-query:query-text.ql?${params.toString()}`,
true
);
const doc = await workspace.openTextDocument(uri);
await Window.showTextDocument(doc, { preview: false });
} catch (error) {
void showAndLogWarningMessage('Could not open query text');
}
}
private async handleMsgFromView(
msg: FromRemoteQueriesMessage
): Promise<void> {
switch (msg.t) { switch (msg.t) {
case 'remoteQueryLoaded': case 'remoteQueryLoaded':
this.panelLoaded = true; this.onWebViewLoaded();
this.panelLoadedCallBacks.forEach((cb) => cb());
this.panelLoadedCallBacks = [];
break; break;
case 'remoteQueryError': case 'remoteQueryError':
void this.logger.log( void this.logger.log(
@@ -238,6 +152,31 @@ export class RemoteQueriesInterfaceManager {
} }
} }
private async openFile(filePath: string) {
try {
const textDocument = await workspace.openTextDocument(filePath);
await Window.showTextDocument(textDocument, ViewColumn.One);
} catch (error) {
void showAndLogWarningMessage(`Could not open file: ${filePath}`);
}
}
private async openVirtualFile(text: string) {
try {
const params = new URLSearchParams({
queryText: encodeURIComponent(SHOW_QUERY_TEXT_MSG + text)
});
const uri = Uri.parse(
`remote-query:query-text.ql?${params.toString()}`,
true
);
const doc = await workspace.openTextDocument(uri);
await Window.showTextDocument(doc, { preview: false });
} catch (error) {
void showAndLogWarningMessage('Could not open query text');
}
}
private async downloadAnalysisResults(msg: RemoteQueryDownloadAnalysisResultsMessage): Promise<void> { private async downloadAnalysisResults(msg: RemoteQueryDownloadAnalysisResultsMessage): Promise<void> {
const queryId = this.currentQueryId; const queryId = this.currentQueryId;
await this.analysesResultsManager.downloadAnalysisResults( await this.analysesResultsManager.downloadAnalysisResults(
@@ -262,10 +201,6 @@ export class RemoteQueriesInterfaceManager {
} }
} }
private postMessage(msg: ToRemoteQueriesMessage): Thenable<boolean> {
return this.getPanel().webview.postMessage(msg);
}
private getDuration(startTime: number, endTime: number): string { private getDuration(startTime: number, endTime: number): string {
const diffInMs = startTime - endTime; const diffInMs = startTime - endTime;
return humanizeUnit(diffInMs); return humanizeUnit(diffInMs);

View File

@@ -75,6 +75,8 @@ export class RemoteQueriesManager extends DisposableObject {
this.onRemoteQueryAdded = this.remoteQueryAddedEventEmitter.event; this.onRemoteQueryAdded = this.remoteQueryAddedEventEmitter.event;
this.onRemoteQueryRemoved = this.remoteQueryRemovedEventEmitter.event; this.onRemoteQueryRemoved = this.remoteQueryRemovedEventEmitter.event;
this.onRemoteQueryStatusUpdate = this.remoteQueryStatusUpdateEventEmitter.event; this.onRemoteQueryStatusUpdate = this.remoteQueryStatusUpdateEventEmitter.event;
this.push(this.interfaceManager);
} }
public async rehydrateRemoteQuery(queryId: string, query: RemoteQuery, status: QueryStatus) { public async rehydrateRemoteQuery(queryId: string, query: RemoteQuery, status: QueryStatus) {

View File

@@ -217,7 +217,7 @@ export async function runRemoteQuery(
if (!controllerRepo || !REPO_REGEX.test(controllerRepo)) { if (!controllerRepo || !REPO_REGEX.test(controllerRepo)) {
void logger.log(controllerRepo ? 'Invalid controller repository name.' : 'No controller repository defined.'); void logger.log(controllerRepo ? 'Invalid controller repository name.' : 'No controller repository defined.');
controllerRepo = await window.showInputBox({ controllerRepo = await window.showInputBox({
title: 'Controller repository in which to display progress and results of variant analysis', title: 'Controller repository in which to run the GitHub Actions workflow for this variant analysis',
placeHolder: '<owner>/<repo>', placeHolder: '<owner>/<repo>',
prompt: 'Enter the name of a GitHub repository in the format <owner>/<repo>', prompt: 'Enter the name of a GitHub repository in the format <owner>/<repo>',
ignoreFocusOut: true, ignoreFocusOut: true,

View File

@@ -1,13 +0,0 @@
module.exports = {
env: {
browser: true
},
extends: [
"plugin:react/recommended"
],
settings: {
react: {
version: 'detect'
}
}
}

View File

@@ -1,18 +0,0 @@
{
"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
},
"exclude": ["node_modules"]
}

View File

@@ -36,7 +36,7 @@ import { compileDatabaseUpgradeSequence, hasNondestructiveUpgradeCapabilities, u
import { ensureMetadataIsComplete } from './query-results'; import { ensureMetadataIsComplete } from './query-results';
import { SELECT_QUERY_NAME } from './contextual/locationFinder'; import { SELECT_QUERY_NAME } from './contextual/locationFinder';
import { DecodedBqrsChunk } from './pure/bqrs-cli-types'; import { DecodedBqrsChunk } from './pure/bqrs-cli-types';
import { getErrorMessage } from './pure/helpers-pure'; import { asError, getErrorMessage } from './pure/helpers-pure';
import { generateSummarySymbolsFile } from './log-insights/summary-parser'; import { generateSummarySymbolsFile } from './log-insights/summary-parser';
/** /**
@@ -207,7 +207,10 @@ export class QueryEvaluationInfo {
logPath: this.evalLogPath, logPath: this.evalLogPath,
}); });
if (await this.hasEvalLog()) { if (await this.hasEvalLog()) {
this.displayHumanReadableLogSummary(queryInfo, qs); queryInfo.evalLogLocation = this.evalLogPath;
queryInfo.evalLogSummaryLocation = await this.generateHumanReadableLogSummary(qs);
void this.logEndSummary(queryInfo.evalLogSummaryLocation, qs); // Logged asynchrnously
if (config.isCanary()) { // Generate JSON summary for viewer. if (config.isCanary()) { // Generate JSON summary for viewer.
await qs.cliServer.generateJsonLogSummary(this.evalLogPath, this.jsonEvalLogSummaryPath); await qs.cliServer.generateJsonLogSummary(this.evalLogPath, this.jsonEvalLogSummaryPath);
queryInfo.jsonEvalLogSummaryLocation = this.jsonEvalLogSummaryPath; queryInfo.jsonEvalLogSummaryLocation = this.jsonEvalLogSummaryPath;
@@ -340,25 +343,40 @@ export class QueryEvaluationInfo {
} }
/** /**
* Calls the appropriate CLI command to generate a human-readable log summary * Calls the appropriate CLI command to generate a human-readable log summary.
* and logs to the Query Server console and query log file. * @param qs The query server client.
* @returns The path to the log summary, or `undefined` if the summary could not be generated.
*/ */
displayHumanReadableLogSummary(queryInfo: LocalQueryInfo, qs: qsClient.QueryServerClient): void { private async generateHumanReadableLogSummary(qs: qsClient.QueryServerClient): Promise<string | undefined> {
queryInfo.evalLogLocation = this.evalLogPath; try {
void qs.cliServer.generateLogSummary(this.evalLogPath, this.evalLogSummaryPath, this.evalLogEndSummaryPath) await qs.cliServer.generateLogSummary(this.evalLogPath, this.evalLogSummaryPath, this.evalLogEndSummaryPath);
.then(() => { return this.evalLogSummaryPath;
queryInfo.evalLogSummaryLocation = this.evalLogSummaryPath;
fs.readFile(this.evalLogEndSummaryPath, (err, buffer) => { } catch (e) {
if (err) { const err = asError(e);
throw new Error(`Could not read structured evaluator log end of summary file at ${this.evalLogEndSummaryPath}.`); void showAndLogWarningMessage(`Failed to generate human-readable structured evaluator log summary. Reason: ${err.message}`);
} return undefined;
void qs.logger.log(' --- Evaluator Log Summary --- ', { additionalLogLocation: this.logPath }); }
void qs.logger.log(buffer.toString(), { additionalLogLocation: this.logPath }); }
});
}) /**
.catch(err => { * Logs the end summary to the Output window and log file.
void showAndLogWarningMessage(`Failed to generate human-readable structured evaluator log summary. Reason: ${err.message}`); * @param logSummaryPath Path to the human-readable log summary
}); * @param qs The query server client.
*/
private async logEndSummary(logSummaryPath: string | undefined, qs: qsClient.QueryServerClient): Promise<void> {
if (logSummaryPath === undefined) {
// Failed to generate the log, so we don't expect an end summary either.
return;
}
try {
const endSummaryContent = await fs.readFile(this.evalLogEndSummaryPath, 'utf-8');
void qs.logger.log(' --- Evaluator Log Summary --- ', { additionalLogLocation: this.logPath });
void qs.logger.log(endSummaryContent, { additionalLogLocation: this.logPath });
} catch (e) {
void showAndLogWarningMessage(`Could not read structured evaluator log end of summary file at ${this.evalLogEndSummaryPath}.`);
}
} }
/** /**

View File

@@ -1,15 +1,16 @@
import * as React from 'react'; import * as React from 'react';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import * as Rdom from 'react-dom';
import { import {
ToCompareViewMessage, ToCompareViewMessage,
SetComparisonsMessage, SetComparisonsMessage,
} from '../../pure/interface-types'; } from '../../pure/interface-types';
import CompareSelector from './CompareSelector'; import CompareSelector from './CompareSelector';
import { vscode } from '../../view/vscode-api'; import { vscode } from '../vscode-api';
import CompareTable from './CompareTable'; import CompareTable from './CompareTable';
import '../results/resultsView.css';
const emptyComparison: SetComparisonsMessage = { const emptyComparison: SetComparisonsMessage = {
t: 'setComparisons', t: 'setComparisons',
stats: {}, stats: {},
@@ -75,10 +76,3 @@ export function Compare(_: Record<string, never>): JSX.Element {
return <div>Error!</div>; return <div>Error!</div>;
} }
} }
Rdom.render(
<Compare />,
document.getElementById('root'),
// Post a message to the extension when fully loaded.
() => vscode.postMessage({ t: 'compareViewLoaded' })
);

View File

@@ -1,11 +1,11 @@
import * as React from 'react'; import * as React from 'react';
import { SetComparisonsMessage } from '../../pure/interface-types'; import { SetComparisonsMessage } from '../../pure/interface-types';
import RawTableHeader from '../../view/RawTableHeader'; import RawTableHeader from '../results/RawTableHeader';
import { className } from '../../view/result-table-utils'; import { className } from '../results/result-table-utils';
import { ResultRow } from '../../pure/bqrs-cli-types'; import { ResultRow } from '../../pure/bqrs-cli-types';
import RawTableRow from '../../view/RawTableRow'; import RawTableRow from '../results/RawTableRow';
import { vscode } from '../../view/vscode-api'; import { vscode } from '../vscode-api';
interface Props { interface Props {
comparison: SetComparisonsMessage; comparison: SetComparisonsMessage;

View File

@@ -0,0 +1,10 @@
import * as React from 'react';
import { WebviewDefinition } from '../webview-interface';
import { Compare } from './Compare';
const definition: WebviewDefinition = {
component: <Compare />,
loadedMessage: 'compareViewLoaded'
};
export default definition;

View File

@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { AnalysisAlert } from '../shared/analysis-result'; import { AnalysisAlert } from '../../remote-queries/shared/analysis-result';
import CodePaths from './CodePaths'; import CodePaths from './CodePaths';
import FileCodeSnippet from './FileCodeSnippet'; import FileCodeSnippet from './FileCodeSnippet';

View File

@@ -4,7 +4,7 @@ import { VSCodeDropdown, VSCodeLink, VSCodeOption, VSCodeTag } from '@vscode/web
import * as React from 'react'; import * as React from 'react';
import { ChangeEvent, useRef, useState } from 'react'; import { ChangeEvent, useRef, useState } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { CodeFlow, AnalysisMessage, ResultSeverity } from '../shared/analysis-result'; import { CodeFlow, AnalysisMessage, ResultSeverity } from '../../remote-queries/shared/analysis-result';
import FileCodeSnippet from './FileCodeSnippet'; import FileCodeSnippet from './FileCodeSnippet';
import SectionTitle from './SectionTitle'; import SectionTitle from './SectionTitle';
import VerticalSpace from './VerticalSpace'; import VerticalSpace from './VerticalSpace';

View File

@@ -1,6 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { CodeSnippet, FileLink, HighlightedRegion, AnalysisMessage, ResultSeverity } from '../shared/analysis-result'; import { CodeSnippet, FileLink, HighlightedRegion, AnalysisMessage, ResultSeverity } from '../../remote-queries/shared/analysis-result';
import VerticalSpace from './VerticalSpace'; import VerticalSpace from './VerticalSpace';
import { createRemoteFileRef } from '../../pure/location-link-utils'; import { createRemoteFileRef } from '../../pure/location-link-utils';
import { parseHighlightedLine, shouldHighlightLine } from '../../pure/sarif-utils'; import { parseHighlightedLine, shouldHighlightLine } from '../../pure/sarif-utils';

View File

@@ -1,18 +1,17 @@
import * as React from 'react'; import * as React from 'react';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import * as Rdom from 'react-dom';
import { Flash, ThemeProvider } from '@primer/react'; import { Flash, ThemeProvider } from '@primer/react';
import { ToRemoteQueriesMessage } from '../../pure/interface-types'; import { ToRemoteQueriesMessage } from '../../pure/interface-types';
import { AnalysisSummary, RemoteQueryResult } from '../shared/remote-query-result'; import { AnalysisSummary, RemoteQueryResult } from '../../remote-queries/shared/remote-query-result';
import { MAX_RAW_RESULTS } from '../shared/result-limits'; import { MAX_RAW_RESULTS } from '../../remote-queries/shared/result-limits';
import { vscode } from '../../view/vscode-api'; import { vscode } from '../vscode-api';
import { VSCodeBadge, VSCodeButton } from '@vscode/webview-ui-toolkit/react'; import { VSCodeBadge, VSCodeButton } from '@vscode/webview-ui-toolkit/react';
import SectionTitle from './SectionTitle'; import SectionTitle from './SectionTitle';
import VerticalSpace from './VerticalSpace'; import VerticalSpace from './VerticalSpace';
import HorizontalSpace from './HorizontalSpace'; import HorizontalSpace from './HorizontalSpace';
import ViewTitle from './ViewTitle'; import ViewTitle from './ViewTitle';
import DownloadButton from './DownloadButton'; import DownloadButton from './DownloadButton';
import { AnalysisResults, getAnalysisResultCount } from '../shared/analysis-result'; import { AnalysisResults, getAnalysisResultCount } from '../../remote-queries/shared/analysis-result';
import DownloadSpinner from './DownloadSpinner'; import DownloadSpinner from './DownloadSpinner';
import CollapsibleItem from './CollapsibleItem'; import CollapsibleItem from './CollapsibleItem';
import { AlertIcon, CodeSquareIcon, FileCodeIcon, RepoIcon, TerminalIcon } from '@primer/octicons-react'; import { AlertIcon, CodeSquareIcon, FileCodeIcon, RepoIcon, TerminalIcon } from '@primer/octicons-react';
@@ -24,6 +23,9 @@ import SortRepoFilter, { Sort, sorter } from './SortRepoFilter';
import LastUpdated from './LastUpdated'; import LastUpdated from './LastUpdated';
import RepoListCopyButton from './RepoListCopyButton'; import RepoListCopyButton from './RepoListCopyButton';
import './baseStyles.css';
import './remoteQueries.css';
const numOfReposInContractedMode = 10; const numOfReposInContractedMode = 10;
const emptyQueryResult: RemoteQueryResult = { const emptyQueryResult: RemoteQueryResult = {
@@ -440,10 +442,3 @@ export function RemoteQueries(): JSX.Element {
return <div>There was an error displaying the view.</div>; return <div>There was an error displaying the view.</div>;
} }
} }
Rdom.render(
<RemoteQueries />,
document.getElementById('root'),
// Post a message to the extension when fully loaded.
() => vscode.postMessage({ t: 'remoteQueryLoaded' })
);

View File

@@ -1,6 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import { vscode } from '../../view/vscode-api'; import { vscode } from '../vscode-api';
import { RemoteQueryResult } from '../shared/remote-query-result'; import { RemoteQueryResult } from '../../remote-queries/shared/remote-query-result';
import { CopyIcon } from '@primer/octicons-react'; import { CopyIcon } from '@primer/octicons-react';
import { IconButton } from '@primer/react'; import { IconButton } from '@primer/react';

View File

@@ -0,0 +1,10 @@
import * as React from 'react';
import { WebviewDefinition } from '../webview-interface';
import { RemoteQueries } from './RemoteQueries';
const definition: WebviewDefinition = {
component: <RemoteQueries />,
loadedMessage: 'remoteQueryLoaded'
};
export default definition;

View File

@@ -1,9 +1,9 @@
import * as React from 'react'; import * as React from 'react';
import { vscode } from './vscode-api'; import { vscode } from '../vscode-api';
import { RawResultsSortState, SortDirection } from '../pure/interface-types'; import { RawResultsSortState, SortDirection } from '../../pure/interface-types';
import { nextSortDirection } from './result-table-utils'; import { nextSortDirection } from './result-table-utils';
import { Column } from '../pure/bqrs-cli-types'; import { Column } from '../../pure/bqrs-cli-types';
interface Props { interface Props {
readonly columns: readonly Column[]; readonly columns: readonly Column[];

View File

@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { ResultRow } from '../pure/bqrs-cli-types'; import { ResultRow } from '../../pure/bqrs-cli-types';
import { zebraStripe } from './result-table-utils'; import { zebraStripe } from './result-table-utils';
import RawTableValue from './RawTableValue'; import RawTableValue from './RawTableValue';

View File

@@ -1,7 +1,7 @@
import * as React from 'react'; import * as React from 'react';
import { renderLocation } from './result-table-utils'; import { renderLocation } from './result-table-utils';
import { CellValue } from '../pure/bqrs-cli-types'; import { CellValue } from '../../pure/bqrs-cli-types';
interface Props { interface Props {
value: CellValue; value: CellValue;

View File

@@ -1,19 +1,19 @@
import * as path from 'path'; import * as path from 'path';
import * as React from 'react'; import * as React from 'react';
import * as Sarif from 'sarif'; import * as Sarif from 'sarif';
import * as Keys from '../pure/result-keys'; import * as Keys from '../../pure/result-keys';
import * as octicons from './octicons'; import * as octicons from './octicons';
import { className, renderLocation, ResultTableProps, zebraStripe, selectableZebraStripe, jumpToLocation, nextSortDirection, emptyQueryResultsMessage } from './result-table-utils'; import { className, renderLocation, ResultTableProps, zebraStripe, selectableZebraStripe, jumpToLocation, nextSortDirection, emptyQueryResultsMessage } from './result-table-utils';
import { onNavigation, NavigationEvent } from './results'; import { onNavigation, NavigationEvent } from './results';
import { InterpretedResultSet, SarifInterpretationData } from '../pure/interface-types'; import { InterpretedResultSet, SarifInterpretationData } from '../../pure/interface-types';
import { import {
parseSarifPlainTextMessage, parseSarifPlainTextMessage,
parseSarifLocation, parseSarifLocation,
isNoLocation isNoLocation
} from '../pure/sarif-utils'; } from '../../pure/sarif-utils';
import { InterpretedResultsSortColumn, SortDirection, InterpretedResultsSortState } from '../pure/interface-types'; import { InterpretedResultsSortColumn, SortDirection, InterpretedResultsSortState } from '../../pure/interface-types';
import { vscode } from './vscode-api'; import { vscode } from '../vscode-api';
import { isWholeFileLoc, isLineColumnLoc } from '../pure/bqrs-utils'; import { isWholeFileLoc, isLineColumnLoc } from '../../pure/bqrs-utils';
export type PathTableProps = ResultTableProps & { resultSet: InterpretedResultSet<SarifInterpretationData> }; export type PathTableProps = ResultTableProps & { resultSet: InterpretedResultSet<SarifInterpretationData> };
export interface PathTableState { export interface PathTableState {

View File

@@ -1,10 +1,10 @@
import * as React from 'react'; import * as React from 'react';
import * as d3 from 'd3'; import * as d3 from 'd3';
import { ResultTableProps } from './result-table-utils'; import { ResultTableProps } from './result-table-utils';
import { InterpretedResultSet, GraphInterpretationData } from '../pure/interface-types'; import { InterpretedResultSet, GraphInterpretationData } from '../../pure/interface-types';
import { graphviz } from 'd3-graphviz'; import { graphviz } from 'd3-graphviz';
import { jumpToLocation } from './result-table-utils'; import { jumpToLocation } from './result-table-utils';
import { tryGetLocationFromString } from '../pure/bqrs-utils'; import { tryGetLocationFromString } from '../../pure/bqrs-utils';
export type GraphProps = ResultTableProps & { resultSet: InterpretedResultSet<GraphInterpretationData> }; export type GraphProps = ResultTableProps & { resultSet: InterpretedResultSet<GraphInterpretationData> };
const graphClassName = 'vscode-codeql__result-tables-graph'; const graphClassName = 'vscode-codeql__result-tables-graph';

View File

@@ -0,0 +1,10 @@
import * as React from 'react';
import { WebviewDefinition } from '../webview-interface';
import { ResultsApp } from './results';
const definition: WebviewDefinition = {
component: <ResultsApp />,
loadedMessage: 'resultViewLoaded'
};
export default definition;

View File

@@ -1,10 +1,10 @@
import * as React from 'react'; import * as React from 'react';
import { ResultTableProps, className, emptyQueryResultsMessage } from './result-table-utils'; import { ResultTableProps, className, emptyQueryResultsMessage } from './result-table-utils';
import { RAW_RESULTS_LIMIT, RawResultsSortState } from '../pure/interface-types'; import { RAW_RESULTS_LIMIT, RawResultsSortState } from '../../pure/interface-types';
import { RawTableResultSet } from '../pure/interface-types'; import { RawTableResultSet } from '../../pure/interface-types';
import RawTableHeader from './RawTableHeader'; import RawTableHeader from './RawTableHeader';
import RawTableRow from './RawTableRow'; import RawTableRow from './RawTableRow';
import { ResultRow } from '../pure/bqrs-cli-types'; import { ResultRow } from '../../pure/bqrs-cli-types';
export type RawTableProps = ResultTableProps & { export type RawTableProps = ResultTableProps & {
resultSet: RawTableResultSet; resultSet: RawTableResultSet;

View File

@@ -1,11 +1,11 @@
import * as React from 'react'; import * as React from 'react';
import { UrlValue, ResolvableLocationValue } from '../pure/bqrs-cli-types'; import { UrlValue, ResolvableLocationValue } from '../../pure/bqrs-cli-types';
import { isStringLoc, tryGetResolvableLocation } from '../pure/bqrs-utils'; import { isStringLoc, tryGetResolvableLocation } from '../../pure/bqrs-utils';
import { RawResultsSortState, QueryMetadata, SortDirection } from '../pure/interface-types'; import { RawResultsSortState, QueryMetadata, SortDirection } from '../../pure/interface-types';
import { assertNever } from '../pure/helpers-pure'; import { assertNever } from '../../pure/helpers-pure';
import { ResultSet } from '../pure/interface-types'; import { ResultSet } from '../../pure/interface-types';
import { vscode } from './vscode-api'; import { vscode } from '../vscode-api';
import { convertNonPrintableChars } from '../text-utils'; import { convertNonPrintableChars } from '../../text-utils';
export interface ResultTableProps { export interface ResultTableProps {
resultSet: ResultSet; resultSet: ResultSet;

View File

@@ -13,7 +13,7 @@ import {
getDefaultResultSetName, getDefaultResultSetName,
ParsedResultSets, ParsedResultSets,
IntoResultsViewMsg, IntoResultsViewMsg,
} from '../pure/interface-types'; } from '../../pure/interface-types';
import { PathTable } from './alert-table'; import { PathTable } from './alert-table';
import { Graph } from './graph'; import { Graph } from './graph';
import { RawTable } from './raw-results-table'; import { RawTable } from './raw-results-table';
@@ -25,7 +25,7 @@ import {
alertExtrasClassName, alertExtrasClassName,
openFile openFile
} from './result-table-utils'; } from './result-table-utils';
import { vscode } from './vscode-api'; import { vscode } from '../vscode-api';
const FILE_PATH_REGEX = /^(?:.+[\\/])*(.+)$/; const FILE_PATH_REGEX = /^(?:.+[\\/])*(.+)$/;

View File

@@ -1,6 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import * as Rdom from 'react-dom'; import { assertNever } from '../../pure/helpers-pure';
import { assertNever } from '../pure/helpers-pure';
import { import {
DatabaseInfo, DatabaseInfo,
Interpretation, Interpretation,
@@ -13,11 +12,12 @@ import {
ALERTS_TABLE_NAME, ALERTS_TABLE_NAME,
GRAPH_TABLE_NAME, GRAPH_TABLE_NAME,
ParsedResultSets, ParsedResultSets,
} from '../pure/interface-types'; } from '../../pure/interface-types';
import { EventHandlers as EventHandlerList } from './event-handler-list'; import { EventHandlers as EventHandlerList } from './event-handler-list';
import { ResultTables } from './result-tables'; import { ResultTables } from './result-tables';
import { ResultSet } from '../pure/interface-types'; import { ResultSet } from '../../pure/interface-types';
import { vscode } from './vscode-api';
import './resultsView.css';
/** /**
* results.tsx * results.tsx
@@ -72,7 +72,7 @@ export const onNavigation = new EventHandlerList<NavigationEvent>();
/** /**
* A minimal state container for displaying results. * A minimal state container for displaying results.
*/ */
class App extends React.Component<Record<string, never>, ResultsViewState> { export class ResultsApp extends React.Component<Record<string, never>, ResultsViewState> {
constructor(props: any) { constructor(props: any) {
super(props); super(props);
this.state = { this.state = {
@@ -302,7 +302,6 @@ class App extends React.Component<Record<string, never>, ResultsViewState> {
componentDidMount(): void { componentDidMount(): void {
this.vscodeMessageHandler = this.vscodeMessageHandler.bind(this); this.vscodeMessageHandler = this.vscodeMessageHandler.bind(this);
window.addEventListener('message', this.vscodeMessageHandler); window.addEventListener('message', this.vscodeMessageHandler);
vscode.postMessage({ t: 'resultViewLoaded' });
} }
componentWillUnmount(): void { componentWillUnmount(): void {
@@ -319,5 +318,3 @@ class App extends React.Component<Record<string, never>, ResultsViewState> {
: console.error(`Invalid event origin ${origin}`); : console.error(`Invalid event origin ${origin}`);
} }
} }
Rdom.render(<App />, document.getElementById('root'));

View File

@@ -0,0 +1,4 @@
export type WebviewDefinition = {
component: JSX.Element,
loadedMessage: 'compareViewLoaded' | 'remoteQueryLoaded' | 'resultViewLoaded';
}

View File

@@ -0,0 +1,36 @@
import * as ReactDOM from 'react-dom';
import { vscode } from './vscode-api';
import { WebviewDefinition } from './webview-interface';
// Allow all views to use Codicons
import '@vscode/codicons/dist/codicon.css';
const render = () => {
const element = document.getElementById('root');
if (!element) {
console.error('Could not find element with id "root"');
return;
}
const viewName = element.dataset.view;
if (!viewName) {
console.error('Could not find view name in data-view attribute');
return;
}
// It's a lot harder to use dynamic imports since those don't import the CSS
// and require a less strict CSP policy
// eslint-disable-next-line @typescript-eslint/no-var-requires
const view: WebviewDefinition = require(`./${viewName}/index.tsx`).default;
ReactDOM.render(
view.component,
document.getElementById('root'),
// Post a message to the extension when fully loaded.
() => vscode.postMessage({ t: view.loadedMessage })
);
};
render();

View File

@@ -44,7 +44,7 @@ const _10MB = _1MB * 10;
// CLI version to test. Hard code the latest as default. And be sure // CLI version to test. Hard code the latest as default. And be sure
// to update the env if it is not otherwise set. // to update the env if it is not otherwise set.
const CLI_VERSION = process.env.CLI_VERSION || 'v2.10.3'; const CLI_VERSION = process.env.CLI_VERSION || 'v2.10.4';
process.env.CLI_VERSION = CLI_VERSION; process.env.CLI_VERSION = CLI_VERSION;
// Base dir where CLIs will be downloaded into // Base dir where CLIs will be downloaded into

View File

@@ -6,15 +6,14 @@ import { expect } from 'chai';
import { window } from 'vscode'; import { window } from 'vscode';
import { import {
convertGithubNwoToDatabaseUrl,
convertLgtmUrlToDatabaseUrl, convertLgtmUrlToDatabaseUrl,
looksLikeLgtmUrl, looksLikeLgtmUrl,
findDirWithFile, findDirWithFile,
looksLikeGithubRepo, looksLikeGithubRepo,
} from '../../databaseFetcher'; } from '../../databaseFetcher';
import { ProgressCallback } from '../../commandRunner'; import { ProgressCallback } from '../../commandRunner';
import * as pq from 'proxyquire'; import * as Octokit from '@octokit/rest';
const proxyquire = pq.noPreserveCache();
describe('databaseFetcher', function() { describe('databaseFetcher', function() {
// These tests make API calls and may need extra time to complete. // These tests make API calls and may need extra time to complete.
@@ -25,20 +24,16 @@ describe('databaseFetcher', function() {
let quickPickSpy: sinon.SinonStub; let quickPickSpy: sinon.SinonStub;
let progressSpy: ProgressCallback; let progressSpy: ProgressCallback;
let mockRequest: sinon.SinonStub; let mockRequest: sinon.SinonStub;
let mod: any; let octokit: Octokit.Octokit;
const credentials = getMockCredentials(0);
beforeEach(() => { beforeEach(() => {
sandbox = sinon.createSandbox(); sandbox = sinon.createSandbox();
quickPickSpy = sandbox.stub(window, 'showQuickPick'); quickPickSpy = sandbox.stub(window, 'showQuickPick');
progressSpy = sandbox.spy(); progressSpy = sandbox.spy();
mockRequest = sandbox.stub(); mockRequest = sandbox.stub();
mod = proxyquire('../../databaseFetcher', { octokit = ({
'./authentication': { request: mockRequest,
Credentials: credentials, }) as unknown as Octokit.Octokit;
},
});
}); });
afterEach(() => { afterEach(() => {
@@ -90,11 +85,17 @@ describe('databaseFetcher', function() {
mockRequest.resolves(mockApiResponse); mockRequest.resolves(mockApiResponse);
quickPickSpy.resolves('javascript'); quickPickSpy.resolves('javascript');
const githubRepo = 'github/codeql'; const githubRepo = 'github/codeql';
const { databaseUrl, name, owner } = await mod.convertGithubNwoToDatabaseUrl( const result = await convertGithubNwoToDatabaseUrl(
githubRepo, githubRepo,
credentials, octokit,
progressSpy progressSpy
); );
expect(result).not.to.be.undefined;
if (result === undefined) {
return;
}
const { databaseUrl, name, owner } = result;
expect(databaseUrl).to.equal( expect(databaseUrl).to.equal(
'https://api.github.com/repos/github/codeql/code-scanning/codeql/databases/javascript' 'https://api.github.com/repos/github/codeql/code-scanning/codeql/databases/javascript'
@@ -119,7 +120,7 @@ describe('databaseFetcher', function() {
mockRequest.resolves(mockApiResponse); mockRequest.resolves(mockApiResponse);
const githubRepo = 'foo/bar-not-real'; const githubRepo = 'foo/bar-not-real';
await expect( await expect(
mod.convertGithubNwoToDatabaseUrl(githubRepo, credentials, progressSpy) convertGithubNwoToDatabaseUrl(githubRepo, octokit, progressSpy)
).to.be.rejectedWith(/Unable to get database/); ).to.be.rejectedWith(/Unable to get database/);
expect(progressSpy).to.have.callCount(0); expect(progressSpy).to.have.callCount(0);
}); });
@@ -133,19 +134,10 @@ describe('databaseFetcher', function() {
mockRequest.resolves(mockApiResponse); mockRequest.resolves(mockApiResponse);
const githubRepo = 'foo/bar-with-no-dbs'; const githubRepo = 'foo/bar-with-no-dbs';
await expect( await expect(
mod.convertGithubNwoToDatabaseUrl(githubRepo, credentials, progressSpy) convertGithubNwoToDatabaseUrl(githubRepo, octokit, progressSpy)
).to.be.rejectedWith(/Unable to get database/); ).to.be.rejectedWith(/Unable to get database/);
expect(progressSpy).to.have.been.calledOnce; expect(progressSpy).to.have.been.calledOnce;
}); });
function getMockCredentials(response: any) {
mockRequest = sinon.stub().resolves(response);
return {
getOctokit: () => ({
request: mockRequest,
}),
};
}
}); });
describe('convertLgtmUrlToDatabaseUrl', () => { describe('convertLgtmUrlToDatabaseUrl', () => {