central 0.2.3 → 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +5 -5
  2. data/bin/central +32 -9
  3. data/lib/central.rb +310 -223
  4. metadata +4 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c28cbd68d80c217678f5b6d81dc82cd2d4de4eed
4
- data.tar.gz: 152e89721d347f991b177e6e81165619c3bfff70
2
+ SHA256:
3
+ metadata.gz: ecf6eb46fbc67795241445bc38629214a0d9eb2b63237f3ade4ccc5009b2eb4f
4
+ data.tar.gz: d105df04c79771d9ae767c471185ec80147fbf7db6d552affa7d01185e4fe965
5
5
  SHA512:
6
- metadata.gz: b873dba32172c267f1419657c5d500736880dbcfe9f04b431984e489406e91ac55a809c9358bb71a361f1f5bf120e238cdf89dc5ee91d9fda7c8bd1c76070dcc
7
- data.tar.gz: 70e5070de43a592bb09234e82e130025516139fbea193caf0cc6d1892ca9a90f98681b83be4118670a3ee01f0fdafcb6d738bf8f61ec2ae6e1a6d6e3a9796d4d
6
+ metadata.gz: b3ec6fa7e823098fc75b55c66399b25da990bc3bdb58d9f54b1c9cb7f26b00eb123699c6772800cfc54aa68826b6152b61284c76555dd248183fbb2d9b497010
7
+ data.tar.gz: 5501d4249660a4d1d288aa6421439045f1ba66da1c316c62517a09d0ed1e7fb14f99f40fbef82966ae7d12ce411c61d0411f64872e4ace77bfdd2c52087ddd11
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- # encoding: utf-8
2
+ # frozen_string_literal: true
3
3
 
4
4
  # -------------------------------------------------------------------------
5
5
  # # central - dot files manager licensed under LGPLv3 (see LICENSE file) |
@@ -7,15 +7,38 @@
7
7
  # -------------------------------------------------------------------------
8
8
 
9
9
  require 'central'
10
+ require 'optparse'
10
11
 
11
- if ARGV.length > 0
12
- if ARGV[0] == '-v' || ARGV[0] == '--version' || ARGV[0] == '-version'
13
- puts "central v0.2.2"
14
- exit 0
12
+ VERSION = 'v0.2.9'
13
+
14
+ options = {:color => true}
15
+ OptionParser.new do |opts|
16
+ opts.banner = %{central #{VERSION} - created by Dmitry Geurkov (d.geurkov@gmail.com) and licensed under LGPLv3
17
+
18
+ Usage: central [options] [directory|configuration.rb, ...]
19
+
20
+ Description: if directory is specified it will use configuration.rb file inside that directory
21
+ if no [directory|configuration.rb] is specified
22
+ it will use configuration.rb in current working directory
23
+
24
+ Options:}
25
+ opts.on("-m", "--monitor", "Monitor erb files for changes and reprocess them automatically") do |_|
26
+ options[:monitor] = true
15
27
  end
16
- if ARGV[0] == '-h' || ARGV[0] == '--help' || ARGV[0] == '-help'
17
- puts "central [path/to/configuration.rb]"
18
- exit 0
28
+
29
+ opts.on("-n", "--no-color", "Do not colorize output") do |_|
30
+ options[:color] = false
31
+ end
32
+
33
+ opts.on("-v", "--version", "Print version") do |_|
34
+ puts "central #{VERSION}"
35
+ exit
19
36
  end
37
+ end.parse!
38
+
39
+ if options[:monitor]
40
+ run_central(ARGV,options[:color])
41
+ run_monitors
42
+ else
43
+ run_central(ARGV,options[:color])
20
44
  end
21
- central(ARGV)
@@ -1,4 +1,5 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
+
2
3
  # -------------------------------------------------------------------------
3
4
  # # central - dot files manager licensed under LGPLv3 (see LICENSE file) |
4
5
  # # written in Ruby by Dmitry Geurkov (d.geurkov@gmail.com) |
