ann-flavor-cocoapods 0.2.0 → 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: 68dcf959d5d5f927bc43085645168885f76ac29cff73dc973fa9d0d5a8809beb
4
- data.tar.gz: 9d214c8beb19db807200490e5a66e4bc6b22b20539cc950abbf316bb059f3ca7
3
+ metadata.gz: 0b4a6fb9baad3561d14ffa73da2e419ebbaa0664edf12924875484973e5e5f21
4
+ data.tar.gz: a88e357ca0d0fdf957a3640229d7f98a852a058d3ea442f5333d8c6f02c1393c
5
5
  SHA512:
6
- metadata.gz: dfd4c329b80c085c88607557aaa9027793f7a43c680751019608acdfb16d930a71ed9dc7d464a5ea7e2beb608af88148661ac3324083ea40aa8d370b0787b7dd
7
- data.tar.gz: a0c24e164cc92f315382be34c593a6326151db80e33b69f473b7fbb3a3bb19431b6520e4357b7475ea39c0ec573d1a3b3b40ec6486c73fbf2eb872f967191748
6
+ metadata.gz: a156d31659cb317a3e114adda24d44048e0a8c28bf781f23b50f3821a8e0eb84e19aabf2ac57beec9da4d3e7cc192ab3182acf8a4f95c5177eb8ff3445298df0
7
+ data.tar.gz: 7c4b22bba78a36cb28338c38152e2126ea9b9d92007e633d1b6684bb40b2db1a30ca5f7ac74ad0855e31a3ae65bcca98210d3e6d8c2f98cac7664dbded19ee18
data/CHANGELOG.md CHANGED
@@ -1,5 +1,66 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Added
6
+ - **New `[ann-flavor] Merge Entitlements` build phase composes the final
7
+ `.entitlements` file Xcode signs with at build time**, not sync time: copies
8
+ `ios/Runner/Runner.entitlements` (the generic/base layer, developer-owned —
9
+ if present; unlike `Runner/Info.plist`, most projects have no stock
10
+ entitlements file at all, so a missing base file here starts from an empty
11
+ plist rather than failing), then merges in every key from the current
12
+ flavor's `ann/Entitlements/<flavor>.entitlements` delta (see
13
+ `ann_flutter_flavor` 1.3.0 — that file now holds only the flavor's
14
+ `entitlements:` cascade, not a full copy of `Runner.entitlements`), with the
15
+ flavor's value winning on any key both define. Uses `PlistBuddy`, mirroring
16
+ `[ann-flavor] Merge Info.plist` (0.3.0) exactly — entitlements files and
17
+ Info.plist are both plain Apple XML property lists, so the same
18
+ type-preserving merge logic applies unchanged. Injected only when at least
19
+ one flavor actually needs entitlements (its own cascade, or a stock
20
+ `Runner.entitlements`); removed automatically once nothing needs it anymore.
21
+
22
+ ### Changed
23
+ - **`CODE_SIGN_ENTITLEMENTS` now points at
24
+ `$(DERIVED_FILE_DIR)/Entitlements-merged.entitlements`** (the Merge
25
+ Entitlements phase's output above) instead of directly at
26
+ `ann/Entitlements/<flavor>.entitlements`, whenever anything needs signing —
27
+ extends the same `INFOPLIST_FILE` rewiring from 0.3.0 to entitlements. See
28
+ [plan 039](../../docs/03-planning/features/039-entitlements-build-time-merge.md)
29
+ and the [Entitlements](../../docs/04-userguide/ios/overview.md#entitlements)
30
+ section of the iOS userguide for the full design.
31
+
32
+ ## 0.3.0
33
+
34
+ ### Added
35
+ - **New `[ann-flavor] Merge Info.plist` build phase composes the final
36
+ Info.plist Xcode builds against at build time**, not sync time: copies
37
+ `ios/Runner/Info.plist` (the generic/base layer, developer-owned, always
38
+ present) to `$(DERIVED_FILE_DIR)/Info-merged.plist`, then merges in every
39
+ key from the current flavor's `ios/ann/Info/Info-<flavor>.plist` delta (see
40
+ `ann_flutter_flavor` 1.2.0 — that file now holds only the flavor's
41
+ `info_plist:` cascade, not a full copy of `Runner/Info.plist`), with the
42
+ flavor's value winning on any key both define. Uses `PlistBuddy`
43
+ (`/usr/libexec/PlistBuddy`), not `python3`/`ruby` — ships as part of Xcode
44
+ itself, so this phase adds no dependency on any scripting runtime beyond
45
+ what Xcode already requires. Every plist value type (string, bool, integer,
46
+ real, array, nested dict — e.g. `SKAdNetworkItems`) round-trips correctly
47
+ through the merge, not just strings. Prints a one-line notice to the Xcode
48
+ build log for any key the flavor delta actually overrides, to make an
49
+ unintentional override easy to spot. Found-and-updated in place on a
50
+ re-run, same UUID-stability guarantee as the Firebase-copy and Print Build
51
+ Info phases.
52
+
53
+ ### Changed
54
+ - **`INFOPLIST_FILE` now points at `$(DERIVED_FILE_DIR)/Info-merged.plist`**
55
+ (the Merge Info.plist phase's output above) instead of directly at
56
+ `ios/ann/Info/Info-<flavor>.plist`. Enables `Runner/Info.plist` to remain a
57
+ live, always-consulted base layer instead of a one-time sync-time template
58
+ — the change this whole release exists to support. See
59
+ [plan 038](../../docs/03-planning/features/038-info-plist-build-time-merge.md)
60
+ and the [`Info.plist`
61
+ keys](../../docs/04-userguide/ios/overview.md#infoplist-keys) section of the
62
+ iOS userguide for the full design and migration notes.
63
+
3
64
  ## 0.2.0
4
65
 
5
66
  ### Added
@@ -155,6 +155,7 @@ module AnnFlavorCocoapods
155
155
  user_project.native_targets.each do |target|
156
156
  if target.symbol_type == :application
157
157
  find_or_create_icon_catalog(target, user_project)
158
+ target_has_entitlements = false
158
159
 
159
160
  ios.flavors.each_key do |flavor_key|
160
161
  flavor = ios.flavors[flavor_key]
@@ -174,24 +175,32 @@ module AnnFlavorCocoapods
174
175
  # until checked against what the xcconfig #include actually
175
176
  # applied).
176
177
  xcconfig_path = "ann/xcconfig/#{flavor_key}#{bt.capitalize}.xcconfig"
177
- info_plist_path = "ann/Info/Info-#{flavor_key}.plist"
178
178
  icon_name = "#{flavor_key.capitalize}AppIcon"
179
179
 
180
180
  assert_icon_generated!(user_project, flavor_key, icon_name)
181
181
 
182
- # Unlike the icon catalog and Info.plist, entitlements: is
183
- # opt-in -- ios_generator.dart's _generateEntitlements skips
184
- # creating ios/ann/Entitlements/ entirely when no annspec.yaml
185
- # entitlements: block and no stock Runner.entitlements exist
186
- # anywhere. Only set CODE_SIGN_ENTITLEMENTS when the file
187
- # actually exists on disk; leaving it unset is correct (no
188
- # entitlements to sign with), not an error condition.
189
- entitlements_path = "ann/Entitlements/#{flavor_key}.entitlements"
190
- entitlements_file = File.join(user_project.project_dir.to_s, 'ann', 'Entitlements', "#{flavor_key}.entitlements")
191
- entitlements_path = nil unless File.exist?(entitlements_file)
182
+ # Extended opt-in check (plan 039): CODE_SIGN_ENTITLEMENTS is
183
+ # wired only when there is something to sign with at all --
184
+ # either this flavor's entitlements: cascade, or a stock
185
+ # Runner.entitlements to fall back to. Mirrors
186
+ # ios_generator.dart's _anyFlavorHasEntitlements OR-condition,
187
+ # but evaluated here instead of at sync time -- the actual
188
+ # merge now happens at this (pod install / build time) layer,
189
+ # not sync time, so this is where "is there anything to sign
190
+ # with" needs to be answered from. The Merge Entitlements
191
+ # phase itself is injected once per target below (like Merge
192
+ # Info.plist), not per flavor/build-type here -- it derives
193
+ # $FLAVOR_NAME from $CONFIGURATION at build time, so one
194
+ # shared phase correctly serves every flavor, whether or not
195
+ # that specific flavor has its own entitlements: cascade.
196
+ flavor_entitlements_file = File.join(user_project.project_dir.to_s, 'ann', 'Entitlements', "#{flavor_key}.entitlements")
197
+ stock_entitlements_file = File.join(user_project.project_dir.to_s, 'Runner', 'Runner.entitlements')
198
+ has_entitlements = File.exist?(flavor_entitlements_file) || File.exist?(stock_entitlements_file)
199
+ entitlements_path = has_entitlements ? '$(DERIVED_FILE_DIR)/Entitlements-merged.entitlements' : nil
200
+ target_has_entitlements ||= has_entitlements
192
201
 
193
202
  configure_build_configuration(
194
- target, user_project, xcode_config, xcconfig_path, info_plist_path,
203
+ target, user_project, xcode_config, xcconfig_path,
195
204
  icon_name, entitlements_path
196
205
  )
197
206
  end
@@ -202,6 +211,19 @@ module AnnFlavorCocoapods
202
211
  if print_build_info
203
212
  inject_build_info_phase(target)
204
213
  end
214
+
215
+ inject_merge_info_plist_phase(target)
216
+
217
+ # Unlike Merge Info.plist (unconditional -- every flavor always
218
+ # has an Info.plist), Merge Entitlements is only injected when at
219
+ # least one flavor actually needs it (its own cascade, or a
220
+ # stock Runner.entitlements) -- entitlements are opt-in overall,
221
+ # and a phase with nothing to sign would just be dead weight.
222
+ if target_has_entitlements
223
+ inject_merge_entitlements_phase(target)
224
+ else
225
+ remove_merge_entitlements_phase(target)
226
+ end
205
227
  end
206
228
 
207
229
  # Fixes a real pod-install failure: PBXNativeTarget#resolved_build_setting
@@ -598,7 +620,7 @@ module AnnFlavorCocoapods
598
620
  CODE_SIGN_ENTITLEMENTS
599
621
  ].freeze
600
622
 
601
- def self.configure_build_configuration(target, user_project, xcode_config, xcconfig_path, info_plist_path, icon_name, entitlements_path = nil)
623
+ def self.configure_build_configuration(target, user_project, xcode_config, xcconfig_path, icon_name, entitlements_path = nil)
602
624
  config_list = target.build_configuration_list
603
625
  config = config_list[xcode_config]
604
626
 
@@ -624,7 +646,12 @@ module AnnFlavorCocoapods
624
646
  user_project.main_group.new_reference(xcconfig_path)
625
647
  config.base_configuration_reference = xcconfig_ref
626
648
 
627
- config.build_settings['INFOPLIST_FILE'] = info_plist_path
649
+ # Points at the [ann-flavor] Merge Info.plist run-script phase's output
650
+ # (plan 038), not directly at ann/Info/Info-<flavor>.plist -- that file
651
+ # now holds only the flavor's info_plist: cascade delta, not a complete
652
+ # plist; the merge phase composes it with Runner/Info.plist (base) at
653
+ # build time and writes the result here.
654
+ config.build_settings['INFOPLIST_FILE'] = '$(DERIVED_FILE_DIR)/Info-merged.plist'
628
655
  config.build_settings['ASSETCATALOG_COMPILER_APPICON_NAME'] = icon_name
629
656
  # entitlements: is opt-in (unlike INFOPLIST_FILE/the icon catalog, which
630
657
  # every flavor always has) -- only set CODE_SIGN_ENTITLEMENTS when the
@@ -769,5 +796,229 @@ module AnnFlavorCocoapods
769
796
  target.build_phases.unshift(phase)
770
797
  end
771
798
  end
799
+
800
+ # Composes the final Info.plist Xcode actually builds against, from two
801
+ # layers -- Runner/Info.plist (generic/base, developer-owned) and the
802
+ # flavor-specific ann/Info/Info-<flavor>.plist (annspec.yaml's info_plist:
803
+ # cascade, opt-in -- may not exist for a flavor with no cascade) -- with
804
+ # the flavor layer winning on any key collision. Plan 038: since plan 035,
805
+ # ios_generator.dart's _generateInfoPlists used to bake a full copy of
806
+ # Runner/Info.plist into every flavor's generated plist at sync time; that
807
+ # made the generated file impossible to read as "what's actually
808
+ # flavor-specific" and gave nothing else (e.g. a CocoaPods plugin like
809
+ # ann_ads) any way to contribute Info.plist keys, since INFOPLIST_FILE
810
+ # pointed at the fully-resolved flavor file and Runner/Info.plist was
811
+ # never read again. This phase moves that composition to build time
812
+ # instead, so Runner/Info.plist stays a live, always-consulted base layer.
813
+ #
814
+ # Uses PlistBuddy (/usr/libexec/PlistBuddy), not python3/ruby -- PlistBuddy
815
+ # ships as part of Xcode itself, so this phase has no dependency on any
816
+ # scripting runtime beyond what Xcode already requires (OQ-1,
817
+ # docs/03-planning/features/038-info-plist-build-time-merge.md).
818
+ #
819
+ # Runs before_compile, ahead of Xcode's own Info.plist processing --
820
+ # INFOPLIST_FILE (set in configure_build_configuration) points at this
821
+ # phase's output, not at either source file directly.
822
+ def self.inject_merge_info_plist_phase(target)
823
+ script = <<~SHELL
824
+ # Injected by ann-flavor-cocoapods: compose the final Info.plist from
825
+ # two layers -- Runner/Info.plist (generic/base) and the flavor-
826
+ # specific ann/Info/Info-<flavor>.plist (annspec.yaml's info_plist:
827
+ # cascade) -- with the flavor layer winning on any key collision.
828
+ # See docs/03-planning/features/038-info-plist-build-time-merge.md.
829
+ PLISTBUDDY=/usr/libexec/PlistBuddy
830
+ BASE="${SRCROOT}/Runner/Info.plist"
831
+ FLAVOR_NAME="${CONFIGURATION#*-}"
832
+ FLAVOR_PLIST="${SRCROOT}/ann/Info/Info-${FLAVOR_NAME}.plist"
833
+ OUT="${DERIVED_FILE_DIR}/Info-merged.plist"
834
+
835
+ if [ ! -f "$BASE" ]; then
836
+ echo "error: [ann-flavor] Runner/Info.plist not found at $BASE"
837
+ exit 1
838
+ fi
839
+
840
+ # Start from a full copy of the base layer -- every key
841
+ # Runner/Info.plist already has is correct by default; only the
842
+ # flavor delta's keys need explicit merging in below.
843
+ cp "$BASE" "$OUT"
844
+
845
+ if [ ! -f "$FLAVOR_PLIST" ]; then
846
+ echo "warning: [ann-flavor] no flavor Info.plist found for '$FLAVOR_NAME' at $FLAVOR_PLIST -- using Runner/Info.plist unmerged."
847
+ exit 0
848
+ fi
849
+
850
+ # Enumerate the flavor delta's top-level keys. PlistBuddy "Print" with
851
+ # no key path dumps the whole dict; grep the top-level "Key = " lines
852
+ # (four leading spaces = depth 1 in PlistBuddy's indented dump format).
853
+ FLAVOR_KEYS=$("$PLISTBUDDY" -c "Print" "$FLAVOR_PLIST" | grep -E '^ [A-Za-z]' | sed 's/ =.*//' | sed 's/^ *//')
854
+
855
+ for KEY in $FLAVOR_KEYS; do
856
+ # OQ-2: warn (non-fatal) only on a genuine collision -- Runner/Info.plist
857
+ # already had this key, with a different value, before the flavor delta
858
+ # is applied. A net-new key (not previously in Runner/Info.plist) is not
859
+ # a collision and stays silent -- that's the expected, common case.
860
+ # Compared via PlistBuddy's human-readable Print output, which is
861
+ # good enough for equality-detection purposes even though it can't
862
+ # reliably tell types apart (see the VALUE_XML extraction below,
863
+ # which is what actually determines the merged value/type).
864
+ BASE_VALUE=$("$PLISTBUDDY" -c "Print :$KEY" "$OUT" 2>/dev/null)
865
+ BASE_HAD_KEY=$?
866
+ FLAVOR_VALUE=$("$PLISTBUDDY" -c "Print :$KEY" "$FLAVOR_PLIST")
867
+
868
+ if [ "$BASE_HAD_KEY" -eq 0 ] && [ "$BASE_VALUE" != "$FLAVOR_VALUE" ]; then
869
+ echo "[ann-flavor] Info.plist: '$KEY' overridden by flavor value ($FLAVOR_NAME) -- Runner/Info.plist's value ignored for this build."
870
+ fi
871
+
872
+ # Extract the flavor value's own XML fragment (e.g. <string>x</string>,
873
+ # <true/>, <integer>42</integer>, <array>...</array>) via PlistBuddy's
874
+ # -x (XML) output, scoped to just this key, stripping the <plist
875
+ # version="1.0">...</plist> wrapper that -x always adds. This
876
+ # preserves the flavor delta's real plist type -- Print's
877
+ # human-readable output alone can't distinguish an integer 42 from
878
+ # the string "42", or a bool true from the string "true", so a
879
+ # scalar-vs-composite TYPE guess from Print was unreliable and has
880
+ # been replaced with this approach (verified against a real
881
+ # PlistBuddy: bool/int/real/string/array/dict all round-trip
882
+ # correctly this way, plan 038 STEP-02).
883
+ VALUE_XML=$("$PLISTBUDDY" -x -c "Print :$KEY" "$FLAVOR_PLIST" | sed -n '/<plist version/,/<\\/plist>/p' | sed '1d;$d')
884
+
885
+ "$PLISTBUDDY" -c "Delete :$KEY" "$OUT" >/dev/null 2>&1
886
+ FRAGMENT=$(mktemp /tmp/ann-flavor-plist-fragment.XXXXXX)
887
+ cat > "$FRAGMENT" <<XMLEOF
888
+ <?xml version="1.0" encoding="UTF-8"?>
889
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
890
+ <plist version="1.0">
891
+ <dict>
892
+ <key>$KEY</key>
893
+ $VALUE_XML
894
+ </dict>
895
+ </plist>
896
+ XMLEOF
897
+ "$PLISTBUDDY" -c "Merge \\"$FRAGMENT\\"" "$OUT"
898
+ rm -f "$FRAGMENT"
899
+ done
900
+ SHELL
901
+
902
+ # Find-or-update in place, rather than delete+recreate -- same
903
+ # idempotency rationale as inject_build_info_phase above.
904
+ existing = target.build_phases.find { |p| p.respond_to?(:name) && p.name == '[ann-flavor] Merge Info.plist' }
905
+ phase = existing || target.project.new(Xcodeproj::Project::Object::PBXShellScriptBuildPhase)
906
+ phase.name = '[ann-flavor] Merge Info.plist'
907
+ phase.shell_path = '/bin/sh'
908
+ phase.shell_script = script
909
+ phase.show_env_vars_in_log = '0'
910
+ # Declares this phase's output so Xcode's build system understands the
911
+ # dependency (same technique already used for the Firebase-copy phase's
912
+ # output_paths) -- suppresses "input file not found" for INFOPLIST_FILE
913
+ # referencing a path nothing else produces until this phase runs.
914
+ phase.output_paths = ['$(DERIVED_FILE_DIR)/Info-merged.plist']
915
+
916
+ target.build_phases.unshift(phase) unless existing
917
+ end
918
+
919
+ # Composes the final .entitlements file Xcode actually signs with, from
920
+ # two layers -- Runner.entitlements (generic/base, developer-owned, may
921
+ # not exist at all) and the flavor-specific ann/Entitlements/<flavor>
922
+ # .entitlements delta (annspec.yaml's entitlements: cascade, opt-in --
923
+ # may also not exist) -- with the flavor layer winning on any key
924
+ # collision. Plan 039: mirrors inject_merge_info_plist_phase (plan 038)
925
+ # exactly -- same PlistBuddy-based merge (entitlements files and Info.plist
926
+ # are both plain Apple XML property lists; PlistBuddy has no notion of
927
+ # file "kind" beyond that), same find-or-update idempotency, same
928
+ # collision-warning behavior. The one real difference: unlike
929
+ # Runner/Info.plist (every Xcode project has one), most projects have no
930
+ # Runner.entitlements at all -- a missing base file here is a normal,
931
+ # non-fatal case (starts from an empty plist dict), not the fatal error
932
+ # Merge Info.plist treats a missing Runner/Info.plist as.
933
+ def self.inject_merge_entitlements_phase(target)
934
+ script = <<~SHELL
935
+ # Injected by ann-flavor-cocoapods: compose the final .entitlements file
936
+ # from two layers -- Runner.entitlements (generic/base, may not exist)
937
+ # and the flavor-specific ann/Entitlements/<flavor>.entitlements delta
938
+ # (annspec.yaml's entitlements: cascade, may also not exist) -- with the
939
+ # flavor layer winning on any key collision. Mirrors [ann-flavor] Merge
940
+ # Info.plist (plan 038) -- see plan 039 for why entitlements needed the
941
+ # same redesign.
942
+ PLISTBUDDY=/usr/libexec/PlistBuddy
943
+ BASE="${SRCROOT}/Runner/Runner.entitlements"
944
+ FLAVOR_NAME="${CONFIGURATION#*-}"
945
+ FLAVOR_FILE="${SRCROOT}/ann/Entitlements/${FLAVOR_NAME}.entitlements"
946
+ OUT="${DERIVED_FILE_DIR}/Entitlements-merged.entitlements"
947
+
948
+ if [ -f "$BASE" ]; then
949
+ cp "$BASE" "$OUT"
950
+ else
951
+ # No stock Runner.entitlements -- start from an empty plist dict
952
+ # rather than failing; the flavor delta (if any) is still applied
953
+ # below.
954
+ cat > "$OUT" <<PLISTEOF
955
+ <?xml version="1.0" encoding="UTF-8"?>
956
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
957
+ <plist version="1.0">
958
+ <dict/>
959
+ </plist>
960
+ PLISTEOF
961
+ fi
962
+
963
+ if [ ! -f "$FLAVOR_FILE" ]; then
964
+ echo "[ann-flavor] Entitlements: no flavor delta found for '$FLAVOR_NAME' at $FLAVOR_FILE -- using Runner.entitlements unmerged."
965
+ exit 0
966
+ fi
967
+
968
+ FLAVOR_KEYS=$("$PLISTBUDDY" -c "Print" "$FLAVOR_FILE" | grep -E '^ [A-Za-z]' | sed 's/ =.*//' | sed 's/^ *//')
969
+
970
+ for KEY in $FLAVOR_KEYS; do
971
+ BASE_VALUE=$("$PLISTBUDDY" -c "Print :$KEY" "$OUT" 2>/dev/null)
972
+ BASE_HAD_KEY=$?
973
+ FLAVOR_VALUE=$("$PLISTBUDDY" -c "Print :$KEY" "$FLAVOR_FILE")
974
+
975
+ if [ "$BASE_HAD_KEY" -eq 0 ] && [ "$BASE_VALUE" != "$FLAVOR_VALUE" ]; then
976
+ echo "[ann-flavor] Entitlements: '$KEY' overridden by flavor value ($FLAVOR_NAME) -- Runner.entitlements' value ignored for this build."
977
+ fi
978
+
979
+ VALUE_XML=$("$PLISTBUDDY" -x -c "Print :$KEY" "$FLAVOR_FILE" | sed -n '/<plist version/,/<\\/plist>/p' | sed '1d;$d')
980
+
981
+ "$PLISTBUDDY" -c "Delete :$KEY" "$OUT" >/dev/null 2>&1
982
+ FRAGMENT=$(mktemp /tmp/ann-flavor-entitlements-fragment.XXXXXX)
983
+ cat > "$FRAGMENT" <<XMLEOF
984
+ <?xml version="1.0" encoding="UTF-8"?>
985
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
986
+ <plist version="1.0">
987
+ <dict>
988
+ <key>$KEY</key>
989
+ $VALUE_XML
990
+ </dict>
991
+ </plist>
992
+ XMLEOF
993
+ "$PLISTBUDDY" -c "Merge \\"$FRAGMENT\\"" "$OUT"
994
+ rm -f "$FRAGMENT"
995
+ done
996
+ SHELL
997
+
998
+ existing = target.build_phases.find { |p| p.respond_to?(:name) && p.name == '[ann-flavor] Merge Entitlements' }
999
+ phase = existing || target.project.new(Xcodeproj::Project::Object::PBXShellScriptBuildPhase)
1000
+ phase.name = '[ann-flavor] Merge Entitlements'
1001
+ phase.shell_path = '/bin/sh'
1002
+ phase.shell_script = script
1003
+ phase.show_env_vars_in_log = '0'
1004
+ phase.output_paths = ['$(DERIVED_FILE_DIR)/Entitlements-merged.entitlements']
1005
+
1006
+ target.build_phases.unshift(phase) unless existing
1007
+ end
1008
+
1009
+ # Removes a previously-injected Merge Entitlements phase when no flavor
1010
+ # needs entitlements anymore (annspec.yaml's entitlements: cascade was
1011
+ # removed, and no stock Runner.entitlements exists either) -- entitlements
1012
+ # are opt-in overall, so a stale phase with nothing to sign would be dead
1013
+ # weight left in project.pbxproj. Uses ObjectList#delete (not plain
1014
+ # Array#reject!/select), same rationale as the stale "[ann-flavor] Remove
1015
+ # Firebase plist" cleanup above -- plain array removal only drops the
1016
+ # phase from target.build_phases, leaving the object itself permanently
1017
+ # orphaned in the project's global objects table.
1018
+ def self.remove_merge_entitlements_phase(target)
1019
+ target.build_phases.select do |p|
1020
+ p.respond_to?(:name) && p.name == '[ann-flavor] Merge Entitlements'
1021
+ end.each { |p| target.build_phases.delete(p) }
1022
+ end
772
1023
  end
773
1024
  end
@@ -1,3 +1,3 @@
1
1
  module AnnFlavorCocoapods
2
- VERSION = "0.2.0"
2
+ VERSION = "0.4.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ann-flavor-cocoapods
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ANN Solutions