central 0.2.8 → 0.3.2

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 +4 -4
  2. data/bin/central +51 -20
  3. data/lib/central.rb +123 -25
  4. metadata +3 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 673d289d151554b977cdbc987fbd5ea65323e605a9e5b193d5a1908b3738ea62
4
- data.tar.gz: a4a52750319b99b127496db875ddbe5df46378a8021c5b8b77bf6ac0f9457859
3
+ metadata.gz: 81f6417d161a53c2d5b0b2785a1e45cb4af4cc5f8f7b18fd8d24fbb18d1d0410
4
+ data.tar.gz: 706d8b11e4d38effa588a5fe9c5b7baada931a3de3b5973cea7f1ac6c05e8dfd
5
5
  SHA512:
6
- metadata.gz: c1dafe44846a27daf9911281bf721ff68bddc004df2e14822fb7b3e4dd1fa7bb87a8a9cedc197e0ec6c2519aa95c4f73ac8b0ccc85d652685bf46ca0f763e339
7
- data.tar.gz: '016499d9cd74e3a1a6bb78a123a60f44ddff74bb25198bb205eda2d8f90834467f4b3cf274544ef4d181cde77ed99e3ebf702f68d328314162808f2761633550'
6
+ metadata.gz: 1e36f5d6f5c9730c95240fc674020af1d557f57054cb9d4ff1eb15ccaae14776c54060ea662759766bc899ef6f4a85a901e0eec34141742d83707e61a8c65f3c
7
+ data.tar.gz: cbfb6d079499dfcdadbb767f5f676292e43e933083f6d13d7008ceb07c31da0c8cc8995931219fac16a1d19a71ea9a39611c4e10f0ce82dc7b6f6db8a6358c88
data/bin/central CHANGED
@@ -7,29 +7,60 @@
7
7
  # -------------------------------------------------------------------------
8
8
 
9
9
  require 'central'
10
+ require 'optparse'
10
11
 
11
- unless ARGV.empty?
12
- if ARGV[0] == '-v' || ARGV[0] == '--version' || ARGV[0] == '-version'
13
- puts 'central v0.2.8'
14
- exit 0
15
- end
16
- if ARGV[0] == '-h' || ARGV[0] == '--help' || ARGV[0] == '-help'
17
- puts 'central v0.2.8 - created by Dmitry Geurkov (d.geurkov@gmail.com)'
18
- puts ' and licensed under LGPLv3'
19
- puts ''
20
- puts 'Usage: central [monitor] [path/to/configuration.rb]'
21
- puts ''
22
- puts 'Description: [monitor] - will monitor erb files for changes'
23
- puts ' reprocess them automatically, disabled by default'
24
- puts ' if no [path/to/configuration.rb] is specified'
25
- puts ' it will use configuration.rb in current working directory'
26
- exit 0
12
+ VERSION = 'v0.3.2'
13
+
14
+ # parse extra options
15
+ ARGV.each do |option|
16
+ if not option.empty? and option.index('-') == 0 and not ['-h', '--help', '-i', '--irb', '-n', '--no-color', '-v', '--version', '-m', '--monitor'].include?(option)
17
+ $options.append(option)
27
18
  end
28
19
  end
29
- if ARGV[0] == 'monitor'
30
- ARGV.shift
31
- run_central(ARGV)
20
+ ARGV.delete_if { |option| $options.include?(option) }
21
+
22
+ # parse central options
23
+ options = {:color => true}
24
+ OptionParser.new do |opts|
25
+ opts.banner = %{central #{VERSION} - created by Dmitry Geurkov (d.geurkov@gmail.com) and licensed under LGPLv3
26
+
27
+ Usage: central [options] [directory|configuration.rb, ...]
28
+
29
+ Description: if directory is specified it will use configuration.rb file inside that directory
30
+ if no [directory|configuration.rb] is specified
31
+ it will use configuration.rb in current working directory
32
+
33
+ Options:}
34
+ opts.on("-i", "--irb", "Start interactive IRB REPL") do |_|
35
+ options[:irb] = true
36
+ end
37
+
38
+ opts.on("-m", "--monitor", "Monitor erb files for changes and reprocess them automatically") do |_|
39
+ options[:monitor] = true
40
+ end
41
+
42
+ opts.on("-n", "--no-color", "Do not colorize output") do |_|
43
+ options[:color] = false
44
+ end
45
+
46
+ opts.on("-v", "--version", "Print version") do |_|
47
+ puts "central #{VERSION}"
48
+ exit
49
+ end
50
+ end.parse!
51
+
52
+ if options[:irb]
53
+ require 'irb'
54
+ $colored = true
55
+ $colored = options[:color] if options[:color]
56
+ info "Welcome to central #{VERSION} IRB repl, interactive playground for your dotcentral configuration"
57
+ info "Please ignore warning below as central has source function, and IRB tries to create alias for irb_source"
58
+ info "In order to use IRB's source functionality just call it without alias as irb_source"
59
+ ARGV.clear
60
+ IRB.start(__FILE__)
61
+ elsif options[:monitor]
62
+ run_central(ARGV,options[:color])
32
63
  run_monitors
