Compare commits
117 Commits
v1.13.0
...
mh-refacto
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
390dfeb087 | ||
|
|
012e597d4a | ||
|
|
cd568db7b4 | ||
|
|
dc745dd8e1 | ||
|
|
29d6b127e2 | ||
|
|
1cbfba32a0 | ||
|
|
c2b2d10591 | ||
|
|
1fe4abf6e8 | ||
|
|
efaef8a2cb | ||
|
|
a8d61a605a | ||
|
|
e44024763d | ||
|
|
9a6aa52a40 | ||
|
|
c106903f01 | ||
|
|
d40cda150c | ||
|
|
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 | ||
|
|
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.
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] [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.
|
||||
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!
|
||||
|
||||
13
.github/workflows/main.yml
vendored
13
.github/workflows/main.yml
vendored
@@ -144,6 +144,19 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
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
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -134,7 +134,7 @@ jobs:
|
||||
|
||||
- name: Publish to Registry
|
||||
run: |
|
||||
npx vsce publish -p $VSCE_TOKEN --packagePath *.vsix
|
||||
npx @vscode/vsce publish -p $VSCE_TOKEN --packagePath *.vsix
|
||||
|
||||
open-vsx-publish:
|
||||
name: Publish to Open VSX Registry
|
||||
|
||||
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -111,4 +111,10 @@
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
},
|
||||
"github.copilot.advanced": {
|
||||
|
||||
|
||||
},
|
||||
"codeQL.variantAnalysis.enableGhecDr": true,
|
||||
"github-enterprise.uri": "http://server:8080/"
|
||||
}
|
||||
|
||||
@@ -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).
|
||||
- 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).
|
||||
- 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
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# 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:
|
||||
- Making substantial new features available to all users. This can include lifting a feature flag.
|
||||
- Breakage in compatibility with recent versions of the CLI.
|
||||
@@ -60,7 +61,7 @@
|
||||
|
||||
## 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:
|
||||
|
||||
@@ -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. 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
|
||||
|
||||
from string path, string kind
|
||||
where sinkModel("vscode-codeql", path, kind)
|
||||
where sinkModel("vscode-codeql", 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.
|
||||
- Check that the file contains entries for the methods that were modeled.
|
||||
|
||||
#### Test Case 3: Model with AI
|
||||
|
||||
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
|
||||
#### Test Case 3: Model as dependency
|
||||
|
||||
Note that this test requires the feature flag: `codeQL.model.flowGeneration`
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ const baseConfig = {
|
||||
"@typescript-eslint/no-throw-literal": "error",
|
||||
"@typescript-eslint/consistent-type-imports": "error",
|
||||
"import/consistent-type-specifier-style": ["error", "prefer-top-level"],
|
||||
curly: ["error", "all"],
|
||||
// curly: ["error", "all"],
|
||||
"escompat/no-regexp-lookbehind": "off",
|
||||
"etc/no-implicit-any-catch": "error",
|
||||
"filenames/match-regex": "off",
|
||||
|
||||
@@ -1 +1 @@
|
||||
v18.18.2
|
||||
v20.9.0
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# CodeQL for Visual Studio Code: Changelog
|
||||
|
||||
## [UNRELEASED]
|
||||
|
||||
## 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
|
||||
|
||||
- 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).
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
@@ -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.
|
||||
|
||||
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.
|
||||
|
||||
@@ -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/).
|
||||
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
|
||||
|
||||
@@ -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. 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
|
||||
|
||||
@@ -106,12 +108,12 @@ If you wish to navigate the query results from your keyboard, you can bind short
|
||||
|
||||
## 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/).
|
||||
- [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/).
|
||||
- [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
|
||||
|
||||
@@ -119,4 +121,4 @@ The CodeQL extension for Visual Studio Code is [licensed](LICENSE.md) under the
|
||||
|
||||
## 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.
|
||||
|
||||
2121
extensions/ql-vscode/package-lock.json
generated
2121
extensions/ql-vscode/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
||||
"description": "CodeQL for Visual Studio Code",
|
||||
"author": "GitHub",
|
||||
"private": true,
|
||||
"version": "1.13.0",
|
||||
"version": "1.13.2",
|
||||
"publisher": "GitHub",
|
||||
"license": "MIT",
|
||||
"icon": "media/VS-marketplace-CodeQL-icon.png",
|
||||
@@ -14,7 +14,7 @@
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "^1.82.0",
|
||||
"node": "^18.18.2",
|
||||
"node": "^20.9.0",
|
||||
"npm": ">=7.20.6"
|
||||
},
|
||||
"categories": [
|
||||
@@ -339,13 +339,6 @@
|
||||
"title": "Variant analysis",
|
||||
"order": 5,
|
||||
"properties": {
|
||||
"codeQL.variantAnalysis.controllerRepo": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"pattern": "^$|^(?:[a-zA-Z0-9]+-)*[a-zA-Z0-9]+/[a-zA-Z0-9-_]+$",
|
||||
"patternErrorMessage": "Please enter a valid GitHub repository",
|
||||
"markdownDescription": "[For internal use only] The name of the GitHub repository in which the GitHub Actions workflow is run when using the \"Run Variant Analysis\" command. The repository should be of the form `<owner>/<repo>`)."
|
||||
},
|
||||
"codeQL.variantAnalysis.defaultResultsFilter": {
|
||||
"type": "string",
|
||||
"default": "all",
|
||||
@@ -1931,11 +1924,6 @@
|
||||
{
|
||||
"view": "codeQLEvalLogViewer",
|
||||
"contents": "Run the 'Show Evaluator Log (UI)' command on a CodeQL query run in the Query History view."
|
||||
},
|
||||
{
|
||||
"view": "codeQLVariantAnalysisRepositories",
|
||||
"contents": "Set up a controller repository to start using variant analysis. [Learn more](https://codeql.github.com/docs/codeql-for-visual-studio-code/running-codeql-queries-at-scale-with-mrva#controller-repository) about controller repositories. \n[Set up controller repository](command:codeQLVariantAnalysisRepositories.setupControllerRepository)",
|
||||
"when": "!config.codeQL.variantAnalysis.controllerRepo"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1955,14 +1943,14 @@
|
||||
"format": "prettier --write **/*.{ts,tsx} && eslint . --ext .ts,.tsx --fix",
|
||||
"lint": "eslint . --ext .js,.ts,.tsx --max-warnings=0",
|
||||
"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",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"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:schemas": "ts-node scripts/generate-schemas.ts",
|
||||
"generate:chromium-version": "ts-node scripts/generate-chromium-version.ts",
|
||||
"generate:schemas": "vite-node scripts/generate-schemas.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\"",
|
||||
"postinstall": "patch-package",
|
||||
"prepare": "cd ../.. && husky install"
|
||||
@@ -1987,13 +1975,13 @@
|
||||
"nanoid": "^5.0.7",
|
||||
"node-fetch": "^2.6.7",
|
||||
"p-queue": "^8.0.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"semver": "^7.6.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"semver": "^7.6.2",
|
||||
"source-map": "^0.7.4",
|
||||
"source-map-support": "^0.5.21",
|
||||
"stream-json": "^1.7.3",
|
||||
"styled-components": "^6.1.8",
|
||||
"styled-components": "^6.1.9",
|
||||
"tmp": "^0.2.1",
|
||||
"tmp-promise": "^3.0.2",
|
||||
"tree-kill": "^1.2.2",
|
||||
@@ -2004,7 +1992,7 @@
|
||||
"zip-a-folder": "^3.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.24.4",
|
||||
"@babel/core": "^7.24.6",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.18.6",
|
||||
"@babel/preset-env": "^7.24.4",
|
||||
"@babel/preset-react": "^7.24.1",
|
||||
@@ -2012,22 +2000,22 @@
|
||||
"@faker-js/faker": "^8.4.1",
|
||||
"@github/markdownlint-github": "^0.6.2",
|
||||
"@playwright/test": "^1.40.1",
|
||||
"@storybook/addon-a11y": "^8.0.9",
|
||||
"@storybook/addon-actions": "^8.0.9",
|
||||
"@storybook/addon-essentials": "^8.0.9",
|
||||
"@storybook/addon-interactions": "^8.0.9",
|
||||
"@storybook/addon-links": "^8.0.9",
|
||||
"@storybook/addon-a11y": "^8.1.10",
|
||||
"@storybook/addon-actions": "^8.1.10",
|
||||
"@storybook/addon-essentials": "^8.1.10",
|
||||
"@storybook/addon-interactions": "^8.1.10",
|
||||
"@storybook/addon-links": "^8.1.10",
|
||||
"@storybook/blocks": "^8.0.2",
|
||||
"@storybook/components": "^8.0.2",
|
||||
"@storybook/csf": "^0.1.6",
|
||||
"@storybook/csf": "^0.1.8",
|
||||
"@storybook/icons": "^1.2.9",
|
||||
"@storybook/manager-api": "^8.0.9",
|
||||
"@storybook/react": "^8.0.9",
|
||||
"@storybook/react-vite": "^8.0.9",
|
||||
"@storybook/theming": "^8.0.9",
|
||||
"@storybook/manager-api": "^8.1.10",
|
||||
"@storybook/react": "^8.1.10",
|
||||
"@storybook/react-vite": "^8.1.10",
|
||||
"@storybook/theming": "^8.1.10",
|
||||
"@testing-library/dom": "^10.1.0",
|
||||
"@testing-library/jest-dom": "^6.4.2",
|
||||
"@testing-library/react": "^15.0.5",
|
||||
"@testing-library/jest-dom": "^6.4.6",
|
||||
"@testing-library/react": "^16.0.0",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@types/child-process-promise": "^2.2.1",
|
||||
"@types/d3": "^7.4.0",
|
||||
@@ -2039,10 +2027,10 @@
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/js-yaml": "^4.0.6",
|
||||
"@types/nanoid": "^3.0.0",
|
||||
"@types/node": "18.18.*",
|
||||
"@types/node": "20.9.*",
|
||||
"@types/node-fetch": "^2.5.2",
|
||||
"@types/react": "^18.0.28",
|
||||
"@types/react-dom": "^18.2.18",
|
||||
"@types/react": "^18.3.1",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/sarif": "^2.1.2",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@types/stream-json": "^1.7.1",
|
||||
@@ -2066,7 +2054,7 @@
|
||||
"eslint-import-resolver-typescript": "^3.6.1",
|
||||
"eslint-plugin-deprecation": "^2.0.0",
|
||||
"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-jest-dom": "^5.2.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
@@ -2078,25 +2066,26 @@
|
||||
"gulp-esbuild": "^0.12.0",
|
||||
"gulp-replace": "^1.1.3",
|
||||
"gulp-typescript": "^5.0.1",
|
||||
"husky": "^9.0.11",
|
||||
"husky": "^9.1.7",
|
||||
"jest": "^29.0.3",
|
||||
"jest-environment-jsdom": "^29.0.3",
|
||||
"jest-runner-vscode": "^3.0.1",
|
||||
"lint-staged": "^15.2.2",
|
||||
"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",
|
||||
"patch-package": "^8.0.0",
|
||||
"prettier": "^3.2.5",
|
||||
"storybook": "^8.0.9",
|
||||
"storybook": "^8.1.10",
|
||||
"tar-stream": "^3.1.7",
|
||||
"through2": "^4.0.2",
|
||||
"ts-jest": "^29.1.2",
|
||||
"ts-json-schema-generator": "^2.0.1",
|
||||
"ts-node": "^10.7.0",
|
||||
"ts-unused-exports": "^10.0.0",
|
||||
"ts-jest": "^29.1.4",
|
||||
"ts-json-schema-generator": "^2.1.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"ts-unused-exports": "^10.1.0",
|
||||
"typescript": "^5.0.2",
|
||||
"vite": "^5.2.6"
|
||||
"vite": "^5.2.11",
|
||||
"vite-node": "^1.5.3"
|
||||
},
|
||||
"lint-staged": {
|
||||
"./**/*.{json,css,scss}": [
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -31,4 +31,9 @@ export interface Credentials {
|
||||
* @returns An OAuth access token, or undefined.
|
||||
*/
|
||||
getExistingAccessToken(): Promise<string | undefined>;
|
||||
|
||||
/**
|
||||
* Returns the ID of the authentication provider to use.
|
||||
*/
|
||||
authProviderId: string;
|
||||
}
|
||||
|
||||
@@ -290,7 +290,6 @@ export type DatabasePanelCommands = {
|
||||
"codeQLVariantAnalysisRepositories.openConfigFile": () => Promise<void>;
|
||||
"codeQLVariantAnalysisRepositories.addNewDatabase": () => Promise<void>;
|
||||
"codeQLVariantAnalysisRepositories.addNewList": () => Promise<void>;
|
||||
"codeQLVariantAnalysisRepositories.setupControllerRepository": () => Promise<void>;
|
||||
|
||||
"codeQLVariantAnalysisRepositories.setSelectedItem": TreeViewContextSingleSelectionCommandFunction<DbTreeViewItem>;
|
||||
"codeQLVariantAnalysisRepositories.setSelectedItemContextMenu": TreeViewContextSingleSelectionCommandFunction<DbTreeViewItem>;
|
||||
|
||||
@@ -29,37 +29,45 @@ function validGitHubNwoOrOwner(
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
export function getNwoFromGitHubUrl(githubUrl: string): string | undefined {
|
||||
return getNwoOrOwnerFromGitHubUrl(githubUrl, "nwo");
|
||||
export function getNwoFromGitHubUrl(
|
||||
repositoryUrl: string,
|
||||
githubUrl: URL,
|
||||
): string | undefined {
|
||||
return getNwoOrOwnerFromGitHubUrl(repositoryUrl, githubUrl, "nwo");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
export function getOwnerFromGitHubUrl(githubUrl: string): string | undefined {
|
||||
return getNwoOrOwnerFromGitHubUrl(githubUrl, "owner");
|
||||
export function getOwnerFromGitHubUrl(
|
||||
repositoryUrl: string,
|
||||
githubUrl: URL,
|
||||
): string | undefined {
|
||||
return getNwoOrOwnerFromGitHubUrl(repositoryUrl, githubUrl, "owner");
|
||||
}
|
||||
|
||||
function getNwoOrOwnerFromGitHubUrl(
|
||||
githubUrl: string,
|
||||
repositoryUrl: string,
|
||||
githubUrl: URL,
|
||||
kind: "owner" | "nwo",
|
||||
): string | undefined {
|
||||
const validHostnames = [githubUrl.hostname, `www.${githubUrl.hostname}`];
|
||||
|
||||
try {
|
||||
let paths: string[];
|
||||
const urlElements = githubUrl.split("/");
|
||||
if (
|
||||
urlElements[0] === "github.com" ||
|
||||
urlElements[0] === "www.github.com"
|
||||
) {
|
||||
paths = githubUrl.split("/").slice(1);
|
||||
const urlElements = repositoryUrl.split("/");
|
||||
if (validHostnames.includes(urlElements[0])) {
|
||||
paths = repositoryUrl.split("/").slice(1);
|
||||
} else {
|
||||
const uri = new URL(githubUrl);
|
||||
if (uri.hostname !== "github.com" && uri.hostname !== "www.github.com") {
|
||||
const uri = new URL(repositoryUrl);
|
||||
if (!validHostnames.includes(uri.hostname)) {
|
||||
return;
|
||||
}
|
||||
paths = uri.pathname.split("/").filter((segment: string) => segment);
|
||||
|
||||
@@ -26,6 +26,8 @@ import {
|
||||
// All path operations in this file must be on paths *within* the zip
|
||||
// archive.
|
||||
import { posix } from "path";
|
||||
import { DatabaseEventKind } from "../../databases/local-databases/database-events";
|
||||
import type { DatabaseManager } from "../../databases/local-databases/database-manager";
|
||||
|
||||
const path = posix;
|
||||
|
||||
@@ -242,15 +244,8 @@ export class ArchiveFileSystemProvider implements FileSystemProvider {
|
||||
|
||||
root = new Directory("");
|
||||
|
||||
constructor() {
|
||||
// When a file system archive is removed from the workspace, we should
|
||||
// also remove it from our cache.
|
||||
workspace.onDidChangeWorkspaceFolders((event) => {
|
||||
for (const removed of event.removed) {
|
||||
const zipPath = removed.uri.fsPath;
|
||||
this.archives.delete(zipPath);
|
||||
}
|
||||
});
|
||||
flushCache(zipPath: string) {
|
||||
this.archives.delete(zipPath);
|
||||
}
|
||||
|
||||
// metadata
|
||||
@@ -366,15 +361,35 @@ export class ArchiveFileSystemProvider implements FileSystemProvider {
|
||||
*/
|
||||
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(
|
||||
workspace.registerFileSystemProvider(
|
||||
zipArchiveScheme,
|
||||
new ArchiveFileSystemProvider(),
|
||||
{
|
||||
isCaseSensitive: true,
|
||||
isReadonly: true,
|
||||
},
|
||||
),
|
||||
// When a file system archive is removed from the workspace, we should
|
||||
// also remove it from our cache.
|
||||
workspace.onDidChangeWorkspaceFolders((event) => {
|
||||
for (const removed of event.removed) {
|
||||
const zipPath = removed.uri.fsPath;
|
||||
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 { Credentials } from "../authentication";
|
||||
import { AppOctokit } from "../octokit";
|
||||
|
||||
export const GITHUB_AUTH_PROVIDER_ID = "github";
|
||||
import { hasGhecDrUri } from "../../config";
|
||||
import { getOctokitBaseUrl } from "./octokit";
|
||||
|
||||
// We need 'repo' scope for triggering workflows, 'gist' scope for exporting results to Gist,
|
||||
// and 'read:packages' for reading private CodeQL packages.
|
||||
@@ -16,30 +16,24 @@ const SCOPES = ["repo", "gist", "read:packages"];
|
||||
*/
|
||||
export class VSCodeCredentials implements Credentials {
|
||||
/**
|
||||
* A specific octokit to return, otherwise a new authenticated octokit will be created when needed.
|
||||
*/
|
||||
private octokit: Octokit | undefined;
|
||||
|
||||
/**
|
||||
* Creates or returns an instance of Octokit.
|
||||
* 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
|
||||
* authentication session.
|
||||
*
|
||||
* @returns An instance of Octokit.
|
||||
*/
|
||||
async getOctokit(): Promise<Octokit> {
|
||||
if (this.octokit) {
|
||||
return this.octokit;
|
||||
}
|
||||
|
||||
const accessToken = await this.getAccessToken();
|
||||
|
||||
return new AppOctokit({
|
||||
auth: accessToken,
|
||||
baseUrl: getOctokitBaseUrl(),
|
||||
});
|
||||
}
|
||||
|
||||
async getAccessToken(): Promise<string> {
|
||||
const session = await authentication.getSession(
|
||||
GITHUB_AUTH_PROVIDER_ID,
|
||||
this.authProviderId,
|
||||
SCOPES,
|
||||
{ createIfNone: true },
|
||||
);
|
||||
@@ -49,11 +43,18 @@ export class VSCodeCredentials implements Credentials {
|
||||
|
||||
async getExistingAccessToken(): Promise<string | undefined> {
|
||||
const session = await authentication.getSession(
|
||||
GITHUB_AUTH_PROVIDER_ID,
|
||||
this.authProviderId,
|
||||
SCOPES,
|
||||
{ createIfNone: false },
|
||||
);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
export function progressUpdate(
|
||||
step: number,
|
||||
maxStep: number,
|
||||
message: string,
|
||||
): ProgressUpdate {
|
||||
return { step, maxStep, message };
|
||||
}
|
||||
|
||||
export type ProgressCallback = (p: ProgressUpdate) => void;
|
||||
|
||||
// Make certain properties within a type optional
|
||||
|
||||
@@ -108,12 +108,57 @@ export function hasEnterpriseUri(): boolean {
|
||||
return getEnterpriseUri() !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the uri look like GHEC-DR?
|
||||
*/
|
||||
function isGhecDrUri(uri: Uri | undefined): boolean {
|
||||
return (
|
||||
uri !== undefined && !uri.authority.toLowerCase().endsWith("github.com")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the GitHub Enterprise URI set to something that looks like GHEC-DR?
|
||||
*/
|
||||
export function hasGhecDrUri(): boolean {
|
||||
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");
|
||||
@@ -548,28 +593,13 @@ export const NO_CACHE_CONTEXTUAL_QUERIES = new Setting(
|
||||
// Settings for variant analysis
|
||||
const VARIANT_ANALYSIS_SETTING = new Setting("variantAnalysis", ROOT_SETTING);
|
||||
|
||||
/**
|
||||
* The name of the "controller" repository that you want to use with the "Run Variant Analysis" command.
|
||||
* Note: This command is only available for internal users.
|
||||
*
|
||||
* This setting should be a GitHub repository of the form `<owner>/<repo>`.
|
||||
*/
|
||||
const REMOTE_CONTROLLER_REPO = new Setting(
|
||||
"controllerRepo",
|
||||
VARIANT_ANALYSIS_SETTING,
|
||||
);
|
||||
|
||||
export function getRemoteControllerRepo(): string | undefined {
|
||||
return REMOTE_CONTROLLER_REPO.getValue<string>() || undefined;
|
||||
}
|
||||
|
||||
export async function setRemoteControllerRepo(repo: string | undefined) {
|
||||
await REMOTE_CONTROLLER_REPO.updateValue(repo, ConfigurationTarget.Global);
|
||||
}
|
||||
|
||||
export interface VariantAnalysisConfig {
|
||||
controllerRepo: string | undefined;
|
||||
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>;
|
||||
}
|
||||
|
||||
@@ -584,13 +614,13 @@ export class VariantAnalysisConfigListener
|
||||
);
|
||||
}
|
||||
|
||||
public get controllerRepo(): string | undefined {
|
||||
return getRemoteControllerRepo();
|
||||
}
|
||||
|
||||
public get showSystemDefinedRepositoryLists(): boolean {
|
||||
return !hasEnterpriseUri();
|
||||
}
|
||||
|
||||
public get githubUrl(): URL {
|
||||
return getGitHubInstanceUrl();
|
||||
}
|
||||
}
|
||||
|
||||
const VARIANT_ANALYSIS_FILTER_RESULTS = new Setting(
|
||||
@@ -628,11 +658,6 @@ export function getVariantAnalysisDefaultResultsSort(): SortKey {
|
||||
*/
|
||||
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 {
|
||||
return ACTION_BRANCH.getValue<string>() || "main";
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { AppOctokit } from "../common/octokit";
|
||||
import type { ProgressCallback } from "../common/vscode/progress";
|
||||
import { UserCancellationException } from "../common/vscode/progress";
|
||||
import type { EndpointDefaults } from "@octokit/types";
|
||||
import { getOctokitBaseUrl } from "../common/vscode/octokit";
|
||||
|
||||
export async function getCodeSearchRepositories(
|
||||
query: string,
|
||||
@@ -54,6 +55,7 @@ async function provideOctokitWithThrottling(
|
||||
|
||||
const octokit = new MyOctokit({
|
||||
auth,
|
||||
baseUrl: getOctokitBaseUrl(),
|
||||
throttle: {
|
||||
onRateLimit: (retryAfter: number, options: EndpointDefaults): boolean => {
|
||||
void logger.log(
|
||||
|
||||
@@ -29,6 +29,8 @@ import {
|
||||
addDatabaseSourceToWorkspace,
|
||||
allowHttp,
|
||||
downloadTimeout,
|
||||
getGitHubInstanceUrl,
|
||||
hasGhecDrUri,
|
||||
isCanary,
|
||||
} from "../config";
|
||||
import { showAndLogInformationMessage } from "../common/logging";
|
||||
@@ -150,10 +152,11 @@ export class DatabaseFetcher {
|
||||
maxStep: 2,
|
||||
});
|
||||
|
||||
const instanceUrl = getGitHubInstanceUrl();
|
||||
|
||||
const options: InputBoxOptions = {
|
||||
title:
|
||||
'Enter a GitHub repository URL or "name with owner" (e.g. https://github.com/github/codeql or github/codeql)',
|
||||
placeHolder: "https://github.com/<owner>/<repo> or <owner>/<repo>",
|
||||
title: `Enter a GitHub repository URL or "name with owner" (e.g. ${new URL("/github/codeql", instanceUrl).toString()} or github/codeql)`,
|
||||
placeHolder: `${new URL("/", instanceUrl).toString()}<owner>/<repo> or <owner>/<repo>`,
|
||||
ignoreFocusOut: true,
|
||||
};
|
||||
|
||||
@@ -180,12 +183,14 @@ export class DatabaseFetcher {
|
||||
makeSelected = true,
|
||||
addSourceArchiveFolder = addDatabaseSourceToWorkspace(),
|
||||
): Promise<DatabaseItem | undefined> {
|
||||
const nwo = getNwoFromGitHubUrl(githubRepo) || githubRepo;
|
||||
const nwo =
|
||||
getNwoFromGitHubUrl(githubRepo, getGitHubInstanceUrl()) || githubRepo;
|
||||
if (!isValidGitHubNwo(nwo)) {
|
||||
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
|
||||
? await credentials.getOctokit()
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { Octokit } from "@octokit/rest";
|
||||
import type { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods";
|
||||
import { showNeverAskAgainDialog } from "../../common/vscode/dialog";
|
||||
import type { GitHubDatabaseConfig } from "../../config";
|
||||
import { hasGhecDrUri } from "../../config";
|
||||
import type { Credentials } from "../../common/authentication";
|
||||
import { AppOctokit } from "../../common/octokit";
|
||||
import type { ProgressCallback } from "../../common/vscode/progress";
|
||||
@@ -67,7 +68,10 @@ export async function listDatabases(
|
||||
credentials: Credentials,
|
||||
config: GitHubDatabaseConfig,
|
||||
): 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
|
||||
? await credentials.getOctokit()
|
||||
|
||||
@@ -109,9 +109,8 @@ class DatabaseTreeDataProvider
|
||||
// Note that events from the database manager are instances of DatabaseChangedEvent
|
||||
// and events fired by the UI are instances of DatabaseItem
|
||||
|
||||
// When event.item is undefined, then the entire tree is refreshed.
|
||||
// When event.item is a db item, then only that item is refreshed.
|
||||
this._onDidChangeTreeData.fire(event.item);
|
||||
// When a full refresh has occurred, then all items are refreshed by passing undefined.
|
||||
this._onDidChangeTreeData.fire(event.fullRefresh ? undefined : event.item);
|
||||
}
|
||||
|
||||
private handleDidChangeCurrentDatabaseItem(
|
||||
|
||||
@@ -16,4 +16,8 @@ export enum DatabaseEventKind {
|
||||
export interface DatabaseChangedEvent {
|
||||
kind: DatabaseEventKind;
|
||||
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({
|
||||
item,
|
||||
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
|
||||
this._onDidChangeDatabaseItem.fire({
|
||||
item: undefined,
|
||||
item,
|
||||
kind: DatabaseEventKind.Add,
|
||||
fullRefresh: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -671,9 +673,9 @@ export class DatabaseManager extends DisposableObject {
|
||||
item.name = newName;
|
||||
await this.updatePersistedDatabaseList();
|
||||
this._onDidChangeDatabaseItem.fire({
|
||||
// pass undefined so that the entire tree is rebuilt in order to re-sort
|
||||
item: undefined,
|
||||
item,
|
||||
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({
|
||||
item: undefined,
|
||||
item,
|
||||
kind: DatabaseEventKind.Remove,
|
||||
fullRefresh: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -776,6 +778,7 @@ export class DatabaseManager extends DisposableObject {
|
||||
this._onDidChangeDatabaseItem.fire({
|
||||
kind: DatabaseEventKind.Refresh,
|
||||
item: databaseItem,
|
||||
fullRefresh: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,13 +18,12 @@ import type { DbManager } from "../db-manager";
|
||||
import { DbTreeDataProvider } from "./db-tree-data-provider";
|
||||
import type { DbTreeViewItem } from "./db-tree-view-item";
|
||||
import { getGitHubUrl } from "./db-tree-view-item-action";
|
||||
import { getControllerRepo } from "../../variant-analysis/run-remote-query";
|
||||
import { getErrorMessage } from "../../common/helpers-pure";
|
||||
import type { DatabasePanelCommands } from "../../common/commands";
|
||||
import type { App } from "../../common/app";
|
||||
import { QueryLanguage } from "../../common/query-language";
|
||||
import { getCodeSearchRepositories } from "../code-search-api";
|
||||
import { showAndLogErrorMessage } from "../../common/logging";
|
||||
import { getGitHubInstanceUrl } from "../../config";
|
||||
|
||||
export interface RemoteDatabaseQuickPickItem extends QuickPickItem {
|
||||
remoteDatabaseKind: string;
|
||||
@@ -73,9 +72,6 @@ export class DbPanel extends DisposableObject {
|
||||
this.addNewRemoteDatabase.bind(this),
|
||||
"codeQLVariantAnalysisRepositories.addNewList":
|
||||
this.addNewList.bind(this),
|
||||
"codeQLVariantAnalysisRepositories.setupControllerRepository":
|
||||
this.setupControllerRepository.bind(this),
|
||||
|
||||
"codeQLVariantAnalysisRepositories.setSelectedItem":
|
||||
this.setSelectedItem.bind(this),
|
||||
"codeQLVariantAnalysisRepositories.setSelectedItemContextMenu":
|
||||
@@ -146,16 +142,19 @@ export class DbPanel extends DisposableObject {
|
||||
}
|
||||
|
||||
private async addNewRemoteRepo(parentList?: string): Promise<void> {
|
||||
const instanceUrl = getGitHubInstanceUrl();
|
||||
|
||||
const repoName = await window.showInputBox({
|
||||
title: "Add a repository",
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nwo = getNwoFromGitHubUrl(repoName) || repoName;
|
||||
const nwo =
|
||||
getNwoFromGitHubUrl(repoName, getGitHubInstanceUrl()) || repoName;
|
||||
if (!isValidGitHubNwo(nwo)) {
|
||||
void showAndLogErrorMessage(
|
||||
this.app.logger,
|
||||
@@ -176,17 +175,20 @@ export class DbPanel extends DisposableObject {
|
||||
}
|
||||
|
||||
private async addNewRemoteOwner(): Promise<void> {
|
||||
const instanceUrl = getGitHubInstanceUrl();
|
||||
|
||||
const ownerName = await window.showInputBox({
|
||||
title: "Add all repositories of a GitHub org or owner",
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
const owner = getOwnerFromGitHubUrl(ownerName) || ownerName;
|
||||
const owner =
|
||||
getOwnerFromGitHubUrl(ownerName, getGitHubInstanceUrl()) || ownerName;
|
||||
if (!isValidGitHubOwner(owner)) {
|
||||
void showAndLogErrorMessage(
|
||||
this.app.logger,
|
||||
@@ -411,7 +413,7 @@ export class DbPanel extends DisposableObject {
|
||||
if (treeViewItem.dbItem === undefined) {
|
||||
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) {
|
||||
throw new Error(
|
||||
"Unable to open on GitHub. Please select a variant analysis repository or owner.",
|
||||
@@ -420,22 +422,4 @@ export class DbPanel extends DisposableObject {
|
||||
|
||||
await this.app.commands.execute("vscode.open", Uri.parse(githubUrl));
|
||||
}
|
||||
|
||||
private async setupControllerRepository(): Promise<void> {
|
||||
try {
|
||||
// This will also validate that the controller repository is valid
|
||||
await getControllerRepo(this.app.credentials);
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof UserCancellationException) {
|
||||
return;
|
||||
}
|
||||
|
||||
void showAndLogErrorMessage(
|
||||
this.app.logger,
|
||||
`An error occurred while setting up the controller repository: ${getErrorMessage(
|
||||
e,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,11 +83,6 @@ export class DbTreeDataProvider
|
||||
}
|
||||
|
||||
private createTree(): DbTreeViewItem[] {
|
||||
// Returning an empty tree here will show the welcome view
|
||||
if (!this.variantAnalysisConfig.controllerRepo) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const dbItemsResult = this.dbManager.getDbItems();
|
||||
|
||||
if (dbItemsResult.isFailure) {
|
||||
|
||||
@@ -62,12 +62,15 @@ function canImportCodeSearch(dbItem: DbItem): boolean {
|
||||
return DbItemKind.RemoteUserDefinedList === dbItem.kind;
|
||||
}
|
||||
|
||||
export function getGitHubUrl(dbItem: DbItem): string | undefined {
|
||||
export function getGitHubUrl(
|
||||
dbItem: DbItem,
|
||||
githubUrl: URL,
|
||||
): string | undefined {
|
||||
switch (dbItem.kind) {
|
||||
case DbItemKind.RemoteOwner:
|
||||
return `https://github.com/${dbItem.ownerName}`;
|
||||
return new URL(`/${dbItem.ownerName}`, githubUrl).toString();
|
||||
case DbItemKind.RemoteRepo:
|
||||
return `https://github.com/${dbItem.repoFullName}`;
|
||||
return new URL(`/${dbItem.repoFullName}`, githubUrl).toString();
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ class QLDebugAdapterTracker
|
||||
): Promise<void> {
|
||||
if (this.localQueryRun !== undefined) {
|
||||
const results: CoreQueryResults = body;
|
||||
await this.localQueryRun.complete(results);
|
||||
await this.localQueryRun.complete(results, (_) => {});
|
||||
this.localQueryRun = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
joinOrderWarningThreshold,
|
||||
QueryHistoryConfigListener,
|
||||
QueryServerConfigListener,
|
||||
VariantAnalysisConfigListener,
|
||||
} from "./config";
|
||||
import {
|
||||
AstViewer,
|
||||
@@ -446,8 +447,9 @@ export async function activate(
|
||||
}
|
||||
|
||||
if (
|
||||
CliVersionConstraint.OLDEST_SUPPORTED_CLI_VERSION.compare(ver.version) <
|
||||
0
|
||||
CliVersionConstraint.OLDEST_SUPPORTED_CLI_VERSION.compare(
|
||||
ver.version,
|
||||
) <= 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@@ -715,12 +717,13 @@ async function installOrUpdateThenTryActivate(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const PACK_GLOBS = [
|
||||
const CLEAR_PACK_CACHE_ON_EDIT_GLOBS = [
|
||||
"**/codeql-pack.yml",
|
||||
"**/qlpack.yml",
|
||||
"**/queries.xml",
|
||||
"**/codeql-pack.lock.yml",
|
||||
"**/qlpack.lock.yml",
|
||||
"**/*.dbscheme",
|
||||
".codeqlmanifest.json",
|
||||
"codeql-workspace.yml",
|
||||
];
|
||||
@@ -767,7 +770,7 @@ async function activateWithInstalledDistribution(
|
||||
ctx,
|
||||
);
|
||||
|
||||
for (const glob of PACK_GLOBS) {
|
||||
for (const glob of CLEAR_PACK_CACHE_ON_EDIT_GLOBS) {
|
||||
const fsWatcher = workspace.createFileSystemWatcher(glob);
|
||||
ctx.subscriptions.push(fsWatcher);
|
||||
|
||||
@@ -864,8 +867,10 @@ async function activateWithInstalledDistribution(
|
||||
"variant-analyses",
|
||||
);
|
||||
await ensureDir(variantAnalysisStorageDir);
|
||||
const variantAnalysisConfig = new VariantAnalysisConfigListener();
|
||||
const variantAnalysisResultsManager = new VariantAnalysisResultsManager(
|
||||
cliServer,
|
||||
variantAnalysisConfig,
|
||||
extLogger,
|
||||
);
|
||||
|
||||
@@ -875,6 +880,7 @@ async function activateWithInstalledDistribution(
|
||||
variantAnalysisStorageDir,
|
||||
variantAnalysisResultsManager,
|
||||
dbModule.dbManager,
|
||||
variantAnalysisConfig,
|
||||
);
|
||||
ctx.subscriptions.push(variantAnalysisManager);
|
||||
ctx.subscriptions.push(variantAnalysisResultsManager);
|
||||
@@ -942,7 +948,7 @@ async function activateWithInstalledDistribution(
|
||||
ctx.subscriptions.push(compareView);
|
||||
|
||||
void extLogger.log("Initializing source archive filesystem provider.");
|
||||
archiveFilesystemProvider_activate(ctx);
|
||||
archiveFilesystemProvider_activate(ctx, dbm);
|
||||
|
||||
const qhelpTmpDir = dirSync({
|
||||
prefix: "qhelp_",
|
||||
|
||||
@@ -485,7 +485,7 @@ export class LocalQueries extends DisposableObject {
|
||||
localQueryRun.logger,
|
||||
);
|
||||
|
||||
await localQueryRun.complete(results);
|
||||
await localQueryRun.complete(results, progress);
|
||||
|
||||
return results;
|
||||
} catch (e) {
|
||||
|
||||
@@ -26,6 +26,8 @@ import type { LocalQueries } from "./local-queries";
|
||||
import { tryGetQueryMetadata } from "../codeql-cli/query-metadata";
|
||||
import { telemetryListener } from "../common/vscode/telemetry";
|
||||
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 {
|
||||
switch (result.resultType) {
|
||||
@@ -79,23 +81,31 @@ export class LocalQueryRun {
|
||||
* This function must be called when the evaluation completes, whether the evaluation was
|
||||
* successful or not.
|
||||
* */
|
||||
public async complete(results: CoreQueryResults): Promise<void> {
|
||||
public async complete(
|
||||
results: CoreQueryResults,
|
||||
progress: ProgressCallback,
|
||||
): Promise<void> {
|
||||
const evalLogPaths = await this.summarizeEvalLog(
|
||||
results.resultType,
|
||||
this.outputDir,
|
||||
this.logger,
|
||||
progress,
|
||||
);
|
||||
if (evalLogPaths !== undefined) {
|
||||
this.queryInfo.setEvaluatorLogPaths(evalLogPaths);
|
||||
}
|
||||
progress(progressUpdate(1, 4, "Getting completed query info"));
|
||||
const queryWithResults = await this.getCompletedQueryInfo(results);
|
||||
progress(progressUpdate(2, 4, "Updating query history"));
|
||||
this.queryHistoryManager.completeQuery(this.queryInfo, queryWithResults);
|
||||
progress(progressUpdate(3, 4, "Showing results"));
|
||||
await this.localQueries.showResultsForCompletedQuery(
|
||||
this.queryInfo as CompletedLocalQueryInfo,
|
||||
WebviewReveal.Forced,
|
||||
);
|
||||
// Note we must update the query history view after showing results as the
|
||||
// display and sorting might depend on the number of results
|
||||
progress(progressUpdate(4, 4, "Updating query history"));
|
||||
await this.queryHistoryManager.refreshTreeView();
|
||||
|
||||
this.logger.dispose();
|
||||
@@ -109,6 +119,7 @@ export class LocalQueryRun {
|
||||
QueryResultType.OTHER_ERROR,
|
||||
this.outputDir,
|
||||
this.logger,
|
||||
(_) => {},
|
||||
);
|
||||
if (evalLogPaths !== undefined) {
|
||||
this.queryInfo.setEvaluatorLogPaths(evalLogPaths);
|
||||
@@ -128,10 +139,12 @@ export class LocalQueryRun {
|
||||
resultType: QueryResultType,
|
||||
outputDir: QueryOutputDir,
|
||||
logger: BaseLogger,
|
||||
progress: ProgressCallback,
|
||||
): Promise<EvaluatorLogPaths | undefined> {
|
||||
const evalLogPaths = await generateEvalLogSummaries(
|
||||
this.cliServer,
|
||||
outputDir,
|
||||
progress,
|
||||
);
|
||||
if (evalLogPaths !== undefined) {
|
||||
if (evalLogPaths.endSummary !== undefined) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
getActionsWorkflowRunUrl as getVariantAnalysisActionsWorkflowRunUrl,
|
||||
} from "../variant-analysis/shared/variant-analysis";
|
||||
import type { QueryLanguage } from "../common/query-language";
|
||||
import { getGitHubInstanceUrl } from "../config";
|
||||
|
||||
export type QueryHistoryInfo = LocalQueryInfo | VariantAnalysisHistoryItem;
|
||||
|
||||
@@ -79,5 +80,8 @@ export function buildRepoLabel(item: VariantAnalysisHistoryItem): string {
|
||||
export function getActionsWorkflowRunUrl(
|
||||
item: VariantAnalysisHistoryItem,
|
||||
): string {
|
||||
return getVariantAnalysisActionsWorkflowRunUrl(item.variantAnalysis);
|
||||
return getVariantAnalysisActionsWorkflowRunUrl(
|
||||
item.variantAnalysis,
|
||||
getGitHubInstanceUrl(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -48,11 +48,6 @@ function mapVariantAnalysisDtoToDto(
|
||||
): VariantAnalysisDto {
|
||||
return {
|
||||
id: variantAnalysis.id,
|
||||
controllerRepo: {
|
||||
id: variantAnalysis.controllerRepo.id,
|
||||
fullName: variantAnalysis.controllerRepo.fullName,
|
||||
private: variantAnalysis.controllerRepo.private,
|
||||
},
|
||||
query: {
|
||||
name: variantAnalysis.query.name,
|
||||
filePath: variantAnalysis.query.filePath,
|
||||
|
||||
@@ -48,12 +48,12 @@ function mapVariantAnalysisToDomainModel(
|
||||
): VariantAnalysis {
|
||||
return {
|
||||
id: variantAnalysis.id,
|
||||
controllerRepo: {
|
||||
id: variantAnalysis.controllerRepo.id,
|
||||
fullName: variantAnalysis.controllerRepo.fullName,
|
||||
private: variantAnalysis.controllerRepo.private,
|
||||
},
|
||||
language: mapQueryLanguageToDomainModel(variantAnalysis.query.language),
|
||||
controllerRepo: {
|
||||
id: 0,
|
||||
fullName: "",
|
||||
private: false,
|
||||
},
|
||||
query: {
|
||||
name: variantAnalysis.query.name,
|
||||
filePath: variantAnalysis.query.filePath,
|
||||
|
||||
@@ -15,11 +15,6 @@ export interface QueryHistoryVariantAnalysisDto {
|
||||
|
||||
export interface VariantAnalysisDto {
|
||||
id: number;
|
||||
controllerRepo: {
|
||||
id: number;
|
||||
fullName: string;
|
||||
private: boolean;
|
||||
};
|
||||
query: {
|
||||
name: string;
|
||||
filePath: string;
|
||||
|
||||
@@ -240,8 +240,32 @@ export class QueryServerClient extends DisposableObject {
|
||||
this.nextCallback = 0;
|
||||
this.nextProgress = 0;
|
||||
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 { createHash } from "crypto";
|
||||
import { QueryOutputDir } from "./local-queries/query-output-dir";
|
||||
import { progressUpdate } from "./common/vscode/progress";
|
||||
import type { ProgressCallback } from "./common/vscode/progress";
|
||||
|
||||
/**
|
||||
* run-queries.ts
|
||||
@@ -519,6 +521,7 @@ export async function createInitialQueryInfo(
|
||||
export async function generateEvalLogSummaries(
|
||||
cliServer: CodeQLCliServer,
|
||||
outputDir: QueryOutputDir,
|
||||
progress: ProgressCallback,
|
||||
): Promise<EvaluatorLogPaths | undefined> {
|
||||
const log = outputDir.evalLogPath;
|
||||
if (!(await pathExists(log))) {
|
||||
@@ -527,6 +530,7 @@ export async function generateEvalLogSummaries(
|
||||
}
|
||||
let humanReadableSummary: string | undefined = undefined;
|
||||
let endSummary: string | undefined = undefined;
|
||||
progress(progressUpdate(1, 3, "Generating evaluator log summary"));
|
||||
if (await generateHumanReadableLogSummary(cliServer, outputDir)) {
|
||||
humanReadableSummary = outputDir.evalLogSummaryPath;
|
||||
endSummary = outputDir.evalLogEndSummaryPath;
|
||||
@@ -535,10 +539,12 @@ export async function generateEvalLogSummaries(
|
||||
let summarySymbols: string | undefined = undefined;
|
||||
if (isCanary()) {
|
||||
// Generate JSON summary for viewer.
|
||||
progress(progressUpdate(2, 3, "Generating JSON log summary"));
|
||||
jsonSummary = outputDir.jsonEvalLogSummaryPath;
|
||||
await cliServer.generateJsonLogSummary(log, jsonSummary);
|
||||
|
||||
if (humanReadableSummary !== undefined) {
|
||||
progress(progressUpdate(3, 3, "Generating summary symbols file"));
|
||||
summarySymbols = outputDir.evalLogSummarySymbolsPath;
|
||||
await generateSummarySymbolsFile(humanReadableSummary, summarySymbols);
|
||||
}
|
||||
|
||||
@@ -15,28 +15,28 @@ export async function extractRawResults(
|
||||
const bqrsInfo = await cliServer.bqrsInfo(filePath);
|
||||
const resultSets = bqrsInfo["result-sets"];
|
||||
|
||||
if (resultSets.length < 1) {
|
||||
if (resultSets.length === 0) {
|
||||
throw new Error("No result sets found in results file.");
|
||||
}
|
||||
if (resultSets.length > 1) {
|
||||
void logger.log(
|
||||
"Multiple result sets found in results file. Only one will be used.",
|
||||
"Multiple result sets found in results file. Using the first one.",
|
||||
);
|
||||
}
|
||||
|
||||
// Always prefer #select over any other result set. #select is usually the result the user
|
||||
// wants to see since it contains the outer #select.
|
||||
// Prefer `#select` result set; otherwise, use the first available set.
|
||||
const schema =
|
||||
resultSets.find((resultSet) => resultSet.name === SELECT_TABLE_NAME) ??
|
||||
resultSets[0];
|
||||
resultSets.find((r) => r.name === SELECT_TABLE_NAME) ?? resultSets[0];
|
||||
|
||||
const chunk = await cliServer.bqrsDecode(filePath, schema.name, {
|
||||
pageSize: MAX_RAW_RESULTS,
|
||||
});
|
||||
|
||||
const resultSet = bqrsToResultSet(schema, chunk);
|
||||
|
||||
const capped = !!chunk.next;
|
||||
|
||||
return { resultSet, fileLinkPrefix, sourceLocationPrefix, capped };
|
||||
return {
|
||||
resultSet,
|
||||
fileLinkPrefix,
|
||||
sourceLocationPrefix,
|
||||
capped: !!chunk.next,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ type ErrorResponse = {
|
||||
|
||||
export function handleRequestError(
|
||||
e: RequestError,
|
||||
githubUrl: URL,
|
||||
logger: NotificationLogger,
|
||||
): boolean {
|
||||
if (e.status !== 422) {
|
||||
@@ -60,9 +61,12 @@ export function handleRequestError(
|
||||
return false;
|
||||
}
|
||||
|
||||
const createBranchURL = `https://github.com/${
|
||||
missingDefaultBranchError.repository
|
||||
}/new/${encodeURIComponent(missingDefaultBranchError.default_branch)}`;
|
||||
const createBranchURL = new URL(
|
||||
`/${
|
||||
missingDefaultBranchError.repository
|
||||
}/new/${encodeURIComponent(missingDefaultBranchError.default_branch)}`,
|
||||
githubUrl,
|
||||
).toString();
|
||||
|
||||
void showAndLogErrorMessage(
|
||||
logger,
|
||||
|
||||
@@ -160,7 +160,7 @@ async function exportVariantAnalysisAnalysisResults(
|
||||
expectedAnalysesResultsCount: number,
|
||||
exportFormat: "gist" | "local",
|
||||
commandManager: AppCommandManager,
|
||||
credentials: Credentials,
|
||||
_credentials: Credentials,
|
||||
progress: ProgressCallback,
|
||||
token: CancellationToken,
|
||||
) {
|
||||
@@ -191,7 +191,6 @@ async function exportVariantAnalysisAnalysisResults(
|
||||
markdownFiles,
|
||||
exportFormat,
|
||||
commandManager,
|
||||
credentials,
|
||||
progress,
|
||||
token,
|
||||
);
|
||||
@@ -236,7 +235,6 @@ async function exportResults(
|
||||
markdownFiles: MarkdownFile[],
|
||||
exportFormat: "gist" | "local",
|
||||
commandManager: AppCommandManager,
|
||||
credentials: Credentials,
|
||||
progress?: ProgressCallback,
|
||||
token?: CancellationToken,
|
||||
) {
|
||||
@@ -249,7 +247,6 @@ async function exportResults(
|
||||
description,
|
||||
markdownFiles,
|
||||
commandManager,
|
||||
credentials,
|
||||
progress,
|
||||
token,
|
||||
);
|
||||
@@ -268,7 +265,6 @@ async function exportToGist(
|
||||
description: string,
|
||||
markdownFiles: MarkdownFile[],
|
||||
commandManager: AppCommandManager,
|
||||
credentials: Credentials,
|
||||
progress?: ProgressCallback,
|
||||
token?: CancellationToken,
|
||||
) {
|
||||
@@ -291,7 +287,7 @@ async function exportToGist(
|
||||
{} as { [key: string]: { content: string } },
|
||||
);
|
||||
|
||||
const gistUrl = await createGist(credentials, description, gistFiles);
|
||||
const gistUrl = await createGist(description, gistFiles);
|
||||
if (gistUrl) {
|
||||
// This needs to use .then to ensure we aren't keeping the progress notification open. We shouldn't await the
|
||||
// "Open gist" button click.
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { OctokitResponse } from "@octokit/types/dist-types";
|
||||
import type { Credentials } from "../../common/authentication";
|
||||
import { getGitHubInstanceUrl } from "../../config";
|
||||
import type { VariantAnalysisSubmission } from "../shared/variant-analysis";
|
||||
import type {
|
||||
VariantAnalysis,
|
||||
@@ -7,84 +6,142 @@ import type {
|
||||
VariantAnalysisSubmissionRequest,
|
||||
} from "./variant-analysis";
|
||||
import type { Repository } from "./repository";
|
||||
import { extLogger } from "../../common/logging/vscode";
|
||||
|
||||
export async function submitVariantAnalysis(
|
||||
credentials: Credentials,
|
||||
submissionDetails: VariantAnalysisSubmission,
|
||||
): Promise<VariantAnalysis> {
|
||||
const octokit = await credentials.getOctokit();
|
||||
|
||||
const { actionRepoRef, language, pack, databases, controllerRepoId } =
|
||||
submissionDetails;
|
||||
|
||||
const data: VariantAnalysisSubmissionRequest = {
|
||||
action_repo_ref: actionRepoRef,
|
||||
language,
|
||||
query_pack: pack,
|
||||
repositories: databases.repositories,
|
||||
repository_lists: databases.repositoryLists,
|
||||
repository_owners: databases.repositoryOwners,
|
||||
};
|
||||
|
||||
const response: OctokitResponse<VariantAnalysis> = await octokit.request(
|
||||
"POST /repositories/:controllerRepoId/code-scanning/codeql/variant-analyses",
|
||||
{
|
||||
controllerRepoId,
|
||||
data,
|
||||
},
|
||||
);
|
||||
|
||||
return response.data;
|
||||
function getOctokitBaseUrl(): string {
|
||||
let apiUrl = getGitHubInstanceUrl().toString();
|
||||
if (apiUrl.endsWith("/")) {
|
||||
apiUrl = apiUrl.slice(0, -1);
|
||||
}
|
||||
if (apiUrl.startsWith("https://")) {
|
||||
apiUrl = apiUrl.replace("https://", "http://");
|
||||
}
|
||||
return apiUrl;
|
||||
}
|
||||
|
||||
export async function submitVariantAnalysis(
|
||||
submissionDetails: VariantAnalysisSubmission,
|
||||
): Promise<VariantAnalysis> {
|
||||
try {
|
||||
console.log("Getting base URL...");
|
||||
const baseUrl = getOctokitBaseUrl();
|
||||
void extLogger.log(`Base URL: ${baseUrl}`);
|
||||
|
||||
const { actionRepoRef, language, pack, databases, controllerRepoId } =
|
||||
submissionDetails;
|
||||
|
||||
const data: VariantAnalysisSubmissionRequest = {
|
||||
action_repo_ref: actionRepoRef,
|
||||
language,
|
||||
query_pack: pack,
|
||||
repositories: databases.repositories,
|
||||
repository_lists: databases.repositoryLists,
|
||||
repository_owners: databases.repositoryOwners,
|
||||
};
|
||||
|
||||
void extLogger.log(
|
||||
`Sending fetch request with data: ${JSON.stringify(data)}`,
|
||||
);
|
||||
|
||||
void extLogger.log(
|
||||
`Fetch request URL: ${baseUrl}/repositories/${controllerRepoId}/code-scanning/codeql/variant-analyses`,
|
||||
);
|
||||
|
||||
const response = await fetch(
|
||||
`${baseUrl}/repositories/${controllerRepoId}/code-scanning/codeql/variant-analyses`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
},
|
||||
);
|
||||
|
||||
void extLogger.log(`Response status: ${response.status}`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Error submitting variant analysis: ${response.statusText}`,
|
||||
);
|
||||
}
|
||||
|
||||
const responseData = await response.json();
|
||||
void extLogger.log(`Response data: ${responseData}`);
|
||||
|
||||
return responseData;
|
||||
} catch (error) {
|
||||
void extLogger.log(`Error: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
export async function getVariantAnalysis(
|
||||
credentials: Credentials,
|
||||
controllerRepoId: number,
|
||||
variantAnalysisId: number,
|
||||
): Promise<VariantAnalysis> {
|
||||
const octokit = await credentials.getOctokit();
|
||||
const baseUrl = getOctokitBaseUrl();
|
||||
|
||||
const response: OctokitResponse<VariantAnalysis> = await octokit.request(
|
||||
"GET /repositories/:controllerRepoId/code-scanning/codeql/variant-analyses/:variantAnalysisId",
|
||||
const response = await fetch(
|
||||
`${baseUrl}/repositories/${controllerRepoId}/code-scanning/codeql/variant-analyses/${variantAnalysisId}`,
|
||||
{
|
||||
controllerRepoId,
|
||||
variantAnalysisId,
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
return response.data;
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error getting variant analysis: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
export async function getVariantAnalysisRepo(
|
||||
credentials: Credentials,
|
||||
controllerRepoId: number,
|
||||
variantAnalysisId: number,
|
||||
repoId: number,
|
||||
): Promise<VariantAnalysisRepoTask> {
|
||||
const octokit = await credentials.getOctokit();
|
||||
const baseUrl = getOctokitBaseUrl();
|
||||
|
||||
const response: OctokitResponse<VariantAnalysisRepoTask> =
|
||||
await octokit.request(
|
||||
"GET /repositories/:controllerRepoId/code-scanning/codeql/variant-analyses/:variantAnalysisId/repositories/:repoId",
|
||||
{
|
||||
controllerRepoId,
|
||||
variantAnalysisId,
|
||||
repoId,
|
||||
const response = await fetch(
|
||||
`${baseUrl}/repositories/${controllerRepoId}/code-scanning/codeql/variant-analyses/${variantAnalysisId}/repositories/${repoId}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
return response.data;
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Error getting variant analysis repo: ${response.statusText}`,
|
||||
);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
export async function getRepositoryFromNwo(
|
||||
credentials: Credentials,
|
||||
owner: string,
|
||||
repo: string,
|
||||
): Promise<Repository> {
|
||||
const octokit = await credentials.getOctokit();
|
||||
const baseUrl = getOctokitBaseUrl();
|
||||
|
||||
const response = await octokit.rest.repos.get({ owner, repo });
|
||||
return response.data as Repository;
|
||||
const response = await fetch(`${baseUrl}/repos/${owner}/${repo}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error getting repository: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,22 +149,29 @@ export async function getRepositoryFromNwo(
|
||||
* Returns the URL of the created gist.
|
||||
*/
|
||||
export async function createGist(
|
||||
credentials: Credentials,
|
||||
description: string,
|
||||
files: { [key: string]: { content: string } },
|
||||
): Promise<string | undefined> {
|
||||
const octokit = await credentials.getOctokit();
|
||||
const response = await octokit.request("POST /gists", {
|
||||
description,
|
||||
files,
|
||||
public: false,
|
||||
const baseUrl = getOctokitBaseUrl();
|
||||
|
||||
const response = await fetch(`${baseUrl}/gists`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
description,
|
||||
files,
|
||||
public: false,
|
||||
}),
|
||||
});
|
||||
if (response.status >= 300) {
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Error exporting variant analysis results: ${response.status} ${
|
||||
response?.data || ""
|
||||
}`,
|
||||
`Error creating gist: ${response.status} ${response.statusText}`,
|
||||
);
|
||||
}
|
||||
return response.data.html_url;
|
||||
|
||||
const data = await response.json();
|
||||
return data.html_url;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
import {
|
||||
VARIANT_ANALYSIS_ENABLE_GHEC_DR,
|
||||
hasEnterpriseUri,
|
||||
hasGhecDrUri,
|
||||
} from "../config";
|
||||
import { hasEnterpriseUri, hasGhecDrUri } from "../config";
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
export function isVariantAnalysisEnabledForGitHubHost(): boolean {
|
||||
return (
|
||||
// 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>())
|
||||
);
|
||||
return !hasEnterpriseUri() || hasGhecDrUri();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { CancellationToken } from "vscode";
|
||||
import { Uri, window } from "vscode";
|
||||
import { Uri } from "vscode";
|
||||
import { join, sep, basename, relative } from "path";
|
||||
import { dump, load } from "js-yaml";
|
||||
import { copy, writeFile, readFile, mkdirp } from "fs-extra";
|
||||
@@ -7,26 +7,17 @@ import type { DirectoryResult } from "tmp-promise";
|
||||
import { dir, tmpName } from "tmp-promise";
|
||||
import { tmpDir } from "../tmp-dir";
|
||||
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
|
||||
import type { Credentials } from "../common/authentication";
|
||||
import type { CodeQLCliServer } from "../codeql-cli/cli";
|
||||
import { extLogger } from "../common/logging/vscode";
|
||||
import {
|
||||
getActionBranch,
|
||||
getRemoteControllerRepo,
|
||||
setRemoteControllerRepo,
|
||||
} from "../config";
|
||||
import { getActionBranch } from "../config";
|
||||
import type { ProgressCallback } from "../common/vscode/progress";
|
||||
import { UserCancellationException } from "../common/vscode/progress";
|
||||
import type { RequestError } from "@octokit/types/dist-types";
|
||||
import type { QueryMetadata } from "../common/interface-types";
|
||||
import { getErrorMessage, REPO_REGEX } from "../common/helpers-pure";
|
||||
import { getRepositoryFromNwo } from "./gh-api/gh-api-client";
|
||||
import type { RepositorySelection } from "./repository-selection";
|
||||
import {
|
||||
getRepositorySelection,
|
||||
isValidSelection,
|
||||
} from "./repository-selection";
|
||||
import type { Repository } from "./shared/repository";
|
||||
import type { DbManager } from "../databases/db-manager";
|
||||
import {
|
||||
getQlPackFilePath,
|
||||
@@ -285,13 +276,11 @@ interface PreparedRemoteQuery {
|
||||
base64Pack: string;
|
||||
modelPacks: ModelPackDetails[];
|
||||
repoSelection: RepositorySelection;
|
||||
controllerRepo: Repository;
|
||||
queryStartTime: number;
|
||||
}
|
||||
|
||||
export async function prepareRemoteQueryRun(
|
||||
cliServer: CodeQLCliServer,
|
||||
credentials: Credentials,
|
||||
qlPackDetails: QlPackDetails,
|
||||
progress: ProgressCallback,
|
||||
token: CancellationToken,
|
||||
@@ -322,8 +311,6 @@ export async function prepareRemoteQueryRun(
|
||||
message: "Determining controller repo",
|
||||
});
|
||||
|
||||
const controllerRepo = await getControllerRepo(credentials);
|
||||
|
||||
progress({
|
||||
maxStep: 4,
|
||||
step: 3,
|
||||
@@ -367,7 +354,6 @@ export async function prepareRemoteQueryRun(
|
||||
base64Pack: generatedPack.base64Pack,
|
||||
modelPacks: generatedPack.modelPacks,
|
||||
repoSelection,
|
||||
controllerRepo,
|
||||
queryStartTime,
|
||||
};
|
||||
}
|
||||
@@ -494,84 +480,6 @@ export function getQueryName(
|
||||
return queryMetadata?.name ?? basename(queryFilePath);
|
||||
}
|
||||
|
||||
export async function getControllerRepo(
|
||||
credentials: Credentials,
|
||||
): Promise<Repository> {
|
||||
// Get the controller repo from the config, if it exists.
|
||||
// If it doesn't exist, prompt the user to enter it, check
|
||||
// whether the repo exists, and save the nwo to the config.
|
||||
|
||||
let shouldSetControllerRepo = false;
|
||||
let controllerRepoNwo: string | undefined;
|
||||
controllerRepoNwo = getRemoteControllerRepo();
|
||||
if (!controllerRepoNwo || !REPO_REGEX.test(controllerRepoNwo)) {
|
||||
void extLogger.log(
|
||||
controllerRepoNwo
|
||||
? "Invalid controller repository name."
|
||||
: "No controller repository defined.",
|
||||
);
|
||||
controllerRepoNwo = await window.showInputBox({
|
||||
title:
|
||||
"Controller repository in which to run GitHub Actions workflows for variant analyses",
|
||||
placeHolder: "<owner>/<repo>",
|
||||
prompt:
|
||||
"Enter the name of a GitHub repository in the format <owner>/<repo>. You can change this in the extension settings.",
|
||||
ignoreFocusOut: true,
|
||||
});
|
||||
if (!controllerRepoNwo) {
|
||||
throw new UserCancellationException("No controller repository entered.");
|
||||
} else if (!REPO_REGEX.test(controllerRepoNwo)) {
|
||||
// Check if user entered invalid input
|
||||
throw new UserCancellationException(
|
||||
"Invalid repository format. Must be a valid GitHub repository in the format <owner>/<repo>.",
|
||||
);
|
||||
}
|
||||
|
||||
shouldSetControllerRepo = true;
|
||||
}
|
||||
|
||||
void extLogger.log(`Using controller repository: ${controllerRepoNwo}`);
|
||||
const controllerRepo = await getControllerRepoFromApi(
|
||||
credentials,
|
||||
controllerRepoNwo,
|
||||
);
|
||||
|
||||
if (shouldSetControllerRepo) {
|
||||
void extLogger.log(
|
||||
`Setting the controller repository as: ${controllerRepoNwo}`,
|
||||
);
|
||||
await setRemoteControllerRepo(controllerRepoNwo);
|
||||
}
|
||||
|
||||
return controllerRepo;
|
||||
}
|
||||
|
||||
async function getControllerRepoFromApi(
|
||||
credentials: Credentials,
|
||||
nwo: string,
|
||||
): Promise<Repository> {
|
||||
const [owner, repo] = nwo.split("/");
|
||||
try {
|
||||
const controllerRepo = await getRepositoryFromNwo(credentials, owner, repo);
|
||||
void extLogger.log(`Controller repository ID: ${controllerRepo.id}`);
|
||||
return {
|
||||
id: controllerRepo.id,
|
||||
fullName: controllerRepo.full_name,
|
||||
private: controllerRepo.private,
|
||||
};
|
||||
} catch (e) {
|
||||
if ((e as RequestError).status === 404) {
|
||||
throw new Error(`Controller repository "${owner}/${repo}" not found`);
|
||||
} else {
|
||||
throw new Error(
|
||||
`Error getting controller repository "${owner}/${repo}": ${getErrorMessage(
|
||||
e,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeWorkspaceRefs(qlpack: QlPackFile) {
|
||||
if (!qlpack.dependencies) {
|
||||
return;
|
||||
|
||||
@@ -295,10 +295,14 @@ export function getSkippedRepoCount(
|
||||
|
||||
export function getActionsWorkflowRunUrl(
|
||||
variantAnalysis: VariantAnalysis,
|
||||
githubUrl: URL,
|
||||
): string {
|
||||
const {
|
||||
actionsWorkflowRunId,
|
||||
controllerRepo: { fullName },
|
||||
} = 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,
|
||||
writeRepoStates,
|
||||
} from "./repo-states-store";
|
||||
import { GITHUB_AUTH_PROVIDER_ID } from "../common/vscode/authentication";
|
||||
import { FetchError } from "node-fetch";
|
||||
import {
|
||||
showAndLogExceptionWithTelemetry,
|
||||
@@ -98,6 +97,7 @@ import { findVariantAnalysisQlPackRoot } from "./ql";
|
||||
import { resolveCodeScanningQueryPack } from "./code-scanning-pack";
|
||||
import { isSarifResultsQueryKind } from "../common/query-metadata";
|
||||
import { isVariantAnalysisEnabledForGitHubHost } from "./ghec-dr";
|
||||
import type { VariantAnalysisConfig } from "../config";
|
||||
import { getEnterpriseUri } from "../config";
|
||||
|
||||
const maxRetryCount = 3;
|
||||
@@ -158,6 +158,7 @@ export class VariantAnalysisManager
|
||||
private readonly storagePath: string,
|
||||
private readonly variantAnalysisResultsManager: VariantAnalysisResultsManager,
|
||||
private readonly dbManager: DbManager,
|
||||
private readonly config: VariantAnalysisConfig,
|
||||
) {
|
||||
super();
|
||||
this.variantAnalysisMonitor = this.push(
|
||||
@@ -372,16 +373,19 @@ export class VariantAnalysisManager
|
||||
);
|
||||
}
|
||||
|
||||
// log to extLogger
|
||||
void this.app.logger.log(
|
||||
`Running variant analysis with query: ${queryName}, language: ${variantAnalysisLanguage}`,
|
||||
);
|
||||
|
||||
const {
|
||||
actionBranch,
|
||||
base64Pack,
|
||||
modelPacks,
|
||||
repoSelection,
|
||||
controllerRepo,
|
||||
queryStartTime,
|
||||
} = await prepareRemoteQueryRun(
|
||||
this.cliServer,
|
||||
this.app.credentials,
|
||||
qlPackDetails,
|
||||
progress,
|
||||
token,
|
||||
@@ -398,12 +402,15 @@ export class VariantAnalysisManager
|
||||
count: qlPackDetails.queryFiles.length,
|
||||
};
|
||||
|
||||
// log that submitting
|
||||
void this.app.logger.log("Submitting variant analysis");
|
||||
|
||||
const variantAnalysisSubmission: VariantAnalysisSubmission = {
|
||||
startTime: queryStartTime,
|
||||
actionRepoRef: actionBranch,
|
||||
controllerRepoId: controllerRepo.id,
|
||||
language: variantAnalysisLanguage,
|
||||
pack: base64Pack,
|
||||
controllerRepoId: 0,
|
||||
query: {
|
||||
name: queryName,
|
||||
filePath: firstQueryFile,
|
||||
@@ -421,15 +428,25 @@ export class VariantAnalysisManager
|
||||
let variantAnalysisResponse: ApiVariantAnalysis;
|
||||
try {
|
||||
variantAnalysisResponse = await submitVariantAnalysis(
|
||||
this.app.credentials,
|
||||
variantAnalysisSubmission,
|
||||
);
|
||||
} catch (e: unknown) {
|
||||
// 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)
|
||||
) {
|
||||
// log
|
||||
void this.app.logger.log(
|
||||
`Error submitting variant analysis: ${getErrorMessage(e)}`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// throwing
|
||||
void this.app.logger.log(
|
||||
`Error submitting variant analysis: ${getErrorMessage(e)}`,
|
||||
);
|
||||
throw e;
|
||||
}
|
||||
|
||||
@@ -745,7 +762,7 @@ export class VariantAnalysisManager
|
||||
private async onDidChangeSessions(
|
||||
event: AuthenticationSessionsChangeEvent,
|
||||
): Promise<void> {
|
||||
if (event.provider.id !== GITHUB_AUTH_PROVIDER_ID) {
|
||||
if (event.provider.id !== this.app.credentials.authProviderId) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -802,8 +819,7 @@ export class VariantAnalysisManager
|
||||
let repoTask: VariantAnalysisRepositoryTask;
|
||||
try {
|
||||
const repoTaskResponse = await getVariantAnalysisRepo(
|
||||
this.app.credentials,
|
||||
variantAnalysis.controllerRepo.id,
|
||||
0,
|
||||
variantAnalysis.id,
|
||||
scannedRepo.repository.id,
|
||||
);
|
||||
@@ -951,7 +967,10 @@ export class VariantAnalysisManager
|
||||
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(
|
||||
"vscode.open",
|
||||
|
||||
@@ -62,7 +62,6 @@ export class VariantAnalysisMonitor extends DisposableObject {
|
||||
try {
|
||||
await this._monitorVariantAnalysis(
|
||||
variantAnalysis.id,
|
||||
variantAnalysis.controllerRepo.id,
|
||||
variantAnalysis.executionStartTime,
|
||||
variantAnalysis.query.name,
|
||||
variantAnalysis.language,
|
||||
@@ -74,7 +73,6 @@ export class VariantAnalysisMonitor extends DisposableObject {
|
||||
|
||||
private async _monitorVariantAnalysis(
|
||||
variantAnalysisId: number,
|
||||
controllerRepoId: number,
|
||||
executionStartTime: number,
|
||||
queryName: string,
|
||||
language: QueryLanguage,
|
||||
@@ -97,11 +95,7 @@ export class VariantAnalysisMonitor extends DisposableObject {
|
||||
|
||||
let variantAnalysisSummary: ApiVariantAnalysis;
|
||||
try {
|
||||
variantAnalysisSummary = await getVariantAnalysis(
|
||||
this.app.credentials,
|
||||
controllerRepoId,
|
||||
variantAnalysisId,
|
||||
);
|
||||
variantAnalysisSummary = await getVariantAnalysis(0, variantAnalysisId);
|
||||
} catch (e) {
|
||||
const errorMessage = getErrorMessage(e);
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import { DisposableObject } from "../common/disposable-object";
|
||||
import { EventEmitter } from "vscode";
|
||||
import { unzipToDirectoryConcurrently } from "../common/unzip-concurrently";
|
||||
import { readRepoTask, writeRepoTask } from "./repo-tasks-store";
|
||||
import type { VariantAnalysisConfig } from "../config";
|
||||
|
||||
type CacheKey = `${number}/${string}`;
|
||||
|
||||
@@ -62,6 +63,7 @@ export class VariantAnalysisResultsManager extends DisposableObject {
|
||||
|
||||
constructor(
|
||||
private readonly cliServer: CodeQLCliServer,
|
||||
private readonly config: VariantAnalysisConfig,
|
||||
private readonly logger: Logger,
|
||||
) {
|
||||
super();
|
||||
@@ -192,7 +194,7 @@ export class VariantAnalysisResultsManager extends DisposableObject {
|
||||
throw new Error("Missing database commit SHA");
|
||||
}
|
||||
|
||||
const fileLinkPrefix = this.createGitHubDotcomFileLinkPrefix(
|
||||
const fileLinkPrefix = this.createGitHubFileLinkPrefix(
|
||||
repoTask.repository.fullName,
|
||||
repoTask.databaseCommitSha,
|
||||
);
|
||||
@@ -283,11 +285,11 @@ export class VariantAnalysisResultsManager extends DisposableObject {
|
||||
return join(variantAnalysisStoragePath, fullName);
|
||||
}
|
||||
|
||||
private createGitHubDotcomFileLinkPrefix(
|
||||
fullName: string,
|
||||
sha: string,
|
||||
): string {
|
||||
return `https://github.com/${fullName}/blob/${sha}`;
|
||||
private createGitHubFileLinkPrefix(fullName: string, sha: string): string {
|
||||
return new URL(
|
||||
`/${fullName}/blob/${sha}`,
|
||||
this.config.githubUrl,
|
||||
).toString();
|
||||
}
|
||||
|
||||
public removeAnalysisResults(variantAnalysis: VariantAnalysis) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[
|
||||
"v2.17.0",
|
||||
"v2.17.5",
|
||||
"v2.16.6",
|
||||
"v2.15.5",
|
||||
"v2.14.6",
|
||||
|
||||
@@ -20,7 +20,7 @@ export default defineConfig({
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
baseURL: "http://localhost:8080",
|
||||
baseURL: "http://server:8080",
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: "on-first-retry",
|
||||
|
||||
@@ -15,6 +15,7 @@ function makeTestOctokit(octokit: Octokit): Credentials {
|
||||
"getExistingAccessToken not supported by test credentials",
|
||||
);
|
||||
},
|
||||
authProviderId: "github",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ export function createMockVariantAnalysisConfig(): VariantAnalysisConfig {
|
||||
return {
|
||||
controllerRepo: "foo/bar",
|
||||
showSystemDefinedRepositoryLists: true,
|
||||
githubUrl: new URL("https://github.com"),
|
||||
onDidChangeConfiguration: jest.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
} from "../../../src/common/github-url-identifier-helper";
|
||||
|
||||
describe("github url identifier helper", () => {
|
||||
const githubUrl = new URL("https://github.com");
|
||||
|
||||
describe("valid GitHub Nwo Or Owner method", () => {
|
||||
it("should return true for valid owner", () => {
|
||||
expect(isValidGitHubOwner("github")).toBe(true);
|
||||
@@ -23,51 +25,96 @@ describe("github url identifier helper", () => {
|
||||
|
||||
describe("getNwoFromGitHubUrl method", () => {
|
||||
it("should handle invalid urls", () => {
|
||||
expect(getNwoFromGitHubUrl("")).toBe(undefined);
|
||||
expect(getNwoFromGitHubUrl("https://ww.github.com/foo/bar")).toBe(
|
||||
expect(getNwoFromGitHubUrl("", githubUrl)).toBe(undefined);
|
||||
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,
|
||||
);
|
||||
expect(getNwoFromGitHubUrl("https://www.github.com/foo")).toBe(undefined);
|
||||
expect(getNwoFromGitHubUrl("foo")).toBe(undefined);
|
||||
expect(getNwoFromGitHubUrl("foo/bar")).toBe(undefined);
|
||||
expect(getNwoFromGitHubUrl("foo", githubUrl)).toBe(undefined);
|
||||
expect(getNwoFromGitHubUrl("foo/bar", githubUrl)).toBe(undefined);
|
||||
});
|
||||
|
||||
it("should handle valid urls", () => {
|
||||
expect(getNwoFromGitHubUrl("github.com/foo/bar")).toBe("foo/bar");
|
||||
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(
|
||||
expect(getNwoFromGitHubUrl("github.com/foo/bar", githubUrl)).toBe(
|
||||
"foo/bar",
|
||||
);
|
||||
expect(getNwoFromGitHubUrl("https://github.com/foo/bar/sub/pages")).toBe(
|
||||
expect(getNwoFromGitHubUrl("www.github.com/foo/bar", githubUrl)).toBe(
|
||||
"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", () => {
|
||||
it("should handle invalid urls", () => {
|
||||
expect(getOwnerFromGitHubUrl("")).toBe(undefined);
|
||||
expect(getOwnerFromGitHubUrl("https://ww.github.com/foo/bar")).toBe(
|
||||
undefined,
|
||||
);
|
||||
expect(getOwnerFromGitHubUrl("foo")).toBe(undefined);
|
||||
expect(getOwnerFromGitHubUrl("foo/bar")).toBe(undefined);
|
||||
expect(getOwnerFromGitHubUrl("", githubUrl)).toBe(undefined);
|
||||
expect(
|
||||
getOwnerFromGitHubUrl("https://ww.github.com/foo/bar", githubUrl),
|
||||
).toBe(undefined);
|
||||
expect(
|
||||
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", () => {
|
||||
expect(getOwnerFromGitHubUrl("http://github.com/foo/bar")).toBe("foo");
|
||||
expect(getOwnerFromGitHubUrl("https://github.com/foo/bar")).toBe("foo");
|
||||
expect(getOwnerFromGitHubUrl("https://www.github.com/foo/bar")).toBe(
|
||||
expect(
|
||||
getOwnerFromGitHubUrl("http://github.com/foo/bar", githubUrl),
|
||||
).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",
|
||||
);
|
||||
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");
|
||||
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", () => {
|
||||
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 actualUrl = getGitHubUrl(dbItem);
|
||||
const actualUrl = getGitHubUrl(dbItem, githubUrl);
|
||||
const expectedUrl = `https://github.com/${dbItem.ownerName}`;
|
||||
|
||||
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 actualUrl = getGitHubUrl(dbItem);
|
||||
const actualUrl = getGitHubUrl(dbItem, githubUrl);
|
||||
const expectedUrl = `https://github.com/${dbItem.repoFullName}`;
|
||||
|
||||
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", () => {
|
||||
const dbItem0 = createRootRemoteDbItem();
|
||||
const dbItem1 = createRemoteSystemDefinedListDbItem();
|
||||
const dbItem2 = createRemoteUserDefinedListDbItem();
|
||||
|
||||
const actualUrl0 = getGitHubUrl(dbItem0);
|
||||
const actualUrl1 = getGitHubUrl(dbItem1);
|
||||
const actualUrl2 = getGitHubUrl(dbItem2);
|
||||
const actualUrl0 = getGitHubUrl(dbItem0, githubUrl);
|
||||
const actualUrl1 = getGitHubUrl(dbItem1, githubUrl);
|
||||
const actualUrl2 = getGitHubUrl(dbItem2, githubUrl);
|
||||
|
||||
expect(actualUrl0).toBeUndefined();
|
||||
expect(actualUrl1).toBeUndefined();
|
||||
|
||||
@@ -151,13 +151,29 @@ describe("isVariantAnalysisComplete", () => {
|
||||
});
|
||||
|
||||
describe("getActionsWorkflowRunUrl", () => {
|
||||
it("should get the run url", () => {
|
||||
it("should get the run url on github.com", () => {
|
||||
const variantAnalysis = createMockVariantAnalysis({});
|
||||
|
||||
const actionsWorkflowRunUrl = getActionsWorkflowRunUrl(variantAnalysis);
|
||||
const actionsWorkflowRunUrl = getActionsWorkflowRunUrl(
|
||||
variantAnalysis,
|
||||
new URL("https://github.com"),
|
||||
);
|
||||
|
||||
expect(actionsWorkflowRunUrl).toBe(
|
||||
`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";
|
||||
|
||||
describe("handleRequestError", () => {
|
||||
const githubUrl = new URL("https://github.com");
|
||||
const logger = createMockLogger();
|
||||
|
||||
it("returns false when handling a non-422 error", () => {
|
||||
const e = mockRequestError(404, {
|
||||
message: "Not Found",
|
||||
});
|
||||
expect(handleRequestError(e, logger)).toBe(false);
|
||||
expect(handleRequestError(e, githubUrl, logger)).toBe(false);
|
||||
expect(logger.showErrorMessage).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -19,13 +20,13 @@ describe("handleRequestError", () => {
|
||||
message:
|
||||
"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();
|
||||
});
|
||||
|
||||
it("returns false when handling an error without response body", () => {
|
||||
const e = mockRequestError(422, undefined);
|
||||
expect(handleRequestError(e, logger)).toBe(false);
|
||||
expect(handleRequestError(e, githubUrl, logger)).toBe(false);
|
||||
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();
|
||||
});
|
||||
|
||||
@@ -58,7 +59,7 @@ describe("handleRequestError", () => {
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(handleRequestError(e, logger)).toBe(false);
|
||||
expect(handleRequestError(e, githubUrl, logger)).toBe(false);
|
||||
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();
|
||||
});
|
||||
|
||||
@@ -92,11 +93,11 @@ describe("handleRequestError", () => {
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(handleRequestError(e, logger)).toBe(false);
|
||||
expect(handleRequestError(e, githubUrl, logger)).toBe(false);
|
||||
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, {
|
||||
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.",
|
||||
@@ -110,11 +111,33 @@ describe("handleRequestError", () => {
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(handleRequestError(e, logger)).toBe(true);
|
||||
expect(handleRequestError(e, githubUrl, 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://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 {
|
||||
|
||||
@@ -78,8 +78,10 @@ describe("Variant Analysis Manager", () => {
|
||||
new DbConfigStore(app),
|
||||
createMockVariantAnalysisConfig(),
|
||||
);
|
||||
const variantAnalysisConfig = createMockVariantAnalysisConfig();
|
||||
variantAnalysisResultsManager = new VariantAnalysisResultsManager(
|
||||
cli,
|
||||
variantAnalysisConfig,
|
||||
extLogger,
|
||||
);
|
||||
variantAnalysisManager = new VariantAnalysisManager(
|
||||
@@ -88,6 +90,7 @@ describe("Variant Analysis Manager", () => {
|
||||
storagePath,
|
||||
variantAnalysisResultsManager,
|
||||
dbManager,
|
||||
variantAnalysisConfig,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import type {
|
||||
VariantAnalysisScannedRepositoryResult,
|
||||
} from "../../../../src/variant-analysis/shared/variant-analysis";
|
||||
import { mockedObject } from "../../utils/mocking.helpers";
|
||||
import { createMockVariantAnalysisConfig } from "../../../factories/config";
|
||||
|
||||
jest.setTimeout(10_000);
|
||||
|
||||
@@ -27,8 +28,10 @@ describe(VariantAnalysisResultsManager.name, () => {
|
||||
variantAnalysisId = faker.number.int();
|
||||
|
||||
const cli = mockedObject<CodeQLCliServer>({});
|
||||
const variantAnalysisConfig = createMockVariantAnalysisConfig();
|
||||
variantAnalysisResultsManager = new VariantAnalysisResultsManager(
|
||||
cli,
|
||||
variantAnalysisConfig,
|
||||
extLogger,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -56,8 +56,10 @@ describe("Variant Analysis Manager", () => {
|
||||
new DbConfigStore(app),
|
||||
createMockVariantAnalysisConfig(),
|
||||
);
|
||||
const variantAnalysisConfig = createMockVariantAnalysisConfig();
|
||||
const variantAnalysisResultsManager = new VariantAnalysisResultsManager(
|
||||
cli,
|
||||
variantAnalysisConfig,
|
||||
extLogger,
|
||||
);
|
||||
variantAnalysisManager = new VariantAnalysisManager(
|
||||
@@ -66,6 +68,7 @@ describe("Variant Analysis Manager", () => {
|
||||
storagePath,
|
||||
variantAnalysisResultsManager,
|
||||
dbManager,
|
||||
variantAnalysisConfig,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -140,7 +140,8 @@ describe("local databases", () => {
|
||||
},
|
||||
]);
|
||||
expect(onDidChangeDatabaseItem).toHaveBeenCalledWith({
|
||||
item: undefined,
|
||||
fullRefresh: true,
|
||||
item: mockDbItem,
|
||||
kind: DatabaseEventKind.Add,
|
||||
});
|
||||
|
||||
@@ -152,7 +153,8 @@ describe("local databases", () => {
|
||||
expect((databaseManager as any)._databaseItems).toEqual([]);
|
||||
expect(updateSpy).toHaveBeenCalledWith("databaseList", []);
|
||||
expect(onDidChangeDatabaseItem).toHaveBeenCalledWith({
|
||||
item: undefined,
|
||||
fullRefresh: true,
|
||||
item: mockDbItem,
|
||||
kind: DatabaseEventKind.Remove,
|
||||
});
|
||||
});
|
||||
@@ -175,7 +177,8 @@ describe("local databases", () => {
|
||||
]);
|
||||
|
||||
expect(onDidChangeDatabaseItem).toHaveBeenCalledWith({
|
||||
item: undefined,
|
||||
fullRefresh: true,
|
||||
item: mockDbItem,
|
||||
kind: DatabaseEventKind.Rename,
|
||||
});
|
||||
});
|
||||
@@ -198,7 +201,8 @@ describe("local databases", () => {
|
||||
]);
|
||||
|
||||
const mockEvent = {
|
||||
item: undefined,
|
||||
fullRefresh: true,
|
||||
item: mockDbItem,
|
||||
kind: DatabaseEventKind.Add,
|
||||
};
|
||||
expect(onDidChangeDatabaseItem).toHaveBeenCalledWith(mockEvent);
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
import { ConfigurationTarget } from "vscode";
|
||||
import {
|
||||
VARIANT_ANALYSIS_ENABLE_GHEC_DR,
|
||||
VSCODE_GITHUB_ENTERPRISE_URI_SETTING,
|
||||
} from "../../../../src/config";
|
||||
import { VSCODE_GITHUB_ENTERPRISE_URI_SETTING } from "../../../../src/config";
|
||||
import { isVariantAnalysisEnabledForGitHubHost } from "../../../../src/variant-analysis/ghec-dr";
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
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(
|
||||
"https://github.example.com",
|
||||
ConfigurationTarget.Global,
|
||||
@@ -18,35 +15,11 @@ describe("checkVariantAnalysisEnabled", () => {
|
||||
expect(isVariantAnalysisEnabledForGitHubHost()).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false when GHES enterprise URI is set and variant analysis feature flag 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 () => {
|
||||
it("returns true when a GHEC-DR URI is set", async () => {
|
||||
await VSCODE_GITHUB_ENTERPRISE_URI_SETTING.updateValue(
|
||||
"https://example.ghe.com",
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
6
package-lock.json
generated
Normal file
6
package-lock.json
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "vscode-codeql",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
26
vscode-codeql.code-workspace
Normal file
26
vscode-codeql.code-workspace
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"typescript.tsdk": "./extensions/ql-vscode/node_modules/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||
"terminal.integrated.env.linux": {
|
||||
"LANG": "en-US",
|
||||
"TZ": "UTC"
|
||||
},
|
||||
"terminal.integrated.env.osx": {
|
||||
"LANG": "en-US",
|
||||
"TZ": "UTC"
|
||||
},
|
||||
"terminal.integrated.env.windows": {
|
||||
"LANG": "en-US",
|
||||
"TZ": "UTC"
|
||||
},
|
||||
"github.copilot.advanced": {},
|
||||
"github-enterprise.uri": "http://server:8080"
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user