diff --git a/.gitattributes b/.gitattributes index 1d6a75767..3660b9515 100644 --- a/.gitattributes +++ b/.gitattributes @@ -22,7 +22,7 @@ CHANGELOG.md merge=union # Mark some JSON files containing test data as generated so they are not included # as part of diffs or language statistics. -extensions/ql-vscode/src/stories/remote-queries/data/*.json linguist-generated +extensions/ql-vscode/src/stories/variant-analysis/data/*.json linguist-generated # Always use LF line endings, also on Windows * text=auto eol=lf diff --git a/.github/codeql/queries/unique-command-use.ql b/.github/codeql/queries/unique-command-use.ql new file mode 100644 index 000000000..9602bf397 --- /dev/null +++ b/.github/codeql/queries/unique-command-use.ql @@ -0,0 +1,134 @@ +/** + * @name A VS Code command should not be used in multiple locations + * @kind problem + * @problem.severity warning + * @id vscode-codeql/unique-command-use + * @description Using each VS Code command from only one location makes + * our telemetry more useful, because we can differentiate more user + * interactions and know which features of the UI our users are using. + * To fix this alert, new commands will need to be made so that each one + * is only used from one location. The commands should share the same + * implementation so we do not introduce duplicate code. + * When fixing this alert, search the codebase for all other references + * to the command name. The location of the alert is an arbitrarily + * chosen usage of the command, and may not necessarily be the location + * that should be changed to fix the alert. + */ + + import javascript + + /** + * The name of a VS Code command. + */ + class CommandName extends string { + CommandName() { exists(CommandUsage e | e.getCommandName() = this) } + + /** + * In how many ways is this command used. Will always be at least 1. + */ + int getNumberOfUsages() { result = count(this.getAUse()) } + + /** + * Get a usage of this command. + */ + CommandUsage getAUse() { result.getCommandName() = this } + + /** + * Get the canonical first usage of this command, to use for the location + * of the alert. The implementation of this ordering of usages is arbitrary + * and the usage given may not be the one that should be changed when fixing + * the alert. + */ + CommandUsage getFirstUsage() { + result = + max(CommandUsage use | + use = this.getAUse() + | + use + order by + use.getFile().getRelativePath(), use.getLocation().getStartLine(), + use.getLocation().getStartColumn() + ) + } + } + + /** + * Represents a single usage of a command, either from within code or + * from the command's definition in package.json + */ + abstract class CommandUsage extends Locatable { + abstract string getCommandName(); + } + + /** + * A usage of a command from the typescript code, by calling `executeCommand`. + */ + class CommandUsageCallExpr extends CommandUsage, CallExpr { + CommandUsageCallExpr() { + this.getCalleeName() = "executeCommand" and + this.getArgument(0).(StringLiteral).getValue().matches("%codeQL%") and + not this.getFile().getRelativePath().matches("extensions/ql-vscode/test/%") + } + + override string getCommandName() { result = this.getArgument(0).(StringLiteral).getValue() } + } + + /** + * A usage of a command from any menu that isn't the command palette. + * This means a user could invoke the command by clicking on a button in + * something like a menu or a dropdown. + */ + class CommandUsagePackageJsonMenuItem extends CommandUsage, JsonObject { + CommandUsagePackageJsonMenuItem() { + exists(this.getPropValue("command")) and + exists(PackageJson packageJson, string menuName | + packageJson + .getPropValue("contributes") + .getPropValue("menus") + .getPropValue(menuName) + .getElementValue(_) = this and + menuName != "commandPalette" + ) + } + + override string getCommandName() { result = this.getPropValue("command").getStringValue() } + } + + /** + * Is the given command disabled for use in the command palette by + * a block with a `"when": "false"` field. + */ + predicate isDisabledInCommandPalette(string commandName) { + exists(PackageJson packageJson, JsonObject commandPaletteObject | + packageJson + .getPropValue("contributes") + .getPropValue("menus") + .getPropValue("commandPalette") + .getElementValue(_) = commandPaletteObject and + commandPaletteObject.getPropValue("command").getStringValue() = commandName and + commandPaletteObject.getPropValue("when").getStringValue() = "false" + ) + } + + /** + * Represents a command being usable from the command palette. + * This means that a user could choose to manually invoke the command. + */ + class CommandUsagePackageJsonCommandPalette extends CommandUsage, JsonObject { + CommandUsagePackageJsonCommandPalette() { + this.getFile().getBaseName() = "package.json" and + exists(this.getPropValue("command")) and + exists(PackageJson packageJson | + packageJson.getPropValue("contributes").getPropValue("commands").getElementValue(_) = this + ) and + not isDisabledInCommandPalette(this.getPropValue("command").getStringValue()) + } + + override string getCommandName() { result = this.getPropValue("command").getStringValue() } + } + + from CommandName c + where c.getNumberOfUsages() > 1 + select c.getFirstUsage(), + "The " + c + " command is used from " + c.getNumberOfUsages() + " locations" + \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 75bc523b3..15d1499c6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -132,7 +132,12 @@ jobs: - name: Run unit tests working-directory: extensions/ql-vscode run: | - npm run test + npm run test:unit + + - name: Run view tests + working-directory: extensions/ql-vscode + run: | + npm run test:view test: name: Test @@ -173,7 +178,7 @@ jobs: VSCODE_CODEQL_GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' run: | unset DBUS_SESSION_BUS_ADDRESS - /usr/bin/xvfb-run npm run integration + /usr/bin/xvfb-run npm run test:vscode-integration - name: Run integration tests (Windows) if: matrix.os == 'windows-latest' @@ -181,7 +186,7 @@ jobs: env: VSCODE_CODEQL_GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' run: | - npm run integration + npm run test:vscode-integration set-matrix: name: Set Matrix for cli-test @@ -254,10 +259,10 @@ jobs: if: matrix.os == 'ubuntu-latest' run: | unset DBUS_SESSION_BUS_ADDRESS - /usr/bin/xvfb-run npm run cli-integration + /usr/bin/xvfb-run npm run test:cli-integration - name: Run CLI tests (Windows) working-directory: extensions/ql-vscode if: matrix.os == 'windows-latest' run: | - npm run cli-integration + npm run test:cli-integration diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bff69e9da..0454b038f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -54,7 +54,7 @@ jobs: echo "ref_name=$REF_NAME" >> "$GITHUB_OUTPUT" - name: Upload artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: vscode-codeql-extension path: artifacts diff --git a/.vscode/launch.json b/.vscode/launch.json index d2479f051..25b4a0586 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,6 +11,7 @@ "--extensionDevelopmentPath=${workspaceRoot}/extensions/ql-vscode", // Add a reference to a workspace to open. Eg- // "${workspaceRoot}/../vscode-codeql-starter/vscode-codeql-starter.code-workspace" + // "${workspaceRoot}/../codespaces-codeql/tutorial.code-workspace" ], "sourceMaps": true, "outFiles": [ diff --git a/CODEOWNERS b/CODEOWNERS index f201e92c8..bd290fd15 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,4 +1,3 @@ **/* @github/codeql-vscode-reviewers -**/remote-queries/ @github/code-scanning-secexp-reviewers **/variant-analysis/ @github/code-scanning-secexp-reviewers **/databases/ @github/code-scanning-secexp-reviewers diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a5e2dddd0..3115d05e7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -98,10 +98,11 @@ We have several types of tests: * Unit tests: these live in the `tests/unit-tests/` directory * View tests: these live in `src/view/variant-analysis/__tests__/` * VSCode integration tests: - * `test/vscode-tests/no-workspace` tests: These are intended to cover functionality that is meant to work before you even have a workspace open. + * `test/vscode-tests/activated-extension` tests: These are intended to cover functionality that require the full extension to be activated but don't require the CLI. This suite is not run against multiple versions of the CLI in CI. + * `test/vscode-tests/no-workspace` tests: These are intended to cover functionality around not having a workspace. The extension is not activated in these tests. * `test/vscode-tests/minimal-workspace` tests: These are intended to cover functionality that need a workspace but don't require the full extension to be activated. * CLI integration tests: these live in `test/vscode-tests/cli-integration` - * These tests are intendended to be cover functionality that is related to the integration between the CodeQL CLI and the extension. + * These tests are intended to cover functionality that is related to the integration between the CodeQL CLI and the extension. These tests are run against each supported versions of the CLI in CI. The CLI integration tests require an instance of the CodeQL CLI to run so they will require some extra setup steps. When adding new tests to our test suite, please be mindful of whether they need to be in the cli-integration folder. If the tests don't depend on the CLI, they are better suited to being a VSCode integration test. @@ -119,7 +120,7 @@ Then, from the `extensions/ql-vscode` directory, use the appropriate command to * Unit tests: `npm run test:unit` * View Tests: `npm test:view` -* VSCode integration tests: `npm run integration` +* VSCode integration tests: `npm run test:vscode-integration` ###### CLI integration tests @@ -130,7 +131,7 @@ The CLI integration tests require the CodeQL standard libraries in order to run 2. Run your test command: ```shell -cd extensions/ql-vscode && npm run cli-integration +cd extensions/ql-vscode && npm run test:cli-integration ``` ##### 2. From VSCode @@ -161,13 +162,13 @@ The easiest way to run a single test is to change the `it` of the test to `it.on to only run tests for this specific file. For example, to run the test `test/vscode-tests/cli-integration/run-queries.test.ts`: ```shell -npm run cli-integration -- --runTestsByPath test/vscode-tests/cli-integration/run-queries.test.ts +npm run test:cli-integration -- --runTestsByPath test/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 `test/vscode-tests/cli-integration/run-queries.test.ts`: ```shell -npm run cli-integration -- --runTestsByPath test/vscode-tests/cli-integration/run-queries.test.ts --testNamePattern "should create a QueryEvaluationInfo" +npm run test:cli-integration -- --runTestsByPath test/vscode-tests/cli-integration/run-queries.test.ts --testNamePattern "should create a QueryEvaluationInfo" ``` ##### 2. From VSCode diff --git a/README.md b/README.md index 1fce91247..d744fa193 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ The extension is released. You can download it from the [Visual Studio Marketpla To see what has changed in the last few versions of the extension, see the [Changelog](https://github.com/github/vscode-codeql/blob/main/extensions/ql-vscode/CHANGELOG.md). [![CI status badge](https://github.com/github/vscode-codeql/workflows/Build%20Extension/badge.svg)](https://github.com/github/vscode-codeql/actions?query=workflow%3A%22Build+Extension%22+branch%3Amaster) -[![VS Marketplace badge](https://vsmarketplacebadge.apphb.com/version/github.vscode-codeql.svg)](https://marketplace.visualstudio.com/items?itemName=github.vscode-codeql) +[![VS Marketplace badge](https://vsmarketplacebadges.dev/version/github.vscode-codeql.svg)](https://marketplace.visualstudio.com/items?itemName=github.vscode-codeql) ## Features diff --git a/extensions/ql-vscode/.eslintrc.js b/extensions/ql-vscode/.eslintrc.js index 77ab05e13..a8463cac1 100644 --- a/extensions/ql-vscode/.eslintrc.js +++ b/extensions/ql-vscode/.eslintrc.js @@ -29,6 +29,7 @@ const baseConfig = { "plugin:@typescript-eslint/recommended", ], rules: { + "@typescript-eslint/await-thenable": "error", "@typescript-eslint/no-use-before-define": 0, "@typescript-eslint/no-unused-vars": [ "warn", diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 23258ee81..cf3ee87e1 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -2,6 +2,21 @@ ## [UNRELEASED] +- Send telemetry about unhandled errors happening within the extension. [#2125](https://github.com/github/vscode-codeql/pull/2125) + +## 1.7.11 - 1 March 2023 + +- Enable collection of telemetry concerning interactions with UI elements, including buttons, links, and other inputs. [#2114](https://github.com/github/vscode-codeql/pull/2114) +- Prevent the automatic installation of CodeQL CLI version 2.12.3 to avoid a bug in the language server. CodeQL CLI 2.12.2 will be used instead. [#2126](https://github.com/github/vscode-codeql/pull/2126) + +## 1.7.10 - 23 February 2023 + +- Fix bug that was causing unwanted error notifications. + +## 1.7.9 - 20 February 2023 + +No user facing changes. + ## 1.7.8 - 2 February 2023 - Renamed command "CodeQL: Run Query" to "CodeQL: Run Query on Selected Database". [#1962](https://github.com/github/vscode-codeql/pull/1962) diff --git a/extensions/ql-vscode/docs/test-plan.md b/extensions/ql-vscode/docs/test-plan.md index 70cd2f8f2..b131580ed 100644 --- a/extensions/ql-vscode/docs/test-plan.md +++ b/extensions/ql-vscode/docs/test-plan.md @@ -2,15 +2,17 @@ This document describes the manual test plan for the QL extension for Visual Studio Code. -The plan will be executed manually to start with but the goal is to eventually automate parts of the process (based on +The plan will be executed manually to start with but the goal is to eventually automate parts of the process (based on effort vs value basis). -#### What this doesn't cover +## What this doesn't cover + We don't need to test features (and permutations of features) that are covered by automated tests. -### Before releasing the VS Code extension +## Before releasing the VS Code extension + - Go through the required test cases listed below -- Check major PRs since the previous release for specific one-off things to test. Based on that, you might want to +- Check major PRs since the previous release for specific one-off things to test. Based on that, you might want to choose to go through some of the Optional Test Cases. - Run a query using the existing version of the extension (to generate an "old" query history item) @@ -24,20 +26,25 @@ choose to go through some of the Optional Test Cases. 1. Open the [UnsafeJQueryPlugin query](https://github.com/github/codeql/blob/main/javascript/ql/src/Security/CWE-079/UnsafeJQueryPlugin.ql). 2. Run a MRVA against the following repo list: -``` -"test-repo-list": [ - "angular-cn/ng-nice", - "apache/hadoop", - "apache/hive" -] -``` + + ```json + { + "name": "test-repo-list", + "repositories": [ + "angular-cn/ng-nice", + "apache/hadoop", + "apache/hive" + ] + } + ``` + 3. Check that a notification message pops up and the results view is opened. 4. Check the query history. It should: - Show that an item has been added to the query history - The item should be marked as "in progress". 5. Once the query starts: - - Check the results view - - Check the code paths view, including the code paths drop down menu. + - Check the results view + - Check the code paths view, including the code paths drop down menu. - Check that the repository filter box works - Click links to files/locations on GitHub - Check that the query history item is updated to show the number of results @@ -48,9 +55,7 @@ choose to go through some of the Optional Test Cases. 1. Open the [ReDoS query](https://github.com/github/codeql/blob/main/javascript/ql/src/Performance/ReDoS.ql). 2. Run a MRVA against the "Top 10" repositories. -3. Check the notification message. It should: - - Show the number of repos that are going to be queried - - Provide a link to the actions workflow +3. Check that there is a notification message. 4. Check the query history. It should: - Show that an item has been added to the query history - The item should be marked as "in progress". @@ -73,22 +78,22 @@ choose to go through some of the Optional Test Cases. 1. Click a history item (for MRVA): - Check that exporting results works - Check that sorting results works - - Check that copying repo lists works -2. Open the query directory (containing results): + - Check that copying repo lists works +2. Open the query results directory: - Check that the correct directory is opened and there are results in it -3. Open variant analysis on GitHub +3. View logs - Check that the correct workflow is opened ### Test Case 5: MRVA - Canceling a variant analysis run Run one of the above MRVAs, but cancel it from within VS Code: - Check that the query is canceled and the query history item is updated. -- Check that the workflow run is also canceled. +- Check that the workflow run is also canceled. - Check that any available results are visible in VS Code. -### Test Case 6: MRVA - Change to a different colour theme +### Test Case 6: MRVA - Change to a different colour theme -Open one of the above MRVAs, try changing to a different colour theme and check that everything looks sensible. +Open one of the above MRVAs, try changing to a different colour theme and check that everything looks sensible. Are there any components that are not showing up? ## Optional Test Cases @@ -98,9 +103,10 @@ These are mostly aimed at MRVA, but some of them are also applicable to non-MRVA ### Selecting repositories to run on #### Test case 1: Running a query on a single repository -1. When the repository exists and is public - 1. Has a CodeQL database for the correct language - 2. Has a CodeQL database for another language + +1. When the repository exists and is public + 1. Has a CodeQL database for the correct language + 2. Has a CodeQL database for another language 3. Does not have any CodeQL databases 2. When the repository exists and is private 1. Is accessible and has a CodeQL database @@ -108,14 +114,16 @@ These are mostly aimed at MRVA, but some of them are also applicable to non-MRVA 3. When the repository does not exist #### Test case 2: Running a query on a custom repository list + 1. The repository list is non-empty - 1. All repositories in the list have a CodeQL database + 1. All repositories in the list have a CodeQL database 2. Some but not all repositories in the list have a CodeQL database 3. No repositories in the list have a CodeQL database 2. The repository list is empty #### Test case 3: Running a query on all repositories in an organization -1. The org exists + +1. The org exists 1. The org contains repositories that have CodeQL databases 2. The org contains repositories of the right language but without CodeQL databases 3. The org contains repositories not of the right language @@ -125,20 +133,25 @@ These are mostly aimed at MRVA, but some of them are also applicable to non-MRVA ### Using different types of controller repos #### Test case 1: Running a query when the controller repository is public + 1. Can run queries on public repositories 2. Can not run queries on private repositories #### Test case 2: Running a query when the controller repository is private + 1. Can run queries on public repositories 2. Can run queries on private repositories #### Test case 3: Running a query when the controller repo exists but you do not have write access + 1. Cannot run queries #### Test case 4: Running a query when the controller repo doesn’t exist + 1. Cannot run queries #### Test case 5: Running a query when the "config field" for the controller repo is not set + 1. Cannot run queries ### Query History @@ -149,6 +162,7 @@ The first test case specifies actions that you can do when the query is first ru with this since it has quite a limited number of actions you can do. #### Test case 1: When variant analysis state is "pending" + 1. Starts monitoring variant analysis 2. Cannot open query history item 3. Can delete a query history item @@ -159,10 +173,10 @@ with this since it has quite a limited number of actions you can do. 2. By query date 3. By result count 5. Cannot open query directory -6. Can open query that produced these results - 1. When the file still exists and has not moved +6. Can open query that produced these results + 1. When the file still exists and has not moved 2. When the file does not exist -7. Cannot open variant analysis on github +7. Cannot view logs 8. Cannot copy repository list 9. Cannot export results 10. Cannot select to create a gist @@ -170,6 +184,7 @@ with this since it has quite a limited number of actions you can do. 12. Cannot cancel analysis #### Test case 2: When the variant analysis state is not "pending" + 1. Query history is loaded when VSCode starts 2. Handles when action workflow was canceled while VSCode was closed 3. Can open query history item @@ -186,7 +201,7 @@ with this since it has quite a limited number of actions you can do. 7. Can open query that produced these results 1. When the file still exists and has not moved 2. When the file does not exist -8. Can open variant analysis on github +8. Can view logs 9. Can copy repository list 1. Text is copied to clipboard 2. Text is a valid repository list @@ -203,12 +218,14 @@ with this since it has quite a limited number of actions you can do. 4. A popup allows you to open the directory #### Test case 3: When variant analysis state is "in_progress" + 1. Starts monitoring variant analysis - 1. Ready results are downloaded -2. Can cancel analysis + 1. Ready results are downloaded +2. Can cancel analysis 1. Causes the actions run to be canceled #### Test case 4: When variant analysis state is in final state ("succeeded"/"failed"/"canceled") + 1. Stops monitoring variant analysis 1. All results are downloaded if state is succeeded 2. Otherwise, ready results are downloaded, if any are available @@ -219,6 +236,7 @@ with this since it has quite a limited number of actions you can do. This requires running a MRVA query and seeing the results view. #### Test case 1: When variant analysis state is "pending" + 1. Can open a results view 2. Results view opens automatically - When starting variant analysis run @@ -226,9 +244,10 @@ This requires running a MRVA query and seeing the results view. 3. Results view is empty #### Test case 2: When variant analysis state is not "pending" + 1. Can open a results view 2. Results view opens automatically - 1. When starting variant analysis run + 1. When starting variant analysis run 2. When VSCode opens (if view was open when VSCode was closed) 3. Can copy repository list 1. Text is copied to clipboard @@ -239,42 +258,84 @@ This requires running a MRVA query and seeing the results view. 6. Can open query file 1. When the file still exists and has not moved 2. When the file does not exist -7. Can open query text -8. Can sort repos - 1. By name - 2. By results - 3. By stars +7. Can open query text +8. Can sort repos + 1. By name + 2. By results + 3. By stars 4. By last updated 9. Can filter repos -10. Shows correct statistics - 1. Total number of results - 2. Total number of repositories +10. Shows correct statistics + 1. Total number of results + 2. Total number of repositories 3. Duration -11. Can see live results +11. Can see live results 1. Results appear in extension as soon as each query is completed 12. Can view interpreted results (i.e. for a "problem" query) - 1. Can view non-path results + 1. Can view non-path results 2. Can view code paths for "path-problem" queries 13. Can view raw results (i.e. for a non "problem" query) 1. Renders a table -14. Can see skipped repositories - 1. Can see repos with no db in a tab - 1. Shown warning that explains the tab +14. Can see skipped repositories + 1. Can see repos with no db in a tab + 1. Shown warning that explains the tab 2. Can see repos with no access in a tab - 1. Shown warning that explains the tab + 1. Shown warning that explains the tab 3. Only shows tab when there are skipped repos -15. Result downloads - 1. All results are downloaded automatically +15. Result downloads + 1. All results are downloaded automatically 2. Download status is indicated by a spinner (Not currently any indication of progress beyond "downloading" and "not downloading") - 3. Only 3 items are downloaded at a time - 4. Results for completed queries are still downloaded when - 1. Some but not all queries failed + 3. Only 3 items are downloaded at a time + 4. Results for completed queries are still downloaded when + 1. Some but not all queries failed 2. The variant analysis was canceled after some queries completed #### Test case 3: When variant analysis state is in "succeeded" state + 1. Can view logs -2. All results are downloaded +2. All results are downloaded #### Test case 4: When variant analysis is in "failed" or "canceled" state + 1. Can view logs 1. Results for finished queries are still downloaded. + +### MRVA repositories panel + +1. Add a list +1. Add a database at the top level +1. Add a database to a list +1. Add a the same database at a top-level and in a list +1. Delete a list +1. Delete a database from the top level +1. Delete a database from a list +1. Add an owner +1. Remove an owner +1. Rename a list +1. Open on GitHub +1. Select a list (via "Select" button and via context menu action) +1. Run MRVA against a user-defined list +1. Run MRVA against a top-N list +1. Run MRVA against an owner +1. Run MRVA against a database +1. Copy repo list +1. Open config file +1. Make changes via config file (ensure JSON schema is helping out) +1. Close and re-open VS Code (ensure lists are there) +1. Collapse/expand tree nodes + +Error cases that trigger an error notification: + +1. Try to add a list with a name that already exists +1. Try to add a top-level database that already exists +1. Try to add a database in a list that already exists in the list + +Error cases that show an error in the panel (and only the edit button should be visible): + +1. Edit the db config file directly and save invalid JSON +1. Edit the db config file directly and save valid JSON but invalid config (e.g. add an unknown property) +1. Edit the db config file directly and save two lists with the same name + +Cases where there the welcome view is shown: + +1. No controller repo is set in the user's settings JSON. diff --git a/extensions/ql-vscode/jest.config.js b/extensions/ql-vscode/jest.config.js index 68933e683..f06906060 100644 --- a/extensions/ql-vscode/jest.config.js +++ b/extensions/ql-vscode/jest.config.js @@ -8,6 +8,7 @@ module.exports = { projects: [ "/src/view", "/test/unit-tests", + "/test/vscode-tests/activated-extension", "/test/vscode-tests/cli-integration", "/test/vscode-tests/no-workspace", "/test/vscode-tests/minimal-workspace", diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 7ba55ebaf..0bac771ee 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-codeql", - "version": "1.7.9", + "version": "1.7.12", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "vscode-codeql", - "version": "1.7.9", + "version": "1.7.12", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 150ba3ac3..20580beee 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -4,7 +4,7 @@ "description": "CodeQL for Visual Studio Code", "author": "GitHub", "private": true, - "version": "1.7.9", + "version": "1.7.12", "publisher": "GitHub", "license": "MIT", "icon": "media/VS-marketplace-CodeQL-icon.png", @@ -294,22 +294,6 @@ "scope": "application", "description": "Specifies whether or not to write telemetry events to the extension log." }, - "codeQL.variantAnalysis.repositoryLists": { - "type": [ - "object", - null - ], - "patternProperties": { - ".*": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "default": null, - "markdownDescription": "[For internal use only] Lists of GitHub repositories that you want to run variant analysis against. This should be a JSON object where each key is a user-specified name for this repository list, and the value is an array of GitHub repositories (of the form `/`)." - }, "codeQL.variantAnalysis.controllerRepo": { "type": "string", "default": "", @@ -335,10 +319,18 @@ "command": "codeQL.runQuery", "title": "CodeQL: Run Query on Selected Database" }, + { + "command": "codeQL.runQueryContextEditor", + "title": "CodeQL: Run Query on Selected Database" + }, { "command": "codeQL.runQueryOnMultipleDatabases", "title": "CodeQL: Run Query on Multiple Databases" }, + { + "command": "codeQL.runQueryOnMultipleDatabasesContextEditor", + "title": "CodeQL: Run Query on Multiple Databases" + }, { "command": "codeQL.runVariantAnalysis", "title": "CodeQL: Run Variant Analysis" @@ -985,10 +977,18 @@ "command": "codeQL.runQuery", "when": "resourceLangId == ql && resourceExtname == .ql" }, + { + "command": "codeQL.runQueryContextEditor", + "when": "false" + }, { "command": "codeQL.runQueryOnMultipleDatabases", "when": "resourceLangId == ql && resourceExtname == .ql" }, + { + "command": "codeQL.runQueryOnMultipleDatabasesContextEditor", + "when": "false" + }, { "command": "codeQL.runVariantAnalysis", "when": "config.codeQL.canary && editorLangId == ql && resourceExtname == .ql" @@ -1240,11 +1240,11 @@ ], "editor/context": [ { - "command": "codeQL.runQuery", + "command": "codeQL.runQueryContextEditor", "when": "editorLangId == ql && resourceExtname == .ql" }, { - "command": "codeQL.runQueryOnMultipleDatabases", + "command": "codeQL.runQueryOnMultipleDatabasesContextEditor", "when": "editorLangId == ql && resourceExtname == .ql" }, { @@ -1295,7 +1295,7 @@ { "id": "codeQLVariantAnalysisRepositories", "name": "Variant Analysis Repositories", - "when": "config.codeQL.canary && config.codeQL.variantAnalysis.repositoriesPanel" + "when": "config.codeQL.canary" }, { "id": "codeQLQueryHistory", @@ -1339,13 +1339,14 @@ "scripts": { "build": "gulp", "watch": "gulp watch", - "test": "npm-run-all -p test:*", + "test": "npm-run-all test:*", "test:unit": "cross-env TZ=UTC LANG=en-US jest --projects test/unit-tests", "test:view": "jest --projects src/view", - "integration": "npm-run-all integration:*", - "integration:no-workspace": "jest --projects test/vscode-tests/no-workspace", - "integration:minimal-workspace": "jest --projects test/vscode-tests/minimal-workspace", - "cli-integration": "jest --projects test/vscode-tests/cli-integration", + "test:vscode-integration": "npm-run-all test:vscode-integration:*", + "test:vscode-integration:activated-extension": "jest --projects test/vscode-tests/activated-extension", + "test:vscode-integration:no-workspace": "jest --projects test/vscode-tests/no-workspace", + "test:vscode-integration:minimal-workspace": "jest --projects test/vscode-tests/minimal-workspace", + "test:cli-integration": "jest --projects test/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 .js,.ts,.tsx --max-warnings=0", diff --git a/extensions/ql-vscode/scripts/add-fields-to-scenarios.ts b/extensions/ql-vscode/scripts/add-fields-to-scenarios.ts index 4a5e7b533..f80e20ceb 100644 --- a/extensions/ql-vscode/scripts/add-fields-to-scenarios.ts +++ b/extensions/ql-vscode/scripts/add-fields-to-scenarios.ts @@ -20,8 +20,8 @@ import { throttling } from "@octokit/plugin-throttling"; import { getFiles } from "./util/files"; import type { GitHubApiRequest } from "../src/mocks/gh-api-request"; 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"; +import { VariantAnalysis } from "../src/variant-analysis/gh-api/variant-analysis"; +import { RepositoryWithMetadata } from "../src/variant-analysis/gh-api/repository"; const extensionDirectory = resolve(__dirname, ".."); const scenariosDirectory = resolve(extensionDirectory, "src/mocks/scenarios"); diff --git a/extensions/ql-vscode/scripts/lint-scenarios.ts b/extensions/ql-vscode/scripts/lint-scenarios.ts index 2ce4f0b4d..46739e0e5 100644 --- a/extensions/ql-vscode/scripts/lint-scenarios.ts +++ b/extensions/ql-vscode/scripts/lint-scenarios.ts @@ -28,7 +28,7 @@ async function lintScenarios() { throw new Error(`Invalid schema: ${ajv.errorsText()}`); } - const validate = await ajv.compile(schema); + const validate = ajv.compile(schema); let invalidFiles = 0; diff --git a/extensions/ql-vscode/src/abstract-webview.ts b/extensions/ql-vscode/src/abstract-webview.ts index c6ad5ab4c..dd50a03f5 100644 --- a/extensions/ql-vscode/src/abstract-webview.ts +++ b/extensions/ql-vscode/src/abstract-webview.ts @@ -24,6 +24,7 @@ export type WebviewPanelConfig = { view: WebviewView; preserveFocus?: boolean; additionalOptions?: WebviewPanelOptions & WebviewOptions; + allowWasmEval?: boolean; }; export abstract class AbstractWebview< @@ -116,6 +117,7 @@ export abstract class AbstractWebview< config.view, { allowInlineStyles: true, + allowWasmEval: !!config.allowWasmEval, }, ); this.push( diff --git a/extensions/ql-vscode/src/astViewer.ts b/extensions/ql-vscode/src/astViewer.ts index 20ed66145..3c5d2bdf8 100644 --- a/extensions/ql-vscode/src/astViewer.ts +++ b/extensions/ql-vscode/src/astViewer.ts @@ -15,7 +15,7 @@ import { } from "vscode"; import { basename } from "path"; -import { DatabaseItem } from "./databases"; +import { DatabaseItem } from "./local-databases"; import { UrlValue, BqrsId } from "./pure/bqrs-cli-types"; import { showLocation } from "./interface-utils"; import { diff --git a/extensions/ql-vscode/src/cli.ts b/extensions/ql-vscode/src/cli.ts index 177f8df01..321357da1 100644 --- a/extensions/ql-vscode/src/cli.ts +++ b/extensions/ql-vscode/src/cli.ts @@ -26,8 +26,9 @@ import { QueryMetadata, SortDirection } from "./pure/interface-types"; import { Logger, ProgressReporter } from "./common"; import { CompilationMessage } from "./pure/legacy-messages"; import { sarifParser } from "./sarif-parser"; -import { dbSchemeToLanguage, walkDirectory } from "./helpers"; +import { walkDirectory } from "./helpers"; import { App } from "./common/app"; +import { QueryLanguage } from "./common/query-language"; /** * The version of the SARIF format that we are using. @@ -123,14 +124,6 @@ export interface SourceInfo { */ export type ResolvedTests = string[]; -/** - * Options for `codeql test run`. - */ -export interface TestRunOptions { - cancellationToken?: CancellationToken; - logger?: Logger; -} - /** * Event fired by `codeql test run`. */ @@ -295,7 +288,7 @@ export class CodeQLCliServer implements Disposable { ); } - return await spawnServer( + return spawnServer( codeQlPath, "CodeQL CLI Server", ["execute", "cli-server"], @@ -455,7 +448,7 @@ export class CodeQLCliServer implements Disposable { void logStream(child.stderr!, logger); } - for await (const event of await splitStreamAtSeparators(child.stdout!, [ + for await (const event of splitStreamAtSeparators(child.stdout!, [ "\0", ])) { yield event; @@ -484,10 +477,15 @@ export class CodeQLCliServer implements Disposable { command: string[], commandArgs: string[], description: string, - cancellationToken?: CancellationToken, - logger?: Logger, + { + cancellationToken, + logger, + }: { + cancellationToken?: CancellationToken; + logger?: Logger; + } = {}, ): AsyncGenerator { - for await (const event of await this.runAsyncCodeQlCliCommandInternal( + for await (const event of this.runAsyncCodeQlCliCommandInternal( command, commandArgs, cancellationToken, @@ -518,8 +516,13 @@ export class CodeQLCliServer implements Disposable { command: string[], commandArgs: string[], description: string, - progressReporter?: ProgressReporter, - onLine?: OnLineCallback, + { + progressReporter, + onLine, + }: { + progressReporter?: ProgressReporter; + onLine?: OnLineCallback; + } = {}, ): Promise { if (progressReporter) { progressReporter.report({ message: description }); @@ -563,22 +566,25 @@ export class CodeQLCliServer implements Disposable { command: string[], commandArgs: string[], description: string, - addFormat = true, - progressReporter?: ProgressReporter, - onLine?: OnLineCallback, + { + addFormat = true, + progressReporter, + onLine, + }: { + addFormat?: boolean; + progressReporter?: ProgressReporter; + onLine?: OnLineCallback; + } = {}, ): Promise { let args: string[] = []; if (addFormat) // Add format argument first, in case commandArgs contains positional parameters. args = args.concat(["--format", "json"]); args = args.concat(commandArgs); - const result = await this.runCodeQlCliCommand( - command, - args, - description, + const result = await this.runCodeQlCliCommand(command, args, description, { progressReporter, onLine, - ); + }); try { return JSON.parse(result) as OutputType; } catch (err) { @@ -616,8 +622,13 @@ export class CodeQLCliServer implements Disposable { command: string[], commandArgs: string[], description: string, - addFormat = true, - progressReporter?: ProgressReporter, + { + addFormat, + progressReporter, + }: { + addFormat?: boolean; + progressReporter?: ProgressReporter; + } = {}, ): Promise { const accessToken = await this.app.credentials.getExistingAccessToken(); @@ -627,24 +638,26 @@ export class CodeQLCliServer implements Disposable { command, [...extraArgs, ...commandArgs], description, - addFormat, - progressReporter, - async (line) => { - if (line.startsWith("Enter value for --github-auth-stdin")) { - try { - return await this.app.credentials.getAccessToken(); - } catch (e) { - // If the user cancels the authentication prompt, we still need to give a value to the CLI. - // By giving a potentially invalid value, the user will just get a 401/403 when they try to access a - // private package and the access token is invalid. - // This code path is very rare to hit. It would only be hit if the user is logged in when - // starting the command, then logging out before the getAccessToken() is called again and - // then cancelling the authentication prompt. - return accessToken; + { + addFormat, + progressReporter, + onLine: async (line) => { + if (line.startsWith("Enter value for --github-auth-stdin")) { + try { + return await this.app.credentials.getAccessToken(); + } catch (e) { + // If the user cancels the authentication prompt, we still need to give a value to the CLI. + // By giving a potentially invalid value, the user will just get a 401/403 when they try to access a + // private package and the access token is invalid. + // This code path is very rare to hit. It would only be hit if the user is logged in when + // starting the command, then logging out before the getAccessToken() is called again and + // then cancelling the authentication prompt. + return accessToken; + } } - } - return undefined; + return undefined; + }, }, ); } @@ -713,7 +726,9 @@ export class CodeQLCliServer implements Disposable { ["resolve", "qlref"], subcommandArgs, "Resolving qlref", - false, + { + addFormat: false, + }, ); } @@ -741,7 +756,13 @@ export class CodeQLCliServer implements Disposable { public async *runTests( testPaths: string[], workspaces: string[], - options: TestRunOptions, + { + cancellationToken, + logger, + }: { + cancellationToken?: CancellationToken; + logger?: Logger; + }, ): AsyncGenerator { const subcommandArgs = this.cliConfig.additionalTestArguments.concat([ ...this.getAdditionalPacksArg(workspaces), @@ -750,12 +771,14 @@ export class CodeQLCliServer implements Disposable { ...testPaths, ]); - for await (const event of await this.runAsyncCodeQlCliCommand( + for await (const event of this.runAsyncCodeQlCliCommand( ["test", "run"], subcommandArgs, "Run CodeQL Tests", - options.cancellationToken, - options.logger, + { + cancellationToken, + logger, + }, )) { yield event; } @@ -786,7 +809,9 @@ export class CodeQLCliServer implements Disposable { ["resolve", "ml-models"], args, "Resolving ML models", - false, + { + addFormat: false, + }, ); } @@ -810,8 +835,9 @@ export class CodeQLCliServer implements Disposable { ["resolve", "ram"], args, "Resolving RAM settings", - true, - progressReporter, + { + progressReporter, + }, ); } /** @@ -1186,9 +1212,11 @@ export class CodeQLCliServer implements Disposable { */ public async getSupportedLanguages(): Promise { if (!this._supportedLanguages) { - // Get the intersection of resolveLanguages with the list of hardcoded languages in dbSchemeToLanguage. + // Get the intersection of resolveLanguages with the list of languages in QueryLanguage. const resolvedLanguages = Object.keys(await this.resolveLanguages()); - const hardcodedLanguages = Object.values(dbSchemeToLanguage); + const hardcodedLanguages = Object.values(QueryLanguage).map((s) => + s.toString(), + ); this._supportedLanguages = resolvedLanguages.filter((lang) => hardcodedLanguages.includes(lang), @@ -1224,6 +1252,26 @@ export class CodeQLCliServer implements Disposable { ); } + /** + * Adds a core language QL library pack for the given query language as a dependency + * of the current package, and then installs them. This command modifies the qlpack.yml + * file of the current package. Formatting and comments will be removed. + * @param dir The directory where QL pack exists. + * @param language The language of the QL pack. + */ + async packAdd(dir: string, queryLanguage: QueryLanguage) { + const args = ["--dir", dir]; + args.push(`codeql/${queryLanguage}-all`); + return this.runJsonCodeQlCliCommandWithAuthentication( + ["pack", "add"], + args, + `Adding and installing ${queryLanguage} pack dependency.`, + { + addFormat: false, + }, + ); + } + /** * Downloads a specified pack. * @param packs The `` of the packs to download. @@ -1562,7 +1610,7 @@ const lineEndings = ["\r\n", "\r", "\n"]; * @param logger The logger that will consume the stream output. */ async function logStream(stream: Readable, logger: Logger): Promise { - for await (const line of await splitStreamAtSeparators(stream, lineEndings)) { + for await (const line of splitStreamAtSeparators(stream, lineEndings)) { // Await the result of log here in order to ensure the logs are written in the correct order. await logger.log(line); } diff --git a/extensions/ql-vscode/src/common/query-language.ts b/extensions/ql-vscode/src/common/query-language.ts new file mode 100644 index 000000000..acbe09018 --- /dev/null +++ b/extensions/ql-vscode/src/common/query-language.ts @@ -0,0 +1,37 @@ +export enum QueryLanguage { + CSharp = "csharp", + Cpp = "cpp", + Go = "go", + Java = "java", + Javascript = "javascript", + Python = "python", + Ruby = "ruby", + Swift = "swift", +} + +export const PACKS_BY_QUERY_LANGUAGE = { + [QueryLanguage.Cpp]: ["codeql/cpp-queries"], + [QueryLanguage.CSharp]: [ + "codeql/csharp-queries", + "codeql/csharp-solorigate-queries", + ], + [QueryLanguage.Go]: ["codeql/go-queries"], + [QueryLanguage.Java]: ["codeql/java-queries"], + [QueryLanguage.Javascript]: [ + "codeql/javascript-queries", + "codeql/javascript-experimental-atm-queries", + ], + [QueryLanguage.Python]: ["codeql/python-queries"], + [QueryLanguage.Ruby]: ["codeql/ruby-queries"], +}; + +export const dbSchemeToLanguage = { + "semmlecode.javascript.dbscheme": "javascript", + "semmlecode.cpp.dbscheme": "cpp", + "semmlecode.dbscheme": "java", + "semmlecode.python.dbscheme": "python", + "semmlecode.csharp.dbscheme": "csharp", + "go.dbscheme": "go", + "ruby.dbscheme": "ruby", + "swift.dbscheme": "swift", +}; diff --git a/extensions/ql-vscode/src/compare/compare-view.ts b/extensions/ql-vscode/src/compare/compare-view.ts index 1d31ccb29..46c35562a 100644 --- a/extensions/ql-vscode/src/compare/compare-view.ts +++ b/extensions/ql-vscode/src/compare/compare-view.ts @@ -7,7 +7,7 @@ import { } from "../pure/interface-types"; import { Logger } from "../common"; import { CodeQLCliServer } from "../cli"; -import { DatabaseManager } from "../databases"; +import { DatabaseManager } from "../local-databases"; import { jumpToLocation } from "../interface-utils"; import { transformBqrsResultSet, diff --git a/extensions/ql-vscode/src/config.ts b/extensions/ql-vscode/src/config.ts index 85f61ec11..5284da6c0 100644 --- a/extensions/ql-vscode/src/config.ts +++ b/extensions/ql-vscode/src/config.ts @@ -83,15 +83,6 @@ export const GLOBAL_ENABLE_TELEMETRY = new Setting( GLOBAL_TELEMETRY_SETTING, ); -const ENABLE_NEW_TELEMETRY = new Setting( - "enableNewTelemetry", - TELEMETRY_SETTING, -); - -export function newTelemetryEnabled(): boolean { - return ENABLE_NEW_TELEMETRY.getValue(); -} - // Distribution configuration const DISTRIBUTION_SETTING = new Setting("cli", ROOT_SETTING); export const CUSTOM_CODEQL_PATH_SETTING = new Setting( @@ -498,48 +489,6 @@ export const NO_CACHE_AST_VIEWER = new Setting( // Settings for variant analysis const VARIANT_ANALYSIS_SETTING = new Setting("variantAnalysis", ROOT_SETTING); -/** - * Lists of GitHub repositories that you want to query remotely via the "Run Variant Analysis" command. - * Note: This command is only available for internal users. - * - * This setting should be a JSON object where each key is a user-specified name (string), - * and the value is an array of GitHub repositories (of the form `/`). - */ -const REMOTE_REPO_LISTS = new Setting( - "repositoryLists", - VARIANT_ANALYSIS_SETTING, -); - -export function getRemoteRepositoryLists(): - | Record - | undefined { - return REMOTE_REPO_LISTS.getValue>() || undefined; -} - -export async function setRemoteRepositoryLists( - lists: Record | undefined, -) { - await REMOTE_REPO_LISTS.updateValue(lists, ConfigurationTarget.Global); -} - -/** - * Path to a file that contains lists of GitHub repositories that you want to query remotely via - * the "Run Variant Analysis" command. - * Note: This command is only available for internal users. - * - * This setting should be a path to a JSON file that contains a JSON object where each key is a - * user-specified name (string), and the value is an array of GitHub repositories - * (of the form `/`). - */ -const REPO_LISTS_PATH = new Setting( - "repositoryListsPath", - VARIANT_ANALYSIS_SETTING, -); - -export function getRemoteRepositoryListsPath(): string | undefined { - return REPO_LISTS_PATH.getValue() || undefined; -} - /** * The name of the "controller" repository that you want to use with the "Run Variant Analysis" command. * Note: This command is only available for internal users. @@ -599,18 +548,6 @@ export function isVariantAnalysisLiveResultsEnabled(): boolean { return true; } -/** - * A flag indicating whether to use the new "variant analysis repositories" panel. - */ -const VARIANT_ANALYSIS_REPOS_PANEL = new Setting( - "repositoriesPanel", - VARIANT_ANALYSIS_SETTING, -); - -export function isVariantAnalysisReposPanelEnabled(): boolean { - return !!VARIANT_ANALYSIS_REPOS_PANEL.getValue(); -} - // Settings for mocking the GitHub API. const MOCK_GH_API_SERVER = new Setting("mockGitHubApiServer", ROOT_SETTING); diff --git a/extensions/ql-vscode/src/contextual/astBuilder.ts b/extensions/ql-vscode/src/contextual/astBuilder.ts index 88cd873f7..16416f07a 100644 --- a/extensions/ql-vscode/src/contextual/astBuilder.ts +++ b/extensions/ql-vscode/src/contextual/astBuilder.ts @@ -1,6 +1,6 @@ import { CodeQLCliServer } from "../cli"; import { DecodedBqrsChunk, BqrsId, EntityValue } from "../pure/bqrs-cli-types"; -import { DatabaseItem } from "../databases"; +import { DatabaseItem } from "../local-databases"; import { ChildAstItem, AstItem } from "../astViewer"; import fileRangeFromURI from "./fileRangeFromURI"; import { Uri } from "vscode"; diff --git a/extensions/ql-vscode/src/contextual/fileRangeFromURI.ts b/extensions/ql-vscode/src/contextual/fileRangeFromURI.ts index e242d0344..47f045206 100644 --- a/extensions/ql-vscode/src/contextual/fileRangeFromURI.ts +++ b/extensions/ql-vscode/src/contextual/fileRangeFromURI.ts @@ -2,7 +2,7 @@ import * as vscode from "vscode"; import { UrlValue, LineColumnLocation } from "../pure/bqrs-cli-types"; import { isEmptyPath } from "../pure/bqrs-utils"; -import { DatabaseItem } from "../databases"; +import { DatabaseItem } from "../local-databases"; export default function fileRangeFromURI( uri: UrlValue | undefined, diff --git a/extensions/ql-vscode/src/contextual/locationFinder.ts b/extensions/ql-vscode/src/contextual/locationFinder.ts index 8809945bc..86af86211 100644 --- a/extensions/ql-vscode/src/contextual/locationFinder.ts +++ b/extensions/ql-vscode/src/contextual/locationFinder.ts @@ -9,7 +9,7 @@ import { ResultSetSchema, } from "../pure/bqrs-cli-types"; import { CodeQLCliServer } from "../cli"; -import { DatabaseManager, DatabaseItem } from "../databases"; +import { DatabaseManager, DatabaseItem } from "../local-databases"; import fileRangeFromURI from "./fileRangeFromURI"; import { ProgressCallback } from "../commandRunner"; import { KeyType } from "./keyType"; diff --git a/extensions/ql-vscode/src/contextual/queryResolver.ts b/extensions/ql-vscode/src/contextual/queryResolver.ts index ca7b3ab61..550d1b6b4 100644 --- a/extensions/ql-vscode/src/contextual/queryResolver.ts +++ b/extensions/ql-vscode/src/contextual/queryResolver.ts @@ -12,7 +12,7 @@ import { } from "../helpers"; import { KeyType, kindOfKeyType, nameOfKeyType, tagOfKeyType } from "./keyType"; import { CodeQLCliServer } from "../cli"; -import { DatabaseItem } from "../databases"; +import { DatabaseItem } from "../local-databases"; import { extLogger } from "../common"; import { createInitialQueryInfo } from "../run-queries-shared"; import { CancellationToken, Uri } from "vscode"; diff --git a/extensions/ql-vscode/src/contextual/templateProvider.ts b/extensions/ql-vscode/src/contextual/templateProvider.ts index 6ec766513..448d36732 100644 --- a/extensions/ql-vscode/src/contextual/templateProvider.ts +++ b/extensions/ql-vscode/src/contextual/templateProvider.ts @@ -17,7 +17,7 @@ import { zipArchiveScheme, } from "../archive-filesystem-provider"; import { CodeQLCliServer } from "../cli"; -import { DatabaseManager } from "../databases"; +import { DatabaseManager } from "../local-databases"; import { CachedOperation } from "../helpers"; import { ProgressCallback, withProgress } from "../commandRunner"; import AstBuilder from "./astBuilder"; diff --git a/extensions/ql-vscode/src/databaseFetcher.ts b/extensions/ql-vscode/src/databaseFetcher.ts index dd944ba81..1e31e7857 100644 --- a/extensions/ql-vscode/src/databaseFetcher.ts +++ b/extensions/ql-vscode/src/databaseFetcher.ts @@ -16,7 +16,7 @@ import { basename, join } from "path"; import * as Octokit from "@octokit/rest"; import { retry } from "@octokit/plugin-retry"; -import { DatabaseManager, DatabaseItem } from "./databases"; +import { DatabaseManager, DatabaseItem } from "./local-databases"; import { showAndLogInformationMessage, tmpDir } from "./helpers"; import { reportStreamProgress, ProgressCallback } from "./commandRunner"; import { extLogger } from "./common"; diff --git a/extensions/ql-vscode/src/databases/README.md b/extensions/ql-vscode/src/databases/README.md index b7ffdc5e9..2bc4c9780 100644 --- a/extensions/ql-vscode/src/databases/README.md +++ b/extensions/ql-vscode/src/databases/README.md @@ -1,3 +1,3 @@ ### Databases -This folder contains code for the new experimental databases panel and new query run experience. +This folder contains tests for the variant analysis repository panel. diff --git a/extensions/ql-vscode/src/databases/config/db-config-store.ts b/extensions/ql-vscode/src/databases/config/db-config-store.ts index 5507402ae..54a3c549d 100644 --- a/extensions/ql-vscode/src/databases/config/db-config-store.ts +++ b/extensions/ql-vscode/src/databases/config/db-config-store.ts @@ -15,6 +15,7 @@ import { renameRemoteList, SelectedDbItem, DB_CONFIG_VERSION, + SelectedDbItemKind, } from "./db-config"; import * as chokidar from "chokidar"; import { DisposableObject, DisposeHandler } from "../../pure/disposable-object"; @@ -472,6 +473,10 @@ export class DbConfigStore extends DisposableObject { databases: [], }, }, + selected: { + kind: SelectedDbItemKind.VariantAnalysisSystemDefinedList, + listName: "top_10", + }, }; } diff --git a/extensions/ql-vscode/src/databases/config/db-config-validator.ts b/extensions/ql-vscode/src/databases/config/db-config-validator.ts index dbfe1789c..e953c8508 100644 --- a/extensions/ql-vscode/src/databases/config/db-config-validator.ts +++ b/extensions/ql-vscode/src/databases/config/db-config-validator.ts @@ -1,30 +1,30 @@ import { readJsonSync } from "fs-extra"; import { resolve } from "path"; -import Ajv from "ajv"; +import Ajv, { ValidateFunction } from "ajv"; import { clearLocalDbConfig, DbConfig } from "./db-config"; -import { findDuplicateStrings } from "../../text-utils"; +import { findDuplicateStrings } from "../../pure/text-utils"; import { DbConfigValidationError, DbConfigValidationErrorKind, } from "../db-validation-errors"; export class DbConfigValidator { - private readonly schema: any; + private readonly validateSchemaFn: ValidateFunction; constructor(extensionPath: string) { const schemaPath = resolve(extensionPath, "databases-schema.json"); - this.schema = readJsonSync(schemaPath); + const schema = readJsonSync(schemaPath); + const schemaValidator = new Ajv({ allErrors: true }); + this.validateSchemaFn = schemaValidator.compile(schema); } public validate(dbConfig: DbConfig): DbConfigValidationError[] { - const ajv = new Ajv({ allErrors: true }); - const localDbs = clearLocalDbConfig(dbConfig); - ajv.validate(this.schema, dbConfig); + this.validateSchemaFn(dbConfig); - if (ajv.errors) { - return ajv.errors.map((error) => ({ + if (this.validateSchemaFn.errors) { + return this.validateSchemaFn.errors.map((error) => ({ kind: DbConfigValidationErrorKind.InvalidConfig, message: `${error.instancePath} ${error.message}`, })); diff --git a/extensions/ql-vscode/src/databases/db-module.ts b/extensions/ql-vscode/src/databases/db-module.ts index 8f71528ce..f81b42d25 100644 --- a/extensions/ql-vscode/src/databases/db-module.ts +++ b/extensions/ql-vscode/src/databases/db-module.ts @@ -6,7 +6,7 @@ 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, isVariantAnalysisReposPanelEnabled } from "../config"; +import { isCanary } from "../config"; export class DbModule extends DisposableObject { public readonly dbManager: DbManager; @@ -36,7 +36,7 @@ export class DbModule extends DisposableObject { return true; } - return isCanary() && isVariantAnalysisReposPanelEnabled(); + return isCanary(); } private async initialize(app: App): Promise { diff --git a/extensions/ql-vscode/src/databases/ui/db-panel.ts b/extensions/ql-vscode/src/databases/ui/db-panel.ts index 0300bb494..f9eccc5fc 100644 --- a/extensions/ql-vscode/src/databases/ui/db-panel.ts +++ b/extensions/ql-vscode/src/databases/ui/db-panel.ts @@ -29,7 +29,7 @@ import { DbManager } from "../db-manager"; import { DbTreeDataProvider } from "./db-tree-data-provider"; import { DbTreeViewItem } from "./db-tree-view-item"; import { getGitHubUrl } from "./db-tree-view-item-action"; -import { getControllerRepo } from "../../remote-queries/run-remote-query"; +import { getControllerRepo } from "../../variant-analysis/run-remote-query"; import { getErrorMessage } from "../../pure/helpers-pure"; import { Credentials } from "../../common/authentication"; diff --git a/extensions/ql-vscode/src/distribution.ts b/extensions/ql-vscode/src/distribution.ts index e9a84c40c..119c82244 100644 --- a/extensions/ql-vscode/src/distribution.ts +++ b/extensions/ql-vscode/src/distribution.ts @@ -315,6 +315,15 @@ class ExtensionSpecificDistributionManager { const extensionSpecificRelease = this.getInstalledRelease(); const latestRelease = await this.getLatestRelease(); + // v2.12.3 was released with a bug that causes the extension to fail + // so we force the extension to ignore it. + if ( + extensionSpecificRelease && + extensionSpecificRelease.name === "v2.12.3" + ) { + return createUpdateAvailableResult(latestRelease); + } + if ( extensionSpecificRelease !== undefined && codeQlPath !== undefined && @@ -430,6 +439,12 @@ class ExtensionSpecificDistributionManager { this.versionRange, this.config.includePrerelease, (release) => { + // v2.12.3 was released with a bug that causes the extension to fail + // so we force the extension to ignore it. + if (release.name === "v2.12.3") { + return false; + } + const matchingAssets = release.assets.filter( (asset) => asset.name === requiredAssetName, ); diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index ea68753fa..f01a926f1 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -10,7 +10,6 @@ import { languages, ProgressLocation, ProgressOptions, - ProviderResult, QuickPickItem, Range, Uri, @@ -44,8 +43,8 @@ import { QueryServerConfigListener, } from "./config"; import { install } from "./languageSupport"; -import { DatabaseItem, DatabaseManager } from "./databases"; -import { DatabaseUI } from "./databases-ui"; +import { DatabaseItem, DatabaseManager } from "./local-databases"; +import { DatabaseUI } from "./local-databases-ui"; import { TemplatePrintAstProvider, TemplatePrintCfgProvider, @@ -73,7 +72,12 @@ import { tmpDir, tmpDirDisposal, } from "./helpers"; -import { asError, assertNever, getErrorMessage } from "./pure/helpers-pure"; +import { + asError, + assertNever, + getErrorMessage, + getErrorStack, +} from "./pure/helpers-pure"; import { spawnIdeServer } from "./ide-server"; import { ResultsView } from "./interface"; import { WebviewReveal } from "./interface-utils"; @@ -101,16 +105,15 @@ import { withProgress, } from "./commandRunner"; import { CodeQlStatusBarHandler } from "./status-bar"; -import { URLSearchParams } from "url"; import { handleDownloadPacks, handleInstallPackDependencies, } from "./packaging"; import { HistoryItemLabelProvider } from "./query-history/history-item-label-provider"; import { - exportSelectedRemoteQueryResults, + exportSelectedVariantAnalysisResults, exportVariantAnalysisResults, -} from "./remote-queries/export-results"; +} from "./variant-analysis/export-results"; import { EvalLogViewer } from "./eval-log-viewer"; import { SummaryLanguageSupport } from "./log-insights/summary-language-support"; import { JoinOrderScannerProvider } from "./log-insights/join-order"; @@ -119,20 +122,21 @@ import { createInitialQueryInfo } from "./run-queries-shared"; import { LegacyQueryRunner } from "./legacy-query-server/legacyRunner"; import { NewQueryRunner } from "./query-server/query-runner"; import { QueryRunner } from "./queryRunner"; -import { VariantAnalysisView } from "./remote-queries/variant-analysis-view"; -import { VariantAnalysisViewSerializer } from "./remote-queries/variant-analysis-view-serializer"; +import { VariantAnalysisView } from "./variant-analysis/variant-analysis-view"; +import { VariantAnalysisViewSerializer } from "./variant-analysis/variant-analysis-view-serializer"; import { VariantAnalysis, VariantAnalysisScannedRepository, -} from "./remote-queries/shared/variant-analysis"; -import { VariantAnalysisManager } from "./remote-queries/variant-analysis-manager"; -import { createVariantAnalysisContentProvider } from "./remote-queries/variant-analysis-content-provider"; +} from "./variant-analysis/shared/variant-analysis"; +import { VariantAnalysisManager } from "./variant-analysis/variant-analysis-manager"; +import { createVariantAnalysisContentProvider } from "./variant-analysis/variant-analysis-content-provider"; import { VSCodeMockGitHubApiServer } from "./mocks/vscode-mock-gh-api-server"; -import { VariantAnalysisResultsManager } from "./remote-queries/variant-analysis-results-manager"; +import { VariantAnalysisResultsManager } from "./variant-analysis/variant-analysis-results-manager"; import { ExtensionApp } from "./common/vscode/vscode-app"; import { RepositoriesFilterSortStateWithIds } from "./pure/variant-analysis-filter-sort"; import { DbModule } from "./databases/db-module"; import { redactableError } from "./pure/errors"; +import { QueryHistoryDirs } from "./query-history/query-history-dirs"; /** * extension.ts @@ -236,6 +240,7 @@ export async function activate( const distributionConfigListener = new DistributionConfigListener(); await initializeLogging(ctx); await initializeTelemetry(extension, ctx); + addUnhandledRejectionListener(); install(); const codelensProvider = new QuickEvalCodeLensProvider(); @@ -462,7 +467,7 @@ export async function activate( ) { registerErrorStubs([checkForUpdatesCommand], (command) => async () => { const installActionName = "Install CodeQL CLI"; - const chosenAction = await void showAndLogErrorMessage( + const chosenAction = await showAndLogErrorMessage( `Can't execute ${command}: missing CodeQL CLI.`, { items: [installActionName], @@ -639,7 +644,7 @@ async function activateWithInstalledDistribution( cliServer, variantAnalysisStorageDir, variantAnalysisResultsManager, - dbModule?.dbManager, // the dbModule is only needed when variantAnalysisReposPanel is enabled + dbModule?.dbManager, ); ctx.subscriptions.push(variantAnalysisManager); ctx.subscriptions.push(variantAnalysisResultsManager); @@ -651,13 +656,18 @@ async function activateWithInstalledDistribution( ); void extLogger.log("Initializing query history."); + const queryHistoryDirs: QueryHistoryDirs = { + localQueriesDirPath: queryStorageDir, + variantAnalysesDirPath: variantAnalysisStorageDir, + }; + const qhm = new QueryHistoryManager( qs, dbm, localQueryResultsView, variantAnalysisManager, evalLogViewer, - queryStorageDir, + queryHistoryDirs, ctx, queryHistoryConfigurationListener, labelProvider, @@ -773,6 +783,74 @@ async function activateWithInstalledDistribution( } } + async function compileAndRunQueryOnMultipleDatabases( + progress: ProgressCallback, + token: CancellationToken, + uri: Uri | undefined, + ): Promise { + let filteredDBs = dbm.databaseItems; + if (filteredDBs.length === 0) { + void showAndLogErrorMessage( + "No databases found. Please add a suitable database to your workspace.", + ); + return; + } + // If possible, only show databases with the right language (otherwise show all databases). + const queryLanguage = await findLanguage(cliServer, uri); + if (queryLanguage) { + filteredDBs = dbm.databaseItems.filter( + (db) => db.language === queryLanguage, + ); + if (filteredDBs.length === 0) { + void showAndLogErrorMessage( + `No databases found for language ${queryLanguage}. Please add a suitable database to your workspace.`, + ); + return; + } + } + const quickPickItems = filteredDBs.map((dbItem) => ({ + databaseItem: dbItem, + label: dbItem.name, + description: dbItem.language, + })); + /** + * Databases that were selected in the quick pick menu. + */ + const quickpick = await window.showQuickPick( + quickPickItems, + { canPickMany: true, ignoreFocusOut: true }, + ); + if (quickpick !== undefined) { + // Collect all skipped databases and display them at the end (instead of popping up individual errors) + const skippedDatabases = []; + const errors = []; + for (const item of quickpick) { + try { + await compileAndRunQuery( + false, + uri, + progress, + token, + item.databaseItem, + ); + } catch (e) { + skippedDatabases.push(item.label); + errors.push(getErrorMessage(e)); + } + } + if (skippedDatabases.length > 0) { + void extLogger.log(`Errors:\n${errors.join("\n")}`); + void showAndLogWarningMessage( + `The following databases were skipped:\n${skippedDatabases.join( + "\n", + )}.\nFor details about the errors, see the logs.`, + ); + } + } else { + void showAndLogErrorMessage("No databases selected."); + } + } + const qhelpTmpDir = dirSync({ prefix: "qhelp_", keep: false, @@ -821,6 +899,7 @@ async function activateWithInstalledDistribution( void extLogger.log("Initializing CodeQL language server."); const client = new LanguageClient( + "codeQL.lsp", "CodeQL Language Server", () => spawnIdeServer(qlConfigurationListener), { @@ -873,6 +952,25 @@ async function activateWithInstalledDistribution( queryServerLogger, ), ); + + // Since we are tracking extension usage through commands, this command mirrors the runQuery command + ctx.subscriptions.push( + commandRunnerWithProgress( + "codeQL.runQueryContextEditor", + async ( + progress: ProgressCallback, + token: CancellationToken, + uri: Uri | undefined, + ) => await compileAndRunQuery(false, uri, progress, token, undefined), + { + title: "Running query", + cancellable: true, + }, + + // Open the query server logger on error since that's usually where the interesting errors appear. + queryServerLogger, + ), + ); interface DatabaseQuickPickItem extends QuickPickItem { databaseItem: DatabaseItem; } @@ -883,71 +981,22 @@ async function activateWithInstalledDistribution( progress: ProgressCallback, token: CancellationToken, uri: Uri | undefined, - ) => { - let filteredDBs = dbm.databaseItems; - if (filteredDBs.length === 0) { - void showAndLogErrorMessage( - "No databases found. Please add a suitable database to your workspace.", - ); - return; - } - // If possible, only show databases with the right language (otherwise show all databases). - const queryLanguage = await findLanguage(cliServer, uri); - if (queryLanguage) { - filteredDBs = dbm.databaseItems.filter( - (db) => db.language === queryLanguage, - ); - if (filteredDBs.length === 0) { - void showAndLogErrorMessage( - `No databases found for language ${queryLanguage}. Please add a suitable database to your workspace.`, - ); - return; - } - } - const quickPickItems = filteredDBs.map( - (dbItem) => ({ - databaseItem: dbItem, - label: dbItem.name, - description: dbItem.language, - }), - ); - /** - * Databases that were selected in the quick pick menu. - */ - const quickpick = await window.showQuickPick( - quickPickItems, - { canPickMany: true, ignoreFocusOut: true }, - ); - if (quickpick !== undefined) { - // Collect all skipped databases and display them at the end (instead of popping up individual errors) - const skippedDatabases = []; - const errors = []; - for (const item of quickpick) { - try { - await compileAndRunQuery( - false, - uri, - progress, - token, - item.databaseItem, - ); - } catch (e) { - skippedDatabases.push(item.label); - errors.push(getErrorMessage(e)); - } - } - if (skippedDatabases.length > 0) { - void extLogger.log(`Errors:\n${errors.join("\n")}`); - void showAndLogWarningMessage( - `The following databases were skipped:\n${skippedDatabases.join( - "\n", - )}.\nFor details about the errors, see the logs.`, - ); - } - } else { - void showAndLogErrorMessage("No databases selected."); - } + ) => await compileAndRunQueryOnMultipleDatabases(progress, token, uri), + { + title: "Running query on selected databases", + cancellable: true, }, + ), + ); + // Since we are tracking extension usage through commands, this command mirrors the runQueryOnMultipleDatabases command + ctx.subscriptions.push( + commandRunnerWithProgress( + "codeQL.runQueryOnMultipleDatabasesContextEditor", + async ( + progress: ProgressCallback, + token: CancellationToken, + uri: Uri | undefined, + ) => await compileAndRunQueryOnMultipleDatabases(progress, token, uri), { title: "Running query on selected databases", cancellable: true, @@ -1076,8 +1125,6 @@ async function activateWithInstalledDistribution( ), ); - registerRemoteQueryTextProvider(); - // The "runVariantAnalysis" command is internal-only. ctx.subscriptions.push( commandRunnerWithProgress( @@ -1176,7 +1223,7 @@ async function activateWithInstalledDistribution( ctx.subscriptions.push( commandRunner("codeQL.exportSelectedVariantAnalysisResults", async () => { - await exportSelectedRemoteQueryResults(qhm); + await exportSelectedVariantAnalysisResults(qhm); }), ); @@ -1529,6 +1576,49 @@ async function activateWithInstalledDistribution( }; } +function addUnhandledRejectionListener() { + const handler = (error: unknown) => { + // This listener will be triggered for errors from other extensions as + // well as errors from this extension. We don't want to flood the user + // with popups about errors from other extensions, and we don't want to + // report them in our telemetry. + // + // The stack trace gets redacted before being sent as telemetry, but at + // this point in the code we have the full unredacted information. + const isFromThisExtension = + extension && getErrorStack(error).includes(extension.extensionPath); + + if (isFromThisExtension) { + const message = redactableError( + asError(error), + )`Unhandled error: ${getErrorMessage(error)}`; + // Add a catch so that showAndLogExceptionWithTelemetry fails, we avoid + // triggering "unhandledRejection" and avoid an infinite loop + showAndLogExceptionWithTelemetry(message).catch( + (telemetryError: unknown) => { + void extLogger.log( + `Failed to send error telemetry: ${getErrorMessage( + telemetryError, + )}`, + ); + void extLogger.log(message.fullMessage); + }, + ); + } + }; + + // "uncaughtException" will trigger whenever an exception reaches the top level. + // This covers extension initialization and any code within a `setTimeout`. + // Notably this does not include exceptions thrown when executing commands, + // because `commandRunner` wraps the command body and handles errors. + process.addListener("uncaughtException", handler); + + // "unhandledRejection" will trigger whenever any promise is rejected and it is + // not handled by a "catch" somewhere in the promise chain. This includes when + // a promise is used with the "void" operator. + process.addListener("unhandledRejection", handler); +} + async function createQueryServer( qlConfigurationListener: QueryServerConfigListener, cliServer: CodeQLCliServer, @@ -1583,21 +1673,6 @@ async function initializeLogging(ctx: ExtensionContext): Promise { const checkForUpdatesCommand = "codeQL.checkForUpdatesToCLI"; -/** - * This text provider lets us open readonly files in the editor. - * - * TODO: Consolidate this with the 'codeql' text provider in query-history-manager.ts. - */ -function registerRemoteQueryTextProvider() { - workspace.registerTextDocumentContentProvider("remote-query", { - provideTextDocumentContent(uri: Uri): ProviderResult { - const params = new URLSearchParams(uri.query); - - return params.get("queryText"); - }, - }); -} - const avoidVersionCheck = "avoid-version-check-at-startup"; const lastVersionChecked = "last-version-checked"; async function assertVSCodeVersionGreaterThan( diff --git a/extensions/ql-vscode/src/helpers.ts b/extensions/ql-vscode/src/helpers.ts index 9f4daec7b..d3245a287 100644 --- a/extensions/ql-vscode/src/helpers.ts +++ b/extensions/ql-vscode/src/helpers.ts @@ -24,6 +24,7 @@ import { QueryMetadata } from "./pure/interface-types"; import { telemetryListener } from "./telemetry"; import { RedactableError } from "./pure/errors"; import { getQlPackPath } from "./pure/ql"; +import { dbSchemeToLanguage } from "./common/query-language"; // Shared temporary folder for the extension. export const tmpDir = dirSync({ @@ -568,16 +569,6 @@ export class CachedOperation { * * @see cli.CodeQLCliServer.resolveDatabase */ -export const dbSchemeToLanguage = { - "semmlecode.javascript.dbscheme": "javascript", - "semmlecode.cpp.dbscheme": "cpp", - "semmlecode.dbscheme": "java", - "semmlecode.python.dbscheme": "python", - "semmlecode.csharp.dbscheme": "csharp", - "go.dbscheme": "go", - "ruby.dbscheme": "ruby", - "swift.dbscheme": "swift", -}; export const languageToDbScheme = Object.entries(dbSchemeToLanguage).reduce( (acc, [k, v]) => { diff --git a/extensions/ql-vscode/src/interface-utils.ts b/extensions/ql-vscode/src/interface-utils.ts index f4b094ab9..da01c3135 100644 --- a/extensions/ql-vscode/src/interface-utils.ts +++ b/extensions/ql-vscode/src/interface-utils.ts @@ -15,7 +15,7 @@ import { ThemeColor, } from "vscode"; import { tryGetResolvableLocation, isLineColumnLoc } from "./pure/bqrs-utils"; -import { DatabaseItem, DatabaseManager } from "./databases"; +import { DatabaseItem, DatabaseManager } from "./local-databases"; import { ViewSourceFileMsg } from "./pure/interface-types"; import { Logger } from "./common"; import { @@ -109,11 +109,7 @@ export function tryResolveLocation( } } -export type WebviewView = - | "results" - | "compare" - | "remote-queries" - | "variant-analysis"; +export type WebviewView = "results" | "compare" | "variant-analysis"; export interface WebviewMessage { t: string; @@ -129,10 +125,13 @@ export function getHtmlForWebview( view: WebviewView, { allowInlineStyles, + allowWasmEval, }: { allowInlineStyles?: boolean; + allowWasmEval?: boolean; } = { allowInlineStyles: false, + allowWasmEval: false, }, ): string { const scriptUriOnDisk = Uri.file(ctx.asAbsolutePath("out/webview.js")); @@ -163,7 +162,9 @@ export function getHtmlForWebview( /* * Content security policy: * default-src: allow nothing by default. - * script-src: allow only the given script, using the nonce. + * script-src: + * - allow the given script, using the nonce. + * - 'wasm-unsafe-eval: allow loading WebAssembly modules if necessary. * style-src: allow only the given stylesheet, using the nonce. * connect-src: only allow fetch calls to webview resource URIs * (this is used to load BQRS result files). @@ -172,7 +173,9 @@ export function getHtmlForWebview( ${stylesheetsHtmlLines.join(` ${EOL}`)} diff --git a/extensions/ql-vscode/src/interface.ts b/extensions/ql-vscode/src/interface.ts index cf27f52db..17655fa7c 100644 --- a/extensions/ql-vscode/src/interface.ts +++ b/extensions/ql-vscode/src/interface.ts @@ -12,7 +12,11 @@ import { } from "vscode"; import * as cli from "./cli"; import { CodeQLCliServer } from "./cli"; -import { DatabaseEventKind, DatabaseItem, DatabaseManager } from "./databases"; +import { + DatabaseEventKind, + DatabaseItem, + DatabaseManager, +} from "./local-databases"; import { showAndLogExceptionWithTelemetry } from "./helpers"; import { asError, @@ -64,7 +68,7 @@ import { ResultSetSchema, } from "./pure/bqrs-cli-types"; import { AbstractWebview, WebviewPanelConfig } from "./abstract-webview"; -import { PAGE_SIZE } from "./config"; +import { isCanary, PAGE_SIZE } from "./config"; import { HistoryItemLabelProvider } from "./query-history/history-item-label-provider"; import { telemetryListener } from "./telemetry"; import { redactableError } from "./pure/errors"; @@ -221,6 +225,8 @@ export class ResultsView extends AbstractWebview< viewColumn: this.chooseColumnForWebview(), preserveFocus: true, view: "results", + // Required for the graph viewer which is using d3-graphviz WASM module. Only supported in canary mode. + allowWasmEval: isCanary(), }; } @@ -393,7 +399,7 @@ export class ResultsView extends AbstractWebview< forceReveal: WebviewReveal, shouldKeepOldResultsWhileRendering = false, ): Promise { - if (!fullQuery.completedQuery.successful) { + if (!fullQuery.completedQuery?.successful) { return; } @@ -656,7 +662,8 @@ export class ResultsView extends AbstractWebview< } let data; let numTotalResults; - if (metadata?.kind === GRAPH_TABLE_NAME) { + // Graph results are only supported in canary mode because the graph viewer is not actively supported + if (metadata?.kind === GRAPH_TABLE_NAME && isCanary()) { data = await interpretGraphResults( this.cliServer, metadata, diff --git a/extensions/ql-vscode/src/legacy-query-server/legacyRunner.ts b/extensions/ql-vscode/src/legacy-query-server/legacyRunner.ts index 933d346cf..44eba17d9 100644 --- a/extensions/ql-vscode/src/legacy-query-server/legacyRunner.ts +++ b/extensions/ql-vscode/src/legacy-query-server/legacyRunner.ts @@ -1,6 +1,6 @@ import { CancellationToken } from "vscode"; import { ProgressCallback } from "../commandRunner"; -import { DatabaseItem } from "../databases"; +import { DatabaseItem } from "../local-databases"; import { Dataset, deregisterDatabases, diff --git a/extensions/ql-vscode/src/legacy-query-server/run-queries.ts b/extensions/ql-vscode/src/legacy-query-server/run-queries.ts index b7ba2dc66..0326d5c24 100644 --- a/extensions/ql-vscode/src/legacy-query-server/run-queries.ts +++ b/extensions/ql-vscode/src/legacy-query-server/run-queries.ts @@ -4,7 +4,7 @@ import { CancellationToken, Uri } from "vscode"; import { LSPErrorCodes, ResponseError } from "vscode-languageclient"; import * as cli from "../cli"; -import { DatabaseItem } from "../databases"; +import { DatabaseItem } from "../local-databases"; import { getOnDiskWorkspaceFolders, showAndLogErrorMessage, @@ -313,7 +313,7 @@ export async function compileAndRunQueryAgainstDatabase( if (!packConfig.dbscheme) { throw new Error( - "Could not find a database scheme for this query. Please check that you have a valid qlpack.yml file for this query, which refers to a database scheme either in the `dbscheme` field or through one of its dependencies.", + "Could not find a database scheme for this query. Please check that you have a valid qlpack.yml or codeql-pack.yml file for this query, which refers to a database scheme either in the `dbscheme` field or through one of its dependencies.", ); } diff --git a/extensions/ql-vscode/src/legacy-query-server/upgrades.ts b/extensions/ql-vscode/src/legacy-query-server/upgrades.ts index 0a5ca5b55..0ec182766 100644 --- a/extensions/ql-vscode/src/legacy-query-server/upgrades.ts +++ b/extensions/ql-vscode/src/legacy-query-server/upgrades.ts @@ -10,7 +10,7 @@ import * as messages from "../pure/legacy-messages"; import * as qsClient from "./queryserver-client"; import * as tmp from "tmp-promise"; import { dirname } from "path"; -import { DatabaseItem } from "../databases"; +import { DatabaseItem } from "../local-databases"; import { asError, getErrorMessage } from "../pure/helpers-pure"; import { redactableError } from "../pure/errors"; diff --git a/extensions/ql-vscode/src/databases-ui.ts b/extensions/ql-vscode/src/local-databases-ui.ts similarity index 96% rename from extensions/ql-vscode/src/databases-ui.ts rename to extensions/ql-vscode/src/local-databases-ui.ts index f55d733f8..5d6d8fcc0 100644 --- a/extensions/ql-vscode/src/databases-ui.ts +++ b/extensions/ql-vscode/src/local-databases-ui.ts @@ -20,7 +20,7 @@ import { DatabaseChangedEvent, DatabaseItem, DatabaseManager, -} from "./databases"; +} from "./local-databases"; import { commandRunner, commandRunnerWithProgress, @@ -375,18 +375,22 @@ export class DatabaseUI extends DisposableObject { // This specifically refers to the database folder in // https://github.com/github/codespaces-codeql const uri = Uri.parse( - `${workspace.workspaceFolders[0].uri}/codeql-tutorial-database`, + `${workspace.workspaceFolders[0].uri}/.tours/codeql-tutorial-database`, ); let databaseItem = this.databaseManager.findDatabaseItem(uri); + const isTutorialDatabase = true; if (databaseItem === undefined) { databaseItem = await this.databaseManager.openDatabase( progress, token, uri, + "CodeQL Tutorial Database", + isTutorialDatabase, ); } await this.databaseManager.setCurrentDatabaseItem(databaseItem); + await this.handleTourDependencies(); } } catch (e) { // rethrow and let this be handled by default error handling. @@ -398,6 +402,22 @@ export class DatabaseUI extends DisposableObject { } }; + private handleTourDependencies = async (): Promise => { + if (!workspace.workspaceFolders?.length) { + throw new Error("No workspace folder is open."); + } else { + const tutorialQueriesPath = join( + workspace.workspaceFolders[0].uri.fsPath, + "tutorial-queries", + ); + const cli = this.queryServer?.cliServer; + if (!cli) { + throw new Error("No CLI server found"); + } + await cli.packInstall(tutorialQueriesPath); + } + }; + handleRemoveOrphanedDatabases = async (): Promise => { void extLogger.log("Removing orphaned databases from workspace storage."); let dbDirs = undefined; diff --git a/extensions/ql-vscode/src/databases.ts b/extensions/ql-vscode/src/local-databases.ts similarity index 97% rename from extensions/ql-vscode/src/databases.ts rename to extensions/ql-vscode/src/local-databases.ts index e6651b8e0..8ba756df7 100644 --- a/extensions/ql-vscode/src/databases.ts +++ b/extensions/ql-vscode/src/local-databases.ts @@ -26,6 +26,8 @@ import { QueryRunner } from "./queryRunner"; import { pathsEqual } from "./pure/files"; import { redactableError } from "./pure/errors"; import { isCodespacesTemplate } from "./config"; +import { QlPackGenerator } from "./qlpack-generator"; +import { QueryLanguage } from "./common/query-language"; /** * databases.ts @@ -605,6 +607,7 @@ export class DatabaseManager extends DisposableObject { token: vscode.CancellationToken, uri: vscode.Uri, displayName?: string, + isTutorialDatabase?: boolean, ): Promise { const contents = await DatabaseResolver.resolveDatabaseContents(uri); // Ignore the source archive for QLTest databases by default. @@ -628,7 +631,7 @@ export class DatabaseManager extends DisposableObject { await this.addDatabaseItem(progress, token, databaseItem); await this.addDatabaseSourceArchiveFolder(databaseItem); - if (isCodespacesTemplate()) { + if (isCodespacesTemplate() && !isTutorialDatabase) { await this.createSkeletonPacks(databaseItem); } @@ -655,9 +658,27 @@ export class DatabaseManager extends DisposableObject { return; } - await showBinaryChoiceDialog( - `We've noticed you don't have a QL pack downloaded to analyze this database. Can we set up a ${databaseItem.language} query pack for you`, + const answer = await showBinaryChoiceDialog( + `We've noticed you don't have a CodeQL pack available to analyze this database. Can we set up a query pack for you?`, ); + + if (!answer) { + return; + } + + try { + const qlPackGenerator = new QlPackGenerator( + folderName, + databaseItem.language as QueryLanguage, + this.cli, + this.ctx.storageUri?.fsPath, + ); + await qlPackGenerator.generate(); + } catch (e: unknown) { + void this.logger.log( + `Could not create skeleton QL pack: ${getErrorMessage(e)}`, + ); + } } private async reregisterDatabases( diff --git a/extensions/ql-vscode/src/mocks/gh-api-request.ts b/extensions/ql-vscode/src/mocks/gh-api-request.ts index 710f868e2..39dfd113f 100644 --- a/extensions/ql-vscode/src/mocks/gh-api-request.ts +++ b/extensions/ql-vscode/src/mocks/gh-api-request.ts @@ -1,8 +1,8 @@ -import { Repository } from "../remote-queries/gh-api/repository"; +import { Repository } from "../variant-analysis/gh-api/repository"; import { VariantAnalysis, VariantAnalysisRepoTask, -} from "../remote-queries/gh-api/variant-analysis"; +} from "../variant-analysis/gh-api/variant-analysis"; // Types that represent requests/responses from the GitHub API // that we need to mock. diff --git a/extensions/ql-vscode/src/mocks/mock-gh-api-server.ts b/extensions/ql-vscode/src/mocks/mock-gh-api-server.ts index 5bf5f0299..1d6bbd3ae 100644 --- a/extensions/ql-vscode/src/mocks/mock-gh-api-server.ts +++ b/extensions/ql-vscode/src/mocks/mock-gh-api-server.ts @@ -96,8 +96,8 @@ export class MockGitHubApiServer extends DisposableObject { } public async stopRecording(): Promise { - await this.recorder.stop(); - await this.recorder.clear(); + this.recorder.stop(); + this.recorder.clear(); } public async getScenarioNames(scenariosPath?: string): Promise { diff --git a/extensions/ql-vscode/src/mocks/vscode-mock-gh-api-server.ts b/extensions/ql-vscode/src/mocks/vscode-mock-gh-api-server.ts index c52543d48..fbe43c2c9 100644 --- a/extensions/ql-vscode/src/mocks/vscode-mock-gh-api-server.ts +++ b/extensions/ql-vscode/src/mocks/vscode-mock-gh-api-server.ts @@ -35,11 +35,11 @@ export class VSCodeMockGitHubApiServer extends DisposableObject { } public async startServer(): Promise { - await this.server.startServer(); + this.server.startServer(); } public async stopServer(): Promise { - await this.server.stopServer(); + this.server.stopServer(); await commands.executeCommand( "setContext", diff --git a/extensions/ql-vscode/src/packaging.ts b/extensions/ql-vscode/src/packaging.ts index 1cc9ad8e4..c17621c09 100644 --- a/extensions/ql-vscode/src/packaging.ts +++ b/extensions/ql-vscode/src/packaging.ts @@ -9,18 +9,7 @@ import { ProgressCallback, UserCancellationException } from "./commandRunner"; import { extLogger } from "./common"; import { asError, getErrorStack } from "./pure/helpers-pure"; import { redactableError } from "./pure/errors"; - -const QUERY_PACKS = [ - "codeql/cpp-queries", - "codeql/csharp-queries", - "codeql/go-queries", - "codeql/java-queries", - "codeql/javascript-queries", - "codeql/python-queries", - "codeql/ruby-queries", - "codeql/csharp-solorigate-queries", - "codeql/javascript-experimental-atm-queries", -]; +import { PACKS_BY_QUERY_LANGUAGE } from "./common/query-language"; /** * Prompts user to choose packs to download, and downloads them. @@ -45,7 +34,7 @@ export async function handleDownloadPacks( { ignoreFocusOut: true }, ); if (quickpick === queryPackOption) { - packsToDownload = QUERY_PACKS; + packsToDownload = Object.values(PACKS_BY_QUERY_LANGUAGE).flat(); } else if (quickpick === customPackOption) { const customPack = await window.showInputBox({ prompt: diff --git a/extensions/ql-vscode/src/pure/bqrs-cli-types.ts b/extensions/ql-vscode/src/pure/bqrs-cli-types.ts index 6f3d93374..1a8be81be 100644 --- a/extensions/ql-vscode/src/pure/bqrs-cli-types.ts +++ b/extensions/ql-vscode/src/pure/bqrs-cli-types.ts @@ -105,7 +105,7 @@ export function transformBqrsResultSet( }; } -type BqrsKind = +export type BqrsKind = | "String" | "Float" | "Integer" diff --git a/extensions/ql-vscode/src/pure/files.ts b/extensions/ql-vscode/src/pure/files.ts index c07e0f26d..e35034ed4 100644 --- a/extensions/ql-vscode/src/pure/files.ts +++ b/extensions/ql-vscode/src/pure/files.ts @@ -67,3 +67,8 @@ export function pathsEqual( } return path1 === path2; } + +export async function readDirFullPaths(path: string): Promise { + const baseNames = await readdir(path); + return baseNames.map((baseName) => join(path, baseName)); +} diff --git a/extensions/ql-vscode/src/pure/interface-types.ts b/extensions/ql-vscode/src/pure/interface-types.ts index 321ad6b4d..bbc4b0903 100644 --- a/extensions/ql-vscode/src/pure/interface-types.ts +++ b/extensions/ql-vscode/src/pure/interface-types.ts @@ -1,9 +1,4 @@ import * as sarif from "sarif"; -import { AnalysisResults } from "../remote-queries/shared/analysis-result"; -import { - AnalysisSummary, - RemoteQueryResult, -} from "../remote-queries/shared/remote-query-result"; import { RawResultSet, ResultRow, @@ -15,7 +10,7 @@ import { VariantAnalysis, VariantAnalysisScannedRepositoryResult, VariantAnalysisScannedRepositoryState, -} from "../remote-queries/shared/variant-analysis"; +} from "../variant-analysis/shared/variant-analysis"; import { RepositoriesFilterSortStateWithIds } from "./variant-analysis-filter-sort"; /** @@ -58,13 +53,6 @@ export interface QueryMetadata { scored?: string; } -export interface PreviousExecution { - queryName: string; - time: string; - databaseName: string; - durationSeconds: number; -} - export type SarifInterpretationData = { t: "SarifInterpretationData"; /** @@ -222,11 +210,6 @@ export interface OpenFileMsg { filePath: string; } -export interface OpenVirtualFileMsg { - t: "openVirtualFile"; - queryText: string; -} - /** * Message from the results view to toggle the display of * query diagnostics. @@ -353,12 +336,6 @@ export interface SetComparisonsMessage { readonly databaseUri: string; } -export enum DiffKind { - Add = "Add", - Remove = "Remove", - Change = "Change", -} - /** * from is the set of rows that have changes in the "from" query. * to is the set of rows that have changes in the "to" query. @@ -407,56 +384,6 @@ export interface ParsedResultSets { resultSet: ResultSet; } -export type FromRemoteQueriesMessage = - | ViewLoadedMsg - | RemoteQueryErrorMessage - | OpenFileMsg - | OpenVirtualFileMsg - | RemoteQueryDownloadAnalysisResultsMessage - | RemoteQueryDownloadAllAnalysesResultsMessage - | RemoteQueryExportResultsMessage - | CopyRepoListMessage - | TelemetryMessage; - -export type ToRemoteQueriesMessage = - | SetRemoteQueryResultMessage - | SetAnalysesResultsMessage; - -export interface SetRemoteQueryResultMessage { - t: "setRemoteQueryResult"; - queryResult: RemoteQueryResult; -} - -export interface SetAnalysesResultsMessage { - t: "setAnalysesResults"; - analysesResults: AnalysisResults[]; -} - -export interface RemoteQueryErrorMessage { - t: "remoteQueryError"; - error: string; -} - -export interface RemoteQueryDownloadAnalysisResultsMessage { - t: "remoteQueryDownloadAnalysisResults"; - analysisSummary: AnalysisSummary; -} - -export interface RemoteQueryDownloadAllAnalysesResultsMessage { - t: "remoteQueryDownloadAllAnalysesResults"; - analysisSummaries: AnalysisSummary[]; -} - -export interface RemoteQueryExportResultsMessage { - t: "remoteQueryExportResults"; - queryId: string; -} - -export interface CopyRepoListMessage { - t: "copyRepoList"; - queryId: string; -} - export interface SetVariantAnalysisMessage { t: "setVariantAnalysis"; variantAnalysis: VariantAnalysis; diff --git a/extensions/ql-vscode/src/pure/location-link-utils.ts b/extensions/ql-vscode/src/pure/location-link-utils.ts index b7c954d20..85aaeb073 100644 --- a/extensions/ql-vscode/src/pure/location-link-utils.ts +++ b/extensions/ql-vscode/src/pure/location-link-utils.ts @@ -1,4 +1,4 @@ -import { FileLink } from "../remote-queries/shared/analysis-result"; +import { FileLink } from "../variant-analysis/shared/analysis-result"; export function createRemoteFileRef( fileLink: FileLink, diff --git a/extensions/ql-vscode/src/pure/sarif-utils.ts b/extensions/ql-vscode/src/pure/sarif-utils.ts index 82723e54b..8af512f6f 100644 --- a/extensions/ql-vscode/src/pure/sarif-utils.ts +++ b/extensions/ql-vscode/src/pure/sarif-utils.ts @@ -1,5 +1,5 @@ import * as Sarif from "sarif"; -import type { HighlightedRegion } from "../remote-queries/shared/analysis-result"; +import type { HighlightedRegion } from "../variant-analysis/shared/analysis-result"; import { ResolvableLocationValue } from "./bqrs-cli-types"; export interface SarifLink { diff --git a/extensions/ql-vscode/src/text-utils.ts b/extensions/ql-vscode/src/pure/text-utils.ts similarity index 100% rename from extensions/ql-vscode/src/text-utils.ts rename to extensions/ql-vscode/src/pure/text-utils.ts diff --git a/extensions/ql-vscode/src/pure/variant-analysis-filter-sort.ts b/extensions/ql-vscode/src/pure/variant-analysis-filter-sort.ts index 623f3e3be..93fe0504c 100644 --- a/extensions/ql-vscode/src/pure/variant-analysis-filter-sort.ts +++ b/extensions/ql-vscode/src/pure/variant-analysis-filter-sort.ts @@ -1,7 +1,7 @@ import { Repository, RepositoryWithMetadata, -} from "../remote-queries/shared/repository"; +} from "../variant-analysis/shared/repository"; import { parseDate } from "./date"; export enum SortKey { diff --git a/extensions/ql-vscode/src/qlpack-generator.ts b/extensions/ql-vscode/src/qlpack-generator.ts new file mode 100644 index 000000000..16809d708 --- /dev/null +++ b/extensions/ql-vscode/src/qlpack-generator.ts @@ -0,0 +1,93 @@ +import { writeFile } from "fs-extra"; +import { dump } from "js-yaml"; +import { join } from "path"; +import { Uri, workspace } from "vscode"; +import { CodeQLCliServer } from "./cli"; +import { QueryLanguage } from "./common/query-language"; + +export class QlPackGenerator { + private readonly qlpackName: string; + private readonly qlpackVersion: string; + private readonly header: string; + private readonly qlpackFileName: string; + private readonly folderUri: Uri; + + constructor( + private readonly folderName: string, + private readonly queryLanguage: QueryLanguage, + private readonly cliServer: CodeQLCliServer, + private readonly storagePath: string | undefined, + ) { + if (this.storagePath === undefined) { + throw new Error("Workspace storage path is undefined"); + } + this.qlpackName = `getting-started/codeql-extra-queries-${this.queryLanguage}`; + this.qlpackVersion = "1.0.0"; + this.header = "# This is an automatically generated file.\n\n"; + + this.qlpackFileName = "codeql-pack.yml"; + this.folderUri = Uri.file(join(this.storagePath, this.folderName)); + } + + public async generate() { + // create QL pack folder and add to workspace + await this.createWorkspaceFolder(); + + // create codeql-pack.yml + await this.createQlPackYaml(); + + // create example.ql + await this.createExampleQlFile(); + + // create codeql-pack.lock.yml + await this.createCodeqlPackLockYaml(); + } + + private async createWorkspaceFolder() { + await workspace.fs.createDirectory(this.folderUri); + + const end = (workspace.workspaceFolders || []).length; + + workspace.updateWorkspaceFolders(end, 0, { + name: this.folderName, + uri: this.folderUri, + }); + } + + private async createQlPackYaml() { + const qlPackFilePath = join(this.folderUri.fsPath, this.qlpackFileName); + + const qlPackYml = { + name: this.qlpackName, + version: this.qlpackVersion, + dependencies: {}, + }; + + await writeFile(qlPackFilePath, this.header + dump(qlPackYml), "utf8"); + } + + private async createExampleQlFile() { + const exampleQlFilePath = join(this.folderUri.fsPath, "example.ql"); + + const exampleQl = ` +/** + * This is an automatically generated file + * @name Hello world + * @kind problem + * @problem.severity warning + * @id ${this.queryLanguage}/example/hello-world + */ + +import ${this.queryLanguage} + +from File f +select f, "Hello, world!" +`.trim(); + + await writeFile(exampleQlFilePath, exampleQl, "utf8"); + } + + private async createCodeqlPackLockYaml() { + await this.cliServer.packAdd(this.folderUri.fsPath, this.queryLanguage); + } +} diff --git a/extensions/ql-vscode/src/query-history/history-item-label-provider.ts b/extensions/ql-vscode/src/query-history/history-item-label-provider.ts index 98b108560..ced715163 100644 --- a/extensions/ql-vscode/src/query-history/history-item-label-provider.ts +++ b/extensions/ql-vscode/src/query-history/history-item-label-provider.ts @@ -15,7 +15,7 @@ import { humanizeQueryStatus } from "../query-status"; interface InterpolateReplacements { t: string; // Start time q: string; // Query name - d: string; // Database/Controller repo name + d: string; // Database/repositories count r: string; // Result count/Empty s: string; // Status f: string; // Query file name diff --git a/extensions/ql-vscode/src/query-history/query-history-dirs.ts b/extensions/ql-vscode/src/query-history/query-history-dirs.ts new file mode 100644 index 000000000..46a463d7e --- /dev/null +++ b/extensions/ql-vscode/src/query-history/query-history-dirs.ts @@ -0,0 +1,4 @@ +export interface QueryHistoryDirs { + localQueriesDirPath: string; + variantAnalysesDirPath: string; +} diff --git a/extensions/ql-vscode/src/query-history/query-history-info.ts b/extensions/ql-vscode/src/query-history/query-history-info.ts index f356874e8..3fb9d5080 100644 --- a/extensions/ql-vscode/src/query-history/query-history-info.ts +++ b/extensions/ql-vscode/src/query-history/query-history-info.ts @@ -5,7 +5,7 @@ import { pluralize } from "../pure/word"; import { hasRepoScanCompleted, getActionsWorkflowRunUrl as getVariantAnalysisActionsWorkflowRunUrl, -} from "../remote-queries/shared/variant-analysis"; +} from "../variant-analysis/shared/variant-analysis"; export type QueryHistoryInfo = LocalQueryInfo | VariantAnalysisHistoryItem; @@ -22,7 +22,7 @@ export function getRawQueryName(item: QueryHistoryInfo): string { /** * Gets an identifier for the query history item which could be - * a local/remote query or a variant analysis. This id isn't guaranteed + * a local query or a variant analysis. This id isn't guaranteed * to be unique for each item in the query history. * @param item the history item. * @returns the id of the query or variant analysis. diff --git a/extensions/ql-vscode/src/query-history/query-history-manager.ts b/extensions/ql-vscode/src/query-history/query-history-manager.ts index 0f90010a0..daffbb9d5 100644 --- a/extensions/ql-vscode/src/query-history/query-history-manager.ts +++ b/extensions/ql-vscode/src/query-history/query-history-manager.ts @@ -40,7 +40,7 @@ import { getQueryText, QueryHistoryInfo, } from "./query-history-info"; -import { DatabaseManager } from "../databases"; +import { DatabaseManager } from "../local-databases"; import { registerQueryHistoryScrubber } from "./query-history-scrubber"; import { QueryStatus, @@ -60,11 +60,12 @@ import EvalLogTreeBuilder from "../eval-log-tree-builder"; import { EvalLogData, parseViewerData } from "../pure/log-summary-parser"; import { QueryWithResults } from "../run-queries-shared"; import { QueryRunner } from "../queryRunner"; -import { VariantAnalysisManager } from "../remote-queries/variant-analysis-manager"; +import { VariantAnalysisManager } from "../variant-analysis/variant-analysis-manager"; import { VariantAnalysisHistoryItem } from "./variant-analysis-history-item"; -import { getTotalResultCount } from "../remote-queries/shared/variant-analysis"; +import { getTotalResultCount } from "../variant-analysis/shared/variant-analysis"; import { HistoryTreeDataProvider } from "./history-tree-data-provider"; import { redactableError } from "../pure/errors"; +import { QueryHistoryDirs } from "./query-history-dirs"; /** * query-history-manager.ts @@ -139,7 +140,7 @@ export class QueryHistoryManager extends DisposableObject { private readonly localQueriesResultsView: ResultsView, private readonly variantAnalysisManager: VariantAnalysisManager, private readonly evalLogViewer: EvalLogViewer, - private readonly queryStorageDir: string, + private readonly queryHistoryDirs: QueryHistoryDirs, ctx: ExtensionContext, private readonly queryHistoryConfigListener: QueryHistoryConfig, private readonly labelProvider: HistoryItemLabelProvider, @@ -389,7 +390,7 @@ export class QueryHistoryManager extends DisposableObject { ONE_HOUR_IN_MS, TWO_HOURS_IN_MS, queryHistoryConfigListener.ttlInMillis, - this.queryStorageDir, + this.queryHistoryDirs, qhm, ctx, ), @@ -755,14 +756,12 @@ export class QueryHistoryManager extends DisposableObject { ) { // show original query file on double click await this.handleOpenQuery(finalSingleItem, [finalSingleItem]); - } else { + } else if ( + finalSingleItem.t === "variant-analysis" || + finalSingleItem.status === QueryStatus.Completed + ) { // show results on single click (if results view is available) - if ( - finalSingleItem.t === "variant-analysis" || - finalSingleItem.status === QueryStatus.Completed - ) { - await this.openQueryResults(finalSingleItem); - } + await this.openQueryResults(finalSingleItem); } } @@ -797,6 +796,8 @@ export class QueryHistoryManager extends DisposableObject { return this.variantAnalysisManager.getVariantAnalysisStorageLocation( queryHistoryItem.variantAnalysis.id, ); + } else { + assertNever(queryHistoryItem); } throw new Error("Unable to get query directory"); @@ -830,6 +831,8 @@ export class QueryHistoryManager extends DisposableObject { ), "timestamp", ); + } else { + assertNever(finalSingleItem); } if (externalFilePath) { @@ -994,6 +997,8 @@ export class QueryHistoryManager extends DisposableObject { "codeQL.cancelVariantAnalysis", item.variantAnalysis.id, ); + } else { + assertNever(item); } } }); @@ -1185,17 +1190,19 @@ export class QueryHistoryManager extends DisposableObject { multiSelect, ); - // Remote queries only - if (!this.assertSingleQuery(finalMultiSelect) || !finalSingleItem) { + // Variant analyses only + if ( + !this.assertSingleQuery(finalMultiSelect) || + !finalSingleItem || + finalSingleItem.t !== "variant-analysis" + ) { return; } - if (finalSingleItem.t === "variant-analysis") { - await commands.executeCommand( - "codeQL.copyVariantAnalysisRepoList", - finalSingleItem.variantAnalysis.id, - ); - } + await commands.executeCommand( + "codeQL.copyVariantAnalysisRepoList", + finalSingleItem.variantAnalysis.id, + ); } async handleExportResults( @@ -1207,17 +1214,19 @@ export class QueryHistoryManager extends DisposableObject { multiSelect, ); - if (!this.assertSingleQuery(finalMultiSelect) || !finalSingleItem) { + // Variant analysis only + if ( + !this.assertSingleQuery(finalMultiSelect) || + !finalSingleItem || + finalSingleItem.t !== "variant-analysis" + ) { return; } - // Variant analysis only - if (finalSingleItem.t === "variant-analysis") { - await commands.executeCommand( - "codeQL.exportVariantAnalysisResults", - finalSingleItem.variantAnalysis.id, - ); - } + await commands.executeCommand( + "codeQL.exportVariantAnalysisResults", + finalSingleItem.variantAnalysis.id, + ); } addQuery(item: QueryHistoryInfo) { @@ -1290,7 +1299,7 @@ the file in the file explorer and dragging it into the workspace.`, singleItem: QueryHistoryInfo, multiSelect: QueryHistoryInfo[], ): Promise { - // Remote queries cannot be compared + // Variant analyses cannot be compared if ( singleItem.t !== "local" || multiSelect.some((s) => s.t !== "local") || diff --git a/extensions/ql-vscode/src/query-history/query-history-scrubber.ts b/extensions/ql-vscode/src/query-history/query-history-scrubber.ts index cae683d7b..878e7c382 100644 --- a/extensions/ql-vscode/src/query-history/query-history-scrubber.ts +++ b/extensions/ql-vscode/src/query-history/query-history-scrubber.ts @@ -1,8 +1,10 @@ -import { pathExists, readdir, stat, remove, readFile } from "fs-extra"; +import { pathExists, stat, remove, readFile } from "fs-extra"; import { EOL } from "os"; import { join } from "path"; import { Disposable, ExtensionContext } from "vscode"; import { extLogger } from "../common"; +import { readDirFullPaths } from "../pure/files"; +import { QueryHistoryDirs } from "./query-history-dirs"; import { QueryHistoryManager } from "./query-history-manager"; const LAST_SCRUB_TIME_KEY = "lastScrubTime"; @@ -23,14 +25,14 @@ type Counter = { * @param wakeInterval How often to check to see if the job should run. * @param throttleTime How often to actually run the job. * @param maxQueryTime The maximum age of a query before is ready for deletion. - * @param queryDirectory The directory containing all queries. + * @param queryHistoryDirs The directories containing all query history information. * @param ctx The extension context. */ export function registerQueryHistoryScrubber( wakeInterval: number, throttleTime: number, maxQueryTime: number, - queryDirectory: string, + queryHistoryDirs: QueryHistoryDirs, qhm: QueryHistoryManager, ctx: ExtensionContext, @@ -42,7 +44,7 @@ export function registerQueryHistoryScrubber( wakeInterval, throttleTime, maxQueryTime, - queryDirectory, + queryHistoryDirs, qhm, ctx, counter, @@ -58,7 +60,7 @@ export function registerQueryHistoryScrubber( async function scrubQueries( throttleTime: number, maxQueryTime: number, - queryDirectory: string, + queryHistoryDirs: QueryHistoryDirs, qhm: QueryHistoryManager, ctx: ExtensionContext, counter?: Counter, @@ -74,18 +76,33 @@ async function scrubQueries( let scrubCount = 0; // total number of directories deleted try { counter?.increment(); - void extLogger.log("Scrubbing query directory. Removing old queries."); - if (!(await pathExists(queryDirectory))) { + void extLogger.log( + "Cleaning up query history directories. Removing old entries.", + ); + + if (!(await pathExists(queryHistoryDirs.localQueriesDirPath))) { void extLogger.log( - `Cannot scrub. Query directory does not exist: ${queryDirectory}`, + `Cannot clean up query history directories. Local queries directory does not exist: ${queryHistoryDirs.localQueriesDirPath}`, + ); + return; + } + if (!(await pathExists(queryHistoryDirs.variantAnalysesDirPath))) { + void extLogger.log( + `Cannot clean up query history directories. Variant analyses directory does not exist: ${queryHistoryDirs.variantAnalysesDirPath}`, ); return; } - const baseNames = await readdir(queryDirectory); + const localQueryDirPaths = await readDirFullPaths( + queryHistoryDirs.localQueriesDirPath, + ); + const variantAnalysisDirPaths = await readDirFullPaths( + queryHistoryDirs.variantAnalysesDirPath, + ); + const allDirPaths = [...localQueryDirPaths, ...variantAnalysisDirPaths]; + const errors: string[] = []; - for (const baseName of baseNames) { - const dir = join(queryDirectory, baseName); + for (const dir of allDirPaths) { const scrubResult = await scrubDirectory(dir, now, maxQueryTime); if (scrubResult.errorMsg) { errors.push(scrubResult.errorMsg); diff --git a/extensions/ql-vscode/src/query-history/variant-analysis-history-item.ts b/extensions/ql-vscode/src/query-history/variant-analysis-history-item.ts index 6cbb7cea9..18ce81838 100644 --- a/extensions/ql-vscode/src/query-history/variant-analysis-history-item.ts +++ b/extensions/ql-vscode/src/query-history/variant-analysis-history-item.ts @@ -1,5 +1,5 @@ import { QueryStatus } from "../query-status"; -import { VariantAnalysis } from "../remote-queries/shared/variant-analysis"; +import { VariantAnalysis } from "../variant-analysis/shared/variant-analysis"; /** * Information about a variant analysis. diff --git a/extensions/ql-vscode/src/query-server/query-runner.ts b/extensions/ql-vscode/src/query-server/query-runner.ts index 755d1d04f..d80a07cbc 100644 --- a/extensions/ql-vscode/src/query-server/query-runner.ts +++ b/extensions/ql-vscode/src/query-server/query-runner.ts @@ -1,6 +1,6 @@ import { CancellationToken } from "vscode"; import { ProgressCallback, UserCancellationException } from "../commandRunner"; -import { DatabaseItem } from "../databases"; +import { DatabaseItem } from "../local-databases"; import { clearCache, ClearCacheParams, diff --git a/extensions/ql-vscode/src/query-server/run-queries.ts b/extensions/ql-vscode/src/query-server/run-queries.ts index dc93032ff..f9ee0a6d7 100644 --- a/extensions/ql-vscode/src/query-server/run-queries.ts +++ b/extensions/ql-vscode/src/query-server/run-queries.ts @@ -2,7 +2,7 @@ import { join } from "path"; import { CancellationToken } from "vscode"; import * as cli from "../cli"; import { ProgressCallback } from "../commandRunner"; -import { DatabaseItem } from "../databases"; +import { DatabaseItem } from "../local-databases"; import { getOnDiskWorkspaceFolders, showAndLogExceptionWithTelemetry, diff --git a/extensions/ql-vscode/src/query-status.ts b/extensions/ql-vscode/src/query-status.ts index 769b20aa4..96d8e342b 100644 --- a/extensions/ql-vscode/src/query-status.ts +++ b/extensions/ql-vscode/src/query-status.ts @@ -1,5 +1,5 @@ import { assertNever } from "./pure/helpers-pure"; -import { VariantAnalysisStatus } from "./remote-queries/shared/variant-analysis"; +import { VariantAnalysisStatus } from "./variant-analysis/shared/variant-analysis"; export enum QueryStatus { InProgress = "InProgress", diff --git a/extensions/ql-vscode/src/queryRunner.ts b/extensions/ql-vscode/src/queryRunner.ts index 7c931ab91..a2e92ee66 100644 --- a/extensions/ql-vscode/src/queryRunner.ts +++ b/extensions/ql-vscode/src/queryRunner.ts @@ -1,7 +1,7 @@ import { CancellationToken } from "vscode"; import { CodeQLCliServer } from "./cli"; import { ProgressCallback } from "./commandRunner"; -import { DatabaseItem } from "./databases"; +import { DatabaseItem } from "./local-databases"; import { InitialQueryInfo, LocalQueryInfo } from "./query-results"; import { QueryWithResults } from "./run-queries-shared"; diff --git a/extensions/ql-vscode/src/quick-query.ts b/extensions/ql-vscode/src/quick-query.ts index b1564774e..7829896d7 100644 --- a/extensions/ql-vscode/src/quick-query.ts +++ b/extensions/ql-vscode/src/quick-query.ts @@ -10,7 +10,7 @@ import { } from "vscode"; import { LSPErrorCodes, ResponseError } from "vscode-languageclient"; import { CodeQLCliServer } from "./cli"; -import { DatabaseUI } from "./databases-ui"; +import { DatabaseUI } from "./local-databases-ui"; import { getInitialQueryContents, getPrimaryDbscheme, diff --git a/extensions/ql-vscode/src/remote-queries/download-link.ts b/extensions/ql-vscode/src/remote-queries/download-link.ts deleted file mode 100644 index a3493409d..000000000 --- a/extensions/ql-vscode/src/remote-queries/download-link.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { join } from "path"; - -/** - * Represents a link to an artifact to be downloaded. - */ -export interface DownloadLink { - /** - * A unique id of the artifact being downloaded. - */ - id: string; - - /** - * The URL path to use against the GitHub API to download the - * linked artifact. - */ - urlPath: string; - - /** - * An optional path to follow inside the downloaded archive containing the artifact. - */ - innerFilePath?: string; - - /** - * A unique id of the remote query run. This is used to determine where to store artifacts and data from the run. - */ - queryId: string; -} - -/** - * Converts a downloadLink to the path where the artifact should be stored. - * - * @param storagePath The base directory to store artifacts in. - * @param downloadLink The DownloadLink - * @param extension An optional file extension to append to the artifact (no `.`). - * - * @returns A full path to the download location of the artifact - */ -export function createDownloadPath( - storagePath: string, - downloadLink: DownloadLink, - extension = "", -) { - return join( - storagePath, - downloadLink.queryId, - downloadLink.id + (extension ? `.${extension}` : ""), - ); -} diff --git a/extensions/ql-vscode/src/remote-queries/repository-selection.ts b/extensions/ql-vscode/src/remote-queries/repository-selection.ts deleted file mode 100644 index 924971fe4..000000000 --- a/extensions/ql-vscode/src/remote-queries/repository-selection.ts +++ /dev/null @@ -1,279 +0,0 @@ -import { pathExists as fs_pathExists, stat, readFile } from "fs-extra"; -import { QuickPickItem, window } from "vscode"; -import { extLogger } from "../common"; -import { - getRemoteRepositoryLists, - getRemoteRepositoryListsPath, - isVariantAnalysisReposPanelEnabled, -} from "../config"; -import { OWNER_REGEX, REPO_REGEX } from "../pure/helpers-pure"; -import { UserCancellationException } from "../commandRunner"; -import { DbManager } from "../databases/db-manager"; -import { DbItemKind } from "../databases/db-item"; - -export interface RepositorySelection { - repositories?: string[]; - repositoryLists?: string[]; - owners?: string[]; -} - -interface RepoListQuickPickItem extends QuickPickItem { - repositories?: string[]; - repositoryList?: string; - useCustomRepo?: boolean; - useAllReposOfOwner?: boolean; -} - -interface RepoList { - label: string; - repositories: string[]; -} - -/** - * Gets the repositories or repository lists to run the query against. - * @returns The user selection. - */ -export async function getRepositorySelection( - dbManager?: DbManager, -): Promise { - if (isVariantAnalysisReposPanelEnabled()) { - const selectedDbItem = dbManager?.getSelectedDbItem(); - if (selectedDbItem) { - switch (selectedDbItem.kind) { - case DbItemKind.LocalDatabase || DbItemKind.LocalList: - throw new UserCancellationException( - "Local databases and lists are not supported yet.", - ); - case DbItemKind.RemoteSystemDefinedList: - return { repositoryLists: [selectedDbItem.listName] }; - case DbItemKind.RemoteUserDefinedList: - if (selectedDbItem.repos.length === 0) { - throw new UserCancellationException( - "The selected repository list is empty. Please add repositories to it before running a variant analysis.", - ); - } else { - return { - repositories: selectedDbItem.repos.map( - (repo) => repo.repoFullName, - ), - }; - } - case DbItemKind.RemoteOwner: - return { owners: [selectedDbItem.ownerName] }; - case DbItemKind.RemoteRepo: - return { repositories: [selectedDbItem.repoFullName] }; - } - } else { - throw new UserCancellationException( - "Please select a remote database to run the query against.", - ); - } - } - - const quickPickItems = [ - createCustomRepoQuickPickItem(), - createAllReposOfOwnerQuickPickItem(), - ...createSystemDefinedRepoListsQuickPickItems(), - ...(await createUserDefinedRepoListsQuickPickItems()), - ]; - - const options = { - placeHolder: - "Select a repository list. You can define repository lists in the `codeQL.variantAnalysis.repositoryLists` setting.", - ignoreFocusOut: true, - }; - - const quickpick = await window.showQuickPick( - quickPickItems, - options, - ); - - if (!quickpick) { - // We don't need to display a warning pop-up in this case, since the user just escaped out of the operation. - // We set 'true' to make this a silent exception. - throw new UserCancellationException("No repositories selected", true); - } - - if (quickpick.repositories?.length) { - void extLogger.log( - `Selected repositories: ${quickpick.repositories.join(", ")}`, - ); - return { repositories: quickpick.repositories }; - } else if (quickpick.repositoryList) { - void extLogger.log(`Selected repository list: ${quickpick.repositoryList}`); - return { repositoryLists: [quickpick.repositoryList] }; - } else if (quickpick.useCustomRepo) { - const customRepo = await getCustomRepo(); - if (customRepo === undefined) { - // The user cancelled, do nothing. - throw new UserCancellationException("No repositories selected", true); - } - if (!customRepo || !REPO_REGEX.test(customRepo)) { - throw new UserCancellationException( - "Invalid repository format. Please enter a valid repository in the format / (e.g. github/codeql)", - ); - } - void extLogger.log(`Entered repository: ${customRepo}`); - return { repositories: [customRepo] }; - } else if (quickpick.useAllReposOfOwner) { - const owner = await getOwner(); - if (owner === undefined) { - // The user cancelled, do nothing. - throw new UserCancellationException("No repositories selected", true); - } - if (!owner || !OWNER_REGEX.test(owner)) { - throw new Error(`Invalid user or organization: ${owner}`); - } - void extLogger.log(`Entered owner: ${owner}`); - return { owners: [owner] }; - } else { - // This means the user has selected something, but there is nothing actually linked to this item. We want to show - // this to the user. - throw new UserCancellationException("No repositories selected", false); - } -} - -/** - * Checks if the selection is valid or not. - * @param repoSelection The selection to check. - * @returns A boolean flag indicating if the selection is valid or not. - */ -export function isValidSelection(repoSelection: RepositorySelection): boolean { - const repositories = repoSelection.repositories || []; - const repositoryLists = repoSelection.repositoryLists || []; - const owners = repoSelection.owners || []; - - return ( - repositories.length > 0 || repositoryLists.length > 0 || owners.length > 0 - ); -} - -function createSystemDefinedRepoListsQuickPickItems(): RepoListQuickPickItem[] { - const topNs = [10, 100, 1000]; - - return topNs.map( - (n) => - ({ - label: `$(star) Top ${n}`, - repositoryList: `top_${n}`, - alwaysShow: true, - } as RepoListQuickPickItem), - ); -} - -async function readExternalRepoLists(): Promise { - const repoLists: RepoList[] = []; - - const path = getRemoteRepositoryListsPath(); - if (!path) { - return repoLists; - } - - await validateExternalRepoListsFile(path); - const json = await readExternalRepoListsJson(path); - - for (const [repoListName, repositories] of Object.entries(json)) { - if (!Array.isArray(repositories)) { - throw Error( - "Invalid repository lists file. It should contain an array of repositories for each list.", - ); - } - - repoLists.push({ - label: repoListName, - repositories, - }); - } - - return repoLists; -} - -async function validateExternalRepoListsFile(path: string): Promise { - const pathExists = await fs_pathExists(path); - if (!pathExists) { - throw Error(`External repository lists file does not exist at ${path}`); - } - - const pathStat = await stat(path); - if (pathStat.isDirectory()) { - throw Error( - "External repository lists path should not point to a directory", - ); - } -} - -async function readExternalRepoListsJson( - path: string, -): Promise> { - let json; - - try { - const fileContents = await readFile(path, "utf8"); - json = await JSON.parse(fileContents); - } catch (error) { - throw Error("Invalid repository lists file. It should contain valid JSON."); - } - - if (Array.isArray(json)) { - throw Error( - "Invalid repository lists file. It should be an object mapping names to a list of repositories.", - ); - } - - return json; -} - -function readRepoListsFromSettings(): RepoList[] { - const repoLists = getRemoteRepositoryLists(); - if (!repoLists) { - return []; - } - - return Object.entries(repoLists).map(([label, repositories]) => ({ - label, - repositories, - })); -} - -async function createUserDefinedRepoListsQuickPickItems(): Promise< - RepoListQuickPickItem[] -> { - const repoListsFromSetings = readRepoListsFromSettings(); - const repoListsFromExternalFile = await readExternalRepoLists(); - - return [...repoListsFromSetings, ...repoListsFromExternalFile]; -} - -function createCustomRepoQuickPickItem(): RepoListQuickPickItem { - return { - label: "$(edit) Enter a GitHub repository", - useCustomRepo: true, - alwaysShow: true, - }; -} - -function createAllReposOfOwnerQuickPickItem(): RepoListQuickPickItem { - return { - label: "$(edit) Enter a GitHub user or organization", - useAllReposOfOwner: true, - alwaysShow: true, - }; -} - -async function getCustomRepo(): Promise { - return await window.showInputBox({ - title: - "Enter a GitHub repository in the format / (e.g. github/codeql)", - placeHolder: "/", - prompt: - "Tip: you can save frequently used repositories in the `codeQL.variantAnalysis.repositoryLists` setting", - ignoreFocusOut: true, - }); -} - -async function getOwner(): Promise { - return await window.showInputBox({ - title: "Enter a GitHub user or organization", - ignoreFocusOut: true, - }); -} diff --git a/extensions/ql-vscode/src/remote-queries/repository.ts b/extensions/ql-vscode/src/remote-queries/repository.ts deleted file mode 100644 index f277b58af..000000000 --- a/extensions/ql-vscode/src/remote-queries/repository.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface Repository { - owner: string; - name: string; -} diff --git a/extensions/ql-vscode/src/remote-queries/shared/analysis-failure.ts b/extensions/ql-vscode/src/remote-queries/shared/analysis-failure.ts deleted file mode 100644 index 4de8643db..000000000 --- a/extensions/ql-vscode/src/remote-queries/shared/analysis-failure.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface AnalysisFailure { - nwo: string; - error: string; -} diff --git a/extensions/ql-vscode/src/remote-queries/shared/remote-query-result.ts b/extensions/ql-vscode/src/remote-queries/shared/remote-query-result.ts deleted file mode 100644 index ed634739f..000000000 --- a/extensions/ql-vscode/src/remote-queries/shared/remote-query-result.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { DownloadLink } from "../download-link"; -import { AnalysisFailure } from "./analysis-failure"; - -export interface RemoteQueryResult { - queryId: string; - queryTitle: string; - queryFileName: string; - queryFilePath: string; - queryText: string; - language: string; - workflowRunUrl: string; - totalRepositoryCount: number; - affectedRepositoryCount: number; - totalResultCount: number; - executionTimestamp: string; - executionDuration: string; - analysisSummaries: AnalysisSummary[]; - analysisFailures: AnalysisFailure[]; -} - -export interface AnalysisSummary { - nwo: string; - databaseSha: string; - resultCount: number; - sourceLocationPrefix: string; - downloadLink: DownloadLink; - fileSize: string; - starCount?: number; - lastUpdated?: number; -} diff --git a/extensions/ql-vscode/src/run-queries-shared.ts b/extensions/ql-vscode/src/run-queries-shared.ts index 6385b9f78..66f85e7e1 100644 --- a/extensions/ql-vscode/src/run-queries-shared.ts +++ b/extensions/ql-vscode/src/run-queries-shared.ts @@ -29,7 +29,7 @@ import { isQuickQueryPath } from "./quick-query"; import { nanoid } from "nanoid"; import { CodeQLCliServer } from "./cli"; import { SELECT_QUERY_NAME } from "./contextual/locationFinder"; -import { DatabaseManager } from "./databases"; +import { DatabaseManager } from "./local-databases"; import { DecodedBqrsChunk } from "./pure/bqrs-cli-types"; import { extLogger, Logger } from "./common"; import { generateSummarySymbolsFile } from "./log-insights/summary-parser"; diff --git a/extensions/ql-vscode/src/stories/common/CodePaths.stories.tsx b/extensions/ql-vscode/src/stories/common/CodePaths.stories.tsx index 2e62a594f..5b68f1c2d 100644 --- a/extensions/ql-vscode/src/stories/common/CodePaths.stories.tsx +++ b/extensions/ql-vscode/src/stories/common/CodePaths.stories.tsx @@ -4,7 +4,7 @@ import { ComponentStory, ComponentMeta } from "@storybook/react"; import { ThemeProvider } from "@primer/react"; import { CodePaths } from "../../view/common"; -import type { CodeFlow } from "../../remote-queries/shared/analysis-result"; +import type { CodeFlow } from "../../variant-analysis/shared/analysis-result"; export default { title: "Code Paths", diff --git a/extensions/ql-vscode/src/stories/remote-queries/TextButton.stories.tsx b/extensions/ql-vscode/src/stories/common/TextButton.stories.tsx similarity index 88% rename from extensions/ql-vscode/src/stories/remote-queries/TextButton.stories.tsx rename to extensions/ql-vscode/src/stories/common/TextButton.stories.tsx index fd3a6e376..6a4332e69 100644 --- a/extensions/ql-vscode/src/stories/remote-queries/TextButton.stories.tsx +++ b/extensions/ql-vscode/src/stories/common/TextButton.stories.tsx @@ -2,7 +2,7 @@ import * as React from "react"; import { ComponentStory, ComponentMeta } from "@storybook/react"; -import TextButtonComponent from "../../view/remote-queries/TextButton"; +import TextButtonComponent from "../../view/common/TextButton"; export default { title: "Text Button", diff --git a/extensions/ql-vscode/src/stories/remote-queries/data/analysesResultsMessage.json b/extensions/ql-vscode/src/stories/data/analysesResultsMessage.json similarity index 100% rename from extensions/ql-vscode/src/stories/remote-queries/data/analysesResultsMessage.json rename to extensions/ql-vscode/src/stories/data/analysesResultsMessage.json diff --git a/extensions/ql-vscode/src/stories/remote-queries/data/rawResults.json b/extensions/ql-vscode/src/stories/data/rawResults.json similarity index 100% rename from extensions/ql-vscode/src/stories/remote-queries/data/rawResults.json rename to extensions/ql-vscode/src/stories/data/rawResults.json diff --git a/extensions/ql-vscode/src/stories/remote-queries/DownloadButton.stories.tsx b/extensions/ql-vscode/src/stories/remote-queries/DownloadButton.stories.tsx deleted file mode 100644 index 1ea54caa0..000000000 --- a/extensions/ql-vscode/src/stories/remote-queries/DownloadButton.stories.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import * as React from "react"; - -import { ComponentStory, ComponentMeta } from "@storybook/react"; - -import DownloadButtonComponent from "../../view/remote-queries/DownloadButton"; - -export default { - title: "Download Button", - component: DownloadButtonComponent, - argTypes: { - onClick: { - action: "clicked", - table: { - disable: true, - }, - }, - }, -} as ComponentMeta; - -const Template: ComponentStory = (args) => ( - -); - -export const DownloadButton = Template.bind({}); -DownloadButton.args = { - text: "Download", -}; diff --git a/extensions/ql-vscode/src/stories/remote-queries/DownloadSpinner.stories.tsx b/extensions/ql-vscode/src/stories/remote-queries/DownloadSpinner.stories.tsx deleted file mode 100644 index 358933b77..000000000 --- a/extensions/ql-vscode/src/stories/remote-queries/DownloadSpinner.stories.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import * as React from "react"; - -import { ComponentMeta } from "@storybook/react"; - -import DownloadSpinnerComponent from "../../view/remote-queries/DownloadSpinner"; - -export default { - title: "Download Spinner", - component: DownloadSpinnerComponent, -} as ComponentMeta; - -export const DownloadSpinner = ; diff --git a/extensions/ql-vscode/src/stories/remote-queries/LastUpdated.stories.tsx b/extensions/ql-vscode/src/stories/remote-queries/LastUpdated.stories.tsx deleted file mode 100644 index b60bd0928..000000000 --- a/extensions/ql-vscode/src/stories/remote-queries/LastUpdated.stories.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import * as React from "react"; - -import { ComponentStory, ComponentMeta } from "@storybook/react"; - -import LastUpdatedComponent from "../../view/remote-queries/LastUpdated"; - -export default { - title: "MRVA/Last Updated", - component: LastUpdatedComponent, -} as ComponentMeta; - -const Template: ComponentStory = (args) => ( - -); - -export const LastUpdated = Template.bind({}); - -LastUpdated.args = { - lastUpdated: -3_600_000, // 1 hour ago -}; diff --git a/extensions/ql-vscode/src/stories/remote-queries/RemoteQueries.stories.tsx b/extensions/ql-vscode/src/stories/remote-queries/RemoteQueries.stories.tsx deleted file mode 100644 index b3ecd2667..000000000 --- a/extensions/ql-vscode/src/stories/remote-queries/RemoteQueries.stories.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import * as React from "react"; -import { useEffect } from "react"; - -import { ComponentStory, ComponentMeta } from "@storybook/react"; - -import { RemoteQueries } from "../../view/remote-queries/RemoteQueries"; - -import * as remoteQueryResult from "./data/remoteQueryResultMessage.json"; -import * as analysesResults from "./data/analysesResultsMessage.json"; - -export default { - title: "MRVA/Remote Queries", - component: RemoteQueries, -} as ComponentMeta; - -const Template: ComponentStory = () => { - useEffect(() => { - window.postMessage(remoteQueryResult); - window.postMessage(analysesResults); - }); - - return ; -}; - -export const Top10JavaScript = Template.bind({}); diff --git a/extensions/ql-vscode/src/stories/remote-queries/RepositoriesSearch.stories.tsx b/extensions/ql-vscode/src/stories/remote-queries/RepositoriesSearch.stories.tsx deleted file mode 100644 index 5207d5806..000000000 --- a/extensions/ql-vscode/src/stories/remote-queries/RepositoriesSearch.stories.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import * as React from "react"; -import { useState } from "react"; - -import { ComponentMeta } from "@storybook/react"; - -import RepositoriesSearchComponent from "../../view/remote-queries/RepositoriesSearch"; - -export default { - title: "MRVA/Repositories Search", - component: RepositoriesSearchComponent, - argTypes: { - filterValue: { - control: { - disable: true, - }, - }, - }, -} as ComponentMeta; - -export const RepositoriesSearch = () => { - const [filterValue, setFilterValue] = useState(""); - - return ( - - ); -}; diff --git a/extensions/ql-vscode/src/stories/remote-queries/data/remoteQueryResultMessage.json b/extensions/ql-vscode/src/stories/remote-queries/data/remoteQueryResultMessage.json deleted file mode 100644 index 6454e6455..000000000 --- a/extensions/ql-vscode/src/stories/remote-queries/data/remoteQueryResultMessage.json +++ /dev/null @@ -1,170 +0,0 @@ -{ - "t": "setRemoteQueryResult", - "queryResult": { - "queryId": "Empty block-LUzMYbSaBM4Lv6YP8GQ8j", - "queryTitle": "Empty block", - "queryFileName": "example.ql", - "queryFilePath": "/home/octocat/vscode-codeql-starter/codeql-custom-queries-javascript/example.ql", - "queryText": "/**\n * @name Empty block\n * @kind problem\n * @problem.severity warning\n * @id javascript/example/empty-block\n */\n\nimport javascript\n\nfrom BlockStmt b\nwhere b.getNumStmt() = 0\nselect b, \"This is an empty block.\"\n", - "language": "javascript", - "workflowRunUrl": "https://github.com/octocat/octo-repo/actions/runs/2955404400", - "totalRepositoryCount": 10, - "affectedRepositoryCount": 10, - "totalResultCount": 16338, - "executionTimestamp": "30 Aug at 0:14 pm", - "executionDuration": "1 minute", - "analysisSummaries": [ - { - "nwo": "angular/angular", - "databaseSha": "a360309f31afa97c4268a94fbd6a5108362a7182", - "resultCount": 8436, - "downloadLink": { - "id": "346232925", - "urlPath": "/repos/octocat/octo-repo/actions/artifacts/346232925", - "innerFilePath": "results.sarif", - "queryId": "Empty block-LUzMYbSaBM4Lv6YP8GQ8j" - }, - "sourceLocationPrefix": "/home/runner/work/bulk-builder/bulk-builder", - "fileSize": "5.67 MB", - "starCount": 83537, - "lastUpdated": -1066284 - }, - { - "nwo": "babel/babel", - "databaseSha": "0f62c58c79830cfe0afb26161e96e0e1b0482c01", - "resultCount": 5502, - "downloadLink": { - "id": "346232926", - "urlPath": "/repos/octocat/octo-repo/actions/artifacts/346232926", - "innerFilePath": "results.sarif", - "queryId": "Empty block-LUzMYbSaBM4Lv6YP8GQ8j" - }, - "sourceLocationPrefix": "/home/runner/work/bulk-builder/bulk-builder", - "fileSize": "3.80 MB", - "starCount": 41292, - "lastUpdated": -1600284 - }, - { - "nwo": "facebook/react", - "databaseSha": "e25648b0a89eab6f82bea2c2a1ef90866ac82b33", - "resultCount": 1205, - "downloadLink": { - "id": "346232919", - "urlPath": "/repos/octocat/octo-repo/actions/artifacts/346232919", - "innerFilePath": "results.sarif", - "queryId": "Empty block-LUzMYbSaBM4Lv6YP8GQ8j" - }, - "sourceLocationPrefix": "/home/runner/work/bulk-builder/bulk-builder", - "fileSize": "905.24 KB", - "starCount": 194007, - "lastUpdated": -379284 - }, - { - "nwo": "facebook/jest", - "databaseSha": "187566a70aa4b6aa5f74952b504bbeddb5854aef", - "resultCount": 643, - "downloadLink": { - "id": "346232921", - "urlPath": "/repos/octocat/octo-repo/actions/artifacts/346232921", - "innerFilePath": "results.sarif", - "queryId": "Empty block-LUzMYbSaBM4Lv6YP8GQ8j" - }, - "sourceLocationPrefix": "/home/runner/work/bulk-builder/bulk-builder", - "fileSize": "441.60 KB", - "starCount": 39987, - "lastUpdated": -113284 - }, - { - "nwo": "facebook/create-react-app", - "databaseSha": "f34d88e30c7d8be7181f728d1abc4fd8d5cd07d3", - "resultCount": 198, - "downloadLink": { - "id": "346232928", - "urlPath": "/repos/octocat/octo-repo/actions/artifacts/346232928", - "innerFilePath": "results.sarif", - "queryId": "Empty block-LUzMYbSaBM4Lv6YP8GQ8j" - }, - "sourceLocationPrefix": "/home/runner/work/bulk-builder/bulk-builder", - "fileSize": "132.28 KB", - "starCount": 96698, - "lastUpdated": -126284 - }, - { - "nwo": "vuejs/vue", - "databaseSha": "810f6d12edea47cde7f39eaf7ec3ae1b7300d40c", - "resultCount": 140, - "downloadLink": { - "id": "346232920", - "urlPath": "/repos/octocat/octo-repo/actions/artifacts/346232920", - "innerFilePath": "results.sarif", - "queryId": "Empty block-LUzMYbSaBM4Lv6YP8GQ8j" - }, - "sourceLocationPrefix": "/home/runner/work/bulk-builder/bulk-builder", - "fileSize": "89.99 KB", - "starCount": 198970, - "lastUpdated": -167284 - }, - { - "nwo": "lodash/lodash", - "databaseSha": "2da024c3b4f9947a48517639de7560457cd4ec6c", - "resultCount": 108, - "downloadLink": { - "id": "346232927", - "urlPath": "/repos/octocat/octo-repo/actions/artifacts/346232927", - "innerFilePath": "results.sarif", - "queryId": "Empty block-LUzMYbSaBM4Lv6YP8GQ8j" - }, - "sourceLocationPrefix": "/home/runner/work/bulk-builder/bulk-builder", - "fileSize": "70.43 KB", - "starCount": 54215, - "lastUpdated": -9080284 - }, - { - "nwo": "jquery/jquery", - "databaseSha": "d2436df36a4b2ef556907e734a90771f0dbdbcaf", - "resultCount": 67, - "downloadLink": { - "id": "346232922", - "urlPath": "/repos/octocat/octo-repo/actions/artifacts/346232922", - "innerFilePath": "results.sarif", - "queryId": "Empty block-LUzMYbSaBM4Lv6YP8GQ8j" - }, - "sourceLocationPrefix": "/home/runner/work/jquery/jquery", - "fileSize": "45.07 KB", - "starCount": 56629, - "lastUpdated": -23284 - }, - { - "nwo": "expressjs/express", - "databaseSha": "33e8dc303af9277f8a7e4f46abfdcb5e72f6797b", - "resultCount": 26, - "downloadLink": { - "id": "346232924", - "urlPath": "/repos/octocat/octo-repo/actions/artifacts/346232924", - "innerFilePath": "results.sarif", - "queryId": "Empty block-LUzMYbSaBM4Lv6YP8GQ8j" - }, - "sourceLocationPrefix": "/home/runner/work/bulk-builder/bulk-builder", - "fileSize": "16.96 KB", - "starCount": 58125, - "lastUpdated": -236284 - }, - { - "nwo": "twbs/bootstrap", - "databaseSha": "af1bd974bba7f6e7f90c5ed3f42738bd101926cb", - "resultCount": 13, - "downloadLink": { - "id": "346232923", - "urlPath": "/repos/octocat/octo-repo/actions/artifacts/346232923", - "innerFilePath": "results.sarif", - "queryId": "Empty block-LUzMYbSaBM4Lv6YP8GQ8j" - }, - "sourceLocationPrefix": "/home/runner/work/bootstrap/bootstrap", - "fileSize": "8.50 KB", - "starCount": 159230, - "lastUpdated": -1754284 - } - ], - "analysisFailures": [] - } -} diff --git a/extensions/ql-vscode/src/stories/remote-queries/AnalysisAlertResult.stories.tsx b/extensions/ql-vscode/src/stories/variant-analysis/AnalysisAlertResult.stories.tsx similarity index 83% rename from extensions/ql-vscode/src/stories/remote-queries/AnalysisAlertResult.stories.tsx rename to extensions/ql-vscode/src/stories/variant-analysis/AnalysisAlertResult.stories.tsx index 219d797a3..7afd3a8cf 100644 --- a/extensions/ql-vscode/src/stories/remote-queries/AnalysisAlertResult.stories.tsx +++ b/extensions/ql-vscode/src/stories/variant-analysis/AnalysisAlertResult.stories.tsx @@ -2,11 +2,11 @@ import * as React from "react"; import { ComponentStory, ComponentMeta } from "@storybook/react"; -import AnalysisAlertResult from "../../view/remote-queries/AnalysisAlertResult"; -import type { AnalysisAlert } from "../../remote-queries/shared/analysis-result"; +import AnalysisAlertResult from "../../view/variant-analysis/AnalysisAlertResult"; +import type { AnalysisAlert } from "../../variant-analysis/shared/analysis-result"; export default { - title: "Analysis Alert Result", + title: "Variant Analysis/Analysis Alert Result", component: AnalysisAlertResult, } as ComponentMeta; diff --git a/extensions/ql-vscode/src/stories/variant-analysis/FailureReasonAlert.stories.tsx b/extensions/ql-vscode/src/stories/variant-analysis/FailureReasonAlert.stories.tsx index a19144a1c..24fb4831e 100644 --- a/extensions/ql-vscode/src/stories/variant-analysis/FailureReasonAlert.stories.tsx +++ b/extensions/ql-vscode/src/stories/variant-analysis/FailureReasonAlert.stories.tsx @@ -1,7 +1,7 @@ import * as React from "react"; import { ComponentMeta, ComponentStory } from "@storybook/react"; -import { VariantAnalysisFailureReason } from "../../remote-queries/shared/variant-analysis"; +import { VariantAnalysisFailureReason } from "../../variant-analysis/shared/variant-analysis"; import { FailureReasonAlert } from "../../view/variant-analysis/FailureReasonAlert"; export default { diff --git a/extensions/ql-vscode/src/stories/variant-analysis/RepoRow.stories.tsx b/extensions/ql-vscode/src/stories/variant-analysis/RepoRow.stories.tsx index 95bce78bd..c51fe9f6a 100644 --- a/extensions/ql-vscode/src/stories/variant-analysis/RepoRow.stories.tsx +++ b/extensions/ql-vscode/src/stories/variant-analysis/RepoRow.stories.tsx @@ -6,15 +6,15 @@ import { VariantAnalysisContainer } from "../../view/variant-analysis/VariantAna import { VariantAnalysisRepoStatus, VariantAnalysisScannedRepositoryDownloadStatus, -} from "../../remote-queries/shared/variant-analysis"; +} from "../../variant-analysis/shared/variant-analysis"; import { AnalysisAlert, AnalysisRawResults, -} from "../../remote-queries/shared/analysis-result"; -import { createMockRepositoryWithMetadata } from "../../../test/factories/remote-queries/shared/repository"; +} from "../../variant-analysis/shared/analysis-result"; +import { createMockRepositoryWithMetadata } from "../../../test/factories/variant-analysis/shared/repository"; -import * as analysesResults from "../remote-queries/data/analysesResultsMessage.json"; -import * as rawResults from "../remote-queries/data/rawResults.json"; +import * as analysesResults from "../data/analysesResultsMessage.json"; +import * as rawResults from "../data/rawResults.json"; import { RepoRow, RepoRowProps } from "../../view/variant-analysis/RepoRow"; export default { diff --git a/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysis.stories.tsx b/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysis.stories.tsx index 3ca84c2ef..2c75e2ba8 100644 --- a/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysis.stories.tsx +++ b/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysis.stories.tsx @@ -11,9 +11,9 @@ import { VariantAnalysisScannedRepositoryResult, VariantAnalysisScannedRepositoryState, VariantAnalysisStatus, -} from "../../remote-queries/shared/variant-analysis"; -import { createMockVariantAnalysis } from "../../../test/factories/remote-queries/shared/variant-analysis"; -import { createMockRepositoryWithMetadata } from "../../../test/factories/remote-queries/shared/repository"; +} from "../../variant-analysis/shared/variant-analysis"; +import { createMockVariantAnalysis } from "../../../test/factories/variant-analysis/shared/variant-analysis"; +import { createMockRepositoryWithMetadata } from "../../../test/factories/variant-analysis/shared/repository"; export default { title: "Variant Analysis/Variant Analysis", diff --git a/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisActions.stories.tsx b/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisActions.stories.tsx index 613438609..4817b483d 100644 --- a/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisActions.stories.tsx +++ b/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisActions.stories.tsx @@ -3,7 +3,7 @@ import * as React from "react"; import { ComponentMeta, ComponentStory } from "@storybook/react"; import { VariantAnalysisContainer } from "../../view/variant-analysis/VariantAnalysisContainer"; -import { VariantAnalysisStatus } from "../../remote-queries/shared/variant-analysis"; +import { VariantAnalysisStatus } from "../../variant-analysis/shared/variant-analysis"; import { VariantAnalysisActions } from "../../view/variant-analysis/VariantAnalysisActions"; export default { diff --git a/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisAnalyzedRepos.stories.tsx b/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisAnalyzedRepos.stories.tsx index 1b18b9942..ab82de001 100644 --- a/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisAnalyzedRepos.stories.tsx +++ b/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisAnalyzedRepos.stories.tsx @@ -10,13 +10,13 @@ import { VariantAnalysisRepoStatus, VariantAnalysisScannedRepositoryDownloadStatus, VariantAnalysisStatus, -} from "../../remote-queries/shared/variant-analysis"; -import { AnalysisAlert } from "../../remote-queries/shared/analysis-result"; -import { createMockVariantAnalysis } from "../../../test/factories/remote-queries/shared/variant-analysis"; -import { createMockRepositoryWithMetadata } from "../../../test/factories/remote-queries/shared/repository"; -import { createMockScannedRepo } from "../../../test/factories/remote-queries/shared/scanned-repositories"; +} from "../../variant-analysis/shared/variant-analysis"; +import { AnalysisAlert } from "../../variant-analysis/shared/analysis-result"; +import { createMockVariantAnalysis } from "../../../test/factories/variant-analysis/shared/variant-analysis"; +import { createMockRepositoryWithMetadata } from "../../../test/factories/variant-analysis/shared/repository"; +import { createMockScannedRepo } from "../../../test/factories/variant-analysis/shared/scanned-repositories"; -import * as analysesResults from "../remote-queries/data/analysesResultsMessage.json"; +import * as analysesResults from "../data/analysesResultsMessage.json"; export default { title: "Variant Analysis/Analyzed Repos", diff --git a/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisHeader.stories.tsx b/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisHeader.stories.tsx index 30920a5f9..9473c6b82 100644 --- a/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisHeader.stories.tsx +++ b/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisHeader.stories.tsx @@ -7,9 +7,9 @@ import { VariantAnalysisHeader } from "../../view/variant-analysis/VariantAnalys import { VariantAnalysisRepoStatus, VariantAnalysisStatus, -} from "../../remote-queries/shared/variant-analysis"; -import { createMockVariantAnalysis } from "../../../test/factories/remote-queries/shared/variant-analysis"; -import { createMockScannedRepo } from "../../../test/factories/remote-queries/shared/scanned-repositories"; +} from "../../variant-analysis/shared/variant-analysis"; +import { createMockVariantAnalysis } from "../../../test/factories/variant-analysis/shared/variant-analysis"; +import { createMockScannedRepo } from "../../../test/factories/variant-analysis/shared/scanned-repositories"; export default { title: "Variant Analysis/Variant Analysis Header", diff --git a/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisOutcomePanels.stories.tsx b/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisOutcomePanels.stories.tsx index 9241bf183..be9ee2224 100644 --- a/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisOutcomePanels.stories.tsx +++ b/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisOutcomePanels.stories.tsx @@ -8,10 +8,10 @@ import { VariantAnalysisOutcomePanels } from "../../view/variant-analysis/Varian import { VariantAnalysisRepoStatus, VariantAnalysisStatus, -} from "../../remote-queries/shared/variant-analysis"; -import { createMockScannedRepo } from "../../../test/factories/remote-queries/shared/scanned-repositories"; -import { createMockVariantAnalysis } from "../../../test/factories/remote-queries/shared/variant-analysis"; -import { createMockRepositoryWithMetadata } from "../../../test/factories/remote-queries/shared/repository"; +} from "../../variant-analysis/shared/variant-analysis"; +import { createMockScannedRepo } from "../../../test/factories/variant-analysis/shared/scanned-repositories"; +import { createMockVariantAnalysis } from "../../../test/factories/variant-analysis/shared/variant-analysis"; +import { createMockRepositoryWithMetadata } from "../../../test/factories/variant-analysis/shared/repository"; import { defaultFilterSortState, RepositoriesFilterSortState, diff --git a/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisSkippedRepositoriesTab.stories.tsx b/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisSkippedRepositoriesTab.stories.tsx index 26d5bb970..a1bd18ce2 100644 --- a/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisSkippedRepositoriesTab.stories.tsx +++ b/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisSkippedRepositoriesTab.stories.tsx @@ -4,7 +4,7 @@ import { ComponentMeta, ComponentStory } from "@storybook/react"; import { VariantAnalysisContainer } from "../../view/variant-analysis/VariantAnalysisContainer"; import { VariantAnalysisSkippedRepositoriesTab } from "../../view/variant-analysis/VariantAnalysisSkippedRepositoriesTab"; -import { createMockRepositoryWithMetadata } from "../../../test/factories/remote-queries/shared/repository"; +import { createMockRepositoryWithMetadata } from "../../../test/factories/variant-analysis/shared/repository"; export default { title: "Variant Analysis/Variant Analysis Skipped Repositories Tab", diff --git a/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisStats.stories.tsx b/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisStats.stories.tsx index 1ca7e1bc0..90ff4c5e8 100644 --- a/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisStats.stories.tsx +++ b/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisStats.stories.tsx @@ -4,7 +4,7 @@ import { ComponentStory, ComponentMeta } from "@storybook/react"; import { VariantAnalysisContainer } from "../../view/variant-analysis/VariantAnalysisContainer"; import { VariantAnalysisStats } from "../../view/variant-analysis/VariantAnalysisStats"; -import { VariantAnalysisStatus } from "../../remote-queries/shared/variant-analysis"; +import { VariantAnalysisStatus } from "../../variant-analysis/shared/variant-analysis"; export default { title: "Variant Analysis/Variant Analysis Stats", diff --git a/extensions/ql-vscode/src/telemetry.ts b/extensions/ql-vscode/src/telemetry.ts index 9c2dec661..4c4cd79d2 100644 --- a/extensions/ql-vscode/src/telemetry.ts +++ b/extensions/ql-vscode/src/telemetry.ts @@ -13,7 +13,6 @@ import { LOG_TELEMETRY, isIntegrationTestMode, isCanary, - newTelemetryEnabled, } from "./config"; import * as appInsights from "applicationinsights"; import { extLogger } from "./common"; @@ -174,10 +173,6 @@ export class TelemetryListener extends ConfigListener { return; } - if (!newTelemetryEnabled()) { - return; - } - this.reporter.sendTelemetryEvent( "ui-interaction", { @@ -196,10 +191,6 @@ export class TelemetryListener extends ConfigListener { return; } - if (!newTelemetryEnabled()) { - return; - } - const properties: { [key: string]: string } = { isCanary: isCanary().toString(), message: error.redactedMessage, diff --git a/extensions/ql-vscode/src/test-adapter.ts b/extensions/ql-vscode/src/test-adapter.ts index b050a9ba2..326f93456 100644 --- a/extensions/ql-vscode/src/test-adapter.ts +++ b/extensions/ql-vscode/src/test-adapter.ts @@ -34,7 +34,7 @@ import { showAndLogWarningMessage, } from "./helpers"; import { testLogger } from "./common"; -import { DatabaseItem, DatabaseManager } from "./databases"; +import { DatabaseItem, DatabaseManager } from "./local-databases"; import { asError, getErrorMessage } from "./pure/helpers-pure"; import { redactableError } from "./pure/errors"; @@ -356,15 +356,11 @@ export class QLTestAdapter extends DisposableObject implements TestAdapter { tests: string[], cancellationToken: CancellationToken, ): Promise { - const workspacePaths = await getOnDiskWorkspaceFolders(); - for await (const event of await this.cliServer.runTests( - tests, - workspacePaths, - { - cancellationToken, - logger: testLogger, - }, - )) { + const workspacePaths = getOnDiskWorkspaceFolders(); + for await (const event of this.cliServer.runTests(tests, workspacePaths, { + cancellationToken, + logger: testLogger, + })) { const state = event.pass ? "passed" : event.messages?.length diff --git a/extensions/ql-vscode/src/remote-queries/bqrs-processing.ts b/extensions/ql-vscode/src/variant-analysis/bqrs-processing.ts similarity index 100% rename from extensions/ql-vscode/src/remote-queries/bqrs-processing.ts rename to extensions/ql-vscode/src/variant-analysis/bqrs-processing.ts diff --git a/extensions/ql-vscode/src/remote-queries/export-results.ts b/extensions/ql-vscode/src/variant-analysis/export-results.ts similarity index 94% rename from extensions/ql-vscode/src/remote-queries/export-results.ts rename to extensions/ql-vscode/src/variant-analysis/export-results.ts index afeed5c5a..faf3f0ee5 100644 --- a/extensions/ql-vscode/src/remote-queries/export-results.ts +++ b/extensions/ql-vscode/src/variant-analysis/export-results.ts @@ -18,10 +18,9 @@ import { generateVariantAnalysisMarkdown, MarkdownFile, RepositorySummary, -} from "./remote-queries-markdown-generation"; +} from "./markdown-generation"; import { pluralize } from "../pure/word"; import { VariantAnalysisManager } from "./variant-analysis-manager"; -import { assertNever } from "../pure/helpers-pure"; import { VariantAnalysis, VariantAnalysisScannedRepository, @@ -35,32 +34,28 @@ import { import { Credentials } from "../common/authentication"; /** - * Exports the results of the currently-selected remote query or variant analysis. + * Exports the results of the currently-selected variant analysis. */ -export async function exportSelectedRemoteQueryResults( +export async function exportSelectedVariantAnalysisResults( queryHistoryManager: QueryHistoryManager, ): Promise { const queryHistoryItem = queryHistoryManager.getCurrentQueryHistoryItem(); - if (!queryHistoryItem || queryHistoryItem.t === "local") { + if (!queryHistoryItem || queryHistoryItem.t !== "variant-analysis") { throw new Error( "No variant analysis results currently open. To open results, click an item in the query history view.", ); } - if (queryHistoryItem.t === "variant-analysis") { - return commands.executeCommand( - "codeQL.exportVariantAnalysisResults", - queryHistoryItem.variantAnalysis.id, - ); - } else { - assertNever(queryHistoryItem); - } + return commands.executeCommand( + "codeQL.exportVariantAnalysisResults", + queryHistoryItem.variantAnalysis.id, + ); } const MAX_VARIANT_ANALYSIS_EXPORT_PROGRESS_STEPS = 2; /** - * Exports the results of the given or currently-selected remote query. + * Exports the results of the given or currently-selected variant analysis. * The user is prompted to select the export format. */ export async function exportVariantAnalysisResults( diff --git a/extensions/ql-vscode/src/remote-queries/gh-api/gh-actions-api-client.ts b/extensions/ql-vscode/src/variant-analysis/gh-api/gh-actions-api-client.ts similarity index 100% rename from extensions/ql-vscode/src/remote-queries/gh-api/gh-actions-api-client.ts rename to extensions/ql-vscode/src/variant-analysis/gh-api/gh-actions-api-client.ts diff --git a/extensions/ql-vscode/src/remote-queries/gh-api/gh-api-client.ts b/extensions/ql-vscode/src/variant-analysis/gh-api/gh-api-client.ts similarity index 100% rename from extensions/ql-vscode/src/remote-queries/gh-api/gh-api-client.ts rename to extensions/ql-vscode/src/variant-analysis/gh-api/gh-api-client.ts diff --git a/extensions/ql-vscode/src/remote-queries/gh-api/repository.ts b/extensions/ql-vscode/src/variant-analysis/gh-api/repository.ts similarity index 100% rename from extensions/ql-vscode/src/remote-queries/gh-api/repository.ts rename to extensions/ql-vscode/src/variant-analysis/gh-api/repository.ts diff --git a/extensions/ql-vscode/src/remote-queries/gh-api/variant-analysis.ts b/extensions/ql-vscode/src/variant-analysis/gh-api/variant-analysis.ts similarity index 90% rename from extensions/ql-vscode/src/remote-queries/gh-api/variant-analysis.ts rename to extensions/ql-vscode/src/variant-analysis/gh-api/variant-analysis.ts index 407ff3940..d9f2ce7a8 100644 --- a/extensions/ql-vscode/src/remote-queries/gh-api/variant-analysis.ts +++ b/extensions/ql-vscode/src/variant-analysis/gh-api/variant-analysis.ts @@ -1,28 +1,19 @@ +import { QueryLanguage } from "../../common/query-language"; import { Repository, RepositoryWithMetadata } from "./repository"; export interface VariantAnalysisSubmissionRequest { action_repo_ref: string; - language: VariantAnalysisQueryLanguage; + language: QueryLanguage; query_pack: string; repositories?: string[]; repository_lists?: string[]; repository_owners?: string[]; } -export type VariantAnalysisQueryLanguage = - | "csharp" - | "cpp" - | "go" - | "java" - | "javascript" - | "python" - | "ruby" - | "swift"; - export interface VariantAnalysis { id: number; controller_repo: Repository; - query_language: VariantAnalysisQueryLanguage; + query_language: QueryLanguage; query_pack_url: string; created_at: string; updated_at: string; diff --git a/extensions/ql-vscode/src/remote-queries/remote-queries-markdown-generation.ts b/extensions/ql-vscode/src/variant-analysis/markdown-generation.ts similarity index 96% rename from extensions/ql-vscode/src/remote-queries/remote-queries-markdown-generation.ts rename to extensions/ql-vscode/src/variant-analysis/markdown-generation.ts index fd64fc3c7..272ee6f52 100644 --- a/extensions/ql-vscode/src/remote-queries/remote-queries-markdown-generation.ts +++ b/extensions/ql-vscode/src/variant-analysis/markdown-generation.ts @@ -2,20 +2,20 @@ import { CellValue } from "../pure/bqrs-cli-types"; import { tryGetRemoteLocation } from "../pure/bqrs-utils"; import { createRemoteFileRef } from "../pure/location-link-utils"; import { parseHighlightedLine, shouldHighlightLine } from "../pure/sarif-utils"; -import { convertNonPrintableChars } from "../text-utils"; -import { +import { convertNonPrintableChars } from "../pure/text-utils"; +import type { AnalysisAlert, AnalysisRawResults, CodeSnippet, FileLink, HighlightedRegion, } from "./shared/analysis-result"; -import { +import type { VariantAnalysis, VariantAnalysisScannedRepository, VariantAnalysisScannedRepositoryResult, } from "./shared/variant-analysis"; -import { RepositoryWithMetadata } from "./shared/repository"; +import type { RepositoryWithMetadata } from "./shared/repository"; export type MarkdownLinkType = "local" | "gist"; @@ -39,7 +39,7 @@ export interface VariantAnalysisMarkdown { * Generates markdown files with variant analysis results. */ export async function generateVariantAnalysisMarkdown( - variantAnalysis: VariantAnalysis, + variantAnalysis: Pick, results: AsyncIterable< [VariantAnalysisScannedRepository, VariantAnalysisScannedRepositoryResult] >, @@ -91,7 +91,7 @@ export async function generateVariantAnalysisMarkdown( // Generate summary file with links to individual files const summaryFile: MarkdownFile = generateVariantAnalysisMarkdownSummary( - variantAnalysis, + variantAnalysis.query, summaries, linkType, ); @@ -103,20 +103,16 @@ export async function generateVariantAnalysisMarkdown( } export function generateVariantAnalysisMarkdownSummary( - variantAnalysis: VariantAnalysis, + query: VariantAnalysis["query"], summaries: RepositorySummary[], linkType: MarkdownLinkType, ): MarkdownFile { const lines: string[] = []; // Title - lines.push(`### Results for "${variantAnalysis.query.name}"`, ""); + lines.push(`### Results for "${query.name}"`, ""); // Expandable section containing query text - const queryCodeBlock = [ - "```ql", - ...variantAnalysis.query.text.split("\n"), - "```", - ]; + const queryCodeBlock = ["```ql", ...query.text.split("\n"), "```"]; lines.push(...buildExpandableMarkdownSection("Query", queryCodeBlock)); // Padding between sections diff --git a/extensions/ql-vscode/src/variant-analysis/repository-selection.ts b/extensions/ql-vscode/src/variant-analysis/repository-selection.ts new file mode 100644 index 000000000..c289067de --- /dev/null +++ b/extensions/ql-vscode/src/variant-analysis/repository-selection.ts @@ -0,0 +1,62 @@ +import { UserCancellationException } from "../commandRunner"; +import { DbManager } from "../databases/db-manager"; +import { DbItemKind } from "../databases/db-item"; + +export interface RepositorySelection { + repositories?: string[]; + repositoryLists?: string[]; + owners?: string[]; +} + +/** + * Gets the repositories or repository lists to run the query against. + * @returns The user selection. + */ +export async function getRepositorySelection( + dbManager?: DbManager, +): Promise { + const selectedDbItem = dbManager?.getSelectedDbItem(); + if (selectedDbItem) { + switch (selectedDbItem.kind) { + case DbItemKind.LocalDatabase || DbItemKind.LocalList: + throw new UserCancellationException( + "Local databases and lists are not supported yet.", + ); + case DbItemKind.RemoteSystemDefinedList: + return { repositoryLists: [selectedDbItem.listName] }; + case DbItemKind.RemoteUserDefinedList: + if (selectedDbItem.repos.length === 0) { + throw new UserCancellationException( + "The selected repository list is empty. Please add repositories to it before running a variant analysis.", + ); + } else { + return { + repositories: selectedDbItem.repos.map((repo) => repo.repoFullName), + }; + } + case DbItemKind.RemoteOwner: + return { owners: [selectedDbItem.ownerName] }; + case DbItemKind.RemoteRepo: + return { repositories: [selectedDbItem.repoFullName] }; + } + } + + throw new UserCancellationException( + "Please select a remote database to run the query against.", + ); +} + +/** + * Checks if the selection is valid or not. + * @param repoSelection The selection to check. + * @returns A boolean flag indicating if the selection is valid or not. + */ +export function isValidSelection(repoSelection: RepositorySelection): boolean { + const repositories = repoSelection.repositories || []; + const repositoryLists = repoSelection.repositoryLists || []; + const owners = repoSelection.owners || []; + + return ( + repositories.length > 0 || repositoryLists.length > 0 || owners.length > 0 + ); +} diff --git a/extensions/ql-vscode/src/remote-queries/run-remote-query.ts b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts similarity index 91% rename from extensions/ql-vscode/src/remote-queries/run-remote-query.ts rename to extensions/ql-vscode/src/variant-analysis/run-remote-query.ts index b5a885b7f..ffeb1d89a 100644 --- a/extensions/ql-vscode/src/remote-queries/run-remote-query.ts +++ b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts @@ -57,8 +57,8 @@ export interface GeneratedQueryPack { /** * Two possibilities: - * 1. There is no qlpack.yml in this directory. Assume this is a lone query and generate a synthetic qlpack for it. - * 2. There is a qlpack.yml in this directory. Assume this is a query pack and use the yml to pack the query before uploading it. + * 1. There is no qlpack.yml (or codeql-pack.yml) in this directory. Assume this is a lone query and generate a synthetic qlpack for it. + * 2. There is a qlpack.yml (or codeql-pack.yml) in this directory. Assume this is a query pack and use the yml to pack the query before uploading it. * * @returns the entire qlpack as a base64 string. */ @@ -169,12 +169,12 @@ async function generateQueryPack( } async function findPackRoot(queryFile: string): Promise { - // recursively find the directory containing qlpack.yml + // recursively find the directory containing qlpack.yml or codeql-pack.yml let dir = dirname(queryFile); while (!(await getQlPackPath(dir))) { dir = dirname(dir); if (isFileSystemRoot(dir)) { - // there is no qlpack.yml in this directory or any parent directory. + // there is no qlpack.yml or codeql-pack.yml in this directory or any parent directory. // just use the query file's directory as the pack root. return dirname(queryFile); } @@ -223,7 +223,7 @@ export async function prepareRemoteQueryRun( uri: Uri | undefined, progress: ProgressCallback, token: CancellationToken, - dbManager?: DbManager, // the dbManager is only needed when variantAnalysisReposPanel is enabled + dbManager?: DbManager, ): Promise { if (!uri?.fsPath.endsWith(".ql")) { throw new UserCancellationException("Not a CodeQL query file."); @@ -300,14 +300,14 @@ export async function prepareRemoteQueryRun( } /** - * Fixes the qlpack.yml file to be correct in the context of the MRVA request. + * Fixes the qlpack.yml or codeql-pack.yml file to be correct in the context of the MRVA request. * * Performs the following fixes: * * - Updates the default suite of the query pack. This is used to ensure * only the specified query is run. * - Ensures the query pack name is set to the name expected by the server. - * - Removes any `${workspace}` version references from the qlpack.yml file. Converts them + * - Removes any `${workspace}` version references from the qlpack.yml or codeql-pack.yml file. Converts them * to `*` versions. * * @param queryPackDir The directory containing the query pack @@ -365,7 +365,10 @@ export async function getControllerRepo( credentials: Credentials, ): Promise { // Get the controller repo from the config, if it exists. - // If it doesn't exist, prompt the user to enter it, and save that value to the config. + // If it doesn't exist, prompt the user to enter it, check + // whether the repo exists, and save the nwo to the config. + + let shouldSetControllerRepo = false; let controllerRepoNwo: string | undefined; controllerRepoNwo = getRemoteControllerRepo(); if (!controllerRepoNwo || !REPO_REGEX.test(controllerRepoNwo)) { @@ -390,15 +393,31 @@ export async function getControllerRepo( "Invalid repository format. Must be a valid GitHub repository in the format /.", ); } + + shouldSetControllerRepo = true; + } + + void extLogger.log(`Using controller repository: ${controllerRepoNwo}`); + const controllerRepo = await getControllerRepoFromApi( + credentials, + controllerRepoNwo, + ); + + if (shouldSetControllerRepo) { void extLogger.log( `Setting the controller repository as: ${controllerRepoNwo}`, ); await setRemoteControllerRepo(controllerRepoNwo); } - void extLogger.log(`Using controller repository: ${controllerRepoNwo}`); - const [owner, repo] = controllerRepoNwo.split("/"); + return controllerRepo; +} +async function getControllerRepoFromApi( + credentials: Credentials, + nwo: string, +): Promise { + const [owner, repo] = nwo.split("/"); try { const controllerRepo = await getRepositoryFromNwo(credentials, owner, repo); void extLogger.log(`Controller repository ID: ${controllerRepo.id}`); @@ -419,6 +438,7 @@ export async function getControllerRepo( } } } + export function removeWorkspaceRefs(qlpack: QlPack) { for (const [key, value] of Object.entries(qlpack.dependencies || {})) { if (value === "${workspace}") { diff --git a/extensions/ql-vscode/src/remote-queries/sarif-processing.ts b/extensions/ql-vscode/src/variant-analysis/sarif-processing.ts similarity index 100% rename from extensions/ql-vscode/src/remote-queries/sarif-processing.ts rename to extensions/ql-vscode/src/variant-analysis/sarif-processing.ts diff --git a/extensions/ql-vscode/src/remote-queries/shared/analysis-result.ts b/extensions/ql-vscode/src/variant-analysis/shared/analysis-result.ts similarity index 62% rename from extensions/ql-vscode/src/remote-queries/shared/analysis-result.ts rename to extensions/ql-vscode/src/variant-analysis/shared/analysis-result.ts index 91a4be05f..024e84f90 100644 --- a/extensions/ql-vscode/src/remote-queries/shared/analysis-result.ts +++ b/extensions/ql-vscode/src/variant-analysis/shared/analysis-result.ts @@ -1,17 +1,5 @@ import { RawResultSet, ResultSetSchema } from "../../pure/bqrs-cli-types"; -export type AnalysisResultStatus = "InProgress" | "Completed" | "Failed"; - -export interface AnalysisResults { - nwo: string; - status: AnalysisResultStatus; - interpretedResults: AnalysisAlert[]; - rawResults?: AnalysisRawResults; - resultCount: number; - starCount?: number; - lastUpdated?: number; -} - export interface AnalysisRawResults { schema: ResultSetSchema; resultSet: RawResultSet; @@ -82,19 +70,3 @@ export interface AnalysisMessageLocationToken { } export type ResultSeverity = "Recommendation" | "Warning" | "Error"; - -/** - * Returns the number of (raw + interpreted) results for an analysis. - */ -export const getAnalysisResultCount = ( - analysisResults: AnalysisResults, -): number => { - const rawResultCount = analysisResults.rawResults?.resultSet.rows.length || 0; - return analysisResults.interpretedResults.length + rawResultCount; -}; - -/** - * Returns the total number of results for an analysis by adding all individual repo results. - */ -export const sumAnalysesResults = (analysesResults: AnalysisResults[]) => - analysesResults.reduce((acc, curr) => acc + getAnalysisResultCount(curr), 0); diff --git a/extensions/ql-vscode/src/remote-queries/shared/repository.ts b/extensions/ql-vscode/src/variant-analysis/shared/repository.ts similarity index 100% rename from extensions/ql-vscode/src/remote-queries/shared/repository.ts rename to extensions/ql-vscode/src/variant-analysis/shared/repository.ts diff --git a/extensions/ql-vscode/src/remote-queries/shared/result-limits.ts b/extensions/ql-vscode/src/variant-analysis/shared/result-limits.ts similarity index 100% rename from extensions/ql-vscode/src/remote-queries/shared/result-limits.ts rename to extensions/ql-vscode/src/variant-analysis/shared/result-limits.ts diff --git a/extensions/ql-vscode/src/remote-queries/shared/variant-analysis.ts b/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts similarity index 94% rename from extensions/ql-vscode/src/remote-queries/shared/variant-analysis.ts rename to extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts index 2c8174516..a370ddc59 100644 --- a/extensions/ql-vscode/src/remote-queries/shared/variant-analysis.ts +++ b/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts @@ -1,5 +1,6 @@ import { Repository, RepositoryWithMetadata } from "./repository"; import { AnalysisAlert, AnalysisRawResults } from "./analysis-result"; +import { QueryLanguage } from "../../common/query-language"; export interface VariantAnalysis { id: number; @@ -7,7 +8,7 @@ export interface VariantAnalysis { query: { name: string; filePath: string; - language: VariantAnalysisQueryLanguage; + language: QueryLanguage; text: string; }; databases: { @@ -26,23 +27,10 @@ export interface VariantAnalysis { skippedRepos?: VariantAnalysisSkippedRepositories; } -export enum VariantAnalysisQueryLanguage { - CSharp = "csharp", - Cpp = "cpp", - Go = "go", - Java = "java", - Javascript = "javascript", - Python = "python", - Ruby = "ruby", - Swift = "swift", -} - export function parseVariantAnalysisQueryLanguage( language: string, -): VariantAnalysisQueryLanguage | undefined { - return Object.values(VariantAnalysisQueryLanguage).find( - (x) => x === language, - ); +): QueryLanguage | undefined { + return Object.values(QueryLanguage).find((x) => x === language); } export enum VariantAnalysisStatus { @@ -148,7 +136,7 @@ export interface VariantAnalysisSubmission { query: { name: string; filePath: string; - language: VariantAnalysisQueryLanguage; + language: QueryLanguage; text: string; // Base64 encoded query pack. diff --git a/extensions/ql-vscode/src/remote-queries/variant-analysis-content-provider.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-content-provider.ts similarity index 100% rename from extensions/ql-vscode/src/remote-queries/variant-analysis-content-provider.ts rename to extensions/ql-vscode/src/variant-analysis/variant-analysis-content-provider.ts diff --git a/extensions/ql-vscode/src/remote-queries/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts similarity index 96% rename from extensions/ql-vscode/src/remote-queries/variant-analysis-manager.ts rename to extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 9bc6fbf57..9156617f4 100644 --- a/extensions/ql-vscode/src/remote-queries/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -60,7 +60,6 @@ import { } from "../pure/variant-analysis-filter-sort"; import { URLSearchParams } from "url"; import { DbManager } from "../databases/db-manager"; -import { isVariantAnalysisReposPanelEnabled } from "../config"; import { App } from "../common/app"; import { redactableError } from "../pure/errors"; @@ -106,7 +105,7 @@ export class VariantAnalysisManager private readonly cliServer: CodeQLCliServer, private readonly storagePath: string, private readonly variantAnalysisResultsManager: VariantAnalysisResultsManager, - private readonly dbManager?: DbManager, // the dbManager is only needed when variantAnalysisReposPanel is enabled + private readonly dbManager?: DbManager, ) { super(); this.variantAnalysisMonitor = this.push( @@ -635,25 +634,15 @@ export class VariantAnalysisManager return; } - let text: string[]; - if (isVariantAnalysisReposPanelEnabled()) { - text = [ - "{", - ` "name": "new-repo-list",`, - ` "repositories": [`, - ...fullNames.slice(0, -1).map((repo) => ` "${repo}",`), - ` "${fullNames[fullNames.length - 1]}"`, - ` ]`, - "}", - ]; - } else { - text = [ - '"new-repo-list": [', - ...fullNames.slice(0, -1).map((repo) => ` "${repo}",`), - ` "${fullNames[fullNames.length - 1]}"`, - "]", - ]; - } + const text = [ + "{", + ` "name": "new-repo-list",`, + ` "repositories": [`, + ...fullNames.slice(0, -1).map((repo) => ` "${repo}",`), + ` "${fullNames[fullNames.length - 1]}"`, + ` ]`, + "}", + ]; await env.clipboard.writeText(text.join(EOL)); } diff --git a/extensions/ql-vscode/src/remote-queries/variant-analysis-monitor.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-monitor.ts similarity index 100% rename from extensions/ql-vscode/src/remote-queries/variant-analysis-monitor.ts rename to extensions/ql-vscode/src/variant-analysis/variant-analysis-monitor.ts diff --git a/extensions/ql-vscode/src/remote-queries/variant-analysis-processor.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-processor.ts similarity index 100% rename from extensions/ql-vscode/src/remote-queries/variant-analysis-processor.ts rename to extensions/ql-vscode/src/variant-analysis/variant-analysis-processor.ts diff --git a/extensions/ql-vscode/src/remote-queries/variant-analysis-results-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-results-manager.ts similarity index 100% rename from extensions/ql-vscode/src/remote-queries/variant-analysis-results-manager.ts rename to extensions/ql-vscode/src/variant-analysis/variant-analysis-results-manager.ts diff --git a/extensions/ql-vscode/src/remote-queries/variant-analysis-view-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-view-manager.ts similarity index 100% rename from extensions/ql-vscode/src/remote-queries/variant-analysis-view-manager.ts rename to extensions/ql-vscode/src/variant-analysis/variant-analysis-view-manager.ts diff --git a/extensions/ql-vscode/src/remote-queries/variant-analysis-view-serializer.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-view-serializer.ts similarity index 100% rename from extensions/ql-vscode/src/remote-queries/variant-analysis-view-serializer.ts rename to extensions/ql-vscode/src/variant-analysis/variant-analysis-view-serializer.ts diff --git a/extensions/ql-vscode/src/remote-queries/variant-analysis-view.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-view.ts similarity index 100% rename from extensions/ql-vscode/src/remote-queries/variant-analysis-view.ts rename to extensions/ql-vscode/src/variant-analysis/variant-analysis-view.ts diff --git a/extensions/ql-vscode/src/view/common/CodePaths/CodeFlowsDropdown.tsx b/extensions/ql-vscode/src/view/common/CodePaths/CodeFlowsDropdown.tsx index 934552f32..743713aef 100644 --- a/extensions/ql-vscode/src/view/common/CodePaths/CodeFlowsDropdown.tsx +++ b/extensions/ql-vscode/src/view/common/CodePaths/CodeFlowsDropdown.tsx @@ -2,7 +2,7 @@ import * as React from "react"; import { ChangeEvent, SetStateAction, useCallback } from "react"; import { VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"; -import { CodeFlow } from "../../../remote-queries/shared/analysis-result"; +import { CodeFlow } from "../../../variant-analysis/shared/analysis-result"; const getCodeFlowName = (codeFlow: CodeFlow) => { const filePath = diff --git a/extensions/ql-vscode/src/view/common/CodePaths/CodePath.tsx b/extensions/ql-vscode/src/view/common/CodePaths/CodePath.tsx index 78e3b7bb1..1e83dbe52 100644 --- a/extensions/ql-vscode/src/view/common/CodePaths/CodePath.tsx +++ b/extensions/ql-vscode/src/view/common/CodePaths/CodePath.tsx @@ -4,7 +4,7 @@ import { AnalysisMessage, CodeFlow, ResultSeverity, -} from "../../../remote-queries/shared/analysis-result"; +} from "../../../variant-analysis/shared/analysis-result"; import { ThreadPath } from "./ThreadPath"; type CodePathProps = { diff --git a/extensions/ql-vscode/src/view/common/CodePaths/CodePaths.tsx b/extensions/ql-vscode/src/view/common/CodePaths/CodePaths.tsx index 717cde2f8..90bd3b44e 100644 --- a/extensions/ql-vscode/src/view/common/CodePaths/CodePaths.tsx +++ b/extensions/ql-vscode/src/view/common/CodePaths/CodePaths.tsx @@ -9,7 +9,7 @@ import { AnalysisMessage, CodeFlow, ResultSeverity, -} from "../../../remote-queries/shared/analysis-result"; +} from "../../../variant-analysis/shared/analysis-result"; import { CodePathsOverlay } from "./CodePathsOverlay"; import { useTelemetryOnChange } from "../telemetry"; diff --git a/extensions/ql-vscode/src/view/common/CodePaths/CodePathsOverlay.tsx b/extensions/ql-vscode/src/view/common/CodePaths/CodePathsOverlay.tsx index 9672e8fe1..7ba11898a 100644 --- a/extensions/ql-vscode/src/view/common/CodePaths/CodePathsOverlay.tsx +++ b/extensions/ql-vscode/src/view/common/CodePaths/CodePathsOverlay.tsx @@ -6,7 +6,7 @@ import { AnalysisMessage, CodeFlow, ResultSeverity, -} from "../../../remote-queries/shared/analysis-result"; +} from "../../../variant-analysis/shared/analysis-result"; import { useTelemetryOnChange } from "../telemetry"; import { SectionTitle } from "../SectionTitle"; import { VerticalSpace } from "../VerticalSpace"; diff --git a/extensions/ql-vscode/src/view/common/CodePaths/ThreadPath.tsx b/extensions/ql-vscode/src/view/common/CodePaths/ThreadPath.tsx index 4aa6340b0..e923a86dd 100644 --- a/extensions/ql-vscode/src/view/common/CodePaths/ThreadPath.tsx +++ b/extensions/ql-vscode/src/view/common/CodePaths/ThreadPath.tsx @@ -6,7 +6,7 @@ import { AnalysisMessage, ResultSeverity, ThreadFlow, -} from "../../../remote-queries/shared/analysis-result"; +} from "../../../variant-analysis/shared/analysis-result"; import { SectionTitle } from "../SectionTitle"; import { FileCodeSnippet } from "../FileCodeSnippet"; diff --git a/extensions/ql-vscode/src/view/common/CodePaths/__tests__/CodePaths.spec.tsx b/extensions/ql-vscode/src/view/common/CodePaths/__tests__/CodePaths.spec.tsx index c650dbb7d..a45ed417b 100644 --- a/extensions/ql-vscode/src/view/common/CodePaths/__tests__/CodePaths.spec.tsx +++ b/extensions/ql-vscode/src/view/common/CodePaths/__tests__/CodePaths.spec.tsx @@ -3,8 +3,8 @@ import { render as reactRender, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { CodePaths, CodePathsProps } from "../CodePaths"; -import { createMockCodeFlows } from "../../../../../test/factories/remote-queries/shared/CodeFlow"; -import { createMockAnalysisMessage } from "../../../../../test/factories/remote-queries/shared/AnalysisMessage"; +import { createMockCodeFlows } from "../../../../../test/factories/variant-analysis/shared/CodeFlow"; +import { createMockAnalysisMessage } from "../../../../../test/factories/variant-analysis/shared/AnalysisMessage"; describe(CodePaths.name, () => { const render = (props?: CodePathsProps) => diff --git a/extensions/ql-vscode/src/view/common/FileCodeSnippet/CodeSnippetCode.tsx b/extensions/ql-vscode/src/view/common/FileCodeSnippet/CodeSnippetCode.tsx index 4a23826f9..7182f1bce 100644 --- a/extensions/ql-vscode/src/view/common/FileCodeSnippet/CodeSnippetCode.tsx +++ b/extensions/ql-vscode/src/view/common/FileCodeSnippet/CodeSnippetCode.tsx @@ -1,7 +1,7 @@ import * as React from "react"; import styled from "styled-components"; -import { HighlightedRegion } from "../../../remote-queries/shared/analysis-result"; +import { HighlightedRegion } from "../../../variant-analysis/shared/analysis-result"; import { parseHighlightedLine, shouldHighlightLine, diff --git a/extensions/ql-vscode/src/view/common/FileCodeSnippet/CodeSnippetLine.tsx b/extensions/ql-vscode/src/view/common/FileCodeSnippet/CodeSnippetLine.tsx index 9b161bb56..e14773585 100644 --- a/extensions/ql-vscode/src/view/common/FileCodeSnippet/CodeSnippetLine.tsx +++ b/extensions/ql-vscode/src/view/common/FileCodeSnippet/CodeSnippetLine.tsx @@ -5,7 +5,7 @@ import { AnalysisMessage, HighlightedRegion, ResultSeverity, -} from "../../../remote-queries/shared/analysis-result"; +} from "../../../variant-analysis/shared/analysis-result"; import { CodeSnippetCode } from "./CodeSnippetCode"; import { CodeSnippetMessage } from "./CodeSnippetMessage"; diff --git a/extensions/ql-vscode/src/view/common/FileCodeSnippet/CodeSnippetMessage.tsx b/extensions/ql-vscode/src/view/common/FileCodeSnippet/CodeSnippetMessage.tsx index dade5e036..f3635b2df 100644 --- a/extensions/ql-vscode/src/view/common/FileCodeSnippet/CodeSnippetMessage.tsx +++ b/extensions/ql-vscode/src/view/common/FileCodeSnippet/CodeSnippetMessage.tsx @@ -5,7 +5,7 @@ import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"; import { AnalysisMessage, ResultSeverity, -} from "../../../remote-queries/shared/analysis-result"; +} from "../../../variant-analysis/shared/analysis-result"; import { createRemoteFileRef } from "../../../pure/location-link-utils"; import { VerticalSpace } from "../VerticalSpace"; import { sendTelemetry } from "../telemetry"; diff --git a/extensions/ql-vscode/src/view/common/FileCodeSnippet/FileCodeSnippet.tsx b/extensions/ql-vscode/src/view/common/FileCodeSnippet/FileCodeSnippet.tsx index 444dea093..3dbb4f868 100644 --- a/extensions/ql-vscode/src/view/common/FileCodeSnippet/FileCodeSnippet.tsx +++ b/extensions/ql-vscode/src/view/common/FileCodeSnippet/FileCodeSnippet.tsx @@ -8,7 +8,7 @@ import { FileLink, HighlightedRegion, ResultSeverity, -} from "../../../remote-queries/shared/analysis-result"; +} from "../../../variant-analysis/shared/analysis-result"; import { createRemoteFileRef } from "../../../pure/location-link-utils"; import { CodeSnippetMessage } from "./CodeSnippetMessage"; import { CodeSnippetLine } from "./CodeSnippetLine"; diff --git a/extensions/ql-vscode/src/view/remote-queries/TextButton.tsx b/extensions/ql-vscode/src/view/common/TextButton.tsx similarity index 100% rename from extensions/ql-vscode/src/view/remote-queries/TextButton.tsx rename to extensions/ql-vscode/src/view/common/TextButton.tsx diff --git a/extensions/ql-vscode/src/view/remote-queries/CollapsibleItem.tsx b/extensions/ql-vscode/src/view/remote-queries/CollapsibleItem.tsx deleted file mode 100644 index ad8f55411..000000000 --- a/extensions/ql-vscode/src/view/remote-queries/CollapsibleItem.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import * as React from "react"; -import styled from "styled-components"; -import { ChevronDownIcon, ChevronRightIcon } from "@primer/octicons-react"; -import { useState } from "react"; - -const Container = styled.div` - display: block; - vertical-align: middle; - cursor: pointer; -`; - -const TitleContainer = styled.span` - display: inline-block; -`; - -const Button = styled.button` - display: inline-block; - background-color: transparent; - color: var(--vscode-editor-foreground); - border: none; - padding-left: 0; - padding-right: 0.1em; -`; - -const CollapsibleItem = ({ - title, - children, -}: { - title: React.ReactNode; - children: React.ReactNode; -}) => { - const [isExpanded, setExpanded] = useState(false); - return ( - <> - setExpanded(!isExpanded)}> - - {title} - - {isExpanded && children} - - ); -}; - -export default CollapsibleItem; diff --git a/extensions/ql-vscode/src/view/remote-queries/DownloadButton.tsx b/extensions/ql-vscode/src/view/remote-queries/DownloadButton.tsx deleted file mode 100644 index c7328ab02..000000000 --- a/extensions/ql-vscode/src/view/remote-queries/DownloadButton.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import * as React from "react"; -import styled from "styled-components"; -import { DownloadIcon } from "@primer/octicons-react"; - -const ButtonLink = styled.a` - display: inline-block; - font-size: x-small; - text-decoration: none; - cursor: pointer; - vertical-align: middle; - - svg { - fill: var(--vscode-textLink-foreground); - } -`; - -const DownloadButton = ({ - text, - onClick, -}: { - text: string; - onClick: () => void; -}) => ( - - - {text} - -); - -export default DownloadButton; diff --git a/extensions/ql-vscode/src/view/remote-queries/DownloadSpinner.tsx b/extensions/ql-vscode/src/view/remote-queries/DownloadSpinner.tsx deleted file mode 100644 index 5778de717..000000000 --- a/extensions/ql-vscode/src/view/remote-queries/DownloadSpinner.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react"; -import * as React from "react"; -import styled from "styled-components"; - -const SpinnerContainer = styled.span` - vertical-align: middle; - display: inline-block; -`; - -const DownloadSpinner = () => ( - - - -); - -export default DownloadSpinner; diff --git a/extensions/ql-vscode/src/view/remote-queries/FullScreenModal.tsx b/extensions/ql-vscode/src/view/remote-queries/FullScreenModal.tsx deleted file mode 100644 index 4de087f7f..000000000 --- a/extensions/ql-vscode/src/view/remote-queries/FullScreenModal.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import * as React from "react"; -import { createPortal } from "react-dom"; -import styled from "styled-components"; -import { XCircleIcon } from "@primer/octicons-react"; - -const Container = styled.div` - position: fixed; - top: 0; - left: 0; - height: 100%; - width: 100%; - opacity: 1; - background-color: var(--vscode-editor-background); - z-index: 5000; - padding-top: 1em; -`; - -const CloseButton = styled.button` - position: absolute; - top: 1em; - right: 1em; - background-color: var(--vscode-editor-background); - border: none; -`; - -const FullScreenModal = ({ - setOpen, - containerElementId, - children, -}: { - setOpen: (open: boolean) => void; - containerElementId: string; - children: React.ReactNode; -}) => { - const containerElement = document.getElementById(containerElementId); - if (!containerElement) { - throw Error(`Could not find container element. Id: ${containerElementId}`); - } - - return createPortal( - <> - - setOpen(false)}> - - - {children} - - , - containerElement, - ); -}; - -export default FullScreenModal; diff --git a/extensions/ql-vscode/src/view/remote-queries/LastUpdated.tsx b/extensions/ql-vscode/src/view/remote-queries/LastUpdated.tsx deleted file mode 100644 index eb41ae4a7..000000000 --- a/extensions/ql-vscode/src/view/remote-queries/LastUpdated.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import * as React from "react"; -import { RepoPushIcon } from "@primer/octicons-react"; -import styled from "styled-components"; - -import { humanizeRelativeTime } from "../../pure/time"; - -const IconContainer = styled.span` - flex-grow: 0; - text-align: right; - margin-right: 0; -`; - -const Duration = styled.span` - text-align: left; - width: 8em; - margin-left: 0.5em; -`; - -type Props = { lastUpdated?: number }; - -const LastUpdated = ({ lastUpdated }: Props) => - // lastUpdated will be undefined for older results that were - // created before the lastUpdated field was added. - Number.isFinite(lastUpdated) ? ( - <> - - - - {humanizeRelativeTime(lastUpdated)} - - ) : ( - <> - ); - -export default LastUpdated; diff --git a/extensions/ql-vscode/src/view/remote-queries/RemoteQueries.tsx b/extensions/ql-vscode/src/view/remote-queries/RemoteQueries.tsx deleted file mode 100644 index 69c8292a6..000000000 --- a/extensions/ql-vscode/src/view/remote-queries/RemoteQueries.tsx +++ /dev/null @@ -1,551 +0,0 @@ -import * as React from "react"; -import { useEffect, useState } from "react"; -import { Flash, ThemeProvider } from "@primer/react"; -import { ToRemoteQueriesMessage } from "../../pure/interface-types"; -import { - AnalysisSummary, - RemoteQueryResult, -} from "../../remote-queries/shared/remote-query-result"; -import { MAX_RAW_RESULTS } from "../../remote-queries/shared/result-limits"; -import { vscode } from "../vscode-api"; -import { VSCodeBadge, VSCodeButton } from "@vscode/webview-ui-toolkit/react"; -import { - HorizontalSpace, - SectionTitle, - VerticalSpace, - ViewTitle, -} from "../common"; -import DownloadButton from "./DownloadButton"; -import { - AnalysisResults, - getAnalysisResultCount, -} from "../../remote-queries/shared/analysis-result"; -import DownloadSpinner from "./DownloadSpinner"; -import CollapsibleItem from "./CollapsibleItem"; -import { - AlertIcon, - CodeSquareIcon, - FileCodeIcon, - RepoIcon, - TerminalIcon, -} from "@primer/octicons-react"; -import AnalysisAlertResult from "./AnalysisAlertResult"; -import RawResultsTable from "./RawResultsTable"; -import RepositoriesSearch from "./RepositoriesSearch"; -import StarCount from "../common/StarCount"; -import SortRepoFilter, { Sort, sorter } from "./SortRepoFilter"; -import LastUpdated from "./LastUpdated"; -import RepoListCopyButton from "./RepoListCopyButton"; - -import "./baseStyles.css"; -import "./remoteQueries.css"; - -const numOfReposInContractedMode = 10; - -const emptyQueryResult: RemoteQueryResult = { - queryId: "", - queryTitle: "", - queryFileName: "", - queryFilePath: "", - queryText: "", - language: "", - workflowRunUrl: "", - totalRepositoryCount: 0, - affectedRepositoryCount: 0, - totalResultCount: 0, - executionTimestamp: "", - executionDuration: "", - analysisSummaries: [], - analysisFailures: [], -}; - -const downloadAnalysisResults = (analysisSummary: AnalysisSummary) => { - vscode.postMessage({ - t: "remoteQueryDownloadAnalysisResults", - analysisSummary, - }); -}; - -const downloadAllAnalysesResults = (query: RemoteQueryResult) => { - vscode.postMessage({ - t: "remoteQueryDownloadAllAnalysesResults", - analysisSummaries: query.analysisSummaries, - }); -}; - -const openQueryFile = (queryResult: RemoteQueryResult) => { - vscode.postMessage({ - t: "openFile", - filePath: queryResult.queryFilePath, - }); -}; - -const openQueryTextVirtualFile = (queryResult: RemoteQueryResult) => { - vscode.postMessage({ - t: "openVirtualFile", - queryText: queryResult.queryText, - }); -}; - -function createResultsDescription(queryResult: RemoteQueryResult) { - const reposCount = `${queryResult.totalRepositoryCount} ${ - queryResult.totalRepositoryCount === 1 ? "repository" : "repositories" - }`; - return `${queryResult.totalResultCount} results from running against ${reposCount} (${queryResult.executionDuration}), ${queryResult.executionTimestamp}`; -} - -const sumAnalysesResults = (analysesResults: AnalysisResults[]) => - analysesResults.reduce((acc, curr) => acc + getAnalysisResultCount(curr), 0); - -const QueryInfo = (queryResult: RemoteQueryResult) => ( - <> - - {createResultsDescription(queryResult)} - - - openQueryFile(queryResult)} - > - - {" "} - {" "} - - {queryResult.queryFileName} - - - - openQueryTextVirtualFile(queryResult)} - > - - {" "} - {" "} - - Query - - - - - - {" "} - {" "} - - Logs - - - -); - -const Failures = (queryResult: RemoteQueryResult) => { - if (queryResult.analysisFailures.length === 0) { - return <>; - } - return ( - <> - - - {queryResult.analysisFailures.map((f, i) => ( -
-

