Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 46b216a6dc | |||
| 0fe7798548 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/expressions",
|
||||
"version": "0.3.41",
|
||||
"version": "0.3.42",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"source": "./src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/languageserver",
|
||||
"version": "0.3.41",
|
||||
"version": "0.3.42",
|
||||
"description": "Language server for GitHub Actions",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
@@ -48,8 +48,8 @@
|
||||
"actions-languageserver": "./bin/actions-languageserver"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/languageservice": "^0.3.41",
|
||||
"@actions/workflow-parser": "^0.3.41",
|
||||
"@actions/languageservice": "^0.3.42",
|
||||
"@actions/workflow-parser": "^0.3.42",
|
||||
"@octokit/rest": "^21.1.1",
|
||||
"@octokit/types": "^9.0.0",
|
||||
"vscode-languageserver": "^8.0.2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/languageservice",
|
||||
"version": "0.3.41",
|
||||
"version": "0.3.42",
|
||||
"description": "Language service for GitHub Actions",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
@@ -47,8 +47,8 @@
|
||||
"watch": "tsc --build tsconfig.build.json --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/expressions": "^0.3.41",
|
||||
"@actions/workflow-parser": "^0.3.41",
|
||||
"@actions/expressions": "^0.3.42",
|
||||
"@actions/workflow-parser": "^0.3.42",
|
||||
"vscode-languageserver-textdocument": "^1.0.7",
|
||||
"vscode-languageserver-types": "^3.17.2",
|
||||
"vscode-uri": "^3.0.8",
|
||||
|
||||
@@ -134,6 +134,49 @@ runs:
|
||||
expect(labels).toContain("arch");
|
||||
expect(labels).toContain("temp");
|
||||
});
|
||||
|
||||
it("completes if expression value for composite run step", async () => {
|
||||
const [doc, position] = createActionDocument(`name: My Action
|
||||
description: Test action
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- if: |
|
||||
run: echo "hello"
|
||||
shell: bash`);
|
||||
const completions = await complete(doc, position);
|
||||
const labels = completions.map(c => c.label);
|
||||
|
||||
// Should show expression-related completions (status functions and contexts)
|
||||
expect(labels).toContain("always");
|
||||
expect(labels).toContain("success");
|
||||
expect(labels).toContain("failure");
|
||||
expect(labels).toContain("cancelled");
|
||||
expect(labels).toContain("runner");
|
||||
expect(labels).toContain("github");
|
||||
expect(labels).toContain("inputs");
|
||||
expect(labels).toContain("steps");
|
||||
});
|
||||
|
||||
it("completes if expression value for composite uses step", async () => {
|
||||
const [doc, position] = createActionDocument(`name: My Action
|
||||
description: Test action
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- if: |
|
||||
uses: actions/checkout@v4`);
|
||||
const completions = await complete(doc, position);
|
||||
const labels = completions.map(c => c.label);
|
||||
|
||||
// Should show expression-related completions
|
||||
expect(labels).toContain("always");
|
||||
expect(labels).toContain("success");
|
||||
expect(labels).toContain("failure");
|
||||
expect(labels).toContain("cancelled");
|
||||
expect(labels).toContain("runner");
|
||||
expect(labels).toContain("github");
|
||||
});
|
||||
});
|
||||
|
||||
describe("top-level completions", () => {
|
||||
@@ -207,6 +250,85 @@ runs:
|
||||
expect(labels).not.toContain("entrypoint");
|
||||
});
|
||||
|
||||
it("filters runs keys for node24 actions", async () => {
|
||||
const [doc, position] = createActionDocument(`name: Test
|
||||
description: Test
|
||||
runs:
|
||||
using: node24
|
||||
|`);
|
||||
const completions = await complete(doc, position);
|
||||
const labels = completions.map(c => c.label);
|
||||
|
||||
// Should show Node.js action keys
|
||||
expect(labels).toContain("main");
|
||||
expect(labels).toContain("pre");
|
||||
expect(labels).toContain("post");
|
||||
expect(labels).toContain("pre-if");
|
||||
expect(labels).toContain("post-if");
|
||||
|
||||
// Should NOT show composite or docker keys
|
||||
expect(labels).not.toContain("steps");
|
||||
expect(labels).not.toContain("image");
|
||||
expect(labels).not.toContain("entrypoint");
|
||||
});
|
||||
|
||||
it("completes pre-if expression value for node actions", async () => {
|
||||
const [doc, position] = createActionDocument(`name: Test
|
||||
description: Test
|
||||
runs:
|
||||
using: node24
|
||||
main: index.js
|
||||
pre: setup.js
|
||||
pre-if: |`);
|
||||
const completions = await complete(doc, position);
|
||||
const labels = completions.map(c => c.label);
|
||||
|
||||
// Should show expression-related completions (context functions and namespaces)
|
||||
expect(labels).toContain("always");
|
||||
expect(labels).toContain("success");
|
||||
expect(labels).toContain("failure");
|
||||
expect(labels).toContain("cancelled");
|
||||
expect(labels).toContain("runner");
|
||||
expect(labels).toContain("github");
|
||||
expect(labels).toContain("inputs");
|
||||
expect(labels).toContain("hashFiles");
|
||||
});
|
||||
|
||||
it("completes post-if expression value for node actions", async () => {
|
||||
const [doc, position] = createActionDocument(`name: Test
|
||||
description: Test
|
||||
runs:
|
||||
using: node24
|
||||
main: index.js
|
||||
post: cleanup.js
|
||||
post-if: |`);
|
||||
const completions = await complete(doc, position);
|
||||
const labels = completions.map(c => c.label);
|
||||
|
||||
// Should show expression-related completions
|
||||
expect(labels).toContain("always");
|
||||
expect(labels).toContain("runner");
|
||||
expect(labels).toContain("hashFiles");
|
||||
});
|
||||
|
||||
it("completes pre-if expression value for docker actions", async () => {
|
||||
const [doc, position] = createActionDocument(`name: Test
|
||||
description: Test
|
||||
runs:
|
||||
using: docker
|
||||
image: docker://alpine
|
||||
pre-entrypoint: setup.sh
|
||||
pre-if: |`);
|
||||
const completions = await complete(doc, position);
|
||||
const labels = completions.map(c => c.label);
|
||||
|
||||
// Should show expression-related completions
|
||||
expect(labels).toContain("always");
|
||||
expect(labels).toContain("runner");
|
||||
expect(labels).toContain("github");
|
||||
expect(labels).toContain("hashFiles");
|
||||
});
|
||||
|
||||
it("filters runs keys for composite actions", async () => {
|
||||
const [doc, position] = createActionDocument(`name: Test
|
||||
description: Test
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import {data, DescriptionDictionary, FeatureFlags} from "@actions/expressions";
|
||||
import {CompletionItem, CompletionItemKind} from "vscode-languageserver-types";
|
||||
import {CompletionItem, CompletionItemKind, MarkupContent} from "vscode-languageserver-types";
|
||||
import {complete, getExpressionInput} from "./complete.js";
|
||||
import {ContextProviderConfig} from "./context-providers/config.js";
|
||||
import {registerLogger} from "./log.js";
|
||||
@@ -419,6 +419,36 @@ jobs:
|
||||
|
||||
expect(result.map(x => x.label)).toEqual(["event"]);
|
||||
});
|
||||
|
||||
it("includes both contexts and extension functions", async () => {
|
||||
const input = `on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo
|
||||
if: |`;
|
||||
const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
|
||||
const labels = result.map(x => x.label);
|
||||
|
||||
// Context namespaces should be present
|
||||
expect(labels).toContain("github");
|
||||
expect(labels).toContain("runner");
|
||||
expect(labels).toContain("env");
|
||||
expect(labels).toContain("steps");
|
||||
|
||||
// Extension functions should be present (from schema context array)
|
||||
expect(labels).toContain("hashFiles");
|
||||
expect(labels).toContain("always");
|
||||
expect(labels).toContain("success");
|
||||
expect(labels).toContain("failure");
|
||||
expect(labels).toContain("cancelled");
|
||||
|
||||
// Built-in functions should be present
|
||||
expect(labels).toContain("toJson");
|
||||
expect(labels).toContain("fromJson");
|
||||
expect(labels).toContain("contains");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1278,6 +1308,7 @@ jobs:
|
||||
expect(hashFiles).toBeDefined();
|
||||
expect(hashFiles!.kind).toBe(CompletionItemKind.Function);
|
||||
expect(hashFiles!.insertText).toBe("hashFiles()");
|
||||
expect((hashFiles!.documentation as MarkupContent)?.value).toContain("Returns a single hash for the set of files");
|
||||
|
||||
// Not a function
|
||||
const github = result.find(x => x.label === "github");
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import {complete as completeExpression, DescriptionDictionary, FeatureFlags} from "@actions/expressions";
|
||||
import {CompletionItem as ExpressionCompletionItem} from "@actions/expressions/completion";
|
||||
import {FunctionInfo} from "@actions/expressions/funcs/info";
|
||||
import {isBasicExpression, isSequence, isString} from "@actions/workflow-parser";
|
||||
import {getActionSchema} from "@actions/workflow-parser/actions/action-schema";
|
||||
import {ErrorPolicy} from "@actions/workflow-parser/model/convert";
|
||||
import {splitAllowedContext} from "@actions/workflow-parser/templates/allowed-context";
|
||||
import {DefinitionType} from "@actions/workflow-parser/templates/schema/definition-type";
|
||||
import {OneOfDefinition} from "@actions/workflow-parser/templates/schema/one-of-definition";
|
||||
import {TemplateSchema} from "@actions/workflow-parser/templates/schema/template-schema";
|
||||
@@ -19,6 +21,7 @@ import {CompletionItem, CompletionItemKind, CompletionItemTag, Range, TextEdit}
|
||||
import {filterActionRunsCompletions, getActionScaffoldingSnippets} from "./complete-action.js";
|
||||
import {ContextProviderConfig} from "./context-providers/config.js";
|
||||
import {getActionExpressionContext, getWorkflowExpressionContext, Mode} from "./context-providers/default.js";
|
||||
import {getFunctionDescription} from "./context-providers/descriptions.js";
|
||||
import {ActionContext, getActionContext} from "./context/action-context.js";
|
||||
import {getWorkflowContext, WorkflowContext} from "./context/workflow-context.js";
|
||||
import {validatorFunctions} from "./expression-validation/functions.js";
|
||||
@@ -121,18 +124,24 @@ export async function complete(
|
||||
}
|
||||
|
||||
// Expression completions
|
||||
if (token && (isBasicExpression(token) || isPotentiallyExpression(token))) {
|
||||
if (token && (isBasicExpression(token) || isPotentiallyExpression(token, isAction))) {
|
||||
const allowedContext = token.definitionInfo?.allowedContext || [];
|
||||
const {namedContexts, functions: extensionFunctions} = splitAllowedContext(allowedContext);
|
||||
const context = isAction
|
||||
? getActionExpressionContext(allowedContext, config?.contextProviderConfig, actionContext, Mode.Completion)
|
||||
? getActionExpressionContext(namedContexts, config?.contextProviderConfig, actionContext, Mode.Completion)
|
||||
: await getWorkflowExpressionContext(
|
||||
allowedContext,
|
||||
namedContexts,
|
||||
config?.contextProviderConfig,
|
||||
workflowContext,
|
||||
Mode.Completion
|
||||
);
|
||||
|
||||
return getExpressionCompletionItems(token, context, newPos, config?.featureFlags);
|
||||
// Populate function descriptions for completion display
|
||||
for (const func of extensionFunctions) {
|
||||
func.description = getFunctionDescription(func.name);
|
||||
}
|
||||
|
||||
return getExpressionCompletionItems(token, context, extensionFunctions, newPos, config?.featureFlags);
|
||||
}
|
||||
|
||||
const indentation = guessIndentation(newDoc, 2, true); // Use 2 spaces as default and most common for YAML
|
||||
@@ -521,6 +530,7 @@ export function getExistingValues(token: TemplateToken | null, parent: TemplateT
|
||||
function getExpressionCompletionItems(
|
||||
token: TemplateToken,
|
||||
context: DescriptionDictionary,
|
||||
extensionFunctions: FunctionInfo[],
|
||||
pos: Position,
|
||||
featureFlags?: FeatureFlags
|
||||
): CompletionItem[] {
|
||||
@@ -541,8 +551,8 @@ function getExpressionCompletionItems(
|
||||
const expressionInput = (getExpressionInput(currentInput, cursorOffset) || "").trim();
|
||||
|
||||
try {
|
||||
return completeExpression(expressionInput, context, [], validatorFunctions, featureFlags).map(item =>
|
||||
mapExpressionCompletionItem(item, currentInput[cursorOffset])
|
||||
return completeExpression(expressionInput, context, extensionFunctions, validatorFunctions, featureFlags).map(
|
||||
item => mapExpressionCompletionItem(item, currentInput[cursorOffset])
|
||||
);
|
||||
} catch (e) {
|
||||
error(`Error while completing expression: '${(e as Error)?.message || "<no details>"}'`);
|
||||
|
||||
@@ -71,7 +71,7 @@ export async function hover(document: TextDocument, position: Position, config?:
|
||||
// Early exit if there's nothing to provide hover for
|
||||
const hoverToken = token || keyToken;
|
||||
const isExpressionHover =
|
||||
token && tokenDefinitionInfo && (isBasicExpression(token) || isPotentiallyExpression(token));
|
||||
token && tokenDefinitionInfo && (isBasicExpression(token) || isPotentiallyExpression(token, isAction));
|
||||
if (!isExpressionHover && !hoverToken?.definition) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
import {isPotentiallyExpression} from "./expression-detection.js";
|
||||
import {TemplateToken} from "@actions/workflow-parser/templates/tokens/template-token";
|
||||
import {TokenType} from "@actions/workflow-parser/templates/tokens/types";
|
||||
import {Definition} from "@actions/workflow-parser/templates/schema/definition";
|
||||
|
||||
// Helper to create a mock TemplateToken with the properties we need to test
|
||||
function createMockToken(options: {value?: string; definitionKey?: string; isString?: boolean}): TemplateToken {
|
||||
const {value = "", definitionKey, isString = true} = options;
|
||||
|
||||
const mockDefinition = definitionKey ? ({key: definitionKey} as Definition) : undefined;
|
||||
|
||||
return {
|
||||
value: isString ? value : undefined,
|
||||
definition: mockDefinition,
|
||||
templateTokenType: isString ? TokenType.String : TokenType.Mapping,
|
||||
// Required by isString type guard (isLiteral checks isLiteral property)
|
||||
isLiteral: isString,
|
||||
isScalar: isString
|
||||
} as unknown as TemplateToken;
|
||||
}
|
||||
|
||||
describe("isPotentiallyExpression", () => {
|
||||
describe("expression markers", () => {
|
||||
it("returns true when token value contains ${{", () => {
|
||||
const token = createMockToken({value: "${{ github.actor }}"});
|
||||
expect(isPotentiallyExpression(token, false)).toBe(true);
|
||||
expect(isPotentiallyExpression(token, true)).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true when token value contains embedded ${{", () => {
|
||||
const token = createMockToken({value: "Hello ${{ github.actor }}!"});
|
||||
expect(isPotentiallyExpression(token, false)).toBe(true);
|
||||
expect(isPotentiallyExpression(token, true)).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false when token value does not contain ${{", () => {
|
||||
const token = createMockToken({value: "plain text"});
|
||||
expect(isPotentiallyExpression(token, false)).toBe(false);
|
||||
expect(isPotentiallyExpression(token, true)).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for non-string tokens without expression marker", () => {
|
||||
const token = createMockToken({isString: false});
|
||||
expect(isPotentiallyExpression(token, false)).toBe(false);
|
||||
expect(isPotentiallyExpression(token, true)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("workflow schema if-conditions", () => {
|
||||
it("returns true for job-if definition in workflow", () => {
|
||||
const token = createMockToken({value: "success()", definitionKey: "job-if"});
|
||||
expect(isPotentiallyExpression(token, false)).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false for job-if definition in action (not valid in action schema)", () => {
|
||||
const token = createMockToken({value: "success()", definitionKey: "job-if"});
|
||||
expect(isPotentiallyExpression(token, true)).toBe(false);
|
||||
});
|
||||
|
||||
it("returns true for step-if definition in workflow", () => {
|
||||
const token = createMockToken({value: "failure()", definitionKey: "step-if"});
|
||||
expect(isPotentiallyExpression(token, false)).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for snapshot-if definition in workflow", () => {
|
||||
const token = createMockToken({value: "always()", definitionKey: "snapshot-if"});
|
||||
expect(isPotentiallyExpression(token, false)).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false for snapshot-if definition in action (not valid in action schema)", () => {
|
||||
const token = createMockToken({value: "always()", definitionKey: "snapshot-if"});
|
||||
expect(isPotentiallyExpression(token, true)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("action schema if-conditions", () => {
|
||||
describe("composite action step if (run and uses)", () => {
|
||||
it("returns true for step-if definition in action", () => {
|
||||
const token = createMockToken({value: "success()", definitionKey: "step-if"});
|
||||
expect(isPotentiallyExpression(token, true)).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for step-if with run step condition", () => {
|
||||
// Composite action run step: if condition
|
||||
const token = createMockToken({value: "github.event_name == 'push'", definitionKey: "step-if"});
|
||||
expect(isPotentiallyExpression(token, true)).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for step-if with uses step condition", () => {
|
||||
// Composite action uses step: if condition
|
||||
const token = createMockToken({value: "runner.os == 'Linux'", definitionKey: "step-if"});
|
||||
expect(isPotentiallyExpression(token, true)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("pre-if and post-if (node/docker actions)", () => {
|
||||
it("returns true for runs-if definition in action (pre-if)", () => {
|
||||
const token = createMockToken({value: "runner.os == 'Linux'", definitionKey: "runs-if"});
|
||||
expect(isPotentiallyExpression(token, true)).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for runs-if definition in action (post-if)", () => {
|
||||
const token = createMockToken({value: "always()", definitionKey: "runs-if"});
|
||||
expect(isPotentiallyExpression(token, true)).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false for runs-if definition in workflow (not valid in workflow schema)", () => {
|
||||
const token = createMockToken({value: "always()", definitionKey: "runs-if"});
|
||||
expect(isPotentiallyExpression(token, false)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("mixed scenarios", () => {
|
||||
it("returns true when expression marker present even if definition is not if-related", () => {
|
||||
const token = createMockToken({value: "${{ github.actor }}", definitionKey: "some-other-definition"});
|
||||
expect(isPotentiallyExpression(token, false)).toBe(true);
|
||||
expect(isPotentiallyExpression(token, true)).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true when both expression marker and if definition present", () => {
|
||||
const token = createMockToken({value: "${{ success() }}", definitionKey: "step-if"});
|
||||
expect(isPotentiallyExpression(token, false)).toBe(true);
|
||||
expect(isPotentiallyExpression(token, true)).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false for plain text with non-if definition", () => {
|
||||
const token = createMockToken({value: "plain text", definitionKey: "string"});
|
||||
expect(isPotentiallyExpression(token, false)).toBe(false);
|
||||
expect(isPotentiallyExpression(token, true)).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false when token has no definition and no expression marker", () => {
|
||||
const token = createMockToken({value: "plain text"});
|
||||
expect(isPotentiallyExpression(token, false)).toBe(false);
|
||||
expect(isPotentiallyExpression(token, true)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("edge cases", () => {
|
||||
it("handles empty string value", () => {
|
||||
const token = createMockToken({value: ""});
|
||||
expect(isPotentiallyExpression(token, false)).toBe(false);
|
||||
expect(isPotentiallyExpression(token, true)).toBe(false);
|
||||
});
|
||||
|
||||
it("handles expression marker as if-condition value", () => {
|
||||
const token = createMockToken({value: "${{ always() }}", definitionKey: "job-if"});
|
||||
expect(isPotentiallyExpression(token, false)).toBe(true);
|
||||
// For action, job-if is not valid, but ${{ is present
|
||||
expect(isPotentiallyExpression(token, true)).toBe(true);
|
||||
});
|
||||
|
||||
it("handles partial expression marker", () => {
|
||||
const token = createMockToken({value: "${incomplete"});
|
||||
expect(isPotentiallyExpression(token, false)).toBe(false);
|
||||
expect(isPotentiallyExpression(token, true)).toBe(false);
|
||||
});
|
||||
|
||||
it("handles ${{ at different positions", () => {
|
||||
const startToken = createMockToken({value: "${{ foo }} bar"});
|
||||
const middleToken = createMockToken({value: "bar ${{ foo }} baz"});
|
||||
const endToken = createMockToken({value: "bar ${{ foo }}"});
|
||||
|
||||
expect(isPotentiallyExpression(startToken, false)).toBe(true);
|
||||
expect(isPotentiallyExpression(middleToken, false)).toBe(true);
|
||||
expect(isPotentiallyExpression(endToken, false)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -2,10 +2,36 @@ import {isString} from "@actions/workflow-parser";
|
||||
import {OPEN_EXPRESSION} from "@actions/workflow-parser/templates/template-constants";
|
||||
import {TemplateToken} from "@actions/workflow-parser/templates/tokens/index";
|
||||
|
||||
export function isPotentiallyExpression(token: TemplateToken): boolean {
|
||||
const containsExpression = isString(token) && token.value != null && token.value.indexOf(OPEN_EXPRESSION) >= 0;
|
||||
// If conditions are always expressions (job-if, step-if, snapshot-if)
|
||||
const definitionKey = token.definition?.key;
|
||||
const isIfCondition = definitionKey === "job-if" || definitionKey === "step-if" || definitionKey === "snapshot-if";
|
||||
return containsExpression || isIfCondition;
|
||||
/**
|
||||
* Workflow schema if-condition definition keys.
|
||||
* - job-if: job level if condition
|
||||
* - step-if: step level if condition
|
||||
* - snapshot-if: snapshot if condition
|
||||
*/
|
||||
const WORKFLOW_IF_DEFINITIONS = new Set(["job-if", "step-if", "snapshot-if"]);
|
||||
|
||||
/**
|
||||
* Action schema if-condition definition keys.
|
||||
* - step-if: composite action step if condition (run-step and uses-step)
|
||||
* - runs-if: pre-if and post-if at the runs level (node/docker actions)
|
||||
*/
|
||||
const ACTION_IF_DEFINITIONS = new Set(["step-if", "runs-if"]);
|
||||
|
||||
export function isPotentiallyExpression(token: TemplateToken, isAction: boolean): boolean {
|
||||
// Check if token contains expression syntax
|
||||
if (isString(token) && token.value != null && token.value.indexOf(OPEN_EXPRESSION) >= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if token is an if-condition (always treated as expressions)
|
||||
if (!token.definition?.key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Definition keys differ between workflow and action schemas
|
||||
if (isAction) {
|
||||
return ACTION_IF_DEFINITIONS.has(token.definition.key);
|
||||
} else {
|
||||
return WORKFLOW_IF_DEFINITIONS.has(token.definition.key);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -6,5 +6,5 @@
|
||||
"languageservice",
|
||||
"languageserver"
|
||||
],
|
||||
"version": "0.3.41"
|
||||
"version": "0.3.42"
|
||||
}
|
||||
Generated
+9
-9
@@ -136,7 +136,7 @@
|
||||
},
|
||||
"expressions": {
|
||||
"name": "@actions/expressions",
|
||||
"version": "0.3.41",
|
||||
"version": "0.3.42",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.0.3",
|
||||
@@ -396,11 +396,11 @@
|
||||
},
|
||||
"languageserver": {
|
||||
"name": "@actions/languageserver",
|
||||
"version": "0.3.41",
|
||||
"version": "0.3.42",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/languageservice": "^0.3.41",
|
||||
"@actions/workflow-parser": "^0.3.41",
|
||||
"@actions/languageservice": "^0.3.42",
|
||||
"@actions/workflow-parser": "^0.3.42",
|
||||
"@octokit/rest": "^21.1.1",
|
||||
"@octokit/types": "^9.0.0",
|
||||
"vscode-languageserver": "^8.0.2",
|
||||
@@ -940,11 +940,11 @@
|
||||
},
|
||||
"languageservice": {
|
||||
"name": "@actions/languageservice",
|
||||
"version": "0.3.41",
|
||||
"version": "0.3.42",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/expressions": "^0.3.41",
|
||||
"@actions/workflow-parser": "^0.3.41",
|
||||
"@actions/expressions": "^0.3.42",
|
||||
"@actions/workflow-parser": "^0.3.42",
|
||||
"vscode-languageserver-textdocument": "^1.0.7",
|
||||
"vscode-languageserver-types": "^3.17.2",
|
||||
"vscode-uri": "^3.0.8",
|
||||
@@ -13345,10 +13345,10 @@
|
||||
},
|
||||
"workflow-parser": {
|
||||
"name": "@actions/workflow-parser",
|
||||
"version": "0.3.41",
|
||||
"version": "0.3.42",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/expressions": "^0.3.41",
|
||||
"@actions/expressions": "^0.3.42",
|
||||
"cronstrue": "^2.21.0",
|
||||
"yaml": "^2.0.0-8"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/workflow-parser",
|
||||
"version": "0.3.41",
|
||||
"version": "0.3.42",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"source": "./src/index.ts",
|
||||
@@ -48,7 +48,7 @@
|
||||
"watch": "tsc --build tsconfig.build.json --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/expressions": "^0.3.41",
|
||||
"@actions/expressions": "^0.3.42",
|
||||
"cronstrue": "^2.21.0",
|
||||
"yaml": "^2.0.0-8"
|
||||
},
|
||||
|
||||
@@ -150,7 +150,8 @@
|
||||
"always(0,0)",
|
||||
"success(0,0)",
|
||||
"failure(0,0)",
|
||||
"cancelled(0,0)"
|
||||
"cancelled(0,0)",
|
||||
"hashFiles(1,255)"
|
||||
],
|
||||
"string": {}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user