central 0.2.8 → 0.3.2

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.
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