-
Notifications
You must be signed in to change notification settings - Fork 698
fix(fastlane): incorporated iOS related changes from template #2639
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
ddfbc32
7dc51d2
620b17d
f73c092
a01a152
527c6d4
233faaf
a780395
76c82c3
3747c61
670ad5e
26c6963
c19f38d
77271a7
af634c0
b510c8c
5d0be19
0ed4562
4168ec2
814d004
37548bc
f234577
234a839
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,13 @@ require_relative File.join(project_dir, 'fastlane-config', 'android_config') | |
| require_relative File.join(project_dir, 'fastlane-config', 'ios_config') | ||
| require_relative './config/config_helpers' | ||
|
|
||
| # Helper: reject empty-string option values so || fallbacks work correctly. | ||
| # GitHub Actions custom actions pass "" for unset optional parameters, | ||
| # but Ruby treats "" as truthy ("" || "fallback" returns "", not "fallback"). | ||
| def sanitize_options(options) | ||
| options.reject { |_, v| v.is_a?(String) && v.strip.empty? } | ||
| end | ||
|
|
||
| default_platform(:android) | ||
|
|
||
| platform :android do | ||
|
|
@@ -325,6 +332,7 @@ platform :ios do | |
| ############################# | ||
|
|
||
| private_lane :setup_ci_if_needed do |options| | ||
| options = sanitize_options(options) | ||
| unless ENV['CI'] | ||
| UI.message("🖥️ Running locally, skipping CI-specific setup.") | ||
| else | ||
|
|
@@ -336,6 +344,7 @@ platform :ios do | |
| end | ||
|
|
||
| private_lane :load_api_key do |options| | ||
| options = sanitize_options(options) | ||
| ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG | ||
|
|
||
| app_store_connect_api_key( | ||
|
|
@@ -347,6 +356,7 @@ platform :ios do | |
| end | ||
|
|
||
| private_lane :fetch_certificates_with_match do |options| | ||
| options = sanitize_options(options) | ||
| ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG | ||
| match( | ||
| type: options[:match_type] || ios_config[:match_type], | ||
|
|
@@ -361,6 +371,7 @@ platform :ios do | |
| end | ||
|
|
||
| private_lane :build_ios_project do |options| | ||
| options = sanitize_options(options) | ||
| ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG | ||
| app_identifier = options[:app_identifier] || ios_config[:app_identifier] | ||
| provisioning_profile_name = options[:provisioning_profile_name] || ios_config[:provisioning_profile_name] | ||
|
|
@@ -435,6 +446,13 @@ platform :ios do | |
| desc "Build Ios application" | ||
| lane :build_ios do |options| | ||
| ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG | ||
|
|
||
| # Install CocoaPods dependencies | ||
| cocoapods( | ||
| podfile: "cmp-ios/Podfile", | ||
| try_repo_update_on_error: true | ||
| ) | ||
|
|
||
| build_ios_app( | ||
| scheme: ios_config[:scheme], | ||
| workspace: ios_config[:workspace_path], | ||
|
|
@@ -447,6 +465,13 @@ platform :ios do | |
|
|
||
| desc "Build Signed Ios application" | ||
| lane :build_signed_ios do |options| | ||
| options = sanitize_options(options) | ||
| # Install CocoaPods dependencies | ||
| cocoapods( | ||
| podfile: "cmp-ios/Podfile", | ||
| try_repo_update_on_error: true | ||
| ) | ||
|
|
||
| setup_ci_if_needed | ||
| load_api_key(options) | ||
| fetch_certificates_with_match(options) | ||
|
|
@@ -493,6 +518,7 @@ platform :ios do | |
|
|
||
| desc "Upload iOS application to Firebase App Distribution" | ||
| lane :deploy_on_firebase do |options| | ||
| options = sanitize_options(options) | ||
| firebase_config = FastlaneConfig.get_firebase_config(:ios) | ||
| ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG | ||
|
|
||
|
|
@@ -517,30 +543,58 @@ platform :ios do | |
|
|
||
| desc "Upload beta build to TestFlight" | ||
| lane :beta do |options| | ||
| options = sanitize_options(options) | ||
| ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG | ||
| testflight_config = FastlaneConfig::IosConfig::TESTFLIGHT_CONFIG | ||
|
|
||
| # Install CocoaPods dependencies | ||
| cocoapods( | ||
| podfile: "cmp-ios/Podfile", | ||
| try_repo_update_on_error: true | ||
| ) | ||
|
|
||
| setup_ci_if_needed | ||
| load_api_key(options) | ||
| fetch_certificates_with_match( | ||
| options.merge(match_type: "appstore") | ||
| ) | ||
|
|
||
| # Get version from gradle (sanitized for App Store) | ||
| version = get_version_from_gradle(sanitize_for_appstore: true) | ||
| gradle_version = get_version_from_gradle(sanitize_for_appstore: true) | ||
|
|
||
| # Increment version number (sanitized for App Store) | ||
| increment_version_number( | ||
| xcodeproj: ios_config[:project_path], | ||
| version_number: options[:version_number] || version | ||
| ) | ||
|
|
||
| # Get latest TestFlight build number and increment | ||
| # Get latest TestFlight build number and version FIRST | ||
| # so we can ensure our version is always higher | ||
| latest_build_number = latest_testflight_build_number( | ||
| app_identifier: options[:app_identifier] || ios_config[:app_identifier], | ||
| api_key: Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY] | ||
| ) | ||
| latest_version = Actions.lane_context[SharedValues::LATEST_TESTFLIGHT_VERSION] | ||
|
|
||
| # Determine final version: must be higher than latest TestFlight version | ||
| version = options[:version_number] || gradle_version | ||
| if latest_version && !latest_version.to_s.empty? | ||
| begin | ||
| if Gem::Version.new(version) <= Gem::Version.new(latest_version) | ||
| # Bump: increment patch of latest TestFlight version | ||
| parts = latest_version.split('.') | ||
| parts[-1] = (parts[-1].to_i + 1).to_s | ||
| bumped = parts.join('.') | ||
| UI.important("⚠️ Gradle version #{version} <= TestFlight #{latest_version}, bumping to #{bumped}") | ||
| version = bumped | ||
| end | ||
| rescue ArgumentError => e | ||
| UI.error("Version comparison failed: #{e.message}, using gradle version #{version}") | ||
| end | ||
| end | ||
| UI.important("📱 Final App Store version: #{version}") | ||
|
|
||
| # Set version in Xcode project | ||
| increment_version_number( | ||
| xcodeproj: ios_config[:project_path], | ||
| version_number: version | ||
| ) | ||
|
|
||
| # Increment build number | ||
| increment_build_number( | ||
| xcodeproj: ios_config[:project_path], | ||
| build_number: latest_build_number + 1 | ||
|
|
@@ -557,11 +611,12 @@ platform :ios do | |
| releaseNotes = generateReleaseNote() | ||
|
|
||
| # Prepare localized build info with changelog | ||
| locale = ios_config[:primary_locale] | ||
| localized_build_info = { | ||
| "default" => { | ||
| whats_new: releaseNotes | ||
| }, | ||
| "en-US" => { | ||
| locale => { | ||
| whats_new: releaseNotes | ||
| } | ||
| } | ||
|
|
@@ -614,30 +669,57 @@ platform :ios do | |
|
|
||
| desc "Upload iOS Application to App Store" | ||
| lane :release do |options| | ||
| options = sanitize_options(options) | ||
| ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG | ||
| appstore_config = FastlaneConfig::IosConfig::APPSTORE_CONFIG | ||
|
|
||
| # Install CocoaPods dependencies | ||
| cocoapods( | ||
| podfile: "cmp-ios/Podfile", | ||
| try_repo_update_on_error: true | ||
| ) | ||
|
|
||
| setup_ci_if_needed | ||
| load_api_key(options) | ||
| fetch_certificates_with_match( | ||
| options.merge(match_type: "appstore") | ||
| ) | ||
|
|
||
| # Get version from gradle (sanitized for App Store) | ||
| version = get_version_from_gradle(sanitize_for_appstore: true) | ||
|
|
||
| # Increment version number (sanitized for App Store) | ||
| increment_version_number( | ||
| xcodeproj: ios_config[:project_path], | ||
| version_number: options[:version_number] || version | ||
| ) | ||
| gradle_version = get_version_from_gradle(sanitize_for_appstore: true) | ||
|
|
||
| # Get latest TestFlight build number and increment | ||
| # Get latest TestFlight build number and version FIRST | ||
| # so we can ensure our version is always higher | ||
| latest_build_number = latest_testflight_build_number( | ||
| app_identifier: options[:app_identifier] || ios_config[:app_identifier], | ||
| api_key: Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY] | ||
| ) | ||
| latest_version = Actions.lane_context[SharedValues::LATEST_TESTFLIGHT_VERSION] | ||
|
|
||
| # Determine final version: must be higher than latest TestFlight version | ||
| version = options[:version_number] || gradle_version | ||
| if latest_version && !latest_version.to_s.empty? | ||
| begin | ||
| if Gem::Version.new(version) <= Gem::Version.new(latest_version) | ||
| parts = latest_version.split('.') | ||
| parts[-1] = (parts[-1].to_i + 1).to_s | ||
| bumped = parts.join('.') | ||
| UI.important("⚠️ Gradle version #{version} <= TestFlight #{latest_version}, bumping to #{bumped}") | ||
| version = bumped | ||
| end | ||
| rescue ArgumentError => e | ||
| UI.error("Version comparison failed: #{e.message}, using gradle version #{version}") | ||
| end | ||
| end | ||
| UI.important("📱 Final App Store version: #{version}") | ||
|
|
||
| # Set version in Xcode project | ||
| increment_version_number( | ||
| xcodeproj: ios_config[:project_path], | ||
| version_number: version | ||
| ) | ||
|
|
||
| # Increment build number | ||
| increment_build_number( | ||
| xcodeproj: ios_config[:project_path], | ||
| build_number: latest_build_number + 1 | ||
|
|
@@ -664,14 +746,15 @@ platform :ios do | |
| releaseNotes = generateReleaseNote() | ||
|
|
||
| # Write release notes to metadata file (required when creating new versions) | ||
| release_notes_path = File.join(ios_config[:metadata_path], "en-US", "release_notes.txt") | ||
| locale = ios_config[:primary_locale] | ||
| release_notes_path = File.join(ios_config[:metadata_path], locale, "release_notes.txt") | ||
| FileUtils.mkdir_p(File.dirname(release_notes_path)) | ||
| File.write(release_notes_path, releaseNotes) | ||
| UI.message("📝 Wrote release notes to: #{release_notes_path}") | ||
|
|
||
| # Log what metadata will be uploaded | ||
| UI.important("📋 Metadata files that will be uploaded:") | ||
| metadata_files = Dir.glob(File.join(ios_config[:metadata_path], "en-US", "*.txt")) | ||
| metadata_files = Dir.glob(File.join(ios_config[:metadata_path], locale, "*.txt")) | ||
| if metadata_files.empty? | ||
| UI.error("⚠️ No metadata files found!") | ||
| else | ||
|
|
@@ -721,7 +804,10 @@ platform :ios do | |
| run_precheck_before_submit: appstore_config[:run_precheck_before_submit], | ||
|
|
||
| # Submission information (compliance, ads, etc.) | ||
| submission_information: appstore_config[:submission_information] | ||
| submission_information: appstore_config[:submission_information], | ||
|
|
||
| # Age rating declarations (lootBox, parentalControls, advertising, etc.) | ||
| app_rating_config_path: ios_config[:app_rating_config_path] | ||
|
Comment on lines
+809
to
+810
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify age-rating file existence and validation logic
echo "=== Searching for age_rating files ==="
fd -a 'age_rating.json' 2>/dev/null || echo "No age_rating.json found"
echo ""
echo "=== Searching for validation and configuration references ==="
rg -n -C3 'validate_config|required_files|age_rating_config_path|app_rating_config_path' fastlane-config/project_config.rb fastlane/FastFile 2>/dev/null || echo "Search may have failed - files might not exist"
echo ""
echo "=== Checking if fastlane/FastFile exists and examining lines 809-815 ==="
if [ -f fastlane/FastFile ]; then
echo "File exists. Reading context around lines 809-815:"
sed -n '800,820p' fastlane/FastFile
else
echo "fastlane/FastFile not found"
fi
echo ""
echo "=== Checking project_config validation ==="
if [ -f fastlane-config/project_config.rb ]; then
echo "File exists. Searching for validation methods:"
rg -n 'def.*valid|def.*required|def.*check' fastlane-config/project_config.rb
else
echo "fastlane-config/project_config.rb not found"
fiRepository: openMF/mifos-x-field-officer-app Length of output: 3426 🏁 Script executed: #!/bin/bash
# Check for any validation between validate_config call and deliver call
echo "=== Looking for validate_config call and subsequent validation ==="
rg -n -B5 -A15 'validate_config' fastlane/FastFile
echo ""
echo "=== Checking full validate_config implementation ==="
sed -n '262,290p' fastlane-config/project_config.rbRepository: openMF/mifos-x-field-officer-app Length of output: 1334 🏁 Script executed: #!/bin/bash
# Search for where validate_config is called and look for any other validations
echo "=== Searching for validate_config call in FastFile ==="
rg -n 'validate_config' fastlane/FastFile
echo ""
echo "=== Looking at the deploy lane structure around line 810 ==="
sed -n '750,815p' fastlane/FastFile
echo ""
echo "=== Check if app_rating_config_path is used anywhere else ==="
rg -n 'app_rating_config_path' fastlane/
</parameter>Repository: openMF/mifos-x-field-officer-app Length of output: 3650 Add pre-check for age-rating config file before Line 810 passes Suggested guard+ unless File.exist?(ios_config[:app_rating_config_path])
+ UI.user_error!("Missing age rating config: #{ios_config[:app_rating_config_path]}")
+ end
+
deliver(🤖 Prompt for AI Agents |
||
| ) | ||
|
|
||
| UI.success("✅ Successfully deployed to App Store!") | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| { | ||
| "alcoholTobaccoOrDrugUseOrReferences": "NONE", | ||
| "contests": "NONE", | ||
| "gamblingSimulated": "NONE", | ||
| "gunsOrOtherWeapons": "NONE", | ||
| "horrorOrFearThemes": "NONE", | ||
| "matureOrSuggestiveThemes": "NONE", | ||
| "medicalOrTreatmentInformation": "NONE", | ||
| "profanityOrCrudeHumor": "NONE", | ||
| "sexualContentGraphicAndNudity": "NONE", | ||
| "sexualContentOrNudity": "NONE", | ||
| "violenceCartoonOrFantasy": "NONE", | ||
| "violenceRealistic": "NONE", | ||
| "violenceRealisticProlongedGraphicOrSadistic": "NONE", | ||
| "advertising": false, | ||
| "ageAssurance": false, | ||
| "gambling": false, | ||
| "healthOrWellnessTopics": false, | ||
| "lootBox": false, | ||
| "messagingAndChat": false, | ||
| "parentalControls": false, | ||
| "unrestrictedWebAccess": false, | ||
| "userGeneratedContent": false, | ||
| "ageRatingOverrideV2": "NONE", | ||
| "koreaAgeRatingOverride": "NONE", | ||
| "kidsAgeBand": null, | ||
| "developerAgeRatingInfoUrl": null | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.