solara 0.5.0 → 0.7.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/solara/lib/.DS_Store +0 -0
  3. data/solara/lib/core/.DS_Store +0 -0
  4. data/solara/lib/core/brands/brand_onboarder.rb +29 -25
  5. data/solara/lib/core/brands/brand_switcher.rb +59 -24
  6. data/solara/lib/core/dashboard/brand/BrandDetailController.js +6 -3
  7. data/solara/lib/core/dashboard/brand/BrandDetailModel.js +1 -0
  8. data/solara/lib/core/dashboard/brand/SectionsFormManager.js +76 -15
  9. data/solara/lib/core/dashboard/brand/brand.html +47 -8
  10. data/solara/lib/core/dashboard/brand/source/BrandLocalSource.js +2 -2
  11. data/solara/lib/core/dashboard/brand/source/BrandRemoteSource.js +19 -3
  12. data/solara/lib/core/dashboard/component/OnboardBrandBottomSheet.js +4 -0
  13. data/solara/lib/core/dashboard/handler/edit_section_handler.rb +5 -17
  14. data/solara/lib/core/dashboard/handler/onboard_brand_handler.rb +0 -15
  15. data/solara/lib/core/doctor/schema/brand_configurations.json +2 -2
  16. data/solara/lib/core/doctor/schema/platform/json_manifest.json +20 -38
  17. data/solara/lib/core/doctor/validator/template/android_template_validation_config.yml +3 -3
  18. data/solara/lib/core/doctor/validator/template/flutter_template_validation_config.yml +2 -2
  19. data/solara/lib/core/doctor/validator/template/ios_template_validation_config.yml +3 -3
  20. data/solara/lib/core/scripts/brand_config_updater.rb +29 -0
  21. data/solara/lib/core/scripts/brand_configurations_manager.rb +76 -14
  22. data/solara/lib/core/scripts/brand_importer.rb +9 -20
  23. data/solara/lib/core/scripts/code_generator.rb +1 -1
  24. data/solara/lib/core/scripts/file_manager.rb +11 -15
  25. data/solara/lib/core/scripts/gitignore_manager.rb +3 -4
  26. data/solara/lib/core/scripts/json_manifest_processor.rb +86 -45
  27. data/solara/lib/core/scripts/resource_manifest_processor.rb +11 -10
  28. data/solara/lib/core/scripts/string_case.rb +18 -0
  29. data/solara/lib/core/template/.DS_Store +0 -0
  30. data/solara/lib/core/template/brands/json/Json-Manifest.md +8 -10
  31. data/solara/lib/core/template/brands/json/json_manifest.json +9 -11
  32. data/solara/lib/core/template/config/android_template_config.json +6 -6
  33. data/solara/lib/core/template/config/flutter_template_config.json +4 -4
  34. data/solara/lib/core/template/config/ios_template_config.json +6 -6
  35. data/solara/lib/core/template/configurations.json +34 -21
  36. data/solara/lib/core/template/project_template_generator.rb +119 -13
  37. data/solara/lib/solara/version.rb +1 -1
  38. data/solara/lib/solara_manager.rb +25 -13
  39. metadata +5 -2
@@ -0,0 +1,18 @@
1
+ class StringCase
2
+
3
+ def self.capitalize(string)
4
+ "#{string[0].upcase}#{string[1..-1]}"
5
+ end
6
+
7
+ def self.snake_to_capitalized_spaced(snake_case_string, exclude: '', transform: ->(item) { StringCase.capitalize(item) })
8
+ # Split by underscores, then apply the transformation to each part
9
+ parts = snake_case_string.split('_').map do |item|
10
+ # Return the item as-is if it matches the exclude value
11
+ item == exclude ? item : transform.call(item)
12
+ end
13
+
14
+ # Join the parts with a space
15
+ parts.join(' ')
16
+ end
17
+
18
+ end
Binary file
@@ -21,16 +21,14 @@ Each directory contains a `json_manifest.json` file with the following structure
21
21
  "files": [
22
22
  {
23
23
  "fileName": "",
24
- "generate": {
25
- "enabled": true,
26
- "className": "",
27
- "customClassNames": [
28
- {
29
- "generatedName": "",
30
- "customName": ""
31
- }
32
- ]
33
- }
24
+ "generate": true,
25
+ "parentClassName": "",
26
+ "customClassNames": [
27
+ {
28
+ "originalName": "",
29
+ "customName": ""
30
+ }
31
+ ]
34
32
  }
35
33
  ]
