ann-flavor-flutter 0.2.2 → 0.4.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d5477ea279ba2d984395d1d074ec8dc64b39bbeb2718d52263ac1582c39c2938
4
- data.tar.gz: bd8c3a32d1b36cbdd3adf4fa80a8fd7effc73370b123acf62a97757623623447
3
+ metadata.gz: c130b0210def1ac340db96f188146dbef72a1550d914b77fa062cd25f7ccb279
4
+ data.tar.gz: 4688888ece3b6fa767225b16d644da23ea7f2762eb6a2b92c95742d309adcb09
5
5
  SHA512:
6
- metadata.gz: dc194b3e7ecf42d90997396c1eca063fa17f83f9c0599dca24bccee6c82321816c4e7a28a8f16dfbbddbbbf1f7987e0d952e58f613491cb3e686247a290335ba
7
- data.tar.gz: 736cf855b427890b45cac71020d6fede172d30b30c87b3235223cd011ebf025b598c29a8bba56c0fc1785b3cfb7a8b73173b3c6eee59ba28a49a701b9422ec69
6
+ metadata.gz: 8940badbe14a1fb8de04d6af4dcf58bba368c329d30434889b1d400703f5f9e1a5dfb6da075c7c677547bc35d699d4c0c775a51519aa72bded1a7a2b445d09db
7
+ data.tar.gz: 30052ccd783ff8f93533467423c3173f50799fd979e385ca56c599aaab8de67713a6eb11971d79c8d28c2e532fc2c93e0e779e1060d2e1a6e2689c0bcec5dc0d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Added
6
+ - **Plan 027**: New `ann_upload_to_cloudflare` action — deploys web flavors to Cloudflare Pages (`wrangler pages deploy`) or Cloudflare Workers (`wrangler deploy --config`). Reads `cloudflare.{project_name, account_id, branch, mode}` from `annspec.yaml` with cascade: flavor → default → fallback (`main`, `pages`). `project_name` is flavor-only (never inherited from default). `account_id` falls back to `CLOUDFLARE_ACCOUNT_ID` env var.
7
+ - `lanes_annai.rb` gains `upload_to_cloudflare` method and `web_output_dir` private helper.
8
+ - `flutter build web` now appends `--output=build/web/<flavor>` when a flavor is specified, matching the output directory used by both Firebase and Cloudflare deploy steps.
9
+ - `upload_to_firebase` uses `web_output_dir(flavor)` for the `public` dir in `firebase.json` so it matches the per-flavor build output.
10
+
11
+ ---
12
+
13
+ ## 0.3.0
14
+
15
+ ### Changed
16
+ - **Plan 026**: `@isWeb` branch in `lanes_annai.rb` now calls `dart run ann_flutter_flavor sync-web --flavor <name>` before `flutter build web`. Zero Ruby copy logic — preprocessing fully delegated to the Flutter CLI.
17
+
18
+ ---
19
+
3
20
  ## 0.2.1
4
21
 
5
22
  ### Added
@@ -1,3 +1,3 @@
1
1
  module AnnFlavorFlutter
