solara 0.1.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.
Files changed (175) hide show
  1. checksums.yaml +7 -0
  2. data/bin/solara +18 -0
  3. data/solara/lib/.DS_Store +0 -0
  4. data/solara/lib/core/.DS_Store +0 -0
  5. data/solara/lib/core/aliases/alias_generator.rb +128 -0
  6. data/solara/lib/core/aliases/alias_generator_manager.rb +28 -0
  7. data/solara/lib/core/aliases/solara_terminal_setup.rb +103 -0
  8. data/solara/lib/core/brands/brand_onboarder.rb +46 -0
  9. data/solara/lib/core/brands/brand_switcher.rb +204 -0
  10. data/solara/lib/core/brands/brands_manager.rb +154 -0
  11. data/solara/lib/core/dashboard/.DS_Store +0 -0
  12. data/solara/lib/core/dashboard/brand/.DS_Store +0 -0
  13. data/solara/lib/core/dashboard/brand/BrandDetail.js +11 -0
  14. data/solara/lib/core/dashboard/brand/BrandDetailController.js +361 -0
  15. data/solara/lib/core/dashboard/brand/BrandDetailModel.js +155 -0
  16. data/solara/lib/core/dashboard/brand/BrandDetailView.js +245 -0
  17. data/solara/lib/core/dashboard/brand/brand.html +477 -0
  18. data/solara/lib/core/dashboard/brand/source/BrandLocalSource.js +123 -0
  19. data/solara/lib/core/dashboard/brand/source/BrandRemoteSource.js +260 -0
  20. data/solara/lib/core/dashboard/brands/Brands.js +10 -0
  21. data/solara/lib/core/dashboard/brands/BrandsController.js +155 -0
  22. data/solara/lib/core/dashboard/brands/BrandsModel.js +136 -0
  23. data/solara/lib/core/dashboard/brands/BrandsView.js +136 -0
  24. data/solara/lib/core/dashboard/brands/brands.html +345 -0
  25. data/solara/lib/core/dashboard/component/AddFieldSheet.js +212 -0
  26. data/solara/lib/core/dashboard/component/AliasesBottomSheet.js +128 -0
  27. data/solara/lib/core/dashboard/component/BrandOptionsBottomSheet.js +130 -0
  28. data/solara/lib/core/dashboard/component/ConfirmationDialog.js +103 -0
  29. data/solara/lib/core/dashboard/component/MessageBottomSheet.js +80 -0
  30. data/solara/lib/core/dashboard/component/OnboardBrandBottomSheet.js +214 -0
  31. data/solara/lib/core/dashboard/dashboard_manager.rb +19 -0
  32. data/solara/lib/core/dashboard/dashboard_server.rb +132 -0
  33. data/solara/lib/core/dashboard/handler/base_handler.rb +25 -0
  34. data/solara/lib/core/dashboard/handler/brand_alisases_handler.rb +33 -0
  35. data/solara/lib/core/dashboard/handler/brand_configurations_handler.rb +18 -0
  36. data/solara/lib/core/dashboard/handler/brand_configurations_manager.rb +73 -0
  37. data/solara/lib/core/dashboard/handler/brand_icon_handler.rb +20 -0
  38. data/solara/lib/core/dashboard/handler/brand_section_handler.rb +20 -0
  39. data/solara/lib/core/dashboard/handler/brands_handler.rb +14 -0
  40. data/solara/lib/core/dashboard/handler/current_brand_handler.rb +18 -0
  41. data/solara/lib/core/dashboard/handler/doctor_handler.rb +39 -0
  42. data/solara/lib/core/dashboard/handler/edit_section_handler.rb +55 -0
  43. data/solara/lib/core/dashboard/handler/offboard_brand_handler.rb +34 -0
  44. data/solara/lib/core/dashboard/handler/onboard_brand_handler.rb +53 -0
  45. data/solara/lib/core/dashboard/handler/redirect_handler.rb +12 -0
  46. data/solara/lib/core/dashboard/handler/switch_handler.rb +25 -0
  47. data/solara/lib/core/dashboard/index.html +36 -0
  48. data/solara/lib/core/dashboard/local.html +41 -0
  49. data/solara/lib/core/dashboard/res/favicon/android-chrome-192x192.png +0 -0
  50. data/solara/lib/core/dashboard/res/favicon/android-chrome-512x512.png +0 -0
  51. data/solara/lib/core/dashboard/res/favicon/apple-touch-icon.png +0 -0
  52. data/solara/lib/core/dashboard/res/favicon/favicon-16x16.png +0 -0
  53. data/solara/lib/core/dashboard/res/favicon/favicon-32x32.png +0 -0
  54. data/solara/lib/core/dashboard/res/favicon/favicon.ico +0 -0
  55. data/solara/lib/core/dashboard/res/favicon/site.webmanifest +1 -0
  56. data/solara/lib/core/dashboard/solara.png +0 -0
  57. data/solara/lib/core/doctor/brand_doctor.rb +94 -0
  58. data/solara/lib/core/doctor/doctor_manager.rb +35 -0
  59. data/solara/lib/core/doctor/project_doctor.rb +8 -0
  60. data/solara/lib/core/doctor/schema/brand_configurations.json +60 -0
  61. data/solara/lib/core/doctor/schema/platform/android/android_config.json +23 -0
  62. data/solara/lib/core/doctor/schema/platform/android/android_signing.json +23 -0
  63. data/solara/lib/core/doctor/schema/platform/ios/ios_config.json +27 -0
  64. data/solara/lib/core/doctor/schema/platform/ios/ios_signing.json +27 -0
  65. data/solara/lib/core/doctor/schema/platform/shared/theme.json +48 -0
  66. data/solara/lib/core/doctor/validator/brand_settings_validator.rb +55 -0
  67. data/solara/lib/core/doctor/validator/brand_settings_validator_manager.rb +82 -0
  68. data/solara/lib/core/doctor/validator/directory_structure_validator.rb +38 -0
  69. data/solara/lib/core/doctor/validator/file_structure_validator.rb +37 -0
  70. data/solara/lib/core/doctor/validator/json_file_validator.rb +21 -0
  71. data/solara/lib/core/doctor/validator/json_schema_validator.rb +32 -0
  72. data/solara/lib/core/doctor/validator/project_filesystem_validator.rb +70 -0
  73. data/solara/lib/core/doctor/validator/template/android_template_validation_config.yml +51 -0
  74. data/solara/lib/core/doctor/validator/template/flutter_template_validation_config.yml +53 -0
  75. data/solara/lib/core/doctor/validator/template/ios_template_validation_config.yml +51 -0
  76. data/solara/lib/core/doctor/validator/template/template_validator.rb +108 -0
  77. data/solara/lib/core/doctor/validator/validation_strategy.rb +7 -0
  78. data/solara/lib/core/scripts/brand_config_generator.rb +245 -0
  79. data/solara/lib/core/scripts/brand_config_manager.rb +90 -0
  80. data/solara/lib/core/scripts/brand_exporter.rb +38 -0
  81. data/solara/lib/core/scripts/brand_importer.rb +84 -0
  82. data/solara/lib/core/scripts/brand_offboarder.rb +19 -0
  83. data/solara/lib/core/scripts/brand_resources_manager.rb +77 -0
  84. data/solara/lib/core/scripts/directory_creator.rb +22 -0
  85. data/solara/lib/core/scripts/file_manager.rb +90 -0
  86. data/solara/lib/core/scripts/file_path.rb +327 -0
  87. data/solara/lib/core/scripts/folder_copier.rb +41 -0
  88. data/solara/lib/core/scripts/gitignore_manager.rb +54 -0
  89. data/solara/lib/core/scripts/interactive_file_system_validator.rb +110 -0
  90. data/solara/lib/core/scripts/platform/android/android_manifest_switcher.rb +24 -0
  91. data/solara/lib/core/scripts/platform/android/android_strings_switcher.rb +39 -0
  92. data/solara/lib/core/scripts/platform/android/gradle_switcher.rb +233 -0
  93. data/solara/lib/core/scripts/platform/android/properties_generator.rb +31 -0
  94. data/solara/lib/core/scripts/platform/ios/ios_file_path_manager.rb +109 -0
  95. data/solara/lib/core/scripts/platform/ios/ios_plist_manager.rb +42 -0
  96. data/solara/lib/core/scripts/platform/ios/xcconfig_generator.rb +44 -0
  97. data/solara/lib/core/scripts/platform/ios/xcode_asset_manager.rb +56 -0
  98. data/solara/lib/core/scripts/platform/ios/xcode_project_manager.rb +82 -0
  99. data/solara/lib/core/scripts/platform/ios/xcode_project_switcher.rb +130 -0
  100. data/solara/lib/core/scripts/project_settings_manager.rb +39 -0
  101. data/solara/lib/core/scripts/solara_logger.rb +103 -0
  102. data/solara/lib/core/scripts/solara_settings_manager.rb +73 -0
  103. data/solara/lib/core/scripts/solara_status_manager.rb +55 -0
  104. data/solara/lib/core/scripts/solara_version_manager.rb +42 -0
  105. data/solara/lib/core/scripts/strings_xml_manager.rb +22 -0
  106. data/solara/lib/core/scripts/terminal_input_manager.rb +22 -0
  107. data/solara/lib/core/scripts/theme_generator.rb +250 -0
  108. data/solara/lib/core/scripts/yaml_manager.rb +72 -0
  109. data/solara/lib/core/solara_configurator.rb +15 -0
  110. data/solara/lib/core/template/brands/android/android_config.json +8 -0
  111. data/solara/lib/core/template/brands/android/android_signing.json +6 -0
  112. data/solara/lib/core/template/brands/android/res/.DS_Store +0 -0
  113. data/solara/lib/core/template/brands/android/res/mipmap-hdpi/ic_launcher.png +0 -0
  114. data/solara/lib/core/template/brands/android/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  115. data/solara/lib/core/template/brands/android/res/mipmap-mdpi/ic_launcher.png +0 -0
  116. data/solara/lib/core/template/brands/android/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  117. data/solara/lib/core/template/brands/android/res/mipmap-xhdpi/ic_launcher.png +0 -0
  118. data/solara/lib/core/template/brands/android/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  119. data/solara/lib/core/template/brands/android/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  120. data/solara/lib/core/template/brands/android/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  121. data/solara/lib/core/template/brands/android/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  122. data/solara/lib/core/template/brands/android/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  123. data/solara/lib/core/template/brands/brands.json +4 -0
  124. data/solara/lib/core/template/brands/ios/assets/.DS_Store +0 -0
  125. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/100.png +0 -0
  126. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/102.png +0 -0
  127. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/1024.png +0 -0
  128. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/114.png +0 -0
  129. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/120.png +0 -0
  130. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/128.png +0 -0
  131. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/144.png +0 -0
  132. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/152.png +0 -0
  133. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/16.png +0 -0
  134. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/167.png +0 -0
  135. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/172.png +0 -0
  136. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/180.png +0 -0
  137. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/196.png +0 -0
  138. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/20.png +0 -0
  139. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/216.png +0 -0
  140. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/256.png +0 -0
  141. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/29.png +0 -0
  142. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/32.png +0 -0
  143. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/40.png +0 -0
  144. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/48.png +0 -0
  145. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/50.png +0 -0
  146. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/512.png +0 -0
  147. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/55.png +0 -0
  148. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/57.png +0 -0
  149. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/58.png +0 -0
  150. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/60.png +0 -0
  151. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/64.png +0 -0
  152. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/66.png +0 -0
  153. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/72.png +0 -0
  154. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/76.png +0 -0
  155. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/80.png +0 -0
  156. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/87.png +0 -0
  157. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/88.png +0 -0
  158. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/92.png +0 -0
  159. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/Contents.json +1 -0
  160. data/solara/lib/core/template/brands/ios/ios_config.json +7 -0
  161. data/solara/lib/core/template/brands/ios/ios_signing.json +7 -0
  162. data/solara/lib/core/template/brands/shared/.DS_Store +0 -0
  163. data/solara/lib/core/template/brands/shared/brand_config.json +2 -0
  164. data/solara/lib/core/template/brands/shared/theme.json +46 -0
  165. data/solara/lib/core/template/config/android_template_config.json +57 -0
  166. data/solara/lib/core/template/config/flutter_template_config.json +62 -0
  167. data/solara/lib/core/template/config/ios_template_config.json +57 -0
  168. data/solara/lib/core/template/project_template_generator.rb +63 -0
  169. data/solara/lib/platform_detector.rb +84 -0
  170. data/solara/lib/solara/cli.rb +5 -0
  171. data/solara/lib/solara/version.rb +3 -0
  172. data/solara/lib/solara.rb +238 -0
  173. data/solara/lib/solara_initializer.rb +44 -0
  174. data/solara/lib/solara_manager.rb +73 -0
  175. metadata +346 -0
