WIP: standalone MRVA
This commit is contained in:
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -111,4 +111,10 @@
|
|||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
},
|
},
|
||||||
|
"github.copilot.advanced": {
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
"codeQL.variantAnalysis.enableGhecDr": true,
|
||||||
|
"github-enterprise.uri": "http://localhost:8080/"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -339,13 +339,6 @@
|
|||||||
"title": "Variant analysis",
|
"title": "Variant analysis",
|
||||||
"order": 5,
|
"order": 5,
|
||||||
"properties": {
|
"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": {
|
"codeQL.variantAnalysis.defaultResultsFilter": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "all",
|
"default": "all",
|
||||||
@@ -1931,11 +1924,6 @@
|
|||||||
{
|
{
|
||||||
"view": "codeQLEvalLogViewer",
|
"view": "codeQLEvalLogViewer",
|
||||||
"contents": "Run the 'Show Evaluator Log (UI)' command on a CodeQL query run in the Query History view."
|
"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"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -290,7 +290,6 @@ export type DatabasePanelCommands = {
|
|||||||
"codeQLVariantAnalysisRepositories.openConfigFile": () => Promise<void>;
|
"codeQLVariantAnalysisRepositories.openConfigFile": () => Promise<void>;
|
||||||
"codeQLVariantAnalysisRepositories.addNewDatabase": () => Promise<void>;
|
"codeQLVariantAnalysisRepositories.addNewDatabase": () => Promise<void>;
|
||||||
"codeQLVariantAnalysisRepositories.addNewList": () => Promise<void>;
|
"codeQLVariantAnalysisRepositories.addNewList": () => Promise<void>;
|
||||||
"codeQLVariantAnalysisRepositories.setupControllerRepository": () => Promise<void>;
|
|
||||||
|
|
||||||
"codeQLVariantAnalysisRepositories.setSelectedItem": TreeViewContextSingleSelectionCommandFunction<DbTreeViewItem>;
|
"codeQLVariantAnalysisRepositories.setSelectedItem": TreeViewContextSingleSelectionCommandFunction<DbTreeViewItem>;
|
||||||
"codeQLVariantAnalysisRepositories.setSelectedItemContextMenu": TreeViewContextSingleSelectionCommandFunction<DbTreeViewItem>;
|
"codeQLVariantAnalysisRepositories.setSelectedItemContextMenu": TreeViewContextSingleSelectionCommandFunction<DbTreeViewItem>;
|
||||||
|
|||||||
@@ -112,7 +112,9 @@ export function hasEnterpriseUri(): boolean {
|
|||||||
* Does the uri look like GHEC-DR?
|
* Does the uri look like GHEC-DR?
|
||||||
*/
|
*/
|
||||||
function isGhecDrUri(uri: Uri | undefined): boolean {
|
function isGhecDrUri(uri: Uri | undefined): boolean {
|
||||||
return uri !== undefined && uri.authority.toLowerCase().endsWith(".ghe.com");
|
return (
|
||||||
|
uri !== undefined && !uri.authority.toLowerCase().endsWith("github.com")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -591,27 +593,7 @@ export const NO_CACHE_CONTEXTUAL_QUERIES = new Setting(
|
|||||||
// Settings for variant analysis
|
// Settings for variant analysis
|
||||||
const VARIANT_ANALYSIS_SETTING = new Setting("variantAnalysis", ROOT_SETTING);
|
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 {
|
export interface VariantAnalysisConfig {
|
||||||
controllerRepo: string | undefined;
|
|
||||||
showSystemDefinedRepositoryLists: boolean;
|
showSystemDefinedRepositoryLists: boolean;
|
||||||
/**
|
/**
|
||||||
* This uses a URL instead of a URI because the URL class is available in
|
* This uses a URL instead of a URI because the URL class is available in
|
||||||
@@ -632,10 +614,6 @@ export class VariantAnalysisConfigListener
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get controllerRepo(): string | undefined {
|
|
||||||
return getRemoteControllerRepo();
|
|
||||||
}
|
|
||||||
|
|
||||||
public get showSystemDefinedRepositoryLists(): boolean {
|
public get showSystemDefinedRepositoryLists(): boolean {
|
||||||
return !hasEnterpriseUri();
|
return !hasEnterpriseUri();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ import type { DbManager } from "../db-manager";
|
|||||||
import { DbTreeDataProvider } from "./db-tree-data-provider";
|
import { DbTreeDataProvider } from "./db-tree-data-provider";
|
||||||
import type { DbTreeViewItem } from "./db-tree-view-item";
|
import type { DbTreeViewItem } from "./db-tree-view-item";
|
||||||
import { getGitHubUrl } from "./db-tree-view-item-action";
|
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 { DatabasePanelCommands } from "../../common/commands";
|
||||||
import type { App } from "../../common/app";
|
import type { App } from "../../common/app";
|
||||||
import { QueryLanguage } from "../../common/query-language";
|
import { QueryLanguage } from "../../common/query-language";
|
||||||
@@ -74,9 +72,6 @@ export class DbPanel extends DisposableObject {
|
|||||||
this.addNewRemoteDatabase.bind(this),
|
this.addNewRemoteDatabase.bind(this),
|
||||||
"codeQLVariantAnalysisRepositories.addNewList":
|
"codeQLVariantAnalysisRepositories.addNewList":
|
||||||
this.addNewList.bind(this),
|
this.addNewList.bind(this),
|
||||||
"codeQLVariantAnalysisRepositories.setupControllerRepository":
|
|
||||||
this.setupControllerRepository.bind(this),
|
|
||||||
|
|
||||||
"codeQLVariantAnalysisRepositories.setSelectedItem":
|
"codeQLVariantAnalysisRepositories.setSelectedItem":
|
||||||
this.setSelectedItem.bind(this),
|
this.setSelectedItem.bind(this),
|
||||||
"codeQLVariantAnalysisRepositories.setSelectedItemContextMenu":
|
"codeQLVariantAnalysisRepositories.setSelectedItemContextMenu":
|
||||||
@@ -427,22 +422,4 @@ export class DbPanel extends DisposableObject {
|
|||||||
|
|
||||||
await this.app.commands.execute("vscode.open", Uri.parse(githubUrl));
|
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[] {
|
private createTree(): DbTreeViewItem[] {
|
||||||
// Returning an empty tree here will show the welcome view
|
|
||||||
if (!this.variantAnalysisConfig.controllerRepo) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const dbItemsResult = this.dbManager.getDbItems();
|
const dbItemsResult = this.dbManager.getDbItems();
|
||||||
|
|
||||||
if (dbItemsResult.isFailure) {
|
if (dbItemsResult.isFailure) {
|
||||||
|
|||||||
@@ -48,11 +48,6 @@ function mapVariantAnalysisDtoToDto(
|
|||||||
): VariantAnalysisDto {
|
): VariantAnalysisDto {
|
||||||
return {
|
return {
|
||||||
id: variantAnalysis.id,
|
id: variantAnalysis.id,
|
||||||
controllerRepo: {
|
|
||||||
id: variantAnalysis.controllerRepo.id,
|
|
||||||
fullName: variantAnalysis.controllerRepo.fullName,
|
|
||||||
private: variantAnalysis.controllerRepo.private,
|
|
||||||
},
|
|
||||||
query: {
|
query: {
|
||||||
name: variantAnalysis.query.name,
|
name: variantAnalysis.query.name,
|
||||||
filePath: variantAnalysis.query.filePath,
|
filePath: variantAnalysis.query.filePath,
|
||||||
|
|||||||
@@ -48,12 +48,12 @@ function mapVariantAnalysisToDomainModel(
|
|||||||
): VariantAnalysis {
|
): VariantAnalysis {
|
||||||
return {
|
return {
|
||||||
id: variantAnalysis.id,
|
id: variantAnalysis.id,
|
||||||
controllerRepo: {
|
|
||||||
id: variantAnalysis.controllerRepo.id,
|
|
||||||
fullName: variantAnalysis.controllerRepo.fullName,
|
|
||||||
private: variantAnalysis.controllerRepo.private,
|
|
||||||
},
|
|
||||||
language: mapQueryLanguageToDomainModel(variantAnalysis.query.language),
|
language: mapQueryLanguageToDomainModel(variantAnalysis.query.language),
|
||||||
|
controllerRepo: {
|
||||||
|
id: 0,
|
||||||
|
fullName: "",
|
||||||
|
private: false,
|
||||||
|
},
|
||||||
query: {
|
query: {
|
||||||
name: variantAnalysis.query.name,
|
name: variantAnalysis.query.name,
|
||||||
filePath: variantAnalysis.query.filePath,
|
filePath: variantAnalysis.query.filePath,
|
||||||
|
|||||||
@@ -15,11 +15,6 @@ export interface QueryHistoryVariantAnalysisDto {
|
|||||||
|
|
||||||
export interface VariantAnalysisDto {
|
export interface VariantAnalysisDto {
|
||||||
id: number;
|
id: number;
|
||||||
controllerRepo: {
|
|
||||||
id: number;
|
|
||||||
fullName: string;
|
|
||||||
private: boolean;
|
|
||||||
};
|
|
||||||
query: {
|
query: {
|
||||||
name: string;
|
name: string;
|
||||||
filePath: string;
|
filePath: string;
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ async function exportVariantAnalysisAnalysisResults(
|
|||||||
expectedAnalysesResultsCount: number,
|
expectedAnalysesResultsCount: number,
|
||||||
exportFormat: "gist" | "local",
|
exportFormat: "gist" | "local",
|
||||||
commandManager: AppCommandManager,
|
commandManager: AppCommandManager,
|
||||||
credentials: Credentials,
|
_credentials: Credentials,
|
||||||
progress: ProgressCallback,
|
progress: ProgressCallback,
|
||||||
token: CancellationToken,
|
token: CancellationToken,
|
||||||
) {
|
) {
|
||||||
@@ -191,7 +191,6 @@ async function exportVariantAnalysisAnalysisResults(
|
|||||||
markdownFiles,
|
markdownFiles,
|
||||||
exportFormat,
|
exportFormat,
|
||||||
commandManager,
|
commandManager,
|
||||||
credentials,
|
|
||||||
progress,
|
progress,
|
||||||
token,
|
token,
|
||||||
);
|
);
|
||||||
@@ -236,7 +235,6 @@ async function exportResults(
|
|||||||
markdownFiles: MarkdownFile[],
|
markdownFiles: MarkdownFile[],
|
||||||
exportFormat: "gist" | "local",
|
exportFormat: "gist" | "local",
|
||||||
commandManager: AppCommandManager,
|
commandManager: AppCommandManager,
|
||||||
credentials: Credentials,
|
|
||||||
progress?: ProgressCallback,
|
progress?: ProgressCallback,
|
||||||
token?: CancellationToken,
|
token?: CancellationToken,
|
||||||
) {
|
) {
|
||||||
@@ -249,7 +247,6 @@ async function exportResults(
|
|||||||
description,
|
description,
|
||||||
markdownFiles,
|
markdownFiles,
|
||||||
commandManager,
|
commandManager,
|
||||||
credentials,
|
|
||||||
progress,
|
progress,
|
||||||
token,
|
token,
|
||||||
);
|
);
|
||||||
@@ -268,7 +265,6 @@ async function exportToGist(
|
|||||||
description: string,
|
description: string,
|
||||||
markdownFiles: MarkdownFile[],
|
markdownFiles: MarkdownFile[],
|
||||||
commandManager: AppCommandManager,
|
commandManager: AppCommandManager,
|
||||||
credentials: Credentials,
|
|
||||||
progress?: ProgressCallback,
|
progress?: ProgressCallback,
|
||||||
token?: CancellationToken,
|
token?: CancellationToken,
|
||||||
) {
|
) {
|
||||||
@@ -291,7 +287,7 @@ async function exportToGist(
|
|||||||
{} as { [key: string]: { content: string } },
|
{} as { [key: string]: { content: string } },
|
||||||
);
|
);
|
||||||
|
|
||||||
const gistUrl = await createGist(credentials, description, gistFiles);
|
const gistUrl = await createGist(description, gistFiles);
|
||||||
if (gistUrl) {
|
if (gistUrl) {
|
||||||
// This needs to use .then to ensure we aren't keeping the progress notification open. We shouldn't await the
|
// This needs to use .then to ensure we aren't keeping the progress notification open. We shouldn't await the
|
||||||
// "Open gist" button click.
|
// "Open gist" button click.
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { OctokitResponse } from "@octokit/types/dist-types";
|
import { getGitHubInstanceUrl } from "../../config";
|
||||||
import type { Credentials } from "../../common/authentication";
|
|
||||||
import type { VariantAnalysisSubmission } from "../shared/variant-analysis";
|
import type { VariantAnalysisSubmission } from "../shared/variant-analysis";
|
||||||
import type {
|
import type {
|
||||||
VariantAnalysis,
|
VariantAnalysis,
|
||||||
@@ -7,84 +6,142 @@ import type {
|
|||||||
VariantAnalysisSubmissionRequest,
|
VariantAnalysisSubmissionRequest,
|
||||||
} from "./variant-analysis";
|
} from "./variant-analysis";
|
||||||
import type { Repository } from "./repository";
|
import type { Repository } from "./repository";
|
||||||
|
import { extLogger } from "../../common/logging/vscode";
|
||||||
|
|
||||||
export async function submitVariantAnalysis(
|
function getOctokitBaseUrl(): string {
|
||||||
credentials: Credentials,
|
let apiUrl = getGitHubInstanceUrl().toString();
|
||||||
submissionDetails: VariantAnalysisSubmission,
|
if (apiUrl.endsWith("/")) {
|
||||||
): Promise<VariantAnalysis> {
|
apiUrl = apiUrl.slice(0, -1);
|
||||||
const octokit = await credentials.getOctokit();
|
}
|
||||||
|
if (apiUrl.startsWith("https://")) {
|
||||||
const { actionRepoRef, language, pack, databases, controllerRepoId } =
|
apiUrl = apiUrl.replace("https://", "http://");
|
||||||
submissionDetails;
|
}
|
||||||
|
return apiUrl;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(
|
export async function getVariantAnalysis(
|
||||||
credentials: Credentials,
|
|
||||||
controllerRepoId: number,
|
controllerRepoId: number,
|
||||||
variantAnalysisId: number,
|
variantAnalysisId: number,
|
||||||
): Promise<VariantAnalysis> {
|
): Promise<VariantAnalysis> {
|
||||||
const octokit = await credentials.getOctokit();
|
const baseUrl = getOctokitBaseUrl();
|
||||||
|
|
||||||
const response: OctokitResponse<VariantAnalysis> = await octokit.request(
|
const response = await fetch(
|
||||||
"GET /repositories/:controllerRepoId/code-scanning/codeql/variant-analyses/:variantAnalysisId",
|
`${baseUrl}/repositories/${controllerRepoId}/code-scanning/codeql/variant-analyses/${variantAnalysisId}`,
|
||||||
{
|
{
|
||||||
controllerRepoId,
|
method: "GET",
|
||||||
variantAnalysisId,
|
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(
|
export async function getVariantAnalysisRepo(
|
||||||
credentials: Credentials,
|
|
||||||
controllerRepoId: number,
|
controllerRepoId: number,
|
||||||
variantAnalysisId: number,
|
variantAnalysisId: number,
|
||||||
repoId: number,
|
repoId: number,
|
||||||
): Promise<VariantAnalysisRepoTask> {
|
): Promise<VariantAnalysisRepoTask> {
|
||||||
const octokit = await credentials.getOctokit();
|
const baseUrl = getOctokitBaseUrl();
|
||||||
|
|
||||||
const response: OctokitResponse<VariantAnalysisRepoTask> =
|
const response = await fetch(
|
||||||
await octokit.request(
|
`${baseUrl}/repositories/${controllerRepoId}/code-scanning/codeql/variant-analyses/${variantAnalysisId}/repositories/${repoId}`,
|
||||||
"GET /repositories/:controllerRepoId/code-scanning/codeql/variant-analyses/:variantAnalysisId/repositories/:repoId",
|
{
|
||||||
{
|
method: "GET",
|
||||||
controllerRepoId,
|
headers: {
|
||||||
variantAnalysisId,
|
"Content-Type": "application/json",
|
||||||
repoId,
|
|
||||||
},
|
},
|
||||||
);
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return response.data;
|
if (!response.ok) {
|
||||||
|
throw new Error(
|
||||||
|
`Error getting variant analysis repo: ${response.statusText}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getRepositoryFromNwo(
|
export async function getRepositoryFromNwo(
|
||||||
credentials: Credentials,
|
|
||||||
owner: string,
|
owner: string,
|
||||||
repo: string,
|
repo: string,
|
||||||
): Promise<Repository> {
|
): Promise<Repository> {
|
||||||
const octokit = await credentials.getOctokit();
|
const baseUrl = getOctokitBaseUrl();
|
||||||
|
|
||||||
const response = await octokit.rest.repos.get({ owner, repo });
|
const response = await fetch(`${baseUrl}/repos/${owner}/${repo}`, {
|
||||||
return response.data as Repository;
|
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.
|
* Returns the URL of the created gist.
|
||||||
*/
|
*/
|
||||||
export async function createGist(
|
export async function createGist(
|
||||||
credentials: Credentials,
|
|
||||||
description: string,
|
description: string,
|
||||||
files: { [key: string]: { content: string } },
|
files: { [key: string]: { content: string } },
|
||||||
): Promise<string | undefined> {
|
): Promise<string | undefined> {
|
||||||
const octokit = await credentials.getOctokit();
|
const baseUrl = getOctokitBaseUrl();
|
||||||
const response = await octokit.request("POST /gists", {
|
|
||||||
description,
|
const response = await fetch(`${baseUrl}/gists`, {
|
||||||
files,
|
method: "POST",
|
||||||
public: false,
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
description,
|
||||||
|
files,
|
||||||
|
public: false,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
if (response.status >= 300) {
|
|
||||||
|
if (!response.ok) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Error exporting variant analysis results: ${response.status} ${
|
`Error creating gist: ${response.status} ${response.statusText}`,
|
||||||
response?.data || ""
|
|
||||||
}`,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return response.data.html_url;
|
|
||||||
|
const data = await response.json();
|
||||||
|
return data.html_url;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { CancellationToken } from "vscode";
|
import type { CancellationToken } from "vscode";
|
||||||
import { Uri, window } from "vscode";
|
import { Uri } from "vscode";
|
||||||
import { join, sep, basename, relative } from "path";
|
import { join, sep, basename, relative } from "path";
|
||||||
import { dump, load } from "js-yaml";
|
import { dump, load } from "js-yaml";
|
||||||
import { copy, writeFile, readFile, mkdirp } from "fs-extra";
|
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 { dir, tmpName } from "tmp-promise";
|
||||||
import { tmpDir } from "../tmp-dir";
|
import { tmpDir } from "../tmp-dir";
|
||||||
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
|
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
|
||||||
import type { Credentials } from "../common/authentication";
|
|
||||||
import type { CodeQLCliServer } from "../codeql-cli/cli";
|
import type { CodeQLCliServer } from "../codeql-cli/cli";
|
||||||
import { extLogger } from "../common/logging/vscode";
|
import { extLogger } from "../common/logging/vscode";
|
||||||
import {
|
import { getActionBranch } from "../config";
|
||||||
getActionBranch,
|
|
||||||
getRemoteControllerRepo,
|
|
||||||
setRemoteControllerRepo,
|
|
||||||
} from "../config";
|
|
||||||
import type { ProgressCallback } from "../common/vscode/progress";
|
import type { ProgressCallback } from "../common/vscode/progress";
|
||||||
import { UserCancellationException } from "../common/vscode/progress";
|
import { UserCancellationException } from "../common/vscode/progress";
|
||||||
import type { RequestError } from "@octokit/types/dist-types";
|
|
||||||
import type { QueryMetadata } from "../common/interface-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 type { RepositorySelection } from "./repository-selection";
|
||||||
import {
|
import {
|
||||||
getRepositorySelection,
|
getRepositorySelection,
|
||||||
isValidSelection,
|
isValidSelection,
|
||||||
} from "./repository-selection";
|
} from "./repository-selection";
|
||||||
import type { Repository } from "./shared/repository";
|
|
||||||
import type { DbManager } from "../databases/db-manager";
|
import type { DbManager } from "../databases/db-manager";
|
||||||
import {
|
import {
|
||||||
getQlPackFilePath,
|
getQlPackFilePath,
|
||||||
@@ -285,13 +276,11 @@ interface PreparedRemoteQuery {
|
|||||||
base64Pack: string;
|
base64Pack: string;
|
||||||
modelPacks: ModelPackDetails[];
|
modelPacks: ModelPackDetails[];
|
||||||
repoSelection: RepositorySelection;
|
repoSelection: RepositorySelection;
|
||||||
controllerRepo: Repository;
|
|
||||||
queryStartTime: number;
|
queryStartTime: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function prepareRemoteQueryRun(
|
export async function prepareRemoteQueryRun(
|
||||||
cliServer: CodeQLCliServer,
|
cliServer: CodeQLCliServer,
|
||||||
credentials: Credentials,
|
|
||||||
qlPackDetails: QlPackDetails,
|
qlPackDetails: QlPackDetails,
|
||||||
progress: ProgressCallback,
|
progress: ProgressCallback,
|
||||||
token: CancellationToken,
|
token: CancellationToken,
|
||||||
@@ -322,8 +311,6 @@ export async function prepareRemoteQueryRun(
|
|||||||
message: "Determining controller repo",
|
message: "Determining controller repo",
|
||||||
});
|
});
|
||||||
|
|
||||||
const controllerRepo = await getControllerRepo(credentials);
|
|
||||||
|
|
||||||
progress({
|
progress({
|
||||||
maxStep: 4,
|
maxStep: 4,
|
||||||
step: 3,
|
step: 3,
|
||||||
@@ -367,7 +354,6 @@ export async function prepareRemoteQueryRun(
|
|||||||
base64Pack: generatedPack.base64Pack,
|
base64Pack: generatedPack.base64Pack,
|
||||||
modelPacks: generatedPack.modelPacks,
|
modelPacks: generatedPack.modelPacks,
|
||||||
repoSelection,
|
repoSelection,
|
||||||
controllerRepo,
|
|
||||||
queryStartTime,
|
queryStartTime,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -494,84 +480,6 @@ export function getQueryName(
|
|||||||
return queryMetadata?.name ?? basename(queryFilePath);
|
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) {
|
function removeWorkspaceRefs(qlpack: QlPackFile) {
|
||||||
if (!qlpack.dependencies) {
|
if (!qlpack.dependencies) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -373,16 +373,19 @@ export class VariantAnalysisManager
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// log to extLogger
|
||||||
|
void this.app.logger.log(
|
||||||
|
`Running variant analysis with query: ${queryName}, language: ${variantAnalysisLanguage}`,
|
||||||
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
actionBranch,
|
actionBranch,
|
||||||
base64Pack,
|
base64Pack,
|
||||||
modelPacks,
|
modelPacks,
|
||||||
repoSelection,
|
repoSelection,
|
||||||
controllerRepo,
|
|
||||||
queryStartTime,
|
queryStartTime,
|
||||||
} = await prepareRemoteQueryRun(
|
} = await prepareRemoteQueryRun(
|
||||||
this.cliServer,
|
this.cliServer,
|
||||||
this.app.credentials,
|
|
||||||
qlPackDetails,
|
qlPackDetails,
|
||||||
progress,
|
progress,
|
||||||
token,
|
token,
|
||||||
@@ -399,12 +402,15 @@ export class VariantAnalysisManager
|
|||||||
count: qlPackDetails.queryFiles.length,
|
count: qlPackDetails.queryFiles.length,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// log that submitting
|
||||||
|
void this.app.logger.log("Submitting variant analysis");
|
||||||
|
|
||||||
const variantAnalysisSubmission: VariantAnalysisSubmission = {
|
const variantAnalysisSubmission: VariantAnalysisSubmission = {
|
||||||
startTime: queryStartTime,
|
startTime: queryStartTime,
|
||||||
actionRepoRef: actionBranch,
|
actionRepoRef: actionBranch,
|
||||||
controllerRepoId: controllerRepo.id,
|
|
||||||
language: variantAnalysisLanguage,
|
language: variantAnalysisLanguage,
|
||||||
pack: base64Pack,
|
pack: base64Pack,
|
||||||
|
controllerRepoId: 0,
|
||||||
query: {
|
query: {
|
||||||
name: queryName,
|
name: queryName,
|
||||||
filePath: firstQueryFile,
|
filePath: firstQueryFile,
|
||||||
@@ -422,7 +428,6 @@ export class VariantAnalysisManager
|
|||||||
let variantAnalysisResponse: ApiVariantAnalysis;
|
let variantAnalysisResponse: ApiVariantAnalysis;
|
||||||
try {
|
try {
|
||||||
variantAnalysisResponse = await submitVariantAnalysis(
|
variantAnalysisResponse = await submitVariantAnalysis(
|
||||||
this.app.credentials,
|
|
||||||
variantAnalysisSubmission,
|
variantAnalysisSubmission,
|
||||||
);
|
);
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
@@ -431,9 +436,17 @@ export class VariantAnalysisManager
|
|||||||
e instanceof RequestError &&
|
e instanceof RequestError &&
|
||||||
handleRequestError(e, this.config.githubUrl, this.app.logger)
|
handleRequestError(e, this.config.githubUrl, this.app.logger)
|
||||||
) {
|
) {
|
||||||
|
// log
|
||||||
|
void this.app.logger.log(
|
||||||
|
`Error submitting variant analysis: ${getErrorMessage(e)}`,
|
||||||
|
);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// throwing
|
||||||
|
void this.app.logger.log(
|
||||||
|
`Error submitting variant analysis: ${getErrorMessage(e)}`,
|
||||||
|
);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -806,8 +819,7 @@ export class VariantAnalysisManager
|
|||||||
let repoTask: VariantAnalysisRepositoryTask;
|
let repoTask: VariantAnalysisRepositoryTask;
|
||||||
try {
|
try {
|
||||||
const repoTaskResponse = await getVariantAnalysisRepo(
|
const repoTaskResponse = await getVariantAnalysisRepo(
|
||||||
this.app.credentials,
|
0,
|
||||||
variantAnalysis.controllerRepo.id,
|
|
||||||
variantAnalysis.id,
|
variantAnalysis.id,
|
||||||
scannedRepo.repository.id,
|
scannedRepo.repository.id,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ export class VariantAnalysisMonitor extends DisposableObject {
|
|||||||
try {
|
try {
|
||||||
await this._monitorVariantAnalysis(
|
await this._monitorVariantAnalysis(
|
||||||
variantAnalysis.id,
|
variantAnalysis.id,
|
||||||
variantAnalysis.controllerRepo.id,
|
|
||||||
variantAnalysis.executionStartTime,
|
variantAnalysis.executionStartTime,
|
||||||
variantAnalysis.query.name,
|
variantAnalysis.query.name,
|
||||||
variantAnalysis.language,
|
variantAnalysis.language,
|
||||||
@@ -74,7 +73,6 @@ export class VariantAnalysisMonitor extends DisposableObject {
|
|||||||
|
|
||||||
private async _monitorVariantAnalysis(
|
private async _monitorVariantAnalysis(
|
||||||
variantAnalysisId: number,
|
variantAnalysisId: number,
|
||||||
controllerRepoId: number,
|
|
||||||
executionStartTime: number,
|
executionStartTime: number,
|
||||||
queryName: string,
|
queryName: string,
|
||||||
language: QueryLanguage,
|
language: QueryLanguage,
|
||||||
@@ -97,11 +95,7 @@ export class VariantAnalysisMonitor extends DisposableObject {
|
|||||||
|
|
||||||
let variantAnalysisSummary: ApiVariantAnalysis;
|
let variantAnalysisSummary: ApiVariantAnalysis;
|
||||||
try {
|
try {
|
||||||
variantAnalysisSummary = await getVariantAnalysis(
|
variantAnalysisSummary = await getVariantAnalysis(0, variantAnalysisId);
|
||||||
this.app.credentials,
|
|
||||||
controllerRepoId,
|
|
||||||
variantAnalysisId,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const errorMessage = getErrorMessage(e);
|
const errorMessage = getErrorMessage(e);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user