Skip to content

Commit

Permalink
fix: support bootstrapping Flutter example packages (#428)
Browse files Browse the repository at this point in the history
  • Loading branch information
blaugold authored Dec 5, 2022
1 parent f0eca8d commit 5387368
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 20 deletions.
27 changes: 25 additions & 2 deletions packages/melos/lib/src/commands/bootstrap.dart
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,35 @@ mixin _BootstrapMixin on _CleanMixin {
);
}

await Stream.fromIterable(workspace.filteredPackages.values).parallel(
final filteredPackages = workspace.filteredPackages.values;

await Stream.fromIterable(filteredPackages).parallel(
(package) async {
if (package.isExample) {
final enclosingPackage = package.enclosingPackage!;
if (enclosingPackage.isFlutterPackage &&
filteredPackages.contains(enclosingPackage)) {
// This package will be bootstrapped as part of bootstrapping
// the enclosing package.
return;
}
}

final bootstrappedPackages = [package];
await _generatePubspecOverrides(workspace, package);
if (package.isFlutterPackage) {
final example = package.examplePackage;
if (example != null && filteredPackages.contains(example)) {
// The flutter tool bootstraps the example package as part of
// bootstrapping the enclosing package, so we need to generate
// the pubspec overrides for the example package as well.
await _generatePubspecOverrides(workspace, example);
bootstrappedPackages.add(example);
}
}
await _runPubGetForPackage(workspace, package);

_logBootstrapSuccess(package);
bootstrappedPackages.forEach(_logBootstrapSuccess);
},
parallelism: workspace.config.commands.bootstrap.runPubGetInParallel &&
workspace.canRunPubGetConcurrently
Expand Down
47 changes: 35 additions & 12 deletions packages/melos/lib/src/package.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import 'dart:io';
import 'package:collection/collection.dart';
import 'package:glob/glob.dart';
import 'package:meta/meta.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';
Expand Down Expand Up @@ -524,7 +523,7 @@ class PackageMap {
late final isIncluded = packages.any((glob) => glob.matches(path)) &&
!ignore.any((glob) => glob.matches(path));

if (entity is File && basename(path) == 'pubspec.yaml' && isIncluded) {
if (entity is File && p.basename(path) == 'pubspec.yaml' && isIncluded) {
final resolvedPath = await entity.resolveSymbolicLinks();
pubspecsByResolvedPath[resolvedPath] = entity;
} else if (entity is Directory &&
Expand Down Expand Up @@ -645,7 +644,7 @@ extension on Iterable<Package> {
return where((package) {
return directoryPaths.every((dirExistsPath) {
// TODO(rrousselGit): should support environment variables
return dirExists(join(package.path, dirExistsPath));
return dirExists(p.join(package.path, dirExistsPath));
});
});
}
Expand All @@ -662,7 +661,7 @@ extension on Iterable<Package> {
final expandedFileExistsPath =
fileExistsPath.replaceAll(r'$MELOS_PACKAGE_NAME', package.name);

return fileExists(join(package.path, expandedFileExistsPath));
return fileExists(p.join(package.path, expandedFileExistsPath));
});
return fileExistsMatched;
});
Expand Down Expand Up @@ -831,7 +830,7 @@ class Package {
required this.publishTo,
required this.pubSpec,
}) : _packageMap = packageMap,
assert(isAbsolute(path));
assert(p.isAbsolute(path));

final Map<String, Package> _packageMap;

Expand Down Expand Up @@ -977,10 +976,10 @@ class Package {
/// pubspec.lock.
Future<void> linkPackages(MelosWorkspace workspace) async {
final pluginTemporaryPath =
join(workspace.melosToolPath, pathRelativeToWorkspace);
p.join(workspace.melosToolPath, pathRelativeToWorkspace);

await Future.forEach(generatedPubFilePaths, (String tempFilePath) async {
final fileToCopy = join(pluginTemporaryPath, tempFilePath);
final fileToCopy = p.join(pluginTemporaryPath, tempFilePath);
if (!fileExists(fileToCopy)) {
return;
}
Expand Down Expand Up @@ -1012,13 +1011,37 @@ class Package {
temporaryFileContents.replaceAll(melosToolPathRegExp, '');

await writeTextFileAsync(
join(path, tempFilePath),
p.join(path, tempFilePath),
temporaryFileContents,
recursive: true,
);
});
}

/// The example [Package] contained within this package, if any.
///
/// A package is considered to be an example if it is located in the `example`
/// directory of the [enclosingPackage].
late final Package? examplePackage = () {
final examplePath = p.join(path, 'example');
return _packageMap.values
.firstWhereOrNull((package) => p.equals(package.path, examplePath));
}();

/// The [Package] that encloses this package, if any.
///
/// A package is considered to be the enclosing package if this package is
/// located in a direct child directory of the enclosing package.
late final Package? enclosingPackage = () {
final enclosingPackagePath = p.dirname(path);
return _packageMap.values.firstWhereOrNull(
(package) => p.equals(package.path, enclosingPackagePath),
);
}();

/// Whether this package is an example package as defined by [examplePackage].
bool get isExample => enclosingPackage?.examplePackage == this;

/// Returns whether this package is a Flutter app.
///
/// This is determined by ensuring all the following conditions are met:
Expand All @@ -1034,7 +1057,7 @@ class Package {
// Must not have a Flutter plugin definition in it's pubspec.yaml.
if (pubSpec.flutter?.plugin != null) return false;

return fileExists(joinAll([path, 'lib', 'main.dart']));
return fileExists(p.join(path, 'lib', 'main.dart'));
}

bool get isAddToApp {
Expand Down Expand Up @@ -1113,7 +1136,7 @@ class Package {
String? get javaPluginClassPath {
if (androidPackage == null || androidPluginClass == null) return null;

final javaPluginClassPath = joinAll([
final javaPluginClassPath = p.joinAll([
path,
'android/src/main/java',
...androidPackage!.split('.'),
Expand All @@ -1127,7 +1150,7 @@ class Package {
String? get kotlinPluginClassPath {
if (androidPackage == null || androidPluginClass == null) return null;

final kotlinPluginClassPath = joinAll([
final kotlinPluginClassPath = p.joinAll([
path,
'android/src/main/kotlin',
...androidPackage!.split('.'),
Expand Down Expand Up @@ -1175,7 +1198,7 @@ class Package {
}

/// Returns whether this package contains a test directory.
bool get hasTests => dirExists(joinAll([path, 'test']));
bool get hasTests => dirExists(p.join(path, 'test'));

bool _flutterAppSupportsPlatform(String platform) {
assert(
Expand Down
53 changes: 47 additions & 6 deletions packages/melos/test/commands/bootstrap_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ Generating IntelliJ IDE files...
config: config,
);

await melos.bootstrap();
await runMelosBootstrap(melos, logger);

final packageConfig = packageConfigForPackageAt(pkgA);
expect(
Expand Down Expand Up @@ -348,7 +348,7 @@ Generating IntelliJ IDE files...
config: config,
);

await melos.bootstrap();
await runMelosBootstrap(melos, logger);

final packageConfig = packageConfigForPackageAt(pkgA);
expect(
Expand All @@ -361,6 +361,51 @@ Generating IntelliJ IDE files...
skip: !isPubspecOverridesSupported(),
);

test('bootstrap flutter example packages', () async {
final workspaceDir = createTemporaryWorkspaceDirectory(
configBuilder: (path) => MelosWorkspaceConfig.fallback(
path: path,
usePubspecOverrides: true,
),
);

await createProject(
workspaceDir,
const PubSpec(
name: 'a',
dependencies: {
'flutter': SdkReference('flutter'),
},
),
path: 'packages/a',
);

final examplePkg = await createProject(
workspaceDir,
PubSpec(
name: 'example',
dependencies: {
'a': HostedReference(VersionConstraint.any),
},
),
path: 'packages/a/example',
);

final logger = TestLogger();
final config = await MelosWorkspaceConfig.fromDirectory(workspaceDir);
final melos = Melos(
logger: logger,
config: config,
);

await runMelosBootstrap(melos, logger);

final examplePkgConfig = packageConfigForPackageAt(examplePkg);
final aPkgDependencyConfig = examplePkgConfig.packages
.firstWhere((package) => package.name == 'a');
expect(aPkgDependencyConfig.rootUri, '../../');
});

group('mergeMelosPubspecOverrides', () {
void expectMergedMelosPubspecOverrides({
required Map<String, String> melosDependencyOverrides,
Expand Down Expand Up @@ -628,10 +673,6 @@ Generating IntelliJ IDE files...
),
);
});

test('can disable IDE generation using melos config', () {}, skip: true);

test('can supports package filter', () {}, skip: true);
});
}

Expand Down

0 comments on commit 5387368

Please sign in to comment.