This being the second time I've set this stuff up, I thought it's worth documenting my VirtualBox development workflow.
A Decent Hacking Environment
The OSX side of my laptop is working pretty smoothly. I've got my stack of tools configured to my liking, from my shell, to my editor, to my documentation browser. I've spent years cargo culting all my dotfiles.
But pick my brain any day and I'll give you a mouthful about Apple and OSX. I also know that there are superior alternatives to most of my software stack.
That said, even if I'm not entirely happy with the setup, I'm definitely content with it, and I have no plans on learning anything new to gain a 3% efficiency in the way I type in text or customize the way I waste time online.
Part of the reason I use OSX is that there is no hope (and therefore no temptation) in trying to fix little annoyances, something that led me to sacrifice countless hours during the brief period of time when I had a fully open source desktop environment.
However, when it comes to installing and configuring various project dependencies (daemons, libraries, etc), OSX can be a real pain compared to a decent Linux distribution.
A Decent Runtime Environment
Disk space is cheap, and virtualization has come along way in recent years, so it really makes a lot more sense to run my code on a superior platform. One image per project also gives me brainless sandboxing, and snapshots mean I can quickly start over when I break everything.
Sweetening the deal even more, I always seem to be surrounded by people who know how to properly maintain a Debian environment much better than I could ever hope to, so I don't even have to think about how to get things right.
Bridging the Gap
In order to make it easy to use both platforms simultaneously, with cheap context switching (on my wetware, that is), I've written a script that acts as my sole entry point to the entire setup.
I don't use the VirtualBox management GUI, and I run the Linux environment completely headless (not just sans X11, without a virtual terminal either).
I hard link the following script in ~/bin, once per VM. To get a
shell on a VM called blah, I just type blah into my shell
prompt and hit enter:
#!/bin/bash
VM="$( basename "$0" )"
if [ -n "$1" ]; then
# explicit control of the VM, e.g. `blah stop`
# useful commands are 'pause', 'resume', 'stop', etc
case "$1" in
status) VBoxManage showvminfo "$VM" | grep -i state ;;
*) VBoxManage controlvm "$VM" ${1/stop/acpipowerbutton} ;; # much easier to type
esac
else
# otherwise just make sure it's up and provide a shell
# boot the virtual machine in headless mode unless it's already running
# note that there is a race condition if the machine is in the process of
# powering down
VBoxManage showvminfo --machinereadable "$VM" | grep -q 'VMState="running"' || \
VBoxManage startvm "$VM" -type vrdp;
# each VM has an SSH config like this:
# Host $VM
# Hostname localhost
# Port 2222 # VBoxManage modifyvm "$VM" --natpf1 ...
# changing ssh port forwarding doesn't require restarting the VM (whereas
# fiddling with VirtualBox port forwarding does). The following section
# should probably just be a per VM include, but for my needs it does the
# job as is.
# ControlMaster works nicely with a global 'ControlPath /tmp/%r@%h:%p' in
# my ~/.ssh/config this means the port forwarding stays up no matter how
# many shells I open and close (unlike ControlMaster auto in the config)
# this loop quietly waits till sshd is up
until nc -z localhost 3000 >/dev/null; do
echo -n "."
ssh -N -f -q \
-L 3000:localhost:3000 \
-o ConnectTimeout=1 \
-o ControlMaster=yes \
"$VM" && echo;
done
# finally, start a shell
exec ssh "$VM"
fi
Once I'm in, I also have my code in a mount point under my home directory. I set up a shared folder using VirtualBox's management GUI (installing the VirtualBox guest additions like this). To mount it automatically I've got this in /etc/fstab on the guest OS:
# <file system> <mount point> <type> <options> <dump> <pass>
some_dir /home/nothingmuch/some_dir vboxsf uid=nothingmuch,gid=nothingmuch 0 0
I use the same path on the OSX side and the Linux side to minimize confusion. I decided not to mount my entire home directory because I suspect most of my dotfiles aren't that portable, and I'm not really running anything but Perl and services on the debian side.
I use all of my familiar tools on OSX, and instantly run the code on Debian without needing to synchronize anything.
When I'm done, blah stop will shut down the VM cleanly.
Finally, as a bonus, my bash prompt helps keep my confusion to a minimum when sshing all over the place.