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.
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"
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.
The bash prompt $PS1 is set in two places in the Ubuntu 16.04 images that we used:
- Loaded when bash is used, no matter which user
- 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:
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:
# 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
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:
- Build container with environment-specific configuration
- Run tests in container
- 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.