view release on metacpan or search on metacpan
t/author-pod-coverage.t
t/author-pod-syntax.t
t/author-portability.t
t/author-synopsis.t
t/bin.t
t/deploy.t
t/deploy/dest.watch
t/deploy/log
t/deploy/source/001/deploy
t/deploy/source/001/revert
t/deploy/source/001/verify
t/deploy/source/002/deploy
t/deploy/source/002/revert
t/deploy/source/002/verify
t/deploy/source/003/deploy
t/deploy/source/003/revert
t/deploy/source/003/verify
t/deploy/source/004/deploy
t/deploy/source/004/revert
t/deploy/source/004/verify
t/deploy/source/005/deploy
t/deploy/source/005/revert
t/deploy/source/005/verify
t/deploy/source/dest.wrap
t/init.t
t/init/actions/001/deploy
t/init/actions/001/revert
t/init/actions/001/verify
t/init/actions/002/deploy
t/init/actions/002/revert
t/init/actions/002/verify
t/init/actions2/001/deploy
t/init/actions2/001/revert
t/init/actions2/001/verify
t/init/actions2/002/deploy
t/init/actions2/002/revert
t/init/actions2/002/verify
t/init/dest.watch
t/init/dest.watch2
t/make.t
t/make/actions/001/deploy
t/make/actions/001/revert
t/make/actions/001/verify
t/make/actions/002/deploy
t/make/actions/002/revert
t/make/actions/002/verify
t/make/dest.watch
t/release-kwalitee.t
t/revert.t
t/revert/dest.watch
t/revert/log
t/revert/source/001/deploy
t/revert/source/001/revert
t/revert/source/001/verify
t/revert/source/002/deploy
t/revert/source/002/revert
t/revert/source/002/verify
t/revert/source/003/deploy
t/revert/source/003/revert
t/revert/source/003/verify
t/revert/source/004/deploy
t/revert/source/004/revert
t/revert/source/004/verify
t/revert/source/005/deploy
t/revert/source/005/revert
t/revert/source/005/verify
t/revert/source/dest.wrap
weaver.ini
our $VERSION = '1.36'; # VERSION
use Pod::Usage 'pod2usage';
use App::Dest;
my @commands = qw(
init add rm
watches putwatch writewatch
make expand list prereqs
status diff clean preinstall nuke
deploy redeploy revdeploy verify revert update
man version
);
my ( $command, @args ) = @ARGV;
( $command = lc( $command || 'undef' ) ) =~ s/^\-+//;
my @command = grep { index( $_, $command ) == 0 } @commands;
($command) = @command;
sub usage {
pod2usage(
lib/App/Dest.pm view on Meta::CPAN
my ( $self, $path, $ext ) = @_;
die "No name specified; usage: dest make [path]\n" unless ($path);
$path = strftime( $path, localtime );
$ext = '.' . $ext if ( defined $ext );
$ext //= '';
try {
mkpath($path);
for ( qw( deploy verify revert ) ) {
open( my $file, '>', "$path/$_$ext" ) or die;
print $file "\n";
}
}
catch ($e) {
die "Failed to fully make $path; check permissions or existing files\n";
}
$self->expand($path);
return 0;
}
sub expand {
my ( $self, $path ) = @_;
print join( ' ', map { <"$path/$_*"> } qw( deploy verify revert ) ), "\n";
return 0;
}
sub list {
my $self = _new(shift);
my ($filter) = @_;
my $tree = $self->_actions_tree($filter);
for my $path ( sort keys %$tree ) {
lib/App/Dest.pm view on Meta::CPAN
}
sub revdeploy {
my $self = _new(shift);
my ($action) = @_;
$self->revert($action);
return $self->deploy($action);
}
sub verify {
my $self = _new(shift);
my ($action) = @_;
return $self->_execute_action( $self->_build_execute_stack( $action, 'verify' )->[0] );
}
sub revert {
my $self = _new(shift);
my ( $dry_run, $action ) = _dry_check(@_);
die "File to revert required; usage: dest revert file\n" unless ($action);
my $execute_stack = $self->_build_execute_stack( $action, 'revert' );
die "Action not deployed\n" if (
not @$execute_stack or
lib/App/Dest.pm view on Meta::CPAN
elsif ( not $a ) {
push(
@{ $data->{actions} },
{
action => $b,
deployed => 0,
},
);
}
elsif ( $a and $b ) {
( my $action = $b ) =~ s,/(?:deploy|verify|revert)(?:\.[^\/]+)?$,,;
push(
@{ $data->{actions} },
{
action => $action,
modified => 1,
file => $b,
},
);
}
lib/App/Dest.pm view on Meta::CPAN
@execute_stack,
{
type => $type,
action => $action,
file => $file,
wrap => $wraps->{$action},
},
);
if ( $type eq 'deploy' ) {
my $verify_file = ( <"$action/verify*"> )[0];
die "Action file does not exist for: verify $action\n" unless ($verify_file);
splice(
@execute_stack,
1,
0,
{
type => 'verify',
action => $action,
file => $verify_file,
wrap => $wraps->{$action},
},
);
}
};
if ( $type eq 'deploy' ) {
$add->() unless ( $state->{$action}{deployed} and not $redeploy );
}
elsif ( $type eq 'revert' ) {
$add->() if ( $state->{$action}{deployed} or $state->{$action}{modified} );
}
elsif ( $type eq 'verify' ) {
$add->();
}
}
return \@execute_stack;
}
sub _dry_run_report {
my ($execute_stack) = @_;
print '' . ( ( $_->{wrap} ) ? $_->{wrap} . ' ' : '' ) . $_->{file}, "\n" for (@$execute_stack);
lib/App/Dest.pm view on Meta::CPAN
chomp($err_str);
if ($died) {
die "Failed to execute $file: $err_str\n";
}
else {
warn "Warnings from executed $file: $err_str\n";
}
}
};
if ( $type eq 'verify' ) {
$run->();
chomp($out);
die "$err\n" if ($err);
if ($out) {
print "ok - verify: $action\n";
}
else {
die "not ok - verify: $action\n";
}
}
else {
print "begin - $type: $action\n";
$run->();
print "ok - $type: $action\n";
}
my $dest_copy = $self->_rel2dir( '.dest/' . $self->_rel2root($action) );
rmtree($dest_copy) unless ( $type eq 'verify' );
dircopy( $action, $dest_copy ) if ( $type eq 'deploy' );
return 0;
}
1;
__END__
=pod
lib/App/Dest.pm view on Meta::CPAN
dest list [FILTER] # list all actions in all watches
dest prereqs [FILTER] # like "list" but include report of prereqs
dest status # check status of tracked directories
dest diff [NAME] # display a diff of any modified actions
dest clean [NAME] # reset dest state to match current files/dirs
dest preinstall [NAME] # set dest state so an update will deploy everything
dest nuke # de-initialize dest; remove all dest stuff
dest deploy NAME [-d] # deployment of a specific action
dest verify [NAME] # verification of tracked actions or specific action
dest revert NAME [-d] # revertion of a specific action
dest redeploy NAME [-d] # deployment of a specific action
dest revdeploy NAME # revert and deployment of a specific action
dest update [INCS] [-d] # automaticall deploy or revert to cause currency
dest version # dest current version
dest help # display command synposis
dest man # display man page
=head1 DESCRIPTION
C<dest> is a simple "deployment state" change management tool. Inspired by
what Sqitch does for databases, it provides a simple mechanism for writing
deploy, verify, and revert parts of a change action. The typical use of
C<dest> is in a development context because it allows for simplified state
changes when switching between branches (as an example).
Let's say you're working with a group of other software engineers on a
particular software project using your favorite revision control system.
Let's also say that you have a database that undergoes schema changes as
features are developed, and you have various system activities like the
installation of libraries or other applications. Then let's also say the team
branches, works on stuff, shares those branches, reverts, merges, etc. And also
from time to time you want to go back in time a bit so you can reproduce a bug.
Maintaining the database state and the state of the system across all that
activity can be problematic. C<dest> tries to solve this in a very simple way,
letting you be able to deploy, revert, and verify to any point in time in
the development history.
See below for an example scenario that may help illustrate using C<dest> in a
pseudo real world situation.
Note that using C<dest> for production deployment, provisioning, or
configuration management is not advised. Use a full-featured configuration
management tool instead.
=head1 COMMANDS
lib/App/Dest.pm view on Meta::CPAN
The initialization will result in a C<.dest> directory being created.
You'll almost certainly want to add ".dest" to your C<.gitignore> file or
similar revision control ignore file.
=head2 add DIR
Once a project has been initialized, you need to tell C<dest> what directories
you want to "track". Into these tracked directories you'll place subdirectories
with recognizable names, and into each subdirectory a set of 3 files: deploy,
revert, and verify.
For example, let's say you have a database. So you create C<db> in your
project's root directory. Then call C<dest add db> from your root directory.
Inside C<db>, you might create the directory C<db/schema>. And under that
directory, add the files: deploy, revert, and verify.
The deploy file contains the instructions to create the database schema. The
revert file contains the instructions to revert what the deploy file did. And
the verify file let's you verify the deploy file worked.
=head2 rm DIR
This removes a directory from the C<dest> tracking list.
=head2 watches
Returns a list of tracked or watched directories.
=head2 putwatch FILE
lib/App/Dest.pm view on Meta::CPAN
dest putwatch dest.watch
=head2 writewatch
Creates (or overwrites) a watch file in the project root directory with the
contents of the currently watched directories.
=head2 make NAME [EXT]
This is a helper command. Given a directory you've already added, it will create
the subdirectory and deploy, revert, and verify files.
# given db, creates db/schema and the 3 files
dest make db/schema
As a nice helper bit, C<make> will list the relative paths of the 3 new files.
So if you want, you can do something like this:
vi `dest make db/schema`
Optionally, you can specify an extension for the created files. For example:
vi `dest make db/schema sql`
# this will create and open in vi:
# db/schema/deploy.sql
# db/schema/revert.sql
# db/schema/verify.sql
And optionally, you can include any date/time format supported by L<POSIX>
C<strftime>.
dest make db/%s_action sql
=head2 expand NAME
This command lists out the relative paths and names of the 3 files of the
action provided, so you can do stuff like:
lib/App/Dest.pm view on Meta::CPAN
=head2 deploy NAME [-d]
This tells C<dest> to deploy a specific action. For example, if you called
C<status> and got back results like in the status example above, you might then
want to:
dest deploy db/new_function
Note that you shouldn't add "/deploy" to the end of that. Also note that a
C<deploy> call will automatically call C<verify> when complete.
Adding a "-d" flag to the command will cause a "dry run" to run, which will
not perform any actions but will instead report what actions would happen.
=head2 verify [NAME]
This will run the verify step on any given action, or if no action name is
provided, all actions under directories that are tracked.
Unlike deploy and revert files, which can run the user through all sorts of
user input/output, verify files must return some value that is either true
or false. C<dest> will assume that if it sees a true value, verification is
confirmed. If it receives a false value, verification is assumed to have failed.
=head2 revert NAME [-d]
This tells C<dest> to revert a specific action. For example, if you deployed
C<db/new_function> but then you wanted to revert it, you'd:
dest revert db/new_function
lib/App/Dest.pm view on Meta::CPAN
This will automatically deploy or revert as appropriate to make your system
match the code. This will likely be the most common command you run.
If there are actions in the code that have not been deployed, these will be
deployed. If there are actions that have been deployed that are no longer in
the code, they will be reverted.
If there are actions that are in the code that have been deployed, but the
"deploy" file has changed, then C<update> will revert the previously deployed
"deploy" file then deploy the new "deploy" file. (And note that the deployment
will automatically call C<verify>.)
You can optionally add one or more "INCS" strings to the update command to
restrict the update to only perform operations that include one of the "INCS" in
its action file. So for example, let's say you have a "db/changes" directory
with some actions and a "etc/changes" directory with some actions. If you were
to specify "db/changes" as one of your "INCS", this would only update actions
from that directory tree.
Adding a "-d" flag to the command will cause a "dry run" to run, which will
not perform any actions but will instead report what actions would happen.
lib/App/Dest.pm view on Meta::CPAN
file. Specifically, it'll assume a file is a wrapper if the filename is
"dest.wrap". If such a file is found, then that file is called, and the name of
the action sub-file is passed as its only argument.
As an example, let's say I created an action set that looked like this
example/
ls/
deploy
revert
verify
Let's then also say that the C<example/ls/deploy> file contains: C<ls>
I could create a deployment file C<example/dest.wrap> that looked like this:
#!/bin/sh
/bin/sh "$1"
Wrappers will only ever be run from the current code. For example, if you have
a revert file for some action and you checkout your working directory to a
point in time prior to the revert file existing, C<dest> maintains a copy of the
original revert file so it can revert the action. However, it will always rely
on whatever wrapper is in the current working directory.
The C<dest.wrap> is called with two parameters: first, the name of the change
program, and second, the action type ("deploy", "revert", "verify").
=head1 WATCH FILE
Optionally, you can elect to use a watch file that can be committed to your
favorite revision control system. In the root directory of your project, create
a filed called "dest.watch" and list therein the directories (relative to the
root directory of the project) to watch.
If this "dest.watch" file exists in the root directory of your project, C<dest>
will add the following behavior:
lib/App/Dest.pm view on Meta::CPAN
mkdir db data # create the directories
dest init # initiate dest for your project
dest add db data # add the directories to the dest watch list
dest writewatch # write the watch list (so others can init without adding)
dest status # show the current status (which is everything is OK)
=head2 Create Schema Action
The next step would probably be to create your database schema as a C<dest>
action. Actions include deploy, verify, and revert files. You can use the "make"
command to create these files for you. The command will return the list of files
created, so you can wrap the command to your favorite editor.
dest make db/schema sql # create "schema" action as ".sql" files
vi `dest list db/schema` # list the "schema" files into vi
vi `dest make db/schema sql` # the previous 2 commands as 1 command
Your deploy file will be the SQL required to create your schema. The revert file
reverts what the deploy file deploys. The verify file needs to return some
positive value if and only if the deploy action worked.
Since your local CLI shell probably doesn't know how to execute SQL files
natively, you'll likely need to create a C<dest.wrap> file.
touch db/dest.wrap && chmod u+x db/dest.wrap && vi db/dest.wrap
This file if it exists will get executed instead of the deploy, verify, and
revert files, and it will be passed the action file being executed.
=head2 Status and Deploying
Now, check the project's C<dest> status:
dest s # short for "dest status"
You should see:
t/author-eol.t view on Meta::CPAN
't/author-pod-coverage.t',
't/author-pod-syntax.t',
't/author-portability.t',
't/author-synopsis.t',
't/bin.t',
't/deploy.t',
't/deploy/dest.watch',
't/deploy/log',
't/deploy/source/001/deploy',
't/deploy/source/001/revert',
't/deploy/source/001/verify',
't/deploy/source/002/deploy',
't/deploy/source/002/revert',
't/deploy/source/002/verify',
't/deploy/source/003/deploy',
't/deploy/source/003/revert',
't/deploy/source/003/verify',
't/deploy/source/004/deploy',
't/deploy/source/004/revert',
't/deploy/source/004/verify',
't/deploy/source/005/deploy',
't/deploy/source/005/revert',
't/deploy/source/005/verify',
't/deploy/source/dest.wrap',
't/init.t',
't/init/actions/001/deploy',
't/init/actions/001/revert',
't/init/actions/001/verify',
't/init/actions/002/deploy',
't/init/actions/002/revert',
't/init/actions/002/verify',
't/init/actions2/001/deploy',
't/init/actions2/001/revert',
't/init/actions2/001/verify',
't/init/actions2/002/deploy',
't/init/actions2/002/revert',
't/init/actions2/002/verify',
't/init/dest.watch',
't/init/dest.watch2',
't/make.t',
't/make/actions/001/deploy',
't/make/actions/001/revert',
't/make/actions/001/verify',
't/make/actions/002/deploy',
't/make/actions/002/revert',
't/make/actions/002/verify',
't/make/dest.watch',
't/release-kwalitee.t',
't/revert.t',
't/revert/dest.watch',
't/revert/log',
't/revert/source/001/deploy',
't/revert/source/001/revert',
't/revert/source/001/verify',
't/revert/source/002/deploy',
't/revert/source/002/revert',
't/revert/source/002/verify',
't/revert/source/003/deploy',
't/revert/source/003/revert',
't/revert/source/003/verify',
't/revert/source/004/deploy',
't/revert/source/004/revert',
't/revert/source/004/verify',
't/revert/source/005/deploy',
't/revert/source/005/revert',
't/revert/source/005/verify',
't/revert/source/dest.wrap'
);
eol_unix_ok($_, { trailing_whitespace => 1 }) foreach @files;
done_testing;
t/author-no-tabs.t view on Meta::CPAN
't/author-pod-coverage.t',
't/author-pod-syntax.t',
't/author-portability.t',
't/author-synopsis.t',
't/bin.t',
't/deploy.t',
't/deploy/dest.watch',
't/deploy/log',
't/deploy/source/001/deploy',
't/deploy/source/001/revert',
't/deploy/source/001/verify',
't/deploy/source/002/deploy',
't/deploy/source/002/revert',
't/deploy/source/002/verify',
't/deploy/source/003/deploy',
't/deploy/source/003/revert',
't/deploy/source/003/verify',
't/deploy/source/004/deploy',
't/deploy/source/004/revert',
't/deploy/source/004/verify',
't/deploy/source/005/deploy',
't/deploy/source/005/revert',
't/deploy/source/005/verify',
't/deploy/source/dest.wrap',
't/init.t',
't/init/actions/001/deploy',
't/init/actions/001/revert',
't/init/actions/001/verify',
't/init/actions/002/deploy',
't/init/actions/002/revert',
't/init/actions/002/verify',
't/init/actions2/001/deploy',
't/init/actions2/001/revert',
't/init/actions2/001/verify',
't/init/actions2/002/deploy',
't/init/actions2/002/revert',
't/init/actions2/002/verify',
't/init/dest.watch',
't/init/dest.watch2',
't/make.t',
't/make/actions/001/deploy',
't/make/actions/001/revert',
't/make/actions/001/verify',
't/make/actions/002/deploy',
't/make/actions/002/revert',
't/make/actions/002/verify',
't/make/dest.watch',
't/release-kwalitee.t',
't/revert.t',
't/revert/dest.watch',
't/revert/log',
't/revert/source/001/deploy',
't/revert/source/001/revert',
't/revert/source/001/verify',
't/revert/source/002/deploy',
't/revert/source/002/revert',
't/revert/source/002/verify',
't/revert/source/003/deploy',
't/revert/source/003/revert',
't/revert/source/003/verify',
't/revert/source/004/deploy',
't/revert/source/004/revert',
't/revert/source/004/verify',
't/revert/source/005/deploy',
't/revert/source/005/revert',
't/revert/source/005/verify',
't/revert/source/dest.wrap'
);
notabs_ok($_) foreach @files;
done_testing;
"diff - actions\n" .
" actions/002\n" .
" M actions/002/deploy\n" .
" + actions/003\n" .
" + actions/004\n" .
" - actions/005\n",
'status',
);
stdout_is(
sub { App::Dest->verify('actions/002') },
"ok - verify: actions/002\n",
'verify',
);
$log .= "# log\n" .
"actions/002/verify\n" .
"002 verify\n";
is( &read_log, $log, 'log correct after verify' );
stdout_is(
sub { App::Dest->deploy( 'actions/004', '-d' ) },
"actions/dest.wrap actions/004/deploy\n" .
"actions/dest.wrap actions/004/verify\n",
'deploy dry run',
);
stdout_is(
sub { App::Dest->deploy('actions/004') },
"begin - deploy: actions/004\n" .
"ok - deploy: actions/004\n" .
"ok - verify: actions/004\n",
'deploy',
);
$log .= "actions/004/deploy\n" .
"004 deploy\n" .
"actions/004/verify\n" .
"004 verify\n";
is( &read_log, $log, 'log correct after deploy' );
stderr_is( sub {
try {
App::Dest->deploy('actions/004');
}
catch ($e) {
warn $e;
}
}, "Action already deployed\n", 'deploy again fails' );
stdout_is(
sub { App::Dest->redeploy('actions/004') },
"begin - deploy: actions/004\n" .
"ok - deploy: actions/004\n" .
"ok - verify: actions/004\n",
'redeploy',
);
$log .= "actions/004/deploy\n" .
"004 deploy\n" .
"actions/004/verify\n" .
"004 verify\n";
is( &read_log, $log, 'log correct after deploy' );
stdout_is(
sub { App::Dest->revdeploy('actions/004') },
"begin - revert: actions/004\n" .
"ok - revert: actions/004\n" .
"begin - deploy: actions/004\n" .
"ok - deploy: actions/004\n" .
"ok - verify: actions/004\n",
'redeploy',
);
$log .= ".dest/actions/004/revert\n" .
"004 revert\n" .
"actions/004/deploy\n" .
"004 deploy\n" .
"actions/004/verify\n" .
"004 verify\n";
is( &read_log, $log, 'log correct after revdeploy' );
stdout_is(
sub { App::Dest->revert('actions/004') },
"begin - revert: actions/004\n" .
"ok - revert: actions/004\n",
'revert',
);
$log .= ".dest/actions/004/revert\n" .
"004 revert\n";
is( &read_log, $log, 'log correct after deploy' );
stdout_is(
sub { App::Dest->update('actions') },
"begin - revert: actions/002\nok - revert: actions/002\n" .
"begin - deploy: actions/002\n" .
"ok - deploy: actions/002\n" .
"ok - verify: actions/002\n" .
"begin - deploy: actions/003\n" .
"ok - deploy: actions/003\n" .
"ok - verify: actions/003\n" .
"begin - deploy: actions/004\n" .
"ok - deploy: actions/004\n" .
"ok - verify: actions/004\n" .
"begin - revert: actions/005\n" .
"ok - revert: actions/005\n",
'update',
);
$log .= ".dest/actions/002/revert\n" .
"002 revert\n" .
"actions/002/deploy\n" .
"002 deploy\n" .
"changed\n" .
"actions/002/verify\n" .
"002 verify\n" .
"actions/003/deploy\n" .
"003 deploy\n" .
"actions/003/verify\n" .
"003 verify\n" .
"actions/004/deploy\n" .
"004 deploy\n" .
"actions/004/verify\n" .
"004 verify\n" .
".dest/actions/005/revert\n" .
"005 revert\n";
is( &read_log, $log, 'log correct after update' );
set_state;
done_testing;
t/deploy/source/001/verify view on Meta::CPAN
001 verify
t/deploy/source/002/verify view on Meta::CPAN
002 verify
t/deploy/source/003/verify view on Meta::CPAN
003 verify
t/deploy/source/004/verify view on Meta::CPAN
004 verify
t/deploy/source/005/verify view on Meta::CPAN
005 verify
);
stdout_is(
sub { App::Dest->list },
"actions actions:\n actions/001\n actions/002\n",
'list',
);
stdout_is(
sub { App::Dest->make( 'actions/003', 'sh' ) },
"actions/003/deploy.sh actions/003/verify.sh actions/003/revert.sh\n",
'make sh',
);
ok(
(
-f 'actions/003/deploy.sh' and
-f 'actions/003/verify.sh' and
-f 'actions/003/revert.sh'
),
'make created files correctly',
);
open( my $out, '>>', 'actions/003/deploy.sh' );
print $out "# dest.prereq: actions/002\n";
close $out;
stdout_is(
sub { App::Dest->prereqs('actions') },
"actions/001 has no prereqs\nactions/002 has no prereqs\nactions/003 prereqs:\n actions/002\n",
'prereqs',
);
stdout_is(
sub { App::Dest->make('actions/000') },
"actions/000/deploy actions/000/verify actions/000/revert\n",
'make',
);
dircopy( 'actions/001', '.dest/actions/001' );
dircopy( 'actions/002', '.dest/actions/002' );
dircopy( 'actions/000', '.dest/actions/000' );
rmtree('actions/000');
open( $out, '>>', '.dest/actions/001/deploy' );
print $out "# change\n";
stderr_is(
sub { App::Dest->init },
"Created new watch list based on dest.watch file:\n" .
" actions\n",
'init succeeds',
);
stdout_is(
sub { App::Dest->update('-d') },
"actions/dest.wrap actions/005/deploy\n" .
"actions/dest.wrap actions/005/verify\n" .
"actions/dest.wrap actions/004/deploy\n" .
"actions/dest.wrap actions/004/verify\n" .
"actions/dest.wrap actions/001/deploy\n" .
"actions/dest.wrap actions/001/verify\n" .
"actions/dest.wrap actions/002/deploy\n" .
"actions/dest.wrap actions/002/verify\n" .
"actions/dest.wrap actions/003/deploy\n" .
"actions/dest.wrap actions/003/verify\n",
'update dry run',
);
stdout_is(
sub { App::Dest->update },
"begin - deploy: actions/005\n" .
"ok - deploy: actions/005\n" .
"ok - verify: actions/005\n" .
"begin - deploy: actions/004\n" .
"ok - deploy: actions/004\n" .
"ok - verify: actions/004\n" .
"begin - deploy: actions/001\n" .
"ok - deploy: actions/001\n" .
"ok - verify: actions/001\n" .
"begin - deploy: actions/002\n" .
"ok - deploy: actions/002\n" .
"ok - verify: actions/002\n" .
"begin - deploy: actions/003\n" .
"ok - deploy: actions/003\n" .
"ok - verify: actions/003\n",
'update',
);
my ( $stdout, $stderr, $exit );
ok(
lives { ( $stdout, $stderr, $exit ) = capture { App::Dest->revert( 'actions/005', '-d' ) } },
'dry run revert specific action',
) or note $@;
like(
t/revert/source/001/verify view on Meta::CPAN
001 verify
t/revert/source/002/verify view on Meta::CPAN
002 verify
t/revert/source/003/verify view on Meta::CPAN
003 verify
t/revert/source/004/verify view on Meta::CPAN
004 verify
t/revert/source/005/verify view on Meta::CPAN
005 verify