homesick 1.1.6 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/homesick.gemspec CHANGED
@@ -1,105 +1,34 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
- # -*- encoding: utf-8 -*-
5
- # stub: homesick 1.1.6 ruby lib
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/homesick/version'
6
4
 
7
5
  Gem::Specification.new do |s|
8
- s.name = "homesick".freeze
9
- s.version = "1.1.6"
6
+ s.name = 'homesick'
7
+ s.version = Homesick::Version::STRING
8
+ s.authors = ['Joshua Nichols', 'Yusuke Murata']
9
+ s.email = ['josh@technicalpickles.com', 'info@muratayusuke.com']
10
+ s.homepage = 'http://github.com/technicalpickles/homesick'
11
+ s.summary = "Your home directory is your castle. Don't leave your dotfiles behind."
12
+ s.description = 'Homesick is sorta like rip, but for dotfiles. It uses git to clone a ' \
13
+ 'repository containing dotfiles, and saves them in ~/.homesick. It then ' \
14
+ 'allows you to symlink all the dotfiles into place with a single command.'
15
+ s.license = 'MIT'
16
+ s.metadata = { 'rubygems_mfa_required' => 'true' }
10
17
 
11
- s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
- s.require_paths = ["lib".freeze]
13
- s.authors = ["Joshua Nichols".freeze, "Yusuke Murata".freeze]
14
- s.date = "2017-12-20"
15
- s.description = "\n Your home directory is your castle. Don't leave your dotfiles behind.\n \n\n Homesick is sorta like rip, but for dotfiles. It uses git to clone a repository containing dotfiles, and saves them in ~/.homesick. It then allows you to symlink all the dotfiles into place with a single command. \n\n ".freeze
16
- s.email = ["josh@technicalpickles.com".freeze, "info@muratayusuke.com".freeze]
17
- s.executables = ["homesick".freeze]
18
- s.extra_rdoc_files = [
19
- "ChangeLog.markdown",
20
- "LICENSE",
21
- "README.markdown"
22
- ]
23
- s.files = [
24
- ".document",
25
- ".rspec",
26
- ".rubocop.yml",
27
- ".travis.yml",
28
- "ChangeLog.markdown",
29
- "Gemfile",
30
- "Guardfile",
31
- "LICENSE",
32
- "README.markdown",
33
- "Rakefile",
34
- "bin/homesick",
35
- "homesick.gemspec",
36
- "lib/homesick.rb",
37
- "lib/homesick/actions/file_actions.rb",
38
- "lib/homesick/actions/git_actions.rb",
39
- "lib/homesick/cli.rb",
40
- "lib/homesick/utils.rb",
41
- "lib/homesick/version.rb",
42
- "spec/homesick_cli_spec.rb",
43
- "spec/spec.opts",
44
- "spec/spec_helper.rb"
45
- ]
46
- s.homepage = "http://github.com/technicalpickles/homesick".freeze
47
- s.licenses = ["MIT".freeze]
48
- s.rubygems_version = "2.6.11".freeze
49
- s.summary = "Your home directory is your castle. Don't leave your dotfiles behind.".freeze
18
+ s.required_ruby_version = '>= 3.2'
50
19
 
51
- if s.respond_to? :specification_version then
52
- s.specification_version = 4
20
+ s.files = `git ls-files`.split("\n")
21
+ s.executables = ['homesick']
22
+ s.require_paths = ['lib']
53
23
 
