mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
[flutter_tools] auto-migrate dev users to beta (#97028)
This commit is contained in:
parent
f6ee925892
commit
6b95add2fe
@ -123,7 +123,10 @@ class ChannelCommand extends FlutterCommand {
|
||||
|
||||
Future<void> _switchChannel(String branchName) async {
|
||||
globals.printStatus("Switching to flutter channel '$branchName'...");
|
||||
if (!kOfficialChannels.contains(branchName)) {
|
||||
if (kObsoleteBranches.containsKey(branchName)) {
|
||||
final String alternative = kObsoleteBranches[branchName]!;
|
||||
globals.printStatus("This channel is obsolete. Consider switching to the '$alternative' channel instead.");
|
||||
} else if (!kOfficialChannels.contains(branchName)) {
|
||||
globals.printStatus('This is not an official channel. For a list of available channels, try "flutter channel".');
|
||||
}
|
||||
await _checkout(branchName);
|
||||
@ -131,6 +134,15 @@ class ChannelCommand extends FlutterCommand {
|
||||
globals.printStatus("To ensure that you're on the latest build from this channel, run 'flutter upgrade'");
|
||||
}
|
||||
|
||||
static Future<void> upgradeChannel(FlutterVersion currentVersion) async {
|
||||
final String channel = currentVersion.channel;
|
||||
if (kObsoleteBranches.containsKey(channel)) {
|
||||
final String alternative = kObsoleteBranches[channel]!;
|
||||
globals.printStatus("Transitioning from '$channel' to '$alternative'...");
|
||||
return _checkout(alternative);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> _checkout(String branchName) async {
|
||||
// Get latest refs from upstream.
|
||||
int result = await globals.processUtils.stream(
|
||||
|
@ -14,6 +14,7 @@ import '../globals.dart' as globals;
|
||||
import '../persistent_tool_state.dart';
|
||||
import '../runner/flutter_command.dart';
|
||||
import '../version.dart';
|
||||
import 'channel.dart';
|
||||
|
||||
// The official docs to install Flutter.
|
||||
const String _flutterInstallDocs = 'https://flutter.dev/docs/get-started/install';
|
||||
@ -163,6 +164,7 @@ class UpgradeCommandRunner {
|
||||
);
|
||||
}
|
||||
recordState(flutterVersion);
|
||||
await ChannelCommand.upgradeChannel(flutterVersion);
|
||||
globals.printStatus('Upgrading Flutter to ${upstreamVersion.frameworkVersion} from ${flutterVersion.frameworkVersion} in $workingDirectory...');
|
||||
await attemptReset(upstreamVersion.frameworkRevision);
|
||||
if (!testFlow) {
|
||||
|
@ -83,7 +83,6 @@ class _DefaultPersistentToolState implements PersistentToolState {
|
||||
static const String _kRedisplayWelcomeMessage = 'redisplay-welcome-message';
|
||||
static const Map<Channel, String> _lastActiveVersionKeys = <Channel,String>{
|
||||
Channel.master: 'last-active-master-version',
|
||||
Channel.dev: 'last-active-dev-version',
|
||||
Channel.beta: 'last-active-beta-version',
|
||||
Channel.stable: 'last-active-stable-version'
|
||||
};
|
||||
|
@ -16,11 +16,18 @@ import 'globals.dart' as globals;
|
||||
|
||||
const String _unknownFrameworkVersion = '0.0.0-unknown';
|
||||
|
||||
/// This maps old branch names to the names of branches that replaced them.
|
||||
///
|
||||
/// For example, in 2021 we deprecated the "dev" channel and transitioned "dev"
|
||||
/// users to the "beta" channel.
|
||||
const Map<String, String> kObsoleteBranches = <String, String>{
|
||||
'dev': 'beta',
|
||||
};
|
||||
|
||||
/// The names of each channel/branch in order of increasing stability.
|
||||
enum Channel {
|
||||
// TODO(fujino): update to main https://github.com/flutter/flutter/issues/95041
|
||||
master,
|
||||
dev,
|
||||
beta,
|
||||
stable,
|
||||
}
|
||||
@ -28,7 +35,6 @@ enum Channel {
|
||||
// Beware: Keep order in accordance with stability
|
||||
const Set<String> kOfficialChannels = <String>{
|
||||
globals.kDefaultFrameworkChannel,
|
||||
'dev',
|
||||
'beta',
|
||||
'stable',
|
||||
};
|
||||
@ -320,7 +326,7 @@ class FlutterVersion {
|
||||
}();
|
||||
if (redactUnknownBranches || _branch!.isEmpty) {
|
||||
// Only return the branch names we know about; arbitrary branch names might contain PII.
|
||||
if (!kOfficialChannels.contains(_branch)) {
|
||||
if (!kOfficialChannels.contains(_branch) && !kObsoleteBranches.containsKey(_branch)) {
|
||||
return '[user-branch]';
|
||||
}
|
||||
}
|
||||
@ -634,6 +640,7 @@ class GitTagVersion {
|
||||
_runGit('git fetch ${globals.flutterGit} --tags -f', processUtils, workingDirectory);
|
||||
}
|
||||
}
|
||||
// find all tags attached to the given [gitRef]
|
||||
final List<String> tags = _runGit(
|
||||
'git tag --points-at $gitRef', processUtils, workingDirectory).trim().split('\n');
|
||||
|
||||
|
@ -60,7 +60,7 @@ void main() {
|
||||
});
|
||||
|
||||
testUsingContext('Downgrade exits on no recorded version', () async {
|
||||
final FakeFlutterVersion fakeFlutterVersion = FakeFlutterVersion(channel: 'dev');
|
||||
final FakeFlutterVersion fakeFlutterVersion = FakeFlutterVersion(channel: 'beta');
|
||||
fileSystem.currentDirectory.childFile('.flutter_tool_state')
|
||||
.writeAsStringSync('{"last-active-master-version":"abcd"}');
|
||||
final DowngradeCommand command = DowngradeCommand(
|
||||
@ -81,7 +81,7 @@ void main() {
|
||||
|
||||
expect(createTestCommandRunner(command).run(const <String>['downgrade']),
|
||||
throwsToolExit(message:
|
||||
'There is no previously recorded version for channel "dev".\n'
|
||||
'There is no previously recorded version for channel "beta".\n'
|
||||
'Channel "master" was previously on: v1.2.3.'
|
||||
),
|
||||
);
|
||||
|
@ -0,0 +1,126 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// @dart = 2.8
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:args/command_runner.dart';
|
||||
import 'package:file/file.dart';
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/commands/upgrade.dart';
|
||||
import 'package:flutter_tools/src/version.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
import '../../src/context.dart';
|
||||
import '../../src/fake_process_manager.dart';
|
||||
import '../../src/fakes.dart' show FakeFlutterVersion;
|
||||
import '../../src/test_flutter_command_runner.dart';
|
||||
|
||||
void main() {
|
||||
FileSystem fileSystem;
|
||||
BufferLogger logger;
|
||||
FakeProcessManager processManager;
|
||||
UpgradeCommand command;
|
||||
CommandRunner<void> runner;
|
||||
FlutterVersion flutterVersion;
|
||||
|
||||
setUpAll(() {
|
||||
Cache.disableLocking();
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
fileSystem = MemoryFileSystem.test();
|
||||
logger = BufferLogger.test();
|
||||
processManager = FakeProcessManager.empty();
|
||||
command = UpgradeCommand(
|
||||
verboseHelp: false,
|
||||
);
|
||||
runner = createTestCommandRunner(command);
|
||||
});
|
||||
|
||||
testUsingContext('can auto-migrate a user from dev to beta', () async {
|
||||
const String startingTag = '3.0.0-1.2.pre';
|
||||
flutterVersion = FakeFlutterVersion(channel: 'dev');
|
||||
const String latestUpstreamTag = '3.0.0-1.3.pre';
|
||||
const String upstreamHeadRevision = 'deadbeef';
|
||||
final Completer<void> reEntryCompleter = Completer<void>();
|
||||
|
||||
Future<void> reEnterTool() async {
|
||||
await runner.run(<String>['upgrade', '--continue', '--no-version-check']);
|
||||
reEntryCompleter.complete();
|
||||
}
|
||||
|
||||
processManager.addCommands(<FakeCommand>[
|
||||
const FakeCommand(
|
||||
command: <String>['git', 'tag', '--points-at', 'HEAD'],
|
||||
stdout: startingTag,
|
||||
),
|
||||
// Ensure we have upstream tags present locally
|
||||
const FakeCommand(
|
||||
command: <String>['git', 'fetch', '--tags'],
|
||||
),
|
||||
const FakeCommand(
|
||||
command: <String>['git', 'rev-parse', '--verify', '@{u}'],
|
||||
stdout: upstreamHeadRevision,
|
||||
),
|
||||
const FakeCommand(
|
||||
command: <String>['git', 'tag', '--points-at', upstreamHeadRevision],
|
||||
stdout: latestUpstreamTag,
|
||||
),
|
||||
// check for uncommitted changes; empty stdout means clean checkout
|
||||
const FakeCommand(
|
||||
command: <String>['git', 'status', '-s'],
|
||||
),
|
||||
|
||||
// here the tool is upgrading the branch from dev -> beta
|
||||
const FakeCommand(
|
||||
command: <String>['git', 'fetch'],
|
||||
),
|
||||
// test if there already exists a local beta branch; 0 exit code means yes
|
||||
const FakeCommand(
|
||||
command: <String>['git', 'show-ref', '--verify', '--quiet', 'refs/heads/beta'],
|
||||
),
|
||||
const FakeCommand(
|
||||
command: <String>['git', 'checkout', 'beta', '--'],
|
||||
),
|
||||
|
||||
// reset instead of pull since cherrypicks from one release branch will
|
||||
// not be present on a newer one
|
||||
const FakeCommand(
|
||||
command: <String>['git', 'reset', '--hard', upstreamHeadRevision],
|
||||
),
|
||||
// re-enter flutter command with the newer version, so that `doctor`
|
||||
// checks will be up to date
|
||||
FakeCommand(
|
||||
command: const <String>['bin/flutter', 'upgrade', '--continue', '--no-version-check'],
|
||||
onRun: reEnterTool,
|
||||
completer: reEntryCompleter,
|
||||
),
|
||||
|
||||
// commands following this are from the re-entrant `flutter upgrade --continue` call
|
||||
|
||||
const FakeCommand(
|
||||
command: <String>['git', 'tag', '--points-at', 'HEAD'],
|
||||
stdout: latestUpstreamTag,
|
||||
),
|
||||
const FakeCommand(
|
||||
command: <String>['bin/flutter', '--no-color', '--no-version-check', 'precache'],
|
||||
),
|
||||
const FakeCommand(
|
||||
command: <String>['bin/flutter', '--no-version-check', 'doctor'],
|
||||
),
|
||||
]);
|
||||
await runner.run(<String>['upgrade']);
|
||||
expect(processManager, hasNoRemainingExpectations);
|
||||
expect(logger.statusText, contains("Transitioning from 'dev' to 'beta'..."));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
FlutterVersion: () => flutterVersion,
|
||||
Logger: () => logger,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
}
|
@ -48,7 +48,7 @@ void main() {
|
||||
});
|
||||
|
||||
testUsingContext('throws on unknown tag, official branch, noforce', () async {
|
||||
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(channel: 'dev');
|
||||
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(channel: 'beta');
|
||||
const String upstreamRevision = '';
|
||||
final FakeFlutterVersion latestVersion = FakeFlutterVersion(frameworkRevision: upstreamRevision);
|
||||
fakeCommandRunner.remoteVersion = latestVersion;
|
||||
@ -68,7 +68,7 @@ void main() {
|
||||
});
|
||||
|
||||
testUsingContext('throws tool exit with uncommitted changes', () async {
|
||||
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(channel: 'dev');
|
||||
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(channel: 'beta');
|
||||
const String upstreamRevision = '';
|
||||
final FakeFlutterVersion latestVersion = FakeFlutterVersion(frameworkRevision: upstreamRevision);
|
||||
fakeCommandRunner.remoteVersion = latestVersion;
|
||||
@ -88,10 +88,10 @@ void main() {
|
||||
Platform: () => fakePlatform,
|
||||
});
|
||||
|
||||
testUsingContext("Doesn't continue on known tag, dev branch, no force, already up-to-date", () async {
|
||||
testUsingContext("Doesn't continue on known tag, beta branch, no force, already up-to-date", () async {
|
||||
const String revision = 'abc123';
|
||||
final FakeFlutterVersion latestVersion = FakeFlutterVersion(frameworkRevision: revision);
|
||||
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(channel: 'dev', frameworkRevision: revision);
|
||||
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(channel: 'beta', frameworkRevision: revision);
|
||||
fakeCommandRunner.alreadyUpToDate = true;
|
||||
fakeCommandRunner.remoteVersion = latestVersion;
|
||||
|
||||
@ -118,7 +118,7 @@ void main() {
|
||||
const String upstreamVersion = '4.5.6';
|
||||
|
||||
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(
|
||||
channel: 'dev',
|
||||
channel: 'beta',
|
||||
frameworkRevision: revision,
|
||||
frameworkRevisionShort: revision,
|
||||
frameworkVersion: version,
|
||||
@ -246,7 +246,7 @@ void main() {
|
||||
|
||||
testUsingContext('throws toolExit if repository url is null', () async {
|
||||
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(
|
||||
channel: 'dev',
|
||||
channel: 'beta',
|
||||
repositoryUrl: null,
|
||||
);
|
||||
|
||||
@ -265,7 +265,7 @@ void main() {
|
||||
|
||||
testUsingContext('does not throw toolExit at standard remote url with FLUTTER_GIT_URL unset', () async {
|
||||
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(
|
||||
channel: 'dev',
|
||||
channel: 'beta',
|
||||
);
|
||||
expect(() => realCommandRunner.verifyStandardRemote(flutterVersion), returnsNormally);
|
||||
expect(processManager, hasNoRemainingExpectations);
|
||||
@ -276,7 +276,7 @@ void main() {
|
||||
|
||||
testUsingContext('throws toolExit at non-standard remote url with FLUTTER_GIT_URL unset', () async {
|
||||
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(
|
||||
channel: 'dev',
|
||||
channel: 'beta',
|
||||
repositoryUrl: flutterNonStandardUrlDotGit,
|
||||
);
|
||||
|
||||
@ -300,7 +300,7 @@ void main() {
|
||||
|
||||
testUsingContext('does not throw toolExit at non-standard remote url with FLUTTER_GIT_URL set', () async {
|
||||
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(
|
||||
channel: 'dev',
|
||||
channel: 'beta',
|
||||
repositoryUrl: flutterNonStandardUrlDotGit,
|
||||
);
|
||||
|
||||
@ -315,7 +315,7 @@ void main() {
|
||||
|
||||
testUsingContext('throws toolExit at remote url and FLUTTER_GIT_URL set to different urls', () async {
|
||||
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(
|
||||
channel: 'dev',
|
||||
channel: 'beta',
|
||||
repositoryUrl: flutterNonStandardUrlDotGit,
|
||||
);
|
||||
|
||||
@ -342,7 +342,7 @@ void main() {
|
||||
|
||||
testUsingContext('exempts standard ssh url from check with FLUTTER_GIT_URL unset', () async {
|
||||
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(
|
||||
channel: 'dev',
|
||||
channel: 'beta',
|
||||
repositoryUrl: flutterStandardSshUrl,
|
||||
);
|
||||
|
||||
@ -413,7 +413,7 @@ void main() {
|
||||
const String upstreamVersion = '4.5.6';
|
||||
|
||||
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(
|
||||
channel: 'dev',
|
||||
channel: 'beta',
|
||||
frameworkRevision: revision,
|
||||
frameworkVersion: version,
|
||||
);
|
||||
@ -474,7 +474,7 @@ void main() {
|
||||
|
||||
testUsingContext('does not throw on unknown tag, official branch, force', () async {
|
||||
fakeCommandRunner.remoteVersion = FakeFlutterVersion(frameworkRevision: null);
|
||||
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(channel: 'dev');
|
||||
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(channel: 'beta');
|
||||
|
||||
final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
|
||||
force: true,
|
||||
@ -492,7 +492,7 @@ void main() {
|
||||
});
|
||||
|
||||
testUsingContext('does not throw tool exit with uncommitted changes and force', () async {
|
||||
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(channel: 'dev');
|
||||
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(channel: 'beta');
|
||||
fakeCommandRunner.remoteVersion = FakeFlutterVersion(frameworkRevision: null);
|
||||
fakeCommandRunner.willHaveUncommittedChanges = true;
|
||||
|
||||
@ -511,8 +511,8 @@ void main() {
|
||||
Platform: () => fakePlatform,
|
||||
});
|
||||
|
||||
testUsingContext("Doesn't throw on known tag, dev branch, no force", () async {
|
||||
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(channel: 'dev');
|
||||
testUsingContext("Doesn't throw on known tag, beta branch, no force", () async {
|
||||
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(channel: 'beta');
|
||||
fakeCommandRunner.remoteVersion = FakeFlutterVersion(frameworkRevision: null);
|
||||
|
||||
final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
|
||||
|
@ -65,7 +65,6 @@ void main() {
|
||||
command: <String>['git', 'branch', '-r'],
|
||||
stdout: 'origin/beta\n'
|
||||
'origin/master\n'
|
||||
'origin/dev\n'
|
||||
'origin/stable\n',
|
||||
),
|
||||
);
|
||||
@ -89,7 +88,6 @@ void main() {
|
||||
stdout: 'origin/beta\n'
|
||||
'origin/master\n'
|
||||
'origin/dependabot/bundler\n'
|
||||
'origin/dev\n'
|
||||
'origin/v1.4.5-hotfixes\n'
|
||||
'origin/stable\n',
|
||||
),
|
||||
@ -142,10 +140,8 @@ void main() {
|
||||
fakeProcessManager.addCommand(
|
||||
const FakeCommand(
|
||||
command: <String>['git', 'branch', '-r'],
|
||||
stdout: 'origin/dev\n'
|
||||
'origin/beta\n'
|
||||
stdout: 'origin/beta\n'
|
||||
'origin/stable\n'
|
||||
'upstream/dev\n'
|
||||
'upstream/beta\n'
|
||||
'upstream/stable\n',
|
||||
),
|
||||
@ -165,7 +161,7 @@ void main() {
|
||||
.where((String line) => line?.isNotEmpty == true)
|
||||
.skip(1); // remove `Flutter channels:` line
|
||||
|
||||
expect(rows, <String>['dev', 'beta', 'stable', 'Currently not on an official channel.']);
|
||||
expect(rows, <String>['beta', 'stable', 'Currently not on an official channel.']);
|
||||
}, overrides: <Type, Generator>{
|
||||
ProcessManager: () => fakeProcessManager,
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
|
@ -63,6 +63,29 @@ void main() {
|
||||
expect(config.keys, isNot(contains('foo')));
|
||||
});
|
||||
|
||||
testWithoutContext('Config does not error on a file with a deprecated field', () {
|
||||
final BufferLogger bufferLogger = BufferLogger.test();
|
||||
final File file = memoryFileSystem.file('.flutter_example')
|
||||
..writeAsStringSync('''
|
||||
{
|
||||
"is-bot": false,
|
||||
"license-hash": "3e8c85e63b26ce223cda96a9a8fbb410",
|
||||
"redisplay-welcome-message": true,
|
||||
"last-devtools-activation-time": "2021-10-04 16:03:19.832823",
|
||||
"last-active-stable-version": "b22742018b3edf16c6cadd7b76d9db5e7f9064b5"
|
||||
}
|
||||
''');
|
||||
config = Config(
|
||||
'example',
|
||||
fileSystem: memoryFileSystem,
|
||||
logger: bufferLogger,
|
||||
platform: fakePlatform,
|
||||
);
|
||||
|
||||
expect(file.existsSync(), isTrue);
|
||||
expect(bufferLogger.errorText, isEmpty);
|
||||
});
|
||||
|
||||
testWithoutContext('Config parse error', () {
|
||||
final BufferLogger bufferLogger = BufferLogger.test();
|
||||
final File file = memoryFileSystem.file('.flutter_example')
|
||||
|
@ -43,7 +43,6 @@ void main() {
|
||||
);
|
||||
|
||||
state1.updateLastActiveVersion('abc', Channel.master);
|
||||
state1.updateLastActiveVersion('def', Channel.dev);
|
||||
state1.updateLastActiveVersion('ghi', Channel.beta);
|
||||
state1.updateLastActiveVersion('jkl', Channel.stable);
|
||||
|
||||
@ -53,7 +52,6 @@ void main() {
|
||||
);
|
||||
|
||||
expect(state2.lastActiveVersion(Channel.master), 'abc');
|
||||
expect(state2.lastActiveVersion(Channel.dev), 'def');
|
||||
expect(state2.lastActiveVersion(Channel.beta), 'ghi');
|
||||
expect(state2.lastActiveVersion(Channel.stable), 'jkl');
|
||||
});
|
||||
|
@ -14,7 +14,7 @@ import '../src/common.dart';
|
||||
import 'test_utils.dart';
|
||||
|
||||
const String _kInitialVersion = 'v1.9.1';
|
||||
const String _kBranch = 'dev';
|
||||
const String _kBranch = 'beta';
|
||||
|
||||
final Stdio stdio = Stdio();
|
||||
final ProcessUtils processUtils = ProcessUtils(processManager: processManager, logger: StdoutLogger(
|
||||
|
Loading…
Reference in New Issue
Block a user