in Articles

Install Composer with Puppet and Vagrant

I started a personal project to update my WordPress theme so that it would match the design on my portfolio site. In the process; I decided it would be a good time to use Vagrant, and in particular, a project called VagrantPress to quickly setup a WordPress development environment. The advantage I wanted to capitalize on was the convenience of throwing away the environment once the project was finished. I didn’t want to leave any remnants of PHP or custom path variables on my laptop after the project was done. It would also be easy to develop on multiple machines since the vagrant instance would be the same across all devices.

After I checked out the VagrantPress project from github, I was ready to roll (commence finger tent gesture). Ran vagrant up. But sadly, errors were thrown about plugins not able to install due to an issue with PEAR. What?? PEAR has been around for a long time and I’ve used it on many, probably every PHP, project. Why is it failing? After some digging around the internets, I came across an  end of life message from the folks that develop/maintain PEAR and PHPUnit. Sad days. The people involved with that project deserve major recognition for their efforts and service through the years. Thousands of PHP projects still rely on PEAR and will have to transition to Composer (or other dependencies managers) if they haven’t already.

Install Composer with Puppet

Alas, new methods to install PHP plugins have emerged in the past few years, and Composer has become the de facto dependency manager for PHP.

This shouldn’t be a big deal to fix, I thought. VagrantPress is using Puppet to provision Vagrant. In other words, puppet handles automated tasks for installing infrastructure dependencies and configurations. I’ll use a puppet project that installs Composer to modify the way VagrantPress installs the PHP Quality Tools. Long story short, it wasn’t easy and I went another route. I still used puppet, but created my own puppet manifest for installing Composer based off an example I found. Here is what I ended up with:

# Install composer

class composer::install {

  package { "curl":
    ensure => installed,
  }

  exec { 'install composer':
    command => 'curl -sS https://getcomposer.org/installer | php && sudo mv composer.phar /usr/local/bin/composer',
    require => Package['curl'],
  }

}

And then the main puppet manifest references the composer puppet manifest:


exec { 'apt_update':
  command => 'apt-get update',
  path    => '/usr/bin'
}

# set global path variable for project
# http://www.puppetcookbook.com/posts/set-global-exec-path.html
Exec { path => [ "/bin/", "/sbin/" , "/usr/bin/", "/usr/sbin/", "/usr/local/bin", "/usr/local/sbin", "~/.composer/vendor/bin/" ] }

class { 'git::install': }
class { 'subversion::install': }
class { 'apache2::install': }
class { 'php5::install': }
class { 'mysql::install': }
class { 'wordpress::install': }
class { 'phpmyadmin::install': }
class { 'composer::install': }
class { 'phpqa::install': }

Thanks to the vagrant-puppet-composer project by Experience for inspiration and bits of the code above.

What’s going on here?

The puppet manifest runs a bash command which makes a cURL call to download the latest version of Composer, and moves the files to /usr/local/bin so that Composer can be accessed globally:

command => 'curl -sS https://getcomposer.org/installer | php && sudo mv composer.phar /usr/local/bin/composer'

Before any of that happens, Puppet checks to make sure cURL is already installed. If not, it will install cURL first before Composer is downloaded:

package { "curl":
  ensure => installed,
}

Once you boot up vagrant and ssh into the box via vagrant ssh and type in composer -v, you will get a response with the version number.

$ composer --version
Composer version 1.0-dev (07c644ac229a21df80180598d8bb9aaba232eecb) 2015-02-03 12:51:10

Yay! Composer is installed. But what about those modules that we were installing with PEAR. How do we use a Puppet manifest that leverages Composer to install dependencies?

Install Dependencies with Composer via Puppet Manifest

Typically, to install dependencies with Composer, you would create a composer.json file which would include all the packages you want composer to install. Then you would run composer install on the command line and everything you declared would install. But, we need a more automated process that works better with our Puppet manifest configuration. Enter the ‘require’ command in Composer. For example, if composer global require "phpunit/phpunit=4.4.*" is typed in the command line, Composer would add the PHPUnit dependency to the composer.json file and then install the package. This method can be used to install multiple dependencies by declaring separate bash commands in the terminal. Here is a partial of the puppet manifest I created for VagrantPress to install Composer dependencies:

  # install phpunit
  exec { "composer install phpunit":
    command => 'composer global require "phpunit/phpunit=4.4.*"',
    environment => ["COMPOSER_HOME=/usr/local/bin"],
    path    => '/usr/bin:/usr/local/bin:~/.composer/vendor/bin/',
    require => Exec['install composer']
  }

What’s going on here?

The manifest will install phpunit via composer command => 'composer global require "phpunit/phpunit=4.4.*"',, with environment and path variables relative to Vagrant. This will only run as long as composer has been installed: require => Exec['install composer'].

Closing Remarks

I hope this helps others navigate using Puppet to provision Vagrant boxes. Using Puppet is extremely powerful and can help you provision Vagrant to your hearts desire. For example, managing PHP dependencies with Composer.

If you’d like to dig further, check out the VagrantPress project and see how Composer was used to replace PEAR as a PHP dependency manager.

Leave a Reply

  1. Great write-up and examples.
    Suggestion : append “creates => “/usr/local/bin/composer” to your exec { ‘install composer’ … declaration.
    This will prevent puppet from executing this command on every check in. ( Which will keep logs cleaner.)