Delete flutter_migrate code (#114253)
This commit is contained in:
parent
0c7ee58696
commit
7ab2bf8ff7
@ -1,96 +0,0 @@
|
|||||||
// 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_utils.dart';
|
|
||||||
import '../runner/flutter_command.dart';
|
|
||||||
import 'migrate_abandon.dart';
|
|
||||||
import 'migrate_apply.dart';
|
|
||||||
import 'migrate_status.dart';
|
|
||||||
|
|
||||||
/// Base command for the migration tool.
|
|
||||||
class MigrateCommand extends FlutterCommand {
|
|
||||||
MigrateCommand({
|
|
||||||
required bool verbose,
|
|
||||||
required this.logger,
|
|
||||||
required FileSystem fileSystem,
|
|
||||||
required Terminal terminal,
|
|
||||||
required Platform platform,
|
|
||||||
required ProcessManager processManager,
|
|
||||||
}) {
|
|
||||||
addSubcommand(MigrateStatusCommand(
|
|
||||||
verbose: verbose,
|
|
||||||
logger: logger,
|
|
||||||
fileSystem: fileSystem,
|
|
||||||
platform: platform,
|
|
||||||
processManager: processManager
|
|
||||||
));
|
|
||||||
addSubcommand(MigrateAbandonCommand(
|
|
||||||
logger: logger,
|
|
||||||
fileSystem: fileSystem,
|
|
||||||
terminal: terminal,
|
|
||||||
platform: platform,
|
|
||||||
processManager: processManager
|
|
||||||
));
|
|
||||||
addSubcommand(MigrateApplyCommand(
|
|
||||||
verbose: verbose,
|
|
||||||
logger: logger,
|
|
||||||
fileSystem: fileSystem,
|
|
||||||
terminal: terminal,
|
|
||||||
platform: platform,
|
|
||||||
processManager: processManager
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
final Logger logger;
|
|
||||||
|
|
||||||
@override
|
|
||||||
final String name = 'migrate';
|
|
||||||
|
|
||||||
@override
|
|
||||||
final String description = 'Migrates flutter generated project files to the current flutter version';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get category => FlutterCommandCategory.project;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => const <DevelopmentArtifact>{};
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<FlutterCommandResult> runCommand() async {
|
|
||||||
return const FlutterCommandResult(ExitStatus.fail);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> gitRepoExists(String projectDirectory, Logger logger, MigrateUtils migrateUtils) async {
|
|
||||||
if (await migrateUtils.isGitRepo(projectDirectory)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
logger.printStatus('Project is not a git repo. Please initialize a git repo and try again.');
|
|
||||||
printCommandText('git init', logger);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> hasUncommittedChanges(String projectDirectory, Logger logger, MigrateUtils migrateUtils) async {
|
|
||||||
if (await migrateUtils.hasUncommittedChanges(projectDirectory)) {
|
|
||||||
logger.printStatus('There are uncommitted changes in your project. Please git commit, abandon, or stash your changes before trying again.');
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prints a command to logger with appropriate formatting.
|
|
||||||
void printCommandText(String command, Logger logger) {
|
|
||||||
logger.printStatus(
|
|
||||||
'\n\$ $command\n',
|
|
||||||
color: TerminalColor.grey,
|
|
||||||
indent: 4,
|
|
||||||
newline: false,
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,134 +0,0 @@
|
|||||||
// 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_utils.dart';
|
|
||||||
import '../project.dart';
|
|
||||||
import '../runner/flutter_command.dart';
|
|
||||||
import 'migrate.dart';
|
|
||||||
|
|
||||||
/// Abandons the existing migration by deleting the migrate working directory.
|
|
||||||
class MigrateAbandonCommand extends FlutterCommand {
|
|
||||||
MigrateAbandonCommand({
|
|
||||||
required this.logger,
|
|
||||||
required this.fileSystem,
|
|
||||||
required this.terminal,
|
|
||||||
required Platform platform,
|
|
||||||
required ProcessManager processManager,
|
|
||||||
}) : 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(
|
|
||||||
'force',
|
|
||||||
abbr: 'f',
|
|
||||||
help: 'Delete the migrate working directory without asking for confirmation.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final Logger logger;
|
|
||||||
|
|
||||||
final FileSystem fileSystem;
|
|
||||||
|
|
||||||
final Terminal terminal;
|
|
||||||
|
|
||||||
final MigrateUtils migrateUtils;
|
|
||||||
|
|
||||||
@override
|
|
||||||
final String name = 'abandon';
|
|
||||||
|
|
||||||
@override
|
|
||||||
final String description = 'Deletes the current active migration working directory.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get category => FlutterCommandCategory.project;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => const <DevelopmentArtifact>{};
|
|
||||||
|
|
||||||
@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.printError('Provided staging directory `$customStagingDirectoryPath` '
|
|
||||||
'does not exist or is not valid.');
|
|
||||||
return const FlutterCommandResult(ExitStatus.fail);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!stagingDirectory.existsSync()) {
|
|
||||||
logger.printStatus('No migration in progress. Start a new migration with:');
|
|
||||||
printCommandText('flutter migrate start', logger);
|
|
||||||
return const FlutterCommandResult(ExitStatus.fail);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.printStatus('\nAbandoning the existing migration will delete the '
|
|
||||||
'migration staging directory at ${stagingDirectory.path}');
|
|
||||||
final bool force = boolArg('force') ?? false;
|
|
||||||
if (!force) {
|
|
||||||
String selection = 'y';
|
|
||||||
terminal.usesTerminalUi = true;
|
|
||||||
try {
|
|
||||||
selection = await terminal.promptForCharInput(
|
|
||||||
<String>['y', 'n'],
|
|
||||||
logger: logger,
|
|
||||||
prompt: 'Are you sure you wish to continue with abandoning? (y)es, (N)o',
|
|
||||||
defaultChoiceIndex: 1,
|
|
||||||
);
|
|
||||||
} on StateError catch(e) {
|
|
||||||
logger.printError(
|
|
||||||
e.message,
|
|
||||||
indent: 0,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (selection != 'y') {
|
|
||||||
return const FlutterCommandResult(ExitStatus.success);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
stagingDirectory.deleteSync(recursive: true);
|
|
||||||
} on FileSystemException catch (e) {
|
|
||||||
logger.printError('Deletion failed with: $e');
|
|
||||||
logger.printError('Please manually delete the staging directory at `${stagingDirectory.path}`');
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.printStatus('\nAbandon complete. Start a new migration with:');
|
|
||||||
printCommandText('flutter migrate start', logger);
|
|
||||||
return const FlutterCommandResult(ExitStatus.success);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,204 +0,0 @@
|
|||||||
// 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 '../flutter_project_metadata.dart';
|
|
||||||
import '../migrate/migrate_manifest.dart';
|
|
||||||
import '../migrate/migrate_update_locks.dart';
|
|
||||||
import '../migrate/migrate_utils.dart';
|
|
||||||
import '../project.dart';
|
|
||||||
import '../runner/flutter_command.dart';
|
|
||||||
import '../version.dart';
|
|
||||||
import 'migrate.dart';
|
|
||||||
|
|
||||||
/// Migrate subcommand that checks the migrate working directory for unresolved conflicts and
|
|
||||||
/// applies the staged changes to the project.
|
|
||||||
class MigrateApplyCommand extends FlutterCommand {
|
|
||||||
MigrateApplyCommand({
|
|
||||||
bool verbose = false,
|
|
||||||
required this.logger,
|
|
||||||
required this.fileSystem,
|
|
||||||
required this.terminal,
|
|
||||||
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(
|
|
||||||
'force',
|
|
||||||
abbr: 'f',
|
|
||||||
help: 'Ignore unresolved merge conflicts and uncommitted changes and '
|
|
||||||
'apply staged changes by force.',
|
|
||||||
);
|
|
||||||
argParser.addFlag(
|
|
||||||
'keep-working-directory',
|
|
||||||
help: 'Do not delete the working directory.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final bool _verbose;
|
|
||||||
|
|
||||||
final Logger logger;
|
|
||||||
|
|
||||||
final FileSystem fileSystem;
|
|
||||||
|
|
||||||
final Terminal terminal;
|
|
||||||
|
|
||||||
final MigrateUtils migrateUtils;
|
|
||||||
|
|
||||||
@override
|
|
||||||
final String name = 'apply';
|
|
||||||
|
|
||||||
@override
|
|
||||||
final String description = r'Accepts the changes produced by `$ flutter '
|
|
||||||
'migrate start` and copies the changed files into '
|
|
||||||
'your project files. All merge conflicts should '
|
|
||||||
'be resolved before apply will complete '
|
|
||||||
'successfully. If conflicts still exist, this '
|
|
||||||
'command will print the remaining conflicted files.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get category => FlutterCommandCategory.project;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => const <DevelopmentArtifact>{};
|
|
||||||
|
|
||||||
@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));
|
|
||||||
|
|
||||||
if (!await gitRepoExists(project.directory.path, logger, migrateUtils)) {
|
|
||||||
logger.printStatus('No git repo found. Please run in a project with an '
|
|
||||||
'initialized git repo or initialize one with:');
|
|
||||||
printCommandText('git init', logger);
|
|
||||||
return const FlutterCommandResult(ExitStatus.fail);
|
|
||||||
}
|
|
||||||
|
|
||||||
final bool force = boolArg('force') ?? false;
|
|
||||||
|
|
||||||
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 at $stagingDirectory. Please run:');
|
|
||||||
printCommandText('flutter migrate start', logger);
|
|
||||||
return const FlutterCommandResult(ExitStatus.fail);
|
|
||||||
}
|
|
||||||
|
|
||||||
final File manifestFile = MigrateManifest.getManifestFileFromDirectory(stagingDirectory);
|
|
||||||
final MigrateManifest manifest = MigrateManifest.fromFile(manifestFile);
|
|
||||||
if (!checkAndPrintMigrateStatus(manifest, stagingDirectory, warnConflict: true, logger: logger) && !force) {
|
|
||||||
logger.printStatus('Conflicting files found. Resolve these conflicts and try again.');
|
|
||||||
logger.printStatus('Guided conflict resolution wizard:');
|
|
||||||
printCommandText('flutter migrate resolve-conflicts', logger);
|
|
||||||
return const FlutterCommandResult(ExitStatus.fail);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await hasUncommittedChanges(project.directory.path, logger, migrateUtils) && !force) {
|
|
||||||
return const FlutterCommandResult(ExitStatus.fail);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.printStatus('Applying migration.');
|
|
||||||
// Copy files from working directory to project root
|
|
||||||
final List<String> allFilesToCopy = <String>[];
|
|
||||||
allFilesToCopy.addAll(manifest.mergedFiles);
|
|
||||||
allFilesToCopy.addAll(manifest.conflictFiles);
|
|
||||||
allFilesToCopy.addAll(manifest.addedFiles);
|
|
||||||
if (allFilesToCopy.isNotEmpty && _verbose) {
|
|
||||||
logger.printStatus('Modifying ${allFilesToCopy.length} files.', indent: 2);
|
|
||||||
}
|
|
||||||
for (final String localPath in allFilesToCopy) {
|
|
||||||
if (_verbose) {
|
|
||||||
logger.printStatus('Writing $localPath');
|
|
||||||
}
|
|
||||||
final File workingFile = stagingDirectory.childFile(localPath);
|
|
||||||
final File targetFile = project.directory.childFile(localPath);
|
|
||||||
if (!workingFile.existsSync()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!targetFile.existsSync()) {
|
|
||||||
targetFile.createSync(recursive: true);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
targetFile.writeAsStringSync(workingFile.readAsStringSync(), flush: true);
|
|
||||||
} on FileSystemException {
|
|
||||||
targetFile.writeAsBytesSync(workingFile.readAsBytesSync(), flush: true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Delete files slated for deletion.
|
|
||||||
if (manifest.deletedFiles.isNotEmpty) {
|
|
||||||
logger.printStatus('Deleting ${manifest.deletedFiles.length} files.', indent: 2);
|
|
||||||
}
|
|
||||||
for (final String localPath in manifest.deletedFiles) {
|
|
||||||
final File targetFile = FlutterProject.current().directory.childFile(localPath);
|
|
||||||
targetFile.deleteSync();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the migrate config files to reflect latest migration.
|
|
||||||
if (_verbose) {
|
|
||||||
logger.printStatus('Updating .migrate_configs');
|
|
||||||
}
|
|
||||||
final FlutterProjectMetadata metadata = FlutterProjectMetadata(project.directory.childFile('.metadata'), logger);
|
|
||||||
final FlutterVersion version = FlutterVersion(workingDirectory: project.directory.absolute.path);
|
|
||||||
|
|
||||||
final String currentGitHash = version.frameworkRevision;
|
|
||||||
metadata.migrateConfig.populate(
|
|
||||||
projectDirectory: project.directory,
|
|
||||||
currentRevision: currentGitHash,
|
|
||||||
logger: logger,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Clean up the working directory
|
|
||||||
final bool keepWorkingDirectory = boolArg('keep-working-directory') ?? false;
|
|
||||||
if (!keepWorkingDirectory) {
|
|
||||||
stagingDirectory.deleteSync(recursive: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect pub dependency locking. Run flutter pub upgrade --major-versions
|
|
||||||
await updatePubspecDependencies(project, migrateUtils, logger, terminal);
|
|
||||||
|
|
||||||
// Detect gradle lockfiles in android directory. Delete lockfiles and regenerate with ./gradlew tasks (any gradle task that requires a build).
|
|
||||||
await updateGradleDependencyLocking(project, migrateUtils, logger, terminal, _verbose, fileSystem);
|
|
||||||
|
|
||||||
logger.printStatus('Migration complete. You may use commands like `git '
|
|
||||||
'status`, `git diff` and `git restore <file>` to continue '
|
|
||||||
'working with the migrated files.');
|
|
||||||
return const FlutterCommandResult(ExitStatus.success);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,176 +0,0 @@
|
|||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,241 +0,0 @@
|
|||||||
// 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:yaml/yaml.dart';
|
|
||||||
|
|
||||||
import '../base/file_system.dart';
|
|
||||||
import '../base/logger.dart';
|
|
||||||
import '../base/terminal.dart';
|
|
||||||
import 'migrate_result.dart';
|
|
||||||
import 'migrate_utils.dart';
|
|
||||||
|
|
||||||
const String _kMergedFilesKey = 'merged_files';
|
|
||||||
const String _kConflictFilesKey = 'conflict_files';
|
|
||||||
const String _kAddedFilesKey = 'added_files';
|
|
||||||
const String _kDeletedFilesKey = 'deleted_files';
|
|
||||||
|
|
||||||
/// Represents the manifest file that tracks the contents of the current
|
|
||||||
/// migration working directory.
|
|
||||||
///
|
|
||||||
/// This manifest file is created with the MigrateResult of a computeMigration run.
|
|
||||||
class MigrateManifest {
|
|
||||||
/// Creates a new manifest from a MigrateResult.
|
|
||||||
MigrateManifest({
|
|
||||||
required this.migrateRootDir,
|
|
||||||
required this.migrateResult,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Parses an existing migrate manifest.
|
|
||||||
MigrateManifest.fromFile(File manifestFile) : migrateResult = MigrateResult.empty(), migrateRootDir = manifestFile.parent {
|
|
||||||
final Object? yamlContents = loadYaml(manifestFile.readAsStringSync());
|
|
||||||
if (yamlContents is! YamlMap) {
|
|
||||||
throw Exception('Invalid .migrate_manifest file in the migrate working directory. File is not a Yaml map.');
|
|
||||||
}
|
|
||||||
final YamlMap map = yamlContents;
|
|
||||||
bool valid = map.containsKey(_kMergedFilesKey) && map.containsKey(_kConflictFilesKey) && map.containsKey(_kAddedFilesKey) && map.containsKey(_kDeletedFilesKey);
|
|
||||||
if (!valid) {
|
|
||||||
throw Exception('Invalid .migrate_manifest file in the migrate working directory. File is missing an entry.');
|
|
||||||
}
|
|
||||||
final Object? mergedFilesYaml = map[_kMergedFilesKey];
|
|
||||||
final Object? conflictFilesYaml = map[_kConflictFilesKey];
|
|
||||||
final Object? addedFilesYaml = map[_kAddedFilesKey];
|
|
||||||
final Object? deletedFilesYaml = map[_kDeletedFilesKey];
|
|
||||||
valid = valid && (mergedFilesYaml is YamlList || mergedFilesYaml == null);
|
|
||||||
valid = valid && (conflictFilesYaml is YamlList || conflictFilesYaml == null);
|
|
||||||
valid = valid && (addedFilesYaml is YamlList || addedFilesYaml == null);
|
|
||||||
valid = valid && (deletedFilesYaml is YamlList || deletedFilesYaml == null);
|
|
||||||
if (!valid) {
|
|
||||||
throw Exception('Invalid .migrate_manifest file in the migrate working directory. Entry is not a Yaml list.');
|
|
||||||
}
|
|
||||||
if (mergedFilesYaml != null) {
|
|
||||||
for (final Object? localPath in mergedFilesYaml as YamlList) {
|
|
||||||
if (localPath is String) {
|
|
||||||
// We can fill the maps with partially dummy data as not all properties are used by the manifest.
|
|
||||||
migrateResult.mergeResults.add(StringMergeResult.explicit(mergedString: '', hasConflict: false, exitCode: 0, localPath: localPath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (conflictFilesYaml != null) {
|
|
||||||
for (final Object? localPath in conflictFilesYaml as YamlList) {
|
|
||||||
if (localPath is String) {
|
|
||||||
migrateResult.mergeResults.add(StringMergeResult.explicit(mergedString: '', hasConflict: true, exitCode: 1, localPath: localPath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (addedFilesYaml != null) {
|
|
||||||
for (final Object? localPath in addedFilesYaml as YamlList) {
|
|
||||||
if (localPath is String) {
|
|
||||||
migrateResult.addedFiles.add(FilePendingMigration(localPath, migrateRootDir.childFile(localPath)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (deletedFilesYaml != null) {
|
|
||||||
for (final Object? localPath in deletedFilesYaml as YamlList) {
|
|
||||||
if (localPath is String) {
|
|
||||||
migrateResult.deletedFiles.add(FilePendingMigration(localPath, migrateRootDir.childFile(localPath)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final Directory migrateRootDir;
|
|
||||||
final MigrateResult migrateResult;
|
|
||||||
|
|
||||||
/// A list of local paths of files that require conflict resolution.
|
|
||||||
List<String> get conflictFiles {
|
|
||||||
final List<String> output = <String>[];
|
|
||||||
for (final MergeResult result in migrateResult.mergeResults) {
|
|
||||||
if (result.hasConflict) {
|
|
||||||
output.add(result.localPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A list of local paths of files that require conflict resolution.
|
|
||||||
List<String> remainingConflictFiles(Directory workingDir) {
|
|
||||||
final List<String> output = <String>[];
|
|
||||||
for (final String localPath in conflictFiles) {
|
|
||||||
if (!_conflictsResolved(workingDir.childFile(localPath).readAsStringSync())) {
|
|
||||||
output.add(localPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A list of local paths of files that had conflicts and are now fully resolved.
|
|
||||||
List<String> resolvedConflictFiles(Directory workingDir) {
|
|
||||||
final List<String> output = <String>[];
|
|
||||||
for (final String localPath in conflictFiles) {
|
|
||||||
if (_conflictsResolved(workingDir.childFile(localPath).readAsStringSync())) {
|
|
||||||
output.add(localPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A list of local paths of files that were automatically merged.
|
|
||||||
List<String> get mergedFiles {
|
|
||||||
final List<String> output = <String>[];
|
|
||||||
for (final MergeResult result in migrateResult.mergeResults) {
|
|
||||||
if (!result.hasConflict) {
|
|
||||||
output.add(result.localPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A list of local paths of files that were newly added.
|
|
||||||
List<String> get addedFiles {
|
|
||||||
final List<String> output = <String>[];
|
|
||||||
for (final FilePendingMigration file in migrateResult.addedFiles) {
|
|
||||||
output.add(file.localPath);
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A list of local paths of files that are marked for deletion.
|
|
||||||
List<String> get deletedFiles {
|
|
||||||
final List<String> output = <String>[];
|
|
||||||
for (final FilePendingMigration file in migrateResult.deletedFiles) {
|
|
||||||
output.add(file.localPath);
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the manifest file given a migration workind directory.
|
|
||||||
static File getManifestFileFromDirectory(Directory workingDir) {
|
|
||||||
return workingDir.childFile('.migrate_manifest');
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes the manifest yaml file in the working directory.
|
|
||||||
void writeFile() {
|
|
||||||
final StringBuffer mergedFileManifestContents = StringBuffer();
|
|
||||||
final StringBuffer conflictFilesManifestContents = StringBuffer();
|
|
||||||
for (final MergeResult result in migrateResult.mergeResults) {
|
|
||||||
if (result.hasConflict) {
|
|
||||||
conflictFilesManifestContents.write(' - ${result.localPath}\n');
|
|
||||||
} else {
|
|
||||||
mergedFileManifestContents.write(' - ${result.localPath}\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final StringBuffer newFileManifestContents = StringBuffer();
|
|
||||||
for (final String localPath in addedFiles) {
|
|
||||||
newFileManifestContents.write(' - $localPath\n)');
|
|
||||||
}
|
|
||||||
|
|
||||||
final StringBuffer deletedFileManifestContents = StringBuffer();
|
|
||||||
for (final String localPath in deletedFiles) {
|
|
||||||
deletedFileManifestContents.write(' - $localPath\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
final String migrateManifestContents = 'merged_files:\n${mergedFileManifestContents}conflict_files:\n${conflictFilesManifestContents}added_files:\n${newFileManifestContents}deleted_files:\n$deletedFileManifestContents';
|
|
||||||
final File migrateManifest = getManifestFileFromDirectory(migrateRootDir);
|
|
||||||
migrateManifest.createSync(recursive: true);
|
|
||||||
migrateManifest.writeAsStringSync(migrateManifestContents, flush: true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the file does not contain any git conflict markers.
|
|
||||||
bool _conflictsResolved(String contents) {
|
|
||||||
if (contents.contains('>>>>>>>') && contents.contains('=======') && contents.contains('<<<<<<<')) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the migration working directory has all conflicts resolved and prints the migration status.
|
|
||||||
///
|
|
||||||
/// The migration status printout lists all added, deleted, merged, and conflicted files.
|
|
||||||
bool checkAndPrintMigrateStatus(MigrateManifest manifest, Directory workingDir, {bool warnConflict = false, Logger? logger}) {
|
|
||||||
final StringBuffer printout = StringBuffer();
|
|
||||||
final StringBuffer redPrintout = StringBuffer();
|
|
||||||
bool result = true;
|
|
||||||
final List<String> remainingConflicts = <String>[];
|
|
||||||
final List<String> mergedFiles = <String>[];
|
|
||||||
for (final String localPath in manifest.conflictFiles) {
|
|
||||||
if (!_conflictsResolved(workingDir.childFile(localPath).readAsStringSync())) {
|
|
||||||
remainingConflicts.add(localPath);
|
|
||||||
} else {
|
|
||||||
mergedFiles.add(localPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mergedFiles.addAll(manifest.mergedFiles);
|
|
||||||
if (manifest.addedFiles.isNotEmpty) {
|
|
||||||
printout.write('Added files:\n');
|
|
||||||
for (final String localPath in manifest.addedFiles) {
|
|
||||||
printout.write(' - $localPath\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (manifest.deletedFiles.isNotEmpty) {
|
|
||||||
printout.write('Deleted files:\n');
|
|
||||||
for (final String localPath in manifest.deletedFiles) {
|
|
||||||
printout.write(' - $localPath\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mergedFiles.isNotEmpty) {
|
|
||||||
printout.write('Modified files:\n');
|
|
||||||
for (final String localPath in mergedFiles) {
|
|
||||||
printout.write(' - $localPath\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (remainingConflicts.isNotEmpty) {
|
|
||||||
if (warnConflict) {
|
|
||||||
printout.write('Unable to apply migration. The following files in the migration working directory still have unresolved conflicts:');
|
|
||||||
} else {
|
|
||||||
printout.write('Merge conflicted files:');
|
|
||||||
}
|
|
||||||
for (final String localPath in remainingConflicts) {
|
|
||||||
redPrintout.write(' - $localPath\n');
|
|
||||||
}
|
|
||||||
result = false;
|
|
||||||
}
|
|
||||||
if (logger != null) {
|
|
||||||
logger.printStatus(printout.toString());
|
|
||||||
logger.printStatus(redPrintout.toString(), color: TerminalColor.red, newline: false);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
@ -1,82 +0,0 @@
|
|||||||
// 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 '../base/file_system.dart';
|
|
||||||
import 'migrate_utils.dart';
|
|
||||||
|
|
||||||
/// Data class that holds all results and generated directories from a computeMigration run.
|
|
||||||
///
|
|
||||||
/// mergeResults, addedFiles, and deletedFiles includes the sets of files to be migrated while
|
|
||||||
/// the other members track the temporary sdk and generated app directories created by the tool.
|
|
||||||
///
|
|
||||||
/// The compute function does not clean up the temp directories, as the directories may be reused,
|
|
||||||
/// so this must be done manually afterwards.
|
|
||||||
class MigrateResult {
|
|
||||||
/// Explicitly initialize the MigrateResult.
|
|
||||||
MigrateResult({
|
|
||||||
required this.mergeResults,
|
|
||||||
required this.addedFiles,
|
|
||||||
required this.deletedFiles,
|
|
||||||
required this.tempDirectories,
|
|
||||||
required this.sdkDirs,
|
|
||||||
required this.mergeTypeMap,
|
|
||||||
required this.diffMap,
|
|
||||||
this.generatedBaseTemplateDirectory,
|
|
||||||
this.generatedTargetTemplateDirectory});
|
|
||||||
|
|
||||||
/// Creates a MigrateResult with all empty members.
|
|
||||||
MigrateResult.empty()
|
|
||||||
: mergeResults = <MergeResult>[],
|
|
||||||
addedFiles = <FilePendingMigration>[],
|
|
||||||
deletedFiles = <FilePendingMigration>[],
|
|
||||||
tempDirectories = <Directory>[],
|
|
||||||
mergeTypeMap = <String, MergeType>{},
|
|
||||||
diffMap = <String, DiffResult>{},
|
|
||||||
sdkDirs = <String, Directory>{};
|
|
||||||
|
|
||||||
/// The results of merging existing files with the target files.
|
|
||||||
final List<MergeResult> mergeResults;
|
|
||||||
|
|
||||||
/// Tracks the files that are to be newly added to the project.
|
|
||||||
final List<FilePendingMigration> addedFiles;
|
|
||||||
|
|
||||||
/// Tracks the files that are to be deleted from the project.
|
|
||||||
final List<FilePendingMigration> deletedFiles;
|
|
||||||
|
|
||||||
/// Tracks the temporary directories created during the migrate compute process.
|
|
||||||
final List<Directory> tempDirectories;
|
|
||||||
|
|
||||||
/// Mapping between the local path of a file and the type of merge that should be used.
|
|
||||||
final Map<String, MergeType> mergeTypeMap;
|
|
||||||
|
|
||||||
/// Mapping between the local path of a file and the diff between the base and target
|
|
||||||
/// versions of the file.
|
|
||||||
final Map<String, DiffResult> diffMap;
|
|
||||||
|
|
||||||
/// The root directory of the base app.
|
|
||||||
Directory? generatedBaseTemplateDirectory;
|
|
||||||
|
|
||||||
/// The root directory of the target app.
|
|
||||||
Directory? generatedTargetTemplateDirectory;
|
|
||||||
|
|
||||||
/// The root directories of the Flutter SDK for each revision.
|
|
||||||
Map<String, Directory> sdkDirs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defines available merge techniques.
|
|
||||||
enum MergeType {
|
|
||||||
/// A standard three-way merge.
|
|
||||||
threeWay,
|
|
||||||
/// A two way merge that ignores the base version of the file.
|
|
||||||
twoWay,
|
|
||||||
/// A `CustomMerge` manually handles the merge.
|
|
||||||
custom,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stores a file that has been marked for migration and metadata about the file.
|
|
||||||
class FilePendingMigration {
|
|
||||||
FilePendingMigration(this.localPath, this.file);
|
|
||||||
String localPath;
|
|
||||||
File file;
|
|
||||||
}
|
|
@ -1,116 +0,0 @@
|
|||||||
// 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 '../base/file_system.dart';
|
|
||||||
import '../base/logger.dart';
|
|
||||||
import '../base/terminal.dart';
|
|
||||||
import '../project.dart';
|
|
||||||
import 'migrate_utils.dart';
|
|
||||||
|
|
||||||
/// Checks if the project uses pubspec dependency locking and prompts if
|
|
||||||
/// the pub upgrade should be run.
|
|
||||||
Future<void> updatePubspecDependencies(
|
|
||||||
FlutterProject flutterProject,
|
|
||||||
MigrateUtils migrateUtils,
|
|
||||||
Logger logger,
|
|
||||||
Terminal terminal
|
|
||||||
) async {
|
|
||||||
final File pubspecFile = flutterProject.directory.childFile('pubspec.yaml');
|
|
||||||
if (!pubspecFile.existsSync()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!pubspecFile.readAsStringSync().contains('# THIS LINE IS AUTOGENERATED')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
logger.printStatus('\nDart dependency locking detected in pubspec.yaml.');
|
|
||||||
terminal.usesTerminalUi = true;
|
|
||||||
String selection = 'y';
|
|
||||||
selection = await terminal.promptForCharInput(
|
|
||||||
<String>['y', 'n'],
|
|
||||||
logger: logger,
|
|
||||||
prompt: 'Do you want the tool to run `flutter pub upgrade --major-versions`? (y)es, (n)o',
|
|
||||||
defaultChoiceIndex: 1,
|
|
||||||
);
|
|
||||||
if (selection == 'y') {
|
|
||||||
// Runs `flutter pub upgrade --major-versions`
|
|
||||||
await migrateUtils.flutterPubUpgrade(flutterProject.directory.path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if gradle dependency locking is used and prompts the developer to
|
|
||||||
/// remove and back up the gradle dependency lockfile.
|
|
||||||
Future<void> updateGradleDependencyLocking(
|
|
||||||
FlutterProject flutterProject,
|
|
||||||
MigrateUtils migrateUtils,
|
|
||||||
Logger logger,
|
|
||||||
Terminal terminal,
|
|
||||||
bool verbose,
|
|
||||||
FileSystem fileSystem
|
|
||||||
) async {
|
|
||||||
final Directory androidDir = flutterProject.directory.childDirectory('android');
|
|
||||||
if (!androidDir.existsSync()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final List<FileSystemEntity> androidFiles = androidDir.listSync();
|
|
||||||
final List<File> lockfiles = <File>[];
|
|
||||||
final List<String> backedUpFilePaths = <String>[];
|
|
||||||
for (final FileSystemEntity entity in androidFiles) {
|
|
||||||
if (entity is! File) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final File file = entity.absolute;
|
|
||||||
// Don't re-handle backed up lockfiles.
|
|
||||||
if (file.path.contains('_backup_')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// lockfiles generated by gradle start with this prefix.
|
|
||||||
if (file.readAsStringSync().startsWith(
|
|
||||||
'# This is a Gradle generated file for dependency locking.\n# '
|
|
||||||
'Manual edits can break the build and are not advised.\n# This '
|
|
||||||
'file is expected to be part of source control.')) {
|
|
||||||
lockfiles.add(file);
|
|
||||||
}
|
|
||||||
} on FileSystemException {
|
|
||||||
if (verbose) {
|
|
||||||
logger.printStatus('Unable to check ${file.path}');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (lockfiles.isNotEmpty) {
|
|
||||||
logger.printStatus('\nGradle dependency locking detected.');
|
|
||||||
logger.printStatus('Flutter can backup the lockfiles and regenerate updated '
|
|
||||||
'lockfiles.');
|
|
||||||
terminal.usesTerminalUi = true;
|
|
||||||
String selection = 'y';
|
|
||||||
selection = await terminal.promptForCharInput(
|
|
||||||
<String>['y', 'n'],
|
|
||||||
logger: logger,
|
|
||||||
prompt: 'Do you want the tool to update locked dependencies? (y)es, (n)o',
|
|
||||||
defaultChoiceIndex: 1,
|
|
||||||
);
|
|
||||||
if (selection == 'y') {
|
|
||||||
for (final File file in lockfiles) {
|
|
||||||
int counter = 0;
|
|
||||||
while (true) {
|
|
||||||
final String newPath = '${file.absolute.path}_backup_$counter';
|
|
||||||
if (!fileSystem.file(newPath).existsSync()) {
|
|
||||||
file.renameSync(newPath);
|
|
||||||
backedUpFilePaths.add(newPath);
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Runs `./gradlew tasks`in the project's android directory.
|
|
||||||
await migrateUtils.gradlewTasks(flutterProject.directory.childDirectory('android').path);
|
|
||||||
logger.printStatus('Old lockfiles renamed to:');
|
|
||||||
for (final String path in backedUpFilePaths) {
|
|
||||||
logger.printStatus(path, color: TerminalColor.grey, indent: 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,359 +0,0 @@
|
|||||||
// 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 'dart:async';
|
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:process/process.dart';
|
|
||||||
|
|
||||||
import '../base/common.dart';
|
|
||||||
import '../base/file_system.dart';
|
|
||||||
import '../base/logger.dart';
|
|
||||||
import '../base/platform.dart';
|
|
||||||
import '../base/process.dart';
|
|
||||||
|
|
||||||
/// The default name of the migrate working directory used to stage proposed changes.
|
|
||||||
const String kDefaultMigrateStagingDirectoryName = 'migrate_staging_dir';
|
|
||||||
|
|
||||||
/// Utility class that contains methods that wrap git and other shell commands.
|
|
||||||
class MigrateUtils {
|
|
||||||
MigrateUtils({
|
|
||||||
required Logger logger,
|
|
||||||
required FileSystem fileSystem,
|
|
||||||
required Platform platform,
|
|
||||||
required ProcessManager processManager,
|
|
||||||
}) :
|
|
||||||
_processUtils = ProcessUtils(processManager: processManager, logger: logger),
|
|
||||||
_logger = logger,
|
|
||||||
_fileSystem = fileSystem,
|
|
||||||
_platform = platform;
|
|
||||||
|
|
||||||
final Logger _logger;
|
|
||||||
final FileSystem _fileSystem;
|
|
||||||
final Platform _platform;
|
|
||||||
final ProcessUtils _processUtils;
|
|
||||||
|
|
||||||
/// Calls `git diff` on two files and returns the diff as a DiffResult.
|
|
||||||
Future<DiffResult> diffFiles(File one, File two) async {
|
|
||||||
if (one.existsSync() && !two.existsSync()) {
|
|
||||||
return DiffResult(diffType: DiffType.deletion);
|
|
||||||
}
|
|
||||||
if (!one.existsSync() && two.existsSync()) {
|
|
||||||
return DiffResult(diffType: DiffType.addition);
|
|
||||||
}
|
|
||||||
final List<String> cmdArgs = <String>['git', 'diff', '--no-index', one.absolute.path, two.absolute.path];
|
|
||||||
final RunResult result = await _processUtils.run(cmdArgs);
|
|
||||||
|
|
||||||
// diff exits with 1 if diffs are found.
|
|
||||||
checkForErrors(result, allowedExitCodes: <int>[0, 1], commandDescription: 'git ${cmdArgs.join(' ')}');
|
|
||||||
return DiffResult(diffType: DiffType.command, diff: result.stdout, exitCode: result.exitCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clones a copy of the flutter repo into the destination directory. Returns false if unsuccessful.
|
|
||||||
Future<bool> cloneFlutter(String revision, String destination) async {
|
|
||||||
// Use https url instead of ssh to avoid need to setup ssh on git.
|
|
||||||
List<String> cmdArgs = <String>['git', 'clone', '--filter=blob:none', 'https://github.com/flutter/flutter.git', destination];
|
|
||||||
RunResult result = await _processUtils.run(cmdArgs);
|
|
||||||
checkForErrors(result, commandDescription: cmdArgs.join(' '));
|
|
||||||
|
|
||||||
cmdArgs.clear();
|
|
||||||
cmdArgs = <String>['git', 'reset', '--hard', revision];
|
|
||||||
result = await _processUtils.run(cmdArgs, workingDirectory: destination);
|
|
||||||
if (!checkForErrors(result, commandDescription: cmdArgs.join(' '), exit: false)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calls `flutter create` as a re-entrant command.
|
|
||||||
Future<String> createFromTemplates(String flutterBinPath, {
|
|
||||||
required String name,
|
|
||||||
bool legacyNameParameter = false,
|
|
||||||
required String androidLanguage,
|
|
||||||
required String iosLanguage,
|
|
||||||
required String outputDirectory,
|
|
||||||
String? createVersion,
|
|
||||||
List<String> platforms = const <String>[],
|
|
||||||
int iterationsAllowed = 5,
|
|
||||||
}) async {
|
|
||||||
// Limit the number of iterations this command is allowed to attempt to prevent infinite looping.
|
|
||||||
if (iterationsAllowed <= 0) {
|
|
||||||
_logger.printError('Unable to `flutter create` with the version of flutter at $flutterBinPath');
|
|
||||||
return outputDirectory;
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<String> cmdArgs = <String>['$flutterBinPath/flutter', 'create'];
|
|
||||||
if (!legacyNameParameter) {
|
|
||||||
cmdArgs.add('--project-name=$name');
|
|
||||||
}
|
|
||||||
cmdArgs.add('--android-language=$androidLanguage');
|
|
||||||
cmdArgs.add('--ios-language=$iosLanguage');
|
|
||||||
if (platforms.isNotEmpty) {
|
|
||||||
String platformsArg = '--platforms=';
|
|
||||||
for (int i = 0; i < platforms.length; i++) {
|
|
||||||
if (i > 0) {
|
|
||||||
platformsArg += ',';
|
|
||||||
}
|
|
||||||
platformsArg += platforms[i];
|
|
||||||
}
|
|
||||||
cmdArgs.add(platformsArg);
|
|
||||||
}
|
|
||||||
cmdArgs.add('--no-pub');
|
|
||||||
if (legacyNameParameter) {
|
|
||||||
cmdArgs.add(name);
|
|
||||||
} else {
|
|
||||||
cmdArgs.add(outputDirectory);
|
|
||||||
}
|
|
||||||
final RunResult result = await _processUtils.run(cmdArgs, workingDirectory: outputDirectory, allowReentrantFlutter: true);
|
|
||||||
final String error = result.stderr;
|
|
||||||
|
|
||||||
// Catch errors due to parameters not existing.
|
|
||||||
|
|
||||||
// Old versions of the tool does not include the platforms option.
|
|
||||||
if (error.contains('Could not find an option named "platforms".')) {
|
|
||||||
return createFromTemplates(
|
|
||||||
flutterBinPath,
|
|
||||||
name: name,
|
|
||||||
legacyNameParameter: legacyNameParameter,
|
|
||||||
androidLanguage: androidLanguage,
|
|
||||||
iosLanguage: iosLanguage,
|
|
||||||
outputDirectory: outputDirectory,
|
|
||||||
iterationsAllowed: iterationsAllowed--,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Old versions of the tool does not include the project-name option.
|
|
||||||
if ((result.stderr).contains('Could not find an option named "project-name".')) {
|
|
||||||
return createFromTemplates(
|
|
||||||
flutterBinPath,
|
|
||||||
name: name,
|
|
||||||
legacyNameParameter: true,
|
|
||||||
androidLanguage: androidLanguage,
|
|
||||||
iosLanguage: iosLanguage,
|
|
||||||
outputDirectory: outputDirectory,
|
|
||||||
platforms: platforms,
|
|
||||||
iterationsAllowed: iterationsAllowed--,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (error.contains('Multiple output directories specified.')) {
|
|
||||||
if (error.contains('Try moving --platforms')) {
|
|
||||||
return createFromTemplates(
|
|
||||||
flutterBinPath,
|
|
||||||
name: name,
|
|
||||||
legacyNameParameter: legacyNameParameter,
|
|
||||||
androidLanguage: androidLanguage,
|
|
||||||
iosLanguage: iosLanguage,
|
|
||||||
outputDirectory: outputDirectory,
|
|
||||||
iterationsAllowed: iterationsAllowed--,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
checkForErrors(result, commandDescription: cmdArgs.join(' '), silent: true);
|
|
||||||
|
|
||||||
if (legacyNameParameter) {
|
|
||||||
return _fileSystem.path.join(outputDirectory, name);
|
|
||||||
}
|
|
||||||
return outputDirectory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Runs the git 3-way merge on three files and returns the results as a MergeResult.
|
|
||||||
///
|
|
||||||
/// Passing the same path for base and current will perform a two-way fast forward merge.
|
|
||||||
Future<MergeResult> gitMergeFile({
|
|
||||||
required String base,
|
|
||||||
required String current,
|
|
||||||
required String target,
|
|
||||||
required String localPath,
|
|
||||||
}) async {
|
|
||||||
final List<String> cmdArgs = <String>['git', 'merge-file', '-p', current, base, target];
|
|
||||||
final RunResult result = await _processUtils.run(cmdArgs);
|
|
||||||
checkForErrors(result, allowedExitCodes: <int>[-1], commandDescription: cmdArgs.join(' '));
|
|
||||||
return StringMergeResult(result, localPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calls `git init` on the workingDirectory.
|
|
||||||
Future<void> gitInit(String workingDirectory) async {
|
|
||||||
final List<String> cmdArgs = <String>['git', 'init'];
|
|
||||||
final RunResult result = await _processUtils.run(cmdArgs, workingDirectory: workingDirectory);
|
|
||||||
checkForErrors(result, commandDescription: cmdArgs.join(' '));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the workingDirectory git repo has any uncommited changes.
|
|
||||||
Future<bool> hasUncommittedChanges(String workingDirectory, {String? migrateStagingDir}) async {
|
|
||||||
final List<String> cmdArgs = <String>[
|
|
||||||
'git',
|
|
||||||
'ls-files',
|
|
||||||
'--deleted',
|
|
||||||
'--modified',
|
|
||||||
'--others',
|
|
||||||
'--exclude=${migrateStagingDir ?? kDefaultMigrateStagingDirectoryName}'
|
|
||||||
];
|
|
||||||
final RunResult result = await _processUtils.run(cmdArgs, workingDirectory: workingDirectory);
|
|
||||||
checkForErrors(result, allowedExitCodes: <int>[-1], commandDescription: cmdArgs.join(' '));
|
|
||||||
if (result.stdout.isEmpty) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the workingDirectory is a git repo.
|
|
||||||
Future<bool> isGitRepo(String workingDirectory) async {
|
|
||||||
final List<String> cmdArgs = <String>['git', 'rev-parse', '--is-inside-work-tree'];
|
|
||||||
final RunResult result = await _processUtils.run(cmdArgs, workingDirectory: workingDirectory);
|
|
||||||
checkForErrors(result, allowedExitCodes: <int>[-1], commandDescription: cmdArgs.join(' '));
|
|
||||||
if (result.exitCode == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the file at `filePath` is covered by the `.gitignore`
|
|
||||||
Future<bool> isGitIgnored(String filePath, String workingDirectory) async {
|
|
||||||
final List<String> cmdArgs = <String>['git', 'check-ignore', filePath];
|
|
||||||
final RunResult result = await _processUtils.run(cmdArgs, workingDirectory: workingDirectory);
|
|
||||||
checkForErrors(result, allowedExitCodes: <int>[0, 1, 128], commandDescription: cmdArgs.join(' '));
|
|
||||||
return result.exitCode == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Runs `flutter pub upgrade --major-revisions`.
|
|
||||||
Future<void> flutterPubUpgrade(String workingDirectory) async {
|
|
||||||
final List<String> cmdArgs = <String>['flutter', 'pub', 'upgrade', '--major-versions'];
|
|
||||||
final RunResult result = await _processUtils.run(cmdArgs, workingDirectory: workingDirectory, allowReentrantFlutter: true);
|
|
||||||
checkForErrors(result, commandDescription: cmdArgs.join(' '));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Runs `./gradlew tasks` in the android directory of a flutter project.
|
|
||||||
Future<void> gradlewTasks(String workingDirectory) async {
|
|
||||||
final String baseCommand = _platform.isWindows ? 'gradlew.bat' : './gradlew';
|
|
||||||
final List<String> cmdArgs = <String>[baseCommand, 'tasks'];
|
|
||||||
final RunResult result = await _processUtils.run(cmdArgs, workingDirectory: workingDirectory);
|
|
||||||
checkForErrors(result, commandDescription: cmdArgs.join(' '));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verifies that the RunResult does not contain an error.
|
|
||||||
///
|
|
||||||
/// If an error is detected, the error can be optionally logged or exit the tool.
|
|
||||||
///
|
|
||||||
/// Passing -1 in allowedExitCodes means all exit codes are valid.
|
|
||||||
bool checkForErrors(
|
|
||||||
RunResult result, {
|
|
||||||
List<int> allowedExitCodes = const <int>[0],
|
|
||||||
String? commandDescription,
|
|
||||||
bool exit = true,
|
|
||||||
bool silent = false
|
|
||||||
}) {
|
|
||||||
if (allowedExitCodes.contains(result.exitCode) || allowedExitCodes.contains(-1)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!silent) {
|
|
||||||
_logger.printError('Command encountered an error with exit code ${result.exitCode}.');
|
|
||||||
if (commandDescription != null) {
|
|
||||||
_logger.printError('Command:');
|
|
||||||
_logger.printError(commandDescription, indent: 2);
|
|
||||||
}
|
|
||||||
_logger.printError('Stdout:');
|
|
||||||
_logger.printError(result.stdout, indent: 2);
|
|
||||||
_logger.printError('Stderr:');
|
|
||||||
_logger.printError(result.stderr, indent: 2);
|
|
||||||
}
|
|
||||||
if (exit) {
|
|
||||||
throwToolExit('Command failed with exit code ${result.exitCode}', exitCode: result.exitCode);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the file does not contain any git conflit markers.
|
|
||||||
bool conflictsResolved(String contents) {
|
|
||||||
final bool hasMarker = contents.contains('>>>>>>>') ||
|
|
||||||
contents.contains('=======') ||
|
|
||||||
contents.contains('<<<<<<<');
|
|
||||||
return !hasMarker;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defines the classification of difference between files.
|
|
||||||
enum DiffType {
|
|
||||||
command,
|
|
||||||
addition,
|
|
||||||
deletion,
|
|
||||||
ignored,
|
|
||||||
none,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tracks the output of a git diff command or any special cases such as addition of a new
|
|
||||||
/// file or deletion of an existing file.
|
|
||||||
class DiffResult {
|
|
||||||
DiffResult({
|
|
||||||
required this.diffType,
|
|
||||||
this.diff,
|
|
||||||
this.exitCode,
|
|
||||||
}) : assert(diffType == DiffType.command && exitCode != null || diffType != DiffType.command && exitCode == null);
|
|
||||||
|
|
||||||
/// The diff string output by git.
|
|
||||||
final String? diff;
|
|
||||||
|
|
||||||
final DiffType diffType;
|
|
||||||
|
|
||||||
/// The exit code of the command. This is zero when no diffs are found.
|
|
||||||
///
|
|
||||||
/// The exitCode is null when the diffType is not `command`.
|
|
||||||
final int? exitCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Data class to hold the results of a merge.
|
|
||||||
abstract class MergeResult {
|
|
||||||
/// Initializes a MergeResult based off of a RunResult.
|
|
||||||
MergeResult(RunResult result, this.localPath) :
|
|
||||||
hasConflict = result.exitCode != 0,
|
|
||||||
exitCode = result.exitCode;
|
|
||||||
|
|
||||||
/// Manually initializes a MergeResult with explicit values.
|
|
||||||
MergeResult.explicit({
|
|
||||||
required this.hasConflict,
|
|
||||||
required this.exitCode,
|
|
||||||
required this.localPath,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// True when there is a merge conflict.
|
|
||||||
bool hasConflict;
|
|
||||||
|
|
||||||
/// The exitcode of the merge command.
|
|
||||||
int exitCode;
|
|
||||||
|
|
||||||
/// The local path relative to the project root of the file.
|
|
||||||
String localPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The results of a string merge.
|
|
||||||
class StringMergeResult extends MergeResult {
|
|
||||||
/// Initializes a BinaryMergeResult based off of a RunResult.
|
|
||||||
StringMergeResult(super.result, super.localPath) :
|
|
||||||
mergedString = result.stdout;
|
|
||||||
|
|
||||||
/// Manually initializes a StringMergeResult with explicit values.
|
|
||||||
StringMergeResult.explicit({
|
|
||||||
required this.mergedString,
|
|
||||||
required super.hasConflict,
|
|
||||||
required super.exitCode,
|
|
||||||
required super.localPath,
|
|
||||||
}) : super.explicit();
|
|
||||||
/// The final merged string.
|
|
||||||
String mergedString;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The results of a binary merge.
|
|
||||||
class BinaryMergeResult extends MergeResult {
|
|
||||||
/// Initializes a BinaryMergeResult based off of a RunResult.
|
|
||||||
BinaryMergeResult(super.result, super.localPath) :
|
|
||||||
mergedBytes = result.stdout as Uint8List;
|
|
||||||
|
|
||||||
/// Manually initializes a BinaryMergeResult with explicit values.
|
|
||||||
BinaryMergeResult.explicit({
|
|
||||||
required this.mergedBytes,
|
|
||||||
required super.hasConflict,
|
|
||||||
required super.exitCode,
|
|
||||||
required super.localPath,
|
|
||||||
}) : super.explicit();
|
|
||||||
/// The final merged bytes.
|
|
||||||
Uint8List mergedBytes;
|
|
||||||
}
|
|
@ -1,146 +0,0 @@
|
|||||||
// 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: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/base/terminal.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() {
|
|
||||||
late FileSystem fileSystem;
|
|
||||||
late BufferLogger logger;
|
|
||||||
late Platform platform;
|
|
||||||
late Terminal terminal;
|
|
||||||
late ProcessManager processManager;
|
|
||||||
late Directory appDir;
|
|
||||||
|
|
||||||
setUp(() {
|
|
||||||
fileSystem = globals.localFileSystem;
|
|
||||||
appDir = fileSystem.systemTempDirectory.createTempSync('apptestdir');
|
|
||||||
logger = BufferLogger.test();
|
|
||||||
platform = FakePlatform();
|
|
||||||
terminal = Terminal.test();
|
|
||||||
processManager = globals.processManager;
|
|
||||||
});
|
|
||||||
|
|
||||||
setUpAll(() {
|
|
||||||
Cache.disableLocking();
|
|
||||||
});
|
|
||||||
|
|
||||||
tearDown(() async {
|
|
||||||
tryToDelete(appDir);
|
|
||||||
});
|
|
||||||
|
|
||||||
testUsingContext('abandon deletes staging directory', () async {
|
|
||||||
final MigrateCommand command = MigrateCommand(
|
|
||||||
verbose: true,
|
|
||||||
logger: logger,
|
|
||||||
fileSystem: fileSystem,
|
|
||||||
terminal: terminal,
|
|
||||||
platform: platform,
|
|
||||||
processManager: processManager,
|
|
||||||
);
|
|
||||||
final Directory stagingDir = appDir.childDirectory(kDefaultMigrateStagingDirectoryName);
|
|
||||||
appDir.childFile('lib/main.dart').createSync(recursive: true);
|
|
||||||
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);
|
|
||||||
|
|
||||||
expect(stagingDir.existsSync(), false);
|
|
||||||
await createTestCommandRunner(command).run(
|
|
||||||
<String>[
|
|
||||||
'migrate',
|
|
||||||
'abandon',
|
|
||||||
'--staging-directory=${stagingDir.path}',
|
|
||||||
'--project-directory=${appDir.path}',
|
|
||||||
]
|
|
||||||
);
|
|
||||||
expect(logger.errorText, contains('Provided staging directory'));
|
|
||||||
expect(logger.errorText, contains('migrate_staging_dir` does not exist or is not valid.'));
|
|
||||||
|
|
||||||
logger.clear();
|
|
||||||
await createTestCommandRunner(command).run(
|
|
||||||
<String>[
|
|
||||||
'migrate',
|
|
||||||
'abandon',
|
|
||||||
'--project-directory=${appDir.path}',
|
|
||||||
]
|
|
||||||
);
|
|
||||||
expect(logger.statusText, contains('No migration in progress. Start a new migration with:'));
|
|
||||||
|
|
||||||
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:
|
|
||||||
''');
|
|
||||||
|
|
||||||
expect(appDir.childFile('lib/main.dart').existsSync(), true);
|
|
||||||
|
|
||||||
expect(stagingDir.existsSync(), true);
|
|
||||||
logger.clear();
|
|
||||||
await createTestCommandRunner(command).run(
|
|
||||||
<String>[
|
|
||||||
'migrate',
|
|
||||||
'abandon',
|
|
||||||
'--staging-directory=${stagingDir.path}',
|
|
||||||
'--project-directory=${appDir.path}',
|
|
||||||
'--force',
|
|
||||||
]
|
|
||||||
);
|
|
||||||
expect(logger.statusText, contains('Abandon complete. Start a new migration with:'));
|
|
||||||
expect(stagingDir.existsSync(), false);
|
|
||||||
}, overrides: <Type, Generator>{
|
|
||||||
FileSystem: () => fileSystem,
|
|
||||||
ProcessManager: () => processManager,
|
|
||||||
Platform: () => platform,
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,229 +0,0 @@
|
|||||||
// 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: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/base/terminal.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() {
|
|
||||||
late FileSystem fileSystem;
|
|
||||||
late BufferLogger logger;
|
|
||||||
late Platform platform;
|
|
||||||
late Terminal terminal;
|
|
||||||
late ProcessManager processManager;
|
|
||||||
late Directory appDir;
|
|
||||||
|
|
||||||
setUp(() {
|
|
||||||
fileSystem = globals.localFileSystem;
|
|
||||||
appDir = fileSystem.systemTempDirectory.createTempSync('apptestdir');
|
|
||||||
logger = BufferLogger.test();
|
|
||||||
platform = FakePlatform();
|
|
||||||
terminal = Terminal.test();
|
|
||||||
processManager = globals.processManager;
|
|
||||||
});
|
|
||||||
|
|
||||||
setUpAll(() {
|
|
||||||
Cache.disableLocking();
|
|
||||||
});
|
|
||||||
|
|
||||||
tearDown(() async {
|
|
||||||
tryToDelete(appDir);
|
|
||||||
});
|
|
||||||
|
|
||||||
testUsingContext('Apply produces all outputs', () async {
|
|
||||||
final MigrateCommand command = MigrateCommand(
|
|
||||||
verbose: true,
|
|
||||||
logger: logger,
|
|
||||||
fileSystem: fileSystem,
|
|
||||||
terminal: terminal,
|
|
||||||
platform: platform,
|
|
||||||
processManager: processManager,
|
|
||||||
);
|
|
||||||
final Directory workingDir = appDir.childDirectory(kDefaultMigrateStagingDirectoryName);
|
|
||||||
appDir.childFile('lib/main.dart').createSync(recursive: true);
|
|
||||||
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 gitignore = appDir.childFile('.gitignore');
|
|
||||||
gitignore.createSync();
|
|
||||||
gitignore.writeAsStringSync(kDefaultMigrateStagingDirectoryName, flush: true);
|
|
||||||
|
|
||||||
logger.clear();
|
|
||||||
await createTestCommandRunner(command).run(
|
|
||||||
<String>[
|
|
||||||
'migrate',
|
|
||||||
'apply',
|
|
||||||
'--staging-directory=${workingDir.path}',
|
|
||||||
'--project-directory=${appDir.path}',
|
|
||||||
]
|
|
||||||
);
|
|
||||||
expect(logger.statusText, contains('Project is not a git repo. Please initialize a git repo and try again.'));
|
|
||||||
|
|
||||||
await processManager.run(<String>['git', 'init'], workingDirectory: appDir.path);
|
|
||||||
|
|
||||||
logger.clear();
|
|
||||||
await createTestCommandRunner(command).run(
|
|
||||||
<String>[
|
|
||||||
'migrate',
|
|
||||||
'apply',
|
|
||||||
'--staging-directory=${workingDir.path}',
|
|
||||||
'--project-directory=${appDir.path}',
|
|
||||||
]
|
|
||||||
);
|
|
||||||
expect(logger.statusText, contains('No migration in progress'));
|
|
||||||
|
|
||||||
final File pubspecModified = workingDir.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 = workingDir.childFile('added.file');
|
|
||||||
addedFile.createSync(recursive: true);
|
|
||||||
addedFile.writeAsStringSync('new file contents');
|
|
||||||
|
|
||||||
final File manifestFile = workingDir.childFile('.migrate_manifest');
|
|
||||||
manifestFile.createSync(recursive: true);
|
|
||||||
manifestFile.writeAsStringSync('''
|
|
||||||
merged_files:
|
|
||||||
- pubspec.yaml
|
|
||||||
conflict_files:
|
|
||||||
- conflict/conflict.file
|
|
||||||
added_files:
|
|
||||||
- added.file
|
|
||||||
deleted_files:
|
|
||||||
''');
|
|
||||||
|
|
||||||
// Add conflict file
|
|
||||||
final File conflictFile = workingDir.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);
|
|
||||||
|
|
||||||
logger.clear();
|
|
||||||
await createTestCommandRunner(command).run(
|
|
||||||
<String>[
|
|
||||||
'migrate',
|
|
||||||
'apply',
|
|
||||||
'--staging-directory=${workingDir.path}',
|
|
||||||
'--project-directory=${appDir.path}',
|
|
||||||
]
|
|
||||||
);
|
|
||||||
expect(logger.statusText, contains(r'''
|
|
||||||
Added files:
|
|
||||||
- added.file
|
|
||||||
Modified files:
|
|
||||||
- pubspec.yaml
|
|
||||||
Unable to apply migration. The following files in the migration working directory still have unresolved conflicts:
|
|
||||||
- conflict/conflict.file
|
|
||||||
Conflicting files found. Resolve these conflicts and try again.
|
|
||||||
Guided conflict resolution wizard:
|
|
||||||
|
|
||||||
$ flutter migrate resolve-conflicts'''));
|
|
||||||
|
|
||||||
conflictFile.writeAsStringSync('''
|
|
||||||
line1
|
|
||||||
linetwo
|
|
||||||
line3
|
|
||||||
''', flush: true);
|
|
||||||
|
|
||||||
logger.clear();
|
|
||||||
await createTestCommandRunner(command).run(
|
|
||||||
<String>[
|
|
||||||
'migrate',
|
|
||||||
'apply',
|
|
||||||
'--staging-directory=${workingDir.path}',
|
|
||||||
'--project-directory=${appDir.path}',
|
|
||||||
]
|
|
||||||
);
|
|
||||||
expect(logger.statusText, contains('There are uncommitted changes in your project. Please git commit, abandon, or stash your changes before trying again.'));
|
|
||||||
|
|
||||||
await processManager.run(<String>['git', 'add', '.'], workingDirectory: appDir.path);
|
|
||||||
await processManager.run(<String>['git', 'commit', '-m', 'Initial commit'], workingDirectory: appDir.path);
|
|
||||||
|
|
||||||
logger.clear();
|
|
||||||
await createTestCommandRunner(command).run(
|
|
||||||
<String>[
|
|
||||||
'migrate',
|
|
||||||
'apply',
|
|
||||||
'--staging-directory=${workingDir.path}',
|
|
||||||
'--project-directory=${appDir.path}',
|
|
||||||
]
|
|
||||||
);
|
|
||||||
expect(logger.statusText, contains(r'''
|
|
||||||
Added files:
|
|
||||||
- added.file
|
|
||||||
Modified files:
|
|
||||||
- conflict/conflict.file
|
|
||||||
- pubspec.yaml
|
|
||||||
|
|
||||||
Applying migration.
|
|
||||||
Modifying 3 files.
|
|
||||||
Writing pubspec.yaml
|
|
||||||
Writing conflict/conflict.file
|
|
||||||
Writing added.file
|
|
||||||
Updating .migrate_configs
|
|
||||||
Migration complete. You may use commands like `git status`, `git diff` and `git restore <file>` to continue working with the migrated files.'''));
|
|
||||||
|
|
||||||
expect(pubspecOriginal.readAsStringSync(), contains('# EXTRALINE'));
|
|
||||||
expect(conflictFileOriginal.readAsStringSync(), contains('linetwo'));
|
|
||||||
expect(appDir.childFile('added.file').existsSync(), true);
|
|
||||||
expect(appDir.childFile('added.file').readAsStringSync(), contains('new file contents'));
|
|
||||||
|
|
||||||
}, overrides: <Type, Generator>{
|
|
||||||
FileSystem: () => fileSystem,
|
|
||||||
ProcessManager: () => processManager,
|
|
||||||
Platform: () => platform,
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,198 +0,0 @@
|
|||||||
// 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: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/base/terminal.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() {
|
|
||||||
late FileSystem fileSystem;
|
|
||||||
late BufferLogger logger;
|
|
||||||
late Platform platform;
|
|
||||||
late Terminal terminal;
|
|
||||||
late ProcessManager processManager;
|
|
||||||
late Directory appDir;
|
|
||||||
|
|
||||||
setUp(() {
|
|
||||||
fileSystem = globals.localFileSystem;
|
|
||||||
appDir = fileSystem.systemTempDirectory.createTempSync('apptestdir');
|
|
||||||
logger = BufferLogger.test();
|
|
||||||
platform = FakePlatform();
|
|
||||||
terminal = Terminal.test();
|
|
||||||
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,
|
|
||||||
terminal: terminal,
|
|
||||||
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,
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,400 +0,0 @@
|
|||||||
// 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:file/memory.dart';
|
|
||||||
import 'package:flutter_tools/src/base/file_system.dart';
|
|
||||||
import 'package:flutter_tools/src/base/logger.dart';
|
|
||||||
import 'package:flutter_tools/src/migrate/migrate_manifest.dart';
|
|
||||||
import 'package:flutter_tools/src/migrate/migrate_result.dart';
|
|
||||||
import 'package:flutter_tools/src/migrate/migrate_utils.dart';
|
|
||||||
|
|
||||||
import '../../src/common.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
late FileSystem fileSystem;
|
|
||||||
late File manifestFile;
|
|
||||||
late BufferLogger logger;
|
|
||||||
|
|
||||||
setUpAll(() {
|
|
||||||
fileSystem = MemoryFileSystem.test();
|
|
||||||
logger = BufferLogger.test();
|
|
||||||
manifestFile = fileSystem.file('.migrate_manifest');
|
|
||||||
});
|
|
||||||
|
|
||||||
group('checkAndPrintMigrateStatus', () {
|
|
||||||
testWithoutContext('empty MigrateResult produces empty output', () async {
|
|
||||||
final Directory workingDir = fileSystem.directory('migrate_working_dir');
|
|
||||||
workingDir.createSync(recursive: true);
|
|
||||||
final MigrateManifest manifest = MigrateManifest(migrateRootDir: workingDir, migrateResult: MigrateResult(
|
|
||||||
mergeResults: <MergeResult>[],
|
|
||||||
addedFiles: <FilePendingMigration>[],
|
|
||||||
deletedFiles: <FilePendingMigration>[],
|
|
||||||
mergeTypeMap: <String, MergeType>{},
|
|
||||||
diffMap: <String, DiffResult>{},
|
|
||||||
tempDirectories: <Directory>[],
|
|
||||||
sdkDirs: <String, Directory>{},
|
|
||||||
));
|
|
||||||
|
|
||||||
checkAndPrintMigrateStatus(manifest, workingDir, warnConflict: true, logger: logger);
|
|
||||||
|
|
||||||
expect(logger.statusText, contains('\n'));
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('populated MigrateResult produces correct output', () async {
|
|
||||||
final Directory workingDir = fileSystem.directory('migrate_working_dir');
|
|
||||||
workingDir.createSync(recursive: true);
|
|
||||||
final MigrateManifest manifest = MigrateManifest(migrateRootDir: workingDir, migrateResult: MigrateResult(
|
|
||||||
mergeResults: <MergeResult>[
|
|
||||||
StringMergeResult.explicit(
|
|
||||||
localPath: 'merged_file',
|
|
||||||
mergedString: 'str',
|
|
||||||
hasConflict: false,
|
|
||||||
exitCode: 0,
|
|
||||||
),
|
|
||||||
StringMergeResult.explicit(
|
|
||||||
localPath: 'conflict_file',
|
|
||||||
mergedString: 'hello\nwow a bunch of lines\n<<<<<<<\n=======\n<<<<<<<\nhi\n',
|
|
||||||
hasConflict: true,
|
|
||||||
exitCode: 1,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
addedFiles: <FilePendingMigration>[FilePendingMigration('added_file', fileSystem.file('added_file'))],
|
|
||||||
deletedFiles: <FilePendingMigration>[FilePendingMigration('deleted_file', fileSystem.file('deleted_file'))],
|
|
||||||
// The following are ignored by the manifest.
|
|
||||||
mergeTypeMap: <String, MergeType>{'test': MergeType.threeWay},
|
|
||||||
diffMap: <String, DiffResult>{},
|
|
||||||
tempDirectories: <Directory>[],
|
|
||||||
sdkDirs: <String, Directory>{},
|
|
||||||
));
|
|
||||||
|
|
||||||
final File conflictFile = workingDir.childFile('conflict_file');
|
|
||||||
conflictFile.writeAsStringSync('hello\nwow a bunch of lines\n<<<<<<<\n=======\n<<<<<<<\nhi\n', flush: true);
|
|
||||||
|
|
||||||
checkAndPrintMigrateStatus(manifest, workingDir, warnConflict: true, logger: logger);
|
|
||||||
|
|
||||||
expect(logger.statusText, contains('''
|
|
||||||
Added files:
|
|
||||||
- added_file
|
|
||||||
Deleted files:
|
|
||||||
- deleted_file
|
|
||||||
Modified files:
|
|
||||||
- conflict_file
|
|
||||||
- merged_file
|
|
||||||
'''));
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('populated MigrateResult detects fixed conflict', () async {
|
|
||||||
final Directory workingDir = fileSystem.directory('migrate_working_dir');
|
|
||||||
workingDir.createSync(recursive: true);
|
|
||||||
final MigrateManifest manifest = MigrateManifest(migrateRootDir: workingDir, migrateResult: MigrateResult(
|
|
||||||
mergeResults: <MergeResult>[
|
|
||||||
StringMergeResult.explicit(
|
|
||||||
localPath: 'merged_file',
|
|
||||||
mergedString: 'str',
|
|
||||||
hasConflict: false,
|
|
||||||
exitCode: 0,
|
|
||||||
),
|
|
||||||
StringMergeResult.explicit(
|
|
||||||
localPath: 'conflict_file',
|
|
||||||
mergedString: 'hello\nwow a bunch of lines\n<<<<<<<\n=======\n<<<<<<<\nhi\n',
|
|
||||||
hasConflict: true,
|
|
||||||
exitCode: 1,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
addedFiles: <FilePendingMigration>[FilePendingMigration('added_file', fileSystem.file('added_file'))],
|
|
||||||
deletedFiles: <FilePendingMigration>[FilePendingMigration('deleted_file', fileSystem.file('deleted_file'))],
|
|
||||||
// The following are ignored by the manifest.
|
|
||||||
mergeTypeMap: <String, MergeType>{'test': MergeType.threeWay},
|
|
||||||
diffMap: <String, DiffResult>{},
|
|
||||||
tempDirectories: <Directory>[],
|
|
||||||
sdkDirs: <String, Directory>{},
|
|
||||||
));
|
|
||||||
|
|
||||||
final File conflictFile = workingDir.childFile('conflict_file');
|
|
||||||
conflictFile.writeAsStringSync('hello\nwow a bunch of lines\nhi\n', flush: true);
|
|
||||||
|
|
||||||
checkAndPrintMigrateStatus(manifest, workingDir, warnConflict: true, logger: logger);
|
|
||||||
expect(logger.statusText, contains('''
|
|
||||||
Added files:
|
|
||||||
- added_file
|
|
||||||
Deleted files:
|
|
||||||
- deleted_file
|
|
||||||
Modified files:
|
|
||||||
- conflict_file
|
|
||||||
- merged_file
|
|
||||||
'''));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group('manifest file parsing', () {
|
|
||||||
testWithoutContext('empty fails', () async {
|
|
||||||
manifestFile.writeAsStringSync('');
|
|
||||||
bool exceptionFound = false;
|
|
||||||
try {
|
|
||||||
MigrateManifest.fromFile(manifestFile);
|
|
||||||
} on Exception catch (e) {
|
|
||||||
exceptionFound = true;
|
|
||||||
expect(e.toString(), 'Exception: Invalid .migrate_manifest file in the migrate working directory. File is not a Yaml map.');
|
|
||||||
}
|
|
||||||
expect(exceptionFound, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('invalid name fails', () async {
|
|
||||||
manifestFile.writeAsStringSync('''
|
|
||||||
merged_files:
|
|
||||||
conflict_files:
|
|
||||||
added_filessssss:
|
|
||||||
deleted_files:
|
|
||||||
''');
|
|
||||||
bool exceptionFound = false;
|
|
||||||
try {
|
|
||||||
MigrateManifest.fromFile(manifestFile);
|
|
||||||
} on Exception catch (e) {
|
|
||||||
exceptionFound = true;
|
|
||||||
expect(e.toString(), 'Exception: Invalid .migrate_manifest file in the migrate working directory. File is missing an entry.');
|
|
||||||
}
|
|
||||||
expect(exceptionFound, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('missing name fails', () async {
|
|
||||||
manifestFile.writeAsStringSync('''
|
|
||||||
merged_files:
|
|
||||||
conflict_files:
|
|
||||||
deleted_files:
|
|
||||||
''');
|
|
||||||
bool exceptionFound = false;
|
|
||||||
try {
|
|
||||||
MigrateManifest.fromFile(manifestFile);
|
|
||||||
} on Exception catch (e) {
|
|
||||||
exceptionFound = true;
|
|
||||||
expect(e.toString(), 'Exception: Invalid .migrate_manifest file in the migrate working directory. File is missing an entry.');
|
|
||||||
}
|
|
||||||
expect(exceptionFound, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('wrong entry type fails', () async {
|
|
||||||
manifestFile.writeAsStringSync('''
|
|
||||||
merged_files:
|
|
||||||
conflict_files:
|
|
||||||
other_key:
|
|
||||||
added_files:
|
|
||||||
deleted_files:
|
|
||||||
''');
|
|
||||||
bool exceptionFound = false;
|
|
||||||
try {
|
|
||||||
MigrateManifest.fromFile(manifestFile);
|
|
||||||
} on Exception catch (e) {
|
|
||||||
exceptionFound = true;
|
|
||||||
expect(e.toString(), 'Exception: Invalid .migrate_manifest file in the migrate working directory. Entry is not a Yaml list.');
|
|
||||||
}
|
|
||||||
expect(exceptionFound, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('unpopulated succeeds', () async {
|
|
||||||
manifestFile.writeAsStringSync('''
|
|
||||||
merged_files:
|
|
||||||
conflict_files:
|
|
||||||
added_files:
|
|
||||||
deleted_files:
|
|
||||||
''');
|
|
||||||
final MigrateManifest manifest = MigrateManifest.fromFile(manifestFile);
|
|
||||||
expect(manifest.mergedFiles.isEmpty, true);
|
|
||||||
expect(manifest.conflictFiles.isEmpty, true);
|
|
||||||
expect(manifest.addedFiles.isEmpty, true);
|
|
||||||
expect(manifest.deletedFiles.isEmpty, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('order does not matter', () async {
|
|
||||||
manifestFile.writeAsStringSync('''
|
|
||||||
added_files:
|
|
||||||
merged_files:
|
|
||||||
deleted_files:
|
|
||||||
conflict_files:
|
|
||||||
''');
|
|
||||||
final MigrateManifest manifest = MigrateManifest.fromFile(manifestFile);
|
|
||||||
expect(manifest.mergedFiles.isEmpty, true);
|
|
||||||
expect(manifest.conflictFiles.isEmpty, true);
|
|
||||||
expect(manifest.addedFiles.isEmpty, true);
|
|
||||||
expect(manifest.deletedFiles.isEmpty, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('basic succeeds', () async {
|
|
||||||
manifestFile.writeAsStringSync('''
|
|
||||||
merged_files:
|
|
||||||
- file1
|
|
||||||
conflict_files:
|
|
||||||
- file2
|
|
||||||
added_files:
|
|
||||||
- file3
|
|
||||||
deleted_files:
|
|
||||||
- file4
|
|
||||||
''');
|
|
||||||
final MigrateManifest manifest = MigrateManifest.fromFile(manifestFile);
|
|
||||||
expect(manifest.mergedFiles.isEmpty, false);
|
|
||||||
expect(manifest.conflictFiles.isEmpty, false);
|
|
||||||
expect(manifest.addedFiles.isEmpty, false);
|
|
||||||
expect(manifest.deletedFiles.isEmpty, false);
|
|
||||||
|
|
||||||
expect(manifest.mergedFiles.length, 1);
|
|
||||||
expect(manifest.conflictFiles.length, 1);
|
|
||||||
expect(manifest.addedFiles.length, 1);
|
|
||||||
expect(manifest.deletedFiles.length, 1);
|
|
||||||
|
|
||||||
expect(manifest.mergedFiles[0], 'file1');
|
|
||||||
expect(manifest.conflictFiles[0], 'file2');
|
|
||||||
expect(manifest.addedFiles[0], 'file3');
|
|
||||||
expect(manifest.deletedFiles[0], 'file4');
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('basic multi-list succeeds', () async {
|
|
||||||
manifestFile.writeAsStringSync('''
|
|
||||||
merged_files:
|
|
||||||
- file1
|
|
||||||
- file2
|
|
||||||
conflict_files:
|
|
||||||
added_files:
|
|
||||||
deleted_files:
|
|
||||||
- file3
|
|
||||||
- file4
|
|
||||||
''');
|
|
||||||
final MigrateManifest manifest = MigrateManifest.fromFile(manifestFile);
|
|
||||||
expect(manifest.mergedFiles.isEmpty, false);
|
|
||||||
expect(manifest.conflictFiles.isEmpty, true);
|
|
||||||
expect(manifest.addedFiles.isEmpty, true);
|
|
||||||
expect(manifest.deletedFiles.isEmpty, false);
|
|
||||||
|
|
||||||
expect(manifest.mergedFiles.length, 2);
|
|
||||||
expect(manifest.conflictFiles.length, 0);
|
|
||||||
expect(manifest.addedFiles.length, 0);
|
|
||||||
expect(manifest.deletedFiles.length, 2);
|
|
||||||
|
|
||||||
expect(manifest.mergedFiles[0], 'file1');
|
|
||||||
expect(manifest.mergedFiles[1], 'file2');
|
|
||||||
expect(manifest.deletedFiles[0], 'file3');
|
|
||||||
expect(manifest.deletedFiles[1], 'file4');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group('manifest MigrateResult creation', () {
|
|
||||||
testWithoutContext('empty MigrateResult', () async {
|
|
||||||
final MigrateManifest manifest = MigrateManifest(migrateRootDir: fileSystem.directory('root'), migrateResult: MigrateResult(
|
|
||||||
mergeResults: <MergeResult>[],
|
|
||||||
addedFiles: <FilePendingMigration>[],
|
|
||||||
deletedFiles: <FilePendingMigration>[],
|
|
||||||
mergeTypeMap: <String, MergeType>{},
|
|
||||||
diffMap: <String, DiffResult>{},
|
|
||||||
tempDirectories: <Directory>[],
|
|
||||||
sdkDirs: <String, Directory>{},
|
|
||||||
));
|
|
||||||
expect(manifest.mergedFiles.isEmpty, true);
|
|
||||||
expect(manifest.conflictFiles.isEmpty, true);
|
|
||||||
expect(manifest.addedFiles.isEmpty, true);
|
|
||||||
expect(manifest.deletedFiles.isEmpty, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('simple MigrateResult', () async {
|
|
||||||
final MigrateManifest manifest = MigrateManifest(migrateRootDir: fileSystem.directory('root'), migrateResult: MigrateResult(
|
|
||||||
mergeResults: <MergeResult>[
|
|
||||||
StringMergeResult.explicit(
|
|
||||||
localPath: 'merged_file',
|
|
||||||
mergedString: 'str',
|
|
||||||
hasConflict: false,
|
|
||||||
exitCode: 0,
|
|
||||||
),
|
|
||||||
StringMergeResult.explicit(
|
|
||||||
localPath: 'conflict_file',
|
|
||||||
mergedString: '<<<<<<<<<<<',
|
|
||||||
hasConflict: true,
|
|
||||||
exitCode: 1,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
addedFiles: <FilePendingMigration>[FilePendingMigration('added_file', fileSystem.file('added_file'))],
|
|
||||||
deletedFiles: <FilePendingMigration>[FilePendingMigration('deleted_file', fileSystem.file('deleted_file'))],
|
|
||||||
// The following are ignored by the manifest.
|
|
||||||
mergeTypeMap: <String, MergeType>{'test': MergeType.threeWay},
|
|
||||||
diffMap: <String, DiffResult>{},
|
|
||||||
tempDirectories: <Directory>[],
|
|
||||||
sdkDirs: <String, Directory>{},
|
|
||||||
));
|
|
||||||
expect(manifest.mergedFiles.isEmpty, false);
|
|
||||||
expect(manifest.conflictFiles.isEmpty, false);
|
|
||||||
expect(manifest.addedFiles.isEmpty, false);
|
|
||||||
expect(manifest.deletedFiles.isEmpty, false);
|
|
||||||
|
|
||||||
expect(manifest.mergedFiles.length, 1);
|
|
||||||
expect(manifest.conflictFiles.length, 1);
|
|
||||||
expect(manifest.addedFiles.length, 1);
|
|
||||||
expect(manifest.deletedFiles.length, 1);
|
|
||||||
|
|
||||||
expect(manifest.mergedFiles[0], 'merged_file');
|
|
||||||
expect(manifest.conflictFiles[0], 'conflict_file');
|
|
||||||
expect(manifest.addedFiles[0], 'added_file');
|
|
||||||
expect(manifest.deletedFiles[0], 'deleted_file');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group('manifest write', () {
|
|
||||||
testWithoutContext('empty', () async {
|
|
||||||
manifestFile.writeAsStringSync('''
|
|
||||||
merged_files:
|
|
||||||
conflict_files:
|
|
||||||
added_files:
|
|
||||||
deleted_files:
|
|
||||||
''');
|
|
||||||
final MigrateManifest manifest = MigrateManifest.fromFile(manifestFile);
|
|
||||||
expect(manifest.mergedFiles.isEmpty, true);
|
|
||||||
expect(manifest.conflictFiles.isEmpty, true);
|
|
||||||
expect(manifest.addedFiles.isEmpty, true);
|
|
||||||
expect(manifest.deletedFiles.isEmpty, true);
|
|
||||||
|
|
||||||
manifest.writeFile();
|
|
||||||
expect(manifestFile.readAsStringSync(), '''
|
|
||||||
merged_files:
|
|
||||||
conflict_files:
|
|
||||||
added_files:
|
|
||||||
deleted_files:
|
|
||||||
''');
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('basic multi-list', () async {
|
|
||||||
manifestFile.writeAsStringSync('''
|
|
||||||
merged_files:
|
|
||||||
- file1
|
|
||||||
- file2
|
|
||||||
conflict_files:
|
|
||||||
added_files:
|
|
||||||
deleted_files:
|
|
||||||
- file3
|
|
||||||
- file4
|
|
||||||
''');
|
|
||||||
final MigrateManifest manifest = MigrateManifest.fromFile(manifestFile);
|
|
||||||
expect(manifest.mergedFiles.isEmpty, false);
|
|
||||||
expect(manifest.conflictFiles.isEmpty, true);
|
|
||||||
expect(manifest.addedFiles.isEmpty, true);
|
|
||||||
expect(manifest.deletedFiles.isEmpty, false);
|
|
||||||
|
|
||||||
expect(manifest.mergedFiles.length, 2);
|
|
||||||
expect(manifest.conflictFiles.length, 0);
|
|
||||||
expect(manifest.addedFiles.length, 0);
|
|
||||||
expect(manifest.deletedFiles.length, 2);
|
|
||||||
|
|
||||||
expect(manifest.mergedFiles[0], 'file1');
|
|
||||||
expect(manifest.mergedFiles[1], 'file2');
|
|
||||||
expect(manifest.deletedFiles[0], 'file3');
|
|
||||||
expect(manifest.deletedFiles[1], 'file4');
|
|
||||||
|
|
||||||
manifest.writeFile();
|
|
||||||
expect(manifestFile.readAsStringSync(), '''
|
|
||||||
merged_files:
|
|
||||||
- file1
|
|
||||||
- file2
|
|
||||||
conflict_files:
|
|
||||||
added_files:
|
|
||||||
deleted_files:
|
|
||||||
- file3
|
|
||||||
- file4
|
|
||||||
''');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,94 +0,0 @@
|
|||||||
// 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:file/file.dart';
|
|
||||||
import 'package:flutter_tools/src/base/file_system.dart';
|
|
||||||
import 'package:flutter_tools/src/base/logger.dart';
|
|
||||||
import 'package:flutter_tools/src/base/process.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';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
late BufferLogger logger;
|
|
||||||
late FileSystem fileSystem;
|
|
||||||
late Directory projectRoot;
|
|
||||||
late String projectRootPath;
|
|
||||||
late ProcessUtils processUtils;
|
|
||||||
late MigrateUtils utils;
|
|
||||||
|
|
||||||
setUpAll(() async {
|
|
||||||
fileSystem = globals.localFileSystem;
|
|
||||||
logger = BufferLogger.test();
|
|
||||||
utils = MigrateUtils(
|
|
||||||
logger: logger,
|
|
||||||
fileSystem: fileSystem,
|
|
||||||
platform: globals.platform,
|
|
||||||
processManager: globals.processManager,
|
|
||||||
);
|
|
||||||
processUtils = ProcessUtils(processManager: globals.processManager, logger: logger);
|
|
||||||
});
|
|
||||||
|
|
||||||
group('git', () {
|
|
||||||
setUp(() async {
|
|
||||||
projectRoot = fileSystem.systemTempDirectory.createTempSync('flutter_migrate_command_test');
|
|
||||||
projectRoot.createSync(recursive: true);
|
|
||||||
projectRootPath = projectRoot.path;
|
|
||||||
});
|
|
||||||
|
|
||||||
tearDown(() async {
|
|
||||||
tryToDelete(projectRoot);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('isGitRepo', () async {
|
|
||||||
expect(projectRoot.existsSync(), true);
|
|
||||||
expect(projectRoot.childDirectory('.git').existsSync(), false);
|
|
||||||
|
|
||||||
expect(await gitRepoExists(projectRootPath, logger, utils), false);
|
|
||||||
expect(logger.statusText, contains('Project is not a git repo. Please initialize a git repo and try again.'));
|
|
||||||
|
|
||||||
await utils.gitInit(projectRootPath);
|
|
||||||
expect(projectRoot.childDirectory('.git').existsSync(), true);
|
|
||||||
|
|
||||||
expect(await gitRepoExists(projectRootPath, logger, utils), true);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('printCommandText produces formatted output', () async {
|
|
||||||
printCommandText('some command --help', logger);
|
|
||||||
|
|
||||||
expect(logger.statusText, contains(r' $ some command --help'));
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('hasUncommittedChanges false on clean repo', () async {
|
|
||||||
expect(projectRoot.existsSync(), true);
|
|
||||||
expect(projectRoot.childDirectory('.git').existsSync(), false);
|
|
||||||
await utils.gitInit(projectRootPath);
|
|
||||||
expect(projectRoot.childDirectory('.git').existsSync(), true);
|
|
||||||
|
|
||||||
projectRoot.childFile('.gitignore')
|
|
||||||
..createSync()
|
|
||||||
..writeAsStringSync('ignored_file.dart', flush: true);
|
|
||||||
|
|
||||||
await processUtils.run(<String>['git', 'add', '.'], workingDirectory: projectRootPath);
|
|
||||||
await processUtils.run(<String>['git', 'commit', '-m', 'Initial commit'], workingDirectory: projectRootPath);
|
|
||||||
|
|
||||||
expect(await hasUncommittedChanges(projectRootPath, logger, utils), false);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('hasUncommittedChanges true on dirty repo', () async {
|
|
||||||
expect(projectRoot.existsSync(), true);
|
|
||||||
expect(projectRoot.childDirectory('.git').existsSync(), false);
|
|
||||||
await utils.gitInit(projectRootPath);
|
|
||||||
expect(projectRoot.childDirectory('.git').existsSync(), true);
|
|
||||||
|
|
||||||
projectRoot.childFile('some_file.dart')
|
|
||||||
..createSync()
|
|
||||||
..writeAsStringSync('void main() {}', flush: true);
|
|
||||||
|
|
||||||
expect(await hasUncommittedChanges(projectRootPath, logger, utils), true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,238 +0,0 @@
|
|||||||
// 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:file/file.dart';
|
|
||||||
import 'package:flutter_tools/src/base/logger.dart';
|
|
||||||
import 'package:flutter_tools/src/flutter_project_metadata.dart';
|
|
||||||
import 'package:flutter_tools/src/project.dart';
|
|
||||||
|
|
||||||
import '../src/common.dart';
|
|
||||||
import '../src/context.dart';
|
|
||||||
import 'test_data/migrate_project.dart';
|
|
||||||
import 'test_driver.dart';
|
|
||||||
import 'test_utils.dart';
|
|
||||||
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
late Directory tempDir;
|
|
||||||
late FlutterRunTestDriver flutter;
|
|
||||||
late Logger logger;
|
|
||||||
|
|
||||||
setUp(() async {
|
|
||||||
tempDir = createResolvedTempDirectorySync('run_test.');
|
|
||||||
flutter = FlutterRunTestDriver(tempDir);
|
|
||||||
logger = BufferLogger.test();
|
|
||||||
});
|
|
||||||
|
|
||||||
tearDown(() async {
|
|
||||||
await flutter.stop();
|
|
||||||
tryToDelete(tempDir);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('parse simple config file', () async {
|
|
||||||
final File metadataFile = tempDir.childFile('.metadata');
|
|
||||||
metadataFile.createSync(recursive: true);
|
|
||||||
metadataFile.writeAsStringSync('''
|
|
||||||
# This file tracks properties of this Flutter project.
|
|
||||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
|
||||||
#
|
|
||||||
# This file should be version controlled.
|
|
||||||
|
|
||||||
version:
|
|
||||||
revision: fj19vkla9vnlka9vni3n808v3nch8cd
|
|
||||||
channel: stable
|
|
||||||
|
|
||||||
project_type: app
|
|
||||||
|
|
||||||
# Tracks metadata for the flutter migrate command
|
|
||||||
migration:
|
|
||||||
platforms:
|
|
||||||
- platform: root
|
|
||||||
create_revision: fj19vkla9vnlka9vni3n808v3nch8cd
|
|
||||||
base_revision: 93kf9v3njfa90vnidfjvn39nvi3vnie
|
|
||||||
- platform: android
|
|
||||||
create_revision: abfj19vkla9vnlka9vni3n808v3nch8cd
|
|
||||||
base_revision: ab93kf9v3njfa90vnidfjvn39nvi3vnie
|
|
||||||
|
|
||||||
# User provided section
|
|
||||||
|
|
||||||
# List of Local paths (relative to this file) that should be
|
|
||||||
# ignored by the migrate tool.
|
|
||||||
#
|
|
||||||
# Files that are not part of the templates will be ignored by default.
|
|
||||||
unmanaged_files:
|
|
||||||
- lib/main.dart
|
|
||||||
- ios/Runner.xcodeproj/project.pbxproj
|
|
||||||
- lib/file1/etc.dart
|
|
||||||
- android/my_file.java
|
|
||||||
''', flush: true);
|
|
||||||
FlutterProjectMetadata metadata = FlutterProjectMetadata(metadataFile, logger);
|
|
||||||
|
|
||||||
expect(metadata.migrateConfig.platformConfigs[SupportedPlatform.root]!.createRevision, equals('fj19vkla9vnlka9vni3n808v3nch8cd'));
|
|
||||||
expect(metadata.migrateConfig.platformConfigs[SupportedPlatform.root]!.baseRevision, equals('93kf9v3njfa90vnidfjvn39nvi3vnie'));
|
|
||||||
|
|
||||||
expect(metadata.migrateConfig.platformConfigs[SupportedPlatform.android]!.createRevision, equals('abfj19vkla9vnlka9vni3n808v3nch8cd'));
|
|
||||||
expect(metadata.migrateConfig.platformConfigs[SupportedPlatform.android]!.baseRevision, equals('ab93kf9v3njfa90vnidfjvn39nvi3vnie'));
|
|
||||||
|
|
||||||
expect(metadata.migrateConfig.unmanagedFiles[0], equals('lib/main.dart'));
|
|
||||||
expect(metadata.migrateConfig.unmanagedFiles[1], equals('ios/Runner.xcodeproj/project.pbxproj'));
|
|
||||||
expect(metadata.migrateConfig.unmanagedFiles[2], equals('lib/file1/etc.dart'));
|
|
||||||
expect(metadata.migrateConfig.unmanagedFiles[3], equals('android/my_file.java'));
|
|
||||||
|
|
||||||
metadataFile.writeAsStringSync('''
|
|
||||||
# This file tracks properties of this Flutter project.
|
|
||||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
|
||||||
#
|
|
||||||
# This file should be version controlled.
|
|
||||||
|
|
||||||
version:
|
|
||||||
revision: fj19vkla9vnlka9vni3n808v3nch8cd
|
|
||||||
channel: stable
|
|
||||||
|
|
||||||
project_type: app
|
|
||||||
''', flush: true);
|
|
||||||
|
|
||||||
metadata = FlutterProjectMetadata(metadataFile, logger);
|
|
||||||
|
|
||||||
expect(metadata.migrateConfig.isEmpty, equals(true));
|
|
||||||
expect(metadata.versionRevision, equals('fj19vkla9vnlka9vni3n808v3nch8cd'));
|
|
||||||
expect(metadata.versionChannel, equals('stable'));
|
|
||||||
});
|
|
||||||
|
|
||||||
testUsingContext('write simple config file', () async {
|
|
||||||
const String testCreateRevision = 'testmc9skl32nlnf23lnakcs9njr3';
|
|
||||||
const String testBaseRevision = 'testanas9anlnq9ba7bjhavan3kma';
|
|
||||||
MigrateConfig config = MigrateConfig(
|
|
||||||
platformConfigs: <SupportedPlatform, MigratePlatformConfig>{
|
|
||||||
SupportedPlatform.android: MigratePlatformConfig(platform: SupportedPlatform.android, createRevision: testCreateRevision, baseRevision: testBaseRevision),
|
|
||||||
SupportedPlatform.ios: MigratePlatformConfig(platform: SupportedPlatform.ios, createRevision: testCreateRevision, baseRevision: testBaseRevision),
|
|
||||||
SupportedPlatform.root: MigratePlatformConfig(platform: SupportedPlatform.root, createRevision: testCreateRevision, baseRevision: testBaseRevision),
|
|
||||||
SupportedPlatform.windows: MigratePlatformConfig(platform: SupportedPlatform.windows, createRevision: testCreateRevision, baseRevision: testBaseRevision),
|
|
||||||
},
|
|
||||||
unmanagedFiles: <String>[
|
|
||||||
'lib/main.dart',
|
|
||||||
'ios/Runner.xcodeproj/project.pbxproj',
|
|
||||||
'lib/file1/etc.dart',
|
|
||||||
],
|
|
||||||
);
|
|
||||||
String outputString = config.getOutputFileString();
|
|
||||||
expect(outputString, equals('''
|
|
||||||
|
|
||||||
# Tracks metadata for the flutter migrate command
|
|
||||||
migration:
|
|
||||||
platforms:
|
|
||||||
- platform: android
|
|
||||||
create_revision: $testCreateRevision
|
|
||||||
base_revision: $testBaseRevision
|
|
||||||
- platform: ios
|
|
||||||
create_revision: $testCreateRevision
|
|
||||||
base_revision: $testBaseRevision
|
|
||||||
- platform: root
|
|
||||||
create_revision: $testCreateRevision
|
|
||||||
base_revision: $testBaseRevision
|
|
||||||
- platform: windows
|
|
||||||
create_revision: $testCreateRevision
|
|
||||||
base_revision: $testBaseRevision
|
|
||||||
|
|
||||||
# User provided section
|
|
||||||
|
|
||||||
# List of Local paths (relative to this file) that should be
|
|
||||||
# ignored by the migrate tool.
|
|
||||||
#
|
|
||||||
# Files that are not part of the templates will be ignored by default.
|
|
||||||
unmanaged_files:
|
|
||||||
- 'lib/main.dart'
|
|
||||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
|
||||||
- 'lib/file1/etc.dart'
|
|
||||||
'''));
|
|
||||||
|
|
||||||
config = MigrateConfig();
|
|
||||||
outputString = config.getOutputFileString();
|
|
||||||
expect(outputString, equals(''));
|
|
||||||
});
|
|
||||||
|
|
||||||
testUsingContext('populate migrate config', () async {
|
|
||||||
// Flutter Stable 1.22.6 hash: 9b2d32b605630f28625709ebd9d78ab3016b2bf6
|
|
||||||
final MigrateProject project = MigrateProject('version:1.22.6_stable');
|
|
||||||
await project.setUpIn(tempDir);
|
|
||||||
|
|
||||||
final File metadataFile = tempDir.childFile('.metadata');
|
|
||||||
|
|
||||||
const String currentRevision = 'test_base_revision';
|
|
||||||
const String createRevision = 'test_create_revision';
|
|
||||||
|
|
||||||
final FlutterProjectMetadata metadata = FlutterProjectMetadata(metadataFile, logger);
|
|
||||||
metadata.migrateConfig.populate(
|
|
||||||
projectDirectory: tempDir,
|
|
||||||
currentRevision: currentRevision,
|
|
||||||
createRevision: createRevision,
|
|
||||||
logger: logger,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(metadata.migrateConfig.platformConfigs.length, equals(3));
|
|
||||||
|
|
||||||
final List<SupportedPlatform> keyList = List<SupportedPlatform>.from(metadata.migrateConfig.platformConfigs.keys);
|
|
||||||
|
|
||||||
expect(keyList[0], equals(SupportedPlatform.root));
|
|
||||||
expect(metadata.migrateConfig.platformConfigs[SupportedPlatform.root]!.baseRevision, equals(currentRevision));
|
|
||||||
expect(metadata.migrateConfig.platformConfigs[SupportedPlatform.root]!.createRevision, equals(createRevision));
|
|
||||||
|
|
||||||
expect(keyList[1], equals(SupportedPlatform.android));
|
|
||||||
expect(metadata.migrateConfig.platformConfigs[SupportedPlatform.android]!.baseRevision, equals(currentRevision));
|
|
||||||
expect(metadata.migrateConfig.platformConfigs[SupportedPlatform.android]!.createRevision, equals(createRevision));
|
|
||||||
|
|
||||||
expect(keyList[2], equals(SupportedPlatform.ios));
|
|
||||||
expect(metadata.migrateConfig.platformConfigs[SupportedPlatform.ios]!.baseRevision, equals(currentRevision));
|
|
||||||
expect(metadata.migrateConfig.platformConfigs[SupportedPlatform.ios]!.createRevision, equals(createRevision));
|
|
||||||
|
|
||||||
final File metadataFileOutput = tempDir.childFile('.metadata_output');
|
|
||||||
metadata.writeFile(outputFile: metadataFileOutput);
|
|
||||||
expect(metadataFileOutput.readAsStringSync(), equals('''
|
|
||||||
# This file tracks properties of this Flutter project.
|
|
||||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
|
||||||
#
|
|
||||||
# This file should be version controlled.
|
|
||||||
|
|
||||||
version:
|
|
||||||
revision: 9b2d32b605630f28625709ebd9d78ab3016b2bf6
|
|
||||||
channel: unknown
|
|
||||||
|
|
||||||
project_type: app
|
|
||||||
|
|
||||||
# Tracks metadata for the flutter migrate command
|
|
||||||
migration:
|
|
||||||
platforms:
|
|
||||||
- platform: root
|
|
||||||
create_revision: $createRevision
|
|
||||||
base_revision: $currentRevision
|
|
||||||
- platform: android
|
|
||||||
create_revision: $createRevision
|
|
||||||
base_revision: $currentRevision
|
|
||||||
- platform: ios
|
|
||||||
create_revision: $createRevision
|
|
||||||
base_revision: $currentRevision
|
|
||||||
|
|
||||||
# User provided section
|
|
||||||
|
|
||||||
# List of Local paths (relative to this file) that should be
|
|
||||||
# ignored by the migrate tool.
|
|
||||||
#
|
|
||||||
# Files that are not part of the templates will be ignored by default.
|
|
||||||
unmanaged_files:
|
|
||||||
- 'lib/main.dart'
|
|
||||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
|
||||||
'''));
|
|
||||||
});
|
|
||||||
|
|
||||||
testUsingContext('equality compares platform', () async {
|
|
||||||
const String testCreateRevision = 'testmc9skl32nlnf23lnakcs9njr3';
|
|
||||||
const String testBaseRevision = 'testanas9anlnq9ba7bjhavan3kma';
|
|
||||||
final MigratePlatformConfig configAndroid = MigratePlatformConfig(platform: SupportedPlatform.android, createRevision: testCreateRevision, baseRevision: testBaseRevision);
|
|
||||||
final MigratePlatformConfig configIos = MigratePlatformConfig(platform: SupportedPlatform.ios, createRevision: testCreateRevision, baseRevision: testBaseRevision);
|
|
||||||
|
|
||||||
expect(configAndroid.equals(configIos), false);
|
|
||||||
expect(configAndroid.equals(configAndroid), true);
|
|
||||||
expect(configIos.equals(configIos), true);
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,236 +0,0 @@
|
|||||||
// 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:file/file.dart';
|
|
||||||
import 'package:flutter_tools/src/base/file_system.dart';
|
|
||||||
import 'package:flutter_tools/src/base/logger.dart';
|
|
||||||
import 'package:flutter_tools/src/base/process.dart';
|
|
||||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
|
||||||
import 'package:flutter_tools/src/migrate/migrate_utils.dart';
|
|
||||||
|
|
||||||
import '../src/common.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
late BufferLogger logger;
|
|
||||||
late FileSystem fileSystem;
|
|
||||||
late Directory projectRoot;
|
|
||||||
late String projectRootPath;
|
|
||||||
late MigrateUtils utils;
|
|
||||||
late ProcessUtils processUtils;
|
|
||||||
|
|
||||||
setUpAll(() async {
|
|
||||||
fileSystem = globals.localFileSystem;
|
|
||||||
logger = BufferLogger.test();
|
|
||||||
utils = MigrateUtils(
|
|
||||||
logger: logger,
|
|
||||||
fileSystem: fileSystem,
|
|
||||||
platform: globals.platform,
|
|
||||||
processManager: globals.processManager,
|
|
||||||
);
|
|
||||||
processUtils = ProcessUtils(processManager: globals.processManager, logger: logger);
|
|
||||||
});
|
|
||||||
|
|
||||||
group('git', () {
|
|
||||||
setUp(() async {
|
|
||||||
projectRoot = fileSystem.systemTempDirectory.createTempSync('flutter_migrate_utils_test');
|
|
||||||
projectRoot.createSync(recursive: true);
|
|
||||||
projectRootPath = projectRoot.path;
|
|
||||||
});
|
|
||||||
|
|
||||||
tearDown(() async {
|
|
||||||
tryToDelete(projectRoot);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('init', () async {
|
|
||||||
expect(projectRoot.existsSync(), true);
|
|
||||||
expect(projectRoot.childDirectory('.git').existsSync(), false);
|
|
||||||
await utils.gitInit(projectRootPath);
|
|
||||||
expect(projectRoot.childDirectory('.git').existsSync(), true);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('isGitIgnored', () async {
|
|
||||||
expect(projectRoot.existsSync(), true);
|
|
||||||
expect(projectRoot.childDirectory('.git').existsSync(), false);
|
|
||||||
await utils.gitInit(projectRootPath);
|
|
||||||
expect(projectRoot.childDirectory('.git').existsSync(), true);
|
|
||||||
|
|
||||||
projectRoot.childFile('.gitignore')
|
|
||||||
..createSync()
|
|
||||||
..writeAsStringSync('ignored_file.dart', flush: true);
|
|
||||||
|
|
||||||
expect(await utils.isGitIgnored('ignored_file.dart', projectRootPath), true);
|
|
||||||
expect(await utils.isGitIgnored('other_file.dart', projectRootPath), false);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('isGitRepo', () async {
|
|
||||||
expect(projectRoot.existsSync(), true);
|
|
||||||
expect(projectRoot.childDirectory('.git').existsSync(), false);
|
|
||||||
expect(await utils.isGitRepo(projectRootPath), false);
|
|
||||||
await utils.gitInit(projectRootPath);
|
|
||||||
expect(projectRoot.childDirectory('.git').existsSync(), true);
|
|
||||||
|
|
||||||
expect(await utils.isGitRepo(projectRootPath), true);
|
|
||||||
|
|
||||||
expect(await utils.isGitRepo(projectRoot.parent.path), false);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('hasUncommittedChanges false on clean repo', () async {
|
|
||||||
expect(projectRoot.existsSync(), true);
|
|
||||||
expect(projectRoot.childDirectory('.git').existsSync(), false);
|
|
||||||
await utils.gitInit(projectRootPath);
|
|
||||||
expect(projectRoot.childDirectory('.git').existsSync(), true);
|
|
||||||
|
|
||||||
projectRoot.childFile('.gitignore')
|
|
||||||
..createSync()
|
|
||||||
..writeAsStringSync('ignored_file.dart', flush: true);
|
|
||||||
|
|
||||||
await processUtils.run(<String>['git', 'add', '.'], workingDirectory: projectRootPath);
|
|
||||||
await processUtils.run(<String>['git', 'commit', '-m', 'Initial commit'], workingDirectory: projectRootPath);
|
|
||||||
|
|
||||||
expect(await utils.hasUncommittedChanges(projectRootPath), false);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('hasUncommittedChanges true on dirty repo', () async {
|
|
||||||
expect(projectRoot.existsSync(), true);
|
|
||||||
expect(projectRoot.childDirectory('.git').existsSync(), false);
|
|
||||||
await utils.gitInit(projectRootPath);
|
|
||||||
expect(projectRoot.childDirectory('.git').existsSync(), true);
|
|
||||||
|
|
||||||
projectRoot.childFile('some_file.dart')
|
|
||||||
..createSync()
|
|
||||||
..writeAsStringSync('void main() {}', flush: true);
|
|
||||||
|
|
||||||
expect(await utils.hasUncommittedChanges(projectRootPath), true);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('diffFiles', () async {
|
|
||||||
expect(projectRoot.existsSync(), true);
|
|
||||||
expect(projectRoot.childDirectory('.git').existsSync(), false);
|
|
||||||
await utils.gitInit(projectRootPath);
|
|
||||||
expect(projectRoot.childDirectory('.git').existsSync(), true);
|
|
||||||
|
|
||||||
final File file1 = projectRoot.childFile('some_file.dart')
|
|
||||||
..createSync()
|
|
||||||
..writeAsStringSync('void main() {}\n', flush: true);
|
|
||||||
|
|
||||||
final File file2 = projectRoot.childFile('some_other_file.dart');
|
|
||||||
|
|
||||||
DiffResult result = await utils.diffFiles(file1, file2);
|
|
||||||
expect(result.diff, null);
|
|
||||||
expect(result.diffType, DiffType.deletion);
|
|
||||||
expect(result.exitCode, null);
|
|
||||||
|
|
||||||
result = await utils.diffFiles(file2, file1);
|
|
||||||
expect(result.diff, null);
|
|
||||||
expect(result.diffType, DiffType.addition);
|
|
||||||
expect(result.exitCode, null);
|
|
||||||
|
|
||||||
file2.createSync();
|
|
||||||
file2.writeAsStringSync('void main() {}\n', flush: true);
|
|
||||||
|
|
||||||
result = await utils.diffFiles(file1, file2);
|
|
||||||
expect(result.diff, '');
|
|
||||||
expect(result.diffType, DiffType.command);
|
|
||||||
expect(result.exitCode, 0);
|
|
||||||
|
|
||||||
file2.writeAsStringSync('void main() {}\na second line\na third line\n', flush: true);
|
|
||||||
|
|
||||||
result = await utils.diffFiles(file1, file2);
|
|
||||||
expect(result.diff, contains('@@ -1 +1,3 @@\n void main() {}\n+a second line\n+a third line'));
|
|
||||||
expect(result.diffType, DiffType.command);
|
|
||||||
expect(result.exitCode, 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('merge', () async {
|
|
||||||
expect(projectRoot.existsSync(), true);
|
|
||||||
expect(projectRoot.childDirectory('.git').existsSync(), false);
|
|
||||||
await utils.gitInit(projectRootPath);
|
|
||||||
expect(projectRoot.childDirectory('.git').existsSync(), true);
|
|
||||||
|
|
||||||
final File file1 = projectRoot.childFile('some_file.dart');
|
|
||||||
file1.createSync();
|
|
||||||
file1.writeAsStringSync('void main() {}\n\nline1\nline2\nline3\nline4\nline5\n', flush: true);
|
|
||||||
final File file2 = projectRoot.childFile('some_other_file.dart');
|
|
||||||
file2.createSync();
|
|
||||||
file2.writeAsStringSync('void main() {}\n\nline1\nline2\nline3.0\nline3.5\nline4\nline5\n', flush: true);
|
|
||||||
final File file3 = projectRoot.childFile('some_other_third_file.dart');
|
|
||||||
file3.createSync();
|
|
||||||
file3.writeAsStringSync('void main() {}\n\nline2\nline3\nline4\nline5\n', flush: true);
|
|
||||||
|
|
||||||
StringMergeResult result = await utils.gitMergeFile(
|
|
||||||
base: file1.path,
|
|
||||||
current: file2.path,
|
|
||||||
target: file3.path,
|
|
||||||
localPath: 'some_file.dart',
|
|
||||||
) as StringMergeResult;
|
|
||||||
|
|
||||||
expect(result.mergedString, 'void main() {}\n\nline2\nline3.0\nline3.5\nline4\nline5\n');
|
|
||||||
expect(result.hasConflict, false);
|
|
||||||
expect(result.exitCode, 0);
|
|
||||||
|
|
||||||
file3.writeAsStringSync('void main() {}\n\nline1\nline2\nline3.1\nline3.5\nline4\nline5\n', flush: true);
|
|
||||||
|
|
||||||
result = await utils.gitMergeFile(
|
|
||||||
base: file1.path,
|
|
||||||
current: file2.path,
|
|
||||||
target: file3.path,
|
|
||||||
localPath: 'some_file.dart',
|
|
||||||
) as StringMergeResult;
|
|
||||||
|
|
||||||
expect(result.mergedString, contains('line3.0\n=======\nline3.1\n>>>>>>>'));
|
|
||||||
expect(result.hasConflict, true);
|
|
||||||
expect(result.exitCode, 1);
|
|
||||||
|
|
||||||
// Two way merge
|
|
||||||
result = await utils.gitMergeFile(
|
|
||||||
base: file1.path,
|
|
||||||
current: file1.path,
|
|
||||||
target: file3.path,
|
|
||||||
localPath: 'some_file.dart',
|
|
||||||
) as StringMergeResult;
|
|
||||||
|
|
||||||
expect(result.mergedString, 'void main() {}\n\nline1\nline2\nline3.1\nline3.5\nline4\nline5\n');
|
|
||||||
expect(result.hasConflict, false);
|
|
||||||
expect(result.exitCode, 0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group('legacy app creation', () {
|
|
||||||
testWithoutContext('clone and create', () async {
|
|
||||||
projectRoot = fileSystem.systemTempDirectory.createTempSync('flutter_sdk_test');
|
|
||||||
const String revision = '5391447fae6209bb21a89e6a5a6583cac1af9b4b';
|
|
||||||
|
|
||||||
expect(await utils.cloneFlutter(revision, projectRoot.path), true);
|
|
||||||
expect(projectRoot.childFile('README.md').existsSync(), true);
|
|
||||||
|
|
||||||
final Directory appDir = fileSystem.systemTempDirectory.createTempSync('flutter_app');
|
|
||||||
await utils.createFromTemplates(
|
|
||||||
projectRoot.childDirectory('bin').path,
|
|
||||||
name: 'testapp',
|
|
||||||
androidLanguage: 'java',
|
|
||||||
iosLanguage: 'objc',
|
|
||||||
outputDirectory: appDir.path,
|
|
||||||
);
|
|
||||||
expect(appDir.childFile('pubspec.yaml').existsSync(), true);
|
|
||||||
expect(appDir.childFile('.metadata').existsSync(), true);
|
|
||||||
expect(appDir.childFile('.metadata').readAsStringSync(), contains(revision));
|
|
||||||
expect(appDir.childDirectory('android').existsSync(), true);
|
|
||||||
expect(appDir.childDirectory('ios').existsSync(), true);
|
|
||||||
expect(appDir.childDirectory('web').existsSync(), false);
|
|
||||||
|
|
||||||
projectRoot.deleteSync(recursive: true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('conflictsResolved', () async {
|
|
||||||
expect(utils.conflictsResolved(''), true);
|
|
||||||
expect(utils.conflictsResolved('hello'), true);
|
|
||||||
expect(utils.conflictsResolved('hello\n'), true);
|
|
||||||
expect(utils.conflictsResolved('hello\nwow a bunch of lines\n\nhi\n'), true);
|
|
||||||
expect(utils.conflictsResolved('hello\nwow a bunch of lines\n>>>>>>>\nhi\n'), false);
|
|
||||||
expect(utils.conflictsResolved('hello\nwow a bunch of lines\n=======\nhi\n'), false);
|
|
||||||
expect(utils.conflictsResolved('hello\nwow a bunch of lines\n<<<<<<<\nhi\n'), false);
|
|
||||||
expect(utils.conflictsResolved('hello\nwow a bunch of lines\n<<<<<<<\n=======\n<<<<<<<\nhi\n'), false);
|
|
||||||
});
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user