@@ -8,6 +9,42 @@ require 'erb'
8
9
  require 'socket'
9
10
  require 'open3'
10
11
  require 'fileutils'
12
+ require 'digest'
13
+
14
+ # cli colors
15
+ $colored = true
16
+ COLOR_RED = 31
17
+ COLOR_GREEN = 32
18
+
19
+ # monitors
20
+ $monitors = {}
21
+
22
+ # putsc, puts with color
23
+ def color(message, color)
24
+ if $colored
25
+ "\e[#{color}m#{message}\e[0m"
26
+ else
27
+ message
28
+ end
29
+ end
30
+
31
+ # info
32
+ def info(message, param = nil)
33
+ puts color(message, COLOR_GREEN) +
34
+ (param.nil? ? '' : ': ' + param)
35
+ end
36
+
37
+ # error
38
+ def error(message, param = nil)
39
+ puts color(message, COLOR_RED) +
40
+ (param.nil? ? '' : ': ' + param)
41
+ end
42
+
43
+ # fail, print message to stderr and exit with 1
44
+ def fail(message, param = nil)
45
+ error message, param
46
+ exit 1
47
+ end
11
48
 
12
49
  # get hostname
13
50
  def hostname
@@ -17,115 +54,106 @@ end
17
54
  # get operating system
18
55
  def os
19
56
  if RUBY_PLATFORM.include?('linux')
20
- return 'linux'
57
+ 'linux'
21
58
  elsif RUBY_PLATFORM.include?('darwin')
22
- return 'osx'
59
+ 'osx'
23
60
  elsif RUBY_PLATFORM.include?('freebsd')
24
- return 'freebsd'
61
+ 'freebsd'
25
62
  elsif RUBY_PLATFORM.include?('solaris')
26
- return 'solaris'
63
+ 'solaris'
27
64
  end
28
65
  end
29
66
 
30
67
  def linux?
31
- return os == 'linux'
68
+ os == 'linux'
32
69
  end
33
70
 
34
71
  def osx?
35
- return os == 'osx'
72
+ os == 'osx'
36
73
  end
37
74
 
38
75
  def freebsd?
39
- return os == 'freebsd'
76
+ os == 'freebsd'
40
77
  end
41
78
 
42
79
  def solaris?
43
- return os == 'solaris'
44
- end
45
-
46
- # run shell command and get output, optionaly can print command running if verbose and if not silent will also print to stdout and stderr
47
- $shell_return_value = nil
48
- def shell(command,options={:verbose => false, :silent => true})
49
- silent = options[:silent]
50
- puts "Executing: #{command}" if options[:verbose]
51
- stdout = ''
52
- stdout_line = ''
53
- stderr = ''
54
- stderr_line = ''
55
- Open3.popen3(command) do |i,o,e,t|
80
+ os == 'solaris'
81
+ end
82
+
83
+ # run shell command and get output, optionaly can print command running
84
+ # if verbose and if not silent will also print to stdout and stderr
85
+ def shell(command, verbose: false, silent: true)
86
+ info 'Executing', command if verbose
87
+ exit_code = nil
88
+ stdout = String.new
89
+ stdout_line = String.new
90
+ stderr = String.new
91
+ stderr_line = String.new
92
+ Open3.popen3(command) do |_, o, e, t|
56
93
  stdout_open = true
57
94
  stderr_open = true
58
- while stdout_open or stderr_open
95
+ while stdout_open || stderr_open
59
96
  if stdout_open
60
97
  begin
61
98
  ch = o.read_nonblock(1)
62
- stdout.insert(-1,ch)
99
+ stdout.insert(-1, ch)
63
100
  unless silent
64
- stdout_line.insert(-1,ch)
101
+ stdout_line.insert(-1, ch)
65
102
  if ch == "\n"
66
103
  STDOUT.puts stdout_line
67
104
  stdout_line = ''
68
105
  end
69
106
  end
70
107
  rescue IO::WaitReadable
