Skip to content

Commit

Permalink
[puppy] Introduce a package for misc Dart CLI tools (#335)
Browse files Browse the repository at this point in the history
First command: `map` to recursively run a command in every
directory that contains pubspec.yaml
  • Loading branch information
kevmoo authored Jan 10, 2025
1 parent 21e81c5 commit 25c57b8
Show file tree
Hide file tree
Showing 10 changed files with 217 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
- changed-files:
- any-glob-to-any-file: 'pkgs/repo_manage/**'

'package:puppy':
- changed-files:
- any-glob-to-any-file: 'pkgs/puppy/**'

'package:sdk_triage_bot':
- changed-files:
- any-glob-to-any-file: 'pkgs/sdk_triage_bot/**'
Expand Down
41 changes: 41 additions & 0 deletions .github/workflows/puppy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: package:puppy

permissions: read-all

on:
pull_request:
branches: [ main ]
paths:
- '.github/workflows/puppy.yml'
- 'pkgs/puppy/**'
push:
branches: [ main ]
paths:
- '.github/workflows/puppy.yml'
- 'pkgs/puppy/**'
schedule:
- cron: '0 0 * * 0' # weekly

defaults:
run:
working-directory: pkgs/puppy

jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
sdk: [3.6, dev]
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: dart-lang/setup-dart@e630b99d28a3b71860378cafdc2a067c71107f94
with:
sdk: ${{ matrix.sdk }}

- run: dart pub get
- run: dart analyze --fatal-infos
- run: dart format --output=none --set-exit-if-changed .
if: ${{ matrix.sdk == 'dev' }}
# TODO: enable when there are tests!
#- run: dart test
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This repository is home to general Dart Ecosystem tools and packages.
| [corpus](pkgs/corpus/) | A tool to calculate the API usage for a package. | |
| [dart_flutter_team_lints](pkgs/dart_flutter_team_lints/) | An analysis rule set used by the Dart and Flutter teams. | [![pub package](https://img.shields.io/pub/v/dart_flutter_team_lints.svg)](https://pub.dev/packages/dart_flutter_team_lints) |
| [firehose](pkgs/firehose/) | A tool to automate publishing of Pub packages from GitHub actions. | [![pub package](https://img.shields.io/pub/v/firehose.svg)](https://pub.dev/packages/firehose) |
| [puppy](pkgs/puppy/) | A grab bag of CLI tools for managing Dart code. | |
| [repo_manage](pkgs/repo_manage/) | Miscellaneous issue, repo, and PR query tools. | |
| [sdk_triage_bot](pkgs/sdk_triage_bot/) | A triage automation tool for dart-lang/sdk issues. | |
| [trebuchet](pkgs/trebuchet/) | A tool for moving existing packages into monorepos. | |
Expand Down
11 changes: 11 additions & 0 deletions pkgs/puppy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Activate locally using:

```console
cd <puppy pkg path>
dart pub global activate --source=path .
```

### Commands

- `run`: runs a command in every directory containing
`pubspec.yaml`.
1 change: 1 addition & 0 deletions pkgs/puppy/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include: package:dart_flutter_team_lints/analysis_options.yaml
14 changes: 14 additions & 0 deletions pkgs/puppy/bin/puppy.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// 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 'package:args/command_runner.dart';
import 'package:puppy/src/constants.dart';
import 'package:puppy/src/map_command.dart';

Future<void> main(List<String> args) async {
var runner = CommandRunner<void>(cmdName, 'Dart repository management tools.')
..addCommand(MapCommand());

await runner.run(args);
}
5 changes: 5 additions & 0 deletions pkgs/puppy/lib/src/constants.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// 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.

const cmdName = 'puppy';
88 changes: 88 additions & 0 deletions pkgs/puppy/lib/src/map_command.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// 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:async';
import 'dart:io';

import 'package:args/command_runner.dart';
import 'package:build_cli_annotations/build_cli_annotations.dart';
import 'package:io/ansi.dart';

import 'constants.dart';

part 'map_command.g.dart';

class MapCommand extends _$MapArgsCommand<void> {
@override
String get description =>
'Run the provided command in each subdirectory containing '
'`pubspec.yaml`.';

@override
String get name => 'map';

@override
Future<void>? run() async {
await _doMap(_options);
}
}

@CliOptions(createCommand: true)
class MapArgs {
@CliOption(abbr: 'd', help: 'Keep looking for "nested" pubspec files.')
final bool deep;

final List<String> rest;

MapArgs({
this.deep = false,
required this.rest,
}) {
if (rest.isEmpty) {
throw UsageException(
'Missing command to invoke!',
'$cmdName map [--deep] <command to invoke>',
);
}
}
}

Future<void> _doMap(MapArgs args) async {
final exe = args.rest.first;
final extraArgs = args.rest.skip(1).toList();

final exits = <String, int>{};

Future<void> inspectDirectory(Directory dir, {required bool deep}) async {
final pubspecs = dir
.listSync()
.whereType<File>()
.where((element) => element.uri.pathSegments.last == 'pubspec.yaml')
.toList();

final pubspecHere = pubspecs.isNotEmpty;
if (pubspecHere) {
print(green.wrap(dir.path));
final proc = await Process.start(
exe,
extraArgs,
mode: ProcessStartMode.inheritStdio,
workingDirectory: dir.path,
);

// TODO(kevmoo): display a summary of results on completion
exits[dir.path] = await proc.exitCode;
}

if (!pubspecHere || deep) {
for (var subDir in dir.listSync().whereType<Directory>().where(
(element) => !element.uri.pathSegments
.any((element) => element.startsWith('.')))) {
await inspectDirectory(subDir, deep: deep);
}
}
}

await inspectDirectory(Directory.current, deep: args.deep);
}
34 changes: 34 additions & 0 deletions pkgs/puppy/lib/src/map_command.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions pkgs/puppy/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: puppy
publish_to: none

environment:
sdk: ^3.6.0

dependencies:
args: ^2.6.0
build_cli_annotations: ^2.1.0
io: ^1.0.5

dev_dependencies:
build_cli: ^2.2.4
build_runner: ^2.4.14
dart_flutter_team_lints: ^3.0.0

executables:
puppy:

0 comments on commit 25c57b8

Please sign in to comment.