Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Faster Smarter Playbook

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.

Start your All-Access Pass
Buy just this tutorial for $10.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

If you executed the playbook twice in a row, you would see "Changed 2". On every playbook run, we're doing unnecessary work: we're always downloading Composer and then moving it globally... even if it's already there!

That's not a big deal... but it is wasteful. Let's make our playbook smarter by running tasks conditionally, based on facts that we collect about the host. In other words: let's not download Composer if it already exists!

Checking if a File Exists: stat

So that's the first mission: figure out if the /usr/local/bin/composer file exists. To do that, we can use a really handy module called stat. It's similar to the Unix stat command... which gives you a ton of info about a file.

Before we download composer, add a new task called "Check for Composer". Use the stat module and set the path to /usr/local/bin/composer. Then, register a new variable called composer_stat. And don't forget the deploy tag!

---
- hosts: vb
... lines 3 - 26
tasks:
... lines 28 - 146
- name: Check for Composer
stat:
path: /usr/local/bin/composer
register: composer_stat
tags:
- deploy
... lines 153 - 158
- name: Download Composer
... lines 160 - 236

To see what goodies that variable has in it, add a debug task to print composer_stat. Give that the deploy tag too:

---
- hosts: vb
... lines 3 - 26
tasks:
... lines 28 - 146
- name: Check for Composer
stat:
path: /usr/local/bin/composer
register: composer_stat
tags:
- deploy
- debug:
var: composer_stat
tags:
- deploy
... lines 158 - 236

Ok! Change over to your terminal and run the playbook with -t deploy:

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

There it is! Hit CTRL+C to quit.

The new variable has a stat key with a lot of info. The key we want is exists. Remove the debug task.

Skipping Tasks

Our goal is to skip the next three tasks if that file exists. Like before, use the when key set to not composer_stat.stat.exists:

---
- hosts: vb
... lines 3 - 26
tasks:
... lines 28 - 153
- name: Download Composer
... line 155
when: not composer_stat.stat.exists
... lines 157 - 233

In other words, check the stat.exists key, and if that is true, we want to not run this.

Copy that and put it below "Move Composer globally", and also "Set Permissions on Composer":

---
- hosts: vb
... lines 3 - 26
tasks:
... lines 28 - 159
- name: Move Composer globally
... lines 161 - 162
when: not composer_stat.stat.exists
... lines 164 - 233

I think we're ready! Try it:

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

Wait... yes! Skipped! Stop the playbook.

Upgrading Composer

This is a nice performance improvement... but like with a lot of things in programming, new features mean new complexity. Thanks to this... the Composer executable will eventually become really old and out-of-date. We need to make sure it's upgraded to the latest version.

Below the set permissions task, add a new one called "Make sure Composer is at its latest version". This time we can use the composer module. Set working_dir to {{ symfony_root_dir }}... though that doesn't matter in this case... because we'll use self-update to upgrade Composer:

---
- hosts: vb
... lines 3 - 26
tasks:
... lines 28 - 166
- name: Set permissions on Composer
... lines 168 - 174
- name: Make sure composer is at its latest version
composer:
working_dir: "{{ symfony_root_dir }}"
command: self-update
... lines 179 - 240

We can even add when: composer_stat.stat.exists to avoid running this if Composer was just downloaded. Give it our favorite deploy tag:

---
- hosts: vb
... lines 3 - 26
tasks:
... lines 28 - 174
- name: Make sure composer is at its latest version
composer:
working_dir: "{{ symfony_root_dir }}"
command: self-update
tags:
- deploy
... lines 181 - 240

Ok, run it! But this time, add a --verbose flag:

ansible-playbook ansible/playbook.yml -i ansible/hosts.ini -t deploy --verbose

This is a sweet trick... because... as you can see, it prints the output from each task... which can save us from needing to add so many debug tasks to print variables.

Boom! The new Composer upgrade task ran! I'll stop with "CTRL+C". And, we can see the stdout: You are already using composer version 1.4.1.

So it worked! But like with other tasks... it's reporting as "Changed" even though it did not actually change anything! We already know how to fix that. Scroll down to the "Create DB" task and copy its changed_when. Scroll back up and paste it under this task. Register a new variable - composer_self_update - and use that in changed_when. Then, all we need to do is search for You are already using composer version:

---
- hosts: vb
... lines 3 - 26
tasks:
... lines 28 - 174
- name: Make sure composer is at its latest version
... lines 176 - 178
register: composer_self_update
changed_when: "not composer_self_update.stdout|search('You are already using composer version')"
... lines 181 - 242

If that shows up in the command, then we know nothing changed. Paste that into the search filter.

Test it out!

ansible-playbook ansible/playbook.yml -i ansible/hosts.ini -t deploy --verbose

There it is! This time it's Okay - not changed. And on that note, we can even start skipping tasks based on whether or not other tasks are "changed" or "ok". Let's try that next!

Leave a comment!

0
Login or Register to join the conversation
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial is built using an older version of Symfony, but the core concepts of Ansible are still valid. New versions of Ansible may contain some features that we don't use here.

What PHP libraries does this tutorial use?

// 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
    }
}
userVoice