Merge pull request #100 from github/thyeggman/cronstrue-hover

Produce hover sentence for cron expressions
This commit is contained in:
Jacob Wallraff
2023-01-24 15:05:53 -08:00
committed by GitHub
11 changed files with 255 additions and 138 deletions
@@ -99,7 +99,6 @@ function getEventContext(workflowContext: WorkflowContext): ExpressionData {
function merge(d: data.Dictionary, toAdd: Object): data.Dictionary {
for (const [key, value] of Object.entries(toAdd)) {
if (value && typeof value === "object" && !d.get(key)) {
if (!Array.isArray(value) && Object.entries(value).length === 0) {
// Allow an empty object to be any value
d.add(key, new data.Null());
+33
View File
@@ -96,6 +96,39 @@ jobs:
expect(result?.contents).toEqual("Runs your workflow when you push a commit or tag.");
});
it("on a cron schedule", async () => {
const input = `on:
schedule:
- cron: '0,30 0|,12 * * *'
`;
const result = await hover(...getPositionFromCursor(input));
expect(result).not.toBeUndefined();
expect(result?.contents).toEqual(
"Runs at 0 and 30 minutes past the hour, at 00:00 and 12:00\n\n" +
"Actions schedules run at most every 5 minutes. " +
"[Learn more](https://docs.github.com/actions/using-workflows/workflow-syntax-for-github-actions#onschedule)"
);
});
it("on a cron mapping key", async () => {
const input = `on:
schedule:
- c|ron: '0 0 * * *'
`;
const result = await hover(...getPositionFromCursor(input));
expect(result).not.toBeUndefined();
expect(result?.contents).toEqual("");
});
it("on an invalid cron schedule", async () => {
const input = `on:
schedule:
- cron: '0 0 |* * * * *'
`;
const result = await hover(...getPositionFromCursor(input));
expect(result).not.toBeUndefined();
expect(result?.contents).toEqual("");
});
it("shows context inherited from parent nodes", async () => {
const input = `
+24 -2
View File
@@ -1,7 +1,10 @@
import {convertWorkflowTemplate, parseWorkflow, ParseWorkflowResult} from "@github/actions-workflow-parser";
import {ErrorPolicy} from "@github/actions-workflow-parser/model/convert";
import {TemplateToken} from "@github/actions-workflow-parser/templates/tokens/template-token";
import {TokenResult} from "./utils/find-token";
import {File} from "@github/actions-workflow-parser/workflows/file";
import {isString} from "@github/actions-workflow-parser/templates/tokens/type-guards";
import {StringToken} from "@github/actions-workflow-parser/templates/tokens/string-token";
import {Position, TextDocument} from "vscode-languageserver-textdocument";
import {Hover} from "vscode-languageserver-types";
import {getWorkflowContext, WorkflowContext} from "./context/workflow-context";
@@ -9,6 +12,7 @@ import {info} from "./log";
import {nullTrace} from "./nulltrace";
import {findToken} from "./utils/find-token";
import {mapRange} from "./utils/range";
import {getCronDescription} from "@github/actions-workflow-parser/model/converter/cron";
export type DescriptionProvider = {
getDescription(context: WorkflowContext, token: TemplateToken, path: TemplateToken[]): Promise<string | undefined>;
@@ -26,14 +30,26 @@ export async function hover(document: TextDocument, position: Position, config?:
};
const result = parseWorkflow(file.name, [file], nullTrace);
const {token, path} = findToken(position, result.value);
const tokenResult = findToken(position, result.value);
const token = tokenResult.token;
if (!token?.definition) {
return null;
}
info(`Calculating hover for token with definition ${token.definition.key}`);
let description = await getDescription(document, config, result, token, path);
if (tokenResult.parent && isCronMappingValue(tokenResult)) {
const tokenValue = (token as StringToken).value
let description = getCronDescription(tokenValue);
if (description) {
return {
contents: description,
range: mapRange(token.range)
} as Hover;
}
}
let description = await getDescription(document, config, result, token, tokenResult.path);
const allowedContext = token.definitionInfo?.allowedContext;
if (allowedContext && allowedContext?.length > 0) {
@@ -64,3 +80,9 @@ async function getDescription(
const description = await config.descriptionProvider.getDescription(workflowContext, token, path);
return description || defaultDescription;
}
function isCronMappingValue(tokenResult: TokenResult): boolean {
return tokenResult.parent?.definition?.key === "cron-mapping" &&
isString(tokenResult.token!) &&
tokenResult.token.value !== "cron"
}
@@ -1248,7 +1248,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- run: echo \${{ github.event.client_payload.anything }}
- run: echo \${{ github.event.client_payload.branch }}`
- run: echo \${{ github.event.client_payload.branch }}`;
const result = await validate(createDocument("wf.yaml", input));