version.dart 7.2 KB
Newer Older
1 2 3 4
// 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.

5 6
import './globals.dart' show ConductorException;

7 8 9 10 11 12
/// Possible string formats that `flutter --version` can return.
enum VersionType {
  /// A stable flutter release.
  ///
  /// Example: '1.2.3'
  stable,
13

14 15 16 17
  /// A pre-stable flutter release.
  ///
  /// Example: '1.2.3-4.5.pre'
  development,
18

19 20 21 22 23 24
  /// A master channel flutter version.
  ///
  /// Example: '1.2.3-4.0.pre.10'
  ///
  /// The last number is the number of commits past the last tagged version.
  latest,
25 26 27 28 29

  /// A master channel flutter version from git describe.
  ///
  /// Example: '1.2.3-4.0.pre-10-gabc123'.
  gitDescribe,
30 31 32 33 34 35
}

final Map<VersionType, RegExp> versionPatterns = <VersionType, RegExp>{
  VersionType.stable: RegExp(r'^(\d+)\.(\d+)\.(\d+)$'),
  VersionType.development: RegExp(r'^(\d+)\.(\d+)\.(\d+)-(\d+)\.(\d+)\.pre$'),
  VersionType.latest: RegExp(r'^(\d+)\.(\d+)\.(\d+)-(\d+)\.(\d+)\.pre\.(\d+)$'),
36
  VersionType.gitDescribe: RegExp(r'^(\d+)\.(\d+)\.(\d+)-(\d+)\.(\d+)\.pre-(\d+)-g[a-f0-9]+$'),
37 38 39 40
};

class Version {
  Version({
41 42 43
    required this.x,
    required this.y,
    required this.z,
44 45 46
    this.m,
    this.n,
    this.commits,
47
    required this.type,
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
  }) {
    switch (type) {
      case VersionType.stable:
        assert(m == null);
        assert(n == null);
        assert(commits == null);
        break;
      case VersionType.development:
        assert(m != null);
        assert(n != null);
        assert(commits == null);
        break;
      case VersionType.latest:
        assert(m != null);
        assert(n != null);
        assert(commits != null);
        break;
65 66 67 68
      case VersionType.gitDescribe:
        throw ConductorException(
          'VersionType.gitDescribe not supported! Use VersionType.latest instead.',
        );
69 70 71 72 73 74 75 76 77 78 79 80 81
    }
  }

  /// Create a new [Version] from a version string.
  ///
  /// It is expected that [versionString] will be generated by
  /// `flutter --version` and match one of `stablePattern`, `developmentPattern`
  /// and `latestPattern`.
  factory Version.fromString(String versionString) {
    assert(versionString != null);

    versionString = versionString.trim();
    // stable tag
82
    Match? match = versionPatterns[VersionType.stable]!.firstMatch(versionString);
83 84
    if (match != null) {
      // parse stable
85 86 87 88
      final List<int> parts = match
          .groups(<int>[1, 2, 3])
          .map((String? s) => int.parse(s!))
          .toList();
89 90 91 92 93 94 95 96
      return Version(
        x: parts[0],
        y: parts[1],
        z: parts[2],
        type: VersionType.stable,
      );
    }
    // development tag
97
    match = versionPatterns[VersionType.development]!.firstMatch(versionString);
98 99 100
    if (match != null) {
      // parse development
      final List<int> parts =
101
          match.groups(<int>[1, 2, 3, 4, 5]).map((String? s) => int.parse(s!)).toList();
102 103 104 105 106 107 108 109 110 111
      return Version(
        x: parts[0],
        y: parts[1],
        z: parts[2],
        m: parts[3],
        n: parts[4],
        type: VersionType.development,
      );
    }
    // latest tag
112
    match = versionPatterns[VersionType.latest]!.firstMatch(versionString);
113 114
    if (match != null) {
      // parse latest
115 116 117 118 119
      final List<int> parts = match.groups(
        <int>[1, 2, 3, 4, 5, 6],
      ).map(
        (String? s) => int.parse(s!),
      ).toList();
120 121 122 123 124 125 126 127 128 129
      return Version(
        x: parts[0],
        y: parts[1],
        z: parts[2],
        m: parts[3],
        n: parts[4],
        commits: parts[5],
        type: VersionType.latest,
      );
    }
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
    match = versionPatterns[VersionType.gitDescribe]!.firstMatch(versionString);
    if (match != null) {
      // parse latest
      final List<int> parts = match.groups(
        <int>[1, 2, 3, 4, 5, 6],
      ).map(
        (String? s) => int.parse(s!),
      ).toList();
      return Version(
        x: parts[0],
        y: parts[1],
        z: parts[2],
        m: parts[3],
        n: parts[4],
        commits: parts[5],
        type: VersionType.latest,
      );
    }
148 149 150 151 152 153 154 155
    throw Exception('${versionString.trim()} cannot be parsed');
  }

