Top Level Namespace

Constant Summary collapse

COLOR_RED =
31
COLOR_GREEN =
32

Instance Method Summary collapse

Instance Method Details

#abs(path) ⇒ Object

absolute path



183
184
185
# File 'lib/central.rb', line 183

def abs(path)
  File.absolute_path(File.expand_path(path))
end

#chdir(dir) ⇒ Object

change current working directory



188
189
190
# File 'lib/central.rb', line 188

def chdir(dir)
  Dir.chdir(abs(dir))
end

#check_tool(name, check) ⇒ Object

function used to check that system has all required tools installed



161
162
163
164
165
166
167
168
# File 'lib/central.rb', line 161

def check_tool(name, check)
  _, output, = shell(check + ' 2>&1')
  if output == '' || output.downcase.include?('command not found')
    fail "#{name} not found, please install it to use central"
  end
rescue Errno::ENOENT
  fail "#{name} not found, please install it to use central"
end

#chmod(path, permissions, recursive: false) ⇒ Object

change file permissions



309
310
311
312
313
# File 'lib/central.rb', line 309

def chmod(path, permissions, recursive: false)
  path = abs(path)
  recursive = recursive ? '-R ' : ''
  shell("chmod #{recursive}#{permissions} \"#{path}\"")
end

#color(message, color) ⇒ Object

putsc, puts with color



38
39
40
41
42
43
44
# File 'lib/central.rb', line 38

def color(message, color)
  if $colored
    "\e[#{color}m#{message}\e[0m"
  else
    message
  end
end

#compare_file(from, to) ⇒ Object

compare_file



449
450
451
452
453
# File 'lib/central.rb', line 449

def compare_file(from, to)
  from = abs(from)
  to = abs(to)
  FileUtils.compare_file(from, to)
end

#copy(from, to) ⇒ Object

copy



468
469
470
471
472
473
474
475
476
477
478
479
# File 'lib/central.rb', line 468

def copy(from, to)
  from = abs(from)
  to = abs(to)
  if dir_exists?(from)
    dir_entries(from).each do |f|
      FileUtils.mkdir_p(to)
      copy("#{from}/#{f}", "#{to}/#{f}")
    end
  else
    copy_file(from, to)
  end
end

#copy_file(from, to) ⇒ Object

copy_file



456
457
458
459
460
461
462
463
464
465
# File 'lib/central.rb', line 456

def copy_file(from, to)
  from = abs(from)
  to = abs(to)
  fail "Couldn't access file", from unless file_exists?(from)

  return if file_exists?(to) && FileUtils.compare_file(from, to)

  FileUtils.copy_file(from, to)
  info 'Copied file', "#{from}#{to}"
end

#curl(url, path, content_length_check: false, verbose: false) ⇒ Object

download url into a path using curl



372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
# File 'lib/central.rb', line 372

def curl(url, path, content_length_check: false, verbose: false)
  path = abs(path)
  if content_length_check and file_exists?(path)
    content_length = curl_headers(url, verbose: verbose)['content-length'].to_i
    return if file_size(path) == content_length
  end
  info 'Downloading', "#{url}#{path}"
  exit_code, output, = shell("curl -s -S \"#{url}\"",
                             verbose: verbose, silent: true)
  unless exit_code.success?
    error output
    fail "Couldn't download file from", url
  end
  File.write(path, output)
  info 'Downloaded', "#{url}#{path}"
end

#curl_headers(url, method: 'HEAD', verbose: false) ⇒ Object

get url response headers as Hash using curl



390
391
392
393
394
395
396
397
398
399
400
401
402
# File 'lib/central.rb', line 390

def curl_headers(url, method: 'HEAD', verbose: false)
  exit_code, output, = shell("curl -I -X #{method} -s -S \"#{url}\"",
                             verbose: verbose, silent: true)
  unless exit_code.success?
    error output
    fail "Couldn't get headers from", url
  end
  headers = {}
  output.scan(/^(?!HTTP)([^:]+):(.*)$/).each do |m|
    headers[m[0].strip.downcase] = m[1].sub("\r","").strip
  end
  headers
