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,248 @@
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_play_store'
8
+ class AnnaiUploadToPlayStoreAction < Action
9
+
10
+ def self.run(params)
11
+ platform = :android # Hardcoded platform
12
+ action_name = "ann_upload_to_play_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 Android 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 Android: #{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 Google Play 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, prioritizing spec over parameters
78
+ final_main_file = spec_loader.get_main_file(platform, flavor) || params[:main_file]
79
+ final_api_key_path = spec_loader.get_api_key_path(platform, flavor) || params[:api_key_path]
80
+ final_priority = spec_loader.get_priority(platform, flavor) || params[:priority]
81
+ final_package_name = spec_loader.get_package_id(platform, flavor) || params[:package_name]
82
+
83
+ UI.message("🚀 Preparing **#{flavor}** flavor...")
84
+
85
+ # Use 'upload_to_store' as the consistent action name for flavor-specific failures
86
+ flavor_action_name = "upload_to_store"
87
+
88
+ begin
89
+ # Android Upload Logic (upload_to_play_store)
90
+ success = annai_lanes.upload_to_play_store(
91
+ package_name: final_package_name,
92
+ api_key_path: final_api_key_path,
93
+ in_app_update_priority: final_priority,
94
+ flavor: flavor,
95
+ main_file: final_main_file,
96
+ track: params[:track],
97
+ track_promote_to: params[:track_promote_to],
98
+ skip_sound_null_safety: params[:skip_sound_null_safety],
99
+ skip_compile: params[:skip_compile],
100
+ skip_upload_prod: params[:skip_upload_prod],
101
+ skip_upload_binary: params[:skip_upload_binary],
102
+ skip_upload_changelogs: params[:skip_upload_changelogs],
103
+ skip_upload_metadata: params[:skip_upload_metadata],
104
+ skip_upload_images: params[:skip_upload_images],
105
+ skip_upload_screenshots: params[:skip_upload_screenshots],
106
+ platform: platform.to_s,
107
+ metadata_path: params[:metadata_path],
108
+ )
109
+
110
+ if success
111
+ UI.success("✅ Successfully uploaded flavor: #{flavor}")
112
+ else
113
+ # If upload_to_play_store returns false (meaning it caught an error and logged it), flag the overall failure.
114
+ if !status_manager.instance_variable_get(:@status).key?([flavor, flavor_action_name, platform.to_s])
115
+ raise "Upload failed for flavor '#{flavor}'"
116
+ else
117
+ raise "Upload failed for flavor '#{flavor}' (error logged previously)"
118
+ end
119
+ end
120
+
121
+ rescue => e
122
+ # Failure handling: Log the error and continue
123
+ if !status_manager.instance_variable_get(:@status).key?([flavor, flavor_action_name, platform.to_s])
124
+ status_manager.logError(flavor, flavor_action_name, platform, e.message)
125
+ end
126
+
127
+ UI.error("❌ Failed to upload flavor: #{flavor}. Continuing to next flavor...")
128
+ any_upload_failed = true
129
+ # Continue to the next flavor
130
+ end
131
+ end
132
+
133
+ # 6. Final Reporting and Status Check
134
+ annai_lanes.finalize
135
+
136
+ if any_upload_failed
137
+ UI.user_error!("Upload failed for one or more flavors. See the Annai Fastlane summary above")
138
+ else
139
+ UI.success("🎉 Successfully uploaded all #{flavors_to_upload.count} requested flavor(s) to the Google Play Store!")
140
+ end
141
+ end
142
+
143
+ # ----------------------------------------------------
144
+ # Define Parameters
145
+ # ----------------------------------------------------
146
+ def self.available_options
147
+ [
148
+ FastlaneCore::ConfigItem.new(key: :flavor,
149
+ description: "The specific flavor(s) to upload (comma-separated). If nil, all flavors are uploaded",
150
+ optional: true,
151
+ default_value: nil),
152
+
153
+ FastlaneCore::ConfigItem.new(key: :spec_file,
154
+ description: "Path to the annai spec configuration file (relative to flutter root). Defaults to standard discovery",
155
+ optional: true,
156
+ default_value: nil),
157
+
158
+ FastlaneCore::ConfigItem.new(key: :clean,
159
+ description: "If true, runs annai_clean_build before uploading (and compiling if not skipped)",
160
+ type: Boolean,
161
+ default_value: false),
162
+
163
+ # --- Common & Android Specific Parameters ---
164
+ FastlaneCore::ConfigItem.new(key: :api_key_path,
165
+ description: "Path to the Google Service Account JSON file",
166
+ optional: true,
167
+ type: String),
168
+ FastlaneCore::ConfigItem.new(key: :main_file,
169
+ description: "Path to the main dart file (used if compilation is required)",
170
+ default_value: "lib/main.dart"),
171
+ FastlaneCore::ConfigItem.new(key: :skip_compile,
172
+ description: "If true, skips the compilation step and assumes artifacts already exist",
173
+ type: Boolean,
174
+ default_value: false),
175
+ FastlaneCore::ConfigItem.new(key: :skip_sound_null_safety,
176
+ description: "Skips sound null safety checks during compilation (if skip_compile is false)",
177
+ type: Boolean,
178
+ default_value: false),
179
+ FastlaneCore::ConfigItem.new(key: :skip_upload_prod,
180
+ description: "If true, skips uploading to the production track",
181
+ type: Boolean,
182
+ default_value: false),
183
+ FastlaneCore::ConfigItem.new(key: :skip_upload_binary,
184
+ description: "If true, skips uploading the binary (.aab)",
185
+ type: Boolean,
186
+ default_value: false),
187
+ FastlaneCore::ConfigItem.new(key: :skip_upload_metadata,
188
+ description: "If true, skips uploading store metadata (description, title, etc)",
189
+ type: Boolean,
190
+ default_value: false),
191
+ FastlaneCore::ConfigItem.new(key: :skip_upload_screenshots,
192
+ description: "If true, skips uploading screenshots and app previews",
193
+ type: Boolean,
194
+ default_value: false),
195
+ FastlaneCore::ConfigItem.new(key: :metadata_path,
196
+ description: "The path to the localized store metadata (e.g., fastlane/metadata/android)",
197
+ optional: true,
198
+ default_value: nil),
199
+
200
+ FastlaneCore::ConfigItem.new(key: :package_name,
201
+ description: "The package name (Android)",
202
+ optional: true,
203
+ default_value: nil),
204
+ FastlaneCore::ConfigItem.new(key: :track,
205
+ description: "The track to upload to (e.g., internal, alpha, beta, production)",
206
+ optional: true,
207
+ default_value: "beta"),
208
+ FastlaneCore::ConfigItem.new(key: :track_promote_to,
209
+ description: "The track to promote to after upload",
210
+ optional: true,
211
+ default_value: "production"),
212
+ FastlaneCore::ConfigItem.new(key: :priority,
213
+ description: "In-app update priority (0-5)",
214
+ optional: true,
215
+ type: Integer,
216
+ default_value: 1),
217
+ FastlaneCore::ConfigItem.new(key: :skip_upload_changelogs,
218
+ description: "If true, skips uploading changelogs",
219
+ type: Boolean,
220
+ default_value: false),
221
+ FastlaneCore::ConfigItem.new(key: :skip_upload_images,
222
+ description: "If true, skips uploading promotional images",
223
+ type: Boolean,
224
+ default_value: false),
225
+ ].compact
226
+ end
227
+
228
+ def self.description
229
+ "Handles compilation (optional) and uploading of one or all flavors to the Google Play Store"
230
+ end
231
+
232
+ def self.is_supported?(platform)
233
+ platform == :android
234
+ end
235
+
236
+ def self.example_code
237
+ 'ann_upload_to_play_store(
238
+ flavor: "production",
239
+ spec_file: "config/annai_config.yaml", # Optional custom spec path
240
+ package_name: "com.example.app",
241
+ api_key_path: "./api_keys/google.json",
242
+ track: "production",
243
+ skip_compile: false
244
+ )'
245
+ end
246
+ end
247
+ end
248
+ end
@@ -0,0 +1,120 @@
1
+ require 'yaml'
2
+ require 'fileutils'
3
+ require 'fastlane/action'
4
+
5
+ module FastlaneFlutterFlavor
6
+
7
+ # ------------------------------------
8
+ # AndroidLanes (Helper Class)
9
+ # ------------------------------------
10
+ class AndroidLanes
11
+
12
+ # NOTE: We keep @lane for general context, but must use other_action for calling built-in Fastlane actions.
13
+ def initialize(lane: ,root_folder:)
14
+ @lane = lane
15
+ @root_folder = root_folder
16
+ end
17
+
18
+ def upload_to_store(aab:, package_name:, api_key_path:, metadata_path:,
19
+ in_app_update_priority: 1, track: "beta", track_promote_to: "production",
20
+ skip_upload_prod: false, skip_upload_binary: false,
21
+ skip_upload_changelogs: false, skip_upload_metadata: false,
22
+ skip_upload_images: false, skip_upload_screenshots: false, platform: "android"
23
+ )
24
+
25
+ # 1. Upload Binary (AAB file)
26
+ unless skip_upload_binary
27
+ # Use 'other_action' to call the built-in Fastlane action
28
+ @lane.other_action.upload_to_play_store(
29
+ track: track,
30
+ aab: aab,
31
+ package_name: package_name,
32
+ in_app_update_priority: in_app_update_priority,
33
+ json_key: api_key_path,
34
+ metadata_path: metadata_path,
35
+
36
+ # Explicitly set AAB upload to FALSE if we are skipping it, otherwise TRUE
37
+ skip_upload_aab: false,
38
+
39
+ # Skip all other non-binary uploads for this initial call
40
+ skip_upload_apk: true,
41
+ skip_upload_changelogs: true,
42
+ skip_upload_metadata: true,
43
+ skip_upload_images: true,
44
+ skip_upload_screenshots: true,
45
+ )
46
+ end
47
+
48
+ # 2. Upload Metadata/Promote to Production (if required)
49
+ unless skip_upload_prod
50
+
51
+ # Use 'other_action' to call the built-in Fastlane action
52
+ versions = @lane.other_action.google_play_track_version_codes(
53
+ track: track,
54
+ package_name: package_name,
55
+ json_key: api_key_path,
56
+ )
57
+
58
+ # Ensure versions is an array and take the first (latest)
59
+ version_code = versions.is_a?(Array) && versions.any? ? versions[0] : nil
60
+
61
+ if version_code.nil?
62
+ Fastlane::UI.error "Could not retrieve version code for package #{package_name} on track #{track}. Skipping metadata/promotion step."
63
+ return
64
+ end
65
+
66
+ Fastlane::UI.message "App Version Code: " + version_code.to_s
67
+ Fastlane::UI.message "App Identifier: " + package_name
68
+ Fastlane::UI.message "Track: " + track
69
+ Fastlane::UI.message "Track Promote to: " + track_promote_to
70
+
71
+ # This call handles metadata, changelogs, images, screenshots, and promotion
72
+ # Use 'other_action' to call the built-in Fastlane action
73
+ @lane.other_action.upload_to_play_store(
74
+ track: track,
75
+ track_promote_to: track_promote_to,
76
+ package_name: package_name,
77
+ version_code: version_code, # Ensure we apply changes to the correct version
78
+ in_app_update_priority: in_app_update_priority,
79
+ json_key: api_key_path,
80
+ metadata_path: metadata_path,
81
+
82
+ # Skip the binary upload here, as it was handled in step 1 or skipped by the user
83
+ skip_upload_aab: true,
84
+ skip_upload_apk: true,
85
+
86
+ # Use user-defined skip flags for other components
87
+ skip_upload_changelogs: skip_upload_changelogs,
88
+ skip_upload_metadata: skip_upload_metadata,
89
+ skip_upload_images: skip_upload_images,
90
+ skip_upload_screenshots: skip_upload_screenshots,
91
+ )
92
+ end
93
+ end
94
+
95
+ def download_from_store(package_name:, api_key_path:, metadata_path:, platform: "android")
96
+ FileUtils.rm_rf(metadata_path)
97
+
98
+ Fastlane::Actions::sh(
99
+ "fastlane", "supply", "init",
100
+ "--metadata_path", metadata_path,
101
+ "--package_name", package_name,
102
+ "--json_key", api_key_path,
103
+ )
104
+ end
105
+
106
+ # Resolves the metadata path: either a custom one relative to the root,
107
+ # or the standard Fastlane structure.
108
+ def get_metadata_path(platform: "android", metadata_path: nil, flavor:)
109
+ # If a custom path is provided (and not nil or empty string), use it.
110
+ unless metadata_path.to_s.empty?
111
+ # Path structure: @root_folder / custom_path / flavor
112
+ return File.join(@root_folder, metadata_path, flavor)
113
+ end
114
+
115
+ # Default Path: Use the standard Fastlane structure.
116
+ # Path structure: @root_folder / fastlane / platform / metadata / flavor
117
+ File.join(@root_folder, "fastlane", platform, 'metadata', flavor)
118
+ end
119
+ end
120
+ end