File Coverage

blib/lib/App/Cme/Command/run.pm
Criterion Covered Total %
statement 218 252 86.5
branch 43 76 56.5
condition 21 36 58.3
subroutine 25 27 92.5
pod 5 11 45.4
total 312 402 77.6


line stmt bran cond sub pod time code
1             #
2             # This file is part of App-Cme
3             #
4             # This software is Copyright (c) 2014-2022 by Dominique Dumont <ddumont@cpan.org>.
5             #
6             # This is free software, licensed under:
7             #
8             # The GNU Lesser General Public License, Version 2.1, February 1999
9             #
10             # ABSTRACT: Run a cme script
11              
12             package App::Cme::Command::run ;
13             $App::Cme::Command::run::VERSION = '1.037';
14 2     2   1238 use strict;
  2         9  
  2         50  
15 2     2   9 use warnings;
  2         3  
  2         41  
16 2     2   25 use v5.20;
  2         7  
17 2     2   465 use File::HomeDir;
  2         4913  
  2         125  
18 2     2   703 use Path::Tiny;
  2         13188  
  2         109  
19 2     2   832 use Config::Model;
  2         438434  
  2         151  
20 2     2   21 use Log::Log4perl qw(get_logger :levels);
  2         4  
  2         22  
21 2     2   1018 use YAML::PP;
  2         54189  
  2         94  
22              
23 2     2   13 use Encode qw(decode_utf8);
  2         3  
  2         109  
24              
25 2     2   421 use App::Cme -command ;
  2         6  
  2         15  
26              
27 2     2   15318 use base qw/App::Cme::Common/;
  2         6  
  2         701  
28 2     2   21 use feature qw/postderef signatures/;
  2         3  
  2         247  
29 2     2   10 no warnings qw/experimental::postderef experimental::signatures experimental::smartmatch/;
  2         4  
  2         6340  
30              
31              
32             my $__test_home = '';
33             # used only by tests
34             ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
35 0     0   0 sub _set_test_home { $__test_home = shift; return;}
  0         0  