54
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
55
- s.add_runtime_dependency(%q<thor>.freeze, [">= 0.14.0"])
56
- s.add_development_dependency(%q<capture-output>.freeze, ["~> 1.0.0"])
57
- s.add_development_dependency(%q<coveralls>.freeze, [">= 0"])
58
- s.add_development_dependency(%q<guard>.freeze, [">= 0"])
59
- s.add_development_dependency(%q<guard-rspec>.freeze, [">= 0"])
60
- s.add_development_dependency(%q<jeweler>.freeze, [">= 1.6.2"])
61
- s.add_development_dependency(%q<rake>.freeze, [">= 0.8.7"])
62
- s.add_development_dependency(%q<rb-readline>.freeze, ["~> 0.5.0"])
63
- s.add_development_dependency(%q<rspec>.freeze, ["~> 3.5.0"])
64
- s.add_development_dependency(%q<rubocop>.freeze, [">= 0"])
65
- s.add_development_dependency(%q<test_construct>.freeze, [">= 0"])
66
- s.add_development_dependency(%q<libnotify>.freeze, [">= 0"])
67
- s.add_development_dependency(%q<terminal-notifier-guard>.freeze, ["~> 1.7.0"])
68
- s.add_development_dependency(%q<listen>.freeze, ["< 3"])
69
- s.add_development_dependency(%q<rack>.freeze, ["< 2"])
70
- else
71
- s.add_dependency(%q<thor>.freeze, [">= 0.14.0"])
72
- s.add_dependency(%q<capture-output>.freeze, ["~> 1.0.0"])
73
- s.add_dependency(%q<coveralls>.freeze, [">= 0"])
74
- s.add_dependency(%q<guard>.freeze, [">= 0"])
75
- s.add_dependency(%q<guard-rspec>.freeze, [">= 0"])
76
- s.add_dependency(%q<jeweler>.freeze, [">= 1.6.2"])
77
- s.add_dependency(%q<rake>.freeze, [">= 0.8.7"])
78
- s.add_dependency(%q<rb-readline>.freeze, ["~> 0.5.0"])
79
- s.add_dependency(%q<rspec>.freeze, ["~> 3.5.0"])
80
- s.add_dependency(%q<rubocop>.freeze, [">= 0"])
81
- s.add_dependency(%q<test_construct>.freeze, [">= 0"])
82
- s.add_dependency(%q<libnotify>.freeze, [">= 0"])
83
- s.add_dependency(%q<terminal-notifier-guard>.freeze, ["~> 1.7.0"])
84
- s.add_dependency(%q<listen>.freeze, ["< 3"])
85
- s.add_dependency(%q<rack>.freeze, ["< 2"])
86
- end
87
- else
88
- s.add_dependency(%q<thor>.freeze, [">= 0.14.0"])
89
- s.add_dependency(%q<capture-output>.freeze, ["~> 1.0.0"])
90
- s.add_dependency(%q<coveralls>.freeze, [">= 0"])
91
- s.add_dependency(%q<guard>.freeze, [">= 0"])
92
- s.add_dependency(%q<guard-rspec>.freeze, [">= 0"])
93
- s.add_dependency(%q<jeweler>.freeze, [">= 1.6.2"])
94
- s.add_dependency(%q<rake>.freeze, [">= 0.8.7"])
95
- s.add_dependency(%q<rb-readline>.freeze, ["~> 0.5.0"])
96
- s.add_dependency(%q<rspec>.freeze, ["~> 3.5.0"])
97
- s.add_dependency(%q<rubocop>.freeze, [">= 0"])
98
- s.add_dependency(%q<test_construct>.freeze, [">= 0"])
99
- s.add_dependency(%q<libnotify>.freeze, [">= 0"])
100
- s.add_dependency(%q<terminal-notifier-guard>.freeze, ["~> 1.7.0"])
101
- s.add_dependency(%q<listen>.freeze, ["< 3"])
102
- s.add_dependency(%q<rack>.freeze, ["< 2"])
103
- end
104
- end
24
+ s.add_dependency 'thor', '~> 1.0'
105
25
 
26
+ s.add_development_dependency 'bundler-audit', '~> 0.9'
27
+ s.add_development_dependency 'capture-output', '~> 1.0'
28
+ s.add_development_dependency 'mutant-rspec'
29
+ s.add_development_dependency 'rake'
30
+ s.add_development_dependency 'rspec', '~> 3.0'
31
+ s.add_development_dependency 'rubocop'
32
+ s.add_development_dependency 'rubocop-rake'
33
+ s.add_development_dependency 'test_construct'
34
+ end
@@ -1,18 +1,17 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
+
2
3
  module Homesick
3
4
  module Actions
4
5
  # File-related helper methods for Homesick
5
6
  module FileActions
7
+ protected
8
+
6
9
  def mv(source, destination)
7
10
  source = Pathname.new(source)
8
11
  destination = Pathname.new(destination + source.basename)
