Compare commits
159 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4546616871 | ||
|
|
b201c45e2d | ||
|
|
3364af5305 | ||
|
|
d6af6e7c4c | ||
|
|
f9f454509b | ||
|
|
9f0453a80a | ||
|
|
af2a592e3d | ||
|
|
a4fcbd093c | ||
|
|
f02c007bdb | ||
|
|
66874823aa | ||
|
|
1a59467707 | ||
|
|
78383e376c | ||
|
|
32cf05cb4b | ||
|
|
1ec47a4eef | ||
|
|
4d2f84d599 | ||
|
|
a858b39f26 | ||
|
|
5d71ce41a8 | ||
|
|
76354c4518 | ||
|
|
4e310ce652 | ||
|
|
68bfa00707 | ||
|
|
51ef674cc5 | ||
|
|
d2612e2dca | ||
|
|
1db3dda533 | ||
|
|
9c3b0b24c9 | ||
|
|
df06c75070 | ||
|
|
b3c44ea317 | ||
|
|
d6673c4ede | ||
|
|
d68e270e90 | ||
|
|
97b9c43ae1 | ||
|
|
7e00e9cd2f | ||
|
|
459d606751 | ||
|
|
a069ae45fb | ||
|
|
a6225c5edf | ||
|
|
64d4439815 | ||
|
|
b11a9e2c88 | ||
|
|
75ab23bbd8 | ||
|
|
5bab2421ea | ||
|
|
d5edbca4e4 | ||
|
|
1ee349d2ef | ||
|
|
79f89c9e4b | ||
|
|
2fbdefe4f0 | ||
|
|
01403aeee7 | ||
|
|
9f7f34a87c | ||
|
|
987b92ab1e | ||
|
|
6fd2579c9c | ||
|
|
c9e1a64d3a | ||
|
|
03f330da52 | ||
|
|
4f6d72a9ce | ||
|
|
b5d3a612d8 | ||
|
|
48209e7732 | ||
|
|
9934449ae9 | ||
|
|
fdc4d97f93 | ||
|
|
b1ca9418b4 | ||
|
|
7d3a350a28 | ||
|
|
ca4c511227 | ||
|
|
d4df484acb | ||
|
|
3e1d924110 | ||
|
|
cc4666a614 | ||
|
|
51906bbcee | ||
|
|
fcfa6979e2 | ||
|
|
f0783ed274 | ||
|
|
fe45e00fb3 | ||
|
|
4a317d33a9 | ||
|
|
ffc7135c1f | ||
|
|
c805b48f18 | ||
|
|
e1b95c2f7c | ||
|
|
481d2f5404 | ||
|
|
15fa23acb4 | ||
|
|
a625d9aabe | ||
|
|
cd70b19bb3 | ||
|
|
7a58084df9 | ||
|
|
916d16126b | ||
|
|
a5eb915267 | ||
|
|
a5440ef482 | ||
|
|
fc86be7687 | ||
|
|
f0909a9d67 | ||
|
|
40b5b5ba7e | ||
|
|
fa85bcfad6 | ||
|
|
dd1b054f26 | ||
|
|
b96cd6c7e1 | ||
|
|
b466d2aa36 | ||
|
|
7f6c1ad7f7 | ||
|
|
10f4b47019 | ||
|
|
b31a769fdd | ||
|
|
c4e2f11372 | ||
|
|
9494d32144 | ||
|
|
8b4f2d2009 | ||
|
|
12555d90c1 | ||
|
|
d46e03b4cc | ||
|
|
f0e2285122 | ||
|
|
f4d0d23170 | ||
|
|
513fb65560 | ||
|
|
7d353ced9b | ||
|
|
be3506d987 | ||
|
|
3fa6304050 | ||
|
|
bb40e5bcad | ||
|
|
e2a8ae318b | ||
|
|
79867e2f9d | ||
|
|
1bc13d70ce | ||
|
|
0bd359997d | ||
|
|
28a7d1cf34 | ||
|
|
1572c095ca | ||
|
|
83df05e731 | ||
|
|
b43b1d4c8e | ||
|
|
79ff9e0378 | ||
|
|
d7a82cc686 | ||
|
|
39ad5b28c7 | ||
|
|
68c159f1fe | ||
|
|
355a6fc552 | ||
|
|
6f54bebe35 | ||
|
|
bea108a412 | ||
|
|
088d2fa91e | ||
|
|
6c92a5b800 | ||
|
|
db0d062443 | ||
|
|
673ea13af8 | ||
|
|
2208b42485 | ||
|
|
376d6b75d6 | ||
|
|
d8a7dcd7d2 | ||
|
|
580257f024 | ||
|
|
233cb1cd41 | ||
|
|
d9fcfa0374 | ||
|
|
1dc48b3309 | ||
|
|
2babb0b106 | ||
|
|
247df2e1f9 | ||
|
|
ba9007e426 | ||
|
|
616b613fbd | ||
|
|
47ae0fe9f5 | ||
|
|
9d6b4e8e5a | ||
|
|
5ae70696a8 | ||
|
|
712dd525ee | ||
|
|
fdc5645928 | ||
|
|
5c81c0e9bf | ||
|
|
0e07e5e3eb | ||
|
|
c90acdb4e1 | ||
|
|
dad37f202d | ||
|
|
1f937ad208 | ||
|
|
086d8e2104 | ||
|
|
c2ddd680c1 | ||
|
|
9a14896a4e | ||
|
|
871fc0b817 | ||
|
|
ca092a1623 | ||
|
|
529bbe3ffa | ||
|
|
31b2d24ca9 | ||
|
|
f54cace05b | ||
|
|
f773f9f675 | ||
|
|
67aa216ea1 | ||
|
|
4b67f4dd54 | ||
|
|
572a8b7828 | ||
|
|
b7d6aeda3c | ||
|
|
2da77acd9e | ||
|
|
607a8380cc | ||
|
|
f698ac321f | ||
|
|
85abd9a1ac | ||
|
|
329eba9536 | ||
|
|
733a0328d0 | ||
|
|
1c7f92b503 | ||
|
|
884ec21e5a | ||
|
|
35c533606f | ||
|
|
10c74d5d8c |
6
.github/pull_request_template.md
vendored
6
.github/pull_request_template.md
vendored
@@ -5,8 +5,4 @@
|
|||||||
|
|
||||||
Replace this with a description of the changes your pull request makes.
|
Replace this with a description of the changes your pull request makes.
|
||||||
|
|
||||||
## Checklist
|
Remember to update the [changelog](https://github.com/github/vscode-codeql/blob/main/extensions/ql-vscode/CHANGELOG.md) if there have been user-facing changes!
|
||||||
|
|
||||||
- [ ] [CHANGELOG.md](https://github.com/github/vscode-codeql/blob/main/extensions/ql-vscode/CHANGELOG.md) has been updated to incorporate all user visible changes made by this pull request.
|
|
||||||
- [ ] Issues have been created for any UI or other user-facing changes made by this pull request.
|
|
||||||
- [ ] _[Maintainers only]_ If this pull request makes user-facing changes that require documentation changes, open a corresponding docs pull request in the [github/codeql](https://github.com/github/codeql/tree/main/docs/codeql/codeql-for-visual-studio-code) repo and add the `ready-for-doc-review` label there.
|
|
||||||
|
|||||||
4
.github/workflows/e2e-tests.yml
vendored
4
.github/workflows/e2e-tests.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Start containers
|
- name: Start containers
|
||||||
working-directory: extensions/ql-vscode/test/e2e
|
working-directory: extensions/ql-vscode/test/e2e
|
||||||
run: docker-compose -f "docker-compose.yml" up -d --build
|
run: docker compose -f "docker-compose.yml" up -d --build
|
||||||
|
|
||||||
- name: Install Playwright Browsers
|
- name: Install Playwright Browsers
|
||||||
working-directory: extensions/ql-vscode
|
working-directory: extensions/ql-vscode
|
||||||
@@ -43,4 +43,4 @@ jobs:
|
|||||||
- name: Stop containers
|
- name: Stop containers
|
||||||
working-directory: extensions/ql-vscode/test/e2e
|
working-directory: extensions/ql-vscode/test/e2e
|
||||||
if: always()
|
if: always()
|
||||||
run: docker-compose -f "docker-compose.yml" down -v
|
run: docker compose -f "docker-compose.yml" down -v
|
||||||
|
|||||||
13
.github/workflows/main.yml
vendored
13
.github/workflows/main.yml
vendored
@@ -144,6 +144,19 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest]
|
os: [ubuntu-latest, windows-latest]
|
||||||
steps:
|
steps:
|
||||||
|
# Enable 8.3 filename creation. This is not required to run the extension but it is required for the unit tests to pass.
|
||||||
|
# This feature is currently enabled by default in Windows 11 for the C: drive and therefore we must maintain support for it.
|
||||||
|
# This setting needs to be enabled before files are created, i.e. before we checkout the repository.
|
||||||
|
- name: Enable 8.3 filenames
|
||||||
|
shell: pwsh
|
||||||
|
if: ${{ matrix.os == 'windows-latest' }}
|
||||||
|
run: |
|
||||||
|
$shortNameEnableProcess = Start-Process -FilePath fsutil.exe -ArgumentList ('8dot3name', 'set', '0') -Wait -PassThru
|
||||||
|
$shortNameEnableExitCode = $shortNameEnableProcess.ExitCode
|
||||||
|
if ($shortNameEnableExitCode -ne 0) {
|
||||||
|
exit $shortNameEnableExitCode
|
||||||
|
}
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -134,7 +134,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Publish to Registry
|
- name: Publish to Registry
|
||||||
run: |
|
run: |
|
||||||
npx vsce publish -p $VSCE_TOKEN --packagePath *.vsix
|
npx @vscode/vsce publish -p $VSCE_TOKEN --packagePath *.vsix
|
||||||
|
|
||||||
open-vsx-publish:
|
open-vsx-publish:
|
||||||
name: Publish to Open VSX Registry
|
name: Publish to Open VSX Registry
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ Here are a few things you can do that will increase the likelihood of your pull
|
|||||||
- [Integration tests that do require the VS Code API are located here](extensions/ql-vscode/src/vscode-tests).
|
- [Integration tests that do require the VS Code API are located here](extensions/ql-vscode/src/vscode-tests).
|
||||||
- Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests.
|
- Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests.
|
||||||
- Write a [good commit message](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
|
- Write a [good commit message](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
|
||||||
|
- Update the [changelog](https://github.com/github/vscode-codeql/blob/main/extensions/ql-vscode/CHANGELOG.md) if you are making user-facing changes.
|
||||||
|
|
||||||
## Setting up a local build
|
## Setting up a local build
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# Releasing (write access required)
|
# Releasing (write access required)
|
||||||
|
|
||||||
|
1. Make sure the needed authentication keys are valid. Most likely the Azure DevOps PAT needs to be regenerated. See below.
|
||||||
1. Determine the new version number. We default to increasing the patch version number, but make our own judgement about whether a change is big enough to warrant a minor version bump. Common reasons for a minor bump could include:
|
1. Determine the new version number. We default to increasing the patch version number, but make our own judgement about whether a change is big enough to warrant a minor version bump. Common reasons for a minor bump could include:
|
||||||
- Making substantial new features available to all users. This can include lifting a feature flag.
|
- Making substantial new features available to all users. This can include lifting a feature flag.
|
||||||
- Breakage in compatibility with recent versions of the CLI.
|
- Breakage in compatibility with recent versions of the CLI.
|
||||||
@@ -60,7 +61,7 @@
|
|||||||
|
|
||||||
## Secrets and authentication for publishing
|
## Secrets and authentication for publishing
|
||||||
|
|
||||||
Repository administrators, will need to manage the authentication keys for publishing to the VS Code marketplace and Open VSX. Each requires an authentication token. The VS Code marketplace token expires yearly.
|
Repository administrators, will need to manage the authentication keys for publishing to the VS Code marketplace and Open VSX. Each requires an authentication token.
|
||||||
|
|
||||||
To regenerate the Open VSX token:
|
To regenerate the Open VSX token:
|
||||||
|
|
||||||
@@ -69,4 +70,4 @@ To regenerate the Open VSX token:
|
|||||||
1. Go to the [Access Tokens](https://open-vsx.org/user-settings/tokens) page and generate a new token.
|
1. Go to the [Access Tokens](https://open-vsx.org/user-settings/tokens) page and generate a new token.
|
||||||
1. Update the secret in the `publish-open-vsx` environment in the project settings.
|
1. Update the secret in the `publish-open-vsx` environment in the project settings.
|
||||||
|
|
||||||
To regenerate the VSCode Marketplace token, please see our internal documentation. Note that Azure DevOps PATs expire every 90 days and must be regenerated.
|
To regenerate the VSCode Marketplace token, please see our internal documentation. Note that Azure DevOps PATs expire every 7 days and must be regenerated.
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ Run one of the above MRVAs, but cancel it from within VS Code:
|
|||||||
import semmle.python.frameworks.data.internal.ApiGraphModelsExtensions
|
import semmle.python.frameworks.data.internal.ApiGraphModelsExtensions
|
||||||
|
|
||||||
from string path, string kind
|
from string path, string kind
|
||||||
where sinkModel("vscode-codeql", path, kind)
|
where sinkModel("vscode-codeql", path, kind, _)
|
||||||
select path, kind
|
select path, kind
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -210,17 +210,7 @@ Run one of the above MRVAs, but cancel it from within VS Code:
|
|||||||
4. Open the ".model.yml" file corresponding to the library that was changed.
|
4. Open the ".model.yml" file corresponding to the library that was changed.
|
||||||
- Check that the file contains entries for the methods that were modeled.
|
- Check that the file contains entries for the methods that were modeled.
|
||||||
|
|
||||||
#### Test Case 3: Model with AI
|
#### Test Case 3: Model as dependency
|
||||||
|
|
||||||
Note that this test requires the feature flag: `codeQL.model.llmGeneration`
|
|
||||||
|
|
||||||
A package that the AI normally gives models for is `javax.servlet-api` from the `jhy/jsoup` repository.
|
|
||||||
|
|
||||||
1. Click "Model with AI".
|
|
||||||
- Check that rows change to "Thinking".
|
|
||||||
- Check that results come back and rows get filled out.
|
|
||||||
|
|
||||||
#### Test Case 4: Model as dependency
|
|
||||||
|
|
||||||
Note that this test requires the feature flag: `codeQL.model.flowGeneration`
|
Note that this test requires the feature flag: `codeQL.model.flowGeneration`
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v18.18.2
|
v20.14.0
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { addons } from "@storybook/manager-api";
|
import { addons } from "@storybook/manager-api";
|
||||||
import { Addon_TypesEnum } from "@storybook/types";
|
import { Addon_TypesEnum } from "storybook/internal/types";
|
||||||
import { ThemeSelector } from "./ThemeSelector";
|
import { ThemeSelector } from "./ThemeSelector";
|
||||||
|
|
||||||
const ADDON_ID = "vscode-theme-addon";
|
const ADDON_ID = "vscode-theme-addon";
|
||||||
|
|||||||
@@ -1,5 +1,16 @@
|
|||||||
# CodeQL for Visual Studio Code: Changelog
|
# CodeQL for Visual Studio Code: Changelog
|
||||||
|
|
||||||
|
## 1.14.0 - 7 August 2024
|
||||||
|
|
||||||
|
- Add Python support to the CodeQL Model Editor. [#3676](https://github.com/github/vscode-codeql/pull/3676)
|
||||||
|
- Update variant analysis view to display the length of the shortest path for path queries. [#3671](https://github.com/github/vscode-codeql/pull/3671)
|
||||||
|
- Remove support for CodeQL CLI versions older than 2.15.5. [#3681](https://github.com/github/vscode-codeql/pull/3681)
|
||||||
|
|
||||||
|
## 1.13.1 - 29 May 2024
|
||||||
|
|
||||||
|
- Fix a bug when re-importing test databases that erroneously showed old source code. [#3616](https://github.com/github/vscode-codeql/pull/3616)
|
||||||
|
- Update the progress window with details on potentially long-running post-processing steps after running a query. [#3622](https://github.com/github/vscode-codeql/pull/3622)
|
||||||
|
|
||||||
## 1.13.0 - 1 May 2024
|
## 1.13.0 - 1 May 2024
|
||||||
|
|
||||||
- Add Ruby support to the CodeQL Model Editor. [#3584](https://github.com/github/vscode-codeql/pull/3584)
|
- Add Ruby support to the CodeQL Model Editor. [#3584](https://github.com/github/vscode-codeql/pull/3584)
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ This project is an extension for Visual Studio Code that adds rich language supp
|
|||||||
|
|
||||||
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).
|
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).
|
||||||
|
|
||||||
|
You can also read full documentation for the extension on the [GitHub documentation website](https://docs.github.com/code-security/codeql-for-vs-code/using-the-advanced-functionality-of-the-codeql-for-vs-code-extension).
|
||||||
|
|
||||||
## Quick start overview
|
## Quick start overview
|
||||||
|
|
||||||
The information in this `README` file describes the quickest way to start using CodeQL.
|
The information in this `README` file describes the quickest way to start using CodeQL.
|
||||||
For information about other configurations, see the separate [CodeQL help](https://codeql.github.com/docs/codeql-for-visual-studio-code/).
|
For information about other configurations, see "[Using the advanced functionality of the CodeQL for VS Code extension](https://docs.github.com/code-security/codeql-for-vs-code/using-the-advanced-functionality-of-the-codeql-for-vs-code-extension)" in the GitHub documentation.
|
||||||
|
|
||||||
### Quick start: Installing and configuring the extension
|
### Quick start: Installing and configuring the extension
|
||||||
|
|
||||||
@@ -42,7 +44,7 @@ The CodeQL extension requires a minimum of Visual Studio Code 1.39. Older versio
|
|||||||
|
|
||||||
The extension uses the [CodeQL CLI](https://codeql.github.com/docs/codeql-cli/) to compile and run queries. The extension automatically manages access to the CLI for you by default (recommended). To check for updates to the CodeQL CLI, you can use the **CodeQL: Check for CLI Updates** command.
|
The extension uses the [CodeQL CLI](https://codeql.github.com/docs/codeql-cli/) to compile and run queries. The extension automatically manages access to the CLI for you by default (recommended). To check for updates to the CodeQL CLI, you can use the **CodeQL: Check for CLI Updates** command.
|
||||||
|
|
||||||
If you want to override the default behavior and use a CodeQL CLI that's already on your machine, see [Configuring access to the CodeQL CLI](https://codeql.github.com/docs/codeql-for-visual-studio-code/setting-up-codeql-in-visual-studio-code/#configuring-access-to-the-codeql-cli).
|
If you want to override the default behavior and use a CodeQL CLI that's already on your machine, see "[Configuring access to the CodeQL CLI](https://docs.github.com/code-security/codeql-for-vs-code/using-the-advanced-functionality-of-the-codeql-for-vs-code-extension/configuring-access-to-the-codeql-cli)" in the GitHub documentation.
|
||||||
|
|
||||||
If you have any difficulty with CodeQL CLI access, see the **CodeQL Extension Log** in the **Output** view for any error messages.
|
If you have any difficulty with CodeQL CLI access, see the **CodeQL Extension Log** in the **Output** view for any error messages.
|
||||||
|
|
||||||
@@ -52,7 +54,7 @@ When you're working with CodeQL, you need access to the standard CodeQL librarie
|
|||||||
Initially, we recommend that you clone and use the ready-to-use [starter workspace](https://github.com/github/vscode-codeql-starter/).
|
Initially, we recommend that you clone and use the ready-to-use [starter workspace](https://github.com/github/vscode-codeql-starter/).
|
||||||
This includes libraries and queries for the main supported languages, with folders set up ready for your custom queries. After cloning the workspace (use `git clone --recursive`), you can use it in the same way as any other VS Code workspace—with the added advantage that you can easily update the CodeQL libraries.
|
This includes libraries and queries for the main supported languages, with folders set up ready for your custom queries. After cloning the workspace (use `git clone --recursive`), you can use it in the same way as any other VS Code workspace—with the added advantage that you can easily update the CodeQL libraries.
|
||||||
|
|
||||||
For information about configuring an existing workspace for CodeQL, [see the documentation](https://codeql.github.com/docs/codeql-for-visual-studio-code/setting-up-codeql-in-visual-studio-code/#updating-an-existing-workspace-for-codeql).
|
For information about configuring an existing workspace for CodeQL, see "[Setting up a CodeQL workspace](https://docs.github.com/code-security/codeql-for-vs-code/using-the-advanced-functionality-of-the-codeql-for-vs-code-extension/setting-up-a-codeql-workspace#option-2-updating-an-existing-workspace-for-codeql-advanced)" in the GitHub documentation.
|
||||||
|
|
||||||
## Upgrading CodeQL standard libraries
|
## Upgrading CodeQL standard libraries
|
||||||
|
|
||||||
@@ -85,7 +87,7 @@ While you can use the [CodeQL CLI to create your own databases](https://codeql.g
|
|||||||
1. Select the language for the database you want to download (only required if the project has databases for multiple languages).
|
1. Select the language for the database you want to download (only required if the project has databases for multiple languages).
|
||||||
1. Once the CodeQL database has been imported, it is displayed in the Databases view.
|
1. Once the CodeQL database has been imported, it is displayed in the Databases view.
|
||||||
|
|
||||||
For more information, see [Choosing a database](https://codeql.github.com/docs/codeql-for-visual-studio-code/analyzing-your-projects/#choosing-a-database) on codeql.github.com.
|
For more information, see "[Managing CodeQL databases](https://docs.github.com/code-security/codeql-for-vs-code/getting-started-with-codeql-for-vs-code/managing-codeql-databases#choosing-a-database-to-analyze)" in the GitHub documentation.
|
||||||
|
|
||||||
### Running a query
|
### Running a query
|
||||||
|
|
||||||
@@ -106,12 +108,12 @@ If you wish to navigate the query results from your keyboard, you can bind short
|
|||||||
|
|
||||||
## What next?
|
## What next?
|
||||||
|
|
||||||
For more information about the CodeQL extension, [see the documentation](https://codeql.github.com/docs/codeql-for-visual-studio-code/). Otherwise, you could:
|
We recommend reading the [full documentation for the extension](https://docs.github.com/code-security/codeql-for-vs-code/) on the GitHub documentation website. You may also find the following resources useful:
|
||||||
|
|
||||||
- [Create a database for a different codebase](https://codeql.github.com/docs/codeql-cli/creating-codeql-databases/).
|
- [Create a database for a different codebase](https://codeql.github.com/docs/codeql-cli/creating-codeql-databases/).
|
||||||
- [Try out variant analysis](https://help.semmle.com/QL/learn-ql/ql-training.html).
|
- [Try out variant analysis](https://docs.github.com/code-security/codeql-for-vs-code/getting-started-with-codeql-for-vs-code/running-codeql-queries-at-scale-with-multi-repository-variant-analysis).
|
||||||
- [Learn more about CodeQL](https://codeql.github.com/docs/).
|
- [Learn more about CodeQL](https://codeql.github.com/docs/).
|
||||||
- [Read how security researchers use CodeQL to find CVEs](https://securitylab.github.com/research).
|
- [Read how security researchers use CodeQL to find CVEs](https://github.blog/tag/github-security-lab/).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
@@ -119,4 +121,4 @@ The CodeQL extension for Visual Studio Code is [licensed](LICENSE.md) under the
|
|||||||
|
|
||||||
## Data and Telemetry
|
## Data and Telemetry
|
||||||
|
|
||||||
If you specifically opt-in to permit GitHub to do so, GitHub will collect usage data and metrics for the purposes of helping the core developers to improve the CodeQL extension for VS Code. This data will not be shared with any parties outside of GitHub. IP addresses and installation IDs will be retained for a maximum of 30 days. Anonymous data will be retained for a maximum of 180 days. For more information about telemetry, [see the documentation](https://codeql.github.com/docs/codeql-for-visual-studio-code/about-telemetry-in-codeql-for-visual-studio-code).
|
If you specifically opt-in to permit GitHub to do so, GitHub will collect usage data and metrics for the purposes of helping the core developers to improve the CodeQL extension for VS Code. This data will not be shared with any parties outside of GitHub. IP addresses and installation IDs will be retained for a maximum of 30 days. Anonymous data will be retained for a maximum of 180 days. For more information, see "[Telemetry in CodeQL for Visual Studio Code](https://docs.github.com/code-security/codeql-for-vs-code/using-the-advanced-functionality-of-the-codeql-for-vs-code-extension/telemetry-in-codeql-for-visual-studio-code)" in the GitHub documentation.
|
||||||
|
|||||||
@@ -77,5 +77,8 @@ export function copyWasmFiles() {
|
|||||||
// to configure the path to the WASM file. So, source-map will always load the file from `__dirname/mappings.wasm`.
|
// to configure the path to the WASM file. So, source-map will always load the file from `__dirname/mappings.wasm`.
|
||||||
// In version 0.8.0, it may be possible to do this properly by calling SourceMapConsumer.initialize by
|
// In version 0.8.0, it may be possible to do this properly by calling SourceMapConsumer.initialize by
|
||||||
// using the "browser" field in source-map's package.json to load the WASM file from a given file path.
|
// using the "browser" field in source-map's package.json to load the WASM file from a given file path.
|
||||||
return src("node_modules/source-map/lib/mappings.wasm").pipe(dest("out"));
|
return src("node_modules/source-map/lib/mappings.wasm", {
|
||||||
|
// WASM is a binary format, so don't try to re-encode it as text.
|
||||||
|
encoding: false,
|
||||||
|
}).pipe(dest("out"));
|
||||||
}
|
}
|
||||||
|
|||||||
6672
extensions/ql-vscode/package-lock.json
generated
6672
extensions/ql-vscode/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
|||||||
"description": "CodeQL for Visual Studio Code",
|
"description": "CodeQL for Visual Studio Code",
|
||||||
"author": "GitHub",
|
"author": "GitHub",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.13.0",
|
"version": "1.14.0",
|
||||||
"publisher": "GitHub",
|
"publisher": "GitHub",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"icon": "media/VS-marketplace-CodeQL-icon.png",
|
"icon": "media/VS-marketplace-CodeQL-icon.png",
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "^1.82.0",
|
"vscode": "^1.82.0",
|
||||||
"node": "^18.18.2",
|
"node": "^20.14.0",
|
||||||
"npm": ">=7.20.6"
|
"npm": ">=7.20.6"
|
||||||
},
|
},
|
||||||
"categories": [
|
"categories": [
|
||||||
@@ -1790,8 +1790,7 @@
|
|||||||
"when": "false"
|
"when": "false"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "codeQL.trimCache",
|
"command": "codeQL.trimCache"
|
||||||
"when": "codeql.supportsTrimCache"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"editor/context": [
|
"editor/context": [
|
||||||
@@ -1955,14 +1954,14 @@
|
|||||||
"format": "prettier --write **/*.{ts,tsx} && eslint . --ext .ts,.tsx --fix",
|
"format": "prettier --write **/*.{ts,tsx} && eslint . --ext .ts,.tsx --fix",
|
||||||
"lint": "eslint . --ext .js,.ts,.tsx --max-warnings=0",
|
"lint": "eslint . --ext .js,.ts,.tsx --max-warnings=0",
|
||||||
"lint:markdown": "markdownlint-cli2 \"../../**/*.{md,mdx}\" \"!**/node_modules/**\" \"!**/.vscode-test/**\" \"!**/build/cli/v*/**\"",
|
"lint:markdown": "markdownlint-cli2 \"../../**/*.{md,mdx}\" \"!**/node_modules/**\" \"!**/.vscode-test/**\" \"!**/build/cli/v*/**\"",
|
||||||
"find-deadcode": "ts-node scripts/find-deadcode.ts",
|
"find-deadcode": "vite-node scripts/find-deadcode.ts",
|
||||||
"format-staged": "lint-staged",
|
"format-staged": "lint-staged",
|
||||||
"storybook": "storybook dev -p 6006",
|
"storybook": "storybook dev -p 6006",
|
||||||
"build-storybook": "storybook build",
|
"build-storybook": "storybook build",
|
||||||
"lint:scenarios": "ts-node scripts/lint-scenarios.ts",
|
"lint:scenarios": "vite-node scripts/lint-scenarios.ts",
|
||||||
"generate": "npm-run-all -p generate:*",
|
"generate": "npm-run-all -p generate:*",
|
||||||
"generate:schemas": "ts-node scripts/generate-schemas.ts",
|
"generate:schemas": "vite-node scripts/generate-schemas.ts",
|
||||||
"generate:chromium-version": "ts-node scripts/generate-chromium-version.ts",
|
"generate:chromium-version": "vite-node scripts/generate-chromium-version.ts",
|
||||||
"check-types": "find . -type f -name \"tsconfig.json\" -not -path \"./node_modules/*\" | sed -r 's|/[^/]+$||' | sort | uniq | xargs -I {} sh -c \"echo Checking types in {} && cd {} && npx tsc --noEmit\"",
|
"check-types": "find . -type f -name \"tsconfig.json\" -not -path \"./node_modules/*\" | sed -r 's|/[^/]+$||' | sort | uniq | xargs -I {} sh -c \"echo Checking types in {} && cd {} && npx tsc --noEmit\"",
|
||||||
"postinstall": "patch-package",
|
"postinstall": "patch-package",
|
||||||
"prepare": "cd ../.. && husky install"
|
"prepare": "cd ../.. && husky install"
|
||||||
@@ -1972,7 +1971,7 @@
|
|||||||
"@octokit/plugin-retry": "^6.0.1",
|
"@octokit/plugin-retry": "^6.0.1",
|
||||||
"@octokit/plugin-throttling": "^8.0.0",
|
"@octokit/plugin-throttling": "^8.0.0",
|
||||||
"@octokit/rest": "^20.0.2",
|
"@octokit/rest": "^20.0.2",
|
||||||
"@vscode/codicons": "^0.0.35",
|
"@vscode/codicons": "^0.0.36",
|
||||||
"@vscode/debugadapter": "^1.59.0",
|
"@vscode/debugadapter": "^1.59.0",
|
||||||
"@vscode/debugprotocol": "^1.65.0",
|
"@vscode/debugprotocol": "^1.65.0",
|
||||||
"@vscode/webview-ui-toolkit": "^1.0.1",
|
"@vscode/webview-ui-toolkit": "^1.0.1",
|
||||||
@@ -1987,47 +1986,47 @@
|
|||||||
"nanoid": "^5.0.7",
|
"nanoid": "^5.0.7",
|
||||||
"node-fetch": "^2.6.7",
|
"node-fetch": "^2.6.7",
|
||||||
"p-queue": "^8.0.1",
|
"p-queue": "^8.0.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.3.1",
|
||||||
"semver": "^7.6.0",
|
"semver": "^7.6.2",
|
||||||
"source-map": "^0.7.4",
|
"source-map": "^0.7.4",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"stream-json": "^1.7.3",
|
"stream-json": "^1.7.3",
|
||||||
"styled-components": "^6.1.8",
|
"styled-components": "^6.1.9",
|
||||||
"tmp": "^0.2.1",
|
"tmp": "^0.2.1",
|
||||||
"tmp-promise": "^3.0.2",
|
"tmp-promise": "^3.0.2",
|
||||||
"tree-kill": "^1.2.2",
|
"tree-kill": "^1.2.2",
|
||||||
"vscode-extension-telemetry": "^0.1.6",
|
"vscode-extension-telemetry": "^0.1.6",
|
||||||
"vscode-jsonrpc": "^8.0.2",
|
"vscode-jsonrpc": "^8.2.1",
|
||||||
"vscode-languageclient": "^8.0.2",
|
"vscode-languageclient": "^8.0.2",
|
||||||
"yauzl": "^2.10.0",
|
"yauzl": "^2.10.0",
|
||||||
"zip-a-folder": "^3.1.6"
|
"zip-a-folder": "^3.1.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.24.4",
|
"@babel/core": "^7.24.6",
|
||||||
"@babel/plugin-transform-modules-commonjs": "^7.18.6",
|
"@babel/plugin-transform-modules-commonjs": "^7.24.7",
|
||||||
"@babel/preset-env": "^7.24.4",
|
"@babel/preset-env": "^7.24.4",
|
||||||
"@babel/preset-react": "^7.24.1",
|
"@babel/preset-react": "^7.24.1",
|
||||||
"@babel/preset-typescript": "^7.21.4",
|
"@babel/preset-typescript": "^7.21.4",
|
||||||
"@faker-js/faker": "^8.4.1",
|
"@faker-js/faker": "^8.4.1",
|
||||||
"@github/markdownlint-github": "^0.6.2",
|
"@github/markdownlint-github": "^0.6.2",
|
||||||
"@playwright/test": "^1.40.1",
|
"@playwright/test": "^1.40.1",
|
||||||
"@storybook/addon-a11y": "^8.0.9",
|
"@storybook/addon-a11y": "^8.2.7",
|
||||||
"@storybook/addon-actions": "^8.0.9",
|
"@storybook/addon-actions": "^8.2.7",
|
||||||
"@storybook/addon-essentials": "^8.0.9",
|
"@storybook/addon-essentials": "^8.2.7",
|
||||||
"@storybook/addon-interactions": "^8.0.9",
|
"@storybook/addon-interactions": "^8.2.7",
|
||||||
"@storybook/addon-links": "^8.0.9",
|
"@storybook/addon-links": "^8.2.7",
|
||||||
"@storybook/blocks": "^8.0.2",
|
"@storybook/blocks": "^8.0.2",
|
||||||
"@storybook/components": "^8.0.2",
|
"@storybook/components": "^8.2.7",
|
||||||
"@storybook/csf": "^0.1.6",
|
"@storybook/csf": "^0.1.11",
|
||||||
"@storybook/icons": "^1.2.9",
|
"@storybook/icons": "^1.2.10",
|
||||||
"@storybook/manager-api": "^8.0.9",
|
"@storybook/manager-api": "^8.2.7",
|
||||||
"@storybook/react": "^8.0.9",
|
"@storybook/react": "^8.2.7",
|
||||||
"@storybook/react-vite": "^8.0.9",
|
"@storybook/react-vite": "^8.2.7",
|
||||||
"@storybook/theming": "^8.0.9",
|
"@storybook/theming": "^8.2.4",
|
||||||
"@testing-library/dom": "^10.1.0",
|
"@testing-library/dom": "^10.4.0",
|
||||||
"@testing-library/jest-dom": "^6.4.2",
|
"@testing-library/jest-dom": "^6.4.8",
|
||||||
"@testing-library/react": "^15.0.5",
|
"@testing-library/react": "^16.0.0",
|
||||||
"@testing-library/user-event": "^14.5.2",
|
"@testing-library/user-event": "^14.5.2",
|
||||||
"@types/child-process-promise": "^2.2.1",
|
"@types/child-process-promise": "^2.2.1",
|
||||||
"@types/d3": "^7.4.0",
|
"@types/d3": "^7.4.0",
|
||||||
@@ -2039,10 +2038,10 @@
|
|||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.12",
|
||||||
"@types/js-yaml": "^4.0.6",
|
"@types/js-yaml": "^4.0.6",
|
||||||
"@types/nanoid": "^3.0.0",
|
"@types/nanoid": "^3.0.0",
|
||||||
"@types/node": "18.18.*",
|
"@types/node": "20.14.*",
|
||||||
"@types/node-fetch": "^2.5.2",
|
"@types/node-fetch": "^2.5.2",
|
||||||
"@types/react": "^18.0.28",
|
"@types/react": "^18.3.1",
|
||||||
"@types/react-dom": "^18.2.18",
|
"@types/react-dom": "^18.3.0",
|
||||||
"@types/sarif": "^2.1.2",
|
"@types/sarif": "^2.1.2",
|
||||||
"@types/semver": "^7.5.8",
|
"@types/semver": "^7.5.8",
|
||||||
"@types/stream-json": "^1.7.1",
|
"@types/stream-json": "^1.7.1",
|
||||||
@@ -2066,15 +2065,15 @@
|
|||||||
"eslint-import-resolver-typescript": "^3.6.1",
|
"eslint-import-resolver-typescript": "^3.6.1",
|
||||||
"eslint-plugin-deprecation": "^2.0.0",
|
"eslint-plugin-deprecation": "^2.0.0",
|
||||||
"eslint-plugin-etc": "^2.0.2",
|
"eslint-plugin-etc": "^2.0.2",
|
||||||
"eslint-plugin-github": "^4.4.1",
|
"eslint-plugin-github": "^4.10.2",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint-plugin-import": "^2.29.1",
|
||||||
"eslint-plugin-jest-dom": "^5.2.0",
|
"eslint-plugin-jest-dom": "^5.2.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"eslint-plugin-react": "^7.34.1",
|
"eslint-plugin-react": "^7.34.1",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.2",
|
||||||
"eslint-plugin-storybook": "^0.8.0",
|
"eslint-plugin-storybook": "^0.8.0",
|
||||||
"glob": "^10.0.0",
|
"glob": "^10.0.0",
|
||||||
"gulp": "^4.0.2",
|
"gulp": "^5.0.0",
|
||||||
"gulp-esbuild": "^0.12.0",
|
"gulp-esbuild": "^0.12.0",
|
||||||
"gulp-replace": "^1.1.3",
|
"gulp-replace": "^1.1.3",
|
||||||
"gulp-typescript": "^5.0.1",
|
"gulp-typescript": "^5.0.1",
|
||||||
@@ -2084,19 +2083,20 @@
|
|||||||
"jest-runner-vscode": "^3.0.1",
|
"jest-runner-vscode": "^3.0.1",
|
||||||
"lint-staged": "^15.2.2",
|
"lint-staged": "^15.2.2",
|
||||||
"markdownlint-cli2": "^0.13.0",
|
"markdownlint-cli2": "^0.13.0",
|
||||||
"markdownlint-cli2-formatter-pretty": "^0.0.5",
|
"markdownlint-cli2-formatter-pretty": "^0.0.6",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"patch-package": "^8.0.0",
|
"patch-package": "^8.0.0",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"storybook": "^8.0.9",
|
"storybook": "^8.2.7",
|
||||||
"tar-stream": "^3.1.7",
|
"tar-stream": "^3.1.7",
|
||||||
"through2": "^4.0.2",
|
"through2": "^4.0.2",
|
||||||
"ts-jest": "^29.1.2",
|
"ts-jest": "^29.1.4",
|
||||||
"ts-json-schema-generator": "^2.0.1",
|
"ts-json-schema-generator": "^2.1.1",
|
||||||
"ts-node": "^10.7.0",
|
"ts-node": "^10.9.2",
|
||||||
"ts-unused-exports": "^10.0.0",
|
"ts-unused-exports": "^10.1.0",
|
||||||
"typescript": "^5.0.2",
|
"typescript": "^5.0.2",
|
||||||
"vite": "^5.2.6"
|
"vite": "^5.2.11",
|
||||||
|
"vite-node": "^1.5.3"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"./**/*.{json,css,scss}": [
|
"./**/*.{json,css,scss}": [
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ interface VersionResult {
|
|||||||
export interface CliFeatures {
|
export interface CliFeatures {
|
||||||
featuresInVersionResult?: boolean;
|
featuresInVersionResult?: boolean;
|
||||||
mrvaPackCreate?: boolean;
|
mrvaPackCreate?: boolean;
|
||||||
|
generateSummarySymbolMap?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VersionAndFeatures {
|
export interface VersionAndFeatures {
|
||||||
|
|||||||
@@ -1211,10 +1211,15 @@ export class CodeQLCliServer implements Disposable {
|
|||||||
outputPath: string,
|
outputPath: string,
|
||||||
endSummaryPath: string,
|
endSummaryPath: string,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
|
const supportsGenerateSummarySymbolMap =
|
||||||
|
await this.cliConstraints.supportsGenerateSummarySymbolMap();
|
||||||
const subcommandArgs = [
|
const subcommandArgs = [
|
||||||
"--format=text",
|
"--format=text",
|
||||||
`--end-summary=${endSummaryPath}`,
|
`--end-summary=${endSummaryPath}`,
|
||||||
"--sourcemap",
|
"--sourcemap",
|
||||||
|
...(supportsGenerateSummarySymbolMap
|
||||||
|
? ["--summary-symbol-map", "--minify-output"]
|
||||||
|
: []),
|
||||||
inputPath,
|
inputPath,
|
||||||
outputPath,
|
outputPath,
|
||||||
];
|
];
|
||||||
@@ -1750,14 +1755,6 @@ export class CodeQLCliServer implements Disposable {
|
|||||||
this._versionChangedListeners.forEach((listener) =>
|
this._versionChangedListeners.forEach((listener) =>
|
||||||
listener(newVersionAndFeatures),
|
listener(newVersionAndFeatures),
|
||||||
);
|
);
|
||||||
// this._version is only undefined upon config change, so we reset CLI-based context key only when necessary.
|
|
||||||
await this.app.commands.execute(
|
|
||||||
"setContext",
|
|
||||||
"codeql.supportsTrimCache",
|
|
||||||
newVersionAndFeatures.version.compare(
|
|
||||||
CliVersionConstraint.CLI_VERSION_WITH_TRIM_CACHE,
|
|
||||||
) >= 0,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._versionChangedListeners.forEach((listener) =>
|
this._versionChangedListeners.forEach((listener) =>
|
||||||
listener(undefined),
|
listener(undefined),
|
||||||
@@ -1912,13 +1909,7 @@ function shouldDebugCliServer() {
|
|||||||
export class CliVersionConstraint {
|
export class CliVersionConstraint {
|
||||||
// The oldest version of the CLI that we support. This is used to determine
|
// The oldest version of the CLI that we support. This is used to determine
|
||||||
// whether to show a warning about the CLI being too old on startup.
|
// whether to show a warning about the CLI being too old on startup.
|
||||||
public static OLDEST_SUPPORTED_CLI_VERSION = new SemVer("2.14.6");
|
public static OLDEST_SUPPORTED_CLI_VERSION = new SemVer("v2.15.5");
|
||||||
|
|
||||||
/**
|
|
||||||
* CLI version where the query server supports the `evaluation/trimCache` method
|
|
||||||
* with `codeql database cleanup --mode=trim` semantics.
|
|
||||||
*/
|
|
||||||
public static CLI_VERSION_WITH_TRIM_CACHE = new SemVer("2.15.1");
|
|
||||||
|
|
||||||
public static CLI_VERSION_WITHOUT_MRVA_EXTENSIBLE_PREDICATE_HACK = new SemVer(
|
public static CLI_VERSION_WITHOUT_MRVA_EXTENSIBLE_PREDICATE_HACK = new SemVer(
|
||||||
"2.16.1",
|
"2.16.1",
|
||||||
@@ -1953,4 +1944,8 @@ export class CliVersionConstraint {
|
|||||||
async supportsMrvaPackCreate(): Promise<boolean> {
|
async supportsMrvaPackCreate(): Promise<boolean> {
|
||||||
return (await this.cli.getFeatures()).mrvaPackCreate === true;
|
return (await this.cli.getFeatures()).mrvaPackCreate === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async supportsGenerateSummarySymbolMap(): Promise<boolean> {
|
||||||
|
return (await this.cli.getFeatures()).generateSummarySymbolMap === true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,4 +31,9 @@ export interface Credentials {
|
|||||||
* @returns An OAuth access token, or undefined.
|
* @returns An OAuth access token, or undefined.
|
||||||
*/
|
*/
|
||||||
getExistingAccessToken(): Promise<string | undefined>;
|
getExistingAccessToken(): Promise<string | undefined>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ID of the authentication provider to use.
|
||||||
|
*/
|
||||||
|
authProviderId: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,37 +29,45 @@ function validGitHubNwoOrOwner(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts an NWO from a GitHub URL.
|
* Extracts an NWO from a GitHub URL.
|
||||||
* @param githubUrl The GitHub repository URL
|
* @param repositoryUrl The GitHub repository URL
|
||||||
|
* @param githubUrl The URL of the GitHub instance
|
||||||
* @return The corresponding NWO, or undefined if the URL is not valid
|
* @return The corresponding NWO, or undefined if the URL is not valid
|
||||||
*/
|
*/
|
||||||
export function getNwoFromGitHubUrl(githubUrl: string): string | undefined {
|
export function getNwoFromGitHubUrl(
|
||||||
return getNwoOrOwnerFromGitHubUrl(githubUrl, "nwo");
|
repositoryUrl: string,
|
||||||
|
githubUrl: URL,
|
||||||
|
): string | undefined {
|
||||||
|
return getNwoOrOwnerFromGitHubUrl(repositoryUrl, githubUrl, "nwo");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts an owner from a GitHub URL.
|
* Extracts an owner from a GitHub URL.
|
||||||
* @param githubUrl The GitHub repository URL
|
* @param repositoryUrl The GitHub repository URL
|
||||||
|
* @param githubUrl The URL of the GitHub instance
|
||||||
* @return The corresponding Owner, or undefined if the URL is not valid
|
* @return The corresponding Owner, or undefined if the URL is not valid
|
||||||
*/
|
*/
|
||||||
export function getOwnerFromGitHubUrl(githubUrl: string): string | undefined {
|
export function getOwnerFromGitHubUrl(
|
||||||
return getNwoOrOwnerFromGitHubUrl(githubUrl, "owner");
|
repositoryUrl: string,
|
||||||
|
githubUrl: URL,
|
||||||
|
): string | undefined {
|
||||||
|
return getNwoOrOwnerFromGitHubUrl(repositoryUrl, githubUrl, "owner");
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNwoOrOwnerFromGitHubUrl(
|
function getNwoOrOwnerFromGitHubUrl(
|
||||||
githubUrl: string,
|
repositoryUrl: string,
|
||||||
|
githubUrl: URL,
|
||||||
kind: "owner" | "nwo",
|
kind: "owner" | "nwo",
|
||||||
): string | undefined {
|
): string | undefined {
|
||||||
|
const validHostnames = [githubUrl.hostname, `www.${githubUrl.hostname}`];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let paths: string[];
|
let paths: string[];
|
||||||
const urlElements = githubUrl.split("/");
|
const urlElements = repositoryUrl.split("/");
|
||||||
if (
|
if (validHostnames.includes(urlElements[0])) {
|
||||||
urlElements[0] === "github.com" ||
|
paths = repositoryUrl.split("/").slice(1);
|
||||||
urlElements[0] === "www.github.com"
|
|
||||||
) {
|
|
||||||
paths = githubUrl.split("/").slice(1);
|
|
||||||
} else {
|
} else {
|
||||||
const uri = new URL(githubUrl);
|
const uri = new URL(repositoryUrl);
|
||||||
if (uri.hostname !== "github.com" && uri.hostname !== "www.github.com") {
|
if (!validHostnames.includes(uri.hostname)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
paths = uri.pathname.split("/").filter((segment: string) => segment);
|
paths = uri.pathname.split("/").filter((segment: string) => segment);
|
||||||
|
|||||||
@@ -147,6 +147,21 @@ interface SetStateMsg {
|
|||||||
parsedResultSets: ParsedResultSets;
|
parsedResultSets: ParsedResultSets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UserSettings {
|
||||||
|
/** Whether to display links to the dataflow models that generated particular nodes in a flow path. */
|
||||||
|
shouldShowProvenance: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DEFAULT_USER_SETTINGS: UserSettings = {
|
||||||
|
shouldShowProvenance: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Message indicating that the user's configuration settings have changed. */
|
||||||
|
interface SetUserSettingsMsg {
|
||||||
|
t: "setUserSettings";
|
||||||
|
userSettings: UserSettings;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message indicating that the results view should display interpreted
|
* Message indicating that the results view should display interpreted
|
||||||
* results.
|
* results.
|
||||||
@@ -191,6 +206,7 @@ interface UntoggleShowProblemsMsg {
|
|||||||
export type IntoResultsViewMsg =
|
export type IntoResultsViewMsg =
|
||||||
| ResultsUpdatingMsg
|
| ResultsUpdatingMsg
|
||||||
| SetStateMsg
|
| SetStateMsg
|
||||||
|
| SetUserSettingsMsg
|
||||||
| ShowInterpretedPageMsg
|
| ShowInterpretedPageMsg
|
||||||
| NavigateMsg
|
| NavigateMsg
|
||||||
| UntoggleShowProblemsMsg;
|
| UntoggleShowProblemsMsg;
|
||||||
@@ -208,13 +224,15 @@ export type FromResultsViewMsg =
|
|||||||
| OpenFileMsg;
|
| OpenFileMsg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message from the results view to open a database source
|
* Message from the results view to open a source
|
||||||
* file at the provided location.
|
* file at the provided location.
|
||||||
*/
|
*/
|
||||||
interface ViewSourceFileMsg {
|
interface ViewSourceFileMsg {
|
||||||
t: "viewSourceFile";
|
t: "viewSourceFile";
|
||||||
loc: UrlValueResolvable;
|
loc: UrlValueResolvable;
|
||||||
databaseUri: string;
|
/** URI of the database whose source archive contains the file, or `undefined` to open a file from
|
||||||
|
* the local disk. The latter case is used for opening links to data extension model files. */
|
||||||
|
databaseUri: string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -341,7 +359,8 @@ interface ChangeCompareMessage {
|
|||||||
|
|
||||||
export type ToCompareViewMessage =
|
export type ToCompareViewMessage =
|
||||||
| SetComparisonQueryInfoMessage
|
| SetComparisonQueryInfoMessage
|
||||||
| SetComparisonsMessage;
|
| SetComparisonsMessage
|
||||||
|
| SetUserSettingsMsg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message to the compare view that sets the metadata of the compared queries.
|
* Message to the compare view that sets the metadata of the compared queries.
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
import type { Log, Tool } from "sarif";
|
import type { Log } from "sarif";
|
||||||
import { createReadStream } from "fs-extra";
|
import { createReadStream } from "fs-extra";
|
||||||
import { connectTo } from "stream-json/Assembler";
|
import { connectTo } from "stream-json/Assembler";
|
||||||
import { getErrorMessage } from "./helpers-pure";
|
import { getErrorMessage } from "./helpers-pure";
|
||||||
import { withParser } from "stream-json/filters/Pick";
|
import { withParser } from "stream-json/filters/Ignore";
|
||||||
|
|
||||||
const DUMMY_TOOL: Tool = { driver: { name: "" } };
|
|
||||||
|
|
||||||
export async function sarifParser(
|
export async function sarifParser(
|
||||||
interpretedResultsPath: string,
|
interpretedResultsPath: string,
|
||||||
): Promise<Log> {
|
): Promise<Log> {
|
||||||
try {
|
try {
|
||||||
// Parse the SARIF file into token streams, filtering out only the results array.
|
// Parse the SARIF file into token streams, filtering out some of the larger subtrees that we
|
||||||
|
// don't need.
|
||||||
const pipeline = createReadStream(interpretedResultsPath).pipe(
|
const pipeline = createReadStream(interpretedResultsPath).pipe(
|
||||||
withParser({ filter: "runs.0.results" }),
|
withParser({
|
||||||
|
// We don't need to run's `artifacts` property, nor the driver's `notifications` property.
|
||||||
|
filter: /^runs\.\d+\.(artifacts|tool\.driver\.notifications)/,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Creates JavaScript objects from the token stream
|
// Creates JavaScript objects from the token stream
|
||||||
@@ -38,15 +40,17 @@ export async function sarifParser(
|
|||||||
});
|
});
|
||||||
|
|
||||||
asm.on("done", (asm) => {
|
asm.on("done", (asm) => {
|
||||||
const log: Log = {
|
const log = asm.current;
|
||||||
version: "2.1.0",
|
|
||||||
runs: [
|
// Do some trivial validation. This isn't a full validation of the SARIF file, but it's at
|
||||||
{
|
// least enough to ensure that we're not trying to parse complete garbage later.
|
||||||
tool: DUMMY_TOOL,
|
if (log.runs === undefined || log.runs.length < 1) {
|
||||||
results: asm.current ?? [],
|
reject(
|
||||||
},
|
new Error(
|
||||||
],
|
"Invalid SARIF file: expecting at least one run with result.",
|
||||||
};
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
resolve(log);
|
resolve(log);
|
||||||
alreadyDone = true;
|
alreadyDone = true;
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import {
|
|||||||
// All path operations in this file must be on paths *within* the zip
|
// All path operations in this file must be on paths *within* the zip
|
||||||
// archive.
|
// archive.
|
||||||
import { posix } from "path";
|
import { posix } from "path";
|
||||||
|
import { DatabaseEventKind } from "../../databases/local-databases/database-events";
|
||||||
|
import type { DatabaseManager } from "../../databases/local-databases/database-manager";
|
||||||
|
|
||||||
const path = posix;
|
const path = posix;
|
||||||
|
|
||||||
@@ -242,15 +244,8 @@ export class ArchiveFileSystemProvider implements FileSystemProvider {
|
|||||||
|
|
||||||
root = new Directory("");
|
root = new Directory("");
|
||||||
|
|
||||||
constructor() {
|
flushCache(zipPath: string) {
|
||||||
// When a file system archive is removed from the workspace, we should
|
this.archives.delete(zipPath);
|
||||||
// also remove it from our cache.
|
|
||||||
workspace.onDidChangeWorkspaceFolders((event) => {
|
|
||||||
for (const removed of event.removed) {
|
|
||||||
const zipPath = removed.uri.fsPath;
|
|
||||||
this.archives.delete(zipPath);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// metadata
|
// metadata
|
||||||
@@ -366,15 +361,35 @@ export class ArchiveFileSystemProvider implements FileSystemProvider {
|
|||||||
*/
|
*/
|
||||||
export const zipArchiveScheme = "codeql-zip-archive";
|
export const zipArchiveScheme = "codeql-zip-archive";
|
||||||
|
|
||||||
export function activate(ctx: ExtensionContext) {
|
export function activate(ctx: ExtensionContext, dbm?: DatabaseManager) {
|
||||||
|
const afsp = new ArchiveFileSystemProvider();
|
||||||
|
if (dbm) {
|
||||||
|
ctx.subscriptions.push(
|
||||||
|
dbm.onDidChangeDatabaseItem(async ({ kind, item: db }) => {
|
||||||
|
if (kind === DatabaseEventKind.Remove) {
|
||||||
|
if (db?.sourceArchive) {
|
||||||
|
afsp.flushCache(db.sourceArchive.fsPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
ctx.subscriptions.push(
|
ctx.subscriptions.push(
|
||||||
workspace.registerFileSystemProvider(
|
// When a file system archive is removed from the workspace, we should
|
||||||
zipArchiveScheme,
|
// also remove it from our cache.
|
||||||
new ArchiveFileSystemProvider(),
|
workspace.onDidChangeWorkspaceFolders((event) => {
|
||||||
{
|
for (const removed of event.removed) {
|
||||||
isCaseSensitive: true,
|
const zipPath = removed.uri.fsPath;
|
||||||
isReadonly: true,
|
afsp.flushCache(zipPath);
|
||||||
},
|
}
|
||||||
),
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.subscriptions.push(
|
||||||
|
workspace.registerFileSystemProvider(zipArchiveScheme, afsp, {
|
||||||
|
isCaseSensitive: true,
|
||||||
|
isReadonly: true,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { authentication } from "vscode";
|
|||||||
import type { Octokit } from "@octokit/rest";
|
import type { Octokit } from "@octokit/rest";
|
||||||
import type { Credentials } from "../authentication";
|
import type { Credentials } from "../authentication";
|
||||||
import { AppOctokit } from "../octokit";
|
import { AppOctokit } from "../octokit";
|
||||||
|
import { hasGhecDrUri } from "../../config";
|
||||||
export const GITHUB_AUTH_PROVIDER_ID = "github";
|
import { getOctokitBaseUrl } from "./octokit";
|
||||||
|
|
||||||
// We need 'repo' scope for triggering workflows, 'gist' scope for exporting results to Gist,
|
// We need 'repo' scope for triggering workflows, 'gist' scope for exporting results to Gist,
|
||||||
// and 'read:packages' for reading private CodeQL packages.
|
// and 'read:packages' for reading private CodeQL packages.
|
||||||
@@ -16,30 +16,24 @@ const SCOPES = ["repo", "gist", "read:packages"];
|
|||||||
*/
|
*/
|
||||||
export class VSCodeCredentials implements Credentials {
|
export class VSCodeCredentials implements Credentials {
|
||||||
/**
|
/**
|
||||||
* A specific octokit to return, otherwise a new authenticated octokit will be created when needed.
|
* Creates or returns an instance of Octokit. The returned instance should
|
||||||
*/
|
* not be stored and reused, as it may become out-of-date with the current
|
||||||
private octokit: Octokit | undefined;
|
* authentication session.
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates or returns an instance of Octokit.
|
|
||||||
*
|
*
|
||||||
* @returns An instance of Octokit.
|
* @returns An instance of Octokit.
|
||||||
*/
|
*/
|
||||||
async getOctokit(): Promise<Octokit> {
|
async getOctokit(): Promise<Octokit> {
|
||||||
if (this.octokit) {
|
|
||||||
return this.octokit;
|
|
||||||
}
|
|
||||||
|
|
||||||
const accessToken = await this.getAccessToken();
|
const accessToken = await this.getAccessToken();
|
||||||
|
|
||||||
return new AppOctokit({
|
return new AppOctokit({
|
||||||
auth: accessToken,
|
auth: accessToken,
|
||||||
|
baseUrl: getOctokitBaseUrl(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAccessToken(): Promise<string> {
|
async getAccessToken(): Promise<string> {
|
||||||
const session = await authentication.getSession(
|
const session = await authentication.getSession(
|
||||||
GITHUB_AUTH_PROVIDER_ID,
|
this.authProviderId,
|
||||||
SCOPES,
|
SCOPES,
|
||||||
{ createIfNone: true },
|
{ createIfNone: true },
|
||||||
);
|
);
|
||||||
@@ -49,11 +43,18 @@ export class VSCodeCredentials implements Credentials {
|
|||||||
|
|
||||||
async getExistingAccessToken(): Promise<string | undefined> {
|
async getExistingAccessToken(): Promise<string | undefined> {
|
||||||
const session = await authentication.getSession(
|
const session = await authentication.getSession(
|
||||||
GITHUB_AUTH_PROVIDER_ID,
|
this.authProviderId,
|
||||||
SCOPES,
|
SCOPES,
|
||||||
{ createIfNone: false },
|
{ createIfNone: false },
|
||||||
);
|
);
|
||||||
|
|
||||||
return session?.accessToken;
|
return session?.accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get authProviderId(): string {
|
||||||
|
if (hasGhecDrUri()) {
|
||||||
|
return "github-enterprise";
|
||||||
|
}
|
||||||
|
return "github";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
extensions/ql-vscode/src/common/vscode/octokit.ts
Normal file
15
extensions/ql-vscode/src/common/vscode/octokit.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { getGitHubInstanceApiUrl } from "../../config";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Octokit base URL to use based on the GitHub instance URL.
|
||||||
|
*
|
||||||
|
* This is necessary because the Octokit base URL should not have a trailing
|
||||||
|
* slash, but this is included by default in a URL.
|
||||||
|
*/
|
||||||
|
export function getOctokitBaseUrl(): string {
|
||||||
|
let apiUrl = getGitHubInstanceApiUrl().toString();
|
||||||
|
if (apiUrl.endsWith("/")) {
|
||||||
|
apiUrl = apiUrl.slice(0, -1);
|
||||||
|
}
|
||||||
|
return apiUrl;
|
||||||
|
}
|
||||||
@@ -33,6 +33,14 @@ export interface ProgressUpdate {
|
|||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function progressUpdate(
|
||||||
|
step: number,
|
||||||
|
maxStep: number,
|
||||||
|
message: string,
|
||||||
|
): ProgressUpdate {
|
||||||
|
return { step, maxStep, message };
|
||||||
|
}
|
||||||
|
|
||||||
export type ProgressCallback = (p: ProgressUpdate) => void;
|
export type ProgressCallback = (p: ProgressUpdate) => void;
|
||||||
|
|
||||||
// Make certain properties within a type optional
|
// Make certain properties within a type optional
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import {
|
|||||||
getResultSetNames,
|
getResultSetNames,
|
||||||
} from "./result-set-names";
|
} from "./result-set-names";
|
||||||
import { compareInterpretedResults } from "./interpreted-results";
|
import { compareInterpretedResults } from "./interpreted-results";
|
||||||
|
import { isCanary } from "../config";
|
||||||
|
|
||||||
interface ComparePair {
|
interface ComparePair {
|
||||||
from: CompletedLocalQueryInfo;
|
from: CompletedLocalQueryInfo;
|
||||||
@@ -116,6 +117,13 @@ export class CompareView extends AbstractWebview<
|
|||||||
panel.reveal(undefined, true);
|
panel.reveal(undefined, true);
|
||||||
await this.waitForPanelLoaded();
|
await this.waitForPanelLoaded();
|
||||||
|
|
||||||
|
await this.postMessage({
|
||||||
|
t: "setUserSettings",
|
||||||
|
userSettings: {
|
||||||
|
shouldShowProvenance: isCanary(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
await this.postMessage({
|
await this.postMessage({
|
||||||
t: "setComparisonQueryInfo",
|
t: "setComparisonQueryInfo",
|
||||||
stats: {
|
stats: {
|
||||||
|
|||||||
@@ -108,12 +108,55 @@ export function hasEnterpriseUri(): boolean {
|
|||||||
return getEnterpriseUri() !== undefined;
|
return getEnterpriseUri() !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does the uri look like GHEC-DR?
|
||||||
|
*/
|
||||||
|
function isGhecDrUri(uri: Uri | undefined): boolean {
|
||||||
|
return uri !== undefined && uri.authority.toLowerCase().endsWith(".ghe.com");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is the GitHub Enterprise URI set to something that looks like GHEC-DR?
|
* Is the GitHub Enterprise URI set to something that looks like GHEC-DR?
|
||||||
*/
|
*/
|
||||||
export function hasGhecDrUri(): boolean {
|
export function hasGhecDrUri(): boolean {
|
||||||
const uri = getEnterpriseUri();
|
const uri = getEnterpriseUri();
|
||||||
return uri !== undefined && uri.authority.toLowerCase().endsWith(".ghe.com");
|
return isGhecDrUri(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The URI for GitHub.com.
|
||||||
|
*/
|
||||||
|
export const GITHUB_URL = new URL("https://github.com");
|
||||||
|
export const GITHUB_API_URL = new URL("https://api.github.com");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the GitHub Enterprise URI is set to something that looks like GHEC-DR, return it.
|
||||||
|
*/
|
||||||
|
export function getGhecDrUri(): Uri | undefined {
|
||||||
|
const uri = getEnterpriseUri();
|
||||||
|
if (isGhecDrUri(uri)) {
|
||||||
|
return uri;
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getGitHubInstanceUrl(): URL {
|
||||||
|
const ghecDrUri = getGhecDrUri();
|
||||||
|
if (ghecDrUri) {
|
||||||
|
return new URL(ghecDrUri.toString());
|
||||||
|
}
|
||||||
|
return GITHUB_URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getGitHubInstanceApiUrl(): URL {
|
||||||
|
const ghecDrUri = getGhecDrUri();
|
||||||
|
if (ghecDrUri) {
|
||||||
|
const url = new URL(ghecDrUri.toString());
|
||||||
|
url.hostname = `api.${url.hostname}`;
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
return GITHUB_API_URL;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ROOT_SETTING = new Setting("codeQL");
|
const ROOT_SETTING = new Setting("codeQL");
|
||||||
@@ -570,6 +613,11 @@ export async function setRemoteControllerRepo(repo: string | undefined) {
|
|||||||
export interface VariantAnalysisConfig {
|
export interface VariantAnalysisConfig {
|
||||||
controllerRepo: string | undefined;
|
controllerRepo: string | undefined;
|
||||||
showSystemDefinedRepositoryLists: boolean;
|
showSystemDefinedRepositoryLists: boolean;
|
||||||
|
/**
|
||||||
|
* This uses a URL instead of a URI because the URL class is available in
|
||||||
|
* unit tests and is fully browser-compatible.
|
||||||
|
*/
|
||||||
|
githubUrl: URL;
|
||||||
onDidChangeConfiguration?: Event<void>;
|
onDidChangeConfiguration?: Event<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -591,6 +639,10 @@ export class VariantAnalysisConfigListener
|
|||||||
public get showSystemDefinedRepositoryLists(): boolean {
|
public get showSystemDefinedRepositoryLists(): boolean {
|
||||||
return !hasEnterpriseUri();
|
return !hasEnterpriseUri();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get githubUrl(): URL {
|
||||||
|
return getGitHubInstanceUrl();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const VARIANT_ANALYSIS_FILTER_RESULTS = new Setting(
|
const VARIANT_ANALYSIS_FILTER_RESULTS = new Setting(
|
||||||
@@ -628,11 +680,6 @@ export function getVariantAnalysisDefaultResultsSort(): SortKey {
|
|||||||
*/
|
*/
|
||||||
const ACTION_BRANCH = new Setting("actionBranch", VARIANT_ANALYSIS_SETTING);
|
const ACTION_BRANCH = new Setting("actionBranch", VARIANT_ANALYSIS_SETTING);
|
||||||
|
|
||||||
export const VARIANT_ANALYSIS_ENABLE_GHEC_DR = new Setting(
|
|
||||||
"enableGhecDr",
|
|
||||||
VARIANT_ANALYSIS_SETTING,
|
|
||||||
);
|
|
||||||
|
|
||||||
export function getActionBranch(): string {
|
export function getActionBranch(): string {
|
||||||
return ACTION_BRANCH.getValue<string>() || "main";
|
return ACTION_BRANCH.getValue<string>() || "main";
|
||||||
}
|
}
|
||||||
@@ -793,7 +840,6 @@ const LLM_GENERATION_DEV_ENDPOINT = new Setting(
|
|||||||
const MODEL_EVALUATION = new Setting("evaluation", MODEL_SETTING);
|
const MODEL_EVALUATION = new Setting("evaluation", MODEL_SETTING);
|
||||||
const MODEL_PACK_LOCATION = new Setting("packLocation", MODEL_SETTING);
|
const MODEL_PACK_LOCATION = new Setting("packLocation", MODEL_SETTING);
|
||||||
const MODEL_PACK_NAME = new Setting("packName", MODEL_SETTING);
|
const MODEL_PACK_NAME = new Setting("packName", MODEL_SETTING);
|
||||||
const ENABLE_PYTHON = new Setting("enablePython", MODEL_SETTING);
|
|
||||||
|
|
||||||
export type ModelConfigPackVariables = {
|
export type ModelConfigPackVariables = {
|
||||||
database: string;
|
database: string;
|
||||||
@@ -810,7 +856,6 @@ export interface ModelConfig {
|
|||||||
variables: ModelConfigPackVariables,
|
variables: ModelConfigPackVariables,
|
||||||
): string;
|
): string;
|
||||||
getPackName(languageId: string, variables: ModelConfigPackVariables): string;
|
getPackName(languageId: string, variables: ModelConfigPackVariables): string;
|
||||||
enablePython: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ModelConfigListener extends ConfigListener implements ModelConfig {
|
export class ModelConfigListener extends ConfigListener implements ModelConfig {
|
||||||
@@ -872,10 +917,6 @@ export class ModelConfigListener extends ConfigListener implements ModelConfig {
|
|||||||
variables,
|
variables,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get enablePython(): boolean {
|
|
||||||
return !!ENABLE_PYTHON.getValue<boolean>();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const GITHUB_DATABASE_SETTING = new Setting("githubDatabase", ROOT_SETTING);
|
const GITHUB_DATABASE_SETTING = new Setting("githubDatabase", ROOT_SETTING);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { AppOctokit } from "../common/octokit";
|
|||||||
import type { ProgressCallback } from "../common/vscode/progress";
|
import type { ProgressCallback } from "../common/vscode/progress";
|
||||||
import { UserCancellationException } from "../common/vscode/progress";
|
import { UserCancellationException } from "../common/vscode/progress";
|
||||||
import type { EndpointDefaults } from "@octokit/types";
|
import type { EndpointDefaults } from "@octokit/types";
|
||||||
|
import { getOctokitBaseUrl } from "../common/vscode/octokit";
|
||||||
|
|
||||||
export async function getCodeSearchRepositories(
|
export async function getCodeSearchRepositories(
|
||||||
query: string,
|
query: string,
|
||||||
@@ -54,6 +55,7 @@ async function provideOctokitWithThrottling(
|
|||||||
|
|
||||||
const octokit = new MyOctokit({
|
const octokit = new MyOctokit({
|
||||||
auth,
|
auth,
|
||||||
|
baseUrl: getOctokitBaseUrl(),
|
||||||
throttle: {
|
throttle: {
|
||||||
onRateLimit: (retryAfter: number, options: EndpointDefaults): boolean => {
|
onRateLimit: (retryAfter: number, options: EndpointDefaults): boolean => {
|
||||||
void logger.log(
|
void logger.log(
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ import {
|
|||||||
addDatabaseSourceToWorkspace,
|
addDatabaseSourceToWorkspace,
|
||||||
allowHttp,
|
allowHttp,
|
||||||
downloadTimeout,
|
downloadTimeout,
|
||||||
|
getGitHubInstanceUrl,
|
||||||
|
hasGhecDrUri,
|
||||||
isCanary,
|
isCanary,
|
||||||
} from "../config";
|
} from "../config";
|
||||||
import { showAndLogInformationMessage } from "../common/logging";
|
import { showAndLogInformationMessage } from "../common/logging";
|
||||||
@@ -150,10 +152,11 @@ export class DatabaseFetcher {
|
|||||||
maxStep: 2,
|
maxStep: 2,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const instanceUrl = getGitHubInstanceUrl();
|
||||||
|
|
||||||
const options: InputBoxOptions = {
|
const options: InputBoxOptions = {
|
||||||
title:
|
title: `Enter a GitHub repository URL or "name with owner" (e.g. ${new URL("/github/codeql", instanceUrl).toString()} or github/codeql)`,
|
||||||
'Enter a GitHub repository URL or "name with owner" (e.g. https://github.com/github/codeql or github/codeql)',
|
placeHolder: `${new URL("/", instanceUrl).toString()}<owner>/<repo> or <owner>/<repo>`,
|
||||||
placeHolder: "https://github.com/<owner>/<repo> or <owner>/<repo>",
|
|
||||||
ignoreFocusOut: true,
|
ignoreFocusOut: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -180,12 +183,14 @@ export class DatabaseFetcher {
|
|||||||
makeSelected = true,
|
makeSelected = true,
|
||||||
addSourceArchiveFolder = addDatabaseSourceToWorkspace(),
|
addSourceArchiveFolder = addDatabaseSourceToWorkspace(),
|
||||||
): Promise<DatabaseItem | undefined> {
|
): Promise<DatabaseItem | undefined> {
|
||||||
const nwo = getNwoFromGitHubUrl(githubRepo) || githubRepo;
|
const nwo =
|
||||||
|
getNwoFromGitHubUrl(githubRepo, getGitHubInstanceUrl()) || githubRepo;
|
||||||
if (!isValidGitHubNwo(nwo)) {
|
if (!isValidGitHubNwo(nwo)) {
|
||||||
throw new Error(`Invalid GitHub repository: ${githubRepo}`);
|
throw new Error(`Invalid GitHub repository: ${githubRepo}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const credentials = isCanary() ? this.app.credentials : undefined;
|
const credentials =
|
||||||
|
isCanary() || hasGhecDrUri() ? this.app.credentials : undefined;
|
||||||
|
|
||||||
const octokit = credentials
|
const octokit = credentials
|
||||||
? await credentials.getOctokit()
|
? await credentials.getOctokit()
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { Octokit } from "@octokit/rest";
|
|||||||
import type { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods";
|
import type { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods";
|
||||||
import { showNeverAskAgainDialog } from "../../common/vscode/dialog";
|
import { showNeverAskAgainDialog } from "../../common/vscode/dialog";
|
||||||
import type { GitHubDatabaseConfig } from "../../config";
|
import type { GitHubDatabaseConfig } from "../../config";
|
||||||
|
import { hasGhecDrUri } from "../../config";
|
||||||
import type { Credentials } from "../../common/authentication";
|
import type { Credentials } from "../../common/authentication";
|
||||||
import { AppOctokit } from "../../common/octokit";
|
import { AppOctokit } from "../../common/octokit";
|
||||||
import type { ProgressCallback } from "../../common/vscode/progress";
|
import type { ProgressCallback } from "../../common/vscode/progress";
|
||||||
@@ -67,7 +68,10 @@ export async function listDatabases(
|
|||||||
credentials: Credentials,
|
credentials: Credentials,
|
||||||
config: GitHubDatabaseConfig,
|
config: GitHubDatabaseConfig,
|
||||||
): Promise<ListDatabasesResult | undefined> {
|
): Promise<ListDatabasesResult | undefined> {
|
||||||
const hasAccessToken = !!(await credentials.getExistingAccessToken());
|
// On GHEC-DR, unauthenticated requests will never work, so we should always ask
|
||||||
|
// for authentication.
|
||||||
|
const hasAccessToken =
|
||||||
|
!!(await credentials.getExistingAccessToken()) || hasGhecDrUri();
|
||||||
|
|
||||||
let octokit = hasAccessToken
|
let octokit = hasAccessToken
|
||||||
? await credentials.getOctokit()
|
? await credentials.getOctokit()
|
||||||
|
|||||||
@@ -109,9 +109,8 @@ class DatabaseTreeDataProvider
|
|||||||
// Note that events from the database manager are instances of DatabaseChangedEvent
|
// Note that events from the database manager are instances of DatabaseChangedEvent
|
||||||
// and events fired by the UI are instances of DatabaseItem
|
// and events fired by the UI are instances of DatabaseItem
|
||||||
|
|
||||||
// When event.item is undefined, then the entire tree is refreshed.
|
// When a full refresh has occurred, then all items are refreshed by passing undefined.
|
||||||
// When event.item is a db item, then only that item is refreshed.
|
this._onDidChangeTreeData.fire(event.fullRefresh ? undefined : event.item);
|
||||||
this._onDidChangeTreeData.fire(event.item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleDidChangeCurrentDatabaseItem(
|
private handleDidChangeCurrentDatabaseItem(
|
||||||
|
|||||||
@@ -16,4 +16,8 @@ export enum DatabaseEventKind {
|
|||||||
export interface DatabaseChangedEvent {
|
export interface DatabaseChangedEvent {
|
||||||
kind: DatabaseEventKind;
|
kind: DatabaseEventKind;
|
||||||
item: DatabaseItem | undefined;
|
item: DatabaseItem | undefined;
|
||||||
|
// If true, event handlers should consider the database manager
|
||||||
|
// to have been fully refreshed. Any state managed by the
|
||||||
|
// event handler should be fully refreshed as well.
|
||||||
|
fullRefresh: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -613,6 +613,7 @@ export class DatabaseManager extends DisposableObject {
|
|||||||
this._onDidChangeCurrentDatabaseItem.fire({
|
this._onDidChangeCurrentDatabaseItem.fire({
|
||||||
item,
|
item,
|
||||||
kind: DatabaseEventKind.Change,
|
kind: DatabaseEventKind.Change,
|
||||||
|
fullRefresh: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -662,8 +663,9 @@ export class DatabaseManager extends DisposableObject {
|
|||||||
}
|
}
|
||||||
// note that we use undefined as the item in order to reset the entire tree
|
// note that we use undefined as the item in order to reset the entire tree
|
||||||
this._onDidChangeDatabaseItem.fire({
|
this._onDidChangeDatabaseItem.fire({
|
||||||
item: undefined,
|
item,
|
||||||
kind: DatabaseEventKind.Add,
|
kind: DatabaseEventKind.Add,
|
||||||
|
fullRefresh: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -671,9 +673,9 @@ export class DatabaseManager extends DisposableObject {
|
|||||||
item.name = newName;
|
item.name = newName;
|
||||||
await this.updatePersistedDatabaseList();
|
await this.updatePersistedDatabaseList();
|
||||||
this._onDidChangeDatabaseItem.fire({
|
this._onDidChangeDatabaseItem.fire({
|
||||||
// pass undefined so that the entire tree is rebuilt in order to re-sort
|
item,
|
||||||
item: undefined,
|
|
||||||
kind: DatabaseEventKind.Rename,
|
kind: DatabaseEventKind.Rename,
|
||||||
|
fullRefresh: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -720,10 +722,10 @@ export class DatabaseManager extends DisposableObject {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// note that we use undefined as the item in order to reset the entire tree
|
|
||||||
this._onDidChangeDatabaseItem.fire({
|
this._onDidChangeDatabaseItem.fire({
|
||||||
item: undefined,
|
item,
|
||||||
kind: DatabaseEventKind.Remove,
|
kind: DatabaseEventKind.Remove,
|
||||||
|
fullRefresh: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -776,6 +778,7 @@ export class DatabaseManager extends DisposableObject {
|
|||||||
this._onDidChangeDatabaseItem.fire({
|
this._onDidChangeDatabaseItem.fire({
|
||||||
kind: DatabaseEventKind.Refresh,
|
kind: DatabaseEventKind.Refresh,
|
||||||
item: databaseItem,
|
item: databaseItem,
|
||||||
|
fullRefresh: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,11 +37,12 @@ export const shownLocationLineDecoration =
|
|||||||
/**
|
/**
|
||||||
* Resolves the specified CodeQL location to a URI into the source archive.
|
* Resolves the specified CodeQL location to a URI into the source archive.
|
||||||
* @param loc CodeQL location to resolve. Must have a non-empty value for `loc.file`.
|
* @param loc CodeQL location to resolve. Must have a non-empty value for `loc.file`.
|
||||||
* @param databaseItem Database in which to resolve the file location.
|
* @param databaseItem Database in which to resolve the file location, or `undefined` to resolve
|
||||||
|
* from the local file system.
|
||||||
*/
|
*/
|
||||||
function resolveFivePartLocation(
|
function resolveFivePartLocation(
|
||||||
loc: UrlValueLineColumnLocation,
|
loc: UrlValueLineColumnLocation,
|
||||||
databaseItem: DatabaseItem,
|
databaseItem: DatabaseItem | undefined,
|
||||||
): Location {
|
): Location {
|
||||||
// `Range` is a half-open interval, and is zero-based. CodeQL locations are closed intervals, and
|
// `Range` is a half-open interval, and is zero-based. CodeQL locations are closed intervals, and
|
||||||
// are one-based. Adjust accordingly.
|
// are one-based. Adjust accordingly.
|
||||||
@@ -52,7 +53,10 @@ function resolveFivePartLocation(
|
|||||||
Math.max(1, loc.endColumn),
|
Math.max(1, loc.endColumn),
|
||||||
);
|
);
|
||||||
|
|
||||||
return new Location(databaseItem.resolveSourceFile(loc.uri), range);
|
return new Location(
|
||||||
|
databaseItem?.resolveSourceFile(loc.uri) ?? Uri.parse(loc.uri),
|
||||||
|
range,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,22 +66,26 @@ function resolveFivePartLocation(
|
|||||||
*/
|
*/
|
||||||
function resolveWholeFileLocation(
|
function resolveWholeFileLocation(
|
||||||
loc: UrlValueWholeFileLocation,
|
loc: UrlValueWholeFileLocation,
|
||||||
databaseItem: DatabaseItem,
|
databaseItem: DatabaseItem | undefined,
|
||||||
): Location {
|
): Location {
|
||||||
// A location corresponding to the start of the file.
|
// A location corresponding to the start of the file.
|
||||||
const range = new Range(0, 0, 0, 0);
|
const range = new Range(0, 0, 0, 0);
|
||||||
return new Location(databaseItem.resolveSourceFile(loc.uri), range);
|
return new Location(
|
||||||
|
databaseItem?.resolveSourceFile(loc.uri) ?? Uri.parse(loc.uri),
|
||||||
|
range,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to resolve the specified CodeQL location to a URI into the source archive. If no exact location
|
* Try to resolve the specified CodeQL location to a URI into the source archive. If no exact location
|
||||||
* can be resolved, returns `undefined`.
|
* can be resolved, returns `undefined`.
|
||||||
* @param loc CodeQL location to resolve
|
* @param loc CodeQL location to resolve
|
||||||
* @param databaseItem Database in which to resolve the file location.
|
* @param databaseItem Database in which to resolve the file location, or `undefined` to resolve
|
||||||
|
* from the local file system.
|
||||||
*/
|
*/
|
||||||
export function tryResolveLocation(
|
export function tryResolveLocation(
|
||||||
loc: UrlValueResolvable | undefined,
|
loc: UrlValueResolvable | undefined,
|
||||||
databaseItem: DatabaseItem,
|
databaseItem: DatabaseItem | undefined,
|
||||||
): Location | undefined {
|
): Location | undefined {
|
||||||
if (!loc) {
|
if (!loc) {
|
||||||
return;
|
return;
|
||||||
@@ -95,7 +103,7 @@ export function tryResolveLocation(
|
|||||||
|
|
||||||
export async function showResolvableLocation(
|
export async function showResolvableLocation(
|
||||||
loc: UrlValueResolvable,
|
loc: UrlValueResolvable,
|
||||||
databaseItem: DatabaseItem,
|
databaseItem: DatabaseItem | undefined,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
@@ -151,13 +159,14 @@ export async function showLocation(location?: Location) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function jumpToLocation(
|
export async function jumpToLocation(
|
||||||
databaseUri: string,
|
databaseUri: string | undefined,
|
||||||
loc: UrlValueResolvable,
|
loc: UrlValueResolvable,
|
||||||
databaseManager: DatabaseManager,
|
databaseManager: DatabaseManager,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
) {
|
) {
|
||||||
const databaseItem = databaseManager.findDatabaseItem(Uri.parse(databaseUri));
|
const databaseItem =
|
||||||
if (databaseItem !== undefined) {
|
databaseUri !== undefined
|
||||||
await showResolvableLocation(loc, databaseItem, logger);
|
? databaseManager.findDatabaseItem(Uri.parse(databaseUri))
|
||||||
}
|
: undefined;
|
||||||
|
await showResolvableLocation(loc, databaseItem, logger);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import type { App } from "../../common/app";
|
|||||||
import { QueryLanguage } from "../../common/query-language";
|
import { QueryLanguage } from "../../common/query-language";
|
||||||
import { getCodeSearchRepositories } from "../code-search-api";
|
import { getCodeSearchRepositories } from "../code-search-api";
|
||||||
import { showAndLogErrorMessage } from "../../common/logging";
|
import { showAndLogErrorMessage } from "../../common/logging";
|
||||||
|
import { getGitHubInstanceUrl } from "../../config";
|
||||||
|
|
||||||
export interface RemoteDatabaseQuickPickItem extends QuickPickItem {
|
export interface RemoteDatabaseQuickPickItem extends QuickPickItem {
|
||||||
remoteDatabaseKind: string;
|
remoteDatabaseKind: string;
|
||||||
@@ -146,16 +147,19 @@ export class DbPanel extends DisposableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async addNewRemoteRepo(parentList?: string): Promise<void> {
|
private async addNewRemoteRepo(parentList?: string): Promise<void> {
|
||||||
|
const instanceUrl = getGitHubInstanceUrl();
|
||||||
|
|
||||||
const repoName = await window.showInputBox({
|
const repoName = await window.showInputBox({
|
||||||
title: "Add a repository",
|
title: "Add a repository",
|
||||||
prompt: "Insert a GitHub repository URL or name with owner",
|
prompt: "Insert a GitHub repository URL or name with owner",
|
||||||
placeHolder: "<owner>/<repo> or https://github.com/<owner>/<repo>",
|
placeHolder: `<owner>/<repo> or ${new URL("/", instanceUrl).toString()}<owner>/<repo>`,
|
||||||
});
|
});
|
||||||
if (!repoName) {
|
if (!repoName) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nwo = getNwoFromGitHubUrl(repoName) || repoName;
|
const nwo =
|
||||||
|
getNwoFromGitHubUrl(repoName, getGitHubInstanceUrl()) || repoName;
|
||||||
if (!isValidGitHubNwo(nwo)) {
|
if (!isValidGitHubNwo(nwo)) {
|
||||||
void showAndLogErrorMessage(
|
void showAndLogErrorMessage(
|
||||||
this.app.logger,
|
this.app.logger,
|
||||||
@@ -176,17 +180,20 @@ export class DbPanel extends DisposableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async addNewRemoteOwner(): Promise<void> {
|
private async addNewRemoteOwner(): Promise<void> {
|
||||||
|
const instanceUrl = getGitHubInstanceUrl();
|
||||||
|
|
||||||
const ownerName = await window.showInputBox({
|
const ownerName = await window.showInputBox({
|
||||||
title: "Add all repositories of a GitHub org or owner",
|
title: "Add all repositories of a GitHub org or owner",
|
||||||
prompt: "Insert a GitHub organization or owner name",
|
prompt: "Insert a GitHub organization or owner name",
|
||||||
placeHolder: "<owner> or https://github.com/<owner>",
|
placeHolder: `<owner> or ${new URL("/", instanceUrl).toString()}<owner>`,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!ownerName) {
|
if (!ownerName) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const owner = getOwnerFromGitHubUrl(ownerName) || ownerName;
|
const owner =
|
||||||
|
getOwnerFromGitHubUrl(ownerName, getGitHubInstanceUrl()) || ownerName;
|
||||||
if (!isValidGitHubOwner(owner)) {
|
if (!isValidGitHubOwner(owner)) {
|
||||||
void showAndLogErrorMessage(
|
void showAndLogErrorMessage(
|
||||||
this.app.logger,
|
this.app.logger,
|
||||||
@@ -411,7 +418,7 @@ export class DbPanel extends DisposableObject {
|
|||||||
if (treeViewItem.dbItem === undefined) {
|
if (treeViewItem.dbItem === undefined) {
|
||||||
throw new Error("Unable to open on GitHub. Please select a valid item.");
|
throw new Error("Unable to open on GitHub. Please select a valid item.");
|
||||||
}
|
}
|
||||||
const githubUrl = getGitHubUrl(treeViewItem.dbItem);
|
const githubUrl = getGitHubUrl(treeViewItem.dbItem, getGitHubInstanceUrl());
|
||||||
if (!githubUrl) {
|
if (!githubUrl) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Unable to open on GitHub. Please select a variant analysis repository or owner.",
|
"Unable to open on GitHub. Please select a variant analysis repository or owner.",
|
||||||
|
|||||||
@@ -62,12 +62,15 @@ function canImportCodeSearch(dbItem: DbItem): boolean {
|
|||||||
return DbItemKind.RemoteUserDefinedList === dbItem.kind;
|
return DbItemKind.RemoteUserDefinedList === dbItem.kind;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getGitHubUrl(dbItem: DbItem): string | undefined {
|
export function getGitHubUrl(
|
||||||
|
dbItem: DbItem,
|
||||||
|
githubUrl: URL,
|
||||||
|
): string | undefined {
|
||||||
switch (dbItem.kind) {
|
switch (dbItem.kind) {
|
||||||
case DbItemKind.RemoteOwner:
|
case DbItemKind.RemoteOwner:
|
||||||
return `https://github.com/${dbItem.ownerName}`;
|
return new URL(`/${dbItem.ownerName}`, githubUrl).toString();
|
||||||
case DbItemKind.RemoteRepo:
|
case DbItemKind.RemoteRepo:
|
||||||
return `https://github.com/${dbItem.repoFullName}`;
|
return new URL(`/${dbItem.repoFullName}`, githubUrl).toString();
|
||||||
default:
|
default:
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ class QLDebugAdapterTracker
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (this.localQueryRun !== undefined) {
|
if (this.localQueryRun !== undefined) {
|
||||||
const results: CoreQueryResults = body;
|
const results: CoreQueryResults = body;
|
||||||
await this.localQueryRun.complete(results);
|
await this.localQueryRun.complete(results, (_) => {});
|
||||||
this.localQueryRun = undefined;
|
this.localQueryRun = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import {
|
|||||||
joinOrderWarningThreshold,
|
joinOrderWarningThreshold,
|
||||||
QueryHistoryConfigListener,
|
QueryHistoryConfigListener,
|
||||||
QueryServerConfigListener,
|
QueryServerConfigListener,
|
||||||
|
VariantAnalysisConfigListener,
|
||||||
} from "./config";
|
} from "./config";
|
||||||
import {
|
import {
|
||||||
AstViewer,
|
AstViewer,
|
||||||
@@ -446,8 +447,9 @@ export async function activate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
CliVersionConstraint.OLDEST_SUPPORTED_CLI_VERSION.compare(ver.version) <
|
CliVersionConstraint.OLDEST_SUPPORTED_CLI_VERSION.compare(
|
||||||
0
|
ver.version,
|
||||||
|
) <= 0
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -715,12 +717,13 @@ async function installOrUpdateThenTryActivate(
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PACK_GLOBS = [
|
const CLEAR_PACK_CACHE_ON_EDIT_GLOBS = [
|
||||||
"**/codeql-pack.yml",
|
"**/codeql-pack.yml",
|
||||||
"**/qlpack.yml",
|
"**/qlpack.yml",
|
||||||
"**/queries.xml",
|
"**/queries.xml",
|
||||||
"**/codeql-pack.lock.yml",
|
"**/codeql-pack.lock.yml",
|
||||||
"**/qlpack.lock.yml",
|
"**/qlpack.lock.yml",
|
||||||
|
"**/*.dbscheme",
|
||||||
".codeqlmanifest.json",
|
".codeqlmanifest.json",
|
||||||
"codeql-workspace.yml",
|
"codeql-workspace.yml",
|
||||||
];
|
];
|
||||||
@@ -767,7 +770,7 @@ async function activateWithInstalledDistribution(
|
|||||||
ctx,
|
ctx,
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const glob of PACK_GLOBS) {
|
for (const glob of CLEAR_PACK_CACHE_ON_EDIT_GLOBS) {
|
||||||
const fsWatcher = workspace.createFileSystemWatcher(glob);
|
const fsWatcher = workspace.createFileSystemWatcher(glob);
|
||||||
ctx.subscriptions.push(fsWatcher);
|
ctx.subscriptions.push(fsWatcher);
|
||||||
|
|
||||||
@@ -864,8 +867,10 @@ async function activateWithInstalledDistribution(
|
|||||||
"variant-analyses",
|
"variant-analyses",
|
||||||
);
|
);
|
||||||
await ensureDir(variantAnalysisStorageDir);
|
await ensureDir(variantAnalysisStorageDir);
|
||||||
|
const variantAnalysisConfig = new VariantAnalysisConfigListener();
|
||||||
const variantAnalysisResultsManager = new VariantAnalysisResultsManager(
|
const variantAnalysisResultsManager = new VariantAnalysisResultsManager(
|
||||||
cliServer,
|
cliServer,
|
||||||
|
variantAnalysisConfig,
|
||||||
extLogger,
|
extLogger,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -875,6 +880,7 @@ async function activateWithInstalledDistribution(
|
|||||||
variantAnalysisStorageDir,
|
variantAnalysisStorageDir,
|
||||||
variantAnalysisResultsManager,
|
variantAnalysisResultsManager,
|
||||||
dbModule.dbManager,
|
dbModule.dbManager,
|
||||||
|
variantAnalysisConfig,
|
||||||
);
|
);
|
||||||
ctx.subscriptions.push(variantAnalysisManager);
|
ctx.subscriptions.push(variantAnalysisManager);
|
||||||
ctx.subscriptions.push(variantAnalysisResultsManager);
|
ctx.subscriptions.push(variantAnalysisResultsManager);
|
||||||
@@ -942,7 +948,7 @@ async function activateWithInstalledDistribution(
|
|||||||
ctx.subscriptions.push(compareView);
|
ctx.subscriptions.push(compareView);
|
||||||
|
|
||||||
void extLogger.log("Initializing source archive filesystem provider.");
|
void extLogger.log("Initializing source archive filesystem provider.");
|
||||||
archiveFilesystemProvider_activate(ctx);
|
archiveFilesystemProvider_activate(ctx, dbm);
|
||||||
|
|
||||||
const qhelpTmpDir = dirSync({
|
const qhelpTmpDir = dirSync({
|
||||||
prefix: "qhelp_",
|
prefix: "qhelp_",
|
||||||
|
|||||||
@@ -485,7 +485,7 @@ export class LocalQueries extends DisposableObject {
|
|||||||
localQueryRun.logger,
|
localQueryRun.logger,
|
||||||
);
|
);
|
||||||
|
|
||||||
await localQueryRun.complete(results);
|
await localQueryRun.complete(results, progress);
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import type { LocalQueries } from "./local-queries";
|
|||||||
import { tryGetQueryMetadata } from "../codeql-cli/query-metadata";
|
import { tryGetQueryMetadata } from "../codeql-cli/query-metadata";
|
||||||
import { telemetryListener } from "../common/vscode/telemetry";
|
import { telemetryListener } from "../common/vscode/telemetry";
|
||||||
import type { Disposable } from "../common/disposable-object";
|
import type { Disposable } from "../common/disposable-object";
|
||||||
|
import type { ProgressCallback } from "../common/vscode/progress";
|
||||||
|
import { progressUpdate } from "../common/vscode/progress";
|
||||||
|
|
||||||
function formatResultMessage(result: CoreQueryResults): string {
|
function formatResultMessage(result: CoreQueryResults): string {
|
||||||
switch (result.resultType) {
|
switch (result.resultType) {
|
||||||
@@ -79,23 +81,31 @@ export class LocalQueryRun {
|
|||||||
* This function must be called when the evaluation completes, whether the evaluation was
|
* This function must be called when the evaluation completes, whether the evaluation was
|
||||||
* successful or not.
|
* successful or not.
|
||||||
* */
|
* */
|
||||||
public async complete(results: CoreQueryResults): Promise<void> {
|
public async complete(
|
||||||
|
results: CoreQueryResults,
|
||||||
|
progress: ProgressCallback,
|
||||||
|
): Promise<void> {
|
||||||
const evalLogPaths = await this.summarizeEvalLog(
|
const evalLogPaths = await this.summarizeEvalLog(
|
||||||
results.resultType,
|
results.resultType,
|
||||||
this.outputDir,
|
this.outputDir,
|
||||||
this.logger,
|
this.logger,
|
||||||
|
progress,
|
||||||
);
|
);
|
||||||
if (evalLogPaths !== undefined) {
|
if (evalLogPaths !== undefined) {
|
||||||
this.queryInfo.setEvaluatorLogPaths(evalLogPaths);
|
this.queryInfo.setEvaluatorLogPaths(evalLogPaths);
|
||||||
}
|
}
|
||||||
|
progress(progressUpdate(1, 4, "Getting completed query info"));
|
||||||
const queryWithResults = await this.getCompletedQueryInfo(results);
|
const queryWithResults = await this.getCompletedQueryInfo(results);
|
||||||
|
progress(progressUpdate(2, 4, "Updating query history"));
|
||||||
this.queryHistoryManager.completeQuery(this.queryInfo, queryWithResults);
|
this.queryHistoryManager.completeQuery(this.queryInfo, queryWithResults);
|
||||||
|
progress(progressUpdate(3, 4, "Showing results"));
|
||||||
await this.localQueries.showResultsForCompletedQuery(
|
await this.localQueries.showResultsForCompletedQuery(
|
||||||
this.queryInfo as CompletedLocalQueryInfo,
|
this.queryInfo as CompletedLocalQueryInfo,
|
||||||
WebviewReveal.Forced,
|
WebviewReveal.Forced,
|
||||||
);
|
);
|
||||||
// Note we must update the query history view after showing results as the
|
// Note we must update the query history view after showing results as the
|
||||||
// display and sorting might depend on the number of results
|
// display and sorting might depend on the number of results
|
||||||
|
progress(progressUpdate(4, 4, "Updating query history"));
|
||||||
await this.queryHistoryManager.refreshTreeView();
|
await this.queryHistoryManager.refreshTreeView();
|
||||||
|
|
||||||
this.logger.dispose();
|
this.logger.dispose();
|
||||||
@@ -109,6 +119,7 @@ export class LocalQueryRun {
|
|||||||
QueryResultType.OTHER_ERROR,
|
QueryResultType.OTHER_ERROR,
|
||||||
this.outputDir,
|
this.outputDir,
|
||||||
this.logger,
|
this.logger,
|
||||||
|
(_) => {},
|
||||||
);
|
);
|
||||||
if (evalLogPaths !== undefined) {
|
if (evalLogPaths !== undefined) {
|
||||||
this.queryInfo.setEvaluatorLogPaths(evalLogPaths);
|
this.queryInfo.setEvaluatorLogPaths(evalLogPaths);
|
||||||
@@ -128,10 +139,12 @@ export class LocalQueryRun {
|
|||||||
resultType: QueryResultType,
|
resultType: QueryResultType,
|
||||||
outputDir: QueryOutputDir,
|
outputDir: QueryOutputDir,
|
||||||
logger: BaseLogger,
|
logger: BaseLogger,
|
||||||
|
progress: ProgressCallback,
|
||||||
): Promise<EvaluatorLogPaths | undefined> {
|
): Promise<EvaluatorLogPaths | undefined> {
|
||||||
const evalLogPaths = await generateEvalLogSummaries(
|
const evalLogPaths = await generateEvalLogSummaries(
|
||||||
this.cliServer,
|
this.cliServer,
|
||||||
outputDir,
|
outputDir,
|
||||||
|
progress,
|
||||||
);
|
);
|
||||||
if (evalLogPaths !== undefined) {
|
if (evalLogPaths !== undefined) {
|
||||||
if (evalLogPaths.endSummary !== undefined) {
|
if (evalLogPaths.endSummary !== undefined) {
|
||||||
|
|||||||
@@ -537,6 +537,14 @@ export class ResultsView extends AbstractWebview<
|
|||||||
resultSetNames,
|
resultSetNames,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
await this.postMessage({
|
||||||
|
t: "setUserSettings",
|
||||||
|
userSettings: {
|
||||||
|
// Only show provenance info in canary mode for now.
|
||||||
|
shouldShowProvenance: isCanary(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
await this.postMessage({
|
await this.postMessage({
|
||||||
t: "setState",
|
t: "setState",
|
||||||
interpretation: interpretationPage,
|
interpretation: interpretationPage,
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export function decodeBqrsToMethods(
|
|||||||
let libraryVersion: string | undefined;
|
let libraryVersion: string | undefined;
|
||||||
let type: ModeledMethodType;
|
let type: ModeledMethodType;
|
||||||
let classification: CallClassification;
|
let classification: CallClassification;
|
||||||
|
let endpointKindColumn: string | BqrsEntityValue | undefined;
|
||||||
let endpointType: EndpointType | undefined = undefined;
|
let endpointType: EndpointType | undefined = undefined;
|
||||||
|
|
||||||
if (mode === Mode.Application) {
|
if (mode === Mode.Application) {
|
||||||
@@ -47,6 +48,7 @@ export function decodeBqrsToMethods(
|
|||||||
libraryVersion,
|
libraryVersion,
|
||||||
type,
|
type,
|
||||||
classification,
|
classification,
|
||||||
|
endpointKindColumn,
|
||||||
] = tuple as ApplicationModeTuple;
|
] = tuple as ApplicationModeTuple;
|
||||||
} else {
|
} else {
|
||||||
[
|
[
|
||||||
@@ -58,6 +60,7 @@ export function decodeBqrsToMethods(
|
|||||||
supported,
|
supported,
|
||||||
library,
|
library,
|
||||||
type,
|
type,
|
||||||
|
endpointKindColumn,
|
||||||
] = tuple as FrameworkModeTuple;
|
] = tuple as FrameworkModeTuple;
|
||||||
|
|
||||||
classification = CallClassification.Unknown;
|
classification = CallClassification.Unknown;
|
||||||
@@ -68,13 +71,18 @@ export function decodeBqrsToMethods(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (definition.endpointTypeForEndpoint) {
|
if (definition.endpointTypeForEndpoint) {
|
||||||
endpointType = definition.endpointTypeForEndpoint({
|
endpointType = definition.endpointTypeForEndpoint(
|
||||||
endpointType,
|
{
|
||||||
packageName,
|
endpointType,
|
||||||
typeName,
|
packageName,
|
||||||
methodName,
|
typeName,
|
||||||
methodParameters,
|
methodName,
|
||||||
});
|
methodParameters,
|
||||||
|
},
|
||||||
|
typeof endpointKindColumn === "object"
|
||||||
|
? endpointKindColumn.label
|
||||||
|
: endpointKindColumn,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (endpointType === undefined) {
|
if (endpointType === undefined) {
|
||||||
|
|||||||
@@ -174,11 +174,14 @@ export type ModelsAsDataLanguage = {
|
|||||||
* be determined by heuristics.
|
* be determined by heuristics.
|
||||||
* @param method The method to get the endpoint type for. The endpoint type can be undefined if the
|
* @param method The method to get the endpoint type for. The endpoint type can be undefined if the
|
||||||
* query does not return an endpoint type.
|
* query does not return an endpoint type.
|
||||||
|
* @param endpointKind An optional column that may be provided by the query to help determine the
|
||||||
|
* endpoint type.
|
||||||
*/
|
*/
|
||||||
endpointTypeForEndpoint?: (
|
endpointTypeForEndpoint?: (
|
||||||
method: Omit<MethodDefinition, "endpointType"> & {
|
method: Omit<MethodDefinition, "endpointType"> & {
|
||||||
endpointType: EndpointType | undefined;
|
endpointType: EndpointType | undefined;
|
||||||
},
|
},
|
||||||
|
endpointKind: string | undefined,
|
||||||
) => EndpointType | undefined;
|
) => EndpointType | undefined;
|
||||||
predicates: ModelsAsDataLanguagePredicates;
|
predicates: ModelsAsDataLanguagePredicates;
|
||||||
modelGeneration?: ModelsAsDataLanguageModelGeneration;
|
modelGeneration?: ModelsAsDataLanguageModelGeneration;
|
||||||
|
|||||||
@@ -4,7 +4,26 @@ import { EndpointType } from "../../method";
|
|||||||
|
|
||||||
const memberTokenRegex = /^Member\[(.+)]$/;
|
const memberTokenRegex = /^Member\[(.+)]$/;
|
||||||
|
|
||||||
export function parsePythonAccessPath(path: string): {
|
// In Python, the type can contain both the package name and the type name.
|
||||||
|
export function parsePythonType(type: string) {
|
||||||
|
// The first part is always the package name. All remaining parts are the type
|
||||||
|
// name.
|
||||||
|
|
||||||
|
const parts = type.split(".");
|
||||||
|
const packageName = parts.shift() ?? "";
|
||||||
|
|
||||||
|
return {
|
||||||
|
packageName,
|
||||||
|
typeName: parts.join("."),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// The type name can also be specified in the type, so this will combine
|
||||||
|
// the already parsed type name and the type name from the access path.
|
||||||
|
export function parsePythonAccessPath(
|
||||||
|
path: string,
|
||||||
|
shortTypeName: string,
|
||||||
|
): {
|
||||||
typeName: string;
|
typeName: string;
|
||||||
methodName: string;
|
methodName: string;
|
||||||
endpointType: EndpointType;
|
endpointType: EndpointType;
|
||||||
@@ -13,8 +32,12 @@ export function parsePythonAccessPath(path: string): {
|
|||||||
const tokens = parseAccessPathTokens(path);
|
const tokens = parseAccessPathTokens(path);
|
||||||
|
|
||||||
if (tokens.length === 0) {
|
if (tokens.length === 0) {
|
||||||
|
const typeName = shortTypeName.endsWith("!")
|
||||||
|
? shortTypeName.slice(0, -1)
|
||||||
|
: shortTypeName;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
typeName: "",
|
typeName,
|
||||||
methodName: "",
|
methodName: "",
|
||||||
endpointType: EndpointType.Method,
|
endpointType: EndpointType.Method,
|
||||||
path: "",
|
path: "",
|
||||||
@@ -23,6 +46,10 @@ export function parsePythonAccessPath(path: string): {
|
|||||||
|
|
||||||
const typeParts = [];
|
const typeParts = [];
|
||||||
let endpointType = EndpointType.Function;
|
let endpointType = EndpointType.Function;
|
||||||
|
// If a short type name was given and it doesn't end in a `!`, then this refers to a method.
|
||||||
|
if (shortTypeName !== "" && !shortTypeName.endsWith("!")) {
|
||||||
|
endpointType = EndpointType.Method;
|
||||||
|
}
|
||||||
|
|
||||||
let remainingTokens: typeof tokens = [];
|
let remainingTokens: typeof tokens = [];
|
||||||
|
|
||||||
@@ -32,6 +59,7 @@ export function parsePythonAccessPath(path: string): {
|
|||||||
if (memberMatch) {
|
if (memberMatch) {
|
||||||
typeParts.push(memberMatch[1]);
|
typeParts.push(memberMatch[1]);
|
||||||
} else if (token.text === "Instance") {
|
} else if (token.text === "Instance") {
|
||||||
|
// Alternative way of specifying that this refers to a method.
|
||||||
endpointType = EndpointType.Method;
|
endpointType = EndpointType.Method;
|
||||||
} else {
|
} else {
|
||||||
remainingTokens = tokens.slice(i);
|
remainingTokens = tokens.slice(i);
|
||||||
@@ -40,9 +68,22 @@ export function parsePythonAccessPath(path: string): {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const methodName = typeParts.pop() ?? "";
|
const methodName = typeParts.pop() ?? "";
|
||||||
const typeName = typeParts.join(".");
|
let typeName = typeParts.join(".");
|
||||||
const remainingPath = remainingTokens.map((token) => token.text).join(".");
|
const remainingPath = remainingTokens.map((token) => token.text).join(".");
|
||||||
|
|
||||||
|
if (shortTypeName !== "") {
|
||||||
|
if (shortTypeName.endsWith("!")) {
|
||||||
|
// The actual type name is the name without the `!`.
|
||||||
|
shortTypeName = shortTypeName.slice(0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeName !== "") {
|
||||||
|
typeName = `${shortTypeName}.${typeName}`;
|
||||||
|
} else {
|
||||||
|
typeName = shortTypeName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
methodName,
|
methodName,
|
||||||
typeName,
|
typeName,
|
||||||
@@ -51,53 +92,59 @@ export function parsePythonAccessPath(path: string): {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function parsePythonTypeAndPath(
|
||||||
|
type: string,
|
||||||
|
path: string,
|
||||||
|
): {
|
||||||
|
packageName: string;
|
||||||
|
typeName: string;
|
||||||
|
methodName: string;
|
||||||
|
endpointType: EndpointType;
|
||||||
|
path: string;
|
||||||
|
} {
|
||||||
|
const { packageName, typeName: shortTypeName } = parsePythonType(type);
|
||||||
|
const {
|
||||||
|
typeName,
|
||||||
|
methodName,
|
||||||
|
endpointType,
|
||||||
|
path: remainingPath,
|
||||||
|
} = parsePythonAccessPath(path, shortTypeName);
|
||||||
|
|
||||||
|
return {
|
||||||
|
packageName,
|
||||||
|
typeName,
|
||||||
|
methodName,
|
||||||
|
endpointType,
|
||||||
|
path: remainingPath,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function pythonMethodSignature(typeName: string, methodName: string) {
|
export function pythonMethodSignature(typeName: string, methodName: string) {
|
||||||
return `${typeName}#${methodName}`;
|
return `${typeName}#${methodName}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function pythonTypePath(typeName: string) {
|
export function pythonType(
|
||||||
if (typeName === "") {
|
packageName: string,
|
||||||
|
typeName: string,
|
||||||
|
endpointType: EndpointType,
|
||||||
|
) {
|
||||||
|
if (typeName !== "" && packageName !== "") {
|
||||||
|
return `${packageName}.${typeName}${endpointType === EndpointType.Function ? "!" : ""}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${packageName}${typeName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function pythonMethodPath(methodName: string) {
|
||||||
|
if (methodName === "") {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return typeName
|
return `Member[${methodName}]`;
|
||||||
.split(".")
|
|
||||||
.map((part) => `Member[${part}]`)
|
|
||||||
.join(".");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pythonMethodPath(
|
export function pythonPath(methodName: string, path: string) {
|
||||||
typeName: string,
|
const methodPath = pythonMethodPath(methodName);
|
||||||
methodName: string,
|
|
||||||
endpointType: EndpointType,
|
|
||||||
) {
|
|
||||||
if (methodName === "") {
|
|
||||||
return pythonTypePath(typeName);
|
|
||||||
}
|
|
||||||
|
|
||||||
const typePath = pythonTypePath(typeName);
|
|
||||||
|
|
||||||
let result = typePath;
|
|
||||||
if (typePath !== "" && endpointType === EndpointType.Method) {
|
|
||||||
result += ".Instance";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result !== "") {
|
|
||||||
result += ".";
|
|
||||||
}
|
|
||||||
|
|
||||||
result += `Member[${methodName}]`;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function pythonPath(
|
|
||||||
typeName: string,
|
|
||||||
methodName: string,
|
|
||||||
endpointType: EndpointType,
|
|
||||||
path: string,
|
|
||||||
) {
|
|
||||||
const methodPath = pythonMethodPath(typeName, methodName, endpointType);
|
|
||||||
if (methodPath === "") {
|
if (methodPath === "") {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
@@ -111,7 +158,24 @@ export function pythonPath(
|
|||||||
|
|
||||||
export function pythonEndpointType(
|
export function pythonEndpointType(
|
||||||
method: Omit<MethodDefinition, "endpointType">,
|
method: Omit<MethodDefinition, "endpointType">,
|
||||||
|
endpointKind: string | undefined,
|
||||||
): EndpointType {
|
): EndpointType {
|
||||||
|
switch (endpointKind) {
|
||||||
|
case "Function":
|
||||||
|
return EndpointType.Function;
|
||||||
|
case "InstanceMethod":
|
||||||
|
return EndpointType.Method;
|
||||||
|
case "ClassMethod":
|
||||||
|
return EndpointType.ClassMethod;
|
||||||
|
case "StaticMethod":
|
||||||
|
return EndpointType.StaticMethod;
|
||||||
|
case "InitMethod":
|
||||||
|
return EndpointType.Constructor;
|
||||||
|
case "Class":
|
||||||
|
return EndpointType.Class;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy behavior for when the kind column is missing.
|
||||||
if (
|
if (
|
||||||
method.methodParameters.startsWith("(self,") ||
|
method.methodParameters.startsWith("(self,") ||
|
||||||
method.methodParameters === "(self)"
|
method.methodParameters === "(self)"
|
||||||
@@ -120,3 +184,12 @@ export function pythonEndpointType(
|
|||||||
}
|
}
|
||||||
return EndpointType.Function;
|
return EndpointType.Function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function hasPythonSelfArgument(endpointType: EndpointType): boolean {
|
||||||
|
// Instance methods and class methods both use `Argument[self]` for the first parameter. The first
|
||||||
|
// parameter after self is called `Argument[0]`.
|
||||||
|
return (
|
||||||
|
endpointType === EndpointType.Method ||
|
||||||
|
endpointType === EndpointType.ClassMethod
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,44 +4,48 @@ import { Mode } from "../../shared/mode";
|
|||||||
import type { MethodArgument } from "../../method";
|
import type { MethodArgument } from "../../method";
|
||||||
import { EndpointType, getArgumentsList } from "../../method";
|
import { EndpointType, getArgumentsList } from "../../method";
|
||||||
import {
|
import {
|
||||||
parsePythonAccessPath,
|
hasPythonSelfArgument,
|
||||||
|
parsePythonTypeAndPath,
|
||||||
pythonEndpointType,
|
pythonEndpointType,
|
||||||
pythonMethodPath,
|
pythonMethodPath,
|
||||||
pythonMethodSignature,
|
pythonMethodSignature,
|
||||||
pythonPath,
|
pythonPath,
|
||||||
|
pythonType,
|
||||||
} from "./access-paths";
|
} from "./access-paths";
|
||||||
|
|
||||||
export const python: ModelsAsDataLanguage = {
|
export const python: ModelsAsDataLanguage = {
|
||||||
availableModes: [Mode.Framework],
|
availableModes: [Mode.Framework],
|
||||||
createMethodSignature: ({ typeName, methodName }) =>
|
createMethodSignature: ({ typeName, methodName }) =>
|
||||||
`${typeName}#${methodName}`,
|
`${typeName}#${methodName}`,
|
||||||
endpointTypeForEndpoint: (method) => pythonEndpointType(method),
|
endpointTypeForEndpoint: (method, endpointKind) =>
|
||||||
|
pythonEndpointType(method, endpointKind),
|
||||||
predicates: {
|
predicates: {
|
||||||
source: {
|
source: {
|
||||||
extensiblePredicate: sharedExtensiblePredicates.source,
|
extensiblePredicate: sharedExtensiblePredicates.source,
|
||||||
supportedKinds: sharedKinds.source,
|
supportedKinds: sharedKinds.source,
|
||||||
supportedEndpointTypes: [EndpointType.Method, EndpointType.Function],
|
supportedEndpointTypes: [
|
||||||
|
EndpointType.Method,
|
||||||
|
EndpointType.Function,
|
||||||
|
EndpointType.Constructor,
|
||||||
|
EndpointType.ClassMethod,
|
||||||
|
EndpointType.StaticMethod,
|
||||||
|
],
|
||||||
// extensible predicate sourceModel(
|
// extensible predicate sourceModel(
|
||||||
// string type, string path, string kind
|
// string type, string path, string kind
|
||||||
// );
|
// );
|
||||||
generateMethodDefinition: (method) => [
|
generateMethodDefinition: (method) => [
|
||||||
method.packageName,
|
pythonType(method.packageName, method.typeName, method.endpointType),
|
||||||
pythonPath(
|
pythonPath(method.methodName, method.output),
|
||||||
method.typeName,
|
|
||||||
method.methodName,
|
|
||||||
method.endpointType,
|
|
||||||
method.output,
|
|
||||||
),
|
|
||||||
method.kind,
|
method.kind,
|
||||||
],
|
],
|
||||||
readModeledMethod: (row) => {
|
readModeledMethod: (row) => {
|
||||||
const packageName = row[0] as string;
|
|
||||||
const {
|
const {
|
||||||
|
packageName,
|
||||||
typeName,
|
typeName,
|
||||||
methodName,
|
methodName,
|
||||||
endpointType,
|
endpointType,
|
||||||
path: output,
|
path: output,
|
||||||
} = parsePythonAccessPath(row[1] as string);
|
} = parsePythonTypeAndPath(row[0] as string, row[1] as string);
|
||||||
return {
|
return {
|
||||||
type: "source",
|
type: "source",
|
||||||
output,
|
output,
|
||||||
@@ -59,30 +63,31 @@ export const python: ModelsAsDataLanguage = {
|
|||||||
sink: {
|
sink: {
|
||||||
extensiblePredicate: sharedExtensiblePredicates.sink,
|
extensiblePredicate: sharedExtensiblePredicates.sink,
|
||||||
supportedKinds: sharedKinds.sink,
|
supportedKinds: sharedKinds.sink,
|
||||||
supportedEndpointTypes: [EndpointType.Method, EndpointType.Function],
|
supportedEndpointTypes: [
|
||||||
|
EndpointType.Method,
|
||||||
|
EndpointType.Function,
|
||||||
|
EndpointType.Constructor,
|
||||||
|
EndpointType.ClassMethod,
|
||||||
|
EndpointType.StaticMethod,
|
||||||
|
],
|
||||||
// extensible predicate sinkModel(
|
// extensible predicate sinkModel(
|
||||||
// string type, string path, string kind
|
// string type, string path, string kind
|
||||||
// );
|
// );
|
||||||
generateMethodDefinition: (method) => {
|
generateMethodDefinition: (method) => {
|
||||||
return [
|
return [
|
||||||
method.packageName,
|
pythonType(method.packageName, method.typeName, method.endpointType),
|
||||||
pythonPath(
|
pythonPath(method.methodName, method.input),
|
||||||
method.typeName,
|
|
||||||
method.methodName,
|
|
||||||
method.endpointType,
|
|
||||||
method.input,
|
|
||||||
),
|
|
||||||
method.kind,
|
method.kind,
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
readModeledMethod: (row) => {
|
readModeledMethod: (row) => {
|
||||||
const packageName = row[0] as string;
|
|
||||||
const {
|
const {
|
||||||
|
packageName,
|
||||||
typeName,
|
typeName,
|
||||||
methodName,
|
methodName,
|
||||||
endpointType,
|
endpointType,
|
||||||
path: input,
|
path: input,
|
||||||
} = parsePythonAccessPath(row[1] as string);
|
} = parsePythonTypeAndPath(row[0] as string, row[1] as string);
|
||||||
return {
|
return {
|
||||||
type: "sink",
|
type: "sink",
|
||||||
input,
|
input,
|
||||||
@@ -100,25 +105,26 @@ export const python: ModelsAsDataLanguage = {
|
|||||||
summary: {
|
summary: {
|
||||||
extensiblePredicate: sharedExtensiblePredicates.summary,
|
extensiblePredicate: sharedExtensiblePredicates.summary,
|
||||||
supportedKinds: sharedKinds.summary,
|
supportedKinds: sharedKinds.summary,
|
||||||
supportedEndpointTypes: [EndpointType.Method, EndpointType.Function],
|
supportedEndpointTypes: [
|
||||||
|
EndpointType.Method,
|
||||||
|
EndpointType.Function,
|
||||||
|
EndpointType.Constructor,
|
||||||
|
EndpointType.ClassMethod,
|
||||||
|
EndpointType.StaticMethod,
|
||||||
|
],
|
||||||
// extensible predicate summaryModel(
|
// extensible predicate summaryModel(
|
||||||
// string type, string path, string input, string output, string kind
|
// string type, string path, string input, string output, string kind
|
||||||
// );
|
// );
|
||||||
generateMethodDefinition: (method) => [
|
generateMethodDefinition: (method) => [
|
||||||
method.packageName,
|
pythonType(method.packageName, method.typeName, method.endpointType),
|
||||||
pythonMethodPath(
|
pythonMethodPath(method.methodName),
|
||||||
method.typeName,
|
|
||||||
method.methodName,
|
|
||||||
method.endpointType,
|
|
||||||
),
|
|
||||||
method.input,
|
method.input,
|
||||||
method.output,
|
method.output,
|
||||||
method.kind,
|
method.kind,
|
||||||
],
|
],
|
||||||
readModeledMethod: (row) => {
|
readModeledMethod: (row) => {
|
||||||
const packageName = row[0] as string;
|
const { packageName, typeName, methodName, endpointType, path } =
|
||||||
const { typeName, methodName, endpointType, path } =
|
parsePythonTypeAndPath(row[0] as string, row[1] as string);
|
||||||
parsePythonAccessPath(row[1] as string);
|
|
||||||
if (path !== "") {
|
if (path !== "") {
|
||||||
throw new Error("Summary path must be a method");
|
throw new Error("Summary path must be a method");
|
||||||
}
|
}
|
||||||
@@ -144,18 +150,13 @@ export const python: ModelsAsDataLanguage = {
|
|||||||
// string type, string path, string kind
|
// string type, string path, string kind
|
||||||
// );
|
// );
|
||||||
generateMethodDefinition: (method) => [
|
generateMethodDefinition: (method) => [
|
||||||
method.packageName,
|
pythonType(method.packageName, method.typeName, method.endpointType),
|
||||||
pythonMethodPath(
|
pythonMethodPath(method.methodName),
|
||||||
method.typeName,
|
|
||||||
method.methodName,
|
|
||||||
method.endpointType,
|
|
||||||
),
|
|
||||||
method.kind,
|
method.kind,
|
||||||
],
|
],
|
||||||
readModeledMethod: (row) => {
|
readModeledMethod: (row) => {
|
||||||
const packageName = row[0] as string;
|
const { packageName, typeName, methodName, endpointType, path } =
|
||||||
const { typeName, methodName, endpointType, path } =
|
parsePythonTypeAndPath(row[0] as string, row[1] as string);
|
||||||
parsePythonAccessPath(row[1] as string);
|
|
||||||
if (path !== "") {
|
if (path !== "") {
|
||||||
throw new Error("Neutral path must be a method");
|
throw new Error("Neutral path must be a method");
|
||||||
}
|
}
|
||||||
@@ -172,25 +173,46 @@ export const python: ModelsAsDataLanguage = {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
type: {
|
||||||
|
extensiblePredicate: "typeModel",
|
||||||
|
// extensible predicate typeModel(string type1, string type2, string path);
|
||||||
|
generateMethodDefinition: (method) => [
|
||||||
|
method.relatedTypeName,
|
||||||
|
pythonType(method.packageName, method.typeName, method.endpointType),
|
||||||
|
pythonPath(method.methodName, method.path),
|
||||||
|
],
|
||||||
|
readModeledMethod: (row) => {
|
||||||
|
const { packageName, typeName, methodName, endpointType, path } =
|
||||||
|
parsePythonTypeAndPath(row[1] as string, row[2] as string);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "type",
|
||||||
|
relatedTypeName: row[0] as string,
|
||||||
|
path,
|
||||||
|
signature: pythonMethodSignature(typeName, methodName),
|
||||||
|
endpointType,
|
||||||
|
packageName,
|
||||||
|
typeName,
|
||||||
|
methodName,
|
||||||
|
methodParameters: "",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
getArgumentOptions: (method) => {
|
getArgumentOptions: (method) => {
|
||||||
// Argument and Parameter are equivalent in Python, but we'll use Argument in the model editor
|
// Argument and Parameter are equivalent in Python, but we'll use Argument in the model editor
|
||||||
const argumentsList = getArgumentsList(method.methodParameters).map(
|
const argumentsList = getArgumentsList(method.methodParameters).map(
|
||||||
(argument, index): MethodArgument => {
|
(argument, index): MethodArgument => {
|
||||||
if (
|
if (hasPythonSelfArgument(method.endpointType) && index === 0) {
|
||||||
method.endpointType === EndpointType.Method &&
|
|
||||||
argument === "self" &&
|
|
||||||
index === 0
|
|
||||||
) {
|
|
||||||
return {
|
return {
|
||||||
path: "Argument[self]",
|
path: "Argument[self]",
|
||||||
label: "Argument[self]: self",
|
label: `Argument[self]: ${argument}`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is a method, self does not count as an argument index, so we
|
// If this endpoint has a self argument, self does not count as an argument index so we
|
||||||
// should start at 0 for the second argument
|
// should start at 0 for the second argument
|
||||||
if (method.endpointType === EndpointType.Method) {
|
if (hasPythonSelfArgument(method.endpointType)) {
|
||||||
index -= 1;
|
index -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ export enum EndpointType {
|
|||||||
Method = "method",
|
Method = "method",
|
||||||
Constructor = "constructor",
|
Constructor = "constructor",
|
||||||
Function = "function",
|
Function = "function",
|
||||||
|
StaticMethod = "staticMethod",
|
||||||
|
ClassMethod = "classMethod",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MethodDefinition {
|
export interface MethodDefinition {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export type Query = {
|
|||||||
* - libraryVersion: the version of the library that contains the external API. This is a string and can be empty if the version cannot be determined.
|
* - libraryVersion: the version of the library that contains the external API. This is a string and can be empty if the version cannot be determined.
|
||||||
* - type: the modeled kind of the method, either "sink", "source", "summary", or "neutral"
|
* - type: the modeled kind of the method, either "sink", "source", "summary", or "neutral"
|
||||||
* - classification: the classification of the use of the method, either "source", "test", "generated", or "unknown"
|
* - classification: the classification of the use of the method, either "source", "test", "generated", or "unknown"
|
||||||
|
* - kind: the kind of the endpoint, language-specific, e.g. "method" or "function"
|
||||||
*/
|
*/
|
||||||
applicationModeQuery: string;
|
applicationModeQuery: string;
|
||||||
/**
|
/**
|
||||||
@@ -32,6 +33,7 @@ export type Query = {
|
|||||||
* - supported: whether this method is modeled. This should be a string representation of a boolean to satify the result pattern for a problem query.
|
* - supported: whether this method is modeled. This should be a string representation of a boolean to satify the result pattern for a problem query.
|
||||||
* - libraryName: the name of the file or library that contains the method. This is a string and usually the basename of a file.
|
* - libraryName: the name of the file or library that contains the method. This is a string and usually the basename of a file.
|
||||||
* - type: the modeled kind of the method, either "sink", "source", "summary", or "neutral"
|
* - type: the modeled kind of the method, either "sink", "source", "summary", or "neutral"
|
||||||
|
* - kind: the kind of the endpoint, language-specific, e.g. "method" or "function"
|
||||||
*/
|
*/
|
||||||
frameworkModeQuery: string;
|
frameworkModeQuery: string;
|
||||||
dependencies?: {
|
dependencies?: {
|
||||||
@@ -50,6 +52,7 @@ export type ApplicationModeTuple = [
|
|||||||
string,
|
string,
|
||||||
ModeledMethodType,
|
ModeledMethodType,
|
||||||
CallClassification,
|
CallClassification,
|
||||||
|
string | BqrsEntityValue | undefined,
|
||||||
];
|
];
|
||||||
|
|
||||||
export type FrameworkModeTuple = [
|
export type FrameworkModeTuple = [
|
||||||
@@ -61,4 +64,5 @@ export type FrameworkModeTuple = [
|
|||||||
boolean,
|
boolean,
|
||||||
string,
|
string,
|
||||||
ModeledMethodType,
|
ModeledMethodType,
|
||||||
|
string | BqrsEntityValue | undefined,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -9,20 +9,16 @@ export const SUPPORTED_LANGUAGES: QueryLanguage[] = [
|
|||||||
QueryLanguage.Java,
|
QueryLanguage.Java,
|
||||||
QueryLanguage.CSharp,
|
QueryLanguage.CSharp,
|
||||||
QueryLanguage.Ruby,
|
QueryLanguage.Ruby,
|
||||||
|
QueryLanguage.Python,
|
||||||
];
|
];
|
||||||
|
|
||||||
export function isSupportedLanguage(
|
export function isSupportedLanguage(
|
||||||
language: QueryLanguage,
|
language: QueryLanguage,
|
||||||
modelConfig: ModelConfig,
|
_modelConfig: ModelConfig,
|
||||||
) {
|
) {
|
||||||
if (SUPPORTED_LANGUAGES.includes(language)) {
|
if (SUPPORTED_LANGUAGES.includes(language)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (language === QueryLanguage.Python) {
|
|
||||||
// Python is only enabled when the config setting is set
|
|
||||||
return modelConfig.enablePython;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
getActionsWorkflowRunUrl as getVariantAnalysisActionsWorkflowRunUrl,
|
getActionsWorkflowRunUrl as getVariantAnalysisActionsWorkflowRunUrl,
|
||||||
} from "../variant-analysis/shared/variant-analysis";
|
} from "../variant-analysis/shared/variant-analysis";
|
||||||
import type { QueryLanguage } from "../common/query-language";
|
import type { QueryLanguage } from "../common/query-language";
|
||||||
|
import { getGitHubInstanceUrl } from "../config";
|
||||||
|
|
||||||
export type QueryHistoryInfo = LocalQueryInfo | VariantAnalysisHistoryItem;
|
export type QueryHistoryInfo = LocalQueryInfo | VariantAnalysisHistoryItem;
|
||||||
|
|
||||||
@@ -79,5 +80,8 @@ export function buildRepoLabel(item: VariantAnalysisHistoryItem): string {
|
|||||||
export function getActionsWorkflowRunUrl(
|
export function getActionsWorkflowRunUrl(
|
||||||
item: VariantAnalysisHistoryItem,
|
item: VariantAnalysisHistoryItem,
|
||||||
): string {
|
): string {
|
||||||
return getVariantAnalysisActionsWorkflowRunUrl(item.variantAnalysis);
|
return getVariantAnalysisActionsWorkflowRunUrl(
|
||||||
|
item.variantAnalysis,
|
||||||
|
getGitHubInstanceUrl(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -240,8 +240,32 @@ export class QueryServerClient extends DisposableObject {
|
|||||||
this.nextCallback = 0;
|
this.nextCallback = 0;
|
||||||
this.nextProgress = 0;
|
this.nextProgress = 0;
|
||||||
this.progressCallbacks = {};
|
this.progressCallbacks = {};
|
||||||
child.on("close", () => {
|
|
||||||
this.restartQueryServerOnFailure();
|
// 'exit' may or may not fire after 'error' event, so we want to guard against restarting the
|
||||||
|
// query server twice if both events fire.
|
||||||
|
let wasExitOrErrorHandled = false;
|
||||||
|
child.on("error", (err: Error) => {
|
||||||
|
if (!wasExitOrErrorHandled) {
|
||||||
|
void this.logger.log(`Query server terminated with error: ${err}.`);
|
||||||
|
this.restartQueryServerOnFailure();
|
||||||
|
wasExitOrErrorHandled = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
child.on("exit", (code: number, signal: string) => {
|
||||||
|
if (!wasExitOrErrorHandled) {
|
||||||
|
if (code !== null) {
|
||||||
|
void this.logger.log(
|
||||||
|
`Query server terminated with exit code: ${code}.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (signal !== null) {
|
||||||
|
void this.logger.log(
|
||||||
|
`Query server terminated due to receipt of signal: ${signal}.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.restartQueryServerOnFailure();
|
||||||
|
wasExitOrErrorHandled = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ import { generateSummarySymbolsFile } from "./log-insights/summary-parser";
|
|||||||
import { getErrorMessage } from "./common/helpers-pure";
|
import { getErrorMessage } from "./common/helpers-pure";
|
||||||
import { createHash } from "crypto";
|
import { createHash } from "crypto";
|
||||||
import { QueryOutputDir } from "./local-queries/query-output-dir";
|
import { QueryOutputDir } from "./local-queries/query-output-dir";
|
||||||
|
import { progressUpdate } from "./common/vscode/progress";
|
||||||
|
import type { ProgressCallback } from "./common/vscode/progress";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* run-queries.ts
|
* run-queries.ts
|
||||||
@@ -519,6 +521,7 @@ export async function createInitialQueryInfo(
|
|||||||
export async function generateEvalLogSummaries(
|
export async function generateEvalLogSummaries(
|
||||||
cliServer: CodeQLCliServer,
|
cliServer: CodeQLCliServer,
|
||||||
outputDir: QueryOutputDir,
|
outputDir: QueryOutputDir,
|
||||||
|
progress: ProgressCallback,
|
||||||
): Promise<EvaluatorLogPaths | undefined> {
|
): Promise<EvaluatorLogPaths | undefined> {
|
||||||
const log = outputDir.evalLogPath;
|
const log = outputDir.evalLogPath;
|
||||||
if (!(await pathExists(log))) {
|
if (!(await pathExists(log))) {
|
||||||
@@ -527,6 +530,7 @@ export async function generateEvalLogSummaries(
|
|||||||
}
|
}
|
||||||
let humanReadableSummary: string | undefined = undefined;
|
let humanReadableSummary: string | undefined = undefined;
|
||||||
let endSummary: string | undefined = undefined;
|
let endSummary: string | undefined = undefined;
|
||||||
|
progress(progressUpdate(1, 3, "Generating evaluator log summary"));
|
||||||
if (await generateHumanReadableLogSummary(cliServer, outputDir)) {
|
if (await generateHumanReadableLogSummary(cliServer, outputDir)) {
|
||||||
humanReadableSummary = outputDir.evalLogSummaryPath;
|
humanReadableSummary = outputDir.evalLogSummaryPath;
|
||||||
endSummary = outputDir.evalLogEndSummaryPath;
|
endSummary = outputDir.evalLogEndSummaryPath;
|
||||||
@@ -535,12 +539,21 @@ export async function generateEvalLogSummaries(
|
|||||||
let summarySymbols: string | undefined = undefined;
|
let summarySymbols: string | undefined = undefined;
|
||||||
if (isCanary()) {
|
if (isCanary()) {
|
||||||
// Generate JSON summary for viewer.
|
// Generate JSON summary for viewer.
|
||||||
|
progress(progressUpdate(2, 3, "Generating JSON log summary"));
|
||||||
jsonSummary = outputDir.jsonEvalLogSummaryPath;
|
jsonSummary = outputDir.jsonEvalLogSummaryPath;
|
||||||
await cliServer.generateJsonLogSummary(log, jsonSummary);
|
await cliServer.generateJsonLogSummary(log, jsonSummary);
|
||||||
|
|
||||||
if (humanReadableSummary !== undefined) {
|
if (humanReadableSummary !== undefined) {
|
||||||
summarySymbols = outputDir.evalLogSummarySymbolsPath;
|
summarySymbols = outputDir.evalLogSummarySymbolsPath;
|
||||||
await generateSummarySymbolsFile(humanReadableSummary, summarySymbols);
|
if (
|
||||||
|
!(await cliServer.cliConstraints.supportsGenerateSummarySymbolMap())
|
||||||
|
) {
|
||||||
|
// We're using an old CLI that cannot generate the summary symbols file while generating the
|
||||||
|
// human-readable log summary. As a fallback, create it by parsing the human-readable
|
||||||
|
// summary.
|
||||||
|
progress(progressUpdate(3, 3, "Generating summary symbols file"));
|
||||||
|
await generateSummarySymbolsFile(humanReadableSummary, summarySymbols);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ type ErrorResponse = {
|
|||||||
|
|
||||||
export function handleRequestError(
|
export function handleRequestError(
|
||||||
e: RequestError,
|
e: RequestError,
|
||||||
|
githubUrl: URL,
|
||||||
logger: NotificationLogger,
|
logger: NotificationLogger,
|
||||||
): boolean {
|
): boolean {
|
||||||
if (e.status !== 422) {
|
if (e.status !== 422) {
|
||||||
@@ -60,9 +61,12 @@ export function handleRequestError(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const createBranchURL = `https://github.com/${
|
const createBranchURL = new URL(
|
||||||
missingDefaultBranchError.repository
|
`/${
|
||||||
}/new/${encodeURIComponent(missingDefaultBranchError.default_branch)}`;
|
missingDefaultBranchError.repository
|
||||||
|
}/new/${encodeURIComponent(missingDefaultBranchError.default_branch)}`,
|
||||||
|
githubUrl,
|
||||||
|
).toString();
|
||||||
|
|
||||||
void showAndLogErrorMessage(
|
void showAndLogErrorMessage(
|
||||||
logger,
|
logger,
|
||||||
|
|||||||
@@ -1,18 +1,10 @@
|
|||||||
import {
|
import { hasEnterpriseUri, hasGhecDrUri } from "../config";
|
||||||
VARIANT_ANALYSIS_ENABLE_GHEC_DR,
|
|
||||||
hasEnterpriseUri,
|
|
||||||
hasGhecDrUri,
|
|
||||||
} from "../config";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether MRVA should be enabled or not for the current GitHub host.
|
* Determines whether MRVA should be enabled or not for the current GitHub host.
|
||||||
|
* MRVA is enabled on github.com and GHEC-DR.
|
||||||
* This is based on the `github-enterprise.uri` setting.
|
* This is based on the `github-enterprise.uri` setting.
|
||||||
*/
|
*/
|
||||||
export function isVariantAnalysisEnabledForGitHubHost(): boolean {
|
export function isVariantAnalysisEnabledForGitHubHost(): boolean {
|
||||||
return (
|
return !hasEnterpriseUri() || hasGhecDrUri();
|
||||||
// MRVA is always enabled on github.com
|
|
||||||
!hasEnterpriseUri() ||
|
|
||||||
// MRVA can be enabled on GHEC-DR using a feature flag
|
|
||||||
(hasGhecDrUri() && !!VARIANT_ANALYSIS_ENABLE_GHEC_DR.getValue<boolean>())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -295,10 +295,14 @@ export function getSkippedRepoCount(
|
|||||||
|
|
||||||
export function getActionsWorkflowRunUrl(
|
export function getActionsWorkflowRunUrl(
|
||||||
variantAnalysis: VariantAnalysis,
|
variantAnalysis: VariantAnalysis,
|
||||||
|
githubUrl: URL,
|
||||||
): string {
|
): string {
|
||||||
const {
|
const {
|
||||||
actionsWorkflowRunId,
|
actionsWorkflowRunId,
|
||||||
controllerRepo: { fullName },
|
controllerRepo: { fullName },
|
||||||
} = variantAnalysis;
|
} = variantAnalysis;
|
||||||
return `https://github.com/${fullName}/actions/runs/${actionsWorkflowRunId}`;
|
return new URL(
|
||||||
|
`/${fullName}/actions/runs/${actionsWorkflowRunId}`,
|
||||||
|
githubUrl,
|
||||||
|
).toString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,6 @@ import {
|
|||||||
REPO_STATES_FILENAME,
|
REPO_STATES_FILENAME,
|
||||||
writeRepoStates,
|
writeRepoStates,
|
||||||
} from "./repo-states-store";
|
} from "./repo-states-store";
|
||||||
import { GITHUB_AUTH_PROVIDER_ID } from "../common/vscode/authentication";
|
|
||||||
import { FetchError } from "node-fetch";
|
import { FetchError } from "node-fetch";
|
||||||
import {
|
import {
|
||||||
showAndLogExceptionWithTelemetry,
|
showAndLogExceptionWithTelemetry,
|
||||||
@@ -98,6 +97,7 @@ import { findVariantAnalysisQlPackRoot } from "./ql";
|
|||||||
import { resolveCodeScanningQueryPack } from "./code-scanning-pack";
|
import { resolveCodeScanningQueryPack } from "./code-scanning-pack";
|
||||||
import { isSarifResultsQueryKind } from "../common/query-metadata";
|
import { isSarifResultsQueryKind } from "../common/query-metadata";
|
||||||
import { isVariantAnalysisEnabledForGitHubHost } from "./ghec-dr";
|
import { isVariantAnalysisEnabledForGitHubHost } from "./ghec-dr";
|
||||||
|
import type { VariantAnalysisConfig } from "../config";
|
||||||
import { getEnterpriseUri } from "../config";
|
import { getEnterpriseUri } from "../config";
|
||||||
|
|
||||||
const maxRetryCount = 3;
|
const maxRetryCount = 3;
|
||||||
@@ -158,6 +158,7 @@ export class VariantAnalysisManager
|
|||||||
private readonly storagePath: string,
|
private readonly storagePath: string,
|
||||||
private readonly variantAnalysisResultsManager: VariantAnalysisResultsManager,
|
private readonly variantAnalysisResultsManager: VariantAnalysisResultsManager,
|
||||||
private readonly dbManager: DbManager,
|
private readonly dbManager: DbManager,
|
||||||
|
private readonly config: VariantAnalysisConfig,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.variantAnalysisMonitor = this.push(
|
this.variantAnalysisMonitor = this.push(
|
||||||
@@ -426,7 +427,10 @@ export class VariantAnalysisManager
|
|||||||
);
|
);
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
// If the error is handled by the handleRequestError function, we don't need to throw
|
// If the error is handled by the handleRequestError function, we don't need to throw
|
||||||
if (e instanceof RequestError && handleRequestError(e, this.app.logger)) {
|
if (
|
||||||
|
e instanceof RequestError &&
|
||||||
|
handleRequestError(e, this.config.githubUrl, this.app.logger)
|
||||||
|
) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -745,7 +749,7 @@ export class VariantAnalysisManager
|
|||||||
private async onDidChangeSessions(
|
private async onDidChangeSessions(
|
||||||
event: AuthenticationSessionsChangeEvent,
|
event: AuthenticationSessionsChangeEvent,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (event.provider.id !== GITHUB_AUTH_PROVIDER_ID) {
|
if (event.provider.id !== this.app.credentials.authProviderId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -951,7 +955,10 @@ export class VariantAnalysisManager
|
|||||||
throw new Error(`No variant analysis with id: ${variantAnalysisId}`);
|
throw new Error(`No variant analysis with id: ${variantAnalysisId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const actionsWorkflowRunUrl = getActionsWorkflowRunUrl(variantAnalysis);
|
const actionsWorkflowRunUrl = getActionsWorkflowRunUrl(
|
||||||
|
variantAnalysis,
|
||||||
|
this.config.githubUrl,
|
||||||
|
);
|
||||||
|
|
||||||
await this.app.commands.execute(
|
await this.app.commands.execute(
|
||||||
"vscode.open",
|
"vscode.open",
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { DisposableObject } from "../common/disposable-object";
|
|||||||
import { EventEmitter } from "vscode";
|
import { EventEmitter } from "vscode";
|
||||||
import { unzipToDirectoryConcurrently } from "../common/unzip-concurrently";
|
import { unzipToDirectoryConcurrently } from "../common/unzip-concurrently";
|
||||||
import { readRepoTask, writeRepoTask } from "./repo-tasks-store";
|
import { readRepoTask, writeRepoTask } from "./repo-tasks-store";
|
||||||
|
import type { VariantAnalysisConfig } from "../config";
|
||||||
|
|
||||||
type CacheKey = `${number}/${string}`;
|
type CacheKey = `${number}/${string}`;
|
||||||
|
|
||||||
@@ -62,6 +63,7 @@ export class VariantAnalysisResultsManager extends DisposableObject {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly cliServer: CodeQLCliServer,
|
private readonly cliServer: CodeQLCliServer,
|
||||||
|
private readonly config: VariantAnalysisConfig,
|
||||||
private readonly logger: Logger,
|
private readonly logger: Logger,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
@@ -192,7 +194,7 @@ export class VariantAnalysisResultsManager extends DisposableObject {
|
|||||||
throw new Error("Missing database commit SHA");
|
throw new Error("Missing database commit SHA");
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileLinkPrefix = this.createGitHubDotcomFileLinkPrefix(
|
const fileLinkPrefix = this.createGitHubFileLinkPrefix(
|
||||||
repoTask.repository.fullName,
|
repoTask.repository.fullName,
|
||||||
repoTask.databaseCommitSha,
|
repoTask.databaseCommitSha,
|
||||||
);
|
);
|
||||||
@@ -283,11 +285,11 @@ export class VariantAnalysisResultsManager extends DisposableObject {
|
|||||||
return join(variantAnalysisStoragePath, fullName);
|
return join(variantAnalysisStoragePath, fullName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private createGitHubDotcomFileLinkPrefix(
|
private createGitHubFileLinkPrefix(fullName: string, sha: string): string {
|
||||||
fullName: string,
|
return new URL(
|
||||||
sha: string,
|
`/${fullName}/blob/${sha}`,
|
||||||
): string {
|
this.config.githubUrl,
|
||||||
return `https://github.com/${fullName}/blob/${sha}`;
|
).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeAnalysisResults(variantAnalysis: VariantAnalysis) {
|
public removeAnalysisResults(variantAnalysis: VariantAnalysis) {
|
||||||
|
|||||||
@@ -12,6 +12,18 @@ const ShowPathsLink = styled(VSCodeLink)`
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const Label = styled.span`
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
margin-left: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
function getShortestPathLength(codeFlows: CodeFlow[]): number {
|
||||||
|
const allPathLengths = codeFlows
|
||||||
|
.map((codeFlow) => codeFlow.threadFlows.length)
|
||||||
|
.flat();
|
||||||
|
return Math.min(...allPathLengths);
|
||||||
|
}
|
||||||
|
|
||||||
export type CodePathsProps = {
|
export type CodePathsProps = {
|
||||||
codeFlows: CodeFlow[];
|
codeFlows: CodeFlow[];
|
||||||
ruleDescription: string;
|
ruleDescription: string;
|
||||||
@@ -40,6 +52,7 @@ export const CodePaths = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ShowPathsLink onClick={onShowPathsClick}>Show paths</ShowPathsLink>
|
<ShowPathsLink onClick={onShowPathsClick}>Show paths</ShowPathsLink>
|
||||||
|
<Label>(Shortest: {getShortestPathLength(codeFlows)})</Label>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,6 +24,12 @@ describe(CodePaths.name, () => {
|
|||||||
expect(screen.getByText("Show paths")).toBeInTheDocument();
|
expect(screen.getByText("Show paths")).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("renders shortest path for code flows", () => {
|
||||||
|
render();
|
||||||
|
|
||||||
|
expect(screen.getByText("(Shortest: 1)")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
it("posts extension message when 'show paths' link clicked", async () => {
|
it("posts extension message when 'show paths' link clicked", async () => {
|
||||||
render();
|
render();
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import type {
|
|||||||
ToCompareViewMessage,
|
ToCompareViewMessage,
|
||||||
SetComparisonsMessage,
|
SetComparisonsMessage,
|
||||||
SetComparisonQueryInfoMessage,
|
SetComparisonQueryInfoMessage,
|
||||||
|
UserSettings,
|
||||||
} from "../../common/interface-types";
|
} from "../../common/interface-types";
|
||||||
|
import { DEFAULT_USER_SETTINGS } from "../../common/interface-types";
|
||||||
import CompareSelector from "./CompareSelector";
|
import CompareSelector from "./CompareSelector";
|
||||||
import { vscode } from "../vscode-api";
|
import { vscode } from "../vscode-api";
|
||||||
import CompareTable from "./CompareTable";
|
import CompareTable from "./CompareTable";
|
||||||
@@ -31,6 +33,9 @@ export function Compare(_: Record<string, never>): React.JSX.Element {
|
|||||||
const [comparison, setComparison] = useState<SetComparisonsMessage | null>(
|
const [comparison, setComparison] = useState<SetComparisonsMessage | null>(
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
|
const [userSettings, setUserSettings] = useState<UserSettings>(
|
||||||
|
DEFAULT_USER_SETTINGS,
|
||||||
|
);
|
||||||
|
|
||||||
const message = comparison?.message || "Empty comparison";
|
const message = comparison?.message || "Empty comparison";
|
||||||
const hasRows =
|
const hasRows =
|
||||||
@@ -48,6 +53,9 @@ export function Compare(_: Record<string, never>): React.JSX.Element {
|
|||||||
case "setComparisons":
|
case "setComparisons":
|
||||||
setComparison(msg);
|
setComparison(msg);
|
||||||
break;
|
break;
|
||||||
|
case "setUserSettings":
|
||||||
|
setUserSettings(msg.userSettings);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
assertNever(msg);
|
assertNever(msg);
|
||||||
}
|
}
|
||||||
@@ -85,6 +93,7 @@ export function Compare(_: Record<string, never>): React.JSX.Element {
|
|||||||
<CompareTable
|
<CompareTable
|
||||||
queryInfo={queryInfo}
|
queryInfo={queryInfo}
|
||||||
comparison={comparison}
|
comparison={comparison}
|
||||||
|
userSettings={userSettings}
|
||||||
></CompareTable>
|
></CompareTable>
|
||||||
) : (
|
) : (
|
||||||
<Message>{message}</Message>
|
<Message>{message}</Message>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type {
|
import type {
|
||||||
SetComparisonQueryInfoMessage,
|
SetComparisonQueryInfoMessage,
|
||||||
SetComparisonsMessage,
|
SetComparisonsMessage,
|
||||||
|
UserSettings,
|
||||||
} from "../../common/interface-types";
|
} from "../../common/interface-types";
|
||||||
import { className } from "../results/result-table-utils";
|
import { className } from "../results/result-table-utils";
|
||||||
import { vscode } from "../vscode-api";
|
import { vscode } from "../vscode-api";
|
||||||
@@ -12,6 +13,7 @@ import { InterpretedCompareResultTable } from "./InterpretedCompareResultTable";
|
|||||||
interface Props {
|
interface Props {
|
||||||
queryInfo: SetComparisonQueryInfoMessage;
|
queryInfo: SetComparisonQueryInfoMessage;
|
||||||
comparison: SetComparisonsMessage;
|
comparison: SetComparisonsMessage;
|
||||||
|
userSettings: UserSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
const OpenButton = styled(TextButton)`
|
const OpenButton = styled(TextButton)`
|
||||||
@@ -29,7 +31,11 @@ const Table = styled.table`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default function CompareTable({ queryInfo, comparison }: Props) {
|
export default function CompareTable({
|
||||||
|
queryInfo,
|
||||||
|
comparison,
|
||||||
|
userSettings,
|
||||||
|
}: Props) {
|
||||||
const result = comparison.result!;
|
const result = comparison.result!;
|
||||||
|
|
||||||
async function openQuery(kind: "from" | "to") {
|
async function openQuery(kind: "from" | "to") {
|
||||||
@@ -78,6 +84,7 @@ export default function CompareTable({ queryInfo, comparison }: Props) {
|
|||||||
{result.kind === "interpreted" && (
|
{result.kind === "interpreted" && (
|
||||||
<InterpretedCompareResultTable
|
<InterpretedCompareResultTable
|
||||||
results={result.from}
|
results={result.from}
|
||||||
|
userSettings={userSettings}
|
||||||
databaseUri={queryInfo.databaseUri}
|
databaseUri={queryInfo.databaseUri}
|
||||||
sourceLocationPrefix={result.sourceLocationPrefix}
|
sourceLocationPrefix={result.sourceLocationPrefix}
|
||||||
/>
|
/>
|
||||||
@@ -96,6 +103,7 @@ export default function CompareTable({ queryInfo, comparison }: Props) {
|
|||||||
{result.kind === "interpreted" && (
|
{result.kind === "interpreted" && (
|
||||||
<InterpretedCompareResultTable
|
<InterpretedCompareResultTable
|
||||||
results={result.to}
|
results={result.to}
|
||||||
|
userSettings={userSettings}
|
||||||
databaseUri={queryInfo.databaseUri}
|
databaseUri={queryInfo.databaseUri}
|
||||||
sourceLocationPrefix={result.sourceLocationPrefix}
|
sourceLocationPrefix={result.sourceLocationPrefix}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,27 +1,32 @@
|
|||||||
import type { Result } from "sarif";
|
import type { Result, Run } from "sarif";
|
||||||
import { AlertTable } from "../results/AlertTable";
|
import { AlertTable } from "../results/AlertTable";
|
||||||
|
import type { UserSettings } from "../../common/interface-types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
results: Result[];
|
results: Result[];
|
||||||
databaseUri: string;
|
databaseUri: string;
|
||||||
sourceLocationPrefix: string;
|
sourceLocationPrefix: string;
|
||||||
|
run?: Run;
|
||||||
|
userSettings: UserSettings;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const InterpretedCompareResultTable = ({
|
export const InterpretedCompareResultTable = ({
|
||||||
results,
|
results,
|
||||||
databaseUri,
|
databaseUri,
|
||||||
sourceLocationPrefix,
|
sourceLocationPrefix,
|
||||||
|
userSettings,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
return (
|
return (
|
||||||
<AlertTable
|
<AlertTable
|
||||||
results={results}
|
results={results}
|
||||||
|
userSettings={userSettings}
|
||||||
databaseUri={databaseUri}
|
databaseUri={databaseUri}
|
||||||
sourceLocationPrefix={sourceLocationPrefix}
|
sourceLocationPrefix={sourceLocationPrefix}
|
||||||
header={
|
header={
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th colSpan={2}></th>
|
<th colSpan={2}></th>
|
||||||
<th className={`vscode-codeql__alert-message-cell`} colSpan={3}>
|
<th className={`vscode-codeql__alert-message-cell`} colSpan={4}>
|
||||||
Message
|
Message
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Location, Result } from "sarif";
|
import type { Location, Result, Run } from "sarif";
|
||||||
import type {
|
import type {
|
||||||
PathNode,
|
PathNode,
|
||||||
Result as ResultKeysResult,
|
Result as ResultKeysResult,
|
||||||
@@ -7,7 +7,7 @@ import type {
|
|||||||
import { getPath, getPathNode, getResult, keyToString } from "./result-keys";
|
import { getPath, getPathNode, getResult, keyToString } from "./result-keys";
|
||||||
import { className, jumpToLocation } from "./result-table-utils";
|
import { className, jumpToLocation } from "./result-table-utils";
|
||||||
import { onNavigation } from "./navigation";
|
import { onNavigation } from "./navigation";
|
||||||
import type { NavigateMsg } from "../../common/interface-types";
|
import type { NavigateMsg, UserSettings } from "../../common/interface-types";
|
||||||
import { NavigationDirection } from "../../common/interface-types";
|
import { NavigationDirection } from "../../common/interface-types";
|
||||||
import { isNoLocation, parseSarifLocation } from "../../common/sarif-utils";
|
import { isNoLocation, parseSarifLocation } from "../../common/sarif-utils";
|
||||||
import { sendTelemetry } from "../common/telemetry";
|
import { sendTelemetry } from "../common/telemetry";
|
||||||
@@ -21,6 +21,8 @@ type Props = {
|
|||||||
results: Result[];
|
results: Result[];
|
||||||
databaseUri: string;
|
databaseUri: string;
|
||||||
sourceLocationPrefix: string;
|
sourceLocationPrefix: string;
|
||||||
|
run?: Run;
|
||||||
|
userSettings: UserSettings;
|
||||||
numTruncatedResults?: number;
|
numTruncatedResults?: number;
|
||||||
|
|
||||||
header: ReactNode;
|
header: ReactNode;
|
||||||
@@ -31,6 +33,8 @@ export function AlertTable({
|
|||||||
results,
|
results,
|
||||||
databaseUri,
|
databaseUri,
|
||||||
sourceLocationPrefix,
|
sourceLocationPrefix,
|
||||||
|
run,
|
||||||
|
userSettings,
|
||||||
numTruncatedResults,
|
numTruncatedResults,
|
||||||
header,
|
header,
|
||||||
noResults,
|
noResults,
|
||||||
@@ -202,6 +206,8 @@ export function AlertTable({
|
|||||||
selectedItem={selectedItem}
|
selectedItem={selectedItem}
|
||||||
selectedItemRef={selectedItemRef}
|
selectedItemRef={selectedItemRef}
|
||||||
databaseUri={databaseUri}
|
databaseUri={databaseUri}
|
||||||
|
run={run}
|
||||||
|
userSettings={userSettings}
|
||||||
sourceLocationPrefix={sourceLocationPrefix}
|
sourceLocationPrefix={sourceLocationPrefix}
|
||||||
updateSelectionCallback={updateSelectionCallback}
|
updateSelectionCallback={updateSelectionCallback}
|
||||||
toggleExpanded={toggle}
|
toggleExpanded={toggle}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export function AlertTableHeader({
|
|||||||
<th colSpan={2}></th>
|
<th colSpan={2}></th>
|
||||||
<th
|
<th
|
||||||
className={`${sortClass()} vscode-codeql__alert-message-cell`}
|
className={`${sortClass()} vscode-codeql__alert-message-cell`}
|
||||||
colSpan={3}
|
colSpan={4}
|
||||||
onClick={toggleSortStateForColumn}
|
onClick={toggleSortStateForColumn}
|
||||||
>
|
>
|
||||||
Message
|
Message
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { ThreadFlowLocation } from "sarif";
|
import type { ThreadFlowLocation, Run } from "sarif";
|
||||||
import type {
|
import type {
|
||||||
PathNode,
|
PathNode,
|
||||||
Result as ResultKeysResult,
|
Result as ResultKeysResult,
|
||||||
@@ -9,6 +9,8 @@ import { SarifLocation } from "./locations/SarifLocation";
|
|||||||
import { selectableZebraStripe } from "./result-table-utils";
|
import { selectableZebraStripe } from "./result-table-utils";
|
||||||
import { useCallback, useMemo } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
import { VerticalRule } from "../common/VerticalRule";
|
import { VerticalRule } from "../common/VerticalRule";
|
||||||
|
import type { UserSettings } from "../../common/interface-types";
|
||||||
|
import { TaxaLocations } from "./locations/TaxaLocations";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
step: ThreadFlowLocation;
|
step: ThreadFlowLocation;
|
||||||
@@ -19,6 +21,8 @@ interface Props {
|
|||||||
selectedItemRef: React.RefObject<HTMLTableRowElement>;
|
selectedItemRef: React.RefObject<HTMLTableRowElement>;
|
||||||
databaseUri: string;
|
databaseUri: string;
|
||||||
sourceLocationPrefix: string;
|
sourceLocationPrefix: string;
|
||||||
|
run?: Run;
|
||||||
|
userSettings: UserSettings;
|
||||||
updateSelectionCallback: (
|
updateSelectionCallback: (
|
||||||
resultKey: PathNode | ResultKeysResult | undefined,
|
resultKey: PathNode | ResultKeysResult | undefined,
|
||||||
) => void;
|
) => void;
|
||||||
@@ -34,6 +38,8 @@ export function AlertTablePathNodeRow(props: Props) {
|
|||||||
selectedItemRef,
|
selectedItemRef,
|
||||||
databaseUri,
|
databaseUri,
|
||||||
sourceLocationPrefix,
|
sourceLocationPrefix,
|
||||||
|
run,
|
||||||
|
userSettings,
|
||||||
updateSelectionCallback,
|
updateSelectionCallback,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@@ -86,6 +92,23 @@ export function AlertTablePathNodeRow(props: Props) {
|
|||||||
"[no location]"
|
"[no location]"
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
|
<td
|
||||||
|
{...selectableZebraStripe(
|
||||||
|
isSelected,
|
||||||
|
zebraIndex,
|
||||||
|
"vscode-codeql__taxa-cell",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{userSettings.shouldShowProvenance ? (
|
||||||
|
<div className="vscode-codeql__taxa-cell-contents">
|
||||||
|
<TaxaLocations
|
||||||
|
taxa={step.taxa}
|
||||||
|
run={run}
|
||||||
|
onClick={handleSarifLocationClicked}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</td>
|
||||||
<td
|
<td
|
||||||
{...selectableZebraStripe(
|
{...selectableZebraStripe(
|
||||||
isSelected,
|
isSelected,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { ThreadFlow } from "sarif";
|
import type { Run, ThreadFlow } from "sarif";
|
||||||
import type {
|
import type {
|
||||||
PathNode,
|
PathNode,
|
||||||
Result as ResultKeysResult,
|
Result as ResultKeysResult,
|
||||||
@@ -10,6 +10,7 @@ import { AlertTablePathNodeRow } from "./AlertTablePathNodeRow";
|
|||||||
import { AlertTableDropdownIndicatorCell } from "./AlertTableDropdownIndicatorCell";
|
import { AlertTableDropdownIndicatorCell } from "./AlertTableDropdownIndicatorCell";
|
||||||
import { useCallback, useMemo } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
import { VerticalRule } from "../common/VerticalRule";
|
import { VerticalRule } from "../common/VerticalRule";
|
||||||
|
import type { UserSettings } from "../../common/interface-types";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
path: ThreadFlow;
|
path: ThreadFlow;
|
||||||
@@ -20,6 +21,8 @@ interface Props {
|
|||||||
selectedItemRef: React.RefObject<HTMLTableRowElement>;
|
selectedItemRef: React.RefObject<HTMLTableRowElement>;
|
||||||
databaseUri: string;
|
databaseUri: string;
|
||||||
sourceLocationPrefix: string;
|
sourceLocationPrefix: string;
|
||||||
|
run?: Run;
|
||||||
|
userSettings: UserSettings;
|
||||||
updateSelectionCallback: (
|
updateSelectionCallback: (
|
||||||
resultKey: PathNode | ResultKeysResult | undefined,
|
resultKey: PathNode | ResultKeysResult | undefined,
|
||||||
) => void;
|
) => void;
|
||||||
@@ -61,7 +64,7 @@ export function AlertTablePathRow(props: Props) {
|
|||||||
expanded={currentPathExpanded}
|
expanded={currentPathExpanded}
|
||||||
onClick={handleDropdownClick}
|
onClick={handleDropdownClick}
|
||||||
/>
|
/>
|
||||||
<td className="vscode-codeql__text-center" colSpan={3}>
|
<td className="vscode-codeql__text-center" colSpan={4}>
|
||||||
Path
|
Path
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Result } from "sarif";
|
import type { Result, Run } from "sarif";
|
||||||
import type {
|
import type {
|
||||||
PathNode,
|
PathNode,
|
||||||
Result as ResultKeysResult,
|
Result as ResultKeysResult,
|
||||||
@@ -12,6 +12,7 @@ import { useCallback, useMemo } from "react";
|
|||||||
import { SarifLocation } from "./locations/SarifLocation";
|
import { SarifLocation } from "./locations/SarifLocation";
|
||||||
import { SarifMessageWithLocations } from "./locations/SarifMessageWithLocations";
|
import { SarifMessageWithLocations } from "./locations/SarifMessageWithLocations";
|
||||||
import { AlertTablePathRow } from "./AlertTablePathRow";
|
import { AlertTablePathRow } from "./AlertTablePathRow";
|
||||||
|
import type { UserSettings } from "../../common/interface-types";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
result: Result;
|
result: Result;
|
||||||
@@ -21,6 +22,8 @@ interface Props {
|
|||||||
selectedItemRef: React.RefObject<HTMLTableRowElement>;
|
selectedItemRef: React.RefObject<HTMLTableRowElement>;
|
||||||
databaseUri: string;
|
databaseUri: string;
|
||||||
sourceLocationPrefix: string;
|
sourceLocationPrefix: string;
|
||||||
|
run?: Run;
|
||||||
|
userSettings: UserSettings;
|
||||||
updateSelectionCallback: (
|
updateSelectionCallback: (
|
||||||
resultKey: PathNode | ResultKeysResult | undefined,
|
resultKey: PathNode | ResultKeysResult | undefined,
|
||||||
) => void;
|
) => void;
|
||||||
@@ -90,7 +93,7 @@ export function AlertTableResultRow(props: Props) {
|
|||||||
{result.codeFlows === undefined ? (
|
{result.codeFlows === undefined ? (
|
||||||
<>
|
<>
|
||||||
<td className="vscode-codeql__icon-cell">{info}</td>
|
<td className="vscode-codeql__icon-cell">{info}</td>
|
||||||
<td colSpan={3}>{msg}</td>
|
<td colSpan={4}>{msg}</td>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
@@ -99,7 +102,7 @@ export function AlertTableResultRow(props: Props) {
|
|||||||
onClick={handleDropdownClick}
|
onClick={handleDropdownClick}
|
||||||
/>
|
/>
|
||||||
<td className="vscode-codeql__icon-cell">{listUnordered}</td>
|
<td className="vscode-codeql__icon-cell">{listUnordered}</td>
|
||||||
<td colSpan={2}>{msg}</td>
|
<td colSpan={3}>{msg}</td>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<td className="vscode-codeql__location-cell">
|
<td className="vscode-codeql__location-cell">
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { AlertTableNoResults } from "./AlertTableNoResults";
|
|||||||
import { AlertTableHeader } from "./AlertTableHeader";
|
import { AlertTableHeader } from "./AlertTableHeader";
|
||||||
|
|
||||||
export function ResultTable(props: ResultTableProps) {
|
export function ResultTable(props: ResultTableProps) {
|
||||||
const { resultSet } = props;
|
const { resultSet, userSettings } = props;
|
||||||
switch (resultSet.t) {
|
switch (resultSet.t) {
|
||||||
case "RawResultSet":
|
case "RawResultSet":
|
||||||
return <RawTable {...props} resultSet={resultSet.resultSet} />;
|
return <RawTable {...props} resultSet={resultSet.resultSet} />;
|
||||||
@@ -21,6 +21,8 @@ export function ResultTable(props: ResultTableProps) {
|
|||||||
sourceLocationPrefix={
|
sourceLocationPrefix={
|
||||||
resultSet.interpretation.sourceLocationPrefix
|
resultSet.interpretation.sourceLocationPrefix
|
||||||
}
|
}
|
||||||
|
run={data.runs[0]}
|
||||||
|
userSettings={userSettings}
|
||||||
numTruncatedResults={resultSet.interpretation.numTruncatedResults}
|
numTruncatedResults={resultSet.interpretation.numTruncatedResults}
|
||||||
header={<AlertTableHeader sortState={data.sortState} />}
|
header={<AlertTableHeader sortState={data.sortState} />}
|
||||||
noResults={
|
noResults={
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import type {
|
|||||||
ResultSet,
|
ResultSet,
|
||||||
ParsedResultSets,
|
ParsedResultSets,
|
||||||
IntoResultsViewMsg,
|
IntoResultsViewMsg,
|
||||||
|
UserSettings,
|
||||||
} from "../../common/interface-types";
|
} from "../../common/interface-types";
|
||||||
import {
|
import {
|
||||||
ALERTS_TABLE_NAME,
|
ALERTS_TABLE_NAME,
|
||||||
@@ -33,6 +34,7 @@ interface ResultTablesProps {
|
|||||||
rawResultSets: readonly ResultSet[];
|
rawResultSets: readonly ResultSet[];
|
||||||
interpretation: Interpretation | undefined;
|
interpretation: Interpretation | undefined;
|
||||||
database: DatabaseInfo;
|
database: DatabaseInfo;
|
||||||
|
userSettings: UserSettings;
|
||||||
metadata?: QueryMetadata;
|
metadata?: QueryMetadata;
|
||||||
resultsPath: string;
|
resultsPath: string;
|
||||||
origResultsPaths: ResultsPaths;
|
origResultsPaths: ResultsPaths;
|
||||||
@@ -94,6 +96,7 @@ export function ResultTables(props: ResultTablesProps) {
|
|||||||
interpretation,
|
interpretation,
|
||||||
database,
|
database,
|
||||||
resultsPath,
|
resultsPath,
|
||||||
|
userSettings,
|
||||||
metadata,
|
metadata,
|
||||||
origResultsPaths,
|
origResultsPaths,
|
||||||
isLoadingNewResults,
|
isLoadingNewResults,
|
||||||
@@ -242,6 +245,7 @@ export function ResultTables(props: ResultTablesProps) {
|
|||||||
<ResultTable
|
<ResultTable
|
||||||
key={resultSetName}
|
key={resultSetName}
|
||||||
resultSet={resultSet}
|
resultSet={resultSet}
|
||||||
|
userSettings={userSettings}
|
||||||
databaseUri={database.databaseUri}
|
databaseUri={database.databaseUri}
|
||||||
resultsPath={resultsPath}
|
resultsPath={resultsPath}
|
||||||
sortState={sortStates.get(resultSetName)}
|
sortState={sortStates.get(resultSetName)}
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ import type {
|
|||||||
ResultsPaths,
|
ResultsPaths,
|
||||||
ParsedResultSets,
|
ParsedResultSets,
|
||||||
ResultSet,
|
ResultSet,
|
||||||
|
UserSettings,
|
||||||
} from "../../common/interface-types";
|
} from "../../common/interface-types";
|
||||||
import {
|
import {
|
||||||
ALERTS_TABLE_NAME,
|
ALERTS_TABLE_NAME,
|
||||||
|
DEFAULT_USER_SETTINGS,
|
||||||
GRAPH_TABLE_NAME,
|
GRAPH_TABLE_NAME,
|
||||||
} from "../../common/interface-types";
|
} from "../../common/interface-types";
|
||||||
import { ResultTables } from "./ResultTables";
|
import { ResultTables } from "./ResultTables";
|
||||||
@@ -77,6 +79,10 @@ export function ResultsApp() {
|
|||||||
isExpectingResultsUpdate: true,
|
isExpectingResultsUpdate: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [userSettings, setUserSettings] = useState<UserSettings>(
|
||||||
|
DEFAULT_USER_SETTINGS,
|
||||||
|
);
|
||||||
|
|
||||||
const updateStateWithNewResultsInfo = useCallback(
|
const updateStateWithNewResultsInfo = useCallback(
|
||||||
(resultsInfo: ResultsInfo): void => {
|
(resultsInfo: ResultsInfo): void => {
|
||||||
let results: Results | null = null;
|
let results: Results | null = null;
|
||||||
@@ -110,6 +116,10 @@ export function ResultsApp() {
|
|||||||
const handleMessage = useCallback(
|
const handleMessage = useCallback(
|
||||||
(msg: IntoResultsViewMsg): void => {
|
(msg: IntoResultsViewMsg): void => {
|
||||||
switch (msg.t) {
|
switch (msg.t) {
|
||||||
|
case "setUserSettings":
|
||||||
|
setUserSettings(msg.userSettings);
|
||||||
|
break;
|
||||||
|
|
||||||
case "setState":
|
case "setState":
|
||||||
updateStateWithNewResultsInfo({
|
updateStateWithNewResultsInfo({
|
||||||
resultsPath: msg.resultsPath,
|
resultsPath: msg.resultsPath,
|
||||||
@@ -217,6 +227,7 @@ export function ResultsApp() {
|
|||||||
? displayedResults.resultsInfo.interpretation
|
? displayedResults.resultsInfo.interpretation
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
userSettings={userSettings}
|
||||||
database={displayedResults.results.database}
|
database={displayedResults.results.database}
|
||||||
origResultsPaths={displayedResults.resultsInfo.origResultsPaths}
|
origResultsPaths={displayedResults.resultsInfo.origResultsPaths}
|
||||||
resultsPath={displayedResults.resultsInfo.resultsPath}
|
resultsPath={displayedResults.resultsInfo.resultsPath}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import type { UrlValueResolvable } from "../../../common/raw-result-types";
|
|||||||
interface Props {
|
interface Props {
|
||||||
loc: UrlValueResolvable;
|
loc: UrlValueResolvable;
|
||||||
label: string;
|
label: string;
|
||||||
databaseUri: string;
|
databaseUri: string | undefined;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,17 +26,13 @@ export function Location({
|
|||||||
const displayLabel = useMemo(() => convertNonPrintableChars(label), [label]);
|
const displayLabel = useMemo(() => convertNonPrintableChars(label), [label]);
|
||||||
|
|
||||||
if (loc === undefined) {
|
if (loc === undefined) {
|
||||||
return <NonClickableLocation msg={displayLabel} />;
|
return <NonClickableLocation msg={displayLabel} locationHint={title} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loc.type === "string") {
|
if (loc.type === "string") {
|
||||||
return <a href={loc.value}>{loc.value}</a>;
|
return <a href={loc.value}>{loc.value}</a>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (databaseUri === undefined) {
|
|
||||||
return <NonClickableLocation msg={displayLabel} locationHint={title} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ClickableLocation
|
<ClickableLocation
|
||||||
loc={loc}
|
loc={loc}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ interface Props {
|
|||||||
text?: string;
|
text?: string;
|
||||||
loc?: SarifLogLocation;
|
loc?: SarifLogLocation;
|
||||||
sourceLocationPrefix: string;
|
sourceLocationPrefix: string;
|
||||||
databaseUri: string;
|
databaseUri: string | undefined;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,199 @@
|
|||||||
|
import type {
|
||||||
|
Location as SarifLogLocation,
|
||||||
|
ArtifactLocation,
|
||||||
|
PhysicalLocation,
|
||||||
|
ReportingDescriptorReference,
|
||||||
|
Run,
|
||||||
|
ToolComponent,
|
||||||
|
} from "sarif";
|
||||||
|
import { SarifLocation } from "./SarifLocation";
|
||||||
|
|
||||||
|
/** The definition of a taxon for a data extension model row. */
|
||||||
|
interface ModelTaxon {
|
||||||
|
kind: "model";
|
||||||
|
location: SarifLogLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A taxon for a built-in model, such as `AdditionalFlowStep`. */
|
||||||
|
interface BuiltInTaxon {
|
||||||
|
kind: "string";
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type TaxonDefinition = ModelTaxon | BuiltInTaxon;
|
||||||
|
|
||||||
|
/** Resolve an `ArtifactLocation` that might contain a relative reference instead of an absolute
|
||||||
|
* URI.
|
||||||
|
*/
|
||||||
|
function resolveArtifactLocation(
|
||||||
|
location: ArtifactLocation,
|
||||||
|
baseUri: URL,
|
||||||
|
): ArtifactLocation {
|
||||||
|
if (location.uri === undefined) {
|
||||||
|
// No URI at all. Just return the original location.
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...location,
|
||||||
|
uri: new URL(location.uri, baseUri).toString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get the URI of the pack's local root directory, if available. */
|
||||||
|
function getLocalPackUri(extension: ToolComponent): URL | undefined {
|
||||||
|
if (extension.locations === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const localPackLocation = extension.locations.find(
|
||||||
|
(loc) =>
|
||||||
|
loc.properties !== undefined &&
|
||||||
|
loc.properties.tags !== undefined &&
|
||||||
|
loc.properties.tags.includes("CodeQL/LocalPackRoot"),
|
||||||
|
);
|
||||||
|
if (localPackLocation === undefined || localPackLocation.uri === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new URL(localPackLocation.uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Resolve a `ReportingDescriptorReference` to the built-in taxon it refers to, or `undefined` if
|
||||||
|
* it is not a built-in taxon.
|
||||||
|
*/
|
||||||
|
function resolveBuiltInTaxon(
|
||||||
|
taxonRef: ReportingDescriptorReference,
|
||||||
|
): BuiltInTaxon | undefined {
|
||||||
|
if (
|
||||||
|
taxonRef.id !== undefined &&
|
||||||
|
taxonRef.index === undefined &&
|
||||||
|
taxonRef.toolComponent === undefined
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
kind: "string",
|
||||||
|
text: taxonRef.id,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a `ReportingDescriptorReference` to the MaD taxon definition it refers to, or
|
||||||
|
* `undefined` if it does not refer to a MaD model.
|
||||||
|
*/
|
||||||
|
function resolveModelTaxon(
|
||||||
|
taxonRef: ReportingDescriptorReference,
|
||||||
|
run: Run,
|
||||||
|
): ModelTaxon | undefined {
|
||||||
|
const extensions = run.tool.extensions;
|
||||||
|
if (extensions === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const extensionIndex = taxonRef.toolComponent?.index;
|
||||||
|
if (
|
||||||
|
extensionIndex === undefined ||
|
||||||
|
extensionIndex < 0 ||
|
||||||
|
extensionIndex >= extensions.length
|
||||||
|
) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const extension = extensions[extensionIndex];
|
||||||
|
if (extension.taxa === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const localPackUri = getLocalPackUri(extension);
|
||||||
|
if (localPackUri === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const taxonIndex = taxonRef.index;
|
||||||
|
if (
|
||||||
|
taxonIndex === undefined ||
|
||||||
|
taxonIndex < 0 ||
|
||||||
|
taxonIndex >= extension.taxa.length
|
||||||
|
) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const taxonDef = extension.taxa[taxonIndex];
|
||||||
|
if (taxonDef.properties === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const location: PhysicalLocation =
|
||||||
|
taxonDef.properties["CodeQL/DataExtensionLocation"];
|
||||||
|
if (location === undefined || location.artifactLocation === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
kind: "model",
|
||||||
|
location: {
|
||||||
|
physicalLocation: {
|
||||||
|
...location,
|
||||||
|
artifactLocation: resolveArtifactLocation(
|
||||||
|
location.artifactLocation,
|
||||||
|
localPackUri,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Resolve a `ReportingDescriptorReference` to the taxon definition it refers to. */
|
||||||
|
function resolveTaxonDefinition(
|
||||||
|
run: Run,
|
||||||
|
taxonRef: ReportingDescriptorReference,
|
||||||
|
): TaxonDefinition | undefined {
|
||||||
|
return resolveModelTaxon(taxonRef, run) ?? resolveBuiltInTaxon(taxonRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
taxa: ReportingDescriptorReference[] | undefined;
|
||||||
|
run: Run | undefined;
|
||||||
|
onClick: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generate the React elements for each taxon. */
|
||||||
|
export function TaxaLocations({
|
||||||
|
taxa,
|
||||||
|
run,
|
||||||
|
onClick,
|
||||||
|
}: Props): React.JSX.Element[] {
|
||||||
|
if (taxa === undefined || taxa.length === 0 || run === undefined) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return taxa.flatMap((taxonRef, index) => {
|
||||||
|
if (taxonRef.properties === undefined) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const role = taxonRef.properties["CodeQL/DataflowRole"];
|
||||||
|
if (typeof role !== "string") {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const taxonDef = resolveTaxonDefinition(run, taxonRef);
|
||||||
|
if (taxonDef === undefined) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={index}>
|
||||||
|
{`(${role}) `}
|
||||||
|
<SarifLocation
|
||||||
|
loc={taxonDef.kind === "model" ? taxonDef.location : undefined}
|
||||||
|
databaseUri={undefined}
|
||||||
|
text={taxonDef.kind === "string" ? taxonDef.text : undefined}
|
||||||
|
sourceLocationPrefix=""
|
||||||
|
onClick={onClick}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import type {
|
|||||||
QueryMetadata,
|
QueryMetadata,
|
||||||
RawResultsSortState,
|
RawResultsSortState,
|
||||||
ResultSet,
|
ResultSet,
|
||||||
|
UserSettings,
|
||||||
} from "../../common/interface-types";
|
} from "../../common/interface-types";
|
||||||
import { SortDirection } from "../../common/interface-types";
|
import { SortDirection } from "../../common/interface-types";
|
||||||
import { assertNever } from "../../common/helpers-pure";
|
import { assertNever } from "../../common/helpers-pure";
|
||||||
@@ -11,6 +12,7 @@ import type { UrlValueResolvable } from "../../common/raw-result-types";
|
|||||||
export interface ResultTableProps {
|
export interface ResultTableProps {
|
||||||
resultSet: ResultSet;
|
resultSet: ResultSet;
|
||||||
databaseUri: string;
|
databaseUri: string;
|
||||||
|
userSettings: UserSettings;
|
||||||
metadata?: QueryMetadata;
|
metadata?: QueryMetadata;
|
||||||
resultsPath: string | undefined;
|
resultsPath: string | undefined;
|
||||||
sortState?: RawResultsSortState;
|
sortState?: RawResultsSortState;
|
||||||
@@ -41,7 +43,7 @@ export const selectedRowClassName = "vscode-codeql__result-table-row--selected";
|
|||||||
|
|
||||||
export function jumpToLocation(
|
export function jumpToLocation(
|
||||||
loc: UrlValueResolvable,
|
loc: UrlValueResolvable,
|
||||||
databaseUri: string,
|
databaseUri: string | undefined,
|
||||||
): void {
|
): void {
|
||||||
vscode.postMessage({
|
vscode.postMessage({
|
||||||
t: "viewSourceFile",
|
t: "viewSourceFile",
|
||||||
|
|||||||
@@ -144,3 +144,12 @@ td.vscode-codeql__path-index-cell {
|
|||||||
.vscode-codeql__location-cell {
|
.vscode-codeql__location-cell {
|
||||||
text-align: right !important;
|
text-align: right !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vscode-codeql__taxa-cell {
|
||||||
|
text-align: left !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-codeql__taxa-cell-contents {
|
||||||
|
background-color: transparent;
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[
|
[
|
||||||
"v2.17.0",
|
"v2.18.1",
|
||||||
|
"v2.17.6",
|
||||||
"v2.16.6",
|
"v2.16.6",
|
||||||
"v2.15.5",
|
"v2.15.5",
|
||||||
"v2.14.6",
|
|
||||||
"nightly"
|
"nightly"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ function makeTestOctokit(octokit: Octokit): Credentials {
|
|||||||
"getExistingAccessToken not supported by test credentials",
|
"getExistingAccessToken not supported by test credentials",
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
authProviderId: "github",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export function createMockVariantAnalysisConfig(): VariantAnalysisConfig {
|
|||||||
return {
|
return {
|
||||||
controllerRepo: "foo/bar",
|
controllerRepo: "foo/bar",
|
||||||
showSystemDefinedRepositoryLists: true,
|
showSystemDefinedRepositoryLists: true,
|
||||||
|
githubUrl: new URL("https://github.com"),
|
||||||
onDidChangeConfiguration: jest.fn(),
|
onDidChangeConfiguration: jest.fn(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import {
|
|||||||
} from "../../../src/common/github-url-identifier-helper";
|
} from "../../../src/common/github-url-identifier-helper";
|
||||||
|
|
||||||
describe("github url identifier helper", () => {
|
describe("github url identifier helper", () => {
|
||||||
|
const githubUrl = new URL("https://github.com");
|
||||||
|
|
||||||
describe("valid GitHub Nwo Or Owner method", () => {
|
describe("valid GitHub Nwo Or Owner method", () => {
|
||||||
it("should return true for valid owner", () => {
|
it("should return true for valid owner", () => {
|
||||||
expect(isValidGitHubOwner("github")).toBe(true);
|
expect(isValidGitHubOwner("github")).toBe(true);
|
||||||
@@ -23,51 +25,96 @@ describe("github url identifier helper", () => {
|
|||||||
|
|
||||||
describe("getNwoFromGitHubUrl method", () => {
|
describe("getNwoFromGitHubUrl method", () => {
|
||||||
it("should handle invalid urls", () => {
|
it("should handle invalid urls", () => {
|
||||||
expect(getNwoFromGitHubUrl("")).toBe(undefined);
|
expect(getNwoFromGitHubUrl("", githubUrl)).toBe(undefined);
|
||||||
expect(getNwoFromGitHubUrl("https://ww.github.com/foo/bar")).toBe(
|
expect(
|
||||||
|
getNwoFromGitHubUrl("https://ww.github.com/foo/bar", githubUrl),
|
||||||
|
).toBe(undefined);
|
||||||
|
expect(
|
||||||
|
getNwoFromGitHubUrl("https://tenant.ghe.com/foo/bar", githubUrl),
|
||||||
|
).toBe(undefined);
|
||||||
|
expect(getNwoFromGitHubUrl("https://www.github.com/foo", githubUrl)).toBe(
|
||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
expect(getNwoFromGitHubUrl("https://www.github.com/foo")).toBe(undefined);
|
expect(getNwoFromGitHubUrl("foo", githubUrl)).toBe(undefined);
|
||||||
expect(getNwoFromGitHubUrl("foo")).toBe(undefined);
|
expect(getNwoFromGitHubUrl("foo/bar", githubUrl)).toBe(undefined);
|
||||||
expect(getNwoFromGitHubUrl("foo/bar")).toBe(undefined);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should handle valid urls", () => {
|
it("should handle valid urls", () => {
|
||||||
expect(getNwoFromGitHubUrl("github.com/foo/bar")).toBe("foo/bar");
|
expect(getNwoFromGitHubUrl("github.com/foo/bar", githubUrl)).toBe(
|
||||||
expect(getNwoFromGitHubUrl("www.github.com/foo/bar")).toBe("foo/bar");
|
|
||||||
expect(getNwoFromGitHubUrl("https://github.com/foo/bar")).toBe("foo/bar");
|
|
||||||
expect(getNwoFromGitHubUrl("http://github.com/foo/bar")).toBe("foo/bar");
|
|
||||||
expect(getNwoFromGitHubUrl("https://www.github.com/foo/bar")).toBe(
|
|
||||||
"foo/bar",
|
"foo/bar",
|
||||||
);
|
);
|
||||||
expect(getNwoFromGitHubUrl("https://github.com/foo/bar/sub/pages")).toBe(
|
expect(getNwoFromGitHubUrl("www.github.com/foo/bar", githubUrl)).toBe(
|
||||||
"foo/bar",
|
"foo/bar",
|
||||||
);
|
);
|
||||||
|
expect(getNwoFromGitHubUrl("https://github.com/foo/bar", githubUrl)).toBe(
|
||||||
|
"foo/bar",
|
||||||
|
);
|
||||||
|
expect(getNwoFromGitHubUrl("http://github.com/foo/bar", githubUrl)).toBe(
|
||||||
|
"foo/bar",
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
getNwoFromGitHubUrl("https://www.github.com/foo/bar", githubUrl),
|
||||||
|
).toBe("foo/bar");
|
||||||
|
expect(
|
||||||
|
getNwoFromGitHubUrl("https://github.com/foo/bar/sub/pages", githubUrl),
|
||||||
|
).toBe("foo/bar");
|
||||||
|
expect(
|
||||||
|
getNwoFromGitHubUrl(
|
||||||
|
"https://tenant.ghe.com/foo/bar",
|
||||||
|
new URL("https://tenant.ghe.com"),
|
||||||
|
),
|
||||||
|
).toBe("foo/bar");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("getOwnerFromGitHubUrl method", () => {
|
describe("getOwnerFromGitHubUrl method", () => {
|
||||||
it("should handle invalid urls", () => {
|
it("should handle invalid urls", () => {
|
||||||
expect(getOwnerFromGitHubUrl("")).toBe(undefined);
|
expect(getOwnerFromGitHubUrl("", githubUrl)).toBe(undefined);
|
||||||
expect(getOwnerFromGitHubUrl("https://ww.github.com/foo/bar")).toBe(
|
expect(
|
||||||
undefined,
|
getOwnerFromGitHubUrl("https://ww.github.com/foo/bar", githubUrl),
|
||||||
);
|
).toBe(undefined);
|
||||||
expect(getOwnerFromGitHubUrl("foo")).toBe(undefined);
|
expect(
|
||||||
expect(getOwnerFromGitHubUrl("foo/bar")).toBe(undefined);
|
getOwnerFromGitHubUrl("https://tenant.ghe.com/foo/bar", githubUrl),
|
||||||
|
).toBe(undefined);
|
||||||
|
expect(getOwnerFromGitHubUrl("foo", githubUrl)).toBe(undefined);
|
||||||
|
expect(getOwnerFromGitHubUrl("foo/bar", githubUrl)).toBe(undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should handle valid urls", () => {
|
it("should handle valid urls", () => {
|
||||||
expect(getOwnerFromGitHubUrl("http://github.com/foo/bar")).toBe("foo");
|
expect(
|
||||||
expect(getOwnerFromGitHubUrl("https://github.com/foo/bar")).toBe("foo");
|
getOwnerFromGitHubUrl("http://github.com/foo/bar", githubUrl),
|
||||||
expect(getOwnerFromGitHubUrl("https://www.github.com/foo/bar")).toBe(
|
).toBe("foo");
|
||||||
|
expect(
|
||||||
|
getOwnerFromGitHubUrl("https://github.com/foo/bar", githubUrl),
|
||||||
|
).toBe("foo");
|
||||||
|
expect(
|
||||||
|
getOwnerFromGitHubUrl("https://www.github.com/foo/bar", githubUrl),
|
||||||
|
).toBe("foo");
|
||||||
|
expect(
|
||||||
|
getOwnerFromGitHubUrl(
|
||||||
|
"https://github.com/foo/bar/sub/pages",
|
||||||
|
githubUrl,
|
||||||
|
),
|
||||||
|
).toBe("foo");
|
||||||
|
expect(
|
||||||
|
getOwnerFromGitHubUrl("https://www.github.com/foo", githubUrl),
|
||||||
|
).toBe("foo");
|
||||||
|
expect(getOwnerFromGitHubUrl("github.com/foo", githubUrl)).toBe("foo");
|
||||||
|
expect(getOwnerFromGitHubUrl("www.github.com/foo", githubUrl)).toBe(
|
||||||
"foo",
|
"foo",
|
||||||
);
|
);
|
||||||
expect(
|
expect(
|
||||||
getOwnerFromGitHubUrl("https://github.com/foo/bar/sub/pages"),
|
getOwnerFromGitHubUrl(
|
||||||
|
"https://tenant.ghe.com/foo/bar",
|
||||||
|
new URL("https://tenant.ghe.com"),
|
||||||
|
),
|
||||||
|
).toBe("foo");
|
||||||
|
expect(
|
||||||
|
getOwnerFromGitHubUrl(
|
||||||
|
"https://tenant.ghe.com/foo",
|
||||||
|
new URL("https://tenant.ghe.com"),
|
||||||
|
),
|
||||||
).toBe("foo");
|
).toBe("foo");
|
||||||
expect(getOwnerFromGitHubUrl("https://www.github.com/foo")).toBe("foo");
|
|
||||||
expect(getOwnerFromGitHubUrl("github.com/foo")).toBe("foo");
|
|
||||||
expect(getOwnerFromGitHubUrl("www.github.com/foo")).toBe("foo");
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -92,32 +92,52 @@ describe("getDbItemActions", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("getGitHubUrl", () => {
|
describe("getGitHubUrl", () => {
|
||||||
it("should return the correct url for a remote owner", () => {
|
const githubUrl = new URL("https://github.com");
|
||||||
|
|
||||||
|
it("should return the correct url for a remote owner with github.com", () => {
|
||||||
const dbItem = createRemoteOwnerDbItem();
|
const dbItem = createRemoteOwnerDbItem();
|
||||||
|
|
||||||
const actualUrl = getGitHubUrl(dbItem);
|
const actualUrl = getGitHubUrl(dbItem, githubUrl);
|
||||||
const expectedUrl = `https://github.com/${dbItem.ownerName}`;
|
const expectedUrl = `https://github.com/${dbItem.ownerName}`;
|
||||||
|
|
||||||
expect(actualUrl).toEqual(expectedUrl);
|
expect(actualUrl).toEqual(expectedUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return the correct url for a remote repo", () => {
|
it("should return the correct url for a remote owner with GHEC-DR", () => {
|
||||||
|
const dbItem = createRemoteOwnerDbItem();
|
||||||
|
|
||||||
|
const actualUrl = getGitHubUrl(dbItem, new URL("https://tenant.ghe.com"));
|
||||||
|
const expectedUrl = `https://tenant.ghe.com/${dbItem.ownerName}`;
|
||||||
|
|
||||||
|
expect(actualUrl).toEqual(expectedUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return the correct url for a remote repo with github.com", () => {
|
||||||
const dbItem = createRemoteRepoDbItem();
|
const dbItem = createRemoteRepoDbItem();
|
||||||
|
|
||||||
const actualUrl = getGitHubUrl(dbItem);
|
const actualUrl = getGitHubUrl(dbItem, githubUrl);
|
||||||
const expectedUrl = `https://github.com/${dbItem.repoFullName}`;
|
const expectedUrl = `https://github.com/${dbItem.repoFullName}`;
|
||||||
|
|
||||||
expect(actualUrl).toEqual(expectedUrl);
|
expect(actualUrl).toEqual(expectedUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should return the correct url for a remote repo with GHEC-DR", () => {
|
||||||
|
const dbItem = createRemoteRepoDbItem();
|
||||||
|
|
||||||
|
const actualUrl = getGitHubUrl(dbItem, new URL("https://tenant.ghe.com"));
|
||||||
|
const expectedUrl = `https://tenant.ghe.com/${dbItem.repoFullName}`;
|
||||||
|
|
||||||
|
expect(actualUrl).toEqual(expectedUrl);
|
||||||
|
});
|
||||||
|
|
||||||
it("should return undefined for other remote db items", () => {
|
it("should return undefined for other remote db items", () => {
|
||||||
const dbItem0 = createRootRemoteDbItem();
|
const dbItem0 = createRootRemoteDbItem();
|
||||||
const dbItem1 = createRemoteSystemDefinedListDbItem();
|
const dbItem1 = createRemoteSystemDefinedListDbItem();
|
||||||
const dbItem2 = createRemoteUserDefinedListDbItem();
|
const dbItem2 = createRemoteUserDefinedListDbItem();
|
||||||
|
|
||||||
const actualUrl0 = getGitHubUrl(dbItem0);
|
const actualUrl0 = getGitHubUrl(dbItem0, githubUrl);
|
||||||
const actualUrl1 = getGitHubUrl(dbItem1);
|
const actualUrl1 = getGitHubUrl(dbItem1, githubUrl);
|
||||||
const actualUrl2 = getGitHubUrl(dbItem2);
|
const actualUrl2 = getGitHubUrl(dbItem2, githubUrl);
|
||||||
|
|
||||||
expect(actualUrl0).toBeUndefined();
|
expect(actualUrl0).toBeUndefined();
|
||||||
expect(actualUrl1).toBeUndefined();
|
expect(actualUrl1).toBeUndefined();
|
||||||
|
|||||||
@@ -1,16 +1,59 @@
|
|||||||
import {
|
import {
|
||||||
parsePythonAccessPath,
|
parsePythonAccessPath,
|
||||||
|
parsePythonType,
|
||||||
pythonEndpointType,
|
pythonEndpointType,
|
||||||
pythonPath,
|
pythonPath,
|
||||||
} from "../../../../../src/model-editor/languages/python/access-paths";
|
} from "../../../../../src/model-editor/languages/python/access-paths";
|
||||||
import { EndpointType } from "../../../../../src/model-editor/method";
|
import { EndpointType } from "../../../../../src/model-editor/method";
|
||||||
|
|
||||||
|
describe("parsePythonType", () => {
|
||||||
|
it("parses a type with a package", () => {
|
||||||
|
expect(parsePythonType("requests.utils")).toEqual({
|
||||||
|
packageName: "requests",
|
||||||
|
typeName: "utils",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("parses a nested type with a package", () => {
|
||||||
|
expect(parsePythonType("requests.adapters.HTTPAdapter")).toEqual({
|
||||||
|
packageName: "requests",
|
||||||
|
typeName: "adapters.HTTPAdapter",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("parses a package without a type", () => {
|
||||||
|
expect(parsePythonType("requests")).toEqual({
|
||||||
|
packageName: "requests",
|
||||||
|
typeName: "",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("parses an empty string", () => {
|
||||||
|
expect(parsePythonType("")).toEqual({
|
||||||
|
packageName: "",
|
||||||
|
typeName: "",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const testCases: Array<{
|
const testCases: Array<{
|
||||||
path: string;
|
path: string;
|
||||||
|
shortTypeName: string;
|
||||||
method: ReturnType<typeof parsePythonAccessPath>;
|
method: ReturnType<typeof parsePythonAccessPath>;
|
||||||
}> = [
|
}> = [
|
||||||
{
|
{
|
||||||
path: "Member[CommonTokens].Member[Class].Instance.Member[foo]",
|
path: "Member[CommonTokens].Member[Class].Instance.Member[foo]",
|
||||||
|
shortTypeName: "",
|
||||||
|
method: {
|
||||||
|
typeName: "CommonTokens.Class",
|
||||||
|
methodName: "foo",
|
||||||
|
endpointType: EndpointType.Method,
|
||||||
|
path: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "Member[foo]",
|
||||||
|
shortTypeName: "CommonTokens.Class",
|
||||||
method: {
|
method: {
|
||||||
typeName: "CommonTokens.Class",
|
typeName: "CommonTokens.Class",
|
||||||
methodName: "foo",
|
methodName: "foo",
|
||||||
@@ -20,6 +63,17 @@ const testCases: Array<{
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "Member[CommonTokens].Member[Class].Instance.Member[foo].Parameter[self]",
|
path: "Member[CommonTokens].Member[Class].Instance.Member[foo].Parameter[self]",
|
||||||
|
shortTypeName: "",
|
||||||
|
method: {
|
||||||
|
typeName: "CommonTokens.Class",
|
||||||
|
methodName: "foo",
|
||||||
|
endpointType: EndpointType.Method,
|
||||||
|
path: "Parameter[self]",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "Member[foo].Parameter[self]",
|
||||||
|
shortTypeName: "CommonTokens.Class",
|
||||||
method: {
|
method: {
|
||||||
typeName: "CommonTokens.Class",
|
typeName: "CommonTokens.Class",
|
||||||
methodName: "foo",
|
methodName: "foo",
|
||||||
@@ -29,6 +83,7 @@ const testCases: Array<{
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "Member[getSource].ReturnValue",
|
path: "Member[getSource].ReturnValue",
|
||||||
|
shortTypeName: "",
|
||||||
method: {
|
method: {
|
||||||
typeName: "",
|
typeName: "",
|
||||||
methodName: "getSource",
|
methodName: "getSource",
|
||||||
@@ -38,6 +93,17 @@ const testCases: Array<{
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "Member[CommonTokens].Member[makePromise].ReturnValue.Awaited",
|
path: "Member[CommonTokens].Member[makePromise].ReturnValue.Awaited",
|
||||||
|
shortTypeName: "",
|
||||||
|
method: {
|
||||||
|
typeName: "CommonTokens",
|
||||||
|
methodName: "makePromise",
|
||||||
|
endpointType: EndpointType.Function,
|
||||||
|
path: "ReturnValue.Awaited",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "Member[makePromise].ReturnValue.Awaited",
|
||||||
|
shortTypeName: "CommonTokens!",
|
||||||
method: {
|
method: {
|
||||||
typeName: "CommonTokens",
|
typeName: "CommonTokens",
|
||||||
methodName: "makePromise",
|
methodName: "makePromise",
|
||||||
@@ -47,6 +113,17 @@ const testCases: Array<{
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "Member[ArgPos].Member[anyParam].Argument[any]",
|
path: "Member[ArgPos].Member[anyParam].Argument[any]",
|
||||||
|
shortTypeName: "",
|
||||||
|
method: {
|
||||||
|
typeName: "ArgPos",
|
||||||
|
methodName: "anyParam",
|
||||||
|
endpointType: EndpointType.Function,
|
||||||
|
path: "Argument[any]",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "Member[anyParam].Argument[any]",
|
||||||
|
shortTypeName: "ArgPos!",
|
||||||
method: {
|
method: {
|
||||||
typeName: "ArgPos",
|
typeName: "ArgPos",
|
||||||
methodName: "anyParam",
|
methodName: "anyParam",
|
||||||
@@ -56,6 +133,17 @@ const testCases: Array<{
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "Member[ArgPos].Instance.Member[self_thing].Argument[self]",
|
path: "Member[ArgPos].Instance.Member[self_thing].Argument[self]",
|
||||||
|
shortTypeName: "",
|
||||||
|
method: {
|
||||||
|
typeName: "ArgPos",
|
||||||
|
methodName: "self_thing",
|
||||||
|
endpointType: EndpointType.Method,
|
||||||
|
path: "Argument[self]",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "Member[self_thing].Argument[self]",
|
||||||
|
shortTypeName: "ArgPos",
|
||||||
method: {
|
method: {
|
||||||
typeName: "ArgPos",
|
typeName: "ArgPos",
|
||||||
methodName: "self_thing",
|
methodName: "self_thing",
|
||||||
@@ -66,44 +154,138 @@ const testCases: Array<{
|
|||||||
];
|
];
|
||||||
|
|
||||||
describe("parsePythonAccessPath", () => {
|
describe("parsePythonAccessPath", () => {
|
||||||
it.each(testCases)("parses $path", ({ path, method }) => {
|
it.each(testCases)(
|
||||||
expect(parsePythonAccessPath(path)).toEqual(method);
|
"parses $path with $shortTypeName",
|
||||||
});
|
({ path, shortTypeName, method }) => {
|
||||||
|
expect(parsePythonAccessPath(path, shortTypeName)).toEqual(method);
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("pythonPath", () => {
|
describe("pythonPath", () => {
|
||||||
it.each(testCases)("constructs $path", ({ path, method }) => {
|
it("returns empty for an empty method name", () => {
|
||||||
expect(
|
expect(pythonPath("", "ReturnValue")).toEqual("ReturnValue");
|
||||||
pythonPath(
|
});
|
||||||
method.typeName,
|
|
||||||
method.methodName,
|
it("returns empty for an empty path", () => {
|
||||||
method.endpointType,
|
expect(pythonPath("foo", "")).toEqual("Member[foo]");
|
||||||
method.path,
|
});
|
||||||
),
|
|
||||||
).toEqual(path);
|
it("returns correctly for a full method name and path", () => {
|
||||||
|
expect(pythonPath("foo", "ReturnValue")).toEqual("Member[foo].ReturnValue");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("pythonEndpointType", () => {
|
describe("pythonEndpointType", () => {
|
||||||
it("returns method for a method", () => {
|
it("returns method for a method", () => {
|
||||||
expect(
|
expect(
|
||||||
pythonEndpointType({
|
pythonEndpointType(
|
||||||
packageName: "testlib",
|
{
|
||||||
typeName: "CommonTokens",
|
packageName: "testlib",
|
||||||
methodName: "foo",
|
typeName: "CommonTokens",
|
||||||
methodParameters: "(self,a)",
|
methodName: "foo",
|
||||||
}),
|
methodParameters: "(self,a)",
|
||||||
|
},
|
||||||
|
"InstanceMethod",
|
||||||
|
),
|
||||||
).toEqual(EndpointType.Method);
|
).toEqual(EndpointType.Method);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("returns class method for a class method", () => {
|
||||||
|
expect(
|
||||||
|
pythonEndpointType(
|
||||||
|
{
|
||||||
|
packageName: "testlib",
|
||||||
|
typeName: "CommonTokens",
|
||||||
|
methodName: "foo",
|
||||||
|
methodParameters: "(cls,a)",
|
||||||
|
},
|
||||||
|
"ClassMethod",
|
||||||
|
),
|
||||||
|
).toEqual(EndpointType.ClassMethod);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns static method for a static method", () => {
|
||||||
|
expect(
|
||||||
|
pythonEndpointType(
|
||||||
|
{
|
||||||
|
packageName: "testlib",
|
||||||
|
typeName: "CommonTokens",
|
||||||
|
methodName: "foo",
|
||||||
|
methodParameters: "(a)",
|
||||||
|
},
|
||||||
|
"StaticMethod",
|
||||||
|
),
|
||||||
|
).toEqual(EndpointType.StaticMethod);
|
||||||
|
});
|
||||||
|
|
||||||
it("returns function for a function", () => {
|
it("returns function for a function", () => {
|
||||||
expect(
|
expect(
|
||||||
pythonEndpointType({
|
pythonEndpointType(
|
||||||
packageName: "testlib",
|
{
|
||||||
typeName: "CommonTokens",
|
packageName: "testlib",
|
||||||
methodName: "foo",
|
typeName: "",
|
||||||
methodParameters: "(a)",
|
methodName: "foo",
|
||||||
}),
|
methodParameters: "(a)",
|
||||||
|
},
|
||||||
|
"Function",
|
||||||
|
),
|
||||||
|
).toEqual(EndpointType.Function);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns constructor for an init method", () => {
|
||||||
|
expect(
|
||||||
|
pythonEndpointType(
|
||||||
|
{
|
||||||
|
packageName: "testlib",
|
||||||
|
typeName: "CommonTokens",
|
||||||
|
methodName: "foo",
|
||||||
|
methodParameters: "(a)",
|
||||||
|
},
|
||||||
|
"InitMethod",
|
||||||
|
),
|
||||||
|
).toEqual(EndpointType.Constructor);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns class for a class", () => {
|
||||||
|
expect(
|
||||||
|
pythonEndpointType(
|
||||||
|
{
|
||||||
|
packageName: "testlib",
|
||||||
|
typeName: "CommonTokens",
|
||||||
|
methodName: "",
|
||||||
|
methodParameters: "",
|
||||||
|
},
|
||||||
|
"Class",
|
||||||
|
),
|
||||||
|
).toEqual(EndpointType.Class);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns method for a method without endpoint kind", () => {
|
||||||
|
expect(
|
||||||
|
pythonEndpointType(
|
||||||
|
{
|
||||||
|
packageName: "testlib",
|
||||||
|
typeName: "CommonTokens",
|
||||||
|
methodName: "foo",
|
||||||
|
methodParameters: "(self,a)",
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
),
|
||||||
|
).toEqual(EndpointType.Method);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns function for a function without endpoint kind", () => {
|
||||||
|
expect(
|
||||||
|
pythonEndpointType(
|
||||||
|
{
|
||||||
|
packageName: "testlib",
|
||||||
|
typeName: "CommonTokens",
|
||||||
|
methodName: "foo",
|
||||||
|
methodParameters: "(a)",
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
),
|
||||||
).toEqual(EndpointType.Function);
|
).toEqual(EndpointType.Function);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -151,13 +151,29 @@ describe("isVariantAnalysisComplete", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("getActionsWorkflowRunUrl", () => {
|
describe("getActionsWorkflowRunUrl", () => {
|
||||||
it("should get the run url", () => {
|
it("should get the run url on github.com", () => {
|
||||||
const variantAnalysis = createMockVariantAnalysis({});
|
const variantAnalysis = createMockVariantAnalysis({});
|
||||||
|
|
||||||
const actionsWorkflowRunUrl = getActionsWorkflowRunUrl(variantAnalysis);
|
const actionsWorkflowRunUrl = getActionsWorkflowRunUrl(
|
||||||
|
variantAnalysis,
|
||||||
|
new URL("https://github.com"),
|
||||||
|
);
|
||||||
|
|
||||||
expect(actionsWorkflowRunUrl).toBe(
|
expect(actionsWorkflowRunUrl).toBe(
|
||||||
`https://github.com/${variantAnalysis.controllerRepo.fullName}/actions/runs/${variantAnalysis.actionsWorkflowRunId}`,
|
`https://github.com/${variantAnalysis.controllerRepo.fullName}/actions/runs/${variantAnalysis.actionsWorkflowRunId}`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should get the run url on GHEC-DR", () => {
|
||||||
|
const variantAnalysis = createMockVariantAnalysis({});
|
||||||
|
|
||||||
|
const actionsWorkflowRunUrl = getActionsWorkflowRunUrl(
|
||||||
|
variantAnalysis,
|
||||||
|
new URL("https://tenant.ghe.com"),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(actionsWorkflowRunUrl).toBe(
|
||||||
|
`https://tenant.ghe.com/${variantAnalysis.controllerRepo.fullName}/actions/runs/${variantAnalysis.actionsWorkflowRunId}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import { handleRequestError } from "../../../src/variant-analysis/custom-errors"
|
|||||||
import { faker } from "@faker-js/faker";
|
import { faker } from "@faker-js/faker";
|
||||||
|
|
||||||
describe("handleRequestError", () => {
|
describe("handleRequestError", () => {
|
||||||
|
const githubUrl = new URL("https://github.com");
|
||||||
const logger = createMockLogger();
|
const logger = createMockLogger();
|
||||||
|
|
||||||
it("returns false when handling a non-422 error", () => {
|
it("returns false when handling a non-422 error", () => {
|
||||||
const e = mockRequestError(404, {
|
const e = mockRequestError(404, {
|
||||||
message: "Not Found",
|
message: "Not Found",
|
||||||
});
|
});
|
||||||
expect(handleRequestError(e, logger)).toBe(false);
|
expect(handleRequestError(e, githubUrl, logger)).toBe(false);
|
||||||
expect(logger.showErrorMessage).not.toHaveBeenCalled();
|
expect(logger.showErrorMessage).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -19,13 +20,13 @@ describe("handleRequestError", () => {
|
|||||||
message:
|
message:
|
||||||
"Unable to trigger a variant analysis. None of the requested repositories could be found.",
|
"Unable to trigger a variant analysis. None of the requested repositories could be found.",
|
||||||
});
|
});
|
||||||
expect(handleRequestError(e, logger)).toBe(false);
|
expect(handleRequestError(e, githubUrl, logger)).toBe(false);
|
||||||
expect(logger.showErrorMessage).not.toHaveBeenCalled();
|
expect(logger.showErrorMessage).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns false when handling an error without response body", () => {
|
it("returns false when handling an error without response body", () => {
|
||||||
const e = mockRequestError(422, undefined);
|
const e = mockRequestError(422, undefined);
|
||||||
expect(handleRequestError(e, logger)).toBe(false);
|
expect(handleRequestError(e, githubUrl, logger)).toBe(false);
|
||||||
expect(logger.showErrorMessage).not.toHaveBeenCalled();
|
expect(logger.showErrorMessage).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ describe("handleRequestError", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(handleRequestError(e, logger)).toBe(false);
|
expect(handleRequestError(e, githubUrl, logger)).toBe(false);
|
||||||
expect(logger.showErrorMessage).not.toHaveBeenCalled();
|
expect(logger.showErrorMessage).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -58,7 +59,7 @@ describe("handleRequestError", () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
expect(handleRequestError(e, logger)).toBe(false);
|
expect(handleRequestError(e, githubUrl, logger)).toBe(false);
|
||||||
expect(logger.showErrorMessage).not.toHaveBeenCalled();
|
expect(logger.showErrorMessage).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -75,7 +76,7 @@ describe("handleRequestError", () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
expect(handleRequestError(e, logger)).toBe(false);
|
expect(handleRequestError(e, githubUrl, logger)).toBe(false);
|
||||||
expect(logger.showErrorMessage).not.toHaveBeenCalled();
|
expect(logger.showErrorMessage).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -92,11 +93,11 @@ describe("handleRequestError", () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
expect(handleRequestError(e, logger)).toBe(false);
|
expect(handleRequestError(e, githubUrl, logger)).toBe(false);
|
||||||
expect(logger.showErrorMessage).not.toHaveBeenCalled();
|
expect(logger.showErrorMessage).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows notification when handling a missing default branch error", () => {
|
it("shows notification when handling a missing default branch error with github.com URL", () => {
|
||||||
const e = mockRequestError(422, {
|
const e = mockRequestError(422, {
|
||||||
message:
|
message:
|
||||||
"Variant analysis failed because controller repository github/pickles does not have a branch 'main'. Please create a 'main' branch in the repository and re-run the variant analysis.",
|
"Variant analysis failed because controller repository github/pickles does not have a branch 'main'. Please create a 'main' branch in the repository and re-run the variant analysis.",
|
||||||
@@ -110,11 +111,33 @@ describe("handleRequestError", () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
expect(handleRequestError(e, logger)).toBe(true);
|
expect(handleRequestError(e, githubUrl, logger)).toBe(true);
|
||||||
expect(logger.showErrorMessage).toHaveBeenCalledWith(
|
expect(logger.showErrorMessage).toHaveBeenCalledWith(
|
||||||
"Variant analysis failed because the controller repository github/pickles does not have a branch 'main'. Please create a 'main' branch by clicking [here](https://github.com/github/pickles/new/main) and re-run the variant analysis query.",
|
"Variant analysis failed because the controller repository github/pickles does not have a branch 'main'. Please create a 'main' branch by clicking [here](https://github.com/github/pickles/new/main) and re-run the variant analysis query.",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("shows notification when handling a missing default branch error with GHEC-DR URL", () => {
|
||||||
|
const e = mockRequestError(422, {
|
||||||
|
message:
|
||||||
|
"Variant analysis failed because controller repository github/pickles does not have a branch 'main'. Please create a 'main' branch in the repository and re-run the variant analysis.",
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
resource: "Repository",
|
||||||
|
field: "default_branch",
|
||||||
|
code: "missing",
|
||||||
|
repository: "github/pickles",
|
||||||
|
default_branch: "main",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
handleRequestError(e, new URL("https://tenant.ghe.com"), logger),
|
||||||
|
).toBe(true);
|
||||||
|
expect(logger.showErrorMessage).toHaveBeenCalledWith(
|
||||||
|
"Variant analysis failed because the controller repository github/pickles does not have a branch 'main'. Please create a 'main' branch by clicking [here](https://tenant.ghe.com/github/pickles/new/main) and re-run the variant analysis query.",
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function mockRequestError(status: number, body: any): RequestError {
|
function mockRequestError(status: number, body: any): RequestError {
|
||||||
|
|||||||
@@ -78,8 +78,10 @@ describe("Variant Analysis Manager", () => {
|
|||||||
new DbConfigStore(app),
|
new DbConfigStore(app),
|
||||||
createMockVariantAnalysisConfig(),
|
createMockVariantAnalysisConfig(),
|
||||||
);
|
);
|
||||||
|
const variantAnalysisConfig = createMockVariantAnalysisConfig();
|
||||||
variantAnalysisResultsManager = new VariantAnalysisResultsManager(
|
variantAnalysisResultsManager = new VariantAnalysisResultsManager(
|
||||||
cli,
|
cli,
|
||||||
|
variantAnalysisConfig,
|
||||||
extLogger,
|
extLogger,
|
||||||
);
|
);
|
||||||
variantAnalysisManager = new VariantAnalysisManager(
|
variantAnalysisManager = new VariantAnalysisManager(
|
||||||
@@ -88,6 +90,7 @@ describe("Variant Analysis Manager", () => {
|
|||||||
storagePath,
|
storagePath,
|
||||||
variantAnalysisResultsManager,
|
variantAnalysisResultsManager,
|
||||||
dbManager,
|
dbManager,
|
||||||
|
variantAnalysisConfig,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import type {
|
|||||||
VariantAnalysisScannedRepositoryResult,
|
VariantAnalysisScannedRepositoryResult,
|
||||||
} from "../../../../src/variant-analysis/shared/variant-analysis";
|
} from "../../../../src/variant-analysis/shared/variant-analysis";
|
||||||
import { mockedObject } from "../../utils/mocking.helpers";
|
import { mockedObject } from "../../utils/mocking.helpers";
|
||||||
|
import { createMockVariantAnalysisConfig } from "../../../factories/config";
|
||||||
|
|
||||||
jest.setTimeout(10_000);
|
jest.setTimeout(10_000);
|
||||||
|
|
||||||
@@ -27,8 +28,10 @@ describe(VariantAnalysisResultsManager.name, () => {
|
|||||||
variantAnalysisId = faker.number.int();
|
variantAnalysisId = faker.number.int();
|
||||||
|
|
||||||
const cli = mockedObject<CodeQLCliServer>({});
|
const cli = mockedObject<CodeQLCliServer>({});
|
||||||
|
const variantAnalysisConfig = createMockVariantAnalysisConfig();
|
||||||
variantAnalysisResultsManager = new VariantAnalysisResultsManager(
|
variantAnalysisResultsManager = new VariantAnalysisResultsManager(
|
||||||
cli,
|
cli,
|
||||||
|
variantAnalysisConfig,
|
||||||
extLogger,
|
extLogger,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -56,8 +56,10 @@ describe("Variant Analysis Manager", () => {
|
|||||||
new DbConfigStore(app),
|
new DbConfigStore(app),
|
||||||
createMockVariantAnalysisConfig(),
|
createMockVariantAnalysisConfig(),
|
||||||
);
|
);
|
||||||
|
const variantAnalysisConfig = createMockVariantAnalysisConfig();
|
||||||
const variantAnalysisResultsManager = new VariantAnalysisResultsManager(
|
const variantAnalysisResultsManager = new VariantAnalysisResultsManager(
|
||||||
cli,
|
cli,
|
||||||
|
variantAnalysisConfig,
|
||||||
extLogger,
|
extLogger,
|
||||||
);
|
);
|
||||||
variantAnalysisManager = new VariantAnalysisManager(
|
variantAnalysisManager = new VariantAnalysisManager(
|
||||||
@@ -66,6 +68,7 @@ describe("Variant Analysis Manager", () => {
|
|||||||
storagePath,
|
storagePath,
|
||||||
variantAnalysisResultsManager,
|
variantAnalysisResultsManager,
|
||||||
dbManager,
|
dbManager,
|
||||||
|
variantAnalysisConfig,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -140,7 +140,8 @@ describe("local databases", () => {
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
expect(onDidChangeDatabaseItem).toHaveBeenCalledWith({
|
expect(onDidChangeDatabaseItem).toHaveBeenCalledWith({
|
||||||
item: undefined,
|
fullRefresh: true,
|
||||||
|
item: mockDbItem,
|
||||||
kind: DatabaseEventKind.Add,
|
kind: DatabaseEventKind.Add,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -152,7 +153,8 @@ describe("local databases", () => {
|
|||||||
expect((databaseManager as any)._databaseItems).toEqual([]);
|
expect((databaseManager as any)._databaseItems).toEqual([]);
|
||||||
expect(updateSpy).toHaveBeenCalledWith("databaseList", []);
|
expect(updateSpy).toHaveBeenCalledWith("databaseList", []);
|
||||||
expect(onDidChangeDatabaseItem).toHaveBeenCalledWith({
|
expect(onDidChangeDatabaseItem).toHaveBeenCalledWith({
|
||||||
item: undefined,
|
fullRefresh: true,
|
||||||
|
item: mockDbItem,
|
||||||
kind: DatabaseEventKind.Remove,
|
kind: DatabaseEventKind.Remove,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -175,7 +177,8 @@ describe("local databases", () => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
expect(onDidChangeDatabaseItem).toHaveBeenCalledWith({
|
expect(onDidChangeDatabaseItem).toHaveBeenCalledWith({
|
||||||
item: undefined,
|
fullRefresh: true,
|
||||||
|
item: mockDbItem,
|
||||||
kind: DatabaseEventKind.Rename,
|
kind: DatabaseEventKind.Rename,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -198,7 +201,8 @@ describe("local databases", () => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const mockEvent = {
|
const mockEvent = {
|
||||||
item: undefined,
|
fullRefresh: true,
|
||||||
|
item: mockDbItem,
|
||||||
kind: DatabaseEventKind.Add,
|
kind: DatabaseEventKind.Add,
|
||||||
};
|
};
|
||||||
expect(onDidChangeDatabaseItem).toHaveBeenCalledWith(mockEvent);
|
expect(onDidChangeDatabaseItem).toHaveBeenCalledWith(mockEvent);
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
import { ConfigurationTarget } from "vscode";
|
import { ConfigurationTarget } from "vscode";
|
||||||
import {
|
import { VSCODE_GITHUB_ENTERPRISE_URI_SETTING } from "../../../../src/config";
|
||||||
VARIANT_ANALYSIS_ENABLE_GHEC_DR,
|
|
||||||
VSCODE_GITHUB_ENTERPRISE_URI_SETTING,
|
|
||||||
} from "../../../../src/config";
|
|
||||||
import { isVariantAnalysisEnabledForGitHubHost } from "../../../../src/variant-analysis/ghec-dr";
|
import { isVariantAnalysisEnabledForGitHubHost } from "../../../../src/variant-analysis/ghec-dr";
|
||||||
|
|
||||||
describe("checkVariantAnalysisEnabled", () => {
|
describe("checkVariantAnalysisEnabled", () => {
|
||||||
it("returns cleanly when no enterprise URI is set", async () => {
|
it("returns true when no enterprise URI is set", async () => {
|
||||||
expect(isVariantAnalysisEnabledForGitHubHost()).toBe(true);
|
expect(isVariantAnalysisEnabledForGitHubHost()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns false when GHES enterprise URI is set and variant analysis feature flag is not set", async () => {
|
it("returns false when GHES enterprise URI is set", async () => {
|
||||||
await VSCODE_GITHUB_ENTERPRISE_URI_SETTING.updateValue(
|
await VSCODE_GITHUB_ENTERPRISE_URI_SETTING.updateValue(
|
||||||
"https://github.example.com",
|
"https://github.example.com",
|
||||||
ConfigurationTarget.Global,
|
ConfigurationTarget.Global,
|
||||||
@@ -18,35 +15,11 @@ describe("checkVariantAnalysisEnabled", () => {
|
|||||||
expect(isVariantAnalysisEnabledForGitHubHost()).toBe(false);
|
expect(isVariantAnalysisEnabledForGitHubHost()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns false when GHES enterprise URI is set and variant analysis feature flag is set", async () => {
|
it("returns true when a GHEC-DR URI is set", async () => {
|
||||||
await VSCODE_GITHUB_ENTERPRISE_URI_SETTING.updateValue(
|
|
||||||
"https://github.example.com",
|
|
||||||
ConfigurationTarget.Global,
|
|
||||||
);
|
|
||||||
await VARIANT_ANALYSIS_ENABLE_GHEC_DR.updateValue(
|
|
||||||
"true",
|
|
||||||
ConfigurationTarget.Global,
|
|
||||||
);
|
|
||||||
expect(isVariantAnalysisEnabledForGitHubHost()).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns false when GHEC-DR URI is set and variant analysis feature flag is not set", async () => {
|
|
||||||
await VSCODE_GITHUB_ENTERPRISE_URI_SETTING.updateValue(
|
await VSCODE_GITHUB_ENTERPRISE_URI_SETTING.updateValue(
|
||||||
"https://example.ghe.com",
|
"https://example.ghe.com",
|
||||||
ConfigurationTarget.Global,
|
ConfigurationTarget.Global,
|
||||||
);
|
);
|
||||||
expect(isVariantAnalysisEnabledForGitHubHost()).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns true when GHEC-DR URI is set and variant analysis feature flag is set", async () => {
|
|
||||||
await VSCODE_GITHUB_ENTERPRISE_URI_SETTING.updateValue(
|
|
||||||
"https://example.ghe.com",
|
|
||||||
ConfigurationTarget.Global,
|
|
||||||
);
|
|
||||||
await VARIANT_ANALYSIS_ENABLE_GHEC_DR.updateValue(
|
|
||||||
"true",
|
|
||||||
ConfigurationTarget.Global,
|
|
||||||
);
|
|
||||||
expect(isVariantAnalysisEnabledForGitHubHost()).toBe(true);
|
expect(isVariantAnalysisEnabledForGitHubHost()).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user