Skip to content

Commit

Permalink
Merge pull request #152 from Leftwitch/feat/markdown-report
Browse files Browse the repository at this point in the history
Add support for multiple diff report formats
  • Loading branch information
devmil authored Sep 26, 2023
2 parents 7b8dc02 + 9c1e598 commit b4c1d34
Show file tree
Hide file tree
Showing 17 changed files with 888 additions and 179 deletions.
2 changes: 1 addition & 1 deletion .fvm/fvm_config.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"flutterSdkVersion": "3.13.1",
"flutterSdkVersion": "3.13.5",
"flavors": {}
}
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## Version 0.16.3
- adds more diff result reporting options (cli, json, markdown)

## Version 0.16.2
- fixes relative path handling in package config (leading to unresolvable types)

Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ Usage: dart-apitool diff [arguments]
--[no-]remove-example Removes examples from the package to analyze.
(defaults to on)
--[no-]ignore-requiredness Whether to ignore the required aspect of interfaces (yielding less strict version bump requirements)
--report-format Which output format should be used
[cli (default), markdown, json]
--report-file-path Where to store the report file (no effect on cli option)
```

## Integration
Expand Down
34 changes: 2 additions & 32 deletions bin/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,41 +7,11 @@ import 'package:colorize/colorize.dart';
import 'package:colorize_lumberdash/colorize_lumberdash.dart';
import 'package:dart_apitool/api_tool_cli.dart';
import 'package:lumberdash/lumberdash.dart';
import 'package:pubspec_parse/pubspec_parse.dart';

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

Future<String> _getOwnVersion() async {
String? result;
final mainFilePath = Platform.script.toFilePath();
final pubspecFile =
File(p.join(p.dirname(mainFilePath), '..', 'pubspec.yaml'));
if (await pubspecFile.exists()) {
final yamlContent = await pubspecFile.readAsString();
final pubSpec = Pubspec.parse(yamlContent);
result = pubSpec.version?.canonicalizedVersion;
}
if (result == null) {
// if we are in a pub global environment we have to read our version from the pubspec.lock file
final pubspecLockFile =
File(p.join(p.dirname(mainFilePath), '..', 'pubspec.lock'));
if (await pubspecLockFile.exists()) {
final pubspecLockContent = await pubspecLockFile.readAsString();
final pubspecLockDom = loadYaml(pubspecLockContent);
result = pubspecLockDom['packages']['dart_apitool']['version'];
}
}
if (result == null) {
return 'UNKNOWN VERSION';
}
return result;
}

void main(List<String> arguments) async {
putLumberdashToWork(withClients: [ColorizeLumberdash()]);
final runner = CommandRunner<int>('dart-apitool', '''
dart-apitool (${Colorize(await _getOwnVersion()).bold()})
dart-apitool (${Colorize(await getOwnVersion()).bold()})
A set of utilities for Package APIs.
''')
Expand All @@ -52,7 +22,7 @@ A set of utilities for Package APIs.
try {
final argParseResult = runner.argParser.parse(arguments);
if (argParseResult['version']) {
print(await _getOwnVersion());
print(await getOwnVersion());
exit(0);
}
final exitCode = await runner.run(arguments);
Expand Down
131 changes: 52 additions & 79 deletions lib/src/cli/commands/diff_command.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import 'dart:io';

import 'package:args/command_runner.dart';
import 'package:colorize/colorize.dart';
import 'package:console/console.dart';
import 'package:dart_apitool/api_tool.dart';
import 'package:dart_apitool/src/diff/report/diff_reporter.dart';
import 'package:dart_apitool/src/diff/report/json_diff_reporter.dart';
import 'package:dart_apitool/src/diff/report/report_format.dart';

import '../../diff/report/console_diff_reporter.dart';
import '../../diff/report/markdown_diff_reporter.dart';
import '../package_ref.dart';
import 'command_mixin.dart';
import 'version_check.dart';
Expand All @@ -20,6 +23,8 @@ String _optionNameCheckSdkVersion = 'check-sdk-version';
String _optionNameDependencyCheckMode = 'dependency-check-mode';
String _optionNameRemoveExample = 'remove-example';
String _optionNameIgnoreRequiredness = 'ignore-requiredness';
String _optionReportFormat = 'report-format';
String _optionReportPath = 'report-file-path';

/// command for diffing two packages
class DiffCommand extends Command<int> with CommandMixin {
Expand Down Expand Up @@ -97,12 +102,37 @@ You may want to do this if you want to make sure
defaultsTo: false,
negatable: true,
);
argParser.addOption(
_optionReportFormat,
help: 'Which output format should be used',
defaultsTo: ReportFormat.cli.name,
allowed: ReportFormat.values.map((e) => e.name),
mandatory: false,
);
argParser.addOption(
_optionReportPath,
help: 'Where to store the report file (no effect on cli option)',
mandatory: false,
);
}

@override
Future<int> run() async {
final oldPackageRef = PackageRef(argResults![_optionNameOld]);
final newPackageRef = PackageRef(argResults![_optionNameNew]);
final outputFormatter = ReportFormat.values.firstWhere(
(element) => element.name == argResults![_optionReportFormat]);
final outputFile = argResults![_optionReportPath];

if (outputFormatter != ReportFormat.cli && outputFile == null) {
throw 'You need to define an output file using the $_optionReportPath parameter when not using the cli option';
}

if (outputFormatter == ReportFormat.cli && outputFile != null) {
stdout.writeln(
'WARNING: $_optionReportPath has no effect because $_optionReportFormat is set to cli');
}

final versionCheckMode = VersionCheckMode.values.firstWhere(
(element) => element.name == argResults![_optionNameVersionCheckMode]);
final ignorePrerelease = argResults![_optionNameIgnorePrerelease] as bool;
Expand Down Expand Up @@ -149,29 +179,27 @@ You may want to do this if you want to make sure
final diffResult =
differ.diff(oldApi: oldPackageApi, newApi: newPackageApi);

stdout.writeln();

// print the diffs
if (diffResult.hasChanges) {
final breakingChanges = _printApiChangeNode(diffResult.rootNode, true);
if (breakingChanges == null) {
stdout.writeln('No breaking changes!');
} else {
stdout.write(breakingChanges);
DiffReporter reporter = (() {
switch (outputFormatter) {
case ReportFormat.cli:
return ConsoleDiffReporter();
case ReportFormat.markdown:
return MarkdownDiffReporter(
oldPackageRef: oldPackageRef,
newPackageRef: newPackageRef,
outputFile: File(outputFile));
case ReportFormat.json:
return JsonDiffReporter(
oldPackageRef: oldPackageRef,
newPackageRef: newPackageRef,
outputFile: File(outputFile));
default:
throw 'Unknown format speicified $outputFormatter';
}
final nonBreakingChanges =
_printApiChangeNode(diffResult.rootNode, false);
if (nonBreakingChanges == null) {
stdout.writeln('No non-breaking changes!');
} else {
stdout.write(nonBreakingChanges);
}
stdout.writeln();
stdout.writeln(
'To learn more about the detected changes visit: https://github.com/bmw-tech/dart_apitool/blob/main/readme/change_codes.md');
} else {
stdout.writeln('No changes detected!');
}
})();

stdout.writeln('-- Generating report using: ${reporter.reporterName} --');
await reporter.generateReport(diffResult);

if (versionCheckMode != VersionCheckMode.none &&
!VersionCheck.versionChangeMatchesChanges(
Expand All @@ -185,59 +213,4 @@ You may want to do this if you want to make sure

return 0;
}

String _getDeclarationNodeHeadline(Declaration declaration) {
var prefix = '';
if (declaration is ExecutableDeclaration) {
switch (declaration.type) {
case ExecutableType.constructor:
prefix = 'Constructor ';
break;
case ExecutableType.method:
prefix = 'Method ';
break;
}
} else if (declaration is FieldDeclaration) {
prefix = 'Field ';
} else if (declaration is InterfaceDeclaration) {
prefix = 'Class ';
}
return prefix + declaration.name;
}

String? _printApiChangeNode(ApiChangeTreeNode node, bool breaking) {
Map nodeToTree(ApiChangeTreeNode n, {String? labelOverride}) {
final relevantChanges = n.changes.where((c) => c.isBreaking == breaking);
final changeNodes = relevantChanges.map((c) =>
'${Colorize(c.changeDescription).italic()} (${c.changeCode.code})${c.isBreaking ? '' : c.type.requiresMinorBump ? ' (minor)' : ' (patch)'}');
final childNodes = n.children.values
.map((value) => nodeToTree(value))
.where((element) => element.isNotEmpty);
final allChildren = [
...changeNodes,
...childNodes,
];
if (allChildren.isEmpty) {
return {};
}
return {
'label': Colorize(labelOverride ??
(n.nodeDeclaration == null
? ''
: _getDeclarationNodeHeadline(n.nodeDeclaration!)))
.bold()
.toString(),
'nodes': allChildren,
};
}

final nodes = nodeToTree(node,
labelOverride: breaking ? 'BREAKING CHANGES' : 'Non-Breaking changes');

if (nodes.isEmpty) {
return null;
}

return createTree(nodes);
}
}
Loading

0 comments on commit b4c1d34

Please sign in to comment.