Class: Fig::OperatingSystem

Inherits:
Object
  • Object
show all
Defined in:
lib/fig/operating_system.rb

Overview

Does things requiring real O/S interaction, primarilly taking care of file transfers and running external commands.

Constant Summary collapse

WINDOWS_FILE_NAME_ILLEGAL_CHARACTERS =
%w[ \\ / : * ? " < > | ]
UNIX_FILE_NAME_ILLEGAL_CHARACTERS =
%w[ / ]

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(login) ⇒ OperatingSystem

Returns a new instance of OperatingSystem.



83
84
85
86
87
88
89
90
91
92
# File 'lib/fig/operating_system.rb', line 83

def initialize()
  @protocols = {}
  @protocols['file'] = Fig::Protocol::File.new
  @protocols['ftp']  = Fig::Protocol::FTP.new 
  @protocols['http'] = Fig::Protocol::HTTP.new
  @protocols['https'] = Fig::Protocol::HTTP.new
  @protocols['sftp'] = Fig::Protocol::SFTP.new
  @protocols['ssh']  = Fig::Protocol::SSH.new
  @protocols['art'] = @protocols['artifactory'] = Fig::Protocol::Artifactory.new
end

Class Method Details

.file_name_illegal_charactersObject



58
59
60
61
62
63
64
# File 'lib/fig/operating_system.rb', line 58

def file_name_illegal_characters()
  if windows?
    return WINDOWS_FILE_NAME_ILLEGAL_CHARACTERS
  end

  return UNIX_FILE_NAME_ILLEGAL_CHARACTERS
end

.get_environment_variables(initial_values = nil) ⇒ Object



74
75
76
77
78
79
80
# File 'lib/fig/operating_system.rb', line 74

def get_environment_variables(initial_values = nil)
  if windows?
    return Fig::EnvironmentVariables::CaseInsensitive.new(initial_values)
  end

  return Fig::EnvironmentVariables::CaseSensitive.new(initial_values)
end

.macos?Boolean

Returns:

  • (Boolean)


46
47
48
49
50
51
52
# File 'lib/fig/operating_system.rb', line 46

def macos?
  return @is_macos if not @is_macos.nil?

  @is_macos = !! (RbConfig::CONFIG['host_os'] =~ /^darwin/)

  return @is_macos
end

.unix?Boolean

Returns:

  • (Boolean)


54
55
56
# File 'lib/fig/operating_system.rb', line 54

def unix?
  ! Fig::OperatingSystem.windows?
end

.windows?Boolean

Returns:

  • (Boolean)


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

def windows?
  return @is_windows if not @is_windows.nil?

  @is_windows = !! (RbConfig::CONFIG['host_os'] =~ /mswin|mingw/)

  return @is_windows
end

.wrap_variable_name_with_shell_expansion(variable_name) ⇒ Object



66
67
68
69
70
71
72
# File 'lib/fig/operating_system.rb', line 66

def wrap_variable_name_with_shell_expansion(variable_name)
  if Fig::OperatingSystem.windows?
    return "%#{variable_name}%"
  else
    return "$#{variable_name}"
  end
end

Instance Method Details

#copy(source, target, msg = nil) ⇒ Object



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/fig/operating_system.rb', line 195

def copy(source, target, msg = nil)
  if File.directory?(source)
    FileUtils.mkdir_p(target)
    Dir.foreach(source) do |child|
      if child != '.' and child != '..'
        copy(File.join(source, child), File.join(target, child), msg)
      end
    end
  else
    if (
          ! File.exist?(target)                     \
      ||  File.mtime(source) != File.mtime(target)  \
      ||  File.size(source) != File.size(target)
    )
      Fig::Logging.info "#{msg} #{target}" if msg
      FileUtils.mkdir_p(File.dirname(target))
      FileUtils.cp(source, target)
      File.utime(File.atime(source), File.mtime(source), target)
    end
  end
end

#create_archive(archive_name, paths_to_archive) ⇒ Object

Expects paths_to_archive as an Array of paths.



222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/fig/operating_system.rb', line 222

def create_archive(archive_name, paths_to_archive)
  existing_files = Set.new

  # TODO: Need to verify files_to_archive exists.
  Zlib::GzipWriter.open(archive_name) do |gz|
    Gem::Package::TarWriter.new(gz) do |writer|
      paths_to_archive.each do |path|
        add_path_to_archive(path, existing_files, writer)
      end
    end
  end
end

#delete_and_recreate_directory(dir) ⇒ Object



190
191
192
193
# File 'lib/fig/operating_system.rb', line 190

def delete_and_recreate_directory(dir)
  FileUtils.rm_rf(dir)
  FileUtils.mkdir_p(dir)
end

#download(url, path, prompt_for_login) ⇒ Object

Returns whether the file was not downloaded because the file already exists and is already up-to-date.



131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/fig/operating_system.rb', line 131

def download(url, path, )
  Fig::VerboseLogging.time_operation("downloading #{File.basename(url)}") do
    protocol, uri = decode_protocol url

    FileUtils.mkdir_p(File.dirname path)

    result = protocol.download uri, path, 
    if File.exist?(path)
      size = File.size(path)
      log_asset_operation("downloaded", path, size)
    end
    result
  end
end

#download_and_unpack_archive(url, download_directory, unpack_directory) ⇒ Object



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/fig/operating_system.rb', line 159

def download_and_unpack_archive(url, download_directory, unpack_directory)
  log_asset_operation("downloading", url)
  basename, path = download_resource(url, download_directory)

  case path
  when /\.tar\.gz$/
    unpack_archive(unpack_directory, path)
  when /\.tgz$/
    unpack_archive(unpack_directory, path)
  when /\.tar\.bz2$/
    unpack_archive(unpack_directory, path)
  when /\.zip$/
    unpack_archive(unpack_directory, path)
  else
    Fig::Logging.fatal "Unknown archive type: #{basename}"
    raise Fig::NetworkError.new("Unknown archive type: #{basename}")
  end

  return
end

#download_list(url) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/fig/operating_system.rb', line 102

def download_list(url)
  Fig::VerboseLogging.time_operation("listing packages from #{url}") do
    begin
      protocol, uri = decode_protocol url

      result = protocol.download_list uri
      log_repository_operation("listed", url, "#{result.size} entries")
      result
    rescue SocketError => error
      Fig::Logging.debug error.message
      raise Fig::NetworkError.new "#{url}: #{error.message}"
    rescue Errno::ETIMEDOUT => error
      Fig::Logging.debug error.message
      raise Fig::NetworkError.new "#{url}: #{error.message}"
    end
  end
end

#download_resource(url, download_directory) ⇒ Object

Returns the basename and full path to the download.



147
148
149
150
151
152
153
154
155
156
157
# File 'lib/fig/operating_system.rb', line 147

def download_resource(url, download_directory)
  log_asset_operation("downloading", url)
  FileUtils.mkdir_p(download_directory)

  basename = CGI.unescape Fig::URL.parse(url).path.split('/').last
  path     = File.join(download_directory, basename)

  download(url, path, false)

  return basename, path
end

#list(dir) ⇒ Object



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

def list(dir)
  Dir.entries(dir) - ['.', '..']
end

#move_file(directory, from, to) ⇒ Object



217
218
219
# File 'lib/fig/operating_system.rb', line 217

def move_file(directory, from, to)
  Dir.chdir(directory) { FileUtils.mv(from, to, :force => true) }
end

#path_up_to_date?(url, path, prompt_for_login) ⇒ Boolean

Determine whether we need to update something. Returns nil to indicate “don’t know”.

Returns:

  • (Boolean)


122
123
124
125
126
127
# File 'lib/fig/operating_system.rb', line 122

def path_up_to_date?(url, path, )
  return false if ! File.exist? path

  protocol, uri = decode_protocol url
  return protocol.path_up_to_date? uri, path, 
end

#plain_exec(command) ⇒ Object



286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/fig/operating_system.rb', line 286

def plain_exec(command)
  # Kernel#exec won't run Kernel#at_exit handlers.
  Fig::AtExit.execute()
  if ENV['FIG_COVERAGE']
    SimpleCov.at_exit.call
  end

  begin
    Kernel.exec(*command)
  rescue SystemCallError => exception
    raise Fig::UserInputError.new exception
  end
end

#plain_or_shell_exec(command) ⇒ Object

sigh Apparently Ruby < v1.9.3 does some wacko thing with single argument exec that causes it to not invoke the shell, so we’ve got this mess.



302
303
304
305
306
307
308
# File 'lib/fig/operating_system.rb', line 302

def plain_or_shell_exec(command)
  if command.size > 1
    plain_exec(command)
  else
    shell_exec(command[0])
  end
end

#shell_exec(command) ⇒ Object



278
279
280
281
282
283
284
# File 'lib/fig/operating_system.rb', line 278

def shell_exec(command)
  if Fig::OperatingSystem.windows?
    plain_exec [ ENV['ComSpec'],            '/c', command ]
  else
    plain_exec [ ENV['SHELL'] || '/bin/sh', '-c', command ]
  end
end

#unpack_archive(directory, archive_path) ⇒ Object

This method can handle the following archive types: .tar.bz2 .tar.gz .tgz .zip



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/fig/operating_system.rb', line 240

def unpack_archive(directory, archive_path)
  Fig::VerboseLogging.time_operation("extracting archive #{File.basename(archive_path)}") do
    FileUtils.mkdir_p directory
    archive_size = File.size(archive_path) if File.exist?(archive_path)
    log_asset_operation("extracting", archive_path, archive_size)
    
    Dir.chdir(directory) do
      if ! File.exist? archive_path
        raise Fig::RepositoryError.new "#{archive_path} does not exist."
      end

      running_on_windows = Fig::OperatingSystem.windows?
      ::Archive.read_open_filename(archive_path) do |reader|
        while entry = reader.next_header
          if running_on_windows
            check_archive_entry_for_windows entry, archive_path
          end

          begin
            reader.extract(entry)
          rescue Archive::Error => exception
            # Nice how the error message doesn't include any information about
            # what was having the problem.
            message = exception.message.sub(/^Extract archive failed: /, '')
            new_exception =
              Fig::RepositoryError.new(
                "Could not extract #{entry.pathname} from #{archive_path}: #{message}"
              )

            new_exception.set_backtrace exception.backtrace
            raise new_exception
          end
        end
      end
    end
  end
end

#upload(local_file, remote_file) ⇒ Object



180
181
182
183
184
185
186
187
188
# File 'lib/fig/operating_system.rb', line 180

def upload(local_file, remote_file)
  Fig::Logging.debug "Uploading #{local_file} to #{remote_file}."


  protocol, uri = decode_protocol remote_file
  protocol.upload local_file, uri

  return
end

#write(path, content) ⇒ Object



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

def write(path, content)
  File.open(path, 'wb') { |f| f.binmode; f << content }
end