Skip to content
This repository has been archived by the owner on Oct 2, 2018. It is now read-only.

Commit

Permalink
Added support for export_options and app thinning
Browse files Browse the repository at this point in the history
  • Loading branch information
bartoszj committed Feb 9, 2016
1 parent 12a01a4 commit 0d78a22
Show file tree
Hide file tree
Showing 9 changed files with 325 additions and 15 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,28 @@ output_directory "./build" # store the ipa in this folder
output_name "MyApp" # the name of the ipa file
```

## Export options

Since Xcode 7, `gym` is using new Xcode API which allows us to specify export options using `plist` file. By default `gym` creates this file for you and you are able to modify some parameters by using `export_method`, `export_team_id`, `include_symbols` or `include_bitcode`. If you want to have more options, like creating manifest file or app thinning, you can provide your own `plist` file:

```ruby
export_options "./ExportOptions.plist"
```

or you can provide hash of values directly in the `Gymfile`:

```ruby
export_options(
method: "ad-hoc",
manifest: {
appURL: "https://example.com/My App.ipa",
},
thinning: "<thin-for-all-variants>"
)
```

For the list of available options run `xcodebuild -help`.

# Automating the whole process

`gym` works great together with [fastlane](https://fastlane.tools), which connects all deployment tools into one streamlined workflow.
Expand Down
19 changes: 19 additions & 0 deletions examples/standard/ExampleExport.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?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>embedOnDemandResourcesAssetPacksInBundle</key>
<true/>
<key>manifest</key>
<dict>
<key>appURL</key>
<string>https://www.example.com/Example.ipa</string>
<key>displayImageURL</key>
<string>https://www.example.com/display.png</string>
<key>fullSizeImageURL</key>
<string>https://www.example.com/fullSize.png</string>
</dict>
<key>method</key>
<string>ad-hoc</string>
</dict>
</plist>
3 changes: 0 additions & 3 deletions lib/gym/detect_values.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ def self.set_additional_default_values

config[:output_name] ||= Gym.project.app_name

# we do it here, since the value is optional and should be pre-filled by fastlane if necessary
config[:export_method] ||= "app-store"

return config
end

Expand Down
16 changes: 16 additions & 0 deletions lib/gym/generators/package_command_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,22 @@ def dsym_path
generator.dsym_path
end

def manifest_path
generator.manifest_path
end

def app_thinning_path
generator.app_thinning_path
end

def app_thinning_size_report_path
generator.app_thinning_size_report_path
end

def apps_path
generator.apps_path
end

# The generator we need to use for the currently used Xcode version
def generator
if Gym.config[:use_legacy_build_api]
Expand Down
16 changes: 16 additions & 0 deletions lib/gym/generators/package_command_generator_legacy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,22 @@ def ipa_path
def dsym_path
Dir[BuildCommandGenerator.archive_path + "/**/*.app.dSYM"].last
end

def manifest_path
""
end

def app_thinning_path
""
end

def app_thinning_size_report_path
""
end

def apps_path
""
end
end
end
end
95 changes: 88 additions & 7 deletions lib/gym/generators/package_command_generator_xcode7.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,20 @@ def temporary_output_path
def ipa_path
unless Gym.cache[:ipa_path]
path = Dir[File.join(temporary_output_path, "*.ipa")].last
ErrorHandler.handle_empty_archive unless path

Gym.cache[:ipa_path] = File.join(temporary_output_path, "#{Gym.config[:output_name]}.ipa")
FileUtils.mv(path, Gym.cache[:ipa_path]) if File.expand_path(path).downcase != File.expand_path(Gym.cache[:ipa_path]).downcase
if path
# Try to find IPA file in the output directory, used when app thinning was not set
Gym.cache[:ipa_path] = File.join(temporary_output_path, "#{Gym.config[:output_name]}.ipa")
FileUtils.mv(path, Gym.cache[:ipa_path]) if File.expand_path(path).downcase != File.expand_path(Gym.cache[:ipa_path]).downcase
elsif Dir.exist?(apps_path)
# Try to find "generic" IPA file inside "Apps" folder, used when app thinning was set
files = Dir[File.join(apps_path, "*.ipa")]
# Generic IPA file doesn't have suffix so its name is the shortest
path = files.min_by(&:length)
Gym.cache[:ipa_path] = File.join(temporary_output_path, "#{Gym.config[:output_name]}.ipa")
FileUtils.cp(path, Gym.cache[:ipa_path]) if File.expand_path(path).downcase != File.expand_path(Gym.cache[:ipa_path]).downcase
else
ErrorHandler.handle_empty_archive unless path
end
end
Gym.cache[:ipa_path]
end
Expand All @@ -62,16 +72,87 @@ def config_path
return Gym.cache[:config_path]
end

# The path to the manifest plist file
def manifest_path
Gym.cache[:manifest_path] ||= File.join(temporary_output_path, "manifest.plist")
end

# The path to the app-thinning plist file
def app_thinning_path
Gym.cache[:app_thinning] ||= File.join(temporary_output_path, "app-thinning.plist")
end

# The path to the App Thinning Size Report file
def app_thinning_size_report_path
Gym.cache[:app_thinning_size_report] ||= File.join(temporary_output_path, "App Thinning Size Report.txt")
end

# The path to the Apps folder
def apps_path
Gym.cache[:apps_path] ||= File.join(temporary_output_path, "Apps")
end

private

def normalize_export_options(hash)
# Normalize some values
hash[:onDemandResourcesAssetPacksBaseURL] = URI.escape(hash[:onDemandResourcesAssetPacksBaseURL]) if hash[:onDemandResourcesAssetPacksBaseURL]
if hash[:manifest]
hash[:manifest][:appURL] = URI.escape(hash[:manifest][:appURL]) if hash[:manifest][:appURL]
hash[:manifest][:displayImageURL] = URI.escape(hash[:manifest][:displayImageURL]) if hash[:manifest][:displayImageURL]
hash[:manifest][:fullSizeImageURL] = URI.escape(hash[:manifest][:fullSizeImageURL]) if hash[:manifest][:fullSizeImageURL]
hash[:manifest][:assetPackManifestURL] = URI.escape(hash[:manifest][:assetPackManifestURL]) if hash[:manifest][:assetPackManifestURL]
end
hash
end

def keys_to_symbols(hash)
# Convert keys to symbols
hash = hash.each_with_object({}) do |(k, v), memo|
memo[k.to_sym] = v
memo
end
hash
end

def read_export_options
# Reads export options
if Gym.config[:export_options]
if Gym.config[:export_options].kind_of?(Hash)
# Reads options from hash
hash = normalize_export_options(Gym.config[:export_options])
else
# Reads optoins from file
hash = Plist.parse_xml(Gym.config[:export_options])
# Convert keys to symbols
hash = keys_to_symbols(hash)
end

# Saves configuration for later use
Gym.config[:export_method] ||= hash[:method]
Gym.config[:include_symbols] = hash[:uploadSymbols] if Gym.config[:include_symbols].nil?
Gym.config[:include_bitcode] = hash[:uploadBitcode] if Gym.config[:include_bitcode].nil?
Gym.config[:export_team_id] ||= hash[:teamID]
else
hash = {}
# Sets default values
Gym.config[:export_method] ||= "app-store"
Gym.config[:include_symbols] = true if Gym.config[:include_symbols].nil?
Gym.config[:include_bitcode] = false if Gym.config[:include_bitcode].nil?
end
hash
end

def config_content
require 'plist'

hash = { method: Gym.config[:export_method] }
hash = read_export_options

# Overrides export options if needed
hash[:method] = Gym.config[:export_method]
if Gym.config[:export_method] == 'app-store'
hash[:uploadSymbols] = (Gym.config[:include_symbols] ? true : false)
hash[:uploadBitcode] = (Gym.config[:include_bitcode] ? true : false)
hash[:uploadSymbols] = (Gym.config[:include_symbols] ? true : false) unless Gym.config[:include_symbols].nil?
hash[:uploadBitcode] = (Gym.config[:include_bitcode] ? true : false) unless Gym.config[:include_bitcode].nil?
end
hash[:teamID] = Gym.config[:export_team_id] if Gym.config[:export_team_id]

Expand Down
17 changes: 13 additions & 4 deletions lib/gym/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,14 @@ def self.plain_options
short_option: "-m",
env_name: "GYM_INCLUDE_SYMBOLS",
description: "Should the ipa file include symbols?",
default_value: true,
is_string: false),
is_string: false,
optional: true),
FastlaneCore::ConfigItem.new(key: :include_bitcode,
short_option: "-z",
env_name: "GYM_INCLUDE_BITCODE",
description: "Should the ipa include bitcode?",
default_value: false,
is_string: false),
is_string: false,
optional: true),
FastlaneCore::ConfigItem.new(key: :use_legacy_build_api,
env_name: "GYM_USE_LEGACY_BUILD_API",
description: "Don't use the new API because of https://openradar.appspot.com/radar?id=4952000420642816",
Expand All @@ -113,6 +113,15 @@ def self.plain_options
av = %w(app-store ad-hoc package enterprise development developer-id)
UI.user_error!("Unsupported export_method, must be: #{av}") unless av.include?(value)
end),
FastlaneCore::ConfigItem.new(key: :export_options,
env_name: "GYM_EXPORT_OPTIONS",
description: "Specifies path to export options plist. User xcodebuild -help to print the full set of available options",
is_string: false,
optional: true,
conflicting_options: [:use_legacy_build_api],
conflict_block: proc do |value|
UI.user_error!("'#{value.key}' must be false to use 'export_options'")
end),

# Very optional
FastlaneCore::ConfigItem.new(key: :archive_path,
Expand Down
56 changes: 55 additions & 1 deletion lib/gym/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ def run
package_app
fix_package
compress_and_move_dsym
move_ipa
path = move_ipa
move_manifest
move_app_thinning
move_app_thinning_size_report
move_apps_folder

path
elsif Gym.project.mac?
compress_and_move_dsym
copy_mac_app
Expand Down Expand Up @@ -148,6 +154,54 @@ def copy_mac_app
app_path
end

# Move the manifest.plist if exists into the output directory
def move_manifest
if File.exist?(PackageCommandGenerator.manifest_path)
FileUtils.mv(PackageCommandGenerator.manifest_path, File.expand_path(Gym.config[:output_directory]), force: true)
manifest_path = File.join(File.expand_path(Gym.config[:output_directory]), File.basename(PackageCommandGenerator.manifest_path))

UI.success "Successfully exported the manifest.plist file:"
UI.message manifest_path
manifest_path
end
end

# Move the app-thinning.plist file into the output directory
def move_app_thinning
if File.exist?(PackageCommandGenerator.app_thinning_path)
FileUtils.mv(PackageCommandGenerator.app_thinning_path, File.expand_path(Gym.config[:output_directory]), force: true)
app_thinning_path = File.join(File.expand_path(Gym.config[:output_directory]), File.basename(PackageCommandGenerator.app_thinning_path))

UI.success "Successfully exported the app-thinning.plist file:"
UI.message app_thinning_path
app_thinning_path
end
end

# Move the App Thinning Size Report.txt file into the output directory
def move_app_thinning_size_report
if File.exist?(PackageCommandGenerator.app_thinning_size_report_path)
FileUtils.mv(PackageCommandGenerator.app_thinning_size_report_path, File.expand_path(Gym.config[:output_directory]), force: true)
app_thinning_size_report_path = File.join(File.expand_path(Gym.config[:output_directory]), File.basename(PackageCommandGenerator.app_thinning_size_report_path))

UI.success "Successfully exported the App Thinning Size Report.txt file:"
UI.message app_thinning_size_report_path
app_thinning_size_report_path
end
end

# Move the Apps folder to the output directory
def move_apps_folder
if Dir.exist?(PackageCommandGenerator.apps_path)
FileUtils.mv(PackageCommandGenerator.apps_path, File.expand_path(Gym.config[:output_directory]), force: true)
apps_path = File.join(File.expand_path(Gym.config[:output_directory]), File.basename(PackageCommandGenerator.apps_path))

UI.success "Successfully exported Apps folder:"
UI.message apps_path
apps_path
end
end

private

def find_archive_path
Expand Down
Loading

0 comments on commit 0d78a22

Please sign in to comment.