Simple caching for parsed workflow and template

This commit is contained in:
Jacob Wallraff
2023-03-02 16:43:23 -08:00
parent 4b8613da02
commit f2f377c21c
10 changed files with 58 additions and 12 deletions
+16
View File
@@ -91,9 +91,20 @@ export function initConnection(connection: Connection) {
return result;
});
connection.onInitialized(() => {
if (hasWorkspaceFolderCapability) {
connection.workspace.onDidChangeWorkspaceFolders(_event => {
// Invalidate caches? Not sure what to do here, but we should track
connection.console.log('Workspace folder change event received.');
});
}
})
// The content of a text document has changed. This event is emitted
// when the text document first opened or when its content has changed.
documents.onDidChangeContent(change => {
clearParsedCacheEntry(change.document.uri);
clearWorkflowTemplateCacheEntry(change.document.uri);
return timeOperation("validation", async () => await validateTextDocument(change.document));
});
@@ -119,6 +130,11 @@ export function initConnection(connection: Connection) {
await connection.sendDiagnostics({uri: textDocument.uri, diagnostics: result});
}
// connection.onDidChangeWatchedFiles(async change => {
// // Monitored files have change in VSCode
// connection.console.log('We received an file change event');
// });
connection.onCompletion(async ({position, textDocument}: TextDocumentPositionParams): Promise<CompletionItem[]> => {
return timeOperation(
"completion",
@@ -17,7 +17,7 @@ export async function createWorkflowContext(
if (!parsed.value) {
throw new Error("Failed to parse workflow");
}
const template = await convertWorkflowTemplate(parsed.context, parsed.value);
const template = await convertWorkflowTemplate("test.yaml", parsed.context, parsed.value);
const context: WorkflowContext = {uri: "test.yaml", template};
if (job) {
+1 -1
View File
@@ -77,7 +77,7 @@ export async function complete(
}
const {token, keyToken, parent, path} = findToken(newPos, result.value);
const template = await convertWorkflowTemplate(result.context, result.value, config?.fileProvider, {
const template = await convertWorkflowTemplate(file.name, result.context, result.value, config?.fileProvider, {
fetchReusableWorkflowDepth: config?.fileProvider ? 1 : 0,
errorPolicy: ErrorPolicy.TryConversion
});
+1 -1
View File
@@ -19,7 +19,7 @@ export async function documentLinks(document: TextDocument): Promise<DocumentLin
return [];
}
const template = await convertWorkflowTemplate(result.context, result.value!, undefined, {
const template = await convertWorkflowTemplate(file.name, result.context, result.value!, undefined, {
errorPolicy: ErrorPolicy.TryConversion
});
+1 -1
View File
@@ -52,7 +52,7 @@ export async function hover(document: TextDocument, position: Position, config?:
const {token, keyToken, parent} = tokenResult;
const tokenDefinitionInfo = (keyToken || parent || token)?.definitionInfo;
const template = await convertWorkflowTemplate(result.context, result.value, config?.fileProvider, {
const template = await convertWorkflowTemplate(file.name, result.context, result.value, config?.fileProvider, {
errorPolicy: ErrorPolicy.TryConversion,
fetchReusableWorkflowDepth: config?.fileProvider ? 1 : 0
});
@@ -18,7 +18,7 @@ export async function testGetWorkflowContext(input: string): Promise<WorkflowCon
let template: WorkflowTemplate | undefined;
if (result.value) {
template = await convertWorkflowTemplate(result.context, result.value, testFileProvider, {
template = await convertWorkflowTemplate("wf.yaml", result.context, result.value, testFileProvider, {
fetchReusableWorkflowDepth: 1
});
}
+1 -1
View File
@@ -57,7 +57,7 @@ export async function validate(textDocument: TextDocument, config?: ValidationCo
const result: ParseWorkflowResult = parseWorkflow(file, nullTrace);
if (result.value) {
// Errors will be updated in the context. Attempt to do the conversion anyway in order to give the user more information
const template = await convertWorkflowTemplate(result.context, result.value, config?.fileProvider, {
const template = await convertWorkflowTemplate(file.name, result.context, result.value, config?.fileProvider, {
fetchReusableWorkflowDepth: config?.fileProvider ? 1 : 0,
errorPolicy: ErrorPolicy.TryConversion
});
+1 -1
View File
@@ -38,7 +38,7 @@ jobs:
`convertWorkflowTemplate` then takes that intermediate representation and converts it to a [`WorkflowTemplate`](./src/workflow-template.ts) object, which is a more convenient representation for working with workflows.
```typescript
const workflowTemplate = await convertWorkflowTemplate(result.context, result.value);
const workflowTemplate = await convertWorkflowTemplate("test.yaml", result.context, result.value);
// workflowTemplate.jobs[0].id === "build"
// workflowTemplate.jobs[0].steps[0].run === "echo 'hello'"
+14
View File
@@ -11,6 +11,8 @@ import {convertReferencedWorkflow} from "./converter/referencedWorkflow";
import {isReusableWorkflowJob} from "./type-guards";
import {WorkflowTemplate} from "./workflow-template";
const workflowTemplateCache = new Map<string, WorkflowTemplate>();
export enum ErrorPolicy {
ReturnErrorsOnly,
TryConversion
@@ -46,11 +48,17 @@ const defaultOptions: Required<WorkflowTemplateConverterOptions> = {
};
export async function convertWorkflowTemplate(
fileName: string,
context: TemplateContext,
root: TemplateToken,
fileProvider?: FileProvider,
options: WorkflowTemplateConverterOptions = defaultOptions
): Promise<WorkflowTemplate> {
const cachedResult = workflowTemplateCache.get(fileName)
if (cachedResult) {
return cachedResult;
}
const result = {} as WorkflowTemplate;
const opts = getOptionsWithDefaults(options);
@@ -58,6 +66,7 @@ export async function convertWorkflowTemplate(
result.errors = context.errors.getErrors().map(x => ({
Message: x.message
}));
workflowTemplateCache.set(fileName, result);
return result;
}
@@ -129,6 +138,7 @@ export async function convertWorkflowTemplate(
}
}
workflowTemplateCache.set(fileName, result);
return result;
}
@@ -145,3 +155,7 @@ function getOptionsWithDefaults(options: WorkflowTemplateConverterOptions): Requ
errorPolicy: options.errorPolicy !== undefined ? options.errorPolicy : defaultOptions.errorPolicy
};
}
export function clearWorkflowTemplateCacheEntry(uri: string) {
workflowTemplateCache.delete(uri);
}
@@ -6,6 +6,9 @@ import {File} from "./file";
import {WORKFLOW_ROOT} from "./workflow-constants";
import {getWorkflowSchema} from "./workflow-schema";
import {YamlObjectReader} from "./yaml-object-reader";
const parsedWorkflowCache = new Map<string, ParseWorkflowResult>();
export interface ParseWorkflowResult {
context: TemplateContext;
value: TemplateToken | undefined;
@@ -14,6 +17,11 @@ export interface ParseWorkflowResult {
export function parseWorkflow(entryFile: File, trace: TraceWriter): ParseWorkflowResult;
export function parseWorkflow(entryFile: File, context: TemplateContext): ParseWorkflowResult;
export function parseWorkflow(entryFile: File, contextOrTrace: TraceWriter | TemplateContext): ParseWorkflowResult {
const cachedResult = parsedWorkflowCache.get(entryFile.name)
if (cachedResult) {
return cachedResult;
}
const context =
contextOrTrace instanceof TemplateContext
? contextOrTrace
@@ -26,15 +34,23 @@ export function parseWorkflow(entryFile: File, contextOrTrace: TraceWriter | Tem
for (const err of reader.errors) {
context.error(fileId, err.message, err.range);
}
return {
const result = {
context,
value: undefined
};
parsedWorkflowCache.set(entryFile.name, result);
return result
}
const result = templateReader.readTemplate(context, WORKFLOW_ROOT, reader, fileId);
const templateToken = templateReader.readTemplate(context, WORKFLOW_ROOT, reader, fileId);
return <ParseWorkflowResult>{
const result = {
context,
value: result
};
value: templateToken
} satisfies ParseWorkflowResult;
parsedWorkflowCache.set(entryFile.name, result);
return result;
}
export function clearParsedCacheEntry(path: string) {
parsedWorkflowCache.delete(path);
}