From cacbbe403d688a2d6b6cde7c14cb1266b2e88e7a Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Wed, 10 Jan 2018 09:00:17 -0500 Subject: [PATCH 01/36] ignore gems --- .gitignore | 1 + CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index be26b81f..75bf4c75 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ /pkg/ /spec/reports/ vendor +*.gem # rspec failure tracking .rspec_status diff --git a/CHANGELOG.md b/CHANGELOG.md index d6bbd532..eb5b802d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Removed ### Fixed +- Built gems are `.gitignore`d ### Security From 8d413929bea9b007ac36c9d85f14a9085182b81e Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Wed, 10 Jan 2018 09:03:54 -0500 Subject: [PATCH 02/36] address security concerns from github regarding gem versions --- CHANGELOG.md | 1 + arduino_ci.gemspec | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb5b802d..72f7c402 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - Built gems are `.gitignore`d +- Updated gems based on Github's security advisories ### Security diff --git a/arduino_ci.gemspec b/arduino_ci.gemspec index 802886d2..1ba46750 100644 --- a/arduino_ci.gemspec +++ b/arduino_ci.gemspec @@ -22,6 +22,6 @@ Gem::Specification.new do |spec| spec.add_development_dependency "bundler", "~> 1.15" spec.add_development_dependency "rspec", "~> 3.0" - spec.add_development_dependency 'rubocop', '~> 0', '>= 0.46.0' - spec.add_development_dependency 'yard', '~>0.8', '>= 0.8' + spec.add_development_dependency 'rubocop', '~>0.49.0' + spec.add_development_dependency 'yard', '~>0.9.11' end From ab47c85ef327c7546a73acc7291b8ed7a6502b75 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Wed, 10 Jan 2018 12:17:25 -0500 Subject: [PATCH 03/36] split some files and add installer functionality --- CHANGELOG.md | 2 + lib/arduino_ci.rb | 118 +----------------- lib/arduino_ci/arduino_cmd.rb | 34 +++++ lib/arduino_ci/arduino_installation.rb | 77 ++++++++++++ lib/arduino_ci/display_manager.rb | 74 +++++++++++ spec/arduino_cmd_spec.rb | 9 ++ spec/arduino_installation_spec.rb | 25 ++++ ...o_exec_spec.rb => display_manager_spec.rb} | 15 +-- 8 files changed, 226 insertions(+), 128 deletions(-) create mode 100644 lib/arduino_ci/arduino_cmd.rb create mode 100644 lib/arduino_ci/arduino_installation.rb create mode 100644 lib/arduino_ci/display_manager.rb create mode 100644 spec/arduino_cmd_spec.rb create mode 100644 spec/arduino_installation_spec.rb rename spec/{arduino_exec_spec.rb => display_manager_spec.rb} (51%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72f7c402..dc516990 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added +- `ArduinoInstallation` class for managing lib / executable paths +- `DisplayManager` class for managing Xvfb instance if needed ### Changed diff --git a/lib/arduino_ci.rb b/lib/arduino_ci.rb index 7edf9596..2f871c67 100644 --- a/lib/arduino_ci.rb +++ b/lib/arduino_ci.rb @@ -1,124 +1,8 @@ require "arduino_ci/version" - -require 'singleton' - -# Cross-platform way of finding an executable in the $PATH. -# via https://stackoverflow.com/a/5471032/2063546 -# which('ruby') #=> /usr/bin/ruby -def which(cmd) - exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : [''] - ENV['PATH'].split(File::PATH_SEPARATOR).each do |path| - exts.each do |ext| - exe = File.join(path, "#{cmd}#{ext}") - return exe if File.executable?(exe) && !File.directory?(exe) - end - end - nil -end +require "arduino_ci/arduino_cmd" # ArduinoCI contains classes for automated testing of Arduino code on the command line # @author Ian Katz module ArduinoCI - # Wrap the Arduino executable. This requires, in some cases, a faked display. - class ArduinoCmd - - # create as many ArduinoCmds as you like, but we need one and only one display manager - class DisplayMgr - include Singleton - attr_reader :enabled - - def initialize - @existing = existing_display? - @enabled = false - @pid = nil - end - - # attempt to determine if the machine is running a graphical display (i.e. not Travis) - def existing_display? - return true if RUBY_PLATFORM.include? "darwin" - return true if ENV["DISPLAY"].nil? - return true if ENV["DISPLAY"].include? ":" - false - end - - # enable a virtual display - def enable - return @enabled = true if @existing # silent no-op if built in display - return unless @pid.nil? - - @enabled = true - @pid = fork do - puts "Forking Xvfb" - system("Xvfb", ":1", "-ac", "-screen", "0", "1280x1024x16") - puts "Xvfb unexpectedly quit!" - end - sleep(3) # TODO: test a connection to the X server? - end - - # disable the virtual display - def disable - return @enabled = false if @existing # silent no-op if built in display - return if @pid.nil? - - begin - Process.kill 9, @pid - ensure - Process.wait @pid - @pid = nil - end - puts "Xvfb killed" - end - - # Enable a virtual display for the duration of the given block - def with_display - enable - begin - yield environment - ensure - disable - end - end - - def environment - return nil unless @existing || @enabled - return {} if @existing - { DISPLAY => ":1.0" } - end - - # On finalize, ensure child process is ended - def self.finalize - disable - end - end - - class << self - protected :new - - # attempt to find a workable Arduino executable across platforms - def guess_executable_location - osx_place = "/Applications/Arduino.app/Contents/MacOS/Arduino" - places = { - "arduino" => !which("arduino").nil?, - osx_place => (File.exist? osx_place), - } - places.each { |k, v| return k if v } - nil - end - - def autolocate - ret = new - ret.path = guess_executable_location - ret - end - end - - attr_accessor :path - - def initialize - @display_mgr = DisplayMgr::instance - end - - end - end diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb new file mode 100644 index 00000000..13ffc1ed --- /dev/null +++ b/lib/arduino_ci/arduino_cmd.rb @@ -0,0 +1,34 @@ +require 'arduino_ci/display_manager' +require 'arduino_ci/arduino_installation' + +module ArduinoCI + + # Wrap the Arduino executable. This requires, in some cases, a faked display. + class ArduinoCmd + + class << self + protected :new + + def autolocate + new(ArduinoInstallation.autolocate) + end + + def autolocate! + new(ArduinoInstallation.autolocate!) + end + + end + + attr_accessor :installation + + def initialize(installation) + @display_mgr = DisplayManager::instance + @installation = installation + end + + def board_installed?(board) + system(@installation, "--board", board) + end + + end +end diff --git a/lib/arduino_ci/arduino_installation.rb b/lib/arduino_ci/arduino_installation.rb new file mode 100644 index 00000000..231c3ee8 --- /dev/null +++ b/lib/arduino_ci/arduino_installation.rb @@ -0,0 +1,77 @@ +# Cross-platform way of finding an executable in the $PATH. +# via https://stackoverflow.com/a/5471032/2063546 +# which('ruby') #=> /usr/bin/ruby +def which(cmd) + exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : [''] + ENV['PATH'].split(File::PATH_SEPARATOR).each do |path| + exts.each do |ext| + exe = File.join(path, "#{cmd}#{ext}") + return exe if File.executable?(exe) && !File.directory?(exe) + end + end + nil +end + +module ArduinoCI + + # Manage the OS-specific install location of Arduino + class ArduinoInstallation + attr_accessor :cmd_path + attr_accessor :lib_dir + + class << self + def force_install_location + File.join(ENV['HOME'], 'arduino_ci_ide') + end + + # attempt to find a workable Arduino executable across platforms + def autolocate + ret = new + + osx_place = "/Applications/Arduino.app/Contents/MacOS/Arduino" + if File.exist? osx_place + ret.cmd_path = File.join(osx_place, "arduino") + ret.lib_dir = File.join(osx_place, "Libraries") + return ret + end + + posix_place = which("arduino") + unless posix_place.nil? + ret.cmd_path = posix_place + ret.lib_dir = File.join(ENV['HOME'], "Sketchbook") # assume linux + # https://learn.adafruit.com/adafruit-all-about-arduino-libraries-install-use/how-to-install-a-library + return ret + end + + if File.exist? force_install_location + ret.cmd_path = File.join(force_install_location, "arduino") + ret.lib_dir = File.join(force_install_location, "libraries") + # TODO: "libraries" is what's in the adafruit install.sh script + return ret + end + + ret + end + + # Attempt to find a workable Arduino executable across platforms, and install it if we don't + def autolocate! + candidate = autolocate + return candidate unless candidate.cmd_path.nil? + # force the install + + if force_install + candidate.cmd_path = File.join(force_install_location, "arduino") + candidate.lib_dir = File.join(force_install_location, "libraries") + end + candidate + end + + def force_install + system("wget", "https://downloads.arduino.cc/arduino-1.6.5-linux64.tar.xz") + system("tar", "xf", "arduino-1.6.5-linux64.tar.xz") + system("mv", "arduino-1.6.5", force_install_location) + end + + end + end +end diff --git a/lib/arduino_ci/display_manager.rb b/lib/arduino_ci/display_manager.rb new file mode 100644 index 00000000..5e540053 --- /dev/null +++ b/lib/arduino_ci/display_manager.rb @@ -0,0 +1,74 @@ +require 'singleton' + +module ArduinoCI + + # When arduino commands run, they need a graphical display. + # This class handles the setup of that display, if needed. + class DisplayManager + include Singleton + attr_reader :enabled + + def initialize + @existing = existing_display? + @enabled = false + @pid = nil + end + + # attempt to determine if the machine is running a graphical display (i.e. not Travis) + def existing_display? + return true if RUBY_PLATFORM.include? "darwin" + return true if ENV["DISPLAY"].nil? + return true if ENV["DISPLAY"].include? ":" + false + end + + # enable a virtual display + def enable + return @enabled = true if @existing # silent no-op if built in display + return unless @pid.nil? + + @enabled = true + @pid = fork do + puts "Forking Xvfb" + system("Xvfb", ":1", "-ac", "-screen", "0", "1280x1024x16") + puts "Xvfb unexpectedly quit!" + end + sleep(3) # TODO: test a connection to the X server? + end + + # disable the virtual display + def disable + return @enabled = false if @existing # silent no-op if built in display + return if @pid.nil? + + begin + Process.kill 9, @pid + ensure + Process.wait @pid + @pid = nil + end + puts "Xvfb killed" + end + + # Enable a virtual display for the duration of the given block + def with_display + enable + begin + yield environment + ensure + disable + end + end + + def environment + return nil unless @existing || @enabled + return {} if @existing + { DISPLAY => ":1.0" } + end + + # On finalize, ensure child process is ended + def self.finalize + disable + end + end +end diff --git a/spec/arduino_cmd_spec.rb b/spec/arduino_cmd_spec.rb new file mode 100644 index 00000000..5d0b2598 --- /dev/null +++ b/spec/arduino_cmd_spec.rb @@ -0,0 +1,9 @@ +require "spec_helper" + +RSpec.describe ArduinoCI::ArduinoCmd do + it "Finds the Arduino executable" do + arduino_cmd = ArduinoCI::ArduinoCmd.autolocate + # expect(arduino_cmd.path).not_to be nil + end +end + diff --git a/spec/arduino_installation_spec.rb b/spec/arduino_installation_spec.rb new file mode 100644 index 00000000..c0a81278 --- /dev/null +++ b/spec/arduino_installation_spec.rb @@ -0,0 +1,25 @@ +require "spec_helper" + +RSpec.describe ArduinoCI::ArduinoInstallation do + context "force_install_location" do + it "is resolvable" do + expect(ArduinoCI::ArduinoInstallation.force_install_location).not_to be nil + end + end + + context "autolocate" do + it "doesn't fail" do + ArduinoCI::ArduinoInstallation.autolocate + end + end + + context "autolocate!" do + it "doesn't fail" do + installation = ArduinoCI::ArduinoInstallation.autolocate! + expect(installation.cmd_path).not_to be nil + expect(installation.lib_dir).not_to be nil + end + end + +end + diff --git a/spec/arduino_exec_spec.rb b/spec/display_manager_spec.rb similarity index 51% rename from spec/arduino_exec_spec.rb rename to spec/display_manager_spec.rb index 19d2147a..3abbab97 100644 --- a/spec/arduino_exec_spec.rb +++ b/spec/display_manager_spec.rb @@ -1,27 +1,20 @@ require "spec_helper" -RSpec.describe ArduinoCI::ArduinoCmd do - it "Finds the Arduino executable" do - arduino_cmd = ArduinoCI::ArduinoCmd.autolocate - # expect(arduino_cmd.path).not_to be nil - end -end - -RSpec.describe ArduinoCI::ArduinoCmd::DisplayMgr do +RSpec.describe ArduinoCI::DisplayManager do context "singleton ::instance" do it "produces an instance" do - expect(ArduinoCI::ArduinoCmd::DisplayMgr::instance).not_to be_nil + expect(ArduinoCI::DisplayManager::instance).not_to be_nil end end context "with_display" do it "Properly enables and disables" do - manager = ArduinoCI::ArduinoCmd::DisplayMgr::instance + manager = ArduinoCI::DisplayManager::instance expect(manager.enabled).to be false manager.with_display do |environment| expect(manager.enabled).to be true expect(environment.class).to eq(Hash) - also_manager = ArduinoCI::ArduinoCmd::DisplayMgr::instance + also_manager = ArduinoCI::DisplayManager::instance expect(also_manager.enabled).to be true end expect(manager.enabled).to be false From d736446b3b0bfdc253a1438c255c83e427bac537 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Wed, 10 Jan 2018 12:25:50 -0500 Subject: [PATCH 04/36] don't disable the display if previously enabled outside with_display --- CHANGELOG.md | 1 + lib/arduino_ci/display_manager.rb | 5 +++-- spec/display_manager_spec.rb | 18 ++++++++++++++++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc516990..57ddf4fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - `DisplayManager` class for managing Xvfb instance if needed ### Changed +- `DisplayManger.with_display` doesn't `disable` if the display was enabled prior to starting the block ### Deprecated diff --git a/lib/arduino_ci/display_manager.rb b/lib/arduino_ci/display_manager.rb index 5e540053..f9e1df83 100644 --- a/lib/arduino_ci/display_manager.rb +++ b/lib/arduino_ci/display_manager.rb @@ -52,11 +52,12 @@ def disable # Enable a virtual display for the duration of the given block def with_display - enable + was_enabled = @enabled + enable unless was_enabled begin yield environment ensure - disable + disable unless was_enabled end end diff --git a/spec/display_manager_spec.rb b/spec/display_manager_spec.rb index 3abbab97..60b25456 100644 --- a/spec/display_manager_spec.rb +++ b/spec/display_manager_spec.rb @@ -8,8 +8,10 @@ end context "with_display" do - it "Properly enables and disables" do - manager = ArduinoCI::DisplayManager::instance + manager = ArduinoCI::DisplayManager::instance + manager.disable + + it "Properly enables and disables when not previously enabled" do expect(manager.enabled).to be false manager.with_display do |environment| expect(manager.enabled).to be true @@ -19,5 +21,17 @@ end expect(manager.enabled).to be false end + + it "Properly enables and disables when previously enabled" do + manager.enable + expect(manager.enabled).to be true + manager.with_display do |environment| + expect(manager.enabled).to be true + expect(environment.class).to eq(Hash) + also_manager = ArduinoCI::DisplayManager::instance + expect(also_manager.enabled).to be true + end + expect(manager.enabled).to be true + end end end From effc5109e44ebbb5b2bd7fcbc0a6b5f90c3ecb86 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Wed, 10 Jan 2018 12:41:18 -0500 Subject: [PATCH 05/36] fix path for OSX --- lib/arduino_ci/arduino_installation.rb | 4 ++-- spec/arduino_cmd_spec.rb | 14 +++++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/arduino_ci/arduino_installation.rb b/lib/arduino_ci/arduino_installation.rb index 231c3ee8..f72ff402 100644 --- a/lib/arduino_ci/arduino_installation.rb +++ b/lib/arduino_ci/arduino_installation.rb @@ -28,9 +28,9 @@ def force_install_location def autolocate ret = new - osx_place = "/Applications/Arduino.app/Contents/MacOS/Arduino" + osx_place = "/Applications/Arduino.app/Contents/MacOS" if File.exist? osx_place - ret.cmd_path = File.join(osx_place, "arduino") + ret.cmd_path = File.join(osx_place, "Arduino") ret.lib_dir = File.join(osx_place, "Libraries") return ret end diff --git a/spec/arduino_cmd_spec.rb b/spec/arduino_cmd_spec.rb index 5d0b2598..ad461de7 100644 --- a/spec/arduino_cmd_spec.rb +++ b/spec/arduino_cmd_spec.rb @@ -1,9 +1,17 @@ require "spec_helper" RSpec.describe ArduinoCI::ArduinoCmd do - it "Finds the Arduino executable" do - arduino_cmd = ArduinoCI::ArduinoCmd.autolocate - # expect(arduino_cmd.path).not_to be nil + context "autolocate" do + it "Finds the Arduino executable" do + arduino_cmd = ArduinoCI::ArduinoCmd.autolocate + end + end + + context "autolocate!" do + it "Finds the Arduino executable" do + arduino_cmd = ArduinoCI::ArduinoCmd.autolocate! + expect(arduino_cmd.installation.cmd_path).not_to be nil + end end end From 64e272dae73fa1d62856f9677113ea504dea2345 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Wed, 10 Jan 2018 12:42:20 -0500 Subject: [PATCH 06/36] add test for the board-is-installed function --- CHANGELOG.md | 1 + spec/arduino_cmd_spec.rb | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57ddf4fb..ccc89a72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - `ArduinoInstallation` class for managing lib / executable paths - `DisplayManager` class for managing Xvfb instance if needed +- `ArduinoCmd` can report on whether a board is installed ### Changed - `DisplayManger.with_display` doesn't `disable` if the display was enabled prior to starting the block diff --git a/spec/arduino_cmd_spec.rb b/spec/arduino_cmd_spec.rb index ad461de7..ac44d081 100644 --- a/spec/arduino_cmd_spec.rb +++ b/spec/arduino_cmd_spec.rb @@ -13,5 +13,16 @@ expect(arduino_cmd.installation.cmd_path).not_to be nil end end -end + context "board_installed?" do + arduino_cmd = ArduinoCI::ArduinoCmd.autolocate! + ArduinoCI::DisplayManager::instance.enable + it "Finds installed boards" do + expect(arduino_cmd.board_installed? "arduino:avr:uno").to be true + end + + it "Doesn't find bogus boards" do + expect(arduino_cmd.board_installed? "eggs:milk:wheat").to be false + end + end +end From 341034de023310c73eac2f20a13330b55199a171 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Wed, 10 Jan 2018 15:55:21 -0500 Subject: [PATCH 07/36] add CI script to gem --- .travis.yml | 1 + arduino_ci.gemspec | 7 +++++-- exe/ci_system_check.rb | 23 +++++++++++++++++++++++ lib/arduino_ci/arduino_cmd.rb | 8 +++++++- 4 files changed, 36 insertions(+), 3 deletions(-) create mode 100755 exe/ci_system_check.rb diff --git a/.travis.yml b/.travis.yml index 08dae787..9a3c487f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,3 +13,4 @@ script: - bundle exec rubocop --version - bundle exec rubocop -D . - bundle exec rspec + - bundle exec ci_system_check.rb diff --git a/arduino_ci.gemspec b/arduino_ci.gemspec index 1ba46750..b25f8ba6 100644 --- a/arduino_ci.gemspec +++ b/arduino_ci.gemspec @@ -14,9 +14,12 @@ Gem::Specification.new do |spec| spec.description = spec.description spec.homepage = "http://github.com/ifreecarve/arduino_ci" - spec.files = ['README.md', '.yardopts'] + Dir['lib/**/*.*'].reject { |f| f.match(%r{^(test|spec|features)/}) } - spec.bindir = "exe" + rejection_regex = %r{^(test|spec|features)/} + libfiles = Dir['lib/**/*.*'].reject { |f| f.match(rejection_regex) } + binfiles = Dir[File.join(spec.bindir, '/**/*.*')].reject { |f| f.match(rejection_regex) } + spec.files = ['README.md', '.yardopts'] + libfiles + binfiles + spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] diff --git a/exe/ci_system_check.rb b/exe/ci_system_check.rb new file mode 100755 index 00000000..bbd104ad --- /dev/null +++ b/exe/ci_system_check.rb @@ -0,0 +1,23 @@ +require 'arduino_ci' + +puts "Enabling display with display manager" +ArduinoCI::DisplayManager::instance.enable + +puts "Autlocating Arduino command" +arduino_cmd = ArduinoCI::ArduinoCmd.autolocate! + +board_tests = { + "arduino:avr:uno" => true, + "eggs:milk:wheat" => false, +} + +got_problem = false +board_tests.each do |k, v| + puts "I expect arduino_cmd.board_installed?(#{k}) to be #{v}" + result = arduino_cmd.board_installed?(k) + puts " board_installed?(#{k}) returns #{result}. expected #{v}" + got_problem = true if v != result +end + +abort if got_problem +exit(0) diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index 13ffc1ed..7c8ef168 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -26,8 +26,14 @@ def initialize(installation) @installation = installation end + def run(*args) + full_args = [@installation.cmd_path] + args + puts "Running $ #{full_args.join(' ')}" + system(*full_args) + end + def board_installed?(board) - system(@installation, "--board", board) + run("--board", board) end end From 2119ebc217159a8bb1b53e820b8306411a51d517 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 11 Jan 2018 07:00:50 -0500 Subject: [PATCH 08/36] wrap 'which' method in its own class and add tests --- lib/arduino_ci/arduino_installation.rb | 16 ++-------------- lib/arduino_ci/display_manager.rb | 1 + lib/arduino_ci/host.rb | 19 +++++++++++++++++++ spec/arduino_ci_spec.rb | 17 +++++++++++++++-- 4 files changed, 37 insertions(+), 16 deletions(-) create mode 100644 lib/arduino_ci/host.rb diff --git a/lib/arduino_ci/arduino_installation.rb b/lib/arduino_ci/arduino_installation.rb index f72ff402..10ef76fd 100644 --- a/lib/arduino_ci/arduino_installation.rb +++ b/lib/arduino_ci/arduino_installation.rb @@ -1,16 +1,4 @@ -# Cross-platform way of finding an executable in the $PATH. -# via https://stackoverflow.com/a/5471032/2063546 -# which('ruby') #=> /usr/bin/ruby -def which(cmd) - exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : [''] - ENV['PATH'].split(File::PATH_SEPARATOR).each do |path| - exts.each do |ext| - exe = File.join(path, "#{cmd}#{ext}") - return exe if File.executable?(exe) && !File.directory?(exe) - end - end - nil -end +require "arduino_ci/host" module ArduinoCI @@ -35,7 +23,7 @@ def autolocate return ret end - posix_place = which("arduino") + posix_place = Host.which("arduino") unless posix_place.nil? ret.cmd_path = posix_place ret.lib_dir = File.join(ENV['HOME'], "Sketchbook") # assume linux diff --git a/lib/arduino_ci/display_manager.rb b/lib/arduino_ci/display_manager.rb index f9e1df83..1965e558 100644 --- a/lib/arduino_ci/display_manager.rb +++ b/lib/arduino_ci/display_manager.rb @@ -1,3 +1,4 @@ +require 'arduino_ci/host' require 'singleton' module ArduinoCI diff --git a/lib/arduino_ci/host.rb b/lib/arduino_ci/host.rb new file mode 100644 index 00000000..aeed2c2f --- /dev/null +++ b/lib/arduino_ci/host.rb @@ -0,0 +1,19 @@ +module ArduinoCI + + # Tools for interacting with the host machine + class Host + # Cross-platform way of finding an executable in the $PATH. + # via https://stackoverflow.com/a/5471032/2063546 + # which('ruby') #=> /usr/bin/ruby + def self.which(cmd) + exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : [''] + ENV['PATH'].split(File::PATH_SEPARATOR).each do |path| + exts.each do |ext| + exe = File.join(path, "#{cmd}#{ext}") + return exe if File.executable?(exe) && !File.directory?(exe) + end + end + nil + end + end +end diff --git a/spec/arduino_ci_spec.rb b/spec/arduino_ci_spec.rb index 0f3d9c57..e63fca26 100644 --- a/spec/arduino_ci_spec.rb +++ b/spec/arduino_ci_spec.rb @@ -1,7 +1,20 @@ require "spec_helper" RSpec.describe ArduinoCI do - it "has a version number" do - expect(ArduinoCI::VERSION).not_to be nil + context "gem" do + it "has a version number" do + expect(ArduinoCI::VERSION).not_to be nil + end end end + +RSpec.describe ArduinoCI::Host do + context "which" do + it "can find things with which" do + ruby_path = ArduinoCI::Host.which("ruby") + expect(ruby_path).not_to be nil + expect(ruby_path.include? "ruby").to be true + end + end + +end From a247e2d58ad4f4c67b2ad5754d8afd419f4d1e7b Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 11 Jan 2018 07:01:28 -0500 Subject: [PATCH 09/36] be more verbose about skipping display manager enable --- lib/arduino_ci/display_manager.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/arduino_ci/display_manager.rb b/lib/arduino_ci/display_manager.rb index 1965e558..7a4ad388 100644 --- a/lib/arduino_ci/display_manager.rb +++ b/lib/arduino_ci/display_manager.rb @@ -25,8 +25,13 @@ def existing_display? # enable a virtual display def enable - return @enabled = true if @existing # silent no-op if built in display - return unless @pid.nil? + if @existing + puts "DisplayManager: no-op for what appears to be an existing display" + @enabled = true + return + end + + return unless @pid.nil? # TODO: disable first? @enabled = true @pid = fork do From a1154665cb65886e72cf2def3df433a63b31d6bd Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 11 Jan 2018 07:06:54 -0500 Subject: [PATCH 10/36] fix logic in existing_display? --- lib/arduino_ci/display_manager.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/arduino_ci/display_manager.rb b/lib/arduino_ci/display_manager.rb index 7a4ad388..bb223964 100644 --- a/lib/arduino_ci/display_manager.rb +++ b/lib/arduino_ci/display_manager.rb @@ -17,16 +17,16 @@ def initialize # attempt to determine if the machine is running a graphical display (i.e. not Travis) def existing_display? - return true if RUBY_PLATFORM.include? "darwin" - return true if ENV["DISPLAY"].nil? - return true if ENV["DISPLAY"].include? ":" + return true if RUBY_PLATFORM.include? "darwin" + return false if ENV["DISPLAY"].nil? + return true if ENV["DISPLAY"].include? ":" false end # enable a virtual display def enable if @existing - puts "DisplayManager: no-op for what appears to be an existing display" + puts "DisplayManager enable: no-op for what appears to be an existing display" @enabled = true return end From 49c42ff0a2891e1a313e5baa5a1ed705882e92bd Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 11 Jan 2018 07:57:27 -0500 Subject: [PATCH 11/36] use IO.popen instead of forking a fork --- lib/arduino_ci/display_manager.rb | 32 +++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/lib/arduino_ci/display_manager.rb b/lib/arduino_ci/display_manager.rb index bb223964..0d2102c5 100644 --- a/lib/arduino_ci/display_manager.rb +++ b/lib/arduino_ci/display_manager.rb @@ -1,5 +1,6 @@ require 'arduino_ci/host' require 'singleton' +require 'timeout' module ArduinoCI @@ -33,27 +34,38 @@ def enable return unless @pid.nil? # TODO: disable first? - @enabled = true - @pid = fork do - puts "Forking Xvfb" - system("Xvfb", ":1", "-ac", "-screen", "0", "1280x1024x16") - puts "Xvfb unexpectedly quit!" - end + # open Xvfb + xvfb_cmd = ["Xvfb", ":1", "-ac", "-screen", "0", "1280x1024x16"] + puts "pipeline_start for Xvfb" + pipe = IO.popen(xvfb_cmd) + @pid = pipe.pid sleep(3) # TODO: test a connection to the X server? + @enabled = true end # disable the virtual display def disable - return @enabled = false if @existing # silent no-op if built in display - return if @pid.nil? + if @existing + puts "DisplayManager disable: no-op for what appears to be an existing display" + return @enabled = false + end + + return @enabled = false if @pid.nil? + # https://www.whatastruggle.com/timeout-a-subprocess-in-ruby begin - Process.kill 9, @pid + Timeout.timeout(30) do + Process.kill("TERM", @pid) + puts "Xvfb TERMed" + end + rescue Timeout::Error + Process.kill(9, @pid) + puts "Xvfb KILLed" ensure Process.wait @pid + @enabled = false @pid = nil end - puts "Xvfb killed" end # Enable a virtual display for the duration of the given block From 6d38e6682d815829ce9b4448971146f278877c46 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 11 Jan 2018 12:14:05 -0500 Subject: [PATCH 12/36] add environment to run command --- lib/arduino_ci/arduino_cmd.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index 7c8ef168..913c824f 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -27,7 +27,7 @@ def initialize(installation) end def run(*args) - full_args = [@installation.cmd_path] + args + full_args = [@display_mgr.environment, @installation.cmd_path] + args puts "Running $ #{full_args.join(' ')}" system(*full_args) end From 7f6b2167b2e707c663eaba6933e7579fa1c2b0ca Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 11 Jan 2018 12:17:10 -0500 Subject: [PATCH 13/36] quote the string --- lib/arduino_ci/display_manager.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/arduino_ci/display_manager.rb b/lib/arduino_ci/display_manager.rb index 0d2102c5..ecc1acd4 100644 --- a/lib/arduino_ci/display_manager.rb +++ b/lib/arduino_ci/display_manager.rb @@ -82,7 +82,7 @@ def with_display def environment return nil unless @existing || @enabled return {} if @existing - { DISPLAY => ":1.0" } + { "DISPLAY" => ":1.0" } end # On finalize, ensure child process is ended From 71c1d3f77c0f11771ea08bfaa3c454e5dd359116 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 11 Jan 2018 12:19:55 -0500 Subject: [PATCH 14/36] use builtin ruby to save development time --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9a3c487f..e404d04b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,12 @@ sudo: false language: ruby -rvm: +# rvm: # - "2.0.0" # - "2.1.0" # - "2.2.0" # - rbx - - "2.5.0" +# - "2.5.0" before_install: gem install bundler -v 1.15.4 script: From a1c6386b1cccb5b52f5da5150b489dcfaa52b9d9 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 11 Jan 2018 12:29:02 -0500 Subject: [PATCH 15/36] log all parameters in arduino_cmd run --- lib/arduino_ci/arduino_cmd.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index 913c824f..c0294560 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -27,9 +27,10 @@ def initialize(installation) end def run(*args) - full_args = [@display_mgr.environment, @installation.cmd_path] + args - puts "Running $ #{full_args.join(' ')}" - system(*full_args) + full_args = [@installation.cmd_path] + args + full_cmd = [@display_mgr.environment] + full_args + puts "Running #{@display_mgr.environment} $ #{full_args.join(' ')}" + system(*full_cmd) end def board_installed?(board) From c0c3ce36ad891bae553bf01ad7b86d91e8b8dd34 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 11 Jan 2018 12:29:56 -0500 Subject: [PATCH 16/36] test output of xdpyinfo --- lib/arduino_ci/display_manager.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/arduino_ci/display_manager.rb b/lib/arduino_ci/display_manager.rb index ecc1acd4..62b7c144 100644 --- a/lib/arduino_ci/display_manager.rb +++ b/lib/arduino_ci/display_manager.rb @@ -41,6 +41,8 @@ def enable @pid = pipe.pid sleep(3) # TODO: test a connection to the X server? @enabled = true + puts "\n\nxdpyinfo:\n\n" + system(environment, "xdpyinfo") end # disable the virtual display From 71198069a82bc8b67c9e94c1699f3ea71c80549b Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 11 Jan 2018 13:29:11 -0500 Subject: [PATCH 17/36] expose ability to run commands in display environment --- lib/arduino_ci/display_manager.rb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lib/arduino_ci/display_manager.rb b/lib/arduino_ci/display_manager.rb index 62b7c144..ed5ff345 100644 --- a/lib/arduino_ci/display_manager.rb +++ b/lib/arduino_ci/display_manager.rb @@ -81,6 +81,28 @@ def with_display end end + # run a command in a display + def run(*args, **kwargs) + ret = false + # do some work to extract & merge environment variables if they exist + has_env = !args.empty? && args[0].class == Hash + with_display do |env_vars| + env_vars = {} if env_vars.nil? + env_vars.merge!(args[0]) if has_env + actual_args = has_env ? args[1..-1] : args # need to shift over if we extracted args + full_cmd = env_vars.empty? ? actual_args : [env_vars] + actual_args + + puts "Running #{env_vars} $ #{args.join(' ')}" + ret = system(*full_cmd, **kwargs) + end + ret + end + + # run a command in a display with no output + def run_silent(*args) + run(*args, out: File::NULL, err: File::NULL) + end + def environment return nil unless @existing || @enabled return {} if @existing From 7740d577ef0f865a19400fb12a8a514d9b4e2774 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 11 Jan 2018 13:45:00 -0500 Subject: [PATCH 18/36] wrap the display manager command runner --- lib/arduino_ci/arduino_cmd.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index c0294560..e03cc97a 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -26,11 +26,10 @@ def initialize(installation) @installation = installation end + # run the arduino command def run(*args) full_args = [@installation.cmd_path] + args - full_cmd = [@display_mgr.environment] + full_args - puts "Running #{@display_mgr.environment} $ #{full_args.join(' ')}" - system(*full_cmd) + @display_mgr.run(*full_args) end def board_installed?(board) From 6d6b08421d757dcc6e3d40bbc167c6d08b09c4c4 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 11 Jan 2018 13:46:09 -0500 Subject: [PATCH 19/36] query x server to wait for launch --- lib/arduino_ci/display_manager.rb | 54 +++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/lib/arduino_ci/display_manager.rb b/lib/arduino_ci/display_manager.rb index ed5ff345..598d4019 100644 --- a/lib/arduino_ci/display_manager.rb +++ b/lib/arduino_ci/display_manager.rb @@ -2,6 +2,8 @@ require 'singleton' require 'timeout' +DESIRED_DISPLAY = ":1.0".freeze + module ArduinoCI # When arduino commands run, they need a graphical display. @@ -24,6 +26,42 @@ def existing_display? false end + # check whether a process is alive + # https://stackoverflow.com/a/32513298/2063546 + def alive?(pid) + Process.kill(0, pid) + true + rescue + false + end + + # check whether an X server is taking connections + def xserver_exist?(display) + system({ "DISPLAY" => display }, "xdpyinfo", out: File::NULL, err: File::NULL) + end + + # wait for the xvfb command to launch + # @param display [String] the value of the DISPLAY env var + # @param pid [Int] the process of Xvfb + # @param timeout [Int] the timeout in seconds + # @return [Bool] whether we detected a launch + def xvfb_launched?(display, pid, timeout) + Timeout.timeout(timeout) do + loop do + unless alive? pid + puts "Xvfb process has died" + return false + end + x = xserver_exist? display + puts "xdpyinfo reports X server status as #{x}" + return true if x + sleep(0.1) + end + end + rescue Timeout::Error + false + end + # enable a virtual display def enable if @existing @@ -35,14 +73,18 @@ def enable return unless @pid.nil? # TODO: disable first? # open Xvfb - xvfb_cmd = ["Xvfb", ":1", "-ac", "-screen", "0", "1280x1024x16"] + xvfb_cmd = [ + "Xvfb", + "+extension", "RANDR", + ":1", + "-ac", + "-screen", "0", + "1280x1024x16", + ] puts "pipeline_start for Xvfb" pipe = IO.popen(xvfb_cmd) @pid = pipe.pid - sleep(3) # TODO: test a connection to the X server? - @enabled = true - puts "\n\nxdpyinfo:\n\n" - system(environment, "xdpyinfo") + @enabled = xvfb_launched?(DESIRED_DISPLAY, @pid, 30) end # disable the virtual display @@ -106,7 +148,7 @@ def run_silent(*args) def environment return nil unless @existing || @enabled return {} if @existing - { "DISPLAY" => ":1.0" } + { "DISPLAY" => DESIRED_DISPLAY } end # On finalize, ensure child process is ended From 7e58049b7e788f53d085604ab68d9944ceab1acf Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 11 Jan 2018 13:46:19 -0500 Subject: [PATCH 20/36] Add some doc comments --- lib/arduino_ci/arduino_cmd.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index e03cc97a..fdb0bdfb 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -9,10 +9,12 @@ class ArduinoCmd class << self protected :new + # @return [ArduinoCmd] A command object with a best guess (or nil) for the installation def autolocate new(ArduinoInstallation.autolocate) end + # @return [ArduinoCmd] A command object, installing Arduino if necessary def autolocate! new(ArduinoInstallation.autolocate!) end @@ -21,6 +23,7 @@ def autolocate! attr_accessor :installation + # @param installation [ArduinoInstallation] the location of the Arduino program installation def initialize(installation) @display_mgr = DisplayManager::instance @installation = installation From c3237b0afbcb839300ee4857e1a9373a15b7e4e6 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 11 Jan 2018 14:41:23 -0500 Subject: [PATCH 21/36] hail mary logging --- lib/arduino_ci/display_manager.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/arduino_ci/display_manager.rb b/lib/arduino_ci/display_manager.rb index 598d4019..3be73bf1 100644 --- a/lib/arduino_ci/display_manager.rb +++ b/lib/arduino_ci/display_manager.rb @@ -134,9 +134,13 @@ def run(*args, **kwargs) actual_args = has_env ? args[1..-1] : args # need to shift over if we extracted args full_cmd = env_vars.empty? ? actual_args : [env_vars] + actual_args - puts "Running #{env_vars} $ #{args.join(' ')}" + puts "Running #{env_vars} $ #{actual_args.join(' ')}" + puts "Full_cmd is #{full_cmd}" + puts "kwargs is #{kwargs}" ret = system(*full_cmd, **kwargs) + puts "system call to #{actual_args[0]} has completed" end + puts "with_display has completed" ret end @@ -147,7 +151,7 @@ def run_silent(*args) def environment return nil unless @existing || @enabled - return {} if @existing + return { "EXISTING_DISPLAY" => "YES" } if @existing { "DISPLAY" => DESIRED_DISPLAY } end From dc2b35fd50c1ba3095648acaeeeede7cb2c2045f Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Fri, 12 Jan 2018 12:16:39 -0500 Subject: [PATCH 22/36] add ability to capture preferences --- CHANGELOG.md | 3 ++- lib/arduino_ci/arduino_cmd.rb | 48 ++++++++++++++++++++++++++++++++--- spec/arduino_cmd_spec.rb | 2 ++ 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ccc89a72..ee6836fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - `ArduinoInstallation` class for managing lib / executable paths - `DisplayManager` class for managing Xvfb instance if needed -- `ArduinoCmd` can report on whether a board is installed +- `ArduinoCmd` captures and caches preferences +- `ArduinoCmd` reports on whether a board is installed ### Changed - `DisplayManger.with_display` doesn't `disable` if the display was enabled prior to starting the block diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index fdb0bdfb..4e37da62 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -22,17 +22,57 @@ def autolocate! end attr_accessor :installation + attr_reader :prefs_cache + attr_reader :prefs_response_time # @param installation [ArduinoInstallation] the location of the Arduino program installation def initialize(installation) - @display_mgr = DisplayManager::instance - @installation = installation + @display_mgr = DisplayManager::instance + @installation = installation + @prefs_response_time = nil + @prefs_cache = prefs + end + + # fetch preferences to a hash + def prefs + resp = nil + @display_mgr.with_display do + start = Time.now + resp = run_and_capture("--get-pref") + @prefs_response_time = Time.now - start + puts "prefs_response_time = #{@prefs_response_time} = #{Time.now} - #{start}" + end + return nil unless resp[:success] + lines = resp[:out].split("\n").select { |l| l.include? "=" } + ret = lines.each_with_object({}) do |e, acc| + parts = e.split("=", 2) + acc[parts[0]] = parts[1] + acc + end + ret end # run the arduino command - def run(*args) + # @return [Hash] {:out => StringIO, :err => StringIO } + def run(*args, **kwargs) full_args = [@installation.cmd_path] + args - @display_mgr.run(*full_args) + @display_mgr.run(*full_args, **kwargs) + + end + + # run a command and capture its output + # @return [Hash] {:out => StringIO, :err => StringIO, :success => bool} + def run_and_capture(*args) + pipe_out, pipe_out_wr = IO.pipe + pipe_err, pipe_err_wr = IO.pipe + success = run(*args, out: pipe_out_wr, err: pipe_err_wr) + pipe_out_wr.close + pipe_err_wr.close + str_out = pipe_out.read + str_err = pipe_err.read + pipe_out.close + pipe_err.close + { out: str_out, err: str_err, success: success } end def board_installed?(board) diff --git a/spec/arduino_cmd_spec.rb b/spec/arduino_cmd_spec.rb index ac44d081..6be396fc 100644 --- a/spec/arduino_cmd_spec.rb +++ b/spec/arduino_cmd_spec.rb @@ -11,6 +11,8 @@ it "Finds the Arduino executable" do arduino_cmd = ArduinoCI::ArduinoCmd.autolocate! expect(arduino_cmd.installation.cmd_path).not_to be nil + expect(arduino_cmd.prefs_cache.class).to be Hash + expect(arduino_cmd.prefs_response_time).not_to be nil end end From fdeda7fa5870948d28103c862feadbae5b712869 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Fri, 12 Jan 2018 12:17:45 -0500 Subject: [PATCH 23/36] guess when graphical error message prevents command from completing --- lib/arduino_ci/arduino_cmd.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index 4e37da62..1e231533 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -76,7 +76,15 @@ def run_and_capture(*args) end def board_installed?(board) - run("--board", board) + # On Travis CI, we get an error message in the GUI instead of on STDERR + # so, assume that if we don't get a rapid reply that things are not installed + x3 = @prefs_response_time * 3 + Timeout.timeout(x3) do + run("--board", board) + end + rescue Timeout::Error + puts "No response in #{x3} seconds. Assuming graphical modal error message about board not installed." + false end end From 110daa12c9053f18f2e696df02a7814aba8faa41 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Fri, 12 Jan 2018 12:32:34 -0500 Subject: [PATCH 24/36] mute a lot of logging --- lib/arduino_ci/arduino_cmd.rb | 1 - lib/arduino_ci/display_manager.rb | 26 ++++++++++++++++++-------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index 1e231533..5a7d493c 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -40,7 +40,6 @@ def prefs start = Time.now resp = run_and_capture("--get-pref") @prefs_response_time = Time.now - start - puts "prefs_response_time = #{@prefs_response_time} = #{Time.now} - #{start}" end return nil unless resp[:success] lines = resp[:out].split("\n").select { |l| l.include? "=" } diff --git a/lib/arduino_ci/display_manager.rb b/lib/arduino_ci/display_manager.rb index 3be73bf1..8debb4ac 100644 --- a/lib/arduino_ci/display_manager.rb +++ b/lib/arduino_ci/display_manager.rb @@ -16,6 +16,11 @@ def initialize @existing = existing_display? @enabled = false @pid = nil + + @xv_pipe_out_wr = nil + @xv_pipe_err_wr = nil + @xv_pipe_out = nil + @xv_pipe_err = nil end # attempt to determine if the machine is running a graphical display (i.e. not Travis) @@ -71,6 +76,8 @@ def enable end return unless @pid.nil? # TODO: disable first? + @xv_pipe_out.close unless @xv_pipe_out.nil? + @xv_pipe_err.close unless @xv_pipe_err.nil? # open Xvfb xvfb_cmd = [ @@ -81,8 +88,11 @@ def enable "-screen", "0", "1280x1024x16", ] - puts "pipeline_start for Xvfb" - pipe = IO.popen(xvfb_cmd) + puts "Xvfb launching" + + @xv_pipe_out, @xv_pipe_out_wr = IO.pipe + @xv_pipe_err, @xv_pipe_err_wr = IO.pipe + pipe = IO.popen(xvfb_cmd, stdout: @xv_pipe_out_wr, err: @xv_pipe_err_wr) @pid = pipe.pid @enabled = xvfb_launched?(DESIRED_DISPLAY, @pid, 30) end @@ -109,6 +119,9 @@ def disable Process.wait @pid @enabled = false @pid = nil + + @xv_pipe_out_wr.close + @xv_pipe_err_wr.close end end @@ -133,14 +146,11 @@ def run(*args, **kwargs) env_vars.merge!(args[0]) if has_env actual_args = has_env ? args[1..-1] : args # need to shift over if we extracted args full_cmd = env_vars.empty? ? actual_args : [env_vars] + actual_args - - puts "Running #{env_vars} $ #{actual_args.join(' ')}" - puts "Full_cmd is #{full_cmd}" - puts "kwargs is #{kwargs}" + shell_vars = env_vars.map { |k, v| "#{k}=#{v}" }.join(" ") + puts " $ #{shell_vars} #{actual_args.join(' ')}" ret = system(*full_cmd, **kwargs) - puts "system call to #{actual_args[0]} has completed" + puts "#{actual_args[0]} has completed" end - puts "with_display has completed" ret end From 8fc631c7901883249323436f75bb4643cfb86705 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Fri, 12 Jan 2018 13:44:30 -0500 Subject: [PATCH 25/36] factor gui guessing into its own function --- lib/arduino_ci/arduino_cmd.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index 5a7d493c..f7fd133f 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -56,7 +56,18 @@ def prefs def run(*args, **kwargs) full_args = [@installation.cmd_path] + args @display_mgr.run(*full_args, **kwargs) + end + def run_with_gui_guess(message, *args, **kwargs) + # On Travis CI, we get an error message in the GUI instead of on STDERR + # so, assume that if we don't get a rapid reply that things are not installed + x3 = @prefs_response_time * 3 + Timeout.timeout(x3) do + run(*args, **kwargs) + end + rescue Timeout::Error + puts "No response in #{x3} seconds. Assuming graphical modal error message#{message}." + false end # run a command and capture its output From e8a7610ff81876bcf260be9807b0d4afdebf2810 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Fri, 12 Jan 2018 14:04:58 -0500 Subject: [PATCH 26/36] add ability to set prefs --- CHANGELOG.md | 1 + exe/ci_system_check.rb | 8 ++++++++ lib/arduino_ci/arduino_cmd.rb | 10 ++++++++++ spec/arduino_cmd_spec.rb | 11 +++++++++++ 4 files changed, 30 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee6836fc..13bb6925 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - `DisplayManager` class for managing Xvfb instance if needed - `ArduinoCmd` captures and caches preferences - `ArduinoCmd` reports on whether a board is installed +- `ArduinoCmd` sets preferences ### Changed - `DisplayManger.with_display` doesn't `disable` if the display was enabled prior to starting the block diff --git a/exe/ci_system_check.rb b/exe/ci_system_check.rb index bbd104ad..c2d6bff9 100755 --- a/exe/ci_system_check.rb +++ b/exe/ci_system_check.rb @@ -19,5 +19,13 @@ got_problem = true if v != result end +urls = [ + "https://adafruit.github.io/arduino-board-index/package_adafruit_index.json", + "http://arduino.esp8266.com/stable/package_esp8266com_index.json" +] + +result = arduino_cmd.set_pref("boardsmanager.additional.urls", urls.join(",")) +got_problem = true unless result + abort if got_problem exit(0) diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index f7fd133f..69c562e5 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -51,6 +51,16 @@ def prefs ret end + # set a preference key/value pair + # @param key [String] the preference key + # @param value [String] the preference value + # @return [bool] whether the command succeeded + def set_pref(key, value) + success = run_with_gui_guess(" about preferences", "--pref", "#{key}=#{value}", "--save-prefs") + @prefs_cache[key] = value if success + success + end + # run the arduino command # @return [Hash] {:out => StringIO, :err => StringIO } def run(*args, **kwargs) diff --git a/spec/arduino_cmd_spec.rb b/spec/arduino_cmd_spec.rb index 6be396fc..9b813310 100644 --- a/spec/arduino_cmd_spec.rb +++ b/spec/arduino_cmd_spec.rb @@ -27,4 +27,15 @@ expect(arduino_cmd.board_installed? "eggs:milk:wheat").to be false end end + + context "set_pref" do + arduino_cmd = ArduinoCI::ArduinoCmd.autolocate! + ArduinoCI::DisplayManager::instance.enable + + it "Sets key to what it was before" do + upload_verify = arduino_cmd.prefs_cache["upload.verify"] + result = arduino_cmd.set_pref("upload.verify", upload_verify) + expect(result).to be true + end + end end From a76f0745ba38f038410a9f1723af4d2715e99005 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Fri, 12 Jan 2018 14:47:37 -0500 Subject: [PATCH 27/36] board_installed? use run_with_gui_guess --- lib/arduino_ci/arduino_cmd.rb | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index 69c562e5..a0f56ee7 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -96,15 +96,7 @@ def run_and_capture(*args) end def board_installed?(board) - # On Travis CI, we get an error message in the GUI instead of on STDERR - # so, assume that if we don't get a rapid reply that things are not installed - x3 = @prefs_response_time * 3 - Timeout.timeout(x3) do - run("--board", board) - end - rescue Timeout::Error - puts "No response in #{x3} seconds. Assuming graphical modal error message about board not installed." - false + run_with_gui_guess(" about board not installed", "--board", board) end end From a4f3ce313d46a6e47ddd08645e6f25afce51c932 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Fri, 12 Jan 2018 14:59:33 -0500 Subject: [PATCH 28/36] add commands to install boards and libraries --- CHANGELOG.md | 2 ++ exe/ci_system_check.rb | 4 ++++ lib/arduino_ci/arduino_cmd.rb | 29 +++++++++++++++++++++++++++-- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13bb6925..9164b474 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - `ArduinoCmd` captures and caches preferences - `ArduinoCmd` reports on whether a board is installed - `ArduinoCmd` sets preferences +- `ArduinoCmd` installs boards +- `ArduinoCmd` installs libraries ### Changed - `DisplayManger.with_display` doesn't `disable` if the display was enabled prior to starting the block diff --git a/exe/ci_system_check.rb b/exe/ci_system_check.rb index c2d6bff9..c87879ba 100755 --- a/exe/ci_system_check.rb +++ b/exe/ci_system_check.rb @@ -27,5 +27,9 @@ result = arduino_cmd.set_pref("boardsmanager.additional.urls", urls.join(",")) got_problem = true unless result +got_problem = true unless arduino_cmd.install_board("arduino:sam") +got_problem = true unless arduino_cmd.install_library("USBHost") +got_problem = true unless arduino_cmd.library_is_indexed + abort if got_problem exit(0) diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index a0f56ee7..5176119e 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -24,6 +24,7 @@ def autolocate! attr_accessor :installation attr_reader :prefs_cache attr_reader :prefs_response_time + attr_reader :library_is_indexed # @param installation [ArduinoInstallation] the location of the Arduino program installation def initialize(installation) @@ -31,6 +32,7 @@ def initialize(installation) @installation = installation @prefs_response_time = nil @prefs_cache = prefs + @library_is_indexed = false end # fetch preferences to a hash @@ -95,8 +97,31 @@ def run_and_capture(*args) { out: str_out, err: str_err, success: success } end - def board_installed?(board) - run_with_gui_guess(" about board not installed", "--board", board) + def board_installed?(boardname) + run_with_gui_guess(" about board not installed", "--board", boardname) + end + + # install a board by name + # @param name [String] the board name + # @return [bool] whether the command succeeded + def install_board(boardname) + run("--install-boards", boardname) + end + + # install a library by name + # @param name [String] the library name + # @return [bool] whether the command succeeded + def install_library(library_name) + result = run("--install-library", library_name) + @library_is_indexed = true if result + result + end + + # update the library index + def update_library_index + # install random lib so the arduino IDE grabs a new library index + # see: https://github.com/arduino/Arduino/issues/3535 + install_library("USBHost") end end From 8b26d2aa78d2a9e5489bd16ac6206f1393ec4eec Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Fri, 12 Jan 2018 15:40:06 -0500 Subject: [PATCH 29/36] suppress arduino output --- lib/arduino_ci/arduino_cmd.rb | 18 +++++++++++------- spec/arduino_cmd_spec.rb | 8 ++++++-- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index 5176119e..bf2b8af3 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -58,13 +58,13 @@ def prefs # @param value [String] the preference value # @return [bool] whether the command succeeded def set_pref(key, value) - success = run_with_gui_guess(" about preferences", "--pref", "#{key}=#{value}", "--save-prefs") + resp = run_and_capture(" about preferences", "--pref", "#{key}=#{value}", "--save-prefs") + success = resp[:success] @prefs_cache[key] = value if success success end # run the arduino command - # @return [Hash] {:out => StringIO, :err => StringIO } def run(*args, **kwargs) full_args = [@installation.cmd_path] + args @display_mgr.run(*full_args, **kwargs) @@ -75,7 +75,8 @@ def run_with_gui_guess(message, *args, **kwargs) # so, assume that if we don't get a rapid reply that things are not installed x3 = @prefs_response_time * 3 Timeout.timeout(x3) do - run(*args, **kwargs) + result = run_and_capture(*args, **kwargs) + result[:success] end rescue Timeout::Error puts "No response in #{x3} seconds. Assuming graphical modal error message#{message}." @@ -97,6 +98,9 @@ def run_and_capture(*args) { out: str_out, err: str_err, success: success } end + # check whether a board is installed + # we do this by just selecting a board. + # the arduino binary will error if unrecognized and do a successful no-op if it's installed def board_installed?(boardname) run_with_gui_guess(" about board not installed", "--board", boardname) end @@ -105,16 +109,16 @@ def board_installed?(boardname) # @param name [String] the board name # @return [bool] whether the command succeeded def install_board(boardname) - run("--install-boards", boardname) + run_and_capture("--install-boards", boardname)[:success] end # install a library by name # @param name [String] the library name # @return [bool] whether the command succeeded def install_library(library_name) - result = run("--install-library", library_name) - @library_is_indexed = true if result - result + result = run_and_capture("--install-library", library_name) + @library_is_indexed = true if result[:success] + result[:success] end # update the library index diff --git a/spec/arduino_cmd_spec.rb b/spec/arduino_cmd_spec.rb index 9b813310..07670fc0 100644 --- a/spec/arduino_cmd_spec.rb +++ b/spec/arduino_cmd_spec.rb @@ -20,11 +20,15 @@ arduino_cmd = ArduinoCI::ArduinoCmd.autolocate! ArduinoCI::DisplayManager::instance.enable it "Finds installed boards" do - expect(arduino_cmd.board_installed? "arduino:avr:uno").to be true + uno_installed = arduino_cmd.board_installed? "arduino:avr:uno" + expect(uno_installed).to be true + expect(uno_installed).not_to be nil end it "Doesn't find bogus boards" do - expect(arduino_cmd.board_installed? "eggs:milk:wheat").to be false + bogus_installed = arduino_cmd.board_installed? "eggs:milk:wheat" + expect(bogus_installed).to be false + expect(bogus_installed).not_to be nil end end From 135f37424fda1daa50491fbdeb70eaab0fa18685 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sat, 13 Jan 2018 09:34:04 -0500 Subject: [PATCH 30/36] prefs can fail graphically, so handle them graphically --- lib/arduino_ci/arduino_cmd.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index bf2b8af3..7a72128a 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -58,8 +58,7 @@ def prefs # @param value [String] the preference value # @return [bool] whether the command succeeded def set_pref(key, value) - resp = run_and_capture(" about preferences", "--pref", "#{key}=#{value}", "--save-prefs") - success = resp[:success] + success = run_with_gui_guess(" about preferences", "--pref", "#{key}=#{value}", "--save-prefs") @prefs_cache[key] = value if success success end From 78fca73eda78b5fb2d7d9271b3fa7ae0a9dd3ff0 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sat, 13 Jan 2018 09:34:39 -0500 Subject: [PATCH 31/36] expose kwargs explicitly in run call --- lib/arduino_ci/arduino_cmd.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index 7a72128a..f95796c8 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -84,10 +84,12 @@ def run_with_gui_guess(message, *args, **kwargs) # run a command and capture its output # @return [Hash] {:out => StringIO, :err => StringIO, :success => bool} - def run_and_capture(*args) + def run_and_capture(*args, **kwargs) pipe_out, pipe_out_wr = IO.pipe pipe_err, pipe_err_wr = IO.pipe - success = run(*args, out: pipe_out_wr, err: pipe_err_wr) + our_kwargs = { out: pipe_out_wr, err: pipe_err_wr } + eventual_kwargs = kwargs.merge(our_kwargs) + success = run(*args, **eventual_kwargs) pipe_out_wr.close pipe_err_wr.close str_out = pipe_out.read From 6124d6ac64448b99e491f2bdc5daf3de589065b2 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sat, 13 Jan 2018 09:35:18 -0500 Subject: [PATCH 32/36] make debug messages in display_manager toggleable --- lib/arduino_ci/display_manager.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/arduino_ci/display_manager.rb b/lib/arduino_ci/display_manager.rb index 8debb4ac..34e15e92 100644 --- a/lib/arduino_ci/display_manager.rb +++ b/lib/arduino_ci/display_manager.rb @@ -11,11 +11,13 @@ module ArduinoCI class DisplayManager include Singleton attr_reader :enabled + attr_accessor :debug def initialize @existing = existing_display? @enabled = false @pid = nil + @debug = false @xv_pipe_out_wr = nil @xv_pipe_err_wr = nil @@ -58,7 +60,7 @@ def xvfb_launched?(display, pid, timeout) return false end x = xserver_exist? display - puts "xdpyinfo reports X server status as #{x}" + puts "xdpyinfo reports X server status as #{x}" if debug return true if x sleep(0.1) end @@ -70,7 +72,7 @@ def xvfb_launched?(display, pid, timeout) # enable a virtual display def enable if @existing - puts "DisplayManager enable: no-op for what appears to be an existing display" + puts "DisplayManager enable: no-op for what appears to be an existing display" if debug @enabled = true return end @@ -88,7 +90,7 @@ def enable "-screen", "0", "1280x1024x16", ] - puts "Xvfb launching" + puts "Xvfb launching" if debug @xv_pipe_out, @xv_pipe_out_wr = IO.pipe @xv_pipe_err, @xv_pipe_err_wr = IO.pipe @@ -100,7 +102,7 @@ def enable # disable the virtual display def disable if @existing - puts "DisplayManager disable: no-op for what appears to be an existing display" + puts "DisplayManager disable: no-op for what appears to be an existing display" if debug return @enabled = false end @@ -110,11 +112,11 @@ def disable begin Timeout.timeout(30) do Process.kill("TERM", @pid) - puts "Xvfb TERMed" + puts "Xvfb TERMed" if debug end rescue Timeout::Error Process.kill(9, @pid) - puts "Xvfb KILLed" + puts "Xvfb KILLed" if debug ensure Process.wait @pid @enabled = false From 87fb012643852661716e81ccb2d8b3ebbf274669 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sat, 13 Jan 2018 09:54:57 -0500 Subject: [PATCH 33/36] add run wrapper that does not capture, but with the same signture as run_with_capture --- lib/arduino_ci/arduino_cmd.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index f95796c8..05fbb265 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -83,7 +83,7 @@ def run_with_gui_guess(message, *args, **kwargs) end # run a command and capture its output - # @return [Hash] {:out => StringIO, :err => StringIO, :success => bool} + # @return [Hash] {:out => String, :err => String, :success => bool} def run_and_capture(*args, **kwargs) pipe_out, pipe_out_wr = IO.pipe pipe_err, pipe_err_wr = IO.pipe @@ -99,6 +99,13 @@ def run_and_capture(*args, **kwargs) { out: str_out, err: str_err, success: success } end + # run a command and don't capture its output, but use the same signature + # @return [Hash] {:out => String, :err => String, :success => bool} + def run_wrap(*args, **kwargs) + success = run(*args, **kwargs) + { out: "NOPE, use run_and_capture", err: "NOPE, use run_and_capture", success: success } + end + # check whether a board is installed # we do this by just selecting a board. # the arduino binary will error if unrecognized and do a successful no-op if it's installed From fb389439cc73a05e6984ef89cad97ae3a501c379 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sat, 13 Jan 2018 09:56:05 -0500 Subject: [PATCH 34/36] add more to test script, get closer to install.sh parity --- exe/ci_system_check.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/exe/ci_system_check.rb b/exe/ci_system_check.rb index c87879ba..1d38bc05 100755 --- a/exe/ci_system_check.rb +++ b/exe/ci_system_check.rb @@ -24,12 +24,13 @@ "http://arduino.esp8266.com/stable/package_esp8266com_index.json" ] -result = arduino_cmd.set_pref("boardsmanager.additional.urls", urls.join(",")) -got_problem = true unless result +got_problem = true unless arduino_cmd.set_pref("boardsmanager.additional.urls", urls.join(",")) got_problem = true unless arduino_cmd.install_board("arduino:sam") got_problem = true unless arduino_cmd.install_library("USBHost") got_problem = true unless arduino_cmd.library_is_indexed +got_problem = true unless arduino_cmd.set_pref("compiler.warning_level", "all") + abort if got_problem exit(0) From d2d77b597947674d809f943e56f4d726ac1bbb86 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sat, 13 Jan 2018 10:39:39 -0500 Subject: [PATCH 35/36] use null file instead of IO.pipe. not sure why this matters for success --- lib/arduino_ci/arduino_cmd.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index 05fbb265..336f41a1 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -88,7 +88,7 @@ def run_and_capture(*args, **kwargs) pipe_out, pipe_out_wr = IO.pipe pipe_err, pipe_err_wr = IO.pipe our_kwargs = { out: pipe_out_wr, err: pipe_err_wr } - eventual_kwargs = kwargs.merge(our_kwargs) + eventual_kwargs = our_kwargs.merge(kwargs) success = run(*args, **eventual_kwargs) pipe_out_wr.close pipe_err_wr.close @@ -117,7 +117,8 @@ def board_installed?(boardname) # @param name [String] the board name # @return [bool] whether the command succeeded def install_board(boardname) - run_and_capture("--install-boards", boardname)[:success] + # TODO: find out why IO.pipe fails but File::NULL succeeds :( + run_and_capture("--install-boards", boardname, out: File::NULL)[:success] end # install a library by name From 64b08868cf947de4154c662442e72e25e7378259 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sat, 13 Jan 2018 23:14:22 -0500 Subject: [PATCH 36/36] allow switching to a particular board for compilation --- CHANGELOG.md | 1 + exe/ci_system_check.rb | 10 +++++++++- lib/arduino_ci/arduino_cmd.rb | 14 ++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9164b474..4f521c27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - `ArduinoCmd` sets preferences - `ArduinoCmd` installs boards - `ArduinoCmd` installs libraries +- `ArduinoCmd` selects boards (compiler preference) ### Changed - `DisplayManger.with_display` doesn't `disable` if the display was enabled prior to starting the block diff --git a/exe/ci_system_check.rb b/exe/ci_system_check.rb index 1d38bc05..84b565c9 100755 --- a/exe/ci_system_check.rb +++ b/exe/ci_system_check.rb @@ -24,13 +24,21 @@ "http://arduino.esp8266.com/stable/package_esp8266com_index.json" ] +puts "Setting additional URLs" got_problem = true unless arduino_cmd.set_pref("boardsmanager.additional.urls", urls.join(",")) +puts "Installing arduino:sam" got_problem = true unless arduino_cmd.install_board("arduino:sam") +puts "Installing USBHost" got_problem = true unless arduino_cmd.install_library("USBHost") +puts "checking that library is indexed" got_problem = true unless arduino_cmd.library_is_indexed - +puts "setting compiler warning level" got_problem = true unless arduino_cmd.set_pref("compiler.warning_level", "all") +puts "use board! (install board)" +got_problem = true unless arduino_cmd.use_board!("arduino:samd:zero") +puts "verify that board has been installed" +got_problem = true unless arduino_cmd.board_installed?("arduino:samd:zero") abort if got_problem exit(0) diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index 336f41a1..aee8b6fe 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -137,5 +137,19 @@ def update_library_index install_library("USBHost") end + # use a particular board for compilation + def use_board(boardname) + run_with_gui_guess(" about board not installed", "--board", boardname, "--save-prefs") + end + + # use a particular board for compilation, installing it if necessary + def use_board!(boardname) + return true if use_board(boardname) + boardfamily = boardname.split(":")[0..1].join(":") + puts "Board '#{boardname}' not found; attempting to install '#{boardfamily}'" + return false unless install_board(boardfamily) # guess board family from first 2 :-separated fields + use_board(boardname) + end + end end