-
Notifications
You must be signed in to change notification settings - Fork 153
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(shorebird_cli): add doctor check that lockfiles are tracked in s…
…ource control (#2738) Co-authored-by: Felix Angelov <[email protected]>
- Loading branch information
1 parent
bcdf128
commit 10322b9
Showing
6 changed files
with
311 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
packages/shorebird_cli/lib/src/validators/tracked_lock_files_validator.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import 'dart:io'; | ||
|
||
import 'package:path/path.dart' as p; | ||
import 'package:shorebird_cli/src/executables/git.dart'; | ||
import 'package:shorebird_cli/src/shorebird_env.dart'; | ||
import 'package:shorebird_cli/src/validators/validators.dart'; | ||
|
||
/// Checks that .lock files (pubspec.lock, Podfile.lock) are tracked in source | ||
/// control if they exist and if the project is part of a git repository. | ||
class TrackedLockFilesValidator extends Validator { | ||
@override | ||
String get description => 'Lock files are tracked in source control'; | ||
|
||
@override | ||
bool canRunInCurrentContext() => shorebirdEnv.hasPubspecYaml; | ||
|
||
@override | ||
Future<List<ValidationIssue>> validate() async { | ||
final projectRoot = shorebirdEnv.getFlutterProjectRoot(); | ||
if (projectRoot == null) { | ||
return []; | ||
} | ||
|
||
final isGitRepo = await git.isGitRepo(directory: projectRoot); | ||
if (!isGitRepo) { | ||
// Don't return an issue if the project is not tracked in git. The user | ||
// may be using a different source control system. | ||
return []; | ||
} | ||
|
||
final lockFilePaths = [ | ||
'pubspec.lock', | ||
p.join('ios', 'Podfile.lock'), | ||
p.join('macos', 'Podfile.lock'), | ||
]; | ||
|
||
final warnings = <ValidationIssue>[]; | ||
for (final path in lockFilePaths) { | ||
final file = File(p.join(projectRoot.path, path)); | ||
if (await _fileExistsAndIsNotTracked(file)) { | ||
warnings.add( | ||
ValidationIssue.warning( | ||
message: | ||
'''$path is not tracked in source control. We recommend tracking lock files in source control to avoid unexpected dependency version changes.''', | ||
), | ||
); | ||
} | ||
} | ||
|
||
return warnings; | ||
} | ||
|
||
/// Returns true if [file] exists but is not tracked in git. | ||
Future<bool> _fileExistsAndIsNotTracked(File file) async { | ||
return file.existsSync() && !(await git.isFileTracked(file: file)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
157 changes: 157 additions & 0 deletions
157
packages/shorebird_cli/test/src/validators/tracked_lock_files_validator_test.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
import 'dart:io'; | ||
|
||
import 'package:mocktail/mocktail.dart'; | ||
import 'package:path/path.dart' as p; | ||
import 'package:scoped_deps/scoped_deps.dart'; | ||
import 'package:shorebird_cli/src/executables/git.dart'; | ||
import 'package:shorebird_cli/src/shorebird_env.dart'; | ||
import 'package:shorebird_cli/src/validators/tracked_lock_files_validator.dart'; | ||
import 'package:shorebird_cli/src/validators/validators.dart'; | ||
import 'package:test/test.dart'; | ||
|
||
import '../mocks.dart'; | ||
|
||
void main() { | ||
group(TrackedLockFilesValidator, () { | ||
late Git git; | ||
late ShorebirdEnv shorebirdEnv; | ||
late Directory projectRoot; | ||
|
||
late TrackedLockFilesValidator validator; | ||
|
||
R runWithOverrides<R>(R Function() body) { | ||
return runScoped( | ||
body, | ||
values: { | ||
gitRef.overrideWith(() => git), | ||
shorebirdEnvRef.overrideWith(() => shorebirdEnv), | ||
}, | ||
); | ||
} | ||
|
||
setUpAll(() { | ||
registerFallbackValue(Directory('')); | ||
registerFallbackValue(File('')); | ||
}); | ||
|
||
setUp(() { | ||
git = MockGit(); | ||
shorebirdEnv = MockShorebirdEnv(); | ||
projectRoot = Directory.systemTemp.createTempSync(); | ||
|
||
when(() => shorebirdEnv.getFlutterProjectRoot()).thenReturn(projectRoot); | ||
|
||
validator = TrackedLockFilesValidator(); | ||
}); | ||
|
||
test('has a non-empty description', () { | ||
expect(validator.description, isNotEmpty); | ||
}); | ||
|
||
group('canRunInCurrentContext', () { | ||
group('when a pubspec.yaml file exists', () { | ||
setUp(() { | ||
when(() => shorebirdEnv.hasPubspecYaml).thenReturn(true); | ||
}); | ||
|
||
test('returns true', () { | ||
expect(runWithOverrides(validator.canRunInCurrentContext), isTrue); | ||
}); | ||
}); | ||
|
||
group('when a pubspec.yaml file does not exist', () { | ||
setUp(() { | ||
when(() => shorebirdEnv.hasPubspecYaml).thenReturn(false); | ||
}); | ||
|
||
test('returns false', () { | ||
expect(runWithOverrides(validator.canRunInCurrentContext), isFalse); | ||
}); | ||
}); | ||
}); | ||
|
||
group('validate', () { | ||
group('when no project root is found', () { | ||
setUp(() { | ||
when(() => shorebirdEnv.getFlutterProjectRoot()).thenReturn(null); | ||
}); | ||
|
||
test('returns an empty list', () async { | ||
final issues = await runWithOverrides(() => validator.validate()); | ||
expect(issues, isEmpty); | ||
}); | ||
}); | ||
|
||
group('when project is not tracked in git', () { | ||
setUp(() { | ||
when( | ||
() => git.isGitRepo(directory: any(named: 'directory')), | ||
).thenAnswer((_) async => false); | ||
}); | ||
|
||
test('returns no issues', () async { | ||
final issues = await runWithOverrides(() => validator.validate()); | ||
expect(issues, isEmpty); | ||
}); | ||
}); | ||
|
||
group('when a lock file does not exist', () { | ||
setUp(() { | ||
when( | ||
() => git.isGitRepo(directory: any(named: 'directory')), | ||
).thenAnswer((_) async => true); | ||
}); | ||
|
||
test('does not warn about lock file not being tracked', () async { | ||
final issues = await runWithOverrides(() => validator.validate()); | ||
expect(issues, isEmpty); | ||
}); | ||
}); | ||
|
||
group('when a lock file exists but is not tracked', () { | ||
setUp(() { | ||
when( | ||
() => git.isGitRepo(directory: any(named: 'directory')), | ||
).thenAnswer((_) async => true); | ||
when( | ||
() => git.isFileTracked(file: any(named: 'file')), | ||
).thenAnswer((_) async => false); | ||
|
||
File(p.join(projectRoot.path, 'pubspec.lock')).createSync(); | ||
}); | ||
|
||
test('recommends adding lock file to source control', () async { | ||
final issues = await runWithOverrides(() => validator.validate()); | ||
expect(issues, hasLength(1)); | ||
expect( | ||
issues.first, | ||
equals( | ||
ValidationIssue.warning( | ||
message: | ||
'''pubspec.lock is not tracked in source control. We recommend tracking lock files in source control to avoid unexpected dependency version changes.''', | ||
), | ||
), | ||
); | ||
}); | ||
}); | ||
|
||
group('when lock file exists and is tracked', () { | ||
setUp(() { | ||
when( | ||
() => git.isGitRepo(directory: any(named: 'directory')), | ||
).thenAnswer((_) async => true); | ||
when( | ||
() => git.isFileTracked(file: any(named: 'file')), | ||
).thenAnswer((_) async => true); | ||
|
||
File(p.join(projectRoot.path, 'pubspec.lock')).createSync(); | ||
}); | ||
|
||
test('returns no issues', () async { | ||
final issues = await runWithOverrides(() => validator.validate()); | ||
expect(issues, isEmpty); | ||
}); | ||
}); | ||
}); | ||
}); | ||
} |