- v1.7.0Latest
- v1.6.0
- v1.5.2
- v1.5.1
- v1.5.0
- v1.4.7
- v1.4.6
- v1.4.5
- v1.4.4
- v1.4.3
- v1.4.2
- v1.4.1
- v1.4.0
- v1.3.2
- v1.3.1
- v1.2.6
- v1.2.5
- v1.2.4
- v1.2.3
- v1.2.2
- v1.2.1
- v1.2.0
- v1.1.1
- v1.1.0
- v1.0.0
- v1.0.0-rc3
- v1.0.0-rc2
- v1.0.0-rc1
- v0.42.0
- v0.41.0
- v0.16.0
- v0.15.1
- v0.15.0
- v0.14.0
- v0.13.0
- v0.12.0
- v0.11.0
- v0.10.1
- v0.10.0
- v0.9.0
- v0.8.0
- v0.7.0
- v0.6.1
- v0.6.0
- v0.5.0
- v0.4.0
- v0.3.1
- v0.3.0
- v0.2.2
- v0.2.1
- v0.2.0
- v0.1.1
- v0.1.0
Drake — a task runner for Deno
Drake is a Make-like task runner for Deno inspired by Make, Rake and Jake.
- Drakefiles (c.f. Makefiles) are Deno TypeScript modules.
- Optional task prerequisites (dependencies).
- File tasks and non-file tasks.
- Drake API functions for defining, registering and running tasks.
drake
CLI.
NOTE: This is a development release. A production release will follow once Deno has reached 1.0.
Tested with Deno 0.35.0 running on Ubuntu 18.04.
Drakefiles
A drakefile is a TypeScript module that:
- Imports the Drake module.
- Defines and registers tasks.
- Runs tasks.
Example drakefile:
import { desc, run, task } from "https://raw.github.com/srackham/drake/master/mod.ts";
desc("Minimal Drake task");
task("hello", [], function() {
console.log("Hello World!");
});
run()
The desc()
and task()
APIs define and register tasks. The run()
API executes the tasks that were specified on the command-line along
with their prerequisite tasks. run()
is normally the last statement
in the drakefile. Tasks are executed in the correct dependency order.
Here are a couple of real-world examples:
- https://github.com/srackham/drake/blob/master/Drakefile.ts
- https://github.com/srackham/rimu-deno/blob/master/Drakefile.ts
Tasks
There are two types of task: Normal tasks and File tasks.
Task types are distinguished by their names. Normal task names can
only contain alphanumeric, underscore and hyphen characters and cannot
start with a hyphen e.g. test
, hello-world
. File task names are
valid file paths. In cases of ambiguity a File task name should be
prefixed with a period and a path separator e.g. ./hello-world
.
A Normal task executes unconditionally. A File task is only executed if it is out of date i.e. either the task name file does not exist or one or more prerequisite files has a more recent modification time.
If a File task execution error occurs the following precautions are taken to ensure the task remains out of date:
- If a new target file has been created then it is deleted.
- If an existing target file modification date has changed then it is reverted to the prior date.
File path task names and file task prerequisite names are normalized at task registration.
Task properties
name: A unique task name.
prereqs: An array of prerequisite task names i.e. the names of tasks to be run prior to executing the task action function. Prerequisites can be Normal task names, File task names, file paths and globs (wildcards):
- Normal task names must have a matching task.
- File path prerequisites do not require a matching task.
- Globs are expanded when the task is registered with the
task()
API.
desc:
An optional task description that is set by the desc()
API.
action:
An optional function that is run if the task is selected for
execution. The action
function is bound to the parent task object
i.e. the parent task properties are accessible inside the action
function through the this
object e.g. this.prereqs
returns the
task’s prerequisite names array.
Asynchronous task actions
Normally you will want tasks to execute sequentially i.e. the next task should not start until the current task has finished. To ensure this happens action functions that call asynchronous functions should:
- Be delared
async
. - Call asynchronous functions with the
await
operator.
For example, the following task does not return until the shell command has successfully executed:
task("shell", [], async function() {
await sh("echo Hello World");
});
Without the await
operator sh("echo Hello World")
will return
immediately and the action function will exit before the shell command
has even started.
Of course you are free to eschew await
and use the promises
returned by asynchronous functions in any way that makes sense.
drake CLI
The drake
CLI is a thin wrapper for executing a drakefile.
To install the drake
CLI executable:
deno install --force -A drake https://raw.github.com/srackham/drake/master/drake.ts
Run it with e.g.
$HOME/.deno/bin/drake --help
The drake
CLI is handy, but you can also run drakefiles directly
with the deno run
command.
drake man page
NAME
drake - a make-like task runner for Deno.
SYNOPSIS
drake [OPTION|VARIABLE|TASK]...
deno run [DENO_OPTION...] DRAKEFILE [OPTION|VARIABLE|TASK]...
DESCRIPTION
The Drake TypeScript library provides functions for defining and executing
build TASKs on the Deno runtime.
A DRAKEFILE is a TypeScript module file containing Drake task definitions.
The 'drake' CLI is a thin wrapper for executing a DRAKEFILE.
A Drake VARIABLE is a named string value e.g. 'vers=0.1.0'. Variables are
accessed via the Drake 'env' object e.g. 'env.vers' or 'env["vers"]'.
OPTIONS
-a, --always-make Unconditionally execute tasks.
-d, --directory DIR Change to directory DIR before running drakefile.
-f, --drakefile FILE Use FILE as drakefile (default: './Drakefile.ts').
-h, --help Display this help message.
-l, --list-tasks Display task names, descriptions and prerequisites.
-n, --dry-run Skip task execution.
-q, --quiet Do not log drake messages to standard output.
--version Display the drake version.
ENVIRONMENT VARIABLES
NO_COLOR Set to disable color (see https://no-color.org/).
SEE ALSO
The Drake user guide: https://github.com/srackham/drake
Command-line variables
A Drake command-line variable is a named string value that is made
available to the drakefile. Variables are formatted like
<name>=<value>
e.g. vers=0.1.0
. Variables are accessed within a
drakefile via the env
object e.g. env.vers
or env["vers"]
.
Variable names can only contain alphanumeric or underscore characters and must start with an alpha character.
Drake API
The Drake library module exports the following objects and functions:
abort
function abort(message: string): void;
Print error message to to stdout
and terminate execution.
desc
function desc(description: string): void;
Set description of next registered task.
env
The Drake env
object stores command-line options, task names and
variables.
Options are keyed by their long option name e.g. env["--dry-run"]
.
Unspecified flag options are undefined; unspecified value options
are assigned their default value.
Task names are stored in the env["--tasks"]
string array. A default
task can be specified by setting env["--default-task"]
to the task
name.
Command-line variable values are keyed by name. For example
vers=1.0.1
on the command-line is available as env["vers"]
and
env.vers
.
execute
async function execute(names: string | string[]);
Unconditionally execute task action functions ignoring task prerequisites.
- If
names
is a task name string execute the task action. - If
names
is an array of task names execute their actions asynchronously. - Silently skip tasks that have no action function.
glob
function glob(...patterns: string[]): string[];
Return a sorted array of normalized file names matching the wildcard patterns.
Wildcard patterns can include the **
(globstar) pattern.
e.g. glob("tmp/*.ts", "lib/**/*.ts", "mod.ts");
log
function log(message: string): void;
Log a message to the console. Do not log the message if the --quiet
command-line option is set.
quote
function quote(values: string[], sep: string = " "): string;
Quote string array values with double-quotes then join them with a separator. Double-quote characters are escaped with a backspace. The separator defaults to a space character.
readFile
function readFile(filename: string): string;
Read the entire contents of a file synchronously to a UTF-8 string.
run
async function run(...names: string[]);
Execute named tasks along with their prerequisite tasks (direct and
indirect). If no names
are specified then the command-line tasks
are run. If no command-line tasks were specified the default task (set
in env["--default-task"]
) is run.
Task execution is ordered such that prerequisite tasks are executed prior to their parent task. The same task is never run twice.
sh
async function sh(commands: string | string[], opts: ShOpts = {});
Execute commands in the command shell.
- If
commands
is a string execute it. - If
commands
is an array of commands execute them asynchronously. - If any command fails throw an error.
- If
opts.stdout
oropts.stderr
is set to"null"
then the respective outputs are ignored. opts.cwd
sets the shell current working directory (defaults to the parent process working directory).- The
opts.env
mapping passes additional environment variables to the shell.
Examples:
await sh("echo Hello World");
await sh(["echo Hello 1", "echo Hello 2", "echo Hello 3"]);
await sh("echo Hello World", { stdout: "null" });
shCapture
async function shCapture(command: string, opts: ShCaptureOpts = {}): Promise<ShOutput>;
Execute command
in the command shell and return a promise for the exit code
,
output
(the stdout output) and error
(the stderr output).
- If the
opts.input
string has been assigned then it is piped to the shellstdin
. opts.cwd
sets the shell current working directory (defaults to the parent process working directory).- The
opts.env
mapping passes additional environment variables to the shell. opts.stdout
andopts.stderr
haveDeno.ProcessStdio
semantics and default to"piped"
.
Examples:
const { code, output, error } = await shCapture("echo Hello");
task
function task(name: string, prereqs: string[] = [], action?: Action): void;
Create and register a task.
name
is a unique task name.prereqs
is an array of prerequisite task names i.e. the names of tasks to be run prior to executing the task action function.action
is an optional function (type Action = (this: Task) => any;
) that is run if the task is selected for execution.
writeFile
function writeFile(filename: string, text: string): void;
Write text to a file synchronously. If the file exists it will be overwritten.
updateFile
function updateFile(filename: string, find: RegExp, replace: string): void;
Find and replace in text file synchronously.
vers
const vers: string;
The Drake version number.
Tips for using Drake
Use shell quoting and escapes to pass command-line variable values containing spaces or special characters e.g.
"title=Foo & bar"
.Don’t forget to use
await
when callingasync
functions.Task path name prerequisites can be glob wildcards.
Path names can refer to any file type (not just regular files).
Use the Drake
readFile
,writeFile
andupdateFile
APIs to synchronously read, write and update text files.The Drake
sh
API can be used to run multiple shell commands asynchronously. The following example starts two shell commands then waits for both to finish before continuing:await sh(["echo foo", "echo bar"]);
The Drake
sh
API can be used to run multi-line template string bash scripts e.g.await sh(`set -e # Exit immediately on error. echo Hello World if [ "$EUID" -eq 0 ]; then echo "Running as root" else echo "Running as $USER" fi ls wc Drakefile.ts`);
The built-in Deno API has many useful functions e.g.
Deno.mkdirSync(dirname); const tempDir= Deno.makeTempDirSync(); const modTime = Deno.statSync(filename).modified;
Escape backslash and backtick characters and placeholders in template string literals with a backslash:
\\
translates to\
\`
translates to`
\${
translates to${
You can use the
utils.ts
module in non-Drakefiles. The utility functions manifest errors by throwingDrakeError
exceptions. Useable functions include:abort
,glob
,log
,quote
,readFile
,sh
,shCapture
,updateFile
,writeFile
. For example:import { glob, sh } from "https://raw.github.com/srackham/drake/master/lib/utils.ts";
A drakefile can be run directly with the
deno run
command and can also be installed as an executable using thedeno install
command.