33
64
  else
34
- run_central(ARGV)
65
+ run_central(ARGV,options[:color])
35
66
  end
data/lib/central.rb CHANGED
@@ -9,8 +9,25 @@ require 'erb'
9
9
  require 'socket'
10
10
  require 'open3'
11
11
  require 'fileutils'
12
+ require 'digest'
13
+
14
+ # options
15
+ $options = []
16
+
17
+ # get option, returns Array if multiple or nil if none
18
+ def option(opt)
19
+ options = $options.filter { |option| option.index(opt) == 0 }
20
+ if options.size == 0
21
+ return nil
22
+ elsif options.size == 1
23
+ return options[0]
24
+ else
25
+ return options
26
+ end
27
+ end
12
28
 
13
29
  # cli colors
30
+ $colored = true
14
31
  COLOR_RED = 31
15
32
  COLOR_GREEN = 32
16
33
 
@@ -19,7 +36,11 @@ $monitors = {}
19
36
 
20
37
  # putsc, puts with color
21
38
  def color(message, color)
22
- "\e[#{color}m#{message}\e[0m"
39
+ if $colored
40
+ "\e[#{color}m#{message}\e[0m"
41
+ else
42
+ message
43
+ end
23
44
  end
24
45
 
25
46
  # info
@@ -66,6 +87,10 @@ def osx?
66
87
  os == 'osx'
67
88
  end
68
89
 
90
+ def macos?
91
+ os == 'osx'
92
+ end
93
+
69
94
  def freebsd?
70
95
  os == 'freebsd'
71
96
  end
@@ -80,24 +105,16 @@ def shell(command, verbose: false, silent: true)
80
105
  info 'Executing', command if verbose
81
106
  exit_code = nil
82
107
  stdout = String.new
83
- stdout_line = String.new
84
108
  stderr = String.new
85
- stderr_line = String.new
86
109
  Open3.popen3(command) do |_, o, e, t|
87
110
  stdout_open = true
88
111
  stderr_open = true
89
112
  while stdout_open || stderr_open
90
113
  if stdout_open
91
114
  begin
92
- ch = o.read_nonblock(1)
93
- stdout.insert(-1, ch)
94
- unless silent
95
- stdout_line.insert(-1, ch)
96
- if ch == "\n"
97
- STDOUT.puts stdout_line
98
- stdout_line = ''
99
- end
100
- end
115
+ buffer = o.read_nonblock(4096)
116
+ stdout << buffer
117
+ STDOUT.write(buffer) unless silent
101
118
  rescue IO::WaitReadable
102
119
  IO.select([o], nil, nil, 0.01) unless stderr_open
103
120
  rescue EOFError
@@ -107,15 +124,9 @@ def shell(command, verbose: false, silent: true)
107
124
  next unless stderr_open
108
125
 
109
126
  begin
110
- ch = e.read_nonblock(1)
111
- stderr.insert(-1, ch)
112
- unless silent
113
- stderr_line.insert(-1, ch)
114
- if ch == "\n"
115
- STDERR.puts stderr_line
116
- stderr_line = ''
117
- end
118
- end
127
+ buffer = e.read_nonblock(4096)
128
+ stderr << buffer
129
+ STDERR.write(buffer) unless silent
119
130
  rescue IO::WaitReadable
120
131
  IO.select([e], nil, nil, 0.01) unless stdout_open
121
132
  rescue EOFError
@@ -170,13 +181,45 @@ def file_exists?(path)
170
181
  File.file?(path) && File.readable?(path)
171
182
  end
172
183
 
184
+ # get file size
185
+ def file_size(path)
186
+ File.new(abs(path)).size
187
+ end
188
+
189
+ # get file creation time
190
+ def file_ctime(path)
191
+ File.new(abs(path)).ctime
192
+ end
193
+
194
+ # get file modification time
195
+ def file_mtime(path)
196
+ File.new(abs(path)).mtime
197
+ end
198
+
199
+ # get directory entries
200
+ def dir_entries(path)
201
+ Dir.entries(abs(path)).select { |f| f != '.' && f != '..' }
202
+ end
203
+
173
204
  # check if directory exists
174
205
  def dir_exists?(path)
175
206
  path = abs(path)
176
207
  Dir.exist?(path)
177
208
  end
178
209
 
179
- # get directory name of a file
210
+ # get file name of a path, optionally strip suffix if needed
211
+ def file_name(path, strip_suffix: '')
212
+ File.basename(abs(path), strip_suffix)
213
+ end
214
+
215
+ # get file suffix of a path
216
+ def file_suffix(path)
217
+ path = file_name(path)
218
+ suffix_index = path =~ /\.[^.]+$/
219
+ return path[suffix_index, path.size - suffix_index] if suffix_index
220
+ end
221
+
222
+ # get directory name of a path
180
223
  def file_dir(path)
181
224
  File.dirname(abs(path))
182
225
  end
@@ -192,6 +235,11 @@ def symlink_path(symlink)
192
235
  out.strip
