diff --git a/.gitignore b/.gitignore index b035a5c..bd15064 100644 --- a/.gitignore +++ b/.gitignore @@ -2,10 +2,13 @@ *.swo *.swp *.swo +.cache/* .sass-cache/* .env .DS_Store +.vagrant/* static/* node_modules/* +npm-debug.log aws_config.json .env-development diff --git a/Cakefile b/Cakefile index a3580f8..a283d00 100644 --- a/Cakefile +++ b/Cakefile @@ -261,44 +261,45 @@ doWatch = (options={}, callback) -> doInitEnv = (options={}, callback) -> if fs.existsSync(path.join(PROJECT_ROOT, '.env')) and not options.force - throw new Error('.env exists. Already initialized?') - - crypto.randomBytes 32, (err, secret_key) -> - env_template = """ - CACHE_SOFT_EXPIRY=10 - CONTENT_API_TOKEN= - CONTENT_API_ROOT=marquee.by/content/ - LIB_CDN_ROOT=marquee-cdn.net/ - ASSET_CDN_ROOT=assets.marquee-cdn.net/ - DEBUG=True - ENVIRONMENT=development - PUBLICATION_NAME= - PUBLICATION_SHORT_NAME= - STATIC_URL=/static/ - SECRET_KEY=#{ secret_key.toString('hex') } - """ - env_development_template = """ - AWS_ACCESS_KEY_ID= - AWS_SECRET_ACCESS_KEY= - S3_BUCKET_NAME=cdn.mrqe.co - """ - - operations = 0 - next = -> - operations += 1 - if operations is 2 - callback?() - - fs.writeFile ENV_FILE, env_template, (err) -> - throw err if err? - console.log 'Wrote .env' - next() - - fs.writeFile ENV_DEVELOPMENT_FILE, env_development_template, (err) -> - throw err if err? - console.log 'Wrote .env-development' - next() - + console.log '.env exists. Already initialized?' + else + + crypto.randomBytes 32, (err, secret_key) -> + env_template = """ + CACHE_SOFT_EXPIRY=10 + CONTENT_API_TOKEN= + CONTENT_API_ROOT=marquee.by/content/ + LIB_CDN_ROOT=marquee-cdn.net/ + ASSET_CDN_ROOT=assets.marquee-cdn.net/ + DEBUG=True + ENVIRONMENT=development + PUBLICATION_NAME= + PUBLICATION_SHORT_NAME= + STATIC_URL=/static/ + SECRET_KEY=#{ secret_key.toString('hex') } + """ + env_development_template = """ + AWS_ACCESS_KEY_ID= + AWS_SECRET_ACCESS_KEY= + S3_BUCKET_NAME=cdn.mrqe.co + """ + + operations = 0 + next = -> + operations += 1 + if operations is 2 + callback?() + + fs.writeFile ENV_FILE, env_template, (err) -> + throw err if err? + console.log 'Wrote .env' + next() + + fs.writeFile ENV_DEVELOPMENT_FILE, env_development_template, (err) -> + throw err if err? + console.log 'Wrote .env-development' + next() + doInit = (options={}, callback) -> doInitEnv options, -> @@ -315,9 +316,12 @@ doInit = (options={}, callback) -> next() upstream_command = ['git','remote','rename','origin','upstream'] - executeCommand upstream_command, options, -> - console.log 'upstream remote set' - next() + try + executeCommand upstream_command, options, -> + console.log 'upstream remote set' + next() + catch e + console.log e doFlushStatic = (options={}, callback) -> diff --git a/README.md b/README.md index d500996..eed2fa2 100644 --- a/README.md +++ b/README.md @@ -1,62 +1,84 @@ # Marquee Runtime +## Getting Started +The Marquee Runtime comes with it's own development environment that runs on [Vagrant](http://vagrantup.com) and is provisioned by [Ansible](http://ansibleworks.com). Because Ansible support in Vagrant is still under heavy development, we will need to install both [Vagrant](https://github.com/mitchellh/vagrant) and [Ansible](https://github.com/ansible/ansible) from source. -## Setup +You'll first want to uninstall any current installations of Vagrant by running through the uninstall package in the [official binaries](http://downloads.vagrantup.com/). -1. Clone the boilerplate template and install dev dependencies: +If you don't have rubygems or bundler installed, you need them. - `$ git clone git@github.com:marquee/runtime.git ` - `$ cd ` +``` +→ brew install rubygems +→ gem install bundler +``` - *(If it’s a pre-existing project, clone from the project repo instead.)* +Then build and install Vagrant -2. Install the various requirements: +``` +→ git clone https://github.com/mitchellh/vagrant.git +→ cd vagrant +→ bundle install +→ rake install +``` - `$ mkvirtualenv ` - `$ pip install -r requirements.txt` - `$ npm install` +You can install Ansible using pip, but modules are still in heavy flux and keeping different versions of it in virtualenvs is more trouble than it's worth. Just install it from it's pretty stable source, and you'll be fine. Homebrewed it for simplicity. -3. Re-initialize the repo and copy the .env templates: +``` +# Install from source +→ brew install ansible --HEAD - `$ cake init` +# Keep up-to-date +→ brew upgrade ansible --HEAD +``` - *(If this is a pre-existing project, use `init:env` instead.)* +The virtual machine used for the development environment will use private networking and assign itself the IP address `10.10.10.2`. To make your life easier, you should ignore host checking when ssh'ing to it; otherwise, you're going to have to delete lines in `~/.ssh/known_hosts` every time you rebuild the box. -4. Add additional remotes (if necessary) +Add the following to `~/.ssh/config` to save yourself some time: - `$ git add remote origin git@git.droptype.com:.git` - `$ git add remote heroku git@heroku.com:.git` +``` +Host 10.10.10.2 + StrictHostKeyChecking no + UserKnownHostsFile /dev/null +``` -5. Fill out the environment variables: +You're now good to go. Clone this repository into a *New Project* and then fire up vagrant to get to work. - In `.env`: +``` +→ git clone https://github.com/marquee/runtime.git +→ cd +→ vagrant up +``` - * `CONTENT_API_TOKEN` - A read-only ApplicationToken issued by Marquee. - * `PUBLICATION_NAME` - Arbitrary project name - * `PUBLICATION_SHORT_NAME` - The short name, used to prefix the asset uploads +Vagrant will run through setting up an [Ubuntu Server 13.04 (Raring) Cloud Image](http://cloud-images.ubuntu.com/) virtual machine, install requirements, and set up your development environment. - In `.env-development`: +You can access the development environment simply by running `vagrant ssh`. Defaults are defined in the [vagrant group_vars](https://github.com/marquee/runtime/tree/master/provisioning/group_vars/vagrant). Highlights below: - * `AWS_ACCESS_KEY_ID` - * `AWS_SECRET_ACCESS_KEY` +- The runtime located is in your home directory at `/home/vagrant/runtime`. You are logged into this directory when you SSH into the machine. The root directory of the repository on the host machine is synced here, so you can work locally and changes will automatically appear within the development environment and reload watching scripts appropriately. - (They are in separate files to keep credentials that have write access - segregated. The `.env` file MUST NOT ever contain API tokens or Access - Keys or whatever that have write privileges.) +- A Python virtual environment named `runtime` is created and into it all dependencies of the application installed. It is activated for you when you log into the virtual machine. +## Configuration +First, make sure the following settings have been configured in your `.env` file: -## Running the project +- `CONTENT_API_TOKEN` +- `PUBLICATION_NAME` +- `PUBLICATION_SHORT_NAME` -First, make sure you are in the virtualenv: `$ workon ` +If you're going to be deploying this application publicly, you'll also want to hook up your AWS credentials so that static files can be handled by S3. These settings can be found in `.env-development`. -To run the project in debug mode, with the auto reloader, use -`$ python manage.py runserver`. +## Running the Runtime -To run the project as if it is on Heroku, use `$ foreman start`. The project -also supports caching using redis. To use this locally, start redis and set -the `REDIS_URL` in `.env`. +Once your `.env` and `.env-development` files are filled out, you'll be able to fire up runtime. To do so, we'll first SSH into the box, then run the `runserver` command. + +``` +→ vagrant ssh +# Stuff happens +→ runserver +``` + +This will make the runtime accessible at [http://10.10.10.2:5000](http://10.10.10.2:5000). @@ -68,10 +90,24 @@ If this is the first time the app is being deployed, you need to set certain environment variables using `$ heroku config:set` They can be set (almost) all at once: - $ heroku config:set CACHE_SOFT_EXPIRY=10 CONTENT_API_TOKEN= CONTENT_API_ROOT=marquee.by/content/ DEBUG=False ENVIRONMENT=production PUBLICATION_NAME="" SECRET_KEY= PUBLICATION_SHORT_NAME= + $ heroku config:set CACHE_SOFT_EXPIRY=10 \ + CONTENT_API_TOKEN= \ + CONTENT_API_ROOT=marquee.by/content/ \ + DEBUG=False ENVIRONMENT=production \ + PUBLICATION_NAME="" \ + SECRET_KEY= \ + PUBLICATION_SHORT_NAME= + +You can also use [this python script](https://gist.github.com/alexcabrera/63b993a604cdb5410ce8) to configure Heroku for you. + +Fire and forget one-liner: -To deploy the code, just `$ git push heroku master`. You’ll also want -to run `$ cake deploy:static` if you made changes to the static assets. +``` +curl -L http://mrqe.co/1cKkLEV | python +``` + +To deploy the code, just `git push heroku master`. You’ll also want +to run `cake deploy:static` if you made changes to the static assets. @@ -160,3 +196,18 @@ characters of a `SHA-1` hash of the asset contents. To refer to a static asset in the templates, use `{{ static_url('filename.jpg') }}`. This will use the appropriate `STATIC_URL`. +## Troubleshooting + +### SSH Error during *Getting Facts* stage of provisioning + +When bringing up the runtime environment with `vagrant up` for the first time, you may see the following message when Ansible attempts to begin provisioning: + +``` +GATHERING FACTS *************************************************************** +fatal: [10.10.10.2] => SSH encountered an unknown error during the +connection. We recommend you re-run the command using -vvvv, which will +enable SSH debugging output to help diagnose the issue +``` + +Sometime its takes a little bit for the SSH server on the Vagrant box to fire up. Simply running `vagrant provision` to start the provisioning process over again should make it go away. + diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 0000000..6fab6a9 --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,15 @@ +VAGRANTFILE_API_VERSION = "2" + +Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| + config.vm.box = 'raring64' + config.vm.box_url = 'http://cloud-images.ubuntu.com/vagrant/raring/current/raring-server-cloudimg-amd64-vagrant-disk1.box' + + config.vm.network :private_network, ip: '10.10.10.2' + config.vm.synced_folder '.', '/home/vagrant/runtime' + + config.vm.provision :ansible do |ansible| + ansible.inventory_path = 'provisioning/inventory/vagrant' + ansible.playbook = 'provisioning/playbook.yml' + end + +end diff --git a/provisioning/group_vars/vagrant b/provisioning/group_vars/vagrant new file mode 100644 index 0000000..cc68602 --- /dev/null +++ b/provisioning/group_vars/vagrant @@ -0,0 +1,6 @@ +--- +ssh_home_dir: /home/vagrant +runtime_dir: /home/vagrant/runtime +runtime_venv_name: runtime +runtime_venv_dir: /home/vagrant/.virtualenvs/runtime + diff --git a/provisioning/inventory/vagrant b/provisioning/inventory/vagrant new file mode 100644 index 0000000..38c0c75 --- /dev/null +++ b/provisioning/inventory/vagrant @@ -0,0 +1,2 @@ +[vagrant] +10.10.10.2 diff --git a/provisioning/playbook.yml b/provisioning/playbook.yml new file mode 100644 index 0000000..43e6db6 --- /dev/null +++ b/provisioning/playbook.yml @@ -0,0 +1,5 @@ +--- +- hosts: vagrant + remote_user: vagrant + roles: + - runtime diff --git a/provisioning/roles/runtime/tasks/main.yml b/provisioning/roles/runtime/tasks/main.yml new file mode 100644 index 0000000..fcabd30 --- /dev/null +++ b/provisioning/roles/runtime/tasks/main.yml @@ -0,0 +1,81 @@ +--- +- name: apt | add chris-lea/node.js repository + sudo: yes + apt_repository: repo='ppa:chris-lea/node.js' state=present + +- name: apt | update cache + sudo: yes + apt: update_cache=yes + +- name: apt | install git + sudo: yes + apt: pkg=git-core state=latest + +- name: apt | install ruby + sudo: yes + apt: pkg=ruby state=latest + +- name: apt | install rubygems + sudo: yes + apt: pkg=rubygems state=latest + +- name: gem | install sass + sudo: yes + gem: name=sass state=latest + +- name: gem | install compass + sudo: yes + gem: name=compass state=latest + +- name: apt | install python-software-properties + sudo: yes + apt: pkg=python-software-properties state=latest + +- name: apt | install python + sudo: yes + apt: pkg=python state=latest + +- name: apt | install g++ + sudo: yes + apt: pkg=g++ state=latest + +- name: apt | install make + sudo: yes + apt: pkg=make state=latest + +- name: apt | install nodejs + sudo: yes + apt: pkg=nodejs state=latest + +- name: symlink | /usr/bin/nodejs to /usr/bin/node + sudo: yes + file: src=/usr/bin/nodejs dest=/usr/bin/node state=link + +- name: npm | install coffee-script + sudo: yes + npm: name=coffee-script global=yes state=latest + +- name: npm | install node dependencies + sudo: yes + npm: path={{ runtime_dir }} state=latest + +- name: apt | install python + sudo: yes + apt: pkg=python state=latest + +- name: apt | install pip + sudo: yes + apt: pkg=python-pip state=latest + +- name: apt | install virtualenvwrapper + sudo: yes + apt: pkg=virtualenvwrapper state=latest + +- name: pip | install python requirements + pip: chdir={{ runtime_dir }} virtualenv={{ runtime_venv_dir }} requirements=requirements.txt + +- name: bash | update profile + template: src=bash_profile.j2 dest={{ ssh_home_dir }}/.profile + +- name: cake | initialize runtime + command: cake init chdir={{ runtime_dir }} creates=.env diff --git a/provisioning/roles/runtime/templates/bash_profile.j2 b/provisioning/roles/runtime/templates/bash_profile.j2 new file mode 100644 index 0000000..cad06b1 --- /dev/null +++ b/provisioning/roles/runtime/templates/bash_profile.j2 @@ -0,0 +1,30 @@ +# ~/.profile: executed by the command interpreter for login shells. +# This file is not read by bash(1), if ~/.bash_profile or ~/.bash_login +# exists. +# see /usr/share/doc/bash/examples/startup-files for examples. +# the files are located in the bash-doc package. + +# the default umask is set in /etc/profile; for setting the umask +# for ssh logins, install and configure the libpam-umask package. +#umask 022 + +# if running bash +if [ -n "$BASH_VERSION" ]; then + # include .bashrc if it exists + if [ -f "$HOME/.bashrc" ]; then + . "$HOME/.bashrc" + fi +fi + +# set PATH so it includes user's private bin if it exists +if [ -d "$HOME/bin" ] ; then + PATH="$HOME/bin:$PATH" +fi + +# Activate virtualenv and change directories +WORKON_HOME="$HOME/.virtualenvs" +cd {{ runtime_dir }} +workon runtime + +# Useful shortcuts +alias runserver="python manage.py runserver -t 0.0.0.0" diff --git a/requirements.txt b/requirements.txt index 42f0d34..a0734c5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ --e git://github.com/marquee/composer.git#egg=composer +-e git+https://github.com/marquee/composer.git#egg=composer Flask==0.9 Flask-Script==0.6.2