Example #1
0
    def test_restore_nomaster_nohome(self, tmp_path, caplog):
        home, repo = self.setup_home_repo(tmp_path)

        calc = CalcOps(repo, home, PlainPlugin(tmp_path / '.data'))
        calc.restore({'file': ['cat1', 'cat2']}).apply()

        assert 'unable to find "file" in repo, skipping' in caplog.text
        assert not (home / 'file').is_file()
Example #2
0
    def test_restore_master_nohome(self, tmp_path):
        home, repo = self.setup_home_repo(tmp_path)
        os.makedirs(repo / 'cat1')
        open(repo / 'cat1' / 'file', 'w').close()

        calc = CalcOps(repo, home, PlainPlugin(tmp_path / '.data'))
        calc.restore({'file': ['cat1', 'cat2']}).apply()

        assert (home / 'file').is_file()
        assert (home / 'file').is_symlink()
        assert (home / 'file').samefile(repo / 'cat1' / 'file')
        assert not (repo / 'cat1' / 'file').is_symlink()
Example #3
0
    def test_restore_dangling_home(self, tmp_path):
        home, repo = self.setup_home_repo(tmp_path)
        os.makedirs(repo / 'cat')
        (repo / 'cat' / 'foo').touch()

        (home / 'foo').symlink_to('/non/existent/path')
        assert not (home / 'foo').exists()

        calc = CalcOps(repo, home, PlainPlugin(tmp_path / '.data'))
        calc.restore({'foo': ['cat']}).apply()

        assert (home / 'foo').is_symlink()
        assert (home / 'foo').exists()
Example #4
0
    def test_diff(self, tmp_path):
        home, repo = self.setup_home_repo(tmp_path)

        (home / 'file').touch()
        (home / 'file2').touch()

        calc = CalcOps(repo, home, PlainPlugin(tmp_path / '.data', hard=True))
        calc.update({'file': ['common'], 'file2': ['common']}).apply()
        calc.restore({'file': ['common'], 'file2': ['common']}).apply()

        (home / 'file').write_text('hello world')
        (home / 'file2').unlink()

        assert calc.diff(['common']) == [f'modified {home / "file"}']
Example #5
0
    def test_restore_master_home_noreplace(self, tmp_path, monkeypatch):
        home, repo = self.setup_home_repo(tmp_path)
        os.makedirs(repo / 'cat1')
        open(repo / 'cat1' / 'file', 'w').close()
        open(home / 'file', 'w').close()

        monkeypatch.setattr('builtins.input', lambda p: 'n')

        calc = CalcOps(repo, home, PlainPlugin(tmp_path / '.data'))
        calc.restore({'file': ['cat1', 'cat2']}).apply()

        assert (home / 'file').is_file()
        assert not (home / 'file').is_symlink()
        assert (repo / 'cat1' / 'file').is_file()
        assert not (repo / 'cat1' / 'file').is_symlink()
Example #6
0
    def test_restore_master_linkedhome(self, tmp_path):
        home, repo = self.setup_home_repo(tmp_path)
        os.makedirs(repo / 'cat1')
        open(repo / 'cat1' / 'file', 'w').close()
        os.symlink(repo / 'cat1' / 'file', home / 'file')

        calc = CalcOps(repo, home, PlainPlugin(tmp_path / '.data'))
        fops = calc.restore({'file': ['cat1', 'cat2']})
        assert fops.ops == []
Example #7
0
    def test_update_externallinkedhome_nomaster_noslave(self, tmp_path):
        home, repo = self.setup_home_repo(tmp_path)

        (home / 'foo').touch()
        (home / 'file').symlink_to(home / 'foo')

        calc = CalcOps(repo, home, PlainPlugin(tmp_path / '.data'))
        calc.update({'file': ['cat']}).apply()

        assert (repo / 'cat').is_dir()
        assert (repo / 'cat' / 'file').exists()
        assert not (repo / 'cat' / 'file').is_symlink()

        calc.restore({'file': ['cat']}).apply()

        assert (home / 'file').is_symlink()
        assert (home / 'file').samefile(repo / 'cat' / 'file')
        assert repo in (home / 'file').resolve().parents
        assert (home / 'foo').exists()
        assert not (home / 'foo').is_symlink()
