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.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +271 -0
- data/Rakefile +6 -0
- data/ann-flutter-flavor.gemspec +25 -0
- data/lib/ann-flutter-flavor.rb +10 -0
- data/lib/ann_flutter_flavor/version.rb +3 -0
- data/lib/fastlane/plugin/ann_flutter_flavor/actions/ann_compile_build_action.rb +219 -0
- data/lib/fastlane/plugin/ann_flutter_flavor/actions/ann_download_from_app_store_action.rb +183 -0
- data/lib/fastlane/plugin/ann_flutter_flavor/actions/ann_download_from_play_store_action.rb +168 -0
- data/lib/fastlane/plugin/ann_flutter_flavor/actions/ann_emulators_action.rb +180 -0
- data/lib/fastlane/plugin/ann_flutter_flavor/actions/ann_run_tests_action.rb +128 -0
- data/lib/fastlane/plugin/ann_flutter_flavor/actions/ann_setup_action.rb +137 -0
- data/lib/fastlane/plugin/ann_flutter_flavor/actions/ann_upload_to_app_store_action.rb +280 -0
- data/lib/fastlane/plugin/ann_flutter_flavor/actions/ann_upload_to_firebase_action.rb +199 -0
- data/lib/fastlane/plugin/ann_flutter_flavor/actions/ann_upload_to_play_store_action.rb +248 -0
- data/lib/fastlane/plugin/ann_flutter_flavor/helper/lanes_android.rb +120 -0
- data/lib/fastlane/plugin/ann_flutter_flavor/helper/lanes_annai.rb +502 -0
- data/lib/fastlane/plugin/ann_flutter_flavor/helper/lanes_ios.rb +157 -0
- data/lib/fastlane/plugin/ann_flutter_flavor/helper/lanes_setup.rb +161 -0
- data/lib/fastlane/plugin/ann_flutter_flavor/helper/podspec_bridge.rb +153 -0
- data/lib/fastlane/plugin/ann_flutter_flavor/helper/test_integration.rb +346 -0
- data/lib/fastlane/plugin/ann_flutter_flavor/helper/utils_project_config.rb +96 -0
- data/lib/fastlane/plugin/ann_flutter_flavor/helper/utils_spec_loader.rb +363 -0
- data/lib/fastlane/plugin/ann_flutter_flavor/helper/utils_status.rb +115 -0
- data/lib/fastlane/plugin/ann_flutter_flavor.rb +23 -0
- data/plugin_manager.sh +140 -0
- 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
|