ann-flutter-flavor 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +271 -0
  5. data/Rakefile +6 -0
  6. data/ann-flutter-flavor.gemspec +25 -0
  7. data/lib/ann-flutter-flavor.rb +10 -0
  8. data/lib/ann_flutter_flavor/version.rb +3 -0
  9. data/lib/fastlane/plugin/ann_flutter_flavor/actions/ann_compile_build_action.rb +219 -0
  10. data/lib/fastlane/plugin/ann_flutter_flavor/actions/ann_download_from_app_store_action.rb +183 -0
  11. data/lib/fastlane/plugin/ann_flutter_flavor/actions/ann_download_from_play_store_action.rb +168 -0
  12. data/lib/fastlane/plugin/ann_flutter_flavor/actions/ann_emulators_action.rb +180 -0
  13. data/lib/fastlane/plugin/ann_flutter_flavor/actions/ann_run_tests_action.rb +128 -0
  14. data/lib/fastlane/plugin/ann_flutter_flavor/actions/ann_setup_action.rb +137 -0
  15. data/lib/fastlane/plugin/ann_flutter_flavor/actions/ann_upload_to_app_store_action.rb +280 -0
  16. data/lib/fastlane/plugin/ann_flutter_flavor/actions/ann_upload_to_firebase_action.rb +199 -0
  17. data/lib/fastlane/plugin/ann_flutter_flavor/actions/ann_upload_to_play_store_action.rb +248 -0
  18. data/lib/fastlane/plugin/ann_flutter_flavor/helper/lanes_android.rb +120 -0
  19. data/lib/fastlane/plugin/ann_flutter_flavor/helper/lanes_annai.rb +502 -0
  20. data/lib/fastlane/plugin/ann_flutter_flavor/helper/lanes_ios.rb +157 -0
  21. data/lib/fastlane/plugin/ann_flutter_flavor/helper/lanes_setup.rb +161 -0
  22. data/lib/fastlane/plugin/ann_flutter_flavor/helper/podspec_bridge.rb +153 -0
  23. data/lib/fastlane/plugin/ann_flutter_flavor/helper/test_integration.rb +346 -0
  24. data/lib/fastlane/plugin/ann_flutter_flavor/helper/utils_project_config.rb +96 -0
  25. data/lib/fastlane/plugin/ann_flutter_flavor/helper/utils_spec_loader.rb +363 -0
  26. data/lib/fastlane/plugin/ann_flutter_flavor/helper/utils_status.rb +115 -0
  27. data/lib/fastlane/plugin/ann_flutter_flavor.rb +23 -0
  28. data/plugin_manager.sh +140 -0
  29. metadata +112 -0
