Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: find packages in matched directories #330

Merged
merged 1 commit into from
Jun 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions docs/configuration/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,17 @@ sdkPath: .fvm/flutter_sdk

> required

A list of local packages Melos will use to execute commands against. The list can be
of specific paths or a [glob](https://docs.python.org/3/library/glob.html) pattern expansion format.
A list of paths to local packages that are included in the Melos workspace. Each entry can be
specific path or a [glob] pattern.

```yaml
packages:
# e.g. all packages inside the /packages directory
# e.g. include all packages inside the `packages` directory at any level
- packages/**
# e.g. including the workspace root as a package
- "*"
# e.g. include all packages inside the `packages` directory that are direct children
- packages/*
# e.g. include the package in the workspace root
- .
```

> You can also reduce the scope of packages on a per-command basis via the [`--scope` filter](/filters#scope) flag.
Expand All @@ -70,13 +72,13 @@ packages:

> required

A list of local packages Melos will ignore when executing commands in the workspace. The list can be
of specific paths or a [glob](https://docs.python.org/3/library/glob.html) pattern expansion format.
A list of paths to local packages that are exluded from the Melos workspace. Each entry can be
specific path or a [glob] pattern.

```yaml
ignore:
# e.g. ignore example apps
- "packages/**/*example"
- "packages/**/example"
```

> You can also expand the scope of ignored packages on a per-command basis via the [`--scope` filter](/filters#scope) flag.
Expand Down Expand Up @@ -195,3 +197,5 @@ command:
version:
updateGitTagRefs: true
```

[glob]: https://docs.python.org/3/library/glob.html
2 changes: 1 addition & 1 deletion melos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Melos
repository: https://github.com/invertase/melos
packages:
- packages/**
- "*"
- .

command:
version:
Expand Down
40 changes: 26 additions & 14 deletions packages/melos/lib/src/package.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import 'dart:io';
import 'package:collection/collection.dart';
import 'package:glob/glob.dart';
import 'package:path/path.dart';
import 'package:path/path.dart' as p;
import 'package:pool/pool.dart';
import 'package:pub_semver/pub_semver.dart';
import 'package:pubspec/pubspec.dart';
Expand Down Expand Up @@ -388,23 +389,34 @@ class PackageMap {
);

final pubspecsByResolvedPath = <String, File>{};
await for (final entity
in Directory(workspacePath).listConditionallyRecursive(
recurseCondition: (dir) {
final path = dir.path;
return !dartToolGlob.matches(path) &&
!symlinksPluginsGlob.matches(path) &&
!fvmGlob.matches(path) &&
!pluginSymlinksGlob.matches(path);
},
)) {

Stream<FileSystemEntity> allWorkspaceEntities() async* {
final workspaceDir = Directory(workspacePath);
yield workspaceDir;
yield* workspaceDir.listConditionallyRecursive(
recurseCondition: (dir) {
final path = dir.path;
return !dartToolGlob.matches(path) &&
!symlinksPluginsGlob.matches(path) &&
!fvmGlob.matches(path) &&
!pluginSymlinksGlob.matches(path);
},
);
}

await for (final entity in allWorkspaceEntities()) {
final path = entity.path;
if (entity is File &&
basename(path) == 'pubspec.yaml' &&
packages.any((glob) => glob.matches(path)) &&
!ignore.any((glob) => glob.matches(path))) {
late final isIncluded = packages.any((glob) => glob.matches(path)) &&
!ignore.any((glob) => glob.matches(path));

if (entity is File && basename(path) == 'pubspec.yaml' && isIncluded) {
final resolvedPath = await entity.resolveSymbolicLinks();
pubspecsByResolvedPath[resolvedPath] = entity;
} else if (entity is Directory &&
isPackageDirectory(entity) &&
isIncluded) {
final pubspecPath = p.join(path, 'pubspec.yaml');
pubspecsByResolvedPath[pubspecPath] = File(pubspecPath);
}
}

Expand Down
5 changes: 3 additions & 2 deletions packages/melos/lib/src/workspace_configs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -550,10 +550,11 @@ You must have one of the following to be a valid Melos workspace:
/// The hosted git repository which contains the workspace.
final HostedGitRepository? repository;

/// A list of paths to packages that are included in the melos workspace.
/// A list of [Glob]s for paths that should be searched for packages.
final List<Glob> packages;

/// A list of paths to exclude from the melos workspace.
/// A list of [Glob]s for paths that should be excluded from the search for
/// packages.
final List<Glob> ignore;

/// A list of scripts that can be executed with `melos run` or will be executed
Expand Down
98 changes: 98 additions & 0 deletions packages/melos/test/workspace_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,104 @@ The packages that caused the problem are:
);
});

group('locate packages', () {
group('in workspace root', () {
test('by matching pubspec.yaml', () async {
final workspaceDir = createTemporaryWorkspaceDirectory(
configBuilder: (path) => MelosWorkspaceConfig.fromYaml(
const {
'name': 'test',
'packages': ['*']
},
path: path,
),
);

await createProject(
workspaceDir,
const PubSpec(name: 'a'),
path: '.',
);

final workspace = await MelosWorkspace.fromConfig(
await MelosWorkspaceConfig.fromDirectory(workspaceDir),
logger: TestLogger().toMelosLogger(),
);

expect(workspace.allPackages['a'], isNotNull);
});

test('by matching package directory', () async {
final workspaceDir = createTemporaryWorkspaceDirectory(
configBuilder: (path) => MelosWorkspaceConfig.fromYaml(
const {
'name': 'test',
'packages': ['.']
},
path: path,
),
);

await createProject(
workspaceDir,
const PubSpec(name: 'a'),
path: '.',
);

final workspace = await MelosWorkspace.fromConfig(
await MelosWorkspaceConfig.fromDirectory(workspaceDir),
logger: TestLogger().toMelosLogger(),
);

expect(workspace.allPackages['a'], isNotNull);
});
});

group('in child directory', () {
test('by matching pubspec.yaml', () async {
final workspaceDir = createTemporaryWorkspaceDirectory(
configBuilder: (path) => MelosWorkspaceConfig.fromYaml(
const {
'name': 'test',
'packages': ['packages/a/*']
},
path: path,
),
);

await createProject(workspaceDir, const PubSpec(name: 'a'));

final workspace = await MelosWorkspace.fromConfig(
await MelosWorkspaceConfig.fromDirectory(workspaceDir),
logger: TestLogger().toMelosLogger(),
);

expect(workspace.allPackages['a'], isNotNull);
});

test('by matching package directory', () async {
final workspaceDir = createTemporaryWorkspaceDirectory(
configBuilder: (path) => MelosWorkspaceConfig.fromYaml(
const {
'name': 'test',
'packages': ['packages/a']
},
path: path,
),
);

await createProject(workspaceDir, const PubSpec(name: 'a'));

final workspace = await MelosWorkspace.fromConfig(
await MelosWorkspaceConfig.fromDirectory(workspaceDir),
logger: TestLogger().toMelosLogger(),
);

expect(workspace.allPackages['a'], isNotNull);
});
});
});

group('sdkPath', () {
test('use SDK path from environment variable', () async {
withMockPlatform(
Expand Down