From dd64afd0e3f4ee362471d5e77296eb9109f9e9d6 Mon Sep 17 00:00:00 2001 From: Jon Bernard Date: Fri, 22 Apr 2011 19:25:55 -0400 Subject: Initial commit --- LICENSE | 14 ++++++++ MANIFEST.in | 1 + README.rst | 80 +++++++++++++++++++++++++++++++++++++++++++ bin/dotfiles | 19 ++++++++++ dotfiles/__init__.py | 8 +++++ dotfiles/cli.py | 51 +++++++++++++++++++++++++++ dotfiles/core.py | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 22 ++++++++++++ 8 files changed, 292 insertions(+) create mode 100644 LICENSE create mode 100644 MANIFEST.in create mode 100644 README.rst create mode 100755 bin/dotfiles create mode 100644 dotfiles/__init__.py create mode 100644 dotfiles/cli.py create mode 100644 dotfiles/core.py create mode 100644 setup.py diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..88f69aa --- /dev/null +++ b/LICENSE @@ -0,0 +1,14 @@ +Copyright (C) 2011 Jon Bernard + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..0c73842 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +include README.rst LICENSE diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..6e71871 --- /dev/null +++ b/README.rst @@ -0,0 +1,80 @@ +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. + +Hosting is left to you. Yes, I've seen ``_ and I don't +believe in 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. + +Installation +------------ + +To install dotfiles, simply: :: + + $ pip install dotfiles + +Or, if you absolutely must: :: + + $ easy_install dotfiles + +But, you really shouldn't do that. + +If you want to work with the latest version, you can install it from `the +repository`_:: + + $ git clone https://github.com/jbernard/dotfiles + $ cd dotfiles + $ ./bin/dotfiles --help + +Examples +-------- + +To install your dotfiles on a new machine, you might do this: :: + + $ git clone https://github.com/me/my-dotfiles Dotfiles + $ dotfiles --sync + +To add '~/.vimrc' to your repository: :: + + $ dotfiles --add ~/.vimrc (relative paths work also) + +To make it available to all your hosts: :: + + $ cd ~/Dotfiles + $ git add vimrc + $ git commit -m "Added vimrc, welcome aboard!" + $ git push + +You get the idea. + +License +------- + +GPL License. :: + + Copyright (C) 2011 Jon Bernard + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Contribute +---------- + +If you'd like to contribute, simply fork `the repository`_, commit your changes +and send a pull request. + +.. _`the repository`: https://github.com/jbernard/dotfiles diff --git a/bin/dotfiles b/bin/dotfiles new file mode 100755 index 0000000..1cd5264 --- /dev/null +++ b/bin/dotfiles @@ -0,0 +1,19 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import sys + +# Add project root directory (enable symlink, and trunk execution). +PROJECT_ROOT_DIRECTORY = os.path.abspath( + os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0])))) + +if (os.path.exists(os.path.join(PROJECT_ROOT_DIRECTORY, 'dotfiles')) + and PROJECT_ROOT_DIRECTORY not in sys.path): + sys.path.insert(0, PROJECT_ROOT_DIRECTORY) + os.putenv('PYTHONPATH', PROJECT_ROOT_DIRECTORY) + +import dotfiles + +if __name__ == '__main__': + dotfiles.cli.main() diff --git a/dotfiles/__init__.py b/dotfiles/__init__.py new file mode 100644 index 0000000..cc4a822 --- /dev/null +++ b/dotfiles/__init__.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- + +import cli +from core import * + +__version__ = '0.1.0' + +__all__ = ['cli', 'core'] diff --git a/dotfiles/cli.py b/dotfiles/cli.py new file mode 100644 index 0000000..910dcdc --- /dev/null +++ b/dotfiles/cli.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +import os +from . import core +from optparse import OptionParser, OptionGroup + + +def method_list(object): + return [method for method in dir(object) + if callable(getattr(object, method))] + + +def parse_args(): + parser = OptionParser(usage="Usage: %prog ACTION [OPTION...] [FILE...]") + + parser.set_defaults(repo=os.path.expanduser("~/Dotfiles")) + parser.add_option("-R", "--repo", type="string", dest="repo", + help="set repository location (default is ~/Dotfiles)") + + action_group = OptionGroup(parser, "Actions") + action_group.add_option("-a", "--add", action="store_const", dest="action", + const="add", help="add dotfile(s) to the repository") + action_group.add_option("-c", "--check", action="store_const", + dest="action", const="check", help="check dotfiles repository") + action_group.add_option("-l", "--list", action="store_const", + dest="action", const="list", + help="list currently managed dotfiles") + action_group.add_option("-r", "--remove", action="store_const", + dest="action", const="remove", + help="remove dotfile(s) from the repository") + action_group.add_option("-s", "--sync", action="store_const", + dest="action", const="sync", help="update dotfile symlinks") + parser.add_option_group(action_group) + + (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) + + return (opts, args) + + +def main(): + (opts, args) = parse_args() + getattr(core.Dotfiles(location=opts.repo), opts.action)(files=args) diff --git a/dotfiles/core.py b/dotfiles/core.py new file mode 100644 index 0000000..3a60865 --- /dev/null +++ b/dotfiles/core.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- + +import os +import shutil + + +class Dotfile(object): + + def __init__(self, name, target): + if name.startswith('/'): + self.name = name + else: + self.name = os.path.expanduser('~/.%s' % name.strip('.')) + self.basename = os.path.basename(self.name) + self.target = target.rstrip('/') + self.status = '' + if not os.path.lexists(self.name): + self.status = 'missing' + elif os.path.realpath(self.name) != self.target: + self.status = 'unmanged' + + def sync(self): + if self.status == 'missing': + os.symlink(self.target, self.name) + + def add(self): + if self.status == 'missing': + print "Skipping \"%s\", file not found" % self.basename + return + 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) + + def remove(self): + if self.status != '': + print "Skipping \"%s\", file is %s" % (self.basename, self.status) + return + os.remove(self.name) + shutil.move(self.target, self.name) + + def __str__(self): + return '%-18s %-s' % (self.name.split('/')[-1], self.status) + + +class Dotfiles(object): + + IGNORES = ['.metadata', '.git', '.gitignore'] + + EXTRAS = {'adobe': '/tmp', + 'bzr.log': '/dev/null', + 'macromedia': '/tmp', + 'uml': '/tmp'} + + def __init__(self, location): + self.location = location + self.dotfiles = [] + contents = [x for x in os.listdir(self.location) + if x not in Dotfiles.IGNORES] + for file in contents: + self.dotfiles.append(Dotfile(file, + os.path.join(self.location, file))) + for file in self.EXTRAS.keys(): + self.dotfiles.append(Dotfile(file, self.EXTRAS[file])) + + def list(self, **kwargs): + for dotfile in sorted(self.dotfiles, + key=lambda dotfile: dotfile.name): + if dotfile.status or kwargs.get('verbose', True): + print dotfile + + def check(self, **kwargs): + self.list(verbose=False) + + def sync(self, **kwargs): + for dotfile in self.dotfiles: + dotfile.sync() + + def add(self, **kwargs): + for file in kwargs.get('files', None): + if os.path.basename(file).startswith('.'): + Dotfile(file, + os.path.join(self.location, + os.path.basename(file).strip('.'))).add() + else: + print "Skipping \"%s\", not a dotfile" % file + + def remove(self, **kwargs): + for file in kwargs.get('files', None): + if os.path.basename(file).startswith('.'): + Dotfile(file, + os.path.join(self.location, + os.path.basename(file).strip('.'))).remove() + else: + print "Skipping \"%s\", not a dotfile" % file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..a14a6c6 --- /dev/null +++ b/setup.py @@ -0,0 +1,22 @@ +from distutils.core import setup +import dotfiles + +setup(name='dotfiles', + version=dotfiles.__version__, + description='Easily manage your dotfiles', + long_description=open('README.rst').read(), + author='Jon Bernard', + author_email='jbernard@tuxion.com', + url='https://github.com/jbernard/dotfiles', + packages=['dotfiles'], + license='GPL', + classifiers=( + 'Development Status :: 4 - Beta', + 'Environment :: Console', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: GNU General Public License (GPL)', + 'Natural Language :: English', + 'Programming Language :: Python', + ), + scripts=['bin/dotfiles'], +) -- cgit v1.2.3