NAME

Venus::Run - Run Class

ABSTRACT

Run Class for Perl 5

SYNOPSIS

package main;

use Venus::Run;

my $run = Venus::Run->new;

# bless({...}, 'Venus::Run')

DESCRIPTION

This package provides a modular command execution framework for Perl projects. It loads a configuration with commands, aliases, scripts, variables, and paths, and resolves them into full shell commands. This allows you to define reusable CLI behaviors using declarative config without writing wrappers or shell scripts. It supports layered configuration, caching, variable expansion, and recursive resolution, with support for custom flow control, Perl module injection, and user prompts. It also resets the PATH and PERL5LIB variables where appropriate. See vns for an executable file which loads this package and provides the CLI. See "FEATURES" for usage and configuration information.

Environment variables:

  • VENUS_FILE - Explicitly set the config file for Venus::Run (and vns).

  • VENUS_RUN_FILE - Alias for VENUS_FILE.

  • VENUS_RUN_CONFIG - Alias for VENUS_FILE.

  • VENUS_RUN_DEBUG - Enable debugging in Venus::Run (and vns).

ATTRIBUTES

This package has the following attributes:

cache

cache(hashref $data) (hashref)

The cache attribute is used to store resolved values and avoid redundant computation during command expansion.

Since 4.15

cache example 1
package main;

use Venus::Run;

my $run = Venus::Run->new;

my $cache = $run->cache;

# {}

config

config(hashref $data) (hashref)

The config attribute is used to store the configuration used to resolve commands, variables, paths, and other runtime behavior.

Since 4.15

config example 1
package main;

use Venus::Run;

my $run = Venus::Run->new;

my $config = $run->config;

# {...}

debug

debug(boolean $data) (boolean)

The debug attribute is used to determine whether to output additional content for the purpose of debugging command execution.

Since 4.15

debug example 1
package main;

use Venus::Run;

my $run = Venus::Run->new;

my $debug = $run->debug;

# false

handler

handler(coderef $data) (coderef)

The handler attribute holds the callback (i.e. coderef) invoked for each step or command returned for a resolved command or expression.

Since 4.15

handler example 1
package main;

use Venus::Run;

my $run = Venus::Run->new;

my $handler = $run->handler;

# sub {...}

INHERITS

This package inherits behaviors from:

Venus::Role::Utility

INTEGRATES

This package integrates behaviors from:

Venus::Role::Optional

METHODS

This package provides the following methods:

callback

callback(coderef $code, any @args) (any)

The callback method executes a against each fully-resolved command derived from the given arguments. This method prepares the runtime environment by expanding variables, updating paths, and loading libraries as defined in the config. It resolves the given arguments into executable commands and passes each one to the callback in sequence. The callback receives the resolved program name followed by its arguments. Environment variables are restored to their original state after execution. Returns the result of the last successful callback execution, or undef if none were executed.

Since 4.15

callback example 1
package main;

use Venus::Run;

my $run = Venus::Run->new;

my $callback = $run->callback;

# undef
callback example 2
package main;

use Venus::Run;

my $run = Venus::Run->new;

my $data;

$run->config({
  exec => {
    info => 'perl -V',
  },
  libs => [
    '-Ilib',
    '-Ilocal/lib/perl5',
  ],
  perl => {
    perl => 'perl',
  },
});

my $callback = $run->callback(sub{join ' ', @_}, 'info');

# perl -Ilib -Ilocal/lib/perl5 -V

execute

execute(any @args) (any)

The execute method "resolves" the argument(s) provided and executes "callback" using the "handler" for each fully-resolved command encountered.

Since 4.15

execute example 1
package main;

use Venus::Run;

my $run = Venus::Run->new;

my $execute = $run->execute;

# undef
execute example 2
package main;

use Venus::Run;

my $run = Venus::Run->new;

$run->handler(sub{join ' ', @_});

my $execute = $run->execute('perl');

# ['perl']
execute example 3
package main;

use Venus::Run;

my $run = Venus::Run->new;

$run->config({
  exec => {
    info => 'perl -V',
  },
  libs => [
    '-Ilib',
    '-Ilocal/lib/perl5',
  ],
  perl => {
    perl => 'perl',
  },
});

