ann-flavor-flutter 0.1.7

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/README.md +274 -0
  5. data/Rakefile +6 -0
  6. data/ann-flavor-flutter.gemspec +26 -0
  7. data/lib/ann_flavor_flutter/version.rb +3 -0
  8. data/lib/ann_flavor_flutter.rb +10 -0
  9. data/lib/fastlane/plugin/ann_flavor_flutter/actions/ann_compile_build_action.rb +219 -0
  10. data/lib/fastlane/plugin/ann_flavor_flutter/actions/ann_download_from_app_store_action.rb +183 -0
  11. data/lib/fastlane/plugin/ann_flavor_flutter/actions/ann_download_from_play_store_action.rb +168 -0
  12. data/lib/fastlane/plugin/ann_flavor_flutter/actions/ann_emulators_action.rb +180 -0
  13. data/lib/fastlane/plugin/ann_flavor_flutter/actions/ann_run_tests_action.rb +128 -0
  14. data/lib/fastlane/plugin/ann_flavor_flutter/actions/ann_setup_action.rb +137 -0
  15. data/lib/fastlane/plugin/ann_flavor_flutter/actions/ann_upload_to_app_store_action.rb +280 -0
  16. data/lib/fastlane/plugin/ann_flavor_flutter/actions/ann_upload_to_firebase_action.rb +199 -0
  17. data/lib/fastlane/plugin/ann_flavor_flutter/actions/ann_upload_to_play_store_action.rb +248 -0
  18. data/lib/fastlane/plugin/ann_flavor_flutter/helper/lanes_android.rb +120 -0
  19. data/lib/fastlane/plugin/ann_flavor_flutter/helper/lanes_annai.rb +502 -0
  20. data/lib/fastlane/plugin/ann_flavor_flutter/helper/lanes_ios.rb +157 -0
  21. data/lib/fastlane/plugin/ann_flavor_flutter/helper/lanes_setup.rb +161 -0
  22. data/lib/fastlane/plugin/ann_flavor_flutter/helper/podspec_bridge.rb +153 -0
  23. data/lib/fastlane/plugin/ann_flavor_flutter/helper/test_integration.rb +346 -0
  24. data/lib/fastlane/plugin/ann_flavor_flutter/helper/utils_project_config.rb +102 -0
  25. data/lib/fastlane/plugin/ann_flavor_flutter/helper/utils_spec_loader.rb +339 -0
  26. data/lib/fastlane/plugin/ann_flavor_flutter/helper/utils_status.rb +115 -0
  27. data/lib/fastlane/plugin/ann_flavor_flutter.rb +23 -0
  28. data/plugin_manager.sh +140 -0
  29. metadata +126 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ab09bf5aa8c6c9049419123f6ff1beb4f2a08cc4c25984f4f1e94efe4712da0b
