plenv-libdirs

A plenv plugin to add additional include directories to Perl.

This plugin sets the contents of file .perl-libdirs. It hooks into plenv-exec command and every time you run perl or any other command under plenv, plenv-libdirs uses the .perl-libdirs files to set the PERL5LIB environment variable.

plenv-libdirs makes use of .perl-libdirs files in the current working directory and every directory between it and root. Environment variable PERL5LIB has a list of paths separated (like in PATH) by a colon on Unixish platforms and by a semicolon on Windows (the proper path separator being given by the command perl -V:path_sep). When plenv-libdirs collects the paths from .perl-libdirs files, the order of the paths follows the order of the directories. The longer the path to .perl-libdirs file, the higher precedence in PERL5LIB.

Like environment variable PATH, Perl uses the paths in PERL5LIB in the order they appear. Likewise, the search paths in perl-libdirs files appear in the same order. Example: three projects in dir root: project-a has a dependency on utils and its test files have a dependency on testing-utils. Together, when working directory in /root/project-a, these would result in: PERL5LIB=/root/testing-utils/lib:/root/utils/lib

root: projects
|- .perl-libdirs: **/root/utils/lib**
|- project-a
|  |- .perl-libdirs: **/root/testing-utils/lib**
|  |- lib
|  |- t
|
|- utils
|  |- lib
|
|- testing-utils
   |- lib

Usage

$ plenv libdirs ../other-project
$ plenv libdirs
../other-project/lib
$ plenv libdirs --add /tmp/second-project
$ plenv libdirs
../other-project/lib:/tmp/second-project
$ plenv libdirs --rm ../other-project
$ plenv libdirs
/tmp/second-project
$ perl -M5.020 -Mstrict -W -e 'say $INC[0];'
/tmp/second-project
$ plenv libdirs --unset
$ plenv libdirs

GitHub

Download from GitHub: https://github.com/mikkoi/plenv-libdirs/

Dot Your Environment

Env::Dot

In the category of “scratching my itch”.

Background

An app’s config is everything that is likely to vary between deploys (staging, production, developer environments, etc). The Twelve-Factor App

Storing the often changing parts of configuration in environment variables is one of the principles of The Twelve-Factor App.

From this principle follows the need to store those environment variables and their values in an easily accessible way. Hence, every developer maintains his or her own project specific .env files next to the project files in the same directory where they are used, for instance, when running locally or testing locally.

Yet Another Dotenv Solution

As if we didn’t have these enough already…

What is different with this one, except the name Env::Dot?

Flexibility in input

  • .env files come in two formats: Shell compatible and Docker combatible. Env::Dot supports both.
  • If no .env file is present, then do nothing.
  • If your .env file is located in another path, not the current working directory, you can use the environment variable DOTENV_FILEPATHS to tell where your dotenv file is located. You can specify several file paths; just separate them by :. Dot::Env will load all the files in the order you specify them.

Flexibility in output

  • Just use Env::Dot in your program and your %ENV will grow with the variables defined in .env file.
  • There is also a command line executable, envdot, to read the .env file and write out commands to create the environment variables.
  • Command envdot can write the env vars in sh (sh/Bash/Zsh), csh (C shell/tcsh) and fish (Fish) shell formats.
  • Command envdot will by default also export variables but you can prevent this if you don’t want the variables to be present in subshells and programs. This would make the variables only local to your current shell.

Existing Environment Takes Precedence

Existing environment variables always take precedence to dotenv variables!

A dotenv variable (variable from a file) does not overwrite an existing environment variable. This is by design because a dotenv file is to augment the environment, not to replace it.

This means that you can override a variable in .env file by creating its counterpart in the environment. For instance:

unset VAR
echo "VAR='Good value'" >> .env
perl -e 'use Env::Dot; print "VAR:$ENV{VAR}\n";'
# VAR:Good value
VAR='Better value'; export VAR
perl -e 'use Env::Dot; print "VAR:$ENV{VAR}\n";'
# VAR:Better value

DotEnv File Meta Commands

The file: commands affect all rows following its use.

The var: commands affect only the subsequent variable definition. If there is another envdot command, the second overwrites the first and default values are applied again.

file:type

Changes how Env::Dot reads lines below from this commands. Default is:

# envdot (file:type=shell)
VAR="value"

Other possible value of file:type is plain. Docker is using these kinds of .env files. Variable name is followed by = and value is the rest of the row before linefeed.

# envdot (file:type=plain)
VAR=My var value

var:allow_interpolate

By default, when writing variable definitions for the shell, every variable is treated as static and surrounded with single quotation marks (‘) in Unix shell which means shell will read the variable content as is. By setting this to 1 or true, you allow shell to interpolate. This meta command is only useful when running envdot command to create variable definitions for eval command to read.

# envdot (var:allow_interpolate)
DYNAMIC_VAR="$(pwd)/${ANOTHER_VAR}"

Usage

use Env::Dot;
print $ENV{'VAR_DEFINED_IN_DOTENV_FILE'};

envdot

envdot is a shell command which translates the dotenv files into shell commands. The file .env is of course the default input.

envdot
# VAR='Good value'; export VAR

It has the following parameters:

—export, —no-export

Write commands to set variables for local shell or for exporting them. You usually want to export the variables to all subsequent programs and subshells, i.e. make them into environment variables.

