File Coverage

blib/lib/Arepa/Builder/Sbuild.pm
Criterion Covered Total %
statement 56 183 30.6
branch 1 42 2.3
condition n/a
subroutine 17 28 60.7
pod 0 8 0.0
total 74 261 28.3


line stmt bran cond sub pod time code
1             package Arepa::Builder::Sbuild;
2              
3 1     1   6 use strict;
  1         2  
  1         27  
4 1     1   4 use warnings;
  1         2  
  1         21  
5              
6 1     1   995 use English qw(-no_match_vars);
  1         4489  
  1         5  
7 1     1   436 use Carp;
  1         2  
  1         72  
8 1     1   5 use Cwd;
  1         2  
  1         60  
9 1     1   753 use File::chmod;
  1         3497  
  1         102  
10 1     1   1273 use File::Temp;
  1         30409  
  1         90  
11 1     1   10 use File::Basename;
  1         2  
  1         87  
12 1     1   6 use File::Path;
  1         2  
  1         46  
13 1     1   6 use File::Find;
  1         2  
  1         55  
14 1     1   1094 use File::Copy;
  1         2890  
  1         57  
15 1     1   820 use Config::Tiny;
  1         1119  
  1         33  
16 1     1   652 use YAML::Syck;
  1         1942  
  1         61  
17              
18 1     1   527 use Arepa;
  1         4  
  1         29  
19              
20 1     1   5 use base qw(Arepa::Builder);
  1         2  
  1         551  
