mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
[flutter_tools] enable flutter upgrade
to support force pushed branches (#55594)
This commit is contained in:
parent
f37a91a7bf
commit
4552af155c
@ -100,7 +100,12 @@ class UpgradeCommandRunner {
|
||||
@required FlutterVersion flutterVersion,
|
||||
@required bool testFlow,
|
||||
}) async {
|
||||
await verifyUpstreamConfigured();
|
||||
final String upstreamRevision = await fetchRemoteRevision();
|
||||
if (flutterVersion.frameworkRevision == upstreamRevision) {
|
||||
globals.printStatus('Flutter is already up to date on channel ${flutterVersion.channel}');
|
||||
globals.printStatus('$flutterVersion');
|
||||
return;
|
||||
}
|
||||
if (!force && gitTagVersion == const GitTagVersion.unknown()) {
|
||||
// If the commit is a recognized branch and not master,
|
||||
// explain that we are avoiding potential damage.
|
||||
@ -132,12 +137,8 @@ class UpgradeCommandRunner {
|
||||
}
|
||||
recordState(flutterVersion);
|
||||
await upgradeChannel(flutterVersion);
|
||||
final bool alreadyUpToDate = await attemptFastForward(flutterVersion);
|
||||
if (alreadyUpToDate) {
|
||||
// If the upgrade was a no op, then do not continue with the second half.
|
||||
globals.printStatus('Flutter is already up to date on channel ${flutterVersion.channel}');
|
||||
globals.printStatus('$flutterVersion');
|
||||
} else if (!testFlow) {
|
||||
await attemptReset(flutterVersion, upstreamRevision);
|
||||
if (!testFlow) {
|
||||
await flutterUpgradeContinue();
|
||||
}
|
||||
}
|
||||
@ -199,16 +200,25 @@ class UpgradeCommandRunner {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Check if there is an upstream repository configured.
|
||||
/// Returns the remote HEAD revision.
|
||||
///
|
||||
/// Exits tool if there is no upstream.
|
||||
Future<void> verifyUpstreamConfigured() async {
|
||||
Future<String> fetchRemoteRevision() async {
|
||||
String revision;
|
||||
try {
|
||||
// Fetch upstream branch's commits and tags
|
||||
await processUtils.run(
|
||||
<String>[ 'git', 'rev-parse', '@{u}'],
|
||||
<String>['git', 'fetch', '--tags'],
|
||||
throwOnError: true,
|
||||
workingDirectory: workingDirectory,
|
||||
);
|
||||
// '@{u}' means upstream HEAD
|
||||
final RunResult result = await processUtils.run(
|
||||
<String>[ 'git', 'rev-parse', '--verify', '@{u}'],
|
||||
throwOnError: true,
|
||||
workingDirectory: workingDirectory,
|
||||
);
|
||||
revision = result.stdout.trim();
|
||||
} on Exception {
|
||||
throwToolExit(
|
||||
'Unable to upgrade Flutter: no origin repository configured. '
|
||||
@ -216,6 +226,7 @@ class UpgradeCommandRunner {
|
||||
"https://github.com/flutter/flutter' in $workingDirectory",
|
||||
);
|
||||
}
|
||||
return revision;
|
||||
}
|
||||
|
||||
/// Attempts to upgrade the channel.
|
||||
@ -227,33 +238,20 @@ class UpgradeCommandRunner {
|
||||
await ChannelCommand.upgradeChannel();
|
||||
}
|
||||
|
||||
/// Attempts to rebase the upstream onto the local branch.
|
||||
/// Attempts a hard reset to the given revision.
|
||||
///
|
||||
/// If there haven't been any hot fixes or local changes, this is equivalent
|
||||
/// to a fast-forward.
|
||||
///
|
||||
/// If the fast forward lands us on the same channel and revision, then
|
||||
/// returns true, otherwise returns false.
|
||||
Future<bool> attemptFastForward(FlutterVersion oldFlutterVersion) async {
|
||||
final int code = await processUtils.stream(
|
||||
<String>['git', 'pull', '--ff-only'],
|
||||
/// This is a reset instead of fast forward because if we are on a release
|
||||
/// branch with cherry picks, there may not be a direct fast-forward route
|
||||
/// to the next release.
|
||||
Future<void> attemptReset(FlutterVersion oldFlutterVersion, String newRevision) async {
|
||||
final RunResult result = await processUtils.run(
|
||||
<String>['git', 'reset', '--hard', newRevision],
|
||||
throwOnError: true,
|
||||
workingDirectory: workingDirectory,
|
||||
mapFunction: (String line) => matchesGitLine(line) ? null : line,
|
||||
);
|
||||
if (code != 0) {
|
||||
throwToolExit(null, exitCode: code);
|
||||
if (result.exitCode != 0) {
|
||||
throwToolExit(null, exitCode: result.exitCode);
|
||||
}
|
||||
|
||||
// Check if the upgrade did anything.
|
||||
bool alreadyUpToDate = false;
|
||||
try {
|
||||
final FlutterVersion newFlutterVersion = FlutterVersion(const SystemClock(), workingDirectory);
|
||||
alreadyUpToDate = newFlutterVersion.channel == oldFlutterVersion.channel &&
|
||||
newFlutterVersion.frameworkRevision == oldFlutterVersion.frameworkRevision;
|
||||
} on Exception catch (e) {
|
||||
globals.printTrace('Failed to determine FlutterVersion after upgrade fast-forward: $e');
|
||||
}
|
||||
return alreadyUpToDate;
|
||||
}
|
||||
|
||||
/// Update the engine repository and precache all artifacts.
|
||||
@ -300,18 +298,4 @@ class UpgradeCommandRunner {
|
||||
allowReentrantFlutter: true,
|
||||
);
|
||||
}
|
||||
|
||||
// dev/benchmarks/complex_layout/lib/main.dart | 24 +-
|
||||
static final RegExp _gitDiffRegex = RegExp(r' (\S+)\s+\|\s+\d+ [+-]+');
|
||||
|
||||
// rename {packages/flutter/doc => dev/docs}/styles.html (92%)
|
||||
// delete mode 100644 doc/index.html
|
||||
// create mode 100644 dev/integration_tests/flutter_gallery/lib/gallery/demo.dart
|
||||
static final RegExp _gitChangedRegex = RegExp(r' (rename|delete mode|create mode) .+');
|
||||
|
||||
static bool matchesGitLine(String line) {
|
||||
return _gitDiffRegex.hasMatch(line)
|
||||
|| _gitChangedRegex.hasMatch(line)
|
||||
|| line == 'Fast-forward';
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/io.dart';
|
||||
|
||||
import 'package:flutter_tools/src/base/os.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/commands/upgrade.dart';
|
||||
import 'package:flutter_tools/src/convert.dart';
|
||||
@ -134,7 +133,10 @@ void main() {
|
||||
});
|
||||
|
||||
testUsingContext("Doesn't continue on known tag, dev branch, no force, already up-to-date", () async {
|
||||
const String revision = 'abc123';
|
||||
when(flutterVersion.frameworkRevision).thenReturn(revision);
|
||||
fakeCommandRunner.alreadyUpToDate = true;
|
||||
fakeCommandRunner.remoteRevision = revision;
|
||||
final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
|
||||
force: false,
|
||||
continueFlow: false,
|
||||
@ -159,16 +161,26 @@ void main() {
|
||||
Platform: () => fakePlatform,
|
||||
});
|
||||
|
||||
testUsingContext('verifyUpstreamConfigured', () async {
|
||||
when(globals.processManager.run(
|
||||
<String>['git', 'rev-parse', '@{u}'],
|
||||
testUsingContext('fetchRemoteRevision', () async {
|
||||
const String revision = 'abc123';
|
||||
when(processManager.run(
|
||||
<String>['git', 'fetch', '--tags'],
|
||||
environment:anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory')),
|
||||
).thenAnswer((Invocation invocation) async {
|
||||
return FakeProcessResult()
|
||||
..exitCode = 0;
|
||||
});
|
||||
await realCommandRunner.verifyUpstreamConfigured();
|
||||
when(processManager.run(
|
||||
<String>['git', 'rev-parse', '--verify', '@{u}'],
|
||||
environment:anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory')),
|
||||
).thenAnswer((Invocation invocation) async {
|
||||
return FakeProcessResult()
|
||||
..exitCode = 0
|
||||
..stdout = revision;
|
||||
});
|
||||
expect(await realCommandRunner.fetchRemoteRevision(), revision);
|
||||
}, overrides: <Type, Generator>{
|
||||
ProcessManager: () => processManager,
|
||||
Platform: () => fakePlatform,
|
||||
@ -284,58 +296,6 @@ void main() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group('matchesGitLine', () {
|
||||
setUpAll(() {
|
||||
Cache.disableLocking();
|
||||
});
|
||||
|
||||
bool _match(String line) => UpgradeCommandRunner.matchesGitLine(line);
|
||||
|
||||
test('regex match', () {
|
||||
expect(_match(' .../flutter_gallery/lib/demo/buttons_demo.dart | 10 +--'), true);
|
||||
expect(_match(' dev/benchmarks/complex_layout/lib/main.dart | 24 +-'), true);
|
||||
|
||||
expect(_match(' rename {packages/flutter/doc => dev/docs}/styles.html (92%)'), true);
|
||||
expect(_match(' delete mode 100644 doc/index.html'), true);
|
||||
expect(_match(' create mode 100644 dev/integration_tests/flutter_gallery/lib/gallery/demo.dart'), true);
|
||||
|
||||
expect(_match('Fast-forward'), true);
|
||||
});
|
||||
|
||||
test("regex doesn't match", () {
|
||||
expect(_match('Updating 79cfe1e..5046107'), false);
|
||||
expect(_match('229 files changed, 6179 insertions(+), 3065 deletions(-)'), false);
|
||||
});
|
||||
|
||||
group('findProjectRoot', () {
|
||||
Directory tempDir;
|
||||
|
||||
setUp(() async {
|
||||
tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_tools_upgrade_test.');
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
tryToDelete(tempDir);
|
||||
});
|
||||
|
||||
testUsingContext('in project', () async {
|
||||
final String projectPath = await createProject(tempDir);
|
||||
expect(findProjectRoot(projectPath), projectPath);
|
||||
expect(findProjectRoot(globals.fs.path.join(projectPath, 'lib')), projectPath);
|
||||
|
||||
final String hello = globals.fs.path.join(Cache.flutterRoot, 'examples', 'hello_world');
|
||||
expect(findProjectRoot(hello), hello);
|
||||
expect(findProjectRoot(globals.fs.path.join(hello, 'lib')), hello);
|
||||
});
|
||||
|
||||
testUsingContext('outside project', () async {
|
||||
final String projectPath = await createProject(tempDir);
|
||||
expect(findProjectRoot(globals.fs.directory(projectPath).parent.path), null);
|
||||
expect(findProjectRoot(Cache.flutterRoot), null);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class FakeUpgradeCommandRunner extends UpgradeCommandRunner {
|
||||
@ -343,8 +303,10 @@ class FakeUpgradeCommandRunner extends UpgradeCommandRunner {
|
||||
|
||||
bool alreadyUpToDate = false;
|
||||
|
||||
String remoteRevision = '';
|
||||
|
||||
@override
|
||||
Future<void> verifyUpstreamConfigured() async {}
|
||||
Future<String> fetchRemoteRevision() async => remoteRevision;
|
||||
|
||||
@override
|
||||
Future<bool> hasUncomittedChanges() async => willHaveUncomittedChanges;
|
||||
@ -353,7 +315,7 @@ class FakeUpgradeCommandRunner extends UpgradeCommandRunner {
|
||||
Future<void> upgradeChannel(FlutterVersion flutterVersion) async {}
|
||||
|
||||
@override
|
||||
Future<bool> attemptFastForward(FlutterVersion flutterVersion) async => alreadyUpToDate;
|
||||
Future<bool> attemptReset(FlutterVersion flutterVersion, String newRevision) async => alreadyUpToDate;
|
||||
|
||||
@override
|
||||
Future<void> precacheArtifacts() async {}
|
||||
|
@ -57,8 +57,7 @@ void main() {
|
||||
'git', 'config', '--system', 'core.longpaths', 'true',
|
||||
]);
|
||||
|
||||
print('Step 1');
|
||||
// Step 1. Clone the dev branch of flutter into the test directory.
|
||||
print('Step 1 - clone the $_kBranch of flutter into the test directory');
|
||||
exitCode = await processUtils.stream(<String>[
|
||||
'git',
|
||||
'clone',
|
||||
@ -66,8 +65,7 @@ void main() {
|
||||
], workingDirectory: parentDirectory.path, trace: true);
|
||||
expect(exitCode, 0);
|
||||
|
||||
print('Step 2');
|
||||
// Step 2. Switch to the dev branch.
|
||||
print('Step 2 - switch to the $_kBranch');
|
||||
exitCode = await processUtils.stream(<String>[
|
||||
'git',
|
||||
'checkout',
|
||||
@ -78,8 +76,7 @@ void main() {
|
||||
], workingDirectory: testDirectory.path, trace: true);
|
||||
expect(exitCode, 0);
|
||||
|
||||
print('Step 3');
|
||||
// Step 3. Revert to a prior version.
|
||||
print('Step 3 - revert back to $_kInitialVersion');
|
||||
exitCode = await processUtils.stream(<String>[
|
||||
'git',
|
||||
'reset',
|
||||
@ -88,9 +85,8 @@ void main() {
|
||||
], workingDirectory: testDirectory.path, trace: true);
|
||||
expect(exitCode, 0);
|
||||
|
||||
print('Step 4');
|
||||
// Step 4. Upgrade to the newest stable. This should update the persistent
|
||||
// tool state with the sha for v1.14.3
|
||||
print('Step 4 - upgrade to the newest $_kBranch');
|
||||
// This should update the persistent tool state with the sha for HEAD
|
||||
exitCode = await processUtils.stream(<String>[
|
||||
flutterBin,
|
||||
'upgrade',
|
||||
@ -99,8 +95,7 @@ void main() {
|
||||
], workingDirectory: testDirectory.path, trace: true);
|
||||
expect(exitCode, 0);
|
||||
|
||||
print('Step 5');
|
||||
// Step 5. Verify that the version is different.
|
||||
print('Step 5 - verify that the version is different');
|
||||
final RunResult versionResult = await processUtils.run(<String>[
|
||||
'git',
|
||||
'describe',
|
||||
@ -111,8 +106,9 @@ void main() {
|
||||
'--tags',
|
||||
], workingDirectory: testDirectory.path);
|
||||
expect(versionResult.stdout, isNot(contains(_kInitialVersion)));
|
||||
print('current version is ${versionResult.stdout.trim()}\ninitial was $_kInitialVersion');
|
||||
|
||||
print('Step 6');
|
||||
print('Step 6 - downgrade back to the initial version');
|
||||
// Step 6. Downgrade back to initial version.
|
||||
exitCode = await processUtils.stream(<String>[
|
||||
flutterBin,
|
||||
@ -122,7 +118,7 @@ void main() {
|
||||
], workingDirectory: testDirectory.path, trace: true);
|
||||
expect(exitCode, 0);
|
||||
|
||||
print('Step 7');
|
||||
print('Step 7 - verify downgraded version matches original version');
|
||||
// Step 7. Verify downgraded version matches original version.
|
||||
final RunResult oldVersionResult = await processUtils.run(<String>[
|
||||
'git',
|
||||
@ -134,5 +130,6 @@ void main() {
|
||||
'--tags',
|
||||
], workingDirectory: testDirectory.path);
|
||||
expect(oldVersionResult.stdout, contains(_kInitialVersion));
|
||||
print('current version is ${oldVersionResult.stdout.trim()}\ninitial was $_kInitialVersion');
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user