Automation with Platter

Platter is built with automation in mind. There are a handful of tools you can use platter together with, to make automated deployments a reality.

Automated Building

To automate the building process we recommend Fabric. Fabric can be used to upload the source artifacts on a build server, then invoke the building process there and to fetch down the resulting build artifact. This allows you to trigger a build from any machine even if it does not have the correct architecture or operating system.

You will need fabric installed locally and a fabfile.py that looks something like this:

import os
import tempfile
from fabric.api import task, local, run, cd, get, put, hosts


@task
@hosts('my-build-server-hostname')
def build(rev='HEAD'):
    # ask git to create an archive for the right version.
    tmp = tempfile.mktemp(suffix='.tar.gz')
    local('git archive "%s" | gzip > "%s"' % (rev, tmp))

    # upload that archive to a temporary folder
    buildtmp = '/tmp/build-%s' % os.urandom(20).encode('hex')
    run('mkdir %s' % buildtmp)
    put(tmp, '%s/src.tar.gz' % buildtmp)

    # In that folder
    with cd(buildtmp):
        # extract the uploaded archive
        run('tar xzf src.tar.gz')
        # and invoke platter to build it
        run('/path/to/venv/bin/platter build .')
        # then download the archive and place it in `dist`
        local('mkdir -p dist')
        get('dist/*.tar.gz', 'dist')

    # Clean up
    run('rm -rf %s' % buildtmp)

This example requires a few things:

  1. in this case we use git as source control system. If you use something else you will need to adjust the code accordingly.
  2. platter is installed on the build server into /path/to/venv into a virtualenv. You can obviously adjust this.

To build the package you can then run this:

$ fab build

Or to build a specific version:

$ fab build:rev=1.0-rc1

The resulting build artifact will end up in the dist directory next to the fabfile.

Automated Installing

For automated installation the archive can be placed on a server, extracted and installed. The usually recommended way for this is to extract the package to a version specific folder and to then symlink the virtualenv to an alias.

This is easy to accomplish because the tarball generated by platter contains metadata that can be used by tools. For instance it contains a file named VERSION with the version number.

Here an example fabfile.py which can upload a package to hosts:

import os
from fabric.api import task, put, run, cd

@task
def deploy(archive=None):
    # If archive is not provided, we use the 'last' one
    if archive is None:
        archive = os.path.join('dist',
            sorted(os.listdir('dist'))[-1])

    # Upload the archive and make some temporary space in /tmp
    put(archive, '/tmp/yourapp.tar.gz')
    run('rm -rf /tmp/yourapp && mkdir -p /tmp/yourapp')

    # Now enter the temporary folder
    with cd('/tmp/yourapp'):
        # Extract the archive, throwing away the toplevel folder
        run('tar --strip-components=1 -xzf /tmp/yourapp.tar.gz')

        # Ask for the version
        version = str(run('cat VERSION'))

        # Install into a version specific directory
        run('./install.sh /srv/yourapp/versions/%s' % version)

        # Create a symlink for the current version
        run('ln -sf %s /srv/yourapp/versions/current' % version)

    # Clean up the mess
    run('rm -rf /tmp/yourapp /tmp/yourapp.tar.gz')

You can then deploy an archive trivially:

$ fab -H myserver deploy