示例#1
0
def repolist(format):
    cfg = RepoListConfig()
    for info in cfg.find_all():
        vars_ = dict(repoid=info.repoid,
                     localpath=info.localrepo.repo_path,
                     canonical=(info.canonicalrepo.repo_path
                                if info.canonicalrepo else ''))
        print(format % vars_)
示例#2
0
def update(identifiers, nopull, only):
    '''
    Performs a `git pull` in each of the repositories registered with
    `homely add`, runs all of their HOMELY.py scripts, and then performs
    automatic cleanup as necessary.

    REPO
        This should be the path to a local dotfiles repository that has already
        been registered using `homely add`. If you specify one or more `REPO`s
        then only the HOMELY.py scripts from those repositories will be run,
        and automatic cleanup will not be performed (automatic cleanup is only
        possible when homely has done an update of all repositories in one go).
        If you do not specify a REPO, all repositories' HOMELY.py scripts will
        be run.

    The --nopull and --only options are useful when you are working on your
    HOMELY.py script - the --nopull option stops you from wasting time checking
    the internet for the same updates on every run, and the --only option
    allows you to execute only the section you are working on.
    '''
    mkcfgdir()
    setallowpull(not nopull)

    cfg = RepoListConfig()
    if len(identifiers):
        updatedict = {}
        for identifier in identifiers:
            repo = cfg.find_by_any(identifier, "ilc")
            if repo is None:
                hint = "Try running %s add /path/to/this/repo first" % CMD
                raise Fatal("Unrecognised repo %s (%s)" % (identifier, hint))
            updatedict[repo.repoid] = repo
        updatelist = updatedict.values()
        cleanup = len(updatelist) == cfg.repo_count()
    else:
        updatelist = list(cfg.find_all())
        cleanup = True
    success = run_update(updatelist,
                         pullfirst=not nopull,
                         only=only,
                         cancleanup=cleanup)
    if not success:
        sys.exit(1)
示例#3
0
def forget(identifier):
    '''
    Tells homely to forget about a dotfiles repository that was previously
    added. You can then run `homely update` to have homely perform automatic
    cleanup of anything that was installed by that dotfiles repo.

    REPO
        This should be the path to a local dotfiles repository that has already
        been registered using `homely add`. You may specify multiple REPOs to
        remove at once.
    '''
    errors = False
    for one in identifier:
        cfg = RepoListConfig()
        info = cfg.find_by_any(one, "ilc")
        if not info:
            warn("No repos matching %r" % one)
            errors = True
            continue

        # update the config ...
        note("Removing record of repo [%s] at %s" %
             (info.shortid(), info.localrepo.repo_path))
        with saveconfig(RepoListConfig()) as cfg:
            cfg.remove_repo(info.repoid)

    # if there were errors, then don't try and do an update
    if errors:
        sys.exit(1)
示例#4
0
def add(repo_path, dest_path):
    '''
    Registers a git repository with homely so that it will run its `HOMELY.py`
    script on each invocation of `homely update`. `homely add` also immediately
    executes a `homely update` so that the dotfiles are installed straight
    away. If the git repository is hosted online, a local clone will be created
    first.

    REPO_PATH
        A path to a local git repository, or the URL for a git repository
        hosted online. If REPO_PATH is a URL, then it should be in a format
        accepted by `git clone`. If REPO_PATH is a URL, you may also specify
        DEST_PATH.
    DEST_PATH
        If REPO_PATH is a URL, then the local clone will be created at
        DEST_PATH. If DEST_PATH is omitted then the path to the local clone
        will be automatically derived from REPO_PATH.
    '''
    mkcfgdir()
    try:
        repo = getrepohandler(repo_path)
    except NotARepo as err:
        echo("ERROR: {}: {}".format(ERR_NOT_A_REPO, err.repo_path))
        sys.exit(1)

    # if the repo isn't on disk yet, we'll need to make a local clone of it
    if repo.isremote:
        localrepo, needpull = addfromremote(repo, dest_path)
    elif dest_path:
        raise UsageError("DEST_PATH is only for repos hosted online")
    else:
        try:
            repoid = repo.getrepoid()
        except RepoHasNoCommitsError as err:
            echo("ERROR: {}".format(ERR_NO_COMMITS))
            sys.exit(1)
        localrepo = RepoInfo(repo, repoid, None)
        needpull = False

    # if we don't have a local repo, then there is nothing more to do
    if not localrepo:
        return

    # remember this new local repo
    with saveconfig(RepoListConfig()) as cfg:
        cfg.add_repo(localrepo)
    success = run_update([localrepo], pullfirst=needpull, cancleanup=True)
    if not success:
        sys.exit(1)