9
- case
10
- when destination.exist? && (options[:force] || shell.file_collision(destination) { source })
11
- say_status :conflict, "#{destination} exists", :red
12
- FileUtils.mv source, destination unless options[:pretend]
13
- else
14
- FileUtils.mv source, destination unless options[:pretend]
15
- end
12
+ collision = destination.exist? && (options[:force] || shell.file_collision(destination) { source })
13
+ say_status :conflict, "#{destination} exists", :red if collision
14
+ FileUtils.mv source, destination unless options[:pretend]
16
15
  end
17
16
 
18
17
  def rm_rf(dir)
@@ -24,7 +23,7 @@ module Homesick
24
23
  target = Pathname.new(target)
25
24
 
26
25
  if target.symlink?
27
- say_status :unlink, "#{target.expand_path}", :green
26
+ say_status :unlink, target.expand_path.to_s, :green
28
27
  FileUtils.rm_rf target
29
28
  else
30
29
  say_status :conflict, "#{target} is not a symlink", :red
@@ -42,45 +41,40 @@ module Homesick
42
41
  end
43
42
 
44
43
  def ln_s(source, destination)
45
- source = Pathname.new(source).realpath
44
+ source = Pathname.new(source).realpath
46
45
  destination = Pathname.new(destination)
47
46
  FileUtils.mkdir_p destination.dirname
48
47
 
49
- action = if destination.symlink? && destination.readlink == source
50
- :identical
51
- elsif destination.symlink?
52
- :symlink_conflict
53
- elsif destination.exist?
54
- :conflict
55
- else
56
- :success
57
- end
48
+ action = :success
49
+ action = :identical if destination.symlink? && destination.readlink == source
50
+ action = :symlink_conflict if destination.symlink?
51
+ action = :conflict if destination.exist?
58
52
 
59
53
  handle_symlink_action action, source, destination
60
54
  end
61
55
 
62
56
  def handle_symlink_action(action, source, destination)
63
- case action
64
- when :identical
57
+ if action == :identical
65
58
  say_status :identical, destination.expand_path, :blue
66
- when :symlink_conflict, :conflict
67
- if action == :conflict
68
- say_status :conflict, "#{destination} exists", :red
69
- else
70
- say_status :conflict,
71
- "#{destination} exists and points to #{destination.readlink}",
72
- :red
73
- end
74
- if collision_accepted?(destination, source)
75
- FileUtils.rm_r destination, force: true unless options[:pretend]
76
- FileUtils.ln_s source, destination, force: true unless options[:pretend]
77
- end
59
+ return
60
+ end
61
+ message = generate_symlink_message action, source, destination
62
+ if %i[symlink_conflict conflict].include?(action)
63
+ say_status :conflict, message, :red
64
+ return unless collision_accepted?(destination, source)
65
+
66
+ FileUtils.rm_r destination, force: true unless options[:pretend]
78
67
  else
79
- say_status :symlink,
80
- "#{source.expand_path} to #{destination.expand_path}",
81
- :green
82
- FileUtils.ln_s source, destination unless options[:pretend]
68
+ say_status :symlink, message, :green
83
69
  end
70
+ FileUtils.ln_s source, destination, force: true unless options[:pretend]
71
+ end
72
+
73
+ def generate_symlink_message(action, source, destination)
74
+ message = "#{source.expand_path} to #{destination.expand_path}"
75
+ message = "#{destination} exists and points to #{destination.readlink}" if action == :symlink_conflict
76
+ message = "#{destination} exists" if action == :conflict
77
+ message
84
78
  end
85
79
  end
86
80
  end
@@ -1,4 +1,5 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
+
2
3
  module Homesick
3
4
  module Actions
4
5
  # Git-related helper methods for Homesick
@@ -8,35 +9,37 @@ module Homesick
8
9
  major: 1,
9
10
  minor: 8,
10
11
  patch: 0
11
- }
12
+ }.freeze
12
13
  STRING = MIN_VERSION.values.join('.')
13
14
 
14
15
  def git_version_correct?
15
16
  info = `git --version`.scan(/(\d+)\.(\d+)\.(\d+)/).flatten.map(&:to_i)
16
17
  return false unless info.count == 3
