---
slug: v0.23
title: moon v0.23 - Scoped task inheritance, and project config updates
authors: [milesj]
tags: [survey, tasks, projects]
image: ./img/v0.23.png
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import Button from '@site/src/ui/Button';

With this release, we're launching the next iteration of our task inheritance model, as well as
quality of life improvements for project configuration.

<!--truncate-->

## Developer survey

Before we dive into this new release, we have a quick survey for everyone. We know how everyone
feels about surveys, but this one is real quick, only a few minutes, and is mostly multiple choice
questions.

We're looking for feedback on moon itself, what features you're looking for, what you currently do
not like, how you're currently using monorepos, your development workflows, so on and so forth. We'd
very much appreciate it if you could engage with this survey!

<div class="flex justify-center">
  <Button
    label="Take survey!"
    href="https://a.sprig.com/UE1SOG1zV3o5SzdRfnNpZDpmOTQ5MjU1Yy1jYTZlLTRmYjQtOTRjZi0wMzZlZjExN2JjZDg="
    size="lg"
  />
</div>

## Improved task inheritance model

One of the guiding principles behind moon is to simplify repository maintenance, with task
management being top of list. We weren't happy with the current state of things, as every build
system and task runner that exists always opted for per-project task management, which is a massive
amount of overhead and tech debt in the long run. To combat this, moon was designed from the
ground-up using a task inheritance model, where "global" tasks were defined in
[`.moon/project.yml`](/docs/config/tasks), with per-project tasks still being an option with
[`moon.yml`](/docs/config/project).

While inheritance worked great, it did have some shortcomings, such as:

- With the addition of [new programming languages](/blog/v0.21), there's no way to easily define
  tasks for specific languages, that should only be inherited by specific projects.
- There's no way to differentiate tasks between applications or libraries, as they typically have
  different build/compilation systems.
