Search code examples
phpdockerdocker-composecodeception

Getting failed loading params from .env vlucas/phpdotenv library required, but library is in


When I try to run my PHP Codeception test environment through Docker in my project, I get the following error:

 docker-compose run --rm codecept run ui_automation 
 SignupCest --env staging --debug
 Starting qa-end-to-end-tests_db_1 ... done
 Creating qa-end-to-end-tests_codecept_run ... done

 ==== Redirecting to Composer-installed version in 
 vendor/codeception. You can skip this using --no- 
 redirect ====
 Command "run ui_automation" is not defined.

So, I run the same command with --no-redirect and I get this error:

In ParamsLoader.php line 51:
                                                            
Failed loading params from .env                                    
`vlucas/phpdotenv` library is required to parse .env 
files.        
Please install it via composer: composer require 
vlucas/phpdotenv  

So I run composer "require vlucas/phpdotenv" and get that it's already installed, which I believe is correct:

 ~/git/qa-end-to-end-tests $ composer require 
 vlucas/phpdotenv
 Using version ^5.2 for vlucas/phpdotenv
 ./composer.json has been updated
 Running composer update vlucas/phpdotenv
 Loading composer repositories with package information
 Updating dependencies
 Nothing to modify in lock file
 Installing dependencies from lock file (including 
 require-dev)
 Nothing to install, update or remove
 Package container-interop/container-interop is 
 abandoned, you should avoid using it. Use psr/container 
 instead.
 Generating autoload files
 45 packages you are using are looking for funding.
 Use the `composer fund` command to find out more!

I say it's already installed because this is what's in my composer.json:

{
"require-dev": {
    "codeception/robo-paracept": "^0.4.2",
    "codeception/codeception": "^4.1.9",
    "codeception/module-phpbrowser": "^1.0.0",
    "codeception/module-asserts": "^1.0.0",
    "codeception/module-webdriver": "^1.1",
    "phpunit/phpunit": "^9.4"
},
"require": {
    "ext-zip": "^1.15",
    "guzzlehttp/guzzle": "^7.2",
    "vlucas/phpdotenv": "^5.2"
},
"autoload": {
    "psr-4": {
        "Tests\\Support\\": "tests/_support",
        "UiAutomationTester\\": " 
"tests/_support/UiAutomation.php"
    }
}
}

And there's a vlucas/photoenv folder in my /vendor folder

My environment is as such:

php 7.4.12 
Mac OS Catalina 10.15.6
Composer 2.0.5
Codecept 4.1.11
Docker 19.03.13   
Docker compose 1.27.4,

This started happening when a dev uploaded a new .env file, but it is working on everyone's machine except mine. I have tried uninstalling and reinstalling docker and uninstalling and reinstalling composer. This is my first PHP work project, I'm not very familiar with PHP in general.

My docker yaml:

version: '3'
services:
codecept:
image: codeception/codeception
depends_on:
  selenium-hub:
    condition: service_healthy
  chrome:
    condition: service_healthy
  web:
    condition: service_started
volumes:
  - .:/project
web:
image: php:7-apache
depends_on:
  - db
volumes:
  - .:/var/www/html
db:
image: percona:5.6
selenium-hub:
image: selenium/hub
ports:
  - "4444:4444"
environment:
  GRID_MAX_SESSION: 1
  GRID_BROWSER_TIMEOUT: 3000
  GRID_TIMEOUT: 3000
healthcheck:
    test: ["CMD", "curl", "-f", 
"http://localhost:4444/grid/api/proxy"] 
    interval: 10s
    timeout: 10s
    retries: 6
    start_period: 10s
chrome:
image: selenium/node-chrome-debug
container_name: web-automation_chrome
depends_on:
  - selenium-hub
environment:
  HUB_PORT_4444_TCP_ADDR: selenium-hub
  HUB_PORT_4444_TCP_PORT: 4444
  NODE_MAX_SESSION: 1
  NODE_MAX_INSTANCES: 1
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:5555"]
  interval: 10s
  timeout: 10s
  retries: 6
  start_period: 10s
volumes:
  - /dev/shm:/dev/shm
ports:
  - "5900:5900"
links:
  - selenium-hub

Solution

  • The reason this is happening despite the dependency being required in the composer.json file is that this is the composer.json file of your project, and not the one Codeception uses. Codeception is installed into its Docker image separately into /codecept, has its own vendor, and its own dependencies.

    I went around this problem by extending the native image, and adding the necessary deps myself.

    FROM codeception/codeception:4.1.21
    
    RUN --mount=type=ssh rm composer.lock; \
        set -eux; \
        composer require \
            vlucas/phpdotenv:^5 \
        composer update --no-dev --prefer-dist --no-interaction --optimize-autoloader --apcu-autoloader
    

    What's Happening

    1. Use the official Codeception image. This is the last of the 4.x line, which I use because there isn't great support for CC5 yet.
    2. Enable SSH agent forwarding for this command in the build. This is necessary in order to avoid sharing sensitive info with the image, like SSH certificates which are often necessary for repo authentication.
    3. Remove the lockfile. Doing this means effectively installing all deps from scratch, without worrying about what is upgradable in the scope of present constraints.
    4. Ensure that the output of the subsequent command is as verbose as possible if someting goes wrong.
    5. Add the dependencies.
    6. Update everything in the way that's most optimized for production use.

    Further

    What if you want to use some additional extension or helper? Would you have to update and re-build your image every time? What if the helper is specific to the project, and lives in the same repo/package as the code you are going to test? For this, there's another way of loading dependencies.

    Codecption can be configured to use a bootstrap script every time it runs. This is just a PHP script, and therefore may load other files via e.g. require. These files may contain other scripts and classes. One of these scripts can be your project's Composer autoload.php file. That means you can add something like this to the bootstrap.php that is being used for your tests, assuming it lives 2 directories under your project root e.g. tests/acceptance:

    $projectDir = dirname(__FILE__, 2);
    require_once("$projectDir/vendor/autoload.php");
    

    From here on, the dependencies of your project will be available to any Codeception run that includes your bootstrap.