blob: 99634d3874c4b020b177eaba0e981fa422aa1cfd [file] [log] [blame] [edit]
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. 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:convert';
import 'dart:io';
import 'dart:ffi';
import 'package:args/args.dart';
import 'package:yaml/yaml.dart';
void main(List<String> arguments) async {
final parser = makeArgParser();
final ArgResults argResults = parser.parse(arguments);
final packages = loadPackagesFromPubspec();
if (argResults['help'] as bool) {
print('A command-line tool for running CI checks on the CI or locally.');
print('');
print('Applies to the following packages:');
for (final package in packages) {
print(' - $package');
}
print('');
print('Usage:');
print(parser.usage);
return;
}
if (argResults['pub'] as bool) {
const paths = [
'.',
'pkgs/hooks_runner/test_data/native_add_version_skew/',
'pkgs/hooks_runner/test_data/native_add_version_skew_2/',
];
for (final path in paths) {
_runProcess('dart', ['pub', 'get', '--directory', path]);
}
}
if (argResults['analyze'] as bool) {
_runProcess('dart', ['analyze', '--fatal-infos', ...packages]);
}
if (argResults['format'] as bool) {
_runProcess('dart', [
'format',
'--output=none',
'--set-exit-if-changed',
...packages,
]);
}
if (argResults['generate'] as bool) {
const generators = [
'pkgs/hooks/tool/generate_schemas.dart',
'pkgs/hooks/tool/generate_syntax.dart',
'pkgs/hooks/tool/normalize.dart',
];
for (final generator in generators) {
_runProcess('dart', [generator, '--set-exit-if-changed']);
}
}
if (argResults['test'] as bool) {
final testUris = getTestUris(packages);
_runProcess('dart', ['test', ...testUris]);
}
if (argResults['example'] as bool) {
const examplesWithTest = [
'native_dynamic_linking',
'native_add_app',
'use_dart_api',
'download_asset',
'system_library',
];
for (final exampleWithTest in examplesWithTest) {
_runProcess(
workingDirectory: repositoryRoot.resolve(
'pkgs/hooks/example/build/$exampleWithTest/',
),
'dart',
['--enable-experiment=native-assets', 'test'],
);
}
_runProcess(
workingDirectory: repositoryRoot.resolve(
'pkgs/hooks/example/build/native_add_app/',
),
'dart',
['--enable-experiment=native-assets', 'run'],
);
_runProcess(
workingDirectory: repositoryRoot.resolve(
'pkgs/hooks/example/build/native_add_app/',
),
'dart',
[
'--enable-experiment=native-assets',
'build',
'cli',
'bin/native_add_app.dart',
],
);
_runProcess(
repositoryRoot
.resolve(
'pkgs/hooks/example/build/native_add_app/build/cli/${Abi.current().toString()}/bundle/bin/native_add_app${Platform.isWindows ? '.exe' : ''}',
)
.toFilePath(),
[],
);
}
if (argResults['coverage'] as bool) {
final testUris = getTestUris(packages);
final scopeOutputs = [
for (final testUri in testUris) Uri.directory(testUri).pathSegments[1],
];
if (argResults['pub'] as bool) {
_runProcess('dart', ['pub', 'global', 'activate', 'coverage']);
}
_runProcess('dart', [
'pub',
'global',
'run',
'coverage:test_with_coverage',
for (final scopeOutput in scopeOutputs) ...[
'--scope-output',
scopeOutput,
],
...testUris,
]);
}
}
ArgParser makeArgParser() {
final parser = ArgParser()
..addFlag(
'help',
abbr: 'h',
negatable: false,
help: 'Prints this help message.',
)
..addFlag(
'analyze',
defaultsTo: true,
help: 'Run `dart analyze` on the packages.',
)
..addFlag(
'coverage',
defaultsTo: false,
help: 'Run `dart run coverage:test_with_coverage` on the packages.',
)
..addFlag(
'example',
defaultsTo: true,
help: 'Run tests and executables for examples.',
)
..addFlag(
'format',
defaultsTo: true,
help: 'Run `dart format` on the packages.',
)
..addFlag(
'generate',
defaultsTo: true,
help: 'Run code generation scripts.',
)
..addFlag(
'pub',
defaultsTo: false,
help:
'Run `dart pub get` on the root and non-workspace packages.\n'
'Run `dart pub global activate coverage`.',
)
..addFlag(
'test',
defaultsTo: true,
help: 'Run `dart test` on the packages.',
);
return parser;
}
final Uri repositoryRoot = Platform.script.resolve('../');
/// Load the root packages from the workspace. Omit nested test/example packages.
List<String> loadPackagesFromPubspec() {
final pubspecYaml = loadYaml(
File.fromUri(repositoryRoot.resolve('pubspec.yaml')).readAsStringSync(),
);
final workspace = (pubspecYaml['workspace'] as List).cast<String>();
final packages = workspace
.where(
(package) =>
!package.contains('test_data') && !package.contains('example'),
)
.toList();
return packages;
}
List<String> getTestUris(List<String> packages) {
final testUris = <String>[];
for (final package in packages) {
final packageTestDirectory = Directory.fromUri(
repositoryRoot.resolve(package /*might end without slash*/),
).uri.resolve('test/');
if (Directory.fromUri(packageTestDirectory).existsSync()) {
final relativePath = packageTestDirectory.toFilePath().replaceAll(
repositoryRoot.toFilePath(),
'',
);
testUris.add(relativePath);
}
}
return testUris;
}
void _runProcess(
String executable,
List<String> arguments, {
Uri? workingDirectory,
}) {
var commandString = '$executable ${arguments.join(' ')}';
if (workingDirectory != null) {
commandString = 'cd ${workingDirectory.toFilePath()} && $commandString';
}
print('+$commandString');
final result = Process.runSync(
executable,
arguments,
workingDirectory: workingDirectory?.toFilePath(),
stderrEncoding: utf8, // Make ✓ from `dart test` show up on GitHub UI.
stdoutEncoding: utf8,
);
if (result.stdout.toString().isNotEmpty) {
print(result.stdout);
}
if (result.stderr.toString().isNotEmpty) {
print(result.stderr);
}
if (result.exitCode != 0) {
print('+$commandString failed with exitCode ${result.exitCode}.');
exit(result.exitCode);
}
}