Skip to content

Commit

Permalink
add a general dart command
Browse files Browse the repository at this point in the history
  • Loading branch information
jakemac53 committed Sep 22, 2023
1 parent 6a8ec22 commit 0af66bf
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 142 deletions.
3 changes: 1 addition & 2 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ linter:
- camel_case_types
- cancel_subscriptions
- cascade_invocations
- collection_methods_unrelated_type
- comment_references
- constant_identifier_names
- control_flow_in_finally
Expand All @@ -45,13 +46,11 @@ linter:
- file_names
- hash_and_equals
- implementation_imports
- iterable_contains_unrelated_type
- join_return_with_assignment
- library_names
- library_prefixes
- library_private_types_in_public_api
- lines_longer_than_80_chars
- list_remove_unrelated_type
- literal_only_boolean_expressions
- missing_whitespace_between_adjacent_strings
- no_duplicate_case_values
Expand Down
5 changes: 5 additions & 0 deletions mono_repo/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 6.6.0

- Added the `dart` command to run arbitrary dart commands across all packages.
- Require the 3.0.0 Dart SDK.

## 6.5.7

- Updated `mono_repo` to use the existing action versions from the generated
Expand Down
129 changes: 129 additions & 0 deletions mono_repo/lib/src/commands/dart.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';
import 'dart:io';

import 'package:args/args.dart';
import 'package:io/ansi.dart';
import 'package:path/path.dart' as p;

import '../package_config.dart';
import '../root_config.dart';
import 'mono_repo_command.dart';

class DartCommand extends MonoRepoCommand {
@override
final ArgParser argParser = ArgParser.allowAnything();

@override
String get name => 'dart';

@override
String get description =>
'Runs the `dart` command with the provided arguments across all '
'packages.';

@override
Future<void> run() => dart(
rootConfig(),
arguments,
executableForPackage,
);

/// The arguments to pass to the executable.
List<String> get arguments => argResults?.rest ?? const [];

/// The executable to use for a given package.
Executable executableForPackage(PackageConfig config) => Executable.dart;
}

Future<void> dart(
RootConfig rootConfig,
List<String> args,
Executable Function(PackageConfig) executableForPackage,
) async {
final pkgDirs = rootConfig.map((pc) => pc.relativePath).toList();

print(
lightBlue.wrap(
'Running `dart ${args.join(' ')}` across ${pkgDirs.length} '
'package(s).',
),
);

var successCount = 0;
final failSet = <String>{};

for (var config in rootConfig) {
final dir = config.relativePath;

print('');
print(
wrapWith(
'`$dir`: Starting `${args.join(' ')}`',
[styleBold, lightBlue],
),
);
final workingDir = p.join(rootConfig.rootDirectory, dir);

final proc = await Process.start(
executableForPackage(config).path,
args,
mode: ProcessStartMode.inheritStdio,
workingDirectory: workingDir,
);

final exit = await proc.exitCode;

if (exit == 0) {
successCount++;
print(wrapWith('`$dir`: success!', [styleBold, green]));
} else {
failSet.add(dir);
print(wrapWith('`$dir`: failed!', [styleBold, red]));
}

if (rootConfig.length > 1) {
print('');
print('Successes: $successCount');
if (failSet.isNotEmpty) {
print(
'Failures: ${failSet.length}\n'
'${failSet.map((e) => ' $e').join('\n')}',
);
}
final remaining = rootConfig.length - (successCount + failSet.length);
if (remaining > 0) {
print('Remaining: $remaining');
}
}
}
}

/// The path to the root directory of the SDK.
final String _sdkDir = (() {
// The Dart executable is in "/path/to/sdk/bin/dart", so two levels up is
// "/path/to/sdk".
final aboveExecutable = p.dirname(p.dirname(Platform.resolvedExecutable));
assert(FileSystemEntity.isFileSync(p.join(aboveExecutable, 'version')));
return aboveExecutable;
})();

final String _dartPath = p.join(_sdkDir, 'bin', 'dart');

/// The "flutter[.bat]" command.
final String _flutterPath = Platform.isWindows ? 'flutter.bat' : 'flutter';

enum Executable {
dart,
flutter,
}

extension ExecutablePath on Executable {
String get path => switch (this) {
Executable.dart => _dartPath,
Executable.flutter => _flutterPath,
};
}
105 changes: 7 additions & 98 deletions mono_repo/lib/src/commands/pub.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,11 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';
import 'dart:io';

import 'package:args/args.dart';
import 'package:io/ansi.dart';
import 'package:path/path.dart' as p;

import '../root_config.dart';
import '../package_config.dart';
import '../utilities.dart';
import 'mono_repo_command.dart';

