aboutsummaryrefslogtreecommitdiffstats
path: root/bin
diff options
context:
space:
mode:
authorPascal Terjan <pterjan@mageia.org>2011-01-07 08:07:11 +0000
committerPascal Terjan <pterjan@mageia.org>2011-01-07 08:07:11 +0000
commitabc9a802404902718dc808fdce36f226533f02de (patch)
treefc46dc3b0da9bf9fead8a50c9a6a3d8a5e27a75a /bin
downloadmga-youri-submit-abc9a802404902718dc808fdce36f226533f02de.tar
mga-youri-submit-abc9a802404902718dc808fdce36f226533f02de.tar.gz
mga-youri-submit-abc9a802404902718dc808fdce36f226533f02de.tar.bz2
mga-youri-submit-abc9a802404902718dc808fdce36f226533f02de.tar.xz
mga-youri-submit-abc9a802404902718dc808fdce36f226533f02de.zip
get_file_name returns a full path, which Install does not like
Diffstat (limited to 'bin')
-rwxr-xr-xbin/youri-check.in395
-rwxr-xr-xbin/youri-submit534
-rwxr-xr-xbin/youri-submit-proxy.in77
-rwxr-xr-xbin/youri-submit-restricted.in64
-rwxr-xr-xbin/youri-submit.in534
5 files changed, 1604 insertions, 0 deletions
diff --git a/bin/youri-check.in b/bin/youri-check.in
new file mode 100755
index 0000000..b32f4fd
--- /dev/null
+++ b/bin/youri-check.in
@@ -0,0 +1,395 @@
+#!/usr/bin/perl
+# $Id: youri-check.in 1699 2006-10-16 11:33:58Z warly $
+
+=head1 NAME
+
+youri-check - package check agent
+
+=head1 VERSION
+
+Version 1.0
+
+=head1 SYNOPSIS
+
+youri-check [options] <mode>
+
+Options:
+
+ --config <file> use file <file> as config file
+ --skip-media <media> skip media <media>
+ --skip-plugin <plugin> skip plugin <plugin>
+ --parallel parallel run
+ --verbose verbose run
+ --test test run
+ --help print this help message
+
+=head1 DESCRIPTION
+
+B<youri-check> allows to check packages in a repository.
+
+In input mode, all medias defined in configuration are passed to a list of
+input plugins, each of them storing their result in a persistent resultset. In
+output mode, this resultset is passed to a list of output plugins, each of them
+producing arbitrary effects.
+
+=head1 OPTIONS
+
+=over
+
+=item B<--config> <file>
+
+Use given file as configuration, instead of normal one.
+
+=item B<--skip-media> <media>
+
+Skip media with given identity.
+
+=item B<--skip-plugin> <plugin>
+
+Skip plugin with given identity.
+
+=item B<--parallel>
+
+Run all plugins parallelously
+
+=item B<--verbose>
+
+Produce more verbose output (can be used more than once)
+
+=item B<--test>
+
+Don't perform any modification.
+
+=item B<--help>
+
+Print a brief help message and exits.
+
+=back
+
+=head1 CONFIGURATION
+
+Configuration is read from the first file found among:
+
+=over
+
+=item * the one specified by B<--config> option on command-line
+
+=item * $HOME/.youri/check.conf
+
+=item * @sysconfdir@/youri/check.conf
+
+=back
+
+All additional configuration files specified by B<includes> directive are then
+processed. Then command line options. Any directive overrides prior definition.
+
+=over
+
+=item B<includes> I<files>
+
+Uses space-separated list I<files> as a list of additional configuration files.
+
+=item B<resolver> I<id>
+
+Declare a maintainer resolver object with identity I<id>.
+
+=item B<preferences> I<id>
+
+Declare a maintainer preferences object with identity I<id>.
+
+=item B<resultset> I<id>
+
+Declare a resultset object with identity I<id>.
+
+=item B<medias> I<ids>
+
+Declares a list of media objects with identity taken in space-separated list
+I<ids>.
+
+=item B<inputs> I<ids>
+
+Declares a list of input plugin objects with identity taken in space-separated
+list I<ids>.
+
+=item B<outputs> I<ids>
+
+Declares a list of output plugin objects with identity taken in space-separated
+list I<ids>.
+
+=back
+
+Each object declared in configuration must be fully defined later, using a
+configuration section, starting with bracketed object identity, followed by at
+least a class directive, then any number of additional object-specific
+directives.
+
+Example:
+
+ objects = foo
+
+ [foo]
+ class = Foo::Bar
+ key1 = value1
+ key2 = value2
+
+=head1 SEE ALSO
+
+Youri::Config, for configuration file format.
+
+Each used plugin man page, for available options.
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2002-2006, YOURI project
+
+This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+
+=cut
+
+use strict;
+use warnings;
+
+use Youri::Config;
+use Youri::Utils;
+use Pod::Usage;
+use Net::Config qw/%NetConfig/;
+use DateTime;
+
+my $config = Youri::Config->new(
+ command_spec => [
+ 'config=s',
+ 'skip-plugin=s@',
+ 'skip-media=s@',
+ 'parallel!',
+ 'help|h!',
+ 'test|t!',
+ 'verbose|v!'
+ ],
+ file_spec => [
+ 'includes=s',
+ 'resolver=s',
+ 'preferences=s',
+ 'resultset=s',
+ 'medias=s',
+ 'inputs=s',
+ 'outputs=s'
+ ],
+ directories => [ '@sysconfdir@', "$ENV{HOME}/.youri" ],
+ file_name => 'check.conf',
+ caller => $0,
+);
+
+pod2usage(
+ -verbose => 0,
+ -message => "No mode specified, aborting\n"
+) unless @ARGV;
+
+my $mode = $ARGV[0];
+
+# convenient global flags
+my $test = $config->get('test');
+my $verbose = $config->get('verbose');
+
+# libnet configuration
+my %netconfig = $config->get_section('netconfig');
+$NetConfig{$_} = $netconfig{$_} foreach keys %netconfig;
+
+# resultset creation
+my $resultset_id = $config->get('resultset');
+die "No resultset defined" unless $resultset_id;
+
+report("Creating resultset $resultset_id");
+my $resultset = create_instance(
+ 'Youri::Check::Resultset',
+ test => $test,
+ verbose => $verbose > 0 ? $verbose - 1 : 0,
+ $config->get_section($resultset_id)
+);
+
+my $children;
+
+my %skip_plugins = map { $_ => 1 } @{$config->get('skip-plugin')};
+
+if ($mode eq 'input') {
+
+ # additional objects
+
+ my $resolver;
+ my $resolver_id = $config->get('resolver');
+ if ($resolver_id) {
+ report("Creating maintainer resolver $resolver_id");
+ eval {
+ $resolver = create_instance(
+ 'Youri::Check::Maintainer::Resolver',
+ test => $test,
+ verbose => $verbose > 1 ? $verbose - 2 : 0,
+ $config->get_section($resolver_id)
+ );
+ };
+ print STDERR "Failed to create maintainer resolver $resolver_id: $@\n" if $@;
+ }
+
+ my $preferences;
+ my $preferences_id = $config->get('preferences');
+ if ($preferences_id) {
+ report("Creating maintainer preferences $preferences_id");
+ eval {
+ $preferences = create_instance(
+ 'Youri::Check::Maintainer::Preferences',
+ test => $test,
+ verbose => $verbose > 1 ? $verbose - 2 : 0,
+ $config->get_section($preferences_id)
+ );
+ };
+ print STDERR "Failed to create maintainer preferences $preferences_id: $@\n" if $@;
+ }
+
+ my @medias;
+ my %skip_medias = map { $_ => 1 } @{$config->get('skip-media')};
+ foreach my $id (split(/\s+/, $config->get('medias'))) {
+ next if $skip_medias{$id};
+ report("Creating media $id");
+ eval {
+ push(
+ @medias,
+ create_instance(
+ 'Youri::Media',
+ id => $id,
+ test => $test,
+ verbose => $verbose > 0 ? $verbose - 1 : 0,
+ $config->get_section($id)
+ )
+ );
+ };
+ print STDERR "Failed to create media $id: $@\n" if $@;
+ }
+
+ # prepare resultset
+ $resultset->reset();
+ $resultset->set_resolver($resolver);
+
+
+ foreach my $id (split(/\s+/, $config->get('inputs'))) {
+ next if $skip_plugins{$id};
+ report("Creating input $id");
+ my $input;
+ eval {
+ $input = create_instance(
+ 'Youri::Check::Input',
+ id => $id,
+ test => $test,
+ verbose => $verbose > 0 ? $verbose - 1 : 0,
+ resolver => $resolver,
+ preferences => $preferences,
+ $config->get_section($id)
+ );
+ };
+ if ($@) {
+ print STDERR "Failed to create input $id: $@\n";
+ } else {
+ if ($config->get('parallel')) {
+ # fork
+ my $pid = fork;
+ die "Can't fork: $!" unless defined $pid;
+ if ($pid) {
+ # parent process
+ $children++;
+ next;
+ }
+ }
+ eval {
+ $input->prepare(@medias);
+ };
+ if ($@) {
+ print STDERR "Failed to prepare input $id: $@\n";
+ } else {
+ # clone resultset in child process
+ $resultset = $config->get('parallel') ?
+ $resultset->clone() :
+ $resultset;
+
+ foreach my $media (@medias) {
+ next if $media->skip_input($id);
+ my $media_id = $media->get_id();
+ report("running input $id on media $media_id");
+ eval {
+ $input->run($media, $resultset);
+ };
+ if ($@) {
+ print STDERR "Failed to run input $id on media $media_id: $@\n";
+ }
+ }
+ }
+ if ($config->get('parallel')) {
+ # child process
+ exit;
+ }
+ }
+ }
+
+} elsif ($mode eq 'output') {
+
+ foreach my $id (split(/\s+/, $config->get('outputs'))) {
+ next if $skip_plugins{$id};
+ report("Creating output $id");
+ my $output;
+ eval {
+ $output = create_instance(
+ 'Youri::Check::Output',
+ id => $id,
+ test => $test,
+ verbose => $verbose > 0 ? $verbose - 1 : 0,
+ config => $config,
+ $config->get_section($id)
+ );
+ };
+ if ($@) {
+ print STDERR "Failed to create output $id: $@\n";
+ } else {
+ if ($config->get('parallel')) {
+ # fork
+ my $pid = fork;
+ die "Can't fork: $!" unless defined $pid;
+ if ($pid) {
+ # parent process
+ $children++;
+ next;
+ }
+ }
+
+ # clone resultset in child process
+ $resultset = $config->get('parallel') ?
+ $resultset->clone() :
+ $resultset;
+
+ report("running output $id");
+ eval {
+ $output->run($resultset);
+ };
+ if ($@) {
+ print STDERR "Failed to run output $id: $@\n";
+ }
+
+ if ($config->get('parallel')) {
+ # child process
+ exit;
+ }
+ }
+ }
+} else {
+ die "Invalid mode $mode";
+}
+
+# wait for all forked processus termination
+while ($children) {
+ wait;
+ $children--;
+}
+
+sub report {
+ my ($message) = @_;
+ print DateTime->now()->strftime('[%H:%M:%S] ')
+ if $verbose > 1;
+ print "$message\n"
+ if $verbose > 0;
+}
diff --git a/bin/youri-submit b/bin/youri-submit
new file mode 100755
index 0000000..2cec209
--- /dev/null
+++ b/bin/youri-submit
@@ -0,0 +1,534 @@
+#!/usr/bin/perl
+# $Id: youri-submit 232579 2007-12-17 19:45:47Z blino $
+
+=head1 NAME
+
+youri-submit - package submission tool
+
+=head1 VERSION
+
+Version 2.0
+
+=head1 SYNOPSIS
+
+youri-submit [options] <target> <files>
+
+youri-submit --list <category> [target]
+
+youri-submit --help [category] [item]
+
+Options:
+
+ --config <file> use file <file> as config file
+ --skip-pre <pre> skip pre <pre>
+ --skip-check <check> skip check <check>
+ --skip-action <action> skip action <action>
+ --skip-post <post> skip post <post>
+ --skip-reject <reject> skip reject <reject>
+ --define <key>=<value> pass additional values
+ --clean delete package after success
+ --verbose verbose run
+ --test test run
+ --list <category> list items from given category
+ --help [category] display contextual help
+
+=head1 DESCRIPTION
+
+B<youri-submit> allows to submit packages to a repository.
+
+All packages given on command lines are passed to a list of check plugins,
+depending on given upload target. If none of them fails, all packages are
+passed to a list of action plugins, depending also on given upload target.
+
+=head1 OPTIONS
+
+=over
+
+=item B<--config> I<file>
+
+Use given file as configuration, instead of normal one.
+
+=item B<--skip-pre> I<id>
+
+Skip pre transaction plugin with given identity
+
+=item B<--skip-check> I<id>
+
+Skip check plugin with given identity.
+
+=item B<--skip-action> I<id>
+
+Skip action plugin with given identity.
+
+=item B<--skip-post> I<id>
+
+Skip post transaction plugin with given identity.
+
+=item B<--skip-reject> I<id>
+
+Skip reject action plugin with given identity.
+
+=item B<--define> <key>=<value>
+
+Define additional parameters, to be used by plugins.
+
+=item B<--clean>
+
+Delete submited packages upon successfull submission.
+
+=item B<--verbose>
+
+Produce more verbose output (can be used more than once)
+
+=item B<--test>
+
+Don't perform any modification.
+
+=item B<--list> I<category>
+
+List available items from given category and exits. Category must be either
+B<targets>, B<actions> or B<checks>. A target is needed for the two last ones.
+
+=item B<--help> I<category>
+
+Display help for given category and exits. Category must be either
+B<repository>, B<action> or B<check>. An item is needed for the two last ones.
+If no category given, display standard help.
+
+=back
+
+=head1 CONFIGURATION
+
+Configuration is read from the first file found among:
+
+=over
+
+=item * the one specified by B<--config> option on command-line
+
+=item * $HOME/.youri/submit.conf
+
+=item * /usr/local/etc/youri/submit.conf
+
+=back
+
+The configuration file should be a YAML-format files, with the following
+mandatory top-level directives:
+
+=over
+
+=item B<repository>
+
+The definition of repository plugin to be used.
+
+=item B<targets>
+
+The list of available submission targets, each one being composed from the
+following keys:
+
+=over
+
+=item B<checks>
+
+The list of check plugins to use for this target.
+
+=item B<actions>
+
+The list of action plugins to use for this target.
+
+=back
+
+=item B<checks>
+
+The list of check plugin definitions, indexed by their identity.
+
+=item B<actions>
+
+The list of action plugin definitions, indexed by their identity.
+
+=back
+
+=head1 SEE ALSO
+
+Youri::Config, for additional details about configuration file format.
+
+Each used plugin man page, for available options.
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2002-2006, YOURI project
+
+This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+
+=cut
+
+use strict;
+use warnings;
+
+use Youri::Config;
+use Youri::Utils;
+use Pod::Usage;
+
+my $config = Youri::Config->new(
+ args => {
+ 'skip-check' => '=s@',
+ 'skip-action' => '=s@',
+ 'define' => '=s%',
+ 'verbose' => '|v!',
+ 'clean' => '!',
+ 'test' => '|t!',
+ 'list' => '|l!',
+ 'config' => '=s',
+ 'skip-prei' => '=s@',
+ 'skip-post' => '=s@',
+ 'skip-reject' => '=s@',
+ },
+ directories => [ "$ENV{HOME}/.youri", '/usr/local/etc/youri' ],
+ file => 'submit.conf',
+);
+
+if ($config->get_arg('list')) {
+ my $category = $ARGV[0];
+ pod2usage(-verbose => 0, -message => "No category specified, aborting\n")
+ unless $category;
+ if ($category eq 'targets') {
+ print join(' ', keys %{$config->get_param('targets')});
+ } elsif ($category eq 'checks' || $category eq 'actions') {
+ my $target = $ARGV[1];
+ pod2usage(-verbose => 0, -message => "No target specified, aborting\n")
+ unless $target;
+ if ($category eq 'checks') {
+ my $checks = $config->get_param('targets')->{$target}->{checks};
+ print join(' ', @{$checks}) if $checks;
+ } else {
+ my $actions = $config->get_param('targets')->{$target}->{actions};
+ print join(' ', @{$actions}) if $actions;
+ }
+ } else {
+ pod2usage(-verbose => 0, -message => "Invalid category $category, aborting\n")
+ }
+ print "\n";
+ exit 0;
+}
+
+if ($config->get_arg('help')) {
+ my $category = $ARGV[0];
+ my ($item, $section);
+ if ($category eq 'repository') {
+ $section = $config->get_param('repository');
+ pod2usage(
+ -verbose => 0,
+ -message => "No repository defined, aborting\n"
+ ) unless $section;
+ } elsif ($category eq 'check' || $category eq 'action') {
+ $item = $ARGV[1];
+ pod2usage(
+ -verbose => 0,
+ -message => "No item specified, aborting\n"
+ ) unless $item;
+ if ($category eq 'check') {
+ $section = $config->get_param('checks')->{$item};
+ pod2usage(
+ -verbose => 0,
+ -message => "No such check $item defined, aborting\n"
+ ) unless $section;
+ } else {
+ $section = $config->get_param('actions')->{$item};
+ pod2usage(
+ -verbose => 0,
+ -message => "No such action $item defined, aborting\n"
+ ) unless $section;
+ }
+ } else {
+ pod2usage(-verbose => 0, -message => "Invalid category $category, aborting\n")
+ }
+ my $file = $section->{class} . '.pm';
+ $file =~ s/::/\//g;
+ pod2usage(
+ -verbose => 99,
+ -sections => 'NAME|DESCRIPTION',
+ -input => $file,
+ -pathlist => \@INC
+ );
+}
+
+
+pod2usage(-verbose => 0, -message => "No target specified, aborting\n")
+ unless @ARGV > 0;
+pod2usage(-verbose => 0, -message => "No packages specified, aborting\n")
+ unless @ARGV > 1 || $config->get_param('allow_omitting_packages');
+
+# convenient global flags
+my $test = $config->get_arg('test');
+my $verbose = $config->get_arg('verbose');
+
+# check target
+my $target = shift @ARGV;
+my $target_conf = $config->get_param('targets')->{$target};
+
+# create repository
+my $repository;
+my $repository_conf = $config->get_param('repository');
+die "No repository declared" unless $repository_conf;
+print "Creating repository\n" if $verbose;
+eval {
+ $repository = create_instance(
+ 'Youri::Repository',
+ $repository_conf,
+ {
+ test => $test,
+ verbose => $verbose > 0 ? $verbose - 1 : 0,
+ targets => [ keys %{$config->get_param('targets')} ],
+ }
+ );
+};
+die "Failed to create repository: $@\n" if $@;
+
+# perfrom pre action
+my @errors;
+my $pre_packages = [];
+my $skip_pres = $config->get_arg('skip-pre');
+my %skip_pres = $skip_pres ? map { $_ => 1 } @{$skip_pres} : ();
+foreach my $id (@{$target_conf->{pres}}) {
+ next if $skip_pres{$id};
+ print "Creating pre $id\n" if $verbose;
+ my $pre;
+ my $pre_conf = $config->get_param('pres')->{$id};
+
+ if (!$pre_conf) {
+ print STDERR "No such pre $id, skipping\n";
+ next;
+ }
+ eval {
+ $pre = create_instance(
+ 'Youri::Submit::Pre',
+ $pre_conf,
+ {
+ id => $id,
+ test => $test,
+ verbose => $verbose > 0 ? $verbose - 1 : 0,
+ }
+ );
+ };
+ if ($@) {
+ print STDERR "Failed to create pre $id: $@\n";
+ } else {
+ print "running pre $id\n" if $verbose;
+ my @err = $pre->run(
+ $pre_packages,
+ $repository,
+ $target,
+ $config->get_arg('define')
+ );
+ push(@errors, @err) if $err[0];
+ }
+}
+
+if (@errors) {
+ print "Pre-submission errors, aborting:\n";
+ foreach my $error (@errors) {
+ print " - $error\n";
+ }
+ exit(1)
+}
+
+# create packages group
+my $group_error;
+my @packages_group;
+foreach my $group ([ map { { section => "", file => $_ } } @ARGV ], @$pre_packages) {
+ my @packages;
+ foreach my $opt (@$group) {
+ print "Preparing upload for $opt->{file} in $opt->{section}\n" if $verbose;
+ $repository->{packages}{$opt->{file}}{section} = $opt->{section};
+ push(
+ @packages,
+ create_instance(
+ 'Youri::Package',
+ {
+ class => $repository->get_package_class(),
+ },
+ {
+ file => $opt->{file},
+ %$opt
+ },
+ )
+ );
+ }
+ @packages or next;
+
+# check all packages pass all tests
+ my %errors;
+ my $skip_check = $config->get_arg('skip-check');
+ my %skip_check = $skip_check ? map { $_ => 1 } @{$skip_check} : ();
+ my @error;
+ foreach my $id (@{$target_conf->{checks}}) {
+ next if $skip_check{$id};
+ print "Creating check $id\n" if $verbose;
+ my $check;
+ my $check_conf = $config->get_param('checks')->{$id};
+
+ if (!$check_conf) {
+ print STDERR "No such check $id, skipping\n";
+ next;
+ }
+ eval {
+ $check = create_instance(
+ 'Youri::Submit::Check',
+ $check_conf,
+ {
+ id => $id,
+ test => $test,
+ verbose => $verbose > 0 ? $verbose - 1 : 0,
+ }
+ );
+ };
+ if ($@) {
+ print STDERR "Failed to create check $id: $@\n";
+ } else {
+ foreach my $package (@packages) {
+ print "running check $id on package $package\n" if $verbose;
+ my @errors = $check->run(
+ $package,
+ $repository,
+ $target,
+ $config->get_arg('define')
+ );
+ push(@{$errors{$package}}, @errors) if $errors[0];
+ }
+ }
+ }
+ if (%errors) {
+ print "Submission errors, aborting:\n";
+ foreach my $package (keys %errors) {
+ print "- $package:\n";
+ foreach my $error (@{$errors{$package}}) {
+ print " - $error\n";
+ }
+ }
+ # reject the packages
+ my $skip_rejects = $config->get_arg('skip-reject');
+ my %skip_rejects = $skip_rejects ? map { $_ => 1 } @{$skip_rejects} : ();
+ foreach my $id (@{$target_conf->{rejects}}) {
+ next if $skip_rejects{$id};
+ print "Creating reject $id\n" if $verbose;
+ my $reject;
+ my $reject_conf = $config->get_param('rejects')->{$id};
+
+ if (!$reject_conf) {
+ print STDERR "No such reject $id, skipping\n";
+ next;
+ }
+ eval {
+ $reject = create_instance(
+ 'Youri::Submit::Reject',
+ $reject_conf,
+ {
+ id => $id,
+ test => $test,
+ verbose => $verbose > 0 ? $verbose - 1 : 0,
+ }
+ );
+ };
+ if ($@) {
+ print STDERR "Failed to create reject $id: $@\n";
+ } else {
+ foreach my $package (@packages) {
+ print "running reject $id on package $package\n" if $verbose;
+ eval {
+ $reject->run($package, \%errors, $repository, $target, $config->get_arg('define'));
+ };
+ if ($@) {
+ print STDERR "Failed to run action $id on package $package: $@\n";
+ }
+ }
+ }
+ }
+ $group_error = 1;
+ next
+ }
+
+# proceed further
+ my $skip_action = $config->get_arg('skip-action');
+ my %skip_action = $skip_action ? map { $_ => 1 } @{$skip_action} : ();
+ foreach my $id (@{$target_conf->{actions}}) {
+ next if $skip_action{$id};
+ print "Creating action $id\n" if $verbose;
+ my $action;
+ my $action_conf = $config->get_param('actions')->{$id};
+
+ if (!$action_conf) {
+ print STDERR "No such action $id, skipping\n";
+ next;
+ }
+ eval {
+ $action = create_instance(
+ 'Youri::Submit::Action',
+ $action_conf,
+ {
+ id => $id,
+ test => $test,
+ verbose => $verbose > 0 ? $verbose - 1 : 0,
+ }
+ );
+ };
+ if ($@) {
+ print STDERR "Failed to create action $id: $@\n";
+ } else {
+ foreach my $package (@packages) {
+ print "running action $id on package $package\n" if $verbose;
+ eval {
+ $action->run(
+ $package,
+ $repository,
+ $target,
+ $config->get_arg('define')
+ );
+ };
+ if ($@) {
+ print STDERR "Failed to run action $id on package $package: $@\n";
+ }
+ }
+ }
+ }
+
+ if ($config->get_arg('clean')) {
+ foreach my $package (@packages) {
+ print "cleaning file $package\n" if $verbose;
+ unlink $package->as_file();
+ }
+ }
+}
+
+# perfrom post action
+my $skip_post = $config->get_arg('skip-post');
+my %skip_post = $skip_post ? map { $_ => 1 } @{$skip_post} : ();
+foreach my $id (@{$target_conf->{posts}}) {
+ next if $skip_post{$id};
+ print "Creating post $id\n" if $verbose;
+ my $post;
+ my $post_conf = $config->get_param('posts')->{$id};
+
+ if (!$post_conf) {
+ print STDERR "No such post $id, skipping\n";
+ next;
+ }
+ eval {
+ $post = create_instance(
+ 'Youri::Submit::Post',
+ $post_conf,
+ {
+ id => $id,
+ test => $test,
+ verbose => $verbose > 0 ? $verbose - 1 : 0,
+ }
+ );
+ };
+ if ($@) {
+ print STDERR "Failed to create post $id: $@\n";
+ } else {
+ print "running post $id\n" if $verbose;
+ my @err = $post->run($repository, $target, $config->get_arg('define'));
+ print STDERR "Error $id: @err\n" if @err
+ }
+}
+
+exit(1) if $group_error;
diff --git a/bin/youri-submit-proxy.in b/bin/youri-submit-proxy.in
new file mode 100755
index 0000000..67fed6e
--- /dev/null
+++ b/bin/youri-submit-proxy.in
@@ -0,0 +1,77 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+youri-submit-proxy - proxy wrapper over youri-submit-restricted
+
+=head1 VERSION
+
+Version 1.0
+
+=head1 SYNOPSIS
+
+youri-submit-proxy [options] <target> <files>
+
+=head1 DESCRIPTION
+
+youri-submit-proxy is a proxy wrapper over youri-submit-restricted, intended to
+be used in collaborative work to change uid before calling it through sudo.
+
+=head1 SEE ALSO
+
+youri-submit-restricted(1), youri-submit(1)
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2002-2006, YOURI project
+
+This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+
+=cut
+
+use strict;
+use warnings;
+use Fcntl ':mode';
+use File::Basename;
+
+my ($uid, $gid);
+if (-l $0) {
+ # this is a symlink, get uid and gid from it
+ ($uid, $gid) = (lstat($0))[4, 5];
+} else {
+ ($uid, $gid) = (stat($0))[4, 5];
+}
+my $user = getpwuid($uid) or die "unknown uid $uid";
+my $prog = '@bindir@/youri-submit-restricted';
+
+my %dirs;
+my @options;
+foreach my $arg (@ARGV) {
+ if (-f $arg) {
+ # push parent dir in list
+ my $parent = dirname($arg);
+ $dirs{$parent}++;
+ }
+ push(@options, $arg);
+}
+
+foreach my $dir (keys %dirs) {
+ # save original perms and gid
+ my ($orig_mode, $orig_gid) = (stat($dir))[2,5];
+ $dirs{$dir} = {
+ mode => $orig_mode,
+ gid => $orig_gid
+ };
+ # ensure correct perms and gid
+ chown -1, $gid, $dir;
+ chmod $orig_mode|S_IRGRP|S_IWGRP, $dir;
+}
+
+# call wrapped program
+system('sudo', '-H', '-u', $user, $prog, @options);
+
+foreach my $dir (keys %dirs) {
+ # restore original perms and gid
+ chown -1, $dirs{$dir}->{gid}, $dir;
+ chmod $dirs{$dir}->{mode}, $dir;
+}
diff --git a/bin/youri-submit-restricted.in b/bin/youri-submit-restricted.in
new file mode 100755
index 0000000..d28ba84
--- /dev/null
+++ b/bin/youri-submit-restricted.in
@@ -0,0 +1,64 @@
+#!/usr/bin/perl -T
+
+=head1 NAME
+
+youri-submit-restricted - filtering wrapper over youri-submit
+
+=head1 VERSION
+
+Version 1.0
+
+=head1 SYNOPSIS
+
+youri-submit-restricted [options] <target> <files>
+
+=head1 DESCRIPTION
+
+youri-submit-restricted is just a filtering wrapper over youri-submit, intended
+to be used in collaborative work to sanitize environment and options before
+calling it.
+
+=head1 SEE ALSO
+
+youri-submit(1)
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2002-2006, YOURI project
+
+This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+
+=cut
+
+use strict;
+use warnings;
+
+my $prog = '@bindir@/youri-submit';
+my @prohibited_options = qw/--config --skip-check --skip-action/;
+my %prohibited_options = map { $_ => 1 } @prohibited_options;
+my @prohibited_envvars = qw/
+ ENV BASH_ENV IFS CDPATH
+ PERLLIB PERL5LIB PERL5OPT PERLIO
+ PERLIO_DEBUG PERL5DB PERL_ENCODING
+ PERL_HASH_SEED PERL_SIGNALS PERL_UNICODE
+/;
+
+my @options;
+while (my $arg = shift @ARGV) {
+ if ($prohibited_options{$arg}) {
+ # drop prohibited options
+ print STDERR "prohibited option $arg, skipping\n";
+ shift @ARGV;
+ } else {
+ # untaint everything else
+ $arg =~ /(.*)/;
+ push(@options, $1);
+ }
+}
+
+# secure ENV
+$ENV{PATH} = "/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin";
+delete $ENV{$_} foreach @prohibited_envvars;
+
+# call wrapped program
+system($prog, @options);
diff --git a/bin/youri-submit.in b/bin/youri-submit.in
new file mode 100755
index 0000000..9892d0e
--- /dev/null
+++ b/bin/youri-submit.in
@@ -0,0 +1,534 @@
+#!/usr/bin/perl
+# $Id: youri-submit.in 232668 2007-12-21 14:37:04Z blino $
+
+=head1 NAME
+
+youri-submit - package submission tool
+
+=head1 VERSION
+
+Version 2.0
+
+=head1 SYNOPSIS
+
+youri-submit [options] <target> <files>
+
+youri-submit --list <category> [target]
+
+youri-submit --help [category] [item]
+
+Options:
+
+ --config <file> use file <file> as config file
+ --skip-pre <pre> skip pre <pre>
+ --skip-check <check> skip check <check>
+ --skip-action <action> skip action <action>
+ --skip-post <post> skip post <post>
+ --skip-reject <reject> skip reject <reject>
+ --define <key>=<value> pass additional values
+ --clean delete package after success
+ --verbose verbose run
+ --test test run
+ --list <category> list items from given category
+ --help [category] display contextual help
+
+=head1 DESCRIPTION
+
+B<youri-submit> allows to submit packages to a repository.
+
+All packages given on command lines are passed to a list of check plugins,
+depending on given upload target. If none of them fails, all packages are
+passed to a list of action plugins, depending also on given upload target.
+
+=head1 OPTIONS
+
+=over
+
+=item B<--config> I<file>
+
+Use given file as configuration, instead of normal one.
+
+=item B<--skip-pre> I<id>
+
+Skip pre transaction plugin with given identity
+
+=item B<--skip-check> I<id>
+
+Skip check plugin with given identity.
+
+=item B<--skip-action> I<id>
+
+Skip action plugin with given identity.
+
+=item B<--skip-post> I<id>
+
+Skip post transaction plugin with given identity.
+
+=item B<--skip-reject> I<id>
+
+Skip reject action plugin with given identity.
+
+=item B<--define> <key>=<value>
+
+Define additional parameters, to be used by plugins.
+
+=item B<--clean>
+
+Delete submited packages upon successfull submission.
+
+=item B<--verbose>
+
+Produce more verbose output (can be used more than once)
+
+=item B<--test>
+
+Don't perform any modification.
+
+=item B<--list> I<category>
+
+List available items from given category and exits. Category must be either
+B<targets>, B<actions> or B<checks>. A target is needed for the two last ones.
+
+=item B<--help> I<category>
+
+Display help for given category and exits. Category must be either
+B<repository>, B<action> or B<check>. An item is needed for the two last ones.
+If no category given, display standard help.
+
+=back
+
+=head1 CONFIGURATION
+
+Configuration is read from the first file found among:
+
+=over
+
+=item * the one specified by B<--config> option on command-line
+
+=item * $HOME/.youri/submit.conf
+
+=item * /usr/local/etc/youri/submit.conf
+
+=back
+
+The configuration file should be a YAML-format files, with the following
+mandatory top-level directives:
+
+=over
+
+=item B<repository>
+
+The definition of repository plugin to be used.
+
+=item B<targets>
+
+The list of available submission targets, each one being composed from the
+following keys:
+
+=over
+
+=item B<checks>
+
+The list of check plugins to use for this target.
+
+=item B<actions>
+
+The list of action plugins to use for this target.
+
+=back
+
+=item B<checks>
+
+The list of check plugin definitions, indexed by their identity.
+
+=item B<actions>
+
+The list of action plugin definitions, indexed by their identity.
+
+=back
+
+=head1 SEE ALSO
+
+Youri::Config, for additional details about configuration file format.
+
+Each used plugin man page, for available options.
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2002-2006, YOURI project
+
+This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+
+=cut
+
+use strict;
+use warnings;
+
+use Youri::Config;
+use Youri::Utils;
+use Pod::Usage;
+
+my $config = Youri::Config->new(
+ args => {
+ 'skip-check' => '=s@',
+ 'skip-action' => '=s@',
+ 'define' => '=s%',
+ 'verbose' => '|v!',
+ 'clean' => '!',
+ 'test' => '|t!',
+ 'list' => '|l!',
+ 'config' => '=s',
+ 'skip-prei' => '=s@',
+ 'skip-post' => '=s@',
+ 'skip-reject' => '=s@',
+ },
+ directories => [ "$ENV{HOME}/.youri", '@sysconfdir@/youri' ],
+ file => 'submit.conf',
+);
+
+if ($config->get_arg('list')) {
+ my $category = $ARGV[0];
+ pod2usage(-verbose => 0, -message => "No category specified, aborting\n")
+ unless $category;
+ if ($category eq 'targets') {
+ print join(' ', keys %{$config->get_param('targets')});
+ } elsif ($category eq 'checks' || $category eq 'actions') {
+ my $target = $ARGV[1];
+ pod2usage(-verbose => 0, -message => "No target specified, aborting\n")
+ unless $target;
+ if ($category eq 'checks') {
+ my $checks = $config->get_param('targets')->{$target}->{checks};
+ print join(' ', @{$checks}) if $checks;
+ } else {
+ my $actions = $config->get_param('targets')->{$target}->{actions};
+ print join(' ', @{$actions}) if $actions;
+ }
+ } else {
+ pod2usage(-verbose => 0, -message => "Invalid category $category, aborting\n")
+ }
+ print "\n";
+ exit 0;
+}
+
+if ($config->get_arg('help')) {
+ my $category = $ARGV[0];
+ my ($item, $section);
+ if ($category eq 'repository') {
+ $section = $config->get_param('repository');
+ pod2usage(
+ -verbose => 0,
+ -message => "No repository defined, aborting\n"
+ ) unless $section;
+ } elsif ($category eq 'check' || $category eq 'action') {
+ $item = $ARGV[1];
+ pod2usage(
+ -verbose => 0,
+ -message => "No item specified, aborting\n"
+ ) unless $item;
+ if ($category eq 'check') {
+ $section = $config->get_param('checks')->{$item};
+ pod2usage(
+ -verbose => 0,
+ -message => "No such check $item defined, aborting\n"
+ ) unless $section;
+ } else {
+ $section = $config->get_param('actions')->{$item};
+ pod2usage(
+ -verbose => 0,
+ -message => "No such action $item defined, aborting\n"
+ ) unless $section;
+ }
+ } else {
+ pod2usage(-verbose => 0, -message => "Invalid category $category, aborting\n")
+ }
+ my $file = $section->{class} . '.pm';
+ $file =~ s/::/\//g;
+ pod2usage(
+ -verbose => 99,
+ -sections => 'NAME|DESCRIPTION',
+ -input => $file,
+ -pathlist => \@INC
+ );
+}
+
+
+pod2usage(-verbose => 0, -message => "No target specified, aborting\n")
+ unless @ARGV > 0;
+pod2usage(-verbose => 0, -message => "No packages specified, aborting\n")
+ unless @ARGV > 1 || $config->get_param('allow_omitting_packages');
+
+# convenient global flags
+my $test = $config->get_arg('test');
+my $verbose = $config->get_arg('verbose');
+
+# check target
+my $target = shift @ARGV;
+my $target_conf = $config->get_param('targets')->{$target};
+
+# create repository
+my $repository;
+my $repository_conf = $config->get_param('repository');
+die "No repository declared" unless $repository_conf;
+print "Creating repository\n" if $verbose;
+eval {
+ $repository = create_instance(
+ 'Youri::Repository',
+ $repository_conf,
+ {
+ test => $test,
+ verbose => $verbose > 0 ? $verbose - 1 : 0,
+ targets => [ keys %{$config->get_param('targets')} ],
+ }
+ );
+};
+die "Failed to create repository: $@\n" if $@;
+
+# perfrom pre action
+my @errors;
+my $pre_packages = [];
+my $skip_pres = $config->get_arg('skip-pre');
+my %skip_pres = $skip_pres ? map { $_ => 1 } @{$skip_pres} : ();
+foreach my $id (@{$target_conf->{pres}}) {
+ next if $skip_pres{$id};
+ print "Creating pre $id\n" if $verbose;
+ my $pre;
+ my $pre_conf = $config->get_param('pres')->{$id};
+
+ if (!$pre_conf) {
+ print STDERR "No such pre $id, skipping\n";
+ next;
+ }
+ eval {
+ $pre = create_instance(
+ 'Youri::Submit::Pre',
+ $pre_conf,
+ {
+ id => $id,
+ test => $test,
+ verbose => $verbose > 0 ? $verbose - 1 : 0,
+ }
+ );
+ };
+ if ($@) {
+ print STDERR "Failed to create pre $id: $@\n";
+ } else {
+ print "running pre $id\n" if $verbose;
+ my @err = $pre->run(
+ $pre_packages,
+ $repository,
+ $target,
+ $config->get_arg('define')
+ );
+ push(@errors, @err) if $err[0];
+ }
+}
+
+if (@errors) {
+ print "Pre-submission errors, aborting:\n";
+ foreach my $error (@errors) {
+ print " - $error\n";
+ }
+ exit(1)
+}
+
+# create packages group
+my $group_error;
+my @packages_group;
+foreach my $group ([ map { { section => "", file => $_ } } @ARGV ], @$pre_packages) {
+ my @packages;
+ foreach my $opt (@$group) {
+ print "Preparing upload for $opt->{file} in $opt->{section}\n" if $verbose;
+ $repository->{packages}{$opt->{file}}{section} = $opt->{section};
+ push(
+ @packages,
+ create_instance(
+ 'Youri::Package',
+ {
+ class => $repository->get_package_class(),
+ },
+ {
+ file => $opt->{file},
+ %$opt
+ },
+ )
+ );
+ }
+ @packages or next;
+
+# check all packages pass all tests
+ my %errors;
+ my $skip_check = $config->get_arg('skip-check');
+ my %skip_check = $skip_check ? map { $_ => 1 } @{$skip_check} : ();
+ my @error;
+ foreach my $id (@{$target_conf->{checks}}) {
+ next if $skip_check{$id};
+ print "Creating check $id\n" if $verbose;
+ my $check;
+ my $check_conf = $config->get_param('checks')->{$id};
+
+ if (!$check_conf) {
+ print STDERR "No such check $id, skipping\n";
+ next;
+ }
+ eval {
+ $check = create_instance(
+ 'Youri::Submit::Check',
+ $check_conf,
+ {
+ id => $id,
+ test => $test,
+ verbose => $verbose > 0 ? $verbose - 1 : 0,
+ }
+ );
+ };
+ if ($@) {
+ print STDERR "Failed to create check $id: $@\n";
+ } else {
+ foreach my $package (@packages) {
+ print "running check $id on package $package\n" if $verbose;
+ my @errors = $check->run(
+ $package,
+ $repository,
+ $target,
+ $config->get_arg('define')
+ );
+ push(@{$errors{$package}}, @errors) if $errors[0];
+ }
+ }
+ }
+ if (%errors) {
+ print "Submission errors, aborting:\n";
+ foreach my $package (keys %errors) {
+ print "- $package:\n";
+ foreach my $error (@{$errors{$package}}) {
+ print " - $error\n";
+ }
+ }
+ # reject the packages
+ my $skip_rejects = $config->get_arg('skip-reject');
+ my %skip_rejects = $skip_rejects ? map { $_ => 1 } @{$skip_rejects} : ();
+ foreach my $id (@{$target_conf->{rejects}}) {
+ next if $skip_rejects{$id};
+ print "Creating reject $id\n" if $verbose;
+ my $reject;
+ my $reject_conf = $config->get_param('rejects')->{$id};
+
+ if (!$reject_conf) {
+ print STDERR "No such reject $id, skipping\n";
+ next;
+ }
+ eval {
+ $reject = create_instance(
+ 'Youri::Submit::Reject',
+ $reject_conf,
+ {
+ id => $id,
+ test => $test,
+ verbose => $verbose > 0 ? $verbose - 1 : 0,
+ }
+ );
+ };
+ if ($@) {
+ print STDERR "Failed to create reject $id: $@\n";
+ } else {
+ foreach my $package (@packages) {
+ print "running reject $id on package $package\n" if $verbose;
+ eval {
+ $reject->run($package, \%errors, $repository, $target, $config->get_arg('define'));
+ };
+ if ($@) {
+ print STDERR "Failed to run action $id on package $package: $@\n";
+ }
+ }
+ }
+ }
+ $group_error = 1;
+ next
+ }
+
+# proceed further
+ my $skip_action = $config->get_arg('skip-action');
+ my %skip_action = $skip_action ? map { $_ => 1 } @{$skip_action} : ();
+ foreach my $id (@{$target_conf->{actions}}) {
+ next if $skip_action{$id};
+ print "Creating action $id\n" if $verbose;
+ my $action;
+ my $action_conf = $config->get_param('actions')->{$id};
+
+ if (!$action_conf) {
+ print STDERR "No such action $id, skipping\n";
+ next;
+ }
+ eval {
+ $action = create_instance(
+ 'Youri::Submit::Action',
+ $action_conf,
+ {
+ id => $id,
+ test => $test,
+ verbose => $verbose > 0 ? $verbose - 1 : 0,
+ }
+ );
+ };
+ if ($@) {
+ print STDERR "Failed to create action $id: $@\n";
+ } else {
+ foreach my $package (@packages) {
+ print "running action $id on package $package\n" if $verbose;
+ eval {
+ $action->run(
+ $package,
+ $repository,
+ $target,
+ $config->get_arg('define')
+ );
+ };
+ if ($@) {
+ print STDERR "Failed to run action $id on package $package: $@\n";
+ }
+ }
+ }
+ }
+
+ if ($config->get_arg('clean')) {
+ foreach my $package (@packages) {
+ print "cleaning file $package\n" if $verbose;
+ unlink $package->as_file();
+ }
+ }
+}
+
+# perfrom post action
+my $skip_post = $config->get_arg('skip-post');
+my %skip_post = $skip_post ? map { $_ => 1 } @{$skip_post} : ();
+foreach my $id (@{$target_conf->{posts}}) {
+ next if $skip_post{$id};
+ print "Creating post $id\n" if $verbose;
+ my $post;
+ my $post_conf = $config->get_param('posts')->{$id};
+
+ if (!$post_conf) {
+ print STDERR "No such post $id, skipping\n";
+ next;
+ }
+ eval {
+ $post = create_instance(
+ 'Youri::Submit::Post',
+ $post_conf,
+ {
+ id => $id,
+ test => $test,
+ verbose => $verbose > 0 ? $verbose - 1 : 0,
+ }
+ );
+ };
+ if ($@) {
+ print STDERR "Failed to create post $id: $@\n";
+ } else {
+ print "running post $id\n" if $verbose;
+ my @err = $post->run($repository, $target, $config->get_arg('define'));
+ print STDERR "Error $id: @err\n" if @err
+ }
+}
+
+exit(1) if $group_error;