Colored app environment in docker shell

When running a shell in a docker container, you only see random hashes as hostname:

$ docker exec -it project_backend_1 bash
root@112adda3eb64:/#

Now imagine having a dozen of terminals open, and then you run ./vendor/bin/phpunit in container 71f68dcd5379. The first thing that the PHPUnit bootstrap script does is emptying the database and then running all migrations and seeds.

Unfortunately, you intendet to run that command in 112adda3eb64, your local development container. Let's just say that 71f68dcd5379 was not the the local dev one, but on a server in a data center, and the data thrown away were kind of important.

Show the environment

To prevent such mistakes in the future, the shell shall clearly show which environment you are in - local development, testing, staging or production.

APP_ENV

This environment is available in our Laravel .env file, but it's not so easy to access in the terminal. So the first step is to add the current environment in the docker-compose.yml file:

---
version: "3"

services:

  backend:
    image: docker-hub.example.org/project/backend-dev:latest
    environment:
      - APP_ENV=local

Now we can access this variable in our shell via $APP_ENV.

Bash prompt

The bash prompt $PS1 is set in two places in the Ubuntu 16.04 images that we used:

/etc/bash.bashrc
Loaded when bash is used, no matter which user
/root/.bashrc
Is loaded after the /etc/ version when the user is root.

Both files define $PS1, so we have to load our bash-coloring file in both of them:

Dockerfile
FROM ubuntu:xenial
ADD bash.colorprompt /etc/bash.colorprompt
RUN echo '. /etc/bash.colorprompt' >> /etc/bash.bashrc\
 && echo '. /etc/bash.colorprompt' >> /root/.bashrc

Now the only thing left is to write that file that sets the prompt:

bash.colorprompt
# color the prompt according to $APP_ENV variable
case "$APP_ENV" in
    production)
        PS1='\e[41m\n=== $APP_ENV ===\e[m\n\u@\h:\w\$ '
        ;;
    testing)
        PS1='\e[43m$APP_ENV\e[m \u@\h:\w\$ '
        ;;
    local)
        PS1='\e[42m$APP_ENV\e[m \u@\h:\w\$ '
        ;;
esac

Screenshots

Different environments

Running docker exec (dbash)

PHPUnit?

The obvious question is why PHPUnit was available on that system in the first place.

Our CI server runs unit/integration tests on every deployment, no matter which environment is being deployed to:

  1. Build container with environment-specific configuration
  2. Run tests in container
  3. Deploy and start container on server

While this is in general a good idea, running the tests on the deployment to every environment is something we later stopped doing.

It turned out to be hard to make sure that every single configuration variable is overwritten in phpunit.xml. And if you can't be sure of this, your tests suddenly use some obscure production service that you forgot to stub out.

Written by Christian Weiske.

Comments? Please send an e-mail.