Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa0fb498a0 | ||
|
|
176dc1fc71 | ||
|
|
a0eebb1e5f | ||
|
|
2af917284b | ||
|
|
4adb8b6301 | ||
|
|
8f5ddbd87c | ||
|
|
b689e55f61 | ||
|
|
7ce3dc2c43 | ||
|
|
eed85e9e28 | ||
|
|
0b56092466 | ||
|
|
4fce213ca8 | ||
|
|
8ed7b991be | ||
|
|
deb544ab93 | ||
|
|
9ec017a30d | ||
|
|
ebdf576196 | ||
|
|
13f725acfe | ||
|
|
1401115c08 | ||
|
|
85c04fc63a | ||
|
|
54ad3649b1 | ||
|
|
66e9272525 | ||
|
|
6793f8e92d | ||
|
|
da28beb82e | ||
|
|
b04ff3c8b9 | ||
|
|
fd4d6b7f30 | ||
|
|
5facab1f9e | ||
|
|
f25c9fd6fd | ||
|
|
a6043f2518 | ||
|
|
6a746ae5bd | ||
|
|
a9eb0a40fd | ||
|
|
d6be401d46 | ||
|
|
158a07cd89 | ||
|
|
7ac5a8f777 | ||
|
|
dc09925149 | ||
|
|
5fd2596537 | ||
|
|
22003e1375 | ||
|
|
2fee4cc368 | ||
|
|
9d2504959b | ||
|
|
77b3f0a025 | ||
|
|
a096e79bd4 | ||
|
|
dedc9c46ab |
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@@ -80,7 +80,7 @@ jobs:
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
cd extensions/ql-vscode
|
||||
$env:CODEQL_PATH=$(Join-Path $env:GITHUB_WORKSPACE -ChildPath 'codeql-home/codeql/codeql.cmd')
|
||||
$env:CODEQL_PATH=$(Join-Path $env:GITHUB_WORKSPACE -ChildPath 'codeql-home/codeql/codeql.exe')
|
||||
npm run test
|
||||
|
||||
- name: Run integration tests (Linux)
|
||||
|
||||
3
.vscode/launch.json
vendored
3
.vscode/launch.json
vendored
@@ -8,7 +8,8 @@
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceRoot}/dist/vscode-codeql"
|
||||
"--extensionDevelopmentPath=${workspaceRoot}/dist/vscode-codeql",
|
||||
"${workspaceRoot}/../vscode-codeql-starter/vscode-codeql-starter.code-workspace"
|
||||
],
|
||||
"stopOnEntry": false,
|
||||
"sourceMaps": true,
|
||||
|
||||
@@ -144,13 +144,23 @@ Alternatively, you can run the tests inside of vscode. There are several vscode
|
||||
|
||||
## Releasing (write access required)
|
||||
|
||||
1. Double-check the `CHANGELOG.md` contains all desired change comments
|
||||
and has the version to be released with date at the top.
|
||||
1. Double-check that the extension `package.json` has the version you intend to release.
|
||||
If you are doing a patch release (as opposed to minor or major version) this should already
|
||||
be correct.
|
||||
1. Trigger a release build on Actions by adding a new tag on master of the format `vxx.xx.xx`
|
||||
1. Monitor the status of the release build in the `Release` workflow in the Actions tab.
|
||||
1. Download the VSIX from the draft GitHub release that is created when the release build finishes.
|
||||
1. Download the VSIX from the draft GitHub release at the top of [the releases page](https://github.com/github/vscode-codeql/releases) that is created when the release build finishes.
|
||||
1. Optionally unzip the `.vsix` and inspect its `package.json` to make sure the version is what you expect,
|
||||
or look at the source if there's any doubt the right code is being shipped.
|
||||
1. Log into the [Visual Studio Marketplace](https://marketplace.visualstudio.com/manage/publishers/github).
|
||||
1. Click the `...` menu in the CodeQL row and click **Update**.
|
||||
1. Drag the `.vsix` file you downloaded from the GitHub release into the Marketplace and click **Upload**.
|
||||
1. Publish the draft GitHub release and confirm the new release is marked as the latest release at <https://github.com/github/vscode-codeql/releases>.
|
||||
1. Go to the draft GitHub release, click 'Edit', add some summary description, and publish it.
|
||||
1. Confirm the new release is marked as the latest release at <https://github.com/github/vscode-codeql/releases>.
|
||||
1. If documentation changes need to be published, notify documentation team that release has been made.
|
||||
1. Review and merge the version bump PR that is automatically created by Actions.
|
||||
|
||||
## Resources
|
||||
|
||||
|
||||
929
build/package-lock.json
generated
929
build/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
246
common/config/rush/pnpm-lock.yaml
generated
246
common/config/rush/pnpm-lock.yaml
generated
@@ -8,7 +8,8 @@ dependencies:
|
||||
'@rush-temp/semmle-vscode-utils': 'file:projects/semmle-vscode-utils.tgz'
|
||||
'@rush-temp/typescript-config': 'file:projects/typescript-config.tgz'
|
||||
'@rush-temp/vscode-codeql': 'file:projects/vscode-codeql.tgz'
|
||||
'@types/chai': 4.2.10
|
||||
'@types/chai': 4.2.11
|
||||
'@types/chai-as-promised': 7.1.2
|
||||
'@types/child-process-promise': 2.2.1
|
||||
'@types/classnames': 2.2.10
|
||||
'@types/fs-extra': 8.1.0
|
||||
@@ -18,23 +19,27 @@ dependencies:
|
||||
'@types/js-yaml': 3.12.2
|
||||
'@types/jszip': 3.1.7
|
||||
'@types/mocha': 5.2.7
|
||||
'@types/node': 12.12.29
|
||||
'@types/node': 12.12.30
|
||||
'@types/node-fetch': 2.5.5
|
||||
'@types/npm-packlist': 1.1.1
|
||||
'@types/proxyquire': 1.3.28
|
||||
'@types/react': 16.9.23
|
||||
'@types/react-dom': 16.9.5
|
||||
'@types/sarif': 2.1.2
|
||||
'@types/sinon': 7.5.2
|
||||
'@types/sinon-chai': 3.2.3
|
||||
'@types/through2': 2.0.34
|
||||
'@types/tmp': 0.1.0
|
||||
'@types/unzipper': 0.10.2
|
||||
'@types/vinyl': 2.0.4
|
||||
'@types/vscode': 1.42.0
|
||||
'@types/vscode': 1.43.0
|
||||
'@types/webpack': 4.41.7
|
||||
'@types/xml2js': 0.4.5
|
||||
'@typescript-eslint/eslint-plugin': 2.23.0_2510d86781fe783b47b58303c18a0d9b
|
||||
'@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.8.3
|
||||
ansi-colors: 4.1.1
|
||||
chai: 4.2.0
|
||||
chai-as-promised: 7.1.1_chai@4.2.0
|
||||
child-process-promise: 2.2.1
|
||||
classnames: 2.2.6
|
||||
css-loader: 3.1.0_webpack@4.42.0
|
||||
@@ -48,16 +53,19 @@ dependencies:
|
||||
js-yaml: 3.13.1
|
||||
jsonc-parser: 2.1.1
|
||||
leb: 0.3.0
|
||||
minimist: 1.2.5
|
||||
mocha: 6.2.2
|
||||
mocha-sinon: 2.1.0
|
||||
node-fetch: 2.6.0
|
||||
npm-packlist: 1.4.8
|
||||
npm-run-all: 4.1.5
|
||||
plugin-error: 1.0.1
|
||||
proxyquire: 2.1.3
|
||||
react: 16.13.0
|
||||
react-dom: 16.13.0_react@16.13.0
|
||||
reflect-metadata: 0.1.13
|
||||
sinon: 9.0.0
|
||||
sinon: 9.0.1
|
||||
sinon-chai: 3.5.0_chai@4.2.0+sinon@9.0.1
|
||||
style-loader: 0.23.1
|
||||
through2: 3.0.1
|
||||
tmp: 0.1.0
|
||||
@@ -322,13 +330,19 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-VQgHxyPMTj3hIlq9SY1mctqx+Jj8kpQfoLvDlVSDNOyuYs8JYfkuY3OW/4+dO657yPmNhHpePRx0/Tje5ImNVQ==
|
||||
/@types/chai/4.2.10:
|
||||
/@types/chai-as-promised/7.1.2:
|
||||
dependencies:
|
||||
'@types/chai': 4.2.11
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-TlWWgb21+0LdkuFqEqfmy7NEgfB/7Jjux15fWQAh3P93gbmXuwTM/vxEdzW89APIcI2BgKR48yjeAkdeH+4qvQ==
|
||||
integrity: sha512-PO2gcfR3Oxa+u0QvECLe1xKXOqYTzCmWf0FhLhjREoW3fPAVamjihL7v1MOVLJLsnAMdLcjkfrs01yvDMwVK4Q==
|
||||
/@types/chai/4.2.11:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-t7uW6eFafjO+qJ3BIV2gGUyZs27egcNRkUdalkud+Qa3+kg//f129iuOFivHDXQ+vnU3fDXuwgv0cqMCbcE8sw==
|
||||
/@types/child-process-promise/2.2.1:
|
||||
dependencies:
|
||||
'@types/node': 12.12.29
|
||||
'@types/node': 12.12.30
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-xZ4kkF82YkmqPCERqV9Tj0bVQj3Tk36BqGlNgxv5XhifgDRhwAqp+of+sccksdpZRbbPsNwMOkmUqOnLgxKtGw==
|
||||
@@ -354,20 +368,20 @@ packages:
|
||||
integrity: sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==
|
||||
/@types/fs-extra/5.0.4:
|
||||
dependencies:
|
||||
'@types/node': 12.12.29
|
||||
'@types/node': 12.12.30
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-DsknoBvD8s+RFfSGjmERJ7ZOP1HI0UZRA3FSI+Zakhrc/Gy26YQsLI+m5V5DHxroHRJqCDLKJp7Hixn8zyaF7g==
|
||||
/@types/fs-extra/8.1.0:
|
||||
dependencies:
|
||||
'@types/node': 12.12.29
|
||||
'@types/node': 12.12.30
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-UoOfVEzAUpeSPmjm7h1uk5MH6KZma2z2O7a75onTGjnNvAvMVrPzPL/vBbT65iIGHWj6rokwfmYcmxmlSf2uwg==
|
||||
/@types/glob-stream/6.1.0:
|
||||
dependencies:
|
||||
'@types/glob': 7.1.1
|
||||
'@types/node': 12.12.29
|
||||
'@types/node': 12.12.30
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-RHv6ZQjcTncXo3thYZrsbAVwoy4vSKosSWhuhuQxLOTv74OJuFQxXkmUuZCr3q9uNBEVCvIzmZL/FeRNbHZGUg==
|
||||
@@ -375,7 +389,7 @@ packages:
|
||||
dependencies:
|
||||
'@types/events': 3.0.0
|
||||
'@types/minimatch': 3.0.3
|
||||
'@types/node': 12.12.29
|
||||
'@types/node': 12.12.30
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==
|
||||
@@ -405,7 +419,7 @@ packages:
|
||||
integrity: sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==
|
||||
/@types/jszip/3.1.7:
|
||||
dependencies:
|
||||
'@types/node': 12.12.29
|
||||
'@types/node': 12.12.30
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-+XQKNI5zpxutK05hO67huUTw/2imXCuJWjnFdU63tRES/xXSX1yVR9cv/QAdO6Rii2y2tTHbzjQ4i2apLfuK0Q==
|
||||
@@ -419,7 +433,7 @@ packages:
|
||||
integrity: sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==
|
||||
/@types/node-fetch/2.5.5:
|
||||
dependencies:
|
||||
'@types/node': 12.12.29
|
||||
'@types/node': 12.12.30
|
||||
form-data: 3.0.0
|
||||
dev: false
|
||||
resolution:
|
||||
@@ -428,10 +442,10 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==
|
||||
/@types/node/12.12.29:
|
||||
/@types/node/12.12.30:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-yo8Qz0ygADGFptISDj3pOC9wXfln/5pQaN/ysDIzOaAWXt73cNHmtEC8zSO2Y+kse/txmwIAJzkYZ5fooaS5DQ==
|
||||
integrity: sha512-sz9MF/zk6qVr3pAnM0BSQvYIBK44tS75QC5N+VbWSE4DjCV/pJ+UzCW/F+vVnl7TkOPcuwQureKNtSSwjBTaMg==
|
||||
/@types/node/8.5.8:
|
||||
dev: false
|
||||
resolution:
|
||||
@@ -444,6 +458,10 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
|
||||
/@types/proxyquire/1.3.28:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-SQaNzWQ2YZSr7FqAyPPiA3FYpux2Lqh3HWMZQk47x3xbMCqgC/w0dY3dw9rGqlweDDkrySQBcaScXWeR+Yb11Q==
|
||||
/@types/react-dom/16.9.5:
|
||||
dependencies:
|
||||
'@types/react': 16.9.23
|
||||
@@ -461,6 +479,17 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-TELZl5h48KaB6SFZqTuaMEw1hrGuusbBcH+yfMaaHdS2pwDr3RTH4CVN0LyY1kqSiDm9PPvAMx8FJ0LUZreOCQ==
|
||||
/@types/sinon-chai/3.2.3:
|
||||
dependencies:
|
||||
'@types/chai': 4.2.11
|
||||
'@types/sinon': 7.5.2
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-TOUFS6vqS0PVL1I8NGVSNcFaNJtFoyZPXZ5zur+qlhDfOmQECZZM4H4kKgca6O8L+QceX/ymODZASfUfn+y4yQ==
|
||||
/@types/sinon/7.5.2:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-T+m89VdXj/eidZyejvmoP9jivXgBDdkOSBVQjU9kF349NEx10QdPNGxHeZUaj1IlJ32/ewdyXJjnJxyxJroYwg==
|
||||
/@types/source-list-map/0.1.2:
|
||||
dev: false
|
||||
resolution:
|
||||
@@ -471,7 +500,7 @@ packages:
|
||||
integrity: sha512-/gG2M/Imw7cQFp8PGvz/SwocNrmKFjFsm5Pb8HdbHkZ1K8pmuPzOX4VeVoiEecFCVf4CsN1r3/BRvx+6sNqwtQ==
|
||||
/@types/through2/2.0.34:
|
||||
dependencies:
|
||||
'@types/node': 12.12.29
|
||||
'@types/node': 12.12.30
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-nhRG8+RuG/L+0fAZBQYaRflXKjTrHOKH8MFTChnf+dNVMxA3wHYYrfj0tztK0W51ABXjGfRCDc0vRkecCOrsow==
|
||||
@@ -497,14 +526,14 @@ packages:
|
||||
integrity: sha512-j4iepCSuY2JGW/hShVtUBagic0klYNFIXP7VweavnYnNC2EjiKxJFeaS9uaJmAT0ty9sQSqTS1aagWMZMV0HyA==
|
||||
/@types/unzipper/0.10.2:
|
||||
dependencies:
|
||||
'@types/node': 12.12.29
|
||||
'@types/node': 12.12.30
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-VgYoNEyj8xkz9I+RTWD00iB9JVViK/RBteNDjOIV3/kdCUPaskka7xAZfFlIxRwKGSPf77F8yje5bJt2PefofQ==
|
||||
/@types/vinyl-fs/2.4.11:
|
||||
dependencies:
|
||||
'@types/glob-stream': 6.1.0
|
||||
'@types/node': 12.12.29
|
||||
'@types/node': 12.12.30
|
||||
'@types/vinyl': 2.0.4
|
||||
dev: false
|
||||
resolution:
|
||||
@@ -512,17 +541,17 @@ packages:
|
||||
/@types/vinyl/2.0.4:
|
||||
dependencies:
|
||||
'@types/expect': 1.20.4
|
||||
'@types/node': 12.12.29
|
||||
'@types/node': 12.12.30
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-2o6a2ixaVI2EbwBPg1QYLGQoHK56p/8X/sGfKbFC8N6sY9lfjsMf/GprtkQkSya0D4uRiutRZ2BWj7k3JvLsAQ==
|
||||
/@types/vscode/1.42.0:
|
||||
/@types/vscode/1.43.0:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-ds6TceMsh77Fs0Mq0Vap6Y72JbGWB8Bay4DrnJlf5d9ui2RSe1wis13oQm+XhguOeH1HUfLGzaDAoupTUtgabw==
|
||||
integrity: sha512-kIaR9qzd80rJOxePKpCB/mdy00mz8Apt2QA5Y6rdrKFn13QNFNeP3Hzmsf37Bwh/3cS7QjtAeGSK7wSqAU0sYQ==
|
||||
/@types/webpack-sources/0.1.6:
|
||||
dependencies:
|
||||
'@types/node': 12.12.29
|
||||
'@types/node': 12.12.30
|
||||
'@types/source-list-map': 0.1.2
|
||||
source-map: 0.6.1
|
||||
dev: false
|
||||
@@ -531,7 +560,7 @@ packages:
|
||||
/@types/webpack/4.41.7:
|
||||
dependencies:
|
||||
'@types/anymatch': 1.3.1
|
||||
'@types/node': 12.12.29
|
||||
'@types/node': 12.12.30
|
||||
'@types/tapable': 1.0.5
|
||||
'@types/uglify-js': 3.0.4
|
||||
'@types/webpack-sources': 0.1.6
|
||||
@@ -541,7 +570,7 @@ packages:
|
||||
integrity: sha512-OQG9viYwO0V1NaNV7d0n79V+n6mjOV30CwgFPIfTzwmk8DHbt+C4f2aBGdCYbo3yFyYD6sjXfqqOjwkl1j+ulA==
|
||||
/@types/xml2js/0.4.5:
|
||||
dependencies:
|
||||
'@types/node': 12.12.29
|
||||
'@types/node': 12.12.30
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-yohU3zMn0fkhlape1nxXG2bLEGZRc1FeqF80RoHaYXJN7uibaauXfhzhOJr1Xh36sn+/tx21QAOf07b/xYVk1w==
|
||||
@@ -1363,7 +1392,7 @@ packages:
|
||||
infer-owner: 1.0.4
|
||||
lru-cache: 5.1.1
|
||||
mississippi: 3.0.0
|
||||
mkdirp: 0.5.1
|
||||
mkdirp: 0.5.3
|
||||
move-concurrently: 1.0.1
|
||||
promise-inflight: 1.0.1
|
||||
rimraf: 2.7.1
|
||||
@@ -1407,6 +1436,15 @@ packages:
|
||||
node: '>=6'
|
||||
resolution:
|
||||
integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
|
||||
/chai-as-promised/7.1.1_chai@4.2.0:
|
||||
dependencies:
|
||||
chai: 4.2.0
|
||||
check-error: 1.0.2
|
||||
dev: false
|
||||
peerDependencies:
|
||||
chai: '>= 2.1.2 < 5'
|
||||
resolution:
|
||||
integrity: sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==
|
||||
/chai/4.2.0:
|
||||
dependencies:
|
||||
assertion-error: 1.1.0
|
||||
@@ -1716,7 +1754,7 @@ packages:
|
||||
aproba: 1.2.0
|
||||
fs-write-stream-atomic: 1.0.10
|
||||
iferr: 0.1.5
|
||||
mkdirp: 0.5.1
|
||||
mkdirp: 0.5.3
|
||||
rimraf: 2.7.1
|
||||
run-queue: 1.0.3
|
||||
dev: false
|
||||
@@ -1815,7 +1853,7 @@ packages:
|
||||
postcss-modules-scope: 2.1.1
|
||||
postcss-modules-values: 3.0.0
|
||||
postcss-value-parser: 4.0.3
|
||||
schema-utils: 2.6.4
|
||||
schema-utils: 2.6.5
|
||||
webpack: 4.42.0_webpack@4.42.0
|
||||
dev: false
|
||||
engines:
|
||||
@@ -1896,7 +1934,7 @@ packages:
|
||||
integrity: sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
|
||||
/debug/4.1.1:
|
||||
dependencies:
|
||||
ms: 2.1.1
|
||||
ms: 2.1.2
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
|
||||
@@ -2345,7 +2383,7 @@ packages:
|
||||
levn: 0.3.0
|
||||
lodash: 4.17.15
|
||||
minimatch: 3.0.4
|
||||
mkdirp: 0.5.1
|
||||
mkdirp: 0.5.3
|
||||
natural-compare: 1.4.0
|
||||
optionator: 0.8.3
|
||||
progress: 2.0.3
|
||||
@@ -2355,7 +2393,7 @@ packages:
|
||||
strip-json-comments: 3.0.1
|
||||
table: 5.4.6
|
||||
text-table: 0.2.0
|
||||
v8-compile-cache: 2.0.3
|
||||
v8-compile-cache: 2.1.0
|
||||
dev: false
|
||||
engines:
|
||||
node: ^8.10.0 || ^10.13.0 || >=11.10.1
|
||||
@@ -2577,6 +2615,15 @@ packages:
|
||||
optional: true
|
||||
resolution:
|
||||
integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
|
||||
/fill-keys/1.0.2:
|
||||
dependencies:
|
||||
is-object: 1.0.1
|
||||
merge-descriptors: 1.0.1
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha1-mo+jb06K1jTjv2tPPIiCVRRS6yA=
|
||||
/fill-range/4.0.0:
|
||||
dependencies:
|
||||
extend-shallow: 2.0.1
|
||||
@@ -2789,7 +2836,7 @@ packages:
|
||||
dependencies:
|
||||
graceful-fs: 4.2.3
|
||||
inherits: 2.0.4
|
||||
mkdirp: 0.5.1
|
||||
mkdirp: 0.5.3
|
||||
rimraf: 2.7.1
|
||||
dev: false
|
||||
engines:
|
||||
@@ -3539,6 +3586,10 @@ packages:
|
||||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==
|
||||
/is-object/1.0.1:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-iVJojF7C/9awPsyF52ngKQMINHA=
|
||||
/is-plain-obj/2.1.0:
|
||||
dev: false
|
||||
engines:
|
||||
@@ -3681,20 +3732,29 @@ packages:
|
||||
integrity: sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
|
||||
/json5/1.0.1:
|
||||
dependencies:
|
||||
minimist: 1.2.0
|
||||
minimist: 1.2.5
|
||||
dev: false
|
||||
hasBin: true
|
||||
resolution:
|
||||
integrity: sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
|
||||
/json5/2.1.1:
|
||||
dependencies:
|
||||
minimist: 1.2.0
|
||||
minimist: 1.2.5
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=6'
|
||||
hasBin: true
|
||||
resolution:
|
||||
integrity: sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==
|
||||
/json5/2.1.2:
|
||||
dependencies:
|
||||
minimist: 1.2.5
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=6'
|
||||
hasBin: true
|
||||
resolution:
|
||||
integrity: sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ==
|
||||
/jsonc-parser/2.1.1:
|
||||
dev: false
|
||||
resolution:
|
||||
@@ -4062,6 +4122,10 @@ packages:
|
||||
node: '>= 0.10.0'
|
||||
resolution:
|
||||
integrity: sha1-htcJCzDORV1j+64S3aUaR93K+bI=
|
||||
/merge-descriptors/1.0.1:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
|
||||
/micromatch/3.1.10:
|
||||
dependencies:
|
||||
arr-diff: 4.0.0
|
||||
@@ -4141,10 +4205,10 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
|
||||
/minimist/1.2.0:
|
||||
/minimist/1.2.5:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
|
||||
integrity: sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||
/minipass/2.9.0:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.0
|
||||
@@ -4187,10 +4251,19 @@ packages:
|
||||
/mkdirp/0.5.1:
|
||||
dependencies:
|
||||
minimist: 0.0.8
|
||||
deprecated: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)
|
||||
dev: false
|
||||
hasBin: true
|
||||
resolution:
|
||||
integrity: sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
|
||||
/mkdirp/0.5.3:
|
||||
dependencies:
|
||||
minimist: 1.2.5
|
||||
deprecated: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)
|
||||
dev: false
|
||||
hasBin: true
|
||||
resolution:
|
||||
integrity: sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==
|
||||
/mocha-sinon/2.1.0:
|
||||
dev: false
|
||||
engines:
|
||||
@@ -4228,12 +4301,16 @@ packages:
|
||||
hasBin: true
|
||||
resolution:
|
||||
integrity: sha512-FgDS9Re79yU1xz5d+C4rv1G7QagNGHZ+iXF81hO8zY35YZZcLEsJVfFolfsqKFWunATEvNzMK0r/CwWd/szO9A==
|
||||
/module-not-found-error/1.0.1:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-z4tP9PKWQGdNbN0CsOO8UjwrvcA=
|
||||
/move-concurrently/1.0.1:
|
||||
dependencies:
|
||||
aproba: 1.2.0
|
||||
copy-concurrently: 1.0.5
|
||||
fs-write-stream-atomic: 1.0.10
|
||||
mkdirp: 0.5.1
|
||||
mkdirp: 0.5.3
|
||||
rimraf: 2.7.1
|
||||
run-queue: 1.0.3
|
||||
dev: false
|
||||
@@ -4247,6 +4324,10 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
|
||||
/ms/2.1.2:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
/mute-stdout/1.0.1:
|
||||
dev: false
|
||||
engines:
|
||||
@@ -4805,7 +4886,7 @@ packages:
|
||||
integrity: sha1-mkr9bfBj3Egm+T+6SpnPIj9mbLg=
|
||||
/parse5/3.0.3:
|
||||
dependencies:
|
||||
'@types/node': 12.12.29
|
||||
'@types/node': 12.12.30
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==
|
||||
@@ -5078,6 +5159,14 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
||||
/proxyquire/2.1.3:
|
||||
dependencies:
|
||||
fill-keys: 1.0.2
|
||||
module-not-found-error: 1.0.1
|
||||
resolve: 1.15.1
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-BQWfCqYM+QINd+yawJz23tbBM40VIGXOdDw3X344KcclI/gtBbdWF6SlQ4nK/bYhF9d27KYug9WzljHC6B9Ysg==
|
||||
/prr/1.0.1:
|
||||
dev: false
|
||||
resolution:
|
||||
@@ -5545,7 +5634,7 @@ packages:
|
||||
node: '>= 4'
|
||||
resolution:
|
||||
integrity: sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==
|
||||
/schema-utils/2.6.4:
|
||||
/schema-utils/2.6.5:
|
||||
dependencies:
|
||||
ajv: 6.12.0
|
||||
ajv-keywords: 3.4.1_ajv@6.12.0
|
||||
@@ -5553,7 +5642,7 @@ packages:
|
||||
engines:
|
||||
node: '>= 8.9.0'
|
||||
resolution:
|
||||
integrity: sha512-VNjcaUxVnEeun6B2fiiUDjXXBtD4ZSH7pdbfIu1pOFwgptDPLMo/z9jr4sUfsjFVPqDCEin/F7IYlq7/E6yDbQ==
|
||||
integrity: sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ==
|
||||
/semver-greatest-satisfied-range/1.1.0:
|
||||
dependencies:
|
||||
sver-compat: 1.5.0
|
||||
@@ -5634,7 +5723,17 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
|
||||
/sinon/9.0.0:
|
||||
/sinon-chai/3.5.0_chai@4.2.0+sinon@9.0.1:
|
||||
dependencies:
|
||||
chai: 4.2.0
|
||||
sinon: 9.0.1
|
||||
dev: false
|
||||
peerDependencies:
|
||||
chai: ^4.0.0
|
||||
sinon: '>=4.0.0 <10.0.0'
|
||||
resolution:
|
||||
integrity: sha512-IifbusYiQBpUxxFJkR3wTU68xzBN0+bxCScEaKMjBvAQERg6FnTTc1F17rseLb1tjmkJ23730AXpFI0c47FgAg==
|
||||
/sinon/9.0.1:
|
||||
dependencies:
|
||||
'@sinonjs/commons': 1.7.1
|
||||
'@sinonjs/fake-timers': 6.0.0
|
||||
@@ -5645,7 +5744,7 @@ packages:
|
||||
supports-color: 7.1.0
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-c4bREcvuK5VuEGyMW/Oim9I3Rq49Vzb0aMdxouFaA44QCFpilc5LJOugrX+mkrvikbqCimxuK+4cnHVNnLR41g==
|
||||
integrity: sha512-iTTyiQo5T94jrOx7X7QLBZyucUJ2WvL9J13+96HMfm2CGoJYbIPqRfl6wgNcqmzk0DI28jeGx5bUTXizkrqBmg==
|
||||
/slice-ansi/2.1.0:
|
||||
dependencies:
|
||||
ansi-styles: 3.2.1
|
||||
@@ -6070,7 +6169,7 @@ packages:
|
||||
fs-minipass: 1.2.7
|
||||
minipass: 2.9.0
|
||||
minizlib: 1.3.3
|
||||
mkdirp: 0.5.1
|
||||
mkdirp: 0.5.3
|
||||
safe-buffer: 5.2.0
|
||||
yallist: 3.1.1
|
||||
dev: false
|
||||
@@ -6086,7 +6185,7 @@ packages:
|
||||
schema-utils: 1.0.0
|
||||
serialize-javascript: 2.1.2
|
||||
source-map: 0.6.1
|
||||
terser: 4.6.6
|
||||
terser: 4.6.7
|
||||
webpack: 4.42.0_webpack@4.42.0
|
||||
webpack-sources: 1.4.3
|
||||
worker-farm: 1.7.0
|
||||
@@ -6097,7 +6196,7 @@ packages:
|
||||
webpack: ^4.0.0
|
||||
resolution:
|
||||
integrity: sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==
|
||||
/terser/4.6.6:
|
||||
/terser/4.6.7:
|
||||
dependencies:
|
||||
commander: 2.20.3
|
||||
source-map: 0.6.1
|
||||
@@ -6107,7 +6206,7 @@ packages:
|
||||
node: '>=6.0.0'
|
||||
hasBin: true
|
||||
resolution:
|
||||
integrity: sha512-4lYPyeNmstjIIESr/ysHg2vUPRGf2tzF9z2yYwnowXVuVzLEamPN1Gfrz7f8I9uEPuHcbFlW4PLIAsJoxXyJ1g==
|
||||
integrity: sha512-fmr7M1f7DBly5cX2+rFDvmGBAaaZyPrHYK4mMdHEDAdNTqXSZgSOfqsfGq2HqPGT/1V0foZZuCZFx8CHKgAk3g==
|
||||
/text-table/0.2.0:
|
||||
dev: false
|
||||
resolution:
|
||||
@@ -6540,6 +6639,10 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==
|
||||
/v8-compile-cache/2.1.0:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==
|
||||
/v8flags/3.1.3:
|
||||
dependencies:
|
||||
homedir-polyfill: 1.0.3
|
||||
@@ -6761,7 +6864,7 @@ packages:
|
||||
loader-utils: 1.4.0
|
||||
memory-fs: 0.4.1
|
||||
micromatch: 3.1.10
|
||||
mkdirp: 0.5.1
|
||||
mkdirp: 0.5.3
|
||||
neo-async: 2.6.1
|
||||
node-libs-browser: 2.2.1
|
||||
schema-utils: 1.0.0
|
||||
@@ -6860,7 +6963,7 @@ packages:
|
||||
/write-json5-file/2.1.2:
|
||||
dependencies:
|
||||
graceful-fs: 4.2.3
|
||||
json5: 2.1.1
|
||||
json5: 2.1.2
|
||||
make-dir: 3.0.2
|
||||
sort-keys: 4.0.0
|
||||
write-file-atomic: 2.4.3
|
||||
@@ -6883,7 +6986,7 @@ packages:
|
||||
integrity: sha512-OHzbrlgjw/K/BAH6LdEOcSQFz5nkk0I/25CjKLIVFvcg2Ej7+QE/GTnitgqWnhlsdghor7OV5gfttQPGogQ1XA==
|
||||
/write/1.0.3:
|
||||
dependencies:
|
||||
mkdirp: 0.5.1
|
||||
mkdirp: 0.5.3
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
@@ -6918,6 +7021,13 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==
|
||||
/yargs-parser/13.1.2:
|
||||
dependencies:
|
||||
camelcase: 5.3.1
|
||||
decamelize: 1.2.0
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==
|
||||
/yargs-parser/5.0.0:
|
||||
dependencies:
|
||||
camelcase: 3.0.0
|
||||
@@ -6946,7 +7056,7 @@ packages:
|
||||
string-width: 3.1.0
|
||||
which-module: 2.0.0
|
||||
y18n: 4.0.0
|
||||
yargs-parser: 13.1.1
|
||||
yargs-parser: 13.1.2
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==
|
||||
@@ -7021,7 +7131,7 @@ packages:
|
||||
'@types/fs-extra': 8.1.0
|
||||
'@types/gulp': 4.0.6
|
||||
'@types/js-yaml': 3.12.2
|
||||
'@types/node': 12.12.29
|
||||
'@types/node': 12.12.30
|
||||
'@types/npm-packlist': 1.1.1
|
||||
'@types/through2': 2.0.34
|
||||
'@types/vinyl': 2.0.4
|
||||
@@ -7051,7 +7161,7 @@ packages:
|
||||
version: 0.0.0
|
||||
'file:projects/semmle-bqrs.tgz_typescript@3.8.3':
|
||||
dependencies:
|
||||
'@types/node': 12.12.29
|
||||
'@types/node': 12.12.30
|
||||
leb: 0.3.0
|
||||
reflect-metadata: 0.1.13
|
||||
typescript-formatter: 7.2.2_typescript@3.8.3
|
||||
@@ -7067,7 +7177,7 @@ packages:
|
||||
'file:projects/semmle-io-node.tgz_typescript@3.8.3':
|
||||
dependencies:
|
||||
'@types/fs-extra': 8.1.0
|
||||
'@types/node': 12.12.29
|
||||
'@types/node': 12.12.30
|
||||
fs-extra: 8.1.0
|
||||
typescript-formatter: 7.2.2_typescript@3.8.3
|
||||
dev: false
|
||||
@@ -7081,7 +7191,7 @@ packages:
|
||||
version: 0.0.0
|
||||
'file:projects/semmle-io.tgz_typescript@3.8.3':
|
||||
dependencies:
|
||||
'@types/node': 12.12.29
|
||||
'@types/node': 12.12.30
|
||||
leb: 0.3.0
|
||||
typescript-formatter: 7.2.2_typescript@3.8.3
|
||||
dev: false
|
||||
@@ -7095,8 +7205,8 @@ packages:
|
||||
version: 0.0.0
|
||||
'file:projects/semmle-vscode-utils.tgz':
|
||||
dependencies:
|
||||
'@types/node': 12.12.29
|
||||
'@types/vscode': 1.42.0
|
||||
'@types/node': 12.12.30
|
||||
'@types/vscode': 1.43.0
|
||||
typescript: 3.8.3
|
||||
typescript-formatter: 7.2.2_typescript@3.8.3
|
||||
dev: false
|
||||
@@ -7114,7 +7224,8 @@ packages:
|
||||
version: 0.0.0
|
||||
'file:projects/vscode-codeql.tgz':
|
||||
dependencies:
|
||||
'@types/chai': 4.2.10
|
||||
'@types/chai': 4.2.11
|
||||
'@types/chai-as-promised': 7.1.2
|
||||
'@types/child-process-promise': 2.2.1
|
||||
'@types/classnames': 2.2.10
|
||||
'@types/fs-extra': 8.1.0
|
||||
@@ -7124,19 +7235,23 @@ packages:
|
||||
'@types/js-yaml': 3.12.2
|
||||
'@types/jszip': 3.1.7
|
||||
'@types/mocha': 5.2.7
|
||||
'@types/node': 12.12.29
|
||||
'@types/node': 12.12.30
|
||||
'@types/node-fetch': 2.5.5
|
||||
'@types/proxyquire': 1.3.28
|
||||
'@types/react': 16.9.23
|
||||
'@types/react-dom': 16.9.5
|
||||
'@types/sarif': 2.1.2
|
||||
'@types/sinon': 7.5.2
|
||||
'@types/sinon-chai': 3.2.3
|
||||
'@types/tmp': 0.1.0
|
||||
'@types/unzipper': 0.10.2
|
||||
'@types/vscode': 1.42.0
|
||||
'@types/vscode': 1.43.0
|
||||
'@types/webpack': 4.41.7
|
||||
'@types/xml2js': 0.4.5
|
||||
'@typescript-eslint/eslint-plugin': 2.23.0_2510d86781fe783b47b58303c18a0d9b
|
||||
'@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.8.3
|
||||
chai: 4.2.0
|
||||
chai-as-promised: 7.1.1_chai@4.2.0
|
||||
child-process-promise: 2.2.1
|
||||
classnames: 2.2.6
|
||||
css-loader: 3.1.0_webpack@4.42.0
|
||||
@@ -7148,13 +7263,16 @@ packages:
|
||||
gulp-sourcemaps: 2.6.5
|
||||
gulp-typescript: 5.0.1_typescript@3.8.3
|
||||
js-yaml: 3.13.1
|
||||
minimist: 1.2.5
|
||||
mocha: 6.2.2
|
||||
mocha-sinon: 2.1.0
|
||||
node-fetch: 2.6.0
|
||||
npm-run-all: 4.1.5
|
||||
proxyquire: 2.1.3
|
||||
react: 16.13.0
|
||||
react-dom: 16.13.0_react@16.13.0
|
||||
sinon: 9.0.0
|
||||
sinon: 9.0.1
|
||||
sinon-chai: 3.5.0_chai@4.2.0+sinon@9.0.1
|
||||
style-loader: 0.23.1
|
||||
through2: 3.0.1
|
||||
tmp: 0.1.0
|
||||
@@ -7176,7 +7294,7 @@ packages:
|
||||
dev: false
|
||||
name: '@rush-temp/vscode-codeql'
|
||||
resolution:
|
||||
integrity: sha512-S/tOZMV3yl8jqavEC2X5ly4pwsb8hcACsAfEggZ5x/l7HDplczVae+Xtc49JFeMueYF00KZN1dSM0cADeftzew==
|
||||
integrity: sha512-PvC3L2Tp+VYm+hMzTgXfdBJPLJopSQpVsT8Ym7kdIxZj/cyTWzO3A+n7HnrH5q/B4DJSRfiDwjp73GwjGhbteQ==
|
||||
tarball: 'file:projects/vscode-codeql.tgz'
|
||||
version: 0.0.0
|
||||
registry: ''
|
||||
@@ -7191,6 +7309,7 @@ specifiers:
|
||||
'@rush-temp/typescript-config': 'file:./projects/typescript-config.tgz'
|
||||
'@rush-temp/vscode-codeql': 'file:./projects/vscode-codeql.tgz'
|
||||
'@types/chai': ^4.1.7
|
||||
'@types/chai-as-promised': ~7.1.2
|
||||
'@types/child-process-promise': ^2.2.1
|
||||
'@types/classnames': ~2.2.9
|
||||
'@types/fs-extra': ^8.0.0
|
||||
@@ -7203,9 +7322,12 @@ specifiers:
|
||||
'@types/node': ^12.0.8
|
||||
'@types/node-fetch': ~2.5.2
|
||||
'@types/npm-packlist': ~1.1.1
|
||||
'@types/proxyquire': ~1.3.28
|
||||
'@types/react': ^16.8.17
|
||||
'@types/react-dom': ^16.8.4
|
||||
'@types/sarif': ~2.1.2
|
||||
'@types/sinon': ~7.5.2
|
||||
'@types/sinon-chai': ~3.2.3
|
||||
'@types/through2': ~2.0.34
|
||||
'@types/tmp': ^0.1.0
|
||||
'@types/unzipper': ~0.10.1
|
||||
@@ -7217,6 +7339,7 @@ specifiers:
|
||||
'@typescript-eslint/parser': ~2.23.0
|
||||
ansi-colors: ^4.0.1
|
||||
chai: ^4.2.0
|
||||
chai-as-promised: ~7.1.1
|
||||
child-process-promise: ^2.2.1
|
||||
classnames: ~2.2.6
|
||||
css-loader: ~3.1.0
|
||||
@@ -7230,16 +7353,19 @@ specifiers:
|
||||
js-yaml: ^3.12.0
|
||||
jsonc-parser: ~2.1.0
|
||||
leb: ^0.3.0
|
||||
minimist: ~1.2.5
|
||||
mocha: ~6.2.1
|
||||
mocha-sinon: ~2.1.0
|
||||
node-fetch: ~2.6.0
|
||||
npm-packlist: ~1.4.4
|
||||
npm-run-all: ^4.1.5
|
||||
plugin-error: ^1.0.1
|
||||
proxyquire: ~2.1.3
|
||||
react: ^16.8.6
|
||||
react-dom: ^16.8.6
|
||||
reflect-metadata: ~0.1.13
|
||||
sinon: ~9.0.0
|
||||
sinon-chai: ~3.5.0
|
||||
style-loader: ~0.23.1
|
||||
through2: ^3.0.1
|
||||
tmp: ^0.1.0
|
||||
|
||||
@@ -6,6 +6,7 @@ module.exports = {
|
||||
ecmaFeatures: {
|
||||
modules: true,
|
||||
},
|
||||
project: ['tsconfig.json', './src/**/tsconfig.json'],
|
||||
},
|
||||
plugins: ['@typescript-eslint'],
|
||||
env: {
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# CodeQL for Visual Studio Code: Changelog
|
||||
|
||||
## 1.1.1 - 23 March 2020
|
||||
|
||||
- Fix quick evaluation in `.qll` files.
|
||||
- Add new command in query history view to view the log file of a
|
||||
query.
|
||||
- Request user acknowledgment before updating the CodeQL binaries.
|
||||
- Warn when using the deprecated `codeql.cmd` launcher on Windows.
|
||||
|
||||
## 1.1.0 - 17 March 2020
|
||||
|
||||
- Add functionality for testing custom CodeQL queries by using the VS
|
||||
@@ -52,7 +60,6 @@ hour for unauthenticated IPs.
|
||||
- Fix the automatic upgrading of CodeQL databases when using upgrade scripts from the workspace.
|
||||
- Allow removal of items from the CodeQL Query History view.
|
||||
|
||||
|
||||
## 1.0.0 - 14 November 2019
|
||||
|
||||
Initial release of CodeQL for Visual Studio Code.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"description": "CodeQL for Visual Studio Code",
|
||||
"author": "GitHub",
|
||||
"private": true,
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"publisher": "GitHub",
|
||||
"license": "MIT",
|
||||
"icon": "media/VS-marketplace-CodeQL-icon.png",
|
||||
@@ -99,7 +99,7 @@
|
||||
"scope": "machine",
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Path to the CodeQL executable that should be used by the CodeQL extension. The executable is named `codeql` on Linux/Mac and `codeql.cmd` on Windows. This overrides all other CodeQL CLI settings."
|
||||
"description": "Path to the CodeQL executable that should be used by the CodeQL extension. The executable is named `codeql` on Linux/Mac and `codeql.exe` on Windows. This overrides all other CodeQL CLI settings."
|
||||
},
|
||||
"codeQL.runningQueries.numberOfThreads": {
|
||||
"type": "integer",
|
||||
@@ -208,6 +208,10 @@
|
||||
"command": "codeQLQueryHistory.itemClicked",
|
||||
"title": "Query History Item"
|
||||
},
|
||||
{
|
||||
"command": "codeQLQueryHistory.showQueryLog",
|
||||
"title": "Show Query Log"
|
||||
},
|
||||
{
|
||||
"command": "codeQLQueryResults.nextPathStep",
|
||||
"title": "CodeQL: Show Next Step on Path"
|
||||
@@ -272,6 +276,11 @@
|
||||
"group": "9_qlCommands",
|
||||
"when": "view == codeQLQueryHistory"
|
||||
},
|
||||
{
|
||||
"command": "codeQLQueryHistory.showQueryLog",
|
||||
"group": "9_qlCommands",
|
||||
"when": "view == codeQLQueryHistory"
|
||||
},
|
||||
{
|
||||
"command": "codeQLTests.showOutputDifferences",
|
||||
"group": "qltest@1",
|
||||
@@ -328,6 +337,10 @@
|
||||
"command": "codeQLQueryHistory.itemClicked",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "codeQLQueryHistory.showQueryLog",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "codeQLQueryHistory.setLabel",
|
||||
"when": "false"
|
||||
@@ -396,7 +409,8 @@
|
||||
"vscode-jsonrpc": "^4.0.0",
|
||||
"vscode-languageclient": "^5.2.1",
|
||||
"vscode-test-adapter-api": "~1.7.0",
|
||||
"vscode-test-adapter-util": "~0.7.0"
|
||||
"vscode-test-adapter-util": "~0.7.0",
|
||||
"minimist": "~1.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.1.7",
|
||||
@@ -444,6 +458,13 @@
|
||||
"webpack-cli": "^3.3.2",
|
||||
"eslint": "~6.8.0",
|
||||
"@typescript-eslint/eslint-plugin": "~2.23.0",
|
||||
"@typescript-eslint/parser": "~2.23.0"
|
||||
"@typescript-eslint/parser": "~2.23.0",
|
||||
"chai-as-promised": "~7.1.1",
|
||||
"@types/chai-as-promised": "~7.1.2",
|
||||
"@types/sinon": "~7.5.2",
|
||||
"sinon-chai": "~3.5.0",
|
||||
"@types/sinon-chai": "~3.2.3",
|
||||
"proxyquire": "~2.1.3",
|
||||
"@types/proxyquire": "~1.3.28"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,10 @@ export type Entry = File | Directory;
|
||||
*/
|
||||
export type DirectoryHierarchyMap = Map<string, Map<string, vscode.FileType>>;
|
||||
|
||||
export type ZipFileReference = { sourceArchiveZipPath: string, pathWithinSourceArchive: string };
|
||||
export type ZipFileReference = {
|
||||
sourceArchiveZipPath: string;
|
||||
pathWithinSourceArchive: string;
|
||||
};
|
||||
|
||||
/** Encodes a reference to a source file within a zipped source archive into a single URI. */
|
||||
export function encodeSourceArchiveUri(ref: ZipFileReference): vscode.Uri {
|
||||
@@ -87,7 +90,7 @@ export function encodeSourceArchiveUri(ref: ZipFileReference): vscode.Uri {
|
||||
});
|
||||
}
|
||||
|
||||
const sourceArchiveUriAuthorityPattern = /^(\d+)\-(\d+)$/;
|
||||
const sourceArchiveUriAuthorityPattern = /^(\d+)-(\d+)$/;
|
||||
|
||||
class InvalidSourceArchiveUriError extends Error {
|
||||
constructor(uri: vscode.Uri) {
|
||||
@@ -139,8 +142,8 @@ function ensureDir(map: DirectoryHierarchyMap, dir: string) {
|
||||
}
|
||||
|
||||
type Archive = {
|
||||
unzipped: unzipper.CentralDirectory,
|
||||
dirMap: DirectoryHierarchyMap,
|
||||
unzipped: unzipper.CentralDirectory;
|
||||
dirMap: DirectoryHierarchyMap;
|
||||
};
|
||||
|
||||
export class ArchiveFileSystemProvider implements vscode.FileSystemProvider {
|
||||
@@ -169,7 +172,7 @@ export class ArchiveFileSystemProvider implements vscode.FileSystemProvider {
|
||||
async readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
|
||||
const ref = decodeSourceArchiveUri(uri);
|
||||
const archive = await this.getArchive(ref.sourceArchiveZipPath);
|
||||
let contents = archive.dirMap.get(ref.pathWithinSourceArchive);
|
||||
const contents = archive.dirMap.get(ref.pathWithinSourceArchive);
|
||||
const result = contents === undefined ? [] : Array.from(contents.entries());
|
||||
if (result === undefined) {
|
||||
throw vscode.FileSystemError.FileNotFound(uri);
|
||||
@@ -189,7 +192,7 @@ export class ArchiveFileSystemProvider implements vscode.FileSystemProvider {
|
||||
|
||||
// write operations, all disabled
|
||||
|
||||
writeFile(_uri: vscode.Uri, _content: Uint8Array, _options: { create: boolean, overwrite: boolean }): void {
|
||||
writeFile(_uri: vscode.Uri, _content: Uint8Array, _options: { create: boolean; overwrite: boolean }): void {
|
||||
throw this.readOnlyError;
|
||||
}
|
||||
|
||||
@@ -239,7 +242,7 @@ export class ArchiveFileSystemProvider implements vscode.FileSystemProvider {
|
||||
}
|
||||
|
||||
private async _lookupAsFile(uri: vscode.Uri): Promise<File> {
|
||||
let entry = await this._lookup(uri);
|
||||
const entry = await this._lookup(uri);
|
||||
if (entry instanceof File) {
|
||||
return entry;
|
||||
}
|
||||
@@ -254,7 +257,7 @@ export class ArchiveFileSystemProvider implements vscode.FileSystemProvider {
|
||||
|
||||
watch(_resource: vscode.Uri): vscode.Disposable {
|
||||
// ignore, fires for all changes...
|
||||
return new vscode.Disposable(() => { });
|
||||
return new vscode.Disposable(() => { /**/ });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -243,11 +243,12 @@ export class CodeQLCliServer implements Disposable {
|
||||
// Kill the process if it isn't already dead.
|
||||
this.killProcessIfRunning();
|
||||
// Report the error (if there is a stderr then use that otherwise just report the error cod or nodejs error)
|
||||
if (stderrBuffers.length == 0) {
|
||||
throw new Error(`${description} failed: ${err}`)
|
||||
} else {
|
||||
throw new Error(`${description} failed: ${Buffer.concat(stderrBuffers).toString("utf8")}`);
|
||||
}
|
||||
const newError =
|
||||
stderrBuffers.length == 0
|
||||
? new Error(`${description} failed: ${err}`)
|
||||
: new Error(`${description} failed: ${Buffer.concat(stderrBuffers).toString("utf8")}`);
|
||||
newError.stack += (err.stack || '');
|
||||
throw newError;
|
||||
} finally {
|
||||
this.logger.log(Buffer.concat(stderrBuffers).toString("utf8"));
|
||||
// Remove the listeners we set up.
|
||||
@@ -604,7 +605,7 @@ export class CodeQLCliServer implements Disposable {
|
||||
resolveQlpacks(additionalPacks: string[], searchPath?: string[]): Promise<QlpacksInfo> {
|
||||
const args = ['--additional-packs', additionalPacks.join(path.delimiter)];
|
||||
if (searchPath !== undefined) {
|
||||
args.push('--search-path', searchPath.join(path.delimiter));
|
||||
args.push('--search-path', path.join(...searchPath));
|
||||
}
|
||||
|
||||
return this.runJsonCodeQlCliCommand<QlpacksInfo>(
|
||||
|
||||
@@ -64,11 +64,11 @@ const DEBUG_SETTING = new Setting('debug', RUNNING_QUERIES_SETTING);
|
||||
const QUERY_SERVER_RESTARTING_SETTINGS = [NUMBER_OF_THREADS_SETTING, MEMORY_SETTING, DEBUG_SETTING];
|
||||
|
||||
export interface QueryServerConfig {
|
||||
codeQlPath: string,
|
||||
debug: boolean,
|
||||
numThreads: number,
|
||||
queryMemoryMb?: number,
|
||||
timeoutSecs: number,
|
||||
codeQlPath: string;
|
||||
debug: boolean;
|
||||
numThreads: number;
|
||||
queryMemoryMb?: number;
|
||||
timeoutSecs: number;
|
||||
onDidChangeQueryServerConfiguration?: Event<void>;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ export interface QueryServerConfig {
|
||||
const QUERY_HISTORY_SETTINGS = [QUERY_HISTORY_FORMAT_SETTING];
|
||||
|
||||
export interface QueryHistoryConfig {
|
||||
format: string,
|
||||
format: string;
|
||||
onDidChangeQueryHistoryConfiguration: Event<void>;
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ abstract class ConfigListener extends DisposableObject {
|
||||
|
||||
export class DistributionConfigListener extends ConfigListener implements DistributionConfig {
|
||||
public get customCodeQlPath(): string | undefined {
|
||||
return CUSTOM_CODEQL_PATH_SETTING.getValue() ? CUSTOM_CODEQL_PATH_SETTING.getValue() : undefined;
|
||||
return CUSTOM_CODEQL_PATH_SETTING.getValue() || undefined;
|
||||
}
|
||||
|
||||
public get includePrerelease(): boolean {
|
||||
@@ -119,7 +119,7 @@ export class DistributionConfigListener extends ConfigListener implements Distri
|
||||
}
|
||||
|
||||
public get personalAccessToken(): string | undefined {
|
||||
return PERSONAL_ACCESS_TOKEN_SETTING.getValue() ? PERSONAL_ACCESS_TOKEN_SETTING.getValue() : undefined;
|
||||
return PERSONAL_ACCESS_TOKEN_SETTING.getValue() || undefined;
|
||||
}
|
||||
|
||||
public get onDidChangeDistributionConfiguration(): Event<void> {
|
||||
|
||||
@@ -9,7 +9,7 @@ import { clearCacheInDatabase, UserCancellationException } from './run-queries';
|
||||
import * as qsClient from './queryserver-client';
|
||||
import { upgradeDatabase } from './upgrades';
|
||||
|
||||
type ThemableIconPath = { light: string, dark: string } | string;
|
||||
type ThemableIconPath = { light: string; dark: string } | string;
|
||||
|
||||
/**
|
||||
* Path to icons to display next to currently selected database.
|
||||
|
||||
@@ -24,13 +24,13 @@ import { Logger, logger } from './logging';
|
||||
* The name of the key in the workspaceState dictionary in which we
|
||||
* persist the current database across sessions.
|
||||
*/
|
||||
const CURRENT_DB: string = 'currentDatabase';
|
||||
const CURRENT_DB = 'currentDatabase';
|
||||
|
||||
/**
|
||||
* The name of the key in the workspaceState dictionary in which we
|
||||
* persist the list of databases across sessions.
|
||||
*/
|
||||
const DB_LIST: string = 'databaseList';
|
||||
const DB_LIST = 'databaseList';
|
||||
|
||||
export interface DatabaseOptions {
|
||||
displayName?: string;
|
||||
@@ -107,8 +107,8 @@ async function findDataset(parentDirectory: string): Promise<vscode.Uri> {
|
||||
return vscode.Uri.file(dbAbsolutePath);
|
||||
}
|
||||
|
||||
async function findSourceArchive(databasePath: string, silent: boolean = false):
|
||||
Promise<vscode.Uri | undefined> {
|
||||
async function findSourceArchive(databasePath: string, silent = false):
|
||||
Promise<vscode.Uri | undefined> {
|
||||
|
||||
const relativePaths = ['src', 'output/src_archive']
|
||||
|
||||
@@ -128,8 +128,9 @@ async function findSourceArchive(databasePath: string, silent: boolean = false):
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function resolveDatabase(databasePath: string):
|
||||
Promise<DatabaseContents | undefined> {
|
||||
async function resolveDatabase(
|
||||
databasePath: string
|
||||
): Promise<DatabaseContents | undefined> {
|
||||
|
||||
const name = path.basename(databasePath);
|
||||
|
||||
@@ -427,7 +428,7 @@ class DatabaseItemImpl implements DatabaseItem {
|
||||
* `event` fires. If waiting for the event takes too long (by default
|
||||
* >1000ms) log a warning, and resolve to undefined.
|
||||
*/
|
||||
function eventFired<T>(event: vscode.Event<T>, timeoutMs: number = 1000): Promise<T | undefined> {
|
||||
function eventFired<T>(event: vscode.Event<T>, timeoutMs = 1000): Promise<T | undefined> {
|
||||
return new Promise((res, _rej) => {
|
||||
let timeout: NodeJS.Timeout | undefined;
|
||||
let disposable: vscode.Disposable | undefined;
|
||||
@@ -436,22 +437,24 @@ function eventFired<T>(event: vscode.Event<T>, timeoutMs: number = 1000): Promis
|
||||
if (disposable !== undefined) disposable.dispose();
|
||||
}
|
||||
disposable = event(e => {
|
||||
res(e); dispose();
|
||||
res(e);
|
||||
dispose();
|
||||
});
|
||||
timeout = setTimeout(() => {
|
||||
logger.log(`Waiting for event ${event} timed out after ${timeoutMs}ms`);
|
||||
res(undefined); dispose();
|
||||
res(undefined);
|
||||
dispose();
|
||||
}, timeoutMs);
|
||||
});
|
||||
}
|
||||
|
||||
export class DatabaseManager extends DisposableObject {
|
||||
private readonly _onDidChangeDatabaseItem =
|
||||
this.push(new vscode.EventEmitter<DatabaseItem | undefined>());
|
||||
this.push(new vscode.EventEmitter<DatabaseItem | undefined>());
|
||||
readonly onDidChangeDatabaseItem = this._onDidChangeDatabaseItem.event;
|
||||
|
||||
private readonly _onDidChangeCurrentDatabaseItem =
|
||||
this.push(new vscode.EventEmitter<DatabaseItem | undefined>());
|
||||
this.push(new vscode.EventEmitter<DatabaseItem | undefined>());
|
||||
readonly onDidChangeCurrentDatabaseItem = this._onDidChangeCurrentDatabaseItem.event;
|
||||
|
||||
private readonly _databaseItems: DatabaseItemImpl[] = [];
|
||||
@@ -466,7 +469,7 @@ export class DatabaseManager extends DisposableObject {
|
||||
}
|
||||
|
||||
public async openDatabase(uri: vscode.Uri, options?: DatabaseOptions):
|
||||
Promise<DatabaseItem> {
|
||||
Promise<DatabaseItem> {
|
||||
|
||||
const contents = await resolveDatabaseContents(uri);
|
||||
const realOptions = options || {};
|
||||
@@ -526,7 +529,7 @@ export class DatabaseManager extends DisposableObject {
|
||||
}
|
||||
|
||||
private async createDatabaseItemFromPersistedState(state: PersistedDatabaseItem):
|
||||
Promise<DatabaseItem> {
|
||||
Promise<DatabaseItem> {
|
||||
|
||||
let displayName: string | undefined = undefined;
|
||||
let ignoreSourceArchive = false;
|
||||
@@ -584,7 +587,7 @@ export class DatabaseManager extends DisposableObject {
|
||||
}
|
||||
|
||||
public async setCurrentDatabaseItem(item: DatabaseItem | undefined,
|
||||
skipRefresh: boolean = false): Promise<void> {
|
||||
skipRefresh = false): Promise<void> {
|
||||
|
||||
if (!skipRefresh && (item !== undefined)) {
|
||||
await item.refresh(); // Will throw on invalid database.
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ExtensionContext, Event } from "vscode";
|
||||
import { DistributionConfig } from "./config";
|
||||
import { InvocationRateLimiter, InvocationRateLimiterResultKind, ProgressUpdate, showAndLogErrorMessage } from "./helpers";
|
||||
import { logger } from "./logging";
|
||||
import * as helpers from "./helpers";
|
||||
import { getCodeQlCliVersion, tryParseVersionString, Version } from "./cli-version";
|
||||
|
||||
/**
|
||||
@@ -19,7 +20,7 @@ import { getCodeQlCliVersion, tryParseVersionString, Version } from "./cli-versi
|
||||
|
||||
/**
|
||||
* Default value for the owner name of the extension-managed distribution on GitHub.
|
||||
*
|
||||
*
|
||||
* We set the default here rather than as a default config value so that this default is invoked
|
||||
* upon blanking the setting.
|
||||
*/
|
||||
@@ -27,7 +28,7 @@ const DEFAULT_DISTRIBUTION_OWNER_NAME = "github";
|
||||
|
||||
/**
|
||||
* Default value for the repository name of the extension-managed distribution on GitHub.
|
||||
*
|
||||
*
|
||||
* We set the default here rather than as a default config value so that this default is invoked
|
||||
* upon blanking the setting.
|
||||
*/
|
||||
@@ -35,7 +36,7 @@ const DEFAULT_DISTRIBUTION_REPOSITORY_NAME = "codeql-cli-binaries";
|
||||
|
||||
/**
|
||||
* Version constraint for the CLI.
|
||||
*
|
||||
*
|
||||
* This applies to both extension-managed and CLI distributions.
|
||||
*/
|
||||
export const DEFAULT_DISTRIBUTION_VERSION_CONSTRAINT: VersionConstraint = {
|
||||
@@ -46,8 +47,8 @@ export const DEFAULT_DISTRIBUTION_VERSION_CONSTRAINT: VersionConstraint = {
|
||||
}
|
||||
|
||||
export interface DistributionProvider {
|
||||
getCodeQlPathWithoutVersionCheck(): Promise<string | undefined>,
|
||||
onDidChangeDistribution?: Event<void>
|
||||
getCodeQlPathWithoutVersionCheck(): Promise<string | undefined>;
|
||||
onDidChangeDistribution?: Event<void>;
|
||||
}
|
||||
|
||||
export class DistributionManager implements DistributionProvider {
|
||||
@@ -94,18 +95,26 @@ export class DistributionManager implements DistributionProvider {
|
||||
};
|
||||
}
|
||||
|
||||
public async hasDistribution(): Promise<boolean> {
|
||||
const result = await this.getDistribution();
|
||||
return result.kind !== FindDistributionResultKind.NoDistribution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to a possibly-compatible CodeQL launcher binary, or undefined if a binary not be found.
|
||||
*/
|
||||
public async getCodeQlPathWithoutVersionCheck(): Promise<string | undefined> {
|
||||
// Check config setting, then extension specific distribution, then PATH.
|
||||
if (this._config.customCodeQlPath !== undefined) {
|
||||
if (this._config.customCodeQlPath) {
|
||||
if (!await fs.pathExists(this._config.customCodeQlPath)) {
|
||||
showAndLogErrorMessage(`The CodeQL executable path is specified as "${this._config.customCodeQlPath}" ` +
|
||||
"by a configuration setting, but a CodeQL executable could not be found at that path. Please check " +
|
||||
"that a CodeQL executable exists at the specified path or remove the setting.");
|
||||
return undefined;
|
||||
}
|
||||
if (deprecatedCodeQlLauncherName() && this._config.customCodeQlPath.endsWith(deprecatedCodeQlLauncherName()!)) {
|
||||
warnDeprecatedLauncher();
|
||||
}
|
||||
return this._config.customCodeQlPath;
|
||||
}
|
||||
|
||||
@@ -116,8 +125,8 @@ export class DistributionManager implements DistributionProvider {
|
||||
|
||||
if (process.env.PATH) {
|
||||
for (const searchDirectory of process.env.PATH.split(path.delimiter)) {
|
||||
const expectedLauncherPath = path.join(searchDirectory, codeQlLauncherName());
|
||||
if (await fs.pathExists(expectedLauncherPath)) {
|
||||
const expectedLauncherPath = await getExecutableFromDirectory(searchDirectory);
|
||||
if (expectedLauncherPath) {
|
||||
return expectedLauncherPath;
|
||||
}
|
||||
}
|
||||
@@ -130,7 +139,7 @@ export class DistributionManager implements DistributionProvider {
|
||||
/**
|
||||
* Check for updates to the extension-managed distribution. If one has not already been installed,
|
||||
* this will return an update available result with the latest available release.
|
||||
*
|
||||
*
|
||||
* Returns a failed promise if an unexpected error occurs during installation.
|
||||
*/
|
||||
public async checkForUpdatesToExtensionManagedDistribution(
|
||||
@@ -152,7 +161,7 @@ export class DistributionManager implements DistributionProvider {
|
||||
|
||||
/**
|
||||
* Installs a release of the extension-managed distribution.
|
||||
*
|
||||
*
|
||||
* Returns a failed promise if an unexpected error occurs during installation.
|
||||
*/
|
||||
public installExtensionManagedDistributionRelease(release: Release,
|
||||
@@ -181,12 +190,11 @@ class ExtensionSpecificDistributionManager {
|
||||
public async getCodeQlPathWithoutVersionCheck(): Promise<string | undefined> {
|
||||
if (this.getInstalledRelease() !== undefined) {
|
||||
// An extension specific distribution has been installed.
|
||||
const expectedLauncherPath = path.join(this.getDistributionRootPath(), codeQlLauncherName());
|
||||
if (await fs.pathExists(expectedLauncherPath)) {
|
||||
const expectedLauncherPath = await getExecutableFromDirectory(this.getDistributionRootPath(), true);
|
||||
if (expectedLauncherPath) {
|
||||
return expectedLauncherPath;
|
||||
}
|
||||
logger.log(`WARNING: Expected to find a CodeQL CLI executable at ${expectedLauncherPath} but one was not found. ` +
|
||||
"Will try PATH.");
|
||||
|
||||
try {
|
||||
await this.removeDistribution();
|
||||
} catch (e) {
|
||||
@@ -200,7 +208,7 @@ class ExtensionSpecificDistributionManager {
|
||||
/**
|
||||
* Check for updates to the extension-managed distribution. If one has not already been installed,
|
||||
* this will return an update available result with the latest available release.
|
||||
*
|
||||
*
|
||||
* Returns a failed promise if an unexpected error occurs during installation.
|
||||
*/
|
||||
public async checkForUpdatesToDistribution(): Promise<DistributionUpdateCheckResult> {
|
||||
@@ -208,7 +216,11 @@ class ExtensionSpecificDistributionManager {
|
||||
const extensionSpecificRelease = this.getInstalledRelease();
|
||||
const latestRelease = await this.getLatestRelease();
|
||||
|
||||
if (extensionSpecificRelease !== undefined && codeQlPath !== undefined && latestRelease.id === extensionSpecificRelease.id) {
|
||||
if (
|
||||
extensionSpecificRelease !== undefined &&
|
||||
codeQlPath !== undefined &&
|
||||
latestRelease.id === extensionSpecificRelease.id
|
||||
) {
|
||||
return createAlreadyUpToDateResult();
|
||||
}
|
||||
return createUpdateAvailableResult(latestRelease);
|
||||
@@ -216,7 +228,7 @@ class ExtensionSpecificDistributionManager {
|
||||
|
||||
/**
|
||||
* Installs a release of the extension-managed distribution.
|
||||
*
|
||||
*
|
||||
* Returns a failed promise if an unexpected error occurs during installation.
|
||||
*/
|
||||
public async installDistributionRelease(release: Release,
|
||||
@@ -247,8 +259,8 @@ class ExtensionSpecificDistributionManager {
|
||||
|
||||
if (progressCallback && contentLength !== null) {
|
||||
const totalNumBytes = parseInt(contentLength, 10);
|
||||
const bytesToDisplayMB = (numBytes: number) => `${(numBytes / (1024 * 1024)).toFixed(1)} MB`;
|
||||
const updateProgress = () => {
|
||||
const bytesToDisplayMB = (numBytes: number): string => `${(numBytes / (1024 * 1024)).toFixed(1)} MB`;
|
||||
const updateProgress = (): void => {
|
||||
progressCallback({
|
||||
step: numBytesDownloaded,
|
||||
maxStep: totalNumBytes,
|
||||
@@ -282,7 +294,7 @@ class ExtensionSpecificDistributionManager {
|
||||
|
||||
/**
|
||||
* Remove the extension-managed distribution.
|
||||
*
|
||||
*
|
||||
* This should not be called for a distribution that is currently in use, as remove may fail.
|
||||
*/
|
||||
private async removeDistribution(): Promise<void> {
|
||||
@@ -357,7 +369,7 @@ export class ReleasesApiConsumer {
|
||||
this._repoName = repoName;
|
||||
}
|
||||
|
||||
public async getLatestRelease(versionConstraint: VersionConstraint, includePrerelease: boolean = false): Promise<Release> {
|
||||
public async getLatestRelease(versionConstraint: VersionConstraint, includePrerelease = false): Promise<Release> {
|
||||
const apiPath = `/repos/${this._ownerName}/${this._repoName}/releases`;
|
||||
const allReleases: GithubRelease[] = await (await this.makeApiCall(apiPath)).json();
|
||||
const compatibleReleases = allReleases.filter(release => {
|
||||
@@ -428,7 +440,7 @@ export class ReleasesApiConsumer {
|
||||
private async makeRawRequest(
|
||||
requestUrl: string,
|
||||
headers: { [key: string]: string },
|
||||
redirectCount: number = 0): Promise<fetch.Response> {
|
||||
redirectCount = 0): Promise<fetch.Response> {
|
||||
const response = await fetch.default(requestUrl, {
|
||||
headers,
|
||||
redirect: "manual"
|
||||
@@ -480,7 +492,7 @@ export async function extractZipArchive(archivePath: string, outPath: string): P
|
||||
|
||||
/**
|
||||
* Comparison of semantic versions.
|
||||
*
|
||||
*
|
||||
* Returns a positive number if a is greater than b.
|
||||
* Returns 0 if a equals b.
|
||||
* Returns a negative number if a is less than b.
|
||||
@@ -502,7 +514,11 @@ export function versionCompare(a: Version, b: Version): number {
|
||||
}
|
||||
|
||||
function codeQlLauncherName(): string {
|
||||
return (os.platform() === "win32") ? "codeql.cmd" : "codeql";
|
||||
return (os.platform() === "win32") ? "codeql.exe" : "codeql";
|
||||
}
|
||||
|
||||
function deprecatedCodeQlLauncherName(): string | undefined {
|
||||
return (os.platform() === "win32") ? "codeql.cmd" : undefined;
|
||||
}
|
||||
|
||||
function isRedirectStatusCode(statusCode: number): boolean {
|
||||
@@ -520,13 +536,16 @@ export enum FindDistributionResultKind {
|
||||
NoDistribution
|
||||
}
|
||||
|
||||
export type FindDistributionResult = CompatibleDistributionResult | UnknownCompatibilityDistributionResult |
|
||||
IncompatibleDistributionResult | NoDistributionResult;
|
||||
export type FindDistributionResult =
|
||||
| CompatibleDistributionResult
|
||||
| UnknownCompatibilityDistributionResult
|
||||
| IncompatibleDistributionResult
|
||||
| NoDistributionResult;
|
||||
|
||||
interface CompatibleDistributionResult {
|
||||
codeQlPath: string;
|
||||
kind: FindDistributionResultKind.CompatibleDistribution;
|
||||
version: Version
|
||||
version: Version;
|
||||
}
|
||||
|
||||
interface UnknownCompatibilityDistributionResult {
|
||||
@@ -551,11 +570,14 @@ export enum DistributionUpdateCheckResultKind {
|
||||
UpdateAvailable
|
||||
}
|
||||
|
||||
type DistributionUpdateCheckResult = AlreadyCheckedRecentlyResult | AlreadyUpToDateResult | InvalidLocationResult |
|
||||
UpdateAvailableResult;
|
||||
type DistributionUpdateCheckResult =
|
||||
| AlreadyCheckedRecentlyResult
|
||||
| AlreadyUpToDateResult
|
||||
| InvalidLocationResult
|
||||
| UpdateAvailableResult;
|
||||
|
||||
export interface AlreadyCheckedRecentlyResult {
|
||||
kind: DistributionUpdateCheckResultKind.AlreadyCheckedRecentlyResult
|
||||
kind: DistributionUpdateCheckResultKind.AlreadyCheckedRecentlyResult;
|
||||
}
|
||||
|
||||
export interface AlreadyUpToDateResult {
|
||||
@@ -599,6 +621,31 @@ function createUpdateAvailableResult(updatedRelease: Release): UpdateAvailableRe
|
||||
};
|
||||
}
|
||||
|
||||
// Exported for testing
|
||||
export async function getExecutableFromDirectory(directory: string, warnWhenNotFound = false): Promise<string | undefined> {
|
||||
const expectedLauncherPath = path.join(directory, codeQlLauncherName());
|
||||
const deprecatedLauncherName = deprecatedCodeQlLauncherName();
|
||||
const alternateExpectedLauncherPath = deprecatedLauncherName ? path.join(directory, deprecatedLauncherName) : undefined;
|
||||
if (await fs.pathExists(expectedLauncherPath)) {
|
||||
return expectedLauncherPath;
|
||||
} else if (alternateExpectedLauncherPath && (await fs.pathExists(alternateExpectedLauncherPath))) {
|
||||
warnDeprecatedLauncher();
|
||||
return alternateExpectedLauncherPath;
|
||||
}
|
||||
if (warnWhenNotFound) {
|
||||
logger.log(`WARNING: Expected to find a CodeQL CLI executable at ${expectedLauncherPath} but one was not found. ` +
|
||||
"Will try PATH.");
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function warnDeprecatedLauncher() {
|
||||
helpers.showAndLogWarningMessage(
|
||||
`The "${deprecatedCodeQlLauncherName()!}" launcher has been deprecated and will be removed in a future version. ` +
|
||||
`Please use "${codeQlLauncherName()}" instead. It is recommended to update to the latest CodeQL binaries.`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A release on GitHub.
|
||||
*/
|
||||
|
||||
@@ -76,10 +76,10 @@ function registerErrorStubs(excludedCommands: string[], stubGenerator: (command:
|
||||
}
|
||||
|
||||
export async function activate(ctx: ExtensionContext): Promise<void> {
|
||||
// Initialise logging, and ensure all loggers are disposed upon exit.
|
||||
ctx.subscriptions.push(logger);
|
||||
logger.log('Starting CodeQL extension');
|
||||
|
||||
initializeLogging(ctx);
|
||||
|
||||
const distributionConfigListener = new DistributionConfigListener();
|
||||
ctx.subscriptions.push(distributionConfigListener);
|
||||
const distributionManager = new DistributionManager(ctx, distributionConfigListener, DEFAULT_DISTRIBUTION_VERSION_CONSTRAINT);
|
||||
@@ -93,6 +93,7 @@ export async function activate(ctx: ExtensionContext): Promise<void> {
|
||||
interface DistributionUpdateConfig {
|
||||
isUserInitiated: boolean;
|
||||
shouldDisplayMessageWhenNoUpdates: boolean;
|
||||
allowAutoUpdating: boolean;
|
||||
}
|
||||
|
||||
async function installOrUpdateDistributionWithProgressTitle(progressTitle: string, config: DistributionUpdateConfig): Promise<void> {
|
||||
@@ -100,24 +101,28 @@ export async function activate(ctx: ExtensionContext): Promise<void> {
|
||||
const noUpdatesLoggingFunc = config.shouldDisplayMessageWhenNoUpdates ?
|
||||
helpers.showAndLogInformationMessage : async (message: string) => logger.log(message);
|
||||
const result = await distributionManager.checkForUpdatesToExtensionManagedDistribution(minSecondsSinceLastUpdateCheck);
|
||||
|
||||
// We do want to auto update if there is no distribution at all
|
||||
const allowAutoUpdating = config.allowAutoUpdating || !await distributionManager.hasDistribution();
|
||||
|
||||
switch (result.kind) {
|
||||
case DistributionUpdateCheckResultKind.AlreadyCheckedRecentlyResult:
|
||||
logger.log("Didn't perform CodeQL CLI update check since a check was already performed within the previous " +
|
||||
`${minSecondsSinceLastUpdateCheck} seconds.`);
|
||||
break;
|
||||
case DistributionUpdateCheckResultKind.AlreadyUpToDate:
|
||||
await noUpdatesLoggingFunc("CodeQL CLI already up to date.");
|
||||
await noUpdatesLoggingFunc('CodeQL CLI already up to date.');
|
||||
break;
|
||||
case DistributionUpdateCheckResultKind.InvalidLocation:
|
||||
await noUpdatesLoggingFunc("CodeQL CLI is installed externally so could not be updated.");
|
||||
await noUpdatesLoggingFunc('CodeQL CLI is installed externally so could not be updated.');
|
||||
break;
|
||||
case DistributionUpdateCheckResultKind.UpdateAvailable:
|
||||
if (beganMainExtensionActivation) {
|
||||
if (beganMainExtensionActivation || !allowAutoUpdating) {
|
||||
const updateAvailableMessage = `Version "${result.updatedRelease.name}" of the CodeQL CLI is now available. ` +
|
||||
"The update will be installed after Visual Studio Code restarts. Restart now to upgrade?";
|
||||
'Do you wish to upgrade?';
|
||||
await ctx.globalState.update(shouldUpdateOnNextActivationKey, true);
|
||||
if (await helpers.showInformationMessageWithAction(updateAvailableMessage, "Restart and Upgrade")) {
|
||||
await commands.executeCommand("workbench.action.reloadWindow");
|
||||
if (await helpers.showInformationMessageWithAction(updateAvailableMessage, 'Restart and Upgrade')) {
|
||||
await commands.executeCommand('workbench.action.reloadWindow');
|
||||
}
|
||||
} else {
|
||||
const progressOptions: ProgressOptions = {
|
||||
@@ -144,8 +149,12 @@ export async function activate(ctx: ExtensionContext): Promise<void> {
|
||||
isInstallingOrUpdatingDistribution = true;
|
||||
const codeQlInstalled = await distributionManager.getCodeQlPathWithoutVersionCheck() !== undefined;
|
||||
const willUpdateCodeQl = ctx.globalState.get(shouldUpdateOnNextActivationKey);
|
||||
const messageText = willUpdateCodeQl ? "Updating CodeQL CLI" :
|
||||
codeQlInstalled ? "Checking for updates to CodeQL CLI" : "Installing CodeQL CLI";
|
||||
const messageText = willUpdateCodeQl
|
||||
? "Updating CodeQL CLI"
|
||||
: codeQlInstalled
|
||||
? "Checking for updates to CodeQL CLI"
|
||||
: "Installing CodeQL CLI";
|
||||
|
||||
try {
|
||||
await installOrUpdateDistributionWithProgressTitle(messageText, config);
|
||||
} catch (e) {
|
||||
@@ -207,7 +216,8 @@ export async function activate(ctx: ExtensionContext): Promise<void> {
|
||||
if (chosenAction === installActionName) {
|
||||
installOrUpdateThenTryActivate({
|
||||
isUserInitiated: true,
|
||||
shouldDisplayMessageWhenNoUpdates: false
|
||||
shouldDisplayMessageWhenNoUpdates: false,
|
||||
allowAutoUpdating: true
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -216,16 +226,22 @@ export async function activate(ctx: ExtensionContext): Promise<void> {
|
||||
|
||||
ctx.subscriptions.push(distributionConfigListener.onDidChangeDistributionConfiguration(() => installOrUpdateThenTryActivate({
|
||||
isUserInitiated: true,
|
||||
shouldDisplayMessageWhenNoUpdates: false
|
||||
shouldDisplayMessageWhenNoUpdates: false,
|
||||
allowAutoUpdating: true
|
||||
})));
|
||||
ctx.subscriptions.push(commands.registerCommand(checkForUpdatesCommand, () => installOrUpdateThenTryActivate({
|
||||
isUserInitiated: true,
|
||||
shouldDisplayMessageWhenNoUpdates: true
|
||||
shouldDisplayMessageWhenNoUpdates: true,
|
||||
allowAutoUpdating: true
|
||||
})));
|
||||
|
||||
await installOrUpdateThenTryActivate({
|
||||
isUserInitiated: !!ctx.globalState.get(shouldUpdateOnNextActivationKey),
|
||||
shouldDisplayMessageWhenNoUpdates: false
|
||||
shouldDisplayMessageWhenNoUpdates: false,
|
||||
|
||||
// only auto update on startup if the user has previously requested an update
|
||||
// otherwise, ask user to accept the update
|
||||
allowAutoUpdating: !!ctx.globalState.get(shouldUpdateOnNextActivationKey)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -238,10 +254,6 @@ async function activateWithInstalledDistribution(ctx: ExtensionContext, distribu
|
||||
const qlConfigurationListener = await QueryServerConfigListener.createQueryServerConfigListener(distributionManager);
|
||||
ctx.subscriptions.push(qlConfigurationListener);
|
||||
|
||||
ctx.subscriptions.push(queryServerLogger);
|
||||
ctx.subscriptions.push(ideServerLogger);
|
||||
|
||||
|
||||
const cliServer = new CodeQLCliServer(distributionManager, logger);
|
||||
ctx.subscriptions.push(cliServer);
|
||||
|
||||
@@ -327,4 +339,13 @@ async function activateWithInstalledDistribution(ctx: ExtensionContext, distribu
|
||||
ctx.subscriptions.push(client.start());
|
||||
}
|
||||
|
||||
function initializeLogging(ctx: ExtensionContext): void {
|
||||
logger.init(ctx);
|
||||
queryServerLogger.init(ctx);
|
||||
ideServerLogger.init(ctx);
|
||||
ctx.subscriptions.push(logger);
|
||||
ctx.subscriptions.push(queryServerLogger);
|
||||
ctx.subscriptions.push(ideServerLogger);
|
||||
}
|
||||
|
||||
const checkForUpdatesCommand = 'codeQL.checkForUpdatesToCLI';
|
||||
|
||||
@@ -131,7 +131,7 @@ export async function showInformationMessageWithAction(message: string, actionMe
|
||||
/** Gets all active workspace folders that are on the filesystem. */
|
||||
export function getOnDiskWorkspaceFolders() {
|
||||
const workspaceFolders = workspace.workspaceFolders || [];
|
||||
let diskWorkspaceFolders: string[] = [];
|
||||
const diskWorkspaceFolders: string[] = [];
|
||||
for (const workspaceFolder of workspaceFolders) {
|
||||
if (workspaceFolder.uri.scheme === "file")
|
||||
diskWorkspaceFolders.push(workspaceFolder.uri.fsPath)
|
||||
@@ -179,8 +179,12 @@ export class InvocationRateLimiter<T> {
|
||||
public async invokeFunctionIfIntervalElapsed(minSecondsSinceLastInvocation: number): Promise<InvocationRateLimiterResult<T>> {
|
||||
const updateCheckStartDate = this._createDate();
|
||||
const lastInvocationDate = this.getLastInvocationDate();
|
||||
if (minSecondsSinceLastInvocation && lastInvocationDate && lastInvocationDate <= updateCheckStartDate &&
|
||||
lastInvocationDate.getTime() + minSecondsSinceLastInvocation * 1000 > updateCheckStartDate.getTime()) {
|
||||
if (
|
||||
minSecondsSinceLastInvocation &&
|
||||
lastInvocationDate &&
|
||||
lastInvocationDate <= updateCheckStartDate &&
|
||||
lastInvocationDate.getTime() + minSecondsSinceLastInvocation * 1000 > updateCheckStartDate.getTime()
|
||||
) {
|
||||
return createRateLimitedResult();
|
||||
}
|
||||
const result = await this._func();
|
||||
@@ -215,15 +219,15 @@ export enum InvocationRateLimiterResultKind {
|
||||
* The function was invoked and returned the value `result`.
|
||||
*/
|
||||
interface InvokedResult<T> {
|
||||
kind: InvocationRateLimiterResultKind.Invoked,
|
||||
result: T
|
||||
kind: InvocationRateLimiterResultKind.Invoked;
|
||||
result: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function was not invoked as the minimum interval since the last invocation had not elapsed.
|
||||
*/
|
||||
interface RateLimitedResult {
|
||||
kind: InvocationRateLimiterResultKind.RateLimited
|
||||
kind: InvocationRateLimiterResultKind.RateLimited;
|
||||
}
|
||||
|
||||
type InvocationRateLimiterResult<T> = InvokedResult<T> | RateLimitedResult;
|
||||
|
||||
@@ -17,8 +17,8 @@ export async function spawnIdeServer(config: QueryServerConfig): Promise<StreamI
|
||||
['execute', 'language-server'],
|
||||
['--check-errors', 'ON_CHANGE'],
|
||||
ideServerLogger,
|
||||
data => ideServerLogger.logWithoutTrailingNewline(data.toString()),
|
||||
data => ideServerLogger.logWithoutTrailingNewline(data.toString()),
|
||||
data => ideServerLogger.log(data.toString(), { trailingNewline: false }),
|
||||
data => ideServerLogger.log(data.toString(), { trailingNewline: false }),
|
||||
progressReporter
|
||||
);
|
||||
return { writer: child.stdin!, reader: child.stdout! };
|
||||
|
||||
@@ -18,10 +18,10 @@ export interface DatabaseInfo {
|
||||
|
||||
/** Arbitrary query metadata */
|
||||
export interface QueryMetadata {
|
||||
name?: string,
|
||||
description?: string,
|
||||
id?: string,
|
||||
kind?: string
|
||||
name?: string;
|
||||
description?: string;
|
||||
id?: string;
|
||||
kind?: string;
|
||||
}
|
||||
|
||||
export interface PreviousExecution {
|
||||
@@ -70,18 +70,18 @@ export interface SetStateMsg {
|
||||
sortedResultsMap: SortedResultsMap;
|
||||
interpretation: undefined | Interpretation;
|
||||
database: DatabaseInfo;
|
||||
metadata?: QueryMetadata
|
||||
metadata?: QueryMetadata;
|
||||
/**
|
||||
* Whether to keep displaying the old results while rendering the new results.
|
||||
*
|
||||
* This is useful to prevent properties like scroll state being lost when rendering the sorted results after sorting a column.
|
||||
*/
|
||||
shouldKeepOldResultsWhileRendering: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
/** Advance to the next or previous path no in the path viewer */
|
||||
export interface NavigatePathMsg {
|
||||
t: 'navigatePath',
|
||||
t: 'navigatePath';
|
||||
|
||||
/** 1 for next, -1 for previous */
|
||||
direction: number;
|
||||
@@ -100,20 +100,20 @@ interface ViewSourceFileMsg {
|
||||
t: 'viewSourceFile';
|
||||
loc: ResolvableLocationValue;
|
||||
databaseUri: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface ToggleDiagnostics {
|
||||
t: 'toggleDiagnostics';
|
||||
databaseUri: string;
|
||||
metadata?: QueryMetadata
|
||||
metadata?: QueryMetadata;
|
||||
origResultsPaths: ResultsPaths;
|
||||
visible: boolean;
|
||||
kind?: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface ResultViewLoaded {
|
||||
t: 'resultViewLoaded';
|
||||
};
|
||||
}
|
||||
|
||||
export enum SortDirection {
|
||||
asc, desc
|
||||
|
||||
@@ -437,9 +437,9 @@ export class InterfaceManager extends DisposableObject {
|
||||
sourceArchiveUri === undefined
|
||||
? undefined
|
||||
: {
|
||||
sourceArchive: sourceArchiveUri.fsPath,
|
||||
sourceLocationPrefix
|
||||
};
|
||||
sourceArchive: sourceArchiveUri.fsPath,
|
||||
sourceLocationPrefix
|
||||
};
|
||||
interpretation = await this.getTruncatedResults(
|
||||
query.metadata,
|
||||
query.resultsPaths,
|
||||
@@ -471,9 +471,9 @@ export class InterfaceManager extends DisposableObject {
|
||||
sourceArchiveUri === undefined
|
||||
? undefined
|
||||
: {
|
||||
sourceArchive: sourceArchiveUri.fsPath,
|
||||
sourceLocationPrefix
|
||||
};
|
||||
sourceArchive: sourceArchiveUri.fsPath,
|
||||
sourceLocationPrefix
|
||||
};
|
||||
const interpretation = await this.getTruncatedResults(
|
||||
metadata,
|
||||
resultsInfo,
|
||||
|
||||
@@ -1,17 +1,36 @@
|
||||
import { window as Window, OutputChannel, Progress } from 'vscode';
|
||||
import { window as Window, OutputChannel, Progress, ExtensionContext, Disposable } from 'vscode';
|
||||
import { DisposableObject } from 'semmle-vscode-utils';
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
|
||||
interface LogOptions {
|
||||
/** If false, don't output a trailing newline for the log entry. Default true. */
|
||||
trailingNewline?: boolean;
|
||||
|
||||
/** If specified, add this log entry to the log file at the specified location. */
|
||||
additionalLogLocation?: string;
|
||||
}
|
||||
|
||||
export interface Logger {
|
||||
/** Writes the given log message, followed by a newline. */
|
||||
log(message: string): void;
|
||||
/** Writes the given log message, not followed by a newline. */
|
||||
logWithoutTrailingNewline(message: string): void;
|
||||
/** Writes the given log message, optionally followed by a newline. */
|
||||
log(message: string, options?: LogOptions): Promise<void>;
|
||||
/**
|
||||
* Reveal this channel in the UI.
|
||||
*
|
||||
* @param preserveFocus When `true` the channel will not take focus.
|
||||
*/
|
||||
show(preserveFocus?: boolean): void;
|
||||
|
||||
/**
|
||||
* Remove the log at the specified location
|
||||
* @param location log to remove
|
||||
*/
|
||||
removeAdditionalLogLocation(location: string | undefined): void;
|
||||
|
||||
/**
|
||||
* The base location location where all side log files are stored.
|
||||
*/
|
||||
getBaseLocation(): string | undefined;
|
||||
}
|
||||
|
||||
export type ProgressReporter = Progress<{ message: string }>;
|
||||
@@ -19,24 +38,98 @@ export type ProgressReporter = Progress<{ message: string }>;
|
||||
/** A logger that writes messages to an output channel in the Output tab. */
|
||||
export class OutputChannelLogger extends DisposableObject implements Logger {
|
||||
public readonly outputChannel: OutputChannel;
|
||||
private readonly additionalLocations = new Map<string, AdditionalLogLocation>();
|
||||
private additionalLogLocationPath: string | undefined;
|
||||
|
||||
constructor(title: string) {
|
||||
constructor(private title: string) {
|
||||
super();
|
||||
this.outputChannel = Window.createOutputChannel(title);
|
||||
this.push(this.outputChannel);
|
||||
}
|
||||
|
||||
log(message: string) {
|
||||
this.outputChannel.appendLine(message);
|
||||
init(ctx: ExtensionContext): void {
|
||||
this.additionalLogLocationPath = path.join(ctx.storagePath || ctx.globalStoragePath, this.title);
|
||||
|
||||
// clear out any old state from previous runs
|
||||
fs.remove(this.additionalLogLocationPath);
|
||||
}
|
||||
|
||||
logWithoutTrailingNewline(message: string) {
|
||||
this.outputChannel.append(message);
|
||||
/**
|
||||
* This function is asynchronous and will only resolve once the message is written
|
||||
* to the side log (if required). It is not necessary to await the results of this
|
||||
* function if you don't need to guarantee that the log writing is complete before
|
||||
* continuing.
|
||||
*/
|
||||
async log(message: string, options = { } as LogOptions): Promise<void> {
|
||||
if (options.trailingNewline === undefined) {
|
||||
options.trailingNewline = true;
|
||||
}
|
||||
|
||||
if (options.trailingNewline) {
|
||||
this.outputChannel.appendLine(message);
|
||||
} else {
|
||||
this.outputChannel.append(message);
|
||||
}
|
||||
|
||||
if (this.additionalLogLocationPath && options.additionalLogLocation) {
|
||||
const logPath = path.join(this.additionalLogLocationPath, options.additionalLogLocation);
|
||||
let additional = this.additionalLocations.get(logPath);
|
||||
if (!additional) {
|
||||
const msg = `| Log being saved to ${logPath} |`;
|
||||
const separator = new Array(msg.length).fill('-').join('');
|
||||
this.outputChannel.appendLine(separator);
|
||||
this.outputChannel.appendLine(msg);
|
||||
this.outputChannel.appendLine(separator);
|
||||
additional = new AdditionalLogLocation(logPath);
|
||||
this.additionalLocations.set(logPath, additional);
|
||||
this.track(additional);
|
||||
}
|
||||
|
||||
await additional.log(message, options);
|
||||
}
|
||||
}
|
||||
|
||||
show(preserveFocus?: boolean) {
|
||||
show(preserveFocus?: boolean): void {
|
||||
this.outputChannel.show(preserveFocus);
|
||||
}
|
||||
|
||||
removeAdditionalLogLocation(location: string | undefined): void {
|
||||
if (this.additionalLogLocationPath && location) {
|
||||
const logPath = location.startsWith(this.additionalLogLocationPath)
|
||||
? location
|
||||
: path.join(this.additionalLogLocationPath, location);
|
||||
const additional = this.additionalLocations.get(logPath);
|
||||
if (additional) {
|
||||
this.disposeAndStopTracking(additional);
|
||||
this.additionalLocations.delete(logPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getBaseLocation() {
|
||||
return this.additionalLogLocationPath;
|
||||
}
|
||||
}
|
||||
|
||||
class AdditionalLogLocation extends Disposable {
|
||||
constructor(private location: string) {
|
||||
super(() => { /**/ });
|
||||
}
|
||||
|
||||
async log(message: string, options = { } as LogOptions): Promise<void> {
|
||||
if (options.trailingNewline === undefined) {
|
||||
options.trailingNewline = true;
|
||||
}
|
||||
await fs.ensureFile(this.location);
|
||||
|
||||
await fs.appendFile(this.location, message + (options.trailingNewline ? '\n' : ''), {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
}
|
||||
|
||||
async dispose(): Promise<void> {
|
||||
await fs.remove(this.location);
|
||||
}
|
||||
}
|
||||
|
||||
/** The global logger for the extension. */
|
||||
@@ -46,7 +139,9 @@ export const logger = new OutputChannelLogger('CodeQL Extension Log');
|
||||
export const queryServerLogger = new OutputChannelLogger('CodeQL Query Server');
|
||||
|
||||
/** The logger for messages from the language server. */
|
||||
export const ideServerLogger = new OutputChannelLogger('CodeQL Language Server');
|
||||
export const ideServerLogger = new OutputChannelLogger(
|
||||
'CodeQL Language Server'
|
||||
);
|
||||
|
||||
/** The logger for messages from tests. */
|
||||
export const testLogger = new OutputChannelLogger('CodeQL Tests');
|
||||
|
||||
@@ -216,19 +216,19 @@ export interface QlFileSet {
|
||||
/**
|
||||
* The files imported by the given file
|
||||
*/
|
||||
imports: { [key: string]: string[]; };
|
||||
imports: { [key: string]: string[] };
|
||||
/**
|
||||
* An id of each file
|
||||
*/
|
||||
nodeNumbering: { [key: string]: number; };
|
||||
nodeNumbering: { [key: string]: number };
|
||||
/**
|
||||
* The code for each file
|
||||
*/
|
||||
qlCode: { [key: string]: string; };
|
||||
qlCode: { [key: string]: string };
|
||||
/**
|
||||
* The resolution of an import in each directory.
|
||||
*/
|
||||
resolvedDirImports: { [key: string]: { [key: string]: string; }; };
|
||||
resolvedDirImports: { [key: string]: { [key: string]: string } };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -313,6 +313,7 @@ export type Severity = number;
|
||||
* Severity of different messages. This namespace is intentionally not
|
||||
* an enum, see "for the sake of extensibility" comment above.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace Severity {
|
||||
/**
|
||||
* The message is a compilation error.
|
||||
@@ -360,6 +361,7 @@ export type ResultColumnKind = number;
|
||||
* The kind of a result column. This namespace is intentionally not an enum, see "for the sake of
|
||||
* extensibility" comment above.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace ResultColumnKind {
|
||||
/**
|
||||
* A column of type `float`
|
||||
@@ -635,7 +637,7 @@ export interface EvaluateQueriesParams {
|
||||
useSequenceHint: boolean;
|
||||
}
|
||||
|
||||
export type TemplateDefinitions = { [key: string]: TemplateSource; }
|
||||
export type TemplateDefinitions = { [key: string]: TemplateSource }
|
||||
|
||||
/**
|
||||
* A single query that should be run
|
||||
@@ -748,7 +750,7 @@ export interface ResultSet {
|
||||
/**
|
||||
* The type returned when the evaluation is complete
|
||||
*/
|
||||
export interface EvaluationComplete { }
|
||||
export type EvaluationComplete = {};
|
||||
|
||||
/**
|
||||
* The result of a single query
|
||||
@@ -778,6 +780,11 @@ export interface EvaluationResult {
|
||||
* An error message if an error happened
|
||||
*/
|
||||
message?: string;
|
||||
|
||||
/**
|
||||
* Full path to file with all log messages emitted while this query was active, if one exists
|
||||
*/
|
||||
logFileLocation?: string;
|
||||
}
|
||||
|
||||
export type QueryResultType = number;
|
||||
@@ -785,6 +792,7 @@ export type QueryResultType = number;
|
||||
* The result of running a query. This namespace is intentionally not
|
||||
* an enum, see "for the sake of extensibility" comment above.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace QueryResultType {
|
||||
/**
|
||||
* The query ran successfully
|
||||
@@ -857,11 +865,11 @@ export interface WithProgressId<T> {
|
||||
/**
|
||||
* The main body
|
||||
*/
|
||||
body: T,
|
||||
body: T;
|
||||
/**
|
||||
* The id used to report progress updates
|
||||
*/
|
||||
progressId: number
|
||||
progressId: number;
|
||||
}
|
||||
|
||||
export interface ProgressMessage {
|
||||
@@ -930,7 +938,7 @@ export const runUpgrade = new rpc.RequestType<WithProgressId<RunUpgradeParams>,
|
||||
* Request returned to the client to notify completion of a query.
|
||||
* The full runQueries job is completed when all queries are acknowledged.
|
||||
*/
|
||||
export const completeQuery = new rpc.RequestType<EvaluationResult, Object, void, void>('evaluation/queryCompleted');
|
||||
export const completeQuery = new rpc.RequestType<EvaluationResult, Record<string, any>, void, void>('evaluation/queryCompleted');
|
||||
|
||||
/**
|
||||
* A notification that the progress has been changed.
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Discovery } from './discovery';
|
||||
export interface QLPack {
|
||||
name: string;
|
||||
uri: Uri;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Service to discover all available QL packs in a workspace folder.
|
||||
|
||||
@@ -4,6 +4,8 @@ import { ExtensionContext, window as Window } from 'vscode';
|
||||
import { CompletedQuery } from './query-results';
|
||||
import { QueryHistoryConfig } from './config';
|
||||
import { QueryWithResults } from './run-queries';
|
||||
import * as helpers from './helpers';
|
||||
import { logger } from './logging';
|
||||
|
||||
/**
|
||||
* query-history.ts
|
||||
@@ -15,14 +17,14 @@ import { QueryWithResults } from './run-queries';
|
||||
*/
|
||||
|
||||
export type QueryHistoryItemOptions = {
|
||||
label?: string, // user-settable label
|
||||
queryText?: string, // stored query for quick query
|
||||
label?: string; // user-settable label
|
||||
queryText?: string; // stored query for quick query
|
||||
}
|
||||
|
||||
/**
|
||||
* Path to icon to display next to a failed query history item.
|
||||
*/
|
||||
const FAILED_QUERY_HISTORY_ITEM_ICON: string = 'media/red-x.svg';
|
||||
const FAILED_QUERY_HISTORY_ITEM_ICON = 'media/red-x.svg';
|
||||
|
||||
/**
|
||||
* Tree data provider for the query history view.
|
||||
@@ -122,7 +124,7 @@ export class QueryHistoryManager {
|
||||
ctx: ExtensionContext;
|
||||
treeView: vscode.TreeView<CompletedQuery>;
|
||||
selectedCallback: ((item: CompletedQuery) => void) | undefined;
|
||||
lastItemClick: { time: Date, item: CompletedQuery } | undefined;
|
||||
lastItemClick: { time: Date; item: CompletedQuery } | undefined;
|
||||
|
||||
async invokeCallbackOn(queryHistoryItem: CompletedQuery) {
|
||||
if (this.selectedCallback !== undefined) {
|
||||
@@ -144,6 +146,7 @@ export class QueryHistoryManager {
|
||||
|
||||
async handleRemoveHistoryItem(queryHistoryItem: CompletedQuery) {
|
||||
this.treeDataProvider.remove(queryHistoryItem);
|
||||
queryHistoryItem.dispose();
|
||||
const current = this.treeDataProvider.getCurrent();
|
||||
if (current !== undefined) {
|
||||
this.treeView.reveal(current);
|
||||
@@ -187,6 +190,34 @@ export class QueryHistoryManager {
|
||||
}
|
||||
}
|
||||
|
||||
async handleShowQueryLog(queryHistoryItem: CompletedQuery) {
|
||||
if (queryHistoryItem.logFileLocation) {
|
||||
const uri = vscode.Uri.parse(queryHistoryItem.logFileLocation);
|
||||
try {
|
||||
await vscode.window.showTextDocument(uri, {
|
||||
});
|
||||
} catch (e) {
|
||||
if (e.message.includes('Files above 50MB cannot be synchronized with extensions')) {
|
||||
const res = await helpers.showBinaryChoiceDialog('File is too large to open in the editor, do you want to open exterally?');
|
||||
if (res) {
|
||||
try {
|
||||
await vscode.commands.executeCommand('revealFileInOS', uri);
|
||||
} catch (e) {
|
||||
helpers.showAndLogErrorMessage(e.message);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
helpers.showAndLogErrorMessage(`Could not open log file ${queryHistoryItem.logFileLocation}`);
|
||||
logger.log(e.message);
|
||||
logger.log(e.stack);
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
helpers.showAndLogWarningMessage('No log file available');
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
ctx: ExtensionContext,
|
||||
private queryHistoryConfigListener: QueryHistoryConfig,
|
||||
@@ -208,6 +239,7 @@ export class QueryHistoryManager {
|
||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.openQuery', this.handleOpenQuery));
|
||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.removeHistoryItem', this.handleRemoveHistoryItem.bind(this)));
|
||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.setLabel', this.handleSetLabel.bind(this)));
|
||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.showQueryLog', this.handleShowQueryLog.bind(this)));
|
||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.itemClicked', async (item) => {
|
||||
return this.handleItemClicked(item);
|
||||
}));
|
||||
|
||||
@@ -14,7 +14,9 @@ export class CompletedQuery implements QueryWithResults {
|
||||
readonly query: QueryInfo;
|
||||
readonly result: messages.EvaluationResult;
|
||||
readonly database: DatabaseInfo;
|
||||
readonly logFileLocation?: string
|
||||
options: QueryHistoryItemOptions;
|
||||
dispose: () => void;
|
||||
|
||||
/**
|
||||
* Map from result set name to SortedResultSetInfo.
|
||||
@@ -31,15 +33,18 @@ export class CompletedQuery implements QueryWithResults {
|
||||
interpretedResultsSortState: InterpretedResultsSortState | undefined;
|
||||
|
||||
constructor(
|
||||
evalaution: QueryWithResults,
|
||||
evaluation: QueryWithResults,
|
||||
public config: QueryHistoryConfig,
|
||||
) {
|
||||
this.query = evalaution.query;
|
||||
this.result = evalaution.result;
|
||||
this.database = evalaution.database;
|
||||
this.query = evaluation.query;
|
||||
this.result = evaluation.result;
|
||||
this.database = evaluation.database;
|
||||
this.logFileLocation = evaluation.logFileLocation;
|
||||
this.options = evaluation.options;
|
||||
this.dispose = evaluation.dispose;
|
||||
|
||||
this.time = new Date().toLocaleString();
|
||||
this.sortedResultsInfo = new Map();
|
||||
this.options = evalaution.options;
|
||||
}
|
||||
|
||||
get databaseName(): string {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as cp from 'child_process';
|
||||
import * as path from 'path';
|
||||
// Import from the specific module within `semmle-vscode-utils`, rather than via `index.ts`, because
|
||||
// we avoid taking an accidental runtime dependency on `vscode` this way.
|
||||
import { DisposableObject } from 'semmle-vscode-utils/out/disposable-object';
|
||||
@@ -8,9 +9,10 @@ import * as cli from './cli';
|
||||
import { QueryServerConfig } from './config';
|
||||
import { Logger, ProgressReporter } from './logging';
|
||||
import { completeQuery, EvaluationResult, progress, ProgressMessage, WithProgressId } from './messages';
|
||||
import * as messages from './messages';
|
||||
|
||||
type ServerOpts = {
|
||||
logger: Logger
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
/** A running query server process and its associated message connection. */
|
||||
@@ -25,7 +27,7 @@ class ServerProcess implements Disposable {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
dispose(): void {
|
||||
this.logger.log('Stopping query server...');
|
||||
this.connection.dispose();
|
||||
this.child.stdin!.end();
|
||||
@@ -53,6 +55,7 @@ export class QueryServerClient extends DisposableObject {
|
||||
nextCallback: number;
|
||||
nextProgress: number;
|
||||
withProgressReporting: WithProgressReporting;
|
||||
public activeQueryName: string | undefined;
|
||||
|
||||
constructor(readonly config: QueryServerConfig, readonly cliServer: cli.CodeQLCliServer, readonly opts: ServerOpts, withProgressReporting: WithProgressReporting) {
|
||||
super();
|
||||
@@ -70,10 +73,12 @@ export class QueryServerClient extends DisposableObject {
|
||||
this.evaluationResultCallbacks = {};
|
||||
}
|
||||
|
||||
get logger() { return this.opts.logger; }
|
||||
get logger(): Logger {
|
||||
return this.opts.logger;
|
||||
}
|
||||
|
||||
/** Stops the query server by disposing of the current server process. */
|
||||
private stopQueryServer() {
|
||||
private stopQueryServer(): void {
|
||||
if (this.serverProcess !== undefined) {
|
||||
this.disposeAndStopTracking(this.serverProcess);
|
||||
} else {
|
||||
@@ -82,23 +87,23 @@ export class QueryServerClient extends DisposableObject {
|
||||
}
|
||||
|
||||
/** Restarts the query server by disposing of the current server process and then starting a new one. */
|
||||
async restartQueryServer() {
|
||||
async restartQueryServer(): Promise<void> {
|
||||
this.stopQueryServer();
|
||||
await this.startQueryServer();
|
||||
}
|
||||
|
||||
async showLog() {
|
||||
showLog(): void {
|
||||
this.logger.show();
|
||||
}
|
||||
|
||||
/** Starts a new query server process, sending progress messages to the status bar. */
|
||||
async startQueryServer() {
|
||||
async startQueryServer(): Promise<void> {
|
||||
// Use an arrow function to preserve the value of `this`.
|
||||
return this.withProgressReporting((progress, _) => this.startQueryServerImpl(progress));
|
||||
}
|
||||
|
||||
/** Starts a new query server process, sending progress messages to the given reporter. */
|
||||
private async startQueryServerImpl(progressReporter: ProgressReporter) {
|
||||
private async startQueryServerImpl(progressReporter: ProgressReporter): Promise<void> {
|
||||
const ramArgs = await this.cliServer.resolveRam(this.config.queryMemoryMb, progressReporter);
|
||||
const args = ['--threads', this.config.numThreads.toString()].concat(ramArgs);
|
||||
if (this.config.debug) {
|
||||
@@ -110,7 +115,10 @@ export class QueryServerClient extends DisposableObject {
|
||||
['execute', 'query-server'],
|
||||
args,
|
||||
this.logger,
|
||||
data => this.logger.logWithoutTrailingNewline(data.toString()),
|
||||
data => this.logger.log(data.toString(), {
|
||||
trailingNewline: false,
|
||||
additionalLogLocation: this.activeQueryName
|
||||
}),
|
||||
undefined, // no listener for stdout
|
||||
progressReporter
|
||||
);
|
||||
@@ -121,12 +129,16 @@ export class QueryServerClient extends DisposableObject {
|
||||
this.logger.log(`No callback associated with run id ${res.runId}, continuing without executing any callback`);
|
||||
}
|
||||
else {
|
||||
const baseLocation = this.logger.getBaseLocation();
|
||||
if (baseLocation && this.activeQueryName) {
|
||||
res.logFileLocation = path.join(baseLocation, this.activeQueryName);
|
||||
}
|
||||
this.evaluationResultCallbacks[res.runId](res);
|
||||
}
|
||||
return {};
|
||||
})
|
||||
connection.onNotification(progress, res => {
|
||||
let callback = this.progressCallbacks[res.id];
|
||||
const callback = this.progressCallbacks[res.id];
|
||||
if (callback) {
|
||||
callback(res);
|
||||
}
|
||||
@@ -148,7 +160,7 @@ export class QueryServerClient extends DisposableObject {
|
||||
return id;
|
||||
}
|
||||
|
||||
unRegisterCallback(id: number) {
|
||||
unRegisterCallback(id: number): void {
|
||||
delete this.evaluationResultCallbacks[id];
|
||||
}
|
||||
|
||||
@@ -157,8 +169,10 @@ export class QueryServerClient extends DisposableObject {
|
||||
}
|
||||
|
||||
async sendRequest<P, R, E, RO>(type: RequestType<WithProgressId<P>, R, E, RO>, parameter: P, token?: CancellationToken, progress?: (res: ProgressMessage) => void): Promise<R> {
|
||||
let id = this.nextProgress++;
|
||||
const id = this.nextProgress++;
|
||||
this.progressCallbacks[id] = progress;
|
||||
|
||||
this.updateActiveQuery(type.method, parameter);
|
||||
try {
|
||||
if (this.serverProcess === undefined) {
|
||||
throw new Error('No query server process found.');
|
||||
@@ -168,4 +182,19 @@ export class QueryServerClient extends DisposableObject {
|
||||
delete this.progressCallbacks[id];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the active query every time there is a new request to compile.
|
||||
* The active query is used to specify the side log.
|
||||
*
|
||||
* This isn't ideal because in situations where there are queries running
|
||||
* in parallel, each query's log messages are interleaved. Fixing this
|
||||
* properly will require a change in the query server.
|
||||
*/
|
||||
private updateActiveQuery(method: string, parameter: any): void {
|
||||
if (method === messages.compileQuery.method) {
|
||||
const queryPath = parameter?.queryToCheck?.queryPath || 'unknown';
|
||||
this.activeQueryName = `query-${path.basename(queryPath)}-${this.nextProgress}.log`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export function isQuickQueryPath(queryPath: string): boolean {
|
||||
|
||||
async function getQlPackFor(cliServer: CodeQLCliServer, dbschemePath: string): Promise<string> {
|
||||
const qlpacks = await cliServer.resolveQlpacks(helpers.getOnDiskWorkspaceFolders());
|
||||
const packs: { packDir: string | undefined, packName: string }[] =
|
||||
const packs: { packDir: string | undefined; packName: string }[] =
|
||||
Object.entries(qlpacks).map(([packName, dirs]) => {
|
||||
if (dirs.length < 1) {
|
||||
logger.log(`In getQlPackFor ${dbschemePath}, qlpack ${packName} has no directories`);
|
||||
@@ -60,7 +60,7 @@ function getBaseText(dbschemeBase: string) {
|
||||
return 'select ""';
|
||||
}
|
||||
|
||||
async function getQuickQueriesDir(ctx: ExtensionContext): Promise<string> {
|
||||
function getQuickQueriesDir(ctx: ExtensionContext): string {
|
||||
const storagePath = ctx.storagePath;
|
||||
if (storagePath === undefined) {
|
||||
throw new Error('Workspace storage path is undefined');
|
||||
|
||||
@@ -38,12 +38,12 @@ export function getResult(sarif: sarif.Log, key: Result): sarif.Result | undefin
|
||||
* Looks up a specific path in a result set.
|
||||
*/
|
||||
export function getPath(sarif: sarif.Log, key: Path): sarif.ThreadFlow | undefined {
|
||||
let result = getResult(sarif, key);
|
||||
const result = getResult(sarif, key);
|
||||
if (result === undefined) return undefined;
|
||||
let index = -1;
|
||||
if (result.codeFlows === undefined) return undefined;
|
||||
for (let codeFlows of result.codeFlows) {
|
||||
for (let threadFlow of codeFlows.threadFlows) {
|
||||
for (const codeFlows of result.codeFlows) {
|
||||
for (const threadFlow of codeFlows.threadFlows) {
|
||||
++index;
|
||||
if (index == key.pathIndex)
|
||||
return threadFlow;
|
||||
@@ -56,7 +56,7 @@ export function getPath(sarif: sarif.Log, key: Path): sarif.ThreadFlow | undefin
|
||||
* Looks up a specific path node in a result set.
|
||||
*/
|
||||
export function getPathNode(sarif: sarif.Log, key: PathNode): sarif.Location | undefined {
|
||||
let path = getPath(sarif, key);
|
||||
const path = getPath(sarif, key);
|
||||
if (path === undefined) return undefined;
|
||||
return path.locations[key.pathNodeIndex];
|
||||
}
|
||||
@@ -85,7 +85,7 @@ export function equalsNotUndefined(key1: PathNode | undefined, key2: PathNode |
|
||||
*/
|
||||
export function getAllPaths(result: sarif.Result): sarif.ThreadFlow[] {
|
||||
if (result.codeFlows === undefined) return [];
|
||||
let paths = [];
|
||||
const paths = [];
|
||||
for (const codeFlow of result.codeFlows) {
|
||||
for (const threadFlow of codeFlow.threadFlows) {
|
||||
paths.push(threadFlow);
|
||||
|
||||
@@ -136,10 +136,13 @@ export class QueryInfo {
|
||||
},
|
||||
queryToCheck: this.program,
|
||||
resultPath: this.compiledQueryPath,
|
||||
target: !!this.quickEvalPosition ? { quickEval: { quickEvalPos: this.quickEvalPosition } } : { query: {} }
|
||||
target: this.quickEvalPosition ? {
|
||||
quickEval: { quickEvalPos: this.quickEvalPosition }
|
||||
} : {
|
||||
query: {}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
compiled = await helpers.withProgress({
|
||||
location: vscode.ProgressLocation.Notification,
|
||||
title: "Compiling Query",
|
||||
@@ -170,10 +173,13 @@ export interface QueryWithResults {
|
||||
readonly result: messages.EvaluationResult;
|
||||
readonly database: DatabaseInfo;
|
||||
readonly options: QueryHistoryItemOptions;
|
||||
readonly logFileLocation?: string;
|
||||
readonly dispose: () => void;
|
||||
}
|
||||
|
||||
export async function clearCacheInDatabase(qs: qsClient.QueryServerClient, dbItem: DatabaseItem):
|
||||
Promise<messages.ClearCacheResult> {
|
||||
export async function clearCacheInDatabase(
|
||||
qs: qsClient.QueryServerClient, dbItem: DatabaseItem
|
||||
): Promise<messages.ClearCacheResult> {
|
||||
if (dbItem.contents === undefined) {
|
||||
throw new Error('Can\'t clear the cache in an invalid database.');
|
||||
}
|
||||
@@ -253,7 +259,7 @@ async function checkDbschemeCompatibility(
|
||||
|
||||
if (query.dbItem.contents !== undefined && query.dbItem.contents.dbSchemeUri !== undefined) {
|
||||
const { scripts, finalDbscheme } = await cliServer.resolveUpgrades(query.dbItem.contents.dbSchemeUri.fsPath, searchPath);
|
||||
async function hash(filename: string): Promise<string> {
|
||||
const hash = async function (filename: string): Promise<string> {
|
||||
return crypto.createHash('sha256').update(await fs.readFile(filename)).digest('hex');
|
||||
}
|
||||
|
||||
@@ -289,7 +295,7 @@ async function checkDbschemeCompatibility(
|
||||
}
|
||||
|
||||
/** Prompts the user to save `document` if it has unsaved changes. */
|
||||
async function promptUserToSaveChanges(document: vscode.TextDocument) {
|
||||
async function promptUserToSaveChanges(document: vscode.TextDocument): Promise<void> {
|
||||
if (document.isDirty) {
|
||||
// TODO: add 'always save' button which records preference in configuration
|
||||
if (await helpers.showBinaryChoiceDialog('Query file has unsaved changes. Save now?')) {
|
||||
@@ -299,8 +305,8 @@ async function promptUserToSaveChanges(document: vscode.TextDocument) {
|
||||
}
|
||||
|
||||
type SelectedQuery = {
|
||||
queryPath: string,
|
||||
quickEvalPosition?: messages.Position
|
||||
queryPath: string;
|
||||
quickEvalPosition?: messages.Position;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -313,7 +319,7 @@ type SelectedQuery = {
|
||||
* @param selectedResourceUri The selected resource when the command was run.
|
||||
* @param quickEval Whether the command being run is `Quick Evaluation`.
|
||||
*/
|
||||
async function determineSelectedQuery(selectedResourceUri: vscode.Uri | undefined, quickEval: boolean): Promise<SelectedQuery> {
|
||||
export async function determineSelectedQuery(selectedResourceUri: vscode.Uri | undefined, quickEval: boolean): Promise<SelectedQuery> {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
|
||||
// Choose which QL file to use.
|
||||
@@ -336,8 +342,15 @@ async function determineSelectedQuery(selectedResourceUri: vscode.Uri | undefine
|
||||
}
|
||||
const queryPath = queryUri.fsPath || '';
|
||||
|
||||
if (!queryPath.endsWith('.ql')) {
|
||||
throw new Error('The selected resource is not a CodeQL query file; It should have the extension ".ql".');
|
||||
if (quickEval) {
|
||||
if (!(queryPath.endsWith('.ql') || queryPath.endsWith('.qll'))) {
|
||||
throw new Error('The selected resource is not a CodeQL file; It should have the extension ".ql" or ".qll".');
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!(queryPath.endsWith('.ql'))) {
|
||||
throw new Error('The selected resource is not a CodeQL query file; It should have the extension ".ql".');
|
||||
}
|
||||
}
|
||||
|
||||
// Whether we chose the file from the active editor or from a context menu,
|
||||
@@ -450,7 +463,11 @@ export async function compileAndRunQueryAgainstDatabase(
|
||||
name: db.name,
|
||||
databaseUri: db.databaseUri.toString(true)
|
||||
},
|
||||
options: historyItemOptions
|
||||
options: historyItemOptions,
|
||||
logFileLocation: result.logFileLocation,
|
||||
dispose: () => {
|
||||
qs.logger.removeAdditionalLogLocation(result.logFileLocation);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
// Error dialogs are limited in size and scrollability,
|
||||
@@ -459,7 +476,7 @@ export async function compileAndRunQueryAgainstDatabase(
|
||||
// However we don't show quick eval errors there so we need to display them anyway.
|
||||
qs.logger.log(`Failed to compile query ${query.program.queryPath} against database scheme ${query.program.dbschemePath}:`);
|
||||
|
||||
let formattedMessages: string[] = [];
|
||||
const formattedMessages: string[] = [];
|
||||
|
||||
for (const error of errors) {
|
||||
const message = error.message || "[no error message available]";
|
||||
@@ -486,7 +503,7 @@ function createSyntheticResult(
|
||||
historyItemOptions: QueryHistoryItemOptions,
|
||||
message: string,
|
||||
resultType: number
|
||||
) {
|
||||
): QueryWithResults {
|
||||
|
||||
return {
|
||||
query,
|
||||
@@ -502,5 +519,6 @@ function createSyntheticResult(
|
||||
databaseUri: db.databaseUri.toString(true)
|
||||
},
|
||||
options: historyItemOptions,
|
||||
dispose: () => { /**/ },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ import * as path from "path"
|
||||
import { LocationStyle, ResolvableLocationValue } from "semmle-bqrs";
|
||||
|
||||
export interface SarifLink {
|
||||
dest: number
|
||||
text: string
|
||||
dest: number;
|
||||
text: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ type ParsedSarifLocation =
|
||||
// doesn't really need to see. We ensure that `userVisibleFile` will not contain
|
||||
// that, and is appropriate for display in the UI.
|
||||
& { userVisibleFile: string }
|
||||
| { t: 'NoLocation', hint: string };
|
||||
| { t: 'NoLocation'; hint: string };
|
||||
|
||||
export type SarifMessageComponent = string | SarifLink
|
||||
|
||||
@@ -27,7 +27,7 @@ export function unescapeSarifText(message: string): string {
|
||||
}
|
||||
|
||||
export function parseSarifPlainTextMessage(message: string): SarifMessageComponent[] {
|
||||
let results: SarifMessageComponent[] = [];
|
||||
const results: SarifMessageComponent[] = [];
|
||||
|
||||
// We want something like "[linkText](4)", except that "[" and "]" may be escaped. The lookbehind asserts
|
||||
// that the initial [ is not escaped. Then we parse a link text with "[" and "]" escaped. Then we parse the numerical target.
|
||||
|
||||
@@ -88,7 +88,7 @@ export class QLTestAdapter extends DisposableObject implements TestAdapter {
|
||||
new EventEmitter<TestLoadStartedEvent | TestLoadFinishedEvent>());
|
||||
private readonly _testStates = this.push(
|
||||
new EventEmitter<TestRunStartedEvent | TestRunFinishedEvent | TestSuiteEvent |
|
||||
TestEvent>());
|
||||
TestEvent>());
|
||||
private readonly _autorun = this.push(new EventEmitter<void>());
|
||||
private runningTask?: vscode.CancellationTokenSource = undefined;
|
||||
|
||||
@@ -109,7 +109,7 @@ export class QLTestAdapter extends DisposableObject implements TestAdapter {
|
||||
}
|
||||
|
||||
public get testStates(): Event<TestRunStartedEvent | TestRunFinishedEvent | TestSuiteEvent |
|
||||
TestEvent> {
|
||||
TestEvent> {
|
||||
|
||||
return this._testStates.event;
|
||||
}
|
||||
@@ -119,7 +119,7 @@ export class QLTestAdapter extends DisposableObject implements TestAdapter {
|
||||
}
|
||||
|
||||
private static createTestOrSuiteInfos(testNodes: readonly QLTestNode[]):
|
||||
(TestSuiteInfo | TestInfo)[] {
|
||||
(TestSuiteInfo | TestInfo)[] {
|
||||
|
||||
return testNodes.map((childNode) => {
|
||||
return QLTestAdapter.createTestOrSuiteInfo(childNode);
|
||||
@@ -149,7 +149,7 @@ export class QLTestAdapter extends DisposableObject implements TestAdapter {
|
||||
}
|
||||
|
||||
private static createTestSuiteInfo(testDirectory: QLTestDirectory, label: string):
|
||||
TestSuiteInfo {
|
||||
TestSuiteInfo {
|
||||
|
||||
return {
|
||||
type: 'suite',
|
||||
|
||||
@@ -19,8 +19,9 @@ const MAX_UPGRADE_MESSAGE_LINES = 10;
|
||||
* Reports errors to both the user and the console.
|
||||
* @returns the `UpgradeParams` needed to start the upgrade, if the upgrade is possible and was confirmed by the user, or `undefined` otherwise.
|
||||
*/
|
||||
async function checkAndConfirmDatabaseUpgrade(qs: qsClient.QueryServerClient, db: DatabaseItem, targetDbScheme: vscode.Uri, upgradesDirectories: vscode.Uri[]):
|
||||
Promise<messages.UpgradeParams | undefined> {
|
||||
async function checkAndConfirmDatabaseUpgrade(
|
||||
qs: qsClient.QueryServerClient, db: DatabaseItem, targetDbScheme: vscode.Uri, upgradesDirectories: vscode.Uri[]
|
||||
): Promise<messages.UpgradeParams | undefined> {
|
||||
if (db.contents === undefined || db.contents.dbSchemeUri === undefined) {
|
||||
helpers.showAndLogErrorMessage("Database is invalid, and cannot be upgraded.");
|
||||
return;
|
||||
@@ -80,7 +81,7 @@ async function checkAndConfirmDatabaseUpgrade(qs: qsClient.QueryServerClient, db
|
||||
const showLogItem: vscode.MessageItem = { title: 'No, Show Changes', isCloseAffordance: true };
|
||||
const yesItem = { title: 'Yes', isCloseAffordance: false };
|
||||
const noItem = { title: 'No', isCloseAffordance: true }
|
||||
let dialogOptions: vscode.MessageItem[] = [yesItem, noItem];
|
||||
const dialogOptions: vscode.MessageItem[] = [yesItem, noItem];
|
||||
|
||||
let messageLines = descriptionMessage.split('\n');
|
||||
if (messageLines.length > MAX_UPGRADE_MESSAGE_LINES) {
|
||||
@@ -110,8 +111,9 @@ async function checkAndConfirmDatabaseUpgrade(qs: qsClient.QueryServerClient, db
|
||||
* First performs a dry-run and prompts the user to confirm the upgrade.
|
||||
* Reports errors during compilation and evaluation of upgrades to the user.
|
||||
*/
|
||||
export async function upgradeDatabase(qs: qsClient.QueryServerClient, db: DatabaseItem, targetDbScheme: vscode.Uri, upgradesDirectories: vscode.Uri[]):
|
||||
Promise<messages.RunUpgradeResult | undefined> {
|
||||
export async function upgradeDatabase(
|
||||
qs: qsClient.QueryServerClient, db: DatabaseItem, targetDbScheme: vscode.Uri, upgradesDirectories: vscode.Uri[]
|
||||
): Promise<messages.RunUpgradeResult | undefined> {
|
||||
const upgradeParams = await checkAndConfirmDatabaseUpgrade(qs, db, targetDbScheme, upgradesDirectories);
|
||||
|
||||
if (upgradeParams === undefined) {
|
||||
@@ -150,8 +152,9 @@ export async function upgradeDatabase(qs: qsClient.QueryServerClient, db: Databa
|
||||
}
|
||||
}
|
||||
|
||||
async function checkDatabaseUpgrade(qs: qsClient.QueryServerClient, upgradeParams: messages.UpgradeParams):
|
||||
Promise<messages.CheckUpgradeResult> {
|
||||
async function checkDatabaseUpgrade(
|
||||
qs: qsClient.QueryServerClient, upgradeParams: messages.UpgradeParams
|
||||
): Promise<messages.CheckUpgradeResult> {
|
||||
return helpers.withProgress({
|
||||
location: vscode.ProgressLocation.Notification,
|
||||
title: "Checking for database upgrades",
|
||||
@@ -159,8 +162,9 @@ async function checkDatabaseUpgrade(qs: qsClient.QueryServerClient, upgradeParam
|
||||
}, (progress, token) => qs.sendRequest(messages.checkUpgrade, upgradeParams, token, progress));
|
||||
}
|
||||
|
||||
async function compileDatabaseUpgrade(qs: qsClient.QueryServerClient, upgradeParams: messages.UpgradeParams):
|
||||
Promise<messages.CompileUpgradeResult> {
|
||||
async function compileDatabaseUpgrade(
|
||||
qs: qsClient.QueryServerClient, upgradeParams: messages.UpgradeParams
|
||||
): Promise<messages.CompileUpgradeResult> {
|
||||
const params: messages.CompileUpgradeParams = {
|
||||
upgrade: upgradeParams,
|
||||
upgradeTempDir: upgradesTmpDir.name
|
||||
@@ -173,8 +177,9 @@ async function compileDatabaseUpgrade(qs: qsClient.QueryServerClient, upgradePar
|
||||
}, (progress, token) => qs.sendRequest(messages.compileUpgrade, params, token, progress));
|
||||
}
|
||||
|
||||
async function runDatabaseUpgrade(qs: qsClient.QueryServerClient, db: DatabaseItem, upgrades: messages.CompiledUpgrades):
|
||||
Promise<messages.RunUpgradeResult> {
|
||||
async function runDatabaseUpgrade(
|
||||
qs: qsClient.QueryServerClient, db: DatabaseItem, upgrades: messages.CompiledUpgrades
|
||||
): Promise<messages.RunUpgradeResult> {
|
||||
|
||||
if (db.contents === undefined || db.contents.datasetUri === undefined) {
|
||||
throw new Error('Can\'t upgrade an invalid database.');
|
||||
|
||||
@@ -4,5 +4,5 @@ module.exports = {
|
||||
},
|
||||
env: {
|
||||
browser: true
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ export class PathTable extends React.Component<PathTableProps, PathTableState> {
|
||||
|
||||
function renderRelatedLocations(msg: string, relatedLocations: Sarif.Location[]): JSX.Element[] {
|
||||
const relatedLocationsById: { [k: string]: Sarif.Location } = {};
|
||||
for (let loc of relatedLocations) {
|
||||
for (const loc of relatedLocations) {
|
||||
relatedLocationsById[loc.id!] = loc;
|
||||
}
|
||||
|
||||
@@ -239,7 +239,7 @@ export class PathTable extends React.Component<PathTableProps, PathTableState> {
|
||||
const additionalMsg = step.location !== undefined ?
|
||||
renderSarifLocation(step.location, pathNodeKey) :
|
||||
'';
|
||||
let isSelected = Keys.equalsNotUndefined(this.state.selectedPathNode, pathNodeKey);
|
||||
const isSelected = Keys.equalsNotUndefined(this.state.selectedPathNode, pathNodeKey);
|
||||
const stepIndex = pathNodeIndex + 1; // Convert to 1-based
|
||||
const zebraIndex = resultIndex + stepIndex;
|
||||
rows.push(
|
||||
@@ -271,23 +271,23 @@ export class PathTable extends React.Component<PathTableProps, PathTableState> {
|
||||
|
||||
private handleNavigationEvent(event: NavigationEvent) {
|
||||
this.setState(prevState => {
|
||||
let { selectedPathNode } = prevState;
|
||||
const { selectedPathNode } = prevState;
|
||||
if (selectedPathNode === undefined) return prevState;
|
||||
|
||||
let path = Keys.getPath(this.props.resultSet.sarif, selectedPathNode);
|
||||
const path = Keys.getPath(this.props.resultSet.sarif, selectedPathNode);
|
||||
if (path === undefined) return prevState;
|
||||
|
||||
let nextIndex = selectedPathNode.pathNodeIndex + event.direction;
|
||||
const nextIndex = selectedPathNode.pathNodeIndex + event.direction;
|
||||
if (nextIndex < 0 || nextIndex >= path.locations.length) return prevState;
|
||||
|
||||
let sarifLoc = path.locations[nextIndex].location;
|
||||
const sarifLoc = path.locations[nextIndex].location;
|
||||
if (sarifLoc === undefined) return prevState;
|
||||
|
||||
let loc = parseSarifLocation(sarifLoc, this.props.resultSet.sourceLocationPrefix);
|
||||
const loc = parseSarifLocation(sarifLoc, this.props.resultSet.sourceLocationPrefix);
|
||||
if (loc.t === 'NoLocation') return prevState;
|
||||
|
||||
jumpToLocation(loc, this.props.databaseUri);
|
||||
let newSelection = { ...selectedPathNode, pathNodeIndex: nextIndex };
|
||||
const newSelection = { ...selectedPathNode, pathNodeIndex: nextIndex };
|
||||
return { ...prevState, selectedPathNode: newSelection };
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,22 +4,22 @@ export type EventHandler<T> = (event: T) => void;
|
||||
* A set of listeners for events of type `T`.
|
||||
*/
|
||||
export class EventHandlers<T> {
|
||||
private handlers: EventHandler<T>[] = [];
|
||||
private handlers: EventHandler<T>[] = [];
|
||||
|
||||
public addListener(handler: EventHandler<T>) {
|
||||
this.handlers.push(handler);
|
||||
}
|
||||
public addListener(handler: EventHandler<T>) {
|
||||
this.handlers.push(handler);
|
||||
}
|
||||
|
||||
public removeListener(handler: EventHandler<T>) {
|
||||
let index = this.handlers.indexOf(handler);
|
||||
if (index !== -1) {
|
||||
this.handlers.splice(index, 1);
|
||||
}
|
||||
public removeListener(handler: EventHandler<T>) {
|
||||
const index = this.handlers.indexOf(handler);
|
||||
if (index !== -1) {
|
||||
this.handlers.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public fire(event: T) {
|
||||
for (let handler of this.handlers) {
|
||||
handler(event);
|
||||
}
|
||||
public fire(event: T) {
|
||||
for (const handler of this.handlers) {
|
||||
handler(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ async function* getChunkIterator(response: Response): AsyncIterableIterator<Uint
|
||||
}
|
||||
|
||||
function translatePrimitiveValue(value: PrimitiveColumnValue, type: PrimitiveTypeKind):
|
||||
ResultValue {
|
||||
ResultValue {
|
||||
|
||||
switch (type) {
|
||||
case 'i':
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import * as assert from 'assert';
|
||||
import * as chai from 'chai';
|
||||
import * as chaiAsPromised from 'chai-as-promised';
|
||||
import 'mocha';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import * as determiningSelectedQueryTest from './determining-selected-query-test';
|
||||
|
||||
chai.use(chaiAsPromised);
|
||||
|
||||
describe('launching with a minimal workspace', async () => {
|
||||
const ext = vscode.extensions.getExtension('GitHub.vscode-codeql');
|
||||
@@ -24,3 +30,5 @@ describe('launching with a minimal workspace', async () => {
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
|
||||
determiningSelectedQueryTest.run();
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { expect } from 'chai';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import { Uri } from 'vscode';
|
||||
import { determineSelectedQuery } from '../../run-queries';
|
||||
|
||||
async function showQlDocument(name: string): Promise<vscode.TextDocument> {
|
||||
const folderPath = vscode.workspace.workspaceFolders![0].uri.fsPath;
|
||||
const documentPath = path.resolve(folderPath, name);
|
||||
const document = await vscode.workspace.openTextDocument(documentPath);
|
||||
await vscode.window.showTextDocument(document!);
|
||||
return document;
|
||||
}
|
||||
|
||||
export function run() {
|
||||
describe('Determining selected query', async () => {
|
||||
it('should allow ql files to be queried', async () => {
|
||||
const q = await determineSelectedQuery(Uri.parse('file:///tmp/queryname.ql'), false);
|
||||
expect(q.queryPath).to.equal(path.join('/', 'tmp', 'queryname.ql'));
|
||||
expect(q.quickEvalPosition).to.equal(undefined);
|
||||
});
|
||||
|
||||
it('should allow ql files to be quick-evaled', async () => {
|
||||
const doc = await showQlDocument('query.ql');
|
||||
const q = await determineSelectedQuery(doc.uri, true);
|
||||
expect(q.queryPath).to.satisfy((p: string) => p.endsWith(path.join('ql-vscode', 'test', 'data', 'query.ql')));
|
||||
});
|
||||
|
||||
it('should allow qll files to be quick-evaled', async () => {
|
||||
const doc = await showQlDocument('library.qll');
|
||||
const q = await determineSelectedQuery(doc.uri, true);
|
||||
expect(q.queryPath).to.satisfy((p: string) => p.endsWith(path.join('ql-vscode', 'test', 'data', 'library.qll')));
|
||||
});
|
||||
|
||||
it('should reject non-ql files when running a query', async () => {
|
||||
await expect(determineSelectedQuery(Uri.parse('file:///tmp/queryname.txt'), false)).to.be.rejectedWith(Error, 'The selected resource is not a CodeQL query file');
|
||||
await expect(determineSelectedQuery(Uri.parse('file:///tmp/queryname.qll'), false)).to.be.rejectedWith(Error, 'The selected resource is not a CodeQL query file');
|
||||
});
|
||||
|
||||
it('should reject non-ql[l] files when running a quick eval', async () => {
|
||||
await expect(determineSelectedQuery(Uri.parse('file:///tmp/queryname.txt'), true)).to.be.rejectedWith(Error, 'The selected resource is not a CodeQL file');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -15,7 +15,7 @@ describe("archive filesystem provider", () => {
|
||||
});
|
||||
|
||||
describe('source archive uri encoding', function() {
|
||||
const testCases: { name: string, input: ZipFileReference }[] = [
|
||||
const testCases: { name: string; input: ZipFileReference }[] = [
|
||||
{
|
||||
name: 'mixed case and unicode',
|
||||
input: { sourceArchiveZipPath: "/I-\u2665-codeql.zip", pathWithinSourceArchive: "/foo/bar" }
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
import { expect } from "chai";
|
||||
import * as chai from "chai";
|
||||
import * as path from "path";
|
||||
import * as fetch from "node-fetch";
|
||||
import 'chai/register-should';
|
||||
import * as sinonChai from 'sinon-chai';
|
||||
import * as sinon from 'sinon';
|
||||
import * as pq from "proxyquire";
|
||||
import "mocha";
|
||||
|
||||
import { Version } from "../../cli-version";
|
||||
import { GithubRelease, GithubReleaseAsset, ReleasesApiConsumer, versionCompare } from "../../distribution"
|
||||
import { GithubRelease, GithubReleaseAsset, ReleasesApiConsumer, versionCompare } from "../../distribution";
|
||||
|
||||
const proxyquire = pq.noPreserveCache();
|
||||
chai.use(sinonChai);
|
||||
const expect = chai.expect;
|
||||
|
||||
describe("Releases API consumer", () => {
|
||||
const owner = "someowner";
|
||||
@@ -176,3 +186,104 @@ describe("Release version ordering", () => {
|
||||
expect(versionCompare(createVersion(2, 1, 0, "alpha.1", "abcdef0"), createVersion(2, 1, 0, "alpha.1", "bcdef01"))).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Launcher path', () => {
|
||||
let sandbox: sinon.SinonSandbox;
|
||||
let warnSpy: sinon.SinonSpy;
|
||||
let logSpy: sinon.SinonSpy;
|
||||
let fsSpy: sinon.SinonSpy;
|
||||
let platformSpy: sinon.SinonSpy;
|
||||
|
||||
let getExecutableFromDirectory: Function;
|
||||
|
||||
let launcherThatExists = '';
|
||||
|
||||
beforeEach(() => {
|
||||
getExecutableFromDirectory = createModule().getExecutableFromDirectory;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('should not warn with proper launcher name', async () => {
|
||||
launcherThatExists = 'codeql.exe';
|
||||
const result = await getExecutableFromDirectory('abc');
|
||||
expect(fsSpy).to.have.been.calledWith(`abc${path.sep}codeql.exe`);
|
||||
|
||||
// correct launcher has been found, so alternate one not looked for
|
||||
expect(fsSpy).not.to.have.been.calledWith(`abc${path.sep}codeql.cmd`);
|
||||
|
||||
// no warning message
|
||||
expect(warnSpy).not.to.have.been.calledWith(sinon.match.string);
|
||||
// No log message
|
||||
expect(logSpy).not.to.have.been.calledWith(sinon.match.string);
|
||||
expect(result).to.equal(`abc${path.sep}codeql.exe`);
|
||||
});
|
||||
|
||||
it('should warn when using a hard-coded deprecated launcher name', async () => {
|
||||
launcherThatExists = 'codeql.cmd';
|
||||
path.sep;
|
||||
const result = await getExecutableFromDirectory('abc');
|
||||
expect(fsSpy).to.have.been.calledWith(`abc${path.sep}codeql.exe`);
|
||||
expect(fsSpy).to.have.been.calledWith(`abc${path.sep}codeql.cmd`);
|
||||
|
||||
// Should have opened a warning message
|
||||
expect(warnSpy).to.have.been.calledWith(sinon.match.string);
|
||||
// No log message
|
||||
expect(logSpy).not.to.have.been.calledWith(sinon.match.string);
|
||||
expect(result).to.equal(`abc${path.sep}codeql.cmd`);
|
||||
});
|
||||
|
||||
it('should avoid warn when no launcher is found', async () => {
|
||||
launcherThatExists = 'xxx';
|
||||
const result = await getExecutableFromDirectory('abc', false);
|
||||
expect(fsSpy).to.have.been.calledWith(`abc${path.sep}codeql.exe`);
|
||||
expect(fsSpy).to.have.been.calledWith(`abc${path.sep}codeql.cmd`);
|
||||
|
||||
// no warning message
|
||||
expect(warnSpy).not.to.have.been.calledWith(sinon.match.string);
|
||||
// log message sent out
|
||||
expect(logSpy).not.to.have.been.calledWith(sinon.match.string);
|
||||
expect(result).to.equal(undefined);
|
||||
});
|
||||
|
||||
it('should warn when no launcher is found', async () => {
|
||||
launcherThatExists = 'xxx';
|
||||
const result = await getExecutableFromDirectory('abc', true);
|
||||
expect(fsSpy).to.have.been.calledWith(`abc${path.sep}codeql.exe`);
|
||||
expect(fsSpy).to.have.been.calledWith(`abc${path.sep}codeql.cmd`);
|
||||
|
||||
// no warning message
|
||||
expect(warnSpy).not.to.have.been.calledWith(sinon.match.string);
|
||||
// log message sent out
|
||||
expect(logSpy).to.have.been.calledWith(sinon.match.string);
|
||||
expect(result).to.equal(undefined);
|
||||
});
|
||||
|
||||
function createModule() {
|
||||
sandbox = sinon.createSandbox();
|
||||
warnSpy = sandbox.spy();
|
||||
logSpy = sandbox.spy();
|
||||
// pretend that only the .cmd file exists
|
||||
fsSpy = sandbox.stub().callsFake(arg => arg.endsWith(launcherThatExists) ? true : false);
|
||||
platformSpy = sandbox.stub().returns('win32');
|
||||
|
||||
return proxyquire('../../distribution', {
|
||||
'./helpers': {
|
||||
showAndLogWarningMessage: warnSpy
|
||||
},
|
||||
'./logging': {
|
||||
'logger': {
|
||||
log: logSpy
|
||||
}
|
||||
},
|
||||
'fs-extra': {
|
||||
pathExists: fsSpy
|
||||
},
|
||||
os: {
|
||||
platform: platformSpy
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -87,16 +87,16 @@ describe("Invocation rate limiter", () => {
|
||||
});
|
||||
|
||||
class MockExtensionContext implements ExtensionContext {
|
||||
subscriptions: { dispose(): unknown; }[] = [];
|
||||
subscriptions: { dispose(): unknown }[] = [];
|
||||
workspaceState: Memento = new MockMemento();
|
||||
globalState: Memento = new MockMemento();
|
||||
extensionPath: string = "";
|
||||
extensionPath = "";
|
||||
asAbsolutePath(_relativePath: string): string {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
storagePath: string = "";
|
||||
globalStoragePath: string = "";
|
||||
logPath: string = "";
|
||||
storagePath = "";
|
||||
globalStoragePath = "";
|
||||
logPath = "";
|
||||
}
|
||||
|
||||
class MockMemento implements Memento {
|
||||
|
||||
@@ -5,9 +5,9 @@ import { runTests } from 'vscode-test';
|
||||
// would simply use instead, but for the fact that it doesn't export
|
||||
// it.
|
||||
type Suite = {
|
||||
extensionDevelopmentPath: string,
|
||||
extensionTestsPath: string,
|
||||
launchArgs: string[]
|
||||
extensionDevelopmentPath: string;
|
||||
extensionTestsPath: string;
|
||||
launchArgs: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
mocha: true
|
||||
}
|
||||
},
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
},
|
||||
}
|
||||
|
||||
3
extensions/ql-vscode/test/data/library.qll
Normal file
3
extensions/ql-vscode/test/data/library.qll
Normal file
@@ -0,0 +1,3 @@
|
||||
predicate foo() {
|
||||
1 == 1
|
||||
}
|
||||
154
extensions/ql-vscode/test/pure-tests/logging.test.ts
Normal file
154
extensions/ql-vscode/test/pure-tests/logging.test.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
import 'chai/register-should';
|
||||
import * as chai from 'chai';
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import * as tmp from 'tmp';
|
||||
import 'mocha';
|
||||
import * as sinonChai from 'sinon-chai';
|
||||
import * as sinon from 'sinon';
|
||||
import * as pq from 'proxyquire';
|
||||
|
||||
const proxyquire = pq.noPreserveCache().noCallThru();
|
||||
chai.use(sinonChai);
|
||||
const expect = chai.expect;
|
||||
|
||||
describe('OutputChannelLogger tests', () => {
|
||||
let OutputChannelLogger;
|
||||
const tempFolders: Record<string, tmp.DirResult> = {};
|
||||
let logger: any;
|
||||
let mockOutputChannel: Record<string, sinon.SinonStub>;
|
||||
|
||||
beforeEach(async () => {
|
||||
OutputChannelLogger = createModule().OutputChannelLogger;
|
||||
tempFolders.globalStoragePath = tmp.dirSync({ prefix: 'logging-tests-global' });
|
||||
tempFolders.storagePath = tmp.dirSync({ prefix: 'logging-tests-workspace' });
|
||||
logger = new OutputChannelLogger('test-logger');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
tempFolders.globalStoragePath.removeCallback();
|
||||
tempFolders.storagePath.removeCallback();
|
||||
});
|
||||
|
||||
it('should log to the output channel', async () => {
|
||||
await logger.log('xxx');
|
||||
expect(mockOutputChannel.appendLine).to.have.been.calledWith('xxx');
|
||||
expect(mockOutputChannel.append).not.to.have.been.calledWith('xxx');
|
||||
|
||||
await logger.log('yyy', { trailingNewline: false });
|
||||
expect(mockOutputChannel.appendLine).not.to.have.been.calledWith('yyy');
|
||||
expect(mockOutputChannel.append).to.have.been.calledWith('yyy');
|
||||
|
||||
// additionalLogLocation ignored since not initialized
|
||||
await logger.log('zzz', { additionalLogLocation: 'hucairz' });
|
||||
|
||||
// should not have created any side logs
|
||||
expect(fs.readdirSync(tempFolders.globalStoragePath.name).length).to.equal(0);
|
||||
expect(fs.readdirSync(tempFolders.storagePath.name).length).to.equal(0);
|
||||
});
|
||||
|
||||
it('should create a side log in the workspace area', async () => {
|
||||
await sideLogTest('storagePath', 'globalStoragePath');
|
||||
});
|
||||
|
||||
it('should create a side log in the global area', async () => {
|
||||
await sideLogTest('globalStoragePath', 'storagePath');
|
||||
});
|
||||
|
||||
async function sideLogTest(expectedArea: string, otherArea: string): Promise<void> {
|
||||
logger.init({
|
||||
[expectedArea]: tempFolders[expectedArea].name,
|
||||
[otherArea]: undefined
|
||||
});
|
||||
|
||||
await logger.log('xxx', { additionalLogLocation: 'first' });
|
||||
await logger.log('yyy', { additionalLogLocation: 'second' });
|
||||
await logger.log('zzz', { additionalLogLocation: 'first', trailingNewline: false });
|
||||
await logger.log('aaa');
|
||||
|
||||
// expect 2 side logs
|
||||
const testLoggerFolder = path.join(tempFolders[expectedArea].name, 'test-logger');
|
||||
expect(fs.readdirSync(testLoggerFolder).length).to.equal(2);
|
||||
expect(fs.readdirSync(tempFolders[otherArea].name).length).to.equal(0);
|
||||
|
||||
// contents
|
||||
expect(fs.readFileSync(path.join(testLoggerFolder, 'first'), 'utf8')).to.equal('xxx\nzzz');
|
||||
expect(fs.readFileSync(path.join(testLoggerFolder, 'second'), 'utf8')).to.equal('yyy\n');
|
||||
}
|
||||
|
||||
it('should delete side logs on dispose', async () => {
|
||||
logger.init({
|
||||
storagePath: tempFolders.storagePath.name
|
||||
});
|
||||
await logger.log('xxx', { additionalLogLocation: 'first' });
|
||||
await logger.log('yyy', { additionalLogLocation: 'second' });
|
||||
|
||||
const testLoggerFolder = path.join(tempFolders.storagePath.name, 'test-logger');
|
||||
expect(fs.readdirSync(testLoggerFolder).length).to.equal(2);
|
||||
|
||||
await logger.dispose();
|
||||
// need to wait for disposable-object to dispose
|
||||
await waitABit();
|
||||
expect(fs.readdirSync(testLoggerFolder).length).to.equal(0);
|
||||
expect(mockOutputChannel.dispose).to.have.been.calledWith();
|
||||
});
|
||||
|
||||
it('should remove an additional log location', async () => {
|
||||
logger.init({
|
||||
storagePath: tempFolders.storagePath.name
|
||||
});
|
||||
await logger.log('xxx', { additionalLogLocation: 'first' });
|
||||
await logger.log('yyy', { additionalLogLocation: 'second' });
|
||||
|
||||
const testLoggerFolder = path.join(tempFolders.storagePath.name, 'test-logger');
|
||||
expect(fs.readdirSync(testLoggerFolder).length).to.equal(2);
|
||||
|
||||
await logger.removeAdditionalLogLocation('first');
|
||||
// need to wait for disposable-object to dispose
|
||||
await waitABit();
|
||||
expect(fs.readdirSync(testLoggerFolder).length).to.equal(1);
|
||||
expect(fs.readFileSync(path.join(testLoggerFolder, 'second'), 'utf8')).to.equal('yyy\n');
|
||||
});
|
||||
|
||||
it('should delete an existing folder on init', async () => {
|
||||
fs.createFileSync(path.join(tempFolders.storagePath.name, 'test-logger', 'xxx'));
|
||||
logger.init({
|
||||
storagePath: tempFolders.storagePath.name
|
||||
});
|
||||
// should be empty dir
|
||||
|
||||
const testLoggerFolder = path.join(tempFolders.storagePath.name, 'test-logger');
|
||||
expect(fs.readdirSync(testLoggerFolder).length).to.equal(1);
|
||||
});
|
||||
|
||||
it('should show the output channel', () => {
|
||||
logger.show(true);
|
||||
expect(mockOutputChannel.show).to.have.been.calledWith(true);
|
||||
});
|
||||
|
||||
function createModule(): any {
|
||||
mockOutputChannel = {
|
||||
append: sinon.stub(),
|
||||
appendLine: sinon.stub(),
|
||||
show: sinon.stub(),
|
||||
dispose: sinon.stub(),
|
||||
};
|
||||
|
||||
return proxyquire('../../src/logging', {
|
||||
vscode: {
|
||||
window: {
|
||||
createOutputChannel: () => mockOutputChannel
|
||||
},
|
||||
Disposable: function() {
|
||||
/**/
|
||||
},
|
||||
'@noCallThru': true,
|
||||
'@global': true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function waitABit(ms = 50): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
});
|
||||
@@ -31,31 +31,31 @@ class Checkpoint<T> {
|
||||
private promise: Promise<T>;
|
||||
|
||||
constructor() {
|
||||
this.res = () => { };
|
||||
this.rej = () => { };
|
||||
this.res = () => { /**/ };
|
||||
this.rej = () => { /**/ };
|
||||
this.promise = new Promise((res, rej) => { this.res = res; this.rej = rej; })
|
||||
}
|
||||
|
||||
async done() {
|
||||
async done(): Promise<T> {
|
||||
return this.promise;
|
||||
}
|
||||
|
||||
async resolve() {
|
||||
(this.res)();
|
||||
async resolve(): Promise<void> {
|
||||
await (this.res)();
|
||||
}
|
||||
|
||||
async reject(e: Error) {
|
||||
(this.rej)(e);
|
||||
async reject(e: Error): Promise<void> {
|
||||
await (this.rej)(e);
|
||||
}
|
||||
}
|
||||
|
||||
type ResultSets = {
|
||||
[name: string]: bqrs.ColumnValue[][]
|
||||
[name: string]: bqrs.ColumnValue[][];
|
||||
}
|
||||
|
||||
type QueryTestCase = {
|
||||
queryPath: string,
|
||||
expectedResultSets: ResultSets
|
||||
queryPath: string;
|
||||
expectedResultSets: ResultSets;
|
||||
}
|
||||
|
||||
// Test cases: queries to run and their expected results.
|
||||
@@ -105,9 +105,10 @@ describe('using the query server', function() {
|
||||
report: (v: { message: string }) => console.log(`progress reporter says ${v.message}`)
|
||||
};
|
||||
const logger: Logger = {
|
||||
log: (s: string) => console.log('logger says', s),
|
||||
logWithoutTrailingNewline: (s: string) => console.log('logger says', s),
|
||||
show: () => { },
|
||||
log: async (s: string) => console.log('logger says', s),
|
||||
show: () => { /**/ },
|
||||
removeAdditionalLogLocation: async () => { /**/ },
|
||||
getBaseLocation: () => ''
|
||||
};
|
||||
cliServer = new cli.CodeQLCliServer({
|
||||
async getCodeQlPathWithoutVersionCheck(): Promise<string | undefined> {
|
||||
@@ -161,7 +162,7 @@ describe('using the query server', function() {
|
||||
resultPath: COMPILED_QUERY_PATH,
|
||||
target: { query: {} }
|
||||
};
|
||||
const result = await qs.sendRequest(messages.compileQuery, params, token, () => { });
|
||||
const result = await qs.sendRequest(messages.compileQuery, params, token, () => { /**/ });
|
||||
expect(result.messages!.length).to.equal(0);
|
||||
compilationSucceeded.resolve();
|
||||
}
|
||||
@@ -194,7 +195,7 @@ describe('using the query server', function() {
|
||||
stopOnError: false,
|
||||
useSequenceHint: false
|
||||
};
|
||||
await qs.sendRequest(messages.runQueries, params, token, () => { });
|
||||
await qs.sendRequest(messages.runQueries, params, token, () => { /**/ });
|
||||
}
|
||||
catch (e) {
|
||||
evaluationSucceeded.reject(e);
|
||||
|
||||
1
tsconfig.json
Normal file
1
tsconfig.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
Reference in New Issue
Block a user