version.dart 6.2 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import '../base/common.dart';
6
import '../base/io.dart';
7 8 9 10 11
import '../base/os.dart';
import '../base/process.dart';
import '../base/version.dart';
import '../cache.dart';
import '../dart/pub.dart';
12
import '../globals.dart' as globals;
13 14 15
import '../runner/flutter_command.dart';

class VersionCommand extends FlutterCommand {
16
  VersionCommand() : super() {
17 18 19 20
    argParser.addFlag('force',
      abbr: 'f',
      help: 'Force switch to older Flutter versions that do not include a version command',
    );
21 22 23 24 25 26 27
    // Don't use usesPubOption here. That will cause the version command to
    // require a pubspec.yaml file, which it doesn't need.
    argParser.addFlag('pub',
      defaultsTo: true,
      hide: true,
      help: 'Whether to run "flutter pub get" after switching versions.',
    );
28 29
  }

30 31 32
  @override
  bool get deprecated => true;

33 34 35 36 37 38 39 40 41 42 43
  @override
  final String name = 'version';

  @override
  final String description = 'List or switch flutter versions.';

  // The first version of Flutter which includes the flutter version command. Switching to older
  // versions will require the user to manually upgrade.
  Version minSupportedVersion = Version.parse('1.2.1');

  Future<List<String>> getTags() async {
44
    globals.flutterVersion.fetchTagsAndUpdate();
45 46
    RunResult runResult;
    try {
47
      runResult = await processUtils.run(
48
        <String>['git', 'tag', '-l', '*.*.*', '--sort=-creatordate'],
49
        throwOnError: true,
50 51 52 53 54
        workingDirectory: Cache.flutterRoot,
      );
    } on ProcessException catch (error) {
      throwToolExit(
        'Unable to get the tags. '
55
        'This is likely due to an internal git error.'
56 57 58
        '\nError: $error.'
      );
    }
59 60 61 62 63 64 65
    return runResult.toString().split('\n');
  }

  @override
  Future<FlutterCommandResult> runCommand() async {
    final List<String> tags = await getTags();
    if (argResults.rest.isEmpty) {
66
      tags.forEach(globals.printStatus);
67
      return FlutterCommandResult.success();
68
    }
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89

    globals.printStatus(
      '╔══════════════════════════════════════════════════════════════════════════════╗\n'
      '║ Warning: "flutter version" will leave the SDK in a detached HEAD state.      ║\n'
      '║ If you are using the command to return to a previously installed SDK version ║\n'
      '║ consider using the "flutter downgrade" command instead.                      ║\n'
      '╚══════════════════════════════════════════════════════════════════════════════╝\n',
      emphasis: true,
    );
    if (globals.stdio.stdinHasTerminal) {
      globals.terminal.usesTerminalUi = true;
      final String result = await globals.terminal.promptForCharInput(
        <String>['y', 'n'],
        logger: globals.logger,
        prompt: 'Are you sure you want to proceed?'
      );
      if (result == 'n') {
        return FlutterCommandResult.success();
      }
    }

90
    final String version = argResults.rest[0].replaceFirst(RegExp('^v'), '');
91 92 93 94
    final List<String> matchingTags = tags.where((String tag) => tag.contains(version)).toList();
    String matchingTag;
    // TODO(fujino): make this a tool exit and fix tests
    if (matchingTags.isEmpty) {
95
      globals.printError('There is no version: $version');
96 97 98
      matchingTag = version;
    } else {
      matchingTag = matchingTags.first.trim();
99 100 101 102
    }

    // check min supported version
    final Version targetVersion = Version.parse(version);
103 104 105 106
    if (targetVersion == null) {
      throwToolExit('Failed to parse version "$version"');
    }

107 108
    bool withForce = false;
    if (targetVersion < minSupportedVersion) {
109
      if (!boolArg('force')) {
110
        globals.printError(
111 112
          'Version command is not supported in $targetVersion and it is supported since version $minSupportedVersion '
          'which means if you switch to version $minSupportedVersion then you can not use version command. '
113 114 115 116 117 118 119 120
          'If you really want to switch to version $targetVersion, please use `--force` flag: `flutter version --force $targetVersion`.'
        );
        return const FlutterCommandResult(ExitStatus.success);
      }
      withForce = true;
    }

    try {
121
      await processUtils.run(
122
        <String>['git', 'checkout', matchingTag],
123
        throwOnError: true,
124
        workingDirectory: Cache.flutterRoot,
125
      );
126 127
    } on Exception catch (e) {
      throwToolExit('Unable to checkout version branch for version $version: $e');
128 129
    }

130
    globals.printStatus('Switching Flutter to version $matchingTag${withForce ? ' with force' : ''}');
131 132 133 134 135

    // Check for and download any engine and pkg/ updates.
    // We run the 'flutter' shell script re-entrantly here
    // so that it will download the updated Dart and so forth
    // if necessary.
136
    globals.printStatus('Downloading engine...');
137
    int code = await processUtils.stream(<String>[
138
      globals.fs.path.join('bin', 'flutter'),
139 140 141 142 143 144 145 146 147
      '--no-color',
      'precache',
    ], workingDirectory: Cache.flutterRoot, allowReentrantFlutter: true);

    if (code != 0) {
      throwToolExit(null, exitCode: code);
    }

    final String projectRoot = findProjectRoot();
148
    if (projectRoot != null && boolArg('pub')) {
149
      globals.printStatus('');
150
      await pub.get(
151 152 153
        context: PubContext.pubUpgrade,
        directory: projectRoot,
        upgrade: true,
154
        checkLastModified: false,
155
        generateSyntheticPackage: false,
156 157 158 159
      );
    }

    // Run a doctor check in case system requirements have changed.
160 161
    globals.printStatus('');
    globals.printStatus('Running flutter doctor...');
162
    code = await processUtils.stream(
163
      <String>[
164
        globals.fs.path.join('bin', 'flutter'),
165 166 167 168 169 170 171 172 173 174
        'doctor',
      ],
      workingDirectory: Cache.flutterRoot,
      allowReentrantFlutter: true,
    );

    if (code != 0) {
      throwToolExit(null, exitCode: code);
    }

175
    return FlutterCommandResult.success();
176 177
  }
}