lib/roku_builder/controller.rb in roku_builder-3.3.3 vs lib/roku_builder/controller.rb in roku_builder-3.3.4
- old
+ new
@@ -1,251 +1,130 @@
module RokuBuilder
# Controls all interaction with other classes
class Controller
-
-
- ### Validation Codes ###
-
- # Valid Options
- VALID = 0
-
- # Too many commands given
- EXTRA_COMMANDS = 1
-
- # No commands given
- NO_COMMANDS = 2
-
- # Too many source options given
- EXTRA_SOURCES = 3
-
- # No source options given
- NO_SOURCE = 4
-
- # Incorrect use of current option
- BAD_CURRENT = 5
-
- # No deeplink options supplied for deeplink
- BAD_DEEPLINK = 6
-
- # Incorrect use of the in option
- BAD_IN_FILE = 7
-
-
-
- ### Device Codes ###
-
- # The default device is offline switched to a secondary device
- CHANGED_DEVICE = -1
-
- # Device is online
- GOOD_DEVICE = 0
-
- # User defined device was not online
- BAD_DEVICE = 1
-
- # No configured devices were online
- NO_DEVICES = 2
-
-
-
- ### Run Codes ###
-
- # Config has deplicated options
- DEPRICATED_CONFIG = -1
-
- # Valid config
- SUCCESS = 0
-
- # Tring to overwrite existing config file
- CONFIG_OVERWRITE = 1
-
- # Missing config file
- MISSING_CONFIG = 2
-
- # Invalid config file
- INVALID_CONFIG = 3
-
- # Missing manifest file
- MISSING_MANIFEST = 4
-
- # Unknow device given
- UNKNOWN_DEVICE = 5
-
- # Unknown project given
- UNKNOWN_PROJECT = 6
-
- # Unknown stage given
- UNKNOWN_STAGE = 7
-
- # Failed to sideload app
- FAILED_SIDELOAD = 8
-
- # Failed to sign app
- FAILED_SIGNING = 9
-
- # Failed to deeplink to app
- FAILED_DEEPLINKING = 10
-
- # Failed to send navigation command
- FAILED_NAVIGATING = 11
-
- # Failed to capture screen
- FAILED_SCREENCAPTURE = 12
-
# Run the builder
# @param options [Hash] The options hash
def self.run(options:)
logger = Logger.new(STDOUT)
- logger.formatter = proc {|severity, datetime, progname, msg|
+ logger.formatter = proc {|severity, datetime, _progname, msg|
"[%s #%s] %5s: %s\n\r" % [datetime.strftime("%Y-%m-%d %H:%M:%S.%4N"), $$, severity, msg]
}
if options[:debug]
logger.level = Logger::DEBUG
elsif options[:verbose]
logger.level = Logger::INFO
else
logger.level = Logger::WARN
end
+ # Validate Options
+ options_code = validate_options(options: options)
+ ErrorHandler.handle_options_codes(options_code: options_code, logger: logger)
- options_code = self.validate_options(options: options, logger: logger)
+ # Configure Gem
+ configure_code = configure(options: options, logger: logger)
+ ErrorHandler.handle_configure_codes(configure_code: configure_code, logger: logger)
- self.handle_error_codes(options: options, options_code: options_code, logger: logger)
+ # Load Config
+ load_code, config, configs = ConfigManager.load_config(options: options, logger: logger)
+ ErrorHandler.handle_load_codes(options: options, load_code: load_code, logger: logger)
- handle_code = self.handle_options(options: options, logger: logger)
+ # Check devices
+ device_code, configs = check_devices(options: options, config: config, configs: configs, logger: logger)
+ ErrorHandler.handle_device_codes(device_code: device_code, logger: logger)
- self.handle_error_codes(options: options, handle_code: handle_code, logger: logger)
+ # Run Commands
+ command_code = execute_commands(options: options, config: config, configs: configs, logger: logger)
+ ErrorHandler.handle_command_codes(command_code: command_code, logger: logger)
end
- protected
+ # Validates the user options
+ # @param options [Hash] The options hash
+ # @return [Integer] Status code for command validation
+ def self.validate_options(options:)
+ command_result = validate_command_options(options: options)
+ return command_result unless command_result == VALID
+ source_result = validate_source_options(options: options)
+ return source_result unless source_result == VALID
+ combination_result = validate_option_combinations(options: options)
+ return combination_result
+ end
+ private_class_method :validate_options
- # Validates the commands
+ # Validates use of command options
# @param options [Hash] The options hash
# @return [Integer] Status code for command validation
- # @param logger [Logger] system logger
- def self.validate_options(options:, logger:)
- commands = options.keys & self.commands
- return EXTRA_COMMANDS if commands.count > 1
- return NO_COMMANDS if commands.count < 1
- sources = options.keys & self.sources
- return EXTRA_SOURCES if sources.count > 1
- if (options.keys & self.source_commands).count == 1
- return NO_SOURCE unless sources.count == 1
+ def self.validate_command_options(options:)
+ all_commands = options.keys & commands
+ return EXTRA_COMMANDS if all_commands.count > 1
+ return NO_COMMANDS if all_commands.count < 1
+ VALID
+ end
+ private_class_method :validate_command_options
+
+ # Validates use of source options
+ # @param options [Hash] The options hash
+ # @return [Integer] Status code for command validation
+ def self.validate_source_options(options:)
+ all_sources = options.keys & sources
+ return EXTRA_SOURCES if all_sources.count > 1
+ if (options.keys & source_commands).count == 1
+ return NO_SOURCE unless all_sources.count == 1
end
- if sources.include?(:current)
+ VALID
+ end
+ private_class_method :validate_source_options
+
+ # Validates proper option combinations
+ # @param options [Hash] The options hash
+ # @return [Integer] Status code for command validation
+ def self.validate_option_combinations(options:)
+ all_sources = options.keys & sources
+ if all_sources.include?(:current)
return BAD_CURRENT unless options[:build] or options[:sideload]
end
if options[:in]
return BAD_IN_FILE unless options[:sideload]
end
if options[:deeplink]
- return BAD_DEEPLINK if !options[:deeplink_options] or options[:deeplink_options].chomp == ""
+ return BAD_DEEPLINK if options[:deeplink_options].to_s.empty?
end
- return VALID
+ VALID
end
+ private_class_method :validate_option_combinations
# Run commands
# @param options [Hash] The options hash
# @return [Integer] Return code for options handeling
# @param logger [Logger] system logger
- def self.handle_options(options:, logger:)
- if options[:configure]
- return configure(options: options, logger: logger)
- end
- code, config, configs = self.load_config(options: options, logger: logger)
- return code if code != SUCCESS
-
- # Check devices
- device_code, configs = self.check_devices(options: options, config: config, configs: configs, logger: logger)
- self.handle_error_codes(options: options, device_code: device_code, logger: logger)
-
- command = (self.commands & options.keys).first
- case command
- when :validate
- # Do Nothing #
- when :sideload
- ### Sideload App ###
- loader = Loader.new(**configs[:device_config])
- success = loader.sideload(**configs[:sideload_config])
- return FAILED_SIDELOAD unless success
- when :package
- ### Package App ###
- keyer = Keyer.new(**configs[:device_config])
- loader = Loader.new(**configs[:device_config])
- packager = Packager.new(**configs[:device_config])
- inspector = Inspector.new(**configs[:device_config])
- logger.warn "Packaging working directory" if options[:working]
- # Sideload #
- build_version = loader.sideload(**configs[:sideload_config])
- return FAILED_SIGNING unless build_version
- # Key #
- success = keyer.rekey(**configs[:key])
- logger.info "Key did not change" unless success
- # Package #
- options[:build_version] = build_version
- configs = self.update_configs(configs: configs, options: options)
- success = packager.package(**configs[:package_config])
- logger.info "Signing Successful: #{configs[:package_config][:out_file]}" if success
- return FAILED_SIGNING unless success
- # Inspect #
- if options[:inspect]
- info = inspector.inspect(configs[:inspect_config])
- logger.unknown "App Name: #{info[:app_name]}"
- logger.unknown "Dev ID: #{info[:dev_id]}"
- logger.unknown "Creation Date: #{info[:creation_date]}"
- logger.unknown "dev.zip: #{info[:dev_zip]}"
+ def self.execute_commands(options:, config:, configs:, logger:)
+ command = (commands & options.keys).first
+ if ControllerCommands.simple_commands.keys.include?(command)
+ params = ControllerCommands.simple_commands[command]
+ params[:configs] = configs
+ ControllerCommands.simple_command(**params)
+ else
+ params = ControllerCommands.method(command.to_s).parameters.collect{|a|a[1]}
+ args = {}
+ params.each do |key|
+ case key
+ when :options
+ args[:options] = options
+ when :config
+ args[:config] = config
+ when :configs
+ args[:configs] = configs
+ when :logger
+ args[:logger] = logger
+ end
end
- when :build
- ### Build ###
- loader = Loader.new(**configs[:device_config])
- build_version = ManifestManager.build_version(**configs[:manifest_config])
- options[:build_version] = build_version
- configs = self.update_configs(configs: configs, options: options)
- outfile = loader.build(**configs[:build_config])
- logger.info "Build: #{outfile}"
- when :update
- ### Update ###
- old_version = ManifestManager.build_version(**configs[:manifest_config])
- new_version = ManifestManager.update_build(**configs[:manifest_config])
- logger.info "Update build version from:\n#{old_version}\nto:\n#{new_version}"
- when :deeplink
- ### Deeplink ###
- linker = Linker.new(**configs[:device_config])
- success = linker.link(**configs[:deeplink_config])
- return FAILED_DEEPLINKING unless success
- when :delete
- loader = Loader.new(**configs[:device_config])
- loader.unload()
- when :monitor
- monitor = Monitor.new(**configs[:device_config])
- monitor.monitor(**configs[:monitor_config])
- when :navigate
- navigator = Navigator.new(**configs[:device_config])
- success = navigator.nav(**configs[:navigate_config])
- return FAILED_NAVIGATING unless success
- when :screen
- navigator = Navigator.new(**configs[:device_config])
- success = navigator.screen(**configs[:screen_config])
- return FAILED_NAVIGATING unless success
- when :screens
- navigator = Navigator.new(**configs[:device_config])
- navigator.screens
- when :text
- navigator = Navigator.new(**configs[:device_config])
- navigator.type(**configs[:text_config])
- when :test
- tester = Tester.new(**configs[:device_config])
- tester.run_tests(**configs[:test_config])
- when :screencapture
- inspector = Inspector.new(**configs[:device_config])
- success = inspector.screencapture(**configs[:screencapture_config])
- return FAILED_SCREENCAPTURE unless success
+ ControllerCommands.send(command, args)
end
- return SUCCESS
end
+ private_class_method :execute_commands
# Ensure that the selected device is accessable
# @param options [Hash] The options hash
# @param logger [Logger] system logger
def self.check_devices(options:, config:, configs:, logger:)
@@ -263,325 +142,59 @@
end
end
}
return [NO_DEVICES, nil]
end
+ private_class_method :check_devices
# List of command options
# @return [Array<Symbol>] List of command symbols that can be used in the options hash
def self.commands
[:sideload, :package, :test, :deeplink,:configure, :validate, :delete,
:navigate, :text, :build, :monitor, :update, :screencapture, :screen,
:screens]
end
+ private_class_method :commands
# List of source options
# @return [Array<Symbol>] List of source symbols that can be used in the options hash
def self.sources
[:ref, :set_stage, :working, :current]
end
+ private_class_method :sources
# List of commands requiring a source option
# @return [Array<Symbol>] List of command symbols that require a source in the options hash
def self.source_commands
[:sideload, :package, :test, :build]
end
+ private_class_method :source_commands
- # Handle error codes
- # @param options_code [Integer] the error code returned by validate_options
- # @param handle_code [Integer] the error code returned by handle_options
- # @param logger [Logger] system logger
- def self.handle_error_codes(options:, options_code: nil, device_code: nil, handle_code: nil, logger:)
- if options_code
- case options_code
- when EXTRA_COMMANDS
- logger.fatal "Only one command is allowed"
- abort
- when NO_COMMANDS
- logger.fatal "At least one command is required"
- abort
- when EXTRA_SOURCES
- logger.fatal "Only use one of --ref, --working, --current or --stage"
- abort
- when NO_SOURCE
- logger.fatal "Must use at least one of --ref, --working, --current or --stage"
- abort
- when BAD_CURRENT
- logger.fatal "Can only sideload or build 'current' directory"
- abort
- when BAD_DEEPLINK
- logger.fatal "Must supply deeplinking options when deeplinking"
- abort
- when BAD_IN_FILE
- logger.fatal "Can only supply in file for building"
- abort
- end
- elsif device_code
- case device_code
- when CHANGED_DEVICE
- logger.info "The default device was not online so a secondary device is being used"
- when BAD_DEVICE
- logger.fatal "The selected device was not online"
- abort
- when NO_DEVICES
- logger.fatal "No configured devices were found"
- abort
- end
- elsif handle_code
- case handle_code
- when DEPRICATED_CONFIG
- logger.warn 'Depricated config. See Above'
- when CONFIG_OVERWRITE
- logger.fatal 'Config already exists. To create default please remove config first.'
- abort
- when MISSING_CONFIG
- logger.fatal "Missing config file: #{options[:config]}"
- abort
- when INVALID_CONFIG
- logger.fatal 'Invalid config. See Above'
- abort
- when MISSING_MANIFEST
- logger.fatal 'Manifest file missing'
- abort
- when UNKNOWN_DEVICE
- logger.fatal "Unkown device id"
- abort
- when UNKNOWN_PROJECT
- logger.fatal "Unknown project id"
- abort
- when UNKNOWN_STAGE
- logger.fatal "Unknown stage"
- abort
- when FAILED_SIDELOAD
- logger.fatal "Failed Sideloading App"
- abort
- when FAILED_SIGNING
- logger.fatal "Failed Signing App"
- abort
- when FAILED_DEEPLINKING
- logger.fatal "Failed Deeplinking To App"
- abort
- when FAILED_NAVIGATING
- logger.fatal "Command not sent"
- abort
- when FAILED_SCREENCAPTURE
- logger.fatal "Failed to Capture Screen"
- abort
- end
- end
- end
# Configure the gem
# @param options [Hash] The options hash
# @return [Integer] Success or failure code
# @param logger [Logger] system logger
def self.configure(options:, logger:)
- source_config = File.expand_path(File.join(File.dirname(__FILE__), "..", '..', 'config.json.example'))
- target_config = File.expand_path(options[:config])
- if File.exist?(target_config)
- unless options[:edit_params]
- return CONFIG_OVERWRITE
- end
- else
- ### Copy Config File ###
- FileUtils.copy(source_config, target_config)
- end
- if options[:edit_params]
- ConfigManager.edit_config(config: target_config, options: options[:edit_params], device: options[:device], project: options[:project], stage: options[:stage], logger: logger)
- end
- return SUCCESS
- end
-
- # Load config file and generate intermeidate configs
- # @param options [Hash] The options hash
- # @param logger [Logger] system logger
- # @return [Integer] Return code
- # @return [Hash] Loaded config
- # @return [Hash] Intermeidate configs
- def self.load_config(options:, logger:)
- config_file = File.expand_path(options[:config])
- return MISSING_CONFIG unless File.exists?(config_file)
- code = SUCCESS
- config = ConfigManager.get_config(config: config_file, logger: logger)
- return INVALID_CONFIG unless config
- configs = {}
- codes = ConfigManager.validate_config(config: config, logger: logger)
- fatal = false
- warning = false
- codes.each {|code|
- if code > 0
- logger.fatal "Invalid Config: "+ ConfigManager.error_codes()[code]
- fatal = true
- elsif code < 0
- logger.warn "Depricated Config: "+ ConfigManager.error_codes()[code]
- warning = true
- elsif code == 0 and options[:validate]
- logger.info "Config Valid"
- end
- }
- return [INVALID_CONFIG, nil, nil] if fatal
- code = DEPRICATED_CONFIG if warning
-
- #set device
- unless options[:device]
- options[:device] = config[:devices][:default]
- end
- #set project
- if options[:current] or not options[:project]
- path = self.system(command: "pwd")
- project = nil
- config[:projects].each_pair {|key,value|
- if value.is_a?(Hash)
- repo_path = Pathname.new(value[:directory]).realdirpath.to_s
- if path.start_with?(repo_path)
- project = key
- break
- end
+ if options[:configure]
+ source_config = File.expand_path(File.join(File.dirname(__FILE__), "..", '..', 'config.json.example'))
+ target_config = File.expand_path(options[:config])
+ if File.exist?(target_config)
+ unless options[:edit_params]
+ return CONFIG_OVERWRITE
end
- }
- if project
- options[:project] = project
else
- options[:project] = config[:projects][:default]
+ ### Copy Config File ###
+ FileUtils.copy(source_config, target_config)
end
- end
- #set outfile
- options[:out_folder] = nil
- options[:out_file] = nil
- if options[:out]
- if options[:out].end_with?(".zip") or options[:out].end_with?(".pkg") or options[:out].end_with?(".jpg")
- options[:out_folder], options[:out_file] = Pathname.new(options[:out]).split.map{|p| p.to_s}
- else
- options[:out_folder] = options[:out]
+ if options[:edit_params]
+ ConfigManager.edit_config(config: target_config, options: options, logger: logger)
end
+ return SUCCESS
end
- unless options[:out_folder]
- options[:out_folder] = "/tmp"
- end
-
- # Create Device Config
- configs[:device_config] = config[:devices][options[:device].to_sym]
- return [UNKNOWN_DEVICE, nil, nil] unless configs[:device_config]
- configs[:device_config][:logger] = logger
-
- #Create Project Config
- project_config = {}
- if options[:current]
- pwd = self.system(command: "pwd")
- return [MISSING_MANIFEST, nil, nil] unless File.exist?(File.join(pwd, "manifest"))
- project_config = {
- directory: pwd,
- folders: nil,
- files: nil,
- stages: { production: { branch: nil } }
- }
- else
- project_config = config[:projects][options[:project].to_sym]
- end
- return [UNKNOWN_PROJECT, nil, nil] unless project_config
- configs[:project_config] = project_config
- stage = options[:stage].to_sym
- return [UNKNOWN_STAGE, nil, nil] unless project_config[:stages][stage]
- configs[:stage] = stage
-
- root_dir = project_config[:directory]
- branch = project_config[:stages][stage][:branch]
- branch = options[:ref] if options[:ref]
- branch = nil if options[:current]
- branch = nil if options[:working]
-
- # Create Sideload Config
- configs[:sideload_config] = {
- root_dir: root_dir,
- branch: branch,
- update_manifest: options[:update_manifest],
- fetch: options[:fetch],
- folders: project_config[:folders],
- files: project_config[:files]
- }
- if options[:package]
- # Create Key Config
- configs[:key] = project_config[:stages][stage][:key]
- # Create Package Config
- configs[:package_config] = {
- password: configs[:key][:password],
- app_name_version: "#{project_config[:app_name]} - #{stage}"
- }
- if options[:out_file]
- configs[:package_config][:out_file] = File.join(options[:out_folder], options[:out_file])
- end
- # Create Inspector Config
- configs[:inspect_config] = {
- pkg: configs[:package_config][:out_file],
- password: configs[:key][:password]
- }
- end if
- # Create Build Config
- configs[:build_config] = {
- root_dir: root_dir,
- branch: branch,
- fetch: options[:fetch],
- folders: project_config[:folders],
- files: project_config[:files]
- }
- # Create Manifest Config
- configs[:manifest_config] = {
- root_dir: project_config[:directory],
- logger: logger
- }
- # Create Deeplink Config
- configs[:deeplink_config] ={
- options: options[:deeplink_options]
- }
- # Create Monitor Config
- if options[:monitor]
- configs[:monitor_config] = {
- type: options[:monitor].to_sym
- }
- end
- # Create Navigate Config
- if options[:navigate]
- configs[:navigate_config] = {
- command: options[:navigate].to_sym
- }
- end
- # Create Text Config
- configs[:text_config] = {
- text: options[:text]
- }
- # Create Test Config
- configs[:test_config] = {
- sideload_config: configs[:sideload_config]
- }
- #Create screencapture config
- configs[:screencapture_config] = {
- out_folder: options[:out_folder],
- out_file: options[:out_file]
- }
-
- if options[:screen]
- configs[:screen_config] = {
- type: options[:screen].to_sym
- }
- end
- return [code, config, configs]
+ nil
end
-
- # Update the intermeidate configs
- # @param configs [Hash] Intermeidate configs hash
- # @param options [Hash] Options hash
- # @return [Hash] New intermeidate configs hash
- def self.update_configs(configs:, options:)
- if options[:build_version]
- configs[:package_config][:app_name_version] = "#{configs[:project_config][:app_name]} - #{configs[:stage]} - #{options[:build_version]}" if configs[:package_config]
- unless options[:outfile]
- configs[:package_config][:out_file] = File.join(options[:out_folder], "#{configs[:project_config][:app_name]}_#{configs[:stage]}_#{options[:build_version]}.pkg") if configs[:package_config]
- configs[:build_config][:outfile] = File.join(options[:out_folder], "#{configs[:project_config][:app_name]}_#{configs[:stage]}_#{options[:build_version]}.zip") if configs[:build_config]
- configs[:inspect_config][:pkg] = configs[:package_config][:out_file] if configs[:inspect_config] and configs[:package_config]
- end
- end
- return configs
- end
+ private_class_method :configure
# Run a system command
# @param command [String] The command to be run
# @return [String] The output of the command
def self.system(command:)