diff --git a/actions-languageserver/src/connection.ts b/actions-languageserver/src/connection.ts index be145ce..7747ded 100644 --- a/actions-languageserver/src/connection.ts +++ b/actions-languageserver/src/connection.ts @@ -24,9 +24,9 @@ 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 {TTLCache} from "./utils/cache"; import {valueProviders} from "./value-providers"; -import {getActionInputs} from "./value-providers/action-inputs"; export function initConnection(connection: Connection) { const documents: TextDocuments = new TextDocuments(TextDocument); @@ -102,9 +102,9 @@ export function initConnection(connection: Connection) { const config: ValidationConfig = { valueProviderConfig: valueProviders(client, repoContext, cache), contextProviderConfig: contextProviders(client, repoContext, cache), - getActionInputs: async action => { + fetchActionMetadata: async action => { if (client) { - return await getActionInputs(client, cache, action); + return await fetchActionMetadata(client, cache, action); } return undefined; diff --git a/actions-languageserver/src/utils/action-metadata.ts b/actions-languageserver/src/utils/action-metadata.ts index cf424f5..c23cfae 100644 --- a/actions-languageserver/src/utils/action-metadata.ts +++ b/actions-languageserver/src/utils/action-metadata.ts @@ -1,14 +1,9 @@ -import {ActionReference, ActionInputs, ActionOutputs, actionIdentifier} from "@github/actions-languageservice/action"; +import {ActionReference, ActionMetadata, actionIdentifier} from "@github/actions-languageservice/action"; import {error} from "@github/actions-languageservice/log"; import {Octokit, RestEndpointMethodTypes} from "@octokit/rest"; import {parse} from "yaml"; import {TTLCache} from "./cache"; -export type ActionMetadata = { - inputs?: ActionInputs; - outputs?: ActionOutputs; -}; - export async function fetchActionMetadata( client: Octokit, cache: TTLCache, diff --git a/actions-languageservice/src/action.ts b/actions-languageservice/src/action.ts index 4c35918..b10a166 100644 --- a/actions-languageservice/src/action.ts +++ b/actions-languageservice/src/action.ts @@ -1,3 +1,9 @@ +// https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions +export type ActionMetadata = { + inputs?: ActionInputs; + outputs?: ActionOutputs; +}; + // https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#inputs export type ActionInput = { description: string; diff --git a/actions-languageservice/src/validate-action.ts b/actions-languageservice/src/validate-action.ts index b5c1931..7e499e4 100644 --- a/actions-languageservice/src/validate-action.ts +++ b/actions-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?.getActionInputs) { + if (!isMapping(stepToken) || !step || !isActionStep(step) || !config?.fetchActionMetadata) { return; } @@ -23,8 +23,8 @@ export async function validateAction( return; } - const actionInputs = await config.getActionInputs(action); - if (actionInputs === undefined) { + const actionMetadata = await config.fetchActionMetadata(action); + if (actionMetadata === undefined) { diagnostics.push({ severity: DiagnosticSeverity.Error, range: mapRange(step.uses.range), @@ -50,6 +50,11 @@ export async function validateAction( } } + const actionInputs = actionMetadata.inputs; + if (actionInputs === undefined) { + return; + } + for (const [input, inputToken] of stepInputs) { if (!actionInputs[input]) { diagnostics.push({ diff --git a/actions-languageservice/src/validate.actions.test.ts b/actions-languageservice/src/validate.actions.test.ts index 0b01bff..fa00c5e 100644 --- a/actions-languageservice/src/validate.actions.test.ts +++ b/actions-languageservice/src/validate.actions.test.ts @@ -1,5 +1,5 @@ import {DiagnosticSeverity} from "vscode-languageserver-types"; -import {ActionInput, ActionReference} from "./action"; +import {ActionMetadata, ActionReference} from "./action"; import {registerLogger} from "./log"; import {createDocument} from "./test-utils/document"; import {TestLogger} from "./test-utils/logger"; @@ -9,53 +9,63 @@ import {ValueProviderKind} from "./value-providers/config"; registerLogger(new TestLogger()); const validationConfig: ValidationConfig = { - getActionInputs: async (ref: ActionReference) => { - let inputs: Record | undefined = undefined; + fetchActionMetadata: async (ref: ActionReference) => { + let metadata: ActionMetadata | undefined = undefined; switch (ref.owner + "/" + ref.name + "@" + ref.ref) { case "actions/checkout@v3": - inputs = { - repository: { - description: "Repository name with owner", - default: "${{ github.repository }}" + metadata = { + inputs: { + repository: { + description: "Repository name with owner", + default: "${{ github.repository }}" + } } }; break; case "actions/setup-node@v1": - 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" + metadata = { + 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": - inputs = { - token: { - required: true, - description: "token to use", - default: "${{ github.token }}" + metadata = { + inputs: { + token: { + required: true, + description: "token to use", + default: "${{ github.token }}" + } } }; break; case "actions/cache@v1": - 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 + metadata = { + 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 = {}; } - return inputs; + return metadata; } }; @@ -103,6 +113,20 @@ jobs: ]); }); + it("action does not define inputs", async () => { + const input = ` + on: push + jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/action-no-input@v1 + `; + const result = await validate(createDocument("wf.yaml", input), validationConfig); + + expect(result).toEqual([]); + }); + it("invalid input", async () => { const input = ` on: push diff --git a/actions-languageservice/src/validate.ts b/actions-languageservice/src/validate.ts index b466642..347300d 100644 --- a/actions-languageservice/src/validate.ts +++ b/actions-languageservice/src/validate.ts @@ -17,7 +17,7 @@ import {File} from "@github/actions-workflow-parser/workflows/file"; import {FileProvider} from "@github/actions-workflow-parser/workflows/file-provider"; import {TextDocument} from "vscode-languageserver-textdocument"; import {Diagnostic, DiagnosticSeverity, URI} from "vscode-languageserver-types"; -import {ActionInputs, ActionReference} from "./action"; +import {ActionMetadata, ActionReference} from "./action"; import {ContextProviderConfig} from "./context-providers/config"; import {getContext, Mode} from "./context-providers/default"; @@ -35,7 +35,7 @@ import {defaultValueProviders} from "./value-providers/default"; export type ValidationConfig = { valueProviderConfig?: ValueProviderConfig; contextProviderConfig?: ContextProviderConfig; - getActionInputs?(action: ActionReference): Promise; + fetchActionMetadata?(action: ActionReference): Promise; fileProvider?: FileProvider; };