From c7ad54d9c3d1d8623acc6b5cbe8460c44140b496 Mon Sep 17 00:00:00 2001 From: Beth Brennan Date: Tue, 13 Dec 2022 15:24:54 -0500 Subject: [PATCH 1/4] Inputs and cron in github.event context --- .../src/complete.expressions.test.ts | 101 +++++++++++++++--- .../src/context-providers/default.ts | 2 +- .../src/context-providers/github.ts | 31 +++++- .../src/validate.expressions.test.ts | 82 ++++++++++++++ 4 files changed, 202 insertions(+), 14 deletions(-) diff --git a/actions-languageservice/src/complete.expressions.test.ts b/actions-languageservice/src/complete.expressions.test.ts index 6af1866..3456ec2 100644 --- a/actions-languageservice/src/complete.expressions.test.ts +++ b/actions-languageservice/src/complete.expressions.test.ts @@ -280,6 +280,10 @@ on: default: some value another-name: type: string + workflow_call: + inputs: + third-name: + type: boolean jobs: a: outputs: @@ -295,7 +299,7 @@ jobs: `; const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); - expect(result.map(x => x.label)).toEqual(["another-name", "name"]); + expect(result.map(x => x.label)).toEqual(["another-name", "name", "third-name"]); }); it("no inputs", async () => { @@ -320,21 +324,94 @@ jobs: expect(result).toEqual([]); }); - it("github context includes expected keys", async () => { - const input = ` -on: push + describe("github context", () => { + it("includes expected keys", async () => { + const input = ` + on: push + + jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: echo \${{ github.| }} + `; -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - run: echo \${{ github.| }} + const result = await complete(...getPositionFromCursor(input), undefined, undefined); + + expect(result.map(x => x.label)).toContain("actor"); + }); + + it("includes event inputs", async () => { + const input = ` + on: + workflow_dispatch: + inputs: + name: + type: string + default: some value + another-name: + type: string + workflow_call: + inputs: + third-name: + type: boolean + 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 \${{ github.event.inputs.| `; + const result = await complete(...getPositionFromCursor(input), undefined, undefined); - const result = await complete(...getPositionFromCursor(input), undefined, undefined); + expect(result.map(x => x.label)).toEqual(["another-name", "name", "third-name"]); + }); - expect(result.map(x => x.label)).toContain("actor"); + it("excludes event inputs and cron when no relevant events", async () => { + const input = ` + on: push + + jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: echo \${{ github.event.| }} + `; + + const result = await complete(...getPositionFromCursor(input), undefined, undefined); + + expect(result.map(x => x.label)).not.toContain("inputs"); + expect(result.map(x => x.label)).not.toContain("cron"); + }); + + it("includes cron schedules", async () => { + const input = ` + on: + schedule: + - cron: '0 0 * * *' + + jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: echo \${{ github.event.| }} + `; + + const result = await complete(...getPositionFromCursor(input), undefined, undefined); + + expect(result.map(x => x.label)).toContain("cron"); + }); + + it("excludes cron scheudle when no schedule", async () => {}); }); describe("steps context", () => { diff --git a/actions-languageservice/src/context-providers/default.ts b/actions-languageservice/src/context-providers/default.ts index 5893c49..8b53961 100644 --- a/actions-languageservice/src/context-providers/default.ts +++ b/actions-languageservice/src/context-providers/default.ts @@ -46,7 +46,7 @@ export async function getContext( function getDefaultContext(name: string, workflowContext: WorkflowContext, mode: Mode): ContextValue | undefined { switch (name) { case "github": - return getGithubContext(); + return getGithubContext(workflowContext); case "inputs": return getInputsContext(workflowContext); diff --git a/actions-languageservice/src/context-providers/github.ts b/actions-languageservice/src/context-providers/github.ts index 0422bb9..ca71be4 100644 --- a/actions-languageservice/src/context-providers/github.ts +++ b/actions-languageservice/src/context-providers/github.ts @@ -1,6 +1,9 @@ import {data} from "@github/actions-expressions"; +import {ExpressionData, Pair} from "@github/actions-expressions/data/expressiondata"; +import {WorkflowContext} from "../context/workflow-context"; +import {getInputsContext} from "./inputs"; -export function getGithubContext(): data.Dictionary { +export function getGithubContext(workflowContext: WorkflowContext): data.Dictionary { // https://docs.github.com/en/actions/learn-github-actions/contexts#github-context const keys = [ "action", @@ -41,7 +44,33 @@ export function getGithubContext(): data.Dictionary { return new data.Dictionary( ...keys.map(key => { + if (key == "event") { + return {key, value: getEventContext(workflowContext)}; + } + return {key, value: new data.Null()}; }) ); } + +function getEventContext(workflowContext: WorkflowContext): ExpressionData { + const d = new data.Dictionary(); + const events = workflowContext?.template?.events; + + if (!events) { + return d; + } + + const inputs = getInputsContext(workflowContext); + if (inputs.values().length > 0) { + d.add("inputs", inputs); + } + + const schedule = events["schedule"]; + if (schedule && schedule.length > 0) { + const default_cron = schedule[0].cron; + d.add("cron", new data.StringData(default_cron)); + } + + return d; +} diff --git a/actions-languageservice/src/validate.expressions.test.ts b/actions-languageservice/src/validate.expressions.test.ts index b914ec9..6867dcd 100644 --- a/actions-languageservice/src/validate.expressions.test.ts +++ b/actions-languageservice/src/validate.expressions.test.ts @@ -777,4 +777,86 @@ jobs: ]); }); }); + + describe("github context", () => { + it("includes only expected keys", async () => { + const input = ` +on: push + +jobs: + test: + runs-on: ubuntu-latest + steps: + - run: echo \${{ github.action }} + - run: echo \${{ github.steps }} +`; + + const result = await validate(createDocument("wf.yaml", input)); + + expect(result).toEqual([ + { + message: "Context access might be invalid: steps", + range: { + end: { + character: 37, + line: 8 + }, + start: { + character: 18, + line: 8 + } + }, + severity: DiagnosticSeverity.Warning + } + ]); + }); + it("validates event inputs", async () => { + const input = ` +on: + workflow_dispatch: + inputs: + name: + type: string + default: some value + another-name: + type: string + workflow_call: + inputs: + third-name: + type: boolean +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 \${{ github.event.inputs.name }}" + - run: echo "hello \${{ github.event.inputs.third-name }}" + - run: echo "hello \${{ github.event.inputs.random }}" +`; + const result = await validate(createDocument("wf.yaml", input)); + + expect(result).toEqual([ + { + message: "Context access might be invalid: random", + range: { + end: { + character: 56, + line: 26 + }, + start: { + character: 23, + line: 26 + } + }, + severity: DiagnosticSeverity.Warning + } + ]); + }); + }); }); From ab39152e93e4b3297aa38497c848c4e6b378edfc Mon Sep 17 00:00:00 2001 From: Beth Brennan Date: Tue, 13 Dec 2022 15:25:20 -0500 Subject: [PATCH 2/4] Include workflow call inputs --- .../src/context-providers/inputs.ts | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/actions-languageservice/src/context-providers/inputs.ts b/actions-languageservice/src/context-providers/inputs.ts index 167669f..22d08f7 100644 --- a/actions-languageservice/src/context-providers/inputs.ts +++ b/actions-languageservice/src/context-providers/inputs.ts @@ -1,21 +1,29 @@ import {data} from "@github/actions-expressions"; +import {InputConfig} from "@github/actions-workflow-parser/model/workflow-template"; import {WorkflowContext} from "../context/workflow-context"; export function getInputsContext(workflowContext: WorkflowContext): data.Dictionary { const d = new data.Dictionary(); - if (!workflowContext?.template?.events) { + + const events = workflowContext?.template?.events; + if (!events) { return d; } - const event = workflowContext.template.events["workflow_dispatch"]; - if (!event) { - return d; + const dispatch = events["workflow_dispatch"]; + if (dispatch?.inputs) { + addInputs(d, dispatch.inputs); } - const inputs = event.inputs; - if (!inputs) { - return d; + const call = events["workflow_call"]; + if (call?.inputs) { + addInputs(d, call.inputs); } + + return d; +} + +function addInputs(d: data.Dictionary, inputs: {[inputName: string]: InputConfig}) { for (const inputName of Object.keys(inputs)) { const input = inputs[inputName]; switch (input.type) { @@ -49,6 +57,4 @@ export function getInputsContext(workflowContext: WorkflowContext): data.Diction break; } } - - return d; } From 7559608e9678222c566d72722f237664d8178e4a Mon Sep 17 00:00:00 2001 From: Beth Brennan Date: Tue, 13 Dec 2022 15:26:50 -0500 Subject: [PATCH 3/4] Remove unused import --- actions-languageservice/src/context-providers/github.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions-languageservice/src/context-providers/github.ts b/actions-languageservice/src/context-providers/github.ts index ca71be4..90e0e33 100644 --- a/actions-languageservice/src/context-providers/github.ts +++ b/actions-languageservice/src/context-providers/github.ts @@ -1,5 +1,5 @@ import {data} from "@github/actions-expressions"; -import {ExpressionData, Pair} from "@github/actions-expressions/data/expressiondata"; +import {ExpressionData} from "@github/actions-expressions/data/expressiondata"; import {WorkflowContext} from "../context/workflow-context"; import {getInputsContext} from "./inputs"; From e9fd1022e04be0104b79ac6b53c72d461e4e5e6f Mon Sep 17 00:00:00 2001 From: Beth Brennan Date: Mon, 19 Dec 2022 12:24:39 -0500 Subject: [PATCH 4/4] Clean tests, comment --- .../src/complete.expressions.test.ts | 23 ------------------- .../src/context-providers/github.ts | 1 + .../src/validate.expressions.test.ts | 11 ++------- 3 files changed, 3 insertions(+), 32 deletions(-) diff --git a/actions-languageservice/src/complete.expressions.test.ts b/actions-languageservice/src/complete.expressions.test.ts index 3456ec2..23ba40a 100644 --- a/actions-languageservice/src/complete.expressions.test.ts +++ b/actions-languageservice/src/complete.expressions.test.ts @@ -286,13 +286,6 @@ on: type: boolean 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.| @@ -308,13 +301,6 @@ on: workflow_dispatch: 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.| @@ -358,13 +344,6 @@ jobs: type: boolean 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 \${{ github.event.inputs.| @@ -410,8 +389,6 @@ jobs: expect(result.map(x => x.label)).toContain("cron"); }); - - it("excludes cron scheudle when no schedule", async () => {}); }); describe("steps context", () => { diff --git a/actions-languageservice/src/context-providers/github.ts b/actions-languageservice/src/context-providers/github.ts index 90e0e33..bd0917c 100644 --- a/actions-languageservice/src/context-providers/github.ts +++ b/actions-languageservice/src/context-providers/github.ts @@ -69,6 +69,7 @@ function getEventContext(workflowContext: WorkflowContext): ExpressionData { const schedule = events["schedule"]; if (schedule && schedule.length > 0) { const default_cron = schedule[0].cron; + // For now, default to the first cron expression only d.add("cron", new data.StringData(default_cron)); } diff --git a/actions-languageservice/src/validate.expressions.test.ts b/actions-languageservice/src/validate.expressions.test.ts index 6867dcd..d8a7a25 100644 --- a/actions-languageservice/src/validate.expressions.test.ts +++ b/actions-languageservice/src/validate.expressions.test.ts @@ -826,13 +826,6 @@ on: type: boolean 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 \${{ github.event.inputs.name }}" @@ -847,11 +840,11 @@ jobs: range: { end: { character: 56, - line: 26 + line: 19 }, start: { character: 23, - line: 26 + line: 19 } }, severity: DiagnosticSeverity.Warning