Return non-zero exit code by rebuildreset on temporary error
[Fedora-Rebuild.git] / bin / rebuildreset
blob9e6252c5dbfc8a3c025dbaeceac0f12bb2564cd8
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
89 # Remove listed packages from file.
90 sub edit_file {
91 my ($file_name, $packages) = @_;
92 my $file;
93 print "Strippig packages from file `$file_name'...\n";
95 # Open input file.
96 # Write mode for exclusive lock
97 open($file, '+<', $file_name) or
98 croak "Could not open `$file_name': $!";
99 flock($file, Fcntl::LOCK_EX) or
100 croak "Could not lock `$file_name' file: $!";
102 # Create output file
103 my ($new_file, $new_file_name) =
104 File::Temp::tempfile($file_name . 'XXXXXX', UNLINK => 0, EXLOCK => 1);
106 # Filter input file into output file
107 while (local $_ = <$file>) {
108 chomp;
109 if (m/^\s*$/) { next; }
110 if ($packages->contains($_)) { next; }
111 print $new_file "$_\n" or
112 croak "Could not write into temporary file `$new_file_name' :$!";
114 if ($!) {
115 croak "Could not read list of package names fromfile `$file_name' :$!";
118 # Finish writing and replace the the files
119 $new_file->flush && $new_file->sync or
120 croak "Could not flush temporary file `$new_file_name': $!";
121 rename $new_file_name, $file_name or
122 croak "Could not replace file `$file_name' with stripped " .
123 "`$new_file_name': $!";
124 close $new_file or
125 croak "Could not close `$file_name': $!";
127 # This unlocks input file.
128 close $file;
130 print "Strippig packages from file `$file_name' finished successfully.\n";
134 GetOptions(
135 'config=s' => \$cfgfile,
136 'changed' => \$changed_only
137 ) or die "Could not parse program options\n";
139 # Load configuration
140 if (-f $cfgfile) {
141 my $cfg = Config::Tiny->new->read($cfgfile);
142 if (! defined $cfg) {
143 print STDERR "Could not parse `" . $cfgfile .
144 "' configuration file: " . $Config::Tiny::errstr . "\n";
145 exit 1;
147 foreach (keys %{$cfg->{_}}) {
148 $config{$_} = $cfg->{_}->{$_};
149 $config{$_} = eval $config{$_} if $_ eq 'buildrequiresfilter';
153 # Load list of packages to synchronize
154 my $packages = Fedora::Rebuild::Set::Package->new();
155 print "Loading list of package names...\n";
156 while (local $_ = <>) {
157 chomp;
158 if (m/^\s*$/) { next; }
159 if ($packages->contains($_)) { next; }
160 my $package = Fedora::Rebuild::Package->new(name => $_,
161 workdir => $config{workdir}, dist => $config{dist},
162 pkgdist => $config{pkgdist}, target => $config{target},
163 message => $config{message});
164 $packages->insert($package);
166 if ($!) {
167 croak "Could not read list of package names: $!";
169 print "Number of packages: " . $packages->size() . "\n";
172 my $changed_packages = Fedora::Rebuild::Set::Package->new();
173 my $temporary_error = 0;
175 # Process packages
176 my $scheduler = Fedora::Rebuild::Scheduler->new(
177 limit => $config{loadthreads},
178 name => ($changed_only) ?
179 'Checking remote repository' : 'Reseting repository',
180 total => $packages->size
182 my %jobs = ();
183 my $i = 0;
185 for my $package ($packages->packages) {
186 my $job = $scheduler->schedule($package->can(
187 ($changed_only) ? 'reset_remotly_updated' : 'reset'
188 ), $package);
189 if (! defined $job) {
190 next;
192 $jobs{$job} = $package;
193 my %finished = $scheduler->finish(++$i < $packages->size);
195 while (my ($job, $status) = each %finished) {
196 my $package = $jobs{$job};
198 if ($changed_only) {
199 if (!$$status[0]) {
200 print "Could not check remote repository for package `",
201 $package->name, "'.\n";
202 $temporary_error++;
203 if (defined $config{failedtemp}) {
204 Fedora::Rebuild::Package::ListLog->new(
205 file => $config{failedtemp})->log($package);
206 } else {
207 print "Waiting for finishing scheduled jobs...\n";
208 $scheduler->finish(1);
209 print "All jobs have finished.\n";
210 croak "Could not check all packages for remote changes.\n";
212 } elsif (2 == $$status[0]) {
213 print "`", $package->name,
214 "' has been remotely changed and locally reset.\n";
215 $changed_packages->insert($package);
217 } else {
218 if (!$$status[0]) {
219 print "Could not reset package `", $package->name, "'.\n";
220 print "Waiting for finishing scheduled jobs...\n";
221 $scheduler->finish(1);
222 print "All jobs have finished.\n";
223 croak "Could not reset all specified packages.\n";
225 $changed_packages->insert($package);
230 if ($changed_packages->size > 0) {
231 print "Following packages have been locally reset (",
232 $changed_packages->size, "): ", $changed_packages->string, "\n";
233 edit_file($config{done}, $changed_packages);
234 edit_file($config{failed}, $changed_packages);
235 } else {
236 print "None package has been reset.\n";
239 if ($temporary_error) {
240 exit 1;
242 exit 0;