App-migrate

 view release on metacpan or  search on metacpan

README  view on Meta::CPAN

    need complex operations to upgrade/downgrade between these versions.
    For example, to migrate source code you can use VCS like Git or
    Mercurial, but they didn't support empty directories, file permissions
    (except executable), non-plain file types (FIFO, UNIX socket, etc.),
    xattr, ACL, configuration files which must differ on each site, and
    databases. So, if you need to migrate anything isn't supported by VCS -
    you can try this module/tool.

    Sometimes it isn't possible to really downgrade because some data was
    lost while upgrade - to handle these situations you should provide a
    ways to create complete backup of your project and restore any
    project's version from these backups while downgrade (of course,
    restoring backups will result in losing new changes, so whenever
    possible it's better to do some extra work to provide a way to
    downgrade without losing any data).

 Example

    Here is example how to run migration from version '1.1.8' to '1.2.3' of
    some project which uses even minor versions '1.0.x' and '1.2.x' for
    stable releases and odd minor versions '1.1.x' for unstable releases.
    The nearest common version between '1.1.8' and '1.2.3' is '1.0.42',
    which was the parent for both '1.1.x' and '1.2.x' branches, so we need
    to downgrade project from '1.1.8' to '1.0.42' first, and then upgrade
    from '1.0.42' to '1.2.3'. You'll need two *.migrate files, one which
    describe migrations from '1.0.42' (or earlier version) to '1.1.8', and
    another with migrations from '1.0.42' (or earlier) to '1.2.3'. For
    brevity let's not make any backups while migration.

        my $migrate = App::migrate
            ->new
            ->load('1.1.8.migrate')
            ->load('1.2.3.migrate');
        $migrate
            ->on(BACKUP => sub {})
            ->run( $migrate->find_paths('1.1.8' => '1.2.3') );

INTERFACE

README  view on Meta::CPAN

    Set handler for given event.

    All handlers will be called only by "run"; they will get single
    parameter - step HASHREF (BACKUP handler will get step in same format
    as RESTORE), see "get_steps" for details of that HASHREF contents. Also
    these handlers may use $ENV{MIGRATE_PREV_VERSION} and
    $ENV{MIGRATE_NEXT_VERSION} - see "run" for more details.

    'BACKUP' event

      Handler will be executed when project backup should be created:
      before starting any new migration, except next one after RESTORE.

      If handler throws then 'error' handler will be executed.

      Default handler will throw (because it doesn't know how to backup
      your project).

      NOTE: If you'll use handler which doesn't really create and keep
      backups for all versions then it will be impossible to do RESTORE
      operation.

    'RESTORE' event

      Handler will be executed when project should be restored from backup:
      when downgrading between versions which contain RESTORE operation or
      when migration fails.

      If handler throws then 'error' handler will be executed.

      Default handler will throw (because it doesn't know how to restore
      your project).

    'VERSION' event

README  view on Meta::CPAN


      Handler will be executed when one of commands executed while
      migration fails or when BACKUP, RESTORE or VERSION handlers throw.

      If handler throws then try to restore version-before-migration
      (without calling error handler again if it throws too).

      Default handler will run $SHELL (to let you manually fix errors) and
      throw if you $SHELL exit status != 0 (to let you choose what to do
      next - continue migration if you fixed error or interrupt migration
      to restore version-before-migration from backup).

 run

        $migrate->run( \@versions );

    Will use "get_steps" to get steps for path given in @versions and
    execute them in order. Will also call handlers as described in "on".

    Before executing each step will set $ENV{MIGRATE_PREV_VERSION} to
    current version (which it will migrate from) and

