shell bypass 403
package Test::Version;
use 5.006;
use strict;
use warnings;
use Carp;
our $VERSION = '2.09'; # VERSION
use parent 'Exporter';
use Test::Builder;
use version 0.86 qw( is_lax is_strict );
use File::Find::Rule::Perl;
use Test::More;
use Module::Metadata 1.000020;
our @EXPORT = qw( version_all_ok ); ## no critic (Modules::ProhibitAutomaticExportation)
our @EXPORT_OK = qw( version_ok );
my $cfg;
sub import { ## no critic qw( Subroutines::RequireArgUnpacking Subroutines::RequireFinalReturn )
my @exports;
foreach my $param ( @_ ) {
unless ( ref( $param ) eq 'HASH' ) {
push @exports, $param;
} else {
$cfg = $param
}
}
$cfg->{is_strict}
= defined $cfg->{is_strict} ? $cfg->{is_strict}
: 0
;
$cfg->{has_version}
= defined $cfg->{has_version} ? $cfg->{has_version}
: 1
;
$cfg->{consistent}
= defined $cfg->{consistent} ? $cfg->{consistent}
: 0
;
$cfg->{filename_match}
= defined $cfg->{filename_match} ? $cfg->{filename_match}
: []
;
$cfg->{multiple}
= defined $cfg->{multiple} ? $cfg->{multiple}
: 0
;
unless(ref($cfg->{filename_match}) eq 'ARRAY') {
$cfg->{filename_match} = [$cfg->{filename_match}];
}
my $mmv = version->parse( $Module::Metadata::VERSION );
my $rec = version->parse( '1.000020' );
if ( $mmv >= $rec && ! defined $cfg->{ignore_unindexable} ) {
$cfg->{ignore_unindexable} = 1;
}
__PACKAGE__->export_to_level( 1, @exports );
}
my $version_counter = 0;
my $version_number;
my $consistent = 1;
my %versions;
my $test = Test::Builder->new;
our $_IN_VERSION_ALL_OK = 0;
our %FILE_FIND_RULE_EXTRAS = (
untaint => 1,
#
# the untainting pattern for Windows used by File::Find seems to be wrong.
#
# - cwd returns an absolute directory will usually return a volume (e.g. 'C:')
# - windows file systems frequently include directorieswith parans and spaces in them
# I am a little dubious that accepting them is safe. The alternative is that
# this module would not be installable in a lot of environments, and I honestly
# don't believe that many people are using Test::Version in taint mode on Windows
# anyway, so I am weighing the risk as worth it.
# - windows has short names with tildes in them (e.g. "FOO~1"). Tilde is not a
# special character in windows shells anyway, so I think we should be safe there.
#
($^O eq 'MSWin32' ? (untaint_pattern => qr|^(([a-zA-Z]:)?[-+@\w./\~\(\) ]+)$|x) : ()),
);
sub version_ok {
my ( $file, $name ) = @_;
$file ||= '';
$name ||= "check version in '$file'";
croak 'No file passed to version_ok().' unless $file;
croak "'$file' doesn't exist." unless -e $file;
my $info = Module::Metadata->new_from_file( $file );
if ( $cfg->{ignore_unindexable} && ! $info->is_indexable) {
$test->skip( "$file not indexable" );
return 0;
}
if(@{ $cfg->{filename_match} } > 0) {
my $match = 0;
foreach my $pattern (@{ $cfg->{filename_match} }) {
if(ref($pattern) eq 'Regexp') {
$match = 1 if $file =~ $pattern;
}
elsif(ref($pattern) eq 'CODE') {
$match = 1 if $pattern->($file);
}
else {
$match = 1 if $file eq $pattern;
}
last if $match;
}
unless ($match) {
$test->skip( "$file does not match filename_match" );
return 0;
}
}
my $ok = 1;
my @diag;
my @packages = $cfg->{multiple} ? $info->packages_inside : ($info->name);
unless($_IN_VERSION_ALL_OK) {
$consistent = 1;
$version_number = undef;
}
unless($cfg->{has_version}) {
@packages = grep { $info->version($_) } @packages;
unless(@packages) {
$test->skip(qq{No versions were found in "$file" and has_version is false});
$consistent = 0;
$versions{$file}->{$info->name} = undef;
return 1;
}
}
unless(@packages) {
$ok = 0;
push @diag, "No packages found in '$file'";
}
foreach my $package (@packages) {
my $version = $info->version($package);
$versions{$file}->{$package} = $version;
if (not defined $version) {
$consistent = 0;
}
$version_counter++;
unless ( $version ) {
$ok = 0;
push @diag, "No version was found in '$file' ($package).";
next;
}
unless (defined $version_number) {
$version_number = $version;
}
if ($version ne $version_number) {
$consistent = 0;
}
unless ( is_lax( $version ) ) {
$ok = 0;
push @diag, "The version '$version' found in '$file' ($package) is invalid.";
next;
}
if ( $cfg->{is_strict} ) {
unless ( is_strict( $version ) ) {
$ok = 0;
push @diag, "The version '$version' found in '$file' ($package) is not strict.";
next;
}
}
}
unless($_IN_VERSION_ALL_OK) {
if($ok && ! $consistent && $cfg->{consistent}) {
$ok = 0;
push @diag, "The versions found in '$file' are inconsistent.";
}
}
$test->ok( $ok, $name );
$test->diag($_) for @diag;
return $ok;
}
sub version_all_ok {
my ( $dir, $name ) = @_;
$version_counter = 0;
$version_number = undef;
$consistent = 1;
%versions = ();
$dir
= defined $dir ? $dir
: -d 'blib' ? 'blib'
: 'lib'
;
croak $dir . ' does not exist, or is not a directory' unless -d $dir;
# Report failure location correctly - GH #1
local $Test::Builder::Level = $Test::Builder::Level + 1; ## no critic (Variables::ProhibitPackageVars)
$name ||= "all modules in $dir have valid versions";
my @files = File::Find::Rule->perl_module->extras(\%FILE_FIND_RULE_EXTRAS)->in($dir);
{
local $_IN_VERSION_ALL_OK = 1;
foreach my $file ( @files ) {
version_ok( $file );
}
}
if ($cfg->{consistent} and not $consistent) {
$test->ok( 0, $name );
$test->diag('The version numbers in this distribution are not the same');
foreach my $file (sort keys %versions) {
foreach my $package (sort keys %{ $versions{$file} }) {
my $version = $versions{$file}->{$package};
$test->diag(sprintf "%10s %s (%s)", defined $version ? $version : 'undef', $file, $package);
}
}
return;
}
# has at least 1 version in the dist
if ( not $cfg->{has_version} and $version_counter < 1 ) {
$test->ok( 0, $name );
$test->diag(
'Your dist has no valid versions defined. '
. 'Must have at least one version'
);
}
else {
$test->ok( 1, $name );
}
return;
}
1;
# ABSTRACT: Check to see that version's in modules are sane
__END__
=pod
=encoding UTF-8
=head1 NAME
Test::Version - Check to see that version's in modules are sane
=head1 VERSION
version 2.09
=head1 SYNOPSIS
use Test::More;
use Test::Version 1.001001 qw( version_all_ok ), {
is_strict => 0,
has_version => 1,
consistent => 1,
};
# test blib or lib by default
version_all_ok();
done_testing;
=head1 DESCRIPTION
This module's goal is to be a one stop shop for checking to see that your
versions across your dist are sane. Please ensure that you use version C<0.04>
or later only, as earlier versions are old code and may not work correctly.
Current feature list:
=over
=item module has a version
Tests to insure that all modules checked have a VERSION defined, Can replace
L<Test::HasVersion>
=item module has a valid version
Tests to insure that all versions are valid, according to the rules of
L<version> method C<is_lax>. To quote:
I<The lax criteria corresponds to what is currently allowed by the version
parser. All of the following formats are acceptable for dotted-decimal formats
strings:>
v1.2
1.2345.6
v1.23_4
1.2345
1.2345_01
I<If you want to limit yourself to a much more narrow definition of what a
version string constitutes, is_strict() is limited to version strings like
the following list:>
v1.234.5
2.3456
you can cause your tests to fail if not strict by setting L<is_strict|/is_strict> to
C<1>
=back
=head1 FUNCTIONS
=head2 version_ok
version_ok( $filename, [ $name ] );
Test a single C<.pm> file by passing a path to the function. Checks if the
module has a version, and that it is valid with C<is_lax>.
=head2 version_all_ok
version_all_ok( [ $directory, [ $name ]] );
Test all modules in a directory with C<version_ok>. By default it will check
C<blib> or C<lib> if you haven't passed it a directory.
=head1 CONFIGURATION AND ENVIRONMENT
=head2 has_version
use Test::Version qw( version_all_ok ), { has_version => 0 };
Allows disabling whether a module has to have a version. If set to 0
version tests will be skipped in any module where no version is found.
really doesn't make sense to use with just L<version_ok|/version_ok>
=head2 is_strict
use Test::Version { is_strict => 1 };
this allows enabling of L<version>s C<is_strict> checks to ensure that your
version is strict.
=head2 consistent
use Test::Version { consistent => 1 };
Check if every module has the same version number.
=head2 ignore_unindexable
use Test::Version { ignore_unindexable => 0};
if you have at least L<Module::Metadata> vC<1.000020> Test::Version will by
default skip any files not considered L<is_indexable|Module::Metadata/is_indexable>
=head2 filename_match
use Test::Version 2.0 { filename_match => [qr{Foo/Bar.pm$}] };
Only test files that match the given pattern. Pattern may be a list of
strings, regular expressions or code references. The filename will match
if it matches one or more patterns.
=over 4
=item string
The file matches if it matches the pattern string exactly.
=item regular expression
The file matches if it matches the regular expression.
=item code reference
The file matches if the code reference returns a true value. The filename
is passed in as the only argument to the code reference.
=back
=head2 multiple
use Test::Version 2.02 { multiple => 1 };
Test each version for each package if multiple packages are found in a file.
=head1 SEE ALSO
The goal is to have the functionality of all of these.
=over
=item L<Test::HasVersion>
=item L<Test::ConsistentVersion>
=item L<Test::GreaterVersion>
=back
If you are using L<Dist::Zilla> there is a plugin
=over
=item L<Dist::Zilla::Plugin::Test::Version>
=back
=head1 BUGS
Please report any bugs or feature requests on the bugtracker website
L<https://github.com/plicease/test-version/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.
=head1 CONTRIBUTORS
=for stopwords Damyan Ivanov Dave Rolsky Gabor Szabo Karen Etheridge Michael G. Schwern Mike Doherty particle
=over 4
=item *
Damyan Ivanov <dmn@debian.org>
=item *
Dave Rolsky <autarch@urth.org>
=item *
Gabor Szabo <gabor@szabgab.com>
=item *
Karen Etheridge <ether@cpan.org>
=item *
Michael G. Schwern <schwern@pobox.com>
=item *
Mike Doherty <doherty@cs.dal.ca>
=item *
particle <particle@cpan.org>
=back
=head1 AUTHORS
=over 4
=item *
Graham Ollis <plicease@cpan.org>
=item *
Caleb Cushing <xenoterracide@gmail.com>
=back
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2018 by Caleb Cushing.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
=cut