23 Commits

Author SHA1 Message Date
Jeremy Cook
ee4388b0f4 Moved code to a more logical home. 2019-01-19 23:21:27 -05:00
Jeremy Cook
a44a514007 Reduced visibility of methods. 2019-01-19 18:59:46 -05:00
Jeremy Cook
9431cb78af Moved code to utils to reduce method complexity. 2019-01-19 18:55:02 -05:00
Jeremy Cook
46c52769a6 Fixed issue where using pretend option would not evaluate files
correctly.
2019-01-19 15:03:28 -05:00
Jeremy Cook
fdb57cd846 Apply fixes suggested by Rubocop. 2019-01-19 11:50:20 -05:00
Jeremy Cook
ff387280d5 Merge branch 'master' of github.com:technicalpickles/homesick 2019-01-18 23:33:41 -05:00
Jeremy Cook
f09c62d922 Minor fixes suggested by rubocop. 2019-01-18 23:32:31 -05:00
Balint Reczey
dd7d52a25d Run Travis tests on Ruby 2.5.0, too 2019-01-18 17:02:57 -05:00
Balint Reczey
f1630ece79 Fix tests on Ruby 2.5 2019-01-18 17:02:57 -05:00
Denny Schäfer
11ee8cdc0d Fix markdown typo 2019-01-18 17:02:57 -05:00
Jeremy Cook
ceb08cbe22 Update Gemfile to remove reference to outdated version of rack. 2019-01-18 17:02:52 -05:00
Jeremy Cook
057e1cfc59 Regenerate gemspec for version 1.1.6 2019-01-18 17:02:52 -05:00
Jeremy Cook
89f3000d8b Prepare for release of new version 2019-01-18 17:02:51 -05:00
Diego Rabatone Oliveira
36e3cb6bbf Require fileutils correctly
Fix #165
2019-01-18 17:02:51 -05:00
mail6543210
9ebae75e7d Add testcase 2019-01-18 17:02:51 -05:00
mail6543210
35e1909790 Real fix for #148 2019-01-18 17:02:51 -05:00
mail6543210
3b633ed326 Rename content to source
It is a instance of Pathname, not binary content
2019-01-18 17:02:51 -05:00
mail6543210
fdf2da84dd Revert "Use source content instead of source path (fixes: #148)"
This reverts commit ed397bdaf8.
2019-01-18 17:02:51 -05:00
Jeremy Cook
e561566b46 Merge pull request #172 from rbalint/master
Fix tests on Ruby 2.5
2018-03-08 06:37:30 -05:00
Balint Reczey
dcef34c17d Run Travis tests on Ruby 2.5.0, too 2018-03-08 08:46:36 +01:00
Balint Reczey
72d11c4a47 Fix tests on Ruby 2.5 2018-03-07 18:06:20 +01:00
Jeremy Cook
c2457bae9f Merge pull request #171 from tuxinaut/master
Fix markdown typo
2018-01-11 07:41:01 -05:00
Denny Schäfer
001bd32bb3 Fix markdown typo 2018-01-11 00:20:23 +01:00
10 changed files with 150 additions and 149 deletions

View File

@@ -1,5 +1,6 @@
language: ruby
rvm:
- 2.5.0
- 2.4.0
- 2.3.3
- 2.2.6

View File

@@ -31,6 +31,6 @@ group :development do
install_if -> { this_ruby < ruby_230 } do
gem 'listen', '< 3'
gem 'rack', '< 2'
gem 'rack', '~> 2.0.6'
end
end

View File

