Adapt to mock-1.4.1-1.fc25
[Fedora-Rebuild.git] / bin / rebuildreset
blob748fbafe0e64a606666aca8b4635e2d945d46ca1
1 #!/usr/bin/perl
2 use strict;
3 use warnings;
4 use Carp;
5 use Config::Tiny;
6 use Getopt::Long;
7 use Fedora::Rebuild::Set::Package;
8 use Fedora::Rebuild::Package;
9 use Fedora::Rebuild::Scheduler;
10 use Fedora::Rebuild::Package::ListLog;
11 use Fcntl;
12 use File::Spec;
13 use File::Temp;
15 =encoding utf8
17 =head1 NAME
19 rebuildreset - Reset package for rebuild
21 =head1 SYNOPIS
23 rebuilreset [--config FILE] [--changed] < PACKAGE_LIST
25 =head1 DESCRIPTION
27 This tool resets packages listed on standard input or in a file named in
28 positional arguments, so that next L<rebuildperl(1)> will consider the
29 packages as not yet rebuilt.
31 This is a helper for L<rebuildperl(1)> and it assumes the packages have
32 already been populated by rebuildperl. It will reset the local repository and
33 removes all stage locks except I<.clone>. It will also edit I<all>, I<done>,
34 and I<failed> package lists (see the configuration) accordingly. (It will
35 remove changed packaged from done and failed list.)
37 With other options, the set of packages to reset can be reduced more.
39 =cut
41 my $cfgfile = File::Spec->catfile($ENV{HOME}, '.rebuildperlrc');
42 my %config = (
43 done => undef,
44 failed => undef,
45 failedtemp => undef,
46 workdir => undef,
47 repodir => undef,
48 dist => undef,
49 loadthreads => 1,
50 target => '',
51 message => '',
53 my $changed_only = 0;
55 =head1 OPTIONS
57 =head2 --config I<FILE>
59 Read configuration from I<FILE>, or F<~/.rebuildperlrc> if not specified.
61 =head2 --changed
63 Reset only packages whose remote repository has changed (e.g. there is a newer
64 version). It compares local copy of I<origin> GIT repository with repository
65 on the remote server and if it finds the HEADs are different it will reset the
66 package. Otherwise the package will not be reset.
68 If I<failedtemp> configuration option is defined and there is an error while
69 checking the remote repository, the faulting package will be logged into
70 the I<failedtemp> file. This is because talking to a GIT server can result
71 into intermittent network error that can disappear on next retry.
73 =head1 FILES
75 =head2 F<~/.rebuildperlrc>
77 Configuration is in L<Config::Tiny> format. Following options are needed:
79 done = done
80 failed = failed
81 failedtemp = failedtemp
82 workdir = workdir
83 dist = rawhide
84 loadthreads = 4
86 =cut
88 # Copy file permissions and ownership from source file handle to target file
89 # handle. Croaks on error.
90 sub copy_file_attributes {
91 my ($source_handle, $source_name, $target_handle, $target_name) = @_;
92 my @source_meta = stat($source_handle) or
93 croak "Could not retrieve `$source_name' meta-data: $!";
94 if (defined $source_meta[2]) {
95 chmod($source_meta[2], $target_handle) or
96 croak "Could not change `$target_name' permissions: $!";
98 if (defined $source_meta[4] and defined $source_meta[5]) {
99 chown($source_meta[4], $source_meta[5], $target_name) or
100 croak "Could not change `$target_name' ownership: $!";
104 # Remove listed packages from file.
105 sub edit_file {
106 my ($file_name, $packages) = @_;
107 my $file;
108 print "Strippig packages from file `$file_name'...\n";
110 # Open input file.
111 # Write mode for exclusive lock
112 open($file, '+<', $file_name) or
113 croak "Could not open `$file_name': $!";
114 flock($file, Fcntl::LOCK_EX) or
115 croak "Could not lock `$file_name' file: $!";
117 # Create output file
118 my ($new_file, $new_file_name) =
119 File::Temp::tempfile($file_name . 'XXXXXX', UNLINK => 0, EXLOCK => 1);
120 copy_file_attributes($file, $file_name, $new_file, $new_file_name);
122 # Filter input file into output file
123 while (local $_ = <$file>) {
124 chomp;
125 if (m/^\s*$/) { next; }
126 if ($packages->contains($_)) { next; }
127 print $new_file "$_\n" or
128 croak "Could not write into temporary file `$new_file_name' :$!";
130 if ($!) {
131 croak "Could not read list of package names fromfile `$file_name' :$!";
134 # Finish writing and replace the the files
135 $new_file->flush && $new_file->sync or
136 croak "Could not flush temporary file `$new_file_name': $!";
137 rename $new_file_name, $file_name or
138 croak "Could not replace file `$file_name' with stripped " .
139 "`$new_file_name': $!";
140 close $new_file or
141 croak "Could not close `$file_name': $!";
143 # This unlocks input file.
144 close $file;
146 print "Strippig packages from file `$file_name' finished successfully.\n";
150 GetOptions(
151 'config=s' => \$cfgfile,
152 'changed' => \$changed_only
153 ) or die "Could not parse program options\n";
155 # Load configuration
156 if (-f $cfgfile) {
157 my $cfg = Config::Tiny->new->read($cfgfile);
158 if (! defined $cfg) {
159 print STDERR "Could not parse `" . $cfgfile .
160 "' configuration file: " . $Config::Tiny::errstr . "\n";
161 exit 1;
163 foreach (keys %{$cfg->{_}}) {
164 $config{$_} = $cfg->{_}->{$_};
165 $config{$_} = eval $config{$_} if $_ eq 'buildrequiresfilter';
169 # Load list of packages to synchronize
170 my $packages = Fedora::Rebuild::Set::Package->new();
171 print "Loading list of package names...\n";
172 while (local $_ = <>) {
173 chomp;
174 if (m/^\s*$/) { next; }
175 if ($packages->contains($_)) { next; }
176 my $package = Fedora::Rebuild::Package->new(name => $_,
177 workdir => $config{workdir}, dist => $config{dist},
178 pkgdist => $config{pkgdist}, target => $config{target},
179 message => $config{message});
180 $packages->insert($package);
182 if ($!) {
183 croak "Could not read list of package names: $!";
185 print "Number of packages: " . $packages->size() . "\n";
188 my $changed_packages = Fedora::Rebuild::Set::Package->new();
189 my $temporary_error = 0;
191 # Process packages
192 my $scheduler = Fedora::Rebuild::Scheduler->new(
193 limit => $config{loadthreads},
194 name => ($changed_only) ?
195 'Checking remote repository' : 'Reseting repository',
196 total => $packages->size
198 my %jobs = ();
199 my $i = 0;
201 for my $package ($packages->packages) {
202 my $job = $scheduler->schedule($package->can(
203 ($changed_only) ? 'reset_remotly_updated' : 'reset'
204 ), $package);
205 if (! defined $job) {
206 next;
208 $jobs{$job} = $package;
209 my %finished = $scheduler->finish(++$i < $packages->size);
211 while (my ($job, $status) = each %finished) {
212 my $package = $jobs{$job};
214 if ($changed_only) {
215 if (!$$status[0]) {
216 print "Could not check remote repository for package `",
217 $package->name, "'.\n";
218 $temporary_error++;
219 if (defined $config{failedtemp}) {
220 Fedora::Rebuild::Package::ListLog->new(
221 file => $config{failedtemp})->log($package);
222 } else {
223 print "Waiting for finishing scheduled jobs...\n";
224 $scheduler->finish(1);
225 print "All jobs have finished.\n";
226 croak "Could not check all packages for remote changes.\n";
228 } elsif (2 == $$status[0]) {
229 print "`", $package->name,
230 "' has been remotely changed and locally reset.\n";
231 $changed_packages->insert($package);
233 } else {
234 if (!$$status[0]) {
235 print "Could not reset package `", $package->name, "'.\n";
236 print "Waiting for finishing scheduled jobs...\n";
237 $scheduler->finish(1);
238 print "All jobs have finished.\n";
239 croak "Could not reset all specified packages.\n";
241 $changed_packages->insert($package);
246 if ($changed_packages->size > 0) {
247 print "Following packages have been locally reset (",
248 $changed_packages->size, "): ", $changed_packages->string, "\n";
249 edit_file($config{done}, $changed_packages);
250 edit_file($config{failed}, $changed_packages);
251 } else {
252 print "None package has been reset.\n";
255 if ($temporary_error) {
256 exit 1;
258 exit 0;