README  view on Meta::CPAN


      Thus custom file format is needed.

      * Make it easier to manually analyse is 'downgrade' operation looks
      correct for corresponding 'upgrade' operation.

      Thus related 'upgrade' and 'downgrade' operations must go one right
      after another.

      * Make it obvious some version can't be downgraded and have to be
      restored from backup.

      Thus RESTORE operation is named in upper case.

      * Given all these requirements try to make it simple and obvious to
      define migrate operations, without needs to write downgrade code for
      typical cases.

      Thus it's possible to define macro to turn combination of
      upgrade/downgrade operations into one user-defined operation (no
      worries here: these macro doesn't support recursion, it isn't

README  view on Meta::CPAN

        VERSION 0.0.0
        # To upgrade from 0.0.0 to 0.1.0 we need to create new empty file and
        # empty directory.
        upgrade     touch   empty_file
        downgrade   rm      empty_file
        upgrade     mkdir   empty_dir
        downgrade   rmdir   empty_dir
        VERSION 0.1.0
        # To upgrade from 0.1.0 to 0.2.0 we need to drop old database. This
        # change can't be undone, so only way to downgrade from 0.2.0 is to
        # restore 0.1.0 from backup.
        upgrade     rm      useless.db
        RESTORE
        VERSION 0.2.0
        # To upgrade from 0.2.0 to 1.0.0 we need to run several commands,
        # and after downgrading we need to kill some background service.
        before_upgrade
          patch    <0.2.0.patch >/dev/null
          chmod +x some_daemon
        downgrade
          patch -R <0.2.0.patch >/dev/null

README  view on Meta::CPAN

    While executing any commands two environment variables will be set:
    $MIGRATE_PREV_VERSION and $MIGRATE_NEXT_VERSION (first is always
    version we're migrating from, and second is always version we're
    migrating to - i.e. while downgrading $MIGRATE_NEXT_VERSION will be
    lower/older version than $MIGRATE_PREV_VERSION)

    All executed commands must complete without error, otherwise emergency
    shell will be started and user should either fix the error and exit
    from shell to continue migration, or exit 1 from shell to interrupt
    migration and restore previous-before-this-migration version from
    backup.

 Supported operations

  VERSION

    Must have exactly one param (version number). Some symbols are not
    allowed in version numbers: special (0x00-0x1F,0x7F), both slash, all
    three quotes, ?, * and space.

    Multiline param not supported.

README  view on Meta::CPAN


  RESTORE

    Doesn't support any params, neither usual nor multiline.

    Can be used only after 'before_upgrade' or 'upgrade' operations.

    When one or more 'RESTORE' operations are used between some 'VERSION'
    operations then all 'downgrade' and 'after_downgrade' operations
    between same 'VERSION' operations will be ignored and on downgrading
    previous version will be restored from backup.

  DEFINE

    This operation must have only one non-multiline param - name of defined
    macro. This name must not be same as one of existing operation names,
    both documented here or created by one of previous 'DEFINE' or
    'DEFINE2' or 'DEFINE4' operations.

    Next operation must be one of 'before_upgrade', 'upgrade', 'downgrade'
    or 'after_downgrade' - it will be substituted in place of all next

lib/App/migrate.pm  view on Meta::CPAN

$SIG{INT} = $SIG{INT}     // sub { exit 130 }; ## no critic (RequireLocalizedPunctuationVars ProhibitMagicNumbers)
$SIG{QUIT}= $SIG{QUIT}    // sub { exit 131 }; ## no critic (RequireLocalizedPunctuationVars ProhibitMagicNumbers)
$SIG{TERM}= $SIG{TERM}    // sub { exit 143 }; ## no critic (RequireLocalizedPunctuationVars ProhibitMagicNumbers)


sub new {
    my ($class) = @_;
    my $self = bless {
        paths   => {},  # {prev_version}{next_version} = \@steps
        on      => {
            BACKUP  => \&_on_backup,
            RESTORE => \&_on_restore,
            VERSION => \&_on_version,
            error   => \&_on_error,
        },
    }, ref $class || $class;
    return $self;
}

sub find_paths {
    my ($self, $from, $to) = @_;

lib/App/migrate.pm  view on Meta::CPAN

}

sub _find_paths {
    my ($self, $to, @from) = @_;
    my $p = $self->{paths}{ $from[-1] } || {};
    return [@from, $to] if $p->{$to};
    my %seen = map {$_=>1} @from;
    return map {$self->_find_paths($to,@from,$_)} grep {!$seen{$_}} keys %{$p};
}

sub _on_backup {
    croak 'You need to define how to make BACKUP';
}

sub _on_restore {
    croak 'You need to define how to RESTORE from backup';
}

sub _on_version {
    # do nothing
}

sub _on_error {
    warn <<'ERROR';

YOU NEED TO MANUALLY FIX THIS ISSUE RIGHT NOW
When done, use:
   exit        to continue migration
   exit 1      to interrupt migration and RESTORE from backup

ERROR
    system($ENV{SHELL} // '/bin/sh') == 0 or die "Migration interrupted\n";
    return;
}

sub _preprocess { ## no critic (ProhibitExcessComplexity)
    my @tokens = @_;
    my @op;
    my %macro;

lib/App/migrate.pm  view on Meta::CPAN

complex operations to upgrade/downgrade between these versions.
For example, to migrate source code you can use VCS like Git or Mercurial,
but they didn't support empty directories, file permissions (except
executable), non-plain file types (FIFO, UNIX socket, etc.), xattr, ACL,
configuration files which must differ on each site, and databases. So, if
you need to migrate anything isn't supported by VCS - you can try this
module/tool.

Sometimes it isn't possible to really downgrade because some data was lost
while upgrade - to handle these situations you should provide a ways to
create complete backup of your project and restore any project's version
from these backups while downgrade (of course, restoring backups will
result in losing new changes, so whenever possible it's better to do some
extra work to provide a way to downgrade without losing any data).

=head2 Example

Here is example how to run migration from version '1.1.8' to '1.2.3' of
some project which uses even minor versions '1.0.x' and '1.2.x' for stable
releases and odd minor versions '1.1.x' for unstable releases. The nearest
common version between '1.1.8' and '1.2.3' is '1.0.42', which was the
parent for both '1.1.x' and '1.2.x' branches, so we need to downgrade
project from '1.1.8' to '1.0.42' first, and then upgrade from '1.0.42' to
'1.2.3'. You'll need two C<*.migrate> files, one which describe migrations
from '1.0.42' (or earlier version) to '1.1.8', and another with migrations
from '1.0.42' (or earlier) to '1.2.3'. For brevity let's not make any
backups while migration.

    my $migrate = App::migrate
        ->new
        ->load('1.1.8.migrate')
        ->load('1.2.3.migrate');
    $migrate
        ->on(BACKUP => sub {})
        ->run( $migrate->find_paths('1.1.8' => '1.2.3') );


lib/App/migrate.pm  view on Meta::CPAN

All handlers will be called only by L</"run">; they will get single
parameter - step HASHREF (BACKUP handler will get step in same format as
RESTORE), see L</"get_steps"> for details of that HASHREF contents.
Also these handlers may use C<$ENV{MIGRATE_PREV_VERSION}> and
C<$ENV{MIGRATE_NEXT_VERSION}> - see L</"run"> for more details.

=over

=item 'BACKUP' event

Handler will be executed when project backup should be created: before
starting any new migration, except next one after RESTORE.

If handler throws then 'error' handler will be executed.

Default handler will throw (because it doesn't know how to backup your
project).

NOTE: If you'll use handler which doesn't really create and keep backups
for all versions then it will be impossible to do RESTORE operation.

=item 'RESTORE' event

Handler will be executed when project should be restored from backup: when
downgrading between versions which contain RESTORE operation or when
migration fails.

If handler throws then 'error' handler will be executed.

Default handler will throw (because it doesn't know how to restore your
project).

=item 'VERSION' event

lib/App/migrate.pm  view on Meta::CPAN


Handler will be executed when one of commands executed while migration
fails or when BACKUP, RESTORE or VERSION handlers throw.

If handler throws then try to restore version-before-migration (without
calling error handler again if it throws too).

Default handler will run $SHELL (to let you manually fix errors) and throw
if you $SHELL exit status != 0 (to let you choose what to do next -
continue migration if you fixed error or interrupt migration to restore
version-before-migration from backup).

=back

=head2 run

    $migrate->run( \@versions );

Will use L</"get_steps"> to get steps for path given in C<@versions> and
execute them in order. Will also call handlers as described in L</"on">.

lib/App/migrate.pm  view on Meta::CPAN


Make it easier to manually analyse is 'downgrade' operation looks correct
for corresponding 'upgrade' operation.

I<Thus related 'upgrade' and 'downgrade' operations must go one right
after another.>

=item *

Make it obvious some version can't be downgraded and have to be restored
from backup.

I<Thus RESTORE operation is named in upper case.>

=item *

Given all these requirements try to make it simple and obvious to define
migrate operations, without needs to write downgrade code for typical
cases.

I<Thus it's possible to define macro to turn combination of

lib/App/migrate.pm  view on Meta::CPAN

    VERSION 0.0.0
    # To upgrade from 0.0.0 to 0.1.0 we need to create new empty file and
    # empty directory.
    upgrade     touch   empty_file
    downgrade   rm      empty_file
    upgrade     mkdir   empty_dir
    downgrade   rmdir   empty_dir
    VERSION 0.1.0
    # To upgrade from 0.1.0 to 0.2.0 we need to drop old database. This
    # change can't be undone, so only way to downgrade from 0.2.0 is to
    # restore 0.1.0 from backup.
    upgrade     rm      useless.db
    RESTORE
    VERSION 0.2.0
    # To upgrade from 0.2.0 to 1.0.0 we need to run several commands,
    # and after downgrading we need to kill some background service.
    before_upgrade
      patch    <0.2.0.patch >/dev/null
      chmod +x some_daemon
    downgrade
      patch -R <0.2.0.patch >/dev/null

lib/App/migrate.pm  view on Meta::CPAN


While executing any commands two environment variables will be set:
C<$MIGRATE_PREV_VERSION> and C<$MIGRATE_NEXT_VERSION> (first is always
version we're migrating from, and second is always version we're migrating
to - i.e. while downgrading C<$MIGRATE_NEXT_VERSION> will be lower/older
version than C<$MIGRATE_PREV_VERSION>)

All executed commands must complete without error, otherwise emergency
shell will be started and user should either fix the error and C<exit>
from shell to continue migration, or C<exit 1> from shell to interrupt
migration and restore previous-before-this-migration version from backup.

=head2 Supported operations

=head3 VERSION

Must have exactly one param (version number). Some symbols are not allowed
in version numbers: special (0x00-0x1F,0x7F), both slash, all three
quotes, ?, * and space.

Multiline param not supported.

lib/App/migrate.pm  view on Meta::CPAN


=head3 RESTORE

Doesn't support any params, neither usual nor multiline.

Can be used only after 'before_upgrade' or 'upgrade' operations.

When one or more 'RESTORE' operations are used between some 'VERSION'
operations then all 'downgrade' and 'after_downgrade' operations between
same 'VERSION' operations will be ignored and on downgrading previous
version will be restored from backup.

=head3 DEFINE

This operation must have only one non-multiline param - name of defined
macro. This name must not be same as one of existing operation names, both
documented here or created by one of previous 'DEFINE' or 'DEFINE2' or
'DEFINE4' operations.

Next operation must be one of 'before_upgrade', 'upgrade', 'downgrade' or
'after_downgrade' - it will be substituted in place of all next operations

script/migrate  view on Meta::CPAN

        print <<'EOUSAGE';
migrate [-B <cmd>] [-R <cmd>] [-V <cmd>] [[-f <file>] ...] <from_ver> <to_ver>
migrate [-B <cmd>] [-R <cmd>] [-V <cmd>] [[-f <file>] ...] -p <ver1> <ver2> ...
migrate -c [<file>]
migrate -h|--help
EOUSAGE
        exit 1;
}

sub main {
        my $b_cmd       = 'echo No backup command defined, skip backup';
        my $r_cmd       = 'false';
        my $v_cmd       = 'echo Current version is $MIGRATE_NEXT_VERSION';
        my @files       = ();
        my $is_path     = 0;
        my $is_check    = 0;
        GetOptionsFromArray(\@_,
                'B=s'   => \$b_cmd,
                'R=s'   => \$r_cmd,
                'V=s'   => \$v_cmd,
                'f=s@'  => \@files,

script/migrate  view on Meta::CPAN

  migrate -h|--help


=head1 DESCRIPTION

To use this tool to migrate your project between any versions you should
provide:

  - one or more *.migrate files, which will define known versions of your
    project and commands needed to migrate between these versions
  - command which will create backup of your project (and keep these backups
    to make it possible to restore any of previous project versions)
  - command which will restore project from backup of any version (if such
    backup exists)
  - (optional) command which will record somewhere current version of
    project after each successful migration

See L<App::migrate> for details.


=head1 OPTIONS

=over

t/example.t  view on Meta::CPAN



my $migrate = App::migrate->new;
my $file    = tempfile('migrate.XXXXXX');

my $proj    = tempdir('migrate.project.XXXXXX');
my $guard   = bless {};
sub DESTROY { chdir q{/} }
chdir $proj or die "chdir($proj): $!";

my (@backup, @restore);
$migrate->on(BACKUP  => sub { push @backup,  shift->{version} });
$migrate->on(RESTORE => sub { push @restore, shift->{version} });
$migrate->on(error   => sub { diag 'on ERROR was called'      });

ok $migrate, 'new';

# Make sure example from documentation actually works
$file->spew_utf8(<<'MIGRATE');
VERSION 0.0.0
# To upgrade from 0.0.0 to 0.1.0 we need to create new empty file and
# empty directory.
upgrade     touch   empty_file
downgrade   rm      empty_file
upgrade     mkdir   empty_dir
downgrade   rmdir   empty_dir
VERSION 0.1.0
# To upgrade from 0.1.0 to 0.2.0 we need to drop old database. This
# change can't be undone, so only way to downgrade from 0.2.0 is to
# restore 0.1.0 from backup.
upgrade     rm      useless.db
RESTORE
VERSION 0.2.0
# To upgrade from 0.2.0 to 1.0.0 we need to run several commands,
# and after downgrading we need to kill some background service.
before_upgrade
  patch -E   <0.2.0.patch >/dev/null
  rm -f *.orig
  chmod +x some_daemon
downgrade



( run in 1.654 second using v1.01-cache-2.11-cpan-49f99fa48dc )