@@ -1,14 +1,29 @@
# -*- encoding : utf-8 -*-
require 'homesick/actions/file_actions'
require 'homesick/actions/git_actions'
require 'homesick/version'
require 'homesick/utils'
require 'homesick/cli'
require 'fileutils'
# Homesick's top-level module
module Homesick
GITHUB_NAME_REPO_PATTERN = %r{\A([A-Za-z0-9_-]+/[A-Za-z0-9_-]+)\Z}
SUBDIR_FILENAME = '.homesick_subdir'
GITHUB_NAME_REPO_PATTERN = %r{\A([A-Za-z0-9_-]+/[A-Za-z0-9_-]+)\Z}.freeze
SUBDIR_FILENAME = '.homesick_subdir'.freeze
DEFAULT_CASTLE_NAME = 'dotfiles'
DEFAULT_CASTLE_NAME = 'dotfiles'.freeze
QUIETABLE = [:say_status].freeze
PRETENDABLE = [:system].freeze
QUIETABLE.each do |method_name|
define_method(method_name) do |*args|
super(*args) unless options[:quiet]
end
end
PRETENDABLE.each do |method_name|
define_method(method_name) do |*args|
super(*args) unless options[:pretend]
end
end
end

View File

@@ -1,18 +1,14 @@
# -*- encoding : utf-8 -*-
module Homesick
module Actions
# File-related helper methods for Homesick
module FileActions
protected
def mv(source, destination)
source = Pathname.new(source)
destination = Pathname.new(destination + source.basename)
case
when destination.exist? && (options[:force] || shell.file_collision(destination) { source })
say_status :conflict, "#{destination} exists", :red
say_status :conflict, "#{destination} exists", :red if destination.exist? && (options[:force] || shell.file_collision(destination) { source })
FileUtils.mv source, destination unless options[:pretend]
else
FileUtils.mv source, destination unless options[:pretend]
end
end
def rm_rf(dir)
@@ -24,7 +20,7 @@ module Homesick
target = Pathname.new(target)
if target.symlink?
say_status :unlink, "#{target.expand_path}", :green
say_status :unlink, target.expand_path.to_s, :green
FileUtils.rm_rf target
else
say_status :conflict, "#{target} is not a symlink", :red
@@ -46,41 +42,36 @@ module Homesick
destination = Pathname.new(destination)
FileUtils.mkdir_p destination.dirname
action = if destination.symlink? && destination.readlink == source
:identical
elsif destination.symlink?
:symlink_conflict
elsif destination.exist?
:conflict
else
:success
end
action = :success
action = :identical if destination.symlink? && destination.readlink == source
action = :symlink_conflict if destination.symlink?
action = :conflict if destination.exist?
handle_symlink_action action, source, destination
end
def handle_symlink_action(action, source, destination)
case action
when :identical
if action == :identical
say_status :identical, destination.expand_path, :blue
when :symlink_conflict, :conflict
if action == :conflict
say_status :conflict, "#{destination} exists", :red
else
say_status :conflict,
"#{destination} exists and points to #{destination.readlink}",
:red
return
end
message = generate_symlink_message action, source, destination
if %i[symlink_conflict conflict].include?(action)
say_status :conflict, message, :red
if collision_accepted?(destination, source)
FileUtils.rm_r destination, force: true unless options[:pretend]
FileUtils.ln_s source, destination, force: true unless options[:pretend]
end
else
say_status :symlink,
"#{source.expand_path} to #{destination.expand_path}",
:green
FileUtils.ln_s source, destination unless options[:pretend]
end
say_status :symlink, message, :green
end
FileUtils.ln_s source, destination, force: true unless options[:pretend]
end
def generate_symlink_message(action, source, destination)
message = "#{source.expand_path} to #{destination.expand_path}"
message = "#{destination} exists and points to #{destination.readlink}" if action == :symlink_conflict
message = "#{destination} exists" if action == :conflict
message
end
end
end

View File