2
- VERSION = "0.2.2"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -0,0 +1,166 @@
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 AnnaiUploadToCloudflareAction < Action
8
+
9
+ def self.run(params)
10
+ platform = :web
11
+ spec_file = params[:spec_file]
12
+
13
+ annai_lanes = FastlaneFlutterFlavor::AnnaiLanes.new(
14
+ lane: self,
15
+ platform: platform,
16
+ spec_file: spec_file
17
+ )
18
+
19
+ status_manager = annai_lanes.instance_variable_get(:@statusManager)
20
+ spec_loader = annai_lanes.instance_variable_get(:@specLoader)
21
+ UI.user_error!("Internal Error: AnnaiLanes failed to initialize specLoader") unless spec_loader
22
+
23
+ if params[:clean]
24
+ UI.header("🧹 Cleaning builds as requested...")
25
+ begin
26
+ annai_lanes.clean_build
27
+ rescue => e
28
+ UI.error("Failed to perform clean build: #{e.message}")
29
+ status_manager.logError("All", "annai_clean_build", platform, e.message)
30
+ end
31
+ end
32
+
33
+ flavors_hash = spec_loader.get_platform_flavors(platform)
34
+
35
+ if flavors_hash.empty?
36
+ UI.important("No flavors found for Web in the Annai spec. Nothing to deploy")
37
+ annai_lanes.finalize
38
+ return
39
+ end
40
+
41
+ all_flavor_names = flavors_hash.keys
42
+
43
+ requested_flavor = params[:flavor]
44
+ if requested_flavor && !requested_flavor.to_s.empty?
45
+ requested_flavors = requested_flavor.to_s.split(',').map(&:strip).reject(&:empty?)
46
+ invalid = requested_flavors.reject { |f| all_flavor_names.include?(f) }
47
+ if invalid.any?
48
+ UI.user_error!("Unknown flavor(s): #{invalid.join(', ')}. Available: #{all_flavor_names.join(', ')}")
49
+ end
50
+ flavors_to_deploy = requested_flavors
51
+ else
52
+ flavors_to_deploy = all_flavor_names
53
+ end
54
+
55
+ UI.header("Deploying to Cloudflare. Flavors: #{flavors_to_deploy.join(', ')}")
56
+
57
+ any_failed = false
58
+
59
+ flavors_to_deploy.each do |flavor_key|
60
+ flavor = flavor_key.to_s
61
+ final_main_file = spec_loader.get_main_file(platform, flavor) || params[:main_file]
62
+
63
+ UI.message("🚀 Deploying flavor: #{flavor}")
64
+
65
+ begin
66
+ success = annai_lanes.upload_to_cloudflare(
67
+ flavor: flavor,
68
+ main_file: final_main_file,
69
+ build_config: params[:build_config],
70
+ skip_compile: params[:skip_compile],
71
+ skip_sound_null_safety: params[:skip_sound_null_safety],
72
+ project_name: params[:project_name],
73
+ account_id: params[:account_id],
74
+ branch: params[:branch],
75
+ mode: params[:mode],
76
+ )
77
+
78
+ if success
79
+ UI.success("✅ Deployed #{flavor} to Cloudflare")
80
+ else
81
+ any_failed = true
82
+ UI.error("❌ Failed to deploy #{flavor}")
83
+ end
84
+
85
+ rescue => e
86
+ status_manager.logError(flavor, "upload_to_cloudflare", platform, e.message)
87
+ UI.error("❌ #{flavor}: #{e.message}")
88
+ any_failed = true
89
+ end
90
+ end
91
+
92
+ annai_lanes.finalize
93
+
94
+ if any_failed
95
+ UI.user_error!("Cloudflare deployment failed for one or more flavors")
96
+ else
97
+ UI.success("🎉 Successfully deployed #{flavors_to_deploy.count} flavor(s) to Cloudflare!")
98
+ end
99
+ end
100
+
101
+ def self.available_options
102
+ [
103
+ FastlaneCore::ConfigItem.new(key: :flavor,
104
+ description: "Flavor(s) to deploy (comma-separated). Defaults to all Web flavors",
105
+ optional: true,
106
+ default_value: nil),
107
+ FastlaneCore::ConfigItem.new(key: :spec_file,
108
+ description: "Path to annspec.yaml (relative to Flutter root). Defaults to standard discovery",
109
+ optional: true,
110
+ default_value: nil),
111
+ FastlaneCore::ConfigItem.new(key: :clean,
112
+ description: "Run clean build before deploying",
113
+ type: Boolean,
114
+ default_value: false),
115
+ FastlaneCore::ConfigItem.new(key: :main_file,
116
+ description: "Path to the main Dart file",
117
+ default_value: "lib/main.dart"),
118
+ FastlaneCore::ConfigItem.new(key: :build_config,
119
+ description: "Flutter build configuration ('release', 'profile', 'debug')",
120
+ optional: true,
121
+ default_value: "release"),
122
+ FastlaneCore::ConfigItem.new(key: :skip_compile,
123
+ description: "Skip compilation step",
124
+ type: Boolean,
125
+ default_value: false),
126
+ FastlaneCore::ConfigItem.new(key: :skip_sound_null_safety,
127
+ description: "Skip sound null safety during compilation",
128
+ type: Boolean,
129
+ default_value: false),
130
+ FastlaneCore::ConfigItem.new(key: :project_name,
131
+ description: "Cloudflare Pages project name (overrides annspec.yaml)",
132
+ optional: true,
133
+ type: String),
134
+ FastlaneCore::ConfigItem.new(key: :account_id,
135
+ description: "Cloudflare account ID (overrides annspec.yaml and CLOUDFLARE_ACCOUNT_ID env var)",
136
+ optional: true,
137
+ type: String),
138
+ FastlaneCore::ConfigItem.new(key: :branch,
139
+ description: "Cloudflare Pages branch (overrides annspec.yaml, default: 'main')",
140
+ optional: true,
141
+ type: String),
142
+ FastlaneCore::ConfigItem.new(key: :mode,
143
+ description: "Deployment mode: 'pages' or 'workers' (overrides annspec.yaml, default: 'pages')",
144
+ optional: true,
145
+ type: String),
146
+ ].compact
147
+ end
148
+
149
+ def self.description
150
+ "Deploy one or all Web flavors to Cloudflare Pages or Cloudflare Workers"
151
+ end
152
+
153
+ def self.is_supported?(platform)
154
+ platform == :web
155
+ end
156
+
157
+ def self.example_code
158
+ 'ann_upload_to_cloudflare(
159
+ flavor: "ledger_in,ledger_us",
160
+ build_config: "release",
161
+ skip_compile: false
162
+ )'
163
+ end
164
+ end
165
+ end
166
+ end
@@ -105,6 +105,13 @@ module FastlaneFlutterFlavor
105
105
  Fastlane::UI.message("Web compilation detected. Omitting '--flavor' from Flutter build command, but using '#{flavor}' for logging.")
