fastlane 2.75.0.beta.20180109010003 → 2.75.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/credentials_manager/lib/credentials_manager/account_manager.rb +11 -9
- data/deliver/lib/assets/DeliverfileDefault +2 -9
- data/deliver/lib/assets/DeliverfileDefault.swift +2 -12
- data/deliver/lib/deliver/setup.rb +1 -4
- data/fastlane/lib/.DS_Store +0 -0
- data/fastlane/lib/assets/.DS_Store +0 -0
- data/fastlane/lib/assets/AppfileTemplate +5 -6
- data/fastlane/lib/assets/AppfileTemplate.swift +3 -4
- data/fastlane/lib/assets/DefaultFastfileTemplate +10 -67
- data/fastlane/lib/assets/DefaultFastfileTemplate.swift +8 -66
- data/fastlane/lib/fastlane/.DS_Store +0 -0
- data/fastlane/lib/fastlane/actions/.DS_Store +0 -0
- data/fastlane/lib/fastlane/actions/docs/.DS_Store +0 -0
- data/fastlane/lib/fastlane/actions/get_build_number.rb +7 -1
- data/fastlane/lib/fastlane/commands_generator.rb +11 -19
- data/fastlane/lib/fastlane/setup/.DS_Store +0 -0
- data/fastlane/lib/fastlane/setup/setup.rb +247 -34
- data/fastlane/lib/fastlane/setup/setup_android.rb +65 -39
- data/fastlane/lib/fastlane/setup/setup_ios.rb +419 -280
- data/fastlane/lib/fastlane/version.rb +1 -1
- data/fastlane/swift/Deliverfile.swift +1 -1
- data/fastlane/swift/Gymfile.swift +1 -1
- data/fastlane/swift/Matchfile.swift +1 -1
- data/fastlane/swift/Precheckfile.swift +1 -1
- data/fastlane/swift/Scanfile.swift +1 -1
- data/fastlane/swift/Screengrabfile.swift +1 -1
- data/fastlane/swift/Snapshotfile.swift +1 -1
- data/fastlane_core/lib/.DS_Store +0 -0
- data/fastlane_core/lib/fastlane_core/.DS_Store +0 -0
- data/fastlane_core/lib/fastlane_core/configuration/configuration.rb +3 -1
- data/sigh/lib/assets/resign.sh +0 -1
- data/snapshot/lib/assets/SnapfileTemplate +9 -12
- data/snapshot/lib/snapshot/commands_generator.rb +2 -0
- data/snapshot/lib/snapshot/setup.rb +20 -9
- data/spaceship/lib/spaceship.rb +4 -0
- data/spaceship/lib/spaceship/portal/ui/select_team.rb +3 -3
- data/spaceship/lib/spaceship/tunes/tunes_client.rb +15 -11
- metadata +24 -17
- data/fastlane/lib/assets/FastfileTemplateAndroid +0 -65
@@ -1,71 +1,97 @@
|
|
1
1
|
module Fastlane
|
2
2
|
class SetupAndroid < Setup
|
3
|
-
|
4
|
-
|
5
|
-
return unless response
|
3
|
+
attr_accessor :json_key_file
|
4
|
+
attr_accessor :package_name
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
def setup_android
|
7
|
+
self.platform = :android
|
8
|
+
self.is_swift_fastfile = false
|
9
|
+
|
10
|
+
welcome_to_fastlane
|
11
|
+
|
12
|
+
self.fastfile_content = fastfile_template_content
|
13
|
+
self.appfile_content = appfile_template_content
|
14
|
+
|
15
|
+
fetch_information_for_appfile
|
16
|
+
|
17
|
+
FastlaneCore::FastlaneFolder.create_folder!
|
12
18
|
|
13
19
|
init_supply
|
14
20
|
|
15
|
-
|
21
|
+
self.append_lane([
|
22
|
+
"desc \"Runs all the tests\"",
|
23
|
+
"lane :test do",
|
24
|
+
" gradle(task: \"test\")",
|
25
|
+
"end"
|
26
|
+
])
|
27
|
+
|
28
|
+
self.append_lane([
|
29
|
+
"desc \"Submit a new Beta Build to Crashlytics Beta\"",
|
30
|
+
"lane :beta do",
|
31
|
+
" gradle(task: \"assembleRelease\")",
|
32
|
+
" crashlytics",
|
33
|
+
"",
|
34
|
+
" # sh \"your_script.sh\"",
|
35
|
+
" # You can also use other beta testing services here",
|
36
|
+
"end"
|
37
|
+
])
|
38
|
+
|
39
|
+
self.append_lane([
|
40
|
+
"desc \"Deploy a new version to the Google Play\"",
|
41
|
+
"lane :deploy do",
|
42
|
+
" gradle(task: \"assembleRelease\")",
|
43
|
+
" upload_to_play_store",
|
44
|
+
"end"
|
45
|
+
])
|
46
|
+
|
47
|
+
self.lane_to_mention = "test"
|
48
|
+
|
49
|
+
finish_up
|
16
50
|
end
|
17
51
|
|
18
|
-
def
|
19
|
-
UI.message('
|
20
|
-
UI.
|
52
|
+
def fetch_information_for_appfile
|
53
|
+
UI.message('')
|
54
|
+
UI.message("To avoid re-entering your package name and issuer every time you run fastlane, we'll store those in a so-called Appfile.")
|
21
55
|
|
22
|
-
package_name = UI.input("Package Name (com.krausefx.app): ")
|
56
|
+
self.package_name = UI.input("Package Name (com.krausefx.app): ")
|
23
57
|
puts("")
|
24
58
|
puts("To automatically upload builds and metadata to Google Play, fastlane needs a service action json secret file".yellow)
|
25
|
-
puts("Feel free to just click Enter to skip not provide certain things")
|
26
59
|
puts("Follow the Setup Guide on how to get the Json file: https://docs.fastlane.tools/actions/supply/".yellow)
|
27
|
-
|
28
|
-
|
29
|
-
template = File.read("#{Fastlane::ROOT}/lib/assets/AppfileTemplateAndroid")
|
30
|
-
template.gsub!('[[JSON_KEY_FILE]]', json_key_file)
|
31
|
-
template.gsub!('[[PACKAGE_NAME]]', package_name)
|
32
|
-
path = File.join(folder, 'Appfile')
|
33
|
-
File.write(path, template)
|
34
|
-
UI.success("Created new file '#{path}'. Edit it to manage your preferred app metadata information.")
|
35
|
-
end
|
60
|
+
puts("Feel free to press Enter at any time in order to skip providing pieces of information when asked")
|
61
|
+
self.json_key_file = UI.input("Path to the json secret file: ")
|
36
62
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
template.gsub!('[[FASTLANE_VERSION]]', Fastlane::VERSION)
|
41
|
-
|
42
|
-
path = File.join(folder, 'Fastfile')
|
43
|
-
File.write(path, template)
|
44
|
-
UI.success("Created new file '#{path}'. Edit it to manage your own deployment lanes.")
|
63
|
+
self.appfile_content.gsub!("[[JSON_KEY_FILE]]", self.json_key_file)
|
64
|
+
self.appfile_content.gsub!("[[PACKAGE_NAME]]", self.package_name)
|
45
65
|
end
|
46
66
|
|
47
67
|
def init_supply
|
48
68
|
UI.message("")
|
49
|
-
question = "Do you plan on uploading metadata, screenshots and builds to Google Play using fastlane?".yellow
|
69
|
+
question = "Do you plan on uploading metadata, screenshots, and builds to Google Play using fastlane?".yellow
|
50
70
|
UI.message(question)
|
51
|
-
UI.message("
|
52
|
-
if UI.confirm(
|
71
|
+
UI.message("We will now download your existing metadata and screenshots into the `fastlane` folder so fastlane can manage it")
|
72
|
+
if UI.confirm("Download existing metadata and setup metadata management?")
|
53
73
|
begin
|
54
74
|
require 'supply'
|
55
75
|
require 'supply/setup'
|
56
|
-
|
76
|
+
supply_config = {
|
77
|
+
json_key: self.json_key_file,
|
78
|
+
package_name: self.package_name
|
79
|
+
}
|
80
|
+
Supply.config = FastlaneCore::Configuration.create(Supply::Options.available_options, supply_config)
|
57
81
|
Supply::Setup.new.perform_download
|
58
82
|
rescue => ex
|
59
83
|
UI.error(ex.to_s)
|
60
|
-
UI.error("supply failed, but don't worry, you can
|
84
|
+
UI.error("Setting up `supply` (metadata management action) failed, but don't worry, you can try setting it up again using `fastlane supply init` whenever you want.")
|
61
85
|
end
|
62
86
|
else
|
63
|
-
UI.success("You can run `fastlane supply init` to
|
87
|
+
UI.success("You can run `fastlane supply init` to set up metadata management at a later point.")
|
64
88
|
end
|
65
89
|
end
|
66
90
|
|
67
|
-
def
|
68
|
-
|
91
|
+
def finish_up
|
92
|
+
self.fastfile_content.gsub!(":ios", ":android")
|
93
|
+
|
94
|
+
super
|
69
95
|
end
|
70
96
|
end
|
71
97
|
end
|
@@ -1,376 +1,515 @@
|
|
1
|
-
require 'spaceship'
|
2
|
-
|
3
1
|
module Fastlane
|
2
|
+
# rubocop:disable Metrics/ClassLength
|
4
3
|
class SetupIos < Setup
|
5
|
-
#
|
6
|
-
attr_accessor :tools
|
4
|
+
# Reference to the iOS project `project.rb`
|
7
5
|
attr_accessor :project
|
8
|
-
attr_accessor :apple_id
|
9
6
|
|
10
|
-
|
11
|
-
attr_accessor :
|
7
|
+
# App Identifier of the current app
|
8
|
+
attr_accessor :app_identifier
|
9
|
+
|
10
|
+
# Scheme of the Xcode project
|
11
|
+
attr_accessor :scheme
|
12
12
|
|
13
|
-
|
14
|
-
attr_accessor :
|
13
|
+
# If the current setup requires a login, this is where we'll store the team ID
|
14
|
+
attr_accessor :itc_team_id
|
15
|
+
attr_accessor :adp_team_id
|
15
16
|
|
16
|
-
attr_accessor :
|
17
|
-
|
17
|
+
attr_accessor :app_exists_on_itc
|
18
|
+
|
19
|
+
attr_accessor :automatic_versioning_enabled
|
20
|
+
|
21
|
+
def setup_ios
|
22
|
+
require 'spaceship'
|
23
|
+
|
24
|
+
self.platform = :ios
|
18
25
|
|
19
|
-
|
26
|
+
welcome_to_fastlane
|
20
27
|
|
21
|
-
|
22
|
-
|
23
|
-
self.apple_id = user
|
24
|
-
show_infos
|
28
|
+
if preferred_setup_method
|
29
|
+
self.send(preferred_setup_method)
|
25
30
|
|
26
|
-
|
27
|
-
|
31
|
+
# We call `choose_swift` here also, as we skip the selection (see below)
|
32
|
+
choose_swift
|
33
|
+
return
|
34
|
+
end
|
35
|
+
|
36
|
+
options = {
|
37
|
+
"📸 Automate screenshots" => :ios_screenshots,
|
38
|
+
"👩✈️ Automate beta distribution to TestFlight" => :ios_testflight,
|
39
|
+
"🚀 Automate App Store distribution" => :ios_app_store,
|
40
|
+
"🛠 Manual setup - manually setup your project to automate your tasks" => :ios_manual
|
41
|
+
}
|
42
|
+
|
43
|
+
selected = UI.select("What would you like to use fastlane for?", options.keys)
|
44
|
+
method_to_use = options[selected]
|
28
45
|
|
29
|
-
|
30
|
-
|
31
|
-
|
46
|
+
# we want to first ask the user of what they want to do
|
47
|
+
# to make them already excited about all the things they can do with fastlane
|
48
|
+
# and only then we ask them about the language they want to use
|
49
|
+
choose_swift
|
32
50
|
|
33
51
|
begin
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
52
|
+
self.send(method_to_use)
|
53
|
+
rescue => ex
|
54
|
+
# If it's already manual, and it has failed
|
55
|
+
# we need to re-raise the exception, as something definitely is wrong
|
56
|
+
raise ex if method_to_use == :ios_manual
|
57
|
+
|
58
|
+
# If we're here, that means something else failed. We now show the
|
59
|
+
# error message and fallback to `:ios_manual`
|
60
|
+
UI.error("--------------------")
|
61
|
+
UI.error("fastlane init failed")
|
62
|
+
UI.error("--------------------")
|
63
|
+
|
64
|
+
UI.verbose(ex.backtrace.join("\n"))
|
65
|
+
if ex.kind_of?(Spaceship::Client::BasicPreferredInfoError) || ex.kind_of?(Spaceship::Client::UnexpectedResponse)
|
66
|
+
UI.error(ex.preferred_error_info)
|
45
67
|
else
|
46
|
-
|
47
|
-
UI.message("Falling back to manual onboarding")
|
48
|
-
manual_setup
|
68
|
+
UI.error(ex.to_s)
|
49
69
|
end
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
rescue => ex # this will also be caused by Ctrl + C
|
57
|
-
UI.message("Ran into error while trying to connect to iTunes Connect / Dev Portal: #{ex}")
|
58
|
-
UI.message("Falling back to manual onboarding")
|
59
|
-
|
60
|
-
if is_manual_setup
|
61
|
-
handle_exception(exception: ex)
|
70
|
+
|
71
|
+
UI.important("Something failed while running `fastlane init`")
|
72
|
+
UI.important("Tried using Apple ID with email '#{self.user}'")
|
73
|
+
UI.important("You can either retry, or fallback to manual setup which will create a basic Fastfile")
|
74
|
+
if UI.confirm("Would you like to fallback to a manual Fastfile?")
|
75
|
+
self.ios_manual
|
62
76
|
else
|
63
|
-
|
64
|
-
UI.error('An error occurred during the setup process. Falling back to manual setup!')
|
65
|
-
try_manual_setup
|
77
|
+
self.send(method_to_use)
|
66
78
|
end
|
79
|
+
# the second time, we're just failing, and don't use a `begin` `rescue` block any more
|
67
80
|
end
|
68
81
|
end
|
69
82
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
83
|
+
# Different iOS flows
|
84
|
+
def ios_testflight
|
85
|
+
UI.header("Setting up fastlane for iOS TestFlight distribution")
|
86
|
+
find_and_setup_xcode_project
|
87
|
+
apple_xcode_project_versioning_enabled
|
88
|
+
ask_for_credentials(adp: true, itc: true)
|
89
|
+
verify_app_exists_adp!
|
90
|
+
verify_app_exists_itc!
|
77
91
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
error_message << "in the `General` section of your project settings."
|
93
|
-
error_message << "Restart `fastlane init` once you're done!"
|
94
|
-
UI.user_error!(error_message.join(" "))
|
92
|
+
if self.is_swift_fastfile
|
93
|
+
lane = ["func betaLane() {",
|
94
|
+
"desc(\"Push a new beta build to TestFlight\")",
|
95
|
+
increment_build_number_if_applicable,
|
96
|
+
"\tbuildApp(#{project_prefix}scheme: \"#{self.scheme}\")",
|
97
|
+
"\tuploadToTestflight(username: \"#{self.user}\")",
|
98
|
+
"}"]
|
99
|
+
else
|
100
|
+
lane = ["desc \"Push a new beta build to TestFlight\"",
|
101
|
+
"lane :beta do",
|
102
|
+
increment_build_number_if_applicable,
|
103
|
+
" build_app(#{project_prefix}scheme: \"#{self.scheme}\")",
|
104
|
+
" upload_to_testflight",
|
105
|
+
"end"]
|
95
106
|
end
|
96
|
-
|
107
|
+
self.append_lane(lane)
|
97
108
|
|
98
|
-
|
99
|
-
|
100
|
-
return false unless File.basename(path) == "ios"
|
101
|
-
return false unless File.exist?(package_json)
|
102
|
-
package_content = File.read(package_json)
|
103
|
-
return true if package_content.include?("react-native")
|
104
|
-
false
|
109
|
+
self.lane_to_mention = "beta"
|
110
|
+
finish_up
|
105
111
|
end
|
106
112
|
|
107
|
-
def
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
+
def ios_app_store
|
114
|
+
UI.header("Setting up fastlane for iOS App Store distribution")
|
115
|
+
find_and_setup_xcode_project
|
116
|
+
apple_xcode_project_versioning_enabled
|
117
|
+
ask_for_credentials(adp: true, itc: true)
|
118
|
+
verify_app_exists_adp!
|
119
|
+
verify_app_exists_itc!
|
120
|
+
|
121
|
+
if self.app_exists_on_itc
|
122
|
+
UI.header("Manage app metadata?")
|
123
|
+
UI.message("Would you like to have fastlane manage your app's metadata?")
|
124
|
+
UI.message("If you enable this feature, fastlane will download your existing metadata and screenshots.")
|
125
|
+
UI.message("This way, you'll be able to edit your app's metadata in local `.txt` files.")
|
126
|
+
UI.message("After editing the local `.txt` files, just run fastlane and all changes will be pushed up.")
|
127
|
+
UI.message("If you don't want to use this feature, you can still use fastlane to upload and distribute new builds to the App Store")
|
128
|
+
include_metadata = UI.confirm("Would you like fastlane to manage your app's metadata?")
|
129
|
+
if include_metadata
|
130
|
+
require 'deliver'
|
131
|
+
require 'deliver/setup'
|
132
|
+
|
133
|
+
deliver_options = FastlaneCore::Configuration.create(
|
134
|
+
Deliver::Options.available_options,
|
135
|
+
{
|
136
|
+
run_precheck_before_submit: false, # precheck doesn't need to run during init
|
137
|
+
username: self.user,
|
138
|
+
app_identifier: self.app_identifier,
|
139
|
+
team_id: self.itc_team_id
|
140
|
+
}
|
141
|
+
)
|
142
|
+
|
143
|
+
Deliver::DetectValues.new.run!(deliver_options, {}) # needed to fetch the app details
|
144
|
+
Deliver::Setup.new.run(deliver_options, is_swift: self.is_swift_fastfile)
|
145
|
+
end
|
113
146
|
end
|
114
|
-
enable_deliver
|
115
|
-
generate_fastfile(manually: false)
|
116
147
|
|
117
148
|
if self.is_swift_fastfile
|
118
|
-
|
149
|
+
lane = ["func releaseLane() {",
|
150
|
+
"desc(\"Push a new release build to the App Store\")",
|
151
|
+
increment_build_number_if_applicable,
|
152
|
+
"\tbuildApp(#{project_prefix}scheme: \"#{self.scheme}\")"]
|
153
|
+
if include_metadata
|
154
|
+
lane << "\tuploadToAppStore(username: \"#{self.user}\", app: \"#{self.app_identifier}\")"
|
155
|
+
else
|
156
|
+
lane << "\tuploadToAppStore(username: \"#{self.user}\", app: \"#{self.app_identifier}\", skipScreenshots: true, skipMetadata: true)"
|
157
|
+
end
|
158
|
+
lane << "}"
|
159
|
+
else
|
160
|
+
lane = ["desc \"Push a new release build to the App Store\"",
|
161
|
+
"lane :release do",
|
162
|
+
increment_build_number_if_applicable,
|
163
|
+
" build_app(#{project_prefix}scheme: \"#{self.scheme}\")"]
|
164
|
+
if include_metadata
|
165
|
+
lane << " upload_to_app_store"
|
166
|
+
else
|
167
|
+
lane << " upload_to_app_store(skip_metadata: true, skip_screenshots: true)"
|
168
|
+
end
|
169
|
+
lane << "end"
|
119
170
|
end
|
120
171
|
|
121
|
-
|
172
|
+
append_lane(lane)
|
173
|
+
self.lane_to_mention = "release"
|
174
|
+
finish_up
|
122
175
|
end
|
123
176
|
|
124
|
-
def
|
125
|
-
|
126
|
-
generate_appfile(manually: true)
|
127
|
-
detect_installed_tools # after copying the existing files
|
128
|
-
ask_to_enable_other_tools
|
129
|
-
generate_fastfile(manually: true)
|
177
|
+
def ios_screenshots
|
178
|
+
UI.header("Setting up fastlane to automate iOS screenshots")
|
130
179
|
|
131
|
-
|
132
|
-
|
133
|
-
|
180
|
+
UI.message("fastlane uses UI Tests to automate generating localized screenshots of your iOS app")
|
181
|
+
UI.message("fastlane will now create 2 helper files that are needed to get the setup running")
|
182
|
+
UI.message("For more information on how this works and best practices, check out")
|
183
|
+
UI.message("\thttps://docs.fastlane.tools/getting-started/ios/screenshots/".cyan)
|
184
|
+
continue_with_enter
|
134
185
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
if wants_to_create_app
|
142
|
-
create_app_if_necessary
|
143
|
-
detect_if_app_is_available # check if the app was, in fact, created.
|
144
|
-
end
|
186
|
+
begin
|
187
|
+
find_and_setup_xcode_project(ask_for_scheme: false) # to get the bundle identifier
|
188
|
+
rescue => ex
|
189
|
+
# If this fails, it's no big deal, since we really just want the bundle identifier
|
190
|
+
# so instead, we'll just ask the user
|
191
|
+
UI.verbose(ex.to_s)
|
145
192
|
end
|
146
|
-
|
147
|
-
|
148
|
-
|
193
|
+
|
194
|
+
require 'snapshot'
|
195
|
+
require 'snapshot/setup'
|
196
|
+
|
197
|
+
Snapshot::Setup.create(
|
198
|
+
FastlaneCore::FastlaneFolder.path,
|
199
|
+
is_swift_fastfile: self.is_swift_fastfile,
|
200
|
+
print_instructions_on_failure: true
|
201
|
+
)
|
202
|
+
|
203
|
+
UI.message("If you want more details on how to setup automatic screenshots, check out")
|
204
|
+
UI.message("\thttps://docs.fastlane.tools/getting-started/ios/screenshots/#setting-up-snapshot".cyan)
|
205
|
+
continue_with_enter
|
206
|
+
|
207
|
+
available_schemes = self.project.schemes
|
208
|
+
ui_testing_scheme = UI.select("Which is your UI Testing scheme? If you can't find it in this list, make sure it's marked as `Shared` in the Xcode scheme list", available_schemes)
|
209
|
+
|
210
|
+
UI.header("Automatically upload to iTC?")
|
211
|
+
UI.message("Would you like fastlane to automatically upload all generated screenshots to iTunes Connect")
|
212
|
+
UI.message("after generating them?")
|
213
|
+
UI.message("If you enable this feature you'll need to provide your iTunes Connect credentials so fastlane can upload the screenshots to iTunes Connect")
|
214
|
+
automatic_upload = UI.confirm("Enable automatic upload of localized screenshots to iTunes Connect?")
|
215
|
+
if automatic_upload
|
216
|
+
ask_for_credentials(adp: true, itc: true)
|
217
|
+
verify_app_exists_itc!
|
149
218
|
end
|
150
|
-
end
|
151
219
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
self.project.select_scheme(preferred_to_include: self.project.project_name)
|
157
|
-
self.app_identifier = self.project.default_app_identifier # These two vars need to be accessed in order to be set
|
158
|
-
self.app_name = self.project.default_app_name # They are set as a side effect, this could/should be changed down the road
|
159
|
-
end
|
220
|
+
if self.is_swift_fastfile
|
221
|
+
lane = ["func screenshotsLane() {",
|
222
|
+
"desc(\"Generate new localized screenshots\")",
|
223
|
+
"\tcaptureScreenshots(#{project_prefix}scheme: \"#{ui_testing_scheme}\")"]
|
160
224
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
puts(Terminal::Table.new(rows: FastlaneCore::PrintTable.transform_output(rows),
|
170
|
-
title: "Detected Values"))
|
171
|
-
puts("")
|
172
|
-
|
173
|
-
unless self.itc_ref || self.project.mac?
|
174
|
-
UI.important("This app identifier doesn't exist on iTunes Connect yet, it will be created for you")
|
175
|
-
end
|
225
|
+
if automatic_upload
|
226
|
+
lane << "\tuploadToAppStore(username: \"#{self.user}\", app: \"#{self.app_identifier}\", skipBinaryUpload: true, skipMetadata: true)"
|
227
|
+
end
|
228
|
+
lane << "}"
|
229
|
+
else
|
230
|
+
lane = ["desc \"Generate new localized screenshots\"",
|
231
|
+
"lane :screenshots do",
|
232
|
+
" capture_screenshots(#{project_prefix}scheme: \"#{ui_testing_scheme}\")"]
|
176
233
|
|
177
|
-
|
178
|
-
|
234
|
+
if automatic_upload
|
235
|
+
lane << " upload_to_app_store(skip_binary_upload: true, skip_metadata: true)"
|
236
|
+
end
|
237
|
+
lane << "end"
|
179
238
|
end
|
180
|
-
|
239
|
+
append_lane(lane)
|
181
240
|
|
182
|
-
|
183
|
-
|
184
|
-
UI.success("fastlane will check what tools you're already using and set up")
|
185
|
-
UI.success('the tool automatically for you. Have fun! ')
|
241
|
+
self.lane_to_mention = "screenshots"
|
242
|
+
finish_up
|
186
243
|
end
|
187
244
|
|
188
|
-
def
|
189
|
-
|
190
|
-
end
|
245
|
+
def ios_manual
|
246
|
+
UI.header("Setting up fastlane so you can manually configure it")
|
191
247
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
248
|
+
if self.is_swift_fastfile
|
249
|
+
append_lane(["func customLane() {",
|
250
|
+
"desc(\"Description of what the lane does\")",
|
251
|
+
"\t// add actions here: https://docs.fastlane.tools/actions",
|
252
|
+
"}"])
|
253
|
+
self.lane_to_mention = "custom" # lane is part of the notation
|
254
|
+
else
|
255
|
+
append_lane(["desc \"Description of what the lane does\"",
|
256
|
+
"lane :custom_lane do",
|
257
|
+
" # add actions here: https://docs.fastlane.tools/actions",
|
258
|
+
"end"])
|
259
|
+
self.lane_to_mention = "custom_lane"
|
200
260
|
end
|
201
|
-
end
|
202
261
|
|
203
|
-
|
204
|
-
self.apple_id ||= UI.input("Your Apple ID (e.g. fastlane@krausefx.com): ")
|
262
|
+
finish_up
|
205
263
|
end
|
206
264
|
|
207
|
-
|
208
|
-
self.app_identifier = UI.input("App Identifier (com.krausefx.app): ")
|
209
|
-
end
|
265
|
+
# Helpers
|
210
266
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
267
|
+
# Every installation setup that needs an Xcode project should
|
268
|
+
# call this method
|
269
|
+
def find_and_setup_xcode_project(ask_for_scheme: true)
|
270
|
+
UI.message("Parsing your local Xcode project to find the available schemes and the app identifier")
|
271
|
+
config = {} # this is needed as the first method call will store information in there
|
272
|
+
if self.project_path.end_with?("xcworkspace")
|
273
|
+
config[:workspace] = self.project_path
|
274
|
+
else
|
275
|
+
config[:project] = self.project_path
|
216
276
|
end
|
217
277
|
|
218
|
-
|
219
|
-
|
220
|
-
template.gsub!('[[APPLE_ID]]', self.apple_id)
|
278
|
+
FastlaneCore::Project.detect_projects(config)
|
279
|
+
self.project = FastlaneCore::Project.new(config)
|
221
280
|
|
222
|
-
if
|
223
|
-
|
224
|
-
path = File.join(folder, 'Appfile.swift')
|
225
|
-
else
|
226
|
-
itc_team = self.itc_team ? "itc_team_id \"#{self.itc_team}\" # iTunes Connect Team ID\n" : ""
|
227
|
-
path = File.join(folder, 'Appfile')
|
281
|
+
if ask_for_scheme
|
282
|
+
self.scheme = self.project.select_scheme(preferred_to_include: self.project.project_name)
|
228
283
|
end
|
229
|
-
template.gsub!('[[ITC_TEAM]]', itc_team)
|
230
284
|
|
231
|
-
|
232
|
-
|
285
|
+
self.app_identifier = self.project.default_app_identifier # These two vars need to be accessed in order to be set
|
286
|
+
if self.app_identifier.to_s.length == 0
|
287
|
+
ask_for_bundle_identifier
|
288
|
+
end
|
233
289
|
end
|
234
290
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
self.dev_portal_team = Spaceship.select_team
|
241
|
-
self.portal_ref = Spaceship::App.find(self.app_identifier)
|
242
|
-
|
243
|
-
Spaceship::Tunes.login(@apple_id, nil)
|
244
|
-
self.itc_team = Spaceship::Tunes.select_team
|
245
|
-
self.itc_ref = Spaceship::Application.find(self.app_identifier)
|
291
|
+
def ask_for_bundle_identifier
|
292
|
+
loop do
|
293
|
+
return if self.app_identifier.to_s.length > 0
|
294
|
+
self.app_identifier = UI.input("Bundle identifier of your app: ")
|
295
|
+
end
|
246
296
|
end
|
247
297
|
|
248
|
-
def
|
249
|
-
UI.
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
298
|
+
def ask_for_credentials(itc: true, adp: false)
|
299
|
+
UI.header("Login with your Apple ID")
|
300
|
+
UI.message("To use iTunes Connect and Apple Developer Portal features as part of fastlane,")
|
301
|
+
UI.message("we will ask you for your Apple ID username and password")
|
302
|
+
UI.message("This is necessary for certain fastlane features, for example:")
|
303
|
+
UI.message("")
|
304
|
+
UI.message("- Create and manage your provisioning profiles on the Developer Portal")
|
305
|
+
UI.message("- Upload and manage TestFlight and App Store builds on iTunes Connect")
|
306
|
+
UI.message("- Manage your iTunes Connect app metadata and screenshots")
|
307
|
+
UI.message("")
|
308
|
+
UI.message("Your Apple ID credentials will only be stored in your Keychain, on your local machine")
|
309
|
+
UI.message("For more information, check out")
|
310
|
+
UI.message("\thttps://github.com/fastlane/fastlane/tree/master/credentials_manager".cyan)
|
311
|
+
UI.message("")
|
312
|
+
|
313
|
+
if self.user.to_s.length == 0
|
314
|
+
UI.important("Please enter your Apple ID developer credentials")
|
315
|
+
self.user = UI.input("Apple ID Username:")
|
316
|
+
end
|
317
|
+
UI.message("Logging in...")
|
318
|
+
|
319
|
+
# Disable the warning texts and information that's not relevant during onboarding
|
320
|
+
ENV["FASTLANE_HIDE_LOGIN_INFORMATION"] = 1.to_s
|
321
|
+
ENV["FASTLANE_HIDE_TEAM_INFORMATION"] = 1.to_s
|
322
|
+
|
323
|
+
if itc
|
324
|
+
Spaceship::Tunes.login(self.user)
|
325
|
+
Spaceship::Tunes.select_team
|
326
|
+
self.itc_team_id = Spaceship::Tunes.client.team_id
|
327
|
+
if self.is_swift_fastfile
|
328
|
+
self.append_team("var itcTeam: String? { return \"#{self.itc_team_id}\" } // iTunes Connect Team ID")
|
329
|
+
else
|
330
|
+
self.append_team("itc_team_id \"#{self.itc_team_id}\" # iTunes Connect Team ID")
|
267
331
|
end
|
268
332
|
end
|
269
|
-
end
|
270
333
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
334
|
+
if adp
|
335
|
+
Spaceship::Portal.login(self.user)
|
336
|
+
Spaceship::Portal.select_team
|
337
|
+
self.adp_team_id = Spaceship::Portal.client.team_id
|
338
|
+
if self.is_swift_fastfile
|
339
|
+
self.append_team("var teamID: String { return \"#{self.adp_team_id}\" } // Apple Developer Portal Team ID")
|
340
|
+
else
|
341
|
+
self.append_team("team_id \"#{self.adp_team_id}\" # Developer Portal Team ID")
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
UI.success("✅ Logging in with your Apple ID was successful")
|
277
346
|
end
|
278
347
|
|
279
|
-
def
|
280
|
-
|
281
|
-
|
282
|
-
|
348
|
+
def apple_xcode_project_versioning_enabled
|
349
|
+
self.automatic_versioning_enabled = false
|
350
|
+
|
351
|
+
paths = self.project.project_paths
|
352
|
+
return false if paths.count == 0
|
283
353
|
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
354
|
+
result = Fastlane::Actions::GetBuildNumberAction.run({
|
355
|
+
project: paths.first, # most of the times, there will only be one project in there
|
356
|
+
hide_error_when_versioning_disabled: true
|
357
|
+
})
|
288
358
|
|
289
|
-
|
290
|
-
|
359
|
+
if result.kind_of?(String) && result.to_f > 0
|
360
|
+
self.automatic_versioning_enabled = true
|
361
|
+
end
|
362
|
+
return self.automatic_versioning_enabled
|
291
363
|
end
|
292
364
|
|
293
|
-
def
|
294
|
-
|
365
|
+
def show_information_about_version_bumps
|
366
|
+
UI.important("It looks like your project isn't set up to do automatic version incrementing")
|
367
|
+
UI.important("To enable fastlane to handle automatic version incrementing for you, please follow this guide:")
|
368
|
+
UI.message("\thttps://developer.apple.com/library/content/qa/qa1827/_index.html".cyan)
|
369
|
+
UI.important("Afterwards check out the fastlane docs on how to set up automatic build increments")
|
370
|
+
UI.message("\thttps://docs.fastlane.tools/getting-started/ios/beta-deployment/#best-practices".cyan)
|
371
|
+
end
|
295
372
|
|
296
|
-
|
373
|
+
def verify_app_exists_adp!
|
374
|
+
UI.user_error!("No app identifier provided") if self.app_identifier.to_s.length == 0
|
375
|
+
UI.message("Checking if the app '#{self.app_identifier}' exists in your Apple Developer Portal...")
|
376
|
+
app = Spaceship::Portal::App.find(self.app_identifier)
|
377
|
+
if app.nil?
|
378
|
+
UI.error("It looks like the app '#{self.app_identifier}' isn't available on the #{'Apple Developer Portal'.bold.underline}")
|
379
|
+
UI.error("for the team ID '#{self.adp_team_id}' on Apple ID '#{self.user}'")
|
297
380
|
|
298
|
-
|
299
|
-
|
300
|
-
if self.is_swift_fastfile
|
301
|
-
template.gsub!('[[SCHEME]]', "scheme: \"#{scheme}\"")
|
381
|
+
if UI.confirm("Do you want fastlane to create the App ID for you on the Apple Developer Portal?")
|
382
|
+
create_app_online!(mode: :adp)
|
302
383
|
else
|
303
|
-
|
384
|
+
UI.important("Alright, we won't create the app for you. Be aware, the build is probably going to fail when you try it")
|
304
385
|
end
|
305
386
|
else
|
306
|
-
|
387
|
+
UI.success("✅ Your app '#{self.app_identifier}' is available on iTunes Connect")
|
307
388
|
end
|
389
|
+
end
|
308
390
|
|
309
|
-
|
310
|
-
|
311
|
-
if self.
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
391
|
+
def verify_app_exists_itc!
|
392
|
+
UI.user_error!("No app identifier provided") if self.app_identifier.to_s.length == 0
|
393
|
+
UI.message("Checking if the app '#{self.app_identifier}' exists on iTunes Connect...")
|
394
|
+
app = Spaceship::Tunes::Application.find(self.app_identifier)
|
395
|
+
if app.nil?
|
396
|
+
UI.error("Looks like the app '#{self.app_identifier}' isn't available on #{'iTunes Connect'.bold.underline}")
|
397
|
+
UI.error("for the team ID '#{self.itc_team_id}' on Apple ID '#{self.user}'")
|
398
|
+
if UI.confirm("Would you like fastlane to create the App on iTunes Connect for you?")
|
399
|
+
create_app_online!(mode: :itc)
|
400
|
+
self.app_exists_on_itc = true
|
401
|
+
else
|
402
|
+
UI.important("Alright, we won't create the app for you. Be aware, the build is probably going to fail when you try it")
|
403
|
+
end
|
316
404
|
else
|
317
|
-
|
318
|
-
|
319
|
-
template.gsub!('carthage', '# cocoapods') unless self.tools[:carthage]
|
320
|
-
path = File.join(folder, 'Fastfile')
|
405
|
+
UI.success("✅ Your app '#{self.app_identifier}' is available on iTunes Connect")
|
406
|
+
self.app_exists_on_itc = true
|
321
407
|
end
|
408
|
+
end
|
322
409
|
|
323
|
-
|
324
|
-
|
325
|
-
|
410
|
+
def finish_up
|
411
|
+
# iOS specific things first
|
412
|
+
if self.app_identifier
|
413
|
+
self.appfile_content.gsub!("# app_identifier", "app_identifier")
|
414
|
+
self.appfile_content.gsub!("[[APP_IDENTIFIER]]", self.app_identifier)
|
326
415
|
end
|
327
416
|
|
328
|
-
|
329
|
-
|
330
|
-
|
417
|
+
if self.user
|
418
|
+
self.appfile_content.gsub!("# apple_id", "apple_id")
|
419
|
+
self.appfile_content.gsub!("[[APPLE_ID]]", self.user)
|
420
|
+
end
|
421
|
+
|
422
|
+
self.show_information_about_version_bumps unless self.automatic_versioning_enabled
|
331
423
|
|
332
|
-
|
333
|
-
FastlaneCore::FastlaneFolder.path
|
424
|
+
super
|
334
425
|
end
|
335
426
|
|
336
|
-
|
337
|
-
|
338
|
-
|
427
|
+
# Returns the `workspace` or `project` key/value pair for
|
428
|
+
# gym and snapshot, but only if necessary
|
429
|
+
# (when there are multiple projects in the current directory)
|
430
|
+
# it's a prefix, and not a suffix, as Swift cares about the order of parameters
|
431
|
+
def project_prefix
|
432
|
+
return "" unless self.had_multiple_projects_to_choose_from
|
433
|
+
|
434
|
+
if self.project_path.end_with?(".xcworkspace")
|
435
|
+
return "workspace: \"#{self.project_path}\", "
|
339
436
|
else
|
340
|
-
return "#{
|
437
|
+
return "project: \"#{self.project_path}\", "
|
341
438
|
end
|
342
439
|
end
|
343
440
|
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
441
|
+
# Choose the language the config files should be based in
|
442
|
+
def choose_swift
|
443
|
+
# The Swift question below is removed, as we don't want everyone
|
444
|
+
# to use the Beta setup yet
|
445
|
+
# using `||=` since if the user already used `fastlane init swift` we want to just use swift
|
446
|
+
# self.is_swift_fastfile ||= UI.confirm("[Beta] Do you want to try our new experimental Swift based configuration files?")
|
447
|
+
|
448
|
+
self.fastfile_content = fastfile_template_content
|
449
|
+
self.appfile_content = appfile_template_content
|
350
450
|
end
|
351
451
|
|
352
|
-
def
|
353
|
-
|
452
|
+
def increment_build_number_if_applicable
|
453
|
+
return nil unless self.automatic_versioning_enabled
|
454
|
+
return nil if self.project.project_paths.first.to_s.length == 0
|
354
455
|
|
355
|
-
|
356
|
-
|
456
|
+
project_path = self.project.project_paths.first
|
457
|
+
# Convert the absolute path to a relative path
|
458
|
+
project_path_name = Pathname.new(project_path)
|
459
|
+
current_path_name = Pathname.new(File.expand_path("."))
|
357
460
|
|
358
|
-
|
461
|
+
relative_project_path = project_path_name.relative_path_from(current_path_name)
|
462
|
+
|
463
|
+
if self.is_swift_fastfile
|
464
|
+
return "\tincrementBuildNumber(xcodeproj: \"#{relative_project_path}\")"
|
465
|
+
else
|
466
|
+
return " increment_build_number(xcodeproj: \"#{relative_project_path}\")"
|
467
|
+
end
|
359
468
|
end
|
360
469
|
|
361
|
-
def
|
362
|
-
#
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
470
|
+
def create_app_online!(mode: nil)
|
471
|
+
# mode is either :adp or :itc
|
472
|
+
require 'produce'
|
473
|
+
produce_options = {
|
474
|
+
username: self.user,
|
475
|
+
team_id: self.adp_team_id,
|
476
|
+
itc_team_id: self.itc_team_id,
|
477
|
+
platform: "ios",
|
478
|
+
app_identifier: self.app_identifier
|
479
|
+
}
|
480
|
+
if mode == :adp
|
481
|
+
produce_options[:skip_itc] = true
|
482
|
+
else
|
483
|
+
produce_options[:skip_devcenter] = true
|
370
484
|
end
|
371
485
|
|
372
|
-
|
373
|
-
|
486
|
+
Produce.config = FastlaneCore::Configuration.create(
|
487
|
+
Produce::Options.available_options,
|
488
|
+
produce_options
|
489
|
+
)
|
490
|
+
|
491
|
+
# The retrying system allows people to correct invalid inputs
|
492
|
+
# e.g. the app's name is already taken
|
493
|
+
loop do
|
494
|
+
begin
|
495
|
+
Produce::Manager.start_producing
|
496
|
+
UI.success("✅ Successfully created app")
|
497
|
+
return # success
|
498
|
+
rescue => ex
|
499
|
+
# show the user facing error, and inform them of what went wrong
|
500
|
+
if ex.kind_of?(Spaceship::Client::BasicPreferredInfoError) || ex.kind_of?(Spaceship::Client::UnexpectedResponse)
|
501
|
+
UI.error(ex.preferred_error_info)
|
502
|
+
else
|
503
|
+
UI.error(ex.to_s)
|
504
|
+
end
|
505
|
+
UI.error(ex.backtrace.join("\n")) if FastlaneCore::Globals.verbose?
|
506
|
+
UI.important("It looks like something went wrong when we tried to create your app on the Apple Developer Portal")
|
507
|
+
unless UI.confirm("Would you like to try again (y)? If you enter (n), fastlane will fall back to the manual setup")
|
508
|
+
raise ex
|
509
|
+
end
|
510
|
+
end
|
511
|
+
end
|
374
512
|
end
|
375
513
|
end
|
514
|
+
# rubocop:enable Metrics/ClassLength
|
376
515
|
end
|