- - {f.nwo}: - {f.error} -

- {i === queryResult.analysisFailures.length - 1 ? ( - <> - ) : ( - - )} -
- ))} -
- - ); -}; - -const SummaryTitleWithResults = ({ - queryResult, - analysesResults, - sort, - setSort, -}: { - queryResult: RemoteQueryResult; - analysesResults: AnalysisResults[]; - sort: Sort; - setSort: (sort: Sort) => void; -}) => { - const showDownloadButton = - queryResult.totalResultCount !== sumAnalysesResults(analysesResults); - - return ( -
- - Repositories with results ({queryResult.affectedRepositoryCount}): - - {showDownloadButton && ( - downloadAllAnalysesResults(queryResult)} - /> - )} -
- - - -
-
- ); -}; - -const SummaryTitleNoResults = () => ( -
- No results found -
-); - -const SummaryItemDownload = ({ - analysisSummary, - analysisResults, -}: { - analysisSummary: AnalysisSummary; - analysisResults: AnalysisResults | undefined; -}) => { - if (!analysisResults || analysisResults.status === "Failed") { - return ( - downloadAnalysisResults(analysisSummary)} - /> - ); - } - - if (analysisResults.status === "InProgress") { - return ( - <> - - - - ); - } - - return <>; -}; - -const SummaryItem = ({ - analysisSummary, - analysisResults, -}: { - analysisSummary: AnalysisSummary; - analysisResults: AnalysisResults | undefined; -}) => ( - <> - - - - {analysisSummary.nwo} - - - {analysisSummary.resultCount.toString()} - - - - - - - -); - -const Summary = ({ - queryResult, - analysesResults, - sort, - setSort, -}: { - queryResult: RemoteQueryResult; - analysesResults: AnalysisResults[]; - sort: Sort; - setSort: (sort: Sort) => void; -}) => { - const [repoListExpanded, setRepoListExpanded] = useState(false); - const numOfReposToShow = repoListExpanded - ? queryResult.analysisSummaries.length - : numOfReposInContractedMode; - - return ( - <> - {queryResult.affectedRepositoryCount === 0 ? ( - - ) : ( - - )} - -
    - {queryResult.analysisSummaries - .slice(0, numOfReposToShow) - .sort(sorter(sort)) - .map((summary, i) => ( -
  • - a.nwo === summary.nwo, - )} - /> -
  • - ))} -