36              
37             my $home = $__test_home || File::HomeDir->my_home;
38              
39             my @script_paths = map {path($_)} (
40             "$home/.cme/scripts",
41             "/etc/cme/scripts/",
42             );
43              
44             push @script_paths, path($INC{"Config/Model.pm"})->parent->child("Model/scripts") ;
45              
46             sub opt_spec {
47 15     15 1 41 my ( $class, $app ) = @_;
48             return (
49 15         138 [ "arg=s@" => "script argument. run 'cme run <script> -doc' for possible arguments" ],
50             [ "backup:s" => "Create a backup of configuration files before saving." ],
51             [ "commit|c:s" => "commit change with passed message" ],
52             [ "cat" => "Show the script file" ],
53             [ "no-commit|nc!" => "skip commit to git" ],
54             [ "doc!" => "show documention of script" ],
55             [ "list!" => "list available scripts" ],
56             $class->cme_global_options,
57             );
58             }
59              
60             sub validate_args {
61 15     15 1 51561 my ($self, $opt, $args) = @_;
62              
63 15         100 $self->check_unknown_args($args);
64 15         28 return;
65             }
66              
67             sub usage_desc {
68 15     15 1 28917 my ($self) = @_;
69 15         66 my $desc = $self->SUPER::usage_desc; # "%c COMMAND %o"
70 15         295 return "$desc [ script ] [ -args foo=12 [ -args bar=13 ]";
71             }
72              
73             sub description {
74 0     0 1 0 my ($self) = @_;
75 0         0 return $self->get_documentation;
76             }
77              
78 15     15 0 24 sub check_script_arguments ($self, $opt, $script_name) {
  15         31  
  15         36  
  15         25  
  15         18  
79 15 50 33     178 if ($opt->{list} or not $script_name) {
80 0         0 my @scripts;
81 0         0 foreach my $path ( @script_paths ) {
82 0 0       0 next unless $path->is_dir;
83 0         0 push @scripts, grep { ! /~$/ } $path->children();
  0         0  
84             }
85 0 0       0 say $opt->{list} ? "Available scripts:" : "Missing script argument. Choose one of:";
86 0         0 say map {"- ".$_->basename."\n"} @scripts ;
  0         0  
87 0         0 return 0;
88             }
89 15         50 return 1;
90             }
91              
92 15     15 0 26 sub find_script_file ($self, $script_name) {
  15         24  
  15         20  
  15         20  
93 15         26 my $script;
94 15 50       74 if ($script_name =~ m!/!) {
95 15         91 $script = path($script_name);
96             }
97             else {
98             # check script in known locations
99 0         0 foreach my $path ( @script_paths ) {
100 0 0       0 next unless $path->is_dir;
101 0         0 $script = $path->child($script_name);
102 0 0       0 last if $script->is_file;
103             }
104             }
105              
106 15 50       700 die "Error: cannot find script $script_name\n" unless $script->is_file;
107              
108 15         700 return $script;
109             }
110              
111             # replace variables with command arguments or eval'ed variables or env variables
112             ## no critic (Subroutines::ProhibitManyArgs)
113 26     26 0 34 sub replace_var_in_value ($user_args, $script_var, $default, $missing, $vars) {
  26         32  
  26         28  
  26         39  
  26         29  
  26         37  
  26         27  
114 26         115 my $var_pattern = qr~(?<!\\) \$([a-zA-Z]\w+) (?!\s*{)~x;
115              
116 26         60 foreach ($vars->@*) {
117             # change $var but not \$var, not $var{} and not $1
118 12         224 s~ $var_pattern
119 14   100     158 ~ $user_args->{$1} // $script_var->{$1} // $ENV{$1} // $default->{$1} // '$'.$1 ~xeg;
      66        
      66        
      66        
120              
121             # register vars without replacements
122 12         118 foreach my $var (m~ $var_pattern ~xg) {
123 3         8 $missing->{$var} = 1 ;
124             }
125              
126             # now change \$var in $var
127 12         51 s!\\\$!\$!g;
128             }
129 26         51 return;
130             }
131              
132 13     13 0 24 sub parse_script_lines ($script, $lines) {
  13         26  
  13         19  
  13         20  
133             # provide default values
134 13         85 my %default ;
135             my @load;
136 13         0 my @doc;
137 13         0 my @code;
138 13         0 my @var;
139 13         0 my $commit_msg ;
140 13         0 my $app;
141 13         22 my $line_nb = 0;
142              
143             # check content, store app
144 13         43 while ($lines->@*) {
145 38         71 my $line = shift $lines->@*;
146 38         49 $line_nb++;
147 38         71 $line =~ s/#.*//; # remove comments
148 38         87 $line =~ s/^\s+//;
149 38         108 $line =~ s/\s+$//;
150 38         54 my ($key,@value);
151              
152 38 100       111 if ($line =~ /^---\s*(\w+)$/) {
    100          
153 3         9 $key = $1;
154 3         17 while ($lines->[0] !~ /^---/) {
155 9         18 $lines->[0] =~ s/#.*//; # remove comments
156 9         26 push @value, shift $lines->@*;
157             }
158             }
159             elsif ($line eq '---') {
160 3         10 next;
161             }
162             else {
163 32         125 ($key,@value) = split /[\s:]+/, $line, 2;
164             }
165              
166 35 50       91 next unless $key ; # empty line
167              
168 35         60 for ($key) {
169 35         76 when (/^app/) {
170 13         51 $app = $value[0];
171             }
172 22         67 when ('var') {
173 6         26 push @var, [ $line_nb, @value ];
174             }
175 16         28 when ('default') {
176             # multi-line default value is not supported
177 2         9 my ($dk, $dv) = split /[\s:=]+/, $value[0], 2;
178 2         11 $default{$dk} = $dv;
179             }
180 14         22 when ('code') {
181 2 100       30 die "Error line $line_nb: Cannot mix code and load section\n" if @load;
182 1         4 push @code, @value;
183             }
184 12         27 when ('doc') {
185 0         0 push @doc, @value;
186             }
187 12         22 when ('load') {
188 12 50       44 die "Error line $line_nb: Cannot mix code and load section\n" if @code;
189 12         48 push @load, @value;
190             }
191 0         0 when ('commit') {
192 0         0 $commit_msg = join "\n",@value;
193             }
194 0         0 default {
195 0         0 die "Error in file $script line $line_nb: unexpected '$key' instruction\n";
196             }
197             }
198             }
199              
200             return {
201 12         103 app => $app,
202             doc => \@doc,
203             code => \@code,
204             commit_msg => $commit_msg,
205             default => \%default,
206             load => \@load,
207             var => \@var,
208             }
209             }
210              
211 13     13 0 21 sub process_script_vars ($user_args, $data) {
  13         33  
  13         18  
  13         24  
212             # $var is used in eval'ed strings
213 13         17 my %var;
214              
215             # find if all variables are accounted for
216 13         45 $data->{missing} = {};
217              
218             # %args can be used in var section of a script. A new entry in
219             # added in %missing if the script tries to read an undefined value
220 13         179 tie my %args, 'App::Cme::Run::Var',$data->{missing}, $data->{default};
221 13         174 %args = $user_args->%*;
222              
223 13   50     176 my $var = delete $data->{var} // [];
224 13         31 foreach my $eval_data ($var->@*) {
225 7         17 my ($line_nb, @value);
226 7 100       25 if (ref $eval_data) {
227             # coming from text format
228 6         17 ($line_nb, @value) = $eval_data->@*;
229             # eval'ed string comes from system file, not from user data
230 6         779 my $res = eval ("@value") ; ## no critic (ProhibitStringyEval)
231 6 50       51 die "Error in var specification line $line_nb: $@\n" if $@;
232             }
233             else {
234             # coming from YAML format
235 1         131 my $res = eval ($eval_data) ; ## no critic (ProhibitStringyEval)
236 1 50       10 die "Error in var specification: $@\n" if $@;
237             }
238             }
239              
240 13         67 replace_var_in_value($user_args, \%var, $data->{default},$data->{missing}, $data->{doc});
241 13         52 replace_var_in_value($user_args, \%var, $data->{default},$data->{missing}, $data->{load});
242              
243 13         118 $data->{values} = {$data->{default}->%*, %var, $user_args->%*};
244              
245 13         64 return $data;
246             }
247              
248 15     15 0 149 sub parse_script ($script, $content, $user_args) {
  15         28  
  15         22  
  15         33  
  15         22  
249 15         109 my $lines->@* = split /\n/,$content;
250              
251 15         34 given ($lines->[0]) {
252 15         79 when (/Format: perl/i) {
253             ## no critic (ProhibitStringyEval)
254 1         188 my $data = eval($content);
255 1 50       7 die "Error in script $script (Perl format): $@\n" if $@;
256 1         4 foreach my $forbidden (qw/load var default/) {
257 3 50       8 die "Unexpected '$forbidden\ section in Perl format script $script\n" if $data->{$forbidden};
258             }
259 1 50       3 die "Unexpected 'code' section in Perl format script $script. Please use a sub section.\n" if $data->{code};
260 1         5 return $data;
261             }
262 14         34 when (/Format: yaml/i) {
263 1         14 my $ypp = YAML::PP->new;
264 1         3564 my $data = $ypp->load_string($content);
265 1         5871 foreach my $key (qw/doc code load var/) {
266 4 100       11 next unless defined $data->{$key};
267 2 50       5 next if ref $data->{$key} eq 'ARRAY';
268 2         5 $data->{$key} = [ $data->{$key} ]
269             }
270 1 50 33     10 if ($data->{default} and ref $data->{default} ne 'HASH') {
271 0   0     0 die "default spec must be a hash ref, not a ", ref $data->{default} // 'scalar', "\n";
272             }
273 1         5 $data = process_script_vars ($user_args, $data);
274 1         120 return $data;
275             }
276 13         33 default {
277 13         45 my $data = parse_script_lines ($script, $lines);
278 12         43 $data = process_script_vars ($user_args, $data);
279 12         41 return $data;
280             }
281             }
282              
283             }
284              
285             sub execute {
286 15     15 1 100 my ($self, $opt, $app_args) = @_;
287              
288             # cannot use logger until Config::Model is initialised
289              
290             # see Debian #839593 and perlunicook(1) section X 13
291 15         48 @$app_args = map { decode_utf8($_, 1) } @$app_args;
  16         307  
292              
293 15         162 my $script_name = shift @$app_args;
294              
295 15 50       58 return unless $self->check_script_arguments($opt, $script_name);
296              
297 15         52 my $script = $self->find_script_file($script_name);
298              
299 15         86 my $content = $script->slurp_utf8;
300              
301 15 50       3368 if ($opt->{cat}) {
302 0         0 print $content;
303 0         0 return;
304             }
305              
306             # parse variables passed on command line
307 15         32 my %user_args = map { split '=',$_,2; } @{ $opt->{arg} };
  6         43  
  15         59  
308              
309 15 100 66     138 if ($content =~ m/^#!/ or $content =~ /^use/m) {
310 1         4 splice @ARGV, 0,2; # remove 'run script' arguments
311 1     1   277 my $done = eval $script->slurp_utf8."\n1;\n"; ## no critic (BuiltinFunctions::ProhibitStringyEval)
  1         2  
  1         146  
  1         5  
312 1 50       15 die "Error in script $script_name: $@\n" unless $done;
313 1         70 return;
314             }
315              
316 14         65 my $script_data = parse_script($script, $content, \%user_args);
317 13         26 my $commit_msg = $script_data->{commit_msg};
318              
319 13 50       45 if ($opt->doc) {
320 0         0 say join "\n", $script_data->{doc}->@*;
321 0 0       0 say "will commit with message: '$commit_msg'" if $commit_msg;
322 0         0 return;
323             }
324              
325 13 100       131 if (my @missing = sort keys $script_data->{missing}->%*) {
326             die "Error: Missing variables '". join("', '",@missing)."' in command arguments for script $script\n"
327 3         21 ."Please use option '".join(' ', map { "-arg $_=xxx"} @missing)."'\n";
  5         113  
328             }
329              
330 10         71 $self->process_args($opt, [ $script_data->{app}, $app_args->@* ]);
331              
332             # override commit message. may also trigger a commit even if none
333             # is specified in script
334 10 50       35 if ($opt->{commit}) {
335 0         0 $commit_msg = $opt->{commit};
336             }
337              
338             # check if workspace and index are clean
339 10 50 33     42 if ($commit_msg and not $opt->{no_commit}) {
340             ## no critic(InputOutput::ProhibitBacktickOperators)
341 0         0 my $r = `git status --porcelain --untracked-files=no`;
342 0 0       0 die "Cannot run commit command in a non clean repo. Please commit or stash pending changes: $r\n"
343             if $r;
344             }
345              
346 10 100       30 $opt->{_verbose} = 'Loader' if $opt->{verbose};
347              
348             # call loads
349 10         51 my ($model, $inst, $root) = $self->init_cme($opt,$app_args);
350 10         59473 foreach my $load_str ($script_data->{load}->@*) {
351 8         54 $root->load($load_str);
352             }
353              
354 10 100       62393 if ($script_data->{code}) {
355 8         23 my $to_run = '';
356 8         58 while (my ($name, $value) = each $script_data->{values}->%*) {
357 11         56 $to_run .= "my \$$name = '$value';\n";
358             }
359 8         75 $to_run .= join("\n",$script_data->{code}->@*);
360 8         933 my $res = eval($to_run); ## no critic (ProhibitStringyEval)
361 8 50       6675 die "Error in code specification: $@\ncode is: \n$to_run\n" if $@;
362             }
363              
364 10 100       42 if ($script_data->{sub}) {
365 1         44 $script_data->{sub}->($root, \%user_args);
366             }
367              
368 10 50       6557 unless ($inst->needs_save) {
369 0         0 say "No change were applied";
370 0         0 return;
371             }
372              
373 10         236 $self->save($inst,$opt) ;
374              
375             # commit if needed
376 10 50 33     61 if ($commit_msg and not $opt->{no_commit}) {
377 0         0 system(qw/git commit -a -m/, $commit_msg);
378             }
379              
380 10         683 return;
381             }
382              
383             package App::Cme::Run::Var; ## no critic (Modules::ProhibitMultiplePackages)
384             $App::Cme::Run::Var::VERSION = '1.037';
385             require Tie::Hash;
386              
387             ## no critic (ClassHierarchies::ProhibitExplicitISA)
388             our @ISA = qw(Tie::ExtraHash);
389              
390             sub FETCH {
391 7     7   22 my ($self, $key) = @_ ;
392 7         18 my ($h, $missing, $default) = @$self;
393 7   100     33 my $res = $h->{$key} // $default->{$key} ;
394 7 100       21 $missing->{$key} = 1 unless defined $res;
395 7   100     106 return $res // '';
396             }
397              
398             1;
399              
400             __END__
401              
402             =pod
403              
404             =encoding UTF-8
405              
406             =head1 NAME
407              
408             App::Cme::Command::run - Run a cme script
409              
410             =head1 VERSION
411              
412             version 1.037
413              
414             =head1 SYNOPSIS
415              
416             $ cat ~/.cme/scripts/remove-mia
417             doc: remove mia from Uploaders. Require mia parameter
418             # declare app to configure
419             app: dpkg
420             # specify one or more instructions
421             load: ! control source Uploaders:-~/$mia$/
422             # commit the modifications with a message (git only)
423             commit: remove MIA dev $mia
424              
425             $ cme run remove-mia -arg mia=longgone@d3bian.org
426              
427             # cme run can also use environment variables
428             $ cat ~/.cme/scripts/add-me-to-uploaders
429             app: dpkg-control
430             load: source Uploaders:.push("$DEBFULLNAME <$DEBEMAIL>")
431              
432             $ cme run add-me-to-uploaders
433             Reading package lists... Done
434             Building dependency tree
435             Reading state information... Done
436             Changes applied to dpkg-control configuration:
437             - source Uploaders:3: '<undef>' -> 'Dominique Dumont <dod@debian.org>'
438              
439             # show the script documentation
440             $ cme run remove-mia -doc
441             remove mia from Uploaders. require mia parameter
442              
443             # list scripts
444             $ cme run -list
445             Available scripts:
446             - update-copyright
447             - add-me-to-uploaders
448              
449             =head1 DESCRIPTION
450              
451             Run a script written for C<cme>
452              
453             A script passed by name is searched in C<~/.cme/scripts>,
454             C</etc/cme/scripts> or C</usr/share/perl5/Config/Model/scripts>.
455             E.g. with C<cme run foo>, C<cme> loads either C<~/.cme/scripts/foo>,
456             C</etc/cme/scripts/foo> or
457             C</usr/share/perl5/Config/Model/scripts/foo>
458              
459             No search is done if the script is passed with a path
460             (e.g. C<cme run ./foo>)
461              
462             C<cme run> accepts scripts written with different syntaxes:
463              
464             =over
465              
466             =item in text
467              
468             For simple script, this text specifies the target app, the doc,
469             optional variables and a load string used by L<Config::Model::Loader> or
470             Perl code.
471              
472             =item YAML
473              
474             Like text above, but using Yaml syntax.
475              
476             =item Perl data structure
477              
478             Writing Perl code in a text file or in a YAML field can be painful as
479             Perl syntax is not highlighted. With a Perl data structure, a cme
480             script specifies the target app, the doc, optional variables, and a
481             perl subroutine (see below).
482              
483             =item plain Perl script
484              
485             C<cme run> can also run plain Perl script. This is syntactic sugar to
486             avoid polluting global namespace, i.e. there's no need to store a
487             script using L<cme function|Config::Model/cme> in C</usr/local/bin/>.
488              
489             =back
490              
491             When run, this script:
492              
493             =over
494              
495             =item *
496              
497             opens the configuration file of C<app>
498              
499             =item *
500              
501             applies the modifications specified with C<load> instructions or the Perl code.
502              
503             =item *
504              
505             save the configuration files
506              
507             =item *
508              
509             commits the result if C<commit> is specified (either in script or on command line).
510              
511             =back
512              
513             See L<App::Cme::Command::run> for details.
514              
515             =head1 Syntax of text format
516              
517             The script accepts instructions in the form:
518              
519             key: value
520              
521             The key line can be repeated when needed.
522              
523             Multi line values can also be:
524              
525             --- key
526             multi line value
527             ---
528              
529             The script accepts the following instructions:
530              
531             =over
532              
533             =item app
534              
535             Specify the target application. Must be one of the application listed
536             by C<cme list> command. Mandatory. Only one C<app> instruction is
537             allowed.
538              
539             =item default
540              
541             Specify default values that can be used in C<load> or C<var> sections.
542              
543             For instance:
544              
545             default: name=foobar
546              
547             =item var
548              
549             Use Perl code to specify variables usable in this script. The Perl
550             code must store data in C<%var> hash. For instance:
551              
552             var: my @l = localtime; $var{year} = $l[5]+1900;
553              
554             The hash C<%args> contains the variables passed with the C<-arg>
555             option. Reading a value from C<%args> which is not set by user
556             triggers a missing option error. Use C<exists> if you need to test if
557             a argument was set by user:
558              
559             var: $var{foo} = exists $var{bar} ? $var{bar} : 'default' # good
560             var: $var{foo} = $var{bar} || 'default' # triggers a "missing arg" error
561              
562             =item load
563              
564             Specify the modifications to apply using a string as specified in
565             L<Config::Model::Loader>. This string can contain variable
566             (e.g. C<$foo>) which are replaced by command argument (e.g. C<-arg
567             foo=bar>) or by a variable set in var: line (e.g. C<$var{foo}> as set
568             above) or by an environment variable (e.g. C<$ENV{foo}>)
569              
570             =item code
571              
572             Specify Perl code to run. See L</code section> for details.
573              
574             =item commit
575              
576             Specify that the change must be committed with the passed commit
577             message. When this option is used, C<cme> raises an error if used on a
578             non-clean workspace. This option works only with L<git>.
579              
580             =back
581              
582             All instructions can use variables like C<$stuff> whose value can be
583             specified with C<-arg> options, with a Perl variable (from C<var:>
584             section explained above) or with an environment variable:
585              
586             For instance:
587              
588             cme run -arg var1=foo -arg var2=bar
589              
590             transforms the instruction:
591              
592             load: ! a=$var1 b=$var2
593              
594             in
595              
596             load: ! a=foo b=bar
597              
598             =head2 Example
599              
600             Here's an example from L<libconfig-model-dpkg-perl scripts|https://salsa.debian.org/perl-team/modules/packages/libconfig-model-dpkg-perl/-/blob/master/lib/Config/Model/scripts/add-me-to-uploaders>:
601              
602             doc: add myself to Uploaders
603             app: dpkg-control
604             load: source Uploaders:.insort("$DEBFULLNAME <$DEBEMAIL>")
605             commit: add $DEBEMAIL to Uploaders
606              
607             =head2 Code section
608              
609             The code section can contain variable (e.g. C<$foo>) which are replaced by
610             command argument (e.g. C<-arg foo=bar>) or by a variable set in var:
611             line (e.g. C<$var{foo}> as set above).
612              
613             When evaluated the following variables are also set:
614              
615             =over
616              
617             =item $root
618              
619             Root node of the configuration (See L<Config::Model::Node>)
620              
621             =item $inst
622              
623             Configuration instance (See L<Config::Model::Instance>)
624              
625             =item $commit_msg
626              
627             Message used to commit the modification.
628              
629             =back
630              
631             Since the code is run in an C<eval>, other variables are available
632             (like C<$self>) to shoot yourself in the foot.
633              
634             For example:
635              
636             app: popcon
637             ---code
638             $root->fetch_element('MY_HOSTID')->store($to_store);
639             ---
640              
641             =head1 Syntax of YAML format
642              
643             This format is intented for people not wanting to user the text format
644             above. It supoorts the same parameters as the text format.
645              
646             For instance:
647              
648             # Format: YAML
649             ---
650             app: popcon
651             default:
652             defname: foobar
653             var: "$var{name} = $args{defname}"
654             load: "! MY_HOSTID=$name"
655              
656             =head1 Syntax of Perl format
657              
658             This format is intended for more complex script where using C<load>
659             instructions is not enough.
660              
661             This script must then begin with C<# Format: perl> and specifies a
662             hash. For instance:
663              
664             # Format: perl
665             {
666             app => 'popcon', # mandatory
667             doc => "Use --arg to_store=a_value to store a_value in MY_HOSTID',
668             commit => "control: update Vcs-Browser and Vcs-Git"
669             sub => sub ($root, $arg) { $root->fetch_element('MY_HOSTID')->store($arg->{to_store}); }
670             }
671              
672             C<$root> is the root if the configuration tree (See L<Config::Model::Node>).
673             C<$arg> is a hash containing the arguments passed to C<cme run> with C<-arg> options.
674              
675             The C<sub> parameter value must be a sub ref. Its parameters are
676             C<$root> (a L<Config::Model::Node> object containing the root of the
677             configuration tree) and C<$arg> (a hash ref containing the keys and
678             values passed to C<cme run> wiht C<--arg> options).
679              
680             Note that this format does not support C<var>, C<default> and C<load>
681             parameters as you can easily achieve the same result with Perl code.
682              
683             =head1 Options
684              
685             =head2 list
686              
687             List available scripts and exits.
688              
689             =head2 arg
690              
691             Arguments for the cme scripts which are used to substitute variables.
692              
693             =head2 doc
694              
695             Show the script documentation. (Note that C<--help> options show the
696             documentation of C<cme run> command)
697              
698             =head2 cat
699              
700             Pop the hood and show the content of the script.
701              
702             =head2 commit
703              
704             Like the commit instruction in script. Specify that the change must be
705             committed with the passed commit message.
706              
707             =head2 no-commit
708              
709             Don't commit to git (even if the above option is set)
710              
711             =head2 verbose
712              
713             Show effect of the modify instructions.
714              
715             =head1 Common options
716              
717             See L<cme/"Global Options">.
718              
719             =head1 Examples
720              
721             =head2 update copyright years in C<debian/copyright>
722              
723             $ cme run update-copyright -cat
724             app: dpkg-copyright
725             load: Files:~ Copyright=~"s/2016,?\s+$name/2017, $name/g"
726             commit: updated copyright year of $name
727              
728             $ cme run update-copyright -arg "name=Dominique Dumont"
729             cme: using Dpkg::Copyright model
730             Changes applied to dpkg-copyright configuration:
731             - Files:"*" Copyright: '2005-2016, Dominique Dumont <dod@debian.org>' -> '2005-2017, Dominique Dumont <dod@debian.org>'
732             - Files:"lib/Dpkg/Copyright/Scanner.pm" Copyright:
733             @@ -1,2 +1,2 @@
734             -2014-2016, Dominique Dumont <dod@debian.org>
735             +2014-2017, Dominique Dumont <dod@debian.org>
736             2005-2012, Jonas Smedegaard <dr@jones.dk>
737              
738             [master ac2e6410] updated copyright year of Dominique Dumont
739             1 file changed, 2 insertions(+), 2 deletions(-)
740              
741             =head2 update VcsGit in debian/control
742              
743             $ cme run set-vcs-git -cat
744             doc: update control Vcs-Browser and Vcs-git from git remote value
745             doc: parameters: remote (default is origin)
746             doc:
747             doc: example:
748             doc: cme run set-vcs-git
749             doc: cme run set-vcs-git -arg remote=debian
750            
751             app: dpkg-control
752             default: remote: origin
753            
754             var: chomp ( $var{url} = `git remote get-url $args{remote}` ) ;
755             var: $var{url} =~ s!^git@!https://!;
756             var: $var{url} =~ s!(https?://[\w.]+):!$1/!;
757             var: $var{browser} = $var{url};
758             var: $var{browser} =~ s/.git$//;
759            
760             load: ! source Vcs-Browser="$browser" Vcs-Git="$url"
761             commit: control: update Vcs-Browser and Vcs-Git
762              
763             This script can also be written using multi line instructions:
764              
765             $ cme run set-vcs-git -cat
766             --- doc
767             update control Vcs-Browser and Vcs-git from git remote value
768             parameters: remote (default is origin)
769            
770             example:
771             cme run set-vcs-git
772             cme run set-vcs-git -arg remote=debian
773             ---
774            
775             app: dpkg-control
776             default: remote: origin
777            
778             --- var
779             chomp ( $var{url} = `git remote get-url $args{remote}` ) ;
780             $var{url} =~ s!^git@!https://!;
781             $var{url} =~ s!(https?://[\w.]+):!$1/!;
782             $var{browser} = $var{url};
783             $var{browser} =~ s/.git$//;
784             ---
785            
786             load: ! source Vcs-Browser="$browser" Vcs-Git="$url"
787             commit: control: update Vcs-Browser and Vcs-Git
788              
789             =head1 SEE ALSO
790              
791             L<cme>
792              
793             =head1 AUTHOR
794              
795             Dominique Dumont
796              
797             =head1 COPYRIGHT AND LICENSE
798              
799             This software is Copyright (c) 2014-2022 by Dominique Dumont <ddumont@cpan.org>.
800              
801             This is free software, licensed under:
802              
803             The GNU Lesser General Public License, Version 2.1, February 1999
804              
805             =cut