Ctrl K
ComunidadGuías…

script/architecture-policies

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env perl
# SPDX-FileCopyrightText: 2026 GPForum Contributors
# SPDX-License-Identifier: BSD-3-Clause
package main;
use v5.40;
use FindBin qw($Bin);
use lib "$Bin/lib";
use lib 'lib';
use GPForum::Quality::AsyncSyntax;
use Carp        qw(croak);
use Digest::SHA qw(sha256_hex);
use JSON::MaybeXS;
use Mojo::File qw(path);
use Perl::Critic;
use PPI::Document;
our $VERSION = '0.001';

sub _observe_file ( $critic, $file, $inventory ) {

    # Every policy requires one of these literal tokens. Avoid parsing the
    # many value objects that cannot trigger one; PPI decides all matches.
    my $file_source = $file->slurp;
    return
         if index( $file_source, 'resultset' ) < 0
      && index( $file_source, 'DBIx::Class' ) < 0
      && index( $file_source, 'GPForum::Schema' ) < 0
      && $file_source !~ /\$(?:c|controller)\b/msx;
    my $document;
    for my $violation ( $critic->critique("$file") ) {
        $document //= PPI::Document->new("$file");
        my $location = $violation->location;
        my $node     = $document->find_first(
            sub {
                $_[1]->isa('PPI::Token')
                  && $_[1]->line_number == $location->[0]
                  && $_[1]->column_number == $location->[1];
            }
        );
        $node //= $document;
        while ( $node->parent && !$node->isa('PPI::Statement::Sub') ) {
            $node = $node->parent;
        }
        my $subroutine_source = $node->content;
        $subroutine_source =~ s/\s+/ /gmsx;
        my $key = join q{|}, $violation->policy, $file,
          sha256_hex($subroutine_source);
        $inventory->{$key}++;
    }
    return;
}

my $critic = Perl::Critic->new(
    -profile => q{},
    -only    => 1,
    -include => [
        qw(GPForum::ControllerPersistence GPForum::ServiceContext GPForum::ResultsetBoundary)
    ]
);
my %observed;

for my $file ( sort grep { /[.]pm\z/msx } @{ path('lib/GPForum')->list_tree } )
{
    _observe_file( $critic, $file, \%observed );
}

# One layout whichever JSON backend is installed, so the inventory's diff
# shows only the keys that changed.
my $json = JSON::MaybeXS->new( canonical => 1, pretty => 1 )->space_before(0);
if ( @ARGV && $ARGV[0] eq '--inventory' ) {
    print $json->encode( \%observed ) or croak 'cannot print inventory';
    exit 0;
}
my $baseline =
  $json->decode( path('etc/architecture-policy-baseline.json')->slurp );

# The inventory is a ratchet both ways. A violation it does not hold is new;
# one it holds that is no longer seen was fixed, or its sub was edited and so
# re-hashed, and its key has to go. Either way the inventory is regenerated
# with --inventory once the change is reviewed, so it only ever shrinks.
my @new = grep { $observed{$_} > ( $baseline->{$_} // 0 ) } sort keys %observed;
my @gone =
  grep { ( $observed{$_} // 0 ) < $baseline->{$_} } sort keys %{$baseline};
my @problems = (
    (
        @new
        ? 'architecture policy violations outside the reviewed legacy'
          . " inventory:\n"
          . join( "\n", @new )
        : ()
    ),
    (
        @gone
        ? 'inventory entries no longer observed (regenerate it with'
          . " --inventory):\n"
          . join( "\n", @gone )
        : ()
    ),
);
if (@problems) {
    print {*STDERR} join( "\n\n", @problems ), "\n"
      or croak 'cannot print the problems';
    exit 1;
}
print "architecture policies: no new violations, none fixed and left listed\n"
  or croak 'cannot print result';
exit 0;