end

#dir_entries(path) ⇒ Object

get directory entries



214
215
216
# File 'lib/central.rb', line 214

def dir_entries(path)
  Dir.entries(abs(path)).select { |f| f != '.' && f != '..' }
end

#dir_exists?(path) ⇒ Boolean

check if directory exists

Returns:

  • (Boolean)


219
220
221
222
# File 'lib/central.rb', line 219

def dir_exists?(path)
  path = abs(path)
  Dir.exist?(path)
end

#erb(file, output_file = nil, monitor = true) ⇒ Object

process erb template into an output_file



502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
# File 'lib/central.rb', line 502

def erb(file, output_file = nil, monitor = true)
  file = abs(file)
  fail 'No erb file found', file unless file_exists?(file)

  if output_file.nil?
    output_file = file.end_with?('.erb') ? file[0...-4] : file + '.out'
  end

  $monitors[file] = proc { erb(file, output_file, false) } if monitor

  out = ERB.new(File.read(file)).result
  return if File.exist?(output_file) && File.read(output_file) == out

  File.write(output_file, out)
  info 'Processed erb', "#{file}#{output_file}"
end

#error(message, param = nil) ⇒ Object

error



53
54
55
56
# File 'lib/central.rb', line 53

def error(message, param = nil)
  puts color(message, COLOR_RED) +
       (param.nil? ? '' : ': ' + param)
end

#fail(message, param = nil) ⇒ Object

fail, print message to stderr and exit with 1



59
60
61
62
# File 'lib/central.rb', line 59

def fail(message, param = nil)
  error message, param
  exit 1
end

#file_ctime(path) ⇒ Object

get file creation time



204
205
206
# File 'lib/central.rb', line 204

def file_ctime(path)
  File.new(abs(path)).ctime
end

#file_dir(path) ⇒ Object

get directory name of a path



237
238
239
# File 'lib/central.rb', line 237

def file_dir(path)
  File.dirname(abs(path))
end

#file_exists?(path) ⇒ Boolean

check if file exists

Returns:

  • (Boolean)


193
194
195
196
# File 'lib/central.rb', line 193

def file_exists?(path)
  path = abs(path)
  File.file?(path) && File.readable?(path)
end

#file_mtime(path) ⇒ Object

get file modification time



209
210
211
# File 'lib/central.rb', line 209

def file_mtime(path)
  File.new(abs(path)).mtime
end

#file_name(path, strip_suffix: '') ⇒ Object

get file name of a path, optionally strip suffix if needed



225
226
227
# File 'lib/central.rb', line 225

def file_name(path, strip_suffix: '')
  File.basename(abs(path), strip_suffix)
end

#file_size(path) ⇒ Object

get file size



199
200
201
# File 'lib/central.rb', line 199

def file_size(path)
  File.new(abs(path)).size
end

#file_suffix(path) ⇒ Object

get file suffix of a path



230
231
232
233
234
# File 'lib/central.rb', line 230

def file_suffix(path)
  path = file_name(path)
  suffix_index = path =~ /\.[^.]+$/
  return path[suffix_index, path.size - suffix_index] if suffix_index
end

#freebsd?Boolean

Returns:

  • (Boolean)


94
95
96
# File 'lib/central.rb', line 94

def freebsd?
  os == 'freebsd'
end

#git(url, path, branch: nil, silent: true, depth: nil) ⇒ Object

git clone url into a path



339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
# File 'lib/central.rb', line 339

