Tasks are commands that are ran in the context of a project. Underneath the hood, a task is simply a binary or system command that is ran as a child process.
A task name (or identifier) is a unique resource for locating a task within a project. The name is
explicitly configured as a key within the
tasks setting, and can be
written in camel/kebab/snake case. Names support
., and must
start with a character.
A task name can be paired with a project name to create a target.
Tasks are categorized into 1 of the following types based on their configured parameters.
- Build - Task generates one or many artifacts, and is derived from the
- Run - Task runs a one-off, long-running, or never-ending process, and is derived from the
- Test - Task asserts code is correct and behaves as expected. This includes linting, typechecking, unit tests, and any other form of testing.
Tasks can be configured per project through
moon.yml, or for many projects
Unlike other task runners that require the same tasks to be repeatedly defined for every project, moon uses an inheritance model where tasks can be defined once at the workspace-level, and are then inherited by many or all projects.
Workspace-level tasks (also known as global tasks) are defined in
.moon/tasks/*.yml, and are inherited by default. However, projects are able to include,
exclude, or rename inherited tasks using the
Scope by project language and type
By default tasks defined in
.moon/tasks.yml will be inherited by all projects. This
approach works well when a monorepo is comprised of a single programming language, but breaks down
quickly in multi-language setups.
To support these complex repositories, we support scoped tasks with
*.yml maps to a project based on a combination of its language and type.
libraries", so on and so forth.
When resolving configuration files, moon will locate and shallow merge files in the following order, from widest scope to narrowest scope:
.moon/tasks.yml- All projects.
.moon/tasks/<language>.yml- Projects with a matching
.moon/tasks/<language>-<type>.yml- Projects with matching
As mentioned above, all of these files are shallow merged into a single "global tasks" configuration that is unique per-project. Merging does not utilize the merge strategies below, as those strategies are only utilized when merging global and local tasks.
TypeScript) based projects have a special lookup order using
platform to account for this:
node.yml would be inherited for Node.js projects,
bun-library.yml for Bun
deno-application.yml for Deno applications. While
typescript-library.yml, etc, will be inherited for all platforms.
When a global task and local task of the same name exist, they are merged into a single task. To accomplish this, one of many merge strategies can be used.
Merging is applied to the parameters
outputs, using the
mergeOutputs options respectively. Each of these options support
one of the following strategy values.
append(default) - Values found in the local task are merged after the values found in the global task. For example, this strategy is useful for toggling flag arguments.
prepend- Values found in the local task are merged before the values found in the global task. For example, this strategy is useful for applying option arguments that must come before positional arguments.
replace- Values found in the local task entirely replaces the values in the global task. This strategy is useful when you need full control.
All 3 of these strategies are demonstrated below, with a somewhat contrived example, but you get the point.
args: '--no-color --no-stats'
# Merged result