From 50f9c04a91c175a9bf3032378891a851ccc3f9a9 Mon Sep 17 00:00:00 2001 From: Beth Brennan Date: Mon, 27 Mar 2023 11:56:41 -0400 Subject: [PATCH] Wrap fetchActionsMetadata in provider checking sessionToken --- languageserver/src/connection.ts | 10 +- languageserver/src/utils/action-metadata.ts | 14 ++ languageservice/src/index.ts | 2 +- languageservice/src/validate-action.ts | 4 +- languageservice/src/validate.actions.test.ts | 145 +++++++++++-------- languageservice/src/validate.ts | 6 +- 6 files changed, 105 insertions(+), 76 deletions(-) diff --git a/languageserver/src/connection.ts b/languageserver/src/connection.ts index c682442..274923a 100644 --- a/languageserver/src/connection.ts +++ b/languageserver/src/connection.ts @@ -26,7 +26,7 @@ import {getFileProvider} from "./file-provider"; import {InitializationOptions, RepositoryContext} from "./initializationOptions"; import {onCompletion} from "./on-completion"; import {ReadFileRequest, Requests} from "./request"; -import {fetchActionMetadata} from "./utils/action-metadata"; +import {getActionsMetadataProvider} from "./utils/action-metadata"; import {TTLCache} from "./utils/cache"; import {timeOperation} from "./utils/timer"; import {valueProviders} from "./value-providers"; @@ -108,13 +108,7 @@ export function initConnection(connection: Connection) { const config: ValidationConfig = { valueProviderConfig: valueProviders(client, repoContext, cache), contextProviderConfig: contextProviders(client, repoContext, cache), - fetchActionMetadata: async action => { - if (client) { - return await fetchActionMetadata(client, cache, action); - } - - return undefined; - }, + actionsMetadataProvider: getActionsMetadataProvider(client, cache), fileProvider: getFileProvider(client, cache, repoContext?.workspaceUri, async path => { return await connection.sendRequest(Requests.ReadFile, {path} satisfies ReadFileRequest); }) diff --git a/languageserver/src/utils/action-metadata.ts b/languageserver/src/utils/action-metadata.ts index ee100f6..dd0b630 100644 --- a/languageserver/src/utils/action-metadata.ts +++ b/languageserver/src/utils/action-metadata.ts @@ -1,10 +1,24 @@ import {actionIdentifier, ActionMetadata, ActionReference} from "@actions/languageservice/action"; +import {ActionsMetadataProvider} from "@actions/languageservice"; import {error} from "@actions/languageservice/log"; import {Octokit, RestEndpointMethodTypes} from "@octokit/rest"; import {parse} from "yaml"; import {TTLCache} from "./cache"; import {errorMessage, errorStatus} from "./error"; +export function getActionsMetadataProvider( + client: Octokit | undefined, + cache: TTLCache +): ActionsMetadataProvider | undefined { + if (!client) { + return undefined; + } + + return { + fetchActionMetadata: async action => fetchActionMetadata(client, cache, action) + }; +} + export async function fetchActionMetadata( client: Octokit, cache: TTLCache, diff --git a/languageservice/src/index.ts b/languageservice/src/index.ts index 4e43252..c96eb20 100644 --- a/languageservice/src/index.ts +++ b/languageservice/src/index.ts @@ -3,5 +3,5 @@ export {ContextProviderConfig} from "./context-providers/config"; export {documentLinks} from "./document-links"; export {hover} from "./hover"; export {Logger, LogLevel, registerLogger, setLogLevel} from "./log"; -export {validate, ValidationConfig} from "./validate"; +export {validate, ValidationConfig, ActionsMetadataProvider} from "./validate"; export {ValueProviderConfig, ValueProviderKind} from "./value-providers/config"; diff --git a/languageservice/src/validate-action.ts b/languageservice/src/validate-action.ts index 04533e7..c065434 100644 --- a/languageservice/src/validate-action.ts +++ b/languageservice/src/validate-action.ts @@ -14,7 +14,7 @@ export async function validateAction( step: Step | undefined, config: ValidationConfig | undefined ): Promise { - if (!isMapping(stepToken) || !step || !isActionStep(step) || !config?.fetchActionMetadata) { + if (!isMapping(stepToken) || !step || !isActionStep(step) || !config?.actionsMetadataProvider) { return; } @@ -23,7 +23,7 @@ export async function validateAction( return; } - const actionMetadata = await config.fetchActionMetadata(action); + const actionMetadata = await config.actionsMetadataProvider.fetchActionMetadata(action); if (actionMetadata === undefined) { diagnostics.push({ severity: DiagnosticSeverity.Error, diff --git a/languageservice/src/validate.actions.test.ts b/languageservice/src/validate.actions.test.ts index 81f61bd..c8fb4bf 100644 --- a/languageservice/src/validate.actions.test.ts +++ b/languageservice/src/validate.actions.test.ts @@ -14,75 +14,77 @@ beforeEach(() => { }); const validationConfig: ValidationConfig = { - fetchActionMetadata: (ref: ActionReference) => { - let metadata: ActionMetadata | undefined = undefined; - switch (ref.owner + "/" + ref.name + "@" + ref.ref) { - case "actions/checkout@v3": - metadata = { - name: "Checkout", - description: "Checkout a Git repository at a particular version", - inputs: { - repository: { - description: "Repository name with owner", - default: "${{ github.repository }}" + actionsMetadataProvider: { + fetchActionMetadata: (ref: ActionReference) => { + let metadata: ActionMetadata | undefined = undefined; + switch (ref.owner + "/" + ref.name + "@" + ref.ref) { + case "actions/checkout@v3": + metadata = { + name: "Checkout", + description: "Checkout a Git repository at a particular version", + inputs: { + repository: { + description: "Repository name with owner", + default: "${{ github.repository }}" + } } - } - }; - break; - case "actions/setup-node@v1": - metadata = { - name: "Setup Node.js environment", - description: - "Setup a Node.js environment by adding problem matchers and optionally downloading and adding it to the PATH.", - inputs: { - version: { - description: "Deprecated. Use node-version instead. Will not be supported after October 1, 2019", - deprecationMessage: - "The version property will not be supported after October 1, 2019. Use node-version instead" + }; + break; + case "actions/setup-node@v1": + metadata = { + name: "Setup Node.js environment", + description: + "Setup a Node.js environment by adding problem matchers and optionally downloading and adding it to the PATH.", + inputs: { + version: { + description: "Deprecated. Use node-version instead. Will not be supported after October 1, 2019", + deprecationMessage: + "The version property will not be supported after October 1, 2019. Use node-version instead" + } } - } - }; - break; - case "actions/deploy-pages@main": - metadata = { - name: "Deploy GitHub Pages site", - description: "A GitHub Action to deploy an artifact as a GitHub Pages site", - inputs: { - token: { - required: true, - description: "token to use", - default: "${{ github.token }}" + }; + break; + case "actions/deploy-pages@main": + metadata = { + name: "Deploy GitHub Pages site", + description: "A GitHub Action to deploy an artifact as a GitHub Pages site", + inputs: { + token: { + required: true, + description: "token to use", + default: "${{ github.token }}" + } } - } - }; - break; - case "actions/cache@v1": - metadata = { - name: "Cache", - description: "Cache artifacts like dependencies and build outputs to improve workflow execution time", - inputs: { - path: { - description: "A directory to store and save the cache", - required: true - }, - key: { - description: "An explicit key for restoring and saving the cache", - required: true - }, - "restore-keys": { - description: "An ordered list of keys to use for restoring the cache if no cache hit occurred for key", - required: false + }; + break; + case "actions/cache@v1": + metadata = { + name: "Cache", + description: "Cache artifacts like dependencies and build outputs to improve workflow execution time", + inputs: { + path: { + description: "A directory to store and save the cache", + required: true + }, + key: { + description: "An explicit key for restoring and saving the cache", + required: true + }, + "restore-keys": { + description: "An ordered list of keys to use for restoring the cache if no cache hit occurred for key", + required: false + } } - } - }; - break; - case "actions/action-no-input@v1": - metadata = { - name: "Action with no inputs", - description: "An action with no inputs" - }; + }; + break; + case "actions/action-no-input@v1": + metadata = { + name: "Action with no inputs", + description: "An action with no inputs" + }; + } + return Promise.resolve(metadata); } - return Promise.resolve(metadata); } }; @@ -101,6 +103,21 @@ jobs: expect(result).toEqual([]); }); + it("no actionsMetadataProvider", async () => { + const input = ` + on: push + jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/does-not-exist@v3 + `; + const config: ValidationConfig = {}; + const result = await validate(createDocument("wf.yaml", input), config); + + expect(result).toEqual([]); + }); + it("action does not exist", async () => { const input = ` on: push diff --git a/languageservice/src/validate.ts b/languageservice/src/validate.ts index 0b6aa4b..34ddea5 100644 --- a/languageservice/src/validate.ts +++ b/languageservice/src/validate.ts @@ -28,10 +28,14 @@ import {defaultValueProviders} from "./value-providers/default"; export type ValidationConfig = { valueProviderConfig?: ValueProviderConfig; contextProviderConfig?: ContextProviderConfig; - fetchActionMetadata?(action: ActionReference): Promise; + actionsMetadataProvider?: ActionsMetadataProvider; fileProvider?: FileProvider; }; +export type ActionsMetadataProvider = { + fetchActionMetadata(action: ActionReference): Promise; +}; + /** * Validates a workflow file *