@@ -0,0 +1,35 @@
1
+ Dir.glob("#{__dir__}/*.rb").each { |file| require file }
2
+ Dir.glob("#{__dir__}/validator/*.rb").each { |file| require file }
3
+ Dir.glob("#{__dir__}/brand/*.rb").each { |file| require file }
4
+ Dir.glob("#{__dir__}/../../.solara/core/brands/*.rb").each { |file| require file }
5
+
6
+ class DoctorManager
7
+ def initialize
8
+ end
9
+
10
+ def visit_brands(brand_keys = [], print_logs: true)
11
+ Solara.logger.header("Brand Doctor")
12
+ ensure_initialized!
13
+ BrandDoctor.new.visit(brand_keys, print_logs: print_logs)
14
+ end
15
+
16
+ def visit_project!
17
+ Solara.logger.header("Project Doctor")
18
+ Solara.logger.start_step("Project Health Check")
19
+ ensure_initialized!
20
+ ProjectDoctor.new.visit
21
+ Solara.logger.end_step("Project Health Check")
22
+ end
23
+
24
+ private
25
+
26
+ def ensure_initialized!
27
+ brands = FilePath.brands
28
+ brands_list = FilePath.brands_list
29
+ platform = SolaraSettingsManager.instance.platform
30
+ unless File.exist?(brands) && File.exist?(brands_list) && !platform.nil? && !platform.empty?
31
+ Solara.logger.error("Solara is not initialized here. Please run 'solara init' to initialize.")
32
+ exit 1
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,8 @@
1
+ Dir.glob("#{__dir__}/*.rb").each { |file| require file }
2
+ Dir.glob("#{__dir__}/../../.solara/core/brands/*.rb").each { |file| require file }
3
+
4
+ class ProjectDoctor
5
+ def visit
6
+ ProjectFileSystemValidator.new.validate
7
+ end
8
+ end
@@ -0,0 +1,60 @@
1
+ {
2
+ "title": "Brand Configurations Schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "solaraVersion": {
6
+ "type": "string"
7
+ },
8
+ "brand": {
9
+ "type": "object",
10
+ "properties": {
11
+ "key": {
12
+ "type": "string"
13
+ },
14
+ "name": {
15
+ "type": "string"
16
+ }
17
+ },
18
+ "required": [
19
+ "key",
20
+ "name"
21
+ ]
22
+ },
23
+ "configurations": {
24
+ "type": "array",
25
+ "items": {
26
+ "type": "object",
27
+ "properties": {
28
+ "key": {
29
+ "type": "string"
30
+ },
31
+ "name": {
32
+ "type": "string"
33
+ },
34
+ "inputType": {
35
+ "type": "string",
36
+ "enum": [
37
+ "color",
38
+ "text"
39
+ ]
40
+ },
41
+ "content": {
42
+ "type": "object",
43
+ "additionalProperties": {}
44
+ }
45
+ },
46
+ "required": [
47
+ "key",
48
+ "name",
49
+ "inputType",
50
+ "content"
51
+ ]
52
+ }
53
+ }
54
+ },
55
+ "required": [
56
+ "solaraVersion",
57
+ "brand",
58
+ "configurations"
59
+ ]
60
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "type": "object",
3
+ "required": [
4
+ "brandName",
5
+ "applicationId",
6
+ "versionName",
7
+ "versionCode"
8
+ ],
9
+ "properties": {
10
+ "brandName": {
11
+ "type": "string"
12
+ },
13
+ "applicationId": {
14
+ "type": "string"
15
+ },
16
+ "versionName": {
17
+ "type": "string"
18
+ },
19
+ "versionCode": {
20
+ "type": "number"
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "type": "object",
3
+ "required": [
4
+ "storeFile",
5
+ "keyAlias",
6
+ "storePassword",
7
+ "keyPassword"
8
+ ],
9
+ "properties": {
10
+ "storeFile": {
11
+ "type": "string"
12
+ },
13
+ "keyAlias": {
14
+ "type": "string"
15
+ },
16
+ "storePassword": {
17
+ "type": "string"
18
+ },
19
+ "keyPassword": {
20
+ "type": "string"
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "type": "object",
3
+ "properties": {
4
+ "PRODUCT_NAME": {
5
+ "type": "string",
6
+ "description": "The name of the product."
7
+ },
8
+ "PRODUCT_BUNDLE_IDENTIFIER": {
9
+ "type": "string",
10
+ "description": "The bundle identifier of the product."
11
+ },
12
+ "MARKETING_VERSION": {
13
+ "type": "string",
14
+ "description": "The marketing version of the product."
15
+ },
16
+ "BUNDLE_VERSION": {
17
+ "type": "number",
18
+ "description": "The bundle version of the product."
19
+ }
20
+ },
21
+ "required": [
22
+ "PRODUCT_NAME",
23
+ "PRODUCT_BUNDLE_IDENTIFIER",
24
+ "MARKETING_VERSION",
25
+ "BUNDLE_VERSION"
26
+ ]
27
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "type": "object",
3
+ "required": [
4
+ "CODE_SIGN_IDENTITY",
5
+ "DEVELOPMENT_TEAM",
6
+ "PROVISIONING_PROFILE_SPECIFIER",
7
+ "CODE_SIGN_STYLE",
8
+ "CODE_SIGN_ENTITLEMENTS"
9
+ ],
10
+ "properties": {
11
+ "CODE_SIGN_IDENTITY": {
12
+ "type": "string"
13
+ },
14
+ "DEVELOPMENT_TEAM": {
15
+ "type": "string"
16
+ },
17
+ "PROVISIONING_PROFILE_SPECIFIER": {
18
+ "type": "string"
19
+ },
20
+ "CODE_SIGN_STYLE": {
21
+ "type": "string"
22
+ },
23
+ "CODE_SIGN_ENTITLEMENTS": {
24
+ "type": "string"
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,48 @@
1
+ {
2
+ "type": "object",
3
+ "properties": {
4
+ "colors": {
5
+ "type": "object",
6
+ "properties": {
7
+ },
8
+ "required": []
9
+ },
10
+ "typography": {
11
+ "type": "object",
12
+ "properties": {
13
+ "fontFamily": {
14
+ "type": "object",
15
+ "properties": {
16
+ },
17
+ "required": []
18
+ },
19
+ "fontSize": {
20
+ "type": "object",
21
+ "properties": {
22
+ },
23
+ "required": []
24
+ }
25
+ },
26
+ "required": ["fontFamily", "fontSize"]
27
+ },
28
+ "spacing": {
29
+ "type": "object",
30
+ "properties": {
31
+ },
32
+ "required": []
33
+ },
34
+ "borderRadius": {
35
+ "type": "object",
36
+ "properties": {
37
+ },
38
+ "required": []
39
+ },
40
+ "elevation": {
41
+ "type": "object",
42
+ "properties": {
43
+ },
44
+ "required": []
45
+ }
46
+ },
47
+ "required": ["colors", "typography", "spacing", "borderRadius", "elevation"]
48
+ }
@@ -0,0 +1,55 @@
1
+ class BrandSettingsValidator
2
+ def validate_properties(property_names, file_path_method, issue_type:)
3
+ property_names.flat_map { |property_name|
4
+ validate_single_property(property_name, file_path_method, issue_type: issue_type)
5
+ }
6
+ end
7
+
8
+ def validate_single_property(property_name, file_path_method, issue_type:)
9
+ validation_result = gather_property_data(property_name, file_path_method, issue_type: issue_type)
10
+ validation_result[:issues]
11
+ end
12
+
13
+ def validate_property_duplicates(property_name, file_path_method, issue_type:)
14
+ validation_result = gather_property_data(property_name, file_path_method, issue_type: issue_type)
15
+ property_value_to_brands_map = validation_result[:property_value_to_brands_map]
16
+ identify_duplicate_properties(property_value_to_brands_map, issue_type: issue_type)
17
+ end
18
+
19
+ private
20
+
21
+ def gather_property_data(property_name, file_path_method, issue_type:)
22
+ brands = BrandsManager.instance.brands_list
23
+ issues = []
24
+ property_value_to_brands_map = {}
25
+
26
+ brands.each do |brand|
27
+ brand_key = brand['key']
28
+ property_value = fetch_json_property(property_name, FilePath.public_send(file_path_method, brand_key))
29
+
30
+ if property_value.to_s.strip.empty?
31
+ issues << Issue.new(issue_type, "#{brand_key}: (#{property_name}) is empty.")
32
+ next
33
+ end
34
+
35
+ map_key = "#{property_value} for #{property_name}"
36
+ (property_value_to_brands_map[map_key] ||= []) << brand_key
37
+ end
38
+
39
+ { issues: issues, property_value_to_brands_map: property_value_to_brands_map }
40
+ end
41
+
42
+ def fetch_json_property(property, file_path)
43
+ JSON.parse(File.read(file_path))[property]
44
+ rescue JSON::ParserError, Errno::ENOENT => e
45
+ raise Issue.error("Error reading JSON file at #{file_path}: #{e.message}")
46
+ end
47
+
48
+ def identify_duplicate_properties(property_value_to_brands_map, issue_type:)
49
+ property_value_to_brands_map.each_with_object([]) do |(property_value, brand_keys), issues|
50
+ if brand_keys.size > 1
51
+ issues << Issue.new(issue_type, "[#{brand_keys.join(', ')}]: Duplicate value #{property_value}")
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,82 @@
1
+ class BrandSettingsValidatorManager
2
+
3
+ def initialize
4
+ @validator = BrandSettingsValidator.new
5
+ end
6
+
7
+ def validate
8
+ issues = ios_config
9
+ issues += ios_signing
10
+ issues += android_config
11
+ issues + android_signing
12
+ end
13
+
14
+ def ios_config
15
+ issues = @validator.validate_properties(
16
+ %w[MARKETING_VERSION BUNDLE_VERSION],
17
+ :ios_config,
18
+ issue_type: Issue::ERROR)
19
+
20
+ issues += @validator.validate_single_property(
21
+ 'PRODUCT_NAME',
22
+ :ios_config,
23
+ issue_type: Issue::ERROR)
24
+
25
+ issues += @validator.validate_property_duplicates(
26
+ 'PRODUCT_NAME',
27
+ :ios_config,
28
+ issue_type: Issue::WARNING)
29
+
30
+ issues += @validator.validate_single_property(
31
+ 'PRODUCT_BUNDLE_IDENTIFIER',
32
+ :ios_config,
33
+ issue_type: Issue::ERROR)
34
+
35
+ issues + @validator.validate_property_duplicates(
36
+ 'PRODUCT_BUNDLE_IDENTIFIER',
37
+ :ios_config,
38
+ issue_type: Issue::ERROR)
39
+ end
40
+
41
+ def ios_signing
42
+ @validator.validate_properties(
43
+ %w[CODE_SIGN_IDENTITY DEVELOPMENT_TEAM PROVISIONING_PROFILE_SPECIFIER CODE_SIGN_STYLE CODE_SIGN_ENTITLEMENTS],
44
+ :ios_brand_signing,
45
+ issue_type: Issue::WARNING)
46
+ end
47
+
48
+ def android_signing
49
+ @validator.validate_properties(
50
+ %w[storeFile keyAlias storePassword keyPassword],
51
+ :android_brand_signing,
52
+ issue_type: Issue::WARNING)
53
+ end
54
+
55
+ def android_config
56
+ issues = @validator.validate_properties(
57
+ %w[versionName versionCode],
58
+ :android_config,
59
+ issue_type: Issue::ERROR)
60
+
61
+ issues += @validator.validate_single_property(
62
+ 'brandName',
63
+ :android_config,
64
+ issue_type: Issue::ERROR)
65
+
66
+ issues += @validator.validate_property_duplicates(
67
+ 'brandName',
68
+ :android_config,
69
+ issue_type: Issue::WARNING)
70
+
71
+ issues += @validator.validate_single_property(
72
+ 'applicationId',
73
+ :android_config,
74
+ issue_type: Issue::ERROR)
75
+
76
+ issues + @validator.validate_property_duplicates(
77
+ 'applicationId',
78
+ :android_config,
79
+ issue_type: Issue::ERROR)
80
+ end
81
+
82
+ end
@@ -0,0 +1,38 @@
1
+ Dir[File.expand_path('*.rb', __dir__)].each { |file| require_relative file }
2
+
3
+ class DirectoryStructure
4
+ attr_reader :path, :use_glob
5
+
6
+ def initialize(path, use_glob: true)
7
+ @path = path
8
+ @use_glob = use_glob
9
+ end
10
+
11
+ def self.expand(pattern, use_glob: true)
12
+ if use_glob
13
+ Dir.glob(pattern).select { |path| File.directory?(path) }.map do |path|
14
+ DirectoryStructure.new(path, use_glob: use_glob)
15
+ end
16
+ else
17
+ [DirectoryStructure.new(pattern, use_glob: use_glob)]
18
+ end
19
+ end
20
+ end
21
+
22
+ class DirectoryStructureValidator < ValidationStrategy
23
+ def initialize(dir)
24
+ @dir = dir
25
+ end
26
+
27
+ def validate(project_path)
28
+ full_path = File.join(project_path, @dir.path)
29
+ paths = @dir.use_glob ? DirectoryStructure.expand(full_path) : [@dir]
30
+
31
+ if paths.any? { |path| File.directory?(path.path) }
32
+ Solara.logger.passed("Directory exists: '#{@dir.path}'")
33
+ else
34
+ raise ValidationError, "Directory missing: '#{@dir.path}'"
35
+ end
36
+ end
37
+
38
+ end
@@ -0,0 +1,37 @@
1
+ Dir[File.expand_path('*.rb', __dir__)].each { |file| require_relative file }
2
+
3
+ class FileStructure
4
+ attr_reader :path, :use_glob
5
+
6
+ def initialize(path, use_glob: true)
7
+ @path = path
8
+ @use_glob = use_glob
9
+ end
10
+
11
+ def self.expand(pattern, use_glob: true)
12
+ if use_glob
13
+ Dir.glob(pattern).select { |path| File.file?(path) }.map do |path|
14
+ FileStructure.new(path, use_glob: use_glob)
15
+ end
16
+ else
17
+ [FileStructure.new(pattern, use_glob: use_glob)]
18
+ end
19
+ end
20
+ end
21
+
22
+ class FileStructureValidator < ValidationStrategy
23
+ def initialize(file)
24
+ @file = file
25
+ end
26
+
27
+ def validate(project_path)
28
+ full_path = File.join(project_path, @file.path)
29
+ paths = @file.use_glob ? FileStructure.expand(full_path) : [@file]
30
+
31
+ if paths.any? { |path| File.file?(path.path) }
32
+ Solara.logger.passed("File exists: '#{@file.path}'")
33
+ else
34
+ raise ValidationError, "File missing: '#{@file.path}'"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,21 @@
1
+ Dir.glob("#{__dir__}/*.rb").each { |file| require file }
2
+
3
+ require 'json'
4
+
5
+ class JsonFileValidator < ValidationStrategy
6
+ def initialize(json_files)
7
+ @json_files = json_files
8
+ end
9
+
10
+ def validate(project_path = nil)
11
+ @json_files.each do |json_file|
12
+ begin
13
+ path = json_file
14
+ JSON.parse(File.read(path))
15
+ Solara.logger.passed("Valid JSON: #{path}")
16
+ rescue JSON::ParserError => e
17
+ raise ValidationError, "Not valid JSON: #{path}. Error: #{e.message}"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,32 @@
1
+ Dir.glob("#{__dir__}/*.rb").each { |file| require file }
2
+ require 'json'
3
+ require 'json-schema'
4
+
5
+ class JsonSchemaValidator < ValidationStrategy
6
+ def initialize(schema_file, json_file)
7
+ @schema_file = schema_file
8
+ @json_file = json_file
9
+ end
10
+
11
+ def validate(project_path = nil)
12
+
13
+ unless File.exist?(@schema_file)
14
+ raise ValidationError, "Schema file not found: #{@schema_file}"
15
+ end
16
+
17
+ begin
18
+ json_content = JSON.parse(File.read(@json_file))
19
+ rescue
20
+ # Ignore as it's not its responsibility
21
+ return
22
+ end
23
+ schema = JSON.parse(File.read(@schema_file))
24
+ errors = JSON::Validator.fully_validate(schema, json_content)
25
+
26
+ if errors.empty?
27
+ Solara.logger.passed("Valid according to schema: #{@json_file}")
28
+ else
29
+ raise ValidationError, "#{@json_file}: #{errors.join(', ')}."
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,70 @@
1
+ class ProjectFileSystemValidator
2
+
3
+ def initialize
4
+ @project_root = SolaraSettingsManager.instance.project_root
5
+ @platform = SolaraSettingsManager.instance.platform
6
+ end
7
+
8
+ def validate
9
+ file_system = project_filesystem
10
+ manager = InteractiveFileSystemValidator.new(@project_root, ProjectSettingsManager.instance)
11
+ manager.start(file_system)
12
+ end
13
+
14
+ def project_filesystem
15
+ case @platform
16
+ when Platform::Flutter
17
+ android_file_system + ios_file_system
18
+ when Platform::IOS
19
+ ios_file_system
20
+ when Platform::Android
21
+ android_file_system
22
+ else
23
+ raise ArgumentError, "Invalid platform: #{@platform}"
24
+ end
25
+ end
26
+
27
+ def android_file_system
28
+ [
29
+ {
30
+ name: FilePath.gradle_name,
31
+ path: File.join(FilePath.android_project_relative_root),
32
+ key: 'build.gradle',
33
+ type: 'file',
34
+ recursive: false,
35
+ platform: Platform::Android
36
+ },
37
+ {
38
+ name: FilePath.gradle_name,
39
+ path: File.join(FilePath.android_project_relative_root, 'app'),
40
+ key: 'app/build.gradle',
41
+ type: 'file',
42
+ recursive: false,
43
+ platform: Platform::Android,
44
+ }
45
+ ]
46
+ end
47
+
48
+ def ios_file_system
49
+ [
50
+ {
51
+ name: '*.xcodeproj',
52
+ key: 'xcodeproj',
53
+ type: 'folder',
54
+ platform: Platform::IOS,
55
+ },
56
+ {
57
+ name: 'Assets.xcassets',
58
+ key: 'Assets.xcassets',
59
+ type: 'folder',
60
+ platform: Platform::IOS,
61
+ },
62
+ {
63
+ name: 'Info.plist',
64
+ key: 'Info.plist',
65
+ type: 'file',
66
+ platform: Platform::IOS,
67
+ }
68
+ ]
69
+ end
70
+ end
@@ -0,0 +1,51 @@
1
+ structure:
2
+ android:
3
+ type: directory
4
+ contents:
5
+ assets:
6
+ type: directory
7
+ res:
8
+ type: directory
9
+ android_config.json:
10
+ type: file
11
+ validations:
12
+ - type: valid_json
13
+ - type: json_schema
14
+ schema_path: platform/android/android_config.json
15
+ android_signing.json:
16
+ type: file
17
+ validations:
18
+ - type: valid_json
19
+ - type: json_schema
20
+ schema_path: platform/android/android_signing.json
21
+ ios:
22
+ type: directory
23
+ contents:
24
+ assets:
25
+ type: directory
26
+ ios_config.json:
27
+ type: file
28
+ validations:
29
+ - type: valid_json
30
+ - type: json_schema
31
+ schema_path: platform/ios/ios_config.json
32
+ ios_signing.json:
33
+ type: file
34
+ validations:
35
+ - type: valid_json
36
+ - type: json_schema
37
+ schema_path: platform/ios/ios_signing.json
38
+ shared:
39
+ type: directory
40
+ contents:
41
+ brand_config.json:
42
+ type: file
43
+ validations:
44
+ - type: valid_json
45
+ theme.json:
46
+ type: file
47
+ validations:
48
+ - type: valid_json
49
+ - type: json_schema
50
+ schema_path: platform/shared/theme.json
51
+ strict: false