Skip to content

Commit 886bb85

Browse files
authored
feat(packages): ensure git dependencies are reachable (#379)
1 parent 666b513 commit 886bb85

File tree

6 files changed

+146
-0
lines changed

6 files changed

+146
-0
lines changed

lib/src/cli/cli.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import 'package:glob/glob.dart';
44
import 'package:lcov_parser/lcov_parser.dart';
55
import 'package:mason/mason.dart';
66
import 'package:path/path.dart' as p;
7+
import 'package:pubspec_parse/pubspec_parse.dart';
78
import 'package:universal_io/io.dart';
89
import 'package:very_good_cli/src/commands/test/templates/test_runner_bundle.dart';
910
import 'package:very_good_test_runner/very_good_test_runner.dart';
1011

1112
part 'dart_cli.dart';
1213
part 'flutter_cli.dart';
14+
part 'git_cli.dart';
1315

1416
/// Abstraction for running commands via command-line.
1517
class _Cmd {

lib/src/cli/flutter_cli.dart

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ class Flutter {
7272
final installDone = progress?.call(
7373
'Running "flutter packages get" in $cwd',
7474
);
75+
76+
try {
77+
await _verifyGitDependencies(cwd);
78+
} catch (_) {
79+
installDone?.call();
80+
rethrow;
81+
}
82+
7583
try {
7684
await _Cmd.run(
7785
'flutter',
@@ -200,6 +208,34 @@ class Flutter {
200208
}
201209
}
202210

211+
/// Ensures all git dependencies are reachable for the pubspec
212+
/// located in the [cwd].
213+
///
214+
/// If any git dependencies are unreachable,
215+
/// an [UnreachableGitDependency] is thrown.
216+
Future<void> _verifyGitDependencies(String cwd) async {
217+
final pubspec = Pubspec.parse(
218+
await File(p.join(cwd, 'pubspec.yaml')).readAsString(),
219+
);
220+
221+
final dependencies = pubspec.dependencies;
222+
final devDependencies = pubspec.devDependencies;
223+
final dependencyOverrides = pubspec.dependencyOverrides;
224+
final gitDependencies = [
225+
...dependencies.entries,
226+
...devDependencies.entries,
227+
...dependencyOverrides.entries
228+
]
229+
.where((entry) => entry.value is GitDependency)
230+
.map((entry) => entry.value)
231+
.cast<GitDependency>()
232+
.toList();
233+
234+
await Future.wait(
235+
gitDependencies.map((dependency) => Git.reachable(dependency.url)),
236+
);
237+
}
238+
203239
/// Run a command on directories with a `pubspec.yaml`.
204240
Future<List<T>> _runCommand<T>({
205241
required Future<T> Function(String cwd) cmd,

lib/src/cli/git_cli.dart

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
part of 'cli.dart';
2+
3+
/// {@template unreachable_git_dependency}
4+
/// Thrown when `flutter packages get` or `flutter pub get`
5+
/// encounters an unreachable git dependency.
6+
/// {@endtemplate}
7+
class UnreachableGitDependency implements Exception {
8+
/// {@macro unreachable_git_dependency}
9+
const UnreachableGitDependency({required this.remote});
10+
11+
/// The associated git remote [Uri].
12+
final Uri remote;
13+
14+
@override
15+
String toString() {
16+
return '''
17+
$remote is unreachable.
18+
Make sure the remote exists and you have the correct access rights.''';
19+
}
20+
}
21+
22+
/// Git CLI
23+
class Git {
24+
/// Determine whether the [remote] is reachable.
25+
static Future<void> reachable(Uri remote) async {
26+
try {
27+
await _Cmd.run('git', ['ls-remote', '$remote', '--exit-code']);
28+
} catch (_) {
29+
throw UnreachableGitDependency(remote: remote);
30+
}
31+
}
32+
}

pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ dependencies:
1515
meta: ^1.3.0
1616
path: ^1.8.0
1717
pub_updater: ^0.2.1
18+
pubspec_parse: ^1.2.0
1819
universal_io: ^2.0.4
1920
usage: ^4.0.2
2021
very_good_analysis: ^2.4.0

test/src/cli/flutter_cli_test.dart

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,16 @@ dev_dependencies:
171171

172172
const invalidPubspec = 'name: example';
173173

174+
const unreachableGitUrlPubspec = '''
175+
name: example
176+
environment:
177+
sdk: ">=2.13.0 <3.0.0"
178+
179+
dev_dependencies:
180+
very_good_analysis:
181+
git:
182+
url: https://github.com/verygoodopensource/_very_good_analysis''';
183+
174184
class MockLogger extends Mock implements Logger {}
175185

176186
void main() {
@@ -194,6 +204,17 @@ void main() {
194204
);
195205
});
196206

207+
test('throws when there is an unreachable git url', () {
208+
final directory = Directory.systemTemp.createTempSync();
209+
File(p.join(directory.path, 'pubspec.yaml'))
210+
.writeAsStringSync(unreachableGitUrlPubspec);
211+
212+
expectLater(
213+
Flutter.packagesGet(cwd: directory.path),
214+
throwsA(isA<UnreachableGitDependency>()),
215+
);
216+
});
217+
197218
test('completes when there is a pubspec.yaml', () {
198219
expectLater(Flutter.packagesGet(), completes);
199220
});
@@ -238,6 +259,17 @@ void main() {
238259
);
239260
});
240261

262+
test('throws when there is an unreachable git url', () {
263+
final directory = Directory.systemTemp.createTempSync();
264+
File(p.join(directory.path, 'pubspec.yaml'))
265+
.writeAsStringSync(unreachableGitUrlPubspec);
266+
267+
expectLater(
268+
Flutter.packagesGet(cwd: directory.path),
269+
throwsA(isA<UnreachableGitDependency>()),
270+
);
271+
});
272+
241273
test('completes when there is a pubspec.yaml', () {
242274
final directory = Directory.systemTemp.createTempSync();
243275
File(p.join(directory.path, 'pubspec.yaml')).writeAsStringSync(pubspec);

test/src/cli/git_cli_test.dart

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import 'package:test/test.dart';
2+
import 'package:very_good_cli/src/cli/cli.dart';
3+
4+
void main() {
5+
group('Git', () {
6+
group('reachable', () {
7+
test('completes for a reachable remote', () async {
8+
await expectLater(
9+
Git.reachable(
10+
Uri.parse('https://github.com/verygoodopensource/very_good_cli'),
11+
),
12+
completes,
13+
);
14+
});
15+
16+
test('throws UnreachableGitDependency for an unreachable remote',
17+
() async {
18+
await expectLater(
19+
Git.reachable(
20+
Uri.parse('https://github.com/verygoodopensource/_very_good_cli'),
21+
),
22+
throwsA(isA<UnreachableGitDependency>()),
23+
);
24+
});
25+
});
26+
27+
group('UnreachableGitDependency', () {
28+
test('has correct toString override', () {
29+
final remote =
30+
Uri.parse('https://github.com/verygoodopensource/_very_good_cli');
31+
final exception = UnreachableGitDependency(remote: remote);
32+
expect(
33+
exception.toString(),
34+
equals(
35+
'''
36+
$remote is unreachable.
37+
Make sure the remote exists and you have the correct access rights.''',
38+
),
39+
);
40+
});
41+
});
42+
});
43+
}

0 commit comments

Comments
 (0)