Further abstracted locating castles into Homesick.all_castles which globs as before, but after that rejects any paths that lie in other paths. This way castles that have submodules don't cause extra output in e.g. list.
186 lines
4.8 KiB
Ruby
186 lines
4.8 KiB
Ruby
require 'thor'
|
|
|
|
class Homesick < Thor
|
|
autoload :Shell, 'homesick/shell'
|
|
autoload :Actions, 'homesick/actions'
|
|
|
|
include Thor::Actions
|
|
include Homesick::Actions
|
|
|
|
add_runtime_options!
|
|
|
|
GITHUB_NAME_REPO_PATTERN = /\A([A-Za-z_-]+\/[A-Za-z_-]+)\Z/
|
|
|
|
def initialize(args=[], options={}, config={})
|
|
super
|
|
self.shell = Homesick::Shell.new
|
|
end
|
|
|
|
desc "clone URI", "Clone +uri+ as a castle for homesick"
|
|
def clone(uri)
|
|
inside repos_dir do
|
|
destination = nil
|
|
if File.exist?(uri)
|
|
destination = Pathname.new(uri).basename
|
|
|
|
ln_s uri, destination
|
|
elsif uri =~ GITHUB_NAME_REPO_PATTERN
|
|
destination = Pathname.new($1)
|
|
git_clone "git://github.com/#{$1}.git", :destination => destination
|
|
elsif uri =~ /\/([^\/]*)(\.git)?\Z/
|
|
destination = Pathname.new($1)
|
|
git_clone uri
|
|
elsif uri =~ /[^:]+:([^:]+)(\.git)?\Z/
|
|
destination = Pathname.new($1)
|
|
git_clone uri
|
|
else
|
|
raise "Unknown URI format: #{uri}"
|
|
end
|
|
|
|
if destination.join('.gitmodules').exist?
|
|
inside destination do
|
|
git_submodule_init
|
|
git_submodule_update
|
|
end
|
|
end
|
|
|
|
homesickrc = destination.join('.homesickrc').expand_path
|
|
if homesickrc.exist?
|
|
proceed = shell.yes?("#{uri} has a .homesickrc. Proceed with evaling it? (This could be destructive)")
|
|
if proceed
|
|
shell.say_status "eval", homesickrc
|
|
inside destination do
|
|
eval homesickrc.read, binding, homesickrc.expand_path
|
|
end
|
|
else
|
|
shell.say_status "eval skip", "not evaling #{homesickrc}, #{destination} may need manual configuration", :blue
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
desc "pull NAME", "Update the specified castle"
|
|
method_option :all, :type => :boolean, :default => false, :required => false, :desc => "Update all cloned castles"
|
|
def pull(name="")
|
|
if options[:all]
|
|
inside_each_castle do |castle|
|
|
update_castle castle
|
|
end
|
|
else
|
|
update_castle name
|
|
end
|
|
|
|
end
|
|
|
|
desc "symlink NAME", "Symlinks all dotfiles from the specified castle"
|
|
def symlink(name)
|
|
check_castle_existance(name, "symlink")
|
|
|
|
inside castle_dir(name) do
|
|
files = Pathname.glob('.*').reject{|a| [".",".."].include?(a.to_s)}
|
|
files.each do |path|
|
|
absolute_path = path.expand_path
|
|
|
|
inside home_dir do
|
|
adjusted_path = (home_dir + path).basename
|
|
|
|
ln_s absolute_path, adjusted_path
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
desc "track FILE CASTLE", "add a file to a castle"
|
|
def track(file, castle)
|
|
castle = Pathname.new(castle)
|
|
file = Pathname.new(file)
|
|
check_castle_existance(castle, 'track')
|
|
|
|
absolute_path = file.expand_path
|
|
castle_path = castle_dir(castle)
|
|
mv absolute_path, castle_path
|
|
|
|
inside home_dir do
|
|
absolute_path = castle_dir(castle) + file.basename
|
|
home_path = home_dir + file
|
|
ln_s absolute_path, home_path
|
|
end
|
|
end
|
|
|
|
desc "list", "List cloned castles"
|
|
def list
|
|
inside_each_castle do |castle|
|
|
say_status castle.relative_path_from(repos_dir), `git config remote.origin.url`.chomp, :cyan
|
|
end
|
|
end
|
|
|
|
desc "generate PATH", "generate a homesick-ready git repo at PATH"
|
|
def generate(castle)
|
|
castle = Pathname.new(castle).expand_path
|
|
|
|
github_user = `git config github.user`.chomp
|
|
github_user = nil if github_user == ""
|
|
github_repo = castle.basename
|
|
|
|
empty_directory castle
|
|
inside castle do
|
|
git_init
|
|
if github_user
|
|
url = "git@github.com:#{github_user}/#{github_repo}.git"
|
|
git_remote_add 'origin', url
|
|
end
|
|
|
|
empty_directory "home"
|
|
end
|
|
end
|
|
|
|
|
|
protected
|
|
|
|
def home_dir
|
|
@home_dir ||= Pathname.new(ENV['HOME'] || '~').expand_path
|
|
end
|
|
|
|
def repos_dir
|
|
@repos_dir ||= home_dir.join('.homesick', 'repos').expand_path
|
|
end
|
|
|
|
def castle_dir(name)
|
|
repos_dir.join(name, 'home')
|
|
end
|
|
|
|
def check_castle_existance(name, action)
|
|
unless castle_dir(name).exist?
|
|
say_status :error, "Could not #{action} #{name}, expected #{castle_dir(name)} exist and contain dotfiles", :red
|
|
|
|
exit(1)
|
|
end
|
|
end
|
|
|
|
def all_castles
|
|
dirs = Pathname.glob("#{repos_dir}/**/*/.git")
|
|
# reject paths that lie inside another castle, like git submodules
|
|
return dirs.reject do |dir|
|
|
dirs.any? {|other| dir != other && dir.fnmatch(other.parent.join('*').to_s) }
|
|
end
|
|
end
|
|
|
|
def inside_each_castle(&block)
|
|
all_castles.each do |git_dir|
|
|
castle = git_dir.dirname
|
|
Dir.chdir castle do # so we can call git config from the right contxt
|
|
yield castle
|
|
end
|
|
end
|
|
end
|
|
|
|
def update_castle(castle)
|
|
check_castle_existance(castle, "pull")
|
|
inside repos_dir.join(castle) do
|
|
git_pull
|
|
git_submodule_init
|
|
git_submodule_update
|
|
end
|
|
end
|
|
end
|