From 4afa72a4cdcde4745c6ff5d5f81f2596088d8a99 Mon Sep 17 00:00:00 2001 From: Jon Bernard Date: Wed, 29 Jun 2011 13:41:17 -0400 Subject: Add an option to override unmanaged files during sync --- dotfiles/cli.py | 10 +++++++--- dotfiles/core.py | 15 +++++++++++---- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/dotfiles/cli.py b/dotfiles/cli.py index fadca25..2ce55bb 100644 --- a/dotfiles/cli.py +++ b/dotfiles/cli.py @@ -20,12 +20,15 @@ def parse_args(): parser.set_defaults(ignore=[]) parser.set_defaults(externals={}) - parser.add_option("-C", "--config", type="string", dest="config", - help="set configuration file location (default is ~/.dotfilesrc)") + parser.add_option("-f", "--force", action="store_true", dest="force", + default=False, help="ignore unmanaged dotfiles (use with --sync)") parser.add_option("-R", "--repo", type="string", dest="repo", help="set repository location (default is ~/Dotfiles)") + parser.add_option("-C", "--config", type="string", dest="config", + help="set configuration file location (default is ~/.dotfilesrc)") + action_group = OptionGroup(parser, "Actions") action_group.add_option("-a", "--add", action="store_const", dest="action", @@ -86,4 +89,5 @@ def main(): getattr(core.Dotfiles(location=opts.repo, prefix=opts.prefix, ignore=opts.ignore, - externals=opts.externals), opts.action)(files=args) + externals=opts.externals, + force=opts.force), opts.action)(files=args) diff --git a/dotfiles/core.py b/dotfiles/core.py index b04892f..3a13fd3 100644 --- a/dotfiles/core.py +++ b/dotfiles/core.py @@ -17,11 +17,17 @@ class Dotfile(object): if not os.path.lexists(self.name): self.status = 'missing' elif os.path.realpath(self.name) != self.target: - self.status = 'unmanged' + self.status = 'unmanaged' - def sync(self): + def sync(self, force): if self.status == 'missing': os.symlink(self.target, self.name) + elif self.status == 'unmanaged': + if not force: + print "Skipping \"%s\", use --force to override" % self.basename + return + os.remove(self.name) + os.symlink(self.target, self.name) def add(self): if self.status == 'missing': @@ -47,8 +53,9 @@ class Dotfile(object): class Dotfiles(object): - def __init__(self, location, prefix, ignore, externals): + def __init__(self, location, prefix, ignore, externals, force): self.location = location + self.force = force self.dotfiles = [] contents = [x for x in os.listdir(self.location) if x not in ignore] @@ -69,7 +76,7 @@ class Dotfiles(object): def sync(self, **kwargs): for dotfile in self.dotfiles: - dotfile.sync() + dotfile.sync(self.force) def add(self, **kwargs): for file in kwargs.get('files', None): -- cgit v1.2.3 From 24c2c90a2f166febcce9e79981cec0eb18fcf922 Mon Sep 17 00:00:00 2001 From: Jon Bernard Date: Wed, 29 Jun 2011 13:50:59 -0400 Subject: Add support for an arbitrary prefix By default, dotfiles are stored with no prefix, so ~/.bashrc will link to ~/Dotfiles/bashrc. Some folks, however, are already storing their dotfiles in a repository where they have a prefix. '.' is common, but I've seen one using '_' as well. This feature will allow those people to specify their prefix in the configuration file without renaming all the files in their repository. To do this, a configuration like: [dotfiles] prefix = . in ~/.dotfilesrc is all that is needed. --- dotfiles/cli.py | 49 ++++++++++++++++++++++++++++++++++--------------- dotfiles/core.py | 14 ++++++-------- 2 files changed, 40 insertions(+), 23 deletions(-) diff --git a/dotfiles/cli.py b/dotfiles/cli.py index 2ce55bb..95e3b82 100644 --- a/dotfiles/cli.py +++ b/dotfiles/cli.py @@ -15,8 +15,6 @@ def parse_args(): parser = OptionParser(usage="Usage: %prog ACTION [OPTION...] [FILE...]") parser.set_defaults(config=os.path.expanduser("~/.dotfilesrc")) - parser.set_defaults(repo=os.path.expanduser("~/Dotfiles")) - parser.set_defaults(prefix='') parser.set_defaults(ignore=[]) parser.set_defaults(externals={}) @@ -26,6 +24,9 @@ def parse_args(): parser.add_option("-R", "--repo", type="string", dest="repo", help="set repository location (default is ~/Dotfiles)") + parser.add_option("-p", "--prefix", type="string", dest="prefix", + help="set prefix character (default is None)") + parser.add_option("-C", "--config", type="string", dest="config", help="set configuration file location (default is ~/.dotfilesrc)") @@ -52,14 +53,9 @@ def parse_args(): (opts, args) = parser.parse_args() - if not os.path.exists(opts.repo): - parser.error("Could not find dotfiles repository \"%s\"" % opts.repo) - - if not opts.action: - parser.error("An action is required.") - - if opts.action not in method_list(core.Dotfiles): - parser.error("No such action \"%s\"" % opts.action) + # Skip checking if the repository exists here. The user may have specified + # a command line argument or a configuration file, which will be examined + # next. return (opts, args) @@ -77,14 +73,37 @@ def main(): parser = ConfigParser.SafeConfigParser(config_defaults) if opts.config: - parser.read(opts.config) if 'dotfiles' in parser.sections(): - opts.repo = os.path.expanduser(parser.get('dotfiles', 'repository')) - opts.prefix = parser.get('dotfiles', 'prefix') - opts.ignore = eval(parser.get('dotfiles', 'ignore')) - opts.externals = eval(parser.get('dotfiles', 'externals')) + + if not opts.repo: + if parser.get('dotfiles', 'repository'): + opts.repo = os.path.expanduser(parser.get('dotfiles', 'repository')) + else: + opts.repo = os.path.expanduser("~/Dotfiles") + + if not opts.prefix: + if parser.get('dotfiles', 'prefix'): + opts.prefix = parser.get('dotfiles', 'prefix') + else: + opts.prefix = '' + + if not opts.ignore and parser.get('dotfiles', 'ignore'): + opts.ignore = eval(parser.get('dotfiles', 'ignore')) + + if not opts.externals and parser.get('dotfiles', 'externals'): + opts.externals = eval(parser.get('dotfiles', 'externals')) + + if not os.path.exists(opts.repo): + print "%s\n" % USAGE + print NO_REPO_MESSAGE % opts.repo + exit(-1) + + if not opts.action: + print "%s\n" % USAGE + print "Error: An action is required." + exit(-1) getattr(core.Dotfiles(location=opts.repo, prefix=opts.prefix, diff --git a/dotfiles/core.py b/dotfiles/core.py index 3a13fd3..843f90f 100644 --- a/dotfiles/core.py +++ b/dotfiles/core.py @@ -36,7 +36,6 @@ class Dotfile(object): if self.status == '': print "Skipping \"%s\", already managed" % self.basename return - print "Adding \"%s\"" % self.basename shutil.move(self.name, self.target) os.symlink(self.target, self.name) @@ -55,19 +54,18 @@ class Dotfiles(object): def __init__(self, location, prefix, ignore, externals, force): self.location = location + self.prefix = prefix self.force = force self.dotfiles = [] - contents = [x for x in os.listdir(self.location) - if x not in ignore] + contents = [x for x in os.listdir(self.location) if x not in ignore] for file in contents: - self.dotfiles.append(Dotfile(file, + self.dotfiles.append(Dotfile(file[len(prefix):], os.path.join(self.location, file))) for file in externals.keys(): self.dotfiles.append(Dotfile(file, externals[file])) def list(self, **kwargs): - for dotfile in sorted(self.dotfiles, - key=lambda dotfile: dotfile.name): + for dotfile in sorted(self.dotfiles, key=lambda dotfile: dotfile.name): if dotfile.status or kwargs.get('verbose', True): print dotfile @@ -83,7 +81,7 @@ class Dotfiles(object): if os.path.basename(file).startswith('.'): Dotfile(file, os.path.join(self.location, - os.path.basename(file).strip('.'))).add() + self.prefix + os.path.basename(file).strip('.'))).add() else: print "Skipping \"%s\", not a dotfile" % file @@ -92,6 +90,6 @@ class Dotfiles(object): if os.path.basename(file).startswith('.'): Dotfile(file, os.path.join(self.location, - os.path.basename(file).strip('.'))).remove() + self.prefix + os.path.basename(file).strip('.'))).remove() else: print "Skipping \"%s\", not a dotfile" % file -- cgit v1.2.3 From cd9eb8fafc237ba8bd64549a2cecba7db613611b Mon Sep 17 00:00:00 2001 From: Jon Bernard Date: Wed, 29 Jun 2011 14:02:08 -0400 Subject: Show a helpful message to first-time users --- dotfiles/cli.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/dotfiles/cli.py b/dotfiles/cli.py index 95e3b82..fda0a2e 100644 --- a/dotfiles/cli.py +++ b/dotfiles/cli.py @@ -5,6 +5,27 @@ from . import core import ConfigParser from optparse import OptionParser, OptionGroup +USAGE = "Usage: %prog ACTION [OPTION...] [FILE...]" + +NO_REPO_MESSAGE = """Could not find dotfiles repository \"%s\" + +If this is your first time running dotfiles, you must first create a +repository. By default, dotfiles will look for '~/Dotfiles'. Something like: + + $ mkdir ~/Dotfiles + +is all you need to do. If you don't like the default, you can put your +repository wherever you like. You have two choices once you've created your +repository. You can specify the path to the repository on the command line +using the '-R' flag. Alternatively, you can create a configuration file at +'~/.dotfilesrc' and place the path to your repository in there. The contents +would look like: + + [dotfiles] + repository = ~/.my-dotfiles-repo + +You can see more information by typing 'dotfiles -h'""" + def method_list(object): return [method for method in dir(object) @@ -12,7 +33,7 @@ def method_list(object): def parse_args(): - parser = OptionParser(usage="Usage: %prog ACTION [OPTION...] [FILE...]") + parser = OptionParser(usage=USAGE) parser.set_defaults(config=os.path.expanduser("~/.dotfilesrc")) parser.set_defaults(ignore=[]) -- cgit v1.2.3 From 1f7dcb2cedf4d46547fbd71c970b98252bb32b95 Mon Sep 17 00:00:00 2001 From: Jon Bernard Date: Sun, 3 Jul 2011 09:20:46 -0400 Subject: Document use of prefixes, externals, and ignores --- README.rst | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index f8b9f9d..132ad2b 100644 --- a/README.rst +++ b/README.rst @@ -2,14 +2,33 @@ Dotfile management made easy ============================ ``dotfiles`` is a tool to make managing your dotfile symlinks in ``$HOME`` -easy, allowing you to keep all your dotfiles in a single directory. +easy, allowing you to keep all your dotfiles in a single directory. Hosting is left to you. Yes, I've seen ``_ but I don't -like that model. If you're advanced enough to need dotfile management, then you -probably already know how you want to host them. Using whatever VCS you +like that model. If you're advanced enough to need dotfile management, then +you probably already know how you want to host them. Using whatever VCS you prefer, or even rsync, you can easily distribute your dotfiles repository across multiple hosts. +Interface +--------- + +``-a, --add `` + Add dotfile(s) to the repository. + +``-c, --check`` + Check for missing or unmanged dotfiles. + +``-l, --list`` + List currently managed dotfiles, one per line. + +``-r, --remove `` + Remove dotfile(s) from the repository. + +``-s, --sync`` + Update dotfile symlinks. You can overwrite unmanaged files with ``-f`` or + ``--force``. + Installation ------------ @@ -49,7 +68,7 @@ To make it available to all your hosts: :: $ git commit -m "Added vimrc, welcome aboard!" $ git push -You get the idea. +You get the idea. Type ``dotfiles --help`` to see the available options. Configuration ------------- @@ -67,6 +86,48 @@ example configuration file might look like: :: '.bzr.log': '/dev/null', '.uml': '/tmp'} +Prefixes +-------- + +Dotfiles are stored in the repository with no prefix by default. So, +``~/.bashrc`` will link to ``~/Dotfiles/bashrc``. If your files already have a +prefix, ``.`` is common, but I've also seen ``_``, then you can specify this +in the configuration file and ``dotfiles`` will do the right thing. An example +configuration in ``~/.dotfilesrc`` might look like: :: + + [dotfiles] + prefix = . + +Externals +--------- + +You may want to link some dotfiles to external locations. For example, ``bzr`` +writes debug information to ``~/.bzr.log`` and there is no easy way to disable +it. For that, I link ``~/.bzr.log`` to ``/dev/null``. Since ``/dev/null`` is +not within the repository, this is called an external. You can have as many of +these as you like. The list of externals is specified in the configuration +file: :: + + [dotfiles] + externals = { + '.bzr.log': '/dev/null', + '.adobe': '/tmp', + '.macromedia': '/tmp'} + +Ignores +------- + +If you're using a VCS to manage your repository of dotfiles, you'll want to +tell ``dotfiles`` to ignore VCS-related files. For example, I use ``git``, so +I have the following in my ``~/.dotfilesrc``: :: + + [dotfiles] + ignore = [ + '.git', + '.gitignore'] + +Any file you list in ``ignore`` will be skipped. + License ------- -- cgit v1.2.3 From 2b5cb6b1ede6dd4ff4abada0845241145cbbe97e Mon Sep 17 00:00:00 2001 From: Jon Bernard Date: Sun, 3 Jul 2011 09:22:01 -0400 Subject: Add a bit of documentation to the top-level modules --- dotfiles/cli.py | 7 +++++++ dotfiles/core.py | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/dotfiles/cli.py b/dotfiles/cli.py index fda0a2e..1e2437f 100644 --- a/dotfiles/cli.py +++ b/dotfiles/cli.py @@ -1,10 +1,17 @@ # -*- coding: utf-8 -*- +""" +dotfiles.cli + +This module provides the CLI interface to dotfiles. +""" + import os from . import core import ConfigParser from optparse import OptionParser, OptionGroup + USAGE = "Usage: %prog ACTION [OPTION...] [FILE...]" NO_REPO_MESSAGE = """Could not find dotfiles repository \"%s\" diff --git a/dotfiles/core.py b/dotfiles/core.py index 843f90f..58310d2 100644 --- a/dotfiles/core.py +++ b/dotfiles/core.py @@ -1,5 +1,12 @@ # -*- coding: utf-8 -*- +""" +dotfiles.core +~~~~~~~~~~~~~ + +This module provides the basic functionality of dotfiles. +""" + import os import shutil -- cgit v1.2.3 From 66dd01b9f1d1c6fea4bf4db544045a8aa410b28d Mon Sep 17 00:00:00 2001 From: Jon Bernard Date: Sun, 3 Jul 2011 09:23:31 -0400 Subject: Only import cli from main script --- bin/dotfiles | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/dotfiles b/bin/dotfiles index 1cd5264..5f4e1c9 100755 --- a/bin/dotfiles +++ b/bin/dotfiles @@ -13,7 +13,7 @@ if (os.path.exists(os.path.join(PROJECT_ROOT_DIRECTORY, 'dotfiles')) sys.path.insert(0, PROJECT_ROOT_DIRECTORY) os.putenv('PYTHONPATH', PROJECT_ROOT_DIRECTORY) -import dotfiles +import dotfiles.cli if __name__ == '__main__': dotfiles.cli.main() -- cgit v1.2.3 From fc4a8aa87908585c9913d1ddd027845be48552e5 Mon Sep 17 00:00:00 2001 From: Jon Bernard Date: Sun, 3 Jul 2011 09:26:11 -0400 Subject: Add option to print version number --- dotfiles/__init__.py | 5 ----- dotfiles/cli.py | 8 ++++++++ dotfiles/core.py | 5 +++++ setup.py | 14 +++++++++++--- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/dotfiles/__init__.py b/dotfiles/__init__.py index 0390200..fcb882b 100644 --- a/dotfiles/__init__.py +++ b/dotfiles/__init__.py @@ -1,8 +1,3 @@ # -*- coding: utf-8 -*- -import cli from core import * - -__version__ = '0.2.0' - -__all__ = ['cli', 'core'] diff --git a/dotfiles/cli.py b/dotfiles/cli.py index 1e2437f..6618719 100644 --- a/dotfiles/cli.py +++ b/dotfiles/cli.py @@ -46,6 +46,10 @@ def parse_args(): parser.set_defaults(ignore=[]) parser.set_defaults(externals={}) + parser.add_option("-v", "--version", action="store_true", + dest="show_version", default=False, + help="show version number and exit") + parser.add_option("-f", "--force", action="store_true", dest="force", default=False, help="ignore unmanaged dotfiles (use with --sync)") @@ -92,6 +96,10 @@ def main(): (opts, args) = parse_args() + if opts.show_version: + print 'dotfiles v%s' % core.__version__ + exit(0) + config_defaults = { 'repository': opts.repo, 'prefix': opts.prefix, diff --git a/dotfiles/core.py b/dotfiles/core.py index 58310d2..d8ca5d1 100644 --- a/dotfiles/core.py +++ b/dotfiles/core.py @@ -11,6 +11,11 @@ import os import shutil +__version__ = '0.2.0' +__author__ = "Jon Bernard" +__license__ = "GPL" + + class Dotfile(object): def __init__(self, name, target): diff --git a/setup.py b/setup.py index a14a6c6..f41e668 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,16 @@ -from distutils.core import setup -import dotfiles +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +try: + from setuptools import setup +except ImportError: + from distutils.core import setup + +from dotfiles.core import __version__ + setup(name='dotfiles', - version=dotfiles.__version__, + version=__version__, description='Easily manage your dotfiles', long_description=open('README.rst').read(), author='Jon Bernard', -- cgit v1.2.3 From e2f981dc01ddf33842fea720e9e53c8b62768cf8 Mon Sep 17 00:00:00 2001 From: Jon Bernard Date: Sun, 3 Jul 2011 09:32:43 -0400 Subject: Bump version number for version 0.3.0 release --- dotfiles/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotfiles/core.py b/dotfiles/core.py index d8ca5d1..c2e368a 100644 --- a/dotfiles/core.py +++ b/dotfiles/core.py @@ -11,7 +11,7 @@ import os import shutil -__version__ = '0.2.0' +__version__ = '0.3.0' __author__ = "Jon Bernard" __license__ = "GPL" -- cgit v1.2.3