| // Copyright 2019 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. |
| |
| import 'dart:async'; |
| import 'dart:io'; |
| |
| import 'package:args/args.dart'; |
| |
| const String cloudbuildDirectory = 'cloud_build'; |
| const String workspaceDirectory = '../'; |
| |
| const String gcloudProjectIdFlag = 'project'; |
| const String gcloudProjectIdAbbrFlag = 'p'; |
| |
| const String gcloudProjectVersionFlag = 'version'; |
| const String gcloudProjectVersionAbbrFlag = 'v'; |
| |
| const String ignoreVersionFlag = 'ignore-version-check'; |
| const String helpFlag = 'help'; |
| |
| String? _gcloudProjectId; |
| String? _gcloudProjectVersion; |
| |
| late bool _ignoreVersion; |
| |
| /// Check if [gcloudProjectIdFlag] and [gcloudProjectVersionFlag] |
| /// were passed as arguments. If they were, also set [_gcloudProjectId] |
| /// and [_gcloudProjectVersion] accordingly. |
| bool _getArgs(ArgParser argParser, List<String> arguments) { |
| final ArgResults args = argParser.parse(arguments); |
| |
| final bool printHelpMessage = args[helpFlag] as bool; |
| if (printHelpMessage) { |
| return false; |
| } |
| |
| _gcloudProjectId = args[gcloudProjectIdFlag] as String?; |
| _gcloudProjectVersion = args[gcloudProjectVersionFlag] as String?; |
| _ignoreVersion = args[ignoreVersionFlag] as bool; |
| |
| if (_gcloudProjectId == null) { |
| stderr.write('--$gcloudProjectIdFlag must be defined\n'); |
| return false; |
| } |
| |
| if (_gcloudProjectVersion == null) { |
| stderr.write('--$gcloudProjectVersionFlag must be defined\n'); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /// Check the Flutter version installed and make sure it is a recent version |
| /// from the past 21 days. |
| /// |
| /// Flutter tools handles the rest of the checks (e.g. Dart version) when |
| /// building the project. |
| Future<bool> _checkDependencies() async { |
| if (_ignoreVersion) { |
| return true; |
| } |
| |
| stdout.writeln('Checking Flutter version via flutter --version'); |
| final ProcessResult result = await Process.run('flutter', <String>['--version']); |
| final String flutterVersionOutput = result.stdout as String; |
| |
| // This makes an assumption that only the framework will have its version |
| // printed out with the date in YYYY-MM-DD format. |
| final RegExp dateRegExp = RegExp(r'([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))'); |
| final String flutterVersionDateRaw = dateRegExp.allMatches(flutterVersionOutput).first.group(0)!; |
| |
| final DateTime flutterVersionDate = DateTime.parse(flutterVersionDateRaw); |
| final DateTime now = DateTime.now(); |
| final Duration lastUpdateToFlutter = now.difference(flutterVersionDate); |
| |
| return lastUpdateToFlutter.inDays < 21; |
| } |
| |
| /// Run the Google Cloud CLI tool to deploy to [_gcloudProjectId] under |
| /// version [_gcloudProjectVersion]. |
| Future<bool> _deployToAppEngine() async { |
| stdout.writeln('Deploying to AppEngine'); |
| |
| /// The Google Cloud deployment command is an interactive process. It will |
| /// print out what it is about to do, and ask for confirmation (Y/n). |
| final Process process = await Process.start( |
| 'gcloud', |
| <String>[ |
| 'app', |
| 'deploy', |
| '--project', |
| _gcloudProjectId!, |
| '--version', |
| _gcloudProjectVersion!, |
| '--no-promote', |
| '--no-stop-previous-version', |
| '--quiet', |
| ], |
| ); |
| |
| /// Let this user confirm the details before Google Cloud sends for deployment. |
| unawaited(stdin.pipe(process.stdin)); |
| |
| await process.stderr.pipe(stderr); |
| await process.stdout.pipe(stdout); |
| |
| return await process.exitCode == 0; |
| } |
| |
| /// Run [args] in bash shell and validate it finshes with exit code 0. |
| Future<void> shellCommand(List<String> args) async { |
| final ProcessResult result = await Process.run( |
| 'bash', |
| args, |
| workingDirectory: workspaceDirectory, |
| ); |
| |
| if (result.exitCode != 0) { |
| print('$args failed with exit code ${result.exitCode}'); |
| print('stdout: ${result.stdout}'); |
| print('stderr: ${result.stderr}'); |
| exit(1); |
| } |
| } |
| |
| Future<void> main(List<String> arguments) async { |
| final ArgParser argParser = ArgParser() |
| ..addOption(gcloudProjectIdFlag, abbr: gcloudProjectIdAbbrFlag) |
| ..addOption(gcloudProjectVersionFlag, abbr: gcloudProjectVersionAbbrFlag) |
| ..addFlag(ignoreVersionFlag) |
| ..addFlag(helpFlag); |
| |
| if (!_getArgs(argParser, arguments)) { |
| stdout.write('Required flags:\n' |
| '--$gcloudProjectIdFlag gcp-id\n' |
| '--$gcloudProjectVersionFlag version\n\n' |
| 'Optional flags:\n' |
| '--$ignoreVersionFlag\tForce deploy with current Flutter version\n'); |
| exit(1); |
| } |
| |
| if (!await _checkDependencies()) { |
| stderr.writeln('Update Flutter to a version on master from the past 3 weeks to deploy Cocoon'); |
| exit(1); |
| } |
| |
| await shellCommand(<String>['$cloudbuildDirectory/dashboard_build.sh']); |
| |
| if (!await _deployToAppEngine()) { |
| stderr.writeln('Failed to deploy to AppEngine'); |
| exit(1); |
| } |
| } |