21              
22             my $schroot_config = undef;
23              
24             sub _get_schroot_conf {
25 0     0   0 my ($self) = @_;
26              
27 0 0       0 if (!defined $schroot_config) {
28 0         0 my $content = "";
29 0         0 for my $path ('/etc/schroot/schroot.conf', glob('/etc/schroot/chroot.d/*')) {
30 0 0       0 if (open F, $path) {
31 0         0 $content .= join("", ) . "\n";
32 0         0 close F;
33             }
34             else {
35 0         0 print STDERR "Ignoring file '$path': couldn't read\n";
36             }
37             }
38 0         0 $schroot_config = Config::Tiny->read_string($content);
39             }
40              
41 0         0 return $schroot_config;
42             }
43              
44             sub ensure_file_exists {
45 0     0 0 0 my ($self, $path) = @_;
46              
47 0 0       0 unless (-e $path) {
48 0 0       0 open F, ">$path" or croak "Couldn't create file '$path'\n";
49 0         0 close F;
50             }
51             }
52              
53             sub builder_exists {
54 0     0 0 0 my ($self, $builder_name) = @_;
55              
56 0         0 return (defined $self->_get_schroot_conf->{$builder_name});
57             }
58              
59             sub get_builder_directory {
60 0     0 0 0 my ($self, $builder_name) = @_;
61              
62 0 0       0 if ($self->builder_exists($builder_name)) {
63 0         0 return $self->_get_schroot_conf->{$builder_name}->{directory};
64             }
65             else {
66 0         0 croak "Can't find schroot information for builder '$builder_name'\n";
67             }
68             }
69              
70             sub do_init {
71 0     0 0 0 my ($self, $builder) = @_;
72              
73 0         0 my $builder_dir = $self->get_builder_directory($builder);
74              
75             # Bind some important files to the 'host'
76 0         0 foreach my $etc_file (qw(resolv.conf passwd shadow group gshadow)) {
77 0         0 my $full_path = "$builder_dir/etc/$etc_file";
78 0 0       0 unless (-e $full_path) {
79 0         0 $self->ensure_file_exists($full_path);
80             }
81 0         0 my $mount_cmd = qq(mount --bind "/etc/$etc_file" "$full_path");
82 0         0 my $remount_cmd = qq(mount --bind -oremount,ro) .
83             qq( "/etc/$etc_file" "$full_path");
84 0         0 $self->ui_module->print_title("Binding /etc/$etc_file to $full_path");
85 0         0 system($mount_cmd);
86 0         0 system($remount_cmd);
87             }
88             }
89              
90             sub do_uninit {
91 0     0 0 0 my ($self, $builder) = @_;
92              
93 0         0 my $builder_dir = $self->get_builder_directory($builder);
94              
95             # Bind some important files to the 'host'
96 0         0 my $ok = 1;
97 0         0 foreach my $etc_file (qw(resolv.conf passwd shadow group gshadow)) {
98 0         0 my $full_path = "$builder_dir/etc/$etc_file";
99 0         0 $self->ui_module->print_title("Unbinding $full_path from /etc/$etc_file");
100 0         0 my $r = system(qq(umount "$full_path" 2>/dev/null));
101 0 0       0 if ($r != 0) {
102 0         0 $ok = 0;
103             }
104             }
105 0         0 return $ok;
106             }
107              
108             sub _call_sbuild {
109 0     0   0 my ($self, $package_spec, $params, $output_dir) = @_;
110              
111             # 1) Create a temporary directory, change to it
112 0         0 my $tmp_dir = File::Temp::tempdir();
113 0         0 my $initial_dir = Cwd::cwd;
114 0         0 chdir $tmp_dir;
115              
116             # 2) Execute sbuild there and save output in last_build_log
117 0         0 $self->{last_build_log} = qx/sbuild -v $params $package_spec 2>&1/;
118 0         0 my $r = $CHILD_ERROR;
119              
120             # 3) Move result to the result directory
121 0         0 chdir $initial_dir;
122 0 0       0 if ($output_dir !~ qr,^/,) {
123 0         0 $output_dir = File::Spec->catfile($initial_dir, $output_dir);
124             }
125             find({ wanted => sub {
126 0 0   0   0 if ($File::Find::name =~ /\.deb$/) {
127 0         0 my $move_r = move($File::Find::name, $output_dir);
128 0 0       0 if (!$move_r) {
129 0         0 print STDERR "Couldn't move $File::Find::name to $output_dir.\nCan't write to $output_dir maybe?\n";
130             }
131             }
132             },
133 0         0 follow => 0 },
134             $tmp_dir);
135              
136             # 4) Remove temporary directory
137 0         0 rmtree($tmp_dir);
138              
139 0         0 return $r;
140             }
141              
142             sub _compile_package_from_spec {
143 2     2   5 my ($self, $package_spec, %user_opts) = @_;
144 2         9 my %opts = (output_dir => '.', bin_nmu => 0, %user_opts);
145              
146 2         4 my $extra_opts = "";
147 2 50       6 if ($opts{bin_nmu}) {
148 0         0 $extra_opts .= " --make-binNMU='Recompiled by Arepa' " .
149             "--binNMU='$opts{bin_nmu}' " .
150             "--maintainer='Arepa '";
151             }
152              
153 2         9 my $builder_name = $self->name;
154 2         16 my $build_params = "--chroot $builder_name -d unstable --apt-update --nolog -A $extra_opts";
155 2         13 my $r = $self->_call_sbuild($package_spec,
156             $build_params,
157             $opts{output_dir});
158              
159 2         16 return ($r == 0);
160             }
161              
162             sub do_compile_package_from_dsc {
163 2     2 0 7 my ($self, $dsc_file, %user_opts) = @_;
164 2         6 my %opts = (output_dir => '.', %user_opts);
165 2         17 return $self->_compile_package_from_spec($dsc_file,
166             %opts);
167             }
168              
169             sub do_compile_package_from_repository {
170 0     0 0   my ($self, $pkg_name, $pkg_version, %user_opts) = @_;
171 0           my %opts = (output_dir => '.', %user_opts);
172 0           my $package_spec = $pkg_name . '_' . $pkg_version;
173              
174 0           return $self->_compile_package_from_spec($package_spec,
175             %opts);
176             }
177              
178             sub do_create {
179 0     0 0   my ($self, $builder_dir, $mirror, $distribution, %opts) = @_;
180              
181             # Strip trailing slash from the builder directory. If present, it
182             # triggers *very* weird errors when building any package with that
183             # builder (you'll see "cd: 1: can't cd to ..." in the log output)
184 0           $builder_dir =~ s,/$,,;
185 0           my $builder_name = basename($builder_dir);
186              
187 0           my $chrootd_dir = "/etc/schroot/chroot.d";
188 0           my $schroot_file = "$chrootd_dir/$builder_name";
189 0 0         if (-e $schroot_file) {
190 0           print STDERR "Builder $builder_name already exists ($schroot_file)\n";
191 0           exit 1;
192             }
193 0           mkpath $chrootd_dir;
194 0           my $schroot_content = <
195             [$builder_name]
196             description=Arepa autobuilder $builder_name
197             directory=$builder_dir
198             root-groups=sbuild
199             # groups=sbuild-security
200             groups=sbuild
201             #aliases=testing
202             #personality=linux32
203             EOCONTENT
204 0           $self->ui_module->print_title("Creating schroot file ($schroot_file)");
205 0 0         if (open F, ">$schroot_file") {
206 0           print F $schroot_content;
207 0           close F;
208             }
209             else {
210 0           print STDERR "Couldn't write to file $schroot_file. Check permissions\n";
211 0           exit 1;
212             }
213              
214 0           $self->ui_module->print_title("Creating base chroot");
215 0           my $extra_opts = "";
216 0 0         if (defined $opts{arch}) {
217 0           $extra_opts .= " --arch $opts{arch}";
218             }
219 0           my $debootstrap_cmd = "debootstrap --variant=buildd $extra_opts " .
220             "$distribution '$builder_dir' $mirror";
221 0           my $r = system($debootstrap_cmd);
222 0 0         if ($r != 0) {
223 0           print STDERR "Error executing debootstrap: error code $r\n";
224 0           print STDERR $debootstrap_cmd, "\n";
225 0           unlink $schroot_file;
226 0           exit 1;
227             }
228              
229             # Create appropriate /etc/apt/sources.list
230 0           $self->ui_module->print_title("Creating default sources.list");
231             open SOURCESLIST, ">$builder_dir/etc/apt/sources.list" or
232 0 0         do {
233 0           print STDERR "Couldn't write to /etc/apt/sources.list";
234 0           exit 1;
235             };
236 0           print SOURCESLIST <
237             deb $mirror $distribution main
238             deb http://localhost/arepa/repository $distribution main
239             deb-src http://localhost/arepa/repository $distribution main
240             EOSOURCES
241 0           close SOURCESLIST;
242              
243             # Making sure /etc/hosts includes localhost
244 0           $self->ui_module->print_title("Checking /etc/hosts");
245 0           my $full_etc_hosts_path = "$builder_dir/etc/hosts";
246 0           $self->ensure_file_exists($full_etc_hosts_path);
247 0 0         if (open F, $full_etc_hosts_path) {
248 0           my $contents = join("", );
249 0           close F;
250 0 0         if (! grep /localhost/, $contents) {
251 0 0         if (open F, ">$full_etc_hosts_path") {
252 0           print F $contents, "\n";
253 0           print F "127.0.0.1\tlocalhost\n";
254 0           close F;
255             }
256             else {
257 0           print STDERR "Couldn't update $full_etc_hosts_path\n";
258             }
259             }
260             }
261             else {
262 0           print STDERR "Couldn't check for a 'localhost' alias in $full_etc_hosts_path\n";
263             }
264              
265             # Make sure certain directories exist and are writable by the 'sbuild'
266             # group
267 0           $self->ui_module->print_title("Creating build directories");
268 0           my ($login, $pass, $uid, $gid) = getpwnam($Arepa::AREPA_MASTER_USER);
269 0 0         if (!defined $login) {
270 0           croak "'" . $Arepa::AREPA_MASTER_USER . "' user doesn't exist!";
271             }
272 0           foreach my $dir (qw(build var/lib/sbuild var/lib/sbuild/srcdep-lock)) {
273 0           my $full_path = "$builder_dir/$dir";
274 0 0         unless (-d $full_path) {
275 0           mkpath $full_path;
276             find({ wanted => sub {
277 0     0     chmod("g+w", $File::Find::name);
278 0           chown $uid, $gid, $File::Find::name;
279             },
280 0           follow => 0 },
281             $full_path);
282             }
283             }
284              
285 0           $self->ui_module->print_title("Binding files");
286 0           Arepa::Builder::Sbuild->init($builder_name);
287              
288 0           $self->ui_module->print_title("Updating package list");
289 0           my $update_cmd = "chroot '$builder_dir' apt-get update";
290 0           system($update_cmd);
291              
292 0           $self->ui_module->print_title("Installing build-essential and fakeroot");
293 0           my $install_cmd = "chroot '$builder_dir' apt-get -y --force-yes install " .
294             "build-essential fakeroot";
295 0           return system($install_cmd);
296             }
297              
298             1;
299              
300             __END__