@@ -1,4 +1,3 @@
# -*- encoding : utf-8 -*-
module Homesick
module Actions
# Git-related helper methods for Homesick
@@ -8,18 +7,20 @@ module Homesick
major: 1,
minor: 8,
patch: 0
}
}.freeze
STRING = MIN_VERSION.values.join('.')
def git_version_correct?
info = `git --version`.scan(/(\d+)\.(\d+)\.(\d+)/).flatten.map(&:to_i)
return false unless info.count == 3
current_version = Hash[[:major, :minor, :patch].zip(info)]
return true if current_version.eql?(MIN_VERSION)
return true if current_version[:major] > MIN_VERSION[:major]
return true if current_version[:major] == MIN_VERSION[:major] && current_version[:minor] > MIN_VERSION[:minor]
return true if current_version[:major] == MIN_VERSION[:major] && current_version[:minor] == MIN_VERSION[:minor] && current_version[:patch] >= MIN_VERSION[:patch]
false
current_version = Hash[%i[major minor patch].zip(info)]
major_equals = current_version.eql?(MIN_VERSION)
major_greater = current_version[:major] > MIN_VERSION[:major]
minor_greater = current_version[:major] == MIN_VERSION[:major] && current_version[:minor] > MIN_VERSION[:minor]
patch_greater = current_version[:major] == MIN_VERSION[:major] && current_version[:minor] == MIN_VERSION[:minor] && current_version[:patch] >= MIN_VERSION[:patch]
major_equals || major_greater || minor_greater || patch_greater
end
# TODO: move this to be more like thor's template, empty_directory, etc

View File

@@ -1,4 +1,3 @@
# -*- encoding : utf-8 -*-
require 'fileutils'
require 'thor'
@@ -25,17 +24,7 @@ module Homesick
say_status :error, "Git version >= #{Homesick::Actions::GitActions::STRING} must be installed to use Homesick", :red
exit(1)
end
# Hack in support for diffing symlinks
# Also adds support for checking if destination or content is a directory
shell_metaclass = class << shell; self; end
shell_metaclass.send(:define_method, :show_diff) do |destination, source|
destination = Pathname.new(destination)
source = Pathname.new(source)
return 'Unable to create diff: destination or content is a directory' if destination.directory? || source.directory?
return super(destination, File.binread(source)) unless destination.symlink?
say "- #{destination.readlink}", :red, true
say "+ #{source.expand_path}", :green, true
end
configure_symlinks_diff
end
desc 'clone URI CASTLE_NAME', 'Clone +uri+ as a castle with name CASTLE_NAME for homesick'
@@ -45,7 +34,7 @@ module Homesick
inside repos_dir do
if File.exist?(uri)
uri = Pathname.new(uri).expand_path
fail "Castle already cloned to #{uri}" if uri.to_s.start_with?(repos_dir.to_s)
raise "Castle already cloned to #{uri}" if uri.to_s.start_with?(repos_dir.to_s)
destination = uri.basename if destination.nil?
@@ -58,7 +47,7 @@ module Homesick
destination = Pathname.new(Regexp.last_match[1].gsub(/\.git$/, '')).basename if destination.nil?
git_clone uri, destination: destination
else
fail "Unknown URI format: #{uri}"
raise "Unknown URI format: #{uri}"
end
setup_castle(destination)
@@ -75,8 +64,10 @@ module Homesick
destination = Pathname.new(name)
homesickrc = destination.join('.homesickrc').expand_path
return unless homesickrc.exist?
proceed = options[:force] || shell.yes?("#{name} has a .homesickrc. Proceed with evaling it? (This could be destructive)")
return say_status 'eval skip', "not evaling #{homesickrc}, #{destination} may need manual configuration", :blue unless proceed
say_status 'eval', homesickrc
inside destination do
eval homesickrc.read, binding, homesickrc.expand_path.to_s
@@ -136,11 +127,12 @@ module Homesick
def link(name = DEFAULT_CASTLE_NAME)
check_castle_existance(name, 'symlink')
inside castle_dir(name) do
castle_path = castle_dir(name)
inside castle_path do
subdirs = subdirs(name)
# link files
symlink_each(name, castle_dir(name), subdirs)
symlink_each(name, castle_path, subdirs)
# link files in subdirs
subdirs.each do |subdir|

