Reset states by rebuildsync
[Fedora-Rebuild.git] / bin / rebuildsync
blob6b1b1e31d58fee4b696bdea5f5b81b9595cfeeb0
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 Fcntl;
11 use File::Spec;
12 use File::Temp;
14 =encoding utf8
16 =head1 NAME
18 rebuildsync - Reset package for rebuild if remote repository has changed
20 =head1 SYNOPIS
22 rebuildsync [--config FILE] < PACKAGE_LIST
24 =head1 DESCRIPTION
26 This tool checks packages listed on standard input or in a file named in
27 positional arguments for newer version in remote GIT repository.
29 This is a helper for L<rebuildperl(1)> and it assumes the packages have
30 already been populated by rebuildperl. This tool compares local copy of origin
31 GIT repository with repository on the remote server and if it finds the HEADs
32 are different, it will reset the local repository and removes all stage locks
33 except I<.clone>. It will also edit I<all>, I<done>, and I<failed> package
34 lists (see the configuration) accordingly. (It will remove changed packaged
35 from done and failed list.)
37 =cut
39 my $cfgfile = File::Spec->catfile($ENV{HOME}, '.rebuildperlrc');
40 my %config = (
41 done => undef,
42 failed => undef,
43 workdir => undef,
44 repodir => undef,
45 dist => undef,
46 loadthreads => 1,
47 target => '',
48 message => '',
51 =head1 OPTIONS
53 =head2 --config I<FILE>
55 Read configuration from I<FILE>, or F<~/.rebuildperlrc> if not specified.
57 =head1 FILES
59 =head2 F<~/.rebuildperlrc>
61 Configuration is in L<Config::Tiny> format. Following options are needed:
63 done = done
64 failed = failed
65 workdir = workdir
66 dist = rawhide
67 loadthreads = 4
69 =cut
72 # Remove listed packages from file.
73 sub edit_file {
74 my ($file_name, $packages) = @_;
75 my $file;
76 print "Strippig packages from file `$file_name'...\n";
78 # Open input file.
79 # Write mode for exclusive lock
80 open($file, '+<', $file_name) or
81 croak "Could not open `$file_name': $!";
82 flock($file, Fcntl::LOCK_EX) or
83 croak "Could not lock `$file_name' file: $!";
85 # Create output file
86 my ($new_file, $new_file_name) =
87 File::Temp::tempfile($file_name . 'XXXXXX', UNLINK => 0, EXLOCK => 1);
89 # Filter input file into output file
90 while (local $_ = <$file>) {
91 chomp;
92 if (m/^\s*$/) { next; }
93 if ($packages->contains($_)) { next; }
94 print $new_file "$_\n" or
95 croak "Could not write into temporary file `$new_file_name' :$!";
97 if ($!) {
98 croak "Could not read list of package names fromfile `$file_name' :$!";
101 # Finish writing and replace the the files
102 $new_file->flush && $new_file->sync or
103 croak "Could not flush temporary file `$new_file_name': $!";
104 rename $new_file_name, $file_name or
105 croak "Could not replace file `$file_name' with stripped " .
106 "`$new_file_name': $!";
107 close $new_file or
108 croak "Could not close `$file_name': $!";
110 # This unlocks input file.
111 close $file;
113 print "Strippig packages from file `$file_name' finished successfully.\n";
117 GetOptions('config=s' => \$cfgfile) or die "Could not parse program options\n";
119 # Load configuration
120 if (-f $cfgfile) {
121 my $cfg = Config::Tiny->new->read($cfgfile);
122 if (! defined $cfg) {
123 print STDERR "Could not parse `" . $cfgfile .
124 "' configuration file: " . $Config::Tiny::errstr . "\n";
125 exit 1;
127 foreach (keys %{$cfg->{_}}) {
128 $config{$_} = $cfg->{_}->{$_};
129 $config{$_} = eval $config{$_} if $_ eq 'buildrequiresfilter';
133 # Load list of packages to synchronize
134 my $packages = Fedora::Rebuild::Set::Package->new();
135 print "Loading list of package names to synchronize with remote origin...\n";
136 while (local $_ = <>) {
137 chomp;
138 if (m/^\s*$/) { next; }
139 if ($packages->contains($_)) { next; }
140 my $package = Fedora::Rebuild::Package->new(name => $_,
141 workdir => $config{workdir}, dist => $config{dist},
142 pkgdist => $config{pkgdist}, target => $config{target},
143 message => $config{message});
144 $packages->insert($package);
146 if ($!) {
147 croak "Could not read list of package names to synchronize: $!";
149 print "Number of packages: " . $packages->size() . "\n";
152 my $changed_packages = Fedora::Rebuild::Set::Package->new();
154 # Process packages
155 my $scheduler = Fedora::Rebuild::Scheduler->new(
156 limit => $config{loadthreads},
157 name => 'Checking remote repository',
158 total => $packages->size
160 my %jobs = ();
161 my $i = 0;
163 for my $package ($packages->packages) {
164 my $job = $scheduler->schedule($package->can('reset_remotly_updated'),
165 $package);
166 if (! defined $job) {
167 next;
169 $jobs{$job} = $package;
170 my %finished = $scheduler->finish(++$i < $packages->size);
172 while (my ($job, $status) = each %finished) {
173 my $package = $jobs{$job};
174 if (!$$status[0]) {
175 print "Could not check remote repository for package `",
176 $package->name, "'.\n";
177 print "Waiting for finishing scheduled jobs...\n";
178 $scheduler->finish(1);
179 print "All jobs have finished.\n";
180 croak "Could not check all packages for remote changes.\n";
181 } elsif (2 == $$status[0]) {
182 print "`", $package->name,
183 "' has been remotly changed and locally reset.\n";
184 $changed_packages->insert($package);
189 if ($changed_packages->size > 0) {
190 print "Following packages have been changed in the remote repository ",
191 "and thus locally reset (",
192 $changed_packages->size, "): ", $changed_packages->string, "\n";
193 edit_file($config{done}, $changed_packages);
194 edit_file($config{failed}, $changed_packages);
195 } else {
196 print "None package has been changed in the remote repository.\n";
199 exit 0;