ann-flavor-cocoapods 0.1.7 → 0.1.8

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: e5bec8aebb837dabd4533a71a696ae1fe9113ddd1d95b3b49eee22ff194405e7
4
- data.tar.gz: 954bab0265c7d86d040fd8966c2da8151b31c3ce3bbafb53b2202bc437e9d07e
3
+ metadata.gz: 65ffc39f0153491522ad751cd067acb59e600473c1aa7ac049b23656f9faafb9
4
+ data.tar.gz: 18ff13f0444baec5a01211ef9f8fb30beae617d55d8d274b74ca297b0966cd89
5
5
  SHA512:
6
- metadata.gz: 495a5336025cad8291ea230a84d4162a5035e0213637bb4de858adea816f08100df2fa586f3200abc29a591e9e8286777931066b5c1c1dc2d7cb94a1811a0012
7
- data.tar.gz: 2bdf9dc302e811bfab9eb0ad502e6d757f1c2fb24e4029c2680f28c66985f0063802d602a82d0d7519f753c2cf2081e091c5a24198c5f4c01c353987a3a77495
6
+ metadata.gz: 9d9db39720735e4d45b88369421bb0f18739dd857d18528370a408e706d71b1765790aff0190c9525b1979202a2b1bed1491df16522c135b482e9810a537485f
7
+ data.tar.gz: d57728fec0711d043df83d72a9ea0766e297568baff91f59ffdc464076d6dedb7e18f4523d39d3836b7e920dbde7d430f0e246ce103b8492011d05d394447df4
data/CHANGELOG.md ADDED
@@ -0,0 +1,63 @@
1
+ # Changelog
2
+
3
+ ## 0.1.8
4
+
5
+ ### Added
6
+ - **DEF-017**: `ann-ios-flavorize setup` CLI command for iOS-only (non-Flutter)
7
+ projects. Checks `annspec.yaml` exists, patches `ios/Podfile` with
8
+ `plugin 'ann-ios-flavorize'`, and prints next-step guidance. Idempotent —
9
+ safe to run multiple times. Install via `gem 'ann-flavor-cocoapods'` in your
10
+ Gemfile, then run `bundle exec ann-ios-flavorize setup`.
11
+
12
+ ### Fixed
13
+ - **DEF-014**: `configure_firebase` now injects build phases in `project_id` mode
14
+ (no `config_file` set). The plist source is derived from
15
+ `lib/generated/firebase/GoogleService-Info-{flavor}-{buildType}.plist`.
16
+ Previously no build phases were injected, causing the Crashlytics dSYM upload
17
+ run script to fail with a missing plist error.
18
+
19
+ ---
20
+
21
+ ## 0.1.7
22
+
23
+ **Backwards-compatibility shims removed** — the deprecated `firebase.file` fallback
24
+ and `firebase_token_file` are no longer accepted. Specs must use `config_file` and
25
+ `service_account` directly.
26
+
27
+ **`service_account` support** — the `service_account` field is read from the spec
28
+ and exported as `GOOGLE_APPLICATION_CREDENTIALS` when invoking Firebase CLI commands.
29
+
30
+ ## 0.1.6
31
+
32
+ **`enabled: false` support** — when `annspec.yaml` sets `enabled: false`, the
33
+ CocoaPods plugin skips all configuration silently.
34
+
35
+ **Improved error messages** — field validation errors now include the exact YAML path
36
+ and a fix hint.
37
+
38
+ ## 0.1.5
39
+
40
+ **Breaking: `annai_app:` root key renamed to `app:`** — `annspec.yaml` files using
41
+ the old `annai_app:` root key must be updated.
42
+
43
+ ## 0.1.4
44
+
45
+ **Firebase `config_file` support** — the podfile helper now reads the `config_file`
46
+ field. The old `file` field is accepted with a deprecation warning; support will be
47
+ removed in a future version.
48
+
49
+ **Firebase `path` / `firebase_app_id` / `build_target` removed** — replaced by
50
+ `config_file` and `project_id`.
51
+
52
+ ## 0.1.3
53
+
54
+ **Schema restructure** — credentials and sdk fields reorganised to match the updated
55
+ `annspec.yaml` structure.
56
+
57
+ ## 0.1.2
58
+
59
+ Documentation and sync-workflow updates. No user-facing behaviour changes.
60
+
61
+ ## 0.1.1
62
+
63
+ Initial release.
@@ -13,6 +13,8 @@ Gem::Specification.new do |spec|
13
13
  spec.license = "MIT"
