File Coverage

Build.PL
Criterion Covered Total %
statement 141 648 21.7
branch 22 332 6.6
condition 15 86 17.4
subroutine 30 82 36.5
pod n/a
total 208 1148 18.1


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