App-Sqitch

 view release on metacpan or  search on metacpan

lib/App/Sqitch/Engine.pm  view on Meta::CPAN


has prompt_accept => (
    is      => 'rw',
    isa     => Bool,
    trigger => sub {
        # Deprecation notice added Feb 2023
        warnings::warnif(
            "deprecated",
            "Engine::prompt_accept is deprecated and will be removed in a future release\n"
            . "Use direct arguments to revert() instead",
        );
    }
);

has log_only => (
    is      => 'rw',
    isa     => Bool,
    default => 0,
);

has with_verify => (
    is      => 'rw',
    isa     => Bool,
    default => 0,
);

has max_name_length => (
    is      => 'rw',
    isa     => Int,
    default => 0,
    lazy    => 1,
    default => sub {
        my $plan = shift->plan;
        max map {
            length $_->format_name_with_tags
        } $plan->changes;
    },
);

has plan => (
    is       => 'rw',
    isa      => Plan,
    lazy     => 1,
    default  => sub { shift->target->plan }
);

has _variables => (
    is      => 'rw',
    isa     => HashRef[Str],
    default => sub { {} },
);

# Usually expressed as an integer, but made a number for the purposes of
# shorter test run times.
has lock_timeout => (
    is      => 'rw',
    isa     => Num,
    default => default_lock_timeout,
);

has _locked => (
    is      => 'rw',
    isa     => Bool,
    default => 0,
);

has _no_registry => (
    is      => 'rw',
    isa     => Bool,
    default => 0,
);

sub variables       { %{ shift->_variables }       }
sub set_variables   {    shift->_variables({ @_ }) }
sub clear_variables { %{ shift->_variables } = ()  }

sub default_registry { 'sqitch' }

sub load {
    my ( $class, $p ) = @_;

    # We should have an engine param.
    my $target = $p->{target} or hurl 'Missing "target" parameter to load()';

    # Load the engine class.
    my $ekey = $target->engine_key or hurl engine => __(
        'No engine specified; specify via target or core.engine'
    );

    my $pkg = __PACKAGE__ . '::' . $target->engine_key;
    eval "require $pkg" or hurl engine => __x(
        'Unknown engine: {engine}',
        engine => $ekey,
    );
    return $pkg->new( $p );
}

sub driver { shift->key }

sub key {
    my $class = ref $_[0] || shift;
    hurl engine => __ 'No engine specified; specify via target or core.engine'
        if $class eq __PACKAGE__;
    my $pkg = quotemeta __PACKAGE__;
    $class =~ s/^$pkg\:://;
    return $class;
}

sub name { shift->key }

sub config_vars {
    return (
        target   => 'any',
        registry => 'any',
        client   => 'any'
    );
}

sub use_driver {
    my $self = shift;
    my $driver = $self->driver;

lib/App/Sqitch/Engine.pm  view on Meta::CPAN

            # Begin work and run the revert.
            try {
                # Don't bother displaying the reverting change name.
                # $self->sqitch->info('  - ', $change->format_name_with_tags);
                $self->begin_work($change);
                $self->run_revert($change->revert_file) unless $self->log_only;
            } catch {
                # Oy, the revert failed. Just emit the error.
                $sqitch->vent(eval { $_->message } // $_);
            };
            hurl private => __ 'Deploy failed';
        };
    } finally {
        $self->finish_work($change);
    } catch {
        $self->log_fail_change($change);
        $sqitch->info(__ 'not ok');
        die $_;
    };
}

sub revert_change {
    my ( $self, $change ) = @_;
    my $sqitch = $self->sqitch;
    my $name   = $change->format_name_with_tags;
    $sqitch->info_literal(
        "  - $name ..",
        '.' x ($self->max_name_length - length $name), ' '
    );

    $self->begin_work($change);

    try {
        my $file = $change->revert_file;
        hurl revert => __x(
            'Revert script {file} does not exist',
            file => $file,
        ) unless -e $file;
        $self->run_revert($file) unless $self->log_only;
        try {
            $self->log_revert_change($change);
            $sqitch->info(__ 'ok');
        } catch {
            # Oy, our logging died. Rollback and revert this change.
            $self->sqitch->vent(eval { $_->message } // $_);
            $self->rollback_work($change);
            hurl revert => 'Revert failed';
        };
    } finally {
        $self->finish_work($change);
    } catch {
        $sqitch->info(__ 'not ok');
        die $_;
    };
}

sub lock_destination {
    my $self = shift;

    # Try to acquire the lock without waiting.
    return $self if $self->_locked;
    return $self->_locked(1) if $self->try_lock;

    # Lock not acquired. Tell the user what's happening.
    my $wait = $self->lock_timeout;
    $self->sqitch->info(__x(
        'Blocked by another instance of Sqitch working on {dest}; waiting {secs} seconds...',
        dest => $self->destination,
        secs => $wait,
    ));

    # Try waiting for the lock.
    return $self->_locked(1) if $self->wait_lock;

    # Timed out, so bail.
    hurl engine => __x(
        'Timed out waiting {secs} seconds for another instance of Sqitch to finish work on {dest}',
        dest => $self->destination,
        secs => $wait,
    );
}

sub _timeout {
    my ($self, $code) = @_;
    require Algorithm::Backoff::Exponential;
    my $ab = Algorithm::Backoff::Exponential->new(
        max_actual_duration => $self->lock_timeout,
        initial_delay       => 0.01, # 10 ms
        max_delay           => 10,   # 10 s
    );

    while (1) {
        if (my $ret = $code->()) {
            return 1;
        }
        my $secs = $ab->failure;
        return 0 if $secs < 0;
        sleep $secs;
    }
}

sub try_lock { 1 }
sub wait_lock {
    my $class = ref $_[0] || $_[0];
    hurl "$class has not implemented wait_lock()";
}

sub begin_work  { shift }
sub finish_work { shift }
sub rollback_work { shift }

sub earliest_change {
    my $self = shift;
    my $change_id = $self->earliest_change_id(@_) // return undef;
    return $self->plan->get( $change_id );
}

sub latest_change {
    my $self = shift;
    my $change_id = $self->latest_change_id(@_) // return undef;
    return $self->plan->get( $change_id );
}

sub needs_upgrade {
    my $self = shift;
    $self->registry_version != $self->registry_release;
}

sub _check_registry {
    my $self   = shift;
    my $newver = $self->registry_release;
    my $oldver = $self->registry_version;
    return $self if $newver == $oldver;



( run in 0.471 second using v1.01-cache-2.11-cpan-40ba7b3775d )