Class: ArduinoCI::Host

Inherits:
Object
  • Object
show all
Defined in:
lib/arduino_ci/host.rb

Overview

Tools for interacting with the host machine

Constant Summary collapse

WINDOWS_VARIANT_REGEX =

TODO: this came from stackoverflow.com/a/22716582/2063546

and I'm not sure if it can be replaced by self.os == :windows
/mswin32|cygwin|mingw|bccwin/.freeze
%r{\d+/\d+/\d+\s+[^<]+<SYMLINKD?>\s+(.*) \[([^\]]+)\]}.freeze

Class Method Summary collapse

Class Method Details

.merge_capture_results(*args) ⇒ Hash

Merge multiple capture results into one aggregate value

Parameters:

  • args (Array)

    Array of hashes from ‘run_and_capture`

Returns:

  • (Hash)

    with keys “stdout” (String), “stderr” (String), and “success” (bool)



47
48
49
50
51
52
53
# File 'lib/arduino_ci/host.rb', line 47

def self.merge_capture_results(*args)
  {
    out: args.map { |a| a[:out] }.join,
    err: args.map { |a| a[:err] }.join,
    success: args.all? { |a| a[:success] }
  }
end

Whether this OS requires a hack for symlinks

Returns:

  • (bool)


104
105
106
# File 'lib/arduino_ci/host.rb', line 104

def self.needs_symlink_hack?
  RUBY_PLATFORM =~ WINDOWS_VARIANT_REGEX
end

.osObject

return [Symbol] the operating system of the host



63
64
65
66
67
# File 'lib/arduino_ci/host.rb', line 63

def self.os
  return :osx if OS.osx?
  return :linux if OS.linux?
  return :windows if OS.windows?
end

.pathname_to_windows(path) ⇒ String

Hack for “realpath” which on windows joins paths with slashes instead of backslashes

Parameters:

  • path (Pathname)

    the path to render

Returns:

  • (String)

    A path that will work on windows



91
92
93
# File 'lib/arduino_ci/host.rb', line 91

def self.pathname_to_windows(path)
  path.to_s.tr("/", "\\")
end

Cross-platform “read link” function

Parameters:

  • path (Pathname)

Returns:

  • (Pathname)

    the link target



120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/arduino_ci/host.rb', line 120

def self.readlink(path)
  return path.readlink unless needs_symlink_hack?

  the_dir  = pathname_to_windows(path.parent)
  the_file = path.basename.to_s

  stdout, _stderr, _exitstatus = Open3.capture3('cmd.exe', "/c dir /al #{the_dir}")
  symlinks = stdout.lines.map { |l| DIR_SYMLINK_REGEX.match(l.scrub) }.compact
  our_link = symlinks.find { |m| m[1] == the_file }
  return nil if our_link.nil?

  windows_to_pathname(our_link[2])
end

.run_and_capture(*args, **kwargs) ⇒ Hash

Execute a shell command and capture stdout, stderr, and status

Returns:

  • (Hash)

    with keys “stdout” (String), “stderr” (String), and “success” (bool)

See Also:



38
39
40
41
# File 'lib/arduino_ci/host.rb', line 38

def self.run_and_capture(*args, **kwargs)
  stdout, stderr, status = Open3.capture3(*args, **kwargs)
  { out: stdout, err: stderr, success: status.exitstatus.zero? }
end

.run_and_output(*args, **kwargs) ⇒ Object

Execute a shell command

See Also:

  • system


58
59
60
# File 'lib/arduino_ci/host.rb', line 58

def self.run_and_output(*args, **kwargs)
  system(*args, **kwargs)
end

Cross-platform symlinking if on windows, call mklink, else self.symlink

Parameters:

  • old_path (Pathname)
  • new_path (Pathname)


73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/arduino_ci/host.rb', line 73

def self.symlink(old_path, new_path)
  # we would prefer `new_path.make_symlink(old_path)` but "symlink function is unimplemented on this machine" with windows
  return new_path.make_symlink(old_path) unless needs_symlink_hack?

  # via https://stackoverflow.com/a/22716582/2063546
  # windows mklink syntax is reverse of unix ln -s
  # windows mklink is built into cmd.exe
  # vulnerable to command injection, but okay because this is a hack to make a cli tool work.
  orp = pathname_to_windows(old_path.realpath)
  np  = pathname_to_windows(new_path)

  _stdout, _stderr, exitstatus = Open3.capture3('cmd.exe', "/C mklink /D #{np} #{orp}")
  exitstatus.success?
end

.symlink?(path) ⇒ bool

Cross-platform is-this-a-symlink function

Parameters:

  • path (Pathname)

Returns:

  • (bool)

    Whether the file is a symlink



111
112
113
114
115
# File 'lib/arduino_ci/host.rb', line 111

def self.symlink?(path)
  return path.symlink? unless needs_symlink_hack?

  !readlink(path).nil?
end

.which(cmd) ⇒ Pathname

Cross-platform way of finding an executable in the $PATH. via stackoverflow.com/a/5471032/2063546

which('ruby') #=> /usr/bin/ruby

Parameters:

  • cmd (String)

    the command to search for

Returns:

  • (Pathname)

    the full path to the command if it exists



21
22
23
24
25
26
27
28
29
30
31
# File 'lib/arduino_ci/host.rb', line 21

def self.which(cmd)
  exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
  ENV['PATH'].split(File::PATH_SEPARATOR).each do |string_path|
    path = OS.windows? ? windows_to_pathname(string_path) : Pathname.new(string_path)
    exts.each do |ext|
      exe = path.join("#{cmd}#{ext}")
      return exe if exe.executable? && !exe.directory?
    end
  end
  nil
end

.windows_to_pathname(str) ⇒ Pathname

Hack for “realpath” which on windows joins paths with slashes instead of backslashes

Parameters:

  • str (String)

    the windows path

Returns:

  • (Pathname)

    A path that will be recognized by pathname



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

def self.windows_to_pathname(str)
  Pathname.new(str.tr("\\", "/"))
end