@@ -0,0 +1,183 @@
1
+ require 'fastlane/action'
2
+ require 'fastlane/plugin/ann_flutter_flavor/helper/lanes_annai'
3
+ require 'fastlane/plugin/ann_flutter_flavor/helper/utils_spec_loader'
4
+
5
+ module Fastlane
6
+ module Actions
7
+ # This class name dictates the action name 'ann_download_from_app_store'
8
+ class AnnaiDownloadFromAppStoreAction < Action
9
+
10
+ def self.run(params)
11
+ # Platform is hardcoded to :ios
12
+ platform = :ios
13
+ action_name = "ann_download_from_app_store"
14
+ requested_flavor = params[:flavor]
15
+ spec_file = params[:spec_file]
16
+
17
+ # 1. Initialize AnnaiLanes
18
+ annai_lanes = FastlaneFlutterFlavor::AnnaiLanes.new(
19
+ lane: self,
20
+ platform: platform, # Pass platform symbol directly
21
+ spec_file: spec_file # Pass optional spec_file
22
+ )
23
+
24
+ # Get the StatusManager and SpecLoader instances
25
+ status_manager = annai_lanes.instance_variable_get(:@statusManager)
26
+ spec_loader = annai_lanes.instance_variable_get(:@specLoader)
27
+
28
+ UI.user_error!("Internal Error: AnnaiLanes failed to initialize specLoader") unless spec_loader
29
+
30
+ # Flag to track if any flavor download failed
31
+ any_download_failed = false
32
+
33
+ # 2. Get the list of all flavors defined for the platform
34
+ flavors_hash = spec_loader.get_platform_flavors(platform)
35
+
36
+ if flavors_hash.empty?
37
+ UI.important("No flavors found for iOS in the Annai spec. Nothing to download")
38
+ annai_lanes.finalize
39
+ return
40
+ end
41
+
42
+ all_flavor_names = flavors_hash.keys
43
+
44
+ # 3. Determine which flavors to download
45
+ if requested_flavor && !requested_flavor.to_s.empty?
46
+ requested_flavors = requested_flavor.to_s.split(',').map(&:strip).reject(&:empty?)
47
+
48
+ # Validate all requested flavors
49
+ invalid_flavors = requested_flavors.reject { |f| all_flavor_names.include?(f) }
50
+
51
+ if invalid_flavors.any?
52
+ UI.user_error!("The following requested flavor(s) are not found in the Annai spec for IOS: #{invalid_flavors.join(', ')}. Available flavors: #{all_flavor_names.join(', ')}")
53
+ end
54
+
55
+ flavors_to_download = requested_flavors
56
+ else
57
+ # No specific flavor requested, download all of them
58
+ flavors_to_download = all_flavor_names
59
+ end
60
+
61
+ UI.header("Starting App Store download. Flavors to process: #{flavors_to_download.join(', ')}")
62
+
63
+ # 4. Loop through each flavor and download
64
+ flavors_to_download.each do |flavor_key|
65
+
66
+ flavor = flavor_key
67
+
68
+ # spec_loader.get_package_id handles the bundle_identifier
69
+ final_api_key_path = spec_loader.get_api_key_path(platform, flavor) || params[:api_key_path]
70
+ final_package_name = spec_loader.get_package_id(platform, flavor) || params[:package_name]
71
+
72
+ UI.message("⬇️ Downloading store content for **#{flavor}** flavor...")
73
+
74
+ begin
75
+ success = annai_lanes.download_from_store(
76
+ package_name: final_package_name,
77
+ api_key_path: final_api_key_path,
78
+ flavor: flavor,
79
+ platform: platform.to_s,
80
+ use_live_version: params[:use_live_version],
81
+ metadata_path: params[:metadata_path],
82
+ screenshots_path: params[:screenshots_path],
83
+ distribution_platform: params[:distribution_platform],
84
+ )
85
+
86
+ if success
87
+ UI.success("✅ Successfully downloaded app store content for flavor: #{flavor}")
88
+ else
89
+ # If download_from_store returns false (meaning it caught an error and logged it), flag the overall failure.
90
+ # We only raise/log here if the inner action didn't handle it, to ensure logging consistency.
91
+ if !status_manager.instance_variable_get(:@status).key?([flavor, "download_from_store", platform.to_s])
92
+ raise "Download failed for flavor '#{flavor}'"
93
+ else
94
+ raise "Download failed for flavor '#{flavor}' (error logged previously)"
95
+ end
96
+ end
97
+
98
+ rescue => e
99
+ # Failure handling: Log the error and continue
100
+ status_manager.logError(flavor, action_name, platform, e.message) unless status_manager.instance_variable_get(:@status).key?([flavor, "download_from_store", platform.to_s])
101
+
102
+ UI.error("❌ Failed to download content for flavor: #{flavor}. Continuing to next flavor...")
103
+ any_download_failed = true
104
+ # Continue to the next flavor
105
+ end
106
+ end
107
+
108
+ # 5. Final Reporting and Status Check
109
+ annai_lanes.finalize
110
+
111
+ if any_download_failed
112
+ UI.user_error!("Download failed for one or more flavors. See the Annai Fastlane summary above")
113
+ else
114
+ UI.success("🎉 Successfully downloaded App Store content for all #{flavors_to_download.count} requested flavor(s)!")
115
+ end
116
+ end
117
+
118
+ # ----------------------------------------------------
119
+ # Define Parameters
120
+ # ----------------------------------------------------
121
+ def self.available_options
122
+ [
123
+ FastlaneCore::ConfigItem.new(key: :flavor,
124
+ description: "The specific flavor(s) to download (comma-separated). If nil, all flavors are processed",
125
+ optional: true,
126
+ default_value: nil),
127
+
128
+ FastlaneCore::ConfigItem.new(key: :spec_file,
129
+ description: "Path to the annai spec configuration file (relative to flutter root). Defaults to standard discovery",
130
+ optional: true,
131
+ default_value: nil),
132
+
133
+ FastlaneCore::ConfigItem.new(key: :api_key_path,
134
+ description: "Path to the App Store Connect API Key JSON file (iOS). Optional if defined in the Annai spec",
135
+ optional: true,
136
+ default_value: nil),
137
+ FastlaneCore::ConfigItem.new(key: :package_name,
138
+ description: "The bundle identifier (iOS). Optional if defined in the Annai spec",
139
+ optional: true,
140
+ default_value: nil),
141
+
142
+ FastlaneCore::ConfigItem.new(key: :distribution_platform,
143
+ description: "The distribution platform (e.g., 'ios' or 'app_store_connect') for App Store uploads",
144
+ optional: true,
145
+ default_value: "ios"),
146
+
147
+ FastlaneCore::ConfigItem.new(key: :metadata_path,
148
+ description: "The path where localization metadata (e.g., descriptions, titles) should be downloaded",
149
+ optional: true,
150
+ default_value: nil),
151
+ FastlaneCore::ConfigItem.new(key: :screenshots_path,
152
+ description: "The path where screenshots should be downloaded (iOS only)",
153
+ optional: true,
154
+ default_value: nil),
155
+
156
+ FastlaneCore::ConfigItem.new(key: :use_live_version,
157
+ description: "If true, downloads the currently published live version. If false (default), downloads the editable version (iOS only)",
158
+ optional: true,
159
+ type: Boolean,
160
+ default_value: false),
161
+ ].compact
162
+ end
163
+
164
+ def self.description
165
+ "Downloads store metadata, screenshots, and other assets for one or all flavors from the Apple App Store"
166
+ end
167
+
168
+ def self.is_supported?(platform)
169
+ platform == :ios
170
+ end
171
+
172
+ def self.example_code
173
+ 'ann_download_from_app_store(
174
+ flavor: "production",
175
+ spec_file: "config/annai_config.yaml", # Optional custom spec path
176
+ api_key_path: "./api_keys/asc.json",
177
+ package_name: "com.example.app",
178
+ use_live_version: true
179
+ )'
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,168 @@
1
+ require 'fastlane/action'
2
+ require 'fastlane/plugin/ann_flutter_flavor/helper/lanes_annai'
3
+ require 'fastlane/plugin/ann_flutter_flavor/helper/utils_spec_loader'
4
+
5
+ module Fastlane
6
+ module Actions
7
+ # This class name dictates the action name 'ann_download_from_play_store'
8
+ class AnnaiDownloadFromPlayStoreAction < Action
9
+
10
+ def self.run(params)
11
+ # Platform is hardcoded to :android
12
+ platform = :android
13
+ action_name = "ann_download_from_play_store"
14
+ requested_flavor = params[:flavor]
15
+ spec_file = params[:spec_file]
16
+
17
+ # 1. Initialize AnnaiLanes
18
+ annai_lanes = FastlaneFlutterFlavor::AnnaiLanes.new(
19
+ lane: self,
20
+ platform: platform, # Pass platform symbol directly
21
+ spec_file: spec_file # Pass optional spec_file
22
+ )
23
+
24
+ # Get the StatusManager and SpecLoader instances
25
+ status_manager = annai_lanes.instance_variable_get(:@statusManager)
26
+ spec_loader = annai_lanes.instance_variable_get(:@specLoader)
27
+
28
+ UI.user_error!("Internal Error: AnnaiLanes failed to initialize specLoader") unless spec_loader
29
+
30
+ # Flag to track if any flavor download failed
31
+ any_download_failed = false
32
+
33
+ # 2. Get the list of all flavors defined for the platform
34
+ flavors_hash = spec_loader.get_platform_flavors(platform)
35
+
36
+ if flavors_hash.empty?
37
+ UI.important("No flavors found for Android in the Annai spec. Nothing to download")
38
+ annai_lanes.finalize
39
+ return
40
+ end
41
+
42
+ all_flavor_names = flavors_hash.keys
43
+
44
+ # 3. Determine which flavors to download
45
+ if requested_flavor && !requested_flavor.to_s.empty?
46
+ requested_flavors = requested_flavor.to_s.split(',').map(&:strip).reject(&:empty?)
47
+
48
+ # Validate all requested flavors
49
+ invalid_flavors = requested_flavors.reject { |f| all_flavor_names.include?(f) }
50
+
51
+ if invalid_flavors.any?
52
+ UI.user_error!("The following requested flavor(s) are not found in the Annai spec for Android: #{invalid_flavors.join(', ')}. Available flavors: #{all_flavor_names.join(', ')}")
53
+ end
54
+
55
+ flavors_to_download = requested_flavors
56
+ else
57
+ # No specific flavor requested, download all of them
58
+ flavors_to_download = all_flavor_names
59
+ end
60
+
61
+ UI.header("Starting Google Play download. Flavors to process: #{flavors_to_download.join(', ')}")
62
+
63
+ # 4. Loop through each flavor and download
64
+ flavors_to_download.each do |flavor_key|
65
+
66
+ flavor = flavor_key
67
+
68
+ # spec_loader.get_package_id handles the package_name
69
+ final_api_key_path = spec_loader.get_api_key_path(platform, flavor) || params[:api_key_path]
70
+ final_package_name = spec_loader.get_package_id(platform, flavor) || params[:package_name]
71
+
72
+ UI.message("⬇️ Downloading store content for **#{flavor}** flavor...")
73
+
74
+ begin
75
+ success = annai_lanes.download_from_store(
76
+ package_name: final_package_name,
77
+ api_key_path: final_api_key_path,
78
+ flavor: flavor,
79
+ platform: platform.to_s,
80
+ # Android only needs metadata path
81
+ metadata_path: params[:metadata_path],
82
+ screenshots_path: nil, # Omitted for Android download action
83
+ use_live_version: nil, # Omitted for Android download action
84
+ distribution_platform: nil # Omitted for Android download action
85
+ )
86
+
87
+ if success
88
+ UI.success("✅ Successfully downloaded store content for flavor: #{flavor}")
89
+ else
90
+ # If download_from_store returns false (meaning it caught an error and logged it), flag the overall failure.
91
+ # We only raise/log here if the inner action didn't handle it, to ensure logging consistency.
92
+ if !status_manager.instance_variable_get(:@status).key?([flavor, "download_from_store", platform.to_s])
93
+ raise "Download failed for flavor '#{flavor}'"
94
+ else
95
+ raise "Download failed for flavor '#{flavor}' (error logged previously)"
96
+ end
97
+ end
98
+
99
+ rescue => e
100
+ # Failure handling: Log the error and continue
101
+ status_manager.logError(flavor, action_name, platform, e.message) unless status_manager.instance_variable_get(:@status).key?([flavor, "download_from_store", platform.to_s])
102
+
103
+ UI.error("❌ Failed to download content for flavor: #{flavor}. Continuing to next flavor...")
104
+ any_download_failed = true
105
+ # Continue to the next flavor
106
+ end
107
+ end
108
+
109
+ # 5. Final Reporting and Status Check
110
+ annai_lanes.finalize
111
+
112
+ if any_download_failed
113
+ UI.user_error!("Download failed for one or more flavors. See the Annai Fastlane summary above")
114
+ else
115
+ UI.success("🎉 Successfully downloaded Google Play content for all #{flavors_to_download.count} requested flavor(s)!")
116
+ end
117
+ end
118
+
119
+ # ----------------------------------------------------
120
+ # Define Parameters
121
+ # ----------------------------------------------------
122
+ def self.available_options
123
+ [
124
+ FastlaneCore::ConfigItem.new(key: :flavor,
125
+ description: "The specific flavor(s) to download (comma-separated). If nil, all flavors are processed",
126
+ optional: true,
127
+ default_value: nil),
128
+
129
+ FastlaneCore::ConfigItem.new(key: :spec_file,
130
+ description: "Path to the annai spec configuration file (relative to flutter root). Defaults to standard discovery",
131
+ optional: true,
132
+ default_value: nil),
133
+
134
+ FastlaneCore::ConfigItem.new(key: :api_key_path,
135
+ description: "Path to the Google Service Account JSON file (Android). Optional if defined in the Annai spec",
136
+ optional: true,
137
+ default_value: nil),
138
+ FastlaneCore::ConfigItem.new(key: :package_name,
139
+ description: "The package name (Android). Optional if defined in the Annai spec",
140
+ optional: true,
141
+ default_value: nil),
142
+
143
+ FastlaneCore::ConfigItem.new(key: :metadata_path,
144
+ description: "The path where localization metadata (e.g., descriptions, titles) should be downloaded",
145
+ optional: true,
146
+ default_value: nil),
147
+ ].compact
148
+ end
149
+
150
+ def self.description
151
+ "Downloads store metadata for one or all flavors from the Google Play Store"
152
+ end
153
+
154
+ def self.is_supported?(platform)
155
+ platform == :android
156
+ end
157
+
158
+ def self.example_code
159
+ 'ann_download_from_play_store(
160
+ flavor: "production",
161
+ spec_file: "config/annai_config.yaml", # Optional custom spec path
162
+ api_key_path: "./api_keys/google_play_key.json",
163
+ package_name: "com.example.app",
164
+ )'
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,180 @@
1
+ require 'fastlane/action'
2
+ # Assuming these helpers are available in the Fastlane plugin structure
3
+ require 'fastlane/plugin/ann_flutter_flavor/helper/test_integration'
4
+ require 'fastlane/plugin/ann_flutter_flavor/helper/utils_status'
5
+ require 'fastlane/plugin/ann_flutter_flavor/helper/utils_project_config'
6
+
7
+
8
+ module Fastlane
9
+ module Actions
10
+ # ----------------------------------------------------
11
+ # AnnaiOpenEmulatorsAction
12
+ # ----------------------------------------------------
13
+ class AnnaiOpenEmulatorsAction < Action
14
+
15
+ def self.run(params)
16
+ platform = params[:platform]
17
+ UI.header("Starting Annai Open Emulators for #{platform}")
18
+
19
+ # 1. Determine the necessary paths and managers
20
+ root_folder = FastlaneFlutterFlavor::ProjectUtil.find_flutter_root
21
+
22
+ # Resolve the test spec file path
23
+ test_spec_file_input = params[:test_spec_file]
24
+ resolved_test_spec_path = FastlaneFlutterFlavor::ProjectUtil.find_annai_test_spec_path(test_spec_file_input)
25
+
26
+ # Status Manager is required by IntegrationTest's constructor, though not strictly needed for open/close
27
+ status_manager = FastlaneFlutterFlavor::StatusManager.new(lane: self)
28
+
29
+ # 2. Instantiate IntegrationTest helper
30
+ integration_test = FastlaneFlutterFlavor::IntegrationTest.new(
31
+ lane: self,
32
+ isAndroid: platform == :android,
33
+ isIos: platform == :ios,
34
+ test_spec_file: resolved_test_spec_path,
35
+ statusManager: status_manager,
36
+ root_folder: root_folder
37
+ )
38
+
39
+ # 3. Call the openEmulators method
40
+ integration_test.openEmulators(
41
+ options: {
42
+ emulator: params[:emulator],
43
+ wipe_data: params[:wipe_data],
44
+ dns_server: params[:dns_server]
45
+ }
46
+ )
47
+
48
+ UI.success("✅ Emulator(s) started successfully for #{platform}.")
49
+ end
50
+
51
+ # ----------------------------------------------------
52
+ # Define Parameters
53
+ # ----------------------------------------------------
54
+ def self.available_options
55
+ [
56
+ FastlaneCore::ConfigItem.new(key: :platform,
57
+ description: "The platform (:ios or :android)",
58
+ is_string: false,
59
+ verify_block: proc do |value|
60
+ UI.user_error!("Platform must be :ios or :android") unless [:ios, :android].include?(value)
61
+ end),
62
+ FastlaneCore::ConfigItem.new(key: :test_spec_file,
63
+ description: "Path to the annai test spec configuration file (relative to flutter root). Defaults to standard discovery",
64
+ optional: true,
65
+ default_value: nil),
66
+
67
+ FastlaneCore::ConfigItem.new(key: :emulator,
68
+ description: "The specific emulator/device name (as defined in the test spec) to open. If nil, all defined emulators for the platform will be opened.",
69
+ optional: true,
70
+ type: String,
71
+ default_value: nil),
72
+ FastlaneCore::ConfigItem.new(key: :wipe_data,
73
+ description: "If true, wipes the emulator data before opening.",
74
+ type: Boolean,
75
+ default_value: true),
76
+ FastlaneCore::ConfigItem.new(key: :dns_server,
77
+ description: "Optional DNS server address for Android emulators (e.g., 8.8.8.8). Ignored for iOS.",
78
+ optional: true,
79
+ type: String,
80
+ default_value: "")
81
+ ].compact
82
+ end
83
+
84
+ def self.description
85
+ "Opens the specified emulators or all emulators defined in the Annai test spec file."
86
+ end
87
+
88
+ def self.is_supported?(platform)
89
+ [:ios, :android].include?(platform)
90
+ end
91
+
92
+ def self.example_code
93
+ 'annai_open_emulators(
94
+ platform: :android,
95
+ emulator: "Pixel_5_API_30",
96
+ wipe_data: true,
97
+ dns_server: "8.8.8.8"
98
+ )'
99
+ end
100
+ end
101
+
102
+ # ----------------------------------------------------
103
+ # AnnaiCloseEmulatorsAction
104
+ # ----------------------------------------------------
105
+ class AnnaiCloseEmulatorsAction < Action
106
+
107
+ def self.run(params)
108
+ platform = params[:platform]
109
+ UI.header("Starting Annai Close Emulators for #{platform}")
110
+
111
+ # 1. Determine the necessary paths and managers
112
+ root_folder = FastlaneFlutterFlavor::ProjectUtil.find_flutter_root
113
+
114
+ # Resolve the test spec file path
115
+ test_spec_file_input = params[:test_spec_file]
116
+ resolved_test_spec_path = FastlaneFlutterFlavor::ProjectUtil.find_annai_test_spec_path(test_spec_file_input)
117
+
118
+ # Status Manager is required by IntegrationTest's constructor
119
+ status_manager = FastlaneFlutterFlavor::StatusManager.new(lane: self)
120
+
121
+ # 2. Instantiate IntegrationTest helper
122
+ integration_test = FastlaneFlutterFlavor::IntegrationTest.new(
123
+ lane: self,
124
+ isAndroid: platform == :android,
125
+ isIos: platform == :ios,
126
+ test_spec_file: resolved_test_spec_path,
127
+ statusManager: status_manager,
128
+ root_folder: root_folder
129
+ )
130
+
131
+ # 3. Call the closeEmulators method
132
+ integration_test.closeEmulators(
133
+ options: {
134
+ emulator: params[:emulator]
135
+ }
136
+ )
137
+
138
+ UI.success("✅ Emulator(s) shut down successfully for #{platform}.")
139
+ end
140
+
141
+ # ----------------------------------------------------
142
+ # Define Parameters
143
+ # ----------------------------------------------------
144
+ def self.available_options
145
+ [
146
+ FastlaneCore::ConfigItem.new(key: :platform,
147
+ description: "The platform (:ios or :android)",
148
+ is_string: false,
149
+ verify_block: proc do |value|
150
+ UI.user_error!("Platform must be :ios or :android") unless [:ios, :android].include?(value)
151
+ end),
152
+ FastlaneCore::ConfigItem.new(key: :test_spec_file,
153
+ description: "Path to the annai test spec configuration file (relative to flutter root). Defaults to standard discovery",
154
+ optional: true,
155
+ default_value: nil),
156
+ FastlaneCore::ConfigItem.new(key: :emulator,
157
+ description: "The specific emulator/device name (as defined in the test spec) to close. If nil, all defined emulators for the platform will be closed.",
158
+ optional: true,
159
+ type: String,
160
+ default_value: nil)
161
+ ].compact
162
+ end
163
+
164
+ def self.description
165
+ "Closes the specified emulators or all emulators defined in the Annai test spec file."
166
+ end
167
+
168
+ def self.is_supported?(platform)
169
+ [:ios, :android].include?(platform)
170
+ end
171
+
172
+ def self.example_code
173
+ 'annai_close_emulators(
174
+ platform: :android,
175
+ emulator: "Pixel_5_API_30"
176
+ )'
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,128 @@
1
+ require 'fastlane/action'
2
+
3
+ require 'fastlane/plugin/ann_flutter_flavor/helper/test_integration'
4
+ require 'fastlane/plugin/ann_flutter_flavor/helper/utils_status'
5
+ require 'fastlane/plugin/ann_flutter_flavor/helper/utils_project_config'
6
+
7
+
8
+ module Fastlane
9
+ module Actions
10
+ class AnnaiRunTestsAction < Action
11
+
12
+ def self.run(params)
13
+ platform = params[:platform]
14
+
15
+ # 1. Determine the necessary paths and managers
16
+ root_folder = FastlaneFlutterFlavor::ProjectUtil.find_flutter_root
17
+
18
+ # Resolve the test spec file path, respecting user input precedence:
19
+ # User Parameter > Environment Variable > Default Path
20
+ test_spec_file_input = params[:test_spec_file]
21
+ resolved_test_spec_path = FastlaneFlutterFlavor::ProjectUtil.find_annai_test_spec_path(test_spec_file_input)
22
+
23
+ # Status Manager is required by IntegrationTest's constructor, though not strictly needed for open/close
24
+ status_manager = FastlaneFlutterFlavor::StatusManager.new(lane: self)
25
+
26
+ # 2. Instantiate IntegrationTest directly
27
+ integration_test = FastlaneFlutterFlavor::IntegrationTest.new(
28
+ lane: self,
29
+ isAndroid: platform == :android,
30
+ isIos: platform == :ios,
31
+ test_spec_file: resolved_test_spec_path,
32
+ statusManager: status_manager,
33
+ root_folder: root_folder
34
+ )
35
+
36
+ UI.header("Running tests using IntegrationTest directly for #{platform}")
37
+
38
+ # 3. Call the runTests method
39
+ integration_test.runTests(
40
+ flavor: params[:flavor],
41
+ emulator: params[:emulator],
42
+ open_emulator: params[:open_emulator],
43
+ close_emulator: params[:close_emulator],
44
+ wipe_data: params[:wipe_data],
45
+ skip_screenshots: params[:skip_screenshots],
46
+ skip_sound_null_safety: params[:skip_sound_null_safety]
47
+ )
48
+
49
+ UI.success("✅ Test execution completed for #{platform}. Check logs for results.")
50
+
51
+ # 4. Finalize status reporting
52
+ status_manager.displayStatus
53
+ end
54
+
55
+ # ----------------------------------------------------
56
+ # Define Parameters
57
+ # ----------------------------------------------------
58
+ def self.available_options
59
+ [
60
+ FastlaneCore::ConfigItem.new(key: :platform,
61
+ description: "The platform (:ios or :android)",
62
+ is_string: false,
63
+ verify_block: proc do |value|
64
+ UI.user_error!("Platform must be :ios or :android") unless [:ios, :android].include?(value)
65
+ end),
66
+ FastlaneCore::ConfigItem.new(key: :test_spec_file,
67
+ description: "Path to the annai test spec configuration file (relative to flutter root). Defaults to standard discovery",
68
+ optional: true,
69
+ default_value: nil),
70
+
71
+ # --- Filtering Options (Matches IntegrationTest logic) ---
72
+ FastlaneCore::ConfigItem.new(key: :flavor,
73
+ description: "The flavor(s) to run tests against (comma-separated list, e.g., 'free,pro'). If nil, all flavors are tested",
74
+ optional: true,
75
+ type: String,
76
+ default_value: nil),
77
+ FastlaneCore::ConfigItem.new(key: :emulator,
78
+ description: "The specific emulator/device name (as defined in the test spec) to run tests on. If nil, tests run on all defined emulators for the given flavor(s)",
79
+ optional: true,
80
+ type: String,
81
+ default_value: nil),
82
+
83
+ # --- Emulator Control ---
84
+ FastlaneCore::ConfigItem.new(key: :open_emulator,
85
+ description: "If true, opens the target emulator before running tests",
86
+ type: Boolean,
87
+ default_value: true),
88
+ FastlaneCore::ConfigItem.new(key: :close_emulator,
89
+ description: "If true, closes the target emulator after running tests",
90
+ type: Boolean,
91
+ default_value: true),
92
+ FastlaneCore::ConfigItem.new(key: :wipe_data,
93
+ description: "If true, wipes the emulator data before opening",
94
+ type: Boolean,
95
+ default_value: true),
96
+
97
+ # --- Test Execution Control ---
98
+ FastlaneCore::ConfigItem.new(key: :skip_screenshots,
99
+ description: "If true, uses 'flutter test' instead of 'flutter drive' (skips screenshots). Overrides the spec file setting",
100
+ type: Boolean,
101
+ default_value: false),
102
+ FastlaneCore::ConfigItem.new(key: :skip_sound_null_safety,
103
+ description: "If true, adds --no-sound-null-safety flag to the flutter command",
104
+ type: Boolean,
105
+ default_value: false)
106
+ ].compact
107
+ end
108
+
109
+ def self.description
110
+ "Runs specified integration tests on target devices using configurations defined within the Annai test spec file"
111
+ end
112
+
113
+ def self.is_supported?(platform)
114
+ [:ios, :android].include?(platform)
115
+ end
116
+
117
+ def self.example_code
118
+ 'ann_run_tests(
119
+ platform: :android,
120
+ test_spec_file: "../config/my_custom_specs.yaml" # Custom path
121
+ emulator: "Pixel_5_API_30",
122
+ flavor: "development",
123
+ skip_screenshots: false
124
+ )'
125
+ end
126
+ end
127
+ end
128
+ end