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,137 @@
|
|
|
1
|
+
require 'fastlane/action'
|
|
2
|
+
|
|
3
|
+
require 'fastlane/plugin/ann_flutter_flavor/helper/utils_status'
|
|
4
|
+
require 'fastlane/plugin/ann_flutter_flavor/helper/utils_project_config'
|
|
5
|
+
require 'fastlane/plugin/ann_flutter_flavor/helper/lanes_setup'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
module Fastlane
|
|
9
|
+
module Actions
|
|
10
|
+
# =========================================================================
|
|
11
|
+
# AnnaiUpgradeSetupAction: Runs comprehensive setup and maintenance
|
|
12
|
+
# =========================================================================
|
|
13
|
+
|
|
14
|
+
class AnnaiUpgradeSetupAction < Action
|
|
15
|
+
def self.run(params)
|
|
16
|
+
|
|
17
|
+
# 1. Determine the necessary paths and managers
|
|
18
|
+
root_folder = FastlaneFlutterFlavor::ProjectUtil.find_flutter_root
|
|
19
|
+
status_manager = FastlaneFlutterFlavor::StatusManager.new(lane: self)
|
|
20
|
+
|
|
21
|
+
# 2. Instantiate SetupLanes directly
|
|
22
|
+
setup_lanes = FastlaneFlutterFlavor::SetupLanes.new(
|
|
23
|
+
root_folder: root_folder,
|
|
24
|
+
status_manager: status_manager,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
Fastlane::UI.message("Starting comprehensive setup and maintenance (Force: #{params[:force]})...")
|
|
28
|
+
|
|
29
|
+
# 3. Execute the maintenance routine
|
|
30
|
+
setup_lanes.upgrade_setup(force: params[:force])
|
|
31
|
+
|
|
32
|
+
Fastlane::UI.success("✅ Upgrade setup completed successfully.")
|
|
33
|
+
|
|
34
|
+
# 4. Finalize status reporting
|
|
35
|
+
status_manager.displayStatus
|
|
36
|
+
rescue => e
|
|
37
|
+
# Log the error using the StatusManager instance
|
|
38
|
+
if status_manager
|
|
39
|
+
# Pass 'self' (the class) as the lane context for consistency
|
|
40
|
+
status_manager.logError(self, 'annai_upgrade_setup', nil, e)
|
|
41
|
+
status_manager.displayStatus
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
raise "AnnaiUpgradeSetupAction failed: #{e.message}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.available_options
|
|
48
|
+
[
|
|
49
|
+
FastlaneCore::ConfigItem.new(key: :force,
|
|
50
|
+
description: "Force the execution even if conditions might advise skipping",
|
|
51
|
+
optional: true,
|
|
52
|
+
is_string: false,
|
|
53
|
+
default_value: false,
|
|
54
|
+
type: Boolean)
|
|
55
|
+
]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.description
|
|
59
|
+
"Runs comprehensive Flutter and Fastlane maintenance (upgrade, doctor, bundle update, gem install)."
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.is_supported?(platform)
|
|
63
|
+
true
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.example_code
|
|
67
|
+
[
|
|
68
|
+
'annai_upgrade_setup',
|
|
69
|
+
'annai_upgrade_setup(force: true)'
|
|
70
|
+
]
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# =========================================================================
|
|
75
|
+
# AnnaiCleanupAction: Cleans up all local build artifacts
|
|
76
|
+
# =========================================================================
|
|
77
|
+
|
|
78
|
+
class AnnaiCleanupAction < Action
|
|
79
|
+
def self.run(params)
|
|
80
|
+
|
|
81
|
+
# 1. Determine the necessary paths and managers
|
|
82
|
+
root_folder = FastlaneFlutterFlavor::ProjectUtil.find_flutter_root
|
|
83
|
+
status_manager = FastlaneFlutterFlavor::StatusManager.new(lane: self)
|
|
84
|
+
|
|
85
|
+
# 2. Instantiate SetupLanes directly
|
|
86
|
+
setup_lanes = FastlaneFlutterFlavor::SetupLanes.new(
|
|
87
|
+
root_folder: root_folder,
|
|
88
|
+
status_manager: status_manager,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
Fastlane::UI.message("Starting full build artifact cleanup (Force: #{params[:force]})...")
|
|
92
|
+
|
|
93
|
+
# 3. Execute the cleanup routine
|
|
94
|
+
setup_lanes.cleanup(force: params[:force])
|
|
95
|
+
|
|
96
|
+
Fastlane::UI.success("✅ Build artifact cleanup completed successfully.")
|
|
97
|
+
|
|
98
|
+
# 4. Finalize status reporting
|
|
99
|
+
status_manager.displayStatus
|
|
100
|
+
rescue => e
|
|
101
|
+
# Log the error using the StatusManager instance
|
|
102
|
+
if status_manager
|
|
103
|
+
# Pass 'self' (the class) as the lane context for consistency
|
|
104
|
+
status_manager.logError(self, 'annai_cleanup', nil, e)
|
|
105
|
+
status_manager.displayStatus
|
|
106
|
+
end
|
|
107
|
+
raise "AnnaiCleanupAction failed: #{e.message}"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def self.available_options
|
|
111
|
+
[
|
|
112
|
+
FastlaneCore::ConfigItem.new(key: :force,
|
|
113
|
+
description: "Force the execution even if conditions might advise skipping",
|
|
114
|
+
optional: true,
|
|
115
|
+
is_string: false,
|
|
116
|
+
default_value: false,
|
|
117
|
+
type: Boolean)
|
|
118
|
+
]
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def self.description
|
|
122
|
+
"Performs full build artifact cleanup (runs 'flutter clean' and deletes platform build folders)."
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def self.is_supported?(platform)
|
|
126
|
+
true
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def self.example_code
|
|
130
|
+
[
|
|
131
|
+
'annai_cleanup',
|
|
132
|
+
'annai_cleanup(force: true)',
|
|
133
|
+
]
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
@@ -0,0 +1,280 @@
|
|
|
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_upload_to_app_store'
|
|
8
|
+
class AnnaiUploadToAppStoreAction < Action
|
|
9
|
+
|
|
10
|
+
def self.run(params)
|
|
11
|
+
platform = :ios # Hardcoded platform
|
|
12
|
+
action_name = "ann_upload_to_app_store"
|
|
13
|
+
requested_flavor = params[:flavor]
|
|
14
|
+
requested_clean_build = params[:clean]
|
|
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
|
+
status_manager = annai_lanes.instance_variable_get(:@statusManager)
|
|
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
|
+
any_upload_failed = false
|
|
29
|
+
|
|
30
|
+
# 2. Perform clean build if requested
|
|
31
|
+
if requested_clean_build
|
|
32
|
+
UI.header("🧹 Cleaning builds as requested...")
|
|
33
|
+
begin
|
|
34
|
+
annai_lanes.clean_build
|
|
35
|
+
rescue => e
|
|
36
|
+
UI.error("Failed to perform clean build: #{e.message}. Attempting to proceed with upload")
|
|
37
|
+
# Note: Using 'annai_clean_build' as the action name for the failure log
|
|
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 iOS in the Annai spec. Nothing to upload")
|
|
47
|
+
annai_lanes.finalize
|
|
48
|
+
return
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
all_flavor_names = flavors_hash.keys
|
|
52
|
+
|
|
53
|
+
# 4. Determine which flavors to upload
|
|
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 IOS: #{invalid_flavors.join(', ')}. Available flavors: #{all_flavor_names.join(', ')}")
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
flavors_to_upload = requested_flavors
|
|
65
|
+
else
|
|
66
|
+
flavors_to_upload = all_flavor_names
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
UI.header("Starting App Store upload. Flavors to process: #{flavors_to_upload.join(', ')}")
|
|
70
|
+
|
|
71
|
+
# 5. Loop through each flavor and upload
|
|
72
|
+
flavors_to_upload.each do |flavor_key|
|
|
73
|
+
|
|
74
|
+
flavor = flavor_key.to_s
|
|
75
|
+
|
|
76
|
+
# Get values, prioritizing spec over parameters
|
|
77
|
+
final_main_file = spec_loader.get_main_file(platform, flavor) || params[:main_file]
|
|
78
|
+
final_api_key_path = spec_loader.get_api_key_path(platform, flavor) || params[:api_key_path]
|
|
79
|
+
final_package_name = spec_loader.get_package_id(platform, flavor) || params[:bundle_identifier]
|
|
80
|
+
final_export_option_file = spec_loader.get_export_option_file(platform, flavor) || params[:export_option_file]
|
|
81
|
+
final_display_name = spec_loader.get_display_name(platform, flavor) || params[:display_name]
|
|
82
|
+
final_team_id = spec_loader.get_team_id(platform, flavor) || params[:team_id]
|
|
83
|
+
final_app_version = spec_loader.get_version_name(platform, flavor) || params[:app_version]
|
|
84
|
+
final_build_number = spec_loader.get_version_code(platform, flavor) || params[:build_number]
|
|
85
|
+
|
|
86
|
+
UI.message("🚀 Preparing **#{flavor}** flavor...")
|
|
87
|
+
|
|
88
|
+
# Use 'upload_to_store' as the consistent action name for flavor-specific failures
|
|
89
|
+
flavor_action_name = "upload_to_store"
|
|
90
|
+
|
|
91
|
+
begin
|
|
92
|
+
# iOS Upload Logic (upload_to_app_store)
|
|
93
|
+
success = annai_lanes.upload_to_app_store(
|
|
94
|
+
bundle_identifier: final_package_name, # Use package name from spec for bundle ID
|
|
95
|
+
api_key_path: final_api_key_path,
|
|
96
|
+
flavor: flavor,
|
|
97
|
+
main_file: final_main_file,
|
|
98
|
+
display_name: final_display_name,
|
|
99
|
+
export_option_file: final_export_option_file,
|
|
100
|
+
team_id: final_team_id,
|
|
101
|
+
signing_certificate: params[:signing_certificate],
|
|
102
|
+
app_version: final_app_version,
|
|
103
|
+
build_number: final_build_number,
|
|
104
|
+
skip_sound_null_safety: params[:skip_sound_null_safety],
|
|
105
|
+
skip_compile: params[:skip_compile],
|
|
106
|
+
skip_upload_binary: params[:skip_upload_binary],
|
|
107
|
+
skip_upload_prod: params[:skip_upload_prod],
|
|
108
|
+
skip_upload_metadata: params[:skip_upload_metadata],
|
|
109
|
+
skip_upload_screenshots: params[:skip_upload_screenshots],
|
|
110
|
+
export_compliance_uses_encryption: params[:export_compliance_uses_encryption],
|
|
111
|
+
add_id_info_uses_idfa: params[:add_id_info_uses_idfa],
|
|
112
|
+
platform: platform.to_s,
|
|
113
|
+
metadata_path: params[:metadata_path],
|
|
114
|
+
screenshots_path: params[:screenshots_path],
|
|
115
|
+
distribution_platform: params[:distribution_platform],
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
if success
|
|
119
|
+
UI.success("✅ Successfully uploaded flavor: #{flavor}")
|
|
120
|
+
else
|
|
121
|
+
# If upload_to_app_store returns false (meaning it caught an error and logged it), flag the overall failure.
|
|
122
|
+
# We only raise/log here if the inner action didn't handle it, to ensure logging consistency.
|
|
123
|
+
if !status_manager.instance_variable_get(:@status).key?([flavor, flavor_action_name, platform.to_s])
|
|
124
|
+
raise "Upload failed for flavor '#{flavor}'"
|
|
125
|
+
else
|
|
126
|
+
raise "Upload failed for flavor '#{flavor}' (error logged previously)"
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
rescue => e
|
|
131
|
+
# Failure handling: Log the error and continue
|
|
132
|
+
if !status_manager.instance_variable_get(:@status).key?([flavor, flavor_action_name, platform.to_s])
|
|
133
|
+
status_manager.logError(flavor, flavor_action_name, platform, e.message)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
UI.error("❌ Failed to upload flavor: #{flavor}. Continuing to next flavor...")
|
|
137
|
+
any_upload_failed = true
|
|
138
|
+
# Continue to the next flavor
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# 6. Final Reporting and Status Check
|
|
143
|
+
annai_lanes.finalize
|
|
144
|
+
|
|
145
|
+
if any_upload_failed
|
|
146
|
+
UI.user_error!("Upload failed for one or more flavors. See the Annai Fastlane summary above")
|
|
147
|
+
else
|
|
148
|
+
UI.success("🎉 Successfully uploaded all #{flavors_to_upload.count} requested flavor(s) to the App Store!")
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# ----------------------------------------------------
|
|
153
|
+
# Define Parameters
|
|
154
|
+
# ----------------------------------------------------
|
|
155
|
+
def self.available_options
|
|
156
|
+
[
|
|
157
|
+
FastlaneCore::ConfigItem.new(key: :flavor,
|
|
158
|
+
description: "The specific flavor(s) to upload (comma-separated). If nil, all flavors are uploaded",
|
|
159
|
+
optional: true,
|
|
160
|
+
default_value: nil),
|
|
161
|
+
|
|
162
|
+
FastlaneCore::ConfigItem.new(key: :spec_file,
|
|
163
|
+
description: "Path to the annai spec configuration file (relative to flutter root). Defaults to standard discovery",
|
|
164
|
+
optional: true,
|
|
165
|
+
default_value: nil),
|
|
166
|
+
|
|
167
|
+
FastlaneCore::ConfigItem.new(key: :clean,
|
|
168
|
+
description: "If true, runs annai_clean_build before uploading (and compiling if not skipped)",
|
|
169
|
+
type: Boolean,
|
|
170
|
+
default_value: false),
|
|
171
|
+
|
|
172
|
+
FastlaneCore::ConfigItem.new(key: :distribution_platform,
|
|
173
|
+
description: "The distribution platform (e.g., 'ios' or 'app_store_connect') for App Store uploads",
|
|
174
|
+
optional: true,
|
|
175
|
+
default_value: "ios"),
|
|
176
|
+
|
|
177
|
+
# --- Common & iOS Specific Parameters ---
|
|
178
|
+
FastlaneCore::ConfigItem.new(key: :api_key_path,
|
|
179
|
+
description: "Path to the App Store Connect API Key JSON file (iOS)",
|
|
180
|
+
optional: true,
|
|
181
|
+
type: String),
|
|
182
|
+
FastlaneCore::ConfigItem.new(key: :main_file,
|
|
183
|
+
description: "Path to the main dart file (used if compilation is required)",
|
|
184
|
+
default_value: "lib/main.dart"),
|
|
185
|
+
FastlaneCore::ConfigItem.new(key: :skip_compile,
|
|
186
|
+
description: "If true, skips the compilation step and assumes artifacts already exist",
|
|
187
|
+
type: Boolean,
|
|
188
|
+
default_value: false),
|
|
189
|
+
FastlaneCore::ConfigItem.new(key: :skip_sound_null_safety,
|
|
190
|
+
description: "Skips sound null safety checks during compilation (if skip_compile is false)",
|
|
191
|
+
type: Boolean,
|
|
192
|
+
default_value: false),
|
|
193
|
+
FastlaneCore::ConfigItem.new(key: :skip_upload_prod,
|
|
194
|
+
description: "If true, skips uploading to the production track/stage",
|
|
195
|
+
type: Boolean,
|
|
196
|
+
default_value: false),
|
|
197
|
+
FastlaneCore::ConfigItem.new(key: :skip_upload_binary,
|
|
198
|
+
description: "If true, skips uploading the binary (.ipa)",
|
|
199
|
+
type: Boolean,
|
|
200
|
+
default_value: false),
|
|
201
|
+
FastlaneCore::ConfigItem.new(key: :skip_upload_metadata,
|
|
202
|
+
description: "If true, skips uploading store metadata (description, title, etc.)",
|
|
203
|
+
type: Boolean,
|
|
204
|
+
default_value: false),
|
|
205
|
+
FastlaneCore::ConfigItem.new(key: :skip_upload_screenshots,
|
|
206
|
+
description: "If true, skips uploading screenshots and app previews",
|
|
207
|
+
type: Boolean,
|
|
208
|
+
default_value: false),
|
|
209
|
+
FastlaneCore::ConfigItem.new(key: :metadata_path,
|
|
210
|
+
description: "The path to the localized store metadata (e.g., fastlane/metadata/ios)",
|
|
211
|
+
optional: true,
|
|
212
|
+
default_value: nil),
|
|
213
|
+
FastlaneCore::ConfigItem.new(key: :screenshots_path,
|
|
214
|
+
description: "The path to the screenshots and app previews",
|
|
215
|
+
optional: true,
|
|
216
|
+
default_value: nil),
|
|
217
|
+
|
|
218
|
+
FastlaneCore::ConfigItem.new(key: :bundle_identifier,
|
|
219
|
+
description: "The bundle identifier (iOS)",
|
|
220
|
+
optional: true,
|
|
221
|
+
default_value: nil),
|
|
222
|
+
FastlaneCore::ConfigItem.new(key: :display_name,
|
|
223
|
+
description: "The display name of the app (used for IPA filename)",
|
|
224
|
+
optional: true,
|
|
225
|
+
default_value: "App"),
|
|
226
|
+
FastlaneCore::ConfigItem.new(key: :export_option_file,
|
|
227
|
+
description: "Path to export options plist",
|
|
228
|
+
optional: true,
|
|
229
|
+
default_value: ""),
|
|
230
|
+
FastlaneCore::ConfigItem.new(key: :team_id,
|
|
231
|
+
description: "Apple Developer Team ID",
|
|
232
|
+
optional: true,
|
|
233
|
+
default_value: nil),
|
|
234
|
+
FastlaneCore::ConfigItem.new(key: :signing_certificate,
|
|
235
|
+
description: "Code signing certificate name",
|
|
236
|
+
optional: true,
|
|
237
|
+
default_value: "iPhone Distribution"),
|
|
238
|
+
FastlaneCore::ConfigItem.new(key: :app_version,
|
|
239
|
+
description: "The app version number",
|
|
240
|
+
optional: true,
|
|
241
|
+
default_value: nil),
|
|
242
|
+
FastlaneCore::ConfigItem.new(key: :build_number,
|
|
243
|
+
description: "The build number",
|
|
244
|
+
optional: true,
|
|
245
|
+
default_value: nil),
|
|
246
|
+
FastlaneCore::ConfigItem.new(key: :export_compliance_uses_encryption,
|
|
247
|
+
description: "Whether the app uses encryption",
|
|
248
|
+
optional: true,
|
|
249
|
+
type: Boolean,
|
|
250
|
+
default_value: nil),
|
|
251
|
+
FastlaneCore::ConfigItem.new(key: :add_id_info_uses_idfa,
|
|
252
|
+
description: "Whether the app uses IDFA",
|
|
253
|
+
optional: true,
|
|
254
|
+
type: Boolean,
|
|
255
|
+
default_value: nil),
|
|
256
|
+
].compact
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def self.description
|
|
260
|
+
"Handles compilation (optional) and uploading of one or all flavors to the Apple App Store"
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def self.is_supported?(platform)
|
|
264
|
+
platform == :ios
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def self.example_code
|
|
268
|
+
'ann_upload_to_app_store(
|
|
269
|
+
flavor: "production",
|
|
270
|
+
spec_file: "config/annai_config.yaml", # Optional custom spec path
|
|
271
|
+
api_key_path: "./api_keys/asc.json",
|
|
272
|
+
bundle_identifier: "com.example.app",
|
|
273
|
+
team_id: "ABCDEFG",
|
|
274
|
+
clean: true,
|
|
275
|
+
skip_compile: false
|
|
276
|
+
)'
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
end
|
|
@@ -0,0 +1,199 @@
|
|
|
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_upload_to_firebase'
|
|
8
|
+
class AnnaiUploadToFirebaseAction < Action
|
|
9
|
+
|
|
10
|
+
def self.run(params)
|
|
11
|
+
platform = :web # Hardcoded platform for web hosting
|
|
12
|
+
action_name = "ann_upload_to_firebase"
|
|
13
|
+
requested_flavor = params[:flavor]
|
|
14
|
+
requested_clean_build = params[:clean]
|
|
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
|
+
status_manager = annai_lanes.instance_variable_get(:@statusManager)
|
|
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
|
+
any_upload_failed = false
|
|
29
|
+
|
|
30
|
+
# 2. Perform clean build if requested
|
|
31
|
+
if requested_clean_build
|
|
32
|
+
UI.header("🧹 Cleaning builds as requested...")
|
|
33
|
+
begin
|
|
34
|
+
annai_lanes.clean_build
|
|
35
|
+
rescue => e
|
|
36
|
+
UI.error("Failed to perform clean build: #{e.message}. Attempting to proceed with upload")
|
|
37
|
+
# Note: Using 'annai_clean_build' as the action name for the failure log
|
|
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 Web in the Annai spec. Nothing to upload")
|
|
47
|
+
annai_lanes.finalize
|
|
48
|
+
return
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
all_flavor_names = flavors_hash.keys
|
|
52
|
+
|
|
53
|
+
# 4. Determine which flavors to upload
|
|
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 Web: #{invalid_flavors.join(', ')}. Available flavors: #{all_flavor_names.join(', ')}")
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
flavors_to_upload = requested_flavors
|
|
65
|
+
else
|
|
66
|
+
flavors_to_upload = all_flavor_names
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
UI.header("Starting Firebase Hosting upload. Flavors to process: #{flavors_to_upload.join(', ')}")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# 5. Loop through each flavor and upload
|
|
73
|
+
flavors_to_upload.each do |flavor_key|
|
|
74
|
+
|
|
75
|
+
flavor = flavor_key.to_s
|
|
76
|
+
|
|
77
|
+
# Get values from spec, falling back to parameters
|
|
78
|
+
# Note: firebase_project is intentionally NOT loaded from spec here,
|
|
79
|
+
# as it's passed directly to AnnaiLanes which handles the prioritization
|
|
80
|
+
# (CLI param > Spec file).
|
|
81
|
+
final_main_file = spec_loader.get_main_file(platform, flavor) || params[:main_file]
|
|
82
|
+
|
|
83
|
+
UI.message("🚀 Preparing **#{flavor}** flavor...")
|
|
84
|
+
|
|
85
|
+
# Use 'upload_to_firebase' as the consistent action name for flavor-specific failures
|
|
86
|
+
flavor_action_name = "upload_to_firebase"
|
|
87
|
+
|
|
88
|
+
begin
|
|
89
|
+
# Web Upload Logic (upload_to_firebase)
|
|
90
|
+
success = annai_lanes.upload_to_firebase(
|
|
91
|
+
flavor: flavor,
|
|
92
|
+
main_file: final_main_file,
|
|
93
|
+
build_config: params[:build_config],
|
|
94
|
+
skip_compile: params[:skip_compile],
|
|
95
|
+
skip_sound_null_safety: params[:skip_sound_null_safety],
|
|
96
|
+
firebase_project: params[:firebase_project],
|
|
97
|
+
firebase_token: params[:firebase_token] || ENV["FIREBASE_TOKEN"],
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if success
|
|
101
|
+
UI.success("✅ Successfully deployed flavor: #{flavor}")
|
|
102
|
+
else
|
|
103
|
+
# If upload_to_firebase returns false, flag the overall failure.
|
|
104
|
+
if !status_manager.instance_variable_get(:@status).key?([flavor, flavor_action_name, platform.to_s])
|
|
105
|
+
raise "Deployment failed for flavor '#{flavor}'"
|
|
106
|
+
else
|
|
107
|
+
raise "Deployment failed for flavor '#{flavor}' (error logged previously)"
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
rescue => e
|
|
112
|
+
# Failure handling: Log the error and continue
|
|
113
|
+
if !status_manager.instance_variable_get(:@status).key?([flavor, flavor_action_name, platform.to_s])
|
|
114
|
+
status_manager.logError(flavor, flavor_action_name, platform, e.message)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
UI.error("❌ Failed to deploy flavor: #{flavor}. Continuing to next flavor...")
|
|
118
|
+
any_upload_failed = true
|
|
119
|
+
# Continue to the next flavor
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# 6. Final Reporting and Status Check
|
|
124
|
+
annai_lanes.finalize
|
|
125
|
+
|
|
126
|
+
if any_upload_failed
|
|
127
|
+
UI.user_error!("Deployment failed for one or more flavors. See the Annai Fastlane summary above")
|
|
128
|
+
else
|
|
129
|
+
UI.success("🎉 Successfully deployed all #{flavors_to_upload.count} requested flavor(s) to Firebase Hosting!")
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# ----------------------------------------------------
|
|
134
|
+
# Define Parameters
|
|
135
|
+
# ----------------------------------------------------
|
|
136
|
+
def self.available_options
|
|
137
|
+
[
|
|
138
|
+
FastlaneCore::ConfigItem.new(key: :flavor,
|
|
139
|
+
description: "The specific flavor(s) to deploy (comma-separated). If nil, all Web flavors are deployed",
|
|
140
|
+
optional: true,
|
|
141
|
+
default_value: nil),
|
|
142
|
+
|
|
143
|
+
FastlaneCore::ConfigItem.new(key: :spec_file,
|
|
144
|
+
description: "Path to the annai spec configuration file (relative to flutter root). Defaults to standard discovery",
|
|
145
|
+
optional: true,
|
|
146
|
+
default_value: nil),
|
|
147
|
+
|
|
148
|
+
FastlaneCore::ConfigItem.new(key: :clean,
|
|
149
|
+
description: "If true, runs annai_clean_build before deploying (and compiling if not skipped)",
|
|
150
|
+
type: Boolean,
|
|
151
|
+
default_value: false),
|
|
152
|
+
|
|
153
|
+
# --- Web / Firebase Specific Parameters ---
|
|
154
|
+
FastlaneCore::ConfigItem.new(key: :firebase_token,
|
|
155
|
+
description: "Firebase CLI CI token for authentication (generated via 'firebase login:ci')",
|
|
156
|
+
optional: true,
|
|
157
|
+
env_name: "FIREBASE_TOKEN",
|
|
158
|
+
type: String),
|
|
159
|
+
FastlaneCore::ConfigItem.new(key: :firebase_project,
|
|
160
|
+
description: "The Firebase Project ID to deploy to. This value overrides the 'project_id' in the annai spec file",
|
|
161
|
+
optional: true,
|
|
162
|
+
type: String),
|
|
163
|
+
FastlaneCore::ConfigItem.new(key: :main_file,
|
|
164
|
+
description: "Path to the main dart file (used if compilation is required)",
|
|
165
|
+
default_value: "lib/main.dart"),
|
|
166
|
+
FastlaneCore::ConfigItem.new(key: :build_config,
|
|
167
|
+
description: "The Flutter build configuration ('release', 'profile', or 'debug')",
|
|
168
|
+
optional: true,
|
|
169
|
+
default_value: "release"),
|
|
170
|
+
FastlaneCore::ConfigItem.new(key: :skip_compile,
|
|
171
|
+
description: "If true, skips the compilation step ('flutter build web') and assumes artifacts already exist",
|
|
172
|
+
type: Boolean,
|
|
173
|
+
default_value: false),
|
|
174
|
+
FastlaneCore::ConfigItem.new(key: :skip_sound_null_safety,
|
|
175
|
+
description: "Skips sound null safety checks during compilation (if skip_compile is false)",
|
|
176
|
+
type: Boolean,
|
|
177
|
+
default_value: false),
|
|
178
|
+
].compact
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def self.description
|
|
182
|
+
"Handles compilation (optional) and deployment of one or all Web flavors to Firebase Hosting"
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def self.is_supported?(platform)
|
|
186
|
+
platform == :web
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def self.example_code
|
|
190
|
+
'ann_upload_to_firebase(
|
|
191
|
+
flavor: "staging,production",
|
|
192
|
+
firebase_project: "my-custom-staging-project", # Overrides spec file
|
|
193
|
+
build_config: "release",
|
|
194
|
+
skip_compile: false
|
|
195
|
+
)'
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|