sbin/sync/core.py
changeset 12 8895d41be7e2
equal deleted inserted replaced
11:93228a694ce7 12:8895d41be7e2
       
     1 # -*- coding: utf-8 -*-
       
     2 '''
       
     3 Created on Feb 20, 2013
       
     4 
       
     5 @author: ymh
       
     6 '''
       
     7 # from fabric.api import run, local, env, cd, put, prefix, sudo, lcd
       
     8 # from fabric.colors import green
       
     9 # from fabric.context_managers import settings
       
    10 # from fabric.contrib.files import exists, upload_template
       
    11 # from fabric.contrib.project import rsync_project
       
    12 # from fabric.tasks import Task
       
    13 from fabric import Connection
       
    14 import imp
       
    15 import os.path
       
    16 import re
       
    17 import shutil
       
    18 import sys
       
    19 import urllib.parse
       
    20 import requirements
       
    21 
       
    22 
       
    23 # __all__ = ["check_folder_access", "migrate", "collectstatic", "do_relaunch_server",
       
    24 #            "export_version", "do_sync_web", "create_config", "clean_export_folder",
       
    25 #            "sync_install_build", "do_create_virtualenv", "clean_rsync_folder", "rsync_export",
       
    26 #            "do_sync_comp", "get_comp_versions_dict", "SyncComp", "get_src_version", "sync_build",
       
    27 #            "install_build", "do_create_virtualenv_requirement", "build_src"]
       
    28 
       
    29 def get_export_path(env, version):
       
    30     base_path = os.path.join(env.base_export_path,env.export_prefix).rstrip("/")
       
    31     return os.path.expanduser(base_path) + "_%s" % (str(version))
       
    32 
       
    33 def clean_export_folder(path):
       
    34     print("Removing %s" % path)
       
    35     if os.path.isdir(path):
       
    36         shutil.rmtree(path, ignore_errors=True)
       
    37 
       
    38 def do_export_version(c, path, **export_keys):
       
    39     print("Export version %s : %s" % (path,repr(export_keys)))
       
    40 
       
    41     env = c.env
       
    42 
       
    43     for export_key, version in export_keys.items():
       
    44         export_path = os.path.join(path,export_key)
       
    45 
       
    46         repo_url = env.repos[export_key]['repo']
       
    47         url_part = urllib.parse.urlparse(repo_url)
       
    48         if url_part.scheme or url_part.netloc:
       
    49             # this is a remote repo. Let's clone first
       
    50             clone_path = os.path.join(path,'clone',export_keys[export_key])
       
    51             os.makedirs(clone_path)
       
    52 
       
    53             output = c.run('git ls-remote \"%s\"' % repo_url, warn=True)
       
    54             print("OUTPUT %r" % output)
       
    55             scm = "hg" if output.failed else "git"
       
    56             if scm == "hg":
       
    57                 output = c.run("hg clone \"%s\" \"%s\"" % (repo_url,clone_path))
       
    58             else:
       
    59                 c.run("git clone \"%s\" \"%s\"" % (repo_url,clone_path))
       
    60         else:
       
    61             clone_path = repo_url
       
    62 
       
    63         with c.cd(clone_path):
       
    64             # detetct .git or .hg subfolder
       
    65             if os.path.exists(os.path.join(clone_path,".git")):
       
    66                 os.makedirs(export_path)
       
    67                 cmd_str = "git archive \'%s\' | tar -x -C \"%s\""
       
    68             else:
       
    69                 cmd_str = "hg archive -r \'%s\' \"%s\""
       
    70             c.run(cmd_str % (str(version),export_path))
       
    71 
       
    72     print("Export version %s done"%repr(export_keys))
       
    73 
       
    74 def launch_setup_command(c, command_array, path):
       
    75     f = None
       
    76     sys.path.append(path)
       
    77     current_path = os.getcwd()
       
    78     try:
       
    79         os.chdir(path)
       
    80         try:
       
    81             f, pathname, description = imp.find_module("setup", [path])
       
    82             print("launch_setup_command at %s : found setup" % path)
       
    83             setup_mod = imp.load_module("setup", f, pathname, description)
       
    84             print("launch_setup_command at %s : setup loaded" % path)
       
    85         except:
       
    86             e = sys.exc_info()[0]
       
    87             print("Error launching commands %s : %s" % (path, str(e)))
       
    88             raise
       
    89         finally:
       
    90             if f:
       
    91                 f.close()
       
    92 
       
    93         return setup_mod.launch_setup("setup.py", command_array)
       
    94     finally:
       
    95         os.chdir(current_path)
       
    96 
       
    97 
       
    98 def get_src_dependencies(c, pkg_name, path):
       
    99     print("Get source dependencies at %s" % path)
       
   100     launch_setup_command(c, ['egg_info'], path)
       
   101     egg_requirement_file = os.path.join(path, "%s.egg-info" % pkg_name, "requires.txt")
       
   102     res = []
       
   103     with open(egg_requirement_file) as f:
       
   104         for req in requirements.parse(f):
       
   105             if req.name in c.env['repos']:
       
   106                 r_version = req.specs[0][1] if req.specs else 'tip'
       
   107                 res.append((req.name, r_version))
       
   108     print("Build source dist at %s done : %r" % (path, res))
       
   109     return res
       
   110 
       
   111 
       
   112 def get_remote_env(c, remotepath, remotevirtualenvpath, application_module, settings_key, settings_module=''):
       
   113     if not settings_module:
       
   114         settings_module = '%s.%s' % (application_module, 'settings')
       
   115     activate_path = os.path.join(remotevirtualenvpath, "bin/activate")
       
   116 
       
   117     env = c.env
       
   118     with Connection(env['hosts'][0]) as rconnection:
       
   119         with rconnection.prefix("echo $SHELL && . \"%s\"" % os.path.join(remotevirtualenvpath, "bin/activate")), rconnection.prefix("export PYTHONPATH=\"%s\"" % remotepath):
       
   120             return rconnection.run("DJANGO_SETTINGS_MODULE=%s python -c 'import django.conf;print(django.conf.settings.%s)'" % (settings_module, settings_key)).stdout
       
   121 
       
   122 
       
   123 # def rsync_export(path, remotepath, filters):
       
   124 #     print("Rsync %s to %s",(path,remotepath))
       
   125 
       
   126 #     filter_option_str = "--progress --stats"
       
   127 #     if filters:
       
   128 #         filter_option_str += " " + " ".join(["--filter \"%s\"" % (f) for f in filters])
       
   129 
       
   130 #     run("mkdir -p \"%s\"" % remotepath)
       
   131 #     rsync_project(remotepath, local_dir=path, extra_opts=filter_option_str, delete=True)
       
   132 #     print("Rsync %s to %s done",(path,remotepath))
       
   133 
       
   134 # def clean_rsync_folder(remotepath):
       
   135 #     print("clean rsync folder %s" % remotepath)
       
   136 #     run("rm -fr \"%s\"" % remotepath)
       
   137 
       
   138 def build_src(c, path):
       
   139     print("Build source dist at %s" % path)
       
   140     launch_setup_command(c, ['sdist'], path)
       
   141     print("Build source dist at %s done" % path)
       
   142 
       
   143 
       
   144 def get_src_version(c, key, path):
       
   145 
       
   146     print("get src version for %s at %s" % (key,path))
       
   147 
       
   148     env = c.env
       
   149 
       
   150     mod_name = env.repos[key].get('module', key) or key
       
   151 
       
   152     f = None
       
   153     sys.path.append(path)
       
   154     current_path = os.getcwd()
       
   155     os.chdir(path)
       
   156     try:
       
   157         f, pathname, description = imp.find_module(mod_name, [path])
       
   158         src_mod = imp.load_module(mod_name, f, pathname, description)
       
   159     except:
       
   160         src_mod = None
       
   161         print("Could not import module, trying to parse")
       
   162     finally:
       
   163         os.chdir(current_path)
       
   164         if f:
       
   165             f.close()
       
   166     version = None
       
   167     if src_mod is None:
       
   168         with open(os.path.join(path,mod_name,"__init__.py"),'r') as init_file:
       
   169             for line in init_file:
       
   170                 m = re.search('VERSION\s+=\s+\((.+)\)', line, re.I)
       
   171                 if m:
       
   172                     version = tuple([re.sub('[\s\"\']','', item) for item in m.group(1).split(',')])
       
   173                     break
       
   174     elif hasattr(src_mod, "VERSION"):
       
   175         version = src_mod.VERSION
       
   176     elif hasattr(src_mod, "__version__"):
       
   177         version = src_mod.__version__
       
   178 
       
   179     print("VERSION : %s" % repr(version))
       
   180 
       
   181     if version is None:
       
   182         version = ""
       
   183 
       
   184     if not isinstance(version, str):
       
   185         if src_mod and hasattr(src_mod, "get_version"):
       
   186             version_str = src_mod.get_version()
       
   187         elif isinstance(version, tuple):
       
   188             #convert num
       
   189             version_str = get_version([int(s) if s.isdigit() else s for s in version])
       
   190         else:
       
   191             version_str = str(version)
       
   192     else:
       
   193         version_str = version
       
   194 
       
   195     print("VERSION str : %s" % repr(version_str))
       
   196     return (version, version_str)
       
   197 
       
   198 
       
   199 def sync_build(c, path):
       
   200     print("Sync build %s" % path)
       
   201     env = c.env
       
   202     with Connection(env['hosts'][0]) as host_connection:
       
   203         with host_connection.cd(env.remote_path['build_export']):
       
   204             filename = os.path.basename(path)
       
   205             res_trans = host_connection.put(path, os.path.join(env.remote_path['build_export'], filename))
       
   206             print("Sync build %s to %s" % (path,res_trans.remote))
       
   207             return res_trans
       
   208 
       
   209 
       
   210 def collectstatic(c, remotepath, remotevirtualenvpath, platform_web_module, module_settings="", admin_cmd="python manage.py"):
       
   211     print("Collect static in %s with %s" % (remotepath, remotevirtualenvpath))
       
   212     remotestaticsitepath = get_remote_env(c, remotepath, remotevirtualenvpath, platform_web_module, "STATIC_ROOT", c.env.settings)
       
   213     activate_path = os.path.join(remotevirtualenvpath, "bin/activate")
       
   214     with Connection(c.env['hosts'][0]) as rconnection:
       
   215         with rconnection.prefix("source \"%s\"" % activate_path), rconnection.prefix("export PYTHONPATH=\"%s\"" % remotepath), rconnection.cd(remotepath):
       
   216             #remove old files optio -c of collect static fail !
       
   217             rconnection.run("rm -fr \"%s\"/*" % (remotestaticsitepath))
       
   218             rconnection.run("%s collectstatic --noinput %s" % (admin_cmd, "--settings="+module_settings if module_settings else ""))
       
   219 
       
   220 
       
   221 def migrate(c, remotepath, remotevirtualenvpath, module_settings="", admin_cmd="python manage.py"):
       
   222     activate_path = os.path.join(remotevirtualenvpath, "bin/activate")
       
   223     with Connection(c.env['hosts'][0]) as rconnection:
       
   224         with rconnection.prefix("source \"%s\"" % activate_path), rconnection.prefix("export PYTHONPATH=\"%s\"" % remotepath), rconnection.cd(remotepath):
       
   225             rconnection.run("%s migrate --noinput %s" % (admin_cmd, "--settings="+module_settings if module_settings else ""))
       
   226 
       
   227 
       
   228 def export_version(c, **kwargs):
       
   229     print("export version %s" % (repr(kwargs)))
       
   230 
       
   231     export_path = kwargs.get('path', None)
       
   232 
       
   233     if not export_path:
       
   234         export_path = get_export_path(c.env, "_".join(["%s_%s" % (k,v) for k,v in kwargs.items()]))
       
   235 
       
   236     clean_export_folder(export_path)
       
   237 
       
   238     do_export_version(c, export_path,**kwargs)
       
   239 
       
   240     return export_path
       
   241 
       
   242 def do_create_virtualenv(c, remote_venv_export_path, remotevirtualenvpath):
       
   243     print("Create virtualenv export_path : %s - remote venvpath : %s" % (remote_venv_export_path, remotevirtualenvpath))
       
   244     env = c.env
       
   245     activate_path = os.path.join(remotevirtualenvpath, "bin/activate")
       
   246     if env.get('remote_baseline_venv'):
       
   247         prefix_str = "source \"%s\"" % os.path.join(env.get('remote_baseline_venv'), "bin/activate")
       
   248     else:
       
   249         prefix_str = "echo"
       
   250     with Connection(env['hosts'][0]) as rconnection:
       
   251         rconnection.run("rm -fr \"%s\"" % remotevirtualenvpath, warn=True)
       
   252         run("mkdir -p \"%s\"" % remotevirtualenvpath)
       
   253         with rconnection.prefix(prefix_str), rconnection.cd(os.path.join(remote_venv_export_path,"virtualenv","web")):
       
   254             rconnection.run("python create_python_env.py")
       
   255             rconnection.run("python project-boot.py \"%s\"" % remotevirtualenvpath)
       
   256         with rconnection.prefix("source \"%s\"" % activate_path):
       
   257             rconnection.run("pip install --no-cache-dir -r \"%s\"" % os.path.join(remote_venv_export_path,"virtualenv","web","res","srvr_requirements.txt"))
       
   258 
       
   259 def do_create_virtualenv_requirement(c, remote_venv_requirement_path, remotevirtualenvpath, python_version = "2"):
       
   260     print("Create virtualenv export_path : %s - remote venvpath : %s" % (remote_venv_requirement_path, remotevirtualenvpath))
       
   261     env = c.env
       
   262     with Connection(env['hosts'][0]) as rconnection:
       
   263         rconnection.run("rm -fr \"%s\"" % remotevirtualenvpath, warn=True)
       
   264         rconnection.run("mkdir -p \"%s\"" % remotevirtualenvpath)
       
   265         # rconnection.run("virtualenv -p `which python%s` %s" % (python_version, remotevirtualenvpath))
       
   266         rconnection.run("python%s -m venv %s" % (python_version, remotevirtualenvpath))
       
   267         with rconnection.prefix("echo $SHELL && . \"%s\"" % os.path.join(remotevirtualenvpath, "bin/activate")):
       
   268             rconnection.run("pip install -r \"%s\"" % remote_venv_requirement_path)
       
   269 
       
   270 
       
   271 def do_relaunch_server(c, do_collectstatic, do_migrate):
       
   272     env = c.env
       
   273 
       
   274     if do_migrate:
       
   275         migrate(c, env.remote_path['src'], env.remote_path['virtualenv'], env.get('settings', ''), env.get('admin_cmd', 'python manage.py'))
       
   276     if do_collectstatic:
       
   277         collectstatic(c, env.remote_path['src'], env.remote_path['virtualenv'], env.platform_web_module, env.get('settings', ''), env.get('admin_cmd', 'python manage.py'))
       
   278 
       
   279     with Connection(env['hosts'][0]) as rconnection:
       
   280         rconnection.sudo(env.web_relaunch_cmd, shell=False)
       
   281