diff options
-rw-r--r-- | .travis.yml | 15 | ||||
-rw-r--r-- | README.rst | 20 | ||||
-rw-r--r-- | dotfiles/cli.py | 6 | ||||
-rw-r--r-- | dotfiles/core.py | 18 | ||||
-rwxr-xr-x | test_dotfiles.py | 72 |
5 files changed, 116 insertions, 15 deletions
diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..8e6edd7 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +language: python +python: + - "2.5" + - "2.6" + - "2.7" + - "3.2" + - "3.3" + +# command to install dependencies +install: + - "pip install . --use-mirrors" + +# command to run tests +script: + - "python test_dotfiles.py" @@ -1,11 +1,17 @@ Dotfile management made easy ============================ +.. image:: https://pypip.in/v/dotfiles/badge.png + :target: https://pypi.python.org/pypi/dotfiles + +.. image:: https://secure.travis-ci.org/jbernard/dotfiles.png?branch=develop + :target: http://travis-ci.org/jbernard/dotfiles + ``dotfiles`` is a tool to make managing your dotfile symlinks in ``$HOME`` easy, allowing you to keep all your dotfiles in a single directory. -Hosting is up to you. Using whatever VCS you prefer, or even rsync, you can -easily distribute your dotfiles repository across multiple hosts. +Hosting is up to you. You can use a VCS like git, Dropbox, or even rsync to +distribute your dotfiles repository across multiple hosts. The repository can be specified at runtime, so you can manage multiple repositories without hassle. See the Configuration_ section below for further @@ -29,12 +35,14 @@ Interface ``-r, --remove <file...>`` Remove dotfile(s) from the repository. -``-s, --sync`` +``-s, --sync [file...]`` Update dotfile symlinks. You can overwrite colliding files with ``-f`` or - ``--force``. + ``--force``. All dotfiles are assumed if you do not specify any files to + this command. -``-m, --move`` - Move dotfiles repository to another location. +``-m, --move <path>`` + Move dotfiles repository to another location, updating all symlinks in the + process. For all commands you can use the ``--dry-run`` option, which will print actions and won't modify anything on your drive. diff --git a/dotfiles/cli.py b/dotfiles/cli.py index 940495e..c0984d6 100644 --- a/dotfiles/cli.py +++ b/dotfiles/cli.py @@ -177,10 +177,8 @@ def parse_config(config_file): def dispatch(dotfiles, action, force, args): if action in ['list', 'check']: getattr(dotfiles, action)() - elif action in ['add', 'remove']: + elif action in ['add', 'remove', 'sync']: getattr(dotfiles, action)(args) - elif action == 'sync': - dotfiles.sync(force) elif action == 'move': if len(args) > 1: print("Error: Move cannot handle multiple targets.") @@ -212,7 +210,7 @@ def main(): (cli_opts, args) = parse_args() - settings['homedir'] = realpath_expanduser(cli_opts.homedir or + settings['homedir'] = realpath_expanduser(cli_opts.homedir or defaults['homedir']) settings['config_file'] = realpath_expanduser(cli_opts.config_file or defaults['config_file']) diff --git a/dotfiles/core.py b/dotfiles/core.py index 1dfcea7..b2e2d0c 100644 --- a/dotfiles/core.py +++ b/dotfiles/core.py @@ -152,7 +152,7 @@ class Dotfiles(object): self._load_recursive(pkg_path) else: self.dotfiles.append(Dotfile(dotfile[len(self.prefix):], - os.path.join(src_dir, dotfile), dst_dir, + os.path.join(src_dir, dotfile), dst_dir, add_dot=not bool(sub_dir), dry_run=self.dry_run)) # Externals are top-level only @@ -182,12 +182,21 @@ class Dotfiles(object): self.list(verbose=False) - def sync(self, force=False): + def sync(self, files=None, force=False): """Synchronize this repository, creating and updating the necessary symbolic links.""" - for dotfile in self.dotfiles: + # unless a set of files is specified, operate on all files + if not files: + dotfiles = self.dotfiles + else: + files = map(lambda x: os.path.join(self.homedir, x), files) + dotfiles = [x for x in self.dotfiles if x.name in files] + if not dotfiles: + raise Exception("file not found") + + for dotfile in dotfiles: dotfile.sync(force) def add(self, files): @@ -211,6 +220,9 @@ class Dotfiles(object): if pkg_name in self.packages: home = os.path.join(self.homedir, sub_dir) target = self._fqpn(file, pkg_name=pkg_name) + dirname = os.path.dirname(target) + if not os.path.exists(dirname): + os.makedirs(dirname) else: home = self.homedir target = self._fqpn(file) diff --git a/test_dotfiles.py b/test_dotfiles.py index 88ffe67..babe43c 100755 --- a/test_dotfiles.py +++ b/test_dotfiles.py @@ -180,7 +180,7 @@ class DotfilesTestCase(unittest.TestCase): self.assertPathEqual( os.path.join(self.repository, original), os.path.join(self.homedir, symlink)) - + def test_packages(self): """ Test packages. @@ -197,7 +197,7 @@ class DotfilesTestCase(unittest.TestCase): os.makedirs(dirname) touch(path) - # Create Dotiles object + # Create Dotfiles object dotfiles = core.Dotfiles( homedir=self.homedir, repository=self.repository, prefix='', ignore=[], externals={}, packages=['package'], @@ -232,6 +232,74 @@ class DotfilesTestCase(unittest.TestCase): dotfiles.move(self.repository) check_all(files, symlinks) + def test_missing_package(self): + """ + Test a non-existent package. + """ + + package_file = '.package/bar' + + # Create Dotfiles object + dotfiles = core.Dotfiles( + homedir=self.homedir, repository=self.repository, + prefix='', ignore=[], externals={}, packages=['package'], + dry_run=False) + + path = os.path.join(self.homedir, package_file) + dirname = os.path.dirname(path) + if not os.path.exists(dirname): + os.makedirs(dirname) + touch(path) + + dotfiles.add([os.path.join(self.homedir, package_file)]) + + + def test_single_sync(self): + """ + Test syncing a single file in the repo + + The following repo dir exists: + + bashrc + netrc + vimrc + + Syncing only vimrc should have the folowing sync result in home: + + .vimrc -> Dotfiles/vimrc + + """ + + # define the repository contents + repo_files = ( + ('bashrc', False), + ('netrc', False), + ('vimrc', True)) + + # populate the repository + for dotfile, _ in repo_files: + touch(os.path.join(self.repository, dotfile)) + + dotfiles = core.Dotfiles( + homedir=self.homedir, repository=self.repository, + prefix='', ignore=[], externals={}, packages=[], + dry_run=False) + + # sync only certain dotfiles + for dotfile, should_sync in repo_files: + if should_sync: + dotfiles.sync(files=['.%s' % dotfile]) + + # verify home directory contents + for dotfile, should_sync in repo_files: + if should_sync: + self.assertPathEqual( + os.path.join(self.repository, dotfile), + os.path.join(self.homedir, '.%s' % dotfile)) + else: + self.assertFalse(os.path.exists( + os.path.join(self.homedir, dotfile))) + def suite(): suite = unittest.TestLoader().loadTestsFromTestCase(DotfilesTestCase) |