lace 0.5.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 46881767c22f451c4e42104713158a054acfe41eee483ab2cac43416d8e99738
4
- data.tar.gz: b8d483c8695e034ddd022522362b8aaad46a9947c98bf02bd92f9880cbda3280
3
+ metadata.gz: f7f0f417d0dbe5369de03faab17bf6deaca9fb97974d297ba00737111a83b3ee
4
+ data.tar.gz: 6506f85738991370546e31f9b2f1fa890ac73266c4acebfe0efc7df151819186
5
5
  SHA512:
6
- metadata.gz: 3200530a5a2007710f79508f5027c1caa72bf08377f296a6adb6b94db404a7b6dca9abbcd5dfe7d9e8bc1945741c9f61299e06cbf7e15a5ac74e2ced4d81ee1e
7
- data.tar.gz: 0a6019e2cae178f0a276ecc943466445f493b6a30df7f0ae5f36db9a344b2641bfde336cced8e6be47e02b979ed7d749632d6e0057e544a65d21b50f3c219242
6
+ metadata.gz: 822ca5da353445be87fb1d31f2c4cb3bd0a8582bbbaecbf64572878e092fb83f63cf226407767414ea2b0e6a0d69d02b299da914ffbea869912301f4b8226915
7
+ data.tar.gz: 3d5039c97612a34c1daadb8239e41b620d33f5b6db0a91b029a1e3b80b39acc32663de850959fb75d8548b74fb40171293d63342136cc85d8a1e42c79771d47d
data/bin/lace CHANGED
@@ -48,7 +48,7 @@ case ARGV.first when '-h', '--help', '--usage', '-?', 'help', nil
48
48
  require 'cmd/help'
49
49
  puts Lace.help_s
50
50
  exit ARGV.first ? 0 : 1
51
- when '--version'
51
+ when '--version', '-v'
52
52
  puts Lace::VERSION
53
53
  exit 0
54
54
  end
data/lib/cmd/diff.rb ADDED
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lace/package'
4
+ require 'lace/exceptions'
5
+
6
+ module Lace
7
+ module_function
8
+
9
+ def diff
10
+ package_name = ARGV.shift
11
+ raise ResourceNotSpecified unless package_name
12
+
13
+ package = Package.new(package_name, ARGV.first)
14
+ diff = PackageUtils.diff package_name
15
+ ohai 'The following files would be added [+] or removed [-]'
16
+ diff.added.each do |f|
17
+ puts "[+] #{f.basename} -> #{f.as_dotfile(Dir.home, package.path).to_path.gsub(Dir.home, '$HOME')}"
18
+ end
19
+ diff.removed.each do |f|
20
+ puts "[-] #{f.basename} -> #{f.as_dotfile(Dir.home, package.path).to_path.gsub(Dir.home, '$HOME')}"
21
+ end
22
+ end
23
+ end
data/lib/cmd/help.rb CHANGED
@@ -24,6 +24,7 @@ Troubleshooting:
24
24
  lace help
25
25
  lace info <pkg-name>
26
26
  lace validate <local-directory>
27
+ lace --version
27
28
 
28
29
  For further help visit:
29
30
  https://github.com/kairichard/lace
data/lib/cmd/setup.rb CHANGED
@@ -1,7 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'lace/package'
2
4
  require 'lace/exceptions'
3
5
 
4
- module Lace extend self
6
+ module Lace
7
+ module_function
8
+
5
9
  def setup
6
10
  package_name = ARGV.shift
7
11
  PackageUtils.setup package_name
data/lib/cmd/validate.rb CHANGED
@@ -1,28 +1,33 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'erb'
2
4
 
3
5
  require 'lace/package'
4
6
  require 'lace/utils'
5
7
  require 'lace/exceptions'
6
8
 
