Skip to content

Instantly share code, notes, and snippets.

@dansimau
Forked from urschrei/mkflask.sh
Created May 20, 2012 23:52
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save dansimau/2759962 to your computer and use it in GitHub Desktop.
Save dansimau/2759962 to your computer and use it in GitHub Desktop.
Bootstrap a Flask project on Ubuntu Precise, with Puppet, Vagrant and Fabric. (For latest version, see https://github.com/dansimau/flask-bootstrap)
*~
*.pyc
.vagrant
venv
from flask import Flask
app = Flask(__name__)
app.config.from_pyfile('%s/config/default.cfg' % app.root_path)
app.config.from_envvar('FLASK_CONFIG')
from fabric.decorators import task
import app
import db
import puppet
import virtualenv
@task
def build():
"""Execute build tasks for all components."""
virtualenv.build()
db.build()

Quick start

After installing Vagrant, create and boot the VM:

vagrant up

SSH to the VM:

vagrant ssh

Run your app:

fab app.run

Notes

After initial boot, you should freeze the newly-installed pip packages at their versions:

pip freeze > requirements.txt

Run fab db.build to init alembic in this directory.

from fabric.decorators import task
from fabric.context_managers import settings
from utils import do
@task
def run():
"""Start app in debug mode (for development)."""
do('export FLASK_CONFIG=$PWD/app/config/dev.cfg && venv/bin/python ./run.py')
remote_path = ''
branches = {
'staging': {
'hosts': ''
},
'uat': {
'hosts': ''
},
'production': {
'hosts': ''
},
}
from fabric.decorators import task
from fabric.context_managers import settings, hide
from fabric.colors import cyan
from utils import do
config_file_path = 'db/alembic.ini'
@task
def build():
"""Initialise and migrate database to latest version."""
print(cyan('\nUpdating database...'))
with settings(hide('warnings'), warn_only=True):
do('venv/bin/alembic -c %s init db/postgresql' % config_file_path)
SECRET_KEY = ''
DEBUG = True
class fabric {
package { 'Fabric':
provider => 'pip',
ensure => 'present',
}
}
class git {
package { 'git':
ensure => 'installed',
}
}
class postgresql {
# postgresql-dev required for Python's psycopg2
package { [ 'postgresql', 'postgresql-server-dev-all' ]:
ensure => 'installed',
}
service { 'postgresql':
ensure => running,
require => Package[postgresql],
}
}
class python {
include python::modules
package { 'python':
ensure => installed,
}
}
class vagrant {
line { 'line-venv-activate':
ensure => present,
file => '/home/vagrant/.bashrc',
line => 'cd /vagrant && . venv/bin/activate',
}
}
define line($file, $line, $ensure = 'present') {
case $ensure {
default : { err ( "unknown ensure value ${ensure}" ) }
present: {
exec { "/bin/echo '${line}' >> '${file}'":
unless => "/bin/grep -qFx '${line}' '${file}'"
}
}
absent: {
exec { "/bin/grep -vFx '${line}' '${file}' | /usr/bin/tee '${file}' >/dev/null 2>&1":
onlyif => "/bin/grep -qFx '${line}' '${file}'"
}
}
uncomment: {
exec { "/bin/sed -i -e'/${line}/s/#\\+//' '${file}'":
onlyif => "/bin/grep '${line}' '${file}' | /bin/grep '^#' | /usr/bin/wc -l"
}
}
comment: {
exec { "/bin/sed -i -e'/${line}/s/\\(.\\+\\)$/#\\1/' '${file}'":
onlyif => "/usr/bin/test `/bin/grep '${line}' '${file}' | /bin/grep -v '^#' | /usr/bin/wc -l` -ne 0"
}
}
}
}
class python::modules {
package { [ 'python-virtualenv', 'python-dev', ]:
ensure => 'installed',
}
}
from fabric.api import env
from fabric.decorators import task
from fabric.colors import cyan
from utils import do
@task
def check():
"""Syntax check on Puppet config."""
print(cyan('\nChecking puppet syntax...'))
do('find puppet -type f -name \'*.pp\' |xargs puppet parser validate')
@task
def apply():
"""Apply Puppet manifest."""
print(cyan('\nApplying puppet manifest...'))
do('sudo puppet apply --modulepath=puppet/modules/ puppet/manifests/standalone.pp' % env)
alembic
Flask
Flask-SQLAlchemy
Flask-Assets
psycopg2
#!/usr/bin/env python
from app import app
app.run(host='0.0.0.0')
#
# Standalone manifest - for dev Vagrant box.
#
import 'lib/*.pp'
include fabric
include git
include postgresql
include python
include vagrant
from fabric.api import require, env, local, run as fab_run
from fabric.utils import abort
import config
def do(*args, **kwargs):
"""
Runs command locally or remotely depending on whether a remote host has
been specified.
"""
if env.host_string:
with settings(cd(config.remote_path)):
return fab_run(*args, **kwargs)
else:
return local(*args, **kwargs)
def require_host():
"""
Forces a remote host to be set, automatically detecting it from the current
git branch if possible.
"""
if not env.host_string:
# Detect current branch from git, based on config
branch = local('git symbolic-ref -q HEAD', capture=True).split('/')[2]
# Ensure the current branch matches up to a known branch
if branch in config.branches:
env.branch = branch
require('branch')
# Manually set host_string variable
env.host_string = env.branches[branch]['hosts']
require('host_string')
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant::Config.run do |config|
# All Vagrant configuration is done here. The most common configuration
# options are documented and commented below. For a complete reference,
# please see the online documentation at vagrantup.com.
# Every Vagrant virtual environment requires a box to build off of.
config.vm.box = "precise64"
# The url from where the 'config.vm.box' box will be fetched if it
# doesn't already exist on the user's system.
config.vm.box_url = "http://files.vagrantup.com/precise64.box"
# Boot with a GUI so you can see the screen. (Default is headless)
# config.vm.boot_mode = :gui
# Assign this VM to a host-only network IP, allowing you to access it
# via the IP. Host-only networks can talk to the host machine as well as
# any other machines on the same network, but cannot be accessed (through this
# network interface) by any external networks.
config.vm.network :hostonly, "172.16.1.2"
# Assign this VM to a bridged network, allowing you to connect directly to a
# network using the host's network device. This makes the VM appear as another
# physical device on your network.
# config.vm.network :bridged
# Forward a port from the guest to the host, which allows for outside
# computers to access the VM, whereas host only networking does not.
config.vm.forward_port 5000, 5000
# Share an additional folder to the guest VM. The first argument is
# an identifier, the second is the path on the guest to mount the
# folder, and the third is the path on the host to the actual folder.
config.vm.share_folder("v-root", "/vagrant", ".", :nfs => true)
# Update apt
config.vm.provision :shell, :inline => "aptitude -q2 update"
# Enable provisioning with Puppet stand alone. Puppet manifests
# are contained in a directory path relative to this Vagrantfile.
# You will need to create the manifests directory and a manifest in
# the file base.pp in the manifests_path directory.
config.vm.provision :puppet do |puppet|
puppet.manifests_path = "puppet/manifests"
puppet.module_path = "puppet/modules"
puppet.manifest_file = "standalone.pp"
end
# Application provision
config.vm.provision :shell, :inline => "cd /vagrant && stdbuf -o0 fab build; exit 0"
end
from fabric.decorators import task
from fabric.context_managers import settings, hide
from fabric.colors import cyan, red
from fabric.utils import abort
from utils import do
@task
def build():
"""Build or update the virtualenv."""
with settings(hide('stdout')):
print(cyan('\nUpdating venv, installing packages...'))
do('[ -e venv ] || virtualenv venv --no-site-packages')
# annoyingly, pip prints errors to stdout (instead of stderr), so we
# have to check the return code and output only if there's an error.
with settings(warn_only=True):
pip = do('venv/bin/pip install -r requirements.txt', capture=True)
if pip.failed:
print(red(pip))
abort("pip exited with return code %i" % pip.return_code)
@drcongo
Copy link

drcongo commented May 25, 2012

I think fabfile/app.py should be...

from fabric.decorators import task
from fabric.context_managers import settings
from utils import do


@task
def run():
    """Start app in debug mode (for development)."""
    do('export FLASK_CONFIG=$PWD/app/config/dev.cfg && venv/bin/python ./app.py')

I could be very wrong though.

@dansimau
Copy link
Author

@drcongo: The purpose of fab app.run is to invoke run.py which launches the built-in Flask development server. There is no app.py to run.

It would be nice to eliminate run.py altogether, though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment