Documentation is currently for moon v2 and latest proto. Documentation for moon v1 has been frozen and can be found here.
Execution plan
An execution plan is a JSON file that declaratively configures how moon exec
(and derived commands) runs tasks. Instead of passing many command line options, you can define your
execution settings in a file and pass it with a single flag — keeping your commands short and your
configuration version-controlled.
Usage
Pass an execution plan to moon exec with the --plan (or -p) flag:
$ moon exec --plan plan.json
Or set the MOON_EXEC_PLAN environment variable:
MOON_EXEC_PLAN=plan.json moon exec
The path can be relative (resolved against the working directory) or absolute. The file must be valid JSON.
When both a plan and CLI options are provided, plan values take priority. If targets are defined in both the plan and the command line, the plan's targets are used and a warning is logged.
Structure
A plan is a JSON object with four top-level blocks. All blocks are optional and default to safe values when omitted. Unknown fields are rejected.
{
"affected": {},
"graph": {},
"pipeline": {},
"targets": []
}
targets
Defines which tasks to run. Accepts the same target syntax as the moon exec command line
(project:task, :task, ~:task, #tag:task, globs, etc).
In its simplest form, targets is an array of target strings:
{
"targets": ["app:build", "lib:build"]
}
For advanced use cases, targets can be an object with one of two strategies:
- Filtered — Run only the targets that match the
includelist (excludingcoming soon):
{
"targets": {
"include": ["app:build", "#frontend:test"]
}
}
- Partitioned — Split targets into groups for parallel CI jobs. Each inner array is one partition:
{
"targets": {
"jobs": [
["app:build", "app:test"],
["lib:build", "lib:test"]
]
}
}
Use in combination with pipeline.job to select which partition to run.
pipeline
Controls how the pipeline executes tasks.
ci(boolean) - Force enable CI mode. Same as--ci.concurrency(number) - Maximum number of tasks to run in parallel.ignoreCiChecks(boolean) - Ignore "run in CI" task checks. Same as--ignore-ci-checks.noActions(boolean) - Skip sync and setup actions. Same as--no-actions.onFailure("bail" | "continue") - When a task fails, either bail the pipeline immediately or continue running other tasks. Same as--on-failure.job(number) - Index of the current job (0-based). Used with partitioned targets; errors if targets are not partitioned. Same as--job.jobTotal(number) - Total number of jobs. Used with partitioned targets. Same as--job-total.
graph
Controls how deeply the dependency graph is traversed when building the task graph.
downstream(none | direct | deep) - Depth of downstream dependents to include. Defaults tonone. Same as--downstream.upstream(none | direct | deep) - Depth of upstream dependencies to include. Defaults tonone. Same as--upstream.
affected
Restricts execution to tasks affected by changed files. When this block is omitted, affected filtering is disabled and all matched targets run.
base(string) - Base branch, commit, or revision to compare against. Same as--base.head(string) - Current branch, commit, or revision to compare with. Defaults toHEAD. Same as--head.includeRelations(boolean) - Include graph relations for affected checks, instead of just changed files. Same as--include-relations.source(string) - Source of affected files. Determines which git commands are used to detect changes. Acceptslocalorremote.status(string[]) - Filter changed files by status. Same as--status.stdin(boolean) - Accept changed files from stdin. Same as--stdin.
Examples
Run specific targets
The simplest plan — just list the targets to run:
{
"targets": ["app:build", "lib:build"]
}
CI with affected filtering
Only run tests affected by changes between the base branch and HEAD:
{
"affected": {
"base": "main",
"source": "remote"
},
"pipeline": {
"ci": true
},
"targets": [":test"]
}
Partitioned CI jobs
Split targets across 3 CI jobs. Each job runs with --plan plan.json and sets its own job index
via the CLI or an environment variable:
{
"pipeline": {
"ci": true,
"jobTotal": 3
},
"targets": {
"jobs": [["app:build", "app:test"], ["lib:build", "lib:test"], ["docs:build"]]
}
}
Then in CI, run each partition:
# Job 0
$ moon exec --plan plan.json --job 0
# Job 1
$ moon exec --plan plan.json --job 1
# Job 2
$ moon exec --plan plan.json --job 2
Continue on failure
Run all lint tasks, continuing past failures so you get the full list of issues:
{
"pipeline": {
"concurrency": 4,
"onFailure": "continue"
},
"targets": [":lint"]
}
Deep dependency graph
Build a target along with all of its upstream dependencies and downstream dependents:
{
"graph": {
"upstream": "deep",
"downstream": "deep"
},
"targets": ["core:build"]
}
Full example
Combine all blocks — run affected tests in CI across partitioned jobs, with full graph traversal:
{
"affected": {
"base": "main",
"head": "HEAD",
"includeRelations": true,
"source": "remote",
"status": ["modified", "added"]
},
"graph": {
"upstream": "deep",
"downstream": "direct"
},
"pipeline": {
"ci": true,
"concurrency": 8,
"jobTotal": 3,
"onFailure": "continue"
},
"targets": {
"jobs": [["app:test", "app:lint"], ["#frontend:test"], ["lib:test", "core:test"]]
}
}