Installing Composer & the script Module
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
With our project cloned, the next step is obvious: use Composer to install our dependencies!
Actually, we used the Composer module earlier from the command line. Google again
for "Ansible Modules" and find the "All Modules" page. I'll find the composer
module and open that in a new tab.
This module is really easy... except for one problem. Under Requirements, it says
that Composer already needs to be installed. We have not done that yet... and,
unfortunately, it can't be installed with apt-get
.
Installing Composer Programmatically?
So how do you install it? Check out https://getcomposer.org and click "Download".
Normally, we just paste these lines into our terminal and celebrate! But... there's this problematic fine print at the bottom:
Do not redistribute the install code. It will change for every version of the install.
Huh. Composer includes a bit of built-in security: a sha hash to make sure that the installer hasn't been tampered with. If we tried to use these 4 commands in Ansible, it would work... for awhile. But next time the installer is updated, and that sha changed... it would stop working.
What to do? Check out that how to install Composer programmatically
link. Eureka: a shell script that will safely download the latest version of Composer.
The end result is a composer.phar
file wherever we run this script from.
Installing Composer with script
Our mission is clear: somehow, execute this shell script via Ansible. But before we do that, near the top, add one new task: Install low-level utilities:
- hosts: vb | |
// ... lines 3 - 6 | |
tasks: | |
// ... lines 8 - 24 | |
- name: Install low-level utilities | |
// ... lines 26 - 109 |
Here, use the apt
module and the with_items
syntax to install zip
and unzip
:
- hosts: vb | |
// ... lines 3 - 6 | |
tasks: | |
// ... lines 8 - 24 | |
- name: Install low-level utilities | |
become: true | |
apt: | |
name: "{{ item }}" | |
with_items: | |
- zip | |
- unzip | |
// ... lines 32 - 109 |
Without these, Composer will run really slowly and you'll blame Jordi when you should be thanking him.
Now, back to our main job: how can we execute a script on a host? Why, with... the
script
module of course!
Runs a local script on a remote node after transferring it
Neato! We just point it at a local script, and it takes care of the rest. Go copy
the script and, in our ansible
directory, create a new scripts
directory and
a new file called install_composer.sh
. Paste the code there:
EXPECTED_SIGNATURE=$(wget -q -O - https://composer.github.io/installer.sig) | |
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" | |
ACTUAL_SIGNATURE=$(php -r "echo hash_file('SHA384', 'composer-setup.php');") | |
if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ] | |
then | |
>&2 echo 'ERROR: Invalid installer signature' | |
rm composer-setup.php | |
exit 1 | |
fi | |
php composer-setup.php --quiet | |
RESULT=$? | |
rm composer-setup.php | |
exit $RESULT |
Back in the playbook, at the bottom, create a new task: Download Composer:
- hosts: vb | |
// ... lines 3 - 6 | |
tasks: | |
// ... lines 8 - 96 | |
- name: Download Composer | |
// ... lines 98 - 109 |
Use the script
module. Then, the easiest way to use this is to literally put the script
filename on the same line as the module name: script: scripts/install_composer.sh
:
- hosts: vb | |
// ... lines 3 - 6 | |
tasks: | |
// ... lines 8 - 96 | |
- name: Download Composer | |
script: scripts/install_composer.sh | |
// ... lines 99 - 109 |
Actually, every module can be used with a one-line syntax like this... but since line breaks are pretty cheap these days, I usually organize things a bit more.
Thanks to this task, we'll have a new composer.phar
file in our home directory,
which is where this task - well, all tasks - are running. But that's not enough:
we need to move this to /usr/local/bin/composer
.
Moving Composer Globally
Create another task: Move Composer globally. This time, use become: true
and use
the command
module:
- hosts: vb | |
// ... lines 3 - 6 | |
tasks: | |
// ... lines 8 - 99 | |
- name: Move Composer globally | |
become: true | |
// ... lines 102 - 109 |
In your browser, go find the command
module. Like with script
, command
has
a short syntax. We'll say: command: mv composer.phar
to /usr/local/bin/composer
:
- hosts: vb | |
// ... lines 3 - 6 | |
tasks: | |
// ... lines 8 - 99 | |
- name: Move Composer globally | |
become: true | |
command: mv composer.phar /usr/local/bin/composer | |
// ... lines 103 - 109 |
If you're a little surprised that I'm using the command
module instead of some
built-in file
or move
module... me too! In general, you should always look for
a built-in module first: they're always more powerful than using command
. But sometimes,
like with moving files, command
is the right tool for the job.
Add one more task to make sure the file is executable: "Set Permissions on Composer"
with become: true
:
- hosts: vb | |
// ... lines 3 - 6 | |
tasks: | |
// ... lines 8 - 103 | |
- name: Set permissions on Composer | |
become: true | |
// ... lines 106 - 109 |
Remember, Ansible is all about state. The job of the file
module isn't really
to create files or symlinks. Instead, it's to make sure that they exist and
have the right permissions. In this case, we're going to take advantage of the
mode
option to guarantee that the file is executable.
In the playbook, use the file
module, set path
to /usr/local/bin/composer
and mode
to "a+x"
to guarantee that all users have executable permission:
- hosts: vb | |
// ... lines 3 - 6 | |
tasks: | |
// ... lines 8 - 103 | |
- name: Set permissions on Composer | |
become: true | |
file: | |
path: /usr/local/bin/composer | |
mode: "a+x" |
Oh, and make sure the file you created is install_composer.sh
.
Time to give this a try. Find your main machine's terminal and run the playbook!
ansible-playbook ansible/playbook.yml -i ansible/hosts.ini
Back on the VM, I'm in my home directory. And right now, it's empty. But if you're
really fast, you can see the installation script doing its work. There it is:
composer-setup.phar
, composer-temp.phar
, composer.phar
and then it's gone
once our task moves it. Yes!
And finally, we can type composer
. Let's install some dependencies already!
Hi Guys, I'm on a trouble with installing composer on EC2. I'm writting here because it's error related on previous comments:
skipping: [192.168.33.10]<br />fatal: [18.203.185.87]: FAILED! => {"changed": true, "failed": true, "rc": 1, "stderr": "Shared connection to 18.203.185.87 closed.\r\n", "stdout": "/home/ubuntu/.ansible/tmp/ansible-tmp-1566331749.55-41571300702954/install_composer.sh: 4: /home/ubuntu/.ansible/tmp/ansible-tmp-1566331749.55-41571300702954/install_composer.sh: php: not found\r\n/home/ubuntu/.ansible/tmp/ansible-tmp-1566331749.55-41571300702954/install_composer.sh: 5: /home/ubuntu/.ansible/tmp/ansible-tmp-1566331749.55-41571300702954/install_composer.sh: php: not found\r\nERROR: Invalid installer signature\r\nrm: cannot remove 'composer-setup.php': No such file or directory\r\n", "stdout_lines": ["/home/ubuntu/.ansible/tmp/ansible-tmp-1566331749.55-41571300702954/install_composer.sh: 4: /home/ubuntu/.ansible/tmp/ansible-tmp-1566331749.55-41571300702954/install_composer.sh: php: not found", "/home/ubuntu/.ansible/tmp/ansible-tmp-1566331749.55-41571300702954/install_composer.sh: 5: /home/ubuntu/.ansible/tmp/ansible-tmp-1566331749.55-41571300702954/install_composer.sh: php: not found", "ERROR: Invalid installer signature", "rm: cannot remove 'composer-setup.php': No such file or directory"]}
As you can see, on vagrant machine it is working fine, but on EC2 it is not correct signature... how I can fix that?
It is a 16.04 ubuntu ec2 instance.
Thanks!