Class: ArduinoCI::ArduinoDownloader

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

Overview

Manage the OS-specific download & install of Arduino

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(desired_version, output = $stdout) ⇒ ArduinoDownloader

Returns a new instance of ArduinoDownloader.

Parameters:

  • desired_version (string)

    Version string e.g. 1.8.7

  • output (IO) (defaults to: $stdout)

    $stdout, $stderr, File.new(/dev/null, ‘w’), etc. where console output will be sent



16
17
18
19
# File 'lib/arduino_ci/arduino_downloader.rb', line 16

def initialize(desired_version, output = $stdout)
  @desired_version = desired_version
  @output = output
end

Class Method Details

.autolocated_executablePathname

The autolocated executable of the installation

Returns:

  • (Pathname)

    or nil



35
36
37
38
39
40
41
# File 'lib/arduino_ci/arduino_downloader.rb', line 35

def self.autolocated_executable
  # Arbitrarily, I'm going to pick the force installed location first
  # if it exists.  I'm not sure why we would have both, but if we did
  # a force install then let's make sure we actually use it.
  locations = [self.force_installed_executable, self.existing_executable]
  locations.find { |loc| !loc.nil? && File.exist?(loc) }
end

.downloaderstring

The technology that will be used to complete the download (for logging purposes)

Returns:

  • (string)


70
71
72
# File 'lib/arduino_ci/arduino_downloader.rb', line 70

def self.downloader
  "open-uri"
end

.existing_executablePathname

The executable Arduino file in an existing installation, or nil

Returns:

  • (Pathname)


45
46
47
# File 'lib/arduino_ci/arduino_downloader.rb', line 45

def self.existing_executable
  Host.which("arduino-cli")
end

.extract(_package_file) ⇒ bool

Extract the package_file to extracted_file

Returns:

  • (bool)

    whether successful



83
84
85
# File 'lib/arduino_ci/arduino_downloader.rb', line 83

def self.extract(_package_file)
  self.must_implement(__method__)
end

.extracted_filestring

The local filename of the extracted IDE package (zip/tar/etc)

Returns:

  • (string)


57
58
59
# File 'lib/arduino_ci/arduino_downloader.rb', line 57

def self.extracted_file
  self.must_implement(__method__)
end

.extractorstring

The technology that will be used to extract the download (for logging purposes)

Returns:

  • (string)


77
78
79
# File 'lib/arduino_ci/arduino_downloader.rb', line 77

def self.extractor
  self.must_implement(__method__)
end

.force_installed_executablePathname

The executable Arduino file in a forced installation, or nil

Returns:

  • (Pathname)


63
64
65
# File 'lib/arduino_ci/arduino_downloader.rb', line 63

def self.force_installed_executable
  Pathname.new(ENV['HOME']) + self.extracted_file
end

.must_implement(method) ⇒ Object

Provide guidelines to the implementer of this class

Raises:

  • (NotImplementedError)


22
23
24
# File 'lib/arduino_ci/arduino_downloader.rb', line 22

def self.must_implement(method)
  raise NotImplementedError, "#{self.class.name} failed to implement ArduinoDownloader.#{method}"
end

Instance Method Details

#downloadbool

Download the package_url to package_file

Returns:

  • (bool)

    whether successful



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/arduino_ci/arduino_downloader.rb', line 95

def download
  # Turned off ssl verification
  # This should be acceptable because it won't happen on a user's machine, just CI

  # define a progress-bar printer
  chunk_size = 1024 * 1024 * 1024
  total_size = 0
  dots = 0
  dot_printer = lambda do |size|
    total_size += size
    needed_dots = (total_size / chunk_size).to_i
    unprinted_dots = needed_dots - dots
    @output.print("." * unprinted_dots) if unprinted_dots.positive?
    dots = needed_dots
  end

  URI.open(package_url, ssl_verify_mode: 0, progress_proc: dot_printer) do |url|
    File.open(package_file, 'wb') { |file| file.write(url.read) }
  end
rescue Net::OpenTimeout, Net::ReadTimeout, OpenURI::HTTPError, URI::InvalidURIError => e
  @output.puts "\nArduino force-install failed downloading #{package_url}: #{e}"
end

#executebool

Forcibly install Arduino on linux from the web

Returns:

  • (bool)

    Whether the command succeeded



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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/arduino_ci/arduino_downloader.rb', line 126

def execute
  error_preparing = prepare
  unless error_preparing.nil?
    @output.puts "Arduino force-install failed preparation: #{error_preparing}"
    return false
  end

  arduino_package = "Arduino #{@desired_version} package"
  attempts = 0

  loop do
    if File.exist?(package_file)
      @output.puts "#{arduino_package} seems to have been downloaded already at #{package_file}" if attempts.zero?
      break
    elsif attempts >= DOWNLOAD_ATTEMPTS
      break @output.puts "After #{DOWNLOAD_ATTEMPTS} attempts, failed to download #{package_url}"
    else
      @output.print "Attempting to download #{arduino_package} with #{self.class.downloader}"
      download
      @output.puts
    end
    attempts += 1
  end

  if File.exist?(self.class.extracted_file)
    @output.puts "#{arduino_package} seems to have been extracted already at #{self.class.extracted_file}"
  elsif File.exist?(package_file)
    @output.print "Extracting archive with #{self.class.extractor}"
    self.class.extract(package_file)
    @output.puts
  end

  if File.exist?(self.class.force_installed_executable)
    @output.puts "#{arduino_package} seems to have been installed already at #{self.class.force_installed_executable}"
  elsif File.exist?(self.class.extracted_file)
    install
  else
    @output.puts "Could not find extracted archive (tried #{self.class.extracted_file})"
  end

  File.exist?(self.class.force_installed_executable)
end

#installbool

Move the extracted package file from extracted_file to the force_installed_executable

Returns:

  • (bool)

    whether successful



120
121
122
# File 'lib/arduino_ci/arduino_downloader.rb', line 120

def install
  FileUtils.mv self.class.extracted_file.to_s, self.class.force_installed_executable.to_s
end

#package_filestring

The local file (dir) name of the desired IDE package (zip/tar/etc)

Returns:

  • (string)


51
52
53
# File 'lib/arduino_ci/arduino_downloader.rb', line 51

def package_file
  self.class.must_implement(__method__)
end

#package_urlstring

The URL of the desired IDE package (zip/tar/etc) for this platform

Returns:

  • (string)


89
90
91
# File 'lib/arduino_ci/arduino_downloader.rb', line 89

def package_url
  "https://github.com/arduino/arduino-cli/releases/download/#{@desired_version}/#{package_file}"
end

#preparestring

Make any preparations or run any checks prior to making changes

Returns:

  • (string)

    Error message, or nil if success



28
29
30
# File 'lib/arduino_ci/arduino_downloader.rb', line 28

def prepare
  nil
end