36
34
  }
@@ -1,18 +1,16 @@
1
1
  {
2
- "description": "This file specifies the JSON files that can be optionally generated for the target platform code.",
2
+ "description": "The JSON Manifest is intended to specify the files you need to customize its generated code",
3
3
  "files": [
4
4
  {
5
5
  "fileName": "",
6
- "generate": {
7
- "enabled": true,
8
- "className": "",
9
- "customClassNames": [
10
- {
11
- "generatedName": "",
12
- "customName": ""
13
- }
14
- ]
15
- }
6
+ "generate": true,
7
+ "parentClassName": "",
8
+ "customClassNames": [
9
+ {
10
+ "originalName": "",
11
+ "customName": ""
12
+ }
13
+ ]
16
14
  }
17
15
  ]
18
16
  }
@@ -72,22 +72,22 @@
72
72
  },
73
73
  {
74
74
  "source": "json/json_manifest.json",
75
- "target": "android/json/",
75
+ "target": "android/json/android_json_manifest.json",
76
76
  "condition": "true"
77
77
  },
78
78
  {
79
79
  "source": "json/json_manifest.json",
80
- "target": "ios/json/",
80
+ "target": "ios/json/ios_json_manifest.json",
81
81
  "condition": "true"
82
82
  },
83
83
  {
84
84
  "source": "json/Json-Manifest.md",
85
- "target": "android/json/",
85
+ "target": "android/json/Json-Manifest.md",
86
86
  "condition": "true"
87
87
  },
88
88
  {
89
89
  "source": "json/Json-Manifest.md",
90
- "target": "ios/json/",
90
+ "target": "ios/json/Json-Manifest.md",
91
91
  "condition": "true"
92
92
  },
93
93
  {
@@ -97,12 +97,12 @@
97
97
  },
98
98
  {
99
99
  "source": "json/json_manifest.json",
100
- "target": "../../global/json",
100
+ "target": "../../global/json/global_json_manifest.json",
101
101
  "condition": "true"
102
102
  },
103
103
  {
104
104
  "source": "json/Json-Manifest.md",
105
- "target": "../../global/json",
105
+ "target": "../../global/json/Json-Manifest.md",
106
106
  "condition": "true"
107
107
  },
108
108
  {
@@ -72,12 +72,12 @@
72
72
  },
73
73
  {
74
74
  "source": "json/json_manifest.json",
75
- "target": "flutter/json/",
75
+ "target": "flutter/json/flutter_json_manifest.json",
76
76
  "condition": "true"
77
77
  },
78
78
  {
79
79
  "source": "json/Json-Manifest.md",
80
- "target": "flutter/json/",
80
+ "target": "flutter/json/Json-Manifest.md",
81
81
  "condition": "true"
82
82
  },
83
83
  {
@@ -87,12 +87,12 @@
87
87
  },
88
88
  {
89
89
  "source": "json/json_manifest.json",
90
- "target": "../../global/json",
90
+ "target": "../../global/json/global_json_manifest.json",
91
91
  "condition": "true"
92
92
  },
93
93
  {
94
94
  "source": "json/Json-Manifest.md",
95
- "target": "../../global/json",
95
+ "target": "../../global/json/Json-Manifest.md",
96
96
  "condition": "true"
97
97
  },
98
98
  {
@@ -72,22 +72,22 @@
72
72
  },
73
73
  {
74
74
  "source": "json/json_manifest.json",
75
- "target": "android/json/",
75
+ "target": "android/json/android_json_manifest.json",
76
76
  "condition": "true"
77
77
  },
78
78
  {
79
79
  "source": "json/json_manifest.json",
80
- "target": "ios/json/",
80
+ "target": "ios/json/ios_json_manifest.json",
81
81
  "condition": "true"
82
82
  },
83
83
  {
84
84
  "source": "json/Json-Manifest.md",
85
- "target": "android/json/",
85
+ "target": "android/json/Json-Manifest.md",
86
86
  "condition": "true"
87
87
  },