71
- IO.select([o],nil,nil,0.1) unless stderr_open
108
+ IO.select([o], nil, nil, 0.01) unless stderr_open
72
109
  rescue EOFError
73
110
  stdout_open = false
74
111
  end
75
112
  end
76
- if stderr_open
77
- begin
78
- ch = e.read_nonblock(1)
79
- stderr.insert(-1,ch)
80
- unless silent
81
- stderr_line.insert(-1,ch)
82
- if ch == "\n"
83
- STDERR.puts stderr_line
84
- stderr_line = ''
85
- end
113
+ next unless stderr_open
114
+
115
+ begin
116
+ ch = e.read_nonblock(1)
117
+ stderr.insert(-1, ch)
118
+ unless silent
119
+ stderr_line.insert(-1, ch)
120
+ if ch == "\n"
121
+ STDERR.puts stderr_line
122
+ stderr_line = ''
86
123
  end
87
- rescue IO::WaitReadable
88
- IO.select([e],nil,nil,0.1) unless stdout_open
89
- rescue EOFError
90
- stderr_open = false
91
124
  end
125
+ rescue IO::WaitReadable
126
+ IO.select([e], nil, nil, 0.01) unless stdout_open
127
+ rescue EOFError
128
+ stderr_open = false
92
129
  end
93
130
  end
94
- $shell_return_value = t.value
95
- end
96
- if stderr == ''
97
- return stdout
98
- else
99
- return stdout, stderr
131
+ exit_code = t.value
100
132
  end
133
+ [exit_code, stdout, stderr]
101
134
  end
102
135
 
103
136
  # run shell command with sudo prefix, acts same as shell
104
- def sudo(command,options)
105
- return shell('sudo '+command, options)
137
+ def sudo(command, verbose:, silent:)
138
+ shell('sudo ' + command, verbose: verbose, silent: silent)
106
139
  end
107
140
 
108
141
  # function used to check that system has all required tools installed
109
- def check_tool(name,check)
110
- begin
111
- output = shell(check+' 2>&1').downcase
112
- if output == '' or output.include?('command not found')
113
- STDERR.puts "#{name} not found, please install it to use central"
114
- exit 1
115
- end
116
- rescue Errno::ENOENT
117
- STDERR.puts "#{name} not found, please install it to use central"
118
- exit 1
142
+ def check_tool(name, check)
143
+ _, output, = shell(check + ' 2>&1')
144
+ if output == '' || output.downcase.include?('command not found')
145
+ fail "#{name} not found, please install it to use central"
119
146
  end
147
+ rescue Errno::ENOENT
148
+ fail "#{name} not found, please install it to use central"
120
149
  end
121
150
 
122
- check_tool('file','file --version')
123
- check_tool('grep','grep --version')
124
- check_tool('ln','ln --version')
125
- check_tool('readlink','readlink --version')
126
- check_tool('git','git --version')
127
- check_tool('curl','curl --version')
128
-
151
+ check_tool('file', 'file --version')
152
+ check_tool('grep', 'grep --version')
153
+ check_tool('ln', 'ln --version')
154
+ check_tool('readlink', 'readlink --version')
155
+ check_tool('git', 'git --version')
156
+ check_tool('curl', 'curl --version')
129
157
 
130
158
  # current working directory
131
159
  def pwd
@@ -134,7 +162,7 @@ end
134
162
 
135
163
  # absolute path
136
164
  def abs(path)
137
- path = File.absolute_path(File.expand_path(path))
165
+ File.absolute_path(File.expand_path(path))
138
166
  end
139
167
 
140
168
  # change current working directory
@@ -148,10 +176,30 @@ def file_exists?(path)
148
176
  File.file?(path) && File.readable?(path)
149
177
  end
150
178
 
