From 089c5b3c49c1da8a1fbe1a8df1e25756276dd6f1 Mon Sep 17 00:00:00 2001 From: Josh Gross Date: Wed, 7 Dec 2022 13:26:34 -0500 Subject: [PATCH] Filter `strategy` context to matrix jobs and add values --- .../src/complete.expressions.test.ts | 20 ++++----- .../src/context-providers/default.ts | 17 +++++-- .../src/context-providers/strategy.ts | 44 ++++++++++++++++--- .../src/utils/scalar-to-data.ts | 24 ++++++++++ .../src/validate.expressions.test.ts | 2 +- package-lock.json | 12 ++--- 6 files changed, 93 insertions(+), 26 deletions(-) create mode 100644 actions-languageservice/src/utils/scalar-to-data.ts diff --git a/actions-languageservice/src/complete.expressions.test.ts b/actions-languageservice/src/complete.expressions.test.ts index 18bab60..255a95a 100644 --- a/actions-languageservice/src/complete.expressions.test.ts +++ b/actions-languageservice/src/complete.expressions.test.ts @@ -337,7 +337,7 @@ jobs: }); describe("strategy context", () => { - it.failing("strategy is not suggested when outside of a matrix job", async () => { + it("strategy is not suggested when outside of a matrix job", async () => { const input = ` on: push @@ -381,15 +381,15 @@ jobs: 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 + 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); diff --git a/actions-languageservice/src/context-providers/default.ts b/actions-languageservice/src/context-providers/default.ts index 4cdea50..809ea2f 100644 --- a/actions-languageservice/src/context-providers/default.ts +++ b/actions-languageservice/src/context-providers/default.ts @@ -4,7 +4,7 @@ import {ContextProviderConfig} from "./config"; import {getInputsContext} from "./inputs"; import {getNeedsContext} from "./needs"; import {getStepsContext} from "./steps"; -import {getStrategyContext} from "./strategy"; +import {allowStrategyContext, getStrategyContext} from "./strategy"; export async function getContext( names: string[], @@ -13,7 +13,8 @@ export async function getContext( ): Promise { const context = new data.Dictionary(); - for (const contextName of names) { + const filteredNames = filterContextNames(names, workflowContext); + for (const contextName of filteredNames) { let value: data.Dictionary | undefined; value = await getDefaultContext(contextName, workflowContext); @@ -53,7 +54,7 @@ async function getDefaultContext(name: string, workflowContext: WorkflowContext) return getStepsContext(workflowContext); case "strategy": - return getStrategyContext(); + return getStrategyContext(workflowContext); } return undefined; @@ -67,3 +68,13 @@ function objectToDictionary(object: {[key: string]: string}): data.Dictionary { return dictionary; } + +function filterContextNames(contextNames: string[], workflowContext: WorkflowContext): string[] { + return contextNames.filter(name => { + switch (name) { + case "strategy": + return allowStrategyContext(workflowContext); + } + return true; + }); +} diff --git a/actions-languageservice/src/context-providers/strategy.ts b/actions-languageservice/src/context-providers/strategy.ts index 011665f..4ca0377 100644 --- a/actions-languageservice/src/context-providers/strategy.ts +++ b/actions-languageservice/src/context-providers/strategy.ts @@ -1,12 +1,44 @@ import {data} from "@github/actions-expressions"; +import {isMapping, isScalar, isString} from "@github/actions-workflow-parser"; +import {WorkflowContext} from "../context/workflow-context"; +import {scalarToData} from "../utils/scalar-to-data"; -export function getStrategyContext(): data.Dictionary { +export function getStrategyContext(workflowContext: WorkflowContext): 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()}; - }) - ); + const strategy = workflowContext.job?.strategy; + if (!strategy || !isMapping(strategy)) { + return new data.Dictionary( + ...keys.map(key => { + return {key, value: new data.Null()}; + }) + ); + } + + const strategyContext = new data.Dictionary(); + for (let i = 0; i < strategy.count; i++) { + const pair = strategy.get(i); + if (!isString(pair.key)) { + continue; + } + if (!keys.includes(pair.key.value)) { + continue; + } + + const value = isScalar(pair.value) ? scalarToData(pair.value) : new data.Null(); + strategyContext.add(pair.key.value, value); + } + + for (const key of keys) { + if (!strategyContext.get(key)) { + strategyContext.add(key, new data.Null()); + } + } + + return strategyContext; +} + +export function allowStrategyContext(workflowContext: WorkflowContext): boolean { + return workflowContext.job?.strategy !== undefined; } diff --git a/actions-languageservice/src/utils/scalar-to-data.ts b/actions-languageservice/src/utils/scalar-to-data.ts new file mode 100644 index 0000000..7de29bc --- /dev/null +++ b/actions-languageservice/src/utils/scalar-to-data.ts @@ -0,0 +1,24 @@ +import {data} from "@github/actions-expressions"; +import {isBoolean, isNumber, isString} from "@github/actions-workflow-parser"; +import {ScalarToken} from "@github/actions-workflow-parser/templates/tokens/scalar-token"; +import {TokenType} from "@github/actions-workflow-parser/templates/tokens/types"; + +export function scalarToData(scalar: ScalarToken): data.ExpressionData { + if (isNumber(scalar)) { + return new data.NumberData(scalar.value); + } + + if (isString(scalar)) { + return new data.StringData(scalar.value); + } + + if (isBoolean(scalar)) { + return new data.BooleanData(scalar.value); + } + + if (scalar.templateTokenType === TokenType.Null) { + return new data.Null(); + } + + return new data.StringData(scalar.toDisplayString()); +} diff --git a/actions-languageservice/src/validate.expressions.test.ts b/actions-languageservice/src/validate.expressions.test.ts index d395888..5b1afd0 100644 --- a/actions-languageservice/src/validate.expressions.test.ts +++ b/actions-languageservice/src/validate.expressions.test.ts @@ -245,7 +245,7 @@ jobs: expect(result).toEqual([]); }); - it.failing("reference outside of a matrix job", async () => { + it("reference outside of a matrix job", async () => { const input = ` on: push diff --git a/package-lock.json b/package-lock.json index 271a4d0..5ef4c0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -665,9 +665,9 @@ "link": true }, "node_modules/@github/actions-workflow-parser": { - "version": "0.0.28", - "resolved": "https://npm.pkg.github.com/download/@github/actions-workflow-parser/0.0.28/ad1da14904a4aa3482176614d454a85041f1e23b", - "integrity": "sha512-x0P5FZ78ige6o03wU4BgfrfkzDOL+iCFbBHk6mTNL4ZX/dR4xCUn8mfhOb9JxCKhuO3UqN5y1+mitcgejGHyfg==", + "version": "0.0.29", + "resolved": "https://npm.pkg.github.com/download/@github/actions-workflow-parser/0.0.29/ecf3d1c9727e4f5f71e52863f456672b173b7096", + "integrity": "sha512-VJLRv+GHD0E7S9uUCH89jIIA3iMdKRcqhrKwKq5TE5u/6Lap5nj1Juy48tBoCFAog07lhuqyqgWjP6FHHyAvPg==", "license": "MIT", "dependencies": { "@github/actions-expressions": "*", @@ -10927,9 +10927,9 @@ } }, "@github/actions-workflow-parser": { - "version": "0.0.28", - "resolved": "https://npm.pkg.github.com/download/@github/actions-workflow-parser/0.0.28/ad1da14904a4aa3482176614d454a85041f1e23b", - "integrity": "sha512-x0P5FZ78ige6o03wU4BgfrfkzDOL+iCFbBHk6mTNL4ZX/dR4xCUn8mfhOb9JxCKhuO3UqN5y1+mitcgejGHyfg==", + "version": "0.0.29", + "resolved": "https://npm.pkg.github.com/download/@github/actions-workflow-parser/0.0.29/ecf3d1c9727e4f5f71e52863f456672b173b7096", + "integrity": "sha512-VJLRv+GHD0E7S9uUCH89jIIA3iMdKRcqhrKwKq5TE5u/6Lap5nj1Juy48tBoCFAog07lhuqyqgWjP6FHHyAvPg==", "requires": { "@github/actions-expressions": "*", "yaml": "^2.0.0-8"