Some time ago I wrote
A happy developer's environment aiming to share my sanbox creation scripts.
I promised a "part 2" that never was. Sorry, I am busy and/or lazy.
Now that my magic-auto-sandbox thing has evolved, it's time to revisit it.
Get virtualbox
In my case, adding repositories as instructed in
https://www.virtualbox.org/wiki/Linux_Downloads.
Get vagrant
It is a virtualization automation tool that was a ruby gem in its origins.
Nowadays, the recommended (linux) install procedure is downloading packages for your favourite distro from:
https://www.vagrantup.com/downloads.html
Create config files
Three files, read and adapt to your particular needs:
./Vagrantfile
It is vagrant's config file, it tells vagrant what machine to start if it already exists or how to create it, if it hasn't been yet.
Vagrant.configure("2") do |vagrant|
vagrant.vm.define "project_name" do |config|
# Inject code from outer FS (edit outside, run inside)
# required by type: "nfs"
config.vm.network "private_network", ip: '192.168.50.10'
config.vm.synced_folder "./code", "/home/vagrant/code", type: "nfs"
# Access app with outer browser
# otherwise use rails s -b 0.0.0.0 and private ip
config.vm.network "forwarded_port", guest: 3000, host: 3000
config.vm.network "public_network", bridge: 'wlan0'
# http://rvm.io/integration/vagrant
config.vm.provision :shell, path: 'provision/bootstrap.sh', keep_color: true
config.vm.provider "virtualbox" do |vb, override|
# http://www.vagrantbox.es/
# Prepare with: wget -O ../trusty-vbox.box http://goo.gl/8wqNnb
override.vm.box_url = "../trusty-vbox.box"
override.vm.box = "trusty-vbox"
vb.name = 'project_name'
vb.memory = ENV['VM_MEM'] || 1024
vb.cpus = ENV['VM_CPU'] || 2
vb.customize ['modifyvm', :id, "--natdnshostresolver1", "on"]
end
end
end
./provision/bootstrap.sh
If you happen to be a systems person (or have one near) here is where they will suggest
puppet or
chef or whatever "configuration automation" tool.
A simple shell script
is enough for Xavi Noria, and is enough for me.
#!/usr/bin/env bash
## Ruby on Rails Development Environment Provision Script ##
# Inspired by @fxn's https://github.com/rails/rails-dev-box
# Configuration variables with custom values
RUBY_VERSION="2.2.1"
APP="project_name"
HOSTNAME='project_name.dev'
echo -e "\n== «$APP» on _$RUBY_VERSION_ ==\n"
# Load auxiliary functions:
source /vagrant/provision/functions.sh
# General System layout
install 'development tools' build-essential
set_timezone 'Etc/UTC'
setup_locale 'LANG="en_US.UTF-8"\nLANGUAGE="en_US:en LC_ALL=en_US.UTF-8"\n'
hostname $HOSTNAME
install Git git
# Ruby & Rails:
install_rvm
install_postgres
install 'Nokogiri dependencies' libxml2 libxml2-dev libxslt1-dev
install_node
# Developer happiness stuff:
install 'ACK-Grep' ack-grep
install 'Log colorizer' ccze
install 'Vim' vim
# App specific stuff
install_bower
echo -e "\033[1;32m All set, rock on! thanks to the awesome @fxn!\033[0m"
./provision/functions.sh
These are the parts that change less from project to project, so I have extracted them for easier reuse.
#!/usr/bin/env bash
echo -e "\033[1;32m Loading auxiliary functions for provision...\033[0m"
# The ouput of all these installation steps is noisy. With this utility
# the progress report is nice and concise.
function install {
echo installing $1 ...
shift
debian_frontend=noninteractive apt-get -y install "$@" >/dev/null 2>&1
echo -e "\033[1;32m [OK] \033[0m"
}
# Executes a command as the vagrant user
# Example:
# run 'Description' 'command'
function vagrant_run {
echo -e "\n$1:"
eval "su -l vagrant -c '$2'"
}
# Installs ruby environment through RVM
function install_rvm {
if [ ! -x /home/vagrant/.rvm/scripts/rvm ]; then
vagrant_run "Getting RVM keyring" \
'gpg --keyserver hkp://keys.gnupg.net --recv-keys D39DC0E3'
echo -e "\nInstalling RVM:"
su -l vagrant -c 'curl -sSL https://get.rvm.io | bash -s -- stable'
echo -e "\nLoading RVM to continue:"
su -l vagrant -c 'source $HOME/.rvm/scripts/rvm'
echo -e "\nInstalling Ruby ($RUBY_VERSION)"
su -l vagrant -c "rvm use $RUBY_VERSION --default --install"
echo -e "\nInstalling Bundler:"
su -l vagrant -c 'gem install bundler'
su -l vagrant -c 'echo "gem: --no-rdoc --no-ri" >> /home/vagrant/.gemrc'
fi
}
# Installs PostgreSQL database
function install_postgres {
echo -e "\nInstalling Postgres DB and config..."
if [ ! -x /usr/bin/psql ]; then
install PostgreSQL postgresql postgresql-contrib libpq-dev
sudo -u postgres createuser --superuser vagrant
sudo -u postgres createdb -O vagrant ${APP}_development
sudo -u postgres createdb -O vagrant ${APP}_test
# enable pg_admin access from host machine:
PG_CONF='/etc/postgresql/9.3/main'
PUBLIC_IP=$(ip -o -4 addr show eth1 | cut -d ' ' -f 7)
echo "listen_addresses = '*'" >> $PG_CONF/postgresql.conf
echo "host all all $PUBLIC_IP trust" >> $PG_CONF/pg_hba.conf
service postgresql restart
fi
}
# Set VM timezone to have consistent test results
function set_timezone {
# http://manpages.ubuntu.com/manpages/precise/man7/debconf.7.html
echo "Setting timezone to: '$1'"
export DEBCONF_NONINTERACTIVE_SEEN=true DEBIAN_FRONTEND=noninteractive
echo "$1" > /etc/timezone
dpkg-reconfigure tzdata
}
function setup_locale {
# Needed for docs generation.
echo -e "$1" > /etc/default/locale
}
function install_node {
install 'ExecJS runtime (for coffee)' nodejs
install 'Npm' npm
su -l vagrant -c 'if [ ! -f $HOME/.npmrc ]; then
echo prefix = $HOME/.node >> $HOME/.npmrc
echo "export PATH=\"\$PATH:\$HOME/.node/bin\"" >> $HOME/.bashrc
source $HOME/.bashrc
fi'
if [ ! -f /usr/bin/node ]; then
ln -s /usr/bin/nodejs /usr/bin/node
fi
}
function install_bower {
echo -e "\nInstalling Bower and vendored assets..."
BOWER_FILE='/home/vagrant/code/.bowerrc'
if [ ! -f $BOWER_FILE ]; then
echo -e "{\n\t"directory": "vendor/assets/components"\n}" >> $BOWER_FILE
fi
if [ ! -x /home/vagrant/.node/bin/bower ]; then
vagrant_run "Installing bower" "npm install -g bower --silent"
# Installing bower dependencies
vagrant_run "Installing bower assets" \
'cd /home/vagrant/code; /home/vagrant/.node/bin/bower install'
fi
}
Get the base machine
As it is instructed in the comments on Vagrantfile:
wget -O ../trusty-vbox.box http://goo.gl/8wqNnb
You will only have to do this once for all the projects you may share the base machine with. (All of them in my case)
Create the machine
You use the command:
vagrant up
and it will create (and provision) the machine the first time is called and all following occasions it will just boot it up.
To connect to your machine you use:
vagrant ssh
To stop it:
vagrant halt
To destroy (in case you are debugging your creation/provision process):
vagrant destroy
There are improvements to the experience if you set up sudo and ~/.ssh/config on the host machine so they never ask you for passwords, but I will let you decide if you want to go that path while you ponder the security implications.