179
+ # get file size
180
+ def file_size(path)
181
+ File.new(abs(path)).size
182
+ end
183
+
184
+ # get file creation time
185
+ def file_ctime(path)
186
+ File.new(abs(path)).ctime
187
+ end
188
+
189
+ # get file modification time
190
+ def file_mtime(path)
191
+ File.new(abs(path)).mtime
192
+ end
193
+
194
+ # get directory entries
195
+ def dir_entries(path)
196
+ Dir.entries(abs(path)).select { |f| f != '.' && f != '..' }
197
+ end
198
+
151
199
  # check if directory exists
152
200
  def dir_exists?(path)
153
201
  path = abs(path)
154
- Dir.exists?(path)
202
+ Dir.exist?(path)
155
203
  end
156
204
 
157
205
  # get directory name of a file
@@ -166,80 +214,75 @@ end
166
214
 
167
215
  # get full path of symlink
168
216
  def symlink_path(symlink)
169
- shell("readlink \"#{abs(symlink)}\" 2>&1").strip
217
+ _, out, = shell("readlink \"#{abs(symlink)}\" 2>&1")
218
+ out.strip
219
+ end
220
+
221
+ # calculate SHA2 digest for a file
222
+ def sha2(file)
223
+ Digest::SHA256.hexdigest read(file)
170
224
  end
171
225
 
172
226
  # make directory including intermediate directories
173
227
  def mkdir(path)
174
228
  path = abs(path)
175
- unless dir_exists?(path)
176
- out = shell("mkdir -p \"#{path}\" 2>&1")
177
- unless $shell_return_value.success?
178
- STDERR.puts out
179
- STDERR.puts "Couldn't create directory: #{path}"
180
- exit 1
181
- end
182
- puts "Created directory: #{path}"
229
+ return if dir_exists?(path)
230
+
231
+ exit_code, out, = shell("mkdir -p \"#{path}\" 2>&1")
232
+ unless exit_code.success?
233
+ error out
234
+ fail "Couldn't create directory", path
183
235
  end
236
+ info 'Created directory', path
184
237
  end
185
238
 
186
239
  # remove file/directory
187
- def rm(path,recursive=false)
240
+ def rm(path, recursive: false)
188
241
  path = abs(path)
189
- if recursive
190
- recursive = '-R '
191
- else
192
- recursive = ''
193
- end
242
+ recursive = recursive ? '-R ' : ''
194
243
  is_dir = dir_exists?(path)
195
244
  is_symlink = symlink?(path)
196
- out = shell("rm #{recursive}-f \"#{path}\" 2>&1")
197
- unless $shell_return_value.success?
198
- STDERR.puts out
199
- STDERR.puts "Couldn't remove path: #{path}"
200
- exit 1
245
+ exit_code, out, = shell("rm #{recursive}-f \"#{path}\" 2>&1")
246
+ unless exit_code.success?
247
+ error out
248
+ fail "Couldn't remove path", path
201
249
  end
202
250
  if is_dir
203
- puts "Removed directory: #{path}"
251
+ info 'Removed directory', path
204
252
  elsif is_symlink
205
- puts "Removed symlink: #{path}"
253
+ info 'Removed symlink', path
206
254
  else
207
- puts "Removed file: #{path}"
255
+ info 'Removed file', path
208
256
  end
209
257
  end
210
258
 
211
259
  # remove directory recursively
212
260
  def rmdir(path)
213
- rm(path,true)
261
+ rm(path, recursive: true)
214
262
  end
215
263
 
216
264
  # touch file
217
265
  def touch(path)
218
266
  path = abs(path)
219
- unless file_exists?(path)
220
- out = shell("touch \"#{path}\" 2>&1")
221
- unless $shell_return_value.success?
222
- STDERR.puts out
223
- STDERR.puts "Couldn't touch file: #{path}"
224
- exit 1
225
- end
226
- puts "Touched file: #{path}"
267
+ return if file_exists?(path)
268
+
269
+ exit_code, out, = shell("touch \"#{path}\" 2>&1")
270
+ unless exit_code.success?
271
+ error out
272
+ fail "Couldn't touch file", path
227
273
  end
274
+ info 'Touched file', path
228
275
  end
