Add support for output templating

Co-authored-by: Fredrik Adelöw <freben@gmail.com>
Co-authored-by: blam<ben@blam.sh>
Co-authored-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Johan Haals
2021-02-11 14:02:00 +01:00
parent c202599008
commit 5aa002fb20
5 changed files with 64 additions and 12 deletions
+3 -2
View File
@@ -50,6 +50,7 @@
"fs-extra": "^9.0.0",
"git-url-parse": "^11.4.4",
"globby": "^11.0.0",
"handlebars": "^4.7.6",
"helmet": "^4.0.0",
"isomorphic-git": "^1.8.0",
"jsonschema": "^1.2.6",
@@ -66,9 +67,9 @@
"@types/mock-fs": "^4.13.0",
"@types/supertest": "^2.0.8",
"mock-fs": "^4.13.0",
"msw": "^0.21.2",
"supertest": "^4.0.2",
"yaml": "^1.10.0",
"msw": "^0.21.2"
"yaml": "^1.10.0"
},
"files": [
"dist",
@@ -63,11 +63,17 @@ export class TaskAgent implements Task {
});
}
async complete(result: CompletedTaskState): Promise<void> {
async complete(
result: CompletedTaskState,
metadata?: JsonObject,
): Promise<void> {
await this.storage.completeTask({
taskId: this.state.taskId,
status: result === 'failed' ? 'failed' : 'completed',
eventBody: { message: `Run completed with status: ${result}` },
eventBody: {
message: `Run completed with status: ${result}`,
...metadata,
},
});
this.isDone = true;
if (this.heartbeatTimeoutId) {
@@ -22,6 +22,7 @@ import { TaskBroker, Task } from './types';
import fs from 'fs-extra';
import path from 'path';
import { TemplateActionRegistry } from './TemplateConverter';
import * as handlebars from 'handlebars';
type Options = {
logger: Logger;
@@ -55,7 +56,11 @@ export class TaskWorker {
`Starting up work with ${task.spec.steps.length} steps`,
);
const outputs: { [name: string]: JsonValue } = {};
const templateCtx: {
steps: {
[stepName: string]: { output: { [outputName: string]: JsonValue } };
};
} = { steps: {} };
for (const step of task.spec.steps) {
const metadata = { stepId: step.id };
@@ -87,8 +92,24 @@ export class TaskWorker {
throw new Error(`Action '${step.action}' does not exist`);
}
// TODO: substitute any placeholders with output from previous steps
const parameters = step.parameters!;
const parameters: { [name: string]: JsonValue } = {};
for (const [name, maybeTemplateStr] of Object.entries(
step.parameters ?? {},
)) {
if (typeof maybeTemplateStr === 'string') {
const value = handlebars.compile(maybeTemplateStr, {
noEscape: true,
strict: true,
data: false,
preventIndent: true,
})(templateCtx);
parameters[name] = value;
} else {
parameters[name] = maybeTemplateStr;
}
}
const stepOutputs: { [name: string]: JsonValue } = {};
await action.handler({
logger,
@@ -96,10 +117,12 @@ export class TaskWorker {
parameters,
workspacePath,
output(name: string, value: JsonValue) {
outputs[name] = value;
stepOutputs[name] = value;
},
});
templateCtx.steps[step.id] = { output: stepOutputs };
await task.emitLog(`Finished step ${step.name}`, {
...metadata,
status: 'completed',
@@ -112,9 +135,24 @@ export class TaskWorker {
throw error;
}
}
await task.complete('completed');
const output = Object.fromEntries(
Object.entries(task.spec.output).map(([name, templateStr]) => {
const value = handlebars.compile(templateStr, {
noEscape: true,
strict: true,
data: false,
preventIndent: true,
})(templateCtx);
return [name, value];
}),
);
await task.complete('completed', { output });
} catch (error) {
await task.complete('failed');
await task.complete('failed', {
error: { name: error.name, message: error.message },
});
}
}
}
@@ -76,7 +76,13 @@ export function templateEntityToSpec(
},
});
return { steps };
return {
steps,
output: {
remoteUrl: '{{ steps.publish.output.remoteUrl }}',
catalogInfoUrl: '{{ steps.publish.output.catalogInfoUrl }}',
},
};
}
type ActionContext = {
@@ -49,6 +49,7 @@ export type TaskSpec = {
action: string;
parameters?: { [name: string]: JsonValue };
}>;
output: { [name: string]: string };
};
export type DispatchResult = {
@@ -59,7 +60,7 @@ export interface Task {
spec: TaskSpec;
done: boolean;
emitLog(message: string, metadata?: JsonValue): Promise<void>;
complete(result: CompletedTaskState): Promise<void>;
complete(result: CompletedTaskState, metadata?: JsonValue): Promise<void>;
getWorkspaceName(): Promise<string>;
}