14
14
 
15
15
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec)/}) }
16
+ spec.bindir = "exe"
17
+ spec.executables = ["ann-ios-flavorize"]
16
18
  spec.require_paths = ["lib"]
17
19
 
18
20
  spec.add_dependency "cocoapods", ">= 1.12"
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'ann_flavor_cocoapods'
5
+
6
+ HELP = <<~HELP
7
+ ann-ios-flavorize — iOS-only setup for ann-flavor-cocoapods
8
+
9
+ Usage:
10
+ bundle exec ann-ios-flavorize setup [--project DIR]
11
+
12
+ Commands:
13
+ setup Patch ios/Podfile to add the ann-ios-flavorize plugin line.
14
+ Run once per project; idempotent on subsequent runs.
15
+
16
+ Options:
17
+ --project DIR Project root directory (default: current directory)
18
+ --help Show this help
19
+ HELP
20
+
21
+ def run(argv)
22
+ command = nil
23
+ root = Dir.pwd
24
+
25
+ i = 0
26
+ while i < argv.length
27
+ case argv[i]
28
+ when 'setup'
29
+ command = :setup
30
+ when '--project'
31
+ i += 1
32
+ root = argv[i] || Dir.pwd
33
+ when '--help', '-h'
34
+ puts HELP
35
+ exit 0
36
+ end
37
+ i += 1
38
+ end
39
+
40
+ if command.nil?
41
+ puts HELP
42
+ exit 1
43
+ end
44
+
45
+ case command
46
+ when :setup
47
+ run_setup(root)
48
+ end
49
+ end
50
+
51
+ def run_setup(root)
52
+ annspec = File.join(root, 'annspec.yaml')
53
+ unless File.exist?(annspec)
54
+ warn "✗ annspec.yaml not found in #{root}"
55
+ warn " Run ann-ios-flavorize from your Flutter project root, or use --project <dir>."
56
+ exit 1
57
+ end
58
+
59
+ podfile_path = File.join(root, 'ios', 'Podfile')
60
+ unless File.exist?(podfile_path)
61
+ warn "✗ ios/Podfile not found in #{root}"
62
+ warn " Create an iOS Flutter project first, then re-run this command."
63
+ exit 1
64
+ end
65
+
66
+ plugin_line = "plugin 'ann-ios-flavorize'"
67
+ content = File.read(podfile_path)
68
+
69
+ if content.include?(plugin_line)
70
+ puts "✓ ios/Podfile already has #{plugin_line} — nothing to do."
71
+ puts "Next: run `bundle exec pod install`"
72
+ return
73
+ end
74
+
75
+ patched = "# Added by ann-flavor-cocoapods — multi-flavor iOS build configuration\n" \
76
+ "#{plugin_line}\n" + content
77
+ File.write(podfile_path, patched)
78
+
79
+ puts "✓ Podfile patched — #{plugin_line} added"
80
+ puts "Next: run `bundle exec pod install`"
81
+ end
82
+
83
+ run(ARGV)
@@ -2,29 +2,67 @@ require_relative 'spec_loader'
2
2
 
3
3
  module AnnFlavorCocoapods
4
4
  module PodfileHelper
5
- # Call inside your Podfile to wire per-flavor Firebase GoogleService-Info.plist.
5
+ # Call inside your Podfile's post_install hook to wire per-flavor Firebase
6
+ # GoogleService-Info.plist into the Xcode build.
6
7
  #
7
8
  # Example Podfile usage:
8
9
  # require 'ann_flavor_cocoapods'
9
- # AnnFlavorCocoapods::PodfileHelper.configure_firebase(self)
10
+ # post_install do |installer|
11
+ # AnnFlavorCocoapods::PodfileHelper.configure_firebase(installer)
12
+ # end
10
13
  #
