Fix datepicker arrows style on hover
[cds-indico.git] / fabfile.py
blob8099b8fc21467cf3736ca1086c72feabf223dcb6
1 # This file is part of Indico.
2 # Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
4 # Indico is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License as
6 # published by the Free Software Foundation; either version 3 of the
7 # License, or (at your option) any later version.
9 # Indico is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with Indico; if not, see <http://www.gnu.org/licenses/>.
17 """
18 fabfile for Indico development operations
19 """
21 import os
22 import re
23 import sys
24 import glob
25 import shutil
26 import requests
27 import json
28 import getpass
29 from contextlib import contextmanager
30 import operator
32 from fabric.api import local, lcd, task, env
33 from fabric.context_managers import prefix, settings
34 from fabric.colors import red, green, yellow, cyan
35 from fabric.contrib import console
36 from fabric.operations import put, run
39 ASSET_TYPES = ['js', 'sass', 'css']
40 DOC_DIRS = ['guides']
41 RECIPES = {}
42 DEFAULT_REQUEST_ERROR_MSG = 'UNDEFINED ERROR (no error description from server)'
43 CONF_FILE_NAME = 'fabfile.conf'
44 SOURCE_DIR = os.path.dirname(__file__)
46 execfile(CONF_FILE_NAME, {}, env)
48 env.update({
49 'conf': CONF_FILE_NAME,
50 'src_dir': SOURCE_DIR,
51 'ext_dir': os.path.join(SOURCE_DIR, env.ext_dirname),
52 'target_dir': os.path.join(SOURCE_DIR, env.target_dirname),
53 'node_env_path': os.path.join(SOURCE_DIR, env.node_env_dirname)
57 def recipe(name):
58 def _wrapper(f):
59 RECIPES[name] = f
60 return _wrapper
63 # Decorators
65 @contextmanager
66 def node_env():
67 if env.system_node:
68 yield
69 else:
70 with prefix('. {0}'.format(os.path.join(env.node_env_path, 'bin/activate'))):
71 yield
74 @contextmanager
75 def pyenv_env(version):
76 cmd_dir = os.path.join(env.pyenv_dir, 'versions', 'indico-build-{0}'.format(version), 'bin')
77 with prefix('PATH={0}:$PATH'.format(cmd_dir)):
78 yield
81 def pyenv_cmd(cmd, **kwargs):
82 cmd_dir = os.path.join(env.pyenv_dir, 'bin')
83 return local('{0}/pyenv {1}'.format(cmd_dir, cmd), **kwargs)
86 # Util functions
88 def _yes_no_input(message, default):
89 c = '? '
90 if default.lower() == 'y':
91 c = ' [Y/n]? '
92 elif default.lower() == 'n':
93 c = ' [y/N]? '
94 s = raw_input(message+c) or default
95 if s.lower() == 'y':
96 return True
97 else:
98 return False
101 def _putl(source_file, dest_dir):
103 To be used instead of put, since it doesn't support symbolic links
106 put(source_file, '/')
107 run("mkdir -p {0}".format(dest_dir))
108 run("mv -f /{0} {1}".format(os.path.basename(source_file), dest_dir))
111 def create_node_env():
112 with settings(warn_only=True):
113 local('nodeenv -c -n {0} {1}'.format(env.node_version, env.node_env_path))
116 def lib_dir(src_dir, dtype):
117 target_dir = os.path.join(src_dir, 'indico', 'htdocs')
118 return os.path.join(target_dir, dtype, 'lib')
121 def _check_pyenv(py_versions):
123 Check that pyenv and pyenv-virtualenv are installed and set up the
124 compilers/virtual envs in case they do not exist
127 if os.system('which pyenv'):
128 print red("Can't find pyenv!")
129 print yellow("Are you sure you have installed it?")
130 sys.exit(-2)
131 elif os.system('which pyenv-virtualenv'):
132 print red("Can't find pyenv-virtualenv!")
133 print yellow("Are you sure you have installed it?")
134 sys.exit(-2)
136 # list available pyenv versions
137 av_versions = os.listdir(os.path.join(env.pyenv_dir, 'versions'))
139 for py_version in py_versions:
140 if py_version not in av_versions:
141 print green('Installing Python {0}'.format(py_version))
142 pyenv_cmd('install {0}'.format(py_version), capture=True)
144 local("echo \'y\' | pyenv virtualenv {0} indico-build-{0}".format(py_version))
146 with pyenv_env(py_version):
147 local("pip install -r requirements.dev.txt")
150 def _check_present(executable, message="Please install it first."):
152 Check that executable exists in $PATH
155 with settings(warn_only=True):
156 if local('which {0} > /dev/null && echo $?'.format(executable), capture=True) != '0':
157 print red('{0} is not available in this system. {1}'.format(executable, message))
158 sys.exit(-2)
161 def _safe_rm(path, recursive=False, ask=True):
162 if path[0] != '/':
163 path = os.path.join(env.lcwd, path)
164 if ask:
165 files = glob.glob(path)
166 if files:
167 print yellow("The following files are going to be deleted:\n ") + '\n '.join(files)
168 if console.confirm(cyan("Are you sure you want to delete them?")):
169 local('rm {0}{1}'.format('-rf ' if recursive else '', path))
170 else:
171 print red("Delete operation cancelled")
172 else:
173 local('rm {0}{1}'.format('-rf ' if recursive else '', path))
176 def _cp_tree(dfrom, dto, exclude=[]):
178 Simple copy with exclude option
180 if dfrom[0] != '/':
181 dfrom = os.path.join(env.lcwd, dfrom)
182 if dto[0] != '/':
183 dto = os.path.join(env.lcwd, dto)
185 print "{0} -> {1}".format(dfrom, dto)
187 shutil.copytree(dfrom, dto, ignore=shutil.ignore_patterns(*exclude))
190 def _find_most_recent(path, cmp=operator.gt, maxt=0):
191 for dirpath, __, fnames in os.walk(path):
192 for fname in fnames:
194 # ignore hidden files and ODTs
195 if fname.startswith(".") or fname.endswith(".odt"):
196 continue
198 mtime = os.stat(os.path.join(dirpath, fname)).st_mtime
199 if cmp(mtime, maxt):
200 maxt = mtime
201 return maxt
204 def _find_least_recent(path):
205 return _find_most_recent(path, cmp=operator.lt, maxt=sys.maxint)
208 def _install_dependencies(mod_name, sub_path, dtype, dest_subpath=None):
209 l_dir = lib_dir(env.src_dir, dtype)
210 dest_dir = os.path.join(l_dir, dest_subpath) if dest_subpath else l_dir
211 local('mkdir -p {0}'.format(dest_dir))
212 local('cp -R {0} {1}/'.format(
213 os.path.join(env.ext_dir, mod_name, sub_path),
214 dest_dir))
217 # Recipes
219 @recipe('angular')
220 def install_angular():
221 with node_env():
222 with lcd(os.path.join(env.ext_dir, 'angular')):
223 local('npm install')
224 local('grunt clean buildall copy write compress')
225 dest_dir_js = lib_dir(env.src_dir, 'js')
226 dest_dir_css = lib_dir(env.src_dir, 'css')
227 local('mkdir -p {0}'.format(dest_dir_js))
228 local('cp build/angular.js {0}/'.format(dest_dir_js))
229 local('cp build/angular-resource.js {0}/'.format(dest_dir_js))
230 local('cp build/angular-sanitize.js {0}/'.format(dest_dir_js))
231 local('cp css/angular.css {0}'.format(dest_dir_css))
234 @recipe('ui-sortable')
235 def install_ui_sortable():
236 with node_env():
237 with lcd(os.path.join(env.ext_dir, 'ui-sortable')):
238 dest_dir_js = lib_dir(env.src_dir, 'js')
239 local('mkdir -p {0}'.format(dest_dir_js))
240 local('cp src/sortable.js {0}/'.format(dest_dir_js))
243 @recipe('compass')
244 def install_compass():
245 _install_dependencies('compass', 'frameworks/compass/stylesheets/*', 'sass', 'compass')
248 @recipe('jquery')
249 def install_jquery():
250 with node_env():
251 with lcd(os.path.join(env.ext_dir, 'jquery')):
252 local('npm install')
253 local('grunt')
254 dest_dir = lib_dir(env.src_dir, 'js')
255 local('mkdir -p {0}'.format(dest_dir))
256 local('cp dist/jquery.js {0}/'.format(dest_dir))
259 @recipe('jed')
260 def install_jed():
261 with lcd(os.path.join(env.ext_dir, 'Jed')):
262 dest_dir = lib_dir(env.src_dir, 'js')
263 local('mkdir -p {0}'.format(dest_dir))
264 local('cp jed.js {0}/'.format(dest_dir))
267 @recipe('jqplot')
268 def install_jqplot():
269 """Install jQPlot from Git"""
270 plugins = ['axis', 'bar', 'cursor', 'highlighter', 'points', 'text']
271 with lcd(os.path.join(env.ext_dir, 'jqplot')):
272 dest_dir_js = os.path.join(lib_dir(env.src_dir, 'js'), 'jqplot')
273 dest_dir_css = lib_dir(env.src_dir, 'css')
274 dest_dir_js_core = os.path.join(dest_dir_js, 'core')
275 dest_dir_js_plugins = os.path.join(dest_dir_js, 'plugins')
276 local('mkdir -p {0} {1}'.format(dest_dir_js_core, dest_dir_css))
277 local('cp src/core/*.js {0}'.format(dest_dir_js_core))
278 local('cp src/core/*.css {0}'.format(dest_dir_css))
279 for plugin_name in plugins:
280 dest = os.path.join(dest_dir_js_plugins, plugin_name)
281 local('mkdir -p {0}'.format(dest))
282 local('cp src/plugins/{0}/* {1}'.format(plugin_name, dest))
285 @recipe('underscore')
286 def install_underscore():
288 Install underscore from Git
290 _install_dependencies('underscore', 'underscore.js', 'js')
293 @recipe('rrule') # rrule.js
294 def install_rrule():
296 Install rrule from Git
298 _install_dependencies('rrule', 'lib/rrule.js', 'js')
301 @recipe('qtip2')
302 def install_qtip2():
303 with node_env():
304 with lcd(os.path.join(env.ext_dir, 'qtip2')):
305 local('npm install')
306 local('grunt --plugins="tips modal viewport svg" init clean concat:dist concat:css concat:libs replace')
307 dest_dir_js, dest_dir_css = lib_dir(env.src_dir, 'js'), lib_dir(env.src_dir, 'css')
308 local('mkdir -p {0} {1}'.format(dest_dir_js, dest_dir_css))
309 local('cp dist/jquery.qtip.js {0}/'.format(dest_dir_js))
310 local('cp dist/jquery.qtip.css {0}/'.format(dest_dir_css))
313 @recipe('jquery-ui-multiselect')
314 def install_jquery_ui_multiselect():
315 with node_env():
316 with lcd(os.path.join(env.ext_dir, 'jquery-ui-multiselect')):
317 dest_dir_js = lib_dir(env.src_dir, 'js')
318 dest_dir_css = lib_dir(env.src_dir, 'css')
319 local('mkdir -p {0} {1}'.format(dest_dir_js, dest_dir_css))
320 local('cp src/jquery.multiselect.js {0}/'.format(dest_dir_js))
321 local('cp src/jquery.multiselect.filter.js {0}/'.format(dest_dir_js))
322 local('cp jquery.multiselect.css {0}/'.format(dest_dir_css))
323 local('cp jquery.multiselect.filter.css {0}/'.format(dest_dir_css))
326 @recipe('MathJax')
327 def install_mathjax():
328 dest_dir = os.path.join(lib_dir(env.src_dir, 'js'), 'mathjax/')
329 mathjax_js = os.path.join(dest_dir, 'MathJax.js')
331 with lcd(os.path.join(env.ext_dir, 'mathjax')):
332 local('rm -rf {0}'.format(os.path.join(dest_dir)))
333 _cp_tree('unpacked/', dest_dir, exclude=["AM*", "MML*", "Accessible*", "Safe*"])
334 _cp_tree('images/', os.path.join(dest_dir, 'images'))
335 _cp_tree('fonts/', os.path.join(dest_dir, 'fonts'), exclude=["png"])
337 with open(mathjax_js, 'r') as f:
338 data = f.read()
339 # Uncomment 'isPacked = true' line
340 data = re.sub(r'//\s*(MathJax\.isPacked\s*=\s*true\s*;)', r'\1', data, re.MULTILINE)
342 with open(mathjax_js, 'w') as f:
343 f.write(data)
346 @recipe('PageDown')
347 def install_pagedown():
348 with lcd(os.path.join(env.ext_dir, 'pagedown')):
349 dest_dir = os.path.join(lib_dir(env.src_dir, 'js'), 'pagedown/')
350 local('mkdir -p {0}'.format(dest_dir))
351 local('cp *.js {0}'.format(dest_dir))
354 @recipe('ZeroClipboard')
355 def install_zeroclipboard():
357 Install ZeroClipboard from Git
359 with lcd(os.path.join(env.ext_dir, 'zeroclipboard')):
360 dest_dir = os.path.join(lib_dir(env.src_dir, 'js'), 'zeroclipboard/')
361 local('mkdir -p {0}'.format(dest_dir))
362 local('cp dist/ZeroClipboard.js {0}/'.format(dest_dir))
363 local('cp dist/ZeroClipboard.swf {0}/'.format(dest_dir))
366 @recipe('dropzone.js')
367 def install_dropzone_js():
369 Install Dropzone from Git
371 with lcd(os.path.join(env.ext_dir, 'dropzone')):
372 dest_js_dir = os.path.join(lib_dir(env.src_dir, 'js'), 'dropzone.js/')
373 dest_css_dir = os.path.join(lib_dir(env.src_dir, 'css'), 'dropzone.js/')
374 local('mkdir -p {0} {1}'.format(dest_js_dir, dest_css_dir))
375 local('cp dist/dropzone.js {0}/'.format(dest_js_dir))
376 local('cp dist/dropzone.css {0}/'.format(dest_css_dir))
379 @recipe('selectize.js')
380 def install_selectize_js():
381 with lcd(os.path.join(env.ext_dir, 'selectize.js')):
382 dest_js_dir = os.path.join(lib_dir(env.src_dir, 'js'), 'selectize.js/')
383 dest_css_dir = os.path.join(lib_dir(env.src_dir, 'css'), 'selectize.js/')
384 local('mkdir -p {0} {1}'.format(dest_js_dir, dest_css_dir))
385 local('cp dist/js/standalone/selectize.js {0}/'.format(dest_js_dir))
386 local('cp dist/css/selectize.css {0}/'.format(dest_css_dir))
387 local('cp dist/css/selectize.default.css {0}/'.format(dest_css_dir))
390 @recipe('jquery-typeahead')
391 def install_jquery_typeahead():
392 with lcd(os.path.join(env.ext_dir, 'jquery-typeahead')):
393 dest_js_dir = lib_dir(env.src_dir, 'js')
394 dest_css_dir = lib_dir(env.src_dir, 'css')
395 local('mkdir -p {0} {1}'.format(dest_js_dir, dest_css_dir))
396 local('cp src/jquery.typeahead.js {0}'.format(dest_js_dir))
397 local('cp src/jquery.typeahead.css {0}'.format(dest_css_dir))
400 # Tasks
402 @task
403 def install(recipe_name):
405 Install a module given the recipe name
407 RECIPES[recipe_name]()
410 @task
411 def init_submodules(src_dir='.'):
413 Initialize submodules (fetch them from external Git repos)
416 print green("Initializing submodules")
417 with lcd(src_dir):
418 local('pwd')
419 local('git submodule update --init --recursive')
422 def _install_deps():
424 Install asset dependencies
426 print green("Installing asset dependencies...")
427 for recipe_name in RECIPES:
428 print cyan("Installing {0}".format(recipe_name))
429 install(recipe_name)
432 @task
433 def setup_deps(n_env=None, n_version=None, src_dir=None, system_node=None):
435 Setup (fetch and install) dependencies for Indico assets
438 src_dir = src_dir or env.src_dir
439 n_env = n_env or env.node_env_path
440 system_node = system_node.lower() in ('1', 'true') if system_node is not None else env.system_node
442 # initialize submodules if they haven't yet been
443 init_submodules(src_dir)
445 ext_dir = os.path.join(src_dir, 'ext_modules')
447 _check_present('curl')
449 with settings(node_env_path=n_env or os.path.join(ext_dir, 'node_env'),
450 node_version=n_version or env.node_version,
451 system_node=system_node,
452 src_dir=src_dir,
453 ext_dir=ext_dir):
455 if not system_node and not os.path.exists(n_env):
456 create_node_env()
458 with node_env():
459 local('npm install -g grunt-cli')
461 _install_deps()
464 @task
465 def clean_deps(src_dir=None):
467 Clean up generated files
470 for dtype in ASSET_TYPES:
471 _safe_rm('{0}/*'.format(lib_dir(src_dir or env.src_dir, dtype)), recursive=True)
474 @task
475 def cleanup(build_dir=None, force=False):
477 Clean up build environment
479 _safe_rm('{0}'.format(build_dir or env.build_dir), recursive=True, ask=(not force))
482 @task
483 def tarball(src_dir=None):
485 Create a source Indico distribution (tarball)
488 src_dir = src_dir or env.src_dir
490 make_docs(src_dir)
492 setup_deps(n_env=os.path.join(src_dir, 'ext_modules', 'node_env'),
493 src_dir=src_dir)
494 local('python setup.py -q sdist')
497 @task
498 def egg(py_versions=None):
500 Create a binary Indico distribution (egg)
503 for py_version in py_versions:
504 cmd_dir = os.path.join(env.pyenv_dir, 'versions', 'indico-build-{0}'.format(py_version), 'bin')
505 local('{0} setup.py -q bdist_egg'.format(os.path.join(cmd_dir, 'python')))
506 print green(local('ls -lah dist/', capture=True))
509 @task
510 def make_docs(src_dir=None, build_dir=None, force=False):
512 Generate Indico docs
515 src_dir = src_dir or env.src_dir
516 doc_src_dir = os.path.join(src_dir, 'doc')
518 if build_dir is None:
519 target_dir = os.path.join(src_dir, 'indico', 'htdocs', 'ihelp')
520 else:
521 target_dir = os.path.join(build_dir or env.build_dir, 'indico', 'htdocs', 'ihelp')
523 if not force:
524 print yellow("Checking if docs need to be generated... "),
525 if _find_most_recent(target_dir) > _find_most_recent(doc_src_dir):
526 print green("Nope.")
527 return
529 print red("Yes :(")
530 _check_present('pdflatex')
532 print green('Generating documentation')
533 with lcd(doc_src_dir):
534 for d in DOC_DIRS:
535 with lcd(d):
536 local('make html')
537 local('make latex')
538 local('rm -rf {0}/*'.format(os.path.join(target_dir, 'html')))
539 local('mv build/html/* {0}'.format(os.path.join(target_dir, 'html')))
541 with lcd(os.path.join('guides', 'build', 'latex')):
542 local('make all-pdf')
543 local('mv *.pdf {0}'.format(os.path.join(target_dir, 'pdf')))
545 print green('Cleaning up')
546 for d in DOC_DIRS:
547 with lcd(d):
548 local('make clean')
551 def _check_request_error(r):
552 if r.status_code >= 400:
553 j = r.json()
554 msg = j.get('message', DEFAULT_REQUEST_ERROR_MSG)
555 print red("ERROR: {0} ({1})".format(msg, r.status_code))
556 sys.exit(-2)
559 def _valid_github_credentials(auth):
560 url = "https://api.github.com/repos/{0}/{1}".format(env.github['org'], env.github['repo'])
561 r = requests.get(url, auth=(env.github['user'], auth))
562 if (r.status_code == 401) and (r.json().get('message') == 'Bad credentials'):
563 print red('Invalid Github credentials for user \'{0}\''.format(env.github['user']))
564 return False
566 return True
569 def _release_exists(tag_name, auth):
570 url = "https://api.github.com/repos/{0}/{1}/releases".format(env.github['org'], env.github['repo'])
571 r = requests.get(url, auth=(env.github['user'], auth))
572 _check_request_error(r)
573 parsed = r.json()
574 for release in parsed:
575 if release.get('tag_name') == tag_name:
576 rel_id = release.get('id')
577 return (True, rel_id, release)
579 return (False, 0, None)
582 def _asset_exists(rel_id, name, auth):
583 url = "https://api.github.com/repos/{0}/{1}/releases/{2}/assets" \
584 .format(env.github['org'], env.github['repo'], rel_id)
585 r = requests.get(url, auth=(env.github['user'], auth))
586 _check_request_error(r)
587 parsed = r.json()
588 for j in parsed:
589 if j.get('name') == name:
590 asset_id = j.get('id')
591 return (True, asset_id)
593 return (False, 0)
596 @task
597 def upload_github(build_dir=None, tag_name=None, auth_token=None,
598 overwrite=None, indico_version='master'):
600 build_dir = build_dir or env.build_dir
601 auth_token = auth_token or env.github['auth_token']
603 while (auth_token is None) or (not _valid_github_credentials(auth_token)):
604 auth_token = getpass.getpass(
605 'Insert the Github password/OAuth token for user \'{0}\': '.format(env.github['user']))
607 auth_creds = (env.github['user'], auth_token)
609 overwrite = overwrite or env.github['overwrite']
611 # Create a new release
612 tag_name = tag_name or indico_version
613 url = "https://api.github.com/repos/{0}/{1}/releases".format(env.github['org'], env.github['repo'])
614 payload = {
615 'tag_name': tag_name,
616 'target_commitish': indico_version,
617 'name': 'Indico {0}'.format(tag_name),
618 'draft': True
621 (exists, rel_id, release_data) = _release_exists(tag_name, auth_token)
623 if exists:
624 if overwrite is None:
625 overwrite = _yes_no_input('Release already exists, do you want to overwrite', 'n')
626 if overwrite:
627 release_id = rel_id
628 else:
629 return
630 else:
631 # We will need to get a new release id from github
632 r = requests.post(url, auth=auth_creds, data=json.dumps(payload))
633 _check_request_error(r)
634 release_data = r.json()
635 release_id = release_data.get('id')
637 # Upload binaries to the new release
638 binaries_dir = os.path.join(build_dir, 'indico', 'dist')
640 # awful way to handle this, but a regex seems like too much
641 url = release_data['upload_url'][:-7]
643 for f in os.listdir(binaries_dir):
645 # jump over hidden/system files
646 if f.startswith('.'):
647 continue
649 if os.path.isfile(os.path.join(binaries_dir, f)):
650 (exists, asset_id) = _asset_exists(release_id, f, auth_token)
652 if exists:
653 # delete previous version
654 del_url = "https://api.github.com/repos/{0}/{1}/releases/assets/{2}" \
655 .format(env.github['org'], env.github['repo'], asset_id)
656 r = requests.delete(del_url, auth=auth_creds)
657 _check_request_error(r)
659 with open(os.path.join(binaries_dir, f), 'rb') as ff:
660 data = ff.read()
661 extension = os.path.splitext(f)[1]
663 # upload eggs using zip mime type
664 if extension == '.gz':
665 headers = {'Content-Type': 'application/x-gzip'}
666 elif extension == '.egg':
667 headers = {'Content-Type': 'application/zip'}
669 headers['Accept'] = 'application/vnd.github.v3+json'
670 headers['Content-Length'] = len(data)
671 params = {'name': f}
673 print green("Uploading \'{0}\' to Github".format(f))
674 r = requests.post(url, auth=auth_creds, headers=headers, data=data, params=params, verify=False)
675 _check_request_error(r)
678 @task
679 def upload_ssh(build_dir=None, server_host=None, server_port=None,
680 ssh_user=None, ssh_key=None, dest_dir=None):
682 build_dir = build_dir or env.build_dir
683 server_host = server_host or env.ssh['host']
684 server_port = server_port or env.ssh['port']
685 ssh_user = ssh_user or env.ssh['user']
686 ssh_key = ssh_key or env.ssh['key']
687 dest_dir = dest_dir or env.ssh['dest_dir']
689 env.host_string = server_host + ':' + server_port
690 env.user = ssh_user
691 env.key_filename = ssh_key
693 binaries_dir = os.path.join(build_dir, 'indico', 'dist')
694 for f in os.listdir(binaries_dir):
695 if os.path.isfile(os.path.join(binaries_dir, f)):
696 _putl(os.path.join(binaries_dir, f), dest_dir)
699 @task
700 def _package_release(build_dir, py_versions, system_node):
701 # Build source tarball
702 with settings(system_node=system_node):
703 print green('Generating '), cyan('tarball')
704 tarball(build_dir)
706 # Build binaries (EGG)
707 print green('Generating '), cyan('eggs')
708 egg(py_versions)
711 @task
712 def package_release(py_versions=None, build_dir=None, system_node=False,
713 indico_version=None, upstream=None, tag_name=None,
714 github_auth=None, overwrite=None, ssh_server_host=None,
715 ssh_server_port=None, ssh_user=None, ssh_key=None,
716 ssh_dest_dir=None, no_clean=False, force_clean=False,
717 upload_to=None, build_here=False):
719 Create an Indico release - source and binary distributions
722 DEVELOP_REQUIRES = ['pojson>=0.4', 'termcolor', 'werkzeug', 'nodeenv', 'fabric',
723 'sphinx', 'repoze.sphinx.autointerface']
725 py_versions = py_versions.split('/') if py_versions else env.py_versions
726 upload_to = upload_to.split('/') if upload_to else []
728 build_dir = build_dir or env.build_dir
729 upstream = upstream or env.github['upstream']
731 ssh_server_host = ssh_server_host or env.ssh['host']
732 ssh_server_port = ssh_server_port or env.ssh['port']
733 ssh_user = ssh_user or env.ssh['user']
734 ssh_key = ssh_key or env.ssh['key']
735 ssh_dest_dir = ssh_dest_dir or env.ssh['dest_dir']
737 indico_version = indico_version or 'master'
739 local('mkdir -p {0}'.format(build_dir))
741 _check_pyenv(py_versions)
743 if build_here:
744 _package_release(os.path.dirname(__file__), py_versions, system_node)
745 else:
746 with lcd(build_dir):
747 if os.path.exists(os.path.join(build_dir, 'indico')):
748 print yellow("Repository seems to already exist.")
749 with lcd('indico'):
750 local('git fetch {0}'.format(upstream))
751 local('git reset --hard FETCH_HEAD')
752 if not no_clean:
753 local('git clean -df')
754 else:
755 local('git clone {0}'.format(upstream))
756 with lcd('indico'):
757 print green("Checking out branch \'{0}\'".format(indico_version))
758 local('git checkout {0}'.format(indico_version))
760 _package_release(os.path.join(build_dir, 'indico'), py_versions, system_node)
762 for u in upload_to:
763 if u == 'github':
764 upload_github(build_dir, tag_name, github_auth, overwrite, indico_version)
765 elif u == 'ssh':
766 upload_ssh(build_dir, ssh_server_host, ssh_server_port, ssh_user, ssh_key, ssh_dest_dir)
768 if not build_here and force_clean:
769 cleanup(build_dir, force=True)