package MJB::Backend::Role::SystemCommand; use Moo::Role; use Storable qw( dclone ); use IPC::Run3 qw( run3 ); sub system_command { my ( $self, $cmd, $settings ) = @_; $settings ||= {}; # Change the directory, if requested. if ( $settings->{chdir} ) { # Throw an error if that directory doesn't exist. die "Error: directory " . $settings->{chdir} . "doesn't exist." unless -d $settings->{chdir}; # Change to that directory, or die with error. chdir $settings->{chdir} or die "Failed to chdir to " . $settings->{chdir} . ": $!"; $settings->{return_chdir} = getcwd(); } # Mask values we don't want exposed in the logs. my $masked_cmd = dclone($cmd); if ( ref $settings->{mask} eq 'HASH' ) { foreach my $key ( keys %{$settings->{mask}} ) { my $value = $settings->{mask}{$key}; $masked_cmd = [ map { s/\Q$key\E/$value/g; $_ } @{$masked_cmd} ]; } } # Log the lines my ( $out, $err ); my $ret = run3( $cmd, \undef, sub { chomp $_; # Mask values we don't want exposed in the logs. if ( ref $settings->{mask} eq 'HASH' ) { foreach my $key ( keys %{$settings->{mask}} ) { my $value = $settings->{mask}{$key}; s/\Q$key\E/$value/g; } } $out .= "$_\n"; }, sub { chomp $_; # Mask values we don't want exposed in the logs. if ( ref $settings->{mask} eq 'HASH' ) { foreach my $key ( keys %{$settings->{mask}} ) { my $value = $settings->{mask}{$key}; s/\Q$key\E/$value/g; } } $err .= "$_\n"; }); # Check stderr for errors to fail on. if ( $settings->{fail_on_stderr} ) { my @tests = @{$settings->{fail_on_stderr}}; while ( my $regex = shift @tests ) { my $reason = shift @tests; if ( $err =~ /$regex/ ) { die $reason; } } } # Return to the directory we started in if we chdir'ed. if ( $settings->{return_chdir} ) { chdir $settings->{return_chdir} or die "Failed to chdir to " . $settings->{chdir} . ": $!"; } if ( $ENV{MJB_DEBUG} ) { require Data::Dumper; print Data::Dumper::Dumper({ stdout => $out, stderr => $err, exitno => $ret, }); } return { stdout => $out, stderr => $err, exitno => $ret, }; } 1;