Saturday, July 04, 2009

Php deployment with Subversion

Subversion, svn for friends, is a source control system, which stores all your source files and their history in a centralized place. How to use Subversion for deployment of websites?

Have you ever had the pain of deploying some megabytes of php source files with ftp? It takes some time, and using library and/or frameworks in you application the total size of files to be transferred is high. Moreover, also the number of single files is huge and this fact will slow down the process while your ftp client walks in and out of thousands of directories.
What if some configuration files are modified? You have to manually search the folders to replace and upload only the "engine" files, while avoiding to overwrite some config.inc.php which database credential are stored in. This is a daunting task to do with ftp.
Take away the pain with Subversion!

Prerequisites
Using a subversion client to deploy your web application requires, obviously, that the php files are stored in an svn repository. On the other hand, using a svn repository gives you only benefits over plain storing of php files in the filesystem, that goes over the scope of this article. Since Ossigeno is open source, I use SourceForge svn repository, that comes for free. If you develop a commercial application, you can eaily set up a svn repository on your machine. I use an old laptop with Ubuntu Server installed to store private modules sources (modules built for contractors), with port forwarding from the router to the laptop and a dyndns account to provide.
To run svn in the document root of the webserver, you'll need to have ssh access to the machine, or, alternatively, have physical access. For instance, this blog runs on SourceForge webserver, and SourceForge gives shell access to registered users. Tipically 8€/year shared hosts does not offer this type of service, however.
Finally, the svn client has to be installed on the webserver. If you have shell access, it's very likely that also svn is present. To find out, simply run:
$ svn --version
on the shell and see what happens.
Now that we know we have the tools for the job, let's install an Ossigeno copy from svn repository.

Initial checkout
Supposing, we are in the folder where we want to install (a document root subfolder /var/www/blog or the document root itself /var/www), we simply run:
$ svn checkout https://ossigeno.svn.sourceforge.net/svnroot/ossigeno/tags/3.0_beta6/core/ .
The SourceForge path has to be substituted with your server path. An hostname should be used, while an ip will not work correctly if it is dinamic (that is probably the case if you don't have an hostname). Subversion stores in its .svn hidden folders the hostname used for the initial checkout, so you don't have to retype a long url everytime you update.
Now that you have a working copy, you can proceed to install the application as it was extracted from a tarball or from a zip package.
Some school of thought uses the svn export command to obtain a copy of the code, but a checkout is more useful for what we're doing next.

Common operations
You had found a typo in a source file, and you corrected it on the webserver. Since this is a working copy and not an exported one, you can simply do:
$ svn commit -m "fixed a typo" folder/script.php
and the diff with the original file will be sent to the repository, sending upstream the simple patch you have created. Depending on your repository setup, a password or a rsa key will be requested by the svn client to proceed with commit.
Now you want to update you application to a new version: this version (of Ossigeno in the example) is tagged 3.0_beta7. This is work for subversion.
$ svn switch https://ossigeno.svn.sourceforge.net/svnroot/ossigeno/tags/3.0_beta7/core/ .
The repository will calculate the diff between 3.0_beta6 and 3.0_beta7 and will send to the client a temporary patch that will be applied to the working copy on the server where you execute the command. Please note that this operations are possible only because we choose to use checkout instead of export. The checkout command duplicates the original files in .svn folders, and thus double the space occupied; but it is very fast on updates and commits because only the file deltas are sent over the network.
So you can switch over the main branch of Ossigeno to have the bleeding edge version:
$ svn switch https://ossigeno.svn.sourceforge.net/svnroot/ossigeno/trunk/core/ .
and every two days run
$ svn update
to obtain the last features.
The last two steps are not recommended as trunk randomly breaks the working copy, being a in-development version.

Issues
You have edited script.php, but not commit this to the repository since it is a local hack that must not spread to other copies of the application. What will happen when you run switch or update?
The answer is multiple. If the file is not modified in the repository, your local copy will be mantained as-is. If the remote file is changed, svn will try to merge the files in a new version and only if the changes overlap, you will be prompted for a manual edit that will resolve the conflict. In Ossigeno example, configuration files are stored in a folder with svn:ignore property set with a filename corresponding to the host, so a file is added for the local configuration in application/config directory and ignored from subversion in commits and updates.
I encountered another issues while installing Ossigeno on this server. In the static/ folder some public directories are kept; Ossigeno puts cached html in static/cache/ and image previews in static/preview/, thus the webserver/php process needs writing permissions on the folders. However, on SourceForge the document root htdocs/ is readonly (from the webserver), and writable directories has to be created in persistent/ and pointed by a simbolic lynk in the htdocs.
So what's the problem? Those dirs were in repository, so I cannot remove them and place some simbolyc links. I had to remove the public dirs in repository, saving static/ as an empty folder with svn:ignore property set to '*'. Then I set up a phing task to recreate them and to setting the writing permissions, leaving the option to the user to create them manually, with phing task or with the installation process, or eventually to put some symlinks in static/.

Conclusion
This points out a general rule to use with subversion deployment: do not import in repository what will change on the production box. Do not import configuration files, because if you do at the next svn commit (that defaults to the current folder) you will sent to your repository the database credentials of the website; do not import dirs that can be substituted by symlinks, because if you removes those folders you will break the working copy. Do not import temporary files. You can setup svn:ignore property on folders to have this files not listed by svn status (see the best Subversion documentation, its book).
Have a nice day using the power of Subversion not only for development but also for deployment!

1 comment:

Fred said...

Do you know the deployment tool for web application called Fredistrano ?
It automates the export of your sources from a subversion repository and synchronizes them with the content of a target directory.

ShareThis