Florian Wagner avatar Florian Wagner committed 3acf2f3

Refactor just about everything and move to Mercurial.

Comments (0)

Files changed (7)

+\.pyc$
+^build$

fabric/contrib/wagnerflo/__init__.py

+# -*- coding: utf-8 -*-
+
+"""
+Copyright (c) 2010, Florian Wagner <florian@wagner-flo.net>.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""
+
+__all__ = ['sync', 'tar', 'remote_chmod']
+
+from . import remote
+from . import compat
+from . import walk
+from . import vcs
+
+from fabric.network import needs_host
+from fabric.state import env,connections,output
+
+import os
+import os.path as path
+import stat
+import errno
+import tarfile
+
+def put_link(ftp, local_path, remote_path, mode):
+    dest = os.readlink(local_path)
+    try:
+        if (remote.islink(ftp, remote_path) and
+            dest == ftp.readlink(remote_path)):
+            return
+
+        remote.rmtree(ftp, remote_path)
+
+    except IOError, e:
+        if e.errno != errno.ENOENT:
+            raise e
+
+    if output.running:
+        print("[%s] symlink: %s -> %s" % (env.host_string, remote_path, dest))
+
+    ftp.symlink(dest, remote_path)
+
+def put_file(ftp, local_path, remote_path, mode):
+    try:
+        if remote.islink(ftp, remote_path) or remote.isdir(ftp, remote_path):
+            remote.rmtree(ftp, remote_path)
+
+        upload = ftp.open(remote_path).read() != open(local_path).read()
+
+    except IOError, e:
+        if e.errno != errno.ENOENT:
+            raise e
+
+        upload = True
+
+    if upload:
+        if output.running:
+            print("[%s] put: %s -> %s" % (
+                    env.host_string, local_path, remote_path))
+
+        ftp.put(local_path, remote_path)
+
+    remote.chmod(ftp, remote_path, mode)
+
+def put_dir(ftp, local_path, remote_path, mode):
+    try:
+        if remote.isdir(ftp, remote_path):
+            remote.chmod(ftp, remote_path, mode)
+            return
+
+        remote.rmtree(ftp, remote_path)
+
+    except IOError, e:
+        if e.errno != errno.ENOENT:
+            raise e
+
+    if output.running:
+        print("[%s] mkdir: %s" % (env.host_string, remote_path))
+
+    ftp.mkdir(remote_path)
+    remote.chmod(ftp, remote_path, mode)
+
+def get_mode(realpath, relpath, modes, defaultmode):
+    if relpath in modes:
+        return stat.S_IMODE(modes[relpath])
+
+    if defaultmode is not None:
+        return stat.S_IMODE(defaultmode)
+
+    return stat.S_IMODE(os.lstat(realpath).st_mode)
+
+
+@needs_host
+def sync(remote_path=None, local_path=None,
+         ignores=[], ignore_vcs=True,
+         modes={}, dirmode=None, filemode=None):
+
+    # set default if none is given for these values
+    if local_path is None:
+        local_path = compat.relpath(path.dirname(env.real_fabfile))
+
+    if remote_path is None:
+        remote_path = env.get('cwd')
+
+    if not remote_path:
+        raise ValueError('Either remote_path argument of sync or use of cd'
+                         'context manager required.')
+
+    # try to find a repository
+    (vcs_ignored,
+     vcs_lock,
+     vcs_unlock) = vcs.find_repos(local_path, ignore_vcs)
+
+    # prepare functions
+    ignored = lambda real,rel: rel in ignores or vcs_ignored(real, rel)
+    dmode = lambda real,rel: get_mode(real, rel, modes, dirmode)
+    fmode = lambda real,rel: get_mode(real, rel, modes, filemode)
+
+    # last preparation step: open a sftp connection
+    ftp = connections[env.host_string].open_sftp()
+
+    try:
+        vcs_lock()
+
+        local_walk = walk.local(local_path, ignored)
+        local_dispatch = {
+            'fil': put_file,
+            'lnk': put_link,
+            'dir': put_dir,
+        }
+
+        for tpe,realpath,relpath in local_walk:
+            if tpe == 'dir':
+                local_walk.send(True)
+
+            local_dispatch[tpe](
+                ftp,
+                realpath,
+                compat.dotjoin(remote_path, relpath),
+                fmode(realpath, relpath))
+
+        remote_walk = walk.remote(ftp, remote_path, ignored)
+
+        for tpe,realpath,relpath in remote_walk:
+            if path.lexists(compat.dotjoin(local_path, relpath)):
+                if tpe == 'dir':
+                    remote_walk.send(True)
+
+                continue
+
+            remote.rmtree(ftp, realpath)
+
+            if tpe == 'dir':
+                remote_walk.send(False)
+
+    finally:
+        vcs_unlock()
+
+def tar(filename=None, local_path=None, fileobj=None, compress='bz2',
+        ignores=[], ignore_vcs=True,
+        modes={}, dirmode=None, filemode=None):
+
+    # set default if none is given for these values
+    if local_path is None:
+        local_path = compat.relpath(path.dirname(env.real_fabfile))
+
+    filename = path.abspath(filename)
+
+    # try to find a repository
+    (vcs_ignored,
+     vcs_lock,
+     vcs_unlock) = vcs.find_repos(local_path, ignore_vcs)
+
+    # prepare functions
+    ignored = lambda real,rel: rel in ignores or vcs_ignored(real, rel)
+    dmode = lambda real,rel: get_mode(real, rel, modes, dirmode)
+    fmode = lambda real,rel: get_mode(real, rel, modes, filemode)
+
+    try:
+        vcs_lock()
+
+        local_walk = walk.local(local_path, ignored)
+        tf = tarfile.open(
+            name=filename, fileobj=fileobj,
+            mode='w|' + (compress if compress else ''))
+
+        for tpe,realpath,relpath in local_walk:
+            if path.abspath(realpath) == filename:
+                continue
+
+            tinfo = tf.gettarinfo(realpath)
+            fileobj = None
+
+            if tpe == 'fil':
+                tinfo.mode = fmode(realpath, relpath)
+                fileobj = file(realpath, 'rb')
+
+            if tpe == 'dir':
+                tinfo.mode = dmode(realpath, relpath)
+                local_walk.send(True)
+
+            tf.addfile(tinfo, fileobj)
+
+        tf.close()
+
+    finally:
+        vcs_unlock()
+
+@needs_host
+def chmod(*sets):
+    if len(sets) % 2:
+        raise ValueError('Function chmod requires even number of arguments.')
+
+    ftp = connections[env.host_string].open_sftp()
+
+    for i in range(0, len(sets), 2):
+        rpath = sets[i]
+
+        if not path.isabs(rpath) and env.get('cwd') is not None:
+            rpath = compat.dotjoin(env.get('cwd'), rpath)
+
+        remote.chmod(ftp, rpath, sets[i+1])

fabric/contrib/wagnerflo/compat.py

+# -*- coding: utf-8 -*-
+
+"""
+Copyright (c) 2010, Florian Wagner <florian@wagner-flo.net>.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""
+
+import sys
+import os.path as path
+
+if sys.version_info < (2,6):
+    if 'posix' in sys.builtin_module_names:
+        def relpath(pth, start=path.curdir):
+            """Return a relative version of a path"""
+
+            if not pth:
+                raise ValueError("no path specified")
+
+            start_list = path.abspath(start).split(path.sep)
+            path_list = path.abspath(pth).split(path.sep)
+
+            # Work out how much of the filepath is shared by start and path.
+            i = len(path.commonprefix([start_list, path_list]))
+
+            rel_list = [path.pardir] * (len(start_list)-i) + path_list[i:]
+            if not rel_list:
+                return path.curdir
+            return path.join(*rel_list)
+    else:
+        def relpath(pth, start=path.curdir):
+            """Return a relative version of a path"""
+
+            if not pth:
+                raise ValueError("no path specified")
+
+            start_list = path.abspath(start).split(path.sep)
+            path_list = path.abspath(pth).split(path.sep)
+
+            if start_list[0].lower() != path_list[0].lower():
+                unc_path, rest = path.splitunc(pth)
+                unc_start, rest = path.splitunc(start)
+                if bool(unc_path) ^ bool(unc_start):
+                    raise ValueError(
+                        "Cannot mix UNC and non-UNC paths (%s and %s)"
+                        % (pth, start))
+                else:
+                    raise ValueError(
+                        "path is on drive %s, start on drive %s"
+                        % (path_list[0], start_list[0]))
+
+            # Work out how much of the filepath is shared by start and path.
+            for i in range(min(len(start_list), len(path_list))):
+                if start_list[i].lower() != path_list[i].lower():
+                    break
+            else:
+                i += 1
+
+            rel_list = [path.pardir] * (len(start_list)-i) + path_list[i:]
+            if not rel_list:
+                return path.curdir
+            return path.join(*rel_list)
+else:
+    relpath = path.relpath
+
+def dotjoin(*items):
+    return path.join(*filter(lambda item: item != '.', items))