7
- VALIDATE = <<-EOS
8
- Lace-Manifest Validation Report:
9
- <% validation.errors.each do |error| -%>
10
- <%= "%-58s [ %s ]" % [error[0] + ':', error[1]] %>
11
- <% unless error[2].nil? -%>
12
- <% error[2].each do |line| -%>
13
- <%= Tty.gray %><%= '# '+line.to_s %><%= Tty.reset %>
14
- <% end -%>
15
- <% end -%>
16
- <% end -%>
9
+ VALIDATE = <<~EOS
10
+ Lace-Manifest Validation Report:
11
+ <% validation.errors.each do |error| -%>
12
+ <%= "%-58s [ %s ]" % [error[0] + ':', error[1]] %>
13
+ <% unless error[2].nil? -%>
14
+ <% error[2].each do |line| -%>
15
+ <%= Tty.gray %><%= '# '+line.to_s %><%= Tty.reset %>
16
+ <% end -%>
17
+ <% end -%>
18
+ <% end -%>
17
19
  EOS
18
20
 
19
- module Lace extend self
21
+ module Lace
22
+ module_function
23
+
20
24
  def validate
21
25
  resource = ARGV.shift
22
26
  flavor = ARGV.shift
23
27
  raise ResourceNotSpecified unless resource
28
+
24
29
  validation = PackageValidator.new(PackageFacts.new(resource), flavor)
25
- puts ERB.new(VALIDATE, nil, '-').result(binding)
30
+ puts ERB.new(VALIDATE, trim_mode: '-').result(binding)
26
31
  Lace.failed = true if validation.has_errors?
27
32
  end
28
33
  end
@@ -32,7 +37,8 @@ class PackageValidator
32
37
 
33
38
  class << self
34
39
  attr_accessor :validations
35
- def validate name, method_name
40
+
41
+ def validate(name, method_name)
36
42
  @validations ||= []
37
43
  @validations << [name, method_name]
38
44
  end
@@ -44,18 +50,19 @@ class PackageValidator
44
50
  validate 'setup', :setup_ok
45
51
  validate 'post-update hook', :post_update_hooks_ok
46
52
 
47
- def initialize facts, flavor=nil
53
+ def initialize(facts, flavor = nil)
48
54
  @facts = facts
49
55
  @errors = []
50
56
  if @facts.has_flavors? && flavor.nil?
51
- raise RuntimeError.new(FlavorArgumentMsg % @facts.flavors.join("\n- "))
57
+ raise FlavorArgumentMsg % @facts.flavors.join("\n- ").to_s
52
58
  elsif @facts.has_flavors? && flavor != false
53
59
  @facts.flavor! flavor
54
60
  end
61
+
55
62
  validate
56
63
  end
57
64
 
58
- def check_hooks hook_cmd
65
+ def check_hooks(hook_cmd)
59
66
  hook_cmd.map do |cmd|
60
67
  if !File.exist? cmd
61
68
  "#{cmd} cannot be found"
@@ -65,13 +72,13 @@ class PackageValidator
65
72
  end.compact
66
73
  end
67
74
 
68
- def hook_ok config_files
75
+ def hook_ok(config_files)
69
76
  hook_cmd = config_files
70
77
  if hook_cmd.empty?
71
78
  ["#{Tty.green}skipped#{Tty.reset}", nil]
72
79
  else
73
80
  errors = check_hooks hook_cmd
74
- if errors.length > 0
81
+ if errors.length.positive?
75
82
  ["#{Tty.red}error#{Tty.reset}", errors]
76
83
  else
77
84
  ["#{Tty.green}ok#{Tty.reset}", nil]
@@ -88,7 +95,7 @@ class PackageValidator
88
95
  end
89
96
 
90
97
  def homepage_present
91
- if @facts.has_key? 'homepage'
98
+ if @facts.key? 'homepage'
92
99
  ["#{Tty.green}found#{Tty.reset}", nil]
93
100
  else
94
101
  ["#{Tty.red}missing#{Tty.reset}", ['adding a homepage improves the credibility', 'of your package']]
@@ -96,7 +103,7 @@ class PackageValidator
96
103
  end
97
104
 
98
105
  def version_present
99
- if @facts.has_key? 'version'
106
+ if @facts.key? 'version'
100
107
  ["#{Tty.green}found#{Tty.reset}", nil]
101
108
  else
