Skip to content
Open
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
24 changes: 24 additions & 0 deletions lib/src/cli/git_cli.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,30 @@ Make sure the remote exists and you have the correct access rights.''';

/// Git CLI
class Git {
/// Returns file paths changed from [baseRef] to `HEAD`.
///
/// Paths are relative to [cwd].
static Future<List<String>> changedFiles(
String baseRef, {
required Logger logger,
String cwd = '.',
}) async {
final result = await _Cmd.run(
'git',
['diff', '--name-only', '--relative', '$baseRef...HEAD'],
logger: logger,
workingDirectory: cwd,
);

final lines = result.stdout
.toString()
.split(RegExp(r'\r?\n'))
.map((line) => line.trim())
.where((line) => line.isNotEmpty)
.toList();
return lines;
}

/// Determine whether the [remote] is reachable.
static Future<void> reachable(Uri remote, {required Logger logger}) async {
try {
Expand Down
4 changes: 4 additions & 0 deletions lib/src/command_runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class VeryGoodCommandRunner extends CompletionCommandRunner<int> {
addCommand(TestCommand(logger: _logger));
addCommand(UpdateCommand(logger: _logger, pubUpdater: pubUpdater));
addCommand(DartCommand(logger: _logger));
addCommand(WorkspaceCommand(logger: _logger));
addCommand(MCPCommand());
}

Expand All @@ -57,6 +58,9 @@ class VeryGoodCommandRunner extends CompletionCommandRunner<int> {
bool? isWindowsOverride;
bool get _isWindows => isWindowsOverride ?? Platform.isWindows;

@override
bool get enableAutoInstall => !environment.containsKey('CI');

@override
void printUsage() => _logger.info(usage);

Expand Down
1 change: 1 addition & 0 deletions lib/src/commands/commands.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export 'dart/dart.dart';
export 'packages/packages.dart';
export 'test/test.dart';
export 'update.dart';
export 'workspace/workspace.dart';
1 change: 1 addition & 0 deletions lib/src/commands/workspace/commands/commands.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export 'matrix.dart';
112 changes: 112 additions & 0 deletions lib/src/commands/workspace/commands/matrix.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import 'dart:convert';
import 'dart:io';

import 'package:args/args.dart';
import 'package:args/command_runner.dart';
import 'package:mason/mason.dart';
import 'package:meta/meta.dart';
import 'package:universal_io/io.dart';
import 'package:very_good_cli/src/cli/cli.dart';
import 'package:very_good_cli/src/workspace_matrix/workspace_matrix.dart';

/// Signature for resolving files changed against a git base ref.
typedef GitChangedFiles =
Future<List<String>> Function({
required String baseRef,
required String cwd,
required Logger logger,
});

/// Signature for resolving affected workspace projects.
typedef ResolveAffectedWorkspaceProjects =
List<WorkspaceProject> Function({
required Directory rootDirectory,
required List<String> changedFiles,
});

/// {@template workspace_matrix_command}
/// `very_good workspace matrix` command.
/// {@endtemplate}
class WorkspaceMatrixCommand extends Command<int> {
/// {@macro workspace_matrix_command}
WorkspaceMatrixCommand({
required this.logger,
GitChangedFiles? gitChangedFiles,
ResolveAffectedWorkspaceProjects? resolveAffectedProjects,
}) : _gitChangedFiles = gitChangedFiles ?? _defaultGitChangedFiles,
_resolveAffectedProjects =
resolveAffectedProjects ?? resolveAffectedWorkspaceProjects {
argParser.addOption(
'base',
help: 'Base git ref used for diffing changes (for example, origin/main).',
valueHelp: 'ref',
);
}

/// Logger used by this command.
final Logger logger;
final GitChangedFiles _gitChangedFiles;
final ResolveAffectedWorkspaceProjects _resolveAffectedProjects;

/// [ArgResults] which can be overridden for testing.
@visibleForTesting
ArgResults? argResultOverrides;

ArgResults get _argResults => argResultOverrides ?? argResults!;

@override
String get description =>
'Return a JSON list of affected projects in the current workspace.';

@override
String get invocation => 'very_good workspace matrix [arguments]';

@override
String get name => 'matrix';

@override
Future<int> run() async {
if (_argResults.rest.isNotEmpty) {
usageException('This command does not accept positional arguments.');
}

final baseRef = (_argResults['base'] as String?)?.trim();
if (baseRef == null || baseRef.isEmpty) {
usageException('Missing required option: --base <ref>.');
}

try {
final cwd = Directory.current.path;
final changedFiles = await _gitChangedFiles(
baseRef: baseRef,
cwd: cwd,
logger: logger,
);

final affectedProjects = _resolveAffectedProjects(
rootDirectory: Directory(cwd),
changedFiles: changedFiles,
);

logger.info(
jsonEncode(
affectedProjects.map((project) => project.toJson()).toList(),
),
);
return ExitCode.success.code;
} on UsageException {
rethrow;
} on Exception catch (error) {
logger.err('$error');
return ExitCode.unavailable.code;
}
}
}

Future<List<String>> _defaultGitChangedFiles({
required String baseRef,
required String cwd,
required Logger logger,
}) {
return Git.changedFiles(baseRef, cwd: cwd, logger: logger);
}
19 changes: 19 additions & 0 deletions lib/src/commands/workspace/workspace.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import 'package:args/command_runner.dart';
import 'package:mason/mason.dart';
import 'package:very_good_cli/src/commands/workspace/commands/commands.dart';

/// {@template workspace_command}
/// `very_good workspace` command for managing Pub workspaces.
/// {@endtemplate}
class WorkspaceCommand extends Command<int> {
/// {@macro workspace_command}
WorkspaceCommand({required Logger logger}) {
addSubcommand(WorkspaceMatrixCommand(logger: logger));
}

@override
String get description => 'Command for managing Pub workspaces.';

@override
String get name => 'workspace';
}
Loading