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,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
|