solara 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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