From da2c275d010f4276d6c2805796080a84cee7bf04 Mon Sep 17 00:00:00 2001 From: Gabriel Terwesten Date: Tue, 20 Dec 2022 19:40:22 +0100 Subject: [PATCH] Move `dependencyOverridePaths` to `command/bootstrap` --- docs/configuration/overview.mdx | 37 ++++---- .../melos/lib/src/commands/bootstrap.dart | 3 +- packages/melos/lib/src/package.dart | 11 --- packages/melos/lib/src/workspace.dart | 21 +++-- packages/melos/lib/src/workspace_configs.dart | 93 ++++++++++++------- packages/melos/test/utils.dart | 2 +- .../melos/test/workspace_config_test.dart | 21 ++++- 7 files changed, 111 insertions(+), 77 deletions(-) diff --git a/docs/configuration/overview.mdx b/docs/configuration/overview.mdx index 095b71a6d..959664a72 100644 --- a/docs/configuration/overview.mdx +++ b/docs/configuration/overview.mdx @@ -8,8 +8,9 @@ description: Configure Melos using the `melos.yaml` file. Every project requires a `melos.yaml` project in the root. The below outlines all the configurable fields and their purpose. -Additionally, projects may include a `melos_overrides.yaml` file to override any `melos.yaml` field. This is useful for -making untracked customizations to a project. +Additionally, projects may include a `melos_overrides.yaml` file to override any +`melos.yaml` field. This is useful for making untracked customizations to a +project. ## `name` @@ -112,22 +113,6 @@ ignore: > You can also expand the scope of ignored packages on a per-command basis via > the [`--scope` filter](/filters#scope) flag. -## `dependencyOverrides` - -> optional - -A list of extra packages in the workspace directory that should be added to each workspace package's dependency -overrides. Each entry can be a specific path or a [glob] pattern. - -Note that this option has a camelCase key, unlike the snake_case key used in `pubspec.yaml`. - -Tip: External local packages can be symlinked into the workspace directory. - -```yaml -dependencyOverrides: - - 'external_dependencies/**' -``` - ## `ide` > optional @@ -174,6 +159,22 @@ Configuration relating to specific Melos commands such as versioning. Configuration for the `bootstrap` command. +### `command/bootstrap/dependencyOverridePaths` + +> optional + +A list of paths to local packages realtive to the workspace directory that +should be added to each workspace package's dependency overrides. Each entry can +be a specific path or a [glob] pattern. + +**Tip:** External local packages can be referenced using paths relative to the +workspace root. + +```yaml +dependencyOverridePaths: + - '../external_project/packages/**' +``` + ### `command/bootstrap/runPubGetInParallel` Whether to run `pub get` in parallel during bootstrapping. diff --git a/packages/melos/lib/src/commands/bootstrap.dart b/packages/melos/lib/src/commands/bootstrap.dart index 747e7eb2c..8e8a0fdc3 100644 --- a/packages/melos/lib/src/commands/bootstrap.dart +++ b/packages/melos/lib/src/commands/bootstrap.dart @@ -113,7 +113,8 @@ mixin _BootstrapMixin on _CleanMixin { } // Add custom workspace overrides. - for (final dependencyOverride in workspace.dependencyOverrides.values) { + for (final dependencyOverride + in workspace.dependencyOverridePackages.values) { melosDependencyOverrides[dependencyOverride.name] = PathReference( utils.relativePath(dependencyOverride.path, package.path), ); diff --git a/packages/melos/lib/src/package.dart b/packages/melos/lib/src/package.dart index 9bd23f6ad..2dfcfe7a7 100644 --- a/packages/melos/lib/src/package.dart +++ b/packages/melos/lib/src/package.dart @@ -615,17 +615,6 @@ The packages that caused the problem are: _logger, ); } - - /// Creates a new [PackageMap] containing the contents of the [overlay] - /// [PackageMap] combined with this [PackageMap]. Any entries in the original - /// with duplicate keys existing in the [overlay] will be overwritten. - /// - /// The [MelosLogger] from this [PackageMap] will be used in the resultant - /// [PackageMap]. - PackageMap operator +(PackageMap overlay) => PackageMap( - {..._map, ...overlay._map}, - _logger, - ); } extension on Iterable { diff --git a/packages/melos/lib/src/workspace.dart b/packages/melos/lib/src/workspace.dart index f021e1f12..38c66c1c4 100644 --- a/packages/melos/lib/src/workspace.dart +++ b/packages/melos/lib/src/workspace.dart @@ -48,7 +48,7 @@ class MelosWorkspace { required this.config, required this.allPackages, required this.filteredPackages, - required this.dependencyOverrides, + required this.dependencyOverridePackages, required this.sdkPath, required this.logger, }); @@ -66,9 +66,9 @@ class MelosWorkspace { ignore: workspaceConfig.ignore, logger: logger, ); - final dependencyOverrides = await PackageMap.resolvePackages( + final dependencyOverridePackages = await PackageMap.resolvePackages( workspacePath: workspaceConfig.path, - packages: workspaceConfig.dependencyOverrides, + packages: workspaceConfig.commands.bootstrap.dependencyOverridePaths, ignore: const [], logger: logger, ); @@ -82,7 +82,7 @@ class MelosWorkspace { allPackages: allPackages, logger: logger, filteredPackages: filteredPackages, - dependencyOverrides: dependencyOverrides, + dependencyOverridePackages: dependencyOverridePackages, sdkPath: resolveSdkPath( configSdkPath: workspaceConfig.sdkPath, envSdkPath: currentPlatform.environment[utils.envKeyMelosSdkPath], @@ -104,15 +104,20 @@ class MelosWorkspace { /// Configuration as defined in the "melos.yaml" file if it exists. final MelosWorkspaceConfig config; - /// All packages according to [MelosWorkspaceConfig]. + /// All packages managed in this Melos workspace. /// - /// Packages filtered by [MelosWorkspaceConfig.ignore] are not included. + /// Packages specified in [MelosWorkspaceConfig.packages] are included, + /// except for those specified in [MelosWorkspaceConfig.ignore]. final PackageMap allPackages; + /// The packages in this Melos workspace after applying filters. + /// + /// Filters are typically specified on the command line. final PackageMap filteredPackages; - /// Packages to use as dependency overrides. - final PackageMap dependencyOverrides; + /// The packages specified in + /// [BootstrapCommandConfigs.dependencyOverridePaths]. + final PackageMap dependencyOverridePackages; late final IdeWorkspace ide = IdeWorkspace._(this); diff --git a/packages/melos/lib/src/workspace_configs.dart b/packages/melos/lib/src/workspace_configs.dart index 0c06b842a..363aa90c9 100644 --- a/packages/melos/lib/src/workspace_configs.dart +++ b/packages/melos/lib/src/workspace_configs.dart @@ -168,7 +168,10 @@ class CommandConfigs { ); return CommandConfigs( - bootstrap: BootstrapCommandConfigs.fromYaml(bootstrapMap ?? const {}), + bootstrap: BootstrapCommandConfigs.fromYaml( + bootstrapMap ?? const {}, + workspacePath: workspacePath, + ), version: VersionCommandConfigs.fromYaml( versionMap ?? const {}, workspacePath: workspacePath, @@ -216,9 +219,13 @@ class BootstrapCommandConfigs { const BootstrapCommandConfigs({ this.runPubGetInParallel = true, this.runPubGetOffline = false, + this.dependencyOverridePaths = const [], }); - factory BootstrapCommandConfigs.fromYaml(Map yaml) { + factory BootstrapCommandConfigs.fromYaml( + Map yaml, { + required String workspacePath, + }) { final runPubGetInParallel = assertKeyIsA( key: 'runPubGetInParallel', map: yaml, @@ -233,9 +240,26 @@ class BootstrapCommandConfigs { ) ?? false; + final dependencyOverridePaths = assertListIsA( + key: 'dependencyOverridePaths', + map: yaml, + isRequired: false, + assertItemIsA: (index, value) => assertIsA( + value: value, + index: index, + path: 'dependencyOverridePaths', + ), + ); + return BootstrapCommandConfigs( runPubGetInParallel: runPubGetInParallel, runPubGetOffline: runPubGetOffline, + dependencyOverridePaths: dependencyOverridePaths + .map( + (override) => + createGlob(override, currentDirectoryPath: workspacePath), + ) + .toList(), ); } @@ -252,10 +276,17 @@ class BootstrapCommandConfigs { /// The default is `false`. final bool runPubGetOffline; + /// A list of [Glob]s for paths that contain packages to be used as dependency + /// overrides for all packages managed in the Melos workspace. + final List dependencyOverridePaths; + Map toJson() { return { 'runPubGetInParallel': runPubGetInParallel, 'runPubGetOffline': runPubGetOffline, + if (dependencyOverridePaths.isNotEmpty) + 'dependencyOverridePaths': + dependencyOverridePaths.map((path) => path.toString()).toList(), }; } @@ -264,13 +295,17 @@ class BootstrapCommandConfigs { other is BootstrapCommandConfigs && runtimeType == other.runtimeType && other.runPubGetInParallel == runPubGetInParallel && - other.runPubGetOffline == runPubGetOffline; + other.runPubGetOffline == runPubGetOffline && + const DeepCollectionEquality(_GlobEquality()) + .equals(other.dependencyOverridePaths, dependencyOverridePaths); @override int get hashCode => runtimeType.hashCode ^ runPubGetInParallel.hashCode ^ - runPubGetOffline.hashCode; + runPubGetOffline.hashCode ^ + const DeepCollectionEquality(_GlobEquality()) + .hash(dependencyOverridePaths); @override String toString() { @@ -278,6 +313,7 @@ class BootstrapCommandConfigs { BootstrapCommandConfigs( runPubGetInParallel: $runPubGetInParallel, runPubGetOffline: $runPubGetOffline, + dependencyOverridePaths: $dependencyOverridePaths, )'''; } } @@ -556,7 +592,6 @@ class MelosWorkspaceConfig { this.repository, required this.packages, this.ignore = const [], - this.dependencyOverrides = const [], this.scripts = Scripts.empty, this.ide = IDEConfigs.empty, this.commands = CommandConfigs.empty, @@ -650,16 +685,6 @@ class MelosWorkspaceConfig { path: 'ignore', ), ); - final dependencyOverrides = assertListIsA( - key: 'dependencyOverrides', - map: yaml, - isRequired: false, - assertItemIsA: (index, value) => assertIsA( - value: value, - index: index, - path: 'dependencyOverrides', - ), - ); final scriptsMap = assertKeyIsA?>( key: 'scripts', @@ -692,9 +717,6 @@ class MelosWorkspaceConfig { ignore: ignore .map((ignore) => createGlob(ignore, currentDirectoryPath: path)) .toList(), - dependencyOverrides: dependencyOverrides - .map((override) => createGlob(override, currentDirectoryPath: path)) - .toList(), scripts: scriptsMap == null ? Scripts.empty : Scripts.fromYaml(scriptsMap, workspacePath: path), @@ -806,10 +828,6 @@ You must have one of the following to be a valid Melos workspace: /// packages. final List ignore; - /// A list of [Glob]s for paths that contain packages to be used as dependency - /// overrides. - final List dependencyOverrides; - /// A list of scripts that can be executed with `melos run` or will be /// executed before/after some specific melos commands. final Scripts scripts; @@ -860,10 +878,10 @@ You must have one of the following to be a valid Melos workspace: other.path == path && other.name == name && other.repository == repository && - const DeepCollectionEquality().equals(other.packages, packages) && - const DeepCollectionEquality().equals(other.ignore, ignore) && - const DeepCollectionEquality() - .equals(other.dependencyOverrides, dependencyOverrides) && + const DeepCollectionEquality(_GlobEquality()) + .equals(other.packages, packages) && + const DeepCollectionEquality(_GlobEquality()) + .equals(other.ignore, ignore) && other.scripts == scripts && other.ide == ide && other.commands == commands; @@ -874,9 +892,8 @@ You must have one of the following to be a valid Melos workspace: path.hashCode ^ name.hashCode ^ repository.hashCode ^ - const DeepCollectionEquality().hash(packages) & - const DeepCollectionEquality().hash(ignore) ^ - const DeepCollectionEquality().hash(dependencyOverrides) ^ + const DeepCollectionEquality(_GlobEquality()).hash(packages) & + const DeepCollectionEquality(_GlobEquality()).hash(ignore) ^ scripts.hashCode ^ ide.hashCode ^ commands.hashCode; @@ -888,9 +905,6 @@ You must have one of the following to be a valid Melos workspace: if (repository != null) 'repository': repository!, 'packages': packages.map((p) => p.toString()).toList(), if (ignore.isNotEmpty) 'ignore': ignore.map((p) => p.toString()).toList(), - if (dependencyOverrides.isNotEmpty) - 'dependencyOverrides': - dependencyOverrides.map((p) => p.toString()).toList(), if (scripts.isNotEmpty) 'scripts': scripts.toJson(), 'ide': ide.toJson(), 'command': commands.toJson(), @@ -906,10 +920,23 @@ MelosWorkspaceConfig( repository: $repository, packages: $packages, ignore: $ignore, - dependencyOverrides: $dependencyOverrides, scripts: ${scripts.toString().indent(' ')}, ide: ${ide.toString().indent(' ')}, commands: ${commands.toString().indent(' ')}, )'''; } } + +class _GlobEquality implements Equality { + const _GlobEquality(); + + @override + bool equals(Glob e1, Glob e2) => + e1.pattern == e2.pattern && e1.context.current == e2.context.current; + + @override + int hash(Glob e) => e.pattern.hashCode ^ e.context.current.hashCode; + + @override + bool isValidKey(Object? o) => true; +} diff --git a/packages/melos/test/utils.dart b/packages/melos/test/utils.dart index 9b4623bcb..6bc83c2f3 100644 --- a/packages/melos/test/utils.dart +++ b/packages/melos/test/utils.dart @@ -294,7 +294,7 @@ class VirtualWorkspaceBuilder { config: config, allPackages: packageMap, filteredPackages: packageMap, - dependencyOverrides: _buildVirtualPackageMap(const [], logger), + dependencyOverridePackages: _buildVirtualPackageMap(const [], logger), logger: logger, sdkPath: sdkPath, ); diff --git a/packages/melos/test/workspace_config_test.dart b/packages/melos/test/workspace_config_test.dart index 45e7f7377..ce8d85f25 100644 --- a/packages/melos/test/workspace_config_test.dart +++ b/packages/melos/test/workspace_config_test.dart @@ -16,6 +16,7 @@ import 'package:melos/melos.dart'; import 'package:melos/src/common/git_repository.dart'; +import 'package:melos/src/common/glob.dart'; import 'package:melos/src/common/platform.dart'; import 'package:melos/src/scripts.dart'; import 'package:melos/src/workspace_configs.dart'; @@ -35,16 +36,19 @@ void main() { group('fromYaml', () { test('accepts empty object', () { expect( - BootstrapCommandConfigs.fromYaml(const {}), + BootstrapCommandConfigs.fromYaml(const {}, workspacePath: '.'), BootstrapCommandConfigs.empty, ); }); test('throws if runPubGetInParallel is not a bool', () { expect( - () => BootstrapCommandConfigs.fromYaml(const { - 'runPubGetInParallel': 42, - }), + () => BootstrapCommandConfigs.fromYaml( + const { + 'runPubGetInParallel': 42, + }, + workspacePath: '.', + ), throwsMelosConfigException(), ); }); @@ -54,10 +58,17 @@ void main() { BootstrapCommandConfigs.fromYaml( const { 'runPubGetInParallel': false, + 'runPubGetOffline': true, + 'dependencyOverridePaths': ['a'], }, + workspacePath: '.', ), - const BootstrapCommandConfigs( + BootstrapCommandConfigs( runPubGetInParallel: false, + runPubGetOffline: true, + dependencyOverridePaths: [ + createGlob('a', currentDirectoryPath: '.') + ], ), ); });