This article is part of my #100DaysOfCode and #100DaysOfBlogging challenge. R1D5


Yesterday I ran into an issue where the build process of an application depended on a proper configured application. There is a distributed configuration file. But for the build process to run successfully, the distributed file has to be copied (and if necessary adjusted) manually.

I see some issues with that.

  1. Build has a dependency on configuration. The build process for an application should stay the same. It must not produce a different outcome depending on the local setup.
  2. Build process will not run on CI/CD. In this setup the application will never be run in CI/CD.
  3. Repetitive tasks. Why should every developer manage configuration, if it can be automated? Automation will lower risk of human error and speed up the process. On the topic of automating things, I can recommend following Ryan Bartram, the author of Don’t forget to automate it. I got the chance to get to know him on the Global Azure Bootcamp, great and funny guy!

Proper way

The proper way to fix this, is to address the author of the abomination feature and improve the build process. This is what I will do.

A connected issue

While solving the issue of getting the build to work, I was thinking about the application setup. Most of our projects require local configuration before they are able to run. If you’re not familiar with an application, it can be a tedious task to find out all requirements and dependencies. Oftentimes we end up sharing our local configuration files. ¯\_(ツ)\_/¯

But what if you could checkout a project which would run out-of-the-box? For development you typically would use virtualization with a configured machine/container laying in the repository, boot up the machine and voilà.

Small steps

Before configuring virtualizations, I wanted to make a smaller step which I and others could use instantly. The idea was to automate the process of copying required configuration files. Some manual steps like database credentials would still be required, but at least the configuration files/templates exist.

Configuration types

There are different types of configurations.

  1. Global - The application is dependent on it. This could be a default locale, template paths and so on. These settings are committed to the VCS.
  2. Local - Database credentials etc. The application depends on them. However, they are not committed to VCS, because they differ from environment to environment and/or they could be sensitive data.
  3. Development - Settings that are only required in a development environment. That is displaying debugging information, a turned off cache etc.

The first type is already in the VCS and does not require local adjustments. The local configuration is what really is interesting. The application depends on it, but the configuration may only exist as a distributed configuration file. The development configuration could potentially be automated as well, but this will not be in today’s scope.

Composer scripts FTW

Very quickly I found a solution by putting Composer scripts to work. At first I put a PHP command directly in the scripts section of the composer.json. However, that just didn’t feel right. It was very hard to read and maintain. So, I defined a callback.

{
  // [..]
  "scripts": {
    "post-install-cmd": ["App\\ComposerScripts::postInstall"]
    // [..]
  }
}

Next, I implemented the referenced ComposerScripts to copy the distributed configuration files, if no local copy exists.

<?php

declare(strict_types = 1);

namespace App;

class ComposerScripts
{
    private static $configurationFiles = [
        'config/autoload/services.local.php.dist' => 'config/autoload/services.local.php',
        'config/autoload/doctrine.local.php.dist' => 'config/autoload/doctrine.local.php',
    ];

    public static function postInstall() : void
    {
        foreach (static::$configurationFiles as $distributed => $destination) {
            if (! file_exists($destination)) {
                copy($distributed, $destination);
            }
        }
    }
}

Beautiful. It works like a charm.

This for sure is a poor man’s solution, quick and dirty. In the sense of Kaizen, it is a change for the better and will continue to improve.