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.
- 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,82 @@
|
|
|
1
|
+
class XcodeProjectManager
|
|
2
|
+
def initialize
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
def add_files_to_group(project, group, directory_path)
|
|
6
|
+
Dir.foreach(directory_path) do |entry|
|
|
7
|
+
next if entry == '.' || entry == '..' || entry == '.DS_Store'
|
|
8
|
+
|
|
9
|
+
file_path = File.join(directory_path, entry)
|
|
10
|
+
if File.directory?(file_path)
|
|
11
|
+
Solara.logger.debug("Processing directory: #{file_path}")
|
|
12
|
+
subgroup = group.groups.find { |g| g.name == entry } || group.new_group(entry)
|
|
13
|
+
add_files_to_group(project, subgroup, file_path)
|
|
14
|
+
else
|
|
15
|
+
add_single_file_to_group(project, group, file_path)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def add_single_file_to_group(project, group, file_path)
|
|
21
|
+
Solara.logger.debug("Adding new file: #{file_path}")
|
|
22
|
+
file_name = File.basename(file_path, File.extname(file_path))
|
|
23
|
+
|
|
24
|
+
existing_file = group.files.find { |f| File.basename(f.path, File.extname(f.path))&.downcase == file_name.downcase }
|
|
25
|
+
existing_group = group.groups.find { |g| g.name&.downcase == file_name.downcase }
|
|
26
|
+
existing_reference = existing_file || existing_group
|
|
27
|
+
|
|
28
|
+
if existing_reference
|
|
29
|
+
if existing_reference.is_a?(Xcodeproj::Project::Object::PBXFileReference)
|
|
30
|
+
Solara.logger.debug("Ignoring file: #{file_path} (Existing file reference: #{existing_reference.path})")
|
|
31
|
+
else
|
|
32
|
+
Solara.logger.debug("Ignoring file: #{file_path} (Existing group with same name: #{existing_reference.name})")
|
|
33
|
+
end
|
|
34
|
+
return nil
|
|
35
|
+
else
|
|
36
|
+
file_reference = group.new_file(file_path)
|
|
37
|
+
Solara.logger.debug("Adding new file: #{file_path} (File reference: #{file_reference.uuid})")
|
|
38
|
+
|
|
39
|
+
# Add the file to the project's main target if it's a source file
|
|
40
|
+
if %w[.swift .m .mm .c .cpp].include?(File.extname(file_path).downcase)
|
|
41
|
+
target = project.targets.first
|
|
42
|
+
target.add_file_references([file_reference])
|
|
43
|
+
Solara.logger.debug(" Added to target: #{target.name}")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
file_reference
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
def self.set_base_xcconfigs(project, xcconfigs)
|
|
50
|
+
# Iterate through all build configurations in the target
|
|
51
|
+
project.targets.first.build_configurations.each do |config|
|
|
52
|
+
# Find the matching XCConfig from the array
|
|
53
|
+
matching_xcconfig = xcconfigs.find { |xc| config.name.downcase.include?(xc[:type].downcase) }
|
|
54
|
+
|
|
55
|
+
# If no matching XCConfig found, use debug as base
|
|
56
|
+
matching_xcconfig ||= xcconfigs.find { |xc| xc[:type].downcase == 'debug' }
|
|
57
|
+
|
|
58
|
+
if matching_xcconfig
|
|
59
|
+
set_xcconfig(project, config, matching_xcconfig)
|
|
60
|
+
else
|
|
61
|
+
Solara.logger.warn("Warning: No matching XCConfig found for #{config.name} configuration, and no Debug XCConfig available")
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
Solara.logger.debug("Base XCConfig files have been set for all build configurations.")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def self.set_xcconfig(project, config, xcconfig)
|
|
69
|
+
# Find the existing file reference or create a new one if it doesn't exist
|
|
70
|
+
file_reference = project.reference_for_path(xcconfig[:path])
|
|
71
|
+
if file_reference
|
|
72
|
+
config.base_configuration_reference = file_reference
|
|
73
|
+
Solara.logger.debug("Set #{xcconfig[:type]} XCConfig for #{config.name} configuration")
|
|
74
|
+
else
|
|
75
|
+
# If the file reference doesn't exist, create a new one
|
|
76
|
+
new_file_reference = project.new_file(xcconfig[:path])
|
|
77
|
+
config.base_configuration_reference = new_file_reference
|
|
78
|
+
Solara.logger.debug("Created and set new #{xcconfig[:type]} XCConfig for #{config.name} configuration")
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
$LOAD_PATH.unshift(File.expand_path(__dir__))
|
|
2
|
+
|
|
3
|
+
class XcodeProjectSwitcher
|
|
4
|
+
def initialize(project_path, brand_key)
|
|
5
|
+
@project_path = project_path
|
|
6
|
+
@brand_key = brand_key
|
|
7
|
+
@project = Xcodeproj::Project.open(@project_path)
|
|
8
|
+
@target = @project.targets.first
|
|
9
|
+
@platform = SolaraSettingsManager.instance.platform
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def switch
|
|
13
|
+
update_xcode_target_settings
|
|
14
|
+
update_info_plist
|
|
15
|
+
add_artifacts_group
|
|
16
|
+
update_debug_and_release_xcconfig
|
|
17
|
+
@project.save
|
|
18
|
+
Solara.logger.debug("Updated #{@project_path} to use the values of #{IOSFilePathManager.instance.brand_xcconfig}")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def update_xcode_target_settings
|
|
24
|
+
@target.build_configurations.each do |config|
|
|
25
|
+
config.build_settings['PRODUCT_BUNDLE_IDENTIFIER'] = '$(PRODUCT_BUNDLE_IDENTIFIER)'
|
|
26
|
+
config.build_settings['PRODUCT_NAME'] = '$(PRODUCT_NAME)'
|
|
27
|
+
config.build_settings['MARKETING_VERSION'] = '$(MARKETING_VERSION)'
|
|
28
|
+
|
|
29
|
+
# We need to apply code signing only if the user has provided its config
|
|
30
|
+
path = FilePath.brand_signing(@brand_key, Platform::IOS)
|
|
31
|
+
signing = JSON.parse(File.read(path))
|
|
32
|
+
unless signing['PROVISIONING_PROFILE_SPECIFIER'].empty?
|
|
33
|
+
config.build_settings['CODE_SIGN_IDENTITY'] = '$(CODE_SIGN_IDENTITY)'
|
|
34
|
+
config.build_settings['DEVELOPMENT_TEAM'] = '$(DEVELOPMENT_TEAM)'
|
|
35
|
+
config.build_settings['PROVISIONING_PROFILE_SPECIFIER'] = '$(PROVISIONING_PROFILE_SPECIFIER)'
|
|
36
|
+
config.build_settings['CODE_SIGN_STYLE'] = '$(CODE_SIGN_STYLE)'
|
|
37
|
+
config.build_settings['CODE_SIGN_ENTITLEMENTS'] = '$(CODE_SIGN_ENTITLEMENTS)'
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# We need to apply APL_MRCH_ID only if the user has provided its config
|
|
41
|
+
path = FilePath.ios_config(@brand_key)
|
|
42
|
+
ios_config = JSON.parse(File.read(path))
|
|
43
|
+
merchant_id = ios_config['APL_MRCH_ID']
|
|
44
|
+
unless merchant_id.nil? || merchant_id.empty?
|
|
45
|
+
config.build_settings['APL_MRCH_ID'] = '$(APL_MRCH_ID)'
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def update_info_plist
|
|
51
|
+
info_plist_path = IOSFilePathManager.instance.info_plist
|
|
52
|
+
manager = IOSPlistManager.new(@project, info_plist_path)
|
|
53
|
+
manager.create_and_add_info_plist
|
|
54
|
+
|
|
55
|
+
xcconfig_values = {
|
|
56
|
+
'PRODUCT_NAME' => "$(PRODUCT_NAME)",
|
|
57
|
+
'PRODUCT_BUNDLE_IDENTIFIER' => "$(PRODUCT_BUNDLE_IDENTIFIER)",
|
|
58
|
+
'MARKETING_VERSION' => "$(MARKETING_VERSION)",
|
|
59
|
+
'BUNDLE_VERSION' => "$(BUNDLE_VERSION)"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
info_plist = Xcodeproj::Plist.read_from_path(info_plist_path)
|
|
63
|
+
info_plist['CFBundleDisplayName'] = xcconfig_values['PRODUCT_NAME']
|
|
64
|
+
info_plist['CFBundleIdentifier'] = xcconfig_values['PRODUCT_BUNDLE_IDENTIFIER']
|
|
65
|
+
info_plist['CFBundleShortVersionString'] = xcconfig_values['MARKETING_VERSION']
|
|
66
|
+
info_plist['CFBundleVersion'] = xcconfig_values['BUNDLE_VERSION']
|
|
67
|
+
|
|
68
|
+
Xcodeproj::Plist.write_to_path(info_plist, info_plist_path)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def update_debug_and_release_xcconfig
|
|
72
|
+
debug_xcconfig_path = IOSFilePathManager.instance.app_xcconfig('Debug.xcconfig')
|
|
73
|
+
release_xcconfig_path = IOSFilePathManager.instance.app_xcconfig('Release.xcconfig')
|
|
74
|
+
|
|
75
|
+
create_xcconfigs_and_add_to_project(debug_xcconfig_path, release_xcconfig_path)
|
|
76
|
+
set_base_xcconfigs(debug_xcconfig_path, release_xcconfig_path)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def create_xcconfigs_and_add_to_project(debug_xcconfig_path, release_xcconfig_path)
|
|
80
|
+
FileManager.create_files_if_not_exist([debug_xcconfig_path, release_xcconfig_path])
|
|
81
|
+
base_xcconfig_name = 'Artifacts/Brand.xcconfig'
|
|
82
|
+
|
|
83
|
+
case @platform
|
|
84
|
+
when Platform::Flutter
|
|
85
|
+
# Nothing
|
|
86
|
+
when Platform::IOS
|
|
87
|
+
group_name = 'XCConfig'
|
|
88
|
+
config_group = @project.groups.find { |group| group.name == group_name }
|
|
89
|
+
config_group = @project.new_group(group_name) if config_group.nil?
|
|
90
|
+
add_files_to_group(config_group, IOSFilePathManager.instance.app_xcconfig_directory)
|
|
91
|
+
else
|
|
92
|
+
raise ArgumentError, "Invalid platform: #{@platform}"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
File.open(debug_xcconfig_path, "w") { |file| file.write("#include \"#{base_xcconfig_name}\"\n") }
|
|
96
|
+
File.open(release_xcconfig_path, "w") { |file| file.write("#include \"#{base_xcconfig_name}\"\n") }
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def set_base_xcconfigs(debug_xcconfig_path, release_xcconfig_path)
|
|
100
|
+
xcconfigs = [
|
|
101
|
+
{ type: 'Debug', path: debug_xcconfig_path },
|
|
102
|
+
{ type: 'Release', path: release_xcconfig_path },
|
|
103
|
+
]
|
|
104
|
+
XcodeProjectManager.set_base_xcconfigs(
|
|
105
|
+
@project,
|
|
106
|
+
xcconfigs
|
|
107
|
+
)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def add_artifacts_group
|
|
111
|
+
case @platform
|
|
112
|
+
when Platform::Flutter
|
|
113
|
+
flutter_group = @project.groups.find { |group| group.name == 'Flutter' }
|
|
114
|
+
artifacts_group = flutter_group.groups.find { |group| group.name == 'Artifacts' }
|
|
115
|
+
artifacts_group = flutter_group.new_group("Artifacts") if artifacts_group.nil?
|
|
116
|
+
add_files_to_group(artifacts_group, IOSFilePathManager.instance.artifacts)
|
|
117
|
+
when Platform::IOS
|
|
118
|
+
artifacts_group = @project.groups.find { |group| group.name == 'Artifacts' }
|
|
119
|
+
artifacts_group = @project.new_group("Artifacts") if artifacts_group.nil?
|
|
120
|
+
add_files_to_group(artifacts_group, IOSFilePathManager.instance.artifacts)
|
|
121
|
+
else
|
|
122
|
+
raise ArgumentError, "Invalid platform: #{@platform}"
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def add_files_to_group(group, directory_path)
|
|
127
|
+
XcodeProjectManager.new.add_files_to_group(@project, group, directory_path)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require 'singleton'
|
|
2
|
+
|
|
3
|
+
require 'singleton'
|
|
4
|
+
require 'json'
|
|
5
|
+
|
|
6
|
+
require 'singleton'
|
|
7
|
+
require 'json'
|
|
8
|
+
|
|
9
|
+
class ProjectSettingsManager
|
|
10
|
+
include Singleton
|
|
11
|
+
|
|
12
|
+
attr_accessor :root, :project_root
|
|
13
|
+
|
|
14
|
+
def value(key, platform)
|
|
15
|
+
settings.dig(platform.to_s, key)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def add(key, value, platform)
|
|
19
|
+
json = settings
|
|
20
|
+
json[platform.to_s] ||= {}
|
|
21
|
+
json[platform.to_s][key] = value
|
|
22
|
+
save(json)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def settings
|
|
28
|
+
path = FilePath.project_settings
|
|
29
|
+
FileManager.create_file_if_not_exist(path)
|
|
30
|
+
JSON.parse(File.read(path))
|
|
31
|
+
rescue JSON::ParserError => e
|
|
32
|
+
{}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def save(json)
|
|
36
|
+
path = FilePath.project_settings
|
|
37
|
+
File.write(path, JSON.pretty_generate(json))
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
$LOAD_PATH.unshift(File.expand_path(__dir__))
|
|
2
|
+
|
|
3
|
+
require 'singleton'
|
|
4
|
+
|
|
5
|
+
require 'logger'
|
|
6
|
+
require 'colorize'
|
|
7
|
+
|
|
8
|
+
class SolaraLogger
|
|
9
|
+
LEVELS = %i[debug info warn error fatal]
|
|
10
|
+
EMOJIS = {
|
|
11
|
+
debug: "",
|
|
12
|
+
info: "",
|
|
13
|
+
warn: "⚠️",
|
|
14
|
+
error: "❌ ",
|
|
15
|
+
fatal: "💀"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
def initialize
|
|
19
|
+
@logger = Logger.new(STDOUT)
|
|
20
|
+
# Set the default to INFO until we override it later.
|
|
21
|
+
@logger.level = Logger::INFO
|
|
22
|
+
@verbose = false
|
|
23
|
+
@step_count = 0
|
|
24
|
+
set_default_format
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def verbose=(value)
|
|
28
|
+
@verbose = value
|
|
29
|
+
@logger.level = @verbose ? Logger::DEBUG : Logger::INFO
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
LEVELS.each do |level|
|
|
33
|
+
define_method(level) do |message|
|
|
34
|
+
emoji = EMOJIS[level]
|
|
35
|
+
@logger.send(level, colorize("#{emoji.empty? ? '' : emoji + "\s"}#{message}", level))
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def start_step(message)
|
|
40
|
+
return unless @verbose
|
|
41
|
+
@step_count += 1
|
|
42
|
+
@logger.debug("Step #{@step_count}: #{message}".green)
|
|
43
|
+
line
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def title(message)
|
|
47
|
+
@logger.info(message.green)
|
|
48
|
+
line
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def end_step(message = "Step completed")
|
|
52
|
+
return unless @verbose
|
|
53
|
+
@logger.debug("FINISHED Step #{@step_count}: #{message}".green)
|
|
54
|
+
line
|
|
55
|
+
info("") # Empty line for better readability
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def line(char: '-', length: 50)
|
|
59
|
+
message = char * length
|
|
60
|
+
@logger.info(message.green)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def header(message)
|
|
64
|
+
return unless @verbose
|
|
65
|
+
|
|
66
|
+
line(char: '=')
|
|
67
|
+
@logger.info(message.upcase.green)
|
|
68
|
+
line(char: '=')
|
|
69
|
+
info("") # Empty line for better readability
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def success(message)
|
|
73
|
+
@logger.info("🎉 #{message.green}")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def failure(message)
|
|
77
|
+
error(message)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def passed(message)
|
|
81
|
+
@logger.debug("✅ #{message.green}")
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
def set_default_format
|
|
88
|
+
@logger.formatter = proc do |severity, datetime, progname, msg|
|
|
89
|
+
"#{msg}\n"
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def colorize(message, level)
|
|
94
|
+
case level
|
|
95
|
+
when :debug then message
|
|
96
|
+
when :info then message
|
|
97
|
+
when :warn then message.yellow
|
|
98
|
+
when :error then message.red
|
|
99
|
+
when :fatal then message.red.bold
|
|
100
|
+
else message
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
require 'singleton'
|
|
2
|
+
|
|
3
|
+
require 'singleton'
|
|
4
|
+
require 'json'
|
|
5
|
+
|
|
6
|
+
class SolaraSettingsManager
|
|
7
|
+
include Singleton
|
|
8
|
+
|
|
9
|
+
attr_accessor :root, :project_root
|
|
10
|
+
|
|
11
|
+
def platform
|
|
12
|
+
settings['platform']
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def platform=(new_platform)
|
|
16
|
+
json = settings
|
|
17
|
+
json['platform'] = new_platform
|
|
18
|
+
save(json)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def value(key)
|
|
22
|
+
settings[key]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def add(key, value)
|
|
26
|
+
json = settings
|
|
27
|
+
json[key] = value
|
|
28
|
+
save(json)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def settings
|
|
34
|
+
path = FilePath.solara_settings
|
|
35
|
+
FileManager.create_file_if_not_exist(path)
|
|
36
|
+
JSON.parse(File.read(path))
|
|
37
|
+
rescue JSON::ParserError => e
|
|
38
|
+
{}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def save(json)
|
|
42
|
+
File.write(FilePath.solara_settings, JSON.pretty_generate(json))
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
module Platform
|
|
47
|
+
Flutter = 'flutter'
|
|
48
|
+
Android = 'android'
|
|
49
|
+
IOS = 'ios'
|
|
50
|
+
Unknown = 'unknown'
|
|
51
|
+
|
|
52
|
+
def self.all
|
|
53
|
+
[Flutter, Android, IOS]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.is_ios
|
|
57
|
+
SolaraSettingsManager.instance.platform.downcase == IOS.downcase
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.is_android
|
|
61
|
+
SolaraSettingsManager.instance.platform.downcase == Android.downcase
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.is_flutter
|
|
65
|
+
SolaraSettingsManager.instance.platform.downcase == Flutter.downcase
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def init(platform)
|
|
70
|
+
unless Platform.all.include?(platform)
|
|
71
|
+
raise ArgumentError, "Invalid platform. Please use one of: #{Platform.all.join(', ')}"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
class SolaraStatusManager
|
|
2
|
+
|
|
3
|
+
def start
|
|
4
|
+
current_brand_draft
|
|
5
|
+
puts
|
|
6
|
+
brands_list
|
|
7
|
+
doctor
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def current_brand_draft
|
|
11
|
+
current_brand = BrandsManager.instance.current_brand
|
|
12
|
+
unless current_brand
|
|
13
|
+
return
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
Solara.logger.title("Current Brand Status")
|
|
17
|
+
|
|
18
|
+
content_changed = current_brand['content_changed']
|
|
19
|
+
|
|
20
|
+
unless content_changed
|
|
21
|
+
Solara.logger.info("Current brand configurations are up-to-date")
|
|
22
|
+
return
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
Solara.logger.warn("Changes for the current brand have been drafted!")
|
|
26
|
+
Solara.logger.info("To apply these changes, please use one of the following methods:")
|
|
27
|
+
message = <<-MESSAGE
|
|
28
|
+
1. Run this command in your terminal:
|
|
29
|
+
|
|
30
|
+
solara switch -k #{current_brand['key']}
|
|
31
|
+
|
|
32
|
+
2. Alternatively, open the dashboard by executing this command in your terminal:
|
|
33
|
+
|
|
34
|
+
solara dashboard -k #{current_brand['key']}
|
|
35
|
+
|
|
36
|
+
Then, click the "Apply Changes" button.
|
|
37
|
+
MESSAGE
|
|
38
|
+
Solara.logger.info(message)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def brands_list
|
|
42
|
+
Solara.logger.title("Brands List")
|
|
43
|
+
|
|
44
|
+
brands_list = BrandsManager.instance.brands_list.each_with_index.map { |brand, index|
|
|
45
|
+
"#{index + 1}. Key: #{brand['key']}\n Name: #{brand['name']}\n"
|
|
46
|
+
}
|
|
47
|
+
brands_list.each do |brand|
|
|
48
|
+
Solara.logger.info(brand)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def doctor
|
|
53
|
+
SolaraManager.new.doctor
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require 'singleton'
|
|
2
|
+
|
|
3
|
+
require 'singleton'
|
|
4
|
+
require 'json'
|
|
5
|
+
|
|
6
|
+
class SolaraVersionManager
|
|
7
|
+
include Singleton
|
|
8
|
+
|
|
9
|
+
def sversion
|
|
10
|
+
json['solaraVersion']
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def version=(value)
|
|
14
|
+
new_json = json
|
|
15
|
+
new_json['solaraVersion'] = value
|
|
16
|
+
save(new_json)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def value(key)
|
|
20
|
+
json[key]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def add(key, value)
|
|
24
|
+
new_json = json
|
|
25
|
+
new_json[key] = value
|
|
26
|
+
save(new_json)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def json
|
|
32
|
+
path = FilePath.solara_version
|
|
33
|
+
FileManager.create_file_if_not_exist(path)
|
|
34
|
+
JSON.parse(File.read(path))
|
|
35
|
+
rescue JSON::ParserError => e
|
|
36
|
+
{}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def save(json)
|
|
40
|
+
File.write(FilePath.solara_version, JSON.pretty_generate(json))
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'rexml/document'
|
|
2
|
+
|
|
3
|
+
class StringsXmlManager
|
|
4
|
+
def initialize(file_path)
|
|
5
|
+
@file_path = file_path
|
|
6
|
+
@xml_content = File.read(@file_path)
|
|
7
|
+
@doc = REXML::Document.new(@xml_content)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def delete_app_name
|
|
11
|
+
app_name_element = @doc.elements['resources/string[@name="app_name"]']
|
|
12
|
+
app_name_element.remove if app_name_element
|
|
13
|
+
save_changes
|
|
14
|
+
Solara.logger.debug("Removed app_name from #{@file_path} to avoid duplication with artifacts/strings.xml.")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def save_changes
|
|
20
|
+
File.write(@file_path, @doc.to_s)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
class TerminalInputManager
|
|
2
|
+
|
|
3
|
+
def get_validated(message)
|
|
4
|
+
loop do
|
|
5
|
+
print message.green
|
|
6
|
+
user_input = STDIN.gets.chomp
|
|
7
|
+
|
|
8
|
+
if yield(user_input)
|
|
9
|
+
return user_input
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def get(message)
|
|
15
|
+
loop do
|
|
16
|
+
print message.green
|
|
17
|
+
user_input = STDIN.gets.chomp
|
|
18
|
+
return user_input unless user_input.nil? || user_input.empty?
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|