Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# CodeQL for Visual Studio Code: Changelog
|
||||
|
||||
## 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,7 +108,7 @@ 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).
|
||||
@@ -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.
|
||||
|
||||
1686
extensions/ql-vscode/package-lock.json
generated
1686
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.1",
|
||||
"publisher": "GitHub",
|
||||
"license": "MIT",
|
||||
"icon": "media/VS-marketplace-CodeQL-icon.png",
|
||||
@@ -1955,14 +1955,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 +1987,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",
|
||||
@@ -2012,22 +2012,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.3",
|
||||
"@storybook/addon-actions": "^8.1.3",
|
||||
"@storybook/addon-essentials": "^8.1.3",
|
||||
"@storybook/addon-interactions": "^8.1.3",
|
||||
"@storybook/addon-links": "^8.1.3",
|
||||
"@storybook/blocks": "^8.0.2",
|
||||
"@storybook/components": "^8.0.2",
|
||||
"@storybook/csf": "^0.1.6",
|
||||
"@storybook/csf": "^0.1.7",
|
||||
"@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.3",
|
||||
"@storybook/react": "^8.1.3",
|
||||
"@storybook/react-vite": "^8.1.3",
|
||||
"@storybook/theming": "^8.1.3",
|
||||
"@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.5",
|
||||
"@testing-library/react": "^15.0.7",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@types/child-process-promise": "^2.2.1",
|
||||
"@types/d3": "^7.4.0",
|
||||
@@ -2041,8 +2041,8 @@
|
||||
"@types/nanoid": "^3.0.0",
|
||||
"@types/node": "18.18.*",
|
||||
"@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 +2066,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",
|
||||
@@ -2084,19 +2084,20 @@
|
||||
"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.3",
|
||||
"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-json-schema-generator": "^2.1.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"ts-unused-exports": "^10.0.0",
|
||||
"typescript": "^5.0.2",
|
||||
"vite": "^5.2.6"
|
||||
"vite": "^5.2.11",
|
||||
"vite-node": "^1.5.3"
|
||||
},
|
||||
"lint-staged": {
|
||||
"./**/*.{json,css,scss}": [
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,55 @@ 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(".ghe.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");
|
||||
@@ -570,6 +613,11 @@ export async function setRemoteControllerRepo(repo: string | undefined) {
|
||||
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>;
|
||||
}
|
||||
|
||||
@@ -591,6 +639,10 @@ export class VariantAnalysisConfigListener
|
||||
public get showSystemDefinedRepositoryLists(): boolean {
|
||||
return !hasEnterpriseUri();
|
||||
}
|
||||
|
||||
public get githubUrl(): URL {
|
||||
return getGitHubInstanceUrl();
|
||||
}
|
||||
}
|
||||
|
||||
const VARIANT_ANALYSIS_FILTER_RESULTS = new Setting(
|
||||
@@ -628,11 +680,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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ 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;
|
||||
@@ -146,16 +147,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 +180,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 +418,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.",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -864,8 +866,10 @@ async function activateWithInstalledDistribution(
|
||||
"variant-analyses",
|
||||
);
|
||||
await ensureDir(variantAnalysisStorageDir);
|
||||
const variantAnalysisConfig = new VariantAnalysisConfigListener();
|
||||
const variantAnalysisResultsManager = new VariantAnalysisResultsManager(
|
||||
cliServer,
|
||||
variantAnalysisConfig,
|
||||
extLogger,
|
||||
);
|
||||
|
||||
@@ -875,6 +879,7 @@ async function activateWithInstalledDistribution(
|
||||
variantAnalysisStorageDir,
|
||||
variantAnalysisResultsManager,
|
||||
dbModule.dbManager,
|
||||
variantAnalysisConfig,
|
||||
);
|
||||
ctx.subscriptions.push(variantAnalysisManager);
|
||||
ctx.subscriptions.push(variantAnalysisResultsManager);
|
||||
@@ -942,7 +947,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(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
@@ -426,7 +427,10 @@ export class VariantAnalysisManager
|
||||
);
|
||||
} 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)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -745,7 +749,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;
|
||||
}
|
||||
|
||||
@@ -951,7 +955,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",
|
||||
|
||||
@@ -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.3",
|
||||
"v2.16.6",
|
||||
"v2.15.5",
|
||||
"v2.14.6",
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user