ann-flutter-flavor 0.1.6
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/README.md +283 -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 +102 -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 +113 -0
|
@@ -0,0 +1,219 @@
|
|
|
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
|
+
class AnnaiCompileBuildAction < Action
|
|
8
|
+
|
|
9
|
+
def self.run(params)
|
|
10
|
+
platform = params[:platform]
|
|
11
|
+
requested_flavor = params[:flavor]
|
|
12
|
+
requested_clean_build = params[:clean]
|
|
13
|
+
spec_file = params[:spec_file]
|
|
14
|
+
|
|
15
|
+
# 1. Initialize AnnaiLanes
|
|
16
|
+
annai_lanes = FastlaneFlutterFlavor::AnnaiLanes.new(
|
|
17
|
+
lane: self,
|
|
18
|
+
platform: platform, # Pass platform symbol directly
|
|
19
|
+
spec_file: spec_file # Pass optional spec_file
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
# Get the StatusManager instance for logging errors explicitly
|
|
23
|
+
status_manager = annai_lanes.instance_variable_get(:@statusManager)
|
|
24
|
+
|
|
25
|
+
spec_loader = annai_lanes.instance_variable_get(:@specLoader)
|
|
26
|
+
UI.user_error!("Internal Error: AnnaiLanes failed to initialize specLoader.") unless spec_loader
|
|
27
|
+
|
|
28
|
+
# Flag to track if any flavor compilation failed
|
|
29
|
+
any_build_failed = false
|
|
30
|
+
|
|
31
|
+
# 2. Perform clean build if requested
|
|
32
|
+
if requested_clean_build
|
|
33
|
+
UI.header("๐งน Cleaning builds as requested...")
|
|
34
|
+
begin
|
|
35
|
+
annai_lanes.clean_build
|
|
36
|
+
rescue => e
|
|
37
|
+
UI.error("Failed to perform clean build: #{e.message}. Attempting to proceed with compilation.")
|
|
38
|
+
status_manager.logError("All", "annai_clean_build", platform, "Clean build failed: #{e.message}")
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# 3. Get the list of all flavors defined for the platform
|
|
43
|
+
flavors_hash = spec_loader.get_platform_flavors(platform)
|
|
44
|
+
|
|
45
|
+
if flavors_hash.empty?
|
|
46
|
+
UI.important("No flavors found for platform '#{platform}' in the Annai spec. Nothing to build.")
|
|
47
|
+
annai_lanes.finalize
|
|
48
|
+
return
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
all_flavor_names = flavors_hash.keys
|
|
52
|
+
|
|
53
|
+
# 4. Determine which flavors to build
|
|
54
|
+
if requested_flavor && !requested_flavor.to_s.empty?
|
|
55
|
+
requested_flavors = requested_flavor.to_s.split(',').map(&:strip).reject(&:empty?)
|
|
56
|
+
|
|
57
|
+
# Validate all requested flavors
|
|
58
|
+
invalid_flavors = requested_flavors.reject { |f| all_flavor_names.include?(f) }
|
|
59
|
+
|
|
60
|
+
if invalid_flavors.any?
|
|
61
|
+
UI.user_error!("The following requested flavor(s) are not found in the Annai spec for platform '#{platform}': #{invalid_flavors.join(', ')}. Available flavors: #{all_flavor_names.join(', ')}")
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
flavors_to_build = requested_flavors
|
|
65
|
+
|
|
66
|
+
else
|
|
67
|
+
# No specific flavor requested, build all of them
|
|
68
|
+
flavors_to_build = all_flavor_names
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
UI.header("Starting build for #{platform}. Flavors to compile: #{flavors_to_build.join(', ')}")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# 5. Loop through each flavor and directly call compile_build on AnnaiLanes
|
|
75
|
+
flavors_to_build.each do |flavor_key|
|
|
76
|
+
|
|
77
|
+
flavor = flavor_key.to_s
|
|
78
|
+
|
|
79
|
+
# Use the new getter, which uses the internal spec data
|
|
80
|
+
flavor_main_file = spec_loader.get_main_file(platform, flavor)
|
|
81
|
+
|
|
82
|
+
# Use the main_file provided by the user as a fallback if the spec doesn't define one
|
|
83
|
+
final_main_file = flavor_main_file || params[:main_file]
|
|
84
|
+
|
|
85
|
+
if final_main_file.nil? || final_main_file.empty?
|
|
86
|
+
error_msg = "Could not determine 'main_file' for flavor '#{flavor}' on platform '#{platform}'. Check your Annai spec or provide a default 'main_file'."
|
|
87
|
+
UI.error("โ #{error_msg}")
|
|
88
|
+
status_manager.logError(flavor, "ann_compile_build_all", platform, error_msg)
|
|
89
|
+
any_build_failed = true
|
|
90
|
+
next
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
UI.message("โ๏ธ Compiling **#{flavor}** flavor using main file: #{final_main_file}...")
|
|
94
|
+
|
|
95
|
+
begin
|
|
96
|
+
# CRITICAL: Direct call to the AnnaiLanes helper method
|
|
97
|
+
success = annai_lanes.compile_build(
|
|
98
|
+
flavor: flavor,
|
|
99
|
+
sub_command: params[:sub_command],
|
|
100
|
+
build_config: params[:build_config],
|
|
101
|
+
main_file: final_main_file,
|
|
102
|
+
skip_sound_null_safety: params[:skip_sound_null_safety],
|
|
103
|
+
skip_code_sign: params[:skip_code_sign],
|
|
104
|
+
export_options_plist: params[:export_options_plist],
|
|
105
|
+
additional_parameter: params[:additional_parameter],
|
|
106
|
+
platform: platform.to_s # Ensure platform is passed as a string/symbol as expected by the helper
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
if success
|
|
110
|
+
UI.success("๐ Successfully compiled flavor: #{flavor}")
|
|
111
|
+
else
|
|
112
|
+
# If compile_build returns false (meaning it caught an error and logged it), flag the overall failure.
|
|
113
|
+
raise "Compilation failed for flavor '#{flavor}' (error logged previously)."
|
|
114
|
+
end
|
|
115
|
+
rescue => e
|
|
116
|
+
# Failure handling: Log the error using StatusManager (if not already logged by compile_build) and continue
|
|
117
|
+
if !status_manager.instance_variable_get(:@status).key?([flavor, "compile_build", platform.to_s])
|
|
118
|
+
status_manager.logError(flavor, "ann_compile_build_all", platform, e.message)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
UI.error("โ Failed to compile flavor: #{flavor}. Continuing to next flavor...")
|
|
122
|
+
any_build_failed = true
|
|
123
|
+
# Continue to the next flavor
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# 6. Final Reporting and Status Check
|
|
128
|
+
# Finalize calls statusManager.displayStatus to show the comprehensive report
|
|
129
|
+
annai_lanes.finalize
|
|
130
|
+
|
|
131
|
+
if any_build_failed
|
|
132
|
+
# If any build failed, we stop the lane with an error after the summary is displayed.
|
|
133
|
+
UI.user_error!("Compilation failed for one or more flavors. See the Annai Fastlane summary above.")
|
|
134
|
+
else
|
|
135
|
+
UI.success("โ
Successfully compiled all #{flavors_to_build.count} requested flavor(s) for #{platform}!")
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# ----------------------------------------------------
|
|
140
|
+
# Define Parameters
|
|
141
|
+
# ----------------------------------------------------
|
|
142
|
+
def self.available_options
|
|
143
|
+
[
|
|
144
|
+
FastlaneCore::ConfigItem.new(key: :platform,
|
|
145
|
+
description: "The platform (:ios, :android, or :web)",
|
|
146
|
+
is_string: false,
|
|
147
|
+
verify_block: proc do |value|
|
|
148
|
+
UI.user_error!("Platform must be :ios, :android, or :web") unless [:ios, :android, :web].include?(value)
|
|
149
|
+
end),
|
|
150
|
+
|
|
151
|
+
FastlaneCore::ConfigItem.new(key: :spec_file,
|
|
152
|
+
description: "Path to the annai spec configuration file (relative to flutter root). Defaults to standard discovery",
|
|
153
|
+
optional: true,
|
|
154
|
+
default_value: nil),
|
|
155
|
+
|
|
156
|
+
FastlaneCore::ConfigItem.new(key: :clean,
|
|
157
|
+
description: "If true, runs annai_clean_build before compiling all flavors",
|
|
158
|
+
type: Boolean,
|
|
159
|
+
default_value: false),
|
|
160
|
+
|
|
161
|
+
FastlaneCore::ConfigItem.new(key: :flavor,
|
|
162
|
+
description: "The specific flavor(s) to build (comma-separated). If nil, all flavors are built",
|
|
163
|
+
optional: true,
|
|
164
|
+
default_value: nil),
|
|
165
|
+
|
|
166
|
+
# All following options are passed directly to the single build action
|
|
167
|
+
FastlaneCore::ConfigItem.new(key: :sub_command,
|
|
168
|
+
description: "Flutter build subcommand (e.g., appbundle, ipa, web) to use for all flavors",
|
|
169
|
+
# Note: If platform is :web, this should typically be 'web' or similar.
|
|
170
|
+
default_value: "appbundle"),
|
|
171
|
+
FastlaneCore::ConfigItem.new(key: :build_config,
|
|
172
|
+
description: "Build configuration (e.g., debug, profile, release) to use for all flavors",
|
|
173
|
+
default_value: "release"),
|
|
174
|
+
FastlaneCore::ConfigItem.new(key: :main_file,
|
|
175
|
+
description: "Path to the main dart file (used as default for all flavors unless overridden in the Annai spec)",
|
|
176
|
+
default_value: "lib/main.dart"),
|
|
177
|
+
FastlaneCore::ConfigItem.new(key: :skip_sound_null_safety,
|
|
178
|
+
type: Boolean,
|
|
179
|
+
default_value: false),
|
|
180
|
+
FastlaneCore::ConfigItem.new(key: :skip_code_sign,
|
|
181
|
+
type: Boolean,
|
|
182
|
+
default_value: false),
|
|
183
|
+
FastlaneCore::ConfigItem.new(key: :export_options_plist,
|
|
184
|
+
description: "Path to export options plist (iOS only)",
|
|
185
|
+
optional: true,
|
|
186
|
+
default_value: ""),
|
|
187
|
+
FastlaneCore::ConfigItem.new(key: :additional_parameter,
|
|
188
|
+
description: "Additional flutter build parameters",
|
|
189
|
+
optional: true,
|
|
190
|
+
default_value: ""),
|
|
191
|
+
]
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def self.description
|
|
195
|
+
"Compiles all defined Flutter flavors for a single platform (:ios, :android, or :web) using the Annai spec configuration"
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def self.is_supported?(platform)
|
|
199
|
+
[:ios, :android, :web].include?(platform)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def self.example_code
|
|
203
|
+
'ann_compile_build_all(
|
|
204
|
+
platform: :android,
|
|
205
|
+
spec_file: "config/my_annai.yaml", # Optional custom spec path
|
|
206
|
+
build_config: "release",
|
|
207
|
+
clean: true,
|
|
208
|
+
flavor: "staging,production" # Optional comma-separated list
|
|
209
|
+
)
|
|
210
|
+
ann_compile_build_all(
|
|
211
|
+
platform: :web,
|
|
212
|
+
sub_command: "web", # Specify the web subcommand
|
|
213
|
+
build_config: "release",
|
|
214
|
+
flavor: "test_web"
|
|
215
|
+
)'
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
@@ -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
|