Mark Garratt

Mark Garratt

Who am I?

Web Developer based in London, UK


What I write about


Recent Posts

Including Codebox IDE in a Vagrant Project (an experiment)

A few days back I came across Codebox.io, an open source Cloud IDE. I got it running on my dev machine and played around with it a bit and it's definitely something that I would like to look at using. Features including being fully cloud based & able to run on a server, git integration and collaborative editing (which I am yet to test).

On a day to day basis I run all of my projects using Vagrant, this allows me to move machine without worrying about setting up the same environment or "it was working on my machine" type issues, but why Vagrant is the way everyone should work is a completely different topic! This got me thinking, could I take Codebox with me everywhere and remove the need to install an IDE when working with Vagrant on an unusual machine? The answer, yes, here's how I did it...

Provisioning a Vagrant box

First things first I needed a Vagrant box to run Codebox on, I normally use Ubuntu so here is a modified Vagrantfile from one of my Node.js projects:

VAGRANTFILE_API_VERSION = "2"
$script = <<SCRIPT
# Add repositories
apt-get update
apt-get -y install python-software-properties build-essential zip unzip
add-apt-repository -y ppa:chris-lea/node.js
apt-key adv --keyserver keyserver.ubuntu.com --recv 7F0CEB10
echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | tee /etc/apt/sources.list.d/mongodb.list

# Update & Upgrade
apt-get update
# http://serverfault.com/questions/479571/running-apt-get-upgrade-with-chef-solo
DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" dist-upgrade

apt-get -y install nodejs mongodb-10gen git

# Clear up
apt-get -y autoremove
apt-get -y autoclean

# Global dependencies
npm install -g grunt-cli bower supervisor

# Local dependencies
cd /var/www/server
npm install --no-bin-links
bower install --allow-root

SCRIPT

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "precise64"
  config.vm.box_url = "http://files.vagrantup.com/precise64.box"
  config.vm.synced_folder ".", "/var/www/server"
  config.vm.synced_folder ".", "/vagrant", disabled: true
  config.vm.network "forwarded_port", guest: 3000, host: 3000, auto_correct: true
  config.vm.network "forwarded_port", guest: 8000, host: 8000, auto_correct: true
  config.vm.network "forwarded_port", guest: 27017, host: 27017, auto_correct: true
  config.vm.network "private_network", ip: "192.168.50.2"
  config.vm.provision "shell", inline: $script

  config.vm.provider :virtualbox do |v|
    v.customize ["modifyvm", :id, "--memory", 1024]
  end
end

This will get Ubuntu 12.04 running in a VirtualBox VM connected to a private network using IP address 192.169.50.2, with ports 3000 (Node.js project), 8000 (Codebox) and 27017 (MongoDB) forwarded to the host machine (localhost). It deals with bringing Ubuntu up to date and installing the necessary packages to run a Node.js project. Git is installed as it is required for Codebox to run, however it is generally not required when using Vagrant as it should be run on the host machine.

Installing Codebox on the VM

Once this machine was up and running I SSH'd into it and starting experimenting with Codebox, turns out it was pretty easy, simply install as a global Node module and run codebox run /var/www/server. The issue is this has to be done as root user because Codebox modifies files within it's directory, but a comment on GitHub says this will be dealt with soon.

After some poking around in the code and reading GitHub issues I found it would be easier to start Codebox from my own Node.js script passing in a config object. I basically took the script from this comment and combined it with the default config from this file. Now I have codebox running as the vagrant user instead of root, the last thing remaining to do is get it running as a daemon that will restart if it crashes, all this needed was a modified version of the upstart script I use for Ghost.

Now I had Codebox running, but as soon as I destroy the VM it will be lost, so I added the following to the Vagrantfile, just after bower install --allow-root and before SCRIPT:

## Setup Codebox
# Install as Global Module
npm install -g codebox

# Create .js file with config to run Codebox
echo "var os = require('os');
var path = require('path');
var codebox = require('codebox');

var users = {
  '[email protected]': 'Password1'
};

var config = {
  'root': path.resolve('/var/www/server/'),
  'title': 'Homeworker.io',
  'public': false,
  'dev': false,
  'workspace': {
    'id': null // Default will be a hash of the root path
  },
  'hooks': {
    // Auth: send auth infos and get status and user settings
    'auth': function(data) {
      if (!data.email || !data.token) {
        return Q.reject(new Error('Need \"token\" and \"email\" for auth hook'));
      }

      var userId = data.email;

      if (!users[userId] || data.token != users[userId]) {
        return Q.reject(new Error('Invalid user !'));
      }

      return {
        'userId': userId,
        'name': userId,
        'token': data.token,
        'email': data.email,
        'settings': {} // user current settings
      };
    }//,
    // Events: send events to a hook
    //'events': process.env.WORKSPACE_HOOK_EVENTS,
    // Settings: send user settings to a hook
    //'settings': process.env.WORKSPACE_HOOK_SETTINGS,
    // Valid addons installation with a hook
    //'addons': process.env.WORKSPACE_HOOK_ADDONS
  },
  'webhook': {
    // Token to pass as Authorization header
    'authToken': null
  },
  'addons': {
    // Base path
    'path': path.resolve(__dirname + '/.codebox/.addons'),
    'defaultsPath': path.resolve(require.resolve('codebox'), '../addons'),
    'tempPath': os.tmpDir(),
    'blacklist': ''.split(',')
  },
  'users': {
    // Max number of collaborators
    'max': 3,

    // Default auth email
    'defaultEmail': null,
    'defaultToken': null,

    // Use git for auth
    'gitDefault': true
  },
  'proc': {
    'urlPattern': 'http://localhost:%d'
  },
  'server': {
    'hostname': '0.0.0.0',
    'port': 8000
  },
  'settings': {
    'path': path.resolve(__dirname + '/settings.json')
  }
};

codebox.start(config).then(function() {
  console.log('codebox is running');
}, function(err) {
  console.log('Error: ', err);
});" > /home/vagrant/codebox.js

# Upstart
echo 'description "Codebox"
author      "Mark Garratt"

start on (local-filesystems and net-device-up IFACE=eth0)
stop  on shutdown

respawn # restart when job dies

# Start the Process
env DIR=/home/vagrant/
env NODE_PATH=/usr/lib/nodejs:/usr/lib/node_modules:/usr/share/javascript
exec start-stop-daemon --start --chuid vagrant --make-pidfile --pidfile /var/run/codebox.pid --chdir ${DIR} --exec /usr/bin/node -- ${DIR}codebox.js >> /var/log/codebox.log 2>&1' > /etc/init/codebox.conf

touch /var/log/codebox.log
service codebox start

This echos out the scripts I am using to the relevant files and starts the service, it's pretty messy, but this was just an experiment. Using this Vagrantfile you can access Codebox at http://localhost:8000/ and be able to edit anything in the directory your Vagrantfile is in, if your firewall allows others on your network should also have access at http://<your_ip>:8000/, although you may need to add more users to the config object.

Going Further

On top of this it is quite simple to use Nginx to proxy Codebox and make it available at a domain, similar to how I setup Ajenti. In production you would need to do this and have Nginx secure the connection with SSL and Basic Auth.