diff options
-rw-r--r-- | ChangeLog | 2 | ||||
-rw-r--r-- | MANIFEST.SKIP | 11 | ||||
-rw-r--r-- | Makefile.PL | 97 | ||||
-rw-r--r-- | README | 45 | ||||
-rw-r--r-- | TODO | 6 | ||||
-rwxr-xr-x | bin/youri-submit-proxy.in | 77 | ||||
-rwxr-xr-x | bin/youri-submit-restricted.in | 64 | ||||
-rwxr-xr-x | bin/youri-submit.in | 404 | ||||
-rw-r--r-- | etc/bash_completion.d/youri-submit | 60 | ||||
-rw-r--r-- | etc/submit.conf | 134 | ||||
-rw-r--r-- | lib/Youri/Submit/Plugin.pm | 93 | ||||
-rwxr-xr-x | t/00distribution.t | 15 |
12 files changed, 853 insertions, 155 deletions
diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..01e42c4 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,2 @@ +2006-04-23 Guillaume Rousse <guillomovitch@zarb.org> 0.9 + * initial release diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP new file mode 100644 index 0000000..f2568cb --- /dev/null +++ b/MANIFEST.SKIP @@ -0,0 +1,11 @@ +\.tar\.gz$ +\.SKIP$ +~$ +^pm_to_blib$ +^Makefile$ +^Makefile\.old$ +^bin/youri-submit$ +^bin/youri-submit-restricted$ +^bin/youri-submit-proxy$ +.svn +blib diff --git a/Makefile.PL b/Makefile.PL new file mode 100644 index 0000000..e246de1 --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,97 @@ +# $Id$ +use ExtUtils::MakeMaker; +use Config; + +WriteMakefile( + NAME => 'youri-submit', + VERSION => 0.9, + AUTHOR => 'Youri project <youri@zarb.org>', + EXE_FILES => [ + 'bin/youri-submit', + 'bin/youri-submit-restricted', + 'bin/youri-submit-proxy' + ], + PREREQ_PM => { + 'Youri::Config' => 0, + 'Youri::Utils' => 0, + 'Pod::Simple::HTMLBatch' => 0 + }, + PREFIX => '/usr/local', + INSTALLPRIVLIB => $Config{installprivlib}, + INSTALLSITELIB => $Config{installsitelib}, + INSTALLVENDORLIB => $Config{installvendorlib}, + INSTALLMAN3DIR => $Config{installman3dir}, + INSTALLSITEMAN3DIR => $Config{installsiteman3dir}, + INSTALLVENDORMAN3DIR => $Config{installvendorman3dir}, + INSTALLSCRIPT => '$(PREFIX)/bin', + INSTALLSITESCRIPT => '$(PREFIX)/bin', + INSTALLVENDORSCRIPT => '$(PREFIX)/bin', + INSTALLMAN1DIR => '$(PREFIX)/share/man/man1', + INSTALLSITEMAN1DIR => '$(PREFIX)/share/man/man1', + INSTALLVENDORMAN1DIR => '$(PREFIX)/share/man/man1', +); + +package MY; + +sub post_constants { + my ($self) = @_; + my $sysconfdir = $self->{ARGS}->{SYSCONFDIR} || '$(PREFIX)/etc'; + return <<EOF; +SYSCONFDIR = $sysconfdir +EOF +} + +sub top_targets { + my ($self) = @_; + my $top_targets = $self->SUPER::top_targets(@_); + $top_targets =~ s/all :: pure_all manifypods/all :: pure_all manifypods htmlifypods/; + $top_targets .= <<'EOF'; +htmlifypods : $(TO_INST_PM) + if [ ! -d blib/html ]; then mkdir blib/html; fi + perl -MPod::Simple::HTMLBatch -e Pod::Simple::HTMLBatch::go lib blib/html + pod2html < bin/youri-submit > blib/html/youri-submit.html + pod2html < bin/youri-submit-restricted > blib/html/youri-submit-restricted.html + pod2html < bin/youri-submit-proxy > blib/html/youri-submit-proxy.html +EOF + return $top_targets; +} + +sub install { + my ($self) = @_; + my $install = $self->SUPER::install(@_); + $install =~ s/install :: all pure_install doc_install/install :: all pure_install doc_install config_install completion_install/; + $install .= <<'EOF'; +config_install : + install -d -m 755 $(DESTDIR)$(SYSCONFDIR)/youri + install -m 644 etc/submit.conf $(DESTDIR)$(SYSCONFDIR)/youri + +completion_install : + install -d -m 755 $(DESTDIR)$(SYSCONFDIR)/bash_completion.d + install -m 644 etc/bash_completion.d/youri-submit $(DESTDIR)$(SYSCONFDIR)/bash_completion.d +EOF + return $install; +} + +sub installbin { + my ($self) = @_; + my $installbin = $self->SUPER::installbin(@_); + $installbin .= <<'EOF'; +bin/youri-submit : bin/youri-submit.in Makefile + perl -p \ + -e 's|\@sysconfdir\@|$(SYSCONFDIR)|;' \ + < $< > $@ + +bin/youri-submit-restricted : bin/youri-submit-restricted.in Makefile + perl -p \ + -e 's|\@sysconfdir\@|$(SYSCONFDIR)|;' \ + -e 's|\@bindir\@|$(PREFIX)/bin|;' \ + < $< > $@ + +bin/youri-submit-proxy : bin/youri-submit-proxy.in Makefile + perl -p \ + -e 's|\@sysconfdir\@|$(SYSCONFDIR)|;' \ + -e 's|\@bindir\@|$(PREFIX)/bin|;' \ + < $< > $@ +EOF + return $installbin; +} @@ -0,0 +1,45 @@ +YOURI project +------------- + +YOURI stands for "Youri Offers an Upload & Repository Infrastucture". It aims +to build tools making management of a coherent set of packages easier. + +Description +----------- +Managing a package repository involves many tasks, such as keeping packages +tree tidy, generating packages indexes, synchronising bug report system, +running coherency checks, checking for available updates, etc... + +Instead of a gazillion project-specific scripts, we aim to provide a generic package-format independant framework, so as to build coherent and robust tools. + +Components +---------- +Available software in this release +- youri-check allows to check packages +- youri-upload allows to upload packages + +Installation +------------ +To install, just use: +perl Makefile.PL +make +make test + +All standard MakeMaker variables are usable, with the addition of SYSCONFDIR to +specify configuration files destination. + +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. + +Authors +------- +Guillaume Rousse <guillomovitch@zarb.org>, +Pascal Terjan <pterjan@zarb.org> +Damien Krotkine <dams@zarb.org> +Olivier Thauvin <nanardon@zarb.org> +Ville Skyttä <ville.skytta@iki.fi> + @@ -0,0 +1,6 @@ +1.0 Goals +========= + +- svn support +- automatic bugzilla ticket closing on upload +- more customizable (template based ?) mail notification 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 index c232cfb..07118f1 100755 --- a/bin/youri-submit.in +++ b/bin/youri-submit.in @@ -3,7 +3,7 @@ =head1 NAME -youri-upload - package upload agent +youri-submit - package submission tool =head1 VERSION @@ -11,27 +11,30 @@ Version 2.0 =head1 SYNOPSIS -youri-upload [options] <target> <files> +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-action <action> skip action <action> --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 - --list-targets list available targets - --list-checks <target> list configured checks for for <target> - --list-actions <target> list configured actions for for <target> + --clean delete package after success --verbose verbose run --test test run - --help print this help message + --list <category> list items from given category + --help [category] display contextual help =head1 DESCRIPTION -B<youri-upload> allows to upload packages in a repository. +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 @@ -69,6 +72,10 @@ Skip reject action plugin with given identity. 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) @@ -77,21 +84,16 @@ Produce more verbose output (can be used more than once) Don't perform any modification. -=item B<--list-targets> - -List available targets - -=item B<--list-checks> I<target> - -List configured checks for given target - -=item B<--list-actions> I<target> - -List configured actions for given target - -=item B<--help> - -Print a brief help message and exits. +=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 @@ -103,48 +105,51 @@ Configuration is read from the first file found among: =item * the one specified by B<--config> option on command-line -=item * $HOME/.youri/upload.conf - -=item * @sysconfdir@/youri/upload.conf +=item * $HOME/.youri/submit.conf + +=item * @sysconfdir@/youri/submit.conf =back -All additional configuration files specified by B<includes> directive are then -processed. Then command line options. Any directive overrides prior definition. +The configuration file should be a YAML-format files, with the following +mandatory top-level directives: =over -=item B<includes> I<files> +=item B<repository> -Uses space-separated list I<files> as a list of additional configuration files. +The definition of repository plugin to be used. -=item B<repository> I<id> +=item B<targets> -Declares a repository object with identity I<id>. +The list of available submission targets, each one being composed from the +following keys: -=item B<targets> I<ids> +=over -Declares a list of upload target objects with identity taken in space-separated list I<ids>. +=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 -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. +=item B<checks> + +The list of check plugin definitions, indexed by their identity. -Example: +=item B<actions> + +The list of action plugin definitions, indexed by their identity. - objects = foo - - [foo] - class = Foo::Bar - key1 = value1 - key2 = value2 +=back =head1 SEE ALSO -Youri::Config, for configuration file format. +Youri::Config, for additional details about configuration file format. Each used plugin man page, for available options. @@ -164,90 +169,136 @@ use Youri::Utils; use Pod::Usage; my $config = Youri::Config->new( - command_spec => [ - 'config=s', - 'skip-pre=s@', - 'skip-check=s@', - 'skip-action=s@', - 'skip-post=s@', - 'skip-reject=s@', - 'list-targets!', - 'list-checks=s', - 'list-actions=s', - 'define=s%', - 'help|h!', - 'test|t!', - 'server|s', - 'verbose|v!' - ], - file_spec => [ - 'includes=s', - 'targets=s', - 'repository=s', - ], - directories => [ '@sysconfdir@', "$ENV{HOME}/.youri" ], - file_name => 'upload.conf', - caller => $0, + 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', ); -# compute available targets first -my %targets = map { $_ => 1 } split(/\s+/, $config->get('targets')); - -if ($config->get('list-targets')) { - print join(' ', keys %targets) . "\n"; - exit 0; -} elsif ($config->get('list-checks')) { - my %target = get_target_config($config->get('list-checks')); - print join(' ', @{$target{checks}}) . "\n"; - exit 0; -} elsif ($config->get('list-actions')) { - my %target = get_target_config($config->get('list-actions')); - print join(' ', @{$target{actions}}) . "\n"; +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; # convenient global flags -my $test = $config->get('test'); -my $verbose = $config->get('verbose'); +my $test = $config->get_arg('test'); +my $verbose = $config->get_arg('verbose'); # check target my $target = shift @ARGV; -my %target = get_target_config($target); +my $target_conf = $config->get_param('targets')->{$target}; # create repository my $repository; -my $repository_id = $config->get('repository'); -die "No repository declared" unless $repository_id; -print "Creating repository $repository_id\n" if $verbose; +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', - test => $test, - verbose => $verbose > 0 ? $verbose - 1 : 0, - targets => [ keys %targets ], - $config->get_section($repository_id) + $repository_conf, + { + test => $test, + verbose => $verbose > 0 ? $verbose - 1 : 0, + targets => [ keys %{$config->get_param('targets')} ], + } ); }; -die "Failed to create repository $repository_id: $@\n" if $@; +die "Failed to create repository: $@\n" if $@; # perfrom pre action my %skip_pres = map { $_ => 1 } @{$config->get('skip-pre')}; my $pre_packages = []; my $ok = 1; -foreach my $id (@{$target{pre}}) { +foreach my $id (@{$target_conf->{pre}}) { next if $skip_pres{$id}; print "Creating pre $id\n" if $verbose; my $pre; eval { $pre = create_instance( 'Youri::Upload::Pre', - id => $id, - test => $test, - verbose => $verbose > 0 ? $verbose - 1 : 0, - $config->get_section($id) + { + id => $id, + test => $test, + verbose => $verbose > 0 ? $verbose - 1 : 0, + } ); }; if ($@) { @@ -272,29 +323,42 @@ foreach my $group ((map { [ { section => "", file => $_ } ] } @ARGV), @$pre_pac @packages, create_instance( 'Youri::Package', - class => $repository->get_package_class(), - file => $opt->{file}, - %$opt + { + class => $repository->get_package_class(), + }, + { + file => $opt->{file}, + %$opt + }, ) ); } @packages or next; # check all packages pass all tests - my %skip_checks = map { $_ => 1 } @{$config->get('skip-check')}; - my $ok = 1; + 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{checks}}) { + foreach my $id (@{$target_conf->{checks}}) { next if $skip_checks{$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::Upload::Check', - id => $id, - test => $test, - verbose => $verbose > 0 ? $verbose - 1 : 0, - $config->get_section($id) + $check_conf, + { + id => $id, + test => $test, + verbose => $verbose > 0 ? $verbose - 1 : 0, + } ); }; if ($@) { @@ -302,30 +366,40 @@ foreach my $group ((map { [ { section => "", file => $_ } ] } @ARGV), @$pre_pac } else { foreach my $package (@packages) { print "running check $id on package $package\n" if $verbose; - unless ($check->run($package, $repository, $target, $config->get('define'))) { - my $err = $check->get_error(); - print STDERR "Error: $err\n"; - push @error, $err; - $ok = 0 - } + my @errors = $check->run( + $package, + $repository, + $target, + $config->get_arg('define') + ); + push(@{$errors{$package}}, @errors) if @errors; + } + } + if (%errors) { + print "Submission errors, aborting:\n"; + foreach my $package (keys %errors) { + print "- $package:\n"; + foreach my $error (@{$errors{$package}}) { + print " - $error\n"; } - } - } - if (!$ok) { # reject the packages - my %skip_rejects = map { $_ => 1 } @{$config->get('skip-reject')}; - foreach my $id (@{$target{rejects}}) { + 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}; eval { $reject = create_instance( 'Youri::Upload::Reject', - id => $id, - test => $test, - verbose => $verbose > 0 ? $verbose - 1 : 0, - $config->get_section($id) + $reject_conf, + { + id => $id, + test => $test, + verbose => $verbose > 0 ? $verbose - 1 : 0, + } ); }; if ($@) { @@ -334,7 +408,7 @@ foreach my $group ((map { [ { section => "", file => $_ } ] } @ARGV), @$pre_pac foreach my $package (@packages) { print "running reject $id on package $package\n" if $verbose; eval { - $reject->run($package, \@error, $repository, $target, $config->get('define')); + $reject->run($package, \%errors, $repository, $target, $config->get('define')); }; if ($@) { print STDERR "Failed to run action $id on package $package: $@\n"; @@ -346,48 +420,70 @@ foreach my $group ((map { [ { section => "", file => $_ } ] } @ARGV), @$pre_pac } # proceed further - my %skip_actions = map { $_ => 1 } @{$config->get('skip-action')}; - foreach my $id (@{$target{actions}}) { + 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_actions{$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::Upload::Action', - id => $id, - test => $test, - verbose => $verbose > 0 ? $verbose - 1 : 0, - $config->get_section($id) + $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('define')); - }; - if ($@) { - print STDERR "Failed to run action $id on package $package: $@\n"; - } - } - } + 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"; + } + } } } # perfrom post action -my %skip_posts = map { $_ => 1 } @{$config->get('skip-post')}; -foreach my $id (@{$target{post}}) { - next if $skip_posts{$id}; +my $skip_post = $config->get_arg('skip-post'); +my %skip_post = $skip_post ? map { $_ => 1 } @{$skip_post} : (); +foreach my $id (@{$target_conf->{post}}) { + 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 action $id, skipping\n"; + next; + } eval { $post = create_instance( 'Youri::Upload::Post', - id => $id, - test => $test, - verbose => $verbose > 0 ? $verbose - 1 : 0, - $config->get_section($id) + $post_conf, + { + id => $id, + test => $test, + verbose => $verbose > 0 ? $verbose - 1 : 0, + } ); }; if ($@) { @@ -400,11 +496,9 @@ foreach my $id (@{$target{post}}) { } } -sub get_target_config { - my ($id) = @_; - die "Unavailable target $target" unless $targets{$id}; - - my %target = $config->get_section($id); - die "Undefined target $id" unless %target; - return %target; +if ($config->get('clean')) { + foreach my $package (@packages) { + print "cleaning file $package\n" if $verbose; + unlink $package->as_file(); + } } diff --git a/etc/bash_completion.d/youri-submit b/etc/bash_completion.d/youri-submit new file mode 100644 index 0000000..be2f6e5 --- /dev/null +++ b/etc/bash_completion.d/youri-submit @@ -0,0 +1,60 @@ +# youri-submit completion +# $Id$ + +_youri-submit() +{ + + local cur prev config + + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + --config) + _filedir + return 0 + ;; + --list) + COMPREPLY=( $( compgen -W 'targets checks actions' -- $cur ) ) + return 0 + ;; + --help) + COMPREPLY=( $( compgen -W 'repository check action' -- $cur ) ) + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--define --clean -l --list -h --help -t \ + --test -v --verbose' -- $cur ) ) + # add dangereous option for main command + if [[ ${COMP_WORDS[0]} == youri-submit ]]; then + COMPREPLY=( $( compgen -W '${COMPREPLY[@]} --config --skip-check \ + --skip-action' -- $cur ) ) + fi + else + _count_args + case $args in + 1) + _find_config + COMPREPLY=( $( compgen -W '$( youri-submit $config --list targets )' -- $cur ) ) + ;; + *) + _filedir + ;; + esac + fi + +} +complete -F _youri-submit youri-submit youri-submit-restricted youri-submit-proxy + +_find_config() +{ + for (( i=1; i < COMP_CWORD; i++ )); do + if [[ "${COMP_WORDS[i]}" == --config ]]; then + config="--config ${COMP_WORDS[i+1]}" + break + fi + done +} diff --git a/etc/submit.conf b/etc/submit.conf new file mode 100644 index 0000000..db12918 --- /dev/null +++ b/etc/submit.conf @@ -0,0 +1,134 @@ +# youri-submit sample configuration file +# $Id$ + +# helper variables +home: /home/user + +# repository definition +repository: + class: Youri::Repository::PLF + options: + install_root: ${home}/ftp/mandriva + version_root: ${home}/cvs + archive_root: ${home}/backup/mandriva + noarch: i586 + +# targets definitions +targets: + cooker: + checks: + - tag + - recency + - history + actions: + - sign + - install + - link + - archive + - clean + - bugzilla + - cvs + - mail + - rss + + 2006.0: + checks: + - type + - tag + - recency + - history + - precedence + actions: + - sign + - install + - link + - archive + - clean + +# checks definitions +checks: + tag: + class: Youri::Submit::Check::Tag + options: + tags: + release: 'plf$' + packager: '<\w+@zarb\.org>$' + distribution: '^Mandriva Linux$' + vendor: '^Penguin Liberation Front$' + + recency: + class: Youri::Submit::Check::Recency + + history: + class: Youri::Submit::Check::History + + precedence: + class: Youri::Submit::Check::Precedence + options: + target: cooker + + type: + class: Youri::Submit::Check::Type + type: binary + +# actions definitions +actions: + sign: + class: Youri::Submit::Action::Sign + options: + name: plf@zarb.org + path: ${home}/.gnupg + passphrase: s3kr3t + + install: + class: Youri::Submit::Action::Install + + link: + class: Youri::Submit::Action::Link + + archive: + class: Youri::Submit::Action::Archive + + clean: + class: Youri::Submit::Action::Clean + + mail: + class: Youri::Submit::Action::Mail + options: + mta: /usr/sbin/sendmail + to: plf-announce@zarb.org + reply_to: plf-discuss@zarb.org + from: plf@zarb.org + prefix: RPM + cc: + hot-base: david@dindinx.org bellamy@neverland.net + dcgui: mathen@ketelhot.de + dclib: mathen@ketelhot.de + Video-DVDRip: dvdrip-users@exit1.org + hackVideo-DVDRip: dvdrip-users@exit1.org + goosnes: tak@bard.sytes.net + avidemux: fixounet@free.fr + vobcopy: robos@muon.de + drip: drip-devel@lists.sourceforge.net + libdscaler: vektor@dumbterm.net + xawdecode: pingus77@ifrance.com + + rss: + class: Youri::Submit::Action::RSS + options: + file: ${home}/www/changelog.rss + title: PLF packages updates + link: http://plf.zarb.org/ + description: ChangeLog for PLF packages + + cvs: + class: Youri::Submit::Action::CVS + + bugzilla: + class: Youri::Submit::Action::Bugzilla + options: + host: localhost + base: plf_bugs + user: plf + pass: s3kr3t + contact: plf@zarb.org diff --git a/lib/Youri/Submit/Plugin.pm b/lib/Youri/Submit/Plugin.pm new file mode 100644 index 0000000..17c3c32 --- /dev/null +++ b/lib/Youri/Submit/Plugin.pm @@ -0,0 +1,93 @@ +# $Id: Base.pm 631 2006-01-26 22:22:23Z guillomovitch $ +package Youri::Submit::Plugin; + +=head1 NAME + +Youri::Submit::Plugin - Abstract youri-submit plugin + +=head1 DESCRIPTION + +This abstract class defines youri-submit plugin interface. + +=cut + +use warnings; +use strict; +use Carp; + +=head1 CLASS METHODS + +=head2 new(%args) + +Creates and returns a new Youri::Submit::Plugin object. + +No generic parameters (subclasses may define additional ones). + +Warning: do not call directly, call subclass constructor instead. + +=cut + +sub new { + my $class = shift; + croak "Abstract class" if $class eq __PACKAGE__; + + my %options = ( + id => '', # object id + test => 0, # test mode + verbose => 0, # verbose mode + @_ + ); + + my $self = bless { + _id => $options{id}, + _test => $options{test}, + _verbose => $options{verbose}, + }, $class; + + $self->_init(%options); + + return $self; +} + +sub _init { + # do nothing +} + +=head1 INSTANCE METHODS + +=head2 get_id() + +Returns plugin identity. + +=cut + +sub get_id { + my ($self) = @_; + croak "Not a class method" unless ref $self; + + return $self->{_id}; +} + +=head2 run($package, $repository, $target, $define) + +Execute action on given L<Youri::Package> object. + +=head1 SUBCLASSING + +The following methods have to be implemented: + +=over + +=item run + +=back + +=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 + +1; diff --git a/t/00distribution.t b/t/00distribution.t new file mode 100755 index 0000000..8a4b2c4 --- /dev/null +++ b/t/00distribution.t @@ -0,0 +1,15 @@ +#!/usr/bin/perl +# $Id$ + +use Test::More; + +BEGIN { + eval { + require Test::Distribution; + }; + if($@) { + plan skip_all => 'Test::Distribution not installed'; + } else { + import Test::Distribution only => [ qw/use pod description/ ]; + } +} |