Skip to content

Commit

Permalink
Merge pull request #5 from Rexios80/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
Rexios80 authored Apr 1, 2022
2 parents 5aa67ef + 2b5794f commit 1e2c16d
Show file tree
Hide file tree
Showing 33 changed files with 980 additions and 13 deletions.
37 changes: 37 additions & 0 deletions .github/workflows/static-analysis.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Static Analysis
on:
push:
pull_request:

jobs:
static-analysis:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@master
- uses: subosito/flutter-action@v2
- name: Pub get
run: flutter pub get
- name: Format
run: flutter format . --set-exit-if-changed
- name: Analyze
run: flutter analyze
# - name: Embedme
# run: |
# npm install embedme
# npx embedme README.md --verify
# - name: Build runner
# if: ${{ matrix.project == 'fast_rx_test' }}
# run: |
# flutter pub run build_runner build --delete-conflicting-outputs
# git diff --exit-code
- name: Activate fvm
run: |
dart pub global activate fvm
fvm install stable
- name: Test
run: flutter test
- name: Pana
run: |
flutter pub global activate pana
pana --no-warning --exit-code-threshold 20
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 1.13.0
- FVM support
- Config file to allow per-project command exclusions
- Added tests

## 1.12.2
- Updated pub_update_checker

Expand Down
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ Run pub commands for all sub projects in the current directory recursively
## Features
- Supports all project-level pub commands
- Determines if a project uses dart or flutter automatically
- FVM support
- Convenience shortcuts for common dart/flutter commands
- Combined exit code for use in CI
- Per-project command exclusions

| Command | Equivalent |
| ---------------------- | ----------------------------------------------------------------------------------- |
Expand All @@ -13,6 +15,8 @@ Run pub commands for all sub projects in the current directory recursively
| `puby test [options]` | `[dart\|flutter] test [options]` |
| `puby clean [options]` | `flutter clean [options]` (only runs in flutter projects) |

For projects configured with FVM, `fvm flutter [options]` is used.

## Use as an executable

### Installation
Expand All @@ -25,4 +29,22 @@ $ dart pub global activate puby
$ puby get
$ puby upgrade --major-versions
...
```
```

## Configuration
Create a `puby.yaml` file in the root of the project you want to configure

### Exclusions
Add command exclusions to prevent them from running in a project

```yaml
exclude:
- test
- pub run build_runner
```
Exclusions match from the start of a command, and the entire exclusion string must be present. Here are some examples:
| Exclusion | Example command excluded |
| ---------------------- | -------------------------------------------- |
| `test` | `[dart\|flutter] test --coverage` |
| `pub run build_runner` | `[dart\|flutter] pub run build_runner build` |
7 changes: 6 additions & 1 deletion analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
include: package:rexios_lints/dart/package.yaml
include: package:rexios_lints/dart/package.yaml

analyzer:
exclude:
# workaround for https://github.com/dart-lang/sdk/issues/42910
- 'test_resources/**'
25 changes: 25 additions & 0 deletions bin/config.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'dart:io';

import 'package:yaml/yaml.dart';

class PubyConfig {
final List<String> excludes;

PubyConfig._({
required this.excludes,
});

PubyConfig.empty() : this._(excludes: []);

factory PubyConfig.fromProjectPath(String path) {
final file = File('$path/puby.yaml');
if (!file.existsSync()) {
return PubyConfig.empty();
}

final yaml = loadYaml(file.readAsStringSync());
final excludes = (yaml['exclude'] as List?)?.cast<String>() ?? [];

return PubyConfig._(excludes: excludes);
}
}
60 changes: 50 additions & 10 deletions bin/puby.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import 'package:path/path.dart';
import 'package:pub_update_checker/pub_update_checker.dart';
import 'package:yaml/yaml.dart';

import 'config.dart';