229
276
 
230
277
  # change file permissions
231
- def chmod(path,permissions,recursive=false)
278
+ def chmod(path, permissions, recursive: false)
232
279
  path = abs(path)
233
- if recursive
234
- recursive = '-R '
235
- else
236
- recursive = ''
237
- end
280
+ recursive = recursive ? '-R ' : ''
238
281
  shell("chmod #{recursive}#{permissions} \"#{path}\"")
239
282
  end
240
283
 
241
284
  # symlink path
242
- def symlink(from,to)
285
+ def symlink(from, to)
243
286
  from = abs(from)
244
287
  to = abs(to)
245
288
  if symlink?(from)
@@ -248,192 +291,216 @@ def symlink(from,to)
248
291
  symlink from, to
249
292
  end
250
293
  elsif file_exists?(from)
251
- STDERR.puts "File #{from} exists in place of symlink..."
252
- exit 1
294
+ fail "File #{from} exists in place of symlink..."
253
295
  elsif dir_exists?(from)
254
- STDERR.puts "Directory #{from} exists in place of symlink..."
255
- exit 1
296
+ fail "Directory #{from} exists in place of symlink..."
256
297
  else
257
- out = shell("ln -s \"#{to}\" \"#{from}\" 2>&1")
258
- unless $shell_return_value.success?
259
- STDERR.puts out
260
- STDERR.puts "Couldn't create symlink: #{from} → #{to}"
261
- exit 1
298
+ exit_code, out, = shell("ln -s \"#{to}\" \"#{from}\" 2>&1")
299
+ unless exit_code.success?
300
+ error out
301
+ fail "Couldn't create symlink", "#{from} → #{to}"
262
302
  end
263
- puts "Created symlink: #{from} → #{to}"
303
+ info 'Created symlink', "#{from} → #{to}"
264
304
  end
265
305
  end
266
306
 
267
307
  # git clone url into a path
268
- def git(url,path,branch=nil,silent=false)
308
+ def git(url, path, branch: nil, silent: true, depth: nil)
269
309
  path = abs(path)
270
310
  if dir_exists?(path) && dir_exists?("#{path}/.git")
271
- cwd = pwd()
311
+ cwd = pwd
272
312
  chdir path
273
313
  out = nil
274
314
  if branch
275
- out = shell('git fetch 2>&1',{:silent => silent})
276
- if out.size > 0
277
- puts out if silent
278
- end
279
- out = shell("git checkout #{branch} 2>&1",{:silent => silent})
315
+ _, out, = shell('git fetch 2>&1', silent: silent)
316
+ puts out if silent && out.size.positive?
317
+ _, out, = shell("git checkout #{branch} 2>&1", silent: silent)
280
318
  unless out.downcase.include? 'is now at'
281
319
  puts out if silent
282
320
  end
283
- out = shell("git pull origin #{branch} 2>&1",{:silent => silent})
321
+ _, out, = shell("git pull origin #{branch} 2>&1", silent: silent)
284
322
  else
285
- out = shell('git pull 2>&1',{:silent => silent})
323
+ _, out, = shell('git pull 2>&1', silent: silent)
286
324
  end
287
- unless out.downcase.include? "already up-to-date"
325
+ unless out.downcase.include? 'already up'
288
326
  puts out if silent
289
- puts "Git repository pulled: #{url} → #{path}"
327
+ info 'Git repository pulled', "#{url} → #{path}"
290
328
  end
291
329
  chdir cwd
292
330
  else
293
- if branch
294
- branch = "-b #{branch} "
295
- else
296
- branch = ''
297
- end
298
- out = shell("git clone #{branch}#{url} \"#{path}\" 2>&1",{:silent => silent})
331
+ branch = branch ? "-b #{branch} " : ''
332
+ depth = depth ? "--depth #{depth} " : ''
333
+ _, out, = shell("git clone #{depth}#{branch}#{url} \"#{path}\" 2>&1",
334
+ silent: silent)
299
335
  puts out if silent