17
- current_version = Hash[[:major, :minor, :patch].zip(info)]
18
- return true if current_version.eql?(MIN_VERSION)
19
- return true if current_version[:major] > MIN_VERSION[:major]
20
- return true if current_version[:major] == MIN_VERSION[:major] && current_version[:minor] > MIN_VERSION[:minor]
21
- return true if current_version[:major] == MIN_VERSION[:major] && current_version[:minor] == MIN_VERSION[:minor] && current_version[:patch] >= MIN_VERSION[:patch]
22
- false
18
+
19
+ current_version = %i[major minor patch].zip(info).to_h
20
+ major_equals = current_version.eql?(MIN_VERSION)
21
+ major_greater = current_version[:major] > MIN_VERSION[:major]
22
+ minor_greater = current_version[:major] == MIN_VERSION[:major] &&
23
+ current_version[:minor] > MIN_VERSION[:minor]
24
+ patch_greater = current_version[:major] == MIN_VERSION[:major] &&
25
+ current_version[:minor] == MIN_VERSION[:minor] &&
26
+ current_version[:patch] >= MIN_VERSION[:patch]
27
+
28
+ major_equals || major_greater || minor_greater || patch_greater
23
29
  end
24
30
 
25
- # TODO: move this to be more like thor's template, empty_directory, etc
26
31
  def git_clone(repo, config = {})
27
- config ||= {}
28
32
  destination = config[:destination] || File.basename(repo, '.git')
29
-
30
33
  destination = Pathname.new(destination) unless destination.is_a?(Pathname)
31
34
  FileUtils.mkdir_p destination.dirname
32
35
 
33
36
  if destination.directory?
34
37
  say_status :exist, destination.expand_path, :blue
35
38
  else
36
- say_status 'git clone',
37
- "#{repo} to #{destination.expand_path}",
38
- :green
39
- system "git clone -q --config push.default=upstream --recursive #{repo} #{destination}"
39
+ say_status 'git clone', "#{repo} to #{destination.expand_path}", :green
40
+ unless options[:pretend]
41
+ system "git clone -q --config push.default=upstream --recursive #{repo} #{destination}"
42
+ end
40
43
  end
41
44
  end
42
45
 
data/lib/homesick/cli.rb CHANGED
@@ -1,4 +1,5 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
+
2
3
  require 'fileutils'
3
4
  require 'thor'
4
5
 
@@ -13,6 +14,10 @@ module Homesick
13
14
 
14
15
  add_runtime_options!
15
16
 
17
+ def self.exit_on_failure?
18
+ true
19
+ end
20
+
16
21
  map '-v' => :version
17
22
  map '--version' => :version
18
23
  # Retain a mapped version of the symlink command for compatibility.
@@ -22,45 +27,20 @@ module Homesick
22
27
  super
23
28
  # Check if git is installed
24
29
  unless git_version_correct?
25
- say_status :error, "Git version >= #{Homesick::Actions::GitActions::STRING} must be installed to use Homesick", :red
30
+ say_status :error,
31
+ "Git version >= #{Homesick::Actions::GitActions::STRING} must be installed to use Homesick",
32
+ :red
26
33
  exit(1)
27
34
  end
28
- # Hack in support for diffing symlinks
29
- # Also adds support for checking if destination or content is a directory
30
- shell_metaclass = class << shell; self; end
31
- shell_metaclass.send(:define_method, :show_diff) do |destination, source|
32
- destination = Pathname.new(destination)
33
- source = Pathname.new(source)
34
- return 'Unable to create diff: destination or content is a directory' if destination.directory? || source.directory?
35
- return super(destination, File.binread(source)) unless destination.symlink?
36
- say "- #{destination.readlink}", :red, true
37
- say "+ #{source.expand_path}", :green, true
38
- end
35
+ configure_symlinks_diff
39
36
  end
40
37
 
41
38
  desc 'clone URI CASTLE_NAME', 'Clone +uri+ as a castle with name CASTLE_NAME for homesick'
42
- def clone(uri, destination=nil)
39
+ def clone(uri, destination = nil)
43
40
  destination = Pathname.new(destination) unless destination.nil?
44
41
 
45
42
  inside repos_dir do