106
106
  cli_flavor = ""
107
107
  cli_flavor_option = ""
108
+ # Preprocess: copy per-flavor web assets into web/ before building
109
+ sh("dart run ann_flutter_flavor sync-web --flavor #{flavor} --project #{@spec_root_folder}")
110
+ # Append per-flavor output directory when a flavor is specified
111
+ unless flavor.empty?
112
+ output_flag = "--output=#{web_output_dir(flavor)}"
113
+ additional_parameter = additional_parameter.to_s.empty? ? output_flag : "#{additional_parameter} #{output_flag}"
114
+ end
108
115
  end
109
116
 
110
117
  # Base command parts
@@ -208,7 +215,7 @@ module FastlaneFlutterFlavor
208
215
 
209
216
  # 4. Update the hosting block
210
217
  config['hosting'] = {
211
- "public" => "build/web",
218
+ "public" => web_output_dir(flavor),
212
219
  "ignore" => ["firebase.json", "**/.*", "**/node_modules/**"],
213
220
  "rewrites" => [{"source" => "**", "destination" => "/index.html"}]
214
221
  }
@@ -498,5 +505,84 @@ module FastlaneFlutterFlavor
498
505
  @setup_lanes.cleanup
499
506
  end
500
507
 
508
+ # Deploys a Flutter web build to Cloudflare Pages or Cloudflare Workers.
509
+ # Cascades: project_name (flavor-only), account_id/branch/mode (flavor → default → fallback).
510
+ def upload_to_cloudflare(flavor: "", main_file: "lib/main.dart", build_config: "release",
511
+ skip_compile: false, skip_sound_null_safety: false,
512
+ project_name: nil, account_id: nil, branch: nil, mode: nil)
513
+ begin
514
+ unless @isWeb
515
+ raise "upload_to_cloudflare is only supported for the :web platform (flavor: #{flavor})"
516
+ end
517
+
518
+ cf = @specLoader.get_cloudflare_config(@platform, flavor)
519
+
520
+ resolved_project_name = project_name || cf['project_name']
521
+ resolved_account_id = account_id || cf['account_id']
522
+ resolved_branch = branch || cf['branch']
523
+ resolved_mode = mode || cf['mode']
524
+
525
+ if resolved_project_name.nil? || resolved_project_name.empty?
526
+ raise "Missing cloudflare.project_name for web flavor '#{flavor}'. Set it in annspec.yaml."
527
+ end
528
+
529
+ unless skip_compile
530
+ unless compile_build(
531
+ flavor: flavor,
532
+ sub_command: "web",
533
+ build_config: build_config,
534
+ main_file: main_file,
535
+ skip_sound_null_safety: skip_sound_null_safety,
536
+ skip_code_sign: true,
537
+ export_options_plist: "",
538
+ platform: @platform,
539
+ )
540
+ raise "Compilation failed for web flavor '#{flavor}'"
541
+ end
542
+ end
543
+
544
+ output_dir = web_output_dir(flavor)
545
+
546
+ case resolved_mode
547
+ when 'pages'
548
+ command_parts = ["wrangler", "pages", "deploy", output_dir,
549
+ "--project-name", resolved_project_name]
550
+ command_parts += ["--branch", resolved_branch] if resolved_branch && !resolved_branch.empty?
551
+ command_parts += ["--commit-dirty", "true"]
552
+ when 'workers'
553
+ config_path = File.join(@root_folder, "web_flavors", flavor, "wrangler.toml")
554
+ command_parts = ["wrangler", "deploy", "--config", config_path]
555
+ else
556
+ raise "Unknown cloudflare mode '#{resolved_mode}'. Use 'pages' or 'workers'."
557
+ end
558
+
559
+ env_overrides = {}
560
+ env_overrides['CLOUDFLARE_ACCOUNT_ID'] = resolved_account_id if resolved_account_id && !resolved_account_id.empty?
561
+
562
+ Fastlane::UI.message("Deploying to Cloudflare #{resolved_mode}: #{command_parts.join(' ')}")
563
+
564
+ Dir.chdir @root_folder do
565
+ env_overrides.each { |k, v| ENV[k] = v }
566
+ Fastlane::Actions::sh(*command_parts)
567
+ env_overrides.keys.each { |k| ENV.delete(k) }
568
+ end
569
+
570
+ @statusManager.logSuccess flavor, "upload_to_cloudflare", @platform
571
+ return true
572
+
573
+ rescue => e
574
+ Fastlane::UI.error "Error while deploying to Cloudflare for web flavor '#{flavor}'"
575
+ Fastlane::UI.error e
576
+ @statusManager.logError flavor, "upload_to_cloudflare", @platform, e
577
+ return false
578
+ end
579
+ end
580
+
581
+ private
582
+
583
+ def web_output_dir(flavor)
584
+ flavor.to_s.empty? ? "build/web" : "build/web/#{flavor}"
585
+ end
586
+
501
587
  end
