NAME

App::Actions::Docker::Variable - an environment variable for a container, kept off the command line

SYNOPSIS

Pass a variable to a container:

use App::Actions::Docker;
use App::Actions::Docker::Variable;

my $docker = App::Actions::Docker->new;
my $token  = App::Actions::Docker::Variable->new('TOKEN', $secret);

# The client is started as "docker run --rm -e TOKEN alpine env", and
# TOKEN is set in its environment only.
$docker->run('run', '--rm', '-e', $token, 'alpine', 'env');

Read its parts:

print $token->name, "\n";     # TOKEN
print $token->value, "\n";    # the secret

Print it, for a log (redact secrets first):

print "$token\n";             # TOKEN=the secret

DESCRIPTION

A program's command line is public. Every user of the computer can read it, for example with ps or from /proc/PID/cmdline. So a command like docker run -e TOKEN=s3cret ... shows the secret to everybody.

A program's environment is private. Only the same user (and root) can read it. The docker client accepts -e TOKEN with no value: it then copies the value from its own environment.

This class is how a job variable says "give me to docker in the private way". Put one after -e in the arguments of an App::Actions::Docker method. When the command runs, App::Actions::Docker replaces it with the bare name and puts the value in the client's environment for that one command. See "Job variables" in App::Actions::Docker for the cases where the variable must stay on the command line instead.

Everywhere else, the object behaves like the string NAME=VALUE: when you print it, join it, or compare it with eq.

METHODS

new

my $variable = App::Actions::Docker::Variable->new($name, $value);

Makes a variable.

ARGUMENTS

  • $name - the variable's name, for example TOKEN.

  • $value - its value. undef becomes the empty string.

RETURNS

The new object.

SIDE EFFECTS

None.

EXAMPLE

my $home = App::Actions::Docker::Variable->new('HOME_DIR', '/home/runner');

API SPECIFICATION

INPUT

{
	name  => { type => 'string', min => 1 },
	value => { type => 'string', optional => 1 },
}

OUTPUT

{ type => 'object', isa => 'App::Actions::Docker::Variable' }

MESSAGES

None.

name

my $name = $variable->name;

Returns the name given to "new".

API SPECIFICATION

INPUT

{}

OUTPUT

{ type => 'string' }

MESSAGES

None.

value

my $value = $variable->value;

Returns the value given to "new", or the empty string if it was undef.

API SPECIFICATION

INPUT

{}

OUTPUT

{ type => 'string' }

MESSAGES

None.

COMMON PITFALLS

  • Printing the object prints the secret. "$variable" is NAME=VALUE. Pass anything you log through "redact" in App::Actions::Docker first. The Docker object's own verbose and dry-run output already does this.

  • Only App::Actions::Docker hides the value. If you pass the object to system or to another module, it becomes the string NAME=VALUE and is on the command line again.

  • A plain string is never hidden. '-e', 'TOKEN=s3cret' is passed as it is. Only an object of this class is moved to the environment.

  • undef is not kept. new('X', undef)->value is the empty string, so a container cannot tell an undefined value from an empty one.

ENCODING

The name should be ASCII letters, digits and _, and must not start with a digit; any other name stays on the command line. The value may hold any text, including non-ASCII characters and emoji, but encode it to bytes (for example UTF-8 with Encode) before you pass it, because an environment and a command line hold bytes, not characters. A value with a NUL character cannot go in an environment, so it stays on the command line.

AUTHOR

Nigel Horne, njh@nigelhorne.com

FORMAL SPECIFICATION

This section describes each routine in the Z notation, a precise mathematical language for describing programs. You do not need to read it to use the module: everything here is also said in plain words in the sections above.

How to read it: a box names a routine. The lines above the middle rule declare the values it works with. A name ending in ? is an input, and a name ending in ! is an output. ΞVariable means the routine changes nothing. The lines below the rule are always true when the routine returns.

Variable ≙ [ name : seq CHAR; value : seq CHAR ]

new

┌─ New ─────────────────────────────────────────────
│ name?  : seq CHAR
│ value? : optional seq CHAR
│ v!     : Variable
├───────────────────────────────────────────────────
│ v!.name = name?
│ value? = ⊥ ⇒ v!.value = ⟨⟩
│ value? ≠ ⊥ ⇒ v!.value = value?
│ string(v!) = v!.name ⁀ ⟨'='⟩ ⁀ v!.value
└───────────────────────────────────────────────────

name

┌─ Name ────────────────────────────────────────────
│ ΞVariable
│ r! : seq CHAR
├───────────────────────────────────────────────────
│ r! = name
└───────────────────────────────────────────────────

value

┌─ Value ───────────────────────────────────────────
│ ΞVariable
│ r! : seq CHAR
├───────────────────────────────────────────────────
│ r! = value
└───────────────────────────────────────────────────

STATE DIAGRAM

A variable never changes after it is made. It has one state, and its only "transition" is how App::Actions::Docker uses it when a command runs:

   new($name, $value)
          |
          v
+------------------+
|     CREATED      |   name and value fixed for ever;
| (immutable)      |   "$v" reads as NAME=VALUE
+------------------+
          |
          | passed after -e to App::Actions::Docker->run/capture
          v
+------------------+  hideable name?   +-----------------------------+
|  CHECKED BY      |-------yes-------->| argv gets "NAME";            |
|  _hide_variables |                   | %ENV{NAME} = value while the |
+------------------+                   | client runs, then restored   |
          |                            +-----------------------------+
          no (client variable, bad name,
          |   case clash or NUL in value)
          v
+-----------------------------+
| argv gets "NAME=VALUE"      |
| (visible to every user)     |
+-----------------------------+

LICENSE AND COPYRIGHT

Copyright 2026 Nigel Horne.

This program is released under the GNU General Public License, version 2. See the LICENSE file that comes with this distribution. If you use it, please let me know.