def git(url, path, branch: nil, silent: true, depth: nil)
  path = abs(path)
  if dir_exists?(path) && dir_exists?("#{path}/.git")
    cwd = pwd
    chdir path
    out = nil
    if branch
      _, out, = shell('git fetch 2>&1', silent: silent)
      puts out if silent && out.size.positive?
      _, out, = shell("git checkout #{branch} 2>&1", silent: silent)
      unless out.downcase.include? 'is now at'
        puts out if silent
      end
      _, out, = shell("git pull origin #{branch} 2>&1", silent: silent)
    else
      _, out, = shell('git pull 2>&1', silent: silent)
    end
    unless out.downcase.include? 'already up'
      puts out if silent
      info 'Git repository pulled', "#{url}#{path}"
    end
    chdir cwd
  else
    branch = branch ? "-b #{branch} " : ''
    depth = depth ? "--depth #{depth} " : ''
    _, out, = shell("git clone #{depth}#{branch}#{url} \"#{path}\" 2>&1",
                    silent: silent)
    puts out if silent
    info 'Git repository cloned', "#{url}#{path}"
  end
end

#hostnameObject

get hostname



65
66
67
# File 'lib/central.rb', line 65

def hostname
  Socket.gethostname
end

#info(message, param = nil) ⇒ Object

info



47
48
49
50
# File 'lib/central.rb', line 47

def info(message, param = nil)
  puts color(message, COLOR_GREEN) +
       (param.nil? ? '' : ': ' + param)
end

#linux?Boolean

Returns:

  • (Boolean)


82
83
84
# File 'lib/central.rb', line 82

def linux?
  os == 'linux'
end

#ls(path, dotfiles: false, grep: '', dir: true, file: true) ⇒ Object

list directory content



431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
# File 'lib/central.rb', line 431

def ls(path, dotfiles: false, grep: '', dir: true, file: true)
  path = abs(path)
  dotfiles = dotfiles ? '-a ' : ''
  command = "ls -1 #{dotfiles}\"#{path}\" 2>&1"
  command += " | grep #{grep}" unless grep.empty?

  _, output, = shell(command)
  if output.downcase.end_with?('no such file or directory')
    fail "Couldn't ls directory", path
  end

  ls = output.split("\n")
  ls = ls.keep_if { |f| !File.directory?("#{path}/#{f}") } unless dir
  ls = ls.keep_if { |f| !File.file?("#{path}/#{f}") } unless file
  ls
end

#macos?Boolean

Returns:

  • (Boolean)


90
91
92
# File 'lib/central.rb', line 90

def macos?
  os == 'osx'
end

#mirror(from, to) ⇒ Object

mirror



482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
# File 'lib/central.rb', line 482

def mirror(from, to)
  from = abs(from)
  to = abs(to)
  if dir_exists?(from)
    from_entries = dir_entries(from)
    if dir_exists?(to)
      dir_entries(to).each do |f|
        rm("#{to}/#{f}", recursive: true) unless from_entries.include?(f)
      end
    end
    from_entries.each do |f|
      FileUtils.mkdir_p(to)
      copy("#{from}/#{f}", "#{to}/#{f}")
    end
  else
    copy_file(from, to)
  end
end

#mkdir(path) ⇒ Object

make directory including intermediate directories



258
259
260
261
262
263
264
265
266
267
268
# File 'lib/central.rb', line 258

def mkdir(path)
  path = abs(path)
  return if dir_exists?(path)

  exit_code, out, = shell("mkdir -p \"#{path}\" 2>&1")
  unless exit_code.success?
    error out
    fail "Couldn't create directory", path
  end
  info 'Created directory', path
end

#monitor(file, &block) ⇒ Object

monitor file for changes and execute proc if file changed



520
521
522
523
524
525
# File 'lib/central.rb', line 520

def monitor(file, &block)
  file = abs(file)
  fail 'No file found', file unless file_exists?(file)

  $monitors[file] = block
end

#option(opt) ⇒ Object

get option, returns Array if multiple or nil if none



18
19
20
21
22
23
24
25
26
27
# File 'lib/central.rb', line 18

def option(opt)
  options = $options.filter { |option| option.index(opt) == 0 }
  if options.size == 0
    return nil
  elsif options.size == 1
    return options[0]
  else
    return options
  end
