mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Migrate status (#102785)
This commit is contained in:
parent
3da9eee2f0
commit
0895130e51
@ -2,20 +2,34 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:process/process.dart';
|
||||||
|
|
||||||
|
import '../base/file_system.dart';
|
||||||
import '../base/logger.dart';
|
import '../base/logger.dart';
|
||||||
|
import '../base/platform.dart';
|
||||||
import '../base/terminal.dart';
|
import '../base/terminal.dart';
|
||||||
import '../migrate/migrate_utils.dart';
|
import '../migrate/migrate_utils.dart';
|
||||||
import '../runner/flutter_command.dart';
|
import '../runner/flutter_command.dart';
|
||||||
|
import 'migrate_status.dart';
|
||||||
|
|
||||||
/// Base command for the migration tool.
|
/// Base command for the migration tool.
|
||||||
class MigrateCommand extends FlutterCommand {
|
class MigrateCommand extends FlutterCommand {
|
||||||
MigrateCommand({
|
MigrateCommand({
|
||||||
|
bool verbose = false,
|
||||||
required this.logger,
|
required this.logger,
|
||||||
// TODO(garyq): Add each parameters in as subcommands land.
|
// TODO(garyq): Add parameter in as they are needed for subcommands.
|
||||||
|
required FileSystem fileSystem,
|
||||||
|
required Platform platform,
|
||||||
|
required ProcessManager processManager,
|
||||||
}) {
|
}) {
|
||||||
// TODO(garyq): Add subcommands.
|
// TODO(garyq): Add each subcommand back in as they land.
|
||||||
|
addSubcommand(MigrateStatusCommand(
|
||||||
|
verbose: verbose,
|
||||||
|
logger: logger,
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
platform: platform,
|
||||||
|
processManager: processManager
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
final Logger logger;
|
final Logger logger;
|
||||||
|
176
packages/flutter_tools/lib/src/commands/migrate_status.dart
Normal file
176
packages/flutter_tools/lib/src/commands/migrate_status.dart
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:process/process.dart';
|
||||||
|
|
||||||
|
import '../base/file_system.dart';
|
||||||
|
import '../base/logger.dart';
|
||||||
|
import '../base/platform.dart';
|
||||||
|
import '../base/terminal.dart';
|
||||||
|
import '../migrate/migrate_manifest.dart';
|
||||||
|
import '../migrate/migrate_utils.dart';
|
||||||
|
import '../project.dart';
|
||||||
|
import '../runner/flutter_command.dart';
|
||||||
|
import 'migrate.dart';
|
||||||
|
|
||||||
|
/// Flutter migrate subcommand to check the migration status of the project.
|
||||||
|
class MigrateStatusCommand extends FlutterCommand {
|
||||||
|
MigrateStatusCommand({
|
||||||
|
bool verbose = false,
|
||||||
|
required this.logger,
|
||||||
|
required this.fileSystem,
|
||||||
|
required Platform platform,
|
||||||
|
required ProcessManager processManager,
|
||||||
|
}) : _verbose = verbose,
|
||||||
|
migrateUtils = MigrateUtils(
|
||||||
|
logger: logger,
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
platform: platform,
|
||||||
|
processManager: processManager,
|
||||||
|
) {
|
||||||
|
requiresPubspecYaml();
|
||||||
|
argParser.addOption(
|
||||||
|
'staging-directory',
|
||||||
|
help: 'Specifies the custom migration working directory used to stage '
|
||||||
|
'and edit proposed changes. This path can be absolute or relative '
|
||||||
|
'to the flutter project root. This defaults to '
|
||||||
|
'`$kDefaultMigrateStagingDirectoryName`',
|
||||||
|
valueHelp: 'path',
|
||||||
|
);
|
||||||
|
argParser.addOption(
|
||||||
|
'project-directory',
|
||||||
|
help: 'The root directory of the flutter project. This defaults to the '
|
||||||
|
'current working directory if omitted.',
|
||||||
|
valueHelp: 'path',
|
||||||
|
);
|
||||||
|
argParser.addFlag(
|
||||||
|
'diff',
|
||||||
|
defaultsTo: true,
|
||||||
|
help: 'Shows the diff output when enabled. Enabled by default.',
|
||||||
|
);
|
||||||
|
argParser.addFlag(
|
||||||
|
'show-added-files',
|
||||||
|
help: 'Shows the contents of added files. Disabled by default.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final bool _verbose;
|
||||||
|
|
||||||
|
final Logger logger;
|
||||||
|
|
||||||
|
final FileSystem fileSystem;
|
||||||
|
|
||||||
|
final MigrateUtils migrateUtils;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String name = 'status';
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String description = 'Prints the current status of the in progress migration.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get category => FlutterCommandCategory.project;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => const <DevelopmentArtifact>{};
|
||||||
|
|
||||||
|
/// Manually marks the lines in a diff that should be printed unformatted for visbility.
|
||||||
|
///
|
||||||
|
/// This is used to ensure the initial lines that display the files being diffed and the
|
||||||
|
/// git revisions are printed and never skipped.
|
||||||
|
final Set<int> _initialDiffLines = <int>{0, 1};
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<FlutterCommandResult> runCommand() async {
|
||||||
|
final String? projectDirectory = stringArg('project-directory');
|
||||||
|
final FlutterProjectFactory flutterProjectFactory = FlutterProjectFactory(logger: logger, fileSystem: fileSystem);
|
||||||
|
final FlutterProject project = projectDirectory == null
|
||||||
|
? FlutterProject.current()
|
||||||
|
: flutterProjectFactory.fromDirectory(fileSystem.directory(projectDirectory));
|
||||||
|
Directory stagingDirectory = project.directory.childDirectory(kDefaultMigrateStagingDirectoryName);
|
||||||
|
final String? customStagingDirectoryPath = stringArg('staging-directory');
|
||||||
|
if (customStagingDirectoryPath != null) {
|
||||||
|
if (fileSystem.path.isAbsolute(customStagingDirectoryPath)) {
|
||||||
|
stagingDirectory = fileSystem.directory(customStagingDirectoryPath);
|
||||||
|
} else {
|
||||||
|
stagingDirectory = project.directory.childDirectory(customStagingDirectoryPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!stagingDirectory.existsSync()) {
|
||||||
|
logger.printStatus('No migration in progress in $stagingDirectory. Start a new migration with:');
|
||||||
|
printCommandText('flutter migrate start', logger);
|
||||||
|
return const FlutterCommandResult(ExitStatus.fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
final File manifestFile = MigrateManifest.getManifestFileFromDirectory(stagingDirectory);
|
||||||
|
if (!manifestFile.existsSync()) {
|
||||||
|
logger.printError('No migrate manifest in the migrate working directory '
|
||||||
|
'at ${stagingDirectory.path}. Fix the working directory '
|
||||||
|
'or abandon and restart the migration.');
|
||||||
|
return const FlutterCommandResult(ExitStatus.fail);
|
||||||
|
}
|
||||||
|
final MigrateManifest manifest = MigrateManifest.fromFile(manifestFile);
|
||||||
|
|
||||||
|
final bool showDiff = boolArg('diff') ?? true;
|
||||||
|
final bool showAddedFiles = boolArg('show-added-files') ?? true;
|
||||||
|
if (showDiff || _verbose) {
|
||||||
|
if (showAddedFiles || _verbose) {
|
||||||
|
for (final String localPath in manifest.addedFiles) {
|
||||||
|
logger.printStatus('Newly added file at $localPath:\n');
|
||||||
|
try {
|
||||||
|
logger.printStatus(stagingDirectory.childFile(localPath).readAsStringSync(), color: TerminalColor.green);
|
||||||
|
} on FileSystemException {
|
||||||
|
logger.printStatus('Contents are byte data\n', color: TerminalColor.grey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final List<String> files = <String>[];
|
||||||
|
files.addAll(manifest.mergedFiles);
|
||||||
|
files.addAll(manifest.resolvedConflictFiles(stagingDirectory));
|
||||||
|
files.addAll(manifest.remainingConflictFiles(stagingDirectory));
|
||||||
|
for (final String localPath in files) {
|
||||||
|
final DiffResult result = await migrateUtils.diffFiles(project.directory.childFile(localPath), stagingDirectory.childFile(localPath));
|
||||||
|
if (result.diff != '' && result.diff != null) {
|
||||||
|
// Print with different colors for better visibility.
|
||||||
|
int lineNumber = -1;
|
||||||
|
for (final String line in result.diff!.split('\n')) {
|
||||||
|
lineNumber++;
|
||||||
|
if (line.startsWith('---') || line.startsWith('+++') || line.startsWith('&&') || _initialDiffLines.contains(lineNumber)) {
|
||||||
|
logger.printStatus(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line.startsWith('-')) {
|
||||||
|
logger.printStatus(line, color: TerminalColor.red);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line.startsWith('+')) {
|
||||||
|
logger.printStatus(line, color: TerminalColor.green);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
logger.printStatus(line, color: TerminalColor.grey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.printBox('Working directory at `${stagingDirectory.path}`');
|
||||||
|
|
||||||
|
checkAndPrintMigrateStatus(manifest, stagingDirectory, logger: logger);
|
||||||
|
|
||||||
|
final bool readyToApply = manifest.remainingConflictFiles(stagingDirectory).isEmpty;
|
||||||
|
|
||||||
|
if (!readyToApply) {
|
||||||
|
logger.printStatus('Guided conflict resolution wizard:');
|
||||||
|
printCommandText('flutter migrate resolve-conflicts', logger);
|
||||||
|
logger.printStatus('Resolve conflicts and accept changes with:');
|
||||||
|
} else {
|
||||||
|
logger.printStatus('All conflicts resolved. Review changes above and '
|
||||||
|
'apply the migration with:',
|
||||||
|
color: TerminalColor.green);
|
||||||
|
}
|
||||||
|
printCommandText('flutter migrate apply', logger);
|
||||||
|
|
||||||
|
return const FlutterCommandResult(ExitStatus.success);
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,7 @@ import '../base/platform.dart';
|
|||||||
import '../base/process.dart';
|
import '../base/process.dart';
|
||||||
|
|
||||||
/// The default name of the migrate working directory used to stage proposed changes.
|
/// The default name of the migrate working directory used to stage proposed changes.
|
||||||
const String kDefaultMigrateWorkingDirectoryName = 'migrate_working_dir';
|
const String kDefaultMigrateStagingDirectoryName = 'migrate_staging_dir';
|
||||||
|
|
||||||
/// Utility class that contains methods that wrap git and other shell commands.
|
/// Utility class that contains methods that wrap git and other shell commands.
|
||||||
class MigrateUtils {
|
class MigrateUtils {
|
||||||
@ -179,14 +179,14 @@ class MigrateUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the workingDirectory git repo has any uncommited changes.
|
/// Returns true if the workingDirectory git repo has any uncommited changes.
|
||||||
Future<bool> hasUncommittedChanges(String workingDirectory, {String? migrateWorkingDir}) async {
|
Future<bool> hasUncommittedChanges(String workingDirectory, {String? migrateStagingDir}) async {
|
||||||
final List<String> cmdArgs = <String>[
|
final List<String> cmdArgs = <String>[
|
||||||
'git',
|
'git',
|
||||||
'ls-files',
|
'ls-files',
|
||||||
'--deleted',
|
'--deleted',
|
||||||
'--modified',
|
'--modified',
|
||||||
'--others',
|
'--others',
|
||||||
'--exclude=${migrateWorkingDir ?? kDefaultMigrateWorkingDirectoryName}'
|
'--exclude=${migrateStagingDir ?? kDefaultMigrateStagingDirectoryName}'
|
||||||
];
|
];
|
||||||
final RunResult result = await _processUtils.run(cmdArgs, workingDirectory: workingDirectory);
|
final RunResult result = await _processUtils.run(cmdArgs, workingDirectory: workingDirectory);
|
||||||
checkForErrors(result, allowedExitCodes: <int>[-1], commandDescription: cmdArgs.join(' '));
|
checkForErrors(result, allowedExitCodes: <int>[-1], commandDescription: cmdArgs.join(' '));
|
||||||
|
@ -0,0 +1,197 @@
|
|||||||
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
// @dart = 2.8
|
||||||
|
|
||||||
|
import 'package:flutter_tools/src/base/file_system.dart';
|
||||||
|
import 'package:flutter_tools/src/base/logger.dart';
|
||||||
|
import 'package:flutter_tools/src/base/platform.dart';
|
||||||
|
import 'package:flutter_tools/src/cache.dart';
|
||||||
|
import 'package:flutter_tools/src/commands/migrate.dart';
|
||||||
|
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||||
|
import 'package:flutter_tools/src/migrate/migrate_utils.dart';
|
||||||
|
|
||||||
|
import '../../src/common.dart';
|
||||||
|
import '../../src/context.dart';
|
||||||
|
import '../../src/test_flutter_command_runner.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
FileSystem fileSystem;
|
||||||
|
BufferLogger logger;
|
||||||
|
Platform platform;
|
||||||
|
// TODO(garyq): Add terminal back in when other subcommands land.
|
||||||
|
ProcessManager processManager;
|
||||||
|
Directory appDir;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
fileSystem = globals.localFileSystem;
|
||||||
|
appDir = fileSystem.systemTempDirectory.createTempSync('apptestdir');
|
||||||
|
logger = BufferLogger.test();
|
||||||
|
platform = FakePlatform();
|
||||||
|
processManager = globals.processManager;
|
||||||
|
});
|
||||||
|
|
||||||
|
setUpAll(() {
|
||||||
|
Cache.disableLocking();
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() async {
|
||||||
|
tryToDelete(appDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('Status produces all outputs', () async {
|
||||||
|
final MigrateCommand command = MigrateCommand(
|
||||||
|
verbose: true,
|
||||||
|
logger: logger,
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
platform: platform,
|
||||||
|
processManager: processManager,
|
||||||
|
);
|
||||||
|
final Directory stagingDir = appDir.childDirectory(kDefaultMigrateStagingDirectoryName);
|
||||||
|
final File pubspecOriginal = appDir.childFile('pubspec.yaml');
|
||||||
|
pubspecOriginal.createSync();
|
||||||
|
pubspecOriginal.writeAsStringSync('''
|
||||||
|
name: originalname
|
||||||
|
description: A new Flutter project.
|
||||||
|
version: 1.0.0+1
|
||||||
|
environment:
|
||||||
|
sdk: '>=2.18.0-58.0.dev <3.0.0'
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
flutter:
|
||||||
|
uses-material-design: true''', flush: true);
|
||||||
|
|
||||||
|
final File pubspecModified = stagingDir.childFile('pubspec.yaml');
|
||||||
|
pubspecModified.createSync(recursive: true);
|
||||||
|
pubspecModified.writeAsStringSync('''
|
||||||
|
name: newname
|
||||||
|
description: new description of the test project
|
||||||
|
version: 1.0.0+1
|
||||||
|
environment:
|
||||||
|
sdk: '>=2.18.0-58.0.dev <3.0.0'
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
flutter:
|
||||||
|
uses-material-design: false
|
||||||
|
EXTRALINE''', flush: true);
|
||||||
|
|
||||||
|
final File addedFile = stagingDir.childFile('added.file');
|
||||||
|
addedFile.createSync(recursive: true);
|
||||||
|
addedFile.writeAsStringSync('new file contents');
|
||||||
|
|
||||||
|
final File manifestFile = stagingDir.childFile('.migrate_manifest');
|
||||||
|
manifestFile.createSync(recursive: true);
|
||||||
|
manifestFile.writeAsStringSync('''
|
||||||
|
merged_files:
|
||||||
|
- pubspec.yaml
|
||||||
|
conflict_files:
|
||||||
|
added_files:
|
||||||
|
- added.file
|
||||||
|
deleted_files:
|
||||||
|
''');
|
||||||
|
|
||||||
|
await createTestCommandRunner(command).run(
|
||||||
|
<String>[
|
||||||
|
'migrate',
|
||||||
|
'status',
|
||||||
|
'--staging-directory=${stagingDir.path}',
|
||||||
|
'--project-directory=${appDir.path}',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(logger.statusText, contains('''
|
||||||
|
Newly added file at added.file:
|
||||||
|
|
||||||
|
new file contents'''));
|
||||||
|
expect(logger.statusText, contains(r'''
|
||||||
|
Added files:
|
||||||
|
- added.file
|
||||||
|
Modified files:
|
||||||
|
- pubspec.yaml
|
||||||
|
|
||||||
|
All conflicts resolved. Review changes above and apply the migration with:
|
||||||
|
|
||||||
|
$ flutter migrate apply
|
||||||
|
'''));
|
||||||
|
|
||||||
|
expect(logger.statusText, contains(r'''
|
||||||
|
@@ -1,5 +1,5 @@
|
||||||
|
-name: originalname
|
||||||
|
-description: A new Flutter project.
|
||||||
|
+name: newname
|
||||||
|
+description: new description of the test project
|
||||||
|
version: 1.0.0+1
|
||||||
|
environment:
|
||||||
|
sdk: '>=2.18.0-58.0.dev <3.0.0'
|
||||||
|
@@ -10,4 +10,5 @@ dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
flutter:
|
||||||
|
- uses-material-design: true
|
||||||
|
\ No newline at end of file
|
||||||
|
+ uses-material-design: false
|
||||||
|
+ EXTRALINE'''));
|
||||||
|
|
||||||
|
// Add conflict file
|
||||||
|
final File conflictFile = stagingDir.childDirectory('conflict').childFile('conflict.file');
|
||||||
|
conflictFile.createSync(recursive: true);
|
||||||
|
conflictFile.writeAsStringSync('''
|
||||||
|
line1
|
||||||
|
<<<<<<< /conflcit/conflict.file
|
||||||
|
line2
|
||||||
|
=======
|
||||||
|
linetwo
|
||||||
|
>>>>>>> /var/folders/md/gm0zgfcj07vcsj6jkh_mp_wh00ff02/T/flutter_tools.4Xdep8/generatedTargetTemplatetlN44S/conflict/conflict.file
|
||||||
|
line3
|
||||||
|
''', flush: true);
|
||||||
|
final File conflictFileOriginal = appDir.childDirectory('conflict').childFile('conflict.file');
|
||||||
|
conflictFileOriginal.createSync(recursive: true);
|
||||||
|
conflictFileOriginal.writeAsStringSync('''
|
||||||
|
line1
|
||||||
|
line2
|
||||||
|
line3
|
||||||
|
''', flush: true);
|
||||||
|
|
||||||
|
manifestFile.writeAsStringSync('''
|
||||||
|
merged_files:
|
||||||
|
- pubspec.yaml
|
||||||
|
conflict_files:
|
||||||
|
- conflict/conflict.file
|
||||||
|
added_files:
|
||||||
|
- added.file
|
||||||
|
deleted_files:
|
||||||
|
''');
|
||||||
|
|
||||||
|
logger.clear();
|
||||||
|
await createTestCommandRunner(command).run(
|
||||||
|
<String>[
|
||||||
|
'migrate',
|
||||||
|
'status',
|
||||||
|
'--staging-directory=${stagingDir.path}',
|
||||||
|
'--project-directory=${appDir.path}',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(logger.statusText, contains('''
|
||||||
|
@@ -1,3 +1,7 @@
|
||||||
|
line1
|
||||||
|
+<<<<<<< /conflcit/conflict.file
|
||||||
|
line2
|
||||||
|
+=======
|
||||||
|
+linetwo
|
||||||
|
+>>>>>>>'''));
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
FileSystem: () => fileSystem,
|
||||||
|
ProcessManager: () => processManager,
|
||||||
|
Platform: () => platform,
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user