PageRenderTime 40ms CodeModel.GetById 36ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Dist/Zilla/Plugin/Run/Role/Runner.pm

https://github.com/Getty/p5-dist-zilla-plugin-run
Perl | 291 lines | 217 code | 54 blank | 20 comment | 24 complexity | f4a3e29da30099f9aa274bb4c03633ca MD5 | raw file
  1. use strict;
  2. use warnings;
  3. package Dist::Zilla::Plugin::Run::Role::Runner;
  4. # vim: set ts=8 sts=4 sw=4 tw=115 et :
  5. our $VERSION = '0.049';
  6. use Moose::Role;
  7. use namespace::autoclean;
  8. use File::Spec (); # core
  9. use Config (); # core
  10. use Moose::Util 'find_meta';
  11. has perlpath => (
  12. is => 'ro',
  13. isa => 'Str',
  14. lazy => 1,
  15. builder => 'current_perl_path',
  16. );
  17. has censor_commands => (
  18. is => 'ro',
  19. isa => 'Bool',
  20. default => 0,
  21. );
  22. has [ qw(run run_if_trial run_no_trial run_if_release run_no_release) ] => (
  23. is => 'ro',
  24. isa => 'ArrayRef[Str]',
  25. default => sub { [] },
  26. );
  27. has eval => (
  28. is => 'ro',
  29. isa => 'ArrayRef[Str]',
  30. default => sub { [] },
  31. );
  32. has fatal_errors => (
  33. is => 'ro',
  34. isa => 'Bool',
  35. default => 1,
  36. );
  37. has quiet => (
  38. is => 'ro',
  39. isa => 'Bool',
  40. default => 0,
  41. );
  42. around dump_config => sub
  43. {
  44. my ($orig, $self) = @_;
  45. my $config = $self->$orig;
  46. $config->{+__PACKAGE__} = {
  47. version => $VERSION,
  48. (map { $_ => $self->$_ ? 1 : 0 } qw(fatal_errors quiet)),
  49. map {
  50. @{ $self->$_ }
  51. # look for user:password URIs
  52. ? ( $_ => [ map { $self->censor_commands || /\b\w+:[^@]+@\b/ ? 'REDACTED' : $_ } @{ $self->$_ } ] )
  53. : ()
  54. }
  55. qw(run run_if_trial run_no_trial run_if_release run_no_release eval),
  56. };
  57. return $config;
  58. };
  59. around BUILDARGS => sub {
  60. my ( $orig, $class, @args ) = @_;
  61. my $built = $class->$orig(@args);
  62. foreach my $dep (qw( notexist_fatal )) {
  63. if ( exists $built->{$dep} ) {
  64. warn(" !\n ! $class attribute '$dep' is deprecated and has no effect.\n !\n");
  65. delete $built->{$dep};
  66. }
  67. }
  68. return $built;
  69. };
  70. sub _is_trial {
  71. my $self = shift;
  72. # we want to avoid provoking other plugins prematurely, but also be as
  73. # accurate as we can with this status
  74. my $release_status_attr = find_meta($self->zilla)->find_attribute_by_name('release_status');
  75. return ( $self->zilla->is_trial ? 1 : 0 ) if
  76. not $release_status_attr # legacy (before Dist::Zilla 5.035)
  77. or $release_status_attr->has_value($self->zilla);
  78. # otherwise, only use the logic that does not require zilla->version
  79. # before Dist::Zilla 5.035, this is what $zilla->is_trial returned
  80. return eval { $self->zilla->_release_status_from_env =~ /\A(?:testing|unstable)\z/ } ? 1 : 0;
  81. }
  82. sub _call_script {
  83. my ( $self, $params ) = @_;
  84. foreach my $run_cmd (@{$self->run}) {
  85. $self->_run_cmd($run_cmd, $params);
  86. }
  87. foreach my $run_cmd (@{$self->run_if_trial}) {
  88. if ($self->_is_trial) {
  89. $self->_run_cmd($run_cmd, $params);
  90. } else {
  91. $self->log_debug([ 'not executing, because no trial: %s', $run_cmd ]);
  92. }
  93. }
  94. foreach my $run_cmd (@{$self->run_no_trial}) {
  95. if ($self->_is_trial) {
  96. $self->log_debug([ 'not executing, because trial: %s', $run_cmd ]);
  97. } else {
  98. $self->_run_cmd($run_cmd, $params);
  99. }
  100. }
  101. my $is_release = defined $ENV{'DZIL_RELEASING'} && $ENV{'DZIL_RELEASING'} == 1 ? 1 : 0;
  102. foreach my $run_cmd (@{$self->run_if_release}) {
  103. if ($is_release) {
  104. $self->_run_cmd($run_cmd, $params);
  105. } else {
  106. $self->log_debug([ 'not executing, because no release: %s', $run_cmd ]);
  107. }
  108. }
  109. foreach my $run_cmd (@{$self->run_no_release}) {
  110. if ($is_release) {
  111. $self->log_debug([ 'not executing, because release: %s', $run_cmd ]);
  112. } else {
  113. $self->_run_cmd($run_cmd, $params);
  114. }
  115. }
  116. if (my @code = @{ $self->eval }) {
  117. my $code = join "\n", @code;
  118. $self->_eval_cmd($code, $params);
  119. }
  120. }
  121. sub _run_cmd {
  122. my ( $self, $run_cmd, $params, $dry_run ) = @_;
  123. if ($dry_run) {
  124. $self->log_debug([ 'dry run, would run: %s', $run_cmd ]);
  125. return;
  126. }
  127. return if not $run_cmd;
  128. require IPC::Open3; # core
  129. my $command = $self->build_formatter($params)->format($run_cmd);
  130. $self->${ $self->quiet ? \'log_debug' : \'log' }([ 'executing: %s', $command ]);
  131. # autoflush STDOUT so we can see command output right away
  132. local $| = 1;
  133. # combine STDOUT and STDERR for ease of proxying through the logger
  134. my $pid = IPC::Open3::open3(my ($in, $out), undef, $command);
  135. binmode $out, ':crlf' if $^O eq 'MSWin32';
  136. while(defined(my $line = <$out>)){
  137. chomp($line); # logger appends its own newline
  138. $self->${ $self->quiet ? \'log_debug' : \'log' }($line);
  139. }
  140. # zombie repellent
  141. waitpid($pid, 0);
  142. if (my $status = ($? >> 8)) {
  143. $self->${ $self->fatal_errors ? \'log_fatal' : $self->quiet ? \'log_debug' : \'log'}
  144. ([ 'command exited with status %s (%s)', $status, $? ]);
  145. }
  146. else {
  147. $self->log_debug('command executed successfully');
  148. }
  149. }
  150. sub _eval_cmd {
  151. my ( $self, $code, $params, $dry_run ) = @_;
  152. if ($dry_run) {
  153. $self->log_debug([ 'dry run, would evaluate: %s', $code ]);
  154. return;
  155. }
  156. $code = $self->build_formatter($params)->format($code);
  157. $self->${ $self->quiet ? \'log_debug' : \'log' }([ 'evaluating: %s', $code ]);
  158. my $sub = __eval_wrapper($code);
  159. $sub->($self);
  160. my $error = $@;
  161. if (defined $error and $error ne '') {
  162. if ($self->fatal_errors and $self->quiet and not $self->zilla->logger->get_debug) {
  163. $self->log([ 'evaluated: %s', $code]);
  164. }
  165. $self->${ $self->fatal_errors ? \'log_fatal' : $self->quiet ? \'log_debug' : \'log'}
  166. ([ 'evaluation died: %s', $error ]);
  167. }
  168. }
  169. sub __eval_wrapper {
  170. my $code = shift;
  171. sub { eval $code };
  172. }
  173. around mvp_multivalue_args => sub {
  174. my ($original, $self) = @_;
  175. my @res = $self->$original();
  176. push @res, qw( run run_no_trial run_if_trial run_if_release run_no_release eval );
  177. @res;
  178. };
  179. my $path_separator = (File::Spec->catfile(qw(a b)) =~ m/^a(.+?)b$/)[0];
  180. sub build_formatter {
  181. my ( $self, $params ) = @_;
  182. require String::Formatter;
  183. String::Formatter->VERSION(0.102082);
  184. my $codes = {
  185. # not always available
  186. # explicitly pass a string (not an object) [rt-72008]
  187. a => sub {
  188. return "$params->{archive}" if defined $params->{archive};
  189. $self->log('attempting to use %a in a non-Release plugin');
  190. '';
  191. },
  192. # source dir
  193. o => sub {
  194. my $dir = $params->{source_dir} || $self->zilla->root;
  195. return $dir ? "$dir" : '';
  196. },
  197. # build dir or mint dir
  198. d => sub {
  199. require Path::Tiny;
  200. # stringify build directory
  201. my $dir = $params->{dir} || $self->zilla->built_in;
  202. return Path::Tiny::path($dir)->canonpath if $dir;
  203. $self->log('attempting to use %d in before_build');
  204. '';
  205. },
  206. # distribution name
  207. n => sub { $self->zilla->name },
  208. # backward compatibility (don't error)
  209. s => '',
  210. # portability
  211. p => $path_separator,
  212. x => sub { $self->perlpath },
  213. };
  214. # available during build, not mint
  215. unless( $params->{minting} ){
  216. $codes->{v} = sub { $self->zilla->version };
  217. $codes->{t} = sub { $self->_is_trial ? '-TRIAL' : '' };
  218. }
  219. # positional replacement of %s (backward compatible)
  220. if( my @pos = @{ $params->{pos} || [] } ){
  221. # where are you defined-or // operator?
  222. $codes->{s} = sub {
  223. my $s = shift(@pos);
  224. $s = $s->() if ref $s eq 'CODE';
  225. defined($s) ? $s : '';
  226. };
  227. }
  228. return String::Formatter->new({ codes => $codes });
  229. }
  230. sub current_perl_path { $^X }
  231. 1;