New release process (#14061)
Generate the "version" file from git tags.
Remove the old VERSION file and mentions of versions in pubspec.yaml files.
Replace the old update_versions.dart script with a new roll_dev.dart script.
Update "flutter channel".
Update "flutter upgrade", including making it transition from alpha to dev.
Update "flutter --version" and "flutter doctor".
diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart
index d20863c..c3e3835 100644
--- a/packages/flutter_tools/lib/executable.dart
+++ b/packages/flutter_tools/lib/executable.dart
@@ -44,7 +44,7 @@
await runner.run(args, <FlutterCommand>[
new AnalyzeCommand(verboseHelp: verboseHelp),
new BuildCommand(verboseHelp: verboseHelp),
- new ChannelCommand(),
+ new ChannelCommand(verboseHelp: verboseHelp),
new CleanCommand(),
new InjectPluginsCommand(hidden: !verboseHelp),
new ConfigCommand(verboseHelp: verboseHelp),
diff --git a/packages/flutter_tools/lib/src/commands/channel.dart b/packages/flutter_tools/lib/src/commands/channel.dart
index 9b1ec42..4c1367d 100644
--- a/packages/flutter_tools/lib/src/commands/channel.dart
+++ b/packages/flutter_tools/lib/src/commands/channel.dart
@@ -9,8 +9,19 @@
import '../cache.dart';
import '../globals.dart';
import '../runner/flutter_command.dart';
+import '../version.dart';
class ChannelCommand extends FlutterCommand {
+ ChannelCommand({ bool verboseHelp: false }) {
+ argParser.addFlag(
+ 'all',
+ abbr: 'a',
+ help: 'Include all the available branches (including local branches) when listing channels.',
+ defaultsTo: false,
+ hide: !verboseHelp,
+ );
+ }
+
@override
final String name = 'channel';
@@ -24,7 +35,7 @@
Future<Null> runCommand() {
switch (argResults.rest.length) {
case 0:
- return _listChannels();
+ return _listChannels(showAll: argResults['all']);
case 1:
return _switchChannel(argResults.rest[0]);
default:
@@ -32,10 +43,12 @@
}
}
- Future<Null> _listChannels() async {
- final String currentBranch = runSync(
- <String>['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
- workingDirectory: Cache.flutterRoot);
+ Future<Null> _listChannels({ bool showAll }) async {
+ // Beware: currentBranch could contain PII. See getBranchName().
+ final String currentChannel = FlutterVersion.instance.channel;
+ final String currentBranch = FlutterVersion.instance.getBranchName();
+
+ showAll = showAll || currentChannel != currentBranch;
printStatus('Flutter channels:');
final int result = await runCommandAndStreamOutput(
@@ -46,24 +59,45 @@
if (split.length < 2)
return null;
final String branchName = split[1];
- if (branchName.startsWith('HEAD'))
- return null;
if (branchName == currentBranch)
return '* $branchName';
- return ' $branchName';
+ if (!branchName.startsWith('HEAD ') &&
+ (showAll || FlutterVersion.officialChannels.contains(branchName)))
+ return ' $branchName';
+ return null;
},
);
if (result != 0)
throwToolExit('List channels failed: $result', exitCode: result);
}
- Future<Null> _switchChannel(String branchName) async {
- printStatus('Switching to flutter channel named $branchName');
+ Future<Null> _switchChannel(String branchName) {
+ printStatus("Switching to flutter channel '$branchName'...");
+ if (FlutterVersion.obsoleteBranches.containsKey(branchName)) {
+ final String alternative = FlutterVersion.obsoleteBranches[branchName];
+ printStatus("This channel is obsolete. Consider switching to the '$alternative' channel instead.");
+ } else if (!FlutterVersion.officialChannels.contains(branchName)) {
+ printStatus('This is not an official channel. For a list of available channels, try "flutter channel".');
+ }
+ return _checkout(branchName);
+ }
+
+ static Future<Null> upgradeChannel() async {
+ final String channel = FlutterVersion.instance.channel;
+ if (FlutterVersion.obsoleteBranches.containsKey(channel)) {
+ final String alternative = FlutterVersion.obsoleteBranches[channel];
+ printStatus("Transitioning from '$channel' to '$alternative'...");
+ return _checkout(alternative);
+ }
+ }
+
+ static Future<Null> _checkout(String branchName) async {
final int result = await runCommandAndStreamOutput(
<String>['git', 'checkout', branchName],
workingDirectory: Cache.flutterRoot,
+ prefix: 'git: ',
);
if (result != 0)
- throwToolExit('Switch channel failed: $result', exitCode: result);
+ throwToolExit('Switching channels failed with error code $result.', exitCode: result);
}
}
diff --git a/packages/flutter_tools/lib/src/commands/upgrade.dart b/packages/flutter_tools/lib/src/commands/upgrade.dart
index 77ac728..bce432b 100644
--- a/packages/flutter_tools/lib/src/commands/upgrade.dart
+++ b/packages/flutter_tools/lib/src/commands/upgrade.dart
@@ -14,6 +14,7 @@
import '../globals.dart';
import '../runner/flutter_command.dart';
import '../version.dart';
+import 'channel.dart';
class UpgradeCommand extends FlutterCommand {
@override
@@ -39,6 +40,8 @@
printStatus('Upgrading Flutter from ${Cache.flutterRoot}...');
+ await ChannelCommand.upgradeChannel();
+
int code = await runCommandAndStreamOutput(
<String>['git', 'pull', '--ff-only'],
workingDirectory: Cache.flutterRoot,
diff --git a/packages/flutter_tools/lib/src/doctor.dart b/packages/flutter_tools/lib/src/doctor.dart
index dfaabdc..8af7acc 100644
--- a/packages/flutter_tools/lib/src/doctor.dart
+++ b/packages/flutter_tools/lib/src/doctor.dart
@@ -198,7 +198,7 @@
final FlutterVersion version = FlutterVersion.instance;
- messages.add(new ValidationMessage('Flutter at ${Cache.flutterRoot}'));
+ messages.add(new ValidationMessage('Flutter version ${version.frameworkVersion} at ${Cache.flutterRoot}'));
if (Cache.flutterRoot.contains(' '))
messages.add(new ValidationMessage.error(
'Flutter SDK install paths with spaces are not yet supported. (https://github.com/flutter/flutter/issues/6577)\n'
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
index 0c7a455..8476617 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
@@ -247,6 +247,7 @@
flutterUsage.suppressAnalytics = true;
_checkFlutterCopy();
+ await FlutterVersion.instance.ensureVersionFile();
await FlutterVersion.instance.checkFlutterVersionFreshness();
if (globalResults.wasParsed('packages'))
diff --git a/packages/flutter_tools/lib/src/usage.dart b/packages/flutter_tools/lib/src/usage.dart
index abdb3d6..e1efafb 100644
--- a/packages/flutter_tools/lib/src/usage.dart
+++ b/packages/flutter_tools/lib/src/usage.dart
@@ -24,7 +24,7 @@
/// used for testing.
Usage({ String settingsName: 'flutter', String versionOverride, String configDirOverride}) {
final FlutterVersion flutterVersion = FlutterVersion.instance;
- final String version = versionOverride ?? flutterVersion.getVersionString(whitelistBranchName: true);
+ final String version = versionOverride ?? flutterVersion.getVersionString(redactUnknownBranches: true);
_analytics = new AnalyticsIO(_kFlutterUA, settingsName, version,
// Analyzer doesn't recognize that [Directory] objects match up due to a
// conditional import.
@@ -34,7 +34,7 @@
// Report a more detailed OS version string than package:usage does by default.
_analytics.setSessionValue('cd1', os.name);
// Send the branch name as the "channel".
- _analytics.setSessionValue('cd2', flutterVersion.getBranchName(whitelistBranchName: true));
+ _analytics.setSessionValue('cd2', flutterVersion.getBranchName(redactUnknownBranches: true));
// Record the host as the application installer ID - the context that flutter_tools is running in.
if (platform.environment.containsKey('FLUTTER_HOST')) {
_analytics.setSessionValue('aiid', platform.environment['FLUTTER_HOST']);
diff --git a/packages/flutter_tools/lib/src/version.dart b/packages/flutter_tools/lib/src/version.dart
index d8e752e..2051932 100644
--- a/packages/flutter_tools/lib/src/version.dart
+++ b/packages/flutter_tools/lib/src/version.dart
@@ -10,20 +10,13 @@
import 'base/common.dart';
import 'base/context.dart';
+import 'base/file_system.dart';
import 'base/io.dart';
import 'base/process.dart';
import 'base/process_manager.dart';
import 'cache.dart';
import 'globals.dart';
-final Set<String> kKnownBranchNames = new Set<String>.from(<String>[
- 'master',
- 'alpha',
- 'hackathon',
- 'codelab',
- 'beta'
-]);
-
class FlutterVersion {
@visibleForTesting
FlutterVersion(this._clock) {
@@ -42,6 +35,7 @@
_frameworkRevision = _runGit('git log -n 1 --pretty=format:%H');
_frameworkAge = _runGit('git log -n 1 --pretty=format:%ar');
+ _frameworkVersion = GitTagVersion.determine().frameworkVersionFor(_frameworkRevision);
}
final Clock _clock;
@@ -49,11 +43,31 @@
String _repositoryUrl;
String get repositoryUrl => _repositoryUrl;
+ static Set<String> officialChannels = new Set<String>.from(<String>[
+ 'master',
+ 'dev',
+ 'beta',
+ 'release',
+ ]);
+
+ /// This maps old branch names to the names of branches that replaced them.
+ ///
+ /// For example, in early 2018 we changed from having an "alpha" branch to
+ /// having a "dev" branch, so anyone using "alpha" now gets transitioned to
+ /// "dev".
+ static Map<String, String> obsoleteBranches = <String, String>{
+ 'alpha': 'dev',
+ 'hackathon': 'dev',
+ 'codelab': 'dev',
+ };
+
String _channel;
- /// `master`, `alpha`, `hackathon`, ...
+ /// The channel is the upstream branch.
+ /// `master`, `dev`, `beta`, `release`; or old ones, like `alpha`, `hackathon`, ...
String get channel => _channel;
- /// The name of the local branch
+ /// The name of the local branch.
+ /// Use getBranchName() to read this.
String _branch;
String _frameworkRevision;
@@ -63,6 +77,9 @@
String _frameworkAge;
String get frameworkAge => _frameworkAge;
+ String _frameworkVersion;
+ String get frameworkVersion => _frameworkVersion;
+
String get frameworkDate => frameworkCommitDate;
String get dartSdkVersion => Cache.instance.dartSdkVersion.split(' ')[0];
@@ -71,16 +88,19 @@
String get engineRevision => Cache.instance.engineRevision;
String get engineRevisionShort => _shortGitRevision(engineRevision);
- String _runGit(String command) => runSync(command.split(' '), workingDirectory: Cache.flutterRoot);
+ Future<Null> ensureVersionFile() {
+ return fs.file(fs.path.join(Cache.flutterRoot, 'version')).writeAsString(_frameworkVersion);
+ }
@override
String toString() {
- final String flutterText = 'Flutter • channel $channel • ${repositoryUrl == null ? 'unknown source' : repositoryUrl}';
+ final String versionText = frameworkVersion == 'unknown' ? '' : ' $frameworkVersion';
+ final String flutterText = 'Flutter$versionText • channel $channel • ${repositoryUrl == null ? 'unknown source' : repositoryUrl}';
final String frameworkText = 'Framework • revision $frameworkRevisionShort ($frameworkAge) • $frameworkCommitDate';
final String engineText = 'Engine • revision $engineRevisionShort';
final String toolsText = 'Tools • Dart $dartSdkVersion';
- // Flutter • channel master • https://github.com/flutter/flutter.git
+ // Flutter 1.3.922-pre.2 • channel master • https://github.com/flutter/flutter.git
// Framework • revision 2259c59be8 • 19 minutes ago • 2016-08-15 22:51:40
// Engine • revision fe509b0d96
// Tools • Dart 1.19.0-dev.5.0
@@ -150,20 +170,22 @@
static FlutterVersion get instance => context.putIfAbsent(FlutterVersion, () => new FlutterVersion(const Clock()));
- /// Return a short string for the version (`alpha/a76bc8e22b`).
- String getVersionString({bool whitelistBranchName: false}) {
- return '${getBranchName(whitelistBranchName: whitelistBranchName)}/$frameworkRevisionShort';
+ /// Return a short string for the version (e.g. `master/0.0.59-pre.92`, `scroll_refactor/a76bc8e22b`).
+ String getVersionString({bool redactUnknownBranches: false}) {
+ if (frameworkVersion != 'unknown')
+ return '${getBranchName(redactUnknownBranches: redactUnknownBranches)}/$frameworkVersion';
+ return '${getBranchName(redactUnknownBranches: redactUnknownBranches)}/$frameworkRevisionShort';
}
/// Return the branch name.
///
- /// If [whitelistBranchName] is true and the branch is unknown,
- /// the branch name will be returned as 'dev'.
- String getBranchName({ bool whitelistBranchName: false }) {
- if (whitelistBranchName || _branch.isEmpty) {
+ /// If [redactUnknownBranches] is true and the branch is unknown,
+ /// the branch name will be returned as `'[user-branch]'`.
+ String getBranchName({ bool redactUnknownBranches: false }) {
+ if (redactUnknownBranches || _branch.isEmpty) {
// Only return the branch names we know about; arbitrary branch names might contain PII.
- if (!kKnownBranchNames.contains(_branch))
- return 'dev';
+ if (!officialChannels.contains(_branch) && !obsoleteBranches.containsKey(_branch))
+ return '[user-branch]';
}
return _branch;
}
@@ -424,6 +446,10 @@
return '';
}
+String _runGit(String command) {
+ return runSync(command.split(' '), workingDirectory: Cache.flutterRoot);
+}
+
/// Runs [command] in the root of the Flutter installation and returns the
/// standard output as a string.
///
@@ -445,3 +471,45 @@
return '';
return revision.length > 10 ? revision.substring(0, 10) : revision;
}
+
+class GitTagVersion {
+ const GitTagVersion(this.x, this.y, this.z, this.commits, this.hash);
+ const GitTagVersion.unknown() : x = null, y = null, z = null, commits = 0, hash = '';
+
+ /// The X in vX.Y.Z.
+ final int x;
+
+ /// The Y in vX.Y.Z.
+ final int y;
+
+ /// The Z in vX.Y.Z.
+ final int z;
+
+ /// Number of commits since the vX.Y.Z tag.
+ final int commits;
+
+ /// The git hash (or an abbreviation thereof) for this commit.
+ final String hash;
+
+ static GitTagVersion determine() {
+ final String version = _runGit('git describe --match v*.*.* --exclude *-* --first-parent --long --tags');
+ final RegExp versionPattern = new RegExp('^v([0-9]+)\.([0-9]+)\.([0-9]+)-([0-9]+)-g([a-f0-9]+)\$');
+ final List<String> parts = versionPattern.matchAsPrefix(version)?.groups(<int>[1, 2, 3, 4, 5]);
+ if (parts == null) {
+ printTrace('Could not interpret results of "git describe": $version');
+ return const GitTagVersion.unknown();
+ }
+ final List<int> parsedParts = parts.take(4).map<int>(
+ (String value) => int.parse(value, onError: (String value) => null),
+ ).toList();
+ return new GitTagVersion(parsedParts[0], parsedParts[1], parsedParts[2], parsedParts[3], parts[4]);
+ }
+
+ String frameworkVersionFor(String revision) {
+ if (x == null || y == null || z == null || !revision.startsWith(hash))
+ return 'unknown';
+ if (commits == 0)
+ return '$x.$y.$z';
+ return '$x.$y.${z + 1}-pre.$commits';
+ }
+}