Compare commits

...

12 Commits

Author SHA1 Message Date
Yanick Champoux
ffe113a10c Merge branch 'documentation' 2022-07-25 12:13:03 -04:00
Yanick Champoux
8952b38553 updating the changelog 2022-07-25 12:12:57 -04:00
Yanick Champoux
868e111930 docs for ::Version 2022-07-25 12:08:20 -04:00
Yanick Champoux
92d3e10756 docs for ::Validate 2022-07-25 12:02:24 -04:00
Yanick Champoux
ec4fee9e69 docs for ::Schema 2022-07-25 11:58:17 -04:00
Yanick Champoux
3b4c019dcc docs for ::Init 2022-07-25 11:55:42 -04:00
Yanick Champoux
0fa5f2a9a4 docs for ::GitGather 2022-07-25 11:45:03 -04:00
Yanick Champoux
364ad6d587 doc for ::Bump 2022-07-25 11:40:56 -04:00
Yanick Champoux
615cc0f581 doc for ::Add 2022-07-25 11:34:24 -04:00
Yanick Champoux
d555b10db9 add --no-next for prod render 2022-07-25 11:32:27 -04:00
Yanick Champoux
dcc2f7192c make main changelord an alias for 'print' 2022-07-25 11:27:16 -04:00
Yanick Champoux
ae29ae0ebf doc for ::Print 2022-07-25 11:21:03 -04:00
15 changed files with 166 additions and 106 deletions

View File

@ -4,14 +4,16 @@ project:
homepage: https://git.babyl.ca/yanick/App-Changelord homepage: https://git.babyl.ca/yanick/App-Changelord
releases: releases:
- version: ~ - version: ~
date: ~
changes: changes:
- Initial release - Initial release
- desc: Doing the thing
change_types: change_types:
- feat: - keywords:
level: minor - feat
title: Features level: minor
keywords: [] title: Features
- fix: - keywords:
level: patch - fix
title: Bug fixes level: patch
keywords: [] title: Bug fixes

View File

@ -1,39 +1,47 @@
package App::Changelord; package App::Changelord;
# SYNOPSIS: cli-based changelog manager
# version next latest # version next latest
use 5.36.0; use 5.36.0;
use Moo; use Moo;
use CLI::Osprey; use CLI::Osprey
desc => 'changelog manager';
use YAML; use YAML;
use List::AllUtils qw/ pairmap partition_by /; use List::AllUtils qw/ pairmap partition_by /;
use App::Changelord::Role::ChangeTypes; use App::Changelord::Role::ChangeTypes;
option source => (
is => 'ro',
format => 's',
doc => 'changelog yaml file',
default => 'CHANGELOG.yml',
);
has changelog => ( is => 'lazy' );
sub _build_changelog($self) {
return YAML::LoadFile($self->source)
}
with 'App::Changelord::Role::ChangeTypes';
with 'App::Changelord::Role::Render';
sub run($self) { sub run($self) {
no warnings 'utf8'; App::Changelord::Command::Print->new(
print $self->as_markdown; parent_command => $self,
)->run;
} }
subcommand $_ => 'App::Changelord::Command::' . ucfirst $_ =~ s/-(.)/uc $1/er subcommand $_ => 'App::Changelord::Command::' . ucfirst $_ =~ s/-(.)/uc $1/er
for qw/ schema validate version bump init add git-gather /; for qw/ schema validate version bump init add git-gather print /;
1; 1;
__END__
=head1 DESCRIPTION
C<App::Changelord> offers a collection of cli commands to
interact with a YAML-based CHANGELOG file format, from which
a Markdown CHANGELOG fit for general comsumption can be generated.
See the original blog entry in the C<SEE ALSO> section for the full
motivation.
For a list of the commands, C<changelord --help>, then to
get information on the individual commands C<changelord *subcommand* --man>.
=head1 SEE ALSO
L<Changelord, registrar of deeds extraordinaire|https://techblog.babyl.ca/entry/changelord> - the introducing blog entry.

