Deno.run
cmd arguments
const p = Deno.run({
cmd: ["echo", "I love 🍉"],
});
// await its completion
await p.status();
Which works just as expected, and echo
'es (prints) the provided text to the terminal.
Though when I wanted my CLI to help me with more complex tasks I run into unexpected behaviour.
I'm using Fly.io to deploy my site using a custom Dockerfile
and I wanted to include the current git hash into my website so I thought I'd pass it to the Dockerfile
via a --build-arg
🤓
Though confusingly the following makes the flyctl
CLI throw: Error unknown flag: --build-arg GIT_REVISION
const p = Deno.run({
cmd: [
"flyctl",
"deploy",
"--remote-only",
"--no-cache",
`--build-arg GIT_REVISION=${await getGitRevision()}`, // won't work
],
});
Which confused me as I actually expected I could just pass kinda any command even in a single string, though that's not the case as for example the following doesn't work either, or at least not as expected
const p = Deno.run({
cmd: ["echo", "I love 🍉", "&&", "echo", "and 🍍"],
});
this prints
I love 🍉 && echo and 🍍
instead of
I love 🍉
and 🍍
Checking the docs
Spawns new subprocess. RunOptions must contain at a minimum the opt.cmd, an array of program arguments, the first of which is the binary.
"[..] the first of which is the binary.", so you can only spawn one command, which when thinking about it probably makes sense and things simpler and as we're already in-code it's no trouble running Deno.run
as often as we like right 🥳
How to properly pass argument values
So the solution to my initial problem is to put the value in it's own array item like in the following example
const p = Deno.run({
cmd: [
"flyctl",
"deploy",
"--remote-only",
"--no-cache",
"--build-arg",
`GIT_REVISION=${await getGitRevision()}`,
],
});
Get && commands working in Deno
function run(cmd: string) {
let cmds: string[];
if (cmd.includes("&&")) {
cmds = cmd.split("&&").map((c) => c.trim());
} else {
cmds = [cmd];
}
const processes: Deno.Process[] = [];
for (const index in cmds) {
const command = cmds[index];
const p = Deno.run({
cmd: command.split(" "),
stdout: "piped",
});
processes.push(p);
}
return processes;
}
Which you can use like so
const [p1, p2] = run("echo 'command 1' && echo 'command 2'");
await p1.status();
await p2.status();
// and to get the commands outputs
const decoder = new TextDecoder();
console.log(decoder.decode(await p1.output()).replace(/\n$/, ""));
console.log(decoder.decode(await p2.output()).replace(/\n$/, ""));
You could chain more of course or even await Promise.all
and because stdout
is set to piped
can read the commands outputs, or remove stdout
from Deno.run
to have it print to your terminal without the need for console.log
.
🦕😃
Bonus: Get the current git revision hash
const getGitRevision = async () => {
const p = await Deno.run({
cmd: ["git", "rev-parse", "--short", "HEAD"],
stdout: "piped",
});
const decoder = new TextDecoder();
return decoder.decode(await p.output()).replace(/\n$/, "");
};
Related

Doing web-development since around 2000, building my digital garden with a mix of back-to-the-roots-use-the-platform and modern edge-rendered-client-side-magic tech 📻🚀
Living and working in Cusco, Perú 🦙