46
- if File.exist?(uri)
47
- uri = Pathname.new(uri).expand_path
48
- fail "Castle already cloned to #{uri}" if uri.to_s.start_with?(repos_dir.to_s)
49
-
50
- destination = uri.basename if destination.nil?
51
-
52
- ln_s uri, destination
53
- elsif uri =~ GITHUB_NAME_REPO_PATTERN
54
- destination = Pathname.new(uri).basename if destination.nil?
55
- git_clone "https://github.com/#{Regexp.last_match[1]}.git",
56
- destination: destination
57
- elsif uri =~ /%r([^%r]*?)(\.git)?\Z/ || uri =~ /[^:]+:([^:]+)(\.git)?\Z/
58
- destination = Pathname.new(Regexp.last_match[1].gsub(/\.git$/, '')).basename if destination.nil?
59
- git_clone uri, destination: destination
60
- else
61
- fail "Unknown URI format: #{uri}"
62
- end
63
-
43
+ destination = clone_from_uri(uri, destination)
64
44
  setup_castle(destination)
65
45
  end
66
46
  end
@@ -75,11 +55,19 @@ module Homesick
75
55
  destination = Pathname.new(name)
76
56
  homesickrc = destination.join('.homesickrc').expand_path
77
57
  return unless homesickrc.exist?
78
- proceed = options[:force] || shell.yes?("#{name} has a .homesickrc. Proceed with evaling it? (This could be destructive)")
79
- return say_status 'eval skip', "not evaling #{homesickrc}, #{destination} may need manual configuration", :blue unless proceed
58
+
59
+ proceed = options[:force] ||
60
+ shell.yes?("#{name} has a .homesickrc. Proceed with evaling it? (This could be destructive)")
61
+ unless proceed
62
+ return say_status 'eval skip',
63
+ "not evaling #{homesickrc}, #{destination} may need manual configuration",
64
+ :blue
65
+ end
66
+
80
67
  say_status 'eval', homesickrc
81
68
  inside destination do
82
- eval homesickrc.read, binding, homesickrc.expand_path.to_s
69
+ ctx = Homesick::RC::Context.new(destination.expand_path)
70
+ ctx.instance_eval(homesickrc.read, homesickrc.expand_path.to_s)
83
71
  end
84
72
  end
85
73
  end
@@ -93,7 +81,7 @@ module Homesick
93
81
  def pull(name = DEFAULT_CASTLE_NAME)
94
82
  if options[:all]
95
83
  inside_each_castle do |castle|
96
- say castle.to_s.gsub(repos_dir.to_s + '/', '') + ':'
84
+ say "#{castle.to_s.gsub("#{repos_dir}/", '')}:"
97
85
  update_castle castle
98
86
  end
99
87
  else
@@ -136,11 +124,12 @@ module Homesick
136
124
  def link(name = DEFAULT_CASTLE_NAME)
137
125
  check_castle_existance(name, 'symlink')
138
126
 
139
- inside castle_dir(name) do
127
+ castle_path = castle_dir(name)
128
+ inside castle_path do
140
129
  subdirs = subdirs(name)
141
130
 
142
131
  # link files
143
- symlink_each(name, castle_dir(name), subdirs)
132
+ symlink_each(name, castle_path, subdirs)
144
133
 
145
134
  # link files in subdirs
146
135
  subdirs.each do |subdir|
@@ -160,22 +149,9 @@ module Homesick
160
149
  castle_path = Pathname.new(castle_dir(castle)).join(relative_dir)
161
150
  FileUtils.mkdir_p castle_path
162
151
 
163
- # Are we already tracking this or anything inside it?
164
152
  target = Pathname.new(castle_path.join(file.basename))
165
153
  if target.exist?
166
- if absolute_path.directory?
167
- move_dir_contents(target, absolute_path)
168
- absolute_path.rmtree
169
- subdir_remove(castle, relative_dir + file.basename)
170
-
171
- elsif more_recent? absolute_path, target
172
- target.delete
173
- mv absolute_path, castle_path
174
- else
175
- say_status(:track,
176
- "#{target} already exists, and is more recent than #{file}. Run 'homesick SYMLINK CASTLE' to create symlinks.",
177
- :blue)
178
- end
154
+ handle_existing_track_target(castle, absolute_path, castle_path, relative_dir, file, target)
179
155
  else
180
156
  mv absolute_path, castle_path
181
157
  end
@@ -190,7 +166,6 @@ module Homesick
190
166
  git_add absolute_path
191
167
  end
192
168
 
193
- # are we tracking something nested? Add the parent dir to the manifest
194
169
  subdir_add(castle, relative_dir) unless relative_dir.eql?(Pathname.new('.'))
195
170
  end
196
171
 
