Managing one-off post-deployment commands

Enrise

19 juni 2015

A couple of weeks ago, my team changed a system’s data store from PostgreSQL to elasticsearch. When we deployed this change the old database tables were to be deleted, and the elasticsearch index had to be created and populated. We wrote some console commands that we could manually call right after the deployment, and everybody was happy.

Another week, another feature to build. This time the default thumbnail size for user uploads had to be changed. Simple change, but all the existing thumbnails had to be of the new default size as well. So again, we wrote a console command to generate them after deployment.

shipit-web

The problem with manual post-deployment operations

There was a problem with these console commands: they had to be executed manually on the target server immediately after the deployment had finished. Or actually, the deployment failed when these commands did not work properly on the new environment.

Also, most people in our team are human, so we forget things. One-off routines like these have to run in all environments. Including your co-worker’s local development environment.
Of course the easiest way to accomplish is to mail everyone in the team in all caps MUCH IMPORTANT: MAKE SURE TO RUN X WHEN YOU PULL COMMIT Y!!1one!

How to manage one-off post-deployment commands

In one project we use Doctrine Migrations, which is great to run database migrations as part of a deployment. You create the migration scripts yourself (in PHP) and Doctrine Migrations runs only the migration scripts it did not run before. It can also do a rollback when the deployment failed.

But! Since they’re PHP scripts you can run arbitrary scripts from them. Making Doctrine Migrations very useful for any one-off post-deployment commands!

Note: Important to know is that you can use Doctrine Migrations even if you don’t use other parts of Doctrine, such as the ORM.

Being PHP and all, there are many different ways you can execute these post-deployment scripts. I’ll name a couple with their pro’s and cons below.

exec/passthru/Process::run()

The easiest way to run arbitrary (system) commands is to use exec, passthru, or the Symfony Process component. The upside of this is obviously greater flexibility. But the downside is that your commands probably contain environment-specific parameters like hostnames.

Container Aware Migrations (Symfony)

If you use Doctrine Migrations with Symfony, another option is to make your migrations implement the ContainerAwareInterface. This way you can access the services and parameters as defined in your DI container. Very convenient. Especially the access to your environment parameters!

Be careful

After tweeting about ContainerAware migrations, Mike Simonson, the maintainer of Doctrine Migrations mentioned to me that you have to be careful when using services in migrations and I think he’s right.

Don’t forget that migrations will run once on every system. So they also run when you set up a new environment using vagrant destroy && vagrant up for example.
While not a problem at all with database migrations (due to their incremental nature), it could be problematic for your one-off scripts. So when you write these types of migrations, be sure to check if your environment is in the state the migration expects it in.

Conclusion

While not very obvious to me at first, Doctrine Migrations are a great tool to run one-off post-deployment commands of any kind.

Writing migration scripts is easy, and using Doctrine Migrations you’re sure that they are executed as part of the next deployment.

 

Effectively you’re doing more with less effort!