fabric/contrib/wagnerflo/remote.py

+# -*- coding: utf-8 -*-
+
+"""
+Copyright (c) 2010, Florian Wagner <florian@wagner-flo.net>.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""
+
+import fabric.state
+import os.path as path
+import stat
+
+def isdir(ftp, rpath):
+    return stat.S_ISDIR(ftp.lstat(rpath).st_mode)
+
+def islink(ftp, rpath):
+    return stat.S_ISLNK(ftp.lstat(rpath).st_mode)
+
+def chmod(ftp, rpath, mode):
+    if mode == stat.S_IMODE(ftp.stat(rpath).st_mode):
+        return
+
+    if fabric.state.output.running:
+        print("[%s] chmod: %s -> %o" % (
+                fabric.state.env.host_string, rpath, mode))
+
+    ftp.chmod(rpath, mode)
+
+def rmtree(ftp, rpath):
+    stack = [(rpath, ftp.lstat(rpath).st_mode)]
+
+    while stack:
+        rpath,mode = stack[-1]
+
+        if stat.S_ISLNK(mode) or not stat.S_ISDIR(mode):
+            if fabric.state.output.running:
+                print("[%s] unlink: %s" % (
+                        fabric.state.env.host_string, rpath))
+
+            ftp.unlink(rpath)
+            stack.pop()
+
+        else:
+            entries = [(path.join(rpath, attr.filename), attr.st_mode)
+                       for attr in ftp.listdir_attr(rpath)]
+
+            if entries:
+                stack.extend(entries)
+
+            else:
+                if fabric.state.output.running:
+                    print("[%s] rmdir: %s" % (
+                            fabric.state.env.host_string, rpath))
+
+                ftp.rmdir(rpath)
+                stack.pop()

fabric/contrib/wagnerflo/vcs.py

+from .  import compat
+from os import path
+
+def find_repos(local_path, search_for_repos):
+    bzrtree = None
+    hgrepo = None
+
+    if search_for_repos:
+        try:
+            from bzrlib import workingtree
+            from bzrlib import errors as bzrorrs
+
+            bzrtree,bzrel = workingtree.WorkingTree.open_containing(
+                local_path)
+        except:
+            pass
+
+        try:
+            from mercurial import ui as hgui
+            from mercurial import hg
+            from mercurial import error as hgerror
+            from mercurial import cmdutil as hgutil
+
+            hgrepo = hgutil.findrepo(path.abspath(local_path))
+
+            if hgrepo is not None:
+                hgrel = compat.relpath(path.abspath(local_path), hgrepo)
+                hgrepo = hg.repository(hgui.ui(), hgrepo)
+                hgrepo = hgrepo.status(ignored=True)[5]
+
+        except:
+            hgrepo = None
+
+    # depending on which repository type we found we define a
+    # function which handles the vcs specific ignore check and
+    # repository lock and unlock functiony
+    if bzrtree is not None:
+        def vcs_ignored(realpath, relpath):
+            return (
+                path.basename(relpath) in ['.bzr', '.bzrignore'] or
+                bzrtree.is_ignored(
+                    path.normpath(compat.dotjoin(bzrel, relpath))))
+
+        def vcs_lock(): bzrtree.lock_read()
+        def vcs_unlock(): bzrtree.unlock()
+
+    elif hgrepo is not None:
+        def vcs_ignored(realpath, relpath):
+            return compat.dotjoin(hgrel, relpath) in hgrepo
+
+        def vcs_lock(): pass
+        def vcs_unlock(): pass
+
+    else:
+        def vcs_ignored(realpath, relpath): return False
+        def vcs_lock(): pass
+        def vcs_unlock(): pass
+
+    return (vcs_ignored,vcs_lock,vcs_unlock)

fabric/contrib/wagnerflo/walk.py

+import os.path as path
+import os
+import stat
+
+from . import compat
+
+def local(local_path, ignored):
+    for dirpath,dirnames,filenames in os.walk(local_path):
+        relprefix = compat.relpath(dirpath, local_path)
+
+        for filename in filenames:
+            realpath = path.normpath(compat.dotjoin(dirpath, filename))
+            relpath = path.normpath(compat.dotjoin(relprefix, filename))
+
+            if ignored(realpath, relpath):
+                continue
+
+            if path.islink(realpath):
+                yield ('lnk', realpath, relpath)
+            else:
+                yield ('fil', realpath, relpath)
+
+        for dirname in dirnames[:]:
+            realpath = path.normpath(compat.dotjoin(dirpath, dirname))
+            relpath = path.normpath(compat.dotjoin(relprefix, dirname))
+
+            if ignored(realpath, relpath):
+                dirnames.remove(dirname)
+                continue
+
+            if path.islink(realpath):
+                yield ('lnk', realpath, relpath)
+                dirnames.remove(dirname)
+            else:
+                if not (yield ('dir', realpath, relpath)):
+                    dirnames.remove(dirname)
+                yield
+
+def remote(ftp, remote_path, ignored):
+    stack = [remote_path]
+
+    while stack:
+        rpath = stack.pop()
+        relprefix = compat.relpath(rpath, remote_path)
+
+        for attr in ftp.listdir_attr(rpath):
+            realpath = compat.dotjoin(rpath, attr.filename)
+            relpath = compat.dotjoin(relprefix, attr.filename)
+            mode = attr.st_mode
+
+            if ignored(realpath, relpath):
+                continue
+
+            if stat.S_ISLNK(mode):
+                yield ('lnk', realpath, relpath)
+
+            elif stat.S_ISDIR(mode):
+                if (yield ('dir', realpath, relpath)):
+                    stack.append(realpath)
+                yield
+
+            else:
+                yield ('fil', realpath, relpath)
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Copyright (c) 2010, Florian Wagner <florian@wagner-flo.net>.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""
+
+from distutils.core import setup
+
+setup(
+    name = 'fabric-wagnerflo',
+    version = '0.2',
+    description = 'Contributed operations for Fabric.',
+    author = 'Florian Wagner',
+    author_email = 'florian@wagner-flo.net',
+    url = 'https://bitbucket.org/wagnerflo/fabric-wagnerflo/',
+    packages = ['fabric.contrib.wagnerflo'],
+    classifiers = [
+        'Development Status :: 4 - Beta',
+        'Environment :: Plugins',
+        'Intended Audience :: Developers',
+        'Intended Audience :: System Administrators',
+        'License :: OSI Approved :: BSD License',
+        'Operating System :: POSIX',
+        'Programming Language :: Python',
+        'Programming Language :: Python :: 2.5',
+        'Programming Language :: Python :: 2.6',
+        'Programming Language :: Python :: 2.7',
+        'Topic :: Software Development',
+        'Topic :: Software Development :: Build Tools',
+        'Topic :: Software Development :: Libraries',
+        'Topic :: Software Development :: Libraries :: Python Modules',
+        'Topic :: System :: Clustering',
+        'Topic :: System :: Software Distribution',
+        'Topic :: System :: Systems Administration',
+    ],
+)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.