193
236
  end
194
237
 
238
+ # calculate SHA2 digest for a file
239
+ def sha2(file)
240
+ Digest::SHA256.hexdigest read(file)
241
+ end
242
+
195
243
  # make directory including intermediate directories
196
244
  def mkdir(path)
197
245
  path = abs(path)
@@ -307,8 +355,12 @@ def git(url, path, branch: nil, silent: true, depth: nil)
307
355
  end
308
356
 
309
357
  # download url into a path using curl
310
- def curl(url, path, verbose: false)
358
+ def curl(url, path, content_length_check: false, verbose: false)
311
359
  path = abs(path)
360
+ if content_length_check and file_exists?(path)
361
+ content_length = curl_headers(url, verbose: verbose)['content-length'].to_i
362
+ return if file_size(path) == content_length
363
+ end
312
364
  info 'Downloading', "#{url} → #{path}"
313
365
  exit_code, output, = shell("curl -s -S \"#{url}\"",
314
366
  verbose: verbose, silent: true)
@@ -320,6 +372,21 @@ def curl(url, path, verbose: false)
320
372
  info 'Downloaded', "#{url} → #{path}"
321
373
  end
322
374
 
375
+ # get url response headers as Hash using curl
376
+ def curl_headers(url, method: 'HEAD', verbose: false)
377
+ exit_code, output, = shell("curl -I -X #{method} -s -S \"#{url}\"",
378
+ verbose: verbose, silent: true)
379
+ unless exit_code.success?
380
+ error output
381
+ fail "Couldn't get headers from", url
382
+ end
383
+ headers = {}
384
+ output.scan(/^(?!HTTP)([^:]+):(.*)$/).each do |m|
385
+ headers[m[0].strip.downcase] = m[1].sub("\r","").strip
386
+ end
387
+ headers
388
+ end
389
+
323
390
  # read content of a file
324
391
  def read(file)
325
392
  file = abs(file)
@@ -364,8 +431,17 @@ def ls(path, dotfiles: false, grep: '', dir: true, file: true)
364
431
  ls
365
432
  end
366
433
 
434
+ # compare_file
435
+ def compare_file(from, to)
436
+ from = abs(from)
437
+ to = abs(to)
438
+ FileUtils.compare_file(from, to)
439
+ end
440
+
367
441
  # copy_file
368
442
  def copy_file(from, to)
443
+ from = abs(from)
444
+ to = abs(to)
369
445
  fail "Couldn't access file", from unless file_exists?(from)
370
446
 
371
447
  return if file_exists?(to) && FileUtils.compare_file(from, to)
@@ -379,7 +455,27 @@ def copy(from, to)
379
455
  from = abs(from)
380
456
  to = abs(to)
381
457
  if dir_exists?(from)
382
- (Dir.entries(from).select { |f| f != '.' && f != '..' }).each do |f|
458
+ dir_entries(from).each do |f|
459
+ FileUtils.mkdir_p(to)
460
+ copy("#{from}/#{f}", "#{to}/#{f}")
461
+ end
462
+ else
463
+ copy_file(from, to)
464
+ end
465
+ end
466
+
467
+ # mirror
468
+ def mirror(from, to)
469
+ from = abs(from)
470
+ to = abs(to)
471
+ if dir_exists?(from)
472
+ from_entries = dir_entries(from)
473
+ if dir_exists?(to)
474
+ dir_entries(to).each do |f|
475
+ rm("#{to}/#{f}", recursive: true) unless from_entries.include?(f)
476
+ end
477
+ end
478
+ from_entries.each do |f|
383
479
  FileUtils.mkdir_p(to)
384
480
  copy("#{from}/#{f}", "#{to}/#{f}")
385
481
  end
@@ -418,6 +514,7 @@ end
418
514
  def run(file)
419
515
  cwd = pwd
420
516
  file = abs(file)
517
+ file = File.join(file,'configuration.rb') if not file_exists?(file) and dir_exists?(file)
421
518
  fail 'No configuration file found', file unless file_exists?(file)
422
519
 
423
520
  info 'Running configuration', file
@@ -433,7 +530,8 @@ def run_if_exists(file)
433
530
  end
434
531
 
435
532
  # run central configuration
436
- def run_central(configurations)
533
+ def run_central(configurations, colored = true)
534
+ $colored = colored
437
535
  if configurations.instance_of?(Array) && !configurations.empty?
438
536
  configurations.each { |configuration| run configuration }
439
537
  elsif configurations.instance_of?(String)
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.8
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Geurkov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-22 00:00:00.000000000 Z
11
+ date: 2022-02-11 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: central dotfile management system
14
14
  email: d.geurkov@gmail.com
@@ -38,8 +38,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
38
38
  - !ruby/object:Gem::Version
39
39
  version: '0'
40
40
  requirements: []
41
- rubyforge_project:
42
- rubygems_version: 2.7.6
41
+ rubygems_version: 3.2.22
43
42
  signing_key:
44
43
  specification_version: 4
45
44
  summary: central dotfile management