fastlane/lib/fastlane/setup/setup.rb in fastlane-2.75.0.beta.20180109010003 vs fastlane/lib/fastlane/setup/setup.rb in fastlane-2.75.0
- old
+ new
@@ -1,61 +1,274 @@
+require "tty-spinner"
+
module Fastlane
class Setup
+ # Is the current `setup` using a swift based configuration file
+ attr_accessor :is_swift_fastfile
+
+ # :ios or :android
+ attr_accessor :platform
+
+ # Path to the xcodeproj or xcworkspace
+ attr_accessor :project_path
+
+ # Used for :manual sometimes
+ attr_accessor :preferred_setup_method
+
+ # remember if there were multiple projects
+ # if so, we set it as part of the Fastfile
+ attr_accessor :had_multiple_projects_to_choose_from
+
+ # The current content of the generated Fastfile
+ attr_accessor :fastfile_content
+
+ # Appfile
+ attr_accessor :appfile_content
+
+ # For iOS projects that's the Apple ID email
+ attr_accessor :user
+
+ # This is the lane that we tell the user to run to try the new fastlane setup
+ # This needs to be setup by each setup
+ attr_accessor :lane_to_mention
+
# Start the setup process
- def run(user: nil, is_swift_fastfile: false)
+ # rubocop:disable Metrics/BlockNesting
+ def self.start(user: nil, is_swift_fastfile: false)
if FastlaneCore::FastlaneFolder.setup? and !Helper.is_test?
- UI.important("fastlane is already set up at path #{FastlaneCore::FastlaneFolder.path}")
+ require 'fastlane/lane_list'
+ Fastlane::LaneList.output(FastlaneCore::FastlaneFolder.fastfile_path)
+ UI.important("------------------")
+ UI.important("fastlane is already set up at path `#{FastlaneCore::FastlaneFolder.path}`, see the available lanes above")
+ UI.message("")
+ self.new.suggest_next_steps
return
end
- platform = nil
- if is_ios?
- UI.message("Detected iOS/Mac project in current directory...")
- platform = :ios
- elsif is_android?
- UI.message("Detected Android project in current directory...")
- platform = :android
- elsif is_react_native?
- UI.important("Detected react-native app. To set up fastlane, please run")
- UI.command("fastlane init")
- UI.important("in the sub-folder for each platform (\"ios\" or \"android\")")
- UI.user_error!("Please navigate to the platform subfolder and run `fastlane init` again")
+ # this is used by e.g. configuration.rb to not show warnings when running produce
+ ENV["FASTLANE_ONBOARDING_IN_PROCESS"] = 1.to_s
+
+ spinner = TTY::Spinner.new("[:spinner] Looking for iOS and Android projects in current directory...", format: :dots)
+ spinner.auto_spin
+
+ ios_projects = Dir["**/*.xcodeproj"] + Dir["**/*.xcworkspace"]
+ android_projects = Dir["**/*.gradle"]
+
+ spinner.success
+
+ FastlaneCore::FastlaneFolder.create_folder!
+
+ # Currently we prefer iOS app projects, as the `init` process is
+ # more intelligent and does more things. The user can easily add
+ # the `:android` platform to the resulting Fastfile
+ if ios_projects.count > 0
+ current_directory = ios_projects.find_all do |current_project_path|
+ current_project_path.split(File::Separator).count == 1
+ end
+ chosen_project = nil
+ had_multiple_projects_to_choose_from = false
+
+ if current_directory.count == 1
+ chosen_project = current_directory.first
+ elsif current_directory.count > 1
+ if current_directory.count == 2
+ # This is a common case (e.g. with CocoaPods), where the project has an xcodeproj and an xcworkspace file
+ extensions = [File.extname(current_directory[0]), File.extname(current_directory[1])]
+ if extensions.sort == [".xcodeproj", ".xcworkspace"].sort
+ # Yep, that's this kind of setup
+ chosen_project = current_directory.find { |d| d.end_with?(".xcworkspace") }
+ end
+ end
+ chosen_project ||= UI.select("Multiple iOS projects found in current directory", current_directory)
+ had_multiple_projects_to_choose_from = true
+ else
+ UI.error("It looks like there is no iOS project in the current directory, though we did find one in a sub-directory")
+ UI.error("Please `cd` into the directory of the intended Xcode project you wish to use.")
+ UI.user_error!("Please `cd` into the directory of the intended Xcode project you wish to use and run `fastlane init` again")
+ end
+
+ if chosen_project == "Pods.xcodeproj"
+ unless UI.confirm("Found '#{chosen_project}', which usually isn't normally what you want. Make sure to switch to the directory containing your intended Xcode project. Would you still like to continue with #{chosen_project}?")
+ UI.user_error!("Make sure to `cd` into the directory containing the Xcode project you intend to use and then use `fastlane init` again")
+ end
+ end
+ UI.message("Detected an iOS/macOS project in the current directory: '#{chosen_project}'")
+
+ SetupIos.new(
+ is_swift_fastfile: is_swift_fastfile,
+ user: user,
+ project_path: chosen_project,
+ had_multiple_projects_to_choose_from: had_multiple_projects_to_choose_from
+ ).setup_ios
+ elsif android_projects.count > 0
+ UI.message("Detected an Android project in the current directory...")
+ SetupAndroid.new.setup_android
else
- UI.important("Couldn't automatically detect the platform")
- val = UI.confirm("Is this project an iOS project?")
- platform = (val ? :ios : :android)
+ UI.error("No iOS or Android projects were found in directory '#{Dir.pwd}'")
+ UI.error("Make sure to `cd` into the directory containing your iOS or Android app")
+ if UI.confirm("Alternatively, would you like to manually setup a fastlane config in the current directory instead?")
+ SetupIos.new(
+ is_swift_fastfile: is_swift_fastfile,
+ user: user,
+ project_path: chosen_project,
+ had_multiple_projects_to_choose_from: had_multiple_projects_to_choose_from,
+ preferred_setup_method: :ios_manual
+ ).setup_ios
+ else
+ UI.user_error!("Make sure to `cd` into the directory containing your project and then use `fastlane init` again")
+ end
end
+ end
+ # rubocop:enable Metrics/BlockNesting
- if platform == :ios
- SetupIos.new.run(user: user, is_swift_fastfile: is_swift_fastfile)
- elsif platform == :android
- SetupAndroid.new.run
+ def initialize(is_swift_fastfile: nil, user: nil, project_path: nil, had_multiple_projects_to_choose_from: nil, preferred_setup_method: nil)
+ self.is_swift_fastfile = is_swift_fastfile
+ self.user = user
+ self.project_path = project_path
+ self.had_multiple_projects_to_choose_from = had_multiple_projects_to_choose_from
+ self.preferred_setup_method = preferred_setup_method
+ end
+
+ # Helpers
+ def welcome_to_fastlane
+ UI.header("Welcome to fastlane 🚀")
+ UI.message("fastlane can help you with all kinds of automation for your mobile app")
+ UI.message("We recommend automating one task first, and then gradually automating more over time")
+ end
+
+ # Append a lane to the current Fastfile template we're generating
+ def append_lane(lane)
+ lane.compact! # remove nil values
+
+ new_lines = "\n\n"
+ if self.is_swift_fastfile
+ new_lines = "" unless self.fastfile_content.include?("lane() {") # the first lane we don't want new lines
+ self.fastfile_content.gsub!("[[LANES]]", "#{new_lines}\t#{lane.join("\n\t")}[[LANES]]")
else
- UI.user_error!("Couldn't find platform '#{platform}'")
+ new_lines = "" unless self.fastfile_content.include?("lane :") # the first lane we don't want new lines
+ self.fastfile_content.gsub!("[[LANES]]", "#{new_lines} #{lane.join("\n ")}[[LANES]]")
end
+ end
- # Now that we've setup all the things, if we're using Swift, do the first time setup
- if is_swift_fastfile
- Fastlane::SwiftLaneManager.first_time_setup
+ # Append a team to the Appfile
+ def append_team(team)
+ self.appfile_content.gsub!("[[TEAMS]]", "#{team}\n[[TEAMS]]")
+ end
+
+ def write_fastfile!
+ # Write the Fastfile
+ fastfile_file_name = "Fastfile"
+ fastfile_file_name += ".swift" if self.is_swift_fastfile
+
+ fastfile_path = File.join(FastlaneCore::FastlaneFolder.path, fastfile_file_name)
+ self.fastfile_content.gsub!("[[LANES]]", "") # since we always keep it until writing out
+ File.write(fastfile_path, self.fastfile_content) # remove trailing spaces before platform ends
+
+ appfile_file_name = "Appfile"
+ appfile_file_name += ".swift" if self.is_swift_fastfile
+ appfile_path = File.join(FastlaneCore::FastlaneFolder.path, appfile_file_name)
+ self.appfile_content.gsub!("[[TEAMS]]", "")
+
+ File.write(appfile_path, self.appfile_content)
+
+ UI.header("✅ Successfully generated fastlane configuration")
+ UI.message("Generated Fastfile at path `#{fastfile_path}`")
+ UI.message("Generated Appfile at path `#{appfile_path}`")
+
+ UI.message("Please check the newly generated configuration files into git along with your project")
+ UI.message("This way everyone in your team can benefit from your fastlane setup")
+ continue_with_enter
+ end
+
+ def finish_up
+ write_fastfile!
+ setup_swift_support if is_swift_fastfile
+ show_analytics_note
+ explain_concepts
+ suggest_next_steps
+ end
+
+ def setup_swift_support
+ runner_source_resources = "#{Fastlane::ROOT}/swift/."
+ destination_path = File.expand_path('swift', FastlaneCore::FastlaneFolder.path)
+ FileUtils.cp_r(runner_source_resources, destination_path)
+ UI.success("Copied Swift fastlane runner project to '#{destination_path}'.")
+
+ Fastlane::SwiftLaneManager.first_time_setup
+ end
+
+ def fastfile_template_content
+ if self.is_swift_fastfile
+ path = "#{Fastlane::ROOT}/lib/assets/DefaultFastfileTemplate.swift"
+ else
+ path = "#{Fastlane::ROOT}/lib/assets/DefaultFastfileTemplate"
end
+
+ return File.read(path)
end
- def is_ios?
- (Dir["*.xcodeproj"] + Dir["*.xcworkspace"]).count > 0
+ def appfile_template_content
+ if self.platform == :ios
+ if self.is_swift_fastfile
+ path = "#{Fastlane::ROOT}/lib/assets/AppfileTemplate.swift"
+ else
+ path = "#{Fastlane::ROOT}/lib/assets/AppfileTemplate"
+ end
+ else
+ path = "#{Fastlane::ROOT}/lib/assets/AppfileTemplateAndroid"
+ end
+
+ return File.read(path)
end
- def is_android?
- Dir["*.gradle"].count > 0
+ def explain_concepts
+ UI.header("fastlane lanes")
+ UI.message("fastlane uses a " + "`Fastfile`".yellow + " to store the automation configuration")
+ UI.message("Within that, you'll see different " + "lanes".yellow + ".")
+ UI.message("Each is there to automate a different task, like screenshots, code signing, or pushing new releases")
+ continue_with_enter
+
+ UI.header("How to customize your Fastfile")
+ UI.message("Use a text editor of your choice to open the newly created Fastfile and take a look")
+ UI.message("You can now edit the available lanes and actions to customize the setup to fit your needs")
+ UI.message("To get a list of all the available actions, open " + "https://docs.fastlane.tools/actions".cyan)
+ continue_with_enter
end
- def is_react_native?
- SetupIos.project_uses_react_native?(path: "./ios")
+ def continue_with_enter
+ UI.input("Continue by pressing Enter ⏎")
end
- def show_analytics
+ def suggest_next_steps
+ UI.header("Where to go from here?")
+ if self.platform == :android
+ UI.message("📸 Learn more about how to automatically generate localized Google Play screenshots:")
+ UI.message("\t\thttps://docs.fastlane.tools/getting-started/android/screenshots/".cyan)
+ UI.message("👩✈️ Learn more about distribution to beta testing services:")
+ UI.message("\t\thttps://docs.fastlane.tools/getting-started/android/beta-deployment/".cyan)
+ UI.message("🚀 Learn more about how to automate the Google Play release process:")
+ UI.message("\t\thttps://docs.fastlane.tools/getting-started/android/release-deployment/".cyan)
+ else
+ UI.message("📸 Learn more about how to automatically generate localized App Store screenshots:")
+ UI.message("\t\thttps://docs.fastlane.tools/getting-started/ios/screenshots/".cyan)
+ UI.message("👩✈️ Learn more about distribution to beta testing services:")
+ UI.message("\t\thttps://docs.fastlane.tools/getting-started/ios/beta-deployment/".cyan)
+ UI.message("🚀 Learn more about how to automate the App Store release process:")
+ UI.message("\t\thttps://docs.fastlane.tools/getting-started/ios/appstore-deployment/".cyan)
+ UI.message("👩⚕️ Lern more about how to setup code signing with fastlane")
+ UI.message("\t\thttps://docs.fastlane.tools/codesigning/getting-started/".cyan)
+ end
+
+ # we crash here, so that this never happens when a new setup method is added
+ return if self.lane_to_mention.to_s.length == 0
+ UI.message("")
+ UI.message("To try your new fastlane setup, just enter and run")
+ UI.command("fastlane #{self.lane_to_mention}")
+ end
+
+ def show_analytics_note
UI.message("fastlane will collect the number of errors for each action to detect integration issues")
- UI.message("No sensitive/private information will be uploaded")
- UI.message("Learn more at https://github.com/fastlane/fastlane#metrics")
+ UI.message("No sensitive/private information will be uploaded, more information: " + "https://docs.fastlane.tools/#metrics".cyan)
end
end
end
require 'fastlane/setup/setup_ios'