doit Good#

Here are some opinions on writing portable, robust doit workflows.

Pick good default_tasks#

A practical convention is that running doit, without any inputs, should leave the project in a useful state for development and inspection.

For example, a good default to consider is what one might show on MyBinder, with the simplest possible postBuild file like:

#!/usr/bin/env bash
doit

This should leave a project with:

  • a verified, up-to-date installed runtime and test dependencies

  • any packages-under-development installed

Get responsive and stable#

A good doit task tree should be responsive and stable.

A responsive task tree should correctly run all of the tasks in the right order, leaving the correct files on disk. When an important file (or part of it) changes, the right tasks and outputs should be out-of-date.

A stable task tree should ideally only require one command, and running the same task without changing any files, should not do any extra work.

Format all the things#

A key way to get the most stable product is to make liberal use of automated formatters, with as aggressive-as-possible options. A doit format task that automates as much as possible will not only improve the stability and responsiveness of a doit task tree, but also lead to cleaner revision control history.

For example, on doitoml’s own Python code, ssort and ruff are used to take as much guesswork as possible out of line- and token-level syntax choices.

Single sources of truth#

Any time there is a magic number (or other value) that is important to multiple processes, try to store it in exactly one place, and either reuse it from there, or check other files against it.

Hint

For example, in a Python project, storing the package’s version in exactly pyproject.toml#/project/version and nowhere else ensures that no automation or build scripts go stale.

Use file_dep and targets#

While task_dep and uptodate can be useful in a pinch, hash-based comparisons of well-known files save time and effort.

Hint

Using custom logging is a good way to get predictablly-named file outputs from otherwise hard-to-observe tasks.

One of the key benefits of knowing this dependency tree up-front is being able to use doit --process=2 (or -n2). This utilizes modern, multi-core systems without too much coordination or needless re-work due to long, but avoidable, command errors about missing files, or worse, incorrect build ordering based on stale data.

Avoid configurable task options#

doit allows tasks to declare their own options which are propagated to the CLI, and discoverable with doit help {task-name}. However, these don’t compose very well without very careful management of names.

Hint

A useful convention is to make use of environment variables, which are more conventionally configurable, to set options to specific tasks. Combining these with skip metadata provided by doitoml allows for customizing tasks when being run in different contexts.