Dist-Conformance
view release on metacpan or search on metacpan
lib/Dist/Conformance.pm view on Meta::CPAN
my $skipped = $self->skipped;
my %merged = %{ $flavor->checks };
Dist::Conformance::Flavor::merge_checks( \%merged, $self->config->{checks} );
my %variables = ( %{ $flavor->variables }, %{ $self->config->{variables} } );
[
map { $self->make_check( $_, $merged{$_}, \%variables ) }
grep { !$skipped->{$_} } $flavor->check_names
];
};
}
sub make_check ( $self, $name, $config, $variables ) {
my $flavor = $self->flavor->name;
my $module = $config->{module} // $name;
my $class = index( $module, '::' ) >= 0 ? $module : "Dist::Conformance::Check::$module";
if ( !Dist::Conformance::Schema::matches( $Dist::Conformance::Check::MODULE, $class ) ) {
die "The flavor $flavor runs the check $name, but $class is not the name of a module\n";
}
if ( !eval { Module::Runtime::require_module($class); 1 } ) {
die( "The flavor $flavor runs the check $name, but its module $class does not load: " . Dist::Conformance::Schema::first_line($@), "\n" );
}
die "The flavor $flavor runs the check $name, but $class is not a Dist::Conformance::Check\n" if !$class->isa('Dist::Conformance::Check');
my $repo = exists $self->config->{checks}{$name} ? " with $Dist::Conformance::Config::FILE" : q{};
return $class->from_config(
name => $name,
config => $config,
source => $self->source,
variables => $variables,
where => "the check $name of the flavor $flavor$repo",
);
}
our %STATUSES = map { $_ => 1 } qw{pass fail info};
sub check ($self) {
my $snapshot = $self->snapshot;
my %run = map { $_->name => $_ } @{ $self->checks };
my @results;
for my $name ( $self->flavor->check_names ) {
my $check = $run{$name};
if ( !$check ) {
push @results, { check => $name, status => 'skip', detail => "skipped by $Dist::Conformance::Config::FILE" };
next;
}
my ( $status, $detail ) = eval { $check->check($snapshot) };
if ( !defined $status ) {
( $status, $detail ) = ( 'fail', 'the check died: ' . Dist::Conformance::Schema::first_line($@) );
}
elsif ( !$STATUSES{$status} ) {
( $status, $detail ) = ( 'fail', "the check returned the status $status, which is not pass, fail or info" );
}
$status = 'info' if $status eq 'fail' && $check->config->{level} eq 'info';
push @results, { check => $name, status => $status, detail => $detail // q{} };
}
return \@results;
}
sub update ( $self, %options ) {
my @results;
for my $check ( @{ $self->checks } ) {
my $update = Dist::Conformance::Update->new( snapshot => $self->snapshot, dry_run => $options{dry_run} // 0 );
if ( !eval { $check->update($update); 1 } ) {
$update->by_hand( 'The update died: ' . Dist::Conformance::Schema::first_line($@) );
}
push @results, { check => $check->name, changes => $update->changes, manual => $update->manual };
}
return \@results;
}
sub failed ($results) {
return List::Util::any { $_->{status} eq 'fail' } @$results;
}
__END__
=pod
=encoding UTF-8
=head1 NAME
Dist::Conformance - Whether the development harness of a repository conforms to its standards, and bringing it up to date
=head1 VERSION
version 0.003
=head1 SYNOPSIS
use 5.040;
use Dist::Conformance;
my $conformance = Dist::Conformance->new( root => '/bogus/checkout' );
for my $result ( @{ $conformance->check } ) {
say join "\t", @{$result}{qw{status check detail}};
}
my $plan = $conformance->update( dry_run => 1 );
say "$_->{action} $_->{path}" for map { @{ $_->{changes} } } @$plan;
=head1 DESCRIPTION
Dist::Conformance answers one question about a repository: does its
development harness conform to the standards of an organization, and if not,
can the tool bring it up to date?
The harness is everything around the code that makes working on it safe: the
agent's instructions in F<CLAUDE.md>, the perlcritic profile and the
configuration of the other tools, the git hooks that run them, the files that
the build needs, and the Claude Code settings that load the right plugin.
Four pieces answer the question:
=over
=item The standards
( run in 1.158 second using v1.01-cache-2.11-cpan-1236ad508bc )