# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#
# file: lib/Dist/Zilla/PluginBundle/Author/VDB.pm
#pod =encoding UTF-8
#pod
#pod =head1 COPYRIGHT AND LICENSE
#pod
#pod Copyright © 2015 Van de Bugger
#pod
#pod This file is part of perl-Dist-Zilla-PluginBundle-Author-VDB.
#pod
#pod perl-Dist-Zilla-PluginBundle-Author-VDB is free software: you can redistribute it and/or modify
#pod it under the terms of the GNU General Public License as published by the Free Software
#pod Foundation, either version 3 of the License, or (at your option) any later version.
#pod
#pod perl-Dist-Zilla-PluginBundle-Author-VDB is distributed in the hope that it will be useful, but
#pod WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
#pod PARTICULAR PURPOSE. See the GNU General Public License for more details.
#pod
#pod You should have received a copy of the GNU General Public License along with
#pod perl-Dist-Zilla-PluginBundle-Author-VDB. If not, see <http://www.gnu.org/licenses/>.
#pod
#pod =cut
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#pod =head1 DESCRIPTION
#pod
#pod It is unlikely that someone else will want to use it, so I will not bother with documenting it, at
#pod least for now.
#pod
#pod =for Pod::Coverage configure
#pod
#pod =cut
use Moose;
use version 0.77;
# PODNAME: Dist::Zilla::PluginBundle::Author::VDB
# ABSTRACT: VDB's plugin bundle
our $VERSION = 'v0.8.6'; # VERSION
# These modules used by the bundle directly.
use Carp qw{ croak };
use Sub::Exporter::ForMethods qw{ method_installer };
use Data::Section { installer => method_installer }, -setup;
# These modules are used by hooks. Require all the modules explicitly now to avoid unexpected
# failures in the middle of build or release.
use File::chdir ();
use IPC::Run3 ();
use Path::Tiny 0.070 ();
# Implicitly required module.
# --------------------------------------------------------------------------------------------------
#pod =option minimum_perl
#pod
#pod Optional, default value C<5.006>.
#pod
#pod =cut
has minimum_perl => (
is => 'ro',
isa => 'Str',
lazy => 1,
default => sub {
my ( $self ) = @_;
return $self->payload->{ minimum_perl } // '5.006';
},
);
# --------------------------------------------------------------------------------------------------
#pod =option copying
#pod
#pod Name of POD file to generate distribution F<COPYING> text file. Empty value disables generation
#pod F<COPYING> file.
#pod
#pod C<Str>, optional, default value C<doc/copying.pod>.
#pod
#pod =cut
has copying => (
is => 'ro',
isa => 'Str',
lazy => 1,
default => sub {
my ( $self ) = @_;
return $self->payload->{ copying } // 'doc/copying.pod';
},
);
# --------------------------------------------------------------------------------------------------
#pod =option readme
#pod
#pod Names of POD files to generate distribution F<README> text file. This is a multi-value option.
#pod Empty values are ignored. Empty list disables generating F<README>file.
#pod
#pod C<ArrayRef[Str]>, optional, default value C<[ 'doc/what.pod', 'doc/why.pod', 'doc/naming.pod', 'doc/forms.pod', 'doc/source.pod', 'doc/distribution.pod', 'doc/installing.pod', 'doc/hacking.pod', 'doc/documentation.pod', 'doc/feedback.pod', 'doc/glossary.pod' ]>.
#pod
#pod =cut
has readme => (
is => 'ro',
isa => 'Maybe[ArrayRef[Str]]',
lazy => 1,
default => sub {
my ( $self ) = @_;
my $readme = $self->payload->{ readme } // [ 'doc/what.pod', 'doc/why.pod', 'doc/naming.pod', 'doc/forms.pod', 'doc/source.pod', 'doc/distribution.pod', 'doc/installing.pod', 'doc/hacking.pod', 'doc/documentation.pod', 'doc/feedback.pod', 'doc/glossary.pod' ];
$readme = [ grep( { $_ ne '' } @$readme ) ]; # Ignore empty items.
if ( not @$readme ) {
$readme = undef;
};
return $readme;
},
);
# --------------------------------------------------------------------------------------------------
#pod =option local_release
#pod
#pod If true, release will be a local one, i. e. no external operations will be done: C<UploadToCPAN>
#pod and C<hg push> will be skipped, <hg tag> will create a local tag.
#pod
#pod Option can be set trough F<dist.ini> file or with C<DZIL_LOCAL_RELEASE> environment variable.
#pod
#pod Optional, default value is 0.
#pod
#pod =cut
has local_release => (
is => 'ro',
isa => 'Bool',
lazy => 1,
default => sub {
my ( $self ) = @_;
return $self->payload->{ local_release } // $ENV{ DZIL_LOCAL_RELEASE };
},
);
# --------------------------------------------------------------------------------------------------
#pod =option archive
#pod
#pod Directory to archive files to. If empty, release will not be archived. If such directory does not
#pod exist, it will be created before release.
#pod
#pod Optional, default value C<".releases">.
#pod
#pod =cut
has archive => (
is => 'ro',
isa => 'Str',
lazy => 1,
default => sub {
my ( $self ) = @_;
return $self->payload->{ archive } // ".releases";
},
);
# --------------------------------------------------------------------------------------------------
#pod =option templates
#pod
#pod This option will be passed to C<Templates> plugin. If you no not want C<Templates> to process
#pod files, specify C<:NoFiles>. This is multi-value option (i. e. may be specified several times).
#pod
#pod Optional, default value C<[ ':InstallModules' ]>.
#pod
#pod =cut
has templates => (
is => 'ro',
isa => 'Maybe[ArrayRef[Str]]',
lazy => 1,
default => sub {
my ( $self ) = @_;
my $templates = $self->payload->{ templates } // [ ':InstallModules' ];
$templates = [ grep( { $_ ne '' } @$templates ) ]; # Ignore empty items.
if ( not @$templates ) {
$templates = undef;
};
return $templates;
},
);
# --------------------------------------------------------------------------------------------------
#pod =option unwanted_module
#pod
#pod =option unwanted_modules
#pod
#pod TODO C<[ qw{ DPP Data::Printer } ]>.
#pod
#pod =cut
has unwanted_modules => (
isa => 'ArrayRef[Str]',
is => 'ro',
lazy => 1,
default => sub {
my ( $self ) = @_;
my $p = $self->payload;
my $u1 = $p->{ unwanted_module };
my $u2 = $p->{ unwanted_modules };
return $u1 || $u2 ? [ $u1 ? @$u1 : (), $u2 ? @$u2 : () ] : [ qw{ DPP Data::Printer } ];
},
);
# --------------------------------------------------------------------------------------------------
#pod =option spellchecker
#pod
#pod Command to run spellchecker. Spellchecker command is expected to read text from stdin, and print to
#pod stdout misspelled words. If empty, spellchecking will be skipped. This involve C<Test::PodSpelling>
#pod plugin and internally implemented checking the F<Changes> file.
#pod
#pod Optional, default value C<aspell list -l en -p ./xt/aspell.en.pws>.
#pod
#pod =cut
has spellchecker => (
is => 'ro',
isa => 'Str',
lazy => 1,
default => sub {
my ( $self ) = @_;
return $self->payload->{ spellchecker } // 'aspell list -l en -p ./xt/aspell.en.pws';
# Leading dot (in `./xt/aspell.en.pws`) is important! Whitout the dot `aspell`
# fails to find the dictionary.
},
);
# --------------------------------------------------------------------------------------------------
#pod =method mvp_multivalue_args
#pod
#pod =cut
sub mvp_multivalue_args {
return qw{ templates unwanted_module unwanted_modules readme };
};
# --------------------------------------------------------------------------------------------------
#pod =method _quote
#pod
#pod Convert an attribute to a form suitable for using in source. C<Str> attribute is converted into a
#pod string literal, C<ArrayRef> attribute is converted to a list of string literals.
#pod
#pod =cut
sub _quote {
my ( $self, @args ) = @_;
my @names;
for my $arg ( @args ) {
for ( ref( $arg ) eq 'ARRAY' ? @$arg : $arg ) {
my $name = $_;
$name =~ s{([\\'])}{\$1}gx;
push( @names, "'$name'" );
};
};
return join( ', ', @names );
};
# --------------------------------------------------------------------------------------------------
# Helper func: Iterate through distribution prerequisities.
sub MY::prereqs($$) { ## no critic ( ProhibitSubroutinePrototypes )
my ( $plugin, $callback ) = @_;
my $prereqs = $plugin->zilla->prereqs->cpan_meta_prereqs;
for my $phase ( $prereqs->__legal_phases ) {
for my $type ( $prereqs->__legal_types ) {
my $reqs = $prereqs->requirements_for( $phase, $type ) or next;
for my $module ( keys( %{ $reqs->{ requirements } } ) ) {
$callback->( $plugin, $module, $phase, $type, $reqs );
};
};
};
return;
};
sub MY::file {
my ( $plugin, $name ) = @_;
our $Self; ## no critic ( ProhibitPackageVars )
my $data = $Self->merged_section_data;
my $root = path( $plugin->zilla->root );
my $file;
if ( $root->child( $name )->exists ) {
$file = Dist::Zilla::File::OnDisk->new( {
name => $name,
} );
} elsif ( $data->{ $name } ) {
$file = Dist::Zilla::File::InMemory->new( {
name => $name,
content => ${ $data->{ $name } },
} );
} else {
croak "$name: file not found";
};
return $file;
};
# --------------------------------------------------------------------------------------------------
sub configure {
my ( $self ) = @_;
our $Self = $self; ## no critic ( ProhibitPackageVars )
# Make sure the plugin is loaded before checking its version.
$self->add_plugins(
[ 'Hook' => 'prologue' => {
# DOES NOT WORK because plugin name will be '@Author::VDB/prologue'.
'hook' => [ q{
use autodie ':all';
use IPC::System::Simple qw{ capture };
} ],
} ],
[ 'Hook::VersionProvider' => {
'hook' => [ q{
use Path::Tiny;
my $file = path( $zilla->root )->child( 'VERSION' );
( my $version = $file->slurp ) =~ s{\s+\z}{};
if ( $version !~ m{\A@{ [ Perl::Version::REGEX ] }\z} ) {
$self->abort( [
'Invalid version string "%s" at %s line 1', $version, "$file"
] );
};
$version;
} ],
} ],
[ 'Hook::Init' => 'my vars' => {
'hook' => [ q[
{ pack] . q[age MY; # Hide declaration from `new-version.t`.
our $name = $dist->name;
( our $package = $name ) =~ s{-}{::}g;
our $version = $dist->version;
our $Abstract = $dist->abstract;
our $abstract = lcfirst( $Abstract );
our $author = $dist->authors->[ -1 ];
our $metacpan = "https://metacpan.org/release/$name";
our $cpan_rt_mailto = "mailto:bug-$name\@rt.cpan.org";
our $repo_type = "hg";
our $repo_host = "fedorapeople.org";
our $repo_url = "https://vandebugger.$repo_host/hg/perl-$name";
our $repo_web = undef;
our $repo_clone = "$repo_type clone $repo_url" .
( $repo_url =~ m{/\Qperl-$name\E\z} ? '' : " \\\n perl-$name" );
our $bundle = $Dist::Zilla::PluginBundle::Author::VDB::Self;
};
$ENV{ 'TEST_FIX' . 'ME_FORMAT' } = 'perl'; # Hide keyword from `fixme.t`.
] ],
} ],
#
# Files to include
#
[ 'Manifest::Read' ],
#
# Generated files
#
$self->copying ne '' ? (
[ 'GenerateFile' => 'COPYING' => {
'filename' => 'COPYING',
'content' => [
q[{] . q[{],
q[ include( MY::file( $plugin, ] . $self->_quote( $self->copying ) . q[ ) ) ],
q[ ->fill_in ],
q[ ->pod2text( width => 80, indent => 0, loose => 1, quotes => 'none' ) ],
q[ ->chomp; ],
# Resulting file may have one or two empty lines at the end, it affects
# testing. Let's try to chomp empty lines to avoid it.
q[}] . q[}], # One newline will be added there.
],
} ],
) : (
),
$self->readme ? (
[ 'GenerateFile' => 'README' => {
'filename' => 'README',
'content' => [
q[{] . q[{],
q[join( ],
q[ "\n\n", ],
q[ map( ],
q[ { ],
q[ include( MY::file( $plugin, $_ ) ) ],
q[ ->fill_in ],
q[ ->pod2text( width => 80, indent => 0, loose => 1, quotes => 'none' ) ],
q[ ->chomp ],
q[ } ],
q[ ] . $self->_quote( $self->readme ) . q[ ],
q[ ) ],
q[); ],
q[}] . q[}], # One newline will be added there.
],
} ],
) : (
),
[ 'Manifest::Write' => { # REQUIRE: v0.9.0
# `Manifest::Write` v0.9.0 strictly requires plugin names, not monikers.
# If a plugin name is added from the bundle, `PluginBundle::Easy` prepends its name
# with the bundle name (or moniker?).
Dist::Zilla::Plugin::Manifest::Write->VERSION() < version->parse( 'v0.9.0' ) ? (
'source_providers' => [
'Manifest::Read',
'GenerateFile',
],
) : (
'source_provider' => [
'@Author::VDB/Manifest::Read',
$self->copying ne '' ? '@Author::VDB/COPYING' : (),
$self->readme ? '@Author::VDB/README' : (),
],
'metainfo_provider' => [
# Defaults are not suitable because they are just `MetaJSON` and `MetaYAML`.
'@Author::VDB/Manifest::Write',
'@Author::VDB/MetaJSON',
'@Author::VDB/MetaYAML',
],
),
} ],
#
# File mungers
#
[ 'Templates' => { # REQUIRE: v0.5.0 for including `Dist::Zilla::File` objects.
'templates' => [
'@Author::VDB/src doc',
@{ $self->templates // [] },
],
} ],
[ 'OurPkgVersion' ],
[ 'SurgicalPodWeaver' => {
'config_plugin' => '@Author::VDB',
'replacer' => 'replace_with_comment',
} ],
[ 'FileFinder::ByName' => 'src doc' => {
# Plugin name will be `@Author::VDB/doc`.
'file' => [
$self->copying ne '' ? ( 'COPYING' ) : (),
$self->readme ? ( 'README' ) : (),
],
} ],
#
# Update sources
#
# Copy built doc files back to source directory.
[ 'Hook::AfterBuild' => 'update src doc' => {
'hook' => [ q{
use Path::Tiny;
my $files = $zilla->plugin_named( '@Author::VDB/src doc' )->find_files();
my $build = path( $arg->{ build_root } );
my $root = path( $dist->root );
for my $file ( @$files ) {
my $new_file = $build->child( $file->name );
my $old_file = $root->child( $file->name );
my $new_bulk = $new_file->slurp;
my $old_bulk = $old_file->exists ? $old_file->slurp : undef;
if ( not defined( $old_bulk ) or $new_bulk ne $old_bulk ) {
$self->log( [ 'updating %s', $file->name ] );
$old_file->spew( $new_bulk );
};
};
} ],
} ],
#
# Tests
#
[ 'Test::DiagINC' ],
# Files
[ 'Test::EOL' => {
'finder' => '@Author::VDB/Manifest::Read',
} ],
[ 'Test::NoTabs' => {
'finder' => '@Author::VDB/Manifest::Read',
} ],
[ 'MojibakeTests' ],
# Code
[ 'Test::Compile' => {
'fake_home' => 1,
} ],
[ 'Test::Version' => { # All modules have version.
#~ 'is_strict' => 1, # Does not work for trial releases.
} ],
[ 'Test::NewVersion' ], # This is not a version already uploaded to CPAN.
[ 'Test::MinimumVersion' => {
'max_target_perl' => $self->minimum_perl,
} ],
[ 'Test::Fixme' ],
[ 'Test::Perl::Critic' => {
'critic_config' => 'xt/perlcritic.ini',
# The test does not check tests. TODO: How to fix?
} ],
# POD
[ 'PodSyntaxTests' ], # `Dist-Zilla`-bundled test, uses `Test::pod`.
[ 'PodCoverageTests' ], # `Dist-Zilla`-bundled test, uses `Pod::Coverage::TrustPod`.
$self->spellchecker ? (
[ 'Test::PodSpelling' => {
'spell_cmd' => $self->spellchecker,
} ],
) : (
),
[ 'Test::Pod::No404s' ], # No dead URLs.
# Metadata
[ 'MetaTests' ], # `Dist-Zilla`-bundled test, uses `Test::CPAN::Meta`, checks `META.yml`.
[ 'Test::CPAN::Meta::JSON' ], # Uses `Test::CPAN::Meta::JSON`.
[ 'Test::CPAN::Changes' ],
# Does not check that `Changes` has a record for current version, see
[ 'Test::DistManifest' ],
# Overall
[ 'Test::Kwalitee' ],
#
# Metainfo
#
[ 'MinimumPerl' ],
[ 'AutoPrereqs' ],
# `Prereqs::AuthorDeps` has a problem:
# It adds local plugins (e. g. tools::GenerateHooks) to the dependencies,
# which obviously are not indexed on CPAN.
[ 'Prereqs::AuthorDeps' => {
#~ 'exclude' => [
#~ # Exclude option requires a list of specific files, while I want to ignore all
#~ # files in specific directory.
#~ ],
} ],
# TODO: Remove when possible.
# This is a dirty hack. Remove it when `Prereqs::AuthorDeps` allows me to ignore all the
# modules from `tools/` directory. Meanwhile, find and remove all the dependencies on
# modules with `tools::` prefix.
[ 'Hook::PrereqSource' => 'tools' => {
'hook' => [ q{
MY::prereqs( $self, sub {
my ( $self, $module, $phase, $type, $reqs ) = @_;
if ( $module =~ m{^tools::} ) {
$self->log_debug( [
'found dependency on module %s (phase %s, type %s), deleting it',
$module, $phase, $type
] );
delete( $reqs->{ requirements }->{ $module } );
};
} );
} ],
} ],
# `use autodie ':all';` implicitly requires `IPC::System::Simple` module, but this
# dependency is not detected by `AutoPrereqs`. If there is dependency on `autodie`, let
# us add dependency on `IPC::System::Simple`.
[ 'Hook::PrereqSource' => 'autodie' => {
'hook' => [ q{
MY::prereqs( $self, sub {
my ( $self, $module, $phase, $type ) = @_;
if ( $module eq 'autodie' ) {
$self->log_debug( [
'found dependency on module %s (phase %s, type %s), ' .
'adding dependency on IPC::System::Simple',
$module, $phase, $type
] );
$dist->register_prereqs(
{ phase => $phase, type => $type },
'IPC::System::Simple' => 0,
);
};
} );
} ],
} ],
[ 'CheckPrereqsIndexed' ], # Make sure all prereqs are published in CPAN.
[ 'MetaProvides::Package' ],
[ 'MetaResources::Template' => {
'delimiters' => '{ }',
'homepage' => '{$MY::metacpan}',
'license' => '{$dist->license->url}',
'repository.type' => '{$MY::repo_type}',
'repository.url' => '{$MY::repo_url}',
'repository.web' => '{$MY::repo_web}',
'bugtracker.mailto' => '{$MY::cpan_rt_mailto}',
'bugtracker.web' => '{$MY::cpan_rt_browse}',
} ],
[ 'MetaYAML' ], # Generate `META.yml`.
[ 'MetaJSON' ], # Generate `META.json`.
#
# Installer
#
[ 'ModuleBuildTiny' ],
#
# Release
#
$self->archive ? (
# Make sure archive directory exists. Do it in the very beginnig of release because
# it is simple and fast operation. It will be annoying to pass all the tests and
# stop release because `dzil` fails to create archive directory.
[ 'Hook::BeforeRelease' => 'archive directory' => {
'hook' => [ q{
my $root = path( $self->zilla->root . '' );
my $dir = path( "} . $self->archive . q{" );
if ( $dir->is_absolute ) {
$self->log_error( [ 'bad archive directory: %s', "$dir" ] );
$self->log_error( [ 'absolute path not allowed' ] );
$self->abort();
};
if ( not $dir->is_dir ) {
$self->log( [ 'creating archive directory %s', "$dir" ] );
$dir->mkpath();
};
} ],
} ]
) : (
),
#~ [ 'RunExtraTests' ], # Does not work for me, see <https://github.com/dagolden/Dist-Zilla-Plugin-CheckExtraTests/issues/26>.
[ 'CheckExtraTests' ], # So use CheckExtraTest meanwhile.
[ 'TestRelease' ], # Unpack tarball and run tests.
# Make sure the distro does not depend on unwanted modules. Unwanted module, for example,
# is `Data::Printer`. I use it often for debugging purposes and forget to remove
# debugging code.
[ 'Hook::BeforeRelease' => 'unwanted deps' => { # REQUIRTE: HHH
'hook' => [ q{
my @unwanted = (} . $self->_quote( $self->unwanted_modules ) . q{);
my %unwanted = map( { $_ => 1 } @unwanted );
my $heading = 'unwanted modules found:';
MY::prereqs( $self, sub {
my ( $self, $module, $phase, $type ) = @_;
if ( $unwanted{ $module } ) {
if ( $heading ) {
$self->log_error( $heading );
$heading = undef;
};
$self->log_error( [
' %s (phase %s, type %s)', $module, $phase, $type
] );
};
} );
$self->abort_if_error();
} ],
} ],
[ 'CheckChangesHasContent' ],
$self->spellchecker ? (
[ 'Hook::BeforeRelease' => 'spellcheck changes' => {
'hook' => [ q{
$self->log( 'spellchecking Changes' );
use File::chdir;
local $CWD = $zilla->built_in . '';
#
# Run spellchecker, collect list of unknown words.
#
use IPC::Run3;
my @list;
run3( '} . $self->spellchecker . q{', 'Changes', \@list );
if ( $? > 0 ) {
$self->abort();
};
chomp( @list );
#
# Steal list of words to ignore from `PodSpelling` plugin.
#
my $podspelling = $zilla->plugin_named( '@Author::VDB/Test::PodSpelling' );
my %ignore = map( { $_ => 1 } @{ $podspelling->stopwords } );
#
# Add all module names.
#
my $prereqs = $dist->prereqs->cpan_meta_prereqs;
for my $phase ( $prereqs->__legal_phases ) {
for my $type ( $prereqs->__legal_types ) {
my $reqs = $prereqs->requirements_for( $phase, $type ) or next;
for my $module ( keys( %{ $reqs->{ requirements } } ) ) {
$ignore{ $_ } = 1 for split( '::', $module );
};
};
};
#
# Build maps: word => count and count => word.
#
my %w2c; # word => count
$w2c{ $_ } += 1 for grep( { not $ignore{ $_ } and not $ignore{ lc( $_ ) } } @list );
my %c2w; # count => word
push( @{ $c2w{ $w2c{ $_ } } }, $_ ) for keys( %w2c );
#
# Now print the list of spelling errors.
#
for my $count ( sort( { $b <=> $a } keys( %c2w ) ) ) {
printf( "%2d: %s\n", $count, join( ', ', sort( @{ $c2w{ $count } } ) ) );
};
if ( %w2c ) {
$self->abort( 'spelling errors found in Changes' );
};
} ],
} ],
) : (
),
# Make sure there is no such tag yet, neither global nor local.
[ 'Hook::BeforeRelease' => 'hg check tag' => {
'hook' => [ q{
use Path::Tiny;
my $version = $dist->version;
my $tags = path( '.hgtags' );
if ( $tags->slurp_utf8 =~ m{ \Q$version\E$}m ) {
$self->abort( [ 'tag %s already exists', $version ] );
};
my $ltags = path( '.hg/localtags' );
if ( $ltags->exists and $ltags->slurp_utf8 =~ m{ \Q$version\E$}m ) {
$self->abort( [ 'local tag %s already exists', $version ] );
};
} ],
} ],
[ 'Hook::BeforeRelease' => 'hg stat' => {
'hook' => [ q{
# Make sure all the files committed.
use IPC::System::Simple 'capture';
my @stdout = capture( 'hg', 'status' );
if ( @stdout ) {
chomp( @stdout );
$self->log_error( 'hg status:' );
$self->log_error( " $_" ) for @stdout;
$self->abort( 'hg status is not clean' );
};
} ],
} ],
$self->local_release ? (
[ 'Hook::BeforeRelease' => 'release note' => {
'hook' => [ q{
$self->log( '*** Preparing to *local* release ***' );
} ],
} ],
) : (
),
[ 'ConfirmRelease' ], # Ask confirmation before uploading the release.
[ 'Hook::Releaser' => 'tgz' => {
'hook' => [ q{
$MY::tgz = $arg;
} ],
} ],
# `Archive` is a good plugin, but I need to copy, not move tarball, because I want to
# archive the tarball first, and then upload it to CPAN.
$self->archive ? (
[ 'Hook::Releaser' => 'archive release' => {
'hook' => [ q{
use Path::Tiny;
my $tgz = path( $arg );
my $dir = path( "} . $self->archive . q{" );
$self->log( [ 'copying %s to %s', "$tgz", "$dir" ] );
$tgz->copy( $dir->child( $tgz->basename ) );
} ],
} ],
) : (
),
$self->local_release ? (
# No need in `FakeRelease`: we have at least one releaser, it is enough.
) : (
[ 'UploadToCPAN' ],
),
[ 'Hook::AfterRelease' => 'hg add tag' => {
'hook' => [ q{
use IPC::System::Simple 'capture';
my @stdout = capture( 'hg', 'id', '--debug' );
chomp( @stdout );
@stdout == 1 and $stdout[ 0 ] =~ m{^([0-9a-f]{40})(\+?) tip$} or do {
$self->log_error( 'fail to parse "hg id" output:' );
$self->log_error( " $_" ) for @stdout;
$self->abort();
};
my ( $id, $dirty ) = ( $1, $2 );
if ( $dirty ) {
$self->abort( 'hg status is not clean' );
};
path( '} . ( $self->local_release ? '.hg/localtags' : '.hgtags' ) . q{' )->append( sprintf( "%s %s\n", $id, $dist->version ) );
} ],
} ],
[ 'NextRelease' => {
'format' => '%V @ %{yyyy-MM-dd HH:mm zzz}d',
'time_zone' => 'UTC',
} ],
[ 'Hook::AfterRelease' => 'bump version' => {
'hook' => [ q{
use Path::Tiny;
use Perl::Version;
my $version = Perl::Version->new( $dist->version );
$version->inc_alpha();
path( $zilla->root )->child( 'VERSION' )->spew( $version );
$self->log( [ 'next release will be %s', "$version" ] );
} ],
} ],
[ 'Hook::AfterRelease' => 'hg post-commit' => {
'hook' => [ q{
use autodie ':all';
system(
'hg', 'commit', '-m', 'post-release',
qw{ .hgtags Changes VERSION }
);
} ],
} ],
$self->local_release ? (
) : (
[ 'Hook::AfterRelease' => 'hg push' => {
'hook' => [ q{
use IPC::System::Simple 'capture';
my @stdout = capture( 'hg', 'push' );
chomp( @stdout );
$self->log( " $_" ) for @stdout;
} ],
} ],
),
[ 'Hook::AfterRelease' => 'install' => {
'hook' => [ q{
use autodie ':all';
use File::chdir;
$self->log( [ 'installing %s-%s', $dist->name, $dist->version ] );
local $CWD = $zilla->built_in;
system( 'cpanm', '--notest', '.' );
# ^ We run the tests on unpacked tarball before release, no need in running
# tests one more time.
} ],
} ],
[ 'Hook::AfterRelease' => 'clean' => {
'hook' => [ q{
$zilla->clean();
} ],
} ],
);
return;
};
__PACKAGE__->meta->make_immutable();
1;
# doc/what.pod #
#pod =encoding UTF-8
#pod
#pod =head1 WHAT?
#pod
#pod C<Dist-Zilla-PluginBundle-Author-VDB> (or just C<@Author::VDB>) is a C<Dist-Zilla> plugin bundle used by VDB.
#pod
#pod =cut
# end of file #
# doc/why.pod #
#pod =encoding UTF-8
#pod
#pod =head1 WHY?
#pod
#pod I have published few distributions on CPAN. Every distribution have F<dist.ini> file. All the
#pod F<dist.ini> files are very similar. Maintaining multiple very similar F<dist.ini> files is boring.
#pod Plugin bundle solves the problem.
#pod
#pod =cut
# end of file #
=pod
=encoding UTF-8
=head1 NAME
Dist::Zilla::PluginBundle::Author::VDB - VDB's plugin bundle
=head1 VERSION
Version v0.8.6, released on 2015-10-03 10:53 UTC.
=head1 WHAT?
C<Dist-Zilla-PluginBundle-Author-VDB> (or just C<@Author::VDB>) is a C<Dist-Zilla> plugin bundle used by VDB.
=head1 DESCRIPTION
It is unlikely that someone else will want to use it, so I will not bother with documenting it, at
least for now.
=head1 OBJECT METHODS
=head2 mvp_multivalue_args
=head2 _quote
Convert an attribute to a form suitable for using in source. C<Str> attribute is converted into a
string literal, C<ArrayRef> attribute is converted to a list of string literals.
=head1 OPTIONS
=head2 minimum_perl
Optional, default value C<5.006>.
=head2 copying
Name of POD file to generate distribution F<COPYING> text file. Empty value disables generation
F<COPYING> file.
C<Str>, optional, default value C<doc/copying.pod>.
=head2 readme
Names of POD files to generate distribution F<README> text file. This is a multi-value option.
Empty values are ignored. Empty list disables generating F<README>file.
C<ArrayRef[Str]>, optional, default value C<[ 'doc/what.pod', 'doc/why.pod', 'doc/naming.pod', 'doc/forms.pod', 'doc/source.pod', 'doc/distribution.pod', 'doc/installing.pod', 'doc/hacking.pod', 'doc/documentation.pod', 'doc/feedback.pod', 'doc/glossary.pod' ]>.
=head2 local_release
If true, release will be a local one, i. e. no external operations will be done: C<UploadToCPAN>
and C<hg push> will be skipped, <hg tag> will create a local tag.
Option can be set trough F<dist.ini> file or with C<DZIL_LOCAL_RELEASE> environment variable.
Optional, default value is 0.
=head2 archive
Directory to archive files to. If empty, release will not be archived. If such directory does not
exist, it will be created before release.
Optional, default value C<".releases">.
=head2 templates
This option will be passed to C<Templates> plugin. If you no not want C<Templates> to process
files, specify C<:NoFiles>. This is multi-value option (i. e. may be specified several times).
Optional, default value C<[ ':InstallModules' ]>.
=head2 unwanted_module
=head2 unwanted_modules
TODO C<[ qw{ DPP Data::Printer } ]>.
=head2 spellchecker
Command to run spellchecker. Spellchecker command is expected to read text from stdin, and print to
stdout misspelled words. If empty, spellchecking will be skipped. This involve C<Test::PodSpelling>
plugin and internally implemented checking the F<Changes> file.
Optional, default value C<aspell list -l en -p ./xt/aspell.en.pws>.
=head1 WHY?
I have published few distributions on CPAN. Every distribution have F<dist.ini> file. All the
F<dist.ini> files are very similar. Maintaining multiple very similar F<dist.ini> files is boring.
Plugin bundle solves the problem.
=for Pod::Coverage configure
=head1 AUTHOR
Van de Bugger <van.de.bugger@gmail.com>
=head1 COPYRIGHT AND LICENSE
Copyright © 2015 Van de Bugger
This file is part of perl-Dist-Zilla-PluginBundle-Author-VDB.
perl-Dist-Zilla-PluginBundle-Author-VDB is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later version.
perl-Dist-Zilla-PluginBundle-Author-VDB is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
perl-Dist-Zilla-PluginBundle-Author-VDB. If not, see <http://www.gnu.org/licenses/>.
=cut
__DATA__
__[ doc/copying.pod ]__
=encoding UTF-8
=head1 COPYRIGHT AND LICENSE
Copyright © 2015 Van de Bugger
C<perl-{{$MY::name}}> is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software Foundation, either
version 3 of the License, or (at your option) any later version.
C<perl-{{$MY::name}}> is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
C<perl-{{$MY::name}}>. If not, see L<http://www.gnu.org/licenses/>.
C<perl-{{$MY::name}}> I<distribution> may contain files generated by C<Dist-Zilla> and/or its
plugins from third-party templates; copyright and license specified above are I<not> applicable to
that files.
=cut
__[ doc/distribution.pod ]__
=encoding UTF-8
=head1 DISTRIBUTION
C<{{$MY::name}}> distributions are published on L<CPAN|{{$MY::metacpan}}>.
=head2 Generated Files
Distribution may contain files preprocessed or generated by C<Dist-Zilla> and its plugins. Some
generated files are made from C<{{$MY::name}}> source, but some are generated from
third-party templates. Files generated from third-party templates usually include a comment near
the top of the file:
This file was generated with NAME
(where I<NAME> is a name of the plugin generated the file). Such files are I<not> part of
C<{{$MY::name}}> source, and C<{{$MY::name}}> copyright and license are not applicable
to such files.
=cut
__[ doc/documentation.pod ]__
=encoding UTF-8
=head1 DOCUMENTATION
=head2 Online
The easiest way is browsing the documentation L<online at meta::cpan|{{$MY::metacpan}}>.
=head2 Locally Installed
If you have the distribution installed, use C<perldoc> tool to browse locally
installed documentation:
$ perldoc {{$MY::package}}::Manual
$ perldoc {{$MY::package}}
=head2 Built from Source
Build C<{{$MY::name}}> first (see L</"HACKING">), then:
$ cd {{$MY::name}}-VERSION
$ perldoc {{$MY::package}}::Manual
$ perldoc {{$MY::package}}
where I<VERSION> is a version of built distribution.
=cut
__[ doc/feedback.pod ]__
=encoding UTF-8
=head1 FEEDBACK
=head2 CPAN Request Tracker
The quickest way to report a bug in C<{{$MY::name}}> is by sending email to
bug-{{$MY::name}} [at] rt.cpan.org.
CPAN request tracker can be used via web interface also:
=over
=item L<Browse bugs|{{$MY::cpan_rt_browse}}>
Browsing bugs does not require authentication.
=item L<Report bugs|{{$MY::cpan_rt_report}}>
You need to be a CPAN author, have a L<BitCard|https://www.bitcard.org/> account, or OpenID in
order to report bugs via the web interface.
(On 2015-04-27 I have logged in successfully with my LiveJournal OpenID, but my Google OpenID did
not work for CPAN. I did not check other OpenID providers.)
=back
=head2 Send Email to Author
As a last resort, send email to author: {{$MY::author}}. Please start message subject with
"perl-{{$MY::name}}:".
=cut
__[ doc/forms.pod ]__
=encoding UTF-8
=head1 FORMS
You may face C<{{$MY::name}}> in I<source> or I<distribution> forms.
If you are going to {{$MY::abstract}}, you will likely be interested in I<using>
C<{{$MY::name}}> I<distribution>. If you are going to I<develop> (or I<hack>) the
C<{{$MY::name}}> itself, you will likely need the I<source>, not distribution.
Since Perl is an interpreting language, modules in the distribution I<look> like sources. Actually,
they are Perl source files. But they are not I<actual> sources, because they are I<built>
(preprocessed or generated) by L<Dist-Zilla>.
How to distinguish source and distribution:
=over
=item *
Source may contain Mercurial files and directories F<.hgignore>, F<.hgtags>, F<.hg/>, while
distribution should not.
=item *
Source should contain files F<dist.ini>, F<weaver.ini>, while distribution may not.
=item *
Source should I<not> contain F<xt/> directory, while distribution should.
=item *
Name of source directory does I<not> include version (e. g. C<{{$MY::name}}>), while name of
distribution does (e. g. C<{{$MY::name}}-0.007>).
=back
=cut
__[ doc/glossary.pod ]__
=encoding UTF-8
=head1 GLOSSARY
=over
=item CPAN
Comprehensive Perl Archive Network, a B<large> collection of Perl software and documentation. See
L<cpan.org|http://www.cpan.org>, L<What is
=item Distribution
Tarball, containing Perl modules and accompanying files (documentation, metainfo, tests). Usually
distributions are uploaded to CPAN, and can be installed with dedicated tools (C<cpan>, C<cpanm>,
and others).
=item Module
Perl library file, usually with C<.pm> suffix. Usually contains one package. See
=item Package
Perl language construct. See L<package|http://perldoc.perl.org/functions/package.html> and
=back
=cut
__[ doc/hacking.pod ]__
=encoding UTF-8
=head1 HACKING
For hacking, you will need Mercurial, Perl interpreter and C<Dist-Zilla> (with some plugins), and
likely C<cpanm> to install missed parts.
Clone the repository first:
$ {{$MY::repo_clone}}
$ cd perl-{{$MY::name}}
To build a distribution from the source, run:
$ dzil build
If required C<Dist-Zilla> plugins are missed, C<dzil> tool will warn you and show the command to
install all the required plugins, e. g.:
Required plugin Dist::Zilla::Plugin::Test::EOL isn't installed.
Run 'dzil authordeps' to see a list of all required plugins.
You can pipe the list to your CPAN client to install or update them:
dzil authordeps --missing | cpanm
To run the tests:
$ dzil test
To run all the tests, including release tests:
$ dzil test --release
To install the distribution:
$ dzil install
or
$ cpanm ./{{$MY::name}}-VERSION.tar.gz
where I<VERSION> is a version of built distribution.
To clean the directory:
$ dzil clean
=cut
__[ doc/installing.pod ]__
=encoding UTF-8
=head1 INSTALLING
=head2 With C<cpanm>
C<cpanm> tool is (probably) the easiest way to install distribution. It automates downloading,
building, testing, installing, and uninstalling.
To install the latest version from CPAN:
$ cpanm {{$MY::package}}
To install a specific version (e. g. I<0.007>) from CPAN:
$ cpanm {{$MY::package}}@0.007
To install locally available distribution (e. g. previously downloaded from CPAN or built from
sources):
$ cpanm ./{{$MY::name}}-0.007.tar.gz
To uninstall the distribution:
$ cpanm -U {{$MY::package}}
=head2 Manually
To install distribution tarball manually (let us assume you have version I<0.007> of the
distribution):
$ tar xaf {{$MY::name}}-0.007.tar.gz
$ cd {{$MY::name}}-0.007
$ perl Build.PL
$ ./Build build
$ ./Build test
$ ./Build install
=head2 See Also
L<How to install CPAN modules|http://www.cpan.org/modules/INSTALL.html>
=cut
__[ doc/naming.pod ]__
=encoding UTF-8
=head1 NAMING
C<perl-{{$MY::name}}> is official software name.
However, in Perl world prefix "perl-" is redundant and not used. For example, on
L<meta::cpan|https://metacpan.org/> this software is named as C<{{$MY::name}}>. In the rest
of the documentation shortened name C<{{$MY::name}}> is used as synonym for full name
C<perl-{{$MY::name}}>. We are in the Perl world, aren't we?
You may notice that name may be spelled with dashes (C<{{$MY::name}}>) or with double colons
(C<{{$MY::package}}>). Strictly speaking, there is difference: the first one is software
name, while the second is name of Perl package, but often these names are interchangeable
especially if software consists of single package.
=cut
__[ doc/source.pod ]__
=encoding UTF-8
=head1 SOURCE
C<{{$MY::name}}> source is in Mercurial repository hosted on {{$MY::repo_host}}.
{{$MY::repo_web ? "You can either L<browse the source online|{{$MY::repo_web}}> or " : "To
"}} clone the entire repository:
$ {{$MY::repo_clone}}
=head2 Source Files
C<{{$MY::name}}> source files usually include a comment near the top of the file:
This file is part of perl-{{$MY::name}}.
Not all source files are included into distribution. Some source files are used at distribution
build time only, and not required for installation.
=cut
__END__
# end of file #