示例#5
0
文件: _ui.py 项目: e3krisztian/homely
def addfromremote(repo, dest_path):
    assert isinstance(repo, Repo) and repo.isremote

    rlist = RepoListConfig()

    if repo.iscanonical:
        # abort if we have already added this repo before
        match = rlist.find_by_canonical(repo.repo_path)
        if match:
            note("Repo [%s] from %s has already been added" %
                 (match.shortid(), repo.repo_path))
            return match, True

    # figure out where the temporary clone should be moved to after it is
    # created
    if dest_path is None:
        assert repo.suggestedlocal is not None
        dest_path = os.path.join(os.environ["HOME"], repo.suggestedlocal)

    with tmpdir(os.path.basename(dest_path)) as tmp:
        # clone the repo to a temporary location
        note("HOME: %s" % os.environ["HOME"])
        note("tmp:  %s" % tmp)
        note("Cloning %s to tmp:%s" % (repo.repo_path, tmp))
        repo.clonetopath(tmp)

        # find out the first commit id
        localrepo = repo.frompath(tmp)
        assert isinstance(localrepo, repo.__class__)
        tmprepoid = localrepo.getrepoid()

        # if we recognise the repo, record the canonical path onto
        # the repo info so we don't have to download it again
        match = rlist.find_by_id(tmprepoid)
        if match is not None:
            note("Repo [%s] from has already been added" %
                 match.localrepo.shortid(tmprepoid))
            if repo.iscanonical:
                match.canonicalrepo = repo
                rlist.add_repo(match)
                rlist.writejson()
            return match, True

        if os.path.exists(dest_path):
            destrepo = localrepo.frompath(dest_path)

            if not destrepo:
                # TODO: use a different type of exception here
                raise Exception("DEST_PATH %s already exists" % dest_path)

            # check that the repo there is the right repo
            destid = destrepo.getrepoid()
            if destid != tmprepoid:
                # TODO: this should be a different type of exception
                raise Exception("Repo with id [%s] already exists at %s" %
                                (destrepo.getrepoid(False), dest_path))

            # we can use the repo that already exists at dest_path
            note("Using the existing repo [%s] at %s" %
                 (destrepo.shortid(destid), dest_path))
            return RepoInfo(destrepo, destid), True

        # move our temporary clone into the final destination
        os.rename(tmp, dest_path)

    destrepo = localrepo.frompath(dest_path)
    assert destrepo is not None
    info = RepoInfo(
        destrepo,
        destrepo.getrepoid(),
        repo if repo.iscanonical else None,
    )
    return info, False