11
- def self.configure_firebase(installer_context, project_root: Dir.pwd)
12
- spec = SpecLoader.load(project_root)
14
+ # For each iOS flavor that has config_file set, this injects two build phases
15
+ # into the Runner target (or the matching umbrella target):
16
+ # - A pre-build shell script that copies the correct plist to
17
+ # ${SRCROOT}/Runner/GoogleService-Info.plist at build time.
18
+ # - A post-build shell script that removes the copied file so the workspace
19
+ # stays clean and a mis-matched plist cannot accidentally survive.
20
+ #
21
+ # Gate: only runs when integrations.firebase is true in annspec.yaml.
22
+ #
23
+ def self.configure_firebase(installer, project_root: Dir.pwd)
24
+ spec = SpecLoader.load(project_root)
25
+
26
+ # integrations.firebase must be explicitly enabled (REQ-FIRE-00010).
27
+ return unless spec.dig('app', 'integrations', 'firebase') == true
28
+
13
29
  flavors = SpecLoader.ios_flavors(spec)
14
30
  default = SpecLoader.ios_default(spec)
15
31
 
32
+ # Build a map: configuration_name → config_file path.
33
+ # Xcode configuration names are capitalized: Release-<flavor>, Debug-<flavor>.
34
+ config_map = {}
16
35
  flavors.each do |flavor_key, flavor_cfg|
17
36
  merged = default.merge(flavor_cfg || {})
18
37
 
19
- release_fb = merged.dig('build_types', 'release', 'firebase') || {}
20
- debug_fb = merged.dig('build_types', 'debug', 'firebase') || {}
21
- release_file = release_fb['config_file']
22
- debug_file = debug_fb['config_file'] || release_file
38
+ default_build_types = default.dig('build_types') || {}
39
+ flavor_build_types = (flavor_cfg || {}).dig('build_types') || {}
40
+ build_types = default_build_types.merge(flavor_build_types)
41
+
42
+ %w[release debug].each do |bt|
43
+ bt_fb = build_types.dig(bt, 'firebase') || {}
44
+ fb_file = bt_fb['config_file']
45
+ project_id = bt_fb['project_id']
23
46
 
24
- next unless release_file
47
+ if fb_file.nil? && project_id
48
+ # project_id mode — derive plist from stable generated path.
49
+ fb_file = "lib/generated/firebase/GoogleService-Info-#{flavor_key}-#{bt}.plist"
50
+ end
25
51
 
26
- puts "ann-flavor-cocoapods: #{flavor_key} firebase release=#{release_file} debug=#{debug_file}"
52
+ next unless fb_file
53
+
54
+ # Map Xcode config name (e.g. "Release-ledger_in") → plist path
55
+ xcode_config = "#{bt.capitalize}-#{flavor_key}"
56
+ config_map[xcode_config] = fb_file
57
+
58
+ warn_if_static_plist(project_root, xcode_config, fb_file)
59
+ end
27
60
  end
61
+
62
+ return if config_map.empty?
63
+ return if installer.nil?
64
+
65
+ inject_build_phases(installer, project_root, config_map)
28
66
  end
29
67
 
30
68
  # Returns the bundle ID for a given flavor key, merging defaults.
@@ -61,5 +99,76 @@ module AnnFlavorCocoapods
61
99
  bt_suffix = build_types.dig(build_type.to_s, 'id_suffix') || ''
62
100
  "#{base}#{id_suffix}#{bt_suffix}"
63
101
  end