class PubCommand extends MonoRepoCommand {
@override
final ArgParser argParser = ArgParser.allowAnything();
import 'dart.dart';

class PubCommand extends DartCommand {
@override
String get name => 'pub';

Expand All @@ -25,90 +15,9 @@ class PubCommand extends MonoRepoCommand {
'Runs the `pub` command with the provided arguments across all packages.';

@override
Future<void> run() => pub(
rootConfig(),
argResults?.rest ?? const [],
);
}

Future<void> pub(RootConfig rootConfig, List<String> args) async {
final pkgDirs = rootConfig.map((pc) => pc.relativePath).toList();

print(
lightBlue.wrap(
'Running `dart pub ${args.join(' ')}` across ${pkgDirs.length} '
'package(s).',
),
);

final packageArgs = ['pub', ...args];

var successCount = 0;
final failSet = <String>{};

for (var config in rootConfig) {
final dir = config.relativePath;
String executable;
Executable executableForPackage(PackageConfig config) =>
config.pubspec.usesFlutter ? Executable.flutter : Executable.dart;

if (config.pubspec.usesFlutter) {
executable = _flutterPath;
} else {
executable = dartPath;
}

print('');
print(
wrapWith(
'`$dir`: Starting `$executable ${packageArgs.join(' ')}`',
[styleBold, lightBlue],
),
);
final workingDir = p.join(rootConfig.rootDirectory, dir);

final proc = await Process.start(
executable,
packageArgs,
mode: ProcessStartMode.inheritStdio,
workingDirectory: workingDir,
);

final exit = await proc.exitCode;

if (exit == 0) {
successCount++;
print(wrapWith('`$dir`: success!', [styleBold, green]));
} else {
failSet.add(dir);
print(wrapWith('`$dir`: failed!', [styleBold, red]));
}

if (rootConfig.length > 1) {
print('');
print('Successes: $successCount');
if (failSet.isNotEmpty) {
print(
'Failures: ${failSet.length}\n'
'${failSet.map((e) => ' $e').join('\n')}',
);
}
final remaining = rootConfig.length - (successCount + failSet.length);
if (remaining > 0) {
print('Remaining: $remaining');
}
}
}
@override
List<String> get arguments => ['pub', ...super.arguments];
}

/// The path to the root directory of the SDK.
final String _sdkDir = (() {
// The Dart executable is in "/path/to/sdk/bin/dart", so two levels up is
// "/path/to/sdk".
final aboveExecutable = p.dirname(p.dirname(Platform.resolvedExecutable));
assert(FileSystemEntity.isFileSync(p.join(aboveExecutable, 'version')));
return aboveExecutable;
})();

final String dartPath = p.join(_sdkDir, 'bin', 'dart');

/// The "flutter[.bat]" command.
final String _flutterPath = Platform.isWindows ? 'flutter.bat' : 'flutter';
2 changes: 2 additions & 0 deletions mono_repo/lib/src/runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:args/args.dart';
import 'package:args/command_runner.dart';

import 'commands/check.dart';
import 'commands/dart.dart';
import 'commands/generate.dart';
import 'commands/list_command.dart';
import 'commands/mono_repo_command.dart';
Expand All @@ -19,6 +20,7 @@ import 'version.dart';
final commands = List<Command<void>>.unmodifiable(
[
CheckCommand(),
DartCommand(),
GenerateCommand(),
ListCommand(),
PresubmitCommand(),
Expand Down
4 changes: 2 additions & 2 deletions mono_repo/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ name: mono_repo
description: >-
CLI tools to make it easier to manage a single source repository containing
multiple Dart packages.
version: 6.5.7
version: 6.6.0
repository: https://github.com/google/mono_repo.dart

topics:
- tool
- repository-management

environment:
sdk: '>=2.18.0 <3.0.0'
sdk: ^3.0.0

dependencies:
args: ^2.0.0
Expand Down
66 changes: 66 additions & 0 deletions mono_repo/test/dart_command_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import 'dart:async';
import 'dart:io';

import 'package:mono_repo/src/commands/dart.dart';
import 'package:mono_repo/src/commands/pub.dart';
import 'package:mono_repo/src/root_config.dart';
import 'package:path/path.dart' as p;
import 'package:test/test.dart';
import 'package:test_descriptor/test_descriptor.dart' as d;

void main() {
group('valid setup', () {
setUp(_setup);

test('can run pub get', () async {
expect(
() => dart(
RootConfig(rootDirectory: d.sandbox),
['pub', 'get'],
PubCommand().executableForPackage,
),
prints(allOf(contains('success!'), isNot(contains('Failures:')))),
);
});

test('can run dart fix', () async {
final file = File(p.join(d.sandbox, 'foo', 'fix.dart'))
..writeAsStringSync('''
import 'b.dart';
import 'a.dart';
''');
addTearDown(() => file.deleteSync);
await expectLater(
() => dart(
RootConfig(rootDirectory: d.sandbox),
['fix', '--apply'],
(_) => Executable.dart,
),
prints(allOf(contains('success!'), isNot(contains('Failures:')))),
);
expect(file.readAsStringSync(), '''
import 'a.dart';
import 'b.dart';
''');
});
});
}

Future _setup() async {
await d.dir('foo', [
d.file('mono_pkg.yaml', ''),
d.file('pubspec.yaml', r'''
name: dart_app
environment:
sdk: ^3.0.0
dependencies:
meta: any
'''),
d.file('analysis_options.yaml', '''
linter:
rules:
- directives_ordering
'''),
]).create();
}
Loading

0 comments on commit 0af66bf

Please sign in to comment.