File Coverage

Build.PL
Criterion Covered Total %
statement 141 649 21.7
branch 22 334 6.5
condition 15 86 17.4
subroutine 30 82 36.5
pod n/a
total 208 1151 18.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2             # -*- perl -*-
3              
4             #
5             # Author: Slaven Rezic
6             #
7             # Copyright (C) 2017,2018,2019,2020,2021,2022,2023,2024,2025 Slaven Rezic. All rights reserved.
8             # This program is free software; you can redistribute it and/or
9             # modify it under the same terms as Perl itself.
10             #
11             # WWW: https://github.com/eserte/Doit
12             #
13              
14 1     1   4342 use strict;
  1         1  
  1         41  
15 1     1   407 use FindBin;
  1         1063  
  1         76  
16 1     1   439 use lib "$FindBin::RealBin/lib";
  1         786  
  1         10  
17              
18 1     1   154 use Cwd qw(realpath getcwd);
  1         2  
  1         66  
19 1     1   4 use Digest::MD5 qw(md5_hex);
  1         1  
  1         48  
20 1     1   4 use File::Basename;
  1         1  
  1         35  
21 1     1   667 use Doit;
  1         3  
  1         6  
22 1     1   6 use Doit::Log;
  1         1  
  1         51  
23 1     1   3 use Doit::Util qw(in_directory get_os_release);
  1         1  
  1         518  