end

#osObject

get operating system



70
71
72
73
74
75
76
77
78
79
80
# File 'lib/central.rb', line 70

def os
  if RUBY_PLATFORM.include?('linux')
    'linux'
  elsif RUBY_PLATFORM.include?('darwin')
    'osx'
  elsif RUBY_PLATFORM.include?('freebsd')
    'freebsd'
  elsif RUBY_PLATFORM.include?('solaris')
    'solaris'
  end
end

#osx?Boolean

Returns:

  • (Boolean)


86
87
88
# File 'lib/central.rb', line 86

def osx?
  os == 'osx'
end

#pwdObject

current working directory



178
179
180
# File 'lib/central.rb', line 178

def pwd
  Dir.pwd
end

#read(file) ⇒ Object

read content of a file



405
406
407
408
409
410
# File 'lib/central.rb', line 405

def read(file)
  file = abs(file)
  return File.read(file) if file_exists?(file)

  fail "Couldn't read file", file
end

#rm(path, recursive: false) ⇒ Object

remove file/directory



271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# File 'lib/central.rb', line 271

def rm(path, recursive: false)
  path = abs(path)
  recursive = recursive ? '-R ' : ''
  is_dir = dir_exists?(path)
  is_symlink = symlink?(path)
  exit_code, out, = shell("rm #{recursive}-f \"#{path}\" 2>&1")
  unless exit_code.success?
    error out
    fail "Couldn't remove path", path
  end
  if is_dir
    info 'Removed directory', path
  elsif is_symlink
    info 'Removed symlink', path
  else
    info 'Removed file', path
  end
end

#rmdir(path) ⇒ Object

remove directory recursively



291
292
293
# File 'lib/central.rb', line 291

def rmdir(path)
  rm(path, recursive: true)
end

#run(file) ⇒ Object

run configuration.rb file



528
529
530
531
532
533
534
535
536
537
538
539
# File 'lib/central.rb', line 528

def run(file)
  cwd = pwd
  file = abs(file)
  file = File.join(file,'configuration.rb') if not file_exists?(file) and dir_exists?(file)
  fail 'No configuration file found', file unless file_exists?(file)

  info 'Running configuration', file
  file_cwd = file_dir(file)
  chdir file_cwd
  load file
  chdir cwd
end

#run_central(configurations, colored = true) ⇒ Object

run central configuration



547
548
549
550
551
552
553
554
555
556
# File 'lib/central.rb', line 547

def run_central(configurations, colored = true)
  $colored = colored
  if configurations.instance_of?(Array) && !configurations.empty?
    configurations.each { |configuration| run configuration }
  elsif configurations.instance_of?(String)
    run configurations
  else
    run 'configuration.rb'
  end
end

#run_if_exists(file) ⇒ Object

run configuration.rb file only if it exists



542
543
544
# File 'lib/central.rb', line 542

def run_if_exists(file)
  run file if file_exists?(file)
end

#run_monitorsObject

run monitors



559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
# File 'lib/central.rb', line 559

def run_monitors
  info 'Monitoring files for changes (press Ctrl-C to stop)'
  file_mtimes = {}
  $monitors.keys.each { |f| file_mtimes[f] = File.mtime(f) }
  loop do
    $monitors.keys.each do |f|
      file_mtime = File.mtime(f)
      next if file_mtime == file_mtimes[f]

      info 'File modified', f
      $monitors[f].call
      file_mtimes[f] = file_mtime
    end
    begin
      sleep(0.5)
    rescue Interrupt
      exit 0
    end
  end
end

#sha2(file) ⇒ Object

calculate SHA2 digest for a file



253
254
255
# File 'lib/central.rb', line 253

def sha2(file)
  Digest::SHA256.hexdigest read(file)
end

#shell(command, verbose: false, silent: true) ⇒ Object