Default: export

-s, —shell

Which shell (family) are you using? Supported: sh, csh, fish.

-e, —dotenv

Path to .env file.

Default: current directory .env

Installation

If you need to use the envdot command in a restricted environment, such as a docker image build, there is a FatPacked executable ready. Usable when using CPAN is overkill.

curl -LSs -o envdot https://raw.githubusercontent.com/mikkoi/env-dot/master/envdot.self-contained
chmod +x ./envdot

Or you can do this in a Dockerfile:

RUN curl -LSs -o /usr/local/bin/envdot \
    https://raw.githubusercontent.com/mikkoi/env-dot/master/envdot.self-contained
RUN chmod +x /usr/local/bin/envdot

There is no extra dependencies outside Perl’s standard distribution, so envdot is as lean as it can be. And Perl, of course, is present in every more or less standard Linux distribution.

Assert Your Environment

Env::Assert

In the category of "scratching my itch".

I was doing some data pipelining and dockerising my creation. And - as always - when testing and devving I forgot to set the right environment variables. And when container image gets passed around, the information about the required env settings will certainly get lost.

Here is something of a solution to that:

How to ensure you have the environment variables and values you need?

Here is a common sight:

$ PLAEC='Stockholm'
$ if [[ "$PLACE" == '' ]]; then echo "Normal OK"; fi
OK

... And the program fails with no errors!

Not quite what we want!

Another example, from a docker container image I created lately:

perl -Ilib bin/repos-gh-yaml.pl --verbose         \
    | perl -Ilib bin/repos-yaml-csv.pl --verbose  \
    | az storage blob upload --data @-            \
        --content-type 'text/csv'                 \
        --content-encoding 'UTF-8'                \
        --content-language 'en_US'                \
        --name "$blob_name"                       \
        --container "$CONTAINER_NAME"             \
        --account-name "$AZURE_STORAGE_ACCOUNT"   \
        --sas-token "$AZURE_STORAGE_SAS_TOKEN"

If the environment variables are wrongly set, or entirely unset, it won't become known until after the run has started. And it could take hours before it reaches that point.

What we need is a way to find out if the environment variables are what we assume them to be. This needs to be done in an easy way and right at the beginning of the run.

Environment Description to the Rescue

Package Env::Assert and the executable envassert that comes with it do just this.

envassert is a CLI command to assert that your environment variables match your environment description.

environment description is a way to describe which environment variables are required by your program.

environment description is written in a file. Default file name is .envdesc.

If you are in the habbit of using .env files anyway, .envdesc complements it. Commit your .envdesc file into your repository and it will act as a template for user to create his/her .env file which should not be committed into Git anyway.

.envdesc actually looks a lot like a .env file, except instead of defining variables and their content, it defines regular expressions which control the variables' content. These regexps are Perl's extended regular expressions (m/<regexp>/msx).

Example:

CONTAINER_NAME=^[a-z0-9-]{1,}$
AZURE_STORAGE_ACCOUNT=^[a-z0-9]{1,}$
AZURE_STORAGE_SAS_TOKEN=^[?].*$
GITHUB_TOKEN=^[[:word:]]{1,}$

In normal circumstances, envassert only verifies the variables that you specifically describe. If you want more control over your environment, there is the meta command envassert (opts: exact=1) which will make envassert also assert that the environment doesn't contain any unknown variables.

## envassert (opts: exact=1)
USER=^username$
HOME=^/home/username$
PATH=^/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin$

You can create an airtight environment description to verify environment variables in both test and production. Just run envassert as the first command during container execution.

This is what I do:

envassert --env-description /home/me/.envdesc \
    || ( echo 'Break execution ...' 1>&2 && exit 1 )

Installation

If I need to add envassert to a container image during build, there is a FatPacked executable ready for those cases when using CPAN is overkill.

I do this in Dockerfile!

RUN curl -LSs -o /usr/local/bin/envassert \
    https://raw.githubusercontent.com/mikkoi/env-assert/main/envassert.self-contained
RUN chmod +x /usr/local/bin/envassert

There is no extra dependencies outside Perl's standard distribution, so envassert is as lean as it can be.

Docker::Names::Random

If you are using Docker, you may have noticed that it creates random names for containers when you haven't provided any specific name. These names are a combination of an adjective and a proper name of an individual. The individuals are famous men and women picked from the history of scientific exploration and engineering.

This package allows you to use the same system in your own programs. You would get combinations like interesting_mendeleev, epic_engelbart, lucid_dhawan, recursing_cori, ecstatic_liskov and busy_ardinghelli.

The combination boring_wozniak is not allowed because Steve Wozniak is not boring. This same limitation exists in the original code.

SYNOPSIS

# As an object (if you create many, this is more efficient).
require Docker::Names::Random;

my $dnr = Docker::Names::Random->new();
my $random_name1 = $dnr->docker_name();

# As an imported function.
use Docker::Names::Random qw( docker_name );
# OR
use Docker::Names::Random qw( :all );

my $random_name2 = docker_name();

Git Repo in Shared Hosting #4 - Git Full Service Via SSH

In this fourth article we will now use SSH connection and SSH public keys to give access and also limit access to our repositories.

http://www.koivunalho.org/blogs/exercises-in-integration-and-delivery/private-repository-part-4.html