The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
## no critic (RequireUseStrict)
package Dist::Zilla::Plugin::Test::LocalBrew;
$Dist::Zilla::Plugin::Test::LocalBrew::VERSION = '0.08';
use File::Spec;
use File::Temp qw(tempdir);

use namespace::clean;

## use critic (RequireUseStrict)
use Moose;
with 'Dist::Zilla::Role::FileGatherer', 'Dist::Zilla::Role::TextTemplate';

has brews => (
    is      => 'ro',
    isa     => 'ArrayRef[Str]',
    default => sub { [] },
);

has notest_deps => (
    is      => 'ro',
    isa     => 'Bool',
    default => 0,
);

has nobrew => (
    is      => 'ro',
    isa     => 'Bool',
    default => 0,
);

sub should_test_deps {
    my ( $self ) = @_;

    return !$self->notest_deps;
}

sub mvp_multivalue_args {
    qw/brews/
}

my $template = <<'TEMPLATE';
#!perl

use strict;
use warnings;

use FindBin;
use File::Copy qw(copy);
use File::Spec;
use File::Temp;
use Test::More;

sub copy_log_file {
    my ( $home ) = @_;
    my $log_file = File::Spec->catfile($home, '.cpanm', 'build.log');
    my $tempfile = File::Temp->new(
        SUFFIX => '.log',
        UNLINK => 0,
    );
    copy($log_file, $tempfile->filename);
    diag("For details, please consult $tempfile")
}

sub is_dist_root {
    my ( @path ) = @_;

    return -e File::Spec->catfile(@path, 'Makefile.PL') ||
           -e File::Spec->catfile(@path, 'Build.PL');
}

delete @ENV{qw/AUTHOR_TESTING RELEASE_TESTING PERL5LIB/};

unless($ENV{'PERLBREW_ROOT'}) {
    plan skip_all => "Environment variable 'PERLBREW_ROOT' not found";
    exit;
}

my $brew = q[{{$brew || ''}}];

my $cpanm_path = qx(which cpanm 2>/dev/null);
unless($cpanm_path) {
    plan skip_all => "The 'cpanm' program is required to run this test";
    exit;
}
chomp $cpanm_path;

my $perlbrew_bin = File::Spec->catdir($ENV{'PERLBREW_ROOT'}, 'perls',
    $brew, 'bin');

my $perlbrew_path = $ENV{'PATH'};
if(my $local_lib_root = $ENV{'PERL_LOCAL_LIB_ROOT'}) {
    my @path = File::Spec->path;

    while(@path && $path[0] =~ /^$local_lib_root/) {
        shift @path;
    }

    if($^O eq 'MSWin32') {
        $perlbrew_path = join(';', @path);
    } else {
        $perlbrew_path = join(':', @path);
    }
}

my ( $env, $status ) = do {
    local $ENV{'PATH'} = $perlbrew_path;
    local $ENV{'SHELL'} = '/bin/bash'; # fool perlbrew
    ( scalar(qx(perlbrew env $brew)), $? )
};

unless($status == 0) {
    plan skip_all => "No such perlbrew environment '$brew'";
    exit;
}

my @lines = split /\n/, $env;