102
+
103
+ private
104
+
105
+ # Warn if a static GoogleService-Info.plist already exists in the project; a
106
+ # higher-priority static file would shadow the one we copy at build time.
107
+ def self.warn_if_static_plist(project_root, xcode_config, config_file)
108
+ static_path = File.join(project_root, 'ios', 'Runner', 'GoogleService-Info.plist')
109
+ if File.exist?(static_path)
110
+ puts "ann-flavor-cocoapods WARNING: A static GoogleService-Info.plist exists at " \
111
+ "ios/Runner/GoogleService-Info.plist. It will be overwritten at build time " \
112
+ "for configuration '#{xcode_config}' (source: #{config_file}). " \
113
+ "Remove the static file to silence this warning."
114
+ end
115
+ end
116
+
117
+ # Injects pre-build and post-build shell script phases into the Runner (or
118
+ # umbrella) target for every native target that matches the project.
119
+ def self.inject_build_phases(installer, project_root, config_map)
120
+ # Build the per-config shell that selects the right source file.
121
+ # Uses a case statement keyed on $CONFIGURATION (Xcode build setting).
122
+ case_body = config_map.map do |xcode_config, plist_path|
123
+ " #{xcode_config}) SRC_PLIST=\"${SRCROOT}/../#{plist_path}\" ;;"
124
+ end.join("\n")
125
+
126
+ pre_build_script = <<~SHELL
127
+ # Injected by ann-flavor-cocoapods: copy per-flavor GoogleService-Info.plist
128
+ case "$CONFIGURATION" in
129
+ #{case_body}
130
+ *) echo "ann-flavor-cocoapods: no Firebase plist for $CONFIGURATION"; exit 0 ;;
131
+ esac
132
+ DEST="${SRCROOT}/Runner/GoogleService-Info.plist"
133
+ if [ ! -f "$SRC_PLIST" ]; then
134
+ echo "ann-flavor-cocoapods ERROR: Firebase plist not found: $SRC_PLIST"
135
+ exit 1
136
+ fi
137
+ cp "$SRC_PLIST" "$DEST"
138
+ echo "ann-flavor-cocoapods: copied $SRC_PLIST -> $DEST"
139
+ SHELL
140
+
141
+ post_build_script = <<~SHELL
142
+ # Injected by ann-flavor-cocoapods: remove copied GoogleService-Info.plist after build
143
+ DEST="${SRCROOT}/Runner/GoogleService-Info.plist"
144
+ rm -f "$DEST"
145
+ echo "ann-flavor-cocoapods: removed $DEST"
146
+ SHELL
147
+
148
+ installer.pods_project.targets.each do |target|
149
+ next unless target.name == 'Pods-Runner'
150
+
151
+ # Avoid duplicate injection on repeated pod install runs.
152
+ next if target.build_phases.any? { |p| p.respond_to?(:shell_script) &&
153
+ p.shell_script.include?('ann-flavor-cocoapods: copy per-flavor') }
154
+
155
+ pre = target.project.new(Xcodeproj::Project::Object::PBXShellScriptBuildPhase)
156
+ pre.name = '[ann-flavor] Copy Firebase plist'
157
+ pre.shell_path = '/bin/sh'
158
+ pre.shell_script = pre_build_script
159
+ pre.show_env_vars_in_log = '0'
160
+ target.build_phases.unshift(pre)
161
+
162
+ post = target.project.new(Xcodeproj::Project::Object::PBXShellScriptBuildPhase)
163
+ post.name = '[ann-flavor] Remove Firebase plist'
164
+ post.shell_path = '/bin/sh'
165
+ post.shell_script = post_build_script
166
+ post.show_env_vars_in_log = '0'
167
+ target.build_phases << post
168
+
169
+ puts "ann-flavor-cocoapods: injected Firebase build phases into #{target.name} " \
170
+ "(#{config_map.size} configuration(s))"
171
+ end
172
+ end
64
173
  end
65
174
  end
@@ -1,3 +1,3 @@
1
1
  module AnnFlavorCocoapods
2
- VERSION = "0.1.7"
2
+ VERSION = "0.1.8"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ann-flavor-cocoapods
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - ANN Solutions
8
8
  autorequire:
9
- bindir: bin
9
+ bindir: exe
10
10
  cert_chain: []
11
- date: 2026-07-01 00:00:00.000000000 Z
11
+ date: 2026-07-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cocoapods
@@ -55,14 +55,17 @@ dependencies:
55
55
  description:
56
56
  email:
57
57
  - support@annaibrands.com
58
- executables: []
58
+ executables:
59
+ - ann-ios-flavorize
59
60
  extensions: []
60
61
  extra_rdoc_files: []
61
62
  files:
62
63
  - ".gitignore"
64
+ - CHANGELOG.md
63
65
  - Gemfile
64
66
  - Rakefile
65
67
  - ann-flavor-cocoapods.gemspec
68
+ - exe/ann-ios-flavorize
66
69
  - lib/ann_flavor_cocoapods.rb
67
70
  - lib/ann_flavor_cocoapods/podfile_helper.rb
68
71
  - lib/ann_flavor_cocoapods/spec_loader.rb