Breyten Ernsting

Webdeveloper at Open State Foundation.

Multiple Environments for Deployment Using Fabric

I use Fabric to deploy my Python projects. While it offers a lot of flexibility, it lacks a concept of different environments (Also referred to as stages) so you need to figure out your own solution.

Such a solution boils down to modifying the env variable on the fly. While this post at StackOverflow points to the right direction, it’s a bit more laborious than I like since you explicitly have to set the attributes on the env variable with each option. The solution I came up with looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from fabric.api import run, local, env, settings, cd, task, put, execute
from fabric.contrib.files import exists
from fabric.operations import _prefix_commands, _prefix_env_vars, require
#from fabric.decorators import runs_once
#from fabric.context_managers import cd, lcd, settings, hide

STAGES = {
    'test': {
        'hosts': ['breyten@test.example.org'],
        'code_dir': '/var/www/test.example.org',
        'code_branch': 'master',
        # ...
    },
    'production': {
        'hosts': ['breyten@example.org'],
        'code_dir': '/var/www/example.org',
        'code_branch': 'production',
        # ...
    },
}

def stage_set(stage_name='test'):
    env.stage = stage_name
    for option, value in STAGES[env.stage].items():
        setattr(env, option, value)

@task
def production():
    stage_set('production')

@task
def test():
    stage_set('test')

@task
def deploy():
    """
    Deploy the project.
    """

    require('stage', provided_by=(test,production,))
    # ...

This allows you to deploy easily: run fab test deploy to the deploy to the test environment, while fab production deploy deploys to the production environment. Running the deploy task directly results in an error so I always have to be aware of the environment I deploy to.

Comments