run shell command and get output, optionaly can print command running if verbose and if not silent will also print to stdout and stderr



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/central.rb', line 104

def shell(command, verbose: false, silent: true)
  info 'Executing', command if verbose
  exit_code = nil
  stdout = String.new
  stdout_line = String.new
  stderr = String.new
  stderr_line = String.new
  Open3.popen3(command) do |_, o, e, t|
    stdout_open = true
    stderr_open = true
    while stdout_open || stderr_open
      if stdout_open
        begin
          ch = o.read_nonblock(1)
          stdout += ch
          unless silent
            stdout_line += ch
            if ch == "\n"
              STDOUT.puts stdout_line
              stdout_line = ''
            end
          end
        rescue IO::WaitReadable
          IO.select([o], nil, nil, 0.01) unless stderr_open
        rescue EOFError
          stdout_open = false
        end
      end
      next unless stderr_open

      begin
        ch = e.read_nonblock(1)
        stderr += ch
        unless silent
          stderr_line += ch
          if ch == "\n"
            STDERR.puts stderr_line
            stderr_line = ''
          end
        end
      rescue IO::WaitReadable
        IO.select([e], nil, nil, 0.01) unless stdout_open
      rescue EOFError
        stderr_open = false
      end
    end
    exit_code = t.value
  end
  [exit_code, stdout, stderr]
end

#solaris?Boolean

Returns:

  • (Boolean)


98
99
100
# File 'lib/central.rb', line 98

def solaris?
  os == 'solaris'
end

#source(file, source) ⇒ Object

source file in sh/bash/zsh script



419
420
421
422
423
424
425
426
427
428
# File 'lib/central.rb', line 419

def source(file, source)
  file = abs(file)
  source = abs(source)
  source_line = "source \"#{source}\""
  _, out, = shell("grep -Fx '#{source_line}' \"#{file}\"")
  return unless out == ''

  shell("echo '#{source_line}' >> \"#{file}\"")
  info 'Added source', "#{source} line to #{file}"
end

#sudo(command, verbose:, silent:) ⇒ Object

run shell command with sudo prefix, acts same as shell



156
157
158
# File 'lib/central.rb', line 156

def sudo(command, verbose:, silent:)
  shell('sudo ' + command, verbose: verbose, silent: silent)
end

symlink path



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/central.rb', line 316

def symlink(from, to)
  from = abs(from)
  to = abs(to)
  if symlink?(from)
    if symlink_path(from) != to
      rm from
      symlink from, to
    end
  elsif file_exists?(from)
    fail "File #{from} exists in place of symlink..."
  elsif dir_exists?(from)
    fail "Directory #{from} exists in place of symlink..."
  else
    exit_code, out, = shell("ln -s \"#{to}\" \"#{from}\" 2>&1")
    unless exit_code.success?
      error out
      fail "Couldn't create symlink", "#{from}#{to}"
    end
    info 'Created symlink', "#{from}#{to}"
  end
end

#symlink?(symlink) ⇒ Boolean

check if file is symlink

Returns:

  • (Boolean)


242
243
244
# File 'lib/central.rb', line 242

def symlink?(symlink)
  File.symlink?(abs(symlink))
end

get full path of symlink



247
248
249
250
# File 'lib/central.rb', line 247

def symlink_path(symlink)
  _, out, = shell("readlink \"#{abs(symlink)}\" 2>&1")
  out.strip
end

#touch(path) ⇒ Object

touch file



296
297
298
299
300
301
302
303
304
305
306
# File 'lib/central.rb', line 296

def touch(path)
  path = abs(path)
  return if file_exists?(path)

  exit_code, out, = shell("touch \"#{path}\" 2>&1")
  unless exit_code.success?
    error out
    fail "Couldn't touch file", path
  end
  info 'Touched file', path
end

#write(file, content) ⇒ Object

write content into a file



413
414
415
416
# File 'lib/central.rb', line 413

def write(file, content)
  file = abs(file)
  File.write(file, content)
end