$run->handler(sub{join ' ', @_});

my $execute = $run->execute('info');

# ['perl', "'-Ilib'", "'-Ilocal/lib/perl5'", "'-V'"]

new

new(any @args) (Venus::Run)

The new method constructs an instance of the package.

Since 4.15

new example 1
package main;

use Venus::Run;

my $run = Venus::Run->new;

# bless({...}, 'Venus::Run')
new example 2
package main;

use Venus;
use Venus::Run;

my $run = Venus::Run->new(debug => true);

# bless({...}, 'Venus::Run')
new example 3
package main;

use Venus;
use Venus::Run;

my $run = Venus::Run->new(debug => false, handler => sub {});

# bless({...}, 'Venus::Run')

resolve

resolve(hashref $config, any @data) (arrayref)

The resolve method expands a given item or command by recursively resolving aliases, variables, and configuration entries into a full command string or array. This method returns a list in list context.

Since 4.15

resolve example 1
package main;

use Venus::Run;

my $run = Venus::Run->new;

my $resolve = $run->resolve;

# []
resolve example 2
package main;

use Venus::Run;

my $run = Venus::Run->new;

my $resolve = $run->resolve({}, 'perl');

# [['perl']]
resolve example 3
package main;

use Venus::Run;

my $run = Venus::Run->new;

my $config = {find => {perl => '/path/to/perl'}};

my $resolve = $run->resolve($config, 'perl -c');

# [['/path/to/perl', "'-c'"]]
resolve example 4
package main;

use Venus::Run;

my $run = Venus::Run->new;

my $config = {
  exec => {
    info => 'perl -V',
  },
  libs => [
    '-Ilib',
    '-Ilocal/lib/perl5',
  ],
  perl => {
    perl => 'perl',
  },
};

my $resolve = $run->resolve($config, 'info');

# [['perl', "'-Ilib'", "'-Ilocal/lib/perl5'", "'-V'"]]
resolve example 5
package main;

use Venus::Run;

my $run = Venus::Run->new;

my $config = {
  exec => {
    info => 'perl -V',
  },
  libs => [
    '-Ilib',
    '-Ilocal/lib/perl5',
  ],
  load => [
    '-MVenus',
  ],
  perl => {
    perl => 'perl',
  },
};

my $resolve = $run->resolve($config, 'info');

# [['perl', "'-Ilib'", "'-Ilocal/lib/perl5'", "'-MVenus'", "'-V'"]]
resolve example 6
package main;

use Venus::Run;

my $run = Venus::Run->new;

my $config = {
  exec => {
    repl => '$REPL',
  },
  libs => [
    '-Ilib',
    '-Ilocal/lib/perl5',
  ],
  load => [
    '-MVenus',
  ],
  perl => {
    perl => 'perl',
  },
  vars => {
    REPL => 'perl -dE0',
  },
};

my $resolve = $run->resolve($config, 'repl');

# [['perl', "'-Ilib'", "'-Ilocal/lib/perl5'", "'-MVenus'", "'-dE0'"]]
resolve example 7
package main;

use Venus::Run;

my $run = Venus::Run->new;

my $config = {
  exec => {
    eval => 'shim -E',
    says => 'eval "map log(eval), @ARGV"',
    shim => '$PERL -MVenus=true,false,log',
  },
  libs => [
    '-Ilib',
    '-Ilocal/lib/perl5',
  ],
  perl => {
    perl => 'perl',
  },
  vars => {
    PERL => 'perl',
  },
};

my $resolve = $run->resolve($config, 'says', 1);

# [['perl', "'-Ilib'", "'-Ilocal/lib/perl5'", "'-MVenus=true,false,log'", "'-E'", "\"map log(eval), \@ARGV\"", "'1'"]]
resolve example 8
package main;

use Venus::Run;

my $run = Venus::Run->new;

my $config = {
  exec => {
    cpan => 'cpanm -llocal -qn',
  },
};

my $resolve = $run->resolve($config, 'cpan', 'Venus');

# [['cpanm', "'-llocal'", "'-qn'", "'Venus'"]]
resolve example 9
package main;

