Pinto-Remote-SelfContained
view release on metacpan or search on metacpan
lib/Pinto/Remote/SelfContained/App.pm view on Meta::CPAN
package
Pinto::Remote::SelfContained::App; # hide from PAUSE
use v5.10;
use Moo;
use Getopt::Long::Descriptive qw(describe_options);
use List::Util qw(max pairgrep);
use Path::Tiny qw(path);
use Pinto::Remote::SelfContained;
use Pinto::Remote::SelfContained::Chrome;
use Pinto::Remote::SelfContained::Types qw(Uri);
use Pinto::Remote::SelfContained::Util qw(current_username);
use Types::Standard qw(ArrayRef Bool HashRef Int Maybe Str);
use namespace::clean;
our $VERSION = '1.000';
has root => (is => 'ro', isa => Uri, coerce => 1, required => 1);
has username => (is => 'ro', isa => Str, default => sub { current_username() });
has password => (is => 'ro', isa => Maybe[Str]);
has quiet => (is => 'ro', isa => Bool);
has verbose => (is => 'ro', isa => Int, default => 1);
has action_name => (is => 'ro', isa => Str, required => 1);
has args => (is => 'ro', isa => HashRef, default => sub { +{} });
my @ATTRS_FROM_OPTIONS = qw(root username password quiet verbose);
sub command_info {
# The "args" is a string listing argument names, each optionally suffixed
# by one of ?+* to indicate optional, required-slurpy, optional-slurpy.
# Optional items must follow all required ones, with the exception that
# 'foo? bar' is a permitted spec; this isn't verified.
my %ret = (
add => {
summary => 'add local archives to the repository',
usage_desc => '%c %o ARCHIVE',
args => sub {
my (undef, $usage) = splice @_, 0, 2;
$usage->die({ pre_text => "Need exactly one archive\n\n" })
if @_ != 1;
my $filename = $_[0];
die "Archive filename $filename must be a tarball\n"
if $filename !~ /\.tar\.gz/;
return archives => [{
name => 'archives',
filename => $filename,
type => 'application/x-tar',
encoding => 'gzip',
}];
},
opt_spec => [
[ 'author=s' => 'The ID of the archive author' ],
[ 'cascade' => 'Always pick latest upstream package' ],
[ 'diff-style=s' => 'Set style of diff reports' ],
[ 'dry-run' => 'Do not commit any changes' ],
[ 'message|m=s' => 'Message to describe the change' ],
[ 'no-fail' => 'Do not fail when there is an error' ],
[ 'no-index|x=s@' => 'Do not index matching packages' ],
[ 'recurse!' => 'Recursively pull prereqs (negatable)' ],
[ 'pin' => 'Pin packages to the stack' ],
[ 'skip-missing-prerequisite|k=s@' => 'Skip missing prereq (repeatable)' ],
[ 'skip-all-missing-prerequisites|K' => 'Skip all missing prereqs' ],
[ 'stack|s=s' => 'Put packages into this stack' ],
[ 'use-default-message|M' => 'Use the generated message' ],
[ 'with-development-prerequisites|wd' => 'Also pull prereqs for development' ],
],
},
clean => {
summary => 'remove orphaned distribution archives',
usage_desc => '%c %o',
opt_spec => [],
},
commands => {
pos => 10,
summary => q[list the application's commands],
usage_desc => '%c %o',
opt_spec => [],
},
copy => {
summary => 'create a new stack by copying another',
usage_desc => '%c %o FROM-STACK TO-STACK',
args => 'stack to_stack',
opt_spec => [
[ 'default' => 'Make the new stack the default stack' ],
[ 'description|d=s' => 'Brief description of the stack' ],
[ 'lock' => 'Lock the new stack to prevent changes' ],
],
},
default => {
summary => 'mark the default stack',
usage_desc => '%c %o (--none | STACK)',
args => sub {
my ($opts, $usage) = splice @_, 0, 2;
$usage->die({ pre_text => "You must specify either a stack or --none\n\n" })
if !@_ && !$opts->{none};
$usage->die({ pre_text => "You cannot specify both a stack and --none\n\n" })
if @_ && $opts->{none};
$usage->die({ pre_text => "Too many arguments\n\n" })
if @_ > 1;
return @_ ? (stack => $_[0]) : ();
},
opt_spec => [
[ 'none' => 'Unmark the default stack' ],
],
},
delete => {
summary => 'permanently remove an archive',
usage_desc => '%c %o TARGET...',
args => 'targets*',
opt_spec => [
[ 'force' => 'Delete even if packages are pinned' ],
],
},
diff => {
summary => 'show difference between two stacks',
usage_desc => '%c %o [LEFT] RIGHT',
args => 'left? right',
opt_spec => [
[ 'diff-style=s' => 'Diff style (concise|detailed)' ],
[ 'format=s' => 'Format specification' ],
],
},
help => {
pos => 20,
summary => q[display a command's help screen],
usage_desc => '%c %o [SUBCOMMAND]...',
opt_spec => [],
},
install => {
summary => 'install stuff from the repository',
usage_desc => '%c %o TARGET...',
args => sub {
my ($opts) = splice @_, 0, 2;
if (my $cpanm_options = $opts->{cpanm_options}) {
my %new;
for my $item (@$cpanm_options) {
my ($name, $value) = $item =~ /\A--?(.+?)(?:=(.*?))?\z/ms;
$new{$name} = $value;
}
$opts->{cpanm_options} = \%new;
}
$opts->{cpanm_options}{'local-lib'} = $_ for grep defined, delete $opts->{local_lib};
$opts->{cpanm_options}{'local-lib-contained'} = $_ for grep defined, delete $opts->{local_lib_contained};
return targets => [@_];
},
opt_spec => [
[ 'cascade' => 'Always pick latest upstream package' ],
[ 'cpanm-exe|cpanm=s' => 'Path to the cpanm executable' ],
[ 'cpanm-options|o=s@' => 'name=value pairs of cpanm options' ],
[ 'diff-style=s' => 'Set style of diff reports' ],
[ 'local-lib|l=s' => 'install into a local lib directory' ],
[ 'local-lib-contained|L=s' => 'install into a contained local lib directory' ],
[ 'message|m=s' => 'Message to describe the change' ],
[ 'do-pull' => 'pull missing prereqs onto the stack first' ],
[ 'stack|s=s' => 'Install modules from this stack' ],
[ 'use-default-message|M' => 'Use the generated message' ],
],
},
kill => {
summary => 'permanently delete a stack',
usage_desc => '%c %o STACK',
args => 'stack',
opt_spec => [
[ 'force' => 'Kill even if stack is locked' ],
],
},
list => {
summary => 'show the packages in a stack',
usage_desc => '%c %o [STACK]',
args => 'stack?',
opt_spec => [
[ 'all|a' => 'List everything in the repository'],
[ 'authors|A=s' => 'Limit to matching author identities' ],
[ 'distributions|D=s' => 'Limit to matching distribution names' ],
[ 'packages|P=s' => 'Limit to matching package names' ],
[ 'pinned!' => 'Limit to pinned packages (negatable)' ],
[ 'format=s' => 'Format specification' ],
[ 'stack|s=s' => 'List contents of this stack' ],
],
},
lock => {
summary => 'mark a stack as read-only',
usage_desc => '%c %o [STACK]',
args => 'stack?',
opt_spec => [
[ 'stack|s=s' => 'Lock this stack' ],
],
},
log => {
summary => 'show the revision logs of a stack',
usage_desc => '%c %o [STACK]',
args => 'stack?',
opt_spec => [
[ 'stack|s=s' => 'Show history for this stack' ],
[ 'with-diffs|d' => 'Show a diff for each revision'],
[ 'diff-style=s' => 'Diff style (concise|detailed)' ],
],
},
look => {
summary => 'unpack and explore distributions with your shell',
usage_desc => '%c %o TARGET...',
args => 'targets+',
opt_spec => [
[ 'stack|s=s' => 'Resolve targets against this stack' ],
],
},
new => {
summary => 'create a new empty stack',
usage_desc => '%c %o STACK',
args => 'stack',
opt_spec => [
[ 'default' => 'Make the new stack the default stack' ],
[ 'description|d=s' => 'Brief description of the stack' ],
[ 'target-perl-version|tpv=s' => 'Target Perl version for this stack' ],
],
},
nop => {
summary => 'do nothing',
usage_desc => '%c %o',
opt_spec => [
[ 'sleep=i' => 'seconds to sleep before exiting' ],
],
},
pin => {
summary => 'force a package to stay in a stack',
usage_desc => '%c %o TARGET...',
args => 'targets*',
opt_spec => [
[ 'diff-style=s' => 'Set style of diff reports' ],
[ 'dry-run' => 'Do not commit any changes' ],
[ 'message|m=s' => 'Message to describe the change' ],
[ 'stack|s=s' => 'Pin targets to this stack' ],
[ 'use-default-message|M' => 'Use the generated message' ],
],
},
props => {
summary => 'show or set stack properties',
usage_desc => '%c %o [STACK]',
args => 'stack?',
opt_spec => [
[ 'format=s' => 'Format specification' ],
[ 'properties|prop|P=s%' => 'name=value pairs of properties' ],
],
},
pull => {
summary => 'pull archives from upstream repositories',
usage_desc => '%c %o TARGET...',
args => 'targets*',
opt_spec => [
[ 'cascade' => 'Always pick latest upstream package' ],
[ 'diff-style=s' => 'Set style of diff reports' ],
[ 'dry-run' => 'Do not commit any changes' ],
[ 'message|m=s' => 'Message to describe the change' ],
[ 'no-fail' => 'Do not fail when there is an error' ],
[ 'recurse!' => 'Recursively pull prereqs (negatable)' ],
[ 'pin' => 'Pin the packages to the stack' ],
[ 'skip-missing-prerequisite|k=s@' => 'Skip missing prereq (repeatable)' ],
[ 'skip-all-missing-prerequisites|K' => 'Skip all missing prereqs' ],
[ 'stack|s=s' => 'Put packages into this stack' ],
[ 'use-default-message|M' => 'Use the generated message' ],
[ 'with-development-prerequisites|wd' => 'Also pull prereqs for development' ],
],
},
register => {
summary => 'put existing packages on a stack',
usage_desc => '%c %o TARGET...',
args => 'targets*',
opt_spec => [
[ 'diff-style=s' => 'Set style of diff reports' ],
[ 'dry-run' => 'Do not commit any changes' ],
[ 'message|m=s' => 'Message to describe the change' ],
[ 'pin' => 'Pin packages to the stack' ],
[ 'stack|s=s' => 'Remove packages from this stack' ],
[ 'use-default-message|M' => 'Use the generated message' ],
],
},
rename => {
summary => 'change the name of a stack',
usage_desc => '%c %o STACK TO-STACK',
args => 'stack to_stack',
opt_spec => [],
},
reset => {
summary => 'reset stack to a prior revision',
usage_desc => '%c %o [STACK] REVISION',
args => 'stack? revision',
opt_spec => [
[ 'force' => 'Reset even if revision is not ancestor' ],
[ 'stack|s=s' => 'Reset this stack' ],
],
},
revert => {
summary => 'revert stack to a prior revision',
usage_desc => '%c %o [STACK] REVISION',
args => 'stack? revision',
opt_spec => [
[ 'dry-run' => 'Do not commit any changes' ],
[ 'force' => 'Revert even if revision is not ancestor' ],
[ 'message|m=s' => 'Message to describe the change' ],
[ 'stack|s=s' => 'Revert this stack' ],
[ 'use-default-message|M' => 'Use the generated message' ],
],
},
roots => {
summary => 'show the roots of a stack',
usage_desc => '%c %o [STACK]',
args => 'stack?',
opt_spec => [
[ 'format=s' => 'Format specification' ],
[ 'stack|s=s' => 'Show roots of this stack' ],
],
},
stacks => {
summary => 'show available stacks',
usage_desc => '%c %o',
opt_spec => [
[ 'format=s' => 'Format of the listing' ],
],
},
statistics => {
summary => 'report statistics about the repository',
usage_desc => '%c %o [STACK]',
args => 'stack?',
opt_spec => [],
},
unlock => {
summary => 'mark a stack as writable',
usage_desc => '%c %o [STACK]',
args => 'stack?',
opt_spec => [
[ 'stack|s=s' => 'Unlock this stack' ],
],
},
unpin => {
summary => 'free packages that have been pinned',
usage_desc => '%c %o TARGET...',
args => 'targets*',
opt_spec => [
[ 'diff-style=s' => 'Set style of diff reports' ],
[ 'dry-run' => 'Do not commit any changes' ],
[ 'message|m=s' => 'Message to describe the change' ],
[ 'stack|s=s' => 'Unpin targets from this stack' ],
[ 'use-default-message|M' => 'Use the generated message' ],
],
},
unregister => {
summary => 'remove packages from a stack',
usage_desc => '%c %o TARGET...',
args => 'targets*',
opt_spec => [
[ 'diff-style=s' => 'Set style of diff reports' ],
[ 'dry-run' => 'Do not commit any changes' ],
[ 'force' => 'Remove packages even if pinned' ],
[ 'message|m=s' => 'Message to describe the change' ],
[ 'stack|s=s' => 'Remove packages from this stack' ],
[ 'use-default-message|M' => 'Use the generated message' ],
],
},
update => {
summary => 'update packages to latest versions',
usage_desc => '%c %o TARGET...',
args => 'targets*',
opt_spec => [
[ 'all' => 'Update all packages in the stack' ],
[ 'cascade' => 'Always pick latest upstream package' ],
[ 'diff-style=s' => 'Set style of diff reports' ],
[ 'dry-run' => 'Do not commit any changes' ],
[ 'force' => 'Force update, even if pinned' ],
[ 'message|m=s' => 'Message to describe the change' ],
[ 'no-fail' => 'Do not fail when there is an error' ],
[ 'recurse!' => 'Recursively pull prereqs (negatable)' ],
[ 'pin' => 'Pin the packages to the stack' ],
[ 'roots' => 'Update all root packages in the stack' ],
[ 'skip-missing-prerequisite|k=s@' => 'Skip missing prereq (repeatable)' ],
[ 'skip-all-missing-prerequisites|K' => 'Skip all missing prereqs' ],
[ 'stack|s=s' => 'Update packages in this stack' ],
[ 'use-default-message|M' => 'Use the generated message' ],
[ 'with-development-prerequisites|wd' => 'Also pull prereqs for development' ],
],
},
verify => {
summary => 'report archives that are missing',
usage_desc => '%c %o',
opt_spec => [],
},
);
for my $cmd (keys %ret) {
$ret{$cmd}{usage_desc} =~ s/^%c\K/ $cmd/;
$ret{$cmd}{usage_desc} .= " - $ret{$cmd}{summary}";
}
return \%ret;
}
sub command_alias {
+{
cp => 'copy',
del => 'delete',
history => 'log',
ls => 'list',
mv => 'rename',
remove => 'delete',
rm => 'delete',
stats => 'statistics',
up => 'update',
};
}
sub global_opt_spec {
return (
[ 'root|r=s' => 'Path to your repository root directory (required)' ],
[ 'color|colour!' => '(Currently ignored)' ],
[ 'password|p=s' => 'Password for server authentication' ],
[ 'quiet|q' => 'Only report fatal errors' ],
[ 'username|u=s' => 'Username for server authentication' ],
[ 'verbose|v+' => 'More diagnostic output (repeatable)' ],
[],
[ 'help|?' => 'Print usage message and exit', { shortcircuit => 1 }],
);
}
sub help_summary {
my ($class) = @_;
my %command_info = %{ $class->command_info };
my $len = max(map length, keys %command_info);
( run in 1.575 second using v1.01-cache-2.11-cpan-9581c071862 )