Compare commits
228 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a60faa451 | ||
|
|
578ffe0e2f | ||
|
|
e805ef37eb | ||
|
|
1e76e583c7 | ||
|
|
25b9aeba8e | ||
|
|
e521112f3e | ||
|
|
216faee279 | ||
|
|
891ce74a80 | ||
|
|
e2e4879548 | ||
|
|
cac7c3ae4e | ||
|
|
7dedfca369 | ||
|
|
1a0d88135d | ||
|
|
ed76b46fa5 | ||
|
|
1dc69a125b | ||
|
|
35f582b731 | ||
|
|
794e874206 | ||
|
|
e7bab9207d | ||
|
|
3bd61ae1a6 | ||
|
|
698b6ad0e4 | ||
|
|
abbebd3d42 | ||
|
|
33ef89a28c | ||
|
|
a9b2aec353 | ||
|
|
1cb0b0b6cb | ||
|
|
3346bd48cb | ||
|
|
5138831cd3 | ||
|
|
484c7db8c5 | ||
|
|
691121d7c7 | ||
|
|
1ea3cfddb0 | ||
|
|
993b8c94d6 | ||
|
|
79f427c05d | ||
|
|
d66e7c6198 | ||
|
|
8205b19125 | ||
|
|
597c9c48ff | ||
|
|
7fd45efed8 | ||
|
|
2e7557ba09 | ||
|
|
0f5117ecec | ||
|
|
0ae7bb821d | ||
|
|
ba419dd561 | ||
|
|
3dd0ef50e0 | ||
|
|
1c1117f7a3 | ||
|
|
4e66b62534 | ||
|
|
572ba290b4 | ||
|
|
63c2932cd9 | ||
|
|
d97eb2e76b | ||
|
|
323862a828 | ||
|
|
502d4236ad | ||
|
|
28652a2088 | ||
|
|
04b4d15099 | ||
|
|
7ef75f5971 | ||
|
|
4bfefb8ebb | ||
|
|
a5fcfe7f40 | ||
|
|
7a3d5c1925 | ||
|
|
18c3ce237e | ||
|
|
6f52469d64 | ||
|
|
728f80189b | ||
|
|
dbba6972e1 | ||
|
|
c436688eb2 | ||
|
|
127baea584 | ||
|
|
425befa959 | ||
|
|
1050968db1 | ||
|
|
7a2f976199 | ||
|
|
9362881e22 | ||
|
|
60b1f141a1 | ||
|
|
02c8df003a | ||
|
|
b91e31cb2c | ||
|
|
272aed7d31 | ||
|
|
96850e442f | ||
|
|
a19a3297c5 | ||
|
|
795a0bc46d | ||
|
|
2967777db1 | ||
|
|
7bb0d0fda9 | ||
|
|
0e744fdb5a | ||
|
|
39a0ef00c4 | ||
|
|
9110af80d8 | ||
|
|
ee056ce2b3 | ||
|
|
2db4a951c2 | ||
|
|
994ebaacd0 | ||
|
|
d00dde06ca | ||
|
|
402b33830d | ||
|
|
735372ee72 | ||
|
|
51d3a018de | ||
|
|
c0187a5650 | ||
|
|
008d7b363f | ||
|
|
c9d6bfd32e | ||
|
|
dc2bb3a6e0 | ||
|
|
1d4cbde48e | ||
|
|
8b354357c1 | ||
|
|
109ee0498e | ||
|
|
944a9115e9 | ||
|
|
9c51974cb0 | ||
|
|
6d1b5ab1e1 | ||
|
|
5bee07c712 | ||
|
|
32f3d32989 | ||
|
|
dd5da2bbc0 | ||
|
|
3d81e1b31e | ||
|
|
bf84dbea69 | ||
|
|
7414a77b8a | ||
|
|
fb154ab423 | ||
|
|
6a431b0719 | ||
|
|
f6bb233a15 | ||
|
|
386e9bb865 | ||
|
|
0f7cf2d935 | ||
|
|
72f253e39f | ||
|
|
168ce8fd28 | ||
|
|
0a633a29fe | ||
|
|
d8bbd3de9f | ||
|
|
99bb04b458 | ||
|
|
8fcbee6ad4 | ||
|
|
195ef45600 | ||
|
|
56660b1720 | ||
|
|
b45c67f24f | ||
|
|
30cf72b6c8 | ||
|
|
670c863f3f | ||
|
|
754fa67231 | ||
|
|
706f2e3ca1 | ||
|
|
dc5d8daa84 | ||
|
|
0b9ba3cb94 | ||
|
|
f539ba3b85 | ||
|
|
bccc573c6a | ||
|
|
b273db13d0 | ||
|
|
a704a2bf46 | ||
|
|
bf328d56d7 | ||
|
|
10fe55cff5 | ||
|
|
b3003fc0ba | ||
|
|
125867d68c | ||
|
|
27b4203d8b | ||
|
|
f9803fc760 | ||
|
|
c958fd2052 | ||
|
|
adea0b4d77 | ||
|
|
3dd6effce5 | ||
|
|
f3e4766287 | ||
|
|
3751f3ec6c | ||
|
|
2b9bff14f3 | ||
|
|
45a87192de | ||
|
|
c889c09584 | ||
|
|
45ffd8a45c | ||
|
|
6c3731fe98 | ||
|
|
74e047cbd8 | ||
|
|
a9a5d098c7 | ||
|
|
8f0e6154ad | ||
|
|
2895e84586 | ||
|
|
56585b43e6 | ||
|
|
6dc1f9f601 | ||
|
|
91edad8df2 | ||
|
|
2637d6d00c | ||
|
|
4771d9b834 | ||
|
|
e6fb4803d1 | ||
|
|
c7dbaebcec | ||
|
|
3fdc328767 | ||
|
|
ec9d6b1907 | ||
|
|
2cdd6de38e | ||
|
|
339819c82b | ||
|
|
75b684b00f | ||
|
|
bc65d8f311 | ||
|
|
0047186c83 | ||
|
|
ff3b6091c6 | ||
|
|
a1b81d9b6f | ||
|
|
4bc97b850c | ||
|
|
7ba90275a8 | ||
|
|
9a9a8c5ac4 | ||
|
|
644bea27ad | ||
|
|
68de9f09e4 | ||
|
|
a16366caae | ||
|
|
ccf4fad912 | ||
|
|
5754d19327 | ||
|
|
595b14cf30 | ||
|
|
858a3268df | ||
|
|
55408a7e0b | ||
|
|
24a61d387d | ||
|
|
927817f99d | ||
|
|
e60ed32731 | ||
|
|
003b9dcf2c | ||
|
|
a23a8f78dd | ||
|
|
f6613867d3 | ||
|
|
51425915d9 | ||
|
|
754cad3815 | ||
|
|
430bcd0aa2 | ||
|
|
4be69e858c | ||
|
|
9fc2c38970 | ||
|
|
aa5026f192 | ||
|
|
e55b8a366e | ||
|
|
5841fbccd7 | ||
|
|
925acbd36e | ||
|
|
91ca9481eb | ||
|
|
f830c23018 | ||
|
|
52e2a63828 | ||
|
|
3f76ad6eb8 | ||
|
|
1c0d6b991a | ||
|
|
68d867ef76 | ||
|
|
e04f941cee | ||
|
|
558eb19c97 | ||
|
|
31b64d2f73 | ||
|
|
d02a5cd3dc | ||
|
|
e895078913 | ||
|
|
1a10fd35f1 | ||
|
|
614785fb7a | ||
|
|
4f8d68d8a2 | ||
|
|
dc6de0f8a8 | ||
|
|
e5967c3851 | ||
|
|
c31635fe90 | ||
|
|
e26e1113be | ||
|
|
01f6576e95 | ||
|
|
347d234906 | ||
|
|
043a7b1039 | ||
|
|
b10cb7771e | ||
|
|
80b2a43be9 | ||
|
|
a985bcb872 | ||
|
|
90dd97fbc0 | ||
|
|
0296db8b25 | ||
|
|
65bb61b530 | ||
|
|
3da0034953 | ||
|
|
a0ab34bf55 | ||
|
|
cc869daf05 | ||
|
|
1c820b4812 | ||
|
|
d215c7e2de | ||
|
|
a88039bf0b | ||
|
|
f5c39d7931 | ||
|
|
261933b6a9 | ||
|
|
33eb33405e | ||
|
|
ec60f3f000 | ||
|
|
0974700557 | ||
|
|
03f1547160 | ||
|
|
91a5db7359 | ||
|
|
fbd2cbd3aa | ||
|
|
55534e19d6 | ||
|
|
b6f33d7c7b | ||
|
|
df832746ad | ||
|
|
c9e87eff56 |
109
.github/workflows/main.yml
vendored
109
.github/workflows/main.yml
vendored
@@ -23,11 +23,13 @@ jobs:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16.14.2'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: extensions/ql-vscode/package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: extensions/ql-vscode
|
||||
run: |
|
||||
npm install
|
||||
npm ci
|
||||
shell: bash
|
||||
|
||||
- name: Build
|
||||
@@ -45,7 +47,7 @@ jobs:
|
||||
cp dist/*.vsix artifacts
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
with:
|
||||
name: vscode-codeql-extension
|
||||
@@ -67,10 +69,45 @@ jobs:
|
||||
LATEST=`gh api repos/dsp-testing/codeql-cli-nightlies/releases --jq '.[].tag_name' --method GET --raw-field 'per_page=1'`
|
||||
echo "nightly-url=https://github.com/dsp-testing/codeql-cli-nightlies/releases/download/$LATEST" >> "$GITHUB_OUTPUT"
|
||||
|
||||
test:
|
||||
name: Test
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16.14.2'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: extensions/ql-vscode/package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: extensions/ql-vscode
|
||||
run: |
|
||||
npm ci
|
||||
shell: bash
|
||||
|
||||
- name: Check types
|
||||
working-directory: extensions/ql-vscode
|
||||
run: |
|
||||
npm run check-types
|
||||
|
||||
- name: Lint
|
||||
working-directory: extensions/ql-vscode
|
||||
run: |
|
||||
npm run lint
|
||||
|
||||
- name: Lint scenarios
|
||||
working-directory: extensions/ql-vscode
|
||||
run: |
|
||||
npm run lint:scenarios
|
||||
|
||||
unit-test:
|
||||
name: Unit Test
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: [find-nightly]
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
@@ -83,11 +120,42 @@ jobs:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16.14.2'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: extensions/ql-vscode/package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: extensions/ql-vscode
|
||||
run: |
|
||||
npm install
|
||||
npm ci
|
||||
shell: bash
|
||||
|
||||
- name: Run unit tests
|
||||
working-directory: extensions/ql-vscode
|
||||
run: |
|
||||
npm run test
|
||||
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16.14.2'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: extensions/ql-vscode/package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: extensions/ql-vscode
|
||||
run: |
|
||||
npm ci
|
||||
shell: bash
|
||||
|
||||
- name: Build
|
||||
@@ -98,28 +166,6 @@ jobs:
|
||||
npm run build
|
||||
shell: bash
|
||||
|
||||
- name: Lint
|
||||
working-directory: extensions/ql-vscode
|
||||
run: |
|
||||
npm run lint
|
||||
|
||||
- name: Lint scenarios
|
||||
working-directory: extensions/ql-vscode
|
||||
run: |
|
||||
npm run lint:scenarios
|
||||
|
||||
- name: Run unit tests (Linux)
|
||||
working-directory: extensions/ql-vscode
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
npm run test
|
||||
|
||||
- name: Run unit tests (Windows)
|
||||
if: matrix.os == 'windows-latest'
|
||||
working-directory: extensions/ql-vscode
|
||||
run: |
|
||||
npm run test
|
||||
|
||||
- name: Run integration tests (Linux)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
working-directory: extensions/ql-vscode
|
||||
@@ -144,7 +190,8 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
version: ['v2.7.6', 'v2.8.5', 'v2.9.4', 'v2.10.5', 'v2.11.3', 'nightly']
|
||||
version: ['v2.7.6', 'v2.8.5', 'v2.9.4', 'v2.10.5', 'v2.11.5', 'nightly']
|
||||
fail-fast: false
|
||||
env:
|
||||
CLI_VERSION: ${{ matrix.version }}
|
||||
NIGHTLY_URL: ${{ needs.find-nightly.outputs.url }}
|
||||
@@ -157,11 +204,13 @@ jobs:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16.14.2'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: extensions/ql-vscode/package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: extensions/ql-vscode
|
||||
run: |
|
||||
npm install
|
||||
npm ci
|
||||
shell: bash
|
||||
|
||||
- name: Build
|
||||
|
||||
4
.vscode/extensions.json
vendored
4
.vscode/extensions.json
vendored
@@ -5,7 +5,9 @@
|
||||
"recommendations": [
|
||||
"amodio.tsl-problem-matcher",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode"
|
||||
"esbenp.prettier-vscode",
|
||||
"firsttris.vscode-jest-runner",
|
||||
"Orta.vscode-jest",
|
||||
],
|
||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||
"unwantedRecommendations": []
|
||||
|
||||
119
.vscode/launch.json
vendored
119
.vscode/launch.json
vendored
@@ -29,24 +29,40 @@
|
||||
"name": "Launch Unit Tests (vscode-codeql)",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/extensions/ql-vscode/node_modules/mocha/bin/_mocha",
|
||||
"program": "${workspaceFolder}/extensions/ql-vscode/node_modules/jest/bin/jest.js",
|
||||
"showAsyncStacks": true,
|
||||
"cwd": "${workspaceFolder}/extensions/ql-vscode",
|
||||
"runtimeArgs": [
|
||||
"--inspect=9229"
|
||||
],
|
||||
"env": {
|
||||
"LANG": "en-US"
|
||||
"LANG": "en-US",
|
||||
"TZ": "UTC"
|
||||
},
|
||||
"args": [
|
||||
"--exit",
|
||||
"-u",
|
||||
"bdd",
|
||||
"--colors",
|
||||
"--diff",
|
||||
"--config",
|
||||
".mocharc.json",
|
||||
"test/pure-tests/**/*.ts"
|
||||
"--projects",
|
||||
"test"
|
||||
],
|
||||
"stopOnEntry": false,
|
||||
"sourceMaps": true,
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen"
|
||||
},
|
||||
{
|
||||
"name": "Launch Selected Unit Test (vscode-codeql)",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/extensions/ql-vscode/node_modules/jest/bin/jest.js",
|
||||
"showAsyncStacks": true,
|
||||
"cwd": "${workspaceFolder}/extensions/ql-vscode",
|
||||
"env": {
|
||||
"LANG": "en-US",
|
||||
"TZ": "UTC"
|
||||
},
|
||||
"args": [
|
||||
"--projects",
|
||||
"test",
|
||||
"-i",
|
||||
"${relativeFile}",
|
||||
"-t",
|
||||
"${selectedText}"
|
||||
],
|
||||
"stopOnEntry": false,
|
||||
"sourceMaps": true,
|
||||
@@ -60,6 +76,10 @@
|
||||
"program": "${workspaceFolder}/extensions/ql-vscode/node_modules/jest/bin/jest.js",
|
||||
"showAsyncStacks": true,
|
||||
"cwd": "${workspaceFolder}/extensions/ql-vscode",
|
||||
"args": [
|
||||
"--projects",
|
||||
"src/view"
|
||||
],
|
||||
"stopOnEntry": false,
|
||||
"sourceMaps": true,
|
||||
"console": "integratedTerminal",
|
||||
@@ -67,60 +87,46 @@
|
||||
},
|
||||
{
|
||||
"name": "Launch Integration Tests - No Workspace (vscode-codeql)",
|
||||
"type": "extensionHost",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"program": "${workspaceFolder}/extensions/ql-vscode/node_modules/jest/bin/jest.js",
|
||||
"showAsyncStacks": true,
|
||||
"cwd": "${workspaceFolder}/extensions/ql-vscode",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceRoot}/extensions/ql-vscode",
|
||||
"--extensionTestsPath=${workspaceRoot}/extensions/ql-vscode/out/vscode-tests/no-workspace/index",
|
||||
"--disable-workspace-trust",
|
||||
"--disable-extensions",
|
||||
"--disable-gpu"
|
||||
"--projects",
|
||||
"src/vscode-tests/no-workspace"
|
||||
],
|
||||
"sourceMaps": true,
|
||||
"outFiles": [
|
||||
"${workspaceRoot}/extensions/ql-vscode/out/**/*.js",
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"attachSimplePort": 9223,
|
||||
},
|
||||
{
|
||||
"name": "Launch Integration Tests - Minimal Workspace (vscode-codeql)",
|
||||
"type": "extensionHost",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"program": "${workspaceFolder}/extensions/ql-vscode/node_modules/jest/bin/jest.js",
|
||||
"showAsyncStacks": true,
|
||||
"cwd": "${workspaceFolder}/extensions/ql-vscode",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceRoot}/extensions/ql-vscode",
|
||||
"--extensionTestsPath=${workspaceRoot}/extensions/ql-vscode/out/vscode-tests/minimal-workspace/index",
|
||||
"--disable-workspace-trust",
|
||||
"--disable-extensions",
|
||||
"--disable-gpu",
|
||||
"${workspaceRoot}/extensions/ql-vscode/test/data"
|
||||
"--projects",
|
||||
"src/vscode-tests/minimal-workspace"
|
||||
],
|
||||
"sourceMaps": true,
|
||||
"outFiles": [
|
||||
"${workspaceRoot}/extensions/ql-vscode/out/**/*.js",
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"attachSimplePort": 9223,
|
||||
},
|
||||
{
|
||||
"name": "Launch Integration Tests - With CLI",
|
||||
"type": "extensionHost",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"program": "${workspaceFolder}/extensions/ql-vscode/node_modules/jest/bin/jest.js",
|
||||
"showAsyncStacks": true,
|
||||
"cwd": "${workspaceFolder}/extensions/ql-vscode",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceRoot}/extensions/ql-vscode",
|
||||
"--extensionTestsPath=${workspaceRoot}/extensions/ql-vscode/out/vscode-tests/cli-integration/index",
|
||||
"--disable-workspace-trust",
|
||||
"--disable-gpu",
|
||||
"--disable-extension",
|
||||
"eamodio.gitlens",
|
||||
"--disable-extension",
|
||||
"github.codespaces",
|
||||
"--disable-extension",
|
||||
"github.copilot",
|
||||
"${workspaceRoot}/extensions/ql-vscode/src/vscode-tests/cli-integration/data",
|
||||
// Uncomment the last line and modify the path to a checked out
|
||||
// instance of the codeql repository so the libraries are
|
||||
// available in the workspace for the tests.
|
||||
// "${workspaceRoot}/../codeql"
|
||||
"--projects",
|
||||
"src/vscode-tests/cli-integration"
|
||||
],
|
||||
"env": {
|
||||
// Optionally, set the version to use for the integration tests.
|
||||
@@ -134,11 +140,16 @@
|
||||
// If not specified, one will be downloaded automatically.
|
||||
// This option overrides the CLI_VERSION option.
|
||||
// "CLI_PATH": "${workspaceRoot}/../semmle-code/target/intree/codeql/codeql",
|
||||
|
||||
// Uncomment the last line and modify the path to a checked out
|
||||
// instance of the codeql repository so the libraries are
|
||||
// available in the workspace for the tests.
|
||||
// "TEST_CODEQL_PATH": "${workspaceRoot}/../codeql",
|
||||
},
|
||||
"sourceMaps": true,
|
||||
"outFiles": [
|
||||
"${workspaceRoot}/extensions/ql-vscode/out/**/*.js",
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"attachSimplePort": 9223,
|
||||
},
|
||||
{
|
||||
"name": "Launch Storybook",
|
||||
|
||||
27
.vscode/settings.json
vendored
27
.vscode/settings.json
vendored
@@ -37,6 +37,33 @@
|
||||
"javascript.preferences.quoteStyle": "single",
|
||||
"editor.wordWrapColumn": 100,
|
||||
"jest.rootPath": "./extensions/ql-vscode",
|
||||
"jest.autoRun": "off",
|
||||
"jest.nodeEnv": {
|
||||
"LANG": "en-US",
|
||||
"TZ": "UTC"
|
||||
},
|
||||
"jestrunner.debugOptions": {
|
||||
// Uncomment to debug integration tests
|
||||
// "attachSimplePort": 9223,
|
||||
"env": {
|
||||
"LANG": "en-US",
|
||||
"TZ": "UTC",
|
||||
// Uncomment to debug integration tests
|
||||
// "VSCODE_WAIT_FOR_DEBUGGER": "true",
|
||||
}
|
||||
},
|
||||
"terminal.integrated.env.linux": {
|
||||
"LANG": "en-US",
|
||||
"TZ": "UTC"
|
||||
},
|
||||
"terminal.integrated.env.osx": {
|
||||
"LANG": "en-US",
|
||||
"TZ": "UTC"
|
||||
},
|
||||
"terminal.integrated.env.windows": {
|
||||
"LANG": "en-US",
|
||||
"TZ": "UTC"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
|
||||
@@ -150,6 +150,32 @@ The CLI integration tests require the CodeQL standard libraries in order to run
|
||||
|
||||
3. Run the VSCode task from the "Run and Debug" view called _Launch Integration Tests - With CLI_.
|
||||
|
||||
#### Running a single test
|
||||
|
||||
##### 1. From the terminal
|
||||
|
||||
The easiest way to run a single test is to change the `it` of the test to `it.only` and then run the test command with some additional options
|
||||
to only run tests for this specific file. For example, to run the test `src/vscode-tests/cli-integration/run-queries.test.ts`:
|
||||
|
||||
```shell
|
||||
npm run cli-integration -- --runTestsByPath src/vscode-tests/cli-integration/run-queries.test.ts
|
||||
```
|
||||
|
||||
You can also use the `--testNamePattern` option to run a specific test within a file. For example, to run the test `src/vscode-tests/cli-integration/run-queries.test.ts`:
|
||||
|
||||
```shell
|
||||
npm run cli-integration -- --runTestsByPath src/vscode-tests/cli-integration/run-queries.test.ts --testNamePattern "should create a QueryEvaluationInfo"
|
||||
```
|
||||
|
||||
##### 2. From VSCode
|
||||
|
||||
Alternatively, you can run a single test inside VSCode. To do so, install the [Jest Runner](https://marketplace.visualstudio.com/items?itemName=firsttris.vscode-jest-runner) extension. Then,
|
||||
you will have quicklinks to run a single test from within test files. To run a single unit or integration test, click the "Run" button. Debugging a single test is currently only supported
|
||||
for unit tests by default. To debug integration tests, open the `.vscode/settings.json` file and uncomment the `jestrunner.debugOptions` lines. This will allow you to debug integration tests.
|
||||
Please make sure to revert this change before committing; with this setting enabled, it is not possible to debug unit tests.
|
||||
|
||||
Without the Jest Runner extension, you can also use the "Launch Selected Unit Test (vscode-codeql)" launch configuration to run a single unit test.
|
||||
|
||||
#### Using a mock GitHub API server
|
||||
|
||||
Multi-Repo Variant Analyses (MRVA) rely on the GitHub API. In order to make development and testing easy, we have functionality that allows us to intercept requests to the GitHub API and provide mock responses.
|
||||
|
||||
@@ -20,6 +20,13 @@ To see what has changed in the last few versions of the extension, see the [Chan
|
||||
|
||||
This project will track new feature development in CodeQL and, whenever appropriate, bring that functionality to the Visual Studio Code experience.
|
||||
|
||||
## Dependencies
|
||||
|
||||
This extension depends on the following two extensions for required functionality. They will be installed automatically when you install VS Code CodeQL.
|
||||
|
||||
- [Test Adapter Converter](https://marketplace.visualstudio.com/items?itemName=ms-vscode.test-adapter-converter)
|
||||
- [Test Explorer UI](https://marketplace.visualstudio.com/items?itemName=hbenl.vscode-test-explorer)
|
||||
|
||||
## Contributing
|
||||
|
||||
This project welcomes contributions. See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to build, install, and contribute.
|
||||
|
||||
@@ -5,16 +5,22 @@ module.exports = {
|
||||
sourceType: "module",
|
||||
project: ["tsconfig.json", "./src/**/tsconfig.json", "./gulpfile.ts/tsconfig.json", "./scripts/tsconfig.json", "./.storybook/tsconfig.json"],
|
||||
},
|
||||
plugins: ["@typescript-eslint"],
|
||||
plugins: [
|
||||
"github",
|
||||
"@typescript-eslint"
|
||||
],
|
||||
env: {
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
extends: [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:github/react",
|
||||
"plugin:github/recommended",
|
||||
"plugin:github/typescript",
|
||||
"plugin:jest-dom/recommended",
|
||||
"plugin:prettier/recommended"
|
||||
"plugin:prettier/recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
rules: {
|
||||
"@typescript-eslint/no-use-before-define": 0,
|
||||
@@ -31,8 +37,36 @@ module.exports = {
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-floating-promises": [ "error", { ignoreVoid: true } ],
|
||||
"@typescript-eslint/no-invalid-this": "off",
|
||||
"@typescript-eslint/no-shadow": "off",
|
||||
"prefer-const": ["warn", { destructuring: "all" }],
|
||||
"@typescript-eslint/no-throw-literal": "error",
|
||||
"no-useless-escape": 0,
|
||||
"camelcase": "off",
|
||||
"eqeqeq": "off",
|
||||
"escompat/no-regexp-lookbehind": "off",
|
||||
"filenames/match-regex": "off",
|
||||
"filenames/match-regexp": "off",
|
||||
"func-style": "off",
|
||||
"i18n-text/no-en": "off",
|
||||
"import/named": "off",
|
||||
"import/no-dynamic-require": "off",
|
||||
"import/no-dynamic-required": "off",
|
||||
"import/no-anonymous-default-export": "off",
|
||||
"import/no-commonjs": "off",
|
||||
"import/no-mutable-exports": "off",
|
||||
"import/no-namespace": "off",
|
||||
"import/no-unresolved": "off",
|
||||
"import/no-webpack-loader-syntax": "off",
|
||||
"jsx-a11y/anchor-is-valid": "off",
|
||||
"jsx-a11y/no-noninteractive-element-interactions": "off",
|
||||
"jsx-a11y/no-static-element-interactions": "off",
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
"no-invalid-this": "off",
|
||||
"no-fallthrough": "off",
|
||||
"no-console": "off",
|
||||
"no-shadow": "off",
|
||||
"github/array-foreach": "off",
|
||||
"github/no-then": "off",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"exit": true,
|
||||
"require": ["test/mocha.setup.js"]
|
||||
}
|
||||
@@ -1,9 +1,17 @@
|
||||
export enum VSCodeTheme {
|
||||
Dark = "dark",
|
||||
Light = "light",
|
||||
LightHighContrast = "light-high-contrast",
|
||||
DarkHighContrast = "dark-high-contrast",
|
||||
GitHubLightDefault = "github-light-default",
|
||||
GitHubDarkDefault = "github-dark-default",
|
||||
}
|
||||
|
||||
export const themeNames: { [key in VSCodeTheme]: string } = {
|
||||
[VSCodeTheme.Dark]: "Dark+",
|
||||
[VSCodeTheme.Light]: "Light+",
|
||||
[VSCodeTheme.LightHighContrast]: "Light High Contrast",
|
||||
[VSCodeTheme.DarkHighContrast]: "Dark High Contrast",
|
||||
[VSCodeTheme.GitHubLightDefault]: "GitHub Light Default",
|
||||
[VSCodeTheme.GitHubDarkDefault]: "GitHub Dark Default",
|
||||
};
|
||||
|
||||
@@ -16,6 +16,22 @@ const themeFiles: { [key in VSCodeTheme]: string } = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
require("!file-loader?modules!../../src/stories/vscode-theme-light.css")
|
||||
.default,
|
||||
[VSCodeTheme.LightHighContrast]:
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
require("!file-loader?modules!../../src/stories/vscode-theme-light-high-contrast.css")
|
||||
.default,
|
||||
[VSCodeTheme.DarkHighContrast]:
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
require("!file-loader?modules!../../src/stories/vscode-theme-dark-high-contrast.css")
|
||||
.default,
|
||||
[VSCodeTheme.GitHubLightDefault]:
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
require("!file-loader?modules!../../src/stories/vscode-theme-github-light-default.css")
|
||||
.default,
|
||||
[VSCodeTheme.GitHubDarkDefault]:
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
require("!file-loader?modules!../../src/stories/vscode-theme-github-dark-default.css")
|
||||
.default,
|
||||
};
|
||||
|
||||
export const withTheme = (
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# CodeQL for Visual Studio Code: Changelog
|
||||
|
||||
## 1.7.7 - 13 December 2022
|
||||
|
||||
- Increase the required version of VS Code to 1.67.0. [#1662](https://github.com/github/vscode-codeql/pull/1662)
|
||||
|
||||
## 1.7.6 - 21 November 2022
|
||||
|
||||
- Warn users when their VS Code version is too old to support all features in the vscode-codeql extension. [#1674](https://github.com/github/vscode-codeql/pull/1674)
|
||||
|
||||
@@ -16,7 +16,9 @@ For information about other configurations, see the separate [CodeQL help](https
|
||||
|
||||
### Quick start: Installing and configuring the extension
|
||||
|
||||
1. [Install the extension](#installing-the-extension).
|
||||
1. [Install the extension](#installing-the-extension).
|
||||
*Note: vscode-codeql installs the following dependencies for required functionality: [Test Adapter Converter](https://marketplace.visualstudio.com/items?itemName=ms-vscode.test-adapter-converter), [Test Explorer UI](https://marketplace.visualstudio.com/items?itemName=hbenl.vscode-test-explorer).*
|
||||
|
||||
1. [Check access to the CodeQL CLI](#checking-access-to-the-codeql-cli).
|
||||
1. [Clone the CodeQL starter workspace](#cloning-the-codeql-starter-workspace).
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as gulp from "gulp";
|
||||
import { src, dest } from "gulp";
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const replace = require("gulp-replace");
|
||||
|
||||
@@ -13,8 +13,7 @@ export function injectAppInsightsKey() {
|
||||
}
|
||||
|
||||
// replace the key
|
||||
return gulp
|
||||
.src(["out/telemetry.js"])
|
||||
return src(["out/telemetry.js"])
|
||||
.pipe(replace(/REPLACE-APP-INSIGHTS-KEY/, process.env.APP_INSIGHTS_KEY))
|
||||
.pipe(gulp.dest("out/"));
|
||||
.pipe(dest("out/"));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import * as fs from "fs-extra";
|
||||
import * as path from "path";
|
||||
import {
|
||||
copy,
|
||||
readFile,
|
||||
mkdirs,
|
||||
readdir,
|
||||
unlinkSync,
|
||||
remove,
|
||||
writeFile,
|
||||
} from "fs-extra";
|
||||
import { resolve, join } from "path";
|
||||
|
||||
export interface DeployedPackage {
|
||||
distPath: string;
|
||||
@@ -25,12 +33,9 @@ async function copyPackage(
|
||||
): Promise<void> {
|
||||
for (const file of packageFiles) {
|
||||
console.log(
|
||||
`copying ${path.resolve(sourcePath, file)} to ${path.resolve(
|
||||
destPath,
|
||||
file,
|
||||
)}`,
|
||||
`copying ${resolve(sourcePath, file)} to ${resolve(destPath, file)}`,
|
||||
);
|
||||
await fs.copy(path.resolve(sourcePath, file), path.resolve(destPath, file));
|
||||
await copy(resolve(sourcePath, file), resolve(destPath, file));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,53 +44,52 @@ export async function deployPackage(
|
||||
): Promise<DeployedPackage> {
|
||||
try {
|
||||
const packageJson: any = JSON.parse(
|
||||
await fs.readFile(packageJsonPath, "utf8"),
|
||||
await readFile(packageJsonPath, "utf8"),
|
||||
);
|
||||
|
||||
// Default to development build; use flag --release to indicate release build.
|
||||
const isDevBuild = !process.argv.includes("--release");
|
||||
const distDir = path.join(__dirname, "../../../dist");
|
||||
await fs.mkdirs(distDir);
|
||||
const distDir = join(__dirname, "../../../dist");
|
||||
await mkdirs(distDir);
|
||||
|
||||
if (isDevBuild) {
|
||||
// NOTE: rootPackage.name had better not have any regex metacharacters
|
||||
const oldDevBuildPattern = new RegExp(
|
||||
"^" + packageJson.name + "[^/]+-dev[0-9.]+\\.vsix$",
|
||||
`^${packageJson.name}[^/]+-dev[0-9.]+\\.vsix$`,
|
||||
);
|
||||
// Dev package filenames are of the form
|
||||
// vscode-codeql-0.0.1-dev.2019.9.27.19.55.20.vsix
|
||||
(await fs.readdir(distDir))
|
||||
(await readdir(distDir))
|
||||
.filter((name) => name.match(oldDevBuildPattern))
|
||||
.map((build) => {
|
||||
console.log(`Deleting old dev build ${build}...`);
|
||||
fs.unlinkSync(path.join(distDir, build));
|
||||
unlinkSync(join(distDir, build));
|
||||
});
|
||||
const now = new Date();
|
||||
packageJson.version =
|
||||
packageJson.version +
|
||||
`-dev.${now.getUTCFullYear()}.${
|
||||
`${packageJson.version}-dev.${now.getUTCFullYear()}.${
|
||||
now.getUTCMonth() + 1
|
||||
}.${now.getUTCDate()}` +
|
||||
`.${now.getUTCHours()}.${now.getUTCMinutes()}.${now.getUTCSeconds()}`;
|
||||
}
|
||||
|
||||
const distPath = path.join(distDir, packageJson.name);
|
||||
await fs.remove(distPath);
|
||||
await fs.mkdirs(distPath);
|
||||
const distPath = join(distDir, packageJson.name);
|
||||
await remove(distPath);
|
||||
await mkdirs(distPath);
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(distPath, "package.json"),
|
||||
await writeFile(
|
||||
join(distPath, "package.json"),
|
||||
JSON.stringify(packageJson, null, 2),
|
||||
);
|
||||
|
||||
const sourcePath = path.join(__dirname, "..");
|
||||
const sourcePath = join(__dirname, "..");
|
||||
console.log(
|
||||
`Copying package '${packageJson.name}' and its dependencies to '${distPath}'...`,
|
||||
);
|
||||
await copyPackage(sourcePath, distPath);
|
||||
|
||||
return {
|
||||
distPath: distPath,
|
||||
distPath,
|
||||
name: packageJson.name,
|
||||
version: packageJson.version,
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as gulp from "gulp";
|
||||
import { series, parallel } from "gulp";
|
||||
import { compileTypeScript, watchTypeScript, cleanOutput } from "./typescript";
|
||||
import { compileTextMateGrammar } from "./textmate";
|
||||
import { copyTestData, watchTestData } from "./tests";
|
||||
@@ -6,9 +6,9 @@ import { compileView, watchView } from "./webpack";
|
||||
import { packageExtension } from "./package";
|
||||
import { injectAppInsightsKey } from "./appInsights";
|
||||
|
||||
export const buildWithoutPackage = gulp.series(
|
||||
export const buildWithoutPackage = series(
|
||||
cleanOutput,
|
||||
gulp.parallel(
|
||||
parallel(
|
||||
compileTypeScript,
|
||||
compileTextMateGrammar,
|
||||
compileView,
|
||||
@@ -27,7 +27,7 @@ export {
|
||||
injectAppInsightsKey,
|
||||
compileView,
|
||||
};
|
||||
export default gulp.series(
|
||||
export default series(
|
||||
buildWithoutPackage,
|
||||
injectAppInsightsKey,
|
||||
packageExtension,
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import * as path from "path";
|
||||
import { resolve } from "path";
|
||||
import { deployPackage } from "./deploy";
|
||||
import * as childProcess from "child-process-promise";
|
||||
import { spawn } from "child-process-promise";
|
||||
|
||||
export async function packageExtension(): Promise<void> {
|
||||
const deployedPackage = await deployPackage(path.resolve("package.json"));
|
||||
const deployedPackage = await deployPackage(resolve("package.json"));
|
||||
console.log(
|
||||
`Packaging extension '${deployedPackage.name}@${deployedPackage.version}'...`,
|
||||
);
|
||||
const args = [
|
||||
"package",
|
||||
"--out",
|
||||
path.resolve(
|
||||
resolve(
|
||||
deployedPackage.distPath,
|
||||
"..",
|
||||
`${deployedPackage.name}-${deployedPackage.version}.vsix`,
|
||||
),
|
||||
];
|
||||
const proc = childProcess.spawn("./node_modules/.bin/vsce", args, {
|
||||
const proc = spawn("./node_modules/.bin/vsce", args, {
|
||||
cwd: deployedPackage.distPath,
|
||||
});
|
||||
proc.childProcess.stdout!.on("data", (data) => {
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import * as gulp from "gulp";
|
||||
import { watch, src, dest } from "gulp";
|
||||
|
||||
export function copyTestData() {
|
||||
return Promise.all([copyNoWorkspaceData(), copyCliIntegrationData()]);
|
||||
}
|
||||
|
||||
export function watchTestData() {
|
||||
return gulp.watch(["src/vscode-tests/*/data/**/*"], copyTestData);
|
||||
return watch(["src/vscode-tests/*/data/**/*"], copyTestData);
|
||||
}
|
||||
|
||||
function copyNoWorkspaceData() {
|
||||
return gulp
|
||||
.src("src/vscode-tests/no-workspace/data/**/*")
|
||||
.pipe(gulp.dest("out/vscode-tests/no-workspace/data"));
|
||||
return src("src/vscode-tests/no-workspace/data/**/*").pipe(
|
||||
dest("out/vscode-tests/no-workspace/data"),
|
||||
);
|
||||
}
|
||||
|
||||
function copyCliIntegrationData() {
|
||||
return gulp
|
||||
.src("src/vscode-tests/cli-integration/data/**/*")
|
||||
.pipe(gulp.dest("out/vscode-tests/cli-integration/data"));
|
||||
return src("src/vscode-tests/cli-integration/data/**/*").pipe(
|
||||
dest("out/vscode-tests/cli-integration/data"),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as gulp from "gulp";
|
||||
import * as jsYaml from "js-yaml";
|
||||
import * as through from "through2";
|
||||
import { src, dest } from "gulp";
|
||||
import { load } from "js-yaml";
|
||||
import { obj } from "through2";
|
||||
import * as PluginError from "plugin-error";
|
||||
import * as Vinyl from "vinyl";
|
||||
|
||||
@@ -61,11 +61,11 @@ function getNodeMatchText(rule: any): string {
|
||||
for (const patternIndex in rule.patterns) {
|
||||
const pattern = rule.patterns[patternIndex];
|
||||
if (pattern.include !== null) {
|
||||
patterns.push("(?" + pattern.include + ")");
|
||||
patterns.push(`(?${pattern.include})`);
|
||||
}
|
||||
}
|
||||
|
||||
return "(?:" + patterns.join("|") + ")";
|
||||
return `(?:${patterns.join("|")})`;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
@@ -149,8 +149,8 @@ function visitAllMatchesInRule(rule: any, action: (match: any) => any) {
|
||||
* @param key Base key of the property to be transformed.
|
||||
*/
|
||||
function expandPatternMatchProperties(rule: any, key: "begin" | "end") {
|
||||
const patternKey = key + "Pattern";
|
||||
const capturesKey = key + "Captures";
|
||||
const patternKey = `${key}Pattern`;
|
||||
const capturesKey = `${key}Captures`;
|
||||
const pattern = rule[patternKey];
|
||||
if (pattern !== undefined) {
|
||||
const patterns: string[] = Array.isArray(pattern) ? pattern : [pattern];
|
||||
@@ -207,7 +207,7 @@ function transformFile(yaml: any) {
|
||||
});
|
||||
|
||||
if (yaml.regexOptions !== undefined) {
|
||||
const regexOptions = "(?" + yaml.regexOptions + ")";
|
||||
const regexOptions = `(?${yaml.regexOptions})`;
|
||||
visitAllRulesInFile(yaml, (rule) => {
|
||||
visitAllMatchesInRule(rule, (match) => {
|
||||
return regexOptions + match;
|
||||
@@ -219,7 +219,7 @@ function transformFile(yaml: any) {
|
||||
}
|
||||
|
||||
export function transpileTextMateGrammar() {
|
||||
return through.obj(
|
||||
return obj(
|
||||
(
|
||||
file: Vinyl,
|
||||
_encoding: string,
|
||||
@@ -230,7 +230,7 @@ export function transpileTextMateGrammar() {
|
||||
} else if (file.isBuffer()) {
|
||||
const buf: Buffer = file.contents;
|
||||
const yamlText: string = buf.toString("utf8");
|
||||
const jsonData: any = jsYaml.load(yamlText);
|
||||
const jsonData: any = load(yamlText);
|
||||
transformFile(jsonData);
|
||||
|
||||
file.contents = Buffer.from(JSON.stringify(jsonData, null, 2), "utf8");
|
||||
@@ -247,8 +247,7 @@ export function transpileTextMateGrammar() {
|
||||
}
|
||||
|
||||
export function compileTextMateGrammar() {
|
||||
return gulp
|
||||
.src("syntaxes/*.tmLanguage.yml")
|
||||
return src("syntaxes/*.tmLanguage.yml")
|
||||
.pipe(transpileTextMateGrammar())
|
||||
.pipe(gulp.dest("out/syntaxes"));
|
||||
.pipe(dest("out/syntaxes"));
|
||||
}
|
||||
|
||||
@@ -1,23 +1,8 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"strict": true,
|
||||
"module": "commonjs",
|
||||
"target": "es2017",
|
||||
"lib": ["ES2021"],
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"rootDir": ".",
|
||||
"strictNullChecks": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"preserveWatchOutput": true,
|
||||
"newLine": "lf",
|
||||
"noImplicitReturns": true,
|
||||
"experimentalDecorators": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"esModuleInterop": true
|
||||
"rootDir": "."
|
||||
},
|
||||
"include": ["*.ts"]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as colors from "ansi-colors";
|
||||
import * as gulp from "gulp";
|
||||
import * as sourcemaps from "gulp-sourcemaps";
|
||||
import { gray, red } from "ansi-colors";
|
||||
import { dest, watch } from "gulp";
|
||||
import { init, write } from "gulp-sourcemaps";
|
||||
import * as ts from "gulp-typescript";
|
||||
import * as del from "del";
|
||||
|
||||
@@ -9,24 +9,16 @@ function goodReporter(): ts.reporter.Reporter {
|
||||
error: (error, typescript) => {
|
||||
if (error.tsFile) {
|
||||
console.log(
|
||||
"[" +
|
||||
colors.gray("gulp-typescript") +
|
||||
"] " +
|
||||
colors.red(
|
||||
error.fullFilename +
|
||||
"(" +
|
||||
(error.startPosition!.line + 1) +
|
||||
"," +
|
||||
error.startPosition!.character +
|
||||
"): ",
|
||||
) +
|
||||
"error TS" +
|
||||
error.diagnostic.code +
|
||||
": " +
|
||||
typescript.flattenDiagnosticMessageText(
|
||||
error.diagnostic.messageText,
|
||||
"\n",
|
||||
),
|
||||
`[${gray("gulp-typescript")}] ${red(
|
||||
`${error.fullFilename}(${error.startPosition!.line + 1},${
|
||||
error.startPosition!.character
|
||||
}): `,
|
||||
)}error TS${
|
||||
error.diagnostic.code
|
||||
}: ${typescript.flattenDiagnosticMessageText(
|
||||
error.diagnostic.messageText,
|
||||
"\n",
|
||||
)}`,
|
||||
);
|
||||
} else {
|
||||
console.log(error.message);
|
||||
@@ -39,24 +31,24 @@ const tsProject = ts.createProject("tsconfig.json");
|
||||
|
||||
export function cleanOutput() {
|
||||
return tsProject.projectDirectory
|
||||
? del(tsProject.projectDirectory + "/out/*")
|
||||
? del(`${tsProject.projectDirectory}/out/*`)
|
||||
: Promise.resolve();
|
||||
}
|
||||
|
||||
export function compileTypeScript() {
|
||||
return tsProject
|
||||
.src()
|
||||
.pipe(sourcemaps.init())
|
||||
.pipe(init())
|
||||
.pipe(tsProject(goodReporter()))
|
||||
.pipe(
|
||||
sourcemaps.write(".", {
|
||||
write(".", {
|
||||
includeContent: false,
|
||||
sourceRoot: ".",
|
||||
}),
|
||||
)
|
||||
.pipe(gulp.dest("out"));
|
||||
.pipe(dest("out"));
|
||||
}
|
||||
|
||||
export function watchTypeScript() {
|
||||
gulp.watch("src/**/*.ts", compileTypeScript);
|
||||
watch("src/**/*.ts", compileTypeScript);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as path from "path";
|
||||
import { resolve } from "path";
|
||||
import * as webpack from "webpack";
|
||||
import * as MiniCssExtractPlugin from "mini-css-extract-plugin";
|
||||
|
||||
@@ -8,7 +8,7 @@ export const config: webpack.Configuration = {
|
||||
webview: "./src/view/webview.tsx",
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, "..", "out"),
|
||||
path: resolve(__dirname, "..", "out"),
|
||||
filename: "[name].js",
|
||||
},
|
||||
devtool: "inline-source-map",
|
||||
|
||||
@@ -3,212 +3,13 @@
|
||||
* https://jestjs.io/docs/configuration
|
||||
*/
|
||||
|
||||
/** @type {import('@jest/types').Config.InitialOptions} */
|
||||
module.exports = {
|
||||
// All imported modules in your tests should be mocked automatically
|
||||
// automock: false,
|
||||
|
||||
// Stop running tests after `n` failures
|
||||
// bail: 0,
|
||||
|
||||
// The directory where Jest should store its cached dependency information
|
||||
// cacheDirectory: "/private/var/folders/6m/1394pht172qgd7dmw1fwjk100000gn/T/jest_dx",
|
||||
|
||||
// Automatically clear mock calls, instances, contexts and results before every test
|
||||
// clearMocks: true,
|
||||
|
||||
// Indicates whether the coverage information should be collected while executing the test
|
||||
// collectCoverage: false,
|
||||
|
||||
// An array of glob patterns indicating a set of files for which coverage information should be collected
|
||||
// collectCoverageFrom: undefined,
|
||||
|
||||
// The directory where Jest should output its coverage files
|
||||
// coverageDirectory: undefined,
|
||||
|
||||
// An array of regexp pattern strings used to skip coverage collection
|
||||
// coveragePathIgnorePatterns: [
|
||||
// "/node_modules/"
|
||||
// ],
|
||||
|
||||
// Indicates which provider should be used to instrument code for coverage
|
||||
coverageProvider: 'v8',
|
||||
|
||||
// A list of reporter names that Jest uses when writing coverage reports
|
||||
// coverageReporters: [
|
||||
// "json",
|
||||
// "text",
|
||||
// "lcov",
|
||||
// "clover"
|
||||
// ],
|
||||
|
||||
// An object that configures minimum threshold enforcement for coverage results
|
||||
// coverageThreshold: undefined,
|
||||
|
||||
// A path to a custom dependency extractor
|
||||
// dependencyExtractor: undefined,
|
||||
|
||||
// Make calling deprecated APIs throw helpful error messages
|
||||
// errorOnDeprecated: false,
|
||||
|
||||
// The default configuration for fake timers
|
||||
// fakeTimers: {
|
||||
// "enableGlobally": false
|
||||
// },
|
||||
|
||||
// Force coverage collection from ignored files using an array of glob patterns
|
||||
// forceCoverageMatch: [],
|
||||
|
||||
// A path to a module which exports an async function that is triggered once before all test suites
|
||||
// globalSetup: undefined,
|
||||
|
||||
// A path to a module which exports an async function that is triggered once after all test suites
|
||||
// globalTeardown: undefined,
|
||||
|
||||
// A set of global variables that need to be available in all test environments
|
||||
// globals: {},
|
||||
|
||||
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
|
||||
// maxWorkers: "50%",
|
||||
|
||||
// An array of directory names to be searched recursively up from the requiring module's location
|
||||
// moduleDirectories: [
|
||||
// "node_modules"
|
||||
// ],
|
||||
|
||||
// An array of file extensions your modules use
|
||||
moduleFileExtensions: [
|
||||
'js',
|
||||
'mjs',
|
||||
'cjs',
|
||||
'jsx',
|
||||
'ts',
|
||||
'tsx',
|
||||
'json'
|
||||
projects: [
|
||||
"<rootDir>/src/view",
|
||||
"<rootDir>/test",
|
||||
"<rootDir>/src/vscode-tests/cli-integration",
|
||||
"<rootDir>/src/vscode-tests/no-workspace",
|
||||
"<rootDir>/src/vscode-tests/minimal-workspace",
|
||||
],
|
||||
|
||||
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
|
||||
'moduleNameMapper': {
|
||||
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/test/__mocks__/fileMock.ts',
|
||||
'\\.(css|less)$': '<rootDir>/test/__mocks__/styleMock.ts'
|
||||
},
|
||||
|
||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||
// modulePathIgnorePatterns: [],
|
||||
|
||||
// Activates notifications for test results
|
||||
// notify: false,
|
||||
|
||||
// An enum that specifies notification mode. Requires { notify: true }
|
||||
// notifyMode: "failure-change",
|
||||
|
||||
// A preset that is used as a base for Jest's configuration
|
||||
preset: 'ts-jest',
|
||||
|
||||
// Run tests from one or more projects
|
||||
// projects: undefined,
|
||||
|
||||
// Use this configuration option to add custom reporters to Jest
|
||||
// reporters: undefined,
|
||||
|
||||
// Automatically reset mock state before every test
|
||||
// resetMocks: false,
|
||||
|
||||
// Reset the module registry before running each individual test
|
||||
// resetModules: false,
|
||||
|
||||
// A path to a custom resolver
|
||||
// resolver: undefined,
|
||||
|
||||
// Automatically restore mock state and implementation before every test
|
||||
// restoreMocks: false,
|
||||
|
||||
// The root directory that Jest should scan for tests and modules within
|
||||
// rootDir: undefined,
|
||||
|
||||
// A list of paths to directories that Jest should use to search for files in
|
||||
// roots: [
|
||||
// "<rootDir>"
|
||||
// ],
|
||||
|
||||
// Allows you to use a custom runner instead of Jest's default test runner
|
||||
// runner: "jest-runner",
|
||||
|
||||
// The paths to modules that run some code to configure or set up the testing environment before each test
|
||||
// setupFiles: [],
|
||||
|
||||
// A list of paths to modules that run some code to configure or set up the testing framework before each test
|
||||
setupFilesAfterEnv: ['<rootDir>/test/jest.setup.ts'],
|
||||
|
||||
// The number of seconds after which a test is considered as slow and reported as such in the results.
|
||||
// slowTestThreshold: 5,
|
||||
|
||||
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
|
||||
// snapshotSerializers: [],
|
||||
|
||||
// The test environment that will be used for testing
|
||||
testEnvironment: 'jsdom',
|
||||
|
||||
// Options that will be passed to the testEnvironment
|
||||
// testEnvironmentOptions: {},
|
||||
|
||||
// Adds a location field to test results
|
||||
// testLocationInResults: false,
|
||||
|
||||
// The glob patterns Jest uses to detect test files
|
||||
testMatch: [
|
||||
'**/__tests__/**/*.[jt]s?(x)'
|
||||
],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
|
||||
// testPathIgnorePatterns: [
|
||||
// "/node_modules/"
|
||||
// ],
|
||||
|
||||
// The regexp pattern or array of patterns that Jest uses to detect test files
|
||||
// testRegex: [],
|
||||
|
||||
// This option allows the use of a custom results processor
|
||||
// testResultsProcessor: undefined,
|
||||
|
||||
// This option allows use of a custom test runner
|
||||
// testRunner: "jest-circus/runner",
|
||||
|
||||
// A map from regular expressions to paths to transformers
|
||||
transform: {
|
||||
'^.+\\.tsx?$': [
|
||||
'ts-jest',
|
||||
{
|
||||
tsconfig: 'src/view/tsconfig.spec.json',
|
||||
},
|
||||
],
|
||||
'node_modules': [
|
||||
'babel-jest',
|
||||
{
|
||||
presets: [
|
||||
'@babel/preset-env'
|
||||
],
|
||||
plugins: [
|
||||
'@babel/plugin-transform-modules-commonjs',
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||
'transformIgnorePatterns': [
|
||||
// These use ES modules, so need to be transformed
|
||||
'node_modules/(?!(?:@vscode/webview-ui-toolkit|@microsoft/.+|exenv-es6)/.*)'
|
||||
],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
||||
// unmockedModulePathPatterns: undefined,
|
||||
|
||||
// Indicates whether each individual test should be reported during the run
|
||||
// verbose: undefined,
|
||||
|
||||
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
|
||||
// watchPathIgnorePatterns: [],
|
||||
|
||||
// Whether to use watchman for file crawling
|
||||
// watchman: true,
|
||||
};
|
||||
|
||||
7157
extensions/ql-vscode/package-lock.json
generated
7157
extensions/ql-vscode/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
||||
"description": "CodeQL for Visual Studio Code",
|
||||
"author": "GitHub",
|
||||
"private": true,
|
||||
"version": "1.7.6",
|
||||
"version": "1.7.7",
|
||||
"publisher": "GitHub",
|
||||
"license": "MIT",
|
||||
"icon": "media/VS-marketplace-CodeQL-icon.png",
|
||||
@@ -13,7 +13,7 @@
|
||||
"url": "https://github.com/github/vscode-codeql"
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "^1.59.0",
|
||||
"vscode": "^1.67.0",
|
||||
"node": "^16.13.0",
|
||||
"npm": ">=7.20.6"
|
||||
},
|
||||
@@ -61,6 +61,9 @@
|
||||
"onCommand:codeQL.chooseDatabaseLgtm",
|
||||
"onCommand:codeQLDatabases.chooseDatabase",
|
||||
"onCommand:codeQLDatabases.setCurrentDatabase",
|
||||
"onCommand:codeQLDatabasesExperimental.openConfigFile",
|
||||
"onCommand:codeQLDatabasesExperimental.addNewList",
|
||||
"onCommand:codeQLDatabasesExperimental.setSelectedItem",
|
||||
"onCommand:codeQL.quickQuery",
|
||||
"onCommand:codeQL.restartQueryServer",
|
||||
"onWebviewPanel:resultsView",
|
||||
@@ -359,10 +362,17 @@
|
||||
{
|
||||
"command": "codeQLDatabasesExperimental.openConfigFile",
|
||||
"title": "Open Database Configuration File",
|
||||
"icon": {
|
||||
"light": "media/light/edit.svg",
|
||||
"dark": "media/dark/edit.svg"
|
||||
}
|
||||
"icon": "$(edit)"
|
||||
},
|
||||
{
|
||||
"command": "codeQLDatabasesExperimental.addNewList",
|
||||
"title": "Add new list",
|
||||
"icon": "$(new-folder)"
|
||||
},
|
||||
{
|
||||
"command": "codeQLDatabasesExperimental.setSelectedItem",
|
||||
"title": "Select Item",
|
||||
"icon": "$(circle-small-filled)"
|
||||
},
|
||||
{
|
||||
"command": "codeQLDatabases.chooseDatabaseFolder",
|
||||
@@ -767,6 +777,11 @@
|
||||
"command": "codeQLDatabasesExperimental.openConfigFile",
|
||||
"when": "view == codeQLDatabasesExperimental",
|
||||
"group": "navigation"
|
||||
},
|
||||
{
|
||||
"command": "codeQLDatabasesExperimental.addNewList",
|
||||
"when": "view == codeQLDatabasesExperimental",
|
||||
"group": "navigation"
|
||||
}
|
||||
],
|
||||
"view/item/context": [
|
||||
@@ -795,6 +810,11 @@
|
||||
"group": "9_qlCommands",
|
||||
"when": "view == codeQLDatabases"
|
||||
},
|
||||
{
|
||||
"command": "codeQLDatabasesExperimental.setSelectedItem",
|
||||
"when": "view == codeQLDatabasesExperimental && viewItem == selectableDbItem",
|
||||
"group": "inline"
|
||||
},
|
||||
{
|
||||
"command": "codeQLQueryHistory.openQuery",
|
||||
"group": "9_qlCommands",
|
||||
@@ -985,6 +1005,14 @@
|
||||
"command": "codeQLDatabasesExperimental.openConfigFile",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "codeQLDatabasesExperimental.addNewList",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "codeQLDatabasesExperimental.setSelectedItem",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "codeQLDatabases.setCurrentDatabase",
|
||||
"when": "false"
|
||||
@@ -1270,19 +1298,21 @@
|
||||
"watch:webpack": "gulp watchView",
|
||||
"watch:files": "gulp watchTestData",
|
||||
"test": "npm-run-all -p test:*",
|
||||
"test:unit": "mocha --config .mocharc.json 'test/pure-tests/**/*.ts'",
|
||||
"test:view": "jest",
|
||||
"integration": "node ./out/vscode-tests/run-integration-tests.js no-workspace,minimal-workspace",
|
||||
"integration:no-workspace": "node ./out/vscode-tests/run-integration-tests.js no-workspace",
|
||||
"integration:minimal-workspace": "node ./out/vscode-tests/run-integration-tests.js minimal-workspace",
|
||||
"cli-integration": "node ./out/vscode-tests/run-integration-tests.js cli-integration",
|
||||
"test:unit": "cross-env TZ=UTC LANG=en-US jest --projects test",
|
||||
"test:view": "jest --projects src/view",
|
||||
"integration": "npm-run-all integration:*",
|
||||
"integration:no-workspace": "jest --projects src/vscode-tests/no-workspace",
|
||||
"integration:minimal-workspace": "jest --projects src/vscode-tests/minimal-workspace",
|
||||
"cli-integration": "jest --projects src/vscode-tests/cli-integration",
|
||||
"update-vscode": "node ./node_modules/vscode/bin/install",
|
||||
"format": "prettier --write **/*.{ts,tsx} && eslint . --ext .ts,.tsx --fix",
|
||||
"lint": "eslint . --ext .ts,.tsx --max-warnings=0",
|
||||
"format-staged": "lint-staged",
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"build-storybook": "build-storybook",
|
||||
"lint:scenarios": "ts-node scripts/lint-scenarios.ts"
|
||||
"lint:scenarios": "ts-node scripts/lint-scenarios.ts",
|
||||
"check-types": "find . -type f -name \"tsconfig.json\" -not -path \"./node_modules/*\" | sed -r 's|/[^/]+$||' | sort | uniq | xargs -I {} sh -c \"echo Checking types in {} && cd {} && npx tsc --noEmit\"",
|
||||
"postinstall": "patch-package"
|
||||
},
|
||||
"dependencies": {
|
||||
"@octokit/plugin-retry": "^3.0.9",
|
||||
@@ -1321,8 +1351,8 @@
|
||||
"tree-kill": "~1.2.2",
|
||||
"unzipper": "~0.10.5",
|
||||
"vscode-extension-telemetry": "^0.1.6",
|
||||
"vscode-jsonrpc": "^5.0.1",
|
||||
"vscode-languageclient": "^6.1.3",
|
||||
"vscode-jsonrpc": "^8.0.2",
|
||||
"vscode-languageclient": "^8.0.2",
|
||||
"vscode-test-adapter-api": "~1.7.0",
|
||||
"vscode-test-adapter-util": "~0.7.0",
|
||||
"zip-a-folder": "~1.1.3"
|
||||
@@ -1343,8 +1373,6 @@
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^12.1.5",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@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/d3": "^7.4.0",
|
||||
@@ -1359,17 +1387,13 @@
|
||||
"@types/jest": "^29.0.2",
|
||||
"@types/js-yaml": "^3.12.5",
|
||||
"@types/jszip": "~3.1.6",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@types/nanoid": "^3.0.0",
|
||||
"@types/node": "^16.11.25",
|
||||
"@types/node-fetch": "~2.5.2",
|
||||
"@types/proxyquire": "~1.3.28",
|
||||
"@types/react": "^17.0.2",
|
||||
"@types/react-dom": "^17.0.2",
|
||||
"@types/sarif": "~2.1.2",
|
||||
"@types/semver": "~7.2.0",
|
||||
"@types/sinon": "~7.5.2",
|
||||
"@types/sinon-chai": "~3.2.3",
|
||||
"@types/stream-chain": "~2.0.1",
|
||||
"@types/stream-json": "~1.7.1",
|
||||
"@types/tar-stream": "^2.2.2",
|
||||
@@ -1383,15 +1407,16 @@
|
||||
"@typescript-eslint/eslint-plugin": "^5.38.0",
|
||||
"@typescript-eslint/parser": "^5.38.0",
|
||||
"@vscode/test-electron": "^2.2.0",
|
||||
"@vscode/vsce": "^2.15.0",
|
||||
"ansi-colors": "^4.1.1",
|
||||
"applicationinsights": "^2.3.5",
|
||||
"babel-loader": "^8.2.5",
|
||||
"chai": "^4.2.0",
|
||||
"chai-as-promised": "~7.1.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"css-loader": "~3.1.0",
|
||||
"del": "^6.0.0",
|
||||
"eslint": "^8.23.1",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-github": "^4.4.1",
|
||||
"eslint-plugin-jest-dom": "^4.0.2",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.31.8",
|
||||
@@ -1406,15 +1431,12 @@
|
||||
"husky": "~4.3.8",
|
||||
"jest": "^29.0.3",
|
||||
"jest-environment-jsdom": "^29.0.3",
|
||||
"jest-runner-vscode": "^3.0.1",
|
||||
"lint-staged": "~10.2.2",
|
||||
"mini-css-extract-plugin": "^2.6.1",
|
||||
"mocha": "^10.0.0",
|
||||
"mocha-sinon": "~2.1.2",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"patch-package": "^6.5.0",
|
||||
"prettier": "^2.7.1",
|
||||
"proxyquire": "~2.1.3",
|
||||
"sinon": "~14.0.0",
|
||||
"sinon-chai": "~3.5.0",
|
||||
"tar-stream": "^2.2.0",
|
||||
"through2": "^4.0.2",
|
||||
"ts-jest": "^29.0.1",
|
||||
@@ -1423,14 +1445,13 @@
|
||||
"ts-node": "^10.7.0",
|
||||
"ts-protoc-gen": "^0.9.0",
|
||||
"typescript": "^4.5.5",
|
||||
"vsce": "^2.7.0",
|
||||
"webpack": "^5.62.2",
|
||||
"webpack-cli": "^4.6.0"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "npm run format-staged",
|
||||
"pre-push": "npm run lint && scripts/forbid-mocha-only"
|
||||
"pre-push": "npm run lint && scripts/forbid-test-only"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
|
||||
155
extensions/ql-vscode/patches/jest-runner-vscode+3.0.1.patch
Normal file
155
extensions/ql-vscode/patches/jest-runner-vscode+3.0.1.patch
Normal file
@@ -0,0 +1,155 @@
|
||||
diff --git a/node_modules/jest-runner-vscode/dist/child/environment.js b/node_modules/jest-runner-vscode/dist/child/environment.js
|
||||
index 1ac28d5..f91f216 100644
|
||||
--- a/node_modules/jest-runner-vscode/dist/child/environment.js
|
||||
+++ b/node_modules/jest-runner-vscode/dist/child/environment.js
|
||||
@@ -10,6 +10,21 @@ const wrap_io_1 = __importDefault(require("./wrap-io"));
|
||||
const load_pnp_1 = __importDefault(require("./load-pnp"));
|
||||
const ipc = new ipc_client_1.default('env');
|
||||
class VSCodeEnvironment extends jest_environment_node_1.default {
|
||||
+ constructor(config, context) {
|
||||
+ super(config, context);
|
||||
+ // The _VSCODE_NODE_MODULES is a proxy which will require a module if any property
|
||||
+ // on it is accessed. This is a workaround for the fact that jest will call
|
||||
+ // _isMockFunction on the module, which will cause that function to be required.
|
||||
+ this.global._VSCODE_NODE_MODULES = new Proxy(this.global._VSCODE_NODE_MODULES, {
|
||||
+ get(target, prop) {
|
||||
+ if (prop === '_isMockFunction') {
|
||||
+ return undefined;
|
||||
+ }
|
||||
+ return target[prop];
|
||||
+ },
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
async setup() {
|
||||
await super.setup();
|
||||
await (0, load_pnp_1.default)();
|
||||
diff --git a/node_modules/jest-runner-vscode/dist/child/runner.js b/node_modules/jest-runner-vscode/dist/child/runner.js
|
||||
index 0663c5c..4991663 100644
|
||||
--- a/node_modules/jest-runner-vscode/dist/child/runner.js
|
||||
+++ b/node_modules/jest-runner-vscode/dist/child/runner.js
|
||||
@@ -18,10 +18,13 @@ async function run() {
|
||||
const ipc = new ipc_client_1.default('child');
|
||||
const disconnected = new Promise(resolve => ipc.on('disconnect', resolve));
|
||||
try {
|
||||
- const { PARENT_JEST_OPTIONS } = process_1.default.env;
|
||||
+ const { PARENT_JEST_OPTIONS, PARENT_CWD } = process_1.default.env;
|
||||
if (!PARENT_JEST_OPTIONS) {
|
||||
throw new Error('PARENT_JEST_OPTIONS is not defined');
|
||||
}
|
||||
+ if (PARENT_CWD) {
|
||||
+ process_1.default.chdir(PARENT_CWD);
|
||||
+ }
|
||||
const options = JSON.parse(PARENT_JEST_OPTIONS);
|
||||
const jestOptions = [
|
||||
...options.args,
|
||||
diff --git a/node_modules/jest-runner-vscode/dist/public-types.d.ts b/node_modules/jest-runner-vscode/dist/public-types.d.ts
|
||||
index 57716e5..d8614af 100644
|
||||
--- a/node_modules/jest-runner-vscode/dist/public-types.d.ts
|
||||
+++ b/node_modules/jest-runner-vscode/dist/public-types.d.ts
|
||||
@@ -59,4 +59,5 @@ export interface RunnerOptions {
|
||||
* code, or download progress. Defaults to `false`.
|
||||
*/
|
||||
quiet?: boolean;
|
||||
+ retries?: number;
|
||||
}
|
||||
diff --git a/node_modules/jest-runner-vscode/dist/run-vscode.d.ts b/node_modules/jest-runner-vscode/dist/run-vscode.d.ts
|
||||
index 8657ace..4d35409 100644
|
||||
--- a/node_modules/jest-runner-vscode/dist/run-vscode.d.ts
|
||||
+++ b/node_modules/jest-runner-vscode/dist/run-vscode.d.ts
|
||||
@@ -16,5 +16,7 @@ export declare type RunVSCodeOptions = {
|
||||
onFailure: JestRunner.OnTestFailure;
|
||||
ipc: InstanceType<typeof IPC>;
|
||||
quiet?: boolean;
|
||||
+ attempt?: number;
|
||||
+ maxRetries?: number;
|
||||
};
|
||||
-export default function runVSCode({ vscodePath, args, jestArgs, env, tests, globalConfig, filterOutput, onStart, onResult, onFailure, ipc, quiet, }: RunVSCodeOptions): Promise<void>;
|
||||
+export default function runVSCode(options: RunVSCodeOptions): Promise<void>;
|
||||
diff --git a/node_modules/jest-runner-vscode/dist/run-vscode.js b/node_modules/jest-runner-vscode/dist/run-vscode.js
|
||||
index 5d8e513..7e556ee 100644
|
||||
--- a/node_modules/jest-runner-vscode/dist/run-vscode.js
|
||||
+++ b/node_modules/jest-runner-vscode/dist/run-vscode.js
|
||||
@@ -5,8 +5,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const child_process_1 = __importDefault(require("child_process"));
|
||||
const console_1 = __importDefault(require("console"));
|
||||
-async function runVSCode({ vscodePath, args, jestArgs, env, tests, globalConfig, filterOutput, onStart, onResult, onFailure, ipc, quiet, }) {
|
||||
- return await new Promise(resolve => {
|
||||
+const fs_1 = __importDefault(require("fs"));
|
||||
+const path_1 = __importDefault(require("path"));
|
||||
+const os_1 = __importDefault(require("os"));
|
||||
+async function runVSCode(options) {
|
||||
+ const { vscodePath, args, jestArgs, env, tests, globalConfig, filterOutput, onStart, onResult, onFailure, ipc, quiet, attempt, maxRetries, } = options;
|
||||
+ const tempUserDir = await fs_1.default.promises.mkdtemp(path_1.default.resolve(os_1.default.tmpdir(), 'jest-runner-vscode-user-data-'));
|
||||
+ return await new Promise(promiseResolve => {
|
||||
+ const resolve = () => {
|
||||
+ fs_1.default.rm(tempUserDir, { recursive: true }, () => {
|
||||
+ promiseResolve();
|
||||
+ });
|
||||
+ };
|
||||
const useStdErr = globalConfig.json || globalConfig.useStderr;
|
||||
const log = useStdErr
|
||||
? console_1.default.error.bind(console_1.default)
|
||||
@@ -82,7 +92,11 @@ async function runVSCode({ vscodePath, args, jestArgs, env, tests, globalConfig,
|
||||
ipc.server.on('stdout', onStdout);
|
||||
ipc.server.on('stderr', onStderr);
|
||||
ipc.server.on('error', onError);
|
||||
- const vscode = child_process_1.default.spawn(vscodePath, args, { env: environment });
|
||||
+ const launchArgs = args;
|
||||
+ if (!hasArg('user-data-dir', launchArgs)) {
|
||||
+ launchArgs.push(`--user-data-dir=${tempUserDir}`);
|
||||
+ }
|
||||
+ const vscode = child_process_1.default.spawn(vscodePath, launchArgs, { env: environment });
|
||||
if (!silent && !filterOutput) {
|
||||
vscode.stdout.pipe(process.stdout);
|
||||
vscode.stderr.pipe(process.stderr);
|
||||
@@ -99,6 +113,29 @@ async function runVSCode({ vscodePath, args, jestArgs, env, tests, globalConfig,
|
||||
exited = true;
|
||||
const exit = code ?? signal ?? '<unknown>';
|
||||
const message = `VS Code exited with exit code ${exit}`;
|
||||
+ const currentAttempt = attempt ?? 0;
|
||||
+ const incompleteTests = tests.some(test => !completedTests.has(test));
|
||||
+ if (maxRetries &&
|
||||
+ maxRetries > 0 &&
|
||||
+ currentAttempt < maxRetries &&
|
||||
+ incompleteTests) {
|
||||
+ silent || quiet || log(message);
|
||||
+ const newAttempt = currentAttempt + 1;
|
||||
+ const newTests = tests.filter(test => !completedTests.has(test));
|
||||
+ ipc.server.off('testFileResult', onTestFileResult);
|
||||
+ ipc.server.off('testStart', onTestStart);
|
||||
+ ipc.server.off('testFileStart', onTestStart);
|
||||
+ ipc.server.off('stdout', onStdout);
|
||||
+ ipc.server.off('stderr', onStderr);
|
||||
+ ipc.server.off('error', onError);
|
||||
+ await runVSCode({
|
||||
+ ...options,
|
||||
+ tests: newTests,
|
||||
+ attempt: newAttempt,
|
||||
+ });
|
||||
+ resolve();
|
||||
+ return;
|
||||
+ }
|
||||
if (typeof code !== 'number' || code !== 0) {
|
||||
silent || quiet || console_1.default.error(message);
|
||||
const error = vscodeError ?? childError ?? new Error(message);
|
||||
@@ -138,3 +175,6 @@ async function runVSCode({ vscodePath, args, jestArgs, env, tests, globalConfig,
|
||||
});
|
||||
}
|
||||
exports.default = runVSCode;
|
||||
+function hasArg(argName, argList) {
|
||||
+ return argList.some(a => a === `--${argName}` || a.startsWith(`--${argName}=`));
|
||||
+}
|
||||
diff --git a/node_modules/jest-runner-vscode/dist/runner.js b/node_modules/jest-runner-vscode/dist/runner.js
|
||||
index e24c976..c374022 100644
|
||||
--- a/node_modules/jest-runner-vscode/dist/runner.js
|
||||
+++ b/node_modules/jest-runner-vscode/dist/runner.js
|
||||
@@ -107,6 +107,7 @@ class VSCodeTestRunner {
|
||||
onFailure,
|
||||
ipc,
|
||||
quiet: vscodeOptions.quiet,
|
||||
+ maxRetries: vscodeOptions.retries,
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
@@ -11,8 +11,8 @@
|
||||
* Usage: npx ts-node scripts/add-fields-to-scenarios.ts
|
||||
*/
|
||||
|
||||
import * as fs from "fs-extra";
|
||||
import * as path from "path";
|
||||
import { pathExists, readJson, writeJson } from "fs-extra";
|
||||
import { resolve, relative } from "path";
|
||||
|
||||
import { Octokit, type RestEndpointMethodTypes } from "@octokit/rest";
|
||||
import { throttling } from "@octokit/plugin-throttling";
|
||||
@@ -23,11 +23,8 @@ import { isGetVariantAnalysisRequest } from "../src/mocks/gh-api-request";
|
||||
import { VariantAnalysis } from "../src/remote-queries/gh-api/variant-analysis";
|
||||
import { RepositoryWithMetadata } from "../src/remote-queries/gh-api/repository";
|
||||
|
||||
const extensionDirectory = path.resolve(__dirname, "..");
|
||||
const scenariosDirectory = path.resolve(
|
||||
extensionDirectory,
|
||||
"src/mocks/scenarios",
|
||||
);
|
||||
const extensionDirectory = resolve(__dirname, "..");
|
||||
const scenariosDirectory = resolve(extensionDirectory, "src/mocks/scenarios");
|
||||
|
||||
// Make sure we don't run into rate limits by automatically waiting until we can
|
||||
// make another request.
|
||||
@@ -84,8 +81,8 @@ async function addFieldsToRepository(repository: RepositoryWithMetadata) {
|
||||
}
|
||||
|
||||
async function addFieldsToScenarios() {
|
||||
if (!(await fs.pathExists(scenariosDirectory))) {
|
||||
console.error("Scenarios directory does not exist: " + scenariosDirectory);
|
||||
if (!(await pathExists(scenariosDirectory))) {
|
||||
console.error(`Scenarios directory does not exist: ${scenariosDirectory}`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -94,7 +91,7 @@ async function addFieldsToScenarios() {
|
||||
continue;
|
||||
}
|
||||
|
||||
const data: GitHubApiRequest = await fs.readJson(file);
|
||||
const data: GitHubApiRequest = await readJson(file);
|
||||
|
||||
if (!isGetVariantAnalysisRequest(data)) {
|
||||
continue;
|
||||
@@ -104,9 +101,7 @@ async function addFieldsToScenarios() {
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Adding fields to '${path.relative(scenariosDirectory, file)}'`,
|
||||
);
|
||||
console.log(`Adding fields to '${relative(scenariosDirectory, file)}'`);
|
||||
|
||||
const variantAnalysis = data.response.body as VariantAnalysis;
|
||||
|
||||
@@ -137,7 +132,7 @@ async function addFieldsToScenarios() {
|
||||
}
|
||||
}
|
||||
|
||||
await fs.writeJson(file, data, { spaces: 2 });
|
||||
await writeJson(file, data, { spaces: 2 });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
* Usage: npx ts-node scripts/fix-scenario-file-numbering.ts <scenario-name>
|
||||
*/
|
||||
|
||||
import * as fs from "fs-extra";
|
||||
import * as path from "path";
|
||||
import { pathExists, readdir, rename, readJson, writeJSON } from "fs-extra";
|
||||
import { resolve, extname, basename, join } from "path";
|
||||
|
||||
if (process.argv.length !== 3) {
|
||||
console.error("Expected 1 argument - the scenario name");
|
||||
@@ -19,21 +19,18 @@ if (process.argv.length !== 3) {
|
||||
|
||||
const scenarioName = process.argv[2];
|
||||
|
||||
const extensionDirectory = path.resolve(__dirname, "..");
|
||||
const scenariosDirectory = path.resolve(
|
||||
extensionDirectory,
|
||||
"src/mocks/scenarios",
|
||||
);
|
||||
const scenarioDirectory = path.resolve(scenariosDirectory, scenarioName);
|
||||
const extensionDirectory = resolve(__dirname, "..");
|
||||
const scenariosDirectory = resolve(extensionDirectory, "src/mocks/scenarios");
|
||||
const scenarioDirectory = resolve(scenariosDirectory, scenarioName);
|
||||
|
||||
async function fixScenarioFiles() {
|
||||
console.log(scenarioDirectory);
|
||||
if (!(await fs.pathExists(scenarioDirectory))) {
|
||||
console.error("Scenario directory does not exist: " + scenarioDirectory);
|
||||
if (!(await pathExists(scenarioDirectory))) {
|
||||
console.error(`Scenario directory does not exist: ${scenarioDirectory}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const files = await fs.readdir(scenarioDirectory);
|
||||
const files = await readdir(scenarioDirectory);
|
||||
|
||||
const orderedFiles = files.sort((a, b) => {
|
||||
const aNum = parseInt(a.split("-")[0]);
|
||||
@@ -43,30 +40,30 @@ async function fixScenarioFiles() {
|
||||
|
||||
let index = 0;
|
||||
for (const file of orderedFiles) {
|
||||
const ext = path.extname(file);
|
||||
const ext = extname(file);
|
||||
if (ext === ".json") {
|
||||
const fileName = path.basename(file, ext);
|
||||
const fileName = basename(file, ext);
|
||||
const fileCurrentIndex = parseInt(fileName.split("-")[0]);
|
||||
const fileNameWithoutIndex = fileName.split("-")[1];
|
||||
if (fileCurrentIndex !== index) {
|
||||
const newFileName = `${index}-${fileNameWithoutIndex}${ext}`;
|
||||
const oldFilePath = path.join(scenarioDirectory, file);
|
||||
const newFilePath = path.join(scenarioDirectory, newFileName);
|
||||
const oldFilePath = join(scenarioDirectory, file);
|
||||
const newFilePath = join(scenarioDirectory, newFileName);
|
||||
console.log(`Rename: ${oldFilePath} -> ${newFilePath}`);
|
||||
await fs.rename(oldFilePath, newFilePath);
|
||||
await rename(oldFilePath, newFilePath);
|
||||
|
||||
if (fileNameWithoutIndex === "getVariantAnalysisRepoResult") {
|
||||
const oldZipFileName = `${fileCurrentIndex}-getVariantAnalysisRepoResult.body.zip`;
|
||||
const newZipFileName = `${index}-getVariantAnalysisRepoResult.body.zip`;
|
||||
const oldZipFilePath = path.join(scenarioDirectory, oldZipFileName);
|
||||
const newZipFilePath = path.join(scenarioDirectory, newZipFileName);
|
||||
const oldZipFilePath = join(scenarioDirectory, oldZipFileName);
|
||||
const newZipFilePath = join(scenarioDirectory, newZipFileName);
|
||||
console.log(`Rename: ${oldZipFilePath} -> ${newZipFilePath}`);
|
||||
await fs.rename(oldZipFilePath, newZipFilePath);
|
||||
await rename(oldZipFilePath, newZipFilePath);
|
||||
|
||||
const json = await fs.readJson(newFilePath);
|
||||
const json = await readJson(newFilePath);
|
||||
json.response.body = `file:${newZipFileName}`;
|
||||
console.log(`Response.body change to ${json.response.body}`);
|
||||
await fs.writeJSON(newFilePath, json);
|
||||
await writeJSON(newFilePath, json);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,44 +1,39 @@
|
||||
import * as fs from "fs-extra";
|
||||
import * as path from "path";
|
||||
import { pathExists, readFile } from "fs-extra";
|
||||
import { resolve, relative } from "path";
|
||||
|
||||
import Ajv from "ajv";
|
||||
import * as tsj from "ts-json-schema-generator";
|
||||
import { createGenerator } from "ts-json-schema-generator";
|
||||
|
||||
import { getFiles } from "./util/files";
|
||||
|
||||
const extensionDirectory = path.resolve(__dirname, "..");
|
||||
const rootDirectory = path.resolve(extensionDirectory, "../..");
|
||||
const scenariosDirectory = path.resolve(
|
||||
extensionDirectory,
|
||||
"src/mocks/scenarios",
|
||||
);
|
||||
const extensionDirectory = resolve(__dirname, "..");
|
||||
const rootDirectory = resolve(extensionDirectory, "../..");
|
||||
const scenariosDirectory = resolve(extensionDirectory, "src/mocks/scenarios");
|
||||
|
||||
const debug = process.env.RUNNER_DEBUG || process.argv.includes("--debug");
|
||||
|
||||
async function lintScenarios() {
|
||||
const schema = tsj
|
||||
.createGenerator({
|
||||
path: path.resolve(extensionDirectory, "src/mocks/gh-api-request.ts"),
|
||||
tsconfig: path.resolve(extensionDirectory, "tsconfig.json"),
|
||||
type: "GitHubApiRequest",
|
||||
skipTypeCheck: true,
|
||||
topRef: true,
|
||||
additionalProperties: true,
|
||||
})
|
||||
.createSchema("GitHubApiRequest");
|
||||
const schema = createGenerator({
|
||||
path: resolve(extensionDirectory, "src/mocks/gh-api-request.ts"),
|
||||
tsconfig: resolve(extensionDirectory, "tsconfig.json"),
|
||||
type: "GitHubApiRequest",
|
||||
skipTypeCheck: true,
|
||||
topRef: true,
|
||||
additionalProperties: true,
|
||||
}).createSchema("GitHubApiRequest");
|
||||
|
||||
const ajv = new Ajv();
|
||||
|
||||
if (!ajv.validateSchema(schema)) {
|
||||
throw new Error("Invalid schema: " + ajv.errorsText());
|
||||
throw new Error(`Invalid schema: ${ajv.errorsText()}`);
|
||||
}
|
||||
|
||||
const validate = await ajv.compile(schema);
|
||||
|
||||
let invalidFiles = 0;
|
||||
|
||||
if (!(await fs.pathExists(scenariosDirectory))) {
|
||||
console.error("Scenarios directory does not exist: " + scenariosDirectory);
|
||||
if (!(await pathExists(scenariosDirectory))) {
|
||||
console.error(`Scenarios directory does not exist: ${scenariosDirectory}`);
|
||||
// Do not exit with a non-zero status code, as this is not a fatal error.
|
||||
return;
|
||||
}
|
||||
@@ -48,21 +43,21 @@ async function lintScenarios() {
|
||||
continue;
|
||||
}
|
||||
|
||||
const contents = await fs.readFile(file, "utf8");
|
||||
const contents = await readFile(file, "utf8");
|
||||
const data = JSON.parse(contents);
|
||||
|
||||
if (!validate(data)) {
|
||||
validate.errors?.forEach((error) => {
|
||||
// https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-error-message
|
||||
console.log(
|
||||
`::error file=${path.relative(rootDirectory, file)}::${
|
||||
`::error file=${relative(rootDirectory, file)}::${
|
||||
error.instancePath
|
||||
}: ${error.message}`,
|
||||
);
|
||||
});
|
||||
invalidFiles++;
|
||||
} else if (debug) {
|
||||
console.log(`File '${path.relative(rootDirectory, file)}' is valid`);
|
||||
console.log(`File '${relative(rootDirectory, file)}' is valid`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import * as fs from "fs-extra";
|
||||
import * as path from "path";
|
||||
import { readdir } from "fs-extra";
|
||||
import { resolve } from "path";
|
||||
|
||||
// https://stackoverflow.com/a/45130990
|
||||
export async function* getFiles(dir: string): AsyncGenerator<string> {
|
||||
const dirents = await fs.readdir(dir, { withFileTypes: true });
|
||||
const dirents = await readdir(dir, { withFileTypes: true });
|
||||
for (const dirent of dirents) {
|
||||
const res = path.resolve(dir, dirent.name);
|
||||
const res = resolve(dir, dirent.name);
|
||||
if (dirent.isDirectory()) {
|
||||
yield* getFiles(res);
|
||||
} else {
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
WebviewPanelOptions,
|
||||
WebviewOptions,
|
||||
} from "vscode";
|
||||
import * as path from "path";
|
||||
import { join } from "path";
|
||||
|
||||
import { DisposableObject, DisposeHandler } from "./pure/disposable-object";
|
||||
import { tmpDir } from "./helpers";
|
||||
@@ -32,7 +32,7 @@ export abstract class AbstractWebview<
|
||||
> extends DisposableObject {
|
||||
protected panel: WebviewPanel | undefined;
|
||||
protected panelLoaded = false;
|
||||
protected panelLoadedCallBacks: (() => void)[] = [];
|
||||
protected panelLoadedCallBacks: Array<() => void> = [];
|
||||
|
||||
private panelResolves?: Array<(panel: WebviewPanel) => void>;
|
||||
|
||||
@@ -83,7 +83,7 @@ export abstract class AbstractWebview<
|
||||
localResourceRoots: [
|
||||
...(config.additionalOptions?.localResourceRoots ?? []),
|
||||
Uri.file(tmpDir.name),
|
||||
Uri.file(path.join(ctx.extensionPath, "out")),
|
||||
Uri.file(join(ctx.extensionPath, "out")),
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import * as fs from "fs-extra";
|
||||
import { pathExists } from "fs-extra";
|
||||
import * as unzipper from "unzipper";
|
||||
import * as vscode from "vscode";
|
||||
import { logger } from "./logging";
|
||||
import { extLogger } from "./common";
|
||||
|
||||
// All path operations in this file must be on paths *within* the zip
|
||||
// archive.
|
||||
import * as _path from "path";
|
||||
const path = _path.posix;
|
||||
import { posix } from "path";
|
||||
const path = posix;
|
||||
|
||||
export class File implements vscode.FileStat {
|
||||
type: vscode.FileType;
|
||||
@@ -75,7 +75,7 @@ export function encodeSourceArchiveUri(ref: ZipFileReference): vscode.Uri {
|
||||
if (encodedPath.startsWith("/")) {
|
||||
sourceArchiveZipPathStartIndex = 0;
|
||||
} else {
|
||||
encodedPath = "/" + encodedPath;
|
||||
encodedPath = `/${encodedPath}`;
|
||||
sourceArchiveZipPathStartIndex = 1;
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ export function encodeSourceArchiveUri(ref: ZipFileReference): vscode.Uri {
|
||||
const sourceArchiveZipPathEndIndex =
|
||||
sourceArchiveZipPathStartIndex + sourceArchiveZipPath.length;
|
||||
const authority = `${sourceArchiveZipPathStartIndex}-${sourceArchiveZipPathEndIndex}`;
|
||||
return vscode.Uri.parse(zipArchiveScheme + ":/", true).with({
|
||||
return vscode.Uri.parse(`${zipArchiveScheme}:/`, true).with({
|
||||
path: encodedPath,
|
||||
authority,
|
||||
});
|
||||
@@ -118,7 +118,7 @@ class InvalidSourceArchiveUriError extends Error {
|
||||
export function decodeSourceArchiveUri(uri: vscode.Uri): ZipFileReference {
|
||||
if (!uri.authority) {
|
||||
// Uri is malformed, but this is recoverable
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
`Warning: ${new InvalidSourceArchiveUriError(uri).message}`,
|
||||
);
|
||||
return {
|
||||
@@ -148,7 +148,7 @@ function ensureFile(map: DirectoryHierarchyMap, file: string) {
|
||||
const dirname = path.dirname(file);
|
||||
if (dirname === ".") {
|
||||
const error = `Ill-formed path ${file} in zip archive (expected absolute path)`;
|
||||
void logger.log(error);
|
||||
void extLogger.log(error);
|
||||
throw new Error(error);
|
||||
}
|
||||
ensureDir(map, dirname);
|
||||
@@ -176,7 +176,7 @@ type Archive = {
|
||||
};
|
||||
|
||||
async function parse_zip(zipPath: string): Promise<Archive> {
|
||||
if (!(await fs.pathExists(zipPath)))
|
||||
if (!(await pathExists(zipPath)))
|
||||
throw vscode.FileSystemError.FileNotFound(zipPath);
|
||||
const archive: Archive = {
|
||||
unzipped: await unzipper.Open.file(zipPath),
|
||||
@@ -209,7 +209,9 @@ export class ArchiveFileSystemProvider implements vscode.FileSystemProvider {
|
||||
return await this._lookup(uri);
|
||||
}
|
||||
|
||||
async readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
|
||||
async readDirectory(
|
||||
uri: vscode.Uri,
|
||||
): Promise<Array<[string, vscode.FileType]>> {
|
||||
const ref = decodeSourceArchiveUri(uri);
|
||||
const archive = await this.getArchive(ref.sourceArchiveZipPath);
|
||||
const contents = archive.dirMap.get(ref.pathWithinSourceArchive);
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
Range,
|
||||
Uri,
|
||||
} from "vscode";
|
||||
import * as path from "path";
|
||||
import { basename } from "path";
|
||||
|
||||
import { DatabaseItem } from "./databases";
|
||||
import { UrlValue, BqrsId } from "./pure/bqrs-cli-types";
|
||||
@@ -138,7 +138,7 @@ export class AstViewer extends DisposableObject {
|
||||
this.treeDataProvider.roots = roots;
|
||||
this.treeDataProvider.db = db;
|
||||
this.treeDataProvider.refresh();
|
||||
this.treeView.message = `AST for ${path.basename(fileUri.fsPath)}`;
|
||||
this.treeView.message = `AST for ${basename(fileUri.fsPath)}`;
|
||||
this.currentFileUri = fileUri;
|
||||
// Handle error on reveal. This could happen if
|
||||
// the tree view is disposed during the reveal.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as semver from "semver";
|
||||
import { runCodeQlCliCommand } from "./cli";
|
||||
import { Logger } from "./logging";
|
||||
import { Logger } from "./common";
|
||||
import { getErrorMessage } from "./pure/helpers-pure";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as cpp from "child-process-promise";
|
||||
import { spawn } from "child-process-promise";
|
||||
import * as child_process from "child_process";
|
||||
import * as fs from "fs-extra";
|
||||
import * as path from "path";
|
||||
import { readFile } from "fs-extra";
|
||||
import { dirname, join, delimiter } from "path";
|
||||
import * as sarif from "sarif";
|
||||
import { SemVer } from "semver";
|
||||
import { Readable } from "stream";
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
getErrorStack,
|
||||
} from "./pure/helpers-pure";
|
||||
import { QueryMetadata, SortDirection } from "./pure/interface-types";
|
||||
import { Logger, ProgressReporter } from "./logging";
|
||||
import { Logger, ProgressReporter } from "./common";
|
||||
import { CompilationMessage } from "./pure/legacy-messages";
|
||||
import { sarifParser } from "./sarif-parser";
|
||||
import { dbSchemeToLanguage, walkDirectory } from "./helpers";
|
||||
@@ -166,7 +166,7 @@ export class CodeQLCliServer implements Disposable {
|
||||
/** The process for the cli server, or undefined if one doesn't exist yet */
|
||||
process?: child_process.ChildProcessWithoutNullStreams;
|
||||
/** Queue of future commands*/
|
||||
commandQueue: (() => void)[];
|
||||
commandQueue: Array<() => void>;
|
||||
/** Whether a command is running */
|
||||
commandInProcess: boolean;
|
||||
/** A buffer with a single null byte. */
|
||||
@@ -414,7 +414,7 @@ export class CodeQLCliServer implements Disposable {
|
||||
|
||||
// Spawn the CodeQL process
|
||||
const codeqlPath = await this.getCodeQlPath();
|
||||
const childPromise = cpp.spawn(codeqlPath, args);
|
||||
const childPromise = spawn(codeqlPath, args);
|
||||
const child = childPromise.childProcess;
|
||||
|
||||
let cancellationRegistration: Disposable | undefined = undefined;
|
||||
@@ -690,10 +690,7 @@ export class CodeQLCliServer implements Disposable {
|
||||
): Promise<MlModelsInfo> {
|
||||
const args = (await this.cliConstraints.supportsPreciseResolveMlModels())
|
||||
? // use the dirname of the path so that we can handle query libraries
|
||||
[
|
||||
...this.getAdditionalPacksArg(additionalPacks),
|
||||
path.dirname(queryPath),
|
||||
]
|
||||
[...this.getAdditionalPacksArg(additionalPacks), dirname(queryPath)]
|
||||
: this.getAdditionalPacksArg(additionalPacks);
|
||||
return await this.runJsonCodeQlCliCommand<MlModelsInfo>(
|
||||
["resolve", "ml-models"],
|
||||
@@ -915,10 +912,10 @@ export class CodeQLCliServer implements Disposable {
|
||||
|
||||
// Warning: this function is untenable for large dot files,
|
||||
async readDotFiles(dir: string): Promise<string[]> {
|
||||
const dotFiles: Promise<string>[] = [];
|
||||
const dotFiles: Array<Promise<string>> = [];
|
||||
for await (const file of walkDirectory(dir)) {
|
||||
if (file.endsWith(".dot")) {
|
||||
dotFiles.push(fs.readFile(file, "utf8"));
|
||||
dotFiles.push(readFile(file, "utf8"));
|
||||
}
|
||||
}
|
||||
return Promise.all(dotFiles);
|
||||
@@ -933,9 +930,7 @@ export class CodeQLCliServer implements Disposable {
|
||||
const additionalArgs = sourceInfo
|
||||
? [
|
||||
"--dot-location-url-format",
|
||||
"file://" +
|
||||
sourceInfo.sourceLocationPrefix +
|
||||
"{path}:{start:line}:{start:column}:{end:line}:{end:column}",
|
||||
`file://${sourceInfo.sourceLocationPrefix}{path}:{start:line}:{start:column}:{end:line}:{end:column}`,
|
||||
]
|
||||
: [];
|
||||
|
||||
@@ -1066,7 +1061,7 @@ export class CodeQLCliServer implements Disposable {
|
||||
): Promise<QlpacksInfo> {
|
||||
const args = this.getAdditionalPacksArg(additionalPacks);
|
||||
if (searchPath?.length) {
|
||||
args.push("--search-path", path.join(...searchPath));
|
||||
args.push("--search-path", join(...searchPath));
|
||||
}
|
||||
|
||||
return this.runJsonCodeQlCliCommand<QlpacksInfo>(
|
||||
@@ -1122,7 +1117,7 @@ export class CodeQLCliServer implements Disposable {
|
||||
): Promise<string[]> {
|
||||
const args = this.getAdditionalPacksArg(additionalPacks);
|
||||
if (searchPath !== undefined) {
|
||||
args.push("--search-path", path.join(...searchPath));
|
||||
args.push("--search-path", join(...searchPath));
|
||||
}
|
||||
if (await this.cliConstraints.supportsAllowLibraryPacksInResolveQueries()) {
|
||||
// All of our usage of `codeql resolve queries` needs to handle library packs.
|
||||
@@ -1241,7 +1236,7 @@ export class CodeQLCliServer implements Disposable {
|
||||
const distribution = await this.distributionProvider.getDistribution();
|
||||
switch (distribution.kind) {
|
||||
case FindDistributionResultKind.CompatibleDistribution:
|
||||
// eslint-disable-next-line no-fallthrough
|
||||
|
||||
case FindDistributionResultKind.IncompatibleDistribution:
|
||||
return distribution.version;
|
||||
|
||||
@@ -1253,9 +1248,7 @@ export class CodeQLCliServer implements Disposable {
|
||||
}
|
||||
|
||||
private getAdditionalPacksArg(paths: string[]): string[] {
|
||||
return paths.length
|
||||
? ["--additional-packs", paths.join(path.delimiter)]
|
||||
: [];
|
||||
return paths.length ? ["--additional-packs", paths.join(delimiter)] : [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
ProgressLocation,
|
||||
} from "vscode";
|
||||
import { showAndLogErrorMessage, showAndLogWarningMessage } from "./helpers";
|
||||
import { logger } from "./logging";
|
||||
import { extLogger } from "./common";
|
||||
import { getErrorMessage, getErrorStack } from "./pure/helpers-pure";
|
||||
import { telemetryListener } from "./telemetry";
|
||||
|
||||
@@ -131,7 +131,7 @@ export function commandRunner(
|
||||
if (e instanceof UserCancellationException) {
|
||||
// User has cancelled this action manually
|
||||
if (e.silent) {
|
||||
void logger.log(errorMessage);
|
||||
void extLogger.log(errorMessage);
|
||||
} else {
|
||||
void showAndLogWarningMessage(errorMessage);
|
||||
}
|
||||
@@ -166,7 +166,7 @@ export function commandRunnerWithProgress<R>(
|
||||
commandId: string,
|
||||
task: ProgressTask<R>,
|
||||
progressOptions: Partial<ProgressOptions>,
|
||||
outputLogger = logger,
|
||||
outputLogger = extLogger,
|
||||
): Disposable {
|
||||
return commands.registerCommand(commandId, async (...args: any[]) => {
|
||||
const startTime = Date.now();
|
||||
|
||||
@@ -3,6 +3,7 @@ import { AppEventEmitter } from "./events";
|
||||
|
||||
export interface App {
|
||||
createEventEmitter<T>(): AppEventEmitter<T>;
|
||||
executeCommand(command: string, ...args: any): Thenable<void>;
|
||||
mode: AppMode;
|
||||
subscriptions: Disposable[];
|
||||
extensionPath: string;
|
||||
|
||||
1
extensions/ql-vscode/src/common/index.ts
Normal file
1
extensions/ql-vscode/src/common/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./logging";
|
||||
3
extensions/ql-vscode/src/common/logging/index.ts
Normal file
3
extensions/ql-vscode/src/common/logging/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./logger";
|
||||
export * from "./vscode/loggers";
|
||||
export * from "./vscode/output-channel-logger";
|
||||
35
extensions/ql-vscode/src/common/logging/logger.ts
Normal file
35
extensions/ql-vscode/src/common/logging/logger.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
export 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, optionally followed by a newline.
|
||||
* 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.
|
||||
*
|
||||
* @param message The message to log.
|
||||
* @param options Optional settings.
|
||||
*/
|
||||
log(message: string, options?: LogOptions): Promise<void>;
|
||||
|
||||
/**
|
||||
* Reveal the logger 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;
|
||||
}
|
||||
19
extensions/ql-vscode/src/common/logging/vscode/loggers.ts
Normal file
19
extensions/ql-vscode/src/common/logging/vscode/loggers.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* This module contains instantiated loggers to use in the extension.
|
||||
*/
|
||||
|
||||
import { OutputChannelLogger } from "./output-channel-logger";
|
||||
|
||||
// Global logger for the extension.
|
||||
export const extLogger = new OutputChannelLogger("CodeQL Extension Log");
|
||||
|
||||
// Logger for messages from the query server.
|
||||
export const queryServerLogger = new OutputChannelLogger("CodeQL Query Server");
|
||||
|
||||
// Logger for messages from the language server.
|
||||
export const ideServerLogger = new OutputChannelLogger(
|
||||
"CodeQL Language Server",
|
||||
);
|
||||
|
||||
// Logger for messages from tests.
|
||||
export const testLogger = new OutputChannelLogger("CodeQL Tests");
|
||||
@@ -1,36 +1,12 @@
|
||||
import { window as Window, OutputChannel, Progress } from "vscode";
|
||||
import { DisposableObject } from "./pure/disposable-object";
|
||||
import * as fs from "fs-extra";
|
||||
import * as path from "path";
|
||||
import { ensureFile, appendFile } from "fs-extra";
|
||||
import { isAbsolute } from "path";
|
||||
import { Logger, LogOptions } from "../logger";
|
||||
import { DisposableObject } from "../../../pure/disposable-object";
|
||||
|
||||
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, 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;
|
||||
}
|
||||
|
||||
export type ProgressReporter = Progress<{ message: string }>;
|
||||
|
||||
/** A logger that writes messages to an output channel in the Output tab. */
|
||||
/**
|
||||
* A logger that writes messages to an output channel in the VS Code Output tab.
|
||||
*/
|
||||
export class OutputChannelLogger extends DisposableObject implements Logger {
|
||||
public readonly outputChannel: OutputChannel;
|
||||
private readonly additionalLocations = new Map<
|
||||
@@ -46,12 +22,6 @@ export class OutputChannelLogger extends DisposableObject implements Logger {
|
||||
this.isCustomLogDirectory = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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> {
|
||||
try {
|
||||
if (options.trailingNewline === undefined) {
|
||||
@@ -64,7 +34,7 @@ export class OutputChannelLogger extends DisposableObject implements Logger {
|
||||
}
|
||||
|
||||
if (options.additionalLogLocation) {
|
||||
if (!path.isAbsolute(options.additionalLogLocation)) {
|
||||
if (!isAbsolute(options.additionalLogLocation)) {
|
||||
throw new Error(
|
||||
`Additional Log Location must be an absolute path: ${options.additionalLogLocation}`,
|
||||
);
|
||||
@@ -108,17 +78,15 @@ export class OutputChannelLogger extends DisposableObject implements Logger {
|
||||
}
|
||||
|
||||
class AdditionalLogLocation {
|
||||
constructor(private location: string) {
|
||||
/**/
|
||||
}
|
||||
constructor(private location: string) {}
|
||||
|
||||
async log(message: string, options = {} as LogOptions): Promise<void> {
|
||||
if (options.trailingNewline === undefined) {
|
||||
options.trailingNewline = true;
|
||||
}
|
||||
await fs.ensureFile(this.location);
|
||||
await ensureFile(this.location);
|
||||
|
||||
await fs.appendFile(
|
||||
await appendFile(
|
||||
this.location,
|
||||
message + (options.trailingNewline ? "\n" : ""),
|
||||
{
|
||||
@@ -128,16 +96,4 @@ class AdditionalLogLocation {
|
||||
}
|
||||
}
|
||||
|
||||
/** The global logger for the extension. */
|
||||
export const logger = new OutputChannelLogger("CodeQL Extension Log");
|
||||
|
||||
/** The logger for messages from the query server. */
|
||||
export const queryServerLogger = new OutputChannelLogger("CodeQL Query Server");
|
||||
|
||||
/** The logger for messages from the language server. */
|
||||
export const ideServerLogger = new OutputChannelLogger(
|
||||
"CodeQL Language Server",
|
||||
);
|
||||
|
||||
/** The logger for messages from tests. */
|
||||
export const testLogger = new OutputChannelLogger("CodeQL Tests");
|
||||
export type ProgressReporter = Progress<{ message: string }>;
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as vscode from "vscode";
|
||||
import { EventEmitter } from "vscode";
|
||||
import { AppEventEmitter } from "../events";
|
||||
|
||||
export class VSCodeAppEventEmitter<T>
|
||||
extends vscode.EventEmitter<T>
|
||||
extends EventEmitter<T>
|
||||
implements AppEventEmitter<T> {}
|
||||
|
||||
@@ -39,4 +39,8 @@ export class ExtensionApp implements App {
|
||||
public createEventEmitter<T>(): AppEventEmitter<T> {
|
||||
return new VSCodeAppEventEmitter<T>();
|
||||
}
|
||||
|
||||
public executeCommand(command: string, ...args: any): Thenable<void> {
|
||||
return vscode.commands.executeCommand(command, args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
ToCompareViewMessage,
|
||||
QueryCompareResult,
|
||||
} from "../pure/interface-types";
|
||||
import { Logger } from "../logging";
|
||||
import { Logger } from "../common";
|
||||
import { CodeQLCliServer } from "../cli";
|
||||
import { DatabaseManager } from "../databases";
|
||||
import { jumpToLocation } from "../interface-utils";
|
||||
@@ -88,7 +88,7 @@ export class CompareView extends AbstractWebview<
|
||||
},
|
||||
columns: fromResultSet.schema.columns,
|
||||
commonResultSetNames,
|
||||
currentResultSetName: currentResultSetName,
|
||||
currentResultSetName,
|
||||
rows,
|
||||
message,
|
||||
databaseUri: to.initialInfo.databaseInfo.databaseUri,
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
ConfigurationTarget,
|
||||
} from "vscode";
|
||||
import { DistributionManager } from "./distribution";
|
||||
import { logger } from "./logging";
|
||||
import { extLogger } from "./common";
|
||||
import { ONE_DAY_IN_MS } from "./pure/time";
|
||||
|
||||
export const ALL_SETTINGS: Setting[] = [];
|
||||
@@ -349,7 +349,7 @@ export class QueryServerConfigListener
|
||||
return undefined;
|
||||
}
|
||||
if (memory == 0 || typeof memory !== "number") {
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
`Ignoring value '${memory}' for setting ${MEMORY_SETTING.qualifiedName}`,
|
||||
);
|
||||
return undefined;
|
||||
@@ -554,14 +554,8 @@ export function isIntegrationTestMode() {
|
||||
return process.env.INTEGRATION_TEST_MODE === "true";
|
||||
}
|
||||
|
||||
/**
|
||||
* A flag indicating whether to enable the experimental "live results" feature
|
||||
* for multi-repo variant analyses.
|
||||
*/
|
||||
const LIVE_RESULTS = new Setting("liveResults", REMOTE_QUERIES_SETTING);
|
||||
|
||||
export function isVariantAnalysisLiveResultsEnabled(): boolean {
|
||||
return !!LIVE_RESULTS.getValue<boolean>();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import * as fs from "fs-extra";
|
||||
import * as yaml from "js-yaml";
|
||||
import * as tmp from "tmp-promise";
|
||||
import * as path from "path";
|
||||
import { writeFile, promises } from "fs-extra";
|
||||
import { dump } from "js-yaml";
|
||||
import { file } from "tmp-promise";
|
||||
import { basename, dirname, resolve } from "path";
|
||||
|
||||
import * as helpers from "../helpers";
|
||||
import {
|
||||
getPrimaryDbscheme,
|
||||
getQlPackForDbscheme,
|
||||
getOnDiskWorkspaceFolders,
|
||||
showAndLogErrorMessage,
|
||||
QlPacksForLanguage,
|
||||
} from "../helpers";
|
||||
import { KeyType, kindOfKeyType, nameOfKeyType, tagOfKeyType } from "./keyType";
|
||||
import { CodeQLCliServer } from "../cli";
|
||||
import { DatabaseItem } from "../databases";
|
||||
import { QlPacksForLanguage } from "../helpers";
|
||||
import { logger } from "../logging";
|
||||
import { extLogger } from "../common";
|
||||
import { createInitialQueryInfo } from "../run-queries-shared";
|
||||
import { CancellationToken, Uri } from "vscode";
|
||||
import { ProgressCallback } from "../commandRunner";
|
||||
@@ -22,8 +27,8 @@ export async function qlpackOfDatabase(
|
||||
throw new Error("Database is invalid and cannot infer QLPack.");
|
||||
}
|
||||
const datasetPath = db.contents.datasetUri.fsPath;
|
||||
const dbscheme = await helpers.getPrimaryDbscheme(datasetPath);
|
||||
return await helpers.getQlPackForDbscheme(cli, dbscheme);
|
||||
const dbscheme = await getPrimaryDbscheme(datasetPath);
|
||||
return await getQlPackForDbscheme(cli, dbscheme);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,7 +45,7 @@ async function resolveQueriesFromPacks(
|
||||
keyType: KeyType,
|
||||
): Promise<string[]> {
|
||||
const suiteFile = (
|
||||
await tmp.file({
|
||||
await file({
|
||||
postfix: ".qls",
|
||||
})
|
||||
).path;
|
||||
@@ -55,11 +60,11 @@ async function resolveQueriesFromPacks(
|
||||
},
|
||||
});
|
||||
}
|
||||
await fs.writeFile(suiteFile, yaml.dump(suiteYaml), "utf8");
|
||||
await writeFile(suiteFile, dump(suiteYaml), "utf8");
|
||||
|
||||
const queries = await cli.resolveQueriesInSuite(
|
||||
suiteFile,
|
||||
helpers.getOnDiskWorkspaceFolders(),
|
||||
getOnDiskWorkspaceFolders(),
|
||||
);
|
||||
return queries;
|
||||
}
|
||||
@@ -124,7 +129,7 @@ export async function resolveQueries(
|
||||
)} queries are not yet available \
|
||||
for this language.`;
|
||||
|
||||
void helpers.showAndLogErrorMessage(errorMessage);
|
||||
void showAndLogErrorMessage(errorMessage);
|
||||
throw new Error(
|
||||
`Couldn't find any queries tagged ${tagOfKeyType(
|
||||
keyType,
|
||||
@@ -144,7 +149,7 @@ async function resolveContextualQuery(
|
||||
// Work out the enclosing pack.
|
||||
const packContents = await cli.packPacklist(query, false);
|
||||
const packFilePath = packContents.find((p) =>
|
||||
["codeql-pack.yml", "qlpack.yml"].includes(path.basename(p)),
|
||||
["codeql-pack.yml", "qlpack.yml"].includes(basename(p)),
|
||||
);
|
||||
if (packFilePath === undefined) {
|
||||
// Should not happen; we already resolved this query.
|
||||
@@ -152,26 +157,26 @@ async function resolveContextualQuery(
|
||||
`Could not find a CodeQL pack file for the pack enclosing the contextual query ${query}`,
|
||||
);
|
||||
}
|
||||
const packPath = path.dirname(packFilePath);
|
||||
const packPath = dirname(packFilePath);
|
||||
const lockFilePath = packContents.find((p) =>
|
||||
["codeql-pack.lock.yml", "qlpack.lock.yml"].includes(path.basename(p)),
|
||||
["codeql-pack.lock.yml", "qlpack.lock.yml"].includes(basename(p)),
|
||||
);
|
||||
let createdTempLockFile = false;
|
||||
if (!lockFilePath) {
|
||||
// No lock file, likely because this library pack is in the package cache.
|
||||
// Create a lock file so that we can resolve dependencies and library path
|
||||
// for the contextual query.
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
`Library pack ${packPath} is missing a lock file; creating a temporary lock file`,
|
||||
);
|
||||
await cli.packResolveDependencies(packPath);
|
||||
createdTempLockFile = true;
|
||||
// Clear CLI server pack cache before installing dependencies,
|
||||
// so that it picks up the new lock file, not the previously cached pack.
|
||||
void logger.log("Clearing the CodeQL CLI server's pack cache");
|
||||
void extLogger.log("Clearing the CodeQL CLI server's pack cache");
|
||||
await cli.clearCache();
|
||||
// Install dependencies.
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
`Installing package dependencies for library pack ${packPath}`,
|
||||
);
|
||||
await cli.packInstall(packPath);
|
||||
@@ -180,12 +185,12 @@ async function resolveContextualQuery(
|
||||
}
|
||||
|
||||
async function removeTemporaryLockFile(packPath: string) {
|
||||
const tempLockFilePath = path.resolve(packPath, "codeql-pack.lock.yml");
|
||||
void logger.log(
|
||||
const tempLockFilePath = resolve(packPath, "codeql-pack.lock.yml");
|
||||
void extLogger.log(
|
||||
`Deleting temporary package lock file at ${tempLockFilePath}`,
|
||||
);
|
||||
// It's fine if the file doesn't exist.
|
||||
await fs.promises.rm(path.resolve(packPath, "codeql-pack.lock.yml"), {
|
||||
await promises.rm(resolve(packPath, "codeql-pack.lock.yml"), {
|
||||
force: true,
|
||||
});
|
||||
}
|
||||
@@ -212,7 +217,7 @@ export async function runContextualQuery(
|
||||
},
|
||||
false,
|
||||
);
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
`Running contextual query ${query}; results will be stored in ${queryStorageDir}`,
|
||||
);
|
||||
const queryResult = await qs.compileAndRunQueryAgainstDatabase(
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
import fetch, { Response } from "node-fetch";
|
||||
import { zip } from "zip-a-folder";
|
||||
import * as unzipper from "unzipper";
|
||||
import { Open } from "unzipper";
|
||||
import { Uri, CancellationToken, commands, window } from "vscode";
|
||||
import { CodeQLCliServer } from "./cli";
|
||||
import * as fs from "fs-extra";
|
||||
import * as path from "path";
|
||||
import {
|
||||
ensureDir,
|
||||
realpath as fs_realpath,
|
||||
pathExists,
|
||||
createWriteStream,
|
||||
remove,
|
||||
stat,
|
||||
readdir,
|
||||
} from "fs-extra";
|
||||
import { basename, join } from "path";
|
||||
import * as Octokit from "@octokit/rest";
|
||||
import { retry } from "@octokit/plugin-retry";
|
||||
|
||||
import { DatabaseManager, DatabaseItem } from "./databases";
|
||||
import { showAndLogInformationMessage } from "./helpers";
|
||||
import { showAndLogInformationMessage, tmpDir } from "./helpers";
|
||||
import { reportStreamProgress, ProgressCallback } from "./commandRunner";
|
||||
import { logger } from "./logging";
|
||||
import { tmpDir } from "./helpers";
|
||||
import { extLogger } from "./common";
|
||||
import { Credentials } from "./authentication";
|
||||
import { REPO_REGEX, getErrorMessage } from "./pure/helpers-pure";
|
||||
|
||||
@@ -285,7 +292,7 @@ async function databaseArchiveFetcher(
|
||||
if (!storagePath) {
|
||||
throw new Error("No storage path specified.");
|
||||
}
|
||||
await fs.ensureDir(storagePath);
|
||||
await ensureDir(storagePath);
|
||||
const unzipPath = await getStorageFolder(storagePath, databaseUrl);
|
||||
|
||||
if (isFile(databaseUrl)) {
|
||||
@@ -334,19 +341,19 @@ async function getStorageFolder(storagePath: string, urlStr: string) {
|
||||
const url = Uri.parse(urlStr);
|
||||
// MacOS has a max filename length of 255
|
||||
// and remove a few extra chars in case we need to add a counter at the end.
|
||||
let lastName = path.basename(url.path).substring(0, 250);
|
||||
let lastName = basename(url.path).substring(0, 250);
|
||||
if (lastName.endsWith(".zip")) {
|
||||
lastName = lastName.substring(0, lastName.length - 4);
|
||||
}
|
||||
|
||||
const realpath = await fs.realpath(storagePath);
|
||||
let folderName = path.join(realpath, lastName);
|
||||
const realpath = await fs_realpath(storagePath);
|
||||
let folderName = join(realpath, lastName);
|
||||
|
||||
// avoid overwriting existing folders
|
||||
let counter = 0;
|
||||
while (await fs.pathExists(folderName)) {
|
||||
while (await pathExists(folderName)) {
|
||||
counter++;
|
||||
folderName = path.join(realpath, `${lastName}-${counter}`);
|
||||
folderName = join(realpath, `${lastName}-${counter}`);
|
||||
if (counter > 100) {
|
||||
throw new Error("Could not find a unique name for downloaded database.");
|
||||
}
|
||||
@@ -379,7 +386,7 @@ async function readAndUnzip(
|
||||
progress?.({
|
||||
maxStep: 10,
|
||||
step: 9,
|
||||
message: `Unzipping into ${path.basename(unzipPath)}`,
|
||||
message: `Unzipping into ${basename(unzipPath)}`,
|
||||
});
|
||||
if (cli && (await cli.cliConstraints.supportsDatabaseUnbundle())) {
|
||||
// Use the `database unbundle` command if the installed cli version supports it
|
||||
@@ -388,7 +395,7 @@ async function readAndUnzip(
|
||||
// Must get the zip central directory since streaming the
|
||||
// zip contents may not have correct local file headers.
|
||||
// Instead, we can only rely on the central directory.
|
||||
const directory = await unzipper.Open.file(zipFile);
|
||||
const directory = await Open.file(zipFile);
|
||||
await directory.extract({ path: unzipPath });
|
||||
}
|
||||
}
|
||||
@@ -406,7 +413,7 @@ async function fetchAndUnzip(
|
||||
// file headers may be incorrect. Additionally, saving to file first will reduce memory
|
||||
// pressure compared with unzipping while downloading the archive.
|
||||
|
||||
const archivePath = path.join(tmpDir.name, `archive-${Date.now()}.zip`);
|
||||
const archivePath = join(tmpDir.name, `archive-${Date.now()}.zip`);
|
||||
|
||||
progress?.({
|
||||
maxStep: 3,
|
||||
@@ -418,7 +425,7 @@ async function fetchAndUnzip(
|
||||
await fetch(databaseUrl, { headers: requestHeaders }),
|
||||
"Error downloading database",
|
||||
);
|
||||
const archiveFileStream = fs.createWriteStream(archivePath);
|
||||
const archiveFileStream = createWriteStream(archivePath);
|
||||
|
||||
const contentLength = response.headers.get("content-length");
|
||||
const totalNumBytes = contentLength ? parseInt(contentLength, 10) : undefined;
|
||||
@@ -444,7 +451,7 @@ async function fetchAndUnzip(
|
||||
);
|
||||
|
||||
// remove archivePath eagerly since these archives can be large.
|
||||
await fs.remove(archivePath);
|
||||
await remove(archivePath);
|
||||
}
|
||||
|
||||
async function checkForFailingResponse(
|
||||
@@ -485,15 +492,15 @@ export async function findDirWithFile(
|
||||
dir: string,
|
||||
...toFind: string[]
|
||||
): Promise<string | undefined> {
|
||||
if (!(await fs.stat(dir)).isDirectory()) {
|
||||
if (!(await stat(dir)).isDirectory()) {
|
||||
return;
|
||||
}
|
||||
const files = await fs.readdir(dir);
|
||||
const files = await readdir(dir);
|
||||
if (toFind.some((file) => files.includes(file))) {
|
||||
return dir;
|
||||
}
|
||||
for (const file of files) {
|
||||
const newPath = path.join(dir, file);
|
||||
const newPath = join(dir, file);
|
||||
const result = await findDirWithFile(newPath, ...toFind);
|
||||
if (result) {
|
||||
return result;
|
||||
@@ -585,7 +592,7 @@ export async function convertGithubNwoToDatabaseUrl(
|
||||
name: repo,
|
||||
};
|
||||
} catch (e) {
|
||||
void logger.log(`Error: ${getErrorMessage(e)}`);
|
||||
void extLogger.log(`Error: ${getErrorMessage(e)}`);
|
||||
throw new Error(`Unable to get database for '${githubRepo}'`);
|
||||
}
|
||||
}
|
||||
@@ -696,7 +703,7 @@ export async function convertLgtmUrlToDatabaseUrl(
|
||||
language,
|
||||
].join("/")}`;
|
||||
} catch (e) {
|
||||
void logger.log(`Error: ${getErrorMessage(e)}`);
|
||||
void extLogger.log(`Error: ${getErrorMessage(e)}`);
|
||||
throw new Error(`Invalid LGTM URL: ${lgtmUrl}`);
|
||||
}
|
||||
}
|
||||
@@ -745,14 +752,11 @@ async function promptForLanguage(
|
||||
* @param databasePath The full path to the unzipped database
|
||||
*/
|
||||
async function ensureZippedSourceLocation(databasePath: string): Promise<void> {
|
||||
const srcFolderPath = path.join(databasePath, "src");
|
||||
const srcZipPath = srcFolderPath + ".zip";
|
||||
const srcFolderPath = join(databasePath, "src");
|
||||
const srcZipPath = `${srcFolderPath}.zip`;
|
||||
|
||||
if (
|
||||
(await fs.pathExists(srcFolderPath)) &&
|
||||
!(await fs.pathExists(srcZipPath))
|
||||
) {
|
||||
if ((await pathExists(srcFolderPath)) && !(await pathExists(srcZipPath))) {
|
||||
await zip(srcFolderPath, srcZipPath);
|
||||
await fs.remove(srcFolderPath);
|
||||
await remove(srcFolderPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as path from "path";
|
||||
import { join, basename, dirname as path_dirname } from "path";
|
||||
import { DisposableObject } from "./pure/disposable-object";
|
||||
import {
|
||||
Event,
|
||||
@@ -9,8 +9,9 @@ import {
|
||||
Uri,
|
||||
window,
|
||||
env,
|
||||
CancellationToken,
|
||||
} from "vscode";
|
||||
import * as fs from "fs-extra";
|
||||
import { pathExists, stat, readdir, remove } from "fs-extra";
|
||||
|
||||
import {
|
||||
DatabaseChangedEvent,
|
||||
@@ -27,14 +28,13 @@ import {
|
||||
isLikelyDbLanguageFolder,
|
||||
showAndLogErrorMessage,
|
||||
} from "./helpers";
|
||||
import { logger } from "./logging";
|
||||
import { extLogger } from "./common";
|
||||
import {
|
||||
importArchiveDatabase,
|
||||
promptImportGithubDatabase,
|
||||
promptImportInternetDatabase,
|
||||
promptImportLgtmDatabase,
|
||||
} from "./databaseFetcher";
|
||||
import { CancellationToken } from "vscode";
|
||||
import { asyncFilter, getErrorMessage } from "./pure/helpers-pure";
|
||||
import { Credentials } from "./authentication";
|
||||
import { QueryRunner } from "./queryRunner";
|
||||
@@ -61,10 +61,10 @@ function joinThemableIconPath(
|
||||
): ThemableIconPath {
|
||||
if (typeof iconPath == "object")
|
||||
return {
|
||||
light: path.join(base, iconPath.light),
|
||||
dark: path.join(base, iconPath.dark),
|
||||
light: join(base, iconPath.light),
|
||||
dark: join(base, iconPath.dark),
|
||||
};
|
||||
else return path.join(base, iconPath);
|
||||
else return join(base, iconPath);
|
||||
}
|
||||
|
||||
enum SortOrder {
|
||||
@@ -241,7 +241,7 @@ export class DatabaseUI extends DisposableObject {
|
||||
}
|
||||
|
||||
init() {
|
||||
void logger.log("Registering database panel commands.");
|
||||
void extLogger.log("Registering database panel commands.");
|
||||
this.push(
|
||||
commandRunnerWithProgress(
|
||||
"codeQL.setCurrentDatabase",
|
||||
@@ -393,14 +393,14 @@ export class DatabaseUI extends DisposableObject {
|
||||
};
|
||||
|
||||
handleRemoveOrphanedDatabases = async (): Promise<void> => {
|
||||
void logger.log("Removing orphaned databases from workspace storage.");
|
||||
void extLogger.log("Removing orphaned databases from workspace storage.");
|
||||
let dbDirs = undefined;
|
||||
|
||||
if (
|
||||
!(await fs.pathExists(this.storagePath)) ||
|
||||
!(await fs.stat(this.storagePath)).isDirectory()
|
||||
!(await pathExists(this.storagePath)) ||
|
||||
!(await stat(this.storagePath)).isDirectory()
|
||||
) {
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
"Missing or invalid storage directory. Not trying to remove orphaned databases.",
|
||||
);
|
||||
return;
|
||||
@@ -408,11 +408,11 @@ export class DatabaseUI extends DisposableObject {
|
||||
|
||||
dbDirs =
|
||||
// read directory
|
||||
(await fs.readdir(this.storagePath, { withFileTypes: true }))
|
||||
(await readdir(this.storagePath, { withFileTypes: true }))
|
||||
// remove non-directories
|
||||
.filter((dirent) => dirent.isDirectory())
|
||||
// get the full path
|
||||
.map((dirent) => path.join(this.storagePath, dirent.name))
|
||||
.map((dirent) => join(this.storagePath, dirent.name))
|
||||
// remove databases still in workspace
|
||||
.filter((dbDir) => {
|
||||
const dbUri = Uri.file(dbDir);
|
||||
@@ -425,7 +425,7 @@ export class DatabaseUI extends DisposableObject {
|
||||
dbDirs = await asyncFilter(dbDirs, isLikelyDatabaseRoot);
|
||||
|
||||
if (!dbDirs.length) {
|
||||
void logger.log("No orphaned databases found.");
|
||||
void extLogger.log("No orphaned databases found.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -434,16 +434,16 @@ export class DatabaseUI extends DisposableObject {
|
||||
await Promise.all(
|
||||
dbDirs.map(async (dbDir) => {
|
||||
try {
|
||||
void logger.log(`Deleting orphaned database '${dbDir}'.`);
|
||||
await fs.remove(dbDir);
|
||||
void extLogger.log(`Deleting orphaned database '${dbDir}'.`);
|
||||
await remove(dbDir);
|
||||
} catch (e) {
|
||||
failures.push(`${path.basename(dbDir)}`);
|
||||
failures.push(`${basename(dbDir)}`);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
if (failures.length) {
|
||||
const dirname = path.dirname(failures[0]);
|
||||
const dirname = path_dirname(failures[0]);
|
||||
void showAndLogErrorMessage(
|
||||
`Failed to delete unused databases (${failures.join(
|
||||
", ",
|
||||
@@ -620,7 +620,7 @@ export class DatabaseUI extends DisposableObject {
|
||||
} catch (e) {
|
||||
// rethrow and let this be handled by default error handling.
|
||||
throw new Error(
|
||||
`Could not set database to ${path.basename(
|
||||
`Could not set database to ${basename(
|
||||
uri.fsPath,
|
||||
)}. Reason: ${getErrorMessage(e)}`,
|
||||
);
|
||||
@@ -774,12 +774,12 @@ export class DatabaseUI extends DisposableObject {
|
||||
*/
|
||||
private async fixDbUri(uri: Uri): Promise<Uri> {
|
||||
let dbPath = uri.fsPath;
|
||||
if ((await fs.stat(dbPath)).isFile()) {
|
||||
dbPath = path.dirname(dbPath);
|
||||
if ((await stat(dbPath)).isFile()) {
|
||||
dbPath = path_dirname(dbPath);
|
||||
}
|
||||
|
||||
if (await isLikelyDbLanguageFolder(dbPath)) {
|
||||
dbPath = path.dirname(dbPath);
|
||||
dbPath = path_dirname(dbPath);
|
||||
}
|
||||
return Uri.file(dbPath);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as fs from "fs-extra";
|
||||
import { pathExists, stat, remove } from "fs-extra";
|
||||
import * as glob from "glob-promise";
|
||||
import * as path from "path";
|
||||
import { join, basename, resolve, relative, dirname, extname } from "path";
|
||||
import * as vscode from "vscode";
|
||||
import * as cli from "./cli";
|
||||
import { ExtensionContext } from "vscode";
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
encodeSourceArchiveUri,
|
||||
} from "./archive-filesystem-provider";
|
||||
import { DisposableObject } from "./pure/disposable-object";
|
||||
import { Logger, logger } from "./logging";
|
||||
import { Logger, extLogger } from "./common";
|
||||
import { getErrorMessage } from "./pure/helpers-pure";
|
||||
import { QueryRunner } from "./queryRunner";
|
||||
|
||||
@@ -115,7 +115,7 @@ async function findDataset(parentDirectory: string): Promise<vscode.Uri> {
|
||||
);
|
||||
}
|
||||
|
||||
const dbAbsolutePath = path.join(parentDirectory, dbRelativePaths[0]);
|
||||
const dbAbsolutePath = join(parentDirectory, dbRelativePaths[0]);
|
||||
if (dbRelativePaths.length > 1) {
|
||||
void showAndLogWarningMessage(
|
||||
`Found multiple dataset directories in database, using '${dbAbsolutePath}'.`,
|
||||
@@ -132,13 +132,13 @@ export async function findSourceArchive(
|
||||
const relativePaths = ["src", "output/src_archive"];
|
||||
|
||||
for (const relativePath of relativePaths) {
|
||||
const basePath = path.join(databasePath, relativePath);
|
||||
const zipPath = basePath + ".zip";
|
||||
const basePath = join(databasePath, relativePath);
|
||||
const zipPath = `${basePath}.zip`;
|
||||
|
||||
// Prefer using a zip archive over a directory.
|
||||
if (await fs.pathExists(zipPath)) {
|
||||
if (await pathExists(zipPath)) {
|
||||
return encodeArchiveBasePath(zipPath);
|
||||
} else if (await fs.pathExists(basePath)) {
|
||||
} else if (await pathExists(basePath)) {
|
||||
return vscode.Uri.file(basePath);
|
||||
}
|
||||
}
|
||||
@@ -152,7 +152,7 @@ export async function findSourceArchive(
|
||||
async function resolveDatabase(
|
||||
databasePath: string,
|
||||
): Promise<DatabaseContents> {
|
||||
const name = path.basename(databasePath);
|
||||
const name = basename(databasePath);
|
||||
|
||||
// Look for dataset and source archive.
|
||||
const datasetUri = await findDataset(databasePath);
|
||||
@@ -180,7 +180,7 @@ async function resolveDatabaseContents(
|
||||
);
|
||||
}
|
||||
const databasePath = uri.fsPath;
|
||||
if (!(await fs.pathExists(databasePath))) {
|
||||
if (!(await pathExists(databasePath))) {
|
||||
throw new InvalidDatabaseError(
|
||||
`Database '${databasePath}' does not exist.`,
|
||||
);
|
||||
@@ -207,9 +207,7 @@ async function resolveDatabaseContents(
|
||||
`Database '${databasePath}' contains multiple CodeQL dbschemes under '${dbPath}'.`,
|
||||
);
|
||||
} else {
|
||||
contents.dbSchemeUri = vscode.Uri.file(
|
||||
path.resolve(dbPath, dbSchemeFiles[0]),
|
||||
);
|
||||
contents.dbSchemeUri = vscode.Uri.file(resolve(dbPath, dbSchemeFiles[0]));
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
@@ -336,7 +334,7 @@ export class DatabaseItemImpl implements DatabaseItem {
|
||||
} else if (this._contents) {
|
||||
return this._contents.name;
|
||||
} else {
|
||||
return path.basename(this.databaseUri.fsPath);
|
||||
return basename(this.databaseUri.fsPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,7 +405,7 @@ export class DatabaseItemImpl implements DatabaseItem {
|
||||
const pathWithinSourceArchive =
|
||||
zipRef.pathWithinSourceArchive === "/"
|
||||
? relativeFilePath
|
||||
: zipRef.pathWithinSourceArchive + "/" + relativeFilePath;
|
||||
: `${zipRef.pathWithinSourceArchive}/${relativeFilePath}`;
|
||||
return encodeSourceArchiveUri({
|
||||
pathWithinSourceArchive,
|
||||
sourceArchiveZipPath: zipRef.sourceArchiveZipPath,
|
||||
@@ -518,14 +516,14 @@ export class DatabaseItemImpl implements DatabaseItem {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
const stats = await fs.stat(testPath);
|
||||
const stats = await stat(testPath);
|
||||
if (stats.isDirectory()) {
|
||||
return !path.relative(testPath, databasePath).startsWith("..");
|
||||
return !relative(testPath, databasePath).startsWith("..");
|
||||
} else {
|
||||
// database for /one/two/three/test.ql is at /one/two/three/three.testproj
|
||||
const testdir = path.dirname(testPath);
|
||||
const testdirbase = path.basename(testdir);
|
||||
return databasePath == path.join(testdir, testdirbase + ".testproj");
|
||||
const testdir = dirname(testPath);
|
||||
const testdirbase = basename(testdir);
|
||||
return databasePath == join(testdir, `${testdirbase}.testproj`);
|
||||
}
|
||||
} catch {
|
||||
// No information available for test path - assume database is unaffected.
|
||||
@@ -545,7 +543,7 @@ function eventFired<T>(
|
||||
): Promise<T | undefined> {
|
||||
return new Promise((res, _rej) => {
|
||||
const timeout = setTimeout(() => {
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
`Waiting for event ${event} timed out after ${timeoutMs}ms`,
|
||||
);
|
||||
res(undefined);
|
||||
@@ -597,7 +595,7 @@ export class DatabaseManager extends DisposableObject {
|
||||
): Promise<DatabaseItem> {
|
||||
const contents = await resolveDatabaseContents(uri);
|
||||
// Ignore the source archive for QLTest databases by default.
|
||||
const isQLTestDatabase = path.extname(uri.fsPath) === ".testproj";
|
||||
const isQLTestDatabase = extname(uri.fsPath) === ".testproj";
|
||||
const fullOptions: FullDatabaseOptions = {
|
||||
ignoreSourceArchive: isQLTestDatabase,
|
||||
// If a displayName is not passed in, the basename of folder containing the database is used.
|
||||
@@ -657,12 +655,12 @@ export class DatabaseManager extends DisposableObject {
|
||||
|
||||
const msg = item.verifyZippedSources();
|
||||
if (msg) {
|
||||
void logger.log(`Could not add source folder because ${msg}`);
|
||||
void extLogger.log(`Could not add source folder because ${msg}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const uri = item.getSourceArchiveExplorerUri();
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
`Adding workspace folder for ${item.name} source archive at index ${end}`,
|
||||
);
|
||||
if ((vscode.workspace.workspaceFolders || []).length < 2) {
|
||||
@@ -916,7 +914,7 @@ export class DatabaseManager extends DisposableObject {
|
||||
(folder) => item.belongsToSourceArchiveExplorerUri(folder.uri),
|
||||
);
|
||||
if (folderIndex >= 0) {
|
||||
void logger.log(`Removing workspace folder at index ${folderIndex}`);
|
||||
void extLogger.log(`Removing workspace folder at index ${folderIndex}`);
|
||||
vscode.workspace.updateWorkspaceFolders(folderIndex, 1);
|
||||
}
|
||||
|
||||
@@ -925,11 +923,11 @@ export class DatabaseManager extends DisposableObject {
|
||||
|
||||
// Delete folder from file system only if it is controlled by the extension
|
||||
if (this.isExtensionControlledLocation(item.databaseUri)) {
|
||||
void logger.log("Deleting database from filesystem.");
|
||||
fs.remove(item.databaseUri.fsPath).then(
|
||||
() => void logger.log(`Deleted '${item.databaseUri.fsPath}'`),
|
||||
void extLogger.log("Deleting database from filesystem.");
|
||||
remove(item.databaseUri.fsPath).then(
|
||||
() => void extLogger.log(`Deleted '${item.databaseUri.fsPath}'`),
|
||||
(e) =>
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
`Failed to delete '${
|
||||
item.databaseUri.fsPath
|
||||
}'. Reason: ${getErrorMessage(e)}`,
|
||||
@@ -1003,7 +1001,7 @@ export class DatabaseManager extends DisposableObject {
|
||||
* scripts returned by the cli's upgrade resolution.
|
||||
*/
|
||||
export function getUpgradesDirectories(scripts: string[]): vscode.Uri[] {
|
||||
const parentDirs = scripts.map((dir) => path.dirname(dir));
|
||||
const parentDirs = scripts.map((dir) => dirname(dir));
|
||||
const uniqueParentDirs = new Set(parentDirs);
|
||||
return Array.from(uniqueParentDirs).map((filePath) =>
|
||||
vscode.Uri.file(filePath),
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import * as fs from "fs-extra";
|
||||
import * as path from "path";
|
||||
import { cloneDbConfig, DbConfig } from "./db-config";
|
||||
import { pathExists, writeJSON, readJSON, readJSONSync } from "fs-extra";
|
||||
import { join } from "path";
|
||||
import {
|
||||
cloneDbConfig,
|
||||
DbConfig,
|
||||
ExpandedDbItem,
|
||||
SelectedDbItem,
|
||||
} from "./db-config";
|
||||
import * as chokidar from "chokidar";
|
||||
import { DisposableObject } from "../../pure/disposable-object";
|
||||
import { DisposableObject, DisposeHandler } from "../../pure/disposable-object";
|
||||
import { DbConfigValidator } from "./db-config-validator";
|
||||
import { ValueResult } from "../../common/value-result";
|
||||
import { App } from "../../common/app";
|
||||
@@ -23,7 +28,7 @@ export class DbConfigStore extends DisposableObject {
|
||||
super();
|
||||
|
||||
const storagePath = app.workspaceStoragePath || app.globalStoragePath;
|
||||
this.configPath = path.join(storagePath, "workspace-databases.json");
|
||||
this.configPath = join(storagePath, "workspace-databases.json");
|
||||
|
||||
this.config = this.createEmptyConfig();
|
||||
this.configErrors = [];
|
||||
@@ -38,7 +43,8 @@ export class DbConfigStore extends DisposableObject {
|
||||
this.watchConfig();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
public dispose(disposeHandler?: DisposeHandler): void {
|
||||
super.dispose(disposeHandler);
|
||||
this.configWatcher?.unwatch(this.configPath);
|
||||
}
|
||||
|
||||
@@ -55,11 +61,59 @@ export class DbConfigStore extends DisposableObject {
|
||||
return this.configPath;
|
||||
}
|
||||
|
||||
public async setSelectedDbItem(dbItem: SelectedDbItem): Promise<void> {
|
||||
if (!this.config) {
|
||||
// If the app is trying to set the selected item without a config
|
||||
// being set it means that there is a bug in our code, so we throw
|
||||
// an error instead of just returning an error result.
|
||||
throw Error("Cannot select database item if config is not loaded");
|
||||
}
|
||||
|
||||
const config: DbConfig = {
|
||||
...this.config,
|
||||
selected: dbItem,
|
||||
};
|
||||
|
||||
await this.writeConfig(config);
|
||||
}
|
||||
|
||||
public async updateExpandedState(expandedItems: ExpandedDbItem[]) {
|
||||
if (!this.config) {
|
||||
throw Error("Cannot update expansion state if config is not loaded");
|
||||
}
|
||||
|
||||
const config: DbConfig = {
|
||||
...this.config,
|
||||
expanded: expandedItems,
|
||||
};
|
||||
|
||||
await this.writeConfig(config);
|
||||
}
|
||||
|
||||
public async addRemoteList(listName: string): Promise<void> {
|
||||
if (!this.config) {
|
||||
throw Error("Cannot add remote list if config is not loaded");
|
||||
}
|
||||
|
||||
const config: DbConfig = cloneDbConfig(this.config);
|
||||
config.databases.remote.repositoryLists.push({
|
||||
name: listName,
|
||||
repositories: [],
|
||||
});
|
||||
|
||||
// TODO: validate that the name doesn't already exist
|
||||
await this.writeConfig(config);
|
||||
}
|
||||
|
||||
private async writeConfig(config: DbConfig): Promise<void> {
|
||||
await writeJSON(this.configPath, config, {
|
||||
spaces: 2,
|
||||
});
|
||||
}
|
||||
|
||||
private async loadConfig(): Promise<void> {
|
||||
if (!(await fs.pathExists(this.configPath))) {
|
||||
await fs.writeJSON(this.configPath, this.createEmptyConfig(), {
|
||||
spaces: 2,
|
||||
});
|
||||
if (!(await pathExists(this.configPath))) {
|
||||
await this.writeConfig(this.createEmptyConfig());
|
||||
}
|
||||
|
||||
await this.readConfig();
|
||||
@@ -68,7 +122,7 @@ export class DbConfigStore extends DisposableObject {
|
||||
private async readConfig(): Promise<void> {
|
||||
let newConfig: DbConfig | undefined = undefined;
|
||||
try {
|
||||
newConfig = await fs.readJSON(this.configPath);
|
||||
newConfig = await readJSON(this.configPath);
|
||||
} catch (e) {
|
||||
this.configErrors = [`Failed to read config file: ${this.configPath}`];
|
||||
}
|
||||
@@ -83,7 +137,7 @@ export class DbConfigStore extends DisposableObject {
|
||||
private readConfigSync(): void {
|
||||
let newConfig: DbConfig | undefined = undefined;
|
||||
try {
|
||||
newConfig = fs.readJSONSync(this.configPath);
|
||||
newConfig = readJSONSync(this.configPath);
|
||||
} catch (e) {
|
||||
this.configErrors = [`Failed to read config file: ${this.configPath}`];
|
||||
}
|
||||
@@ -116,6 +170,7 @@ export class DbConfigStore extends DisposableObject {
|
||||
databases: [],
|
||||
},
|
||||
},
|
||||
expanded: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as fs from "fs-extra";
|
||||
import * as path from "path";
|
||||
import { readJsonSync } from "fs-extra";
|
||||
import { resolve } from "path";
|
||||
import Ajv from "ajv";
|
||||
import { DbConfig } from "./db-config";
|
||||
|
||||
@@ -7,11 +7,11 @@ export class DbConfigValidator {
|
||||
private readonly schema: any;
|
||||
|
||||
constructor(extensionPath: string) {
|
||||
const schemaPath = path.resolve(
|
||||
const schemaPath = resolve(
|
||||
extensionPath,
|
||||
"workspace-databases-schema.json",
|
||||
);
|
||||
this.schema = fs.readJsonSync(schemaPath);
|
||||
this.schema = readJsonSync(schemaPath);
|
||||
}
|
||||
|
||||
public validate(dbConfig: DbConfig): string[] {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
export interface DbConfig {
|
||||
databases: DbConfigDatabases;
|
||||
expanded: ExpandedDbItem[];
|
||||
selected?: SelectedDbItem;
|
||||
}
|
||||
|
||||
@@ -10,14 +11,53 @@ export interface DbConfigDatabases {
|
||||
local: LocalDbConfig;
|
||||
}
|
||||
|
||||
export interface SelectedDbItem {
|
||||
kind: SelectedDbItemKind;
|
||||
value: string;
|
||||
}
|
||||
export type SelectedDbItem =
|
||||
| SelectedLocalUserDefinedList
|
||||
| SelectedLocalDatabase
|
||||
| SelectedRemoteSystemDefinedList
|
||||
| SelectedRemoteUserDefinedList
|
||||
| SelectedRemoteOwner
|
||||
| SelectedRemoteRepository;
|
||||
|
||||
export enum SelectedDbItemKind {
|
||||
ConfigDefined = "configDefined",
|
||||
LocalUserDefinedList = "localUserDefinedList",
|
||||
LocalDatabase = "localDatabase",
|
||||
RemoteSystemDefinedList = "remoteSystemDefinedList",
|
||||
RemoteUserDefinedList = "remoteUserDefinedList",
|
||||
RemoteOwner = "remoteOwner",
|
||||
RemoteRepository = "remoteRepository",
|
||||
}
|
||||
|
||||
export interface SelectedLocalUserDefinedList {
|
||||
kind: SelectedDbItemKind.LocalUserDefinedList;
|
||||
listName: string;
|
||||
}
|
||||
|
||||
export interface SelectedLocalDatabase {
|
||||
kind: SelectedDbItemKind.LocalDatabase;
|
||||
databaseName: string;
|
||||
listName?: string;
|
||||
}
|
||||
|
||||
export interface SelectedRemoteSystemDefinedList {
|
||||
kind: SelectedDbItemKind.RemoteSystemDefinedList;
|
||||
listName: string;
|
||||
}
|
||||
|
||||
export interface SelectedRemoteUserDefinedList {
|
||||
kind: SelectedDbItemKind.RemoteUserDefinedList;
|
||||
listName: string;
|
||||
}
|
||||
|
||||
export interface SelectedRemoteOwner {
|
||||
kind: SelectedDbItemKind.RemoteOwner;
|
||||
ownerName: string;
|
||||
}
|
||||
|
||||
export interface SelectedRemoteRepository {
|
||||
kind: SelectedDbItemKind.RemoteRepository;
|
||||
repositoryName: string;
|
||||
listName?: string;
|
||||
}
|
||||
|
||||
export interface RemoteDbConfig {
|
||||
@@ -48,6 +88,37 @@ export interface LocalDatabase {
|
||||
storagePath: string;
|
||||
}
|
||||
|
||||
export type ExpandedDbItem =
|
||||
| RootLocalExpandedDbItem
|
||||
| LocalUserDefinedListExpandedDbItem
|
||||
| RootRemoteExpandedDbItem
|
||||
| RemoteUserDefinedListExpandedDbItem;
|
||||
|
||||
export enum ExpandedDbItemKind {
|
||||
RootLocal = "rootLocal",
|
||||
LocalUserDefinedList = "localUserDefinedList",
|
||||
RootRemote = "rootRemote",
|
||||
RemoteUserDefinedList = "remoteUserDefinedList",
|
||||
}
|
||||
|
||||
export interface RootLocalExpandedDbItem {
|
||||
kind: ExpandedDbItemKind.RootLocal;
|
||||
}
|
||||
|
||||
export interface LocalUserDefinedListExpandedDbItem {
|
||||
kind: ExpandedDbItemKind.LocalUserDefinedList;
|
||||
listName: string;
|
||||
}
|
||||
|
||||
export interface RootRemoteExpandedDbItem {
|
||||
kind: ExpandedDbItemKind.RootRemote;
|
||||
}
|
||||
|
||||
export interface RemoteUserDefinedListExpandedDbItem {
|
||||
kind: ExpandedDbItemKind.RemoteUserDefinedList;
|
||||
listName: string;
|
||||
}
|
||||
|
||||
export function cloneDbConfig(config: DbConfig): DbConfig {
|
||||
return {
|
||||
databases: {
|
||||
@@ -69,11 +140,60 @@ export function cloneDbConfig(config: DbConfig): DbConfig {
|
||||
databases: config.databases.local.databases.map((db) => ({ ...db })),
|
||||
},
|
||||
},
|
||||
expanded: config.expanded.map(cloneDbConfigExpandedItem),
|
||||
selected: config.selected
|
||||
? {
|
||||
kind: config.selected.kind,
|
||||
value: config.selected.value,
|
||||
}
|
||||
? cloneDbConfigSelectedItem(config.selected)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
function cloneDbConfigSelectedItem(selected: SelectedDbItem): SelectedDbItem {
|
||||
switch (selected.kind) {
|
||||
case SelectedDbItemKind.LocalUserDefinedList:
|
||||
return {
|
||||
kind: SelectedDbItemKind.LocalUserDefinedList,
|
||||
listName: selected.listName,
|
||||
};
|
||||
case SelectedDbItemKind.LocalDatabase:
|
||||
return {
|
||||
kind: SelectedDbItemKind.LocalDatabase,
|
||||
databaseName: selected.databaseName,
|
||||
listName: selected.listName,
|
||||
};
|
||||
case SelectedDbItemKind.RemoteSystemDefinedList:
|
||||
return {
|
||||
kind: SelectedDbItemKind.RemoteSystemDefinedList,
|
||||
listName: selected.listName,
|
||||
};
|
||||
case SelectedDbItemKind.RemoteUserDefinedList:
|
||||
return {
|
||||
kind: SelectedDbItemKind.RemoteUserDefinedList,
|
||||
listName: selected.listName,
|
||||
};
|
||||
case SelectedDbItemKind.RemoteOwner:
|
||||
return {
|
||||
kind: SelectedDbItemKind.RemoteOwner,
|
||||
ownerName: selected.ownerName,
|
||||
};
|
||||
case SelectedDbItemKind.RemoteRepository:
|
||||
return {
|
||||
kind: SelectedDbItemKind.RemoteRepository,
|
||||
repositoryName: selected.repositoryName,
|
||||
listName: selected.listName,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function cloneDbConfigExpandedItem(item: ExpandedDbItem): ExpandedDbItem {
|
||||
switch (item.kind) {
|
||||
case ExpandedDbItemKind.RootLocal:
|
||||
case ExpandedDbItemKind.RootRemote:
|
||||
return { kind: item.kind };
|
||||
case ExpandedDbItemKind.LocalUserDefinedList:
|
||||
case ExpandedDbItemKind.RemoteUserDefinedList:
|
||||
return {
|
||||
kind: item.kind,
|
||||
listName: item.listName,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
66
extensions/ql-vscode/src/databases/db-item-expansion.ts
Normal file
66
extensions/ql-vscode/src/databases/db-item-expansion.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { ExpandedDbItem, ExpandedDbItemKind } from "./config/db-config";
|
||||
import { DbItem, DbItemKind } from "./db-item";
|
||||
|
||||
export function calculateNewExpandedState(
|
||||
currentExpandedItems: ExpandedDbItem[],
|
||||
dbItem: DbItem,
|
||||
itemExpanded: boolean,
|
||||
): ExpandedDbItem[] {
|
||||
if (itemExpanded) {
|
||||
const expandedDbItem = mapDbItemToExpandedDbItem(dbItem);
|
||||
const expandedItems = [...currentExpandedItems];
|
||||
if (!expandedItems.some((i) => isDbItemEqualToExpandedDbItem(dbItem, i))) {
|
||||
expandedItems.push(expandedDbItem);
|
||||
}
|
||||
return expandedItems;
|
||||
} else {
|
||||
return currentExpandedItems.filter(
|
||||
(i) => !isDbItemEqualToExpandedDbItem(dbItem, i),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapDbItemToExpandedDbItem(dbItem: DbItem): ExpandedDbItem {
|
||||
switch (dbItem.kind) {
|
||||
case DbItemKind.RootLocal:
|
||||
return { kind: ExpandedDbItemKind.RootLocal };
|
||||
case DbItemKind.LocalList:
|
||||
return {
|
||||
kind: ExpandedDbItemKind.LocalUserDefinedList,
|
||||
listName: dbItem.listName,
|
||||
};
|
||||
case DbItemKind.RootRemote:
|
||||
return { kind: ExpandedDbItemKind.RootRemote };
|
||||
case DbItemKind.RemoteUserDefinedList:
|
||||
return {
|
||||
kind: ExpandedDbItemKind.RemoteUserDefinedList,
|
||||
listName: dbItem.listName,
|
||||
};
|
||||
default:
|
||||
throw Error(`Unknown db item kind ${dbItem.kind}`);
|
||||
}
|
||||
}
|
||||
|
||||
function isDbItemEqualToExpandedDbItem(
|
||||
dbItem: DbItem,
|
||||
expandedDbItem: ExpandedDbItem,
|
||||
) {
|
||||
switch (dbItem.kind) {
|
||||
case DbItemKind.RootLocal:
|
||||
return expandedDbItem.kind === ExpandedDbItemKind.RootLocal;
|
||||
case DbItemKind.LocalList:
|
||||
return (
|
||||
expandedDbItem.kind === ExpandedDbItemKind.LocalUserDefinedList &&
|
||||
expandedDbItem.listName === dbItem.listName
|
||||
);
|
||||
case DbItemKind.RootRemote:
|
||||
return expandedDbItem.kind === ExpandedDbItemKind.RootRemote;
|
||||
case DbItemKind.RemoteUserDefinedList:
|
||||
return (
|
||||
expandedDbItem.kind === ExpandedDbItemKind.RemoteUserDefinedList &&
|
||||
expandedDbItem.listName === dbItem.listName
|
||||
);
|
||||
default:
|
||||
throw Error(`Unknown db item kind ${dbItem.kind}`);
|
||||
}
|
||||
}
|
||||
94
extensions/ql-vscode/src/databases/db-item-selection.ts
Normal file
94
extensions/ql-vscode/src/databases/db-item-selection.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { DbItem, DbItemKind, LocalDbItem, RemoteDbItem } from "./db-item";
|
||||
import { SelectedDbItem, SelectedDbItemKind } from "./config/db-config";
|
||||
|
||||
export function getSelectedDbItem(dbItems: DbItem[]): DbItem | undefined {
|
||||
for (const dbItem of dbItems) {
|
||||
if (
|
||||
dbItem.kind === DbItemKind.RootRemote ||
|
||||
dbItem.kind === DbItemKind.RootLocal
|
||||
) {
|
||||
for (const child of dbItem.children) {
|
||||
const selectedItem = extractSelected(child);
|
||||
if (selectedItem) return selectedItem;
|
||||
}
|
||||
} else {
|
||||
const selectedItem = extractSelected(dbItem);
|
||||
if (selectedItem) return selectedItem;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function extractSelected(
|
||||
dbItem: RemoteDbItem | LocalDbItem,
|
||||
): DbItem | undefined {
|
||||
if (dbItem.selected) {
|
||||
return dbItem;
|
||||
}
|
||||
switch (dbItem.kind) {
|
||||
case DbItemKind.LocalList:
|
||||
for (const database of dbItem.databases) {
|
||||
if (database.selected) {
|
||||
return database;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DbItemKind.RemoteUserDefinedList:
|
||||
for (const repo of dbItem.repos) {
|
||||
if (repo.selected) {
|
||||
return repo;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function mapDbItemToSelectedDbItem(
|
||||
dbItem: DbItem,
|
||||
): SelectedDbItem | undefined {
|
||||
switch (dbItem.kind) {
|
||||
case DbItemKind.RootLocal:
|
||||
case DbItemKind.RootRemote:
|
||||
// Root items are not selectable.
|
||||
return undefined;
|
||||
|
||||
case DbItemKind.LocalList:
|
||||
return {
|
||||
kind: SelectedDbItemKind.LocalUserDefinedList,
|
||||
listName: dbItem.listName,
|
||||
};
|
||||
|
||||
case DbItemKind.RemoteUserDefinedList:
|
||||
return {
|
||||
kind: SelectedDbItemKind.RemoteUserDefinedList,
|
||||
listName: dbItem.listName,
|
||||
};
|
||||
|
||||
case DbItemKind.RemoteSystemDefinedList:
|
||||
return {
|
||||
kind: SelectedDbItemKind.RemoteSystemDefinedList,
|
||||
listName: dbItem.listName,
|
||||
};
|
||||
|
||||
case DbItemKind.RemoteOwner:
|
||||
return {
|
||||
kind: SelectedDbItemKind.RemoteOwner,
|
||||
ownerName: dbItem.ownerName,
|
||||
};
|
||||
|
||||
case DbItemKind.LocalDatabase:
|
||||
return {
|
||||
kind: SelectedDbItemKind.LocalDatabase,
|
||||
listName: dbItem?.parentListName,
|
||||
databaseName: dbItem.databaseName,
|
||||
};
|
||||
|
||||
case DbItemKind.RemoteRepo:
|
||||
return {
|
||||
kind: SelectedDbItemKind.RemoteRepository,
|
||||
listName: dbItem?.parentListName,
|
||||
repositoryName: dbItem.repoFullName,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ export enum DbItemKind {
|
||||
|
||||
export interface RootLocalDbItem {
|
||||
kind: DbItemKind.RootLocal;
|
||||
expanded: boolean;
|
||||
children: LocalDbItem[];
|
||||
}
|
||||
|
||||
@@ -20,20 +21,25 @@ export type LocalDbItem = LocalListDbItem | LocalDatabaseDbItem;
|
||||
|
||||
export interface LocalListDbItem {
|
||||
kind: DbItemKind.LocalList;
|
||||
expanded: boolean;
|
||||
selected: boolean;
|
||||
listName: string;
|
||||
databases: LocalDatabaseDbItem[];
|
||||
}
|
||||
|
||||
export interface LocalDatabaseDbItem {
|
||||
kind: DbItemKind.LocalDatabase;
|
||||
selected: boolean;
|
||||
databaseName: string;
|
||||
dateAdded: number;
|
||||
language: string;
|
||||
storagePath: string;
|
||||
parentListName?: string;
|
||||
}
|
||||
|
||||
export interface RootRemoteDbItem {
|
||||
kind: DbItemKind.RootRemote;
|
||||
expanded: boolean;
|
||||
children: RemoteDbItem[];
|
||||
}
|
||||
|
||||
@@ -51,6 +57,7 @@ export type RemoteDbItem =
|
||||
|
||||
export interface RemoteSystemDefinedListDbItem {
|
||||
kind: DbItemKind.RemoteSystemDefinedList;
|
||||
selected: boolean;
|
||||
listName: string;
|
||||
listDisplayName: string;
|
||||
listDescription: string;
|
||||
@@ -58,16 +65,68 @@ export interface RemoteSystemDefinedListDbItem {
|
||||
|
||||
export interface RemoteUserDefinedListDbItem {
|
||||
kind: DbItemKind.RemoteUserDefinedList;
|
||||
expanded: boolean;
|
||||
selected: boolean;
|
||||
listName: string;
|
||||
repos: RemoteRepoDbItem[];
|
||||
}
|
||||
|
||||
export interface RemoteOwnerDbItem {
|
||||
kind: DbItemKind.RemoteOwner;
|
||||
selected: boolean;
|
||||
ownerName: string;
|
||||
}
|
||||
|
||||
export interface RemoteRepoDbItem {
|
||||
kind: DbItemKind.RemoteRepo;
|
||||
selected: boolean;
|
||||
repoFullName: string;
|
||||
parentListName?: string;
|
||||
}
|
||||
|
||||
export function isRemoteSystemDefinedListDbItem(
|
||||
dbItem: DbItem,
|
||||
): dbItem is RemoteSystemDefinedListDbItem {
|
||||
return dbItem.kind === DbItemKind.RemoteSystemDefinedList;
|
||||
}
|
||||
|
||||
export function isRemoteUserDefinedListDbItem(
|
||||
dbItem: DbItem,
|
||||
): dbItem is RemoteUserDefinedListDbItem {
|
||||
return dbItem.kind === DbItemKind.RemoteUserDefinedList;
|
||||
}
|
||||
|
||||
export function isRemoteOwnerDbItem(
|
||||
dbItem: DbItem,
|
||||
): dbItem is RemoteOwnerDbItem {
|
||||
return dbItem.kind === DbItemKind.RemoteOwner;
|
||||
}
|
||||
|
||||
export function isRemoteRepoDbItem(dbItem: DbItem): dbItem is RemoteRepoDbItem {
|
||||
return dbItem.kind === DbItemKind.RemoteRepo;
|
||||
}
|
||||
|
||||
export function isLocalListDbItem(dbItem: DbItem): dbItem is LocalListDbItem {
|
||||
return dbItem.kind === DbItemKind.LocalList;
|
||||
}
|
||||
|
||||
export function isLocalDatabaseDbItem(
|
||||
dbItem: DbItem,
|
||||
): dbItem is LocalDatabaseDbItem {
|
||||
return dbItem.kind === DbItemKind.LocalDatabase;
|
||||
}
|
||||
|
||||
export type SelectableDbItem = RemoteDbItem | LocalDbItem;
|
||||
|
||||
export function isSelectableDbItem(dbItem: DbItem): dbItem is SelectableDbItem {
|
||||
return SelectableDbItemKinds.includes(dbItem.kind);
|
||||
}
|
||||
|
||||
const SelectableDbItemKinds = [
|
||||
DbItemKind.LocalList,
|
||||
DbItemKind.LocalDatabase,
|
||||
DbItemKind.RemoteSystemDefinedList,
|
||||
DbItemKind.RemoteUserDefinedList,
|
||||
DbItemKind.RemoteOwner,
|
||||
DbItemKind.RemoteRepo,
|
||||
];
|
||||
|
||||
@@ -3,6 +3,11 @@ import { AppEvent, AppEventEmitter } from "../common/events";
|
||||
import { ValueResult } from "../common/value-result";
|
||||
import { DbConfigStore } from "./config/db-config-store";
|
||||
import { DbItem } from "./db-item";
|
||||
import { calculateNewExpandedState } from "./db-item-expansion";
|
||||
import {
|
||||
getSelectedDbItem,
|
||||
mapDbItemToSelectedDbItem,
|
||||
} from "./db-item-selection";
|
||||
import { createLocalTree, createRemoteTree } from "./db-tree-creator";
|
||||
|
||||
export class DbManager {
|
||||
@@ -18,6 +23,16 @@ export class DbManager {
|
||||
});
|
||||
}
|
||||
|
||||
public getSelectedDbItem(): DbItem | undefined {
|
||||
const dbItems = this.getDbItems();
|
||||
|
||||
if (dbItems.isFailure) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return getSelectedDbItem(dbItems.value);
|
||||
}
|
||||
|
||||
public getDbItems(): ValueResult<DbItem[]> {
|
||||
const configResult = this.dbConfigStore.getConfig();
|
||||
if (configResult.isFailure) {
|
||||
@@ -33,4 +48,33 @@ export class DbManager {
|
||||
public getConfigPath(): string {
|
||||
return this.dbConfigStore.getConfigPath();
|
||||
}
|
||||
|
||||
public async setSelectedDbItem(dbItem: DbItem): Promise<void> {
|
||||
const selectedDbItem = mapDbItemToSelectedDbItem(dbItem);
|
||||
if (selectedDbItem) {
|
||||
await this.dbConfigStore.setSelectedDbItem(selectedDbItem);
|
||||
}
|
||||
}
|
||||
|
||||
public async updateDbItemExpandedState(
|
||||
dbItem: DbItem,
|
||||
itemExpanded: boolean,
|
||||
): Promise<void> {
|
||||
const configResult = this.dbConfigStore.getConfig();
|
||||
if (configResult.isFailure) {
|
||||
throw Error("Cannot update expanded state if config is not loaded");
|
||||
}
|
||||
|
||||
const newExpandedItems = calculateNewExpandedState(
|
||||
configResult.value.expanded,
|
||||
dbItem,
|
||||
itemExpanded,
|
||||
);
|
||||
|
||||
await this.dbConfigStore.updateExpandedState(newExpandedItems);
|
||||
}
|
||||
|
||||
public async addNewRemoteList(listName: string): Promise<void> {
|
||||
await this.dbConfigStore.addRemoteList(listName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,54 @@
|
||||
import { window } from "vscode";
|
||||
import { App, AppMode } from "../common/app";
|
||||
import { isCanary, isNewQueryRunExperienceEnabled } from "../config";
|
||||
import { logger } from "../logging";
|
||||
import { extLogger } from "../common";
|
||||
import { DisposableObject } from "../pure/disposable-object";
|
||||
import { DbConfigStore } from "./config/db-config-store";
|
||||
import { DbManager } from "./db-manager";
|
||||
import { DbPanel } from "./ui/db-panel";
|
||||
import { DbSelectionDecorationProvider } from "./ui/db-selection-decoration-provider";
|
||||
import { isCanary, isNewQueryRunExperienceEnabled } from "../config";
|
||||
|
||||
export class DbModule extends DisposableObject {
|
||||
public async initialize(app: App): Promise<void> {
|
||||
public readonly dbManager: DbManager;
|
||||
private readonly dbConfigStore: DbConfigStore;
|
||||
|
||||
private constructor(app: App) {
|
||||
super();
|
||||
|
||||
this.dbConfigStore = new DbConfigStore(app);
|
||||
this.dbManager = new DbManager(app, this.dbConfigStore);
|
||||
}
|
||||
|
||||
public static async initialize(app: App): Promise<DbModule | undefined> {
|
||||
if (
|
||||
app.mode !== AppMode.Development ||
|
||||
!isCanary() ||
|
||||
!isNewQueryRunExperienceEnabled()
|
||||
isCanary() &&
|
||||
isNewQueryRunExperienceEnabled() &&
|
||||
app.mode === AppMode.Development
|
||||
) {
|
||||
// Currently, we only want to expose the new database panel when we
|
||||
// are in development and canary mode and the developer has enabled the
|
||||
// new query run experience.
|
||||
return;
|
||||
const dbModule = new DbModule(app);
|
||||
app.subscriptions.push(dbModule);
|
||||
|
||||
await dbModule.initialize();
|
||||
|
||||
return dbModule;
|
||||
}
|
||||
|
||||
void logger.log("Initializing database module");
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const dbConfigStore = new DbConfigStore(app);
|
||||
await dbConfigStore.initialize();
|
||||
private async initialize(): Promise<void> {
|
||||
void extLogger.log("Initializing database module");
|
||||
|
||||
const dbManager = new DbManager(app, dbConfigStore);
|
||||
const dbPanel = new DbPanel(dbManager);
|
||||
await this.dbConfigStore.initialize();
|
||||
|
||||
const dbPanel = new DbPanel(this.dbManager);
|
||||
await dbPanel.initialize();
|
||||
|
||||
this.push(dbPanel);
|
||||
this.push(dbConfigStore);
|
||||
this.push(this.dbConfigStore);
|
||||
|
||||
const dbSelectionDecorationProvider = new DbSelectionDecorationProvider();
|
||||
|
||||
window.registerFileDecorationProvider(dbSelectionDecorationProvider);
|
||||
}
|
||||
}
|
||||
|
||||
export async function initializeDbModule(app: App): Promise<DbModule> {
|
||||
const dbModule = new DbModule();
|
||||
await dbModule.initialize(app);
|
||||
return dbModule;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import {
|
||||
DbConfig,
|
||||
ExpandedDbItemKind,
|
||||
LocalDatabase,
|
||||
LocalList,
|
||||
RemoteRepositoryList,
|
||||
SelectedDbItemKind,
|
||||
} from "./config/db-config";
|
||||
import {
|
||||
DbItemKind,
|
||||
@@ -18,16 +20,24 @@ import {
|
||||
|
||||
export function createRemoteTree(dbConfig: DbConfig): RootRemoteDbItem {
|
||||
const systemDefinedLists = [
|
||||
createSystemDefinedList(10),
|
||||
createSystemDefinedList(100),
|
||||
createSystemDefinedList(1000),
|
||||
createSystemDefinedList(10, dbConfig),
|
||||
createSystemDefinedList(100, dbConfig),
|
||||
createSystemDefinedList(1000, dbConfig),
|
||||
];
|
||||
|
||||
const userDefinedRepoLists = dbConfig.databases.remote.repositoryLists.map(
|
||||
createUserDefinedList,
|
||||
(r) => createRemoteUserDefinedList(r, dbConfig),
|
||||
);
|
||||
const owners = dbConfig.databases.remote.owners.map(createOwnerItem);
|
||||
const repos = dbConfig.databases.remote.repositories.map(createRepoItem);
|
||||
const owners = dbConfig.databases.remote.owners.map((o) =>
|
||||
createOwnerItem(o, dbConfig),
|
||||
);
|
||||
const repos = dbConfig.databases.remote.repositories.map((r) =>
|
||||
createRepoItem(r, dbConfig),
|
||||
);
|
||||
|
||||
const expanded =
|
||||
dbConfig.expanded &&
|
||||
dbConfig.expanded.some((e) => e.kind === ExpandedDbItemKind.RootRemote);
|
||||
|
||||
return {
|
||||
kind: DbItemKind.RootRemote,
|
||||
@@ -37,66 +47,148 @@ export function createRemoteTree(dbConfig: DbConfig): RootRemoteDbItem {
|
||||
...userDefinedRepoLists,
|
||||
...repos,
|
||||
],
|
||||
expanded: !!expanded,
|
||||
};
|
||||
}
|
||||
|
||||
export function createLocalTree(dbConfig: DbConfig): RootLocalDbItem {
|
||||
const localLists = dbConfig.databases.local.lists.map(createLocalList);
|
||||
const localDbs = dbConfig.databases.local.databases.map(createLocalDb);
|
||||
const localLists = dbConfig.databases.local.lists.map((l) =>
|
||||
createLocalList(l, dbConfig),
|
||||
);
|
||||
const localDbs = dbConfig.databases.local.databases.map((l) =>
|
||||
createLocalDb(l, dbConfig),
|
||||
);
|
||||
|
||||
const expanded =
|
||||
dbConfig.expanded &&
|
||||
dbConfig.expanded.some((e) => e.kind === ExpandedDbItemKind.RootLocal);
|
||||
|
||||
return {
|
||||
kind: DbItemKind.RootLocal,
|
||||
children: [...localLists, ...localDbs],
|
||||
expanded: !!expanded,
|
||||
};
|
||||
}
|
||||
|
||||
function createSystemDefinedList(n: number): RemoteSystemDefinedListDbItem {
|
||||
function createSystemDefinedList(
|
||||
n: number,
|
||||
dbConfig: DbConfig,
|
||||
): RemoteSystemDefinedListDbItem {
|
||||
const listName = `top_${n}`;
|
||||
|
||||
const selected =
|
||||
dbConfig.selected &&
|
||||
dbConfig.selected.kind === SelectedDbItemKind.RemoteSystemDefinedList &&
|
||||
dbConfig.selected.listName === listName;
|
||||
|
||||
return {
|
||||
kind: DbItemKind.RemoteSystemDefinedList,
|
||||
listName: `top_${n}`,
|
||||
listName,
|
||||
listDisplayName: `Top ${n} repositories`,
|
||||
listDescription: `Top ${n} repositories of a language`,
|
||||
selected: !!selected,
|
||||
};
|
||||
}
|
||||
|
||||
function createUserDefinedList(
|
||||
function createRemoteUserDefinedList(
|
||||
list: RemoteRepositoryList,
|
||||
dbConfig: DbConfig,
|
||||
): RemoteUserDefinedListDbItem {
|
||||
const selected =
|
||||
dbConfig.selected &&
|
||||
dbConfig.selected.kind === SelectedDbItemKind.RemoteUserDefinedList &&
|
||||
dbConfig.selected.listName === list.name;
|
||||
|
||||
const expanded =
|
||||
dbConfig.expanded &&
|
||||
dbConfig.expanded.some(
|
||||
(e) =>
|
||||
e.kind === ExpandedDbItemKind.RemoteUserDefinedList &&
|
||||
e.listName === list.name,
|
||||
);
|
||||
|
||||
return {
|
||||
kind: DbItemKind.RemoteUserDefinedList,
|
||||
listName: list.name,
|
||||
repos: list.repositories.map((r) => createRepoItem(r)),
|
||||
repos: list.repositories.map((r) => createRepoItem(r, dbConfig, list.name)),
|
||||
selected: !!selected,
|
||||
expanded: !!expanded,
|
||||
};
|
||||
}
|
||||
|
||||
function createOwnerItem(owner: string): RemoteOwnerDbItem {
|
||||
function createOwnerItem(owner: string, dbConfig: DbConfig): RemoteOwnerDbItem {
|
||||
const selected =
|
||||
dbConfig.selected &&
|
||||
dbConfig.selected.kind === SelectedDbItemKind.RemoteOwner &&
|
||||
dbConfig.selected.ownerName === owner;
|
||||
|
||||
return {
|
||||
kind: DbItemKind.RemoteOwner,
|
||||
ownerName: owner,
|
||||
selected: !!selected,
|
||||
};
|
||||
}
|
||||
|
||||
function createRepoItem(repo: string): RemoteRepoDbItem {
|
||||
function createRepoItem(
|
||||
repo: string,
|
||||
dbConfig: DbConfig,
|
||||
listName?: string,
|
||||
): RemoteRepoDbItem {
|
||||
const selected =
|
||||
dbConfig.selected &&
|
||||
dbConfig.selected.kind === SelectedDbItemKind.RemoteRepository &&
|
||||
dbConfig.selected.repositoryName === repo &&
|
||||
dbConfig.selected.listName === listName;
|
||||
|
||||
return {
|
||||
kind: DbItemKind.RemoteRepo,
|
||||
repoFullName: repo,
|
||||
selected: !!selected,
|
||||
parentListName: listName,
|
||||
};
|
||||
}
|
||||
|
||||
function createLocalList(list: LocalList): LocalListDbItem {
|
||||
function createLocalList(list: LocalList, dbConfig: DbConfig): LocalListDbItem {
|
||||
const selected =
|
||||
dbConfig.selected &&
|
||||
dbConfig.selected.kind === SelectedDbItemKind.LocalUserDefinedList &&
|
||||
dbConfig.selected.listName === list.name;
|
||||
|
||||
const expanded =
|
||||
dbConfig.expanded &&
|
||||
dbConfig.expanded.some(
|
||||
(e) =>
|
||||
e.kind === ExpandedDbItemKind.LocalUserDefinedList &&
|
||||
e.listName === list.name,
|
||||
);
|
||||
|
||||
return {
|
||||
kind: DbItemKind.LocalList,
|
||||
listName: list.name,
|
||||
databases: list.databases.map(createLocalDb),
|
||||
databases: list.databases.map((d) => createLocalDb(d, dbConfig, list.name)),
|
||||
selected: !!selected,
|
||||
expanded: !!expanded,
|
||||
};
|
||||
}
|
||||
|
||||
function createLocalDb(db: LocalDatabase): LocalDatabaseDbItem {
|
||||
function createLocalDb(
|
||||
db: LocalDatabase,
|
||||
dbConfig: DbConfig,
|
||||
listName?: string,
|
||||
): LocalDatabaseDbItem {
|
||||
const selected =
|
||||
dbConfig.selected &&
|
||||
dbConfig.selected.kind === SelectedDbItemKind.LocalDatabase &&
|
||||
dbConfig.selected.databaseName === db.name &&
|
||||
dbConfig.selected.listName === listName;
|
||||
|
||||
return {
|
||||
kind: DbItemKind.LocalDatabase,
|
||||
databaseName: db.name,
|
||||
dateAdded: db.dateAdded,
|
||||
language: db.language,
|
||||
storagePath: db.storagePath,
|
||||
selected: !!selected,
|
||||
parentListName: listName,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import * as vscode from "vscode";
|
||||
import { TreeViewExpansionEvent, window, workspace } from "vscode";
|
||||
import { commandRunner } from "../../commandRunner";
|
||||
import { DisposableObject } from "../../pure/disposable-object";
|
||||
import { DbManager } from "../db-manager";
|
||||
import { DbTreeDataProvider } from "./db-tree-data-provider";
|
||||
import { DbTreeViewItem } from "./db-tree-view-item";
|
||||
|
||||
export class DbPanel extends DisposableObject {
|
||||
private readonly dataProvider: DbTreeDataProvider;
|
||||
@@ -12,12 +13,20 @@ export class DbPanel extends DisposableObject {
|
||||
|
||||
this.dataProvider = new DbTreeDataProvider(dbManager);
|
||||
|
||||
const treeView = vscode.window.createTreeView(
|
||||
"codeQLDatabasesExperimental",
|
||||
{
|
||||
treeDataProvider: this.dataProvider,
|
||||
canSelectMany: false,
|
||||
},
|
||||
const treeView = window.createTreeView("codeQLDatabasesExperimental", {
|
||||
treeDataProvider: this.dataProvider,
|
||||
canSelectMany: false,
|
||||
});
|
||||
|
||||
this.push(
|
||||
treeView.onDidCollapseElement(async (e) => {
|
||||
await this.onDidCollapseElement(e);
|
||||
}),
|
||||
);
|
||||
this.push(
|
||||
treeView.onDidExpandElement(async (e) => {
|
||||
await this.onDidExpandElement(e);
|
||||
}),
|
||||
);
|
||||
|
||||
this.push(treeView);
|
||||
@@ -29,11 +38,65 @@ export class DbPanel extends DisposableObject {
|
||||
this.openConfigFile(),
|
||||
),
|
||||
);
|
||||
this.push(
|
||||
commandRunner("codeQLDatabasesExperimental.addNewList", () =>
|
||||
this.addNewRemoteList(),
|
||||
),
|
||||
);
|
||||
this.push(
|
||||
commandRunner(
|
||||
"codeQLDatabasesExperimental.setSelectedItem",
|
||||
(treeViewItem: DbTreeViewItem) => this.setSelectedItem(treeViewItem),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private async openConfigFile(): Promise<void> {
|
||||
const configPath = this.dbManager.getConfigPath();
|
||||
const document = await vscode.workspace.openTextDocument(configPath);
|
||||
await vscode.window.showTextDocument(document);
|
||||
const document = await workspace.openTextDocument(configPath);
|
||||
await window.showTextDocument(document);
|
||||
}
|
||||
|
||||
private async addNewRemoteList(): Promise<void> {
|
||||
// TODO: check that config exists *before* showing the input box
|
||||
const listName = await window.showInputBox({
|
||||
prompt: "Enter a name for the new list",
|
||||
placeHolder: "example-list",
|
||||
});
|
||||
if (listName === undefined) {
|
||||
return;
|
||||
}
|
||||
await this.dbManager.addNewRemoteList(listName);
|
||||
}
|
||||
|
||||
private async setSelectedItem(treeViewItem: DbTreeViewItem): Promise<void> {
|
||||
if (treeViewItem.dbItem === undefined) {
|
||||
throw new Error(
|
||||
"Not a selectable database item. Please select a valid item.",
|
||||
);
|
||||
}
|
||||
await this.dbManager.setSelectedDbItem(treeViewItem.dbItem);
|
||||
}
|
||||
|
||||
private async onDidCollapseElement(
|
||||
event: TreeViewExpansionEvent<DbTreeViewItem>,
|
||||
): Promise<void> {
|
||||
const dbItem = event.element.dbItem;
|
||||
if (!dbItem) {
|
||||
throw Error("Expected a database item.");
|
||||
}
|
||||
|
||||
await this.dbManager.updateDbItemExpandedState(event.element.dbItem, false);
|
||||
}
|
||||
|
||||
private async onDidExpandElement(
|
||||
event: TreeViewExpansionEvent<DbTreeViewItem>,
|
||||
): Promise<void> {
|
||||
const dbItem = event.element.dbItem;
|
||||
if (!dbItem) {
|
||||
throw Error("Expected a database item.");
|
||||
}
|
||||
|
||||
await this.dbManager.updateDbItemExpandedState(event.element.dbItem, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import {
|
||||
CancellationToken,
|
||||
FileDecoration,
|
||||
FileDecorationProvider,
|
||||
ProviderResult,
|
||||
Uri,
|
||||
} from "vscode";
|
||||
|
||||
export class DbSelectionDecorationProvider implements FileDecorationProvider {
|
||||
provideFileDecoration(
|
||||
uri: Uri,
|
||||
_token: CancellationToken,
|
||||
): ProviderResult<FileDecoration> {
|
||||
if (uri?.query === "selected=true") {
|
||||
return {
|
||||
badge: "●",
|
||||
tooltip: "Currently selected",
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as vscode from "vscode";
|
||||
import {
|
||||
DbItem,
|
||||
isSelectableDbItem,
|
||||
LocalDatabaseDbItem,
|
||||
LocalListDbItem,
|
||||
RemoteOwnerDbItem,
|
||||
@@ -28,6 +29,16 @@ export class DbTreeViewItem extends vscode.TreeItem {
|
||||
public readonly children: DbTreeViewItem[],
|
||||
) {
|
||||
super(label, collapsibleState);
|
||||
|
||||
if (dbItem && isSelectableDbItem(dbItem)) {
|
||||
if (dbItem.selected) {
|
||||
// Define the resource id to drive the UI to render this item as selected.
|
||||
this.resourceUri = vscode.Uri.parse("codeql://databases?selected=true");
|
||||
} else {
|
||||
// Define a context value to drive the UI to show an action to select the item.
|
||||
this.contextValue = "selectableDbItem";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +70,7 @@ export function createDbTreeViewItemRoot(
|
||||
undefined,
|
||||
label,
|
||||
tooltip,
|
||||
vscode.TreeItemCollapsibleState.Collapsed,
|
||||
getCollapsibleState(dbItem.expanded),
|
||||
children,
|
||||
);
|
||||
}
|
||||
@@ -89,7 +100,7 @@ export function createDbTreeViewItemUserDefinedList(
|
||||
undefined,
|
||||
listName,
|
||||
undefined,
|
||||
vscode.TreeItemCollapsibleState.Collapsed,
|
||||
getCollapsibleState(dbItem.expanded),
|
||||
children,
|
||||
);
|
||||
}
|
||||
@@ -136,3 +147,11 @@ export function createDbTreeViewItemLocalDatabase(
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
function getCollapsibleState(
|
||||
expanded: boolean,
|
||||
): vscode.TreeItemCollapsibleState {
|
||||
return expanded
|
||||
? vscode.TreeItemCollapsibleState.Expanded
|
||||
: vscode.TreeItemCollapsibleState.Collapsed;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DisposableObject } from "./pure/disposable-object";
|
||||
import { logger } from "./logging";
|
||||
import { extLogger } from "./common";
|
||||
|
||||
/**
|
||||
* Base class for "discovery" operations, which scan the file system to find specific kinds of
|
||||
@@ -62,7 +62,7 @@ export abstract class Discovery<T> extends DisposableObject {
|
||||
})
|
||||
|
||||
.catch((err) => {
|
||||
void logger.log(`${this.name} failed. Reason: ${err.message}`);
|
||||
void extLogger.log(`${this.name} failed. Reason: ${err.message}`);
|
||||
})
|
||||
|
||||
.finally(() => {
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import * as fetch from "node-fetch";
|
||||
import * as fs from "fs-extra";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
import { pathExists, mkdtemp, createWriteStream, remove } from "fs-extra";
|
||||
import { tmpdir } from "os";
|
||||
import { delimiter, dirname, join } from "path";
|
||||
import * as semver from "semver";
|
||||
import * as unzipper from "unzipper";
|
||||
import * as url from "url";
|
||||
import { parse } from "url";
|
||||
import { ExtensionContext, Event } from "vscode";
|
||||
import { DistributionConfig } from "./config";
|
||||
import {
|
||||
@@ -13,9 +12,15 @@ import {
|
||||
showAndLogErrorMessage,
|
||||
showAndLogWarningMessage,
|
||||
} from "./helpers";
|
||||
import { logger } from "./logging";
|
||||
import { extLogger } from "./common";
|
||||
import { getCodeQlCliVersion } from "./cli-version";
|
||||
import { ProgressCallback, reportStreamProgress } from "./commandRunner";
|
||||
import {
|
||||
codeQlLauncherName,
|
||||
deprecatedCodeQlLauncherName,
|
||||
extractZipArchive,
|
||||
getRequiredAssetName,
|
||||
} from "./pure/distribution";
|
||||
|
||||
/**
|
||||
* distribution.ts
|
||||
@@ -55,22 +60,6 @@ export interface DistributionProvider {
|
||||
}
|
||||
|
||||
export class DistributionManager implements DistributionProvider {
|
||||
/**
|
||||
* Get the name of the codeql cli installation we prefer to install, based on our current platform.
|
||||
*/
|
||||
public static getRequiredAssetName(): string {
|
||||
switch (os.platform()) {
|
||||
case "linux":
|
||||
return "codeql-linux64.zip";
|
||||
case "darwin":
|
||||
return "codeql-osx64.zip";
|
||||
case "win32":
|
||||
return "codeql-win64.zip";
|
||||
default:
|
||||
return "codeql.zip";
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
public readonly config: DistributionConfig,
|
||||
private readonly versionRange: semver.Range,
|
||||
@@ -101,7 +90,10 @@ export class DistributionManager implements DistributionProvider {
|
||||
kind: FindDistributionResultKind.NoDistribution,
|
||||
};
|
||||
}
|
||||
const version = await getCodeQlCliVersion(distribution.codeQlPath, logger);
|
||||
const version = await getCodeQlCliVersion(
|
||||
distribution.codeQlPath,
|
||||
extLogger,
|
||||
);
|
||||
if (version === undefined) {
|
||||
return {
|
||||
distribution,
|
||||
@@ -161,7 +153,7 @@ export class DistributionManager implements DistributionProvider {
|
||||
> {
|
||||
// Check config setting, then extension specific distribution, then PATH.
|
||||
if (this.config.customCodeQlPath) {
|
||||
if (!(await fs.pathExists(this.config.customCodeQlPath))) {
|
||||
if (!(await pathExists(this.config.customCodeQlPath))) {
|
||||
void 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 " +
|
||||
@@ -196,7 +188,7 @@ export class DistributionManager implements DistributionProvider {
|
||||
}
|
||||
|
||||
if (process.env.PATH) {
|
||||
for (const searchDirectory of process.env.PATH.split(path.delimiter)) {
|
||||
for (const searchDirectory of process.env.PATH.split(delimiter)) {
|
||||
const expectedLauncherPath = await getExecutableFromDirectory(
|
||||
searchDirectory,
|
||||
);
|
||||
@@ -207,7 +199,7 @@ export class DistributionManager implements DistributionProvider {
|
||||
};
|
||||
}
|
||||
}
|
||||
void logger.log("INFO: Could not find CodeQL on path.");
|
||||
void extLogger.log("INFO: Could not find CodeQL on path.");
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@@ -270,9 +262,9 @@ export class DistributionManager implements DistributionProvider {
|
||||
// not managed externally
|
||||
return false;
|
||||
}
|
||||
const dir = path.dirname(this.config.customCodeQlPath);
|
||||
const newLaunderPath = path.join(dir, codeQlLauncherName());
|
||||
return await fs.pathExists(newLaunderPath);
|
||||
const dir = dirname(this.config.customCodeQlPath);
|
||||
const newLaunderPath = join(dir, codeQlLauncherName());
|
||||
return await pathExists(newLaunderPath);
|
||||
}
|
||||
|
||||
private readonly extensionSpecificDistributionManager: ExtensionSpecificDistributionManager;
|
||||
@@ -303,7 +295,7 @@ class ExtensionSpecificDistributionManager {
|
||||
try {
|
||||
await this.removeDistribution();
|
||||
} catch (e) {
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
"WARNING: Tried to remove corrupted CodeQL CLI at " +
|
||||
`${this.getDistributionStoragePath()} but encountered an error: ${e}.`,
|
||||
);
|
||||
@@ -354,14 +346,14 @@ class ExtensionSpecificDistributionManager {
|
||||
try {
|
||||
await this.removeDistribution();
|
||||
} catch (e) {
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
`Tried to clean up old version of CLI at ${this.getDistributionStoragePath()} ` +
|
||||
`but encountered an error: ${e}.`,
|
||||
);
|
||||
}
|
||||
|
||||
// Filter assets to the unique one that we require.
|
||||
const requiredAssetName = DistributionManager.getRequiredAssetName();
|
||||
const requiredAssetName = getRequiredAssetName();
|
||||
const assets = release.assets.filter(
|
||||
(asset) => asset.name === requiredAssetName,
|
||||
);
|
||||
@@ -371,9 +363,10 @@ class ExtensionSpecificDistributionManager {
|
||||
);
|
||||
}
|
||||
if (assets.length > 1) {
|
||||
void logger.log(
|
||||
"WARNING: chose a release with more than one asset to install, found " +
|
||||
assets.map((asset) => asset.name).join(", "),
|
||||
void extLogger.log(
|
||||
`WARNING: chose a release with more than one asset to install, found ${assets
|
||||
.map((asset) => asset.name)
|
||||
.join(", ")}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -381,13 +374,11 @@ class ExtensionSpecificDistributionManager {
|
||||
await this.createReleasesApiConsumer().streamBinaryContentOfAsset(
|
||||
assets[0],
|
||||
);
|
||||
const tmpDirectory = await fs.mkdtemp(
|
||||
path.join(os.tmpdir(), "vscode-codeql"),
|
||||
);
|
||||
const tmpDirectory = await mkdtemp(join(tmpdir(), "vscode-codeql"));
|
||||
|
||||
try {
|
||||
const archivePath = path.join(tmpDirectory, "distributionDownload.zip");
|
||||
const archiveFile = fs.createWriteStream(archivePath);
|
||||
const archivePath = join(tmpDirectory, "distributionDownload.zip");
|
||||
const archiveFile = createWriteStream(archivePath);
|
||||
|
||||
const contentLength = assetStream.headers.get("content-length");
|
||||
const totalNumBytes = contentLength
|
||||
@@ -409,12 +400,12 @@ class ExtensionSpecificDistributionManager {
|
||||
|
||||
await this.bumpDistributionFolderIndex();
|
||||
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
`Extracting CodeQL CLI to ${this.getDistributionStoragePath()}`,
|
||||
);
|
||||
await extractZipArchive(archivePath, this.getDistributionStoragePath());
|
||||
} finally {
|
||||
await fs.remove(tmpDirectory);
|
||||
await remove(tmpDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,14 +416,14 @@ class ExtensionSpecificDistributionManager {
|
||||
*/
|
||||
private async removeDistribution(): Promise<void> {
|
||||
await this.storeInstalledRelease(undefined);
|
||||
if (await fs.pathExists(this.getDistributionStoragePath())) {
|
||||
await fs.remove(this.getDistributionStoragePath());
|
||||
if (await pathExists(this.getDistributionStoragePath())) {
|
||||
await remove(this.getDistributionStoragePath());
|
||||
}
|
||||
}
|
||||
|
||||
private async getLatestRelease(): Promise<Release> {
|
||||
const requiredAssetName = DistributionManager.getRequiredAssetName();
|
||||
void logger.log(
|
||||
const requiredAssetName = getRequiredAssetName();
|
||||
void extLogger.log(
|
||||
`Searching for latest release including ${requiredAssetName}.`,
|
||||
);
|
||||
return this.createReleasesApiConsumer().getLatestRelease(
|
||||
@@ -444,13 +435,13 @@ class ExtensionSpecificDistributionManager {
|
||||
);
|
||||
if (matchingAssets.length === 0) {
|
||||
// For example, this could be a release with no platform-specific assets.
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
`INFO: Ignoring a release with no assets named ${requiredAssetName}`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (matchingAssets.length > 1) {
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
`WARNING: Ignoring a release with more than one asset named ${requiredAssetName}`,
|
||||
);
|
||||
return false;
|
||||
@@ -492,7 +483,7 @@ class ExtensionSpecificDistributionManager {
|
||||
ExtensionSpecificDistributionManager._currentDistributionFolderIndexStateKey,
|
||||
0,
|
||||
) || "";
|
||||
return path.join(
|
||||
return join(
|
||||
this.extensionContext.globalStoragePath,
|
||||
ExtensionSpecificDistributionManager._currentDistributionFolderBaseName +
|
||||
distributionFolderIndex,
|
||||
@@ -500,7 +491,7 @@ class ExtensionSpecificDistributionManager {
|
||||
}
|
||||
|
||||
private getDistributionRootPath(): string {
|
||||
return path.join(
|
||||
return join(
|
||||
this.getDistributionStoragePath(),
|
||||
ExtensionSpecificDistributionManager._codeQlExtractedFolderName,
|
||||
);
|
||||
@@ -658,7 +649,7 @@ export class ReleasesApiConsumer {
|
||||
redirectUrl &&
|
||||
redirectCount < ReleasesApiConsumer._maxRedirects
|
||||
) {
|
||||
const parsedRedirectUrl = url.parse(redirectUrl);
|
||||
const parsedRedirectUrl = parse(redirectUrl);
|
||||
if (parsedRedirectUrl.protocol != "https:") {
|
||||
throw new Error("Encountered a non-https redirect, rejecting");
|
||||
}
|
||||
@@ -683,39 +674,6 @@ export class ReleasesApiConsumer {
|
||||
private static readonly _maxRedirects = 20;
|
||||
}
|
||||
|
||||
export async function extractZipArchive(
|
||||
archivePath: string,
|
||||
outPath: string,
|
||||
): Promise<void> {
|
||||
const archive = await unzipper.Open.file(archivePath);
|
||||
await archive.extract({
|
||||
concurrency: 4,
|
||||
path: outPath,
|
||||
});
|
||||
// Set file permissions for extracted files
|
||||
await Promise.all(
|
||||
archive.files.map(async (file) => {
|
||||
// Only change file permissions if within outPath (path.join normalises the path)
|
||||
const extractedPath = path.join(outPath, file.path);
|
||||
if (
|
||||
extractedPath.indexOf(outPath) !== 0 ||
|
||||
!(await fs.pathExists(extractedPath))
|
||||
) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return fs.chmod(extractedPath, file.externalFileAttributes >>> 16);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export function codeQlLauncherName(): string {
|
||||
return os.platform() === "win32" ? "codeql.exe" : "codeql";
|
||||
}
|
||||
|
||||
function deprecatedCodeQlLauncherName(): string | undefined {
|
||||
return os.platform() === "win32" ? "codeql.cmd" : undefined;
|
||||
}
|
||||
|
||||
function isRedirectStatusCode(statusCode: number): boolean {
|
||||
return (
|
||||
statusCode === 301 ||
|
||||
@@ -846,22 +804,22 @@ export async function getExecutableFromDirectory(
|
||||
directory: string,
|
||||
warnWhenNotFound = false,
|
||||
): Promise<string | undefined> {
|
||||
const expectedLauncherPath = path.join(directory, codeQlLauncherName());
|
||||
const expectedLauncherPath = join(directory, codeQlLauncherName());
|
||||
const deprecatedLauncherName = deprecatedCodeQlLauncherName();
|
||||
const alternateExpectedLauncherPath = deprecatedLauncherName
|
||||
? path.join(directory, deprecatedLauncherName)
|
||||
? join(directory, deprecatedLauncherName)
|
||||
: undefined;
|
||||
if (await fs.pathExists(expectedLauncherPath)) {
|
||||
if (await pathExists(expectedLauncherPath)) {
|
||||
return expectedLauncherPath;
|
||||
} else if (
|
||||
alternateExpectedLauncherPath &&
|
||||
(await fs.pathExists(alternateExpectedLauncherPath))
|
||||
(await pathExists(alternateExpectedLauncherPath))
|
||||
) {
|
||||
warnDeprecatedLauncher();
|
||||
return alternateExpectedLauncherPath;
|
||||
}
|
||||
if (warnWhenNotFound) {
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
`WARNING: Expected to find a CodeQL CLI executable at ${expectedLauncherPath} but one was not found. ` +
|
||||
"Will try PATH.",
|
||||
);
|
||||
|
||||
@@ -19,16 +19,19 @@ import {
|
||||
ProviderResult,
|
||||
version as vscodeVersion,
|
||||
} from "vscode";
|
||||
import { LanguageClient } from "vscode-languageclient";
|
||||
import * as os from "os";
|
||||
import * as fs from "fs-extra";
|
||||
import * as path from "path";
|
||||
import * as tmp from "tmp-promise";
|
||||
import { LanguageClient } from "vscode-languageclient/node";
|
||||
import { platform, arch } from "os";
|
||||
import { ensureDir } from "fs-extra";
|
||||
import { join, basename } from "path";
|
||||
import { dirSync } from "tmp-promise";
|
||||
import { testExplorerExtensionId, TestHub } from "vscode-test-adapter-api";
|
||||
import * as semver from "semver";
|
||||
import { parse, lt } from "semver";
|
||||
|
||||
import { AstViewer } from "./astViewer";
|
||||
import * as archiveFilesystemProvider from "./archive-filesystem-provider";
|
||||
import {
|
||||
activate as archiveFilesystemProvider_activate,
|
||||
zipArchiveScheme,
|
||||
} from "./archive-filesystem-provider";
|
||||
import QuickEvalCodeLensProvider from "./quickEvalCodeLensProvider";
|
||||
import { CodeQLCliServer, CliVersionConstraint } from "./cli";
|
||||
import {
|
||||
@@ -41,7 +44,7 @@ import {
|
||||
QueryHistoryConfigListener,
|
||||
QueryServerConfigListener,
|
||||
} from "./config";
|
||||
import * as languageSupport from "./languageSupport";
|
||||
import { install } from "./languageSupport";
|
||||
import { DatabaseItem, DatabaseManager } from "./databases";
|
||||
import { DatabaseUI } from "./databases-ui";
|
||||
import {
|
||||
@@ -76,14 +79,14 @@ import { ResultsView } from "./interface";
|
||||
import { WebviewReveal } from "./interface-utils";
|
||||
import {
|
||||
ideServerLogger,
|
||||
logger,
|
||||
extLogger,
|
||||
ProgressReporter,
|
||||
queryServerLogger,
|
||||
} from "./logging";
|
||||
} from "./common";
|
||||
import { QueryHistoryManager } from "./query-history";
|
||||
import { CompletedLocalQueryInfo, LocalQueryInfo } from "./query-results";
|
||||
import * as legacyQueryServer from "./legacy-query-server/queryserver-client";
|
||||
import * as newQueryServer from "./query-server/queryserver-client";
|
||||
import { QueryServerClient as LegacyQueryServerClient } from "./legacy-query-server/queryserver-client";
|
||||
import { QueryServerClient } from "./query-server/queryserver-client";
|
||||
import { displayQuickQuery } from "./quick-query";
|
||||
import { QLTestAdapterFactory } from "./test-adapter";
|
||||
import { TestUIService } from "./test-ui";
|
||||
@@ -132,9 +135,9 @@ import { VariantAnalysisManager } from "./remote-queries/variant-analysis-manage
|
||||
import { createVariantAnalysisContentProvider } from "./remote-queries/variant-analysis-content-provider";
|
||||
import { VSCodeMockGitHubApiServer } from "./mocks/vscode-mock-gh-api-server";
|
||||
import { VariantAnalysisResultsManager } from "./remote-queries/variant-analysis-results-manager";
|
||||
import { initializeDbModule } from "./databases/db-module";
|
||||
import { ExtensionApp } from "./common/vscode/vscode-app";
|
||||
import { RepositoriesFilterSortStateWithIds } from "./pure/variant-analysis-filter-sort";
|
||||
import { DbModule } from "./databases/db-module";
|
||||
|
||||
/**
|
||||
* extension.ts
|
||||
@@ -230,7 +233,7 @@ const MIN_VERSION = "1.67.0";
|
||||
export async function activate(
|
||||
ctx: ExtensionContext,
|
||||
): Promise<CodeQLExtensionInterface | Record<string, never>> {
|
||||
void logger.log(`Starting ${extensionId} extension`);
|
||||
void extLogger.log(`Starting ${extensionId} extension`);
|
||||
if (extension === undefined) {
|
||||
throw new Error(`Can't find extension ${extensionId}`);
|
||||
}
|
||||
@@ -238,7 +241,7 @@ export async function activate(
|
||||
const distributionConfigListener = new DistributionConfigListener();
|
||||
await initializeLogging(ctx);
|
||||
await initializeTelemetry(extension, ctx);
|
||||
languageSupport.install();
|
||||
install();
|
||||
|
||||
const codelensProvider = new QuickEvalCodeLensProvider();
|
||||
languages.registerCodeLensProvider(
|
||||
@@ -278,7 +281,7 @@ export async function activate(
|
||||
const minSecondsSinceLastUpdateCheck = config.isUserInitiated ? 0 : 86400;
|
||||
const noUpdatesLoggingFunc = config.shouldDisplayMessageWhenNoUpdates
|
||||
? showAndLogInformationMessage
|
||||
: async (message: string) => void logger.log(message);
|
||||
: async (message: string) => void extLogger.log(message);
|
||||
const result =
|
||||
await distributionManager.checkForUpdatesToExtensionManagedDistribution(
|
||||
minSecondsSinceLastUpdateCheck,
|
||||
@@ -291,7 +294,7 @@ export async function activate(
|
||||
|
||||
switch (result.kind) {
|
||||
case DistributionUpdateCheckResultKind.AlreadyCheckedRecentlyResult:
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
"Didn't perform CodeQL CLI update check since a check was already performed within the previous " +
|
||||
`${minSecondsSinceLastUpdateCheck} seconds.`,
|
||||
);
|
||||
@@ -370,12 +373,13 @@ export async function activate(
|
||||
codeQlInstalled && !config.isUserInitiated
|
||||
? showAndLogWarningMessage
|
||||
: showAndLogErrorMessage;
|
||||
const taskDescription =
|
||||
(willUpdateCodeQl
|
||||
const taskDescription = `${
|
||||
willUpdateCodeQl
|
||||
? "update"
|
||||
: codeQlInstalled
|
||||
? "check for updates to"
|
||||
: "install") + " CodeQL CLI";
|
||||
: "install"
|
||||
} CodeQL CLI`;
|
||||
|
||||
if (e instanceof GithubRateLimitedError) {
|
||||
void alertFunction(
|
||||
@@ -386,11 +390,10 @@ export async function activate(
|
||||
);
|
||||
} else if (e instanceof GithubApiError) {
|
||||
void alertFunction(
|
||||
`Encountered GitHub API error while trying to ${taskDescription}. ` +
|
||||
e,
|
||||
`Encountered GitHub API error while trying to ${taskDescription}. ${e}`,
|
||||
);
|
||||
}
|
||||
void alertFunction(`Unable to ${taskDescription}. ` + e);
|
||||
void alertFunction(`Unable to ${taskDescription}. ${e}`);
|
||||
} finally {
|
||||
isInstallingOrUpdatingDistribution = false;
|
||||
}
|
||||
@@ -400,7 +403,7 @@ export async function activate(
|
||||
const result = await distributionManager.getDistribution();
|
||||
switch (result.kind) {
|
||||
case FindDistributionResultKind.CompatibleDistribution:
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
`Found compatible version of CodeQL CLI (version ${result.version.raw})`,
|
||||
);
|
||||
break;
|
||||
@@ -543,18 +546,18 @@ async function activateWithInstalledDistribution(
|
||||
// of activation.
|
||||
errorStubs.forEach((stub) => stub.dispose());
|
||||
|
||||
void logger.log("Initializing configuration listener...");
|
||||
void extLogger.log("Initializing configuration listener...");
|
||||
const qlConfigurationListener =
|
||||
await QueryServerConfigListener.createQueryServerConfigListener(
|
||||
distributionManager,
|
||||
);
|
||||
ctx.subscriptions.push(qlConfigurationListener);
|
||||
|
||||
void logger.log("Initializing CodeQL cli server...");
|
||||
void extLogger.log("Initializing CodeQL cli server...");
|
||||
const cliServer = new CodeQLCliServer(
|
||||
distributionManager,
|
||||
new CliConfigListener(),
|
||||
logger,
|
||||
extLogger,
|
||||
);
|
||||
ctx.subscriptions.push(cliServer);
|
||||
|
||||
@@ -564,7 +567,7 @@ async function activateWithInstalledDistribution(
|
||||
);
|
||||
ctx.subscriptions.push(statusBar);
|
||||
|
||||
void logger.log("Initializing query server client.");
|
||||
void extLogger.log("Initializing query server client.");
|
||||
const qs = await createQueryServer(qlConfigurationListener, cliServer, ctx);
|
||||
|
||||
for (const glob of PACK_GLOBS) {
|
||||
@@ -575,14 +578,14 @@ async function activateWithInstalledDistribution(
|
||||
});
|
||||
}
|
||||
|
||||
void logger.log("Initializing database manager.");
|
||||
const dbm = new DatabaseManager(ctx, qs, cliServer, logger);
|
||||
void extLogger.log("Initializing database manager.");
|
||||
const dbm = new DatabaseManager(ctx, qs, cliServer, extLogger);
|
||||
|
||||
// Let this run async.
|
||||
void dbm.loadPersistedState();
|
||||
|
||||
ctx.subscriptions.push(dbm);
|
||||
void logger.log("Initializing database panel.");
|
||||
void extLogger.log("Initializing database panel.");
|
||||
const databaseUI = new DatabaseUI(
|
||||
dbm,
|
||||
qs,
|
||||
@@ -593,22 +596,22 @@ async function activateWithInstalledDistribution(
|
||||
databaseUI.init();
|
||||
ctx.subscriptions.push(databaseUI);
|
||||
|
||||
void logger.log("Initializing evaluator log viewer.");
|
||||
void extLogger.log("Initializing evaluator log viewer.");
|
||||
const evalLogViewer = new EvalLogViewer();
|
||||
ctx.subscriptions.push(evalLogViewer);
|
||||
|
||||
void logger.log("Initializing query history manager.");
|
||||
void extLogger.log("Initializing query history manager.");
|
||||
const queryHistoryConfigurationListener = new QueryHistoryConfigListener();
|
||||
ctx.subscriptions.push(queryHistoryConfigurationListener);
|
||||
const showResults = async (item: CompletedLocalQueryInfo) =>
|
||||
showResultsForCompletedQuery(item, WebviewReveal.Forced);
|
||||
const queryStorageDir = path.join(ctx.globalStorageUri.fsPath, "queries");
|
||||
await fs.ensureDir(queryStorageDir);
|
||||
const queryStorageDir = join(ctx.globalStorageUri.fsPath, "queries");
|
||||
await ensureDir(queryStorageDir);
|
||||
const labelProvider = new HistoryItemLabelProvider(
|
||||
queryHistoryConfigurationListener,
|
||||
);
|
||||
|
||||
void logger.log("Initializing results panel interface.");
|
||||
void extLogger.log("Initializing results panel interface.");
|
||||
const localQueryResultsView = new ResultsView(
|
||||
ctx,
|
||||
dbm,
|
||||
@@ -618,21 +621,28 @@ async function activateWithInstalledDistribution(
|
||||
);
|
||||
ctx.subscriptions.push(localQueryResultsView);
|
||||
|
||||
void logger.log("Initializing variant analysis manager.");
|
||||
const variantAnalysisStorageDir = path.join(
|
||||
void extLogger.log("Initializing variant analysis manager.");
|
||||
|
||||
const app = new ExtensionApp(ctx);
|
||||
|
||||
const dbModule = await DbModule.initialize(app);
|
||||
|
||||
const variantAnalysisStorageDir = join(
|
||||
ctx.globalStorageUri.fsPath,
|
||||
"variant-analyses",
|
||||
);
|
||||
await fs.ensureDir(variantAnalysisStorageDir);
|
||||
await ensureDir(variantAnalysisStorageDir);
|
||||
const variantAnalysisResultsManager = new VariantAnalysisResultsManager(
|
||||
cliServer,
|
||||
logger,
|
||||
extLogger,
|
||||
);
|
||||
|
||||
const variantAnalysisManager = new VariantAnalysisManager(
|
||||
ctx,
|
||||
cliServer,
|
||||
variantAnalysisStorageDir,
|
||||
variantAnalysisResultsManager,
|
||||
dbModule?.dbManager, // the dbModule is only needed when the newQueryRunExperience is enabled
|
||||
);
|
||||
ctx.subscriptions.push(variantAnalysisManager);
|
||||
ctx.subscriptions.push(variantAnalysisResultsManager);
|
||||
@@ -643,11 +653,16 @@ async function activateWithInstalledDistribution(
|
||||
),
|
||||
);
|
||||
|
||||
void logger.log("Initializing remote queries manager.");
|
||||
const rqm = new RemoteQueriesManager(ctx, cliServer, queryStorageDir, logger);
|
||||
void extLogger.log("Initializing remote queries manager.");
|
||||
const rqm = new RemoteQueriesManager(
|
||||
ctx,
|
||||
cliServer,
|
||||
queryStorageDir,
|
||||
extLogger,
|
||||
);
|
||||
ctx.subscriptions.push(rqm);
|
||||
|
||||
void logger.log("Initializing query history.");
|
||||
void extLogger.log("Initializing query history.");
|
||||
const qhm = new QueryHistoryManager(
|
||||
qs,
|
||||
dbm,
|
||||
@@ -665,7 +680,7 @@ async function activateWithInstalledDistribution(
|
||||
|
||||
ctx.subscriptions.push(qhm);
|
||||
|
||||
void logger.log("Initializing evaluation log scanners.");
|
||||
void extLogger.log("Initializing evaluation log scanners.");
|
||||
const logScannerService = new LogScannerService(qhm);
|
||||
ctx.subscriptions.push(logScannerService);
|
||||
ctx.subscriptions.push(
|
||||
@@ -674,7 +689,7 @@ async function activateWithInstalledDistribution(
|
||||
),
|
||||
);
|
||||
|
||||
void logger.log("Initializing compare view.");
|
||||
void extLogger.log("Initializing compare view.");
|
||||
const compareView = new CompareView(
|
||||
ctx,
|
||||
dbm,
|
||||
@@ -685,8 +700,8 @@ async function activateWithInstalledDistribution(
|
||||
);
|
||||
ctx.subscriptions.push(compareView);
|
||||
|
||||
void logger.log("Initializing source archive filesystem provider.");
|
||||
archiveFilesystemProvider.activate(ctx);
|
||||
void extLogger.log("Initializing source archive filesystem provider.");
|
||||
archiveFilesystemProvider_activate(ctx);
|
||||
|
||||
async function showResultsForComparison(
|
||||
from: CompletedLocalQueryInfo,
|
||||
@@ -767,7 +782,7 @@ async function activateWithInstalledDistribution(
|
||||
}
|
||||
}
|
||||
|
||||
const qhelpTmpDir = tmp.dirSync({
|
||||
const qhelpTmpDir = dirSync({
|
||||
prefix: "qhelp_",
|
||||
keep: false,
|
||||
unsafeCleanup: true,
|
||||
@@ -781,8 +796,8 @@ async function activateWithInstalledDistribution(
|
||||
: window.activeTextEditor?.document.uri.fsPath;
|
||||
if (pathToQhelp) {
|
||||
// Create temporary directory
|
||||
const relativePathToMd = path.basename(pathToQhelp, ".qhelp") + ".md";
|
||||
const absolutePathToMd = path.join(qhelpTmpDir.name, relativePathToMd);
|
||||
const relativePathToMd = `${basename(pathToQhelp, ".qhelp")}.md`;
|
||||
const absolutePathToMd = join(qhelpTmpDir.name, relativePathToMd);
|
||||
const uri = Uri.file(absolutePathToMd);
|
||||
try {
|
||||
await cliServer.generateQueryHelp(pathToQhelp, absolutePathToMd);
|
||||
@@ -821,7 +836,7 @@ async function activateWithInstalledDistribution(
|
||||
|
||||
ctx.subscriptions.push(tmpDirDisposal);
|
||||
|
||||
void logger.log("Initializing CodeQL language server.");
|
||||
void extLogger.log("Initializing CodeQL language server.");
|
||||
const client = new LanguageClient(
|
||||
"CodeQL Language Server",
|
||||
() => spawnIdeServer(qlConfigurationListener),
|
||||
@@ -839,7 +854,7 @@ async function activateWithInstalledDistribution(
|
||||
true,
|
||||
);
|
||||
|
||||
void logger.log("Initializing QLTest interface.");
|
||||
void extLogger.log("Initializing QLTest interface.");
|
||||
const testExplorerExtension = extensions.getExtension<TestHub>(
|
||||
testExplorerExtensionId,
|
||||
);
|
||||
@@ -856,7 +871,7 @@ async function activateWithInstalledDistribution(
|
||||
ctx.subscriptions.push(testUIService);
|
||||
}
|
||||
|
||||
void logger.log("Registering top-level command palette commands.");
|
||||
void extLogger.log("Registering top-level command palette commands.");
|
||||
ctx.subscriptions.push(
|
||||
commandRunnerWithProgress(
|
||||
"codeQL.runQuery",
|
||||
@@ -938,7 +953,7 @@ async function activateWithInstalledDistribution(
|
||||
}
|
||||
}
|
||||
if (skippedDatabases.length > 0) {
|
||||
void logger.log(`Errors:\n${errors.join("\n")}`);
|
||||
void extLogger.log(`Errors:\n${errors.join("\n")}`);
|
||||
void showAndLogWarningMessage(
|
||||
`The following databases were skipped:\n${skippedDatabases.join(
|
||||
"\n",
|
||||
@@ -976,9 +991,7 @@ async function activateWithInstalledDistribution(
|
||||
// warn user and display selected files when a directory is selected because some ql
|
||||
// files may be hidden from the user.
|
||||
if (dirFound) {
|
||||
const fileString = files
|
||||
.map((file) => path.basename(file))
|
||||
.join(", ");
|
||||
const fileString = files.map((file) => basename(file)).join(", ");
|
||||
const res = await showBinaryChoiceDialog(
|
||||
`You are about to run ${files.length} queries: ${fileString} Do you want to continue?`,
|
||||
);
|
||||
@@ -1229,9 +1242,11 @@ async function activateWithInstalledDistribution(
|
||||
);
|
||||
|
||||
ctx.subscriptions.push(
|
||||
commandRunner(
|
||||
commandRunnerWithProgress(
|
||||
"codeQL.exportVariantAnalysisResults",
|
||||
async (
|
||||
progress: ProgressCallback,
|
||||
token: CancellationToken,
|
||||
variantAnalysisId: number,
|
||||
filterSort?: RepositoriesFilterSortStateWithIds,
|
||||
) => {
|
||||
@@ -1240,8 +1255,14 @@ async function activateWithInstalledDistribution(
|
||||
variantAnalysisManager,
|
||||
variantAnalysisId,
|
||||
filterSort,
|
||||
progress,
|
||||
token,
|
||||
);
|
||||
},
|
||||
{
|
||||
title: "Exporting variant analysis results",
|
||||
cancellable: true,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1267,6 +1288,24 @@ async function activateWithInstalledDistribution(
|
||||
),
|
||||
);
|
||||
|
||||
ctx.subscriptions.push(
|
||||
commandRunner(
|
||||
"codeQL.openVariantAnalysisQueryText",
|
||||
async (variantAnalysisId: number) => {
|
||||
await variantAnalysisManager.openQueryText(variantAnalysisId);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
ctx.subscriptions.push(
|
||||
commandRunner(
|
||||
"codeQL.openVariantAnalysisQueryFile",
|
||||
async (variantAnalysisId: number) => {
|
||||
await variantAnalysisManager.openQueryFile(variantAnalysisId);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
ctx.subscriptions.push(
|
||||
commandRunner("codeQL.openReferencedFile", openReferencedFile),
|
||||
);
|
||||
@@ -1362,7 +1401,7 @@ async function activateWithInstalledDistribution(
|
||||
commandRunner("codeQL.copyVersion", async () => {
|
||||
const text = `CodeQL extension version: ${
|
||||
extension?.packageJSON.version
|
||||
} \nCodeQL CLI version: ${await getCliVersion()} \nPlatform: ${os.platform()} ${os.arch()}`;
|
||||
} \nCodeQL CLI version: ${await getCliVersion()} \nPlatform: ${platform()} ${arch()}`;
|
||||
await env.clipboard.writeText(text);
|
||||
void showAndLogInformationMessage(text);
|
||||
}),
|
||||
@@ -1415,27 +1454,31 @@ async function activateWithInstalledDistribution(
|
||||
|
||||
ctx.subscriptions.push(
|
||||
commandRunner("codeQL.showLogs", async () => {
|
||||
logger.show();
|
||||
extLogger.show();
|
||||
}),
|
||||
);
|
||||
|
||||
ctx.subscriptions.push(new SummaryLanguageSupport());
|
||||
|
||||
void logger.log("Starting language server.");
|
||||
ctx.subscriptions.push(client.start());
|
||||
|
||||
void extLogger.log("Starting language server.");
|
||||
await client.start();
|
||||
ctx.subscriptions.push({
|
||||
dispose: () => {
|
||||
void client.stop();
|
||||
},
|
||||
});
|
||||
// Jump-to-definition and find-references
|
||||
void logger.log("Registering jump-to-definition handlers.");
|
||||
void extLogger.log("Registering jump-to-definition handlers.");
|
||||
|
||||
// Store contextual queries in a temporary folder so that they are removed
|
||||
// when the application closes. There is no need for the user to interact with them.
|
||||
const contextualQueryStorageDir = path.join(
|
||||
const contextualQueryStorageDir = join(
|
||||
tmpDir.name,
|
||||
"contextual-query-storage",
|
||||
);
|
||||
await fs.ensureDir(contextualQueryStorageDir);
|
||||
await ensureDir(contextualQueryStorageDir);
|
||||
languages.registerDefinitionProvider(
|
||||
{ scheme: archiveFilesystemProvider.zipArchiveScheme },
|
||||
{ scheme: zipArchiveScheme },
|
||||
new TemplateQueryDefinitionProvider(
|
||||
cliServer,
|
||||
qs,
|
||||
@@ -1445,7 +1488,7 @@ async function activateWithInstalledDistribution(
|
||||
);
|
||||
|
||||
languages.registerReferenceProvider(
|
||||
{ scheme: archiveFilesystemProvider.zipArchiveScheme },
|
||||
{ scheme: zipArchiveScheme },
|
||||
new TemplateQueryReferenceProvider(
|
||||
cliServer,
|
||||
qs,
|
||||
@@ -1541,14 +1584,10 @@ async function activateWithInstalledDistribution(
|
||||
|
||||
await commands.executeCommand("codeQLDatabases.removeOrphanedDatabases");
|
||||
|
||||
void logger.log("Reading query history");
|
||||
void extLogger.log("Reading query history");
|
||||
await qhm.readQueryHistory();
|
||||
|
||||
const app = new ExtensionApp(ctx);
|
||||
const dbModule = await initializeDbModule(app);
|
||||
ctx.subscriptions.push(dbModule);
|
||||
|
||||
void logger.log("Successfully finished extension initialization.");
|
||||
void extLogger.log("Successfully finished extension initialization.");
|
||||
|
||||
return {
|
||||
ctx,
|
||||
@@ -1584,7 +1623,7 @@ async function createQueryServer(
|
||||
task,
|
||||
);
|
||||
if (await cliServer.cliConstraints.supportsNewQueryServer()) {
|
||||
const qs = new newQueryServer.QueryServerClient(
|
||||
const qs = new QueryServerClient(
|
||||
qlConfigurationListener,
|
||||
cliServer,
|
||||
qsOpts,
|
||||
@@ -1594,7 +1633,7 @@ async function createQueryServer(
|
||||
await qs.startQueryServer();
|
||||
return new NewQueryRunner(qs);
|
||||
} else {
|
||||
const qs = new legacyQueryServer.QueryServerClient(
|
||||
const qs = new LegacyQueryServerClient(
|
||||
qlConfigurationListener,
|
||||
cliServer,
|
||||
qsOpts,
|
||||
@@ -1611,7 +1650,7 @@ function getContextStoragePath(ctx: ExtensionContext) {
|
||||
}
|
||||
|
||||
async function initializeLogging(ctx: ExtensionContext): Promise<void> {
|
||||
ctx.subscriptions.push(logger);
|
||||
ctx.subscriptions.push(extLogger);
|
||||
ctx.subscriptions.push(queryServerLogger);
|
||||
ctx.subscriptions.push(ideServerLogger);
|
||||
}
|
||||
@@ -1654,8 +1693,8 @@ async function assertVSCodeVersionGreaterThan(
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const parsedVersion = semver.parse(vscodeVersion);
|
||||
const parsedMinVersion = semver.parse(minVersion);
|
||||
const parsedVersion = parse(vscodeVersion);
|
||||
const parsedMinVersion = parse(minVersion);
|
||||
if (!parsedVersion || !parsedMinVersion) {
|
||||
void showAndLogWarningMessage(
|
||||
`Could not do a version check of vscode because could not parse version number: actual vscode version ${vscodeVersion} or minimum supported vscode version ${minVersion}.`,
|
||||
@@ -1663,7 +1702,7 @@ async function assertVSCodeVersionGreaterThan(
|
||||
return;
|
||||
}
|
||||
|
||||
if (semver.lt(parsedVersion, parsedMinVersion)) {
|
||||
if (lt(parsedVersion, parsedMinVersion)) {
|
||||
const message = `The CodeQL extension requires VS Code version ${minVersion} or later. Current version is ${vscodeVersion}. Please update VS Code to get the latest features of CodeQL.`;
|
||||
const result = await showBinaryChoiceDialog(
|
||||
message,
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
import * as fs from "fs-extra";
|
||||
import {
|
||||
ensureDirSync,
|
||||
readFile,
|
||||
pathExists,
|
||||
ensureDir,
|
||||
writeFile,
|
||||
opendir,
|
||||
} from "fs-extra";
|
||||
import * as glob from "glob-promise";
|
||||
import * as yaml from "js-yaml";
|
||||
import * as path from "path";
|
||||
import * as tmp from "tmp-promise";
|
||||
import { load } from "js-yaml";
|
||||
import { join, basename } from "path";
|
||||
import { dirSync } from "tmp-promise";
|
||||
import {
|
||||
ExtensionContext,
|
||||
Uri,
|
||||
@@ -12,24 +19,24 @@ import {
|
||||
} from "vscode";
|
||||
import { CodeQLCliServer, QlpacksInfo } from "./cli";
|
||||
import { UserCancellationException } from "./commandRunner";
|
||||
import { logger } from "./logging";
|
||||
import { extLogger } from "./common";
|
||||
import { QueryMetadata } from "./pure/interface-types";
|
||||
|
||||
// Shared temporary folder for the extension.
|
||||
export const tmpDir = tmp.dirSync({
|
||||
export const tmpDir = dirSync({
|
||||
prefix: "queries_",
|
||||
keep: false,
|
||||
unsafeCleanup: true,
|
||||
});
|
||||
export const upgradesTmpDir = path.join(tmpDir.name, "upgrades");
|
||||
fs.ensureDirSync(upgradesTmpDir);
|
||||
export const upgradesTmpDir = join(tmpDir.name, "upgrades");
|
||||
ensureDirSync(upgradesTmpDir);
|
||||
|
||||
export const tmpDirDisposal = {
|
||||
dispose: () => {
|
||||
try {
|
||||
tmpDir.removeCallback();
|
||||
} catch (e) {
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
`Failed to remove temporary directory ${tmpDir.name}: ${e}`,
|
||||
);
|
||||
}
|
||||
@@ -51,7 +58,7 @@ export const tmpDirDisposal = {
|
||||
export async function showAndLogErrorMessage(
|
||||
message: string,
|
||||
{
|
||||
outputLogger = logger,
|
||||
outputLogger = extLogger,
|
||||
items = [] as string[],
|
||||
fullMessage = undefined as string | undefined,
|
||||
} = {},
|
||||
@@ -80,7 +87,7 @@ function dropLinesExceptInitial(message: string, n = 2) {
|
||||
*/
|
||||
export async function showAndLogWarningMessage(
|
||||
message: string,
|
||||
{ outputLogger = logger, items = [] as string[] } = {},
|
||||
{ outputLogger = extLogger, items = [] as string[] } = {},
|
||||
): Promise<string | undefined> {
|
||||
return internalShowAndLog(
|
||||
message,
|
||||
@@ -100,7 +107,7 @@ export async function showAndLogWarningMessage(
|
||||
*/
|
||||
export async function showAndLogInformationMessage(
|
||||
message: string,
|
||||
{ outputLogger = logger, items = [] as string[], fullMessage = "" } = {},
|
||||
{ outputLogger = extLogger, items = [] as string[], fullMessage = "" } = {},
|
||||
): Promise<string | undefined> {
|
||||
return internalShowAndLog(
|
||||
message,
|
||||
@@ -119,7 +126,7 @@ type ShowMessageFn = (
|
||||
async function internalShowAndLog(
|
||||
message: string,
|
||||
items: string[],
|
||||
outputLogger = logger,
|
||||
outputLogger = extLogger,
|
||||
fn: ShowMessageFn,
|
||||
fullMessage?: string,
|
||||
): Promise<string | undefined> {
|
||||
@@ -360,12 +367,12 @@ async function findDbschemePack(
|
||||
): Promise<{ name: string; isLibraryPack: boolean }> {
|
||||
for (const { packDir, packName } of packs) {
|
||||
if (packDir !== undefined) {
|
||||
const qlpack = yaml.load(
|
||||
await fs.readFile(path.join(packDir, "qlpack.yml"), "utf8"),
|
||||
const qlpack = load(
|
||||
await readFile(join(packDir, "qlpack.yml"), "utf8"),
|
||||
) as { dbscheme?: string; library?: boolean };
|
||||
if (
|
||||
qlpack.dbscheme !== undefined &&
|
||||
path.basename(qlpack.dbscheme) === path.basename(dbschemePath)
|
||||
basename(qlpack.dbscheme) === basename(dbschemePath)
|
||||
) {
|
||||
return {
|
||||
name: packName,
|
||||
@@ -402,13 +409,13 @@ export async function getQlPackForDbscheme(
|
||||
const packs: QlPackWithPath[] = Object.entries(qlpacks).map(
|
||||
([packName, dirs]) => {
|
||||
if (dirs.length < 1) {
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
`In getQlPackFor ${dbschemePath}, qlpack ${packName} has no directories`,
|
||||
);
|
||||
return { packName, packDir: undefined };
|
||||
}
|
||||
if (dirs.length > 1) {
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
`In getQlPackFor ${dbschemePath}, qlpack ${packName} has more than one directory; arbitrarily choosing the first`,
|
||||
);
|
||||
}
|
||||
@@ -432,7 +439,7 @@ export async function getQlPackForDbscheme(
|
||||
export async function getPrimaryDbscheme(
|
||||
datasetFolder: string,
|
||||
): Promise<string> {
|
||||
const dbschemes = await glob(path.join(datasetFolder, "*.dbscheme"));
|
||||
const dbschemes = await glob(join(datasetFolder, "*.dbscheme"));
|
||||
|
||||
if (dbschemes.length < 1) {
|
||||
throw new Error(
|
||||
@@ -460,7 +467,7 @@ export class CachedOperation<U> {
|
||||
private readonly lru: string[];
|
||||
private readonly inProgressCallbacks: Map<
|
||||
string,
|
||||
[(u: U) => void, (reason?: any) => void][]
|
||||
Array<[(u: U) => void, (reason?: any) => void]>
|
||||
>;
|
||||
|
||||
constructor(
|
||||
@@ -471,7 +478,7 @@ export class CachedOperation<U> {
|
||||
this.lru = [];
|
||||
this.inProgressCallbacks = new Map<
|
||||
string,
|
||||
[(u: U) => void, (reason?: any) => void][]
|
||||
Array<[(u: U) => void, (reason?: any) => void]>
|
||||
>();
|
||||
this.cached = new Map<string, U>();
|
||||
}
|
||||
@@ -499,7 +506,7 @@ export class CachedOperation<U> {
|
||||
}
|
||||
|
||||
// Otherwise compute the new value, but leave a callback to allow sharing work
|
||||
const callbacks: [(u: U) => void, (reason?: any) => void][] = [];
|
||||
const callbacks: Array<[(u: U) => void, (reason?: any) => void]> = [];
|
||||
this.inProgressCallbacks.set(t, callbacks);
|
||||
try {
|
||||
const result = await this.operation(t, ...args);
|
||||
@@ -544,6 +551,7 @@ export const dbSchemeToLanguage = {
|
||||
"semmlecode.csharp.dbscheme": "csharp",
|
||||
"go.dbscheme": "go",
|
||||
"ruby.dbscheme": "ruby",
|
||||
"swift.dbscheme": "swift",
|
||||
};
|
||||
|
||||
export const languageToDbScheme = Object.entries(dbSchemeToLanguage).reduce(
|
||||
@@ -568,9 +576,7 @@ export const languageToDbScheme = Object.entries(dbSchemeToLanguage).reduce(
|
||||
*/
|
||||
export function getInitialQueryContents(language: string, dbscheme: string) {
|
||||
if (!language) {
|
||||
const dbschemeBase = path.basename(
|
||||
dbscheme,
|
||||
) as keyof typeof dbSchemeToLanguage;
|
||||
const dbschemeBase = basename(dbscheme) as keyof typeof dbSchemeToLanguage;
|
||||
language = dbSchemeToLanguage[dbschemeBase];
|
||||
}
|
||||
|
||||
@@ -586,8 +592,8 @@ export function getInitialQueryContents(language: string, dbscheme: string) {
|
||||
export async function isLikelyDatabaseRoot(maybeRoot: string) {
|
||||
const [a, b, c] = await Promise.all([
|
||||
// databases can have either .dbinfo or codeql-database.yml.
|
||||
fs.pathExists(path.join(maybeRoot, ".dbinfo")),
|
||||
fs.pathExists(path.join(maybeRoot, "codeql-database.yml")),
|
||||
pathExists(join(maybeRoot, ".dbinfo")),
|
||||
pathExists(join(maybeRoot, "codeql-database.yml")),
|
||||
|
||||
// they *must* have a db-{language} folder
|
||||
glob("db-*/", { cwd: maybeRoot }),
|
||||
@@ -601,8 +607,7 @@ export async function isLikelyDatabaseRoot(maybeRoot: string) {
|
||||
*/
|
||||
export async function isLikelyDbLanguageFolder(dbPath: string) {
|
||||
return (
|
||||
path.basename(dbPath).startsWith("db-") &&
|
||||
!(await isLikelyDatabaseRoot(dbPath))
|
||||
basename(dbPath).startsWith("db-") && !(await isLikelyDatabaseRoot(dbPath))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -622,10 +627,10 @@ export async function findLanguage(
|
||||
uri,
|
||||
);
|
||||
const language = Object.keys(queryInfo.byLanguage)[0];
|
||||
void logger.log(`Detected query language: ${language}`);
|
||||
void extLogger.log(`Detected query language: ${language}`);
|
||||
return language;
|
||||
} catch (e) {
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
"Could not autodetect query language. Select language manually.",
|
||||
);
|
||||
}
|
||||
@@ -673,7 +678,7 @@ export async function tryGetQueryMetadata(
|
||||
return await cliServer.resolveMetadata(queryPath);
|
||||
} catch (e) {
|
||||
// Ignore errors and provide no metadata.
|
||||
void logger.log(`Couldn't resolve metadata for ${queryPath}: ${e}`);
|
||||
void extLogger.log(`Couldn't resolve metadata for ${queryPath}: ${e}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -686,9 +691,9 @@ export async function tryGetQueryMetadata(
|
||||
* It does not need to exist.
|
||||
*/
|
||||
export async function createTimestampFile(storagePath: string) {
|
||||
const timestampPath = path.join(storagePath, "timestamp");
|
||||
await fs.ensureDir(storagePath);
|
||||
await fs.writeFile(timestampPath, Date.now().toString(), "utf8");
|
||||
const timestampPath = join(storagePath, "timestamp");
|
||||
await ensureDir(storagePath);
|
||||
await writeFile(timestampPath, Date.now().toString(), "utf8");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -703,8 +708,8 @@ export async function* walkDirectory(
|
||||
dir: string,
|
||||
): AsyncIterableIterator<string> {
|
||||
const seenFiles = new Set<string>();
|
||||
for await (const d of await fs.opendir(dir)) {
|
||||
const entry = path.join(dir, d.name);
|
||||
for await (const d of await opendir(dir)) {
|
||||
const entry = join(dir, d.name);
|
||||
seenFiles.add(entry);
|
||||
if (d.isDirectory()) {
|
||||
yield* walkDirectory(entry);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { env } from "vscode";
|
||||
import * as path from "path";
|
||||
import { basename } from "path";
|
||||
import { QueryHistoryConfig } from "./config";
|
||||
import { LocalQueryInfo } from "./query-results";
|
||||
import {
|
||||
@@ -106,7 +106,7 @@ export class HistoryItemLabelProvider {
|
||||
d: buildRepoLabel(item),
|
||||
r: resultCount,
|
||||
s: humanizeQueryStatus(item.status),
|
||||
f: path.basename(item.remoteQuery.queryFilePath),
|
||||
f: basename(item.remoteQuery.queryFilePath),
|
||||
"%": "%",
|
||||
};
|
||||
}
|
||||
@@ -125,7 +125,7 @@ export class HistoryItemLabelProvider {
|
||||
d: buildRepoLabel(item),
|
||||
r: resultCount,
|
||||
s: humanizeQueryStatus(item.status),
|
||||
f: path.basename(item.variantAnalysis.query.filePath),
|
||||
f: basename(item.variantAnalysis.query.filePath),
|
||||
"%": "%",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ProgressLocation, window } from "vscode";
|
||||
import { StreamInfo } from "vscode-languageclient";
|
||||
import * as cli from "./cli";
|
||||
import { StreamInfo } from "vscode-languageclient/node";
|
||||
import { shouldDebugIdeServer, spawnServer } from "./cli";
|
||||
import { QueryServerConfig } from "./config";
|
||||
import { ideServerLogger } from "./logging";
|
||||
import { ideServerLogger } from "./common";
|
||||
|
||||
/**
|
||||
* Managing the language server for CodeQL.
|
||||
@@ -16,12 +16,12 @@ export async function spawnIdeServer(
|
||||
{ title: "CodeQL language server", location: ProgressLocation.Window },
|
||||
async (progressReporter, _) => {
|
||||
const args = ["--check-errors", "ON_CHANGE"];
|
||||
if (cli.shouldDebugIdeServer()) {
|
||||
if (shouldDebugIdeServer()) {
|
||||
args.push(
|
||||
"-J=-agentlib:jdwp=transport=dt_socket,address=localhost:9009,server=y,suspend=n,quiet=y",
|
||||
);
|
||||
}
|
||||
const child = cli.spawnServer(
|
||||
const child = spawnServer(
|
||||
config.codeQlPath,
|
||||
"CodeQL language server",
|
||||
["execute", "language-server"],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as crypto from "crypto";
|
||||
import * as os from "os";
|
||||
import { randomBytes } from "crypto";
|
||||
import { EOL } from "os";
|
||||
import {
|
||||
Uri,
|
||||
Location,
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
import { tryGetResolvableLocation, isLineColumnLoc } from "./pure/bqrs-utils";
|
||||
import { DatabaseItem, DatabaseManager } from "./databases";
|
||||
import { ViewSourceFileMsg } from "./pure/interface-types";
|
||||
import { Logger } from "./logging";
|
||||
import { Logger } from "./common";
|
||||
import {
|
||||
LineColumnLocation,
|
||||
WholeFileLocation,
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
|
||||
/** Gets a nonce string created with 128 bits of entropy. */
|
||||
export function getNonce(): string {
|
||||
return crypto.randomBytes(16).toString("base64");
|
||||
return randomBytes(16).toString("base64");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,7 +175,7 @@ export function getHtmlForWebview(
|
||||
content="default-src 'none'; script-src 'nonce-${nonce}'; font-src ${fontSrc}; style-src ${styleSrc}; connect-src ${
|
||||
webview.cspSource
|
||||
};">
|
||||
${stylesheetsHtmlLines.join(` ${os.EOL}`)}
|
||||
${stylesheetsHtmlLines.join(` ${EOL}`)}
|
||||
</head>
|
||||
<body>
|
||||
<div id=root data-view="${view}">
|
||||
|
||||
@@ -33,13 +33,16 @@ import {
|
||||
GRAPH_TABLE_NAME,
|
||||
RawResultsSortState,
|
||||
NavigationDirection,
|
||||
getDefaultResultSetName,
|
||||
ParsedResultSets,
|
||||
} from "./pure/interface-types";
|
||||
import { Logger } from "./logging";
|
||||
import { Logger } from "./common";
|
||||
import { commandRunner } from "./commandRunner";
|
||||
import {
|
||||
CompletedQueryInfo,
|
||||
interpretResultsSarif,
|
||||
interpretGraphResults,
|
||||
CompletedLocalQueryInfo,
|
||||
} from "./query-results";
|
||||
import { QueryEvaluationInfo } from "./run-queries-shared";
|
||||
import {
|
||||
@@ -54,10 +57,6 @@ import {
|
||||
shownLocationLineDecoration,
|
||||
jumpToLocation,
|
||||
} from "./interface-utils";
|
||||
import {
|
||||
getDefaultResultSetName,
|
||||
ParsedResultSets,
|
||||
} from "./pure/interface-types";
|
||||
import {
|
||||
RawResultSet,
|
||||
transformBqrsResultSet,
|
||||
@@ -65,7 +64,6 @@ import {
|
||||
} from "./pure/bqrs-cli-types";
|
||||
import { AbstractWebview, WebviewPanelConfig } from "./abstract-webview";
|
||||
import { PAGE_SIZE } from "./config";
|
||||
import { CompletedLocalQueryInfo } from "./query-results";
|
||||
import { HistoryItemLabelProvider } from "./history-item-label-provider";
|
||||
|
||||
/**
|
||||
@@ -609,7 +607,7 @@ export class ResultsView extends AbstractWebview<
|
||||
resultSet: { t: "RawResultSet", ...resultSet },
|
||||
numPages: numPagesOfResultSet(resultSet),
|
||||
numInterpretedPages: numInterpretedPages(this._interpretation),
|
||||
selectedTable: selectedTable,
|
||||
selectedTable,
|
||||
resultSetNames,
|
||||
};
|
||||
|
||||
@@ -825,7 +823,7 @@ export class ResultsView extends AbstractWebview<
|
||||
return;
|
||||
}
|
||||
|
||||
const diagnostics: [Uri, ReadonlyArray<Diagnostic>][] = [];
|
||||
const diagnostics: Array<[Uri, readonly Diagnostic[]]> = [];
|
||||
|
||||
for (const result of data.runs[0].results) {
|
||||
const message = result.message.text;
|
||||
@@ -847,7 +845,7 @@ export class ResultsView extends AbstractWebview<
|
||||
}
|
||||
const resultLocation = tryResolveLocation(sarifLoc, databaseItem);
|
||||
if (!resultLocation) {
|
||||
void this.logger.log("Sarif location was not resolvable " + sarifLoc);
|
||||
void this.logger.log(`Sarif location was not resolvable ${sarifLoc}`);
|
||||
continue;
|
||||
}
|
||||
const parsedMessage = parseSarifPlainTextMessage(message);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Logger } from "./logging";
|
||||
import { Logger } from "./common";
|
||||
import * as cp from "child_process";
|
||||
import { Disposable } from "vscode";
|
||||
import { MessageConnection } from "vscode-jsonrpc";
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import * as path from "path";
|
||||
import * as fs from "fs-extra";
|
||||
import { dirname } from "path";
|
||||
import { ensureFile } from "fs-extra";
|
||||
|
||||
import { DisposableObject } from "../pure/disposable-object";
|
||||
import { CancellationToken, commands } from "vscode";
|
||||
import { createMessageConnection, RequestType } from "vscode-jsonrpc";
|
||||
import { createMessageConnection, RequestType } from "vscode-jsonrpc/node";
|
||||
import * as cli from "../cli";
|
||||
import { QueryServerConfig } from "../config";
|
||||
import { Logger, ProgressReporter } from "../logging";
|
||||
import { Logger, ProgressReporter } from "../common";
|
||||
import {
|
||||
completeQuery,
|
||||
EvaluationResult,
|
||||
progress,
|
||||
ProgressMessage,
|
||||
WithProgressId,
|
||||
compileQuery,
|
||||
} from "../pure/legacy-messages";
|
||||
import * as messages from "../pure/legacy-messages";
|
||||
import { ProgressCallback, ProgressTask } from "../commandRunner";
|
||||
import { findQueryLogFile } from "../run-queries-shared";
|
||||
import { ServerProcess } from "../json-rpc-server";
|
||||
@@ -47,7 +47,7 @@ export class QueryServerClient extends DisposableObject {
|
||||
nextProgress: number;
|
||||
withProgressReporting: WithProgressReporting;
|
||||
|
||||
private readonly queryServerStartListeners = [] as ProgressTask<void>[];
|
||||
private readonly queryServerStartListeners = [] as Array<ProgressTask<void>>;
|
||||
|
||||
// Can't use standard vscode EventEmitter here since they do not cause the calling
|
||||
// function to fail if one of the event handlers fail. This is something that
|
||||
@@ -154,7 +154,7 @@ export class QueryServerClient extends DisposableObject {
|
||||
|
||||
if (await this.cliServer.cliConstraints.supportsStructuredEvalLog()) {
|
||||
const structuredLogFile = `${this.opts.contextStoragePath}/structured-evaluator-log.json`;
|
||||
await fs.ensureFile(structuredLogFile);
|
||||
await ensureFile(structuredLogFile);
|
||||
|
||||
args.push("--evaluator-log");
|
||||
args.push(structuredLogFile);
|
||||
@@ -237,8 +237,8 @@ export class QueryServerClient extends DisposableObject {
|
||||
return this.serverProcess!.child.pid || 0;
|
||||
}
|
||||
|
||||
async sendRequest<P, R, E, RO>(
|
||||
type: RequestType<WithProgressId<P>, R, E, RO>,
|
||||
async sendRequest<P, R, E>(
|
||||
type: RequestType<WithProgressId<P>, R, E>,
|
||||
parameter: P,
|
||||
token?: CancellationToken,
|
||||
progress?: (res: ProgressMessage) => void,
|
||||
@@ -270,10 +270,8 @@ export class QueryServerClient extends DisposableObject {
|
||||
* properly will require a change in the query server.
|
||||
*/
|
||||
private updateActiveQuery(method: string, parameter: any): void {
|
||||
if (method === messages.compileQuery.method) {
|
||||
this.activeQueryLogFile = findQueryLogFile(
|
||||
path.dirname(parameter.resultPath),
|
||||
);
|
||||
if (method === compileQuery.method) {
|
||||
this.activeQueryLogFile = findQueryLogFile(dirname(parameter.resultPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as crypto from "crypto";
|
||||
import * as fs from "fs-extra";
|
||||
import { createHash } from "crypto";
|
||||
import { readFile } from "fs-extra";
|
||||
import * as tmp from "tmp-promise";
|
||||
import * as path from "path";
|
||||
import { basename, join } from "path";
|
||||
import { CancellationToken, Uri } from "vscode";
|
||||
import { ErrorCodes, ResponseError } from "vscode-languageclient";
|
||||
import { LSPErrorCodes, ResponseError } from "vscode-languageclient";
|
||||
|
||||
import * as cli from "../cli";
|
||||
import { DatabaseItem } from "../databases";
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
} from "../helpers";
|
||||
import { ProgressCallback } from "../commandRunner";
|
||||
import { QueryMetadata } from "../pure/interface-types";
|
||||
import { logger } from "../logging";
|
||||
import { extLogger } from "../common";
|
||||
import * as messages from "../pure/legacy-messages";
|
||||
import { InitialQueryInfo, LocalQueryInfo } from "../query-results";
|
||||
import * as qsClient from "./queryserver-client";
|
||||
@@ -257,9 +257,8 @@ async function checkDbschemeCompatibility(
|
||||
false,
|
||||
);
|
||||
const hash = async function (filename: string): Promise<string> {
|
||||
return crypto
|
||||
.createHash("sha256")
|
||||
.update(await fs.readFile(filename))
|
||||
return createHash("sha256")
|
||||
.update(await readFile(filename))
|
||||
.digest("hex");
|
||||
};
|
||||
|
||||
@@ -379,14 +378,14 @@ export async function compileAndRunQueryAgainstDatabase(
|
||||
// database. (Queries that merely need the database to be upgraded
|
||||
// won't trigger this check)
|
||||
// This test will produce confusing results if we ever change the name of the database schema files.
|
||||
const querySchemaName = path.basename(packConfig.dbscheme);
|
||||
const dbSchemaName = path.basename(dbItem.contents.dbSchemeUri.fsPath);
|
||||
const querySchemaName = basename(packConfig.dbscheme);
|
||||
const dbSchemaName = basename(dbItem.contents.dbSchemeUri.fsPath);
|
||||
if (querySchemaName != dbSchemaName) {
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
`Query schema was ${querySchemaName}, but database schema was ${dbSchemaName}.`,
|
||||
);
|
||||
throw new Error(
|
||||
`The query ${path.basename(
|
||||
`The query ${basename(
|
||||
initialInfo.queryPath,
|
||||
)} cannot be run against the selected database (${
|
||||
dbItem.name
|
||||
@@ -411,7 +410,7 @@ export async function compileAndRunQueryAgainstDatabase(
|
||||
|
||||
let availableMlModels: cli.MlModelInfo[] = [];
|
||||
if (!(await cliServer.cliConstraints.supportsResolveMlModels())) {
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
"Resolving ML models is unsupported by this version of the CLI. Running the query without any ML models.",
|
||||
);
|
||||
} else {
|
||||
@@ -423,13 +422,13 @@ export async function compileAndRunQueryAgainstDatabase(
|
||||
)
|
||||
).models;
|
||||
if (availableMlModels.length) {
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
`Found available ML models at the following paths: ${availableMlModels
|
||||
.map((x) => `'${x.path}'`)
|
||||
.join(", ")}.`,
|
||||
);
|
||||
} else {
|
||||
void logger.log("Did not find any available ML models.");
|
||||
void extLogger.log("Did not find any available ML models.");
|
||||
}
|
||||
} catch (e) {
|
||||
const message =
|
||||
@@ -441,7 +440,7 @@ export async function compileAndRunQueryAgainstDatabase(
|
||||
|
||||
const hasMetadataFile = await dbItem.hasMetadataFile();
|
||||
const query = new QueryInProgress(
|
||||
path.join(queryStorageDir, initialInfo.id),
|
||||
join(queryStorageDir, initialInfo.id),
|
||||
dbItem.databaseUri.fsPath,
|
||||
hasMetadataFile,
|
||||
packConfig.dbscheme,
|
||||
@@ -480,7 +479,10 @@ export async function compileAndRunQueryAgainstDatabase(
|
||||
try {
|
||||
errors = await query.compile(qs, qlProgram, progress, token);
|
||||
} catch (e) {
|
||||
if (e instanceof ResponseError && e.code == ErrorCodes.RequestCancelled) {
|
||||
if (
|
||||
e instanceof ResponseError &&
|
||||
e.code == LSPErrorCodes.RequestCancelled
|
||||
) {
|
||||
return createSyntheticResult(query, "Query cancelled");
|
||||
} else {
|
||||
throw e;
|
||||
@@ -499,7 +501,7 @@ export async function compileAndRunQueryAgainstDatabase(
|
||||
);
|
||||
if (result.resultType !== messages.QueryResultType.SUCCESS) {
|
||||
const message = result.message || "Failed to run query";
|
||||
void logger.log(message);
|
||||
void extLogger.log(message);
|
||||
void showAndLogErrorMessage(message);
|
||||
}
|
||||
const message = formatLegacyMessage(result);
|
||||
@@ -539,8 +541,9 @@ export async function compileAndRunQueryAgainstDatabase(
|
||||
// and will be trimmed by the function displaying the error popup. Accordingly, we only
|
||||
// try to show the errors if there are 2 or less, otherwise we direct the user to the log.
|
||||
void showAndLogErrorMessage(
|
||||
"Quick evaluation compilation failed: " +
|
||||
formattedMessages.join("\n"),
|
||||
`Quick evaluation compilation failed: ${formattedMessages.join(
|
||||
"\n",
|
||||
)}`,
|
||||
);
|
||||
} else {
|
||||
void showAndLogErrorMessage(
|
||||
|
||||
@@ -5,11 +5,11 @@ import {
|
||||
tmpDir,
|
||||
} from "../helpers";
|
||||
import { ProgressCallback, UserCancellationException } from "../commandRunner";
|
||||
import { logger } from "../logging";
|
||||
import { extLogger } from "../common";
|
||||
import * as messages from "../pure/legacy-messages";
|
||||
import * as qsClient from "./queryserver-client";
|
||||
import * as tmp from "tmp-promise";
|
||||
import * as path from "path";
|
||||
import { dirname } from "path";
|
||||
import { DatabaseItem } from "../databases";
|
||||
|
||||
/**
|
||||
@@ -69,7 +69,7 @@ async function compileDatabaseUpgrade(
|
||||
// We have the upgrades we want but compileUpgrade
|
||||
// requires searching for them. So we use the parent directories of the upgrades
|
||||
// as the upgrade path.
|
||||
const parentDirs = resolvedSequence.map((dir) => path.dirname(dir));
|
||||
const parentDirs = resolvedSequence.map((dir) => dirname(dir));
|
||||
const uniqueParentDirs = new Set(parentDirs);
|
||||
progress({
|
||||
step: 1,
|
||||
@@ -107,7 +107,7 @@ async function checkAndConfirmDatabaseUpgrade(
|
||||
descriptionMessage += `Would perform upgrade: ${script.description}\n`;
|
||||
descriptionMessage += `\t-> Compatibility: ${script.compatibility}\n`;
|
||||
}
|
||||
void logger.log(descriptionMessage);
|
||||
void extLogger.log(descriptionMessage);
|
||||
|
||||
// If the quiet flag is set, do the upgrade without a popup.
|
||||
if (quiet) {
|
||||
@@ -143,7 +143,7 @@ async function checkAndConfirmDatabaseUpgrade(
|
||||
);
|
||||
|
||||
if (chosenItem === showLogItem) {
|
||||
logger.outputChannel.show();
|
||||
extLogger.outputChannel.show();
|
||||
}
|
||||
|
||||
if (chosenItem !== yesItem) {
|
||||
|
||||
@@ -34,7 +34,7 @@ function makeKey(
|
||||
"queryCausingWork was not defined on an event we expected it to be defined for!",
|
||||
);
|
||||
}
|
||||
return `${queryCausingWork}:${predicate}${suffix ? " " + suffix : ""}`;
|
||||
return `${queryCausingWork}:${predicate}${suffix ? ` ${suffix}` : ""}`;
|
||||
}
|
||||
|
||||
const DEPENDENT_PREDICATES_REGEXP = (() => {
|
||||
@@ -149,7 +149,7 @@ class JoinOrderScanner implements EvaluationLogScanner {
|
||||
private readonly predicateSizes = new Map<string, number>();
|
||||
private readonly layerEvents = new Map<
|
||||
string,
|
||||
(ComputeRecursive | InLayer)[]
|
||||
Array<ComputeRecursive | InLayer>
|
||||
>();
|
||||
// Map a key of the form 'query-with-demand : predicate name' to its badness input.
|
||||
private readonly maxTupleCountMap = new Map<string, number[]>();
|
||||
@@ -360,7 +360,7 @@ class JoinOrderScanner implements EvaluationLogScanner {
|
||||
const maxDependentPredicateSize = safeMax(dependentPredicateSizes);
|
||||
return {
|
||||
maxTupleCount: safeMax(pipelineRun.counts),
|
||||
maxDependentPredicateSize: maxDependentPredicateSize,
|
||||
maxDependentPredicateSize,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -491,7 +491,7 @@ class JoinOrderScanner implements EvaluationLogScanner {
|
||||
);
|
||||
orderTobucket.set(raReference, {
|
||||
tupleCounts: newTupleCounts,
|
||||
resultSize: resultSize,
|
||||
resultSize,
|
||||
dependentPredicateSizes: newDependentPredicateSizes,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as fs from "fs-extra";
|
||||
import { readFile } from "fs-extra";
|
||||
|
||||
/**
|
||||
* Read a file consisting of multiple JSON objects. Each object is separated from the previous one
|
||||
@@ -14,7 +14,7 @@ export async function readJsonlFile(
|
||||
path: string,
|
||||
handler: (value: any) => Promise<void>,
|
||||
): Promise<void> {
|
||||
const logSummary = await fs.readFile(path, "utf-8");
|
||||
const logSummary = await readFile(path, "utf-8");
|
||||
|
||||
// Remove newline delimiters because summary is in .jsonl format.
|
||||
const jsonSummaryObjects: string[] = logSummary.split(/\r?\n\r?\n/g);
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
EvaluationLogScannerSet,
|
||||
} from "./log-scanner";
|
||||
import { PipelineInfo, SummarySymbols } from "./summary-parser";
|
||||
import * as fs from "fs-extra";
|
||||
import { logger } from "../logging";
|
||||
import { readFile } from "fs-extra";
|
||||
import { extLogger } from "../common";
|
||||
|
||||
/**
|
||||
* Compute the key used to find a predicate in the summary symbols.
|
||||
@@ -56,7 +56,7 @@ class ProblemReporter implements EvaluationLogProblemReporter {
|
||||
}
|
||||
|
||||
public log(message: string): void {
|
||||
void logger.log(message);
|
||||
void extLogger.log(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ export class LogScannerService extends DisposableObject {
|
||||
let symbols: SummarySymbols | undefined = undefined;
|
||||
if (symbolsLocation !== undefined) {
|
||||
symbols = JSON.parse(
|
||||
await fs.readFile(symbolsLocation, { encoding: "utf-8" }),
|
||||
await readFile(symbolsLocation, { encoding: "utf-8" }),
|
||||
);
|
||||
}
|
||||
const problemReporter = new ProblemReporter(symbols);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as fs from "fs-extra";
|
||||
import { readFile } from "fs-extra";
|
||||
import { RawSourceMap, SourceMapConsumer } from "source-map";
|
||||
import {
|
||||
commands,
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
} from "vscode";
|
||||
import { DisposableObject } from "../pure/disposable-object";
|
||||
import { commandRunner } from "../commandRunner";
|
||||
import { logger } from "../logging";
|
||||
import { extLogger } from "../common";
|
||||
import { getErrorMessage } from "../pure/helpers-pure";
|
||||
|
||||
/** A `Position` within a specified file on disk. */
|
||||
@@ -95,15 +95,15 @@ export class SummaryLanguageSupport extends DisposableObject {
|
||||
if (this.lastDocument !== document) {
|
||||
this.clearCache();
|
||||
|
||||
const mapPath = document.uri.fsPath + ".map";
|
||||
const mapPath = `${document.uri.fsPath}.map`;
|
||||
|
||||
try {
|
||||
const sourceMapText = await fs.readFile(mapPath, "utf-8");
|
||||
const sourceMapText = await readFile(mapPath, "utf-8");
|
||||
const rawMap: RawSourceMap = JSON.parse(sourceMapText);
|
||||
this.sourceMap = await new SourceMapConsumer(rawMap);
|
||||
} catch (e: unknown) {
|
||||
// Error reading sourcemap. Pretend there was no sourcemap.
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
`Error reading sourcemap file '${mapPath}': ${getErrorMessage(e)}`,
|
||||
);
|
||||
this.sourceMap = undefined;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as fs from "fs-extra";
|
||||
import { writeFile, promises } from "fs-extra";
|
||||
|
||||
/**
|
||||
* Location information for a single pipeline invocation in the RA.
|
||||
@@ -51,7 +51,7 @@ export async function generateSummarySymbolsFile(
|
||||
symbolsPath: string,
|
||||
): Promise<void> {
|
||||
const symbols = await generateSummarySymbols(summaryPath);
|
||||
await fs.writeFile(symbolsPath, JSON.stringify(symbols));
|
||||
await writeFile(symbolsPath, JSON.stringify(symbols));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,7 +64,7 @@ export async function generateSummarySymbolsFile(
|
||||
async function generateSummarySymbols(
|
||||
summaryPath: string,
|
||||
): Promise<SummarySymbols> {
|
||||
const summary = await fs.promises.readFile(summaryPath, {
|
||||
const summary = await promises.readFile(summaryPath, {
|
||||
encoding: "utf-8",
|
||||
});
|
||||
const symbols: SummarySymbols = {
|
||||
@@ -111,8 +111,8 @@ async function generateSummarySymbols(
|
||||
}
|
||||
symbol.iterations[iteration] = {
|
||||
startLine: lineNumber,
|
||||
raStartLine: raStartLine,
|
||||
raEndLine: raEndLine,
|
||||
raStartLine,
|
||||
raEndLine,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as path from "path";
|
||||
import * as fs from "fs-extra";
|
||||
import { join, resolve } from "path";
|
||||
import { pathExists } from "fs-extra";
|
||||
import { setupServer, SetupServerApi } from "msw/node";
|
||||
|
||||
import { DisposableObject } from "../pure/disposable-object";
|
||||
@@ -50,7 +50,7 @@ export class MockGitHubApiServer extends DisposableObject {
|
||||
}
|
||||
}
|
||||
|
||||
const scenarioPath = path.join(scenariosPath, scenarioName);
|
||||
const scenarioPath = join(scenariosPath, scenarioName);
|
||||
|
||||
const handlers = await createRequestHandlers(scenarioPath);
|
||||
this.server.resetHandlers();
|
||||
@@ -129,10 +129,10 @@ export class MockGitHubApiServer extends DisposableObject {
|
||||
|
||||
public async getDefaultScenariosPath(): Promise<string | undefined> {
|
||||
// This should be the directory where package.json is located
|
||||
const rootDirectory = path.resolve(__dirname, "../..");
|
||||
const rootDirectory = resolve(__dirname, "../..");
|
||||
|
||||
const scenariosPath = path.resolve(rootDirectory, "src/mocks/scenarios");
|
||||
if (await fs.pathExists(scenariosPath)) {
|
||||
const scenariosPath = resolve(rootDirectory, "src/mocks/scenarios");
|
||||
if (await pathExists(scenariosPath)) {
|
||||
return scenariosPath;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as fs from "fs-extra";
|
||||
import * as path from "path";
|
||||
import { ensureDir, writeFile } from "fs-extra";
|
||||
import { join } from "path";
|
||||
|
||||
import { MockedRequest } from "msw";
|
||||
import { SetupServerApi } from "msw/node";
|
||||
@@ -66,15 +66,15 @@ export class Recorder extends DisposableObject {
|
||||
}
|
||||
|
||||
public async save(scenariosPath: string, name: string): Promise<string> {
|
||||
const scenarioDirectory = path.join(scenariosPath, name);
|
||||
const scenarioDirectory = join(scenariosPath, name);
|
||||
|
||||
await fs.ensureDir(scenarioDirectory);
|
||||
await ensureDir(scenarioDirectory);
|
||||
|
||||
for (let i = 0; i < this.currentRecordedScenario.length; i++) {
|
||||
const request = this.currentRecordedScenario[i];
|
||||
|
||||
const fileName = `${i}-${request.request.kind}.json`;
|
||||
const filePath = path.join(scenarioDirectory, fileName);
|
||||
const filePath = join(scenarioDirectory, fileName);
|
||||
|
||||
let writtenRequest = {
|
||||
...request,
|
||||
@@ -87,8 +87,8 @@ export class Recorder extends DisposableObject {
|
||||
: "bin";
|
||||
|
||||
const bodyFileName = `${i}-${writtenRequest.request.kind}.body.${extension}`;
|
||||
const bodyFilePath = path.join(scenarioDirectory, bodyFileName);
|
||||
await fs.writeFile(bodyFilePath, writtenRequest.response.body);
|
||||
const bodyFilePath = join(scenarioDirectory, bodyFileName);
|
||||
await writeFile(bodyFilePath, writtenRequest.response.body);
|
||||
|
||||
writtenRequest = {
|
||||
...writtenRequest,
|
||||
@@ -99,7 +99,7 @@ export class Recorder extends DisposableObject {
|
||||
};
|
||||
}
|
||||
|
||||
await fs.writeFile(filePath, JSON.stringify(writtenRequest, null, 2));
|
||||
await writeFile(filePath, JSON.stringify(writtenRequest, null, 2));
|
||||
}
|
||||
|
||||
this.stop();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as path from "path";
|
||||
import * as fs from "fs-extra";
|
||||
import { join } from "path";
|
||||
import { readdir, readJson, readFile } from "fs-extra";
|
||||
import { DefaultBodyType, MockedRequest, rest, RestHandler } from "msw";
|
||||
import {
|
||||
GitHubApiRequest,
|
||||
@@ -33,7 +33,7 @@ export async function createRequestHandlers(
|
||||
async function readRequestFiles(
|
||||
scenarioDirPath: string,
|
||||
): Promise<GitHubApiRequest[]> {
|
||||
const files = await fs.readdir(scenarioDirPath);
|
||||
const files = await readdir(scenarioDirPath);
|
||||
|
||||
const orderedFiles = files.sort((a, b) => {
|
||||
const aNum = parseInt(a.split("-")[0]);
|
||||
@@ -47,8 +47,8 @@ async function readRequestFiles(
|
||||
continue;
|
||||
}
|
||||
|
||||
const filePath = path.join(scenarioDirPath, file);
|
||||
const request: GitHubApiRequest = await fs.readJson(filePath, {
|
||||
const filePath = join(scenarioDirPath, file);
|
||||
const request: GitHubApiRequest = await readJson(filePath, {
|
||||
encoding: "utf8",
|
||||
});
|
||||
|
||||
@@ -56,8 +56,8 @@ async function readRequestFiles(
|
||||
typeof request.response.body === "string" &&
|
||||
request.response.body.startsWith("file:")
|
||||
) {
|
||||
request.response.body = await fs.readFile(
|
||||
path.join(scenarioDirPath, request.response.body.substring(5)),
|
||||
request.response.body = await readFile(
|
||||
join(scenarioDirPath, request.response.body.substring(5)),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"response": {
|
||||
"status": 422,
|
||||
"body": {
|
||||
"message": "No repositories could be queried."
|
||||
"message": "Unable to trigger a variant analysis. None of the requested repositories could be found."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as fs from "fs-extra";
|
||||
import { pathExists } from "fs-extra";
|
||||
import {
|
||||
commands,
|
||||
env,
|
||||
@@ -216,7 +216,7 @@ export class VSCodeMockGitHubApiServer extends DisposableObject {
|
||||
this.ctx.extensionUri,
|
||||
"src/mocks/scenarios",
|
||||
).fsPath.toString();
|
||||
if (await fs.pathExists(developmentScenariosPath)) {
|
||||
if (await pathExists(developmentScenariosPath)) {
|
||||
return developmentScenariosPath;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
} from "./helpers";
|
||||
import { QuickPickItem, window } from "vscode";
|
||||
import { ProgressCallback, UserCancellationException } from "./commandRunner";
|
||||
import { logger } from "./logging";
|
||||
import { extLogger } from "./common";
|
||||
|
||||
const QUERY_PACKS = [
|
||||
"codeql/cpp-queries",
|
||||
@@ -139,7 +139,7 @@ export async function handleInstallPackDependencies(
|
||||
}
|
||||
}
|
||||
if (failedPacks.length > 0) {
|
||||
void logger.log(`Errors:\n${errors.join("\n")}`);
|
||||
void extLogger.log(`Errors:\n${errors.join("\n")}`);
|
||||
throw new Error(
|
||||
`Unable to install pack dependencies for: ${failedPacks.join(
|
||||
", ",
|
||||
|
||||
53
extensions/ql-vscode/src/pure/distribution.ts
Normal file
53
extensions/ql-vscode/src/pure/distribution.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { platform } from "os";
|
||||
import { Open } from "unzipper";
|
||||
import { join } from "path";
|
||||
import { pathExists, chmod } from "fs-extra";
|
||||
|
||||
/**
|
||||
* Get the name of the codeql cli installation we prefer to install, based on our current platform.
|
||||
*/
|
||||
export function getRequiredAssetName(): string {
|
||||
switch (platform()) {
|
||||
case "linux":
|
||||
return "codeql-linux64.zip";
|
||||
case "darwin":
|
||||
return "codeql-osx64.zip";
|
||||
case "win32":
|
||||
return "codeql-win64.zip";
|
||||
default:
|
||||
return "codeql.zip";
|
||||
}
|
||||
}
|
||||
|
||||
export async function extractZipArchive(
|
||||
archivePath: string,
|
||||
outPath: string,
|
||||
): Promise<void> {
|
||||
const archive = await Open.file(archivePath);
|
||||
await archive.extract({
|
||||
concurrency: 4,
|
||||
path: outPath,
|
||||
});
|
||||
// Set file permissions for extracted files
|
||||
await Promise.all(
|
||||
archive.files.map(async (file) => {
|
||||
// Only change file permissions if within outPath (path.join normalises the path)
|
||||
const extractedPath = join(outPath, file.path);
|
||||
if (
|
||||
extractedPath.indexOf(outPath) !== 0 ||
|
||||
!(await pathExists(extractedPath))
|
||||
) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return chmod(extractedPath, file.externalFileAttributes >>> 16);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export function codeQlLauncherName(): string {
|
||||
return platform() === "win32" ? "codeql.exe" : "codeql";
|
||||
}
|
||||
|
||||
export function deprecatedCodeQlLauncherName(): string | undefined {
|
||||
return platform() === "win32" ? "codeql.cmd" : undefined;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as fs from "fs-extra";
|
||||
import * as path from "path";
|
||||
import { pathExists, stat, readdir } from "fs-extra";
|
||||
import { join } from "path";
|
||||
|
||||
/**
|
||||
* Recursively finds all .ql files in this set of Uris.
|
||||
@@ -14,13 +14,10 @@ export async function gatherQlFiles(
|
||||
const gatheredUris: Set<string> = new Set();
|
||||
let dirFound = false;
|
||||
for (const nextPath of paths) {
|
||||
if (
|
||||
(await fs.pathExists(nextPath)) &&
|
||||
(await fs.stat(nextPath)).isDirectory()
|
||||
) {
|
||||
if ((await pathExists(nextPath)) && (await stat(nextPath)).isDirectory()) {
|
||||
dirFound = true;
|
||||
const subPaths = await fs.readdir(nextPath);
|
||||
const fullPaths = subPaths.map((p) => path.join(nextPath, p));
|
||||
const subPaths = await readdir(nextPath);
|
||||
const fullPaths = subPaths.map((p) => join(nextPath, p));
|
||||
const nestedFiles = (await gatherQlFiles(fullPaths))[0];
|
||||
nestedFiles.forEach((nested) => gatheredUris.add(nested));
|
||||
} else if (nextPath.endsWith(".ql")) {
|
||||
@@ -38,14 +35,14 @@ export async function gatherQlFiles(
|
||||
export async function getDirectoryNamesInsidePath(
|
||||
path: string,
|
||||
): Promise<string[]> {
|
||||
if (!(await fs.pathExists(path))) {
|
||||
if (!(await pathExists(path))) {
|
||||
throw Error(`Path does not exist: ${path}`);
|
||||
}
|
||||
if (!(await fs.stat(path)).isDirectory()) {
|
||||
if (!(await stat(path)).isDirectory()) {
|
||||
throw Error(`Path is not a directory: ${path}`);
|
||||
}
|
||||
|
||||
const dirItems = await fs.readdir(path, { withFileTypes: true });
|
||||
const dirItems = await readdir(path, { withFileTypes: true });
|
||||
|
||||
const dirNames = dirItems
|
||||
.filter((dirent) => dirent.isDirectory())
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* the fact that any unknown QueryResultType value counts as an error.
|
||||
*/
|
||||
|
||||
import * as rpc from "vscode-jsonrpc";
|
||||
import { RequestType } from "vscode-jsonrpc";
|
||||
import * as shared from "./messages-shared";
|
||||
|
||||
/**
|
||||
@@ -970,129 +970,115 @@ export type ProgressMessage = shared.ProgressMessage;
|
||||
/**
|
||||
* Check a Ql query for errors without compiling it
|
||||
*/
|
||||
export const checkQuery = new rpc.RequestType<
|
||||
export const checkQuery = new RequestType<
|
||||
WithProgressId<CheckQueryParams>,
|
||||
CheckQueryResult,
|
||||
void,
|
||||
void
|
||||
>("compilation/checkQuery");
|
||||
/**
|
||||
* Compile a Ql query into a qlo
|
||||
*/
|
||||
export const compileQuery = new rpc.RequestType<
|
||||
export const compileQuery = new RequestType<
|
||||
WithProgressId<CompileQueryParams>,
|
||||
CheckQueryResult,
|
||||
void,
|
||||
void
|
||||
>("compilation/compileQuery");
|
||||
/**
|
||||
* Compile a dil query into a qlo
|
||||
*/
|
||||
export const compileDilQuery = new rpc.RequestType<
|
||||
export const compileDilQuery = new RequestType<
|
||||
WithProgressId<CompileDilParams>,
|
||||
CheckQueryResult,
|
||||
void,
|
||||
void
|
||||
>("compilation/compileDilQuery");
|
||||
|
||||
/**
|
||||
* Check if there is a valid upgrade path between two dbschemes.
|
||||
*/
|
||||
export const checkUpgrade = new rpc.RequestType<
|
||||
export const checkUpgrade = new RequestType<
|
||||
WithProgressId<UpgradeParams>,
|
||||
CheckUpgradeResult,
|
||||
void,
|
||||
void
|
||||
>("compilation/checkUpgrade");
|
||||
/**
|
||||
* Compile an upgrade script to upgrade a dataset.
|
||||
*/
|
||||
export const compileUpgrade = new rpc.RequestType<
|
||||
export const compileUpgrade = new RequestType<
|
||||
WithProgressId<CompileUpgradeParams>,
|
||||
CompileUpgradeResult,
|
||||
void,
|
||||
void
|
||||
>("compilation/compileUpgrade");
|
||||
/**
|
||||
* Compile an upgrade script to upgrade a dataset.
|
||||
*/
|
||||
export const compileUpgradeSequence = new rpc.RequestType<
|
||||
export const compileUpgradeSequence = new RequestType<
|
||||
WithProgressId<CompileUpgradeSequenceParams>,
|
||||
CompileUpgradeSequenceResult,
|
||||
void,
|
||||
void
|
||||
>("compilation/compileUpgradeSequence");
|
||||
|
||||
/**
|
||||
* Start a new structured log in the evaluator, terminating the previous one if it exists
|
||||
*/
|
||||
export const startLog = new rpc.RequestType<
|
||||
export const startLog = new RequestType<
|
||||
WithProgressId<StartLogParams>,
|
||||
StartLogResult,
|
||||
void,
|
||||
void
|
||||
>("evaluation/startLog");
|
||||
|
||||
/**
|
||||
* Terminate a structured log in the evaluator. Is a no-op if we aren't logging to the given location
|
||||
*/
|
||||
export const endLog = new rpc.RequestType<
|
||||
export const endLog = new RequestType<
|
||||
WithProgressId<EndLogParams>,
|
||||
EndLogResult,
|
||||
void,
|
||||
void
|
||||
>("evaluation/endLog");
|
||||
|
||||
/**
|
||||
* Clear the cache of a dataset
|
||||
*/
|
||||
export const clearCache = new rpc.RequestType<
|
||||
export const clearCache = new RequestType<
|
||||
WithProgressId<ClearCacheParams>,
|
||||
ClearCacheResult,
|
||||
void,
|
||||
void
|
||||
>("evaluation/clearCache");
|
||||
/**
|
||||
* Trim the cache of a dataset
|
||||
*/
|
||||
export const trimCache = new rpc.RequestType<
|
||||
export const trimCache = new RequestType<
|
||||
WithProgressId<TrimCacheParams>,
|
||||
ClearCacheResult,
|
||||
void,
|
||||
void
|
||||
>("evaluation/trimCache");
|
||||
|
||||
/**
|
||||
* Run some queries on a dataset
|
||||
*/
|
||||
export const runQueries = new rpc.RequestType<
|
||||
export const runQueries = new RequestType<
|
||||
WithProgressId<EvaluateQueriesParams>,
|
||||
EvaluationComplete,
|
||||
void,
|
||||
void
|
||||
>("evaluation/runQueries");
|
||||
|
||||
/**
|
||||
* Run upgrades on a dataset
|
||||
*/
|
||||
export const runUpgrade = new rpc.RequestType<
|
||||
export const runUpgrade = new RequestType<
|
||||
WithProgressId<RunUpgradeParams>,
|
||||
RunUpgradeResult,
|
||||
void,
|
||||
void
|
||||
>("evaluation/runUpgrade");
|
||||
|
||||
export const registerDatabases = new rpc.RequestType<
|
||||
export const registerDatabases = new RequestType<
|
||||
WithProgressId<RegisterDatabasesParams>,
|
||||
RegisterDatabasesResult,
|
||||
void,
|
||||
void
|
||||
>("evaluation/registerDatabases");
|
||||
|
||||
export const deregisterDatabases = new rpc.RequestType<
|
||||
export const deregisterDatabases = new RequestType<
|
||||
WithProgressId<DeregisterDatabasesParams>,
|
||||
DeregisterDatabasesResult,
|
||||
void,
|
||||
void
|
||||
>("evaluation/deregisterDatabases");
|
||||
|
||||
@@ -1100,10 +1086,9 @@ export const deregisterDatabases = new rpc.RequestType<
|
||||
* 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<
|
||||
export const completeQuery = new RequestType<
|
||||
EvaluationResult,
|
||||
Record<string, any>,
|
||||
void,
|
||||
void
|
||||
>("evaluation/queryCompleted");
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* the fact that any unknown QueryResultType value counts as an error.
|
||||
*/
|
||||
|
||||
import * as rpc from "vscode-jsonrpc";
|
||||
import { NotificationType } from "vscode-jsonrpc";
|
||||
|
||||
/**
|
||||
* A position within a QL file.
|
||||
@@ -106,6 +106,6 @@ export interface ProgressMessage {
|
||||
/**
|
||||
* A notification that the progress has been changed.
|
||||
*/
|
||||
export const progress = new rpc.NotificationType<ProgressMessage, void>(
|
||||
export const progress = new NotificationType<ProgressMessage>(
|
||||
"ql/progressUpdated",
|
||||
);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* the fact that any unknown QueryResultType value counts as an error.
|
||||
*/
|
||||
|
||||
import * as rpc from "vscode-jsonrpc";
|
||||
import { RequestType } from "vscode-jsonrpc";
|
||||
import * as shared from "./messages-shared";
|
||||
|
||||
/**
|
||||
@@ -164,60 +164,53 @@ export type ProgressMessage = shared.ProgressMessage;
|
||||
/**
|
||||
* Clear the cache of a dataset
|
||||
*/
|
||||
export const clearCache = new rpc.RequestType<
|
||||
export const clearCache = new RequestType<
|
||||
WithProgressId<ClearCacheParams>,
|
||||
ClearCacheResult,
|
||||
void,
|
||||
void
|
||||
>("evaluation/clearCache");
|
||||
/**
|
||||
* Trim the cache of a dataset
|
||||
*/
|
||||
export const trimCache = new rpc.RequestType<
|
||||
export const trimCache = new RequestType<
|
||||
WithProgressId<TrimCacheParams>,
|
||||
ClearCacheResult,
|
||||
void,
|
||||
void
|
||||
>("evaluation/trimCache");
|
||||
|
||||
/**
|
||||
* Clear the pack cache
|
||||
*/
|
||||
export const clearPackCache = new rpc.RequestType<
|
||||
export const clearPackCache = new RequestType<
|
||||
WithProgressId<ClearPackCacheParams>,
|
||||
ClearPackCacheResult,
|
||||
void,
|
||||
void
|
||||
>("evaluation/clearPackCache");
|
||||
|
||||
/**
|
||||
* Run a query on a database
|
||||
*/
|
||||
export const runQuery = new rpc.RequestType<
|
||||
export const runQuery = new RequestType<
|
||||
WithProgressId<RunQueryParams>,
|
||||
RunQueryResult,
|
||||
void,
|
||||
void
|
||||
>("evaluation/runQuery");
|
||||
|
||||
export const registerDatabases = new rpc.RequestType<
|
||||
export const registerDatabases = new RequestType<
|
||||
WithProgressId<RegisterDatabasesParams>,
|
||||
RegisterDatabasesResult,
|
||||
void,
|
||||
void
|
||||
>("evaluation/registerDatabases");
|
||||
|
||||
export const deregisterDatabases = new rpc.RequestType<
|
||||
export const deregisterDatabases = new RequestType<
|
||||
WithProgressId<DeregisterDatabasesParams>,
|
||||
DeregisterDatabasesResult,
|
||||
void,
|
||||
void
|
||||
>("evaluation/deregisterDatabases");
|
||||
|
||||
export const upgradeDatabase = new rpc.RequestType<
|
||||
export const upgradeDatabase = new RequestType<
|
||||
WithProgressId<UpgradeParams>,
|
||||
UpgradeResult,
|
||||
void,
|
||||
void
|
||||
>("evaluation/runUpgrade");
|
||||
|
||||
|
||||
@@ -114,11 +114,5 @@ export function getAllPaths(result: sarif.Result): sarif.ThreadFlow[] {
|
||||
* as the key in a map or set.
|
||||
*/
|
||||
export function keyToString(key: ResultKey) {
|
||||
return (
|
||||
key.resultIndex +
|
||||
"-" +
|
||||
(key.pathIndex ?? "") +
|
||||
"-" +
|
||||
(key.pathNodeIndex ?? "")
|
||||
);
|
||||
return `${key.resultIndex}-${key.pathIndex ?? ""}-${key.pathNodeIndex ?? ""}`;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as Sarif from "sarif";
|
||||
import { HighlightedRegion } from "../remote-queries/shared/analysis-result";
|
||||
import type { HighlightedRegion } from "../remote-queries/shared/analysis-result";
|
||||
import { ResolvableLocationValue } from "./bqrs-cli-types";
|
||||
|
||||
export interface SarifLink {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as unzipper from "unzipper";
|
||||
import { Open } from "unzipper";
|
||||
|
||||
/**
|
||||
* Unzips a zip file to a directory.
|
||||
@@ -6,6 +6,6 @@ import * as unzipper from "unzipper";
|
||||
* @param destinationPath The path to the directory to unzip to.
|
||||
*/
|
||||
export async function unzipFile(sourcePath: string, destinationPath: string) {
|
||||
const file = await unzipper.Open.file(sourcePath);
|
||||
const file = await Open.file(sourcePath);
|
||||
await file.extract({ path: destinationPath });
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as path from "path";
|
||||
import { dirname, basename, join, normalize, relative, extname } from "path";
|
||||
import { Discovery } from "./discovery";
|
||||
import {
|
||||
EventEmitter,
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from "vscode";
|
||||
import { MultiFileSystemWatcher } from "./vscode-utils/multi-file-system-watcher";
|
||||
import { CodeQLCliServer } from "./cli";
|
||||
import * as fs from "fs-extra";
|
||||
import { pathExists } from "fs-extra";
|
||||
|
||||
/**
|
||||
* A node in the tree of tests. This will be either a `QLTestDirectory` or a `QLTestFile`.
|
||||
@@ -52,12 +52,12 @@ export class QLTestDirectory extends QLTestNode {
|
||||
}
|
||||
|
||||
public createDirectory(relativePath: string): QLTestDirectory {
|
||||
const dirName = path.dirname(relativePath);
|
||||
const dirName = dirname(relativePath);
|
||||
if (dirName === ".") {
|
||||
return this.createChildDirectory(relativePath);
|
||||
} else {
|
||||
const parent = this.createDirectory(dirName);
|
||||
return parent.createDirectory(path.basename(relativePath));
|
||||
return parent.createDirectory(basename(relativePath));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ export class QLTestDirectory extends QLTestNode {
|
||||
// collapse children
|
||||
const replacement = new QLTestDirectory(
|
||||
child.children[0].path,
|
||||
child.name + " / " + child.children[0].name,
|
||||
`${child.name} / ${child.children[0].name}`,
|
||||
Array.from(child.children[0].children),
|
||||
);
|
||||
this._children[i] = replacement;
|
||||
@@ -89,7 +89,7 @@ export class QLTestDirectory extends QLTestNode {
|
||||
if (existingChild !== undefined) {
|
||||
return existingChild as QLTestDirectory;
|
||||
} else {
|
||||
const newChild = new QLTestDirectory(path.join(this.path, name), name);
|
||||
const newChild = new QLTestDirectory(join(this.path, name), name);
|
||||
this.addChild(newChild);
|
||||
return newChild;
|
||||
}
|
||||
@@ -200,17 +200,15 @@ export class QLTestDiscovery extends Discovery<QLTestDiscoveryResults> {
|
||||
const rootDirectory = new QLTestDirectory(fullPath, name);
|
||||
|
||||
// Don't try discovery on workspace folders that don't exist on the filesystem
|
||||
if (await fs.pathExists(fullPath)) {
|
||||
if (await pathExists(fullPath)) {
|
||||
const resolvedTests = (
|
||||
await this.cliServer.resolveTests(fullPath)
|
||||
).filter((testPath) => !QLTestDiscovery.ignoreTestPath(testPath));
|
||||
for (const testPath of resolvedTests) {
|
||||
const relativePath = path.normalize(path.relative(fullPath, testPath));
|
||||
const dirName = path.dirname(relativePath);
|
||||
const relativePath = normalize(relative(fullPath, testPath));
|
||||
const dirName = dirname(relativePath);
|
||||
const parentDirectory = rootDirectory.createDirectory(dirName);
|
||||
parentDirectory.addChild(
|
||||
new QLTestFile(testPath, path.basename(testPath)),
|
||||
);
|
||||
parentDirectory.addChild(new QLTestFile(testPath, basename(testPath)));
|
||||
}
|
||||
|
||||
rootDirectory.finish();
|
||||
@@ -223,10 +221,10 @@ export class QLTestDiscovery extends Discovery<QLTestDiscoveryResults> {
|
||||
* @param testPath Path to the test file.
|
||||
*/
|
||||
private static ignoreTestPath(testPath: string): boolean {
|
||||
switch (path.extname(testPath).toLowerCase()) {
|
||||
switch (extname(testPath).toLowerCase()) {
|
||||
case ".ql":
|
||||
case ".qlref":
|
||||
return path.basename(testPath).startsWith("__");
|
||||
return basename(testPath).startsWith("__");
|
||||
|
||||
default:
|
||||
return false;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as fs from "fs-extra";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
import { pathExists, readdir, stat, remove, readFile } from "fs-extra";
|
||||
import { EOL } from "os";
|
||||
import { join } from "path";
|
||||
import { Disposable, ExtensionContext } from "vscode";
|
||||
import { logger } from "./logging";
|
||||
import { extLogger } from "./common";
|
||||
import { QueryHistoryManager } from "./query-history";
|
||||
|
||||
const LAST_SCRUB_TIME_KEY = "lastScrubTime";
|
||||
@@ -74,18 +74,18 @@ async function scrubQueries(
|
||||
let scrubCount = 0; // total number of directories deleted
|
||||
try {
|
||||
counter?.increment();
|
||||
void logger.log("Scrubbing query directory. Removing old queries.");
|
||||
if (!(await fs.pathExists(queryDirectory))) {
|
||||
void logger.log(
|
||||
void extLogger.log("Scrubbing query directory. Removing old queries.");
|
||||
if (!(await pathExists(queryDirectory))) {
|
||||
void extLogger.log(
|
||||
`Cannot scrub. Query directory does not exist: ${queryDirectory}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const baseNames = await fs.readdir(queryDirectory);
|
||||
const baseNames = await readdir(queryDirectory);
|
||||
const errors: string[] = [];
|
||||
for (const baseName of baseNames) {
|
||||
const dir = path.join(queryDirectory, baseName);
|
||||
const dir = join(queryDirectory, baseName);
|
||||
const scrubResult = await scrubDirectory(dir, now, maxQueryTime);
|
||||
if (scrubResult.errorMsg) {
|
||||
errors.push(scrubResult.errorMsg);
|
||||
@@ -96,12 +96,12 @@ async function scrubQueries(
|
||||
}
|
||||
|
||||
if (errors.length) {
|
||||
throw new Error(os.EOL + errors.join(os.EOL));
|
||||
throw new Error(EOL + errors.join(EOL));
|
||||
}
|
||||
} catch (e) {
|
||||
void logger.log(`Error while scrubbing queries: ${e}`);
|
||||
void extLogger.log(`Error while scrubbing queries: ${e}`);
|
||||
} finally {
|
||||
void logger.log(`Scrubbed ${scrubCount} old queries.`);
|
||||
void extLogger.log(`Scrubbed ${scrubCount} old queries.`);
|
||||
}
|
||||
await qhm.removeDeletedQueries();
|
||||
}
|
||||
@@ -115,34 +115,34 @@ async function scrubDirectory(
|
||||
errorMsg?: string;
|
||||
deleted: boolean;
|
||||
}> {
|
||||
const timestampFile = path.join(dir, "timestamp");
|
||||
const timestampFile = join(dir, "timestamp");
|
||||
try {
|
||||
let deleted = true;
|
||||
if (!(await fs.stat(dir)).isDirectory()) {
|
||||
void logger.log(` ${dir} is not a directory. Deleting.`);
|
||||
await fs.remove(dir);
|
||||
} else if (!(await fs.pathExists(timestampFile))) {
|
||||
void logger.log(` ${dir} has no timestamp file. Deleting.`);
|
||||
await fs.remove(dir);
|
||||
} else if (!(await fs.stat(timestampFile)).isFile()) {
|
||||
void logger.log(` ${timestampFile} is not a file. Deleting.`);
|
||||
await fs.remove(dir);
|
||||
if (!(await stat(dir)).isDirectory()) {
|
||||
void extLogger.log(` ${dir} is not a directory. Deleting.`);
|
||||
await remove(dir);
|
||||
} else if (!(await pathExists(timestampFile))) {
|
||||
void extLogger.log(` ${dir} has no timestamp file. Deleting.`);
|
||||
await remove(dir);
|
||||
} else if (!(await stat(timestampFile)).isFile()) {
|
||||
void extLogger.log(` ${timestampFile} is not a file. Deleting.`);
|
||||
await remove(dir);
|
||||
} else {
|
||||
const timestampText = await fs.readFile(timestampFile, "utf8");
|
||||
const timestampText = await readFile(timestampFile, "utf8");
|
||||
const timestamp = parseInt(timestampText, 10);
|
||||
|
||||
if (Number.isNaN(timestamp)) {
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
` ${dir} has invalid timestamp '${timestampText}'. Deleting.`,
|
||||
);
|
||||
await fs.remove(dir);
|
||||
await remove(dir);
|
||||
} else if (now - timestamp > maxQueryTime) {
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
` ${dir} is older than ${maxQueryTime / 1000} seconds. Deleting.`,
|
||||
);
|
||||
await fs.remove(dir);
|
||||
await remove(dir);
|
||||
} else {
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
` ${dir} is not older than ${maxQueryTime / 1000} seconds. Keeping.`,
|
||||
);
|
||||
deleted = false;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as path from "path";
|
||||
import { join, dirname } from "path";
|
||||
import {
|
||||
commands,
|
||||
Disposable,
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
showAndLogWarningMessage,
|
||||
showBinaryChoiceDialog,
|
||||
} from "./helpers";
|
||||
import { logger } from "./logging";
|
||||
import { extLogger } from "./common";
|
||||
import { URLSearchParams } from "url";
|
||||
import { DisposableObject } from "./pure/disposable-object";
|
||||
import { commandRunner } from "./commandRunner";
|
||||
@@ -48,7 +48,7 @@ import {
|
||||
variantAnalysisStatusToQueryStatus,
|
||||
} from "./query-status";
|
||||
import { slurpQueryHistory, splatQueryHistory } from "./query-serialization";
|
||||
import * as fs from "fs-extra";
|
||||
import { pathExists } from "fs-extra";
|
||||
import { CliVersionConstraint } from "./cli";
|
||||
import { HistoryItemLabelProvider } from "./history-item-label-provider";
|
||||
import { Credentials } from "./authentication";
|
||||
@@ -167,15 +167,12 @@ export class HistoryTreeDataProvider
|
||||
private readonly labelProvider: HistoryItemLabelProvider,
|
||||
) {
|
||||
super();
|
||||
this.failedIconPath = path.join(
|
||||
extensionPath,
|
||||
FAILED_QUERY_HISTORY_ITEM_ICON,
|
||||
);
|
||||
this.localSuccessIconPath = path.join(
|
||||
this.failedIconPath = join(extensionPath, FAILED_QUERY_HISTORY_ITEM_ICON);
|
||||
this.localSuccessIconPath = join(
|
||||
extensionPath,
|
||||
LOCAL_SUCCESS_QUERY_HISTORY_ITEM_ICON,
|
||||
);
|
||||
this.remoteSuccessIconPath = path.join(
|
||||
this.remoteSuccessIconPath = join(
|
||||
extensionPath,
|
||||
REMOTE_SUCCESS_QUERY_HISTORY_ITEM_ICON,
|
||||
);
|
||||
@@ -414,7 +411,7 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
// This is because the query history is specific to each workspace.
|
||||
// For situations where `ctx.storageUri` is undefined (i.e., there is no workspace),
|
||||
// we default to global storage.
|
||||
this.queryMetadataStorageLocation = path.join(
|
||||
this.queryMetadataStorageLocation = join(
|
||||
(ctx.storageUri || ctx.globalStorageUri).fsPath,
|
||||
WORKSPACE_QUERY_HISTORY_FILE,
|
||||
);
|
||||
@@ -460,7 +457,7 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
}),
|
||||
);
|
||||
|
||||
void logger.log("Registering query history panel commands.");
|
||||
void extLogger.log("Registering query history panel commands.");
|
||||
this.push(
|
||||
commandRunner(
|
||||
"codeQLQueryHistory.openQuery",
|
||||
@@ -705,7 +702,7 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
});
|
||||
await this.refreshTreeView();
|
||||
} else {
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
"Variant analysis status update event received for unknown variant analysis",
|
||||
);
|
||||
}
|
||||
@@ -775,7 +772,7 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
}
|
||||
await this.refreshTreeView();
|
||||
} else {
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
"Variant analysis status update event received for unknown variant analysis",
|
||||
);
|
||||
}
|
||||
@@ -787,7 +784,7 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
}
|
||||
|
||||
async readQueryHistory(): Promise<void> {
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
`Reading cached query history from '${this.queryMetadataStorageLocation}'.`,
|
||||
);
|
||||
const history = await slurpQueryHistory(this.queryMetadataStorageLocation);
|
||||
@@ -829,6 +826,14 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
return;
|
||||
}
|
||||
|
||||
if (finalSingleItem.t === "variant-analysis") {
|
||||
await commands.executeCommand(
|
||||
"codeQL.openVariantAnalysisQueryFile",
|
||||
finalSingleItem.variantAnalysis.id,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let queryPath: string;
|
||||
switch (finalSingleItem.t) {
|
||||
case "local":
|
||||
@@ -837,9 +842,6 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
case "remote":
|
||||
queryPath = finalSingleItem.remoteQuery.queryFilePath;
|
||||
break;
|
||||
case "variant-analysis":
|
||||
queryPath = finalSingleItem.variantAnalysis.query.filePath;
|
||||
break;
|
||||
default:
|
||||
assertNever(finalSingleItem);
|
||||
}
|
||||
@@ -877,7 +879,7 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
if (
|
||||
item.t == "local" &&
|
||||
item.completedQuery &&
|
||||
!(await fs.pathExists(item.completedQuery?.query.querySaveDir))
|
||||
!(await pathExists(item.completedQuery?.query.querySaveDir))
|
||||
) {
|
||||
this.treeDataProvider.remove(item);
|
||||
item.completedQuery?.dispose();
|
||||
@@ -929,9 +931,9 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
// Remote queries can be removed locally, but not remotely.
|
||||
// The user must cancel the query on GitHub Actions explicitly.
|
||||
this.treeDataProvider.remove(item);
|
||||
void logger.log(`Deleted ${this.labelProvider.getLabel(item)}.`);
|
||||
void extLogger.log(`Deleted ${this.labelProvider.getLabel(item)}.`);
|
||||
if (item.status === QueryStatus.InProgress) {
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
"The variant analysis is still running on GitHub Actions. To cancel there, you must go to the workflow run in your browser.",
|
||||
);
|
||||
}
|
||||
@@ -945,9 +947,9 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
// We can remove a Variant Analysis locally, but not remotely.
|
||||
// The user must cancel the query on GitHub Actions explicitly.
|
||||
this.treeDataProvider.remove(item);
|
||||
void logger.log(`Deleted ${this.labelProvider.getLabel(item)}.`);
|
||||
void extLogger.log(`Deleted ${this.labelProvider.getLabel(item)}.`);
|
||||
if (item.status === QueryStatus.InProgress) {
|
||||
void logger.log(
|
||||
void extLogger.log(
|
||||
"The variant analysis is still running on GitHub Actions. To cancel there, you must go to the workflow run in your browser.",
|
||||
);
|
||||
}
|
||||
@@ -1109,7 +1111,7 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
return queryHistoryItem.completedQuery.query.querySaveDir;
|
||||
}
|
||||
} else if (queryHistoryItem.t === "remote") {
|
||||
return path.join(this.queryStorageDir, queryHistoryItem.queryId);
|
||||
return join(this.queryStorageDir, queryHistoryItem.queryId);
|
||||
} else if (queryHistoryItem.t === "variant-analysis") {
|
||||
return this.variantAnalysisManager.getVariantAnalysisStorageLocation(
|
||||
queryHistoryItem.variantAnalysis.id,
|
||||
@@ -1135,19 +1137,19 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
let externalFilePath: string | undefined;
|
||||
if (finalSingleItem.t === "local") {
|
||||
if (finalSingleItem.completedQuery) {
|
||||
externalFilePath = path.join(
|
||||
externalFilePath = join(
|
||||
finalSingleItem.completedQuery.query.querySaveDir,
|
||||
"timestamp",
|
||||
);
|
||||
}
|
||||
} else if (finalSingleItem.t === "remote") {
|
||||
externalFilePath = path.join(
|
||||
externalFilePath = join(
|
||||
this.queryStorageDir,
|
||||
finalSingleItem.queryId,
|
||||
"timestamp",
|
||||
);
|
||||
} else if (finalSingleItem.t === "variant-analysis") {
|
||||
externalFilePath = path.join(
|
||||
externalFilePath = join(
|
||||
this.variantAnalysisManager.getVariantAnalysisStorageLocation(
|
||||
finalSingleItem.variantAnalysis.id,
|
||||
),
|
||||
@@ -1156,11 +1158,11 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
}
|
||||
|
||||
if (externalFilePath) {
|
||||
if (!(await fs.pathExists(externalFilePath))) {
|
||||
if (!(await pathExists(externalFilePath))) {
|
||||
// timestamp file is missing (manually deleted?) try selecting the parent folder.
|
||||
// It's less nice, but at least it will work.
|
||||
externalFilePath = path.dirname(externalFilePath);
|
||||
if (!(await fs.pathExists(externalFilePath))) {
|
||||
externalFilePath = dirname(externalFilePath);
|
||||
if (!(await pathExists(externalFilePath))) {
|
||||
throw new Error(
|
||||
`Query directory does not exist: ${externalFilePath}`,
|
||||
);
|
||||
@@ -1248,7 +1250,7 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
// Summary log file doesn't exist.
|
||||
if (
|
||||
finalSingleItem.evalLogLocation &&
|
||||
(await fs.pathExists(finalSingleItem.evalLogLocation))
|
||||
(await pathExists(finalSingleItem.evalLogLocation))
|
||||
) {
|
||||
// If raw log does exist, then the summary log is still being generated.
|
||||
this.warnInProgressEvalLogSummary();
|
||||
@@ -1343,6 +1345,14 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
return;
|
||||
}
|
||||
|
||||
if (finalSingleItem.t === "variant-analysis") {
|
||||
await commands.executeCommand(
|
||||
"codeQL.openVariantAnalysisQueryText",
|
||||
finalSingleItem.variantAnalysis.id,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const params = new URLSearchParams({
|
||||
isQuickEval: String(
|
||||
!!(
|
||||
@@ -1604,8 +1614,8 @@ the file in the file explorer and dragging it into the workspace.`,
|
||||
}
|
||||
} else {
|
||||
void showAndLogErrorMessage(`Could not open file ${fileLocation}`);
|
||||
void logger.log(getErrorMessage(e));
|
||||
void logger.log(getErrorStack(e));
|
||||
void extLogger.log(getErrorMessage(e));
|
||||
void extLogger.log(getErrorStack(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ import { CancellationTokenSource, env } from "vscode";
|
||||
import * as messages from "./pure/messages-shared";
|
||||
import * as legacyMessages from "./pure/legacy-messages";
|
||||
import * as cli from "./cli";
|
||||
import * as fs from "fs-extra";
|
||||
import * as path from "path";
|
||||
import { pathExists } from "fs-extra";
|
||||
import { basename } from "path";
|
||||
import {
|
||||
RawResultsSortState,
|
||||
SortedResultSetInfo,
|
||||
@@ -13,8 +13,8 @@ import {
|
||||
ResultsPaths,
|
||||
SarifInterpretationData,
|
||||
GraphInterpretationData,
|
||||
DatabaseInfo,
|
||||
} from "./pure/interface-types";
|
||||
import { DatabaseInfo } from "./pure/interface-types";
|
||||
import { QueryStatus } from "./query-status";
|
||||
import { QueryEvaluationInfo, QueryWithResults } from "./run-queries-shared";
|
||||
import { formatLegacyMessage } from "./legacy-query-server/run-queries";
|
||||
@@ -161,7 +161,7 @@ export async function interpretResultsSarif(
|
||||
): Promise<SarifInterpretationData> {
|
||||
const { resultsPath, interpretedResultsPath } = resultsPaths;
|
||||
let res;
|
||||
if (await fs.pathExists(interpretedResultsPath)) {
|
||||
if (await pathExists(interpretedResultsPath)) {
|
||||
res = await sarifParser(interpretedResultsPath);
|
||||
} else {
|
||||
res = await cli.interpretBqrsSarif(
|
||||
@@ -184,7 +184,7 @@ export async function interpretGraphResults(
|
||||
sourceInfo?: cli.SourceInfo,
|
||||
): Promise<GraphInterpretationData> {
|
||||
const { resultsPath, interpretedResultsPath } = resultsPaths;
|
||||
if (await fs.pathExists(interpretedResultsPath)) {
|
||||
if (await pathExists(interpretedResultsPath)) {
|
||||
const dot = await cli.readDotFiles(interpretedResultsPath);
|
||||
return { dot, t: "GraphInterpretationData" };
|
||||
}
|
||||
@@ -271,9 +271,9 @@ export class LocalQueryInfo {
|
||||
if (this.initialInfo.quickEvalPosition) {
|
||||
const { line, endLine, fileName } = this.initialInfo.quickEvalPosition;
|
||||
const lineInfo = line === endLine ? `${line}` : `${line}-${endLine}`;
|
||||
return `${path.basename(fileName)}:${lineInfo}`;
|
||||
return `${basename(fileName)}:${lineInfo}`;
|
||||
}
|
||||
return path.basename(this.initialInfo.queryPath);
|
||||
return basename(this.initialInfo.queryPath);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -285,7 +285,7 @@ export class LocalQueryInfo {
|
||||
*/
|
||||
getQueryName() {
|
||||
if (this.initialInfo.quickEvalPosition) {
|
||||
return "Quick evaluation of " + this.getQueryFileName();
|
||||
return `Quick evaluation of ${this.getQueryFileName()}`;
|
||||
} else if (this.completedQuery?.query.metadata?.name) {
|
||||
return this.completedQuery?.query.metadata?.name;
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as fs from "fs-extra";
|
||||
import * as path from "path";
|
||||
import { pathExists, readFile, remove, mkdir, writeFile } from "fs-extra";
|
||||
import { dirname } from "path";
|
||||
|
||||
import { showAndLogErrorMessage } from "./helpers";
|
||||
import {
|
||||
@@ -17,11 +17,11 @@ export async function slurpQueryHistory(
|
||||
fsPath: string,
|
||||
): Promise<QueryHistoryInfo[]> {
|
||||
try {
|
||||
if (!(await fs.pathExists(fsPath))) {
|
||||
if (!(await pathExists(fsPath))) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const data = await fs.readFile(fsPath, "utf8");
|
||||
const data = await readFile(fsPath, "utf8");
|
||||
const obj = JSON.parse(data);
|
||||
if (![1, 2].includes(obj.version)) {
|
||||
void showAndLogErrorMessage(
|
||||
@@ -89,7 +89,7 @@ export async function slurpQueryHistory(
|
||||
return true;
|
||||
}
|
||||
const resultsPath = q.completedQuery?.query.resultsPaths.resultsPath;
|
||||
return !!resultsPath && (await fs.pathExists(resultsPath));
|
||||
return !!resultsPath && (await pathExists(resultsPath));
|
||||
});
|
||||
} catch (e) {
|
||||
void showAndLogErrorMessage("Error loading query history.", {
|
||||
@@ -98,7 +98,7 @@ export async function slurpQueryHistory(
|
||||
),
|
||||
});
|
||||
// since the query history is invalid, it should be deleted so this error does not happen on next startup.
|
||||
await fs.remove(fsPath);
|
||||
await remove(fsPath);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -117,8 +117,8 @@ export async function splatQueryHistory(
|
||||
fsPath: string,
|
||||
): Promise<void> {
|
||||
try {
|
||||
if (!(await fs.pathExists(fsPath))) {
|
||||
await fs.mkdir(path.dirname(fsPath), { recursive: true });
|
||||
if (!(await pathExists(fsPath))) {
|
||||
await mkdir(dirname(fsPath), { recursive: true });
|
||||
}
|
||||
// remove incomplete local queries since they cannot be recreated on restart
|
||||
const filteredQueries = queries.filter((q) =>
|
||||
@@ -135,7 +135,7 @@ export async function splatQueryHistory(
|
||||
null,
|
||||
2,
|
||||
);
|
||||
await fs.writeFile(fsPath, data);
|
||||
await writeFile(fsPath, data);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Error saving query history to ${fsPath}: ${getErrorMessage(e)}`,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import * as path from "path";
|
||||
import * as fs from "fs-extra";
|
||||
import { dirname } from "path";
|
||||
import { ensureFile } from "fs-extra";
|
||||
|
||||
import { DisposableObject } from "../pure/disposable-object";
|
||||
import { CancellationToken, commands } from "vscode";
|
||||
import { createMessageConnection, RequestType } from "vscode-jsonrpc";
|
||||
import { createMessageConnection, RequestType } from "vscode-jsonrpc/node";
|
||||
import * as cli from "../cli";
|
||||
import { QueryServerConfig } from "../config";
|
||||
import { Logger, ProgressReporter } from "../logging";
|
||||
import { Logger, ProgressReporter } from "../common";
|
||||
import {
|
||||
progress,
|
||||
ProgressMessage,
|
||||
@@ -44,7 +44,7 @@ export class QueryServerClient extends DisposableObject {
|
||||
nextProgress: number;
|
||||
withProgressReporting: WithProgressReporting;
|
||||
|
||||
private readonly queryServerStartListeners = [] as ProgressTask<void>[];
|
||||
private readonly queryServerStartListeners = [] as Array<ProgressTask<void>>;
|
||||
|
||||
// Can't use standard vscode EventEmitter here since they do not cause the calling
|
||||
// function to fail if one of the event handlers fail. This is something that
|
||||
@@ -140,7 +140,7 @@ export class QueryServerClient extends DisposableObject {
|
||||
}
|
||||
|
||||
const structuredLogFile = `${this.opts.contextStoragePath}/structured-evaluator-log.json`;
|
||||
await fs.ensureFile(structuredLogFile);
|
||||
await ensureFile(structuredLogFile);
|
||||
|
||||
args.push("--evaluator-log");
|
||||
args.push(structuredLogFile);
|
||||
@@ -201,8 +201,8 @@ export class QueryServerClient extends DisposableObject {
|
||||
return this.serverProcess!.child.pid || 0;
|
||||
}
|
||||
|
||||
async sendRequest<P, R, E, RO>(
|
||||
type: RequestType<WithProgressId<P>, R, E, RO>,
|
||||
async sendRequest<P, R, E>(
|
||||
type: RequestType<WithProgressId<P>, R, E>,
|
||||
parameter: P,
|
||||
token?: CancellationToken,
|
||||
progress?: (res: ProgressMessage) => void,
|
||||
@@ -236,9 +236,7 @@ export class QueryServerClient extends DisposableObject {
|
||||
private updateActiveQuery(method: string, parameter: any): void {
|
||||
if (method === messages.runQuery.method) {
|
||||
this.activeQueryLogFile = findQueryLogFile(
|
||||
path.dirname(
|
||||
path.dirname((parameter as messages.RunQueryParams).outputPath),
|
||||
),
|
||||
dirname(dirname((parameter as messages.RunQueryParams).outputPath)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as path from "path";
|
||||
import { join } from "path";
|
||||
import { CancellationToken } from "vscode";
|
||||
import * as cli from "../cli";
|
||||
import { ProgressCallback } from "../commandRunner";
|
||||
@@ -9,9 +9,9 @@ import {
|
||||
showAndLogWarningMessage,
|
||||
tryGetQueryMetadata,
|
||||
} from "../helpers";
|
||||
import { logger } from "../logging";
|
||||
import { extLogger } from "../common";
|
||||
import * as messages from "../pure/new-messages";
|
||||
import * as legacyMessages from "../pure/legacy-messages";
|
||||
import { QueryResultType } from "../pure/legacy-messages";
|
||||
import { InitialQueryInfo, LocalQueryInfo } from "../query-results";
|
||||
import { QueryEvaluationInfo, QueryWithResults } from "../run-queries-shared";
|
||||
import * as qsClient from "./queryserver-client";
|
||||
@@ -52,7 +52,7 @@ export async function compileAndRunQueryAgainstDatabase(
|
||||
|
||||
const hasMetadataFile = await dbItem.hasMetadataFile();
|
||||
const query = new QueryEvaluationInfo(
|
||||
path.join(queryStorageDir, initialInfo.id),
|
||||
join(queryStorageDir, initialInfo.id),
|
||||
dbItem.databaseUri.fsPath,
|
||||
hasMetadataFile,
|
||||
initialInfo.quickEvalPosition,
|
||||
@@ -110,7 +110,7 @@ export async function compileAndRunQueryAgainstDatabase(
|
||||
|
||||
if (result.resultType !== messages.QueryResultType.SUCCESS) {
|
||||
const message = result.message || "Failed to run query";
|
||||
void logger.log(message);
|
||||
void extLogger.log(message);
|
||||
void showAndLogErrorMessage(message);
|
||||
}
|
||||
let message;
|
||||
@@ -143,8 +143,8 @@ export async function compileAndRunQueryAgainstDatabase(
|
||||
evaluationTime: result.evaluationTime,
|
||||
queryId: 0,
|
||||
resultType: successful
|
||||
? legacyMessages.QueryResultType.SUCCESS
|
||||
: legacyMessages.QueryResultType.OTHER_ERROR,
|
||||
? QueryResultType.SUCCESS
|
||||
: QueryResultType.OTHER_ERROR,
|
||||
runId: 0,
|
||||
message,
|
||||
},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user