use Venus::Run;

my $run = Venus::Run->new;

my $config = {
  exec => {
    test => '$PROVE',
  },
  libs => [
    '-Ilib',
    '-Ilocal/lib/perl5',
  ],
  perl => {
    perl => 'perl',
    prove => 'prove',
  },
  vars => {
    PROVE => 'prove -j8',
  },
};

my $resolve = $run->resolve($config, 'test', 't');

# [['prove', "'-Ilib'", "'-Ilocal/lib/perl5'", "'-j8'", "'t'"]]
resolve example 10
package main;

use Venus::Run;

my $run = Venus::Run->new;

my $config = {};

my $resolve = $run->resolve($config, 'echo 1 | less');

# [['echo', "'1'", '|', "'less'"]]
resolve example 11
package main;

use Venus::Run;

my $run = Venus::Run->new;

my $config = {};

my $resolve = $run->resolve($config, 'echo 1 && echo 2');

# [['echo', "'1'", '&&', 'echo', "'2'"]]
resolve example 12
package main;

use Venus::Run;

my $run = Venus::Run->new;

my $config = {};

my $resolve = $run->resolve($config, 'echo 1 || echo 2');

# [['echo', "'1'", '||', 'echo', "'2'"]]
resolve example 13
package main;

use Venus::Run;

my $run = Venus::Run->from_file('t/conf/from.perl');

# in config
#
# ---
# from:
# - /path/to/parent
#
# ...
#
# exec:
#   mypan: cpan -M https://pkg.myapp.com

# in config (/path/to/parent)
#
# ---
# exec:
#   cpan: cpanm -llocal -qn
#
# ...

my $config = $run->prepare_conf($run->config);

my $resolve = $run->resolve($config, 'mypan');

# [['cpanm', "'-llocal'", "'-qn'", "'-M'", "'https://pkg.myapp.com'"]]
resolve example 14
package main;

use Venus::Run;

my $run = Venus::Run->from_file('t/conf/func.perl');

# in config
#
# ---
# with:
#   psql: /path/to/other
#
# ...

# in config (/path/to/other)
#
# ---
# exec:
#   backup: pg_backupcluster
#   restore: pg_restorecluster
#
# ...

my $config = $run->prepare_conf($run->config);

my $resolve = $run->resolve($config, 'psql', 'backup');

# [['pg_backupcluster']]
resolve example 15
package main;

use Venus::Run;

my $run = Venus::Run->from_file('t/conf/psql.perl');

# in config
#
# ---
# exec:
#   backup: pg_backupcluster
#   restore: pg_restorecluster
#
# ...

my $config = $run->prepare_conf($run->config);

my $resolve = $run->resolve($config, 'backup');

# [['pg_backupcluster']]
resolve example 16
package main;

use Venus::Run;

my $run = Venus::Run->from_file('t/conf/flow.perl');

# in config
#
# ---
# exec:
#   cpan: cpanm -llocal -qn
#
# ...
#
# flow:
#   setup-term:
#   - cpan Term::ReadKey
#   - cpan Term::ReadLine::Gnu
#
# ...

my $config = $run->prepare_conf($run->config);

my $resolve = $run->resolve($config, 'setup-term');

# [
#   ['cpanm', "'-llocal'", "'-qn'", "'Term::ReadKey'"],
#   ['cpanm', "'-llocal'", "'-qn'", "'Term::ReadLine::Gnu'"],
# ]
resolve example 17
package main;

use Venus::Run;

my $run = Venus::Run->from_file('t/conf/asks.perl');

# in config
#
# ---
# asks:
#   PASS: What's the password
#
# ...

my $config = $run->prepare_vars($run->prepare_conf($run->config));

my $resolve = $run->resolve($config, 'echo', '$PASS');

# [['echo', "'secret'"]]
resolve example 18
package main;

use Venus::Run;

my $run = Venus::Run->from_file('t/conf/with.perl');

# in config
#
# ---
# with:
#   psql: /path/to/other
#
# ...

# in config (/path/to/other)
#
# ---
# exec:
#   backup: pg_backupcluster
#   restore: pg_restorecluster
#
# ...

my $config = $run->prepare_conf($run->config);

