Email iconarrow-down-circleGroup 8Path 3arrow-rightGroup 4Combined Shapearrow-rightGroup 4Combined ShapeUntitled 2Untitled 2Path 3ozFill 166crosscupcake-icondribbble iconGroupPage 1GitHamburgerPage 1Page 1LinkedInOval 1Page 1Email iconphone iconPodcast ctaPodcast ctaPodcastpushpinblog icon copy 2 + Bitmap Copy 2Fill 1medal copy 3Group 7twitter icontwitter iconPage 1

We manage the application parameters for our Symfony and Silex projects using the Incenteev Parameter-Handler bundle. The bundle uses composer hooks such as post-install-cmd to generate parameters.yml, providing a straight forward alternative to placing your top secret db passwords and session tokens under version control.

We maintain a parameters.yml.dist file (which is version controlled) and when a dev runs composer install, they’ll get a nice prompt asking them to accept the defaults from the .dist file or override the values for themselves. As we add more parameters to our application, it’s just a case of updating the .dist file and running composer install.

On top of this the bundle has an additional feature, which allows you to use environment variables as parameter values. The script will fallback to the values it finds in dist should the env variable not exist. The docs explain how to specify the environment variables in the composer.json file.

By using a combination of these two techniques, it’s possible to keep default values for parameters such as database_user in the .dist file and then use a different value when deploying to a specfic environment. For example:

parameters.yml.dist:

 parameters: database_user: default_db_user 

composer.json:

 "incenteev-parameters": { "file": "app/config/parameters.yml", "env-map": { "database_user" : "PROJECT_DATABASE_USER" 

Top secret parameters such as database credentials need only be stored on the deployment environment, as opposed to git repos or dev workstations.

In terms of getting this to work during a deployment, it’s necessary to integrate the steps into your tooling. Luckily for us (and most other readers I’m sure), we deploy using Capifony and sometimes plain old Capistrano. What’s more, as part of a default deployment Capifony will run composer install, so it should just work, right?!

Not quite. I came up against a few gotchas.

1. Set the right flags for composer

By default, Capifony runs composer in no-script mode, so the bundle’s script will not actually be kicked off. Override the composer options as follows:

set :composer_options, "--no-dev --verbose --prefer-dist --optimize-autoloader" 

Also, you don’t want the script to present a command prompt during the deployment (especially if your deploying via jenkins). Run composer in no-interaction mode by setting the following variable.

set :interactive_mode, false 

2. Fix an empty parameters.yml file

Your paramters.yml file is likely to be a shared_file for your Capifony deployment. As a result Capifony will run touch on the filepath when setting up the shared files, in order to create the symlinks. This doesnt play particularly well with the script. When it attempts to parse an empty parameters.yml file (one which doesnt contain a parameters: node), it will throw an exception, and the deployment will bomb out.

I’ve created a simple task which checks for an empty parameters.yml file and adds parameters: to the first line. If you kick this off before the symfony:composer:install task is run, the script should run with no issues. Add the following to deploy.rb.

 #task to add 'parameters:' to an empty parameters file task :fix_empty_parameters_yml do parameters_path = "app/config/parameters.yml" run "LEN=$(cat #{shared_path}/#{parameters_path} | wc -c); if [ $LEN -eq 0 ] ; then echo "parameters:" > #{shared_path}/#{parameters_path} ; fi" end #run the parameters fix task before composer kicks off before "symfony:composer:install", "fix_empty_parameters_yml" 

3. Exposing environment variables to capistrano

I initially thought that specifying environment variables on each server, in a .profile file, would do the trick…not quite. Logging in as the deployment user via ssh the variables were present. However, in order for them to be available in Capistrano, (during the commands it executes over ssh) a different technique needs to be used.

The specific stackoverflow post I found is here, but in summary:

SSH will only expose a given set of variables by default so set PermitUserEnvironment yes in the sshd config of your remote server. For example – /etc/ssh/sshd_config

Variables are read from the ~/.ssh/environment file for the deployment user. Create this file if it doesn’t exist and add variables in the format VARIABLE=value. The example above might look like:

PROJECT_DATABASE_USER=testing_db_user 

Run cap {environment} COMMAND=printenv invoke to check the environment variables are set. You should get something similar to:

 * 2013-07-25 15:25:26 executing `invoke' * executing "printenv" servers: ["your.server.com"] [your.server.com] executing command ** [out :: your.server.com] PROJECT_DATABASE_USER=testing_db_user 

That should do the trick. Run cap {environment} deploy and you should see a parameters.yml, generated from the variables on your server.

Please let me know if you have any issues.

Share: