From e97a1a6790d887132f9d55e175345fc2a657d64b Mon Sep 17 00:00:00 2001 From: dsc Date: Mon, 14 May 2012 13:19:23 -0700 Subject: [PATCH] Updates deployer with new targets for test-reportcard and dev-reportcard, allows overrides to various conf variables. --- fabfile/__init__.py | 46 ++++++++++++------------ fabfile/deploy.py | 2 +- fabfile/stages.py | 100 ++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 108 insertions(+), 40 deletions(-) diff --git a/fabfile/__init__.py b/fabfile/__init__.py index f4aaad9..e13d825 100644 --- a/fabfile/__init__.py +++ b/fabfile/__init__.py @@ -3,6 +3,8 @@ "GraphKit Deployer" import sys +from functools import wraps + # Deal with the fact that we aren't *really* a Python project, # so we haven't declared python dependencies. @@ -31,21 +33,27 @@ from util import * ### Fabric Config # TODO: env.rcfile = 'fabfile/fabricrc.conf' -env.project_name = 'kraken-ui' -env.colors = True -env.use_ssh_config = True - - -### Project Config -env.dev_server = 'localhost:8081' -env.minify_cmd = 'uglifyjs' +# We `update()` here so --set on the commandline will override. +for k, v in dict( + project_name = 'kraken-ui', + colors = True, + use_ssh_config = True, + + dev_server = 'localhost:8081', + minify_cmd = 'uglifyjs', + + dist = 'dist', + local_tmp = 'tmp', + browserify_js = 'vendor/browserify.js', + supervisor_job = 'reportcard', +).iteritems(): + env.setdefault(k, v) + +for k in ('dist', 'local_tmp',): + env[k] = p(env[k]) -env.dist = p('dist') -env.local_tmp = p('tmp') env.work_dir = env.local_tmp/env.dist - -env.browserify_js = 'vendor/browserify.js' env.work_browserify_js = env.work_dir/env.browserify_js env.vendor_search_dirs = map(p, ['static', 'var', env.work_dir]) env.vendor_bundle = env.work_dir/'vendor/vendor-bundle.min.js' @@ -53,22 +61,14 @@ env.app_bundle = env.work_dir/'js/kraken/app-bundle.js' env.app_bundle_min = p(env.app_bundle.replace('.js', '.min.js')) - ### Setup Staging Environments # Envs aren't declared with @task in the stages module so that we can # decorate them here and have them show up at the top level. import stages -@task -def prod(): - "Set deploy environment to production." - stages.prod() - -@task -def stage(): - "Set deploy environment to stage." - stages.stage() +for name in stages.NAMES: + globals()[name] = task(getattr(stages, name)) @@ -78,7 +78,7 @@ import bundle import deploy @task(default=True) -@stages.ensure_stage +@stages.prompt_for_stage def full_deploy(): """ Bundles and deploys the project. [Default] """ diff --git a/fabfile/deploy.py b/fabfile/deploy.py index 2139204..4111088 100644 --- a/fabfile/deploy.py +++ b/fabfile/deploy.py @@ -53,6 +53,6 @@ def sync_files(): def restart_node(): """ Restarts node.js server on the deployment host. """ - sudo("supervisorctl restart reportcard") + sudo("supervisorctl restart %(supervisor_job)s" % env) diff --git a/fabfile/stages.py b/fabfile/stages.py index 0140fef..2b58c72 100644 --- a/fabfile/stages.py +++ b/fabfile/stages.py @@ -2,11 +2,12 @@ # -*- coding: utf-8 -*- "Setup Staging Environments" +import sys from functools import wraps -from fabric.api import env, abort +from fabric.api import env, abort, prompt, execute from fabric.colors import white, blue, cyan, green, yellow, red, magenta -__all__ = ('prod', 'stage', 'ensure_stage',) +__all__ = ['NAMES', 'validate_stage', 'prompt_for_stage', 'ensure_stage', 'stage',] # (otto) There should be a way to do this using stages. # See: http://tav.espians.com/fabric-python-with-cleaner-api-and-parallel-deployment-support.html @@ -16,11 +17,63 @@ __all__ = ('prod', 'stage', 'ensure_stage',) # env.stages = ['prod', 'staging'] +NAMES = [] + +def validate_stage(name): + "Tests whether given name is a valid staging environment." + name = name.strip() + if name not in NAMES: + raise Exception("%r is not a valid staging environment!" % name) + return name + + +def prompt_for_stage(fn): + "Decorator which prompts for a stage-name if not set." + + @wraps(fn) + def wrapper(*args, **kwargs): + if 'deploy_env' not in env: + name = prompt(white('Please select a staging target %s:' % NAMES, bold=True), validate=validate_stage) + execute(name) + # Must call `execute` on the supplied task, otherwise the host-lists won't be updated. + execute(fn, *args, **kwargs) + + return wrapper + + +def ensure_stage(fn): + "Decorator that ensures a stage is set." + + @wraps(fn) + def wrapper(*args, **kwargs): + if 'deploy_env' not in env: + abort(red('You must specify a staging environment (prod, stage) prior to deploy!', bold=True)) + return fn(*args, **kwargs) + + return wrapper + +def stage(fn): + """ Decorator indicating this function sets a stage environment: + + @stage + def mystage(): + env.deploy_env = 'mystage' + ... + + """ + NAMES.append(fn.__name__) + __all__.append(fn.__name__) + return fn + + +### # NOTE WELL: These do not have @task as they'd show up as "stages.prod", # and we want them at the top level. See __init__.py for the wrappers. # Unfortunately, we can't put them in __init__.py without preventing deploy.py # from importing them. +### +@stage def prod(): """ Set deploy environment to production. """ @@ -31,23 +84,38 @@ def prod(): env.owner = 'www-data' env.group = 'www' -def stage(): - """ Set deploy environment to stage. +@stage +def test(): + """ Set deploy environment to test. + """ + env.deploy_env = 'test' + env.hosts = ['kripke.pmtpa.wmflabs'] + env.gateway = 'bastion.wmflabs.org' + env.target_dir = '/srv/test-reportcard.wmflabs.org/kraken-ui' + env.owner = 'www-data' + env.group = 'www' + env.supervisor_job = 'test-reportcard' + +@stage +def dev(): + """ Set deploy environment to dev. + """ + env.deploy_env = 'dev' + env.hosts = ['kripke.pmtpa.wmflabs'] + env.gateway = 'bastion.wmflabs.org' + env.target_dir = '/srv/dev-reportcard.wmflabs.org/kraken-ui' + env.owner = 'www-data' + env.group = 'www' + env.supervisor_job = 'dev-reportcard' + +@stage +def lessly(): + """ Set deploy environment to lessly. """ - env.deploy_env = 'stage' + env.deploy_env = 'lessly' env.hosts = ['less.ly'] env.target_dir = '/home/wmf/projects/kraken-ui' env.owner = 'wmf' env.group = 'www' - -def ensure_stage(fn): - "Decorator that ensures a stage is set." - - @wraps(fn) - def wrapper(*args, **kwargs): - if 'deploy_env' not in env: - abort(red('You must specify a staging environment (prod, stage) prior to deploy!', bold=True)) - return fn(*args, **kwargs) - - return wrapper + del env['gateway'] -- 1.7.0.4