Example #8
0
def main(args=None, cwd=os.getcwd(), home=info.home):
    if args is None:
        args = sys.argv[1:]

    # parse cmd arguments
    args = Arguments(args)
    logging.basicConfig(format='%(message)s ', level=args.verbose_level)
    logging.debug(f'ran with arguments {args}')

    repo = cwd
    flist_fname = os.path.join(repo, 'filelist')

    # run safety checks
    if not safety_checks(repo, home, args.action == Actions.INIT):
        logging.error(f'safety checks failed for {os.getcwd()}, exiting')
        return 1

    # check for init
    if args.action == Actions.INIT:
        init_repo(repo, flist_fname)
        return 0

    # parse filelist
    filelist = Filelist(flist_fname)
    # generate manifest for later cleaning
    manifest = filelist.manifest()
    # activate categories on filelist
    try:
        filelist = filelist.activate(args.categories)
    except RuntimeError:
        return 1

    # set up git interface
    git = Git(repo)

    # set the dotfiles repo
    dotfiles = os.path.join(repo, 'dotfiles')
    logging.debug(f'dotfiles path is {dotfiles}')

    # init plugins
    plugins_data_dir = os.path.join(repo, '.plugins')
    plugins = {
        'plain': PlainPlugin(
            data_dir=os.path.join(plugins_data_dir, 'plain'),
            repo_dir=os.path.join(dotfiles, 'plain'),
            hard=args.hard_mode),
        'encrypt': EncryptPlugin(
            data_dir=os.path.join(plugins_data_dir, 'encrypt'),
            repo_dir=os.path.join(dotfiles, 'encrypt'))
    }

    plugin_dirs = {plugin: os.path.join(dotfiles, plugin) for plugin in
                   plugins}

    if args.action in [Actions.UPDATE, Actions.RESTORE, Actions.CLEAN]:
        clean_ops = []

        # calculate and apply file operations
        for plugin in plugins:
            # filter out filelist paths that use current plugin
            flist = {path: filelist[path]['categories'] for path in filelist if
                     filelist[path]['plugin'] == plugin}
            if not flist:
                continue
            logging.debug(f'active filelist for plugin {plugin}: {flist}')

            plugin_dir = plugin_dirs[plugin]
            calc_ops = CalcOps(plugin_dir, home, plugins[plugin])

            if args.action == Actions.UPDATE:
                calc_ops.update(flist).apply(args.dry_run)
                calc_ops.restore(flist).apply(args.dry_run)
            elif args.action == Actions.RESTORE:
                calc_ops.restore(flist).apply(args.dry_run)
            elif args.action == Actions.CLEAN:
                calc_ops.clean(flist).apply(args.dry_run)

            clean_ops.append(calc_ops.clean_repo(manifest[plugin]))
            plugins[plugin].clean_data(manifest[plugin])

        # execute cleaning ops after everything else
        for clean_op in clean_ops:
            clean_op.apply(args.dry_run)

    elif args.action in [Actions.DIFF, Actions.COMMIT]:
        # calculate and apply git operations
        if args.action == Actions.DIFF:
            print('\n'.join(git.diff(ignore=['.plugins/'])))

            for plugin in plugins:
                calc_ops = CalcOps(plugin_dirs[plugin], home, plugins[plugin])
                diff = calc_ops.diff(args.categories)

                if diff:
                    print(f'\n{plugin}-plugin updates not yet in repo:')
                    print('\n'.join(diff))

        elif args.action == Actions.COMMIT:
            if not git.has_changes():
                logging.warning('no changes detected in repo, not creating '
                                'commit')
                return 0
            git.add()
            msg = git.gen_commit_message(ignore=['.plugins/'])
            git.commit(msg)

            if git.has_remote():
                ans = input('remote for repo detected, push to remote? [Yn] ')
                ans = ans if ans else 'y'
                if ans.lower() == 'y':
                    git.push()
                    logging.info('successfully pushed to git remote')

    elif args.action == Actions.PASSWD:
        logging.debug('attempting to change encryption password')
        repo = os.path.join(dotfiles, 'encrypt')
        if os.path.exists(repo):
            plugins['encrypt'].init_password()
            plugins['encrypt'].change_password(repo=repo)
        else:
            plugins['encrypt'].change_password()

    return 0