Use fetchActionMetadata to check action validity (#147)

This commit is contained in:
Laura Yu
2023-02-15 16:19:05 -08:00
committed by GitHub
parent a3d27a797b
commit d0be8df735
6 changed files with 74 additions and 44 deletions
+3 -3
View File
@@ -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<TextDocument> = 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;
@@ -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,
+6
View File
@@ -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;
@@ -14,7 +14,7 @@ export async function validateAction(
step: Step | undefined,
config: ValidationConfig | undefined
): Promise<void> {
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({
@@ -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<string, ActionInput> | 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
+2 -2
View File
@@ -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<ActionInputs | undefined>;
fetchActionMetadata?(action: ActionReference): Promise<ActionMetadata | undefined>;
fileProvider?: FileProvider;
};