Buy

vars_prompt & Environment Variables

We missed a deploy step! We're not clearing Symfony's cache. That sounds like a job for a new task! Call it "Clear Cache". It's easy: we just need to run a command: {{ symfony_console_path }} cache:clear --env=prod:

206 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 10
tasks:
... lines 12 - 188
- name: Clear cache
command: '{{ symfony_console_path }} cache:clear --env=prod'
... lines 191 - 206

Easy! Except... that --env=prod part is interesting. Sometimes I might want to deploy our app in the prod environment - like if we're deploying to production. But other times, like if we're deploying to some test machine, I might want to deploy our app in the dev environment.

What I'm saying is: I want that environment to be configurable. And actually, this is important in two places: here, and when installing the Composer dependencies.

Symfony Composer Deps in the prod Environment

Google for "Symfony deploy" to find a page on Symfony.com. Scroll down to a section about installing and updating your vendors. Ah, so it recommends that when you deploy, you use:

composer install --no-dev

That's actually what the composer module tried to do by default. But then we - via a no_dev option - told it to stop that nonsense!

206 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 10
tasks:
... lines 12 - 149
- name: Install Composer's dependencies
composer:
... line 152
no_dev: no
... lines 154 - 206

We had to do that because some of the Composer post-install commands in a Symfony app require the packages in the require-dev section.

In reality, this --no-dev flag is not a big deal. But, we can use it... as long as we set an environment variable: SYMFONY_ENV=prod. Yep, those problematic post-install commands are setup to look for this environment variable, and not to do certain things rely on the require-dev dependencies.

So this is our mission: make the environment configurable, use it in the "Clear Cache" task and set a new environment variable.

Prompting for Input?

How? Start at the top: add a new vars_prompt key with name set to symfony_env:

221 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 10
vars_prompt:
- name: symfony_env
... lines 13 - 221

Then, prompt: Enter the environment for your Symfony app (prod|dev|test):

221 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 10
vars_prompt:
- name: symfony_env
prompt: "Enter the environment for your Symfony app (prod|dev|test)"
... lines 14 - 221

As you're probably guessing, Ansible will now ask us what environment we want to use. Set a default value to prod and private: no - you can set that to yes to obscure passwords as you type them:

221 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 10
vars_prompt:
- name: symfony_env
prompt: "Enter the environment for your Symfony app (prod|dev|test)"
default: prod
private: no
... lines 16 - 221

Cool!

Setting an Environment Variable

Next question: how can we use this variable to set an environment variable? How about... an environment key! Yep, setting environment variables is a native task for Ansible. Set SYMFONY_ENV to {{ symfony_env|lower }}:

221 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 10
vars_prompt:
- name: symfony_env
... lines 13 - 16
environment:
SYMFONY_ENV: "{{ symfony_env|lower }}"
... lines 19 - 221

This uses the variable we just set... but pipes it through a lower filter... just in case we get crazy and use upper-case letters.

To see what this all looks like, at the top, let's debug some variables. First, debug one called ansible_env:

221 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 19
tasks:
- debug:
var: ansible_env
... lines 23 - 221

This is a built-in variable that has a lot of info about the "host" environment - meaning, the machine (or machines) that you're running Ansible against. It should also contain our environment variable.

Let's also debug the symfony_env variable that we set above:

221 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 19
tasks:
- debug:
var: ansible_env
- debug:
var: symfony_env
... lines 26 - 221

Oh, and down on the "Clear Cache" task, I forgot to add the tag for deploy:

221 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 19
tasks:
... lines 21 - 203
- name: Clear cache
command: '{{ symfony_console_path }} cache:clear --env=prod'
tags:
- deploy
... lines 208 - 221

Change over to your terminal and run the playbook - but take off the -t option so that everything runs:

ansible-playbook ansible/playbook.yml -i ansible/hosts.ini

Yes! Right at the start, it asks us for the environment. I'll leave it blank to use prod. Then, hit ctrl+c to quit: we can already see the variables!

First, it printed ansible_env... which has some pretty cool stuff! It has a HOME key for the home directory, PWD for the current directory and other goodies. AND, it has SYMFONY_ENV set to prod. Not surprisingly, the symfony_env variable also prints prod.

Try this again.. but be tricky... with an uppercase PROD:

ansible-playbook ansible/playbook.yml -i ansible/hosts.ini

Yep! The environment variable was lowercased, but not the symfony_env variable. That's no surprise... but if we want to guard against this, it will be a problem in a minute when we try to use this in more places... like down on my "Clear Cache" task:

221 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 19
tasks:
... lines 21 - 203
- name: Clear cache
command: '{{ symfony_console_path }} cache:clear --env=prod'
... lines 206 - 221

We could keep using the lower filter. But, there's a cooler way: a "pre task".

