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