diff --git a/lib/commands/autofixes/minify_strings.rb b/lib/commands/autofixes/minify_strings.rb new file mode 100644 index 0000000..287363c --- /dev/null +++ b/lib/commands/autofixes/minify_strings.rb @@ -0,0 +1,99 @@ +require 'dry/cli' +require 'xcodeproj' + +module EmergeCLI + module Commands + module Autofixes + class MinifyStrings < EmergeCLI::Commands::GlobalOptions + desc 'Minify strings in the app' + + option :path, type: :string, required: true, desc: 'Path to the xcarchive' + + # Constants + SCRIPT_NAME = 'EmergeTools Minify Strings'.freeze + ENABLE_USER_SCRIPT_SANDBOXING = 'ENABLE_USER_SCRIPT_SANDBOXING'.freeze + STRINGS_FILE_OUTPUT_ENCODING = 'STRINGS_FILE_OUTPUT_ENCODING'.freeze + STRINGS_FILE_OUTPUT_ENCODING_VALUE = 'UTF-8'.freeze + SCRIPT_CONTENT = %{import os +import json +from multiprocessing.pool import ThreadPool + +def minify(file_path): + os.system(f"plutil -convert json '{file_path}'") + new_content = '' + try: + with open(file_path, 'r') as input_file: + data = json.load(input_file) + + for key, value in data.items(): + fixed_key = json.dumps(key, ensure_ascii=False).encode('utf8').decode() + fixed_value = json.dumps(value, ensure_ascii=False).encode('utf8').decode() + new_line = f'{fixed_key} = {fixed_value};\\n' + new_content += new_line + + with open(file_path, 'w') as output_file: + output_file.write(new_content) + except: + return + +file_extension = '.strings' +stringFiles = [] + +for root, _, files in os.walk(os.environ['BUILT_PRODUCTS_DIR'], followlinks=True): + for filename in files: + if filename.endswith(file_extension): + input_path = os.path.join(root, filename) + stringFiles.append(input_path) + +# create a thread pool +with ThreadPool() as pool: + pool.map(minify, stringFiles) +}.freeze + + def call(**options) + @options = options + before(options) + + raise 'Path must be an xcodeproj' unless @options[:path].end_with?('.xcodeproj') + raise 'Path does not exist' unless File.exist?(@options[:path]) + + Sync do + project = Xcodeproj::Project.open(@options[:path]) + + project.targets.each do |target| + target.build_configurations.each do |config| + enable_user_script_sandboxing(config) + set_output_encoding(config) + end + + add_run_script(target) + end + + project.save + end + end + + private + + def enable_user_script_sandboxing(config) + Logger.info "Enabling user script sandboxing for #{config.name}" + config.build_settings[ENABLE_USER_SCRIPT_SANDBOXING] = 'NO' + end + + def set_output_encoding(config) + Logger.info "Setting output encoding for #{config.name}" + config.build_settings[STRINGS_FILE_OUTPUT_ENCODING] = STRINGS_FILE_OUTPUT_ENCODING_VALUE + end + + def add_run_script(target) + phase = target.shell_script_build_phases.find { |item| item.name == SCRIPT_NAME } + return unless phase.nil? + Logger.info "Creating script '#{SCRIPT_NAME}'" + phase = target.new_shell_script_build_phase(SCRIPT_NAME) + phase.shell_script = SCRIPT_CONTENT + phase.shell_path = `which python3`.strip + end + end + end + end +end diff --git a/lib/emerge_cli.rb b/lib/emerge_cli.rb index 84f53f0..b0c4811 100644 --- a/lib/emerge_cli.rb +++ b/lib/emerge_cli.rb @@ -16,6 +16,7 @@ require_relative 'commands/order_files/validate_xcode_project' require_relative 'commands/upload/build' require_relative 'commands/build_distribution/validate_app' +require_relative 'commands/autofixes/minify_strings' require_relative 'reaper/ast_parser' require_relative 'reaper/code_deleter' @@ -66,6 +67,10 @@ module EmergeCLI register 'build-distribution' do |prefix| prefix.register 'validate-app', Commands::BuildDistribution::ValidateApp end + + register 'autofix' do |prefix| + prefix.register 'minify-strings', Commands::Autofixes::MinifyStrings + end end # By default the log level is INFO, but can be overridden by the --debug flag diff --git a/test/commands/autofixes/minify_strings_test.rb b/test/commands/autofixes/minify_strings_test.rb new file mode 100644 index 0000000..5c4cd58 --- /dev/null +++ b/test/commands/autofixes/minify_strings_test.rb @@ -0,0 +1,68 @@ +require 'test_helper' + +module EmergeCLI + module Commands + module Autofixes + class MinifyStringsTest < Minitest::Test + SCRIPT_NAME = 'EmergeTools Minify Strings'.freeze + ENABLE_USER_SCRIPT_SANDBOXING = 'ENABLE_USER_SCRIPT_SANDBOXING'.freeze + STRINGS_FILE_OUTPUT_ENCODING = 'STRINGS_FILE_OUTPUT_ENCODING'.freeze + STRINGS_FILE_OUTPUT_ENCODING_VALUE = 'UTF-8'.freeze + + def setup + @command = EmergeCLI::Commands::Autofixes::MinifyStrings.new + + FileUtils.mkdir_p('tmp/test_autofix_strings') + FileUtils.cp_r('test/test_files/ExampleApp.xcodeproj', 'tmp/test_autofix_strings/ExampleApp.xcodeproj') + end + + def teardown + FileUtils.rm_rf('tmp/test_autofix_strings') + end + + def test_script_is_created + options = { + path: 'tmp/test_autofix_strings/ExampleApp.xcodeproj' + } + + @command.call(**options) + + project = Xcodeproj::Project.open('tmp/test_autofix_strings/ExampleApp.xcodeproj') + + phase = project.targets[0].shell_script_build_phases.find do |item| + item.name == SCRIPT_NAME + end + assert_equal SCRIPT_NAME, phase.name + end + + def test_user_script_sandboxing_is_disabled + options = { + path: 'tmp/test_autofix_strings/ExampleApp.xcodeproj' + } + + @command.call(**options) + + project = Xcodeproj::Project.open('tmp/test_autofix_strings/ExampleApp.xcodeproj') + + project.targets[0].build_configurations.each do |config| + assert_equal 'NO', config.build_settings[ENABLE_USER_SCRIPT_SANDBOXING] + end + end + + def test_strings_encoding_is_utf8 + options = { + path: 'tmp/test_autofix_strings/ExampleApp.xcodeproj' + } + + @command.call(**options) + + project = Xcodeproj::Project.open('tmp/test_autofix_strings/ExampleApp.xcodeproj') + + project.targets[0].build_configurations.each do |config| + assert_equal STRINGS_FILE_OUTPUT_ENCODING_VALUE, config.build_settings[STRINGS_FILE_OUTPUT_ENCODING] + end + end + end + end + end +end