Workspace scripts can be executed with melos run or will be executed as
lifecycle hooks of some specific Melos commands.
Remember, the scripts go in the scripts section under the melos section in
the root pubspec.yaml file. For readability the melos section is omitted in
all examples below except for the first one.
With the simple syntax, only the name of the script and the command to execute needs to be specified:
melos:
scripts:
hello: echo 'Hello World'The extends syntax allows for a more complex configuration of a script:
scripts:
hello:
name: hey
description: Greet the world
run: echo "$GREETING World"
env:
GREETING: 'Hey'Scripts are executed in a shell. On Windows the shell is cmd.exe and on all
other platforms it is sh.
Variables defined in a script's env block (as well as the injected MELOS_*
variables) can be referenced with $GREETING, ${GREETING} or %GREETING%.
Use $GREETING or ${GREETING} for cross-platform scripts.
On sh (macOS, Linux) the shell expands $GREETING and ${GREETING} itself,
so normal shell quoting rules apply: single quotes prevent expansion
(echo '$GREETING' prints $GREETING literally) while double quotes allow it,
and values are treated as data rather than being re-parsed, so an env value
containing characters such as & or | does not alter the command.
On Windows the command is handed to cmd.exe through an intermediate
environment variable, which cmd only expands once, so Melos substitutes the
value directly for the reference. This means the expansion happens regardless of
quoting, and cmd.exe's usual caveats around special characters in values
apply.
If multiple commands are being executed in a script and no further commands
should be executed after a command has failed, connect the commands with &&:
scripts:
prepare: melos bootstrap && melos run buildA unique identifier for the script.
A short description, shown when using melos run with no argument.
The command to execute.
Controls how the script's process is connected to Melos's own input and output streams. Accepts one of:
pipe(default) — Melos captures the script'sstdoutandstderrso it can prefix the output with the script name. The script'sstdinis not connected.inherit— the script inherits Melos'sstdin,stdout, andstderrdirectly, giving it a real terminal (TTY). Use this for interactive commands that need a controlling terminal, such astmux attach,vim,less, or the hot-reload key handlers influtter run/dart_frog dev. In this mode Melos does not prefix the script's output.
scripts:
attach:
run: tmux attach -t my_session
stdio: inheritstdio: inherit runs a single foreground process attached to your terminal, so
it cannot be combined with exec (which runs the
script across multiple packages) or steps
(which run inside a shared shell). Set stdio: inherit on the individual
scripts referenced from steps instead.
Steps enables the combination of multiple scripts within a single script definition for complex workflows. In the example below, the pre-commit script is configured to sequentially invoke a simple command echo 'hello world', followed by the Melos command to format your workspace and a command to run tests through Dart, each command with their specific arguments.
scripts:
pre-commit:
description: pre-commit git hook script
steps:
- echo 'hello world'
- format --output none --set-exit-if-changed
- dart test --fail-fast .Note: When utilizing the steps, it's important to understand that options
related to exec — such as concurrency — or packageFilters cannot be directly
applied within the steps configuration. Instead, these options should be
configured in the individual scripts that are being called as part of the step.
The names of the scripts that have to run successfully before the script runs.
In the example below, melos run test first runs generate, and only runs the
tests if generate succeeded.
scripts:
generate:
exec: dart run build_runner build
test:
exec: dart test
dependsOn:
- generateThe dependencies form a graph, which is more flexible than a sequential list of
steps:
- The scripts that a script depends on can depend on other scripts themselves. All of them run before the requested script, each one after the scripts that it depends on.
- A script that multiple scripts depend on only runs once when the dependencies
of the requested script are resolved. A script that is called from
stepsresolves its own dependencies, so a dependency that is shared between multiple steps runs once for each of them. - If one of the scripts fails, the scripts that come after it do not run.
- A script can consist of only
dependsOn, withoutrun,execorsteps, to group other scripts under a single name. TheenvandpackageFiltersof such a script are not applied to the scripts that it depends on.
scripts:
generate: dart run tool/generate.dart
build:
run: dart run tool/build.dart
dependsOn:
- generate
test:
exec: dart test
dependsOn:
- generate
# Runs generate, build and test, with generate only running once.
verify:
dependsOn:
- build
- testExtra arguments that are passed to melos run are only passed to the requested
script, not to the scripts that it depends on. Package filters that are passed
on the command line, such as --scope, apply to all of the scripts. A script
that is depended on is skipped with a warning if no package matches its filters,
since it has nothing to do for the selected packages.
Private scripts can be used as dependencies.
Melos reports an error if a script depends on a script that does not exist, or
if the dependencies form a cycle. dependsOn is not supported for
hooks.
Execute a script in multiple packages through melos exec.
run and exec are mutually exclusive: a script either runs once in the
workspace root with run, or across multiple
packages with exec.
When using the default options for melos exec, specify the command directly as
the value of exec:
scripts:
hello:
exec: echo 'Hello $(dirname $PWD)'If you need to provide options for the exec command, turn exec into an object
and specify the command with the command key alongside the options:
scripts:
hello:
exec:
command: echo 'Hello $(dirname $PWD)'
concurrency: 1Prior to Melos 8.0.0, the command was specified in run and the options in
exec. This is no longer supported. Move the command into exec under the
command key. See the
migration guide for details.
See the packageFilters option for
filtering the packages to execute the command in.
The command to execute in each package. Required when exec is an object.
Defines the maximum concurrency value, i.e., the maximum number of packages that the script will execute in concurrently at any given time. By default, this value is set to the number of processors available on the underlying machine.
Whether exec should fail fast and not execute the script in further packages
if the script fails in an individual package. Defaults to false.
Whether exec should order the execution of the script in multiple packages
based on the dependency graph of the packages. The script will be executed in
leaf packages first and then in packages that depend on them and so on. This is
useful for example, for a script that generates code in multiple packages, which
depend on each other. Defaults to false.
Whether the output of the packages should be buffered and printed grouped per
package once every package has finished, instead of being streamed and
interleaved while the packages run. The output of packages in which the script
failed is printed last. Defaults to false.
As with the command line flag, this only has an effect when the script runs in
more than one package with a concurrency greater than 1 — with
concurrency: 1 it is a no-op.
See melos exec --group-logs for more details.
A list of globs, relative to the root of each package, that match the files the result of the script depends on. The script is skipped in the packages in which none of the matching files, including those of their dependencies in the workspace, changed since the script last succeeded in them.
melos:
scripts:
generate:
exec:
command: dart run build_runner build
orderDependents: true
sources:
- lib/**.dart
- pubspec.yamlA script that generates files that match the sources, like the one above,
should also specify orderDependents. Scripts that do not
change the sources, such as running tests, do not need it.
Use melos run --run-unchanged to run the
script in every package regardless, or
melos run --ignore-sources to turn off the
checksums completely.
See melos exec --sources for more details.
A map of environment variables that will be passed to the executed command.
Selects the packages a script applies to. When running a script that uses
packageFilters, you are prompted to select one of the matching packages, or
all of them. Use the --no-select option to skip
the prompt and select all matching packages.
See the global options for a list of supported filters.
The filter names in packageFilters are camel cased. For example, for the
equivalent of the command line option --file-exists use fileExists.
For an exec script, the filters decide in which
packages the command is executed. The hello_flutter script below is only
executed in Flutter packages:
scripts:
hello_flutter:
exec: echo 'Hello $(dirname $PWD)'
packageFilters:
flutter: trueA run script always executes once, in the
workspace root. packageFilters does not change that. What it does is pass the
selected packages to the script through the
MELOS_PACKAGES environment variable,
which scopes any melos command the script itself invokes.
This is useful when the script wraps a Melos command:
scripts:
analyze_with_lib:
run: melos exec -- dart analyze
packageFilters:
dirExists:
- libSelecting a package in the prompt above scopes the nested melos exec, so
dart analyze runs in that package.
If the command is not a melos command, nothing reads MELOS_PACKAGES, so
the filters have no effect beyond the prompt.
The script below always runs dart analyze once in the workspace root,
whichever package is selected in the prompt:
scripts:
analyze:
run: dart analyze
packageFilters:
dirExists:
- libTo analyze each package individually, use
exec instead of run.
This option allows you to mark scripts as private. Private scripts cannot be executed directly and are hidden from the command prompt.
They can only be referenced as steps within other scripts, or in the
dependsOn of other scripts.
If you use the --include-private flag, this restriction is ignored.
Defines one or more groups for the specified script.
Scripts can be organized into logical groups, which can later be targeted or filtered
using the --group option when running commands. This is useful for hiding or executing
only scripts that belong to certain groups.
Certain Melos commands support running scripts before and after the command is executed, as well as at other interesting points in the command's execution.
All commands that support hooks, support at least the pre and post hook.
Hooks are configured in the hooks section of a command's configuration in the
root pubspec.yaml file.
command:
bootstrap:
hooks:
pre: echo `bootstrap command is running...`
post: echo `bootstrap command is done`Currently, the following Melos commands support hooks:
Custom scripts can override Melos built-in commands. When a custom script has the same name as a built-in command, the custom script takes precedence. This allows you to customize the behavior of built-in commands to match your workspace needs.
For example, you can override the built-in format command:
scripts:
format: dart run custom_formatterWith this configuration, both melos format and melos run format will execute
your custom format script instead of the built-in format command.

