blob: a4d2585ed479d9542a0385d0607604ba09e0d93f [file] [log] [blame]
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:path/path.dart' as p;
import '../git.dart' as git;
import '../log.dart' as log;
import '../utils.dart';
import '../validator.dart';
/// A validator that validates that no checked in files are modified in git.
///
/// Doesn't report on newly added files, as generated files might not be checked
/// in to git.
class GitStatusValidator extends Validator {
@override
Future<void> validate() async {
if (!package.inGitRepo) {
return;
}
final Uint8List output;
final String reporoot;
try {
final maybeReporoot = git.repoRoot(package.dir);
if (maybeReporoot == null) {
log.fine(
'Could not determine the repository root from ${package.dir}.',
);
// This validation is only a warning.
return;
}
reporoot = maybeReporoot;
output = git.runSyncBytes([
'status',
'-z', // Machine parsable
'--no-renames', // We don't care about renames.
'--untracked-files=no', // Don't show untracked files.
], workingDir: package.dir);
} on git.GitException catch (e) {
log.fine('Could not run `git status` files in repo (${e.message}).');
// This validation is only a warning.
// If git is not supported on the platform, we just continue silently.
return;
}
final List<String> modifiedFiles;
try {
modifiedFiles =
git
.splitZeroTerminated(output, skipPrefix: 3)
.map((bytes) {
try {
final filename = utf8.decode(bytes);
final fullPath = p.join(reporoot, filename);
if (!files.any((f) => p.equals(fullPath, f))) {
// File is not in the published set - ignore.
return null;
}
return p.relative(fullPath);
} on FormatException catch (e) {
// Filename is not utf8 - ignore.
log.fine('Cannot decode file name: $e');
return null;
}
})
.nonNulls
.toList();
} on FormatException catch (e) {
// Malformed output from `git status`. Skip this validation.
log.fine('Malformed output from `git status -z`: $e');
return;
}
if (modifiedFiles.isNotEmpty) {
warnings.add('''
${modifiedFiles.length} checked-in ${pluralize('file', modifiedFiles.length)} ${modifiedFiles.length == 1 ? 'is' : 'are'} modified in git.
Usually you want to publish from a clean git state.
Consider committing these files or reverting the changes.
Modified files:
${modifiedFiles.take(10).map(p.relative).join('\n')}
${modifiedFiles.length > 10 ? '...\n' : ''}
Run `git status` for more information.
''');
}
}
}