require 'commander' require 'fastlane/new_action' require 'fastlane_core/ui/help_formatter' HighLine.track_eof = false module Fastlane class CommandsGenerator include Commander::Methods def self.start # since at this point we haven't yet loaded commander # however we do want to log verbose information in the PluginManager FastlaneCore::Globals.verbose = true if ARGV.include?("--verbose") if ARGV.include?("--capture_output") FastlaneCore::Globals.verbose = true FastlaneCore::Globals.capture_output = true end FastlaneCore::Swag.show_loader # has to be checked here - in case we want to troubleshoot plugin related issues if ARGV.include?("--troubleshoot") self.confirm_troubleshoot end if FastlaneCore::Globals.capture_output? # Trace mode is enabled # redirect STDOUT and STDERR out_channel = StringIO.new $stdout = out_channel $stderr = out_channel end Fastlane.load_actions FastlaneCore::Swag.stop_loader # do not use "include" as it may be some where in the commandline where "env" is required, therefore explicit index->0 unless ARGV[0] == "env" || CLIToolsDistributor.running_version_command? || CLIToolsDistributor.running_help_command? # *after* loading the plugins hide_plugins_table = FastlaneCore::Env.truthy?("FASTLANE_HIDE_PLUGINS_TABLE") Fastlane.plugin_manager.load_plugins(print_table: !hide_plugins_table) Fastlane::PluginUpdateManager.start_looking_for_updates end self.new.run ensure Fastlane::PluginUpdateManager.show_update_status if FastlaneCore::Globals.capture_output? if $stdout.respond_to?(:string) # Sometimes you can get NoMethodError: undefined method `string' for #> when running with FastlaneRunner (swift) FastlaneCore::Globals.captured_output = Helper.strip_ansi_colors($stdout.string) end $stdout = STDOUT $stderr = STDERR require "fastlane/environment_printer" Fastlane::EnvironmentPrinter.output end end def self.confirm_troubleshoot if Helper.ci? UI.error("---") UI.error("You are trying to use '--troubleshoot' on CI") UI.error("this option is not usable in CI, as it is insecure") UI.error("---") UI.user_error!("Do not use --troubleshoot in CI") end # maybe already set by 'start' return if $troubleshoot UI.error("---") UI.error("Are you sure you want to enable '--troubleshoot'?") UI.error("All commmands will run in full unfiltered output mode.") UI.error("Sensitive data, like passwords, could be printed to the log.") UI.error("---") if UI.confirm("Do you really want to enable --troubleshoot") $troubleshoot = true end end def run program :name, 'fastlane' program :version, Fastlane::VERSION program :description, [ "CLI for 'fastlane' - #{Fastlane::DESCRIPTION}\n", "\tRun using `fastlane [platform] [lane_name]`", "\tTo pass values to the lanes use `fastlane [platform] [lane_name] key:value key2:value2`" ].join("\n") program :help, 'Author', 'Felix Krause ' program :help, 'Website', 'https://fastlane.tools' program :help, 'GitHub', 'https://github.com/fastlane/fastlane' program :help_formatter, FastlaneCore::HelpFormatter global_option('--verbose') { FastlaneCore::Globals.verbose = true } global_option('--capture_output', 'Captures the output of the current run, and generates a markdown issue template') do FastlaneCore::Globals.capture_output = false FastlaneCore::Globals.verbose = true end global_option('--troubleshoot', 'Enables extended verbose mode. Use with caution, as this even includes ALL sensitive data. Cannot be used on CI.') global_option('--env STRING[,STRING2]', String, 'Add environment(s) to use with `dotenv`') always_trace! command :trigger do |c| c.syntax = 'fastlane [lane]' c.description = 'Run a specific lane. Pass the lane name and optionally the platform first.' c.option('--disable_runner_upgrades', 'Prevents fastlane from attempting to update FastlaneRunner swift project') c.option('--swift_server_port INT', 'Set specific port to communicate between fastlane and FastlaneRunner') c.action do |args, options| if ensure_fastfile Fastlane::CommandLineHandler.handle(args, options) end end end command :init do |c| c.syntax = 'fastlane init' c.description = 'Helps you with your initial fastlane setup' c.option('-u STRING', '--user STRING', String, 'iOS projects only: Your Apple ID') c.action do |args, options| is_swift_fastfile = args.include?("swift") Fastlane::Setup.start(user: options.user, is_swift_fastfile: is_swift_fastfile) end end # Creating alias for mapping "swift init" to "init swift" alias_command(:'swift init', :init, 'swift') command :new_action do |c| c.syntax = 'fastlane new_action' c.description = 'Create a new custom action for fastlane.' c.option('--name STRING', String, 'Name of your new action') c.action do |args, options| Fastlane::NewAction.run(new_action_name: options.name) end end command :socket_server do |c| c.syntax = 'fastlane start_server' c.description = 'Starts local socket server and enables only a single local connection' c.option('-s', '--stay_alive', 'Keeps socket server up even after error or disconnects, requires CTRL-C to kill.') c.option('-c seconds', '--connection_timeout', 'Sets connection established timeout') c.option('-p port', '--port', "Sets the port on localhost for the socket connection") c.action do |args, options| default_connection_timeout = 5 stay_alive = options.stay_alive || false connection_timeout = options.connection_timeout || default_connection_timeout port = options.port || 2000 if stay_alive && options.connection_timeout.nil? UI.important("stay_alive is set, but the connection timeout is not, this will give you #{default_connection_timeout} seconds to (re)connect") end require 'fastlane/server/socket_server' require 'fastlane/server/socket_server_action_command_executor' command_executor = SocketServerActionCommandExecutor.new server = Fastlane::SocketServer.new( command_executor: command_executor, connection_timeout: connection_timeout, stay_alive: stay_alive, port: port ) result = server.start UI.success("Result: #{result}") if result end end command :lanes do |c| c.syntax = 'fastlane lanes' c.description = 'Lists all available lanes and shows their description' c.option("-j", "--json", "Output the lanes in JSON instead of text") c.action do |args, options| if options.json || ensure_fastfile require 'fastlane/lane_list' path = FastlaneCore::FastlaneFolder.fastfile_path if options.json Fastlane::LaneList.output_json(path) else Fastlane::LaneList.output(path) end end end end command :list do |c| c.syntax = 'fastlane list' c.description = 'Lists all available lanes without description' c.action do |args, options| if ensure_fastfile ff = Fastlane::FastFile.new(FastlaneCore::FastlaneFolder.fastfile_path) UI.message("Available lanes:") ff.runner.available_lanes.each do |lane| UI.message("- #{lane}") end UI.important("Execute using `fastlane [lane_name]`") end end end command :docs do |c| c.syntax = 'fastlane docs' c.description = 'Generate a markdown based documentation based on the Fastfile' c.option('-f', '--force', 'Overwrite the existing README.md in the ./fastlane folder') c.action do |args, options| if ensure_fastfile ff = Fastlane::FastFile.new(File.join(FastlaneCore::FastlaneFolder.path || '.', 'Fastfile')) UI.message("You don't need to run `fastlane docs` manually any more, this will be done automatically for you when running a lane.") Fastlane::DocsGenerator.run(ff) end end end command :run do |c| c.syntax = 'fastlane run [action] key1:value1 key2:value2' c.description = 'Run a fastlane one-off action without a full lane' c.action do |args, options| require 'fastlane/one_off' result = Fastlane::OneOff.execute(args: args) UI.success("Result: #{result}") if result end end command :actions do |c| c.syntax = 'fastlane actions' c.description = 'Lists all available fastlane actions' c.option('--platform STRING', String, 'Only show actions available on the given platform') c.action do |args, options| require 'fastlane/documentation/actions_list' Fastlane::ActionsList.run(filter: args.first, platform: options.platform) end end command :action do |c| c.syntax = 'fastlane action [tool_name]' c.description = 'Shows more information for a specific command' c.action do |args, options| require 'fastlane/documentation/actions_list' Fastlane::ActionsList.run(filter: args.first) end end command :enable_auto_complete do |c| c.syntax = 'fastlane enable_auto_complete' c.description = 'Enable tab auto completion' c.option('-c STRING[,STRING2]', '--custom STRING[,STRING2]', String, 'Add custom command(s) for which tab auto complete should be enabled too') c.action do |args, options| require 'fastlane/auto_complete' Fastlane::AutoComplete.execute(args, options) end end command :env do |c| c.syntax = 'fastlane env' c.description = 'Print your fastlane environment, use this when you submit an issue on GitHub' c.action do |args, options| require "fastlane/environment_printer" Fastlane::EnvironmentPrinter.output end end command :update_fastlane do |c| c.syntax = 'fastlane update_fastlane' c.description = 'Update fastlane to the latest release' c.action do |args, options| require 'fastlane/one_off' Fastlane::OneOff.run(action: "update_fastlane", parameters: {}) end end ##################################################### # @!group Plugins ##################################################### command :new_plugin do |c| c.syntax = 'fastlane new_plugin [plugin_name]' c.description = 'Create a new plugin that can be used with fastlane' c.action do |args, options| PluginGenerator.new.generate(args.shift) end end command :add_plugin do |c| c.syntax = 'fastlane add_plugin [plugin_name]' c.description = 'Add a new plugin to your fastlane setup' c.action do |args, options| args << UI.input("Enter the name of the plugin to install: ") if args.empty? args.each do |plugin_name| Fastlane.plugin_manager.add_dependency(plugin_name) end UI.important("Make sure to commit your Gemfile, Gemfile.lock and #{PluginManager::PLUGINFILE_NAME} to version control") Fastlane.plugin_manager.install_dependencies! end end command :install_plugins do |c| c.syntax = 'fastlane install_plugins' c.description = 'Install all plugins for this project' c.action do |args, options| Fastlane.plugin_manager.install_dependencies! end end command :update_plugins do |c| c.syntax = 'fastlane update_plugins' c.description = 'Update all plugin dependencies' c.action do |args, options| Fastlane.plugin_manager.update_dependencies! end end command :search_plugins do |c| c.syntax = 'fastlane search_plugins [search_query]' c.description = 'Search for plugins, search query is optional' c.action do |args, options| search_query = args.last PluginSearch.print_plugins(search_query: search_query) end end ##################################################### # @!group Swift ##################################################### if FastlaneCore::FastlaneFolder.swift? command :generate_swift do |c| c.syntax = 'fastlane generate_swift' c.description = 'Generates additional Swift APIs for plugins and local actions' c.action do |args, options| SwiftActionsAPIGenerator.new(target_output_path: FastlaneCore::FastlaneFolder.swift_folder_path).generate_swift SwiftPluginsAPIGenerator.new(target_output_path: FastlaneCore::FastlaneFolder.swift_folder_path).generate_swift end end end default_command(:trigger) run! end # Makes sure a Fastfile is available # Shows an appropriate message to the user # if that's not the case # return true if the Fastfile is available def ensure_fastfile return true if FastlaneCore::FastlaneFolder.setup? create = UI.confirm('Could not find fastlane in current directory. Make sure to have your fastlane configuration files inside a folder called "fastlane". Would you like to set fastlane up?') if create Fastlane::Setup.start end return false end end end