502
588
  end
@@ -377,6 +377,28 @@ module FastlaneFlutterFlavor
377
377
  config&.dig('admob', 'gms_ads_id') || config&.dig('gms_ads_id')
378
378
  end
379
379
 
380
+ # Returns the resolved Cloudflare config for a web flavor as a Hash with keys:
381
+ # project_name, account_id, branch, mode — cascaded flavor → default → hardcoded fallback.
382
+ # project_name is flavor-only (never inherited from default).
383
+ # @return [Hash] Always returns a Hash; values may be nil if not configured.
384
+ def get_cloudflare_config(platform, flavor_name)
385
+ return {} unless @spec_data
386
+
387
+ platform_key = platform.to_s
388
+ platform_data = @spec_data.dig('app', platform_key)
389
+ return {} unless platform_data.is_a?(Hash)
390
+
391
+ flavor_cf = platform_data.dig('flavor', flavor_name.to_s, 'cloudflare') || {}
392
+ default_cf = platform_data.dig('default', 'cloudflare') || {}
393
+
394
+ {
395
+ 'project_name' => flavor_cf['project_name'],
396
+ 'account_id' => flavor_cf['account_id'] || default_cf['account_id'],
397
+ 'branch' => flavor_cf['branch'] || default_cf['branch'] || 'main',
398
+ 'mode' => flavor_cf['mode'] || default_cf['mode'] || 'pages',
399
+ }
400
+ end
401
+
380
402
  # For Google Sign-In (annai_auth)
381
403
  def get_auth_client_id(platform, flavor_name, build_config)
382
404
  config = get_merged_flavor_config(platform, flavor_name)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ann-flavor-flutter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ANN Solutions
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-08-12 00:00:00.000000000 Z
11
+ date: 2026-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fastlane
@@ -89,6 +89,7 @@ files:
89
89
  - lib/fastlane/plugin/ann_flavor_flutter/actions/ann_run_tests_action.rb
90
90
  - lib/fastlane/plugin/ann_flavor_flutter/actions/ann_setup_action.rb
91
91
  - lib/fastlane/plugin/ann_flavor_flutter/actions/ann_upload_to_app_store_action.rb
92
+ - lib/fastlane/plugin/ann_flavor_flutter/actions/ann_upload_to_cloudflare_action.rb
92
93
  - lib/fastlane/plugin/ann_flavor_flutter/actions/ann_upload_to_firebase_action.rb
93
94
  - lib/fastlane/plugin/ann_flavor_flutter/actions/ann_upload_to_play_store_action.rb
94
95
  - lib/fastlane/plugin/ann_flavor_flutter/helper/lanes_android.rb