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,19 @@
1
+ class BrandOffboarder
2
+
3
+ def offboard(brand_key, confirm: true)
4
+ if confirm
5
+ Solara.logger.warn("Are you sure you need to offboard #{brand_key} and delete all its configurations? (y/n)")
6
+ confirmation = STDIN.gets.chomp.downcase
7
+
8
+ unless confirmation == 'y'
9
+ Solara.logger.info("Offboarding #{brand_key} cancelled.")
10
+ return
11
+ end
12
+ end
13
+ BrandsManager.instance.offboard(brand_key)
14
+ Solara.logger.success("Offboarded #{brand_key} successfully.")
15
+ is_current_brand = BrandsManager.instance.is_current_brand(brand_key)
16
+ SolaraManager.new.switch(BrandsManager.instance.first_brand_key, ignore_health_check: true) if is_current_brand
17
+ end
18
+
19
+ end
@@ -0,0 +1,77 @@
1
+ require 'fileutils'
2
+ require_relative 'file_manager'
3
+ require_relative 'yaml_manager'
4
+
5
+ class BrandResourcesManager
6
+ def initialize(brand_key)
7
+ @brand_key = brand_key
8
+ end
9
+
10
+ require 'fileutils'
11
+
12
+ def update_ios
13
+ Solara.logger.start_step("Update iOS resources")
14
+ source = IOSFilePathManager.instance.brand_assets(@brand_key)
15
+ destination = IOSFilePathManager.instance.assets_artifacts
16
+ FileManager.delete_if_exists(destination)
17
+
18
+ # Copy images and set the Content.json for each one
19
+ add_to_asset_catalog
20
+
21
+ # Copy all folders only in source to destination
22
+ Dir.glob("#{source}/*").each do |item|
23
+ if File.directory?(item)
24
+ FileUtils.cp_r(item, destination)
25
+ end
26
+ end
27
+
28
+ Solara.logger.end_step("Update iOS resources")
29
+ end
30
+
31
+ def add_to_asset_catalog
32
+ source = IOSFilePathManager.instance.brand_assets(@brand_key)
33
+ destination = IOSFilePathManager.instance.assets_artifacts
34
+ asset_manager = XcodeAssetManager.new(destination)
35
+ asset_manager.add(source)
36
+ end
37
+
38
+ def update_android
39
+ Solara.logger.start_step("Update Android resources")
40
+ FileManager.new.copy_files_recursively(
41
+ FilePath.android_brand_res(@brand_key),
42
+ FilePath.android_res_artifacts)
43
+
44
+ assets_artifacts = File.join(FilePath.android_project_assets, 'artifacts')
45
+ FileManager.delete_if_exists(assets_artifacts)
46
+
47
+ FileManager.new.copy_files_recursively(
48
+ FilePath.android_brand_assets(@brand_key),
49
+ assets_artifacts)
50
+
51
+ # Delete the original launcher icons to fix duplicate resources
52
+ FileManager.new.delete_folders_by_prefix(FilePath.android_res, 'mipmap')
53
+ Solara.logger.end_step("Update Android resources")
54
+ end
55
+
56
+ def update_flutter
57
+ Solara.logger.start_step("Update Flutter resources")
58
+ case SolaraSettingsManager.instance.platform
59
+ when Platform::Flutter
60
+ YamlManager.new(FilePath.pub_spec_yaml).add_to_nested_array(
61
+ 'flutter',
62
+ 'assets',
63
+ 'assets/artifacts/')
64
+
65
+ destination = FilePath.flutter_assets_artifacts
66
+ FileManager.delete_if_exists(destination)
67
+
68
+ FileManager.new.copy_files_recursively(
69
+ FilePath.brand_flutter_assets(@brand_key),
70
+ destination)
71
+ else
72
+ # Nothing
73
+ end
74
+
75
+ Solara.logger.end_step("Update Flutter resources")
76
+ end
77
+ end
@@ -0,0 +1,22 @@
1
+ class DirectoryCreator
2
+ def self.create_directories(directories, delete_if_exists = false)
3
+ directories.each do |dir|
4
+ create_directory(dir, delete_if_exists)
5
+ end
6
+ end
7
+
8
+ private
9
+
10
+ def self.create_directory(dir, delete_if_exists)
11
+ if Dir.exist?(dir)
12
+ if delete_if_exists
13
+ FileUtils.rm_rf(dir)
14
+ Solara.logger.debug("🧹 Deleted directory: #{dir}")
15
+ Dir.mkdir(dir)
16
+ end
17
+ else
18
+ Dir.mkdir(dir)
19
+ end
20
+ Solara.logger.debug("✨ Created directory: #{dir}")
21
+ end
22
+ end
@@ -0,0 +1,90 @@
1
+ require 'find'
2
+
3
+ class FileManager
4
+ def copy_files_recursively(source_dir, destination_dir)
5
+ if Dir.exist?(source_dir)
6
+ Dir.glob(File.join(source_dir, '*')).each do |item|
7
+ relative_path = Pathname.new(item).relative_path_from(Pathname.new(source_dir)).to_s
8
+ destination_path = File.join(destination_dir, relative_path)
9
+
10
+ if File.directory?(item)
11
+ FileUtils.mkdir_p(destination_path)
12
+ FileUtils.cp_r(File.join(item, '.'), destination_path)
13
+ else
14
+ FileUtils.mkdir_p(File.dirname(destination_path))
15
+ FileUtils.cp(item, destination_path)
16
+ end
17
+
18
+ Solara.logger.debug("🚗 Copied #{relative_path} \n\t↖️From: #{source_dir} \n\t↗️To: #{destination_dir}")
19
+ end
20
+ else
21
+ Solara.logger.failure("#{source_dir} not found in Solara")
22
+ end
23
+ end
24
+
25
+ def delete_folders_by_prefix(directory, folder_prefix)
26
+ # Get a list of all folders in the directory
27
+ folders = Dir.entries(directory).select { |entry| File.directory?(File.join(directory, entry)) && entry.start_with?(folder_prefix) }
28
+
29
+ # Delete each folder
30
+ folders.each do |folder|
31
+ folder_path = File.join(directory, folder)
32
+ FileUtils.rm_rf(folder_path)
33
+ Solara.logger.debug("🧹 Deleted folder: #{folder_path}")
34
+ end
35
+ end
36
+
37
+ def self.create_file_if_not_exist(file_path)
38
+ unless File.exist?(file_path)
39
+ FileUtils.mkdir_p(File.dirname(file_path))
40
+ File.open(file_path, 'w') {}
41
+ Solara.logger.debug("✨ Created file: #{file_path}")
42
+ end
43
+ end
44
+
45
+ def self.create_files_if_not_exist(file_paths)
46
+ file_paths.each do |file_path|
47
+ create_file_if_not_exist(file_path)
48
+ end
49
+ end
50
+
51
+ def self.find_file_extesion(extension)
52
+ files = []
53
+ Find.find(SolaraSettingsManager.instance.project_root) do |path|
54
+ if FileTest.file?(path) && File.extname(path) == extension
55
+ files << path
56
+ end
57
+ end
58
+ files
59
+ end
60
+
61
+ def self.find_files_by_extension(directory, extension)
62
+ Dir.glob(File.join(directory, "**", "*#{extension}"))
63
+ end
64
+
65
+ def self.find_files_by_name(directory, filename)
66
+ Dir.glob(File.join(directory, "**", filename)).to_a
67
+ end
68
+
69
+ def self.get_relative_path(root_dir, sub_dir)
70
+ Pathname(sub_dir).relative_path_from(Pathname(root_dir)).to_s
71
+ rescue
72
+ sub_dir
73
+ end
74
+
75
+ def self.get_relative_path_to_root(sub_dir)
76
+ Pathname(sub_dir).relative_path_from(Pathname(FilePath.project_root)).to_s
77
+ rescue
78
+ sub_dir
79
+ end
80
+
81
+ def self.get_folders(source)
82
+ # Fetches only directories in the specified source directory
83
+ Dir.glob(File.join(source, '*')).select { |path| File.directory?(path) }
84
+ end
85
+
86
+ def self.delete_if_exists(path)
87
+ FileUtils.rm_rf(path) if File.exist?(path)
88
+ end
89
+
90
+ end
@@ -0,0 +1,327 @@
1
+ Dir.glob("#{__dir__}/*.rb").each { |file| require file }
2
+
3
+ module FilePath
4
+
5
+ def self.project_root
6
+ SolaraSettingsManager.instance.project_root
7
+ end
8
+
9
+ def self.root
10
+ SolaraSettingsManager.instance.root
11
+ end
12
+
13
+ def self.android
14
+ "android"
15
+ end
16
+
17
+ def self.android_project_root
18
+ case SolaraSettingsManager.instance.platform
19
+ when Platform::Flutter
20
+ File.join(project_root, android)
21
+ when Platform::Android
22
+ project_root
23
+ else
24
+ raise ArgumentError, "Invalid platform: #{SolaraSettingsManager.instance.platform}"
25
+ end
26
+ end
27
+
28
+ def self.ios_project_root
29
+ case SolaraSettingsManager.instance.platform
30
+ when Platform::Flutter
31
+ File.join(project_root, ios)
32
+ when Platform::IOS
33
+ project_root
34
+ else
35
+ raise ArgumentError, "Invalid platform: #{SolaraSettingsManager.instance.platform}"
36
+ end
37
+ end
38
+
39
+ def self.android_project_relative_root
40
+ case SolaraSettingsManager.instance.platform
41
+ when Platform::Flutter
42
+ android
43
+ when Platform::Android
44
+ ''
45
+ else
46
+ raise ArgumentError, "Invalid platform: #{SolaraSettingsManager.instance.platform}"
47
+ end
48
+ end
49
+
50
+ def self.android_brand_relative_root
51
+ android
52
+ end
53
+
54
+ def self.android_brand_root(brand_key)
55
+ brand_root(android, brand_key)
56
+ end
57
+
58
+ def self.ios_brand_root(brand_key)
59
+ brand_root(ios, brand_key)
60
+ end
61
+
62
+ def self.brand_root(platform, brand_key)
63
+ File.join(brands, brand_key, platform)
64
+ end
65
+
66
+ def self.solara
67
+ File.join(Dir.pwd, 'solara')
68
+ end
69
+
70
+ def self.dot_solara
71
+ File.join(project_root, 'solara', '.solara')
72
+ end
73
+
74
+ def self.ios
75
+ "ios"
76
+ end
77
+
78
+ def self.brands
79
+ File.join(project_root, 'solara', 'brands')
80
+ end
81
+
82
+ def self.brand(brand_key)
83
+ File.join(brands, brand_key)
84
+ end
85
+
86
+ def self.android_config(brand_key)
87
+ File.join(android_brand_root(brand_key), 'android_config.json')
88
+ end
89
+
90
+ def self.ios_config(brand_key)
91
+ File.join(ios_brand_root(brand_key), 'ios_config.json')
92
+ end
93
+
94
+ def self.brand_config(brand_key)
95
+ File.join(brands, brand_key, 'shared', 'brand_config.json')
96
+ end
97
+
98
+ def self.brand_theme(brand_key)
99
+ File.join(brands, brand_key, 'shared', 'theme.json')
100
+ end
101
+
102
+ def self.brand_signing(brand_key, platform)
103
+ config("#{platform}_signing.json", brand_key, platform)
104
+ end
105
+
106
+ def self.android_brand_signing(brand_key)
107
+ brand_signing(brand_key, Platform::Android)
108
+ end
109
+
110
+ def self.ios_brand_signing(brand_key)
111
+ brand_signing(brand_key, Platform::IOS)
112
+ end
113
+
114
+ def self.config(name, brand_key, platform = SolaraSettingsManager.instance.platform)
115
+ case platform
116
+ when Platform::Flutter
117
+ File.join(brands, brand_key, 'shared', "#{name}")
118
+ when Platform::Android
119
+ File.join(android_brand_root(brand_key), "#{name}")
120
+ when Platform::IOS
121
+ File.join(ios_brand_root(brand_key), "#{name}")
122
+ else
123
+ raise ArgumentError, "Invalid platform: #{SolaraSettingsManager.instance.platform}"
124
+ end
125
+ end
126
+
127
+ def self.generated_config(name, platform)
128
+ case platform
129
+ when Platform::Flutter
130
+ File.join(flutter_artifacts, name)
131
+ when Platform::Android
132
+ File.join(android_main_artifacts, name)
133
+ when Platform::IOS
134
+ File.join(IOSFilePathManager.instance.artifacts, name)
135
+ else
136
+ raise ArgumentError, "Invalid platform: #{SolaraSettingsManager.instance.platform}"
137
+ end
138
+ end
139
+
140
+ def self.flutter_artifacts
141
+ File.join(project_root, 'lib', 'artifacts')
142
+ end
143
+
144
+ def self.android_artifacts
145
+ File.join(android_project_root, 'artifacts')
146
+ end
147
+
148
+ def self.android_main_artifacts
149
+ File.join(android_project_root, 'app', 'src', 'main', 'java', 'artifacts')
150
+ end
151
+
152
+ def self.android_brand_config(brand_key)
153
+ File.join(android_brand_root(brand_key), 'android_config.json')
154
+ end
155
+
156
+ def self.android_app_gradle
157
+ File.join(android_project_root, 'app', gradle_name)
158
+ end
159
+
160
+ def self.android_app_gradle_by_type(type = '')
161
+ File.join(android_project_root, 'app', "build.gradle#{type}")
162
+ end
163
+
164
+ def self.is_koltin_gradle
165
+ File.exist?(android_app_gradle_by_type('.kts'))
166
+ end
167
+
168
+ def self.gradle_name
169
+ is_koltin_gradle ? 'build.gradle.kts' : 'build.gradle'
170
+ end
171
+
172
+ def self.android_manifest
173
+ File.join(android_project_root, 'app', 'src', 'main', 'AndroidManifest.xml')
174
+ end
175
+
176
+ def self.android_project_assets
177
+ File.join(android_project_root, 'app', 'src', 'main', 'assets')
178
+ end
179
+
180
+ def self.android_brand_assets(brand_key)
181
+ File.join(android_brand_root(brand_key), 'assets')
182
+ end
183
+
184
+ def self.android_generated_properties
185
+ File.join(android_artifacts, 'brand.properties')
186
+ end
187
+
188
+ def self.android_res
189
+ File.join(android_project_root, 'app', 'src', 'main', 'res')
190
+ end
191
+
192
+ def self.android_res_artifacts
193
+ File.join(android_project_root, 'app', 'src', 'main', 'artifacts')
194
+ end
195
+
196
+ def self.android_artifacts_strings
197
+ File.join(android_res_artifacts, 'values', 'strings.xml')
198
+ end
199
+
200
+ def self.android_strings
201
+ File.join(android_project_root, 'app', 'src', 'main', 'res', 'values', 'strings.xml')
202
+ end
203
+
204
+ def self.android_brand_res(brand_key)
205
+ File.join(android_brand_root(brand_key), 'res')
206
+ end
207
+
208
+ def self.brand_flutter_assets(brand_key)
209
+ File.join(brands, brand_key, 'shared', 'assets')
210
+ end
211
+
212
+ def self.flutter_assets_artifacts
213
+ File.join(project_root, 'assets', 'artifacts')
214
+ end
215
+
216
+ def self.pub_spec_yaml
217
+ File.join(project_root, 'pubspec.yaml')
218
+ end
219
+
220
+ def self.flutter_artifacts_config
221
+ File.join(flutter_artifacts, 'app_config.dart')
222
+ end
223
+
224
+ def self.dashboard
225
+ File.join(root, 'core', 'dashboard')
226
+ end
227
+
228
+ def self.brands_list
229
+ File.join(brands, 'brands.json')
230
+ end
231
+
232
+ def self.current_brand
233
+ File.join(brands, 'current_brand.json')
234
+ end
235
+
236
+ def self.solara_template_brands(platform)
237
+ File.join(solara_template, 'brands', platform)
238
+ end
239
+
240
+ def self.template_brands
241
+ File.join(solara_template, 'brands')
242
+ end
243
+
244
+ def self.template_config
245
+ platform = SolaraSettingsManager.instance.platform
246
+ File.join(solara_template, 'config', "#{platform}_template_config.json")
247
+ end
248
+
249
+ def self.template_validation_config
250
+ platform = SolaraSettingsManager.instance.platform
251
+ File.join(root, 'core', 'doctor', 'validator', 'template', "#{platform}_template_validation_config.yml")
252
+ end
253
+
254
+ def self.solara_template_brands_json
255
+ File.join(root, 'core', 'template', 'brands', 'brands.json')
256
+ end
257
+
258
+ def self.solara_template
259
+ File.join(root, 'core', 'template')
260
+ end
261
+
262
+ def self.solara_aliases_json
263
+ File.join(dot_solara, 'aliases', 'aliases.json')
264
+ end
265
+
266
+ def self.project_settings
267
+ File.join(dot_solara, 'project_settings.json')
268
+ end
269
+
270
+ def self.solara_settings
271
+ File.join(dot_solara, 'solara_settings.json')
272
+ end
273
+
274
+ def self.solara_version
275
+ File.join(dot_solara, 'solara_version.json')
276
+ end
277
+
278
+ def self.solara_aliases_sh
279
+ File.join(ENV['HOME'], '.solara', 'aliases.sh')
280
+ end
281
+
282
+ def self.solara_aliases_readme
283
+ File.join(dot_solara, 'aliases', 'README.md')
284
+ end
285
+
286
+ def self.android_launcher_icon(brand_key)
287
+ paths = %w[mipmap-xxxhdpi mipmap-xxhdpi mipmap-xhdpi mipmap-mdpi mipmap-hdpi]
288
+
289
+ paths.each do |path|
290
+ full_path = File.join(android_brand_root(brand_key), 'res', path)
291
+ return File.join(full_path, 'ic_launcher.png') if File.exist?(full_path)
292
+ end
293
+
294
+ nil # No existing path found
295
+ end
296
+
297
+ def self.launcher_icon(brand_key)
298
+ case SolaraSettingsManager.instance.platform
299
+ when Platform::Flutter
300
+ return android_launcher_icon(brand_key)
301
+ when Platform::Android
302
+ return android_launcher_icon(brand_key)
303
+ when Platform::IOS
304
+ path = IOSFilePathManager.instance.brand_app_icon_image(brand_key)
305
+ return path
306
+ else
307
+ raise ArgumentError, "Invalid platform: #{SolaraSettingsManager.instance.platform}"
308
+ end
309
+ end
310
+
311
+ def self.schema
312
+ File.join(root, 'core', 'doctor', 'schema')
313
+ end
314
+
315
+ def self.brand_configurations_schema
316
+ File.join(schema, 'brand_configurations.json')
317
+ end
318
+
319
+ def self.flutter_xcconfig_debug
320
+ File.join(project_root, ios, 'Flutter', 'Debug.xcconfig')
321
+ end
322
+
323
+ def self.flutter_xcconfig_release
324
+ File.join(project_root, ios, 'Flutter', 'Release.xcconfig')
325
+ end
326
+
327
+ end
@@ -0,0 +1,41 @@
1
+ require 'fileutils'
2
+
3
+ class FolderCopier
4
+ def initialize(source_path, destination_path)
5
+ @source_path = source_path
6
+ @destination_path = destination_path
7
+ end
8
+
9
+ def copy(delete_if_exists: true)
10
+ if File.exist?(@destination_path)
11
+ if delete_if_exists
12
+ FileUtils.rm_rf(@destination_path)
13
+ end
14
+ end
15
+ FileUtils.mkdir_p(@destination_path)
16
+ copy_files_and_folders(@source_path, @destination_path)
17
+ end
18
+
19
+ private
20
+
21
+ def copy_files_and_folders(source, destination)
22
+ if File.file?(source)
23
+ FileUtils.cp_r(source, destination)
24
+ elsif File.directory?(source)
25
+ Dir.foreach(source) do |item|
26
+ next if item == '.' || item == '..'
27
+ source_item_path = File.join(source, item)
28
+ destination_item_path = File.join(destination, item)
29
+
30
+ if File.directory?(source_item_path)
31
+ FileUtils.mkdir_p(destination_item_path) unless File.directory?(destination_item_path)
32
+ copy_files_and_folders(source_item_path, destination_item_path)
33
+ else
34
+ FileUtils.cp_r(source_item_path, destination_item_path)
35
+ end
36
+ end
37
+ else
38
+ raise "Source is not a file or directory: #{source}"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,54 @@
1
+ class GitignoreManager
2
+ def initialize(gitignore_path = '.gitignore')
3
+ @gitignore_path = gitignore_path.end_with?('.gitignore') ? gitignore_path : File.join(gitignore_path, '.gitignore')
4
+ create_gitignore_if_not_exists
5
+ end
6
+
7
+ def self.ignore_redundant_files
8
+ GitignoreManager.new(FilePath.project_root)
9
+ .add_items([
10
+ "# Generated by Solara. Ignore redundant brand specific changes.",
11
+ "**/artifacts/",
12
+ "**/Artifacts/",
13
+ "solara/brands/current_app.json",
14
+ "solara/.solara/aliases/",
15
+ "solara/.solara/solara_settings.json",
16
+ ])
17
+ end
18
+
19
+ def add_items(items)
20
+ items.each do |item|
21
+ add_item(item)
22
+ end
23
+ end
24
+
25
+ def add_item(item)
26
+ existing_items = read_gitignore
27
+
28
+ if existing_items.include?(item)
29
+ Solara.logger.debug("'#{item}' already exists in .gitignore")
30
+ else
31
+ File.open(@gitignore_path, 'a') do |file|
32
+ file.puts(item)
33
+ end
34
+ Solara.logger.debug("Added '#{item}' to .gitignore")
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def create_gitignore_if_not_exists
41
+ unless File.exist?(@gitignore_path)
42
+ File.open(@gitignore_path, 'w') do |file|
43
+ file.puts("# Generated by Solara")
44
+ file.puts("# Git ignore file")
45
+ file.puts("# Add items to ignore below")
46
+ end
47
+ Solara.logger.debug("Created new .gitignore file at #{@gitignore_path}")
48
+ end
49
+ end
50
+
51
+ def read_gitignore
52
+ File.readlines(@gitignore_path).map(&:chomp)
53
+ end
54
+ end