Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dev/tools/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
checkouts/
39 changes: 39 additions & 0 deletions dev/tools/bin/conductor
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env bash

set -euo pipefail

# Needed because if it is set, cd may print the path it changed to.
unset CDPATH

# On Mac OS, readlink -f doesn't work, so follow_links traverses the path one
# link at a time, and then cds into the link destination and find out where it
# ends up.
#
# The returned filesystem path must be a format usable by Dart's URI parser,
# since the Dart command line tool treats its argument as a file URI, not a
# filename. For instance, multiple consecutive slashes should be reduced to a
# single slash, since double-slashes indicate a URI "authority", and these are
# supposed to be filenames. There is an edge case where this will return
# multiple slashes: when the input resolves to the root directory. However, if
# that were the case, we wouldn't be running this shell, so we don't do anything
# about it.
#
# The function is enclosed in a subshell to avoid changing the working directory
# of the caller.
function follow_links() (
cd -P "$(dirname -- "$1")"
file="$PWD/$(basename -- "$1")"
while [[ -h "$file" ]]; do
cd -P "$(dirname -- "$file")"
file="$(readlink -- "$file")"
cd -P "$(dirname -- "$file")"
file="$PWD/$(basename -- "$file")"
done
echo "$file"
)

PROG_NAME="$(follow_links "${BASH_SOURCE[0]}")"
BIN_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
DART_BIN="$BIN_DIR/../../../bin/dart"

"$DART_BIN" --enable-asserts "$BIN_DIR/conductor.dart" "$@"
77 changes: 77 additions & 0 deletions dev/tools/bin/conductor.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// 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.

// Rolls the dev channel.
// Only tested on Linux.
//
// See: https://github.com/flutter/flutter/wiki/Release-process

import 'dart:io' as io;

import 'package:args/command_runner.dart';
import 'package:file/file.dart';
import 'package:file/local.dart';
import 'package:platform/platform.dart';
import 'package:process/process.dart';
import 'package:dev_tools/repository.dart';
import 'package:dev_tools/roll_dev.dart';
import 'package:dev_tools/stdio.dart';

void main(List<String> args) {
const FileSystem fileSystem = LocalFileSystem();
const ProcessManager processManager = LocalProcessManager();
const Platform platform = LocalPlatform();
final Stdio stdio = VerboseStdio(
stdout: io.stdout,
stderr: io.stderr,
stdin: io.stdin,
);
final Checkouts checkouts = Checkouts(
fileSystem: fileSystem,
platform: platform,
processManager: processManager,
);
final CommandRunner<void> runner = CommandRunner<void>(
'conductor',
'A tool for coordinating Flutter releases.',
usageLineLength: 80,
);

<Command<void>>[
RollDev(
fileSystem: fileSystem,
platform: platform,
repository: checkouts.addRepo(
fileSystem: fileSystem,
platform: platform,
repoType: RepositoryType.framework,
stdio: stdio,
),
stdio: stdio,
),
].forEach(runner.addCommand);

if (!assertsEnabled()) {
stdio.printError('The conductor tool must be run with --enable-asserts.');
io.exit(1);
}

try {
runner.run(args);
} on Exception catch (e) {
stdio.printError(e.toString());
io.exit(1);
}
}

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

assert(() {
assertsEnabled = true;
return true;
}());
return assertsEnabled;
}
72 changes: 72 additions & 0 deletions dev/tools/lib/git.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// 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.

import 'dart:io';

import 'package:meta/meta.dart';
import 'package:process/process.dart';

import './globals.dart';

/// A wrapper around git process calls that can be mocked for unit testing.
class Git {
Git(this.processManager) : assert(processManager != null);

final ProcessManager processManager;

String getOutput(
List<String> args,
String explanation, {
@required String workingDirectory,
}) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and elsewhere, I would separate the cast from the method invocation. maybe a little helper method like:

String decode(dynamic value) => (value as String).trim();

but with a better name 😭

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can do that. Is your concern line numbers in the stack trace, or is there a higher level code quality concern?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its a bit awkward to read with the cast inline. Another approach would be to use toString() instead since that is defined on Object/dynamic

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implemented String stdoutToString(dynamic input)

final ProcessResult result = _run(args, workingDirectory);
if (result.exitCode == 0) {
return stdoutToString(result.stdout);
}
_reportFailureAndExit(args, workingDirectory, result, explanation);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a note, once this is opted into null safety you can give _reportFailureAndExit a return type of Never and then there will be no analyzer warning

return null; // for the analyzer's sake
}

int run(
List<String> args,
String explanation, {
bool allowNonZeroExitCode = false,
@required String workingDirectory,
}) {
final ProcessResult result = _run(args, workingDirectory);
if (result.exitCode != 0 && !allowNonZeroExitCode) {
_reportFailureAndExit(args, workingDirectory, result, explanation);
}
return result.exitCode;
}

ProcessResult _run(List<String> args, String workingDirectory) {
return processManager.runSync(
<String>['git', ...args],
workingDirectory: workingDirectory,
);
}

void _reportFailureAndExit(
List<String> args,
String workingDirectory,
ProcessResult result,
String explanation,
) {
final StringBuffer message = StringBuffer();
if (result.exitCode != 0) {
message.writeln(
'Command "git ${args.join(' ')}" failed in directory "$workingDirectory" to '
'$explanation. Git exited with error code ${result.exitCode}.',
);
} else {
message.writeln('Command "git ${args.join(' ')}" failed to $explanation.');
}
if ((result.stdout as String).isNotEmpty)
message.writeln('stdout from git:\n${result.stdout}\n');
if ((result.stderr as String).isNotEmpty)
message.writeln('stderr from git:\n${result.stderr}\n');
throw Exception(message);
}
}
26 changes: 26 additions & 0 deletions dev/tools/lib/globals.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uber-nit: constants?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added the function stdoutToString here, is that cool?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just have an irrational fear of globals

// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

const String kIncrement = 'increment';
const String kCommit = 'commit';
const String kRemoteName = 'remote';
const String kJustPrint = 'just-print';
const String kYes = 'yes';
const String kForce = 'force';
const String kSkipTagging = 'skip-tagging';

const String kUpstreamRemote = 'https://github.com/flutter/flutter.git';

const List<String> kReleaseChannels = <String>[
'stable',
'beta',
'dev',
'master',
];

/// Cast a dynamic to String and trim.
String stdoutToString(dynamic input) {
final String str = input as String;
return str.trim();
}
Loading