102
109
  ["#{Tty.red}missing#{Tty.reset}", ['adding a version to the manifest improves', 'a future update experince']]
@@ -106,8 +113,10 @@ class PackageValidator
106
113
  def config_files_present
107
114
  if @facts.config_files.empty?
108
115
  ["#{Tty.red}missing#{Tty.reset}", ['Add config_files see manual for more information']]
109
- elsif @facts.config_files.any?{|f| !File.exist? f}
110
- ["#{Tty.red}error#{Tty.reset}", @facts.config_files.select{|f| !File.exist? f}.map{|f| "#{f.to_s.split("/").last} is missing from this package"}]
116
+ elsif @facts.config_files.any? { |f| !File.exist? f }
117
+ ["#{Tty.red}error#{Tty.reset}", @facts.config_files.reject do |f|
118
+ File.exist? f
119
+ end.map { |f| "#{f.to_s.split('/').last} is missing from this package" }]
111
120
  else
112
121
  ["#{Tty.green}found#{Tty.reset}", nil]
113
122
  end
@@ -120,6 +129,6 @@ class PackageValidator
120
129
  end
121
130
 
122
131
  def has_errors?
123
- errors.any?{|e| !e[2].nil? }
132
+ errors.any? { |e| !e[2].nil? }
124
133
  end
125
134
  end
@@ -1,8 +1,10 @@
1
- #Copyright 2009-2014 Max Howell and other contributors.
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2009-2014 Max Howell and other contributors.
2
4
  #
3
- #Redistribution and use in source and binary forms, with or without
4
- #modification, are permitted provided that the following conditions
5
- #are met:
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions
7
+ # are met:
6
8
  #
7
9
  # 1. Redistributions of source code must retain the above copyright
8
10
  # notice, this list of conditions and the following disclaimer.
@@ -10,45 +12,41 @@
10
12
  # notice, this list of conditions and the following disclaimer in the
11
13
  # documentation and/or other materials provided with the distribution.
12
14
  #
13
- #THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14
- #IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15
- #OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16
- #IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17
- #INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18
- #NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19
- #DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20
- #THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21
- #(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22
- #THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23
-
24
- require "fileutils"
15
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18
+ # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20
+ # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24
+ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
+
26
+ require 'fileutils'
25
27
 
26
28
  class AbstractDownloadStrategy
27
- attr_reader :name, :resource, :target_folder
29
+ attr_reader :resource, :target_folder, :uri
28
30
 
29
- def initialize uri, desired_package_name=nil
31
+ def initialize(uri, desired_package_name = nil)
30
32
  @desired_package_name = desired_package_name
31
33
  @uri = uri
32
- @target_folder = Lace.pkgs_folder/name
33
- end
34
-
35
- def uri
36
- @uri
34
+ @target_folder = Lace.pkgs_folder / name
37
35
  end
38
36
 
39
37
  # All download strategies are expected to implement these methods
40
38
  def fetch; end
41
39
  def stage; end
40
+
42
41
  def name
43
42
  @desired_package_name
44
43
  end
45
44
  end
46
45
 
47
-
48
46
  class LocalFileStrategy < AbstractDownloadStrategy
49
47
  def fetch
50
- ohai "Fetching #@uri into #@target_folder"
51
- FileUtils.cp_r @uri, @target_folder, :preserve => true
48
+ ohai "Fetching #{@uri} into #{@target_folder}"
49
+ FileUtils.cp_r @uri, @target_folder, preserve: true
52
50
  @target_folder
53
51
  end
54
52
 
@@ -57,22 +55,21 @@ class LocalFileStrategy < AbstractDownloadStrategy
57
55
  end
58
56
  end
59
57
 
60
-
61
58
  module GitCommands
62
59
  def update_repo
63
60
  safe_system 'git', 'fetch', 'origin'
64
61
  end
65
62
 
66
63
  def reset
67
- safe_system 'git', "reset" , "--hard", "origin/HEAD"
64
+ safe_system 'git', 'reset', '--hard', 'origin/HEAD'
68
65
  end
69
66
 
70
67
  def git_dir