  // Returns a new version with the given [increment] part incremented.
  // NOTE new version must be of same type as previousVersion.
  factory Version.increment(
    Version previousVersion,
    String increment, {
156
    VersionType? nextVersionType,
157 158 159 160
  }) {
    final int nextX = previousVersion.x;
    int nextY = previousVersion.y;
    int nextZ = previousVersion.z;
161 162
    int? nextM = previousVersion.m;
    int? nextN = previousVersion.n;
163
    if (nextVersionType == null) {
164
      if (previousVersion.type == VersionType.latest || previousVersion.type == VersionType.gitDescribe) {
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
        nextVersionType = VersionType.development;
      } else {
        nextVersionType = previousVersion.type;
      }
    }

    switch (increment) {
      case 'x':
        // This was probably a mistake.
        throw Exception('Incrementing x is not supported by this tool.');
      case 'y':
        // Dev release following a beta release.
        nextY += 1;
        nextZ = 0;
        if (previousVersion.type != VersionType.stable) {
          nextM = 0;
          nextN = 0;
        }
        break;
      case 'z':
        // Hotfix to stable release.
        assert(previousVersion.type == VersionType.stable);
        nextZ += 1;
        break;
      case 'm':
190
        assert(false, "Do not increment 'm' via Version.increment, use instead Version.fromCandidateBranch()");
191 192 193
        break;
      case 'n':
        // Hotfix to internal roll.
194
        nextN = nextN! + 1;
195 196 197 198 199 200 201 202 203 204 205 206 207 208
        break;
      default:
        throw Exception('Unknown increment level $increment.');
    }
    return Version(
      x: nextX,
      y: nextY,
      z: nextZ,
      m: nextM,
      n: nextN,
      type: nextVersionType,
    );
  }

209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
  factory Version.fromCandidateBranch(String branchName) {
    // Regular dev release.
    final RegExp pattern = RegExp(r'flutter-(\d+)\.(\d+)-candidate.(\d+)');
    final RegExpMatch? match = pattern.firstMatch(branchName);
    late final int x;
    late final int y;
    late final int m;
    try {
      x = int.parse(match!.group(1)!);
      y = int.parse(match.group(2)!);
      m = int.parse(match.group(3)!);
    } on Exception {
      throw ConductorException('branch named $branchName not recognized as a valid candidate branch');
    }

    return Version(
      type: VersionType.development,
      x: x,
      y: y,
      z: 0,
      m: m,
      n: 0,
    );
  }

234 235 236 237 238 239 240 241 242 243
  /// Major version.
  final int x;

  /// Zero-indexed count of beta releases after a major release.
  final int y;

  /// Number of hotfix releases after a stable release.
  final int z;

  /// Zero-indexed count of dev releases after a beta release.
244
  final int? m;
245 246

  /// Number of hotfixes required to make a dev release.
247
  final int? n;
248 249

  /// Number of commits past last tagged dev release.
250
  final int? commits;
251 252 253 254 255 256 257 258 259 260 261 262

  final VersionType type;

  @override
  String toString() {
    switch (type) {
      case VersionType.stable:
        return '$x.$y.$z';
      case VersionType.development:
        return '$x.$y.$z-$m.$n.pre';
      case VersionType.latest:
        return '$x.$y.$z-$m.$n.pre.$commits';
263 264
      case VersionType.gitDescribe:
        return '$x.$y.$z-$m.$n.pre.$commits';
265 266 267
    }
  }
}