App-git-ship
view release on metacpan or search on metacpan
lib/App/git/ship/perl.pm view on Meta::CPAN
package App::git::ship::perl;
use Mojo::Base 'App::git::ship';
use Module::CPANfile;
use Mojo::File qw(path tempfile);
use Mojo::Util 'decode';
use POSIX qw(setlocale strftime LC_TIME);
use Pod::Markdown;
use constant DEBUG => $ENV{GIT_SHIP_DEBUG} || 0;
my $CONTRIB_END_RE = qr{^=head1};
my $CONTRIB_NAME_EMAIL_RE = qr{^(\w[\w\s]*\w) - C<(.+)>$};
my $CONTRIB_NAME_RE = qr{^(\w[\w\s]*\w)$};
my $CONTRIB_START_RE = qr{^=head1 AUTHOR};
my $VERSION_RE = qr{\W*\b(\d+\.[\d_]+)\b};
sub build {
my $self = shift;
$self->clean(0);
$self->system(prove => split /\s/, $self->config('build_test_options'))
if $self->config('build_test_options');
$self->clean(0);
$self->run_hook('before_build');
$self->_render_makefile_pl if -e 'cpanfile';
$self->_timestamp_to_changes;
$self->_update_version_info;
$self->_render_readme;
$self->_make('manifest');
$self->_make('dist', '-e');
$self->run_hook('after_build');
$self;
}
sub can_handle_project {
my ($class, $file) = @_;
return $file =~ /\.pm$/ ? 1 : 0 if $file;
return path('lib')->list_tree->grep(sub {/\.pm$/})->size;
}
sub clean {
my $self = shift;
my $all = shift // 1;
my @files = qw(Makefile Makefile.old MANIFEST MYMETA.json MYMETA.yml);
unlink 'Makefile' and $self->_make('clean') if -e 'Makefile';
push @files, qw(Changes.bak META.json META.yml) if $all;
push @files, $self->_dist_files->each;
for my $file (@files) {
next unless -e $file;
unlink $file or warn "!! rm $file: $!" and next;
say "\$ rm $file" unless $self->SILENT;
}
return $self;
}
sub ship {
my $self = shift;
my $dist_file = $self->_dist_files->[0];
my $changelog = $self->config('changelog_filename');
my $uploader;
require CPAN::Uploader;
lib/App/git/ship/perl.pm view on Meta::CPAN
@lines = ("#!start included $file\n");
local @ARGV = ($file);
push @lines, $_ while <>;
return join "", @lines, "#!end included $file\n";
}
sub _make {
my ($self, @args) = @_;
$self->_render_makefile_pl unless -e 'Makefile.PL';
$self->system(perl => 'Makefile.PL') unless -e 'Makefile';
$self->system(make => @args);
}
sub _render_makefile_pl {
my $self = shift;
my $prereqs = Module::CPANfile->load->prereqs;
my $args = {force => 1};
my $r;
$args->{PREREQ_PM} = $prereqs->requirements_for(qw(runtime requires))->as_string_hash;
$r = $prereqs->requirements_for(qw(build requires))->as_string_hash;
$args->{BUILD_REQUIRES} = $r;
$r = $prereqs->requirements_for(qw(test requires))->as_string_hash;
$args->{TEST_REQUIRES} = $r;
$args->{RECOMMENDS} = $prereqs->requirements_for(qw(runtime recommends))->as_string_hash;
$args->{CONTRIBUTORS} = [split /,\s*/, $self->config('contributors')];
$self->render_template('Makefile.PL', $args);
$self->system(qw(perl -c Makefile.PL)); # test Makefile.PL
}
sub _render_readme {
my $self = shift;
my $skip;
if (-e 'README.md') {
my $re = "# NAME[\\n\\r\\s]+@{[$self->config('project_name')]}\\s-\\s";
$skip = path('README.md')->slurp =~ m!$re! ? undef : 'Custom README.md is in place';
}
elsif (my @alternative = path->list->grep(sub {/^README/i})->each) {
$skip = "@alternative exists.";
}
if ($skip) {
say "# Will not generate README.md: $skip" unless $self->SILENT;
return;
}
open my $README, '>:encoding(UTF-8)', 'README.md' or die "Write README.md: $!";
my $parser = Pod::Markdown->new;
$parser->output_fh($README);
$parser->parse_string_document(path($self->config('main_module_path'))->slurp);
say '# Generated README.md' unless $self->SILENT;
}
sub _timestamp_to_changes {
my $self = shift;
my $changelog = $self->config('changelog_filename');
my $loc = setlocale(LC_TIME);
my $release_line;
$release_line = sub {
my $v = shift;
my $str = $self->config('new_version_format');
$str =~ s!(%-?\d*)v!{ sprintf "${1}s", $v }!e;
setlocale LC_TIME, 'C';
$str = strftime $str, localtime;
setlocale LC_TIME, $loc;
return $str;
};
local @ARGV = $changelog;
local $^I = '';
while (<>) {
$self->config(next_version => $1)
if s/^$VERSION_RE\x20*(?:Not Released)?\x20*([\r\n]+)/{ $release_line->($1) . $2 }/e;
print; # print back to same file
}
say '# Building version ', $self->config('next_version') unless $self->SILENT;
$self->abort('Unable to add timestamp to ./%s', $changelog) unless $self->config('next_version');
}
sub _update_changes {
my $self = shift;
unless (eval "require CPAN::Changes; 1") {
say "# Cannot update './Changes' without CPAN::Changes. Install using 'cpanm CPAN::Changes'."
unless $self->SILENT;
return;
}
my $changes = CPAN::Changes->load('Changes');
$changes->preamble(
'Revision history for perl distribution ' . ($self->config('project_name') =~ s!::!-!gr));
path('Changes')->spurt($changes->serialize);
say "# Generated Changes" unless $self->SILENT;
}
sub _update_version_info {
my $self = shift;
my $version = $self->config('next_version')
or $self->abort('Internal error: Are you sure Changes has a timestamp?');
local @ARGV = ($self->config('main_module_path'));
local $^I = '';
my %r;
while (<>) {
$r{pod} ||= s/$VERSION_RE/$version/ if /^=head1 VERSION/ .. $r{pod} && /^=(cut|head1)/ || eof;
$r{var} ||= s/((?:our)?\s*\$VERSION)\s*=.*/$1 = '$version';/;
print; # print back to same file
}
$self->abort('Could not update VERSION in %s', $self->config('main_module_path')) unless $r{var};
}
1;
=encoding utf8
=head1 NAME
App::git::ship::perl - Ship your Perl module
=head1 SYNOPSIS
# Set up basic files for a Perl repo
# (Not needed if you already have an existing repo)
( run in 2.589 seconds using v1.01-cache-2.11-cpan-ceb78f64989 )