2022-07-13 16:09:07 +00:00
|
|
|
package App::Changelord;
|
|
|
|
|
|
|
|
use 5.36.0;
|
|
|
|
|
|
|
|
use Moo;
|
|
|
|
use CLI::Osprey;
|
|
|
|
use YAML;
|
|
|
|
|
2022-07-13 19:00:23 +00:00
|
|
|
use List::AllUtils qw/ pairmap partition_by /;
|
|
|
|
|
2022-07-13 16:09:07 +00:00
|
|
|
option source => (
|
|
|
|
is => 'ro',
|
2022-07-13 19:00:23 +00:00
|
|
|
format => 's',
|
2022-07-13 17:18:13 +00:00
|
|
|
doc => 'changelog yaml file',
|
2022-07-13 16:09:07 +00:00
|
|
|
default => 'CHANGELOG.yml',
|
|
|
|
);
|
|
|
|
|
2022-07-13 17:18:13 +00:00
|
|
|
has changelog => (
|
2022-07-13 16:09:07 +00:00
|
|
|
lazy => 1,
|
|
|
|
is => 'ro',
|
|
|
|
default => sub($self) {
|
|
|
|
return YAML::LoadFile($self->source)
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2022-07-13 19:00:23 +00:00
|
|
|
has change_types => (
|
|
|
|
is => 'ro',
|
|
|
|
default => sub($self) {
|
|
|
|
return [
|
|
|
|
{ title => 'Features' , level => 'minor', keywords => [ 'feat' ] } ,
|
|
|
|
{ title => 'Bug fixes' , level => 'patch', keywords => [ 'fix' ] },
|
|
|
|
]
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2022-07-13 18:26:08 +00:00
|
|
|
sub render_header($self) {
|
2022-07-13 16:09:07 +00:00
|
|
|
|
|
|
|
my $output = "# Changelog";
|
|
|
|
|
2022-07-13 18:26:08 +00:00
|
|
|
my $name = $self->changelog->{project}{name};
|
|
|
|
|
|
|
|
my %links = ();
|
|
|
|
|
|
|
|
if( $self->changelog->{project}{homepage} ) {
|
|
|
|
$name = "[$name][homepage]";
|
|
|
|
$links{homepage} = $self->changelog->{project}{homepage};
|
|
|
|
}
|
|
|
|
|
|
|
|
$output .= " for $name" if $name;
|
|
|
|
|
|
|
|
if(%links) {
|
|
|
|
$output .= "\n\n";
|
|
|
|
$output .= $self->render_refs(%links);
|
|
|
|
}
|
2022-07-13 16:09:07 +00:00
|
|
|
|
|
|
|
$output .= "\n\n";
|
|
|
|
|
2022-07-13 18:26:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sub render_refs($self,%links) {
|
|
|
|
my $output = '';
|
|
|
|
|
|
|
|
for my $ref ( sort keys %links ) {
|
|
|
|
$output .= " [$ref]: $links{$ref}\n"
|
|
|
|
}
|
|
|
|
|
|
|
|
return $output . "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
sub as_markdown($self) {
|
|
|
|
my $changelog = $self->changelog;
|
|
|
|
|
|
|
|
my $output = $self->render_header;
|
|
|
|
|
2022-07-13 16:09:07 +00:00
|
|
|
my $n = 0;
|
|
|
|
$output .= join "\n", map { $self->render_release($_, $n++) } $changelog->{releases}->@*;
|
|
|
|
|
|
|
|
return $output;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub render_release($self, $release, $n=0) {
|
|
|
|
|
|
|
|
# it's a string? Okay then!
|
|
|
|
return $release unless ref $release;
|
|
|
|
|
|
|
|
my $version = $release->{version} || ( $n ? '???' : 'NEXT' );
|
|
|
|
my $date = $release->{date};
|
|
|
|
|
|
|
|
my $output = '';
|
|
|
|
|
|
|
|
$output .= "## $version";
|
2022-07-13 19:00:23 +00:00
|
|
|
$output .= ", $date" if $date;
|
2022-07-13 16:09:07 +00:00
|
|
|
|
2022-07-13 19:00:23 +00:00
|
|
|
$output .= "\n";
|
|
|
|
|
|
|
|
if( $release->{changes} ) {
|
|
|
|
my @changes = map { ref ? $_ : { desc => $_ } } $release->{changes}->@*;
|
|
|
|
|
|
|
|
my @keywords = map { $_->{keywords}->@* } $self->change_types->@*;
|
|
|
|
|
|
|
|
# find the generics
|
|
|
|
my @generics = grep {
|
|
|
|
my $type = $_->{type};
|
|
|
|
|
|
|
|
my $res = !$type;
|
|
|
|
|
|
|
|
if( $type and not grep { $type eq $_} @keywords ) {
|
|
|
|
$res = 1;
|
|
|
|
warn "change type '$type' is not recognized\n";
|
|
|
|
}
|
|
|
|
$res;
|
|
|
|
} @changes;
|
|
|
|
|
|
|
|
|
|
|
|
$output .= "\n" if @generics;
|
|
|
|
$output .= " * $_->{desc}\n" for @generics;
|
|
|
|
|
|
|
|
my %keyword_mapping = map {
|
|
|
|
my $title = $_->{title};
|
|
|
|
map { $_ => $title } $_->{keywords}->@*;
|
|
|
|
} $self->change_types->@*;
|
|
|
|
|
|
|
|
|
|
|
|
my %groups = partition_by {
|
|
|
|
no warnings qw/ uninitialized /;
|
|
|
|
$keyword_mapping{$_->{type}} || ''
|
|
|
|
} @changes;
|
|
|
|
|
|
|
|
for my $type ( $self->change_types->@* ) {
|
|
|
|
my $c = $groups{$type->{title}} or next;
|
|
|
|
$output .= "\n### $type->{title}\n\n";
|
|
|
|
$output .= $self->render_change($_) for $c->@*;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return $output . "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
sub render_change($self, $change) {
|
|
|
|
return " * " . $change->{desc} . "\n";
|
2022-07-13 16:09:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sub run($self) {
|
|
|
|
no warnings 'utf8';
|
|
|
|
print $self->as_markdown;
|
|
|
|
}
|
|
|
|
|
2022-07-13 17:18:13 +00:00
|
|
|
subcommand 'schema' => 'App::Changelord::Command::Schema';
|
2022-07-13 18:03:40 +00:00
|
|
|
subcommand 'validate' => 'App::Changelord::Command::Validate';
|
2022-07-13 17:18:13 +00:00
|
|
|
|
2022-07-13 16:09:07 +00:00
|
|
|
'end of App::Changeman';
|