@@ -262,14 +237,14 @@ module Homesick
262
237
  "Opening a new shell in castle '#{castle}'. To return to the original one exit from the new shell.",
263
238
  :green
264
239
  inside castle_dir do
265
- system(ENV['SHELL'])
240
+ system(ENV.fetch('SHELL', nil))
266
241
  end
267
242
  end
268
243
 
269
244
  desc 'open CASTLE',
270
245
  'Open your default editor in the root of the given castle'
271
246
  def open(castle = DEFAULT_CASTLE_NAME)
272
- unless ENV['EDITOR']
247
+ unless ENV.fetch('EDITOR', nil)
273
248
  say_status :error,
274
249
  'The $EDITOR environment variable must be set to use this command',
275
250
  :red
@@ -278,11 +253,11 @@ module Homesick
278
253
  end
279
254
  check_castle_existance castle, 'open'
280
255
  castle_dir = repos_dir.join(castle)
281
- say_status "#{castle_dir.realpath}: #{ENV['EDITOR']} .",
282
- "Opening the root directory of castle '#{castle}' in editor '#{ENV['EDITOR']}'.",
256
+ say_status "#{castle_dir.realpath}: #{ENV.fetch('EDITOR', nil)} .",
257
+ "Opening the root directory of castle '#{castle}' in editor '#{ENV.fetch('EDITOR', nil)}'.",
283
258
  :green
284
259
  inside castle_dir do
285
- system("#{ENV['EDITOR']} .")
260
+ system("#{ENV.fetch('EDITOR', nil)} .")
286
261
  end
287
262
  end
288
263
 
@@ -290,15 +265,16 @@ module Homesick
290
265
  'Execute a single shell command inside the root of a castle'
291
266
  def exec(castle, *args)
292
267
  check_castle_existance castle, 'exec'
293
- unless args.count > 0
268
+ unless args.any?
294
269
  say_status :error,
295
270
  'You must pass a shell command to execute',
296
271
  :red
297
272
  exit(1)
298
273
  end
299
274
  full_command = args.join(' ')
275
+ action = options[:pretend] ? 'Would execute' : 'Executing command'
300
276
  say_status "exec '#{full_command}'",
301
- "#{options[:pretend] ? 'Would execute' : 'Executing command'} '#{full_command}' in castle '#{castle}'",
277
+ "#{action} '#{full_command}' in castle '#{castle}'",
302
278
  :green
303
279
  inside repos_dir.join(castle) do
304
280
  system(full_command)
@@ -308,7 +284,7 @@ module Homesick
308
284
  desc 'exec_all COMMAND',
309
285
  'Execute a single shell command inside the root of every cloned castle'
310
286
  def exec_all(*args)
311
- unless args.count > 0
287
+ unless args.any?
312
288
  say_status :error,
313
289
  'You must pass a shell command to execute',
314
290
  :red
@@ -316,8 +292,9 @@ module Homesick
316
292
  end
317
293
  full_command = args.join(' ')
318
294
  inside_each_castle do |castle|
295
+ action = options[:pretend] ? 'Would execute' : 'Executing command'
319
296
  say_status "exec '#{full_command}'",
320
- "#{options[:pretend] ? 'Would execute' : 'Executing command'} '#{full_command}' in castle '#{castle}'",
297
+ "#{action} '#{full_command}' in castle '#{castle}'",
321
298
  :green
322
299
  system(full_command)
323
300
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+
5
+ module Homesick
6
+ module RC
7
+ # Evaluation context for .homesickrc scripts.
8
+ #
9
+ # Runs castle setup scripts in a clean object rather than in the
10
+ # Homesick::CLI binding, so scripts cannot access CLI internals.
11
+ # Standard Ruby library methods (File, Dir, system, etc.) remain
12
+ # available to scripts.
13
+ class Context
14
+ # @param castle_path [Pathname, String] absolute path to the castle root
15
+ def initialize(castle_path)
16
+ @castle_path = Pathname.new(castle_path)
17
+ end
18
+
19
+ # The absolute path of the castle being configured.
20
+ attr_reader :castle_path
21
+
22
+ # Execute a shell command.
23
+ # @param command [String]
24
+ # @return [Boolean] true if the command succeeded
25
+ def run(command)
26
+ system(command)
27
+ end
28
+ end
29
+ end
30
+ end