First commit
commit
f4d828fa39
|
@ -0,0 +1,12 @@
|
||||||
|
syntax: glob
|
||||||
|
|
||||||
|
*.json
|
||||||
|
*.gunicorn.*
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
db.db
|
||||||
|
local_settings.py
|
||||||
|
logs
|
||||||
|
media/admin
|
||||||
|
media/cache
|
||||||
|
media/css/style.css
|
|
@ -0,0 +1,28 @@
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import site
|
||||||
|
|
||||||
|
parent = os.path.dirname
|
||||||
|
site_dir = parent(os.path.abspath(__file__))
|
||||||
|
project_dir = parent(parent(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
sys.path.insert(0, project_dir)
|
||||||
|
sys.path.insert(0, site_dir)
|
||||||
|
|
||||||
|
import local_settings
|
||||||
|
site.addsitedir(local_settings.VIRTUALENV_PATH)
|
||||||
|
|
||||||
|
from django.core.management import setup_environ
|
||||||
|
import settings
|
||||||
|
setup_environ(settings)
|
||||||
|
|
||||||
|
import django.core.handlers.wsgi
|
||||||
|
application = django.core.handlers.wsgi.WSGIHandler()
|
||||||
|
|
||||||
|
from werkzeug.debug import DebuggedApplication
|
||||||
|
application = DebuggedApplication(application, evalex=True)
|
||||||
|
|
||||||
|
def null_technical_500_response(request, exc_type, exc_value, tb):
|
||||||
|
raise exc_type, exc_value, tb
|
||||||
|
from django.views import debug
|
||||||
|
debug.technical_500_response = null_technical_500_response
|
|
@ -0,0 +1,215 @@
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
|
import os
|
||||||
|
from fabric.api import *
|
||||||
|
from dwfab.misc import growl as _growl
|
||||||
|
|
||||||
|
|
||||||
|
UPLOADS_DIR = 'media/uploads'
|
||||||
|
APPS_TO_SYNC = [
|
||||||
|
'auth',
|
||||||
|
'contenttypes',
|
||||||
|
'redirects',
|
||||||
|
'registration',
|
||||||
|
]
|
||||||
|
|
||||||
|
def prod():
|
||||||
|
'''Run on the production site.'''
|
||||||
|
env.env_name = 'production'
|
||||||
|
env.hosts = ['nick@beta.snipt.net:38038']
|
||||||
|
env.process_name = 'beta-snipt'
|
||||||
|
env.site_path = '/var/www/beta-snipt'
|
||||||
|
env.venv_path = '/home/nick/.virtualenvs/beta-snipt'
|
||||||
|
env.site_url = 'https://beta.snipt.net/'
|
||||||
|
env.uploads_path = env.site_path + '/' + UPLOADS_DIR
|
||||||
|
|
||||||
|
|
||||||
|
def _python(cmd):
|
||||||
|
return env.venv_path.rstrip('/') + '/bin/python ' + cmd
|
||||||
|
def _lpython(cmd):
|
||||||
|
return os.getenv('VIRTUAL_ENV').rstrip('/') + '/bin/python ' + cmd
|
||||||
|
def _pip(cmd):
|
||||||
|
return env.venv_path.rstrip('/') + '/bin/pip ' + cmd
|
||||||
|
|
||||||
|
|
||||||
|
def pull_uploads():
|
||||||
|
'''Copy the uploads from the site to your local machine.'''
|
||||||
|
require('uploads_path', provided_by=['prod'])
|
||||||
|
|
||||||
|
sudo('chmod -R a+r "%s"' % env.uploads_path)
|
||||||
|
|
||||||
|
rsync_command = r"""rsync -av -e 'ssh -p %s' %s@%s:%s %s""" % (
|
||||||
|
env.port,
|
||||||
|
env.user, env.host,
|
||||||
|
env.uploads_path.rstrip('/') + '/',
|
||||||
|
UPLOADS_DIR.rstrip('/')
|
||||||
|
)
|
||||||
|
print local(rsync_command, capture=False)
|
||||||
|
|
||||||
|
def pull_data():
|
||||||
|
'''Copy the data from the site to your local machine.'''
|
||||||
|
require('site_path', provided_by=['prod'])
|
||||||
|
require('venv_path', provided_by=['prod'])
|
||||||
|
|
||||||
|
for app in APPS_TO_SYNC:
|
||||||
|
local(_lpython('manage.py reset --noinput %s' % app))
|
||||||
|
local(_lpython('manage.py migrate --fake %s' % app))
|
||||||
|
|
||||||
|
for app in APPS_TO_SYNC:
|
||||||
|
with cd(env.site_path):
|
||||||
|
sudo(_python('manage.py dumpdata --format=json --indent=2 --natural ' +
|
||||||
|
'%s > snipt-fixtures-%s.json' % (app, app)))
|
||||||
|
get('%s/snipt-fixtures-%s.json' % (env.site_path, app), 'snipt-fixtures-%s.json' % app)
|
||||||
|
|
||||||
|
local(_lpython('manage.py loaddata snipt-fixtures-%s.json' % app))
|
||||||
|
|
||||||
|
def pull_all():
|
||||||
|
'''Copy the uploads and data from the site to your local machine.'''
|
||||||
|
pull_uploads()
|
||||||
|
pull_data()
|
||||||
|
_growl('Snipt: Pull Complete', 'The database and uploads have been refreshed.')
|
||||||
|
|
||||||
|
|
||||||
|
def reindex():
|
||||||
|
require('site_path', provided_by=['prod'])
|
||||||
|
|
||||||
|
with cd(env.site_path):
|
||||||
|
sudo(_python('manage.py rebuild_index --noinput'))
|
||||||
|
|
||||||
|
|
||||||
|
def syncdb():
|
||||||
|
'''Run syncdb.'''
|
||||||
|
require('site_path', provided_by=['prod'])
|
||||||
|
require('venv_path', provided_by=['prod'])
|
||||||
|
|
||||||
|
with cd(env.site_path):
|
||||||
|
sudo(_python('manage.py syncdb'))
|
||||||
|
|
||||||
|
def migrate():
|
||||||
|
'''Run any needed migrations.'''
|
||||||
|
require('site_path', provided_by=['prod'])
|
||||||
|
require('venv_path', provided_by=['prod'])
|
||||||
|
|
||||||
|
with cd(env.site_path):
|
||||||
|
sudo(_python('manage.py migrate'))
|
||||||
|
|
||||||
|
def requirements():
|
||||||
|
'''Copy local requirements.txt to the site and install requirements.'''
|
||||||
|
require('site_path', provided_by=['prod'])
|
||||||
|
require('venv_path', provided_by=['prod'])
|
||||||
|
|
||||||
|
with cd(env.site_path):
|
||||||
|
put('requirements.txt', 'requirements.txt')
|
||||||
|
run(_pip('install -r requirements.txt'))
|
||||||
|
run('hg revert requirements.txt')
|
||||||
|
|
||||||
|
|
||||||
|
def retag():
|
||||||
|
'''Check which revision the site is at and update the local tag.
|
||||||
|
|
||||||
|
Useful if someone else has deployed (which makes your production/staging local
|
||||||
|
tag incorrect.
|
||||||
|
'''
|
||||||
|
require('site_path', provided_by=['prod'])
|
||||||
|
require('env_name', provided_by=['prod'])
|
||||||
|
|
||||||
|
with cd(env.site_path):
|
||||||
|
current = run('hg id --rev . --quiet').strip(' \n+')
|
||||||
|
|
||||||
|
local('hg tag --local --force %s --rev %s' % (env.env_name, current))
|
||||||
|
|
||||||
|
def deploy(rev='.'):
|
||||||
|
'''Deploy your current revision to the site.
|
||||||
|
|
||||||
|
You can also specify a different revision to deploy by passing an argument:
|
||||||
|
|
||||||
|
fab stag deploy:1a2cc06d
|
||||||
|
|
||||||
|
You can use your local revision numbers as arguments -- the full hash will be
|
||||||
|
looked up and used.
|
||||||
|
'''
|
||||||
|
require('site_path', provided_by=['prod'])
|
||||||
|
|
||||||
|
rev = local('hg id --rev %s --quiet' % rev).strip(' \n+')
|
||||||
|
|
||||||
|
local('hg push --rev %s' % rev)
|
||||||
|
|
||||||
|
with cd(env.site_path):
|
||||||
|
run('hg tag --local --force previous')
|
||||||
|
run('hg pull --rev %s' % rev)
|
||||||
|
run('hg update --rev %s' % rev)
|
||||||
|
|
||||||
|
retag()
|
||||||
|
|
||||||
|
syncdb()
|
||||||
|
migrate()
|
||||||
|
|
||||||
|
restart()
|
||||||
|
check()
|
||||||
|
|
||||||
|
|
||||||
|
def deploy_template(rev='.'):
|
||||||
|
'''Deploy your current revision to the site, without restarting the server.
|
||||||
|
|
||||||
|
You can also specify a different revision to deploy by passing an argument:
|
||||||
|
|
||||||
|
fab stag deploy:1a2cc06d
|
||||||
|
|
||||||
|
You can use your local revision numbers as arguments -- the full hash will be
|
||||||
|
looked up and used.
|
||||||
|
'''
|
||||||
|
require('site_path', provided_by=['prod'])
|
||||||
|
|
||||||
|
rev = local('hg id --rev %s --quiet' % rev).strip(' \n+')
|
||||||
|
|
||||||
|
local('hg push --rev %s' % rev)
|
||||||
|
|
||||||
|
with cd(env.site_path):
|
||||||
|
run('hg tag --local --force previous')
|
||||||
|
run('hg pull --rev %s' % rev)
|
||||||
|
run('hg update --rev %s' % rev)
|
||||||
|
|
||||||
|
retag()
|
||||||
|
check()
|
||||||
|
|
||||||
|
|
||||||
|
def rollback():
|
||||||
|
'''Roll the site back to the version it was at before the last deployment.
|
||||||
|
|
||||||
|
Things may break if migrations were made between the versions. TODO: Fix this.
|
||||||
|
'''
|
||||||
|
require('site_path', provided_by=['prod'])
|
||||||
|
|
||||||
|
with cd(env.site_path):
|
||||||
|
run('hg update previous')
|
||||||
|
|
||||||
|
retag()
|
||||||
|
restart()
|
||||||
|
check()
|
||||||
|
|
||||||
|
def check():
|
||||||
|
'''Check that the home page of the site returns an HTTP 200.
|
||||||
|
|
||||||
|
If it does, a normal growl message is sent.
|
||||||
|
|
||||||
|
If it does not, a warning is issued and a sticky growl message is sent.
|
||||||
|
'''
|
||||||
|
require('site_url', provided_by=['prod'])
|
||||||
|
|
||||||
|
if not '200 OK' in run('curl --silent -I "%s"' % env.site_url):
|
||||||
|
warn("Something is wrong (we didn't get a 200 response)!")
|
||||||
|
_growl('Snipt: DEPLOYMENT ERROR', 'Something went wrong. Please investigate.', sticky=True)
|
||||||
|
else:
|
||||||
|
_growl('Snipt: Deployment Complete', 'Deployment finished, site is working.')
|
||||||
|
|
||||||
|
def restart():
|
||||||
|
'''Restart the site's gunicorn server.'''
|
||||||
|
require('site_path', provided_by=['prod'])
|
||||||
|
require('process_name', provided_by=['prod'])
|
||||||
|
|
||||||
|
sudo('supervisorctl restart %s' % env.process_name)
|
||||||
|
|
||||||
|
with cd(env.site_path):
|
||||||
|
sudo('chmod a+r media/css/*.css')
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
gunicorn -c gunicorn.conf.py debug_wsgi:application
|
|
@ -0,0 +1,21 @@
|
||||||
|
bind = "unix:/tmp/gunicorn.snipt.sock"
|
||||||
|
daemon = True # Whether work in the background
|
||||||
|
debug = False # Some extra logging
|
||||||
|
logfile = ".gunicorn.log" # Name of the log file
|
||||||
|
loglevel = "info" # The level at which to log
|
||||||
|
pidfile = ".gunicorn.pid" # Path to a PID file
|
||||||
|
workers = 1 # Number of workers to initialize
|
||||||
|
umask = 0 # Umask to set when daemonizing
|
||||||
|
user = None # Change process owner to user
|
||||||
|
group = None # Change process group to group
|
||||||
|
proc_name = "gunicorn-snipt" # Change the process name
|
||||||
|
tmp_upload_dir = None # Set path used to store temporary uploads
|
||||||
|
|
||||||
|
|
||||||
|
def post_fork(server, worker):
|
||||||
|
server.log.info("Worker spawned (pid: %s)" % worker.pid)
|
||||||
|
|
||||||
|
import local_settings, monitor
|
||||||
|
if local_settings.DEBUG:
|
||||||
|
server.log.info("Starting change monitor.")
|
||||||
|
monitor.start(interval=1.0)
|
|
@ -0,0 +1,21 @@
|
||||||
|
bind = "unix:/tmp/gunicorn.snipt.sock"
|
||||||
|
daemon = False # Whether work in the background
|
||||||
|
debug = False # Some extra logging
|
||||||
|
logfile = ".gunicorn.log" # Name of the log file
|
||||||
|
loglevel = "info" # The level at which to log
|
||||||
|
pidfile = ".gunicorn.pid" # Path to a PID file
|
||||||
|
workers = 9 # Number of workers to initialize
|
||||||
|
umask = 0 # Umask to set when daemonizing
|
||||||
|
user = None # Change process owner to user
|
||||||
|
group = None # Change process group to group
|
||||||
|
proc_name = "gunicorn-snipt" # Change the process name
|
||||||
|
tmp_upload_dir = None # Set path used to store temporary uploads
|
||||||
|
|
||||||
|
|
||||||
|
def post_fork(server, worker):
|
||||||
|
server.log.info("Worker spawned (pid: %s)" % worker.pid)
|
||||||
|
|
||||||
|
import local_settings, monitor
|
||||||
|
if local_settings.DEBUG:
|
||||||
|
server.log.info("Starting change monitor.")
|
||||||
|
monitor.start(interval=1.0)
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Copy this file to local_settings.py and change it as needed.
|
||||||
|
|
||||||
|
DEBUG = True
|
||||||
|
TEMPLATE_DEBUG = DEBUG
|
||||||
|
|
||||||
|
EMAIL_BACKEND = 'postmark.backends.PostmarkBackend'
|
||||||
|
POSTMARK_API_KEY = ''
|
||||||
|
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'sqlite3',
|
||||||
|
'NAME': '/path/to/db/',
|
||||||
|
'USER': '',
|
||||||
|
'PASSWORD': '',
|
||||||
|
'HOST': '',
|
||||||
|
'PORT': '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VIRTUALENV_PATH = '/path/to/virtualenv/'
|
|
@ -0,0 +1,11 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
from django.core.management import execute_manager
|
||||||
|
try:
|
||||||
|
import settings # Assumed to be in the same directory.
|
||||||
|
except ImportError:
|
||||||
|
import sys
|
||||||
|
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
execute_manager(settings)
|
|
@ -0,0 +1,48 @@
|
||||||
|
/* http://meyerweb.com/eric/tools/css/reset/
|
||||||
|
v2.0 | 20110126
|
||||||
|
License: none (public domain)
|
||||||
|
*/
|
||||||
|
|
||||||
|
html, body, div, span, applet, object, iframe,
|
||||||
|
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||||
|
a, abbr, acronym, address, big, cite, code,
|
||||||
|
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||||
|
small, strike, strong, sub, sup, tt, var,
|
||||||
|
b, u, i, center,
|
||||||
|
dl, dt, dd, ol, ul, li,
|
||||||
|
fieldset, form, label, legend,
|
||||||
|
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||||
|
article, aside, canvas, details, embed,
|
||||||
|
figure, figcaption, footer, header, hgroup,
|
||||||
|
menu, nav, output, ruby, section, summary,
|
||||||
|
time, mark, audio, video {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
font-size: 100%;
|
||||||
|
font: inherit;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
/* HTML5 display-role reset for older browsers */
|
||||||
|
article, aside, details, figcaption, figure,
|
||||||
|
footer, header, hgroup, menu, nav, section {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
ol, ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
blockquote, q {
|
||||||
|
quotes: none;
|
||||||
|
}
|
||||||
|
blockquote:before, blockquote:after,
|
||||||
|
q:before, q:after {
|
||||||
|
content: '';
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
// Fonts
|
||||||
|
@Helvetica: 'Helvetica Neue', Helvetica, Arial, 'Liberation Sans', FreeSans, sans-serif;
|
||||||
|
|
||||||
|
// Focus
|
||||||
|
*:focus {
|
||||||
|
.box-shadow(0, 0, 10px, #47B7F2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mixins
|
||||||
|
.border-radius(@radius: 5px) {
|
||||||
|
-webkit-background-clip: padding-box;
|
||||||
|
-webkit-border-radius: @radius;
|
||||||
|
-moz-background-clip: padding-box;
|
||||||
|
-moz-border-radius: @radius;
|
||||||
|
border-radius: @radius;
|
||||||
|
background-clip: padding-box;
|
||||||
|
}
|
||||||
|
.box-shadow(@horizontal: 0px, @vertical: 1px, @blur: 2px, @color: #CCC) {
|
||||||
|
-webkit-box-shadow: @horizontal @vertical @blur @color;
|
||||||
|
-moz-box-shadow: @horizontal @vertical @blur @color;
|
||||||
|
box-shadow: @horizontal @vertical @blur @color;
|
||||||
|
}
|
||||||
|
.inset-box-shadow(@horizontal: 0px, @vertical: 1px, @blur: 2px, @color: #CCC) {
|
||||||
|
-webkit-box-shadow: inset @horizontal @vertical @blur @color;
|
||||||
|
-moz-box-shadow: inset @horizontal @vertical @blur @color;
|
||||||
|
box-shadow: inset @horizontal @vertical @blur @color;
|
||||||
|
}
|
||||||
|
.multi-color-border(@top, @sides, @bottom) {
|
||||||
|
border-top: 1px solid @top;
|
||||||
|
border-left: 1px solid @sides;
|
||||||
|
border-right: 1px solid @sides;
|
||||||
|
border-bottom: 1px solid @bottom;
|
||||||
|
}
|
||||||
|
.multi-border-radius(@topLeft: 5px, @topRight: 5px, @bottomRight: 5px, @bottomLeft: 5px) {
|
||||||
|
-webkit-border-top-left-radius: @topLeft;
|
||||||
|
-webkit-border-top-right-radius: @topRight;
|
||||||
|
-webkit-border-bottom-right-radius: @bottomRight;
|
||||||
|
-webkit-border-bottom-left-radius: @bottomLeft;
|
||||||
|
-moz-border-radius-topleft: @topLeft;
|
||||||
|
-moz-border-radius-topright: @topRight;
|
||||||
|
-moz-border-radius-bottomright: @bottomRight;
|
||||||
|
-moz-border-radius-bottomleft: @bottomLeft;
|
||||||
|
border-top-left-radius: @topLeft;
|
||||||
|
border-top-right-radius: @topRight;
|
||||||
|
border-bottom-right-radius: @bottomRight;
|
||||||
|
border-bottom-left-radius: @bottomLeft;
|
||||||
|
}
|
||||||
|
.vertical-gradient(@start: #000, @stop: #FFF) { background: (@start + @stop) / 2;
|
||||||
|
background: -webkit-gradient(linear, left top, left bottom, from(@start), to(@stop));
|
||||||
|
background: -moz-linear-gradient(center top, @start 0%, @stop 100%);
|
||||||
|
background: -moz-gradient(center top, @start 0%, @stop 100%);
|
||||||
|
}
|
||||||
|
.vertical-gradient-with-image(@start: #000, @stop: #FFF, @image) {
|
||||||
|
background: (@start + @stop) / 2 @image;
|
||||||
|
background: @image, -webkit-gradient(linear, left top, left bottom, from(@start), to(@stop));
|
||||||
|
background: @image, -moz-linear-gradient(center top, @start 0%, @stop 100%);
|
||||||
|
background: @image, -moz-gradient(center top, @start 0%, @stop 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Page
|
||||||
|
html, body {
|
||||||
|
background-color: #FFF;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
color: #666;
|
||||||
|
font: normal 14px @Helvetica;
|
||||||
|
overflow-y: scroll;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
.group:after {
|
||||||
|
content: ".";
|
||||||
|
display: block;
|
||||||
|
height: 0;
|
||||||
|
clear: both;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
||||||
|
$(function() {});
|
|
@ -0,0 +1,113 @@
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import signal
|
||||||
|
import threading
|
||||||
|
import atexit
|
||||||
|
import Queue
|
||||||
|
|
||||||
|
_interval = 1.0
|
||||||
|
_times = {}
|
||||||
|
_files = []
|
||||||
|
|
||||||
|
_running = False
|
||||||
|
_queue = Queue.Queue()
|
||||||
|
_lock = threading.Lock()
|
||||||
|
|
||||||
|
def _restart(path):
|
||||||
|
_queue.put(True)
|
||||||
|
prefix = 'monitor (pid=%d):' % os.getpid()
|
||||||
|
print >> sys.stderr, '%s Change detected to \'%s\'.' % (prefix, path)
|
||||||
|
print >> sys.stderr, '%s Triggering process restart.' % prefix
|
||||||
|
os.kill(os.getpid(), signal.SIGINT)
|
||||||
|
|
||||||
|
def _modified(path):
|
||||||
|
try:
|
||||||
|
# If path doesn't denote a file and were previously
|
||||||
|
# tracking it, then it has been removed or the file type
|
||||||
|
# has changed so force a restart. If not previously
|
||||||
|
# tracking the file then we can ignore it as probably
|
||||||
|
# pseudo reference such as when file extracted from a
|
||||||
|
# collection of modules contained in a zip file.
|
||||||
|
|
||||||
|
if not os.path.isfile(path):
|
||||||
|
return path in _times
|
||||||
|
|
||||||
|
# Check for when file last modified.
|
||||||
|
|
||||||
|
mtime = os.stat(path).st_mtime
|
||||||
|
if path not in _times:
|
||||||
|
_times[path] = mtime
|
||||||
|
|
||||||
|
# Force restart when modification time has changed, even
|
||||||
|
# if time now older, as that could indicate older file
|
||||||
|
# has been restored.
|
||||||
|
|
||||||
|
if mtime != _times[path]:
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
# If any exception occured, likely that file has been
|
||||||
|
# been removed just before stat(), so force a restart.
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _monitor():
|
||||||
|
while 1:
|
||||||
|
# Check modification times on all files in sys.modules.
|
||||||
|
|
||||||
|
for module in sys.modules.values():
|
||||||
|
if not hasattr(module, '__file__'):
|
||||||
|
continue
|
||||||
|
path = getattr(module, '__file__')
|
||||||
|
if not path:
|
||||||
|
continue
|
||||||
|
if os.path.splitext(path)[1] in ['.pyc', '.pyo', '.pyd']:
|
||||||
|
path = path[:-1]
|
||||||
|
if _modified(path):
|
||||||
|
return _restart(path)
|
||||||
|
|
||||||
|
# Check modification times on files which have
|
||||||
|
# specifically been registered for monitoring.
|
||||||
|
|
||||||
|
for path in _files:
|
||||||
|
if _modified(path):
|
||||||
|
return _restart(path)
|
||||||
|
|
||||||
|
# Go to sleep for specified interval.
|
||||||
|
|
||||||
|
try:
|
||||||
|
return _queue.get(timeout=_interval)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
_thread = threading.Thread(target=_monitor)
|
||||||
|
_thread.setDaemon(True)
|
||||||
|
|
||||||
|
def _exiting():
|
||||||
|
try:
|
||||||
|
_queue.put(True)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
_thread.join()
|
||||||
|
|
||||||
|
atexit.register(_exiting)
|
||||||
|
|
||||||
|
def track(path):
|
||||||
|
if not path in _files:
|
||||||
|
_files.append(path)
|
||||||
|
|
||||||
|
def start(interval=1.0):
|
||||||
|
global _interval
|
||||||
|
if interval < _interval:
|
||||||
|
_interval = interval
|
||||||
|
|
||||||
|
global _running
|
||||||
|
_lock.acquire()
|
||||||
|
if not _running:
|
||||||
|
prefix = 'monitor (pid=%d):' % os.getpid()
|
||||||
|
print >> sys.stderr, '%s Starting change monitor.' % prefix
|
||||||
|
_running = True
|
||||||
|
_thread.start()
|
||||||
|
_lock.release()
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Django
|
||||||
|
-e git://github.com/django/django.git@1.3.X#egg=django
|
||||||
|
-e git://github.com/dziegler/django-css.git#egg=django-css
|
||||||
|
-e git://github.com/django-extensions/django-extensions.git#egg=django_extensions
|
||||||
|
-e hg+http://bitbucket.org/offline/django-annoying#egg=django-annoying
|
||||||
|
-e git://github.com/alex/django-templatetag-sugar.git#egg=templatetag_sugar
|
||||||
|
-e hg+http://bitbucket.org/dwaiter/django-bcrypt#egg=django-bcrypt
|
||||||
|
-e hg+ssh://hg@bitbucket.org/nicksergeant/django-registration#egg=django-registration
|
||||||
|
|
||||||
|
# Admin
|
||||||
|
-e svn+http://django-grappelli.googlecode.com/svn/trunk#egg=django-grappelli
|
||||||
|
-e hg+http://bitbucket.org/fetzig/grappelli-admin-tools#egg=admin_tools
|
||||||
|
|
||||||
|
# Deployment
|
||||||
|
-e git://github.com/bitprophet/fabric.git#egg=fabric
|
||||||
|
-e git+http://github.com/benoitc/gunicorn.git#egg=gunicorn
|
||||||
|
-e hg+ssh://hg@bitbucket.org/nicksergeant/dwfab#egg=dwfab
|
||||||
|
|
||||||
|
BeautifulSoup
|
||||||
|
django-postmark
|
||||||
|
johnny-cache
|
||||||
|
python-memcached
|
||||||
|
South
|
||||||
|
Werkzeug
|
|
@ -0,0 +1,90 @@
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
BASE_PATH = os.path.dirname(__file__)
|
||||||
|
|
||||||
|
ADMINS = (
|
||||||
|
('Nick Sergeant', 'nick@snipt.net'),
|
||||||
|
)
|
||||||
|
MANAGERS = ADMINS
|
||||||
|
|
||||||
|
INTERNAL_IPS = ('127.0.0.1',)
|
||||||
|
|
||||||
|
LOGIN_REDIRECT_URL = '/'
|
||||||
|
LOGIN_URL = '/login/'
|
||||||
|
LOGOUT_URL = '/logout/'
|
||||||
|
|
||||||
|
TIME_ZONE = 'America/New_York'
|
||||||
|
LANGUAGE_CODE = 'en-us'
|
||||||
|
SITE_ID = 1
|
||||||
|
USE_I18N = True
|
||||||
|
|
||||||
|
MEDIA_ROOT = os.path.join(BASE_PATH, 'media')
|
||||||
|
MEDIA_URL = '/media/'
|
||||||
|
ADMIN_MEDIA_PREFIX = '/media/admin/'
|
||||||
|
|
||||||
|
SECRET_KEY = 'm5w4e9^9r69f!6b9qio%)_p%a*1d(waqki+r_g11=qijh=#wuk'
|
||||||
|
|
||||||
|
SESSION_COOKIE_AGE = 31556926
|
||||||
|
|
||||||
|
TEMPLATE_CONTEXT_PROCESSORS = (
|
||||||
|
'django.core.context_processors.auth',
|
||||||
|
'django.core.context_processors.debug',
|
||||||
|
'django.core.context_processors.i18n',
|
||||||
|
'django.core.context_processors.media',
|
||||||
|
'django.core.context_processors.request',
|
||||||
|
'grappelli.context_processors.admin_template_path',
|
||||||
|
)
|
||||||
|
TEMPLATE_LOADERS = (
|
||||||
|
'django.template.loaders.filesystem.load_template_source',
|
||||||
|
'django.template.loaders.app_directories.load_template_source',
|
||||||
|
)
|
||||||
|
MIDDLEWARE_CLASSES = (
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
'django.middleware.csrf.CsrfResponseMiddleware',
|
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
'django.middleware.locale.LocaleMiddleware',
|
||||||
|
'django.contrib.redirects.middleware.RedirectFallbackMiddleware',
|
||||||
|
)
|
||||||
|
|
||||||
|
ROOT_URLCONF = 'urls'
|
||||||
|
|
||||||
|
TEMPLATE_DIRS = os.path.join(BASE_PATH, 'templates')
|
||||||
|
|
||||||
|
INSTALLED_APPS = (
|
||||||
|
'grappelli',
|
||||||
|
'admin_tools.theming',
|
||||||
|
'admin_tools.menu',
|
||||||
|
'django.contrib.admin',
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.humanize',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.sites',
|
||||||
|
'django.contrib.redirects',
|
||||||
|
|
||||||
|
'compressor',
|
||||||
|
'django_bcrypt',
|
||||||
|
'south',
|
||||||
|
)
|
||||||
|
|
||||||
|
# CSS compression
|
||||||
|
|
||||||
|
COMPRESS_OUTPUT_DIR = "cache"
|
||||||
|
COMPILER_FORMATS = {
|
||||||
|
'.less': {
|
||||||
|
'binary_path':'lessc',
|
||||||
|
'arguments': '*.less *.css'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Grappelli
|
||||||
|
|
||||||
|
GRAPPELLI_ADMIN_TITLE = '<a href="/">Snipt</a>'
|
||||||
|
|
||||||
|
# Local settings and debug
|
||||||
|
|
||||||
|
from local_settings import *
|
||||||
|
|
||||||
|
if DEBUG:
|
||||||
|
INSTALLED_APPS += ('django_extensions',)
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
ln -s "$VIRTUAL_ENV/src/django-grappelli/grappelli/media" "media/admin"
|
|
@ -0,0 +1,7 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block page-title %}Page Not Found - {{ block.super }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
404: Page Not Found
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block page-title %}Server Error - {{ block.super }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
500: Server Error
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" class="{% block html-class %}{% endblock %}">
|
||||||
|
<head>
|
||||||
|
<title>{% block page-title %}Snipt{% endblock %}</title>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="description" content="Long-term memory for coders. Share and store code snippets." />
|
||||||
|
|
||||||
|
{% load compress %}
|
||||||
|
|
||||||
|
{% compress css %}
|
||||||
|
<link rel="stylesheet" href="/media/css/reset.css" />
|
||||||
|
{% if not debug %}
|
||||||
|
<link rel="stylesheet" href="/media/css/style.less" />
|
||||||
|
{% endif %}
|
||||||
|
{% endcompress %}
|
||||||
|
{% if debug %}
|
||||||
|
<link rel="stylesheet/less" href="/media/css/style.less" />
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% compress js %}
|
||||||
|
<script type="text/javascript" src="/media/js/jquery.js"></script>
|
||||||
|
<script type="text/javascript" src="/media/js/script.js"></script>
|
||||||
|
{% if debug %}
|
||||||
|
<script type="text/javascript" src="/media/js/less.js"></script>
|
||||||
|
{% endif %}
|
||||||
|
{% endcompress %}
|
||||||
|
|
||||||
|
<!--[if IE]>
|
||||||
|
<style type="text/css">
|
||||||
|
.group {
|
||||||
|
display: block;
|
||||||
|
zoom: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<![endif]-->
|
||||||
|
|
||||||
|
<!--[if lt IE 9]>
|
||||||
|
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||||
|
<![endif]-->
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body class="{% block body-class %}{% endblock %}">
|
||||||
|
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
|
||||||
|
{% if not debug %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
//<![CDATA[
|
||||||
|
var _gaq = _gaq || [];
|
||||||
|
_gaq.push(['_setAccount', 'UA-514462-44']);
|
||||||
|
_gaq.push(['_trackPageview']);
|
||||||
|
(function() {
|
||||||
|
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||||
|
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||||
|
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ga);
|
||||||
|
})();
|
||||||
|
//]]>
|
||||||
|
</script>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,5 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
Home
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,19 @@
|
||||||
|
from django.views.generic.simple import direct_to_template
|
||||||
|
from django.conf.urls.defaults import *
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
admin.autodiscover()
|
||||||
|
|
||||||
|
from views import home
|
||||||
|
|
||||||
|
|
||||||
|
urlpatterns = patterns('',
|
||||||
|
url(r'^admin/', include(admin.site.urls)),
|
||||||
|
url(r'^admin_tools/', include('admin_tools.urls')),
|
||||||
|
url(r'^grappelli/', include('grappelli.urls')),
|
||||||
|
|
||||||
|
url(r'^404/$', direct_to_template, {'template': '404.html'}),
|
||||||
|
url(r'^500/$', direct_to_template, {'template': '500.html'}),
|
||||||
|
|
||||||
|
url(r'^$', home),
|
||||||
|
)
|
Loading…
Reference in New Issue