/
push.ts
125 lines (104 loc) · 3.76 KB
/
push.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import { Args, Flags } from "@oclif/core";
import BaseCommand from "@/lib/base-command";
import { KnockEnv } from "@/lib/helpers/const";
import { formatError, formatErrors, SourceError } from "@/lib/helpers/error";
import * as CustomFlags from "@/lib/helpers/flag";
import { merge } from "@/lib/helpers/object.isomorphic";
import { formatErrorRespMessage, isSuccessResp } from "@/lib/helpers/request";
import { indentString } from "@/lib/helpers/string";
import { spinner } from "@/lib/helpers/ux";
import { WithAnnotation } from "@/lib/marshal/shared/types";
import * as Workflow from "@/lib/marshal/workflow";
import WorkflowValidate from "./validate";
export default class WorkflowPush extends BaseCommand<typeof WorkflowPush> {
static summary =
"Push one or more workflows from a local file system to Knock.";
static flags = {
environment: Flags.string({
summary:
"Pushing a workflow is only allowed in the development environment",
default: KnockEnv.Development,
options: [KnockEnv.Development],
}),
all: Flags.boolean({
summary: "Whether to push all workflows from the target directory.",
}),
"workflows-dir": CustomFlags.dirPath({
summary: "The target directory path to find all workflows to push.",
dependsOn: ["all"],
}),
commit: Flags.boolean({
summary: "Push and commit the workflow(s) at the same time",
}),
"commit-message": Flags.string({
summary: "Use the given value as the commit message",
char: "m",
dependsOn: ["commit"],
}),
};
static args = {
workflowKey: Args.string({
required: false,
}),
};
async run(): Promise<void> {
const { flags } = this.props;
// 1. First read all workflow directories found for the given command.
const target = await Workflow.ensureValidCommandTarget(
this.props,
this.runContext,
);
const [workflows, readErrors] = await Workflow.readAllForCommandTarget(
target,
{ withExtractedFiles: true },
);
if (readErrors.length > 0) {
this.error(formatErrors(readErrors, { prependBy: "\n\n" }));
}
if (workflows.length === 0) {
this.error(`No workflow directories found in ${target.context.abspath}`);
}
// 2. Then validate them all ahead of pushing them.
spinner.start(`‣ Validating`);
const apiErrors = await WorkflowValidate.validateAll(
this.apiV1,
this.props,
workflows,
);
if (apiErrors.length > 0) {
this.error(formatErrors(apiErrors, { prependBy: "\n\n" }));
}
spinner.stop();
// 3. Finally push up each workflow, abort on the first error.
spinner.start(`‣ Pushing`);
for (const workflow of workflows) {
const props = merge(this.props, { flags: { annotate: true } });
// eslint-disable-next-line no-await-in-loop
const resp = await this.apiV1.upsertWorkflow<WithAnnotation>(props, {
...workflow.content,
key: workflow.key,
});
if (isSuccessResp(resp)) {
// Update the workflow directory with the successfully pushed workflow
// payload from the server.
// eslint-disable-next-line no-await-in-loop
await Workflow.writeWorkflowDirFromData(workflow, resp.data.workflow!);
continue;
}
const error = new SourceError(
formatErrorRespMessage(resp),
Workflow.workflowJsonPath(workflow),
"ApiError",
);
this.error(formatError(error));
}
spinner.stop();
// 4. Display a success message.
const workflowKeys = workflows.map((w) => w.key);
const actioned = flags.commit ? "pushed and committed" : "pushed";
this.log(
`‣ Successfully ${actioned} ${workflows.length} workflow(s):\n` +
indentString(workflowKeys.join("\n"), 4),
);
}
}