Merge branch 'main' into joshmgross/schema-function-descriptions

This commit is contained in:
Josh Gross
2023-03-20 18:58:10 -04:00
committed by GitHub
23 changed files with 1741 additions and 233 deletions
@@ -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);
}
};
+7 -3
View File
@@ -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 = {};
+18 -16
View File
@@ -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);
}
};
+3 -3
View File
@@ -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
+3 -2
View File
@@ -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;
}
}
+2 -6
View File
@@ -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();
+4 -4
View File
@@ -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 -3
View File
@@ -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
}
};
+11 -10
View File
@@ -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
);