globals.dart 6.01 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
import 'package:args/args.dart';
6

7
import 'proto/conductor_state.pb.dart' as pb;
8
import 'repository.dart';
9

10 11
const String gsutilBinary = 'gsutil.py';

12
const String kFrameworkDefaultBranch = 'master';
13
const String kForceFlag = 'force';
14

15 16 17 18 19
const List<String> kBaseReleaseChannels = <String>['stable', 'beta', 'dev'];

const List<String> kReleaseChannels = <String>[...kBaseReleaseChannels, FrameworkRepository.defaultBranch];

const List<String> KReleaseIncrements = <String>['y', 'z', 'm', 'n'];
20

21 22
const String kReleaseDocumentationUrl = 'https://github.com/flutter/flutter/wiki/Flutter-Cherrypick-Process';

23 24
const String kLuciPackagingConsoleLink = 'https://ci.chromium.org/p/flutter/g/packaging/console';

25 26
const String kWebsiteReleasesUrl = 'https://docs.flutter.dev/development/tools/sdk/releases';

27 28 29 30
final RegExp releaseCandidateBranchRegex = RegExp(
  r'flutter-(\d+)\.(\d+)-candidate\.(\d+)',
);

31 32 33 34 35
/// Cast a dynamic to String and trim.
String stdoutToString(dynamic input) {
  final String str = input as String;
  return str.trim();
}
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55

class ConductorException implements Exception {
  ConductorException(this.message);

  final String message;

  @override
  String toString() => 'Exception: $message';
}

bool assertsEnabled() {
  // Verify asserts enabled
  bool assertsEnabled = false;

  assert(() {
    assertsEnabled = true;
    return true;
  }());
  return assertsEnabled;
}
56 57 58 59 60 61 62 63

/// Either return the value from [env] or fall back to [argResults].
///
/// If the key does not exist in either the environment or CLI args, throws a
/// [ConductorException].
///
/// The environment is favored over CLI args since the latter can have a default
/// value, which the environment should be able to override.
64
String? getValueFromEnvOrArgs(
65 66
  String name,
  ArgResults argResults,
67
  Map<String, String> env, {
68 69
  bool allowNull = false,
}) {
70
  final String envName = fromArgToEnvName(name);
71
  if (env[envName] != null) {
72
    return env[envName];
73
  }
74
  final String? argValue = argResults[name] as String?;
75 76 77 78
  if (argValue != null) {
    return argValue;
  }

79 80 81
  if (allowNull) {
    return null;
  }
82 83
  throw ConductorException('Expected either the CLI arg --$name or the environment variable $envName '
      'to be provided!');
84 85
}

86 87 88 89 90 91 92 93 94 95 96 97
bool getBoolFromEnvOrArgs(
  String name,
  ArgResults argResults,
  Map<String, String> env,
) {
  final String envName = fromArgToEnvName(name);
  if (env[envName] != null) {
    return (env[envName]?.toUpperCase()) == 'TRUE';
  }
  return argResults[name] as bool;
}

98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
/// Return multiple values from the environment or fall back to [argResults].
///
/// Values read from an environment variable are assumed to be comma-delimited.
///
/// If the key does not exist in either the CLI args or environment, throws a
/// [ConductorException].
///
/// The environment is favored over CLI args since the latter can have a default
/// value, which the environment should be able to override.
List<String> getValuesFromEnvOrArgs(
  String name,
  ArgResults argResults,
  Map<String, String> env,
) {
  final String envName = fromArgToEnvName(name);
  if (env[envName] != null && env[envName] != '') {
114
    return env[envName]!.split(',');
115 116 117 118 119 120
  }
  final List<String> argValues = argResults[name] as List<String>;
  if (argValues != null) {
    return argValues;
  }

121 122
  throw ConductorException('Expected either the CLI arg --$name or the environment variable $envName '
      'to be provided!');
123 124 125 126 127 128 129 130
}

/// Translate CLI arg names to env variable names.
///
/// For example, 'state-file' -> 'STATE_FILE'.
String fromArgToEnvName(String argName) {
  return argName.toUpperCase().replaceAll(r'-', r'_');
}
131 132

/// Return a web link for the user to open a new PR.
133 134
///
/// Includes PR title and body via query params.
135 136 137
String getNewPrLink({
  required String userName,
  required String repoName,
138
  required pb.ConductorState state,
139
}) {
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
  assert(state.releaseChannel.isNotEmpty);
  assert(state.releaseVersion.isNotEmpty);
  late final String candidateBranch;
  late final String workingBranch;
  late final String repoLabel;
  switch (repoName) {
    case 'flutter':
      candidateBranch = state.framework.candidateBranch;
      workingBranch = state.framework.workingBranch;
      repoLabel = 'Framework';
      break;
    case 'engine':
      candidateBranch = state.engine.candidateBranch;
      workingBranch = state.engine.workingBranch;
      repoLabel = 'Engine';
      break;
    default:
      throw ConductorException('Expected repoName to be one of flutter or engine but got $repoName.');
  }
  assert(candidateBranch.isNotEmpty);
  assert(workingBranch.isNotEmpty);
  final String title = '[flutter_releases] Flutter ${state.releaseChannel} '
      '${state.releaseVersion} $repoLabel Cherrypicks';
  final StringBuffer body = StringBuffer();
  body.write('''
# Flutter ${state.releaseChannel} ${state.releaseVersion} $repoLabel

## Scheduled Cherrypicks

''');
  if (repoName == 'engine') {
    if (state.engine.dartRevision.isNotEmpty) {
      // shorten hashes to make final link manageable
173
      // prefix with github org/repo so GitHub will auto-generate a hyperlink
174 175
      body.writeln('- Roll dart revision: dart-lang/sdk@${state.engine.dartRevision.substring(0, 9)}');
    }
176 177 178 179 180 181
    for (final pb.Cherrypick cp in state.engine.cherrypicks) {
      // Only list commits that map to a commit that exists upstream.
      if (cp.trunkRevision.isNotEmpty) {
        body.writeln('- commit: flutter/engine@${cp.trunkRevision.substring(0, 9)}');
      }
    }
182
  } else {
183 184 185 186 187 188
    for (final pb.Cherrypick cp in state.framework.cherrypicks) {
      // Only list commits that map to a commit that exists upstream.
      if (cp.trunkRevision.isNotEmpty) {
        body.writeln('- commit: ${cp.trunkRevision.substring(0, 9)}');
      }
    }
189
  }
190 191
  return 'https://github.com/flutter/$repoName/compare/'
      '$candidateBranch...$userName:$workingBranch?'
192 193 194
      'expand=1'
      '&title=${Uri.encodeQueryComponent(title)}'
      '&body=${Uri.encodeQueryComponent(body.toString())}';
195
}