24              
25 1         9 my $doit = Doit->init;
26              
27 1         2 my $Build_PL_file_contents = do {
28 1 50       41 open my $fh, '<', 'Build.PL'
29             or error "Error opening Build.PL: $!";
30 1         5 local $/ = undef;
31 1         64 <$fh>;
32             };
33 1         121 my $Build_PL_md5hex = md5_hex $Build_PL_file_contents;
34              
35 1         4 for my $argv_i (0 .. $#ARGV) {
36 3         4 my $argv = $ARGV[$argv_i];
37 3 0 0     9 if ($argv =~ /^[^-].*=.*/ && ($argv_i == 0 || $ARGV[$argv_i-1] !~ /^--?config$/)) { # looks like oldfashioned option without leading dash (e.g. "installdirs=vendor"), but not if following --config
      33        
38 0         0 $argv =~ s/^/--/;
39             }
40             }
41              
42 1         2 my %action_with_arg_handling;
43              
44 1 50       45 if (basename($0) eq 'Build.PL') {
45 0         0 _Build_PL_mode();
46             }
47              
48             # Check if Build is up-to-date (md5 check, no timestamp check)
49             {
50 1 50       2 open my $fh, '<', $0
  1         26  
51             or error "Can't open $0: $!";
52 1         16 my $shebang = <$fh>;
53 1         2 my $md5_line = <$fh>;
54 1 50       7 if (my($old_md5hex) = $md5_line =~ m{^# MD5: (\S+)}) {
55 1 50       22 if ($old_md5hex ne $Build_PL_md5hex) {
56 0         0 my $perl;
57 0 0       0 if (($perl) = $shebang =~ m{^#!\s*(.*)}) {
58             # parsed it
59             } else {
60 0         0 warning "Cannot parse perl interpreter path out of '$shebang', fallback to 'perl'";
61 0         0 $perl = "perl";
62             }
63 0         0 error "Build.PL changed, please run '$perl Build.PL' again";
64             }
65             } else {
66 0         0 error "Unexpected: no MD5 found in '$md5_line'";
67             }
68             }
69              
70 1         6 require Getopt::Long;
71 1         6 my %opt = (verbose => 0, uninst => 0, destdir => '', create_packlist => 1);
72 1         2 $Build_PL::ARGV=$Build_PL::ARGV if 0; # cease -w
73 1         2 @ARGV = (@$Build_PL::ARGV, @ARGV);
74 1 50       4 Getopt::Long::GetOptions(
75             \%opt,
76             'allow_mb_mismatch=i',
77             'config=s%',
78             'create_packlist=i',
79             'destdir=s',
80             'installdirs=s',
81             'install_base=s',
82             'install_path=s%',
83             'jobs=i',
84             'prefix=s',
85             'pureperl-only:1',
86             'uninst:1',
87             'verbose:1',
88             'versionlib=s',
89             'version=s',
90             # The following are just ignored, but currently set by cover
91             'extra_compiler_flags=s',
92             'extra_linker_flags=s',
93             )
94             or error "usage: $0 [options]";
95              
96 1   50     1241 my $action = shift || 'build';
97 1         3 $action =~ s/-/_/g;
98 1 50       3 if (!$action_with_arg_handling{$action}) {
99 0 0       0 if (@ARGV) {
100 0         0 error "No arguments allowed for action '$action'";
101             }
102             }
103              
104 1         8 tie my %Config, 'CustomConfig', $opt{config};
105              
106             {
107 1     1   2 no strict 'refs';
  1         5  
  1         1  
  1         93  
108 1         3 &$action;
109             }
110              
111             sub build {
112 1     1   3 build_libs();
113 1         13 manifypods();
114             }
115              
116             sub build_libs {
117 1     1   5 $doit->make_path('blib/lib');
118 1         10 $doit->make_path('blib/arch'); # not used, but keep ExtUtils::Install quiet
119 1         6 require File::Find;
120 1         2 my @pm_files;
121 1 100 66 1   4 File::Find::find(sub { no warnings 'once'; push @pm_files, $File::Find::name if /\.(pm|pod)$/ && -f $_ }, "lib");
  1     39   1  
  1         344  
  1         84  
  39         753  
122 1         5 my %seen_blib;
123 1         2 for my $file (@pm_files) {
124 36         31 my $dest = 'blib/'.$file;
125 36 50 33     552 if (!-e $dest || -M $dest > -M $file) {
126 0         0 $doit->make_path(dirname($dest));
127 0         0 $doit->copy($file, $dest);
128             }
129 36         78 $seen_blib{$dest} = 1;
130             }
131             File::Find::find
132             ({
133             wanted => sub {
134 40 50 66 40   764 if (-f $_ && !$seen_blib{$File::Find::name}) {
135 0         0 warning "Stray file $File::Find::name found, removing...";
136 0         0 $doit->unlink($File::Find::name);
137             }
138             },
139 1         68 no_chdir => 1,
140             },
141             "blib/lib", "blib/arch",
142             );
143             }
144              
145             sub manifypods {
146             # Handles only Pods in .pod files (which is the case in the Doit distribution)
147 1     1   4 require File::Find;
148 1         3 require File::Glob;
149 1         708 require Pod::Man;
150 1 50       40517 my $mansep = $^O =~ m{^(MSWin32|cygwin)$} ? '.' : '::';
151 1         2 my %seen_blib_man;
152             File::Find::find
153             ({
154             wanted => sub {
155 39 100 100 39   810 if (-f $_ && /\.pod$/) {
156 1     1   5 no warnings 'once';
  1         6  
  1         457  
157 13         14 my $pod = $File::Find::name;
158 13         11 my $section = 3; # no scripts yet, so we can hardcode here
159 13         17 my %options = (section => $section);
160 13         28 (my $man = $pod) =~ s{^lib.}{};
161 13         27 $man =~ s{/+}{$mansep}g;
162 13         54 $man =~ s{\.pod$}{.$Config{"man${section}ext"}};
163 13         20 $man = "blib/man$section/$man";
164 13 50 33     341 if (!-e $man || -M $man > -M $pod || -M $man > -M "Build") {
      33        
165 0         0 $doit->make_path(dirname($man));
166 0         0 my $parser = Pod::Man->new(%options);
167 0 0       0 if ($doit->is_dry_run) {
168 0         0 info "$pod -> $man (dry-run)";
169             } else {
170 0         0 info "$pod -> $man";
171 0 0       0 $parser->parse_from_file($pod, $man)
172             or error "Could not install $man";
173             }
174 0         0 $doit->chmod(0644, $man); # XXX should this be changeable? like $PERM_RW in Makefile.PL?
175             }
176 13         188 $seen_blib_man{$man} = 1;
177             }
178             },
179 1         120 no_chdir => 1,
180             },
181             "lib");
182             File::Find::find
183             ({
184             wanted => sub {
185 14 50 66 14   226 if (-f $_ && !$seen_blib_man{$File::Find::name}) {
186 0         0 warning "Stray file $File::Find::name found, removing...";
187 0         0 $doit->unlink($File::Find::name);
188             }
189             },
190 1         155 no_chdir => 1,
191             },
192             File::Glob::bsd_glob("blib/man*")
193             );
194             }
195              
196             sub clean {
197 0     0   0 $doit->remove_tree('blib');
198             }
199              
200 0     0   0 sub realclean { &clean }
201              
202 1     1   638 BEGIN { $action_with_arg_handling{'test'} = 1 }
203             sub test {
204 0 0   0   0 my $usage = sub { error(($_[0] ? "$_[0]\n" : "") . "usage: $0 test -- [--no-build] [file...]") };
  1     1   6  
205 1         1 my $do_build = 1;
206 1 50       4 Getopt::Long::GetOptions(
207             'build!' => \$do_build,
208             ) or $usage->();
209 1         79 my @test_files = @ARGV; @ARGV = ();
  1         2  
210              
211 1 50       4 build() if $do_build;
212              
213 1         16 local $ENV{PERL_DL_NONLAZY} = 1;
214 1         8 require File::Glob;
215 1         2 require File::Spec;
216 1         5 local $ENV{HARNESS_OPTIONS} = $ENV{HARNESS_OPTIONS};
217 1 50 0     4 if ($opt{jobs} && (!defined $ENV{HARNESS_OPTIONS} || $ENV{HARNESS_OPTIONS} !~ /(^|:)j\d+($|:)/)) {
      33        
218 0 0 0     0 $doit->setenv(HARNESS_OPTIONS => (defined $ENV{HARNESS_OPTIONS} && $ENV{HARNESS_OPTIONS} ne '' ? $ENV{HARNESS_OPTIONS} . ':' : '') . "j$opt{jobs}")
219             }
220 1         2 if (0) {
221             $doit->system(_prove_path(), '-b', 't', @test_files); # use right perl?
222             } else {
223 1 50       2 if (!@test_files) {
224 1         442 @test_files = sort { lc $a cmp lc $b } File::Glob::bsd_glob(File::Spec->catfile("t", "*.t"));
  50         48  
225             }
226 1         5 if (0) {
227             $doit->system($^X, "-MExtUtils::Command::MM", "-MTest::Harness", "-e", 'undef *Test::Harness::Switches; test_harness(0, "blib/lib", "blib/arch")', @test_files);
228             } else {
229 1         726 require Test::Harness;
230 1 50       34118 if ($doit->is_dry_run) {
231 0         0 info "test @test_files (dry-run)";
232             } else {
233 1         4 local @INC = @INC;
234 1         2 unshift @INC, map { File::Spec->rel2abs($_) } qw(blib/lib blib/arch);
  2         47  
235 1         18 info "test @test_files";
236 1         118 Test::Harness::runtests(@test_files);
237             }
238             }
239             }
240             }
241              
242             sub test_xt {
243 0 0   0   0 $doit->system(_prove_path(), '-b', ($opt{jobs} ? ('--jobs', $opt{jobs}) : ()), 'xt'); # use right perl?
244             }
245              
246             sub test_installed {
247 0     0   0 my $t_dir = "$FindBin::RealBin/t";
248 0 0       0 chdir "/"
249             or error "Cannot chdir to /: $!";
250 0         0 $doit->system(_prove_path(), $t_dir);
251             }
252              
253 1     1   1106 BEGIN { $action_with_arg_handling{'test_in_docker'} = 1 }
254             sub test_in_docker {
255 0 0   0   0 my $usage = sub { error(($_[0] ? "$_[0]\n" : "") . "usage: $0 test_in_docker -- [--more-testing] dockerimage:tag") };
  0     0   0  
256 0 0       0 Getopt::Long::GetOptions(
257             "more-testing!" => \my $more_testing,
258             "invalidate-cache!" => \my $do_invalidate_cache,
259             ) or $usage->();
260 0 0       0 my $distro_spec = shift @ARGV or $usage->("dockerimage:tag missing");
261 0 0       0 @ARGV and $usage->("too many arguments");
262 0 0 0     0 if (!$distro_spec || $distro_spec !~ m{^.*:.*$}) {
263 0         0 $usage->("Please set dockerimage:tag argument to something like 'debian:bookworm'");
264             }
265 0 0       0 Doit::Log::set_label("[$distro_spec" . ($more_testing ? " (more)" : "") . "]");
266 0         0 for my $tool (qw(docker)) {
267 0 0       0 $doit->which($tool) or error "$tool is missing";
268             }
269 0         0 require File::Temp;
270 0         0 my $dir = File::Temp::tempdir("Doit_test_in_docker_XXXXXXXX", TMPDIR => 1, CLEANUP => 1);
271 0         0 my $dockerfile = <<"EOF";
272             FROM $distro_spec
273              
274             EOF
275 0 0       0 if ($do_invalidate_cache) {
276 0         0 _add_Dockerfile_invalidate_cmd(\$dockerfile);
277             }
278              
279 0         0 my $add_opts = {};
280 0         0 $dockerfile .= _fix_Dockerfile_for_dist($distro_spec, $add_opts);
281 0         0 _docker_add_distro_specific_files(dir => $dir, distro_spec => $distro_spec);
282 0 0       0 if (-e "$dir/.distro_support") {
283 0         0 $dockerfile .= <<'EOF';
284             COPY .distro_support .distro_support
285             RUN .distro_support/run.sh
286             EOF
287             }
288              
289 0 0       0 if ($distro_spec =~ m{^(centos|rockylinux|fedora):}) {
    0          
290             ######################################################################
291             # CentOS
292             # From https://fedoraproject.org/wiki/EPEL/FAQ#How_can_I_install_the_packages_from_the_EPEL_software_repository.3F
293 0 0       0 if ($distro_spec eq 'centos:6') {
    0          
    0          
294 0         0 $dockerfile .= <<"EOF";
295             RUN rpm -Uvh https://dl.fedoraproject.org/pub/archive/epel/6/x86_64/epel-release-6-8.noarch.rpm
296             EOF
297             } elsif ($distro_spec eq 'centos:7') {
298 0         0 $dockerfile .= <<"EOF";
299             RUN rpm -Uvh https://dl.fedoraproject.org/pub/archive/epel/7/x86_64/Packages/e/epel-release-7-14.noarch.rpm
300             EOF
301             } elsif ($distro_spec eq 'rockylinux:8') {
302 0         0 $dockerfile .= <<"EOF";
303             RUN yum -y install epel-release
304             EOF
305             } else {
306 0         0 info "No EPEL support for $distro_spec";
307             }
308 0         0 $dockerfile .= <<"EOF";
309             RUN yum -y install \\
310             sudo \\
311             rsync \\
312             "perl(Digest::MD5)" \\
313             "perl(ExtUtils::MakeMaker)" \\
314             "perl(FindBin)" \\
315             "perl(Hash::Util)" \\
316             "perl(Sys::Hostname)" \\
317             "perl(Test::More)" \\
318             "perl(Tie::File)" \\
319             make \\
320             git
321              
322             EOF
323 0 0       0 if ($distro_spec eq 'centos:8') {
    0          
    0          
324             # Requires also PowerTools repo additionally to EPEL
325 0         0 $dockerfile .= <<"EOF";
326             RUN dnf -y install 'dnf-command(config-manager)'
327             RUN yum config-manager --set-enabled PowerTools
328             EOF
329             } elsif ($distro_spec eq 'rockylinux:8') {
330 0         0 $dockerfile .= <<"EOF";
331             RUN dnf -y --enablerepo=powertools install perl-IPC-Run
332             EOF
333             } elsif ($distro_spec eq 'rockylinux:9') {
334 0         0 $dockerfile .= <<"EOF";
335             RUN dnf -y --enablerepo=crb install perl-IPC-Run
336             EOF
337             }
338 0         0 $dockerfile .= <<"EOF";
339             RUN yum -y install \\
340             "perl(IPC::Run)"
341             EOF
342 0 0 0     0 if ($distro_spec ne 'centos:8' && $distro_spec ne 'rockylinux:8' && $distro_spec ne 'rockylinux:9') {
      0        
343             # Currently not available in CentOS8 and Rocky
344 0         0 $dockerfile .= <<"EOF";
345             RUN yum -y install \\
346             "perl(Net::OpenSSH)"
347             EOF
348             }
349 0 0       0 if ($distro_spec eq 'centos:6') {
350 0         0 $dockerfile .= <<"EOF";
351             RUN curl -L https://cpanmin.us | perl - App::cpanminus
352              
353             # Not available as an RPM
354             RUN cpanm --quiet --notest CPAN::Meta
355             EOF
356             } else {
357 0         0 $dockerfile .= <<"EOF";
358             RUN yum -y install \\
359             "perl(CPAN::Meta)"
360             EOF
361             }
362 0 0       0 if ($more_testing) {
363 0         0 $dockerfile .= <<"EOF";
364             RUN yum -y install \\
365             "perl(Capture::Tiny)" \\
366             "perl(Term::ANSIColor)" \\
367             "perl(BSD::Resource)" \\
368             "perl(CPAN::Meta::Validator)" \\
369             "perl(LWP::UserAgent)" \\
370             "perl(Config::IniFiles)" \\
371             file
372              
373             ENV GITHUB_ACTIONS 1
374             ENV DOIT_TEST_WITH_SUDO 1
375              
376             EOF
377 0 0       0 if ($distro_spec eq 'centos:6') {
378 0         0 $dockerfile .= <<"EOF";
379             # Term::ANSIColor exists, but is too old (no colorstrip function)
380             RUN cpanm --quiet --notest "Term::ANSIColor~>=2.01"
381              
382             RUN yum -y install python34-pip
383              
384             EOF
385             } else {
386 0         0 $dockerfile .= <<"EOF";
387             RUN yum -y install python3-pip
388             EOF
389             }
390             }
391             } elsif ($distro_spec =~ m{^alpine(:|$)}) {
392 0         0 $dockerfile .= <<"EOF";
393             RUN apk update
394             # perl-utils for prove
395             # shadow for useradd
396             RUN apk add sudo rsync git perl perl-utils make shadow
397             EOF
398 0 0       0 if ($more_testing) {
399 0         0 $dockerfile .= <<"EOF";
400             RUN apk add py3-pip
401              
402             #ENV GITHUB_ACTIONS 1
403             ENV DOIT_TEST_WITH_SUDO 1
404              
405             EOF
406             }
407              
408             } else {
409             ######################################################################
410             # Debian-like (Debian, Ubuntu, LinuxMint ...)
411 0         0 my $packages_spec;
412             {
413 0         0 my @packages;
  0         0  
414             {
415 0 0       0 push @packages, (
  0         0  
416             'libipc-run-perl',
417             ($distro_spec eq 'ubuntu:precise' ? () : 'libnet-openssh-perl'),
418             'sudo',
419             'rsync',
420             'git',
421             );
422             }
423 0 0       0 if ($more_testing) {
424 0 0       0 push @packages, (
425             'libbsd-resource-perl',
426             'libcapture-tiny-perl',
427             'libconfig-inifiles-perl',
428             'libcpan-meta-perl',
429             'libdevel-hide-perl',
430             'libwww-perl',
431             'perl-modules',
432             'locales',
433             'file',
434             ($distro_spec eq 'ubuntu:precise' ? () : 'python3-pip'),
435             );
436             }
437 0         0 $packages_spec = join(" \\\n", map { " $_" } @packages);
  0         0  
438             }
439              
440 0 0       0 if ($distro_spec =~ m{^(ubuntu:precise)$}) {
441             # See https://gist.github.com/dergachev/f5da514802fcbbb441a1
442 0         0 $dockerfile .= <<'EOF'
443             RUN sed -i.bak -r 's/(archive|security).ubuntu.com/old-releases.ubuntu.com/g' /etc/apt/sources.list
444             EOF
445             }
446 0         0 $dockerfile .= <<"EOF";
447             ENV DEBIAN_FRONTEND noninteractive
448              
449             RUN apt-get$add_opts->{apt_get_update_opts} update && apt-get install -y --no-install-recommends$add_opts->{apt_get_install_opts} \\
450             $packages_spec
451              
452             EOF
453 0 0       0 if ($more_testing) {
454 0         0 $dockerfile .= <<"EOF";
455             ENV GITHUB_ACTIONS 1
456             ENV DOIT_TEST_WITH_SUDO 1
457              
458             EOF
459             }
460             }
461              
462 0         0 for my $env_key (qw(HARNESS_OPTIONS)) {
463 0 0       0 if (defined $ENV{$env_key}) {
464 0         0 $dockerfile .= <<"EOF";
465             ENV $env_key "$ENV{$env_key}"
466             EOF
467             }
468             }
469              
470 0         0 $doit->write_binary("$dir/Dockerfile", $dockerfile);
471             in_directory {
472 0     0   0 (my $tag = $distro_spec) =~ s{:}{-}g;
473 0         0 $tag = 'doit-test-' . $tag;
474 0         0 $doit->system(qw(docker build -t), $tag, qw(.));
475 0         0 $doit->system(
476             qw(docker run), '-v', "$FindBin::RealBin:/data:ro", $tag,
477             'sh', '-c', 'rsync -a --no-owner --no-group --exclude=blib "--exclude=Doit-*.tar.gz" /data/ /tmp/Doit/ && cd /tmp/Doit && perl Build.PL && ./Build && ./Build generate_META_json && ./Build test && ./Build test_xt && ./Build dist_install_and_test'
478             );
479 0         0 } $dir;
480              
481 0         0 Doit::Log::set_label(undef);
482             }
483              
484 1     1   1919 BEGIN { $action_with_arg_handling{'test_package_in_docker'} = 1 }
485             sub test_package_in_docker { # XXX Currently only support for deb packages
486 0 0   0   0 my $usage = sub { error(($_[0] ? "$_[0]\n" : "") . "usage: $0 test_package_in_docker -- --package package.deb dockerimage:tag") };
  0     0   0  
487 0 0       0 Getopt::Long::GetOptions(
488             "package=s" => \my $package,
489             ) or $usage->();
490 0 0       0 $package or $usage->("--package is mandatory");
491 0 0       0 my $distro_spec = shift @ARGV or $usage->("dockerimage:tag missing");
492 0 0       0 @ARGV and $usage->("too many arguments");
493 0 0 0     0 if (!$distro_spec || $distro_spec !~ m{^.*:.*$}) {
494 0         0 $usage->("Please set dockerimage:tag argument to something like 'debian:bookworm'");
495             }
496 0         0 Doit::Log::set_label("[$distro_spec]");
497 0         0 for my $tool (qw(docker)) {
498 0 0       0 $doit->which($tool) or error "$tool is missing";
499             }
500 0         0 require File::Temp;
501 0         0 my $dir = File::Temp::tempdir("Doit_test_in_docker_XXXXXXXX", TMPDIR => 1, CLEANUP => 1);
502 0         0 $doit->copy($package, $dir);
503 0         0 my $package_basename = basename($package);
504 0         0 my $dockerfile = <<"EOF";
505             FROM $distro_spec
506              
507             COPY $package_basename /tmp/$package_basename
508             EOF
509 0         0 my $add_opts = {};
510 0         0 $dockerfile .= _fix_Dockerfile_for_dist($distro_spec, $add_opts);
511              
512 0         0 $dockerfile .= <<"EOF";
513             ENV DEBIAN_FRONTEND noninteractive
514             RUN apt-get$add_opts->{apt_get_update_opts} update && \\
515             apt-get install -y --no-install-recommends$add_opts->{apt_get_install_opts} /tmp/$package_basename && \\
516             apt-get install -y --no-install-recommends$add_opts->{apt_get_install_opts} rsync
517             EOF
518              
519 0         0 $doit->write_binary("$dir/Dockerfile", $dockerfile);
520             in_directory {
521 0     0   0 $doit->system(qw(docker build -t doit-test .));
522 0         0 $doit->system(
523             qw(docker run), '-v', "$FindBin::RealBin:/data:ro", 'doit-test',
524             'sh', '-c', 'rsync -a --no-owner --no-group --exclude=blib "--exclude=Doit-*.tar.gz" /data/ /tmp/Doit/ && cd /tmp/Doit && perl Build.PL && ./Build test'
525             );
526 0         0 } $dir;
527              
528 0         0 Doit::Log::set_label(undef);
529             }
530              
531             # Run docker-based tests on a number of Linux distributions:
532             # the stable ones of Debian, Ubuntu and CentOS, and some
533             # older versions of these distributions.
534             sub test_standard {
535 0     0   0 Doit::Log::set_label("[local]");
536 0         0 $doit->system($^X, 'Build.PL');
537 0         0 $doit->system('./Build');
538 0         0 $doit->system('./Build', 'test');
539 0         0 Doit::Log::set_label(undef);
540 0         0 my @results;
541 0         0 for my $distro_spec ('debian:bookworm', 'ubuntu:jammy', 'debian:bullseye', 'ubuntu:focal', 'debian:buster', 'ubuntu:bionic', 'centos:7', 'debian:stretch', 'debian:jessie', 'ubuntu:xenial', 'ubuntu:precise', 'centos:6', 'alpine:latest') {
542 0         0 for my $more_testing (0, 1) {
543 0         0 my $t0 = time; # currently not hires, should be fine for now
544 0 0       0 $doit->system('./Build', 'test_in_docker', '--', ($more_testing ? '--more-testing' : ()), $distro_spec);
545 0         0 my $t1 = time;
546 0         0 push @results, { distro => $distro_spec, more_testing => $more_testing, result => 'pass', time => ($t1-$t0)."s" };
547             }
548             }
549              
550 0         0 my $report = "test_standard results\n";
551 0         0 my @cols = qw(distro more_testing result time);
552 0         0 my @col_widths;
553 0         0 for my $col (@cols) {
554 0         0 my $max_width;
555 0         0 for my $result (@results) {
556 0         0 my $width = length $result->{$col};
557 0 0 0     0 $max_width = $width if !defined $max_width || $max_width < $width;
558             }
559 0         0 push @col_widths, $max_width;
560             }
561 0         0 for my $result (@results) {
562 0         0 for my $col_i (0 .. $#cols) {
563 0         0 $report .= sprintf "%-${col_widths[$col_i]}s ", $result->{$cols[$col_i]};
564             }
565 0         0 $report .= "\n";
566             }
567 0         0 info $report;
568             }
569              
570             sub test_kwalitee (;$) {
571 0     0   0 my($distdir) = @_;
572             # If cwd is used then more tests fail,
573             # because META files are not available here.
574 0 0       0 $distdir = $FindBin::RealBin if !defined $distdir;
575              
576 0 0       0 if (eval { require Test::Kwalitee; 1 }) {
  0         0  
  0         0  
577             in_directory {
578 0     0   0 local $ENV{RELEASE_TESTING} = 1;
579 0         0 eval { $doit->system($^X, '-MTest::More', '-MTest::Kwalitee=kwalitee_ok', '-e', 'kwalitee_ok(qw(-has_manifest -has_meta_yml)); done_testing') };
  0         0  
580 0         0 } $distdir;
581             } else {
582 0         0 warning "Test::Kwalitee is not installed";
583             }
584             }
585              
586             sub test_pod (;$) {
587 0     0   0 my($distdir) = @_;
588 0 0       0 $distdir = $FindBin::RealBin if !defined $distdir;
589              
590 0 0       0 if (eval { require Test::Pod; 1 }) {
  0         0  
  0         0  
591             in_directory {
592 0     0   0 eval { $doit->system($^X, '-MTest::Pod', '-e', 'all_pod_files_ok()') };
  0         0  
593 0         0 } $distdir;
594             } else {
595 0         0 warning "Test::Pod is not installed";
596             }
597             }
598              
599             sub test_cpan_versions {
600 0     0   0 my($distdir) = @_;
601              
602 0 0       0 if ($doit->which("cpan_check_versions")) {
603             in_directory {
604 0     0   0 $doit->system('cpan_check_versions', '-remote');
605 0         0 } $distdir;
606             } else {
607 0         0 warning <
608             No cpan_check_versions found in PATH, cannot check for properly
609             incremented VERSIONs. Please check git://github.com/eserte/srezic-misc.git
610             EOF
611             }
612             }
613              
614             sub git_checks {
615 0     0   0 _check_clean_git();
616              
617 0         0 my $Doit_VERSION = _get_Doit_VERSION();
618              
619 0         0 my $out = $doit->info_qx(qw(git tag -l), $Doit_VERSION);
620 0 0 0     0 if (defined $out && $out ne '') {
621 0         0 error "A git tag $Doit_VERSION already exists";
622             }
623             }
624              
625             sub distdir {
626 0     0   0 my(%options) = @_;
627 0         0 my $temporary = delete $options{temporary};
628 0 0       0 error "Unhandled options: " . join(" ", %options) if %options;
629              
630 0         0 require File::Temp;
631 0         0 my $tempdir = File::Temp::tempdir("Doit_distdir_XXXXXXXX", TMPDIR => 1, CLEANUP => $temporary);
632 0         0 my $Doit_VERSION = _get_Doit_VERSION();
633 0         0 my $distdir = "$tempdir/Doit-$Doit_VERSION";
634 0         0 $doit->mkdir($distdir);
635 0         0 for my $line (split /\0/, $doit->info_qx({quiet=>1}, 'git', 'ls-files', '-z')) {
636 0 0       0 next if ($line =~ m{^( \.travis\.yml
637             | \.?appveyor\.yml
638             | \.github/.*
639             | \.gitignore
640             )$}x);
641             # XXX maybe implement also MANIFEST.SKIP?
642 0         0 my $dirname = dirname $line;
643 0 0       0 if ($dirname ne '.') {
644 0         0 $doit->make_path("$distdir/$dirname");
645             }
646 0         0 $doit->copy($line, "$distdir/$dirname/");
647             }
648              
649 0         0 generate_META_json("$distdir/META.json");
650 0         0 generate_META_yml("$distdir/META.yml" );
651              
652 0         0 test_kwalitee($distdir);
653 0         0 test_pod($distdir);
654              
655             # If not temporary, then it is assumed that the
656             # interactive distdir action is called, and
657             # provide information to the user.
658 0 0       0 if (!$temporary) {
659 0         0 info "Distribution directory is $distdir";
660             }
661              
662 0         0 $distdir;
663             }
664              
665             sub dist {
666 0 0   0   0 if (!$ENV{DOIT_TEST_SKIP_SOME_CHECKS}) {
667 0         0 git_checks();
668             }
669             # else skip because Doit version is usually not incremented in
670             # this phase and the git tag already exists
671              
672              
673 0         0 my $tarfile = _get_tarfilename();
674 0 0       0 if (-e $tarfile) {
675 0         0 error "$tarfile already exists";
676             }
677 0         0 my $distdir = distdir(temporary => 1);
678              
679 0 0       0 if (!$ENV{DOIT_TEST_SKIP_SOME_CHECKS}) {
680 0         0 test_cpan_versions($distdir);
681             }
682             # else because versions of all Doit modules are usually not
683             # incremented in this phase
684              
685             in_directory {
686             # Note: tar cfvz C:/... ... does not seem to work on Windows
687             # (for all or some tar versions?). Error message is:
688             # tar (child): Cannot connect to C: resolve failed
689             # So create in cwd first, and move to final location.
690 0     0   0 $doit->system('tar', 'cfvz', basename($tarfile), basename($distdir));
691 0         0 $doit->move(basename($tarfile), $tarfile);
692 0         0 } "$distdir/..";
693             }
694              
695             sub dist_install_and_test {
696 0     0   0 local $ENV{DOIT_TEST_SKIP_SOME_CHECKS} = 1; # XXX is there a better interface?
697 0         0 my $tarfile = _get_tarfilename();
698 0 0       0 if (!-e $tarfile) {
699 0         0 dist();
700             }
701 0         0 require File::Temp;
702 0         0 my $dir = File::Temp::tempdir("Doit_dist_install_and_test_XXXXXXXX", TMPDIR => 1, CLEANUP => 1);
703 0         0 my $Doit_VERSION = _get_Doit_VERSION();
704             in_directory {
705             # Note: see above for tar under Windows problems
706 0     0   0 $doit->move($tarfile, basename($tarfile));
707 0         0 $doit->system('tar', 'xfz', basename($tarfile));
708             in_directory {
709 0         0 $doit->system($^X, 'Build.PL');
710 0 0       0 my @Build = $^O eq 'MSWin32' ? ($^X, 'Build') : ('./Build');
711 0 0       0 my @sudo = $^O eq 'MSWin32' ? () : ('sudo');
712 0         0 $doit->system(@Build);
713 0         0 $doit->system(@Build, 'test');
714 0         0 $doit->system(@sudo, @Build, 'install');
715 0         0 $doit->system(@Build, 'test-installed');
716 0         0 } "Doit-$Doit_VERSION";
717 0         0 } $dir;
718             }
719              
720             sub dist_install_with_cpanm {
721 0     0   0 my $tarfile = _get_tarfilename();
722 0 0       0 if (!-e $tarfile) {
723 0         0 dist();
724             }
725 0         0 $doit->system('cpanm', $tarfile);
726 0         0 test_installed();
727             }
728              
729             sub look {
730 0     0   0 my $distdir = distdir(temporary => 1);
731             in_directory {
732 0     0   0 info "Spawning $ENV{SHELL} in $distdir...";
733 0         0 local $ENV{DOIT_BUILD_SHELL_LEVEL} = $ENV{DOIT_BUILD_SHELL_LEVEL};
734 0         0 $ENV{DOIT_BUILD_SHELL_LEVEL}++;
735 0         0 $doit->system($ENV{SHELL});
736 0         0 } $distdir;
737             }
738              
739 1     1   156 BEGIN { $action_with_arg_handling{'cover'} = 1 }
740             sub cover {
741 0     0   0 my(@files) = @ARGV;
742 0 0       0 if (@files) {
743 0         0 for my $file (@files) {
744 0         0 $doit->system($^X, '-MDevel::Cover', $file);
745             }
746             } else {
747 0         0 $doit->system(_cover_path(), '--test');
748             }
749 0         0 $doit->unlink('MYMETA.json.lock'); # https://github.com/pjcj/Devel--Cover/issues/263
750             }
751              
752 1     1   2084 BEGIN { $action_with_arg_handling{'show_cover'} = 1 }
753             sub show_cover {
754 0 0   0   0 Getopt::Long::GetOptions(
755             'show-only' => \my $show_only,
756             ) or error "usage: $0 show_cover -- --show-only [file ...]";
757 0         0 my @files = @ARGV;
758 0 0 0     0 if (@files && $show_only) { error "Cannot specify --show-only together with list of files" }
  0         0  
759 0 0       0 if ($show_only) {
760 0         0 $doit->system(_cover_path());
761             } else {
762 0         0 cover(@files);
763             }
764 0 0       0 my $browser = $^O eq 'darwin' ? 'open' : $doit->which('xdg-open') ? 'xdg-open' : 'firefox';
    0          
765 0         0 $doit->system($browser, "$FindBin::RealBin/cover_db/coverage.html");
766             }
767              
768             sub install {
769 0     0   0 build();
770             # XXX check if test suite was run?
771              
772             # MOD_INSTALL = $(ABSPERLRUN) -MExtUtils::Install -e 'install([ from_to => {@ARGV}, verbose => '\''$(VERBINST)'\'', uninstall_shadows => '\''$(UNINST)'\'', dir_mode => '\''$(PERM_DIR)'\'' ]);' --
773             # $(MOD_INSTALL) \
774              
775             # for a perl install:
776             # read "$(PERL_ARCHLIB)/auto/$(FULLEXT)/.packlist" \
777             # write "$(DESTINSTALLARCHLIB)/auto/$(FULLEXT)/.packlist" \
778             # "$(INST_LIB)" "$(DESTINSTALLPRIVLIB)" \
779             # "$(INST_ARCHLIB)" "$(DESTINSTALLARCHLIB)" \
780             # "$(INST_BIN)" "$(DESTINSTALLBIN)" \
781             # "$(INST_SCRIPT)" "$(DESTINSTALLSCRIPT)" \
782             # "$(INST_MAN1DIR)" "$(DESTINSTALLMAN1DIR)" \
783             # "$(INST_MAN3DIR)" "$(DESTINSTALLMAN3DIR)"
784              
785             # for a site install:
786             # read "$(SITEARCHEXP)/auto/$(FULLEXT)/.packlist" \
787             # write "$(DESTINSTALLSITEARCH)/auto/$(FULLEXT)/.packlist" \
788             # "$(INST_LIB)" "$(DESTINSTALLSITELIB)" \
789             # "$(INST_ARCHLIB)" "$(DESTINSTALLSITEARCH)" \
790             # "$(INST_BIN)" "$(DESTINSTALLSITEBIN)" \
791             # "$(INST_SCRIPT)" "$(DESTINSTALLSITESCRIPT)" \
792             # "$(INST_MAN1DIR)" "$(DESTINSTALLSITEMAN1DIR)" \
793             # "$(INST_MAN3DIR)" "$(DESTINSTALLSITEMAN3DIR)"
794            
795 0         0 require Data::Dumper;
796 0         0 require ExtUtils::Install;
797 0         0 my $FULLEXT = 'Doit';
798 0         0 my $INST_LIB = 'blib/lib';
799 0         0 my $INST_ARCHLIB = 'blib/arch';
800 0         0 my $INST_BIN = 'blib/bin';
801 0         0 my $INST_SCRIPT = 'blib/script';
802 0         0 my $INST_MAN1DIR = 'blib/man1';
803 0         0 my $INST_MAN3DIR = 'blib/man3';
804 0         0 my $PERM_DIR = '755';
805             my @euii_args = (
806             from_to => {
807             (
808             $opt{install_base}
809             ?
810             (
811             read => "$Config{sitearchexp}/auto/$FULLEXT/.packlist",
812             ($opt{create_packlist} ? (write => "$opt{install_base}/lib/perl5/$Config{archname}/auto/$FULLEXT/.packlist") : ()),
813             $INST_LIB => "$opt{install_base}/lib/perl5",
814             $INST_ARCHLIB => "$opt{install_base}/lib/perl5",
815             $INST_BIN => "$opt{install_base}/bin",
816             $INST_SCRIPT => "$opt{install_base}/bin",
817             $INST_MAN1DIR => "$opt{install_base}/man/man1",
818             $INST_MAN3DIR => "$opt{install_base}/man/man3",
819             )
820             :
821             ($opt{installdirs}||'') eq 'core'
822             ?
823             (
824             read => "$Config{archlib}/auto/$FULLEXT/.packlist",
825             ($opt{create_packlist} ? (write => "$opt{destdir}$Config{installarchlib}/auto/$FULLEXT/.packlist") : ()),
826             $INST_LIB => "$opt{destdir}$Config{installprivlib}",
827             $INST_ARCHLIB => "$opt{destdir}$Config{installarchlib}",
828             $INST_BIN => "$opt{destdir}$Config{installbin}",
829             $INST_SCRIPT => "$opt{destdir}$Config{installscript}",
830             ($Config{installman1dir} ? ($INST_MAN1DIR => "$opt{destdir}$Config{installman1dir}") : ()),
831             ($Config{installman3dir} ? ($INST_MAN3DIR => "$opt{destdir}$Config{installman3dir}") : ()),
832             )
833             :
834             ($opt{installdirs}||'') eq 'vendor'
835             ?
836             (
837             read => "$Config{vendorlib}/auto/$FULLEXT/.packlist",
838             ($opt{create_packlist} ? (write => "$opt{destdir}$Config{installvendorarch}/auto/$FULLEXT/.packlist") : ()),
839             $INST_LIB => "$opt{destdir}$Config{installvendorlib}",
840             $INST_ARCHLIB => "$opt{destdir}$Config{installvendorarch}",
841             $INST_BIN => "$opt{destdir}$Config{installvendorbin}",
842             $INST_SCRIPT => "$opt{destdir}$Config{installvendorscript}",
843             ($Config{installvendorman1dir} ? ($INST_MAN1DIR => "$opt{destdir}$Config{installvendorman1dir}") : ()),
844             ($Config{installvendorman3dir} ? ($INST_MAN3DIR => "$opt{destdir}$Config{installvendorman3dir}") : ()),
845             )
846             : # default is site
847             (
848             read => "$Config{sitearchexp}/auto/$FULLEXT/.packlist",
849             ($opt{create_packlist} ? (write => "$opt{destdir}$Config{installsitearch}/auto/$FULLEXT/.packlist") : ()),
850             $INST_LIB => "$opt{destdir}$Config{installsitelib}",
851             $INST_ARCHLIB => "$opt{destdir}$Config{installsitearch}",
852             $INST_BIN => "$opt{destdir}$Config{installsitebin}",
853             $INST_SCRIPT => "$opt{destdir}$Config{installsitescript}",
854             ($Config{installsiteman1dir} ? ($INST_MAN1DIR => "$opt{destdir}$Config{installsiteman1dir}") : ()),
855             ($Config{installsiteman3dir} ? ($INST_MAN3DIR => "$opt{destdir}$Config{installsiteman3dir}") : ()),
856             )
857             )
858             },
859             verbose => $opt{verbose},
860             uninstall_shadows => $opt{uninst},
861 0 0 0     0 dir_mode => $PERM_DIR,
    0 0        
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
862             dry_run => $doit->is_dry_run,
863             );
864 0         0 my $euii_args_dump = Data::Dumper->new([{ @euii_args }],[qw()])->Indent(1)->Useqq(1)->Sortkeys(1)->Terse(1)->Dump;
865 0         0 info "Run ExtUtils::Install::install with parameters\n" . $euii_args_dump;
866 0         0 ExtUtils::Install::install([@euii_args]);
867 0 0       0 if ($doit->is_dry_run) {
868 0         0 info "(this was dry-run mode)";
869             }
870             }
871              
872             {
873 1         12 my $Doit_VERSION;
  0         0  
874              
875             sub _get_Doit_VERSION () {
876 0 0   0   0 return $Doit_VERSION if defined $Doit_VERSION;
877              
878             {
879 0         0 my $Doit_pm = $INC{"Doit.pm"};
  0         0  
880 0 0       0 open my $fh, $Doit_pm or die "Cannot open $Doit_pm: $!";
881 0         0 while(<$fh>) {
882 0 0       0 if (/\$VERSION\s*=\s*'(.*)'/) {
883 0         0 $Doit_VERSION = $1;
884 0         0 last;
885             }
886             }
887 0 0       0 if (!defined $Doit_VERSION) {
888 0         0 error "Fatal: Cannot find \$VERSION in $Doit_pm";
889             }
890             }
891 0 0       0 if ($Doit_VERSION !~ m{^\d+\.[\d_]+$}) {
892 0         0 error "Doit.pm VERSION $Doit_VERSION does not look as expected";
893             }
894             # Check also for numerical value of version
895 0 0       0 if (!defined $Doit::VERSION) {
896 0         0 error "Unexpected: cannot find \$VERSION in loaded Doit.pm";
897             }
898             {
899 0         0 (my $Doit_numerical_version = $Doit_VERSION) =~ s{_}{}g;
  0         0  
900 0 0       0 if ($Doit_numerical_version != $Doit::VERSION) {
901 0         0 error "Unexpected: parsed version $Doit_VERSION is not numerically equal to loaded version $Doit::VERSION";
902             }
903             }
904 0         0 $Doit_VERSION;
905             }
906             }
907              
908 1         138977004 sub _get_tarfilename () {
909 0     0   0 my $Doit_VERSION = _get_Doit_VERSION();
910 0         0 getcwd . "/Doit-" . $Doit_VERSION . ".tar.gz";
911             }
912              
913             sub _generate_META ($;$) {
914 0     0   0 my($destfile, $meta_version) = @_;
915              
916 0         0 require CPAN::Meta;
917 0         0 require ExtUtils::MakeMaker;
918 0         0 my $meta = CPAN::Meta->new({
919             name => 'Doit',
920             author => 'Slaven Rezic ',
921             abstract => 'a scripting framework',
922             license => 'perl',
923             version => MM->parse_version("$FindBin::RealBin/lib/Doit.pm"),
924             dynamic_config => 0,
925             prereqs => {
926             configure => {
927             requires => {
928             'Digest::MD5' => 0,
929             'Exporter' => 5.57, # use Exporter "import"
930             'File::Path' => 2.0, # make_path
931             },
932             },
933             runtime => {
934             recommends => {
935             'IPC::Run' => 0,
936             'Net::OpenSSH' => 0,
937             },
938             requires => {
939             'perl' => 5.006,
940             'Exporter' => 5.57, # use Exporter "import"
941             'File::Path' => 2.07, # make_path
942             },
943             },
944             test => {
945             requires => {
946             'Test::More' => 0,
947             },
948             },
949             },
950             resources => {
951             repository => { url => 'git://github.com/eserte/Doit' },
952             },
953             generated_by => "Doit version " . _get_Doit_VERSION(),
954             });
955 0 0       0 if ($doit->is_dry_run) {
956 0 0       0 info "Would create $destfile" . (defined $meta_version ? " (version $meta_version)" : "") . " (dry-run)";
957             } else {
958 0 0       0 $meta->save($destfile, (defined $meta_version ? { version => $meta_version } : ()));
959             }
960             }
961              
962             sub generate_META_json (;$) {
963 0   0 0   0 my $destfile = shift || 'META.json';
964 0         0 _generate_META $destfile;
965             }
966              
967             sub generate_META_yml (;$) {
968 0   0 0   0 my $destfile = shift || 'META.yml';
969 0         0 _generate_META $destfile, 1.4;
970             }
971              
972             # XXX the debian package build functionality should go into
973             # a separate Doit component
974              
975             sub debian_package {
976 0     0   0 _debian_package('--depends' => 'perl, libnet-openssh-perl, libipc-run-perl', '--add-distro-version' => '1');
977             }
978              
979             sub _debian_package {
980 0     0   0 my(%opts) = @_;
981              
982 0         0 for my $tool (qw(dh-make-perl)) { # git is checked if needed
983 0 0       0 $doit->which($tool) or error "$tool is missing";
984             }
985              
986 0         0 my $distdir = distdir(temporary => 1);
987              
988 0         0 my $version = delete $opts{'--version'};
989 0         0 my $add_distro_version = delete $opts{'--add-distro-version'};
990 0 0       0 if (!defined $version) {
991 0         0 $version = _basic_debian_version();
992 0 0       0 if ($add_distro_version) {
993 0         0 my $osr = get_os_release();
994 0 0       0 if (!$osr) {
995 0         0 error 'Cannot read /etc/os-release --- please specify --version manually';
996             }
997 0         0 my $dist_id = $osr->{ID};
998 0         0 my $rel = $osr->{VERSION_ID};
999 0 0       0 if ($dist_id eq 'debian') {
    0          
    0          
1000 0         0 $rel =~ s{^(\d+).*}{$1};
1001 0         0 $version .= "+deb${rel}u${add_distro_version}";
1002             } elsif ($dist_id eq 'linuxmint') {
1003 0         0 $version .= "+linuxmint${rel}u${add_distro_version}";
1004             } elsif ($dist_id eq 'ubuntu') {
1005 0         0 $version .= "~ubuntu${rel}.${add_distro_version}";
1006             } else {
1007 0         0 error "No distro version support for '$dist_id'";
1008             }
1009             }
1010             }
1011              
1012 0         0 my $deb;
1013             in_directory {
1014 0 0   0   0 $doit->system(qw(dh-make-perl --build --vcs none), (defined $version ? ('--version', $version) : ()), %opts);
1015 0         0 require File::Glob;
1016 0         0 my(@debs) = File::Glob::bsd_glob("$distdir/../*.deb");
1017 0 0       0 if (@debs != 1) {
1018 0         0 error "Expecting exactly one generated .deb, but got: <@debs>\n";
1019             }
1020 0         0 $deb = $debs[0];
1021 0         0 } $distdir;
1022 0         0 $doit->copy($deb, basename($deb));
1023 0         0 print basename($deb), "\n";
1024             }
1025              
1026             sub _basic_debian_version {
1027 0     0   0 my $version;
1028 0 0       0 $doit->which('git') or error 'git is missing --- please install it or alternatively specify --version manually';
1029 0         0 chomp(my $git_describe = eval { $doit->info_qx('git', 'describe') }); # XXX what if this fails? Optionally made fatal?
  0         0  
1030 0 0       0 if (defined $git_describe) {
1031 0 0       0 if ($git_describe =~ m{^([0-9\._]+)$}) {
    0          
1032 0         0 $version = $1;
1033             } elsif ($git_describe =~ m{^([0-9\._]+)-(\d+)-g(.*)}) {
1034 0         0 $version = $1."+git".$2."+".$3."-1"; # XXX make "1" configurable?
1035             } else {
1036 0         0 error "Cannot parse output from git describe: '$git_describe'";
1037             }
1038 0         0 $version =~ s{_}{-}; # replace "devel" version specifier
1039             } else {
1040 0         0 error "No information using git-describe available. Please specify --version manually.";
1041             }
1042 0         0 $version;
1043             }
1044              
1045 1     1   2432 BEGIN { $action_with_arg_handling{'debian_package_with_docker'} = 1 }
1046             sub debian_package_with_docker {
1047 0 0   0   0 my $usage = sub { error(($_[0] ? "$_[0]\n" : "") . "usage: $0 debian_package_with_docker -- [--use-workdir | --tag ...] dockerimage:tag") };
  0     0   0  
1048 0 0       0 Getopt::Long::GetOptions(
1049             "use-workdir!" => \my $use_workdir,
1050             "tag=s" => \my $tag,
1051             ) or $usage->();
1052 0 0 0     0 $tag && $use_workdir and $usage->("Cannot use --use-workdir together with --tag");
1053 0 0       0 my $distro_spec = shift @ARGV or $usage->("dockerimage:tag missing");
1054 0 0       0 @ARGV and $usage->("too many arguments");
1055 0 0       0 if ($distro_spec eq 'self') {
1056 0         0 $distro_spec = join ":", @{ get_os_release() }{qw(ID VERSION_CODENAME)};
  0         0  
1057             }
1058 0 0 0     0 if (!$distro_spec || $distro_spec !~ m{^.*:.*$}) {
1059 0         0 $usage->("Please set dockerimage:tag argument to something like 'debian:bookworm' or 'self'");
1060             }
1061 0         0 for my $tool (qw(docker)) {
1062 0 0       0 $doit->which($tool) or error "$tool is missing";
1063             }
1064              
1065             # Cannot access directories outside of /Users when working
1066             # with docker-machine @ Mac, see for an explanation:
1067             # https://stackoverflow.com/questions/37673140/docker-volume-located-in-tmp-on-osx-empty
1068 0         0 my $limited_volume_availability = $^O eq 'darwin';
1069              
1070 0         0 require File::Temp;
1071 0         0 my $dir = File::Temp::tempdir("Doit_debian_package_docker_XXXXXXXX", TMPDIR => !$limited_volume_availability, CLEANUP => 1);
1072 0         0 $dir = realpath $dir; # docker volumes have to be specified with absolute paths
1073              
1074 0 0       0 my $pkgdir = $limited_volume_availability ? "$FindBin::RealBin/pkg" : "/tmp";
1075 0 0       0 if (!-d $pkgdir) {
1076 0         0 $doit->mkdir($pkgdir);
1077             }
1078              
1079 0 0       0 if ($use_workdir) {
1080             # no --cvs-exclude --- git-describe has to work
1081 0         0 $doit->system('rsync', '-a', '--exclude=blib', '.', $dir);
1082             } else {
1083 0         0 $doit->add_component('git');
1084 0 0       0 $doit->git_repo_update(
1085             repository => $FindBin::RealBin,
1086             directory => $dir,
1087             ($tag ? (branch => $tag) : ()),
1088             # no: we need the full history for correct git version calculation: clone_opts => [qw(--depth=1)],
1089             );
1090             }
1091             in_directory {
1092 0     0   0 my $dockerfile = <<"EOF";
1093             FROM $distro_spec
1094             EOF
1095 0         0 my $add_opts = {};
1096 0         0 $dockerfile .= _fix_Dockerfile_for_dist($distro_spec, $add_opts);
1097 0         0 $dockerfile .= <<"EOF";
1098              
1099             ENV DEBIAN_FRONTEND noninteractive
1100              
1101             RUN apt-get$add_opts->{apt_get_update_opts} update && apt-get$add_opts->{apt_get_install_opts} install -y --no-install-recommends \\
1102             dh-make-perl \\
1103             git
1104              
1105             # XXX This is depending on the current perl module and should be configurable
1106             RUN apt-get$add_opts->{apt_get_install_opts} install -y --no-install-recommends \\
1107             libipc-run-perl \\
1108             libnet-openssh-perl
1109              
1110             EOF
1111 0         0 for my $env_key (qw(DEBFULLNAME DEBEMAIL EMAIL)) {
1112 0 0       0 if ($ENV{$env_key}) {
1113 0         0 $dockerfile .= <<"EOF";
1114             ENV $env_key "$ENV{$env_key}"
1115             EOF
1116             }
1117             }
1118 0         0 $doit->write_binary("Dockerfile", $dockerfile);
1119              
1120 0         0 (my $label = $distro_spec) =~ s{:}{-}g;
1121 0         0 $label = "doit-deb-$label";
1122 0         0 $doit->system(qw(docker build -t), $label, qw(.));
1123 0         0 $doit->system(
1124             qw(docker run), '-v', "$dir:/data", '-v', "$pkgdir:/pkg", $label,
1125             'sh', '-c', 'git config --global --add safe.directory /data && cd /data && perl Build.PL && ./Build debian_package && cp *.deb /pkg'
1126             );
1127 0         0 } $dir;
1128 0         0 info "Package is available in $pkgdir";
1129             }
1130              
1131             sub release {
1132 0     0   0 my $Doit_VERSION = _get_Doit_VERSION();
1133 0         0 for my $existing_tag (split /\n/, $doit->info_qx({quiet=>1}, 'git', 'tag')) {
1134 0 0       0 if ($Doit_VERSION eq $existing_tag) {
1135 0         0 error "The version $Doit_VERSION is already tagged --- probably the release was already done";
1136             }
1137             }
1138              
1139             FIND_VERSION: {
1140 0 0       0 open my $cfh, '<', 'Changes'
  0         0  
1141             or error "Can't open Changes: $!";
1142 0         0 while(<$cfh>) {
1143 0 0       0 if (/^\Q$Doit_VERSION\E\s/) {
1144 0         0 last FIND_VERSION;
1145             }
1146             }
1147 0         0 error "Cannot find version $Doit_VERSION in Changes";
1148             }
1149              
1150 0         0 $doit->system('git', 'fetch');
1151              
1152 0         0 $doit->add_component('git');
1153 0         0 my $git_status = $doit->git_short_status;
1154 0 0 0     0 if ($git_status ne '' && $git_status ne '<') {
1155 0         0 error "Please check the git status";
1156             }
1157              
1158             # XXX TODO: check first if the current version already exists at CPAN
1159 0         0 my $tarfile = _get_tarfilename();
1160 0 0       0 if (!-e $tarfile) {
1161 0         0 dist();
1162             } else {
1163 0         0 info "Use existing tarball $tarfile";
1164             }
1165              
1166 0         0 print STDERR "Upload $tarfile? (y/n) ";
1167 0 0       0 if (!y_or_n()) {
1168 0         0 error "exiting release process";
1169             }
1170              
1171 0         0 $doit->system('cpan-upload', basename($tarfile));
1172              
1173 0         0 $doit->system('git', 'tag', '-a', '-m', "* $Doit_VERSION", $Doit_VERSION);
1174 0         0 $doit->system('git', 'push', 'origin', 'master', $Doit_VERSION);
1175             }
1176              
1177             sub ci_precheck {
1178 0     0   0 my @ci_systems = ('github-actions', 'appveyor');
1179              
1180 0         0 _check_clean_git();
1181              
1182 0         0 for my $ci_system (@ci_systems) {
1183 0         0 $doit->system('git', 'push', 'origin', "+HEAD:XXX-${ci_system}");
1184             }
1185              
1186             # check-ci is available here: https://github.com/eserte/srezic-misc/blob/master/scripts/check-ci
1187 0         0 $doit->system('check-ci', (map { "--$_" } @ci_systems));
  0         0  
1188             }
1189              
1190             sub _prove_path {
1191 0     0   0 my @directory_candidates = ($Config{bin}, dirname(realpath $^X));
1192 0         0 my @basename_candidates = ('prove', "prove$Config{version}");
1193             my @candidates = map {
1194 0         0 my $basename = $_;
  0         0  
1195             map {
1196 0         0 "$_/$basename";
  0         0  
1197             } @directory_candidates;
1198             } @basename_candidates;
1199 0 0       0 if ($^O eq 'MSWin32') {
1200 0         0 unshift @candidates, map { "$_.bat"} @candidates;
  0         0  
1201             }
1202 0         0 for my $candidate (@candidates) {
1203 0 0       0 if (-x $candidate) {
1204 0         0 return $candidate;
1205             }
1206             }
1207 0         0 error "No 'prove' for the current perl found";
1208             }
1209              
1210             sub _cover_path {
1211 0     0   0 my @candidates = ("$Config{bin}/cover");
1212 0 0       0 if ($^O eq 'MSWin32') {
1213 0         0 unshift @candidates, map { "$_.bat"} @candidates;
  0         0  
1214             }
1215 0         0 for my $candidate (@candidates) {
1216 0 0       0 if (-x $candidate) {
1217 0         0 return $candidate;
1218             }
1219             }
1220 0         0 error "No 'cover' for the current perl found";
1221             }
1222              
1223             sub _Build_PL_mode {
1224 0     0   0 tie my %Config, 'CustomConfig'; # no options here!
1225              
1226 0 0       0 if ($ENV{PERL_MB_OPT}) {
1227 0         0 require Text::ParseWords;
1228 0         0 push @ARGV, Text::ParseWords::shellwords($ENV{PERL_MB_OPT});
1229             }
1230              
1231 0         0 require Data::Dumper;
1232 0         0 my $argv_serialized = "\n" . '$Build_PL::ARGV = ' . Data::Dumper->new([\@ARGV], [])->Indent(1)->Useqq(1)->Sortkeys(1)->Terse(1)->Dump . ";\n\n";
1233             {
1234 0 0       0 if (-l "Build") { # formerly this used to be a symlink
  0         0  
1235 0         0 $doit->unlink("Build");
1236             }
1237 0         0 my $preamble = <<"EOF";
1238             #! $Config{perlpath}
1239             # MD5: $Build_PL_md5hex
1240             EOF
1241 0         0 $preamble .= $argv_serialized;
1242 0         0 $doit->write_binary({quiet=>1}, 'Build', $preamble . qq{# line 1 "Build.PL"\n} . $Build_PL_file_contents);
1243 0         0 $doit->chmod(0755, 'Build');
1244              
1245 0         0 eval {
1246 0         0 generate_META_json("MYMETA.json");
1247 0         0 generate_META_yml("MYMETA.yml" );
1248             };
1249 0 0       0 warning "Failure while generating MYMETA.* files, continue without.\nError was:\n$@" if $@;
1250             }
1251 0         0 exit;
1252             }
1253              
1254             # Return a string for adding to Dockerfile after the FROM line. Also
1255             # the 2nd parameter (which has to be an empty hashref) is filled with
1256             # further instructions to amend the Dockerfile (currently only
1257             # apt_get_update_opts).
1258             sub _fix_Dockerfile_for_dist {
1259 0     0   0 my($distro_spec, $add_opts_ref) = @_;
1260 0 0       0 if ($distro_spec =~ m{^debian:(wheezy|jessie|stretch|buster|7|8|9|10)$}) {
    0          
1261 0         0 my $codename = $1;
1262 0 0       0 if ($codename eq '7') { $codename = 'wheezy' }
  0 0       0  
    0          
    0          
1263 0         0 elsif ($codename eq '8') { $codename = 'jessie' }
1264 0         0 elsif ($codename eq '9') { $codename = 'stretch' }
1265 0         0 elsif ($codename eq '10') { $codename = 'buster' }
1266             # https://unix.stackexchange.com/questions/508724/failed-to-fetch-jessie-backports-repository
1267 0         0 $add_opts_ref->{apt_get_update_opts} = ' -o Acquire::Check-Valid-Until=false';
1268             # https://askubuntu.com/questions/74345/how-do-i-bypass-ignore-the-gpg-signature-checks-of-apt
1269 0         0 $add_opts_ref->{apt_get_install_opts} = ' -o APT::Get::AllowUnauthenticated=true';
1270 0         0 <
1271             RUN echo 'deb [check-valid-until=no] http://archive.debian.org/debian $codename main' > /etc/apt/sources.list
1272             RUN echo 'deb [check-valid-until=no] http://archive.debian.org/debian-security/ $codename/updates main' >> /etc/apt/sources.list
1273             EOF
1274             } elsif ($distro_spec =~ m{^perl:(5\.18\.\d+|.*stretch)$}) { # XXX add more perl versions based on jessie/stretch/... containers
1275             # docker perl images are based on debian base images; older
1276             # ones need patching
1277 0         0 $add_opts_ref->{apt_get_install_opts} = ' -o APT::Get::AllowUnauthenticated=true';
1278 0         0 <<'EOF';
1279             RUN perl -i -pe \
1280             's{deb http://(?:http.debian.net|deb.debian.org)/debian (\S+) main}{deb [check-valid-until=no] http://archive.debian.org/debian $1 main}; \
1281             s{deb http://security.debian.org.* ([^/]+)/updates main}{deb [check-valid-until=no] http://archive.debian.org/debian-security/ $1/updates main}; \
1282             $_ = "" if m{^deb .*-updates main}; \
1283             ' /etc/apt/sources.list
1284             EOF
1285             } else {
1286 0         0 $add_opts_ref->{apt_get_update_opts} = '';
1287 0         0 $add_opts_ref->{apt_get_install_opts} = '';
1288 0         0 '';
1289             }
1290             }
1291              
1292             sub _add_Dockerfile_invalidate_cmd {
1293 0     0   0 my $dockerfile_ref = shift;
1294 0         0 require POSIX;
1295 0         0 $$dockerfile_ref .= <<"EOF";
1296             # Just a hack to make sure that the following lines
1297             # are executed at least once a day
1298 0         0 RUN echo @{[ POSIX::strftime("%F", localtime) ]}
1299             EOF
1300             }
1301              
1302             sub _docker_add_distro_specific_files {
1303 0     0   0 my %opts = @_;
1304 0   0     0 my $dir = delete $opts{dir} || die 'Please specify dir';
1305 0   0     0 my $distro_spec = delete $opts{distro_spec} || die 'Please specify distro_spec';
1306 0 0       0 die 'Unhandled options: ' . join(' ', %opts) if %opts;
1307              
1308 0 0 0     0 if ($distro_spec eq 'centos:6' || $distro_spec eq 'centos:7') {
1309 0         0 $doit->mkdir("$dir/.distro_support");
1310             # Instructions from https://gcore.de/de/hilfe/linux/centos6-repo-nach-eol-anpassen.php
1311 0 0       0 if ($distro_spec eq 'centos:6') {
    0          
1312 0         0 $doit->write_binary("$dir/.distro_support/CentOS.repo", <<'EOF');
1313             [base]
1314             name=CentOS-6.10 - Base
1315             baseurl=http://vault.centos.org/6.10/os/$basearch/
1316             gpgcheck=1
1317             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6
1318             enabled=1
1319             metadata_expire=never
1320              
1321             #released updates
1322             [updates]
1323             name=CentOS-6.10 - Updates
1324             baseurl=http://vault.centos.org/6.10/updates/$basearch/
1325             gpgcheck=1
1326             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6
1327             enabled=1
1328             metadata_expire=never
1329              
1330             # additional packages that may be useful
1331             [extras]
1332             name=CentOS-6.10 - Extras
1333             baseurl=http://vault.centos.org/6.10/extras/$basearch/
1334             gpgcheck=1
1335             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6
1336             enabled=1
1337             metadata_expire=never
1338              
1339             # additional packages that extend functionality of existing packages
1340             [centosplus]
1341             name=CentOS-6.10 - CentOSPlus
1342             baseurl=http://vault.centos.org/6.10/centosplus/$basearch/
1343             gpgcheck=1
1344             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6
1345             enabled=0
1346             metadata_expire=never
1347              
1348             #contrib - packages by Centos Users
1349             [contrib]
1350             name=CentOS-6.10 - Contrib
1351             baseurl=http://vault.centos.org/6.10/contrib/$basearch/
1352             gpgcheck=1
1353             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6
1354             enabled=0
1355             metadata_expire=never
1356             EOF
1357 0         0 $doit->write_binary("$dir/.distro_support/epel.repo", <<'EOF');
1358             [epel]
1359             name=Extra Packages for Enterprise Linux 6 - $basearch
1360             baseurl=https://archives.fedoraproject.org/pub/archive/epel/6/$basearch
1361             enabled=1
1362             gpgcheck=1
1363             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6
1364             metadata_expire=never
1365              
1366             [epel-debuginfo]
1367             name=Extra Packages for Enterprise Linux 6 - $basearch - Debug
1368             baseurl=https://archives.fedoraproject.org/pub/archive/epel/6/$basearch/debug
1369             enabled=0
1370             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6
1371             gpgcheck=1
1372             metadata_expire=never
1373              
1374             [epel-source]
1375             name=Extra Packages for Enterprise Linux 6 - $basearch - Source
1376             baseurl=https://archives.fedoraproject.org/pub/archive/epel/6/SRPMS
1377             enabled=0
1378             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6
1379             gpgcheck=1
1380             metadata_expire=never
1381             EOF
1382             } elsif ($distro_spec eq 'centos:7') {
1383 0         0 $doit->write_binary("$dir/.distro_support/CentOS.repo", <<'EOF');
1384             [base]
1385             name=CentOS-$releasever - Base
1386             baseurl=https://vault.centos.org/7.9.2009/os/$basearch/
1387             gpgcheck=1
1388             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
1389              
1390             [updates]
1391             name=CentOS-$releasever - Updates
1392             baseurl=https://vault.centos.org/7.9.2009/updates/$basearch/
1393             gpgcheck=1
1394             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
1395              
1396             [extras]
1397             name=CentOS-$releasever - Extras
1398             baseurl=https://vault.centos.org/7.9.2009/extras/$basearch/
1399             gpgcheck=1
1400             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
1401             EOF
1402 0         0 $doit->write_binary("$dir/.distro_support/epel.repo", <<'EOF');
1403             [epel]
1404             name=Extra Packages for Enterprise Linux 7 - $basearch
1405             baseurl=https://archives.fedoraproject.org/pub/archive/epel/7/$basearch
1406             enabled=1
1407             gpgcheck=1
1408             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
1409             EOF
1410             } else {
1411 0         0 error "Should never happen";
1412             }
1413 0         0 $doit->write_binary("$dir/.distro_support/run.sh", <<'EOF');
1414             #! /bin/sh
1415             set -ex
1416             rm -f /etc/yum.repos.d/CentOS*.repo
1417             rm -f /etc/yum.repos.d/epel.repo
1418             cp .distro_support/CentOS.repo /etc/yum.repos.d/
1419             cp .distro_support/epel.repo /etc/yum.repos.d/
1420             yum clean all
1421             EOF
1422 0         0 $doit->chmod(0755, "$dir/.distro_support/run.sh");
1423             }
1424             }
1425              
1426             sub _check_clean_git {
1427 0     0   0 $doit->add_component('git');
1428              
1429 0         0 my $status = $doit->git_short_status;
1430 0 0       0 if ($status eq '<<') {
1431 0         0 error 'Working directory has uncomitted changes: ' . `git status`;
1432             }
1433 0 0       0 if ($status eq '*') {
1434 0         0 error 'Working directory has files not under git control (and not in .gitignore or .git/info/exclude): ' . `git status`;
1435             }
1436             }
1437              
1438             # REPO BEGIN
1439             # REPO NAME y_or_n /home/eserte/src/srezic-repository
1440             # REPO MD5 146cfcf8f954555fe0117a55b0ddc9b1
1441              
1442             #=head2 y_or_n
1443             #
1444             #Accept user input. Return true on 'y', return false on 'n', otherwise
1445             #ask again.
1446             #
1447             #A default may be supplied as an optional argument:
1448             #
1449             # y_or_n 'y';
1450             # y_or_n 'n';
1451             #
1452             #=cut
1453              
1454             sub y_or_n (;$) {
1455 0     0   0 my $default = shift;
1456 0         0 while () {
1457 0         0 chomp(my $yn = );
1458 0 0 0     0 if ($yn eq '' && defined $default) {
1459 0         0 $yn = $default;
1460             }
1461 0 0       0 if (lc $yn eq 'y') {
    0          
1462 0         0 return 1;
1463             } elsif (lc $yn eq 'n') {
1464 0         0 return 0;
1465             } else {
1466 0         0 print STDERR "Please answer y or n: ";
1467             }
1468             }
1469             }
1470             # REPO END
1471              
1472             {
1473 0         0 package CustomConfig;
1474 1     1   8 use Config '%Config';
  1         1  
  1         40  
1475 1     1   557 use Tie::Hash;
  1         702  
  1         54685  
1476 1         58 our @ISA = qw(Tie::StdHash);
1477              
1478             sub TIEHASH {
1479 1     1   2 my($class, $custom_options) = @_;
1480 1         4 bless { custom_options => $custom_options }, $class;
1481             }
1482              
1483             sub FETCH {
1484 13     13   14 my($self, $key) = @_;
1485 13 50       23 if (exists $self->{custom_options}->{$key}) {
1486 0         0 return $self->{original_hash}->{$key};
1487             } else {
1488 13         129 return $Config::Config{$key};
1489             }
1490             }
1491             }
1492              
1493 1         22 1;
1494             __END__