@@ -12,11 +12,12 @@ APPSTORE_CONFIGURATION = "Release"
1212TESTFLIGHT_APP_ENVIRONMENT = "staging"
1313APPSTORE_APP_ENVIRONMENT = "prod"
1414TESTFLIGHT_DATABASE_ID = "(default)"
15- APPSTORE_DATABASE_ID = "prod"
16- TESTFLIGHT_FUNCTION_API_BASE_URL = "https://asia-northeast3-devlog-staging.cloudfunctions.net/api/api"
17- APPSTORE_FUNCTION_API_BASE_URL = "https://asia-northeast3-devlog-c87b6.cloudfunctions.net/prodApi/api"
18- TESTFLIGHT_FIREBASE_PROJECT_ID = "devlog-staging"
19- TESTFLIGHT_GOOGLE_APP_ID = "1:686659799179:ios:eba4e86096153f0539ec8e"
15+ APPSTORE_DATABASE_ID = "(default)"
16+ SHARED_FUNCTION_API_PATH = "/api/api"
17+ APP_CONFIG_XCCONFIG_PATH = File . expand_path (
18+ "../Application/App/Sources/Resource/Config.xcconfig" ,
19+ __dir__
20+ )
2021VERSION_XCCONFIG_PATH = File . expand_path (
2122 "../Application/Shared/Version.xcconfig" ,
2223 __dir__
@@ -54,6 +55,79 @@ rescue SystemCallError => error
5455 UI . user_error! ( "Could not read version configuration file at #{ expanded_path } : #{ error . message } " )
5556end
5657
58+ def function_api_base_url_from_xcconfig ( configuration )
59+ expanded_path = File . expand_path ( APP_CONFIG_XCCONFIG_PATH )
60+ UI . user_error! ( "Missing app configuration file: #{ expanded_path } " ) if !File . file? ( expanded_path )
61+
62+ escaped_configuration = Regexp . escape ( configuration )
63+ assignments = File . foreach ( expanded_path ) . filter_map do |line |
64+ assignment = line . match (
65+ /^\s *FUNCTION_API_BASE_URL\s *\[ \s *config\s *=\s *#{ escaped_configuration } \s *\] \s *=\s *(.*)$/
66+ )
67+ next if assignment . nil?
68+
69+ assignment [ 1 ] . strip
70+ end
71+
72+ if assignments . empty?
73+ UI . user_error! ( "Missing FUNCTION_API_BASE_URL for #{ configuration } in #{ expanded_path } " )
74+ end
75+
76+ function_api_base_url = assignments . last . gsub ( "/$()/" , "//" )
77+ if function_api_base_url . empty?
78+ UI . user_error! ( "Empty FUNCTION_API_BASE_URL for #{ configuration } in #{ expanded_path } " )
79+ end
80+
81+ function_api_base_url
82+ rescue SystemCallError => error
83+ UI . user_error! ( "Could not read app configuration file at #{ expanded_path } : #{ error . message } " )
84+ end
85+
86+ def expected_firebase_configuration_from_xcconfig ( configuration )
87+ expanded_path = File . expand_path ( APP_CONFIG_XCCONFIG_PATH )
88+ UI . user_error! ( "Missing app configuration file: #{ expanded_path } " ) if !File . file? ( expanded_path )
89+
90+ escaped_configuration = Regexp . escape ( configuration )
91+ expected_settings = {
92+ firebase_project_id : "EXPECTED_FIREBASE_PROJECT_ID" ,
93+ google_app_id : "EXPECTED_GOOGLE_APP_ID"
94+ }
95+
96+ expected_settings . to_h do |key , setting |
97+ assignments = File . foreach ( expanded_path ) . filter_map do |line |
98+ assignment = line . match (
99+ /^\s *#{ setting } \s *\[ \s *config\s *=\s *#{ escaped_configuration } \s *\] \s *=\s *(.*)$/
100+ )
101+ next if assignment . nil?
102+
103+ assignment [ 1 ] . strip
104+ end
105+
106+ if assignments . empty? || assignments . last . empty?
107+ UI . user_error! ( "Missing #{ setting } for #{ configuration } in #{ expanded_path } " )
108+ end
109+
110+ [ key , assignments . last ]
111+ end
112+ rescue SystemCallError => error
113+ UI . user_error! ( "Could not read app configuration file at #{ expanded_path } : #{ error . message } " )
114+ end
115+
116+ def verify_function_api_base_url_policy ( function_api_base_url )
117+ require "uri"
118+
119+ uri = URI . parse ( function_api_base_url )
120+ is_valid = uri . is_a? ( URI ::HTTPS ) &&
121+ !uri . host . to_s . empty? &&
122+ uri . path == SHARED_FUNCTION_API_PATH &&
123+ uri . query . nil? &&
124+ uri . fragment . nil?
125+
126+ UI . user_error! ( "Invalid FUNCTION_API_BASE_URL for store build" ) if !is_valid
127+ rescue URI ::InvalidURIError
128+ UI . user_error! ( "Invalid FUNCTION_API_BASE_URL for store build" )
129+ end
130+
57131default_platform ( :ios )
58132
59133platform :ios do
@@ -105,12 +179,12 @@ platform :ios do
105179 when TESTFLIGHT_CONFIGURATION
106180 {
107181 database_id : TESTFLIGHT_DATABASE_ID ,
108- function_api_base_url : TESTFLIGHT_FUNCTION_API_BASE_URL
182+ function_api_base_url : function_api_base_url_from_xcconfig ( TESTFLIGHT_CONFIGURATION )
109183 }
110184 when APPSTORE_CONFIGURATION
111185 {
112186 database_id : APPSTORE_DATABASE_ID ,
113- function_api_base_url : APPSTORE_FUNCTION_API_BASE_URL
187+ function_api_base_url : function_api_base_url_from_xcconfig ( APPSTORE_CONFIGURATION )
114188 }
115189 else
116190 UI . user_error! ( "Unsupported store configuration: #{ configuration } " )
@@ -119,6 +193,8 @@ platform :ios do
119193 UI . user_error! ( "Missing Firestore database ID for #{ configuration } " ) if database_id . empty?
120194 UI . user_error! ( "Missing Function API base URL for #{ configuration } " ) if function_api_base_url . empty?
121195
196+ verify_function_api_base_url_policy ( function_api_base_url )
197+
122198 if database_id != expected_configuration [ :database_id ]
123199 UI . user_error! (
124200 "Invalid store configuration: #{ configuration } must use FIRESTORE_DATABASE_ID=#{ expected_configuration [ :database_id ] } , got #{ database_id } "
@@ -127,11 +203,11 @@ platform :ios do
127203
128204 if function_api_base_url != expected_configuration [ :function_api_base_url ]
129205 UI . user_error! (
130- "Invalid store configuration: #{ configuration } must use FUNCTION_API_BASE_URL= #{ expected_configuration [ :function_api_base_url ] } , got #{ function_api_base_url } "
206+ "Invalid store configuration: #{ configuration } uses an unexpected FUNCTION_API_BASE_URL "
131207 )
132208 end
133209
134- UI . message ( "Verified store configuration: #{ configuration } /#{ database_id } / #{ function_api_base_url } " )
210+ UI . message ( "Verified store configuration: #{ configuration } /#{ database_id } " )
135211 end
136212
137213 private_lane :verify_store_info_plist do |options |
@@ -205,7 +281,8 @@ platform :ios do
205281 path : plist_path ,
206282 source : "app Info.plist" ,
207283 key : "FUNCTION_API_BASE_URL" ,
208- expected : expected_configuration [ :function_api_base_url ]
284+ expected : expected_configuration [ :function_api_base_url ] ,
285+ redact_values : true
209286 )
210287
211288 verify_plist_value (
@@ -235,15 +312,11 @@ platform :ios do
235312 )
236313
237314 if !expected_firebase_project_id . empty? && actual_firebase_project_id != expected_firebase_project_id
238- UI . user_error! (
239- "Unexpected PROJECT_ID in GoogleService-Info.plist: expected #{ expected_firebase_project_id } , got #{ actual_firebase_project_id } "
240- )
315+ UI . user_error! ( "Unexpected PROJECT_ID in GoogleService-Info.plist" )
241316 end
242317
243318 if !expected_google_app_id . empty? && actual_google_app_id != expected_google_app_id
244- UI . user_error! (
245- "Unexpected GOOGLE_APP_ID in GoogleService-Info.plist: expected #{ expected_google_app_id } , got #{ actual_google_app_id } "
246- )
319+ UI . user_error! ( "Unexpected GOOGLE_APP_ID in GoogleService-Info.plist" )
247320 end
248321
249322 UI . message ( "Verified PROJECT_ID in GoogleService-Info.plist" )
@@ -283,9 +356,11 @@ platform :ios do
283356 UI . user_error! ( "Missing expected #{ options [ :key ] } " ) if expected_value . empty?
284357
285358 if actual_value != expected_value
286- UI . user_error! (
287- "Unexpected #{ options [ :key ] } in #{ options [ :source ] } : expected #{ expected_value } , got #{ actual_value } "
288- )
359+ message = "Unexpected #{ options [ :key ] } in #{ options [ :source ] } "
360+ if options [ :redact_values ] != true
361+ message += ": expected #{ expected_value } , got #{ actual_value } "
362+ end
363+ UI . user_error! ( message )
289364 end
290365
291366 UI . message ( "Verified #{ options [ :key ] } in #{ options [ :source ] } " )
@@ -297,7 +372,7 @@ platform :ios do
297372 expected_database_id = options [ :database_id ] ||
298373 ( configuration == APPSTORE_CONFIGURATION ? APPSTORE_DATABASE_ID : TESTFLIGHT_DATABASE_ID )
299374 expected_function_api_base_url = options [ :function_api_base_url ] ||
300- ( configuration == APPSTORE_CONFIGURATION ? APPSTORE_FUNCTION_API_BASE_URL : TESTFLIGHT_FUNCTION_API_BASE_URL )
375+ function_api_base_url_from_xcconfig ( configuration )
301376 expected_app_environment = configuration == APPSTORE_CONFIGURATION ?
302377 APPSTORE_APP_ENVIRONMENT : TESTFLIGHT_APP_ENVIRONMENT
303378
@@ -307,6 +382,8 @@ platform :ios do
307382 function_api_base_url : expected_function_api_base_url
308383 )
309384
385+ firebase_configuration = expected_firebase_configuration_from_xcconfig ( configuration )
386+
310387 if ENV [ "FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT" ] . to_s . strip . empty?
311388 ENV [ "FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT" ] = "30"
312389 end
@@ -398,8 +475,8 @@ platform :ios do
398475 bundle_id : APP_IDENTIFIER ,
399476 database_id : expected_database_id ,
400477 function_api_base_url : expected_function_api_base_url ,
401- firebase_project_id : options [ :firebase_project_id ] ,
402- google_app_id : options [ :google_app_id ]
478+ firebase_project_id : firebase_configuration [ :firebase_project_id ] ,
479+ google_app_id : firebase_configuration [ :google_app_id ]
403480 }
404481 )
405482
@@ -415,9 +492,7 @@ platform :ios do
415492 build_for_store (
416493 configuration : TESTFLIGHT_CONFIGURATION ,
417494 database_id : TESTFLIGHT_DATABASE_ID ,
418- function_api_base_url : TESTFLIGHT_FUNCTION_API_BASE_URL ,
419- firebase_project_id : TESTFLIGHT_FIREBASE_PROJECT_ID ,
420- google_app_id : TESTFLIGHT_GOOGLE_APP_ID
495+ function_api_base_url : function_api_base_url_from_xcconfig ( TESTFLIGHT_CONFIGURATION )
421496 )
422497
423498 upload_testflight_build
@@ -427,9 +502,7 @@ platform :ios do
427502 build_for_store (
428503 configuration : TESTFLIGHT_CONFIGURATION ,
429504 database_id : TESTFLIGHT_DATABASE_ID ,
430- function_api_base_url : TESTFLIGHT_FUNCTION_API_BASE_URL ,
431- firebase_project_id : TESTFLIGHT_FIREBASE_PROJECT_ID ,
432- google_app_id : TESTFLIGHT_GOOGLE_APP_ID
505+ function_api_base_url : function_api_base_url_from_xcconfig ( TESTFLIGHT_CONFIGURATION )
433506 )
434507 end
435508
@@ -438,7 +511,7 @@ platform :ios do
438511 configuration : APPSTORE_CONFIGURATION ,
439512 output_directory : APPSTORE_BUILD_OUTPUT_DIRECTORY ,
440513 database_id : APPSTORE_DATABASE_ID ,
441- function_api_base_url : APPSTORE_FUNCTION_API_BASE_URL
514+ function_api_base_url : function_api_base_url_from_xcconfig ( APPSTORE_CONFIGURATION )
442515 )
443516 end
444517
@@ -447,7 +520,7 @@ platform :ios do
447520 configuration : APPSTORE_CONFIGURATION ,
448521 output_directory : APPSTORE_BUILD_OUTPUT_DIRECTORY ,
449522 database_id : APPSTORE_DATABASE_ID ,
450- function_api_base_url : APPSTORE_FUNCTION_API_BASE_URL
523+ function_api_base_url : function_api_base_url_from_xcconfig ( APPSTORE_CONFIGURATION )
451524 )
452525
453526 upload_appstore_build
@@ -468,6 +541,7 @@ platform :ios do
468541
469542 lane :upload_testflight_build do
470543 api_key = asc_api_key
544+ firebase_configuration = expected_firebase_configuration_from_xcconfig ( TESTFLIGHT_CONFIGURATION )
471545 # lane_context는 같은 fastlane 실행 내에서만 유지되므로, 별도 CI step에서는 고정 ipa 경로를 사용한다.
472546 ipa_output_path = lane_context [ SharedValues ::IPA_OUTPUT_PATH ] . to_s
473547 ipa_output_path = TESTFLIGHT_IPA_OUTPUT_PATH if ipa_output_path . empty?
@@ -480,9 +554,9 @@ platform :ios do
480554 app_environment : TESTFLIGHT_APP_ENVIRONMENT ,
481555 bundle_id : APP_IDENTIFIER ,
482556 database_id : TESTFLIGHT_DATABASE_ID ,
483- function_api_base_url : TESTFLIGHT_FUNCTION_API_BASE_URL ,
484- firebase_project_id : TESTFLIGHT_FIREBASE_PROJECT_ID ,
485- google_app_id : TESTFLIGHT_GOOGLE_APP_ID
557+ function_api_base_url : function_api_base_url_from_xcconfig ( TESTFLIGHT_CONFIGURATION ) ,
558+ firebase_project_id : firebase_configuration [ :firebase_project_id ] ,
559+ google_app_id : firebase_configuration [ :google_app_id ]
486560 }
487561 )
488562
@@ -495,6 +569,7 @@ platform :ios do
495569
496570 lane :upload_appstore_build do
497571 api_key = asc_api_key
572+ firebase_configuration = expected_firebase_configuration_from_xcconfig ( APPSTORE_CONFIGURATION )
498573 # lane_context는 같은 fastlane 실행 내에서만 유지되므로, 별도 CI step에서는 고정 ipa 경로를 사용한다.
499574 ipa_output_path = lane_context [ SharedValues ::IPA_OUTPUT_PATH ] . to_s
500575 ipa_output_path = APPSTORE_IPA_OUTPUT_PATH if ipa_output_path . empty?
@@ -507,7 +582,9 @@ platform :ios do
507582 app_environment : APPSTORE_APP_ENVIRONMENT ,
508583 bundle_id : APP_IDENTIFIER ,
509584 database_id : APPSTORE_DATABASE_ID ,
510- function_api_base_url : APPSTORE_FUNCTION_API_BASE_URL
585+ function_api_base_url : function_api_base_url_from_xcconfig ( APPSTORE_CONFIGURATION ) ,
586+ firebase_project_id : firebase_configuration [ :firebase_project_id ] ,
587+ google_app_id : firebase_configuration [ :google_app_id ]
511588 }
512589 )
513590
0 commit comments