Merge branch 'main' into joshmgross/schema-function-descriptions
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import {data, DescriptionDictionary} from "@github/actions-expressions";
|
||||
import {CompletionItem, CompletionItemKind} from "vscode-languageserver-types";
|
||||
import {complete, getExpressionInput} from "./complete";
|
||||
@@ -9,17 +10,19 @@ import {testFileProvider} from "./test-utils/test-file-provider";
|
||||
import {clearCache} from "./utils/workflow-cache";
|
||||
|
||||
const contextProviderConfig: ContextProviderConfig = {
|
||||
getContext: async (context: string) => {
|
||||
getContext: (context: string) => {
|
||||
switch (context) {
|
||||
case "github":
|
||||
return new DescriptionDictionary({
|
||||
key: "event",
|
||||
value: new data.StringData("push"),
|
||||
description: "The event that triggered the workflow"
|
||||
});
|
||||
return Promise.resolve(
|
||||
new DescriptionDictionary({
|
||||
key: "event",
|
||||
value: new data.StringData("push"),
|
||||
description: "The event that triggered the workflow"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -225,6 +225,10 @@ function getExpressionCompletionItems(
|
||||
context: DescriptionDictionary,
|
||||
pos: Position
|
||||
): CompletionItem[] {
|
||||
if (!token.range) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let currentInput = "";
|
||||
|
||||
if (isBasicExpression(token)) {
|
||||
@@ -234,15 +238,15 @@ function getExpressionCompletionItems(
|
||||
currentInput = stringToken.source || stringToken.value;
|
||||
}
|
||||
|
||||
const relCharOffset = getRelCharOffset(token.range!, currentInput, pos);
|
||||
const relCharOffset = getRelCharOffset(token.range, currentInput, pos);
|
||||
const expressionInput = (getExpressionInput(currentInput, relCharOffset) || "").trim();
|
||||
|
||||
try {
|
||||
return completeExpression(expressionInput, context, [], validatorFunctions).map(item =>
|
||||
mapExpressionCompletionItem(item, currentInput[relCharOffset])
|
||||
);
|
||||
} catch (e: any) {
|
||||
error(`Error while completing expression: '${e?.message || "<no details>"}'`);
|
||||
} catch (e) {
|
||||
error(`Error while completing expression: '${(e as Error)?.message || "<no details>"}'`);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,10 +73,10 @@ type DeduplicatedWebhooks = {
|
||||
};
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any */
|
||||
const dedupedWebhookPayloads: DeduplicatedWebhooks = webhooks as any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const objects: Param[] = webhookObjects as any;
|
||||
/* eslint-enable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any */
|
||||
|
||||
// Hydrated webhook payloads
|
||||
const webhookPayloads: Webhooks = {};
|
||||
|
||||
@@ -9,28 +9,30 @@ import {TestLogger} from "./test-utils/logger";
|
||||
import {clearCache} from "./utils/workflow-cache";
|
||||
|
||||
const contextProviderConfig: ContextProviderConfig = {
|
||||
getContext: async (context: string) => {
|
||||
getContext: (context: string) => {
|
||||
switch (context) {
|
||||
case "github":
|
||||
return new DescriptionDictionary(
|
||||
{
|
||||
key: "event",
|
||||
value: new data.StringData("push"),
|
||||
description: "The event that triggered the workflow"
|
||||
},
|
||||
{
|
||||
key: "test",
|
||||
value: new DescriptionDictionary({
|
||||
key: "name",
|
||||
return Promise.resolve(
|
||||
new DescriptionDictionary(
|
||||
{
|
||||
key: "event",
|
||||
value: new data.StringData("push"),
|
||||
description: "Name for the test"
|
||||
}),
|
||||
description: "Test dictionary"
|
||||
}
|
||||
description: "The event that triggered the workflow"
|
||||
},
|
||||
{
|
||||
key: "test",
|
||||
value: new DescriptionDictionary({
|
||||
key: "name",
|
||||
value: new data.StringData("push"),
|
||||
description: "Name for the test"
|
||||
}),
|
||||
description: "Test dictionary"
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {isString} from "@github/actions-workflow-parser";
|
||||
import {StringToken} from "@github/actions-workflow-parser/templates/tokens/string-token";
|
||||
import {DescriptionProvider, hover, HoverConfig} from "./hover";
|
||||
import {getPositionFromCursor} from "./test-utils/cursor-position";
|
||||
import {testFileProvider} from "./test-utils/test-file-provider";
|
||||
@@ -8,15 +7,16 @@ import {clearCache} from "./utils/workflow-cache";
|
||||
export function testHoverConfig(tokenValue: string, tokenKey: string, description?: string) {
|
||||
return {
|
||||
descriptionProvider: {
|
||||
getDescription: async (_, token, __) => {
|
||||
getDescription: (_, token) => {
|
||||
if (!isString(token)) {
|
||||
throw new Error("Test provider only supports string tokens");
|
||||
}
|
||||
|
||||
expect(token.value).toEqual(tokenValue);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
expect(token.definition!.key).toEqual(tokenKey);
|
||||
|
||||
return description;
|
||||
return Promise.resolve(description);
|
||||
}
|
||||
} satisfies DescriptionProvider,
|
||||
fileProvider: testFileProvider
|
||||
|
||||
@@ -160,7 +160,8 @@ async function getDescription(
|
||||
function isCronMappingValue(tokenResult: TokenResult): boolean {
|
||||
return (
|
||||
tokenResult.parent?.definition?.key === "cron-mapping" &&
|
||||
isString(tokenResult.token!) &&
|
||||
!!tokenResult.token &&
|
||||
isString(tokenResult.token) &&
|
||||
tokenResult.token.value !== "cron"
|
||||
);
|
||||
}
|
||||
@@ -211,7 +212,7 @@ function expressionHover(
|
||||
};
|
||||
} catch (e) {
|
||||
// Hovering over an invalid expression should not cause an error here
|
||||
info(`Encountered error trying to calculate expression hover: ${e}`);
|
||||
info(`Encountered error trying to calculate expression hover: ${(e as Error).message}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import {TraceWriter} from "@github/actions-workflow-parser/templates/trace-writer";
|
||||
import {NoOperationTraceWriter} from "@github/actions-workflow-parser/templates/trace-writer";
|
||||
|
||||
export const nullTrace: TraceWriter = {
|
||||
info: x => {},
|
||||
verbose: x => {},
|
||||
error: x => {}
|
||||
};
|
||||
export const nullTrace = new NoOperationTraceWriter();
|
||||
|
||||
@@ -14,7 +14,7 @@ beforeEach(() => {
|
||||
});
|
||||
|
||||
const validationConfig: ValidationConfig = {
|
||||
fetchActionMetadata: async (ref: ActionReference) => {
|
||||
fetchActionMetadata: (ref: ActionReference) => {
|
||||
let metadata: ActionMetadata | undefined = undefined;
|
||||
switch (ref.owner + "/" + ref.name + "@" + ref.ref) {
|
||||
case "actions/checkout@v3":
|
||||
@@ -82,7 +82,7 @@ const validationConfig: ValidationConfig = {
|
||||
description: "An action with no inputs"
|
||||
};
|
||||
}
|
||||
return metadata;
|
||||
return Promise.resolve(metadata);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -326,8 +326,8 @@ jobs:
|
||||
config.valueProviderConfig = {
|
||||
"step-with": {
|
||||
kind: ValueProviderKind.AllowedValues,
|
||||
get: async () => {
|
||||
return [{label: "repository", description: "Repository name with owner."}];
|
||||
get: () => {
|
||||
return Promise.resolve([{label: "repository", description: "Repository name with owner."}]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {data, DescriptionDictionary} from "@github/actions-expressions/.";
|
||||
import {DescriptionDictionary} from "@github/actions-expressions/.";
|
||||
import {DiagnosticSeverity} from "vscode-languageserver-types";
|
||||
import {ContextProviderConfig} from "./context-providers/config";
|
||||
import {registerLogger} from "./log";
|
||||
@@ -48,15 +48,16 @@ jobs:
|
||||
|
||||
it("partial skip access invalid context on incomplete", async () => {
|
||||
const contextProviderConfig: ContextProviderConfig = {
|
||||
getContext: async (context: string) => {
|
||||
getContext: (context: string) => {
|
||||
switch (context) {
|
||||
case "secrets":
|
||||
case "secrets": {
|
||||
const dict = new DescriptionDictionary();
|
||||
dict.complete = false;
|
||||
return dict;
|
||||
return Promise.resolve(dict);
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import {createDocument} from "./test-utils/document";
|
||||
import {validate} from "./validate";
|
||||
import {defaultValueProviders} from "./value-providers/default";
|
||||
import {clearCache} from "./utils/workflow-cache";
|
||||
import {ValueProvider, ValueProviderConfig, ValueProviderKind} from "./value-providers/config";
|
||||
import {ValueProviderConfig, ValueProviderKind} from "./value-providers/config";
|
||||
|
||||
beforeEach(() => {
|
||||
clearCache();
|
||||
@@ -249,7 +249,7 @@ jobs:
|
||||
const valueProviderConfig: ValueProviderConfig = {
|
||||
"job-environment": {
|
||||
kind: ValueProviderKind.AllowedValues,
|
||||
get: async () => [{label: "test"}],
|
||||
get: () => Promise.resolve([{label: "test"}]),
|
||||
caseInsensitive: false
|
||||
}
|
||||
};
|
||||
@@ -286,7 +286,7 @@ jobs:
|
||||
const valueProviderConfig: ValueProviderConfig = {
|
||||
"job-environment": {
|
||||
kind: ValueProviderKind.AllowedValues,
|
||||
get: async () => [{label: "test"}],
|
||||
get: () => Promise.resolve([{label: "test"}]),
|
||||
caseInsensitive: true
|
||||
}
|
||||
};
|
||||
|
||||
@@ -23,6 +23,7 @@ import {validateAction} from "./validate-action";
|
||||
import {ValueProviderConfig, ValueProviderKind} from "./value-providers/config";
|
||||
import {defaultValueProviders} from "./value-providers/default";
|
||||
import {fetchOrParseWorkflow, fetchOrConvertWorkflowTemplate} from "./utils/workflow-cache";
|
||||
import {TokenRange} from "@github/actions-workflow-parser/templates/tokens/token-range";
|
||||
|
||||
export type ValidationConfig = {
|
||||
valueProviderConfig?: ValueProviderConfig;
|
||||
@@ -72,7 +73,7 @@ export async function validate(textDocument: TextDocument, config?: ValidationCo
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
error(`Unhandled error while validating: ${e}`);
|
||||
error(`Unhandled error while validating: ${(e as Error).message}`);
|
||||
}
|
||||
|
||||
return diagnostics;
|
||||
@@ -92,18 +93,18 @@ async function additionalValidations(
|
||||
const validationDefinition = validationToken.definition;
|
||||
|
||||
// If this is an expression, validate it
|
||||
if (isBasicExpression(token)) {
|
||||
if (isBasicExpression(token) && token.range) {
|
||||
await validateExpression(
|
||||
diagnostics,
|
||||
token,
|
||||
validationToken.definitionInfo?.allowedContext || [],
|
||||
config?.contextProviderConfig,
|
||||
getProviderContext(documentUri, template, root, token)
|
||||
getProviderContext(documentUri, template, root, token.range)
|
||||
);
|
||||
}
|
||||
|
||||
if (token.definition?.key === "regular-step") {
|
||||
const context = getProviderContext(documentUri, template, root, token);
|
||||
if (token.definition?.key === "regular-step" && token.range) {
|
||||
const context = getProviderContext(documentUri, template, root, token.range);
|
||||
await validateAction(diagnostics, token, context.step, config);
|
||||
}
|
||||
|
||||
@@ -129,7 +130,7 @@ async function additionalValidations(
|
||||
}
|
||||
|
||||
if (valueProvider) {
|
||||
const customValues = await valueProvider.get(getProviderContext(documentUri, template, root, token));
|
||||
const customValues = await valueProvider.get(getProviderContext(documentUri, template, root, token.range));
|
||||
const caseInsensitive = valueProvider.caseInsensitive ?? false;
|
||||
const customValuesMap = new Set(customValues.map(x => (caseInsensitive ? x.label.toLowerCase() : x.label)));
|
||||
|
||||
@@ -161,12 +162,12 @@ function getProviderContext(
|
||||
documentUri: URI,
|
||||
template: WorkflowTemplate,
|
||||
root: TemplateToken,
|
||||
token: TemplateToken
|
||||
tokenRange: TokenRange
|
||||
): WorkflowContext {
|
||||
const {parent, path} = findToken(
|
||||
const {path} = findToken(
|
||||
{
|
||||
line: token.range!.start.line - 1,
|
||||
character: token.range!.start.column - 1
|
||||
line: tokenRange.start.line - 1,
|
||||
character: tokenRange.start.column - 1
|
||||
},
|
||||
root
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user