300
- puts "Git repository cloned: #{url} → #{path}"
336
+ info 'Git repository cloned', "#{url} → #{path}"
301
337
  end
302
338
  end
303
339
 
304
340
  # download url into a path using curl
305
- def curl(url,path,verbose=false)
341
+ def curl(url, path, content_length_check: false, verbose: false)
306
342
  path = abs(path)
307
- output = shell("curl -s -S \"#{url}\"",{:verbose => verbose, :silent => true})
308
- unless $shell_return_value.success?
309
- STDERR.puts output
310
- STDERR.puts "Couldn't download file from #{url}..."
311
- exit 1
343
+ if content_length_check and file_exists?(path)
344
+ content_length = curl_headers(url, verbose: verbose)['content-length'].to_i
345
+ return if file_size(path) == content_length
312
346
  end
313
- if File.exists?(path) && File.read(path) == output
314
- return
347
+ info 'Downloading', "#{url} → #{path}"
348
+ exit_code, output, = shell("curl -s -S \"#{url}\"",
349
+ verbose: verbose, silent: true)
350
+ unless exit_code.success?
351
+ error output
352
+ fail "Couldn't download file from", url
315
353
  end
316
- File.write(path,output)
317
- puts "Downloaded #{url} → #{path}"
354
+ File.write(path, output)
355
+ info 'Downloaded', "#{url} → #{path}"
356
+ end
357
+
358
+ # get url response headers as Hash using curl
359
+ def curl_headers(url, method: 'HEAD', verbose: false)
360
+ exit_code, output, = shell("curl -I -X #{method} -s -S \"#{url}\"",
361
+ verbose: verbose, silent: true)
362
+ unless exit_code.success?
363
+ error output
364
+ fail "Couldn't get headers from", url
365
+ end
366
+ headers = {}
367
+ output.scan(/^(?!HTTP)([^:]+):(.*)$/).each do |m|
368
+ headers[m[0].strip.downcase] = m[1].sub("\r","").strip
369
+ end
370
+ headers
318
371
  end
319
372
 
320
373
  # read content of a file
321
374
  def read(file)
322
375
  file = abs(file)
323
- if file_exists?(file)
324
- return File.read(file)
325
- else
326
- STDERR.puts "Couldn't read file #{file}..."
327
- exit 1
328
- end
376
+ return File.read(file) if file_exists?(file)
377
+
378
+ fail "Couldn't read file", file
329
379
  end
330
380
 
331
381
  # write content into a file
332
- def write(file,content)
382
+ def write(file, content)
333
383
  file = abs(file)
334
- File.write(file,content)
384
+ File.write(file, content)
335
385
  end
336
386
 
337
387
  # source file in sh/bash/zsh script
338
- def source(file,source)
388
+ def source(file, source)
339
389
  file = abs(file)
340
390
  source = abs(source)
341
391
  source_line = "source \"#{source}\""
342
- out = shell("grep -Fx '#{source_line}' \"#{file}\"")
343
- if out == ""
344
- shell("echo '#{source_line}' >> \"#{file}\"")
345
- puts "Added source #{source} line to #{file}"
346
- end
392
+ _, out, = shell("grep -Fx '#{source_line}' \"#{file}\"")
393
+ return unless out == ''
394
+
395
+ shell("echo '#{source_line}' >> \"#{file}\"")
396
+ info 'Added source', "#{source} line to #{file}"
347
397
  end
348
398
 
349
399
  # list directory content
350
- def ls(path,options={})
400
+ def ls(path, dotfiles: false, grep: '', dir: true, file: true)
351
401
  path = abs(path)
352
- if options[:dotfiles]
353
- dotfiles = '-a '
354
- else
355
- dotfiles = ''
356
- end
402
+ dotfiles = dotfiles ? '-a ' : ''
357
403
  command = "ls -1 #{dotfiles}\"#{path}\" 2>&1"