- {queryResult.analysisSummaries.length > numOfReposInContractedMode && ( - - )} - - ); -}; - -const AnalysesResultsTitle = ({ - totalAnalysesResults, - totalResults, -}: { - totalAnalysesResults: number; - totalResults: number; -}) => { - if (totalAnalysesResults === totalResults) { - return {totalAnalysesResults} results; - } - - return ( - - {totalAnalysesResults}/{totalResults} results - - ); -}; - -const exportResults = (queryResult: RemoteQueryResult) => { - vscode.postMessage({ - t: "remoteQueryExportResults", - queryId: queryResult.queryId, - }); -}; - -const AnalysesResultsDescription = ({ - queryResult, - analysesResults, -}: { - queryResult: RemoteQueryResult; - analysesResults: AnalysisResults[]; -}) => { - const showDownloadsMessage = queryResult.analysisSummaries.some( - (s) => - !analysesResults.some((a) => a.nwo === s.nwo && a.status === "Completed"), - ); - const downloadsMessage = ( - <> - - Some results haven't been downloaded automatically because of their - size or because enough were downloaded already. Download them manually - from the list above if you want to see them here. - - ); - - const showMaxResultsMessage = analysesResults.some( - (a) => a.rawResults?.capped, - ); - const maxRawResultsMessage = ( - <> - - Some repositories have more than {MAX_RAW_RESULTS} results. We will only - show you up to  - {MAX_RAW_RESULTS} results for each repository. - - ); - - return ( - <> - {showDownloadsMessage && downloadsMessage} - {showMaxResultsMessage && maxRawResultsMessage} - - ); -}; - -const RepoAnalysisResults = (analysisResults: AnalysisResults) => { - const numOfResults = getAnalysisResultCount(analysisResults); - const title = ( - <> - {analysisResults.nwo} - - {numOfResults.toString()} - - ); - - return ( - -
    - {analysisResults.interpretedResults.map((r, i) => ( -
  • - - -
  • - ))} -
- {analysisResults.rawResults && ( - - )} -
- ); -}; - -const AnalysesResults = ({ - queryResult, - analysesResults, - totalResults, - sort, -}: { - queryResult: RemoteQueryResult; - analysesResults: AnalysisResults[]; - totalResults: number; - sort: Sort; -}) => { - const totalAnalysesResults = sumAnalysesResults(analysesResults); - const [filterValue, setFilterValue] = useState(""); - - if (totalResults === 0) { - return <>; - } - - return ( - <> - -
-
- -
-
- exportResults(queryResult)}> - Export all - -
-
- - - - - -
    - {analysesResults - .filter( - (a) => - a.interpretedResults.length || - a.rawResults?.resultSet?.rows?.length, - ) - .filter((a) => - a.nwo.toLowerCase().includes(filterValue.toLowerCase()), - ) - .sort(sorter(sort)) - .map((r) => ( -
  • - -
  • - ))} -
- - ); -}; - -export function RemoteQueries(): JSX.Element { - const [queryResult, setQueryResult] = - useState(emptyQueryResult); - const [analysesResults, setAnalysesResults] = useState([]); - const [sort, setSort] = useState("name"); - - useEffect(() => { - const listener = (evt: MessageEvent) => { - if (evt.origin === window.origin) { - const msg: ToRemoteQueriesMessage = evt.data; - if (msg.t === "setRemoteQueryResult") { - setQueryResult(msg.queryResult); - } else if (msg.t === "setAnalysesResults") { - setAnalysesResults(msg.analysesResults); - } - } else { - // sanitize origin - const origin = evt.origin.replace(/\n|\r/g, ""); - console.error(`Invalid event origin ${origin}`); - } - }; - window.addEventListener("message", listener); - - return () => { - window.removeEventListener("message", listener); - }; - }, []); - - if (!queryResult) { - return
Waiting for results to load.
; - } - - try { - return ( -
- - {queryResult.queryTitle} - - - - - -
- ); - } catch (err) { - console.error(err); - return
There was an error displaying the view.
; - } -} diff --git a/extensions/ql-vscode/src/view/remote-queries/RepoListCopyButton.tsx b/extensions/ql-vscode/src/view/remote-queries/RepoListCopyButton.tsx deleted file mode 100644 index a241c17a9..000000000 --- a/extensions/ql-vscode/src/view/remote-queries/RepoListCopyButton.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import * as React from "react"; -import { vscode } from "../vscode-api"; -import { RemoteQueryResult } from "../../remote-queries/shared/remote-query-result"; -import { CopyIcon } from "@primer/octicons-react"; -import { IconButton } from "@primer/react"; - -const copyRepositoryList = (queryResult: RemoteQueryResult) => { - vscode.postMessage({ - t: "copyRepoList", - queryId: queryResult.queryId, - }); -}; - -const RepoListCopyButton = ({ - queryResult, -}: { - queryResult: RemoteQueryResult; -}) => ( - copyRepositoryList(queryResult)} - /> -); - -export default RepoListCopyButton; diff --git a/extensions/ql-vscode/src/view/remote-queries/RepositoriesSearch.tsx b/extensions/ql-vscode/src/view/remote-queries/RepositoriesSearch.tsx deleted file mode 100644 index dfcdf14ad..000000000 --- a/extensions/ql-vscode/src/view/remote-queries/RepositoriesSearch.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import * as React from "react"; -import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"; - -interface RepositoriesSearchProps { - filterValue: string; - setFilterValue: (value: string) => void; -} - -const RepositoriesSearch = ({ - filterValue, - setFilterValue, -}: RepositoriesSearchProps) => { - return ( - <> - - setFilterValue((e.target as HTMLInputElement).value) - } - > - - - - ); -}; - -export default RepositoriesSearch; diff --git a/extensions/ql-vscode/src/view/remote-queries/SortRepoFilter.tsx b/extensions/ql-vscode/src/view/remote-queries/SortRepoFilter.tsx deleted file mode 100644 index 6c63b5c73..000000000 --- a/extensions/ql-vscode/src/view/remote-queries/SortRepoFilter.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import * as React from "react"; -import { FilterIcon } from "@primer/octicons-react"; -import { ActionList, ActionMenu, IconButton } from "@primer/react"; -import styled from "styled-components"; - -const SortWrapper = styled.span` - flex-grow: 2; - text-align: right; - margin-right: 0; -`; - -export type Sort = "name" | "stars" | "results" | "lastUpdated"; -type Props = { - sort: Sort; - setSort: (sort: Sort) => void; -}; - -type Sortable = { - nwo: string; - starCount?: number; - resultCount?: number; - lastUpdated?: number; -}; - -const sortBy = [ - { name: "Sort by Name", sort: "name" }, - { name: "Sort by Results", sort: "results" }, - { name: "Sort by Stars", sort: "stars" }, - { name: "Sort by Last Updated", sort: "lastUpdated" }, -]; - -export function sorter( - sort: Sort, -): (left: Sortable, right: Sortable) => number { - // stars and results are highest to lowest - // name is alphabetical - return (left: Sortable, right: Sortable) => { - if (sort === "stars") { - const stars = (right.starCount || 0) - (left.starCount || 0); - if (stars !== 0) { - return stars; - } - } - if (sort === "lastUpdated") { - const lastUpdated = (right.lastUpdated || 0) - (left.lastUpdated || 0); - if (lastUpdated !== 0) { - return lastUpdated; - } - } - if (sort === "results") { - const results = (right.resultCount || 0) - (left.resultCount || 0); - if (results !== 0) { - return results; - } - } - - // Fall back on name compare if results, stars, or lastUpdated are equal - return left.nwo.localeCompare(right.nwo, undefined, { - sensitivity: "base", - }); - }; -} - -const SortRepoFilter = ({ sort, setSort }: Props) => { - return ( - - - - - - - - - {sortBy.map((type, index) => ( - setSort(type.sort as Sort)} - > - {type.name} - - ))} - - - - - ); -}; - -export default SortRepoFilter; diff --git a/extensions/ql-vscode/src/view/remote-queries/baseStyles.css b/extensions/ql-vscode/src/view/remote-queries/baseStyles.css deleted file mode 100644 index 4d3d4d543..000000000 --- a/extensions/ql-vscode/src/view/remote-queries/baseStyles.css +++ /dev/null @@ -1,4 +0,0 @@ -body { - font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, - sans-serif, Apple Color Emoji, Segoe UI Emoji; -} diff --git a/extensions/ql-vscode/src/view/remote-queries/index.tsx b/extensions/ql-vscode/src/view/remote-queries/index.tsx deleted file mode 100644 index d7292eeca..000000000 --- a/extensions/ql-vscode/src/view/remote-queries/index.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import * as React from "react"; -import { WebviewDefinition } from "../webview-definition"; -import { RemoteQueries } from "./RemoteQueries"; - -const definition: WebviewDefinition = { - component: , -}; - -export default definition; diff --git a/extensions/ql-vscode/src/view/remote-queries/remoteQueries.css b/extensions/ql-vscode/src/view/remote-queries/remoteQueries.css deleted file mode 100644 index 943de24f4..000000000 --- a/extensions/ql-vscode/src/view/remote-queries/remoteQueries.css +++ /dev/null @@ -1,53 +0,0 @@ -.vscode-codeql__remote-queries { - max-width: 55em; -} - -.vscode-codeql__query-info-link { - text-decoration: none; - padding-right: 1em; - color: var(--vscode-editor-foreground); -} - -.vscode-codeql__query-info-link:hover { - color: var(--vscode-editor-foreground); -} - -.vscode-codeql__query-summary-container { - padding-top: 1.5em; - display: flex; -} - -.vscode-codeql__analysis-summaries-list-item { - margin-top: 0.5em; - display: flex; -} - -.vscode-codeql__analyses-results-list-item { - padding-top: 0.5em; -} - -.vscode-codeql__analysis-item { - padding-right: 0.1em; -} - -.vscode-codeql__expand-button { - background: none; - color: var(--vscode-textLink-foreground); - border: none; - cursor: pointer; - padding-top: 1em; - font-size: x-small; -} - -.vscode-codeql__analysis-failure { - margin: 0; - font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, - Liberation Mono, monospace; - color: var(--vscode-editor-foreground); -} - -.vscode-codeql__flat-list { - list-style-type: none; - margin: 0; - padding: 0.5em 0 0 0; -} diff --git a/extensions/ql-vscode/src/view/results/graph.tsx b/extensions/ql-vscode/src/view/results/graph.tsx index e5f9b86a8..ae5722bcc 100644 --- a/extensions/ql-vscode/src/view/results/graph.tsx +++ b/extensions/ql-vscode/src/view/results/graph.tsx @@ -5,7 +5,7 @@ import { InterpretedResultSet, GraphInterpretationData, } from "../../pure/interface-types"; -import { graphviz } from "d3-graphviz"; +import { graphviz, GraphvizOptions } from "d3-graphviz"; import { tryGetLocationFromString } from "../../pure/bqrs-utils"; export type GraphProps = ResultTableProps & { resultSet: InterpretedResultSet; @@ -59,11 +59,12 @@ export class Graph extends React.Component { return; } - const options = { + const options: GraphvizOptions = { fit: true, fade: false, growEnteringEdges: false, zoom: true, + useWorker: false, }; const element = document.querySelector(`#${graphId}`); @@ -77,8 +78,7 @@ export class Graph extends React.Component { const borderColor = getComputedStyle(element).borderColor; let firstPolygon = true; - graphviz(`#${graphId}`) - .options(options) + graphviz(`#${graphId}`, options) .attributer(function (d) { if (d.tag === "a") { const url = d.attributes["xlink:href"] || d.attributes["href"]; diff --git a/extensions/ql-vscode/src/view/results/result-table-utils.tsx b/extensions/ql-vscode/src/view/results/result-table-utils.tsx index af1037dd1..7c75d4900 100644 --- a/extensions/ql-vscode/src/view/results/result-table-utils.tsx +++ b/extensions/ql-vscode/src/view/results/result-table-utils.tsx @@ -9,7 +9,7 @@ import { } from "../../pure/interface-types"; import { assertNever } from "../../pure/helpers-pure"; import { vscode } from "../vscode-api"; -import { convertNonPrintableChars } from "../../text-utils"; +import { convertNonPrintableChars } from "../../pure/text-utils"; import { sendTelemetry } from "../common/telemetry"; export interface ResultTableProps { diff --git a/extensions/ql-vscode/src/view/remote-queries/AnalysisAlertResult.tsx b/extensions/ql-vscode/src/view/variant-analysis/AnalysisAlertResult.tsx similarity index 90% rename from extensions/ql-vscode/src/view/remote-queries/AnalysisAlertResult.tsx rename to extensions/ql-vscode/src/view/variant-analysis/AnalysisAlertResult.tsx index df91e487a..7f54ae310 100644 --- a/extensions/ql-vscode/src/view/remote-queries/AnalysisAlertResult.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/AnalysisAlertResult.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { AnalysisAlert } from "../../remote-queries/shared/analysis-result"; +import { AnalysisAlert } from "../../variant-analysis/shared/analysis-result"; import { CodePaths, FileCodeSnippet } from "../common"; const AnalysisAlertResult = ({ alert }: { alert: AnalysisAlert }) => { diff --git a/extensions/ql-vscode/src/view/variant-analysis/AnalyzedRepoItemContent.tsx b/extensions/ql-vscode/src/view/variant-analysis/AnalyzedRepoItemContent.tsx index 085cbd84d..9d5d630be 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/AnalyzedRepoItemContent.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/AnalyzedRepoItemContent.tsx @@ -3,13 +3,13 @@ import styled from "styled-components"; import { AnalysisAlert, AnalysisRawResults, -} from "../../remote-queries/shared/analysis-result"; -import AnalysisAlertResult from "../remote-queries/AnalysisAlertResult"; -import RawResultsTable from "../remote-queries/RawResultsTable"; +} from "../../variant-analysis/shared/analysis-result"; +import AnalysisAlertResult from "./AnalysisAlertResult"; +import RawResultsTable from "./RawResultsTable"; import { VariantAnalysisRepoStatus, VariantAnalysisScannedRepositoryDownloadStatus, -} from "../../remote-queries/shared/variant-analysis"; +} from "../../variant-analysis/shared/variant-analysis"; import { Alert } from "../common"; const ContentContainer = styled.div` diff --git a/extensions/ql-vscode/src/view/variant-analysis/FailureReasonAlert.tsx b/extensions/ql-vscode/src/view/variant-analysis/FailureReasonAlert.tsx index 1965d8a4d..2c5f947be 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/FailureReasonAlert.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/FailureReasonAlert.tsx @@ -3,7 +3,7 @@ import { ReactNode } from "react"; import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"; import { Alert } from "../common"; import { vscode } from "../vscode-api"; -import { VariantAnalysisFailureReason } from "../../remote-queries/shared/variant-analysis"; +import { VariantAnalysisFailureReason } from "../../variant-analysis/shared/variant-analysis"; type Props = { failureReason: VariantAnalysisFailureReason; diff --git a/extensions/ql-vscode/src/view/remote-queries/RawResultsTable.tsx b/extensions/ql-vscode/src/view/variant-analysis/RawResultsTable.tsx similarity index 97% rename from extensions/ql-vscode/src/view/remote-queries/RawResultsTable.tsx rename to extensions/ql-vscode/src/view/variant-analysis/RawResultsTable.tsx index 6ad78c299..3ebbde553 100644 --- a/extensions/ql-vscode/src/view/remote-queries/RawResultsTable.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/RawResultsTable.tsx @@ -8,8 +8,8 @@ import { ResultSetSchema, } from "../../pure/bqrs-cli-types"; import { tryGetRemoteLocation } from "../../pure/bqrs-utils"; -import TextButton from "./TextButton"; -import { convertNonPrintableChars } from "../../text-utils"; +import TextButton from "../common/TextButton"; +import { convertNonPrintableChars } from "../../pure/text-utils"; import { sendTelemetry, useTelemetryOnChange } from "../common/telemetry"; const numOfResultsInContractedMode = 5; diff --git a/extensions/ql-vscode/src/view/variant-analysis/RepoRow.tsx b/extensions/ql-vscode/src/view/variant-analysis/RepoRow.tsx index 2a54fca0e..15334f0da 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/RepoRow.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/RepoRow.tsx @@ -7,7 +7,7 @@ import { VariantAnalysisRepoStatus, VariantAnalysisScannedRepositoryDownloadStatus, VariantAnalysisScannedRepositoryState, -} from "../../remote-queries/shared/variant-analysis"; +} from "../../variant-analysis/shared/variant-analysis"; import { formatDecimal } from "../../pure/number"; import { Codicon, @@ -16,11 +16,11 @@ import { SuccessIcon, WarningIcon, } from "../common"; -import { RepositoryWithMetadata } from "../../remote-queries/shared/repository"; +import { RepositoryWithMetadata } from "../../variant-analysis/shared/repository"; import { AnalysisAlert, AnalysisRawResults, -} from "../../remote-queries/shared/analysis-result"; +} from "../../variant-analysis/shared/analysis-result"; import { vscode } from "../vscode-api"; import { AnalyzedRepoItemContent } from "./AnalyzedRepoItemContent"; import StarCount from "../common/StarCount"; diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx index 55c564d10..cf4a0df25 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx @@ -6,7 +6,7 @@ import { VariantAnalysisScannedRepositoryResult, VariantAnalysisScannedRepositoryState, VariantAnalysisStatus, -} from "../../remote-queries/shared/variant-analysis"; +} from "../../variant-analysis/shared/variant-analysis"; import { VariantAnalysisHeader } from "./VariantAnalysisHeader"; import { VariantAnalysisOutcomePanels } from "./VariantAnalysisOutcomePanels"; import { VariantAnalysisLoading } from "./VariantAnalysisLoading"; diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisActions.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisActions.tsx index d6a2588a0..4df2a9157 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisActions.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisActions.tsx @@ -1,7 +1,7 @@ import * as React from "react"; import styled from "styled-components"; import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"; -import { VariantAnalysisStatus } from "../../remote-queries/shared/variant-analysis"; +import { VariantAnalysisStatus } from "../../variant-analysis/shared/variant-analysis"; export type VariantAnalysisActionsProps = { variantAnalysisStatus: VariantAnalysisStatus; diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisAnalyzedRepos.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisAnalyzedRepos.tsx index 0fa7ec783..2e787641d 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisAnalyzedRepos.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisAnalyzedRepos.tsx @@ -6,7 +6,7 @@ import { VariantAnalysis, VariantAnalysisScannedRepositoryResult, VariantAnalysisScannedRepositoryState, -} from "../../remote-queries/shared/variant-analysis"; +} from "../../variant-analysis/shared/variant-analysis"; import { filterAndSortRepositoriesWithResultsByName, RepositoriesFilterSortState, diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisHeader.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisHeader.tsx index ec3fd41b1..9661acc56 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisHeader.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisHeader.tsx @@ -8,7 +8,7 @@ import { VariantAnalysis, VariantAnalysisScannedRepositoryDownloadStatus, VariantAnalysisScannedRepositoryState, -} from "../../remote-queries/shared/variant-analysis"; +} from "../../variant-analysis/shared/variant-analysis"; import { QueryDetails } from "./QueryDetails"; import { VariantAnalysisActions } from "./VariantAnalysisActions"; import { VariantAnalysisStats } from "./VariantAnalysisStats"; diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisOutcomePanels.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisOutcomePanels.tsx index 8e36aee32..7d57e6cf4 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisOutcomePanels.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisOutcomePanels.tsx @@ -13,7 +13,7 @@ import { VariantAnalysisScannedRepositoryResult, VariantAnalysisScannedRepositoryState, VariantAnalysisStatus, -} from "../../remote-queries/shared/variant-analysis"; +} from "../../variant-analysis/shared/variant-analysis"; import { VariantAnalysisAnalyzedRepos } from "./VariantAnalysisAnalyzedRepos"; import { Alert } from "../common"; import { VariantAnalysisSkippedRepositoriesTab } from "./VariantAnalysisSkippedRepositoriesTab"; diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisRepositoriesStats.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisRepositoriesStats.tsx index cd91fcffe..bd6cbf488 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisRepositoriesStats.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisRepositoriesStats.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { VariantAnalysisStatus } from "../../remote-queries/shared/variant-analysis"; +import { VariantAnalysisStatus } from "../../variant-analysis/shared/variant-analysis"; import { formatDecimal } from "../../pure/number"; import { ErrorIcon, diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisSkippedRepositoriesTab.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisSkippedRepositoriesTab.tsx index 8aec2166a..84d19c4c6 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisSkippedRepositoriesTab.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisSkippedRepositoriesTab.tsx @@ -1,7 +1,7 @@ import * as React from "react"; import { useMemo } from "react"; import styled from "styled-components"; -import { VariantAnalysisSkippedRepositoryGroup } from "../../remote-queries/shared/variant-analysis"; +import { VariantAnalysisSkippedRepositoryGroup } from "../../variant-analysis/shared/variant-analysis"; import { Alert } from "../common"; import { RepoRow } from "./RepoRow"; import { diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStats.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStats.tsx index f0883de7d..25802d20e 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStats.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStats.tsx @@ -1,7 +1,7 @@ import * as React from "react"; import { useMemo } from "react"; import styled from "styled-components"; -import { VariantAnalysisStatus } from "../../remote-queries/shared/variant-analysis"; +import { VariantAnalysisStatus } from "../../variant-analysis/shared/variant-analysis"; import { StatItem } from "./StatItem"; import { formatDecimal } from "../../pure/number"; import { humanizeUnit } from "../../pure/time"; diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStatusStats.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStatusStats.tsx index 5444f54d1..cd0d0c429 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStatusStats.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStatusStats.tsx @@ -2,7 +2,7 @@ import * as React from "react"; import styled from "styled-components"; import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"; import { formatDate } from "../../pure/date"; -import { VariantAnalysisStatus } from "../../remote-queries/shared/variant-analysis"; +import { VariantAnalysisStatus } from "../../variant-analysis/shared/variant-analysis"; export type VariantAnalysisStatusStatsProps = { variantAnalysisStatus: VariantAnalysisStatus; diff --git a/extensions/ql-vscode/src/view/variant-analysis/__tests__/AnalyzedRepoItemContent.spec.tsx b/extensions/ql-vscode/src/view/variant-analysis/__tests__/AnalyzedRepoItemContent.spec.tsx index 67315381c..848f2eed2 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/__tests__/AnalyzedRepoItemContent.spec.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/__tests__/AnalyzedRepoItemContent.spec.tsx @@ -3,7 +3,7 @@ import { render as reactRender, screen } from "@testing-library/react"; import { VariantAnalysisRepoStatus, VariantAnalysisScannedRepositoryDownloadStatus, -} from "../../../remote-queries/shared/variant-analysis"; +} from "../../../variant-analysis/shared/variant-analysis"; import { AnalyzedRepoItemContent, AnalyzedRepoItemContentProps, diff --git a/extensions/ql-vscode/src/view/variant-analysis/__tests__/RepoRow.spec.tsx b/extensions/ql-vscode/src/view/variant-analysis/__tests__/RepoRow.spec.tsx index 570be30b7..8e740307d 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/__tests__/RepoRow.spec.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/__tests__/RepoRow.spec.tsx @@ -3,10 +3,10 @@ import { render as reactRender, screen, waitFor } from "@testing-library/react"; import { VariantAnalysisRepoStatus, VariantAnalysisScannedRepositoryDownloadStatus, -} from "../../../remote-queries/shared/variant-analysis"; +} from "../../../variant-analysis/shared/variant-analysis"; import userEvent from "@testing-library/user-event"; import { RepoRow, RepoRowProps } from "../RepoRow"; -import { createMockRepositoryWithMetadata } from "../../../../test/factories/remote-queries/shared/repository"; +import { createMockRepositoryWithMetadata } from "../../../../test/factories/variant-analysis/shared/repository"; describe(RepoRow.name, () => { const render = (props: Partial = {}) => { diff --git a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysis.spec.tsx b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysis.spec.tsx index 2f1fb3dd4..af20b11ca 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysis.spec.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysis.spec.tsx @@ -3,9 +3,9 @@ import { render as reactRender, screen } from "@testing-library/react"; import { VariantAnalysisFailureReason, VariantAnalysisStatus, -} from "../../../remote-queries/shared/variant-analysis"; +} from "../../../variant-analysis/shared/variant-analysis"; import { VariantAnalysis, VariantAnalysisProps } from "../VariantAnalysis"; -import { createMockVariantAnalysis } from "../../../../test/factories/remote-queries/shared/variant-analysis"; +import { createMockVariantAnalysis } from "../../../../test/factories/variant-analysis/shared/variant-analysis"; describe(VariantAnalysis.name, () => { const render = (props: Partial = {}) => diff --git a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisActions.spec.tsx b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisActions.spec.tsx index 20c291128..a655f7d1c 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisActions.spec.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisActions.spec.tsx @@ -1,7 +1,7 @@ import * as React from "react"; import { render as reactRender, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; -import { VariantAnalysisStatus } from "../../../remote-queries/shared/variant-analysis"; +import { VariantAnalysisStatus } from "../../../variant-analysis/shared/variant-analysis"; import { VariantAnalysisActions, VariantAnalysisActionsProps, diff --git a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisAnalyzedRepos.spec.tsx b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisAnalyzedRepos.spec.tsx index da2b9096c..a1cc2ad6b 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisAnalyzedRepos.spec.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisAnalyzedRepos.spec.tsx @@ -5,14 +5,14 @@ import { VariantAnalysisRepoStatus, VariantAnalysisScannedRepositoryDownloadStatus, VariantAnalysisStatus, -} from "../../../remote-queries/shared/variant-analysis"; +} from "../../../variant-analysis/shared/variant-analysis"; import { VariantAnalysisAnalyzedRepos, VariantAnalysisAnalyzedReposProps, } from "../VariantAnalysisAnalyzedRepos"; -import { createMockVariantAnalysis } from "../../../../test/factories/remote-queries/shared/variant-analysis"; -import { createMockRepositoryWithMetadata } from "../../../../test/factories/remote-queries/shared/repository"; -import { createMockScannedRepo } from "../../../../test/factories/remote-queries/shared/scanned-repositories"; +import { createMockVariantAnalysis } from "../../../../test/factories/variant-analysis/shared/variant-analysis"; +import { createMockRepositoryWithMetadata } from "../../../../test/factories/variant-analysis/shared/repository"; +import { createMockScannedRepo } from "../../../../test/factories/variant-analysis/shared/scanned-repositories"; import { defaultFilterSortState, SortKey, diff --git a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisOutcomePanels.spec.tsx b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisOutcomePanels.spec.tsx index d52777d36..39eb209f2 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisOutcomePanels.spec.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisOutcomePanels.spec.tsx @@ -5,17 +5,17 @@ import { VariantAnalysisFailureReason, VariantAnalysisRepoStatus, VariantAnalysisStatus, -} from "../../../remote-queries/shared/variant-analysis"; +} from "../../../variant-analysis/shared/variant-analysis"; import { VariantAnalysisOutcomePanelProps, VariantAnalysisOutcomePanels, } from "../VariantAnalysisOutcomePanels"; -import { createMockVariantAnalysis } from "../../../../test/factories/remote-queries/shared/variant-analysis"; -import { createMockRepositoryWithMetadata } from "../../../../test/factories/remote-queries/shared/repository"; +import { createMockVariantAnalysis } from "../../../../test/factories/variant-analysis/shared/variant-analysis"; +import { createMockRepositoryWithMetadata } from "../../../../test/factories/variant-analysis/shared/repository"; import { createMockScannedRepo, createMockScannedRepos, -} from "../../../../test/factories/remote-queries/shared/scanned-repositories"; +} from "../../../../test/factories/variant-analysis/shared/scanned-repositories"; import { defaultFilterSortState } from "../../../pure/variant-analysis-filter-sort"; describe(VariantAnalysisOutcomePanels.name, () => { diff --git a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStats.spec.tsx b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStats.spec.tsx index 77381cfe0..30cf29b98 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStats.spec.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStats.spec.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import { render as reactRender, screen } from "@testing-library/react"; -import { VariantAnalysisStatus } from "../../../remote-queries/shared/variant-analysis"; +import { VariantAnalysisStatus } from "../../../variant-analysis/shared/variant-analysis"; import { VariantAnalysisStats, VariantAnalysisStatsProps, diff --git a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStatusStats.spec.tsx b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStatusStats.spec.tsx index fd356bc5f..807179988 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStatusStats.spec.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStatusStats.spec.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import { render as reactRender, screen } from "@testing-library/react"; -import { VariantAnalysisStatus } from "../../../remote-queries/shared/variant-analysis"; +import { VariantAnalysisStatus } from "../../../variant-analysis/shared/variant-analysis"; import { VariantAnalysisStatusStats, VariantAnalysisStatusStatsProps, diff --git a/extensions/ql-vscode/src/view/vscode-api.ts b/extensions/ql-vscode/src/view/vscode-api.ts index 067a9185f..fa2eaa948 100644 --- a/extensions/ql-vscode/src/view/vscode-api.ts +++ b/extensions/ql-vscode/src/view/vscode-api.ts @@ -1,6 +1,5 @@ import { FromCompareViewMessage, - FromRemoteQueriesMessage, FromResultsViewMsg, FromVariantAnalysisMessage, VariantAnalysisState, @@ -14,7 +13,6 @@ export interface VsCodeApi { msg: | FromResultsViewMsg | FromCompareViewMessage - | FromRemoteQueriesMessage | FromVariantAnalysisMessage, ): void; diff --git a/extensions/ql-vscode/supported_cli_versions.json b/extensions/ql-vscode/supported_cli_versions.json index 45b233221..0e7b60aa4 100644 --- a/extensions/ql-vscode/supported_cli_versions.json +++ b/extensions/ql-vscode/supported_cli_versions.json @@ -1,5 +1,5 @@ [ - "v2.12.2", + "v2.12.3", "v2.11.6", "v2.7.6", "v2.8.5", diff --git a/extensions/ql-vscode/test/factories/query-history/query-history-dirs.ts b/extensions/ql-vscode/test/factories/query-history/query-history-dirs.ts new file mode 100644 index 000000000..02f9cdf79 --- /dev/null +++ b/extensions/ql-vscode/test/factories/query-history/query-history-dirs.ts @@ -0,0 +1,14 @@ +import { QueryHistoryDirs } from "../../../src/query-history/query-history-dirs"; + +export function createMockQueryHistoryDirs({ + localQueriesDirPath = "mock-local-queries-dir-path", + variantAnalysesDirPath = "mock-variant-analyses-dir-path", +}: { + localQueriesDirPath?: string; + variantAnalysesDirPath?: string; +} = {}): QueryHistoryDirs { + return { + localQueriesDirPath, + variantAnalysesDirPath, + }; +} diff --git a/extensions/ql-vscode/test/factories/query-history/variant-analysis-history-item.ts b/extensions/ql-vscode/test/factories/query-history/variant-analysis-history-item.ts index 35f110d6f..e395e09e2 100644 --- a/extensions/ql-vscode/test/factories/query-history/variant-analysis-history-item.ts +++ b/extensions/ql-vscode/test/factories/query-history/variant-analysis-history-item.ts @@ -1,15 +1,19 @@ import { VariantAnalysisHistoryItem } from "../../../src/query-history/variant-analysis-history-item"; import { QueryStatus } from "../../../src/query-status"; -import { VariantAnalysisStatus } from "../../../src/remote-queries/shared/variant-analysis"; -import { createMockVariantAnalysis } from "../remote-queries/shared/variant-analysis"; +import { + VariantAnalysis, + VariantAnalysisStatus, +} from "../../../src/variant-analysis/shared/variant-analysis"; +import { createMockVariantAnalysis } from "../variant-analysis/shared/variant-analysis"; export function createMockVariantAnalysisHistoryItem({ historyItemStatus = QueryStatus.InProgress, variantAnalysisStatus = VariantAnalysisStatus.Succeeded, failureReason = undefined, resultCount = 0, - userSpecifiedLabel = "query-name", + userSpecifiedLabel = undefined, executionStartTime = undefined, + variantAnalysis = undefined, }: { historyItemStatus?: QueryStatus; variantAnalysisStatus?: VariantAnalysisStatus; @@ -17,6 +21,7 @@ export function createMockVariantAnalysisHistoryItem({ resultCount?: number; userSpecifiedLabel?: string; executionStartTime?: number; + variantAnalysis?: VariantAnalysis; }): VariantAnalysisHistoryItem { return { t: "variant-analysis", @@ -24,10 +29,12 @@ export function createMockVariantAnalysisHistoryItem({ resultCount, status: historyItemStatus, completed: false, - variantAnalysis: createMockVariantAnalysis({ - status: variantAnalysisStatus, - executionStartTime, - }), + variantAnalysis: + variantAnalysis ?? + createMockVariantAnalysis({ + status: variantAnalysisStatus, + executionStartTime, + }), userSpecifiedLabel, }; } diff --git a/extensions/ql-vscode/test/factories/remote-queries/gh-api/repository.ts b/extensions/ql-vscode/test/factories/variant-analysis/gh-api/repository.ts similarity index 89% rename from extensions/ql-vscode/test/factories/remote-queries/gh-api/repository.ts rename to extensions/ql-vscode/test/factories/variant-analysis/gh-api/repository.ts index 07cbfae50..621ea0b47 100644 --- a/extensions/ql-vscode/test/factories/remote-queries/gh-api/repository.ts +++ b/extensions/ql-vscode/test/factories/variant-analysis/gh-api/repository.ts @@ -2,7 +2,7 @@ import { faker } from "@faker-js/faker"; import { Repository, RepositoryWithMetadata, -} from "../../../../src/remote-queries/gh-api/repository"; +} from "../../../../src/variant-analysis/gh-api/repository"; export function createMockRepository(name = faker.random.word()): Repository { return { diff --git a/extensions/ql-vscode/test/factories/remote-queries/gh-api/scanned-repositories.ts b/extensions/ql-vscode/test/factories/variant-analysis/gh-api/scanned-repositories.ts similarity index 93% rename from extensions/ql-vscode/test/factories/remote-queries/gh-api/scanned-repositories.ts rename to extensions/ql-vscode/test/factories/variant-analysis/gh-api/scanned-repositories.ts index 84e5819c5..cab58359a 100644 --- a/extensions/ql-vscode/test/factories/remote-queries/gh-api/scanned-repositories.ts +++ b/extensions/ql-vscode/test/factories/variant-analysis/gh-api/scanned-repositories.ts @@ -2,7 +2,7 @@ import { faker } from "@faker-js/faker"; import { VariantAnalysisRepoStatus, VariantAnalysisScannedRepository, -} from "../../../../src/remote-queries/gh-api/variant-analysis"; +} from "../../../../src/variant-analysis/gh-api/variant-analysis"; import { createMockRepositoryWithMetadata } from "./repository"; export function createMockScannedRepo( diff --git a/extensions/ql-vscode/test/factories/remote-queries/gh-api/skipped-repositories.ts b/extensions/ql-vscode/test/factories/variant-analysis/gh-api/skipped-repositories.ts similarity index 94% rename from extensions/ql-vscode/test/factories/remote-queries/gh-api/skipped-repositories.ts rename to extensions/ql-vscode/test/factories/variant-analysis/gh-api/skipped-repositories.ts index 4ec66e180..469fd0370 100644 --- a/extensions/ql-vscode/test/factories/remote-queries/gh-api/skipped-repositories.ts +++ b/extensions/ql-vscode/test/factories/variant-analysis/gh-api/skipped-repositories.ts @@ -3,7 +3,7 @@ import { VariantAnalysisNotFoundRepositoryGroup, VariantAnalysisSkippedRepositories, VariantAnalysisSkippedRepositoryGroup, -} from "../../../../src/remote-queries/gh-api/variant-analysis"; +} from "../../../../src/variant-analysis/gh-api/variant-analysis"; import { createMockRepositoryWithMetadata } from "./repository"; export function createMockSkippedRepos(): VariantAnalysisSkippedRepositories { diff --git a/extensions/ql-vscode/test/factories/remote-queries/gh-api/variant-analysis-api-response.ts b/extensions/ql-vscode/test/factories/variant-analysis/gh-api/variant-analysis-api-response.ts similarity index 87% rename from extensions/ql-vscode/test/factories/remote-queries/gh-api/variant-analysis-api-response.ts rename to extensions/ql-vscode/test/factories/variant-analysis/gh-api/variant-analysis-api-response.ts index 9ae585ea6..ae3d5bd05 100644 --- a/extensions/ql-vscode/test/factories/remote-queries/gh-api/variant-analysis-api-response.ts +++ b/extensions/ql-vscode/test/factories/variant-analysis/gh-api/variant-analysis-api-response.ts @@ -4,8 +4,8 @@ import { VariantAnalysisScannedRepository, VariantAnalysisSkippedRepositories, VariantAnalysisStatus, -} from "../../../../src/remote-queries/gh-api/variant-analysis"; -import { VariantAnalysisQueryLanguage } from "../../../../src/remote-queries/shared/variant-analysis"; +} from "../../../../src/variant-analysis/gh-api/variant-analysis"; +import { QueryLanguage } from "../../../../src/common/query-language"; import { createMockScannedRepos } from "./scanned-repositories"; import { createMockSkippedRepos } from "./skipped-repositories"; import { createMockRepository } from "./repository"; @@ -23,7 +23,7 @@ export function createMockApiResponse( full_name: "github/pickles", private: false, }, - query_language: VariantAnalysisQueryLanguage.Javascript, + query_language: QueryLanguage.Javascript, query_pack_url: "https://example.com/foo", created_at: faker.date.recent().toISOString(), updated_at: faker.date.recent().toISOString(), diff --git a/extensions/ql-vscode/test/factories/remote-queries/gh-api/variant-analysis-repo-task.ts b/extensions/ql-vscode/test/factories/variant-analysis/gh-api/variant-analysis-repo-task.ts similarity index 69% rename from extensions/ql-vscode/test/factories/remote-queries/gh-api/variant-analysis-repo-task.ts rename to extensions/ql-vscode/test/factories/variant-analysis/gh-api/variant-analysis-repo-task.ts index c06661941..27b578430 100644 --- a/extensions/ql-vscode/test/factories/remote-queries/gh-api/variant-analysis-repo-task.ts +++ b/extensions/ql-vscode/test/factories/variant-analysis/gh-api/variant-analysis-repo-task.ts @@ -1,6 +1,6 @@ import { faker } from "@faker-js/faker"; -import { VariantAnalysisRepoTask } from "../../../../src/remote-queries/gh-api/variant-analysis"; -import { VariantAnalysisRepoStatus } from "../../../../src/remote-queries/shared/variant-analysis"; +import { VariantAnalysisRepoTask } from "../../../../src/variant-analysis/gh-api/variant-analysis"; +import { VariantAnalysisRepoStatus } from "../../../../src/variant-analysis/shared/variant-analysis"; import { createMockRepository } from "./repository"; export function createMockVariantAnalysisRepoTask(): VariantAnalysisRepoTask { diff --git a/extensions/ql-vscode/test/factories/remote-queries/shared/AnalysisMessage.ts b/extensions/ql-vscode/test/factories/variant-analysis/shared/AnalysisMessage.ts similarity index 64% rename from extensions/ql-vscode/test/factories/remote-queries/shared/AnalysisMessage.ts rename to extensions/ql-vscode/test/factories/variant-analysis/shared/AnalysisMessage.ts index cdd462908..364398d28 100644 --- a/extensions/ql-vscode/test/factories/remote-queries/shared/AnalysisMessage.ts +++ b/extensions/ql-vscode/test/factories/variant-analysis/shared/AnalysisMessage.ts @@ -1,4 +1,4 @@ -import { AnalysisMessage } from "../../../../src/remote-queries/shared/analysis-result"; +import { AnalysisMessage } from "../../../../src/variant-analysis/shared/analysis-result"; export function createMockAnalysisMessage(): AnalysisMessage { return { diff --git a/extensions/ql-vscode/test/factories/remote-queries/shared/CodeFlow.ts b/extensions/ql-vscode/test/factories/variant-analysis/shared/CodeFlow.ts similarity index 85% rename from extensions/ql-vscode/test/factories/remote-queries/shared/CodeFlow.ts rename to extensions/ql-vscode/test/factories/variant-analysis/shared/CodeFlow.ts index eb1713d8e..16f728c13 100644 --- a/extensions/ql-vscode/test/factories/remote-queries/shared/CodeFlow.ts +++ b/extensions/ql-vscode/test/factories/variant-analysis/shared/CodeFlow.ts @@ -1,4 +1,4 @@ -import { CodeFlow } from "../../../../src/remote-queries/shared/analysis-result"; +import { CodeFlow } from "../../../../src/variant-analysis/shared/analysis-result"; import { createMockAnalysisMessage } from "./AnalysisMessage"; export function createMockCodeFlows(): CodeFlow[] { diff --git a/extensions/ql-vscode/test/factories/remote-queries/shared/repository.ts b/extensions/ql-vscode/test/factories/variant-analysis/shared/repository.ts similarity index 89% rename from extensions/ql-vscode/test/factories/remote-queries/shared/repository.ts rename to extensions/ql-vscode/test/factories/variant-analysis/shared/repository.ts index 7220c99e3..d5579aeb0 100644 --- a/extensions/ql-vscode/test/factories/remote-queries/shared/repository.ts +++ b/extensions/ql-vscode/test/factories/variant-analysis/shared/repository.ts @@ -2,7 +2,7 @@ import { faker } from "@faker-js/faker"; import { Repository, RepositoryWithMetadata, -} from "../../../../src/remote-queries/shared/repository"; +} from "../../../../src/variant-analysis/shared/repository"; export function createMockRepository(): Repository { return { diff --git a/extensions/ql-vscode/test/factories/remote-queries/shared/scanned-repositories.ts b/extensions/ql-vscode/test/factories/variant-analysis/shared/scanned-repositories.ts similarity index 93% rename from extensions/ql-vscode/test/factories/remote-queries/shared/scanned-repositories.ts rename to extensions/ql-vscode/test/factories/variant-analysis/shared/scanned-repositories.ts index f78506c5d..e043f5652 100644 --- a/extensions/ql-vscode/test/factories/remote-queries/shared/scanned-repositories.ts +++ b/extensions/ql-vscode/test/factories/variant-analysis/shared/scanned-repositories.ts @@ -2,7 +2,7 @@ import { faker } from "@faker-js/faker"; import { VariantAnalysisRepoStatus, VariantAnalysisScannedRepository, -} from "../../../../src/remote-queries/shared/variant-analysis"; +} from "../../../../src/variant-analysis/shared/variant-analysis"; import { createMockRepositoryWithMetadata } from "./repository"; export function createMockScannedRepo( diff --git a/extensions/ql-vscode/test/factories/remote-queries/shared/skipped-repositories.ts b/extensions/ql-vscode/test/factories/variant-analysis/shared/skipped-repositories.ts similarity index 93% rename from extensions/ql-vscode/test/factories/remote-queries/shared/skipped-repositories.ts rename to extensions/ql-vscode/test/factories/variant-analysis/shared/skipped-repositories.ts index 090158f44..01d7e080f 100644 --- a/extensions/ql-vscode/test/factories/remote-queries/shared/skipped-repositories.ts +++ b/extensions/ql-vscode/test/factories/variant-analysis/shared/skipped-repositories.ts @@ -2,7 +2,7 @@ import { faker } from "@faker-js/faker"; import { VariantAnalysisSkippedRepositories, VariantAnalysisSkippedRepositoryGroup, -} from "../../../../src/remote-queries/shared/variant-analysis"; +} from "../../../../src/variant-analysis/shared/variant-analysis"; import { createMockRepositoryWithMetadata } from "./repository"; export function createMockSkippedRepos(): VariantAnalysisSkippedRepositories { diff --git a/extensions/ql-vscode/test/factories/remote-queries/shared/variant-analysis-repo-tasks.ts b/extensions/ql-vscode/test/factories/variant-analysis/shared/variant-analysis-repo-tasks.ts similarity index 91% rename from extensions/ql-vscode/test/factories/remote-queries/shared/variant-analysis-repo-tasks.ts rename to extensions/ql-vscode/test/factories/variant-analysis/shared/variant-analysis-repo-tasks.ts index dc610313c..1ca1ac70c 100644 --- a/extensions/ql-vscode/test/factories/remote-queries/shared/variant-analysis-repo-tasks.ts +++ b/extensions/ql-vscode/test/factories/variant-analysis/shared/variant-analysis-repo-tasks.ts @@ -2,7 +2,7 @@ import { faker } from "@faker-js/faker"; import { VariantAnalysisRepositoryTask, VariantAnalysisRepoStatus, -} from "../../../../src/remote-queries/shared/variant-analysis"; +} from "../../../../src/variant-analysis/shared/variant-analysis"; import { createMockRepositoryWithMetadata } from "./repository"; export function createMockVariantAnalysisRepositoryTask( diff --git a/extensions/ql-vscode/test/factories/remote-queries/shared/variant-analysis-submission.ts b/extensions/ql-vscode/test/factories/variant-analysis/shared/variant-analysis-submission.ts similarity index 67% rename from extensions/ql-vscode/test/factories/remote-queries/shared/variant-analysis-submission.ts rename to extensions/ql-vscode/test/factories/variant-analysis/shared/variant-analysis-submission.ts index fd12aa271..3269cd1a0 100644 --- a/extensions/ql-vscode/test/factories/remote-queries/shared/variant-analysis-submission.ts +++ b/extensions/ql-vscode/test/factories/variant-analysis/shared/variant-analysis-submission.ts @@ -1,8 +1,6 @@ import { faker } from "@faker-js/faker"; -import { - VariantAnalysisQueryLanguage, - VariantAnalysisSubmission, -} from "../../../../src/remote-queries/shared/variant-analysis"; +import { VariantAnalysisSubmission } from "../../../../src/variant-analysis/shared/variant-analysis"; +import { QueryLanguage } from "../../../../src/common/query-language"; export function createMockSubmission(): VariantAnalysisSubmission { return { @@ -12,7 +10,7 @@ export function createMockSubmission(): VariantAnalysisSubmission { query: { name: "query-name", filePath: "query-file-path", - language: VariantAnalysisQueryLanguage.Javascript, + language: QueryLanguage.Javascript, text: "query-text", pack: "base64-encoded-string", }, diff --git a/extensions/ql-vscode/test/factories/remote-queries/shared/variant-analysis.ts b/extensions/ql-vscode/test/factories/variant-analysis/shared/variant-analysis.ts similarity index 88% rename from extensions/ql-vscode/test/factories/remote-queries/shared/variant-analysis.ts rename to extensions/ql-vscode/test/factories/variant-analysis/shared/variant-analysis.ts index d2fed5dda..ba9a455f5 100644 --- a/extensions/ql-vscode/test/factories/remote-queries/shared/variant-analysis.ts +++ b/extensions/ql-vscode/test/factories/variant-analysis/shared/variant-analysis.ts @@ -1,14 +1,14 @@ import { faker } from "@faker-js/faker"; import { VariantAnalysis, - VariantAnalysisQueryLanguage, VariantAnalysisScannedRepository, VariantAnalysisSkippedRepositories, VariantAnalysisStatus, -} from "../../../../src/remote-queries/shared/variant-analysis"; +} from "../../../../src/variant-analysis/shared/variant-analysis"; import { createMockScannedRepos } from "./scanned-repositories"; import { createMockSkippedRepos } from "./skipped-repositories"; import { createMockRepository } from "./repository"; +import { QueryLanguage } from "../../../../src/common/query-language"; export function createMockVariantAnalysis({ status = VariantAnalysisStatus.InProgress, @@ -32,7 +32,7 @@ export function createMockVariantAnalysis({ query: { name: "a-query-name", filePath: "a-query-file-path", - language: VariantAnalysisQueryLanguage.Javascript, + language: QueryLanguage.Javascript, text: "a-query-text", }, databases: { diff --git a/extensions/ql-vscode/test/unit-tests/data/markdown-generation/interpreted-results/path-problem/analyses-results.json b/extensions/ql-vscode/test/unit-tests/data/markdown-generation/interpreted-results/path-problem/analyses-results.json new file mode 100644 index 000000000..d5a339a58 --- /dev/null +++ b/extensions/ql-vscode/test/unit-tests/data/markdown-generation/interpreted-results/path-problem/analyses-results.json @@ -0,0 +1,756 @@ +[ + { + "repository": { + "id": 143040428, + "fullName": "github/codeql", + "private": false, + "stargazersCount": 5703, + "updatedAt": "2023-02-15T10:11:45Z" + }, + "analysisStatus": "succeeded", + "resultCount": 4, + "artifactSizeInBytes": 3785, + "interpretedResults": [ + { + "message": { + "tokens": [ + { + "t": "text", + "text": "This shell command depends on an uncontrolled " + }, + { + "t": "location", + "text": "absolute path", + "location": { + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b", + "filePath": "javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js" + }, + "highlightedRegion": { + "startLine": 4, + "startColumn": 35, + "endLine": 4, + "endColumn": 44 + } + } + }, + { "t": "text", "text": "." } + ] + }, + "shortDescription": "This shell command depends on an uncontrolled ,absolute path,.", + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b", + "filePath": "javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js" + }, + "severity": "Warning", + "codeSnippet": { + "startLine": 3, + "endLine": 6, + "text": "function cleanupTemp() {\n let cmd = \"rm -rf \" + path.join(__dirname, \"temp\");\n cp.execSync(cmd); // BAD\n}\n" + }, + "highlightedRegion": { + "startLine": 5, + "startColumn": 15, + "endLine": 5, + "endColumn": 18 + }, + "codeFlows": [ + { + "threadFlows": [ + { + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b", + "filePath": "javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js" + }, + "codeSnippet": { + "startLine": 2, + "endLine": 6, + "text": " path = require(\"path\");\nfunction cleanupTemp() {\n let cmd = \"rm -rf \" + path.join(__dirname, \"temp\");\n cp.execSync(cmd); // BAD\n}\n" + }, + "highlightedRegion": { + "startLine": 4, + "startColumn": 35, + "endLine": 4, + "endColumn": 44 + } + }, + { + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b", + "filePath": "javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js" + }, + "codeSnippet": { + "startLine": 2, + "endLine": 6, + "text": " path = require(\"path\");\nfunction cleanupTemp() {\n let cmd = \"rm -rf \" + path.join(__dirname, \"temp\");\n cp.execSync(cmd); // BAD\n}\n" + }, + "highlightedRegion": { + "startLine": 4, + "startColumn": 25, + "endLine": 4, + "endColumn": 53 + } + }, + { + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b", + "filePath": "javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js" + }, + "codeSnippet": { + "startLine": 2, + "endLine": 6, + "text": " path = require(\"path\");\nfunction cleanupTemp() {\n let cmd = \"rm -rf \" + path.join(__dirname, \"temp\");\n cp.execSync(cmd); // BAD\n}\n" + }, + "highlightedRegion": { + "startLine": 4, + "startColumn": 13, + "endLine": 4, + "endColumn": 53 + } + }, + { + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b", + "filePath": "javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js" + }, + "codeSnippet": { + "startLine": 2, + "endLine": 6, + "text": " path = require(\"path\");\nfunction cleanupTemp() {\n let cmd = \"rm -rf \" + path.join(__dirname, \"temp\");\n cp.execSync(cmd); // BAD\n}\n" + }, + "highlightedRegion": { + "startLine": 4, + "startColumn": 7, + "endLine": 4, + "endColumn": 53 + } + }, + { + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b", + "filePath": "javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js" + }, + "codeSnippet": { + "startLine": 3, + "endLine": 6, + "text": "function cleanupTemp() {\n let cmd = \"rm -rf \" + path.join(__dirname, \"temp\");\n cp.execSync(cmd); // BAD\n}\n" + }, + "highlightedRegion": { + "startLine": 5, + "startColumn": 15, + "endLine": 5, + "endColumn": 18 + } + } + ] + } + ] + }, + { + "message": { + "tokens": [ + { + "t": "text", + "text": "This shell command depends on an uncontrolled " + }, + { + "t": "location", + "text": "absolute path", + "location": { + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b", + "filePath": "javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js" + }, + "highlightedRegion": { + "startLine": 6, + "startColumn": 36, + "endLine": 6, + "endColumn": 45 + } + } + }, + { "t": "text", "text": "." } + ] + }, + "shortDescription": "This shell command depends on an uncontrolled ,absolute path,.", + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b", + "filePath": "javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js" + }, + "severity": "Warning", + "codeSnippet": { + "startLine": 4, + "endLine": 8, + "text": "(function() {\n\tcp.execFileSync('rm', ['-rf', path.join(__dirname, \"temp\")]); // GOOD\n\tcp.execSync('rm -rf ' + path.join(__dirname, \"temp\")); // BAD\n\n\texeca.shell('rm -rf ' + path.join(__dirname, \"temp\")); // NOT OK\n" + }, + "highlightedRegion": { + "startLine": 6, + "startColumn": 14, + "endLine": 6, + "endColumn": 54 + }, + "codeFlows": [ + { + "threadFlows": [ + { + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b", + "filePath": "javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js" + }, + "codeSnippet": { + "startLine": 4, + "endLine": 8, + "text": "(function() {\n\tcp.execFileSync('rm', ['-rf', path.join(__dirname, \"temp\")]); // GOOD\n\tcp.execSync('rm -rf ' + path.join(__dirname, \"temp\")); // BAD\n\n\texeca.shell('rm -rf ' + path.join(__dirname, \"temp\")); // NOT OK\n" + }, + "highlightedRegion": { + "startLine": 6, + "startColumn": 36, + "endLine": 6, + "endColumn": 45 + } + }, + { + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b", + "filePath": "javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js" + }, + "codeSnippet": { + "startLine": 4, + "endLine": 8, + "text": "(function() {\n\tcp.execFileSync('rm', ['-rf', path.join(__dirname, \"temp\")]); // GOOD\n\tcp.execSync('rm -rf ' + path.join(__dirname, \"temp\")); // BAD\n\n\texeca.shell('rm -rf ' + path.join(__dirname, \"temp\")); // NOT OK\n" + }, + "highlightedRegion": { + "startLine": 6, + "startColumn": 26, + "endLine": 6, + "endColumn": 54 + } + }, + { + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b", + "filePath": "javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js" + }, + "codeSnippet": { + "startLine": 4, + "endLine": 8, + "text": "(function() {\n\tcp.execFileSync('rm', ['-rf', path.join(__dirname, \"temp\")]); // GOOD\n\tcp.execSync('rm -rf ' + path.join(__dirname, \"temp\")); // BAD\n\n\texeca.shell('rm -rf ' + path.join(__dirname, \"temp\")); // NOT OK\n" + }, + "highlightedRegion": { + "startLine": 6, + "startColumn": 14, + "endLine": 6, + "endColumn": 54 + } + } + ] + } + ] + }, + { + "message": { + "tokens": [ + { + "t": "text", + "text": "This shell command depends on an uncontrolled " + }, + { + "t": "location", + "text": "absolute path", + "location": { + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b", + "filePath": "javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js" + }, + "highlightedRegion": { + "startLine": 8, + "startColumn": 36, + "endLine": 8, + "endColumn": 45 + } + } + }, + { "t": "text", "text": "." } + ] + }, + "shortDescription": "This shell command depends on an uncontrolled ,absolute path,.", + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b", + "filePath": "javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js" + }, + "severity": "Warning", + "codeSnippet": { + "startLine": 6, + "endLine": 10, + "text": "\tcp.execSync('rm -rf ' + path.join(__dirname, \"temp\")); // BAD\n\n\texeca.shell('rm -rf ' + path.join(__dirname, \"temp\")); // NOT OK\n\texeca.shellSync('rm -rf ' + path.join(__dirname, \"temp\")); // NOT OK\n\n" + }, + "highlightedRegion": { + "startLine": 8, + "startColumn": 14, + "endLine": 8, + "endColumn": 54 + }, + "codeFlows": [ + { + "threadFlows": [ + { + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b", + "filePath": "javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js" + }, + "codeSnippet": { + "startLine": 6, + "endLine": 10, + "text": "\tcp.execSync('rm -rf ' + path.join(__dirname, \"temp\")); // BAD\n\n\texeca.shell('rm -rf ' + path.join(__dirname, \"temp\")); // NOT OK\n\texeca.shellSync('rm -rf ' + path.join(__dirname, \"temp\")); // NOT OK\n\n" + }, + "highlightedRegion": { + "startLine": 8, + "startColumn": 36, + "endLine": 8, + "endColumn": 45 + } + }, + { + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b", + "filePath": "javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js" + }, + "codeSnippet": { + "startLine": 6, + "endLine": 10, + "text": "\tcp.execSync('rm -rf ' + path.join(__dirname, \"temp\")); // BAD\n\n\texeca.shell('rm -rf ' + path.join(__dirname, \"temp\")); // NOT OK\n\texeca.shellSync('rm -rf ' + path.join(__dirname, \"temp\")); // NOT OK\n\n" + }, + "highlightedRegion": { + "startLine": 8, + "startColumn": 26, + "endLine": 8, + "endColumn": 54 + } + }, + { + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b", + "filePath": "javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js" + }, + "codeSnippet": { + "startLine": 6, + "endLine": 10, + "text": "\tcp.execSync('rm -rf ' + path.join(__dirname, \"temp\")); // BAD\n\n\texeca.shell('rm -rf ' + path.join(__dirname, \"temp\")); // NOT OK\n\texeca.shellSync('rm -rf ' + path.join(__dirname, \"temp\")); // NOT OK\n\n" + }, + "highlightedRegion": { + "startLine": 8, + "startColumn": 14, + "endLine": 8, + "endColumn": 54 + } + } + ] + } + ] + }, + { + "message": { + "tokens": [ + { + "t": "text", + "text": "This shell command depends on an uncontrolled " + }, + { + "t": "location", + "text": "absolute path", + "location": { + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b", + "filePath": "javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js" + }, + "highlightedRegion": { + "startLine": 9, + "startColumn": 40, + "endLine": 9, + "endColumn": 49 + } + } + }, + { "t": "text", "text": "." } + ] + }, + "shortDescription": "This shell command depends on an uncontrolled ,absolute path,.", + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b", + "filePath": "javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js" + }, + "severity": "Warning", + "codeSnippet": { + "startLine": 7, + "endLine": 11, + "text": "\n\texeca.shell('rm -rf ' + path.join(__dirname, \"temp\")); // NOT OK\n\texeca.shellSync('rm -rf ' + path.join(__dirname, \"temp\")); // NOT OK\n\n\tconst safe = \"\\\"\" + path.join(__dirname, \"temp\") + \"\\\"\";\n" + }, + "highlightedRegion": { + "startLine": 9, + "startColumn": 18, + "endLine": 9, + "endColumn": 58 + }, + "codeFlows": [ + { + "threadFlows": [ + { + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b", + "filePath": "javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js" + }, + "codeSnippet": { + "startLine": 7, + "endLine": 11, + "text": "\n\texeca.shell('rm -rf ' + path.join(__dirname, \"temp\")); // NOT OK\n\texeca.shellSync('rm -rf ' + path.join(__dirname, \"temp\")); // NOT OK\n\n\tconst safe = \"\\\"\" + path.join(__dirname, \"temp\") + \"\\\"\";\n" + }, + "highlightedRegion": { + "startLine": 9, + "startColumn": 40, + "endLine": 9, + "endColumn": 49 + } + }, + { + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b", + "filePath": "javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js" + }, + "codeSnippet": { + "startLine": 7, + "endLine": 11, + "text": "\n\texeca.shell('rm -rf ' + path.join(__dirname, \"temp\")); // NOT OK\n\texeca.shellSync('rm -rf ' + path.join(__dirname, \"temp\")); // NOT OK\n\n\tconst safe = \"\\\"\" + path.join(__dirname, \"temp\") + \"\\\"\";\n" + }, + "highlightedRegion": { + "startLine": 9, + "startColumn": 30, + "endLine": 9, + "endColumn": 58 + } + }, + { + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b", + "filePath": "javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js" + }, + "codeSnippet": { + "startLine": 7, + "endLine": 11, + "text": "\n\texeca.shell('rm -rf ' + path.join(__dirname, \"temp\")); // NOT OK\n\texeca.shellSync('rm -rf ' + path.join(__dirname, \"temp\")); // NOT OK\n\n\tconst safe = \"\\\"\" + path.join(__dirname, \"temp\") + \"\\\"\";\n" + }, + "highlightedRegion": { + "startLine": 9, + "startColumn": 18, + "endLine": 9, + "endColumn": 58 + } + } + ] + } + ] + } + ] + }, + { + "repository": { + "id": 23578923, + "fullName": "test/no-results", + "private": false, + "stargazersCount": 7289, + "updatedAt": "2023-01-01T00:00:00Z" + }, + "analysisStatus": "succeeded", + "resultCount": 0, + "artifactSizeInBytes": 100, + "interpretedResults": [] + }, + { + "repository": { + "id": 3214406, + "fullName": "meteor/meteor", + "private": false, + "stargazersCount": 43274, + "updatedAt": "2023-02-14T21:06:55Z" + }, + "analysisStatus": "succeeded", + "resultCount": 1, + "artifactSizeInBytes": 2378, + "interpretedResults": [ + { + "message": { + "tokens": [ + { + "t": "text", + "text": "This shell command depends on an uncontrolled " + }, + { + "t": "location", + "text": "absolute path", + "location": { + "fileLink": { + "fileLinkPrefix": "https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec", + "filePath": "npm-packages/meteor-installer/config.js" + }, + "highlightedRegion": { + "startLine": 39, + "startColumn": 20, + "endLine": 39, + "endColumn": 61 + } + } + }, + { "t": "text", "text": "." } + ] + }, + "shortDescription": "This shell command depends on an uncontrolled ,absolute path,.", + "fileLink": { + "fileLinkPrefix": "https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec", + "filePath": "npm-packages/meteor-installer/install.js" + }, + "severity": "Warning", + "codeSnippet": { + "startLine": 257, + "endLine": 261, + "text": " if (isWindows()) {\n //set for the current session and beyond\n child_process.execSync(`setx path \"${meteorPath}/;%path%`);\n return;\n }\n" + }, + "highlightedRegion": { + "startLine": 259, + "startColumn": 28, + "endLine": 259, + "endColumn": 62 + }, + "codeFlows": [ + { + "threadFlows": [ + { + "fileLink": { + "fileLinkPrefix": "https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec", + "filePath": "npm-packages/meteor-installer/config.js" + }, + "codeSnippet": { + "startLine": 37, + "endLine": 41, + "text": "\nconst meteorLocalFolder = '.meteor';\nconst meteorPath = path.resolve(rootPath, meteorLocalFolder);\n\nmodule.exports = {\n" + }, + "highlightedRegion": { + "startLine": 39, + "startColumn": 20, + "endLine": 39, + "endColumn": 61 + } + }, + { + "fileLink": { + "fileLinkPrefix": "https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec", + "filePath": "npm-packages/meteor-installer/config.js" + }, + "codeSnippet": { + "startLine": 37, + "endLine": 41, + "text": "\nconst meteorLocalFolder = '.meteor';\nconst meteorPath = path.resolve(rootPath, meteorLocalFolder);\n\nmodule.exports = {\n" + }, + "highlightedRegion": { + "startLine": 39, + "startColumn": 7, + "endLine": 39, + "endColumn": 61 + } + }, + { + "fileLink": { + "fileLinkPrefix": "https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec", + "filePath": "npm-packages/meteor-installer/config.js" + }, + "codeSnippet": { + "startLine": 42, + "endLine": 46, + "text": " METEOR_LATEST_VERSION,\n extractPath: rootPath,\n meteorPath,\n release: process.env.INSTALL_METEOR_VERSION || METEOR_LATEST_VERSION,\n rootPath,\n" + }, + "highlightedRegion": { + "startLine": 44, + "startColumn": 3, + "endLine": 44, + "endColumn": 13 + } + }, + { + "fileLink": { + "fileLinkPrefix": "https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec", + "filePath": "npm-packages/meteor-installer/install.js" + }, + "codeSnippet": { + "startLine": 10, + "endLine": 14, + "text": "const os = require('os');\nconst {\n meteorPath,\n release,\n startedPath,\n" + }, + "highlightedRegion": { + "startLine": 12, + "startColumn": 3, + "endLine": 12, + "endColumn": 13 + } + }, + { + "fileLink": { + "fileLinkPrefix": "https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec", + "filePath": "npm-packages/meteor-installer/install.js" + }, + "codeSnippet": { + "startLine": 9, + "endLine": 25, + "text": "const tmp = require('tmp');\nconst os = require('os');\nconst {\n meteorPath,\n release,\n startedPath,\n extractPath,\n isWindows,\n rootPath,\n sudoUser,\n isSudo,\n isMac,\n METEOR_LATEST_VERSION,\n shouldSetupExecPath,\n} = require('./config.js');\nconst { uninstall } = require('./uninstall');\nconst {\n" + }, + "highlightedRegion": { + "startLine": 11, + "startColumn": 7, + "endLine": 23, + "endColumn": 27 + } + }, + { + "fileLink": { + "fileLinkPrefix": "https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec", + "filePath": "npm-packages/meteor-installer/install.js" + }, + "codeSnippet": { + "startLine": 257, + "endLine": 261, + "text": " if (isWindows()) {\n //set for the current session and beyond\n child_process.execSync(`setx path \"${meteorPath}/;%path%`);\n return;\n }\n" + }, + "highlightedRegion": { + "startLine": 259, + "startColumn": 42, + "endLine": 259, + "endColumn": 52 + } + }, + { + "fileLink": { + "fileLinkPrefix": "https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec", + "filePath": "npm-packages/meteor-installer/install.js" + }, + "codeSnippet": { + "startLine": 257, + "endLine": 261, + "text": " if (isWindows()) {\n //set for the current session and beyond\n child_process.execSync(`setx path \"${meteorPath}/;%path%`);\n return;\n }\n" + }, + "highlightedRegion": { + "startLine": 259, + "startColumn": 42, + "endLine": 259, + "endColumn": 52 + } + }, + { + "fileLink": { + "fileLinkPrefix": "https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec", + "filePath": "npm-packages/meteor-installer/install.js" + }, + "codeSnippet": { + "startLine": 257, + "endLine": 261, + "text": " if (isWindows()) {\n //set for the current session and beyond\n child_process.execSync(`setx path \"${meteorPath}/;%path%`);\n return;\n }\n" + }, + "highlightedRegion": { + "startLine": 259, + "startColumn": 42, + "endLine": 259, + "endColumn": 52 + } + }, + { + "fileLink": { + "fileLinkPrefix": "https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec", + "filePath": "npm-packages/meteor-installer/install.js" + }, + "codeSnippet": { + "startLine": 257, + "endLine": 261, + "text": " if (isWindows()) {\n //set for the current session and beyond\n child_process.execSync(`setx path \"${meteorPath}/;%path%`);\n return;\n }\n" + }, + "highlightedRegion": { + "startLine": 259, + "startColumn": 42, + "endLine": 259, + "endColumn": 52 + } + }, + { + "fileLink": { + "fileLinkPrefix": "https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec", + "filePath": "npm-packages/meteor-installer/install.js" + }, + "codeSnippet": { + "startLine": 257, + "endLine": 261, + "text": " if (isWindows()) {\n //set for the current session and beyond\n child_process.execSync(`setx path \"${meteorPath}/;%path%`);\n return;\n }\n" + }, + "highlightedRegion": { + "startLine": 259, + "startColumn": 42, + "endLine": 259, + "endColumn": 52 + } + }, + { + "fileLink": { + "fileLinkPrefix": "https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec", + "filePath": "npm-packages/meteor-installer/install.js" + }, + "codeSnippet": { + "startLine": 257, + "endLine": 261, + "text": " if (isWindows()) {\n //set for the current session and beyond\n child_process.execSync(`setx path \"${meteorPath}/;%path%`);\n return;\n }\n" + }, + "highlightedRegion": { + "startLine": 259, + "startColumn": 28, + "endLine": 259, + "endColumn": 62 + } + } + ] + }, + { + "threadFlows": [ + { + "fileLink": { + "fileLinkPrefix": "https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec", + "filePath": "npm-packages/meteor-installer/config.js" + }, + "codeSnippet": { + "startLine": 37, + "endLine": 41, + "text": "\nconst meteorLocalFolder = '.meteor';\nconst meteorPath = path.resolve(rootPath, meteorLocalFolder);\n\nmodule.exports = {\n" + }, + "highlightedRegion": { + "startLine": 39, + "startColumn": 20, + "endLine": 39, + "endColumn": 61 + } + }, + { + "fileLink": { + "fileLinkPrefix": "https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec", + "filePath": "npm-packages/meteor-installer/install.js" + }, + "codeSnippet": { + "startLine": 257, + "endLine": 261, + "text": " if (isWindows()) {\n //set for the current session and beyond\n child_process.execSync(`setx path \"${meteorPath}/;%path%`);\n return;\n }\n" + }, + "highlightedRegion": { + "startLine": 259, + "startColumn": 28, + "endLine": 259, + "endColumn": 62 + } + } + ] + } + ] + } + ] + } +] diff --git a/extensions/ql-vscode/test/unit-tests/data/markdown-generation/interpreted-results/path-problem/expected/_summary.md b/extensions/ql-vscode/test/unit-tests/data/markdown-generation/interpreted-results/path-problem/expected/_summary.md new file mode 100644 index 000000000..ad1eca260 --- /dev/null +++ b/extensions/ql-vscode/test/unit-tests/data/markdown-generation/interpreted-results/path-problem/expected/_summary.md @@ -0,0 +1,49 @@ +### Results for "Shell command built from environment values" + +
+Query + +```ql +/** + * @name Shell command built from environment values + * @description Building a shell command string with values from the enclosing + * environment may cause subtle bugs or vulnerabilities. + * @kind path-problem + * @problem.severity warning + * @security-severity 6.3 + * @precision high + * @id js/shell-command-injection-from-environment + * @tags correctness + * security + * external/cwe/cwe-078 + * external/cwe/cwe-088 + */ + +import javascript +import DataFlow::PathGraph +import semmle.javascript.security.dataflow.ShellCommandInjectionFromEnvironmentQuery + +from + Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Node highlight, + Source sourceNode +where + sourceNode = source.getNode() and + cfg.hasFlowPath(source, sink) and + if cfg.isSinkWithHighlight(sink.getNode(), _) + then cfg.isSinkWithHighlight(sink.getNode(), highlight) + else highlight = sink.getNode() +select highlight, source, sink, "This shell command depends on an uncontrolled $@.", sourceNode, + sourceNode.getSourceType() + +``` + +
+ +
+ +### Summary + +| Repository | Results | +| --- | --- | +| github/codeql | [4 result(s)](#file-result-1-github-codeql-md) | +| meteor/meteor | [1 result(s)](#file-result-2-meteor-meteor-md) | diff --git a/extensions/ql-vscode/test/unit-tests/data/markdown-generation/interpreted-results/path-problem/expected/result-1-github-codeql.md b/extensions/ql-vscode/test/unit-tests/data/markdown-generation/interpreted-results/path-problem/expected/result-1-github-codeql.md new file mode 100644 index 000000000..cd8eeac28 --- /dev/null +++ b/extensions/ql-vscode/test/unit-tests/data/markdown-generation/interpreted-results/path-problem/expected/result-1-github-codeql.md @@ -0,0 +1,195 @@ +### github/codeql + +[javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js#L5-L5) + +
function cleanupTemp() {
+  let cmd = "rm -rf " + path.join(__dirname, "temp");
+  cp.execSync(cmd); // BAD
+}
+
+ +*This shell command depends on an uncontrolled [absolute path](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js#L4-L4).* + +#### Paths + +
+Path with 5 steps + +1. [javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js#L4-L4) +
  path = require("path");
+   function cleanupTemp() {
+     let cmd = "rm -rf " + path.join(__dirname, "temp");
+     cp.execSync(cmd); // BAD
+   }
+   
+ +2. [javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js#L4-L4) +
  path = require("path");
+   function cleanupTemp() {
+     let cmd = "rm -rf " + path.join(__dirname, "temp");
+     cp.execSync(cmd); // BAD
+   }
+   
+ +3. [javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js#L4-L4) +
  path = require("path");
+   function cleanupTemp() {
+     let cmd = "rm -rf " + path.join(__dirname, "temp");
+     cp.execSync(cmd); // BAD
+   }
+   
+ +4. [javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js#L4-L4) +
  path = require("path");
+   function cleanupTemp() {
+     let cmd = "rm -rf " + path.join(__dirname, "temp");
+     cp.execSync(cmd); // BAD
+   }
+   
+ +5. [javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js#L5-L5) +
function cleanupTemp() {
+     let cmd = "rm -rf " + path.join(__dirname, "temp");
+     cp.execSync(cmd); // BAD
+   }
+   
+ + +
+ +---------------------------------------- + +[javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L6-L6) + +
(function() {
+	cp.execFileSync('rm',  ['-rf', path.join(__dirname, "temp")]); // GOOD
+	cp.execSync('rm -rf ' + path.join(__dirname, "temp")); // BAD
+
+	execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
+
+ +*This shell command depends on an uncontrolled [absolute path](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L6-L6).* + +#### Paths + +
+Path with 3 steps + +1. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L6-L6) +
(function() {
+   	cp.execFileSync('rm',  ['-rf', path.join(__dirname, "temp")]); // GOOD
+   	cp.execSync('rm -rf ' + path.join(__dirname, "temp")); // BAD
+   
+   	execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
+   
+ +2. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L6-L6) +
(function() {
+   	cp.execFileSync('rm',  ['-rf', path.join(__dirname, "temp")]); // GOOD
+   	cp.execSync('rm -rf ' + path.join(__dirname, "temp")); // BAD
+   
+   	execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
+   
+ +3. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L6-L6) +
(function() {
+   	cp.execFileSync('rm',  ['-rf', path.join(__dirname, "temp")]); // GOOD
+   	cp.execSync('rm -rf ' + path.join(__dirname, "temp")); // BAD
+   
+   	execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
+   
+ + +
+ +---------------------------------------- + +[javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L8-L8) + +
	cp.execSync('rm -rf ' + path.join(__dirname, "temp")); // BAD
+
+	execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
+	execa.shellSync('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
+
+
+ +*This shell command depends on an uncontrolled [absolute path](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L8-L8).* + +#### Paths + +
+Path with 3 steps + +1. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L8-L8) +
	cp.execSync('rm -rf ' + path.join(__dirname, "temp")); // BAD
+   
+   	execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
+   	execa.shellSync('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
+   
+   
+ +2. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L8-L8) +
	cp.execSync('rm -rf ' + path.join(__dirname, "temp")); // BAD
+   
+   	execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
+   	execa.shellSync('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
+   
+   
+ +3. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L8-L8) +
	cp.execSync('rm -rf ' + path.join(__dirname, "temp")); // BAD
+   
+   	execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
+   	execa.shellSync('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
+   
+   
+ + +
+ +---------------------------------------- + +[javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L9-L9) + +

+	execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
+	execa.shellSync('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
+
+	const safe = "\"" + path.join(__dirname, "temp") + "\"";
+
+ +*This shell command depends on an uncontrolled [absolute path](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L9-L9).* + +#### Paths + +
+Path with 3 steps + +1. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L9-L9) +

+   	execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
+   	execa.shellSync('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
+   
+   	const safe = "\"" + path.join(__dirname, "temp") + "\"";
+   
+ +2. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L9-L9) +

+   	execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
+   	execa.shellSync('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
+   
+   	const safe = "\"" + path.join(__dirname, "temp") + "\"";
+   
+ +3. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L9-L9) +

+   	execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
+   	execa.shellSync('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
+   
+   	const safe = "\"" + path.join(__dirname, "temp") + "\"";
+   
+ + +
+ +---------------------------------------- diff --git a/extensions/ql-vscode/test/unit-tests/data/markdown-generation/interpreted-results/path-problem/expected/result-2-meteor-meteor.md b/extensions/ql-vscode/test/unit-tests/data/markdown-generation/interpreted-results/path-problem/expected/result-2-meteor-meteor.md new file mode 100644 index 000000000..6a216f144 --- /dev/null +++ b/extensions/ql-vscode/test/unit-tests/data/markdown-generation/interpreted-results/path-problem/expected/result-2-meteor-meteor.md @@ -0,0 +1,144 @@ +### meteor/meteor + +[npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L259-L259) + +
  if (isWindows()) {
+    //set for the current session and beyond
+    child_process.execSync(`setx path "${meteorPath}/;%path%`);
+    return;
+  }
+
+ +*This shell command depends on an uncontrolled [absolute path](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/config.js#L39-L39).* + +#### Paths + +
+Path with 11 steps + +1. [npm-packages/meteor-installer/config.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/config.js#L39-L39) +

+   const meteorLocalFolder = '.meteor';
+   const meteorPath = path.resolve(rootPath, meteorLocalFolder);
+   
+   module.exports = {
+   
+ +2. [npm-packages/meteor-installer/config.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/config.js#L39-L39) +

+   const meteorLocalFolder = '.meteor';
+   const meteorPath = path.resolve(rootPath, meteorLocalFolder);
+   
+   module.exports = {
+   
+ +3. [npm-packages/meteor-installer/config.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/config.js#L44-L44) +
  METEOR_LATEST_VERSION,
+     extractPath: rootPath,
+     meteorPath,
+     release: process.env.INSTALL_METEOR_VERSION || METEOR_LATEST_VERSION,
+     rootPath,
+   
+ +4. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L12-L12) +
const os = require('os');
+   const {
+     meteorPath,
+     release,
+     startedPath,
+   
+ +5. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L11-L23) +
const tmp = require('tmp');
+   const os = require('os');
+   const {
+     meteorPath,
+     release,
+     startedPath,
+     extractPath,
+     isWindows,
+     rootPath,
+     sudoUser,
+     isSudo,
+     isMac,
+     METEOR_LATEST_VERSION,
+     shouldSetupExecPath,
+   } = require('./config.js');
+   const { uninstall } = require('./uninstall');
+   const {
+   
+ +6. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L259-L259) +
  if (isWindows()) {
+       //set for the current session and beyond
+       child_process.execSync(`setx path "${meteorPath}/;%path%`);
+       return;
+     }
+   
+ +7. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L259-L259) +
  if (isWindows()) {
+       //set for the current session and beyond
+       child_process.execSync(`setx path "${meteorPath}/;%path%`);
+       return;
+     }
+   
+ +8. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L259-L259) +
  if (isWindows()) {
+       //set for the current session and beyond
+       child_process.execSync(`setx path "${meteorPath}/;%path%`);
+       return;
+     }
+   
+ +9. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L259-L259) +
  if (isWindows()) {
+       //set for the current session and beyond
+       child_process.execSync(`setx path "${meteorPath}/;%path%`);
+       return;
+     }
+   
+ +10. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L259-L259) +
  if (isWindows()) {
+        //set for the current session and beyond
+        child_process.execSync(`setx path "${meteorPath}/;%path%`);
+        return;
+      }
+    
+ +11. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L259-L259) +
  if (isWindows()) {
+        //set for the current session and beyond
+        child_process.execSync(`setx path "${meteorPath}/;%path%`);
+        return;
+      }
+    
+ + +
+ +
+Path with 2 steps + +1. [npm-packages/meteor-installer/config.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/config.js#L39-L39) +

+   const meteorLocalFolder = '.meteor';
+   const meteorPath = path.resolve(rootPath, meteorLocalFolder);
+   
+   module.exports = {
+   
+ +2. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L259-L259) +
  if (isWindows()) {
+       //set for the current session and beyond
+       child_process.execSync(`setx path "${meteorPath}/;%path%`);
+       return;
+     }
+   
+ + +
+ +---------------------------------------- diff --git a/extensions/ql-vscode/test/unit-tests/data/markdown-generation/interpreted-results/problem/analyses-results.json b/extensions/ql-vscode/test/unit-tests/data/markdown-generation/interpreted-results/problem/analyses-results.json new file mode 100644 index 000000000..b8db420bf --- /dev/null +++ b/extensions/ql-vscode/test/unit-tests/data/markdown-generation/interpreted-results/problem/analyses-results.json @@ -0,0 +1,198 @@ +[ + { + "repository": { + "id": 143040428, + "fullName": "github/codeql", + "private": false, + "stargazersCount": 5703, + "updatedAt": "2023-02-15T10:11:45Z" + }, + "analysisStatus": "succeeded", + "resultCount": 1, + "artifactSizeInBytes": 1038, + "interpretedResults": [ + { + "message": { + "tokens": [ + { + "t": "text", + "text": "This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'aa'." + } + ] + }, + "shortDescription": "This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'aa'.", + "fileLink": { + "fileLinkPrefix": "https://github.com/github/codeql/blob/d094bbc06d063d0da8d0303676943c345e61de53", + "filePath": "javascript/extractor/tests/regexp/input/multipart.js" + }, + "severity": "Warning", + "codeSnippet": { + "startLine": 15, + "endLine": 22, + "text": "\nvar bad95 = new RegExp(\n \"(a\" + \n \"|\" + \n \"aa)*\" + \n \"b$\"\n);\n\n" + }, + "highlightedRegion": { + "startLine": 17, + "startColumn": 6, + "endLine": 20, + "endColumn": 6 + }, + "codeFlows": [] + } + ] + }, + { + "repository": { + "id": 3214406, + "fullName": "meteor/meteor", + "private": false, + "stargazersCount": 43274, + "updatedAt": "2023-02-14T21:06:55Z" + }, + "analysisStatus": "succeeded", + "resultCount": 5, + "artifactSizeInBytes": 3478, + "interpretedResults": [ + { + "message": { + "tokens": [ + { + "t": "text", + "text": "This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '----'." + } + ] + }, + "shortDescription": "This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '----'.", + "fileLink": { + "fileLinkPrefix": "https://github.com/meteor/meteor/blob/53f3c4442d3542d3d2a012a854472a0d1bef9d12", + "filePath": "packages/deprecated/markdown/showdown.js" + }, + "severity": "Warning", + "codeSnippet": { + "startLine": 413, + "endLine": 417, + "text": "\t\t/g,hashElement);\n\t*/\n\ttext = text.replace(/(\\n\\n[ ]{0,3}[ \\t]*(?=\\n{2,}))/g,hashElement);\n\n\t// PHP and ASP-style processor instructions ( and <%...%>)\n" + }, + "highlightedRegion": { + "startLine": 415, + "startColumn": 41, + "endLine": 415, + "endColumn": 48 + }, + "codeFlows": [] + }, + { + "message": { + "tokens": [ + { + "t": "text", + "text": "This part of the regular expression may cause exponential backtracking on strings starting with '