# moonrepo
> A developer productivity tooling platform.
This file contains all documentation content in a single document following the llmstxt.org standard.
## Cheat sheet
Don't have time to read the docs? Here's a quick cheat sheet to get you started.
## Tasks
Learn more about [tasks](./concepts/task) and [targets](./concepts/target).
#### Run all build and test tasks for all projects
```shell
moon check --all
```
#### Run all build and test tasks in a project
```shell
moon check project
```
#### Run a task in all projects
```shell
moon run :task
```
#### Run a task in all projects with a tag
```shell
moon run '#tag:task'
# OR
moon run \#tag:task
# OR
moon run :task --query "projectTag=tag"
```
#### Run a task in a project
```shell
moon run project:task
```
#### Run multiple tasks in all projects
```shell
moon run :task1 :task2
```
#### Run multiple tasks in any project
```shell
moon run projecta:task1 projectb:task2
```
#### Run a task in applications, libraries, or tools
```shell
moon run :task --query "projectLayer=application"
```
#### Run a task in projects of a specific language
```shell
moon run :task --query "language=typescript"
```
#### Run a task in projects matching a keyword
```shell
moon run :task --query "project~react-*"
```
#### Run a task in projects based on file path
```shell
moon run :task --query "projectSource~packages/*"
```
## Task configuration
Learn more about [available options](./config/project#tasks).
#### Disable caching
```yaml title="moon.yml"
tasks:
example:
# ...
options:
cache: false
```
#### Re-run flaky tasks
```yaml title="moon.yml"
tasks:
example:
# ...
options:
retryCount: 3
```
#### Depend on tasks from parent project's dependencies
```yaml title="moon.yml"
# Also inferred from the language
dependsOn:
- 'project-a'
- 'project-b'
tasks:
example:
# ...
deps:
- '^:build'
```
#### Depend on tasks from arbitrary projects
```yaml title="moon.yml"
tasks:
example:
# ...
deps:
- 'other-project:task'
```
#### Run dependencies serially
```yaml title="moon.yml"
tasks:
example:
# ...
deps:
- 'first'
- 'second'
- 'third'
options:
runDepsInParallel: false
```
#### Run multiple watchers/servers in parallel
```yaml title="moon.yml"
tasks:
example:
command: 'noop'
deps:
- 'app:watch'
- 'backend:start'
- 'tailwind:watch'
preset: 'server'
```
> The `persistent` setting is required for this to work.
## Languages
#### Run system binaries available on `PATH`
```yaml title="moon.yml"
language: 'bash' # batch, etc
tasks:
example:
command: 'printenv'
```
```yaml title="moon.yml"
tasks:
example:
command: 'printenv'
toolchain: 'system'
```
#### Run language binaries not supported in moon's toolchain
```yaml title="moon.yml"
language: 'ruby'
tasks:
example:
command: 'rubocop'
toolchain: 'system'
```
#### Run npm binaries (Node.js)
```yaml title="moon.yml"
language: 'javascript' # typescript
tasks:
example:
command: 'eslint'
```
```yaml title="moon.yml"
tasks:
example:
command: 'eslint'
toolchain: 'node'
```
---
## action-graph
The `moon action-graph [target]` (or `moon ag`) command will generate and serve a visual graph of
all actions and tasks within the workspace, known as the
[action graph](../how-it-works/action-graph). In other tools, this is sometimes referred to as a
dependency graph or task graph.
```shell
# Run the visualizer locally
$ moon action-graph
# Export to DOT format
$ moon action-graph --dot > graph.dot
```
> A target can be passed to focus the graph, including dependencies _and_ dependents. For example,
> `moon action-graph app:build`.
### Arguments
- `[target]` - Optional target to focus.
### Options
- `--dependents` - Include dependents of the focused target.
- `--dot` - Print the graph in DOT format.
- `--host` - The host address. Defaults to `127.0.0.1`.
- `--json` - Print the graph in JSON format.
- `--port` - The port to bind to. Defaults to a random port.
### Configuration
- [`pipeline`](../config/workspace#pipeline) in `.moon/workspace.*`
- [`tasks`](../config/tasks#tasks) in `.moon/tasks/*`
- [`tasks`](../config/project#tasks) in `moon.*`
## Example output
The following output is an example of the graph in DOT format.
```dot
digraph {
0 [ label="SetupToolchain(node)" style=filled, shape=oval, fillcolor=black, fontcolor=white]
1 [ label="InstallWorkspaceDeps(node)" style=filled, shape=oval, fillcolor=gray, fontcolor=black]
2 [ label="SyncProject(node, node)" style=filled, shape=oval, fillcolor=gray, fontcolor=black]
3 [ label="RunTask(node:standard)" style=filled, shape=oval, fillcolor=gray, fontcolor=black]
1 -> 0 [ arrowhead=box, arrowtail=box]
2 -> 0 [ arrowhead=box, arrowtail=box]
3 -> 1 [ arrowhead=box, arrowtail=box]
3 -> 2 [ arrowhead=box, arrowtail=box]
}
```
---
## bin
The `moon bin ` command will return an absolute path to a tool's binary within the
toolchain. If a tool has not been configured or installed, this will return a 1 or 2 exit code with
no value respectively.
```shell
$ moon bin node
/Users/example/.proto/tools/node/x.x.x/bin/node
```
> A tool is considered "not configured" when not in use, for example, querying yarn/pnpm when the
> package manager is configured for "npm". A tool is considered "not installed", when it has not
> been downloaded and installed into the tools directory.
### Arguments
- `` - Name of the toolchain to query.
---
## check
The `moon check [...projects]` (or `moon c`) command will run _all_
[build and test tasks](../concepts/task#types) for one or many projects. This is a convenience
command for verifying the current state of a project, instead of running multiple
[`moon run`](./run) commands.
```shell
# Check project by name
$ moon check app
# Check multiple projects by name
$ moon check client server
# Check closest project from current working directory
$ moon check --closest
# Check ALL projects (may be costly)
$ moon check --all
```
### Arguments
- `[...id]` - List of project IDs or aliases to explicitly check, as defined in
[`projects`](../config/workspace#projects).
### Options
Inherits all options from [`moon exec`](./exec), and pre-fills with: `--on-failure=bail`,
`--upstream=deep`.
- `--all` - Run check for all projects in the workspace.
- `--closest` - Run check for the closest project starting from the current working directory.
### Configuration
- [`projects`](../config/workspace#projects) in `.moon/workspace.*`
- [`tasks`](../config/tasks#tasks) in `.moon/tasks/*`
- [`tasks`](../config/project#tasks) in `moon.*`
---
## ci
The `moon ci` command is a special command that should be ran in a continuous integration (CI)
environment, as it does all the heavy lifting necessary for effectively running tasks.
By default this will run all tasks that are affected by changed files and have the
[`runInCI`](../config/project#runinci) task option enabled.
```shell
$ moon ci
```
However, you can also provide a list of targets to explicitly run, which will still be filtered down
by `runInCI`.
```shell
$ moon ci :build :lint
```
:::info
View the official [continuous integration guide](../guides/ci) for a more in-depth example of how to
utilize this command.
:::
### Reports
After execution, moon writes the generated report to `.moon/cache/ciReport.json`. The non-CI task
execution commands write the same report format to `.moon/cache/runReport.json`.
These reports live in `.moon/cache`, so they may be overwritten or deleted between runs. Copy the
report elsewhere if you need to persist it, like uploading it as a CI artifact.
### Arguments
- `...[target]` - [Task targets](../concepts/target) to run.
### Options
Inherits most options from [`moon exec`](./exec) and pre-fills with: `--affected`, `--ci`,
`--on-failure=continue`, `--summary=detailed`, `--upstream=deep`, `--downstream=direct`.
### Configuration
- [`tasks`](../config/tasks#tasks) in `.moon/tasks/*`
- [`tasks`](../config/project#tasks) in `moon.*`
- [`tasks.*.options.runInCI`](../config/project#runinci) in `moon.*`
---
## clean
The `moon clean` command will clean the current workspace by deleting stale cache. For the most
part, the action pipeline will clean automatically, but this command can be used to reset the
workspace entirely.
```shell
$ moon clean
# Delete cache with a custom lifetime
$ moon clean --lifetime '24 hours'
```
### Options
- `--all` - Clean all cached items and reset state.
- `--lifetime` - The maximum lifetime of cached artifacts before being marked as stale. Defaults to
"7 days".
---
## completions
The `moon completions` command will generate moon command and argument completions for your current
shell. This command will write to stdout, which can then be redirected to a file of your choice.
```shell
$ moon completions > ./path/to/write/to
```
### Options
- `--shell` - Shell to explicitly generate for.
### Examples
If using [bash-completion](https://github.com/scop/bash-completion).
```shell
mkdir -p ~/.bash_completion.d
moon completions > ~/.bash_completion.d/moon.sh
```
Otherwise write the file to a common location, and source it in your profile.
```shell
mkdir -p ~/.bash_completions
moon completions > ~/.bash_completions/moon.sh
# In your profile
source ~/.bash_completions/moon.sh
```
Write the file to Fish's completions directory.
```shell
mkdir -p ~/.config/fish/completions
moon completions > ~/.config/fish/completions/moon.fish
```
If using [oh-my-zsh](https://ohmyz.sh/) (the `_` prefix is required).
```shell
mkdir -p ~/.oh-my-zsh/completions
moon completions > ~/.oh-my-zsh/completions/_moon
# Reload shell (or restart terminal)
omz reload
```
---
## daemon logs
The `moon daemon logs` command tails the daemon's log file in real time, streaming new entries as
they are written. The daemon must be running for this command to work.
```shell
$ moon daemon logs
```
The log file is located at `.moon/cache/daemon/server.log` and contains detailed trace-level output,
including file watcher events, graph rebuilds, and RPC requests.
:::info
On macOS/Linux this command uses `tail -f` under the hood. On Windows it uses PowerShell's
`Get-Content -Wait`.
:::
If the daemon is not running or the log file does not exist, a warning is displayed and no action is
taken.
---
## daemon restart
The `moon daemon restart` command will stop the currently running daemon process and start a new
one. This is useful after manual configuration changes, when the cached state seems stale, or after
upgrading moon to a new version.
```shell
$ moon daemon restart
```
:::caution
The daemon must be enabled in your [workspace configuration](../../config/workspace) before it can
be restarted. If the `daemon` setting is not enabled, this command will display a warning and exit.
:::
:::info
View the official [daemon guide](../../guides/daemon) for more information on how the daemon works.
:::
---
## daemon start
The `moon daemon start` command will start the daemon background process if it's not already
running. If a daemon is already running for this workspace, the existing process is reused.
```shell
$ moon daemon start
```
:::info
You typically don't need to run this command manually. When the daemon is
[enabled](../../guides/daemon#enabling-the-daemon), it starts automatically with any `moon` command.
:::
:::caution
The daemon must be enabled in your [workspace configuration](../../config/workspace) before it can
be started. If the `daemon` setting is not enabled, this command will display a warning and exit.
:::
---
## daemon status
The `moon daemon status` command displays information about the running daemon process.
```shell
$ moon daemon status
```
When the daemon is running, the following information is displayed:
- **PID** — The process ID of the daemon.
- **Socket** (macOS/Linux) or **Named pipe** (Windows) — The IPC endpoint used for communication.
- **Uptime** — How long the daemon has been running.
- **PID file** — Path to the PID file (`.moon/cache/daemon/moond.pid`).
- **Log file** — Path to the log file (`.moon/cache/daemon/server.log`).
If the daemon is not running, a warning is displayed instead.
:::info
View the official [daemon guide](../../guides/daemon) for more information on how the daemon works.
:::
---
## daemon stop
The `moon daemon stop` command will stop the running daemon process. It first attempts a graceful
shutdown, and if the daemon does not exit within a few seconds, it will be forcefully killed. Daemon
files (PID, socket) are cleaned up automatically.
```shell
$ moon daemon stop
```
If the daemon is not running, a warning is displayed and no action is taken.
:::info
View the official [daemon guide](../../guides/daemon) for more information on how the daemon works.
:::
---
## docker file
The `moon docker file ` command can be used to generate a multi-staged `Dockerfile` for a
project, that takes full advantage of Docker's layer caching, and is primarily for production
deploys (this should not be used for development).
```shell
$ moon docker file
```
As mentioned above, the generated `Dockerfile` uses a multi-stage approach, where each stage is
broken up into the following:
- `base` - The base stage, which simply installs moon for a chosen Docker image. This stage requires
Bash.
- `skeleton` - Scaffolds workspace and sources repository skeletons using
[`moon docker scaffold`](./scaffold).
- `build` - Copies required sources, installs the toolchain using [`moon docker setup`](./setup),
optionally builds the project, and optionally prunes the image using
[`moon docker prune`](./prune).
- `start` - Runs the project after it has been built. This is typically starting an HTTP server, or
executing a binary.
:::info
View the official [Docker usage guide](../../guides/docker) for a more in-depth example of how to
utilize this command.
:::
### Arguments
- `` - Name or alias of a project, as defined in [`projects`](../../config/workspace#projects).
- `[dest]` - Destination to write the file, relative from the project root. Defaults to
`Dockerfile`.
### Options
- `--defaults` - Use default options instead of prompting in the terminal.
- `--build-task` - Name of a task to build the project. Defaults to the
[`docker.file.buildTask`](../../config/project#buildtask) setting, or prompts in the terminal.
- `--image` - Base Docker image to use. Defaults to an image derived from the toolchain, or prompts
in the terminal.
- `--no-prune` - Do not prune dependencies in the build stage.
- `--no-setup` - Do not setup dependencies in the build stage.
- `--no-toolchain` - Do not use the toolchain and instead use system binaries.
- `--start-task` - Name of a task to start the project. Defaults to the
[`docker.file.startTask`](../../config/project#starttask) setting, or prompts in the terminal.
- `--template` - Template path, relative from the workspace root, to render the Dockerfile with.
### Configuration
- [`docker.file`](../../config/project#file) in `moon.*`
---
## docker prune
The `moon docker prune` command will reduce the overall filesize of the Docker environment by
installing production only dependencies for projects that were scaffolded, and removing any
applicable extraneous files.
```shell
$ moon docker prune
```
:::info
View the official [Docker usage guide](../../guides/docker) for a more in-depth example of how to
utilize this command.
:::
:::caution
This command _must be_ ran after [`moon docker scaffold`](./scaffold) and is typically ran within a
`Dockerfile`! The [`moon docker file`](./file) command can be used to generate a `Dockerfile`.
:::
### Configuration
- [`docker.prune`](../../config/workspace#prune) in `.moon/workspace.*`
---
## docker scaffold
The `moon docker scaffold <...projects>` command creates multiple repository skeletons for use
within `Dockerfile`s, to effectively take advantage of Docker's layer caching. It utilizes the
[project graph][graph] to copy only critical files, like manifests, lockfiles, and configuration.
```shell
# Scaffold a skeleton to .moon/docker
$ moon docker scaffold
```
:::info
View the official [Docker usage guide](../../guides/docker) for a more in-depth example of how to
utilize this command.
:::
### Arguments
- `<...ids>` - List of project IDs or aliases to scaffold sources for, as defined in
[`projects`][graph].
### Configuration
- [`docker.scaffold`](../../config/workspace#scaffold) in `.moon/workspace.*` (entire workspace)
- [`docker.scaffold`](../../config/project#scaffold) in `moon.*` (per project)
## How it works
This command may seem like magic, but it's relative simple thanks to moon's infrastructure and its
project graph. When the command is ran, we generate 2 skeleton structures in `.moon/docker` (be sure
to gitignore this). One for configs, and the other for sources.
:::warning
Because scaffolding uses the project graph, it requires all projects to be [configured in
moon][graph]. Otherwise, moon will fail to copy all required files and builds may fail.
:::
### Configs
The configs skeleton mirrors the project folder structure of the repository 1:1, and only copies
files required for dependencies to install. This is typically manifests (`package.json`), lockfiles
(`yarn.lock`, etc), other critical configs, and `.moon` itself. This is necessary for package
managers to install dependencies (otherwise they will fail), and for dependencies to be layer cached
in Docker.
An example of this skeleton using Yarn may look like the following:
```
.moon/docker/configs/
├── .moon/
├── .yarn/
├── apps/
│ ├── client/
│ │ └── package.json
│ └── server/
│ └── package.json
├── packages/
│ ├── foo/
│ │ └── package.json
│ ├── bar/
│ │ └── package.json
│ └── baz/
│ └── package.json
├── .yarnrc.yml
├── package.json
└── yarn.lock
```
### Sources
The sources skeleton is not a 1:1 mirror of the repository, and instead is the source files of a
project (passed as an argument to the command), and all of its dependencies. This allows
[`moon run`](../run) and other commands to work within the `Dockerfile`, and avoid having to
`COPY . .` the entire repository.
Using our example workspace above, our sources skeleton would look like the following, assuming our
`client` project is passed as an argument, and this project depends on the `foo` and `baz` projects.
```
.moon/docker/sources/
├── apps/
│ └── client/
| ├── src/
| ├── tests/
| ├── public/
| ├── package.json
| ├── tsconfig.json
│ └── (anything else)
└── packages/
├── foo/
│ ├── lib/
│ ├── src/
│ ├── package.json
│ ├── tsconfig.json
│ └── (anything else)
└── baz/
├── lib/
├── src/
├── package.json
├── tsconfig.json
└── (anything else)
```
[graph]: ../../config/workspace#projects
---
## docker setup
The `moon docker setup` command will efficiently install dependencies for focused projects. This is
an all-in-one command for toolchain and dependency installations, and should replace `npm install`
and other commands.
```shell
$ moon docker setup
```
:::info
View the official [Docker usage guide](../../guides/docker) for a more in-depth example of how to
utilize this command.
:::
:::caution
This command _must be_ ran after [`moon docker scaffold`](./scaffold) and is typically ran within a
`Dockerfile`! The [`moon docker file`](./file) command can be used to generate a `Dockerfile`.
:::
### Configuration
- [`*`](../../config/toolchain) in `.moon/toolchains.*`
---
## exec
The `moon exec` (or `moon x`, or `moonx`) command is a low-level command for executing tasks in the
action pipeline. It provides fine-grained control over how tasks are selected and executed, through
command line options, making it ideal for custom workflows and advanced use cases.
The [`moon check`](./check), [`moon ci`](./ci), and [`moon run`](./run) commands are all built on
top of `moon exec`, so be sure to check those out for more user-friendly abstractions!
```shell
# Run `lint` in project `app`
$ moon exec app:lint
$ moonx app:lint
# Run `dev` in project `client` and `server`
$ moon exec client:dev server:dev
$ moonx client:dev server:dev
# Run `test` in all projects
$ moon exec :test
$ moonx :test
# Run `test` in the closest project, relative to the current working directory
$ moon exec '~:test'
$ moonx '~:test'
# Run `test` in all projects with tag `frontend`
$ moon exec '#frontend:test'
$ moonx '#frontend:test'
# Run `format` in the default project
$ moon exec format
$ moonx format
# Run `build` in projects matching the query
$ moon exec :build --query "language=javascript && projectLayer=library"
```
:::info
For a declarative alternative to CLI options, see the [execution plan guide](../guides/exec-plan).
:::
## Arguments
- `...` - [Task targets](../concepts/target) or project relative tasks to run.
- `[-- ]` - Additional arguments to
[pass to the underlying command](../run-task#passing-arguments-to-the-underlying-command).
## Options
- `--ci` - Force enable CI mode.
- `-f`, `--force` - Force run and bypass cache, ignore changed files, and skip affected checks.
- `-i`, `--interactive` - Run the pipeline and tasks interactively.
- `-p`, `--plan ` - Path to an execution plan JSON file. See the
[execution plan guide](../guides/exec-plan) for more details.
- `-s`, `--summary [LEVEL]` - Print a summary of all actions that were ran in the pipeline.
### Workflow
- `--ignore-ci-checks` - Ignore "run in CI" task checks.
- `--on-failure ` - When a task fails, either bail the pipeline, or continue executing.
- `--query ` - Filter tasks based on the result of a query.
- `--no-actions` - Run the pipeline without sync and setup related actions.
### Affected
- `--affected [BY]` - Only run tasks if affected by changed files. Optionally accepts "local" or
"remote".
- `--base ` - Base branch, commit, or revision to compare against.
- `--head ` - Current branch, commit, or revision to compare with.
- `-g`, `--include-relations` - Include graph relations for affected checks, instead of just changed
files.
- `--status ` - Filter changed files based on a changed status.
- `--stdin` - Accept changed files from stdin for affected checks.
### Graph
- `--downstream `, `--dependents ` - Control the depth of downstream dependents.
Supports "none" (default), "direct", "deep".
- `--upstream `, `--dependencies ` - Control the depth of upstream dependencies.
Supports "none", "direct", "deep" (default).
### Parallelism
- `--job ` - Index of the current job (0 based).
- `--job-total ` - Total amount of jobs to run.
---
## ext
The `moon ext ` command will execute an extension (a WASM plugin) that has been configured in
[`.moon/extensions.*`](../config). View our official [extensions guide](../guides/extensions) for
more information.
```shell
$ moon ext download -- --url https://github.com/moonrepo/moon/archive/refs/tags/v1.19.3.zip
```
Extensions typically support command line arguments, which _must_ be passed after a `--` separator
(as seen above). Any arguments before the separator will be passed to the `moon ext` command itself.
:::caution
This command requires an internet connection if the extension's `.wasm` file must be downloaded from
a URL, and it hasn't been cached locally.
:::
### Arguments
- `` - Name of the extension to execute.
- `[-- ]` - Arguments to pass to the extension.
### Configuration
- [`*`](../config/workspace#extensions) in `.moon/extensions.*`
---
## extension add
The `moon extension add [plugin]` command will add a extension to the workspace by injecting a
configuration block into `.moon/extensions.*`. To do this, the command will download the WASM
plugin, extract information, and call initialize functions.
For built-in extensions, the [plugin locator][locator] argument is optional, and will be derived
from the identifier.
```shell
$ moon extension add download
```
For third-party extensions, the [plugin locator][locator] argument is required, and must point to
the WASM plugin.
```shell
$ moon extension add custom https://example.com/path/to/plugin.wasm
```
### Arguments
- `` - ID of the extension to use.
- `[plugin]` - Optional [plugin locator][locator] for third-party extensions.
### Options
- `--minimal` - Generate minimal configurations and sane defaults.
- `--yes` - Skip all prompts and enables tools based on file detection.
[locator]: ../../guides/wasm-plugins#configuring-plugin-locations
---
## extension info
The `moon extension info [plugin]` command will display detailed information about a extension.
To do this, the command will download the WASM plugin, extract information, and call specific
functions.
For built-in extensions, the [plugin locator][locator] argument is optional, and will be derived
from the identifier.
```shell
$ moon extension info download
```
For third-party extensions, the [plugin locator][locator] argument is required, and must point to
the WASM plugin.
```shell
$ moon extension info custom https://example.com/path/to/plugin.wasm
```
### Arguments
- `` - ID of the extension to view.
- `[plugin]` - Optional [plugin locator][locator] for third-party extensions.
## Example output
```
Extension ─────────────────────────────────────────────────────────────────
Download a file from a URL into the current working directory.
ID: download
Title: Download
Version: 1.0.0
APIs ──────────────────────────────────────────────────────────────────────
⚫️ define_extension_config
🟢 execute_extension
⚫️ extend_command
⚫️ extend_project_graph
⚫️ extend_task_command
⚫️ extend_task_script
⚫️ initialize_extension
🟢 register_extension (required)
⚫️ sync_project
⚫️ sync_workspace
```
---
## generate
The `moon generate ` (or `moon g`) command will generate code (files and folders) from a
pre-defined template of the same name, using an interactive series of prompts. Templates are located
based on the [`generator.templates`](../config/workspace#templates) setting.
```shell
# Generate code from a template
$ moon generate npm-package
# Generate code from a template to a target directory
$ moon generate npm-package --to ./packages/example
# Generate code while declaring custom variable values
$ moon generate npm-package --to ./packages/example -- --name "@company/example"
# Create a new template
$ moon generate react-app --template
```
> View the official [code generation guide](../guides/codegen) for a more in-depth example of how to
> utilize this command.
### Arguments
- `` - ID of the template to generate.
- `[-- ]` - Additional arguments to override default variable values.
### Options
- `--defaults` - Use the default value of all variables instead of prompting the user.
- `--dry-run` - Run entire generator process without writing files.
- `--force` - Force overwrite any existing files at the destination.
- `--template` - Create a new template with the provided name.
- `--to` - Destination to write files to, relative from the current working directory. If not
defined, will be prompted during generation.
### Configuration
- [`generator`](../config/workspace#generator) in `.moon/workspace.*`
---
## hash
Use the `moon hash` command to inspect the contents and sources of a generated hash, also known as
the hash manifest. This is extremely useful in debugging task inputs.
```shell
$ moon hash 0b55b234f1018581c45b00241d7340dc648c63e639fbafdaf85a4cd7e718fdde
# Query hash using short form
$ moon hash 0b55b234
```
By default, this will output the contents of the hash manifest (which is JSON), and the fully
qualified resolved hash.
```json
Hash: 0b55b234f1018581c45b00241d7340dc648c63e639fbafdaf85a4cd7e718fdde
{
"command": "build",
"args": ["./build"]
// ...
}
```
The command can also be output raw JSON by passing the `--json` flag.
### Comparing hashes
The command can also be used to compare two hashes by diffing their contents. Simply pass two hashes
as arguments.
```shell
# Diff between 2 hashes
$ moon hash 0b55b234f1018581c45b00241d7340dc648c63e639fbafdaf85a4cd7e718fdde 2388552fee5a02062d0ef402bdc7232f0a447458b058c80ce9c3d0d4d7cfe171
# Diff between 2 hashes using short form
$ moon hash 0b55b234 2388552f
```
By default, this will output the contents of a hash file (which is JSON), highlighting the
differences between the left and right hashes. Lines that match will be printed in white, while the
left differences printed in green, and right differences printed in red. If you use `git diff`, this
will feel familiar to you.
```diff
Left: 0b55b234f1018581c45b00241d7340dc648c63e639fbafdaf85a4cd7e718fdde
Right: 2388552fee5a02062d0ef402bdc7232f0a447458b058c80ce9c3d0d4d7cfe171
{
"command": "build",
"args": [
+ "./dist"
- "./build"
],
...
}
```
The differences can also be output in JSON by passing the `--json` flag. The output has the
following structure:
```ts
{
left: string,
left_hash: string,
left_diffs: string[],
right: string,
right_hash: string,
right_diffs: string[],
}
```
### Options
- `--json` - Display the diff in JSON format.
### Configuration
- [`hasher`](../config/workspace#hasher) in `.moon/workspace.*`
---
## init
The `moon init` command will initialize moon into a repository and scaffold necessary config files
by creating a `.moon` folder.
```shell
$ moon init
# In another directory
$ moon init ./app
```
### Arguments
- `[dest]` - Destination to initialize and scaffold into. Defaults to `.` (current working
directory).
### Options
- `--force` - Overwrite existing config files if they exist.
- `--minimal` - Generate minimal configurations and sane defaults.
- `--yes` - Skip all prompts and enables tools based on file detection.
---
## mcp
The `moon mcp` command will start an [MCP](https://modelcontextprotocol.io) server that listens for
requests from AI assistants. This allows for agentic workflows in your favorite editor.
```shell
$ moon mcp
```
:::info
This command should not be ran manually and instead should be integrated into your editor. View the
[MCP guide](../guides/mcp) for more information.
:::
---
## Overview
The following options are available for _all_ moon commands.
- `--cache ` - The mode for [cache operations](#caching).
- `--color` - Force [colored output](#colors) for moon (not tasks).
- `--concurrency`, `-c` - Maximum number of threads to utilize.
- `--dump` - Dump a [trace profile](#profiling) to the working directory.
- `--help` - Display the help menu for the current command.
- `--log ` - The lowest [log level to output](#logging).
- `--log-file ` - Write logs to the defined file.
- `--quiet`, `-q` - Hide all non-important moon specific terminal output.
- `--theme` - Terminal theme to write output in.
- `--version` - Display the version of the CLI.
## Caching
We provide a powerful [caching layer](../concepts/cache), but sometimes you need to debug failing or
broken tasks, and this cache may get in the way. To circumvent this, we support the `--cache` global
option, or the `MOON_CACHE` environment variable, both of which accept one of the following values.
- `off` - Turn off caching entirely. Every task will run fresh, including dependency installs.
- `read` - Read existing items from the cache, but do not write to them.
- `read-write` (default) - Read and write items to the cache.
- `write` - Do not read existing cache items, but write new items to the cache.
```shell
$ moon run app:build --cache off
# Or
$ MOON_CACHE=off moon run app:build
```
## Colors
Colored output is a complicated subject, with differing implementations and standards across tooling
and operating systems. moon aims to normalize this as much as possible, by doing the following:
- By default, moon colors are inherited from your terminal settings (`TERM` and `COLORTERM`
environment variables).
- Colors can be force enabled by passing the `--color` option (preferred), or `MOON_COLOR` or
`FORCE_COLOR` environment variables.
```shell
$ moon app:build --color run
# Or
$ MOON_COLOR=2 moon run app:build
```
When forcing colors with `MOON_COLOR` or `FORCE_COLOR`, you may set it to one of the following
numerical values for the desired level of color support. This is automatically inferred if you use
`--color`.
- `0` - No colors
- `1` - 16 colors (standard terminal colors)
- `2` - 256 colors
- `3` - 16 million colors (truecolor)
### Themes
By default, moon assumes a dark themed terminal is being used, and will output colors accordingly.
However, if you use a light theme, these colors are hard to read. To mitigate this, we support
changing the theme with the `--theme` global option, or the `MOON_THEME` environment variable.
```shell
$ moon run app:build --theme light
# Or
$ MOON_THEME=light moon run app:build
```
### Piped output
When tasks (child processes) are piped, colors and ANSI escape sequences are lost, since the target
is not a TTY and we do not implement a PTY. This is a common pattern this is quite annoying.
However, many tools and CLIs support a `--color` option to work around this limitation and to always
force colors, even when not a TTY.
To mitigate this problem as a whole, and to avoid requiring `--color` for every task, moon supports
the [`pipeline.inheritColorsForPipedTasks`](../config/workspace#inheritcolorsforpipedtasks)
configuration setting. When enabled, all piped child processes will inherit the color settings of
the currently running terminal.
## Concurrency
The `--concurrency` option or `MOON_CONCURRENCY` environment variable can be used to control the
maximum amount of threads to utilize in our thread pool. If not defined, defaults to the number of
operating system cores.
```shell
$ moon run app:build --concurrency 1
# Or
$ MOON_CONCURRENCY=1 moon run app:build
```
## Debugging
At minimum, most debugging can be done by passing [`--log debug`](#logging) on the command line and
sifting through the logs. We also provide the following environment variables to toggle output.
:::info
For a complete list of environment variables that moon sets and reads, see the
[environment variables](../env-vars) reference.
:::
- `MOON_DEBUG_PROCESS_ENV` - By default moon hides the environment variables (except for `MOON_`)
passed to processes to avoid leaking sensitive information. However, knowing what environment
variables are passed around is helpful in debugging. Declare this variable to reveal the entire
environment.
- `MOON_DEBUG_PROCESS_INPUT` - By default moon truncates the stdin passed to processes to avoid
thrashing the console with a large input string. However, knowing what input is passed around is
helpful in debugging. Declare this variable to reveal the entire input.
- `MOON_DEBUG_PROTO_INSTALL` - Debug the proto installation process.
- `MOON_DEBUG_REMOTE` - Debug our remote caching implementation by including additional logging
output, and printing internal connection errors.
- `MOON_DEBUG_WASM` - Debug our WASM plugins by including additional logging output, and optionally
dumping memory/core profiles.
## Logging
By default, moon aims to output as little as possible, as we want to preserve the original output of
the command's being ran, excluding warnings and errors. This is managed through log levels, which
can be defined with the `--log` global option, or the `MOON_LOG` environment variable. The following
levels are supported, in priority order.
- `off` - Turn off logging entirely.
- `error` - Only show error logs.
- `warn` - Only show warning logs and above.
- `info` (default) - Only show info logs and above.
- `debug` - Only show debug logs and above. Recommended for most debugging, as it includes the
majority of useful diagnostic information.
- `trace` - Show all logs, including network requests and child processes. As of v2.4 this is _very_
verbose and is primarily intended for agents and deep diagnostics, and may be too spammy for
normal debugging.
- `verbose` - Like `trace` but also includes span information.
```shell
$ moon run app:build --log trace
# Or
$ MOON_LOG=trace moon run app:build
```
### Writing logs to a file
moon can dump the logs from a command to a file using the `--logFile` option, or the `MOON_LOG_FILE`
environment variable. The dumped logs will respect the `--log` option and filter the logs piped to
the output file.
```shell
$ moon run app:build --logFile=output.log
# Or
$ MOON_LOG_FILE=output.log moon run app:build
```
## Profiling
When the `--dump` option or `MOON_DUMP` environment variable is set, moon will generate a trace
profile and dump it to the current working directory. This profile can be opened with Chrome (via
`chrome://tracing`) or [Perfetto](https://ui.perfetto.dev/).
This profile will display many of the operations within moon as a flame chart, allowing you to
inspect and debug slow operations.
---
## project-graph
The `moon project-graph [id]` (or `moon pg`) command will generate and serve a visual graph of all
configured projects as nodes, with dependencies between as edges, and can also output the graph in
[Graphviz DOT format](https://graphviz.org/doc/info/lang.html).
```shell
# Run the visualizer locally
$ moon project-graph
# Export to DOT format
$ moon project-graph --dot > graph.dot
# Focus a specific project
$ moon project-graph app
```
### Arguments
- `[id]` - Optional ID or alias of a project to focus, as defined in
[`projects`](../config/workspace#projects).
### Options
- `--dependents` - Include direct dependents of the focused project.
- `--dot` - Print the graph in DOT format.
- `--host` - The host address. Defaults to `127.0.0.1`.
- `--json` - Print the graph in JSON format.
- `--port` - The port to bind to. Defaults to a random port.
### Configuration
- [`projects`](../config/workspace#projects) in `.moon/workspace.*`
## Example output
The following output is an example of the graph in DOT format.
```dot
digraph {
0 [ label="(workspace)" style=filled, shape=circle, fillcolor=black, fontcolor=white]
1 [ label="runtime" style=filled, shape=circle, fillcolor=gray, fontcolor=black]
2 [ label="website" style=filled, shape=circle, fillcolor=gray, fontcolor=black]
0 -> 1 [ arrowhead=none]
0 -> 2 [ arrowhead=none]
}
```
---
## project
The `moon project [id]` (or `moon p`) command will display all available information about a project
that has been configured and exists within the graph. If a project does not exist, the program will
return with a 1 exit code.
```shell
$ moon project web
```
### Arguments
- `[id]` - ID or alias of a project, as defined in [`projects`](../config/workspace#projects).
### Options
- `--json` - Print the project and its configuration as JSON.
- `--no-tasks` - Do not list tasks for the project.
## Example output
The following output is an example of what this command prints, using our very own
`@moonrepo/runtime` package.
```
RUNTIME
Project: runtime
Alias: @moonrepo/runtime
Source: packages/runtime
Root: ~/Projects/moon/packages/runtime
Toolchain: node
Language: typescript
Stack: unknown
Type: library
DEPENDS ON
- types (implicit, production)
INHERITS FROM
- .moon/tasks/node.yml
TASKS
build:
› packemon build --addFiles --addExports --declaration
format:
› prettier --check --config ../../prettier.config.js --ignore-path ../../.prettierignore --no-error-on-unmatched-pattern .
lint:
› eslint --cache --cache-location ./.eslintcache --color --ext .js,.ts,.tsx --ignore-path ../../.eslintignore --exit-on-fatal-error --no-error-on-unmatched-pattern --report-unused-disable-directives .
lint-fix:
› eslint --cache --cache-location ./.eslintcache --color --ext .js,.ts,.tsx --ignore-path ../../.eslintignore --exit-on-fatal-error --no-error-on-unmatched-pattern --report-unused-disable-directives . --fix
test:
› jest --cache --color --preset jest-preset-moon --passWithNoTests
typecheck:
› tsc --build
FILE GROUPS
configs:
- packages/runtime/*.{js,json}
sources:
- packages/runtime/src/**/*
- packages/runtime/types/**/*
tests:
- packages/runtime/tests/**/*
```
### Configuration
- [`projects`](../config/workspace#projects) in `.moon/workspace.*`
- [`project`](../config/project#project) in `moon.*`
---
## projects
The `moon projects` command will list all projects configured in the workspace as a table of
information.
```shell
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│Project Source Stack Layer Toolchains Description │
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
│types packages/types frontend library javascript, node, typescript, yarn │
│website website frontend application javascript, node, typescript, yarn A static website powered by Docusaurus. │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
```
:::info
Use [`moon query projects`](./query/projects) for advanced querying and filtering of projects.
:::
### Options
- `--json` - Print the projects as JSON.
---
## query affected
Use the `moon query affected` sub-command to query for all affected projects and tasks based on the
state of the workspace and VCS.
```shell
# Return affected
$ moon query affected
# Return affected including dependency relationships
$ moon query affected --upstream deep
```
This will output a map of projects and tasks as JSON. The output has the following structure:
```ts
{
projects: Record,
tasks: Record,
}
```
### Options
- `--downstream` - Include downstream dependents. Supports "none" (default), "direct", "deep".
- `--upstream` - Include upstream dependencies. Supports "none" (default), "direct", "deep".
---
## query changed-files
Use the `moon query changed-files` sub-command to query for a list of changed files (added,
modified, deleted, etc) using the current VCS state. These are the same queries that
[`moon exec`](../exec) use under the hood.
Changed files are determined using the following logic:
- If `--defaultBranch` is provided, and the current branch is the
[`vcs.defaultBranch`](../../config/workspace#defaultbranch), then compare against the previous
revision of the default branch (`HEAD~1`). This is what [continuous integration](../../guides/ci)
uses.
- If `--local` is provided, changed files are based on your local index only (`git status`).
- Otherwise, then compare the defined base (`--base`) against head (`--head`).
- When no explicit `--head` is provided, the comparison targets your working tree, so changed files
in your local index (`git status`) are also included. An explicit `--head` is a pure revision to
revision comparison, and does not include the local index.
```shell
# Return all files
$ moon query changed-files
# Return deleted files
$ moon query changed-files --status deleted
# Return all files between 2 revisions
$ moon query changed-files --base --head
```
This will output a list of workspace relative files as JSON. The output has the following structure:
```ts
{
files: string[],
options: QueryOptions,
}
```
### Options
- `--default-branch` - When on the default branch, compare against the previous revision.
- `--base ` - Base branch, commit, or revision to compare against. Defaults to
[`vcs.defaultBranch`](../../config/workspace#defaultbranch).
- `--head ` - Current branch, commit, or revision to compare with. Defaults to `HEAD`.
- `--local` - Gather files from the local state instead of remote.
- `--remote` - Gather files from the remote state instead of local.
- `--status ` - Filter files based on a changed status. Can be passed multiple times.
- Types: `all` (default), `added`, `deleted`, `modified`, `staged`, `unstaged`, `untracked`
### Configuration
- [`vcs`](../../config/workspace#vcs) in `.moon/workspace.*`
---
## query projects
Use the `moon query projects` sub-command to query information about all projects in the project
graph. The project list can be filtered by passing a [query statement](../../concepts/query-lang) as
an argument, or by using [options](#options) arguments.
```shell
# Find all projects
$ moon query projects
# Find all projects with an id that matches "react"
$ moon query projects --id react
$ moon query projects "project~react"
# Find all projects with a `lint` or `build` task
$ moon query projects --tasks "lint|build"
$ moon query projects "task=[lint,build]"
```
This will output a list of projects as JSON. The output has the following structure:
```ts
{
projects: Project[],
options: QueryOptions,
}
```
### Affected projects
This command can also be used to query for affected projects, based on the state of the VCS working
tree. For advanced control, you can also pass the results of `moon query changed-files` to stdin.
```shell
# Find all affected projects
$ moon query projects --affected
# Find all affected projects using the results of another query
$ moon query changed-files | moon query projects --affected
```
### Arguments
- `[query]` - An optional [query statement](../../concepts/query-lang) to filter projects with. When
provided, all [filter options](#filters) are ignored.
### Options
#### Affected
- `--affected` - Filter projects that have been affected by changed files.
- `--downstream` - Include downstream dependents of queried projects. Supports "none" (default),
"direct", "deep".
- `--upstream` - Include upstream dependencies of queried projects. Supports "none" (default),
"direct", "deep".
#### Filters
All option values are case-insensitive regex patterns.
- `--alias ` - Filter projects that match this alias.
- `--id ` - Filter projects that match this ID/name.
- `--language ` - Filter projects of this programming language.
- `--layer ` - Filter project of this layer.
- `--source ` - Filter projects that match this source path.
- `--stack ` - Filter projects of the tech stack.
- `--tags ` - Filter projects that have the following tags.
- `--tasks ` - Filter projects that have the following tasks.
### Configuration
- [`projects`](../../config/workspace#projects) in `.moon/workspace.*`
---
## query tasks
Use the `moon query tasks` sub-command to query task information for all projects in the project
graph. The tasks list can be filtered by passing a [query statement](../../concepts/query-lang) as
an argument, or by using [options](#options) arguments.
```shell
# Find all tasks grouped by project
$ moon query tasks
# Find all tasks from projects with an id that matches "react"
$ moon query tasks --id react
$ moon query tasks "task~react"
```
This will output a list of projects as JSON. The output has the following structure:
```ts
{
tasks: Record>,
options: QueryOptions,
}
```
### Arguments
- `[query]` - An optional [query statement](../../concepts/query-lang) to filter projects with. When
provided, all [filter options](#filters) are ignored.
### Options
#### Affected
- `--affected` - Filter tasks that have been affected by changed files.
- `--downstream` - Include downstream dependents of queried tasks. Supports "none" (default),
"direct", "deep".
- `--upstream` - Include upstream dependencies of queried tasks. Supports "none", "direct", "deep"
(default).
#### Filters
All option values are case-insensitive regex patterns.
- `--command ` - Filter tasks that match this command.
- `--id ` - Filter tasks that match this ID.
- `--project ` - Filter tasks that belong to this project.
- `--script ` - Filter tasks that match this script.
- `--toolchain ` - Filter tasks of this toolchain.
- `--tags ` - Filter tasks that have the following tags.
- `--type ` - Filter tasks of this type.
### Configuration
- [`projects`](../../config/workspace#projects) in `.moon/workspace.*`
- [`tasks`](../../config/project#tasks) in `moon.*`
---
## run
The `moon run` (or `moon r`) command will run one or many [targets](../concepts/target) and all of
its dependencies in topological order. Each run will incrementally cache each task, improving speed
and development times... over time. View the official [Run a task](../run-task) and
[Cheat sheet](../cheat-sheet#tasks) articles for more information!
```shell
# Run `lint` in project `app`
$ moon run app:lint
# Run `dev` in project `client` and `server`
$ moon run client:dev server:dev
# Run `test` in all projects
$ moon run :test
# Run `test` in the closest project, relative to the current working directory
$ moon run '~:test'
# Run `test` in all projects with tag `frontend`
$ moon run '#frontend:test'
# Run `format` in default project
$ moon run format
# Run `build` in projects matching the query
$ moon run :build --query "language=javascript && projectLayer=library"
```
:::info
The default behavior for `moon run` is to "fail fast", meaning that any failed task will immediately
abort execution of the entire action graph. Use `moon exec --on-failure continue` for alternative
behavior.
:::
### Arguments
- `...` - [Targets](../concepts/target) or project relative tasks to run.
- `[-- ]` - Additional arguments to
[pass to the underlying command](../run-task#passing-arguments-to-the-underlying-command).
### Options
Inherits all options from [`moon exec`](./exec) and pre-fills with: `--on-failure=bail`,
`--upstream=deep`.
- `query` - Filter tasks based on the result of a query.
### Configuration
- [`projects`](../config/workspace#projects) in `.moon/workspace.*`
- [`tasks`](../config/tasks#tasks) in `.moon/tasks/*`
- [`tasks`](../config/project#tasks) in `moon.*`
---
## setup
The `moon setup` command can be used to setup the developer and pipeline environments. It achieves
this by downloading and installing all configured toolchains.
```shell
$ moon setup
```
:::info
This command should rarely be used, as the environment is automatically setup when running other
commands, like detecting affected projects, running a task, or generating a build artifact.
:::
### Configuration
- [`*`](../config/toolchain) in `.moon/toolchains.*`
---
## sync code-owners
The `moon sync code-owners` command will manually sync code owners, by aggregating all owners from
projects, and generating a single `CODEOWNERS` file. Refer to the official
[code owners](../../guides/codeowners) guide for more information.
```shell
$ moon sync code-owners
```
### Options
- `--clean` - Clean and remove previously generated file.
- `--force` - Bypass cache and force create file.
### Configuration
- [`codeowners`](../../config/workspace#codeowners) in `.moon/workspace.*`
- [`owners`](../../config/project#owners) in `moon.*`
---
## sync config-schemas
The `moon sync config-schemas` command will manually generate JSON schemas to `.moon/cache/schemas`
for all our different configuration files.
```shell
$ moon sync config-schemas
```
### Options
- `--force` - Bypass cache and force create files.
---
## sync projects
The `moon sync projects` command will force sync _all_ projects in the workspace to help achieve a
healthy repository state. This applies the following:
- Ensures cross-project dependencies are linked based on
[`dependsOn`](../../config/project#dependson).
- Ensures language specific configuration files are present and accurate.
- Ensures root configuration and project configuration are in sync.
- Any additional language specific semantics that may be required.
```shell
$ moon sync projects
```
> This command should rarely be ran, as [`moon run`](../run) will sync affected projects
> automatically! However, when migrating or refactoring, manual syncing may be necessary.
### Configuration
- [`projects`](../../config/workspace#projects) in `.moon/workspace.*`
---
## sync vcs-hooks
The `moon sync vcs-hooks` command will manually sync hooks for the configured
[VCS](../../config/workspace#vcs), by generating and referencing hook scripts from the
[`vcs.hooks`](../../config/workspace#hooks) setting. Refer to the official
[VCS hooks](../../guides/vcs-hooks) guide for more information.
```shell
$ moon sync vcs-hooks
```
### Options
- `--clean` - Clean and remove previously generated hooks.
- `--force` - Bypass cache and force create hooks.
### Configuration
- [`vcs.hooks`](../../config/workspace#hooks) in `.moon/workspace.*`
---
## task-graph
The `moon task-graph [target]` (or `moon tg`) command will generate and serve a visual graph of all
configured tasks as nodes, with dependencies between as edges, and can also output the graph in
[Graphviz DOT format](https://graphviz.org/doc/info/lang.html).
```shell
# Run the visualizer locally
$ moon task-graph
# Export to DOT format
$ moon task-graph --dot > graph.dot
```
> A task target can be passed to focus the graph to only that task and its dependencies. For
> example, `moon task-graph app:build`.
### Arguments
- `[target]` - Optional target of task to focus.
### Options
- `--dependents` - Include direct dependents of the focused task.
- `--dot` - Print the graph in DOT format.
- `--host` - The host address. Defaults to `127.0.0.1`.
- `--json` - Print the graph in JSON format.
- `--port` - The port to bind to. Defaults to a random port.
## Example output
The following output is an example of the graph in DOT format.
```dot
digraph {
0 [ label="types:build" style=filled, shape=oval, fillcolor=gray, fontcolor=black]
1 [ label="runtime:build" style=filled, shape=oval, fillcolor=gray, fontcolor=black]
2 [ label="website:build" style=filled, shape=oval, fillcolor=gray, fontcolor=black]
1 -> 0 [ label="required" arrowhead=box, arrowtail=box]
2 -> 1 [ label="required" arrowhead=box, arrowtail=box]
2 -> 0 [ label="required" arrowhead=box, arrowtail=box]
}
```
---
## task
The `moon task [target]` (or `moon t`) command will display information about a task that has been
configured and exists within a project. If a task does not exist, the program will return with a 1
exit code.
```shell
$ moon task web:build
```
### Arguments
- `[target]` - Fully qualified project + task target.
### Options
- `--json` - Print the task and its configuration as JSON.
## Example output
The following output is an example of what this command prints, using our very own
`@moonrepo/runtime` package.
```
RUNTIME:BUILD
Task: build
Project: runtime
Toolchain: node
Type: build
PROCESS
Command: packemon build --addFiles --addExports --declaration
Environment variables:
- NODE_ENV = production
Working directory: ~/Projects/moon/packages/runtime
Runs dependencies: Concurrently
Runs in CI: Yes
DEPENDS ON
- types:build
INHERITS FROM
- .moon/tasks/node.yml
INPUTS
- .moon/*.yml
- .moon/tasks/node.yml
- packages/runtime/package.json
- packages/runtime/src/**/*
- packages/runtime/tsconfig.*.json
- packages/runtime/tsconfig.json
- packages/runtime/types/**/*
- tsconfig.options.json
OUTPUTS
- packages/runtime/cjs
```
### Configuration
- [`tasks`](../config/tasks#tasks) in `.moon/tasks/*`
- [`tasks`](../config/project#tasks) in `moon.*`
---
## tasks
The `moon tasks` command will list all tasks available in the workspace as a table of information.
```shell
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│Task Command Type Preset Toolchains Description │
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
│website:build docusaurus build typescript, javascript, node, yarn Builds the Docusaurus app. │
│website:format prettier test javascript, node, yarn │
│website:format-write prettier test javascript, node, yarn │
│website:lint eslint test javascript, node, yarn │
│website:lint-fix eslint test javascript, node, yarn │
│website:start docusaurus run server typescript, javascript, node, yarn │
│website:test jest test javascript, node, yarn │
│website:typecheck tsc test typescript, javascript, node, yarn │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
```
:::info
Use [`moon query tasks`](./query/tasks) for advanced querying and filtering of tasks.
:::
### Arguments
- `[id]` - Filter tasks to a specific project ID.
### Options
- `--json` - Print the projects as JSON.
---
## teardown
The `moon teardown` command, as its name infers, will teardown and clean the current environment,
opposite the [`setup`](./setup) command. It achieves this by doing the following:
- Uninstalling all configured toolchains.
- Removing any download or temporary files/folders.
```shell
$ moon teardown
```
### Configuration
- [`*`](../config/toolchain) in `.moon/toolchains.*`
---
## template
The `moon template [id]` command will display information about a template, its files, and
variables.
```shell
Template title ───────────────────────────────────────────────────────────
Some description of the template and its files.
About ────────────────────────────────────────────────────────────────────
Template: package
Location: /templates/package
Destination: packages/[name | kebab_case]
Extends: —
Assets: —
Files:
- package.json
Variables ────────────────────────────────────────────────────────────────
name: string
```
### Arguments
- `[id]` - The template ID.
### Options
- `--json` - Print the template as JSON.
---
## templates
The `moon templates` command will list all templates available for [code generation](./generate).
This list will include the template title, description, default destination, where it's source files
are located, and more.
```shell
$ moon templates
```
### Options
- `--filter` - Filter templates by a search term.
- `--json` - Print templates in JSON format.
### Configuration
- [`generator`](../config/workspace#generator) in `.moon/workspace.*`
---
## toolchain add
The `moon toolchain add [plugin]` command will add a toolchain to the workspace by injecting a
configuration block into `.moon/toolchains.*`. To do this, the command will download the WASM
plugin, extract information, and call initialize functions.
For built-in toolchains, the [plugin locator][locator] argument is optional, and will be derived
from the identifier.
```shell
$ moon toolchain add typescript
```
For third-party toolchains, the [plugin locator][locator] argument is required, and must point to
the WASM plugin.
```shell
$ moon toolchain add custom https://example.com/path/to/plugin.wasm
```
### Arguments
- `` - ID of the toolchain to use.
- `[plugin]` - Optional [plugin locator][locator] for third-party toolchains.
### Options
- `--minimal` - Generate minimal configurations and sane defaults.
- `--yes` - Skip all prompts and enables tools based on file detection.
[locator]: ../../guides/wasm-plugins#configuring-plugin-locations
---
## toolchain info
The `moon toolchain info [plugin]` command will display detailed information about a toolchain,
like what files are scanned, what configuration settings are available, and what tier APIs are
supported. To do this, the command will download the WASM plugin, extract information, and call
specific functions.
For built-in toolchains, the [plugin locator][locator] argument is optional, and will be derived
from the identifier.
```shell
$ moon toolchain info typescript
```
For third-party toolchains, the [plugin locator][locator] argument is required, and must point to
the WASM plugin.
```shell
$ moon toolchain info custom https://example.com/path/to/plugin.wasm
```
### Arguments
- `` - ID of the toolchain to view.
- `[plugin]` - Optional [plugin locator][locator] for third-party toolchains.
## Example output
```
Toolchain ─────────────────────────────────────────────────────────────────
Provides sync operations that keep tsconfig.json's in a healthy state.
ID: typescript
Name: TypeScript
Version: 0.2.0
Configuration ─────────────────────────────────────────────────────────────
createMissingConfig: bool
When `syncProjectReferences` is enabled, will create a `tsconfig.json`
in referenced projects if it does not exist.
includeProjectReferenceSources: bool
Appends sources of project reference to `include` in `tsconfig.json`,
for each project.
includeSharedTypes: bool
Appends shared types to `include` in `tsconfig.json`, for each project.
projectConfigFileName: string
Name of the `tsconfig.json` file within each project.
root: string
The relative root to the TypeScript root. Primarily used for
resolving project references.
rootConfigFileName: string
Name of the `tsconfig.json` file at the workspace root.
rootOptionsConfigFileName: string
Name of the shared compiler options `tsconfig.json` file
at the workspace root.
routeOutDirToCache: bool
Updates and routes `outDir` in `tsconfig.json` to moon's cache,
for each project.
syncProjectReferences: bool
Syncs all project dependencies as `references` in `tsconfig.json`,
for each project.
syncProjectReferencesToPaths: bool
Syncs all project dependencies as `paths` in `tsconfig.json`,
for each project.
Tier 1 - Usage detection ──────────────────────────────────────────────────
Config files: tsconfig.json, tsconfig.*.json, *.tsconfig.json, .tsbuildinfo, *.tsbuildinfo
Executable names: tsc, tsserver
APIs:
🟢 register_toolchain (required)
🟢 define_toolchain_config
🟢 initialize_toolchain
⚫️ detect_version_files
⚫️ parse_version_file
🟢 define_docker_metadata
⚫️ scaffold_docker
⚫️ prune_docker
🟢 sync_project
⚫️ sync_workspace
Tier 2 - Ecosystem integration ─────────────────────────────────────────────
APIs:
⚫️ extend_project_graph
⚫️ extend_task_command
⚫️ extend_task_script
⚫️ locate_dependencies_root
⚫️ install_dependencies
🟢 hash_task_contents
⚫️ parse_lock
⚫️ parse_manifest
⚫️ setup_environment
Tier 3 - Tool management ──────────────────────────────────────────────────
APIs:
⚫️ register_tool (required)
⚫️ load_versions
⚫️ resolve_version
⚫️ download_prebuilt (required)
⚫️ unpack_archive
⚫️ locate_executables (required)
⚫️ setup_toolchain
⚫️ teardown_toolchain
```
---
## upgrade
The `moon upgrade` command can be used to upgrade your current moon binary (if installed globally)
to the latest version.
```shell
$ moon upgrade
```
:::caution
This command will only work if moon was installed in the `~/.moon` directory, using our official
[installation script](../install). If installed another way, you'll need to upgrade manually.
:::
### Options
- `--update-constraint` - After upgrading, update the
[`versionConstraint`](../config/workspace#versionconstraint) in the workspace configuration to
require the newly installed version.
---
## Feature comparison
DockerTable,
GeneratorTable,
JavaScriptTable,
OtherSystemsTable,
ProjectsTable,
TasksTable,
TaskRunnerTable,
ToolchainTable,
WorkspaceTable,
} from '@site/src/components/ComparisonTable';
The following comparisons are _not_ an exhaustive list of features, and may be inaccurate or out of
date, but represent a good starting point for investigation. If something is not correct, please
[create an issue](https://github.com/moonrepo/moon/issues) or
[submit a patch](https://github.com/moonrepo/moon/blob/master/website/src/components/ComparisonTable.tsx).
Before diving into our comparisons below, we highly suggest reading
[monorepo.tools](https://monorepo.tools/) for a deeper insight into monorepos and available tooling.
It's a great resource for learning about the current state of things and the ecosystem.
:::info
Looking to migrate from Nx or Turborepo to moon? Use our
[`moon ext migrate-nx`](./guides/extensions#migrate-nx) or
[`moon ext migrate-turborepo`](./guides/extensions#migrate-turborepo) commands for a (somewhat)
seamless migration!
:::
## Unique features
Although moon is still in its infancy, we provide an array of powerful features that other frontend
centric task runners do not, such as...
- **[Integrated toolchain](./concepts/toolchain)** - moon manages its own version of programming
languages and dependency managers behind the scenes, so that every task is executed with the
_exact same version_, across _all machines_.
- **[Task inheritance](./concepts/task-inheritance)** - Instead of defining the same tasks (lint,
test, etc) over and over again for _every_ project in the monorepo, moon supports a task
inheritance model where it only needs to be defined once at the top-level. Projects can then merge
with, exclude, or override if need be.
- **[Continuous integration](./guides/ci)** - By default, all moon tasks will run in CI, as we want
to encourage every facet of a project or repository to be continually tested and verified. This
can be turned off on a per-task basis.
Curious to learn more? Check out the "[Why use moon?](.)" or "[Features](.)" sections for more
information, or these wonderful articles provided by the community:
- [A review of moon + Packemon](https://azu.github.io/slide/2022/moa/moon-packemon.html) by
[azu](https://twitter.com/azu_re)
- [Improve repo management with moon](https://blog.logrocket.com/improve-repo-management-moon/) by
[James Sinkala](https://jamesinkala.com/)
## Comparison
### Turborepo
At a high-level, Turborepo and moon seem very similar as they both claim to be task runners. They
both support incremental builds, content/smart hashing, local and remote caching1,
parallel execution, and everything else you'd expect from a task runner. But that's where the
similarities stop, because in the end, Turborepo is nothing more than a `package.json` scripts
orchestrator with a caching layer. While moon also supports this, it
[aims to be far more](#unique-features) with a heavy focus on the developer experience.
In the next section, we'll be talking about a few key areas that we deem important to consumers. If
you'd prefer a more granular comparison, jump down to the [comparison tables](#comparison-tables).
#### Configuration
Turborepo only supports the Node.js ecosystem, so implicitly uses a conventions based approach. It
provides very little to no configuration for customizing Turborepo to your needs.
>
}
right={
<>
moon is language agnostic, with initial support for Node.js and its ecosystem. Because of this, moon
provides a ton of configuration for customizing moon to your needs. It prefers a configuration over
conventions approach, as every repository is different.
>
}
/>
#### Projects
Turborepo infers projects from `package.json` workspaces, and does not support non-JavaScript based
projects.
>
}
right={
<>
moon requires projects to be defined in `.moon/workspace.*`, and supports any programming
language2.
>
}
/>
#### Tasks
Turborepo requires `package.json` scripts to be defined for every project. This results in the same
scripts being repeated constantly.
>
}
right={
<>
moon avoids this overhead by using [task inheritance](#unique-features). No more repetition.
>
}
/>
#### CI
Each pipeline in `turbo.json` must be individually ran as a step in CI. Scripts not configured as
pipeline tasks are never ran.
>
}
right={
<>
moon runs every task automatically using `moon ci`, which also supports parallelism/sharding.
>
}
/>
#### Long-term
Turborepo is in the process of being rewritten in Rust, with its codebase being shared and coupled
with the new Turbopack library, a Rust based bundler.
Outside of this, there are no publicly available plans for Turborepo's future.
>
}
right={
<>
moon plans to be so much more than a task runner, with one such facet being a repository management
tool. This includes code ownership, dependency management and auditing, repository linting, in-repo
secrets, and anything else we deem viable.
We also plan to support additional languages as first-class citizens within our toolchain.
>
}
/>
1. Turborepo remote caching is powered by Vercel. moon provides its own paid service.
2. moon projects may run commands for any language, but not all languages are supported in the
toolchain.
### Lerna
Lerna was a fantastic tool that helped the JavaScript ecosystem grow and excelled at package
versioning and publishing (and still does), but it offered a very rudimentary task runner. While
Lerna was able to run scripts in parallel, it wasn't the most efficient, as it did not support
caching, hashing, or performant scheduling.
However, the reason Lerna is not compared in-depth, is that Lerna was unowned and unmaintained for
quite some time, and has recently fallen under the Nx umbrella. Lerna is basically Nx lite now.
## Comparison tables
🟩 Supported
🟨 Partially supported
🟦 Similarly supported
🟥 Not supported
### Workspace
### Toolchain
### Projects
### Tasks
### Task runner
### Generator
### Other systems
### JavaScript ecosystem
### Docker integration
---
## Affected
Affected is a term to describe when a project or task is _affected by a change_ in the environment
(workspace). This is a core concept in moon, and is the basis for many of our features, such as task
running, incremental builds, and more.
## Change types
To start, there are 3 types of "sources" that can trigger an affected state: files, environment
variables, and graph relations.
### Files
A file is considered changed if it has been added, modified, deleted, renamed, moved, copied, so on
and so forth. This state is determined by the version control system (VCS) in use, such as Git,
Mercurial, or Subversion. For Git, this is determined by the `git status` command.
For a project, any changed file that is within the project folder (starts with the project source),
triggers affected.
For a task, any changed file that is configured within the task's
[`inputs`](../config/project#inputs) triggers affected.
### Environment variables
An environment variable is considered changed if it exists and is non-empty. This is determined by
the presence of the variable in the environment, and its value.
For projects, they are _not_ affected by environment variables.
For a task, any changed environment variable that is configured within the task's
[`inputs`](../config/project#inputs) triggers affected.
### Graph relations
A relation is considered changed if a project or task that is depended on, or depends on, is
affected by a changed file or environment variable. This is determined by the project and task
graph, which is built from the workspace configuration.
For a project, affected dependency/dependent projects of the project (via
[`dependsOn`](../config/project#dependson)), or affected tasks within the project, triggers
affected.
For a task, affected dependency/dependent tasks of the task (via [`deps`](../config/project#deps))
triggers affected.
:::info
This check is conditionally enabled. For [`moon query`](../commands/query), it is always enabled.
For exec-based commands, it is not enabled by default and the `--include-relations` flag must be
passed.
:::
## Graph depth
When determining affected state based on graph relations, the depth of traversal can be configured
with the `--upstream` (`--dependencies`) and `--downstream` (`--dependents`) options. These options
can be set to one of the following values:
- `none` - Do not include any relations.
- `direct` - Include only direct relations.
- `deep` - Include all relations.
For exec-based commands, the default is `--upstream=deep` and `--downstream=none`, meaning all
dependencies are included, but no dependents are included. For query and other commands, the default
is `none` for both.
---
## Cache
moon's able to achieve high performance and blazing speeds by implementing a cache that's powered by
our own unique smart hashing layer. All cache is stored in `.moon/cache`, relative from the
workspace root (be sure to git ignore this folder).
## Hashing
Incremental builds are possible through a concept known as hashing, where in multiple sources are
aggregated to generate a unique hash. In the context of moon, each time a target is ran we generate
a hash, and if this hash already exists we abort early (cache hit), otherwise we continue the run
(cache miss).
The tiniest change may trigger a different hash, for example, changing a line of code (when an
input), or updating a package version, so don't worry if you see _a lot_ of hashes.
Our smart hashing currently takes the following sources into account:
- Command (`command`) being ran and its arguments (`args`).
- Input sources (`inputs`).
- Output targets (`outputs`).
- Environment variables (`env`).
- Dependencies between projects (`dependsOn`) and tasks (`deps`). Task dependency contribution can
be tuned per-dependency via [`cacheStrategy`](../config/project#cache-strategy).
- **For Deno tasks**:
- Deno version.
- `deno.json`/`deps.ts` imports, import maps, and scopes.
- `tsconfig.json` compiler options (when applicable).
- **For Bun and Node.js tasks**:
- Bun/Node.js version.
- `package.json` dependencies (including development and peer).
- `tsconfig.json` compiler options (when applicable).
:::caution
Be aware that greedy inputs (`**/*`, the default) will include _everything_ in the target directory
as a source. We do our best to filter out VCS ignored files, and `outputs` for the current task, but
files may slip through that you don't expect. We suggest using explicit `inputs` and routinely
auditing the hash files for accuracy!
:::
## Archiving & hydration
On top of our hashing layer, we have another concept known as archiving, where in we create a
tarball archive of a task's outputs and store it in `.moon/cache/outputs`. These are akin to build
artifacts.
When we encounter a cache hit on a hash, we trigger a mechanism known as hydration, where we
efficiently unpack an existing tarball archive into a task's outputs. This can be understood as a
timeline, where every point in time will have its own hash + archive that moon can play back.
Furthermore, if we receive a cache hit on the hash, and the hash is the same as the last run, and
outputs exist, we exit early without hydrating and assume the project is already hydrated. In the
terminal, you'll see a message for "cached".
> As of v2.3, an experimental content-addressable storage (CAS) layer is also available that
> replaces the tarball-based local cache with the same content-addressed format used by the remote
> cache. Enable it via the [`experiments.casOutputsCache`](../config/workspace#casoutputscache)
> setting.
## File structure
The following diagram outlines our cache folder structure and why each piece exists.
```shell
.moon/cache/
# A content-addressable storage (CAS) layer for storing blobs of data, which are
# referenced by their hash. This is used for both local and remote caching.
blobs/
//
# Contains the daemon's socket, log, and state files.
daemon/
# The daemon's state file, which contains metadata about the daemon process.
daemon.json
# Stores hash manifests of every ran task. Exists purely for debugging purposes.
hashes/
# Contents includes all sources used to generate the hash.
.json
# File system locks for parallel processes.
locks/
.lock
# A key-value layer for storing cache manifests, which denote the relationship between
# a task's hash and its outputs.
manifests/
//
# Stores `tar.gz` archives of a task's outputs based on its generated hash.
outputs/
.tar.gz
# JSON schemas for validating configuration files.
schemas/
.json
# State information about anything and everything within moon. Toolchain,
# dependencies, projects, running targets, etc.
states/
# Files at the root pertain to the entire workspace.
.json
# Files for a project are nested within a folder by the project name.
/
# Informational snapshot of the project, its tasks, and its configs.
# Can be used at runtime by tasks that require this information.
snapshot.json
/
# Contents of the child process, including the exit code and
# unique hash that is referenced above.
lastRun.json
# Outputs of last run target.
stderr.log
stdout.log
```
---
## File groups
File groups are a mechanism for grouping similar types of files and environment variables within a
project using [file glob patterns or literal file paths](./file-pattern). These groups are then used
by [tasks](./task) to calculate functionality like cache computation, affected files since last
change, deterministic builds, and more.
## Configuration
File groups can be configured per project through [`moon.*`](../config/project), or for many
projects through [`.moon/tasks/**/*`](../config/tasks).
### Token functions
File groups can be referenced in [tasks](./task) using [token functions](./token). For example, the
`@group(name)` token will expand to all paths configured in the `sources` file group.
```yaml title="moon.yml"
tasks:
build:
command: 'vite build'
inputs:
- '@group(sources)'
```
## Inheritance and merging
When a file group of the same name exists in both [configuration files](#configuration), the
project-level group will override the workspace-level group, and all other workspace-level groups
will be inherited as-is.
A primary scenario in which to define file groups at the project-level is when you want to
_override_ file groups defined at the workspace-level. For example, say we want to override the
`sources` file group because our source folder is named "lib" and not "src", we would define our
file groups as followed.
```yaml title=".moon/tasks/all.yml"
fileGroups:
sources:
- 'src/**/*'
- 'types/**/*'
tests:
- 'tests/**/*.test.*'
- '**/__tests__/**/*'
```
```yaml title="moon.yml"
fileGroups:
# Overrides global
sources:
- 'lib/**/*'
- 'types/**/*'
# Inherited as-is
tests:
- 'tests/**/*.test.*'
- '**/__tests__/**/*'
```
---
## File patterns
## Globs
Globs in moon are [Rust-based globs](https://github.com/olson-sean-k/wax), _not_ JavaScript-based.
This may result in different or unexpected results. The following guidelines must be met when using
globs:
- Must use forward slashes (`/`) for path separators, even on Windows.
- Must _not_ start with or use any relative path parts, `.` or `..`.
### Supported syntax
- `*` - Matches zero or more characters, but does not match the `/` character. Will attempt to match
the longest possible text (eager).
- `$` - Like `*`, but will attempt to match the shortest possible text (lazy).
- `**` - Matches zero or more directories.
- `?` - Matches exactly one character, but not `/`.
- `[abc]` - Matches one case-sensitive character listed in the brackets.
- `[!xyz]` - Like the above, but will match any character _not_ listed.
- `[a-z]` - Matches one case-sensitive character in range in the brackets.
- `[!x-z]` - Like the above, but will match any character _not_ in range.
- `{glob,glob}` - Matches one or more comma separated list of sub-glob patterns.
- `` - Matches a sub-glob within a defined bounds.
- `!` - At the start of a pattern, will negate previous positive patterns.
### Examples
```bash
README.{md,mdx,txt}
src/**/*
tests/**/*.?js
!**/__tests__/**/*
logs/<[0-9]:4>-<[0-9]:2>-<[0-9]:2>.log
```
## Project relative
When configuring [`fileGroups`](../config/project#filegroups), [`inputs`](../config/project#inputs),
and [`outputs`](../config/project#outputs), all listed file paths and globs are relative from the
project root they will be ran in. They _must not_ traverse upwards with `..`.
```bash
# Valid
src/**/*
./src/**/*
package.json
# Invalid
../utils
```
## Workspace relative
When configuring [`fileGroups`](../config/project#filegroups), [`inputs`](../config/project#inputs),
and [`outputs`](../config/project#outputs), a listed file path or glob can be prefixed with `/` to
resolve relative from the workspace root, and _not_ the project root.
```bash
# In project
package.json
# In workspace
/package.json
```
---
## Projects(Concepts)
A project is a library, application, package, binary, tool, etc, that contains source files, test
files, assets, resources, and more. A project must exist and be configured within a
[workspace](./workspace).
## IDs
A project identifier (or name) is a unique resource for locating a project. The ID is explicitly
configured within [`.moon/workspace.*`](../config/workspace), as a key within the
[`projects`](../config/workspace#projects) setting, and can be written in camel/kebab/snake case.
IDs support alphabetic unicode characters, `0-9`, `_`, `-`, `/`, `.`, and must start with a
character.
IDs are used heavily by configuration and the command line to link and reference everything. They're
also a much easier concept for remembering projects than file system paths, and they typically can
be written with less key strokes.
Lastly, a project ID can be paired with a task ID to create a [target](./target).
## Aliases
Aliases are a secondary approach for naming projects, and can be used as a drop-in replacement for
standard names. What this means is that an alias can also be used when configuring dependencies, or
defining [targets](./target).
However, the difference between aliases and names is that aliases _can not_ be explicit configured
in moon. Instead, they are derived from toolchain's that have been detected for the project. For
example, a JavaScript project will use the `name` field from its `package.json` as the alias.
Because of this, a project can either be referenced by its name or alias, or both. Choose the
pattern that makes the most sense for your company or team!
## Dependencies
Projects can depend on other projects within the [workspace](./workspace) to build a
[project graph](../how-it-works/action-graph), and in turn, an action graph for executing
[tasks](./task). Project dependencies are divided into 2 categories:
- **Explicit dependencies** - These are dependencies that are explicitly defined in a project's
[`moon.*`](../config/project) config file, using the [`dependsOn`](../config/project#dependson)
setting.
- **Implicit dependencies** - These are dependencies that are implicitly discovered by moon when
scanning the repository. How an implicit dependency is discovered is based on the project's
[`language`](../config/project#language) setting, and how that language's ecosystem functions.
## Configuration
Projects can be configured with an optional [`moon.*`](../config/project) in the project root, or
through the optional workspace-level [`.moon/tasks/**/*`](../config/tasks).
---
## Query language
moon supports an integrated query language, known as MQL, that can be used to filter and select
projects from the project graph, using an SQL-like syntax. MQL is primarily used by
[`moon run`](../commands/run) with the `--query` option.
## Syntax
### Comparisons
A comparison (also known as an assignment) is an expression that defines a piece of criteria, and is
a building block of a query. This criteria maps a [field](#fields) to a value, with an explicit
comparison operator.
#### Equals, Not equals
The equals (`=`) and not equals (`!=`) comparison operators can be used for _exact_ value matching.
```
projectLayer=library && language!=javascript
```
You can also define a list of values using square bracket syntax, that will match against one of the
values.
```
language=[javascript, typescript]
```
#### Like, Not like
The like (`~`) and not like (`!~`) comparison operators can be used for _wildcard_ value matching,
using [glob syntax](./file-pattern#globs).
```
projectSource~packages/* && tag!~*-app
```
> Like comparisons can only be used on non-enum fields.
### Conditions
The `&&` and `||` logical operators can be used to combine multiple comparisons into a condition.
The `&&` operator is used to combine comparisons into a logical AND, and the `||` operator is used
for logical OR.
```
taskToolchain=system || taskToolchain=node
```
For readability concerns, you can also use `AND` or `OR`.
```
taskToolchain=system OR taskToolchain=node
```
> Mixing both operators in the same condition is not supported.
### Grouping
For advanced queries and complex conditions, you can group comparisons using parentheses to create
logical groupings. Groups can also be nested within other groups.
```
language=javascript && (taskType=test || taskType=build)
```
## Fields
The following fields can be used as criteria, and are related to [task tokens](./token#variables).
### `language`
Programming language the project is written in, as defined in
[`moon.*`](../config/project#language).
```
language=rust
```
### `project`
Name OR alias of the project.
```
project=server
```
### `projectAlias`
Alias of the project. For example, the `package.json` name.
```
projectAlias~@scope/*
```
### `projectLayer`
The project layer, as defined in [`moon.*`](../config/project#layer).
```
projectLayer=application
```
### `projectId`
Name of the project, as defined in [`.moon/workspace.*`](../config/workspace), or `id` in
[`moon.*`](../config/project#id).
```
projectId=server
```
### `projectSource`
Relative file path from the workspace root to the project root, as defined in
[`.moon/workspace.*`](../config/workspace).
```
projectSource~packages/*
```
### `projectStack`
The project stack, as defined in [`moon.*`](../config/project#stack).
```
projectStack=frontend
```
### `projectTag`
A tag within the project, as defined in [`moon.*`](../config/project#tags).
```
projectTag~react-*
```
### `task`
ID/name of a task within the project.
```
task=[build,test]
```
### `taskTag`
A tag within the task, as defined in [`moon.*`](../config/project#tags-1).
```
taskTag=quality
```
### `taskToolchain`
The toolchain a task will run against, as defined in [`moon.*`](../config/project).
```
taskToolchain=node
```
### `taskType`
The [type of task](./task#types), based on its configured settings.
```
taskType=build
```
---
## Targets
A target is a compound identifier that pairs a [scope](#common-scopes) to a [task](./task),
separated by a `:`, in the format of `scope:task`.
Targets are used by terminal commands...
```shell
$ moon run designSystem:build
```
And configurations for declaring cross-project or cross-task dependencies.
```yaml
tasks:
build:
command: 'webpack'
deps:
- 'designSystem:build'
```
## Common scopes
These scopes are available for both running targets and configuring them.
### By project
The most common scope is the project scope, which requires the name of a project, as defined in
[`.moon/workspace.*`](../config/workspace). When paired with a task name, it will run a specific
task from that project.
```shell
# Run `lint` in project `app`
$ moon run app:lint
```
### By project tag
Another way to target projects is with the tag scope, which requires the name of a tag prefixed with
`#`, and will run a specific task in all projects with that tag.
```shell
# Run `lint` in projects with the tag `frontend`
$ moon run '#frontend:lint'
```
:::caution
Because `#` is a special character in the terminal (is considered a comment), you'll need to wrap
the target in quotes, or escape it like so `\#`.
:::
### By task tag
Targets can also be scoped by task tags, which is similar to the project tag scope, but instead of
targeting projects with a specific tag, it targets tasks with a specific tag, regardless of the
project. This allows you to run specific tasks across different projects based on their tags.
```shell
# Run `#quality` tagged tasks in the app project
$ moon run app:#quality
```
## Run scopes
These scopes are only available on the command line when running tasks with `moon run` or `moon ci`.
### All projects
For situations where you want to run a specific target in _all_ projects, for example `lint`ing, you
can utilize the all projects scope by omitting the project name from the target: `:lint`.
```shell
# Run `lint` in all projects
$ moon run :lint
```
### Closest project `~`
If you are within a project folder, or an arbitrarily nested folder, and want to run a task in the
closest project (traversing upwards), the `~` scope can be used.
```shell
# Run `lint` in the closest project
$ moon run '~:lint'
```
:::caution
Because `~` is a special character in the terminal (tilde expansion), you'll need to wrap the target
in quotes, or escape it like so `\~`.
:::
## Config scopes
These scopes are only available when configuring a task.
### Dependencies `^`
When you want to include a reference for each project [that's depended on](./project#dependencies),
you can utilize the `^` scope. This will be expanded to _all_ depended on projects. If you do not
want all projects, then you'll need to explicitly define them.
```yaml title="moon.yml"
dependsOn:
- 'apiClients'
- 'designSystem'
# Configured as
tasks:
build:
command: 'webpack'
deps:
- '^:build'
# Resolves to
tasks:
build:
command: 'webpack'
deps:
- 'apiClients:build'
- 'designSystem:build'
```
### Owning project `~`
When referring to another task within the current project, you can utilize the `~` scope, or omit
the `~:` prefix altogether, which will be expanded to the current project's name. This is useful for
situations where the name is unknown, for example, when configuring
[`.moon/tasks/**/*`](../config/tasks), or if you just want a shortcut!
```yaml title=".moon/tasks/all.yml"
# Configured as
tasks:
lint:
command: 'eslint'
deps:
- '~:typecheck'
# OR
- 'typecheck'
typecheck:
command: 'tsc'
# Resolves to (assuming project is "foo")
tasks:
lint:
command: 'eslint'
deps:
- 'foo:typecheck'
typecheck:
command: 'tsc'
```
---
## Task inheritance
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/**/*`][tasks], and
are inherited by based on conditions. However, projects are able to include, exclude, or rename
inherited tasks using the [`workspace.inheritedTasks`](../config/project#inheritedtasks) in
[`moon.*`](../config/project).
## Conditional inheritance
Task inheritance is powered by the [`inheritedBy`](../config/tasks#inheritedby) setting in global
task configurations (those in [`.moon/tasks/**/*`][tasks]). This setting defines conditions that a
project must meet in order for inheritance to occur. If the setting is not defined, or no conditions
are defined, the configuration is inherited by _all_ projects.
The following conditions are supported:
- `file`, `files` - Inherit for projects that contain specific files (does not support globs).
- `language`, `languages` - Inherit for projects that belong to specific
[`language`](../config/project#language)s.
- `layer`, `layers` - Inherit for projects that belong to specific
[`layer`](../config/project#layer)s.
- `stack`, `stacks` - Inherit for projects that belong to specific
[`stack`](../config/project#stack)s.
- `tag`, `tags` - Inherit for projects that have specific [`tags`](../config/project#tags).
- `toolchain`, `toolchains` - Inherit for projects that belong to specific
[`toolchains`](../config/project#toolchains).
One or many conditions can be defined, and all conditions must be met for inheritance to occur. For
example, the following configuration will only be inherited by Node.js frontend libraries.
```yaml title=".moon/tasks/node-frontend-library.yml"
inheritedBy:
toolchain: 'node'
stack: 'frontend'
layer: 'library'
```
Each condition supports a single value or an array of values. For example, the previous example
could be rewritten to inherit for both Node.js or Deno frontend libraries.
```yaml title=".moon/tasks/js-frontend-library.yml"
inheritedBy:
toolchains: ['node', 'deno']
stack: 'frontend'
layer: 'library'
```
### Clauses
The `tags` and `toolchains` conditions support special clauses `and`, `or` (the default), and `not`
for matching more complex scenarios.
```yaml
inheritedBy:
toolchains:
or: ['javascript', 'typescript']
not: ['ruby']
layer: 'library'
```
## Merge strategies
When a [global task](../config/tasks#tasks) and [local task](../config/project#tasks) of the same
name exist, they are merged into a single task. To accomplish this, one of many
[merge strategies](../config/project#options) can be used.
Merging is applied to the parameters [`args`](../config/project#args),
[`deps`](../config/project#deps), [`env`](../config/project#env-1),
[`inputs`](../config/project#inputs), [`outputs`](../config/project#outputs), and
[`toolchains`](../config/project#toolchains), using the [`merge`](../config/project#merge),
[`mergeArgs`](../config/project#mergeargs), [`mergeDeps`](../config/project#mergedeps),
[`mergeEnv`](../config/project#mergeenv), [`mergeInputs`](../config/project#mergeinputs),
[`mergeOutputs`](../config/project#mergeoutputs) and
[`mergeToolchains`](../config/project#mergetoolchains) 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.
- `preserve` - Preserve the original global task values. This should rarely be used, but exists for
situations where an inheritance chain is super long and complex, but we simply want to the base
values.
- `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.
```yaml
# Global
tasks:
build:
command:
- 'webpack'
- '--mode'
- 'production'
- '--color'
deps:
- 'designSystem:build'
inputs:
- '/webpack.config.js'
outputs:
- 'build/'
# Local
tasks:
build:
args: '--no-color --no-stats'
deps:
- 'reactHooks:build'
inputs:
- 'webpack.config.js'
options:
mergeArgs: 'append'
mergeDeps: 'prepend'
mergeInputs: 'replace'
# Merged result
tasks:
build:
command:
- 'webpack'
- '--mode'
- 'production'
- '--color'
- '--no-color'
- '--no-stats'
deps:
- 'reactHooks:build'
- 'designSystem:build'
inputs:
- 'webpack.config.js'
outputs:
- 'build/'
options:
mergeArgs: 'append'
mergeDeps: 'prepend'
mergeInputs: 'replace'
```
[tags]: ../config/project#tags
[tasks]: ../config/tasks
[language]: ../config/project#language
[stack]: ../config/project#stack
[layer]: ../config/project#layer
---
## Tasks(Concepts)
Tasks are commands that are ran in the context of a [project](./project). Underneath the hood, a
task is simply a binary or system command that is ran as a child process.
## IDs
A task identifier (or name) is a unique resource for locating a task _within_ a project. The ID is
explicitly configured as a key within the [`tasks`](../config/project#tasks) setting, and can be
written in camel/kebab/snake case. IDs support alphabetic unicode characters, `0-9`, `_`, `-`, `/`,
`.`, and must start with a character.
A task ID can be paired with a scope to create a [target](./target).
## Types
Tasks are grouped into 1 of the following types based on their configured parameters.
- **Build** - Task generates one or many artifacts, and is derived from the
[`outputs`](../config/project#outputs) setting.
- **Run** - Task runs a one-off, long-running, or never-ending process, and is derived from the
[`options.persistent`](../config/project#persistent) setting.
- **Test** - Task asserts code is correct and behaves as expected. This includes linting,
typechecking, unit tests, and any other form of testing. Is the default.
## Modes
Alongside types, tasks can also grouped into a special mode that provides unique handling within the
action graph and pipelines.
### Local server
Tasks either run locally, in CI (continuous integration pipelines), or both. For tasks that should
_only_ be ran locally, for example, development servers, we provide a mechanism for marking a task
as local only server. When enabled, caching is turned off, the task will not run in CI, terminal
output is not captured, and the task is marked as [persistent](#persistent).
To mark a task as local only, enable the [`preset`](../config/project#preset) setting.
```yaml title="moon.yml"
tasks:
dev:
command: 'start-dev-server'
preset: 'server'
```
### Internal only
Internal tasks are tasks that are not meant to be ran explicitly by the user (via the command line),
but are used internally as dependencies of other tasks. Additionally, internal tasks are not
displayed in a project's tasks list, but can be inspected with [`moon task`](../commands/task).
To mark a task as internal, enable the [`options.internal`](../config/project#internal) setting.
```yaml title="moon.yml"
tasks:
prepare:
command: 'intermediate-step'
options:
internal: true
```
### Interactive
Tasks that need to interact with the user via terminal prompts are known as interactive tasks.
Because interactive tasks require stdin, and it's not possible to have multiple parallel running
tasks interact with stdin, we isolate interactive tasks from other tasks in the action graph. This
ensures that only 1 interactive task is ran at a time.
To mark a task as interactive, enable the [`options.interactive`](../config/project#interactive)
setting.
```yaml title="moon.yml"
tasks:
init:
command: 'init-app'
options:
interactive: true
```
### Persistent
Tasks that never complete, like servers and watchers, are known as persistent tasks. Persistent
tasks are typically problematic when it comes to dependency graphs, because if they run in the
middle of the graph, subsequent tasks will never run because the persistent task never completes!
However in moon, this is a non-issue, as we collect all persistent tasks within the action graph and
run them _last as a batch_. This is perfect for a few reasons:
- All persistent tasks are ran in parallel, so they don't block each other.
- Running both the backend API and frontend webapp in parallel is a breeze.
- Dependencies of persistent tasks are guaranteed to have ran and completed.
To mark a task as persistent, enable the [`options.persistent`](../config/project#persistent)
setting.
```yaml title="moon.yml"
tasks:
dev:
command: 'start-dev-server'
options:
persistent: true
# OR
preset: 'server'
```
## Configuration
Tasks can be configured per project through [`moon.*`](../config/project), or for many projects
through [`.moon/tasks/**/*`](../config/tasks).
### Commands vs Scripts
A task is either a command or script, but not both. So what's the difference exactly? In the context
of a moon task, a command is a single binary execution with optional arguments, configured with the
[`command`](../config/project#command) and [`args`](../config/project#args) settings (which both
support a string or array). While a script is one or many binary executions, with support for pipes
and redirects, and configured with the [`script`](../config/project#script) setting (which is only a
string).
A command also supports merging during task inheritance, while a script does not and will always
replace values. Refer to the table below for more differences between the 2.
| | Command | Script |
| :--------------------------------------- | :------------------------ | :----------------- |
| Configured as | string, array | string |
| Inheritance merging | ✅ via `mergeArgs` option | ⚠️ always replaces |
| Additional args | ✅ via `args` setting | ❌ |
| Passthrough args (from CLI) | ✅ | ❌ |
| Multiple commands (with `&&` or `;`) | ❌ | ✅ |
| Pipes, redirects, etc | ❌ | ✅ |
| Always ran in a shell | ❌ | ✅ |
| Custom platform/toolchain | ✅ | ✅ |
| [Token](./token) functions and variables | ✅ | ✅ |
### Inheritance
View the official documentation on [task inheritance](./task-inheritance).
---
## Tokens
Tokens are variables and functions that can be used by [`command`](../config/project#command),
[`args`](../config/project#args), [`env`](../config/project#env) (>= v1.12),
[`inputs`](../config/project#inputs), and [`outputs`](../config/project#outputs) when configuring a
task. They provide a way of accessing file group paths, referencing values from other task fields,
and referencing metadata about the project and task itself.
## Functions
A token function is labeled as such as it takes a single argument, starts with an `@`, and is
formatted as `@name(arg)`. The following token functions are available, grouped by their
functionality.
:::caution
Token functions _must_ be the only content within a value, as they expand to multiple files. When
used in an `env` value, multiple files are joined with a comma (`,`).
:::
### File groups
These functions reference file groups by name, where the name is passed as the argument.
### `@group`
> Usable in `args`, `env`, `inputs`, and `outputs`.
The `@group(file_group)` token is a standard token that will be replaced with the file group items
as-is, for both file paths and globs. This token merely exists for reusability purposes.
```yaml
fileGroups:
storybook:
- '.storybook/**/*'
- 'src/**/*'
- '**/*.stories.*'
# Configured as
tasks:
build:
command: 'build-storybook'
inputs:
- '@group(storybook)'
start:
command: 'start-storybook'
inputs:
- '@group(storybook)'
# Resolves to
tasks:
build:
command: 'build-storybook'
inputs:
- '/path/to/project/.storybook/**/*'
- '/path/to/project/src/**/*'
- '/path/to/project/**/*.stories.*'
start:
command: 'start-storybook'
inputs:
- '/path/to/project/.storybook/**/*'
- '/path/to/project/src/**/*'
- '/path/to/project/**/*.stories.*'
```
### `@dirs`
> Usable in `args`, `env`, `inputs`, and `outputs`.
The `@dirs(file_group)` token will be replaced with an expanded list of directory paths, derived
from the file group of the same name. If a glob pattern is detected within the file group, it will
aggregate all directories found.
:::warning
This token walks the file system to verify each directory exists, and filters out those that don't.
If using within `outputs`, you're better off using [`@group`](#group) instead.
:::
```yaml
fileGroups:
lintable:
- 'src'
- 'tests'
- 'scripts'
- '*.config.js'
# Configured as
tasks:
lint:
command: 'eslint @dirs(lintable) --color'
inputs:
- '@dirs(lintable)'
# Resolves to
tasks:
lint:
command:
- 'eslint'
- 'src'
- 'tests'
- 'scripts'
- '--color'
inputs:
- '/path/to/project/src'
- '/path/to/project/tests'
- '/path/to/project/scripts'
```
### `@files`
> Usable in `args`, `env`, `inputs`, and `outputs`.
The `@files(file_group)` token will be replaced with an expanded list of file paths, derived from
the file group of the same name. If a glob pattern is detected within the file group, it will
aggregate all files found.
:::warning
This token walks the file system to verify each file exists, and filters out those that don't. If
using within `outputs`, you're better off using [`@group`](#group) instead.
:::
```yaml
fileGroups:
config:
- '*.config.js'
- 'package.json'
# Configured as
tasks:
build:
command: 'webpack build @files(config)'
inputs:
- '@files(config)'
# Resolves to
tasks:
build:
command:
- 'webpack'
- 'build'
- 'babel.config.js'
- 'webpack.config.js'
- 'package.json'
inputs:
- '/path/to/project/babel.config.js'
- '/path/to/project/webpack.config.js'
- '/path/to/project/package.json'
```
### `@globs`
> Usable in `args`, `env`, `inputs`, and `outputs`.
The `@globs(file_group)` token will be replaced with the list of glob patterns as-is, derived from
the file group of the same name. If a non-glob pattern is detected within the file group, it will be
ignored.
```yaml
fileGroups:
tests:
- 'tests/**/*'
- '**/__tests__/**/*'
# Configured as
tasks:
test:
command: 'jest --testMatch @globs(tests)'
inputs:
- '@globs(tests)'
# Resolves to
tasks:
test:
command:
- 'jest'
- '--testMatch'
- 'tests/**/*'
- '**/__tests__/**/*'
inputs:
- '/path/to/project/tests/**/*'
- '/path/to/project/**/__tests__/**/*'
```
### `@root`
> Usable in `args`, `env`, `inputs`, and `outputs`.
The `@root(file_group)` token will be replaced with the lowest common directory, derived from the
file group of the same name. If a glob pattern is detected within the file group, it will walk the
file system and aggregate all directories found before reducing.
```yaml
fileGroups:
sources:
- 'src/app'
- 'src/packages'
- 'src/scripts'
# Configured as
tasks:
format:
command: 'prettier --write @root(sources)'
inputs:
- '@root(sources)'
# Resolves to
tasks:
format:
command:
- 'prettier'
- '--write'
- 'src'
inputs:
- '/path/to/project/src'
```
> When there's no directies, or too many directories, this function will return the project root
> using `.`.
### `@envs`
> Usable in `inputs`.
The `@envs(file_group)` token will be replaced with all environment variables that have been
configured in the group of the provided name.
```yaml
fileGroups:
sources:
- 'src/**/*'
- '$NODE_ENV'
# Configured as
tasks:
build:
command: 'vite build'
inputs:
- '@envs(sources)'
# Resolves to
tasks:
build:
command: 'vite build'
inputs:
- '$NODE_ENV'
```
### Inputs & outputs
### `@in`
> Usable in `script` and `args` only.
The `@in(index)` token will be replaced with a single path, derived from
[`inputs`](../config/project#inputs) by numerical index. If a glob pattern is referenced by index,
the glob will be used as-is, instead of returning the expanded list of files.
```yaml
# Configured as
tasks:
build:
command:
- 'babel'
- '--copy-files'
- '--config-file'
- '@in(1)'
- '@in(0)'
inputs:
- 'src'
- 'babel.config.js'
# Resolves to
tasks:
build:
command:
- 'babel'
- '--copy-files'
- '--config-file'
- 'babel.config.js'
- 'src'
inputs:
- '/path/to/project/src'
- '/path/to/project/babel.config.js'
```
### `@out`
> Usable in `script` and `args` only.
The `@out(index)` token will be replaced with a single path, derived from
[`outputs`](../config/project#outputs) by numerical index.
```yaml
# Configured as
tasks:
build:
command:
- 'babel'
- '.'
- '--out-dir'
- '@out(0)'
outputs:
- 'lib'
# Resolves to
tasks:
build:
command:
- 'babel'
- '.'
- '--out-dir'
- 'lib'
outputs:
- '/path/to/project/lib'
```
### Miscellaneous
### `@meta`
> Usable in `command`, `script`, `args`, `env`, `inputs`, and `outputs` only.
The `@meta(key)` token can be used to access project metadata and will be replaced with a value
derived from [`project`](../config/project#project) in [`moon.*`](../config/project).
The top-level fields (like `name` and `description`) will be used as-is (no quotes). If the setting
is not defined, it will default to nothing or an empty string. For lists of values, they will be
joined with `,`.
Custom metadata defined in [`project`](../config/project#project) can also be accessed by key, but
will return a JSON stringified value. For example, a custom string value of `example` will be
stringified to `"example"` (with quotes).
```yaml
project:
title: 'example'
index: 123
# Configured as
tasks:
build:
script: 'build --name @meta(title) --index @meta(index)'
# Resolves to
tasks:
build:
script: 'build --name example --index 123'
```
## Variables
A token variable is a value that starts with `$` and is substituted to a value derived from the
current workspace, project, and task. And unlike token functions, token variables can be placed
_within_ content when necessary, and supports multiple variables within the same content.
### Environment
- `$arch` - The current host architecture, derived from the Rust
[`ARCH` constant](https://doc.rust-lang.org/std/env/consts/constant.ARCH.html).
- `$os` - The current operating system, derived from the Rust
[`OS` constant](https://doc.rust-lang.org/std/env/consts/constant.OS.html).
- `$osFamily` - The current operating system family, either `unix` or `windows`.
```yaml
# Configured as
tasks:
build:
command: 'example --arch $arch'
# Resolves to
tasks:
build:
command:
- 'example'
- '--arch'
- 'aarch64'
```
### Workspace
- `$workingDir` - The current working directory.
- `$workspaceRoot` - Absolute file path to the workspace root.
```yaml
# Configured as
tasks:
build:
command:
- 'example'
- '--cwd'
- '$workspaceRoot'
# Resolves to
tasks:
build:
command:
- 'example'
- '--cwd'
- '/path/to/repo'
```
### Project
Most values are derived from settings in [`moon.*`](../config/project). When a setting is not
defined, or does not have a config, the variable defaults to "unknown" (for enums) or an empty
string.
- `$language` Programming language the project is written in, as defined with
[`language`](../config/project#language).
- `$project` - ID of the project that owns the currently running task, as defined in
[`.moon/workspace.*`](../config/workspace).
- `$projectAlias` - Alias of the project that owns the currently running task.
- `$projectChannel` - The discussion channel for the project, as defined with
[`project.channel`](../config/project#channel).
- `$projectLayer` - The project layer, as defined with [`layer`](../config/project#layer).
- `$projectTitle` - The human-readable name of the project, as defined with
[`project.title`](../config/project#title).
- `$projectOwner` - The owner of the project, as defined with
[`project.owner`](../config/project#name).
- `$projectRoot` - Absolute file path to the project root.
- `$projectSource` - Relative file path from the workspace root to the project root, as defined in
[`.moon/workspace.*`](../config/workspace).
- `$projectStack` - The stack of the project, as defined with [`stack`](../config/project#stack).
```yaml
# Configured as
tasks:
build:
command: 'example debug $language'
# Resolves to
tasks:
build:
command:
- 'example'
- 'debug'
- 'node'
```
### Task
- `$target` - Fully-qualified target that is currently running.
- `$task` - ID of the task that is currently running. Does not include the project ID.
- `$taskToolchain` - The toolchain that task will run against, as defined in
[`moon.*`](../config/project).
- `$taskType` - The [type of task](./task#types), based on its configured settings.
```yaml
# Configured as
tasks:
build:
command: 'example $target'
# Resolves to
tasks:
build:
command:
- 'example'
- 'web:build'
```
### Date/Time
- `$date` - The current date in the format of `YYYY-MM-DD`.
- `$datetime` - The current date and time in the format of `YYYY-MM-DD_HH:MM:SS`.
- `$time` - The current time in the format of `HH:MM:SS`.
- `$timestamp` - The current date and time as a UNIX timestamp in seconds.
```yaml
# Configured as
tasks:
build:
command: 'example --date $date'
# Resolves to
tasks:
build:
command:
- 'example'
- '--date'
- '2023-03-17'
```
### VCS
- `$vcsBranch` - The current branch.
- `$vcsRepository` - The repository slug, in the format of `owner/repo`.
- `$vcsRevision` - The current revision (commit, etc).
```yaml
# Configured as
tasks:
build:
command: 'example --branch $vcsBranch'
# Resolves to
tasks:
build:
command:
- 'example'
- '--branch'
- 'master'
```
---
## Toolchain
The toolchain is an internal layer for downloading, installing, and managing tools (languages,
dependency managers, libraries, and binaries) that are required at runtime. We embrace this approach
over relying on these tools "existing" in the current environment, as it ensures the following
across any environment or machine:
- The version and enabled features of a tool are identical.
- Tools are isolated and unaffected by external sources.
- Builds are consistent, reproducible, and _hopefully_ deterministic.
Furthermore, this avoids a developer, pipeline, machine, etc, having to pre-install all the
necessary tools, _and_ to keep them in sync as time passes.
## How it works
The toolchain is built around [proto](/proto), our stand-alone multi-language version manager. moon
will piggyback of proto's toolchain found at `~/.proto` and reuse any tools available, or download
and install them if they're missing.
### Force disabling
The `MOON_TOOLCHAIN_FORCE_GLOBALS` environment variable can be set to `true` to force moon to use
tool binaries available on `PATH`, instead of downloading and installing them. This is useful for
pre-configured environments, like CI and Docker.
```shell
MOON_TOOLCHAIN_FORCE_GLOBALS=true
```
Additionally, the name of one or many tools can be passed to this variable to only force globals for
those tools, and use the toolchain for the remaining tools.
```shell
MOON_TOOLCHAIN_FORCE_GLOBALS=node,yarn
```
## Configuration
The tools that are managed by the toolchain are configured through the
[`.moon/toolchains.*`](../config/toolchain) file, but can be overridden in each project with
[`moon.*`](../config/project#toolchain).
### Version specification
As mentioned above, tools within the toolchain are managed _by version_ for consistency across
machines. These versions are configured on a per-tool basis in
[`.moon/toolchains.*`](../config/toolchain). So what kinds of versions are allowed?
- **Full versions** - A full version is a semantic version that is fully specified, such as `1.2.3`
or `2.0.0-rc.1`. This is the most common way to specify a version, and is preferred to avoid
subtle deviations.
- **Partial versions** - A partial version is a version that is either missing a patch number, minor
number, or both, such as `1.2` or `1`. These can also be represented with requirement syntax, such
as `^1.2` or `~1`. If using partials, we suggest having a major and minor number to reduce the
deviation of versions across machines.
- **Aliases** - An alias is a human-readable word that maps to a specific version. For example,
`latest` or `stable` maps to the latest version of a tool, or `canary` which maps to applicable
canary release, or even a completely custom alias like `berry`. Aliases are language specific, are
not managed by moon, and are not suggested for use since they can change at any time (or even
daily!).
This sounds great, but how exactly does this work? For full versions and aliases, it's straight
forward, as the resolved version is used as-is (assuming it's a legitimate version), and can be
found at `~/.proto/tools//`.
For partial versions, we first check locally installed versions for a match, by scanning
`~/.proto/tools/`. For example, if the requested version is `1.2` and we have `1.2.10`
installed locally, we'll use that version instead of downloading the latest `1.2.*` version.
Otherwise, we'll download the latest version that matches the partial version, and install it
locally.
---
## Workspace
A workspace is a directory that contains [projects](./project), manages a [toolchain](./toolchain),
runs [tasks](./task), and is coupled with a VCS repository. The root of a workspace is denoted by a
`.moon` folder.
By default moon has been designed for monorepos, but can also be used for polyrepos.
## Configuration
Configuration that's applied to the entire workspace is defined in
[`.moon/workspace.*`](../config/workspace).
---
## .moon/extensions
The `.moon/extensions.*` file configures extensions that can hook into pipeline events, or be
executed directly. This file is _optional_.
## `extends`
Defines one or many external `.moon/extensions.*`'s to extend and inherit settings from. Perfect for
reusability and sharing configuration across repositories and projects. When defined, this setting
must be an HTTPS URL _or_ relative file system path that points to a valid YAML document!
```yaml title=".moon/extensions.yml" {1}
extends: 'https://raw.githubusercontent.com/organization/repository/master/.moon/extensions.yml'
```
:::caution
Settings will be merged recursively for blocks, with values defined in the local configuration
taking precedence over those defined in the extended configuration.
:::
## How it works
A mapping of extensions that can be downloaded and executed with the [`moon ext`](../commands/ext)
command. An extension is a WASM plugin, and the location of the WASM file must be defined with the
`plugin` field, which requires a
[plugin locator string](../guides/wasm-plugins#configuring-plugin-locations).
```yaml title=".moon/extensions.yml" {2-5}
example:
plugin: 'file://./path/to/example.wasm'
# or
plugin: 'https://example.com/path/to/example.wasm'
```
Additionally, extensions support custom configuration that is passed to the WASM runtime when the
plugin is instantiated. This configuration is defined by inserting additional fields under the
extension name, relative to the `plugin` field. Each extension may have its own settings, so refer
to their documentation for more information.
```yaml title=".moon/extensions.yml" {2-5}
example:
plugin: 'file://./path/to/example.wasm'
setting1: true
setting2: 'abc'
```
## Supported extensions
View the [official guide](../guides/extensions) for all built-in extensions.
---
## Overview(Config)
## Supported formats
In moon, you can define configuration files in a variety of formats. We currently support the
following:
- JSON (`.json`)
- JSON with comments (`.jsonc`)
- [HCL](https://github.com/hashicorp/hcl) (`.hcl`)
- [Pkl](https://pkl-lang.org/) (`.pkl`)
- [TOML](https://toml.io/en/) (`.toml`)
- YAML (`.yml`, `.yaml`)
:::info
In moon v1, only YAML (`.yml`) and Pkl (`.pkl`) configuration files were supported.
:::
## Schema validation
We support schema validation for all configuration files through
[JSON Schema](https://json-schema.org/), even for formats that are not JSON (depends on tool/editor
support). To reference the schema for a specific configuration file, configure the `$schema`
property at the top of the file with the appropriate schema found at `.moon/cache/schemas`.
```yaml title=".moon/workspace.yml"
$schema: './cache/schemas/workspace.json'
```
```yaml title=".moon/extensions.yml"
$schema: './cache/schemas/extensions.json'
```
```yaml title=".moon/toolchains.yml"
$schema: './cache/schemas/toolchains.json'
```
```yaml title=".moon/tasks/all.yml"
$schema: '../cache/schemas/tasks.json'
```
```yaml title="moon.yml"
$schema: '../path/to/.moon/cache/schemas/project.json'
```
```yaml title="template.yml"
$schema: '../path/to/.moon/cache/schemas/template.json'
```
:::info
The schemas are automatically created when running a task. If they do not exist yet, you can run
[`moon sync config-schemas`](../commands/sync/config-schemas) to generate them manually.
:::
:::danger
In older versions of moon, the schema files were located at `https://moonrepo.dev/schemas`. These
URLs are now deprecated, as they do not support dynamic settings. Please update your `$schema`
references to point to the local schema files in `.moon/cache/schemas`.
:::
## Setup & usage
### Pkl
Pkl utilizes a client-server architecture, which means that the `pkl` binary must exist in the
environment for parsing and evaluating `.pkl` files. Jump over to the
[official documentation for instructions on how to install Pkl](https://pkl-lang.org/main/current/pkl-cli/index.html#installation).
If you are using [proto](/proto), you can install Pkl with the following commands.
```shell
proto plugin add pkl https://raw.githubusercontent.com/milesj/proto-plugins/refs/heads/master/pkl.toml
proto install pkl --pin
```
To start using Pkl in moon, simply:
- Install [Pkl](#installing-pkl) and the
[VS Code extension](https://pkl-lang.org/vscode/current/index.html).
- Create configs with the `.pkl` extension.
:::info
We highly suggest reading the Pkl
[language reference](https://pkl-lang.org/main/current/language-reference/index.html) and the
[standard library](https://pkl-lang.org/main/current/standard-library.html).
:::
#### Caveats and restrictions
Since this is an entirely new configuration format that is quite dynamic compared to YAML, there are
some key differences to be aware of!
- Only files are supported. Cannot use or extend from URLs.
- Each `.pkl` file is evaluated in isolation (loops are processed, variables assigned, etc). This
means that task inheritance and file merging cannot extend or infer this native functionality.
- `default` is a
[special feature](https://pkl-lang.org/main/current/language-reference/index.html#default-element)
in Pkl and cannot be used as a setting name. This only applies to
[`template.pkl`](../config/template#default), but can be worked around by using `defaultValue`
instead.
```pkl title="template.pkl"
variables {
["age"] {
type = "number"
prompt = "Age?"
defaultValue = 0
}
```
#### Example functionality
Loops and conditionals:
```pkl
tasks {
for (_os in List("linux", "macos", "windows")) {
["build-\(_os)"] {
command = "cargo"
args = List(
"--target",
if (_os == "linux") "x86_64-unknown-linux-gnu"
else if (_os == "macos") "x86_64-apple-darwin"
else "i686-pc-windows-msvc",
"--verbose"
)
options {
os = _os
}
}
}
}
```
Local variables:
```pkl
local _sharedInputs = List("src/**/*")
tasks {
["test"] {
// ...
inputs = List("tests/**/*") + _sharedInputs
}
["lint"] {
// ...
inputs = List("**/*.graphql") + _sharedInputs
}
}
```
---
## moon
The `moon.*` configuration file _is not required_ but can be used to define additional metadata for
a project, override inherited tasks, and more at the project-level. When used, this file must exist
in a project's root, as configured in [`projects`](./workspace#projects).
## `dependsOn`
Explicitly defines _other_ projects that _this_ project depends on, primarily when generating the
project and task graphs. The most common use case for this is building those projects _before_
building this one. When defined, this setting requires an array of project names, which are the keys
found in the [`projects`](./workspace#projects) map.
```yaml title="moon.yml"
dependsOn:
- 'apiClients'
- 'designSystem'
```
A dependency object can also be defined, where a specific `scope` can be assigned, which accepts
"production" (default), "development", "build", or "peer".
```yaml title="moon.yml"
dependsOn:
- id: 'apiClients'
scope: 'production'
- id: 'designSystem'
scope: 'peer'
```
> Learn more about [implicit and explicit dependencies](../concepts/project#dependencies).
## Metadata
## `id`
Overrides the name (identifier) of the project, which was configured in or derived from the
[`projects`](./workspace#projects) setting in [`.moon/workspace.*`](./workspace). This setting is
useful when using glob based project location, and want to avoid using the folder name as the
project name.
```yaml title="moon.yml"
id: 'custom-id'
```
:::info
All references to the project must use the new identifier, including project and task dependencies.
:::
## `language`
The primary programming language the project is written in. This setting is required for
[task inheritance](./tasks), editor extensions, and more. Supports the following values:
- `bash` - A [Bash]() based project (Unix only).
- `batch` - A [Batch](https://en.wikibooks.org/wiki/Windows_Batch_Scripting)/PowerShell based
project (Windows only).
- `go` - A [Go](https://go.dev/) based project.
- `javascript` - A [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript) based
project.
- `php` - A [PHP](https://www.php.net) based project.
- `python` - A [Python](https://www.python.org/) based project.
- `ruby` - A [Ruby](https://www.ruby-lang.org/en/) based project.
- `rust` - A [Rust](https://www.rust-lang.org/) based project.
- `typescript` - A [TypeScript](https://www.typescriptlang.org/) based project.
- `unknown` (default) - When not configured or inferred.
- `*` - A custom language. Values will be converted to kebab-case.
```yaml title="moon.yml"
language: 'javascript'
# Custom
language: 'kotlin'
```
> For convenience, when this setting is not defined, moon will attempt to detect the language based
> on configuration files found in the project root. This only applies to non-custom languages!
## `owners`
Defines ownership of source code within the current project, by mapping file system paths to owners.
An owner is either a user, team, or group.
Currently supports
[GitHub](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners),
[GitLab](https://docs.gitlab.com/ee/user/project/codeowners/reference.html), and
[Bitbucket](https://marketplace.atlassian.com/apps/1218598/code-owners-for-bitbucket?tab=overview&hosting=cloud)
(via app).
### `customGroups`
When using the
[Code Owners for Bitbucket](https://marketplace.atlassian.com/apps/1218598/code-owners-for-bitbucket?tab=overview&hosting=cloud)
app, this setting provides a way to define custom groups that will be injected at the top of the
`CODEOWNERS` file. These groups _must_ be unique across all projects.
```yaml title="moon.yml" {2,3}
owners:
customGroups:
'@@@backend': ['@"user name"', '@@team']
```
### `defaultOwner`
The default owner for all [`paths`](#paths). This setting is optional in some cases but helps to
avoid unnecessary repetition.
```yaml title="moon.yml" {2}
owners:
defaultOwner: '@frontend'
```
### `optional`
For GitLab, marks the project's
[code owners section](https://docs.gitlab.com/ee/user/project/codeowners/reference.html#optional-sections)
as optional. Defaults to `false`.
```yaml title="moon.yml" {2}
owners:
optional: true
```
### `paths`
The primary setting for defining ownership of source code within the current project. This setting
supports 2 formats, the first being a list of file paths relative from the current project. This
format requires [`defaultOwner`](#defaultowner) to be defined, and only supports 1 owner for every
path (the default owner).
```yaml title="moon.yml" {3-6}
owners:
defaultOwner: '@frontend'
paths:
- '**/*.ts'
- '**/*.tsx'
- '*.config.js'
```
The second format provides far more granularity, allowing for multiple owners per path. This format
requires a map, where the key is a file path relative from the current project, and the value is a
list of owners. Paths with an empty list of owners will fallback to [`defaultOwner`](#defaultowner).
```yaml title="moon.yml" {3-6}
owners:
defaultOwner: '@frontend'
paths:
'**/*.rs': ['@backend']
'**/*.js': []
'*.config.js': ['@frontend', '@frontend-infra']
```
> The syntax for owners is dependent on the provider you are using for version control (GitHub,
> GitLab, Bitbucket). moon provides no validation or guarantees that these are correct.
### `requiredApprovals`
Requires a specific number of approvals for a pull/merge request to be satisfied. Defaults to `1`.
- For Bitbucket, defines the
[`Check()` condition](https://docs.mibexsoftware.com/codeowners/merge-checks#MergeChecks-2.MergeChecks:HowmanyoftheseCodeOwnersneedtoapprovebeforeapullrequestcanbemerged?)
when using a [`defaultOwner`](#defaultowner).
- For GitLab, defines a requirement on the
[code owners section](https://docs.gitlab.com/ee/user/project/codeowners/reference.html#sections-requiring-multiple-approvals).
```yaml title="moon.yml" {2}
owners:
requiredApprovals: 2
```
## `layer`
The layer within a [stack](#stack). Supports the following values:
- `application` - An application of any kind.
- `automation` - An automated testing suite, like E2E, integration, or visual tests.
- `configuration` - Configuration files or infrastructure.
- `library` - A self-contained, shareable, and publishable set of code.
- `scaffolding` - Templates or generators for scaffolding.
- `tool` - An internal tool, CLI, one-off script, etc.
- `unknown` (default) - When not configured.
```yaml title="moon.yml"
layer: 'application'
```
:::info
The project layer is used in [task inheritance](../concepts/task-inheritance),
[constraints and boundaries](./workspace#constraints), editor extensions, and more!
:::
## `project`
The `project` setting defines metadata about the project itself.
```yaml title="moon.yml"
project:
title: 'moon'
description: 'A monorepo management tool.'
channel: '#moon'
owner: 'infra.platform'
maintainers: ['miles.johnson']
```
The information listed within `project` is purely informational and primarily displayed within the
CLI. However, this setting exists for you, your team, and your company, as a means to identify and
organize all projects. Feel free to build your own tooling around these settings!
### `channel`
The Slack, Discord, Teams, IRC, etc channel name (with leading #) in which to discuss the project.
### `description`
A description of what the project does and aims to achieve. Be as descriptive as possible, as this
is the kind of information search engines would index on.
### `maintainers`
A list of people/developers that maintain the project, review code changes, and can provide support.
Can be a name, email, LDAP name, GitHub username, etc, the choice is yours.
### `title`
A human readable name of the project. This is _different_ from the unique project name configured in
[`projects`](./workspace#projects).
### `owner`
The team or organization that owns the project. Can be a title, LDAP name, GitHub team, etc. We
suggest _not_ listing people/developers as the owner, use [maintainers](#maintainers) instead.
### Custom fields
Additional fields can be configured as custom metadata to associate to this project. Supports all
value types that are valid JSON.
```yaml title="moon.yml"
project:
# ...
deprecated: true
```
## `stack`
The technology stack this project belongs to, primarily for categorization. Supports the following
values:
- `backend` - Server-side APIs, etc.
- `data` - Data sources, database layers, etc.
- `frontend` - Client-side user interfaces, etc.
- `infrastructure` - Cloud/server infrastructure, Docker, etc.
- `systems` - Low-level systems programming.
- `unknown` (default) - When not configured.
```yaml title="moon.yml"
stack: 'frontend'
```
:::info
The project stack is also used in [constraints and boundaries](./workspace#constraints)!
:::
## `tags`
Tags are a simple mechanism for categorizing projects. They can be used to group projects together
for [easier querying](../commands/query/projects), enforcing of
[project boundaries and constraints](./workspace#constraints),
[task inheritance](../concepts/task-inheritance), and more.
```yaml title="moon.yml"
tags:
- 'react'
- 'prisma'
```
## Integrations
## `docker`
Configures Docker integration for the current project.
### `file`
Configures the `Dockerfile` generation process when [`moon docker file`](../commands/docker/file) is
executed.
#### `buildTask`
The name of a task within the current project that will be used for building the project before
running it. If not defined, does nothing.
```yaml title="moon.yml" {3}
docker:
file:
buildTask: 'build'
```
#### `image`
The Docker image to use in the base stage. Defaults to an image based on the first detected
toolchain.
```yaml title="moon.yml" {3}
docker:
file:
image: 'node:latest'
```
#### `runPrune`
Run the `moon docker prune` command after building the project, but before starting it. Defaults to
`true`.
```yaml title="moon.yml" {3}
docker:
file:
runPrune: false
```
#### `runSetup`
Run the `moon docker setup` command after scaffolding, but before building the project. Defaults to
`true`.
```yaml title="moon.yml" {3}
docker:
file:
runSetup: false
```
#### `startTask`
The name of a task within the current project that will run the project after it has been built (if
required). This task will be used as `CMD` within the `Dockerfile`.
```yaml title="moon.yml" {3}
docker:
file:
startTask: 'start'
```
#### `template`
A custom template file, relative from the workspace root, to use when rendering the `Dockerfile`.
Powered by Tera.
```yaml title="moon.yml" {3}
docker:
file:
template: 'templates/Dockerfile.tera'
```
### `scaffold`
Configures aspects of the Docker scaffolding process when
[`moon docker scaffold`](../commands/docker/scaffold) is executed. Only applies to the
[sources skeleton](../commands/docker/scaffold#sources).
#### `configsPhaseGlobs`
List of globs in which to copy project-relative files into the `.moon/docker/configs` skeleton. When
not defined, defaults to `**/*`. Applies to both project and workspace level scaffolding.
```yaml title="moon.yml" {3,4}
docker:
scaffold:
configsPhaseGlobs:
- '*.json'
```
#### `sourcesPhaseGlobs`
List of globs in which to copy project-relative files into the `.moon/docker/sources` skeleton. When
not defined, defaults to `**/*`. Applies to both project and workspace level scaffolding.
```yaml title="moon.yml" {3,4}
docker:
scaffold:
sourcesPhaseGlobs:
- 'src/**/*'
```
## Tasks
## `env`
The `env` field is map of strings that are passed as environment variables to _all tasks_ within the
current project. Project-level variables will not override task-level variables of the same name.
```yaml title="moon.yml"
env:
NODE_ENV: 'production'
```
> View the task [`env`](#env-1) setting for more usage examples and information.
## `fileGroups`
Defines [file groups](../concepts/file-group) to be used by local tasks. By default, this setting
_is not required_ for the following reasons:
- File groups are an optional feature, and are designed for advanced use cases.
- File groups defined in [`.moon/tasks/**/*`](./tasks) will be inherited by all projects.
When defined this setting requires a map, where the key is the file group name, and the value is a
list of [globs or file paths](../concepts/file-pattern), or environment variables. Globs and paths
are [relative to a project](../concepts/file-pattern#project-relative) (even when defined
[globally](./tasks)).
```yaml title="moon.yml"
# Example groups
fileGroups:
configs:
- '*.config.{js,cjs,mjs}'
- '*.json'
sources:
- 'src/**/*'
- 'types/**/*'
tests:
- 'tests/**/*'
- '**/__tests__/**/*'
assets:
- 'assets/**/*'
- 'images/**/*'
- 'static/**/*'
- '**/*.{scss,css}'
```
Once your groups have been defined, you can reference them within [`args`](#args),
[`inputs`](#inputs), [`outputs`](#outputs), and more, using
[token functions and variables](../concepts/token).
```yaml title="moon.yml"
tasks:
build:
command: 'vite build'
inputs:
- '@group(configs)'
- '@group(sources)'
```
## `tasks`
Tasks are actions that are ran within the context of a [project](../concepts/project), and commonly
wrap an npm binary or system command. This setting requires a map, where the key is a unique name
for the task, and the value is an object of task parameters.
```yaml title="moon.yml"
tasks:
format:
command: 'prettier'
lint:
command: 'eslint'
test:
command: 'jest'
typecheck:
command: 'tsc'
```
### `extends`
The `extends` field can be used to extend the settings from a sibling task within the same project,
or [inherited from the global scope](../concepts/task-inheritance). This is useful for composing
similar tasks with different arguments or options.
When extending another task, the same
[merge strategies](../concepts/task-inheritance#merge-strategies) used for inheritance are applied.
```yaml title="moon.yml" {8}
tasks:
lint:
command: 'eslint .'
inputs:
- 'src/**/*'
lint-fix:
extends: 'lint'
args: '--fix'
preset: 'utility'
```
### `description`
A human-readable description of what the task does. This information is displayed within the
[`moon project`](../commands/project) and [`moon task`](../commands/task) commands.
```yaml title="moon.yml" {3}
tasks:
build:
description: 'Builds the project using Vite'
command: 'vite build'
```
### `command`
The `command` field is a _single_ command to execute for the task, including the command binary/name
(must be first) and any optional [arguments](#args). This field supports task inheritance and
merging of arguments.
This setting can be defined using a string, or an array of strings. We suggest using arrays when
dealing with many args, or the args string cannot be parsed easily.
```yaml title="moon.yml" {4,6-9}
tasks:
format:
# Using a string
command: 'prettier --check .'
# Using an array
command:
- 'prettier'
- '--check'
- '.'
```
:::info
If you need to support pipes, redirects, or multiple commands, use [`script`](#script) instead.
Learn more about [commands vs scripts](../concepts/task#commands-vs-scripts).
:::
### `args`
The `args` field is a collection of _additional_ arguments to append to the [`command`](#command)
when executing the task. This field exists purely to provide arguments for
[inherited tasks](./tasks#tasks).
This setting can be defined using a string, or an array of strings. We suggest using arrays when
dealing with many args, or the args string cannot be parsed easily.
```yaml title="moon.yml" {5,7-10}
tasks:
test:
command: 'jest'
# Using a string
args: '--color --maxWorkers 3'
# Using an array
args:
- '--color'
- '--maxWorkers'
- '3'
```
However, for the array approach to work correctly, each argument _must_ be its own distinct item,
including argument values. For example:
```yaml title="moon.yml"
tasks:
test:
command: 'jest'
args:
# Valid
- '--maxWorkers'
- '3'
# Also valid
- '--maxWorkers=3'
# Invalid
- '--maxWorkers 3'
```
### `checks`
The `checks` field is a list of shell scripts that are executed _before_ the task runs. Depending on
the type of check and the result of its script, the task may fail, be skipped, or continue as
normal. This is useful for asserting preconditions, avoiding redundant work, or invalidating the
cache based on external state.
There are three types of checks, denoted by the `check` field. When a check is defined as a plain
string, it is treated as a [`requirement`](#requirement) by default.
```yaml title="moon.yml" {4-12}
tasks:
deploy:
command: './deploy.sh'
checks:
# A string is shorthand for a requirement
- 'command -v aws'
# Or the expanded object form
- check: 'condition'
script: './scripts/already-deployed.sh'
- check: 'fingerprint'
script: 'aws --version'
```
Checks support [task inheritance](../concepts/task-inheritance), and the merge behavior can be
customized with the [`mergeChecks`](#mergechecks) option.
#### `requirement`
A `requirement` check must _pass_ (exit code 0) for the task to run. If the script fails, the task
fails and does _not_ execute. This is the default type when a check is defined as a string.
```yaml title="moon.yml" {5-7}
tasks:
build:
command: 'cargo build'
checks:
- check: 'requirement'
script: 'rustc --version'
```
#### `condition`
A `condition` check is used to _skip_ a task. When _all_ condition checks pass (exit code 0), the
task is skipped instead of running. If any condition fails, the task runs as normal. This is useful
for idempotency checks, where re-running the task would be redundant.
```yaml title="moon.yml" {5-7}
tasks:
migrate:
command: './migrate.sh'
checks:
- check: 'condition'
script: './scripts/is-migrated.sh'
```
#### `fingerprint`
A `fingerprint` check always runs alongside the task, and its output is mixed into the task's
[hash](../concepts/cache). This invalidates the cache when the script's output changes, even if none
of the task's declared [`inputs`](#inputs) changed — useful for tracking external state like tool
versions.
The `hash` field controls what portion of the script's execution is hashed. It accepts a boolean
(hash all output), or one of `exit-code`, `stdout`, or `stderr`. Defaults to `true`.
```yaml title="moon.yml" {5-8}
tasks:
build:
command: 'vite build'
checks:
- check: 'fingerprint'
script: 'node --version'
# Only hash stdout, not the exit code or stderr
hash: 'stdout'
```
### `deps`
The `deps` field is a list of other tasks (known as [targets](../concepts/target)), either within
this project or found in another project, that will be executed _before_ this task. It achieves this
by generating a directed task graph based on the project graph.
```yaml title="moon.yml" {4-8}
tasks:
build:
command: 'webpack'
deps:
- 'apiClients:build'
- 'designSystem:build'
# A task within the current project
- 'codegen'
```
#### Args & env
Furthermore, for each dependency target, you can configure additional command line arguments and
environment variables that'll be passed to the dependent task when it is ran. The `args` field
supports a list of strings, while `env` is an object of key-value pairs.
```yaml title="moon.yml" {4-8}
tasks:
build:
command: 'webpack'
deps:
- target: 'apiClients:build'
args: ['--env', 'production']
env:
NODE_ENV: 'production'
```
> Dependencies of inherited tasks will be excluded and renamed according to the
> [`workspace.inheritedTasks`](#inheritedtasks) setting. This process _only_ uses filters from the
> current project, and not filters from dependent projects. Furthermore, `args` and `env` are not
> deeply merged.
#### Optional
By default, all dependencies are required to exist when tasks are being built and expanded, but this
isn't always true when dealing with composition and inheritance. For dependencies that may not exist
based on what's inherited, you can mark it as `optional`.
```yaml title="moon.yml" {4-6}
tasks:
build:
command: 'webpack'
deps:
- target: 'apiClients:build'
optional: true
```
#### Cache strategy
The `cacheStrategy` field controls how a dependency's changes invalidate the current task's cache.
When omitted, the strategy is chosen for you based on whether the dependency declares outputs:
- A dependency **with** outputs (e.g. a `build` task) defaults to `hash` — any change to the
dependency invalidates this task.
- A dependency **without** outputs (e.g. a `lint` or `test` task) defaults to `ignored` — the
dependency is treated as a sequencing edge only and its changes never invalidate this task.
You can override the default explicitly with one of:
- `hash` - Use the dependency task's hash for cache invalidation. The current task is invalidated
whenever the dependency changes (inputs, command, args, env, etc.).
- `ignored` - Ignore the dependency task's hash for cache invalidation. The current task is
**never** invalidated by this dependency's changes.
- `outputs` - Use the dependency task's outputs instead of its hash for cache invalidation. The
current task is only invalidated when the dependency's outputs change, not when its inputs change.
Useful for build tasks where you only care about a dependency's outputs, not what triggered the
dependency to run.
```yaml title="moon.yml" {4-6}
tasks:
build:
command: 'webpack'
deps:
- target: 'apiClients:build'
cacheStrategy: 'outputs'
```
The most common use case for `cacheStrategy: outputs` is when you have build dependencies. Instead
of rebuilding a project whenever an upstream task's inputs change, you rebuild only when the
upstream task's outputs change:
```yaml title="moon.yml"
tasks:
build:
command: 'npm run build'
inputs:
- 'src/**/*'
outputs:
- 'dist/**/*'
deps:
# Only invalidate if the dependency's dist/ changes, not if its src/ or other inputs change
- target: '^:build'
cacheStrategy: 'outputs'
```
### `env`
The `env` field is map of strings that are passed as environment variables when running the command.
Variables defined here will take precedence over those loaded with [`envFile`](#envfile).
```yaml title="moon.yml" {4,5}
tasks:
build:
command: 'webpack'
env:
NODE_ENV: 'production'
```
Variables also support substitution using the syntax `${VAR_NAME}`. When using substitution, only
variables in the current process can be referenced, and not those currently defined in `env`.
```yaml title="moon.yml" {4,5}
tasks:
build:
command: 'webpack'
env:
APP_TARGET: '${REGION}-${ENVIRONMENT}'
```
### `inputs`
The `inputs` field is a list of sources that calculate whether to execute this task based on the
environment and files that have been touched since the last time the task has been ran. If _not_
defined or inherited, then all files within a project are considered an input (`**/*`), excluding
root-level tasks.
Inputs support the following source types:
- Environment variables
- Environment variable wildcards
- Files, folders, and globs
- [Token functions and variables](../concepts/token)
```yaml title="moon.yml" {4-12}
tasks:
lint:
command: 'eslint'
inputs:
# Config files anywhere within the project
- '**/.eslintignore'
- '**/.eslintrc.js'
# Config files at the workspace root
- '/.eslintignore'
- '/.eslintrc.js'
# Tokens
- '$projectRoot'
- '@group(sources)'
```
#### Environment variables
Environment variables can be used as inputs and must start with a `$`. Wildcard variables can use
`*` to match any character.
```yaml title="moon.yml"
tasks:
example:
inputs:
- '$FOO_CACHE'
- '$FOO_*'
```
:::caution
When using an environment variable, we assume _it's not defined_ by default, and will trigger an
affected state when it _is_ defined. If the environment variable always exists, then the task will
always run and bypass the cache.
:::
#### File paths
File paths support
[project and workspace relative file/folder patterns](../concepts/file-pattern#project-relative).
They can be defined as a literal path, or a `file://` URI ,
or as an object with a `file` property . Additionally, the
following parameters are supported as a URI query or as object fields:
- `content`, `match`, `matches` (`string`) - When determining affected state, will match against the
file's content using the defined regex pattern, instead of relying on file existence.
- `optional` (`boolean`) - When hashing and set to `true` and the file is missing, will not log a
warning. When set to `false` and the file is missing, will fail with an error. Defaults to logging
a warning.
```yaml title="moon.yml"
tasks:
example:
inputs:
# Literal paths
- 'project/relative/file.js'
- '/workspace/relative/file.js'
# Using file protocol
- 'file://project/relative/file.js?optional'
- 'file:///workspace/relative/file.js?content=a|b|c'
# Using an object
- file: 'project/relative/file.js'
optional: true
- file: '/workspace/relative/file.js'
content: 'a|b|c'
```
#### File groups
A file group input will reference the defined files/globs within from a file group in the current
project. It can be defined with a `group://` URI, or as an object with a `group` property.
Additionally, the following parameters are supported as a URI query or as object fields:
- `format`, `as` (`string`) - The format in which to gather the file group results. Supported values
are `static` (default), `files`, `dirs`, `globs`, `envs`, and `root`.
```yaml title="moon.yml"
fileGroups:
sources:
- 'src/**/*'
tasks:
build:
# ...
inputs:
# Using group protocol
- 'group://sources?format=dirs'
# Using an object
- group: 'sources'
format: 'dirs'
```
#### Glob patterns
Glob patterns support
[project and workspace relative file/folder patterns](../concepts/file-pattern#project-relative).
They can be defined as a literal path, or a `glob://` URI ,
or as an object with a `glob` property . Additionally, the
following parameters are supported as a URI query or as object fields:
- `cache` (`boolean`) - When gathering inputs for hashing, defines whether the glob results should
be cached for the duration of the moon process. Defaults to `true`.
```yaml title="moon.yml"
tasks:
example:
inputs:
# Literal paths
- 'project/relative/file.*'
- '/workspace/relative/**/*'
# Using glob protocol
- 'glob://project/relative/file.*?cache=false'
- 'glob:///workspace/relative/**/*?cache'
# Using an object
- glob: 'project/relative/file.*'
cache: false
- glob: '/workspace/relative/**/*'
```
Globs can also be negated by prefixing the path with `!`, which will exclude all files that match
the glob.
```yaml title="moon.yml"
tasks:
example:
inputs:
- '!**/*.md'
- 'glob://!/workspace/relative/**/*'
- glob: '!/workspace/relative/**/*'
```
:::warning
Glob patterns that contain `?`, for example `*.tsx?`, cannot be used in URI format, as it conflicts
with the query string syntax. Use the path or object format instead.
:::
:::danger
Be aware that files that match the glob, but are ignored via `.gitignore` (or similar), will _not_
be considered an input. To work around this, use explicit file inputs.
:::
#### External projects
Tasks can also depend on files and globs from other projects within the same workspace. This is
useful for handling cross-project relationships without needing to define explicit task
dependencies.
External projects can be defined as a `project://` URI, or as an object with a `project` property,
both of which require a project identifier, or `^` for all dependent projects. Additionally, the
following parameters are supported as a URI query or as object fields:
- `group`, `fileGroup` (`id`) - The name of a file group within the external project in which file
and glob patterns will be used for matching. Takes precedence over `filter`.
- `filter` (`string[]`) - A list of
[project relative glob patterns](../concepts/file-pattern#project-relative) that will be used for
matching.
If neither `group` nor `filter` are defined, all files within the external project are considered a
match (`**/*`).
```yaml title="moon.yml"
tasks:
example:
inputs:
# Using project protocol
- 'project://foo'
- 'project://bar?group=sources'
- 'project://baz?filter=src/**/*'
# Using an object
- project: 'foo'
- project: 'bar'
group: 'sources'
- project: 'baz'
filter: ['src/**/*']
```
### `outputs`
The `outputs` field is a list of [files and folders](../concepts/file-pattern#project-relative) that
are _created_ as a result of executing this task, typically from a build or compilation related
task. Outputs are necessary for [incremental caching and hydration](../concepts/cache). If you'd
prefer to avoid that functionality, omit this field.
#### File paths
File paths support
[project and workspace relative file/folder patterns](../concepts/file-pattern#project-relative).
They can be defined as a literal path, or a `file://` URI ,
or as an object with a `file` property . Additionally, the
following parameters are supported as a URI query or as object fields:
- `optional` (`boolean`) - When archiving and set to `true` and the file is missing, will not fail
with a missing output error. Defaults to `false`.
```yaml title="moon.yml"
tasks:
example:
inputs:
# Literal paths
- 'build/'
# Using file protocol
- 'file://build/'
# Using an object
- file: 'build/'
optional: true
```
#### Glob patterns
Glob patterns support
[project and workspace relative file/folder patterns](../concepts/file-pattern#project-relative).
They can be defined as a literal path, or a `glob://` URI ,
or as an object with a `glob` property . Additionally, the
following parameters are supported as a URI query or as object fields:
- `optional` (`boolean`) - When archiving and set to `true` and the glob produced no results, will
not fail with a missing output error. Defaults to `false`.
```yaml title="moon.yml"
tasks:
example:
inputs:
# Literal paths
- 'build/**/*.js'
- '!build/internal.js'
# Using glob protocol
- 'glob://build/**/*.js'
# Using an object
- glob: 'build/**/*.js'
```
:::warning
Glob patterns that contain `?`, for example `*.tsx?`, cannot be used in URI format, as it conflicts
with the query string syntax. Use the path or object format instead.
:::
:::danger
When using globs and moon hydrates an output (a cache hit), all files not matching the glob will be
**deleted**. Ensure that all files critical for the build to function correctly are included.
:::
### `preset`
Applies the chosen preset to the task. A preset defines a collection of task options that will be
inherited as the default, and can then be overridden within the task itself. The following presets
are available:
- `server`
- [`cache`](#cache) -> Turned off
- [`outputStyle`](#outputstyle) -> Set to "stream"
- [`persistent`](#persistent) -> Turned on
- [`runInCI`](#runinci) -> Turned off
- `utility`
- [`cache`](#cache) -> Turned off
- [`interactive`](#interactive) -> Turned on
- [`outputStyle`](#outputstyle) -> Set to "stream"
- [`persistent`](#persistent) -> Turned off
- [`runInCI`](#runinci) -> Skipped
Tasks named "dev", "start", or "serve" are marked as `server` automatically.
```yaml title="moon.yml" {5}
tasks:
dev:
command: 'webpack server'
preset: 'server'
```
### `script`
The `script` field is _one or many_ commands to execute for the task, with support for pipes,
redirects, and more. This field does _not_ support task inheritance merging, and can only be defined
with a string.
If defined, will supersede [`command`](#command) and [`args`](#args).
```yaml title="moon.yml" {4,6,8,10}
tasks:
exec:
# Single command
script: 'cp ./in ./out'
# Multiple commands
script: 'rm -rf ./out && cp ./in ./out'
# Pipes
script: 'ps aux | grep 3000'
# Redirects
script: './gen.sh > out.json'
```
:::info
If you need to support merging during task inheritance, use [`command`](#command) instead. Learn
more about [commands vs scripts](../concepts/task#commands-vs-scripts).
:::
### `tags`
Tags are a simple mechanism for categorizing tasks. They can be used to group tasks together for
[easier querying](../commands/query/tasks), referencing within targets, and more.
```yaml title="moon.yml"
tasks:
lint:
command: 'eslint'
tags:
- 'quality'
- 'ci'
```
### `toolchains`
The `toolchain` field defines additional [toolchain(s)](../concepts/toolchain) the command runs on,
where to locate its executable, and more. By default, moon will set to a value based on the
project's [`language`](#language), default [`toolchains.default`](#toolchain-1), or via detection.
```yaml title="moon.yml" {4}
tasks:
env:
command: 'printenv'
toolchains: 'system'
```
This setting also supports multiple values.
```yaml title="moon.yml" {4}
tasks:
build:
command: 'npm run build'
toolchains: ['javascript', 'node', 'npm']
```
### `options`
The `options` field is an object of configurable options that can be used to modify the task and its
execution. The following fields can be provided, with merge related fields supporting all
[merge strategies](../concepts/task-inheritance#merge-strategies).
```yaml title="moon.yml"
tasks:
typecheck:
command: 'tsc --noEmit'
options:
mergeArgs: 'replace'
runFromWorkspaceRoot: true
```
#### `affectedFiles`
When enabled and the [`--affected` option](../run-task#running-based-on-affected-files-only) is
provided, all affected files that match this task's [`inputs`](#inputs) will be passed as relative
file paths as command line arguments, and as a `MOON_AFFECTED_FILES` environment variable.
If there are no affected files, `.` (current directory) will be passed instead for arguments, and an
empty value for the environment variable. This functionality can be changed with the
[`affectedPassInputs`](#affectedpassinputs) setting.
```yaml title="moon.yml" {5,7,9}
tasks:
lint:
command: 'eslint'
options:
affectedFiles: true
# Only pass args
affectedFiles: 'args'
# Only set env var
affectedFiles: 'env'
```
:::caution
When using this option, ensure that explicit files or `.` _are not present_ in the [`args`](#args)
list. Furthermore, this functionality will only work if the task's command supports an arbitrary
list of files being passed as arguments.
:::
This setting also supports an object format with additional parameters. The `pass` field is
required, which accepts a value described above.
```yaml title="moon.yml" {5,7,9}
tasks:
lint:
command: 'eslint'
options:
affectedFiles:
pass: 'args'
```
The following additional parameters are supported:
- `filter` (`boolean`) - A list of glob patterns to filter the
affected files list before passing to the task. Globs must start with `**` to match against
absolute paths.
- `ignoreProjectBoundary` (`boolean`) - When matching affected
files, ignore the project boundary and include workspace relative files. Otherwise, only files
within the project are matched. Defaults to `false`.
- `passDotWhenNoResults` (`boolean`) - When no affected files are
found, will pass `.` instead of an empty or no value. Defaults to `true`.
- `passInputsWhenNoMatch` (`boolean`) - When no affected files are found, will pass all configured
[`inputs`](#inputs) as relative file paths instead. Defaults to `false`.
#### `allowFailure`
Allows a task to fail without failing the entire pipeline. When enabled, the following changes
occur:
- Other tasks _cannot_ depend on this task, as we can't ensure it's side-effect free.
- For [`moon run`](../commands/run), the process will not bail early and will run to completion.
- For [`moon ci`](../commands/ci), the process will not exit with a non-zero exit code, if the only
failing tasks are allowed to fail.
```yaml title="moon.yml" {5}
tasks:
lint:
command: 'eslint'
options:
allowFailure: true
```
#### `cache`
Whether to cache the task's execution result using our [smart hashing](../concepts/cache#hashing)
system. If disabled, _will not_ create a cache hash, and _will not_ persist a task's
[outputs](#outputs). Supports the following values:
- `true` (default) - Cache the task's output.
- `false` - Do not cache the task's output.
- `local` - Only cache locally.
- `remote` - Only cache [remotely](../guides/remote-cache).
We suggest disabling caching when defining cleanup tasks, one-off scripts, or file system heavy
operations.
```yaml title="moon.yml" {5}
tasks:
clean:
command: 'rm -rf ./temp'
options:
cache: false
```
#### `cacheKey`
A custom key to include in the cache and task hashing process. Can be used to invalidate local and
remote caches.
```yaml title="moon.yml" {5}
tasks:
build:
command: 'some-costly-build'
options:
cacheKey: 'v1'
```
#### `cacheLifetime`
The lifetime in which a [cached task](#cache) will live before being marked as stale and re-running.
This applies to a task even if it does not produce [outputs](#outputs).
The lifetime can be configured in a human-readable string format, for example, `1 day`, `3 hr`,
`1m`, etc. If the lifetime is not defined, the cache will live forever, or until the task inputs are
touched.
```yaml title="moon.yml" {5}
tasks:
build:
command: 'some-costly-build'
options:
cacheLifetime: '1 day'
```
> String formats are powered by the
> [humantime](https://docs.rs/humantime/2.1.0/humantime/fn.parse_duration.html) crate.
#### `envFile`
A boolean or path to a `.env` file (also know as dotenv file) that defines a collection of
[environment variables](#env-1) for the current task. Variables will be loaded on project creation,
but will _not_ override those defined in [`env`](#env-1).
Variables defined in the file support value substitution/expansion by wrapping the variable name in
curly brackets, such as `${VAR_NAME}`.
```yaml title="moon.yml" {6,8,10}
tasks:
build:
command: 'webpack'
options:
# Defaults to .env
envFile: true
# Or
envFile: '.env.production'
# Or from the workspace root
envFile: '/.env.shared'
```
When set to `true`, moon will load the following files in order, with later files taking precedence
over earlier ones:
- `/.env`
- `/.env.local`
- `.env`
- `.env.local`
- `.env.`
- `.env..local`
Additionally, a list of file paths can also be provided. When using a list, the order of the files
is important, as environment variables from all files will be aggregated into a single map, with
subsequent files taking precedence over previous ones. Once aggregated, the variables will be passed
to the task, but will _not_ override those defined in [`env`](#env-1).
```yaml title="moon.yml" {5-7}
tasks:
build:
command: 'webpack'
options:
envFile:
- '.env'
- '.env.production'
```
#### `inferInputs`
Automatically infer [inputs](#inputs) based on the following parameters configured within the task's
`command`, `script`, `args`, or `env`. Defaults to `false`.
- File/glob paths derived from [file group based token functions](../concepts/token#file-groups).
- Environment variables being substituted within a command or script.
```yaml title="moon.yml" {5}
tasks:
build:
# ...
options:
inferInputs: false
```
#### `internal`
Marks the task as internal only. [Internal tasks](../concepts/task#internal-only) can not be
explicitly ran on the command line, but can be depended on by other tasks.
```yaml title="moon.yml" {5}
tasks:
prepare:
# ...
options:
internal: true
```
#### `interactive`
Marks the task as interactive. [Interactive tasks](../concepts/task#interactive) run in isolation so
that they can interact with stdin.
This setting also disables caching, turns of CI, and other functionality, similar to the
[`preset`](#preset) setting.
```yaml title="moon.yml" {5}
tasks:
init:
# ...
options:
interactive: true
```
#### `merge`
The [strategy](../concepts/task-inheritance#merge-strategies) to use when merging [`args`](#args),
[`deps`](#deps), [`env`](#env-1), [`inputs`](#inputs), and [`outputs`](#outputs) with an inherited
task. This option can be overridden with the field specific merge options below.
#### `mergeArgs`
The [strategy](../concepts/task-inheritance#merge-strategies) to use when merging the
[`args`](#args) list with an inherited task. Defaults to "append".
#### `mergeChecks`
The [strategy](../concepts/task-inheritance#merge-strategies) to use when merging the
[`checks`](#checks) list with an inherited task. Defaults to "append".
#### `mergeDeps`
The [strategy](../concepts/task-inheritance#merge-strategies) to use when merging the
[`deps`](#deps) list with an inherited task. Defaults to "append".
#### `mergeEnv`
The [strategy](../concepts/task-inheritance#merge-strategies) to use when merging the
[`env`](#env-1) map with an inherited task. Defaults to "append".
#### `mergeInputs`
The [strategy](../concepts/task-inheritance#merge-strategies) to use when merging the
[`inputs`](#inputs) list with an inherited task. Defaults to "append".
#### `mergeOutputs`
The [strategy](../concepts/task-inheritance#merge-strategies) to use when merging the
[`outputs`](#outputs) list with an inherited task. Defaults to "append".
#### `mergeTags`
The [strategy](../concepts/task-inheritance#merge-strategies) to use when merging the
[`tags`](#tags) list with an inherited task. Defaults to "append".
#### `mergeToolchains`
The [strategy](../concepts/task-inheritance#merge-strategies) to use when merging the
[`toolchains`](#toolchains) list with an inherited task. Defaults to "append".
#### `mutex`
Creates an exclusive lock on a "virtual resource", preventing other tasks using the same "virtual
resource" from running concurrently.
If you have many tasks that require exclusive access to a resource that can't be tracked by moon
(like a database, an ignored file, a file that's not part of the project, or a remote resource) you
can use the `mutex` option to prevent them from running at the same time.
```yaml title="moon.yml" {5,10}
tasks:
a:
# ...
options:
mutex: 'virtual_resource_name'
# b doesn't necessarily have to be in the same project
b:
# ...
options:
mutex: 'virtual_resource_name'
```
#### `os`
When defined, the task will _only_ run on the configured operating system. For other operating
systems, the task becomes a no-operation. Supports the values `linux`, `macos`, and `windows`.
Can be defined as a single value, or a list of values.
```yaml title="moon.yml" {5,10}
tasks:
build-unix:
# ...
options:
os: ['linux', 'macos']
build-windows:
# ...
options:
os: 'windows'
```
#### `outputStyle`
Controls how stdout/stderr is displayed when the task is run as a _transitive (non-primary) target_.
Primary targets always display their output and ignore this setting. By default, this setting is not
defined and defers to the action pipeline, but can be overridden with one of the following values:
- `buffer` - Buffers output and displays after the task has exited (either success or failure).
- `buffer-only-failure` - Like `buffer`, but only displays on failures.
- `hash` - Ignores output and only displays the generated [hash](../concepts/cache#hashing).
- `none` - Ignores output.
- `stream` - Streams output directly to the terminal. Will prefix each line of output with the
target.
```yaml title="moon.yml" {5}
tasks:
test:
# ...
options:
outputStyle: 'stream'
```
#### `persistent`
Marks the task as persistent (continuously running). [Persistent tasks](../concepts/task#persistent)
are handled differently than non-persistent tasks in the action graph. When running a target, all
persistent tasks are _ran last_ and _in parallel_, after all their dependencies have completed.
This is extremely useful for running a server (or a watcher) in the background while other tasks are
running.
```yaml title="moon.yml" {5}
tasks:
dev:
# ...
options:
persistent: true
```
> We suggest using the [`preset`](#preset) setting instead, which enables this setting, amongst
> other useful settings.
#### `priority`
The priority level determines the position of the task within the action pipeline queue. A task with
a higher priority will run sooner rather than later, while still respecting the topological order.
Supports the following levels:
- `critical`
- `high`
- `normal` (default)
- `low`
```yaml title="moon.yml" {5}
tasks:
build:
# ...
options:
priority: 'high'
```
#### `retryCount`
The number of attempts the task will retry execution before returning a failure. This is especially
useful for flaky tasks. Defaults to `0`.
```yaml title="moon.yml" {5}
tasks:
test:
# ...
options:
retryCount: 3
```
#### `runDepsInParallel`
Whether to run the task's direct [`deps`](#deps) in parallel or serial (in order). Defaults to
`true`.
When disabled, each dependency runs after the previous one completes. This ordering applies to a
dependency's entire dependency subtree, so transitive dependencies (dependencies of dependencies)
also run after the preceding direct dependency — not only the direct dependency itself.
```yaml title="moon.yml" {8}
tasks:
start:
# ...
deps:
- '~:clean'
- '~:build'
options:
runDepsInParallel: false
```
:::caution
Because tasks run only once per pipeline, serializing a dependency's subtree also delays any task
within it that is shared with _other_ targets in the same run (like `moon ci`), which can reduce
parallelism. When multiple parents request conflicting orderings for a shared task, the ordering
that would introduce a cycle is skipped.
:::
#### `runInCI`
Whether to run the task automatically in a CI (continuous integration) environment when affected by
changed files using the [`moon ci`](../commands/ci) command. Supports the following values:
- `always` - Always run in CI, regardless if affected or not.
- `affected`, `true` (default) - Only run in CI if affected by changed files.
- `false` - Never run in CI.
- `only` - Only run in CI, and not locally, if affected by changed files.
- `skip` - Skip running in CI but run locally and allow task relationships to be valid.
```yaml title="moon.yml" {5}
tasks:
build:
# ...
options:
runInCI: false
```
#### `runInSyncPhase`
Whether to run the task automatically during `moon sync`. Defaults to `false`.
```yaml title="moon.yml" {5}
tasks:
generate-schema:
# ...
options:
runInSyncPhase: true
```
#### `runFromWorkspaceRoot`
Whether to use the workspace root as the working directory when executing a task. Defaults to
`false` and runs from the task's project root.
```yaml title="moon.yml" {5}
tasks:
typecheck:
# ...
options:
runFromWorkspaceRoot: true
```
#### `shell`
Whether to run the command within a shell or not. Defaults to `true` for system toolchain or
Windows, and `false` otherwise. The shell to run is determined by the [`unixShell`](#unixshell) and
[`windowsShell`](#windowsshell) options respectively.
```yaml title="moon.yml" {5}
tasks:
native:
command: 'echo $SHELL'
options:
shell: true
```
However, if you'd like to use a different shell, or customize the shell's arguments, or have
granular control, you can set `shell` to false and configure a fully qualified command.
```yaml title="moon.yml" {5}
tasks:
native:
command: '/bin/zsh -c "echo $SHELL"'
options:
shell: false
```
#### `timeout`
The maximum time in seconds that the task is allowed to run, before it is force cancelled. If not
defined, will run indefinitely.
```yaml title="moon.yml" {5}
tasks:
build:
# ...
options:
timeout: 120
```
#### `unixShell`
Customize the shell to run with when on a Unix operating system. Accepts `bash`, `elvish`, `fish`,
`ion`, `murex`, `nu`, `pwsh`, `xonsh`, or `zsh`. If not defined, will derive the shell from the
`SHELL` environment variable, or defaults to `bash`.
```yaml title="moon.yml" {5}
tasks:
native:
command: 'echo $SHELL'
options:
unixShell: 'fish'
```
#### `windowsShell`
Customize the shell to run with when on a Windows operating system. Accepts `bash` (typically via
Git), `elvish`, `fish`, `murex`, `nu`, `pwsh`, or `xonsh`. If not defined, defaults to `pwsh`.
```yaml title="moon.yml" {5}
tasks:
native:
command: 'echo $SHELL'
options:
windowsShell: 'bash'
```
## `taskOptions`
Defines default [task options](#options) that are applied to _all tasks_ within the current project,
which can be overridden per task. This is similar to the workspace-level
[`taskOptions`](./tasks#taskoptions) in [`.moon/tasks.*`](./tasks), but is scoped to a single
project and sits later in the [inheritance chain](../concepts/task-inheritance), so it takes
precedence over inherited defaults.
```yaml title="moon.yml" {1-5}
taskOptions:
# Never cache tasks in this project
cache: false
# Always re-run flaky tasks
retryCount: 2
tasks:
build:
# ...
options:
# Override the project default
cache: true
```
## Overrides
Dictates how a project interacts with settings defined at the top-level.
## `toolchains`
### `default`
The default [`toolchain`](#toolchain-1) for all task's within the current project. When a task's
`toolchain` has _not been_ explicitly configured, the toolchain will fallback to this configured
value, otherwise the toolchain will be detected from the project's environment.
```yaml title="moon.yml"
toolchains:
default: 'node'
```
### `*`
Configures and overrides [workspace-level settings](./toolchain) for specific toolchains. The key is
the name of the toolchain, and the value is an object of settings to override.
```yaml title="moon.yml" {2-4}
toolchains:
typescript:
# Disable refs for this project
syncProjectReferences: false
```
Alternatively, if you want to _disable_ a toolchain for a project, you can set the value to `false`
or `null`, which will prevent the toolchain from being auto-detected and used within the project.
```yaml title="moon.yml" {2,3}
toolchains:
typescript: false
```
## `workspace`
### `inheritedTasks`
Provides a layer of control when inheriting tasks from [`.moon/tasks/**/*`](./tasks).
#### `exclude`
The optional `exclude` setting permits a project to exclude specific tasks from being inherited. It
accepts a list of strings, where each string is the name of a global task to exclude.
```yaml title="moon.yml" {4}
workspace:
inheritedTasks:
# Exclude the inherited `test` task for this project
exclude: ['test']
```
> Exclusion is applied after inclusion and before renaming.
#### `include`
The optional `include` setting permits a project to _only_ include specific inherited tasks (works
like an allow/white list). It accepts a list of strings, where each string is the name of a global
task to include.
When this field is not defined, the project will inherit all tasks from the global project config.
```yaml title="moon.yml" {4,7-9}
workspace:
inheritedTasks:
# Include *no* tasks (works like a full exclude)
include: []
# Only include the `lint` and `test` tasks for this project
include:
- 'lint'
- 'test'
```
> Inclusion is applied before exclusion and renaming.
#### `rename`
The optional `rename` setting permits a project to rename the inherited task within the current
project. It accepts a map of strings, where the key is the original name (found in the global
project config), and the value is the new name to use.
For example, say we have 2 tasks in the global project config called `buildPackage` and
`buildApplication`, but we only need 1, and since we're an application, we should omit and rename.
```yaml title="moon.yml" {4,5}
workspace:
inheritedTasks:
exclude: ['buildPackage']
rename:
buildApplication: 'build'
```
> Renaming occurs after inclusion and exclusion.
---
## .moon/tasks
The `.moon/tasks/**/*` files configures file groups and tasks that are inherited by _every matching_
project in the workspace based on inheritance conditions.
[Learn more about task inheritance!](../concepts/task-inheritance)
Projects can override or merge with these settings within their respective [`moon.*`](./project).
## `extends`
Defines one or many external `.moon/tasks/**/*`'s to extend and inherit settings from. Perfect for
reusability and sharing configuration across repositories and projects. When defined, this setting
must be an HTTPS URL _or_ relative file system path that points to a valid YAML document!
```yaml title=".moon/tasks/all.yml" {1}
extends: 'https://raw.githubusercontent.com/organization/repository/master/.moon/tasks/all.yml'
```
:::caution
For map-based settings, `fileGroups` and `tasks`, entries from both the extended configuration and
local configuration are merged into a new map, with the values of the local taking precedence. Map
values _are not_ deep merged!
:::
## `fileGroups`
> For more information on file group configuration, refer to the
> [`fileGroups`](./project#filegroups) section in the [`moon.*`](./project) doc.
Defines [file groups](../concepts/file-group) that will be inherited by projects, and also enables
enforcement of organizational patterns and file locations. For example, encourage projects to place
source files in a `src` folder, and all test files in `tests`.
```yaml title=".moon/tasks/all.yml"
fileGroups:
configs:
- '*.config.{js,cjs,mjs}'
- '*.json'
sources:
- 'src/**/*'
- 'types/**/*'
tests:
- 'tests/**/*'
- '**/__tests__/**/*'
assets:
- 'assets/**/*'
- 'images/**/*'
- 'static/**/*'
- '**/*.{scss,css}'
```
:::info
File paths and globs used within a file group are relative from the inherited project's root, and
not the workspace root.
:::
## `implicitDeps`
Defines task [`deps`](./project#deps) that are implicitly inserted into _all_ inherited tasks within
a project. This is extremely useful for pre-building projects that are used extensively throughout
the repo, or always building project dependencies. Defaults to an empty list.
```yaml title=".moon/tasks/all.yml" {1-2}
implicitDeps:
- '^:build'
```
:::info
Implicit dependencies are _always_ inherited, regardless of the [`mergeDeps`](./project#mergedeps)
option.
:::
## `implicitInputs`
Defines task [`inputs`](./project#inputs) that are implicitly inserted into _all_ inherited tasks
within a project. This is extremely useful for the "changes to these files should always trigger a
task" scenario.
Like `inputs`, file paths/globs defined here are relative from the inheriting project.
[Project and workspace relative file patterns](../concepts/file-pattern#project-relative) are
supported and encouraged.
```yaml title=".moon/tasks/node.yml" {1-2}
implicitInputs:
- 'package.json'
```
:::info
Implicit inputs are _always_ inherited, regardless of the [`mergeInputs`](./project#mergeinputs)
option.
:::
## `inheritedBy`
A map of conditions that must be met for the configuration within the file to be inherited by a
project. When this field is not defined, or is an empty map, the configuration will be inherited by
all projects.
```yaml title=".moon/tasks/custom.yml"
inheritedBy:
# Project belongs to either javascript or typescript toolchain, but not the ruby toolchain
toolchains:
or: ['javascript', 'typescript']
not: ['ruby']
# And project is either a frontend or backend stack
stacks: ['frontend', 'backend']
# And project is either a library or tool layer
layers: ['library', 'tool']
```
:::info
View the [official task inheritance guide](../concepts/task-inheritance) for more information!
:::
## `tasks`
> For more information on task configuration, refer to the [`tasks`](./project#tasks) section in the
> [`moon.*`](./project) doc.
As mentioned in the link above, [tasks](../concepts/task) are actions that are ran within the
context of a project, and commonly wrap a command. For most workspaces, every project _should_ have
linting, typechecking, testing, code formatting, so on and so forth. To reduce the amount of
boilerplate that _every_ project would require, this setting offers the ability to define tasks that
are inherited by many projects within the workspace, but can also be overridden per project.
```yaml title=".moon/tasks/all.yml"
tasks:
format:
command: 'prettier --check .'
lint:
command: 'eslint --no-error-on-unmatched-pattern .'
test:
command: 'jest --passWithNoTests'
typecheck:
command: 'tsc --build'
```
:::info
Relative file paths and globs used within a task are relative from the inherited project's root, and
not the workspace root, or the location of the `.moon/tasks/*` file.
:::
## `taskOptions`
> For more information on task options, refer to the [`options`](./project#options) section in the
> [`moon.*`](./project) doc.
Like [tasks](#tasks), this setting allows you to define task options that will be inherited by _all
tasks_ within the configured file, and by all project-level inherited tasks. This setting is the 1st
link in the inheritance chain, and can be overridden within each task.
:::info
As of v2.4, task options can also be defined per project with the
[`taskOptions`](./project#taskoptions) setting in [`moon.*`](./project), which sits later in the
inheritance chain and takes precedence over these workspace-level defaults.
:::
```yaml title=".moon/tasks/all.yml"
taskOptions:
# Never cache builds
cache: false
# Always re-run flaky tests
retryCount: 2
tasks:
build:
# ...
options:
# Override the default cache setting
cache: true
```
---
## template(Config)
The `template.*` file configures metadata and variables for a template,
[used by the generator](../guides/codegen), and must exist at the root of a named template folder.
## `id`
Overrides the name (identifier) of the template, instead of inferring the name from the template
folder. Be aware that template names must be unique across the workspace, and across all template
locations that have been configured in [`generator.templates`](./workspace#templates).
```yaml title="template.yml"
id: 'npm-package'
```
## `title`
A human readable title that will be displayed during the [`moon generate`](../commands/generate)
process.
```yaml title="template.yml"
title: 'npm package'
```
## `description`
A description of why the template exists, what its purpose is, and any other relevant information.
```yaml title="template.yml"
description: |
Scaffolds the initial structure for an npm package,
including source and test folders, a package.json, and more.
```
## `destination`
An optional file path in which this template should be generated into. This provides a mechanism for
standardizing a destination location, and avoids having to manually pass a destination to
[`moon generate`](../commands/generate).
If the destination is prefixed with `/`, it will be relative from the workspace root, otherwise it
is relative from the current working directory.
```yaml title="template.yml"
destination: 'packages/[name]'
```
> This setting supports [template variables](#variables) through `[varName]` syntax. Learn more in
> the [code generation documentation](../guides/codegen#interpolation).
## `extends`
One or many other templates that this template should extend. Will deeply inherit all template files
and variables.
```yaml title="template.yml"
extends: ['base', 'configs']
```
## `variables`
A mapping of variables that will be interpolated into all template files and file system paths when
[rendering with Tera](https://tera.netlify.app/docs/#variables). The map key is the variable name
(in camelCase or snake_case), while the value is a configuration object, as described with the
settings below.
```yaml title="template.yml"
variables:
name:
type: 'string'
default: ''
required: true
prompt: 'Package name?'
```
### `type`
The type of value for the variable. Accepts `array`, `boolean`, `string`, `object`, `number`, or
`enum`. Floats _are not supported_, use strings instead.
For arrays and objects, the value of each member must be a JSON compatible type.
### `internal`
Marks a variable as internal only, which avoids the variable value being overwritten by command line
arguments.
### `order`
The order in which the variable will be prompted to the user. By default, variables are prompted in
the order they are defined in the `template.*` file.
### Primitives & collections
Your basic primitives: boolean, numbers, strings, and collections: arrays, objects.
```yaml title="template.yml"
variables:
type:
type: 'array'
prompt: 'Type?'
default: ['app', 'lib']
```
```yaml title="template.yml"
variables:
private:
type: 'boolean'
prompt: 'Private?'
default: false
```
```yaml title="template.yml"
variables:
age:
type: 'number'
prompt: 'Age?'
default: 0
required: true
```
```yaml title="template.yml"
variables:
metadata:
type: 'object'
prompt: 'Metadata?'
default:
type: 'lib'
dev: true
```
```yaml title="template.yml"
variables:
name:
type: 'string'
prompt: 'Name?'
required: true
```
### `default`
The default value of the variable. When `--defaults` is passed to
[`moon generate`](../commands/generate) or [`prompt`](#prompt) is not defined, the default value
will be used, otherwise the user will be prompted to enter a custom value.
### `prompt`
When defined, will prompt the user with a message in the terminal to input a custom value, otherwise
[`default`](#default) will be used.
For arrays and objects, a valid JSON string must be provided as the value.
### `required`
Marks the variable as required during _prompting only_. For arrays, strings, and objects, will error
for empty values (`''`). For numbers, will error for zero's (`0`).
### Enums
An enum is an explicit list of string values that a user can choose from.
```yaml title="template.yml"
variables:
color:
type: 'enum'
values: ['red', 'green', 'blue', 'purple']
default: 'purple'
prompt: 'Favorite color?'
```
### `default`
The default value of the variable. When `--defaults` is passed to
[`moon generate`](../commands/generate) or [`prompt`](#prompt) is not defined, the default value
will be used, otherwise the user will be prompted to enter a custom value.
For enums, the default value can be a string when [`multiple`](#multiple) is false, or a string or
an array of strings when `multiple` is true. Furthermore, each default value must exist in the
[`values`](#values) list.
```yaml title="template.yml"
# Single
variables:
color:
type: 'enum'
values: ['red', 'green', 'blue', 'purple']
default: 'purple'
prompt: 'Favorite color?'
# Multiple
variables:
color:
type: 'enum'
values: ['red', 'green', 'blue', 'purple']
default: ['red', 'purple']
multiple: true
prompt: 'Favorite color?'
```
### `prompt`
When defined, will prompt the user with a message in the terminal to input a custom value, otherwise
[`default`](#default) will be used.
### `multiple`
Allows multiple values to be chosen during prompting. In the template, an array or strings will be
rendered, otherwise when not-multiple, a single string will be.
### `values`
List of explicit values to choose from. Can either be defined with a string, which acts as a value
and label, or as an object, which defines an explicit value and label.
```yaml title="template.yml"
variables:
color:
type: 'enum'
values:
- 'red'
# OR
- value: 'red'
label: 'Red 🔴'
# ...
```
## Frontmatter
The following settings _are not_ available in `template.*`, but can be defined as frontmatter at the
top of a template file. View the [code generation guide](../guides/codegen#frontmatter) for more
information.
### `force`
When enabled, will always overwrite a file of the same name at the destination path, and will bypass
any prompting in the terminal.
```twig
---
force: true
---
Some template content!
```
### `to`
Defines a custom file path, relative from the destination root, in which to create the file. This
will override the file path within the template folder, and allow for conditional rendering and
engine filters to be used.
```twig
{% set component_name = name | pascal_case %}
---
to: components/{{ component_name }}.tsx
---
export function {{ component_name }}() {
return ;
}
```
### `skip`
When enabled, the template file will be skipped while writing to the destination path. This setting
can be used to conditionally render a file.
```twig
---
skip: {{ name == "someCondition" }}
---
Some template content!
```
---
## .moon/toolchains
The `.moon/toolchains.*` file configures the toolchain and the workspace development environment.
This file is _optional_.
Managing tool version's within the toolchain ensures a deterministic environment across any machine
(whether a developer, CI, or production machine).
## `extends`
Defines one or many external `.moon/toolchains.*`'s to extend and inherit settings from. Perfect for
reusability and sharing configuration across repositories and projects. When defined, this setting
must be an HTTPS URL _or_ relative file system path that points to a valid YAML document!
```yaml title=".moon/toolchains.yml" {1}
extends: 'https://raw.githubusercontent.com/organization/repository/master/.moon/toolchains.yml'
```
:::caution
Settings will be merged recursively for blocks, with values defined in the local configuration
taking precedence over those defined in the extended configuration.
:::
## `moon`
Configures how moon will receive information about latest releases and download locations.
### `manifestUrl`
Defines an HTTPS URL in which to fetch the current version information from.
```yaml title=".moon/toolchains.yml" {2}
moon:
manifestUrl: 'https://proxy.corp.net/moon/version'
```
### `downloadUrl`
Defines an HTTPS URL in which the moon binary can be downloaded from. The download file name is
hard-coded and will be appended to the provided URL.
Defaults to downloading from GitHub: https://github.com/moonrepo/moon/releases
```yaml title=".moon/toolchains.yml" {2}
moon:
downloadUrl: 'https://github.com/moonrepo/moon/releases/latest/download'
```
## `proto`
Configures how moon integrates with and utilizes [proto](/proto).
### `version`
The version of proto to install and run toolchains with. If proto or this version of proto has not
been installed yet, it will be installed automatically when running a task.
```yaml title=".moon/toolchains.yml" {2}
proto:
version: '0.51.0'
```
## Shared
The following settings are available and shared across all toolchains. Run
`moon toolchain info ` for all available settings for a specific toolchain.
### `inheritAliases`
When enabled, will inherit [aliases for projects](../concepts/project#aliases) while the toolchain
is extending the project graph. An alias is typically derived from a `name` field in a toolchain
manifest file (`package.json`, `Cargo.toml`, etc). Defaults to `true`.
```yaml title=".moon/toolchains.yml"
javascript:
inheritAliases: false
```
### `installDependencies`
When enabled and running a task, will automatically install toolchain dependencies if the lockfile,
manifest, or environment has changed changed since the last run. This is achieved through the
[`InstallDependencies` action](../how-it-works/action-graph). Defaults to `true`.
```yaml title=".moon/toolchains.yml"
javascript:
installDependencies: false
```
### `plugin`
Configures the location of the `.wasm` plugin file that moon will use to run this toolchain.
Supports `file://` (relative from `.moon`), `https://`, and `github://` protocols.
[Learn more about plugin locators](../guides/wasm-plugins#configuring-plugin-locations).
```yaml title=".moon/toolchains.yml"
custom-toolchain:
plugin: 'file://../path/to/plugin.wasm'
```
> This field is not required for built-in toolchains. However, it can be configured to override the
> built-in plugin if you want to use your own implementation.
### `versionFromPrototools`
When a toolchain supports the `version` setting, this setting controls whether the version will be
inherited from the root `.prototools` configuration file. This is useful for sharing a single
version across multiple toolchains, and only having to update it in one place.
When `false`, doesn't inherit a version. When `true`, matches the version specified in `.prototools`
using the same toolchain identifier. Otherwise a string can be provided to specify a proto-specific
identifier. Defaults to `true`.
> Keeping versions in `.prototools` also pairs well with [Renovate](../guides/renovate), which can
> update them automatically.
```yaml title=".moon/toolchains.yml"
node:
versionFromPrototools: 'nodejs'
```
```toml title=".prototools"
nodejs = "~24"
```
## Go
## `go`
Run `moon toolchain info go` for all available settings.
## JavaScript
## `javascript`
Run `moon toolchain info javascript` for all available settings.
## `bun`
Run `moon toolchain info bun` for all available settings.
:::info
This toolchain requires the [`javascript`](#javascript) toolchain to also be enabled.
:::
## `deno`
Run `moon toolchain info deno` for all available settings.
:::info
This toolchain requires the [`javascript`](#javascript) toolchain to also be enabled.
:::
## `node`
Run `moon toolchain info node` for all available settings.
:::info
This toolchain requires the [`javascript`](#javascript) toolchain to also be enabled.
:::
## `npm`
Run `moon toolchain info npm` for all available settings.
:::info
This toolchain requires the [`node`](#node) toolchain to also be enabled.
:::
## `pnpm`
Run `moon toolchain info pnpm` for all available settings.
:::info
This toolchain requires the [`node`](#node) toolchain to also be enabled.
:::
## `yarn`
Run `moon toolchain info yarn` for all available settings.
:::info
This toolchain requires the [`node`](#node) toolchain to also be enabled.
:::
## `typescript`
Run `moon toolchain info typescript` for all available settings.
## Python
## `unstable_python`
Run `moon toolchain info unstable_python` for all available settings.
## `unstable_pip`
Run `moon toolchain info unstable_pip` for all available settings.
:::info
This toolchain requires the [`unstable_python`](#unstable_python) toolchain to also be enabled.
:::
## `unstable_poetry`
Run `moon toolchain info unstable_poetry` for all available settings.
:::info
This toolchain requires the [`unstable_python`](#unstable_python) toolchain to also be enabled.
:::
## `unstable_uv`
Run `moon toolchain info unstable_uv` for all available settings.
:::info
This toolchain requires the [`unstable_python`](#unstable_python) toolchain to also be enabled.
:::
## Ruby
## `unstable_ruby`
Run `moon toolchain info unstable_ruby` for all available settings.
## Rust
## `rust`
Run `moon toolchain info rust` for all available settings.
---
## .moon/workspace
The `.moon/workspace.*` file configures projects and services in the workspace. This file is
_required_.
## `extends`
Defines one or many external `.moon/workspace.*`'s to extend and inherit settings from. Perfect for
reusability and sharing configuration across repositories and projects. When defined, this setting
must be an HTTPS URL _or_ relative file system path that points to a valid YAML document!
```yaml title=".moon/workspace.yml" {1}
extends: 'https://raw.githubusercontent.com/organization/repository/master/.moon/workspace.yml'
```
:::info
Settings will be merged recursively for blocks, with values defined in the local configuration
taking precedence over those defined in the extended configuration. However, the `projects` setting
_does not merge_!
:::
## `projects`
Defines the location of all [projects](../concepts/project) within the workspace. Supports either a
manual map of projects (default), a list of globs in which to automatically locate projects, _or_
both.
:::caution
Projects that depend on each other and form a cycle must be avoided! While we do our best to avoid
an infinite loop and disconnect nodes from each other, there's no guarantee that tasks will run in
the correct order.
:::
### Using a map
When using a map, each project must be _manually_ configured and requires a unique
[name](../concepts/project#names) as the map key, where this name is used heavily on the command
line and within the project graph for uniquely identifying the project amongst all projects. The map
value (known as the project source) is a file system path to the project folder, relative from the
workspace root, and must be contained within the workspace boundary.
```yaml title=".moon/workspace.yml"
projects:
admin: 'apps/admin'
apiClients: 'packages/api-clients'
designSystem: 'packages/design-system'
web: 'apps/web'
```
### Using globs
If manually mapping projects is too tedious or cumbersome, you may provide a list of
[globs](../concepts/file-pattern#globs) to automatically locate all project folders, relative from
the workspace root.
When using this approach, the project name is derived from the project folder name, and is cleaned
to our [supported characters](../concepts/project#names), but can be customized with the
[`id`](./project#id) setting in [`moon.*`](./project). Furthermore, globbing **does risk the chance
of collision**, and when that happens, we log a warning and skip the conflicting project from being
configured in the project graph.
```yaml title=".moon/workspace.yml"
projects:
- 'apps/*'
- 'packages/*'
# Only shared folders with a moon configuration
- 'shared/*/moon.yml'
```
### Using a map _and_ globs
For those situations where you want to use _both_ patterns, you can! The list of globs can be
defined under a `globs` field, while the map of projects under a `sources` field.
```yaml title=".moon/workspace.yml"
projects:
globs:
- 'apps/*'
- 'packages/*'
sources:
www: 'www'
```
Additionally, you can customize the format of project IDs for glob discovered projects. By default
it inherits the fodler name, but this has a high chance of collision. Instead you can configure
`globFormat` to use a different format, for example, using the full workspace relative path as the
project ID.
```yaml title=".moon/workspace.yml"
projects:
globFormat: 'source-path'
globs:
- 'packages/**/moon.yml'
```
## `defaultProject`
Defines the default project to focus on when no project scope is specified on the command line for
task targets.
```yaml title=".moon/workspace.yml" {2}
defaultProject: 'web'
```
## `cache`
Configures aspects of the caching engine and layer. These settings primarily tune the
content-addressable storage (CAS) cache and the native file hasher.
### `cas`
Configures aspects of the content-addressable storage (CAS) cache.
#### `verifyIntegrity`
Re-verifies the hash of cached content on every read. When enabled, reads are slower but on-disk
corruption is detected. Defaults to `false`.
```yaml title=".moon/workspace.yml" {2,3}
cache:
cas:
verifyIntegrity: true
```
## `codeowners`
Configures code owners (`CODEOWNERS`) integration across the entire workspace.
### `globalPaths`
This setting defines file patterns and their owners at the workspace-level, and are applied to any
matching path, at any depth, within the entire workspace. This is useful for defining global or
fallback owners when a granular [project-level path](./project#paths) does not match or exist.
```yaml title=".moon/workspace.yml" {2-5}
codeowners:
globalPaths:
'*': ['@admins']
'config/': ['@infra']
'/.github/': ['@infra']
```
### `orderBy`
The order in which code owners, grouped by project, are listed in the `CODEOWNERS` file. Accepts
"file-source" (default) or "project-id".
```yaml title=".moon/workspace.yml" {2}
codeowners:
orderBy: 'project-id'
```
### `sync`
Will automatically generate a `CODEOWNERS` file by aggregating and syncing all project
[`owners`](./project#owners) in the workspace when a [target is run](../concepts/target). The format
and location of the `CODEOWNERS` file is based on the [`vcs.provider`](#provider) setting. Defaults
to `false`.
```yaml title=".moon/workspace.yml" {2}
codeowners:
sync: true
```
## `constraints`
Configures constraints between projects that are enforced during project graph generation. This is
also known as project boundaries.
### `enforceLayerRelationships`
Enforces allowed relationships between a project and its dependencies based on the project's
[`layer`](./project#layer) and [`stack`](./project#stack) settings. When a project depends on
another project of an invalid layer, a layering violation error will be thrown when attempting to
run a task.
Layers are allowed to depend on lower layers in the same stack, but not higher layers. Additionally,
layers may depend on itself, excluding automations and applications. The following layers are
stacked as such:
| Layer | Description |
| --------------- | ------------------------------------------------------------------- |
| `automation` | An automated testing suite, like E2E, integration, or visual tests. |
| `application` | An application of any kind. |
| `tool` | An internal tool, CLI, one-off script, etc. |
| `library` | A self-contained, shareable, and publishable set of code. |
| `scaffolding` | Templates or generators for scaffolding. |
| `configuration` | Configuration files or infrastructure. |
| `unknown` | When not configured. |
When the project `stack` setting is defined, it alters these rules to allow these kinds of
relationships. For example, a frontend application can depend on a backend application, but not
another frontend application.
```yaml title=".moon/workspace.yml" {2}
constraints:
enforceLayerRelationships: false
```
> Projects with an unconfigured or unknown layer are ignored during enforcement.
### `tagRelationships`
Enforces allowed relationships between a project and its dependencies based on the project's
[`tags`](./project#tags) setting. This works in a similar fashion to `enforceLayerRelationships`,
but gives you far more control over what these relationships look like.
For example, let's enforce that Next.js projects using the `next` tag can only depend on React
projects using the `react` tag. If a dependency does not have one of the configured required tags,
in this case `react`, an error will occur.
```yaml title=".moon/workspace.yml" {2,3}
constraints:
tagRelationships:
next: ['react']
```
On the project side, we would configure [`moon.*`](./project#tags) like so:
```yaml title="app/moon.yml"
tags: ['next']
dependsOn: ['components']
```
```yaml title="packages/components/moon.yml"
tags: ['react']
```
## `docker`
Configures Docker integration for the entire workspace.
### `prune`
Configures aspects of the Docker pruning process when
[`moon docker prune`](../commands/docker/prune) is executed.
#### `deleteVendorDirectories`
Automatically delete vendor directories (package manager dependencies, build targets, etc) while
pruning. For example, `node_modules` for JavaScript, or `target` for Rust. Defaults to `true`.
```yaml title=".moon/workspace.yml" {3}
docker:
prune:
deleteVendorDirectories: false
```
> This process happens before toolchain dependencies are installed.
#### `installToolchainDependencies`
Automatically install production dependencies for all required toolchain's of the focused projects
within the Docker build. For example, `node_modules` for JavaScript. Defaults to `true`.
```yaml title=".moon/workspace.yml" {3}
docker:
prune:
installToolchainDependencies: false
```
> This process happens after vendor directories are deleted.
### `scaffold`
Configures aspects of the Docker scaffolding process when
[`moon docker scaffold`](../commands/docker/scaffold) is executed. Only applies to the
[workspace skeleton](../commands/docker/scaffold#workspace).
#### `configsPhaseGlobs`
List of globs in which to copy additional workspace-relative files into the `.moon/docker/workspace`
skeleton. When not defined, does nothing.
```yaml title=".moon/workspace.yml" {3,4}
docker:
scaffold:
configsPhaseGlobs:
- '**/package.json'
```
## `experiments`
Enable or disable experiments that alter core functionality.
:::warning
Experiments are a work in progress and may be buggy. Please report any issues you encounter!
:::
### `asyncAffectedTracking`
Utilizes a new asynchronous implementation of the affected tracker that can improve performance by
100-150%. Defaults to `false`.
```yaml title=".moon/workspace.yml" {2}
experiments:
asyncAffectedTracking: true
```
Can also be enabled with the `MOON_EXPERIMENT_ASYNC_AFFECTED_TRACKING` environment variable.
### `asyncGraphBuilding`
Utilizes an asynchronous graph building implementation that can improve performance by 100-170% in
large workspaces. Defaults to `false`.
```yaml title=".moon/workspace.yml" {2}
experiments:
asyncGraphBuilding: true
```
Can also be enabled with the `MOON_EXPERIMENT_ASYNC_GRAPH_BUILDING` environment variable.
### `casOutputsCache`
Stores task outputs in a local content-addressable storage (CAS) cache, instead of the legacy
tarball-based local cache. This shares the same content-addressed format used by the remote cache,
enabling deduplicated storage across tasks and faster hydration. Defaults to `false`.
```yaml title=".moon/workspace.yml" {2}
experiments:
casOutputsCache: true
```
Can also be enabled with the `MOON_EXPERIMENT_CAS_OUTPUTS_CACHE` environment variable.
> The CAS layer can be tuned through the top-level [`cache`](#cache) setting.
### `nativeFileHashing`
Replaces the VCS-based file hashing mechanism with a custom native implementation that runs within
moon's task pool. In our benchmarks this improves performance by 10-50% depending on workspace size
and file count. Defaults to `false`.
```yaml title=".moon/workspace.yml" {2}
experiments:
nativeFileHashing: true
```
Can also be enabled with the `MOON_EXPERIMENT_NATIVE_FILE_HASHING` environment variable.
> The hasher can be tuned through the top-level [`cache`](#cache) setting.
## `generator`
Configures aspects of the template generator.
### `templates`
A list of paths in which templates can be located. Supports the following types of paths, and
defaults to `./templates`.
- File system paths, relative from the workspace root.
- Git repositories and a revision, prefixed with `git://`.
- npm packages and a version, prefixed with `npm://`.
```yaml title=".moon/workspace.yml" {2-4}
generator:
templates:
- './templates'
- 'file://./other/templates'
- 'git://github.com/moonrepo/templates#master'
- 'npm://@moonrepo/templates#1.2.3'
```
> Learn more about this in the official
> [code generation guide](../guides/codegen#configuring-template-locations)!
## `hasher`
Configures aspects of the smart hashing layer.
### `ignoreMissingPatterns`
When [`hasher.warnOnMissingInputs`](#warnonmissinginputs) is enabled, moon will log a warning to the
terminal that an input is missing. This is useful for uncovering misconfigurations, but can be quite
noisy when inputs are truly optional.
To ignore warnings for missing inputs, a list of [glob patterns](../concepts/file-pattern#globs) can
be configured to filter and ignore files. Files are matched against workspace relative paths, so
prefixing patterns with `**/` is suggested.
Defaults to `**/.env` and `**/.env.*` but will be overwritten when configured.
```yaml title=".moon/workspace.yml" {2-4}
hasher:
ignoreMissingPatterns:
- '**/.eslintrc.*'
- '**/*.config.*'
```
### `ignorePatterns`
A list of [glob patterns](../concepts/file-pattern#globs) used to filter and ignore files during the
inputs hashing process. Files are matched against workspace relative paths, so prefixing patterns
with `**/` is suggested.
```yaml title=".moon/workspace.yml" {2,3}
hasher:
ignorePatterns:
- '**/*.png'
```
### `optimization`
Determines the optimization level to utilize when hashing content before running targets.
- `accuracy` (default) - When hashing dependency versions, utilize the resolved value in the
lockfile. This requires parsing the lockfile, which may reduce performance.
- `performance` - When hashing dependency versions, utilize the value defined in the manifest. This
is typically a version range or requirement.
```yaml title=".moon/workspace.yml" {2}
hasher:
optimization: 'performance'
```
### `walkStrategy`
Defines the file system walking strategy to utilize when discovering inputs to hash.
- `glob` - Walks the file system using glob patterns.
- `vcs` (default) - Calls out to the [VCS](#vcs) to extract files from its working tree.
```yaml title=".moon/workspace.yml" {2}
hasher:
walkStrategy: 'glob'
```
### `warnOnMissingInputs`
When enabled, will log warnings to the console when attempting to hash an input that does not exist.
This is useful in uncovering misconfigured tasks. Defaults to `true`.
```yaml title=".moon/workspace.yml" {2}
hasher:
warnOnMissingInputs: false
```
## `notifier`
Configures how moon notifies and interacts with a developer or an external system.
### `terminalNotifications`
When defined, will display OS notifications for action pipeline events when running commands from a
terminal. Supports the following values:
- `always` - Display on pipeline success and failure.
- `failure` - Display on pipeline failure only.
- `success` - Display on pipeline success only.
- `task-failure` - Display for each task failure.
```yaml title=".moon/workspace.yml" {2}
notifier:
terminalNotifications: 'always'
```
### `webhookUrl`
Defines an HTTPS URL that all pipeline events will be posted to. View the
[webhooks guide for more information](../guides/webhooks) on available events.
```yaml title=".moon/workspace.yml" {2}
notifier:
webhookUrl: 'https://api.company.com/some/endpoint'
```
### `webhookAcknowledge`
When enabled, webhook notifier will wait for request result and validates the return code for 2xx.
Defaults to `false`.
:::warning
Activating this setting will slow down your pipeline, because every webhook request will be
evaluated!
:::
```yaml title=".moon/workspace.yml" {2}
notifier:
webhookUrl: 'https://api.company.com/some/endpoint'
webhookAcknowledge: true
```
## `pipeline`
Configures aspects of task running and the action pipeline.
### `autoCleanCache`
Automatically cleans cached artifacts older than [`cacheLifetime`](#cachelifetime) from the cache
directory (`.moon/cache`) after every run. This is useful for keeping the cache directory lean.
Defaults to `true`.
```yaml title=".moon/workspace.yml" {2}
pipeline:
autoCleanCache: false
```
### `cacheLifetime`
The maximum lifetime of cached artifacts before they're marked as stale and automatically removed by
the action pipeline. Defaults to "7 days". This field requires an integer and a timeframe unit that
can be [parsed as a duration](https://docs.rs/humantime/2.1.0/humantime/fn.parse_duration.html).
```yaml title=".moon/workspace.yml" {2}
pipeline:
cacheLifetime: '24 hours'
```
### `inheritColorsForPipedTasks`
Force colors to be inherited from the current terminal for all tasks that are ran as a child process
and their output is piped to the action pipeline. Defaults to `true`.
[View more about color handling in moon](../commands/overview#colors).
```yaml title=".moon/workspace.yml" {2}
pipeline:
inheritColorsForPipedTasks: true
```
### `installDependencies`
When enabled, runs the
[`InstallWorkspaceDeps` and `InstallProjectDeps` actions](../how-it-works/action-graph#install-dependencies)
within the pipeline before running an applicable task. Installation is determined based on changed
manifests and lockfiles. Defaults to `true`.
```yaml title=".moon/workspace.yml" {2}
pipeline:
installDependencies: false
```
Instead of a boolean, a list of toolchain IDs can be provided to only allow those toolchains to
install dependencies.
```yaml title=".moon/workspace.yml" {2}
pipeline:
installDependencies: ['node']
```
### `killProcessThreshold`
Threshold in milliseconds in which to force kill running child processes after the pipeline receives
an external signal (like `SIGINT` or `SIGTERM`). A value of 0 will not kill the process and let them
run to completion. Defaults to `2000` (2 seconds).
```yaml title=".moon/workspace.yml" {2}
pipeline:
killProcessThreshold: 5000
```
### `logRunningCommand`
When enabled, will log the task's command, resolved arguments, and working directory when a target
is ran. Defaults to `false`.
```yaml title=".moon/workspace.yml" {2}
pipeline:
logRunningCommand: true
```
### `syncProjects`
When enabled, runs the [`SyncProject` action](../how-it-works/action-graph#sync-project) within the
pipeline before running an applicable task. Defaults to `true`.
```yaml title=".moon/workspace.yml" {2}
pipeline:
syncProjects: false
```
Instead of a boolean, a list of project IDs can be provided to only sync those projects.
```yaml title=".moon/workspace.yml" {2}
pipeline:
syncProjects: ['app']
```
> The [`moon sync projects`](../commands/sync/projects) command can be executed to manually sync
> projects.
### `syncWorkspace`
When enabled, runs the [`SyncWorkspace` action](../how-it-works/action-graph#sync-workspace) within
the pipeline before all other actions. This syncing includes operations such as codeowners, VCS
hooks, and more. Defaults to `true`.
```yaml title=".moon/workspace.yml" {2}
pipeline:
syncWorkspace: false
```
> The [`moon sync ...`](../commands/sync) sub-commands can be executed to manually sync features.
## `remote`
Configures a remote service, primarily for cloud-based caching of artifacts. Learn more about this
in the [remote caching](../guides/remote-cache) guide.
### `api`
The API format of the remote server. This format dictates which type of client moon uses for
communicating with. Supports the following:
- `grpc` (default) - Uses the gRPC API: https://github.com/bazelbuild/remote-apis
- `http` - Uses the HTTP API: https://bazel.build/remote/caching#http-caching
```yaml title=".moon/workspace.yml" {2}
remote:
api: 'grpc'
```
### `auth`
Configures authorization and authentication level features of our remote clients.
#### `headers`
A mapping of HTTP headers to include in all requests to the remote server. These headers are applied
to all [API formats and protocols](#api), not just HTTP.
```yaml title=".moon/workspace.yml" {2-4}
remote:
auth:
headers:
'X-Custom-Header': 'value'
```
#### `token`
The name of an environment variable in which to extract a token for
[Bearer HTTP authorization](https://swagger.io/docs/specification/v3_0/authentication/bearer-authentication/).
An `Authorization` HTTP header will be included in all requests to the remote server.
If the token does not exist, or is not enabled, remote caching will be disabled.
```yaml title=".moon/workspace.yml" {2-4}
remote:
auth:
token: 'ENV_VAR_NAME'
```
### `cache`
Configures aspects of the caching layer, primarily the action cache (AC) and content addressable
cache (CAS).
#### `compression`
The compression format to use when uploading/downloading blobs. Supports `none` and `zstd`, and
defaults to no compression (`identity` format in RE API).
```yaml title=".moon/workspace.yml" {3}
remote:
cache:
compression: 'zstd'
```
:::info
Compression is only applied to gRPC based APIs, not HTTP.
:::
#### `instanceName`
A
[unique identifier](https://github.com/bazelbuild/remote-apis/blob/main/build/bazel/remote/execution/v2/remote_execution.proto#L223)
used to distinguish between the various instances on the host. This allows the same remote service
to serve and partition multiple moon repositories. Defaults to `moon-outputs`.
```yaml title=".moon/workspace.yml" {3}
remote:
cache:
instanceName: 'custom-dir-name'
```
> We suggest changing the instance name to the name of your repository!
#### `localReadOnly`
When enabled and developing locally, existing remote blobs will only be downloaded, but new local
blobs will _not_ be uploaded. Blobs will only be uploaded in CI environments.
```yaml title=".moon/workspace.yml" {3}
remote:
cache:
localReadOnly: true
```
#### `verifyIntegrity`
When downloading blobs, verify the digests/hashes in the response match the associated blob
contents. This will reduce performance but ensure partial or corrupted blobs won't cause failures.
Defaults to `false`.
```yaml title=".moon/workspace.yml" {3}
remote:
cache:
verifyIntegrity: true
```
### `host`
The host URL to communicate with when uploading and downloading artifacts. Supports both
`grpc(s)://` and `http(s)://` protocols. This field is required!
```yaml title=".moon/workspace.yml" {2}
remote:
host: 'grpcs://your-host.com:9092'
```
### `mtls`
Connect to the host using server and client authentication with mTLS. This takes precedence over
normal TLS.
```yaml title=".moon/workspace.yml" {3-7}
remote:
# ...
mtls:
caCert: 'certs/ca.pem'
clientCert: 'certs/client.pem'
clientKey: 'certs/client.key'
domain: 'your-host.com'
```
#### `assumeHttp2`
If true, assume that the host supports HTTP/2, even if it doesn't provide protocol negotiation via
ALPN.
#### `caCert`
A file path, relative from the workspace root, to the certificate authority PEM encoded X509
certificate (typically `ca.pem`).
#### `clientCert`
A file path, relative from the workspace root, to the client's PEM encoded X509 certificate
(typically `client.pem`).
#### `clientKey`
A file path, relative from the workspace root, to the client's PEM encoded X509 private key
(typically `client.key`).
#### `domain`
The domain name in which to verify the TLS certificate.
### `tls`
Connect to the host using server-only authentication with TLS.
```yaml title=".moon/workspace.yml" {3-5}
remote:
# ...
tls:
cert: 'certs/ca.pem'
domain: 'your-host.com'
```
#### `assumeHttp2`
If true, assume that the host supports HTTP/2, even if it doesn't provide protocol negotiation via
ALPN.
#### `cert`
A file path, relative from the workspace root, to the certificate authority PEM encoded X509
certificate (typically `ca.pem`).
#### `domain`
The domain name in which to verify the TLS certificate.
## `telemetry`
When enabled, will check for a newer moon version and send anonymous usage data to the moonrepo
team. This data is used to improve the quality and reliability of the tool. Defaults to `true`.
```yaml title=".moon/workspace.yml" {1}
telemetry: false
```
## `vcs`
Configures the version control system to utilize within the workspace (and repository). A VCS is
required for determining touched (added, modified, etc) files, calculating file hashes, computing
affected files, and much more.
### `defaultBranch`
Defines the default branch in the repository for comparing differences against. For git, this is
typically "master" (default) or "main".
```yaml title=".moon/workspace.yml" {2}
vcs:
defaultBranch: 'master'
```
### `hooks`
Defines a mapping of hooks to a list of commands to run when that event is triggered. There are no
restrictions to what commands can be run, but the binaries for each command must exist on each
machine that will be running hooks.
For Git, each [hook name](https://git-scm.com/docs/githooks#_hooks) must be a valid kebab-cased
name. [Learn more about Git hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks).
```yaml title=".moon/workspace.yml" {2-5}
vcs:
hooks:
pre-commit:
- 'moon run :lint :format --affected --status=staged --no-bail'
- 'another-command'
```
:::info
If running `moon` commands directly, the `moon` binary must be installed globally!
:::
### `hookFormat`
The shell and file type in which generated hook files are formatted with. Supports the following:
- `native` (default) - The format native to the current operating system. Bash on Unix, and
PowerShell on Windows.
- `bash` - Forces the format to Bash for all operating systems.
```yaml title=".moon/workspace.yml" {2}
vcs:
hookFormat: 'bash'
```
### `client`
Defines the VCS tool/binary that is being used for managing the repository. Accepts "git" (default).
Expect more version control systems in the future!
```yaml title=".moon/workspace.yml" {2}
vcs:
client: 'git'
```
### `provider`
Defines the service provider that the repository is hosted on. Accepts "github" (default), "gitlab",
"bitbucket", or "other".
```yaml title=".moon/workspace.yml" {2}
vcs:
provider: 'github'
```
### `remoteCandidates`
(Git only) Defines a list of remote candidates to query against to determine merge bases. Defaults
to "origin" and "upstream".
```yaml title=".moon/workspace.yml" {2-4}
vcs:
remoteCandidates:
- 'origin'
- 'upstream'
```
### `sync`
Will automatically generate [hook scripts](#hooks) to `.moon/hooks` and sync the scripts to the
local VCS checkout. The hooks format and location is based on the [`vcs.client`](#client) setting.
Defaults to `false`.
```yaml title=".moon/workspace.yml" {4}
vcs:
hooks:
# ...
sync: true
```
:::caution
When enabled, this will sync hooks for _all_ users of the repository. For personal or small
projects, this may be fine, but for larger projects, this may be undesirable and disruptive!
:::
## `versionConstraint`
Defines a version requirement for the currently running moon binary. This provides a mechanism for
enforcing that the globally installed moon on every developers machine is using an applicable
version.
```yaml title=".moon/workspace.yml" {1}
versionConstraint: '>=0.20.0'
```
---
## Create a project
With a [workspace](./setup-workspace), we can now house one or many [projects](./concepts/project),
with a project being an application, library, or more. In the end, each project will have its own
build layer, personal tasks, and custom configuration.
## Declaring a project in the workspace
Although a project may exist in your repository, it's not accessible from moon until it's been
mapped in the [`projects`](./config/workspace#projects) setting found in
[`.moon/workspace.*`](./config/workspace). When mapping a project, we require a unique name for the
project, and a project source location (path relative from the workspace root).
Let's say we have a frontend web application called "client", and a backend application called
"server", our `projects` setting would look like the following.
```yaml title=".moon/workspace.yml"
projects:
client: 'apps/client'
server: 'apps/server'
```
We can now run [`moon project client`](./commands/project) and
[`moon project server`](./commands/project) to display information about each project. If these
projects were not mapped, or were pointing to an invalid source, the command would throw an error.
:::success
The [`projects`](./config/workspace#projects) setting also supports a list of globs, if you'd prefer
to not manually curate the projects list!
:::
## Configuring a project
A project can be configured in 1 of 2 ways:
- Through the [`.moon/tasks/**/*`](./config/tasks) config files, which defines file groups and tasks
that are inherited by _matching_ projects within the workspace. Perfect for standardizing common
tasks like linting, typechecking, and code formatting.
- Through the [`/moon.*`](./config/project) config file, found at the root of each project,
which defines files groups, tasks, dependencies, and more that are unique to that project.
Both config files are optional, and can be used separately or together, the choice is yours!
Now let's continue with our client and server example above. If we wanted to configure both
projects, and define config that's also shared between the 2, we could do something like the
following:
```yaml title="apps/client/moon.yml"
tasks:
build:
command: 'vite dev'
inputs:
- 'src/**/*'
outputs:
- 'dist'
```
```yaml title="apps/server/moon.yml"
tasks:
build:
command: 'babel src --out-dir build'
inputs:
- 'src/**/*'
outputs:
- 'build'
```
```yaml title=".moon/tasks/all.yml"
tasks:
format:
command: 'prettier --check .'
lint:
command: 'eslint --no-error-on-unmatched-pattern .'
test:
command: 'jest --passWithNoTests .'
typecheck:
command: 'tsc --build'
```
### Adding optional metadata
When utilizing moon in a large monorepo or organization, ownership becomes very important, but also
difficult to maintain. To combat this problem, moon supports the
[`project`](./config/project#project) field within a project's [`moon.*`](./config/project) config.
This field is _optional_ by default, but when defined it provides metadata about the project,
specifically around team ownership, which developers maintain the project, where to discuss it, and
more!
Furthermore, we also support the [`layer`](./config/project#layer) and
[`language`](./config/project#language) settings for a more granular breakdown of what exists in the
repository.
```yaml title="/moon.yml"
layer: 'tool'
language: 'typescript'
project:
name: 'moon'
description: 'A repo management tool.'
channel: '#moon'
owner: 'infra.platform'
maintainers: ['miles.johnson']
```
## Next steps
.moon/workspace.* further
),
url: './config/workspace',
},
{
icon: 'project-config-global',
label: (
Configure .moon/tasks/**/* further
),
url: './config/tasks',
},
{
icon: 'project-config',
label: (
Configure moon.* further
),
url: './config/project',
},
{ icon: 'project', label: 'Learn about projects', url: './concepts/project' },
]}
/>
---
## Create a task
The primary focus of moon is a task runner, and for it to operate in any capacity, it requires tasks
to run. In moon, a task is a binary or system command that is ran as a child process within the
context of a project (is the current working directory). Tasks are defined per project with
[`moon.*`](./config/project), or inherited by many projects with
[`.moon/tasks/**/*`](./config/tasks), but can also be inferred from a language's ecosystem.
:::tip
Change the language dropdown at the top right to switch the examples!
:::
## Configuring a task
Most — if not all projects — utilize the same core tasks: linting, testing, code formatting,
typechecking, and _building_. Because these are so universal, let's implement the build task within
a project using [`moon.*`](./config/project).
Begin by creating the `moon.*` file at the root of a project and add `build` to the
[`tasks`](./config/project#tasks) field, with a [`command`](./config/project#command) parameter.
```yaml title="/moon.yml" {5,6}
language: 'javascript'
toolchain:
default: 'bun'
tasks:
build:
command: 'webpack build'
```
```yaml title="/moon.yml" {4,5}
language: 'typescript'
tasks:
build:
command: 'deno compile ./src/main.ts'
```
```yaml title="/moon.yml" {4,5}
language: 'go'
tasks:
build:
command: 'go build'
```
```yaml title="/moon.yml" {4,5}
language: 'javascript'
tasks:
build:
command: 'webpack build'
```
```yaml title="/moon.yml" {4,5}
language: 'php'
tasks:
build:
command: 'phar pack'
```
:::caution
PHP doesn't have a concept of building like compiled languages, so these examples will reference
[PHAR creation](https://www.mankier.com/1/phar) using the command line.
:::
```yaml title="/moon.yml" {4,5}
language: 'python'
tasks:
build:
command: 'python'
```
:::caution
Python doesn't have a concept of building like compiled languages, so these examples will reference
[project building](https://packaging.python.org/en/latest/tutorials/packaging-projects/).
:::
```yaml title="/moon.yml" {4,5}
language: 'ruby'
tasks:
build:
command: 'rake build'
```
:::caution
Ruby doesn't have a concept of building like compiled languages, so these examples are merely
theoretical.
:::
```yaml title="/moon.yml" {4,5}
language: 'rust'
tasks:
build:
command: 'cargo build'
```
By itself, this isn't doing much, so let's add some arguments. Arguments can also be defined with
the [`args`](./config/project#args) setting.
```yaml title="/moon.yml" {6}
language: 'javascript'
toolchain:
default: 'bun'
tasks:
build:
command: 'webpack build --mode production --no-stats'
```
```yaml title="/moon.yml" {5}
language: 'typescript'
tasks:
build:
command: 'deno compile ./src/main.ts -o ./out.js'
```
```yaml title="/moon.yml" {5}
language: 'go'
tasks:
build:
command: 'go build -o build'
```
```yaml title="/moon.yml" {5}
language: 'javascript'
tasks:
build:
command: 'webpack build --mode production --no-stats'
```
```yaml title="/moon.yml" {5}
language: 'php'
tasks:
build:
command: 'phar pack -h sha256'
```
```yaml title="/moon.yml" {5}
language: 'python'
tasks:
build:
command: 'python -m build'
```
```yaml title="/moon.yml" {5}
language: 'ruby'
tasks:
build:
command: 'rake build --quiet'
```
```yaml title="/moon.yml" {5}
language: 'rust'
tasks:
build:
command: 'cargo build --release'
```
With this, the task can be ran from the command line with
[`moon run :build`](./commands/run)! This is tasks in its most simplest form, but continue
reading on how to take full advantage of our task runner.
### Inputs
Our task above works, but isn't very efficient as it _always_ runs, regardless of what has changed
since the last time it has ran. This becomes problematic in continuous integration environments, not
just locally.
To mitigate this problem, moon provides a system known as inputs, which are file paths, globs, and
environment variables that are used by the task when it's ran. moon will use and compare these
inputs to calculate whether to run, or to return the previous run state from the cache.
If you're a bit confused, let's demonstrate this by expanding the task with the
[`inputs`](./config/project#inputs) setting.
```yaml title="/moon.yml" {7-10}
language: 'javascript'
toolchain:
default: 'bun'
tasks:
build:
command: 'webpack build --mode production --no-stats'
inputs:
- 'src/**/*'
- 'webpack.config.js'
- '/webpack-shared.config.js'
```
```yaml title="/moon.yml" {6-9}
language: 'typescript'
tasks:
build:
command: 'deno compile ./src/main.ts -o ./out.js'
inputs:
- 'src/**/*'
- 'deno.*'
```
```yaml title="/moon.yml" {6-9}
language: 'go'
tasks:
build:
command: 'go build -o build'
inputs:
- 'src/**/*'
```
```yaml title="/moon.yml" {6-9}
language: 'javascript'
tasks:
build:
command: 'webpack build --mode production --no-stats'
inputs:
- 'src/**/*'
- 'webpack.config.js'
- '/webpack-shared.config.js'
```
```yaml title="/moon.yml" {6-9}
language: 'php'
tasks:
build:
command: 'phar pack -h sha256'
inputs:
- 'src/**/*'
- 'composer.json'
- '/composer.json'
```
```yaml title="/moon.yml" {6-9}
language: 'python'
tasks:
build:
command: 'python -m build'
inputs:
- 'src/**/*'
- 'pyproject.toml'
- '/.python-version'
```
```yaml title="/moon.yml" {6-9}
language: 'ruby'
tasks:
build:
command: 'rake build --quiet'
inputs:
- 'src/**/*'
- 'Rakefile'
- '/Gemfile'
```
```yaml title="/moon.yml" {6-9}
language: 'rust'
tasks:
build:
command: 'cargo build --release'
inputs:
- 'src/**/*'
- 'Cargo.toml'
- '/Cargo.toml'
```
This list of inputs may look complicated, but they are merely run checks. For example, when moon
detects a change in...
- Any files within the `src` folder, relative from the project's root.
- A config file in the project's root.
- A shared config file in the workspace root (denoted by the leading `/`).
...the task will be ran! If the change occurs _outside_ of the project or _outside_ the list of
inputs, the task will _not_ be ran.
:::tip
Inputs are a powerful feature that can be fine-tuned to your project's need. Be as granular or open
as you want, the choice is yours!
:::
### Outputs
Outputs are the opposite of [inputs](#inputs), as they are files and folders that are created as a
result of running the task. With that being said, outputs are _optional_, as not all tasks require
them, and the ones that do are typically build related.
Now why is declaring outputs important? For incremental builds and smart caching! When moon
encounters a build that has already been built, it hydrates all necessary outputs from the cache,
then immediately exits. No more waiting for long builds!
Continuing our example, let's route the built files and expand our task with the
[`outputs`](./config/project#outputs) setting.
```yaml title="/moon.yml" {6,11,12}
language: 'javascript'
toolchain:
default: 'bun'
tasks:
build:
command: 'webpack build --mode production --no-stats --output-path @out(0)'
inputs:
- 'src/**/*'
- 'webpack.config.js'
- '/webpack-shared.config.js'
outputs:
- 'build'
```
```yaml title="/moon.yml" {5,10,11}
language: 'typescript'
tasks:
build:
command: 'deno compile ./src/main.ts -o ./out.js'
inputs:
- 'src/**/*'
- 'deno.*'
outputs:
- 'out.js'
```
```yaml title="/moon.yml" {5,10,11}
language: 'go'
tasks:
build:
command: 'go build -o @out(0)'
inputs:
- 'src/**/*'
outputs:
- 'build' # Just an example!
```
```yaml title="/moon.yml" {5,10,11}
language: 'javascript'
tasks:
build:
command: 'webpack build --mode production --no-stats --output-path @out(0)'
inputs:
- 'src/**/*'
- 'webpack.config.js'
- '/webpack-shared.config.js'
outputs:
- 'build'
```
```yaml title="/moon.yml" {10,11}
language: 'php'
tasks:
build:
command: 'phar pack -h sha256'
inputs:
- 'src/**/*'
- 'composer.json'
- '/composer.json'
outputs:
- 'file.phar' # Just an example!
```
```yaml title="/moon.yml" {10,11}
language: 'python'
tasks:
build:
command: 'python -m build'
inputs:
- 'src/**/*'
- 'pyproject.toml'
- '/.python-version'
outputs:
- 'dist'
```
```yaml title="/moon.yml" {10,11}
language: 'ruby'
tasks:
build:
command: 'rake build --quiet'
inputs:
- 'src/**/*'
- 'Rakefile'
- '/Gemfile'
outputs:
- 'build' # Just an example!
```
```yaml title="/moon.yml" {10,11}
language: 'rust'
tasks:
build:
command: 'cargo build --release'
inputs:
- 'src/**/*'
- 'Cargo.toml'
- '/Cargo.toml'
outputs:
- 'target/release' # Just an example!
```
## Depending on other tasks
For scenarios where you need run a task _before_ another task, as you're expecting some repository
state or artifact to exist, can be achieved with the [`deps`](./config/project#deps) setting, which
requires a list of [targets](./concepts/target):
- `:` - Full canonical target.
- `~:` or `` - A task within the current project.
- `^:` - A task from all [depended on projects](./concepts/project#dependencies).
```yaml title="/moon.yml" {1,7,8}
dependsOn:
# ...
tasks:
build:
# ...
deps:
- '^:build'
```
## Using file groups
Once you're familiar with configuring tasks, you may notice certain inputs being repeated
constantly, like source files, test files, and configuration. To reduce the amount of boilerplate
required, moon provides a feature known as [file groups](./concepts/file-group), which enables
grouping of similar file types within a project using
[file glob patterns or literal file paths](./concepts/file-pattern).
File groups are defined with the [`fileGroups`](./config/project#filegroups) setting, which maps a
list of file paths/globs to a group, like so.
```yaml title="/moon.yml"
fileGroups:
configs:
- '*.config.js'
sources:
- 'src/**/*'
- 'types/**/*'
tests:
- 'tests/**/*'
```
We can then replace the inputs in our task above with these new file groups using a syntax known as
[tokens](./concepts/token), specifically the [`@globs`](./concepts/token#globs) and
[`@files`](./concepts/token#files) token functions. Tokens are an advanced feature, so please refer
to their documentation for more information!
```yaml title="/moon.yml" {11}
language: 'javascript'
toolchain:
default: 'bun'
fileGroups:
# ...
tasks:
build:
command: 'webpack build --mode production --no-stats --output-path @out(0)'
inputs:
- '@globs(sources)'
- 'webpack.config.js'
- '/webpack-shared.config.js'
outputs:
- 'build'
```
```yaml title="/moon.yml" {8,10}
language: 'typescript'
fileGroups:
# ...
tasks:
build:
command: 'deno compile ./src/main.ts -o ./out.js'
inputs:
- '@globs(sources)'
- 'deno.*'
outputs:
- 'out.js'
```
```yaml title="/moon.yml" {8,10}
language: 'go'
fileGroups:
# ...
tasks:
build:
command: 'go build -o @out(0)'
inputs:
- '@globs(sources)'
outputs:
- 'build' # Just an example!
```
```yaml title="/moon.yml" {10}
language: 'javascript'
fileGroups:
# ...
tasks:
build:
command: 'webpack build --mode production --no-stats --output-path @out(0)'
inputs:
- '@globs(sources)'
- 'webpack.config.js'
- '/webpack-shared.config.js'
outputs:
- 'build'
```
```yaml title="/moon.yml" {10}
language: 'php'
fileGroups:
# ...
tasks:
build:
command: 'phar pack -h sha256'
inputs:
- '@globs(sources)'
- 'composer.json'
- '/composer.json'
outputs:
- 'file.phar' # Just an example!
```
```yaml title="/moon.yml" {10}
language: 'python'
fileGroups:
# ...
tasks:
build:
command: 'python -m build'
inputs:
- '@globs(sources)'
- 'pyproject.toml'
- '/.python-version'
outputs:
- 'dist'
```
```yaml title="/moon.yml" {10}
language: 'ruby'
fileGroups:
# ...
tasks:
build:
command: 'rake build --quiet'
inputs:
- '@globs(sources)'
- 'Rakefile'
- '/Gemfile'
outputs:
- 'build' # Just an example!
```
```yaml title="/moon.yml" {10}
language: 'rust'
fileGroups:
# ...
tasks:
build:
command: 'cargo build --release'
inputs:
- '@globs(sources)'
- 'Cargo.toml'
- '/Cargo.toml'
outputs:
- 'target/release' # Just an example!
```
With file groups (and tokens), you're able to reduce the amount of configuration required _and_
encourage certain file structures for consuming projects!
## Next steps
.moon/tasks/**/* further
),
url: './config/tasks',
},
{
icon: 'project-config',
label: (
Configure moon.* further
),
url: './config/project',
},
{ icon: 'task', label: 'Learn about tasks', url: './concepts/task' },
{ icon: 'token', label: 'Learn about tokens', url: './concepts/token' },
]}
/>
---
## VS Code extension
Enhance your VS Code experience with our integrated moon console! Whether you're a fan of the
command line, or prefer interactive interfaces, our console will be a welcome experience.
> This extension is in its early stages. Expect more advanced features in the future, like
> autocompletion, config validation, and more!
## Views
All views are available within the moon sidebar. Simply click the moon icon in the left activity
bar!
}>
### Projects
The backbone of moon is the projects view. In this view, all moon configured projects will be
listed, categorized by their [`layer`](../config/project#layer), [`stack`](../config/project#stack),
and designated with their [`language`](../config/project#language).
Each project can then be expanded to view all available tasks. Tasks can be ran by clicking the `▶`
icon, or using the command palette.
> This view is available in both the "Explorer" and "moon" sidebars.
}>
### Tags
Similar to the projects view, the tags view displays projects grouped by their
[`tags`](../config/project#tags).
> This view is only available in the "moon" sidebar.
}
>
### Last run
Information about the last ran task will be displayed in a beautiful table with detailed stats.
This table displays all actions that were ran alongside the primary target(s). They are ordered
topologically via the action graph.
## Features
### YAML validation
To enable accurate validation of our YAML configuration files, you'll need to update the
`yaml.schemas` setting in `.vscode/settings.json` to point to the local schemas at
`.moon/cache/schemas`.
This can be automated by running the "moon: Append YAML schemas configuration to settings" in the
command palette, after the extension has been installed.
## Troubleshooting
View the
[official VS Code marketplace](https://marketplace.visualstudio.com/items?itemName=moonrepo.moon-console)
for more information on the extension, its commands, available settings, and more!
---
## Environment variables
moon interacts with environment variables in two directions:
- **[Variables moon sets](#variables-moon-sets)** — injected into the environment of every task
process (and the child processes moon spawns), so your commands and scripts can read them.
- **[Variables moon reads](#variables-moon-reads)** — consumed from the shell to configure moon
itself, override [configuration settings](./config/workspace), or toggle behavior.
:::info
moon embeds [proto](/proto) for toolchain management, so proto's own environment variables (like
`PROTO_HOME` or `PROTO_LOG`) also apply. See the [proto documentation](/proto) for details. This
page only documents moon's variables.
:::
## Variables moon sets
### Within tasks
The following variables are injected into the environment of _every_ [task](./concepts/task) that
moon runs, and can be referenced from within your commands, scripts, and `.env` files.
| Variable | Description |
| :---------------------- | :-------------------------------------------------------------------------------------------------------------------------------- |
| `MOON_CACHE_DIR` | Absolute path to moon's cache directory (`.moon/cache`). |
| `MOON_PROJECT_ID` | ID of the [project](./concepts/project) the running task belongs to. |
| `MOON_PROJECT_ROOT` | Absolute path to the project's root directory. |
| `MOON_PROJECT_SOURCE` | The project's source path, relative from the workspace root. |
| `MOON_PROJECT_SNAPSHOT` | Absolute path to a JSON snapshot of the project (the same data as `moon project --json`), regenerated for each run. |
| `MOON_TARGET` | Fully-qualified [target](./concepts/target) of the running task, e.g. `app:build`. |
| `MOON_TASK_ID` | ID of the running task. |
| `MOON_TASK_HASH` | The unique hash generated for the running task. |
| `MOON_WORKSPACE_ROOT` | Absolute path to the [workspace](./concepts/workspace) root. |
| `MOON_WORKING_DIR` | Absolute path to the directory moon was invoked from. |
| `PWD` | Absolute path to the directory the task runs in — the project root, or the workspace root when `runFromWorkspaceRoot` is enabled. |
:::note
Toolchains may inject additional variables (and prepend their binary directories to `PATH`) when
setting up a task's environment. The variables above are always present regardless of toolchain.
:::
### During retries
When a task is retried (via [`retryCount`](./config/project#retrycount)), these are set for each
attempt.
| Variable | Description |
| :------------------------ | :----------------------------------------------- |
| `MOON_TASK_RETRY_ATTEMPT` | The current attempt number (starts at 1). |
| `MOON_TASK_RETRY_TOTAL` | The total number of attempts that will be tried. |
### Affected files
When a task configures [`affectedFiles`](./config/project#affectedfiles) to pass results through the
environment, moon sets the following.
| Variable | Description |
| :-------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MOON_AFFECTED_FILES` | The list of affected files (relative to the working directory), joined by the OS path delimiter. May be `.` or empty when there are no matches, depending on configuration. |
### Terminal & color
When [`pipeline.inheritColorsForPipedTasks`](./config/workspace#inheritcolorsforpipedtasks) is
enabled (the default), moon inherits the terminal's color support into piped task processes.
| Variable | Description |
| :--------------- | :---------------------------------------------------------------------- |
| `FORCE_COLOR` | Set to the detected color support level so child processes emit color. |
| `CLICOLOR_FORCE` | Same as `FORCE_COLOR`, for tools that follow the `CLICOLOR` convention. |
| `COLUMNS` | Forced to `80` so cached output is consistent across machines. |
| `LINES` | Forced to `24` so cached output is consistent across machines. |
> `NO_COLOR` is removed from the task environment when colors are being forced.
### proto integration
Set on task processes and any proto/toolchain child processes that moon spawns.
| Variable | Value | Description |
| :----------------------------- | :------ | :------------------------------------------------------------------------ |
| `PROTO_AUTO_INSTALL` | `false` | Prevents proto from auto-installing tools during a task run. |
| `PROTO_IGNORE_MIGRATE_WARNING` | `true` | Silences proto migration warnings. |
| `PROTO_NO_PROGRESS` | `true` | Disables proto progress bars. |
| `PROTO_VERSION` | pinned | The proto version pinned in [`.moon/toolchains.yml`](./config/toolchain). |
| `STARBASE_FORCE_TTY` | `true` | Forces TTY behavior for consistent output. |
### Internal
Set by moon for its own processes and integrations.
| Variable | Description |
| :-------------------- | :------------------------------------------------------------------------------------------- |
| `MOON_VERSION` | The currently running moon version. |
| `MOON_DAEMON_RUNNING` | Set to `true` inside the [daemon](./guides/daemon) server process. |
| `MOON_VCS_REPO_SLUG` | The detected `owner/repository` slug, derived from the VCS remote (only if not already set). |
## Variables moon reads
### Global options
Each of these has an equivalent [global command line option](./commands/overview). The command line
option takes precedence over the environment variable.
| Variable | Option | Description |
| :----------------- | :-------------- | :------------------------------------------------------------------------------------------------- |
| `MOON_CACHE` | `--cache` | Mode for [cache](./concepts/cache) operations: `read`, `read-write` (default), `write`, or `off`. |
| `MOON_COLOR` | `--color` | Force colored output. |
| `MOON_CONCURRENCY` | `--concurrency` | Maximum number of threads to utilize. Defaults to the number of CPU cores. |
| `MOON_DUMP` | `--dump` | Dump a trace profile to the working directory. |
| `MOON_LOG` | `--log` | Lowest log level to output: `off`, `error`, `warn`, `info` (default), `debug`, `trace`, `verbose`. |
| `MOON_LOG_FILE` | `--log-file` | Path to a file to write logs to. |
| `MOON_QUIET` | `--quiet` | Hide all moon console output. |
| `MOON_THEME` | `--theme` | Terminal theme to print with. |
### Running tasks & the pipeline
Read by [`moon exec`](./commands/exec), [`moon run`](./commands/run), [`moon check`](./commands/check), and [`moon ci`](./commands/ci).
Most map to a command line option.
| Variable | Option | Description |
| :----------------------- | :-------------------- | :------------------------------------------------------------------------------------------------------- |
| `MOON_FORCE` | `--force` | Force run and bypass cache, ignore changed files, and skip affected checks. |
| `MOON_NO_ACTIONS` | `--no-actions` | Run the pipeline without sync and setup related actions. |
| `MOON_EXEC_PLAN` | `--plan` | Relative path to an execution plan (JSON) that customizes the action graph. |
| `MOON_SUMMARY` | `--summary` | Print a summary of all actions that ran in the pipeline. |
| `MOON_JOB` | `--job` | Zero-based index of the current job (for [CI job sharding](./guides/ci)). |
| `MOON_JOB_TOTAL` | `--job-total` | Total amount of jobs to run. |
| `MOON_AFFECTED` | `--affected` | Only run tasks affected by changed files. |
| `MOON_BASE` | `--base` | Base branch, commit, or revision to compare [affected](./concepts/affected) against. |
| `MOON_HEAD` | `--head` | Current branch, commit, or revision to compare with. |
| `MOON_INCLUDE_RELATIONS` | `--include-relations` | Include graph relations for affected checks, instead of just changed files. |
| `MOON_ON_FAILURE` | `--on-failure` | When a task fails, either bail the pipeline or continue executing. |
| `MOON_OUTPUT_STYLE` | — | The [`outputStyle`](./config/project#outputstyle) task option in which output is printed. |
| `MOON_RETRY_COUNT` | — | The [`retryCount`](./config/project#retrycount) task option — number of times a failing task is retried. |
### Skipping actions
Skip individual [pipeline actions](./guides/exec-plan). Set to `true` (or `1`, `*`) to skip all, or
provide a comma-separated list of project IDs / toolchain IDs / targets to skip only those that
match.
| Variable | Description |
| :---------------------------- | :---------------------------------------------------------- |
| `MOON_SKIP_SYNC_WORKSPACE` | Skip the `SyncWorkspace` action. |
| `MOON_SKIP_SYNC_PROJECT` | Skip `SyncProject` actions (matches project IDs). |
| `MOON_SKIP_SETUP_TOOLCHAIN` | Skip `SetupToolchain` actions (matches toolchain IDs). |
| `MOON_SKIP_SETUP_ENVIRONMENT` | Skip `SetupEnvironment` actions (matches toolchain IDs). |
| `MOON_SKIP_INSTALL_DEPS` | Skip `InstallDependencies` actions (matches toolchain IDs). |
### Toolchain versions
`MOON__VERSION` overrides the version of a [toolchain](./concepts/toolchain) configured
in [`.moon/toolchains.yml`](./config/toolchain), where `` is the uppercased toolchain ID.
This is especially useful for testing against multiple versions in a [CI matrix](./guides/open-source).
For example:
```shell
$ MOON_NODE_VERSION=20.0.0 moon run app:build
$ MOON_RUST_VERSION=1.90.0 moon run app:build
```
### Configuration overrides
Override [`.moon/workspace.yml`](./config/workspace) settings without editing the file.
| Variable | Setting |
| :------------------------------------- | :------------------------------------------------------------------------- |
| `MOON_DAEMON` | [`daemon`](./guides/daemon) |
| `MOON_TELEMETRY` | [`telemetry`](./config/workspace#telemetry) |
| `MOON_WEBHOOK_URL` | [`notifier.webhookUrl`](./config/workspace#webhookurl) |
| `MOON_WEBHOOK_ACKNOWLEDGE` | [`notifier.webhookAcknowledge`](./config/workspace#webhookacknowledge) |
| `MOON_PIPELINE_AUTO_CLEAN_CACHE` | [`pipeline.autoCleanCache`](./config/workspace#autocleancache) |
| `MOON_PIPELINE_CACHE_LIFETIME` | [`pipeline.cacheLifetime`](./config/workspace#cachelifetime) |
| `MOON_PIPELINE_KILL_PROCESS_THRESHOLD` | [`pipeline.killProcessThreshold`](./config/workspace#killprocessthreshold) |
#### Remote caching
Override [`remote`](./config/workspace#remote) settings for [remote caching](./guides/remote-cache).
| Variable | Setting / description |
| :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------- |
| `MOON_REMOTE_HOST` | [`remote.host`](./config/workspace#host) — gRPC host to connect to. |
| `MOON_REMOTE_API` | [`remote.api`](./config/workspace#api) — API format of the remote service. |
| `MOON_REMOTE_AUTH_TOKEN` | [`remote.auth.token`](./config/workspace#token) — name of the env var to use as a bearer token. |
| `MOON_REMOTE_CACHE_*` | Override [`remote.cache`](./config/workspace#remote) fields (e.g. `MOON_REMOTE_CACHE_COMPRESSION`, `MOON_REMOTE_CACHE_INSTANCE_NAME`). |
| `MOON_REMOTE_TLS_HTTP2` | [`remote.tls.assumeHttp2`](./config/workspace#tls). |
| `MOON_REMOTE_TLS_*` | Override [`remote.tls`](./config/workspace#tls) fields. |
| `MOON_REMOTE_MTLS_HTTP` | [`remote.mtls.assumeHttp2`](./config/workspace#mtls). |
| `MOON_REMOTE_MTLS_*` | Override [`remote.mtls`](./config/workspace#mtls) fields. |
#### Experiments
Toggle [`experiments`](./config/workspace#experiments). Accepts a boolean-like value (`true`, `1`,
`false`, `0`).
| Variable | Setting |
| :---------------------------------------- | :------------------------------------------------------------------------------ |
| `MOON_EXPERIMENT_ASYNC_AFFECTED_TRACKING` | [`experiments.asyncAffectedTracking`](./config/workspace#asyncaffectedtracking) |
| `MOON_EXPERIMENT_ASYNC_GRAPH_BUILDING` | [`experiments.asyncGraphBuilding`](./config/workspace#asyncgraphbuilding) |
| `MOON_EXPERIMENT_CAS_OUTPUTS_CACHE` | [`experiments.casOutputsCache`](./config/workspace#casoutputscache) |
| `MOON_EXPERIMENT_NATIVE_FILE_HASHING` | [`experiments.nativeFileHashing`](./config/workspace#nativefilehashing) |
### Debugging
Declare any of these (to a truthy value) to reveal additional diagnostic output. See
[debugging](./commands/overview#debugging) for more.
| Variable | Description |
| :------------------------- | :------------------------------------------------------------------------------------------------------------------------------------ |
| `MOON_DEBUG_PROCESS_ENV` | Reveal the full environment passed to processes. By default moon hides everything except `MOON_*` variables to avoid leaking secrets. |
| `MOON_DEBUG_PROCESS_INPUT` | Reveal the full stdin passed to processes, instead of truncating it. |
| `MOON_DEBUG_REMOTE` | Extra logging for remote caching, including internal connection errors. |
| `MOON_DEBUG_WASM` | Extra logging for [WASM plugins](./guides/wasm-plugins), and optionally dumps memory/core profiles. |
| `MOON_DEBUG_DAEMON` | Extra logging for the [daemon](./guides/daemon). |
| `MOON_DEBUG_MCP` | Extra logging for the [MCP server](./guides/mcp). |
### Graph visualizer
Read by the graph commands ([`project-graph`](./commands/project-graph),
[`task-graph`](./commands/task-graph), [`action-graph`](./commands/action-graph)) when starting the
local visualization server.
| Variable | Option | Description |
| :------------ | :------- | :--------------------------------------------------------------- |
| `MOON_HOST` | `--host` | The host address to bind the server to. Defaults to `127.0.0.1`. |
| `MOON_PORT` | `--port` | The port to bind to. Defaults to `0` (a random open port). |
| `MOON_JS_URL` | — | Override the URL of the visualizer JavaScript bundle (advanced). |
### Store & installation
| Variable | Description |
| :----------------- | :------------------------------------------------------------------------------------------------------------- |
| `MOON_HOME` | Override moon's store directory. Defaults to `~/.moon` (or `$XDG_DATA_HOME/moon` when `XDG_DATA_HOME` is set). |
| `MOON_INSTALL_DIR` | Directory that [`moon upgrade`](./commands/upgrade) installs the binary into. Defaults to `~/.moon/bin`. |
### Advanced
| Variable | Description |
| :----------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------- |
| `MOON_TOOLCHAIN_FORCE_GLOBALS` | Force toolchains to use globally installed tools instead of proto-managed versions. Set automatically within [Docker](./guides/docker). |
| `MOON_PLUGINS_USE_URL_DIST` | Load plugins from their URL distribution instead of a locally built artifact. |
| `MOON_VCS_REPO_SLUG` | Override the detected `owner/repository` slug (see [above](#internal)). |
| `MOON_TRACE_ID` | A trace/correlation ID included in [webhook](./guides/webhooks) payloads. |
## System variables
moon also inspects a number of standard, third-party variables to detect the current environment.
These are read, never written.
| Variable(s) | Purpose |
| :---------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------ |
| `CI`, `CI_NAME`, `AZURE_PIPELINES` | Detect whether moon is running in a [CI](./guides/ci) environment. |
| `NO_COLOR`, `FORCE_COLOR`, `CLICOLOR`, `CLICOLOR_FORCE` | Standard [color output](./commands/overview#colors) controls, honored and propagated. |
| `XDG_DATA_HOME` | Fallback location for moon's store directory. |
| `SSH_CLIENT`, `SSH_TTY` | Detect a remote SSH session. |
| `CODESPACES`, `GITHUB_CODESPACES`, `GITPOD_INSTANCE_ID`, `DEVPOD`, `REPL_ID`, and similar | Detect a cloud development environment ("devbox"). |
| `PATH` | Resolve binaries, and the base that toolchains prepend their bin directories to. |
---
## FAQ
## General
### Where did the name "moon" come from?
The first incarnation of the name was a misspelling of monorepo (= moonrepo). This is where the
domain moonrepo.dev came from, and our official company, moonrepo, Inc.
However, moonrepo is quite a long name with many syllables, and as someone who prefers short 1
syllable words, moon was perfect. The word moon also has great symmetry, as you can see in our logo!
But that's not all... moon is also an acronym. It originally stood for **m**onorepo,
**o**rganization, **o**rchestration, and **n**otification tool. But since moon can also be used for
polyrepos, we replaced monorepo with **m**anagement (as shown on the homepage). This is a great
acronym, as it embraces what moon is trying to solve:
- **M**anage repos, projects, and tasks with ease.
- **O**rganize projects and the repo to scale.
- **O**rchestrate tasks as efficiently as possible.
- **N**otify developers and systems about important events.
### Will moon support other languages?
Yes! Although we're focusing right now on the web ecosystem (Node.js, Rust, Go, PHP, Python, etc),
we've designed moon to be language agnostic and easily pluggable in the future. View our
[supported languages for more information](/docs#supported-languages).
### Will moon support continuous deployment?
Yes! We plan to integrate CD with the current build and CI system, but we are focusing on the latter
2 for the time being. Why not start using moon today so that you can easily adopt CD when it's
ready?
### How to stop moon formatting JSON and YAML files?
To ensure a healthy repository state, moon constantly modifies JSON and YAML files, specifically
`package.json` and `tsconfig.json`. This may result in a different formatting style in regards to
indentation. While there is no way to stop or turn off this functionality, we respect
[EditorConfig](https://editorconfig.org/) during this process.
Create a root `.editorconfig` file to enforce a consistent syntax.
```ini title=".editorconfig"
[*.{json,yaml,yml}]
indent_style = space
indent_size = 4
```
## Projects & tasks
### How to pipe or redirect tasks?
Piping (`|`) or redirecting (`>`) the output of one moon task to another moon task, whether via
stdin or through `inputs`, is not possible within our pipeline (task runner) directly.
However, we do support this functionality on the command line, or within a task itself, using the
[`script`](./config/project#script) setting.
```yaml title="moon.yml"
tasks:
pipe:
script: 'gen-json | jq ...'
```
Alternativaly, you can wrap this script in something like a Bash file, and execute that instead.
```bash title="scripts/pipe.sh"
#!/usr/bin/env bash
gen-json | jq ...
```
```yaml title="moon.yml"
tasks:
pipe:
command: 'bash ./scripts/pipe.sh'
```
### How to run multiple commands within a task?
Only [`script`](./config/project#script) based tasks can run multiple commands via `&&` or `;`
syntax. This is possible as we execute the entire script within a shell, and not directly with the
toolchain.
```yaml title="moon.yml"
tasks:
multiple:
script: 'mkdir test && cd test && do-something'
```
### How to run tasks in a shell?
By default, all tasks run in a shell, based on the task's [`shell`](./config/project#shell) option,
as demonstrated below:
```yaml title="moon.yml"
tasks:
# Runs in a shell
global:
command: 'some-command-on-path'
# Custom shells
unix:
command: 'bash -c some-command'
options:
shell: false
windows:
command: 'pwsh.exe -c some-command'
options:
shell: false
```
### Can we run other languages?
Yes! Although our toolchain only supports a few languages at this time, you can still run other
languages within tasks by setting their [`toolchain`](./config/project#toolchain) to "system".
System tasks are an escape hatch that will use any command available on the current machine.
```yaml title="moon.yml"
tasks:
# Ruby
lint:
command: 'rubocop'
toolchain: 'system'
# PHP
test:
command: 'phpunit tests'
toolchain: 'system'
```
However, because these languages are not supported directly within our toolchain, they will not
receive the benefits of the toolchain. Some of which are:
- Automatic installation of the language. System tasks expect the command to already exist in the
environment, which requires the user to manually install them.
- Consistent language and dependency manager versions across all machines.
- Built-in cpu and heap profiling (language specific).
- Automatic dependency installs when the lockfile changes.
- And many more.
## JavaScript ecosystem
### Can we use `package.json` scripts?
We encourage everyone to define tasks in a [`moon.*`](./config/project#tasks) file, as it allows for
additional metadata like `inputs`, `outputs`, `options`, and more. However, if you'd like to keep
using `package.json` scripts, enable the
[`node.inferTasksFromScripts`](./config/toolchain#infertasksfromscripts) setting.
### Can moon version/publish packages?
At this time, no, as we're focusing on the build and test aspect of development. With that being
said, this is something we'd like to support first-class in the future, but until then, we suggest
the following popular tools:
- [Yarn releases](https://yarnpkg.com/features/release-workflow) (requires >= v2)
- [Changesets](https://github.com/changesets/changesets)
- [Lerna](https://github.com/lerna/lerna)
### Why is npm/pnpm/yarn install running twice when running a task?
moon will automatically install dependencies in a project or in the workspace root (when using
package workspaces) when the lockfile or `package.json` has been modified since the last time the
install ran. If you are running a task and multiple installs are occurring (and it's causing
issues), it can mean 1 of 2 things:
- If you are using package workspaces, then one of the projects triggering the install is not listed
within the `workspaces` field in the root `package.json` (for npm and yarn), or in
`pnpm-workspace.*` (for pnpm).
- If the install is triggering in a non-JavaScript related project, then this project is incorrectly
listed as a package workspace.
- If you don't want a package included in the workspace, but do want to install its dependencies,
then it'll need its own lockfile.
## Troubleshooting
### How to resolve the "version 'GLIBC_X.XX' not found" error?
This is typically caused by running moon in an old environment, like Ubuntu 18, and the minimum
required libc doesn't exist or is too old. Since moon is Rust based, we're unable to support all
environments and versions perpetually, and will only support relatively modern environments.
There's not an easy fix to this problem, but there are a few potential solutions, from easiest to
hardest:
- Run moon in a Docker container/image that has the correct environment and libs. For example, the
`node:latest` image.
- Upgrade the environment to a newer one. For example, Ubuntu 18 -> 22.
- Try and install a newer libc
([more information](https://stackoverflow.com/questions/72513993/how-install-glibc-2-29-or-higher-in-ubuntu-18-04)).
For more information on this problem as a whole,
[refer to this in-depth article](https://kobzol.github.io/rust/ci/2021/05/07/building-rust-binaries-in-ci-that-work-with-older-glibc.html).
---
## Continuous integration (CI)
All companies and projects rely on continuous integration (CI) to ensure high quality code and to
avoid regressions. Because this is such a critical piece of every developer's workflow, we wanted to
support it as a first-class feature within moon, and we do just that with the
[`moon ci`](../commands/ci) command.
## How it works
The `ci` command does all the heavy lifting necessary for effectively running jobs. It achieves this
by automatically running the following steps:
- Determines changed files by comparing the current HEAD against a base.
- Determines all [targets](../concepts/target) that need to run based on changed files.
- Additionally runs affected [targets](../concepts/target) dependencies _and_ dependents.
- Generates an action and dependency graph.
- Installs the toolchain and applicable dependencies.
- Runs all actions within the graph using a thread pool.
- Displays stats about all passing, failed, and invalid actions.
## Configuring tasks
By default, _all tasks_ run in CI, as you should always be building, linting, typechecking, testing,
so on and so forth. However, this isn't always true, so this can be disabled on a per-task basis
through the [`runInCI`](../config/project#runinci) option.
```yaml
tasks:
dev:
command: 'webpack server'
options:
runInCI: false
```
:::caution
This option _must_ be set to false for tasks that spawn a long-running or never-ending process, like
HTTP or development servers. To help mitigate this, tasks named `dev`, `start`, or `serve` are false
by default.
:::
## Integrating
The following examples can be referenced for setting up moon and its CI workflow in popular
providers. For GitHub, we're using our
[`setup-toolchain` action](https://github.com/moonrepo/setup-toolchain) to install moon. For other
providers, we assume moon is an npm dependency and must be installed with Node.js.
```yaml title=".github/workflows/ci.yml"
name: 'Pipeline'
on:
push:
branches:
- 'master'
pull_request:
jobs:
ci:
name: 'CI'
runs-on: 'ubuntu-latest'
steps:
- uses: 'actions/checkout@v4'
with:
fetch-depth: 0
filter: 'blob:none'
- uses: 'moonrepo/setup-toolchain@v0'
- run: 'moon ci'
```
```yaml title=".buildkite/pipeline.yml"
steps:
- label: 'CI'
commands:
- 'moon ci'
```
```yaml title=".circleci/config.yml"
version: 2.1
jobs:
ci:
docker:
- image: 'cimg/base:stable'
steps:
- 'checkout'
- run: 'moon ci'
workflows:
pipeline:
jobs:
- 'ci'
```
```yaml title=".travis.yml"
language: 'node_js'
script: 'moon ci'
```
:::info
moon requires a full commit history to accurately determine the merge base and changed files
between revisions. Avoid shallow clones (e.g. a fetch depth of 1), as affected detection will be
inaccurate or fail entirely. If clone speed is a concern, use a blobless
[partial clone](https://git-scm.com/docs/partial-clone) instead — `git clone --filter=blob:none`,
or `filter: 'blob:none'` with `actions/checkout` — which keeps the full history while only
downloading file content on demand.
:::
## Choosing targets
By default `moon ci` will run _all_ tasks from _all_ projects that are affected by changed files and
have the [`runInCI`](../config/project#runinci) task option enabled. This is a great catch-all
solution, but may not vibe with your workflow or requirements.
If you'd prefer more control, you can pass a list of targets to `moon ci`, instead of moon
attempting to detect them. When providing targets, `moon ci` will still only run them if affected by
changed files, but will still filter with the `runInCI` option.
```shell
# Run all builds
$ moon ci :build
# In another job, run tests
$ moon ci :test :lint
```
## Comparing revisions
By default the command will attempt to detect the base and head revisions automatically based on the
current CI provider (powered by the [`ci_env`](https://github.com/milesj/rust-cicd-env) Rust crate).
If nothing was detected, this will fallback to the configured
[`vcs.defaultBranch`](../config/workspace#defaultbranch) for the base revision, and `HEAD` for the
head revision.
These values can be customized with the `--base` and `--head` command line options, or the
`MOON_BASE` and `MOON_HEAD` environment variables, which takes highest precedence.
```shell
$ moon ci --base --head
# Or
$ MOON_BASE= MOON_HEAD= moon ci
```
## Parallelizing tasks
If your CI environment supports sharding across multiple jobs, then you can utilize moon's built in
parallelism by passing `--job-total` and `--job` options. The `--job-total` option is an integer of
the total number of jobs available, and `--job` is the current index (0 based) amongst the total.
When these options are passed, moon will only run affected [targets](../concepts/target) based on
the current job slice.
GitHub Actions do not support native parallelism, but it can be emulated using it's matrix.
```yaml title=".github/workflows/ci.yml"
# ...
jobs:
ci:
# ...
strategy:
matrix:
index: [0, 1]
steps:
# ...
- run: 'moon ci --job ${{ matrix.index }} --job-total 2'
```
- [Documentation](https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs)
```yaml title=".buildkite/pipeline.yml"
# ...
steps:
- label: 'CI'
parallelism: 10
commands:
# ...
- 'moon ci --job $$BUILDKITE_PARALLEL_JOB --job-total $$BUILDKITE_PARALLEL_JOB_COUNT'
```
- [Documentation](https://buildkite.com/docs/tutorials/parallel-builds#parallel-jobs)
```yaml title=".circleci/config.yml"
# ...
jobs:
ci:
# ...
parallelism: 10
steps:
# ...
- run: 'moon ci --job $CIRCLE_NODE_INDEX --job-total $CIRCLE_NODE_TOTAL'
```
- [Documentation](https://circleci.com/docs/2.0/parallelism-faster-jobs/)
TravisCI does not support native parallelism, but it can be emulated using it's matrix.
```yaml title=".travis.yml"
# ...
env:
global:
- TRAVIS_JOB_TOTAL=2
jobs:
- TRAVIS_JOB_INDEX=0
- TRAVIS_JOB_INDEX=1
script: 'moon ci --job $TRAVIS_JOB_INDEX --job-total $TRAVIS_JOB_TOTAL'
```
- [Documentation](https://docs.travis-ci.com/user/speeding-up-the-build/)
> Your CI environment may provide environment variables for these 2 values.
## Caching artifacts
When a CI pipeline reaches a certain scale, its run times increase, tasks are unnecessarily ran, and
build artifacts are not shared. To combat this, we support [remote caching](./remote-cache), a
mechanism where we store build artifacts in the cloud, and sync these artifacts to machines on
demand.
### Manual persistence
If you'd prefer to _not use_ remote caching at this time, you can cache artifacts yourself, by
persisting the `.moon/cache/{hashes,outputs}` directories. All other files and folders in
`.moon/cache` _should not_ be persisted, as they are not safe/portable across machines.
However, because tasks can generate a different hash each run, you'll need to manually invalidate
your cache. Blindly storing the `hashes` and `outputs` directories without a mechanism to invalidate
will simply not work, as the contents will drastically change between CI runs. This is the primary
reason why the remote caching service exists.
## Keeping dependencies updated
To automate toolchain and dependency updates, moon integrates with
[Renovate](https://docs.renovatebot.com/). Tool versions in [`.prototools`](../proto/config) are
updated out of the box, and moon publishes a shared preset for updating moon-specific configuration.
Refer to the [Renovate guide](./renovate) for setup.
## Reporting run results
If you're using GitHub Actions as your CI provider, we suggest using our
[`moonrepo/run-report-action`](https://github.com/marketplace/actions/moon-ci-run-reports). This
action will report the results of a [`moon ci`](../commands/ci) run to a pull request as a comment
and workflow summary.
For the generated report file locations and caching caveats, refer to the
[`moon ci` reports documentation](../commands/ci#reports).
```yaml title=".github/workflows/ci.yml"
# ...
jobs:
ci:
name: 'CI'
runs-on: 'ubuntu-latest'
steps:
# ...
- run: 'moon ci'
- uses: 'moonrepo/run-report-action@v1'
if: success() || failure()
with:
access-token: ${{ secrets.GITHUB_TOKEN }}
```
The report looks something like the following:
### Community offerings
The following GitHub actions are provided by the community:
- [`appthrust/moon-ci-retrospect`](https://github.com/appthrust/moon-ci-retrospect) - Displays the
results of a `moon ci` run in a more readable fashion.
- [`kymckay/moon-ci-booster`](https://github.com/kymckay/moon-ci-booster) - Displays failing
`moon ci` tasks as comments with error logs directly on your pull request.
---
## Code generation
Code generation provides an easy mechanism for automating common development workflows and file
structures. Whether it's scaffolding a new library or application, updating configuration, or
standardizing patterns.
To accomplish this, we provide a generator, which is divided into two parts. The first being the
templates and their files to be scaffolded. The second is our rendering engine that writes template
files to a destination.
## Creating a new template
To create a new template, run [`moon generate`][command] while passing the `--template` option. This
will create a template directory and [`template.*`][config] file in the 1st file-based template
location defined in [`generator.templates`][gen-templates].
```shell
$ moon generate --template
```
### Configuring `template.*`
Every template requires a [`template.*`][config] file in the template's directory root. This file
acts as a schema and declares metadata and variables required by the generator.
```yaml title="template.yml"
title: 'npm package'
description: |
Scaffolds the initial structure for an npm package,
including source and test folders, a package.json, and more.
variables:
name:
type: 'string'
default: ''
required: true
prompt: 'Package name?'
```
### Managing files
Feel free to add any files and folders to the template that you'd like to be generated by consumers!
These files will then be scaffolded 1:1 in structure at the target destination.
An example of the templates folder structure may look something like the following:
```
templates/
├── npm-package/
│ ├── src/
│ ├── tests/
│ ├── package.json
│ └── template.yml
└── react-app/
```
#### Interpolation
Variables can be interpolated into file paths using the form `[varName]`. For example, if you had a
template file `src/[type].ts`, and a variable `type` with a value of "bin", then the destination
file path would be `src/bin.ts`.
This syntax also supports [filters](#filters), such as `[varName | camel_case]`. However, spaces may
cause issues with file path encoding, so this functionality is primarily recommended for the
[`destination`](../config/template#destination) setting.
#### File extensions
To enable syntax highlighting for template engine syntax, you may use the `.tera` (preferred) or
`.twig` file extensions. These extensions are optional, but will be removed when the files are
generated.
Depending on your preferred editor, these extensions may be supported through a plugin, or can be
configured based on file type.
- **VS Code**
- [Tera extension](https://marketplace.visualstudio.com/items?itemName=karunamurti.tera)
- [Twig extension](https://marketplace.visualstudio.com/items?itemName=mblode.twig-language-2)
- **Atom**
- [Twig package](https://atom.io/packages/atom-twig)
- **Webstorm**
- [Twig plugin](https://plugins.jetbrains.com/plugin/7303-twig)
#### Partials
Partials are special template files that are used for
[composition](https://keats.github.io/tera/docs/#include) and
[inheritance](https://keats.github.io/tera/docs/#inheritance). Because of this, these files _should
not_ be generated into the target destination, and _do not_ support frontmatter.
To ensure they are not generated, include the word "partial" anywhere in the file path. For example,
`partials/header.tpl` or `header.partial.tpl`.
#### Raws
Raw template files are another special type of file that bypass all Tera rendering, and are used
as-is instead. This is useful for files that contain syntax that conflicts with Tera.
To mark a file as raw, add a `.raw` extension, for example: `file.raw.js` or `file.js.raw`. When the
file is generated, the `.raw` extension will be removed.
#### Frontmatter
Frontmatter is a well-known concept for "per-file configuration", and is achieved by inserting YAML
at the top of the file, delimited by wrapping `---`. This is a very powerful feature that provides
more control than the alternatives, and allows for some very cool integrations.
moon's frontmatter supports functionality like file skipping, force overwriting, and destination
path rewriting.
[View the configuration docs for a full list of supported fields](../config/template#frontmatter).
```twig title="package.json"
---
force: true
---
{
"name": "{{ name | kebab_case }}",
"version": "0.0.1"
}
```
Since frontmatter exists in the file itself, you can take advantage of the rendering engine to
populate the field values dynamically. For example, if you're scaffolding a React component, you can
convert the component name and file name to PascalCase.
```twig
{% set component_name = name | pascal_case %}
---
to: components/{{ component_name }}.tsx
---
export function {{ component_name }}() {
return ;
}
```
#### Assets
Assets are binary files that are copied as-is to the destination, without any rendering, and no
support for frontmatter. This applies to all non-text based files, like images, audio, video, etc.
### Template engine & syntax
Rendering templates is powered by [Tera](https://keats.github.io/tera/), a Rust based template
engine with syntax similar to Twig, Liquid, Django, and more. We highly encourage everyone to read
Tera's documentation for an in-depth understanding, but as a quick reference, Tera supports the
following:
- [Variable interpolation](https://keats.github.io/tera/docs/#variables) (defined with the
[`variables`](../config/template#variables) setting), with
[built-in filters](https://keats.github.io/tera/docs/#built-in-filters).
```twig
{{ varName }} -> foo
{{ varName | upper }} -> FOO
```
- [Conditional blocks](https://keats.github.io/tera/docs/#if) and
[loops](https://keats.github.io/tera/docs/#for).
```twig
{% if price < 10 or always_show %}
Price is {{ price }}.
{% elif price > 1000 and not rich %}
That's expensive!
{% else %}
N/A
{% endif %}
```
```twig
{% for item in items %}
{{ loop.index }} - {{ item.name }}
{% endfor %}
```
- And many more features, like auto-escaping, white space control, and math operators!
#### Filters
Filters are a mechanism for transforming values during interpolation and are written using pipes
(`|`). Tera provides many [built-in filters](https://keats.github.io/tera/docs/#built-in-filters),
but we also provide the following custom filters:
- Strings - `camel_case`, `pascal_case`, `snake_case`, `upper_snake_case`, `kebab_case`,
`upper_kebab_case`, `lower_case`, `upper_case`
```twig
{{ some_value | upper_case }}
```
- Paths - `path_join`, `path_relative`
```twig
{{ some_path | path_join(part = "another/folder") }}
{{ some_path | path_relative(from = other_path) }}
{{ some_path | path_relative(to = other_path) }}
```
#### Functions
The following functions are available within a template:
- `variables()` - Returns an object containing all variables within the current template.
#### Variables
The following variables are always available within a template:
- `dest_dir` - Absolute path to the destination folder.
- `dest_rel_dir` - Relative path to the destination folder from the working directory.
- `working_dir` - Current working directory.
- `workspace_root` - The moon workspace root.
## Generating code from a template
Once a template has been created and configured, you can generate files based on it using the
[`moon generate`][command] command! This is also know as scaffolding or code generation.
This command requires the name of a template as the 1st argument. The template name is the folder
name on the file system that houses all the template files, or the [`id`](../config/template#id)
setting configured in [`template.*`](../config/template).
```shell
$ moon generate npm-package
```
An optional destination path, relative from the current working directory, can be provided as the
2nd argument. If not provided, the [`destination`](../config/template#destination) setting
configured in [`template.*`](../config/template) will be used, or you'll be prompted during
generation to provide one.
```shell
$ moon generate npm-package --to ./packages/example
```
> This command is extremely interactive, as we'll prompt you for the destination path, variable
> values, whether to overwrite files, and more. If you'd prefer to avoid interactions, pass
> `--defaults`, or `--force`, or both.
### Configuring template locations
Templates can be located anywhere, especially when [being shared](#sharing-templates). Because of
this, our generator will loop through all template paths configured in
[`generator.templates`][gen-templates], in order, until a match is found.
```yaml title=".moon/workspace.yml"
generator:
templates:
- './templates'
# Or
- 'file://other/templates'
```
When using literal file paths, all paths are relative from the workspace root.
#### Archive URLs
Template locations can reference archives (zip, tar, etc) through https URLs. These archives should
contain templates and will be downloaded and unpacked. The list of
[available archive formats can be found here](https://github.com/moonrepo/starbase/blob/master/crates/archive/src/lib.rs#L76).
```yaml title=".moon/workspace.yml"
generator:
templates:
- 'https://domain.com/some/path/to/archive.zip'
```
> Archives will be unpacked to `~/.moon/templates/archive/`, and will be cached for future
> use.
#### Globs
If you'd prefer more control over literal file paths (above), you can instead use glob paths or the
`glob://` protocol. Globs are relative from the workspace root, and will only match directories, or
patterns that end in `template.*`.
```yaml title=".moon/workspace.yml"
generator:
templates:
- './templates/*'
# Or
- 'glob://projects/*/templates/*'
```
#### Git repositories
Templates locations can also reference templates in an external Git repository using the `git://`
locator protocol. This locator requires the Git host, repository path, and revision (branch, tag,
commit, etc).
```yaml title=".moon/workspace.yml"
generator:
templates:
- 'git://github.com/moonrepo/templates#master'
- 'git://gitlab.com/org/repo#v1.2.3'
```
> Git repositories will be cloned to `~/.moon/templates/git/` using an HTTPS URL (not a Git
> URL), and will be cached for future use.
#### npm packages
Additionally, template locations can also reference npm packages using the `npm://` locator
protocol. This locator requires a package name and published version.
```yaml title=".moon/workspace.yml"
generator:
templates:
- 'npm://@moonrepo/templates#1.2.3'
- 'npm://other-templates#4.5.6'
```
> npm packages will be downloaded and unpacked to `~/.moon/templates/npm` and cached for future use.
### Declaring variables with CLI arguments
During generation, you'll be prompted in the terminal to provide a value for any configured
variables. However, you can pre-fill these variable values by passing arbitrary command line
arguments after `--` to [`moon generate`][command]. Argument names must exactly match the variable
names.
Using the package template example above, we could pre-fill the `name` variable like so:
```shell
$ moon generate npm-package --to ./packages/example -- --name '@company/example' --private
```
:::info
- Array variables support multiple options of the same name.
- Boolean variables can be negated by prefixing the argument with `--no-`.
- Object variables _can not_ declare values through arguments.
:::
## Sharing templates
Although moon is designed for a monorepo, you may be using multiple repositories and would like to
use the same templates across all of them. So how can we share templates across repositories? Why
not try...
- Git submodules
- Git repositories (using `git://` protocol)
- File archives
- Node.js modules
- npm packages (using `npm://` protocol)
- Another packaging system
Regardless of the choice, simply configure [`generator.templates`][gen-templates] to point to these
locations:
```yaml title=".moon/workspace.yml"
generator:
templates:
- './templates'
- 'file://./templates'
# Git
- './path/to/submodule'
- 'git://github.com/org/repo#branch'
# npm
- './node_modules/@company/shared-templates'
- 'npm://@company/shared-templates#1.2.3'
```
### Git and npm layout structure
If you plan to share templates using Git repositories (`git://`) or npm packages (`npm://`), then
the layout of those projects must follow these guidelines:
- A project must support multiple templates
- A template is denoted by a folder in the root of the project
- Each template must have a [`template.*`][config] file
- Template names are derived from the folder name, or the `id` field in [`template.*`][config]
An example of this layout structure may look something like the following:
```
├── template-one/
│ └── template.yml
├── template-two/
│ └── template.yml
├── template-three/
│ └── template.yml
└── package.json, etc
```
These templates can then be referenced by name, such as [`moon generate template-one`][command].
[config]: ../config/template
[command]: ../commands/generate
[gen-templates]: ../config/workspace#templates
---
## Code owners
Code owners enables companies to define individuals, teams, or groups that are responsible for code
in a repository. This is useful in ensuring that pull/merge requests are reviewed and approved by a
specific set of contributors, before the branch is merged into the base branch.
With that being said, moon _does not_ implement a custom code owners solution, and instead builds
upon the popular `CODEOWNERS` integration in VCS providers, like GitHub, GitLab, and Bitbucket.
## Defining owners
With moon, you _do not_ modify a `CODEOWNERS` file directly. Instead you define owners _per project_
with [`moon.*`](../config/project), or globally with [`.moon/workspace.*`](../config/workspace).
These owners are then aggregated and automatically
[synced to a `CODEOWNERS` file](#generating-codeowners).
:::info
An owner is a user, team, or group unique to your VCS provider. Please refer to your provider's
documentation for the correct format in which to define owners.
:::
### Project-level
For projects, we support an [`owners`](../config/project#owners) setting in
[`moon.*`](../config/project) that accepts file patterns/paths and their owners (contributors
required to review), as well as operational settings for minimum required approvals, custom groups,
and more.
Paths configured here are relative from the project root, and will be prefixed with the project
source (path from workspace root to project root) when the file is synced.
```yaml title="packages/components/moon.yml"
owners:
requiredApprovals: 2
paths:
'src/': ['@frontend', '@design-system']
'*.config.js': ['@frontend-infra']
'*.json': ['@frontend-infra']
```
The configuration above would generate the following:
```shell title=".github/CODEOWNERS"
# components
/packages/components/src/ @frontend @design-system
/packages/components/*.config.js @frontend-infra
/packages/components/*.json @frontend-infra
```
```shell title=".gitlab/CODEOWNERS"
# components
[components][2]
/packages/components/src/ @frontend @design-system
/packages/components/*.config.js @frontend-infra
/packages/components/*.json @frontend-infra
```
```shell title=".bitbucket/CODEOWNERS"
# components
/packages/components/src/ @frontend @design-system
/packages/components/*.config.js @frontend-infra
/packages/components/*.json @frontend-infra
```
### Workspace-level
Project scoped owners are great but sometimes you need to define owners for files that span across
all projects, or files at any depth within the repository. With the
[`codeowners.globalPaths`](../config/workspace#globalpaths) setting in
[`.moon/workspace.*`](../config/workspace), you can do just that.
Paths configured here are used as-is, allowing for full control of what ownership is applied.
```yaml title=".moon/workspace.yml"
codeowners:
globalPaths:
# All files
'*': ['@admins']
# Config folder at any depth
'config/': ['@app-platform']
# GitHub folder at the root
'/.github/': ['@infra']
```
The configuration above would generate the following at the top of the file (is the same for all
providers):
```shell title=".github/CODEOWNERS"
# (workspace)
* @admins
config/ @app-platform
/.github/ @infra
```
```shell title=".gitlab/CODEOWNERS"
# (workspace)
* @admins
config/ @app-platform
/.github/ @infra
```
```shell title=".bitbucket/CODEOWNERS"
# (workspace)
* @admins
config/ @app-platform
/.github/ @infra
```
## Generating `CODEOWNERS`
Code owners is an opt-in feature, and as such, the `CODEOWNERS` file can be generated in a few ways.
The first is manually, with the [`moon sync codeowners`](../commands/sync/code-owners) command.
```shell
$ moon sync codeowners
```
While this works, it is a manual process, and can easily be forgotten, resulting in an out-of-date
file.
An alternative solution is the [`codeowners.sync`](../config/workspace#sync) setting in
[`.moon/workspace.*`](../config/workspace#codeowners), that when enabled, moon will automatically
generate a `CODEOWNERS` file when a [target](../concepts/target) is ran.
```yaml title=".moon/workspace.yml"
codeowners:
sync: true
```
> The format and location of the `CODEOWNERS` file is based on the
> [`vcs.provider`](../config/workspace#provider) setting.
## FAQ
### What providers or formats are supported?
The following providers are supported, based on the [`vcs.provider`](../config/workspace#provider)
setting.
- [Bitbucket](https://support.atlassian.com/bitbucket-cloud/docs/set-up-and-use-code-owners/) (native
code owners, uses the same syntax as GitHub)
- Bitbucket (legacy) — the `bitbucket-legacy` provider, for the 3rd-party
[DevSensei / Code Owners for Bitbucket](https://marketplace.atlassian.com/apps/1218598/code-owners-for-bitbucket?tab=overview&hosting=cloud)
app, which uses a root `CODEOWNERS` and the `Check()` syntax
- [GitHub](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners)
- [GitLab](https://docs.gitlab.com/ee/user/project/codeowners/reference.html)
- Other (very basic syntax)
:::warning
The `bitbucket` provider now targets Bitbucket's native code owners (`.bitbucket/CODEOWNERS`,
GitHub-compatible syntax). If you were relying on the
[DevSensei / Code Owners for Bitbucket](https://marketplace.atlassian.com/apps/1218598/code-owners-for-bitbucket?tab=overview&hosting=cloud)
app (root `CODEOWNERS` with the `Check()` syntax), set `vcs.provider` to `bitbucket-legacy` to keep
the previous output.
:::
### Where does the `CODEOWNERS` file get created?
The location of the file is dependent on the configured provider.
- GitHub -> `.github/CODEOWNERS`
- GitLab -> `.gitlab/CODEOWNERS`
- Bitbucket -> `.bitbucket/CODEOWNERS`
- Everything else (including `bitbucket-legacy`) -> `CODEOWNERS`
### Why are owners defined in `moon.*` and not an alternative like `OWNERS`?
A very popular pattern for defining owners is through an `OWNERS` file, which can appear in any
folder, at any depth, within the repository. All of these files are then aggregated into a single
`CODEOWNERS` file.
While this is useful for viewing ownership of a folder at a glance, it incurs a massive performance
hit as we'd have to constantly glob the _entire_ repository to find all `OWNERS` files. We found it
best to define owners in `moon.*` instead for the following reasons:
- No performance hit, as we're already loading and parsing these config files.
- Co-locates owners with the rest of moon's configuration.
- Ownership is now a part of the project graph, enabling future features.
---
## Daemon
As workspaces grow in size, the time spent building
[project and task graphs](../how-it-works/project-graph) becomes a noticeable bottleneck. Every
`moon` CLI invocation rebuilds these graphs from scratch, even when nothing has changed since the
last run. The daemon solves this by running a background server process that keeps the workspace
graph hot in memory, watches for file changes, and rebuilds only when necessary. This eliminates
redundant work and significantly improves CLI response times, especially in larger workspaces with
hundreds or thousands of projects/tasks.
:::caution
This feature is currently **unstable** and must be explicitly enabled. Its behavior and
configuration may change in future releases.
:::
## Enabling the daemon
To enable the daemon, set the [`unstable_daemon`](../config/workspace#daemon) setting in
[`.moon/workspace.yml`](../config/workspace).
```yaml title=".moon/workspace.yml"
unstable_daemon: true
```
Alternatively, you can enable it via the `MOON_DAEMON` environment variable without modifying your
configuration. This is useful for trying it out before committing to the change.
```shell
MOON_DAEMON=true moon run :build
```
## How it works
### Background server
When the daemon is enabled, the first `moon` CLI command you run automatically spawns a background
server process. This process detaches from the terminal and continues running after the command
completes. Subsequent CLI invocations connect to the running daemon instead of starting fresh.
The daemon communicates with CLI processes over a local IPC channel — Unix domain sockets on
macOS/Linux, and named pipes on Windows. All daemon files (state, socket, and logs) are stored in
`.moon/cache/daemon`. The daemon records its metadata in a `daemon.json` state file, and takes exclusive ownership of its workspace through an
advisory file lock held for its entire lifetime. A crashed daemon releases the lock automatically,
so a stale socket or state file can no longer block or misdirect the next start.
If the daemon fails to start or is unavailable, the CLI falls back to building graphs in-process as
it normally would without the daemon. You won't see an error — the CLI simply proceeds without the
performance benefit.
Connecting to the daemon and starting it are a single operation — a command that needs
the daemon will start one itself if a background pre-warm hasn't already, with concurrent starts
coordinating so only one daemon is ever spawned. The daemon also retires itself after a long idle
period with no requests, and exits immediately if its workspace is deleted, so an abandoned
workspace no longer leaves a daemon running forever.
### File watching
To keep the in-memory graph up to date, the daemon watches the workspace root recursively for file
system changes. Events are debounced (coalesced within a short window) to avoid redundant rebuilds
during rapid edits.
The following directories are always ignored by the watcher:
- `.git`, `.svn`, `node_modules`
- `.moon/cache`, `.moon/docker`
Changes to the following files and directories trigger an asynchronous graph rebuild:
- [`.prototools`](../proto/config) — reloads toolchain configuration
- `.moon/workspace.*` — reloads workspace settings
- `.moon/toolchains.*` — reloads toolchain plugins
- `.moon/extensions.*` — reloads extension plugins
- `.moon/tasks/**/*` — reloads task inheritance
- `moon.*` config files in project directories — reloads project configuration
- Creation or removal of project directories matching configured
[project sources](../config/workspace#projects)
Because rebuilds happen asynchronously in the background, the daemon remains responsive to new CLI
connections while a rebuild is in progress.
## Managing the daemon
The [`moon daemon`](../commands/daemon) command provides subcommands for managing the daemon's
lifecycle. In most cases you won't need these — the daemon starts automatically and stays out of the
way — but they're useful for debugging or manual control.
### Starting
```shell
$ moon daemon start
```
Starts the daemon if it's not already running. If a daemon is already running for this workspace,
the existing process is reused. You typically don't need to run this manually, as the daemon
auto-starts with any `moon` command when enabled. See
[`moon daemon start`](../commands/daemon/start).
### Stopping
```shell
$ moon daemon stop
```
Stops the daemon gracefully. If the daemon does not shut down within a few seconds, it will be
forcefully killed. Daemon files (state, socket) are cleaned up automatically. See
[`moon daemon stop`](../commands/daemon/stop).
### Restarting
```shell
$ moon daemon restart
```
Stops the running daemon and starts a new one. This is useful after manual configuration changes, or
if the daemon's cached state seems stale. See [`moon daemon restart`](../commands/daemon/restart).
### Checking status
```shell
$ moon daemon status
```
Displays information about the running daemon, including its process ID, uptime, socket/pipe
endpoint, and file paths for the state and log files. Whether the daemon is running is determined by
connecting to it, rather than probing a process ID. See
[`moon daemon status`](../commands/daemon/status).
### Viewing logs
```shell
$ moon daemon logs
```
Tails the daemon's log file in real time. The daemon must be running for this command to work. The
log file is located at `.moon/cache/daemon/server.log` and contains detailed trace-level output. As
of v2.4, log files are rotated up to 7 times, with older files automatically deleted. See
[`moon daemon logs`](../commands/daemon/logs).
## Troubleshooting
### The daemon won't start
- Verify the daemon is enabled: check that `unstable_daemon: true` is set in
[`.moon/workspace.yml`](../config/workspace), or that the `MOON_DAEMON` environment variable is
set.
- Check if a daemon is already running with [`moon daemon status`](../commands/daemon/status).
- As of v2.4, a crashed daemon releases its workspace lock automatically, so a stale state file no
longer blocks startup. If startup still fails, delete `.moon/cache/daemon` and try again.
### The graph seems stale
If tasks or projects appear to be missing or outdated despite config changes, the file watcher may
have missed an event. Run [`moon daemon restart`](../commands/daemon/restart) to force a fresh graph
build.
### Where are the logs?
The daemon writes detailed logs to `.moon/cache/daemon/server.log`. Use
[`moon daemon logs`](../commands/daemon/logs) to tail this file, or open it directly in an editor.
### After upgrading moon
As of v2.4, this is handled automatically — when connecting, the CLI checks the running daemon's
moon and protocol version against its own, and restarts it on a mismatch, so a daemon left over from
before a `moon upgrade` is replaced instead of serving the old binary indefinitely. If you ever need
to force it, restart the daemon manually with
[`moon daemon restart`](../commands/daemon/restart).
### CI environments
The daemon is designed for local development where a persistent background process can be reused
across multiple CLI invocations. In CI environments, where each run starts fresh, the daemon
provides no benefit since there's no long-lived process to connect to. Enabling it in CI won't cause
harm, but it won't improve performance either.
## Limitations
- This feature is **unstable** and may change in future releases. Behavior, configuration, and CLI
commands are subject to change.
- Each workspace runs its own daemon process. If you work across multiple workspaces, each will have
a separate daemon.
- On Windows, the daemon uses named pipes instead of Unix domain sockets. The behavior is
equivalent, but the underlying IPC mechanism differs.
---
## Debugging a task
Running [tasks](../concepts/task) is the most common way to interact with moon, so what do you do
when your task isn't working as expected? Diagnose it of course! Diagnosing the root cause of a
broken task can be quite daunting, but do not fret, as the following steps will help guide you in
this endeavor.
## Using an AI skill
We provide an AI agent skill called `debug-task` that helps you systematically diagnose failing or
misbehaving moon tasks. If you've ever spent time hunting down why a task is producing stale
results, getting unexpected cache misses, or behaving differently in CI, this skill is for you.
To install this skill, run the following command:
```shell
$ npx skills add moonrepo/moon --skill debug-task
```
The skill walks you through a 5-step diagnostic workflow:
1. Inspect the resolved task configuration (after inheritance merging)
2. Run with maximum verbosity to see exactly what moon is doing
3. Inspect cache state and hash manifests
4. Match symptoms to root causes using a built-in decision tree
5. Validate that the fix actually resolves the issue
It covers 15+ specific problem types across cache issues, configuration mistakes, execution
problems, and CI vs local discrepancies. Most issues resolve by step 3, with deeper reference
material available on demand.
To use it, simply describe your task problem in Claude Code, Codex, OpenCode, or another compatible
AI agent — mention the task target, what you expected, and what actually happened. The skill will
take it from there.
```shell
/debug-task Help me debug why the `app:build` task is not being cached
```
## Verify configuration
Before we dive into the internals of moon, we should first verify that the task is actually
configured correctly. Our configuration layer is very strict, but it can't catch everything, so jump
to the [`moon.*`](../config/project#tasks) documentation for more information.
To start, moon will create a snapshot of the project and its tasks, with all [tokens][token]
resolved, and paths expanded. This snapshot is located at
`.moon/cache/states//snapshot.json`. With the snapshot open, inspect the root `tasks`
object for any inconsistencies or inaccuracies.
Some issues to look out for:
- Have `command` and `args` been parsed correctly?
- Have [tokens][token] resolved correctly? If not, verify syntax or try another token type.
- Have `inputFiles`, `inputGlobs`, and `inputVars` expanded correctly from [`inputs`][inputs]?
- Have `outputFiles` and `outputGlobs` expanded correctly from [`outputs`][outputs]?
- Is the `toolchains` correct for the command? If incorrect, explicitly set the
[`toolchain`][toolchain].
- Are `options` and `state` correct?
:::info
Resolved information can also be inspected with the [`moon task --json`](../commands/task)
command.
:::
### Verify inherited configuration
If the configuration from the previous step looks correct, you can skip this step, otherwise let's
verify that the inherited configuration is also correct. In the `snapshot.json` file, inspect the
root `inherited` object, which is structured as follows:
- `config` - A mapping of configuration files that were loaded, in order. Each config represents a
partial object (not expanded or resolved). Only files that exist will be mapped here.
- `layers` - A mapping of task IDs to configuration files that have been inherited.
## Inspect trace logs
If configuration looks good, let's move on to inspecting the trace logs, which can be a non-trivial
amount of effort. Run the task to generate the logs, bypass the cache, and include debug
information:
```shell
MOON_DEBUG_PROCESS_ENV=true MOON_DEBUG_PROCESS_INPUT=true moon run --log trace --force
```
Once ran, a large amount of information will be logged to the terminal. However, most of it can be
ignored, as we're only interested in the "is this task affected by changes" logs. This breaks down
as follows:
1. First, we gather changed files from the local checkout, which is typically
`git status --porcelain --untracked-files` (from the `moon_process` module).
2. Secondly, we gather all files from the project directory, using the
`git ls-files --full-name --cached --modified --others --exclude-standard --deduplicate`
command (also from the `moon_process` module). This command can also be ran locally to verify the
output.
3. Lastly, all files from the previous 2 commands will be hashed using the `git hash-object`
command. If you passed the `MOON_DEBUG_PROCESS_INPUT` environment variable, you'll see a massive
log entry of all files being hashed. This is what we use to generate moon's specific hash.
If all went well, you should see a log entry that looks like this:
```
moon_task_runner::task_runner Generated a unique hash task_target="" hash=""
```
The important piece is the hash, which is a 64-character SHA256 hash, and represents the unique hash
of this task/target. This is what moon uses to determine a cache hit/miss, and whether or not to
skip re-running a task.
Let's copy the hash and move on to the next step.
## Inspect the hash manifest
With the hash in hand, let's dig deeper into moon's internals, by inspecting the hash manifest at
`.moon/cache/hashes/.json`, or running the [`moon hash`](../commands/hash) command:
```shell
moon hash
```
The manifest is JSON and its contents are all the information used to generate its unique hash. This
information is an array, and breaks down as follows:
- The first item in the array is the task itself. The important fields to diagnose here are `deps`
and `inputs`.
- Dependencies are other tasks (and their hash) that this task depends on.
- Inputs are all the files (and their hash from `git hash-object`) this task requires to run.
- The remaining array items are toolchain/language specific, some examples are:
- **Node.js** - The current Node.js version and the resolved versions/hashes of all `package.json`
dependencies.
- **Rust** - The current Rust version and the resolved versions/hashes of all `Cargo.toml`
dependencies.
- **TypeScript** - Compiler options for changing compilation output.
Some issues to look out for:
- Do the dependencies match the task's configured [`deps`][deps] and [`implicitDeps`][implicitDeps]?
- Do the inputs match the task's configured [`inputs`][inputs] and
[`implicitInputs`][implicitInputs]? If not, try tweaking the config.
- Are the toolchain/language specific items correct?
- Are dependency versions/hashes correctly parsed from the appropriate lockfile?
### Diffing a previous hash
Another avenue for diagnosing a task is to diff the hash against a hash from a previous run. Since
we require multiple hashes, we'll need to run the task multiple times,
[inspect the logs](#inspect-trace-logs), and extract the hash for each. If you receive the same hash
for each run, you'll need to tweak configuration or change files to produce a different hash.
Once you have 2 unique hashes, we can pass them to the [`moon hash`](../commands/hash) command. This
will produce a `git diff` styled output, allowing for simple line-by-line comparison debugging.
```shell
moon hash
```
```diff
Left: 0b55b234f1018581c45b00241d7340dc648c63e639fbafdaf85a4cd7e718fdde
Right: 2388552fee5a02062d0ef402bdc7232f0a447458b058c80ce9c3d0d4d7cfe171
[
{
"command": "build",
"args": [
+ "./dist"
- "./build"
],
...
}
]
```
This is extremely useful in diagnoising why a task is running differently than before, and is much
easier than inspecting the hash manifest files manually!
## Ask for help
If you've made it this far, and still can't figure out why a task is not working correctly, please
ask for help!
- [Join the Discord community](https://discord.gg/qCh9MEynv2) (if lost)
- [Report an issue](https://github.com/moonrepo/moon/issues/new/choose) (if an actual bug)
[token]: ../concepts/token
[deps]: ../config/project#deps
[inputs]: ../config/project#inputs
[outputs]: ../config/project#outputs
[toolchain]: ../config/project#toolchain
[implicitDeps]: ../config/tasks#implicitdeps
[implicitInputs]: ../config/tasks#implicitinputs
---
## Docker integration
Using [Docker](https://www.docker.com/) to run your applications? Or build your artifacts? No
worries, moon can be utilized with Docker, and supports a robust integration layer.
:::success
Looking to speed up your Docker builds? Want to build in the cloud?
[Give Depot a try](https://depot.dev?ref=moonrepo)!
:::
## Requirements
The first requirement, which is very important, is adding `.moon/cache` to the workspace root
`.dockerignore` (moon assumes builds are running from the root). Not all files in `.moon/cache` are
portable across machines/environments, so copying these file into Docker will definitely cause
interoperability issues.
```text title=".dockerignore"
.moon/cache
```
The other requirement depends on how you want to integrate Git with Docker. Since moon executes
`git` commands under the hood, there are some special considerations to be aware of when running
moon within Docker. There's 2 scenarios to choose from:
1. (recommended) Add the `.git` folder to `.dockerignore`, so that it's not `COPY`'d. moon will
continue to work just fine, albeit with some functionality disabled, like caching.
2. Ensure that the `git` library is installed in the container, and copy the `.git` folder with
`COPY`. moon will work with full functionality, but it will increase the overall size of the
image because of caching.
## Creating a `Dockerfile`
:::info
Our [`moon docker file`][file] command can automatically generate a `Dockerfile` based on this
guide! We suggest generating the file then reading the guide below to understand what's going on.
:::
We're very familiar with how tedious `Dockerfile`s are to write and maintain, so in an effort to
reduce this headache, we've built a handful of tools to make this process much easier. With moon,
we'll take advantage of Docker's layer caching and staged builds as much as possible.
With that being said, there's many approaches you can utilize, depending on your workflow (we'll
document them below):
- Running `moon docker` commands _before_ running `docker run|build` commands.
- Running `moon docker` commands _within_ the `Dockerfile`.
- Using multi-staged or non-staged (standard) builds.
- Something else unique to your setup!
:::warning
This guide and our Docker approach is merely a suggestion and is not a requirement for using moon
with Docker! Feel free to use this as a starting point, or not at all. Choose the approach that
works best for you!
:::
### What we're trying to avoid
Before we dive into writing a perfect `Dockerfile`, we'll briefly talk about the pain points we're
trying to avoid. In the context of Node.js and monorepo's, you may be familiar with having to `COPY`
each individual `package.json` in the monorepo before installing `node_modules`, to effectively use
layer caching. This is very brittle, as each new application or package is created, every
`Dockerfile` in the monorepo will need to be modified to account for this new `package.json`.
Furthermore, we'll have to follow a similar process for _only copying source files_ necessary for
the build or `CMD` to complete. This is _very tedious_, so most developers simply use `COPY . .` and
forget about it. Copying the entire monorepo is costly, especially as it grows.
As an example, we'll use moon's official repository. The `Dockerfile` would look something like the
following.
```docker
FROM node:latest
WORKDIR /app
# Install moon binary
RUN npm install -g @moonrepo/cli
# Copy moon files
COPY ./.moon ./.moon
# Copy all package.json's and lockfiles
COPY ./packages/cli/package.json ./packages/cli/package.json
COPY ./packages/core-linux-arm64-gnu/package.json ./packages/core-linux-arm64-gnu/package.json
COPY ./packages/core-linux-arm64-musl/package.json ./packages/core-linux-arm64-musl/package.json
COPY ./packages/core-linux-x64-gnu/package.json ./packages/core-linux-x64-gnu/package.json
COPY ./packages/core-linux-x64-musl/package.json ./packages/core-linux-x64-musl/package.json
COPY ./packages/core-macos-arm64/package.json ./packages/core-macos-arm64/package.json
COPY ./packages/core-macos-x64/package.json ./packages/core-macos-x64/package.json
COPY ./packages/core-windows-x64-msvc/package.json ./packages/core-windows-x64-msvc/package.json
COPY ./packages/runtime/package.json ./packages/runtime/package.json
COPY ./packages/types/package.json ./packages/types/package.json
COPY ./package.json ./package.json
COPY ./yarn.lock ./yarn.lock
COPY ./.yarn ./.yarn
COPY ./.yarnrc.yml ./yarnrc.yml
# Install toolchain and dependencies
# In non-moon repos: yarn install
RUN moon docker setup
# Copy project and required files
# Or COPY . .
COPY ./packages/types ./packages/types
COPY ./packages/runtime ./packages/runtime
# Build the target
RUN moon run runtime:build
```
For such a small monorepo, this already looks too confusing!!! Let's remedy this by utilizing moon
itself to the fullest!
### Scaffolding the bare minimum
The first step in this process is to only copy the bare minimum of files necessary for installing
dependencies (Node.js modules, etc). This is typically manifests (`package.json`), lockfiles
(`yarn.lock`, etc), and any configuration (`.yarnrc.yml`, etc).
This can all be achieved with the [`moon docker scaffold`][scaffold] command, which scaffolds a
skeleton of the repository structure, with only necessary files (the above). Let's update our
`Dockerfile` usage.
This assumes [`moon docker scaffold `][scaffold] is ran outside of the `Dockerfile`.
```docker
FROM node:latest
WORKDIR /app
# Install moon binary
RUN npm install -g @moonrepo/cli
# Copy workspace skeleton
COPY ./.moon/docker/workspace .
# Install toolchain and dependencies
RUN moon docker setup
```
```docker
#### BASE
FROM node:latest AS base
WORKDIR /app
# Install moon binary
RUN npm install -g @moonrepo/cli
#### SKELETON
FROM base AS skeleton
# Copy entire repository and scaffold
COPY . .
RUN moon docker scaffold
#### BUILD
FROM base AS build
# Copy workspace skeleton
COPY --from=skeleton /app/.moon/docker/workspace .
# Install toolchain and dependencies
RUN moon docker setup
```
And with this, our dependencies will be layer cached effectively! Let's now move onto copying source
files.
### Copying necessary source files
The next step is to copy all source files necessary for `CMD` or any `RUN` commands to execute
correctly. This typically requires copying all source files for the project _and_ all source files
of the project's dependencies... NOT the entire repository!
Luckily our [`moon docker scaffold `][scaffold] command has already done this for us! Let's
continue updating our `Dockerfile` to account for this, by appending the following:
```docker
# Copy source files
COPY ./.moon/docker/sources .
# Build something (optional)
RUN moon run :
```
```docker
# Copy source files
COPY --from=skeleton /app/.moon/docker/sources .
# Build something (optional)
RUN moon run :
```
:::info
If you need to copy additional files for your commands to run successfully, you can configure the
`docker.scaffold` settings in [`.moon/workspace.yaml`](../config/workspace#scaffold) (entire
workspace) or [`moon.*`](../config/project#scaffold) (per project).
:::
### Pruning extraneous files
Now that we've ran a command or built an artifact, we should prune the Docker environment to remove
unneeded files and folders. We can do this with the [`moon docker prune`][prune] command, which
_must be ran_ within the context of a `Dockerfile`!
```docker
# Prune workspace
RUN moon docker prune
```
When ran, this command will do the following, in order:
- Remove extraneous dependencies (`node_modules`) for unfocused projects.
- Install production only dependencies for the projects that were scaffolded.
:::info
This process can be customized using the `docker.prune` setting in
[`.moon/workspace.yaml`](../config/workspace#prune).
:::
### Final result
And with this moon integration, we've reduced the original `Dockerfile` of 35 lines to 18 lines, a
reduction of almost 50%. The original file can also be seen as `O(n)`, as each new manifest requires
cascading updates, while the moon approach is `O(1)`!
```docker
FROM node:latest
WORKDIR /app
# Install moon binary
RUN npm install -g @moonrepo/cli
# Copy workspace skeleton
COPY ./.moon/docker/workspace .
# Install toolchain and dependencies
RUN moon docker setup
# Copy source files
COPY ./.moon/docker/sources .
# Build something (optional)
RUN moon run :
# Prune workspace
RUN moon docker prune
# CMD
```
```docker
#### BASE
FROM node:latest AS base
WORKDIR /app
# Install moon binary
RUN npm install -g @moonrepo/cli
#### SKELETON
FROM base AS skeleton
# Copy entire repository and scaffold
COPY . .
RUN moon docker scaffold
#### BUILD
FROM base AS build
# Copy workspace skeleton
COPY --from=skeleton /app/.moon/docker/workspace .
# Install toolchain and dependencies
RUN moon docker setup
# Copy source files
COPY --from=skeleton /app/.moon/docker/sources .
# Build something (optional)
RUN moon run :
# Prune workspace
RUN moon docker prune
# CMD
```
## Running `docker` commands
When running `docker` commands, they _must_ be ran from moon's workspace root (typically the
repository root) so that the project graph and all `moon docker` commands resolve correctly.
```shell
docker build .
```
If you're `Dockerfile`s are located within each applicable project, use the `-f` argument.
```shell
docker run -f ./apps/client/Dockerfile .
```
## Troubleshooting
### Supporting `node:alpine` images
If you're trying to use the `node:alpine` image with moon's
[integrated toolchain](../concepts/toolchain), you'll need to set the `MOON_TOOLCHAIN_FORCE_GLOBALS`
environment variable in the Docker image to disable moon's toolchain. This is required as Node.js
does not provide pre-built binaries for the Alpine target, so installing the Node.js toolchain will
fail.
```docker
FROM node:alpine
ENV MOON_TOOLCHAIN_FORCE_GLOBALS=true
```
[file]: ../commands/docker/file
[prune]: ../commands/docker/prune
[scaffold]: ../commands/docker/scaffold
---
## Angular example
In this guide, you'll learn how to integrate [Angular](https://angular.io/) into moon.
Begin by creating a new Angular project in the root of an existing moon project (this should not be
created in the workspace root, unless a polyrepo).
```shell
cd apps && npx -p @angular/cli@latest ng new angular-app
```
> View the [official Angular docs](https://angular.io/start) for a more in-depth guide to getting
> started!
## Setup
Since Angular is per-project, the associated moon tasks should be defined in each project's
[`moon.*`](../../config/project) file.
```yaml title="/moon.yml"
fileGroups:
app:
- 'src/**/*'
- 'angular.*'
tasks:
dev:
command: 'ng serve'
preset: 'server'
build:
command: 'ng build'
inputs:
- '@group(app)'
- '@group(sources)'
outputs:
- 'dist'
# Extends the top-level lint
lint:
args:
- '--ext'
- '.ts'
```
### ESLint integration
Angular does not provide a built-in linting abstraction, but instead there is an
[ESLint package](https://github.com/angular-eslint/angular-eslint), which is great, but complicates
things a bit. Because of this, you have two options for moving forward:
- Use a [global `lint` task](./eslint) and bypass Angular's solution (preferred).
- Use Angular's ESLint package solution only.
Regardless of which option is chosen, the following changes are applicable to all options and should
be made. Begin be installing the dependencies that the
[`@angular-eslint`](https://nextjs.org/docs/basic-features/eslint#eslint-config) package need in the
application's `package.json`.
Since Angular has some specific rules, we'll need to tell the ESLint package to overrides the
default ones. This can be achieved with a project-level `.eslintrc.json` file.
```json title="/.eslintrc.json"
{
"root": true,
"ignorePatterns": ["projects/**/*"],
"overrides": [
{
"files": ["*.ts"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@angular-eslint/recommended",
// This is required if you use inline templates in Components
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
/**
* Any TypeScript source code (NOT TEMPLATE) related rules you wish to use/reconfigure over and above the
* recommended set provided by the @angular-eslint project would go here.
*/
"@angular-eslint/directive-selector": [
"error",
{ "type": "attribute", "prefix": "app", "style": "camelCase" }
],
"@angular-eslint/component-selector": [
"error",
{ "type": "element", "prefix": "app", "style": "kebab-case" }
]
}
},
{
"files": ["*.html"],
"extends": [
"plugin:@angular-eslint/template/recommended",
"plugin:@angular-eslint/template/accessibility"
],
"rules": {
/**
* Any template/HTML related rules you wish to use/reconfigure over and above the
* recommended set provided by the @angular-eslint project would go here.
*/
}
}
]
}
```
With the basics now setup, choose the option that works best for you.
We encourage using the global `lint` task for consistency across all projects within the repository.
With this approach, the `eslint` command itself will be ran and the `ng lint` command will be
ignored, but the `@angular-eslint` rules will still be used.
If you'd prefer to use the `ng lint` command, add it as a task to the project's
[`moon.*`](../../config/project).
```yaml title="/moon.yml"
tasks:
lint:
command: 'ng lint'
inputs:
- '@group(angular)'
```
Furthermore, if a global `lint` task exists, be sure to exclude it from being inherited.
```yaml title="/moon.yml"
workspace:
inheritedTasks:
exclude: ['lint']
```
In addition to configuring `moon.*`, you also need to add a lint target in the `angular.json` file
for linting to work properly. The lint target specifies which builder to use for linting, as well as
the file patterns that should be linted.
```json title="/angular.json"
{
"projects": {
"angular-app": {
"architect": {
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
}
}
}
}
}
}
```
Adding this lint target is crucial for ensuring that the linting process is properly configured and
integrated with Angular's build system.
### TypeScript integration
Angular has [built-in support for TypeScript](https://angular.io/guide/typescript-configuration), so
there is no need for additional configuration to enable TypeScript support.
At this point we'll assume that a `tsconfig.json` has been created in the application, and
typechecking works. From here we suggest utilizing a [global `typecheck` task](./typescript) for
consistency across all projects within the repository.
## Configuration
### Root-level
We suggest _against_ root-level configuration, as Angular should be installed per-project, and the
`ng` command expects the configuration to live relative to the project root.
### Project-level
When creating a new Angular project, a [`angular.json`](https://angular.io/guide/workspace-config)
is created, and _must_ exist in the project root. This allows each project to configure Angular for
their needs.
```json title="/angular.json"
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"projects": {
"angular-app": {
"projectType": "application",
...
}
},
...
}
```
---
## Astro example
In this guide, you'll learn how to integrate [Astro](https://docs.astro.build).
Begin by creating a new Astro project in the root of an existing moon project (this should not be
created in the workspace root, unless a polyrepo).
```shell
cd apps && npm create astro@latest
```
## Setup
Since Astro is per-project, the associated moon tasks should be defined in each project's
[`moon.*`](../../config/project) file.
:::tip
We suggest inheriting Astro tasks from the
[official moon configuration preset](https://github.com/moonrepo/moon-configs/tree/master/javascript/astro).
:::
```yaml title="/moon.yml"
# Inherit tasks from the `astro` preset
# https://github.com/moonrepo/moon-configs
tags: ['astro']
# Disable project references
toolchains:
typescript:
syncProjectReferences: false
```
### ESLint integration
When using a [`lint`](./eslint) task, the
[`eslint-plugin-astro`](https://ota-meshi.github.io/eslint-plugin-astro/user-guide/) package must be
installed to lint `.astro` files.
Once the dependency has been installed in the application's `package.json`. We can then enable this
configuration by creating an `.eslintrc.js` file in the project root. Be sure this file is listed in
your lint task's inputs!
```js title="/.eslintrc.js"
module.exports = {
extends: ['plugin:astro/recommended'],
overrides: [
{
files: ['*.astro'],
parser: 'astro-eslint-parser',
// If using TypeScript
parserOptions: {
parser: '@typescript-eslint/parser',
extraFileExtensions: ['.astro'],
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
},
},
],
};
```
And lastly, when linting through moon's command line, you'll need to include the `.astro` extension
within the `lint` task. This can be done by extending the top-level task within the project (below),
or by adding it to the top-level entirely.
```yaml title="/moon.yml"
tasks:
lint:
args:
- '--ext'
- '.ts,.tsx,.astro'
```
### Prettier integration
When using a [`format`](./prettier) task, the `prettier-plugin-astro` package must be installed to
format `.astro` files. View the official
[Astro docs](https://docs.astro.build/en/editor-setup/#prettier) for more information.
### TypeScript integration
Since Astro utilizes custom `.astro` files, it requires a specialized TypeScript integration, and
luckily Astro provides an [in-depth guide](https://docs.astro.build/en/guides/typescript/). With
that being said, we do have a few requirements and pointers!
- Use the official [Astro `tsconfig.json`](https://docs.astro.build/en/guides/typescript/#setup) as
a basis.
- From our internal testing, the `astro check` command (that typechecks `.astro` files) _does not
support project references_. If the `composite` compiler option is enabled, the checker will fail
to find `.astro` files. To work around this, we disable `workspace.typescript` in our moon config
above.
- Since typechecking requires 2 commands, one for `.astro` files, and the other for `.ts`, `.tsx`
files, we've added the [`typecheck`](./typescript) task as a dependency for the `check` task. This
will run both commands through a single task!
## Configuration
### Root-level
We suggest _against_ root-level configuration, as Astro should be installed per-project, and the
`astro` command expects the configuration to live relative to the project root.
### Project-level
When creating a new Astro project, a
[`astro.config.mjs`](https://docs.astro.build/en/reference/configuration-reference/) is created, and
_must_ exist in the project root. This allows each project to configure Astro for their needs.
```js title="/astro.config.mjs"
// https://astro.build/config
export default defineConfig({});
```
---
## ESLint example
In this guide, you'll learn how to integrate [ESLint](https://eslint.org/) into moon.
Begin by installing `eslint` and any plugins in your root. We suggest using the same version across
the entire repository.
## Setup
Since linting is a universal workflow, add a `lint` task to [`.moon/tasks/**/*`](../../config/tasks)
with the following parameters.
```yaml title=".moon/tasks/eslint.yml"
tasks:
lint:
command:
- 'eslint'
# Support other extensions
- '--ext'
- '.js,.jsx,.ts,.tsx'
# Always fix and run extra checks
- '--fix'
- '--report-unused-disable-directives'
# Dont fail if a project has nothing to lint
- '--no-error-on-unmatched-pattern'
# Do fail if we encounter a fatal error
- '--exit-on-fatal-error'
# Only 1 ignore file is supported, so use the root
- '--ignore-path'
- '@in(4)'
# Run in current dir
- '.'
inputs:
# Source and test files
- 'src/**/*'
- 'tests/**/*'
# Other config files
- '*.config.*'
# Project configs, any format, any depth
- '**/.eslintrc.*'
# Root configs, any format
- '/.eslintignore'
- '/.eslintrc.*'
```
Projects can extend this task and provide additional parameters if need be, for example.
```yaml title="/moon.yml"
tasks:
lint:
args:
# Enable caching for this project
- '--cache'
```
### TypeScript integration
If you're using the [`@typescript-eslint`](https://typescript-eslint.io) packages, and want to
enable type-safety based lint rules, we suggest something similar to the official
[monorepo configuration](https://typescript-eslint.io/docs/linting/monorepo).
Create a `tsconfig.eslint.json` in your repository root, extend your shared compiler options (we use
[`tsconfig.options.json`](./typescript)), and include all your project files.
```json title="tsconfig.eslint.json"
{
"extends": "./tsconfig.options.json",
"compilerOptions": {
"emitDeclarationOnly": false,
"noEmit": true
},
"include": ["apps/**/*", "packages/**/*"]
}
```
Append the following inputs to your `lint` task.
```yaml title=".moon/tasks/node.yml"
tasks:
lint:
# ...
inputs:
# TypeScript support
- 'types/**/*'
- 'tsconfig.json'
- '/tsconfig.eslint.json'
- '/tsconfig.options.json'
```
And lastly, add `parserOptions` to your [root-level config](#root-level).
## Configuration
### Root-level
The root-level ESLint config is _required_, as ESLint traverses upwards from each file to find
configurations, and this denotes the stopping point. It's also used to define rules for the _entire_
repository.
```js title=".eslintrc.js"
module.exports = {
root: true, // Required!
extends: ['moon'],
rules: {
'no-console': 'error',
},
// TypeScript support
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.eslint.json',
tsconfigRootDir: __dirname,
},
};
```
The `.eslintignore` file must also be defined at the root, as
[only 1 ignore file](https://eslint.org/docs/user-guide/configuring/ignoring-code#the-eslintignore-file)
can exist in a repository. We ensure this ignore file is used by passing `--ignore-path` above.
```bash title=".eslintignore"
node_modules/
*.min.js
*.map
*.snap
```
### Project-level
A project-level ESLint config can be utilized by creating a `.eslintrc.` in the
project root. This is optional, but necessary when defining rules and ignore patterns unique to the
project.
```js title="/.eslintrc.js"
module.exports = {
// Patterns to ignore (alongside the root .eslintignore)
ignorePatterns: ['build', 'lib'],
// Project specific rules
rules: {
'no-console': 'off',
},
};
```
> The
> [`extends`](https://eslint.org/docs/user-guide/configuring/configuration-files#extending-configuration-files)
> setting should **not** extend the root-level config, as ESLint will automatically merge configs
> while traversing upwards!
### Sharing
To share configuration across projects, you have 3 options:
- Define settings in the [root-level config](#root-level). This only applies to the parent
repository.
- Create and publish an
[`eslint-config`](https://eslint.org/docs/developer-guide/shareable-configs#using-a-shareable-config)
or [`eslint-plugin`](https://eslint.org/docs/developer-guide/working-with-plugins) npm package.
This can be used in any repository.
- A combination of 1 and 2.
For options 2 and 3, if you're utilizing package workspaces, create a local package with the
following content.
```js title="packages/eslint-config-company/index.js"
module.exports = {
extends: ['airbnb'],
};
```
Within your root-level ESLint config, you can extend this package to inherit the settings.
```js title=".eslintrc.js"
module.exports = {
extends: 'eslint-config-company',
};
```
> When using this approach, the package must be built and symlinked into `node_modules` _before_ the
> linter will run correctly. Take this into account when going down this path!
## FAQ
### How to lint a single file or folder?
Unfortunately, this isn't currently possible, as the `eslint` binary itself requires a file or
folder path to operate on, and in the task above we pass `.` (current directory). If this was not
passed, then nothing would be linted.
This has the unintended side-effect of not being able to filter down lintable targets by passing
arbitrary file paths. This is something we hope to resolve in the future.
To work around this limitation, you can create another lint task.
### Should we use `overrides`?
Projects should define their own rules using an ESLint config in their project root. However, if you
want to avoid touching many ESLint configs (think migrations), then
[overrides in the root](https://eslint.org/docs/user-guide/configuring/configuration-files#configuration-based-on-glob-patterns)
are a viable option. Otherwise, we highly encourage project-level configs.
```js title=".eslintrc.js"
module.exports = {
// ...
overrides: [
// Only apply to apps "foo" and "bar", but not others
{
files: ['apps/foo/**/*', 'apps/bar/**/*'],
rules: {
'no-magic-numbers': 'off',
},
},
],
};
```
---
## Jest example
In this guide, you'll learn how to integrate [Jest](https://jestjs.io/) into moon.
Begin by installing `jest` in your root. We suggest using the same version across the entire
repository.
## Setup
Since testing is a universal workflow, add a `test` task to [`.moon/tasks/**/*`](../../config/tasks)
with the following parameters.
```yaml title=".moon/tasks/jest.yml"
tasks:
test:
command:
- 'jest'
# Always run code coverage
- '--coverage'
# Dont fail if a project has no tests
- '--passWithNoTests'
inputs:
# Source and test files
- 'src/**/*'
- 'tests/**/*'
# Project configs, any format
- 'jest.config.*'
```
Projects can extend this task and provide additional parameters if need be, for example.
```yaml title="/moon.yml"
tasks:
test:
args:
# Disable caching for this project
- '--no-cache'
```
## Configuration
### Root-level
A root-level Jest config is not required and should be avoided, instead, use a [preset](#sharing) to
share configuration.
### Project-level
A project-level Jest config can be utilized by creating a `jest.config.` in the
project root. This is optional, but necessary when defining project specific settings.
```js title="/jest.config.js"
module.exports = {
// Project specific settings
testEnvironment: 'node',
};
```
### Sharing
To share configuration across projects, you can utilize Jest's built-in
[`preset`](https://jestjs.io/docs/configuration#preset-string) functionality. If you're utilizing
package workspaces, create a local package with the following content, otherwise publish the npm
package for consumption.
```js title="packages/company-jest-preset/jest-preset.js"
module.exports = {
testEnvironment: 'jsdom',
watchman: true,
};
```
Within your project-level Jest config, you can extend the preset to inherit the settings.
```js title="/jest.config.js"
module.exports = {
preset: 'company-jest-preset',
};
```
> You can take this a step further by passing the `--preset` option in the [task above](#setup), so
> that all projects inherit the preset by default.
## FAQ
### How to test a single file or folder?
You can filter tests by passing a file name, folder name, glob, or regex pattern after `--`. Any
passed files are relative from the project's root, regardless of where the `moon` command is being
ran.
```shell
$ moon run :test -- filename
```
### How to use `projects`?
With moon, there's no reason to use
[`projects`](https://jestjs.io/docs/configuration#projects-arraystring--projectconfig) as the `test`
task is ran _per_ project. If you'd like to test multiple projects, use
[`moon run :test`](../../commands/run).
---
## Nest example
In this guide, you'll learn how to integrate [NestJS](https://nestjs.com/) into moon.
Begin by creating a new NestJS project in the root of an existing moon project (this should not be
created in the workspace root, unless a polyrepo).
```shell
npx @nestjs/cli@latest new nestjs-app --skip-git
```
> View the [official NestJS docs](https://docs.nestjs.com/first-steps) for a more in-depth guide to
> getting started!
## Setup
Since NestJS is per-project, the associated moon tasks should be defined in each project's
[`moon.*`](../../config/project) file.
```yaml title="/moon.yml"
layer: 'application'
fileGroups:
app:
- 'nest-cli.*'
tasks:
dev:
command: 'nest start --watch'
preset: 'server'
build:
command: 'nest build'
inputs:
- '@group(app)'
- '@group(sources)'
```
### TypeScript integration
NestJS has [built-in support for TypeScript](https://NestJS.io/guide/typescript-configuration), so
there is no need for additional configuration to enable TypeScript support.
At this point we'll assume that a `tsconfig.json` has been created in the application, and
typechecking works. From here we suggest utilizing a [global `typecheck` task](./typescript) for
consistency across all projects within the repository.
## Configuration
### Root-level
We suggest _against_ root-level configuration, as NestJS should be installed per-project, and the
`nest` command expects the configuration to live relative to the project root.
### Project-level
When creating a new NestJS project, a [`nest-cli.json`](https://docs.nestjs.com/cli/monorepo) is
created, and _must_ exist in the project root. This allows each project to configure NestJS for
their needs.
```json title="/nest-cli.json"
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"type": "application",
"root": "./",
"sourceRoot": "src",
"compilerOptions": {
"tsConfigPath": "tsconfig.build.json"
}
}
```
---
## Next example
In this guide, you'll learn how to integrate [Next.js](https://nextjs.org) into moon.
Begin by creating a new Next.js project at a specified folder path (this should not be created in
the workspace root, unless a polyrepo).
```shell
cd apps && npx create-next-app --typescript
```
> View the [official Next.js docs](https://nextjs.org/learn/basics/create-nextjs-app/setup) for a
> more in-depth guide to getting started!
## Setup
Since Next.js is per-project, the associated moon tasks should be defined in each project's
[`moon.*`](../../config/project) file.
:::tip
We suggest inheriting Next.js tasks from the
[official moon configuration preset](https://github.com/moonrepo/moon-configs/tree/master/javascript/next).
:::
```yaml title="/moon.yml"
# Inherit tasks from the `next` preset
# https://github.com/moonrepo/moon-configs
tags: ['next']
```
### ESLint integration
Next.js has [built-in support for ESLint](https://nextjs.org/docs/basic-features/eslint), which is
great, but complicates things a bit. Because of this, you have two options for moving forward:
- Use a [global `lint` task](./eslint) and bypass Next.js's solution (preferred).
- Use Next.js's solution only.
Regardless of which option is chosen, the following changes are applicable to all options and should
be made. Begin be installing the
[`eslint-config-next`](https://nextjs.org/docs/basic-features/eslint#eslint-config) dependency in
the application's `package.json`.
Since the Next.js app is located within a subfolder, we'll need to tell the ESLint plugin where to
locate it. This can be achieved with a project-level `.eslintrc.js` file.
```js title="/.eslintrc.js"
module.exports = {
extends: 'next', // or 'next/core-web-vitals'
settings: {
next: {
rootDir: __dirname,
},
},
};
```
With the basics now setup, choose the option that works best for you.
We encourage using the global `lint` task for consistency across all projects within the repository.
With this approach, the `eslint` command itself will be ran and the `next lint` command will be
ignored, but the `eslint-config-next` rules will still be used.
Additionally, we suggest disabling the linter during the build process, but is not a requirement. As
a potential alternative, add the `lint` task as a dependency for the `build` task.
```js title="/next.config.js"
module.exports = {
eslint: {
ignoreDuringBuilds: true,
},
};
```
If you'd prefer to use the `next lint` command, add it as a task to the project's
[`moon.*`](../../config/project).
```yaml title="/moon.yml"
tasks:
lint:
command: 'next lint'
inputs:
- '@group(next)'
```
Furthermore, if a global `lint` task exists, be sure to exclude it from being inherited.
```yaml title="/moon.yml"
workspace:
inheritedTasks:
exclude: ['lint']
```
### TypeScript integration
Next.js also has
[built-in support for TypeScript](https://nextjs.org/docs/basic-features/typescript), but has
similar caveats to the [ESLint integration](#eslint-integration). TypeScript itself is a bit
involved, so we suggest reading the official Next.js documentation before continuing.
At this point we'll assume that a `tsconfig.json` has been created in the application, and
typechecking works. From here we suggest utilizing a [global `typecheck` task](./typescript) for
consistency across all projects within the repository.
Additionally, we suggest disabling the typechecker during the build process, but is not a
requirement. As a potential alternative, add the `typecheck` task as a dependency for the `build`
task.
```js title="/next.config.js"
module.exports = {
typescript: {
ignoreBuildErrors: true,
},
};
```
## Configuration
### Root-level
We suggest _against_ root-level configuration, as Next.js should be installed per-project, and the
`next` command expects the configuration to live relative to the project root.
### Project-level
When creating a new Next.js project, a
[`next.config.`](https://nextjs.org/docs/api-reference/next.config.js/introduction) is
created, and _must_ exist in the project root. This allows each project to configure Next.js for
their needs.
```js title="/next.config.js"
module.exports = {
compress: true,
};
```
---
## Nuxt example
In this guide, you'll learn how to integrate [Nuxt v3](https://nuxt.com), a [Vue](./vue) framework,
into moon.
Begin by creating a new Nuxt project at a specified folder path (this should not be created in the
workspace root, unless a polyrepo).
```shell
cd apps && npx nuxi init
```
> View the [official Nuxt docs](https://nuxt.com/docs/getting-started/installation) for a more
> in-depth guide to getting started!
## Setup
Since Nuxt is per-project, the associated moon tasks should be defined in each project's
[`moon.*`](../../config/project) file.
```yaml title="/moon.yml"
fileGroups:
nuxt:
- 'assets/**/*'
- 'components/**/*'
- 'composables/**/*'
- 'content/**/*'
- 'layouts/**/*'
- 'middleware/**/*'
- 'pages/**/*'
- 'plugins/**/*'
- 'public/**/*'
- 'server/**/*'
- 'utils/**/*'
- '.nuxtignore'
- 'app.config.*'
- 'app.vue'
- 'nuxt.config.*'
tasks:
nuxt:
command: 'nuxt'
preset: 'server'
# Production build
build:
command: 'nuxt build'
inputs:
- '@group(nuxt)'
outputs:
- '.nuxt'
- '.output'
# Development server
dev:
command: 'nuxt dev'
preset: 'server'
# Preview production build locally
preview:
command: 'nuxt preview'
deps:
- '~:build'
preset: 'server'
```
Be sure to keep the `postinstall` script in your project's `package.json`.
```json title="/package.json"
{
// ...
"scripts": {
"postinstall": "nuxt prepare"
}
}
```
### ESLint integration
Refer to our [Vue documentation](./vue#eslint-integration) for more information on linting.
### TypeScript integration
Nuxt requires `vue-tsc` for typechecking, so refer to our
[Vue documentation](./vue#typescript-integration) for more information.
## Configuration
### Root-level
We suggest _against_ root-level configuration, as Nuxt should be installed per-project, and the
`nuxt` command expects the configuration to live relative to the project root.
### Project-level
When creating a new Nuxt project, a
[`nuxt.config.ts`](https://v3.nuxtjs.org/api/configuration/nuxt-config) is created, and _must_ exist
in the project root. This allows each project to configure Next.js for their needs.
```js title="/nuxt.config.ts"
export default defineNuxtConfig({});
```
## Testing
Nuxt supports testing through [Jest](https://jestjs.io/) or [Vitest](https://vitest.dev/). Refer to
our [Jest documentation](./jest) or [Vitest documentation](./vite) for more information on testing.
---
## Packemon example
In this guide, you'll learn how to integrate [Packemon](https://packemon.dev/) into moon. Packemon
is a tool for properly building npm packages for distribution, it does this by providing the
following functionality:
- Compiles source code to popular formats: CJS, MJS, ESM, UMD, etc.
- Validates the `package.json` for incorrect fields or values.
- Generates `exports` mappings for `package.json` based on the define configuration.
- And many more [optimizations and features](https://packemon.dev/docs/features)!
Begin by installing `packemon` in your root. We suggest using the same version across the entire
repository.
## Setup
Since Packemon is per-project, the associated moon tasks should be defined in each project's
[`moon.*`](../../config/project) file.
:::tip
We suggest inheriting Packemon tasks from the
[official moon configuration preset](https://github.com/moonrepo/moon-configs/tree/master/javascript/packemon).
:::
```yaml title="/moon.yml"
# Inherit tasks from the `packemon` preset
# https://github.com/moonrepo/moon-configs
tags: ['packemon']
# Set the output formats
tasks:
build:
outputs:
- 'cjs'
```
### TypeScript integration
Packemon has built-in support for TypeScript, but to _not_ conflict with a
[typecheck task](./typescript), a separate `tsconfig.json` file is required, which is named
`tsconfig..json`.
This config is necessary to _only_ compile source files, and to not include unwanted files in the
declaration output directory.
```json title="tsconfig.esm.json"
{
"extends": "../../tsconfig.options.json",
"compilerOptions": {
"outDir": "esm",
"rootDir": "src"
},
"include": ["src/**/*"],
"references": []
}
```
### Build targets
To configure the target platform(s) and format(s), you must define a
[`packemon` block](https://packemon.dev/docs/config) in the project's `package.json`. The chosen
formats must also be listed as `outputs` in the task.
```json title="package.json"
{
"name": "package",
// ...
"packemon": {
"format": "esm",
"platform": "browser"
}
}
```
---
## Prettier example
In this guide, you'll learn how to integrate [Prettier](https://prettier.io/) into moon.
Begin by installing `prettier` in your root. We suggest using the same version across the entire
repository.
## Setup
Since code formatting is a universal workflow, add a `format` task to
[`.moon/tasks/**/*`](../../config/tasks) with the following parameters.
```yaml title=".moon/tasks/prettier.yml"
tasks:
format:
command:
- 'prettier'
# Use the same config for the entire repo
- '--config'
- '@in(4)'
# Use the same ignore patterns as well
- '--ignore-path'
- '@in(3)'
# Fail for unformatted code
- '--check'
# Run in current dir
- '.'
inputs:
# Source and test files
- 'src/**/*'
- 'tests/**/*'
# Config and other files
- '**/*.{md,mdx,yml,yaml,json}'
# Root configs, any format
- '/.prettierignore'
- '/.prettierrc.*'
```
## Configuration
### Root-level
The root-level Prettier config is _required_, as it defines conventions and standards to apply to
the entire repository.
```js title=".prettierrc.js"
module.exports = {
arrowParens: 'always',
semi: true,
singleQuote: true,
tabWidth: 2,
trailingComma: 'all',
useTabs: true,
};
```
The `.prettierignore` file must also be defined at the root, as
[only 1 ignore file](https://prettier.io/docs/en/ignore.html#ignoring-files-prettierignore) can
exist in a repository. We ensure this ignore file is used by passing `--ignore-path` above.
```bash title=".prettierignore"
node_modules/
*.min.js
*.map
*.snap
```
### Project-level
We suggest _against_ project-level configurations, as the entire repository should be formatted
using the same standards. However, if you're migrating code and need an escape hatch,
[overrides in the root](https://prettier.io/docs/en/configuration.html#configuration-overrides) will
work.
## FAQ
### How to use `--write`?
Unfortunately, this isn't currently possible, as the `prettier` binary itself requires either the
`--check` or `--write` options, and since we're configuring `--check` in the task above, that takes
precedence. This is also the preferred pattern as checks will run (and fail) in CI.
To work around this limitation, we suggest the following alternatives:
- Configure your editor to run Prettier on save.
- Define another task to write the formatted code, like `format-write`.
---
## React example
React is an application or library concern, and not a build system one, since the bundling of React
is abstracted away through another tool like webpack. Because of this, moon has no guidelines around
utilizing React directly. You can use React however you wish!
However, with that being said, we do suggest the following:
- Add `react` and related dependencies to each project, not the root. This includes `@types/react`
as well. This will ensure accurate [hashing](../../concepts/cache#hashing).
- Configure Babel with the `@babel/preset-react` preset.
- Configure [TypeScript](./typescript) compiler options with `"jsx": "react-jsx"`.
---
## Remix example
In this guide, you'll learn how to integrate [Remix](https://remix.run) into moon.
Begin by creating a new Remix project at a specified folder path (this should not be created in the
workspace root, unless a polyrepo).
```shell
cd apps && npx create-remix
```
During this installation, Remix will ask a handful of questions, but be sure to answer "No" for the
"Do you want me to run `npm install`?" question. We suggest installing dependencies at the workspace
root via package workspaces!
> View the [official Remix docs](https://remix.run/docs/en/v1) for a more in-depth guide to getting
> started!
## Setup
Since Remix is per-project, the associated moon tasks should be defined in each project's
[`moon.*`](../../config/project) file.
:::tip
We suggest inheriting Remix tasks from the
[official moon configuration preset](https://github.com/moonrepo/moon-configs/tree/master/javascript/remix).
:::
```yaml title="/moon.yml"
# Inherit tasks from the `remix` preset
# https://github.com/moonrepo/moon-configs
tags: ['remix']
```
### ESLint integration
Remix does not provide a built-in linting abstraction, and instead provides a simple ESLint
configuration package,
[`@remix-run/eslint-config`](https://www.npmjs.com/package/@remix-run/eslint-config). For the rest
of this section, we're going to assume that a [global `lint` task](./eslint) has been configured.
Begin be installing the `@remix-run/eslint-config` dependency in the application's `package.json`.
We can then enable this configuration by creating an `.eslintrc.js` file in the project root. Be
sure this file is listed in your `lint` task's inputs!
```js title="/.eslintrc.js"
module.exports = {
extends: ['@remix-run/eslint-config', '@remix-run/eslint-config/node'],
// If using TypeScript
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
},
};
```
### TypeScript integration
Remix ships with TypeScript support (when enabled during installation), but the `tsconfig.json` it
generates is _not_ setup for TypeScript project references, which we suggest using with a
[global `typecheck` task](./typescript).
When using project references, we suggest the following `tsconfig.json`, which is a mix of Remix and
moon. Other compiler options, like `isolatedModules` and `esModuleInterop`, should be declared in a
shared configuration found in the workspace root (`tsconfig.projectOptions.json` in the example).
```json title="/tsconfig.json"
{
"extends": "../../tsconfig.projectOptions.json",
"compilerOptions": {
"baseUrl": ".",
"emitDeclarationOnly": false,
"jsx": "react-jsx",
"resolveJsonModule": true,
"moduleResolution": "node",
"noEmit": true,
"paths": {
"~/*": ["./app/*"]
}
},
"include": [".eslintrc.js", "remix.env.d.ts", "**/*"],
"exclude": [".cache", "build", "public"]
}
```
## Configuration
### Root-level
We suggest _against_ root-level configuration, as Remix should be installed per-project, and the
`remix` command expects the configuration to live relative to the project root.
### Project-level
When creating a new Remix project, a
[`remix.config.js`](https://remix.run/docs/en/v1/api/conventions) is created, and _must_ exist in
the project root. This allows each project to configure Remix for their needs.
```js title="/remix.config.js"
module.exports = {
appDirectory: 'app',
};
```
---
## Solid example
[Solid](https://www.solidjs.com) (also known as SolidJS) is a JavaScript framework for building
interactive web applications. Because of this, Solid is an application or library concern, and not a
build system one, since the bundling of Solid is abstracted away through the application or a
bundler.
With that being said, we do have some suggestions on utilizing Solid effectively in a monorepo. To
begin, install Solid to a project.
## Setup
Solid utilizes JSX for rendering markup, which requires
[`babel-preset-solid`](https://www.npmjs.com/package/babel-preset-solid) for parsing and
transforming. To enable the preset for the entire monorepo, add the preset to a root
`babel.config.js`, otherwise add it to a `.babelrc.js` in each project that requires it.
```js
module.exports = {
presets: ['solid'],
};
```
### TypeScript integration
For each project using Solid, add the following compiler options to the `tsconfig.json` found in the
project root.
```json title="/tsconfig.json"
{
"compilerOptions": {
"jsx": "preserve",
"jsxImportSource": "solid-js"
}
}
```
### Vite integration
If you're using a [Vite](./vite) powered application (Solid Start or starter templates), you should
enable [`vite-plugin-solid`](https://www.npmjs.com/package/vite-plugin-solid) instead of configuring
Babel. Be sure to read our [guide on Vite](./vite) as well!
```js title="/vite.config.js"
export default defineConfig({
// ...
plugins: [solidPlugin()],
});
```
---
## Storybook example
Storybook is a frontend workshop for building UI components and pages in isolation. Thousands of
teams use it for UI development, testing, and documentation. It’s open source and free.
[Storybook v7](https://storybook.js.org/docs/7.0) is typically coupled with
[Vite](https://vitejs.dev/). To scaffold a new Storybook project with Vite, run the following
command in a project root. This guide assumes you are using React, however it is possible to use
almost any (meta) framework with Storybook.
```shell
cd && npx storybook init
```
> We highly suggest reading our documentation on [using Vite (and Vitest) with moon](./vite) and
> [using Jest with moon](./jest) for a more holistic view.
## Setup
This section assumes Storybook is being used with Vite, and is integrated on a per-project basis.
After setting up Storybook, ensure [`moon.*`](../../config/project) has the following tasks:
```yaml title="/moon.yml"
fileGroups:
storybook:
- 'src/**/*'
- 'stories/**/*'
- 'tests/**/*'
- '.storybook/**/*'
tasks:
buildStorybook:
command: 'build-storybook --output-dir @out(0)'
inputs:
- '@group(storybook)'
outputs:
- 'build'
storybook:
preset: 'server'
command: 'start-storybook'
inputs:
- '@group(storybook)'
```
To run the Storybook development server:
```shell
moon run :storybook
```
### Vite integration
Storybook 7 uses Vite out of the box, and as such, no configuration is required, but should you
choose to extend the Vite config, you can do so by passing in `viteFinal`:
```ts title=".storybook/main.ts"
export default {
stories: ['../stories/**/*.stories.mdx', '../stories/**/*.stories.@(js|jsx|ts|tsx)'],
addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
core: {
builder: '@storybook/builder-vite',
},
async viteFinal(config) {
// Merge custom configuration into the default config
return mergeConfig(config, {
// Use the same "resolve" configuration as your app
resolve: (await import('../vite.config.js')).default.resolve,
// Add dependencies to pre-optimization
optimizeDeps: {
include: ['storybook-dark-mode'],
},
});
},
};
```
For more information on how to integrate Vite with Storybook see the
[relevant documentation](https://storybook.js.org/docs/7.0/react/builders/vite#configuration).
### Webpack integration
If you want to use Webpack with your Storybook project, you can do so by installing the relevant
package and updating configuration.
```ts title=".storybook/main.ts"
export default {
core: {
builder: '@storybook/builder-webpack5',
},
};
```
For more information on how to integrate Webpack with Storybook, see the
[relevant documentation](https://storybook.js.org/docs/7.0/react/builders/webpack).
### Jest integration
You can use Jest to test your stories, but isn't a requirement. Storybook ships with first-party
plugins for improved developer experience.
Install the test runner and any relevant packages:
Add the test task to your project:
```yaml title="/moon.yml"
tasks:
testStorybook:
command: 'test-storybook'
inputs:
- '@group(storybook)'
```
Then enable plugins and interactions in your Storybook project:
```ts title=".storybook/main.ts"
export default {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
// Other Storybook addons
'@storybook/addon-interactions', // Addon is registered here
'@storybook/addon-coverage',
],
features: {
interactionsDebugger: true, // Enable playback controls
},
};
```
You can now start writing your tests. For an extended guide on how to write tests within your
stories, see
[writing an interaction test](https://storybook.js.org/docs/react/writing-tests/interaction-testing#write-an-interaction-test)
on the Storybook docs.
## Configuration
Storybook requires a `.storybook` folder relative to the project root. Because of this, Storybook
should be scaffolded in each project individually. Configuration may be shared through package
imports.
---
## SvelteKit example
[SvelteKit](https://kit.svelte.dev) is built on [Svelte](https://svelte.dev), a UI framework that
uses a compiler to let you write breathtakingly concise components that do minimal work in the
browser, using languages you already know — HTML, CSS and JavaScript. It's a love letter to web
development.
```shell
cd apps && npm create svelte@latest
```
You will be prompted to choose between select templates, TypeScript, ESLint, Prettier, Playwright
and Vitest among other options. moon supports and has guides for many of these tools.
> We highly suggest reading our documentation on [using Vite (and Vitest) with moon](./vite),
> [using ESLint with moon](./eslint) and [using Prettier with moon](./prettier) for a more holistic
> view.
## Setup
Since SvelteKit is per-project, the associated moon tasks should be defined in each project's
[`moon.*`](../../config/project) file.
:::tip
We suggest inheriting SvelteKit tasks from the
[official moon configuration preset](https://github.com/moonrepo/moon-configs/tree/master/javascript/sveltekit).
:::
```yaml title="/moon.yml"
# Inherit tasks from the `sveltekit` preset
# https://github.com/moonrepo/moon-configs
tags: ['sveltekit']
```
### ESLint integration
SvelteKit provides an option to setup ESLint along with your project, with moon you can use a
[global `lint` task](./eslint). We encourage using the global `lint` task for consistency across all
projects within the repository. With this approach, the `eslint` command itself will be ran and the
`svelte3` rules will still be used.
```yaml title="/moon.yml"
tasks:
# Extends the top-level lint
lint:
args:
- '--ext'
- '.ts,.svelte'
```
Be sure to enable the Svelte parser and plugin in a project local ESLint configuration file.
```js title=".eslintrc.cjs"
module.exports = {
plugins: ['svelte3'],
ignorePatterns: ['*.cjs'],
settings: {
'svelte3/typescript': () => require('typescript'),
},
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
};
```
### TypeScript integration
SvelteKit also has built-in support for TypeScript, but has similar caveats to the
[ESLint integration](#eslint-integration). TypeScript itself is a bit involved, so we suggest
reading the official [SvelteKit documentation](https://kit.svelte.dev/docs/introduction) before
continuing.
At this point we'll assume that a `tsconfig.json` has been created in the application, and
typechecking works. From here we suggest utilizing a [global `typecheck` task](./typescript) for
consistency across all projects within the repository. However, because Svelte isn't standard
JavaScript, it requires the use of the `svelte-check` command for type-checking.
:::info
The
[moon configuration preset](https://github.com/moonrepo/moon-configs/tree/master/javascript/sveltekit)
provides the `check` task below.
:::
```yaml title="/moon.yml"
workspace:
inheritedTasks:
exclude: ['typecheck']
tasks:
check:
command: 'svelte-check --tsconfig ./tsconfig.json'
deps:
- 'typecheck-sync'
inputs:
- '@group(svelte)'
- 'tsconfig.json'
```
In case Svelte doesn't automatically create a `tsconfig.json`, you can use the following:
```json title="/tsconfig.json"
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true
}
}
```
## Configuration
### Root-level
We suggest _against_ root-level configuration, as SvelteKit should be installed per-project, and the
`vite` command expects the configuration to live relative to the project root.
### Project-level
When creating a new SvelteKit project, a
[`svelte.config.js`](https://kit.svelte.dev/docs/configuration) is created, and _must_ exist in the
project root. This allows each project to configure SvelteKit for their needs.
```js title="/svelte.config.js"
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: vitePreprocess(),
kit: {
adapter: adapter(),
},
};
export default config;
```
---
## TypeScript example
In this guide, you'll learn how to integrate [TypeScript](https://www.typescriptlang.org/) into
moon. We'll be using [project references](../javascript/typescript-project-refs), as it ensures that
only affected projects are built, and not the entire repository.
Begin by installing `typescript` and any pre-configured tsconfig packages in your root. We suggest
using the same version across the entire repository.
## Setup
Since typechecking is a universal workflow, add a `typecheck` task to
[`.moon/tasks/**/*`](../../config/tasks) with the following parameters.
```yaml title=".moon/tasks/typescript.yml"
tasks:
typecheck:
command:
- 'tsc'
# Use incremental builds with project references
- '--build'
# Always use pretty output
- '--pretty'
# Use verbose logging to see affected projects
- '--verbose'
inputs:
# Source and test files
- 'src/**/*'
- 'tests/**/*'
# Type declarations
- 'types/**/*'
# Project configs
- 'tsconfig.json'
- 'tsconfig.*.json'
# Root configs (extended from only)
- '/tsconfig.options.json'
outputs:
# Matches `compilerOptions.outDir`
- 'lib'
```
Projects can extend this task and provide additional parameters if need be, for example.
```yaml title="/moon.yml"
tasks:
typecheck:
args:
# Force build every time
- '--force'
```
## Configuration
### Root-level
Multiple root-level TypeScript configs are _required_, as we need to define compiler options that
are shared across the repository, and we need to house a list of all project references.
To start, let's create a `tsconfig.options.json` that will contain our compiler options. In our
example, we'll extend [tsconfig-moon](https://www.npmjs.com/package/tsconfig-moon) for convenience.
Specifically, the `tsconfig.workspaces.json` config, which enables ECMAScript modules, composite
mode, declaration emitting, and incremental builds.
```json title="tsconfig.options.json"
{
"extends": "tsconfig-moon/tsconfig.projects.json",
"compilerOptions": {
// Your custom options
"moduleResolution": "nodenext",
"target": "es2022"
}
}
```
We'll also need the standard `tsconfig.json` to house our project references. This is used by
editors and tooling for deep integrations.
```json title="tsconfig.json"
{
"extends": "./tsconfig.options.json",
"files": [],
// All project references in the repo
"references": []
}
```
> The [`typescript.rootConfigFileName`](../../config/toolchain#rootconfigfilename) setting can be
> used to change the root-level config name and the
> [`typescript.syncProjectReferences`](../../config/toolchain#syncprojectreferences) setting will
> automatically keep project references in sync!
### Project-level
Every project will require a `tsconfig.json`, as TypeScript itself requires it. The following
`tsconfig.json` will typecheck the entire project, including source and test files.
```json title="/tsconfig.json"
{
// Extend the root compiler options
"extends": "../../tsconfig.options.json",
"compilerOptions": {
// Declarations are written here
"outDir": "lib"
},
// Include files in the project
"include": ["src/**/*", "tests/**/*"],
// Depends on other projects
"references": []
}
```
> The [`typescript.projectConfigFileName`](../../config/toolchain#projectconfigfilename) setting can
> be used to change the project-level config name.
### Sharing
To share configuration across projects, you have 3 options:
- Define settings in a [root-level config](#root-level). This only applies to the parent repository.
- Create and publish an
[`tsconfig base`](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html#tsconfig-bases)
npm package. This can be used in any repository.
- A combination of 1 and 2.
For options 2 and 3, if you're utilizing package workspaces, create a local package with the
following content.
```json title="packages/tsconfig-company/tsconfig.json"
{
"compilerOptions": {
// ...
"lib": ["esnext"]
}
}
```
Within another `tsconfig.json`, you can extend this package to inherit the settings.
```json title="tsconfig.json"
{
"extends": "tsconfig-company/tsconfig.json"
}
```
## FAQ
### How to preserve pretty output?
TypeScript supports a pretty format where it includes codeframes and color highlighting for
failures. However, when `tsc` is piped or the terminal is not a TTY, the pretty format is lost. To
preserve and always display the pretty format, be sure to pass the `--pretty` argument!
---
## Vite & Vitest example
In this guide, you'll learn how to integrate [Vite](https://vitejs.dev/) and
[Vitest](https://vitest.dev/) into moon.
Begin by creating a new Vite project in the root of an existing moon project (this should not be
created in the workspace root, unless a polyrepo).
If you plan on using Vitest, run the following command to add the `vitest` dependency to a project,
otherwise skip to the setup section.
## Setup
Since Vite is per-project, the associated moon tasks should be defined in each project's
[`moon.*`](../../config/project) file.
:::tip
We suggest inheriting Vite tasks from the
[official moon configuration preset](https://github.com/moonrepo/moon-configs/tree/master/javascript/vite).
:::
```yaml title="/moon.yml"
# Inherit tasks from the `vite` and `vitest` presets
# https://github.com/moonrepo/moon-configs
tags: ['vite', 'vitest']
```
## Configuration
### Root-level
We suggest _against_ root-level configuration, as Vite should be installed per-project, and the
`vite` command expects the configuration to live relative to the project root.
### Project-level
When creating a new Vite project, a [`vite.config.`](https://vitejs.dev/config) is created,
and _must_ exist in the project root.
```js title="/vite.config.js"
export default defineConfig({
// ...
build: {
// These must be `outputs` in the `build` task
outDir: 'dist',
},
test: {
// Vitest settings
},
});
```
> If you'd prefer to configure Vitest in a
> [separate configuration file](https://vitest.dev/guide/#configuring-vitest), create a
> `vitest.config.` file.
---
## Vue example
Vue is an application or library concern, and not a build system one, since the bundling of Vue is
abstracted away through other tools. Because of this, moon has no guidelines around utilizing Vue
directly. You can use Vue however you wish!
However, with that being said, Vue is typically coupled with [Vite](https://vitejs.dev/). To
scaffold a new Vue project with Vite, run the following command in a project root.
```shell
npm init vue@latest
```
> We highly suggest reading our documentation on [using Vite (and Vitest) with moon](./vite) for a
> more holistic view.
## Setup
This section assumes Vue is being used with Vite.
### ESLint integration
When linting with [ESLint](./eslint) and the
[`eslint-plugin-vue`](https://eslint.vuejs.org/user-guide/#installation) library, you'll need to
include the `.vue` extension within the `lint` task. This can be done by extending the top-level
task within the project (below), or by adding it to the top-level entirely.
```yaml title="/moon.yml"
tasks:
lint:
args:
- '--ext'
- '.js,.ts,.vue'
```
Furthermore, when using TypeScript within ESLint, we need to make a few additional changes to the
`.eslintrc.js` config found in the root (if the entire repo is Vue), or within the project (if only
the project is Vue).
```js
module.exports = {
parser: 'vue-eslint-parser',
parserOptions: {
extraFileExtensions: ['.vue'],
parser: '@typescript-eslint/parser',
project: 'tsconfig.json', // Or another config
tsconfigRootDir: __dirname,
},
};
```
### TypeScript integration
Vue does not use [TypeScript](./typescript)'s `tsc` binary directly, but instead uses
[`vue-tsc`](https://vuejs.org/guide/typescript/overview.html), which is a thin wrapper around `tsc`
to support Vue components. Because of this, we should update the `typecheck` task in the project to
utilize this command instead.
```yaml title="/moon.yml"
workspace:
inheritedTasks:
exclude: ['typecheck']
tasks:
typecheck:
command:
- 'vue-tsc'
- '--noEmit'
# Always use pretty output
- '--pretty'
inputs:
- 'env.d.ts'
# Source and test files
- 'src/**/*'
- 'tests/**/*'
# Project configs
- 'tsconfig.json'
- 'tsconfig.*.json'
# Root configs (extended from only)
- '/tsconfig.options.json'
```
> Be sure `tsconfig.json` compiler options are based on
> [`@vue/tsconfig`](https://vuejs.org/guide/typescript/overview.html#configuring-tsconfig-json).
---
## Execution plan
An execution plan is a JSON file that declaratively configures how [`moon exec`](../commands/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:
```shell
$ moon exec --plan plan.json
```
Or set the `MOON_EXEC_PLAN` environment variable:
```shell
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.
```json
{
"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:
```json
{
"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 `include` list (`excluding` coming soon):
```json
{
"targets": {
"include": ["app:build", "#frontend:test"]
}
}
```
- **Partitioned** — Split targets into groups for parallel CI jobs. Each inner array is one
partition:
```json
{
"targets": {
"jobs": [
["app:build", "app:test"],
["lib:build", "lib:test"]
]
}
}
```
Use in combination with [`pipeline.job`](#pipeline) 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 to
`none`. Same as `--downstream`.
- `upstream` (`none | direct | deep`) - Depth of upstream dependencies to include. Defaults to
`none`. 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 to `HEAD`. 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. Accepts `local` or `remote`.
- `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:
```json
{
"targets": ["app:build", "lib:build"]
}
```
### CI with affected filtering
Only run tests affected by changes between the base branch and HEAD:
```json
{
"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:
```json
{
"pipeline": {
"ci": true,
"jobTotal": 3
},
"targets": {
"jobs": [["app:build", "app:test"], ["lib:build", "lib:test"], ["docs:build"]]
}
}
```
Then in CI, run each partition:
```shell
# 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:
```json
{
"pipeline": {
"concurrency": 4,
"onFailure": "continue"
},
"targets": [":lint"]
}
```
### Deep dependency graph
Build a target along with all of its upstream dependencies and downstream dependents:
```json
{
"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:
```json
{
"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"]]
}
}
```
---
## Extensions
An extension is a WASM plugin that allows you to extend moon with additional functionality, have
whitelisted access to the file system, and receive partial information about the current workspace.
Extensions are extremely useful in offering new and unique functionality that doesn't need to be
built into moon's core. It also enables the community to build and share their own extensions!
## Using extensions
Before an extension can be executed with the [`moon ext`](../commands/ext) command, it must be
configured with [`.moon/extensions.*`](../config/extensions) (excluding
[built-in's](#built-in-extensions)).
```yaml title=".moon/extensions.yml"
example:
plugin: 'https://example.com/path/to/example.wasm'
```
Once configured, it can be executed with [`moon ext`](../commands/ext) by name. Arguments unique to
the extension _must_ be passed after a `--` separator.
```shell
$ moon ext example -- --arg1 --arg2
```
## Built-in extensions
moon is shipped with a few built-in extensions that are configured and enabled by default. Official
moon extensions are built and published in our [moonrepo/moon-extensions][repo] repository.
### `download`
The `download` extension can be used to download a file from a URL into the current workspace, as
defined by the `--url` argument. For example, say we want to download the latest [proto](/proto)
binary:
```shell
$ moon ext download --\
--url https://github.com/moonrepo/proto/releases/latest/download/proto_cli-aarch64-apple-darwin.tar.xz
```
By default this will download `proto_cli-aarch64-apple-darwin.tar.xz` into the current working
directory. To customize the location, use the `--dest` argument. However, do note that the
destination _must be_ within the current moon workspace, as only certain directories are whitelisted
for WASM.
```shell
$ moon ext download --\
--url https://github.com/moonrepo/proto/releases/latest/download/proto_cli-aarch64-apple-darwin.tar.xz\
--dest ./temp
```
#### Arguments
- `--url` (required) - URL of a file to download.
- `--dest` - Destination folder to save the file. Defaults to the current working directory.
- `--name` - Override the file name. Defaults to the file name in the URL.
### `migrate-nx`
> This extension is currently _experimental_ and will be improved over time.
The `migrate-nx` extension can be used to migrate an Nx powered repository to moon. This process
will convert the root `nx.json` and `workspace.json` files, and any `project.json` and
`package.json` files found within the repository. The following changes are made:
- Migrates `targetDefaults` as global tasks to [`.moon/tasks/node.yml`](../config/tasks#tasks) (or
`bun.yml`), `namedInputs` as file groups, `workspaceLayout` as projects, and more.
- Migrates all `project.json` settings to [`moon.yml`](../config/project#tasks) equivalent settings.
Target to task conversion assumes the following:
- Target `executor` will be removed, and we'll attempt to extract the appropriate npm package
command. For example, `@nx/webpack:build` -> `webpack build`.
- Target `options` will be converted to task `args`.
- The `{projectRoot}` and `{workspaceRoot}` interpolations will be replaced with moon tokens.
```shell
$ moon ext migrate-nx
```
:::caution
Nx and moon are quite different, so many settings are either ignored when converting, or are not a
1:1 conversion. We do our best to convert as much as possible, but some manual patching will most
likely be required! We suggest testing each converted task 1-by-1 to ensure it works as expected.
:::
#### Arguments
- `--bun` - Migrate to Bun based commands instead of Node.js.
- `--cleanup` - Remove Nx configs/files after migrating.
#### Unsupported
The following features are not supported in moon, and are ignored when converting.
- Most settings in `nx.json`.
- Named input variants: external dependencies, dependent task output files, dependent project
inputs, or runtime commands.
- Target `configurations` and `defaultConfiguration`. Another task will be created instead that uses
`extends`.
- Project `root` and `sourceRoot`.
### `migrate-turborepo`
The `migrate-turborepo` extension can be used to migrate a Turborepo powered repository to moon.
This process will convert the root `turbo.json` file, and any `turbo.json` files found within the
repository. The following changes are made:
- Migrates `pipeline` (v1) and `tasks` (v2) global tasks to
[`.moon/tasks/node.yml`](../config/tasks#tasks) (or `bun.yml`) and project scoped tasks to
[`moon.*`](../config/project#tasks). Task commands will execute `package.json` scripts through a
package manager.
- Migrates root `global*` settings to [`.moon/tasks/node.yml`](../config/tasks#implicitinputs) (or
`bun.yml`) as `implicitInputs`.
```shell
$ moon ext migrate-turborepo
```
#### Arguments
- `--bun` - Migrate to Bun based commands instead of Node.js.
- `--cleanup` - Remove Turborepo configs/files after migrating.
### `unpack`
The `unpack` extension can be used to unpack an archive (zip/tar) from a file path or URL into a
destination folder.
```shell
$ moon ext unpack -- --src ./path/to/archive.zip --dest ./output --prefix path/to/strip
```
#### Arguments
- `--src` (required) - Path or URL of a file to unpack.
- `--dest` - Destination folder to unpack into. Defaults to the current working directory.
- `--prefix` - A prefix path to strip from unpacked files.
## Creating an extension
Refer to our [official WASM guide](./wasm-plugins) for more information on how our WASM plugins
work, critical concepts to know, how to create a plugin, and more. Once you have a good
understanding, you may continue this specific guide.
:::note
Refer to our [moonrepo/plugins][repo] repository for in-depth examples.
:::
### Registering metadata
Before we begin, we must implement the `register_extension` function, which simply provides some
metadata that we can bubble up to users, or to use for deeper integrations.
```rust
use extism_pdk::*;
use moon_pdk::*;
#[plugin_fn]
pub fn register_extension(Json(input): Json) -> FnResult> {
Ok(Json(ExtensionMetadataOutput {
name: "Extension name".into(),
description: Some("A description about what the extension does.".into()),
plugin_version: env!("CARGO_PKG_VERSION").into(),
..ExtensionMetadataOutput::default()
}))
}
```
#### Configuration schema
If you are using [configuration](#supporting-configuration), you can register the shape of the
configuration using the [`schematic`](https://crates.io/crates/schematic) crate. This shape will be
used to generate outputs such as JSON schemas, or TypeScript types.
```rust
#[plugin_fn]
pub fn define_extension_config() -> FnResult> {
Ok(Json(DefineExtensionConfigOutput {
schema: schematic::SchemaBuilder::build_root::(),
}))
}
```
Schematic is a heavy library, so we suggest adding the dependency like so:
```toml
[dependencies]
schematic = { version = "*", default-features = false, features = ["schema"] }
```
### Implementing execution
Extensions support a single plugin function, `execute_extension`, which is called by the
[`moon ext`](../commands/ext) command to execute the extension. This is where all your business
logic will reside.
```rust
#[host_fn]
extern "ExtismHost" {
fn host_log(input: Json);
}
#[plugin_fn]
pub fn execute_extension(Json(input): Json) -> FnResult<()> {
host_log!(stdout, "Executing extension!");
Ok(())
}
```
### Supporting arguments
Most extensions will require arguments, as it provides a mechanism for users to pass information
into the WASM runtime. To parse arguments, we provide the
[`Args`](https://docs.rs/clap/latest/clap/trait.Args.html) trait/macro from the
[clap](https://crates.io/crates/clap) crate. Refer to their
[official documentation on usage](https://docs.rs/clap/latest/clap/_derive/index.html) (we don't
support everything).
```rust
use moon_pdk::*;
#[derive(Args)]
pub struct ExampleExtensionArgs {
// --url, -u
#[arg(long, short = 'u', required = true)]
pub url: String,
}
```
Once your struct has been defined, you can parse the provided input arguments using the
[`parse_args`](https://docs.rs/moon_pdk/latest/moon_pdk/args/fn.parse_args.html) function.
```rust
#[plugin_fn]
pub fn execute_extension(Json(input): Json) -> FnResult<()> {
let args = parse_args::(&input.args)?;
args.url; // --url
Ok(())
}
```
### Supporting configuration
Users can configure [extensions](../config/workspace#extensions) with additional settings in
[`.moon/extensions.*`](../config/extensions). Do note that settings should be in camelCase for them
to be parsed correctly!
```yaml title=".moon/extensions.yml"
example:
plugin: 'file://./path/to/example.wasm'
someSetting: 'abc'
anotherSetting: 123
```
In the plugin, we can map these settings (excluding `plugin`) into a struct. The `Default` trait
must be implemented to handle situations where settings were not configured, or some are missing.
```rust
config_struct!(
#[derive(Default)]
pub struct ExampleExtensionConfig {
pub some_setting: String,
pub another_setting: u32,
}
);
```
Once your struct has been defined, you can access the configuration using the
[`get_extension_config`](https://docs.rs/moon_pdk/latest/moon_pdk/extension/fn.get_extension_config.html)
function.
```rust
#[plugin_fn]
pub fn execute_extension(Json(input): Json) -> FnResult<()> {
let config = get_extension_config::()?;
config.another_setting; // 123
Ok(())
}
```
[repo]: https://github.com/moonrepo/plugins
---
## Bun handbook
Utilizing JavaScript (and TypeScript) in a monorepo can be a daunting task, especially when using
Bun (or Node.js), as there are many ways to structure your code and to configure your tools. With
this handbook, we'll help guide you through this process.
:::info
This guide is a living document and will continue to be updated over time!
:::
## moon setup
For this part of the handbook, we'll be focusing on [moon](/moon), our task runner. To start,
languages in moon act like plugins, where their functionality and support _is not_ enabled unless
explicitly configured. We follow this approach to avoid unnecessary overhead.
### Enabling the language
To enable JavaScript support via Bun, define the [`bun`](../../config/toolchain#bun) setting in
[`.moon/toolchains.*`](../../config/toolchain), even if an empty object. The
[`javascript`](../../config/toolchain#javascript) toolchain must also be enabled, and configured to
use Bun as the package manager.
```yaml title=".moon/toolchains.yml"
# Enable JavaScript
javascript:
packageManager: 'bun'
# Enable Bun
bun: {}
```
Or by pinning a `bun` version in [`.prototools`](../../proto/config) in the workspace root.
```toml title=".prototools"
bun = "1.0.0"
```
This will enable the JavaScript and Bun toolchains and provide the following automations around its
ecosystem:
- Node modules will automatically be installed if dependencies in `package.json` have changed, or
the lockfile has changed, since the last time a task has ran.
- We'll also take `package.json` workspaces into account and install modules in the correct
location; either the workspace root, in a project, or both.
- Relationships between projects will automatically be discovered based on `dependencies`,
`devDependencies`, and `peerDependencies` in `package.json`.
### Utilizing the toolchain
When a language is enabled, moon by default will assume that the language's binary is available
within the current environment (typically on `PATH`). This has the downside of requiring all
developers and machines to manually install the correct version of the language, _and to stay in
sync_.
Instead, you can utilize [moon's toolchain](../../concepts/toolchain), which will download and
install the language in the background, and ensure every task is executed using the exact version
across all machines.
Enabling the toolchain is as simple as defining the [`bun.version`](../../config/toolchain#version)
setting.
```yaml title=".moon/toolchains.yml"
# Enable Bun toolchain with an explicit version
bun:
version: '1.0.0'
```
> Versions can also be defined with [`.prototools`](../../proto/config).
### Configuring the toolchain
Since the JavaScript ecosystem supports multiple runtimes, moon is unable to automatically detect
the correct runtime for all scenarios. Does the existence of a `package.json` mean Node.js or Bun?
We don't know, and default to Node.js because of its popularity.
To work around this, you can set `toolchain` to "bun" at the task-level or project-level.
```yaml title="moon.yml"
# For all tasks in the project
toolchains:
default: ['javascript', 'bun']
tasks:
build:
command: 'webpack'
# For this specific task
toolchains: ['javascript', 'bun']
```
> The task-level `toolchains.default` only needs to be set if executing a `node_modules` binary! The
> `bun` binary automatically sets the toolchain to Bun.
### Using `package.json` scripts
If you're looking to prototype moon, or reduce the migration effort to moon tasks, you can configure
moon to inherit `package.json` scripts, and internally convert them to moon tasks. This can be
achieved with the [`javascript.inferTasksFromScripts`](../../config/toolchain#infertasksfromscripts)
setting.
```yaml title=".moon/toolchains.yml"
javascript:
inferTasksFromScripts: true
```
Or you can run scripts through `bun run` calls.
```yaml title="moon.yml"
tasks:
build:
command: 'bun run build'
```
## Handbook
:::info
Refer to the [Node.js handbook](./node-handbook) for more information on repository structure,
dependency management, and more. Since both runtimes are extremely similar, the information in that
handbook also applies to Bun!
:::
---
## Deno handbook
Utilizing Deno in a TypeScript based monorepo can be a non-trivial task. With this handbook, we'll
help guide you through this process.
:::info
This guide is a living document and will continue to be updated over time!
:::
## moon setup
For this part of the handbook, we'll be focusing on [moon](/moon), our task runner. To start,
languages in moon act like plugins, where their functionality and support _is not_ enabled unless
explicitly configured. We follow this approach to avoid unnecessary overhead.
### Enabling the language
To enable TypeScript support via Deno, define the [`deno`](../../config/toolchain#deno) setting in
[`.moon/toolchains.*`](../../config/toolchain), even if an empty object. The
[`javascript`](../../config/toolchain#javascript) toolchain must also be enabled, and configured to
use Bun as the package manager.
```yaml title=".moon/toolchains.yml"
# Enable JavaScript
javascript:
packageManager: 'deno'
# Enable Deno
deno: {}
```
Or by pinning a `deno` version in [`.prototools`](../../proto/config) in the workspace root.
```toml title=".prototools"
deno = "2.0.0"
```
This will enable the Deno toolchain and provide the following automations around its ecosystem:
- Automatic handling and caching of lockfiles (when the setting is enabled).
- Relationships between projects will automatically be discovered based on `imports`, `importMap`,
and `deps.ts` (currently experimental).
- And more to come!
## Coming soon!
The handbook is currently being written while we finalize our Deno integration support!
---
## Node.js handbook
Utilizing JavaScript (and TypeScript) in a monorepo can be a daunting task, especially when using
Node.js, as there are many ways to structure your code and to configure your tools. With this
handbook, we'll help guide you through this process.
:::info
This guide is a living document and will continue to be updated over time!
:::
## moon setup
For this part of the handbook, we'll be focusing on [moon](/moon), our task runner. To start,
languages in moon act like plugins, where their functionality and support _is not_ enabled unless
explicitly configured. We follow this approach to avoid unnecessary overhead.
### Enabling the language
To enable JavaScript support via Node.js, define the [`node`](../../config/toolchain#node) setting
in [`.moon/toolchains.*`](../../config/toolchain), even if an empty object. The
[`javascript`](../../config/toolchain#javascript) toolchain must also be enabled, and configured to
use Bun as the package manager.
```yaml title=".moon/toolchains.yml"
# Enable JavaScript
javascript:
packageManager: 'pnpm'
# Enable Node.js and pnpm
node: {}
pnpm: {}
```
Or by pinning a `node` version in [`.prototools`](../../proto/config) in the workspace root.
```toml title=".prototools"
node = "24.0.0"
pnpm = "7.29.0"
```
This will enable the Node.js toolchain and provide the following automations around its ecosystem:
- Node modules will automatically be installed if dependencies in `package.json` have changed, or
the lockfile has changed, since the last time a task has ran.
- We'll also take `package.json` workspaces into account and install modules in the correct
location; either the workspace root, in a project, or both.
- Relationships between projects will automatically be discovered based on `dependencies`,
`devDependencies`, and `peerDependencies` in `package.json`.
- The versions of these packages will also be automatically synced when changed.
- Tasks can be [automatically inferred](../../config/toolchain#infertasksfromscripts) from
`package.json` scripts.
- And much more!
### Utilizing the toolchain
When a language is enabled, moon by default will assume that the language's binary is available
within the current environment (typically on `PATH`). This has the downside of requiring all
developers and machines to manually install the correct version of the language, _and to stay in
sync_.
Instead, you can utilize [moon's toolchain](../../concepts/toolchain), which will download and
install the language in the background, and ensure every task is executed using the exact version
across all machines.
Enabling the toolchain is as simple as defining the [`node.version`](../../config/toolchain#version)
setting.
```yaml title=".moon/toolchains.yml"
# Enable Node.js toolchain with an explicit version
node:
version: '18.0.0'
```
> Versions can also be defined with [`.prototools`](../../proto/config).
### Using `package.json` scripts
If you're looking to prototype moon, or reduce the migration effort to moon tasks, you can configure
moon to inherit `package.json` scripts, and internally convert them to moon tasks. This can be
achieved with the [`javascript.inferTasksFromScripts`](../../config/toolchain#infertasksfromscripts)
setting.
```yaml title=".moon/toolchains.yml"
javascript:
inferTasksFromScripts: true
```
Or you can run scripts through `npm run` (or `pnpm`, `yarn`) calls.
```yaml title="moon.yml"
tasks:
build:
command: 'npm run build'
```
## Repository structure
JavaScript monorepo's work best when projects are split into applications and packages, with each
project containing its own `package.json` and dependencies. A root `package.json` must also exist
that pieces all projects together through workspaces.
For small repositories, the following structure typically works well:
```
/
├── .moon/
├── package.json
├── apps/
│ ├── client/
| | ├── ...
│ | └── package.json
│ └── server/
| ├── ...
│ └── package.json
└── packages/
├── components/
| ├── ...
│ └── package.json
├── theme/
| ├── ...
│ └── package.json
└── utils/
├── ...
└── package.json
```
For large repositories, grouping projects by team or department helps with ownership and
organization. With this structure, applications and libraries can be nested at any depth.
```
/
├── .moon/
├── package.json
├── infra/
│ └── ...
├── internal/
│ └── ...
├── payments/
│ └── ...
└── shared/
└── ...
```
### Applications
Applications are runnable or executable, like an HTTP server, and are pieced together with packages
and its own encapsulated code. They represent the whole, while packages are the pieces. Applications
can import and depend on packages, but they _must not_ import and depend on other applications.
In moon, you can denote a project as an application using the [`layer`](../../config/project#layer)
setting in [`moon.*`](../../config/project).
```yaml title="moon.yml"
layer: 'application'
```
### Packages
Packages (also known as a libraries) are self-contained reusable pieces of code, and are the
suggested pattern for [code sharing](#code-sharing). Packages can import and depend on other
packages, but they _must not_ import and depend on applications!
In moon, you can denote a project as a library using the [`layer`](../../config/project#layer)
setting in [`moon.*`](../../config/project).
```yaml title="moon.yml"
layer: 'library'
```
### Configuration
Every tool that you'll utilize in a repository will have its own configuration file. This will be a
lot of config files, but regardless of what tool it is, where the config file should go will fall
into 1 of these categories:
- **Settings are inherited by all projects.** These are known as universal tools, and enforce code
consistency and quality across the entire repository. Their config file must exist in the
repository root, but may support overrides in each project.
- Examples: Babel, [ESLint](../examples/eslint), [Prettier](../examples/prettier),
[TypeScript](../examples/typescript)
- **Settings are unique per project.** These are developers tools that must be configured separately
for each project, as they'll have different concerns. Their config file must exist in each
project, but a shared configuration may exist as a base (for example, Jest presets).
- Examples: [Jest](../examples/jest), [TypeScript](../examples/typescript) (with project
references)
- **Settings are one-offs.** These are typically for applications or tools that require their own
config, but aren't prevalent throughout the entire repository.
- Examples: [Astro](../examples/astro), [Next](../examples/next), [Nuxt](../examples/nuxt),
[Remix](../examples/remix), Tailwind
## Dependency management
Dependencies, also known as node modules, are required by all projects, and are installed through a
package manager like npm, pnpm, or yarn. It doesn't matter which package manager you choose, but we
highly suggest choosing one that has proper workspaces support. If you're unfamiliar with
workspaces, they will:
- Resolve all `package.json`'s in a repository using glob patterns.
- Install dependencies from all `package.json`'s at once, in the required locations.
- Create symlinks of local packages in `node_modules` (to emulate an installed package).
- Deduplicate and hoist `node_modules` when applicable.
All of this functionality enables robust monorepo support, and can be enabled with the following:
```json title="package.json"
{
// ...
"workspaces": ["apps/*", "packages/*"]
}
```
```yaml title=".yarnrc.yml"
# ...
nodeLinker: 'node-modules'
```
- [Documentation](https://yarnpkg.com/features/workspaces)
```json title="package.json"
{
// ...
"workspaces": ["apps/*", "packages/*"]
}
```
- [Documentation](https://classic.yarnpkg.com/en/docs/workspaces)
```json title="package.json"
{
// ...
"workspaces": ["apps/*", "packages/*"]
}
```
- [Documentation](https://docs.npmjs.com/cli/v8/using-npm/workspaces)
```yaml title="pnpm-workspace.yaml"
packages:
- 'apps/*'
- 'packages/*'
```
- [Documentation](https://pnpm.io/workspaces)
:::info
Package workspaces are not a requirement for monorepos, but they do solve an array of problems
around module resolution, avoiding duplicate packages in bundles, and general interoperability.
Proceed with caution for non-workspaces setups!
:::
### Workspace commands
The following common commands can be used for adding, removing, or managing dependencies in a
workspace. View the package manager's official documentation for a thorough list of commands.
Install dependencies:
```shell
npm install
```
Add a package:
```shell
# At the root
npm install
# In a project
npm install --workspace
```
Remove a package:
```shell
# At the root
npm install
# In a project
npm install --workspace
```
Update packages:
```shell
npx npm-check-updates --interactive
```
Install dependencies:
```shell
pnpm install
```
Add a package:
```shell
# At the root
pnpm add
# In a project
pnpm add --filter
```
Remove a package:
```shell
# At the root
pnpm remove
# In a project
pnpm remove --filter
```
Update packages:
```shell
pnpm update -i -r --latest
```
Install dependencies:
```shell
yarn install
```
Add a package:
```shell
# At the root
yarn add
# In a project
yarn workspace add
```
Remove a package:
```shell
# At the root
yarn remove
# In a project
yarn workspace remove
```
Update packages:
```shell
yarn upgrade-interactive
```
Install dependencies:
```shell
yarn install
```
Add a package:
```shell
# At the root
yarn add -w
# In a project
yarn workspace add
```
Remove a package:
```shell
# At the root
yarn remove -w
# In a project
yarn workspace remove
```
Update packages:
```shell
yarn upgrade-interactive --latest
```
### Developer tools at the root
While not a strict guideline to follow, we've found that installing universal developer tool related
dependencies (Babel, ESLint, Jest, TypeScript, etc) in the root `package.json` as `devDependencies`
to be a good pattern for consistency, quality, and the health of the repository. It provides the
following benefits:
- It ensures all projects are utilizing the same version (and sometimes configuration) of a tool.
- It allows the tool to easily be upgraded. Upgrade once, applied everywhere.
- It avoids conflicting or outdated versions of the same package.
With that being said, this _does not_ include development dependencies that are unique to a project!
### Product libraries in a project
Product, application, and or framework specific packages should be installed as production
`dependencies` in a project's `package.json`. We've found this pattern to work well for the
following reasons:
- Application dependencies are pinned per project, avoiding accidental regressions.
- Applications can upgrade their dependencies and avoid breaking neighbor applications.
## Code sharing
One of the primary reasons to use a monorepo is to easily share code between projects. When code is
co-located within the same repository, it avoids the overhead of the "build -> version -> publish to
registry -> upgrade in consumer" workflow (when the code is located in an external repository).
Co-locating code also provides the benefit of fast iteration, fast adoption, and easier migration
(when making breaking changes for example).
With [package workspaces](#dependency-management), code sharing is a breeze. As mentioned above,
every project that contains a `package.json` that is part of the workspace, will be symlinked into
`node_modules`. Because of this, these packages can easily be imported using their `package.json`
name.
```ts
// Imports from /packages/utils/package.json
```
### Depending on packages
Because packages are symlinked into `node_modules`, we can depend on them as if they were normal npm
packages, but with 1 key difference. Since these packages aren't published, they do not have a
version to reference, and instead, we can use the special `workspace:^` version (yarn and pnpm only,
use `*` for npm).
```json
{
"name": "@company/consumer",
"dependencies": {
"@company/provider": "workspace:^"
}
}
```
The `workspace:` version basically means "use the package found in the current workspace". The `:^`
determines the version range to _substitute with when publishing_. For example, the `workspace:^`
above would be replaced with version of `@company/provider` as `^` when the
`@company/consumer` package is published.
There's also `workspace:~` and `workspace:*` which substitutes to `~` and ``
respectively. We suggest using `:^` so that version ranges can be deduped.
### Types of packages
When sharing packages in a monorepo, there's typically 3 different kinds of packages:
#### Local only
A local only package is just that, it's only available locally to the repository and _is not_
published to a registry, and _is not_ available to external repositories. For teams and companies
that utilize a single repository, this will be the most common type of package.
A benefit of local packages is that they do not require a build step, as source files can be
imported directly ([when configured correctly](#bundler-integration)). This avoids a lot of
`package.json` overhead, especially in regards to `exports`, `imports`, and other import patterns.
#### Internally published
An internal package is published to a private registry, and _is not_ available to the public.
Published packages are far more strict than local packages, as the `package.json` structure plays a
much larger role for downstream consumers, as it dictates how files are imported, where they can be
found, what type of formats are supported (CJS, ESM), so on and so forth.
Published packages require a build step, for both source code and TypeScript types (when
applicable). We suggest using [esbuild](https://esbuild.github.io/) or
[Packemon](../examples/packemon) to handle this entire flow. With that being said, local projects
can still [import their source files](#bundler-integration).
#### Externally published
An external package is structured similarly to an internal package, but instead of publishing to a
private registry, it's published to the npm public registry.
External packages are primarily for open source projects, and require the repository to also be
public.
### Bundler integration
Co-locating packages is great, but how do you import and use them effectively? The easiest solution
is to configure resolver aliases within your bundler (Webpack, Vite, etc). By doing so, you enable
the following functionality:
- Avoids having to build (and rebuild) the package everytime its code changes.
- Enables file system watching of the package, not just the application.
- Allows for hot module reloading (HMR) to work.
- Package code is transpiled and bundled alongside application code.
```ts title="vite.config.ts"
export default defineConfig({
// ...
resolve: {
alias: {
'@company/utils': path.join(__dirname, '../packages/utils/src'),
},
},
});
```
```ts title="webpack.config.js"
const path = require('path');
module.exports = {
// ...
resolve: {
alias: {
'@company/utils': path.join(__dirname, '../packages/utils/src'),
},
},
};
```
:::info
When configuring aliases, we suggest using the `package.json` name as the alias! This ensures that
on the consuming side, you're using the package as if it's a normal node module, and avoids
deviating from the ecosystem.
:::
### TypeScript integration
We suggest using TypeScript project references. Luckily, we have an
[in-depth guide on how to properly and efficiently integrate them](./typescript-project-refs)!
---
## TypeScript project references
> The ultimate in-depth guide for using TypeScript in a monorepo effectively!
How to use TypeScript in a monorepo? What are project references? Why use project references? What
is the best way to use project references? These are just a handful of questions that are
_constantly_ asked on Twitter, forums, Stack Overflow, and even your workplace.
Based on years of experience managing large-scale frontend repositories, we firmly believe that
TypeScript project references are the proper solution for effectively scaling TypeScript in a
monorepo. The official
[TypeScript documentation on project references](https://www.typescriptlang.org/docs/handbook/project-references.html)
answers many of these questions, but it basically boils down to the following:
- Project references _enforce project boundaries, disallowing imports_ to arbitrary projects unless
they have been referenced explicitly in configuration. This avoids circular references / cycles.
- It enables TypeScript to _process individual units_, instead of the entire repository as a whole.
Perfect for reducing CI and local development times.
- It supports _incremental compilation_, so only out-of-date or affected projects are processed. The
more TypeScript's cache is warmed, the faster it will be.
- It simulates how types work in the Node.js package ecosystem.
This all sounds amazing but there's got to be some downsides right? Unfortunately, there is:
- Project references require generating declarations to resolve type information correctly. This
results in a lot of compilation artifacts littered throughout the repository. There
[are ways](#gitignore) [around this](../../config/toolchain#routeoutdirtocache).
- This approach is a bit involved and may require some cognitive overhead based on your current
level of TypeScript tooling knowledge.
:::success
If you'd like a real-world repository to reference, our
[moonrepo/moon](https://github.com/moonrepo/moon), [moonrepo/dev](https://github.com/moonrepo/dev),
and [moonrepo/examples](https://github.com/moonrepo/examples) repositories utilizes this
architecture!
:::
## Preface
Before you dive into this questionably long guide, we'd like to preface with:
- This guide is a living document and will continually be updated with best practices and frequently
asked questions. Keep returning to learn more!
- This guide assumes a basic level knowledge of TypeScript and how it works.
- The architecture outlined in this guide assumes that TypeScript is _only_ used for typechecking
and _not_ compiling. However, supporting compilation should be as easy as modifying a handful of
compiler options.
- Although this guide exists within moon's documentation, it _does not_ require moon. We've kept all
implementation details generic enough for it be used in any repository, but have also included
many notes on how moon would improve this experience.
## Configuration
The most complicated part of integrating TypeScript in a monorepo is a proper configuration setup.
Based on our extensive experience, we suggest the following architecture as a base! This _is not_
perfect and can most definitely be expanded upon or modified to fit your needs.
### Root-level
In a polyrepo, the root `tsconfig.json` is typically the only configuration file, as it defines
common compiler options, and includes files to typecheck. In a monorepo, these responsibilities are
now split across multiple configuration files.
#### `tsconfig.json`
To start, the root `tsconfig.json` file is nothing more than a list of _all_ projects in the
monorepo, with each project being an individual entry in the `references` field. Each entry must
contain a `path` field with a relative file system path to the project root (that contains their
config).
We also _do not_ define compiler options in this file, as project-level configuration files would
_not_ be able to extend this file, as it would trigger a circular reference. Instead, we define
common compiler options in a root [`tsconfig.options.json`](#tsconfigoptionsjson) file, that this
file also `extends` from.
In the end, this file should only contain 3 fields: `extends`, `files` (an empty list), and
`references`. This abides the
[official guidance around structure](https://www.typescriptlang.org/docs/handbook/project-references.html#overall-structure).
```json file="tsconfig.json"
{
"extends": "./tsconfig.options.json",
"files": [],
"references": [
{
"path": "apps/foo"
},
{
"path": "packages/bar"
}
// ... more
]
}
```
> When using moon, the
> [`typescript.syncProjectReferences`](../../config/toolchain#syncprojectreferences) setting will
> keep this `references` list automatically in sync, and the name of the file can be customized with
> [`typescript.rootConfigFileName`](../../config/toolchain#rootconfigfilename).
#### `tsconfig.options.json`
This file will contain common compiler options that will be inherited by _all_ projects in the
monorepo. For project references to work correctly, the following settings _must_ be enabled at the
root, and typically should not be disabled in each project.
- `composite` - Enables project references and informs the TypeScript program where to find
referenced outputs.
- `declaration` - Project references rely on the compiled declarations (`.d.ts`) of external
projects. If declarations do not exist, TypeScript will generate them on demand.
- `declarationMap` - Generate sourcemaps for declarations, so that language server integrations in
editors like "Go to" resolve correctly.
- `incremental` - Enables incremental compilation, greatly improving performance.
- `noEmitOnError` - If the typechecker fails, avoid generating invalid or partial declarations.
- `skipLibCheck` - Avoids eager loading and analyzing all declarations, greatly improving
performance.
Furthermore, we have 2 settings that should be enabled _per project_, depending on the project type.
- `emitDeclarationOnly` - For packages: Emit declarations, as they're required for references, but
avoid compiling to JavaScript.
- `noEmit` - For applications: Don't emit declarations, as others _should not_ be depending on the
project.
For convenience, we provide the
[`tsconfig-moon`](https://github.com/moonrepo/dev/tree/master/packages/tsconfig) package, which
defines common compiler options and may be used here.
```json file="tsconfig.options.json"
{
"compilerOptions": {
"composite": true,
"declaration": true,
"declarationMap": true,
"emitDeclarationOnly": true,
"incremental": true,
"noEmitOnError": true,
"skipLibCheck": true
// ... others
}
}
```
> When using moon, the name of the file can be customized with
> [`typescript.rootOptionsConfigFileName`](../../config/toolchain#rootoptionsconfigfilename).
##### ECMAScript interoperability
ECMAScript modules (ESM) have been around for quite a while now, but the default TypeScript settings
are not configured for them. We suggest the following compiler options if you want proper ESM
support with interoperability with the ecosystem.
```json file="tsconfig.options.json"
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"isolatedModules": true,
"module": "esnext",
"moduleResolution": "bundler",
"strict": true,
"target": "esnext"
// ... others
}
}
```
#### `.gitignore`
Project references unfortunately generate _a ton_ of artifacts that typically shouldn't be committed
to the repository (but could be if you so choose). We suggest ignoring the following:
```shell title=".gitignore"
# The `outDir` for declarations
lib/
# Build cache manifests
*.tsbuildinfo
```
### Project-level
Each project that contains TypeScript files and will utilize the typechecker _must_ contain a
`tsconfig.json` in the project root, typically as a sibling to `package.json`.
#### `tsconfig.json`
A `tsconfig.json` in the root of a project (application or package) is required, as it informs
TypeScript that this is a project, and that it can be referenced by other projects. In its simplest
form, this file should extend the root [`tsconfig.options.json`](#tsconfigoptionsjson) to inherit
common compiler options, define its own compiler options (below), define includes/excludes, and any
necessary references.
> When using moon, the name of the file can be customized with
> [`typescript.projectConfigFileName`](../../config/toolchain#projectconfigfilename).
For applications, declaration emitting can be disabled, since external projects _should not_ be
importing files from an application. If this use case ever arises, move those files into a package.
```json title="apps/foo/tsconfig.json"
{
"extends": "../../../../tsconfig.options.json",
"compilerOptions": {
"noEmit": true
},
"include": [],
"references": []
}
```
For packages, we must define the location in which to generate declarations. These are the
declarations that external projects would reference. This location is typically
[gitignored](#gitignore)!
```json title="packages/bar/tsconfig.json"
{
"extends": "../../../../tsconfig.options.json",
"compilerOptions": {
"emitDeclarationOnly": true,
"outDir": "./lib"
},
"include": [],
"references": []
}
```
> When using moon, the `outDir` can automatically be re-routed to a shared cache using
> [`typescript.routeOutDirToCache`](../../config/toolchain#routeoutdirtocache), to avoid littering
> the repository with compilation artifacts.
##### Includes and excludes
Based on experience, we suggest defining `include` instead of `exclude`, as managing a whitelist of
typecheckable files is much easier. When dealing with excludes, there are far too many
possibilities. To start, you have `node_modules`, and for applications maybe `dist`, `build`,
`.next`, or another application specific folder, and then for packages you may have `lib`, `cjs`,
`esm`, etc. It becomes very... tedious.
The other benefit of using `include` is that it forces TypeScript to only load _what's necessary_,
instead of eager loading everything into memory, and for typechecking files that aren't part of
source, like configuration.
```json title="/tsconfig.json"
{
// ...
"include": ["src/**/*", "tests/**/*", "*.js", "*.ts"]
}
```
##### Depending on other projects
When a project depends on another project (by importing code from it), either using relative paths,
[path aliases](#using-paths-aliases), or its `package.json` name, it must be declared as a
reference. If not declared, TypeScript will error with a message about importing outside the project
boundary.
```json title="/tsconfig.json"
{
// ...
"references": [
{
"path": "../../foo"
},
{
"path": "../../bar"
},
{
"path": "../../../../baz"
}
]
}
```
To make use of editor intellisense and auto-imports of deeply nested files, you'll most likely need
to add includes for referenced projects as well.
```json title="/tsconfig.json"
{
// ...
"include": [
// ...
"src/**/*",
"../../foo/src/**/*",
"../../bar/src/**/*",
"../../../../baz/src/**/*"
]
}
```
> When using moon, the
> [`typescript.syncProjectReferences`](../../config/toolchain#syncprojectreferences) setting will
> keep this `references` list automatically in sync, and
> [`typescript.includeProjectReferenceSources`](../../config/toolchain#syncprojectreferences) for
> `include`.
#### `tsconfig.*.json`
Additional configurations may exist in a project that serve a role outside of typechecking, with one
such role being _npm package publishing_. These configs are sometimes named `tsconfig.build.json`,
`tsconfig.types.json`, or `tsconfig.lib.json`. Regardless of what they're called, these configs are
_optional_, so unless you have a business need for them, you may skip this section.
##### Package publishing
As mentioned previously, these configs may be used for npm packages, primarily for generating
TypeScript declarations that are mapped through the `package.json`
[`types` (or `typings`) field](https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html).
Given this `package.json`...
```json title="/package.json"
{
// ...
"types": "./lib/index.d.ts"
}
```
Our `tsconfig.build.json` may look like...
```json title="/tsconfig.build.json"
{
"extends": "../../../../tsconfig.options.json",
"compilerOptions": {
"outDir": "lib",
"rootDir": "src"
},
"include": ["src/**/*"]
}
```
Simple right? But why do we need an additional configuration? Why not use the other `tsconfig.json`?
Great questions! The major reason is that we _only want to publish declarations for source files_,
and the declarations file structure should match 1:1 with the sources structure. The `tsconfig.json`
_does not_ guarantee this, as it may include test, config, or arbitrary files, all of which may not
exist in the sources directory (`src`), and will alter the output to an incorrect directory
structure. Our `tsconfig.build.json` solves this problem by only including source files, and by
forcing the source root to `src` using the `rootDir` compiler option.
However, there is a giant caveat with this approach! Because TypeScript utilizes Node.js's module
resolution, it will reference the declarations defined by the `package.json` `types` or
[`exports`](#supporting-packagejson-exports) fields, instead of the `outDir` compiler option, and
the other `tsconfig.json` _does not guarantee_ these files will exist. This results in TypeScript
failing to find the appropriate types! To solve this, add the `tsconfig.build.json` as a project
reference to `tsconfig.json`.
```json title="/tsconfig.json"
{
// ...
"references": [
{
"path": "./tsconfig.build.json"
}
// ... others
]
}
```
##### Vendor specific
Some vendors, like [Vite](../examples/vite), [Vitest](../examples/vite), and
[Astro](../examples/astro) may include additional `tsconfig.*.json` files unique to their ecosystem.
We suggest following their guidelines and implementation when applicable.
## Running the typechecker
Now that our configuration is place, we can run the typechecker, or attempt to at least! This can be
done with the `tsc --build` command, which acts as a
[build orchestrator](https://www.typescriptlang.org/docs/handbook/project-references.html#build-mode-for-typescript).
We also suggest passing `--verbose` for insights into what projects are compiling, and which are
out-of-date.
### On all projects
From the root of the repository, run `tsc --build --verbose` to typecheck _all_ projects, as defined
in [tsconfig.json](#tsconfigjson). TypeScript will generate a directed acyclic graph (DAG) and
compile projects _in order_ so that dependencies and references are resolved correctly.
:::info
Why run TypeScript in the root? Typically you would only want to run against projects, but for
situations where you need to verify that all projects still work, running in the root is the best
approach. Some such situations are upgrading TypeScript itself, upgrading global `@types` packages,
updating shared types, reworking build processes, and more.
:::
### On an individual project
To only typecheck a single project (and its dependencies), there are 2 approaches. The first is to
run from the root, and pass a relative path to the project, such as
`tsc --build --verbose packages/foo`. The second is to change the working directory to the project,
and run from there, such as `cd packages/foo && tsc --build --verbose`.
Both approaches are viable, and either may be used based on your tooling, build system, task runner,
so on and so forth. This is the approach moon suggests with its
[`typecheck` task](../examples/typescript).
### On affected projects
In CI environments, it's nice to _only run_ the typechecker on affected projects — projects that
have changed files. While this isn't entirely possible with `tsc`, it is possible with moon! Head
over to the
[official docs for more information](../../run-task#running-based-on-affected-files-only).
## Using `paths` aliases
Path aliases, also known as path mapping or magic imports, is the concept of defining an import
alias that re-maps its underlying location on the file system. In TypeScript, this is achieved with
the
[`paths` compiler option](https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping).
In a monorepo world, we suggest using path aliases on a per-project basis, instead of defining them
"globally" in the root. This gives projects full control of what's available and what they want to
import, and also plays nice with the mandatory `baseUrl` compiler option.
```json title="/tsconfig.json"
{
// ...
"compilerOptions": {
// ...
"baseUrl": ".",
"paths": {
// Within the project
":components/*": ["./src/components/*"],
// To a referenced project
":shared/*": ["../../shared/code/*"]
}
},
"references": [
{
"path": "../../shared/code"
}
]
}
```
The above aliases would be imported like the following:
```ts
// Before
// After
```
:::info
When using path aliases, we suggest prefixing or suffixing the alias with `:` so that it's apparent
that it's an alias (this also matches the new `node:` import syntax). Using no special character or
`@` is problematic as it risks a chance of collision with a public npm package and may accidentally
open your repository to a
[supply chain attack](https://snyk.io/blog/npm-security-preventing-supply-chain-attacks/). Other
characters like `~` and `$` have an existing meaning in the ecosystem, so it's best to avoid them
aswell.
:::
### Importing source files from local packages
If you are importing from a project reference using a `package.json` name, then TypeScript will
abide by Node.js module resolution logic, and will import using the
[`main`/`types` or `exports` entry points](https://nodejs.org/api/packages.html#package-entry-points).
This means that you're importing _compiled code_ instead of source code, and will require the
package to be constantly rebuilt if changes are made to it.
However, why not simply import source files instead? With path aliases, you can do just that, by
defining a `paths` alias that maps the `package.json` name to its source files, like so.
```json title="/tsconfig.json"
{
// ...
"compilerOptions": {
// ...
"paths": {
// Index import
"@scope/name": ["../../shared/package/src/index.ts"],
// Deep imports
"@scope/name/*": ["../../shared/package/src/*"]
}
},
"references": [
{
"path": "../../shared/package"
}
]
}
```
> When using moon, the
> [`typescript.syncProjectReferencesToPaths`](../../config/toolchain#syncprojectreferencestopaths)
> setting will automatically create `paths` based on the local references.
## Sharing and augmenting types
Declaring global types, augmenting node modules, and sharing reusable types is a common practice.
There are many ways to achieve this, so choose what works best for your repository. We use the
following pattern with great success.
At the root of the repository, create a `types` folder as a sibling to `tsconfig.json`. This folder
_must only_ contain declarations (`.d.ts`) files for the following reasons:
- Declarations can be `include`ed in a project without having to be a project reference.
- Hard-coded declarations _do not_ need to be compiled from TypeScript files.
Based on the above, update your project's `tsconfig.json` to include all of these types, or just
some of these types.
```json title="/tsconfig.json"
{
// ...
"include": ["src/**/*", "../../../../types/**/*"]
}
```
> In the future, moon will provide a setting to automate this workflow!
## Supporting `package.json` exports
In Node.js v12, they introduced a new field to `package.json` called `exports` that aims to solve
the shortcomings of the `main` field. The `exports` field is very complicated, and instead of
repeating all of its implementation details, we suggest reading
[the official Node.js docs on this topic](https://nodejs.org/api/packages.html#package-entry-points).
With that being said, TypeScript completely ignored the `exports` field until
[v4.7](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/#esm-nodejs), and
respecting `exports` is _still ignored unless_ the `moduleResolution` compiler option is set to
"nodenext", "node16", or "bundler". If `moduleResolution` is set to "node", then your integration is
resolving based on the `main` and `types` field, which are basically "legacy".
:::warning
Enabling `package.json` imports/exports resolution is very complicated, and may be very tedious,
especially considering the state of the npm ecosystem. Proceed with caution!
:::
### State of the npm ecosystem
As mentioned above, the npm ecosystem (as of November 2022) is in a very fragile state in regards to
imports/exports. Based on our experience attempting to utilize them in a monorepo, we ran into an
array of problems, some of which are:
- Published packages are simply utilizing imports/exports incorrectly. The semantics around CJS/ESM
are very strict, and they may be configured wrong. This is exacerbated by the new `type` field.
- The `exports` field _overrides_ the `main` and `types` fields. If `exports` exists without type
conditions, but the `types` field exists, the `types` entry point is completely ignored, resulting
in TypeScript failures.
With that being said, there are [ways around this](#resolving-issues) and moving forward is
possible, if you dare!
### Enabling imports/exports resolution
To start, set the `moduleResolution` compiler option to "nodenext" (for packages) or "bundler" (for
apps) in the [`tsconfig.options.json`](#tsconfigoptionsjson) file.
```json file="tsconfig.options.json"
{
"compilerOptions": {
// ...
"moduleResolution": "nodenext"
}
}
```
Next, [run the typechecker from the root](#on-all-projects) against all projects. This will help
uncover all potential issues with the dependencies you're using or the current configuration
architecture. If no errors are found, well _congratulations_, otherwise jump to the next section for
more information on [resolving them](#resolving-issues).
If you're trying to use `exports` in your own packages, ensure that the `types` condition is set,
and it's the first condition in the mapping! We also suggest including `main` and the top-level
`types` for tooling that do not support `exports` yet.
```json title="package.json"
{
// ...
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"exports": {
"./package.json": "./package.json",
".": {
"types": "./lib/index.d.ts",
"node": "./lib/index.js"
}
}
}
```
:::info
Managing `exports` is non-trivial. If you'd prefer them to be automatically generated based on a set
of inputs, we suggest using [Packemon](https://packemon.dev/)!
:::
### Resolving issues
There's only one way to resolve issues around incorrectly published `exports`, and that is package
patching, either with [Yarn's patching feature](https://yarnpkg.com/features/protocols/#patch),
[pnpm's patching feature](https://pnpm.io/cli/patch), or the
[`patch-package` package](https://www.npmjs.com/package/patch-package). With patching, you can:
- Inject the `types` condition/field if it's missing.
- Re-structure the `exports` mapping if it's incorrect.
- Fix incorrect entry point paths.
- And even fix invalid TypeScript declarations or JavaScript code!
```diff title="package.json"
{
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"exports": {
"./package.json": "./package.json",
- ".": "./lib/index.js"
+ ".": {
+ "types": "./lib/index.d.ts",
+ "node": "./lib/index.js"
+ }
}
}
```
:::info
More often than not, the owners of these packages may be unaware that their `exports` mapping is
incorrect. Why not be a good member of the community and report an issue or even submit a pull
request?
:::
## Editor integration
Unfortunately, we only have experience with VS Code. If you prefer another editor and have guidance
you'd like to share with the community, feel free to submit a pull request and we'll include it
below!
### VS Code
[VS Code](https://code.visualstudio.com/) has first-class support for TypeScript and project
references, and should "just work" without any configuration. You can verify this by restarting the
TypeScript server in VS Code (with the cmd + shift + p command palette) and navigating to
each project. Pay attention to the status bar at the bottom, as you'll see this:
When this status appears, it means that VS Code is _compiling a project_. It will re-appear multiple
times, basically for each project, instead of once for the entire repository.
Furthermore, ensure that VS Code is using the version of TypeScript from the `typescript` package in
`node_modules`. Relying on the version that ships with VS Code may result in unexpected TypeScript
failures.
```json title=".vscode/settings.json"
{
"typescript.tsdk": "node_modules/typescript/lib"
// Or "Select TypeScript version" from the command palette
}
```
## FAQ
### I still have questions, where can I ask them?
We'd love to answer your questions and help anyway that we can. Feel free to...
- Join the [moonrepo discord](https://discord.gg/qCh9MEynv2) and post your question in the
`#typescript` channel.
- Ping me, [Miles Johnson](https://twitter.com/mileswjohnson), on Twitter. I'll try my best to
respond to every tweet.
### Do I have to use project references?
Short answer, no. If you have less than say 10 projects, references may be overkill. If your
repository is primarily an application, but then has a handful of shared npm packages, references
may also be unnecessary here. In the end, it really depends on how many projects exist in the
monorepo, and what your team/company is comfortable with.
However, we do suggest using project references for very large monorepos (think 100s of projects),
or repositories with a large number of contributors, or if you merely want to reduce CI typechecking
times.
### What about not using project references and only using source files?
A popular alternative to project references is to simply use the source files as-is, by updating the
`main` and `types` entry fields within each `package.json` to point to the original TypeScript
files. This approach is also known as "internal packages".
```json title="package.json"
{
// ...
"main": "./src/index.tsx",
"types": "./src/index.tsx"
}
```
While this _works_, there are some downsides to this approach.
- Loading declaration files are much faster than source files.
- You'll lose all the benefits of TypeScript's incremental caching and compilation. TypeScript will
consistently load, parse, and evaluate these source files every time. This is especially true for
CI environments.
- When using `package.json` workspaces, bundlers and other tools may consider these source files
"external" as they're found in `node_modules`. This will require custom configuration to allow it.
- It breaks consistency. Consistency with the npm ecosystem, and consistency with how packaging and
TypeScript was designed to work. If all packages are internal, then great, but if you have some
packages that are published, you now have 2 distinct patterns for "using packages" instead of 1.
With that being said, theres a 3rd alternative that may be the best of both worlds, using project
references _and_ source files,
[by using `paths` aliases](#importing-source-files-from-local-packages).
All in all, this is a viable approach if you're comfortable with the downsides listed above. Use the
pattern that works best for your repository, team, or company!
### How to integrate with ESLint?
We initially included ESLint integration in this guide, but it was very complex and in-depth on its
own, so we've opted to push it to another guide. Unfortunately, that guide is not yet available, so
please come back soon! We'll announce when it's ready.
### How to handle circular references?
Project references _do **not** support
[circular references](https://github.com/microsoft/TypeScript/issues/33685)_ (cycles), which is
great, as they are a _code smell_! If you find yourself arbitrarily importing code from random
sources, or between 2 projects that depend on each other, then this highlights a problem with your
architecture. Projects should be encapsulated and isolated from outside sources, unless explicitly
allowed through a dependency. Dependencies are "upstream", so having them depend on the current
project (the "downstream"), makes little to no sense.
If you're trying to adopt project references and are unfortunately hitting the circular reference
problem, don't fret, untangling is possible, although non-trivial depending on the size of your
repository. It basically boils down to creating an additional project to move coupled code to.
For example, if project A was importing from project B, and B from A, then the solution would be to
create another project, C (typically a shared npm package), and move both pieces of code into C. A
and B would then import from C, instead of from each other. We're not aware of any tools that would
automate this, or detect cycles, so you'll need to do it manually.
---
## MCP integration
[Model Context Protocol](https://modelcontextprotocol.io) (MCP) is an open standard that enables AI
models to interact with external tools and services through a unified interface. The moon CLI
contains an MCP server that you can register with your code editor to allow LLMs to use moon
directly.
## Setup
### Claude Code
To use [MCP servers in Claude Code](https://docs.anthropic.com/en/docs/claude-code/mcp), run the
following command in your terminal:
```shell
claude mcp add moon -s project -e MOON_WORKSPACE_ROOT=/absolute/path/to/your/moon/workspace -- moon mcp
```
Or create an `.mcp.json` file in your project directory.
```json
{
"mcpServers": {
"moon": {
"command": "moon",
"args": ["mcp"],
"env": {
"MOON_WORKSPACE_ROOT": "/absolute/path/to/your/moon/workspace"
}
}
}
}
```
### Cursor
To use [MCP servers in Cursor](https://docs.cursor.com/context/model-context-protocol), create a
`.cursor/mcp.json` file in your project directory, or `~/.cursor/mcp.json` globally, with the
following content:
```json title=".cursor/mcp.json"
{
"mcpServers": {
"moon": {
"command": "moon",
"args": ["mcp"],
"env": {
"MOON_WORKSPACE_ROOT": "/absolute/path/to/your/moon/workspace"
}
}
}
}
```
Once configured, the moon MCP server should appear in the "Available Tools" section on the MCP
settings page in Cursor.
### VS Code
To use MCP servers in VS Code, you must have the
[Copilot Chat](https://code.visualstudio.com/docs/copilot/chat/copilot-chat) extension installed.
Once installed, create a `.vscode/mcp.json` file with the following content:
```json title=".vscode/mcp.json"
{
"servers": {
"moon": {
"type": "stdio",
"command": "moon",
"args": ["mcp"],
// >= 1.102 (June 2025)
"cwd": "${workspaceFolder}",
// Older versions
"env": {
"MOON_WORKSPACE_ROOT": "${workspaceFolder}"
}
}
}
}
```
Once your MCP server is configured, you can use it with
[GitHub Copilot’s agent mode](https://code.visualstudio.com/docs/copilot/chat/chat-agent-mode):
- Open the Copilot Chat view in VS Code
- Enable agent mode using the mode select dropdown
- Toggle on moon's MCP tools using the "Tools" button
### Zed
To use [MCP servers in Zed](https://zed.dev/docs/ai/mcp), create a `.zed/settings.json` file in your
project directory, or `~/.config/zed/settings.json` globally, with the following content:
```json title=".zed/settings.json"
{
"context_servers": {
"moon": {
"command": {
"path": "moon",
"args": ["mcp"],
"env": {
"MOON_WORKSPACE_ROOT": "/absolute/path/to/your/moon/workspace"
}
}
}
}
}
```
Once your MCP server is configured, you'll need to enable the tools using the following steps:
- Open the Agent panel in Zed
- Click the Write/Ask toggle button and go to "Configure Profiles"
- Click "Customize" in the Ask section
- Click "Configure MCP Tools"
- Enable each tool under the "moon" section
## Available tools
The following tools are available in the moon MCP server and can be executed by LLMs using agent
mode.
- `get_project` - Get a project and its tasks by `id`.
- `get_projects` - Get all projects.
- `get_task` - Get a task by `target`.
- `get_tasks` - Get all tasks.
- `get_template` - Get a template and its merged variable schema by `id`, with the `extends` chain
resolved.
- `get_templates` - Get all templates, with an optional case-insensitive filter regex (mirrors
`moon templates --filter`).
- `get_changed_files` - Gets changed files between base and head revisions.
- `sync_projects` - Runs the `SyncProject` action for one or many projects by `id`.
- `sync_workspace` - Runs the `SyncWorkspace` action.
:::info
The
[request and response shapes](https://github.com/moonrepo/moon/blob/master/packages/types/src/mcp.ts)
for these tools are defined as TypeScript types in the
[`@moonrepo/types`](https://www.npmjs.com/package/@moonrepo/types) package.
:::
---
## Terminal notifications
moon is able to send operating system desktop notifications for specific events in the action
pipeline, on behalf of your terminal application. This is useful for continuous feedback loops and
reacting to long-running commands while multi-tasking.
Notifications are opt-in and must be enabled with the
[`notify.terminalNotifications`](/docs/config/workspace#terminalnotifications) setting.
```yaml title=".moon/workspace.yml"
notifier:
terminalNotifications: 'always'
```
## Setup
Notifications must be enabled at the operating system level.
### Linux
Linux support is based on the [XDG specification](https://en.wikipedia.org/wiki/XDG) and utilizes
D-BUS APIs, primarily the
[`org.freedesktop.Notifications.Notify`](https://www.galago-project.org/specs/notification/0.9/x408.html#command-notify)
method. Refer to your desktop distribution for more information.
Notifications will be sent using the `moon` application name (the current executable).
### macOS
- Open "System Settings" or "System Preferences"
- Select "Notifications" in the left sidebar
- Select your terminal application from the list (e.g., "Terminal", "iTerm", etc)
- Ensure "Allow notifications" is enabled
- Customize the other settings as desired
Notifications will be sent from your currently running terminal application, derived from the
`TERM_PROGRAM` environment variable. If we fail to detect the terminal, it will default to "Finder".
### Windows
Requires Windows 10 or later.
- Open "Settings"
- Go to the "System" panel
- Select "Notifications & Actions" in the left sidebar
- Ensure notifications are enabled
Notifications will be sent from the "Windows Terminal" app if it's currently in use, otherwise from
"Microsoft PowerShell".
---
## Offline mode
moon assumes that an internet connection is always available, as we download and install tools into
the toolchain, resolve versions against upstream manifests, and automatically install dependencies.
While this is useful, having a constant internet connection isn't always viable.
To support workflows where internet isn't available or is spotty, moon will automatically check for
an active internet connection, and drop into offline mode if necessary.
## What's disabled when offline
When offline, moon will skip or disable the following:
- Automatic dependency installation will be skipped.
- Toolchain will skip resolving, downloading, and installing tools, and instead use the local cache.
- If no local cache available, will fallback to binaries found on `PATH`.
- If not available on `PATH`, will fail to run.
- Upgrade and version checks will be skipped.
## Toggling modes
While we automatically check for an internet connection, both online and offline modes can be forced
with the `PROTO_OFFLINE` environment variable. Setting the variable to `1` or `true` will force
offline mode, while `0` and `false` will force online mode.
## Environment variables
Some additional variables to interact with offline checks.
- `PROTO_OFFLINE_TIMEOUT` - Customize the timeout for offline checks (in milliseconds). Defaults to
`750`.
- `PROTO_OFFLINE_HOSTS` - Customize additional hosts/IPs to check for offline status. Separate
multiple hosts with a `,`.
- `PROTO_OFFLINE_IP_VERSION` - Customize which IP version to support, `4` or `6`. If not defined,
supports both.
---
## Open source usage
Although moon was designed for large monorepos, it can also be used for open source projects,
especially when coupled with our [built-in continuous integration support](./ci).
However, a pain point with moon is that it has an explicitly configured version for each tool in the
[toolchain](../concepts/toolchain), but open source projects typically need to run checks against
multiple versions! To mitigate this problem, you can set the matrix value as an environment
variable, in the format of `MOON__VERSION`.
```yaml title=".github/workflows/ci.yml"
name: 'Pipeline'
on:
push:
branches:
- 'master'
pull_request:
jobs:
ci:
name: 'CI'
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: ['ubuntu-latest', 'windows-latest']
node-version: [20, 22, 24]
steps:
# Checkout repository
- uses: 'actions/checkout@v4'
with:
fetch-depth: 0
# Install Node.js
- uses: 'actions/setup-node@v6'
# Install dependencies
- run: 'yarn install --immutable'
# Run moon and affected tasks
- run: 'yarn moon ci'
env:
MOON_NODE_VERSION: ${{ matrix.node-version }}
```
:::info
This example is only for GitHub actions, but the same mechanism can be applied to other CI
environments.
:::
## Reporting run results
We also suggest using our
[`moonrepo/run-report-action`](https://github.com/marketplace/actions/moon-ci-run-reports) GitHub
action. This action will report the results of a [`moon ci`](../commands/ci) run to a pull request
as a comment and workflow summary.
For the generated report file locations and caching caveats, refer to the
[`moon ci` reports documentation](../commands/ci#reports).
```yaml title=".github/workflows/ci.yml"
# ...
jobs:
ci:
name: 'CI'
runs-on: 'ubuntu-latest'
steps:
# ...
- run: 'yarn moon ci'
- uses: 'moonrepo/run-report-action@v1'
if: success() || failure()
with:
access-token: ${{ secrets.GITHUB_TOKEN }}
```
The report looks something like the following:
---
## Task profiling
Troubleshooting slow or unperformant tasks? Profile and diagnose them with ease!
:::caution
Profiling is only supported by `node` based tasks, and is not supported by tasks that are created
through `package.json` inference, or for packages that ship non-JavaScript code (like Rust or Go).
:::
## CPU snapshots
CPU profiling helps you get a better understanding of which parts of your code require the most CPU
time, and how your code is executed and optimized by Node.js. The profiler will measure code
execution and activities performed by the engine itself, such as compilation, calls of system
libraries, optimization, and garbage collection.
### Record a profile
To record a CPU profile, pass `--profile cpu` to the [`moon run`](../commands/run) command. When
successful, the profile will be written to
`.moon/cache/states///snapshot.cpuprofile`.
```shell
$ moon run --profile cpu app:lint
```
### Analyze in Chrome
CPU profiles can be reviewed and analyzed with
[Chrome developer tools](https://developer.chrome.com/docs/devtools/) using the following steps.
1. Open Chrome and navigate to `chrome://inspect`.
2. Under "Devices", navigate to "Open dedicated DevTools for Node".
3. The following window will popup. Ensure the "Profiler" tab is selected.
4. Click "Load" and select the `snapshot.cpuprofile` that was
[previously recorded](#record-a-profile). If successful, the snapshot will appear in the left
column.
> On macOS, press `command` + `shift` + `.` to display hidden files and folders, to locate the
> `.moon` folder.
5. Select the snapshot in the left column. From here, the snapshot can be analyzed and represented
with [Bottom up](#bottom-up), [Top down](#top-down), or [Flame chart](#flame-chart) views.
## Heap snapshots
Heap profiling lets you detect memory leaks, dynamic memory problems, and locate the fragments of
code that caused them.
### Record a profile
To record a heap profile, pass `--profile heap` to the [`moon run`](../commands/run) command. When
successful, the profile will be written to
`.moon/cache/states///snapshot.heapprofile`.
```shell
$ moon run --profile heap app:lint
```
### Analyze in Chrome
Heap profiles can be reviewed and analyzed with
[Chrome developer tools](https://developer.chrome.com/docs/devtools/) using the following steps.
1. Open Chrome and navigate to `chrome://inspect`.
2. Under "Devices", navigate to "Open dedicated DevTools for Node".
3. The following window will popup. Ensure the "Memory" tab is selected.
4. Click "Load" and select the `snapshot.heapprofile` that was
[previously recorded](#record-a-profile-1). If successful, the snapshot will appear in the left
column.
> On macOS, press `command` + `shift` + `.` to display hidden files and folders, to locate the
> `.moon` folder.
5. Select the snapshot in the left column. From here, the snapshot can be analyzed and represented
with [Bottom up](#bottom-up), [Top down](#top-down), or [Flame chart](#flame-chart) views.
## Views
Chrome DevTools provide 3 views for analyzing activities within a snapshot. Each view gives you a
different perspective on these activities.
### Bottom up
The Bottom up view is helpful if you encounter a heavy function and want to find out where it was
called from.
- The "Self Time" column represents the aggregated time spent directly in that activity, across all
of its occurrences.
- The "Total Time" column represents aggregated time spent in that activity or any of its children.
- The "Function" column is the function that was executed, including source location, and any
children.
### Top down
The Top down view works in a similar fashion to [Bottom up](#bottom-up), but displays functions
starting from the top-level entry points. These are also known as root activities.
### Flame chart
DevTools represents main thread activity with a flame chart. The x-axis represents the recording
over time. The y-axis represents the call stack. The events on top cause the events below it.
---
## Remote caching
Is your CI pipeline running slower than usual? Are you tired of running the same build over and over
although nothing has changed? Do you wish to reuse the same local cache across other machines and
environments? These are just a few scenarios that remote caching aims to solve.
Remote caching is a system that shares artifacts to improve performance, reduce unnecessary
computation time, and alleviate resources. It achieves this by uploading hashed artifacts to a cloud
storage provider, like AWS S3 or Google Cloud, and downloading them on demand when a build matches a
derived hash.
To make use of remote caching, we provide 2 solutions.
## Self-hosted
This solution allows you to host any remote caching service that is compatible with the
[Bazel Remote Execution v2 API](https://github.com/bazelbuild/remote-apis/tree/main/build/bazel/remote/execution/v2),
such as [`bazel-remote`](https://github.com/buchgr/bazel-remote). When using this solution, the
following RE API features must be enabled:
- Action result caching
- Content addressable storage caching
- SHA256 digest hashing
- gRPC requests
### Host your service
When you have chosen (or built) a compatible service, host it and make it available through gRPC or
HTTPS. For example, if you plan to use `bazel-remote`, you can do something like the following:
```bash
bazel-remote --dir /path/to/moon-cache --max_size 10 --storage_mode uncompressed --grpc_address 0.0.0.0:9092
```
If you've configured the [`remote.cache.compression`](../config/workspace#compression) setting to
"zstd", you'll need to run the binary with that storage mode as well.
```bash
bazel-remote --dir /path/to/moon-cache --max_size 10 --storage_mode zstd --grpc_address 0.0.0.0:9092
```
:::info
View the official [`bazel-remote`](https://github.com/buchgr/bazel-remote#usage) documentation for
all the available options, like storing artifacts in S3, configuring authentication (TLS/mTLS),
proxies, and more.
:::
### Configure remote caching
Once your service is running, you can enable remote caching by configuring the
[`remote`](../config/workspace#remote) settings in [`.moon/workspace.*`](../config/workspace). At
minimum, the only setting that is required is `host`.
```yaml title=".moon/workspace.yml"
# gRPC
remote:
host: 'grpc://your-host.com:9092'
# HTTPS
remote:
api: 'http'
host: 'https://your-host.com:8080'
```
:::info
Remote caching can be conditionally enabled by setting the `MOON_REMOTE_HOST` environment variable.
:::
#### TLS and mTLS
We have rudimentary support for TLS and mTLS, but it's very unstable, and has not been thoroughly
tested. There's also [many](https://github.com/hyperium/tonic/issues/1652)
[many](https://github.com/hyperium/tonic/issues/1989)
[issues](https://github.com/hyperium/tonic/issues/1033) around authentication in Tonic.
```yaml title=".moon/workspace.yml"
# TLS
remote:
host: 'grpcs://your-host.com:9092'
tls:
cert: 'certs/ca.pem'
domain: 'your-host.com'
# mTLS
remote:
host: 'grpcs://your-host.com:9092'
mtls:
caCert: 'certs/ca.pem'
clientCert: 'certs/client.pem'
clientKey: 'certs/client.key'
domain: 'your-host.com'
```
## Cloud-hosted
### Depot
If you'd prefer not to host your own solution, you could use
[Depot Cache](https://depot.dev/products/cache), a cloud-based caching solution. To make use of
Depot, follow these steps:
- Create an account on [depot.dev](https://depot.dev)
- Create an organization
- Go to organization settings -> API tokens
- Create a new API token
- Add the token as a `DEPOT_TOKEN` environment variable to your moon pipelines
Once these steps have been completed, you can enable remote caching in moon with the following
configuration. If your Depot account has more than 1 organization, you'll need to set the
`X-Depot-Org` header.
```yaml title=".moon/workspace.yml"
remote:
host: 'grpcs://cache.depot.dev'
auth:
token: 'DEPOT_TOKEN'
headers:
'X-Depot-Org': ''
```
## FAQ
#### What is an artifact?
In the context of moon and remote caching, an artifact is the
[outputs of a task](../config/project#outputs), as well as the stdout and stderr of the task that
generated the outputs. Artifacts are uniquely identified by the
[moon generated hash](../concepts/cache#hashing).
#### Do I have to use remote caching?
No, remote caching is _optional_. It's intended purpose is to store long lived build artifacts to
speed up CI pipelines, and optionally local development. For the most part,
[`moon ci`](../commands/ci) does a great job of only running what's affected in pull requests, and
is a great starting point.
#### Does remote caching store source code?
No, remote caching _does not_ store source code. It stores the
[outputs of a task](../config/project#outputs), which is typically built and compiled code. To
verify this, you can inspect the tar archives in `.moon/cache/outputs`.
#### Does moon collect any personally identifiable information?
No, moon does not collect any PII as part of the remote caching process.
#### Are artifacts encrypted?
We do not encrypt on moon's side, as encryption is provided by your cloud storage provider.
---
## Renovate
[Renovate](https://docs.renovatebot.com/) automates dependency updates by opening pull requests when
new versions are released. Because moon builds on [proto](../proto) for toolchain management, and
proto is supported by Renovate out of the box, most moon repositories work with Renovate with little
to no configuration. This guide covers that native support, and how to extend it to versions pinned
in moon's own configuration files.
## Pin versions in `.prototools` (recommended)
The cleanest setup requires **no Renovate configuration and no annotations at all**. Renovate ships a
built-in [`proto` manager](https://docs.renovatebot.com/modules/manager/proto/) that updates tool
versions declared in [`.prototools`](../proto/config) files automatically.
```toml title=".prototools"
node = "22.18.0"
yarn = "4.16.0"
rust = "1.85.0"
moon = "2.4.2"
```
Because moon toolchains inherit their version from `.prototools` by default (via
[`versionFromPrototools`](../config/toolchain#versionfromprototools)), you can leave the `version`
out of [`.moon/toolchains.yml`](../config/toolchain) entirely and let proto be the single source of
truth:
```yaml title=".moon/toolchains.yml"
# No version here — inherited from .prototools
node: {}
rust: {}
```
With this approach, Renovate keeps `.prototools` current, moon resolves toolchains from it, and
derived fields (like `package.json`'s `packageManager`) stay in sync — no `# renovate:` comments
required.
:::tip
If you can, prefer this over hard-coding a `version` in `.moon/toolchains.yml`. It's the least
configuration, and nothing drifts out of sync.
:::
## Versions in moon configuration
If you'd rather pin versions in moon's config files — or you have versions that don't live in
`.prototools` at all (the moon [`versionConstraint`](../config/workspace#versionconstraint), a CI
input, a WASM plugin) — moon publishes a shared Renovate
[config preset](https://docs.renovatebot.com/config-presets/) to cover them.
```json title="renovate.json"
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended", "github>moonrepo/moon//ecosystem/renovate"]
}
```
If you'd rather not depend on the preset, copy its
[custom managers](https://docs.renovatebot.com/modules/manager/custom/) directly into your
configuration — see the source at
[`ecosystem/renovate.json`](https://github.com/moonrepo/moon/blob/master/ecosystem/renovate.json).
### Toolchain versions (no annotations)
The preset includes [JSONata managers](https://docs.renovatebot.com/modules/manager/jsonata/) that
read your [`toolchains`](../config/toolchain) config _by structure_, so a `version` pinned for a
built-in toolchain is updated automatically — **no comments needed**. This works for the `yml`,
`yaml`, `json`, and `toml` formats (in `.moon/` or `.config/moon/`):
```yaml title=".moon/toolchains.yml"
node:
version: '22.18.0'
rust:
version: '1.85.0'
```
### Other versions
For anything the structural managers don't cover, the preset also includes a regex manager that reads
a trailing `# renovate:` (or `// renovate:`) comment. Add it to the _end of the line_ containing the
version, and point it at a [datasource](https://docs.renovatebot.com/modules/datasource/) and package.
It applies to `.moon/` (and `.config/moon/`) configs, `moon.*` project files, and
`.github/workflows/*.yml`, in any comment-supporting format (`yml`, `yaml`, `toml`, `jsonc`, `pkl`,
`hcl` — strict `json` has no comments):
```yaml title=".moon/workspace.yml"
# Pin the version of moon itself
versionConstraint: '>=2.4.2' # renovate: datasource=github-releases depName=moonrepo/moon extractVersion=^v(?.*)$
```
```yaml title=".github/workflows/ci.yml"
- uses: 'moonrepo/setup-toolchain@v0'
with:
proto-version: '0.58.2' # renovate: datasource=github-releases depName=moonrepo/proto extractVersion=^v(?.*)$
```
In formats that use `//` for comments (JSONC, Pkl, HCL), the `// renovate:` form works the same way:
```json title=".moon/workspace.jsonc"
{
"versionConstraint": ">=2.4.2" // renovate: datasource=github-releases depName=moonrepo/moon extractVersion=^v(?.*)$
}
```
To annotate versions in _other_ files — such as WASM plugin versions in `.prototools` — append their
paths to `managerFilePatterns` in your own `customManagers` entry.
### Datasource reference
Useful when annotating a version, or adding a toolchain the structural manager doesn't cover. These
mirror the datasources Renovate's native `proto` manager uses.
| Tool | `datasource` | `depName` / `packageName` | `extractVersion` |
| ---------- | ----------------- | ------------------------- | ----------------------- |
| node | `node-version` | `node` | |
| npm / pnpm | `npm` | `npm` / `pnpm` | |
| yarn | `npm` | `@yarnpkg/cli` | |
| rust | `github-tags` | `rust-lang/rust` | |
| go | `github-tags` | `golang/go` | `^go(?.*)$` |
| python | `github-tags` | `python/cpython` | `^v(?.*)$` |
| deno | `github-releases` | `denoland/deno` | `^v(?.*)$` |
| bun | `github-releases` | `oven-sh/bun` | `^bun-v(?.*)$` |
| moon | `github-releases` | `moonrepo/moon` | `^v(?.*)$` |
| proto | `github-releases` | `moonrepo/proto` | `^v(?.*)$` |
## Tips and limitations
- **Config format coverage.** Comment-free structural updates work for the `yml`, `yaml`, `json`, and
`toml` formats; `jsonc`, `pkl`, and `hcl` are updated through `#` or `//` annotations. Strict `json`
can't hold comments, so pin those toolchains structurally or in `.prototools`.
- **Aliases are skipped.** Values like `latest`, `stable`, `canary`, or `nightly` can't be resolved
to a concrete version, and are ignored.
- **Pin each toolchain in one place.** Don't set the same version in both `.prototools` and
`.moon/toolchains.yml` (or add an annotation on top of a structurally-managed version) — you'll get
duplicate pull requests.
- **Propagate changes on self-hosted Renovate.** You can run `moon sync` (or any command) after an
update via [`postUpgradeTasks`](https://docs.renovatebot.com/configuration-options/#postupgradetasks)
to keep generated files in sync.\
to batch toolchain bumps into a single pull request.
---
## Root-level project
Coming from other repositories or task runner, you may be familiar with tasks available at the
repository root, in which one-off, organization, maintenance, or process oriented tasks can be ran.
moon supports this through a concept known as a root-level project.
Begin by adding the root to [`projects`](../config/workspace#projects) with a source value of `.`
(current directory relative from the workspace).
```yaml title=".moon/workspace.yml"
# As a map
projects:
root: '.'
# As a list of globs
projects:
- '.'
```
> When using globs, the root project's name will be inferred from the repository folder name. Be
> wary of this as it can change based on what a developer has checked out as.
Once added, create a [`moon.*`](../config/project) in the root of the repository. From here you can
define tasks that can be ran using this new root-level project name, for example,
`moon run root:`.
```yaml title="moon.yml"
tasks:
versionCheck:
command: 'yarn version check'
inputs: []
options:
cache: false
```
And that's it, but there are a few caveats to be aware of...
## Caveats
### Greedy inputs
:::warning
In moon v1.24, root-level tasks default to no inputs. In previous versions, inputs defaulted to
`**/*`. This section is only applicable for older moon versions!
:::
Task [`inputs`](../config/project#inputs) default to `**/*`, which would result in root-level tasks
scanning _all_ files in the repository. This will be a very expensive operation! We suggest
restricting inputs to a very succinct whitelist, or disabling inputs entirely.
```yaml title="moon.yml"
tasks:
oneOff:
# ...
inputs: []
```
### Inherited tasks
Because a root project is still a project in the workspace, it will inherit all tasks defined in
[`.moon/tasks/**/*`](../config/tasks), which may be unexpected. To mitigate this, you can exclude
some or all of these tasks in the root config with
[`workspace.inheritedTasks`](../config/project#inheritedtasks).
```yaml title="moon.yml"
workspace:
inheritedTasks:
include: []
```
---
## Rust handbook
Utilizing Rust in a monorepo is a trivial task, thanks to Cargo, and also moon. With this handbook,
we'll help guide you through this process.
:::info
moon is not a build system and does _not_ replace Cargo. Instead, moon runs `cargo` commands, and
efficiently orchestrates those tasks within the workspace.
:::
## moon setup
For this part of the handbook, we'll be focusing on [moon](/moon), our task runner. To start,
languages in moon act like plugins, where their functionality and support _is not_ enabled unless
explicitly configured. We follow this approach to avoid unnecessary overhead.
### Enabling the language
To enable Rust, define the [`rust`](../../config/toolchain#rust) setting in
[`.moon/toolchains.*`](../../config/toolchain), even if an empty object.
```yaml title=".moon/toolchains.yml"
# Enable Rust
rust: {}
# Enable Rust and override default settings
rust:
syncToolchainConfig: true
```
Or by pinning a `rust` version in [`.prototools`](../../proto/config) in the workspace root.
```toml title=".prototools"
rust = "1.93.0"
```
This will enable the Rust toolchain and provide the following automations around its ecosystem:
- Manifests and lockfiles are parsed for accurate dependency versions for hashing purposes.
- Cargo binaries (in `~/.cargo/bin`) are properly located and executed.
- Automatically sync `rust-toolchain.toml` configuration files.
- For non-workspaces, will inherit `package.name` from `Cargo.toml` as a project alias.
- And more to come!
### Utilizing the toolchain
When a language is enabled, moon by default will assume that the language's binary is available
within the current environment (typically on `PATH`). This has the downside of requiring all
developers and machines to manually install the correct version of the language, _and to stay in
sync_.
Instead, you can utilize [moon's toolchain](../../concepts/toolchain), which will download and
install the language in the background, and ensure every task is executed using the exact version
across all machines.
Enabling the toolchain is as simple as defining the
[`rust.version`](../../config/toolchain#version-2) setting.
```yaml title=".moon/toolchains.yml"
# Enable Rust toolchain with an explicit version
rust:
version: '1.69.0'
```
> Versions can also be defined with [`.prototools`](../../proto/config).
:::caution
moon requires `rustup` to exist in the environment, and will use this to install the necessary Rust
toolchains. moon will attempt to auto-install `rustup` if it's not found, but this may fail in some
environments.
:::
## Repository structure
Rust/Cargo repositories come in two flavors: a single crate with one `Cargo.toml`, or multiple
crates with many `Cargo.toml`s using
[Cargo workspaces](https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html). The latter is
highly preferred as it enables Cargo incremental caching.
With moon, you can place [`moon.*`](../../config/project) in each crate, or once relative to
`Cargo.lock`. The choice is yours! But be aware, if you do per-crate, Cargo itself will "wait for
build lock" when multiple processes are running.
An example of this layout is demonstrated below:
```
/
├── .moon/
├── crates/
│ ├── client/
| │ ├── ...
│ │ └── Cargo.toml
│ ├── server/
| │ ├── ...
│ │ └── Cargo.toml
│ └── utils/
| ├── ...
│ └── Cargo.toml
├── target/
├── Cargo.lock
├── Cargo.toml
└── moon.yml
```
```
/
├── .moon/
├── crates/
│ ├── client/
| │ ├── ...
| │ ├── moon.yml
│ │ └── Cargo.toml
│ ├── server/
| │ ├── ...
| │ ├── moon.yml
│ │ └── Cargo.toml
│ └── utils/
| ├── ...
| │ ├── moon.yml
│ └── Cargo.toml
├── target/
├── Cargo.lock
└── Cargo.toml
```
```
/
├── .moon/
├── src/
│ └── lib.rs
├── tests/
│ └── ...
├── target/
├── Cargo.lock
├── Cargo.toml
└── moon.yml
```
### Example `moon.*`
The following configuration represents a base that covers most Rust projects.
```yaml title="/moon.yml"
language: 'rust'
layer: 'application'
env:
CARGO_TERM_COLOR: 'always'
fileGroups:
sources:
- 'crates/*/src/**/*'
- 'crates/*/Cargo.toml'
- 'Cargo.toml'
tests:
- 'crates/*/benches/**/*'
- 'crates/*/tests/**/*'
tasks:
build:
command: 'cargo build'
inputs:
- '@globs(sources)'
check:
command: 'cargo check --workspace'
inputs:
- '@globs(sources)'
format:
command: 'cargo fmt --all --check'
inputs:
- '@globs(sources)'
- '@globs(tests)'
lint:
command: 'cargo clippy --workspace'
inputs:
- '@globs(sources)'
- '@globs(tests)'
test:
command: 'cargo test --workspace'
inputs:
- '@globs(sources)'
- '@globs(tests)'
```
```yaml title="/moon.yml"
language: 'rust'
layer: 'application'
env:
CARGO_TERM_COLOR: 'always'
fileGroups:
sources:
- 'src/**/*'
- 'Cargo.toml'
tests:
- 'benches/**/*'
- 'tests/**/*'
tasks:
build:
command: 'cargo build'
inputs:
- '@globs(sources)'
check:
command: 'cargo check'
inputs:
- '@globs(sources)'
format:
command: 'cargo fmt --check'
inputs:
- '@globs(sources)'
- '@globs(tests)'
lint:
command: 'cargo clippy'
inputs:
- '@globs(sources)'
- '@globs(tests)'
test:
command: 'cargo test'
inputs:
- '@globs(sources)'
- '@globs(tests)'
```
## Cargo integration
You can't use Rust without Cargo -- well you could but why would you do that? With moon, we're doing
our best to integrate with Cargo as much as possible. Here's a few of the benefits we currently
provide.
### Global binaries
Cargo supports global binaries through the
[`cargo install`](https://doc.rust-lang.org/cargo/commands/cargo-install.html) command, which
installs a crate to `~/.cargo/bin`, or makes it available through the `cargo ` command. These
are extremely beneficial for development, but they do require every developer to manually install
the crate (and appropriate version) to their machine.
With moon, this is no longer an issue with the [`rust.bins`](../../config/toolchain#bins) setting.
This setting requires a list of crates (with optional versions) to install, and moon will install
them as part of the task runner install dependencies action. Furthermore, binaries will be installed
with [`cargo-binstall`](https://crates.io/crates/cargo-binstall) in an effort to reduce build and
compilation times.
```yaml title=".moon/toolchains.yml" {2-4}
rust:
bins:
- 'cargo-make@0.35.0'
- 'cargo-nextest'
```
At this point, tasks can be configured to run this binary as a command. The `cargo` prefix is
optional, as we'll inject it when necessary.
```yaml title="/moon.yml"
tasks:
test:
command: 'nextest run --workspace'
toolchain: 'rust'
```
:::tip
The `cargo-binstall` crate may require a `GITHUB_TOKEN` environment variable to make GitHub Releases
API requests, especially in CI. If you're being rate limited, or fail to find a download, try
creating a token with necessary permissions.
:::
### Lockfile handling
To expand our integration even further, we also take `Cargo.lock` into account, and apply the
following automations when a target is being ran:
- If the lockfile does not exist, we generate one with
[`cargo generate-lockfile`](https://doc.rust-lang.org/cargo/commands/cargo-generate-lockfile.html).
- We parse and extract the resolved checksums and versions for more accurate hashing.
## FAQ
### Should we cache the `target` directory as an output?
No, definitely not! Both moon and Cargo support incremental caching, but they're not entirely
compatible, and will most likely cause problems when used together.
The biggest factor is that moon's caching and hydration uses a tarball strategy, where each task
would unpack a tarball on cache hit, and archive a tarball on cache miss. The Cargo target directory
is extremely large (moon's is around 50gb), and coupling this with our tarball strategy is not
viable. This would cause massive performance degradation.
However, at maximum, you _could_ cache the compiled binary itself as an output, instead of the
entire target directory. Example:
```yaml title="moon.yml"
tasks:
build:
command: 'cargo build --release'
outputs: ['target/release/moon']
```
### How can we improve CI times?
Rust is known for slow build times and CI is no exception. With that being said, there are a few
patterns to help alleviate this, both on the moon side and outside of it.
To start, you can cache Rust builds in CI. This is a non-moon solution to the `target` directory
problem above.
1. If you use GitHub Actions, feel free to use our
[moonrepo/setup-rust](https://github.com/moonrepo/setup-rust) action, which has built-in caching.
2. A more integrated solution is [sccache](https://crates.io/crates/sccache), which stores build
artifacts in a cloud storage provider.
---
## Sharing workspace configuration
For large companies, open source maintainers, and those that love reusability, more often than not
you'll want to use the same configuration across all repositories for consistency. This helps reduce
the maintenance burden while ensuring a similar developer experience.
To help streamline this process, moon provides an `extends` setting in both
[`.moon/workspace.*`](../config/workspace#extends),
[`.moon/toolchains.*`](../config/toolchain#extends),
[`.moon/extensions.*`](../config/extensions#extends), and
[`.moon/tasks/**/*`](../config/tasks#extends). This setting requires a HTTPS URL _or_ relative file
system path that points to a valid YAML document for the configuration in question.
A great way to share configuration is by using GitHub's "raw file view", as demonstrated below using
our very own [examples repository](https://github.com/moonrepo/examples).
```yaml title=".moon/tasks/all.yml"
extends: 'https://raw.githubusercontent.com/moonrepo/examples/master/.moon/tasks/all.yml'
```
## Versioning
Inheriting an upstream configuration can be dangerous, as the settings may change at any point,
resulting in broken builds. To mitigate this, you can used a "versioned" upstream configuration,
which is ideally a fixed point in time. How this is implemented is up to you or your company, but we
suggest the following patterns:
### Using versioned filenames
A rudimentary solution is to append a version to the upstream filename. When the file is modified, a
new version should be created, while the previous version remains untouched.
```diff
-extends: '../shared/project.yml'
+extends: '../shared/project-v1.yml'
```
### Using branches, tags, or commits
When using a version control platform, like GitHub above, you can reference the upstream
configuration through a branch, tag, commit, or sha. Since these are a reference point in time, they
are relatively safe.
```diff
-extends: 'https://raw.githubusercontent.com/moonrepo/examples/master/.moon/tasks/all.yml'
+extends: 'https://raw.githubusercontent.com/moonrepo/examples/c3f10160bcd16b48b8d4d21b208bb50f6b09bd96/.moon/tasks/all.yml'
```
---
## VCS hooks
VCS hooks (most popular with [Git](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks)) are a
mechanism for running scripts at pre-defined phases in the VCS's lifecycle, most commonly
pre-commit, pre-push, or pre-merge. With moon, we provide a built-in solution for managing hooks,
and syncing them across developers and machines.
- [Learn more about Git hooks](https://git-scm.com/docs/githooks)
## Defining hooks
Hooks can be configured with the [`vcs.hooks`](../config/workspace#hooks) setting in
[`.moon/workspace.*`](../config/workspace). This setting requires a map of hook names (in the format
required by your VCS), to a list of arbitrary commands to run within the hook script. Commands are
used as-is and are not formatted or interpolated in any way.
To demonstrate this, let's configure a `pre-commit` hook that runs a moon `lint` task for affected
projects, and also verifies that the commit message abides by a specified format (using
[pre-commit](https://pre-commit.com/) and the
[commitlint hook](https://github.com/alessandrojcm/commitlint-pre-commit-hook), for example).
```yaml title=".moon/workspace.yml"
vcs:
hooks:
pre-commit:
- 'pre-commit run'
- 'moon run :lint --affected'
commit-msg:
- 'pre-commit run --hook-stage commit-msg --commit-msg-filename $ARG1'
```
:::info
All commands are executed from the repository root (not moon's workspace root) and must exist on
`PATH`. If `moon` is installed locally, you can execute it using a repository relative path, like
`./node_modules/@moonrepo/cli/moon`.
:::
### Accessing arguments
To ease interoperability between operating systems and terminal shells, we set passed arguments as
environment variables.
In your hook commands, you can access these arguments using the `$ARG` format, where `` is the
1-indexed position of the argument. For example, to access the first argument, you would use
`$ARG1`, the second argument would be `$ARG2`, and so on. `$ARG0` exists and points to the current
script.
## Enabling hooks
Hooks are a divisive subject, as some developers love them, and others hate them. Finding a viable
solution for everyone can be difficult, so with moon, we opted to support 2 distinct options, but
only 1 can be used at a time. Choose the option that works best for your project, team, or company!
### Automatically for everyone
If you'd like hooks to be enforced for every contributor of the repository, then simply enable the
[`vcs.sync`](../config/workspace#sync) setting in [`.moon/workspace.*`](../config/workspace). This
will automatically generate hook scripts and link them with the local VCS checkout, everytime a
[task](../concepts/target) is ran.
```yaml title=".moon/workspace.yml"
vcs:
hooks: [...]
sync: true
```
:::caution
Automatically activating hooks on everyone's computer is considered a sensitive action, because it
enables the execution of arbitrary code on the computers of the team members. Be careful about the
hook commands you define in the [`.moon/workspace.*`](../config/workspace) file.
:::
### Manually by each developer
If you'd prefer contributors to have a choice in whether or not they want to use hooks, then simply
do nothing, and guide them to run the [`moon sync hooks`](../commands/sync/vcs-hooks) command. This
command will generate hook scripts and link them with the local VCS checkout.
```shell
$ moon sync hooks
```
## Disabling hooks
If you choose to stop using hooks, you'll need to cleanup the previously generated hook scripts, and
reset the VCS checkout. To start, disable the `vcs.sync` setting.
```yaml title=".moon/workspace.yml"
vcs:
sync: false
```
And then run the following command, which will delete files from your local filesystem. Every
developer that is using hooks will need to run this command.
```shell
$ moon sync hooks --clean
```
## How it works
When hooks are [enabled](#enabling-hooks), the following processes will take place.
1. The configured [hooks](#defining-hooks) will be generated as individual script files in the
`.moon/hooks` directory. Whether or not you commit or ignore these script files is your choice.
They are written to the `.moon` directory so that they can be reviewed, audited, and easily
tested, but _are required_. If your workspace configuration lives in `.config/moon` instead of
`.moon`, the hooks are written to `.config/moon/hooks`.
2. We then sync these generated hook scripts with the current VCS. For Git, we set `core.hooksPath`
to point to the `.moon/hooks` directory, which tells Git to look for hook scripts in that
directory instead of the default `.git/hooks` directory.
:::info
The `.moon/hooks` scripts are generated as Bash scripts (use a `.sh` file extension) on Unix, and
PowerShell scripts (use a `.ps1` file extension) on Windows.
:::
## Examples
### Pre-commit
A perfect use case for the `pre-commit` hook is to check linting and formatting of the files being
committed. If either of these tasks fail, the commit will abort until they are fixed. Be sure to use
the [`--affected`](../run-task#running-based-on-affected-files-only) option so that we _only run_ on
changed projects!
```yaml title=".moon/workspace.yml"
vcs:
hooks:
pre-commit:
- 'moon run :lint :format --affected --status=staged'
```
> By default this will run on the _entire_ project (all files). If you want to filter it to only the
> changed files, enable the [`affectedFiles`](../config/project#affectedfiles) task option.
---
## WASM plugins
[moon](/moon) and [proto](/proto) plugins can be written in
[WebAssembly (WASM)](https://webassembly.org/), a portable binary format. This means that plugins
can be written in any language that compiles to WASM, like Rust, C, C++, Go, TypeScript, and more.
Because WASM based plugins are powered by a programming language, they implicitly support complex
business logic and behavior, have access to a sandboxed file system (via WASI), can execute child
processes, and much more.
:::danger
Since our WASM plugin implementations are still experimental, expect breaking changes to occur in
non-major releases.
:::
## Powered by Extism
Our WASM plugin system is powered by [Extism](https://extism.org/), a Rust-based cross-language
framework for building WASM plugins under a unified guest and host API. Under the hood, Extism uses
[wasmtime](https://wasmtime.dev/) as its WASM runtime.
For the most part, you do _not_ need to know about Extism's host SDK, as we have implemented the
bulk of it within moon and proto directly. However, you _should_ be familiar with the guest PDKs, as
this is what you'll be using to implement Rust-based plugins. We suggest reading the following
material:
- [Plugin development kits](https://extism.org/docs/concepts/pdk) (PDKs)
- The [extism-pdk](https://github.com/extism/rust-pdk) Rust crate
- [Host functions](https://extism.org/docs/concepts/host-functions) (how they work)
## Concepts
Before we begin, let's talk about a few concepts that are critical to WASM and our plugin systems.
### Plugin identifier
When implementing plugin functions, you'll need to access information about the current plugin. To
get the current plugin identifier (the key the plugin was configured with), use the
[`get_plugin_id`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/fn.get_plugin_id.html) function.
```rust
let id = get_plugin_id();
```
### Virtual paths
WASM by default does not have access to the host file system, but through [WASI](https://wasi.dev/),
we can provide sandboxed access to a pre-defined list of allowed directories. We call these
[virtual paths](https://docs.rs/warpgate_api/latest/warpgate_api/struct.VirtualPath.html), and all
paths provided via function input or context use them.
Virtual paths are implemented by mapping a real path (host machine) to a virtual path (guest
runtime) using file path prefixes. The following prefixes are currently supported:
| Real path | Virtual path | Only for |
| -------------- | ------------ | -------- |
| `~` | `/userhome` | ~ |
| `~/.proto` | `/proto` | ~ |
| `~/.moon` | `/moon` | moon |
| moon workspace | `/workspace` | moon |
For example, from the context of WASM, you may have a virtual path of `/proto/tools/node/1.2.3`,
which simply maps back to `~/.proto/tools/node/1.2.3` on the host machine. However, this should
almost always be transparent to you, the developer, and to end users.
The `VirtualPath` type is a newtype wrapper around Rust's `PathBuf`, with a sibling
[`RealPath`](https://docs.rs/warpgate_api/latest/warpgate_api/struct.RealPath.html) type that
represents a real path on the host machine. Both types dereference to `PathBuf`, so all `Path` and
`PathBuf` methods are available.
However, there may be a few cases where you need access to the real path from WASM, for example,
logging or executing commands. For this, the real path can be accessed with the
[`to_real_path`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/trait.VirtualPathExt.html)
extension trait method (this is a Rust only feature). Jump to [converting paths](#converting-paths)
for more information.
```rust
let real_path = virtual_path.to_real_path()?; // `Option`
```
:::caution
The path types were reworked in proto v0.60. Previously, `VirtualPath` was an enum that carried both
the virtual and real path variants, and the real path was accessed with its `real_path()` method.
moon has not yet adopted the reworked API, so moon plugins should continue to use the previous APIs
until moon updates its warpgate dependencies.
:::
#### File system caveats
When working with the file system from the context of WASM, there are a few caveats to be aware of.
- All `fs` calls must use the virtual path. Real paths will error.
- Paths not white listed (using prefixes above) will error.
- Changing file permissions is not supported (on Unix and Windows).
- This is because WASI does not support this.
- This also means operations like unpacking archives is not possible.
### Host environment
Since WASM executes in its own runtime, it _does not_ have access to the current host operating
system, architecture, so on and so forth. To bridge this gap, we provide the
[`get_host_environment`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/fn.get_host_environment.html)
function.
[Learn more about this type](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/struct.HostEnvironment.html).
```rust
let env = get_host_environment()?;
```
The host operating system and architecture can be accessed with `os` and `arch` fields respectively.
Both fields are an enum in Rust, or a string in other languages.
```rust
if env.os == HostOS::Windows {
// Windows only
}
if env.arch == HostArch::Arm64 {
// aarch64 only
}
```
Furthermore, the user's home directory (`~`) can be accessed with the `home_dir` field, which is a
[virtual path](#virtual-paths).
```rust
if env.home_dir.join(some_path).exists() {
// Do something
}
```
### Host functions & macros
WASM is pretty powerful but it can't do everything since it's sandboxed. To work around this, we
provide a mechanism known as host functions, which are functions that are implemented on the host
(in Rust), and can be executed from WASM. The following host functions are currently available:
- [`exec_command`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/macro.exec_command.html) -
Execute a system command on the host machine, with a provided list of arguments or environment
variables.
- [`get_env_var`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/macro.host_env.html) - Get an
environment variable value from the host environment.
- [`host_log`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/macro.host_log.html) - Log an
stdout, stderr, or tracing message to the host's terminal.
- [`send_request`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/macro.send_request.html) -
Requests a URL on the host machine using a Rust-based HTTP client (not WASM).
- [`set_env_var`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/macro.host_env.html) - Set an
environment variable to the host environment.
> Before proto v0.60, `from_virtual_path` and `to_virtual_path` host functions were also available
> for converting between virtual and real paths. These have been removed (but are still available
> in moon), as conversions now happen directly in the guest. Jump to
> [converting paths](#converting-paths) for more information.
To use host functions, you'll need to make them available by registering them at the top of your
Rust file (only add the functions you want to use) using the
[extism-pdk](https://crates.io/crates/extism-pdk) crate.
```rust
use extism_pdk::*;
#[host_fn]
extern "ExtismHost" {
fn exec_command(input: Json) -> Json;
fn get_env_var(key: String) -> String;
fn host_log(input: Json);
fn send_request(input: Json) -> Json;
fn set_env_var(key: String, value: String);
}
```
:::info
To simplify development, we provide built-in functions and macros for the host functions above.
Continue reading for more information on these macros.
:::
#### Converting paths
When working with virtual paths, you may need to convert them to real paths, and vice versa. The
[`VirtualPathExt`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/trait.VirtualPathExt.html) and
[`RealPathExt`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/trait.RealPathExt.html) extension
traits provide `create` constructors and conversion methods for such situations. Unlike the rest of
this section, these are not host functions — conversions are performed directly in the guest, using
the list of host-to-guest path mappings provided by the host.
```rust
// Create a virtual path from a real path
let virt = VirtualPath::create("/some/real/path")?;
// Convert a virtual path into a real path (returns an `Option`)
let real = virt.to_real_path()?;
```
For lower-level control, the
[`convert_to_virtual_path`](https://docs.rs/warpgate_api/latest/warpgate_api/fn.convert_to_virtual_path.html)
and
[`convert_to_real_path`](https://docs.rs/warpgate_api/latest/warpgate_api/fn.convert_to_real_path.html)
functions can be used instead, paired with
[`get_host_to_guest_paths`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/fn.get_host_to_guest_paths.html).
> Before proto v0.60, paths were converted with the `into_virtual_path` and `into_real_path`
> functions, which were powered by the `to_virtual_path` and `from_virtual_path` host functions
> respectively. These are still available in moon.
#### Environment variables
The [`get_host_env_var`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/fn.get_host_env_var.html)
and [`set_host_env_var`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/fn.set_host_env_var.html)
functions can be used to read and write environment variables on the host, using the `set_env_var`
and `get_env_var` host functions respectively.
```rust
// Set a value
set_host_env_var("ENV_VAR", "value")?;
// Get a value (returns an `Option`)
let value = get_host_env_var("ENV_VAR")?;
```
Additionally, the
[`add_host_paths`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/fn.add_host_paths.html) function
can be used to append paths to the `PATH` environment variable.
```rust
// Append to path
add_host_paths(["/userhome/some/virtual/path"])?;
```
#### Executing commands
The [`exec_command!`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/macro.exec_command.html)
macro can be used to execute a command on the host, using the `exec_command` host function. If the
command does not exist on `PATH`, an error is thrown. This macros supports three modes: pipe,
inherit, and raw (returns `Result`).
```rust
let result = exec_command!(raw, "which", ["node"]);
```
If you want a simpler API, the
[`exec`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/fn.exec.html),
[`exec_captured`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/fn.exec_captured.html) (pipe),
and [`exec_streamed`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/fn.exec_streamed.html)
(inherit) functions can be used.
```rust
// Pipe stdout/stderr
let output = exec_captured("which", ["node"])?;
// Inherit stdout/stderr
exec_streamed("npm", ["install"])?;
// Full control
exec(ExecCommandInput {
command: "npm".into(),
args: vec!["install".into()],
..ExecCommandInput::default()
})?;
```
#### Sending requests
The [`send_request`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/macro.send_request.html) macro
can be used to request a URL on the host, instead of from WASM, allowing it to use the same HTTP
client as the host CLI. This macro returns a response object, with the raw body in bytes, and the
status code.
```rust
let response = send_request!("https://some.com/url/to/fetch");
if response.status == 200 {
let json = response.json::()?;
let text = response.text()?;
} else {
// Error!
}
```
To simplify the handling of requests -> responses, we also provide the
[`fetch_bytes`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/fn.fetch_bytes.html),
[`fetch_json`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/fn.fetch_json.html), and
[`fetch_text`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/fn.fetch_text.html) functions.
```rust
let json: T = fetch_json("https://some.com/url/to/fetch.json")?;
```
> Only GET requests are supported.
#### Logging
The [`host_log!`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/macro.host_log.html) macro can be
used to write stdout or stderr messages to the host's terminal, using the `host_log` host function.
It supports the same argument patterns as `format!`.
If you want full control, like providing data/fields, use the input mode and provide
[`HostLogInput`](https://docs.rs/warpgate_pdk/latest/warpgate_pdk/struct.HostLogInput.html).
```rust
host_log!(stdout, "Some message");
host_log!(stderr, "Some message with {}", "args");
// With data
host_log!(input, HostLogInput {
message: "Some message with data".into(),
data: HashMap::from_iter([
("data".into(), serde_json::to_value(data)?),
]),
target: HostLogTarget::Stderr,
});
```
Furthermore, the [extism-pdk](https://crates.io/crates/extism-pdk) crate provides a handful of
macros for writing level-based messages that'll appear in the host's terminal when `--log` is
enabled in the CLI. These also support arguments.
```rust
debug!("This is a debug message");
info!("Something informational happened");
warn!("Proceed with caution");
error!("Oh no, something went wrong");
```
## Configuring plugin locations
To use a WASM plugin, it'll need to be configured in both moon and proto. Luckily both tools use a
similar approach for configuring plugins called the
[plugin locator](https://docs.rs/warpgate/latest/warpgate/enum.PluginLocator.html). A locator string
is composed of 2 parts separated by `://`, the former is the protocol, and the latter is the
location.
```toml
"://"
```
The following locator patterns are supported:
### `file`
The `file://` protocol represents a file path, either absolute or relative (from the current
configuration file).
```toml
# Relative
"file://./path/to/example.wasm"
# Absolute
"file:///root/path/to/example.wasm"
```
### `github`
The `github://` protocol can be used to target and download an asset from a specific GitHub release.
The location must be an organization + repository slug (owner/repo), and the release _must have_ a
`.wasm` asset available to download.
```toml
"github://moonrepo/example-repo"
```
If you are targeting releases in a monorepo, you can append the project name after the repository.
The project name will be used as a prefix for tags, and will match `@v?` or
`-v?` based tags.
```toml
"github://moonrepo/example-repo/project-name"
```
By default, the latest release will be used and cached for 7 days. If you'd prefer to target a
specific release (preferred), append the release tag to the end of the location.
```toml
"github://moonrepo/example-repo@v1.2.3"
```
This strategy is powered by the [GitHub API](https://api.github.com/) and is subject to rate
limiting. If running in a CI environment, we suggesting setting a `GITHUB_TOKEN` environment
variable to authorize API requests with. If using GitHub Actions, it's as simple as:
```yaml
# In some job or step...
env:
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
```
### `https`
The `https://` protocol is your standard URL, and must point to an absolute file path. Files will be
downloaded to `~/.moon/plugins` or `~/.proto/plugins`. Non-secure URLs are _not supported_!
```toml
"https://domain.com/path/to/plugins/example.wasm"
```
## Creating a plugin
:::info
Although plugins can be written in any language that compiles to WASM, we've only tested Rust. The
rest of this article assume you're using Rust and Cargo! Refer to [Extism](https://extism.org/)'s
documentation for other examples.
:::
To start, create a new crate with Cargo:
```shell
cargo new plugin --lib
cd plugin
```
Set the lib type to `cdylib`, and provide other required settings.
```toml title="Cargo.toml"
[package]
name = "example_plugin"
version = "0.0.1"
edition = "2024"
publish = false
[lib]
crate-type = ['cdylib']
[profile.release]
codegen-units = 1
debug = false
lto = true
opt-level = "s"
panic = "abort"
```
Our Rust plugins are powered by [Extism](https://extism.org/), so lets add their PDK and ours as a
dependency.
```shell
cargo add extism-pdk
# For proto
cargo add proto_pdk
# For moon
cargo add moon_pdk
```
In all Rust files, we can import all the PDKs with the following:
```rust title="src/lib.rs"
use extism_pdk::*;
```
We can then build the WASM binary. The file will be available at
`target/wasm32-wasip1/debug/.wasm`.
```shell
cargo build --target wasm32-wasip1
```
## Building and publishing
At this point, you should have a fully working WASM plugin, but to make it available to the
community, you'll still need to build and make the `.wasm` file available. The easiest solution is
to publish a GitHub release and include the `.wasm` file as an asset.
### Building, optimizing, and stripping
WASM files are pretty fat, even when compiling in release mode. To reduce the size of these files,
we can use `wasm-opt` and `wasm-strip`, both of which are provided by the
[WebAssembly](https://github.com/WebAssembly) group. The following script is what we use to build
our own plugins.
:::info
This functionality is natively supported in our
[moonrepo/build-wasm-plugin](https://github.com/moonrepo/build-wasm-plugin) GitHub Action!
:::
```bash title="build-wasm"
#!/usr/bin/env bash
target="${CARGO_TARGET_DIR:-target}"
input="$target/wasm32-wasip1/release/$1.wasm"
output="$target/wasm32-wasip1/$1.wasm"
echo "Building"
cargo build --target wasm32-wasip1 --release
echo "Optimizing"
# https://github.com/WebAssembly/binaryen
~/binaryen/bin/wasm-opt -Os "$input" --output "$output"
echo "Stripping"
# https://github.com/WebAssembly/wabt
~/wabt/bin/wasm-strip "$output"
```
### Manually create releases
When your plugin is ready to be published, you can create a release on GitHub using the following
steps.
1. Tag the release and push to GitHub.
```shell
git tag v0.0.1
git push --tags
```
2. Build a release version of the plugin using the `build-wasm` script above. The file will be
available at `target/wasm32-wasip1/.wasm`.
```shell
build-wasm
```
3. In GitHub, navigate to the tags page, find the new tag, create a new release, and attach the
built file as an asset.
### Automate releases
If you're using GitHub Actions, you can automate the release process with our official
[moonrepo/build-wasm-plugin](https://github.com/moonrepo/build-wasm-plugin) action.
1. Create a new workflow file at `.github/workflows/release.yml`. Refer to the link above for a
working example.
2. Tag the release and push to GitHub.
```shell
# In a polyrepo
git tag v0.0.1
# In a monorepo
git tag example_plugin-v0.0.1
# Push the tags
git push --tags
```
3. The action will automatically build the plugin, create a release, and attach the built file as an
asset.
---
## Webhooks
Looking to gather metrics for your pipelines? Gain insight into run durations and failures? Maybe
you want to send Slack or Discord notifications? With our webhooks, all of these are possible!
When the [`notifier.webhookUrl`](../config/workspace#webhookurl) setting is configured with an HTTPS
URL, and moon is running in a CI environment, moon will POST a payload to this endpoint for every
event in our pipeline.
## Payload structure
Every webhook event is posted with the following request body, known as a payload.
- `type` (`string`) - The type of [event](#events).
- `environment` (`object | null`) - Information about the current CI/CD pipeline environment.
- `event` (`object`) - The event specific payload. View each event for an example of their
structure.
- `createdAt` (`string`) - When the event was created, as a UTC timestamp in ISO 8601 (RFC 3339)
format.
- `uuid` (`string`) - A unique identifier for all webhooks in the current run batch.
- `trace` (`string`) - A unique identifier for all webhooks in the overall run batch. Can be defined
via `MOON_TRACE_ID` environment variable.
```json
{
"type": "...",
"environment": "...",
"event": {
// ...
},
"createdAt": "...",
"uuid": "...",
"trace": "..."
}
```
> The `uuid` field can be used to differentiate concurrently running pipelines!
### Pipeline environment
When webhooks are sent from a CI/CD pipeline, we attempt to include information about the
environment under the `environment` field. If information could not be detected, this field is null,
otherwise it contains these fields.
- `baseBranch` (`string | null`) - When a merge/pull request, the target (base) branch, otherwise
null.
- `branch` (`string`) - When a merge/pull request, the source (head) branch, otherwise the
triggering branch.
- `id` (`string`) - ID of the current pipeline instance.
- `provider` (`string`) - Name of your CI/CD provider. GitHub Actions, GitLab, CircleCI, etc.
- `requestId` (`string | null`) - The ID of the merge/pull request.
- `requestUrl` (`string | null`) - Link to the merge/pull request.
- `revision` (`string`) - The HEAD commit, revision, tag, ref, etc, that triggered the pipeline.
- `url` (`string | null`) - Link to the current pipeline, when available.
## Events
### Pipeline
Runs actions within moon using a robust dependency graph. Is triggered when using
[`moon run`](../commands/run).
### `pipeline.started`
Triggered when the pipeline has been created but before actions have started to run.
This event includes the number of actions registered within the pipeline, but does not provide
detailed information about the actions. Use the [`action.*`](#actionstarted) events for this.
```json
{
"type": "pipeline.started",
"createdAt": "...",
"environment": "...",
"event": {
"actionsCount": 15
},
"uuid": "..."
}
```
### `pipeline.finished`
Triggered when the pipeline has finished running all actions, with aggregated counts based on final
status.
This event is _not_ triggered if the pipeline crashes (this does not include actions that have
failed, as those are legitimate runs). Use the [`pipeline.aborted`](#pipelineaborted) event if you
want to also catch crashes.
```json
{
"type": "pipeline.finished",
"createdAt": "...",
"environment": "...",
"event": {
"cachedCount": 10,
"baselineDuration": {
"secs": 60,
"nanos": 3591693
},
"duration": {
"secs": 120,
"nanos": 3591693
},
"estimatedSavings": {
"secs": 60,
"nanos": 0
},
"failedCount": 1,
"passedCount": 4
},
"uuid": "..."
}
```
### `pipeline.aborted`
Triggered when the pipeline has crashed for unknown reasons, or had to abort as a result of a
critical action failing.
```json
{
"type": "pipeline.aborted",
"createdAt": "...",
"environment": "...",
"event": {
"error": "..."
},
"uuid": "..."
}
```
### Actions
Actions are "jobs" within the pipeline that are executed topologically.
### `action.started`
Triggered when an action within the pipeline has started to run.
```json
{
"type": "action.started",
"createdAt": "...",
"environment": "...",
"event": {
"action": {
"attempts": null,
"createdAt": "...",
"duration": {
"secs": 0,
"nanos": 3591693
},
"error": null,
"label": "InstallWorkspaceDeps(node:18.0.0)",
"nodeIndex": 5,
"status": "passed"
},
"node": {
"action": "InstallDeps",
"params": [
{
"toolchain": "Node",
"version": "18.0.0"
}
]
}
},
"uuid": "..."
}
```
### `action.finished`
Triggered when an action within the pipeline has finished running, either with a success or failure.
If the action failed, the `error` field will be set with the error message.
```json
{
"type": "action.finished",
"createdAt": "...",
"environment": "...",
"event": {
"action": {
"attempts": null,
"createdAt": "...",
"duration": {
"secs": 0,
"nanos": 3591693
},
"error": null,
"label": "InstallWorkspaceDeps(node:18.0.0)",
"nodeIndex": 5,
"status": "passed"
},
"error": null,
"node": {
"action": "InstallDeps",
"params": {
"toolchain": "Node",
"version": "18.0.0"
}
}
},
"uuid": "..."
}
```
### `dependencies.installing`
Triggered when dependencies for a workspace or project have started to install. When targeting a
project, the `project` field will be set, otherwise `null` for the entire workspace.
```json
{
"type": "dependencies.installing",
"createdAt": "...",
"environment": "...",
"event": {
"project": {
"id": "server"
// ...
},
"runtime": {
"toolchain": "Node",
"version": "18.0.0"
},
"root": ".",
"toolchain": "node"
},
"uuid": "..."
}
```
### `dependencies.installed`
Triggered when dependencies for a workspace or project have finished installing. When targeting a
project, the `project` field will be set, otherwise `null` for the entire workspace. If the install
failed, the `error` field will be set with the error message.
For more information about the action, refer to the [`action.finished`](#actionfinished) event.
Installed deps can be scoped with the `InstallDeps(...)` labels.
```json
{
"type": "dependencies.installed",
"createdAt": "...",
"environment": "...",
"event": {
"error": null,
"project": null,
"runtime": {
"toolchain": "Node",
"version": "18.0.0"
},
"root": ".",
"toolchain": "node"
},
"uuid": "..."
}
```
### `environment.initializing`
Triggered when an environment is being setup for a toolchain. When targeting a project, the
`project` field will be set, otherwise `null` for the entire workspace.
```json
{
"type": "environment.initializing",
"createdAt": "...",
"environment": "...",
"event": {
"project": {
"id": "server"
// ...
},
"root": ".",
"toolchain": "node"
},
"uuid": "..."
}
```
### `environment.initialized`
Triggered when an environment has been setup for a toolchain. When targeting a project, the
`project` field will be set, otherwise `null` for the entire workspace. If setup failed, the `error`
field will be set with the error message.
For more information about the action, refer to the [`action.finished`](#actionfinished) event.
Installed deps can be scoped with the `SetupEnvironment(...)` labels.
```json
{
"type": "environment.initialized",
"createdAt": "...",
"environment": "...",
"event": {
"error": null,
"project": null,
"root": ".",
"toolchain": "node"
},
"uuid": "..."
}
```
### `project.syncing`
Triggered when an affected project has started syncing its workspace state. This occurs
automatically before a project's task is ran.
```json
{
"type": "project.syncing",
"createdAt": "...",
"environment": "...",
"event": {
"project": {
"id": "client"
// ...
},
"runtime": {
"toolchain": "Node",
"version": "18.0.0"
}
},
"uuid": "..."
}
```
### `project.synced`
Triggered when an affected project has finished syncing. If the sync failed, the `error` field will
be set with the error message.
For more information about the action, refer to the [`action.finished`](#actionfinished) event.
Synced projects can be scoped with the `SyncProject(...)` labels.
```json
{
"type": "project.synced",
"createdAt": "...",
"environment": "...",
"event": {
"error": null,
"project": {
"id": "client"
// ...
},
"runtime": {
"toolchain": "Node",
"version": "18.0.0"
}
},
"uuid": "..."
}
```
### `toolchain.installing`
Triggered when a toolchain plugin has started downloading and installing.
This event is _always_ triggered, regardless of whether the tool has already been installed or not.
For an accurate state, use the [`action.finished`](#actionfinished) event. If the `status` is
"skipped", then the tool was already installed.
```json
{
"type": "toolchain.installing",
"createdAt": "...",
"environment": "...",
"event": {
"spec": {
"id": "node",
"req": "18.0.0"
}
},
"uuid": "..."
}
```
### `toolchain.installed`
Triggered when a toolchain plugin has finished installing. If the install failed, the `error` field
will be set with the error message.
For more information about the action, refer to the [`action.finished`](#actionfinished) event.
Tools can be scoped with the `SetupToolchain(...)` labels.
```json
{
"type": "toolchain.installed",
"createdAt": "...",
"environment": "...",
"event": {
"error": null,
"spec": {
"id": "node",
"req": "18.0.0"
}
},
"uuid": "..."
}
```
### `task.running`
Triggered when a [task](../concepts/task) has started to run (via [`moon run`](../commands/run) or
similar command).
```json
{
"type": "task.running",
"createdAt": "...",
"environment": "...",
"event": {
"target": "app:build"
},
"uuid": "..."
}
```
### `task.ran`
Triggered when a [task](../concepts/task) has finished running. If the run failed, the `error` field
will be set with the error message.
For more information about the action, refer to the [`action.finished`](#actionfinished) event. Ran
tasks can be scoped with the `RunTask(...)`, `RunInteractiveTask(...)`, and `RunPersistentTask(...)`
labels.
```json
{
"type": "task.ran",
"createdAt": "...",
"environment": "...",
"event": {
"error": null,
"target": "app:build"
},
"uuid": "..."
}
```
### `workspace.syncing`
Triggered when the workspace is being synced.
```json
{
"type": "workspace.syncing",
"createdAt": "...",
"environment": "...",
"event": {
"target": "app:build"
},
"uuid": "..."
}
```
### `workspace.synced`
Triggered when the workspace has finished syncing. If the action failed, the `error` field will be
set with the error message.
```json
{
"type": "workspace.synced",
"createdAt": "...",
"environment": "...",
"event": {
"error": null
},
"uuid": "..."
}
```
---
## Action graph
When you run a [task](../config/project#tasks-1) on the command line, we generate an action graph to
ensure [dependencies](../config/project#deps) of tasks have ran before running run the primary task.
The action graph is a representation of all [tasks](../concepts/task), derived from the
[project graph](./project-graph) and [task graph](./task-graph), and is also 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, we
represent each node in the graph as an action to perform. This allows us to be more flexible and
efficient with how we run tasks, and allows us to provide more functionality and automation than
other runners.
The following actions compose our action graph:
### Sync workspace
This is a common action that always runs and give's moon a chance to perform operations and health
checks across the entire workspace.
:::info
This action can be skipped by disabling the
[`pipeline.syncWorkspace`](../config/workspace#syncworkspace) setting.
:::
### Setup toolchain
The most important action in the graph is the setup toolchain action, which downloads and installs a
tier 3 language into the toolchain. For other tiers, this is basically a no-operation.
- When the tool has already been installed, this action will be skipped.
- Actions will be scoped by toolchain and version (or none if using global `PATH`).
:::info
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
This action runs after the toolchain has been setup, but before dependencies are installed, so that
the development environment can be setup and configured. This includes operations such as modifying
a manifest (`package.json`, etc), updating configuration files, initializing venv's (Python), so on
and so forth.
### Setup proto
This action runs before all toolchain related actions and ensures that [proto](/proto) has been
installed and is available for use. This is required for toolchains that will be downloaded and
installed.
### Install dependencies
Before we run a task, we ensure that all language/toolchain dependencies (`node_modules` for
example) have been installed, by automatically installing them if we detect changes since the last
run. We achieve this by comparing lockfile modified timestamps, parsing manifest files, and hashing
resolved dependency versions.
- When dependencies do _not_ need to be installed, this action will be skipped.
- Depending on the language and configuration, we may install dependencies in a project, or in the
workspace root for all projects.
- Actions will be scoped by toolchain and dependencies root.
:::info
This action can be skipped by disabling the
[`pipeline.installDependencies`](../config/workspace#installdependencies) setting.
:::
### Sync project
To ensure a consistently healthy project and repository, we run a process known as syncing
_everytime_ a task is ran. This action will run sync operations for all toolchains associated with
the project.
:::info
This action can be skipped by disabling the
[`pipeline.syncProject`](../config/workspace#syncproject) setting.
:::
### Run task
The primary action in the graph is the run [task](../concepts/task) action, which runs a project's
task as a child process, derived from a [target](../concepts/target). Tasks can depend on other
tasks, and they'll be effectively orchestrated and executed by running in topological order using a
thread pool.
### Run interactive task
Like the base run task, but runs the [task interactively](../concepts/task#interactive) with stdin
capabilities. All interactive tasks are run in isolation in the graph.
### Run persistent task
Like the base run task, but runs the [task in a persistent process](../concepts/task#persistent)
that never exits. All persistent tasks are run in parallel as the last batch in the graph.
## What is the graph used for?
Without the action graph, tasks would not efficiently run, or possibly at all! The graph helps to
run tasks in parallel, in the correct order, and to ensure a reliable outcome.
---
## Languages
Although moon is currently focusing on the JavaScript ecosystem, our long-term vision is to be a
multi-language task runner and monorepo management tool. To that end, our languages (known as
toolchains) are implemented as WASM plugins, where their functionality is implemented in isolation,
and is _opt-in_.
## Enabling a language
moon [supported languages](../#supported-languages) are opt-in, and _are not_ enabled by default. We
chose this pattern to avoid unnecessary overhead, especially for the future when we have 10 or more
built-in languages.
To enable a supported language, simply define a configuration block with the language's name in
[`.moon/toolchains.*`](../config/toolchain). Even an empty block will enable the language.
```yaml title=".moon/toolchains.yml"
# Enable JavaScript
javascript: {}
# Enable JavaScript with custom settings
javascript:
packageManager: 'pnpm'
# Enable TypeScript
typescript: {}
```
## System language and toolchain
When working with moon, you'll most likely have tasks that run built-in system commands that do not
belong to any of the supported languages. For example, you may have a task that runs `git` or
`docker` commands, or common commands like `rm`, `cp`, `mv`, etc.
For these cases, moon provides a special language/toolchain called `system`, that is always enabled.
This toolchain is a catch-all, an escape-hatch, a fallback, and provides the following:
- Runs a system command or a binary found on `PATH`.
- Wraps the execution in a shell.
To run system commands, set a task's [`toolchain`](../config/project#toolchain) setting to "system".
```yaml title="moon.yml"
tasks:
example:
command: 'git status'
toolchain: 'system'
```
## Tier structure and responsibilities
As mentioned in our introduction,
[language support is divided up into tiers](../#supported-languages), where each tier introduces
more internal integrations and automations, but requires more work to properly implement.
### Tier 0 = Unsupported
The zero tier represents all languages _not directly_ supported by moon. This tier merely exists as
a mechanism for running non-supported language binaries via the
[system toolchain](#system-language-and-toolchain).
```yaml title="moon.yml"
tasks:
example:
command: 'ruby'
toolchain: 'system'
```
### Tier 1 = Language
The first tier is the language itself. This is the most basic level of support, and is the only tier
that is required to be implemented for a language to be considered minimally supported. This tier is
in charge of:
- Declaring metadata about the language. For example, the name of the binary, supported file
extensions, available dependency/package/version managers, names of config/manifest/lock files,
etc.
- Mechanisms for detecting the language of a project based on config files and other criteria.
- Maps to a project's [`language`](../config/project#language) setting.
- Supports a configuration block by name in [`.moon/toolchains.*`](../config/toolchain).
```yaml title="moon.yml"
language: 'javascript'
```
### Tier 2 = Ecosystem
The second tier requires the language functionality from tier 1, and eventually the toolchain
functionality from tier 3, and provides interoperability with moon's internals. This is the most
complex of all tiers, and the tier is in charge of:
- Determining when, where, and how to install dependencies for a project or the workspace.
- Loading project aliases and inferring implicit relationships between projects.
- Syncing a project and ensuring a healthy project state.
- Hashing efficiently for dependency installs and target runs.
- Helpers for parsing lockfiles and manifest files, and interacting with the language's ecosystem
(for example, Node.js module resolution).
- Prepending `PATH` with appropriate lookups to execute a task.
- Running a target's command with proper arguments, environment variables, and flags.
- Maps to a project's [`toolchains.default`](../config/project#toolchain-1) or task's
[`toolchains`](../config/project#toolchain) setting.
```yaml title="moon.yml"
tasks:
example:
command: 'webpack'
toolchain: 'node'
```
```yaml title=".moon/toolchains.yml"
javascript: {}
node: {}
```
### Tier 3 = Toolchain
The third tier is toolchain support via [proto](/proto). This is the final tier, as the toolchain is
unusable unless the platform has been entirely integrated, and as such, the platform depends on this
tier. This tier handles:
- Downloading and installing a language into the toolchain.
- Installing and deduping project dependencies.
- Detecting appropriate versions of tools to use.
- Determining which binary to use and execute targets with.
- Supports a `version` field in the named configuration block in
[`.moon/toolchains.*`](../config/toolchain).
```yaml title=".moon/toolchains.yml"
node:
version: '18.0.0'
```
---
## Project graph
The project graph is a representation of all configured
[projects in the workspace](../config/workspace#projects) and their relationships between each
other, and is represented internally as a directed acyclic graph (DAG). Below is a visual
representation of a project graph, composed of multiple applications and libraries, where both
project types depend on libraries.
:::info
The [`moon project-graph`](../commands/project-graph) command can be used to view the structure of
your workspace.
:::
## Relationships
A relationship is between a dependent (downstream project) and a dependency/requirement (upstream
project). Relationships are derived from source code and configuration files within the repository,
and fall into 1 of 2 categories:
### Explicit
These are dependencies that are explicitly defined in a project's [`moon.*`](../config/project)
config file, using the [`dependsOn`](../config/project#dependson) setting.
```yaml title="moon.yml"
dependsOn:
- 'components'
- id: 'utils'
scope: 'peer'
```
### Implicit
These are dependencies that are implicitly discovered by moon when scanning the repository. How an
implicit dependency is discovered is based on a
[language's platform integration](./languages#tier-2--platform), and how that language's ecosystem
functions.
```json title="package.json"
{
// ...
"dependencies": {
"@company/components": "workspace:*"
},
"peerDependencies": {
"@company/utils": "workspace:*"
}
}
```
:::caution
If a language is not officially supported by moon, then implicit dependencies will _not_ be
resolved. For unsupported languages, you must explicitly configure dependencies.
:::
### Scopes
Every relationship is categorized into a scope that describes the type of relationship between the
parent and child. Scopes are currently used for [project syncing](../commands/sync) and deep Docker
integration.
- **Production** - Dependency is required in production, _will not be_ pruned in production
environments, and will sync as a production dependency.
- **Development** - Dependency is required in development and production, _will be_ pruned from
production environments, and will sync as a development-only dependency.
- **Build** - Dependency is required for building only, and will sync as a build dependency.
- **Peer** - Dependency is a peer requirement, with language specific semantics. Will sync as a peer
dependency when applicable.
## What is the graph used for?
Great question, the project graph is used throughout the codebase to accomplish a variety of
functions, but mainly:
- Is fed into the [task graph](./task-graph) to determine relationships of tasks between other
tasks, and across projects.
- Powers our [Docker](../guides/docker) layer caching and scaffolding implementations.
- Utilized for [project syncing](../commands/sync) to ensure a healthy repository state.
- Determines affected projects in [continuous integration](../guides/ci) workflows.
---
## Task graph
The task graph is a representation of all configured
[tasks in the workspace](../config/workspace#projects) and their relationships between each other,
and is represented internally as a directed acyclic graph (DAG). This graph is derived from
information in the [project graph](./project-graph). Below is a visual representation of a task
graph.
:::info
The [`moon task-graph`](../commands/task-graph) command can be used to view the structure of your
workspace.
:::
## Relationships
A relationship is between a dependent (downstream task) and a dependency/requirement (upstream
task). Relationships are derived explicitly with the task [`deps`](../config/project#deps) setting,
and fall into 1 of 2 categories:
### Required
These are dependencies that are required to run and complete with a success, before the owning task
can run. If a required dependency fails, then the owning task will abort.
### Optional
The opposite of [required](#required), these are dependencies that can either a) not exist during
task inheritance, or b) run and fail without aborting the owning task.
## What is the graph used for?
Great question, the task graph is extremely important for running tasks (duh), and it also:
- Is fed into the [action graph](./action-graph) that can be executed in topological order.
- Determines affected tasks in [continuous integration](../guides/ci) workflows.
---
## Install moon
The following guide can be used to install moon and integrate it into an existing repository (with
or without incremental adoption), or to a fresh repository.
## Installing
The entirety of moon is packaged and shipped as a single binary. It works on all major operating
systems, and does not require any external dependencies. For convenience, we provide the following
scripts to download and install moon.
### proto
moon can be installed and managed in [proto's toolchain](/proto). This will install moon to
`~/.proto/tools/moon` and make the binary available at `~/.proto/bin`.
```shell
proto install moon
```
Furthermore, the version of moon can be pinned on a per-project basis using the
[`.prototools` config file](/docs/proto/config).
```toml title=".prototools"
moon = "2.0.0"
```
:::info
We suggest using proto to manage moon (and other tools), as it allows for multiple versions to be
installed and used. The other installation options only allow for a single version (typically the
last installed).
:::
### Linux, macOS, WSL
In a terminal that supports Bash, run:
```shell
bash <(curl -fsSL https://moonrepo.dev/install/moon.sh)
```
This will install moon to `~/.moon/bin`. You'll then need to set `PATH` manually in your shell
profile.
```shell
export PATH="$HOME/.moon/bin:$PATH"
```
### Windows
In Powershell or Windows Terminal, run:
```shell
irm https://moonrepo.dev/install/moon.ps1 | iex
```
This will install moon to `~\.moon\bin` and prepend to the `PATH` environment variable for the
current session. To persist across sessions, update `PATH` manually in your system environment
variables.
:::info
If you are using Git Bash on Windows, you can run the [Unix commands](#linux-macos-wsl) above.
:::
### npm
moon is also packaged and shipped as a single binary through the
[`@moonrepo/cli`](https://www.npmjs.com/package/@moonrepo/cli) npm package. Begin by installing this
package at the root of the repository.
If you are installing with Bun, you'll need to add `@moonrepo/cli` as a
[trusted dependency](https://bun.sh/docs/install/lifecycle#trusteddependencies).
:::info
When a global `moon` binary is executed, and the `@moonrepo/cli` binary exists within the
repository, the npm package version will be executed instead. We do this because the npm package
denotes the exact version the repository is pinned it.
:::
### Nix
On Linux, moon can be run directly from the flake in the repository, without installing anything
first.
```shell
nix run github:moonrepo/moon -- --version
```
Append any git ref that contains `flake.nix` to build that revision instead, such as
`github:moonrepo/moon/master`. To keep moon around, install it into your profile.
```shell
nix profile install github:moonrepo/moon
```
For a flake based NixOS or home-manager setup, add moon as an input and take the package from it.
```nix
{
inputs.moon.url = "github:moonrepo/moon";
outputs = { nixpkgs, moon, ... }: {
# Somewhere in your configuration
environment.systemPackages = [ moon.packages.x86_64-linux.default ];
};
}
```
moon is also in [nixpkgs](https://search.nixos.org/packages?query=moon), but that package is updated
separately from this repository and may lag behind the latest release.
### Other
moon can also be downloaded and installed manually, by downloading an asset from
[https://github.com/moonrepo/moon/releases](https://github.com/moonrepo/moon/releases). Be sure to
rename the file after downloading, and apply the executable bit (`chmod +x`) on macOS and Linux.
## Upgrading
If using proto, moon can be upgraded using the following command:
```shell
proto install moon --pin
```
Otherwise, moon can be upgraded with the [`moon upgrade`](./commands/upgrade) command. However, this
will only upgrade moon if it was installed in `~/.moon/bin`.
```shell
moon upgrade
```
Otherwise, you can re-run the installers above and it will download, install, and overwrite with the
latest version.
## Next steps
---
## Introduction
moonrepo is a productivity platform that aims to eliminate pain points for both developers and
companies, by automating tiresome and complex workflows, and improving the overall developer
experience.
We currently achieve this through the following tools and services:
## moon
[moon](/moon) is a repository *m*anagement, *o*rganization, *o*rchestration, and *n*otification tool
for the web ecosystem, written in Rust. Many of the concepts within moon are heavily inspired from
Bazel and other popular build systems, but tailored for our
[supported languages](#supported-languages).
You can think of a moon as a tool that sits firmly in the middle between Bazel (high complexity,
full structure), and make/just/etc scripts (low complexity, no structure).
### Why use moon?
Working in a language's ecosystem can be very involved, especially when it comes to managing a
repository effectively. Which language version to use? Which dependency manager to use? How to use
packages? Or how to build packages? So on and so forth. moon aims to streamline this entire process
and provide a first-class developer experience.
- **Increased productivity** - With [Rust](https://www.rust-lang.org/) as our foundation, we can
ensure robust speeds, high performance, and low memory usage. Instead of long builds blocking you,
focus on your work.
- **Exceptional developer experience** - As veterans of developer tooling, we're well aware of the
pain points and frustrations. Our goal is to mitigate and overcome these obstacles.
- **Incremental adoption** - At its core, moon has been designed to be adopted incrementally and is
_not_ an "all at once adoption". Migrate project-by-project, or task-by-task, it's up to you!
- **Reduced tasks confusion** - Tasks (for example, `package.json` scripts) can become unwieldy,
very quickly. No more duplicating the same task into every project, or reverse-engineering which
root scripts to use. With moon, all you need to know is the project name, and a task name.
- **Ensure correct versions** - Whether it's a programming language or dependency manager, ensure
the same version of each tool is the same across _every_ developer's environment. No more wasted
hours of debugging.
- **Automation built-in** - When applicable, moon will automatically install dependencies
(`node_modules`), or
[sync project dependencies](/docs/config/toolchain#syncprojectworkspacedependencies), or even
[sync TypeScript project references](/docs/config/toolchain#syncprojectreferences).
- And of course, the amazing list of [features](#features) below!
### Supported languages
moon's long-term vision is to robustly support multiple programming languages (and dependency
managers) so that a repository composed of projects with differing languages and tools can all work
in unison. This is a lofty vision that requires a massive amount of time and resources to achieve,
and as such, is not available on initial release, but will gradually be supported over time.
To help achieve this vision, language support is broken down into 4 tiers, allowing us to
incrementally integrate and improve them over time. The 4 tiers are as follows:
- **No direct integration** - Tool is not
directly supported in moon, but can still be run using the
["system" task toolchain](/docs/faq#can-we-run-other-languages), which expects the tool to exist
in the current environment.
- **Project categorization** - Projects can
configure their primary [language in `moon.*`](/docs/config/project#language), and have a
dedicated Rust crate for metadata.
- **Ecosystem platformization** - moon deeply
integrates with the language's ecosystem by parsing manifests, lockfiles, and other semantic files
to infer dependencies, tasks, and other necessary information.
- **Toolchain integration** - Language is
directly supported in the toolchain, configured in [`.moon/toolchains.*`](/docs/config/toolchain),
and will automatically be downloaded and installed.
| | Tier 0 | Tier 1 | Tier 2 | Tier 3 |
| :---------------------------- | :----: | :----: | :----: | :----: |
| Bash/Batch | 🟢 | 🟢 | | |
| Bun (JavaScript, TypeScript) | 🟢 | 🟢 | 🟢 | 🟢 |
| Deno (JavaScript, TypeScript) | 🟢 | 🟢 | 🟢 | 🟢 |
| Go | 🟢 | 🟢 | 🟢 | 🟢 |
| Node (JavaScript, TypeScript) | 🟢 | 🟢 | 🟢 | 🟢 |
| └─ npm, pnpm, yarn | 🟢 | ⚪️ | 🟢 | 🟢 |
| PHP | 🟢 | 🟢 | | |
| └─ Composer | 🟢 | ⚪️ | | |
| Python | 🟢 | 🟢 | 🟣 | 🟣 |
| └─ Pip, Poetry, uv | 🟢 | ⚪️ | 🟣 | 🟣 |
| Ruby | 🟢 | 🟢 | 🟣 | 🟣 |
| └─ Gems, Bundler | 🟢 | ⚪️ | 🟣 | 🟣 |
| Rust | 🟢 | 🟢 | 🟢 | 🟢 |
| └─ Cargo | 🟢 | ⚪️ | 🟢 | 🟢 |
| Other (Kotlin, Java, C#, ...) | 🟢 | | | |
- ⚪️ Not applicable
- 🟣 Partially supported (experimental)
- 🟢 Fully Supported
### Supported targets
Because moon is written in Rust, we only support targets that are explicitly compiled for, which are
currently:
| Operating system | Architecture | Target |
| :--------------- | :----------- | :--------------------------- |
| macOS 64-bit | Intel | `x86_64-apple-darwin` |
| macOS 64-bit | ARM | `aarch64-apple-darwin` |
| Linux 64-bit | Intel GNU | `x86_64-unknown-linux-gnu` |
| Linux 64-bit | Intel musl | `x86_64-unknown-linux-musl` |
| Linux 64-bit | ARM GNU | `aarch64-unknown-linux-gnu` |
| Linux 64-bit | ARM musl | `aarch64-unknown-linux-musl` |
| Windows 64-bit | Intel | `x86_64-pc-windows-msvc` |
### Features
#### Management
- **Smart hashing** - Collects inputs from multiple sources to ensure builds are deterministic and
reproducible.
- **Remote caching** - Persists builds, hashes, and caches between teammates and CI/CD environments.
- **Integrated toolchain** - Automatically downloads and installs explicit versions of Node.js and
other tools for consistency across the entire workspace or per project.
- **Multi-platform** - Runs on common development platforms: Linux, macOS, and Windows.
#### Organization
- **Project graph** - Generates a project graph for dependency and dependent relationships.
- **Code generation** - Easily scaffold new applications, libraries, tooling, and more!
- **Dependency workspaces** - Works alongside package manager workspaces so that projects have
distinct dependency trees.
- **Code ownership** - Declare owners, maintainers, support channels, and more. Generate CODEOWNERS.
#### Orchestration
- **Dependency graph** - Generates a dependency graph to increase performance and reduce workloads.
- **Action pipeline** - Executes actions in parallel and in order using a thread pool and our
dependency graph.
- **Action distribution** - Distributes actions across
multiple machines to increase throughput.
- **Incremental builds** - With our smart hashing, only rebuild projects that have been touched
since the last build.
#### Notification
- **Flakiness detection** - Reduce flaky builds with automatic retries and passthrough settings.
- **Webhook events** - Receive a webhook for every event in
the pipeline. Useful for metrics gathering and insights.
- **Terminal notifications** - Receives notifications in
your chosen terminal when builds are successful... or are not.
- **Git hooks** - Manage Git hooks to enforce workflows and requirements for contributors.
## proto
[proto](/proto) is a version manager for your favorite programming languages.
[View proto documentation](/docs/proto).
---
## Migrate to moon v2.0
To ease the migration process from moon v1 to v2, we've compiled a list of all breaking changes and
important changes that you should be aware of. Please read through these carefully before upgrading
your workspace.
To automate some of the migration process, we've created the `moon migrate v2` command that will
migrate all applicable settings in configuration files.
```shell
$ moon migrate v2
```
## CLI
- Removed canary and nightly releases.
### Commands
- We've done a large polish pass for all commands, based on the [CLI guidelines](https://clig.dev).
- Updated the `moonx` binary to use `moon exec` instead of `moon run` under the hood.
- Renamed all options and flags to kebab-case instead of camelCase.
- Example: `--logLevel` -> `--log-level`
- Renamed options for all commands:
- `--platform` -> `--toolchain`
- Removed commands:
- `moon node`
- `moon migrate from-package-json` (use the `migrate-turborepo` extension instead)
- `moon query hash` (use `moon hash` instead)
- `moon query hash-diff` (use `moon hash` instead)
#### `moon action-graph`
- Changed the output of `--json` to a new JSON structure (now matches the project and task graphs).
#### `moon check`
- Now runs `moon exec` under the hood, with some arguments/options pre-filled.
- If the project ID is not specifed, it will no longer find the closest project. Instead you must
pass `--closest`.
- Renamed options:
- `--update-cache, -u` -> `--force, -f`
#### `moon ci`
- Now runs `moon exec` under the hood, with some arguments/options pre-filled.
- No longer includes graph relations when determining affected state, and solely relies on changed
files/env vars. Pass `--include-relations` to include them.
- Renamed options:
- `--update-cache, -u` -> `--force, -f`
#### `moon generate`
- Changed the destination from a positional argument, to the `--to` option.
- Example: `moon generate ./dist` -> `moon generate --to ./dist`
#### `moon init`
- Removed "scaffolding a toolchain" functionality from the command. Use the `moon toolchain add`
command instead.
- Removed options:
- `--to` (use a positional argument instead)
#### `moon mcp`
- Updated protocol version to 2025-11-25.
- Updated the `get_projects` tool to no longer have an `includeTasks` option.
- Updated the `get_projects` tool to return a list of project fragments, instead of the whole
project object. This was required as the response was too large for MCP.
- Updated the `get_tasks` tool to return a list of task fragments, instead of the whole task object.
This was required as the response was too large for MCP.
#### `moon query projects`
- Removed options:
- `--dependents`
#### `moon run`
- Now runs `moon exec` under the hood, with some arguments/options pre-filled.
- No longer includes graph relations when determining affected state, and solely relies on changed
files/env vars. Pass `--include-relations` to include them.
- Updated options:
- `--dependents` now requires a value, either `deep` or `direct`
- Renamed options:
- `--update-cache, -u` -> `--force, -f`
- Removed options:
- `--no-bail` (use `moon exec` instead)
- `--profile`
- `--remote` (use `--affected remote` instead)
#### `moon templates`
- Changed the output to render a table instead of a list.
#### `moon run`
- Running a target without a scope no longer locates the closest project and instead uses the new
default project feature. To run a target in the closets project, use `~:` scope instead.
- Example: `moon run build` -> `moon run ~:build`
## Workspace
### Configuration: `.moon/workspace.*`
- Renamed setting values:
- `codeowners.orderBy` value `project-name` -> `project-id`
- Renamed settings:
- `codeowners.syncOnRun` -> `codeowners.sync`
- `constraints.enforceProjectTypeRelationships` -> `constraints.enforceLayerRelationships`
- `docker.prune.installToolchainDeps` -> `docker.prune.installToolchainDependencies`
- `docker.scaffold.include` -> `docker.scaffold.configsPhaseGlobs`
- `runner` -> `pipeline`
- `unstable_remote` -> `remote`
- `vcs.manager` -> `vcs.client`
- `vcs.syncHooks` -> `vcs.sync`
- Removed settings:
- `docker.scaffold.copyToolchainFiles`
- `experiments.*`
- `hasher.batchSize`
- `pipeline.archivableTargets`
## Toolchains
### Configuration: `.moon/toolchain.*`
- This file was renamed to `.moon/toolchains.*` (plural) to reflect that multiple toolchains are
configured. This also aligns with the new `.moon/extensions.*` file.
- All toolchains have been stabilized (excluding Python), so the `unstable_` prefix must be removed
from identifiers.
### JavaScript
The `bun`, `deno`, and `node` toolchains now require the `javascript` toolchain to be defined as
well. All shared settings have been moved to the `javascript` toolchain.
In addition, all `node` package managers are no longer nested under the `node` toolchain, but are
now top-level settings. These are only required when the `javascript.packageManager` setting is
defined.
- Moved settings:
- `bun.dependencyVersionFormat` -> `javascript.dependencyVersionFormat`
- `bun.inferTasksFromScripts` -> `javascript.inferTasksFromScripts`
- `bun.rootPackageOnly` -> `javascript.rootPackageDependenciesOnly`
- `bun.syncProjectWorkspaceDependencies` -> `javascript.syncProjectWorkspaceDependencies`
- `node.dependencyVersionFormat` -> `javascript.dependencyVersionFormat`
- `node.dedupeOnLockfileChange` -> `javascript.dedupeOnLockfileChange`
- `node.inferTasksFromScripts` -> `javascript.inferTasksFromScripts`
- `node.packageManager` -> `javascript.packageManager`
- `node.rootPackageOnly` -> `javascript.rootPackageDependenciesOnly`
- `node.syncPackageManagerField` -> `javascript.syncPackageManagerField`
- `node.syncProjectWorkspaceDependencies` -> `javascript.syncProjectWorkspaceDependencies`
- `node.bun` -> `bun`
- `node.npm` -> `npm`
- `node.pnpm` -> `pnpm`
- `node.yarn` -> `yarn`
- Renamed settings:
- `node.binExecArgs` -> `node.executeArgs`
- Removed settings:
- `bun.packagesRoot`
- `deno.depsFile`
- `deno.lockfile`
- `node.addEnginesConstraint`
- `node.packagesRoot`
```yaml title=".moon/toolchains.yml"
# Before
node:
version: '22.14.0'
packageManager: 'yarn'
inferTasksFromScripts: false
syncPackageManagerField: true
syncProjectWorkspaceDependencies: true
yarn:
version: '4.8.0'
# After
javascript:
packageManager: 'yarn'
inferTasksFromScripts: false
syncPackageManagerField: true
syncProjectWorkspaceDependencies: true
node:
version: '22.14.0'
yarn:
version: '4.8.0'
```
### Python
All the Python toolchains are still unstable, and still require the `unstable_` prefix. We're not
Python experts, so we would love help from the community to test and improve the implementation. If
you're interested in helping out, please reach out in our Discord server.
- Moved settings:
- `python.pip` -> `unstable_pip`
- `python.uv` -> `unstable_uv`
- Removed settings:
- `python.rootVenvOnly`
```yaml title=".moon/toolchains.yml"
# Before
python:
version: '3.12.0'
packageManager: 'uv'
uv:
version: '0.10.2'
# After
unstable_python:
version: '3.12.0'
packageManager: 'uv'
unstable_uv:
version: '0.10.2'
```
## Extensions
### Configuration: `.moon/extensions.*`
- The `extensions` setting from `.moon/workspace.*` has been moved (and flattened) to its own file,
`.moon/extensions.*`.
- The built-in extensions `download`, `migrate-nx`, and `migrate-turborepo` must now be enabled in
the configuration file before they can be used. Simply set an empty object.
- This change was made to reduce the number of extensions that are loaded by default, improving
performance.
```yaml title=".moon/extensions.yml"
download: {}
migrate-nx: {}
```
## Pipeline
### Affected graph relations
In v1 when determining the affected state of a task (via `--affected`), the graph relations
(dependencies and dependents) were always included in the calculation. This meant that tasks were
affected, even if there were no changed files or environment variables, just because they were
related to other affected tasks. This was sometimes confusing to users, as it seemed like the
pipeline was over-zealously running tasks that it shouldn't (even though it was correct).
In v2 and later, the graph relations are no longer included by default, and only changed files and
environment variables are taken into account. To include graph relations in the affected
calculation, you can pass the `--include-relations` (`-g`) flag.
### Better CI handling
Tasks have an option called `runInCI` that indicates whether the task should be run in CI or not,
and can be configured with more granulary conditions. However, in v1 this option was only taken into
account in the `moon ci` command, and not in `moon run`. There was no way to force this option to be
respected in `moon run`, which meant that users had to be careful to not accidentally run CI-only
tasks locally.
In v2, we've improved the implementation of this option, and how we detect CI environments. The
following changes have been made.
- `moon ci`, `moon check`, `moon run`, and `moon exec` now all respect the `runInCI` option.
- `moon ci` is always forced in CI mode. The other commands are dependent on the `CI` environment
variable.
- To force CI mode, set the `CI` environment variable to `true`, or pass the `--ci` flag.
- The `runInCI` option will be applied when in an CI environment. The exception to this is the
"only" condition, which also applies locally.
- The `runInCI` option can be disabled entirely with `moon exec --ignore-ci-checks`.
## Project
### Configuration: `moon.*`
- Renamed settings:
- `docker.scaffold.include` -> `docker.scaffold.sourcesPhaseGlobs`
- `project.name` -> `project.title`
- `type` -> `layer`
- `toolchain` -> `toolchains`
- `platform` -> `toolchains.default`
- Removed settings:
- [`project.metadata`](#custom-metadata)
- [`toolchain.*.disabled`](#toolchain-disabling)
### Language detection
The primary `language` is now detected from toolchains, instead of being a hardcoded implementation.
The result may now differ, as the first toolchain in the list will be used. Additionally, languages
that don't have a toolchain yet, like PHP or Ruby, will not be detected and must be explicitly
configured.
```yaml title="moon.yml"
# After
language: 'ruby'
```
### Custom metadata
The `project.metadata` setting has been removed, but all custom metadata fields can now be defined
at the root of the `project` setting.
```yaml title="moon.yml"
# Before
project:
metadata:
customField: 'value'
# After
project:
customField: 'value'
```
### Toolchain disabling
The `toolchain.*.disabled` setting was removed. Instead set the toolchain itself to null/false.
```yaml title="moon.yml"
# Before
toolchain:
typescript:
disabled: true
# After
toolchains:
typescript: null
```
## Tasks
### Configuration: `moon.*`, `.moon/tasks`
- The file `.moon/tasks.yml` has been removed. If you want to support tasks that are inherited by
_all projects_, then move this to `.moon/tasks/all.yml` and _do not_ configure the new
`inheritedBy` setting.
- Renamed tokens:
- `$projectName` -> `$projectTitle`
- `$projectType` -> `$projectLayer`
- `$taskPlatform` -> `$taskToolchain`
- Renamed settings:
- [`tasks.*.local`](#local-mode) -> `tasks.*.preset` using `server` value
- `tasks.*.platform` -> `tasks.*.toolchains`
- `tasks.*.options.affectedPassInputs` -> `tasks.*.options.affectedFiles.passInputsWhenNoMatch`
- Removed setting values:
- `tasks.*.preset` value `watcher`
- Changed settings:
- `tasks.deps.*.args` no longer supports a string. Use a list of strings instead.
- `tasks.*.options.affectedFiles` now supports an object for more granular control.
- Changed option defaults:
- `tasks.*.options.envFile` now defaults to a list of files, instead of a single file, when
`true`. Refer to the blog post for more information.
- `tasks.*.options.inferInputs` now defaults to `false` instead of `true`.
- `tasks.*.options.shell` now defaults to `true` instead of `false`.
- `tasks.*.options.unixShell` now defaults to `bash` instead of nothing.
- `tasks.*.options.windowsShell` now defaults to `pwsh` instead of nothing.
### Simple commands only
We've reworked `command` (and `args`) to only support simple commands. A simple command is an
executable (binary, file, etc) followed by zero or many arguments.
Complex commands that involve shell features like piping (`|`), redirection (`>`, `<`), chaining
(`&&`, `||`, `;`), and environment variable assignment (`KEY=value`) are no longer supported
directly in the `command` setting. Use `script` instead.
```yaml title="moon.yml"
# Before
tasks:
example:
command: 'echo "foo" | grep "f"'
# Before
tasks:
example:
script: 'echo "foo" | grep "f"'
```
> Shell features like expansion, globbing, and substitution is still supported in `command`.
### Local mode
The `local` task setting has been removed as the name was confusing. Users assumed it meant "only
run locally", but it actually meant "this is a persistent server that should only run locally".
Instead, use the `preset` setting with a value of `server`.
```yaml title="moon.yml"
# Before
tasks:
dev:
command: 'start-dev'
local: true
# Before
tasks:
dev:
command: 'start-dev'
preset: 'server'
```
If you want a task that is simply "local only" without other options changes, use `options.runInCI`
directly.
```yaml title="moon.yml"
tasks:
dev:
command: 'start-dev'
options:
runInCI: false
```
### Shells by default
Tasks now run in a shell by default, and will use Bash on Unix (`options.unixShell`), and pwsh on
Windows (`options.windowsShell`). You can disable this behavior by setting `options.shell` to
`false`.
```yaml title="moon.yml"
tasks:
dev:
command: 'start-dev'
options:
shell: false
```
### Env var substitution behavior
The syntax and behavior for substituting (expanding/interpolation) environment variables has
changed, to better align with the standard of `.env` files. The biggest change is that flagless
tokens (`$VAR`) and `?` flag tokens (`$VAR?`) swapped functionality. Refer to the following table:
| Syntax | v1 | v2 |
| --------------------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------- |
| `$VAR` | Substitute with variable syntax (`$VAR`) if variable empty | 💥 Substitute with empty string if variable empty |
| `$VAR!` | Don't substitute and keep variable syntax (`$VAR`) | 💥 Removed syntax |
| `$VAR?` | Substitute with empty string if variable empty | 💥 Removed syntax |
| `${VAR}` | Substitute with variable syntax (`$VAR`) if variable empty | 💥 Substitute with empty string if variable empty |
| `${VAR!}` | Don't substitute and keep variable syntax (`$VAR`) | ~ |
| `${VAR?}` | Substitute with empty string if variable empty | 💥 Substitute with variable syntax (`$VAR`) if variable empty |
| `${VAR:default}` | Use default value if variable empty | ~ |
| `${VAR:-default}`, `${VAR-default}` | ⛔️ Not supported | Use default value if variable empty |
| `${VAR:+alternate}`, `${VAR+alternate}` | ⛔️ Not supported | Use alternate value if variable non-empty |
Legend:
- `~` indicates the syntax/functionality is the same as v1
- `💥` indicates breaking change
- `⛔️` indicates not supported
### Env var precedence
The order of precedence for environment variables has slightly changed when running tasks, as it was
confusing to users. The new order of precedence is as follows, from lowest to highest tier, with the
latter overwriting the former:
- Task `.env` files
- via `options.envFile`
- Task env variables
- via `env` or `deps.*.env`
- System variables
- via profile scripts (`.bashrc`, `.zshrc`, etc)
- via command line: `KEY=value moon ...`
In regards to variable substitution, each tier can reference variables within the same tier, or the
higher tier(s), but _not_ from lower tier(s). This is because variables are processed in _reverse
order_. Refer to the following table:
| Tier | Can reference | Evaluated during |
| ------ | -------------------- | --------------------- |
| Dotenv | Dotenv, Task, System | Before task execution |
| Task | Task, System | After task creation |
| System | System | CLI startup |
If you don't want to inherit a system variable, you can override it in the task with a null value.
```yaml
tasks:
dev:
env:
EXAMPLE: null
```
### Deferred `.env` files
In v1, when `options.envFile` was enabled, the `.env` file(s) were loaded at the time of task
creation, during the building of project/task graphs. This meant that if the `.env` file changed
between the time of graph creation and task execution, the changes would not be reflected.
In v2 and later, `.env` files are loaded _just before_ task execution, ensuring that any changes to
the file are picked up.
:::caution
Because of this change, task `env` variables will continue to override `.env` file variables, BUT
can no longer reference them for substitution. This is because the `.env` files are loaded later in
the process.
:::
### Toolchain detection
How toolchains work and get detected for projects and tasks has been heavily reworked in v2.
Ideally, you should _never_ configure the `toolchains` setting for a task directly. This can cause
unintended consequences, and instead you should rely on the toolchain detection system itself.
For example, in moon v1, the `platform: node` setting represented Node.js, the configured package
manager (npm, pnpm, yarn), and JavaScript itself. That's 3 different layers of functionality in 1
implementation. In v2, these layers are now split into separate toolchains (plugins), and act
independently.
So if you had the following configuration in v2:
```yaml
tasks:
build:
# ...
toolchains: 'node'
```
This would _only_ apply the Node.js toolchain, and NOT the JavaScript toolchain, NOR the package
manager toolchain. This may cause unexpected issues. The correct configuration should be:
```yaml
tasks:
build:
# ...
toolchains: ['node', 'javascript', 'npm']
```
But adding all those toolchains is cumbersome, so we suggest omitting `toolchains` entirely and let
the detection do its thing.
### Other changes
- When `options.affectedFiles` is enabled, the list of files will be joined with the OS path
separator (`:` on Unix, `;` on Windows) instead of a comma (`,`) when passed as the
`MOON_AFFECTED_FILES` environment variable.
## Task inheritance
### Deep merged instead of shallow merged
In v1, when inheriting tasks, all global configs (those in `.moon`) were _shallow merged_ into a
single config ignoring merge task options, and _then_ merged with the local config (`moon.*`) using
merge task options. This was not intuitive, as users expected all configs to be merged in sequence.
To demonstrate this, take the following example configs, in order of inherited:
```yaml
# .moon/tasks.yml
tasks:
build:
command: 'build --cache'
options:
mutex: 'build'
# .moon/tasks/tag-a.yml
tasks:
build:
args: '--force'
# .moon/tasks/tag-b.yml
tasks:
build:
args: '--clean'
```
Users would expect the final `build` task to be `build --cache --force --clean` with the mutex
option set. However, since the `tasks` setting was shallow merged, only the last config (`tag-b`)
would be used, resulting in the task being `noop --clean` without the mutex. The `noop` pops up
because the `command` setting was not configured in the last config.
To remedy this, and to improve task composition overall, global configs are no longer shallow merged
into a single config before merging with the local config. Instead, all configs are merged in
sequence, while respecting the task merge options. This is the order of operations for the new
system:
- Load all global configs (`.moon`) into a list, in order, based on the `inheritedBy` setting.
- Load the local config (`moon.*`).
- Create an inheritance chain by resolving global configs first, then the local config last, while
respecting `extend` and other composition settings.
- Merge all task options in order, to create the final task options.
- Merge all tasks in order, using the final task options to guide the merging behavior.
### File groups are merged
Because of the new deep merging behavior, file groups defined in inherited configs are now merged
together, instead of being replaced by the following config in the sequence. For example, given the
configs:
```yaml
# .moon/tasks/tag-a.yml
fileGroups:
sources:
- 'src/**'
# .moon/tasks/tag-b.yml
fileGroups:
sources:
- 'docs/**'
```
In v1, the final `sources` file group would only include `docs/**`, as the second config would
replace the first. In v2, the final `sources` file group includes both `src/**` and `docs/**`.
## VCS
### New hooks system
We've rewritten our Git hooks from the ground up to be based around the `core.hooksPath` setting.
The following changes have been made:
- We no longer write hooks to the `.git/hooks` directory.
- Instead, all hooks are written to `.moon/hooks` and Git is configured to use this directory.
- Bash scripts no longer end in `.sh`.
:::caution
We currently don't have an easy way to clean the previous implementation of hooks. You may need to
manually remove the old scripts from `.git/hooks` and `.moon/hooks` if they are causing issues.
:::
## Other changes
### Changed (touched) files
We renamed the terminology "touched files" to "changed files" throughout the codebase and
documentation. This better aligns with common VCS terminology and reduces confusion. Because of
this, the following changes were made:
- Renamed the `moon query touched-files` CLI command to `moon query changed-files`.
- Renamed the `get_touched_files` MCP tool to `get_changed_files`.
- Renamed the `touchedFiles` run report field to `changedFiles`.
```shell
# Before
$ moon query touched-files
# After
$ moon query changed-files
```
### Docker
- The scaffolded `.moon/docker/workspace` directory was renamed to `.moon/docker/configs`.
- The `moon docker file` command will now loop through all toolchains and use the first image found,
otherwise it defaults to "scratch". If you want to be explicit, set the `docker.file.image`
setting.
### Query language (MQL)
- Renamed fields:
- `projectName` -> `projectId`
- `projectType` -> `projectLayer`
- `taskPlatform` -> `taskToolchain`
```shell
# Before
projectType=application && taskPlatform=node
# After
projectLayer=application && taskToolchain=node
```
### Webhooks
- Removed the `tool.*` events. Use `toolchain.*` events instead.
- Removed the `runtime` field from `dependencies.*` events. Use the `toolchain` field instead.
---
## activate
The `proto activate ` command will activate proto for the current shell session, by exporting
environment variables and prepending `PATH` for each tool configured in the current directory.
Activation is ran each time the current directory changes using a shell hook.
:::info
Learn more about
[shell activation in the official workflow documentation](../workflows#shell-activation)!
:::
### Arguments
- `` - The shell to activate for.
### Options
- `--export` - Print the activate instructions in shell-specific syntax.
- `--json` - Print the activate instructions in JSON format.
- `--no-bin` - Do not include `~/.proto/bin` when appending `PATH`.
- `--no-shim` - Do not include `~/.proto/shims` when prepending `PATH`.
- `--no-init` - Do not trigger activation when initialized in the shell, and instead wait for a
cd/prompt change.
### Caveats
- Only tools that have a [version configured in `.prototools`](../config#pinning-versions) will be
activated.
- Tool versions configured in the global `~/.proto/.prototools` are _not_ included by default. Pass
`--config-mode all` during activation to include them.
- Do note that this will worsen performance depending on the number of tools.
### Setup
The following activation steps should be added _after_ all environment variable and `PATH`
modifications have happened in your shell, typically at the end of your shell profile.
#### Bash
Add the following line to the end of your `~/.bashrc` or `~/.bash_profile`.
```shell
eval "$(proto activate bash)"
```
#### Elvish
Generate the hook:
```shell
proto activate elvish > ~/.elvish/lib/proto-hook.elv
```
Then add the following line to your `~/.elvish/rc.elv` file.
```shell
use proto-hook
```
#### Fish
Add the following line to the end of your `~/.config/fish/config.fish`.
```shell
proto activate fish | source
```
#### Murex
Add the following line to the end of your `~/.murex_profile`.
```shell
proto activate murex -> source
```
#### Nu
Generate the hook:
```shell
(proto activate nu) | save ~/.config/nushell/proto-hook.nu
```
Then add the following line to your `~/.config/nushell/config.nu` file.
```shell
use proto-hook.nu
```
#### Pwsh
Add the following line to the end of your profile (`$PROFILE`).
```shell
proto activate pwsh | Out-String | Invoke-Expression
```
#### Zsh
Add the following line to the end of your `~/.zshrc`.
```shell
eval "$(proto activate zsh)"
```
---
## alias
The `proto alias ` (or `proto a`) command will define a custom alias that
maps to a specific version for the provided tool. Aliases can be used anywhere a version is
accepted.
```shell
$ proto alias node work 16.16
```
By default this will update the local [`./.prototools`](../config) file. Pass `--to` to customize
the location.
### Arguments
- `` - Type of tool.
- `` - Name of the alias. Supports alphanumeric chars.
- `` - Version to map to the alias.
## Options
- `--to` - [Location of `.prototools`](../config#locations) to update. Supports `global`, `local`,
and `user`.
---
## bin(Commands)
The `proto bin [version]` command will return an absolute path to a tool's binary within the
toolchain. When a tool has not been installed, or a version cannot be resolved, the command will
exit with a failure.
```shell
$ proto bin node 16.10.0
/Users/example/.proto/tools/node/16.10.0/bin/node
```
This command can also return directories using the `--dir` option.
```shell
$ proto bin node 16.10.0 --dir exes
/Users/example/.proto/tools/node/16.10.0/bin
$ proto bin node 16.10.0 --dir globals
/Users/example/.proto/tools/node/globals/bin
```
### Arguments
- `` - Type of tool.
- `[version]` - Version of tool. If not provided, will attempt to
[detect the version](../detection).
### Options
- `--all` - Return multiple paths, separated by newlines, instead of the first path.
- `--dir ` - Return a directory instead of of the main file.
- `exes` - Returns the executable's directory.
- `globals` - Returns the globals/packages directory.
- `--bin` - When applicable, return the `~/.proto/bin` path.
- `--shim` - When applicable, return the `~/.proto/shims` path.
---
## clean(Commands)
The `proto clean` command can be used to uninstall stale and unused tools, plugins, and more. By
default, it will remove items that haven't been used in the last 30 days.
```shell
$ proto clean
```
Furthermore, the command can be used to target a specific artifact type.
```shell
$ proto clean plugins
```
> Cleaning stale tools does _not_ remove records from [lockfiles](../lockfile), as
> the version may still be required by a configuration, or in use on other machines.
### Arguments
- `[target]` - Type of target. Accepts `cache`, `plugins`, `temp`, or `tools`.
### Options
- `--days` - Number of days before a tool is considered stale.
- `--json` - Print the clean result in JSON format.
- `--yes` - Avoid and confirm all prompts.
---
## completions(Commands)
The `proto completions` command will generate proto command and argument completions for your
current shell. This command will write to stdout, which can then be redirected to a file of your
choice.
```shell
$ proto completions > ./path/to/write/to
```
### Options
- `--shell` - Shell to explicitly generate for.
### Examples
If using [bash-completion](https://github.com/scop/bash-completion).
```shell
mkdir -p ~/.bash_completion.d
proto completions > ~/.bash_completion.d/proto.sh
```
Otherwise write the file to a common location, and source it in your profile.
```shell
mkdir -p ~/.bash_completions
proto completions > ~/.bash_completions/proto.sh
# In your profile
source ~/.bash_completions/proto.sh
```
Write the file to Fish's completions directory.
```shell
mkdir -p ~/.config/fish/completions
proto completions > ~/.config/fish/completions/proto.fish
```
If using [oh-my-zsh](https://ohmyz.sh/) (the `_` prefix is required).
```shell
mkdir -p ~/.oh-my-zsh/completions
proto completions > ~/.oh-my-zsh/completions/_proto
# Reload shell (or restart terminal)
omz reload
```
---
## debug config
The `proto debug config` command will list all `.prototools` configuration files (in TOML format)
that have been loaded, in order of precedence, with the final merged configuration printed at the
end.
If [lockfiles](../../lockfile) are in use, `.protolock` files will be rendered
alongside their sibling configuration files.
```
$ proto debug config
/Users/name/.proto/.prototools ───────────────────────────────────────────
node = "20.0.0"
npm = "bundled"
[tools.node.aliases]
stable = "~20"
[settings]
auto-clean = false
Final configuration ───────────────────────────────────────────────────────
node = "20.0.0"
npm = "bundled"
[tools.node.aliases]
stable = "~20"
[plugins.tools]
node = "https://github.com/moonrepo/node-plugin/releases/download/v0.6.1/node_plugin.wasm"
[settings]
auto-clean = false
auto-install = false
detect-strategy = "first-available"
[settings.http]
allow-invalid-certs = false
proxies = []
```
### Options
- `--json` - Print the list in JSON format.
---
## debug env
The `proto debug env` command will print information about your current proto environment. Primarily
the store location, relevant file paths, and environment variables.
```
$ proto debug env
Store ─────────────────────────────────────────────────────────────────────
Root: /Users/name/.proto
Bins: /Users/name/.proto/bin
Shims: /Users/name/.proto/shims
Plugins: /Users/name/.proto/plugins
Tools: /Users/name/.proto/tools
Temp: /Users/name/.proto/temp
Environment ───────────────────────────────────────────────────────────────
Proto version: 0.44.0
Operating system: macos
Architecture: arm64
Config sources:
- /Users/name/Projects/example/.prototools
- /Users/name/.proto/.prototools
Virtual paths:
/userhome = /Users/name
/proto = /Users/name/.proto
Environment variables:
PROTO_APP_LOG = proto=info,schematic=info,starbase=info,warpgate=info,extism::pdk=info
PROTO_HOME = /Users/name/.proto
PROTO_OFFLINE_TIMEOUT = 750
PROTO_VERSION = 0.44.0
```
### Options
- `--json` - Print the list in JSON format.
---
## diagnose
The `proto diagnose` command will diagnose your proto installation for any potential issues. Issues
are categorized into errors and warnings, with the former being a must fix, and the latter being a
maybe fix (depending on your usage of proto).
```
$ proto diagnose
Shell: zsh
Shell profile: /Users/name/.zshrc
Errors ────────────────────────────────────────────────────────────────────
- Issue: Bin directory /Users/name/.proto/bin was found BEFORE the shims directory /Users/name/.proto/shims on PATH
Resolution: Ensure the shims path comes before the bin path in your shell
Comment: Runtime version detection will not work correctly unless shims are used
```
Among other checks, proto inspects your `PATH` ordering and shell setup, and looks for executable
name collisions — multiple configured tools that resolve to the same shim and bin name (for example,
the same identifier provided by two different backends). Since only one can be linked, the others
are silently shadowed, so these are reported as warnings.
If [lockfiles](../lockfile) are in use, proto will also audit their health, and
report warnings for records that are missing a resolved version, duplicate records for the same
specification, and stale records that no longer match a configured version.
### Options
- `--shell` - The shell to diagnose (will detect automatically).
- `--json` - Print the diagnosis in JSON format.
---
## exec(Commands)
The `proto exec -- ` (or `proto x`) command will activate a temporary
environment by loading and initializing any number of tools, and then execute an arbitrary command
within that environment.
```shell
$ proto exec node pnpm -- pnpm run dev
```
Tools will automatically detect a version to execute with based on loaded `.prototools`, but the
version can be provided inline by suffixing the tool with `@`.
```shell
$ proto exec node@24.2 pnpm@10 -- pnpm run dev
```
### Shell support
By default, the command will not be executed in a shell, and will be excuted in the context of the
parent process. If you want to execute the command within a shell (using `-c`), you can use the
`--shell` option.
```shell
$ proto exec node pnpm --shell bash --
```
If your command contains special characters, complex expressions, or shell specific syntax, you may
need to pass `--raw` to avoid quoting/escaping issues.
```shell
$ proto exec node pnpm --shell bash --raw --
```
Furthermore, if you want to launch an interactive shell session with the activated environment, you
can pass the shell command itself as the exec command.
```shell
$ proto exec node pnpm -- bash
```
### Arguments
- `` - List of tool identifiers with optional version.
- `` - Command to execute within the environment. Must be passed after a `--` separator.
### Options
- `--tools-from-config` - Inherit tools to initialize from `.prototools` configs, instead of passing
an explicit list.
- `--raw` - Execute the command as-is without quoting or escaping when using `--shell`.
- `--shell` - Shell to execute the command with (e.g. `bash` or `pwsh`).
---
## install
The `proto install` (or `proto i`) command can be used to install one or many tools.
### Installing all tools
The `proto install` command (without arguments) will download and install _all_ tools and plugins
from all parent [`.prototools`](../config) configuration files, and any
[versions detected](../detection) in the current working directory (if not defined in
`.prototools`).
```shell
$ proto install
```
> By default, this command _does not_ install tools for versions pinned in the global
> `~/.proto/.prototools` file. Pass `--config-mode all` to include them.
### Installing one tool
The `proto install [version]` command will download and install a single tool by unpacking
their archive to `~/.proto/tools/`. If the tool has already been installed, the command will
exit early.
The command is also smart enough to resolve partial versions, so 1, 1.2, and 1.2.3 are all
acceptable. It even supports aliases when applicable, like `latest`, `next`, `beta`, etc. To install
a canary release, use `canary`.
```shell
$ proto install deno
$ proto install deno 1.31
$ proto install deno canary
```
#### Pinning the version
By default this command will only install the tool into `~/.proto/tools` but will not make the
binary available. If you would like to also pin the resolved version to a `.prototools` file, use
the `--pin` option.
```shell
# ./.prototools
$ proto install bun --pin
$ proto install bun --pin local
# ~/.proto/.prototools
$ proto install bun --pin global
# ~/.prototools
$ proto install bun --pin user
```
### Handling plugin hooks
Some tools run [post-install hooks](../tools) that support arbitrary arguments that can be passed
after `--`.
```shell
$ proto install go -- --no-gobin
```
### Arguments
- One tool
- `[tool]` - Type of tool.
- `[version]` - Version of tool. Defaults to a pinned version in `.prototools` or "latest".
- `[-- ]` - Additional arguments to pass to post-install hooks.
### Options
- `--force` - Force install, even if already installed.
- `--update-lockfile` - Don't inherit a version from the [lockfile](../lockfile) and update the
existing record.
- One tool
- `--build` - Build from source if available.
- `--no-build` - Download a pre-built if available.
- `--pin` - Pin the resolved version and create a symlink in `~/.proto/bin`. Accepts a boolean
(pins locally by default), or the string "global", or the string "local".
---
## list-remote
:::danger
This command was removed in v0.44, use [`proto versions`](./versions) instead!
:::
The `proto list-remote ` (or `proto lsr`) command will list available versions by resolving
versions from the tool's remote release manifest.
```shell
$ proto list-remote node
...
18.10.0
18.11.0
18.12.0
18.12.1
18.13.0
18.14.0
18.14.1
18.14.2
19.0.0
19.0.1
19.1.0
19.2.0
19.3.0
19.4.0
19.5.0
19.6.0
19.6.1
19.7.0
```
### Arguments
- `