358
- if options.key?(:grep) && options[:grep].length > 0
359
- command += " | grep #{options[:grep]}"
360
- end
361
- output = shell(command)
404
+ command += " | grep #{grep}" unless grep.empty?
405
+
406
+ _, output, = shell(command)
362
407
  if output.downcase.end_with?('no such file or directory')
363
- STDERR.puts "Couldn't ls directory #{path}..."
364
- exit 1
408
+ fail "Couldn't ls directory", path
365
409
  end
410
+
366
411
  ls = output.split("\n")
367
- dir = true
368
- file = true
369
- if options.key?(:dir)
370
- dir = options[:dir]
371
- end
372
- if options.key?(:file)
373
- file = options[:file]
374
- end
375
- unless dir
376
- ls = ls.keep_if {|f| !File.directory?("#{path}/#{f}") }
377
- end
378
- unless file
379
- ls = ls.keep_if {|f| !File.file?("#{path}/#{f}") }
380
- end
381
- return ls
412
+ ls = ls.keep_if { |f| !File.directory?("#{path}/#{f}") } unless dir
413
+ ls = ls.keep_if { |f| !File.file?("#{path}/#{f}") } unless file
414
+ ls
415
+ end
416
+
417
+ # compare_file
418
+ def compare_file(from, to)
419
+ from = abs(from)
420
+ to = abs(to)
421
+ FileUtils.compare_file(from, to)
422
+ end
423
+
424
+ # copy_file
425
+ def copy_file(from, to)
426
+ from = abs(from)
427
+ to = abs(to)
428
+ fail "Couldn't access file", from unless file_exists?(from)
429
+
430
+ return if file_exists?(to) && FileUtils.compare_file(from, to)
431
+
432
+ FileUtils.copy_file(from, to)
433
+ info 'Copied file', "#{from} → #{to}"
382
434
  end
383
435
 
384
436
  # copy
385
- def copy(from,to)
437
+ def copy(from, to)
386
438
  from = abs(from)
387
439
  to = abs(to)
388
440
  if dir_exists?(from)
389
- (Dir.entries(from).select { |f| f != "." and f != ".." }).each do |f|
441
+ dir_entries(from).each do |f|
390
442
  FileUtils.mkdir_p(to)
391
- copy("#{from}/#{f}","#{to}/#{f}")
443
+ copy("#{from}/#{f}", "#{to}/#{f}")
392
444
  end
393
445
  else
394
- unless file_exists?(from)
395
- STDERR.puts "Couldn't access file #{from}..."
396
- exit 1
446
+ copy_file(from, to)
447
+ end
448
+ end
449
+
450
+ # mirror
451
+ def mirror(from, to)
452
+ from = abs(from)
453
+ to = abs(to)
454
+ if dir_exists?(from)
455
+ from_entries = dir_entries(from)
456
+ if dir_exists?(to)
457
+ dir_entries(to).each do |f|
458
+ rm("#{to}/#{f}", recursive: true) unless from_entries.include?(f)
459
+ end
397
460
  end
398
- if !file_exists?(to) or !FileUtils.compare_file(from,to)
399
- FileUtils.copy_file(from,to)
400
- puts "Copied file: #{from} #{to}"
461
+ from_entries.each do |f|
462
+ FileUtils.mkdir_p(to)
463
+ copy("#{from}/#{f}", "#{to}/#{f}")
401
464
  end
465
+ else
466
+ copy_file(from, to)
402
467
  end
403
468
  end
404
469
 
405
470
  # process erb template into an output_file
406
- def erb(file,output_file = nil)
471
+ def erb(file, output_file = nil, monitor = true)
407
472
  file = abs(file)
