diff --git a/packages/melos/lib/src/scripts.dart b/packages/melos/lib/src/scripts.dart index 9142d339..66d66b42 100644 --- a/packages/melos/lib/src/scripts.dart +++ b/packages/melos/lib/src/scripts.dart @@ -24,6 +24,9 @@ import 'common/utils.dart'; import 'common/validation.dart'; import 'package.dart'; +// https://regex101.com/r/44dzaz/1 +final _leadingMelosExecRegExp = RegExp(r'^\s*melos\s+exec'); + /// Scripts to be executed before/after a melos command. class LifecycleHook { LifecycleHook._({required this.pre, required this.post}); @@ -83,6 +86,14 @@ class Scripts extends MapView { ); } + /// Validates the scripts. Throws a [MelosConfigException] if any script is + /// invalid. + void validate() { + for (final script in values) { + script.validate(); + } + } + Map toJson() { return { for (final entry in entries) entry.key: entry.value.toJson(), @@ -300,6 +311,21 @@ class Script { return run; } + /// Validates the script. Throws a [MelosConfigException] if the script is + /// invalid. + void validate() { + if (exec != null && run.startsWith(_leadingMelosExecRegExp)) { + throw MelosConfigException( + 'Do not use "melos exec" in "run" when also providing options in ' + '"exec". In this case the script in "run" is already being executed by ' + '"melos exec".\n' + 'For more information, see https://melos.invertase.dev/configuration/scripts#scriptsexec.\n' + '\n' + ' run: $run', + ); + } + } + Map toJson() { return { 'name': name, diff --git a/packages/melos/lib/src/workspace_configs.dart b/packages/melos/lib/src/workspace_configs.dart index 363aa90c..36cbee62 100644 --- a/packages/melos/lib/src/workspace_configs.dart +++ b/packages/melos/lib/src/workspace_configs.dart @@ -860,6 +860,8 @@ You must have one of the following to be a valid Melos workspace: 'repository must be specified if commands/version/linkToCommits is true', ); } + + scripts.validate(); } /// Validates the physical workspace on the file system. diff --git a/packages/melos/test/workspace_config_test.dart b/packages/melos/test/workspace_config_test.dart index ce8d85f2..d3d4519b 100644 --- a/packages/melos/test/workspace_config_test.dart +++ b/packages/melos/test/workspace_config_test.dart @@ -453,6 +453,23 @@ void main() { throwsA(isA()), ); }); + + test('throws when using "melos exec" in "run" and specifying "exec"', () { + expect( + () => Scripts.fromYaml( + createYamlMap({ + 'a': { + 'run': 'melos exec a', + 'exec': { + 'concurrency': 1, + }, + }, + }), + workspacePath: testWorkspacePath, + ).validate(), + throwsA(isA()), + ); + }); }); });