From d68f49668d48b05ed9cec02aaf46d40e2b1a3d79 Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Wed, 20 Jul 2022 09:59:23 -0400 Subject: [PATCH] add link to changes --- lib/App/Changelord.pm | 109 +------------- .../Changelord/Command/changelog-schema.yml | 6 + lib/App/Changelord/Role/Render.pm | 137 ++++++++++++++++++ t/render.t | 69 +++++++++ 4 files changed, 213 insertions(+), 108 deletions(-) create mode 100644 lib/App/Changelord/Role/Render.pm create mode 100644 t/render.t diff --git a/lib/App/Changelord.pm b/lib/App/Changelord.pm index 91cb85f..8d10443 100644 --- a/lib/App/Changelord.pm +++ b/lib/App/Changelord.pm @@ -26,114 +26,7 @@ sub _build_changelog($self) { } with 'App::Changelord::Role::ChangeTypes'; - -sub render_header($self) { - - my $output = "# Changelog"; - - 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); - } - - $output .= "\n\n"; - -} - -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; - - 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"; - $output .= ", $date" if $date; - - $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"; -} +with 'App::Changelord::Role::Render'; sub run($self) { no warnings 'utf8'; diff --git a/lib/App/Changelord/Command/changelog-schema.yml b/lib/App/Changelord/Command/changelog-schema.yml index d4958a8..e817d4b 100644 --- a/lib/App/Changelord/Command/changelog-schema.yml +++ b/lib/App/Changelord/Command/changelog-schema.yml @@ -15,6 +15,12 @@ properties: description: name of the project examples: - App::Changelord + ticket_url: + type: string + description: perl code that takes a ticket string (e.g. 'GH123') via the `$_` variable and turns it into a link. + examples: + - s!GH(\d+)!https://github.com/yanick/App-Changelord/issue/$1/ + - /^\d+$/ ? "https://.../$_" : undef change_types: type: array items: diff --git a/lib/App/Changelord/Role/Render.pm b/lib/App/Changelord/Role/Render.pm new file mode 100644 index 0000000..75d22ee --- /dev/null +++ b/lib/App/Changelord/Role/Render.pm @@ -0,0 +1,137 @@ +package App::Changelord::Role::Render; + +use v5.36.0; + +use Moo::Role; + +use List::AllUtils qw/ pairmap partition_by /; + +sub render_header ($self) { + + my $output = "# Changelog"; + + 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); + } + + $output .= "\n\n"; + +} + +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; + + 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"; + $output .= ", $date" if $date; + + $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 .= join '', map { $self->render_change($_) } @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->@*; + } + } + + my $links = ''; + $output =~ s/(\n \[.*?\]: .*?)\n/$links .= $1;''/gem; + + return $output . $links . "\n"; +} + +sub render_change ( $self, $change ) { + my $out = " * " . $change->{desc}; + + my $link = ""; + + if ( $change->{ticket} ) { + $out .= " [$change->{ticket}]"; + if ( $self->changelog->{project}{ticket_url} ) { + local $_ = $change->{ticket}; + eval $self->changelog->{project}{ticket_url}; + warn $@ if $@; + if ($_) { + $link = " [$change->{ticket}]: $_"; + $out .= "\n\n$link"; + } + } + } + + return $out . "\n"; +} + +1; diff --git a/t/render.t b/t/render.t new file mode 100644 index 0000000..2973de5 --- /dev/null +++ b/t/render.t @@ -0,0 +1,69 @@ +use Test2::V0; + +use v5.36.0; + +package TestMe { + use Moo; + with 'App::Changelord::Role::Render'; + with 'App::Changelord::Role::ChangeTypes'; + + has changelog => ( + is => 'ro', + default => sub {{ + project => { ticket_url => undef } + }} + ); + + sub set_url($self, $url) { + $self->changelog->{project}{ticket_url} = $url; + } + +} + +my $test = TestMe->new(); + +is $test->render_change( { desc => 'blah', ticket => 'GT123' } ), +<<'END', "no ticket_url"; + * blah [GT123] +END + +$test->set_url( 's!GT!https://github.com/yanick/App-Changelord/issue/!' ); + +is $test->render_change( { desc => 'blah', ticket => 'GT123' } ), +<<'END', 'with a ticket_url'; + * blah [GT123] + + [GT123]: https://github.com/yanick/App-Changelord/issue/123 +END + +$test->set_url( '$_ = undef' ); + +is $test->render_change( { desc => 'blah', ticket => 'GT123' } ), +<<'END', 'with a ticket_url, but returns nothing'; + * blah [GT123] +END + +subtest 'all links go at the bottom' => sub { + $test->set_url( 's!^!link://!' ); + + is $test->render_release({ + changes => [ + { desc => 'this', ticket => 1 }, + { desc => 'that', ticket => 2 }, + { desc => 'else' }, + ] + }), <<'END'; +## NEXT + + * this [1] + * that [2] + * else + + [1]: link://1 + [2]: link://2 +END + + +}; + +done_testing;