408
- if output_file == nil
409
- if file.end_with?('.erb')
410
- output_file = file[0...-4]
411
- else
412
- output_file = file+'.out'
413
- end
414
- end
415
- if file_exists?(file)
416
- output = ERB.new(File.read(file)).result
417
- if File.exists?(output_file) && File.read(output_file) == output
418
- return
419
- end
420
- File.write(output_file,output)
421
- puts "Processed erb #{file} → #{output_file}"
422
- else
423
- STDERR.puts "Couldn't process erb file #{file}..."
424
- exit 1
473
+ fail 'No erb file found', file unless file_exists?(file)
474
+
475
+ if output_file.nil?
476
+ output_file = file.end_with?('.erb') ? file[0...-4] : file + '.out'
425
477
  end
478
+
479
+ $monitors[file] = proc { erb(file, output_file, false) } if monitor
480
+
481
+ out = ERB.new(File.read(file)).result
482
+ return if File.exist?(output_file) && File.read(output_file) == out
483
+
484
+ File.write(output_file, out)
485
+ info 'Processed erb', "#{file} → #{output_file}"
486
+ end
487
+
488
+ # monitor file for changes and execute proc if file changed
489
+ def monitor(file, &block)
490
+ file = abs(file)
491
+ fail 'No file found', file unless file_exists?(file)
492
+
493
+ $monitors[file] = block
426
494
  end
427
495
 
428
496
  # run configuration.rb file
429
497
  def run(file)
430
- cwd = pwd()
498
+ cwd = pwd
431
499
  file = abs(file)
432
- unless file_exists?(file)
433
- STDERR.puts "No configuration file: #{file} found"
434
- exit 1
435
- end
436
- puts "Running configuration: "+file
500
+ file = File.join(file,'configuration.rb') if not file_exists?(file) and dir_exists?(file)
501
+ fail 'No configuration file found', file unless file_exists?(file)
502
+
503
+ info 'Running configuration', file
437
504
  file_cwd = file_dir(file)
438
505
  chdir file_cwd
439
506
  load file
@@ -442,15 +509,14 @@ end
442
509
 
443
510
  # run configuration.rb file only if it exists
444
511
  def run_if_exists(file)
445
- if file_exists?(file)
446
- run file
447
- end
512
+ run file if file_exists?(file)
448
513
  end
449
514
 
450
515
  # run central configuration
451
- def central(configurations)
452
- if configurations.instance_of?(Array) && configurations.length > 0
453
- configurations.each {|configuration| run configuration }
516
+ def run_central(configurations, colored = true)
517
+ $colored = colored
518
+ if configurations.instance_of?(Array) && !configurations.empty?
519
+ configurations.each { |configuration| run configuration }
454
520
  elsif configurations.instance_of?(String)
455
521
  run configurations
456
522
  else
@@ -458,3 +524,24 @@ def central(configurations)
458
524
  end
459
525
  end
460
526
 
527
+ # run monitors
528
+ def run_monitors
529
+ info 'Monitoring files for changes (press Ctrl-C to stop)'
530
+ file_mtimes = {}
531
+ $monitors.keys.each { |f| file_mtimes[f] = File.mtime(f) }
532
+ loop do
533
+ $monitors.keys.each do |f|
534
+ file_mtime = File.mtime(f)
535
+ next if file_mtime == file_mtimes[f]
536
+
537
+ info 'File modified', f
538
+ $monitors[f].call
539
+ file_mtimes[f] = file_mtime
540
+ end
541
+ begin
542
+ sleep(0.5)
543
+ rescue Interrupt
544
+ exit 0
545
+ end
546
+ end
547
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: central
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.2.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Geurkov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-14 00:00:00.000000000 Z
11
+ date: 2020-12-31 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: central dotfile management system
14
14
  email: d.geurkov@gmail.com
@@ -31,15 +31,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
31
31
  requirements:
32
32
  - - ">="
33
33
  - !ruby/object:Gem::Version
34
- version: '0'
34
+ version: '2.3'
35
35
  required_rubygems_version: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
39
  version: '0'
40
40
  requirements: []
41
- rubyforge_project:
42
- rubygems_version: 2.5.2
41
+ rubygems_version: 3.1.4
43
42
  signing_key:
44
43
  specification_version: 4
45
44
  summary: central dotfile management