Merge remote-tracking branch 'upstream/main' into aeisenberg/run-with-all-data-extensions
This commit is contained in:
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -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
|
||||
|
||||
134
.github/codeql/queries/unique-command-use.ql
vendored
Normal file
134
.github/codeql/queries/unique-command-use.ql
vendored
Normal file
@@ -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"
|
||||
|
||||
15
.github/workflows/main.yml
vendored
15
.github/workflows/main.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -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
|
||||
|
||||
1
.vscode/launch.json
vendored
1
.vscode/launch.json
vendored
@@ -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": [
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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).
|
||||
|
||||
[](https://github.com/github/vscode-codeql/actions?query=workflow%3A%22Build+Extension%22+branch%3Amaster)
|
||||
[](https://marketplace.visualstudio.com/items?itemName=github.vscode-codeql)
|
||||
[](https://marketplace.visualstudio.com/items?itemName=github.vscode-codeql)
|
||||
|
||||
## Features
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -5,10 +5,12 @@ This document describes the manual test plan for the QL extension for Visual Stu
|
||||
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
|
||||
choose to go through some of the Optional Test Cases.
|
||||
@@ -24,13 +26,18 @@ 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
|
||||
@@ -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".
|
||||
@@ -74,9 +79,9 @@ choose to go through some of the Optional Test Cases.
|
||||
- Check that exporting results works
|
||||
- Check that sorting results works
|
||||
- Check that copying repo lists works
|
||||
2. Open the query directory (containing results):
|
||||
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
|
||||
@@ -98,6 +103,7 @@ 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
|
||||
@@ -108,6 +114,7 @@ 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
|
||||
2. Some but not all repositories in the list have a CodeQL database
|
||||
@@ -115,6 +122,7 @@ These are mostly aimed at MRVA, but some of them are also applicable to non-MRVA
|
||||
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 contains repositories that have CodeQL databases
|
||||
2. The org contains repositories of the right language but without CodeQL databases
|
||||
@@ -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
|
||||
@@ -162,7 +176,7 @@ with this since it has quite a limited number of actions you can do.
|
||||
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. 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,6 +244,7 @@ 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
|
||||
@@ -272,9 +291,51 @@ This requires running a MRVA query and seeing the results view.
|
||||
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
|
||||
|
||||
#### 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.
|
||||
|
||||
@@ -8,6 +8,7 @@ module.exports = {
|
||||
projects: [
|
||||
"<rootDir>/src/view",
|
||||
"<rootDir>/test/unit-tests",
|
||||
"<rootDir>/test/vscode-tests/activated-extension",
|
||||
"<rootDir>/test/vscode-tests/cli-integration",
|
||||
"<rootDir>/test/vscode-tests/no-workspace",
|
||||
"<rootDir>/test/vscode-tests/minimal-workspace",
|
||||
|
||||
4
extensions/ql-vscode/package-lock.json
generated
4
extensions/ql-vscode/package-lock.json
generated
@@ -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": {
|
||||
|
||||
@@ -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 `<owner>/<repo>`)."
|
||||
},
|
||||
"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",
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<EventType, void, unknown> {
|
||||
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<string> {
|
||||
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<OutputType> {
|
||||
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<OutputType> {
|
||||
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<TestCompleted, void, unknown> {
|
||||
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<TestCompleted>(
|
||||
for await (const event of this.runAsyncCodeQlCliCommand<TestCompleted>(
|
||||
["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<string[]> {
|
||||
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 `<package-scope/name[@version]>` 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<void> {
|
||||
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);
|
||||
}
|
||||
|
||||
37
extensions/ql-vscode/src/common/query-language.ts
Normal file
37
extensions/ql-vscode/src/common/query-language.ts
Normal file
@@ -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",
|
||||
};
|
||||
@@ -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,
|
||||
|
||||
@@ -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<boolean>();
|
||||
}
|
||||
|
||||
// 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 `<owner>/<repo>`).
|
||||
*/
|
||||
const REMOTE_REPO_LISTS = new Setting(
|
||||
"repositoryLists",
|
||||
VARIANT_ANALYSIS_SETTING,
|
||||
);
|
||||
|
||||
export function getRemoteRepositoryLists():
|
||||
| Record<string, string[]>
|
||||
| undefined {
|
||||
return REMOTE_REPO_LISTS.getValue<Record<string, string[]>>() || undefined;
|
||||
}
|
||||
|
||||
export async function setRemoteRepositoryLists(
|
||||
lists: Record<string, string[]> | 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 `<owner>/<repo>`).
|
||||
*/
|
||||
const REPO_LISTS_PATH = new Setting(
|
||||
"repositoryListsPath",
|
||||
VARIANT_ANALYSIS_SETTING,
|
||||
);
|
||||
|
||||
export function getRemoteRepositoryListsPath(): string | undefined {
|
||||
return REPO_LISTS_PATH.getValue<string>() || 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<boolean>();
|
||||
}
|
||||
|
||||
// Settings for mocking the GitHub API.
|
||||
const MOCK_GH_API_SERVER = new Setting("mockGitHubApiServer", ROOT_SETTING);
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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}`,
|
||||
}));
|
||||
|
||||
@@ -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<void> {
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
@@ -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<void> {
|
||||
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<DatabaseQuickPickItem>((dbItem) => ({
|
||||
databaseItem: dbItem,
|
||||
label: dbItem.name,
|
||||
description: dbItem.language,
|
||||
}));
|
||||
/**
|
||||
* Databases that were selected in the quick pick menu.
|
||||
*/
|
||||
const quickpick = await window.showQuickPick<DatabaseQuickPickItem>(
|
||||
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<DatabaseQuickPickItem>(
|
||||
(dbItem) => ({
|
||||
databaseItem: dbItem,
|
||||
label: dbItem.name,
|
||||
description: dbItem.language,
|
||||
}),
|
||||
);
|
||||
/**
|
||||
* Databases that were selected in the quick pick menu.
|
||||
*/
|
||||
const quickpick = await window.showQuickPick<DatabaseQuickPickItem>(
|
||||
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<void> {
|
||||
|
||||
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<string> {
|
||||
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(
|
||||
|
||||
@@ -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<U> {
|
||||
*
|
||||
* @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]) => {
|
||||
|
||||
@@ -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(
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Security-Policy"
|
||||
content="default-src 'none'; script-src 'nonce-${nonce}'; font-src ${fontSrc}; style-src ${styleSrc}; connect-src ${
|
||||
content="default-src 'none'; script-src 'nonce-${nonce}'${
|
||||
allowWasmEval ? " 'wasm-unsafe-eval'" : ""
|
||||
}; font-src ${fontSrc}; style-src ${styleSrc}; connect-src ${
|
||||
webview.cspSource
|
||||
};">
|
||||
${stylesheetsHtmlLines.join(` ${EOL}`)}
|
||||
|
||||
@@ -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<void> {
|
||||
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,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CancellationToken } from "vscode";
|
||||
import { ProgressCallback } from "../commandRunner";
|
||||
import { DatabaseItem } from "../databases";
|
||||
import { DatabaseItem } from "../local-databases";
|
||||
import {
|
||||
Dataset,
|
||||
deregisterDatabases,
|
||||
|
||||
@@ -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.",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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<void> => {
|
||||
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> => {
|
||||
void extLogger.log("Removing orphaned databases from workspace storage.");
|
||||
let dbDirs = undefined;
|
||||
@@ -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<DatabaseItem> {
|
||||
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(
|
||||
@@ -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.
|
||||
|
||||
@@ -96,8 +96,8 @@ export class MockGitHubApiServer extends DisposableObject {
|
||||
}
|
||||
|
||||
public async stopRecording(): Promise<void> {
|
||||
await this.recorder.stop();
|
||||
await this.recorder.clear();
|
||||
this.recorder.stop();
|
||||
this.recorder.clear();
|
||||
}
|
||||
|
||||
public async getScenarioNames(scenariosPath?: string): Promise<string[]> {
|
||||
|
||||
@@ -35,11 +35,11 @@ export class VSCodeMockGitHubApiServer extends DisposableObject {
|
||||
}
|
||||
|
||||
public async startServer(): Promise<void> {
|
||||
await this.server.startServer();
|
||||
this.server.startServer();
|
||||
}
|
||||
|
||||
public async stopServer(): Promise<void> {
|
||||
await this.server.stopServer();
|
||||
this.server.stopServer();
|
||||
|
||||
await commands.executeCommand(
|
||||
"setContext",
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -105,7 +105,7 @@ export function transformBqrsResultSet(
|
||||
};
|
||||
}
|
||||
|
||||
type BqrsKind =
|
||||
export type BqrsKind =
|
||||
| "String"
|
||||
| "Float"
|
||||
| "Integer"
|
||||
|
||||
@@ -67,3 +67,8 @@ export function pathsEqual(
|
||||
}
|
||||
return path1 === path2;
|
||||
}
|
||||
|
||||
export async function readDirFullPaths(path: string): Promise<string[]> {
|
||||
const baseNames = await readdir(path);
|
||||
return baseNames.map((baseName) => join(path, baseName));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
Repository,
|
||||
RepositoryWithMetadata,
|
||||
} from "../remote-queries/shared/repository";
|
||||
} from "../variant-analysis/shared/repository";
|
||||
import { parseDate } from "./date";
|
||||
|
||||
export enum SortKey {
|
||||
|
||||
93
extensions/ql-vscode/src/qlpack-generator.ts
Normal file
93
extensions/ql-vscode/src/qlpack-generator.ts
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface QueryHistoryDirs {
|
||||
localQueriesDirPath: string;
|
||||
variantAnalysesDirPath: string;
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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<CompletedLocalQueryInfo | undefined> {
|
||||
// Remote queries cannot be compared
|
||||
// Variant analyses cannot be compared
|
||||
if (
|
||||
singleItem.t !== "local" ||
|
||||
multiSelect.some((s) => s.t !== "local") ||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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}` : ""),
|
||||
);
|
||||
}
|
||||
@@ -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<RepositorySelection> {
|
||||
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<RepoListQuickPickItem>(
|
||||
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 <owner>/<repo> (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<RepoList[]> {
|
||||
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<void> {
|
||||
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<Record<string, unknown>> {
|
||||
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<RepoList>(([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<string | undefined> {
|
||||
return await window.showInputBox({
|
||||
title:
|
||||
"Enter a GitHub repository in the format <owner>/<repo> (e.g. github/codeql)",
|
||||
placeHolder: "<owner>/<repo>",
|
||||
prompt:
|
||||
"Tip: you can save frequently used repositories in the `codeQL.variantAnalysis.repositoryLists` setting",
|
||||
ignoreFocusOut: true,
|
||||
});
|
||||
}
|
||||
|
||||
async function getOwner(): Promise<string | undefined> {
|
||||
return await window.showInputBox({
|
||||
title: "Enter a GitHub user or organization",
|
||||
ignoreFocusOut: true,
|
||||
});
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export interface Repository {
|
||||
owner: string;
|
||||
name: string;
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export interface AnalysisFailure {
|
||||
nwo: string;
|
||||
error: string;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
@@ -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<typeof DownloadButtonComponent>;
|
||||
|
||||
const Template: ComponentStory<typeof DownloadButtonComponent> = (args) => (
|
||||
<DownloadButtonComponent {...args} />
|
||||
);
|
||||
|
||||
export const DownloadButton = Template.bind({});
|
||||
DownloadButton.args = {
|
||||
text: "Download",
|
||||
};
|
||||
@@ -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<typeof DownloadSpinnerComponent>;
|
||||
|
||||
export const DownloadSpinner = <DownloadSpinnerComponent />;
|
||||
@@ -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<typeof LastUpdatedComponent>;
|
||||
|
||||
const Template: ComponentStory<typeof LastUpdatedComponent> = (args) => (
|
||||
<LastUpdatedComponent {...args} />
|
||||
);
|
||||
|
||||
export const LastUpdated = Template.bind({});
|
||||
|
||||
LastUpdated.args = {
|
||||
lastUpdated: -3_600_000, // 1 hour ago
|
||||
};
|
||||
@@ -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<typeof RemoteQueries>;
|
||||
|
||||
const Template: ComponentStory<typeof RemoteQueries> = () => {
|
||||
useEffect(() => {
|
||||
window.postMessage(remoteQueryResult);
|
||||
window.postMessage(analysesResults);
|
||||
});
|
||||
|
||||
return <RemoteQueries />;
|
||||
};
|
||||
|
||||
export const Top10JavaScript = Template.bind({});
|
||||
@@ -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<typeof RepositoriesSearchComponent>;
|
||||
|
||||
export const RepositoriesSearch = () => {
|
||||
const [filterValue, setFilterValue] = useState("");
|
||||
|
||||
return (
|
||||
<RepositoriesSearchComponent
|
||||
filterValue={filterValue}
|
||||
setFilterValue={setFilterValue}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -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": []
|
||||
}
|
||||
}
|
||||
@@ -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<typeof AnalysisAlertResult>;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<void> {
|
||||
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
|
||||
|
||||
@@ -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<void> {
|
||||
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(
|
||||
@@ -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;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user