Avoid failing in gitignore validator (#3354)
diff --git a/lib/src/git.dart b/lib/src/git.dart
index dc45edc..ded45e9 100644
--- a/lib/src/git.dart
+++ b/lib/src/git.dart
@@ -5,6 +5,7 @@
/// Helper functionality for invoking Git.
import 'dart:async';
+import 'package:collection/collection.dart';
import 'package:path/path.dart' as p;
import 'package:pub_semver/pub_semver.dart';
@@ -88,24 +89,9 @@
return result.stdout;
}
-/// Returns the name of the git command-line app, or `null` if Git could not be
-/// found on the user's PATH.
-String? get command {
- if (_commandCache != null) return _commandCache;
-
- if (_tryGitCommand('git')) {
- _commandCache = 'git';
- } else if (_tryGitCommand('git.cmd')) {
- _commandCache = 'git.cmd';
- } else {
- return null;
- }
-
- log.fine('Determined git command $_commandCache.');
- return _commandCache;
-}
-
-String? _commandCache;
+/// The name of the git command-line app, or `null` if Git could not be found on
+/// the user's PATH.
+final String? command = ['git', 'git.cmd'].firstWhereOrNull(_tryGitCommand);
/// Returns the root of the git repo [dir] belongs to. Returns `null` if not
/// in a git repo or git is not installed.
@@ -150,6 +136,7 @@
for $topLevelProgram it is recommended to use git version 2.14 or newer.
''');
}
+ log.fine('Determined git command $command.');
return true;
} on RunProcessException catch (err) {
// If the process failed, they probably don't have it.
diff --git a/lib/src/validator/gitignore.dart b/lib/src/validator/gitignore.dart
index 4fb49b1..532178b 100644
--- a/lib/src/validator/gitignore.dart
+++ b/lib/src/validator/gitignore.dart
@@ -11,6 +11,7 @@
import '../git.dart' as git;
import '../ignore.dart';
import '../io.dart';
+import '../log.dart' as log;
import '../utils.dart';
import '../validator.dart';
@@ -23,12 +24,21 @@
@override
Future<void> validate() async {
if (entrypoint.root.inGitRepo) {
- final checkedIntoGit = git.runSync([
- 'ls-files',
- '--cached',
- '--exclude-standard',
- '--recurse-submodules'
- ], workingDir: entrypoint.root.dir);
+ late final List<String> checkedIntoGit;
+ try {
+ checkedIntoGit = git.runSync([
+ 'ls-files',
+ '--cached',
+ '--exclude-standard',
+ '--recurse-submodules'
+ ], workingDir: entrypoint.root.dir);
+ } on git.GitException catch (e) {
+ log.fine('Could not run `git ls-files` files in repo (${e.message}).');
+ // This validation is only a warning.
+ // If git is not supported on the platform, or too old to support
+ // --recurse-submodules we just continue silently.
+ return;
+ }
final root = git.repoRoot(entrypoint.root.dir) ?? entrypoint.root.dir;
var beneath = p.posix.joinAll(
p.split(p.normalize(p.relative(entrypoint.root.dir, from: root))));
diff --git a/test/get/git/git_not_installed_test.dart b/test/get/git/git_not_installed_test.dart
index e146f0e..e590770 100644
--- a/test/get/git/git_not_installed_test.dart
+++ b/test/get/git/git_not_installed_test.dart
@@ -5,42 +5,11 @@
@TestOn('linux')
import 'dart:io';
-import 'package:path/path.dart' as p;
-import 'package:pub/src/io.dart' show runProcess;
import 'package:test/test.dart';
-import 'package:test_descriptor/test_descriptor.dart' show sandbox;
import '../../descriptor.dart' as d;
import '../../test_pub.dart';
-/// Create temporary folder 'bin/' containing a 'git' script in [sandbox]
-/// By adding the bin/ folder to the search `$PATH` we can prevent `pub` from
-/// detecting the installed 'git' binary and we can test that it prints
-/// a useful error message.
-Future<void> setUpFakeGitScript(
- {required String bash, required String batch}) async {
- await d.dir('bin', [
- if (!Platform.isWindows) d.file('git', bash),
- if (Platform.isWindows) d.file('git.bat', batch),
- ]).create();
- if (!Platform.isWindows) {
- // Make the script executable.
-
- await runProcess('chmod', ['+x', p.join(sandbox, 'bin', 'git')]);
- }
-}
-
-/// Returns an environment where PATH is extended with `$sandbox/bin`.
-Map<String, String> extendedPathEnv() {
- final separator = Platform.isWindows ? ';' : ':';
- final binFolder = p.join(sandbox, 'bin');
-
- return {
- // Override 'PATH' to ensure that we can't detect a working "git" binary
- 'PATH': '$binFolder$separator${Platform.environment['PATH']}',
- };
-}
-
void main() {
test('reports failure if Git is not installed', () async {
await setUpFakeGitScript(bash: '''
diff --git a/test/test_pub.dart b/test/test_pub.dart
index fcba6a6..6968f4e 100644
--- a/test/test_pub.dart
+++ b/test/test_pub.dart
@@ -980,3 +980,30 @@
});
return server;
}
+
+/// Create temporary folder 'bin/' containing a 'git' script in [sandbox]
+/// By adding the bin/ folder to the search `$PATH` we can prevent `pub` from
+/// detecting the installed 'git' binary and we can test that it prints
+/// a useful error message.
+Future<void> setUpFakeGitScript(
+ {required String bash, required String batch}) async {
+ await d.dir('bin', [
+ if (!Platform.isWindows) d.file('git', bash),
+ if (Platform.isWindows) d.file('git.bat', batch),
+ ]).create();
+ if (!Platform.isWindows) {
+ // Make the script executable.
+ await runProcess('chmod', ['+x', p.join(d.sandbox, 'bin', 'git')]);
+ }
+}
+
+/// Returns an environment where PATH is extended with `$sandbox/bin`.
+Map<String, String> extendedPathEnv() {
+ final separator = Platform.isWindows ? ';' : ':';
+ final binFolder = p.join(d.sandbox, 'bin');
+
+ return {
+ // Override 'PATH' to ensure that we can't detect a working "git" binary
+ 'PATH': '$binFolder$separator${Platform.environment['PATH']}',
+ };
+}
diff --git a/test/validator/gitignore_test.dart b/test/validator/gitignore_test.dart
index f7b02fe..3cd4a11 100644
--- a/test/validator/gitignore_test.dart
+++ b/test/validator/gitignore_test.dart
@@ -14,12 +14,13 @@
Future<void> expectValidation(
error,
int exitCode, {
+ Map<String, String> environment = const {},
String? workingDirectory,
}) async {
await runPub(
error: error,
args: ['publish', '--dry-run'],
- environment: {'_PUB_TEST_SDK_VERSION': '2.12.0'},
+ environment: {'_PUB_TEST_SDK_VERSION': '2.12.0', ...environment},
workingDirectory: workingDirectory ?? d.path(appPath),
exitCode: exitCode,
);
@@ -52,6 +53,20 @@
exit_codes.DATA);
});
+ test('should not fail on missing git', () async {
+ await d.git('myapp', [
+ ...d.validPackage.contents,
+ d.file('.gitignore', '*.txt'),
+ d.file('foo.txt'),
+ ]).create();
+
+ await pubGet(environment: {'_PUB_TEST_SDK_VERSION': '1.12.0'});
+ await setUpFakeGitScript(bash: 'echo "Not git"', batch: 'echo "Not git"');
+ await expectValidation(
+ allOf([contains('Package has 0 warnings.')]), exit_codes.SUCCESS,
+ environment: extendedPathEnv());
+ });
+
test('Should also consider gitignores from above the package root', () async {
await d.git('reporoot', [
d.dir(