From 65ae66aac255a62c7b988e14359c4bd0ba05b7fb Mon Sep 17 00:00:00 2001 From: Christopher Schleiden Date: Fri, 2 Dec 2022 11:28:03 -0800 Subject: [PATCH] Add `inputs` context-provider --- .../src/complete.expressions.test.ts | 32 +++++++++++- .../src/context-providers/default.ts | 9 +++- .../src/context-providers/inputs.ts | 51 +++++++++++++++++++ 3 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 actions-languageservice/src/context-providers/inputs.ts diff --git a/actions-languageservice/src/complete.expressions.test.ts b/actions-languageservice/src/complete.expressions.test.ts index cfd1ab4..c0b4167 100644 --- a/actions-languageservice/src/complete.expressions.test.ts +++ b/actions-languageservice/src/complete.expressions.test.ts @@ -166,7 +166,7 @@ jobs: needs: [b] runs-on: ubuntu-latest steps: - - run: echo "hello \${{ needs.| + - run: echo "hello \${{ needs.| `; const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); @@ -185,7 +185,7 @@ jobs: needs: [a] runs-on: ubuntu-latest steps: - - run: echo "hello \${{ needs.a.| + - run: echo "hello \${{ needs.a.| `; const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); @@ -212,4 +212,32 @@ jobs: expect(result.map(x => x.label)).toEqual(["build_id"]); }); + + it("inputs", async () => { + const input = ` +on: + workflow_dispatch: + inputs: + name: + type: string + default: some value + another-name: + type: string +jobs: + a: + outputs: + build_id: my-build-id + runs-on: ubuntu-latest + steps: + - run: echo hello a + b: + needs: [a] + runs-on: ubuntu-latest + steps: + - run: echo "hello \${{ inputs.| +`; + const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); + + expect(result.map(x => x.label)).toEqual(["another-name", "name"]); + }); }); diff --git a/actions-languageservice/src/context-providers/default.ts b/actions-languageservice/src/context-providers/default.ts index d47c876..bb0b28d 100644 --- a/actions-languageservice/src/context-providers/default.ts +++ b/actions-languageservice/src/context-providers/default.ts @@ -1,6 +1,7 @@ import {data} from "@github/actions-expressions"; import {WorkflowContext} from "../context/workflow-context"; import {ContextProviderConfig} from "./config"; +import {getInputsContext} from "./inputs"; import {getNeedsContext} from "./needs"; export async function getContext( @@ -13,7 +14,7 @@ export async function getContext( for (const contextName of names) { let value: data.Dictionary | undefined; - value = getDefaultContext(contextName, workflowContext); + value = await getDefaultContext(contextName, workflowContext); if (!value) { value = await config?.getContext(contextName); @@ -29,7 +30,7 @@ export async function getContext( return context; } -function getDefaultContext(name: string, workflowContext: WorkflowContext): data.Dictionary | undefined { +async function getDefaultContext(name: string, workflowContext: WorkflowContext): Promise { switch (name) { case "runner": return objectToDictionary({ @@ -39,8 +40,12 @@ function getDefaultContext(name: string, workflowContext: WorkflowContext): data tool_cache: "/opt/hostedtoolcache", temp: "/home/runner/work/_temp" }); + case "needs": return getNeedsContext(workflowContext); + + case "inputs": + return getInputsContext(workflowContext); } return undefined; diff --git a/actions-languageservice/src/context-providers/inputs.ts b/actions-languageservice/src/context-providers/inputs.ts new file mode 100644 index 0000000..0d5a951 --- /dev/null +++ b/actions-languageservice/src/context-providers/inputs.ts @@ -0,0 +1,51 @@ +import {data} from "@github/actions-expressions"; +import {WorkflowContext} from "../context/workflow-context"; + +export function getInputsContext(workflowContext: WorkflowContext): data.Dictionary { + const d = new data.Dictionary(); + if (!workflowContext?.template?.events) { + return d; + } + + const event = workflowContext.template.events["workflow_dispatch"]; + if (!event) { + return d; + } + + const inputs = event.inputs; + for (const inputName of Object.keys(inputs)) { + const input = inputs[inputName]; + switch (input.type) { + case "choice": + if (input.default) { + d.add(inputName, new data.StringData(input.default as string)); + } else { + // Default to the first input or an empty string + d.add(inputName, new data.StringData((input.options || [""])[0])); + } + break; + + case "environment": + if (input.default) { + d.add(inputName, new data.StringData(input.default as string)); + } else { + // For now default to an empty value if there is no default value. This will always be an environment, so + // we could also dynamically look up environments and default to the first one, but leaving this as a + // future enhancement for now. + d.add(inputName, new data.StringData("")); + } + break; + + case "boolean": + d.add(inputName, new data.BooleanData((input.default as boolean) || false)); + break; + + case "string": + default: + d.add(inputName, new data.StringData((input.default as string) || inputName)); + break; + } + } + + return d; +}