View File

@ -3,15 +3,17 @@ package App::Changelord::Command::Add;
use 5.36.0; use 5.36.0;
use Moo; use Moo;
use CLI::Osprey desc => 'add a change to the changelog'; use CLI::Osprey
desc => 'add a change to the NEXT release',
description_pod => <<'END';
Add a change entry to the NEXT release.
END
use PerlX::Maybe; use PerlX::Maybe;
use Path::Tiny; use Path::Tiny;
use App::Changelord::Command::Init; use App::Changelord::Command::Init;
has changelog => ( is => 'lazy' ); with 'App::Changelord::Role::Changelog';
sub _build_changelog ($self) { $self->parent_command->changelog }
# TODO validate the type # TODO validate the type
option type => ( option type => (
@ -48,7 +50,7 @@ sub next_release($self) {
} }
sub save_changelog($self) { sub save_changelog($self) {
my $src = $self->parent_command->source; my $src = $self->source;
path($src)->spew( App::Changelord::Command::Init::serialize_changelog($self) ); path($src)->spew( App::Changelord::Command::Init::serialize_changelog($self) );
} }
@ -63,6 +65,8 @@ sub run ($self) {
}; };
$self->save_changelog; $self->save_changelog;
say "change added to the changelog";
} }
'end of App::Changelog::Command::Add'; 'end of App::Changelog::Command::Add';

View File

@ -3,7 +3,11 @@ package App::Changelord::Command::Bump;
use 5.36.0; use 5.36.0;
use Moo; use Moo;
use CLI::Osprey desc => 'bump next version'; use CLI::Osprey desc => 'bump next version',
description_pod => <<'END';
Set a version for the NEXT release based on the types of its changes.
Also set the release date of that release to today.
END
use Path::Tiny; use Path::Tiny;
use JSON; use JSON;
@ -11,15 +15,11 @@ use YAML qw/ Bless /;
use List::AllUtils qw/ first min uniq /; use List::AllUtils qw/ first min uniq /;
use Version::Dotted::Semantic; use Version::Dotted::Semantic;
with 'App::Changelord::Role::Changelog';
with 'App::Changelord::Role::ChangeTypes'; with 'App::Changelord::Role::ChangeTypes';
has changelog => ( is => 'lazy' );
with 'App::Changelord::Role::Versions'; with 'App::Changelord::Role::Versions';
with 'App::Changelord::Role::Stats'; with 'App::Changelord::Role::Stats';
sub _build_changelog ($self) { $self->parent_command->changelog }
sub run ($self) { sub run ($self) {
my $bump = shift @ARGV; my $bump = shift @ARGV;
@ -73,7 +73,7 @@ sub run ($self) {
Bless($_)->keys( [ uniq qw/ version date changes /, sort keys %$_ ] ); Bless($_)->keys( [ uniq qw/ version date changes /, sort keys %$_ ] );
} }
path( $self->parent_command->source )->spew( YAML::Dump($change) ); path( $self->source )->spew( YAML::Dump($change) );
say "new version minted: $version"; say "new version minted: $version";
} }

View File

