Unverified Commit 6b95add2 authored by Christopher Fujino's avatar Christopher Fujino Committed by GitHub

[flutter_tools] auto-migrate dev users to beta (#97028)

parent f6ee9258
......@@ -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.'
),
);
......
// 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(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment