From e6db5bc7177e60e72ebe7b993b808dfa6d222af5 Mon Sep 17 00:00:00 2001 From: Olli-Antti Kivilahti Date: Mon, 14 Sep 2015 16:29:20 +0300 Subject: [PATCH] Bug 14698 - AtomicUpdater - Followup, add single atomicupdate-script execution. Add dry-run mode. --- Koha/AtomicUpdater.pm | 46 ++++++++++++++++++++++++--------- installer/data/mysql/atomicupdate.pl | 27 +++++++++++++++++--- t/db_dependent/Koha/AtomicUpdater.t | 49 ++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 15 deletions(-) diff --git a/Koha/AtomicUpdater.pm b/Koha/AtomicUpdater.pm index 919d956..658de3e 100644 --- a/Koha/AtomicUpdater.pm +++ b/Koha/AtomicUpdater.pm @@ -58,6 +58,7 @@ sub new { $self->{verbose} = $params->{verbose} || $self->{verbose} || 0; $self->{scriptDir} = $params->{scriptDir} || $self->{scriptDir} || C4::Context->config('intranetdir') . '/installer/data/mysql/atomicupdate/'; $self->{gitRepo} = $params->{gitRepo} || $self->{gitRepo} || $ENV{KOHA_PATH}; + $self->{dryRun} = $params->{dryRun} || $self->{dryRun} || 0; return $self; } @@ -166,19 +167,8 @@ sub applyAtomicUpdates { my $atomicUpdate = $atomicUpdates->{$issueId}; next unless $atomicUpdate; #Not each ordered Git commit necessarily have a atomicupdate-script. - my $filename = $atomicUpdate->filename; - print "Applying file '$filename'\n" if $self->{verbose} > 2; - - if ( $filename =~ /\.sql$/ ) { - my $installer = C4::Installer->new(); - my $rv = $installer->load_sql( $self->{scriptDir}.'/'.$filename ) ? 0 : 1; - } elsif ( $filename =~ /\.(perl|pl)$/ ) { - do $self->{scriptDir}.'/'.$filename; - } - - $atomicUpdate->store(); + $self->applyAtomicUpdate($atomicUpdate); $appliedUpdates{$issueId} = $atomicUpdate; - print "File '$filename' applied\n" if $self->{verbose} > 2; } #Check that we have actually applied all the updates. @@ -191,6 +181,38 @@ sub applyAtomicUpdates { return \%appliedUpdates; } +sub applyAtomicUpdate { + my ($self, $atomicUpdate) = @_; + #Validate params + unless ($atomicUpdate) { + Koha::Exception::BadParameter->throw(error => __PACKAGE__."->applyAtomicUpdate($atomicUpdate):> Parameter must be a Koha::AtomicUpdate-object or a path to a valid atomicupdates-script!"); + } + if ($atomicUpdate && ref($atomicUpdate) eq '') { #We have a scalar, presumably a filepath to atomicUpdate-script. + $atomicUpdate = Koha::AtomicUpdate->new({filename => $atomicUpdate}); + } + $atomicUpdate = Koha::AtomicUpdater->cast($atomicUpdate); + + my $filename = $atomicUpdate->filename; + print "Applying file '$filename'\n" if $self->{verbose} > 2; + + unless ($self->{dryRun}) { + if ( $filename =~ /\.sql$/ ) { + my $installer = C4::Installer->new(); + my $rv = $installer->load_sql( $self->{scriptDir}.'/'.$filename ) ? 0 : 1; + } elsif ( $filename =~ /\.(perl|pl)$/ ) { + my $fileAndPath = $self->{scriptDir}.'/'.$filename; + unless (my $return = do $fileAndPath ) { + warn "couldn't parse $fileAndPath: $@\n" if $@; + warn "couldn't do $fileAndPath: $!\n" unless defined $return; + warn "couldn't run $fileAndPath\n" unless $return; + } + } + $atomicUpdate->store(); + } + + print "File '$filename' applied\n" if $self->{verbose} > 2; +} + =head _getValidAtomicUpdateScripts @RETURNS HASHRef of Koha::AtomicUpdate-objects, of all the files diff --git a/installer/data/mysql/atomicupdate.pl b/installer/data/mysql/atomicupdate.pl index a53cf21..21cc882 100644 --- a/installer/data/mysql/atomicupdate.pl +++ b/installer/data/mysql/atomicupdate.pl @@ -19,7 +19,7 @@ # use Modern::Perl; -use Getopt::Long; +use Getopt::Long qw(:config no_ignore_case); use C4::Context; @@ -29,22 +29,26 @@ my $verbose = 0; my $help = 0; my $apply = 0; my $remove = ''; +my $dryRun = 0; my $insert = ''; my $list = 0; my $pending = 0; my $directory = ''; my $git = ''; +my $single = ''; GetOptions( 'v|verbose:i' => \$verbose, 'h|help' => \$help, 'a|apply' => \$apply, + 'D|dry-run' => \$dryRun, 'd|directory:s' => \$directory, 'r|remove:s' => \$remove, 'i|insert:s' => \$insert, 'l|list' => \$list, 'p|pending' => \$pending, 'g|git:s' => \$git, + 's|single:s' => \$single, ); my $usage = << 'ENDUSAGE'; @@ -58,21 +62,36 @@ applied. Also acts as a gateway to CRUD the koha.database_updates-table. -v --verbose Integer, 1 is not so verbose, 3 is maximally verbose. + + -D --dry-run Flag, Run the script but don't execute any atomicupdates. + You should use --verbose 3 to see what is happening. + -h --help Flag, This nice help! + -a --apply Flag, Apply all the pending atomicupdates from the atomicupdates-directory. + -d --directory Path, From which directory to look for atomicupdate-scripts. Defaults to '$KOHA_PATH/installer/data/mysql/atomicupdate/' + + -s --single Path, execute a single atomicupdate-script. + eg. atomicupdate/Bug01243-SingleFeature.pl + -r --remove String, Remove the upgrade entry from koha.database_updates eg. --remove "Bug71337" + -i --insert Path, Add an upgrade log entry for the given atomicupdate-file. Useful to revert an accidental --remove -operation or for - testing. + testing. Does not execute the update script, simply adds + the log entry. eg. -i installer/data/mysql/atomicupdate/Bug5453-Example.pl + -l --list Flag, List all entries in the koha.database_updates-table. This typically means all applied atomicupdates. + -p --pending Flag, List all pending atomicupdates from the atomicupdates-directory. + -g --git Path, Build the update order from the Git repository given, or default to the Git repository in $KOHA_PATH. Eg. --git 1, to build with default values, or @@ -126,7 +145,9 @@ if ( $help ) { my $atomicupdater = Koha::AtomicUpdater->new({verbose => $verbose, scriptDir => $directory, - gitRepo => (length($git) == 1) ? '' : $git}); + gitRepo => (length($git) == 1) ? '' : $git, + dryRun => $dryRun,} + ,); if ($git) { $atomicupdater->buildUpdateOrderFromGit(10000); diff --git a/t/db_dependent/Koha/AtomicUpdater.t b/t/db_dependent/Koha/AtomicUpdater.t index 381f379..cfd5ff1 100644 --- a/t/db_dependent/Koha/AtomicUpdater.t +++ b/t/db_dependent/Koha/AtomicUpdater.t @@ -45,6 +45,7 @@ my $atomicupdates = t::lib::TestObjects::AtomicUpdateFactory->createTestGroup([ package Koha::AtomicUpdater; sub _getGitCommits { #instead of requiring a Git repository, we just mock the input. return [#Newest commit + '2e8a39762b506738195f21c8ff67e4e7bfe6dbba Bug_01243-SingleUpdate', '2e8a39762b506738195f21c8ff67e4e7bfe6d7ab #:-55 : Fiftyfive', '2e8a39762b506738195f21c8ff67e4e7bfe6d7ab #54 - KohaCon in Finland next year', 'b447b595acacb0c4823582acf9d8a08902118e59 #53 - Place to be.pl', @@ -270,5 +271,53 @@ sub listPendingAtomicupdates { t::lib::TestObjects::AtomicUpdateFactory->tearDownTestContext($subtestContext); } +subtest "Apply single atomicupdate from file" => \&applySingleAtomicUpdateFromFile; +sub applySingleAtomicUpdateFromFile { + my $subtestContext = {}; + eval { + my $files = t::lib::TestObjects::FileFactory->createTestGroup([ + { filepath => 'atomicupdate/', + filename => 'Bug_01243-SingleUpdate.pl', + content => '$ENV{ATOMICUPDATE_TESTS_2} = 10;',}, + ], + undef, $subtestContext, $testContext); + ### Try first as a dry-run ### + my $atomicUpdater = Koha::AtomicUpdater->new({ + scriptDir => $files->{'Bug_01243-SingleUpdate.pl'}->dirname(), + dryRun => 1, + }); + + $atomicUpdater->applyAtomicUpdate($files->{'Bug_01243-SingleUpdate.pl'}->stringify); + my $atomicUpdate = $atomicUpdater->find({issue_id => 'Bug01243'}); + + ok(not($atomicUpdate), + "--dry-run doesn't add anything"); + is($ENV{ATOMICUPDATE_TESTS_2}, + undef, + "--dry-run doesn't execute anything"); + + ### Make a change! ### + $atomicUpdater = Koha::AtomicUpdater->new({ + scriptDir => $files->{'Bug_01243-SingleUpdate.pl'}->dirname(), + }); + + $atomicUpdater->applyAtomicUpdate($files->{'Bug_01243-SingleUpdate.pl'}->stringify); + $atomicUpdate = $atomicUpdater->find({issue_id => 'Bug01243'}); + t::lib::TestObjects::AtomicUpdateFactory->addToContext($atomicUpdate, undef, $subtestContext, $testContext); #Keep track of changes + + is($atomicUpdate->filename, + "Bug_01243-SingleUpdate.pl", + "Bug_01243-SingleUpdate.pl added to DB"); + is($ENV{ATOMICUPDATE_TESTS_2}, + 10, + "Bug_01243-SingleUpdate.pl executed"); + + }; + if ($@) { + ok(0, $@); + } + t::lib::TestObjects::ObjectFactory->tearDownTestContext($subtestContext); +} + t::lib::TestObjects::ObjectFactory->tearDownTestContext($testContext); done_testing; \ No newline at end of file -- 1.9.1