Files
windmill/cli/flow.ts
Kai Jellinghaus cdd3e2cfc1 feat(cli): Run flows & scripts (#940)
* Enable Script Running from CLI

* Improve Script Logic & Enable Flow run

* Update README

* Fix empty input 415 unsupported media type

* Add flow execution asciicast

* Allow reading inputs

* Add --silent & print result

* Updated syntax

* Update readme

* Fix superadmin users

* Handle values correctly

* Rework input parsing to try-catch JSON

* Accept all input types

* VHS scripts

* Test add Video to Markdown

* Use GIF only

* Final revisions

* I'm not sure why this works but stackoverflow told me
https://stackoverflow.com/questions/4279611/how-to-embed-a-video-into-github-readme-md/4279746#4279746

* Also rename file?

* Use MP4

* Use GIF

* Use MP4 again

* Revert "Use MP4 again"

This reverts commit d3ed4dc28a.
2022-11-25 18:05:23 +01:00

177 lines
4.6 KiB
TypeScript

import { Command } from "https://deno.land/x/cliffy@v0.25.4/command/command.ts";
import {
FlowService,
JobService,
} from "https://deno.land/x/windmill@v1.50.0/mod.ts";
import { GlobalOptions } from "./types.ts";
import {
Flow,
OpenFlow,
} from "https://deno.land/x/windmill@v1.50.0/windmill-api/index.ts";
import { colors } from "https://deno.land/x/cliffy@v0.25.4/ansi/colors.ts";
import { getContext } from "./context.ts";
import { Table } from "https://deno.land/x/cliffy@v0.25.4/table/table.ts";
import { resolve, track_job } from "./script.ts";
type Options = GlobalOptions;
async function push(opts: Options, filePath: string, remotePath: string) {
const { workspace } = await getContext(opts);
if (!(remotePath.startsWith("g") || remotePath.startsWith("u"))) {
console.log(
colors.red(
"Given remote path looks invalid. Remote paths are typicall of the form <u|g>/<username|group>/..."
)
);
return;
}
await pushFlow(filePath, workspace, remotePath);
console.log(colors.bold.underline.green("Flow successfully pushed"));
}
export async function pushFlow(
filePath: string,
workspace: string,
remotePath: string
) {
const data: OpenFlow = JSON.parse(await Deno.readTextFile(filePath));
if (
await FlowService.existsFlowByPath({
workspace: workspace,
path: remotePath,
})
) {
console.log(colors.bold.yellow("Updating existing flow..."));
await FlowService.updateFlow({
workspace: workspace,
path: remotePath,
requestBody: {
path: remotePath,
summary: data.summary,
value: data.value,
schema: data.schema,
description: data.description,
},
});
} else {
console.log(colors.bold.yellow("Creating new flow..."));
await FlowService.createFlow({
workspace: workspace,
requestBody: {
path: remotePath,
summary: data.summary,
value: data.value,
schema: data.schema,
description: data.description,
},
});
}
}
async function list(opts: GlobalOptions & { showArchived?: boolean }) {
const { workspace } = await getContext(opts);
let page = 0;
const perPage = 10;
const total: Flow[] = [];
while (true) {
const res = await FlowService.listFlows({
workspace,
page,
perPage,
showArchived: opts.showArchived ?? false,
});
page += 1;
total.push(...res);
if (res.length < perPage) {
break;
}
}
new Table()
.header(["path", "summary", "edited at", "edited by"])
.padding(2)
.border(true)
.body(
total.map((x) => [
x.path,
x.summary,
x.edited_at,
x.edited_by,
x.description ?? "-",
])
)
.render();
}
async function run(
opts: GlobalOptions & {
input: string[];
silent: boolean;
},
path: string
) {
const { workspace } = await getContext(opts);
const input = await resolve(opts.input);
const id = await JobService.runFlowByPath({
workspace,
path,
requestBody: input,
});
let i = 0;
while (true) {
const jobInfo = await JobService.getJob({ workspace, id });
if (jobInfo.flow_status!.modules.length <= i) {
break;
}
const module = jobInfo.flow_status!.modules[i];
if (module.job) {
if (!opts.silent) {
console.log("====== Job " + (i + 1) + " ======");
await track_job(workspace, module.job);
}
} else {
console.log(module.type);
await new Promise((resolve, _) =>
setTimeout(() => resolve(undefined), 100)
);
continue;
}
i++;
}
if (!opts.silent) {
console.log(colors.green.underline.bold("Flow ran to completion"));
}
const jobInfo = await JobService.getCompletedJob({ workspace, id });
console.log(jobInfo.result ?? {});
}
const command = new Command()
.description("flow related commands")
.option("--show-archived", "Enable archived scripts in output")
.action(list as any)
.command(
"push",
"push a local flow spec. This overrides any remote versions."
)
.arguments("<file_path:string> <remote_path:string>")
.action(push as any)
.command("run", "run a flow by path.")
.arguments("<path:string>")
.option(
"-i --input [inputs...:string]",
"Inputs specified as JSON objects or simply as <name>=<value>. Supports file inputs using @<filename> and stdin using @- these also need to be formatted as JSON. Later inputs override earlier ones."
)
.option(
"-s --silent",
"Do not ouput anything other then the final output. Useful for scripting."
)
.action(run as any);
export default command;