71
- @target_folder.join(".git")
68
+ @target_folder.join('.git')
72
69
  end
73
70
 
74
71
  def repo_valid?
75
- quiet_system "git", "--git-dir", git_dir, "status", "-s"
72
+ quiet_system 'git', '--git-dir', git_dir, 'status', '-s'
76
73
  end
77
74
 
78
75
  def repo_modified?
@@ -85,11 +82,11 @@ module GitCommands
85
82
  end
86
83
 
87
84
  def submodules?
88
- @target_folder.join(".gitmodules").exist?
85
+ @target_folder.join('.gitmodules').exist?
89
86
  end
90
87
 
91
88
  def clone_args
92
- args = %w{clone}
89
+ args = %w[clone]
93
90
  args << @uri << @target_folder
94
91
  end
95
92
 
@@ -103,46 +100,44 @@ module GitCommands
103
100
  end
104
101
  end
105
102
 
106
-
107
103
  class GitUpdateStrategy
108
104
  include GitCommands
109
105
 
110
- def initialize name
111
- @target_folder = Lace.pkgs_folder/name
106
+ def initialize(name)
107
+ @target_folder = Lace.pkgs_folder / name
112
108
  end
113
109
 
114
110
  def update
115
111
  if repo_valid?
116
- puts "Updating #@target_folder"
112
+ puts "Updating #{@target_folder}"
117
113
  @target_folder.cd do
118
114
  update_repo
119
115
  reset
120
116
  update_submodules if submodules?
121
117
  end
122
118
  else
123
- puts "Removing invalid .git repo"
119
+ puts 'Removing invalid .git repo'
124
120
  FileUtils.rm_rf @target_folder
125
121
  clone_repo
126
122
  end
127
123
  end
128
124
  end
129
125
 
130
-
131
126
  class GitDownloadStrategy < AbstractDownloadStrategy
132
127
  include GitCommands
133
128
 
134
129
  def fetch
135
- ohai "Cloning #@uri"
130
+ ohai "Cloning #{@uri}"
136
131
 
137
132
  if @target_folder.exist? && repo_valid?
138
- puts "Updating #@target_folder"
133
+ puts "Updating #{@target_folder}"
139
134
  @target_folder.cd do
140
135
  update_repo
141
136
  reset
142
137
  update_submodules if submodules?
143
138
  end
144
139
  elsif @target_folder.exist?
145
- puts "Removing invalid .git repo"
140
+ puts 'Removing invalid .git repo'
146
141
  FileUtils.rm_rf @target_folder
147
142
  clone_repo
148
143
  else
@@ -154,47 +149,45 @@ class GitDownloadStrategy < AbstractDownloadStrategy
154
149
  def name
155
150
  if super
156
151
  super
157
- elsif @uri.include? "github.com"
158
- @uri.split("/")[-2]
152
+ elsif @uri.include? 'github.com'
153
+ @uri.split('/')[-2]
159
154
  elsif File.directory? @uri
160
155
  File.basename(@uri)
161
156
  else
162
- raise "Cannot determine a proper name with #@uri"
157
+ raise "Cannot determine a proper name with #{@uri}"
163
158
  end
164
159
  end
165
-
166
160
  end
167
161
 
168
- class AbbrevGitDownloadStrategy < GitDownloadStrategy
169
- def initialize uri, desired_package_name=nil
170
- unless uri.end_with?(".git")
171
- uri = "#{uri}.git"
172
- end
162
+ class GitHubDownloadStrategy < GitDownloadStrategy
163
+ def initialize(uri, desired_package_name = nil)
164
+ uri = "#{uri}.git" unless uri.end_with?('.git')
173
165
  uri = "https://github.com/#{uri}"
174
- super uri, desired_package_name
166
+ super
175
167
  end
176
168
  end
177
169
 
178
-
179
170
  class DownloadStrategyDetector
180
171
  def self.detect(uri)
181
- detect_from_uri(uri)
172
+ detect_from_uri(uri)
182
173
  end
183
174
 
184
175
  def self.detect_from_uri(uri)
