class Object

Constants

COLOR_GREEN
COLOR_RED

Public Instance Methods

abs(path) click to toggle source

absolute path

# File lib/central.rb, line 183
def abs(path)
  File.absolute_path(File.expand_path(path))
end
chdir(dir) click to toggle source

change current working directory

# File lib/central.rb, line 188
def chdir(dir)
  Dir.chdir(abs(dir))
end
check_tool(name, check) click to toggle source

function used to check that system has all required tools installed

# 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) click to toggle source

change file permissions

# 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) click to toggle source

putsc, puts with color

# 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) click to toggle source

compare_file

# 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) click to toggle source

copy

# 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) click to toggle source

copy_file

# 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) click to toggle source

download url into a path using curl

# 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) click to toggle source

get url response headers as Hash using curl

# 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) click to toggle source

get directory entries

# File lib/central.rb, line 214
def dir_entries(path)
  Dir.entries(abs(path)).select { |f| f != '.' && f != '..' }
end
dir_exists?(path) click to toggle source

check if directory exists

# File lib/central.rb, line 219
def dir_exists?(path)
  path = abs(path)
  Dir.exist?(path)
end
erb(file, output_file = nil, monitor = true) click to toggle source

process erb template into an output_file

# 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) click to toggle source

error

# File lib/central.rb, line 53
def error(message, param = nil)
  puts color(message, COLOR_RED) +
       (param.nil? ? '' : ': ' + param)
end
fail(message, param = nil) click to toggle source

fail, print message to stderr and exit with 1

# File lib/central.rb, line 59
def fail(message, param = nil)
  error message, param
  exit 1
end
file_ctime(path) click to toggle source

get file creation time

# File lib/central.rb, line 204
def file_ctime(path)
  File.new(abs(path)).ctime
end
file_dir(path) click to toggle source

get directory name of a path

# File lib/central.rb, line 237
def file_dir(path)
  File.dirname(abs(path))
end
file_exists?(path) click to toggle source

check if file exists

# File lib/central.rb, line 193
def file_exists?(path)
  path = abs(path)
  File.file?(path) && File.readable?(path)
end
file_mtime(path) click to toggle source

get file modification time

# File lib/central.rb, line 209
def file_mtime(path)
  File.new(abs(path)).mtime
end
file_name(path, strip_suffix: '') click to toggle source

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

# File lib/central.rb, line 225
def file_name(path, strip_suffix: '')
  File.basename(abs(path), strip_suffix)
end
file_size(path) click to toggle source

get file size

# File lib/central.rb, line 199
def file_size(path)
  File.new(abs(path)).size
end
file_suffix(path) click to toggle source

get file suffix of a path

# 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?() click to toggle source
# File lib/central.rb, line 94
def freebsd?
  os == 'freebsd'
end
git(url, path, branch: nil, silent: true, depth: nil) click to toggle source

git clone url into a path

# 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
hostname() click to toggle source

get hostname

# File lib/central.rb, line 65
def hostname
  Socket.gethostname
end
info(message, param = nil) click to toggle source

info

# File lib/central.rb, line 47
def info(message, param = nil)
  puts color(message, COLOR_GREEN) +
       (param.nil? ? '' : ': ' + param)
end
linux?() click to toggle source
# File lib/central.rb, line 82
def linux?
  os == 'linux'
end
ls(path, dotfiles: false, grep: '', dir: true, file: true) click to toggle source

list directory content

# 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?() click to toggle source
# File lib/central.rb, line 90
def macos?
  os == 'osx'
end
mirror(from, to) click to toggle source

mirror

# 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) click to toggle source

make directory including intermediate directories

# 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) click to toggle source

monitor file for changes and execute proc if file changed

# 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) click to toggle source

get option, returns Array if multiple or nil if none

# 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
os() click to toggle source

get operating system

# 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?() click to toggle source
# File lib/central.rb, line 86
def osx?
  os == 'osx'
end
pwd() click to toggle source

current working directory

# File lib/central.rb, line 178
def pwd
  Dir.pwd
end
read(file) click to toggle source

read content of a file

# 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) click to toggle source

remove file/directory

# 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) click to toggle source

remove directory recursively

# File lib/central.rb, line 291
def rmdir(path)
  rm(path, recursive: true)
end
run(file) click to toggle source

run configuration.rb file

# 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) click to toggle source

run central configuration

# 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) click to toggle source

run configuration.rb file only if it exists

# File lib/central.rb, line 542
def run_if_exists(file)
  run file if file_exists?(file)
end
run_monitors() click to toggle source

run monitors

# 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) click to toggle source

calculate SHA2 digest for a file

# File lib/central.rb, line 253
def sha2(file)
  Digest::SHA256.hexdigest read(file)
end
shell(command, verbose: false, silent: true) click to toggle source

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

# 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?() click to toggle source
# File lib/central.rb, line 98
def solaris?
  os == 'solaris'
end
source(file, source) click to toggle source

source file in sh/bash/zsh script

# 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:) click to toggle source

run shell command with sudo prefix, acts same as shell

# File lib/central.rb, line 156
def sudo(command, verbose:, silent:)
  shell('sudo ' + command, verbose: verbose, silent: silent)
end
touch(path) click to toggle source

touch file

# 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) click to toggle source

write content into a file

# File lib/central.rb, line 413
def write(file, content)
  file = abs(file)
  File.write(file, content)
end