Skip to content

Commit 921925f

Browse files
committed
Refactor force_install code in preparation for windows CI
1 parent 9b43554 commit 921925f

8 files changed

+467
-94
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1212
### Changed
1313
- Author
1414
- Splash-screen-skip hack on OSX now falls back on "official" launch method if the hack doesn't work
15+
- Refactored download/install code in prepration for windows CI
1516

1617
### Deprecated
1718

lib/arduino_ci/arduino_downloader.rb

+171
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
DOWNLOAD_ATTEMPTS = 3
2+
3+
module ArduinoCI
4+
5+
# Manage the OS-specific download & install of Arduino
6+
class ArduinoDownloader
7+
8+
def initialize(desired_ide_version)
9+
@desired_ide_version = desired_ide_version
10+
end
11+
12+
# Provide guidelines to the implementer of this class
13+
def self.must_implement(method)
14+
raise NotImplementedError, "#{self.class.name} failed to implement ArduinoDownloader.#{method}"
15+
end
16+
17+
# Make any preparations or run any checks prior to making changes
18+
# @return [string] Error message, or nil if success
19+
def prepare
20+
nil
21+
end
22+
23+
# The autolocated executable of the installation
24+
#
25+
# @return [string] or nil
26+
def self.autolocated_executable
27+
# Arbitrarily, I'm going to pick the force installed location first
28+
# if it exists. I'm not sure why we would have both, but if we did
29+
# a force install then let's make sure we actually use it.
30+
locations = [self.force_installed_executable, self.existing_executable]
31+
locations.each do |loc|
32+
next if loc.nil?
33+
next unless File.exist? loc
34+
return loc
35+
end
36+
nil
37+
end
38+
39+
# The autolocated directory of the installation
40+
#
41+
# @return [string] or nil
42+
def self.autolocated_installation
43+
# Arbitrarily, I'm going to pick the force installed location first
44+
# if it exists. I'm not sure why we would have both, but if we did
45+
# a force install then let's make sure we actually use it.
46+
locations = [self.force_install_location, self.existing_installation]
47+
locations.each do |loc|
48+
next if loc.nil?
49+
next unless File.exist? loc
50+
return loc
51+
end
52+
nil
53+
end
54+
55+
# The path to the directory of an existing installation, or nil
56+
# @return [string]
57+
def self.existing_installation
58+
self.must_implement(__method__)
59+
end
60+
61+
# The executable Arduino file in an existing installation, or nil
62+
# @return [string]
63+
def self.existing_executable
64+
self.must_implement(__method__)
65+
end
66+
67+
# The executable Arduino file in a forced installation, or nil
68+
# @return [string]
69+
def self.force_installed_executable
70+
self.must_implement(__method__)
71+
end
72+
73+
# The technology that will be used to complete the download
74+
# (for logging purposes)
75+
# @return [string]
76+
def downloader
77+
self.class.must_implement(__method__)
78+
end
79+
80+
# The technology that will be used to extract the download
81+
# (for logging purposes)
82+
# @return [string]
83+
def extracter
84+
self.class.must_implement(__method__)
85+
end
86+
87+
# The URL of the desired IDE package (zip/tar/etc) for this platform
88+
# @return [string]
89+
def package_url
90+
"https://downloads.arduino.cc/#{package_file}"
91+
end
92+
93+
# The local file (dir) name of the desired IDE package (zip/tar/etc)
94+
# @return [string]
95+
def package_file
96+
self.class.must_implement(__method__)
97+
end
98+
99+
# The local filename of the extracted IDE package (zip/tar/etc)
100+
# @return [string]
101+
def extracted_file
102+
self.class.must_implement(__method__)
103+
end
104+
105+
# @return [String] The location where a forced install will go
106+
def self.force_install_location
107+
File.join(ENV['HOME'], 'arduino_ci_ide')
108+
end
109+
110+
# Download the package_url to package_file, and maybe print a line of dots......
111+
# @return [bool] whether successful
112+
def download
113+
self.class.must_implement(__method__)
114+
end
115+
116+
# Extract the package_file to extracted_file
117+
# @return [bool] whether successful
118+
def extract
119+
self.class.must_implement(__method__)
120+
end
121+
122+
# Move the extracted package file from extracted_file to the force_install_location
123+
# @return [bool] whether successful
124+
def install
125+
self.class.must_implement(__method__)
126+
end
127+
128+
# Forcibly install Arduino on linux from the web
129+
# @return [bool] Whether the command succeeded
130+
def execute
131+
error_preparing = prepare
132+
unless error_preparing.nil?
133+
puts "Arduino force-install failed preparation: #{error_preparing}"
134+
return false
135+
end
136+
137+
attempts = 0
138+
139+
loop do
140+
if File.exist? package_file
141+
puts "Arduino package seems to have been downloaded already" if attempts.zero?
142+
break
143+
elsif attempts >= DOWNLOAD_ATTEMPTS
144+
break puts "After #{DOWNLOAD_ATTEMPTS} attempts, failed to download #{package_url}"
145+
else
146+
puts "Attempting to download Arduino package with #{downloader}"
147+
download
148+
end
149+
attempts += 1
150+
end
151+
152+
if File.exist? extracted_file
153+
puts "Arduino package seems to have been extracted already"
154+
elsif File.exist? package_file
155+
puts "Extracting archive with #{extracter}"
156+
extract
157+
end
158+
159+
if File.exist? self.class.force_install_location
160+
puts "Arduino package seems to have been installed already"
161+
elsif File.exist? extracted_file
162+
install
163+
else
164+
puts "Arduino force-install failed"
165+
end
166+
167+
File.exist? self.class.force_install_location
168+
end
169+
170+
end
171+
end
+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
require "arduino_ci/arduino_downloader_posix"
2+
3+
USE_BUILDER = false
4+
5+
module ArduinoCI
6+
7+
# Manage the linux download & install of Arduino
8+
class ArduinoDownloaderLinux < ArduinoDownloaderPosix
9+
10+
# The local filename of the desired IDE package (zip/tar/etc)
11+
# @return [string]
12+
def package_file
13+
"#{extracted_file}-linux64.tar.xz"
14+
end
15+
16+
# The technology that will be used to extract the download
17+
# (for logging purposes)
18+
# @return [string]
19+
def extracter
20+
"tar"
21+
end
22+
23+
# Extract the package_file to extracted_file
24+
# @return [bool] whether successful
25+
def extract
26+
system(extracter, "xf", package_file)
27+
end
28+
29+
# The local file (dir) name of the extracted IDE package (zip/tar/etc)
30+
# @return [string]
31+
def extracted_file
32+
"arduino-#{@desired_ide_version}"
33+
end
34+
35+
# The path to the directory of an existing installation, or nil
36+
# @return [string]
37+
def self.existing_installation
38+
exe = self.existing_executable
39+
return nil if exe.nil?
40+
File.dirname(exe) # it's not really this
41+
# but for this platform it doesn't really matter
42+
end
43+
44+
# The executable Arduino file in an existing installation, or nil
45+
# @return [string]
46+
def self.existing_executable
47+
if USE_BUILDER
48+
# builder_name = "arduino-builder"
49+
# cli_place = Host.which(builder_name)
50+
# unless cli_place.nil?
51+
# ret = ArduinoCmdLinuxBuilder.new
52+
# ret.base_cmd = [cli_place]
53+
# return ret
54+
# end
55+
end
56+
Host.which("arduino")
57+
end
58+
59+
# The executable Arduino file in a forced installation, or nil
60+
# @return [string]
61+
def self.force_installed_executable
62+
if USE_BUILDER
63+
# forced_builder = File.join(ArduinoCmdLinuxBuilder.force_install_location, builder_name)
64+
# if File.exist?(forced_builder)
65+
# ret = ArduinoCmdLinuxBuilder.new
66+
# ret.base_cmd = [forced_builder]
67+
# return ret
68+
# end
69+
end
70+
forced_arduino = File.join(self.force_install_location, "arduino")
71+
return forced_arduino if File.exist? forced_arduino
72+
nil
73+
end
74+
75+
end
76+
end
+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
require "arduino_ci/arduino_downloader_posix"
2+
3+
module ArduinoCI
4+
5+
# Manage the OSX download & install of Arduino
6+
class ArduinoDownloaderOSX < ArduinoDownloaderPosix
7+
8+
# The local filename of the desired IDE package (zip/tar/etc)
9+
# @return [string]
10+
def package_file
11+
"arduino-#{@desired_ide_version}-macosx.zip"
12+
end
13+
14+
# The technology that will be used to extract the download
15+
# (for logging purposes)
16+
# @return [string]
17+
def extracter
18+
"unzip"
19+
end
20+
21+
# Extract the package_file to extracted_file
22+
# @return [bool] whether successful
23+
def extract
24+
system(extracter, package_file)
25+
end
26+
27+
# The local file (dir) name of the extracted IDE package (zip/tar/etc)
28+
# @return [string]
29+
def extracted_file
30+
"Arduino.app"
31+
end
32+
33+
# An existing Arduino directory in one of the given directories, or nil
34+
# @param Array<string> a list of places to look
35+
# @return [string]
36+
def self.find_existing_arduino_dir(paths)
37+
paths.each do |path|
38+
return path if File.exist? path
39+
end
40+
nil
41+
end
42+
43+
# An existing Arduino file in one of the given directories, or nil
44+
# @param Array<string> a list of places to look for the executable
45+
# @return [string]
46+
def self.find_existing_arduino_exe(paths)
47+
paths.each do |path|
48+
exe = File.join(path, "MacOS", "Arduino")
49+
return exe if File.exist? exe
50+
end
51+
nil
52+
end
53+
54+
# The path to the directory of an existing installation, or nil
55+
# @return [string]
56+
def self.existing_installation
57+
self.find_existing_arduino_dir(["/Applications/Arduino.app/Contents"])
58+
end
59+
60+
# The executable Arduino file in an existing installation, or nil
61+
# @return [string]
62+
def self.existing_executable
63+
self.find_existing_arduino_exe(["/Applications/Arduino.app/Contents"])
64+
end
65+
66+
# The executable Arduino file in a forced installation, or nil
67+
# @return [string]
68+
def self.force_installed_executable
69+
self.find_existing_arduino_exe([File.join(self.force_install_location, "Contents")])
70+
end
71+
72+
end
73+
end
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
require "arduino_ci/arduino_downloader"
2+
3+
module ArduinoCI
4+
5+
# Manage the POSIX download & install of Arduino
6+
class ArduinoDownloaderPosix < ArduinoDownloader
7+
8+
# Make any preparations or run any checks prior to making changes
9+
# @return [string] Error message, or nil if success
10+
def prepare
11+
reqs = [downloader, extracter]
12+
reqs.each do |req|
13+
return "#{req} does not appear to be installed!" unless Host.which(req)
14+
end
15+
nil
16+
end
17+
18+
# The technology that will be used to complete the download
19+
# (for logging purposes)
20+
# @return [string]
21+
def downloader
22+
"wget"
23+
end
24+
25+
# Download the package_url to package_file
26+
# @return [bool] whether successful
27+
def download
28+
system(downloader, "--quiet", "--progress=dot:giga", package_url)
29+
end
30+
31+
# Move the extracted package file from extracted_file to the force_install_location
32+
# @return [bool] whether successful
33+
def install
34+
system("mv", extracted_file, self.class.force_install_location)
35+
end
36+
37+
end
38+
end

0 commit comments

Comments
 (0)