Skip to content

Setting up a multi-user, multi-project development environment

by Jean-Marie de Boer on February 23rd, 2012

In our company a number of developers work together on Symfony projects, so we need a sensible, coherent and predictable development environment. A solution would be to have each developer run a php-enabled webserver on their local machine, but this has some obvious drawbacks when it comes to predictability. By having everybody run their code on a central server we ensure that it works on a reference system, avoiding ‘well it runs on my machine’ type situations. It also means that developers don’t have to worry about maintaining webservers.

Here’s how I created a flexible environment that is low on maintenance.

First of all I set up a DNS server for our local network. It is authorative for the fake .intra top level domain. I might also have chosen .lan, .local or whatever, als long as it’s not a real TLD. The .intra zone has some normal entries for printers and workstations and such, and a wildcard entry that points to the development server. So, if I lookup a nonexistent hostname in that zone, it always resolves to the development box, i.e:

$ dig server1.intra a
server1.intra. 38400 IN A 10.0.0.1
$ dig server2.intra a
server2.intra. 38400 IN A 10.0.0.2

server2 is the development box, so:

$ dig something.intra a
something.intra. 38400 IN A 10.0.0.2
$ dig totally.random.intra a
totally.random.intra. 38400 IN A 10.0.0.2

Ok, so I want to serve some Symfony2 (or other) content now. The development box is a linux server (CentOS FTW ;) ), running the same software as the production servers. In our case that’s Apache/PHP5.3/MySQL and some assorted php modules. Pretty standard stuff. The trick is in configuring apache.

Let’s say we have two developers, John and Jane. They are working on two projects, coolproject and stuffyproject.

In their home directory, each developer has a directory called ‘checkouts’ (or another name of your choice). Inside that directory, they create a directory for each project. How they get the files in there is irrelevant for this article, but it will likely be a Git or Subversion checkout accessed via Samba or NFS. So we have:
/home/john/checkouts/coolproject
/home/john/checkouts/stuffyproject
/home/jane/checkouts/coolproject
/home/jane/checkouts/stuffyproject

Each of these directories contains a documentroot, called ‘web’ in the case of Symfony2.

Now, to have apache to serve content from these directories, rewrites to the rescue! As the last (so I don’t inadvertedly match to greedy) virtual host in my configuration, I create this:

<VirtualHost *:80>
  ServerName *.*.intra

  RewriteEngine on
  RewriteCond %{HTTP_HOST} ^([a-z0-9\-]+)+\.([a-z0-9\-]+)+\.intra$
  RewriteCond /home/%1/checkouts/%2/web -d
  RewriteRule ^(.+) %{HTTP_HOST}$1 [C]
  RewriteRule ^([a-z0-9\-]+)\.([a-z0-9\-].+)\.intra(.*) /home/$1/checkouts/$2/web$3 [L]

  <Directory "/home/*/checkouts/web">
    Options FollowSymLinks MultiViews
    AllowOverride All
    Order allow,deny
    Allow from all
  </Directory>

</VirtualHost>

Basically what this does is map the hostname to a specific user’s project documentroot while also retaining the rest of the URI.
For instance http://john.coolproject.intra/something results in apache opening /home/john/checkouts/coolproject/web/something
Likewise http://jane.stuffyproject.intra/foo/bar results in apache opening /home/jane/checkouts/stuffyproject/web/foo/bar

Since I use AllowOverride All you can stick the Symfony2 .htaccess file in the documentroot and all rewrites function normally, i.e. you frontend controller gets started and the URI is passed to it.

The neat thing about this is that anybody with a home directory can create a virtual host at any time and it will work instantly. If the directory /home/jack/checkouts/testproject/web get created, its will be visible on http://jack.testproject.intra immediately, no further configuration necessary.

Almost there, all that’s left to tackle now is the sharing of data.

In our setup, everybody has their own checkout, but we (usually) share the database. This is simple enough, we have a non-versioned config file with the database credentials in it. But normally, there is also uploaded or generated files associated with that data, so you’d want to share that too. It’s simple enough. Say you have an ‘uploads’ directory in ‘web’. We make sure it’s not versioned but ignored.

Then I create a shared directory somewhere, say for instance /www/coolproject/pages/uploads
After that each user just creates a symlink to it, like so:

ln -s /www/coolproject/pages/uploads /home/jane/checkouts/coolproject/pages/uploads

So any file that Jane uploads is visible in John’s checkout and vice versa. Database and files stay in sync for all users.

In reality our setup is a little bit more complicated than this, as we can also reach the checkouts via external (real) url’s, and we cater for more documentroots such as htdocs, and we may use other directory structures too. But the idea is the same.

You can also use this setup if you are on a *nix box working solo, once you have it set up you can create virtual hosts on the fly, you may never need to open httpd.conf again! ;)

No comments yet

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS