solara 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/solara +18 -0
- data/solara/lib/.DS_Store +0 -0
- data/solara/lib/core/.DS_Store +0 -0
- data/solara/lib/core/aliases/alias_generator.rb +128 -0
- data/solara/lib/core/aliases/alias_generator_manager.rb +28 -0
- data/solara/lib/core/aliases/solara_terminal_setup.rb +103 -0
- data/solara/lib/core/brands/brand_onboarder.rb +46 -0
- data/solara/lib/core/brands/brand_switcher.rb +204 -0
- data/solara/lib/core/brands/brands_manager.rb +154 -0
- data/solara/lib/core/dashboard/.DS_Store +0 -0
- data/solara/lib/core/dashboard/brand/.DS_Store +0 -0
- data/solara/lib/core/dashboard/brand/BrandDetail.js +11 -0
- data/solara/lib/core/dashboard/brand/BrandDetailController.js +361 -0
- data/solara/lib/core/dashboard/brand/BrandDetailModel.js +155 -0
- data/solara/lib/core/dashboard/brand/BrandDetailView.js +245 -0
- data/solara/lib/core/dashboard/brand/brand.html +477 -0
- data/solara/lib/core/dashboard/brand/source/BrandLocalSource.js +123 -0
- data/solara/lib/core/dashboard/brand/source/BrandRemoteSource.js +260 -0
- data/solara/lib/core/dashboard/brands/Brands.js +10 -0
- data/solara/lib/core/dashboard/brands/BrandsController.js +155 -0
- data/solara/lib/core/dashboard/brands/BrandsModel.js +136 -0
- data/solara/lib/core/dashboard/brands/BrandsView.js +136 -0
- data/solara/lib/core/dashboard/brands/brands.html +345 -0
- data/solara/lib/core/dashboard/component/AddFieldSheet.js +212 -0
- data/solara/lib/core/dashboard/component/AliasesBottomSheet.js +128 -0
- data/solara/lib/core/dashboard/component/BrandOptionsBottomSheet.js +130 -0
- data/solara/lib/core/dashboard/component/ConfirmationDialog.js +103 -0
- data/solara/lib/core/dashboard/component/MessageBottomSheet.js +80 -0
- data/solara/lib/core/dashboard/component/OnboardBrandBottomSheet.js +214 -0
- data/solara/lib/core/dashboard/dashboard_manager.rb +19 -0
- data/solara/lib/core/dashboard/dashboard_server.rb +132 -0
- data/solara/lib/core/dashboard/handler/base_handler.rb +25 -0
- data/solara/lib/core/dashboard/handler/brand_alisases_handler.rb +33 -0
- data/solara/lib/core/dashboard/handler/brand_configurations_handler.rb +18 -0
- data/solara/lib/core/dashboard/handler/brand_configurations_manager.rb +73 -0
- data/solara/lib/core/dashboard/handler/brand_icon_handler.rb +20 -0
- data/solara/lib/core/dashboard/handler/brand_section_handler.rb +20 -0
- data/solara/lib/core/dashboard/handler/brands_handler.rb +14 -0
- data/solara/lib/core/dashboard/handler/current_brand_handler.rb +18 -0
- data/solara/lib/core/dashboard/handler/doctor_handler.rb +39 -0
- data/solara/lib/core/dashboard/handler/edit_section_handler.rb +55 -0
- data/solara/lib/core/dashboard/handler/offboard_brand_handler.rb +34 -0
- data/solara/lib/core/dashboard/handler/onboard_brand_handler.rb +53 -0
- data/solara/lib/core/dashboard/handler/redirect_handler.rb +12 -0
- data/solara/lib/core/dashboard/handler/switch_handler.rb +25 -0
- data/solara/lib/core/dashboard/index.html +36 -0
- data/solara/lib/core/dashboard/local.html +41 -0
- data/solara/lib/core/dashboard/res/favicon/android-chrome-192x192.png +0 -0
- data/solara/lib/core/dashboard/res/favicon/android-chrome-512x512.png +0 -0
- data/solara/lib/core/dashboard/res/favicon/apple-touch-icon.png +0 -0
- data/solara/lib/core/dashboard/res/favicon/favicon-16x16.png +0 -0
- data/solara/lib/core/dashboard/res/favicon/favicon-32x32.png +0 -0
- data/solara/lib/core/dashboard/res/favicon/favicon.ico +0 -0
- data/solara/lib/core/dashboard/res/favicon/site.webmanifest +1 -0
- data/solara/lib/core/dashboard/solara.png +0 -0
- data/solara/lib/core/doctor/brand_doctor.rb +94 -0
- data/solara/lib/core/doctor/doctor_manager.rb +35 -0
- data/solara/lib/core/doctor/project_doctor.rb +8 -0
- data/solara/lib/core/doctor/schema/brand_configurations.json +60 -0
- data/solara/lib/core/doctor/schema/platform/android/android_config.json +23 -0
- data/solara/lib/core/doctor/schema/platform/android/android_signing.json +23 -0
- data/solara/lib/core/doctor/schema/platform/ios/ios_config.json +27 -0
- data/solara/lib/core/doctor/schema/platform/ios/ios_signing.json +27 -0
- data/solara/lib/core/doctor/schema/platform/shared/theme.json +48 -0
- data/solara/lib/core/doctor/validator/brand_settings_validator.rb +55 -0
- data/solara/lib/core/doctor/validator/brand_settings_validator_manager.rb +82 -0
- data/solara/lib/core/doctor/validator/directory_structure_validator.rb +38 -0
- data/solara/lib/core/doctor/validator/file_structure_validator.rb +37 -0
- data/solara/lib/core/doctor/validator/json_file_validator.rb +21 -0
- data/solara/lib/core/doctor/validator/json_schema_validator.rb +32 -0
- data/solara/lib/core/doctor/validator/project_filesystem_validator.rb +70 -0
- data/solara/lib/core/doctor/validator/template/android_template_validation_config.yml +51 -0
- data/solara/lib/core/doctor/validator/template/flutter_template_validation_config.yml +53 -0
- data/solara/lib/core/doctor/validator/template/ios_template_validation_config.yml +51 -0
- data/solara/lib/core/doctor/validator/template/template_validator.rb +108 -0
- data/solara/lib/core/doctor/validator/validation_strategy.rb +7 -0
- data/solara/lib/core/scripts/brand_config_generator.rb +245 -0
- data/solara/lib/core/scripts/brand_config_manager.rb +90 -0
- data/solara/lib/core/scripts/brand_exporter.rb +38 -0
- data/solara/lib/core/scripts/brand_importer.rb +84 -0
- data/solara/lib/core/scripts/brand_offboarder.rb +19 -0
- data/solara/lib/core/scripts/brand_resources_manager.rb +77 -0
- data/solara/lib/core/scripts/directory_creator.rb +22 -0
- data/solara/lib/core/scripts/file_manager.rb +90 -0
- data/solara/lib/core/scripts/file_path.rb +327 -0
- data/solara/lib/core/scripts/folder_copier.rb +41 -0
- data/solara/lib/core/scripts/gitignore_manager.rb +54 -0
- data/solara/lib/core/scripts/interactive_file_system_validator.rb +110 -0
- data/solara/lib/core/scripts/platform/android/android_manifest_switcher.rb +24 -0
- data/solara/lib/core/scripts/platform/android/android_strings_switcher.rb +39 -0
- data/solara/lib/core/scripts/platform/android/gradle_switcher.rb +233 -0
- data/solara/lib/core/scripts/platform/android/properties_generator.rb +31 -0
- data/solara/lib/core/scripts/platform/ios/ios_file_path_manager.rb +109 -0
- data/solara/lib/core/scripts/platform/ios/ios_plist_manager.rb +42 -0
- data/solara/lib/core/scripts/platform/ios/xcconfig_generator.rb +44 -0
- data/solara/lib/core/scripts/platform/ios/xcode_asset_manager.rb +56 -0
- data/solara/lib/core/scripts/platform/ios/xcode_project_manager.rb +82 -0
- data/solara/lib/core/scripts/platform/ios/xcode_project_switcher.rb +130 -0
- data/solara/lib/core/scripts/project_settings_manager.rb +39 -0
- data/solara/lib/core/scripts/solara_logger.rb +103 -0
- data/solara/lib/core/scripts/solara_settings_manager.rb +73 -0
- data/solara/lib/core/scripts/solara_status_manager.rb +55 -0
- data/solara/lib/core/scripts/solara_version_manager.rb +42 -0
- data/solara/lib/core/scripts/strings_xml_manager.rb +22 -0
- data/solara/lib/core/scripts/terminal_input_manager.rb +22 -0
- data/solara/lib/core/scripts/theme_generator.rb +250 -0
- data/solara/lib/core/scripts/yaml_manager.rb +72 -0
- data/solara/lib/core/solara_configurator.rb +15 -0
- data/solara/lib/core/template/brands/android/android_config.json +8 -0
- data/solara/lib/core/template/brands/android/android_signing.json +6 -0
- data/solara/lib/core/template/brands/android/res/.DS_Store +0 -0
- data/solara/lib/core/template/brands/android/res/mipmap-hdpi/ic_launcher.png +0 -0
- data/solara/lib/core/template/brands/android/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- data/solara/lib/core/template/brands/android/res/mipmap-mdpi/ic_launcher.png +0 -0
- data/solara/lib/core/template/brands/android/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- data/solara/lib/core/template/brands/android/res/mipmap-xhdpi/ic_launcher.png +0 -0
- data/solara/lib/core/template/brands/android/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- data/solara/lib/core/template/brands/android/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- data/solara/lib/core/template/brands/android/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- data/solara/lib/core/template/brands/android/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- data/solara/lib/core/template/brands/android/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- data/solara/lib/core/template/brands/brands.json +4 -0
- data/solara/lib/core/template/brands/ios/assets/.DS_Store +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/100.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/102.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/1024.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/114.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/120.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/128.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/144.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/152.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/16.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/167.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/172.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/180.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/196.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/20.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/216.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/256.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/29.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/32.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/40.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/48.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/50.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/512.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/55.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/57.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/58.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/60.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/64.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/66.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/72.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/76.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/80.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/87.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/88.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/92.png +0 -0
- data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/Contents.json +1 -0
- data/solara/lib/core/template/brands/ios/ios_config.json +7 -0
- data/solara/lib/core/template/brands/ios/ios_signing.json +7 -0
- data/solara/lib/core/template/brands/shared/.DS_Store +0 -0
- data/solara/lib/core/template/brands/shared/brand_config.json +2 -0
- data/solara/lib/core/template/brands/shared/theme.json +46 -0
- data/solara/lib/core/template/config/android_template_config.json +57 -0
- data/solara/lib/core/template/config/flutter_template_config.json +62 -0
- data/solara/lib/core/template/config/ios_template_config.json +57 -0
- data/solara/lib/core/template/project_template_generator.rb +63 -0
- data/solara/lib/platform_detector.rb +84 -0
- data/solara/lib/solara/cli.rb +5 -0
- data/solara/lib/solara/version.rb +3 -0
- data/solara/lib/solara.rb +238 -0
- data/solara/lib/solara_initializer.rb +44 -0
- data/solara/lib/solara_manager.rb +73 -0
- 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,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
|