4
+ data.tar.gz: e6bd5b196b3716ef79440ad2977287dff9278702747b7d1483fe96126b839d99
5
+ SHA512:
6
+ metadata.gz: 7e754735605d623ba6bbb0d3f7bed2742f1d6fff56a912057064e484a78feac8f2eaedc2e5e487633dc6aa336ac48a969f1e76de1fa4802e1141da2839202aa0
7
+ data.tar.gz: fa535b179dd36c5e9af712ac58884f3325b40947cdd392f07c9aed6f9a7fc53f391f34065fb0ef3f134ab56ff04449a4235fb9656300acfde1dabcb499387e86
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ /pkg/
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,274 @@
1
+ # ANN Flutter Flavor — Fastlane Plugin
2
+
3
+ [![RubyGems](https://img.shields.io/gem/v/ann-flavor-flutter)](https://rubygems.org/gems/ann-flavor-flutter)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ Fastlane plugin that reads `annspec.yaml` and provides lanes for building, signing, uploading and testing all Flutter flavors across Android, iOS and Web — without writing repetitive Fastfile boilerplate for each flavor.
7
+
8
+ ---
9
+
10
+ ## Installation
11
+
12
+ Add to your `Pluginfile`:
13
+
14
+ ```ruby
15
+ gem 'ann-flavor-flutter'
16
+ ```
17
+
18
+ Then run:
19
+
20
+ ```bash
21
+ bundle install
22
+ ```
23
+
24
+ Or add directly via Fastlane:
25
+
26
+ ```bash
27
+ fastlane add_plugin ann_flavor_flutter
28
+ ```
29
+
30
+ ---
31
+
32
+ ## Quick start
33
+
34
+ ```ruby
35
+ # Fastfile
36
+ lane :deploy_android do
37
+ ann_compile_build(platform: :android, build_config: "release")
38
+ ann_upload_to_play_store(platform: :android)
39
+ end
40
+
41
+ lane :deploy_ios do
42
+ ann_compile_build(platform: :ios, build_config: "release")
43
+ ann_upload_to_app_store(platform: :ios)
44
+ end
45
+ ```
46
+
47
+ ```bash
48
+ bundle exec fastlane deploy_android
49
+ bundle exec fastlane deploy_ios
50
+ ```
51
+
52
+ ---
53
+
54
+ ## Actions
55
+
56
+ ### `ann_compile_build` — Build all flavors for a platform
57
+
58
+ ```ruby
59
+ ann_compile_build(
60
+ platform: :android, # :ios, :android, or :web
61
+ build_config: "release", # "debug", "profile", "release"
62
+ sub_command: "appbundle", # "apk", "appbundle", "ipa", "web"
63
+ flavor: "production", # Comma-separated. Omit to build ALL flavors
64
+ clean: false,
65
+ main_file: "lib/main.dart",
66
+ skip_sound_null_safety: false,
67
+ skip_code_sign: false,
68
+ export_options_plist: "", # iOS only
69
+ additional_parameter: "",
70
+ spec_file: nil # Custom path to annspec.yaml (optional)
71
+ )
72
+ ```
73
+
74
+ **Examples:**
75
+
76
+ ```ruby
77
+ ann_compile_build(platform: :android) # All Android flavors
78
+ ann_compile_build(platform: :ios, flavor: "production,staging") # Specific iOS flavors
79
+ ann_compile_build(platform: :web, sub_command: "web", clean: true) # Web with clean
80
+ ```
81
+
82
+ ---
83
+
84
+ ### `ann_upload_to_play_store` — Upload Android to Google Play
85
+
86
+ ```ruby
87
+ ann_upload_to_play_store(
88
+ flavor: "production", # Specific flavor(s). Omit for all
89
+ clean: false,
90
+ spec_file: nil
91
+ )
92
+ ```
93
+
94
+ ---
95
+
96
+ ### `ann_upload_to_app_store` — Upload iOS to App Store
97
+
98
+ ```ruby
99
+ ann_upload_to_app_store(
100
+ flavor: "production",
101
+ clean: false,
102
+ spec_file: nil
103
+ )
104
+ ```
105
+
106
+ ---
107
+
108
+ ### `ann_upload_to_firebase` — Deploy web to Firebase Hosting
109
+
110
+ ```ruby
111
+ ann_upload_to_firebase(
112
+ flavor: "web",
113
+ clean: false,
114
+ spec_file: nil
115
+ )
116
+ ```
117
+
118
+ ---
119
+
120
+ ### `ann_run_tests` — Run integration tests
121
+
122
+ ```ruby
123
+ ann_run_tests(
124
+ platform: :android,
125
+ test_spec_file: nil
126
+ )
127
+ ```
128
+
129
+ ---
130
+
131
+ ### `ann_open_emulators` — Start emulators/simulators
132
+
133
+ ```ruby
134
+ ann_open_emulators(
135
+ platform: :android,
136
+ test_spec_file: nil
137
+ )
138
+ ```
139
+
140
+ ---
141
+
142
+ ### `annai_upgrade_setup` — Maintenance setup
143
+
144
+ ```ruby
145
+ annai_upgrade_setup # Skip if already up to date
146
+ annai_upgrade_setup(force: true) # Force run
147
+ ```
148
+
149
+ ---
150
+
151
+ ### `annai_cleanup` — Clean build artifacts
152
+
153
+ ```ruby
154
+ annai_cleanup
155
+ annai_cleanup(force: true)
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Configuration
161
+
162
+ The plugin reads `annspec.yaml` from your Flutter project root. Keys used by Fastlane actions:
163
+
164
+ ```yaml
165
+ annai_app:
166
+ android:
167
+ default:
168
+ signature:
169
+ key_file: "../annai_app_data/keys/android/my_key.properties"
170
+ stores:
171
+ google_play:
172
+ api_key: "../annai_app_data/keys/android/google-api-key.json"
173
+ flavor:
174
+ production:
175
+ main_file: "lib/flavors/main_prod.dart"
176
+ version_name: 2.1.0
177
+ version_code: 20100
178
+
179
+ ios:
180
+ default:
181
+ team_id: "XXXXXXXXXX"
182
+ stores:
183
+ app_store:
184
+ api_key: "../annai_app_data/keys/ios/apple_api_key.json"
185
+ export_options_plist: "../annai_app_data/keys/ios/exportOptions.plist"
186
+ flavor:
187
+ production:
188
+ apple_id: "1234567890"
189
+ main_file: "lib/flavors/main_prod.dart"
190
+ version_name: 2.1.0
191
+ version_code: 20100
192
+ ```
193
+
194
+ See the [Gradle plugin README](../ann-flavor-gradle#configuration) for the complete `annspec.yaml` field reference.
195
+
196
+ ---
197
+
198
+ ## Typical CI/CD Fastfile
199
+
200
+ ```ruby
201
+ default_platform(:android)
202
+
203
+ platform :android do
204
+ lane :build do
205
+ ann_compile_build(platform: :android, build_config: "release", sub_command: "appbundle", clean: true)
206
+ end
207
+
208
+ lane :deploy do
209
+ ann_upload_to_play_store
210
+ end
211
+
212
+ lane :test do
213
+ ann_open_emulators(platform: :android)
214
+ ann_run_tests(platform: :android)
215
+ end
216
+ end
217
+
218
+ platform :ios do
219
+ lane :build do
220
+ ann_compile_build(platform: :ios, build_config: "release", sub_command: "ipa")
221
+ end
222
+
223
+ lane :deploy do
224
+ ann_upload_to_app_store
225
+ end
226
+ end
227
+ ```
228
+
229
+ ---
230
+
231
+ ## Testing
232
+
233
+ The plugin has an RSpec test suite:
234
+
235
+ ```bash
236
+ # From ann-flavor-tooling root
237
+ cd plugins/ann-flavor-fastlane && bundle exec rake spec
238
+
239
+ # Or via the publish script (also saves results + posts Check Run)
240
+ cd ../.. && ./scripts/publish.sh fastlane 0.1.3
241
+ ```
242
+
243
+ ---
244
+
245
+ ## Publishing
246
+
247
+ Publishing is managed from the `ann-flavor-tooling` root:
248
+
249
+ ```bash
250
+ cd ../..
251
+ ./scripts/publish.sh fastlane 0.1.3
252
+ ```
253
+
254
+ ---
255
+
256
+ ## Requirements
257
+
258
+ - Fastlane `>= 2.232.2`
259
+ - Ruby `>= 3.0`
260
+ - Flutter project with `annspec.yaml` at the project root
261
+
262
+ ---
263
+
264
+ ## Related plugins
265
+
266
+ - **[ann-flavor-gradle](../ann-flavor-gradle)** — Gradle plugin for Android build configuration
267
+ - **[ann-flavor-studio](../ann-flavor-studio)** — Android Studio plugin for interactive spec sync
268
+ - **[ann-flavor-flutter](../ann-flavor-flutter)** — Dart runtime API
269
+
270
+ ---
271
+
272
+ ## License
273
+
274
+ [MIT](LICENSE) © [ANN Solutions](https://github.com/anntech-dev)
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('lib', __dir__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ require 'ann_flavor_flutter/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "ann-flavor-flutter"
9
+ spec.version = AnnFlavorFlutter::VERSION
10
+ spec.authors = ["ANN Solutions"]
11
+ spec.email = ["support@annaibrands.com"]
12
+ spec.summary = "A fastlane plugin to manage Flutter build flavors and shared setup."
13
+ spec.homepage = "https://github.com/anntech-dev"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ # Defines the fastlane dependency
22
+ spec.add_dependency "fastlane", ">= 2.232.2"
23
+ spec.add_development_dependency 'pry'
24
+ spec.add_development_dependency 'rspec', '~> 3.4.0'
25
+ spec.add_development_dependency 'rspec_junit_formatter', '~> 0.6'
26
+ end
@@ -0,0 +1,3 @@
1
+ module AnnFlavorFlutter
2
+ VERSION = "0.1.7"
3
+ end
@@ -0,0 +1,10 @@
1
+ # This file is loaded by require 'annai-flutter-flavor'
2
+
3
+ # Load the version (Good Practice)
4
+ require 'ann_flavor_flutter/version'
5
+
6
+ require 'fastlane'
7
+
8
+ # Load the Fastlane plugin structure (which defines the Plugin class)
9
+ require 'fastlane/plugin/ann_flavor_flutter'
10
+
@@ -0,0 +1,219 @@
1
+ require 'fastlane/action'
2
+ require 'fastlane/plugin/ann_flavor_flutter/helper/lanes_annai'
3
+ require 'fastlane/plugin/ann_flavor_flutter/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