summaryrefslogtreecommitdiffhomepage
path: root/dragon/ios_wizard.rb
diff options
context:
space:
mode:
authorAmir Rajan <[email protected]>2020-11-13 01:29:16 -0600
committerAmir Rajan <[email protected]>2020-11-13 01:29:16 -0600
commit128fa1d90cea6289605a49daf56a0cbb72e2dd28 (patch)
tree5cfdb499d275e2b43075e4d6a076365fc58ff0f7 /dragon/ios_wizard.rb
parent05cbef7fb8224332795e5685be499d81d20e7d93 (diff)
downloaddragonruby-game-toolkit-contrib-128fa1d90cea6289605a49daf56a0cbb72e2dd28.tar.gz
dragonruby-game-toolkit-contrib-128fa1d90cea6289605a49daf56a0cbb72e2dd28.zip
synced from DRGTK 1.27
Diffstat (limited to 'dragon/ios_wizard.rb')
-rw-r--r--dragon/ios_wizard.rb617
1 files changed, 617 insertions, 0 deletions
diff --git a/dragon/ios_wizard.rb b/dragon/ios_wizard.rb
new file mode 100644
index 0000000..e38faf1
--- /dev/null
+++ b/dragon/ios_wizard.rb
@@ -0,0 +1,617 @@
+# Copyright 2019 DragonRuby LLC
+# MIT License
+# ios_wizard.rb has been released under MIT (*only this file*).
+
+class WizardException < Exception
+ attr_accessor :console_primitives
+
+ def initialize *console_primitives
+ @console_primitives = console_primitives
+ end
+end
+
+class IOSWizard
+ def initialize
+ @doctor_executed_at = 0
+ end
+
+ def relative_path
+ (File.dirname $gtk.binary_path)
+ end
+
+ def steps
+ [
+ :check_for_xcode,
+ :check_for_brew,
+ :check_for_certs,
+ :check_for_device,
+ :check_for_dev_profile,
+ :determine_app_name,
+ :determine_app_id,
+ :blow_away_temp,
+ :stage_app,
+ :write_info_plist,
+ :write_entitlements_plist,
+ :code_sign,
+ :create_ipa,
+ :deploy,
+ ]
+ end
+
+ def get_reserved_sprite png
+ sprite_path = ".dragonruby/sprites/wizards/ios/#{png}"
+
+ if !$gtk.ivar :rcb_release_mode
+ sprite_path = "deploy_template/#{sprite_path}"
+ $gtk.reset_sprite sprite_path
+ end
+
+ if !$gtk.read_file sprite_path
+ log_error "png #{png} not found."
+ end
+
+ sprite_path
+ end
+
+ def start
+ @certificate_name = nil
+ init_wizard_status
+ log_info "Starting iOS Wizard so we can deploy to your device."
+ @start_at = Kernel.global_tick_count
+ steps.each do |m|
+ begin
+ result = (send m) || :success if @wizard_status[m][:result] != :success
+ @wizard_status[m][:result] = result
+ rescue Exception => e
+ if e.is_a? WizardException
+ $console.log.clear
+ $console.archived_log.clear
+ log "=" * $console.console_text_width
+ e.console_primitives.each do |p|
+ $console.add_primitive p
+ end
+ log "=" * $console.console_text_width
+ else
+ log_error "Step #{m} failed."
+ log_error e.to_s
+ end
+
+ $console.set_command "$wizards.ios.start"
+
+ break
+ end
+ end
+
+ return nil
+ end
+
+ def always_fail
+ return false if $gtk.ivar :rcb_release_mode
+ return true
+ end
+
+ def check_for_xcode
+ if !cli_app_exist?(xcodebuild_cli_app)
+ raise WizardException.new(
+ "* You need Xcode to use $wizards.ios.start.",
+ { w: 75, h: 75, path: get_reserved_sprite("xcode.png") },
+ "** 1. Go to http://developer.apple.com and register.",
+ "** 2. Download Xcode 11.3+ from http://developer.apple.com/downloads.",
+ " NOTE: DO NOT install Xcode from the App Store. Use the link above.",
+ { w: 700, h: 359, path: get_reserved_sprite("xcode-downloads.png") },
+ "** 3. After installing. Open up Xcode to accept the EULA."
+ )
+ end
+ end
+
+ def check_for_brew
+ if !cli_app_exist?('brew')
+ raise WizardException.new(
+ "* You need to install Brew.",
+ { w: 700, h: 388, path: get_reserved_sprite("brew.png") },
+ "** 1. Go to http://brew.sh.",
+ "** 2. Copy the command that starts with `/bin/bash -c` on the site.",
+ "** 3. Open Terminal and run the command you copied from the website.",
+ { w: 700, h: 99, path: get_reserved_sprite("terminal.png") },
+ )
+ end
+ end
+
+ def init_wizard_status
+ @wizard_status = {}
+ steps.each do |m|
+ @wizard_status[m] = { result: :not_started }
+ end
+
+ previous_step = nil
+ next_step = nil
+ steps.each_cons(2) do |current_step, next_step|
+ @wizard_status[current_step][:next_step] = next_step
+ end
+
+ steps.reverse.each_cons(2) do |current_step, previous_step|
+ @wizard_status[current_step][:previous_step] = previous_step
+ end
+ end
+
+ def restart
+ init_wizard_status
+ start
+ end
+
+ def check_for_dev_profile
+ if !($gtk.read_file 'profiles/development.mobileprovision')
+ $gtk.system "mkdir -p #{relative_path}/profiles"
+ $gtk.system "open #{relative_path}/profiles"
+ $gtk.system "echo Download the mobile provisioning profile and place it here with the name development.mobileprovision > #{relative_path}/profiles/README.txt"
+ raise WizardException.new(
+ "* I didn't find a mobile provision.",
+ "** 1. Go to http://developer.apple.com and click \"Certificates, IDs & Profiles\".",
+ "** 2. Add an App Identifier.",
+ "** 3. Select the App IDs option from the list.",
+ { w: 700, h: 75, path: get_reserved_sprite("identifiers.png") },
+ "** 4. Add your Device next. You can use idevice_id -l to get the UUID of your device.",
+ { w: 365, h: 69, path: get_reserved_sprite("device-link.png") },
+ "** 5. Create a Profile. Associate your certs, id, and device.",
+ { w: 300, h: 122, path: get_reserved_sprite("profiles.png") },
+ "** 6. Download the mobile provision and save it to 'profiles/development.mobileprovision'.",
+ { w: 200, h: 124, path: get_reserved_sprite("profiles-folder.png") },
+ )
+ end
+ end
+
+ def determine_app_name
+ @app_name = dev_profile_xml[:children].first[:children].first[:children][1][:children].first[:data]
+ log_info "App name is: #{@app_name}."
+ end
+
+ def dev_profile_xml
+ xml = $gtk.read_file 'profiles/development.mobileprovision'
+ scrubbed = xml.each_line.map do |l|
+ if l.strip.start_with? "<"
+ if l.start_with? '</plist>'
+ '</plist>'
+ elsif l.include? "Apple Inc."
+ nil
+ elsif l.include? '<data>'
+ nil
+ else
+ l
+ end
+ else
+ nil
+ end
+ end.reject { |l| !l }.join
+ $gtk.parse_xml scrubbed
+ end
+
+ def determine_app_id
+ # lol
+ @app_id = dev_profile_xml[:children].first[:children].first[:children][13][:children][1][:children].first[:data]
+ log_info "App Identifier is set to : #{@app_id}"
+ end
+
+ def set_app_name name
+ @app_name = name
+ start
+ end
+
+ def set_dev_profile path
+ if !$gtk.read_file path
+ log_error "I couldn't find a development profile at #{path}."
+ ask_for_dev_profile
+ else
+ @dev_profile_path = path
+ start
+ end
+ end
+
+ def blow_away_temp
+ sh "rm -rf #{tmp_directory}"
+ end
+
+ def stage_app
+ log_info "Staging."
+ sh "mkdir -p #{tmp_directory}"
+ sh "cp -R #{relative_path}/dragonruby-ios.app \"#{tmp_directory}/#{@app_name}.app\""
+ end
+
+ def set_app_id id
+ log_info = "App Id set to: #{id}"
+ @app_id = id
+ start
+ end
+
+ def check_for_device
+ log_info "Looking for device."
+
+ if !cli_app_exist?(idevice_id_cli_app)
+ raise WizardException.new(
+ "* It doesn't look like you have the libimobiledevice iOS protocol library installed.",
+ "** 1. Open Terminal.",
+ { w: 700, h: 99, path: get_reserved_sprite("terminal.png") },
+ "** 2. Run: `brew install libimobiledevice`.",
+ { w: 500, h: 93, path: get_reserved_sprite("brew-install-libimobiledevice.png") },
+ )
+ end
+
+ if connected_devices.length == 0
+ raise WizardException.new("* I couldn't find any connected devices. Connect your iOS device to your Mac and try again.")
+ end
+
+ @device_id = connected_devices.first
+ log_info "I will be using device with UUID #{@device_id}"
+ end
+
+ def check_for_certs
+ log_info "Attempting to find certificates on your computer."
+
+ if !cli_app_exist?(security_cli_app)
+ raise WizardException.new(
+ "* It doesn't look like you have #{security_cli_app}.",
+ "** 1. Open Disk Utility and run First Aid.",
+ { w: 700, h: 148, path: get_reserved_sprite("disk-utility.png") },
+ )
+ end
+
+ if valid_certs.length == 0
+ raise WizardException.new(
+ "* It doesn't look like you have any valid certs installed.",
+ "** 1. Open Xcode.",
+ "** 2. Log into your developer account. Xcode -> Preferences -> Accounts.",
+ { w: 700, h: 98, path: get_reserved_sprite("login-xcode.png") },
+ "** 3. After loggin in, select Manage Certificates...",
+ { w: 700, h: 115, path: get_reserved_sprite("manage-certificates.png") },
+ "** 4. Add a certificate for Apple Development.",
+ { w: 700, h: 217, path: get_reserved_sprite("add-cert.png") },
+ )
+ raise "You do not have any Apple development certs on this computer."
+ end
+
+ @certificate_name = valid_certs.first[:name]
+ log_info "I will be using '#{@certificate_name}' to deploy to your device."
+ end
+
+ def idevice_id_cli_app
+ "idevice_id"
+ end
+
+ def security_cli_app
+ "/usr/bin/security"
+ end
+
+ def xcodebuild_cli_app
+ "xcodebuild"
+ end
+
+ def valid_certs
+ certs = sh("#{security_cli_app} -q find-identity -p codesigning -v").each_line.map do |l|
+ if l.include?(")") && !l.include?("Developer ID") && l.include?("Development")
+ l.strip
+ else
+ nil
+ end
+ end.reject_nil.map do |l|
+ number, id, name = l.split(' ', 3)
+ name = name.gsub("\"", "") if name
+ {
+ number: 1,
+ id: id,
+ name: name
+ }
+ end
+ end
+
+ def connected_devices
+ sh("idevice_id -l").strip.each_line.map do |l|
+ l.strip
+ end.reject { |l| l.length == 0 }
+ end
+
+ def cli_app_exist? app
+ `which #{app}`.strip.length != 0
+ end
+
+ def write_entitlements_plist
+ entitlement_plist_string = <<-XML
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>application-identifier</key>
+ <string>:app_id</string>
+ <key>get-task-allow</key>
+ <true/>
+ </dict>
+</plist>
+XML
+
+ log_info "Creating Entitlements.plist"
+
+ $gtk.write_file_root "tmp/ios/Entitlements.plist", entitlement_plist_string.gsub(":app_id", @app_id).strip
+
+ sh "/usr/bin/plutil -convert binary1 \"#{tmp_directory}/Entitlements.plist\""
+ sh "/usr/bin/plutil -convert xml1 \"#{tmp_directory}/Entitlements.plist\""
+
+ @entitlement_plist_written = true
+ end
+
+ def code_sign
+ sh "cp #{@dev_profile_path} \"#{app_path}/embedded.mobileprovision\""
+
+ log_info "Signing app with #{@certificate_name}."
+
+ sh "/usr/bin/plutil -convert binary1 \"#{app_path}/Info.plist\""
+
+ sh "CODESIGN_ALLOCATE=\"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/codesign_allocate\" /usr/bin/codesign -f -s \"#{@certificate_name}\" --entitlements #{tmp_directory}/Entitlements.plist \"#{tmp_directory}/#{@app_name}.app\""
+
+ @code_sign_completed = true
+ end
+
+ def write_info_plist
+ log_info "Adding Info.plist."
+
+ info_plist_string = <<-XML
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>NSAppTransportSecurity</key>
+ <dict>
+ <key>NSAllowsArbitraryLoads</key>
+ <true/>
+ <key>NSExceptionDomains</key>
+ <dict>
+ <key>google.com</key>
+ <dict>
+ <key>NSExceptionAllowsInsecureHTTPLoads</key>
+ <true/>
+ <key>NSIncludesSubdomains</key>
+ <true/>
+ </dict>
+ </dict>
+ </dict>
+ <key>BuildMachineOSBuild</key>
+ <string>19C57</string>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleDisplayName</key>
+ <string>:app_name</string>
+ <key>CFBundleExecutable</key>
+ <string>Runtime</string>
+ <key>CFBundleIconFiles</key>
+ <array>
+ <string>AppIcon60x60</string>
+ </array>
+ <key>CFBundleIcons</key>
+ <dict>
+ <key>CFBundlePrimaryIcon</key>
+ <dict>
+ <key>CFBundleIconFiles</key>
+ <array>
+ <string>AppIcon60x60</string>
+ </array>
+ <key>CFBundleIconName</key>
+ <string>AppIcon</string>
+ </dict>
+ </dict>
+ <key>CFBundleIcons~ipad</key>
+ <dict>
+ <key>CFBundlePrimaryIcon</key>
+ <dict>
+ <key>CFBundleIconFiles</key>
+ <array>
+ <string>AppIcon60x60</string>
+ <string>AppIcon76x76</string>
+ </array>
+ <key>CFBundleIconName</key>
+ <string>AppIcon</string>
+ </dict>
+ </dict>
+ <key>CFBundleIdentifier</key>
+ <string>com.carlile.swisscheese</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>:app_name</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleSupportedPlatforms</key>
+ <array>
+ <string>iPhoneOS</string>
+ </array>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>DTCompiler</key>
+ <string>com.apple.compilers.llvm.clang.1_0</string>
+ <key>DTPlatformBuild</key>
+ <string>17B102</string>
+ <key>DTPlatformName</key>
+ <string>iphoneos</string>
+ <key>DTPlatformVersion</key>
+ <string>13.2</string>
+ <key>DTSDKBuild</key>
+ <string>17B102</string>
+ <key>DTSDKName</key>
+ <string>iphoneos13.2</string>
+ <key>DTXcode</key>
+ <string>01131</string>
+ <key>DTXcodeBuild</key>
+ <string>11C505</string>
+ <key>ITSAppUsesNonExemptEncryption</key>
+ <false/>
+ <key>MinimumOSVersion</key>
+ <string>11.0</string>
+ <key>UIAppFonts</key>
+ <array/>
+ <key>UIBackgroundModes</key>
+ <array/>
+ <key>UIDeviceFamily</key>
+ <array>
+ <integer>1</integer>
+ <integer>2</integer>
+ </array>
+ <key>UILaunchImages</key>
+ <array>
+ <dict>
+ <key>UILaunchImageMinimumOSVersion</key>
+ <string>11.0</string>
+ <key>UILaunchImageName</key>
+ <string>LaunchImage-1100-Portrait-2436h</string>
+ <key>UILaunchImageOrientation</key>
+ <string>Portrait</string>
+ <key>UILaunchImageSize</key>
+ <string>{375, 812}</string>
+ </dict>
+ <dict>
+ <key>UILaunchImageMinimumOSVersion</key>
+ <string>8.0</string>
+ <key>UILaunchImageName</key>
+ <string>LaunchImage-800-Portrait-736h</string>
+ <key>UILaunchImageOrientation</key>
+ <string>Portrait</string>
+ <key>UILaunchImageSize</key>
+ <string>{414, 736}</string>
+ </dict>
+ <dict>
+ <key>UILaunchImageMinimumOSVersion</key>
+ <string>8.0</string>
+ <key>UILaunchImageName</key>
+ <string>LaunchImage-800-667h</string>
+ <key>UILaunchImageOrientation</key>
+ <string>Portrait</string>
+ <key>UILaunchImageSize</key>
+ <string>{375, 667}</string>
+ </dict>
+ <dict>
+ <key>UILaunchImageMinimumOSVersion</key>
+ <string>7.0</string>
+ <key>UILaunchImageName</key>
+ <string>LaunchImage-700</string>
+ <key>UILaunchImageOrientation</key>
+ <string>Portrait</string>
+ <key>UILaunchImageSize</key>
+ <string>{320, 480}</string>
+ </dict>
+ <dict>
+ <key>UILaunchImageMinimumOSVersion</key>
+ <string>7.0</string>
+ <key>UILaunchImageName</key>
+ <string>LaunchImage-700-568h</string>
+ <key>UILaunchImageOrientation</key>
+ <string>Portrait</string>
+ <key>UILaunchImageSize</key>
+ <string>{320, 568}</string>
+ </dict>
+ <dict>
+ <key>UILaunchImageMinimumOSVersion</key>
+ <string>7.0</string>
+ <key>UILaunchImageName</key>
+ <string>LaunchImage-700-Portrait</string>
+ <key>UILaunchImageOrientation</key>
+ <string>Portrait</string>
+ <key>UILaunchImageSize</key>
+ <string>{768, 1024}</string>
+ </dict>
+ </array>
+ <key>UIRequiredDeviceCapabilities</key>
+ <array>
+ <string>arm64</string>
+ </array>
+ <key>UIRequiresFullScreen</key>
+ <true/>
+ <key>UIStatusBarStyle</key>
+ <string>UIStatusBarStyleDefault</string>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>#{device_orientation_xml}</string>
+ </array>
+</dict>
+</plist>
+XML
+
+ # <string>UIInterfaceOrientationPortrait</string>
+ # <string>UIInterfaceOrientationLandscapeRight</string>
+
+ $gtk.write_file_root "tmp/ios/#{@app_name}.app/Info.plist", info_plist_string.gsub(":app_name", @app_name).strip
+
+ @info_plist_written = true
+ end
+
+ def device_orientation_xml
+ return "UIInterfaceOrientationLandscapeRight" if $gtk.logical_width > $gtk.logical_height
+ return "UIInterfaceOrientationPortrait"
+ end
+
+ def tmp_directory
+ "#{relative_path}/tmp/ios"
+ end
+
+ def app_path
+ "#{tmp_directory}/#{@app_name}.app"
+ end
+
+ def root_folder
+ "#{relative_path}/#{$gtk.cli_arguments[:dragonruby]}"
+ end
+
+ def write_ip_address
+ $gtk.write_file "app/server_ip_address.txt", $gtk.ffi_misc.get_local_ip_address.strip
+ end
+
+ def create_ipa
+ write_ip_address
+ sh "rm \"#{@app_name}\".ipa"
+ sh "rm -rf \"#{app_path}/app\""
+ sh "rm -rf \"#{app_path}/sounds\""
+ sh "rm -rf \"#{app_path}/sprites\""
+ sh "rm -rf \"#{app_path}/data\""
+ sh "rm -rf \"#{app_path}/fonts\""
+ sh "cp -r \"#{root_folder}/app/\" \"#{app_path}/app/\""
+ sh "cp -r \"#{root_folder}/sounds/\" \"#{app_path}/sounds/\""
+ sh "cp -r \"#{root_folder}/sprites/\" \"#{app_path}/sprites/\""
+ sh "cp -r \"#{root_folder}/data/\" \"#{app_path}/data/\""
+ sh "cp -r \"#{root_folder}/fonts/\" \"#{app_path}/fonts/\""
+ sh "mkdir -p #{tmp_directory}/ipa_root/Payload"
+ sh "cp -r \"#{app_path}\" \"#{tmp_directory}/ipa_root/Payload\""
+ sh "chmod -R 755 \"#{tmp_directory}/ipa_root/Payload\""
+ do_zip
+ sh "cp \"#{tmp_directory}/ipa_root/archive.zip\" \"#{tmp_directory}/#{@app_name}.ipa\""
+ sh "XCODE_DIR=\"/Applications/Xcode.app/Contents/Developer\" \"#{relative_path}/dragonruby-deploy-ios\" -d \"#{@device_id}\" \"#{tmp_directory}/#{@app_name}.ipa\""
+ cmd_result = `ps -e | grep civetweb`
+ is_civet_running = (`ps -e | grep civetweb`).strip.each_line.to_a.length > 2
+ if !is_civet_running
+ $gtk.system "cp \"#{relative_path}/civetweb\" \"#{tmp_directory}/../src_backup/civetweb\""
+ $gtk.system "open \"#{tmp_directory}/../src_backup/civetweb\" -g"
+ else
+ log "* INFO: civetweb is running already running. No need to start another instance."
+ end
+ log_info "Check your device!!"
+ end
+
+ def do_zip
+ $gtk.write_file_root "tmp/ios/do_zip.sh", <<-SCRIPT
+pushd #{tmp_directory}/ipa_root/
+zip -q -r archive.zip Payload
+popd
+SCRIPT
+
+ sh "sh #{tmp_directory}/do_zip.sh"
+ end
+
+ def sh cmd
+ log_info cmd.strip
+ result = `#{cmd}`
+ if result.strip.length > 0
+ log_info result.strip.each_line.map(&:strip).join("\n")
+ end
+ result
+ end
+
+ def deploy
+ end
+end