Skip to content

Commit

Permalink
add a 'mono_repo' tweak (#109)
Browse files Browse the repository at this point in the history
* add a 'mono_repo' tweak

* use => syntax
  • Loading branch information
devoncarew authored May 26, 2023
1 parent 1d3fccd commit 601d5a8
Show file tree
Hide file tree
Showing 11 changed files with 161 additions and 28 deletions.
16 changes: 12 additions & 4 deletions pkgs/blast_repo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,16 @@ A tool to bulk validate and fix GitHub repos.
```
Usage: blast_repo <options> [org/repo]
--keep-temp
--include-unstable To run tweaks that are not stable.
--pr-reviewer The GitHub handle for the desired reviewer.
-h, --help Prints out usage and exits
--keep-temp Don't delete the temporary repo clone.
--tweaks=<tweak1,tweak2> Optionally list the specific tweaks to run (defaults to all applicable tweaks).
[auto-publish, dependabot, github-actions, monorepo, no-response]
--reviewer=<github-id> Specify the GitHub handle for the desired reviewer.
-h, --help Prints out usage and exits.
available tweaks:
auto-publish: configure a github action to enable package auto-publishing
dependabot: ensure ".github/dependabot.yml" exists and has the correct content
github-actions: ensure GitHub actions use the latest versions and are keyed by SHA
monorepo: regenerate the latest configuration files for package:mono_repo
no-response: configure a 'no response' bot to handle needs-info labels
```
17 changes: 5 additions & 12 deletions pkgs/blast_repo/bin/blast_repo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,12 @@ Future<void> main(List<String> args) async {
)
..addMultiOption('tweaks',
help: 'Optionally list the specific tweaks to run (defaults to all '
'stable tweaks).',
'applicable tweaks).',
allowed: allTweaks.map((t) => t.id),
valueHelp: 'tweak1,tweak2')
..addFlag(
'include-unstable',
help: 'Run tweaks that are not stable.',
negatable: false,
)
..addOption(
'pr-reviewer',
'reviewer',
aliases: ['pr-reviewer'],
valueHelp: 'github-id',
help: 'Specify the GitHub handle for the desired reviewer.',
)
Expand All @@ -44,8 +40,7 @@ Future<void> main(List<String> args) async {
print(parser.usage);
print('\navailable tweaks:');
for (var tweak in allTweaks) {
var unstable = tweak.stable ? '' : ' (unstable)';
print(' ${tweak.id}: ${tweak.description}$unstable');
print(' ${tweak.id}: ${tweak.description}');
}
}

Expand All @@ -68,8 +63,7 @@ Future<void> main(List<String> args) async {

final keepTemp = argResults['keep-temp'] as bool;

final includeUnstable = argResults['include-unstable'] as bool;
final prReviewer = argResults['pr-reviewer'] as String?;
final prReviewer = argResults['reviewer'] as String?;
final explicitTweakIds = argResults['tweaks'] as List<String>;
final explicitTweaks = explicitTweakIds.isEmpty
? null
Expand All @@ -82,7 +76,6 @@ Future<void> main(List<String> args) async {
slug: slug,
deleteTemp: !keepTemp,
tweaks: explicitTweaks,
onlyStable: !includeUnstable,
prReviewer: prReviewer,
);
} catch (error, stack) {
Expand Down
6 changes: 5 additions & 1 deletion pkgs/blast_repo/lib/src/repo_tweak.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ abstract class RepoTweak {
final String id;
final String description;

bool get stable => true;
/// Returns whether this tweak should run for the given repository by default.
///
/// Some tweaks may only run by default in the presence of a file in the repo,
/// or only if the repo is a mono repo.
bool shouldRunByDefault(Directory checkout, String repoSlug);

/// Checks to see if the [checkout] needs to be fixed.
///
Expand Down
13 changes: 4 additions & 9 deletions pkgs/blast_repo/lib/src/top_level.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:blast_repo/src/tweaks/mono_repo_tweak.dart';
import 'package:git/git.dart';

import 'repo_tweak.dart';
Expand All @@ -19,13 +20,13 @@ final allTweaks = Set<RepoTweak>.unmodifiable([
AutoPublishTweak(),
DependabotTweak(),
GitHubActionTweak(),
MonoRepoTweak(),
NoResponseTweak(),
]);

Future<void> runFix({
required String slug,
required bool deleteTemp,
required bool onlyStable,
required String? prReviewer,
Iterable<RepoTweak>? tweaks,
}) async {
Expand All @@ -38,7 +39,6 @@ Future<void> runFix({
slug,
tempDir,
tweaks: tweaks,
onlyStable: onlyStable,
);

final fixes = result.entries
Expand Down Expand Up @@ -109,9 +109,9 @@ Future<Map<RepoTweak, FixResult>> fixAll(
String repoSlug,
Directory checkout, {
Iterable<RepoTweak>? tweaks,
required bool onlyStable,
}) async {
tweaks ??= allTweaks.orAll(onlyStable: onlyStable);
tweaks ??=
allTweaks.where((tweak) => tweak.shouldRunByDefault(checkout, repoSlug));

return {
for (var tweak in tweaks)
Expand All @@ -133,8 +133,3 @@ Future<T> _safeRun<T>(
rethrow;
}
}

extension on Iterable<RepoTweak>? {
Iterable<RepoTweak> orAll({required bool onlyStable}) =>
(this ?? allTweaks).where((element) => !onlyStable || element.stable);
}
3 changes: 2 additions & 1 deletion pkgs/blast_repo/lib/src/tweaks/auto_publish_tweak.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ class AutoPublishTweak extends ExactFileTweak {
);

@override
bool get stable => false;
bool shouldRunByDefault(Directory checkout, String repoSlug) =>
monoRepo(checkout, repoSlug);

@override
String expectedContent(Directory checkout, String repoSlug) {
Expand Down
3 changes: 3 additions & 0 deletions pkgs/blast_repo/lib/src/tweaks/dependabot_tweak.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ class DependabotTweak extends RepoTweak {
description: 'ensure "$_filePath" exists and has the correct content',
);

@override
bool shouldRunByDefault(Directory checkout, String repoSlug) => true;

@override
FutureOr<FixResult> fix(Directory checkout, String repoSlug) {
final file = _dependabotFile(checkout);
Expand Down
3 changes: 3 additions & 0 deletions pkgs/blast_repo/lib/src/tweaks/github_action_tweak.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ class GitHubActionTweak extends RepoTweak {
'by SHA',
);

@override
bool shouldRunByDefault(Directory checkout, String repoSlug) => true;

@override
FutureOr<FixResult> fix(Directory checkout, String repoSlug) async {
final files = _workflowFiles(checkout);
Expand Down
73 changes: 73 additions & 0 deletions pkgs/blast_repo/lib/src/tweaks/mono_repo_tweak.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// 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:io';

import 'package:path/path.dart' as p;

import '../repo_tweak.dart';
import '../utils.dart';

final _instance = MonoRepoTweak._();

/// Regenerate the latest configuration files for package:mono_repo.
class MonoRepoTweak extends RepoTweak {
factory MonoRepoTweak() => _instance;

MonoRepoTweak._()
: super(
id: 'monorepo',
description:
'regenerate the latest configuration files for package:mono_repo',
);

@override
bool shouldRunByDefault(Directory checkout, String repoSlug) {
return File(p.join(checkout.path, 'mono_repo.yaml')).existsSync();
}

@override
FutureOr<FixResult> fix(Directory checkout, String repoSlug) async {
// get the latest mono_repo
await runProc(
'activating mono_repo',
Platform.resolvedExecutable,
['pub', 'global', 'activate', 'mono_repo'],
workingDirectory: checkout.path,
);

// record the current values for the files
final files = {
'.github/workflows/dart.yml',
'tool/ci.sh',
};
final existingContent = {
for (var path in files)
path: File(p.join(checkout.path, path)).existsSync()
? File(p.join(checkout.path, path)).readAsStringSync()
: '',
};

// run mono_repo generate
await runProc(
'run mono_repo generate',
Platform.resolvedExecutable,
['pub', 'global', 'run', 'mono_repo', 'generate'],
workingDirectory: checkout.path,
);

// return the results
final fixes = <String>[];

for (var entry in existingContent.entries) {
final file = File(p.join(checkout.path, entry.key));
if (file.readAsStringSync() != entry.value) {
fixes.add('updated ${entry.key}');
}
}

return fixes.isEmpty ? FixResult.noFixesMade : FixResult(fixes: fixes);
}
}
4 changes: 3 additions & 1 deletion pkgs/blast_repo/lib/src/tweaks/no_reponse_tweak.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import 'dart:io';

import '../exact_file_tweak.dart';
import '../utils.dart';

final _instance = NoResponseTweak._();

Expand All @@ -20,7 +21,8 @@ class NoResponseTweak extends ExactFileTweak {
);

@override
bool get stable => false;
bool shouldRunByDefault(Directory checkout, String repoSlug) =>
monoRepo(checkout, repoSlug);

@override
String expectedContent(Directory checkout, String repoSlug) {
Expand Down
16 changes: 16 additions & 0 deletions pkgs/blast_repo/lib/src/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,19 @@ bool singlePackageRepo(Directory repoDir) {
var pubspec = File(p.join(repoDir.path, 'pubspec.yaml'));
return pubspec.existsSync();
}

/// Returns whether the given repo follows some conventions for our monorepos.
///
/// Currently this checks for either the presense of a `mono_repo.yaml` file or
/// of a top-level `pkgs/` directory.
bool monoRepo(Directory dir, String repoSlug) {
if (File(p.join(dir.path, 'mono_repo.yaml')).existsSync()) {
return true;
}

if (Directory(p.join(dir.path, 'pkgs')).existsSync()) {
return true;
}

return false;
}
35 changes: 35 additions & 0 deletions pkgs/blast_repo/test/mono_repo_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2023, 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:io' as io;

import 'package:blast_repo/src/tweaks/mono_repo_tweak.dart';
import 'package:test/test.dart';
import 'package:test_descriptor/test_descriptor.dart' as d;

void main() {
late MonoRepoTweak tweak;
late io.Directory dir;

setUp(() async {
tweak = MonoRepoTweak();
await d.dir('foo', [
d.file('mono_repo.yaml', '# foo bar\n'),
]).create();
dir = d.dir('foo').io;
});

test('recognizes mono_repo repo', () async {
expect(tweak.shouldRunByDefault(dir, 'my_org/my_repo'), true);
});

test('ignores non-managed repo', () async {
await d.dir('foo', [
d.file('README.md', '# package name\n\n'),
]).create();
dir = d.dir('foo').io;

expect(tweak.shouldRunByDefault(dir, 'my_org/my_repo'), true);
});
}

0 comments on commit 601d5a8

Please sign in to comment.