Skip to main content

moon v0.23 - Scoped task inheritance, and project config updates

· 6 min read
Miles Johnson

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

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!

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, with per-project tasks still being an option with moon.yml.

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

  • With the addition of new programming languages, 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 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".

.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 or type using the new .moon/tasks/<language>.yml or .moon/tasks/<language>-<type>.yml configuration files! Jump to the official documentation on 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.

.moon/tasks/node.yml
tasks:
format:
command: 'prettier --write .'

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 and .moon/tasks/*.yml 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:

.moon/tasks/node.yml
implicitInputs:
- 'package.json'

Project-level environment variables

Since moon's inception, tasks can be configured with pre-defined environment variables using the env 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 using the top-level 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:

<project>/moon.yml
tasks:
dev:
# ...
env:
TARGET_ENV: 'development'

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

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

Can be rewritten as:

<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, 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).

moon.yml
tasks:
build:
command: 'webpack'
outputs:
- 'build/**/*.{js,css}'

Other changes

View the official release 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.