Leave a comment!

  • 2017-10-09 Victor Bocharsky

    Hey Juan,

    Don't worry too much about your previous comments, we'll keep them ;)

    Hm, it's a bit weird that fixtures broke your prod environment even when you do not use fixtures in prod at all. But I really glad you got it working!

    Cheers!

  • 2017-10-06 Juan Luis Garcia

    Victor Bocharsky First I want to thank you for all your help, I have solved the error

    The installation and configuration were right, but in DoctrineFixtures classes I was using a OrderedFixtureInterface old, at the current version is different....


    //https://symfony.com/doc/master/bundles/DoctrineFixturesBundle/index.html
    public function getDependencies()
    {
    return array(
    UserFixtures::class,
    );
    }

    I'm really grateful to you

    I will edit the first comment to report that the error is to another different problem than this lesson . Or if you wish, I can delete it so as not to confuse.

    Thanks!

  • 2017-10-06 Juan Luis Garcia

    HI Victor Bocharsky ,
    In local:
    - SYMFONY_ENV:
    In local I have defined SYMFONY_ENV=prod


    #~/.zshrc
    export SYMFONY_ENV="prod"


    When I run in my terminal$ echo $SYMFONY_ENV return prod

    - composer install --no-dev [local] ..... BUUM!!!!

    ...
    PHP Fatal error: Uncaught ReflectionException: Class Doctrine\Common\DataFixtures\AbstractFixture not found in /Users/juanluisgarciaborrego/Sites/project/src/AppBundle/DataFixtures/ORM/LoadUserData.php:10
    ...

    Fatal error: Uncaught ReflectionException: Class Doctrine\Common\DataFixtures\AbstractFixture not found in /Users/juanluisgarciaborrego/Sites/project/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/FileLoader.php on line 179
    ...

    [RuntimeException]
    An error occurred when executing the "'cache:clear --no-warmup'" command:
    Fatal error: Uncaught ReflectionException: Class Doctrine\Common\DataFixtures\AbstractFixture not found in /Users/juanluisgarciaborrego/Sites/project vendor/symfony/symfony/src/Symfony/Component/Config/Loader/FileLoader.php on line 179
    Symfony\Component\Config\Exception\FileLoaderLoadException: Class Doctrine\Common\DataFixtures\AbstractFixture not found in /Users/juanluisgarciaborrego/Sites/project/app/config/services.yml (which is being imported from "/Users/juanluisg
    arciaborrego/Sites/project/app/config/config.yml"). in /Users/juanluisgarciaborrego/Sites/project/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/FileLoader.php on line 179


    =(

    Thanks!!!

  • 2017-10-06 Victor Bocharsky

    Hey Juan,

    I agree, that sounds not quite right workflow. In prod environment you really need to skip loading fixtures, but you still need to clear/warm up the cache, but this time with "--env=prod" option. Also, for production you won't just update schema with bin/console doctrine:schema:update, you need to run migrations instead to prevent loosing data.

    Cheers!

  • 2017-10-06 Victor Bocharsky

    Also, let's see SYMFONY_ENV works well, try to follow a few steps:
    - SSH to AWS server
    - cd /to/project/dir
    - try again to install deps without importing SYMFONY_ENV, just execute: "composer install --no-dev"
    - On this step you should see the error you reported, right?
    - Now let's export prod environment var by executing: "export SYMFONY_ENV=prod"
    - And in the same terminal tab you exported SYMFONY_ENV, execute again: "composer install --no-dev"

    Do you still have that error? Or, does it work now?

    Cheers!

  • 2017-10-06 Victor Bocharsky

    Yes, this sounds correct if you register DoctrineFixturesBundle in AppKernel only for dev/test environments.

    Cheers!

  • 2017-10-06 Victor Bocharsky

    Hey Juan,

    Hm, are you sure you include DoctrineFixturesBundle namespace in AppKernel for dev/test environment only? And what about your local machine? Does it work locally when you execute "composer install --no-dev"?

    UPD: I can suggest you to try to upgrade Composer dependencies with "composer update". It will pull latest versions and regenerate composer.lock. Then, try to execute "composer install --no-dev" again.

    Cheers!

  • 2017-10-05 Juan Luis Garcia

    Victor,
    I'm trying and I think
    It's all ok.

    First I run ansible-playbook -i ......... with environment DEV, so it installs :
    - create BD
    - schema update
    - fixtures load

    That is good, the next, I run with environment PROD, I ansible is happy and skypped Load data fixtures and Warmup cache.

    But, this I think not is the workflow correct.

  • 2017-10-05 Juan Luis Garcia

    More info:
    In my LoadUser.php

    When I installed composer with --no-dev the namespaces next are wrong, because not exits Doctrine\Common\DataFixtures.

    use Doctrine\Common\DataFixtures\AbstractFixture;
    use Doctrine\Common\DataFixtures\OrderedFixtureInterface;

  • 2017-10-05 Juan Luis Garcia

    HeyVictor

    En aws server with ssh:
    When I try composer install (dev) is all ok.


    [OK] All assets were successfully installed.

    But when I try: composer install --no-dev


    //- other bundles ok
    - Installing willdurand/hateoas-bundle (1.3.0): Loading from cache
    Generating autoload files
    > Incenteev\ParameterHandler\ScriptHandler::buildParameters
    Updating the "app/config/parameters.yml" file
    > Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::buildBootstrap
    > Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::clearCache
    PHP Fatal error: Uncaught Symfony\Component\Debug\Exception\ClassNotFoundException: Attempted to load class "DoctrineFixturesBundle" from namespace "Doctrine\Bundle\FixturesBundle".
    Did you forget a "use" statement for another namespace? in /var/www/project/app/AppKernel.php:30
    Stack trace:
    #0 /var/www/project/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php(450): AppKernel->registerBundles()
    #1 /var/www/project/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php(116): Symfony\Component\HttpKernel\Kernel->initializeBundles()
    #2 /var/www/project/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php(137): Symfony\Component\HttpKernel\Kernel->boot()
    #3 /var/www/project/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php(124): Symfony\Bundle\FrameworkBundle\Console\Application->registerCommands()
    #4 /var/www/project/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php(90): Symfony\Bundle\FrameworkBundle\Console\Applica in /var/www/project/app/AppKernel.php on line 30
    Script Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::clearCache handling the symfony-scripts event terminated with an exception


    [RuntimeException]
    An error occurred when executing the "'cache:clear --no-warmup'" command:
    PHP Fatal error: Uncaught Symfony\Component\Debug\Exception\ClassNotFoundException: Attempted to load class "DoctrineFixturesBundle" from namespace "Doctrine\Bundle\FixturesBundle".
    Did you forget a "use" statement for another namespace? in /var/www/project/app/AppKernel.php:30
    Stack trace:
    #0 /var/www/project/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php(450): AppKernel->registerBundles()
    #1 /var/www/project/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php(116): Symfony\Component\HttpKernel\Kernel->initializeBundles()
    #2 /var/www/project/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php(137): Symfony\Component\HttpKernel\Kernel->boot()
    #3 /var/www/project/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php(124): Symfony\Bundle\FrameworkBundle\Console\Application->registerCommands()
    #4 /var/www/project/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php(90): Symfony\Bundle\FrameworkBundle\Console\Applica in /var/www/project/app/AppKernel.php on line 30


    install [--prefer-source] [--prefer-dist] [--dry-run] [--dev] [--no-dev] [--no-custom-installers] [--no-autoloader] [--no-scripts] [--no-progress] [--no-suggest] [-v|vv|vvv|--verbose] [-o|--optimize-autoloader] [-a|--classmap-authoritative] [--apcu-autoloader] [--ignore-platform-reqs] [--] [<packages>]...

    Thanks!

  • 2017-10-05 Victor Bocharsky

    Hey Juan,

    Hm, at the first sight, it's correct. Let's debug a bit more. Could you SSH to your server and run "composer install" manually? Do you have the same error? Also, you could try to always install dev deps with simply "no_dev: no". Does it fix the problem? Btw, could you try it manually locally? Are you sure you don't have the same problem and fixtures could be loaded successfully locally?

    Cheers!

  • 2017-10-05 Juan Luis Garcia

    Hi!

    I've a problem with environment the SYMFONY_ENV var. In my case I've called app_env instance of symfony_env. But when composer install --no-dev there is a error

    TASK [Install Composer's dependencies] ***********************
    \n An error occurred when executing the \"'cache:clear --no-warmup'\" command: \n PHP Fatal error: Uncaught ReflectionException: Class Doctrine\\Common\\DataFixtures\\AbstractFixture not found in /var/www/project/src/AppBundle/DataFixtures/ORM/LoadUserData.php:10

    My playbook:


    vars_prompt:
    - name: app_env
    prompt: "Enter the environment (prod|dev)"
    default: prod
    private: no

    environment:
    SYMFONY_ENV: "{{ app_env|lower }}"

    pre_tasks:
    - name: Convert entered App environment to lowercase
    set_fact:
    app_env: "{{ app_env|lower }}"
    tags:
    - always


    - name: Install Composer's dependencies
    composer:
    working_dir: "{{ symfony_root_dir }}"
    no_dev: "{{ 'yes' if ('prod' == app_env) else 'no' }}"
    //I have also tried no_dev: "{{ 'True' if ('prod' == app_env) else 'False' }}"
    tags:
    - deploy
    when: code_changed


    My project use doctrine:fixtures:load. As the docs say, I register the bundle in:


    if (in_array($this->getEnvironment(), ['dev', 'test'], true)) {
    //default bundles
    $bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle();

    if ('dev' === $this->getEnvironment()) {
    //default bundles
    }
    }

    Do you know any idea that it can be ...? 😞
    thanks!