Merge pull request #67 from github/cschleiden/link-to-actions
Provide links to open actions on GitHub
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
import {hover} from "@github/actions-languageservice/hover";
|
||||
import {documentLinks, hover, validate, ValidationConfig} from "@github/actions-languageservice";
|
||||
import {registerLogger, setLogLevel} from "@github/actions-languageservice/log";
|
||||
import {validate, ValidationConfig} from "@github/actions-languageservice/validate";
|
||||
import {Octokit} from "@octokit/rest";
|
||||
import {
|
||||
CompletionItem,
|
||||
Connection,
|
||||
DocumentLink,
|
||||
DocumentLinkParams,
|
||||
Hover,
|
||||
HoverParams,
|
||||
InitializeParams,
|
||||
@@ -61,7 +62,10 @@ export function initConnection(connection: Connection) {
|
||||
resolveProvider: false,
|
||||
triggerCharacters: [":", "."]
|
||||
},
|
||||
hoverProvider: true
|
||||
hoverProvider: true,
|
||||
documentLinkProvider: {
|
||||
resolveProvider: false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -103,7 +107,6 @@ export function initConnection(connection: Connection) {
|
||||
connection.sendDiagnostics({uri: textDocument.uri, diagnostics: result});
|
||||
}
|
||||
|
||||
// This handler provides the initial list of the completion items.
|
||||
connection.onCompletion(async ({position, textDocument}: TextDocumentPositionParams): Promise<CompletionItem[]> => {
|
||||
return await onCompletion(
|
||||
position,
|
||||
@@ -118,6 +121,10 @@ export function initConnection(connection: Connection) {
|
||||
return hover(documents.get(textDocument.uri)!, position);
|
||||
});
|
||||
|
||||
connection.onDocumentLinks(async ({textDocument}: DocumentLinkParams): Promise<DocumentLink[] | null> => {
|
||||
return documentLinks(documents.get(textDocument.uri)!);
|
||||
});
|
||||
|
||||
// Make the text document manager listen on the connection
|
||||
// for open, change and close text document events
|
||||
documents.listen(connection);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {data} from "@github/actions-expressions";
|
||||
import {ContextProviderConfig} from "@github/actions-languageservice/.";
|
||||
import {ContextProviderConfig} from "@github/actions-languageservice";
|
||||
import {WorkflowContext} from "@github/actions-languageservice/context/workflow-context";
|
||||
import {Octokit} from "@octokit/rest";
|
||||
import {getSecrets} from "./context-providers/secrets";
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import {documentLinks} from "./document-links";
|
||||
import {createDocument} from "./test-utils/document";
|
||||
|
||||
describe("documentLinks", () => {
|
||||
it("no links without actions", async () => {
|
||||
const input = `on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [self-hosted]
|
||||
steps:
|
||||
- run: echo "Hello World"`;
|
||||
const result = await documentLinks(createDocument("test.yaml", input));
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("no links for invalid workflow", async () => {
|
||||
const input = `onFOO: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [self-hosted]`;
|
||||
const result = await documentLinks(createDocument("test.yaml", input));
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("links for actions in workflow", async () => {
|
||||
const input = `on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [self-hosted]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: github/codeql-action/init@v2`;
|
||||
const result = await documentLinks(createDocument("test.yaml", input));
|
||||
expect(result).toEqual([
|
||||
{
|
||||
range: {
|
||||
end: {
|
||||
character: 31,
|
||||
line: 5
|
||||
},
|
||||
start: {
|
||||
character: 12,
|
||||
line: 5
|
||||
}
|
||||
},
|
||||
target: "https://www.github.com/actions/checkout/tree/v2/",
|
||||
tooltip: "Open action on GitHub"
|
||||
},
|
||||
{
|
||||
range: {
|
||||
end: {
|
||||
character: 31,
|
||||
line: 6
|
||||
},
|
||||
start: {
|
||||
character: 12,
|
||||
line: 6
|
||||
}
|
||||
},
|
||||
target: "https://www.github.com/actions/checkout/tree/v3/",
|
||||
tooltip: "Open action on GitHub"
|
||||
},
|
||||
{
|
||||
range: {
|
||||
end: {
|
||||
character: 40,
|
||||
line: 7
|
||||
},
|
||||
start: {
|
||||
character: 12,
|
||||
line: 7
|
||||
}
|
||||
},
|
||||
target: "https://www.github.com/github/codeql-action/tree/v2/init",
|
||||
tooltip: "Open action on GitHub"
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
import {convertWorkflowTemplate, parseWorkflow} from "@github/actions-workflow-parser";
|
||||
import {ErrorPolicy} from "@github/actions-workflow-parser/model/convert";
|
||||
import {File} from "@github/actions-workflow-parser/workflows/file";
|
||||
import {TextDocument} from "vscode-languageserver-textdocument";
|
||||
import {DocumentLink} from "vscode-languageserver-types";
|
||||
import {parseActionReference} from "./action";
|
||||
import {nullTrace} from "./nulltrace";
|
||||
import {mapRange} from "./utils/range";
|
||||
|
||||
export async function documentLinks(document: TextDocument): Promise<DocumentLink[]> {
|
||||
const file: File = {
|
||||
name: document.uri,
|
||||
content: document.getText()
|
||||
};
|
||||
|
||||
const result = parseWorkflow(file.name, [file], nullTrace);
|
||||
if (!result.value) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const template = convertWorkflowTemplate(result.context, result.value!, ErrorPolicy.TryConversion);
|
||||
|
||||
// Add links to referenced actions
|
||||
const actionLinks: DocumentLink[] = [];
|
||||
|
||||
// TODO: Support base uri for GHES
|
||||
const gitHubBaseUri = "https://www.github.com/";
|
||||
|
||||
for (const job of template?.jobs || []) {
|
||||
for (const step of job?.steps || []) {
|
||||
if ("uses" in step) {
|
||||
const actionRef = parseActionReference(step.uses.value);
|
||||
if (!actionRef) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const url = `${gitHubBaseUri}${actionRef.owner}/${actionRef.name}/tree/${actionRef.ref}/${
|
||||
actionRef.path || ""
|
||||
}`;
|
||||
|
||||
actionLinks.push({
|
||||
range: mapRange(step.uses.range),
|
||||
target: url,
|
||||
tooltip: `Open action on GitHub`
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [...actionLinks];
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
export {complete} from "./complete";
|
||||
export {ContextProviderConfig} from "./context-providers/config";
|
||||
export {documentLinks} from "./document-links";
|
||||
export {hover} from "./hover";
|
||||
export {Logger, LogLevel, registerLogger, setLogLevel} from "./log";
|
||||
export {validate} from "./validate";
|
||||
export {validate, ValidationConfig} from "./validate";
|
||||
export {ValueProviderConfig, ValueProviderKind} from "./value-providers/config";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {isScalar, parseWorkflow} from "@github/actions-workflow-parser/.";
|
||||
import {isScalar, parseWorkflow} from "@github/actions-workflow-parser";
|
||||
import {TemplateToken} from "@github/actions-workflow-parser/templates/tokens/template-token";
|
||||
import {TokenType} from "@github/actions-workflow-parser/templates/tokens/types";
|
||||
import {nullTrace} from "../nulltrace";
|
||||
|
||||
Reference in New Issue
Block a user