View File

@@ -1,25 +1,8 @@
# -*- encoding : utf-8 -*-
require 'pathname'
module Homesick
# Various utility methods that are used by Homesick
module Utils
QUIETABLE = [:say_status]
PRETENDABLE = [:system]
QUIETABLE.each do |method_name|
define_method(method_name) do |*args|
super(*args) unless options[:quiet]
end
end
PRETENDABLE.each do |method_name|
define_method(method_name) do |*args|
super(*args) unless options[:pretend]
end
end
protected
def home_dir
@@ -36,6 +19,7 @@ module Homesick
def check_castle_existance(name, action)
return if castle_dir(name).exist?
say_status :error,
"Could not #{action} #{name}, expected #{castle_dir(name)} to exist and contain dotfiles",
:red
@@ -149,47 +133,11 @@ module Homesick
end
def collision_accepted?(destination, source)
fail "Arguments must be instances of Pathname, #{destination.class.name} and #{source.class.name} given" unless destination.instance_of?(Pathname) && source.instance_of?(Pathname)
raise "Arguments must be instances of Pathname, #{destination.class.name} and #{source.class.name} given" unless destination.instance_of?(Pathname) && source.instance_of?(Pathname)
options[:force] || shell.file_collision(destination) { source }
end
def each_file(castle, basedir, subdirs)
absolute_basedir = Pathname.new(basedir).expand_path
inside basedir do
files = Pathname.glob('{.*,*}').reject do |a|
['.', '..'].include?(a.to_s)
end
files.each do |path|
absolute_path = path.expand_path
castle_home = castle_dir(castle)
# make ignore dirs
ignore_dirs = []
subdirs.each do |subdir|
# ignore all parent of each line in subdir file
Pathname.new(subdir).ascend do |p|
ignore_dirs.push(p)
end
end
# ignore dirs written in subdir file
matched = false
ignore_dirs.uniq.each do |ignore_dir|
if absolute_path == castle_home.join(ignore_dir)
matched = true
break
end
end
next if matched
relative_dir = absolute_basedir.relative_path_from(castle_home)
home_path = home_dir.join(relative_dir).join(path)
yield(absolute_path, home_path)
end
end
end
def unsymlink_each(castle, basedir, subdirs)
each_file(castle, basedir, subdirs) do |_absolute_path, home_path|
rm_link home_path
@@ -212,5 +160,56 @@ module Homesick
rc(path)
end
def each_file(castle, basedir, subdirs)
absolute_basedir = Pathname.new(basedir).expand_path
castle_home = castle_dir(castle)
inside basedir do |destination_root|
FileUtils.cd(destination_root) unless destination_root == FileUtils.pwd
files = Pathname.glob('*', File::FNM_DOTMATCH)
.reject { |a| ['.', '..'].include?(a.to_s) }
.reject { |path| matches_ignored_dir? castle_home, path.expand_path, subdirs }
files.each do |path|
absolute_path = path.expand_path
relative_dir = absolute_basedir.relative_path_from(castle_home)
home_path = home_dir.join(relative_dir).join(path)
yield(absolute_path, home_path)
end
end
end
def matches_ignored_dir?(castle_home, absolute_path, subdirs)
# make ignore dirs
ignore_dirs = []
subdirs.each do |subdir|
# ignore all parent of each line in subdir file
Pathname.new(subdir).ascend do |p|
ignore_dirs.push(p)
end
end
# ignore dirs written in subdir file
ignore_dirs.uniq.each do |ignore_dir|
return true if absolute_path == castle_home.join(ignore_dir)
end
false
end
def configure_symlinks_diff
# Hack in support for diffing symlinks
# Also adds support for checking if destination or content is a directory
shell_metaclass = class << shell; self; end
shell_metaclass.send(:define_method, :show_diff) do |destination, source|
destination = Pathname.new(destination)
source = Pathname.new(source)
return 'Unable to create diff: destination or content is a directory' if destination.directory? || source.directory?
return super(destination, File.binread(source)) unless destination.symlink?
say "- #{destination.readlink}", :red, true
say "+ #{source.expand_path}", :green, true
end
end
end
end

