solara 0.5.0 → 0.7.0

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