-
Notifications
You must be signed in to change notification settings - Fork 29.7k
MigrateConfig and migrate integration testing base #99092
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
f48f1d5 to
cdbe0c8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to clarify, will flutter create . include a .migrate_config file in the app's root directory?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there any issue with using the existing .metadata file for the root project directory, and rename .migrate_config as .metadata for the platform directories?
Migrate is adding a few entries to the dictionary, I'm not sure if we need a dedicated file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the existing .metadata file is being managed via templates, while the migrate_config is being managed by the MigrateConfig class. It is possible to merge the two of course, but we would also have to add it to each platform and handle the case where we need to modify it contrary to what the template would generate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And yes, flutter create will create the .migrate_config file in the root as well as each platform dir.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the existing .metadata file is being managed via templates, while the migrate_config is being managed by the MigrateConfig class.
That sounds good. The .metadata file was created with these goals in mind:
# 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.
It sounds like the use cases of .migrate_config should be covered by this file. Any thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that we shouldnt have too many metadata files. I'll see if we can merge the existing .metadata file with the .migrate_config file. Though the new version of .metadata will probably no longer be a read-only template generated file anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is a proposed extended .metadata file. This unifies separate config files in each platform dir to one single migrate section in the root .metadata file to remain consistent with the existing metadata tracking system. Since this file will now be owned by the tool, we can easily update the values in it as new platforms are added/edited.
# 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: {{flutterRevision}}
channel: {{flutterChannel}}
project_type: app
# Tracks metadata for the flutter migrate command
migrate:
platforms:
- platform: android
createRevision: fj19vkla9vnlka9vni3n808v3nch8cd
baseRevision: 93kf9v3njfa90vnidfjvn39nvi3vnie
- platform: ios
createRevision: fj19vkla9vnlka9vni3n808v3nch8cd
baseRevision: 93kf9v3njfa90vnidfjvn39nvi3vnie
# 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.
unmanagedFiles:
- lib/file1/etc.dart
- android/my_file.java
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the only minor comment is to maybe use a noun for consistency, so migrate -> migration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
an enum may be safer here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might go through the whole tools repo and unify references to the different platforms into a single enum. We currently have a few different ways of id-ing platforms already.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer an enum; if not, it doesn't seem like the String? platform arg should be nullable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why would a consumer of this API avoid passing the platform?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made non-nullable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: projectDirectory docs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can be avoided if an enum is used instead. I believe Dart will enforce all cases in the enum to be handled in the switch statement. If that's not true, I've been doing too much golang on the side then :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this seems like a utility that belongs in FlutterProject. A method that provides all the supported platforms.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about if result.exitCode != 0 ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll be moving this into a utility class in the next PR where I handle all of this. I'd prefer to not replicate all the error handling here temporarily as it will be removed very soon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
projectPath sounds like the app's project path, but this is the directory where Flutter is installed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
did it mean .metadata?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, this doc is correct. The fallback here is for when the migration config doesn't know the revision, so we try to parse it out of .metadata and other means.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this throw?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @jmagman
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have this, but it needs to be run on a Mac with Xcode installed.
flutter/packages/flutter_tools/lib/src/macos/cocoapods.dart
Lines 246 to 249 in 320d2cf
| final bool isSwift = (await _xcodeProjectInterpreter.getBuildSettings( | |
| runnerProject.path, | |
| buildContext: const XcodeProjectBuildContext(), | |
| )).containsKey('SWIFT_VERSION'); |
I'm guessing this needs to work on all platforms, like if you ran this on Windows you would still want the ios directory migrated.
A user could rename AppDelegate.swift but they likely wouldn't.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. All platform compatibility is important. I can try to include a few more fallback checks if that is something that could help with robustness.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does it need to be in the app's directory? could it use the temp directory instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The intention of this working dir is to be easily accessible and obvious. Putting it in a temp or hidden away folder makes it hard for developers to enter and work with the files inside.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sgtm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about .migration? Some context: https://unix.stackexchange.com/a/21867
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I specifically don't want this directory hidden as it is meant to be accessed by the developer as part of the migration process. Also, just calling it migrate may not be sufficiently clear that it is meant to be modified/edited/viewed as part of migration imo.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does the tool also support Flutter modules?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. Not yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But if the future if/when we do, it would be nice to have this already in the .gitignore to allow for better backwards compatibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this support the glob pattern? for example: lib/**/*.dart seems reasonable to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a lib we typically use to support this? It seems we support just simple directories and file URIs in pubspec.yaml
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: EOF
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh TIL
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this file test data or code that runs as part of the test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This runs as part of the test to set up the environment/sample app.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was under the impression that this was test data since it's under a directory named test_data
Could it be moved to a different directory?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This generates/gets the test fixtures, which is consistent with what the other files in this directory do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok. This seems like a different pattern. Usually test data doesn't contain code that is executed to generate test data or fixtures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could this be set in the constructor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not in the way the current project class is designed. The directory is only known once we call setUpIn(), which must be called first before any of the other getters/methods are valid. We track the appPath here because some of the getters are dynamically filled with the CIPD downloaded contents.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you pull these into locals, it's used right above here:
androidPlatform: templateContext['android'] as bool ?? falseand to update the gradle properties.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When would this need to be specified?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is used in the other half of the feature where create is called programmatically to create an app to diff against. We want to specify this so that the metadata around the revisions is using the app's metadata, not creating brand new metadata as a regular create run would do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this only supposed to be called re-entrantly, let's make it hide: true and specify in the help text this is not supposed to be called by users.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turns out test/general.shard/args_test.dart: Help for command line arguments is consistently styled and complete enforces that options are not permanently hidden. The description advising not to use the flag should be sufficient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hide: !verboseHelp seems correct.
flutter/packages/flutter_tools/test/general.shard/args_test.dart
Lines 67 to 69 in 3e43c3e
| 'documentation we can add just for ourselves. If it is not intended ' | |
| 'for developers, then use "hide: !verboseHelp" to only show the ' | |
| 'help when people run with "--help --verbose".'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of "revision" maybe more specific "Flutter SDK git commit hash" or something like that @christopherfujino
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use Object? in null safe files, not dynamic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| unmanagedFiles: _kDefaultUnmanagedFiles[platform] != null ? _kDefaultUnmanagedFiles[platform]! : <String>[], | |
| unmanagedFiles: _kDefaultUnmanagedFiles[platform] ?? <String>[], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use flutterVersion.frameworkRevision?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me validate if flutterVersion.frameworkRevision always produces the form that I need.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to use FlutterVersion!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactor your new code to not use the global context variables, these should all pass with testWithoutContext.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FlutterProject in project.dart seems to be using globals.projectFactory and I'd prefer to leave this refactoring for a different PR. I've made the tests that can be context free context free.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha, yeah do what you can, and avoid it where possible in new code. There's still a bunch of tricky ones still hanging out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should be downloading cipd packages in a tools integration test. It should probably be its own test shard, with the cipd package dependency set up in the ci.yaml? @christopherfujino
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, if a test requires a cipd dependency, it should be specified in the ci.yaml: https://github.com/flutter/flutter/blob/master/.ci.yaml#L174 and then the actual downloading specified in a recipe module
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The way this has to be tested requires frequent setup and teardown of a sample app, and the recipes+deps system is not designed to support this case efficiently. I would need to frequently re-get the cipd deps and the recipes gets deps once at the beginning.
I could host the vanilla sample apps somewhere else and avoid CIPD. The choice to use CIPD is to avoid having to check in entire vanilla apps as test fixtures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already check in quite a few test apps, I say we just check it in. This way also if the app needs to be fixed, it's easy to find.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked in a test fixture app for multidex which is a much smaller mini-app, and even that needed to get hosted files such as icon images and stuff.
The test apps required for this must be full apps, which can get quite large. Also, due to the nature of this feature, I intend to eventually branch out to test between many full apps created by different historic flutter versions. Checking in multiple full flutter apps is not sustainable, which is why I turned to hosting on cipd. I could shift the host to github if that makes more sense. I'm not sure if github will appreciate the test infra traffic though, and git makes it much harder to cherrypick individual app fixtures by version and would likely need to be in separate repos to be efficient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I missed this message. I'll file a tracking issue for discussion on the approach to this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
/// The Flutter SDK revision this platform was created by.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: /// The Flutter SDK revision this platform was last migrated by.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when would the revision be unknown?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Legacy apps may not have this information available as it was created before we tracked it, in which case we use a fallback revision
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ahh ok.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this be a bit more programmatic (i.e. easier to extend)? Something like:
final validations = <String, Type>{
'platform': String,
'unmanagedFiles': YamlList,
};
bool isValid = true;
for (final entry in validations.entries) {
if (map[entry.key].runtimeType != entry.value) {
isValid = false;
logger.printError('The value of key ${entry.key} was expected to be ${entry.value} but was ${map[entry.key].runtimeType}');
break;
}
}
return isValid;| _versionYaml = versionYaml; | ||
| bool get isEmpty => platformConfigs.isEmpty && (unmanagedFiles.isEmpty || unmanagedFiles == _kDefaultUnmanagedFiles); | ||
|
|
||
| /// Parses the project for all supported platforms and populates the MigrateConfig |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// Parses the project for all supported platforms and populates the MigrateConfig | |
| /// Parses the project for all supported platforms and populates the [MigrateConfig] |
| import 'dart:io'; | ||
| // import 'package:flutter_tools/src/base/io.dart'; | ||
| import 'package:file/file.dart'; | ||
| // import 'package:flutter_tools/src/globals.dart' as globals; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are these commented out intentionally?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops!
blasten
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
zanderso
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of courtesy to the reviewers, please give a gentle ping when a final pass to give lgtm is needed. In particular, I'd like to see an lgtm from @christopherfujino on future PRs in this area that add large bits of new functionality to the tool, in particular #100284.
| await processManager.run(<String>[ | ||
| 'git', | ||
| 'clone', | ||
| 'https://chromium.googlesource.com/chromium/tools/depot_tools', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DBC
This should be pinned, and it should do a shallow clone.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For context: #100254
We already have infrastructure for downloading CIPD artifacts for tests
This PR is a subset of #95708
This adds MigrateConfig, which handles tracking migration metadata by writing a
.migrate_configfile in each platform directory.Each migrate config file tracks the initial create revision, base revision (revision that the app was last successfully migrated with), the platform the file is addressing, and a list of files and directories that should be unmanaged by the migration tool.
This PR also adds the core migrate_project.dart to enable integration testing of the tool. The testing framework downloads copies of fully generated apps off of CIPD.
Resolves #95360