mirror of
https://github.com/flutter/packages.git
synced 2025-07-01 23:51:55 +08:00
[flutter_migrate] Start command and executables (#2735)
* [flutter_migrate] Start command * Sync with upstream changes * Integrate with compute and add E2E tests * E2E tests passing * Resolve hangs, add help messages * Use CommandRunner * Cleanup hooks to prevent hang * Formatting * Softer requirements on test matching to ignore order * Null safety, address minor comments * Address comments * Cleanup and logging improvements * format, normalize * Kick tests * Fix tests * Improve win test robustness * Windows perms copyall * Fix directory typo bug * takeown command in CI * Add logging for windows * windows command debug logging * Skip running on unsupported flutter versions * Add main.dart * Bots memory, dynamic flutter executable * Different temp dir * Takeown of main.dart * test adjustments * Analyzer * Increase resources to dart_unit_tests * Remove logging
This commit is contained in:
28
.cirrus.yml
28
.cirrus.yml
@ -128,19 +128,6 @@ task:
|
|||||||
- else
|
- else
|
||||||
- echo "Only run in presubmit"
|
- echo "Only run in presubmit"
|
||||||
- fi
|
- fi
|
||||||
- name: dart_unit_tests
|
|
||||||
env:
|
|
||||||
matrix:
|
|
||||||
CHANNEL: "master"
|
|
||||||
CHANNEL: "stable"
|
|
||||||
unit_test_script:
|
|
||||||
- ./script/tool_runner.sh test --exclude=script/configs/dart_unit_tests_exceptions.yaml
|
|
||||||
pathified_unit_test_script:
|
|
||||||
# Run tests with path-based dependencies to ensure that publishing
|
|
||||||
# the changes won't break tests of other packages in the repository
|
|
||||||
# that depend on it.
|
|
||||||
- ./script/tool_runner.sh make-deps-path-based --target-dependencies-with-non-breaking-updates
|
|
||||||
- $PLUGIN_TOOL_COMMAND test --run-on-dirty-packages --exclude=script/configs/dart_unit_tests_exceptions.yaml
|
|
||||||
- name: analyze
|
- name: analyze
|
||||||
env:
|
env:
|
||||||
matrix:
|
matrix:
|
||||||
@ -256,7 +243,7 @@ task:
|
|||||||
zone: us-central1-a
|
zone: us-central1-a
|
||||||
namespace: default
|
namespace: default
|
||||||
cpu: 4
|
cpu: 4
|
||||||
memory: 12G
|
memory: 16G
|
||||||
matrix:
|
matrix:
|
||||||
### Android tasks ###
|
### Android tasks ###
|
||||||
- name: android-platform_tests
|
- name: android-platform_tests
|
||||||
@ -320,6 +307,19 @@ task:
|
|||||||
- cd ../..
|
- cd ../..
|
||||||
- flutter packages get
|
- flutter packages get
|
||||||
- dart testing/web_benchmarks_test.dart
|
- dart testing/web_benchmarks_test.dart
|
||||||
|
- name: dart_unit_tests
|
||||||
|
env:
|
||||||
|
matrix:
|
||||||
|
CHANNEL: "master"
|
||||||
|
CHANNEL: "stable"
|
||||||
|
unit_test_script:
|
||||||
|
- ./script/tool_runner.sh test --exclude=script/configs/dart_unit_tests_exceptions.yaml
|
||||||
|
pathified_unit_test_script:
|
||||||
|
# Run tests with path-based dependencies to ensure that publishing
|
||||||
|
# the changes won't break tests of other packages in the repository
|
||||||
|
# that depend on it.
|
||||||
|
- ./script/tool_runner.sh make-deps-path-based --target-dependencies-with-non-breaking-updates
|
||||||
|
- $PLUGIN_TOOL_COMMAND test --run-on-dirty-packages --exclude=script/configs/dart_unit_tests_exceptions.yaml
|
||||||
|
|
||||||
# ARM macOS tasks.
|
# ARM macOS tasks.
|
||||||
task:
|
task:
|
||||||
|
@ -24,7 +24,11 @@ any migrations that are broken.
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
The core command sequence to use is `start`, `apply`.
|
To run the tool enter the root directory of your flutter project and run:
|
||||||
|
|
||||||
|
`dart run <path_to_flutter_migrate_package>/bin/flutter_migrate.dart <subcommand> [parameters]`
|
||||||
|
|
||||||
|
The core subcommand sequence to use is `start`, `apply`.
|
||||||
|
|
||||||
* `start` will generate a migration that will be staged in the `migration_staging_directory`
|
* `start` will generate a migration that will be staged in the `migration_staging_directory`
|
||||||
in your project home. This command may take some time to complete depending on network speed.
|
in your project home. This command may take some time to complete depending on network speed.
|
||||||
|
9
packages/flutter_migrate/bin/flutter_migrate.dart
Normal file
9
packages/flutter_migrate/bin/flutter_migrate.dart
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// Copyright 2013 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_migrate/executable.dart' as executable;
|
||||||
|
|
||||||
|
void main(List<String> args) {
|
||||||
|
executable.main(args);
|
||||||
|
}
|
95
packages/flutter_migrate/lib/executable.dart
Normal file
95
packages/flutter_migrate/lib/executable.dart
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
// Copyright 2013 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:io' as io;
|
||||||
|
|
||||||
|
import 'package:args/args.dart';
|
||||||
|
import 'package:args/command_runner.dart';
|
||||||
|
|
||||||
|
import 'src/base/command.dart';
|
||||||
|
import 'src/base/file_system.dart';
|
||||||
|
import 'src/base_dependencies.dart';
|
||||||
|
|
||||||
|
import 'src/commands/abandon.dart';
|
||||||
|
import 'src/commands/apply.dart';
|
||||||
|
import 'src/commands/start.dart';
|
||||||
|
import 'src/commands/status.dart';
|
||||||
|
|
||||||
|
Future<void> main(List<String> args) async {
|
||||||
|
final bool veryVerbose = args.contains('-vv');
|
||||||
|
final bool verbose =
|
||||||
|
args.contains('-v') || args.contains('--verbose') || veryVerbose;
|
||||||
|
|
||||||
|
final MigrateBaseDependencies baseDependencies = MigrateBaseDependencies();
|
||||||
|
|
||||||
|
final List<MigrateCommand> commands = <MigrateCommand>[
|
||||||
|
MigrateStartCommand(
|
||||||
|
verbose: verbose,
|
||||||
|
logger: baseDependencies.logger,
|
||||||
|
fileSystem: baseDependencies.fileSystem,
|
||||||
|
processManager: baseDependencies.processManager,
|
||||||
|
),
|
||||||
|
MigrateStatusCommand(
|
||||||
|
verbose: verbose,
|
||||||
|
logger: baseDependencies.logger,
|
||||||
|
fileSystem: baseDependencies.fileSystem,
|
||||||
|
processManager: baseDependencies.processManager,
|
||||||
|
),
|
||||||
|
MigrateAbandonCommand(
|
||||||
|
logger: baseDependencies.logger,
|
||||||
|
fileSystem: baseDependencies.fileSystem,
|
||||||
|
terminal: baseDependencies.terminal,
|
||||||
|
processManager: baseDependencies.processManager),
|
||||||
|
MigrateApplyCommand(
|
||||||
|
verbose: verbose,
|
||||||
|
logger: baseDependencies.logger,
|
||||||
|
fileSystem: baseDependencies.fileSystem,
|
||||||
|
terminal: baseDependencies.terminal,
|
||||||
|
processManager: baseDependencies.processManager),
|
||||||
|
];
|
||||||
|
|
||||||
|
final MigrateCommandRunner runner = MigrateCommandRunner();
|
||||||
|
|
||||||
|
commands.forEach(runner.addCommand);
|
||||||
|
await runner.run(args);
|
||||||
|
|
||||||
|
await _exit(0, baseDependencies,
|
||||||
|
shutdownHooks: baseDependencies.fileSystem.shutdownHooks);
|
||||||
|
await baseDependencies.fileSystem.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simple extension of a CommandRunner to provide migrate specific global flags.
|
||||||
|
class MigrateCommandRunner extends CommandRunner<void> {
|
||||||
|
MigrateCommandRunner()
|
||||||
|
: super(
|
||||||
|
'flutter',
|
||||||
|
'Migrates legacy flutter projects to modern versions.',
|
||||||
|
) {
|
||||||
|
argParser.addFlag('verbose',
|
||||||
|
abbr: 'v',
|
||||||
|
negatable: false,
|
||||||
|
help: 'Noisy logging, including all shell commands executed.');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
ArgParser get argParser => _argParser;
|
||||||
|
final ArgParser _argParser = ArgParser();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> _exit(int code, MigrateBaseDependencies baseDependencies,
|
||||||
|
{required ShutdownHooks shutdownHooks}) async {
|
||||||
|
// Run shutdown hooks before flushing logs
|
||||||
|
await shutdownHooks.runShutdownHooks(baseDependencies.logger);
|
||||||
|
|
||||||
|
final Completer<void> completer = Completer<void>();
|
||||||
|
|
||||||
|
// Give the task / timer queue one cycle through before we hard exit.
|
||||||
|
Timer.run(() {
|
||||||
|
io.exit(code);
|
||||||
|
});
|
||||||
|
|
||||||
|
await completer.future;
|
||||||
|
return code;
|
||||||
|
}
|
@ -4,6 +4,9 @@
|
|||||||
|
|
||||||
import 'package:args/command_runner.dart';
|
import 'package:args/command_runner.dart';
|
||||||
|
|
||||||
|
import 'logger.dart';
|
||||||
|
import 'project.dart';
|
||||||
|
|
||||||
enum ExitStatus {
|
enum ExitStatus {
|
||||||
success,
|
success,
|
||||||
warning,
|
warning,
|
||||||
@ -11,6 +14,9 @@ enum ExitStatus {
|
|||||||
killed,
|
killed,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const String flutterNoPubspecMessage = 'Error: No pubspec.yaml file found.\n'
|
||||||
|
'This command should be run from the root of your Flutter project.';
|
||||||
|
|
||||||
class CommandResult {
|
class CommandResult {
|
||||||
const CommandResult(this.exitStatus);
|
const CommandResult(this.exitStatus);
|
||||||
|
|
||||||
@ -50,6 +56,7 @@ abstract class MigrateCommand extends Command<void> {
|
|||||||
@override
|
@override
|
||||||
Future<void> run() async {
|
Future<void> run() async {
|
||||||
await runCommand();
|
await runCommand();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<CommandResult> runCommand();
|
Future<CommandResult> runCommand();
|
||||||
@ -71,4 +78,12 @@ abstract class MigrateCommand extends Command<void> {
|
|||||||
|
|
||||||
/// Gets the parsed command-line option named [name] as an `int`.
|
/// Gets the parsed command-line option named [name] as an `int`.
|
||||||
int? intArg(String name) => argResults?[name] as int?;
|
int? intArg(String name) => argResults?[name] as int?;
|
||||||
|
|
||||||
|
bool validateWorkingDirectory(FlutterProject project, Logger logger) {
|
||||||
|
if (!project.pubspecFile.existsSync()) {
|
||||||
|
logger.printError(flutterNoPubspecMessage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
87
packages/flutter_migrate/lib/src/base_dependencies.dart
Normal file
87
packages/flutter_migrate/lib/src/base_dependencies.dart
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
// Copyright 2013 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:io';
|
||||||
|
|
||||||
|
import 'package:process/process.dart';
|
||||||
|
|
||||||
|
import 'base/common.dart';
|
||||||
|
import 'base/file_system.dart';
|
||||||
|
import 'base/io.dart';
|
||||||
|
import 'base/logger.dart';
|
||||||
|
import 'base/signals.dart';
|
||||||
|
import 'base/terminal.dart';
|
||||||
|
|
||||||
|
/// Initializes the boilerplate dependencies needed by the migrate tool.
|
||||||
|
class MigrateBaseDependencies {
|
||||||
|
MigrateBaseDependencies() {
|
||||||
|
processManager = const LocalProcessManager();
|
||||||
|
fileSystem = LocalFileSystem(
|
||||||
|
LocalSignals.instance, Signals.defaultExitSignals, ShutdownHooks());
|
||||||
|
|
||||||
|
stdio = Stdio();
|
||||||
|
terminal = AnsiTerminal(stdio: stdio);
|
||||||
|
|
||||||
|
final LoggerFactory loggerFactory = LoggerFactory(
|
||||||
|
outputPreferences: OutputPreferences(
|
||||||
|
wrapText: stdio.hasTerminal,
|
||||||
|
showColor: stdout.supportsAnsiEscapes,
|
||||||
|
stdio: stdio,
|
||||||
|
),
|
||||||
|
terminal: terminal,
|
||||||
|
stdio: stdio,
|
||||||
|
);
|
||||||
|
logger = loggerFactory.createLogger(
|
||||||
|
windows: isWindows,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final ProcessManager processManager;
|
||||||
|
late final LocalFileSystem fileSystem;
|
||||||
|
late final Stdio stdio;
|
||||||
|
late final Terminal terminal;
|
||||||
|
late final Logger logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An abstraction for instantiation of the correct logger type.
|
||||||
|
///
|
||||||
|
/// Our logger class hierarchy and runtime requirements are overly complicated.
|
||||||
|
class LoggerFactory {
|
||||||
|
LoggerFactory({
|
||||||
|
required Terminal terminal,
|
||||||
|
required Stdio stdio,
|
||||||
|
required OutputPreferences outputPreferences,
|
||||||
|
StopwatchFactory stopwatchFactory = const StopwatchFactory(),
|
||||||
|
}) : _terminal = terminal,
|
||||||
|
_stdio = stdio,
|
||||||
|
_stopwatchFactory = stopwatchFactory,
|
||||||
|
_outputPreferences = outputPreferences;
|
||||||
|
|
||||||
|
final Terminal _terminal;
|
||||||
|
final Stdio _stdio;
|
||||||
|
final StopwatchFactory _stopwatchFactory;
|
||||||
|
final OutputPreferences _outputPreferences;
|
||||||
|
|
||||||
|
/// Create the appropriate logger for the current platform and configuration.
|
||||||
|
Logger createLogger({
|
||||||
|
required bool windows,
|
||||||
|
}) {
|
||||||
|
Logger logger;
|
||||||
|
if (windows) {
|
||||||
|
logger = WindowsStdoutLogger(
|
||||||
|
terminal: _terminal,
|
||||||
|
stdio: _stdio,
|
||||||
|
outputPreferences: _outputPreferences,
|
||||||
|
stopwatchFactory: _stopwatchFactory,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
logger = StdoutLogger(
|
||||||
|
terminal: _terminal,
|
||||||
|
stdio: _stdio,
|
||||||
|
outputPreferences: _outputPreferences,
|
||||||
|
stopwatchFactory: _stopwatchFactory);
|
||||||
|
}
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,7 @@ class MigrateAbandonCommand extends MigrateCommand {
|
|||||||
required this.fileSystem,
|
required this.fileSystem,
|
||||||
required this.terminal,
|
required this.terminal,
|
||||||
required ProcessManager processManager,
|
required ProcessManager processManager,
|
||||||
|
this.standalone = false,
|
||||||
}) : migrateUtils = MigrateUtils(
|
}) : migrateUtils = MigrateUtils(
|
||||||
logger: logger,
|
logger: logger,
|
||||||
fileSystem: fileSystem,
|
fileSystem: fileSystem,
|
||||||
@ -60,6 +61,8 @@ class MigrateAbandonCommand extends MigrateCommand {
|
|||||||
|
|
||||||
final MigrateUtils migrateUtils;
|
final MigrateUtils migrateUtils;
|
||||||
|
|
||||||
|
final bool standalone;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final String name = 'abandon';
|
final String name = 'abandon';
|
||||||
|
|
||||||
@ -75,7 +78,12 @@ class MigrateAbandonCommand extends MigrateCommand {
|
|||||||
? FlutterProject.current(fileSystem)
|
? FlutterProject.current(fileSystem)
|
||||||
: flutterProjectFactory
|
: flutterProjectFactory
|
||||||
.fromDirectory(fileSystem.directory(projectDirectory));
|
.fromDirectory(fileSystem.directory(projectDirectory));
|
||||||
final bool isSubcommand = boolArg('flutter-subcommand') ?? false;
|
final bool isSubcommand = boolArg('flutter-subcommand') ?? !standalone;
|
||||||
|
|
||||||
|
if (!validateWorkingDirectory(project, logger)) {
|
||||||
|
return const CommandResult(ExitStatus.fail);
|
||||||
|
}
|
||||||
|
|
||||||
Directory stagingDirectory =
|
Directory stagingDirectory =
|
||||||
project.directory.childDirectory(kDefaultMigrateStagingDirectoryName);
|
project.directory.childDirectory(kDefaultMigrateStagingDirectoryName);
|
||||||
final String? customStagingDirectoryPath = stringArg('staging-directory');
|
final String? customStagingDirectoryPath = stringArg('staging-directory');
|
||||||
|
@ -25,6 +25,7 @@ class MigrateApplyCommand extends MigrateCommand {
|
|||||||
required this.fileSystem,
|
required this.fileSystem,
|
||||||
required this.terminal,
|
required this.terminal,
|
||||||
required ProcessManager processManager,
|
required ProcessManager processManager,
|
||||||
|
this.standalone = false,
|
||||||
}) : _verbose = verbose,
|
}) : _verbose = verbose,
|
||||||
_processManager = processManager,
|
_processManager = processManager,
|
||||||
migrateUtils = MigrateUtils(
|
migrateUtils = MigrateUtils(
|
||||||
@ -76,6 +77,8 @@ class MigrateApplyCommand extends MigrateCommand {
|
|||||||
|
|
||||||
final MigrateUtils migrateUtils;
|
final MigrateUtils migrateUtils;
|
||||||
|
|
||||||
|
final bool standalone;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final String name = 'apply';
|
final String name = 'apply';
|
||||||
|
|
||||||
@ -98,12 +101,16 @@ class MigrateApplyCommand extends MigrateCommand {
|
|||||||
final FlutterToolsEnvironment environment =
|
final FlutterToolsEnvironment environment =
|
||||||
await FlutterToolsEnvironment.initializeFlutterToolsEnvironment(
|
await FlutterToolsEnvironment.initializeFlutterToolsEnvironment(
|
||||||
_processManager, logger);
|
_processManager, logger);
|
||||||
final bool isSubcommand = boolArg('flutter-subcommand') ?? false;
|
final bool isSubcommand = boolArg('flutter-subcommand') ?? !standalone;
|
||||||
|
|
||||||
|
if (!validateWorkingDirectory(project, logger)) {
|
||||||
|
return CommandResult.fail();
|
||||||
|
}
|
||||||
|
|
||||||
if (!await gitRepoExists(project.directory.path, logger, migrateUtils)) {
|
if (!await gitRepoExists(project.directory.path, logger, migrateUtils)) {
|
||||||
logger.printStatus('No git repo found. Please run in a project with an '
|
logger.printStatus('No git repo found. Please run in a project with an '
|
||||||
'initialized git repo or initialize one with:');
|
'initialized git repo or initialize one with:');
|
||||||
printCommandText('git init', logger, standalone: null);
|
printCommand('git init', logger);
|
||||||
return const CommandResult(ExitStatus.fail);
|
return const CommandResult(ExitStatus.fail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
345
packages/flutter_migrate/lib/src/commands/start.dart
Normal file
345
packages/flutter_migrate/lib/src/commands/start.dart
Normal file
@ -0,0 +1,345 @@
|
|||||||
|
// Copyright 2013 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/command.dart';
|
||||||
|
import '../base/file_system.dart';
|
||||||
|
import '../base/logger.dart';
|
||||||
|
import '../base/project.dart';
|
||||||
|
|
||||||
|
import '../compute.dart';
|
||||||
|
import '../environment.dart';
|
||||||
|
import '../manifest.dart';
|
||||||
|
import '../result.dart';
|
||||||
|
import '../utils.dart';
|
||||||
|
|
||||||
|
class MigrateStartCommand extends MigrateCommand {
|
||||||
|
MigrateStartCommand({
|
||||||
|
bool verbose = false,
|
||||||
|
required this.logger,
|
||||||
|
required this.fileSystem,
|
||||||
|
required this.processManager,
|
||||||
|
this.standalone = false,
|
||||||
|
}) : _verbose = verbose,
|
||||||
|
migrateUtils = MigrateUtils(
|
||||||
|
logger: logger,
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
processManager: processManager,
|
||||||
|
) {
|
||||||
|
argParser.addOption(
|
||||||
|
'staging-directory',
|
||||||
|
help:
|
||||||
|
'Specifies the custom migration staging directory used to stage and edit proposed changes. '
|
||||||
|
'This path can be absolute or relative to the flutter project root.',
|
||||||
|
valueHelp: 'path',
|
||||||
|
);
|
||||||
|
argParser.addOption(
|
||||||
|
'project-directory',
|
||||||
|
help: 'The root directory of the flutter project.',
|
||||||
|
valueHelp: 'path',
|
||||||
|
);
|
||||||
|
argParser.addOption(
|
||||||
|
'platforms',
|
||||||
|
help:
|
||||||
|
'Restrict the tool to only migrate the listed platforms. By default all platforms generated by '
|
||||||
|
'flutter create will be migrated. To indicate the project root, use the `root` platform',
|
||||||
|
valueHelp: 'root,android,ios,windows...',
|
||||||
|
);
|
||||||
|
argParser.addFlag(
|
||||||
|
'delete-temp-directories',
|
||||||
|
help:
|
||||||
|
'Indicates if the temporary directories created by the migrate tool will be deleted.',
|
||||||
|
);
|
||||||
|
argParser.addOption(
|
||||||
|
'base-app-directory',
|
||||||
|
help:
|
||||||
|
'The directory containing the base reference app. This is used as the common ancestor in a 3 way merge. '
|
||||||
|
'Providing this directory will prevent the tool from generating its own. This is primarily used '
|
||||||
|
'in testing and CI.',
|
||||||
|
valueHelp: 'path',
|
||||||
|
hide: !verbose,
|
||||||
|
);
|
||||||
|
argParser.addOption(
|
||||||
|
'target-app-directory',
|
||||||
|
help:
|
||||||
|
'The directory containing the target reference app. This is used as the target app in 3 way merge. '
|
||||||
|
'Providing this directory will prevent the tool from generating its own. This is primarily used '
|
||||||
|
'in testing and CI.',
|
||||||
|
valueHelp: 'path',
|
||||||
|
hide: !verbose,
|
||||||
|
);
|
||||||
|
argParser.addFlag(
|
||||||
|
'allow-fallback-base-revision',
|
||||||
|
help:
|
||||||
|
'If a base revision cannot be determined, this flag enables using flutter 1.0.0 as a fallback base revision. '
|
||||||
|
'Using this fallback will typically produce worse quality migrations and possibly more conflicts.',
|
||||||
|
);
|
||||||
|
argParser.addOption(
|
||||||
|
'base-revision',
|
||||||
|
help:
|
||||||
|
'Manually sets the base revision to generate the base ancestor reference app with. This may be used '
|
||||||
|
'if the tool is unable to determine an appropriate base revision.',
|
||||||
|
valueHelp: 'git revision hash',
|
||||||
|
);
|
||||||
|
argParser.addOption(
|
||||||
|
'target-revision',
|
||||||
|
help:
|
||||||
|
'Manually sets the target revision to generate the target reference app with. Passing this indicates '
|
||||||
|
'that the current flutter sdk version is not the version that should be migrated to.',
|
||||||
|
valueHelp: 'git revision hash',
|
||||||
|
);
|
||||||
|
argParser.addFlag(
|
||||||
|
'prefer-two-way-merge',
|
||||||
|
negatable: false,
|
||||||
|
help:
|
||||||
|
'Avoid three way merges when possible. Enabling this effectively ignores the base ancestor reference '
|
||||||
|
'files when a merge is required, opting for a simpler two way merge instead. In some edge cases typically '
|
||||||
|
'involving using a fallback or incorrect base revision, the default three way merge algorithm may produce '
|
||||||
|
'incorrect merges. Two way merges are more conflict prone, but less likely to produce incorrect results '
|
||||||
|
'silently.',
|
||||||
|
);
|
||||||
|
argParser.addFlag(
|
||||||
|
'flutter-subcommand',
|
||||||
|
help:
|
||||||
|
'Enable when using the flutter tool as a subcommand. This changes the '
|
||||||
|
'wording of log messages to indicate the correct suggested commands to use.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final bool _verbose;
|
||||||
|
|
||||||
|
final Logger logger;
|
||||||
|
|
||||||
|
final FileSystem fileSystem;
|
||||||
|
|
||||||
|
final MigrateUtils migrateUtils;
|
||||||
|
|
||||||
|
final ProcessManager processManager;
|
||||||
|
|
||||||
|
final bool standalone;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String name = 'start';
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String description =
|
||||||
|
r'Begins a new migration. Computes the changes needed to migrate the project from the base revision of Flutter to the current revision of Flutter and outputs the results in a working directory. Use `$ flutter migrate apply` accept and apply the changes.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<CommandResult> runCommand() async {
|
||||||
|
final FlutterToolsEnvironment environment =
|
||||||
|
await FlutterToolsEnvironment.initializeFlutterToolsEnvironment(
|
||||||
|
processManager, logger);
|
||||||
|
if (!_validateEnvironment(environment)) {
|
||||||
|
return const CommandResult(ExitStatus.fail);
|
||||||
|
}
|
||||||
|
final String? projectRootDirPath = stringArg('project-directory') ??
|
||||||
|
environment.getString('FlutterProject.directory');
|
||||||
|
final Directory projectRootDir = fileSystem.directory(projectRootDirPath);
|
||||||
|
final FlutterProjectFactory flutterProjectFactory = FlutterProjectFactory();
|
||||||
|
final FlutterProject project = projectRootDirPath == null
|
||||||
|
? FlutterProject.current(fileSystem)
|
||||||
|
: flutterProjectFactory
|
||||||
|
.fromDirectory(fileSystem.directory(projectRootDirPath));
|
||||||
|
|
||||||
|
if (!validateWorkingDirectory(project, logger)) {
|
||||||
|
return CommandResult.fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
final bool isModule =
|
||||||
|
environment.getBool('FlutterProject.isModule') ?? false;
|
||||||
|
final bool isPlugin =
|
||||||
|
environment.getBool('FlutterProject.isPlugin') ?? false;
|
||||||
|
if (isModule || isPlugin) {
|
||||||
|
logger.printError(
|
||||||
|
'Migrate tool only supports app projects. This project is a ${isModule ? 'module' : 'plugin'}');
|
||||||
|
return const CommandResult(ExitStatus.fail);
|
||||||
|
}
|
||||||
|
final bool isSubcommand = boolArg('flutter-subcommand') ?? !standalone;
|
||||||
|
|
||||||
|
if (!await gitRepoExists(project.directory.path, logger, migrateUtils)) {
|
||||||
|
return const CommandResult(ExitStatus.fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
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('Old migration already in progress.', emphasis: true);
|
||||||
|
logger.printStatus(
|
||||||
|
'Pending migration files exist in `${stagingDirectory.path}/$kDefaultMigrateStagingDirectoryName`');
|
||||||
|
logger.printStatus(
|
||||||
|
'Resolve merge conflicts and accept changes with by running:');
|
||||||
|
printCommandText('apply', logger, standalone: !isSubcommand);
|
||||||
|
logger.printStatus(
|
||||||
|
'You may also abandon the existing migration and start a new one with:');
|
||||||
|
printCommandText('abandon', logger, standalone: !isSubcommand);
|
||||||
|
return const CommandResult(ExitStatus.fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await hasUncommittedChanges(
|
||||||
|
project.directory.path, logger, migrateUtils)) {
|
||||||
|
return const CommandResult(ExitStatus.fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SupportedPlatform>? platforms;
|
||||||
|
if (stringArg('platforms') != null) {
|
||||||
|
platforms = <SupportedPlatform>[];
|
||||||
|
for (String platformString in stringArg('platforms')!.split(',')) {
|
||||||
|
platformString = platformString.trim();
|
||||||
|
platforms.add(SupportedPlatform.values.firstWhere(
|
||||||
|
(SupportedPlatform val) =>
|
||||||
|
val.toString() == 'SupportedPlatform.$platformString'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final MigrateCommandParameters commandParameters = MigrateCommandParameters(
|
||||||
|
verbose: _verbose,
|
||||||
|
baseAppPath: stringArg('base-app-directory'),
|
||||||
|
targetAppPath: stringArg('target-app-directory'),
|
||||||
|
baseRevision: stringArg('base-revision'),
|
||||||
|
targetRevision: stringArg('target-revision'),
|
||||||
|
deleteTempDirectories: boolArg('delete-temp-directories') ?? true,
|
||||||
|
platforms: platforms,
|
||||||
|
preferTwoWayMerge: boolArg('prefer-two-way-merge') ?? false,
|
||||||
|
allowFallbackBaseRevision:
|
||||||
|
boolArg('allow-fallback-base-revision') ?? false,
|
||||||
|
);
|
||||||
|
|
||||||
|
final MigrateResult? migrateResult = await computeMigration(
|
||||||
|
flutterProject: project,
|
||||||
|
commandParameters: commandParameters,
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
logger: logger,
|
||||||
|
migrateUtils: migrateUtils,
|
||||||
|
environment: environment,
|
||||||
|
);
|
||||||
|
if (migrateResult == null) {
|
||||||
|
return const CommandResult(ExitStatus.fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
await writeStagingDir(migrateResult, logger,
|
||||||
|
verbose: _verbose, projectRootDir: projectRootDir);
|
||||||
|
|
||||||
|
_deleteTempDirectories(
|
||||||
|
paths: <String>[],
|
||||||
|
directories: migrateResult.tempDirectories,
|
||||||
|
);
|
||||||
|
|
||||||
|
logger.printStatus(
|
||||||
|
'The migrate tool has staged proposed changes in the migrate staging directory.\n');
|
||||||
|
logger.printStatus('Guided conflict resolution wizard:');
|
||||||
|
printCommandText('resolve-conflicts', logger, standalone: !isSubcommand);
|
||||||
|
logger.printStatus('Check the status and diffs of the migration with:');
|
||||||
|
printCommandText('status', logger, standalone: !isSubcommand);
|
||||||
|
logger.printStatus('Abandon the proposed migration with:');
|
||||||
|
printCommandText('abandon', logger, standalone: !isSubcommand);
|
||||||
|
logger.printStatus(
|
||||||
|
'Accept staged changes after resolving any merge conflicts with:');
|
||||||
|
printCommandText('apply', logger, standalone: !isSubcommand);
|
||||||
|
|
||||||
|
return const CommandResult(ExitStatus.success);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deletes the files or directories at the provided paths.
|
||||||
|
void _deleteTempDirectories(
|
||||||
|
{List<String> paths = const <String>[],
|
||||||
|
List<Directory> directories = const <Directory>[]}) {
|
||||||
|
for (final Directory d in directories) {
|
||||||
|
try {
|
||||||
|
d.deleteSync(recursive: true);
|
||||||
|
} on FileSystemException catch (e) {
|
||||||
|
logger.printError(
|
||||||
|
'Unabled to delete ${d.path} due to ${e.message}, please clean up manually.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (final String p in paths) {
|
||||||
|
try {
|
||||||
|
fileSystem.directory(p).deleteSync(recursive: true);
|
||||||
|
} on FileSystemException catch (e) {
|
||||||
|
logger.printError(
|
||||||
|
'Unabled to delete $p due to ${e.message}, please clean up manually.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _validateEnvironment(FlutterToolsEnvironment environment) {
|
||||||
|
if (environment.getString('FlutterProject.directory') == null) {
|
||||||
|
logger.printError(
|
||||||
|
'No valid flutter project found. This command must be run from a flutter project directory');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (environment.getString('FlutterProject.manifest.appname') == null) {
|
||||||
|
logger.printError('No app name found in project pubspec.yaml');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!(environment.getBool('FlutterProject.android.exists') ?? false) &&
|
||||||
|
environment['FlutterProject.android.isKotlin'] == null) {
|
||||||
|
logger.printError(
|
||||||
|
'Could not detect if android project uses kotlin or java');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!(environment.getBool('FlutterProject.ios.exists') ?? false) &&
|
||||||
|
environment['FlutterProject.ios.isSwift'] == null) {
|
||||||
|
logger.printError(
|
||||||
|
'Could not detect if iosProject uses swift or objective-c');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes the files into the working directory for the developer to review and resolve any conflicts.
|
||||||
|
Future<void> writeStagingDir(MigrateResult migrateResult, Logger logger,
|
||||||
|
{bool verbose = false, required Directory projectRootDir}) async {
|
||||||
|
final Directory stagingDir =
|
||||||
|
projectRootDir.childDirectory(kDefaultMigrateStagingDirectoryName);
|
||||||
|
if (verbose) {
|
||||||
|
logger.printStatus(
|
||||||
|
'Writing migrate staging directory at `${stagingDir.path}`');
|
||||||
|
}
|
||||||
|
// Write files in working dir
|
||||||
|
for (final MergeResult result in migrateResult.mergeResults) {
|
||||||
|
final File file = stagingDir.childFile(result.localPath);
|
||||||
|
file.createSync(recursive: true);
|
||||||
|
if (result is StringMergeResult) {
|
||||||
|
file.writeAsStringSync(result.mergedString, flush: true);
|
||||||
|
} else {
|
||||||
|
file.writeAsBytesSync((result as BinaryMergeResult).mergedBytes,
|
||||||
|
flush: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write all files that are newly added in target
|
||||||
|
for (final FilePendingMigration addedFile in migrateResult.addedFiles) {
|
||||||
|
final File file = stagingDir.childFile(addedFile.localPath);
|
||||||
|
file.createSync(recursive: true);
|
||||||
|
try {
|
||||||
|
file.writeAsStringSync(addedFile.file.readAsStringSync(), flush: true);
|
||||||
|
} on FileSystemException {
|
||||||
|
file.writeAsBytesSync(addedFile.file.readAsBytesSync(), flush: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the MigrateManifest.
|
||||||
|
final MigrateManifest manifest = MigrateManifest(
|
||||||
|
migrateRootDir: stagingDir,
|
||||||
|
migrateResult: migrateResult,
|
||||||
|
);
|
||||||
|
manifest.writeFile();
|
||||||
|
|
||||||
|
// output the manifest contents.
|
||||||
|
checkAndPrintMigrateStatus(manifest, stagingDir, logger: logger);
|
||||||
|
|
||||||
|
logger.printBox('Staging directory created at `${stagingDir.path}`');
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,7 @@ class MigrateStatusCommand extends MigrateCommand {
|
|||||||
required this.logger,
|
required this.logger,
|
||||||
required this.fileSystem,
|
required this.fileSystem,
|
||||||
required ProcessManager processManager,
|
required ProcessManager processManager,
|
||||||
|
this.standalone = false,
|
||||||
}) : _verbose = verbose,
|
}) : _verbose = verbose,
|
||||||
migrateUtils = MigrateUtils(
|
migrateUtils = MigrateUtils(
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -65,6 +66,8 @@ class MigrateStatusCommand extends MigrateCommand {
|
|||||||
|
|
||||||
final MigrateUtils migrateUtils;
|
final MigrateUtils migrateUtils;
|
||||||
|
|
||||||
|
final bool standalone;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final String name = 'status';
|
final String name = 'status';
|
||||||
|
|
||||||
@ -86,7 +89,11 @@ class MigrateStatusCommand extends MigrateCommand {
|
|||||||
? FlutterProject.current(fileSystem)
|
? FlutterProject.current(fileSystem)
|
||||||
: flutterProjectFactory
|
: flutterProjectFactory
|
||||||
.fromDirectory(fileSystem.directory(projectDirectory));
|
.fromDirectory(fileSystem.directory(projectDirectory));
|
||||||
final bool isSubcommand = boolArg('flutter-subcommand') ?? false;
|
final bool isSubcommand = boolArg('flutter-subcommand') ?? !standalone;
|
||||||
|
|
||||||
|
if (!validateWorkingDirectory(project, logger)) {
|
||||||
|
return CommandResult.fail();
|
||||||
|
}
|
||||||
|
|
||||||
Directory stagingDirectory =
|
Directory stagingDirectory =
|
||||||
project.directory.childDirectory(kDefaultMigrateStagingDirectoryName);
|
project.directory.childDirectory(kDefaultMigrateStagingDirectoryName);
|
||||||
@ -166,7 +173,7 @@ class MigrateStatusCommand extends MigrateCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.printBox('Working directory at `${stagingDirectory.path}`');
|
logger.printBox('Staging directory at `${stagingDirectory.path}`');
|
||||||
|
|
||||||
checkAndPrintMigrateStatus(manifest, stagingDirectory, logger: logger);
|
checkAndPrintMigrateStatus(manifest, stagingDirectory, logger: logger);
|
||||||
|
|
||||||
|
@ -55,11 +55,8 @@ bool _skipped(String localPath, FileSystem fileSystem,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (skippedPrefixes != null) {
|
if (skippedPrefixes != null) {
|
||||||
final Iterable<String> canonicalizedSkippedPrefixes =
|
return skippedPrefixes.any((String prefix) => localPath.startsWith(
|
||||||
_skippedFiles.map<String>((String path) => canonicalize(path));
|
'${normalize(prefix.replaceAll(r'\', fileSystem.path.separator))}${fileSystem.path.separator}'));
|
||||||
return canonicalizedSkippedPrefixes.any((String prefix) =>
|
|
||||||
canonicalizedLocalPath
|
|
||||||
.startsWith('${canonicalize(prefix)}${fileSystem.path.separator}'));
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -114,7 +111,6 @@ Set<String> _getSkippedPrefixes(List<SupportedPlatform> platforms) {
|
|||||||
for (final SupportedPlatform platform in platforms) {
|
for (final SupportedPlatform platform in platforms) {
|
||||||
skippedPrefixes.remove(platformToSubdirectoryPrefix(platform));
|
skippedPrefixes.remove(platformToSubdirectoryPrefix(platform));
|
||||||
}
|
}
|
||||||
skippedPrefixes.remove(null);
|
|
||||||
return skippedPrefixes;
|
return skippedPrefixes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,10 +271,6 @@ Future<MigrateResult?> computeMigration({
|
|||||||
platforms: platforms,
|
platforms: platforms,
|
||||||
commandParameters: commandParameters,
|
commandParameters: commandParameters,
|
||||||
);
|
);
|
||||||
result.generatedBaseTemplateDirectory =
|
|
||||||
referenceProjects.baseProject.directory;
|
|
||||||
result.generatedTargetTemplateDirectory =
|
|
||||||
referenceProjects.targetProject.directory;
|
|
||||||
|
|
||||||
// Generate diffs. These diffs are used to determine if a file is newly added, needs merging,
|
// Generate diffs. These diffs are used to determine if a file is newly added, needs merging,
|
||||||
// or deleted (rare). Only files with diffs between the base and target revisions need to be
|
// or deleted (rare). Only files with diffs between the base and target revisions need to be
|
||||||
@ -377,8 +369,10 @@ Future<ReferenceProjects> _generateBaseAndTargetReferenceProjects({
|
|||||||
// Use user-provided projects if provided, if not, generate them internally.
|
// Use user-provided projects if provided, if not, generate them internally.
|
||||||
final bool customBaseProjectDir = commandParameters.baseAppPath != null;
|
final bool customBaseProjectDir = commandParameters.baseAppPath != null;
|
||||||
final bool customTargetProjectDir = commandParameters.targetAppPath != null;
|
final bool customTargetProjectDir = commandParameters.targetAppPath != null;
|
||||||
Directory? baseProjectDir;
|
Directory baseProjectDir =
|
||||||
Directory? targetProjectDir;
|
context.fileSystem.systemTempDirectory.createTempSync('baseProject');
|
||||||
|
Directory targetProjectDir =
|
||||||
|
context.fileSystem.systemTempDirectory.createTempSync('targetProject');
|
||||||
if (customBaseProjectDir) {
|
if (customBaseProjectDir) {
|
||||||
baseProjectDir =
|
baseProjectDir =
|
||||||
context.fileSystem.directory(commandParameters.baseAppPath);
|
context.fileSystem.directory(commandParameters.baseAppPath);
|
||||||
@ -402,6 +396,9 @@ Future<ReferenceProjects> _generateBaseAndTargetReferenceProjects({
|
|||||||
await context.migrateUtils.gitInit(baseProjectDir.absolute.path);
|
await context.migrateUtils.gitInit(baseProjectDir.absolute.path);
|
||||||
await context.migrateUtils.gitInit(targetProjectDir.absolute.path);
|
await context.migrateUtils.gitInit(targetProjectDir.absolute.path);
|
||||||
|
|
||||||
|
result.generatedBaseTemplateDirectory = baseProjectDir;
|
||||||
|
result.generatedTargetTemplateDirectory = targetProjectDir;
|
||||||
|
|
||||||
final String name =
|
final String name =
|
||||||
context.environment['FlutterProject.manifest.appname']! as String;
|
context.environment['FlutterProject.manifest.appname']! as String;
|
||||||
final String androidLanguage =
|
final String androidLanguage =
|
||||||
@ -821,7 +818,7 @@ class MigrateBaseFlutterProject extends MigrateFlutterProject {
|
|||||||
final List<String> platforms = <String>[];
|
final List<String> platforms = <String>[];
|
||||||
for (final MigratePlatformConfig config
|
for (final MigratePlatformConfig config
|
||||||
in revisionToConfigs[revision]!) {
|
in revisionToConfigs[revision]!) {
|
||||||
if (config.component == null) {
|
if (config.component == FlutterProjectComponent.root) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
platforms.add(config.component.toString().split('.').last);
|
platforms.add(config.component.toString().split('.').last);
|
||||||
|
@ -220,6 +220,7 @@ class MigrateUtils {
|
|||||||
'--deleted',
|
'--deleted',
|
||||||
'--modified',
|
'--modified',
|
||||||
'--others',
|
'--others',
|
||||||
|
'--exclude-standard',
|
||||||
'--exclude=${migrateStagingDir ?? kDefaultMigrateStagingDirectoryName}'
|
'--exclude=${migrateStagingDir ?? kDefaultMigrateStagingDirectoryName}'
|
||||||
];
|
];
|
||||||
final ProcessResult result =
|
final ProcessResult result =
|
||||||
@ -332,7 +333,7 @@ Future<bool> gitRepoExists(
|
|||||||
}
|
}
|
||||||
logger.printStatus(
|
logger.printStatus(
|
||||||
'Project is not a git repo. Please initialize a git repo and try again.');
|
'Project is not a git repo. Please initialize a git repo and try again.');
|
||||||
printCommandText('git init', logger);
|
printCommand('git init', logger);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -341,27 +342,34 @@ Future<bool> hasUncommittedChanges(
|
|||||||
if (await migrateUtils.hasUncommittedChanges(projectDirectory)) {
|
if (await migrateUtils.hasUncommittedChanges(projectDirectory)) {
|
||||||
logger.printStatus(
|
logger.printStatus(
|
||||||
'There are uncommitted changes in your project. Please git commit, abandon, or stash your changes before trying again.');
|
'There are uncommitted changes in your project. Please git commit, abandon, or stash your changes before trying again.');
|
||||||
logger.printStatus(
|
logger.printStatus('You may commit your changes using');
|
||||||
'You may commit your changes using \'git add .\' and \'git commit -m "<message"\'');
|
printCommand('git add .', logger, newlineAfter: false);
|
||||||
|
printCommand('git commit -m "<message>"', logger);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prints a command to logger with appropriate formatting.
|
void printCommand(String command, Logger logger, {bool newlineAfter = true}) {
|
||||||
void printCommandText(String command, Logger logger,
|
|
||||||
{bool? standalone = true}) {
|
|
||||||
final String prefix = standalone == null
|
|
||||||
? ''
|
|
||||||
: (standalone ? './bin/flutter_migrate ' : 'flutter migrate ');
|
|
||||||
logger.printStatus(
|
logger.printStatus(
|
||||||
'\n\$ $prefix$command\n',
|
'\n\$ $command${newlineAfter ? '\n' : ''}',
|
||||||
color: TerminalColor.grey,
|
color: TerminalColor.grey,
|
||||||
indent: 4,
|
indent: 4,
|
||||||
newline: false,
|
newline: false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Prints a command to logger with appropriate formatting.
|
||||||
|
void printCommandText(String command, Logger logger,
|
||||||
|
{bool? standalone = true, bool newlineAfter = true}) {
|
||||||
|
final String prefix = standalone == null
|
||||||
|
? ''
|
||||||
|
: (standalone
|
||||||
|
? 'dart run <flutter_migrate_dir>${Platform.pathSeparator}bin${Platform.pathSeparator}flutter_migrate.dart '
|
||||||
|
: 'flutter migrate ');
|
||||||
|
printCommand('$prefix$command', logger, newlineAfter: newlineAfter);
|
||||||
|
}
|
||||||
|
|
||||||
/// Defines the classification of difference between files.
|
/// Defines the classification of difference between files.
|
||||||
enum DiffType {
|
enum DiffType {
|
||||||
command,
|
command,
|
||||||
|
440
packages/flutter_migrate/test/migrate_test.dart
Normal file
440
packages/flutter_migrate/test/migrate_test.dart
Normal file
@ -0,0 +1,440 @@
|
|||||||
|
// Copyright 2013 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_migrate/src/base/common.dart';
|
||||||
|
import 'package:flutter_migrate/src/base/file_system.dart';
|
||||||
|
import 'package:flutter_migrate/src/base/io.dart';
|
||||||
|
import 'package:flutter_migrate/src/base/logger.dart';
|
||||||
|
import 'package:flutter_migrate/src/base/signals.dart';
|
||||||
|
import 'package:flutter_migrate/src/base/terminal.dart';
|
||||||
|
import 'package:process/process.dart';
|
||||||
|
|
||||||
|
import 'src/common.dart';
|
||||||
|
import 'src/context.dart';
|
||||||
|
import 'test_data/migrate_project.dart';
|
||||||
|
|
||||||
|
// This file contains E2E test that execute the core migrate commands
|
||||||
|
// and simulates manual conflict resolution and other manipulations of
|
||||||
|
// the project files.
|
||||||
|
void main() {
|
||||||
|
late Directory tempDir;
|
||||||
|
late BufferLogger logger;
|
||||||
|
late ProcessManager processManager;
|
||||||
|
late FileSystem fileSystem;
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
logger = BufferLogger.test();
|
||||||
|
processManager = const LocalProcessManager();
|
||||||
|
fileSystem = LocalFileSystem.test(signals: LocalSignals.instance);
|
||||||
|
tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_run_test');
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() async {
|
||||||
|
tryToDelete(tempDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<bool> hasFlutterEnvironment() async {
|
||||||
|
final String flutterRoot = getFlutterRoot();
|
||||||
|
final String flutterExecutable = fileSystem.path
|
||||||
|
.join(flutterRoot, 'bin', 'flutter${isWindows ? '.bat' : ''}');
|
||||||
|
final ProcessResult result = await Process.run(
|
||||||
|
flutterExecutable, <String>['analyze', '--suggestions', '--machine']);
|
||||||
|
if (result.exitCode != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrates a clean untouched app generated with flutter create
|
||||||
|
testUsingContext('vanilla migrate process succeeds', () async {
|
||||||
|
// This tool does not support old versions of flutter that dont include
|
||||||
|
// `flutter analyze --suggestions --machine` command
|
||||||
|
if (!await hasFlutterEnvironment()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Flutter Stable 1.22.6 hash: 9b2d32b605630f28625709ebd9d78ab3016b2bf6
|
||||||
|
await MigrateProject.installProject('version:1.22.6_stable', tempDir);
|
||||||
|
|
||||||
|
ProcessResult result = await runMigrateCommand(<String>[
|
||||||
|
'start',
|
||||||
|
'--verbose',
|
||||||
|
], workingDirectory: tempDir.path);
|
||||||
|
expect(result.stdout.toString(), contains('Staging directory created at'));
|
||||||
|
const String linesToMatch = '''
|
||||||
|
Added files:
|
||||||
|
- android/app/src/main/res/values-night/styles.xml
|
||||||
|
- android/app/src/main/res/drawable-v21/launch_background.xml
|
||||||
|
- analysis_options.yaml
|
||||||
|
Modified files:
|
||||||
|
- .metadata
|
||||||
|
- ios/Runner/Info.plist
|
||||||
|
- ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
|
||||||
|
- ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
|
||||||
|
- ios/Flutter/AppFrameworkInfo.plist
|
||||||
|
- ios/.gitignore
|
||||||
|
- pubspec.yaml
|
||||||
|
- .gitignore
|
||||||
|
- android/app/build.gradle
|
||||||
|
- android/app/src/profile/AndroidManifest.xml
|
||||||
|
- android/app/src/main/res/values/styles.xml
|
||||||
|
- android/app/src/main/AndroidManifest.xml
|
||||||
|
- android/app/src/debug/AndroidManifest.xml
|
||||||
|
- android/gradle/wrapper/gradle-wrapper.properties
|
||||||
|
- android/.gitignore
|
||||||
|
- android/build.gradle''';
|
||||||
|
for (final String line in linesToMatch.split('\n')) {
|
||||||
|
expect(result.stdout.toString(), contains(line));
|
||||||
|
}
|
||||||
|
|
||||||
|
result = await runMigrateCommand(<String>[
|
||||||
|
'apply',
|
||||||
|
'--verbose',
|
||||||
|
], workingDirectory: tempDir.path);
|
||||||
|
logger.printStatus('${result.exitCode}', color: TerminalColor.blue);
|
||||||
|
logger.printStatus(result.stdout, color: TerminalColor.green);
|
||||||
|
logger.printStatus(result.stderr, color: TerminalColor.red);
|
||||||
|
expect(result.exitCode, 0);
|
||||||
|
expect(result.stdout.toString(), contains('Migration complete'));
|
||||||
|
|
||||||
|
expect(tempDir.childFile('.metadata').readAsStringSync(),
|
||||||
|
contains('migration:\n platforms:\n - platform: root\n'));
|
||||||
|
|
||||||
|
expect(
|
||||||
|
tempDir
|
||||||
|
.childFile('android/app/src/main/res/values-night/styles.xml')
|
||||||
|
.existsSync(),
|
||||||
|
true);
|
||||||
|
expect(tempDir.childFile('analysis_options.yaml').existsSync(), true);
|
||||||
|
}, timeout: const Timeout(Duration(seconds: 500)), skip: isWindows);
|
||||||
|
|
||||||
|
// Migrates a clean untouched app generated with flutter create
|
||||||
|
testUsingContext('vanilla migrate builds', () async {
|
||||||
|
// This tool does not support old versions of flutter that dont include
|
||||||
|
// `flutter analyze --suggestions --machine` command
|
||||||
|
if (!await hasFlutterEnvironment()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Flutter Stable 2.0.0 hash: 60bd88df915880d23877bfc1602e8ddcf4c4dd2a
|
||||||
|
await MigrateProject.installProject('version:2.0.0_stable', tempDir,
|
||||||
|
main: '''
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
runApp(const MyApp());
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyApp extends StatelessWidget {
|
||||||
|
const MyApp({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
// This widget is the root of your application.
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MaterialApp(
|
||||||
|
title: 'Flutter Demo',
|
||||||
|
theme: ThemeData(
|
||||||
|
primarySwatch: Colors.blue,
|
||||||
|
),
|
||||||
|
home: Container(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
''');
|
||||||
|
ProcessResult result = await runMigrateCommand(<String>[
|
||||||
|
'start',
|
||||||
|
'--verbose',
|
||||||
|
], workingDirectory: tempDir.path);
|
||||||
|
expect(result.stdout.toString(), contains('Staging directory created at'));
|
||||||
|
|
||||||
|
result = await runMigrateCommand(<String>[
|
||||||
|
'apply',
|
||||||
|
'--verbose',
|
||||||
|
], workingDirectory: tempDir.path);
|
||||||
|
logger.printStatus('${result.exitCode}', color: TerminalColor.blue);
|
||||||
|
logger.printStatus(result.stdout, color: TerminalColor.green);
|
||||||
|
logger.printStatus(result.stderr, color: TerminalColor.red);
|
||||||
|
expect(result.exitCode, 0);
|
||||||
|
expect(result.stdout.toString(), contains('Migration complete'));
|
||||||
|
|
||||||
|
result = await processManager.run(<String>[
|
||||||
|
'flutter',
|
||||||
|
'build',
|
||||||
|
'apk',
|
||||||
|
'--debug',
|
||||||
|
], workingDirectory: tempDir.path);
|
||||||
|
expect(result.exitCode, 0);
|
||||||
|
expect(result.stdout.toString(), contains('app-debug.apk'));
|
||||||
|
// Skipped due to being flaky, the build completes successfully, but sometimes
|
||||||
|
// Gradle crashes due to resources on the bot. We should fine tune this to
|
||||||
|
// make it stable.
|
||||||
|
}, timeout: const Timeout(Duration(seconds: 900)), skip: true);
|
||||||
|
|
||||||
|
testUsingContext('migrate abandon', () async {
|
||||||
|
// Abandon in an empty dir fails.
|
||||||
|
ProcessResult result = await runMigrateCommand(<String>[
|
||||||
|
'abandon',
|
||||||
|
'--verbose',
|
||||||
|
], workingDirectory: tempDir.path);
|
||||||
|
expect(result.exitCode, 0);
|
||||||
|
expect(result.stderr.toString(),
|
||||||
|
contains('Error: No pubspec.yaml file found'));
|
||||||
|
expect(
|
||||||
|
result.stderr.toString(),
|
||||||
|
contains(
|
||||||
|
'This command should be run from the root of your Flutter project'));
|
||||||
|
|
||||||
|
final File manifestFile =
|
||||||
|
tempDir.childFile('migrate_staging_dir/.migrate_manifest');
|
||||||
|
expect(manifestFile.existsSync(), false);
|
||||||
|
|
||||||
|
// Flutter Stable 1.22.6 hash: 9b2d32b605630f28625709ebd9d78ab3016b2bf6
|
||||||
|
await MigrateProject.installProject('version:1.22.6_stable', tempDir);
|
||||||
|
|
||||||
|
// Initialized repo fails.
|
||||||
|
result = await runMigrateCommand(<String>[
|
||||||
|
'abandon',
|
||||||
|
'--verbose',
|
||||||
|
], workingDirectory: tempDir.path);
|
||||||
|
expect(result.exitCode, 0);
|
||||||
|
expect(result.stdout.toString(), contains('No migration in progress'));
|
||||||
|
|
||||||
|
// Create migration.
|
||||||
|
manifestFile.createSync(recursive: true);
|
||||||
|
|
||||||
|
// Directory with manifest_staging_dir succeeds.
|
||||||
|
result = await runMigrateCommand(<String>[
|
||||||
|
'abandon',
|
||||||
|
'--verbose',
|
||||||
|
'--force',
|
||||||
|
], workingDirectory: tempDir.path);
|
||||||
|
expect(result.exitCode, 0);
|
||||||
|
expect(result.stdout.toString(), contains('Abandon complete'));
|
||||||
|
}, timeout: const Timeout(Duration(seconds: 300)));
|
||||||
|
|
||||||
|
// Migrates a user-modified app
|
||||||
|
testUsingContext('modified migrate process succeeds', () async {
|
||||||
|
// This tool does not support old versions of flutter that dont include
|
||||||
|
// `flutter analyze --suggestions --machine` command
|
||||||
|
if (!await hasFlutterEnvironment()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Flutter Stable 1.22.6 hash: 9b2d32b605630f28625709ebd9d78ab3016b2bf6
|
||||||
|
await MigrateProject.installProject('version:1.22.6_stable', tempDir,
|
||||||
|
vanilla: false);
|
||||||
|
|
||||||
|
ProcessResult result = await runMigrateCommand(<String>[
|
||||||
|
'apply',
|
||||||
|
'--verbose',
|
||||||
|
], workingDirectory: tempDir.path);
|
||||||
|
expect(result.exitCode, 0);
|
||||||
|
expect(result.stdout.toString(), contains('No migration'));
|
||||||
|
|
||||||
|
result = await runMigrateCommand(<String>[
|
||||||
|
'status',
|
||||||
|
'--verbose',
|
||||||
|
], workingDirectory: tempDir.path);
|
||||||
|
expect(result.exitCode, 0);
|
||||||
|
expect(result.stdout.toString(), contains('No migration'));
|
||||||
|
|
||||||
|
result = await runMigrateCommand(<String>[
|
||||||
|
'start',
|
||||||
|
'--verbose',
|
||||||
|
], workingDirectory: tempDir.path);
|
||||||
|
expect(result.exitCode, 0);
|
||||||
|
expect(result.stdout.toString(), contains('Staging directory created at'));
|
||||||
|
const String linesToMatch = '''
|
||||||
|
Modified files:
|
||||||
|
- .metadata
|
||||||
|
- ios/Runner/Info.plist
|
||||||
|
- ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
|
||||||
|
- ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
|
||||||
|
- ios/Flutter/AppFrameworkInfo.plist
|
||||||
|
- ios/.gitignore
|
||||||
|
- .gitignore
|
||||||
|
- android/app/build.gradle
|
||||||
|
- android/app/src/profile/AndroidManifest.xml
|
||||||
|
- android/app/src/main/res/values/styles.xml
|
||||||
|
- android/app/src/main/AndroidManifest.xml
|
||||||
|
- android/app/src/debug/AndroidManifest.xml
|
||||||
|
- android/gradle/wrapper/gradle-wrapper.properties
|
||||||
|
- android/.gitignore
|
||||||
|
- android/build.gradle
|
||||||
|
Merge conflicted files:
|
||||||
|
- pubspec.yaml''';
|
||||||
|
for (final String line in linesToMatch.split('\n')) {
|
||||||
|
expect(result.stdout.toString(), contains(line));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call apply with conflicts remaining. Should fail.
|
||||||
|
result = await runMigrateCommand(<String>[
|
||||||
|
'apply',
|
||||||
|
'--verbose',
|
||||||
|
], workingDirectory: tempDir.path);
|
||||||
|
expect(result.exitCode, 0);
|
||||||
|
expect(
|
||||||
|
result.stdout.toString(),
|
||||||
|
contains(
|
||||||
|
'Conflicting files found. Resolve these conflicts and try again.'));
|
||||||
|
expect(result.stdout.toString(), contains('- pubspec.yaml'));
|
||||||
|
|
||||||
|
result = await runMigrateCommand(<String>[
|
||||||
|
'status',
|
||||||
|
'--verbose',
|
||||||
|
], workingDirectory: tempDir.path);
|
||||||
|
expect(result.exitCode, 0);
|
||||||
|
expect(result.stdout.toString(), contains('Modified files'));
|
||||||
|
expect(result.stdout.toString(), contains('Merge conflicted files'));
|
||||||
|
|
||||||
|
// Manually resolve conflics. The correct contents for resolution may change over time,
|
||||||
|
// but it shouldnt matter for this test.
|
||||||
|
final File metadataFile =
|
||||||
|
tempDir.childFile('migrate_staging_dir/.metadata');
|
||||||
|
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 and should not be manually edited.
|
||||||
|
|
||||||
|
version:
|
||||||
|
revision: e96a72392696df66755ca246ff291dfc6ca6c4ad
|
||||||
|
channel: unknown
|
||||||
|
|
||||||
|
project_type: app
|
||||||
|
|
||||||
|
''', flush: true);
|
||||||
|
final File pubspecYamlFile =
|
||||||
|
tempDir.childFile('migrate_staging_dir/pubspec.yaml');
|
||||||
|
pubspecYamlFile.writeAsStringSync('''
|
||||||
|
name: vanilla_app_1_22_6_stable
|
||||||
|
description: This is a modified description from the default.
|
||||||
|
|
||||||
|
# The following line prevents the package from being accidentally published to
|
||||||
|
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||||
|
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
|
|
||||||
|
# The following defines the version and build number for your application.
|
||||||
|
# A version number is three numbers separated by dots, like 1.2.43
|
||||||
|
# followed by an optional build number separated by a +.
|
||||||
|
# Both the version and the builder number may be overridden in flutter
|
||||||
|
# build by specifying --build-name and --build-number, respectively.
|
||||||
|
# In Android, build-name is used as versionName while build-number used as versionCode.
|
||||||
|
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
|
||||||
|
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||||
|
# Read more about iOS versioning at
|
||||||
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
|
version: 1.0.0+1
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ">=2.17.0-79.0.dev <3.0.0"
|
||||||
|
|
||||||
|
# Dependencies specify other packages that your package needs in order to work.
|
||||||
|
# To automatically upgrade your package dependencies to the latest versions
|
||||||
|
# consider running `flutter pub upgrade --major-versions`. Alternatively,
|
||||||
|
# dependencies can be manually updated by changing the version numbers below to
|
||||||
|
# the latest version available on pub.dev. To see which dependencies have newer
|
||||||
|
# versions available, run `flutter pub outdated`.
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
|
||||||
|
# The following adds the Cupertino Icons font to your application.
|
||||||
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
|
cupertino_icons: ^1.0.2
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
# The "flutter_lints" package below contains a set of recommended lints to
|
||||||
|
# encourage good coding practices. The lint set provided by the package is
|
||||||
|
# activated in the `analysis_options.yaml` file located at the root of your
|
||||||
|
# package. See that file for information about deactivating specific lint
|
||||||
|
# rules and activating additional ones.
|
||||||
|
flutter_lints: ^1.0.0
|
||||||
|
|
||||||
|
# For information on the generic Dart part of this file, see the
|
||||||
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
||||||
|
# The following section is specific to Flutter packages.
|
||||||
|
flutter:
|
||||||
|
|
||||||
|
# The following line ensures that the Material Icons font is
|
||||||
|
# included with your application, so that you can use the icons in
|
||||||
|
# the material Icons class.
|
||||||
|
uses-material-design: true
|
||||||
|
|
||||||
|
# To add assets to your application, add an assets section, like this:
|
||||||
|
assets:
|
||||||
|
- images/a_dot_burr.jpeg
|
||||||
|
- images/a_dot_ham.jpeg
|
||||||
|
|
||||||
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
|
# https://flutter.dev/assets-and-images/#resolution-aware.
|
||||||
|
|
||||||
|
# For details regarding adding assets from package dependencies, see
|
||||||
|
# https://flutter.dev/assets-and-images/#from-packages
|
||||||
|
|
||||||
|
# To add custom fonts to your application, add a fonts section here,
|
||||||
|
# in this "flutter" section. Each entry in this list should have a
|
||||||
|
# "family" key with the font family name, and a "fonts" key with a
|
||||||
|
# list giving the asset and other descriptors for the font. For
|
||||||
|
# example:
|
||||||
|
# fonts:
|
||||||
|
# - family: Schyler
|
||||||
|
# fonts:
|
||||||
|
# - asset: fonts/Schyler-Regular.ttf
|
||||||
|
# - asset: fonts/Schyler-Italic.ttf
|
||||||
|
# style: italic
|
||||||
|
# - family: Trajan Pro
|
||||||
|
# fonts:
|
||||||
|
# - asset: fonts/TrajanPro.ttf
|
||||||
|
# - asset: fonts/TrajanPro_Bold.ttf
|
||||||
|
# weight: 700
|
||||||
|
#
|
||||||
|
# For details regarding fonts from package dependencies,
|
||||||
|
# see https://flutter.dev/custom-fonts/#from-packages
|
||||||
|
|
||||||
|
''', flush: true);
|
||||||
|
|
||||||
|
result = await runMigrateCommand(<String>[
|
||||||
|
'status',
|
||||||
|
'--verbose',
|
||||||
|
], workingDirectory: tempDir.path);
|
||||||
|
expect(result.exitCode, 0);
|
||||||
|
expect(result.stdout.toString(), contains('Modified files'));
|
||||||
|
expect(result.stdout.toString(), contains('diff --git'));
|
||||||
|
expect(result.stdout.toString(), contains('@@'));
|
||||||
|
expect(result.stdout.toString(), isNot(contains('Merge conflicted files')));
|
||||||
|
|
||||||
|
result = await runMigrateCommand(<String>[
|
||||||
|
'apply',
|
||||||
|
'--verbose',
|
||||||
|
], workingDirectory: tempDir.path);
|
||||||
|
expect(result.exitCode, 0);
|
||||||
|
expect(result.stdout.toString(), contains('Migration complete'));
|
||||||
|
|
||||||
|
expect(tempDir.childFile('.metadata').readAsStringSync(),
|
||||||
|
contains('e96a72392696df66755ca246ff291dfc6ca6c4ad'));
|
||||||
|
expect(tempDir.childFile('pubspec.yaml').readAsStringSync(),
|
||||||
|
isNot(contains('">=2.6.0 <3.0.0"')));
|
||||||
|
expect(tempDir.childFile('pubspec.yaml').readAsStringSync(),
|
||||||
|
contains('">=2.17.0-79.0.dev <3.0.0"'));
|
||||||
|
expect(
|
||||||
|
tempDir.childFile('pubspec.yaml').readAsStringSync(),
|
||||||
|
contains(
|
||||||
|
'description: This is a modified description from the default.'));
|
||||||
|
expect(tempDir.childFile('lib/main.dart').readAsStringSync(),
|
||||||
|
contains('OtherWidget()'));
|
||||||
|
expect(tempDir.childFile('lib/other.dart').existsSync(), true);
|
||||||
|
expect(tempDir.childFile('lib/other.dart').readAsStringSync(),
|
||||||
|
contains('class OtherWidget'));
|
||||||
|
|
||||||
|
expect(
|
||||||
|
tempDir
|
||||||
|
.childFile('android/app/src/main/res/values-night/styles.xml')
|
||||||
|
.existsSync(),
|
||||||
|
true);
|
||||||
|
expect(tempDir.childFile('analysis_options.yaml').existsSync(), true);
|
||||||
|
}, timeout: const Timeout(Duration(seconds: 500)), skip: isWindows);
|
||||||
|
}
|
@ -56,7 +56,7 @@ class MigrateProject extends Project {
|
|||||||
'git',
|
'git',
|
||||||
'commit',
|
'commit',
|
||||||
'-m',
|
'-m',
|
||||||
'"Initial commit"',
|
'"All changes"',
|
||||||
], workingDirectory: dir.path);
|
], workingDirectory: dir.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,18 +101,37 @@ class MigrateProject extends Project {
|
|||||||
], workingDirectory: dir.path);
|
], workingDirectory: dir.path);
|
||||||
|
|
||||||
if (Platform.isWindows) {
|
if (Platform.isWindows) {
|
||||||
await processManager.run(<String>[
|
ProcessResult res = await processManager.run(<String>[
|
||||||
'robocopy',
|
'robocopy',
|
||||||
tempDir.path,
|
tempDir.path,
|
||||||
dir.path,
|
dir.path,
|
||||||
'*',
|
'*',
|
||||||
'/E',
|
'/E',
|
||||||
|
'/V',
|
||||||
'/mov',
|
'/mov',
|
||||||
]);
|
]);
|
||||||
// Add full access permissions to Users
|
// Robocopy exit code 1 means some files were copied. 0 means no files were copied.
|
||||||
await processManager.run(<String>[
|
assert(res.exitCode == 1);
|
||||||
|
res = await processManager.run(<String>[
|
||||||
|
'takeown',
|
||||||
|
'/f',
|
||||||
|
dir.path,
|
||||||
|
'/r',
|
||||||
|
]);
|
||||||
|
res = await processManager.run(<String>[
|
||||||
|
'takeown',
|
||||||
|
'/f',
|
||||||
|
'${dir.path}\\lib\\main.dart',
|
||||||
|
'/r',
|
||||||
|
]);
|
||||||
|
res = await processManager.run(<String>[
|
||||||
'icacls',
|
'icacls',
|
||||||
tempDir.path,
|
dir.path,
|
||||||
|
], workingDirectory: dir.path);
|
||||||
|
// Add full access permissions to Users
|
||||||
|
res = await processManager.run(<String>[
|
||||||
|
'icacls',
|
||||||
|
dir.path,
|
||||||
'/q',
|
'/q',
|
||||||
'/c',
|
'/c',
|
||||||
'/t',
|
'/t',
|
||||||
@ -138,8 +157,16 @@ class MigrateProject extends Project {
|
|||||||
|
|
||||||
await processManager.run(<String>[
|
await processManager.run(<String>[
|
||||||
'chmod',
|
'chmod',
|
||||||
|
'-R',
|
||||||
'+w',
|
'+w',
|
||||||
'${dir.path}${fileSystem.path.separator}*',
|
dir.path,
|
||||||
|
], workingDirectory: dir.path);
|
||||||
|
|
||||||
|
await processManager.run(<String>[
|
||||||
|
'chmod',
|
||||||
|
'-R',
|
||||||
|
'+r',
|
||||||
|
dir.path,
|
||||||
], workingDirectory: dir.path);
|
], workingDirectory: dir.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter_migrate/src/base/common.dart';
|
||||||
import 'package:flutter_migrate/src/base/file_system.dart';
|
import 'package:flutter_migrate/src/base/file_system.dart';
|
||||||
import 'package:flutter_migrate/src/base/io.dart';
|
import 'package:flutter_migrate/src/base/io.dart';
|
||||||
import 'package:flutter_migrate/src/base/logger.dart';
|
import 'package:flutter_migrate/src/base/logger.dart';
|
||||||
@ -238,7 +239,17 @@ void main() {
|
|||||||
logger.clear();
|
logger.clear();
|
||||||
|
|
||||||
printCommandText('fullstandalone', logger);
|
printCommandText('fullstandalone', logger);
|
||||||
expect(logger.statusText, contains('./bin/flutter_migrate fullstandalone'));
|
if (isWindows) {
|
||||||
|
expect(
|
||||||
|
logger.statusText,
|
||||||
|
contains(
|
||||||
|
r'dart run <flutter_migrate_dir>\bin\flutter_migrate.dart fullstandalone'));
|
||||||
|
} else {
|
||||||
|
expect(
|
||||||
|
logger.statusText,
|
||||||
|
contains(
|
||||||
|
'dart run <flutter_migrate_dir>/bin/flutter_migrate.dart fullstandalone'));
|
||||||
|
}
|
||||||
logger.clear();
|
logger.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user