From d96630c2761d5628c285088aab544a6ee1317362 Mon Sep 17 00:00:00 2001 From: Josh Gross Date: Tue, 6 Dec 2022 18:02:54 -0500 Subject: [PATCH] Support `strategy` context --- .../src/complete.expressions.test.ts | 61 ++++++++++++++ .../src/context-providers/default.ts | 4 + .../src/context-providers/strategy.ts | 12 +++ .../src/validate.expressions.test.ts | 82 +++++++++++++++++++ 4 files changed, 159 insertions(+) create mode 100644 actions-languageservice/src/context-providers/strategy.ts diff --git a/actions-languageservice/src/complete.expressions.test.ts b/actions-languageservice/src/complete.expressions.test.ts index b2788d6..18bab60 100644 --- a/actions-languageservice/src/complete.expressions.test.ts +++ b/actions-languageservice/src/complete.expressions.test.ts @@ -335,4 +335,65 @@ jobs: expect(result.map(x => x.label)).toEqual(["b"]); }); }); + + describe("strategy context", () => { + it.failing("strategy is not suggested when outside of a matrix job", async () => { + const input = ` +on: push + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: npm test > test-job-\${{ | }}.txt +`; + + const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); + + expect(result.map(x => x.label)).not.toContain("strategy"); + }); + + it("strategy is suggested within a matrix job", async () => { + const input = ` +on: push + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + test-group: [1, 2] + node: [14, 16] + steps: + - uses: actions/checkout@v3 + - run: npm test > test-job-\${{ | }}.txt +`; + + const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); + + expect(result.map(x => x.label)).toContain("strategy"); + }); + }); + + it("includes expected keys", async () => { + const input = ` +on: push + +jobs: +test: + runs-on: ubuntu-latest + strategy: + matrix: + test-group: [1, 2] + node: [14, 16] + steps: + - uses: actions/checkout@v3 + - run: npm test > test-job-\${{ strategy.| }}.txt +`; + + const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); + + expect(result.map(x => x.label)).toEqual(["fail-fast", "job-index", "job-total", "max-parallel"]); + }); }); diff --git a/actions-languageservice/src/context-providers/default.ts b/actions-languageservice/src/context-providers/default.ts index d325592..4cdea50 100644 --- a/actions-languageservice/src/context-providers/default.ts +++ b/actions-languageservice/src/context-providers/default.ts @@ -4,6 +4,7 @@ import {ContextProviderConfig} from "./config"; import {getInputsContext} from "./inputs"; import {getNeedsContext} from "./needs"; import {getStepsContext} from "./steps"; +import {getStrategyContext} from "./strategy"; export async function getContext( names: string[], @@ -50,6 +51,9 @@ async function getDefaultContext(name: string, workflowContext: WorkflowContext) case "steps": return getStepsContext(workflowContext); + + case "strategy": + return getStrategyContext(); } return undefined; diff --git a/actions-languageservice/src/context-providers/strategy.ts b/actions-languageservice/src/context-providers/strategy.ts new file mode 100644 index 0000000..011665f --- /dev/null +++ b/actions-languageservice/src/context-providers/strategy.ts @@ -0,0 +1,12 @@ +import {data} from "@github/actions-expressions"; + +export function getStrategyContext(): data.Dictionary { + // https://docs.github.com/en/actions/learn-github-actions/contexts#strategy-context + const keys = ["fail-fast", "job-index", "job-total", "max-parallel"]; + + return new data.Dictionary( + ...keys.map(key => { + return {key, value: new data.Null()}; + }) + ); +} diff --git a/actions-languageservice/src/validate.expressions.test.ts b/actions-languageservice/src/validate.expressions.test.ts index faaba00..d395888 100644 --- a/actions-languageservice/src/validate.expressions.test.ts +++ b/actions-languageservice/src/validate.expressions.test.ts @@ -219,4 +219,86 @@ jobs: ]); }); }); + + describe("strategy context", () => { + it("reference within a matrix job", async () => { + const input = ` +on: push + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + test-group: [1, 2] + node: [14, 16] + steps: + - uses: actions/checkout@v3 + - run: echo \${{ strategy.fail-fast }} + - run: echo \${{ strategy.job-index }} + - run: echo \${{ strategy.job-total }} + - run: echo \${{ strategy.max-parallel }} +`; + + const result = await validate(createDocument("wf.yaml", input)); + + expect(result).toEqual([]); + }); + + it.failing("reference outside of a matrix job", async () => { + const input = ` +on: push + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: echo \${{ strategy.fail-fast }} + - run: echo \${{ strategy.job-index }} + - run: echo \${{ strategy.job-total }} + - run: echo \${{ strategy.max-parallel }} +`; + + const result = await validate(createDocument("wf.yaml", input)); + + expect(result).not.toEqual([]); + }); + + it("invalid strategy property", async () => { + const input = ` +on: push + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + test-group: [1, 2] + node: [14, 16] + steps: + - uses: actions/checkout@v3 + - run: echo \${{ strategy.fail-faster-than-fast }} +`; + + const result = await validate(createDocument("wf.yaml", input)); + + expect(result).toEqual([ + { + message: "Context access might be invalid: fail-faster-than-fast", + range: { + end: { + character: 55, + line: 12 + }, + start: { + character: 18, + line: 12 + } + }, + severity: DiagnosticSeverity.Warning + } + ]); + }); + }); });