diff --git a/docs/configuration/overview.mdx b/docs/configuration/overview.mdx
index 35386bd5..5c54844f 100644
--- a/docs/configuration/overview.mdx
+++ b/docs/configuration/overview.mdx
@@ -110,10 +110,24 @@ ignore:
- 'packages/**/example'
```
-
- You can also expand the scope of packages on a per-command basis via the
- [`--scope` filter](/filters#--scope) flag.
-
+## categories
+
+Categories are used to group packages together.
+
+To define custom package categories, add a `categories` section in your `melos.yaml` file.
+Under this section, you can specify category names as keys, and their corresponding values
+should be lists of glob patterns that match the packages you want to include in each category.
+
+```yaml
+# melos.yaml
+
+categories:
+ examples:
+ - packages/example*
+ alpha:
+ - packages/feature_a/*
+ - packages/feature_b
+```
## ide/intellij
diff --git a/docs/filters.mdx b/docs/filters.mdx
index d3bc8732..171edd06 100644
--- a/docs/filters.mdx
+++ b/docs/filters.mdx
@@ -46,6 +46,15 @@ repeated.
melos exec --ignore="*internal*" -- flutter build ios
```
+## --categories
+
+Filter packages based on categories declared in the `melos.yaml` file.
+
+```bash
+# Run `flutter build ios` on all packages in the "examples" category.
+melos exec --category="examples" -- flutter build ios
+```
+
## --diff
Filter packages based on whether there were changes between a commit and the
diff --git a/melos.yaml b/melos.yaml
index e17f1354..411b2f4e 100644
--- a/melos.yaml
+++ b/melos.yaml
@@ -5,6 +5,9 @@ packages:
- packages/*
ignore:
- packages/melos_flutter_deps_check
+categories:
+ testando:
+ - packages/*
command:
bootstrap:
diff --git a/packages/melos/lib/src/command_runner/base.dart b/packages/melos/lib/src/command_runner/base.dart
index 9f604d84..48ed0b7c 100644
--- a/packages/melos/lib/src/command_runner/base.dart
+++ b/packages/melos/lib/src/command_runner/base.dart
@@ -78,6 +78,14 @@ abstract class MelosCommand extends Command {
'option can be repeated.',
);
+ argParser.addMultiOption(
+ filterOptionCategory,
+ valueHelp: 'glob',
+ help:
+ 'Include only packages with categories matching the given glob. This '
+ 'option can be repeated.',
+ );
+
argParser.addMultiOption(
filterOptionIgnore,
valueHelp: 'glob',
@@ -151,6 +159,7 @@ abstract class MelosCommand extends Command {
final diff = diffEnabled ? argResults![filterOptionDiff] as String? : null;
final scope = argResults![filterOptionScope] as List? ?? [];
+ final categories = argResults![filterOptionCategory] as List? ?? [];
final ignore = argResults![filterOptionIgnore] as List? ?? [];
return PackageFilters(
@@ -161,6 +170,9 @@ abstract class MelosCommand extends Command {
.map((e) => createGlob(e, currentDirectoryPath: workingDirPath))
.toList()
..addAll(config.ignore),
+ categories: categories
+ .map((e) => createGlob(e, currentDirectoryPath: workingDirPath))
+ .toList(),
diff: diff,
includePrivatePackages: argResults![filterOptionPrivate] as bool?,
published: argResults![filterOptionPublished] as bool?,
diff --git a/packages/melos/lib/src/common/utils.dart b/packages/melos/lib/src/common/utils.dart
index bcc8a4ac..bbc75f37 100644
--- a/packages/melos/lib/src/common/utils.dart
+++ b/packages/melos/lib/src/common/utils.dart
@@ -26,6 +26,7 @@ const globalOptionSdkPath = 'sdk-path';
const autoSdkPathOptionValue = 'auto';
const filterOptionScope = 'scope';
+const filterOptionCategory = 'category';
const filterOptionIgnore = 'ignore';
const filterOptionDirExists = 'dir-exists';
const filterOptionFileExists = 'file-exists';
diff --git a/packages/melos/lib/src/common/validation.dart b/packages/melos/lib/src/common/validation.dart
index 688e3538..6d741f14 100644
--- a/packages/melos/lib/src/common/validation.dart
+++ b/packages/melos/lib/src/common/validation.dart
@@ -89,6 +89,26 @@ List assertListIsA({
];
}
+Map assertMapIsA({
+ String? path,
+ required Object key,
+ required Map