185
- is_git_dir = File.directory?(uri+"/.git")
186
- has_single_slash = uri.scan("/").count == 1
187
- if File.directory?(uri) && !is_git_dir
176
+ is_git_dir = File.directory?("#{uri}/.git")
177
+ has_single_slash = uri.scan('/').count == 1
178
+ via_ssh = uri.start_with?('git@')
179
+
180
+ if File.directory?(uri) && !is_git_dir && !via_ssh
188
181
  return LocalFileStrategy
189
182
  elsif is_git_dir
190
183
  return GitDownloadStrategy
191
- elsif has_single_slash
192
- return AbbrevGitDownloadStrategy
184
+ elsif has_single_slash && !via_ssh
185
+ return GitHubDownloadStrategy
193
186
  end
194
187
 
195
188
  case uri
196
- when %r[^git://] then GitDownloadStrategy
197
- when %r[^https?://.+\.git$] then GitDownloadStrategy
189
+ when /^git@/ then GitDownloadStrategy
190
+ when %r{^https?://.+\.git$} then GitDownloadStrategy
198
191
  # else CurlDownloadStrategy
199
192
  else
200
193
  raise "Cannot determine download startegy from #{uri}"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'yaml'
2
4
  require 'erb'
3
5
 
@@ -8,17 +10,18 @@ class PackageFacts
8
10
  "#<Facts:#{@package_path}>"
9
11
  end
10
12
 
11
- def initialize package_path
13
+ def initialize(package_path)
12
14
  @package_path = Pathname.new(package_path)
13
- @facts_file = @package_path/".lace.yml"
14
- raise PackageFactsNotFound.new(@package_path) unless @facts_file.exist?
15
+ @facts_file = @package_path / '.lace.yml'
16
+ raise PackageFactsNotFound, @package_path unless @facts_file.exist?
17
+
15
18
  @facts = facts_file_to_hash
16
19
  @unflavorable_facts = facts_file_to_hash
17
20
  end
18
21
 
19
- def flavor_with the_flavor
22
+ def flavor_with(the_flavor)
20
23
  if has_flavors? && the_flavor.nil?
21
- raise FlavorArgumentRequired.new flavors
24
+ raise FlavorArgumentRequired, flavors
22
25
  elsif has_flavors? && the_flavor != false
23
26
  flavor! the_flavor
24
27
  end
@@ -26,78 +29,88 @@ class PackageFacts
26
29
 
27
30
  def config_files
28
31
  if has_config_files?
29
- @facts["config_files"].flatten.map do |path|
32
+ @facts['config_files'].flatten.map do |path|
30
33
  Pathname.glob(@package_path + path).delete_if do |match|
31
- match.directory? and path.include? "*"
34
+ match.directory? and path.include? '*'
32
35
  end
33
36
  end.flatten
34
- else [] end
37
+ else
38
+ [] end
39
+ end
40
+
41
+ def globbed_folder
42
+ if has_config_files?
43
+ @facts['config_files'].flatten.select { |f| f.include? '*' }
44
+ else
45
+ [] end
35
46
  end
36
47
 
37
48
  def has_config_files?
38
- has_key? 'config_files'
49
+ key? 'config_files'
39
50
  end
40
51
 
41
52
  def has_flavors?
42
- @unflavorable_facts && !@unflavorable_facts["flavors"].nil?
53
+ @unflavorable_facts && !@unflavorable_facts['flavors'].nil?
43
54
  end
44
55
 
45
- def has_key? key
46
- @unflavorable_facts && (@unflavorable_facts.has_key?(key) or @facts.has_key?(key))
56
+ def key?(key)
57
+ @unflavorable_facts && (@unflavorable_facts.key?(key) or @facts.key?(key))
47
58
  end
48
59
 
49
60
  def version
50
- @unflavorable_facts["version"] if @unflavorable_facts.key? "version"
61
+ @unflavorable_facts['version'] if @unflavorable_facts.key? 'version'
51
62
  end
52
63
 
53
64
  def setup_files
54
- @facts["setup"].flatten rescue []
65
+ @facts['setup'].flatten
66
+ rescue StandardError
67
+ []
55
68
  end
56
69
 
57
70
  def homepage
58
- @unflavorable_facts["homepage"] if @unflavorable_facts.key? "homepage"
71
+ @unflavorable_facts['homepage'] if @unflavorable_facts.key? 'homepage'
59
72
  end
60
73
 
61
74
  def flavors
62
- if @unflavorable_facts && @unflavorable_facts.key?("flavors")
63
- @unflavorable_facts["flavors"].keys.sort
75
+ if @unflavorable_facts&.key?('flavors')
76
+ @unflavorable_facts['flavors'].keys.sort
64
77
  else
65
78
  []
66
79
  end
67
80
  end
68
81
 
69
- def flavor! the_flavor
82
+ def flavor!(the_flavor)
70
83
  raise PackageFlavorDoesNotExist.new(the_flavor, flavors) unless flavors.include? the_flavor
71
- @facts = @unflavorable_facts["flavors"][the_flavor]
84
+
85
+ @facts = @unflavorable_facts['flavors'][the_flavor]
72
86
  end
73
87
 
74
88
  def unflavor!
75
89
  @facts = @unflavorable_facts
76
90
  end
77
91
 
78
- def post hook_point
79
- if @unflavorable_facts.nil? or !@facts.key? "post"
92
+ def post(hook_point)
93
+ if @unflavorable_facts.nil? || !@facts.key?('post')
80
94
  []
81
95
  else
82
- post_hook = @facts["post"]
96
+ post_hook = @facts['post']
83
97
  (post_hook[hook_point.to_s] || []).flatten
84
98
  end
85
99
  end
86
100
 
87
101
  protected
102
+
88
103
  def facts_file_to_hash
89
104
  begin
90
105
  rendered_manifest = ERB.new(@facts_file.read, trim_mode: '-').result(binding)
91
106
  rescue Exception => e
92
107
  raise ManifestErbError.new(self, e)
93
108
  end
94
- value = YAML.load rendered_manifest, aliases: true
95
- if value.is_a?(String) && value == "---"
96
- return Hash.new
109
+ value = YAML.safe_load rendered_manifest, aliases: true
110
+ if value.is_a?(String) && value == '---'
111
+ {}
97
112
  else
98
113
  value
99
114
  end
100
115
  end
101
116
  end
102
-
103
-
@@ -1,31 +1,35 @@
1
- require 'set'
1
+ # frozen_string_literal: true
2
2
 
3
+ require 'set'
3
4
  require 'lace/download_strategy'
4
5
 
5
6
  class Package
6
7
  include GitCommands
7
8
  attr_reader :name, :facts, :path
8
9
 
9
- def initialize name, flavor=nil
10
+ def initialize(name, flavor = nil)
10
11
  @name = name
11
- @path = Lace.pkgs_folder/name
12
- raise PackageNotInstalled.new(name) unless @path.exist?
12
+ @path = Lace.pkgs_folder / name
13
+ raise PackageNotInstalled, name unless @path.exist?
14
+
13
15
  @flavor = flavor
14
16
  read_facts!
15
17
  end
16
18
 
17
19
  def setup
18
20
  return if ARGV.nohooks?
21
+
19
22
  ENV['LACEPKG_PATH'] = @path
20
- @path.cd do
21
- facts.setup_files.each do |cmd|
22
- safe_system cmd
23
- end
24
- end
23
+ @path.cd do
24
+ facts.setup_files.each do |cmd|
25
+ safe_system cmd
26
+ end
27
+ end
25
28
  end
26
29
 
27
30
  def after_update
28
31
  return if ARGV.nohooks?
32
+
29
33
  @path.cd do
30
34
  facts.post(:update).each do |cmd|
31
35
  system cmd
@@ -40,17 +44,18 @@ class Package
40
44
 
41
45
  def is_modified?
42
46
  return false unless is_git_repo?
47
+
43
48
  @target_folder = @path
44
49
  repo_modified?
45
50
  end
46
51
 
47
52
  def is_active?
48
- home_dir = ENV["HOME"]
53
+ home_dir = Dir.home
49
54
  if @facts.has_flavors? && @flavor == false
50
- @facts.flavors.any?{|f| Package.new(@name, f).is_active?}
55
+ @facts.flavors.any? { |f| Package.new(@name, f).is_active? }
51
56
  else
52
57
  config_files = Set.new @facts.config_files
53
- config_files.all? do |p|
58
+ config_files.all? do |p|
54
59
  dotfile = p.as_dotfile(home_dir, @path)
55
60
  dotfile.exist? and dotfile.symlink? and dotfile.readlink.dirname.to_s.include?(@path)
56
61
  end
@@ -64,7 +69,7 @@ class Package
64
69
 
65
70
  def deactivate!
66
71
  files = @facts.config_files
67
- home_dir = ENV["HOME"]
72
+ home_dir = Dir.home
68
73
  files.each do |file|
69
74
  file = Pathname.new(file)
70
75
  dotfile = file.as_dotfile(home_dir, @path)
@@ -79,23 +84,21 @@ class Package
79
84
  end
80
85
  end
81
86
 
82
- def link_file file
83
- home_dir = ENV["HOME"]
87
+ def link_file(file)
88
+ home_dir = Dir.home
84
89
  # if ends in erb -> generate it
85
90
  src = file
86
91
  dest = src.as_dotfile(home_dir, @path)
87
92
  if dest.exist? && dest.directory?
88
93
  raise WouldOverwriteError.new(dest, src) unless ARGV.force?
94
+
89
95
  FileUtils.mv dest, dest.as_backup
90
96
  end
91
97
  link_file_or_directory src, dest
92
98
  end
93
99
 
94
100
  def link_file_or_directory(src, dest)
95
- puts "link_file"
96
- unless dest.dirname.exist?
97
- dest.dirname.mkpath
98
- end
99
- FileUtils.ln_s src, dest, :force => ARGV.force?
101
+ dest.dirname.mkpath unless dest.dirname.exist?
102
+ FileUtils.ln_s src, dest, force: ARGV.force?
100
103
  end
101
104
  end
@@ -1,59 +1,99 @@
1
- require 'lace/exceptions'
1
+ # frozen_string_literal: true
2
2
 
3
- class PackageUtils
3
+ require('lace/exceptions')
4
+
5
+ Diff = Struct.new(:added, :removed) do
6
+ end
4
7
 
5
- def self.fetch uri, desired_package_name=nil
8
+ class PackageUtils
9
+ def self.fetch(uri, desired_package_name = nil)
6
10
  downloader_cls = DownloadStrategyDetector.detect(uri)
7
11
  downloader = downloader_cls.new(uri, desired_package_name)
8
- raise PackageAlreadyInstalled.new(downloader.target_folder) if downloader.target_folder.exist?
12
+ raise(PackageAlreadyInstalled, downloader.target_folder) if downloader.target_folder.exist?
13
+
9
14
  downloader.fetch
10
- return downloader.name, downloader.target_folder
15
+ [downloader.name, downloader.target_folder]
11
16
  end
12
17
 
13
- def self.remove package_name
18
+ def self.remove(package_name)
14
19
  package = Package.new(package_name, false)
15
- raise CannotRemoveActivePackage.new if package.is_active?
16
- ohai "Removing"
17
- FileUtils.rm_rf package.path
18
- end
19
-
20
- def self.setup package_name
21
- begin
22
- package = Package.new(package_name, ARGV.first)
23
- package.activate!
24
- package.setup
25
- rescue FlavorError => e
26
- onoe e.message
27
- onoe "Package remains installed but was not activated"
28
- end
20
+ raise(CannotRemoveActivePackage) if package.is_active?
21
+
22
+ ohai('Removing')
23
+ FileUtils.rm_rf(package.path)
24
+ end
25
+
26
+ def self.setup(package_name)
27
+ package = Package.new(package_name, ARGV.first)
28
+ package.activate!
29
+ package.setup
30
+ rescue StandardError => e
31
+ onoe(e.message)
32
+ odie('Package remains installed but was not activated')
29
33
  end
30
34
 
31
- def self.deactivate package_name
35
+ def self.deactivate(package_name)
32
36
  package = Package.new(package_name, ARGV.first)
33
- raise NonActiveFlavorError.new unless package.is_active? || ARGV.force?
34
- ohai "Deactivating"
37
+ raise(NonActiveFlavorError) unless package.is_active? && ARGV.force?
38
+
39
+ ohai('Deactivating')
35
40
  package.deactivate!
36
41
  end
37
42
 
38
- def self.activate package_name
43
+ def self.activate(package_name)
39
44
  package = Package.new(package_name, ARGV.first)
40
- raise AlreadyActiveError.new if Package.new(package_name, false).is_active?
41
- ohai "Activating"
45
+ raise(AlreadyActiveError) if Package.new(package_name, false).is_active?
46
+
47
+ ohai('Activating')
42
48
  package.activate!
43
49
  end
44
50
 
45
- def self.update package_name
51
+ def self.update(package_name)
46
52
  package = Package.new(package_name, false)
47
- raise OnlyGitReposCanBeUpdatedError.new unless package.is_git_repo?
53
+ raise(OnlyGitReposCanBeUpdatedError) unless package.is_git_repo?
54
+
48
55
  updater = GitUpdateStrategy.new(package_name)
49
56
  was_active_before_update = package.is_active?
50
- self.deactivate(package_name) if was_active_before_update
51
- ohai "Updating"
57
+ deactivate(package_name) if was_active_before_update
58
+ ohai('Updating')
52
59
  updater.update
53
- self.activate(package_name) if was_active_before_update
54
- package = Package.new package_name, false
60
+ activate(package_name) if was_active_before_update
61
+ package = Package.new(package_name, false)
55
62
  package.after_update
56
63
  end
64
+
65
+ def self.diff(package_name)
66
+ home_dir = Dir.home
67
+ package = Package.new(package_name, ARGV.first)
68
+ config_files = Set.new(package.facts.config_files)
69
+ files_pointing_to_package = symlinks_to_package(package)
70
+ files_from_manifest_not_in_home = Set.new(config_files.reject do |f|
71
+ f.as_dotfile(home_dir, package.path).exist?
72
+ end)
73
+ Diff.new(files_from_manifest_not_in_home, (files_pointing_to_package - config_files))
74
+ end
75
+
76
+ def self.symlinks_to_package(package)
77
+ home_dir = Dir.home
78
+ found_links = Set.new
79
+ traverse_directory(home_dir, package) { |entry| (found_links << Pathname.new(File.readlink(entry))) }
80
+ found_links
81
+ end
57
82
  end
58
83
 
84
+ COMMON_CONFIG_FOLDERS = ['config'].freeze
85
+
86
+ def traverse_directory(directory, package, &block)
87
+ package_path = package.path
88
+ whitelisted_folders = package.facts.globbed_folder + COMMON_CONFIG_FOLDERS
89
+ Dir.foreach(directory) do |entry|
90
+ next if ['.', '..'].include?(entry)
59
91
 
92
+ entry_path = File.join(directory, entry)
93
+ if File.symlink?(entry_path) && File.readlink(entry_path).include?(package_path)
94
+ block.call(entry_path)
95
+ elsif File.directory?(entry_path) && whitelisted_folders.any? { |f| entry_path.include?(f) }
96
+ traverse_directory(entry_path, package, &block)
97
+ end
98
+ end
99
+ end
data/lib/lace/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Lace
2
- VERSION = "0.5.1"
2
+ VERSION = "0.5.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lace
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kai Richard Koenig
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-12-13 00:00:00.000000000 Z
11
+ date: 2024-12-25 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Lace lets you manage your dotfiles when using them on multiple machines
14
14
  email: kai.richard.koenig@gmail.com
@@ -20,6 +20,7 @@ files:
20
20
  - bin/lace
21
21
  - lib/cmd/activate.rb
22
22
  - lib/cmd/deactivate.rb
23
+ - lib/cmd/diff.rb
23
24
  - lib/cmd/fetch.rb
24
25
  - lib/cmd/help.rb
25
26
  - lib/cmd/inspect.rb