Skip to content
This repository was archived by the owner on Jul 16, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# Changelog

## 1.1.0
- Added support for CodeClimate

## 1.0.0
- Initial release
5 changes: 4 additions & 1 deletion bin/metrics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ void main(List<String> 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')
Expand Down Expand Up @@ -82,6 +82,9 @@ void main(List<String> 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);
}
Expand Down
1 change: 1 addition & 0 deletions lib/reporters.dart
Original file line number Diff line number Diff line change
@@ -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';
103 changes: 103 additions & 0 deletions lib/src/reporters/code_climate/code_climate_issue.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
class CodeClimateIssue {
static const String type = 'issue';
static const Iterable<String> categories = ['Complexity'];
static const int remediation_points = 50000;

final String check_name;
final String description;
final CodeClimateLocation location;

CodeClimateIssue._(this.check_name, this.description, this.location);

factory CodeClimateIssue._create(String name, String desc, int startLine, int endLine, String fileName) {
final position =
CodeClimatePosition(CodeClimateLineColumnPosition(startLine), CodeClimateLineColumnPosition(endLine));
final location = CodeClimateLocation(fileName, position);
return CodeClimateIssue._(name, 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 (min 40 allowed). Consider refactoring.';
return CodeClimateIssue._create('maintainabilityIndex', desc, startLine, endLine, fileName);
}

Map<String, Object> toJson() {
return <String, Object>{
'type': type,
'check_name': check_name,
'categories': categories,
'remediation_points': remediation_points,
'description': description,
'location': location.toJson(),
};
}
}

class CodeClimateIssueContent {
final String body;

CodeClimateIssueContent(this.body);

Map<String, Object> toJson() {
return <String, Object>{
'body': body,
};
}
}

class CodeClimateLocation {
final String path;
final CodeClimatePosition positions;

CodeClimateLocation(this.path, this.positions);

Map<String, Object> toJson() {
return <String, Object>{
'path': path,
'positions': positions.toJson(),
};
}
}

class CodeClimatePosition {
final CodeClimateLineColumnPosition begin;
final CodeClimateLineColumnPosition end;

CodeClimatePosition(this.begin, this.end);

Map<String, Object> toJson() {
return <String, Object>{
'begin': begin.toJson(),
'end': end.toJson(),
};
}
}

class CodeClimateLineColumnPosition {
final num line;

CodeClimateLineColumnPosition(this.line);

Map<String, Object> toJson() {
return <String, Object>{
'line': line,
'column': 1,
};
}
}
50 changes: 50 additions & 0 deletions lib/src/reporters/code_climate/code_climate_reporter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
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/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;
static const String _nullCharacter = '\u0000';
CodeClimateReporter({@required this.reportConfig});

@override
void report(Iterable<ComponentRecord> 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) + _nullCharacter);
}
}

bool _isIssueLevel(ViolationLevel level) => level == ViolationLevel.warning || level == ViolationLevel.alarm;

List<CodeClimateIssue> _toIssues(ComponentRecord record) {
final result = <CodeClimateIssue>[];
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;
}
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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 <dmitry.krutskikh@team.wrike.com>

Expand Down