my $resolve = $run->resolve($config, 'psql', 'backup');

# [['pg_backupcluster']]
resolve example 19
package main;

use Venus::Run;

my $run = Venus::Run->from_file('t/conf/when.perl');

# in config
#
# ---
# exec:
#   name: echo $OSNAME
#
# ...
# when:
#   is_lin:
#     data:
#       OSNAME: LINUX
#   is_win:
#     data:
#       OSNAME: WINDOWS
#
# ...

# assume Linux OS

my $config = $run->prepare_vars($run->prepare_conf($run->config));

my $resolve = $run->resolve($config, 'name');

# [['echo', "'LINUX'"]]
resolve example 20
package main;

use Venus::Run;

my $run = Venus::Run->from_file('t/conf/when.perl');

# in config
#
# ---
# exec:
#   name: echo $OSNAME
#
# ...
# when:
#   is_lin:
#     data:
#       OSNAME: LINUX
#   is_win:
#     data:
#       OSNAME: WINDOWS
#
# ...

# assume Windows OS

my $config = $run->prepare_vars($run->prepare_conf($run->config));

my $resolve = $run->resolve($config, 'name');

# [['echo', "'WINDOWS'"]]

result

result(any @args) (any)

The result method is an alias for the "execute" method, which executes the the "handler" for each fully-resolved command based on the arguments provided.

Since 4.15

result example 1
package main;

use Venus::Run;

my $run = Venus::Run->new;

my $result = $run->result;

# undef
result example 2
package main;

use Venus::Run;

my $run = Venus::Run->new;

$run->handler(sub{join ' ', @_});

my $result = $run->result('perl');

# ['perl']
result example 3
package main;

use Venus::Run;

my $run = Venus::Run->new;

$run->config({
  exec => {
    info => 'perl -V',
  },
  libs => [
    '-Ilib',
    '-Ilocal/lib/perl5',
  ],
  perl => {
    perl => 'perl',
  },
});

$run->handler(sub{join ' ', @_});

my $result = $run->result('info');

# ['perl', "'-Ilib'", "'-Ilocal/lib/perl5'", "'-V'"]

FEATURES

This package provides the following features:

config
config(hashref $data) (hashref)

The CLI provided by this package operates on a configuration file, typically having a base name of .vns with a Perl, JSON, or YAML file extension. Here is an example of a configuration file using YAML with the filename .vns.yaml.

---
exec:
  cpan: cpanm -llocal -qn
  okay: $PERL -c
  repl: $PERL -dE0
  says: $PERL -E "map log(eval), @ARGV"
  test: $PROVE
libs:
- -Ilib
- -Ilocal/lib/perl5
load:
- -MVenus=true,false
path:
- ./bin
- ./dev
- -Ilocal/bin
perl:
  perl: perl
  prove: prove
vars:
  PERL: perl
  PROVE: prove

Since 4.15

example 1

package main;

use Venus::Run;

my $run = Venus::Run->new;

my $config = $run->config;

# {...}
config: asks
---
asks:
  HOME: Enter your home dir

The configuration file's asks section provides a list of key/value pairs where the key is the name of the environment variable and the value is used as the message used by the CLI to prompt for input if the environment variable is not defined.

config: data
---
data:
  VENUS_DEBUG: true

The configuration file's data section provides a non-dynamic list of key/value pairs that will be used as environment variables.

config: exec
---
exec:
  okay: $PERL -c

The configuration file's exec section provides the main dynamic tasks which can be recursively resolved and expanded.

config: find
---
find:
  cpanm: /usr/local/bin/cpanm

The configuration file's find section provides aliases which can be recursively resolved and expanded for use in other tasks.

config: flow
---
flow:
  deps:
  - cpan Term::ReadKey
  - cpan Term::ReadLine::Gnu

The configuration file's flow section provides chainable tasks which are recursively resolved and expanded from other tasks.

config: from
---
from:
- /usr/share/vns/.vns.yaml

The configuration file's from section provides paths to other configuration files which will be merged before execution allowing the inheritance of of configuration values.

config: func
---
func:
  psql: ./psql-tools/.vns.yml