示例#6
0
文件: _ui.py 项目: e3krisztian/homely
def run_update(infos, pullfirst, only=None, cancleanup=None):
    from homely._engine2 import initengine, resetengine, setrepoinfo

    assert cancleanup is not None
    if only is None:
        only = []
    elif len(only):
        assert len(infos) <= 1
    global _CURRENT_REPO
    errors = False

    if not _writepidfile():
        return False

    isfullupdate = False
    if (cancleanup and (not len(only))
            and len(infos) == RepoListConfig().repo_count()):
        isfullupdate = True

        # remove the fail file if it is still hanging around
        if os.path.exists(FAILFILE):
            os.unlink(FAILFILE)

    try:
        # write the section file with the current section name
        _write(SECTIONFILE, "<preparing>")

        engine = initengine()

        for info in infos:
            setrepoinfo(info)
            assert isinstance(info, RepoInfo)
            _CURRENT_REPO = info
            localrepo = info.localrepo
            with entersection(os.path.basename(localrepo.repo_path)), \
                    head("Updating from {} [{}]".format(
                        localrepo.repo_path, info.shortid())):
                if pullfirst:
                    with note("Pulling changes for {}".format(
                            localrepo.repo_path)):
                        if localrepo.isdirty():
                            dirty("Aborting - uncommitted changes")
                        else:
                            try:
                                localrepo.pullchanges()
                            except ConnectionError:
                                noconn("Could not connect to remote server")

                # make sure the HOMELY.py script exists
                pyscript = os.path.join(localrepo.repo_path, 'HOMELY.py')
                if not os.path.exists(pyscript):
                    warn("{}: {}".format(ERR_NO_SCRIPT, localrepo.repo_path))
                    continue

                if len(only):
                    engine.onlysections(only)

                try:
                    homely._utils._loadmodule('HOMELY', pyscript)
                except Exception as err:
                    import traceback
                    tb = traceback.format_exc()
                    warn(str(err))
                    for line in tb.split('\n'):
                        warn(line)

                # Remove 'HOMELY' from sys modules so it is ready for the next
                # run. Note that if the call to load_module() failed then the
                # HOMELY module might not be present.
                sys.modules.pop('HOMELY', None)

        setrepoinfo(None)

        if isfullupdate:
            if _NOTECOUNT.get('warn'):
                note("Automatic Cleanup not possible due to previous warnings")
            else:
                _write(SECTIONFILE, "<cleaning up>")
                engine.cleanup(engine.WARN)

        resetengine()
        os.unlink(SECTIONFILE)
    except KeyboardInterrupt:
        errors = True
        raise
    except Exception as err:
        warn(str(err))
        import traceback
        tb = traceback.format_exc()
        for line in tb.split('\n'):
            warn(line)
        errors = True
    finally:
        warncount = _NOTECOUNT.get('warn')
        noconncount = _NOTECOUNT.get('noconn')
        dirtycount = _NOTECOUNT.get('dirty')
        if isfullupdate:
            if errors or warncount:
                # touch the FAILFILE if there were errors or warnings
                with open(FAILFILE, 'w') as f:
                    pass
            elif noconncount:
                with open(FAILFILE, 'w') as f:
                    f.write(UpdateStatus.NOCONN)
            elif dirtycount:
                with open(FAILFILE, 'w') as f:
                    f.write(UpdateStatus.DIRTY)
            _write(TIMEFILE, time.strftime("%H:%M"))
        if os.path.exists(RUNFILE):
            os.unlink(RUNFILE)

    return not (errors or warncount or noconncount or dirtycount)
