blob: dd8514ca843580c4df8e5cefc66c58d2c7e570b8 [file] [log] [blame]
// Copyright (c) 2022, 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:async';
import 'dart:convert';
import 'dart:io';
import 'package:git/git.dart';
import 'repo_tweak.dart';
import 'tweaks/auto_publish_tweak.dart';
import 'tweaks/dependabot_tweak.dart';
import 'tweaks/github_action_tweak.dart';
import 'tweaks/mono_repo_tweak.dart';
import 'tweaks/no_reponse_tweak.dart';
import 'utils.dart';
final allTweaks = Set<RepoTweak>.unmodifiable([
AutoPublishTweak(),
DependabotTweak(),
GitHubActionTweak(),
MonoRepoTweak(),
NoResponseTweak(),
]);
Future<void> runFix({
required String slug,
required bool deleteTemp,
required String? reviewer,
List<RepoTweak>? tweaks,
List<String> labels = const [],
bool dryRun = false,
}) async {
await withSystemTemp(
deleteTemp: deleteTemp,
(tempDir, runKey) async {
await cloneGitHubRepoToPath(slug, tempDir.path);
final result = await fixAll(
slug,
tempDir,
tweaks: tweaks,
);
final fixes = result.entries
.where((element) => element.value.fixes.isNotEmpty)
.map((e) => e.key.id)
.toList()
..sort();
if (fixes.isEmpty) {
printHeader('No changes! All done!');
return;
}
printHeader('Fixes:');
for (var entry in result.entries) {
print(entry.key.id);
print(const JsonEncoder.withIndent(' ').convert(entry.value.fixes));
}
final gitDir = await GitDir.fromExisting(tempDir.path);
await gitDir.exec(
'Creating a working branch',
['checkout', '-b', runKey],
);
await gitDir.exec(
'Add all files to index',
['add', '-v', '.'],
);
await gitDir.exec(
'Commit changes',
[
'commit',
'-am',
'''
$packageName fixes
${fixes.join(', ')}
'''
],
);
await gitDir.exec(
'Changes:',
['--no-pager', 'show'],
);
await runProc(
'Creating pull request',
'gh',
[
'pr',
'create',
'--fill',
'--title',
'blast repo changes: ${fixes.join(', ')}',
'--body',
'This PR contains changes created by the blast repo tool.\n\n'
'${fixes.map((fix) => '- `$fix`').join('\n')}',
'--repo',
slug,
if (reviewer != null) ...['--reviewer', reviewer],
for (final label in labels) ...['--label', label],
],
workingDirectory: tempDir.path,
skipExecution: dryRun,
);
},
);
}
Future<Map<RepoTweak, FixResult>> fixAll(
String repoSlug,
Directory checkout, {
List<RepoTweak>? tweaks,
}) async {
tweaks ??= allTweaks
.where((tweak) => tweak.shouldRunByDefault(checkout, repoSlug))
.toList();
return {
for (var tweak in tweaks)
tweak: await _safeRun(repoSlug, checkout, tweak.id, tweak.fix),
};
}
Future<T> _safeRun<T>(
String repoSlug,
Directory checkout,
String id,
FutureOr<T> Function(Directory, String repoSlug) action,
) async {
printHeader('Running "$id"');
try {
return await action(checkout, repoSlug);
} catch (_) {
printError(' Error running $id');
rethrow;
}
}