ann-flavor-flutter 0.4.5 → 0.4.6
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 +4 -4
- data/.gitignore +5 -0
- data/CHANGELOG.md +5 -0
- data/lib/ann_flavor_flutter/version.rb +1 -1
- data/lib/ann_flavor_flutter.rb +8 -0
- data/lib/vendor/ann_flavor_core/lib/ann_flavor_core/ios_resolution.rb +37 -0
- data/lib/vendor/ann_flavor_core/lib/ann_flavor_core/model.rb +196 -0
- data/lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb +378 -0
- data/lib/vendor/ann_flavor_core/lib/ann_flavor_core/resolver.rb +266 -0
- data/lib/vendor/ann_flavor_core/lib/ann_flavor_core/strict_mode_checker.rb +136 -0
- data/lib/vendor/ann_flavor_core/lib/ann_flavor_core/version.rb +3 -0
- data/lib/vendor/ann_flavor_core/lib/ann_flavor_core.rb +6 -0
- metadata +10 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2884061122825135d9f07bb18db889d23509649848aca1fca2c45b63b9b8c3bd
|
|
4
|
+
data.tar.gz: 5ed44c51bcf6c29195763aaa2615f4bb59b71a5b0908f26fdbc83677419273a4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8635f03da486e2aa4da6a7c9ed423d76260624ff5710a58c4a174951ecf2922bd874e4ff197edcb551db650ae7ef3fc76725d4f58935a076785f0c3f203e3a81
|
|
7
|
+
data.tar.gz: 0e5a13e1e4c03ddf24b31bb7b7c07fd330934213601b7a373408fdb051b8cfb2bb0bf298a9ad254df1ede039315b4871f6b2ff44a90818c7574a7361f2f197b6
|
data/.gitignore
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
/pkg/
|
|
2
2
|
Gemfile.lock
|
|
3
|
+
|
|
4
|
+
# Vendored ann_flavor_core source, generated transiently during publish (DEF-058).
|
|
5
|
+
# Never committed for real — CI/`make publish-fastlane` `git add -f` it briefly
|
|
6
|
+
# so `gem build`'s `git ls-files`-based file list includes it, then discard it.
|
|
7
|
+
/lib/vendor/
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.6
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- Loading this gem (`require 'ann_flavor_flutter'` / `require 'fastlane/plugin/ann_flavor_flutter'`) failed with `cannot load such file -- ann_flavor_core` — the 0.4.5 core-delegation change added a hard `require 'ann_flavor_core'` in `utils_spec_loader.rb`, but `ann_flavor_core` is intentionally never published to RubyGems (ADR-008). The published gem now vendors `ann_flavor_core`'s source and resolves it automatically; local monorepo development is unaffected. ([#58](https://github.com/anntech-dev/ann-flavor-tooling/issues/58))
|
|
7
|
+
|
|
3
8
|
## 0.4.5
|
|
4
9
|
|
|
5
10
|
### Changed
|
data/lib/ann_flavor_flutter.rb
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# This file is loaded by require 'annai-flutter-flavor'
|
|
2
2
|
|
|
3
|
+
# The published gem vendors ann_flavor_core's source under lib/vendor/ (ADR-008:
|
|
4
|
+
# ann_flavor_core is never published to RubyGems, only path/git dependencies).
|
|
5
|
+
# In the monorepo this directory doesn't exist, and ann_flavor_core resolves via
|
|
6
|
+
# the real gem installed by the local Gemfile's `path:` dependency instead.
|
|
7
|
+
vendored_core_lib = File.expand_path('vendor/ann_flavor_core/lib', __dir__)
|
|
8
|
+
$LOAD_PATH.unshift(vendored_core_lib) if File.directory?(vendored_core_lib) &&
|
|
9
|
+
!$LOAD_PATH.include?(vendored_core_lib)
|
|
10
|
+
|
|
3
11
|
# Load the version (Good Practice)
|
|
4
12
|
require 'ann_flavor_flutter/version'
|
|
5
13
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require_relative 'resolver'
|
|
2
|
+
|
|
3
|
+
module AnnFlavorCore
|
|
4
|
+
# Shared iOS bundle-ID / Firebase-path resolution — used by both
|
|
5
|
+
# ann-flavor-cocoapods and ann-flavor-fastlane so the two plugins compute
|
|
6
|
+
# identical effective values instead of independently re-deriving them
|
|
7
|
+
# (plan-030 Phase 6 / REQ-CDEL-00070).
|
|
8
|
+
module IosResolution
|
|
9
|
+
# Effective bundle ID for a flavor at a specific build type. Always applies
|
|
10
|
+
# the full cascade (default → flavor → build_type suffix) — there is no
|
|
11
|
+
# build-type-less variant; callers must pass the actual build type they are
|
|
12
|
+
# assembling for.
|
|
13
|
+
#
|
|
14
|
+
# An unknown flavor key falls back to the unsuffixed default id (matches
|
|
15
|
+
# pre-consolidation behavior — never raises for a missing flavor).
|
|
16
|
+
def self.bundle_id(ios_platform, flavor_key, build_type)
|
|
17
|
+
flavor = ios_platform.flavors[flavor_key.to_s] || AnnFlavorCore::IosFlavor.defaults
|
|
18
|
+
Resolver.resolve_ios_flavor(flavor, ios_platform.default, build_type.to_s).effective_id
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Effective Firebase config file path for a flavor at a specific build
|
|
22
|
+
# type. Full 4-level cascade via the core resolver (config_file,
|
|
23
|
+
# project_id, service_account, target). When only project_id is set (no
|
|
24
|
+
# config_file), synthesizes the stable generated-plist path convention:
|
|
25
|
+
# #{generated_dir}/GoogleService-Info-#{flavor_key}-#{build_type}.plist
|
|
26
|
+
#
|
|
27
|
+
# Returns nil when neither config_file nor project_id is set.
|
|
28
|
+
def self.firebase_config_path(ios_platform, flavor_key, build_type, generated_dir: 'lib/generated/firebase')
|
|
29
|
+
flavor = ios_platform.flavors[flavor_key.to_s] || AnnFlavorCore::IosFlavor.defaults
|
|
30
|
+
resolved = Resolver.resolve_ios_flavor(flavor, ios_platform.default, build_type.to_s)
|
|
31
|
+
fb = resolved.effective_firebase
|
|
32
|
+
return nil if fb.nil?
|
|
33
|
+
|
|
34
|
+
fb.config_file || (fb.project_id && "#{generated_dir}/GoogleService-Info-#{flavor_key}-#{build_type}.plist")
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
module AnnFlavorCore
|
|
2
|
+
# ── dart_defines ────────────────────────────────────────────────────────────
|
|
3
|
+
#
|
|
4
|
+
# compile/run/deploy are reserved action-scope sub-keys (REQ-DDEF-00015).
|
|
5
|
+
# +common+ holds keys declared directly under dart_defines — they apply to every
|
|
6
|
+
# action. Each action Hash is resolved as an addition on top of +common+, never a
|
|
7
|
+
# replacement.
|
|
8
|
+
DartDefines = Struct.new(:common, :compile, :run, :deploy, keyword_init: true) do
|
|
9
|
+
def self.defaults = new(common: {}, compile: {}, run: {}, deploy: {})
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# ── Top-level ───────────────────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
AnnSpec = Struct.new(:app, :enabled, :debug_config, :tooling, keyword_init: true) do
|
|
15
|
+
def self.defaults = new(app: AnnApp.new, enabled: true, debug_config: DebugConfig.new)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
AnnApp = Struct.new(:android, :ios, :web, :windows, :general, :integrations, keyword_init: true)
|
|
19
|
+
|
|
20
|
+
# ── Tooling (REQ-TOOL-00010 — top-level, non-cascading) ──────────────────────
|
|
21
|
+
|
|
22
|
+
ToolingConfig = Struct.new(:gradle_plugin, :cocoapods_plugin, :fastlane_plugin, keyword_init: true)
|
|
23
|
+
|
|
24
|
+
# ── Integrations ─────────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
AnnIntegrations = Struct.new(:fastlane, :melos, :firebase, keyword_init: true) do
|
|
27
|
+
def self.defaults = new(fastlane: false, melos: false, firebase: false)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# ── Android ─────────────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
AndroidPlatform = Struct.new(:default, :flavors, keyword_init: true) do
|
|
33
|
+
def self.defaults = new(default: AndroidDefault.new, flavors: {})
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
AndroidDefault = Struct.new(
|
|
37
|
+
:id, :name, :version_name, :version_code, :main_file,
|
|
38
|
+
:admob, :icon, :sdk, :credentials, :firebase, :build_types, :custom, :dart_defines,
|
|
39
|
+
keyword_init: true
|
|
40
|
+
) do
|
|
41
|
+
def self.defaults = new(build_types: {}, custom: {}, dart_defines: DartDefines.defaults)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
AndroidFlavor = Struct.new(
|
|
45
|
+
:id, :id_suffix, :name, :name_suffix, :version_name, :version_code,
|
|
46
|
+
:main_file, :admob, :icon, :firebase, :stores, :credentials, :build_types, :custom, :dart_defines,
|
|
47
|
+
keyword_init: true
|
|
48
|
+
) do
|
|
49
|
+
def self.defaults = new(id_suffix: '', name_suffix: '', build_types: {}, custom: {}, dart_defines: DartDefines.defaults)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
AndroidSdk = Struct.new(:min_sdk, :compile_sdk, :target_sdk, keyword_init: true)
|
|
53
|
+
|
|
54
|
+
AndroidCredentials = Struct.new(:signing, :google_play, :samsung_galaxy, :amazon, keyword_init: true)
|
|
55
|
+
AndroidSigning = Struct.new(:key_file, keyword_init: true)
|
|
56
|
+
GooglePlayCredentials = Struct.new(:api_key, keyword_init: true)
|
|
57
|
+
SamsungGalaxyCredentials = Struct.new(:seller_id, :api_key, keyword_init: true)
|
|
58
|
+
AmazonCredentials = Struct.new(:client_id, :client_secret, keyword_init: true)
|
|
59
|
+
|
|
60
|
+
# ── iOS ─────────────────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
IosPlatform = Struct.new(:default, :flavors, keyword_init: true) do
|
|
63
|
+
def self.defaults = new(default: IosDefault.new, flavors: {})
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
IosDefault = Struct.new(
|
|
67
|
+
:id, :name, :version_name, :version_code, :main_file,
|
|
68
|
+
:admob, :icon, :credentials, :firebase, :build_types, :custom, :dart_defines,
|
|
69
|
+
keyword_init: true
|
|
70
|
+
) do
|
|
71
|
+
def self.defaults = new(build_types: {}, custom: {}, dart_defines: DartDefines.defaults)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
IosFlavor = Struct.new(
|
|
75
|
+
:id, :id_suffix, :name, :name_suffix, :version_name, :version_code,
|
|
76
|
+
:main_file, :admob, :icon, :firebase, :stores, :credentials, :build_types, :custom, :dart_defines,
|
|
77
|
+
keyword_init: true
|
|
78
|
+
) do
|
|
79
|
+
def self.defaults = new(id_suffix: '', name_suffix: '', build_types: {}, custom: {}, dart_defines: DartDefines.defaults)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
IosCredentials = Struct.new(:signing, :app_store, keyword_init: true)
|
|
83
|
+
IosSigning = Struct.new(:team_id, keyword_init: true)
|
|
84
|
+
AppStoreCredentials = Struct.new(
|
|
85
|
+
:api_key, :export_options_plist, :export_options_team_id,
|
|
86
|
+
:export_options_signing_certificate,
|
|
87
|
+
keyword_init: true
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# ── Web ─────────────────────────────────────────────────────────────────────
|
|
91
|
+
|
|
92
|
+
WebPlatform = Struct.new(:default, :flavors, keyword_init: true) do
|
|
93
|
+
def self.defaults = new(default: WebDefault.new, flavors: {})
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
CloudflareConfig = Struct.new(:project_name, :account_id, :branch, :mode, keyword_init: true)
|
|
97
|
+
|
|
98
|
+
WebDefault = Struct.new(
|
|
99
|
+
:id, :name, :version_name, :version_code, :main_file, :icon, :cloudflare, :firebase, :build_types, :custom, :dart_defines,
|
|
100
|
+
keyword_init: true
|
|
101
|
+
) do
|
|
102
|
+
def self.defaults = new(build_types: {}, custom: {}, dart_defines: DartDefines.defaults)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
WebFlavor = Struct.new(
|
|
106
|
+
:id, :id_suffix, :name, :name_suffix, :version_name, :version_code,
|
|
107
|
+
:main_file, :icon, :cloudflare, :firebase, :build_types, :custom, :dart_defines,
|
|
108
|
+
keyword_init: true
|
|
109
|
+
) do
|
|
110
|
+
def self.defaults = new(id_suffix: '', name_suffix: '', build_types: {}, custom: {}, dart_defines: DartDefines.defaults)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# ── Windows ─────────────────────────────────────────────────────────────────
|
|
114
|
+
|
|
115
|
+
WindowsPlatform = Struct.new(:default, :flavors, keyword_init: true) do
|
|
116
|
+
def self.defaults = new(default: WindowsDefault.new, flavors: {})
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
WindowsDefault = Struct.new(
|
|
120
|
+
:id, :name, :version_name, :version_code, :main_file, :firebase, :build_types, :custom, :dart_defines,
|
|
121
|
+
keyword_init: true
|
|
122
|
+
) do
|
|
123
|
+
def self.defaults = new(build_types: {}, custom: {}, dart_defines: DartDefines.defaults)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
WindowsFlavor = Struct.new(
|
|
127
|
+
:id, :id_suffix, :name, :name_suffix, :version_name, :version_code,
|
|
128
|
+
:main_file, :firebase, :build_types, :custom, :dart_defines,
|
|
129
|
+
keyword_init: true
|
|
130
|
+
) do
|
|
131
|
+
def self.defaults = new(id_suffix: '', name_suffix: '', build_types: {}, custom: {}, dart_defines: DartDefines.defaults)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# ── Shared types ─────────────────────────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
AdmobConfig = Struct.new(:gms_ads_id, keyword_init: true)
|
|
137
|
+
|
|
138
|
+
BuildTypeConfig = Struct.new(
|
|
139
|
+
:id_suffix, :name_suffix,
|
|
140
|
+
:firebase, :auth, :admob,
|
|
141
|
+
:minify_enabled, :shrink_resources, :lint_check_release_builds,
|
|
142
|
+
:ndk_version, :ndk_debug_symbol_level, :ndk_abi_filters, :custom, :dart_defines,
|
|
143
|
+
keyword_init: true
|
|
144
|
+
) do
|
|
145
|
+
def self.defaults = new(id_suffix: '', name_suffix: '', ndk_abi_filters: [], custom: {}, dart_defines: DartDefines.defaults)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
FirebaseConfig = Struct.new(:project_id, :config_file, :service_account, :target, keyword_init: true)
|
|
149
|
+
|
|
150
|
+
AuthConfig = Struct.new(:client_id, :reversed_client_id, :others, keyword_init: true) do
|
|
151
|
+
def self.defaults = new(others: {})
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
FlavorStores = Struct.new(:google_play, :samsung_galaxy, :amazon, :app_store, keyword_init: true)
|
|
155
|
+
GooglePlayFlavorStore = Struct.new(:priority, keyword_init: true)
|
|
156
|
+
SamsungGalaxyFlavorStore = Struct.new(:app_id, keyword_init: true)
|
|
157
|
+
AmazonFlavorStore = Struct.new(:app_id, keyword_init: true)
|
|
158
|
+
AppStoreFlavorStore = Struct.new(:apple_id, :team_id, keyword_init: true)
|
|
159
|
+
|
|
160
|
+
class GeneralConfig
|
|
161
|
+
def self.defaults = new
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
DebugConfig = Struct.new(
|
|
165
|
+
:print_debug, :print_build_and_flavor_info, :print_sdk_versions, :print_release_build_type_info,
|
|
166
|
+
keyword_init: true
|
|
167
|
+
) do
|
|
168
|
+
def self.defaults
|
|
169
|
+
new(
|
|
170
|
+
print_debug: false,
|
|
171
|
+
print_build_and_flavor_info: true,
|
|
172
|
+
print_sdk_versions: true,
|
|
173
|
+
print_release_build_type_info: false
|
|
174
|
+
)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# ── Resolved output types ────────────────────────────────────────────────────
|
|
179
|
+
|
|
180
|
+
ResolvedSpec = Struct.new(:android, :ios, :web, :windows, keyword_init: true)
|
|
181
|
+
ResolvedFlavor = Struct.new(:flavor_name, :by_build_type, keyword_init: true)
|
|
182
|
+
|
|
183
|
+
ResolvedBuildOutput = Struct.new(
|
|
184
|
+
:flavor_name, :build_type,
|
|
185
|
+
:effective_id, :effective_name,
|
|
186
|
+
:effective_version_name, :effective_version_code,
|
|
187
|
+
:effective_main_file,
|
|
188
|
+
:effective_firebase, :effective_auth,
|
|
189
|
+
:effective_gms_ads_id,
|
|
190
|
+
:effective_icon,
|
|
191
|
+
:effective_cloudflare,
|
|
192
|
+
:effective_custom,
|
|
193
|
+
:effective_dart_defines,
|
|
194
|
+
keyword_init: true
|
|
195
|
+
)
|
|
196
|
+
end
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
require_relative 'model'
|
|
3
|
+
|
|
4
|
+
module AnnFlavorCore
|
|
5
|
+
#
|
|
6
|
+
# Single source of truth for parsing annspec.yaml.
|
|
7
|
+
#
|
|
8
|
+
# Two entry points:
|
|
9
|
+
# AnnFlavorCore::Parser.parse(yaml_string) — from raw YAML text
|
|
10
|
+
# AnnFlavorCore::Parser.parse_file(path) — from a file path
|
|
11
|
+
# AnnFlavorCore::Parser.from_hash(hash) — from a pre-parsed Hash
|
|
12
|
+
# (e.g. in-memory UI data)
|
|
13
|
+
#
|
|
14
|
+
# After parsing, pass the AnnSpec to AnnFlavorCore::Resolver.resolve_all
|
|
15
|
+
# to obtain all resolved (effective) values per flavor × build-type.
|
|
16
|
+
#
|
|
17
|
+
module Parser
|
|
18
|
+
def self.parse(yaml_string)
|
|
19
|
+
raw = YAML.safe_load(yaml_string, aliases: true) || {}
|
|
20
|
+
from_hash(raw)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.parse_file(path)
|
|
24
|
+
parse(File.read(path))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.from_hash(raw)
|
|
28
|
+
app_hash = h(raw, 'app')
|
|
29
|
+
tooling_hash = h(raw, 'tooling')
|
|
30
|
+
AnnSpec.new(
|
|
31
|
+
app: parse_ann_app(app_hash),
|
|
32
|
+
enabled: raw.fetch('enabled', true),
|
|
33
|
+
debug_config: parse_debug_config(h(raw, 'debug')),
|
|
34
|
+
tooling: (parse_tooling(tooling_hash) unless tooling_hash.empty?),
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.parse_custom_config(m)
|
|
39
|
+
return {} unless m.is_a?(Hash)
|
|
40
|
+
m.transform_values do |group_val|
|
|
41
|
+
next {} unless group_val.is_a?(Hash)
|
|
42
|
+
group_val.transform_values { |v| normalize_custom_value(v) }
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.normalize_custom_value(v)
|
|
47
|
+
return v.map { |e| e.nil? ? '' : e.to_s } if v.is_a?(Array)
|
|
48
|
+
v
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def self.parse_tooling(m)
|
|
52
|
+
ToolingConfig.new(
|
|
53
|
+
gradle_plugin: m['gradle_plugin'],
|
|
54
|
+
cocoapods_plugin: m['cocoapods_plugin'],
|
|
55
|
+
fastlane_plugin: m['fastlane_plugin'],
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# ── AnnApp ────────────────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
def self.parse_ann_app(m)
|
|
62
|
+
AnnApp.new(
|
|
63
|
+
android: (parse_android(h(m, 'android')) unless h(m, 'android').empty?),
|
|
64
|
+
ios: (parse_ios(h(m, 'ios')) unless h(m, 'ios').empty?),
|
|
65
|
+
web: (parse_web(h(m, 'web')) unless h(m, 'web').empty?),
|
|
66
|
+
windows: (parse_windows(h(m, 'windows')) unless h(m, 'windows').empty?),
|
|
67
|
+
general: (parse_general(h(m, 'general')) unless h(m, 'general').empty?),
|
|
68
|
+
integrations: (parse_integrations(h(m, 'integrations')) unless h(m, 'integrations').empty?),
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self.parse_integrations(m)
|
|
73
|
+
AnnIntegrations.new(
|
|
74
|
+
fastlane: m.fetch('fastlane', false),
|
|
75
|
+
melos: m.fetch('melos', false),
|
|
76
|
+
firebase: m.fetch('firebase', false),
|
|
77
|
+
)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# ── Android ───────────────────────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
def self.parse_android(m)
|
|
83
|
+
AndroidPlatform.new(
|
|
84
|
+
default: parse_android_default(h(m, 'default')),
|
|
85
|
+
flavors: parse_flavors(h(m, 'flavor')) { |v| parse_android_flavor(v) },
|
|
86
|
+
)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def self.parse_android_default(m)
|
|
90
|
+
AndroidDefault.new(
|
|
91
|
+
id: m['id'],
|
|
92
|
+
name: m['name'],
|
|
93
|
+
version_name: m['version_name']&.to_s,
|
|
94
|
+
version_code: m['version_code']&.to_i,
|
|
95
|
+
main_file: m['main_file'],
|
|
96
|
+
admob: (AdmobConfig.new(gms_ads_id: h(m, 'admob')['gms_ads_id'] || m['gms_ads_id']) unless (h(m, 'admob')['gms_ads_id'] || m['gms_ads_id']).nil?),
|
|
97
|
+
icon: m['icon'],
|
|
98
|
+
sdk: (parse_android_sdk(h(m, 'sdk')) unless h(m, 'sdk').empty?),
|
|
99
|
+
credentials: (parse_android_credentials(h(m, 'credentials')) unless h(m, 'credentials').empty?),
|
|
100
|
+
firebase: (parse_firebase_config(h(m, 'firebase')) unless h(m, 'firebase').empty?),
|
|
101
|
+
build_types: parse_build_type_configs(h(m, 'build_types')),
|
|
102
|
+
custom: parse_custom_config(h(m, 'custom')),
|
|
103
|
+
dart_defines: parse_dart_defines(h(m, 'dart_defines')),
|
|
104
|
+
)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def self.parse_android_flavor(m)
|
|
108
|
+
AndroidFlavor.new(
|
|
109
|
+
id: m['id'],
|
|
110
|
+
id_suffix: m.fetch('id_suffix', '').to_s,
|
|
111
|
+
name: m['name'],
|
|
112
|
+
name_suffix: m.fetch('name_suffix', '').to_s,
|
|
113
|
+
version_name: m['version_name']&.to_s,
|
|
114
|
+
version_code: m['version_code']&.to_i,
|
|
115
|
+
main_file: m['main_file'],
|
|
116
|
+
admob: (AdmobConfig.new(gms_ads_id: h(m, 'admob')['gms_ads_id'] || m['gms_ads_id']) unless (h(m, 'admob')['gms_ads_id'] || m['gms_ads_id']).nil?),
|
|
117
|
+
icon: m['icon'],
|
|
118
|
+
firebase: (parse_firebase_config(h(m, 'firebase')) unless h(m, 'firebase').empty?),
|
|
119
|
+
stores: (parse_flavor_stores(h(m, 'stores')) unless h(m, 'stores').empty?),
|
|
120
|
+
credentials: (parse_android_credentials(h(m, 'credentials')) unless h(m, 'credentials').empty?),
|
|
121
|
+
build_types: parse_build_type_configs(h(m, 'build_types')),
|
|
122
|
+
custom: parse_custom_config(h(m, 'custom')),
|
|
123
|
+
dart_defines: parse_dart_defines(h(m, 'dart_defines')),
|
|
124
|
+
)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def self.parse_android_sdk(m)
|
|
128
|
+
AndroidSdk.new(min_sdk: m['minSdk'], compile_sdk: m['compileSdk'], target_sdk: m['targetSdk'])
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def self.parse_android_credentials(m)
|
|
132
|
+
AndroidCredentials.new(
|
|
133
|
+
signing: (AndroidSigning.new(key_file: h(m, 'signing')['key_file']) unless h(m, 'signing').empty?),
|
|
134
|
+
google_play: (GooglePlayCredentials.new(api_key: h(m, 'google_play')['api_key']) unless h(m, 'google_play').empty?),
|
|
135
|
+
samsung_galaxy: (SamsungGalaxyCredentials.new(seller_id: h(m, 'samsung_galaxy')['seller_id'],
|
|
136
|
+
api_key: h(m, 'samsung_galaxy')['api_key']) unless h(m, 'samsung_galaxy').empty?),
|
|
137
|
+
amazon: (AmazonCredentials.new(client_id: h(m, 'amazon')['client_id'],
|
|
138
|
+
client_secret: h(m, 'amazon')['client_secret']) unless h(m, 'amazon').empty?),
|
|
139
|
+
)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# ── iOS ───────────────────────────────────────────────────────────────────
|
|
143
|
+
|
|
144
|
+
def self.parse_ios(m)
|
|
145
|
+
IosPlatform.new(
|
|
146
|
+
default: parse_ios_default(h(m, 'default')),
|
|
147
|
+
flavors: parse_flavors(h(m, 'flavor')) { |v| parse_ios_flavor(v) },
|
|
148
|
+
)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def self.parse_ios_default(m)
|
|
152
|
+
IosDefault.new(
|
|
153
|
+
id: m['id'],
|
|
154
|
+
name: m['name'],
|
|
155
|
+
version_name: m['version_name']&.to_s,
|
|
156
|
+
version_code: m['version_code']&.to_i,
|
|
157
|
+
main_file: m['main_file'],
|
|
158
|
+
admob: (AdmobConfig.new(gms_ads_id: h(m, 'admob')['gms_ads_id'] || m['gms_ads_id']) unless (h(m, 'admob')['gms_ads_id'] || m['gms_ads_id']).nil?),
|
|
159
|
+
icon: m['icon'],
|
|
160
|
+
credentials: (parse_ios_credentials(h(m, 'credentials')) unless h(m, 'credentials').empty?),
|
|
161
|
+
firebase: (parse_firebase_config(h(m, 'firebase')) unless h(m, 'firebase').empty?),
|
|
162
|
+
build_types: parse_build_type_configs(h(m, 'build_types')),
|
|
163
|
+
custom: parse_custom_config(h(m, 'custom')),
|
|
164
|
+
dart_defines: parse_dart_defines(h(m, 'dart_defines')),
|
|
165
|
+
)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def self.parse_ios_flavor(m)
|
|
169
|
+
IosFlavor.new(
|
|
170
|
+
id: m['id'],
|
|
171
|
+
id_suffix: m.fetch('id_suffix', '').to_s,
|
|
172
|
+
name: m['name'],
|
|
173
|
+
name_suffix: m.fetch('name_suffix', '').to_s,
|
|
174
|
+
version_name: m['version_name']&.to_s,
|
|
175
|
+
version_code: m['version_code']&.to_i,
|
|
176
|
+
main_file: m['main_file'],
|
|
177
|
+
admob: (AdmobConfig.new(gms_ads_id: h(m, 'admob')['gms_ads_id'] || m['gms_ads_id']) unless (h(m, 'admob')['gms_ads_id'] || m['gms_ads_id']).nil?),
|
|
178
|
+
icon: m['icon'],
|
|
179
|
+
firebase: (parse_firebase_config(h(m, 'firebase')) unless h(m, 'firebase').empty?),
|
|
180
|
+
stores: (parse_flavor_stores(h(m, 'stores')) unless h(m, 'stores').empty?),
|
|
181
|
+
credentials: (parse_ios_credentials(h(m, 'credentials')) unless h(m, 'credentials').empty?),
|
|
182
|
+
build_types: parse_build_type_configs(h(m, 'build_types')),
|
|
183
|
+
custom: parse_custom_config(h(m, 'custom')),
|
|
184
|
+
dart_defines: parse_dart_defines(h(m, 'dart_defines')),
|
|
185
|
+
)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def self.parse_ios_credentials(m)
|
|
189
|
+
IosCredentials.new(
|
|
190
|
+
signing: (IosSigning.new(team_id: h(m, 'signing')['team_id']) unless h(m, 'signing').empty?),
|
|
191
|
+
app_store: (AppStoreCredentials.new(
|
|
192
|
+
api_key: h(m, 'app_store')['api_key'],
|
|
193
|
+
export_options_plist: h(m, 'app_store')['export_options_plist'],
|
|
194
|
+
export_options_team_id: h(m, 'app_store')['export_options_team_id'],
|
|
195
|
+
export_options_signing_certificate: h(m, 'app_store')['export_options_signing_certificate'],
|
|
196
|
+
) unless h(m, 'app_store').empty?),
|
|
197
|
+
)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# ── Web ───────────────────────────────────────────────────────────────────
|
|
201
|
+
|
|
202
|
+
def self.parse_web(m)
|
|
203
|
+
WebPlatform.new(
|
|
204
|
+
default: parse_web_default(h(m, 'default')),
|
|
205
|
+
flavors: parse_flavors(h(m, 'flavor')) { |v| parse_web_flavor(v) },
|
|
206
|
+
)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def self.parse_web_default(m)
|
|
210
|
+
WebDefault.new(
|
|
211
|
+
id: m['id'], name: m['name'],
|
|
212
|
+
version_name: m['version_name']&.to_s, version_code: m['version_code']&.to_i,
|
|
213
|
+
main_file: m['main_file'], icon: m['icon'],
|
|
214
|
+
cloudflare: parse_cloudflare(h(m, 'cloudflare')),
|
|
215
|
+
firebase: (parse_firebase_config(h(m, 'firebase')) unless h(m, 'firebase').empty?),
|
|
216
|
+
build_types: parse_build_type_configs(h(m, 'build_types')),
|
|
217
|
+
custom: parse_custom_config(h(m, 'custom')),
|
|
218
|
+
dart_defines: parse_dart_defines(h(m, 'dart_defines')),
|
|
219
|
+
)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def self.parse_web_flavor(m)
|
|
223
|
+
WebFlavor.new(
|
|
224
|
+
id: m['id'], id_suffix: m.fetch('id_suffix', '').to_s,
|
|
225
|
+
name: m['name'], name_suffix: m.fetch('name_suffix', '').to_s,
|
|
226
|
+
version_name: m['version_name']&.to_s, version_code: m['version_code']&.to_i,
|
|
227
|
+
main_file: m['main_file'], icon: m['icon'],
|
|
228
|
+
cloudflare: parse_cloudflare(h(m, 'cloudflare')),
|
|
229
|
+
firebase: (parse_firebase_config(h(m, 'firebase')) unless h(m, 'firebase').empty?),
|
|
230
|
+
build_types: parse_build_type_configs(h(m, 'build_types')),
|
|
231
|
+
custom: parse_custom_config(h(m, 'custom')),
|
|
232
|
+
dart_defines: parse_dart_defines(h(m, 'dart_defines')),
|
|
233
|
+
)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def self.parse_cloudflare(m)
|
|
237
|
+
return nil if m.empty?
|
|
238
|
+
CloudflareConfig.new(
|
|
239
|
+
project_name: m['project_name'],
|
|
240
|
+
account_id: m['account_id'],
|
|
241
|
+
branch: m['branch'],
|
|
242
|
+
mode: m['mode'],
|
|
243
|
+
)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# ── Windows ───────────────────────────────────────────────────────────────
|
|
247
|
+
|
|
248
|
+
def self.parse_windows(m)
|
|
249
|
+
WindowsPlatform.new(
|
|
250
|
+
default: parse_windows_default(h(m, 'default')),
|
|
251
|
+
flavors: parse_flavors(h(m, 'flavor')) { |v| parse_windows_flavor(v) },
|
|
252
|
+
)
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def self.parse_windows_default(m)
|
|
256
|
+
WindowsDefault.new(
|
|
257
|
+
id: m['id'], name: m['name'],
|
|
258
|
+
version_name: m['version_name']&.to_s, version_code: m['version_code']&.to_i,
|
|
259
|
+
main_file: m['main_file'],
|
|
260
|
+
firebase: (parse_firebase_config(h(m, 'firebase')) unless h(m, 'firebase').empty?),
|
|
261
|
+
build_types: parse_build_type_configs(h(m, 'build_types')),
|
|
262
|
+
custom: parse_custom_config(h(m, 'custom')),
|
|
263
|
+
dart_defines: parse_dart_defines(h(m, 'dart_defines')),
|
|
264
|
+
)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def self.parse_windows_flavor(m)
|
|
268
|
+
WindowsFlavor.new(
|
|
269
|
+
id: m['id'], id_suffix: m.fetch('id_suffix', '').to_s,
|
|
270
|
+
name: m['name'], name_suffix: m.fetch('name_suffix', '').to_s,
|
|
271
|
+
version_name: m['version_name']&.to_s, version_code: m['version_code']&.to_i,
|
|
272
|
+
main_file: m['main_file'],
|
|
273
|
+
firebase: (parse_firebase_config(h(m, 'firebase')) unless h(m, 'firebase').empty?),
|
|
274
|
+
build_types: parse_build_type_configs(h(m, 'build_types')),
|
|
275
|
+
custom: parse_custom_config(h(m, 'custom')),
|
|
276
|
+
dart_defines: parse_dart_defines(h(m, 'dart_defines')),
|
|
277
|
+
)
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# ── Shared parsers ────────────────────────────────────────────────────────
|
|
281
|
+
|
|
282
|
+
def self.parse_firebase_config(m)
|
|
283
|
+
FirebaseConfig.new(
|
|
284
|
+
project_id: m['project_id'],
|
|
285
|
+
config_file: m['config_file'],
|
|
286
|
+
service_account: m['service_account'],
|
|
287
|
+
target: m['target'],
|
|
288
|
+
)
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def self.parse_auth_config(m)
|
|
292
|
+
AuthConfig.new(
|
|
293
|
+
client_id: m['clientId'],
|
|
294
|
+
reversed_client_id: m['reversedClientId'],
|
|
295
|
+
others: h(m, 'others'),
|
|
296
|
+
)
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def self.parse_build_type_configs(m)
|
|
300
|
+
m.transform_values do |v|
|
|
301
|
+
v = v.is_a?(Hash) ? v : {}
|
|
302
|
+
BuildTypeConfig.new(
|
|
303
|
+
id_suffix: v.fetch('id_suffix', '').to_s,
|
|
304
|
+
name_suffix: v.fetch('name_suffix', '').to_s,
|
|
305
|
+
firebase: (parse_firebase_config(h(v, 'firebase')) unless h(v, 'firebase').empty?),
|
|
306
|
+
auth: (parse_auth_config(h(v, 'auth')) unless h(v, 'auth').empty?),
|
|
307
|
+
admob: (AdmobConfig.new(gms_ads_id: h(v, 'admob')['gms_ads_id']) unless h(v, 'admob').empty?),
|
|
308
|
+
minify_enabled: v['minifyEnabled'],
|
|
309
|
+
shrink_resources: v['shrinkResources'],
|
|
310
|
+
lint_check_release_builds: v['lintCheckReleaseBuilds'],
|
|
311
|
+
ndk_version: v['ndkVersion'],
|
|
312
|
+
ndk_debug_symbol_level: v['ndkDebugSymbolLevel'],
|
|
313
|
+
ndk_abi_filters: (v['ndkAbiFilters'].is_a?(Array) ? v['ndkAbiFilters'] : []),
|
|
314
|
+
custom: parse_custom_config(h(v, 'custom')),
|
|
315
|
+
dart_defines: parse_dart_defines(h(v, 'dart_defines')),
|
|
316
|
+
)
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def self.parse_flavor_stores(m)
|
|
321
|
+
FlavorStores.new(
|
|
322
|
+
google_play: (GooglePlayFlavorStore.new(priority: h(m, 'google_play')['priority']) unless h(m, 'google_play').empty?),
|
|
323
|
+
samsung_galaxy: (SamsungGalaxyFlavorStore.new(app_id: h(m, 'samsung_galaxy')['app_id']) unless h(m, 'samsung_galaxy').empty?),
|
|
324
|
+
amazon: (AmazonFlavorStore.new(app_id: h(m, 'amazon')['app_id']) unless h(m, 'amazon').empty?),
|
|
325
|
+
app_store: (AppStoreFlavorStore.new(apple_id: h(m, 'app_store')['apple_id'],
|
|
326
|
+
team_id: h(m, 'app_store')['team_id']) unless h(m, 'app_store').empty?),
|
|
327
|
+
)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def self.parse_general(_m)
|
|
331
|
+
GeneralConfig.new
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def self.parse_debug_config(m)
|
|
335
|
+
DebugConfig.new(
|
|
336
|
+
print_debug: m.fetch('printDebug', false),
|
|
337
|
+
print_build_and_flavor_info: m.fetch('printBuildAndFlavorInfo', true),
|
|
338
|
+
print_sdk_versions: m.fetch('printSdkVersions', true),
|
|
339
|
+
print_release_build_type_info: m.fetch('printReleaseBuildTypeInfo', false),
|
|
340
|
+
)
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def self.parse_flavors(flavor_map, &parse_fn)
|
|
344
|
+
return {} unless flavor_map.is_a?(Hash)
|
|
345
|
+
flavor_map.transform_values { |v| parse_fn.call(v.is_a?(Hash) ? v : {}) }
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
# Safe Hash accessor — returns {} when key is missing or value is not a Hash
|
|
349
|
+
def self.h(map, key)
|
|
350
|
+
val = map.is_a?(Hash) ? map[key] : nil
|
|
351
|
+
val.is_a?(Hash) ? val : {}
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
DART_DEFINES_ACTION_KEYS = %w[compile run deploy].freeze
|
|
355
|
+
|
|
356
|
+
# Parses dart_defines: — plain string entries are the common map; the three
|
|
357
|
+
# reserved sub-keys compile/run/deploy (each a nested map) become the
|
|
358
|
+
# corresponding action scope. Values are coerced to strings.
|
|
359
|
+
def self.parse_dart_defines(m)
|
|
360
|
+
return DartDefines.defaults unless m.is_a?(Hash)
|
|
361
|
+
|
|
362
|
+
string_map = ->(raw) {
|
|
363
|
+
return {} unless raw.is_a?(Hash)
|
|
364
|
+
raw.transform_values { |v| v.nil? ? '' : v.to_s }
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
common = m.reject { |k, _| DART_DEFINES_ACTION_KEYS.include?(k) }
|
|
368
|
+
.transform_values { |v| v.nil? ? '' : v.to_s }
|
|
369
|
+
|
|
370
|
+
DartDefines.new(
|
|
371
|
+
common: common,
|
|
372
|
+
compile: string_map.call(m['compile']),
|
|
373
|
+
run: string_map.call(m['run']),
|
|
374
|
+
deploy: string_map.call(m['deploy']),
|
|
375
|
+
)
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
end
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
require_relative 'model'
|
|
2
|
+
|
|
3
|
+
module AnnFlavorCore
|
|
4
|
+
module Resolver
|
|
5
|
+
STANDARD_BUILD_TYPES = %w[debug release profile].freeze
|
|
6
|
+
|
|
7
|
+
# ── Low-level cascade helpers (used by all plugins) ───────────────────────
|
|
8
|
+
|
|
9
|
+
def self.effective_build_type_suffix(flavor_suffix, default_suffix)
|
|
10
|
+
flavor_suffix.to_s.empty? ? default_suffix.to_s : flavor_suffix.to_s
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.resolve_id(base_id, id_suffix, build_type_id_suffix = '')
|
|
14
|
+
base_id.to_s + id_suffix.to_s + build_type_id_suffix.to_s
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.resolve_name(base_name, name_suffix, build_type_name_suffix = '')
|
|
18
|
+
base_name.to_s + name_suffix.to_s + build_type_name_suffix.to_s
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# ── High-level resolver ───────────────────────────────────────────────────
|
|
22
|
+
#
|
|
23
|
+
# Usage patterns:
|
|
24
|
+
#
|
|
25
|
+
# # From YAML (batch — all platforms, all build types)
|
|
26
|
+
# spec = AnnFlavorCore::Parser.parse_file('annspec.yaml')
|
|
27
|
+
# resolved = AnnFlavorCore::Resolver.resolve_all(spec)
|
|
28
|
+
#
|
|
29
|
+
# # From in-memory model (e.g. live UI data before saving to YAML)
|
|
30
|
+
# output = AnnFlavorCore::Resolver.resolve_android_flavor(flavor, defaults, 'debug')
|
|
31
|
+
|
|
32
|
+
def self.resolve_all(spec, build_types: STANDARD_BUILD_TYPES)
|
|
33
|
+
ResolvedSpec.new(
|
|
34
|
+
android: spec.app&.android ? resolve_android(spec.app.android, build_types: build_types) : {},
|
|
35
|
+
ios: spec.app&.ios ? resolve_ios(spec.app.ios, build_types: build_types) : {},
|
|
36
|
+
web: spec.app&.web ? resolve_web(spec.app.web, build_types: build_types) : {},
|
|
37
|
+
windows: spec.app&.windows ? resolve_windows(spec.app.windows, build_types: build_types) : {},
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.resolve_android(platform, build_types: STANDARD_BUILD_TYPES)
|
|
42
|
+
platform.flavors.each_with_object({}) do |(name, flavor), result|
|
|
43
|
+
result[name] = ResolvedFlavor.new(
|
|
44
|
+
flavor_name: name,
|
|
45
|
+
by_build_type: build_types.each_with_object({}) { |bt, h| h[bt] = resolve_android_flavor(flavor, platform.default, bt) },
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.resolve_ios(platform, build_types: STANDARD_BUILD_TYPES)
|
|
51
|
+
platform.flavors.each_with_object({}) do |(name, flavor), result|
|
|
52
|
+
result[name] = ResolvedFlavor.new(
|
|
53
|
+
flavor_name: name,
|
|
54
|
+
by_build_type: build_types.each_with_object({}) { |bt, h| h[bt] = resolve_ios_flavor(flavor, platform.default, bt) },
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.resolve_web(platform, build_types: STANDARD_BUILD_TYPES)
|
|
60
|
+
platform.flavors.each_with_object({}) do |(name, flavor), result|
|
|
61
|
+
result[name] = ResolvedFlavor.new(
|
|
62
|
+
flavor_name: name,
|
|
63
|
+
by_build_type: build_types.each_with_object({}) { |bt, h| h[bt] = resolve_web_flavor(flavor, platform.default, bt) },
|
|
64
|
+
)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def self.resolve_windows(platform, build_types: STANDARD_BUILD_TYPES)
|
|
69
|
+
platform.flavors.each_with_object({}) do |(name, flavor), result|
|
|
70
|
+
result[name] = ResolvedFlavor.new(
|
|
71
|
+
flavor_name: name,
|
|
72
|
+
by_build_type: build_types.each_with_object({}) { |bt, h| h[bt] = resolve_windows_flavor(flavor, platform.default, bt) },
|
|
73
|
+
)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# ── Per-flavor resolvers (callable directly from UI / in-memory data) ─────
|
|
78
|
+
|
|
79
|
+
def self.resolve_android_flavor(flavor, defaults, build_type)
|
|
80
|
+
bt = effective_bt_config(flavor.build_types, defaults.build_types, build_type)
|
|
81
|
+
ResolvedBuildOutput.new(
|
|
82
|
+
flavor_name: nil,
|
|
83
|
+
build_type: build_type,
|
|
84
|
+
effective_id: resolve_id((flavor.id || defaults.id).to_s, flavor.id_suffix.to_s, bt.id_suffix),
|
|
85
|
+
effective_name: resolve_name((flavor.name || defaults.name).to_s, flavor.name_suffix.to_s, bt.name_suffix),
|
|
86
|
+
effective_version_name: (flavor.version_name || defaults.version_name).to_s,
|
|
87
|
+
effective_version_code: flavor.version_code || defaults.version_code,
|
|
88
|
+
effective_main_file: flavor.main_file || defaults.main_file,
|
|
89
|
+
effective_firebase: resolve_firebase(flavor.build_types[build_type]&.firebase, flavor.firebase, defaults.build_types[build_type]&.firebase, defaults.firebase),
|
|
90
|
+
effective_auth: bt.auth,
|
|
91
|
+
effective_gms_ads_id: flavor.admob&.gms_ads_id || defaults.admob&.gms_ads_id,
|
|
92
|
+
effective_icon: flavor.icon || defaults.icon,
|
|
93
|
+
effective_custom: resolve_custom(defaults.custom, defaults.build_types[build_type]&.custom, flavor.custom, flavor.build_types[build_type]&.custom),
|
|
94
|
+
effective_dart_defines: resolve_dart_defines(defaults.dart_defines, defaults.build_types[build_type]&.dart_defines, flavor.dart_defines, flavor.build_types[build_type]&.dart_defines),
|
|
95
|
+
)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def self.resolve_ios_flavor(flavor, defaults, build_type)
|
|
99
|
+
bt = effective_bt_config(flavor.build_types, defaults.build_types, build_type)
|
|
100
|
+
ResolvedBuildOutput.new(
|
|
101
|
+
flavor_name: nil,
|
|
102
|
+
build_type: build_type,
|
|
103
|
+
effective_id: resolve_id((flavor.id || defaults.id).to_s, flavor.id_suffix.to_s, bt.id_suffix),
|
|
104
|
+
effective_name: resolve_name((flavor.name || defaults.name).to_s, flavor.name_suffix.to_s, bt.name_suffix),
|
|
105
|
+
effective_version_name: (flavor.version_name || defaults.version_name).to_s,
|
|
106
|
+
effective_version_code: flavor.version_code || defaults.version_code,
|
|
107
|
+
effective_main_file: flavor.main_file || defaults.main_file,
|
|
108
|
+
effective_firebase: resolve_firebase(flavor.build_types[build_type]&.firebase, flavor.firebase, defaults.build_types[build_type]&.firebase, defaults.firebase),
|
|
109
|
+
effective_auth: bt.auth,
|
|
110
|
+
effective_gms_ads_id: flavor.admob&.gms_ads_id || defaults.admob&.gms_ads_id,
|
|
111
|
+
effective_icon: flavor.icon || defaults.icon,
|
|
112
|
+
effective_custom: resolve_custom(defaults.custom, defaults.build_types[build_type]&.custom, flavor.custom, flavor.build_types[build_type]&.custom),
|
|
113
|
+
effective_dart_defines: resolve_dart_defines(defaults.dart_defines, defaults.build_types[build_type]&.dart_defines, flavor.dart_defines, flavor.build_types[build_type]&.dart_defines),
|
|
114
|
+
)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def self.resolve_web_flavor(flavor, defaults, build_type)
|
|
118
|
+
bt = effective_bt_config(flavor.build_types, defaults.build_types, build_type)
|
|
119
|
+
ResolvedBuildOutput.new(
|
|
120
|
+
flavor_name: nil,
|
|
121
|
+
build_type: build_type,
|
|
122
|
+
effective_id: resolve_id((flavor.id || defaults.id).to_s, flavor.id_suffix.to_s, bt.id_suffix),
|
|
123
|
+
effective_name: resolve_name((flavor.name || defaults.name).to_s, flavor.name_suffix.to_s, bt.name_suffix),
|
|
124
|
+
effective_version_name: (flavor.version_name || defaults.version_name).to_s,
|
|
125
|
+
effective_version_code: flavor.version_code || defaults.version_code,
|
|
126
|
+
effective_main_file: flavor.main_file || defaults.main_file,
|
|
127
|
+
effective_firebase: resolve_firebase(flavor.build_types[build_type]&.firebase, flavor.firebase, defaults.build_types[build_type]&.firebase, defaults.firebase),
|
|
128
|
+
effective_auth: bt.auth,
|
|
129
|
+
effective_gms_ads_id: nil,
|
|
130
|
+
effective_icon: flavor.icon || defaults.icon,
|
|
131
|
+
effective_cloudflare: resolve_cloudflare(flavor.cloudflare, defaults.cloudflare),
|
|
132
|
+
effective_custom: resolve_custom(defaults.custom, defaults.build_types[build_type]&.custom, flavor.custom, flavor.build_types[build_type]&.custom),
|
|
133
|
+
effective_dart_defines: resolve_dart_defines(defaults.dart_defines, defaults.build_types[build_type]&.dart_defines, flavor.dart_defines, flavor.build_types[build_type]&.dart_defines),
|
|
134
|
+
)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def self.resolve_cloudflare(flavor_cf, default_cf)
|
|
138
|
+
CloudflareConfig.new(
|
|
139
|
+
project_name: flavor_cf&.project_name,
|
|
140
|
+
account_id: flavor_cf&.account_id || default_cf&.account_id,
|
|
141
|
+
branch: flavor_cf&.branch || default_cf&.branch || 'main',
|
|
142
|
+
mode: flavor_cf&.mode || default_cf&.mode || 'pages',
|
|
143
|
+
)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def self.resolve_windows_flavor(flavor, defaults, build_type)
|
|
147
|
+
bt = effective_bt_config(flavor.build_types, defaults.build_types, build_type)
|
|
148
|
+
ResolvedBuildOutput.new(
|
|
149
|
+
flavor_name: nil,
|
|
150
|
+
build_type: build_type,
|
|
151
|
+
effective_id: resolve_id((flavor.id || defaults.id).to_s, flavor.id_suffix.to_s, bt.id_suffix),
|
|
152
|
+
effective_name: resolve_name((flavor.name || defaults.name).to_s, flavor.name_suffix.to_s, bt.name_suffix),
|
|
153
|
+
effective_version_name: (flavor.version_name || defaults.version_name).to_s,
|
|
154
|
+
effective_version_code: flavor.version_code || defaults.version_code,
|
|
155
|
+
effective_main_file: flavor.main_file || defaults.main_file,
|
|
156
|
+
effective_firebase: resolve_firebase(flavor.build_types[build_type]&.firebase, flavor.firebase, defaults.build_types[build_type]&.firebase, defaults.firebase),
|
|
157
|
+
effective_auth: bt.auth,
|
|
158
|
+
effective_gms_ads_id: nil,
|
|
159
|
+
effective_icon: nil,
|
|
160
|
+
effective_custom: resolve_custom(defaults.custom, defaults.build_types[build_type]&.custom, flavor.custom, flavor.build_types[build_type]&.custom),
|
|
161
|
+
effective_dart_defines: resolve_dart_defines(defaults.dart_defines, defaults.build_types[build_type]&.dart_defines, flavor.dart_defines, flavor.build_types[build_type]&.dart_defines),
|
|
162
|
+
)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# ── Internal helpers ──────────────────────────────────────────────────────
|
|
166
|
+
|
|
167
|
+
# Deep-merges custom config — lowest priority first, key-by-key within each group.
|
|
168
|
+
def self.resolve_custom(default_custom, default_bt_custom, flavor_custom, flavor_bt_custom)
|
|
169
|
+
merge = lambda do |base, over|
|
|
170
|
+
next base if over.nil? || over.empty?
|
|
171
|
+
result = base.dup
|
|
172
|
+
over.each { |group, group_map| result[group] = (result[group] || {}).merge(group_map) }
|
|
173
|
+
result
|
|
174
|
+
end
|
|
175
|
+
merge.call(merge.call(merge.call(default_custom || {}, default_bt_custom), flavor_custom), flavor_bt_custom)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Shallow key-union across all four cascade levels — most specific key wins.
|
|
179
|
+
def self.merge_defines_map(default_map, default_bt_map, flavor_map, flavor_bt_map)
|
|
180
|
+
(default_map || {})
|
|
181
|
+
.merge(default_bt_map || {})
|
|
182
|
+
.merge(flavor_map || {})
|
|
183
|
+
.merge(flavor_bt_map || {})
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Resolves all four dart_defines sub-maps (common + three action scopes)
|
|
187
|
+
# independently.
|
|
188
|
+
def self.resolve_dart_defines(default_defines, default_bt_defines, flavor_defines, flavor_bt_defines)
|
|
189
|
+
d = default_defines || DartDefines.defaults
|
|
190
|
+
db = default_bt_defines || DartDefines.defaults
|
|
191
|
+
f = flavor_defines || DartDefines.defaults
|
|
192
|
+
fb = flavor_bt_defines || DartDefines.defaults
|
|
193
|
+
DartDefines.new(
|
|
194
|
+
common: merge_defines_map(d.common, db.common, f.common, fb.common),
|
|
195
|
+
compile: merge_defines_map(d.compile, db.compile, f.compile, fb.compile),
|
|
196
|
+
run: merge_defines_map(d.run, db.run, f.run, fb.run),
|
|
197
|
+
deploy: merge_defines_map(d.deploy, db.deploy, f.deploy, fb.deploy),
|
|
198
|
+
)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def self.effective_bt_config(flavor_bts, default_bts, build_type)
|
|
202
|
+
f = (flavor_bts || {})[build_type]
|
|
203
|
+
d = (default_bts || {})[build_type]
|
|
204
|
+
BuildTypeConfig.new(
|
|
205
|
+
id_suffix: effective_build_type_suffix(f&.id_suffix, d&.id_suffix),
|
|
206
|
+
name_suffix: effective_build_type_suffix(f&.name_suffix, d&.name_suffix),
|
|
207
|
+
firebase: merge_firebase_config(f&.firebase, d&.firebase),
|
|
208
|
+
auth: merge_auth_config(f&.auth, d&.auth),
|
|
209
|
+
)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Full 4-level cascade for every firebase field (most specific wins):
|
|
213
|
+
# L1: flavor_bt — flavor.build_types[bt].firebase
|
|
214
|
+
# L2: flavor_top — flavor.firebase
|
|
215
|
+
# L3: default_bt — default.build_types[bt].firebase
|
|
216
|
+
# L4: default_top — defaults.firebase
|
|
217
|
+
def self.resolve_firebase(flavor_bt, flavor_top, default_bt, default_top)
|
|
218
|
+
project_id = flavor_bt&.project_id ||
|
|
219
|
+
flavor_top&.project_id ||
|
|
220
|
+
default_bt&.project_id ||
|
|
221
|
+
default_top&.project_id
|
|
222
|
+
config_file = flavor_bt&.config_file ||
|
|
223
|
+
flavor_top&.config_file ||
|
|
224
|
+
default_bt&.config_file ||
|
|
225
|
+
default_top&.config_file
|
|
226
|
+
sa = flavor_bt&.service_account ||
|
|
227
|
+
flavor_top&.service_account ||
|
|
228
|
+
default_bt&.service_account ||
|
|
229
|
+
default_top&.service_account
|
|
230
|
+
target = flavor_bt&.target ||
|
|
231
|
+
flavor_top&.target ||
|
|
232
|
+
default_bt&.target ||
|
|
233
|
+
default_top&.target
|
|
234
|
+
return nil if project_id.nil? && config_file.nil? && sa.nil? && target.nil?
|
|
235
|
+
FirebaseConfig.new(
|
|
236
|
+
project_id: project_id,
|
|
237
|
+
config_file: config_file,
|
|
238
|
+
service_account: sa,
|
|
239
|
+
target: target,
|
|
240
|
+
)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def self.merge_firebase_config(flavor, default)
|
|
244
|
+
return default if flavor.nil?
|
|
245
|
+
return flavor if default.nil?
|
|
246
|
+
# service_account cascades independently — a flavor can override project_id without having to
|
|
247
|
+
# repeat the service_account key, and vice versa.
|
|
248
|
+
FirebaseConfig.new(
|
|
249
|
+
project_id: flavor.project_id || default.project_id,
|
|
250
|
+
config_file: flavor.config_file || default.config_file,
|
|
251
|
+
service_account: flavor.service_account || default.service_account,
|
|
252
|
+
target: flavor.target || default.target,
|
|
253
|
+
)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def self.merge_auth_config(flavor, default)
|
|
257
|
+
return default if flavor.nil?
|
|
258
|
+
return flavor if default.nil?
|
|
259
|
+
AuthConfig.new(
|
|
260
|
+
client_id: flavor.client_id || default.client_id,
|
|
261
|
+
reversed_client_id: flavor.reversed_client_id || default.reversed_client_id,
|
|
262
|
+
others: (default.others || {}).merge(flavor.others || {}),
|
|
263
|
+
)
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
end
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
require_relative 'model'
|
|
2
|
+
|
|
3
|
+
module AnnFlavorCore
|
|
4
|
+
#
|
|
5
|
+
# Opt-in unknown-key detection for annspec.yaml.
|
|
6
|
+
#
|
|
7
|
+
# Per-level VALID KEYS are derived automatically from each Struct's +members+ (Ruby's
|
|
8
|
+
# built-in reflection for Struct.new(...)-defined classes) -- this part can never
|
|
9
|
+
# drift out of sync with the model, the same way Kotlin's reflection-based checker
|
|
10
|
+
# can't. Ruby has no static typing, so which nested Struct class a field recurses
|
|
11
|
+
# into cannot be derived by reflection alone; RECURSION_TARGETS is the one small,
|
|
12
|
+
# explicit map needed for that -- it is a target-class lookup, not a key list, so it
|
|
13
|
+
# stays small (~20 entries) and stable even as fields are added to existing structs.
|
|
14
|
+
#
|
|
15
|
+
# Does NOT modify AnnFlavorCore::Parser in any way -- a fully separate, additive pass
|
|
16
|
+
# over the same raw Hash the parser already receives.
|
|
17
|
+
#
|
|
18
|
+
# Usage: AnnFlavorCore::StrictModeChecker.check(raw_hash, AnnFlavorCore::AnnSpec)
|
|
19
|
+
#
|
|
20
|
+
module StrictModeChecker
|
|
21
|
+
# Fields whose value is a free-form Hash (arbitrary user-declared keys are data,
|
|
22
|
+
# not schema) -- exempted from unknown-key checking entirely, at any level.
|
|
23
|
+
FREE_FORM_FIELDS = %i[custom dart_defines].freeze
|
|
24
|
+
|
|
25
|
+
# (containing_struct, field_name) => target_struct — only the "does this field
|
|
26
|
+
# recurse, and into what" relationship; the valid-keys-within-that-struct question
|
|
27
|
+
# is still fully derived from Struct#members.
|
|
28
|
+
RECURSION_TARGETS = {
|
|
29
|
+
[AnnSpec, :app] => AnnApp,
|
|
30
|
+
[AnnSpec, :tooling] => ToolingConfig,
|
|
31
|
+
[AnnApp, :android] => AndroidPlatform,
|
|
32
|
+
[AnnApp, :ios] => IosPlatform,
|
|
33
|
+
[AnnApp, :web] => WebPlatform,
|
|
34
|
+
[AnnApp, :windows] => WindowsPlatform,
|
|
35
|
+
[AnnApp, :integrations] => AnnIntegrations,
|
|
36
|
+
|
|
37
|
+
[AndroidPlatform, :default] => AndroidDefault,
|
|
38
|
+
[AndroidPlatform, :flavors] => AndroidFlavor, # via `flavor:` YAML key, map values
|
|
39
|
+
[IosPlatform, :default] => IosDefault,
|
|
40
|
+
[IosPlatform, :flavors] => IosFlavor,
|
|
41
|
+
[WebPlatform, :default] => WebDefault,
|
|
42
|
+
[WebPlatform, :flavors] => WebFlavor,
|
|
43
|
+
[WindowsPlatform, :default] => WindowsDefault,
|
|
44
|
+
[WindowsPlatform, :flavors] => WindowsFlavor,
|
|
45
|
+
|
|
46
|
+
[AndroidDefault, :admob] => AdmobConfig,
|
|
47
|
+
[AndroidDefault, :sdk] => AndroidSdk,
|
|
48
|
+
[AndroidDefault, :credentials] => AndroidCredentials,
|
|
49
|
+
[AndroidDefault, :firebase] => FirebaseConfig,
|
|
50
|
+
[AndroidDefault, :build_types] => BuildTypeConfig, # map values
|
|
51
|
+
[AndroidFlavor, :admob] => AdmobConfig,
|
|
52
|
+
[AndroidFlavor, :credentials] => AndroidCredentials,
|
|
53
|
+
[AndroidFlavor, :firebase] => FirebaseConfig,
|
|
54
|
+
[AndroidFlavor, :stores] => FlavorStores,
|
|
55
|
+
[AndroidFlavor, :build_types] => BuildTypeConfig,
|
|
56
|
+
|
|
57
|
+
[IosDefault, :admob] => AdmobConfig,
|
|
58
|
+
[IosDefault, :credentials] => IosCredentials,
|
|
59
|
+
[IosDefault, :firebase] => FirebaseConfig,
|
|
60
|
+
[IosDefault, :build_types] => BuildTypeConfig,
|
|
61
|
+
[IosFlavor, :admob] => AdmobConfig,
|
|
62
|
+
[IosFlavor, :credentials] => IosCredentials,
|
|
63
|
+
[IosFlavor, :firebase] => FirebaseConfig,
|
|
64
|
+
[IosFlavor, :stores] => FlavorStores,
|
|
65
|
+
[IosFlavor, :build_types] => BuildTypeConfig,
|
|
66
|
+
|
|
67
|
+
[WebDefault, :cloudflare] => CloudflareConfig,
|
|
68
|
+
[WebDefault, :build_types] => BuildTypeConfig,
|
|
69
|
+
[WebFlavor, :cloudflare] => CloudflareConfig,
|
|
70
|
+
[WebFlavor, :build_types] => BuildTypeConfig,
|
|
71
|
+
|
|
72
|
+
[WindowsDefault, :build_types] => BuildTypeConfig,
|
|
73
|
+
[WindowsFlavor, :build_types] => BuildTypeConfig,
|
|
74
|
+
|
|
75
|
+
[BuildTypeConfig, :admob] => AdmobConfig,
|
|
76
|
+
[BuildTypeConfig, :firebase] => FirebaseConfig,
|
|
77
|
+
[BuildTypeConfig, :auth] => AuthConfig,
|
|
78
|
+
|
|
79
|
+
[AndroidCredentials, :signing] => AndroidSigning,
|
|
80
|
+
[AndroidCredentials, :google_play] => GooglePlayCredentials,
|
|
81
|
+
[AndroidCredentials, :samsung_galaxy] => SamsungGalaxyCredentials,
|
|
82
|
+
[AndroidCredentials, :amazon] => AmazonCredentials,
|
|
83
|
+
[IosCredentials, :signing] => IosSigning,
|
|
84
|
+
[IosCredentials, :app_store] => AppStoreCredentials,
|
|
85
|
+
|
|
86
|
+
[FlavorStores, :google_play] => GooglePlayFlavorStore,
|
|
87
|
+
[FlavorStores, :samsung_galaxy] => SamsungGalaxyFlavorStore,
|
|
88
|
+
[FlavorStores, :amazon] => AmazonFlavorStore,
|
|
89
|
+
[FlavorStores, :app_store] => AppStoreFlavorStore,
|
|
90
|
+
}.freeze
|
|
91
|
+
|
|
92
|
+
# Fields whose target struct's instances live inside a Hash keyed by an arbitrary
|
|
93
|
+
# user-chosen name (flavor name, build-type name), not a single nested Hash.
|
|
94
|
+
MAP_VALUED_FIELDS = %i[flavors build_types].freeze
|
|
95
|
+
|
|
96
|
+
def self.check(raw, root_struct)
|
|
97
|
+
issues = []
|
|
98
|
+
walk(raw, root_struct, '', issues)
|
|
99
|
+
issues
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def self.walk(raw, struct_class, path, issues)
|
|
103
|
+
return unless raw.is_a?(Hash)
|
|
104
|
+
|
|
105
|
+
valid_keys = struct_class.members.map(&:to_s)
|
|
106
|
+
# `flavors` (Struct member) reads YAML key `flavor` (singular) -- the one
|
|
107
|
+
# deliberate field-name <-> YAML-key exception in the schema.
|
|
108
|
+
valid_keys = valid_keys.map { |k| k == 'flavors' ? 'flavor' : k }
|
|
109
|
+
|
|
110
|
+
raw.each do |raw_key, raw_value|
|
|
111
|
+
key = raw_key.to_s
|
|
112
|
+
field_path = path.empty? ? key : "#{path}.#{key}"
|
|
113
|
+
|
|
114
|
+
unless valid_keys.include?(key)
|
|
115
|
+
context = path.empty? ? 'top-level annspec.yaml' : "#{path} block"
|
|
116
|
+
issues << "Unknown field \"#{key}\" in #{context}. Check for typos or remove the field."
|
|
117
|
+
next
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
field_sym = (key == 'flavor' ? 'flavors' : key).to_sym
|
|
121
|
+
next if FREE_FORM_FIELDS.include?(field_sym)
|
|
122
|
+
|
|
123
|
+
target_struct = RECURSION_TARGETS[[struct_class, field_sym]]
|
|
124
|
+
next unless target_struct
|
|
125
|
+
|
|
126
|
+
if MAP_VALUED_FIELDS.include?(field_sym)
|
|
127
|
+
raw_value.is_a?(Hash) && raw_value.each_value do |entry_val|
|
|
128
|
+
walk(entry_val, target_struct, field_path, issues)
|
|
129
|
+
end
|
|
130
|
+
else
|
|
131
|
+
walk(raw_value, target_struct, field_path, issues)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
require_relative 'ann_flavor_core/version'
|
|
2
|
+
require_relative 'ann_flavor_core/model'
|
|
3
|
+
require_relative 'ann_flavor_core/parser'
|
|
4
|
+
require_relative 'ann_flavor_core/resolver'
|
|
5
|
+
require_relative 'ann_flavor_core/ios_resolution'
|
|
6
|
+
require_relative 'ann_flavor_core/strict_mode_checker'
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ann-flavor-flutter
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ANN Solutions
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: fastlane
|
|
@@ -66,7 +65,6 @@ dependencies:
|
|
|
66
65
|
- - "~>"
|
|
67
66
|
- !ruby/object:Gem::Version
|
|
68
67
|
version: '0.6'
|
|
69
|
-
description:
|
|
70
68
|
email:
|
|
71
69
|
- support@annaibrands.com
|
|
72
70
|
executables: []
|
|
@@ -102,12 +100,18 @@ files:
|
|
|
102
100
|
- lib/fastlane/plugin/ann_flavor_flutter/helper/utils_project_config.rb
|
|
103
101
|
- lib/fastlane/plugin/ann_flavor_flutter/helper/utils_spec_loader.rb
|
|
104
102
|
- lib/fastlane/plugin/ann_flavor_flutter/helper/utils_status.rb
|
|
103
|
+
- lib/vendor/ann_flavor_core/lib/ann_flavor_core.rb
|
|
104
|
+
- lib/vendor/ann_flavor_core/lib/ann_flavor_core/ios_resolution.rb
|
|
105
|
+
- lib/vendor/ann_flavor_core/lib/ann_flavor_core/model.rb
|
|
106
|
+
- lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb
|
|
107
|
+
- lib/vendor/ann_flavor_core/lib/ann_flavor_core/resolver.rb
|
|
108
|
+
- lib/vendor/ann_flavor_core/lib/ann_flavor_core/strict_mode_checker.rb
|
|
109
|
+
- lib/vendor/ann_flavor_core/lib/ann_flavor_core/version.rb
|
|
105
110
|
- plugin_manager.sh
|
|
106
111
|
homepage: https://github.com/anntech-dev
|
|
107
112
|
licenses:
|
|
108
113
|
- MIT
|
|
109
114
|
metadata: {}
|
|
110
|
-
post_install_message:
|
|
111
115
|
rdoc_options: []
|
|
112
116
|
require_paths:
|
|
113
117
|
- lib
|
|
@@ -122,8 +126,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
122
126
|
- !ruby/object:Gem::Version
|
|
123
127
|
version: '0'
|
|
124
128
|
requirements: []
|
|
125
|
-
rubygems_version: 3.
|
|
126
|
-
signing_key:
|
|
129
|
+
rubygems_version: 3.6.9
|
|
127
130
|
specification_version: 4
|
|
128
131
|
summary: A fastlane plugin to manage Flutter build flavors and shared setup.
|
|
129
132
|
test_files: []
|