diff --git a/package.json b/package.json index 8b4e7dd9..9df62bc0 100644 --- a/package.json +++ b/package.json @@ -159,6 +159,12 @@ "when": "viewItem =~ /run/", "icon": "$(globe)" }, + { + "command": "github-actions.workflow.run.importAnnotations", + "category": "GitHub Actions", + "title": "Import workflow run annotations", + "when": "viewItem =~ /run/" + }, { "command": "github-actions.workflow.logs", "category": "GitHub Actions", diff --git a/src/annotations/workflowRunAnnotations.test.ts b/src/annotations/workflowRunAnnotations.test.ts new file mode 100644 index 00000000..f20a9ecc --- /dev/null +++ b/src/annotations/workflowRunAnnotations.test.ts @@ -0,0 +1,30 @@ +import {toDiagnosticAnnotation} from "./workflowRunAnnotations"; + +describe("toDiagnosticAnnotation", () => { + it.each([ + ["failure", "error"], + ["warning", "warning"], + ["notice", "information"] + ] as const)("maps %s annotations to %s diagnostics", (annotation_level, severity) => { + expect( + toDiagnosticAnnotation({ + annotation_level, + end_column: 8, + end_line: 4, + message: "Expected a value", + path: "src/index.ts", + start_column: 2, + start_line: 3, + title: "Type check" + }) + ).toEqual({ + endColumn: 8, + endLine: 4, + message: "Type check: Expected a value", + path: "src/index.ts", + severity, + startColumn: 2, + startLine: 3 + }); + }); +}); diff --git a/src/annotations/workflowRunAnnotations.ts b/src/annotations/workflowRunAnnotations.ts new file mode 100644 index 00000000..a01fe4be --- /dev/null +++ b/src/annotations/workflowRunAnnotations.ts @@ -0,0 +1,39 @@ +export interface WorkflowRunAnnotation { + annotation_level: "failure" | "warning" | "notice"; + end_column: number; + end_line: number; + message: string; + path: string; + start_column: number; + start_line: number; + title: string; +} + +export type AnnotationSeverity = "error" | "warning" | "information"; + +export interface DiagnosticAnnotation { + endColumn: number; + endLine: number; + message: string; + path: string; + severity: AnnotationSeverity; + startColumn: number; + startLine: number; +} + +export function toDiagnosticAnnotation(annotation: WorkflowRunAnnotation): DiagnosticAnnotation { + return { + path: annotation.path, + startLine: annotation.start_line, + startColumn: annotation.start_column, + endLine: annotation.end_line, + endColumn: annotation.end_column, + message: annotation.title ? `${annotation.title}: ${annotation.message}` : annotation.message, + severity: + annotation.annotation_level === "failure" + ? "error" + : annotation.annotation_level === "warning" + ? "warning" + : "information" + }; +} diff --git a/src/commands/importWorkflowRunAnnotations.ts b/src/commands/importWorkflowRunAnnotations.ts new file mode 100644 index 00000000..d71c76ca --- /dev/null +++ b/src/commands/importWorkflowRunAnnotations.ts @@ -0,0 +1,53 @@ +import * as vscode from "vscode"; +import {toDiagnosticAnnotation} from "../annotations/workflowRunAnnotations"; +import {WorkflowRunCommandArgs} from "../treeViews/shared/workflowRunNode"; + +const diagnostics = vscode.languages.createDiagnosticCollection("github-actions"); + +export function registerImportWorkflowRunAnnotations(context: vscode.ExtensionContext) { + context.subscriptions.push(diagnostics); + context.subscriptions.push( + vscode.commands.registerCommand( + "github-actions.workflow.run.importAnnotations", + async (args: WorkflowRunCommandArgs) => { + const {gitHubRepoContext, run} = args; + const annotations = await gitHubRepoContext.client.paginate( + "GET /repos/{owner}/{repo}/actions/runs/{run_id}/annotations", + { + owner: gitHubRepoContext.owner, + repo: gitHubRepoContext.name, + run_id: run.run.id, + per_page: 100 + } + ); + + const byUri = new Map(); + for (const annotation of annotations) { + const diagnostic = toDiagnosticAnnotation(annotation); + const range = new vscode.Range( + new vscode.Position(Math.max(diagnostic.startLine - 1, 0), Math.max(diagnostic.startColumn - 1, 0)), + new vscode.Position(Math.max(diagnostic.endLine - 1, 0), Math.max(diagnostic.endColumn - 1, 0)) + ); + const item = new vscode.Diagnostic( + range, + diagnostic.message, + diagnostic.severity === "error" + ? vscode.DiagnosticSeverity.Error + : diagnostic.severity === "warning" + ? vscode.DiagnosticSeverity.Warning + : vscode.DiagnosticSeverity.Information + ); + item.source = "GitHub Actions"; + const uri = vscode.Uri.joinPath(gitHubRepoContext.workspaceUri, diagnostic.path).toString(); + byUri.set(uri, [...(byUri.get(uri) ?? []), item]); + } + + diagnostics.clear(); + for (const [uri, items] of byUri) { + diagnostics.set(vscode.Uri.parse(uri), items); + } + void vscode.window.showInformationMessage(`Imported ${annotations.length} workflow run annotation(s).`); + } + ) + ); +} diff --git a/src/extension.ts b/src/extension.ts index be9a157f..0e377099 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -35,6 +35,7 @@ import {initTreeViews} from "./treeViews/treeViews"; import {deactivateLanguageServer, initLanguageServer} from "./workflow/languageServer"; import {registerSignIn} from "./commands/signIn"; import {registerDebugger, registerDebuggerAvailabilityGuard} from "./debugger/debugger"; +import {registerImportWorkflowRunAnnotations} from "./commands/importWorkflowRunAnnotations"; export async function activate(context: vscode.ExtensionContext) { initLogger(); @@ -78,6 +79,7 @@ export async function activate(context: vscode.ExtensionContext) { registerTriggerWorkflowRun(context); registerReRunWorkflowRun(context); registerCancelWorkflowRun(context); + registerImportWorkflowRunAnnotations(context); registerAddSecret(context); registerDeleteSecret(context);