view release on metacpan or search on metacpan
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
jesp
Or use from your own program (in Perl):
my $jesp = App::JESP->new({
interactive => 0, # No ANSI color
home => 'path/to/jesphome',
dsn => ...,
username => ...,
password => ...
});
$jesp->install();
$jesp->deploy();
# CONFIGURATION
All JESP configuration must live in a JESP home directory.
This home directory must contain a plan.json file, containing the patching
lib/App/JESP.pm view on Meta::CPAN
use DBIx::Simple;
use File::Spec;
use IO::Interactive;
use Log::Any qw/$log/;
use String::Truncate;
# Settings
## DB Connection attrbutes.
has 'dsn' => ( is => 'ro', isa => 'Str', required => 1 );
has 'username' => ( is => 'ro', isa => 'Maybe[Str]', required => 1);
has 'password' => ( is => 'ro', isa => 'Maybe[Str]', required => 1);
has 'home' => ( is => 'ro', isa => 'Str', required => 1 );
## JESP Attributes
has 'prefix' => ( is => 'ro', isa => 'Str', default => 'jesp_' );
has 'driver_class' => ( is => 'ro', isa => 'Str', lazy_build => 1);
# Operational stuff
has 'get_dbh' => ( is => 'ro', isa => 'CodeRef', default => sub{
my ($self) = @_;
return sub{
return DBI->connect( $self->dsn(), $self->username(), $self->password(),
{ RaiseError => 1,
PrintError => 0,
AutoCommit => 1,
});
};
});
has 'dbix_simple' => ( is => 'ro', isa => 'DBIx::Simple', lazy_build => 1);
has 'patches_table_name' => ( is => 'ro', isa => 'Str' , lazy_build => 1);
has 'meta_patches' => ( is => 'ro', isa => 'ArrayRef[HashRef]',
lib/App/JESP.pm view on Meta::CPAN
jesp
Or use from your own program (in Perl):
my $jesp = App::JESP->new({
interactive => 0, # No ANSI color
home => 'path/to/jesphome',
dsn => ...,
username => ...,
password => ...
});
$jesp->install();
$jesp->deploy();
=cut
=head1 CONFIGURATION
All JESP configuration must live in a JESP home directory.
lib/App/JESP/Cmd/CommandJESP.pm view on Meta::CPAN
"The DSN to connect to the DB. See https://metacpan.org/pod/DBI#parse_dsn for DSN format"
."\nExamples:\n"
."\n dbi:mysql:database=testdb;host=localhost;port=3306"
."\n dbi:SQLite:dbname=demo/test.db"
."\n dbi:Pg:dbname=testdb;host=localhost;port=5432"
."\n"
],
[ 'username=s' =>
"The username to connect to the DB", { default => undef } ],
[ 'password=s' =>
"The password to connect to the DB", { default => undef } ],
[ 'prefix=s' =>
"The prefix for all jesp metatables. Defaults to 'jesp_'" ],
$class->options($app),
)
}
=head2 options
Override this in subclasses to add options to opt_spec
lib/App/JESP/Cmd/CommandJESP.pm view on Meta::CPAN
my ( $self, $opts, $args ) = @_;
unless( $opts->dsn() ){ die "Missing 'dsn' option. Run with -h\n"; }
unless( $opts->home() ){ die "Missing 'home' option. Run with -h\n"; }
# Time to build the JESP
$log->debug("Building App::JESP instance");
my $jesp = App::JESP->new({
dsn => $opts->dsn(),
home => $opts->home(),
( $opts->username() ? ( username => $opts->username() ) : ( username => undef ) ),
( $opts->password() ? ( password => $opts->password() ) : ( password => undef ) ),
});
$log->debug("App::JESP instance built");
# Inject __jesp in myself.
# Yes this is a bit dirty, but it works.
$self->{__jesp} = $jesp;
$self->validate( $opts, $args );
}
=head2 validate
lib/App/JESP/Driver.pm view on Meta::CPAN
my @stderr;
my $on_stderr = sub{
$log->warn( @_ );
push @stderr , @_;
};
my $properties = {};
my ($scheme, $driver, $attr_string, $attr_hash, $driver_dsn) = DBI->parse_dsn( $self->jesp()->dsn() );
ref($self)->_OdbcParse( $driver_dsn , $properties , [] );
$properties->{user} ||= $self->jesp()->username();
$properties->{password} ||= $self->jesp()->password();
$properties = {
%$properties,
%{ defined( $attr_hash ) ? $attr_hash : {} },
dsn => $self->jesp()->dsn(),
scheme => $scheme,
driver => $driver,
driver_dsn => $driver_dsn,
attr_string => $attr_string,
};
my %EXTRA_ENV = ();
lib/App/JESP/Driver/mysql.pm view on Meta::CPAN
my @cmd = ( $mysql );
# Time to build the command according to the dsn properties
my $properties = {};
{
eval "require DBD::mysql" or die "Please install DBD::mysql for this to work\n";
my ($scheme, $driver, $attr_string, $attr_hash, $driver_dsn) = DBI->parse_dsn( $self->jesp()->dsn() );
DBD::mysql->_OdbcParse( $driver_dsn , $properties , [] );
$properties->{user} ||= $self->jesp()->username();
$properties->{password} ||= $self->jesp()->password();
$log->trace('mysql properties: '.Dumper( $properties ));
}
push @cmd , '-B'; # This is a batch command. We dont want interactive at all.
if( my $user = $properties->{user} ){
push @cmd , ( '-u' , String::ShellQuote::shell_quote( $user ));
}
if( my $database = $properties->{database} ){
push @cmd , ( '-D' , String::ShellQuote::shell_quote( $database ));
}
if( my $host = $properties->{host} ){
push @cmd , ( '-h' , String::ShellQuote::shell_quote( $host ));
}
if( my $port = $properties->{port} ){
push @cmd , ( '-P' , String::ShellQuote::shell_quote( $port ));
}
if( my $mysql_socket = $properties->{mysql_socket} ){
push @cmd , ( '-S' , String::ShellQuote::shell_quote( $mysql_socket ));
}
if( my $password = $properties->{password} ){
push @cmd , ( '-p'.String::ShellQuote::shell_quote( $password ));
}
my $on_stdout = sub{
$log->info( @_ );
};
my @stderr;
my $on_stderr = sub{
$log->warn( @_ );
push @stderr , @_;
#! perl -w
use Test::Most;
use App::JESP;
use Log::Any::Adapter qw/Stderr/;
ok( my $jesp = App::JESP->new({ dsn => 'dbi:SQLite:dbname=:memory:', username => undef, password => undef, home => 'bla' }) );
ok( ! $jesp->interactive(), "Ok interactive is false");
is( $jesp->prefix() , 'jesp_' );
ok( $jesp->dbix_simple(), "Ok can get DBIx::Simple DB");
done_testing();
#! perl -w
use Test::Most;
use App::JESP;
use Term::ANSIColor;
use Log::Any::Adapter qw/Stderr/;
ok( my $jesp = App::JESP->new({ dsn => 'dbi:SQLite:dbname=:memory:', username => undef, password => undef, home => 'bla', interactive => 1 }) );
is( $jesp->colorizer()->colored("Foo", "blue") , Term::ANSIColor::colored("Foo", "blue") );
done_testing();
use Test::Most;
use App::JESP;
# Test deployment in SQLite
# use Log::Any::Adapter qw/Stderr/;
# A home that is there.
my $jesp = App::JESP->new({ dsn => 'dbi:SQLite:dbname=:memory:',
username => undef,
password => undef,
home => './t/home/'
});
throws_ok(sub{ $jesp->deploy() } , qr/ERROR querying meta/ );
# Time to install
$jesp->install();
my $status = $jesp->status();
is( scalar( @{$status->{plan_patches}} ) , 4, "Ok 4 patches in plan");
is( $jesp->deploy(), 4, "Ok applied 4 patches");
t/deploy_mysql.t view on Meta::CPAN
my @mysqls = ( $mysqld );
if( $ENV{EXTENDED_TESTING} ){
# Other flavours of Test::mysqld
push @mysqls , Test::mysqld->new( my_cnf => { port => Net::EmptyPort::empty_port() } );
}
foreach my $mysql ( @mysqls ){
# A home that is there.
my $jesp = App::JESP->new({ dsn => $mysql->dsn(),
password => '',
username => '',
home => './t/home_mysql/'
});
throws_ok(sub{ $jesp->deploy() } , qr/ERROR querying meta/ );
# Time to install
$jesp->install();
# And deploy
is( $jesp->deploy(), 2, "Ok applied 2 patches");
is( $jesp->deploy(), 0, "Ok applied 0 patches on the second call");
t/deploy_mysqlpasswd.t view on Meta::CPAN
my $dbh = DBI->connect( $mysql->dsn() , '', '' , { RaiseError => 1, AutoCommit => 1 });
$dbh->do('CREATE DATABASE grotest');
$dbh->do('GRANT ALL ON grotest.* TO \'salengro\'@\'localhost\' IDENTIFIED BY \'mufflin!\'');
my $dsn = $mysql->dsn();
$dsn =~ s/dbname=test/dbname=grotest/;
$dsn =~ s/;user=root//;
# # A home that is there.
my $jesp = App::JESP->new({ dsn => $dsn,
password => 'mufflin!',
username => 'salengro',
home => './t/home_mysql/'
});
$jesp->install();
# And deploy
is( $jesp->deploy(), 2, "Ok applied 2 patches");
is( $jesp->deploy(), 0, "Ok applied 0 patches on the second call");
done_testing();
t/deploy_pgsql.t view on Meta::CPAN
plan skip_all => "Test::PostgreSQL is required for this test" if $@;
eval "use Net::EmptyPort";
plan skip_all => "Net::EmptyPort is required for this test" if $@;
}
use App::JESP;
my $pgsql = eval{ Test::PostgreSQL->new({ port => Net::EmptyPort::empty_port() }) } or plan skip_all => $@.' - '.$Test::PostgreSQL::errstr;
my $jesp = App::JESP->new({ dsn => $pgsql->dsn(),
password => '',
username => 'postgres',
home => './t/home_pgsql/'
});
throws_ok(sub{ $jesp->deploy() } , qr/ERROR querying meta/ );
# Time to install
$jesp->install();
# And deploy
is( $jesp->deploy(), 2, "Ok applied 2 patches");
is( $jesp->deploy(), 0, "Ok applied 0 patches on the second call");
t/deploy_script.t view on Meta::CPAN
plan skip_all => 'No sqlite3 found';
}
delete $ENV{PATH};
$ENV{WHICH_SQLITE3} = $which_sqlite3;
my ($fh, $dbname) = File::Temp::tempfile( EXLOCK => 0 );
# A home that is there.
my $jesp = App::JESP->new({ dsn => "dbi:SQLite:dbname=$dbname",
username => undef,
password => undef,
home => './t/homescripts/'
});
# Time to install
$jesp->install();
is( $jesp->deploy(), 3, "Ok applied 3 patches");
unlink $dbname;
done_testing();
t/home_mysql/patches/sakila1.sql view on Meta::CPAN
CREATE TABLE staff (
staff_id TINYINT UNSIGNED NOT NULL AUTO_INCREMENT,
first_name VARCHAR(45) NOT NULL,
last_name VARCHAR(45) NOT NULL,
address_id SMALLINT UNSIGNED NOT NULL,
picture BLOB DEFAULT NULL,
email VARCHAR(50) DEFAULT NULL,
store_id TINYINT UNSIGNED NOT NULL,
active BOOLEAN NOT NULL DEFAULT TRUE,
username VARCHAR(16) NOT NULL,
password VARCHAR(40) BINARY DEFAULT NULL,
last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (staff_id),
KEY idx_fk_store_id (store_id),
KEY idx_fk_address_id (address_id),
CONSTRAINT fk_staff_store FOREIGN KEY (store_id) REFERENCES store (store_id) ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT fk_staff_address FOREIGN KEY (address_id) REFERENCES address (address_id) ON DELETE RESTRICT ON UPDATE CASCADE
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Table structure for table `store`
t/home_pgsql/patches/pagila1.sql view on Meta::CPAN
CREATE TABLE staff (
staff_id integer DEFAULT nextval('staff_staff_id_seq'::regclass) NOT NULL,
first_name character varying(45) NOT NULL,
last_name character varying(45) NOT NULL,
address_id smallint NOT NULL,
email character varying(50),
store_id smallint NOT NULL,
active boolean DEFAULT true NOT NULL,
username character varying(16) NOT NULL,
password character varying(40),
last_update timestamp without time zone DEFAULT now() NOT NULL,
picture bytea
);
ALTER TABLE public.staff OWNER TO postgres;
--
-- Name: store_store_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
--
t/install.t view on Meta::CPAN
#! perl -w
use Test::Most;
use App::JESP;
use Carp::Always;
# use Log::Any::Adapter qw/Stderr/;
ok( my $jesp = App::JESP->new({ dsn => 'dbi:SQLite:dbname=:memory:', username => undef, password => undef, home => 'bla' }) );
ok( $jesp->install(), "Ok can install JESP in the given Database");
my @installed_patches = $jesp->dbix_simple()->select( $jesp->patches_table_name() )->hashes();
is( scalar( @installed_patches ) , 1 );
is( $installed_patches[0]->{id} , $jesp->prefix().'meta_zero' , "Good zero name" );
ok( exists( $installed_patches[0]->{applied_datetime} ) , "There is an applied time" );
# Try pushing a patch in the meta patches, and check that it becomes applied.
push @{$jesp->meta_patches()},
t/install_dbs.t view on Meta::CPAN
}
use App::JESP;
use File::Spec;
# use Log::Any::Adapter qw/Stderr/;
# First something with SQLite.
my @connection_params = ({ dsn => 'dbi:SQLite:dbname=:memory:',
username => undef,
password => undef
});
# Then something with MySQL
my $mysql = Test::mysqld->new( my_cnf => {
'skip-networking' => '1',
socket => File::Spec->catfile( File::Spec->tmpdir() , 'socket-'.$$.'-testmysqld')
});
if( $mysql ){
push @connection_params, { dsn => $mysql->dsn(),
password => '',
username => ''
};
}else{
diag("Warning: could not build Test::mysqld ".$Test::mysqld::errstr);
}
my $pgsql = eval{ Test::PostgreSQL->new(); };
if( $pgsql ){
push @connection_params, { dsn => $pgsql->dsn(),
password => undef,
username => 'postgres'
};
}else{
diag("Warning: could not build Test::PostgreSQL: ".$@.' - '.$Test::PostgreSQL::errstr);
}
foreach my $connect_params ( @connection_params ){
ok( my $jesp = App::JESP->new({ dsn => $connect_params->{dsn},
username => $connect_params->{username},
password => $connect_params->{postgres},
home => 'bla'
}) );
ok( $jesp->install(), "Ok can install JESP in the given Database");
my @installed_patches = $jesp->dbix_simple()->select( $jesp->patches_table_name() )->hashes();
is( scalar( @installed_patches ) , 1 );
is( $installed_patches[0]->{id} , $jesp->prefix().'meta_zero' , "Good zero name" );
ok( exists( $installed_patches[0]->{applied_datetime} ) , "There is an applied time" );
}
ok(1);
use Test::Most;
use App::JESP;
# use Log::Any::Adapter qw/Stderr/;
{
# A home that is not there.
my $jesp = App::JESP->new({ dsn => 'dbi:SQLite:dbname=:memory:',
username => undef,
password => undef,
home => 'bla'
});
throws_ok(sub{ my $plan = $jesp->plan() } , qr/does not exists/ );
}
{
# A home that is there.
my $jesp = App::JESP->new({ dsn => 'dbi:SQLite:dbname=:memory:',
username => undef,
password => undef,
home => './t/home/'
});
ok( my $plan = $jesp->plan() );
ok( my $patches = $plan->patches() );
is( scalar( @{$patches} ) , 4 , "4 test patches");
foreach my $patch ( @{$patches} ){
ok( $patch->sql() , "Ok got SQL" );
}
}
t/plan_script.t view on Meta::CPAN
# use Log::Any::Adapter qw/Stderr/;
if( $^O =~ /Win/ ){
plan skip_all => 'No script test on windows please';
}
{
# A home that is there.
my $jesp = App::JESP->new({ dsn => 'dbi:SQLite:dbname=:memory:',
username => undef,
password => undef,
home => './t/homescripts/'
});
ok( my $plan = $jesp->plan() );
ok( my $patches = $plan->patches() );
is( scalar( @{$patches} ) , 3 , "3 test patches");
ok( ! $patches->[2]->sql() , "No sql in patch 5");
ok( $patches->[2]->script_file(), "Patch 3 is a script" );
}