示例#7
0
def autoupdate(**kwargs):
    options = ('pause', 'unpause', 'outfile', 'daemon', 'clear')
    action = None
    for name in options:
        if kwargs[name]:
            if action is not None:
                raise UsageError("--%s and --%s options cannot be combined" %
                                 (action, name))
            action = name

    if action is None:
        raise UsageError("Either %s must be used" %
                         (" or ".join("--{}".format(o) for o in options)))

    mkcfgdir()
    if action == "pause":
        with open(PAUSEFILE, 'w'):
            pass
        return

    if action == "unpause":
        if os.path.exists(PAUSEFILE):
            os.unlink(PAUSEFILE)
        return

    if action == "clear":
        if os.path.exists(FAILFILE):
            os.unlink(FAILFILE)
        return

    if action == "outfile":
        print(OUTFILE)
        return

    # is an update necessary?
    assert action == "daemon"

    # check if we're allowed to start an update
    status, mtime, _ = getstatus()
    if status == UpdateStatus.FAILED:
        print("Can't start daemon - previous update failed")
        sys.exit(1)
    if status == UpdateStatus.PAUSED:
        print("Can't start daemon - updates are paused")
        sys.exit(1)
    if status == UpdateStatus.RUNNING:
        print("Can't start daemon - an update is already running")
        sys.exit(1)

    # abort the update if it hasn't been long enough
    interval = 20 * 60 * 60
    if mtime is not None and (time.time() - mtime) < interval:
        print("Can't start daemon - too soon to start another update")
        sys.exit(1)

    assert status in (UpdateStatus.OK, UpdateStatus.NEVER, UpdateStatus.NOCONN)

    oldcwd = os.getcwd()
    import daemon
    with daemon.DaemonContext(), open(OUTFILE, 'w') as f:
        try:
            from homely._ui import setstreams
            setstreams(f, f)

            # we need to chdir back to the old working directory or  imports
            # will be broken!
            if sys.version_info[0] < 3:
                os.chdir(oldcwd)

            cfg = RepoListConfig()
            run_update(list(cfg.find_all()), pullfirst=True, cancleanup=True)
        except Exception:
            import traceback
            f.write(traceback.format_exc())
            raise
示例#8
0
文件: _ui.py 项目: phodge/homely
def addfromremote(repo, dest_path):
    assert isinstance(repo, Repo) and repo.isremote

    rlist = RepoListConfig()

    if repo.iscanonical:
        # abort if we have already added this repo before
        match = rlist.find_by_canonical(repo.repo_path)
        if match:
            note("Repo [%s] from %s has already been added" %
                 (match.shortid(), repo.repo_path))
            return match, True

    # figure out where the temporary clone should be moved to after it is
    # created
    if dest_path is None:
        assert repo.suggestedlocal is not None
        dest_path = os.path.join(os.environ["HOME"], repo.suggestedlocal)

    with tmpdir(os.path.basename(dest_path)) as tmp:
        # clone the repo to a temporary location
        note("HOME: %s" % os.environ["HOME"])
        note("tmp:  %s" % tmp)
        note("Cloning %s to tmp:%s" % (repo.repo_path, tmp))
        repo.clonetopath(tmp)

        # find out the first commit id
        localrepo = repo.frompath(tmp)
        assert isinstance(localrepo, repo.__class__)
        tmprepoid = localrepo.getrepoid()

        # if we recognise the repo, record the canonical path onto
        # the repo info so we don't have to download it again
        match = rlist.find_by_id(tmprepoid)
        if match is not None:
            note("Repo [%s] from has already been added" %
                 match.localrepo.shortid(tmprepoid))
            if repo.iscanonical:
                match.canonicalrepo = repo
                rlist.add_repo(match)
                rlist.writejson()
            return match, True

        if os.path.exists(dest_path):
            destrepo = localrepo.frompath(dest_path)

            if not destrepo:
                # TODO: use a different type of exception here
                raise Exception("DEST_PATH %s already exists" % dest_path)

            # check that the repo there is the right repo
            destid = destrepo.getrepoid()
            if destid != tmprepoid:
                # TODO: this should be a different type of exception
                raise Exception("Repo with id [%s] already exists at %s" %
                                (destrepo.getrepoid(False), dest_path))

            # we can use the repo that already exists at dest_path
            note("Using the existing repo [%s] at %s" %
                 (destrepo.shortid(destid), dest_path))
            return RepoInfo(destrepo, destid), True

        # move our temporary clone into the final destination
        os.rename(tmp, dest_path)

    destrepo = localrepo.frompath(dest_path)
    assert destrepo is not None
    info = RepoInfo(destrepo,
                    destrepo.getrepoid(),
                    repo if repo.iscanonical else None,
                    )
    return info, False