]> Sergey Matveev's repositories - public-inbox.git/blob - ci/deps.perl
e0fda01191c0be44d61ab876febdb01377607afe
[public-inbox.git] / ci / deps.perl
1 #!/usr/bin/perl -w
2 # Copyright (C) 2019 all contributors <meta@public-inbox.org>
3 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
4 # Helper script for installing/uninstalling packages for CI use
5 # Intended for use on non-production chroots or VMs since it
6 # changes installed packages
7 use strict;
8 my $usage = "$0 PKG_FMT PROFILE [PROFILE_MOD]";
9 my $pkg_fmt = shift;
10 @ARGV or die $usage, "\n";
11
12 # package profiles
13 my $profiles = {
14         # the smallest possible profile
15         # TODO: trim this, Plack pulls in Filesys::Notify::Simple,
16         # and we don't need that for mda-only installs
17         essential => [ qw(
18                 git
19                 perl
20                 Date::Parse
21                 Devel::Peek
22                 Email::Simple
23                 Email::MIME
24                 Email::MIME::ContentType
25                 Encode
26                 Filesys::Notify::Simple
27                 Plack
28                 URI::Escape
29                 ) ],
30
31         # everything optional for normal use
32         optional => [ qw(
33                 BSD::Resource
34                 DBD::SQLite
35                 DBI
36                 IO::Compress::Gzip
37                 Inline::C
38                 Net::Server
39                 Plack::Middleware::Deflater
40                 Plack::Middleware::ReverseProxy
41                 Search::Xapian
42                 Socket6
43                 highlight.pm
44                 xapian-compact
45                 ) ],
46
47         # developer stuff
48         devtest => [ qw(
49                 IPC::Run
50                 Test::HTTP::Server::Simple
51                 XML::Feed
52                 curl
53                 w3m
54                 ) ],
55 };
56
57 # account for granularity differences between package systems and OSes
58 my @precious;
59 if ($^O eq 'freebsd') {
60         @precious = qw(perl curl Socket6 IO::Compress::Gzip);
61 }
62
63 if (@precious) {
64         my $re = join('|', map { quotemeta($_) } @precious);
65         for my $list (values %$profiles) {
66                 @$list = grep(!/\A(?:$re)\z/, @$list);
67         }
68         push @{$profiles->{essential}}, @precious;
69 }
70
71
72 # bare minimum for v2
73 $profiles->{v2essential} = [ @{$profiles->{essential}}, qw(
74         DBD::SQLite
75         DBI
76         Search::Xapian
77         xapian-compact
78 ) ];
79
80 # package names which can't be mapped automatically:
81 my $non_auto = {
82         'perl' => { pkg => 'perl5' },
83         'Date::Parse' => {
84                 deb => 'libtimedate-perl',
85                 pkg => 'p5-TimeDate',
86                 rpm => 'perl-TimeDate',
87         },
88         'Devel::Peek' => {
89                 deb => 'perl', # libperl5.XX, but the XX varies
90                 pkg => 'perl5',
91         },
92         'Encode' => {
93                 deb => 'perl', # libperl5.XX, but the XX varies
94                 pkg => 'perl5',
95                 rpm => 'perl-Encode',
96         },
97         'IO::Compress::Gzip' => {
98                 deb => 'perl', # perl-modules-5.xx
99                 pkg => 'perl5',
100                 rpm => 'perl-PerlIO-gzip',
101         },
102         'DBD::SQLite' => { deb => 'libdbd-sqlite3-perl' },
103         'URI::Escape' => {
104                 deb => 'liburi-perl',
105                 pkg => 'p5-URI',
106                 rpm => 'perl-URI',
107         },
108         'highlight.pm' => {
109                 deb => 'libhighlight-perl',
110                 pkg => [],
111                 rpm => [],
112         },
113
114         # we call xapian-compact(1) in public-inbox-compact(1)
115         'xapian-compact' => {
116                 deb => 'xapian-tools',
117                 pkg => 'xapian-core',
118                 rpm => 'xapian-core', # ???
119         },
120
121         # OS-specific
122         'IO::KQueue' => {
123                 deb => [],
124                 pkg => 'p5-IO-KQueue',
125                 rpm => [],
126         },
127 };
128
129 my (@pkg_install, @pkg_remove, %all);
130 for my $ary (values %$profiles) {
131         $all{$_} = \@pkg_remove for @$ary;
132 }
133 if ($^O eq 'freebsd') {
134         $all{'IO::KQueue'} = \@pkg_remove;
135 }
136 $profiles->{all} = [ keys %all ]; # pseudo-profile for all packages
137
138 # parse the profile list from the command-line
139 for my $profile (@ARGV) {
140         if ($profile =~ s/-\z//) {
141                 # like apt-get, trailing "-" means remove
142                 profile2dst($profile, \@pkg_remove);
143         } else {
144                 profile2dst($profile, \@pkg_install);
145         }
146 }
147
148 # fill in @pkg_install and @pkg_remove:
149 while (my ($pkg, $dst_pkg_list) = each %all) {
150         push @$dst_pkg_list, list(pkg2ospkg($pkg, $pkg_fmt));
151 }
152
153 my @apt_opts =
154         qw(-o APT::Install-Recommends=false -o APT::Install-Suggests=false);
155
156 # OS-specific cleanups appreciated
157
158 if ($pkg_fmt eq 'deb') {
159         my @quiet = $ENV{V} ? () : ('-q');
160         root('apt-get', @apt_opts, qw(install --purge -y), @quiet,
161                 @pkg_install,
162                 # apt-get lets you suffix a package with "-" to
163                 # remove it in an "install" sub-command:
164                 map { "$_-" } @pkg_remove);
165         root('apt-get', @apt_opts, qw(autoremove --purge -y), @quiet);
166 } elsif ($pkg_fmt eq 'pkg') {
167         my @quiet = $ENV{V} ? () : ('-q');
168         # FreeBSD, maybe other *BSDs are similar?
169
170         # don't remove stuff that isn't installed:
171         exclude_uninstalled(\@pkg_remove);
172         root(qw(pkg remove -y), @quiet, @pkg_remove) if @pkg_remove;
173         root(qw(pkg install -y), @quiet, @pkg_install) if @pkg_install;
174         root(qw(pkg autoremove -y), @quiet);
175 # TODO: yum / rpm support
176 } else {
177         die "unsupported package format: $pkg_fmt\n";
178 }
179 exit 0;
180
181
182 # map a generic package name to an OS package name
183 sub pkg2ospkg {
184         my ($pkg, $fmt) = @_;
185
186         # check explicit overrides, first:
187         if (my $ospkg = $non_auto->{$pkg}->{$fmt}) {
188                 return $ospkg;
189         }
190
191         # check common Perl module name patterns:
192         if ($pkg =~ /::/ || $pkg =~ /\A[A-Z]/) {
193                 if ($fmt eq 'deb') {
194                         $pkg =~ s/::/-/g;
195                         $pkg =~ tr/A-Z/a-z/;
196                         return "lib$pkg-perl";
197                 } elsif ($fmt eq 'rpm') {
198                         $pkg =~ s/::/-/g;
199                         return "perl-$pkg"
200                 } elsif ($fmt eq 'pkg') {
201                         $pkg =~ s/::/-/g;
202                         return "p5-$pkg"
203                 } else {
204                         die "unsupported package format: $fmt for $pkg\n"
205                 }
206         }
207
208         # use package name as-is (e.g. 'curl' or 'w3m')
209         $pkg;
210 }
211
212 # maps a install profile to a package list (@pkg_remove or @pkg_install)
213 sub profile2dst {
214         my ($profile, $dst_pkg_list) = @_;
215         if (my $pkg_list = $profiles->{$profile}) {
216                 $all{$_} = $dst_pkg_list for @$pkg_list;
217         } elsif ($all{$profile}) { # $profile is just a package name
218                 $all{$profile} = $dst_pkg_list;
219         } else {
220                 die "unrecognized profile or package: $profile\n";
221         }
222 }
223
224 sub exclude_uninstalled {
225         my ($list) = @_;
226         my %inst_check = (
227                 pkg => sub { system(qw(pkg info -q), $_[0]) == 0 },
228                 deb => sub { system("dpkg -s $_[0] >/dev/null 2>&1") == 0 },
229                 rpm => sub { system("rpm -qs $_[0] >/dev/null 2>&1") == 0 },
230         );
231
232         my $cb = $inst_check{$pkg_fmt} || die <<"";
233 don't know how to check install status for $pkg_fmt
234
235         my @tmp;
236         for my $pkg (@$list) {
237                 push @tmp, $pkg if $cb->($pkg);
238         }
239         @$list = @tmp;
240 }
241
242 sub root {
243         print join(' ', @_), "\n";
244         return if $ENV{DRY_RUN};
245         return if system(@_) == 0;
246         warn 'command failed: ', join(' ', @_), "\n";
247         exit($? >> 8);
248 }
249
250 # ensure result can be pushed into an array:
251 sub list {
252         my ($pkg) = @_;
253         ref($pkg) eq 'ARRAY' ? @$pkg : $pkg;
254 }