From 30a50e02cf191d073f9f64a442696ec222ee70b1 Mon Sep 17 00:00:00 2001 From: Sinegovsky Ivan Date: Fri, 31 Jan 2020 17:26:51 +0300 Subject: [PATCH 1/6] add code climate reporter --- bin/metrics.dart | 6 +- .../code_climate/code_climate_issue.dart | 95 +++++++++++++++++++ .../code_climate/code_climate_reporter.dart | 48 ++++++++++ 3 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 lib/src/reporters/code_climate/code_climate_issue.dart create mode 100644 lib/src/reporters/code_climate/code_climate_reporter.dart diff --git a/bin/metrics.dart b/bin/metrics.dart index 2929114310..539d0a6ec8 100644 --- a/bin/metrics.dart +++ b/bin/metrics.dart @@ -4,6 +4,7 @@ import 'package:args/args.dart'; import 'package:glob/glob.dart'; import 'package:metrics/metrics_analyzer.dart'; import 'package:metrics/reporters.dart'; +import 'package:metrics/src/reporters/code_climate/code_climate_reporter.dart'; void main(List args) { const helpFlagName = 'help'; @@ -22,7 +23,7 @@ void main(List args) { abbr: 'r', help: 'The format of the output of the analysis', valueHelp: 'console', - allowed: ['console', 'json', 'html'], + allowed: ['console', 'json', 'html', 'codeclimate'], defaultsTo: 'console') ..addOption(cyclomaticComplexityThreshold, help: 'Cyclomatic complexity threshold', valueHelp: '20', defaultsTo: '20') @@ -82,6 +83,9 @@ void main(List args) { case 'html': HtmlReporter(reportConfig: config).report(runner.results()); break; + case 'codeclimate': + CodeClimateReporter(reportConfig: config).report(runner.results()); + break; default: throw ArgumentError.value(arguments[reporterOptionName], reporterOptionName); } diff --git a/lib/src/reporters/code_climate/code_climate_issue.dart b/lib/src/reporters/code_climate/code_climate_issue.dart new file mode 100644 index 0000000000..85ffd6fcfc --- /dev/null +++ b/lib/src/reporters/code_climate/code_climate_issue.dart @@ -0,0 +1,95 @@ +class CodeClimateIssue { + final String type = 'issue'; + final String check_name; + final List categories = ['Style']; + final num remediation_points = 50000; + + final CodeClimateIssueContent content; + final String description; + final CodeClimateLocation location; + + CodeClimateIssue._(this.check_name, this.content, this.description, this.location); + + factory CodeClimateIssue._create(String name, String desc, int startLine, int endLine, String fileName) { + final content = CodeClimateIssueContent(desc); + final position = CodeClimatePosition(CodeClimateLineColumnPosition(startLine), CodeClimateLineColumnPosition(endLine)); + final location = CodeClimateLocation(fileName, position); + return CodeClimateIssue._(name, content, desc, location); + } + + factory CodeClimateIssue.linesOfCode(int startLine, int endLine, String fileName, String functionName, int threshold) { + final desc = 'Function `$functionName` has ${endLine - startLine} lines of code (exceeds $threshold allowed). Consider refactoring.'; + return CodeClimateIssue._create('linesOfCode', desc, startLine, endLine, fileName); + } + + factory CodeClimateIssue.cyclomaticComplexity(int startLine, int endLine, int value, String fileName, String functionName, int threshold) { + final desc = 'Function `$functionName` has a Cyclomatic Complexity of $value (exceeds $threshold allowed). Consider refactoring.'; + return CodeClimateIssue._create('cyclomaticComplexity', desc, startLine, endLine, fileName); + } + + factory CodeClimateIssue.maintainabilityIndex(int startLine, int endLine, double value, String fileName, String functionName) { + final desc = 'Function `$functionName` has a Maintainability Index of $value (max 40 allowed). Consider refactoring.'; + return CodeClimateIssue._create('maintainabilityIndex', desc, startLine, endLine, fileName); + } + + Map toJson() { + return { + 'type': type, + 'check_name': check_name, + 'categories': categories, + 'remediation_points': remediation_points, + 'content': content.toJson(), + 'description': description, + 'location': location.toJson(), + }; + } +} + +class CodeClimateIssueContent { + final String body; + CodeClimateIssueContent(this.body); + + Map toJson() { + return { + 'body': body, + }; + } +} + +class CodeClimateLocation { + final String path; + final CodeClimatePosition positions; + CodeClimateLocation(this.path, this.positions); + + Map toJson() { + return { + 'path': path, + 'positions': positions.toJson(), + }; + } +} + +class CodeClimatePosition { + final CodeClimateLineColumnPosition begin; + final CodeClimateLineColumnPosition end; + CodeClimatePosition(this.begin, this.end); + + Map toJson() { + return { + 'begin': begin.toJson(), + 'end': end.toJson(), + }; + } +} + +class CodeClimateLineColumnPosition { + final num line; + CodeClimateLineColumnPosition(this.line); + + Map toJson() { + return { + 'line': line, + 'column': 1, + }; + } +} diff --git a/lib/src/reporters/code_climate/code_climate_reporter.dart b/lib/src/reporters/code_climate/code_climate_reporter.dart new file mode 100644 index 0000000000..93d81a5d9d --- /dev/null +++ b/lib/src/reporters/code_climate/code_climate_reporter.dart @@ -0,0 +1,48 @@ +import 'dart:convert'; + +import 'package:meta/meta.dart'; +import 'package:metrics/src/models/component_record.dart'; +import 'package:metrics/src/models/config.dart'; +import 'package:metrics/src/models/function_record.dart'; +import 'package:metrics/src/models/violation_level.dart'; +import 'package:metrics/src/reporters/code_climate/code_climate_issue.dart'; +import 'package:metrics/src/reporters/reporter.dart'; +import 'package:metrics/src/reporters/utility_selector.dart'; + +class CodeClimateReporter implements Reporter { + final Config reportConfig; + + CodeClimateReporter({@required this.reportConfig}); + + @override + void report(Iterable records) { + if (records?.isEmpty ?? true) { + return; + } + + final data = records.map(_toIssues).expand((r) => r).toList(growable: false); + for (final issue in data) { + print(json.encode(issue)); + } + } + + bool _isIssueLevel(ViolationLevel level) => level == ViolationLevel.warning || level == ViolationLevel.alarm; + + List _toIssues(ComponentRecord record) { + final result = []; + for (final key in record.records.keys) { + final func = record.records[key]; + final report = UtilitySelector.functionReport(func, reportConfig); + if (_isIssueLevel(report.linesOfCodeViolationLevel)) { + result.add(CodeClimateIssue.linesOfCode(func.firstLine, func.lastLine, record.relativePath, key, reportConfig.linesOfCodeWarningLevel)); + } + if (_isIssueLevel(report.cyclomaticComplexityViolationLevel)) { + result.add(CodeClimateIssue.cyclomaticComplexity(func.firstLine, func.lastLine, report.cyclomaticComplexity, record.relativePath, key, reportConfig.linesOfCodeWarningLevel)); + } + if (_isIssueLevel(report.maintainabilityIndexViolationLevel)) { + result.add(CodeClimateIssue.maintainabilityIndex(func.firstLine, func.lastLine, report.maintainabilityIndex, record.relativePath, key)); + } + } + return result; + } +} From 008d82410ac94e03e203f74cc1fa9599ad8d1d81 Mon Sep 17 00:00:00 2001 From: Sinegovsky Ivan Date: Mon, 3 Feb 2020 12:55:55 +0300 Subject: [PATCH 2/6] format code --- .../reporters/code_climate/code_climate_issue.dart | 14 +++++++++----- .../code_climate/code_climate_reporter.dart | 3 ++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/src/reporters/code_climate/code_climate_issue.dart b/lib/src/reporters/code_climate/code_climate_issue.dart index 85ffd6fcfc..ac52f05e4c 100644 --- a/lib/src/reporters/code_climate/code_climate_issue.dart +++ b/lib/src/reporters/code_climate/code_climate_issue.dart @@ -33,7 +33,7 @@ class CodeClimateIssue { } Map toJson() { - return { + return { 'type': type, 'check_name': check_name, 'categories': categories, @@ -47,10 +47,11 @@ class CodeClimateIssue { class CodeClimateIssueContent { final String body; + CodeClimateIssueContent(this.body); Map toJson() { - return { + return { 'body': body, }; } @@ -59,10 +60,11 @@ class CodeClimateIssueContent { class CodeClimateLocation { final String path; final CodeClimatePosition positions; + CodeClimateLocation(this.path, this.positions); Map toJson() { - return { + return { 'path': path, 'positions': positions.toJson(), }; @@ -72,10 +74,11 @@ class CodeClimateLocation { class CodeClimatePosition { final CodeClimateLineColumnPosition begin; final CodeClimateLineColumnPosition end; + CodeClimatePosition(this.begin, this.end); Map toJson() { - return { + return { 'begin': begin.toJson(), 'end': end.toJson(), }; @@ -84,10 +87,11 @@ class CodeClimatePosition { class CodeClimateLineColumnPosition { final num line; + CodeClimateLineColumnPosition(this.line); Map toJson() { - return { + return { 'line': line, 'column': 1, }; diff --git a/lib/src/reporters/code_climate/code_climate_reporter.dart b/lib/src/reporters/code_climate/code_climate_reporter.dart index 93d81a5d9d..a578016004 100644 --- a/lib/src/reporters/code_climate/code_climate_reporter.dart +++ b/lib/src/reporters/code_climate/code_climate_reporter.dart @@ -37,7 +37,8 @@ class CodeClimateReporter implements Reporter { result.add(CodeClimateIssue.linesOfCode(func.firstLine, func.lastLine, record.relativePath, key, reportConfig.linesOfCodeWarningLevel)); } if (_isIssueLevel(report.cyclomaticComplexityViolationLevel)) { - result.add(CodeClimateIssue.cyclomaticComplexity(func.firstLine, func.lastLine, report.cyclomaticComplexity, record.relativePath, key, reportConfig.linesOfCodeWarningLevel)); + result.add(CodeClimateIssue.cyclomaticComplexity( + func.firstLine, func.lastLine, report.cyclomaticComplexity, record.relativePath, key, reportConfig.linesOfCodeWarningLevel)); } if (_isIssueLevel(report.maintainabilityIndexViolationLevel)) { result.add(CodeClimateIssue.maintainabilityIndex(func.firstLine, func.lastLine, report.maintainabilityIndex, record.relativePath, key)); From cec6eec1b68d553356b855f5344542a0e6145ee8 Mon Sep 17 00:00:00 2001 From: Sinegovsky Ivan Date: Tue, 4 Feb 2020 11:05:29 +0300 Subject: [PATCH 3/6] remove import --- lib/src/reporters/code_climate/code_climate_reporter.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/src/reporters/code_climate/code_climate_reporter.dart b/lib/src/reporters/code_climate/code_climate_reporter.dart index a578016004..c9414dbedf 100644 --- a/lib/src/reporters/code_climate/code_climate_reporter.dart +++ b/lib/src/reporters/code_climate/code_climate_reporter.dart @@ -3,7 +3,6 @@ import 'dart:convert'; import 'package:meta/meta.dart'; import 'package:metrics/src/models/component_record.dart'; import 'package:metrics/src/models/config.dart'; -import 'package:metrics/src/models/function_record.dart'; import 'package:metrics/src/models/violation_level.dart'; import 'package:metrics/src/reporters/code_climate/code_climate_issue.dart'; import 'package:metrics/src/reporters/reporter.dart'; From bb0fbc7ac9c7dd367f0b1c8bc2691f7ffac01b8b Mon Sep 17 00:00:00 2001 From: Sinegovsky Ivan Date: Tue, 4 Feb 2020 11:19:09 +0300 Subject: [PATCH 4/6] fix format --- .../code_climate/code_climate_issue.dart | 21 ++++++++++++------- .../code_climate/code_climate_reporter.dart | 10 +++++---- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/lib/src/reporters/code_climate/code_climate_issue.dart b/lib/src/reporters/code_climate/code_climate_issue.dart index ac52f05e4c..0256d96b16 100644 --- a/lib/src/reporters/code_climate/code_climate_issue.dart +++ b/lib/src/reporters/code_climate/code_climate_issue.dart @@ -12,23 +12,30 @@ class CodeClimateIssue { factory CodeClimateIssue._create(String name, String desc, int startLine, int endLine, String fileName) { final content = CodeClimateIssueContent(desc); - final position = CodeClimatePosition(CodeClimateLineColumnPosition(startLine), CodeClimateLineColumnPosition(endLine)); + final position = + CodeClimatePosition(CodeClimateLineColumnPosition(startLine), CodeClimateLineColumnPosition(endLine)); final location = CodeClimateLocation(fileName, position); return CodeClimateIssue._(name, content, desc, location); } - factory CodeClimateIssue.linesOfCode(int startLine, int endLine, String fileName, String functionName, int threshold) { - final desc = 'Function `$functionName` has ${endLine - startLine} lines of code (exceeds $threshold allowed). Consider refactoring.'; + factory CodeClimateIssue.linesOfCode( + int startLine, int endLine, String fileName, String functionName, int threshold) { + final desc = + 'Function `$functionName` has ${endLine - startLine} lines of code (exceeds $threshold allowed). Consider refactoring.'; return CodeClimateIssue._create('linesOfCode', desc, startLine, endLine, fileName); } - factory CodeClimateIssue.cyclomaticComplexity(int startLine, int endLine, int value, String fileName, String functionName, int threshold) { - final desc = 'Function `$functionName` has a Cyclomatic Complexity of $value (exceeds $threshold allowed). Consider refactoring.'; + factory CodeClimateIssue.cyclomaticComplexity( + int startLine, int endLine, int value, String fileName, String functionName, int threshold) { + final desc = + 'Function `$functionName` has a Cyclomatic Complexity of $value (exceeds $threshold allowed). Consider refactoring.'; return CodeClimateIssue._create('cyclomaticComplexity', desc, startLine, endLine, fileName); } - factory CodeClimateIssue.maintainabilityIndex(int startLine, int endLine, double value, String fileName, String functionName) { - final desc = 'Function `$functionName` has a Maintainability Index of $value (max 40 allowed). Consider refactoring.'; + factory CodeClimateIssue.maintainabilityIndex( + int startLine, int endLine, double value, String fileName, String functionName) { + final desc = + 'Function `$functionName` has a Maintainability Index of $value (max 40 allowed). Consider refactoring.'; return CodeClimateIssue._create('maintainabilityIndex', desc, startLine, endLine, fileName); } diff --git a/lib/src/reporters/code_climate/code_climate_reporter.dart b/lib/src/reporters/code_climate/code_climate_reporter.dart index c9414dbedf..acec3c40b7 100644 --- a/lib/src/reporters/code_climate/code_climate_reporter.dart +++ b/lib/src/reporters/code_climate/code_climate_reporter.dart @@ -33,14 +33,16 @@ class CodeClimateReporter implements Reporter { final func = record.records[key]; final report = UtilitySelector.functionReport(func, reportConfig); if (_isIssueLevel(report.linesOfCodeViolationLevel)) { - result.add(CodeClimateIssue.linesOfCode(func.firstLine, func.lastLine, record.relativePath, key, reportConfig.linesOfCodeWarningLevel)); + result.add(CodeClimateIssue.linesOfCode( + func.firstLine, func.lastLine, record.relativePath, key, reportConfig.linesOfCodeWarningLevel)); } if (_isIssueLevel(report.cyclomaticComplexityViolationLevel)) { - result.add(CodeClimateIssue.cyclomaticComplexity( - func.firstLine, func.lastLine, report.cyclomaticComplexity, record.relativePath, key, reportConfig.linesOfCodeWarningLevel)); + result.add(CodeClimateIssue.cyclomaticComplexity(func.firstLine, func.lastLine, report.cyclomaticComplexity, + record.relativePath, key, reportConfig.linesOfCodeWarningLevel)); } if (_isIssueLevel(report.maintainabilityIndexViolationLevel)) { - result.add(CodeClimateIssue.maintainabilityIndex(func.firstLine, func.lastLine, report.maintainabilityIndex, record.relativePath, key)); + result.add(CodeClimateIssue.maintainabilityIndex( + func.firstLine, func.lastLine, report.maintainabilityIndex, record.relativePath, key)); } } return result; From d54d20e3ec280b86bfa23487bde7852052458b69 Mon Sep 17 00:00:00 2001 From: Sinegovsky Ivan Date: Thu, 6 Feb 2020 11:23:28 +0300 Subject: [PATCH 5/6] fixes for codeClimate docker format --- .../reporters/code_climate/code_climate_issue.dart | 11 ++++------- .../reporters/code_climate/code_climate_reporter.dart | 4 ++-- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/src/reporters/code_climate/code_climate_issue.dart b/lib/src/reporters/code_climate/code_climate_issue.dart index 0256d96b16..3bd4b70148 100644 --- a/lib/src/reporters/code_climate/code_climate_issue.dart +++ b/lib/src/reporters/code_climate/code_climate_issue.dart @@ -1,21 +1,19 @@ class CodeClimateIssue { final String type = 'issue'; final String check_name; - final List categories = ['Style']; + final List categories = ['Complexity']; final num remediation_points = 50000; - final CodeClimateIssueContent content; final String description; final CodeClimateLocation location; - CodeClimateIssue._(this.check_name, this.content, this.description, this.location); + CodeClimateIssue._(this.check_name, this.description, this.location); factory CodeClimateIssue._create(String name, String desc, int startLine, int endLine, String fileName) { - final content = CodeClimateIssueContent(desc); final position = CodeClimatePosition(CodeClimateLineColumnPosition(startLine), CodeClimateLineColumnPosition(endLine)); final location = CodeClimateLocation(fileName, position); - return CodeClimateIssue._(name, content, desc, location); + return CodeClimateIssue._(name, desc, location); } factory CodeClimateIssue.linesOfCode( @@ -35,7 +33,7 @@ class CodeClimateIssue { factory CodeClimateIssue.maintainabilityIndex( int startLine, int endLine, double value, String fileName, String functionName) { final desc = - 'Function `$functionName` has a Maintainability Index of $value (max 40 allowed). Consider refactoring.'; + 'Function `$functionName` has a Maintainability Index of $value (min 40 allowed). Consider refactoring.'; return CodeClimateIssue._create('maintainabilityIndex', desc, startLine, endLine, fileName); } @@ -45,7 +43,6 @@ class CodeClimateIssue { 'check_name': check_name, 'categories': categories, 'remediation_points': remediation_points, - 'content': content.toJson(), 'description': description, 'location': location.toJson(), }; diff --git a/lib/src/reporters/code_climate/code_climate_reporter.dart b/lib/src/reporters/code_climate/code_climate_reporter.dart index acec3c40b7..a217360871 100644 --- a/lib/src/reporters/code_climate/code_climate_reporter.dart +++ b/lib/src/reporters/code_climate/code_climate_reporter.dart @@ -10,7 +10,7 @@ import 'package:metrics/src/reporters/utility_selector.dart'; class CodeClimateReporter implements Reporter { final Config reportConfig; - + static const String _nullCharacter = '\u0000'; CodeClimateReporter({@required this.reportConfig}); @override @@ -21,7 +21,7 @@ class CodeClimateReporter implements Reporter { final data = records.map(_toIssues).expand((r) => r).toList(growable: false); for (final issue in data) { - print(json.encode(issue)); + print(json.encode(issue) + _nullCharacter); } } From 54e265e3627b1732d2b7e9fc051460299c1ae2d1 Mon Sep 17 00:00:00 2001 From: Sinegovsky Ivan Date: Thu, 6 Feb 2020 14:32:05 +0300 Subject: [PATCH 6/6] review fixes --- CHANGELOG.md | 3 +++ bin/metrics.dart | 1 - lib/reporters.dart | 1 + lib/src/reporters/code_climate/code_climate_issue.dart | 8 ++++---- pubspec.yaml | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c914039970..386db367cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ # Changelog +## 1.1.0 +- Added support for CodeClimate + ## 1.0.0 - Initial release diff --git a/bin/metrics.dart b/bin/metrics.dart index 539d0a6ec8..0d63431e61 100644 --- a/bin/metrics.dart +++ b/bin/metrics.dart @@ -4,7 +4,6 @@ import 'package:args/args.dart'; import 'package:glob/glob.dart'; import 'package:metrics/metrics_analyzer.dart'; import 'package:metrics/reporters.dart'; -import 'package:metrics/src/reporters/code_climate/code_climate_reporter.dart'; void main(List args) { const helpFlagName = 'help'; diff --git a/lib/reporters.dart b/lib/reporters.dart index cdb3322d88..91d6540c44 100644 --- a/lib/reporters.dart +++ b/lib/reporters.dart @@ -1,4 +1,5 @@ export 'package:metrics/src/reporters/console_reporter.dart'; +export 'package:metrics/src/reporters/code_climate/code_climate_reporter.dart'; export 'package:metrics/src/reporters/html_reporter.dart'; export 'package:metrics/src/reporters/json_reporter.dart'; export 'package:metrics/src/reporters/reporter.dart'; diff --git a/lib/src/reporters/code_climate/code_climate_issue.dart b/lib/src/reporters/code_climate/code_climate_issue.dart index 3bd4b70148..22d1eb34ee 100644 --- a/lib/src/reporters/code_climate/code_climate_issue.dart +++ b/lib/src/reporters/code_climate/code_climate_issue.dart @@ -1,9 +1,9 @@ class CodeClimateIssue { - final String type = 'issue'; - final String check_name; - final List categories = ['Complexity']; - final num remediation_points = 50000; + static const String type = 'issue'; + static const Iterable categories = ['Complexity']; + static const int remediation_points = 50000; + final String check_name; final String description; final CodeClimateLocation location; diff --git a/pubspec.yaml b/pubspec.yaml index 18192dbd23..b2fd0b7509 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: metrics description: Command line tool which helps to improve code quality -version: 1.0.0 +version: 1.1.0 homepage: https://github.com/wrike/metrics author: Dmitry Krutskih