View File

@@ -1,4 +1,3 @@
# -*- encoding : utf-8 -*-
module Homesick
# A representation of Homesick's version number in constants, including a
# String of the entire version number

View File

@@ -1,4 +1,3 @@
# -*- encoding : utf-8 -*-
require 'spec_helper'
require 'capture-output'
require 'pathname'
@@ -334,7 +333,7 @@ describe Homesick::CLI do
context 'when call and some files conflict' do
it 'shows differences for conflicting text files' do
contents = {:castle => 'castle has new content', :home => 'home already has content'}
contents = { castle: 'castle has new content', home: 'home already has content' }
dotfile = castle.file('text')
File.open(dotfile.to_s, 'w') do |f|
@@ -348,7 +347,7 @@ describe Homesick::CLI do
end
it 'shows message or differences for conflicting binary files' do
# content which contains NULL character, without any parentheses, braces, ...
contents = {:castle => (0..255).step(30).map{|e| e.chr}.join(), :home => (0..255).step(30).reverse_each.map{|e| e.chr}.join()}
contents = { castle: (0..255).step(30).map(&:chr).join, home: (0..255).step(30).reverse_each.map(&:chr).join }
dotfile = castle.file('binary')
File.open(dotfile.to_s, 'w') do |f|
@@ -739,6 +738,8 @@ describe Homesick::CLI do
end
it 'returns an error message when the $EDITOR environment variable is not set' do
# Return empty ENV, the test does not call it anyway
allow(ENV).to receive(:[]).and_return(nil)
# Set the default editor to make sure it fails.
allow(ENV).to receive(:[]).with('EDITOR').and_return(nil)
expect(homesick).to receive('say_status').once
@@ -747,6 +748,8 @@ describe Homesick::CLI do
end
it 'returns an error message when the given castle does not exist' do
# Return empty ENV, the test does not call it anyway
allow(ENV).to receive(:[]).and_return(nil)
# Set a default just in case none is set
allow(ENV).to receive(:[]).with('EDITOR').and_return('vim')
allow(homesick).to receive('say_status').once
@@ -794,7 +797,7 @@ describe Homesick::CLI do
allow(homesick).to receive('say_status').once
.with(be_a(String), match(/.*Would execute.*/), :green)
expect(homesick).to receive('system').never
Capture.stdout { homesick.invoke 'exec', %w(castle_repo ls -la), pretend: true }
Capture.stdout { homesick.invoke 'exec', %w[castle_repo ls -la], pretend: true }
end
end
@@ -803,7 +806,7 @@ describe Homesick::CLI do
expect(homesick).to receive('say_status').never
allow(homesick).to receive('system').once
.with('ls -la')
Capture.stdout { homesick.invoke 'exec', %w(castle_repo ls -la), quiet: true }
Capture.stdout { homesick.invoke 'exec', %w[castle_repo ls -la], quiet: true }
end
end
end
@@ -842,7 +845,7 @@ describe Homesick::CLI do
allow(homesick).to receive('say_status').at_least(:once)
.with(be_a(String), match(/.*Would execute.*/), :green)
expect(homesick).to receive('system').never
Capture.stdout { homesick.invoke 'exec_all', %w(ls -la), pretend: true }
Capture.stdout { homesick.invoke 'exec_all', %w[ls -la], pretend: true }
end
end
@@ -851,7 +854,7 @@ describe Homesick::CLI do
expect(homesick).to receive('say_status').never
allow(homesick).to receive('system').at_least(:once)
.with('ls -la')
Capture.stdout { homesick.invoke 'exec_all', %w(ls -la), quiet: true }
Capture.stdout { homesick.invoke 'exec_all', %w[ls -la], quiet: true }
end
end
end