blob: 8f62bccf49a14d3d133fc4387b2e0e3999f0464c [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Updates the version numbers of the Flutter repo.
// Only tested on Linux.
//
// See: https://github.com/flutter/flutter/wiki/Release-process
import 'dart:io';
import 'package:args/args.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path;
const String kIncrement = 'increment';
const String kBrokeSdk = 'broke-sdk';
const String kBrokeFramework = 'broke-framework';
const String kBrokeTest = 'broke-test';
const String kBrokeDriver = 'broke-driver';
const String kMarkRelease = 'release';
const String kHelp = 'help';
const String kYamlVersionPrefix = 'version: ';
const String kDev = '-dev';
enum VersionKind { dev, release }
void main(List<String> args) {
// If we're run from the `tools` dir, set the cwd to the repo root.
if (path.basename(Directory.current.path) == 'tools')
Directory.current = Directory.current.parent.parent;
final ArgParser argParser = new ArgParser();
argParser.addFlag(kIncrement, defaultsTo: false, help: 'Increment all the version numbers. Cannot be specified with --$kMarkRelease or with any --broke-* commands.');
argParser.addFlag(kBrokeSdk, defaultsTo: false, negatable: false, help: 'Increment the Flutter SDK version number to indicate that there has been a breaking change to the SDK (for example, to the command line options).');
argParser.addFlag(kBrokeFramework, defaultsTo: false, negatable: false, help: 'Increment the "flutter" package version number to indicate that there has been a breaking change to the Flutter framework.');
argParser.addFlag(kBrokeTest, defaultsTo: false, negatable: false, help: 'Increment the "flutter_test" package version number to indicate that there has been a breaking change to the test API framework.');
argParser.addFlag(kBrokeDriver, defaultsTo: false, negatable: false, help: 'Increment the "flutter_driver" package version number to indicate that there has been a breaking change to the driver API framework.');
argParser.addFlag(kMarkRelease, defaultsTo: false, help: 'Remove "-dev" from each version number. This is used when releasing. When not present, "-dev" is added to each version number. Cannot be specified with --$kIncrement or with any --broke-* commands.');
argParser.addFlag(kHelp, negatable: false, help: 'Show this help message.');
final ArgResults argResults = argParser.parse(args);
final bool increment = argResults[kIncrement];
final bool brokeSdk = argResults[kBrokeSdk];
final bool brokeFramework = argResults[kBrokeFramework];
final bool brokeTest = argResults[kBrokeTest];
final bool brokeDriver = argResults[kBrokeDriver];
final bool brokeAnything = brokeSdk || brokeFramework || brokeTest || brokeDriver;
final VersionKind level = argResults[kMarkRelease] ? VersionKind.release : VersionKind.dev;
final bool help = argResults[kHelp];
if (help) {
print('update_versions.dart - update version numbers of Flutter packages and SDK');
print(argParser.usage);
exit(0);
}
final bool release = level == VersionKind.release;
if ((brokeAnything && release) || (brokeAnything && increment) || (release && increment)) {
print('You can either increment all the version numbers (--$kIncrement), indicate that some packages have had breaking changes (--broke-*), or switch to release mode (--$kMarkRelease).');
print('You cannot combine these, however.');
exit(1);
}
final RawVersion sdk = new RawVersion('VERSION');
final PubSpecVersion framework = new PubSpecVersion('packages/flutter/pubspec.yaml');
final PubSpecVersion test = new PubSpecVersion('packages/flutter_test/pubspec.yaml');
final PubSpecVersion driver = new PubSpecVersion('packages/flutter_driver/pubspec.yaml');
if (increment || brokeAnything)
sdk.increment(brokeAnything);
sdk.setMode(level);
if (increment || brokeFramework)
framework.increment(brokeFramework);
framework.setMode(level);
if (increment || brokeTest)
test.increment(brokeTest);
test.setMode(level);
if (increment || brokeDriver)
driver.increment(brokeDriver);
driver.setMode(level);
sdk.write();
framework.write();
test.write();
driver.write();
print('Flutter SDK is now at version: $sdk');
print('flutter package is now at version: $framework');
print('flutter_test package is now at version: $test');
print('flutter_driver package is now at version: $driver');
if (release) {
print('\nDuring the tagging step in the instructions, the commands will be:');
print('git tag $sdk');
print('git push upstream $sdk');
}
}
abstract class Version {
Version() {
read();
}
@protected
final List<int> version = <int>[];
@protected
VersionKind level;
@protected
bool dirty = false;
@protected
void read();
void interpret(String value) {
level = value.endsWith(kDev) ? VersionKind.dev : VersionKind.release;
if (level == VersionKind.dev)
value = value.substring(0, value.length - kDev.length);
version.addAll(value.split('.').map<int>(int.parse));
}
void increment(bool breaking) {
assert(version.length == 3);
if (breaking) {
version[1] += 1;
version[2] = 0;
} else {
version[2] += 1;
}
dirty = true;
}
void setMode(VersionKind value) {
if (value != level) {
level = value;
dirty = true;
}
}
void write();
@override
String toString() => version.join('.') + (level == VersionKind.dev ? kDev : '');
}
class PubSpecVersion extends Version {
PubSpecVersion(this.path);
final String path;
@override
void read() {
final List<String> lines = new File(path).readAsLinesSync();
final String versionLine = lines.where((String line) => line.startsWith(kYamlVersionPrefix)).single;
interpret(versionLine.substring(kYamlVersionPrefix.length));
}
@override
void write() {
if (!dirty)
return;
final List<String> lines = new File(path).readAsLinesSync();
for (int index = 0; index < lines.length; index += 1) {
final String line = lines[index];
if (line.startsWith(kYamlVersionPrefix)) {
lines[index] = '$kYamlVersionPrefix$this';
break;
}
}
new File(path).writeAsStringSync(lines.join('\n') + '\n');
}
}
class RawVersion extends Version {
RawVersion(this.path);
final String path;
@override
void read() {
final List<String> lines = new File(path).readAsLinesSync();
interpret(lines.where((String line) => line.isNotEmpty && !line.startsWith('#')).single);
}
@override
void write() {
if (!dirty)
return;
final List<String> lines = new File(path).readAsLinesSync();
for (int index = 0; index < lines.length; index += 1) {
final String line = lines[index];
if (line.isNotEmpty && !line.startsWith('#')) {
lines[index] = '$this';
break;
}
}
new File(path).writeAsStringSync(lines.join('\n') + '\n');
}
}