# 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 VS Code - Sidebar icon 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. DevTools Profiler - CPU 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. DevTools Profiler - CPU snapshot loaded 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. DevTools Profiler - CPU snapshot being analyzed through charts ## 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. DevTools Profiler - Heap 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. DevTools Profiler - Heap snapshot loaded 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. DevTools Profiler - Heap snapshot being analyzed through charts ## 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. Bottom up profiler view ### 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. Top down profiler view ### 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. Flame chart profiler view --- ## 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: -