diff --git a/lib/App/Yath2.pm b/lib/App/Yath2.pm index 1f9450bcf..317210505 100644 --- a/lib/App/Yath2.pm +++ b/lib/App/Yath2.pm @@ -506,10 +506,12 @@ sub process_args { $self->{+ENV_VARS} = $self->{+STATE_ENV}; $self->{+OPTION_STATE} = $state; + my $no_plugins = eval { $settings->yath->no_plugin_scan } // 0; for my $module (keys %{$self->{+STATE_MODULES}}) { for my $set (['yath', 'plugins', 'App::Yath2::Plugin'], ['renderer', 'classes', 'App::Yath2::Renderer'], ['resource', 'classes', 'App::Yath2::Resource']) { my ($group, $field, $type) = @$set; next unless $module->isa($type); + next if $no_plugins && $type eq 'App::Yath2::Plugin'; $settings->$group->option($field => {}) unless $settings->$group->$field; my $args = $settings->$group->$field->{$module} //= []; next unless $module->can('args_from_settings'); diff --git a/lib/App/Yath2/Options/Yath.pm b/lib/App/Yath2/Options/Yath.pm index 78ba25f63..0b0cece53 100644 --- a/lib/App/Yath2/Options/Yath.pm +++ b/lib/App/Yath2/Options/Yath.pm @@ -144,6 +144,12 @@ option_group {group => 'yath', category => 'Yath Options'} => sub { return $class => $args; }, ); + + option no_plugin_scan => ( + type => 'Bool', + default => 0, + description => 'Disable auto-loading of configured plugins. Explicitly requested plugins (-p) are still loaded. Useful in tests to prevent ambient plugin configuration from interfering.', + ); }; 1; diff --git a/t/Yath/integration/replay.t b/t/Yath/integration/replay.t index e0c96d150..4eb8f93b8 100644 --- a/t/Yath/integration/replay.t +++ b/t/Yath/integration/replay.t @@ -1,8 +1,5 @@ # HARNESS-CONFLICTS YATH -use Test2::V0; -plan skip_all => "TODO: replay command not yet aligned with current log/streamer format"; -__END__ - +# HARNESS-DURATION-SLOW use Test2::V0; use File::Temp qw/tempdir/; @@ -21,6 +18,7 @@ sub clean_output { my $out = shift; $out->{output} =~ s/^.*duration.*$//m; $out->{output} =~ s/^.*Wrote log file:.*$//m; + $out->{output} =~ s/^.*Wrote archive:.*$//m; $out->{output} =~ s/^.*Symlinked to:.*$//m; $out->{output} =~ s/^.*Linked log file:.*$//m; $out->{output} =~ s/^\s*Wall Time:.*seconds//m; @@ -35,6 +33,11 @@ sub clean_output { # Can remove this once the fixme is removed $out->{output} =~ s/^FIXME: publish should send log to server$//gm; + # Normalize display job numbers: parallel jobs complete in non-deterministic + # order so the renderer assigns job 1/2/... differently each run. Replace + # all "job N" sequences with a "job N" sentinel so both sides match. + $out->{output} =~ s/\bjob\s+\d+\b/job N/g; + my @lines; my $start; for my $line (split /\n/, $out->{output}) { @@ -45,7 +48,32 @@ sub clean_output { push @lines => $line; } - $out->{output} = join "\n" => @lines; + # Live (`yath test`) and replay emit the same per-job lines but in + # different orders, depending on how the failed job's diagnostics + # (FAIL / DIAG / REASON) interleave with another job's status line. + # Trying to group diag lines back to "their" status line is fragile + # because diag lines carry only `job N` (already normalised above) -- + # there is no filename to disambiguate. The robust fix is to + # canonicalise the order of every `job N`-prefixed line: collect + # consecutive runs of them, sort the run, flush. Non-job lines (the + # "The following jobs failed:" table, the "Yath Result Summary" block, + # etc.) pass through verbatim so the file's overall narrative stays + # intact. + my @normalized; + my @run; + for my $line (@lines) { + if ($line =~ /\bjob\s+N\b/) { + push @run => $line; + } + else { + push @normalized => sort @run if @run; + @run = (); + push @normalized => $line; + } + } + push @normalized => sort @run if @run; + + $out->{output} = join "\n" => @normalized; } my $out1 = yath(