Skip to main content
When you run a task on the command line, moon generates an action graph to ensure dependencies of tasks have run before running the primary task. The action graph is a representation of all tasks and related operations, derived from the project graph and task graph, and is represented internally as a directed acyclic graph (DAG).

Actions

Unlike other task runners in the industry that represent each node in the graph as a task to run, moon represents each node in the graph as an action to perform. This allows for more flexibility and efficiency in task execution, and enables additional functionality and automation. The following actions compose the action graph:

Sync workspace

A common action that always runs first, giving moon a chance to perform operations and health checks across the entire workspace. This includes:
  • Validating workspace configuration
  • Checking for configuration changes
  • Initializing workspace-level state
  • Running workspace-level hooks
This action can be skipped by disabling the pipeline.syncWorkspace setting.

Setup proto

Runs before all toolchain-related actions and ensures that proto has been installed and is available for use. This is required for toolchains that will be downloaded and installed.

Setup toolchain

The most important action in the graph, which downloads and installs a tier 3 language into the toolchain. For other tiers, this is essentially a no-operation.
  • When the tool has already been installed, this action will be skipped
  • Actions are scoped by language and version (runtime). For example:
    • SetupToolchain(node:18.1.0)
    • SetupToolchain(deno:1.31.0)
  • Tools that require a global binary (found on PATH) will display the version as “global”. For example:
    • SetupToolchain(node:global)
This action can be skipped by setting the MOON_SKIP_SETUP_TOOLCHAIN=true environment variable. The skip can be scoped per tool by setting the value to the tool name (node), and also by version (node:20.0.0). Supports a comma-separated list.

Setup environment

Runs after the toolchain has been set up, but before dependencies are installed, allowing the development environment to be configured. This includes operations such as:
  • Modifying manifests (package.json, etc.)
  • Updating configuration files
  • Initializing virtual environments (Python venv’s)
  • Running environment-specific setup scripts

Install dependencies

Before running a task, moon ensures that all language dependencies (node_modules for example) have been installed. Dependencies are automatically installed if changes are detected since the last run. This is achieved by:
  • Comparing lockfile modified timestamps
  • Parsing manifest files
  • Hashing resolved dependency versions
Behavior:
  • When dependencies do not need to be installed, this action will be skipped
  • Depending on the language and configuration, dependencies may be installed:
    • Per-project: InstallProjectDeps(node:18.1.0, example)
    • Workspace-wide: InstallWorkspaceDeps(node:18.1.0)
  • Actions are scoped by language and version (runtime)
This action can be skipped by disabling the pipeline.installDependencies setting.

Sync project

To ensure a consistently healthy project and repository, moon runs a process known as syncing every time a task is run. This action runs sync operations for all toolchains associated with the project, which may include:
  • Syncing configuration files
  • Updating project manifests
  • Running project-level health checks
  • Applying project-specific toolchain configurations
This action can be skipped by disabling the pipeline.syncProject setting.

Run task

The primary action in the graph, which runs a project’s task as a child process, derived from a target. Tasks can depend on other tasks, and they are effectively orchestrated and executed by running in topological order using a thread pool. Key features:
  • Dependencies - Ensures all task dependencies complete before execution
  • Caching - Skips tasks if outputs are cached and inputs haven’t changed
  • Parallel execution - Runs independent tasks concurrently
  • Task states - Tracks success, failure, cached, or skipped states

Run interactive task

Like the base run task, but runs the task interactively with stdin capabilities. This is used for tasks that require user input or terminal interaction.
  • All interactive tasks are run in isolation in the graph
  • Only one interactive task runs at a time
  • stdin/stdout/stderr are directly connected to the terminal

Run persistent task

Like the base run task, but runs the task in a persistent process that never exits. This is used for long-running services like development servers.
  • All persistent tasks are run in parallel as the last batch in the graph
  • They continue running even after other tasks complete
  • Examples: dev servers, watch modes, background services

Graph construction

The action graph is built through a multi-step process:

1. Initialize builder

The action graph builder is initialized with:
  • App context - Global application state
  • Workspace graph - The complete project graph
  • Options - Configuration for which actions to include
pub struct ActionGraphBuilderOptions {
    pub install_dependencies: PipelineActionSwitch,
    pub setup_environment: PipelineActionSwitch,
    pub setup_toolchains: PipelineActionSwitch,
    pub sync_projects: PipelineActionSwitch,
    pub sync_project_dependencies: bool,
    pub sync_workspace: bool,
}

2. Add targets

Targets (task identifiers) are added to the graph. For each target:
  • Resolve the target to a specific project and task
  • Add the run task action node
  • Recursively add dependency tasks based on --upstream setting
  • Add dependent tasks if --downstream is specified

3. Add supporting actions

For each task added, the builder automatically adds supporting actions:
  • Setup toolchain (if needed for the task’s toolchain)
  • Install dependencies (if needed for the project)
  • Setup environment (if needed)
  • Sync project (if enabled)
These actions are connected with proper edges to ensure correct execution order.

4. Apply query filters

If a --query is specified, the graph is filtered to include only tasks matching the query criteria. See Query language for details.

5. Apply affected filtering

If --affected is enabled, the graph is filtered to include only tasks affected by changed files:
  • Determine changed files from VCS
  • Mark tasks as affected based on:
    • Changed input files
    • Changed environment variables
    • Affected dependencies (if --include-relations is set)
    • Affected dependents (if --include-relations is set)
  • Remove non-affected tasks from the graph

6. Build and optimize

Final optimizations are applied:
  • Transitive reduction - Remove redundant edges
  • Cycle detection - Ensure the graph is acyclic
  • Priority grouping - Group actions by priority level
  • Topological sorting - Determine execution order

Graph execution

Once built, the action graph is executed through the action pipeline:

1. Topological sort

The graph is sorted topologically to determine a valid execution order. This ensures dependencies always run before their dependents.

2. Priority grouping

Actions are grouped by priority level:
  • Priority 0 (Critical) - Workspace sync, proto setup
  • Priority 1 (High) - Toolchain setup
  • Priority 2 (Normal) - Dependency installation, environment setup
  • Priority 3 (Low) - Task execution

3. Parallel execution

Within each priority group, independent actions are executed in parallel using a thread pool. The number of concurrent actions is controlled by:
  • Available CPU cores
  • Configuration settings
  • System resource limits

4. State tracking

As actions execute, their states are tracked:
  • Pending - Waiting for dependencies
  • Running - Currently executing
  • Passed - Completed successfully
  • Failed - Failed with error
  • Cached - Skipped due to cache hit
  • Skipped - Skipped due to conditions

5. Failure handling

When an action fails:
  • Bail mode (--on-failure=bail) - Immediately stop execution
  • Continue mode (--on-failure=continue) - Continue executing independent actions

Visualization

You can visualize the action graph using:
# Generate DOT format
moon action-graph app:build

# Generate JSON format  
moon action-graph app:build --json
The visualization shows:
  • All actions as nodes
  • Dependencies as directed edges
  • Action types with different visual styles
  • Execution order and groupings

Benefits

The action graph provides several key benefits:

Correctness

  • Guarantees tasks run in the correct order
  • Ensures all dependencies are satisfied
  • Prevents race conditions and conflicts

Performance

  • Maximizes parallelism where possible
  • Minimizes redundant work through caching
  • Optimizes resource utilization

Flexibility

  • Supports various execution modes (interactive, persistent)
  • Allows fine-grained control via options
  • Enables advanced workflows like job partitioning

Observability

  • Tracks detailed execution state
  • Provides clear error messages
  • Enables visualization and debugging

Build docs developers (and LLMs) love