@ -31,7 +31,7 @@ Git messages are compared to the regular expression
configured at `project.commit_regex`. If none is found, it configured at `project.commit_regex`. If none is found, it
defaults to defaults to
^(?<type>[^:]+):(?<desc>.*?)(\[(?<ticket>[^\]]+)\])?$ ^(?<type>[^: ]+):(?<desc>.*?)(\[(?<ticket>[^\]]+)\])?$
The regular expression must capture a C<desc> field, and may The regular expression must capture a C<desc> field, and may
capture a C<type> and C<ticket> as well. capture a C<type> and C<ticket> as well.
@ -41,10 +41,7 @@ END_POD
use Path::Tiny; use Path::Tiny;
use Git::Repository; use Git::Repository;
has changelog => ( is => 'lazy' ); with 'App::Changelord::Role::Changelog';
sub _build_changelog ($self) { $self->parent_command->changelog }
with 'App::Changelord::Role::Versions'; with 'App::Changelord::Role::Versions';
with 'App::Changelord::Role::ChangeTypes'; with 'App::Changelord::Role::ChangeTypes';
@ -59,7 +56,7 @@ has commit_regex => (
sub _build_commit_regex($self) { sub _build_commit_regex($self) {
my $regex = $self->changelog->{project}{commit_regex}; my $regex = $self->changelog->{project}{commit_regex};
my $default = '^(?<type>[^:]+):(?<desc>.*?)(\[(?<ticket>[^\]]+)\])?$'; my $default = '^(?<type>[^: ]+):(?<desc>.*?)(\[(?<ticket>[^\]]+)\])?$';
if(!$regex) { if(!$regex) {
warn "project.commit_regex not configured, using the default /$default/\n"; warn "project.commit_regex not configured, using the default /$default/\n";
$regex = $default; $regex = $default;
@ -69,7 +66,7 @@ sub _build_commit_regex($self) {
sub lower_bound($self) { sub lower_bound($self) {
# either the most recent commit in the current release # either the most recent commit in the current release
my @sha1s = grep { $_ } map { $_->{commit} } $self->next_release->{changes}->@*; my @sha1s = grep { $_ } map { $_->{commit} } grep { ref } $self->next_release->{changes}->@*;
return pop @sha1s if @sha1s; return pop @sha1s if @sha1s;
@ -92,7 +89,7 @@ sub munge_message($self,$message) {
} }
sub save_changelog($self) { sub save_changelog($self) {
my $src = $self->parent_command->source; my $src = $self->source;
path($src)->spew( App::Changelord::Command::Init::serialize_changelog($self) ); path($src)->spew( App::Changelord::Command::Init::serialize_changelog($self) );
} }
@ -104,7 +101,7 @@ sub run ($self) {
# figure out lower bound # figure out lower bound
my $from = $self->lower_bound; my $from = $self->lower_bound;
say "checking from ", ( $from || 'the dawn of time' ), " on"; say "checking since ", ( $from || 'the dawn of time' );
my @messages = map { $self->munge_message($_) } $self->get_commits($from); my @messages = map { $self->munge_message($_) } $self->get_commits($from);
@ -121,7 +118,7 @@ sub run ($self) {
$self->save_changelog; $self->save_changelog;
say $self->parent_command->source, " updated"; say $self->source, " updated";
} }
1; 1;

View File

@ -12,13 +12,9 @@ use List::AllUtils qw/ first min uniq /;
use Version::Dotted::Semantic; use Version::Dotted::Semantic;
with 'App::Changelord::Role::ChangeTypes'; with 'App::Changelord::Role::ChangeTypes';
with 'App::Changelord::Role::Changelog';
has changelog => ( is => 'lazy' );
with 'App::Changelord::Role::Versions'; with 'App::Changelord::Role::Versions';
sub _build_changelog ($self) { $self->parent_command->changelog }
sub serialize_changelog($self, $changelog = undef) { sub serialize_changelog($self, $changelog = undef) {
$changelog //= $self->changelog; $changelog //= $self->changelog;
@ -42,7 +38,7 @@ sub serialize_changelog($self, $changelog = undef) {
} }
sub run ($self) { sub run ($self) {
my $src = $self->parent_command->source; my $src = $self->source;
die "file '$src' already exists, aborting\n" if -f $src; die "file '$src' already exists, aborting\n" if -f $src;
my $change = { my $change = {
@ -51,7 +47,7 @@ sub run ($self) {
homepage => undef, homepage => undef,
with_stats => 'true', with_stats => 'true',
ticket_url => undef, ticket_url => undef,
commit_regex => /^(?<type>[^:]+):(?<desc>.*?)(\[(?<ticket>[^\]]+)\])?$/, commit_regex => q/^(?<type>[^: ]+):(?<desc>.*?)(\[(?<ticket>[^\]]+)\])?$/,
}, },
change_types => $self->change_types, change_types => $self->change_types,
releases => [ releases => [

View File

@ -0,0 +1,39 @@
package App::Changelord::Command::Print;
use 5.36.0;
use Moo;
use CLI::Osprey
desc => 'print the changelog',
description_pod => <<'END';
Render the full changelog. The default is to render the changelog
in markdow, but the option C<--json> can be used to have a JSON
version instead.
To generate the changelog without the NEXT release, uses the
C<--no-next> option.
END
with 'App::Changelord::Role::Changelog';
with 'App::Changelord::Role::ChangeTypes';
with 'App::Changelord::Role::Render';
option json => (
is => 'ro',
default => 0,
doc => 'output schema as json',
);
option next => (
is => 'ro',
default => 1,
negatable => 1,
doc => 'include the NEXT release. Defaults to true.',
);
sub run($self) {
no warnings 'utf8';
print $self->as_markdown( $self->next );
}
'end of App::Changelog::Command::Print';

View File

@ -4,7 +4,14 @@ package App::Changelord::Command::Schema;
use 5.36.0; use 5.36.0;
use Moo; use Moo;
use CLI::Osprey; use CLI::Osprey
doc => 'print JSON schema for the changelog format',
description_pod => <<'END';
Print the JSON schema describing the data format used by changelord.
By defaults prints the schema in YAML. Can also be printed as JSON
via the C<--json> option.
END
use Path::Tiny; use Path::Tiny;
use JSON; use JSON;

View File

@ -3,13 +3,19 @@ package App::Changelord::Command::Validate;
use 5.36.0; use 5.36.0;
use Moo; use Moo;
use CLI::Osprey; use CLI::Osprey
doc => 'validate the changelog yaml',
description_pod => <<'END';
Validate the changelog against the JSON Schema used by changelord.
END
use Path::Tiny; use Path::Tiny;
use JSON; use JSON;
use YAML::XS; use YAML::XS;
use JSON::Schema::Modern; use JSON::Schema::Modern;
with 'App::Changelord::Role::Changelog';
option json => ( option json => (
is => 'ro', is => 'ro',
default => 0, default => 0,
@ -24,7 +30,7 @@ sub run($self) {
my $result = JSON::Schema::Modern->new( my $result = JSON::Schema::Modern->new(
output_format => 'detailed', output_format => 'detailed',
)->evaluate( )->evaluate(
$self->parent_command->changelog, $self->changelog,
YAML::XS::Load($schema), YAML::XS::Load($schema),
); );

View File

@ -13,38 +13,9 @@ use YAML::XS;
use List::AllUtils qw/ first min /; use List::AllUtils qw/ first min /;
use Version::Dotted::Semantic; use Version::Dotted::Semantic;
with 'App::Changelord::Role::Changelog';
with 'App::Changelord::Role::ChangeTypes'; with 'App::Changelord::Role::ChangeTypes';
with 'App::Changelord::Role::Versions';
has changelog => (
is => 'lazy'
);
sub _build_changelog($self){ $self->parent_command->changelog }
sub latest_version($self){
first { $_ } grep { $_ ne 'NEXT' } map { eval { $_->{version} } } $self->changelog->{releases}->@*;
}
sub next_version($self) {
my $version = Version::Dotted::Semantic->new($self->latest_version // '0.0.0');
my $upcoming = $self->changelog->{releases}[0];
if( $upcoming->{version} and $upcoming->{version} ne 'NEXT') {
$upcoming = { changes => [] };
}
my %mapping = map {
my $level = $_->{level};
map { $_ => $level } $_->{keywords}->@*
} $self->change_types->@*;
my $bump =min 2, map { $_ eq 'major' ? 0 : $_ eq 'minor' ? 1 : 2 } map { $mapping{$_->{type}} || 'patch' } $upcoming->{changes}->@*;
$version->bump($bump);
return $version->normal;
}
sub run($self) { sub run($self) {
my $param = shift @ARGV; my $param = shift @ARGV;

View File

@ -42,16 +42,18 @@ properties:
- type: object - type: object
additionalProperties: false additionalProperties: false
properties: properties:
version: { type: string } version: { type: [ 'null', string ] }
date: { type: ['null',string] } date: { type: ['null',string] }
changes: { type: 'array', items: { $ref: '#/$defs/change' } } changes: { type: 'array', items: { $ref: '#/$defs/change' } }
$defs: $defs:
change: change:
type: object oneOf:
required: [ desc ] - type: string
additionalProperties: false - type: object
properties: required: [ desc ]
desc: { type: string } additionalProperties: false
ticket: { type: [ string, 'null' ] } properties:
type: { type: [ string, 'null' ] } desc: { type: string }
commit: { type: [ string, 'null' ] } ticket: { type: [ string, 'null' ] }
type: { type: [ string, 'null' ] }
commit: { type: [ string, 'null' ] }

View File

@ -0,0 +1,21 @@
package App::Changelord::Role::Changelog;
use v5.36.0;
use Moo::Role;
use CLI::Osprey;
option source => (
is => 'ro',
format => 's',
doc => q{changelog yaml file. Defaults to the env variable $CHANGELOG, or 'CHANGELOG.yml'},
default => $ENV{CHANGELOG} || 'CHANGELOG.yml',
);
has changelog => ( is => 'lazy' );
sub _build_changelog($self) {
return YAML::LoadFile($self->source)
}
1;

View File

@ -40,14 +40,18 @@ sub render_refs ( $self, %links ) {
return $output . "\n"; return $output . "\n";
} }
sub as_markdown ($self) { sub as_markdown ($self, $with_next = 1) {
my $changelog = $self->changelog; my $changelog = $self->changelog;
my $output = $self->render_header; my $output = $self->render_header;
my $n = 0; my $n = 0;
$output .= join "\n", $output .= join "\n",
map { $self->render_release( $_, $n++ ) } $changelog->{releases}->@*; map { $self->render_release( $_, $n++ ) }
grep {
$with_next ? 1 : ( $_->{version} && $_->version ne 'NEXT' )
}
$changelog->{releases}->@*;
return $output; return $output;
} }

View File

@ -12,7 +12,7 @@ use feature 'try';
requires 'changelog'; requires 'changelog';
sub latest_version($self){ sub latest_version($self){
first { $_ } grep { $_ ne 'NEXT' } map { eval { $_->{version} } } $self->changelog->{releases}->@*; first { $_ } grep { $_ ne 'NEXT' } map { eval { $_->{version} || '' } } $self->changelog->{releases}->@*, { version => 'v0.0.0' };
} }
sub next_version($self) { sub next_version($self) {
@ -29,7 +29,10 @@ sub next_version($self) {
map { $_ => $level } $_->{keywords}->@* map { $_ => $level } $_->{keywords}->@*
} $self->change_types->@*; } $self->change_types->@*;
my $bump =min 2, map { $_ eq 'major' ? 0 : $_ eq 'minor' ? 1 : 2 } map { $mapping{$_->{type}} || 'patch' } $upcoming->{changes}->@*; no warnings;
my $bump =min 2, map { $_ eq 'major' ? 0 : $_ eq 'minor' ? 1 : 2 } map { $mapping{$_->{type}} || 'patch' }
map { ref ? $_ : { desc => $_ } }
$upcoming->{changes}->@*;
$version->bump($bump); $version->bump($bump);

View File

@ -2,9 +2,9 @@ use 5.36.0;
use Test2::V0; use Test2::V0;
use App::Changelord; use App::Changelord::Command::Print;
my $change = App::Changelord->new( my $change = App::Changelord::Command::Print->new(
changelog => { changelog => {
project => { name => 'Foo' }, project => { name => 'Foo' },
} }