03064a90ba
- Update to 0.22: - fix whitespace parsing issue with zypper lr -d - align usage text OBS-URL: https://build.opensuse.org/request/show/198643 OBS-URL: https://build.opensuse.org/package/show/system:packagemanager/rypper?expand=0&rev=3
318 lines
9.0 KiB
Perl
318 lines
9.0 KiB
Perl
#!/usr/bin/perl
|
|
|
|
=head1 NAME
|
|
|
|
rypper - extension for managing multiple repositories via zypper
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
# See complete usage documentation
|
|
rypper --help
|
|
|
|
# list all disabled repos
|
|
rypper -d
|
|
|
|
# list all enabled repos with autorefresh off
|
|
rypper -e -R
|
|
|
|
# list all repos which have anything to do with KDE
|
|
rypper -x kde
|
|
|
|
# list priority and URIs for all repos whose alias contains 'home:'
|
|
rypper -a home: l -pu
|
|
|
|
# enable autorefresh on all OpenSUSE Build Service repos
|
|
rypper -u download.opensuse.org -R mr -r
|
|
|
|
# remove all repos on external USB HDD mounted on /media/disk
|
|
rypper -u /media/disk rr
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
rypper is a wrapper around zypper for performing repository
|
|
operations in batch. It allows selection of which
|
|
repositories to operate on via a number of different
|
|
repository selection specifiers.
|
|
|
|
The zypper commands which can be wrapped are:
|
|
|
|
removerepo, rr Remove specified repository.
|
|
modifyrepo, mr Modify specified repository.
|
|
refresh, ref Refresh all repositories.
|
|
clean Clean local caches.
|
|
|
|
Additionally, the zypper pseudo-command 'l' can be used to
|
|
list matching repos. There are a number of options
|
|
controlling the output format; run C<rypper --help> for
|
|
details.
|
|
|
|
If no zypper command is provided, it defaults to the 'l'
|
|
pseudo-command, trying to intelligently choose which fields to
|
|
display, e.g. omitting any which were included in the repo
|
|
selection parameters.
|
|
|
|
=head1 SEE ALSO
|
|
|
|
L<zypper>
|
|
|
|
=head1 LICENSE
|
|
|
|
Copyright (C) 2009 Adam Spiers
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License at L<http://www.gnu.org/licenses/> for
|
|
more details.
|
|
|
|
=head1 AUTHOR
|
|
|
|
Adam Spiers <rypper@adamspiers.org>
|
|
|
|
=cut
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use Getopt::Long;
|
|
|
|
our $VERSION = '0.22';
|
|
|
|
(my $ME = $0) =~ s,.*/,,;
|
|
|
|
my @ALLOWED_ZYPPER_CMDS = qw(removerepo rr modifyrepo mr refresh ref clean);
|
|
my @ZYPPER_L_AUTO_COLS = qw(id alias name enabled refresh);
|
|
my @DEFAULT_ZYPPER_L_OPTS =
|
|
$ENV{DEFAULT_ZYPPER_L_OPTS} ?
|
|
split /\s+/, $ENV{DEFAULT_ZYPPER_L_OPTS}
|
|
: ('-iaeu');
|
|
|
|
my $DEFAULT_L_DELIMITER = ' ';
|
|
|
|
sub usage {
|
|
warn @_, "\n" if @_;
|
|
|
|
(my $ME = $0) =~ s,.*/,,;
|
|
|
|
die <<EOUSAGE;
|
|
$ME: run a zypper command on selected repositories.
|
|
Usage: $ME [<specifiers...>] [<zypper-cmd> [<zypper-args> ...]]
|
|
|
|
The specifiers determine which repositories the zypper command applies to:
|
|
-d, --disabled disabled repos
|
|
-e, --enabled enabled repos
|
|
-r, --refresh repos with autorefresh enabled
|
|
-R, --no-refresh repos with autorefresh disabled
|
|
-a, --alias=REGEXP repos whose alias matches REGEXP
|
|
-n, --name=REGEXP repos whose name matches REGEXP
|
|
-p, --priority=PRIORITY repos whose priority equals PRIORITY
|
|
-u, --uri=REGEXP repos whose URI matches REGEXP
|
|
-s, --service=REGEXP repos whose service matches REGEXP
|
|
-t, --type=REGEXP repos whose type matches REGEXP
|
|
-x, --match=REGEXP repos where REGEXP appears in *any* attribute
|
|
-T, --test dry run: display commands which would be run
|
|
Multiple specifiers are ANDed together. REGEXPs are case-insensitive.
|
|
|
|
Additionally, the zypper pseudo-command 'l' can be used with
|
|
the following options to list matching repos:
|
|
-a, --alias Include a column for repo aliases
|
|
-e, --enabled Include a column for repo enablement
|
|
-n, --name Include a column for repo names
|
|
-p, --priority Include a column for repo priorities
|
|
-r, --refresh Include a column for repo auto-refresh
|
|
-s, --service Include a column for repo services
|
|
-t, --type Include a column for repo types
|
|
-u, --uri Include a column for repo URIs
|
|
-d, --delimiter=STR Change output field delimiter (default '$DEFAULT_L_DELIMITER')
|
|
-N, --no-header Omit column header
|
|
In the absence of options, it defaults to '@DEFAULT_ZYPPER_L_OPTS'
|
|
(this can be overridden by the \$DEFAULT_ZYPPER_L_OPTS environment variable).
|
|
|
|
If no zypper command is provided, it defaults to the 'l'
|
|
command, trying to intelligently choose which fields to
|
|
display by omitting any which were included in the repo
|
|
selection parameters.
|
|
|
|
Other options:
|
|
-h, --help Show this help
|
|
-V, --version Show $ME version
|
|
EOUSAGE
|
|
}
|
|
|
|
Getopt::Long::Configure(qw{no_bundling no_ignore_case require_order});
|
|
|
|
my %opts = ( verbosity => 1 );
|
|
|
|
usage() if @ARGV == 0;
|
|
GetOptions(
|
|
\%opts,
|
|
'disabled|d',
|
|
'enabled|e',
|
|
'refresh|r',
|
|
'no-refresh|R',
|
|
'alias|a=s',
|
|
'name|n=s',
|
|
'priority|p=s',
|
|
'service|s=s',
|
|
'type|t=s',
|
|
'uri|u=s',
|
|
'match|x=s',
|
|
#'verbosity|verbose|v:+',
|
|
'test|T',
|
|
'help|h',
|
|
'version|V',
|
|
) or usage();
|
|
usage() if $opts{help};
|
|
|
|
if ($opts{version}) {
|
|
print "$ME version $VERSION\n";
|
|
exit 0;
|
|
}
|
|
|
|
sub get_repo_ids {
|
|
my (@ids, %repos);
|
|
my @FIELDS = qw(id alias name enabled refresh priority type uri service);
|
|
# Thanks to Michal Marek for pointing out that on openSUSE 11.1 at
|
|
# least, zypper's output can sometimes start with a strange escape
|
|
# sequence when localised. We work-around by setting LC_MESSAGES=C.
|
|
open(ZYPPER, "LC_MESSAGES=C zypper lr -d|")
|
|
or die "Couldn't open(zypper lr -d|): $!\n";
|
|
while (<ZYPPER>) {
|
|
chomp;
|
|
next if /^#/ || /^[-+]{40}/;
|
|
s/^\s+//;
|
|
my @values = split /\s+\|\s+/, $_;
|
|
my %repo = map { $FIELDS[$_] => ($values[$_] || '') } 0 .. $#FIELDS;
|
|
if (repo_matches(%repo)) {
|
|
push @ids, \%repo;
|
|
}
|
|
}
|
|
close(ZYPPER)
|
|
or die "close(zypper lr -d|) failed: $!\n";
|
|
return sort { $a->{id} <=> $b->{id} } @ids;
|
|
}
|
|
|
|
sub repo_matches {
|
|
my %repo = @_;
|
|
|
|
# Filter out non-matches
|
|
return 0 if $opts{enabled} and $repo{enabled} ne 'Yes';
|
|
return 0 if $opts{disabled} and $repo{enabled} ne 'No';
|
|
return 0 if $opts{refresh} and $repo{refresh} ne 'Yes';
|
|
return 0 if $opts{'no-refresh'} and $repo{refresh} ne 'No';
|
|
return 0 if $opts{alias} and $repo{alias} !~ /$opts{alias}/i;
|
|
return 0 if $opts{name} and $repo{name} !~ /$opts{name}/i;
|
|
return 0 if $opts{priority} and $repo{priority} ne $opts{priority};
|
|
return 0 if $opts{service} and $repo{service} !~ /$opts{service}/i;
|
|
return 0 if $opts{type} and $repo{type} !~ /$opts{type}/i;
|
|
return 0 if $opts{uri} and $repo{uri} !~ /$opts{uri}/i;
|
|
|
|
if ($opts{match}) {
|
|
my $combo = join '|', map $repo{$_}, qw(alias name type uri service);
|
|
return 0 unless $combo =~ /$opts{match}/i;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
my @repos = get_repo_ids();
|
|
|
|
if (@ARGV == 0) {
|
|
my @l_opts = $opts{match} ? () : get_zypper_l_auto_cols();
|
|
|
|
@ARGV = ('l', @l_opts);
|
|
}
|
|
|
|
if ($ARGV[0] eq 'l') {
|
|
shift @ARGV;
|
|
if (@ARGV == 0) {
|
|
@ARGV = @DEFAULT_ZYPPER_L_OPTS;
|
|
}
|
|
list_repos();
|
|
}
|
|
else {
|
|
my %ALLOWED_ZYPPER_CMDS = map { $_ => 1 } @ALLOWED_ZYPPER_CMDS;
|
|
if (! $ALLOWED_ZYPPER_CMDS{$ARGV[0]}) {
|
|
die "$ARGV[0] is not a valid zypper repository command.\nRun $ME --help for help.\n";
|
|
}
|
|
foreach my $repo (@repos) {
|
|
my $id = $repo->{id};
|
|
my @cmd = ('zypper', @ARGV, $id);
|
|
if ($opts{test}) {
|
|
print "@cmd\n";
|
|
}
|
|
else {
|
|
system @cmd;
|
|
}
|
|
}
|
|
}
|
|
|
|
sub list_repos {
|
|
my (%l_opts, @cols);
|
|
Getopt::Long::Configure(qw{bundling no_ignore_case});
|
|
GetOptions(
|
|
\%l_opts,
|
|
'delimiter|d=s',
|
|
'no-header|N',
|
|
'alias|a',
|
|
'enabled|e',
|
|
'id|i',
|
|
'name|n',
|
|
'priority|p',
|
|
'refresh|r',
|
|
'service|s',
|
|
'type|t',
|
|
'uri|u',
|
|
) or usage();
|
|
usage() if @ARGV;
|
|
my %widths;
|
|
for my $col (qw{id alias name enabled refresh type priority uri service}) {
|
|
if ($l_opts{$col}) {
|
|
push @cols, $col;
|
|
my $max_width = 0;
|
|
my @values = ($col, map($_->{$col}, @repos));
|
|
foreach my $width (map length, @values) {
|
|
$max_width = $width if $width > $max_width;
|
|
}
|
|
$widths{$col} = $max_width;
|
|
}
|
|
}
|
|
my $delimiter = $l_opts{delimiter} || $DEFAULT_L_DELIMITER;
|
|
my $format = join($delimiter,
|
|
map { get_col_format($_, $widths{$_}) } @cols) . "\n";
|
|
|
|
unless ($l_opts{'no-header'}) {
|
|
my $header = sprintf $format, @cols;
|
|
print $header, '-' x length($header), "\n";
|
|
}
|
|
foreach my $repo (@repos) {
|
|
printf $format, map { $repo->{$_} } @cols;
|
|
}
|
|
}
|
|
|
|
sub get_col_format {
|
|
my ($name, $max_width) = @_;
|
|
my $type = 's';
|
|
my $align = '-';
|
|
if ($name eq 'id') {
|
|
#$type = 'd';
|
|
$align = '';
|
|
}
|
|
return "%$align$max_width$type";
|
|
}
|
|
|
|
sub get_zypper_l_auto_cols {
|
|
my %auto_opts = map { $_ => 1 } @ZYPPER_L_AUTO_COLS;
|
|
foreach my $opt (@ZYPPER_L_AUTO_COLS) {
|
|
delete $auto_opts{$opt} if $opts{$opt};
|
|
}
|
|
delete $auto_opts{refresh} if $opts{'no-refresh'};
|
|
delete $auto_opts{name} if $auto_opts{alias};
|
|
return map "--$_", keys %auto_opts;
|
|
}
|