Skip to content

Commit b5aba49

Browse files
committed
feat: implemented exec command running logic
1 parent 153bc1a commit b5aba49

5 files changed

Lines changed: 203 additions & 11 deletions

File tree

packages/dart_diff_cli/lib/src/commands/exec_command.dart

Lines changed: 81 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import 'dart:io';
2+
13
import 'package:args/command_runner.dart';
4+
import 'package:dart_diff_cli/src/utils/index.dart';
25
import 'package:mason_logger/mason_logger.dart';
36

47
class ExecCommand extends Command<int> {
@@ -7,15 +10,15 @@ class ExecCommand extends Command<int> {
710
}) : _logger = logger {
811
argParser
912
..addOption(
10-
'branch',
11-
abbr: 'b',
13+
Options.branch.name,
14+
abbr: Options.branch.abbr,
15+
defaultsTo: Options.branch.defaultVal,
1216
help: 'Specify the base branch to use for git diff',
13-
defaultsTo: 'main',
1417
)
1518
..addOption(
16-
'remote',
17-
abbr: 'r',
18-
defaultsTo: 'origin',
19+
Options.remote.name,
20+
abbr: Options.remote.abbr,
21+
defaultsTo: Options.remote.defaultVal,
1922
help: 'Specify the remote repository to use for git diff',
2023
)
2124
..addFlag(
@@ -35,11 +38,78 @@ class ExecCommand extends Command<int> {
3538

3639
@override
3740
Future<int> run() async {
38-
//TODO(droyder7) implement run
39-
_logger.info(
40-
'Why did the scarecrow win an award? Because he was outstanding in his field!');
41-
_logger.info('This is a joke, but the command is working');
42-
_logger.info('args: ${argResults?.arguments.join(', ')}');
41+
if (!isFlutterProjectRoot()) {
42+
print(
43+
'Error: No pubspec.yaml file found. '
44+
'This command should be run from the root of your Flutter project.',
45+
);
46+
return 1;
47+
}
48+
49+
if (argResults == null) {
50+
_logger.info('No arguments.\n$usage');
51+
return 1;
52+
}
53+
54+
final args = argResults!;
55+
_logger.info('args: ${args.arguments.join(', ')}');
56+
57+
final branch = Options.branch.parsedValue(args);
58+
final remote = Options.remote.parsedValue(args);
59+
60+
final extraArgs = args.rest;
61+
if (extraArgs.isEmpty) {
62+
_logger.info('No extra args.\n$usage');
63+
return 1;
64+
}
65+
66+
final bool isTest = extraArgs.any((e) => e == 'test');
67+
68+
print('Running: ${extraArgs.join(' ')}');
69+
70+
final relativeBasePath = getRelativeBasePath(_logger);
71+
72+
final modifiedFiles = getModifiedFiles(remote, branch)
73+
.where(
74+
(file) => file.endsWith('.dart') && file.startsWith(relativeBasePath),
75+
)
76+
.toList();
77+
78+
if (modifiedFiles.isEmpty) {
79+
print('No modified Dart files detected.');
80+
return 0;
81+
}
82+
83+
print('Modified Dart files:\n${modifiedFiles.join('\n')}');
84+
85+
final files = <String>[];
86+
final testFiles = <String>{};
87+
88+
for (final file in modifiedFiles) {
89+
final relativePath = file.withoutBasePath(relativeBasePath);
90+
if (File(relativePath.withPlatformPath()).existsSync()) {
91+
files.add(relativePath);
92+
if (!isTest) {
93+
continue;
94+
}
95+
if (relativePath.endsWith('_test.dart')) {
96+
testFiles.add(relativePath);
97+
} else {
98+
final testFile = calculateTestFile(relativePath);
99+
if (File(testFile).existsSync()) {
100+
testFiles.add(testFile);
101+
} else {
102+
print('No test file found for $relativePath. Skipping...');
103+
}
104+
}
105+
} else {
106+
print('File with path: $relativePath does not exist. Skipping...');
107+
}
108+
}
109+
110+
final fileList = isTest ? testFiles : files;
111+
runCommand([...extraArgs, ...fileList]);
112+
43113
return 0;
44114
}
45115
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import 'dart:io';
2+
3+
String runCommand(List<String> command, {bool output = true}) {
4+
if (output) {
5+
print('Running: ${command.join(' ')}');
6+
}
7+
final result = Process.runSync(
8+
command.first,
9+
command.sublist(1),
10+
workingDirectory: Directory.current.path,
11+
);
12+
if (result.exitCode != 0) {
13+
print('Error running ${command.sublist(0, 2).join(' ')} ${result.stderr}');
14+
exit(1);
15+
}
16+
if (output) {
17+
stdout.write(result.stdout);
18+
}
19+
return result.stdout.toString();
20+
}
21+
22+
bool isFlutterProjectRoot() {
23+
return File('pubspec.yaml').existsSync();
24+
}
25+
26+
String calculateTestFile(String filePath) {
27+
if (filePath.startsWith('lib/')) {
28+
return filePath
29+
.replaceFirst('lib/', 'test/')
30+
.replaceAll('.dart', '_test.dart');
31+
}
32+
return filePath.replaceAll('.dart', '_test.dart');
33+
}
34+
35+
extension StringExtension on String {
36+
String withoutBasePath(String path) => replaceFirst(
37+
'${path.withUnixPath()}/',
38+
'',
39+
);
40+
41+
String withUnixPath() => replaceAll('\\', '/');
42+
43+
String withWindowsPath() => replaceAll('/', '\\');
44+
45+
String withPlatformPath() => Platform.isWindows ? withWindowsPath() : this;
46+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import 'package:args/args.dart';
2+
3+
enum Options {
4+
branch('main', 'b'),
5+
remote('origin', 'r');
6+
7+
final String defaultVal, abbr;
8+
9+
const Options(this.defaultVal, this.abbr);
10+
11+
String parsedValue(ArgResults args) => args.option(name) ?? defaultVal;
12+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import 'dart:io';
2+
3+
import 'package:dart_diff_cli/src/utils/common_utils.dart';
4+
import 'package:mason_logger/mason_logger.dart';
5+
6+
String getGitRepoRoot() {
7+
final result = Process.runSync('git', ['rev-parse', '--show-toplevel']);
8+
if (result.exitCode != 0) {
9+
print('Error getting git repo root: ${result.stderr}');
10+
exit(1);
11+
}
12+
final path = result.stdout.toString().trim();
13+
return path;
14+
}
15+
16+
String getRelativeBasePath([Logger? logger]) {
17+
final basePath = Directory.current.path.withUnixPath();
18+
final repoRoot = getGitRepoRoot();
19+
final relativeBasePath = basePath.withoutBasePath(repoRoot);
20+
logger?.info('Running in current directory: $relativeBasePath \n'
21+
'Repository root: $repoRoot \n'
22+
'Relative base path: $relativeBasePath \n');
23+
return relativeBasePath;
24+
}
25+
26+
List<String> getModifiedFiles(String remote, String branch) {
27+
if (!_isGitInstalled()) {
28+
print('Error: Git is not installed or not found in PATH.');
29+
exit(1);
30+
}
31+
if (!_isGitRepository()) {
32+
print('Error: Not a git repository.');
33+
exit(1);
34+
}
35+
runCommand(['git', 'fetch', remote, branch], output: false);
36+
final result = runCommand(
37+
[
38+
'git',
39+
'diff',
40+
'--name-only',
41+
'--diff-filter=ACMRT',
42+
'$remote/$branch',
43+
],
44+
output: false,
45+
);
46+
return result.split('\n').map((e) => e.trim()).toList();
47+
}
48+
49+
bool _isGitInstalled() {
50+
try {
51+
final result = Process.runSync('git', ['--version']);
52+
return result.exitCode == 0;
53+
} catch (e) {
54+
return false;
55+
}
56+
}
57+
58+
bool _isGitRepository() {
59+
final result = Process.runSync('git', ['rev-parse', '--is-inside-work-tree']);
60+
return result.exitCode == 0 && result.stdout.toString().trim() == 'true';
61+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export 'common_utils.dart';
2+
export 'constants.dart';
3+
export 'git_utils.dart';

0 commit comments

Comments
 (0)