The configuration file's func section provides a list of static key/value pairs where the key is the "subcommand" passed to the runner as the first arugment, and the value is the configuration file where the subcommand task definitions are defined which the runner dispatches to. The differs from the with section commands in that the runner doesn't "chdir"|perlfunc/chdir into the configuration root before executing commands. When executed, the following environment variables are set.

Environment variables:

  • VENUS_RUN_FROM_ROOT - The $VENUS_FILE root directory.

  • VENUS_RUN_FROM_FILE - The $VENUS_FILE configuration file path.

  • VENUS_RUN_FUNC_ROOT - The "func" configuration file root directory.

  • VENUS_RUN_FUNC_FILE - The "func" configuration file path.

config: libs
---
libs:
- -Ilib
- -Ilocal/lib/perl5

The configuration file's libs section provides a list of -I/path/to/lib "include" statements that will be automatically added to tasks expanded from the perl section.

config: load
---
load:
- -MVenus=true,false

The configuration file's load section provides a list of -MPackage "import" statements that will be automatically added to tasks expanded from the perl section.

config: path
---
path:
- ./bin
- ./dev
- -Ilocal/bin

The configuration file's path section provides a list of paths to be prepended to the PATH environment variable which allows programs to be found.

config: perl
---
perl:
  perl: perl

The configuration file's perl section provides the dynamic perl tasks which can serve as tasks with default commands (with options) and which can be recursively resolved and expanded.

config: task
---
task:
  setup: $PERL -MMyApp::Task::Setup -E0 --

The configuration file's task section provides the dynamic perl tasks which "load" Venus::Task derived packages, and which can be recursively resolved and expanded. These tasks will typically take the form of perl -Ilib -MMyApp::Task -E0 -- and will be automatically executed as a CLI.

config: vars
---
vars:
  PERL: perl

The configuration file's vars section provides a list of dynamic key/value pairs that can be recursively resolved and expanded and will be used as environment variables.

config: when
---
when:
  is_lin:
    data:
      OSNAME: LINUX
  is_win:
    data:
      OSNAME: WINDOWS

The configuration file's when section provides a configuration tree to be merged with the existing configuration based on the name current operating system. The is_$name key should correspond to one of the types specified by "type" in Venus::Os.

config: with
---
with:
  psql: ./psql-tools/.vns.yml

The configuration file's with section provides a list of static key/value pairs where the key is the "subcommand" passed to the runner as the first arugment, and the value is the configuration file where the subcommand task definitions are defined which the runner dispatches to. The differs from the func section commands in that the runner will "chdir"|perlfunc/chdir into the configuration root before executing commands. When executed, the following environment variables are set.

Environment variables:

  • VENUS_RUN_FROM_ROOT - The $VENUS_FILE root directory.

  • VENUS_RUN_FROM_FILE - The $VENUS_FILE configuration file path.

  • VENUS_RUN_WITH_ROOT - The "with" configuration file root directory.

  • VENUS_RUN_WITH_FILE - The "with" configuration file path.

runner: vns

Here are example usages of the configuration file mentioned, executed by the vns CLI, which is an executable file which loads this package.

# Mint a new configuration file
vns new pl

# Mint a new JSON configuration file
vns new json

# Mint a new YAML configuration file
vns new yaml

# Install a distribution
vns cpan $DIST

# i.e.
# cpanm --llocal -qn $DIST

# Check that a package can be compiled
vns okay $FILE

# i.e.
# perl -Ilib -Ilocal/lib/perl5 -c $FILE

# Use the Perl debugger as a REPL
vns repl

# i.e.
# perl -Ilib -Ilocal/lib/perl5 -dE0

# Evaluate arbitrary Perl expressions
vns exec ...

# i.e.
# perl -Ilib -Ilocal/lib/perl5 -MVenus=log -E $@

# Test the Perl project in the CWD
vns test t

# i.e.
# prove -Ilib -Ilocal/lib/perl5 t

AUTHORS

Awncorp, awncorp@cpan.org

LICENSE

Copyright (C) 2022, Awncorp, awncorp@cpan.org.

This program is free software, you can redistribute it and/or modify it under the terms of the Apache license version 2.0.