Haskell web development environment (on a mac)
I’ve been doing a bit of development with the Snap Framework, a web framework in Haskell, and I’m doing this on a Mac development host. Since I am deploying to linux (more specifically, Debian), and prefer to have a similar environment on my development machine as on the production server that it will eventually live on, this meant that just installing the Haskell Platform and other libraries on my laptop wasn’t going to be good enough.
The solution I ended up with is based on Virtualbox - each separate project has a separate virtual machine, that has the same operating system as it will be deployed to (debian, in this case), and all the same libraries. This is also helpful in that it keeps everything separate, so that the libraries of one application won’t conflict with any other (I use this process for non-haskell web development too). The virtual machine is set up as a server, so it will only be accessed remotely or via command line (and that only to set up ssh in the first place).
Then, each virtual machine mounts a folder from the host, which contains the source code for the application. With Virtualbox, this is done with “Shared Folders”, but I’m sure any other virtualization solution would provide something similar. Then once the application is running, set up port forwarding so that the host machine (my development laptop) can access the application running inside the virtual machine. And access it via SSH too, of course.
I’d been doing this for a little while, and keeping an ssh session with gnu screen open to the virtual machine in order to rebuild the application and restart it. But, this was sort of a pain, and I wanted to make it even more transparent that I was using a virtual machine at all (so needing to manually SSH in when I start the machine, and switching to that session to rebuild and relaunch wasn’t ideal).
So the next iteration had two parts: first, use God (the process manager) to maintain the running development web app. This is also duplicating what I have running in production, so this is a good thing! The second step is to make a script that takes care of the process of building and restarting the process, so that it can be easily triggered remotely. The script is very simple:
#!/bin/bash
PATH=/home/dbp/ghc-7.0.4/bin:/home/dbp/.cabal/bin:$PATH
cd /path/to/app
cabal-dev install && sudo god restart app-name
Now this can be triggered remotely with:
ssh -p PORTNUM localhost /path/to/script
Where PORTNUM is the port you have forwarded from the virtual machine for SSH (ie, what on the host corresponds to 22 on the virtual machine).
Now to round out my (at least for now) development environment, I am using Sublime Text 2 (just started, so this is all up in the air), and have set up a custom build system that looks line this:
{
"cmd": ["ssh", "-p", "2222", "localhost", "/path/to/build-script"],
}
What this means is that I can edit my application (all the files are on the host, just shared with the virtual machine, so there is nothing involved in doing that), and then with a shortcut trigger the build. This will compile the application and relaunch it, with the output of the compilation in a little panel so I can see what is going on.
The last parts are version control, testing, and deploying. I use darcs, and Quickcheck according to the guide here: How_to_write_a_Haskell_program#Running_the_test_suite_from_darcs, which allows you to run the tests as a precommit hook (which means commit will fail if the tests do not pass) like:
darcs --record --test
I am constantly pushing patches to a repository at Darcsden, a shared hosting site similar to github but for darcs, which serves as a level of redundancy (and if working with other developers, acts as a common repo we both can push to / pull from). There is also a repository on the production server, which can be pushed to as well.
This repository has a Heroku-inspired (though primitive) post-commit hook to build and deploy the application. This is a part I’d like to improve in the future - building binaries locally and just pushing them is one improvement; another is keeping around the old binaries to make it easy to switch back to a previous version. But for now, this is what the _darcs/prefs/defaults file in the production darcs repo looks like:
apply posthook echo "Rebuilding and deploying...\n" && cabal install && echo "Copying resources...\n" && sudo cp -R resources /path/to/production/root && echo "Restarting App-Name.\n" && sudo /var/lib/gems/1.8/bin/god restart app-name
apply run-posthook
This means that after recording changes locally, to deploy, I run:
darcs push servername:/path/to/production/repo
and select which patches I want to push. It will then build and deploy the application.
Aside from making the deployment more robust, I just need to integrate the version control / deployment into my editor to make this process completely streamlined (ie, being able to do everything from my editor, and just switch between it and a browser to test), which since it has a console pane, should be pretty easy.