- All of the problems above can be "solved" with
  [`workspace.inheritedTasks`](/docs/config/project#inheritedtasks) in all projects, but it's a
  maintenance headache.

We've been documenting a solution to these problems for many months now, and we're very excited to
finally release our new and improved task inheritance model that solves all of the problems above,
and opens the doors for future enhancements! Keep reading for more information.

### New `.moon/tasks.yml` (breaking)

To start, we renamed `.moon/project.yml` to `.moon/tasks.yml` as we want to emphasize that this
configuration file is for task inheritance functionality only. However, the semantics of this file
has _not_ changed, and is still "tasks to be inherited by _all_ projects".

```yaml title=".moon/tasks.yml"
$schema: 'https://moonrepo.dev/schemas/tasks.json'

tasks:
  # ...
```

> We'll automatically rename this file for you when running a `moon` command!

### New scoped tasks with `.moon/tasks/*.yml`

The biggest change to task inheritance is that tasks can now be scoped by a project's
[`language`](/docs/config/project#language) or [`type`](/docs/config/project#type) using the new
`.moon/tasks/<language>.yml` or `.moon/tasks/<language>-<type>.yml` configuration files! Jump to the
[official documentation on task inheritance](/docs/concepts/task-inheritance) for more information
on how scoping works, the lookup order of files, and much more.

As a demonstration, you can scope tasks to Node.js projects with `.moon/tasks/node.yml`, Rust
applications with `.moon/tasks/rust-application.yml`, Go libraries with
`.moon/tasks/go-library.yml`, Ruby scripts with `.moon/tasks/ruby-tool.yml`, so on and so forth!

We're very excited for this feature, as it's something we personally needed, and we're sure you all
do as well. It also future proofs moon for new programming languages, additional implicit scenarios
to handle, and yet to be discovered functionality.

<Tabs
  groupId="scoped-task"
  defaultValue="node"
  values={[
    { label: 'Node', value: 'node' },
    { label: 'Go', value: 'go' },
    { label: 'PHP', value: 'php' },
    { label: 'Python', value: 'python' },
    { label: 'Ruby', value: 'ruby' },
    { label: 'Rust', value: 'rust' },
  ]}
>
<TabItem value="node">

```yaml title=".moon/tasks/node.yml"
tasks:
  format:
    command: 'prettier --write .'
```

</TabItem>
<TabItem value="go">

```yaml title=".moon/tasks/go.yml"
tasks:
  format:
    command: 'go fmt'
```

</TabItem>
<TabItem value="php">

```yaml title=".moon/tasks/php.yml"
tasks:
  format:
    command: 'phpcbf .'
```

</TabItem>
<TabItem value="python">

```yaml title=".moon/tasks/python.yml"
tasks:
  format:
    command: 'pylint .'
```

</TabItem>
<TabItem value="ruby">

```yaml title=".moon/tasks/ruby.yml"
tasks:
  format:
    command: 'rubocop -l'
```

</TabItem>
<TabItem value="rust">

```yaml title=".moon/tasks/rust.yml"
tasks:
  format:
    command: 'cargo fmt --all --check'
```

</TabItem>
</Tabs>

### Moved `implicitDeps` and `implicitInputs` (breaking)

To standardize inheritance and expansion related functionality, we've moved the
`runner.implicitDeps` and `runner.implicitInputs` settings from `.moon/workspace.yml` to
[`.moon/tasks.yml`](/docs/config/tasks#implicitdeps) and
[`.moon/tasks/*.yml`](/docs/config/tasks#implicitinputs) and removed the `runner` prefix.

This allows for implicits to also be scoped accordingly and granularly. For example, projects can
now inherit dependency manager related files as implicit inputs on a per-language basis:

<Tabs
  groupId="scoped-task"
  defaultValue="node"
  values={[
    { label: 'Node', value: 'node' },
    { label: 'Go', value: 'go' },
    { label: 'PHP', value: 'php' },
    { label: 'Python', value: 'python' },
    { label: 'Ruby', value: 'ruby' },
    { label: 'Rust', value: 'rust' },
  ]}
>
<TabItem value="node">

```yaml title=".moon/tasks/node.yml"
implicitInputs:
  - 'package.json'
```

</TabItem>
<TabItem value="go">

```yaml title=".moon/tasks/go.yml"
implicitInputs:
  - 'go.mod'
```

</TabItem>
<TabItem value="php">

```yaml title=".moon/tasks/php.yml"
implicitInputs:
  - 'composer.json'
```

</TabItem>
<TabItem value="python">

```yaml title=".moon/tasks/python.yml"
implicitInputs:
  - 'pyproject.toml'
```

</TabItem>
<TabItem value="ruby">

```yaml title=".moon/tasks/ruby.yml"
implicitInputs:
  - 'Gemfile'
```

</TabItem>
<TabItem value="rust">

```yaml title=".moon/tasks/rust.yml"
implicitInputs:
  - 'Cargo.toml'
```

</TabItem>
</Tabs>

## Project-level environment variables

Since moon's inception, tasks can be configured with pre-defined environment variables using the
[`env`](/docs/config/project#env-1) setting. These variables would then be passed to the command
during execution. This works perfectly for encapsulation, but becomes tedious when the same
variables are repeated for multiple tasks.

To remedy this, environment variables can now be defined at the top of
[`moon.yml`](/docs/config/project) using the top-level [`env`](/docs/config/project#env) setting.
Variables defined at the top-level will be inherited by all tasks in the current project, but will
not override task-level variables of the same name.

To demonstrate this, the following config:

```yaml title="<project>/moon.yml"
tasks:
  dev:
    # ...
    env:
      TARGET_ENV: 'development'

  build:
    # ...
    env:
      TARGET_ENV: 'development'

  serve:
    # ...
    env:
      TARGET_ENV: 'development'
```

Can be rewritten as:

```yaml title="<project>/moon.yml"
env:
  TARGET_ENV: 'development'

tasks:
  dev:
    # ...

  build:
    # ...

  serve:
    # ...
```

## Globs in task outputs

Another feature that's been around since moon's inception is task
[`outputs`](/docs/config/project#outputs), which only supported relative files and folders. For
historical reasons, it was the easiest solution at the time, but in practice, supporting more
granular control is better.

As such, task `outputs` now support glob patterns as well! This is perfect for restricting and
filtering down which files are cached in the artifact. However, be aware that during hydration (a
cache hit), all files _not matching the glob_ will be deleted, so ensure that critical files _do_
match.

To demonstrate this, if building a JavaScript project, you may want to include `.js` and `.css`
files, but exclude everything else (`.map`, etc).

```yaml title="moon.yml" {4,5}
tasks:
  build:
    command: 'webpack'
    outputs:
      - 'build/**/*.{js,css}'
```

## Other changes

View the
[official release](https://github.com/moonrepo/moon/releases/tag/%40moonrepo%2Fcli%400.23.0) for a
full list of changes.

- Updated `moon migrate from-turborepo` to preserve globs in outputs.
- Updated project graph to no longer cache when there's no VCS root.
- Updated pnpm to use the new `pnpm dedupe` command when the version is >= 7.26.0.

## What's next?

Expect the following in the v0.24 release!

- New `moon query tasks` command.
- New per-project `platform` setting.
- Token support in task `outputs`.
- TypeScript v5 support.