foreach my $line (@lines) {
    if($line =~ /^\s*export\s+([0-9a-zA-Z_]+)=(.*)$/) {
        my ( $k, $v ) = ( $1, $2 );
        if($v =~ /^("|')(.*)\1$/) {
            $v = $2;
            $v =~ s!\\(.)!$1!ge;
        }
        $ENV{$k} = $v;
    } elsif($line =~ /^unset\s+([0-9a-zA-Z_]+)/) {
        delete $ENV{$1};
    }
}

my $pristine_path = do {
    local $ENV{'PATH'} = $perlbrew_path;
    qx(perlbrew display-pristine-path);
};
chomp $pristine_path;
$ENV{'PATH'} = join(':', $ENV{'PERLBREW_PATH'}, $pristine_path);

plan tests => 1;

my $tmpdir  = File::Temp->newdir;
my $tmphome = File::Temp->newdir;

my $pid = fork;
if(!defined $pid) {
    fail "Forking failed!";
    exit 1;
} elsif($pid) {
    waitpid $pid, 0;
    ok !$?, "cpanm should successfully install your dist with no issues" or copy_log_file($tmphome->dirname);
} else {
    open STDIN, '<', File::Spec->devnull;
    open STDOUT, '>', File::Spec->devnull;
    open STDERR, '>', File::Spec->devnull;

    my @path = File::Spec->splitdir($FindBin::Bin);

    while(@path && !is_dist_root(@path)) {
        pop @path;
    }
    unless(@path) {
        die "Unable to find dist root\n";
    }
    chdir File::Spec->catdir(@path); # exit test directory

    # override where cpanm puts its log file
    $ENV{'HOME'} = $tmphome->dirname;

{{
        unless($should_test_deps) {
            return <<'END_PERL';
    system 'perl', $cpanm_path, '--notest', '--installdeps', '-L', $tmpdir->dirname, '.';
    if($?) {
        exit($? >> 8);
    }
END_PERL
        }
        return '';
}}

    # We use system here instead of exec so that $tmpdir gets cleaned up
    # after cpanm finishes
    system 'perl', $cpanm_path, '-L', $tmpdir->dirname, '.';
    exit($? >> 8);
}
TEMPLATE

sub gather_files {
    my ( $self ) = @_;

    my $brews = $self->brews;

    unless(@$brews) {
        $self->log_fatal('No perlbrew environments specified in your dist.ini');
    }

    foreach my $brew (@$brews) {
        $self->add_file(Dist::Zilla::File::InMemory->new(
            name    => "xt/release/localbrew-$brew.t",
            content => $self->fill_in_string($template, {
                brew             => $brew,
                should_test_deps => $self->should_test_deps,
            }),
        ));
    }

    if($self->nobrew) {
        $self->add_file(Dist::Zilla::File::InMemory->new(
            name    => "xt/release/system-localbrew.t",
            content => $self->fill_in_string($template, {
                brew             => undef,
                should_test_deps => $self->should_test_deps,
            }),
        ));
    }
}

no Moose;
1;

=pod

=encoding UTF-8

=head1 NAME

Dist::Zilla::Plugin::Test::LocalBrew - Verify that your distribution tests well in a fresh perlbrew

=head1 VERSION

version 0.08

=head1 SYNOPSIS

  # in your dist.ini
  [Test::LocalBrew]
  brews = first-perlbrew
  brews = second-perlbrew

=head1 DESCRIPTION

This plugin adds a release test for your module that tests it against a set
of given perlbrew environments.  Any dependencies are installed via cpanminus
into a temporary local lib, so your perlbrew environments aren't altered.
This comes in handy when you want to build against a set of "fresh" Perl
installations (ie. those with only core modules) to make sure all of your
prerequisites are included correctly.

=head1 ATTRIBUTES

=head2 brews

A list of perlbrew environments to build and test in.

=head2 notest_deps

If this flag is set, don't test dependency modules.

=head2 nobrew

If this flag is set, test against the system perl as well.

=head1 ISSUES

=over

=item Relies on the 'which' program to detect cpanm.

=back

=head1 SEE ALSO

L<Dist::Zilla>, L<App::perlbrew>, L<App::cpanminus>, L<local::lib>

=begin comment

=over

=item mvp_multivalue_args

=item gather_files

=item should_test_deps

=back

=end comment

=head1 AUTHOR

Rob Hoelz <rob@hoelz.ro>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2014 by Rob Hoelz.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=head1 BUGS

Please report any bugs or feature requests on the bugtracker website
https://github.com/hoelzro/dist-zilla-plugin-test-localbrew/issues

When submitting a bug or request, please include a test-file or a
patch to an existing test-file that illustrates the bug or desired
feature.

=cut

__END__

# ABSTRACT: Verify that your distribution tests well in a fresh perlbrew