If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
Ok, let's install our Composer dependencies already! Go back to the Ansible composer
module for reference. Then, find your playbook and add a new task with a poetic
and flowery name: "Install Composer's dependencies":
- hosts: vb | |
... lines 3 - 6 | |
tasks: | |
... lines 8 - 109 | |
- name: Install Composer's dependencies | |
... lines 111 - 113 |
Ok, boring name, but clear! Use the composer
module, and set the one required
option - working_dir
- to {{ symfony_root_dir }}
:
- hosts: vb | |
... lines 3 - 6 | |
tasks: | |
... lines 8 - 109 | |
- name: Install Composer's dependencies | |
composer: | |
working_dir: "{{ symfony_root_dir }}" |
Hey, that variable is coming in handy!
Run that playbook!
ansible-playbook ansible/playbook.yml -i ansible/hosts.ini
It's running... running, installing Composer's dependencies and... explosion! Ah! So much red! Run!
Then... come back. Let's see what's going on. It looks like it was downloading
stuff... if we move to /var/www/project
on the VM and ls vendor/
, yep, it was
populated.
The problem was later - when one of Symfony's post-install tasks ran:
Fatal error: Uncaught exception, SensioGeneratorBundle does not exist.
Oh yea. By default, the composer
module runs composer like this:
composer install --no-dev
This means that your require-dev
dependencies from composer.json
are not
installed:
{ | |
... lines 2 - 35 | |
"require-dev": { | |
"sensio/generator-bundle": "^3.0", | |
"symfony/phpunit-bridge": "^3.0", | |
"doctrine/data-fixtures": "^1.1", | |
"hautelook/alice-bundle": "^1.3" | |
}, | |
... lines 43 - 76 | |
} |
If you're deploying to production, you may want that: it gives you a slight performance boost. But in a Symfony 3 application, it makes things blow up! You can fix this by setting an environment variable... and we will do that later.
But, since this is a development machine, we probably do want the dev dependencies.
To fix that, in the playbook, set no_dev
to no
:
- hosts: vb | |
... lines 3 - 6 | |
tasks: | |
... lines 8 - 109 | |
- name: Install Composer's dependencies | |
composer: | |
working_dir: "{{ symfony_root_dir }}" | |
no_dev: no |
Try the playbook now.
ansible-playbook ansible/playbook.yml -i ansible/hosts.ini
This time, I personally guarantee it'll work. In fact, I'm so confident, that if it doesn't work this time, I'll buy you a beer or your drink of choice if we meet in person. Yep, it's definitely going to work - I've never been so sure of anything in my entire life.
Ah! No! It blew up again! Find the culprit!
Attempted to load class "DOMDocument" from the global namespace.
Uh oh. I skipped past something I shouldn't have. When you download a new Symfony project, you can make sure your system is setup by running:
php bin/symfony_requirements
Your system is not ready to run Symfony projects.
Duh! The message - about the SimpleXML extension - means that we're missing an extension!
In our playbook, find the task where we install PHP. Add another extension: php7.1-xml
:
- hosts: vb | |
... lines 3 - 6 | |
tasks: | |
... lines 8 - 55 | |
- name: Install PHP packages | |
... lines 57 - 60 | |
with_items: | |
... lines 62 - 66 | |
- php7.1-xml | |
... lines 68 - 115 |
Run that playbook - hopefully - one last time:
ansible-playbook ansible/playbook.yml -i ansible/hosts.ini
Ya know, this is the great thing about Ansible. Sure, we might have forgotten to install an extension. But instead of installing it manually and forgetting all about it next time, it now lives permanently in our playbook. We'll never forget it again.
Phew! It worked! Go back to the VM and check out requirements again:
php bin/symfony_requirements
We're good! And most importantly, we can boot up our Symfony app via the console:
php bin/console
Our app is working! And there's just one last big step to get things running: configure NGINX with PHP-FPM and point it at our project. Let's go!
sorry: in htop I've found an processes:<br />/usr/bin/python3 ~/.ansible/tmp/ansible-tmp-1603580969.8799226-12682-259632965674037/AnsiballZ_composer.py<br />/usr/bin/php /usr/local/bin/composer help install --format=json<br />
Ahhh, found the solution by killing the process. In the background was a hint "avoid running Composer as super-user/root".
I used become: true
globally and not like you provide in every task.
Hey Axel,
Yeah, looks like on your system the Composer is running under root privileges and so yes, become true
should do the trick, nice catch :) An alternative but more complex solution would be to fix permissions and make it be run without root privileges. One of possible reasons why it requires root is that it might be installed via root. Anyway, I'm glad you were able to find the working solution for you!
Cheers!
Not sure why but I get an error running the composer install the second time.
Uncaught TypeError: Argument 1 passed to Composer\\Autoload\\ClassLoader::addClassMap() must be of the type array, integer given
Error goes away only when 'optimize_autoloader' is disabled
- name: Install Composer Dependencies
composer:
working_dir: "{{ symfony_root_dir }}"
no_dev: no
optimize_autoloader: false
Hey gstanto ,
Hm, difficult to say why without debugging, probably it's a bug in Composer, I'd recommend to you to upgrade your composer to the latest available version and try again.
Btw, sometimes problems could be due to the PHP version difference, e.g. when you use different PHP versions locally (where you lock dependencies in composer.lock) and on the server where you're trying to run this script.
UPD: Actually, I found a similar issue in Composer repo: https://github.com/composer... try to follow some instructions from there. Probably the easy fix is to upgrade/downgrade Composer, maybe you just have a bad version.
Let us know if a solution from the issue works for you.
Cheers!
I can't get it to run. I deleted composer.lock, vendor dir, and deleted all cache files, and everything resolved. But only the first run. Same thing happened with previous version install. If I find a solution, I'll post it here. Thank you very much for the timely response btw.
Hm, I just double checked the downloaded course code from both start/ and finish/ directories and it works for me well when I run both "composer install" and "composer install --optimize-autoloader" commands several times locally, i.e. I don't see the error you mentioned. Btw, could you try to install dependencies by yourself, i.e. not via Ansible but directly in your console as I did. Do you also see this error?
Cheers!
Thanks Victor. I did as you said, no errors in console when I was direct on Vagrant. Ran a second time was still fine. Deleted everything, ran and still fine. Changed everything to work for Symfony4, still fine! Must have been me.
sorry for the hassle and thanks for the help. This tutorial was extremely helpful, btw.
If I made mistakes like you, and I rerun the playbook again. Does that mean I re-download php7.1 mysql and composer......over again??
Yo jian su!
Yes and no :). This depends on your task, and it's something we talk about later, in fact in the next chapter to be released: https://knpuniversity.com/screencast/ansible/faster-smarter-playbook
When you use the apt
module, it's basically running apt-get install php7.1
. And apt is smart enough to not re-download and re-install something if it's already there (however, if you use the "latest" state, then it will upgrade the package if there is a new version). So for apt
the answer is mostly no: the packages are not redownloaded and reinstalled.
But for Composer, the answer (until the later chapter), is YES! This is because we're simply telling Ansible to run some commands (which download Composer), so it simply runs those commands every time. That's not a big deal, but it is wasteful. So, we fix it later.
Cheers!
Hi Guys:
After running this task and have problem
- name: Install Composer's dependencies
composer:
working_dir: "{{ symfony_root_dir }}"
no_dev: no
Need help, I have the following error
[Symfony\Component\Config\Exception\FileLoaderLoadException]
The configuration key "exclude" is unsupported for definition "AppBundle\" in "/var/www/project/app/config/services.yml". Allowed configuration key
s are "resource", "parent", "shared", "lazy", "public", "abstract", "deprecated", "factory", "arguments", "properties", "configurator", "calls", "t
ags", "autowire", "autoconfigure" in /var/www/project/app/config/services.yml (which is being imported from "/var/www/project/app/config/config.yml
").
Hey jian su!
Ah, ha! This is related to Symfony! Are you trying to use the new Symfony 3.3 dependency injection features? The exclude
key is pretty new (only a few days old), so you likely need to update to the absolute latest commit of Symfony on the master branch. I hope you enjoy those features - I've been personally working very hard on them (along with other people).
Cheers!
The Install Composer's dependencies is failing for me with
TASK [Install Composer's dependencies] *****************************************
fatal: [192.168.33.10]: FAILED! => {"changed": false, "failed": true, "module_stderr": "Shared connection to 192.168.33.10 closed.\r\n", "module_stdout": "Traceback (most recent call last):\r\n File \"/tmp/ansible_c3hnjciv/ansible_module_composer.py\", line 233, in <module>\r\n main()\r\n File \"/tmp/ansible_c3hnjciv/ansible_module_composer.py\", line 212, in main\r\n for param, option in option_params.iteritems():\r\nAttributeError: 'dict' object has no attribute 'iteritems'\r\n", "msg": "MODULE FAILURE"}
as output. The error seems to be related to the version of python but I am not sure if it's the local version or the guest os one.
Hey Nia,
Hm, you can try to run "composer install --no-ansi --no-interaction --no-progress" manually, do you see any errors in the output? If not, then there's some bug in Ansible's Composer module. Btw, do you have the latest Composer version? Do you have any other extra Composer parameters in your task?
Cheers!
Hi Victor,
Running "composer install --no-ansi --no-interaction --no-progress" manually didn't yield any errors and by googling the error it looks like it something related to the composer module.
> Btw, do you have the latest Composer version?
The latest version on the guest or host OS? If guest I using the script at https://getcomposer.org/doc... to download it and install it as suggested in the screencast.
> Do you have any other extra Composer parameters in your task?
No extras parameters in the task.
- name: Install Composer's dependencies
composer:
working_dir: "/var/www/project"
no_dev: no
Also using "ubuntu/xenial64" for vagrant box.
Thanks for the quick reply and feedback.
Hey Nia,
> The latest version on the guest or host OS?
I meant that OS on which you run that composer install task and see that error.
So if you run "composer install" manually and it works fine without any errors but failed with Ansible - most probably related to the Composer module. If you have a detailed steps to reproduce, I think you can report an issue in Ansible's repository if nobody did it already.
So there're a few possible solutions:
1. Try to downgrade your composer.phar file to a lower version with which this task worked fine. But for this one your need to know what version worked fine for you before.
2. Wait for the fix, but you need to be sure that this bug is reported and confirmed, so someone will fix it.
3. Or temporarily write a custom composer task using "command" module to install composer dependencies. You can even play with "changed_when" based on the output to make this task green if nothing was done by Composer.
If first 2 cases are not an option for you - I think the 3rd should be good and easy to implement.
Cheers!
Went with the 3rd option and added
register: composer_dependencies_installed
changed_when: "composer_no_update not in composer_dependencies_installed.stdout_lines"
with
vars:
composer_no_update: "Nothing to install or update"
Thanks again, I will try to make the composer module work in the future.
Hey Nia,
Thanks for sharing your temporary solution with others! Most of the times I just hardcode strings in "changed_when" statements, it makes sense to create a var only when you need to use that string in a few places. But yeah, good work!
Cheers!
// composer.json
{
"require": {
"php": ">=5.5.9",
"symfony/symfony": "3.1.*", // v3.1.4
"doctrine/orm": "^2.5", // v2.7.2
"doctrine/doctrine-bundle": "^1.6", // 1.6.4
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
"symfony/swiftmailer-bundle": "^2.3", // v2.3.11
"symfony/monolog-bundle": "^2.8", // 2.11.1
"symfony/polyfill-apcu": "^1.0", // v1.2.0
"sensio/distribution-bundle": "^5.0", // v5.0.12
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
"incenteev/composer-parameter-handler": "^2.0", // v2.1.2
"doctrine/doctrine-migrations-bundle": "^1.2", // v1.2.0
"snc/redis-bundle": "^2.0", // 2.0.0
"predis/predis": "^1.1", // v1.1.1
"composer/package-versions-deprecated": "^1.11" // 1.11.99
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.0.8
"symfony/phpunit-bridge": "^3.0", // v3.1.4
"doctrine/data-fixtures": "^1.1", // 1.3.3
"hautelook/alice-bundle": "^1.3" // v1.4.1
}
}
Hi there,
I've found this screencast and testing around with Ubuntu Focal and Bionic... and I also have issues with "Install Composer's dependencies". On both systems ansible stops with nothing... no error, no output and no process (viewed in htop)...
`
TASK [Download Composer] ***********************************
changed: [192.168.19.146]
TASK [Move Composer globally] **********************************
changed: [192.168.19.146]
TASK [Set permissions on Composer] *********************************
ok: [192.168.19.146]
TASK [Install Composer's dependencies] *****************************
`
Any ideas what happend at this point?
Thank you a lot... take care!