Files
gosick/lib/homesick.rb
2010-03-10 10:24:25 -05:00

131 lines
3.2 KiB
Ruby

require 'thor'
class Homesick < Thor
include Thor::Actions
# Hack in support for diffing symlinks
class Shell < Thor::Shell::Color
def show_diff(destination, content)
destination = Pathname.new(destination)
if destination.symlink?
say "+ #{content.expand_path}", :green, true
say "- #{destination.readlink}", :red, true
else
super
end
end
end
def initialize(args=[], options={}, config={})
super
self.shell = Homesick::Shell.new
end
GIT_URI_PATTERN = /^git:\/\//
GITHUB_NAME_REPO_PATTERN = /([A-Za-z_-]+)\/([A-Za-z_-]+)/
desc "clone URI", "Clone +uri+ as a castle for homesick"
def clone(uri)
empty_directory repos_dir, :verbose => false
inside repos_dir do
if uri =~ GIT_URI_PATTERN
git_clone uri
elsif uri =~ GITHUB_NAME_REPO_PATTERN
git_clone "git://github.com/#{$1}/#{$2}.git", "#{$1}_#{$2}"
end
end
end
desc "link NAME", "Symlinks all dotfiles from the specified castle"
def link(home)
inside castle_dir(home) do
files = Pathname.glob('.*')[2..-1] # skip . and .., ie the first two
files.each do |path|
absolute_path = path.expand_path
inside user_dir do
adjusted_path = (user_dir + path).basename
symlink absolute_path, adjusted_path
end
end
end
end
desc "list", "List cloned castles"
def list
inside repos_dir do
Pathname.glob('*') do |home|
puts home
end
end
end
no_tasks do
# class method, so it's convenient to stub out during tests
def self.user_dir
@user_dir ||= Pathname.new('~').expand_path
end
def self.repos_dir
@repos_dir ||= Pathname.new('~/.homesick/repos').expand_path
end
def repos_dir
self.class.repos_dir
end
end
protected
# TODO move this to be more like thor's template, empty_directory, etc
def git_clone(repo, config = {})
config ||= {}
destination = config[:destination] || begin
repo =~ /([^\/]+)\.git$/
$1
end
destination = Pathname.new(destination) unless destination.kind_of?(Pathname)
if ! destination.directory?
say_status 'git clone', "#{repo} to #{destination.expand_path}", :green if config.fetch(:verbose, true)
system "git clone #{repo} #{destination}" unless options[:pretend]
else
say_status :exist, destination.expand_path, :blue
end
end
def symlink(source, destination, config = {})
destination = Pathname.new(destination)
if destination.symlink?
if destination.readlink == source
say_status :identical, destination.expand_path, :blue
else
say_status :conflict, "#{destination} exists and points to #{destination.readlink}", :red
if shell.file_collision(destination) { source }
system "ln -sf #{source} #{destination}"
end
end
else
say_status :symlink, "#{source.expand_path} to #{destination.expand_path}", :green
system "ln -s #{source} #{destination}"
end
end
def user_dir
self.class.user_dir
end
def castle_dir(name)
repos_dir.join(name, 'home')
end
end