File Coverage

blib/lib/App/Cme/Command/run.pm
Criterion Covered Total %
statement 229 263 87.0
branch 47 80 58.7
condition 24 36 66.6
subroutine 26 28 92.8
pod 5 12 41.6
total 331 419 79.0


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