Replace allowPartialContext with a mode enum
This commit is contained in:
@@ -11,7 +11,7 @@ import {File} from "@github/actions-workflow-parser/workflows/file";
|
|||||||
import {Position, TextDocument} from "vscode-languageserver-textdocument";
|
import {Position, TextDocument} from "vscode-languageserver-textdocument";
|
||||||
import {CompletionItem, TextEdit, Range} from "vscode-languageserver-types";
|
import {CompletionItem, TextEdit, Range} from "vscode-languageserver-types";
|
||||||
import {ContextProviderConfig} from "./context-providers/config";
|
import {ContextProviderConfig} from "./context-providers/config";
|
||||||
import {getContext} from "./context-providers/default";
|
import {getContext, Mode} from "./context-providers/default";
|
||||||
import {getWorkflowContext, WorkflowContext} from "./context/workflow-context";
|
import {getWorkflowContext, WorkflowContext} from "./context/workflow-context";
|
||||||
import {nullTrace} from "./nulltrace";
|
import {nullTrace} from "./nulltrace";
|
||||||
import {getAllowedContext} from "./utils/allowed-context";
|
import {getAllowedContext} from "./utils/allowed-context";
|
||||||
@@ -85,7 +85,7 @@ export async function complete(
|
|||||||
const expressionInput = (getExpressionInput(currentInput, relCharPos) || "").trim();
|
const expressionInput = (getExpressionInput(currentInput, relCharPos) || "").trim();
|
||||||
|
|
||||||
const allowedContext = getAllowedContext(token, parent);
|
const allowedContext = getAllowedContext(token, parent);
|
||||||
const context = await getContext(allowedContext, contextProviderConfig, workflowContext, true);
|
const context = await getContext(allowedContext, contextProviderConfig, workflowContext, Mode.Completion);
|
||||||
|
|
||||||
return completeExpression(expressionInput, context, []);
|
return completeExpression(expressionInput, context, []);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,17 +12,23 @@ import {getStrategyContext} from "./strategy";
|
|||||||
// Null indicates that the context provider doesn't have any value to provide
|
// Null indicates that the context provider doesn't have any value to provide
|
||||||
export type ContextValue = data.Dictionary | data.Null;
|
export type ContextValue = data.Dictionary | data.Null;
|
||||||
|
|
||||||
|
export enum Mode {
|
||||||
|
Completion,
|
||||||
|
Validation,
|
||||||
|
Hover
|
||||||
|
}
|
||||||
|
|
||||||
export async function getContext(
|
export async function getContext(
|
||||||
names: string[],
|
names: string[],
|
||||||
config: ContextProviderConfig | undefined,
|
config: ContextProviderConfig | undefined,
|
||||||
workflowContext: WorkflowContext,
|
workflowContext: WorkflowContext,
|
||||||
allowPartialContext: boolean = false
|
mode: Mode
|
||||||
): Promise<data.Dictionary> {
|
): Promise<data.Dictionary> {
|
||||||
const context = new data.Dictionary();
|
const context = new data.Dictionary();
|
||||||
|
|
||||||
const filteredNames = filterContextNames(names, workflowContext);
|
const filteredNames = filterContextNames(names, workflowContext);
|
||||||
for (const contextName of filteredNames) {
|
for (const contextName of filteredNames) {
|
||||||
let value = getDefaultContext(contextName, workflowContext, allowPartialContext) || new data.Dictionary();
|
let value = getDefaultContext(contextName, workflowContext, mode) || new data.Dictionary();
|
||||||
if (value.kind === Kind.Null) {
|
if (value.kind === Kind.Null) {
|
||||||
context.add(contextName, value);
|
context.add(contextName, value);
|
||||||
continue;
|
continue;
|
||||||
@@ -36,11 +42,7 @@ export async function getContext(
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDefaultContext(
|
function getDefaultContext(name: string, workflowContext: WorkflowContext, mode: Mode): ContextValue | undefined {
|
||||||
name: string,
|
|
||||||
workflowContext: WorkflowContext,
|
|
||||||
allowPartialContext: boolean
|
|
||||||
): ContextValue | undefined {
|
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "runner":
|
case "runner":
|
||||||
return objectToDictionary({
|
return objectToDictionary({
|
||||||
@@ -67,7 +69,7 @@ function getDefaultContext(
|
|||||||
return getStrategyContext(workflowContext);
|
return getStrategyContext(workflowContext);
|
||||||
|
|
||||||
case "matrix":
|
case "matrix":
|
||||||
return getMatrixContext(workflowContext, allowPartialContext);
|
return getMatrixContext(workflowContext, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {SequenceToken} from "@github/actions-workflow-parser/templates/tokens/se
|
|||||||
import {StringToken} from "@github/actions-workflow-parser/templates/tokens/string-token";
|
import {StringToken} from "@github/actions-workflow-parser/templates/tokens/string-token";
|
||||||
import {TemplateToken} from "@github/actions-workflow-parser/templates/tokens/template-token";
|
import {TemplateToken} from "@github/actions-workflow-parser/templates/tokens/template-token";
|
||||||
import {WorkflowContext} from "../context/workflow-context";
|
import {WorkflowContext} from "../context/workflow-context";
|
||||||
|
import {Mode} from "./default";
|
||||||
import {getMatrixContext} from "./matrix";
|
import {getMatrixContext} from "./matrix";
|
||||||
|
|
||||||
type MatrixMap = {
|
type MatrixMap = {
|
||||||
@@ -62,7 +63,7 @@ describe("matrix context", () => {
|
|||||||
const workflowContext = {} as WorkflowContext;
|
const workflowContext = {} as WorkflowContext;
|
||||||
expect(workflowContext.job).toBeUndefined();
|
expect(workflowContext.job).toBeUndefined();
|
||||||
|
|
||||||
const context = getMatrixContext(workflowContext, false);
|
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||||
expect(context).toEqual(new data.Dictionary());
|
expect(context).toEqual(new data.Dictionary());
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -71,7 +72,7 @@ describe("matrix context", () => {
|
|||||||
const workflowContext = {job} as WorkflowContext;
|
const workflowContext = {job} as WorkflowContext;
|
||||||
expect(workflowContext.job!.strategy).toBeUndefined();
|
expect(workflowContext.job!.strategy).toBeUndefined();
|
||||||
|
|
||||||
const context = getMatrixContext(workflowContext, false);
|
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||||
expect(context).toEqual(new data.Dictionary());
|
expect(context).toEqual(new data.Dictionary());
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -79,7 +80,7 @@ describe("matrix context", () => {
|
|||||||
const workflowContext = contextFromStrategy(stringToToken("hello"));
|
const workflowContext = contextFromStrategy(stringToToken("hello"));
|
||||||
expect(workflowContext.job!.strategy).toBeDefined();
|
expect(workflowContext.job!.strategy).toBeDefined();
|
||||||
|
|
||||||
const context = getMatrixContext(workflowContext, false);
|
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||||
expect(context).toEqual(new data.Dictionary());
|
expect(context).toEqual(new data.Dictionary());
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -87,7 +88,7 @@ describe("matrix context", () => {
|
|||||||
const strategy = new MappingToken(undefined, undefined, undefined);
|
const strategy = new MappingToken(undefined, undefined, undefined);
|
||||||
const workflowContext = contextFromStrategy(strategy);
|
const workflowContext = contextFromStrategy(strategy);
|
||||||
|
|
||||||
const context = getMatrixContext(workflowContext, false);
|
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||||
expect(context).toEqual(new data.Null());
|
expect(context).toEqual(new data.Null());
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -96,7 +97,7 @@ describe("matrix context", () => {
|
|||||||
strategy.add(stringToToken("matrix"), stringToToken("hello"));
|
strategy.add(stringToToken("matrix"), stringToToken("hello"));
|
||||||
const workflowContext = contextFromStrategy(strategy);
|
const workflowContext = contextFromStrategy(strategy);
|
||||||
|
|
||||||
const context = getMatrixContext(workflowContext, false);
|
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||||
expect(context).toEqual(new data.Null());
|
expect(context).toEqual(new data.Null());
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -105,7 +106,7 @@ describe("matrix context", () => {
|
|||||||
strategy.add(stringToToken("matrix"), new MappingToken(undefined, undefined, undefined));
|
strategy.add(stringToToken("matrix"), new MappingToken(undefined, undefined, undefined));
|
||||||
const workflowContext = contextFromStrategy(strategy);
|
const workflowContext = contextFromStrategy(strategy);
|
||||||
|
|
||||||
const context = getMatrixContext(workflowContext, false);
|
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||||
expect(context).toEqual(new data.Dictionary());
|
expect(context).toEqual(new data.Dictionary());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -116,7 +117,7 @@ describe("matrix context", () => {
|
|||||||
strategy.add(stringToToken("matrix"), expressionToToken("${{ fromJSON(needs.job1.outputs.matrix) }}"));
|
strategy.add(stringToToken("matrix"), expressionToToken("${{ fromJSON(needs.job1.outputs.matrix) }}"));
|
||||||
|
|
||||||
const workflowContext = contextFromStrategy(strategy);
|
const workflowContext = contextFromStrategy(strategy);
|
||||||
const context = getMatrixContext(workflowContext, false);
|
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||||
|
|
||||||
expect(context).toEqual(new data.Null());
|
expect(context).toEqual(new data.Null());
|
||||||
});
|
});
|
||||||
@@ -136,12 +137,12 @@ describe("matrix context", () => {
|
|||||||
strategy.add(stringToToken("matrix"), matrix);
|
strategy.add(stringToToken("matrix"), matrix);
|
||||||
|
|
||||||
const workflowContext = contextFromStrategy(strategy);
|
const workflowContext = contextFromStrategy(strategy);
|
||||||
const context = getMatrixContext(workflowContext, false);
|
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||||
|
|
||||||
expect(context).toEqual(new data.Null());
|
expect(context).toEqual(new data.Null());
|
||||||
});
|
});
|
||||||
|
|
||||||
it("matrix with include expression and partial context allowed", () => {
|
it("matrix with include expression during completion", () => {
|
||||||
const include = expressionToToken("${{ fromJSON(needs.job1.outputs.matrix) }}");
|
const include = expressionToToken("${{ fromJSON(needs.job1.outputs.matrix) }}");
|
||||||
|
|
||||||
const nodeSequence = new SequenceToken(undefined, undefined, undefined);
|
const nodeSequence = new SequenceToken(undefined, undefined, undefined);
|
||||||
@@ -157,7 +158,7 @@ describe("matrix context", () => {
|
|||||||
|
|
||||||
const workflowContext = contextFromStrategy(strategy);
|
const workflowContext = contextFromStrategy(strategy);
|
||||||
|
|
||||||
const context = getMatrixContext(workflowContext, true);
|
const context = getMatrixContext(workflowContext, Mode.Completion);
|
||||||
|
|
||||||
expect(context).toEqual(
|
expect(context).toEqual(
|
||||||
new data.Dictionary({
|
new data.Dictionary({
|
||||||
@@ -177,7 +178,7 @@ describe("matrix context", () => {
|
|||||||
strategy.add(stringToToken("matrix"), matrix);
|
strategy.add(stringToToken("matrix"), matrix);
|
||||||
|
|
||||||
const workflowContext = contextFromStrategy(strategy);
|
const workflowContext = contextFromStrategy(strategy);
|
||||||
const context = getMatrixContext(workflowContext, false);
|
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||||
|
|
||||||
expect(context).toEqual(
|
expect(context).toEqual(
|
||||||
new data.Dictionary({
|
new data.Dictionary({
|
||||||
@@ -192,7 +193,7 @@ describe("matrix context", () => {
|
|||||||
it("basic matrix", () => {
|
it("basic matrix", () => {
|
||||||
const workflowContext = createMatrix({os: ["ubuntu-latest", "windows-latest"]});
|
const workflowContext = createMatrix({os: ["ubuntu-latest", "windows-latest"]});
|
||||||
|
|
||||||
const context = getMatrixContext(workflowContext, false);
|
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||||
expect(context).toEqual(
|
expect(context).toEqual(
|
||||||
new data.Dictionary({
|
new data.Dictionary({
|
||||||
key: "os",
|
key: "os",
|
||||||
@@ -207,7 +208,7 @@ describe("matrix context", () => {
|
|||||||
node: ["12", "14"]
|
node: ["12", "14"]
|
||||||
});
|
});
|
||||||
|
|
||||||
const context = getMatrixContext(workflowContext, false);
|
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||||
expect(context).toEqual(
|
expect(context).toEqual(
|
||||||
new data.Dictionary(
|
new data.Dictionary(
|
||||||
{
|
{
|
||||||
@@ -234,7 +235,7 @@ describe("matrix context", () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
const context = getMatrixContext(workflowContext, false);
|
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||||
|
|
||||||
expect(context).toEqual(
|
expect(context).toEqual(
|
||||||
new data.Dictionary(
|
new data.Dictionary(
|
||||||
@@ -268,7 +269,7 @@ describe("matrix context", () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
const context = getMatrixContext(workflowContext, false);
|
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||||
|
|
||||||
expect(context).toEqual(
|
expect(context).toEqual(
|
||||||
new data.Dictionary(
|
new data.Dictionary(
|
||||||
@@ -302,7 +303,7 @@ describe("matrix context", () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
const context = getMatrixContext(workflowContext, false);
|
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||||
|
|
||||||
expect(context).toEqual(
|
expect(context).toEqual(
|
||||||
new data.Dictionary(
|
new data.Dictionary(
|
||||||
@@ -337,7 +338,7 @@ describe("matrix context", () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
const context = getMatrixContext(workflowContext, false);
|
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||||
|
|
||||||
expect(context).toEqual(new data.Dictionary());
|
expect(context).toEqual(new data.Dictionary());
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import {KeyValuePair} from "@github/actions-workflow-parser/templates/tokens/key
|
|||||||
import {MappingToken} from "@github/actions-workflow-parser/templates/tokens/mapping-token";
|
import {MappingToken} from "@github/actions-workflow-parser/templates/tokens/mapping-token";
|
||||||
import {SequenceToken} from "@github/actions-workflow-parser/templates/tokens/sequence-token";
|
import {SequenceToken} from "@github/actions-workflow-parser/templates/tokens/sequence-token";
|
||||||
import {WorkflowContext} from "../context/workflow-context";
|
import {WorkflowContext} from "../context/workflow-context";
|
||||||
import {ContextValue} from "./default";
|
import {ContextValue, Mode} from "./default";
|
||||||
|
|
||||||
export function getMatrixContext(workflowContext: WorkflowContext, allowPartialContext: boolean): ContextValue {
|
export function getMatrixContext(workflowContext: WorkflowContext, mode: Mode): ContextValue {
|
||||||
// https://docs.github.com/en/actions/learn-github-actions/contexts#matrix-context
|
// https://docs.github.com/en/actions/learn-github-actions/contexts#matrix-context
|
||||||
const strategy = workflowContext.job?.strategy;
|
const strategy = workflowContext.job?.strategy;
|
||||||
if (!strategy || !isMapping(strategy)) {
|
if (!strategy || !isMapping(strategy)) {
|
||||||
@@ -19,7 +19,7 @@ export function getMatrixContext(workflowContext: WorkflowContext, allowPartialC
|
|||||||
return new data.Null();
|
return new data.Null();
|
||||||
}
|
}
|
||||||
|
|
||||||
const properties = matrixProperties(matrix, allowPartialContext);
|
const properties = matrixProperties(matrix, mode);
|
||||||
if (!properties) {
|
if (!properties) {
|
||||||
// Matrix included an expression, so there's no context we can provide
|
// Matrix included an expression, so there's no context we can provide
|
||||||
return new data.Null();
|
return new data.Null();
|
||||||
@@ -94,10 +94,7 @@ export function getMatrixContext(workflowContext: WorkflowContext, allowPartialC
|
|||||||
*
|
*
|
||||||
* Keys: os, version, environment
|
* Keys: os, version, environment
|
||||||
*/
|
*/
|
||||||
function matrixProperties(
|
function matrixProperties(matrix: MappingToken, mode: Mode): Map<string, Set<string> | undefined> | undefined {
|
||||||
matrix: MappingToken,
|
|
||||||
allowPartialContext: boolean
|
|
||||||
): Map<string, Set<string> | undefined> | undefined {
|
|
||||||
const properties = new Map<string, Set<string> | undefined>();
|
const properties = new Map<string, Set<string> | undefined>();
|
||||||
|
|
||||||
let include: SequenceToken | undefined;
|
let include: SequenceToken | undefined;
|
||||||
@@ -113,7 +110,8 @@ function matrixProperties(
|
|||||||
case "include":
|
case "include":
|
||||||
// If "include" is an expression, we can't know the full properties of the matrix
|
// If "include" is an expression, we can't know the full properties of the matrix
|
||||||
if (isBasicExpression(pair.value) || !isSequence(pair.value)) {
|
if (isBasicExpression(pair.value) || !isSequence(pair.value)) {
|
||||||
if (!allowPartialContext) {
|
// Without the full properties of the matrix, we shouldn't validate anything
|
||||||
|
if (mode === Mode.Validation) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import {
|
|||||||
} from "@github/actions-workflow-parser";
|
} from "@github/actions-workflow-parser";
|
||||||
import {ErrorPolicy} from "@github/actions-workflow-parser/model/convert";
|
import {ErrorPolicy} from "@github/actions-workflow-parser/model/convert";
|
||||||
import {splitAllowedContext} from "@github/actions-workflow-parser/templates/allowed-context";
|
import {splitAllowedContext} from "@github/actions-workflow-parser/templates/allowed-context";
|
||||||
import {Definition} from "@github/actions-workflow-parser/templates/schema/definition";
|
|
||||||
import {BasicExpressionToken} from "@github/actions-workflow-parser/templates/tokens/basic-expression-token";
|
import {BasicExpressionToken} from "@github/actions-workflow-parser/templates/tokens/basic-expression-token";
|
||||||
import {StringToken} from "@github/actions-workflow-parser/templates/tokens/string-token";
|
import {StringToken} from "@github/actions-workflow-parser/templates/tokens/string-token";
|
||||||
import {TemplateToken} from "@github/actions-workflow-parser/templates/tokens/template-token";
|
import {TemplateToken} from "@github/actions-workflow-parser/templates/tokens/template-token";
|
||||||
@@ -19,7 +18,7 @@ import {TextDocument} from "vscode-languageserver-textdocument";
|
|||||||
import {Diagnostic, DiagnosticSeverity, URI} from "vscode-languageserver-types";
|
import {Diagnostic, DiagnosticSeverity, URI} from "vscode-languageserver-types";
|
||||||
|
|
||||||
import {ContextProviderConfig} from "./context-providers/config";
|
import {ContextProviderConfig} from "./context-providers/config";
|
||||||
import {getContext} from "./context-providers/default";
|
import {getContext, Mode} from "./context-providers/default";
|
||||||
import {getWorkflowContext, WorkflowContext} from "./context/workflow-context";
|
import {getWorkflowContext, WorkflowContext} from "./context/workflow-context";
|
||||||
import {AccessError, wrapDictionary} from "./expression-validation/error-dictionary";
|
import {AccessError, wrapDictionary} from "./expression-validation/error-dictionary";
|
||||||
import {error} from "./log";
|
import {error} from "./log";
|
||||||
@@ -196,7 +195,7 @@ async function validateExpression(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const context = await getContext(namedContexts, contextProviderConfig, workflowContext);
|
const context = await getContext(namedContexts, contextProviderConfig, workflowContext, Mode.Validation);
|
||||||
|
|
||||||
const e = new Evaluator(expr, wrapDictionary(context));
|
const e = new Evaluator(expr, wrapDictionary(context));
|
||||||
e.evaluate();
|
e.evaluate();
|
||||||
|
|||||||
Reference in New Issue
Block a user