2 # local-debbugs is part of debbugs, and is released
3 # under the terms of the GPL version 2, or any later version, at your
4 # option. See the file README and COPYING for more information.
5 # Copyright 2008 by Don Armstrong <don@donarmstrong.com>.
16 local-debbugs - use a local mirror of debbugs
23 --mirror, -M update local mirror
24 --daemon, -D start the daemon
25 --search, -S run a search
27 --debug, -d debugging level (Default 0)
28 --help, -h display this help
29 --man, -m display manual
37 Update the local mirror of debbugs bugs
41 Start up the daemon on the configured local port to serve bugs which
42 have been previously retried
46 Cause the running daemon to show the pkgreport.cgi page corresponding
47 to the search by invoking sensible-browser and an appropriate url
51 Cause the running daemon to show the bugreport.cgi page corresponding
52 to the bug by invoking sensible-browser and an appropriate url
56 The port that the daemon is running on (or will be running on.)
58 Defaults to the value of the currently running daemon, the value in
59 the configuration file, or 8080 if nothing is set.
61 =item B<--bugs-to-get>
63 File which contains the set of bugs to get.
64 Defaults to ~/.debbugs/bugs_to_get
68 Debug verbosity. (Default 0)
72 Display brief useage information.
90 use File::Temp qw(tempdir);
91 use Params::Validate qw(validate_with :types);
93 my %options = (debug => 0,
100 my %option_defaults = (port => 8080,
101 debbugs_config => User->Home.'/.debbugs/debbugs_config',
102 mirror_location => User->Home.'/.debbugs/mirror/',
103 bugs_to_get => User->Home.'/.debbugs/bugs_to_get',
106 GetOptions(\%options,
107 'daemon|D','show|s','search|select|S','mirror|M',
108 'verbose|v+','quiet|q+',
109 'debug|d+','help|h|?','man|m');
111 pod2usage() if $options{help};
112 pod2usage({verbose=>2}) if $options{man};
114 $DEBUG = $options{debug};
117 if (1 != scalar @options{qw(daemon show search mirror)}) {
118 push @USAGE_ERRORS,"You must pass one (and only one) of --daemon --show --search or --mirror";
120 $options{verbose} = $options{verbose} - $options{quiet};
122 pod2usage(join("\n",@USAGE_ERRORS)) if @USAGE_ERRORS;
125 # munge in local configuration
127 local_config(\%options);
129 if ($options{daemon}) {
130 # daemonize, do stuff
132 elsif ($options{mirror}) {
133 # run the mirror jobies
134 # figure out which bugs we need
135 my @bugs = select_bugs(\%options);
137 my $tempdir = tempdir(CLEANUP => 1);
138 my $mirror_log = IO::File->new($options{mirror_location}.'/mirror.log') or
139 die "Unable to open $options{mirror_location}/mirror.log for writing: $!";
140 my $inc_fh = IO::File->new("$tempdir/include_list",'w') or
141 die "Unable to open $tempdir/include_list for writing: $!";
142 foreach my $bug (@bugs) {
143 print {$inc_fh} "*/${bug}.*\n" or
144 die "Unable to write to $tempdir/include_list: $!";
147 die "Unable to close $tempdir/include_list: $!";
149 my @common_rsync_options = ('-avz','--partial');
150 print "Rsyncing bugs\n" if not $options{quiet};
151 run_rsync(log => $mirror_log,
152 ($options{debug}?(debug => \*STDERR):()),
153 options => [@common_rsync_options,
155 '--include-from',"$tempdir/include_list",
156 # skip things not specifically included
158 # skip the -1,-2,-3.log files
160 'rsync://'.$options{bug_mirror}.'/bts-spool-db/',
161 $options{mirror_location}.'/db-h/']
163 print "Rsyncing archived bugs\n" if $options{verbose};
164 run_rsync(log => $mirror_log,
165 ($options{debug}?(debug => \*STDERR):()),
166 options => [@common_rsync_options,
168 '--include-from',"$tempdir/include_list",
169 # skip things not specifically included
171 # skip the -1,-2,-3.log files
173 'rsync://'.$options{bug_mirror}.'/bts-spool-archive/',
174 $options{mirror_location}.'/archive/',
177 print "Rsyncing indexes\n" if $options{verbose};
178 run_rsync(log => $mirror_log,
179 ($options{debug}?(debug => \*STDERR):()),
180 options => [@common_rsync_options,
183 'rsync://'.$options{bug_mirror}.'/bts-spool-index/',
184 $options{mirror_location}.'/',
187 print "Rsyncing versions\n" if $options{verbose};
188 run_rsync(log => $mirror_log,
189 ($options{debug}?(debug => \*STDERR):()),
190 options => [@common_rsync_options,
194 'rsync://'.$options{bug_mirror}.'/bts-spool-versions/',
195 $options{mirror_location}.'/versions/',
199 elsif ($options{show}) {
202 elsif ($options{search}) {
205 # you get here, you were an idiot in checking for @USAGE_ERRORS
207 die "No option that we understand was passed (the first check for this is now buggy, so shoot your maintainer)"
211 # determine the local configuration
215 if (-e '/etc/debbugs/local_debbugs.conf') {
216 Config::Simple->import_from('/etc/debbugs/local_debbugs.conf', $config) or
217 die "Unable to read configuration from /etc/debbugs/local_debbugs.conf: $!";
219 if (-e User->Home.'/.debbugs/local_debbugs.conf') {
220 Config::Simple->import_from(User->Home.'/.debbugs/local_debbugs.conf', $config) or
221 die "Unable to read configuration from ".User->Home.'/.debbugs/local_debbugs.conf: '.$!;
223 for (keys %option_defaults) {
224 if (exists $config->{$_} and not defined $options->{$_}) {
225 $options->{$_} = $config->{$_};
227 if (not defined $options->{$_}) {
228 $options->{$_} = $option_defaults{$_};
233 # actually run rsync with the passed options
235 my %param = validate_with(params => \@_,
236 spec => {log => {type => HANDLE,
238 debug => {type => HANDLE,
241 options => {type => ARRAYREF,
245 my ($output_fh,@rsync_options) = @_;
247 my $pid = open3($wfh,$rfh,
250 ) or die "Unable to start rsync: $!";
251 close $wfh or die "Unable to close the writer filehandle $?";
253 print {$param{log}} $_;
254 if (exists $param{debug}) {
255 print {$param{debug}} $_;
262 # select a set of bugs
266 my %valid_keys = (package => 'package',
271 maintainer => 'maint',
272 submitter => 'submitter',
281 distribution => 'dist',
283 archive => 'archive',
284 severity => 'severity',
285 correspondent => 'correspondent',
286 affects => 'affects',
289 my $soap = SOAP::Lite
290 -> uri('Debbugs/SOAP/V1')
291 -> proxy("http://$options{bug_mirror}/cgi-bin/soap.cgi");
293 my @bug_selections = ();
294 if (not -e $options{bugs_to_get}) {
295 my ($addr) = get_addresses(exists $ENV{DEBEMAIL}?
297 (User->Login . '@' . qx(hostname --fqdn)));
298 # by default include bugs talked to by this user packages
299 # maintained by this user, submitted by this user, and rc
301 push @bug_selections,
302 ("correspondent:$addr archive:both",
303 "maint:$addr archive:both",
304 "submitter:$addr archive:both",
305 "severity:serious severity:grave severity:critical archive:both",
309 my $btg_fh = IO::File->new($options{bugs_to_get},'r') or
310 die "unable to open bugs to get file '$options{bugs_to_get}' for reading: $!";
318 push @bug_selections, $_;
322 for my $selection (@bug_selections) {
323 my @subselects = split /\s+/,$selection;
324 my %search_parameters;
326 for my $subselect (@subselects) {
327 my ($key,$value) = split /:/, $subselect, 2;
329 if (exists $valid_keys{$key}) {
330 push @{$search_parameters{$valid_keys{$key}}},
332 } elsif ($key =~/users?$/) {
333 $users{$value} = 1 if $value;
337 for my $user (keys %users) {
338 my $ut = $soap->get_usertag($user)->result();
339 next unless defined $ut and $ut ne "";
340 for my $tag (keys %{$ut}) {
341 push @{$usertags{$tag}},
345 my $bugs = $soap->get_bugs(%search_parameters,
346 (keys %usertags)?(usertags=>\%usertags):()
348 push @bugs,@{$bugs} if defined $bugs and @{$bugs};