Remove zeroclipboard from root folder
[cds-indico.git] / fabfile.py
blob8e494f5b833c17c83dc220190111e3d54b2afa49
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 pythonbrew versions
137 av_versions = list(entry.strip() for entry in pyenv_cmd('versions', capture=True).split('\n')[1:])
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():
222 Install Angular.js from Git
224 with node_env():
225 with lcd(os.path.join(env.ext_dir, 'angular')):
226 local('npm install')
227 local('grunt clean buildall copy write compress')
228 dest_dir_js = lib_dir(env.src_dir, 'js')
229 dest_dir_css = lib_dir(env.src_dir, 'css')
230 local('mkdir -p {0}'.format(dest_dir_js))
231 local('cp build/angular.js {0}/'.format(dest_dir_js))
232 local('cp build/angular-resource.js {0}/'.format(dest_dir_js))
233 local('cp build/angular-sanitize.js {0}/'.format(dest_dir_js))
234 local('cp css/angular.css {0}'.format(dest_dir_css))
237 @recipe('ui-sortable')
238 def install_ui_sortable():
240 Install angular ui-sortable from Git
242 with node_env():
243 with lcd(os.path.join(env.ext_dir, 'ui-sortable')):
244 dest_dir_js = lib_dir(env.src_dir, 'js')
245 local('mkdir -p {0}'.format(dest_dir_js))
246 local('cp src/sortable.js {0}/'.format(dest_dir_js))
249 @recipe('compass')
250 def install_compass():
252 Install compass stylesheets from Git
254 _install_dependencies('compass', 'frameworks/compass/stylesheets/*', 'sass', 'compass')
257 @recipe('jquery')
258 def install_jquery():
260 Install jquery from Git
262 with node_env():
263 with lcd(os.path.join(env.ext_dir, 'jquery')):
264 local('npm install')
265 local('grunt')
266 dest_dir = lib_dir(env.src_dir, 'js')
267 local('mkdir -p {0}'.format(dest_dir))
268 local('cp dist/jquery.js {0}/'.format(dest_dir))
271 @recipe('jqplot')
272 def install_jqplot():
273 """Install jQPlot from Git"""
274 plugins = ['axis', 'bar', 'cursor', 'highlighter', 'points', 'text']
275 with lcd(os.path.join(env.ext_dir, 'jqplot')):
276 dest_dir_js = os.path.join(lib_dir(env.src_dir, 'js'), 'jqplot')
277 dest_dir_css = lib_dir(env.src_dir, 'css')
278 dest_dir_js_core = os.path.join(dest_dir_js, 'core')
279 dest_dir_js_plugins = os.path.join(dest_dir_js, 'plugins')
280 local('mkdir -p {0} {1}'.format(dest_dir_js_core, dest_dir_css))
281 local('cp src/core/*.js {0}'.format(dest_dir_js_core))
282 local('cp src/core/*.css {0}'.format(dest_dir_css))
283 for plugin_name in plugins:
284 dest = os.path.join(dest_dir_js_plugins, plugin_name)
285 local('mkdir -p {0}'.format(dest))
286 local('cp src/plugins/{0}/* {1}'.format(plugin_name, dest))
289 @recipe('underscore')
290 def install_underscore():
292 Install underscore from Git
294 _install_dependencies('underscore', 'underscore.js', 'js')
297 @recipe('rrule') # rrule.js
298 def install_rrule():
300 Install rrule from Git
302 _install_dependencies('rrule', 'lib/rrule.js', 'js')
305 @recipe('qtip2')
306 def install_qtip2():
308 Install qtip2 from Git
310 with node_env():
311 with lcd(os.path.join(env.ext_dir, 'qtip2')):
312 local('npm install')
313 local('grunt --plugins="tips modal viewport svg" init clean concat:dist concat:css concat:libs replace')
314 dest_dir_js, dest_dir_css = lib_dir(env.src_dir, 'js'), lib_dir(env.src_dir, 'css')
315 local('mkdir -p {0} {1}'.format(dest_dir_js, dest_dir_css))
316 local('cp dist/jquery.qtip.js {0}/'.format(dest_dir_js))
317 local('cp dist/jquery.qtip.css {0}/'.format(dest_dir_css))
320 @recipe('jquery-ui-multiselect')
321 def install_jquery_ui_multiselect():
323 Install jquery ui multiselect widget from Git
325 with node_env():
326 with lcd(os.path.join(env.ext_dir, 'jquery-ui-multiselect')):
327 dest_dir_js = lib_dir(env.src_dir, 'js')
328 dest_dir_css = lib_dir(env.src_dir, 'css')
329 local('mkdir -p {0} {1}'.format(dest_dir_js, dest_dir_css))
330 local('cp src/jquery.multiselect.js {0}/'.format(dest_dir_js))
331 local('cp src/jquery.multiselect.filter.js {0}/'.format(dest_dir_js))
332 local('cp jquery.multiselect.css {0}/'.format(dest_dir_css))
333 local('cp jquery.multiselect.filter.css {0}/'.format(dest_dir_css))
336 @recipe('MathJax')
337 def install_mathjax():
339 Install MathJax from Git
342 dest_dir = os.path.join(lib_dir(env.src_dir, 'js'), 'mathjax/')
343 mathjax_js = os.path.join(dest_dir, 'MathJax.js')
345 with lcd(os.path.join(env.ext_dir, 'mathjax')):
346 local('rm -rf {0}'.format(os.path.join(dest_dir)))
347 _cp_tree('unpacked/', dest_dir, exclude=["AM*", "MML*", "Accessible*", "Safe*"])
348 _cp_tree('images/', os.path.join(dest_dir, 'images'))
349 _cp_tree('fonts/', os.path.join(dest_dir, 'fonts'), exclude=["png"])
351 with open(mathjax_js, 'r') as f:
352 data = f.read()
353 # Uncomment 'isPacked = true' line
354 data = re.sub(r'//\s*(MathJax\.isPacked\s*=\s*true\s*;)', r'\1', data, re.MULTILINE)
356 with open(mathjax_js, 'w') as f:
357 f.write(data)
360 @recipe('PageDown')
361 def install_pagedown():
363 Install PageDown from Git (mirror!)
365 with lcd(os.path.join(env.ext_dir, 'pagedown')):
366 dest_dir = os.path.join(lib_dir(env.src_dir, 'js'), 'pagedown/')
367 local('mkdir -p {0}'.format(dest_dir))
368 local('cp *.js {0}'.format(dest_dir))
371 @recipe('ZeroClipboard')
372 def install_zeroclipboard():
374 Install ZeroClipboard from Git
376 with lcd(os.path.join(env.ext_dir, 'zeroclipboard')):
377 dest_dir = os.path.join(lib_dir(env.src_dir, 'js'), 'zeroclipboard/')
378 local('mkdir -p {0}'.format(dest_dir))
379 local('cp dist/ZeroClipboard.js {0}/'.format(dest_dir))
380 local('cp dist/ZeroClipboard.min.js {0}/'.format(dest_dir))
381 local('cp dist/ZeroClipboard.min.map {0}/'.format(dest_dir))
382 local('cp dist/ZeroClipboard.swf {0}/'.format(dest_dir))
385 # Tasks
387 @task
388 def install(recipe_name):
390 Install a module given the recipe name
392 RECIPES[recipe_name]()
395 @task
396 def init_submodules(src_dir='.'):
398 Initialize submodules (fetch them from external Git repos)
401 print green("Initializing submodules")
402 with lcd(src_dir):
403 local('pwd')
404 local('git submodule update --init --recursive')
407 def _install_deps():
409 Install asset dependencies
411 print green("Installing asset dependencies...")
412 for recipe_name in RECIPES:
413 print cyan("Installing {0}".format(recipe_name))
414 install(recipe_name)
417 @task
418 def setup_deps(n_env=None, n_version=None, src_dir=None, system_node=None):
420 Setup (fetch and install) dependencies for Indico assets
423 src_dir = src_dir or env.src_dir
424 n_env = n_env or env.node_env_path
425 system_node = system_node.lower() in ('1', 'true') if system_node is not None else env.system_node
427 # initialize submodules if they haven't yet been
428 init_submodules(src_dir)
430 ext_dir = os.path.join(src_dir, 'ext_modules')
432 _check_present('curl')
434 with settings(node_env_path=n_env or os.path.join(ext_dir, 'node_env'),
435 node_version=n_version or env.node_version,
436 system_node=system_node,
437 src_dir=src_dir,
438 ext_dir=ext_dir):
440 if not system_node and not os.path.exists(n_env):
441 create_node_env()
443 with node_env():
444 local('npm install -g grunt-cli')
446 _install_deps()
449 @task
450 def clean_deps(src_dir=None):
452 Clean up generated files
455 for dtype in ASSET_TYPES:
456 _safe_rm('{0}/*'.format(lib_dir(src_dir or env.src_dir, dtype)), recursive=True)
459 @task
460 def cleanup(build_dir=None, force=False):
462 Clean up build environment
464 _safe_rm('{0}'.format(build_dir or env.build_dir), recursive=True, ask=(not force))
467 @task
468 def tarball(src_dir=None):
470 Create a source Indico distribution (tarball)
473 src_dir = src_dir or env.src_dir
475 make_docs(src_dir)
477 setup_deps(n_env=os.path.join(src_dir, 'ext_modules', 'node_env'),
478 src_dir=src_dir)
479 local('python setup.py -q sdist')
482 @task
483 def egg(py_versions=None):
485 Create a binary Indico distribution (egg)
488 for py_version in py_versions:
489 cmd_dir = os.path.join(env.pyenv_dir, 'versions', 'indico-build-{0}'.format(py_version), 'bin')
490 local('{0} setup.py -q bdist_egg'.format(os.path.join(cmd_dir, 'python')))
491 print green(local('ls -lah dist/', capture=True))
494 @task
495 def make_docs(src_dir=None, build_dir=None, force=False):
497 Generate Indico docs
500 src_dir = src_dir or env.src_dir
501 doc_src_dir = os.path.join(src_dir, 'doc')
503 if build_dir is None:
504 target_dir = os.path.join(src_dir, 'indico', 'htdocs', 'ihelp')
505 else:
506 target_dir = os.path.join(build_dir or env.build_dir, 'indico', 'htdocs', 'ihelp')
508 if not force:
509 print yellow("Checking if docs need to be generated... "),
510 if _find_most_recent(target_dir) > _find_most_recent(doc_src_dir):
511 print green("Nope.")
512 return
514 print red("Yes :(")
515 _check_present('pdflatex')
517 print green('Generating documentation')
518 with lcd(doc_src_dir):
519 for d in DOC_DIRS:
520 with lcd(d):
521 local('make html')
522 local('make latex')
523 local('rm -rf {0}/*'.format(os.path.join(target_dir, 'html')))
524 local('mv build/html/* {0}'.format(os.path.join(target_dir, 'html')))
526 with lcd(os.path.join('guides', 'build', 'latex')):
527 local('make all-pdf')
528 local('mv *.pdf {0}'.format(os.path.join(target_dir, 'pdf')))
530 print green('Cleaning up')
531 for d in DOC_DIRS:
532 with lcd(d):
533 local('make clean')
536 def _check_request_error(r):
537 if r.status_code >= 400:
538 j = r.json()
539 msg = j.get('message', DEFAULT_REQUEST_ERROR_MSG)
540 print red("ERROR: {0} ({1})".format(msg, r.status_code))
541 sys.exit(-2)
544 def _valid_github_credentials(auth):
545 url = "https://api.github.com/repos/{0}/{1}".format(env.github['org'], env.github['repo'])
546 r = requests.get(url, auth=(env.github['user'], auth))
547 if (r.status_code == 401) and (r.json().get('message') == 'Bad credentials'):
548 print red('Invalid Github credentials for user \'{0}\''.format(env.github['user']))
549 return False
551 return True
554 def _release_exists(tag_name, auth):
555 url = "https://api.github.com/repos/{0}/{1}/releases".format(env.github['org'], env.github['repo'])
556 r = requests.get(url, auth=(env.github['user'], auth))
557 _check_request_error(r)
558 parsed = r.json()
559 for release in parsed:
560 if release.get('tag_name') == tag_name:
561 rel_id = release.get('id')
562 return (True, rel_id, release)
564 return (False, 0, None)
567 def _asset_exists(rel_id, name, auth):
568 url = "https://api.github.com/repos/{0}/{1}/releases/{2}/assets" \
569 .format(env.github['org'], env.github['repo'], rel_id)
570 r = requests.get(url, auth=(env.github['user'], auth))
571 _check_request_error(r)
572 parsed = r.json()
573 for j in parsed:
574 if j.get('name') == name:
575 asset_id = j.get('id')
576 return (True, asset_id)
578 return (False, 0)
581 @task
582 def upload_github(build_dir=None, tag_name=None, auth_token=None,
583 overwrite=None, indico_version='master'):
585 build_dir = build_dir or env.build_dir
586 auth_token = auth_token or env.github['auth_token']
588 while (auth_token is None) or (not _valid_github_credentials(auth_token)):
589 auth_token = getpass.getpass(
590 'Insert the Github password/OAuth token for user \'{0}\': '.format(env.github['user']))
592 auth_creds = (env.github['user'], auth_token)
594 overwrite = overwrite or env.github['overwrite']
596 # Create a new release
597 tag_name = tag_name or indico_version
598 url = "https://api.github.com/repos/{0}/{1}/releases".format(env.github['org'], env.github['repo'])
599 payload = {
600 'tag_name': tag_name,
601 'target_commitish': indico_version,
602 'name': 'Indico {0}'.format(tag_name),
603 'draft': True
606 (exists, rel_id, release_data) = _release_exists(tag_name, auth_token)
608 if exists:
609 if overwrite is None:
610 overwrite = _yes_no_input('Release already exists, do you want to overwrite', 'n')
611 if overwrite:
612 release_id = rel_id
613 else:
614 return
615 else:
616 # We will need to get a new release id from github
617 r = requests.post(url, auth=auth_creds, data=json.dumps(payload))
618 _check_request_error(r)
619 release_data = r.json()
620 release_id = release_data.get('id')
622 # Upload binaries to the new release
623 binaries_dir = os.path.join(build_dir, 'indico', 'dist')
625 # awful way to handle this, but a regex seems like too much
626 url = release_data['upload_url'][:-7]
628 for f in os.listdir(binaries_dir):
630 # jump over hidden/system files
631 if f.startswith('.'):
632 continue
634 if os.path.isfile(os.path.join(binaries_dir, f)):
635 (exists, asset_id) = _asset_exists(release_id, f, auth_token)
637 if exists:
638 # delete previous version
639 del_url = "https://api.github.com/repos/{0}/{1}/releases/assets/{2}" \
640 .format(env.github['org'], env.github['repo'], asset_id)
641 r = requests.delete(del_url, auth=auth_creds)
642 _check_request_error(r)
644 with open(os.path.join(binaries_dir, f), 'rb') as ff:
645 data = ff.read()
646 extension = os.path.splitext(f)[1]
648 # upload eggs using zip mime type
649 if extension == '.gz':
650 headers = {'Content-Type': 'application/x-gzip'}
651 elif extension == '.egg':
652 headers = {'Content-Type': 'application/zip'}
654 headers['Accept'] = 'application/vnd.github.v3+json'
655 headers['Content-Length'] = len(data)
656 params = {'name': f}
658 print green("Uploading \'{0}\' to Github".format(f))
659 r = requests.post(url, auth=auth_creds, headers=headers, data=data, params=params, verify=False)
660 _check_request_error(r)
663 @task
664 def upload_ssh(build_dir=None, server_host=None, server_port=None,
665 ssh_user=None, ssh_key=None, dest_dir=None):
667 build_dir = build_dir or env.build_dir
668 server_host = server_host or env.ssh['host']
669 server_port = server_port or env.ssh['port']
670 ssh_user = ssh_user or env.ssh['user']
671 ssh_key = ssh_key or env.ssh['key']
672 dest_dir = dest_dir or env.ssh['dest_dir']
674 env.host_string = server_host + ':' + server_port
675 env.user = ssh_user
676 env.key_filename = ssh_key
678 binaries_dir = os.path.join(build_dir, 'indico', 'dist')
679 for f in os.listdir(binaries_dir):
680 if os.path.isfile(os.path.join(binaries_dir, f)):
681 _putl(os.path.join(binaries_dir, f), dest_dir)
684 @task
685 def _package_release(build_dir, py_versions, system_node):
686 # Build source tarball
687 with settings(system_node=system_node):
688 print green('Generating '), cyan('tarball')
689 tarball(build_dir)
691 # Build binaries (EGG)
692 print green('Generating '), cyan('eggs')
693 egg(py_versions)
696 @task
697 def package_release(py_versions=None, build_dir=None, system_node=False,
698 indico_version=None, upstream=None, tag_name=None,
699 github_auth=None, overwrite=None, ssh_server_host=None,
700 ssh_server_port=None, ssh_user=None, ssh_key=None,
701 ssh_dest_dir=None, no_clean=False, force_clean=False,
702 upload_to=None, build_here=False):
704 Create an Indico release - source and binary distributions
707 DEVELOP_REQUIRES = ['pojson>=0.4', 'termcolor', 'werkzeug', 'nodeenv', 'fabric',
708 'sphinx', 'repoze.sphinx.autointerface']
710 py_versions = py_versions.split('/') if py_versions else env.py_versions
711 upload_to = upload_to.split('/') if upload_to else []
713 build_dir = build_dir or env.build_dir
714 upstream = upstream or env.github['upstream']
716 ssh_server_host = ssh_server_host or env.ssh['host']
717 ssh_server_port = ssh_server_port or env.ssh['port']
718 ssh_user = ssh_user or env.ssh['user']
719 ssh_key = ssh_key or env.ssh['key']
720 ssh_dest_dir = ssh_dest_dir or env.ssh['dest_dir']
722 indico_version = indico_version or 'master'
724 local('mkdir -p {0}'.format(build_dir))
726 _check_pyenv(py_versions)
728 if build_here:
729 _package_release(os.path.dirname(__file__), py_versions, system_node)
730 else:
731 with lcd(build_dir):
732 if os.path.exists(os.path.join(build_dir, 'indico')):
733 print yellow("Repository seems to already exist.")
734 with lcd('indico'):
735 local('git fetch {0}'.format(upstream))
736 local('git reset --hard FETCH_HEAD')
737 if not no_clean:
738 local('git clean -df')
739 else:
740 local('git clone {0}'.format(upstream))
741 with lcd('indico'):
742 print green("Checking out branch \'{0}\'".format(indico_version))
743 local('git checkout {0}'.format(indico_version))
745 _package_release(os.path.join(build_dir, 'indico'), py_versions, system_node)
747 for u in upload_to:
748 if u == 'github':
749 upload_github(build_dir, tag_name, github_auth, overwrite, indico_version)
750 elif u == 'ssh':
751 upload_ssh(build_dir, ssh_server_host, ssh_server_port, ssh_user, ssh_key, ssh_dest_dir)
753 if not build_here and force_clean:
754 cleanup(build_dir, force=True)