Commits

Zachary Voase  committed 2e172a1

Initial import, at long last.

  • Participants

Comments (0)

Files changed (33)

+syntax: glob
+
+*.pyc
+.DS_Store
+Copyright (c) 2009 Zachary Voase
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+<!--*-markdown-*-->
+
+# Django-in-a-Box™
+## *Fast and simple Django site scaffolding.*
+
+Django-in-a-Box is a simple way to set up a Django project with Jinja2,
+Supervisor, lighttpd and FastCGI. To get started, consult the Markdown
+documentation in the `docs/` directory in this repo.

File docs/concepts.md

+# Concepts
+
+Django-in-a-Box breaks the typical Django project into a several core concepts:
+
+Project
+:   A collection of resources consisting of the three main reusable parts of a site — *code*, *settings* and *media*. These resources are read-only from the perspective of the running application.
+
+Site
+:   An installable, runnable instance of a Django project. This consists of the project, plus all the 'writable' components: log files, filesystem-based caches, uploads, UNIX socket files, installed third-party packages and binaries, and flat-file SQLite databases.
+
+Deployment
+:   A running, installed instance of a site.
+
+Project Directory
+:   This is the typical directory generated by `django-admin startproject project_name`. It houses the code, settings and media that make up the project. Technically speaking, it is also a Python module.
+  
+Site Directory
+:   The directory containing the project plus the writable resources which make up the site.
+  
+Application
+:   In this context, an application, or ‘app’, refers to a Django app; a single module with any combination of models, views, URLs, templates and business logic.
+
+Library
+:   A library, or ‘lib’ for short, is a Python module which is site-specific but does not belong in only one app, or has functionality that is needed by several apps without clearly belonging in only one.
+
+Web Server
+:   The process which handles HTTP connections from the client, passing them on to your Django instance for further processing. Django-in-a-Box uses [lighttpd](http://www.lighttpd.net/) by default, and includes a full configuration template for it.
+
+There are also a couple of technologies which DiaB uses that you may not be familiar with:
+
+FastCGI
+:   A model which allows you to run your Django application separately from the server which handles your HTTP requests. Rather than embedding the Python interpreter in the web server, a separate Python worker process (or pool of such processes) runs as a daemon. The web server communicates with this process via the FastCGI protocol; as a result, your applications run faster and more securely. Consult the [FastCGI website](http://www.fastcgi.com) for more information.
+
+Supervisor
+:   [Supervisor](http://supervisord.org) is a tool, written in Python, that allows you to start, stop and manage multiple daemonised processes using a simple configuration file and an easy command-line (and XML-RPC) interface (`supervisorctl`).

File docs/etc-directory.md

+# The `etc/` Directory
+
+This is a deep enough topic to warrant its own page of documentation. The `etc/` directory within a project holds the plaintext configuration files for that project. Services like Supervisor and lighttpd use these config files to obtain their settings within a given deployment, and as such they should be contained within the project directory (since they are read-only and should be tracked by the VCS).
+
+However, the content of these config files will also vary based on deployment. For this reason, Django-in-a-Box uses a further two directories to smooth out the commonalities between config files, and to help keep deployment configurations separate but still within the project directory.
+
+The `etcs/` directory contains multiple folders, each one corresponding to a given deployment. Within a deployment, `project_dir/etc` is *symlinked* (symbolically linked) to `project_dir/etcs/DEPLOYMENT_NAME`. This means that, behind the scenes, the operating system will re-route `etc/supervisord.conf` to `etcs/DEPLOYMENT_NAME/supervisord.conf`. This requires you to tell your VCS to ignore the `etc/` directory (since this is only a symlink which you do not want to track).
+
+To create these configuration files in various deployments, the `generate_config` task has been defined in `project_dir/libs/templatecfg.py`. This takes a list of configured templates from the pavement options, and renders them out to a given `etcs/DEPLOYMENT` directory which you can then symlink `etc/` to. To do this, just run:
+
+    paver generate_config -d DEPLOYMENT_NAME
+
+You can also specify a `--force` option which will tell `generate_config` to overwrite any existing configuration files which exist at that location.

File docs/index.md

+# Django-in-a-Box documentation
+
+You should begin by reading the [Concepts](/concepts) page.
+
+Once you’re familiar with the concepts which Django-in-a-Box (DiaB) is based upon, you should move onto the [directory layout](/layout/) documentation so you know where everything goes and where to look for certain things. The [Tools](/tools/) section will give you information on the tools which DiaB comes with by default, and the [`etc/` directory documentation](/etc-directory) provides a reference for the plaintext configuration machinery in DiaB. Finally, you can read the [Quickstart](/quickstart) document to get started straight away.
+
+For helpful information on site administration, consult the README which comes with every DiaB site.
+
+## Platforms
+
+Django-in-a-Box currently only runs on UNIX-based systems. That means Mac OS X, *BSD, GNU/Linux and a few others. Unfortunately, I have neither the time, the resources nor the inclination to work around the issues which Windows presents; if you want to fork the project and add support yourself, go ahead.
+
+## Version Control
+
+Django-in-a-Box was built to work well with Version Control Systems. There are just a couple of things to remember:
+
+* The project directory should be under version control, but *not* the site directory. The contents of the site directory will vary constantly from deployment to deployment, but the project directory should remain constant.
+
+* You'll want to tell your VCS to ignore certain types of file: compiled `.pyc` files, `.DS_Store` files, and the `etc/` symlink in the project directory. On Mercurial, this is done with a `.hgignore` file; Git uses a `.gitignore`, and Subversion uses `svn:ignore`.

File docs/layout/index.md

+# Django-in-a-Box Documentation
+
+Django-in-a-Box’s main piece of scaffolding is its directory layout, which places code, templates, media and settings in logical places. At its heart lies the split between a [site](/concepts#site) and a [project](/concepts#project). See the documentation on [site directories](site-directories) and [project directories](project-directories) to find out how that applies to a practical [deployment](/concepts#deployment).

File docs/layout/project-directories.md

+# Project Directories
+
+Django-in-a-Box’s default project layout looks like this:
+
+    project_dir/
+    |-- apps/
+    |-- etc/ -> etcs/deployment
+    |-- etc.templates/
+    |   |-- common.py
+    |   |-- fastcgi.light.conf
+    |   |-- fastcgi.svd.conf
+    |   |-- lighttpd.conf
+    |   |-- lighttpd.svd.conf
+    |   |-- mimetypes.light.conf
+    |   `-- supervisord.conf
+    |-- etcs/
+    |   `-- deployment/
+    |       |-- fastcgi.light.conf
+    |       |-- fastcgi.svd.conf
+    |       |-- lighttpd.conf
+    |       |-- lighttpd.svd.conf
+    |       |-- mimetypes.light.conf
+    |       `-- supervisord.conf
+    |-- libs/
+    |   |-- djpave.py
+    |   `-- templatecfg.py
+    |-- media/
+    |   |-- css/
+    |   |-- img/
+    |   |-- js/
+    |   `-- favicon.ico
+    |-- settings/
+    |   |-- common.py
+    |   |-- development.py
+    |   |-- production.py
+    |   `-- staging.py
+    |-- static/
+    |   `-- error/
+    |       |-- 404.html
+    |       |-- 500.html
+    |       `-- 503.html
+    |-- templates/
+    |-- README
+    |-- __init__.py
+    |-- pavement.py
+    `-- urls.py
+
+
+The project directory is for all the content which remains invariant (read-only) while your site is running. It is the top-level of the version control repository, as it contains everything needed to install and run a copy of the site: **media**, **code**, **templates** and **settings**. It also contains documentation and requirement specifications, since these also need to be tracked by the VCS.
+
+`apps/`
+:   Contains modules representing all the site-specific Django apps for this project. There is no need for an `__init__.py` file because it is added to the Python module search path automatically. This allows apps to be imported directly by their name, instead of `project.apps.app_name`.
+
+`libs/`
+:   Contains modules which are *not* Django apps but still need to be imported in your code. Again, this is automatically added to the search path. This already houses a couple of libraries which are used by the pavement file.
+
+`etc/`, `etc.templates/` and `etcs/`
+:   Contains all of the project's plaintext configuration files; out of the box this is just Supervisor and lighttpd, but you can add to this. `etc.templates/` holds Jinja2 templates for the various configuration files, `etcs/` holds multiple directories (each corresponding to a different deployment) with the rendered plaintext configurations, and `etc/` is a symlink to a single `etcs/deployment` directory.
+
+`media/`
+:   Holds all site media: JavaScript, CSS and images. These do not change based on the installation, and need to be tracked by the VCS, so are kept in the project directory.
+
+`settings/`
+:   Holds all of the various deployment-specific settings files, along with the `common.py` settings module.
+
+`static/`
+:   A directory for the various non-media static files required by your project. For example, static error pages for your web server (as opposed 404 templates for your app), and other pieces of raw data which your app might need that won't be served directly out of the `media/` directory.
+
+`templates/`
+:   Should contain all of your site-wide Django/Jinja2 templates. You should also have `templates/` subdirectories in some of your applications.
+
+`pavement.py`
+:   A file containing definitions for Paver. Paver is a tool for managing software projects, much like Fabric, Make, Rake or Capistrano. This replaces the traditional `manage.py` within Django-in-a-Box; instead of running `python manage.py command`, use `paver manage command`.

File docs/layout/site-directories.md

+# Site Directories
+
+A standard Django-in-a-Box site directory looks like this:
+
+    site_dir/
+    |-- bin/
+    |-- cache/
+    |-- db/
+    |-- include/
+    |-- lib/
+    |-- log/
+    |-- pid/
+    |-- project_dir/
+    |-- sock/
+    `-- uploads/
+
+The site directory will contain everything needed for a running instance of your site. The immediate subdirectories in the site directory, with the exception of the project directory (`project_dir/`), are specific to that one deployment of the site; things like UNIX socket files, filesystem-based caches, SQLite database and virtualenv machinery go in these immediate subdirectories. For more information on the role of the *site* as it relates the the *project*, consult the [Concepts](/concepts) documentation.
+
+`bin/`, `include/` and `lib/`
+:   These directories contain the virtualenv machinery; virtualenv allows a site to contain sandboxed installations of Python packages.
+
+`cache/`
+:   Used as a filesystem-based cache for whatever components need it; it can also be used to save temporary files. At the moment it is used by lighttpd’s `mod_compress` to cache gzipped media files.
+
+`db/`
+:   Can be used in a development environment to store SQLite databases. These are usually stored as `db/development.sqlite`. SQLite is sometimes used for non-production deployments because it is a lot more easy to set up than MySQL or PostgreSQL. It is, however, unsuitable for deployments with high concurrency or load.
+
+`log/`
+:   Used to store log files from Python, Supervisor, lighttpd and any other site-specific program which may generate logging data.
+
+`pid/`
+:   Houses the PID files for running services; by default, this is just Supervisor (`pid/supervisord.pid`). You can forcibly kill a given service by running `kill -9 $(cat pid/SERVICE_NAME.pid)`.
+
+`sock/`
+:   Contains UNIX sockets; these are used instead of the typical `host:port` model for interprocess communication via TCP. In the scaffolding, the FastCGI daemon config uses a UNIX socket so that Supervisor can spread the load amongst several worker processes.
+
+`uploads/`
+:   Stores temporary and permanent file uploads from the web server. It can be used by the Django site to save user uploads, but it is also used by lighttpd to store uploads in chunks when a large amount of POST data is being sent from a client. Usually, this chunking occurs for uploads of 1MB or greater.

File docs/quickstart.md

+# Quickstart
+
+Begin by installing Paver on your machine:
+
+    easy_install Paver
+
+Then, create a directory to hold your site:
+
+    mkdir MyProj
+    cd MyProj/
+
+Copy the Django-in-a-Box skeleton project to a sub-folder within this directory:
+
+    cp -R $DIAB_PROJECT/ ./myproj/
+    cd myproj/
+
+Create and run the bootstrap script:
+
+    paver boostrap
+    python start.py
+
+This will download all the required packages from PyPI, initialize a virtualenv and make the necessary directories throughout your project and site. Take a look through the `options` section of the `pavement.py` file in the project and make sure all the settings are correct, then run:
+
+    paver generate_config -d development
+
+To generate all the configuration files for a deployment called `development`. Now edit `etcs/development/lighttpd.conf`, and uncomment the lines with `server.bind` and `server.port` so that the server runs on localhost, port 8001 (otherwise you would need to run lighttpd as root).
+
+If you want to use the Django admin, edit the `settings/common.py` file and uncomment the line under `INSTALLED_APPS`. You can also play around with the settings in `settings/common.py` and `settings/development.py`; the `common` settings are those which will remain constant across *all* deployments of the site; `development.py` is for *development* deployments only, and as such contains settings for the database, cache, etc.
+
+When you're happy with your configuration files, run:
+
+    ln -s `pwd`/etcs/development `pwd`/etc
+
+to make a symlink in the project directory from `etc` to the `etcs/development` directory. Run:
+
+    . ../bin/activate
+    export DJANGO_SETTINGS_MODULE=myproj.settings.development
+
+to activate the virtualenv, and let Django know that you're using the `development` settings. Then run:
+
+    paver manage syncdb
+
+to create your SQLite database (which will be in `db/development.sqlite` under your site directory). You can now run:
+
+    paver manage runserver
+
+to run a development web server on port 8000, or run:
+
+    supervisord
+
+to start Supervisor, which will run an instance of lighttpd with five FastCGI worker processes on port 8001. To stop Supervisor, run:
+
+    supervisorctl
+
+You can also restart lighttpd with `supervisorctl restart lighttpd`, and you can restart the FastCGI processes with `supervisorctl restart django:*`. For more information, consult `supervisorctl --help`.

File docs/tools/index.md

+# Tools
+
+Django-in-a-Box comes with several tools which make the process of deploying and running a Django site a lot easier. See the other files in this directory for more information and instructions on how best to use them.

File docs/tools/jinja2.md

+# Jinja2
+
+[Jinja2][Jinja2] is a fast and simple templating system for Python. It has a syntax based on Django’s built-in templating system, but gives you a bit more expressive power and flexibility. With this power, however, comes with responsibility; you should always try to constrain your heavy business logic to your view functions.
+
+Nevertheless, if you use it right, it can be a powerful tool and can save you a lot of time and effort working around the constraints of the Django’s built-in templating system. Anyone who has ever had to type `{% endifnotequal %}` knows what that means.
+
+## DjanJinja
+
+In order to integrate more closely with Jinja2, Django-in-a-Box includes [DjanJinja][DjanJinja], a Django application which provides several shortcuts and wrappers to make using Jinja2 feel more native within Django. To get started straight away, write your templates using Jinja2, putting them in the same location as usual. To render them from within your views:
+
+    from djanjinja.views import render_to_response
+    
+    def myview(request):
+        return render_to_response('template_name.txt', {'key': 'value'})
+
+For more information, consult the DjanJinja documentation at its [project page][DjanJinja].
+
+[Jinja2]: http://jinja.pocoo.org/2/documentation
+[DjanJinja]: http://code.zacharyvoase.com/djanjinja/

File docs/tools/lighttpd.md

+# lighttpd
+
+[lighttpd][lighttpd] (pronounced ‘lighty’) is a small, lightweight, fully-featured HTTP web server. From its web page:
+
+> Security, speed, compliance, and flexibility — all of these describe lighttpd (pron. lighty) which is rapidly redefining efficiency of a webserver; as it is designed and optimized for high performance environments. With a small memory footprint compared to other web-servers, effective management of the cpu-load, and advanced feature set (FastCGI, SCGI, Auth, Output-Compression, URL-Rewriting and many more) lighttpd is the perfect solution for every server that is suffering load problems.
+
+lighttpd comes with FastCGI baked in, so you won’t need to install any additional modules. It also uses a very simple configuration file syntax, that you can understand and edit without having to take a specialized course.
+
+[lighttpd]: http://www.lighttpd.net/

File docs/tools/supervisor.md

+# Supervisor
+
+[Supervisor][Supervisor] is a tool for starting, stopping and managing processes. It is used in Django-in-a-Box to manage the lighttpd and FastCGI processes. You can also run daemons like task queues and various workers.
+
+By default, Supervisor is configured with a program called `memmon` which will kill any runaway processes (by default, those with a memory consumption greater than 300MB). For more information on configuring the `memmon` command, consult the documentation at the [Superlance PyPI page][Superlance].
+
+The [Supervisor Manual][svd-manual] provides far better documentation than I can hope to provide here. I can, however, offer some good recipes for Django-in-a-Box:
+
+    # Start Supervisor
+    $ supervisord
+    
+    # Check Supervisor’s status
+    $ supervisorctl status
+    
+    # Restart lighttpd
+    $ supervisorctl restart lighttpd
+    
+    # Restart all FastCGI workers. If you have customised the FastCGI 
+    # `program_name` setting in `pavement.py`, replace `django` with the custom 
+    # value.
+    $ supervisorctl restart django:*
+    
+    # Shut down Supervisor
+    $ supervisorctl shutdown
+
+If you ever run `supervisorctl` and receive an error message ending with something like:
+    
+    ...
+    socket.error: [Errno 61] Connection refused
+
+That's usually a signal that Supervisor is not running. Check the `site_dir/log/supervisord.log` file for more information if this persists after running `supervisord` again.
+
+[Supervisor]: http://supervisord.org/
+[Superlance]: http://pypi.python.org/pypi/superlance
+[svd-manual]: http://supervisord.org/manual/current/index.html

File project/README

+Virtualenv
+==========
+
+Activate:
+    
+    . site_dir/bin/activate
+
+Deactivate:
+
+    deactivate
+
+Django Management Commands
+==========================
+
+**N.B.:** Don't forget to set `DJANGO_SETTINGS_MODULE`!
+
+From the project directory:
+
+    paver manage syncdb
+    paver manage runserver
+    paver manage dbshell
+    ...
+
+Supervisor
+==========
+
+Starting (from project directory):
+
+    supervisord
+
+Managing:
+    
+    supervisorctl status
+    supervisorctl restart lighttpd
+    supervisorctl restart django:*
+
+Stopping:
+    
+    supervisorctl stop django:*
+    supervisorctl stop lighttpd
+
+Shut down (stops all processes automatically):
+
+    supervisorctl shutdown

File project/__init__.py

Empty file added.

File project/etc.templates/common.py

+# -*- coding: utf-8 -*-
+
+import django
+from paver.path import path
+
+
+## Project
+
+PROJECT_DIR = path(__file__).abspath().dirname().parent
+PROJECT_NAME = PROJECT_DIR.basename()
+SITE_DIR = PROJECT_DIR.parent
+
+ADMINS = (
+    ('Your Name', 'username@example.com'),
+)
+
+MANAGERS = ADMINS
+
+TIME_ZONE = 'Europe/London'
+LANGUAGE_CODE = 'en-gb'
+
+SITE_ID = 1
+USE_I18N = True
+
+SECRET_KEY = '{{ generate_secret_key() }}'
+
+## Media
+
+MEDIA_DIR = PROJECT_DIR / 'media' / ''
+MEDIA_URL = '/media/'
+ADMIN_MEDIA_PREFIX = '/media/admin/'
+ADMIN_MEDIA_DIR = path(django.__file__).abspath().dirname() / 'contrib' / 'admin' / 'media'
+
+## Templates
+
+TEMPLATE_LOADERS = (
+    'django.template.loaders.filesystem.load_template_source',
+    'django.template.loaders.app_directories.load_template_source',
+)
+
+TEMPLATE_CONTEXT_PROCESSORS = (
+    'django.core.context_processors.auth',
+    'django.core.context_processors.debug',
+    'django.core.context_processors.i18n',
+    'django.core.context_processors.media'
+)
+
+TEMPLATE_DIRS = (
+    PROJECT_DIR / 'templates',
+)
+
+MIDDLEWARE_CLASSES = (
+    'django.middleware.common.CommonMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'djanjinja.middleware.RequestContextMiddleware',
+)
+
+## URLs
+
+ROOT_URLCONF = PROJECT_NAME + '.urls'
+FORCE_SCRIPT_NAME = '' # Fixes a common issue with lighttpd + FastCGI.
+
+## Installed applications
+
+
+INSTALLED_APPS = (
+    ## Django contrib apps
+    # 'django.contrib.admin',
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    
+    ## Third-party apps
+    'djanjinja',
+    
+    ## Local apps
+    # Nothing here yet.
+)

File project/etc.templates/fastcgi.light.conf

+fastcgi.server = (
+    "/{{ options.fastcgi.program_name }}.fcgi" => (
+        "main" => (
+            "socket" => "{{ options.fastcgi.socket }}",
+            "allow-x-send-file" => "enable",
+            "check-local" => "disable",
+        )
+    )
+)
+
+
+alias.url = (
+    "/media/admin" => "{{ get_admin_media_dir() / '' }}",
+)
+
+
+url.rewrite-once = (
+    "^(/media/admin/.*)$" => "$1",
+    "^(/media/)$" => "$1",
+    "^/media/(.+)$" => "$1",
+    "^(/favicon\.ico)$" => "$1",
+    "^(/.*)$" => "/{{ options.fastcgi.program_name }}.fcgi$1",
+)

File project/etc.templates/fastcgi.svd.conf

+[fcgi-program:{{ options.fastcgi.program_name }}]
+# Command
+command                 = paver manage runfcgi method=threaded daemonize=false socket="{{ options.fastcgi.socket }}"
+socket                  = unix://{{ options.fastcgi.socket }}
+directory               = {{ options.project_dir }}
+# Process
+process_name            = %(program_name)s_%(process_num)02d
+numprocs                = {{ options.fastcgi.num_workers }}
+{% if 'user' in options.fastcgi -%}
+user                    = {{ options.fastcgi.user }}
+{% endif -%}
+{% if 'group' in options.fastcgi -%}
+group                   = {{ options.fastcgi.group }}
+{% endif -%}
+priority                = 50
+# (Re)starting and stopping
+startsecs               = 3
+stopsignal              = QUIT
+# Logging
+redirect_stderr         = true
+stdout_logfile          = {{ options.site_dir / 'log' / 'django' / '%(program_name)s_%(process_num)02d.log' }}
+stdout_logfile_maxbytes = 4MB
+stdout_logfile_backups  = 3
+stderr_logfile          = NONE

File project/etc.templates/lighttpd.conf

+### Server core configuration
+
+server.modules = (
+    "mod_rewrite", # Used for routing requests to FastCGI.
+    "mod_alias", # Used for routing around FastCGI for media files.
+    "mod_access", # Deny access to any directories above STATIC_DIR.
+    "mod_fastcgi",
+    "mod_compress", # Compress certain static files (JS, CSS, images, et cetera)
+    "mod_accesslog", # Produce apache-style access logs.
+)
+
+## Information
+server.name                    = "{{ options.hostname }}"
+server.tag                     = "lighttpd"
+# server.bind                    = "127.0.0.1"
+# server.port                    = 8001
+
+## Logging
+accesslog.filename             = "{{ options.site_dir / 'log' / 'lighttpd' / 'access.log' }}"
+server.errorlog                = "{{ options.site_dir / 'log' / 'lighttpd' / 'error.log' }}"
+
+## File handling
+server.document-root           = "{{ options.project_dir / 'media' }}"
+server.upload-dirs             = ( "{{ options.site_dir / 'uploads' }}" )
+index-file.names               = ( "index.html", "default.html", "index.htm" )
+server.errorfile-prefix        = "{{ options.project_dir / 'static' / 'error' / '' }}" 
+
+## Security (always before performance)
+{% if options.get('lighttpd', {}).get('user') -%}
+server.username                = "{{ options.lighttpd.user }}"
+{% endif -%}
+{% if options.get('lighttpd', {}).get('group') -%}
+server.groupname               = "{{ options.lighttpd.group }}"
+{% endif -%}
+server.dir-listing             = "disable"
+server.follow-symlink          = "disable"
+server.core-files              = "disable"
+
+## Performance
+# See http://redmine.lighttpd.net/projects/1/wiki/Server.event-handlerDetails
+server.event-handler           = "select"
+# See http://redmine.lighttpd.net/projects/1/wiki/Server.network-backendDetails
+server.network-backend         = "writev"
+server.stat-cache-engine       = "simple"
+server.max-read-idle           = 10
+server.max-write-idle          = 30
+server.max-keep-alive-requests = 6
+server.max-request-size        = 4096 # KB => 4 MB.
+# Compression
+compress.allowed-encodings     = ( "gzip", "deflate" )
+compress.cache-dir             = "{{ options.site_dir / 'cache' / 'compress' }}"
+compress.filetype               = ( "text/css", "text/javascript", "text/plain" )
+
+
+### Additional configuration
+include_shell "cat {{ (options.project_dir / 'etc' / '*.light.conf') }}"

File project/etc.templates/lighttpd.svd.conf

+[program:lighttpd]
+# Command
+command                 = lighttpd -D -f "{{ options.project_dir / 'etc' / 'lighttpd.conf' }}"
+directory               = {{ options.project_dir }}
+# Process
+process_name            = lighttpd
+priority                = 10
+# (Re)starting and stopping
+startsecs               = 1
+stopsignal              = TERM
+# Logging
+redirect_stderr         = true
+stdout_logfile          = {{ options.site_dir / 'log' / 'lighttpd' / 'process.log' }}
+stdout_logfile_maxbytes = 4MB
+stdout_logfile_backups  = 3
+stderr_logfile          = NONE

File project/etc.templates/mimetypes.light.conf

+mimetype.assign = (
+  ".pdf"          =>      "application/pdf",
+  ".sig"          =>      "application/pgp-signature",
+  ".spl"          =>      "application/futuresplash",
+  ".class"        =>      "application/octet-stream",
+  ".ps"           =>      "application/postscript",
+  ".torrent"      =>      "application/x-bittorrent",
+  ".dvi"          =>      "application/x-dvi",
+  ".gz"           =>      "application/x-gzip",
+  ".pac"          =>      "application/x-ns-proxy-autoconfig",
+  ".swf"          =>      "application/x-shockwave-flash",
+  ".tar.gz"       =>      "application/x-tgz",
+  ".tgz"          =>      "application/x-tgz",
+  ".tar"          =>      "application/x-tar",
+  ".zip"          =>      "application/zip",
+  ".mp3"          =>      "audio/mpeg",
+  ".m3u"          =>      "audio/x-mpegurl",
+  ".wma"          =>      "audio/x-ms-wma",
+  ".wax"          =>      "audio/x-ms-wax",
+  ".ogg"          =>      "application/ogg",
+  ".wav"          =>      "audio/x-wav",
+  ".gif"          =>      "image/gif",
+  ".jar"          =>      "application/x-java-archive",
+  ".jpg"          =>      "image/jpeg",
+  ".jpeg"         =>      "image/jpeg",
+  ".png"          =>      "image/png",
+  ".xbm"          =>      "image/x-xbitmap",
+  ".xpm"          =>      "image/x-xpixmap",
+  ".xwd"          =>      "image/x-xwindowdump",
+  ".css"          =>      "text/css",
+  ".html"         =>      "text/html",
+  ".htm"          =>      "text/html",
+  ".js"           =>      "text/javascript",
+  ".asc"          =>      "text/plain",
+  ".c"            =>      "text/plain",
+  ".cpp"          =>      "text/plain",
+  ".log"          =>      "text/plain",
+  ".conf"         =>      "text/plain",
+  ".text"         =>      "text/plain",
+  ".txt"          =>      "text/plain",
+  ".dtd"          =>      "text/xml",
+  ".xml"          =>      "text/xml",
+  ".mpeg"         =>      "video/mpeg",
+  ".mpg"          =>      "video/mpeg",
+  ".mov"          =>      "video/quicktime",
+  ".qt"           =>      "video/quicktime",
+  ".avi"          =>      "video/x-msvideo",
+  ".asf"          =>      "video/x-ms-asf",
+  ".asx"          =>      "video/x-ms-asf",
+  ".wmv"          =>      "video/x-ms-wmv",
+  ".bz2"          =>      "application/x-bzip",
+  ".tbz"          =>      "application/x-bzip-compressed-tar",
+  ".tar.bz2"      =>      "application/x-bzip-compressed-tar",
+  # default mime type
+  ""              =>      "application/octet-stream",
+ )

File project/etc.templates/supervisord.conf

+[supervisord]
+# Process
+{% if 'user' in options.supervisor -%}
+user                    = {{ options.supervisor.user }}
+{% endif -%}
+{% if 'group' in options.supervisor -%}
+group                   = {{ options.supervisor.group }}
+{% endif -%}
+umask                   = 002
+directory               = {{ options.site_dir }}
+pidfile                 = {{ options.site_dir / 'pid' / 'supervisord.pid' }}
+# Logging
+logfile                 = {{ options.site_dir / 'log' / 'supervisord.log' }}
+logfile_maxbytes        = 4MB
+logfile_backups         = 3
+childlogdir             = {{ options.site_dir / 'log' }}
+
+[inet_http_server]
+port = 127.0.0.1:{{ options.supervisor.get('port', 9001) }}
+
+[supervisorctl]
+serverurl = http://127.0.0.1:{{ options.supervisor.get('port', 9001) }}/
+
+# This section is necessary for supervisorctl to work.
+[rpcinterface:supervisor]
+supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
+
+{% if options.supervisor.get('use_memmon', False) -%}
+[eventlistener:memmon]
+# Command
+command                 = memmon {{ cmd_join(options.supervisor.memmon_args) }}
+process_name            = memmon
+# Events
+events                  = TICK_60
+buffer_size             = 10
+# Process
+priority                = 1
+exitcodes               = 0
+# Logging
+redirect_stderr         = true
+stdout_logfile          = {{ options.site_dir / 'log' / '%(program_name)s.log' }}
+stdout_logfile_maxbytes = 4MB
+stdout_logfile_backups  = 3
+stderr_logfile          = NONE
+{% endif -%}
+
+[include]
+files = {{ options.project_dir / 'etc' / '*.svd.conf' }}

File project/libs/djpave.py

+# -*- coding: utf-8 -*-
+# djpave.py - Run Django management commands with a Paver task.
+# 
+# It’s simple really; just run `paver manage syncdb`, or
+# `paver manage runserver`, or indeed any Django management command. You can
+# also define Paver tasks which load the Django settings before execution.
+
+from functools import wraps
+import imp
+import os
+import random
+import sys
+
+from django.core import management as mgmt
+from django.utils import importlib
+
+from paver.easy import *
+
+# Control what gets imported in `from django_pave import *`.
+__all__ = ['manage', 'management', 'get_admin_media_dir', 'generate_secret_key']
+
+
+# The following hackery is necessary to make sure the entire `paver manage`
+# command is shown as the program's name (instead of just `paver`).
+OldOptionParser = mgmt.LaxOptionParser
+
+class LaxOptionParser(mgmt.LaxOptionParser):
+    
+    def __init__(self, *args, **kwargs):
+        kwargs['prog'] = 'paver manage'
+        OldOptionParser.__init__(self, *args, **kwargs)
+
+mgmt.LaxOptionParser = LaxOptionParser
+
+
+# And this hackery stops paver from printing the customary '---> task_name'
+# header at the beginning of all task output. This might seem purely cosmetic,
+# but for Django tasks which rely on streaming output (such as `dumpdata`), it
+# is actually necessary for the task to function properly.
+class QuietTask(tasks.Task):
+    
+    """A task which doesn’t print '---> task_name'."""
+    
+    def __call__(self, *args, **kwargs):
+        # Switch on `tasks.environment.quiet`, which prevents the header from
+        # being printed.
+        old_quiet = tasks.environment.quiet
+        tasks.environment.quiet = True
+        
+        return_value = super(QuietTask, self).__call__(*args, **kwargs)
+        
+        # Restore the old value of `tasks.environment.quiet`.
+        tasks.environment.quiet = old_quiet
+        return return_value
+
+
+def generate_secret_key():
+    """Return a generated secret key for use in settings files."""
+    
+    chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)'
+    return ''.join(random.choice(chars) for i in range(50))
+
+
+def import_module(name, path=None, parent_mod_name=''):
+    """
+    Import a given module name, looking on optional path.
+    
+    This is essentially a wrapper over the functions included in the ``imp``
+    module which allows for a slightly more dynamic way of importing modules:
+    
+        import_module('project.settings', path='...') =>
+            <module 'project.settings' ...>
+    """
+    
+    if parent_mod_name:
+        original_name = parent_mod_name + '.' + name
+    else:
+        original_name = name
+    
+    if original_name in sys.modules:
+        return sys.modules[original_name]
+    
+    path = path or sys.path
+    
+    while '.' in name:
+        supername, name = name.split('.', 1)
+        module = import_module(supername, path=path, parent_mod_name=parent_mod_name)
+        path = module.__path__
+        parent_mod_name = '.'.join((parent_mod_name, supername)).strip('.')
+    
+    modfile, pathname, description = imp.find_module(name, path)
+    try:
+        module = imp.load_module(original_name, modfile, pathname, description)
+    finally:
+        if modfile:
+            modfile.close()
+    
+    return module
+
+
+def setup_settings():
+    """Set up the Django settings module in the Paver environment."""
+    
+    # Return straight away if the Django settings have already been loaded.
+    if getattr(environment.options, 'django', None):
+        return
+    
+    pavement_file = getattr(tasks.environment.pavement, '__file__', None)
+    search_path = sys.path
+    if pavement_file:
+        # Add directory containing the pavement file to the system path.
+        pavement_dir = unicode(path(pavement_file).abspath().dirname())
+        if pavement_dir not in search_path:
+            search_path += [pavement_dir]
+    
+    # If a Django settings module was specified in the environment, use that.
+    # Otherwise assume it is in a module called `settings` on the path (which
+    # now should include the same directory as the pavement file).
+    django_settings_module = os.environ.get('DJANGO_SETTINGS_MODULE',
+                                            'settings')
+    
+    # If it has already been loaded, use it.
+    if django_settings_module in sys.modules:
+        settings = sys.modules[django_settings_module]
+    else:
+        try:
+            settings = import_module(django_settings_module, path=search_path)
+        except ImportError, exc:
+            sys.stderr.write(
+                'There was an error importing your Django settings module.\n'
+                '\n' + repr(exc) + '\n')
+            sys.exit(1)
+    
+    # Set up the management environment using the settings. We begin by setting
+    # the environment variable to the value of `django_settings_module`, because
+    # this may have been set to 'settings' earlier. Other parts of Django may
+    # rely on this being present.
+    os.environ['DJANGO_SETTINGS_MODULE'] = django_settings_module
+    mgmt.setup_environ(settings)
+    
+    # Add the settings to the Paver environment.
+    environment.options.django = settings
+
+
+def management(function):
+    
+    """
+    Simple decorator to load Django settings before a task is run.
+    
+    ``management`` is a decorator which should be used on tasks to make sure
+    that the Django environment is loaded before the task begins executing.
+    If your task accepts ``options``, the Django settings will be available
+    as ``options.django``.
+    """
+    
+    class ManagementTask(QuietTask):
+        def __call__(self, *args, **kwargs):
+            setup_settings()
+            return super(ManagementTask, self).__call__(*args, **kwargs)
+    
+    return ManagementTask(function)
+
+
+@consume_args
+@management
+def manage(options):
+    
+    """
+    Run Django management commands.
+    
+    The ``manage`` task allows you to run any Django management command as if
+    it were a paver task. To use it, instead of running ``python manage.py``
+    just use ``paver manage`` instead. For example::
+        
+        paver manage syncdb
+        paver manage dumpdata myappname
+        paver manage runserver
+    
+    Et cetera.
+    """
+    
+    utility = mgmt.ManagementUtility(['paver manage'] + options.args)
+    # This is again so that the name of the program shows up as the whole
+    #`paver manage` instead of just `paver`.
+    utility.prog_name = 'paver manage'    
+    utility.execute()
+
+
+def get_admin_media_dir():
+    """Retrieve the absolute path to the admin media directory."""
+    
+    import django.conf as conf
+    if not conf.settings.configured:
+        conf.settings.configure() # Completely blank config
+    import django.contrib.admin
+
+    return path(django.contrib.admin.__file__).abspath().dirname() / 'media'

File project/libs/templatecfg.py

+# -*- coding: utf-8 -*-
+
+from paver.easy import *
+
+@task
+@needs('mkdirs')
+@cmdopts([
+    ('force-regenerate', 'f', 'Force overwriting current config files'),
+    ('deployment=', 'd', 'Build configuration for given deployment')])
+def generate_config(options):
+    """Generate specified configuration files using Jinja2."""
+    
+    import jinja2
+    
+    loader = jinja2.FileSystemLoader([])
+    env = jinja2.Environment(loader=loader)
+    
+    context = {'options': options}
+    context.update(options.get('extra_context', {}))
+    
+    deployment = getattr(options, 'deployment', 'default')
+    
+    for template_file, config_file in options.templatecfg.config_files:
+        config_file %= {'deployment': deployment}
+        
+        template_file, config_file = map(path, (template_file, config_file))
+        config_file.dirname().makedirs()
+        
+        info('Processing config file "%s"', config_file)
+        
+        if config_file.exists() and (not options.get('force_regenerate')):
+            info('Config file "%s" exists. Skipping.', config_file)
+            continue
+        
+        # For each template, just make the template's parent dir the only path
+        # on the template search path.
+        loader.searchpath = [template_file.dirname()]
+        template = env.get_template(template_file.basename())
+        
+        output = template.render(context)
+        
+        def write_data():
+            fp = open(config_file, 'w')
+            try:
+                fp.write(output)
+            finally:
+                fp.close()
+        dry('Writing data to "%s"' % config_file, write_data)

File project/media/favicon.ico

Added
New image

File project/pavement.py

+# -*- coding: utf-8 -*-
+
+import os
+import subprocess
+
+from paver import virtual
+from paver.easy import *
+from paver.path import path
+
+
+project_dir = path(__file__).abspath().dirname()
+project_name = project_dir.basename()
+site_dir = project_dir.parent
+
+sys.path.append(site_dir)
+sys.path.append(project_dir / 'apps')
+sys.path.append(project_dir / 'libs')
+
+from djpave import *
+from templatecfg import *
+
+
+options(
+    # This should be the name of the module containing your Django project.
+    project_name = project_name,
+    # This is used in the lighttpd config, and should be the hostname of
+    # your *production* server.
+    hostname = 'example.com',
+    # These values can be left alone. They just incorporate references to the
+    # standard directories into pavement options.
+    project_dir = project_dir,
+    site_dir = site_dir,
+    
+    virtualenv = Bunch(
+        script_name = 'start.py',
+        dest_dir = site_dir,
+        no_site_packages = True,
+        packages_to_install = [
+            'DjanJinja',
+            'Django',
+            'Jinja2',
+            'flup',
+            'pip',
+            'superlance',
+            'supervisor',
+        ],
+        paver_command_line = 'quickstart',
+    ),
+    
+    supervisor = Bunch(
+        port = 9001,
+        # user = 'django',
+        # group = 'django',
+        use_memmon = True,
+        # Limit all processes to 300MB of RAM.
+        memmon_args = ['-a', '300MB'],
+    ),
+    
+    lighttpd = Bunch(
+        # user = 'httpd',
+        # group = 'httpd',
+    ),
+    
+    fastcgi = Bunch(
+        # user = 'django',
+        # group = 'django',
+        num_workers = 6,
+        socket = site_dir / 'sock' / 'fcgi.sock',
+        program_name = 'django',
+    ),
+    
+    templatecfg = Bunch(
+        config_files = [
+            # All entries take the format:
+            #     (template_path, output_path)
+            # Where output path will be formatted with the deployment in a
+            # dictionary (i.e. `output_path % {'deployment': 'foobar'}`).
+            
+            # Supervisor
+            (project_dir / 'etc.templates' / 'supervisord.conf',
+             project_dir / 'etcs' / '%(deployment)s' / 'supervisord.conf'),
+            
+            (project_dir / 'etc.templates' / 'lighttpd.svd.conf',
+             project_dir / 'etcs' / '%(deployment)s' / 'lighttpd.svd.conf'),
+            
+            (project_dir / 'etc.templates' / 'fastcgi.svd.conf',
+             project_dir / 'etcs' / '%(deployment)s' / 'fastcgi.svd.conf'),
+            
+            # lighttpd
+            (project_dir / 'etc.templates' / 'lighttpd.conf',
+             project_dir / 'etcs' / '%(deployment)s' / 'lighttpd.conf'),
+            
+            (project_dir / 'etc.templates' / 'fastcgi.light.conf',
+             project_dir / 'etcs' / '%(deployment)s' / 'fastcgi.light.conf'),
+            
+            (project_dir / 'etc.templates' / 'mimetypes.light.conf',
+             project_dir / 'etcs' / '%(deployment)s' / 'mimetypes.light.conf'),
+            
+            # Django settings
+            (project_dir / 'etc.templates' / 'common.py',
+             project_dir / 'settings' / 'common.py'),
+        ],
+        
+        # Additional context passed to Jinja2 template for rendering.
+        extra_context = {
+            'generate_secret_key': generate_secret_key,
+            'get_admin_media_dir': get_admin_media_dir,
+            'cmd_join': subprocess.list2cmdline,
+        },
+    ),
+    
+    mkdirs = [
+        project_dir / 'apps',
+        project_dir / 'etcs',
+        project_dir / 'media' / 'js',
+        project_dir / 'media' / 'css',
+        project_dir / 'media' / 'img',
+        project_dir / 'templates',
+        site_dir / 'cache' / 'compress',
+        site_dir / 'db',
+        site_dir / 'log' / 'django',
+        site_dir / 'log' / 'lighttpd',
+        site_dir / 'pid',
+        site_dir / 'sock',
+        site_dir / 'uploads',
+    ],
+)
+
+
+@task
+def mkdirs(options):
+    """Ensure that certain necessary directories exist."""
+    
+    for directory in options.mkdirs:
+        if not directory.exists():
+            path(directory).makedirs()
+
+
+@task
+def quickstart(options):
+    """Quickly bootstrap the deployment environment."""
+    
+    (options.project_dir / 'start.py').remove()
+    call_task('mkdirs')
+    info(
+        "\nNext steps:\n"
+        "  * Edit pavement.py so all the settings are correct.\n"
+        "  * Run `paver generate_config -d DEPLOYMENT`\n"
+        "  * Symlink PROJECT_DIR/etc -> PROJECT_DIR/etcs/DEPLOYMENT\n\n"
+        "And don't forget to set DJANGO_SETTINGS_MODULE to the correct value "
+        "(e.g. 'settings.development', et cetera). This can be done in the "
+        "`bin/activate` file in your site directory, so it will be set every "
+        "time you activate the virtualenv.")

File project/settings/__init__.py

Empty file added.

File project/settings/development.py

+# -*- coding: utf-8 -*-
+
+from common import *
+
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+DATABASE_ENGINE = 'sqlite3'
+DATABASE_NAME = SITE_DIR / 'db' / 'development.sqlite'
+DATABASE_USER = ''
+DATABASE_PASSWORD = ''
+DATABASE_HOST = ''
+DATABASE_PORT = ''
+
+CACHE_BACKEND = 'dummy://'
+CACHE_MIDDLEWARE_SECONDS = 1
+CACHE_MIDDLEWARE_KEY_PREFIX = PROJECT_NAME + '_'

File project/static/error/404.html

+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+  <head>
+    <title>Not Found</title>
+  </head>
+
+  <body>
+    <h1>Not Found</h1>
+    <p>The page you requested was not found.</p>
+  </body>
+</html>

File project/static/error/500.html

+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+  <head>
+    <title>Server Error</title>
+  </head>
+
+  <body>
+    <h1>Server Error</h1>
+    <p>There was an error processing your request.</p>
+  </body>
+</html>

File project/static/error/503.html

+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+  <head>
+    <title>Service Unavailable</title>
+  </head>
+
+  <body>
+    <h1>Service Unavailable</h1>
+    <p>The site is currently down for maintenance. Please try again later.</p>
+  </body>
+</html>

File project/urls.py

+# -*- coding: utf-8 -*-
+
+from django.conf import settings
+from django.conf.urls.defaults import *
+
+
+urlpatterns = patterns('',
+    ## Nothing here yet.
+)
+
+
+if 'django.contrib.admin' in settings.INSTALLED_APPS:
+    from django.contrib import admin
+    admin.autodiscover()
+    
+    # Ensure admin comes first.
+    urlpatterns = patterns('',
+        url(r'^admin/', include(admin.site.urls), name='admin-root')
+    ) + urlpatterns
+
+
+# If we’re in a production environment, these paths will get handled by the
+# web server anyway.
+urlpatterns += patterns('',
+    url(r'^media/admin/(?P<path>.*)$', 'django.views.static.serve',
+        {'document_root': settings.ADMIN_MEDIA_DIR}),
+    url(r'^media/(?P<path>.*)$', 'django.views.static.serve',
+        {'document_root': settings.MEDIA_DIR}),
+)