final decoder = Utf8Decoder();
final convenienceCommands = {
'gen': [
Expand Down Expand Up @@ -49,35 +51,41 @@ Usage:
exit(1);
}

final List<String> args;
final List<String> transformedArgs;
final firstArg = arguments.first;
if (convenienceCommands.containsKey(firstArg)) {
args = convenienceCommands[firstArg]! + arguments.sublist(1);
transformedArgs = convenienceCommands[firstArg]! + arguments.sublist(1);
} else {
args = ['pub', ...arguments];
transformedArgs = ['pub', ...arguments];
}
final argString = args.join(' ');

final projects = await findProjects();

int exitCode = 0;
for (final project in projects) {
if (shouldSkipProject(project, projects.length, args)) {
// Fvm is a layer on top of flutter, so don't add the prefix args for these checks
if (explicitExclude(project, transformedArgs) ||
defaultExclude(project, projects.length, transformedArgs)) {
continue;
}

final finalArgs = project.engine.prefixArgs + transformedArgs;

final argString = finalArgs.join(' ');
final pathString = project.path == '.' ? 'current directory' : project.path;
print(
greenPen(
'\nRunning "${project.engine.name} $argString" in $pathString...',
),
);

final process = await Process.start(
project.engine.name,
args,
finalArgs,
workingDirectory: project.path,
runInShell: true,
);

// Piping directly to stdout and stderr can cause unexpected behavior
process.stdout.listen((e) => stdout.write(decoder.convert(e)));
process.stderr.listen((e) => stderr.write(redPen(decoder.convert(e))));
Expand All @@ -97,14 +105,14 @@ Usage:
exit(exitCode);
}

bool shouldSkipProject(Project project, int projectCount, List<String> args) {
bool defaultExclude(Project project, int projectCount, List<String> args) {
final bool skip;
final String? message;
if (project.hidden) {
// Skip hidden folders
message = 'Skipping hidden project: ${project.path}';
skip = true;
} else if (project.engine == Engine.flutter &&
} else if (project.engine.isFlutter &&
project.example &&
args.length >= 2 &&
args[0] == 'pub' &&
Expand All @@ -128,6 +136,17 @@ bool shouldSkipProject(Project project, int projectCount, List<String> args) {
return skip;
}

bool explicitExclude(Project project, List<String> args) {
final argString = args.join(' ');

final skip = project.config.excludes.any(argString.startsWith);
if (skip) {
print(yellowPen('\nSkipping project with exclusion: ${project.path}'));
}

return skip;
}

Future<List<Project>> findProjects() async {
final pubspecEntities =
Directory.current.listSync(recursive: true, followLinks: false).where(
Expand All @@ -145,27 +164,32 @@ Future<List<Project>> findProjects() async {
class Project {
final Engine engine;
final String path;
final PubyConfig config;
final bool example;
final bool hidden;

Project._({
required this.engine,
required this.path,
required this.config,
required this.example,
required this.hidden,
});

static Future<Project> fromPubspecEntity(FileSystemEntity entity) async {
final pubspec = await loadYaml(File(entity.path).readAsStringSync());
final path = relative(entity.parent.path);
final config = PubyConfig.fromProjectPath(path);

final Engine engine;
if (pubspec['dependencies']?['flutter'] != null) {
if (Directory('$path/.fvm').existsSync()) {
engine = Engine.fvm;
} else if (pubspec['dependencies']?['flutter'] != null) {
engine = Engine.flutter;
} else {
engine = Engine.dart;
}

final path = relative(entity.parent.path);
final example = path.split(Platform.pathSeparator).last == 'example';
final hidden = path
.split(Platform.pathSeparator)
Expand All @@ -174,6 +198,7 @@ class Project {
return Project._(
engine: engine,
path: path,
config: config,
example: example,
hidden: hidden,
);
Expand All @@ -183,4 +208,19 @@ class Project {
enum Engine {
dart,
flutter,
fvm,
}

extension on Engine {
bool get isFlutter => this == Engine.flutter || this == Engine.fvm;

List<String> get prefixArgs {
switch (this) {
case Engine.dart:
case Engine.flutter:
return [];
case Engine.fvm:
return ['flutter'];
}
}
}
3 changes: 2 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: puby
description: Run pub commands for all sub projects in the current directory recursively
version: 1.12.2
version: 1.13.0
homepage: https://github.com/Rexios80/puby

environment:
Expand All @@ -13,6 +13,7 @@ dependencies:
pub_update_checker: ^1.2.0

dev_dependencies:
test: ^1.20.2
rexios_lints: ^1.0.0

executables:
Expand Down
37 changes: 37 additions & 0 deletions test/clean_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import 'dart:io';

import 'package:test/test.dart';

import 'test_utils.dart';

void main() {
test('[engine] clean', () async {
final result = await testCommand(['clean']);
final stdout = result.stdout;

expect(result.exitCode, 0);

// dart
// Default exclusion
expectLine(stdout, ['dart_puby_test', 'Skip']);
// Default exclusion
expectLine(
stdout,
['dart_puby_test${Platform.pathSeparator}example', 'Skip'],
);

// flutter
expectLine(stdout, ['flutter_puby_test', 'flutter clean']);
expectLine(stdout, [
'flutter_puby_test${Platform.pathSeparator}example',
'flutter clean',
]);

// fvm
expectLine(stdout, ['fvm_puby_test', 'fvm flutter clean']);
expectLine(stdout, [
'fvm_puby_test${Platform.pathSeparator}example',
'fvm flutter clean',
]);
});
}
48 changes: 48 additions & 0 deletions test/gen_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import 'dart:io';

import 'package:test/test.dart';

import 'test_utils.dart';

void main() {
test('[engine] gen', () async {
final result = await testCommand(['gen']);
final stdout = result.stdout;

// Since these projects have no code generation, the command should fail
expect(result.exitCode, isNot(0));

// dart
expectLine(stdout, [
'dart_puby_test',
'dart pub run build_runner build --delete-conflicting-outputs',
]);
// Explicit exclusion
expectLine(
stdout,
['dart_puby_test${Platform.pathSeparator}example', 'Skip'],
);

// flutter
expectLine(stdout, [
'flutter_puby_test',
'flutter pub run build_runner build --delete-conflicting-outputs',
]);
// Explicit exclusion
expectLine(
stdout,
['flutter_puby_test${Platform.pathSeparator}example', 'Skip'],
);

// fvm
expectLine(stdout, [
'fvm_puby_test',
'fvm flutter pub run build_runner build --delete-conflicting-outputs',
]);
// Explicit exclusion
expectLine(
stdout,
['fvm_puby_test${Platform.pathSeparator}example', 'Skip'],
);
});
}
Loading

0 comments on commit 1e2c16d

Please sign in to comment.