Skip to content

Commit 202c1f4

Browse files
committed
feat(changelog_cli): enhance generate command with new options for grouping and formatting changelog entries (--group-by, --date-format, --date-format-locale)
1 parent d089d38 commit 202c1f4

9 files changed

Lines changed: 352 additions & 62 deletions

File tree

changelog_cli/lib/src/commands/generate_command.dart

Lines changed: 62 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import 'package:args/command_runner.dart';
44
import 'package:changelog_cli/src/model/model.dart';
55
import 'package:changelog_cli/src/printers/printers.dart';
6+
import 'package:changelog_cli/src/processors/preprocessor.dart';
67
import 'package:conventional_commit/conventional_commit.dart';
78
import 'package:git/git.dart';
89
import 'package:mason_logger/mason_logger.dart';
@@ -20,12 +21,12 @@ class GenerateCommand extends Command<int> {
2021
argParser.addOption(
2122
'start',
2223
abbr: 's',
23-
help: 'Start git reference (e.g. commit SHA)',
24+
help: 'Start git reference (e.g. commit SHA or tag)',
2425
);
2526
argParser.addOption(
2627
'end',
2728
abbr: 'e',
28-
help: 'End git reference (e.g. commit SHA)',
29+
help: 'End git reference (e.g. commit SHA or tag)',
2930
);
3031
argParser.addMultiOption(
3132
'include',
@@ -50,7 +51,7 @@ class GenerateCommand extends Command<int> {
5051
argParser.addOption(
5152
'version',
5253
abbr: 'v',
53-
help: 'Manually specify version',
54+
help: 'Manually specify version printed in the header of the changelog',
5455
defaultsTo: '',
5556
);
5657
argParser.addOption(
@@ -78,6 +79,29 @@ class GenerateCommand extends Command<int> {
7879
'slack-markdown',
7980
],
8081
);
82+
argParser.addOption(
83+
'group-by',
84+
abbr: 'g',
85+
help: 'Group entries by type',
86+
allowed: [
87+
'date-asc',
88+
'date-desc',
89+
'scope-asc',
90+
'scope-desc',
91+
],
92+
);
93+
argParser.addOption(
94+
'date-format',
95+
help: 'Date format, providing empty skips date formatting. \nNeeds '
96+
'to be valid ISO date format e.g. yyyy-MM-dd, yyyy-MM-dd HH:mm:ss. '
97+
'By default it does not print any dates. Uses system-default locale.',
98+
defaultsTo: '',
99+
);
100+
argParser.addOption(
101+
'date-format-locale',
102+
help: 'Date format passed to the date formatting, expected format: xx_XX',
103+
defaultsTo: 'en_US',
104+
);
81105
}
82106

83107
@override
@@ -91,25 +115,25 @@ class GenerateCommand extends Command<int> {
91115

92116
@override
93117
Future<int> run() async {
94-
final start = argResults?['start'] as String?;
95-
final end = argResults?['end'] as String?;
96-
final include = argResults?['include'] as List<String>? ?? [];
97-
final version = argResults?['version'] as String;
98-
final limit = int.tryParse(argResults?['limit'] as String? ?? '');
118+
if (argResults == null) {
119+
return ExitCode.usage.code;
120+
}
121+
122+
final configuration = GenerateConfiguration.fromArgs(argResults!);
99123

100124
final path = await getGitPath();
101125

102126
if (path != null) {
103127
final String? startRef;
104-
if (argResults?['auto'] == true) {
128+
if (configuration.auto) {
105129
startRef = await getLastTag(path: path);
106130
} else {
107-
startRef = start;
131+
startRef = configuration.start;
108132
}
109133

110134
final commits = await getCommits(
111135
start: startRef,
112-
end: end,
136+
end: configuration.end,
113137
path: path,
114138
);
115139
if (commits.isEmpty) {
@@ -122,29 +146,28 @@ class GenerateCommand extends Command<int> {
122146
final conventionalCommit = ConventionalCommit.tryParse(v.value.message);
123147

124148
if (conventionalCommit != null) {
125-
if (include.contains(conventionalCommit.type)) {
149+
if (configuration.include.contains(conventionalCommit.type)) {
126150
list.add(
127151
ChangelogEntry(
128152
conventionalCommit: conventionalCommit,
129153
ref: v.key,
130154
commit: v.value,
155+
date: parseDate(v.value.author),
131156
),
132157
);
133158
}
134159
}
135160
}
136161
_logger.detail('Found ${list.length} conventional commits');
137162

138-
final printer = getPrinter(argResults?['printer'] as String?);
163+
final processedList = Preprocessor.processGitHistory(list, configuration);
139164

140-
final output = printer.print(
141-
entries: list,
142-
version: version,
143-
types: include,
144-
);
165+
final printer = getPrinter(configuration);
145166

146-
if (limit != null && limit > 0) {
147-
final limitClamped = limit.clamp(0, output.length);
167+
final output = printer.print(entries: processedList);
168+
169+
if (configuration.limit > 0) {
170+
final limitClamped = configuration.limit.clamp(0, output.length);
148171
_logger.info(output.substring(0, limitClamped));
149172
} else {
150173
_logger.info(output);
@@ -214,14 +237,25 @@ class GenerateCommand extends Command<int> {
214237
}
215238
}
216239

217-
Printer getPrinter(String? argResult) {
218-
switch (argResult) {
219-
case 'markdown':
220-
return MarkdownPrinter();
221-
case 'slack-markdown':
222-
return SlackMarkdownPrinter();
223-
default:
224-
return SimplePrinter();
240+
Printer getPrinter(GenerateConfiguration configuration) {
241+
switch (configuration.printer) {
242+
case PrinterType.markdown:
243+
return MarkdownPrinter(configuration: configuration);
244+
case PrinterType.slackMarkdown:
245+
return SlackMarkdownPrinter(configuration: configuration);
246+
case PrinterType.simple:
247+
return SimplePrinter(configuration: configuration);
225248
}
226249
}
227250
}
251+
252+
DateTime? parseDate(String gitAuthor) {
253+
final author = gitAuthor.split(' ');
254+
final oneBeforeLast = author.length - 2;
255+
final date = author[oneBeforeLast];
256+
final seconds = int.tryParse(date);
257+
if (seconds != null) {
258+
return DateTime.fromMillisecondsSinceEpoch(seconds * 1000);
259+
}
260+
return null;
261+
}

changelog_cli/lib/src/model/changelog_entry.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ class ChangelogEntry extends Equatable {
77
required this.conventionalCommit,
88
required this.ref,
99
required this.commit,
10+
required this.date,
1011
});
1112

1213
final ConventionalCommit conventionalCommit;
1314
final String ref;
1415
final Commit commit;
16+
final DateTime? date;
1517

1618
String get message =>
1719
conventionalCommit.description ?? conventionalCommit.header;
@@ -22,5 +24,22 @@ class ChangelogEntry extends Equatable {
2224
conventionalCommit,
2325
ref,
2426
commit,
27+
date,
28+
];
29+
}
30+
31+
class ChangelogEntryGroup extends Equatable {
32+
const ChangelogEntryGroup({
33+
required this.entries,
34+
required this.type,
35+
});
36+
37+
final List<ChangelogEntry> entries;
38+
final String type;
39+
40+
@override
41+
List<Object?> get props => [
42+
entries,
43+
type,
2544
];
2645
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// ignore_for_file: avoid_equals_and_hash_code_on_mutable_classes
2+
3+
import 'package:args/args.dart';
4+
import 'package:intl/intl.dart';
5+
6+
enum GroupBy {
7+
dateAsc,
8+
dateDesc,
9+
scopeAsc,
10+
scopeDesc,
11+
}
12+
13+
GroupBy getGroupByFromString(String input) {
14+
switch (input) {
15+
case 'date-asc':
16+
return GroupBy.dateAsc;
17+
case 'date-desc':
18+
return GroupBy.dateDesc;
19+
case 'scope-asc':
20+
return GroupBy.scopeAsc;
21+
case 'scope-desc':
22+
return GroupBy.scopeDesc;
23+
default:
24+
return GroupBy.dateAsc;
25+
}
26+
}
27+
28+
enum PrinterType {
29+
simple,
30+
markdown,
31+
slackMarkdown;
32+
}
33+
34+
PrinterType getPrinterFromString(String input) {
35+
switch (input) {
36+
case 'simple':
37+
return PrinterType.simple;
38+
case 'markdown':
39+
return PrinterType.markdown;
40+
case 'slack-markdown':
41+
return PrinterType.slackMarkdown;
42+
default:
43+
return PrinterType.simple;
44+
}
45+
}
46+
47+
class GenerateConfiguration {
48+
const GenerateConfiguration({
49+
required this.start,
50+
required this.end,
51+
required this.path,
52+
required this.include,
53+
required this.printer,
54+
required this.groupBy,
55+
required this.auto,
56+
required this.version,
57+
required this.limit,
58+
required this.dateFormat,
59+
});
60+
61+
factory GenerateConfiguration.fromArgs(
62+
ArgResults args,
63+
) {
64+
final start = args['start'] as String?;
65+
final end = args['end'] as String?;
66+
final path = args['path'] as String?;
67+
final include = args['include'] as List<String>?;
68+
final printer = args['printer'] as String?;
69+
final groupBy = args['group-by'] as String?;
70+
final auto = args['auto'] as bool?;
71+
final version = args['version'] as String?;
72+
final limit = int.tryParse(args['limit'] as String? ?? '');
73+
final dateFormat = args['date-format'] as String?;
74+
75+
final matchingPrinter = getPrinterFromString(printer ?? '');
76+
77+
final matchingGroupBy = getGroupByFromString(groupBy ?? '');
78+
79+
final locale = args['date-format-locale'] as String? ?? 'en_US';
80+
Intl.defaultLocale = locale;
81+
82+
return GenerateConfiguration(
83+
start: start ?? '',
84+
end: end ?? '',
85+
path: path ?? '',
86+
include: include ?? [],
87+
printer: matchingPrinter,
88+
groupBy: matchingGroupBy,
89+
auto: auto ?? false,
90+
version: version ?? '',
91+
limit: limit ?? 0,
92+
dateFormat: dateFormat ?? '',
93+
);
94+
}
95+
96+
final String start;
97+
final String end;
98+
final String path;
99+
final List<String> include;
100+
final PrinterType printer;
101+
final GroupBy groupBy;
102+
final bool auto;
103+
final String version;
104+
final int limit;
105+
final String dateFormat;
106+
107+
String formatDateTime(DateTime? date) {
108+
if (date == null || dateFormat.isEmpty) {
109+
return '';
110+
}
111+
return DateFormat(dateFormat).format(date);
112+
}
113+
114+
@override
115+
int get hashCode => Object.hash(
116+
start,
117+
end,
118+
path,
119+
include,
120+
printer,
121+
groupBy,
122+
auto,
123+
version,
124+
limit,
125+
dateFormat,
126+
);
127+
128+
@override
129+
bool operator ==(Object other) =>
130+
identical(this, other) ||
131+
other is GenerateConfiguration &&
132+
runtimeType == other.runtimeType &&
133+
start == other.start &&
134+
end == other.end &&
135+
path == other.path &&
136+
include == other.include &&
137+
printer == other.printer &&
138+
groupBy == other.groupBy &&
139+
auto == other.auto &&
140+
version == other.version &&
141+
limit == other.limit &&
142+
dateFormat == other.dateFormat;
143+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export 'changelog_entry.dart';
2+
export 'generate_configuration.dart';

0 commit comments

Comments
 (0)