88
88
  {
89
89
  "source": "json/Json-Manifest.md",
90
- "target": "ios/json/",
90
+ "target": "ios/json/Json-Manifest.md",
91
91
  "condition": "true"
92
92
  },
93
93
  {
@@ -97,12 +97,12 @@
97
97
  },
98
98
  {
99
99
  "source": "json/json_manifest.json",
100
- "target": "../../global/json",
100
+ "target": "../../global/json/global_json_manifest.json",
101
101
  "condition": "true"
102
102
  },
103
103
  {
104
104
  "source": "json/Json-Manifest.md",
105
- "target": "../../global/json",
105
+ "target": "../../global/json/Json-Manifest.md",
106
106
  "condition": "true"
107
107
  },
108
108
  {
@@ -1,46 +1,59 @@
1
1
  {
2
2
  "configurations": [
3
3
  {
4
- "key": "brand_config.json",
5
- "name": "Brand Configuration",
4
+ "filename": "brand_config.json",
6
5
  "filePath": "shared/brand_config.json",
7
- "url": "https://raw.githubusercontent.com/Solara-Kit/Solara/refs/heads/develop/solara/lib/core/template/brands/shared/brand_config.json"
6
+ "url": "https://raw.githubusercontent.com/Solara-Kit/Solara/refs/heads/main/solara/lib/core/template/brands/shared/brand_config.json"
8
7
  },
9
8
  {
10
- "key": "theme.json",
11
- "name": "Theme Configuration",
9
+ "filename": "theme.json",
12
10
  "filePath": "shared/theme.json",
13
- "url": "https://raw.githubusercontent.com/Solara-Kit/Solara/refs/heads/develop/solara/lib/core/template/brands/shared/theme.json"
11
+ "url": "https://raw.githubusercontent.com/Solara-Kit/Solara/refs/heads/main/solara/lib/core/template/brands/shared/theme.json"
14
12
  },
15
13
  {
16
- "key": "android_config.json",
17
- "name": "Android Configuration",
14
+ "filename": "android_config.json",
18
15
  "filePath": "android/android_config.json",
19
- "url": "https://raw.githubusercontent.com/Solara-Kit/Solara/refs/heads/develop/solara/lib/core/template/brands/android/android_config.json"
16
+ "url": "https://raw.githubusercontent.com/Solara-Kit/Solara/refs/heads/main/solara/lib/core/template/brands/android/android_config.json"
20
17
  },
21
18
  {
22
- "key": "android_signing.json",
23
- "name": "Android Signing",
19
+ "filename": "android_signing.json",
24
20
  "filePath": "android/android_signing.json",
25
- "url": "https://raw.githubusercontent.com/Solara-Kit/Solara/refs/heads/develop/solara/lib/core/template/brands/android/android_signing.json"
21
+ "url": "https://raw.githubusercontent.com/Solara-Kit/Solara/refs/heads/main/solara/lib/core/template/brands/android/android_signing.json"
26
22
  },
27
23
  {
28
- "key": "ios_config.json",
29
- "name": "iOS Configuration",
24
+ "filename": "ios_config.json",
30
25
  "filePath": "ios/ios_config.json",
31
- "url": "https://raw.githubusercontent.com/Solara-Kit/Solara/refs/heads/develop/solara/lib/core/template/brands/ios/ios_config.json"
26
+ "url": "https://raw.githubusercontent.com/Solara-Kit/Solara/refs/heads/main/solara/lib/core/template/brands/ios/ios_config.json"
32
27
  },
33
28
  {
34
- "key": "ios_signing.json",
35
- "name": "iOS Signing",
29
+ "filename": "ios_signing.json",
36
30
  "filePath": "ios/ios_signing.json",
37
- "url": "https://raw.githubusercontent.com/Solara-Kit/Solara/refs/heads/develop/solara/lib/core/template/brands/ios/ios_signing.json"
31
+ "url": "https://raw.githubusercontent.com/Solara-Kit/Solara/refs/heads/main/solara/lib/core/template/brands/ios/ios_signing.json"
38
32
  },
39
33
  {
40
- "key": "InfoPlist.xcstrings",
41
- "name": "InfoPlist.xcstrings",
34
+ "filename": "InfoPlist.xcstrings",
42
35
  "filePath": "ios/InfoPlist.xcstrings",
43
- "url": "https://raw.githubusercontent.com/Solara-Kit/Solara/refs/heads/develop/solara/lib/core/template/brands/ios/InfoPlist.xcstrings"
36
+ "url": "https://raw.githubusercontent.com/Solara-Kit/Solara/refs/heads/main/solara/lib/core/template/brands/ios/InfoPlist.xcstrings"
37
+ },
38
+ {
39
+ "filename": "ios_json_manifest.json",
40
+ "filePath": "ios/json/ios_json_manifest.json",
41
+ "url": "https://raw.githubusercontent.com/Solara-Kit/Solara/refs/heads/main/solara/lib/core/template/brands/json/json_manifest.json"
42
+ },
43
+ {
44
+ "filename": "android_json_manifest.json",
45
+ "filePath": "android/json/android_json_manifest.json",
46
+ "url": "https://raw.githubusercontent.com/Solara-Kit/Solara/refs/heads/main/solara/lib/core/template/brands/json/json_manifest.json"
47
+ },
48
+ {
49
+ "filename": "flutter_json_manifest.json",
50
+ "filePath": "flutter/json/flutter_json_manifest.json",
51
+ "url": "https://raw.githubusercontent.com/Solara-Kit/Solara/refs/heads/main/solara/lib/core/template/brands/json/json_manifest.json"
52
+ },
53
+ {
54
+ "filename": "global_json_manifest.json",
55
+ "filePath": "../../global/json/global_json_manifest.json",
56
+ "url": "https://raw.githubusercontent.com/Solara-Kit/Solara/refs/heads/main/solara/lib/core/template/brands/json/json_manifest.json"
44
57
  }
45
58
  ]
46
59
  }
@@ -7,28 +7,125 @@ class ProjectTemplateGenerator
7
7
  @target_dir = target_dir
8
8
  @config_file = config_file
9
9
  @config = read_config
10
+ @file_mappings = build_file_mappings
10
11
  end
11
12
 
12
13
  def create_project
13
14
  @config["files"].each do |file|
14
- if evaluate_condition(file["condition"], @config["variables"])
15
- source_path = File.join(@template_dir, file["source"])
16
- target_path = File.join(@target_dir, file["target"])
15
+ next unless evaluate_condition(file["condition"], @config["variables"])
16
+ source_path = File.join(@template_dir, file["source"])
17
+ target_path = File.join(@target_dir, file["target"])
17
18
 
18
- if file["source"].nil? || file["source"].empty? || !File.exist?(source_path)
19
- # Create the target directory if no source path is provided
20
- FileUtils.mkdir_p(target_path)
21
- else
22
- copy_content = file.fetch("copy_content", true)
23
- copy_item(source_path, target_path, copy_content)
24
- replace_variables(target_path, @config["variables"]) if copy_content
25
- end
19
+ if file["source"].nil? || file["source"].empty? || !File.exist?(source_path)
20
+ # Create the target directory if no source path is provided
21
+ FileUtils.mkdir_p(target_path)
22
+ else
23
+ copy_content = file.fetch("copy_content", true)
24
+ copy_item(source_path, target_path, copy_content, file)
25
+ replace_variables(target_path, @config["variables"]) if copy_content
26
+ end
27
+ end
28
+ end
29
+
30
+ def sync_with_template
31
+ @config["files"].each do |file|
32
+ next unless evaluate_condition(file["condition"], @config["variables"])
33
+ next if file["source"].nil? || file["source"].empty?
34
+
35
+ source_path = File.join(@template_dir, file["source"])
36
+ target_path = File.join(@target_dir, file["target"])
37
+
38
+ next unless File.exist?(source_path)
39
+
40
+ if File.directory?(source_path)
41
+ FileUtils.mkdir_p(target_path) unless Dir.exist?(target_path)
42
+ sync_directory(source_path, target_path, file)
43
+ elsif !File.exist?(target_path) && should_copy_path?(source_path)
44
+ FileUtils.mkdir_p(File.dirname(target_path))
45
+ FileUtils.cp(source_path, target_path)
46
+ copy_content = file.fetch("copy_content", true)
47
+ replace_variables(target_path, @config["variables"]) if copy_content
26
48
  end
27
49
  end
28
50
  end
29
51
 
30
52
  private
31
53
 
54
+ # This method:
55
+ # - Creates a hash of source paths to their targets
56
+ # - Removes leading slashes for consistency
57
+ # - Handles directories differently from files
58
+ # - For directories: stores true to indicate it's a directory that should be copied
59
+ # For files: stores the specific target path
60
+ def build_file_mappings
61
+ mappings = {}
62
+ @config["files"].each do |file|
63
+ source_path = file["source"].sub(/^\//, '') # Removes leading slashes for consistency
64
+ if File.directory?(File.join(@template_dir, source_path))
65
+ mappings[source_path] = true # Directories are marked as true
66
+ else
67
+ mappings[source_path] = file["target"] # Files store their target path
68
+ end
69
+ end
70
+ mappings
71
+ end
72
+
73
+ # This method:
74
+ # - Converts the full path to a relative path
75
+ # - Checks if the path matches any configured source
76
+ # - For directories (ending with '/'): checks if the path is within that directory
77
+ # - For files: checks for exact matches
78
+ # - Returns false if no match is found
79
+ def should_copy_path?(path)
80
+ relative_path = path.sub(@template_dir, '').sub(/^\//, '')
81
+
82
+ @file_mappings.each do |source, target|
83
+ if source.end_with?('/')
84
+ return true if relative_path.start_with?(source)
85
+ else
86
+ return true if relative_path == source
87
+ end
88
+ end
89
+
90
+ false
91
+ end
92
+
93
+ def get_target_path(source_path)
94
+ relative_path = source_path.sub(@template_dir, '').sub(/^\//, '')
95
+ @config["files"].each do |file|
96
+ if file["source"] == relative_path
97
+ return File.join(@target_dir, file["target"])
98
+ end
99
+ end
100
+ nil
101
+ end
102
+
103
+ def sync_directory(source_dir, target_dir, config_entry)
104
+ return unless File.directory?(source_dir)
105
+
106
+ Dir.foreach(source_dir) do |item|
107
+ next if item == '.' || item == '..'
108
+
109
+ source_path = File.join(source_dir, item)
110
+
111
+ if specific_target = get_target_path(source_path)
112
+ target_path = specific_target
113
+ else
114
+ target_path = File.join(target_dir, item)
115
+ end
116
+
117
+ next unless should_copy_path?(source_path)
118
+
119
+ if File.directory?(source_path)
120
+ FileUtils.mkdir_p(target_path) unless Dir.exist?(target_path)
121
+ sync_directory(source_path, target_path, config_entry)
122
+ elsif !File.exist?(target_path)
123
+ FileUtils.mkdir_p(File.dirname(target_path))
124
+ FileUtils.cp(source_path, target_path)
125
+ end
126
+ end
127
+ end
128
+
32
129
  def read_config
33
130
  JSON.parse(File.read(@config_file))
34
131
  end
@@ -37,12 +134,21 @@ class ProjectTemplateGenerator
37
134
  true
38
135
  end
39
136
 
40
- def copy_item(source, target, copy_content)
137
+ def copy_item(source, target, copy_content, config_entry)
41
138
  if File.directory?(source)
42
139
  FileUtils.mkdir_p(target)
43
140
  Dir.foreach(source) do |item|
44
141
  next if item == '.' || item == '..'
45
- copy_item(File.join(source, item), File.join(target, item), copy_content)
142
+ source_path = File.join(source, item)
143
+
144
+ if specific_target = get_target_path(source_path)
145
+ target_path = specific_target
146
+ else
147
+ target_path = File.join(target, item)
148
+ end
149
+
150
+ next unless should_copy_path?(source_path)
151
+ copy_item(source_path, target_path, copy_content, config_entry)
46
152
  end
47
153
  else
48
154
  FileUtils.mkdir_p(File.dirname(target))
@@ -1,3 +1,3 @@
1
1
  module Solara
2
- VERSION = "0.5.0"
2
+ VERSION = "0.7.0"
3
3
  end
@@ -32,24 +32,32 @@ class SolaraManager
32
32
  end
33
33
 
34
34
  def onboard(brand_key, brand_name, init: false, clone_brand_key: nil, open_dashboard: true, success_message: nil)
35
- Solara.logger.header("Onboarding #{brand_key}")
35
+ begin
36
+ Solara.logger.header("Onboarding #{brand_key}")
36
37
 
37
- if !init && BrandsManager.instance.exists(brand_key)
38
- Solara.logger.fatal("Brand with key (#{brand_key}) already added to brands!")
39
- return
40
- end
38
+ if !init && BrandsManager.instance.exists(brand_key)
39
+ Solara.logger.fatal("Brand with key (#{brand_key}) already added to brands!")
40
+ return
41
+ end
42
+
43
+ BrandOnboarder.new.onboard(brand_key, brand_name, clone_brand_key: clone_brand_key)
41
44
 
42
- BrandOnboarder.new(brand_key, brand_name, clone_brand_key: clone_brand_key).onboard
45
+ switch(brand_key, ignore_health_check: true)
43
46
 
44
- switch(brand_key, ignore_health_check: true)
45
47
 
46
- clone_message = clone_brand_key.nil? || clone_brand_key.empty? ? '.' : ", cloned from #{clone_brand_key}."
47
- message = success_message || "Onboarded #{brand_key} successfully#{clone_message}"
48
- Solara.logger.success(message)
48
+ clone_message = clone_brand_key.nil? || clone_brand_key.empty? ? '.' : ", cloned from #{clone_brand_key}."
49
+ message = success_message || "Onboarded #{brand_key} successfully#{clone_message}"
50
+ Solara.logger.success(message)
49
51
 
50
- if open_dashboard
51
- Solara.logger.success("Openning the dashboard for #{brand_key} to complete its details.")
52
- dashboard(brand_key)
52
+ if open_dashboard
53
+ Solara.logger.success("Openning the dashboard for #{brand_key} to complete its details.")
54
+ dashboard(brand_key)
55
+ end
56
+ rescue => e
57
+ # Rollback this brand
58
+ offboard(brand_key, confirm: false)
59
+ Solara.logger.debug("Performed rollback for (#{brand_key}).")
60
+ raise e
53
61
  end
54
62
  end
55
63
 
@@ -70,4 +78,8 @@ class SolaraManager
70
78
  DoctorManager.new.visit_brands(keys, print_logs: print_logs)
71
79
  end
72
80
 
81
+ def sync_brand_with_template(brand_key)
82
+ BrandOnboarder.new.sync_with_template(brand_key)
83
+ end
84
+
73
85
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solara
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Malek Kamel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-18 00:00:00.000000000 Z
11
+ date: 2024-10-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -239,6 +239,7 @@ files:
239
239
  - solara/lib/core/doctor/validator/template/template_validator.rb
240
240
  - solara/lib/core/doctor/validator/validation_strategy.rb
241
241
  - solara/lib/core/scripts/brand_config_manager.rb
242
+ - solara/lib/core/scripts/brand_config_updater.rb
242
243
  - solara/lib/core/scripts/brand_configurations_manager.rb
243
244
  - solara/lib/core/scripts/brand_exporter.rb
244
245
  - solara/lib/core/scripts/brand_importer.rb
@@ -270,11 +271,13 @@ files:
270
271
  - solara/lib/core/scripts/solara_settings_manager.rb
271
272
  - solara/lib/core/scripts/solara_status_manager.rb
272
273
  - solara/lib/core/scripts/solara_version_manager.rb
274
+ - solara/lib/core/scripts/string_case.rb
273
275
  - solara/lib/core/scripts/strings_xml_manager.rb
274
276
  - solara/lib/core/scripts/terminal_input_manager.rb
275
277
  - solara/lib/core/scripts/theme_generator.rb
276
278
  - solara/lib/core/scripts/yaml_manager.rb
277
279
  - solara/lib/core/solara_configurator.rb
280
+ - solara/lib/core/template/.DS_Store
278
281
  - solara/lib/core/template/brands/android/android_config.json
279
282
  - solara/lib/core/template/brands/android/android_signing.json
280
283
  - solara/lib/core/template/brands/android/res/.DS_Store