From 3d4b604f4f9f6275c5dd825d57c31e3bfde548aa Mon Sep 17 00:00:00 2001 From: Jerome Lacoste Date: Fri, 11 Sep 2015 08:29:19 +0200 Subject: [PATCH 001/176] Little refactoring --- lib/gym/runner.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb index 4a4fc9e..47dbc64 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -120,9 +120,11 @@ def compress_and_move_dsym # Moves over the binary and dsym file to the output directory # @return (String) The path to the resulting ipa file def move_ipa - FileUtils.mv(PackageCommandGenerator.ipa_path, Gym.config[:output_directory], force: true) + ipa_path = BuildCommandGenerator.archive_path - ipa_path = File.join(Gym.config[:output_directory], File.basename(PackageCommandGenerator.ipa_path)) + FileUtils.mv(ipa_path, Gym.config[:output_directory], force: true) + + ipa_path = File.join(Gym.config[:output_directory], File.basename(ipa_path)) Helper.log.info "Successfully exported and signed the ipa file:".green Helper.log.info ipa_path From 28674a357e1ddda4a380b62e2c06d06411b4c5ee Mon Sep 17 00:00:00 2001 From: Jerome Lacoste Date: Fri, 11 Sep 2015 06:44:16 +0200 Subject: [PATCH 002/176] Updated gym to use the new xcodebuild API (reapplied 1bb6302869f720ccf4674e8f1c54b9ab0b320a52 manually) --- lib/gym.rb | 5 ----- lib/gym/package_command_generator.rb | 28 ++++------------------------ lib/gym/runner.rb | 13 +------------ 3 files changed, 5 insertions(+), 41 deletions(-) diff --git a/lib/gym.rb b/lib/gym.rb index 7c1167a..6244522 100644 --- a/lib/gym.rb +++ b/lib/gym.rb @@ -9,11 +9,6 @@ require 'gym/options' require 'gym/detect_values' -# Import all the fixes -require 'gym/xcodebuild_fixes/swift_fix' -require 'gym/xcodebuild_fixes/watchkit_fix' -require 'gym/xcodebuild_fixes/package_application_fix' - require 'fastlane_core' require 'terminal-table' diff --git a/lib/gym/package_command_generator.rb b/lib/gym/package_command_generator.rb index ddcb9f4..51e1967 100644 --- a/lib/gym/package_command_generator.rb +++ b/lib/gym/package_command_generator.rb @@ -8,7 +8,7 @@ module Gym class PackageCommandGenerator class << self def generate - parts = ["/usr/bin/xcrun #{XcodebuildFixes.patch_package_application} -v"] + parts = ["/usr/bin/xcrun xcodebuild -exportArchive"] parts += options parts += pipe @@ -18,17 +18,9 @@ def generate def options options = [] - options << Shellwords.escape(appfile_path) - options << "-o '#{ipa_path}'" - options << "exportFormat ipa" - - if Gym.config[:provisioning_profile_path] - options << "--embed '#{Gym.config[:provisioning_profile_path]}'" - end - - if Gym.config[:codesigning_identity] - options << "--sign '#{Gym.config[:codesigning_identity]}'" - end + options << "-exportOptionsPlist /tmp/config.plist" + options << "-archivePath '#{BuildCommandGenerator.archive_path}'" + options << "-exportPath '#{BuildCommandGenerator.build_path}'" # we move it once the binary is finished options end @@ -37,18 +29,6 @@ def pipe [""] end - def appfile_path - path = Dir.glob("#{BuildCommandGenerator.archive_path}/Products/Applications/*.app").first - path ||= Dir[BuildCommandGenerator.archive_path + "/**/*.app"].last - - return path - end - - # We export it to the temporary folder and move it over to the actual output once it's finished and valid - def ipa_path - File.join(BuildCommandGenerator.build_path, "#{Gym.config[:output_name]}.ipa") - end - # The path the the dsym file for this app. Might be nil def dsym_path Dir[BuildCommandGenerator.archive_path + "/**/*.app.dSYM"].last diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb index 47dbc64..2364fd3 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -6,7 +6,6 @@ module Gym class Runner # @return (String) The path to the resulting ipa def run - clear_old_files build_app verify_archive @@ -14,10 +13,6 @@ def run if Gym.project.ios? package_app - Gym::XcodebuildFixes.swift_library_fix - Gym::XcodebuildFixes.watchkit_fix - Gym::XcodebuildFixes.clear_patched_package_application - compress_and_move_dsym move_ipa elsif Gym.project.mac? @@ -59,12 +54,6 @@ def print_command(command, title) # @!group The individual steps ##################################################### - def clear_old_files - if File.exist? PackageCommandGenerator.ipa_path - File.delete(PackageCommandGenerator.ipa_path) - end - end - # Builds the app and prepares the archive def build_app command = BuildCommandGenerator.generate @@ -120,7 +109,7 @@ def compress_and_move_dsym # Moves over the binary and dsym file to the output directory # @return (String) The path to the resulting ipa file def move_ipa - ipa_path = BuildCommandGenerator.archive_path + ipa_path = Dir.glob(File.join(BuildCommandGenerator.build_path, "*.ipa")).last FileUtils.mv(ipa_path, Gym.config[:output_directory], force: true) From 185d26f8b256c2cfed4ddc6723800eeea602df8e Mon Sep 17 00:00:00 2001 From: Jerome Lacoste Date: Fri, 11 Sep 2015 08:27:00 +0200 Subject: [PATCH 003/176] Support both V6 and V7 --- lib/gym.rb | 25 ++++++ lib/gym/package_command_generator.rb | 101 +++++++++++++++++++++++- lib/gym/runner.rb | 28 ++++++- spec/package_command_generator_spec.rb | 16 ++-- spec/xcodebuild_fixes/swift_fix_spec.rb | 4 + 5 files changed, 164 insertions(+), 10 deletions(-) diff --git a/lib/gym.rb b/lib/gym.rb index 6244522..065c4be 100644 --- a/lib/gym.rb +++ b/lib/gym.rb @@ -30,7 +30,32 @@ def gymfile_name def xcode_path @path ||= `xcode-select --print-path`.strip end + + def xcode_version + @version ||= parse_version + end + + private def parse_version + output = `DEVELOPER_DIR='' "#{xcode_path}/usr/bin/xcodebuild" -version` + return '0.0' if output.nil? + output.split("\n").first.split(' ')[1] + end + + def pre_7? + v = xcode_version + is_pre = v.split('.')[0].to_i < 7 + is_pre + end + + def init_libs + return unless pre_7? + # Import all the fixes + require 'gym/xcodebuild_fixes/swift_fix' + require 'gym/xcodebuild_fixes/watchkit_fix' + require 'gym/xcodebuild_fixes/package_application_fix' + end end Helper = FastlaneCore::Helper # you gotta love Ruby: Helper.* should use the Helper class contained in FastlaneCore + Gym.init_libs end diff --git a/lib/gym/package_command_generator.rb b/lib/gym/package_command_generator.rb index 51e1967..8ffd518 100644 --- a/lib/gym/package_command_generator.rb +++ b/lib/gym/package_command_generator.rb @@ -1,24 +1,119 @@ require 'shellwords' module Gym - # Responsible for building the fully working xcodebuild command + class PackageCommandGenerator + class << self + def generate + generator.generate + end + + def appfile_path + generator.appfile_path + end + + def ipa_path + generator.ipa_path + end + + def dsym_path + generator.dsym_path + end + + private + + def generator + Gym.pre_7? ? PackageCommandGeneratorPre7 : PackageCommandGeneratorV7 + end + end + end + + # Responsible for building the fully working xcodebuild command on xcode < 7 # # Because of a known bug in PackageApplication Perl script used by Xcode the packaging process is performed with # a patched version of the script. - class PackageCommandGenerator + class PackageCommandGeneratorPre7 + class << self + def generate + parts = ["/usr/bin/xcrun #{XcodebuildFixes.patch_package_application} -v"] + parts += options + parts += pipe + + parts + end + + def options + options = [] + + options << Shellwords.escape(appfile_path) + options << "-o '#{ipa_path}'" + options << "exportFormat ipa" + + if Gym.config[:provisioning_profile_path] + options << "--embed '#{Gym.config[:provisioning_profile_path]}'" + end + + if Gym.config[:codesigning_identity] + options << "--sign '#{Gym.config[:codesigning_identity]}'" + end + + options + end + + def pipe + [""] + end + + def appfile_path + path = Dir.glob("#{BuildCommandGenerator.archive_path}/Products/Applications/*.app").first + path ||= Dir[BuildCommandGenerator.archive_path + "/**/*.app"].last + + return path + end + + # We export it to the temporary folder and move it over to the actual output once it's finished and valid + def ipa_path + File.join(BuildCommandGenerator.build_path, "#{Gym.config[:output_name]}.ipa") + end + + # The path the the dsym file for this app. Might be nil + def dsym_path + Dir[BuildCommandGenerator.archive_path + "/**/*.app.dSYM"].last + end + end + end + + # Responsible for building the fully working xcodebuild command + class PackageCommandGeneratorV7 + @config_content = %( + + + + method + app-store + uploadSymbols + + uploadBitcode + + + +) + @config_path = '/tmp/config.plist' + class << self def generate parts = ["/usr/bin/xcrun xcodebuild -exportArchive"] parts += options parts += pipe + File.write(@config_path, @config_content) # overwrite everytime. Could be optimized + parts end def options options = [] - options << "-exportOptionsPlist /tmp/config.plist" + options << "-exportOptionsPlist #{@config_path}" options << "-archivePath '#{BuildCommandGenerator.archive_path}'" options << "-exportPath '#{BuildCommandGenerator.build_path}'" # we move it once the binary is finished diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb index 2364fd3..d823ac8 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -6,6 +6,7 @@ module Gym class Runner # @return (String) The path to the resulting ipa def run + clear_old_files build_app verify_archive @@ -13,6 +14,7 @@ def run if Gym.project.ios? package_app + fix_package compress_and_move_dsym move_ipa elsif Gym.project.mac? @@ -54,6 +56,20 @@ def print_command(command, title) # @!group The individual steps ##################################################### + def clear_old_files + return unless Gym.pre_7? + if File.exist? PackageCommandGenerator.ipa_path + File.delete(PackageCommandGenerator.ipa_path) + end + end + + def fix_package + return unless Gym.pre_7? + Gym::XcodebuildFixes.swift_library_fix + Gym::XcodebuildFixes.watchkit_fix + Gym::XcodebuildFixes.clear_patched_package_application + end + # Builds the app and prepares the archive def build_app command = BuildCommandGenerator.generate @@ -109,7 +125,7 @@ def compress_and_move_dsym # Moves over the binary and dsym file to the output directory # @return (String) The path to the resulting ipa file def move_ipa - ipa_path = Dir.glob(File.join(BuildCommandGenerator.build_path, "*.ipa")).last + ipa_path = find_archive_path FileUtils.mv(ipa_path, Gym.config[:output_directory], force: true) @@ -132,5 +148,15 @@ def move_mac_app Helper.log.info app_path app_path end + + private + + def find_archive_path + if Gym.pre_7? + BuildCommandGenerator.archive_path + else + Dir.glob(File.join(BuildCommandGenerator.build_path, "*.ipa")).last + end + end end end diff --git a/spec/package_command_generator_spec.rb b/spec/package_command_generator_spec.rb index 73a24ad..8d82f34 100644 --- a/spec/package_command_generator_spec.rb +++ b/spec/package_command_generator_spec.rb @@ -1,14 +1,18 @@ +require 'gym/xcodebuild_fixes/swift_fix' +require 'gym/xcodebuild_fixes/watchkit_fix' +require 'gym/xcodebuild_fixes/package_application_fix' + describe Gym do - describe Gym::PackageCommandGenerator do + describe Gym::PackageCommandGeneratorPre7 do it "works with the example project with no additional parameters" do options = { project: "./examples/standard/Example.xcodeproj" } Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) - result = Gym::PackageCommandGenerator.generate + result = Gym::PackageCommandGeneratorPre7.generate expect(result).to eq([ "/usr/bin/xcrun /tmp/PackageApplication4Gym -v", "''", - "-o '#{Gym::PackageCommandGenerator.ipa_path}'", + "-o '#{Gym::PackageCommandGeneratorPre7.ipa_path}'", "exportFormat ipa", "" ]) @@ -18,13 +22,13 @@ options = { project: "./examples/standard/Example.xcodeproj" } Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) - allow(Gym::PackageCommandGenerator).to receive(:appfile_path).and_return("Krause's App") + allow(Gym::PackageCommandGeneratorPre7).to receive(:appfile_path).and_return("Krause's App") - result = Gym::PackageCommandGenerator.generate + result = Gym::PackageCommandGeneratorPre7.generate expect(result).to eq([ "/usr/bin/xcrun /tmp/PackageApplication4Gym -v", "Krause\\'s\\ App", - "-o '#{Gym::PackageCommandGenerator.ipa_path}'", + "-o '#{Gym::PackageCommandGeneratorPre7.ipa_path}'", "exportFormat ipa", "" ]) diff --git a/spec/xcodebuild_fixes/swift_fix_spec.rb b/spec/xcodebuild_fixes/swift_fix_spec.rb index ee2467e..dbb1ea8 100644 --- a/spec/xcodebuild_fixes/swift_fix_spec.rb +++ b/spec/xcodebuild_fixes/swift_fix_spec.rb @@ -1,3 +1,7 @@ +require 'gym/xcodebuild_fixes/swift_fix' +require 'gym/xcodebuild_fixes/watchkit_fix' +require 'gym/xcodebuild_fixes/package_application_fix' + describe Gym do describe Gym::XcodebuildFixes do class FakePackageCommandGenerator From 86a780c9b97106206f5d02b01ba305ebb0a2b106 Mon Sep 17 00:00:00 2001 From: Jerome Lacoste Date: Sat, 12 Sep 2015 11:45:16 +0200 Subject: [PATCH 004/176] Code style compatibility with 2.0.0 --- lib/gym.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/gym.rb b/lib/gym.rb index 065c4be..d5ac827 100644 --- a/lib/gym.rb +++ b/lib/gym.rb @@ -35,12 +35,6 @@ def xcode_version @version ||= parse_version end - private def parse_version - output = `DEVELOPER_DIR='' "#{xcode_path}/usr/bin/xcodebuild" -version` - return '0.0' if output.nil? - output.split("\n").first.split(' ')[1] - end - def pre_7? v = xcode_version is_pre = v.split('.')[0].to_i < 7 @@ -54,6 +48,14 @@ def init_libs require 'gym/xcodebuild_fixes/watchkit_fix' require 'gym/xcodebuild_fixes/package_application_fix' end + + private + + def parse_version + output = `DEVELOPER_DIR='' "#{xcode_path}/usr/bin/xcodebuild" -version` + return '0.0' if output.nil? + output.split("\n").first.split(' ')[1] + end end Helper = FastlaneCore::Helper # you gotta love Ruby: Helper.* should use the Helper class contained in FastlaneCore From 7c60a482267208a25509568dc8a6808fa35bffe9 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 16 Sep 2015 10:01:46 -0700 Subject: [PATCH 005/176] Verification thta provisioning profile is used Fixes https://github.com/fastlane/gym/issues/69 --- spec/package_command_generator_spec.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/spec/package_command_generator_spec.rb b/spec/package_command_generator_spec.rb index 73a24ad..332d9b7 100644 --- a/spec/package_command_generator_spec.rb +++ b/spec/package_command_generator_spec.rb @@ -29,5 +29,25 @@ "" ]) end + + it "supports passing a path to a provisioning profile" do + # Profile Installation + expect(FastlaneCore::ProvisioningProfile).to receive(:install).with("./spec/fixtures/dummy.mobileprovision") + options = { + project: "./examples/standard/Example.xcodeproj", + provisioning_profile_path: "./spec/fixtures/dummy.mobileprovision" + } + Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) + + result = Gym::PackageCommandGenerator.generate + expect(result).to eq([ + "/usr/bin/xcrun /tmp/PackageApplication4Gym -v", + "''", + "-o '#{Gym::PackageCommandGenerator.ipa_path}'", + "exportFormat ipa", + "--embed './spec/fixtures/dummy.mobileprovision'", + "" + ]) + end end end From c9c6f18248880f951686731833c630e0487ab293 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 16 Sep 2015 10:03:38 -0700 Subject: [PATCH 006/176] Version Bump by fastlane --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index 62f772a..7f221a3 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "0.4.6" + VERSION = "0.5.0" DESCRIPTION = "Building your iOS apps has never been easier" end From 77fffcff0803c484c5d314df69fcb75f3f670901 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 16 Sep 2015 10:03:55 -0700 Subject: [PATCH 007/176] Added provisioning profile fixture --- spec/fixtures/dummy.mobileprovision | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 spec/fixtures/dummy.mobileprovision diff --git a/spec/fixtures/dummy.mobileprovision b/spec/fixtures/dummy.mobileprovision new file mode 100644 index 0000000..e69de29 From ee3f916c9cd210bc48196086d65f45e82a877f45 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 16 Sep 2015 10:44:26 -0700 Subject: [PATCH 008/176] Refactored the new package command generator Added new Xcode class Updated tests --- lib/gym.rb | 30 +--- lib/gym/generators/README.md | 1 + .../build_command_generator.rb | 0 .../generators/package_command_generator.rb | 34 +++++ .../package_command_generator_legacy.rb | 56 ++++++++ .../package_command_generator_xcode7.rb | 54 +++++++ lib/gym/manager.rb | 2 +- lib/gym/package_command_generator.rb | 133 ------------------ lib/gym/runner.rb | 10 +- lib/gym/xcode.rb | 30 ++++ .../package_application_fix.rb | 2 +- lib/gym/xcodebuild_fixes/swift_fix.rb | 2 +- lib/gym/xcodebuild_fixes/watchkit_fix.rb | 2 +- ... package_command_generator_legacy_spec.rb} | 16 +-- spec/package_command_generator_xcode7_spec.rb | 0 15 files changed, 197 insertions(+), 175 deletions(-) create mode 100644 lib/gym/generators/README.md rename lib/gym/{ => generators}/build_command_generator.rb (100%) create mode 100644 lib/gym/generators/package_command_generator.rb create mode 100644 lib/gym/generators/package_command_generator_legacy.rb create mode 100644 lib/gym/generators/package_command_generator_xcode7.rb delete mode 100644 lib/gym/package_command_generator.rb create mode 100644 lib/gym/xcode.rb rename spec/{package_command_generator_spec.rb => package_command_generator_legacy_spec.rb} (76%) create mode 100644 spec/package_command_generator_xcode7_spec.rb diff --git a/lib/gym.rb b/lib/gym.rb index d5ac827..ba75c62 100644 --- a/lib/gym.rb +++ b/lib/gym.rb @@ -2,12 +2,13 @@ require 'gym/version' require 'gym/manager' require 'gym/project' -require 'gym/build_command_generator' -require 'gym/package_command_generator' +require 'gym/generators/build_command_generator' +require 'gym/generators/package_command_generator' require 'gym/runner' require 'gym/error_handler' require 'gym/options' require 'gym/detect_values' +require 'gym/xcode' require 'fastlane_core' require 'terminal-table' @@ -27,37 +28,16 @@ def gymfile_name "Gymfile" end - def xcode_path - @path ||= `xcode-select --print-path`.strip - end - - def xcode_version - @version ||= parse_version - end - - def pre_7? - v = xcode_version - is_pre = v.split('.')[0].to_i < 7 - is_pre - end - def init_libs - return unless pre_7? + return unless Xcode.pre_7? # Import all the fixes require 'gym/xcodebuild_fixes/swift_fix' require 'gym/xcodebuild_fixes/watchkit_fix' require 'gym/xcodebuild_fixes/package_application_fix' end - - private - - def parse_version - output = `DEVELOPER_DIR='' "#{xcode_path}/usr/bin/xcodebuild" -version` - return '0.0' if output.nil? - output.split("\n").first.split(' ')[1] - end end Helper = FastlaneCore::Helper # you gotta love Ruby: Helper.* should use the Helper class contained in FastlaneCore + Gym.init_libs end diff --git a/lib/gym/generators/README.md b/lib/gym/generators/README.md new file mode 100644 index 0000000..4d7905e --- /dev/null +++ b/lib/gym/generators/README.md @@ -0,0 +1 @@ +All the classes that generate commands we want to execute diff --git a/lib/gym/build_command_generator.rb b/lib/gym/generators/build_command_generator.rb similarity index 100% rename from lib/gym/build_command_generator.rb rename to lib/gym/generators/build_command_generator.rb diff --git a/lib/gym/generators/package_command_generator.rb b/lib/gym/generators/package_command_generator.rb new file mode 100644 index 0000000..fa20b95 --- /dev/null +++ b/lib/gym/generators/package_command_generator.rb @@ -0,0 +1,34 @@ +require 'shellwords' + +# The concrete implementations +require 'gym/generators/package_command_generator_legacy' +require 'gym/generators/package_command_generator_xcode7' + +module Gym + class PackageCommandGenerator + class << self + def generate + generator.generate + end + + def appfile_path + generator.appfile_path + end + + def ipa_path + generator.ipa_path + end + + def dsym_path + generator.dsym_path + end + + private + + # The generator we need to use for the currently used Xcode version + def generator + Xcode.pre_7? ? PackageCommandGeneratorLegacy : PackageCommandGeneratorXcode7 + end + end + end +end diff --git a/lib/gym/generators/package_command_generator_legacy.rb b/lib/gym/generators/package_command_generator_legacy.rb new file mode 100644 index 0000000..690bc88 --- /dev/null +++ b/lib/gym/generators/package_command_generator_legacy.rb @@ -0,0 +1,56 @@ +module Gym + # Responsible for building the fully working xcodebuild command on Xcode < 7 + # + # Because of a known bug in PackageApplication Perl script used by Xcode the packaging process is performed with + # a patched version of the script. + class PackageCommandGeneratorLegacy + class << self + def generate + parts = ["/usr/bin/xcrun #{XcodebuildFixes.patch_package_application} -v"] + parts += options + parts += pipe + + parts + end + + def options + options = [] + + options << Shellwords.escape(appfile_path) + options << "-o '#{ipa_path}'" + options << "exportFormat ipa" + + if Gym.config[:provisioning_profile_path] + options << "--embed '#{Gym.config[:provisioning_profile_path]}'" + end + + if Gym.config[:codesigning_identity] + options << "--sign '#{Gym.config[:codesigning_identity]}'" + end + + options + end + + def pipe + [""] + end + + def appfile_path + path = Dir.glob("#{BuildCommandGenerator.archive_path}/Products/Applications/*.app").first + path ||= Dir[BuildCommandGenerator.archive_path + "/**/*.app"].last + + return path + end + + # We export it to the temporary folder and move it over to the actual output once it's finished and valid + def ipa_path + File.join(BuildCommandGenerator.build_path, "#{Gym.config[:output_name]}.ipa") + end + + # The path the the dsym file for this app. Might be nil + def dsym_path + Dir[BuildCommandGenerator.archive_path + "/**/*.app.dSYM"].last + end + end + end +end diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb new file mode 100644 index 0000000..7d765d6 --- /dev/null +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -0,0 +1,54 @@ +module Gym + # Responsible for building the fully working xcodebuild command + class PackageCommandGeneratorXcode7 + @config_content = %( + + + + method + app-store + uploadSymbols + + uploadBitcode + + + +) + @config_path = '/tmp/config.plist' + + class << self + def generate + parts = ["/usr/bin/xcrun xcodebuild -exportArchive"] + parts += options + parts += pipe + + File.write(@config_path, @config_content) # overwrite everytime. Could be optimized + + parts + end + + def options + options = [] + + options << "-exportOptionsPlist #{@config_path}" + options << "-archivePath '#{BuildCommandGenerator.archive_path}'" + options << "-exportPath '#{BuildCommandGenerator.build_path}'" # we move it once the binary is finished + + options + end + + def pipe + [""] + end + + def ipa_path + File.join(BuildCommandGenerator.build_path, "#{Gym.config[:output_name]}.ipa") + end + + # The path the the dsym file for this app. Might be nil + def dsym_path + Dir[BuildCommandGenerator.archive_path + "/**/*.app.dSYM"].last + end + end + end +end diff --git a/lib/gym/manager.rb b/lib/gym/manager.rb index 9f4d499..43e5019 100644 --- a/lib/gym/manager.rb +++ b/lib/gym/manager.rb @@ -19,7 +19,7 @@ def print_summary rows << ["Configuration", config[:configuration]] if config[:configuration] rows << ["Archive Path", config[:archive_path]] if config[:archive_path] rows << ["Platform", Gym.project.ios? ? "iOS" : "Mac"] - rows << ["Xcode Path", Gym.xcode_path.gsub("/Contents/Developer", "")] + rows << ["Xcode Path", Xcode.xcode_path.gsub("/Contents/Developer", "")] puts "" puts Terminal::Table.new( diff --git a/lib/gym/package_command_generator.rb b/lib/gym/package_command_generator.rb deleted file mode 100644 index 8ffd518..0000000 --- a/lib/gym/package_command_generator.rb +++ /dev/null @@ -1,133 +0,0 @@ -require 'shellwords' - -module Gym - class PackageCommandGenerator - class << self - def generate - generator.generate - end - - def appfile_path - generator.appfile_path - end - - def ipa_path - generator.ipa_path - end - - def dsym_path - generator.dsym_path - end - - private - - def generator - Gym.pre_7? ? PackageCommandGeneratorPre7 : PackageCommandGeneratorV7 - end - end - end - - # Responsible for building the fully working xcodebuild command on xcode < 7 - # - # Because of a known bug in PackageApplication Perl script used by Xcode the packaging process is performed with - # a patched version of the script. - class PackageCommandGeneratorPre7 - class << self - def generate - parts = ["/usr/bin/xcrun #{XcodebuildFixes.patch_package_application} -v"] - parts += options - parts += pipe - - parts - end - - def options - options = [] - - options << Shellwords.escape(appfile_path) - options << "-o '#{ipa_path}'" - options << "exportFormat ipa" - - if Gym.config[:provisioning_profile_path] - options << "--embed '#{Gym.config[:provisioning_profile_path]}'" - end - - if Gym.config[:codesigning_identity] - options << "--sign '#{Gym.config[:codesigning_identity]}'" - end - - options - end - - def pipe - [""] - end - - def appfile_path - path = Dir.glob("#{BuildCommandGenerator.archive_path}/Products/Applications/*.app").first - path ||= Dir[BuildCommandGenerator.archive_path + "/**/*.app"].last - - return path - end - - # We export it to the temporary folder and move it over to the actual output once it's finished and valid - def ipa_path - File.join(BuildCommandGenerator.build_path, "#{Gym.config[:output_name]}.ipa") - end - - # The path the the dsym file for this app. Might be nil - def dsym_path - Dir[BuildCommandGenerator.archive_path + "/**/*.app.dSYM"].last - end - end - end - - # Responsible for building the fully working xcodebuild command - class PackageCommandGeneratorV7 - @config_content = %( - - - - method - app-store - uploadSymbols - - uploadBitcode - - - -) - @config_path = '/tmp/config.plist' - - class << self - def generate - parts = ["/usr/bin/xcrun xcodebuild -exportArchive"] - parts += options - parts += pipe - - File.write(@config_path, @config_content) # overwrite everytime. Could be optimized - - parts - end - - def options - options = [] - - options << "-exportOptionsPlist #{@config_path}" - options << "-archivePath '#{BuildCommandGenerator.archive_path}'" - options << "-exportPath '#{BuildCommandGenerator.build_path}'" # we move it once the binary is finished - - options - end - - def pipe - [""] - end - - # The path the the dsym file for this app. Might be nil - def dsym_path - Dir[BuildCommandGenerator.archive_path + "/**/*.app.dSYM"].last - end - end - end -end diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb index d823ac8..f5b5f6b 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -57,14 +57,14 @@ def print_command(command, title) ##################################################### def clear_old_files - return unless Gym.pre_7? - if File.exist? PackageCommandGenerator.ipa_path + return unless Xcode.pre_7? + if File.exist?(PackageCommandGenerator.ipa_path) File.delete(PackageCommandGenerator.ipa_path) end end def fix_package - return unless Gym.pre_7? + return unless Xcode.pre_7? Gym::XcodebuildFixes.swift_library_fix Gym::XcodebuildFixes.watchkit_fix Gym::XcodebuildFixes.clear_patched_package_application @@ -119,7 +119,7 @@ def compress_and_move_dsym puts "" # new line - Helper.log.info "Successfully exported and compressed dSYM file.".green + Helper.log.info "Successfully exported and compressed dSYM file".green end # Moves over the binary and dsym file to the output directory @@ -152,7 +152,7 @@ def move_mac_app private def find_archive_path - if Gym.pre_7? + if Xcode.pre_7? BuildCommandGenerator.archive_path else Dir.glob(File.join(BuildCommandGenerator.build_path, "*.ipa")).last diff --git a/lib/gym/xcode.rb b/lib/gym/xcode.rb new file mode 100644 index 0000000..6c53409 --- /dev/null +++ b/lib/gym/xcode.rb @@ -0,0 +1,30 @@ +module Gym + class Xcode + class << self + # Path to the Xcode installation we're using + def xcode_path + @path ||= `xcode-select --print-path`.strip + end + + # Version of Xcode, e.g. 7.0 + def xcode_version + @version ||= parse_version + end + + # Below Xcode 7 (which offers a new nice API to sign the app) + def pre_7? + v = xcode_version + is_pre = v.split('.')[0].to_i < 7 + is_pre + end + + private + + def parse_version + output = `DEVELOPER_DIR='' "#{xcode_path}/usr/bin/xcodebuild" -version` + return '0.0' if output.nil? + output.split("\n").first.split(' ')[1] + end + end + end +end diff --git a/lib/gym/xcodebuild_fixes/package_application_fix.rb b/lib/gym/xcodebuild_fixes/package_application_fix.rb index 3b894ed..4bcaa0a 100644 --- a/lib/gym/xcodebuild_fixes/package_application_fix.rb +++ b/lib/gym/xcodebuild_fixes/package_application_fix.rb @@ -18,7 +18,7 @@ def patch_package_application expected_md5 = File.read(path) # If that location changes, search it using xcrun --sdk iphoneos -f PackageApplication - package_application_path = "#{Gym.xcode_path}/Platforms/iPhoneOS.platform/Developer/usr/bin/PackageApplication" + package_application_path = "#{Xcode.xcode_path}/Platforms/iPhoneOS.platform/Developer/usr/bin/PackageApplication" raise "Unable to patch the `PackageApplication` script bundled in XCode. This is not supported." unless expected_md5 == Digest::MD5.file(package_application_path).hexdigest diff --git a/lib/gym/xcodebuild_fixes/swift_fix.rb b/lib/gym/xcodebuild_fixes/swift_fix.rb index eeffba4..051265f 100644 --- a/lib/gym/xcodebuild_fixes/swift_fix.rb +++ b/lib/gym/xcodebuild_fixes/swift_fix.rb @@ -23,7 +23,7 @@ def swift_library_fix ipa_swift_frameworks.each do |path| framework = File.basename(path) - FileUtils.copy_file("#{Gym.xcode_path}/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphoneos/#{framework}", File.join(swift_support, framework)) + FileUtils.copy_file("#{Xcode.xcode_path}/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphoneos/#{framework}", File.join(swift_support, framework)) end # Add "SwiftSupport" to the .ipa archive diff --git a/lib/gym/xcodebuild_fixes/watchkit_fix.rb b/lib/gym/xcodebuild_fixes/watchkit_fix.rb index b9100d7..dd37d20 100644 --- a/lib/gym/xcodebuild_fixes/watchkit_fix.rb +++ b/lib/gym/xcodebuild_fixes/watchkit_fix.rb @@ -13,7 +13,7 @@ def watchkit_fix Dir.mkdir(watchkit_support) # Copy WK from Xcode into WatchKitSupport - FileUtils.copy_file("#{Gym.xcode_path}/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/Library/Application Support/WatchKit/WK", File.join(watchkit_support, "WK")) + FileUtils.copy_file("#{Xcode.xcode_path}/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/Library/Application Support/WatchKit/WK", File.join(watchkit_support, "WK")) # Add "WatchKitSupport" to the .ipa archive Dir.chdir(tmpdir) do diff --git a/spec/package_command_generator_spec.rb b/spec/package_command_generator_legacy_spec.rb similarity index 76% rename from spec/package_command_generator_spec.rb rename to spec/package_command_generator_legacy_spec.rb index 34be09b..40a7b67 100644 --- a/spec/package_command_generator_spec.rb +++ b/spec/package_command_generator_legacy_spec.rb @@ -3,16 +3,16 @@ require 'gym/xcodebuild_fixes/package_application_fix' describe Gym do - describe Gym::PackageCommandGeneratorPre7 do + describe Gym::PackageCommandGeneratorLegacy, now: true do it "works with the example project with no additional parameters" do options = { project: "./examples/standard/Example.xcodeproj" } Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) - result = Gym::PackageCommandGeneratorPre7.generate + result = Gym::PackageCommandGeneratorLegacy.generate expect(result).to eq([ "/usr/bin/xcrun /tmp/PackageApplication4Gym -v", "''", - "-o '#{Gym::PackageCommandGeneratorPre7.ipa_path}'", + "-o '#{Gym::PackageCommandGeneratorLegacy.ipa_path}'", "exportFormat ipa", "" ]) @@ -22,13 +22,13 @@ options = { project: "./examples/standard/Example.xcodeproj" } Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) - allow(Gym::PackageCommandGeneratorPre7).to receive(:appfile_path).and_return("Krause's App") + allow(Gym::PackageCommandGeneratorLegacy).to receive(:appfile_path).and_return("Krause's App") - result = Gym::PackageCommandGeneratorPre7.generate + result = Gym::PackageCommandGeneratorLegacy.generate expect(result).to eq([ "/usr/bin/xcrun /tmp/PackageApplication4Gym -v", "Krause\\'s\\ App", - "-o '#{Gym::PackageCommandGeneratorPre7.ipa_path}'", + "-o '#{Gym::PackageCommandGeneratorLegacy.ipa_path}'", "exportFormat ipa", "" ]) @@ -43,11 +43,11 @@ } Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) - result = Gym::PackageCommandGenerator.generate + result = Gym::PackageCommandGeneratorLegacy.generate expect(result).to eq([ "/usr/bin/xcrun /tmp/PackageApplication4Gym -v", "''", - "-o '#{Gym::PackageCommandGenerator.ipa_path}'", + "-o '#{Gym::PackageCommandGeneratorLegacy.ipa_path}'", "exportFormat ipa", "--embed './spec/fixtures/dummy.mobileprovision'", "" diff --git a/spec/package_command_generator_xcode7_spec.rb b/spec/package_command_generator_xcode7_spec.rb new file mode 100644 index 0000000..e69de29 From 3e4832c4fddd59d742def2310c056cc51cb84d1d Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 16 Sep 2015 11:48:31 -0700 Subject: [PATCH 009/176] Dynamic generation of plist files & options for symbols and bitcode --- gym.gemspec | 1 + .../package_command_generator_xcode7.rb | 38 ++++++++++--------- lib/gym/options.rb | 10 +++++ 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/gym.gemspec b/gym.gemspec index 77ccd0b..8805c7a 100644 --- a/gym.gemspec +++ b/gym.gemspec @@ -24,6 +24,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'fastlane_core', '>= 0.16.1', '< 1.0.0' # all shared code and dependencies spec.add_dependency 'xcpretty' # pretty xcodebuild output spec.add_dependency 'terminal-table' # print out build information + spec.add_dependency 'plist' # Generate the Xcode config plist file spec.add_dependency 'rubyzip', '>= 1.1.7' # fix swift/ipa # Development only diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index 7d765d6..10d3d2d 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -1,28 +1,13 @@ module Gym # Responsible for building the fully working xcodebuild command class PackageCommandGeneratorXcode7 - @config_content = %( - - - - method - app-store - uploadSymbols - - uploadBitcode - - - -) - @config_path = '/tmp/config.plist' - class << self def generate parts = ["/usr/bin/xcrun xcodebuild -exportArchive"] parts += options parts += pipe - File.write(@config_path, @config_content) # overwrite everytime. Could be optimized + File.write(config_path, config_content) # overwrite everytime. Could be optimized parts end @@ -30,7 +15,7 @@ def generate def options options = [] - options << "-exportOptionsPlist #{@config_path}" + options << "-exportOptionsPlist #{config_path}" options << "-archivePath '#{BuildCommandGenerator.archive_path}'" options << "-exportPath '#{BuildCommandGenerator.build_path}'" # we move it once the binary is finished @@ -49,6 +34,25 @@ def ipa_path def dsym_path Dir[BuildCommandGenerator.archive_path + "/**/*.app.dSYM"].last end + + private + + def config_path + @config_path ||= "/tmp/gym_config_#{Time.now.to_i}.plist" + return @config_path + end + + def config_content + require 'plist' + symbols = (Gym.config[:skip_symbols] ? false : true) + bitcode = (Gym.config[:upload_bitcode] ? true : false) + + { + method: 'app-store', + uploadSymbols: symbols, + uploadBitcode: bitcode + }.to_plist + end end end end diff --git a/lib/gym/options.rb b/lib/gym/options.rb index 1d9b406..81cbbcf 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -101,6 +101,16 @@ def self.plain_options verify_block: proc do |value| raise "File not found at path '#{File.expand_path(value)}'".red unless File.exist?(value) end), + FastlaneCore::ConfigItem.new(key: :skip_symbols, + short_option: "-m", + env_name: "GYM_SKIP_SYMBOLS", + description: "Should the ipa file *not* include symbols?", + default_value: true), + FastlaneCore::ConfigItem.new(key: :upload_bitcode, + short_option: "-z", + env_name: "GYM_UPLOAD_BITCODE", + description: "Should the ipa include bitcode?", + default_value: false), FastlaneCore::ConfigItem.new(key: :provisioning_profile_path, short_option: "-e", env_name: "GYM_PROVISIONING_PROFILE_PATH", From c6802e5d7c1692167087c53f243515368079f47a Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 16 Sep 2015 11:50:55 -0700 Subject: [PATCH 010/176] Simplified legacy build command generator specs --- spec/package_command_generator_legacy_spec.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/spec/package_command_generator_legacy_spec.rb b/spec/package_command_generator_legacy_spec.rb index 40a7b67..0b6eab3 100644 --- a/spec/package_command_generator_legacy_spec.rb +++ b/spec/package_command_generator_legacy_spec.rb @@ -1,9 +1,5 @@ -require 'gym/xcodebuild_fixes/swift_fix' -require 'gym/xcodebuild_fixes/watchkit_fix' -require 'gym/xcodebuild_fixes/package_application_fix' - describe Gym do - describe Gym::PackageCommandGeneratorLegacy, now: true do + describe Gym::PackageCommandGeneratorLegacy do it "works with the example project with no additional parameters" do options = { project: "./examples/standard/Example.xcodeproj" } Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) From 117386fe43150c6fdb9f54b97ce5eee94cfd27a3 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 16 Sep 2015 12:04:23 -0700 Subject: [PATCH 011/176] Finished Xcode 7 usage and added tests --- .../package_command_generator_xcode7.rb | 15 +++++++++++---- spec/package_command_generator_xcode7_spec.rb | 17 +++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index 10d3d2d..6da0f2e 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -3,6 +3,12 @@ module Gym class PackageCommandGeneratorXcode7 class << self def generate + if Gym.config[:provisioning_profile_path] + Helper.log.info "You're using Xcode 7, the `provisioning_profile_path` value will be ignored".yellow + Helper.log.info "Please follow the Code Signing Guide: https://github.com/KrauseFx/fastlane/blob/master/docs/CodeSigning.md".yellow + end + + parts = ["/usr/bin/xcrun xcodebuild -exportArchive"] parts += options parts += pipe @@ -15,7 +21,7 @@ def generate def options options = [] - options << "-exportOptionsPlist #{config_path}" + options << "-exportOptionsPlist '#{config_path}'" options << "-archivePath '#{BuildCommandGenerator.archive_path}'" options << "-exportPath '#{BuildCommandGenerator.build_path}'" # we move it once the binary is finished @@ -27,7 +33,7 @@ def pipe end def ipa_path - File.join(BuildCommandGenerator.build_path, "#{Gym.config[:output_name]}.ipa") + raise "You can't just access the path to the ipa in Xcode 7" end # The path the the dsym file for this app. Might be nil @@ -35,13 +41,14 @@ def dsym_path Dir[BuildCommandGenerator.archive_path + "/**/*.app.dSYM"].last end - private - + # The path the config file we use to sign our app def config_path @config_path ||= "/tmp/gym_config_#{Time.now.to_i}.plist" return @config_path end + private + def config_content require 'plist' symbols = (Gym.config[:skip_symbols] ? false : true) diff --git a/spec/package_command_generator_xcode7_spec.rb b/spec/package_command_generator_xcode7_spec.rb index e69de29..bb5658c 100644 --- a/spec/package_command_generator_xcode7_spec.rb +++ b/spec/package_command_generator_xcode7_spec.rb @@ -0,0 +1,17 @@ +describe Gym do + describe Gym::PackageCommandGeneratorXcode7, now: true do + it "works with the example project with no additional parameters" do + options = { project: "./examples/standard/Example.xcodeproj" } + Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) + + result = Gym::PackageCommandGeneratorXcode7.generate + expect(result).to eq([ + "/usr/bin/xcrun xcodebuild -exportArchive", + "-exportOptionsPlist '#{Gym::PackageCommandGeneratorXcode7.config_path}'", + "-archivePath '#{Gym::BuildCommandGenerator.archive_path}'", + "-exportPath '#{Gym::BuildCommandGenerator.build_path}'", + "" + ]) + end + end +end From 7d1ba3cffea6682e3e2f15636b6c132563db5087 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 16 Sep 2015 12:05:46 -0700 Subject: [PATCH 012/176] Improved code style --- lib/gym/generators/package_command_generator_xcode7.rb | 1 - spec/package_command_generator_xcode7_spec.rb | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index 6da0f2e..b199f3e 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -8,7 +8,6 @@ def generate Helper.log.info "Please follow the Code Signing Guide: https://github.com/KrauseFx/fastlane/blob/master/docs/CodeSigning.md".yellow end - parts = ["/usr/bin/xcrun xcodebuild -exportArchive"] parts += options parts += pipe diff --git a/spec/package_command_generator_xcode7_spec.rb b/spec/package_command_generator_xcode7_spec.rb index bb5658c..ec03b41 100644 --- a/spec/package_command_generator_xcode7_spec.rb +++ b/spec/package_command_generator_xcode7_spec.rb @@ -1,15 +1,15 @@ describe Gym do - describe Gym::PackageCommandGeneratorXcode7, now: true do + describe Gym::PackageCommandGeneratorXcode7 do it "works with the example project with no additional parameters" do options = { project: "./examples/standard/Example.xcodeproj" } Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) result = Gym::PackageCommandGeneratorXcode7.generate expect(result).to eq([ - "/usr/bin/xcrun xcodebuild -exportArchive", - "-exportOptionsPlist '#{Gym::PackageCommandGeneratorXcode7.config_path}'", - "-archivePath '#{Gym::BuildCommandGenerator.archive_path}'", - "-exportPath '#{Gym::BuildCommandGenerator.build_path}'", + "/usr/bin/xcrun xcodebuild -exportArchive", + "-exportOptionsPlist '#{Gym::PackageCommandGeneratorXcode7.config_path}'", + "-archivePath '#{Gym::BuildCommandGenerator.archive_path}'", + "-exportPath '#{Gym::BuildCommandGenerator.build_path}'", "" ]) end From f7811196a9b6c137431e3e4a9fe9a7d329f77cb4 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 16 Sep 2015 12:08:00 -0700 Subject: [PATCH 013/176] Updated README --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 03d2877..90a22c3 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,11 @@ If you run into any issues, use the `verbose` mode to get more information gym --verbose +To pass boolean parameters make sure to use `gym` like this: + + gym --upload_bitcode true --skip_symbols true + + # Gymfile Since you might want to manually trigger a new build but don't want to specify all the parameters every time, you can store your defaults in a so called `Gymfile`. From 88d76a33ca59ea50bb92ff76c94cb0a000bdef35 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 16 Sep 2015 14:29:45 -0700 Subject: [PATCH 014/176] Fixed not generating an ipa file with Xcode 7 --- lib/gym/generators/package_command_generator_xcode7.rb | 2 +- lib/gym/runner.rb | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index b199f3e..a3b2dc9 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -32,7 +32,7 @@ def pipe end def ipa_path - raise "You can't just access the path to the ipa in Xcode 7" + File.join(BuildCommandGenerator.build_path, "#{Gym.config[:output_name]}.ipa") end # The path the the dsym file for this app. Might be nil diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb index f5b5f6b..85977ae 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -125,11 +125,9 @@ def compress_and_move_dsym # Moves over the binary and dsym file to the output directory # @return (String) The path to the resulting ipa file def move_ipa - ipa_path = find_archive_path + FileUtils.mv(PackageCommandGenerator.ipa_path, Gym.config[:output_directory], force: true) - FileUtils.mv(ipa_path, Gym.config[:output_directory], force: true) - - ipa_path = File.join(Gym.config[:output_directory], File.basename(ipa_path)) + ipa_path = File.join(Gym.config[:output_directory], File.basename(PackageCommandGenerator.ipa_path)) Helper.log.info "Successfully exported and signed the ipa file:".green Helper.log.info ipa_path From 91e7cc780fe229ec3f46e4b1cfee9a8e0a11b248 Mon Sep 17 00:00:00 2001 From: Jesse Crocker Date: Thu, 17 Sep 2015 09:49:36 -0600 Subject: [PATCH 015/176] Change sdk in GymfileTemplate xcodebuild does not think 9.0 is a valid value, but it does like iphoneos9.0 --- lib/assets/GymfileTemplate | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/assets/GymfileTemplate b/lib/assets/GymfileTemplate index b0fa0fe..bf74edd 100644 --- a/lib/assets/GymfileTemplate +++ b/lib/assets/GymfileTemplate @@ -9,6 +9,6 @@ # scheme "Example" -# sdk "9.0" +# sdk "iphoneos9.0" output_directory "./" From 4ee337bc6d481ce9adf6143a8ce2963a9ef97868 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Thu, 17 Sep 2015 10:13:36 -0700 Subject: [PATCH 016/176] Changed from skip_symbols to upload_symbols --- README.md | 2 +- lib/gym/generators/package_command_generator_xcode7.rb | 2 +- lib/gym/options.rb | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 90a22c3..3112bd3 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ If you run into any issues, use the `verbose` mode to get more information To pass boolean parameters make sure to use `gym` like this: - gym --upload_bitcode true --skip_symbols true + gym --upload_bitcode true --upload_symbols true # Gymfile diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index a3b2dc9..06b9123 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -50,7 +50,7 @@ def config_path def config_content require 'plist' - symbols = (Gym.config[:skip_symbols] ? false : true) + symbols = (Gym.config[:upload_symbols] ? true : false) bitcode = (Gym.config[:upload_bitcode] ? true : false) { diff --git a/lib/gym/options.rb b/lib/gym/options.rb index 81cbbcf..287a155 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -101,10 +101,10 @@ def self.plain_options verify_block: proc do |value| raise "File not found at path '#{File.expand_path(value)}'".red unless File.exist?(value) end), - FastlaneCore::ConfigItem.new(key: :skip_symbols, + FastlaneCore::ConfigItem.new(key: :upload_symbols, short_option: "-m", - env_name: "GYM_SKIP_SYMBOLS", - description: "Should the ipa file *not* include symbols?", + env_name: "GYM_UPLOAD_SYMBOLS", + description: "Should the ipa file include symbols?", default_value: true), FastlaneCore::ConfigItem.new(key: :upload_bitcode, short_option: "-z", From 95e3a08e95043f43a990fb90344b252a9d64351e Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Thu, 17 Sep 2015 10:14:08 -0700 Subject: [PATCH 017/176] Moved xcode path detection into fastlane_core helper --- gym.gemspec | 2 +- lib/gym/xcode.rb | 18 ------------------ 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/gym.gemspec b/gym.gemspec index 8805c7a..b13c5bd 100644 --- a/gym.gemspec +++ b/gym.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency 'fastlane_core', '>= 0.16.1', '< 1.0.0' # all shared code and dependencies + spec.add_dependency 'fastlane_core', '>= 0.17.0', '< 1.0.0' # all shared code and dependencies spec.add_dependency 'xcpretty' # pretty xcodebuild output spec.add_dependency 'terminal-table' # print out build information spec.add_dependency 'plist' # Generate the Xcode config plist file diff --git a/lib/gym/xcode.rb b/lib/gym/xcode.rb index 6c53409..e3b0fd6 100644 --- a/lib/gym/xcode.rb +++ b/lib/gym/xcode.rb @@ -1,30 +1,12 @@ module Gym class Xcode class << self - # Path to the Xcode installation we're using - def xcode_path - @path ||= `xcode-select --print-path`.strip - end - - # Version of Xcode, e.g. 7.0 - def xcode_version - @version ||= parse_version - end - # Below Xcode 7 (which offers a new nice API to sign the app) def pre_7? v = xcode_version is_pre = v.split('.')[0].to_i < 7 is_pre end - - private - - def parse_version - output = `DEVELOPER_DIR='' "#{xcode_path}/usr/bin/xcodebuild" -version` - return '0.0' if output.nil? - output.split("\n").first.split(' ')[1] - end end end end From e6ba98b417221167bf8723cc2e2382a3902291af Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Thu, 17 Sep 2015 10:27:50 -0700 Subject: [PATCH 018/176] Added new helper methods to Xcode class --- lib/gym/xcode.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/gym/xcode.rb b/lib/gym/xcode.rb index e3b0fd6..ab7f7d4 100644 --- a/lib/gym/xcode.rb +++ b/lib/gym/xcode.rb @@ -1,6 +1,14 @@ module Gym class Xcode class << self + def xcode_path + Helper.xcode_path + end + + def xcode_version + Helper.xcode_version + end + # Below Xcode 7 (which offers a new nice API to sign the app) def pre_7? v = xcode_version From 3a72dac127a6867a847096bc47b1ba53d150f624 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Fri, 18 Sep 2015 11:28:13 -0700 Subject: [PATCH 019/176] Added encoding fix for `swift_fix --- lib/gym/xcodebuild_fixes/swift_fix.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/gym/xcodebuild_fixes/swift_fix.rb b/lib/gym/xcodebuild_fixes/swift_fix.rb index 051265f..2a59680 100644 --- a/lib/gym/xcodebuild_fixes/swift_fix.rb +++ b/lib/gym/xcodebuild_fixes/swift_fix.rb @@ -1,3 +1,7 @@ +# encoding: utf-8 +# from http://stackoverflow.com/a/9857493/445598 +# because of +# `incompatible encoding regexp match (UTF-8 regexp with ASCII-8BIT string) (Encoding::CompatibilityError)` require 'zip' module Gym From 3fa40ca3023a2ed5040d4e535572509cdaa25d50 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Fri, 18 Sep 2015 11:35:32 -0700 Subject: [PATCH 020/176] Added information abut the Xcode version to use --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 3112bd3..57c23bc 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,10 @@ That's all you need to build your application. If you want more control, here ar gym --workspace "Example.xcworkspace" --scheme "AppName" --clean +If you need to use a different xcode install, use xcode-select or define DEVELOPER_DIR: + + DEVELOPER_DIR=/Applications/Xcode6.2.app gym + For a list of all available parameters use gym --help From 15fbd007ffbb2ab0af83871572c49064921ef14f Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Fri, 18 Sep 2015 11:37:33 -0700 Subject: [PATCH 021/176] Fixed code style --- lib/gym/xcodebuild_fixes/swift_fix.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/xcodebuild_fixes/swift_fix.rb b/lib/gym/xcodebuild_fixes/swift_fix.rb index 2a59680..def9377 100644 --- a/lib/gym/xcodebuild_fixes/swift_fix.rb +++ b/lib/gym/xcodebuild_fixes/swift_fix.rb @@ -1,6 +1,6 @@ # encoding: utf-8 # from http://stackoverflow.com/a/9857493/445598 -# because of +# because of # `incompatible encoding regexp match (UTF-8 regexp with ASCII-8BIT string) (Encoding::CompatibilityError)` require 'zip' From e857d3f02a2adc8d82581cc7180ca0f69ffcf336 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Fri, 18 Sep 2015 12:37:25 -0700 Subject: [PATCH 022/176] Added test for XML config file for new Xcode 7 --- spec/package_command_generator_xcode7_spec.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/spec/package_command_generator_xcode7_spec.rb b/spec/package_command_generator_xcode7_spec.rb index ec03b41..701bcc0 100644 --- a/spec/package_command_generator_xcode7_spec.rb +++ b/spec/package_command_generator_xcode7_spec.rb @@ -13,5 +13,20 @@ "" ]) end + + it "generates a valid plist file we need" do + options = { project: "./examples/standard/Example.xcodeproj" } + Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) + + result = Gym::PackageCommandGeneratorXcode7.generate + config_path = Gym::PackageCommandGeneratorXcode7.config_path + + require 'plist' + expect(Plist.parse_xml(config_path)).to eq({ + 'method' => "app-store", + 'uploadBitcode' => false, + 'uploadSymbols' => true + }) + end end end From 4fbee87148d4ab3f6052891dfbf2896667358a7a Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Fri, 18 Sep 2015 12:38:53 -0700 Subject: [PATCH 023/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index 7f221a3..3edebfc 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "0.5.0" + VERSION = "0.6.0" DESCRIPTION = "Building your iOS apps has never been easier" end From cec3c0f69fd5437823f9975dcd095757eb2256a3 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Fri, 18 Sep 2015 12:39:26 -0700 Subject: [PATCH 024/176] Added quotes around env variable example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 57c23bc..6f0eb77 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ That's all you need to build your application. If you want more control, here ar If you need to use a different xcode install, use xcode-select or define DEVELOPER_DIR: - DEVELOPER_DIR=/Applications/Xcode6.2.app gym + DEVELOPER_DIR="/Applications/Xcode6.2.app" gym For a list of all available parameters use From 86dae668b5ac3d984e68b1c2c36828999e5909c1 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Fri, 18 Sep 2015 13:37:23 -0700 Subject: [PATCH 025/176] Changed from `upload` to `include` --- README.md | 2 +- lib/gym/generators/package_command_generator_xcode7.rb | 4 ++-- lib/gym/options.rb | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6f0eb77..bcccc7f 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ If you run into any issues, use the `verbose` mode to get more information To pass boolean parameters make sure to use `gym` like this: - gym --upload_bitcode true --upload_symbols true + gym --include_bitcode true --include_symbols true # Gymfile diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index 06b9123..5f0abae 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -50,8 +50,8 @@ def config_path def config_content require 'plist' - symbols = (Gym.config[:upload_symbols] ? true : false) - bitcode = (Gym.config[:upload_bitcode] ? true : false) + symbols = (Gym.config[:include_symbols] ? true : false) + bitcode = (Gym.config[:include_bitcode] ? true : false) { method: 'app-store', diff --git a/lib/gym/options.rb b/lib/gym/options.rb index 287a155..922d404 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -101,14 +101,14 @@ def self.plain_options verify_block: proc do |value| raise "File not found at path '#{File.expand_path(value)}'".red unless File.exist?(value) end), - FastlaneCore::ConfigItem.new(key: :upload_symbols, + FastlaneCore::ConfigItem.new(key: :include_symbols, short_option: "-m", - env_name: "GYM_UPLOAD_SYMBOLS", + env_name: "GYM_INCLUDE_SYMBOLS", description: "Should the ipa file include symbols?", default_value: true), - FastlaneCore::ConfigItem.new(key: :upload_bitcode, + FastlaneCore::ConfigItem.new(key: :include_bitcode, short_option: "-z", - env_name: "GYM_UPLOAD_BITCODE", + env_name: "GYM_INCLUDE_BITCODE", description: "Should the ipa include bitcode?", default_value: false), FastlaneCore::ConfigItem.new(key: :provisioning_profile_path, From d8513bc1675d687b2d1afe06eebb0d7a0c8524a4 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Fri, 18 Sep 2015 13:37:46 -0700 Subject: [PATCH 026/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index 3edebfc..8678f8a 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "0.6.0" + VERSION = "0.6.1" DESCRIPTION = "Building your iOS apps has never been easier" end From bedd24bedfdda558fe12044700bc81689ce01182 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Fri, 18 Sep 2015 14:57:40 -0700 Subject: [PATCH 027/176] Improved ipa file detection with Xcode 7 Added new tests --- lib/gym/generators/package_command_generator.rb | 1 + .../package_command_generator_xcode7.rb | 15 +++++++++++++-- spec/package_command_generator_xcode7_spec.rb | 10 +++++++++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/lib/gym/generators/package_command_generator.rb b/lib/gym/generators/package_command_generator.rb index fa20b95..e1b61c0 100644 --- a/lib/gym/generators/package_command_generator.rb +++ b/lib/gym/generators/package_command_generator.rb @@ -15,6 +15,7 @@ def appfile_path generator.appfile_path end + # The path in which the ipa file will be available after executing the command def ipa_path generator.ipa_path end diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index 5f0abae..b033b84 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -22,7 +22,7 @@ def options options << "-exportOptionsPlist '#{config_path}'" options << "-archivePath '#{BuildCommandGenerator.archive_path}'" - options << "-exportPath '#{BuildCommandGenerator.build_path}'" # we move it once the binary is finished + options << "-exportPath '#{temporary_output_path}'" options end @@ -31,8 +31,19 @@ def pipe [""] end + # We export the ipa into this directory, as we can't specify the ipa file directly + def temporary_output_path + @temporary_output_path ||= File.join("/tmp", Time.now.to_i.to_s) + end + def ipa_path - File.join(BuildCommandGenerator.build_path, "#{Gym.config[:output_name]}.ipa") + unless @ipa_path + path = Dir[File.join(temporary_output_path, "*.ipa")].last + + @ipa_path = File.join(temporary_output_path, "#{Gym.config[:output_name]}.ipa") + FileUtils.mv(path, @ipa_path) + end + @ipa_path end # The path the the dsym file for this app. Might be nil diff --git a/spec/package_command_generator_xcode7_spec.rb b/spec/package_command_generator_xcode7_spec.rb index 701bcc0..84eb1f2 100644 --- a/spec/package_command_generator_xcode7_spec.rb +++ b/spec/package_command_generator_xcode7_spec.rb @@ -9,7 +9,7 @@ "/usr/bin/xcrun xcodebuild -exportArchive", "-exportOptionsPlist '#{Gym::PackageCommandGeneratorXcode7.config_path}'", "-archivePath '#{Gym::BuildCommandGenerator.archive_path}'", - "-exportPath '#{Gym::BuildCommandGenerator.build_path}'", + "-exportPath '#{Gym::PackageCommandGeneratorXcode7.temporary_output_path}'", "" ]) end @@ -28,5 +28,13 @@ 'uploadSymbols' => true }) end + + it "uses a temporary folder to store the resulting ipa file" do + options = { project: "./examples/standard/Example.xcodeproj" } + Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) + + result = Gym::PackageCommandGeneratorXcode7.generate + expect(Gym::PackageCommandGeneratorXcode7.temporary_output_path).to match(%r{/tmp/\d+}) + end end end From abf253b7a314af5c446b57156e1a5aa2296840a1 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Fri, 18 Sep 2015 14:57:48 -0700 Subject: [PATCH 028/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index 8678f8a..ff6782c 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "0.6.1" + VERSION = "0.6.2" DESCRIPTION = "Building your iOS apps has never been easier" end From 5743b53283e052af5eefaaf1ca4110c86f7cdf94 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Fri, 18 Sep 2015 15:41:34 -0700 Subject: [PATCH 029/176] Fixed parameter types for `include` parameters --- lib/gym/options.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/gym/options.rb b/lib/gym/options.rb index 922d404..93a0d65 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -105,12 +105,14 @@ def self.plain_options short_option: "-m", env_name: "GYM_INCLUDE_SYMBOLS", description: "Should the ipa file include symbols?", - default_value: true), + default_value: true, + is_string: false), FastlaneCore::ConfigItem.new(key: :include_bitcode, short_option: "-z", env_name: "GYM_INCLUDE_BITCODE", description: "Should the ipa include bitcode?", - default_value: false), + default_value: false, + is_string: false), FastlaneCore::ConfigItem.new(key: :provisioning_profile_path, short_option: "-e", env_name: "GYM_PROVISIONING_PROFILE_PATH", From 486c60448ab0debc8377f8c22d9b2055291b2ae1 Mon Sep 17 00:00:00 2001 From: KrauseFx Date: Sat, 19 Sep 2015 20:45:35 -0700 Subject: [PATCH 030/176] Added new `export_method` option for non appstore builds --- .../package_command_generator_xcode7.rb | 17 +++++++++-------- lib/gym/options.rb | 10 ++++++++++ spec/package_command_generator_xcode7_spec.rb | 13 +++++++++++++ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index b033b84..2cbb68b 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -61,14 +61,15 @@ def config_path def config_content require 'plist' - symbols = (Gym.config[:include_symbols] ? true : false) - bitcode = (Gym.config[:include_bitcode] ? true : false) - - { - method: 'app-store', - uploadSymbols: symbols, - uploadBitcode: bitcode - }.to_plist + + 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) + end + + hash.to_plist end end end diff --git a/lib/gym/options.rb b/lib/gym/options.rb index 93a0d65..bad8cbe 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -113,6 +113,16 @@ def self.plain_options description: "Should the ipa include bitcode?", default_value: false, is_string: false), + FastlaneCore::ConfigItem.new(key: :export_method, + short_option: "-j", + env_name: "GYM_EXPORT_METHOD", + description: "How should gym export the archive?", + default_value: "app-store", + is_string: true, + verify_block: proc do |value| + av = %w(app-store ad-hoc package enterprise development developer-id) + raise "Unsupported export_method, must be: #{av}" unless av.include?(value) + end), FastlaneCore::ConfigItem.new(key: :provisioning_profile_path, short_option: "-e", env_name: "GYM_PROVISIONING_PROFILE_PATH", diff --git a/spec/package_command_generator_xcode7_spec.rb b/spec/package_command_generator_xcode7_spec.rb index 84eb1f2..c49aa9b 100644 --- a/spec/package_command_generator_xcode7_spec.rb +++ b/spec/package_command_generator_xcode7_spec.rb @@ -29,6 +29,19 @@ }) end + it "doesn't store bitcode/symbols information for non app-store builds" do + options = { project: "./examples/standard/Example.xcodeproj", export_method: 'ad-hoc' } + Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) + + result = Gym::PackageCommandGeneratorXcode7.generate + config_path = Gym::PackageCommandGeneratorXcode7.config_path + + require 'plist' + expect(Plist.parse_xml(config_path)).to eq({ + 'method' => "ad-hoc" + }) + end + it "uses a temporary folder to store the resulting ipa file" do options = { project: "./examples/standard/Example.xcodeproj" } Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) From 44d9b5ce12f281c60a4a76f2aa1bce791b7e9e2e Mon Sep 17 00:00:00 2001 From: KrauseFx Date: Sat, 19 Sep 2015 20:50:54 -0700 Subject: [PATCH 031/176] Improved output when using legacy parameters --- .../package_command_generator_xcode7.rb | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index 2cbb68b..829839e 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -3,10 +3,7 @@ module Gym class PackageCommandGeneratorXcode7 class << self def generate - if Gym.config[:provisioning_profile_path] - Helper.log.info "You're using Xcode 7, the `provisioning_profile_path` value will be ignored".yellow - Helper.log.info "Please follow the Code Signing Guide: https://github.com/KrauseFx/fastlane/blob/master/docs/CodeSigning.md".yellow - end + print_legacy_information parts = ["/usr/bin/xcrun xcodebuild -exportArchive"] parts += options @@ -71,6 +68,18 @@ def config_content hash.to_plist end + + def print_legacy_information + if Gym.config[:provisioning_profile_path] + Helper.log.info "You're using Xcode 7, the `provisioning_profile_path` value will be ignored".yellow + Helper.log.info "Please follow the Code Signing Guide: https://github.com/KrauseFx/fastlane/blob/master/docs/CodeSigning.md".yellow + end + + if Gym.config[:codesigning_identity] + Helper.log.info "You're using Xcode 7, the `codesigning_identity` value will be ignored".yellow + Helper.log.info "Please follow the Code Signing Guide: https://github.com/KrauseFx/fastlane/blob/master/docs/CodeSigning.md".yellow + end + end end end end From 288e998cc768da1bc15998434aca1467a22f354e Mon Sep 17 00:00:00 2001 From: KrauseFx Date: Sat, 19 Sep 2015 20:54:37 -0700 Subject: [PATCH 032/176] Added support for code signing identity option while building --- lib/gym/generators/build_command_generator.rb | 4 +++- lib/gym/generators/package_command_generator_xcode7.rb | 5 ----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/gym/generators/build_command_generator.rb b/lib/gym/generators/build_command_generator.rb index e1ef677..f58c3db 100644 --- a/lib/gym/generators/build_command_generator.rb +++ b/lib/gym/generators/build_command_generator.rb @@ -57,7 +57,9 @@ def actions end def suffix - [] + suffix = [] + suffix << "CODE_SIGN_IDENTITY='#{Gym.config[:codesigning_identity]}'" if Gym.config[:codesigning_identity] + suffix end def pipe diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index 829839e..903484f 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -74,11 +74,6 @@ def print_legacy_information Helper.log.info "You're using Xcode 7, the `provisioning_profile_path` value will be ignored".yellow Helper.log.info "Please follow the Code Signing Guide: https://github.com/KrauseFx/fastlane/blob/master/docs/CodeSigning.md".yellow end - - if Gym.config[:codesigning_identity] - Helper.log.info "You're using Xcode 7, the `codesigning_identity` value will be ignored".yellow - Helper.log.info "Please follow the Code Signing Guide: https://github.com/KrauseFx/fastlane/blob/master/docs/CodeSigning.md".yellow - end end end end From 5bbc95c32e609c02d277b83d7836c50d2969fd0c Mon Sep 17 00:00:00 2001 From: KrauseFx Date: Sat, 19 Sep 2015 20:54:40 -0700 Subject: [PATCH 033/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index ff6782c..b491321 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "0.6.2" + VERSION = "0.7.0" DESCRIPTION = "Building your iOS apps has never been easier" end From 705f395de19d5e5579aaa8fd0d09e4fec818b644 Mon Sep 17 00:00:00 2001 From: KrauseFx Date: Sat, 19 Sep 2015 21:03:09 -0700 Subject: [PATCH 034/176] Fixed bug where `gym` tries to move a file which we don't need --- lib/gym/generators/package_command_generator_xcode7.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index 903484f..7eec9d5 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -38,7 +38,7 @@ def ipa_path path = Dir[File.join(temporary_output_path, "*.ipa")].last @ipa_path = File.join(temporary_output_path, "#{Gym.config[:output_name]}.ipa") - FileUtils.mv(path, @ipa_path) + FileUtils.mv(path, @ipa_path) if File.expand_path(path) != File.expand_path(@ipa_path) end @ipa_path end From 29ceb64c310cae588139909fa689079f2291267b Mon Sep 17 00:00:00 2001 From: Nicolae Ghimbovschi Date: Sun, 20 Sep 2015 11:16:56 +0300 Subject: [PATCH 035/176] Generate xcodebuild.log for more details --- lib/gym/generators/build_command_generator.rb | 2 +- spec/build_command_generator_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/gym/generators/build_command_generator.rb b/lib/gym/generators/build_command_generator.rb index f58c3db..99f3d95 100644 --- a/lib/gym/generators/build_command_generator.rb +++ b/lib/gym/generators/build_command_generator.rb @@ -63,7 +63,7 @@ def suffix end def pipe - ["| xcpretty"] + ["| tee 'xcodebuild-#{Gym.config[:scheme]}.log' | xcpretty"] end # The path to set the Derived Data to diff --git a/spec/build_command_generator_spec.rb b/spec/build_command_generator_spec.rb index dec9edf..c2a9287 100644 --- a/spec/build_command_generator_spec.rb +++ b/spec/build_command_generator_spec.rb @@ -26,7 +26,7 @@ "-archivePath '#{Gym::BuildCommandGenerator.archive_path}'", "DEBUG=1 BUNDLE_NAME=Example\\ App", :archive, - "| xcpretty" + "| tee 'xcodebuild-Example.log' | xcpretty" ]) end @@ -47,7 +47,7 @@ "-destination 'generic/platform=iOS'", "-archivePath '#{Gym::BuildCommandGenerator.archive_path}'", :archive, - "| xcpretty" + "| tee 'xcodebuild-Example.log' | xcpretty" ]) end From 7afcf58581b510f580092b62374aaa913f5a198c Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Mon, 21 Sep 2015 09:29:35 -0700 Subject: [PATCH 036/176] Added Xcode 7 information --- README.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bcccc7f..759b4db 100644 --- a/README.md +++ b/README.md @@ -190,9 +190,25 @@ xcodebuild -scheme 'Example' \ archive | xcpretty ``` - After building the archive it is being checked by `gym`. If it's valid, it gets packaged up and signed into an `ipa` file. +`gym` automatically chooses a different packaging method depending on the version of Xcode you're using. + +### Xcode 7 and above + +``` +/usr/bin/xcrun xcodebuild -exportArchive \ +-exportOptionsPlist '/tmp/gym_config_1442852529.plist' \ +-archivePath '/Users/fkrause/Library/Developer/Xcode/Archives/2015-09-21/App 2015-09-21 09.21.56.xcarchive' \ +-exportPath '/tmp/1442852529' +``` + +`gym` makes use of the new Xcode 7 API which allows us to specify the export options using a `plist` file. You can find more information about the available options by running `xcodebuild --help`. + +Using this method there are no workarounds for WatchKit or Swift required, as it uses the same technique Xcode uses when exporting your binary. + +### Xcode 6 and below + ``` /usr/bin/xcrun /path/to/PackageApplication4Gym -v \ '/Users/felixkrause/Library/Developer/Xcode/Archives/2015-08-11/ExampleProductName 2015-08-11 18.15.30.xcarchive/Products/Applications/name.app' -o \ From 31e0f1b69b7ceaad0321cce209d61afe21fee70a Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Mon, 21 Sep 2015 09:54:52 -0700 Subject: [PATCH 037/176] Don't delete patched file after running `gym` Fixes https://github.com/fastlane/gym/issues/95 --- lib/gym/runner.rb | 1 - lib/gym/xcodebuild_fixes/package_application_fix.rb | 13 ++----------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb index 85977ae..a9ba09d 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -67,7 +67,6 @@ def fix_package return unless Xcode.pre_7? Gym::XcodebuildFixes.swift_library_fix Gym::XcodebuildFixes.watchkit_fix - Gym::XcodebuildFixes.clear_patched_package_application end # Builds the app and prepares the archive diff --git a/lib/gym/xcodebuild_fixes/package_application_fix.rb b/lib/gym/xcodebuild_fixes/package_application_fix.rb index 4bcaa0a..61d7c95 100644 --- a/lib/gym/xcodebuild_fixes/package_application_fix.rb +++ b/lib/gym/xcodebuild_fixes/package_application_fix.rb @@ -7,8 +7,8 @@ def patch_package_application # Initialization @patched_package_application_path = File.join("/tmp", "PackageApplication4Gym") - # Remove any previous patched PackageApplication - FileUtils.rm @patched_package_application_path if File.exist?(@patched_package_application_path) + + return if File.exist?(@patched_package_application_path) Dir.mktmpdir do |tmpdir| # Check current PackageApplication MD5 @@ -42,15 +42,6 @@ def patch_package_application return @patched_package_application_path # Return path to the patched PackageApplication end - - # Remove the patched script after it was used - def clear_patched_package_application - if @patched_package_application_path and File.exist?(@patched_package_application_path) - Helper.log.debug "Removing patched PackageApplication file at path '#{@patched_package_application_path}'" if $verbose - # force in the unlikely event that the file was deleted (e.g. by a concurrent job) - FileUtils.rm @patched_package_application_path, force: true - end - end end end end From 2a0c1732ba18888b05bd495ac490c726be500920 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Mon, 21 Sep 2015 10:06:10 -0700 Subject: [PATCH 038/176] Fixed not returning the patched path if it already exists --- lib/gym/xcodebuild_fixes/package_application_fix.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/xcodebuild_fixes/package_application_fix.rb b/lib/gym/xcodebuild_fixes/package_application_fix.rb index 61d7c95..681a82e 100644 --- a/lib/gym/xcodebuild_fixes/package_application_fix.rb +++ b/lib/gym/xcodebuild_fixes/package_application_fix.rb @@ -8,7 +8,7 @@ def patch_package_application # Initialization @patched_package_application_path = File.join("/tmp", "PackageApplication4Gym") - return if File.exist?(@patched_package_application_path) + return @patched_package_application_path if File.exist?(@patched_package_application_path) Dir.mktmpdir do |tmpdir| # Check current PackageApplication MD5 From d8629e975c7d6e1b1d6af2cef3cfff991bee0f00 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Mon, 21 Sep 2015 12:25:15 -0700 Subject: [PATCH 039/176] Added temporary use_legacy_build_api option --- lib/gym/generators/package_command_generator.rb | 6 +++++- lib/gym/options.rb | 12 ++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/gym/generators/package_command_generator.rb b/lib/gym/generators/package_command_generator.rb index e1b61c0..5a72e22 100644 --- a/lib/gym/generators/package_command_generator.rb +++ b/lib/gym/generators/package_command_generator.rb @@ -28,7 +28,11 @@ def dsym_path # The generator we need to use for the currently used Xcode version def generator - Xcode.pre_7? ? PackageCommandGeneratorLegacy : PackageCommandGeneratorXcode7 + if Xcode.pre_7? and !Gym.config[:use_legacy_build_api] + PackageCommandGeneratorLegacy + else + PackageCommandGeneratorXcode7 + end end end end diff --git a/lib/gym/options.rb b/lib/gym/options.rb index bad8cbe..34ffbde 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -1,6 +1,7 @@ require "fastlane_core" require "credentials_manager" +# rubocop:disable Metrics/AbcSize module Gym class Options def self.available_options @@ -113,6 +114,16 @@ def self.plain_options description: "Should the ipa include bitcode?", default_value: false, is_string: false), + 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", + default_value: false, + is_string: false, + verify_block: proc do |value| + if value + Helper.log.info "Using legacy build system - waiting for radar to be fixed: https://openradar.appspot.com/radar?id=4952000420642816".red + end + end), FastlaneCore::ConfigItem.new(key: :export_method, short_option: "-j", env_name: "GYM_EXPORT_METHOD", @@ -136,3 +147,4 @@ def self.plain_options end end end +# rubocop:enable Metrics/AbcSize From 2e17e8506f8ac1cd34c7b03dc1b9a6dda7489a7e Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Mon, 21 Sep 2015 12:25:38 -0700 Subject: [PATCH 040/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index b491321..464c818 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "0.7.0" + VERSION = "0.7.1" DESCRIPTION = "Building your iOS apps has never been easier" end From a4d61829da26effbdbbce9adec58945d664c39dd Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Mon, 21 Sep 2015 12:48:36 -0700 Subject: [PATCH 041/176] Added auto-detect of the export issue with instructions how to solve it --- lib/gym/error_handler.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/gym/error_handler.rb b/lib/gym/error_handler.rb index 767d977..78d7828 100644 --- a/lib/gym/error_handler.rb +++ b/lib/gym/error_handler.rb @@ -80,6 +80,10 @@ def handle_package_error(output) when /Codesign check fails/ print "A general code signing error occurred. Make sure you passed a valid" print "provisioning profile and code signing identity." + when /expected one of \{\}/ + print "It seems like you ran into this radar" + print "https://openradar.appspot.com/radar?id=4952000420642816" + print "You can temporary use the :use_legacy_build_api option to get the build to work again" end raise "Error packaging up the application".red end From a2f1fbcfb7847b7a6029530d7de3100060665c01 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Mon, 21 Sep 2015 13:58:35 -0700 Subject: [PATCH 042/176] Fixed use_legacy_build_api flag --- lib/gym.rb | 1 - lib/gym/generators/package_command_generator.rb | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/gym.rb b/lib/gym.rb index ba75c62..64e6aa4 100644 --- a/lib/gym.rb +++ b/lib/gym.rb @@ -29,7 +29,6 @@ def gymfile_name end def init_libs - return unless Xcode.pre_7? # Import all the fixes require 'gym/xcodebuild_fixes/swift_fix' require 'gym/xcodebuild_fixes/watchkit_fix' diff --git a/lib/gym/generators/package_command_generator.rb b/lib/gym/generators/package_command_generator.rb index 5a72e22..27c870b 100644 --- a/lib/gym/generators/package_command_generator.rb +++ b/lib/gym/generators/package_command_generator.rb @@ -28,7 +28,7 @@ def dsym_path # The generator we need to use for the currently used Xcode version def generator - if Xcode.pre_7? and !Gym.config[:use_legacy_build_api] + if Xcode.pre_7? or Gym.config[:use_legacy_build_api] PackageCommandGeneratorLegacy else PackageCommandGeneratorXcode7 From 3901023dd0fbced2cc8514f1a7857f386d79940e Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Mon, 21 Sep 2015 13:58:58 -0700 Subject: [PATCH 043/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index 464c818..5651928 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "0.7.1" + VERSION = "0.7.2" DESCRIPTION = "Building your iOS apps has never been easier" end From bfebecbdcc473dc644456af9a1628cef5954b967 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Mon, 21 Sep 2015 15:00:50 -0700 Subject: [PATCH 044/176] Updated bundle identifiers of example projects --- .../WatchKit.xcodeproj/project.pbxproj | 9 +- .../xcshareddata/WatchKit.xcscmblueprint | 30 ++++ ...ication - WatchKit WatchKit 1 App.xcscheme | 145 ++++++++++++++++++ .../WatchKit WatchKit 1 App.xcscheme | 141 +++++++++++++++++ .../xcschemes/WatchKit.xcscheme | 91 +++++++++++ .../xcschemes/xcschememanagement.plist | 37 +++++ .../Example.xcodeproj/project.pbxproj | 8 +- .../xcschemes/xcschememanagement.plist | 24 +++ .../xcshareddata/Example.xcscmblueprint | 30 ++++ .../xcschemes/xcschememanagement.plist | 19 +++ .../Example.xcodeproj/project.pbxproj | 8 +- .../xcshareddata/Example.xcscmblueprint | 30 ++++ .../xcschemes/xcschememanagement.plist | 24 +++ .../Example.xcodeproj/project.pbxproj | 8 +- .../xcshareddata/Example.xcscmblueprint | 30 ++++ .../xcschemes/xcschememanagement.plist | 24 +++ .../today/Example.xcodeproj/project.pbxproj | 12 +- .../xcshareddata/Example.xcscmblueprint | 30 ++++ .../xcschemes/xcschememanagement.plist | 24 +++ .../With Spaces.xcodeproj/project.pbxproj | 9 +- .../xcshareddata/With Spaces.xcscmblueprint | 30 ++++ ...tion - With Spaces WatchKit 1 App.xcscheme | 117 ++++++++++++++ .../With Spaces WatchKit 1 App.xcscheme | 113 ++++++++++++++ .../xcschemes/With Spaces.xcscheme | 91 +++++++++++ .../xcschemes/xcschememanagement.plist | 37 +++++ 25 files changed, 1097 insertions(+), 24 deletions(-) create mode 100644 examples/WatchKit/WatchKit.xcodeproj/project.xcworkspace/xcshareddata/WatchKit.xcscmblueprint create mode 100644 examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/Notification - WatchKit WatchKit 1 App.xcscheme create mode 100644 examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/WatchKit WatchKit 1 App.xcscheme create mode 100644 examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/WatchKit.xcscheme create mode 100644 examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 examples/cocoapods/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 examples/cocoapods/Example.xcworkspace/xcshareddata/Example.xcscmblueprint create mode 100644 examples/cocoapods/Pods/Pods.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 examples/multipleSchemes/Example.xcodeproj/project.xcworkspace/xcshareddata/Example.xcscmblueprint create mode 100644 examples/multipleSchemes/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 examples/standard/Example.xcodeproj/project.xcworkspace/xcshareddata/Example.xcscmblueprint create mode 100644 examples/standard/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 examples/today/Example.xcodeproj/project.xcworkspace/xcshareddata/Example.xcscmblueprint create mode 100644 examples/today/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 examples/with spaces/With Spaces.xcodeproj/project.xcworkspace/xcshareddata/With Spaces.xcscmblueprint create mode 100644 examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/Notification - With Spaces WatchKit 1 App.xcscheme create mode 100644 examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/With Spaces WatchKit 1 App.xcscheme create mode 100644 examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/With Spaces.xcscheme create mode 100644 examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist diff --git a/examples/WatchKit/WatchKit.xcodeproj/project.pbxproj b/examples/WatchKit/WatchKit.xcodeproj/project.pbxproj index 6db1529..b593aec 100644 --- a/examples/WatchKit/WatchKit.xcodeproj/project.pbxproj +++ b/examples/WatchKit/WatchKit.xcodeproj/project.pbxproj @@ -445,9 +445,10 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Distribution"; INFOPLIST_FILE = WatchKit/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.WatchKit; + PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -456,9 +457,10 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Distribution"; INFOPLIST_FILE = WatchKit/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.WatchKit; + PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -534,6 +536,7 @@ CA461FED1B84738C0037A37B /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; CA46200F1B8473930037A37B /* Build configuration list for PBXNativeTarget "WatchKit WatchKit 1 App" */ = { isa = XCConfigurationList; @@ -542,6 +545,7 @@ CA4620111B8473930037A37B /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; CA4620121B8473930037A37B /* Build configuration list for PBXNativeTarget "WatchKit WatchKit 1 Extension" */ = { isa = XCConfigurationList; @@ -550,6 +554,7 @@ CA4620141B8473930037A37B /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/examples/WatchKit/WatchKit.xcodeproj/project.xcworkspace/xcshareddata/WatchKit.xcscmblueprint b/examples/WatchKit/WatchKit.xcodeproj/project.xcworkspace/xcshareddata/WatchKit.xcscmblueprint new file mode 100644 index 0000000..229e29b --- /dev/null +++ b/examples/WatchKit/WatchKit.xcodeproj/project.xcworkspace/xcshareddata/WatchKit.xcscmblueprint @@ -0,0 +1,30 @@ +{ + "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D", + "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { + + }, + "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { + "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : 0, + "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : 0 + }, + "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "384272E4-7D16-4230-B482-A546F8BD54A7", + "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { + "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : "gym\/", + "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : "" + }, + "DVTSourceControlWorkspaceBlueprintNameKey" : "WatchKit", + "DVTSourceControlWorkspaceBlueprintVersion" : 204, + "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "examples\/WatchKit\/WatchKit.xcodeproj", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/fastlane\/countdown", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" + }, + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/krausefx\/gym", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" + } + ] +} \ No newline at end of file diff --git a/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/Notification - WatchKit WatchKit 1 App.xcscheme b/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/Notification - WatchKit WatchKit 1 App.xcscheme new file mode 100644 index 0000000..9a91d5e --- /dev/null +++ b/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/Notification - WatchKit WatchKit 1 App.xcscheme @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/WatchKit WatchKit 1 App.xcscheme b/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/WatchKit WatchKit 1 App.xcscheme new file mode 100644 index 0000000..91825a7 --- /dev/null +++ b/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/WatchKit WatchKit 1 App.xcscheme @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/WatchKit.xcscheme b/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/WatchKit.xcscheme new file mode 100644 index 0000000..257cf89 --- /dev/null +++ b/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/WatchKit.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist b/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..708572f --- /dev/null +++ b/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,37 @@ + + + + + SchemeUserState + + Notification - WatchKit WatchKit 1 App.xcscheme + + orderHint + 2 + + WatchKit WatchKit 1 App.xcscheme + + orderHint + 1 + + WatchKit.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + CA461FD31B84738C0037A37B + + primary + + + CA4620001B8473930037A37B + + primary + + + + + diff --git a/examples/cocoapods/Example.xcodeproj/project.pbxproj b/examples/cocoapods/Example.xcodeproj/project.pbxproj index 7431d27..b0026d0 100644 --- a/examples/cocoapods/Example.xcodeproj/project.pbxproj +++ b/examples/cocoapods/Example.xcodeproj/project.pbxproj @@ -471,10 +471,10 @@ baseConfigurationReference = ACCA4067A20AE82A13A55B4D /* Pods-Example.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "iPhone Distribution: SunApps GmbH (5A997XSHK2)"; + CODE_SIGN_IDENTITY = "iPhone Distribution"; INFOPLIST_FILE = Example/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.9; + PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app; PRODUCT_NAME = "$(TARGET_NAME)ProductName"; PROVISIONING_PROFILE = ""; }; @@ -485,10 +485,10 @@ baseConfigurationReference = 7F17E2E4DB202336112485AA /* Pods-Example.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "iPhone Distribution: SunApps GmbH (5A997XSHK2)"; + CODE_SIGN_IDENTITY = "iPhone Distribution"; INFOPLIST_FILE = Example/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.9; + PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app; PRODUCT_NAME = "$(TARGET_NAME)ProductName"; PROVISIONING_PROFILE = ""; }; diff --git a/examples/cocoapods/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist b/examples/cocoapods/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..642a1dd --- /dev/null +++ b/examples/cocoapods/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,24 @@ + + + + + SuppressBuildableAutocreation + + CAC1E1971B6A326100A8B23A + + primary + + + CAC1E1B01B6A326100A8B23A + + primary + + + CAC1E1BB1B6A326200A8B23A + + primary + + + + + diff --git a/examples/cocoapods/Example.xcworkspace/xcshareddata/Example.xcscmblueprint b/examples/cocoapods/Example.xcworkspace/xcshareddata/Example.xcscmblueprint new file mode 100644 index 0000000..818935e --- /dev/null +++ b/examples/cocoapods/Example.xcworkspace/xcshareddata/Example.xcscmblueprint @@ -0,0 +1,30 @@ +{ + "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D", + "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { + + }, + "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { + "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : 0, + "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : 0 + }, + "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "B0B54A05-D112-479E-84BB-D9FA30D6062E", + "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { + "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : "gym\/", + "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : "" + }, + "DVTSourceControlWorkspaceBlueprintNameKey" : "Example", + "DVTSourceControlWorkspaceBlueprintVersion" : 204, + "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "examples\/cocoapods\/Example.xcworkspace", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/fastlane\/countdown", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" + }, + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/krausefx\/gym", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" + } + ] +} \ No newline at end of file diff --git a/examples/cocoapods/Pods/Pods.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist b/examples/cocoapods/Pods/Pods.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..c2d7cb5 --- /dev/null +++ b/examples/cocoapods/Pods/Pods.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,19 @@ + + + + + SuppressBuildableAutocreation + + 8E46265702A9FA999EF6AC4B2576B14F + + primary + + + FB2714B8EF1C625F78C19E0D0EF6E5A8 + + primary + + + + + diff --git a/examples/multipleSchemes/Example.xcodeproj/project.pbxproj b/examples/multipleSchemes/Example.xcodeproj/project.pbxproj index f4ca25b..ef00a57 100644 --- a/examples/multipleSchemes/Example.xcodeproj/project.pbxproj +++ b/examples/multipleSchemes/Example.xcodeproj/project.pbxproj @@ -411,10 +411,10 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "iPhone Distribution: SunApps GmbH (5A997XSHK2)"; + CODE_SIGN_IDENTITY = "iPhone Distribution"; INFOPLIST_FILE = Example/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.9; + PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app; PRODUCT_NAME = "$(TARGET_NAME)ProductName"; PROVISIONING_PROFILE = ""; }; @@ -424,10 +424,10 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "iPhone Distribution: SunApps GmbH (5A997XSHK2)"; + CODE_SIGN_IDENTITY = "iPhone Distribution"; INFOPLIST_FILE = Example/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.9; + PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app; PRODUCT_NAME = "$(TARGET_NAME)ProductName"; PROVISIONING_PROFILE = ""; }; diff --git a/examples/multipleSchemes/Example.xcodeproj/project.xcworkspace/xcshareddata/Example.xcscmblueprint b/examples/multipleSchemes/Example.xcodeproj/project.xcworkspace/xcshareddata/Example.xcscmblueprint new file mode 100644 index 0000000..1255ee2 --- /dev/null +++ b/examples/multipleSchemes/Example.xcodeproj/project.xcworkspace/xcshareddata/Example.xcscmblueprint @@ -0,0 +1,30 @@ +{ + "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D", + "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { + + }, + "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { + "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : 0, + "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : 0 + }, + "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "FF59B69C-C47E-4AB7-80E3-C9D623C3D37B", + "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { + "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : "gym\/", + "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : "" + }, + "DVTSourceControlWorkspaceBlueprintNameKey" : "Example", + "DVTSourceControlWorkspaceBlueprintVersion" : 204, + "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "examples\/multipleSchemes\/Example.xcodeproj", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/fastlane\/countdown", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" + }, + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/krausefx\/gym", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" + } + ] +} \ No newline at end of file diff --git a/examples/multipleSchemes/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist b/examples/multipleSchemes/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..642a1dd --- /dev/null +++ b/examples/multipleSchemes/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,24 @@ + + + + + SuppressBuildableAutocreation + + CAC1E1971B6A326100A8B23A + + primary + + + CAC1E1B01B6A326100A8B23A + + primary + + + CAC1E1BB1B6A326200A8B23A + + primary + + + + + diff --git a/examples/standard/Example.xcodeproj/project.pbxproj b/examples/standard/Example.xcodeproj/project.pbxproj index f4ca25b..ef00a57 100644 --- a/examples/standard/Example.xcodeproj/project.pbxproj +++ b/examples/standard/Example.xcodeproj/project.pbxproj @@ -411,10 +411,10 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "iPhone Distribution: SunApps GmbH (5A997XSHK2)"; + CODE_SIGN_IDENTITY = "iPhone Distribution"; INFOPLIST_FILE = Example/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.9; + PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app; PRODUCT_NAME = "$(TARGET_NAME)ProductName"; PROVISIONING_PROFILE = ""; }; @@ -424,10 +424,10 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "iPhone Distribution: SunApps GmbH (5A997XSHK2)"; + CODE_SIGN_IDENTITY = "iPhone Distribution"; INFOPLIST_FILE = Example/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.9; + PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app; PRODUCT_NAME = "$(TARGET_NAME)ProductName"; PROVISIONING_PROFILE = ""; }; diff --git a/examples/standard/Example.xcodeproj/project.xcworkspace/xcshareddata/Example.xcscmblueprint b/examples/standard/Example.xcodeproj/project.xcworkspace/xcshareddata/Example.xcscmblueprint new file mode 100644 index 0000000..4f6a956 --- /dev/null +++ b/examples/standard/Example.xcodeproj/project.xcworkspace/xcshareddata/Example.xcscmblueprint @@ -0,0 +1,30 @@ +{ + "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D", + "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { + + }, + "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { + "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : 0, + "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : 0 + }, + "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "F3078953-4758-46C3-AC3D-87FEF88956AF", + "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { + "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : "gym\/", + "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : "" + }, + "DVTSourceControlWorkspaceBlueprintNameKey" : "Example", + "DVTSourceControlWorkspaceBlueprintVersion" : 204, + "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "examples\/standard\/Example.xcodeproj", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/fastlane\/countdown", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" + }, + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/krausefx\/gym", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" + } + ] +} \ No newline at end of file diff --git a/examples/standard/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist b/examples/standard/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..642a1dd --- /dev/null +++ b/examples/standard/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,24 @@ + + + + + SuppressBuildableAutocreation + + CAC1E1971B6A326100A8B23A + + primary + + + CAC1E1B01B6A326100A8B23A + + primary + + + CAC1E1BB1B6A326200A8B23A + + primary + + + + + diff --git a/examples/today/Example.xcodeproj/project.pbxproj b/examples/today/Example.xcodeproj/project.pbxproj index 17ca18f..ef00a57 100644 --- a/examples/today/Example.xcodeproj/project.pbxproj +++ b/examples/today/Example.xcodeproj/project.pbxproj @@ -411,12 +411,12 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "iPhone Distribution: SunApps GmbH (5A997XSHK2)"; + CODE_SIGN_IDENTITY = "iPhone Distribution"; INFOPLIST_FILE = Example/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.9; + PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app; PRODUCT_NAME = "$(TARGET_NAME)ProductName"; - PROVISIONING_PROFILE = "067a4fdf-f03d-4fb8-8dc9-f9dfb6dbd"; + PROVISIONING_PROFILE = ""; }; name = Debug; }; @@ -424,12 +424,12 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "iPhone Distribution: SunApps GmbH (5A997XSHK2)"; + CODE_SIGN_IDENTITY = "iPhone Distribution"; INFOPLIST_FILE = Example/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.9; + PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app; PRODUCT_NAME = "$(TARGET_NAME)ProductName"; - PROVISIONING_PROFILE = "067a4fdf-f03d-4fb8-8dc9-f9dfb6dbd"; + PROVISIONING_PROFILE = ""; }; name = Release; }; diff --git a/examples/today/Example.xcodeproj/project.xcworkspace/xcshareddata/Example.xcscmblueprint b/examples/today/Example.xcodeproj/project.xcworkspace/xcshareddata/Example.xcscmblueprint new file mode 100644 index 0000000..52370e6 --- /dev/null +++ b/examples/today/Example.xcodeproj/project.xcworkspace/xcshareddata/Example.xcscmblueprint @@ -0,0 +1,30 @@ +{ + "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D", + "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { + + }, + "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { + "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : 0, + "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : 0 + }, + "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "40549047-3043-4E27-90B2-4497ECB8EE80", + "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { + "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : "gym\/", + "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : "" + }, + "DVTSourceControlWorkspaceBlueprintNameKey" : "Example", + "DVTSourceControlWorkspaceBlueprintVersion" : 204, + "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "examples\/today\/Example.xcodeproj", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/fastlane\/countdown", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" + }, + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/krausefx\/gym", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" + } + ] +} \ No newline at end of file diff --git a/examples/today/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist b/examples/today/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..642a1dd --- /dev/null +++ b/examples/today/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,24 @@ + + + + + SuppressBuildableAutocreation + + CAC1E1971B6A326100A8B23A + + primary + + + CAC1E1B01B6A326100A8B23A + + primary + + + CAC1E1BB1B6A326200A8B23A + + primary + + + + + diff --git a/examples/with spaces/With Spaces.xcodeproj/project.pbxproj b/examples/with spaces/With Spaces.xcodeproj/project.pbxproj index 1276cd0..3c60f1b 100644 --- a/examples/with spaces/With Spaces.xcodeproj/project.pbxproj +++ b/examples/with spaces/With Spaces.xcodeproj/project.pbxproj @@ -362,9 +362,9 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; INFOPLIST_FILE = "With Spaces/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.9; + PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = "675dcbd2-06a8-4a38-91a9-33ad0faf22b1"; + PROVISIONING_PROFILE = ""; }; name = Debug; }; @@ -375,9 +375,9 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; INFOPLIST_FILE = "With Spaces/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.9; + PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = "675dcbd2-06a8-4a38-91a9-33ad0faf22b1"; + PROVISIONING_PROFILE = ""; }; name = Release; }; @@ -443,6 +443,7 @@ CAB109241B7A641500F53B5F /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/examples/with spaces/With Spaces.xcodeproj/project.xcworkspace/xcshareddata/With Spaces.xcscmblueprint b/examples/with spaces/With Spaces.xcodeproj/project.xcworkspace/xcshareddata/With Spaces.xcscmblueprint new file mode 100644 index 0000000..c515232 --- /dev/null +++ b/examples/with spaces/With Spaces.xcodeproj/project.xcworkspace/xcshareddata/With Spaces.xcscmblueprint @@ -0,0 +1,30 @@ +{ + "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D", + "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { + + }, + "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { + "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : 0, + "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : 0 + }, + "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "DAED6B94-01EB-4907-BDCD-FC15F46055DF", + "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { + "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : "gym\/", + "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : "" + }, + "DVTSourceControlWorkspaceBlueprintNameKey" : "With Spaces", + "DVTSourceControlWorkspaceBlueprintVersion" : 204, + "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "examples\/with spaces\/With Spaces.xcodeproj", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/fastlane\/countdown", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" + }, + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/krausefx\/gym", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" + } + ] +} \ No newline at end of file diff --git a/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/Notification - With Spaces WatchKit 1 App.xcscheme b/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/Notification - With Spaces WatchKit 1 App.xcscheme new file mode 100644 index 0000000..543a4fe --- /dev/null +++ b/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/Notification - With Spaces WatchKit 1 App.xcscheme @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/With Spaces WatchKit 1 App.xcscheme b/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/With Spaces WatchKit 1 App.xcscheme new file mode 100644 index 0000000..a3abf36 --- /dev/null +++ b/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/With Spaces WatchKit 1 App.xcscheme @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/With Spaces.xcscheme b/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/With Spaces.xcscheme new file mode 100644 index 0000000..e2a92f1 --- /dev/null +++ b/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/With Spaces.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist b/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..12872a3 --- /dev/null +++ b/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,37 @@ + + + + + SchemeUserState + + Notification - With Spaces WatchKit 1 App.xcscheme + + orderHint + 2 + + With Spaces WatchKit 1 App.xcscheme + + orderHint + 1 + + With Spaces.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + CAA5624F1B7A5A39007AECEF + + primary + + + CAB109121B7A641500F53B5F + + primary + + + + + From 69d3cf8dffd714bcc96341ddf1e4d27572451e11 Mon Sep 17 00:00:00 2001 From: Jerome Lacoste Date: Tue, 22 Sep 2015 07:16:06 +0200 Subject: [PATCH 045/176] Reuse Project.mac? to detect project destination on Xcode 6.4 --- lib/gym/detect_values.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/detect_values.rb b/lib/gym/detect_values.rb index 6fe2671..c14635f 100644 --- a/lib/gym/detect_values.rb +++ b/lib/gym/detect_values.rb @@ -132,7 +132,7 @@ def self.detect_scheme # Is it an iOS device or a Mac? def self.detect_platform return if Gym.config[:destination] - platform = Gym.project.build_settings(key: "PLATFORM_DISPLAY_NAME") || "iOS" # either `iOS` or `OS X` + platform = Gym.project.mac? ? "OS X" : "iOS" # either `iOS` or `OS X` Gym.config[:destination] = "generic/platform=#{platform}" end From d97410d8f24e23520a6e4a5eb6155e3f1eca0b47 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Tue, 22 Sep 2015 15:44:24 -0700 Subject: [PATCH 046/176] Hide legacy note when value is automatically set via fastlane --- lib/gym/generators/package_command_generator_xcode7.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index 7eec9d5..022a319 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -3,7 +3,7 @@ module Gym class PackageCommandGeneratorXcode7 class << self def generate - print_legacy_information + print_legacy_information unless Helper.fastlane_enabled? parts = ["/usr/bin/xcrun xcodebuild -exportArchive"] parts += options From 76a18509b7546712d7aaa6ac61ca585a2c905aa6 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Tue, 22 Sep 2015 17:36:31 -0700 Subject: [PATCH 047/176] Added raw xcodebuild logs in logs folder Added note in README --- README.md | 1 + lib/gym/generators/build_command_generator.rb | 7 ++++++- spec/build_command_generator_spec.rb | 8 ++++++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 759b4db..7848fee 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,7 @@ To pass boolean parameters make sure to use `gym` like this: gym --include_bitcode true --include_symbols true +To access the raw `xcodebuild` output open `~/Library/Logs` and search for files starting with `xcodebuild` # Gymfile diff --git a/lib/gym/generators/build_command_generator.rb b/lib/gym/generators/build_command_generator.rb index 99f3d95..5e2d47a 100644 --- a/lib/gym/generators/build_command_generator.rb +++ b/lib/gym/generators/build_command_generator.rb @@ -63,7 +63,12 @@ def suffix end def pipe - ["| tee 'xcodebuild-#{Gym.config[:scheme]}.log' | xcpretty"] + ["| tee '#{xcodebuild_log_path}' | xcpretty"] + end + + def xcodebuild_log_path + file_name = "xcodebuild-#{Gym.project.app_name}-#{Gym.config[:scheme]}.log" + return File.expand_path("~/Library/Logs/#{file_name}") end # The path to set the Derived Data to diff --git a/spec/build_command_generator_spec.rb b/spec/build_command_generator_spec.rb index c2a9287..cd8b029 100644 --- a/spec/build_command_generator_spec.rb +++ b/spec/build_command_generator_spec.rb @@ -7,6 +7,8 @@ end it "supports additional parameters" do + log_path = File.expand_path("~/Library/Logs/xcodebuild-ExampleProductName-Example.log") + xcargs_hash = { DEBUG: "1", BUNDLE_NAME: "Example App" } xcargs = xcargs_hash.map do |k, v| "#{k.to_s.shellescape}=#{v.shellescape}" @@ -26,7 +28,7 @@ "-archivePath '#{Gym::BuildCommandGenerator.archive_path}'", "DEBUG=1 BUNDLE_NAME=Example\\ App", :archive, - "| tee 'xcodebuild-Example.log' | xcpretty" + "| tee '#{log_path}' | xcpretty" ]) end @@ -37,6 +39,8 @@ end it "uses the correct build command with the example project with no additional parameters" do + log_path = File.expand_path("~/Library/Logs/xcodebuild-ExampleProductName-Example.log") + result = Gym::BuildCommandGenerator.generate expect(result).to eq([ "set -o pipefail &&", @@ -47,7 +51,7 @@ "-destination 'generic/platform=iOS'", "-archivePath '#{Gym::BuildCommandGenerator.archive_path}'", :archive, - "| tee 'xcodebuild-Example.log' | xcpretty" + "| tee '#{log_path}' | xcpretty" ]) end From 2e3c45c34379af93aa395ff8bf3eeb207b5ef6f6 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Tue, 22 Sep 2015 17:41:04 -0700 Subject: [PATCH 048/176] Raw xcodebuild logs are now stored in gym subfolder --- README.md | 2 +- lib/gym/generators/build_command_generator.rb | 7 +++++-- spec/build_command_generator_spec.rb | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7848fee..f9464bc 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ To pass boolean parameters make sure to use `gym` like this: gym --include_bitcode true --include_symbols true -To access the raw `xcodebuild` output open `~/Library/Logs` and search for files starting with `xcodebuild` +To access the raw `xcodebuild` output open `~/Library/Logs/gym` # Gymfile diff --git a/lib/gym/generators/build_command_generator.rb b/lib/gym/generators/build_command_generator.rb index 5e2d47a..43b02dc 100644 --- a/lib/gym/generators/build_command_generator.rb +++ b/lib/gym/generators/build_command_generator.rb @@ -67,8 +67,11 @@ def pipe end def xcodebuild_log_path - file_name = "xcodebuild-#{Gym.project.app_name}-#{Gym.config[:scheme]}.log" - return File.expand_path("~/Library/Logs/#{file_name}") + file_name = "#{Gym.project.app_name}-#{Gym.config[:scheme]}.log" + containing = File.expand_path("~/Library/Logs/gym") + FileUtils.mkdir_p(containing) + + return File.join(containing, file_name) end # The path to set the Derived Data to diff --git a/spec/build_command_generator_spec.rb b/spec/build_command_generator_spec.rb index cd8b029..067b392 100644 --- a/spec/build_command_generator_spec.rb +++ b/spec/build_command_generator_spec.rb @@ -7,7 +7,7 @@ end it "supports additional parameters" do - log_path = File.expand_path("~/Library/Logs/xcodebuild-ExampleProductName-Example.log") + log_path = File.expand_path("~/Library/Logs/gym/ExampleProductName-Example.log") xcargs_hash = { DEBUG: "1", BUNDLE_NAME: "Example App" } xcargs = xcargs_hash.map do |k, v| @@ -39,7 +39,7 @@ end it "uses the correct build command with the example project with no additional parameters" do - log_path = File.expand_path("~/Library/Logs/xcodebuild-ExampleProductName-Example.log") + log_path = File.expand_path("~/Library/Logs/gym/ExampleProductName-Example.log") result = Gym::BuildCommandGenerator.generate expect(result).to eq([ From d3b2d3f9d9faa258877bd77aa8bcb5687872f730 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Tue, 22 Sep 2015 17:41:32 -0700 Subject: [PATCH 049/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index 5651928..ef4ac00 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "0.7.2" + VERSION = "0.7.3" DESCRIPTION = "Building your iOS apps has never been easier" end From c7ab17c51e265d283f4ed76027925d591d645ce7 Mon Sep 17 00:00:00 2001 From: Nicolae Ghimbovschi Date: Wed, 23 Sep 2015 04:58:44 +0300 Subject: [PATCH 050/176] Added buildlog_path option to change the location of the xcodebuild logs --- lib/gym/generators/build_command_generator.rb | 4 +++- lib/gym/options.rb | 5 +++++ spec/build_command_generator_spec.rb | 12 ++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/gym/generators/build_command_generator.rb b/lib/gym/generators/build_command_generator.rb index 43b02dc..0ef5992 100644 --- a/lib/gym/generators/build_command_generator.rb +++ b/lib/gym/generators/build_command_generator.rb @@ -68,7 +68,9 @@ def pipe def xcodebuild_log_path file_name = "#{Gym.project.app_name}-#{Gym.config[:scheme]}.log" - containing = File.expand_path("~/Library/Logs/gym") + file_dir = Gym.config[:buildlog_path] + file_dir ||= "~/Library/Logs/gym" + containing = File.expand_path(file_dir) FileUtils.mkdir_p(containing) return File.join(containing, file_name) diff --git a/lib/gym/options.rb b/lib/gym/options.rb index 34ffbde..e04b79b 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -63,6 +63,11 @@ def self.plain_options verify_block: proc do |value| value.gsub!(".ipa", "") end), + FastlaneCore::ConfigItem.new(key: :buildlog_path, + short_option: "-l", + env_name: "GYM_BUILDLOG_PATH", + description: "The directory were to store the build log", + optional: true), FastlaneCore::ConfigItem.new(key: :sdk, short_option: "-k", env_name: "GYM_SDK", diff --git a/spec/build_command_generator_spec.rb b/spec/build_command_generator_spec.rb index 067b392..27ef922 100644 --- a/spec/build_command_generator_spec.rb +++ b/spec/build_command_generator_spec.rb @@ -71,6 +71,18 @@ regex = %r{Library/Developer/Xcode/Archives/\d\d\d\d\-\d\d\-\d\d/ExampleProductName \d\d\d\d\-\d\d\-\d\d \d\d\.\d\d\.\d\d.xcarchive} expect(result).to match(regex) end + + it "#buildlog_path is used when provided" do + options = { project: "./examples/standard/Example.xcodeproj", buildlog_path: "/tmp/my/path" } + Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) + result = Gym::BuildCommandGenerator.xcodebuild_log_path + expect(result).to include("/tmp/my/path") + end + + it "#buildlog_path is not used when not provided" do + result = Gym::BuildCommandGenerator.xcodebuild_log_path + expect(result.to_s).to include("Library/Logs/gym") + end end end end From e9ba16a98c82ce88692c776b871fae9aecd9d6e4 Mon Sep 17 00:00:00 2001 From: Nicolae Ghimbovschi Date: Wed, 23 Sep 2015 05:37:23 +0300 Subject: [PATCH 051/176] Simplified the buildlog_path implementation --- lib/gym/generators/build_command_generator.rb | 4 +--- lib/gym/options.rb | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/gym/generators/build_command_generator.rb b/lib/gym/generators/build_command_generator.rb index 0ef5992..60d70e4 100644 --- a/lib/gym/generators/build_command_generator.rb +++ b/lib/gym/generators/build_command_generator.rb @@ -68,9 +68,7 @@ def pipe def xcodebuild_log_path file_name = "#{Gym.project.app_name}-#{Gym.config[:scheme]}.log" - file_dir = Gym.config[:buildlog_path] - file_dir ||= "~/Library/Logs/gym" - containing = File.expand_path(file_dir) + containing = File.expand_path(Gym.config[:buildlog_path]) FileUtils.mkdir_p(containing) return File.join(containing, file_name) diff --git a/lib/gym/options.rb b/lib/gym/options.rb index e04b79b..6b1eea2 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -67,7 +67,7 @@ def self.plain_options short_option: "-l", env_name: "GYM_BUILDLOG_PATH", description: "The directory were to store the build log", - optional: true), + default_value: "~/Library/Logs/gym"), FastlaneCore::ConfigItem.new(key: :sdk, short_option: "-k", env_name: "GYM_SDK", From 7837fe18265ee6de2ca2ad05ae64a46e3e5dc221 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 23 Sep 2015 09:52:54 -0700 Subject: [PATCH 052/176] Added showing of export method in build summary --- lib/gym/generators/package_command_generator.rb | 2 -- lib/gym/manager.rb | 5 +++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/gym/generators/package_command_generator.rb b/lib/gym/generators/package_command_generator.rb index 27c870b..d75ef77 100644 --- a/lib/gym/generators/package_command_generator.rb +++ b/lib/gym/generators/package_command_generator.rb @@ -24,8 +24,6 @@ def dsym_path generator.dsym_path end - private - # The generator we need to use for the currently used Xcode version def generator if Xcode.pre_7? or Gym.config[:use_legacy_build_api] diff --git a/lib/gym/manager.rb b/lib/gym/manager.rb index 43e5019..2d6b3d5 100644 --- a/lib/gym/manager.rb +++ b/lib/gym/manager.rb @@ -17,6 +17,11 @@ def print_summary rows << ["Workspace", config[:workspace]] if config[:workspace] rows << ["Scheme", config[:scheme]] if config[:scheme] rows << ["Configuration", config[:configuration]] if config[:configuration] + + if PackageCommandGenerator.generator == PackageCommandGeneratorXcode7 + rows << ["Export Method", config[:export_method]] + end + rows << ["Archive Path", config[:archive_path]] if config[:archive_path] rows << ["Platform", Gym.project.ios? ? "iOS" : "Mac"] rows << ["Xcode Path", Xcode.xcode_path.gsub("/Contents/Developer", "")] From 928d28275350aab583ad4ce2ff7362a95f961a50 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 23 Sep 2015 09:53:00 -0700 Subject: [PATCH 053/176] Improved code style --- .rubocop_general.yml | 9 +++++---- lib/gym/options.rb | 2 -- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.rubocop_general.yml b/.rubocop_general.yml index 4283c8c..fee75a1 100644 --- a/.rubocop_general.yml +++ b/.rubocop_general.yml @@ -51,6 +51,11 @@ Metrics/MethodLength: Exclude: - 'lib/*/options.rb' +Metrics/AbcSize: + Max: 60 + Exclude: + - 'lib/*/options.rb' + # Both string notations are okay Style/StringLiterals: Enabled: false @@ -83,10 +88,6 @@ Style/AlignHash: Style/AndOr: Enabled: false -# Offense count: 20 -Metrics/AbcSize: - Max: 60 - # Configuration parameters: CountComments. Metrics/ClassLength: Max: 320 diff --git a/lib/gym/options.rb b/lib/gym/options.rb index 34ffbde..42dcd16 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -1,7 +1,6 @@ require "fastlane_core" require "credentials_manager" -# rubocop:disable Metrics/AbcSize module Gym class Options def self.available_options @@ -147,4 +146,3 @@ def self.plain_options end end end -# rubocop:enable Metrics/AbcSize From d8b111e97aac672c4801b8c6427d2c2e56a01be5 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 23 Sep 2015 10:15:13 -0700 Subject: [PATCH 054/176] Fixed problem with case sensitivity Fixes https://github.com/fastlane/gym/issues/101 --- lib/gym/generators/package_command_generator_xcode7.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index 022a319..bb2af2d 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -38,7 +38,7 @@ def ipa_path path = Dir[File.join(temporary_output_path, "*.ipa")].last @ipa_path = File.join(temporary_output_path, "#{Gym.config[:output_name]}.ipa") - FileUtils.mv(path, @ipa_path) if File.expand_path(path) != File.expand_path(@ipa_path) + FileUtils.mv(path, @ipa_path) if File.expand_path(path).downcase != File.expand_path(@ipa_path).downcase end @ipa_path end From 1f83a30eab85275894b4300e1ad961cd74c82278 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 23 Sep 2015 11:22:43 -0700 Subject: [PATCH 055/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index ef4ac00..ee5a8ff 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "0.7.3" + VERSION = "0.8.0" DESCRIPTION = "Building your iOS apps has never been easier" end From 9a68848bf15b0e9707581f7cca6c24a321f17536 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 23 Sep 2015 13:42:56 -0700 Subject: [PATCH 056/176] export_method will be determined at a later point --- lib/gym/detect_values.rb | 3 +++ lib/gym/options.rb | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/gym/detect_values.rb b/lib/gym/detect_values.rb index c14635f..ef5a33d 100644 --- a/lib/gym/detect_values.rb +++ b/lib/gym/detect_values.rb @@ -30,6 +30,9 @@ 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 diff --git a/lib/gym/options.rb b/lib/gym/options.rb index c362f71..127a5a2 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -132,8 +132,8 @@ def self.plain_options short_option: "-j", env_name: "GYM_EXPORT_METHOD", description: "How should gym export the archive?", - default_value: "app-store", is_string: true, + optional: true, verify_block: proc do |value| av = %w(app-store ad-hoc package enterprise development developer-id) raise "Unsupported export_method, must be: #{av}" unless av.include?(value) From 6cba18d693933eb212e78d75064074a655edcc4c Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 23 Sep 2015 13:43:45 -0700 Subject: [PATCH 057/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index ee5a8ff..ebf39fa 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "0.8.0" + VERSION = "0.8.1" DESCRIPTION = "Building your iOS apps has never been easier" end From 5635574d492847ed78c34aee6ce80310218813af Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 23 Sep 2015 16:45:14 -0700 Subject: [PATCH 058/176] Fixed gym paths when running gym multiple times in one lane --- lib/gym.rb | 5 ++++- lib/gym/generators/build_command_generator.rb | 16 ++++++++-------- .../package_command_generator_xcode7.rb | 14 +++++++------- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/lib/gym.rb b/lib/gym.rb index 64e6aa4..d2af068 100644 --- a/lib/gym.rb +++ b/lib/gym.rb @@ -19,10 +19,13 @@ class << self attr_accessor :project + attr_accessor :cache + def config=(value) @config = value DetectValues.set_additional_default_values - end + @cache = {} + end def gymfile_name "Gymfile" diff --git a/lib/gym/generators/build_command_generator.rb b/lib/gym/generators/build_command_generator.rb index 60d70e4..e941b45 100644 --- a/lib/gym/generators/build_command_generator.rb +++ b/lib/gym/generators/build_command_generator.rb @@ -76,22 +76,22 @@ def xcodebuild_log_path # The path to set the Derived Data to def build_path - unless @build_path + unless Gym.cache[:build_path] day = Time.now.strftime("%F") # e.g. 2015-08-07 - @build_path = File.expand_path("~/Library/Developer/Xcode/Archives/#{day}/") - FileUtils.mkdir_p @build_path + Gym.cache[:build_path] = File.expand_path("~/Library/Developer/Xcode/Archives/#{day}/") + FileUtils.mkdir_p Gym.cache[:build_path] end - @build_path + Gym.cache[:build_path] end def archive_path - @archive_path ||= Gym.config[:archive_path] - unless @archive_path + Gym.cache[:archive_path] ||= Gym.config[:archive_path] + unless Gym.cache[:archive_path] file_name = [Gym.config[:output_name], Time.now.strftime("%F %H.%M.%S")] # e.g. 2015-08-07 14.49.12 - @archive_path = File.join(build_path, file_name.join(" ") + ".xcarchive") + Gym.cache[:archive_path] = File.join(build_path, file_name.join(" ") + ".xcarchive") end - return @archive_path + return Gym.cache[:archive_path] end end end diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index bb2af2d..08114b3 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -30,17 +30,17 @@ def pipe # We export the ipa into this directory, as we can't specify the ipa file directly def temporary_output_path - @temporary_output_path ||= File.join("/tmp", Time.now.to_i.to_s) + Gym.cache[:temporary_output_path] ||= File.join("/tmp", Time.now.to_i.to_s) end def ipa_path - unless @ipa_path + unless Gym.cache[:ipa_path] path = Dir[File.join(temporary_output_path, "*.ipa")].last - @ipa_path = File.join(temporary_output_path, "#{Gym.config[:output_name]}.ipa") - FileUtils.mv(path, @ipa_path) if File.expand_path(path).downcase != File.expand_path(@ipa_path).downcase + 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 end - @ipa_path + Gym.cache[:ipa_path] end # The path the the dsym file for this app. Might be nil @@ -50,8 +50,8 @@ def dsym_path # The path the config file we use to sign our app def config_path - @config_path ||= "/tmp/gym_config_#{Time.now.to_i}.plist" - return @config_path + Gym.cache[:config_path] ||= "/tmp/gym_config_#{Time.now.to_i}.plist" + return Gym.cache[:config_path] end private From f6dcf319766c1e65017313036145c6160139231e Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 23 Sep 2015 16:45:27 -0700 Subject: [PATCH 059/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index ebf39fa..5010c75 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "0.8.1" + VERSION = "0.8.2" DESCRIPTION = "Building your iOS apps has never been easier" end From 3fc6cbf9b4494e6e2e1d3f6cab283b2b0902f85e Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 23 Sep 2015 16:46:27 -0700 Subject: [PATCH 060/176] Fixed code style --- lib/gym.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym.rb b/lib/gym.rb index d2af068..1449c02 100644 --- a/lib/gym.rb +++ b/lib/gym.rb @@ -25,7 +25,7 @@ def config=(value) @config = value DetectValues.set_additional_default_values @cache = {} - end + end def gymfile_name "Gymfile" From 93eda014e14d1d480876a1d8cf4999db304c3bcc Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Sun, 27 Sep 2015 02:03:57 -0700 Subject: [PATCH 061/176] Check use_legacy_api instead of direct Xcode 7 check --- lib/gym/runner.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb index a9ba09d..5ef65d1 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -57,14 +57,14 @@ def print_command(command, title) ##################################################### def clear_old_files - return unless Xcode.pre_7? + return unless Gym.config[:use_legacy_build_api] if File.exist?(PackageCommandGenerator.ipa_path) File.delete(PackageCommandGenerator.ipa_path) end end def fix_package - return unless Xcode.pre_7? + return unless Gym.config[:use_legacy_build_api] Gym::XcodebuildFixes.swift_library_fix Gym::XcodebuildFixes.watchkit_fix end @@ -149,7 +149,7 @@ def move_mac_app private def find_archive_path - if Xcode.pre_7? + if Gym.config[:use_legacy_build_api] BuildCommandGenerator.archive_path else Dir.glob(File.join(BuildCommandGenerator.build_path, "*.ipa")).last From 921b94be0fd814df5cfb9235b6ee2c2b6ddae9eb Mon Sep 17 00:00:00 2001 From: KrauseFx Date: Sun, 27 Sep 2015 13:20:47 -0700 Subject: [PATCH 062/176] Automatic setting of legacy_build_api when using an old version of Xcode --- lib/gym/detect_values.rb | 2 ++ lib/gym/generators/package_command_generator.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/gym/detect_values.rb b/lib/gym/detect_values.rb index ef5a33d..244e4e3 100644 --- a/lib/gym/detect_values.rb +++ b/lib/gym/detect_values.rb @@ -24,6 +24,8 @@ def self.set_additional_default_values config.load_configuration_file(Gym.gymfile_name) end + config[:use_legacy_build_api] = true if Xcode.pre_7? + detect_scheme detect_platform # we can only do that *after* we have the scheme detect_configuration diff --git a/lib/gym/generators/package_command_generator.rb b/lib/gym/generators/package_command_generator.rb index d75ef77..6f34813 100644 --- a/lib/gym/generators/package_command_generator.rb +++ b/lib/gym/generators/package_command_generator.rb @@ -26,7 +26,7 @@ def dsym_path # The generator we need to use for the currently used Xcode version def generator - if Xcode.pre_7? or Gym.config[:use_legacy_build_api] + if Gym.config[:use_legacy_build_api] PackageCommandGeneratorLegacy else PackageCommandGeneratorXcode7 From cabecce746e670c579aa20c9a6ff83996d3a9386 Mon Sep 17 00:00:00 2001 From: KrauseFx Date: Sun, 27 Sep 2015 13:24:13 -0700 Subject: [PATCH 063/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index 5010c75..49931b8 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "0.8.2" + VERSION = "0.8.3" DESCRIPTION = "Building your iOS apps has never been easier" end From 9505b6b88e0dc71e9e1444bd0d4a416124171a1b Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 30 Sep 2015 13:50:37 -0700 Subject: [PATCH 064/176] Updated documentation for provisioning profile --- lib/gym/error_handler.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/gym/error_handler.rb b/lib/gym/error_handler.rb index 78d7828..0a00662 100644 --- a/lib/gym/error_handler.rb +++ b/lib/gym/error_handler.rb @@ -69,8 +69,6 @@ def handle_package_error(output) print "Could not find provisioning profile with the name '#{$1}'" print "Make sure the name of the provisioning profile is correct" print "and it matches a locally installed profile" - print "You can pass the name of the provisioning profile using the" - print "`--provisioning_profile_path` option" when /mismatch between specified provisioning profile and signing identity/ print "Mismatch between provisioning profile and code signing identity" print "This means, the specified provisioning profile was not created using" From 96c7530491dbb3482d6953bfd31b14c53c875379 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 30 Sep 2015 13:50:54 -0700 Subject: [PATCH 065/176] No more automatic 'Release' configuration Fixes https://github.com/KrauseFx/fastlane/issues/176 --- lib/gym/detect_values.rb | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/lib/gym/detect_values.rb b/lib/gym/detect_values.rb index 244e4e3..36bb59e 100644 --- a/lib/gym/detect_values.rb +++ b/lib/gym/detect_values.rb @@ -155,25 +155,6 @@ def self.detect_configuration config[:configuration] = nil end end - - # Usually we want `Release` - # We prefer `Release` to export the DSYM file as well - config[:configuration] ||= "Release" if configurations.include?("Release") - - return if config[:configuration].to_s.length > 0 - - if configurations.count == 1 - config[:configuration] = configurations.last - else - if Helper.is_ci? - Helper.log.error "Multiple configurations found but you haven't specified one.".red - Helper.log.error "Since this is a CI, please pass one using the `configuration` option".red - raise "Multiple configurations found".red - else - puts "Select Configuration: " - config[:configuration] = choose(*(configurations)) - end - end end end end From 83b383db5e2ef2e782d679a48e42ddbcf1852456 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 30 Sep 2015 13:51:02 -0700 Subject: [PATCH 066/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index 49931b8..49b9b87 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "0.8.3" + VERSION = "0.8.4" DESCRIPTION = "Building your iOS apps has never been easier" end From a12e17822f0194d63ee8f85848f2d916a249341c Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 30 Sep 2015 13:51:53 -0700 Subject: [PATCH 067/176] Updated fastlane_core dependency --- gym.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gym.gemspec b/gym.gemspec index b13c5bd..60033cf 100644 --- a/gym.gemspec +++ b/gym.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency 'fastlane_core', '>= 0.17.0', '< 1.0.0' # all shared code and dependencies + spec.add_dependency 'fastlane_core', '>= 0.18.2', '< 1.0.0' # all shared code and dependencies spec.add_dependency 'xcpretty' # pretty xcodebuild output spec.add_dependency 'terminal-table' # print out build information spec.add_dependency 'plist' # Generate the Xcode config plist file From 2be29362d60b8bd7eaf1c756599d16aa2db3dd8c Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 30 Sep 2015 13:54:19 -0700 Subject: [PATCH 068/176] Added new rake push task --- Rakefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Rakefile b/Rakefile index ed2a05e..195f9ce 100644 --- a/Rakefile +++ b/Rakefile @@ -7,3 +7,7 @@ task default: :spec task :test do sh "../fastlane/bin/fastlane test" end + +task :push do + sh "../fastlane/bin/fastlane release" +end From 90d90a2f79f0f4f7fa3e61a130cd8dbf2d3331fe Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 30 Sep 2015 13:56:23 -0700 Subject: [PATCH 069/176] Updated tests --- spec/build_command_generator_spec.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/build_command_generator_spec.rb b/spec/build_command_generator_spec.rb index 27ef922..59bc718 100644 --- a/spec/build_command_generator_spec.rb +++ b/spec/build_command_generator_spec.rb @@ -22,7 +22,6 @@ "xcodebuild", "-scheme 'Example'", "-project './examples/standard/Example.xcodeproj'", - "-configuration 'Release'", "-sdk '9.0'", "-destination 'generic/platform=iOS'", "-archivePath '#{Gym::BuildCommandGenerator.archive_path}'", @@ -47,7 +46,6 @@ "xcodebuild", "-scheme 'Example'", "-project './examples/standard/Example.xcodeproj'", - "-configuration 'Release'", "-destination 'generic/platform=iOS'", "-archivePath '#{Gym::BuildCommandGenerator.archive_path}'", :archive, From 128f35d7a18e28b4ba8d768da786d4a4e757a9ca Mon Sep 17 00:00:00 2001 From: KrauseFx Date: Thu, 1 Oct 2015 16:04:35 -0700 Subject: [PATCH 070/176] Updated fastlane_core dependency --- gym.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gym.gemspec b/gym.gemspec index b13c5bd..fd6731f 100644 --- a/gym.gemspec +++ b/gym.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency 'fastlane_core', '>= 0.17.0', '< 1.0.0' # all shared code and dependencies + spec.add_dependency 'fastlane_core', '>= 0.19.0', '< 1.0.0' # all shared code and dependencies spec.add_dependency 'xcpretty' # pretty xcodebuild output spec.add_dependency 'terminal-table' # print out build information spec.add_dependency 'plist' # Generate the Xcode config plist file From 5c6dbf7c8392de2e92c4d72b03ebd615a1ff80c5 Mon Sep 17 00:00:00 2001 From: Brian Michel Date: Tue, 6 Oct 2015 11:22:28 -0400 Subject: [PATCH 071/176] Zip all dSYM files together When building an application with many frameworks, each framework will produce it's own dSYM file. These need to be included in the dSYM zip file to allow for symbolication of crashes in these frameworks. Really we just need the archive path, but this will allow for easy backward compatibility with existing code. --- lib/gym/runner.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb index 5ef65d1..8e958a3 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -108,10 +108,13 @@ def compress_and_move_dsym # Compress and move the dsym file containing_directory = File.expand_path("..", PackageCommandGenerator.dsym_path) - file_name = File.basename(PackageCommandGenerator.dsym_path) + + available_dsyms = Dir.glob("#{containing_directory}/*.dSYM") + + Helper.log.info "Compressing #{available_dsyms.count} dSYM(s)" output_path = File.expand_path(File.join(Gym.config[:output_directory], Gym.config[:output_name] + ".app.dSYM.zip")) - command = "cd '#{containing_directory}' && zip -r '#{output_path}' '#{file_name}'" + command = "cd '#{containing_directory}' && zip -r '#{output_path}' '*.dSYM'" Helper.log.info command.yellow unless Gym.config[:silent] command_result = `#{command}` Helper.log.info command_result if $verbose From 2c89a228bd538a0874cfd336d952e872bdaa5680 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Tue, 6 Oct 2015 17:31:26 -0400 Subject: [PATCH 072/176] Stop detecting the project if we already have a workspace Fixes https://github.com/KrauseFx/fastlane/issues/659 --- lib/gym/detect_values.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/gym/detect_values.rb b/lib/gym/detect_values.rb index 36bb59e..1619d12 100644 --- a/lib/gym/detect_values.rb +++ b/lib/gym/detect_values.rb @@ -61,6 +61,8 @@ def self.detect_provisioning_profile end def self.detect_projects + return if Gym.config[:project].to_s.length > 0 + if Gym.config[:workspace].to_s.length == 0 workspace = Dir["./*.xcworkspace"] if workspace.count > 1 @@ -71,6 +73,8 @@ def self.detect_projects end end + return if Gym.config[:workspace].to_s.length > 0 + if Gym.config[:workspace].to_s.length == 0 and Gym.config[:project].to_s.length == 0 project = Dir["./*.xcodeproj"] if project.count > 1 From eeada548b7a77357ba8271009071a7563b01b911 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Tue, 6 Oct 2015 17:33:48 -0400 Subject: [PATCH 073/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index 49b9b87..450a0b6 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "0.8.4" + VERSION = "0.8.5" DESCRIPTION = "Building your iOS apps has never been easier" end From a515f049b03730235c3c5c9a7f37adf35195bedd Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Tue, 6 Oct 2015 22:34:20 -0400 Subject: [PATCH 074/176] Updated travis configuration --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 9167d41..db8d8c5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,4 +3,6 @@ osx_image: xcode6.4 before_install: gem update --system rvm: - 2.0.0 + - 2.1.6 + - 2.2.2 script: bundle exec fastlane test From 38f9d625dd418c12bd4f3341cc392a6fbea09b5e Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Tue, 6 Oct 2015 22:40:26 -0400 Subject: [PATCH 075/176] Updated travis configuration --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index db8d8c5..9167d41 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,4 @@ osx_image: xcode6.4 before_install: gem update --system rvm: - 2.0.0 - - 2.1.6 - - 2.2.2 script: bundle exec fastlane test From a651d761839dd64b3345a73ce1e26065e98da034 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Thu, 8 Oct 2015 11:48:38 -0400 Subject: [PATCH 076/176] Disbabled web requests for tests --- spec/spec_helper.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 40d7abd..3de06be 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -6,3 +6,5 @@ module SpecHelper end require 'gym' + +WebMock.disable_net_connect!(allow: 'coveralls.io') From f3f2a371e85ce7b72104cf3b10bddadc3798b812 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Thu, 8 Oct 2015 11:48:51 -0400 Subject: [PATCH 077/176] `gym init` now creates the configuration in the fastlane folder if exists --- lib/gym/commands_generator.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/gym/commands_generator.rb b/lib/gym/commands_generator.rb index 425f49a..bb209ed 100644 --- a/lib/gym/commands_generator.rb +++ b/lib/gym/commands_generator.rb @@ -46,10 +46,12 @@ def run c.syntax = "gym init" c.description = "Creates a new Gymfile for you" c.action do |_args, options| - raise "Gymfile already exists" if File.exist?(Gym.gymfile_name) + containing = (File.directory?("fastlane") ? 'fastlane' : '.') + path = File.join(containing, Gym.gymfile_name) + raise "Gymfile already exists" if File.exist?(path) template = File.read("#{Helper.gem_path('gym')}/lib/assets/GymfileTemplate") - File.write(Gym.gymfile_name, template) - Helper.log.info "Successfully created '#{Gym.gymfile_name}'. Open the file using a code editor.".green + File.write(path, template) + Helper.log.info "Successfully created '#{path}'. Open the file using a code editor.".green end end From 7ce04e7656f00f58588eade2a27e813f6e78505b Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Thu, 8 Oct 2015 12:05:15 -0400 Subject: [PATCH 078/176] Added webmock --- spec/spec_helper.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3de06be..29bfebe 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,8 @@ require "coveralls" Coveralls.wear! unless ENV["FASTLANE_SKIP_UPDATE_CHECK"] +require 'webmock' + # This module is only used to check the environment is currently a testing env module SpecHelper end From 99a82293c70467295074fcc1a0ffefdcffaf7a46 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Sat, 10 Oct 2015 20:22:56 -0400 Subject: [PATCH 079/176] Moved Project class to fastlane core --- gym.gemspec | 2 +- lib/gym.rb | 1 - lib/gym/detect_values.rb | 33 +-------- lib/gym/options.rb | 2 +- lib/gym/project.rb | 155 --------------------------------------- spec/gymfile_spec.rb | 2 +- spec/project_spec.rb | 84 --------------------- 7 files changed, 5 insertions(+), 274 deletions(-) delete mode 100644 lib/gym/project.rb delete mode 100644 spec/project_spec.rb diff --git a/gym.gemspec b/gym.gemspec index fd6731f..1593daa 100644 --- a/gym.gemspec +++ b/gym.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency 'fastlane_core', '>= 0.19.0', '< 1.0.0' # all shared code and dependencies + spec.add_dependency 'fastlane_core', '>= 0.20.0', '< 1.0.0' # all shared code and dependencies spec.add_dependency 'xcpretty' # pretty xcodebuild output spec.add_dependency 'terminal-table' # print out build information spec.add_dependency 'plist' # Generate the Xcode config plist file diff --git a/lib/gym.rb b/lib/gym.rb index 1449c02..27f6365 100644 --- a/lib/gym.rb +++ b/lib/gym.rb @@ -1,7 +1,6 @@ require 'json' require 'gym/version' require 'gym/manager' -require 'gym/project' require 'gym/generators/build_command_generator' require 'gym/generators/package_command_generator' require 'gym/runner' diff --git a/lib/gym/detect_values.rb b/lib/gym/detect_values.rb index 1619d12..0fe1fab 100644 --- a/lib/gym/detect_values.rb +++ b/lib/gym/detect_values.rb @@ -16,7 +16,7 @@ def self.set_additional_default_values raise "No project/workspace found in the current directory.".red end - Gym.project = Project.new(config) + Gym.project = FastlaneCore::Project.new(config) detect_provisioning_profile # Go into the project's folder @@ -106,36 +106,7 @@ def self.choose_project end def self.detect_scheme - config = Gym.config - proj_schemes = Gym.project.schemes - - if config[:scheme].to_s.length > 0 - # Verify the scheme is available - unless proj_schemes.include?(config[:scheme].to_s) - Helper.log.error "Couldn't find specified scheme '#{config[:scheme]}'.".red - config[:scheme] = nil - end - end - - return if config[:scheme].to_s.length > 0 - - if proj_schemes.count == 1 - config[:scheme] = proj_schemes.last - elsif proj_schemes.count > 1 - if Helper.is_ci? - Helper.log.error "Multiple schemes found but you haven't specified one.".red - Helper.log.error "Since this is a CI, please pass one using the `scheme` option".red - raise "Multiple schemes found".red - else - puts "Select Scheme: " - config[:scheme] = choose(*(proj_schemes)) - end - else - Helper.log.error "Couldn't find any schemes in this project, make sure that the scheme is shared if you are using a workspace".red - Helper.log.error "Open Xcode, click on `Manage Schemes` and check the `Shared` box for the schemes you want to use".red - - raise "No Schemes found".red - end + Gym.project.select_scheme end # Is it an iOS device or a Mac? diff --git a/lib/gym/options.rb b/lib/gym/options.rb index 127a5a2..6fd9b2a 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -65,7 +65,7 @@ def self.plain_options FastlaneCore::ConfigItem.new(key: :buildlog_path, short_option: "-l", env_name: "GYM_BUILDLOG_PATH", - description: "The directory were to store the build log", + description: "The directory where to store the build log", default_value: "~/Library/Logs/gym"), FastlaneCore::ConfigItem.new(key: :sdk, short_option: "-k", diff --git a/lib/gym/project.rb b/lib/gym/project.rb deleted file mode 100644 index f98975e..0000000 --- a/lib/gym/project.rb +++ /dev/null @@ -1,155 +0,0 @@ -module Gym - # Represents the Xcode project/workspace - class Project - # Path to the project/workspace - attr_accessor :path - - attr_accessor :is_workspace - - def initialize(options) - self.path = File.expand_path(options[:workspace] || options[:project]) - self.is_workspace = (options[:workspace].to_s.length > 0) - - if !path or !File.directory? path - raise "Could not find project at path '#{path}'".red - end - end - - def workspace? - self.is_workspace - end - - # Get all available schemes in an array - def schemes - results = [] - output = raw_info.split("Schemes:").last.split(":").first - - if raw_info.include?("There are no schemes in workspace") or raw_info.include?("This project contains no schemes") - return results - end - - output.split("\n").each do |current| - current = current.strip - - next if current.length == 0 - results << current - end - - results - end - - # Get all available configurations in an array - def configurations - results = [] - splitted = raw_info.split("Configurations:") - return [] if splitted.count != 2 # probably a CocoaPods project - - output = splitted.last.split(":").first - output.split("\n").each_with_index do |current, index| - current = current.strip - - if current.length == 0 - next if index == 0 - break # as we want to break on the empty line - end - - results << current - end - - results - end - - def app_name - # WRAPPER_NAME: Example.app - # WRAPPER_SUFFIX: .app - name = build_settings(key: "WRAPPER_NAME") - - return name.gsub(build_settings(key: "WRAPPER_SUFFIX"), "") if name - return "App" # default value - end - - def mac? - # Some projects have different values... we have to look for all of them - return true if build_settings(key: "PLATFORM_NAME") == "macosx" - return true if build_settings(key: "PLATFORM_DISPLAY_NAME") == "OS X" - false - end - - def ios? - !mac? - end - - ##################################################### - # @!group Raw Access - ##################################################### - - # Get the build settings for our project - # this is used to properly get the DerivedData folder - # @param [String] The key of which we want the value for (e.g. "PRODUCT_NAME") - def build_settings(key: nil, optional: true) - unless @build_settings - # We also need to pass the workspace and scheme to this command - command = "xcrun xcodebuild -showBuildSettings #{BuildCommandGenerator.project_path_array.join(' ')}" - Helper.log.info command.yellow unless Gym.config[:silent] - @build_settings = `#{command}` - end - - begin - result = @build_settings.split("\n").find { |c| c.include? key } - return result.split(" = ").last - rescue => ex - return nil if optional # an optional value, we really don't care if something goes wrong - - Helper.log.error caller.join("\n\t") - Helper.log.error "Could not fetch #{key} from project file: #{ex}" - end - - nil - end - - def raw_info - # Examples: - - # Standard: - # - # Information about project "Example": - # Targets: - # Example - # ExampleUITests - # - # Build Configurations: - # Debug - # Release - # - # If no build configuration is specified and -scheme is not passed then "Release" is used. - # - # Schemes: - # Example - # ExampleUITests - - # CococaPods - # - # Example.xcworkspace - # Information about workspace "Example": - # Schemes: - # Example - # HexColors - # Pods-Example - - return @raw if @raw - - # Unfortunately since we pass the workspace we also get all the - # schemes generated by CocoaPods - - options = BuildCommandGenerator.project_path_array.delete_if { |a| a.to_s.include? "scheme" } - command = "xcrun xcodebuild -list #{options.join(' ')}" - Helper.log.info command.yellow unless Gym.config[:silent] - - @raw = `#{command}`.to_s - - raise "Error parsing xcode file using `#{command}`".red if @raw.length == 0 - - return @raw - end - end -end diff --git a/spec/gymfile_spec.rb b/spec/gymfile_spec.rb index 9b8a0cc..55d7300 100644 --- a/spec/gymfile_spec.rb +++ b/spec/gymfile_spec.rb @@ -4,7 +4,7 @@ options = { project: "./examples/multipleSchemes/Example.xcodeproj" } Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) - @project = Gym::Project.new(Gym.config) + @project = FastlaneCore::Project.new(Gym.config) end it "#schemes returns all available schemes" do diff --git a/spec/project_spec.rb b/spec/project_spec.rb deleted file mode 100644 index 302839e..0000000 --- a/spec/project_spec.rb +++ /dev/null @@ -1,84 +0,0 @@ -describe Gym do - describe Gym::Project do - it "raises an exception if path was not found" do - expect do - Gym::Project.new(project: "/tmp/notHere123") - end.to raise_error "Could not find project at path '/tmp/notHere123'".red - end - - describe "Valid Standard Project" do - before do - options = { project: "./examples/standard/Example.xcodeproj" } - Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) - - @project = Gym::Project.new(Gym.config) - end - - it "#path" do - expect(@project.path).to eq(File.expand_path("./examples/standard/Example.xcodeproj")) - end - - it "#is_workspace" do - expect(@project.is_workspace).to eq(false) - end - - it "#schemes returns all available schemes" do - expect(@project.schemes).to eq(["Example"]) - end - - it "#configurations returns all available configurations" do - expect(@project.configurations).to eq(["Debug", "Release"]) - end - - it "#app_name" do - expect(@project.app_name).to eq("ExampleProductName") - end - - it "#mac?" do - expect(@project.mac?).to eq(false) - end - - it "#ios?" do - expect(@project.ios?).to eq(true) - end - end - - describe "Valid CocoaPods Project" do - before do - options = { workspace: "./examples/cocoapods/Example.xcworkspace", scheme: "Example" } - Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) - - @workspace = Gym::Project.new(Gym.config) - end - - it "#schemes returns all schemes" do - expect(@workspace.schemes).to eq(["Example", "HexColors", "Pods-Example"]) - end - - it "#schemes returns all configurations" do - expect(@workspace.configurations).to eq([]) - end - end - - describe "Mac Project" do - before do - options = { project: "./examples/Mac/Mac.xcodeproj" } - Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) - - @project = Gym::Project.new(Gym.config) - end - - it "#mac?" do - expect(@project.mac?).to eq(true) - end - - it "#ios?" do - expect(@project.ios?).to eq(false) - end - - it "schemes" do - expect(@project.schemes).to eq(["Mac"]) - end - end - end -end From 663ac30c881aae55ff3b5b1c604a9e2f5958b7fc Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Sun, 11 Oct 2015 08:19:27 -0400 Subject: [PATCH 080/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index 450a0b6..d1fd9c0 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "0.8.5" + VERSION = "0.9.0" DESCRIPTION = "Building your iOS apps has never been easier" end From c24e2d310e5fd015bd27cf56f4fd6f741a75ee24 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Sun, 11 Oct 2015 15:43:29 -0700 Subject: [PATCH 081/176] Added more ruby versions --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 9167d41..db8d8c5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,4 +3,6 @@ osx_image: xcode6.4 before_install: gem update --system rvm: - 2.0.0 + - 2.1.6 + - 2.2.2 script: bundle exec fastlane test From 53fd161432a2a60673e71451db59af631e68d32a Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Sun, 11 Oct 2015 15:55:34 -0700 Subject: [PATCH 082/176] Moved project discovery to fastlane_core --- gym.gemspec | 2 +- lib/gym/detect_values.rb | 55 +--------------------------------------- 2 files changed, 2 insertions(+), 55 deletions(-) diff --git a/gym.gemspec b/gym.gemspec index 1593daa..7876b8e 100644 --- a/gym.gemspec +++ b/gym.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency 'fastlane_core', '>= 0.20.0', '< 1.0.0' # all shared code and dependencies + spec.add_dependency 'fastlane_core', '>= 0.21.0', '< 1.0.0' # all shared code and dependencies spec.add_dependency 'xcpretty' # pretty xcodebuild output spec.add_dependency 'terminal-table' # print out build information spec.add_dependency 'plist' # Generate the Xcode config plist file diff --git a/lib/gym/detect_values.rb b/lib/gym/detect_values.rb index 0fe1fab..a7eedab 100644 --- a/lib/gym/detect_values.rb +++ b/lib/gym/detect_values.rb @@ -6,15 +6,7 @@ class DetectValues def self.set_additional_default_values config = Gym.config - detect_projects - - if config[:workspace].to_s.length > 0 and config[:project].to_s.length > 0 - raise "You can only pass either a workspace or a project path, not both".red - end - - if config[:workspace].to_s.length == 0 and config[:project].to_s.length == 0 - raise "No project/workspace found in the current directory.".red - end + FastlaneCore::Project.detect_projects(config) Gym.project = FastlaneCore::Project.new(config) detect_provisioning_profile @@ -60,51 +52,6 @@ def self.detect_provisioning_profile end end - def self.detect_projects - return if Gym.config[:project].to_s.length > 0 - - if Gym.config[:workspace].to_s.length == 0 - workspace = Dir["./*.xcworkspace"] - if workspace.count > 1 - puts "Select Workspace: " - Gym.config[:workspace] = choose(*(workspace)) - else - Gym.config[:workspace] = workspace.first # this will result in nil if no files were found - end - end - - return if Gym.config[:workspace].to_s.length > 0 - - if Gym.config[:workspace].to_s.length == 0 and Gym.config[:project].to_s.length == 0 - project = Dir["./*.xcodeproj"] - if project.count > 1 - puts "Select Project: " - Gym.config[:project] = choose(*(project)) - else - Gym.config[:project] = project.first # this will result in nil if no files were found - end - end - end - - def self.choose_project - loop do - path = ask("Couldn't automatically detect the project file, please provide a path: ".yellow).strip - if File.directory? path - if path.end_with? ".xcworkspace" - config[:workspace] = path - break - elsif path.end_with? ".xcodeproj" - config[:project] = path - break - else - Helper.log.error "Path must end with either .xcworkspace or .xcodeproj" - end - else - Helper.log.error "Couldn't find project at path '#{File.expand_path(path)}'".red - end - end - end - def self.detect_scheme Gym.project.select_scheme end From 58222ea81493266dce3fc3bd79092bbc674e1b04 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Sun, 11 Oct 2015 17:49:24 -0700 Subject: [PATCH 083/176] Updated travis file to install bundler --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index db8d8c5..86a16f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ language: objective-c osx_image: xcode6.4 -before_install: gem update --system +before_install: + - gem update --system + - gem install bundler rvm: - 2.0.0 - 2.1.6 From 63ea7c2573f688eb289a32eccc37335f73f08795 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Sun, 11 Oct 2015 17:51:13 -0700 Subject: [PATCH 084/176] Updated build_command_generator to use new Project API --- lib/gym/generators/build_command_generator.rb | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/gym/generators/build_command_generator.rb b/lib/gym/generators/build_command_generator.rb index e941b45..82f1c88 100644 --- a/lib/gym/generators/build_command_generator.rb +++ b/lib/gym/generators/build_command_generator.rb @@ -21,12 +21,7 @@ def prefix # This will also include the scheme (if given) # @return [Array] The array with all the components to join def project_path_array - config = Gym.config - proj = [] - proj << "-workspace '#{config[:workspace]}'" if config[:workspace] - proj << "-scheme '#{config[:scheme]}'" if config[:scheme] - proj << "-project '#{config[:project]}'" if config[:project] - + proj = Gym.project.xcodebuild_parameters return proj if proj.count > 0 raise "No project/workspace found" end From 5ddbc5a73ba37eb91cd15a62c7a324be1a727170 Mon Sep 17 00:00:00 2001 From: Christopher Bowns Date: Mon, 12 Oct 2015 18:13:37 -0700 Subject: [PATCH 085/176] Update README.md Fix the broken example Gymfile's "9.0" SDK. (I wish I'd used `gym init` instead of copying and pasting this example.) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f9464bc..110fc57 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ Run `gym init` to create a new configuration file. Example: ```ruby scheme "Example" -sdk "9.0" +sdk "iphoneos9.0" clean true From 016f89cab6948fedbf2f1a9f8ffc16694a4c46e9 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Tue, 13 Oct 2015 14:34:46 -0700 Subject: [PATCH 086/176] Removed outdated information in documentation --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index f9464bc..673dad6 100644 --- a/README.md +++ b/README.md @@ -100,8 +100,6 @@ gym # Installation -This tool is still work in progress. You can already try it by cloning the repo and running - sudo gem install gym Make sure, you have the latest version of the Xcode command line tools installed: From 7db8c3c257a07d859327583614d1f332e2c882cb Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Tue, 13 Oct 2015 18:28:37 -0700 Subject: [PATCH 087/176] Fixed regex encoding issues --- lib/gym/generators/package_command_generator.rb | 5 +++++ lib/gym/generators/package_command_generator_legacy.rb | 5 +++++ lib/gym/generators/package_command_generator_xcode7.rb | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/lib/gym/generators/package_command_generator.rb b/lib/gym/generators/package_command_generator.rb index 6f34813..30e03fe 100644 --- a/lib/gym/generators/package_command_generator.rb +++ b/lib/gym/generators/package_command_generator.rb @@ -1,3 +1,8 @@ +# encoding: utf-8 +# from http://stackoverflow.com/a/9857493/445598 +# because of +# `incompatible encoding regexp match (UTF-8 regexp with ASCII-8BIT string) (Encoding::CompatibilityError)` + require 'shellwords' # The concrete implementations diff --git a/lib/gym/generators/package_command_generator_legacy.rb b/lib/gym/generators/package_command_generator_legacy.rb index 690bc88..25b2a7e 100644 --- a/lib/gym/generators/package_command_generator_legacy.rb +++ b/lib/gym/generators/package_command_generator_legacy.rb @@ -1,3 +1,8 @@ +# encoding: utf-8 +# from http://stackoverflow.com/a/9857493/445598 +# because of +# `incompatible encoding regexp match (UTF-8 regexp with ASCII-8BIT string) (Encoding::CompatibilityError)` + module Gym # Responsible for building the fully working xcodebuild command on Xcode < 7 # diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index 08114b3..008f1fa 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -1,3 +1,8 @@ +# encoding: utf-8 +# from http://stackoverflow.com/a/9857493/445598 +# because of +# `incompatible encoding regexp match (UTF-8 regexp with ASCII-8BIT string) (Encoding::CompatibilityError)` + module Gym # Responsible for building the fully working xcodebuild command class PackageCommandGeneratorXcode7 From 3050ab1e5a9eae5ddce167b59cf985508877e9b5 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Tue, 13 Oct 2015 18:28:40 -0700 Subject: [PATCH 088/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index d1fd9c0..4d40e3d 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "0.9.0" + VERSION = "0.9.1" DESCRIPTION = "Building your iOS apps has never been easier" end From f2c913224e3bd0b6b4cfa1e18ec00c55f8caf267 Mon Sep 17 00:00:00 2001 From: Brian Michel Date: Thu, 15 Oct 2015 17:07:57 -0400 Subject: [PATCH 089/176] Don't use string linteral for glob --- lib/gym/runner.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb index 8e958a3..a03ed99 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -114,7 +114,7 @@ def compress_and_move_dsym Helper.log.info "Compressing #{available_dsyms.count} dSYM(s)" output_path = File.expand_path(File.join(Gym.config[:output_directory], Gym.config[:output_name] + ".app.dSYM.zip")) - command = "cd '#{containing_directory}' && zip -r '#{output_path}' '*.dSYM'" + command = "cd '#{containing_directory}' && zip -r '#{output_path}' *.dSYM" Helper.log.info command.yellow unless Gym.config[:silent] command_result = `#{command}` Helper.log.info command_result if $verbose From 731bb2486f627448e5ff4d9d8e6e00a061ac1b37 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Thu, 15 Oct 2015 14:47:39 -0700 Subject: [PATCH 090/176] Improvements --- lib/assets/GymfileTemplate | 3 +-- lib/gym/commands_generator.rb | 2 +- lib/gym/error_handler.rb | 5 +++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/assets/GymfileTemplate b/lib/assets/GymfileTemplate index bf74edd..223f931 100644 --- a/lib/assets/GymfileTemplate +++ b/lib/assets/GymfileTemplate @@ -4,8 +4,7 @@ # In general, you can use the options available # gym --help -# Remove the # in front of the line to enable this -# particular option +# Remove the # in front of the line to enable the option # scheme "Example" diff --git a/lib/gym/commands_generator.rb b/lib/gym/commands_generator.rb index bb209ed..d626b74 100644 --- a/lib/gym/commands_generator.rb +++ b/lib/gym/commands_generator.rb @@ -48,7 +48,7 @@ def run c.action do |_args, options| containing = (File.directory?("fastlane") ? 'fastlane' : '.') path = File.join(containing, Gym.gymfile_name) - raise "Gymfile already exists" if File.exist?(path) + raise "Gymfile already exists".yellow if File.exist?(path) template = File.read("#{Helper.gem_path('gym')}/lib/assets/GymfileTemplate") File.write(path, template) Helper.log.info "Successfully created '#{path}'. Open the file using a code editor.".green diff --git a/lib/gym/error_handler.rb b/lib/gym/error_handler.rb index 0a00662..d0a14c4 100644 --- a/lib/gym/error_handler.rb +++ b/lib/gym/error_handler.rb @@ -2,7 +2,7 @@ module Gym # This classes methods are called when something goes wrong in the building process class ErrorHandler class << self - # @param [Array] The output of the errored build (line by line) + # @param [String] The output of the errored build # This method should raise an exception in any case, as the return code indicated a failed build def handle_build_error(output) # The order of the handling below is import @@ -47,7 +47,7 @@ def handle_build_error(output) print "You'll have to restart your shell session after updating the file." print "If you are using zshell or another shell, make sure to edit the correct bash file." print "For more information visit this stackoverflow answer:" - print "http://stackoverflow.com/a/17031697/445598" + print "https://stackoverflow.com/a/17031697/445598" end raise "Error building the application - see the log above".red end @@ -90,6 +90,7 @@ def handle_empty_archive print "The generated archive is invalid, this can have various reasons:" print "Usually it's caused by the `Skip Install` option in Xcode, set it to `NO`" print "For more information visit https://developer.apple.com/library/ios/technotes/tn2215/_index.html" + print "Also, make sure to have a valid code signing identity and provisioning profile installed" raise "Archive invalid" end From 35918a954bee93e7f549329ad89e25cfce9d7b12 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Thu, 15 Oct 2015 17:51:04 -0700 Subject: [PATCH 091/176] Updated fastlane_core dependency --- gym.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gym.gemspec b/gym.gemspec index 7876b8e..fd21a47 100644 --- a/gym.gemspec +++ b/gym.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency 'fastlane_core', '>= 0.21.0', '< 1.0.0' # all shared code and dependencies + spec.add_dependency 'fastlane_core', '>= 0.22.3', '< 1.0.0' # all shared code and dependencies spec.add_dependency 'xcpretty' # pretty xcodebuild output spec.add_dependency 'terminal-table' # print out build information spec.add_dependency 'plist' # Generate the Xcode config plist file From 1945c0c6edbb353e25a869411525bce64dc0fd39 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Thu, 15 Oct 2015 17:51:24 -0700 Subject: [PATCH 092/176] Improved handling when archive is invalid due to wrong code signing --- lib/gym/error_handler.rb | 1 + lib/gym/generators/package_command_generator_xcode7.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/gym/error_handler.rb b/lib/gym/error_handler.rb index d0a14c4..0970722 100644 --- a/lib/gym/error_handler.rb +++ b/lib/gym/error_handler.rb @@ -91,6 +91,7 @@ def handle_empty_archive print "Usually it's caused by the `Skip Install` option in Xcode, set it to `NO`" print "For more information visit https://developer.apple.com/library/ios/technotes/tn2215/_index.html" print "Also, make sure to have a valid code signing identity and provisioning profile installed" + print "Follow this guide to setup code signing https://github.com/KrauseFx/fastlane/blob/master/docs/CodeSigning.md" raise "Archive invalid" end diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index 008f1fa..6ba17dc 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -41,6 +41,7 @@ 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 From 27f876d0e6adcad041879ecedef9c99b1e2610c9 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Mon, 19 Oct 2015 15:16:55 -0700 Subject: [PATCH 093/176] Updated dependencies --- gym.gemspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gym.gemspec b/gym.gemspec index fd21a47..927dfbd 100644 --- a/gym.gemspec +++ b/gym.gemspec @@ -21,8 +21,8 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency 'fastlane_core', '>= 0.22.3', '< 1.0.0' # all shared code and dependencies - spec.add_dependency 'xcpretty' # pretty xcodebuild output + spec.add_dependency 'fastlane_core', '>= 0.25.0', '< 1.0.0' # all shared code and dependencies + spec.add_dependency 'xcpretty', '~> 0.1' # pretty xcodebuild output spec.add_dependency 'terminal-table' # print out build information spec.add_dependency 'plist' # Generate the Xcode config plist file spec.add_dependency 'rubyzip', '>= 1.1.7' # fix swift/ipa From b1fdb8892439e10cb47657eccfddd860846e52ce Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Mon, 19 Oct 2015 15:20:32 -0700 Subject: [PATCH 094/176] Changed the order of the available options --- lib/gym/options.rb | 69 +++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/lib/gym/options.rb b/lib/gym/options.rb index 6fd9b2a..8743e2b 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -49,11 +49,6 @@ def self.plain_options env_name: "GYM_OUTPUT_DIRECTORY", description: "The directory in which the ipa file should be stored in", default_value: "."), - FastlaneCore::ConfigItem.new(key: :archive_path, - short_option: "-b", - env_name: "GYM_ARCHIVE_PATH", - description: "The directory in which the archive file should be stored in", - optional: true), FastlaneCore::ConfigItem.new(key: :output_name, short_option: "-n", env_name: "GYM_OUTPUT_NAME", @@ -62,16 +57,6 @@ def self.plain_options verify_block: proc do |value| value.gsub!(".ipa", "") end), - FastlaneCore::ConfigItem.new(key: :buildlog_path, - short_option: "-l", - env_name: "GYM_BUILDLOG_PATH", - description: "The directory where to store the build log", - default_value: "~/Library/Logs/gym"), - FastlaneCore::ConfigItem.new(key: :sdk, - short_option: "-k", - env_name: "GYM_SDK", - description: "The SDK that should be used for building the application", - optional: true), FastlaneCore::ConfigItem.new(key: :configuration, short_option: "-q", env_name: "GYM_CONFIGURATION", @@ -88,24 +73,6 @@ def self.plain_options env_name: "GYM_CODE_SIGNING_IDENTITY", description: "The name of the code signing identity to use. It has to match the name exactly. e.g. 'iPhone Distribution: SunApps GmbH'", optional: true), - FastlaneCore::ConfigItem.new(key: :destination, - short_option: "-d", - env_name: "GYM_DESTINATION", - description: "Use a custom destination for building the app", - optional: true), - FastlaneCore::ConfigItem.new(key: :xcargs, - short_option: "-x", - env_name: "GYM_XCARGS", - description: "Pass additional arguments to xcodebuild. Be sure to quote the setting names and values e.g. OTHER_LDFLAGS=\"-ObjC -lstdc++\"", - optional: true), - FastlaneCore::ConfigItem.new(key: :xcconfig, - short_option: "-y", - env_name: "GYM_XCCONFIG", - description: "Use an extra XCCONFIG file to build your app", - optional: true, - verify_block: proc do |value| - raise "File not found at path '#{File.expand_path(value)}'".red unless File.exist?(value) - end), FastlaneCore::ConfigItem.new(key: :include_symbols, short_option: "-m", env_name: "GYM_INCLUDE_SYMBOLS", @@ -138,6 +105,23 @@ def self.plain_options av = %w(app-store ad-hoc package enterprise development developer-id) raise "Unsupported export_method, must be: #{av}" unless av.include?(value) end), + + # Very optional + FastlaneCore::ConfigItem.new(key: :archive_path, + short_option: "-b", + env_name: "GYM_ARCHIVE_PATH", + description: "The directory in which the archive file should be stored in", + optional: true), + FastlaneCore::ConfigItem.new(key: :buildlog_path, + short_option: "-l", + env_name: "GYM_BUILDLOG_PATH", + description: "The directory where to store the build log", + default_value: "~/Library/Logs/gym"), + FastlaneCore::ConfigItem.new(key: :sdk, + short_option: "-k", + env_name: "GYM_SDK", + description: "The SDK that should be used for building the application", + optional: true), FastlaneCore::ConfigItem.new(key: :provisioning_profile_path, short_option: "-e", env_name: "GYM_PROVISIONING_PROFILE_PATH", @@ -145,8 +129,25 @@ def self.plain_options optional: true, verify_block: proc do |value| raise "Provisioning profile not found at path '#{File.expand_path(value)}'".red unless File.exist?(value) + end), + FastlaneCore::ConfigItem.new(key: :destination, + short_option: "-d", + env_name: "GYM_DESTINATION", + description: "Use a custom destination for building the app", + optional: true), + FastlaneCore::ConfigItem.new(key: :xcargs, + short_option: "-x", + env_name: "GYM_XCARGS", + description: "Pass additional arguments to xcodebuild. Be sure to quote the setting names and values e.g. OTHER_LDFLAGS=\"-ObjC -lstdc++\"", + optional: true), + FastlaneCore::ConfigItem.new(key: :xcconfig, + short_option: "-y", + env_name: "GYM_XCCONFIG", + description: "Use an extra XCCONFIG file to build your app", + optional: true, + verify_block: proc do |value| + raise "File not found at path '#{File.expand_path(value)}'".red unless File.exist?(value) end) - ] end end From 9a90676adfaa2f5217bf33c487d009778cba7eef Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Mon, 19 Oct 2015 15:20:47 -0700 Subject: [PATCH 095/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index 4d40e3d..5178614 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "0.9.1" + VERSION = "1.0.0" DESCRIPTION = "Building your iOS apps has never been easier" end From f4c35320c7828b6d8fba566d8d6c63555a7957d4 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Mon, 19 Oct 2015 15:26:50 -0700 Subject: [PATCH 096/176] Updated fastlane dependency --- gym.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gym.gemspec b/gym.gemspec index 927dfbd..3d48009 100644 --- a/gym.gemspec +++ b/gym.gemspec @@ -29,7 +29,7 @@ Gem::Specification.new do |spec| # Development only spec.add_development_dependency "bundler" - spec.add_development_dependency "fastlane", ">= 1.25.0" # yes, we use fastlane for testing + spec.add_development_dependency "fastlane", ">= 1.33.0" # yes, we use fastlane for testing spec.add_development_dependency "rake" spec.add_development_dependency "rubocop" spec.add_development_dependency "rspec", "~> 3.1.0" From 2a73bee04b1dec2df9f5e52d6cdc84ded1360a58 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Tue, 20 Oct 2015 14:33:33 -0700 Subject: [PATCH 097/176] Added scan --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 234dea7..3f79903 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,14 @@ snapshotframeitPEM • - sigh • + sighproducecert • - codesspaceshippilotboarding • - gym + gym • + scan

------- @@ -229,10 +229,10 @@ Afterwards the `ipa` file is moved to the output folder. The `dSYM` file is comp - [`PEM`](https://github.com/KrauseFx/pem): Automatically generate and renew your push notification profiles - [`produce`](https://github.com/KrauseFx/produce): Create new iOS apps on iTunes Connect and Dev Portal using the command line - [`cert`](https://github.com/KrauseFx/cert): Automatically create and maintain iOS code signing certificates -- [`codes`](https://github.com/KrauseFx/codes): Create promo codes for iOS Apps using the command line - [`spaceship`](https://github.com/fastlane/spaceship): Ruby library to access the Apple Dev Center and iTunes Connect - [`pilot`](https://github.com/fastlane/pilot): The best way to manage your TestFlight testers and builds from your terminal - [`boarding`](https://github.com/fastlane/boarding): The easiest way to invite your TestFlight beta testers +- [`scan`](https://github.com/fastlane/scan): The easiest way to run tests of your iOS and Mac app ##### [Like this tool? Be the first to know about updates and new fastlane tools](https://tinyletter.com/krausefx) From c92cec3dfb703125d2d582a86d661509710459ce Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Tue, 20 Oct 2015 14:43:01 -0700 Subject: [PATCH 098/176] Added new logo --- README.md | 2 +- assets/gym.png | Bin 4267 -> 39004 bytes assets/gym.pxm | Bin 281830 -> 0 bytes assets/gymFullSize.png | Bin 17600 -> 0 bytes 4 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 assets/gym.pxm delete mode 100644 assets/gymFullSize.png diff --git a/README.md b/README.md index 3f79903..ad1555d 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ -------

- +

gym diff --git a/assets/gym.png b/assets/gym.png index 97d7532211921baf9f303e305e9ca411cc9d1708..8a3d16ef14d072766d1da2c5b9edc736ff6ad47c 100644 GIT binary patch literal 39004 zcmaI7S3pzS)-W1%BPs$aEh;l!!=Z(n1MI?uzF--@On2{e6+FQRirL%(3QrYGS0#dgjs@5D3Jot8>Q`1Y)9q zK*!5Yodljh$=T|_e;0$atb@#uu0f$r0WKi*2S{fZL0umwHy2YErw8HwUtCl`pc7sm z=GH;hh6c(Aq>q#n14k;%#}A+efmC2&eohE4mmooB7dH=IRiTYmoRFZ$163g_IYVhf zKTQ{R51ohr7qbW>b3}v}Lg|4JOifTFOc`L{;}YZ~80O>c8>k$nD)g7HGVnL!F+@o4 zFG`S?s?dLwvNkjk)IxNneCn51vu)&Fw1;}VDn@bC-rK>7+YI665agM(Cs08Rh11Rp;`!~bRM8~8tl z0!#)8bMk{grKBM~K8$t!#SRQIb@_kQ_&;I?nuq(jKulc%k--57z#pzx|4R(m?*A@` zfe2`$Y#iVLIK|2P4iXXUn!V}h*115pZ^oR)?IlySy>G^c?~6bC{#;M33^xYj-0fHhPtNwT_ttd z|I+IE1_n9#B3%9}+XImOKeV#{S6XGw02ikqWPmvm>HXg*FmXo)Ap_l!euA2sf+B`a z2oGPzZ&8Mi{VnJWHU?5#r;UXw#q} z&azKo8t5?#UfR-BSkx{d-ql%J_$*rAFtYLQ)}nDo%M}knmpVVQG_ZD=$z(GPV>&L6?F$l*RE%(D80X89YaM zp;zkuc4A-Q+G&=f(4^=5+^eaZ$zVF=UszdTX!j%!aRo}l`yL0VhYS;`x9v+_?ifGa z90hYg$9oi86L*%(Kf{XS!foy?BAg58>&UL6eVhUwZuy-5`0PbuFSO&*N#rF)0UOc) z$c=iP^Wfd#+IPfA0ynghviKiYlrOlO4mRw7zNcJW(6>;Lh-V=8VHdS=g%@U5j@cy<2a6m76=eq-xdy{{omKUyIsoy!4)4aA$#_>!6;rL$^s_v zOc+3p-85NESl5#bH@%0tdG^c^c7k?#xbxq<`)gLBv|u!jHEy0RkXj>tHj!>iXK+XV z>kJFh7V9v=hxv11*(saD8p6x!!?kbsv0%Ca-Uq5?+8qRLV`boe5E3D4Mq)H*F3^tF z#HDU)VaZW+g5rqj<8*`1G&q73X8xy~?Ic+N#?yT$#BJ9A}x!*OpX%%2}pQDXQFtIhCb zhJC|iyJOr7IJz9k+u~i0C+Uwfh60w4jR%(N)Y9BK9r5%H{Ju3Bmn8(?7OikniZ5m* zs1v9t@JTJ&4V}|`^5=;?Z}-ckMGomkXjtOrvmF2tWM4ALz8f3>-XWkYz;(ZEM6dq= zFMph95L;)DlZOfXyIBk-WbrdtiUTYihJ`SYr<*O(FFvB)XMA{6S&@>@i5)6*J)*m! zVd%|we|h?pPO>lZ0q*K7y8Uu`r{ehq>-RfFtMwMMJj6ys8w5{J+(coR0jBPkyJJL_ zE<)$QL^)`I1fR70^0Dbu^UZoNj*Hk1RfEU@NVn~HAxzIK#FS}aXdr~7@G$l>)Mq2< z<;3;CG5>wy#~E1$7K;eEYd`-SaS=K%KwJlJpf?RX9xfEv@TG=Y^(^(46S)|x@UPo^ zxH)x>NCG1hYmAhO8nSx!X=0{7=yT9RXouKed1f!B*k5P5GU0^q{m}J)H^+;H*bTQz zNKMoLH01e%G|*?9fn>(@yvVq~P|25*;uMC(?KOZ4pF;UgMt9U7#FF)U z4$2Wx70R^8u>$}JRHWD)W8Eav=@_zM0G5~!$7bAi!w~7JV+VryJmY$&0Th!oXIS4x z8d%|pdU*j>`u8CFxwqTu%8=E{0gh~)zZj1I49Q(0P6@vbwqjmWj%}8#9^hy#-^RO& zGSm$)T}|lW=K!oMboOu)CxQ1LJ}xojKNfy9A4aQFE^HcSge#&9bnJx3kA4UAV9(@5 zqLS%xiEp*#+h3N-w|j5?<%8F%DXh}(ZWKuN*9J>8gcoOku7)@T#~cKp6_~Y901wBr zoRsCaqwrf?a~&6mR*1Z-*FU_#Z1+{0FXbyRufzf;x9s#{ z`v$58V4>zqSO3H03&13CRG!$=Rn$c8RevrZvaw2v+Q}pxm9(=19NxJnQiGfX2;4-7 zbDNCF)`{KPDrHzSRvF$be*%DOnwb<;rX=@75VUQ&T4pGfy!itx@YFv(2*eud60An?3yxv% zevuB4`dOkwdz!A}lQ1XTU-DQjHHKO(-2#BIJtnJVUyPn^j!RpQZW)r@iWVnU6fBln z3$X(X^MFvs{u!m~jkQ|Ug)E5jHjUKP18Bs<7DZ3E(+?^tafW;jlS zJi>{6w>u9MLkaWK(ksKHw*C3<$3y^7P>wS!IMf-9;wF;8$bbFwGC(R{{l^YA$)u?8 zR~Z1t?L85)nHwaf<`y1;w-)6C_0+vrhObu*6f%Gb08kst)lES}DdOXYk4a~+ly5Il z;>Ohg=+MPP>ZlJYDi969@5d~ItkX8C2g;Bk?Lc zOztStJFvnvZN`)!2KnYgNl`Ce*~HxuV|(W@|4Uk3$Khs08*c{o%Jdneqp8j?{mo8rE22$945a*nYb+dP`d1wXPKph>r+fbZERX_}KaleE z{5_bdJv09eK7h`IJ&}5LB@a9TjSgSg#$vmJG zf7s?YDPr@^G60Lc2_JBh8f2T60lo9HvY1GA(`N|!iJ2U{A0veCLF7qtfzJL|B^_?2 z|8>HF2syAM-FXLbWQyj%oQT!ZnMe&apGZ$HVI&;hxTdhHGO0!!FOLN|{?lMFks17# zj0iBSSn3ASkjwPghf|o9uZ30<7Xb=K15S!=j3SR&ojkF zC|9)@%~{(w0RPZXdKnCPeg<^untfx4 z#RQZAc*seaSv~?6{<~>_1rQX@e;0g1hY2JZq7mFqar^7MRLms78zw-6bt@W)fqusZ zwNtvmIKYM=&>x_>_h%&X%^;4@=Aa>UdT9$=#3Z9tRaj8n)Ie`mD7)6Gyu8sE--IYr?0aO%7qrvU@;t{0k5XtRs~ zOB(V31U)+gzU91Aava3}+RFY0*AYPQTf!%b1mTj3k-@VAYWFQCyrtyqY$Gv@GE@bv z#m$=*({W7kF(B)ICqDgVmibF0*iAdsi%*O}PXLi2F}Z>`vIR?cGPKJto`f4?Z*Q(R znY;smo&*M?>6bYF(wqZmbmO1GS-FW!u^6D;Q22+55sJzhHMkg>85$+V3VQbdc9F<~ zVN?exlhx|%#-1k2OCZMwF=F^ZU4~bs+!r77k*4P86 z-owG@XT%zRcy|3Vn)7bEv=0 zs7rQ6DOut7Z=)9hzfF~_tTUIdNHa))$iKMMz26Ql2aNaL&c;4r5b!<-G_6qs!`;B| zo9uFc{8WMAPV9BF)xnjYj>1Y;yq11;q-RtO~T#!Ng3! zuF2U|OE?imfXY5}rn|R8@%6ydN`FJN&tI$0iIP(XYMqa)Jb+#705TTLXuLi3Yf!QU zU}7B95{R+7fkcur{SFoY6jgCjvVCpPbbg{Y79h}@I3QrFfKeI9*elV8FxCL}&VfJvY&+1bDb)^_D>odc)dH>$c%I z8GUG+b_Vo2FNjLdC&e%zp6WHIFkk~gLgfK%g2wNl>5VNA01&&tNlDnNMzk3KGPfOU z=XijJxBU~TT~oZoC4l*xqx->(kOgUpk|DFx;Eg2zM?mgpa=fL))&F&{cy8G z0dE8R=Dji}aCCPFCjjt%#D9Z{Woralm3Hz?qnsBaPi=pjG@@Z$|7(ceKstS;;=XE2 zO-IO5F$}fz@f?}H_rKM_Fr7^_eF;U2N?E%PpPw(MTX z3ko>pH4*N&;V~ z%cy0|qGW-YuVCCo#7pzr{Iy!tU1L?*%k-W4O~L!~Z3Gwc!w;*2;_P;v+w+kcP4wHR z_M%iDQ$tz#&xTzET|(Lf6Z~w@l)sfCkCT$R2DHH9tkV1mJmkW*E04{rdaS3C3Di27 zlQln%H0kR<5;XRI(LUyamXCd~>Hv4f5#ATx?kb@?pPcXQ0)Qm1_ z%<>~IP}5rBTYe`^1JlSeYzIncJQeYsc5XfAv=hNfW1j?nL1ky%YyqP<5ebYU{4B|t z{taUjj+0=0C+qQxrj;$a4$=BL#gu(kNVl6_Jag)v8xew z{RR-21U>DG6L4{6P!RqO*O#BX^cYdLPm`)WFo6S{`iysC&7lZNZ7b~ZBDA6PF!W2E z;j^CLnOyoU5i)EXhu*yJ5gXsSzjbZ^H@Ql^U^+tmkTD@H;qnW7DN$+E*_lM2c!@XP?REne~KYY0<2YZ~q zc#`g(jStI}{ctpVmL`6KS#{s6I{d3X(zN6iu9Lo?y25nj{!!m%YT%%PM$hT}19!S+t&{iJ)R;ggWUxU$ zKRl(9+jLbR%n9vPa*XX|em82lwtm?osgBQ$XRkdHeyWkyJVkqUuVoO}5(I#q;CICr zX_56ZYY1PUUh|Y?cVuVr)E8>e8hdKP;X8yFAH2Ne!8~F`^2bnIeQsxQf~U;z2oQ5u zRMha*JwQ_+_;_3D%#Sp2ji&Dxn%!%6vhF4>wTV22M9Dukj=J`k8Fw8beRo-nI0qe` z8vAwV}PC+oLG zXSy!XHu|>#67^&b;N%l=FX#LewNT)4VmT#MHt?7Fvh^rWhbdFW4wKuoaO`-Fm|x5k z#}+4rko6<$k+@*WlZY!M2Qe;WBOdocLDL8KyU1Kx-s42RCPhU$4Ok^ZAUPtkVnB6&zZT4b==t2x<9{rI=}a~=`O#id@3F0mz9DYP5YuT zzMIA0S<*q1ar#4!rx0_Nooncyz#A$4kNS;yN)}doQGO;B7C+rYO~285VuT;__*m>D zoL4Mv5m34(_GBEQ7-D=_oT=Y+W0U%Kor z!*+%;>fdmdE!L~XGKY|c<>g0p`@UiWzBfZN&g-%qI9x<#(l?FS$q+}~Vd`gC_LJgW zmXzq^9nBSkCpRmF4owMQ4yQkU!dyv`Z8P4rTJ-#}5Da=u{W;FiHHqdCU0x8BF;VYK zH*;Olo`+n zn?CbyoSOPujhxAKMHdpSVNQG=bDYObDBHYfoTq#;FnP9`CVTlH zZp?rAP%VPP!WRbqT4(S&Z|9_8#EVi^SJ%{)&l@j|Ch1eZN-KxGW3*@wTlZLF=C_m2 z@kjXmS!#!??cfM|{a{MAnvth{?W+jm-zEEjYb_rONBaIuJ@TKJuhNlx?Qqa?u3)nw zOkwZx6_tHS%GI-1=E{!L-&qr-hx;b?I4|9b_IUaJbkSx66TVIKY~q*E-aEne->S-5 z8_auLl_6n*3e%M9>;o<0K9sdF|4iq`3^d#-kvh?%*!!#0$MoYtB>Zxxhg;CfUdLX+ zxpmmNPE|O&6W#tw)DqCY_}k47`c34g(w6LCs(Yy1`IZ;ag75yePIK$b$1V>D^lj1O z2udLivD!-IE%5MKa|!E1p%~3#$g}pMNp?ep^Is(zrn3*H?>&29In6wLc86~)#_0ln zVX&I&sP4t4TN$l(nYgxI+*8fcO>^tsKcAb^5I(Q35jMawg99POR#85}k0aL;Lv8nd zK}e~ct8QxV|FpZV75y&cKc44rd1__6(JeA(kv%DG-3q8rN#=bUEo(nM>`tg{X>Ca| zJu$8>F0*8JrKtz*ob|;jUF#cLPPvt%!4mpVI=Aqk-`^C?s$nUV(SLOWM4qCoo18Ur z6#(_=v4;*sN1R%M^ET4&$TU({CFp?zEI)qjP2r_e-K_Q?w@RF2CVbdsCg2nu`pN~> ztdSR+ecqNAWQQJz)SCQcHHF_7(v!X69LW`XV$AdlDw(hm{Ax5RuOy{&u@oy z8NbDA_j=-&GfM0!H8S@_L)bXWLStHX0d~Nt@25c`k6Cd zibSR`MAB#C${&Qs;X8VCP0!@>9^0Gux>q-|>JEHLk}cMUH$0Qk=iR+(=#RiKkL}?g zY<#a5?Th323E_zS;&UD6AWtLJltfHqY?q65#SR4=_HnDtyU1OcneiKv21ob!4jrYI z)r8ySV{*+U_IrPkj}uFo&$bu+%WRl-hT7~_#DDhiLiWvjiyemX%jQ7_Of@g1MdSw_ zZ??+?g#B1^r~E0s`f6?MxxA&))TI{t4DY~O74`{y@YbHFsQ%yzKJkqN0VIdy^vUu^ zayRdmR(8|iDgLke_0usSk_5`MtJF{q_f-4tpk~t|u>m2Kz(X5s6?v>*^@ctpXoa6i zlNm@1eTsv+Tm7S=*OGdG0YlOlt~}GgbE$Pn|Fo=L^@kT&5m#Zqm~@Nv+*yZfsv
or;MNcd7Ann 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

WG-%^rfQnH0L}PPpq%pd?6-X8Xw{DcDDA=jIe*v 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 crjcKEqjaxcTyk;{ElpEJhFYtj_$w8S*Zj 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 LbG=4gP{RKK3CV1- diff --git a/assets/gym.pxm b/assets/gym.pxm deleted file mode 100644 index 3bf7158267a0623f38865c0cbcc9782a061f488e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 281830 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 z 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 diff --git a/assets/gymFullSize.png b/assets/gymFullSize.png deleted file mode 100644 index 73a7c585e509c491a503141da8755e77f325cb91..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17600 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 From b0aecd479d2bb97451822355653441e06b8efae6 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Fri, 23 Oct 2015 12:06:41 -0700 Subject: [PATCH 099/176] Updated size of logo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ad1555d..872da76 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ -------

- +

gym From b9dd3d643dc5711d701a3a72df57f669e7f7c39e Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Fri, 23 Oct 2015 14:24:45 -0700 Subject: [PATCH 100/176] Added export_team_id option --- lib/gym/generators/package_command_generator_xcode7.rb | 1 + lib/gym/options.rb | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index 6ba17dc..37e30ee 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -70,6 +70,7 @@ def config_content if Gym.config[:export_method] == 'app-store' hash[:uploadSymbols] = (Gym.config[:include_symbols] ? true : false) hash[:uploadBitcode] = (Gym.config[:include_bitcode] ? true : false) + hash[:teamID] = Gym.config[:export_team_id] if Gym.config[:export_team_id] end hash.to_plist diff --git a/lib/gym/options.rb b/lib/gym/options.rb index 8743e2b..f1524c2 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -135,6 +135,11 @@ def self.plain_options env_name: "GYM_DESTINATION", description: "Use a custom destination for building the app", optional: true), + FastlaneCore::ConfigItem.new(key: :export_team_id, + short_option: "-g", + env_name: "GYM_EXPORT_TEAM_ID", + description: "Optional: Sometimes you need to specify a team id when exporting the ipa file", + optional: true), FastlaneCore::ConfigItem.new(key: :xcargs, short_option: "-x", env_name: "GYM_XCARGS", From 3d489de3203a2c7651bdebdebdc66f1d42b5886e Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Fri, 23 Oct 2015 14:35:08 -0700 Subject: [PATCH 101/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index 5178614..11caf06 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "1.0.0" + VERSION = "1.1.0" DESCRIPTION = "Building your iOS apps has never been easier" end From 551d485617367242f25cd6b2755e2ffed6ebb67b Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Sat, 24 Oct 2015 14:23:09 -0700 Subject: [PATCH 102/176] Added legacy api note --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 872da76..c7c1ee3 100644 --- a/README.md +++ b/README.md @@ -124,12 +124,15 @@ For a list of all available parameters use If you run into any issues, use the `verbose` mode to get more information - gym --verbose +In general, if you run into issues while exporting the archive, try using: + + gym --use_legacy_build_api true + To pass boolean parameters make sure to use `gym` like this: - gym --include_bitcode true --include_symbols true + gym --include_bitcode true --include_symbols false To access the raw `xcodebuild` output open `~/Library/Logs/gym` From 7ed5f2571932d1be5b0fd44a4d184ee377dcc056 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Mon, 26 Oct 2015 14:02:15 -0700 Subject: [PATCH 103/176] Support for export_team_id for non App Store builds Fixes https://github.com/fastlane/gym/issues/129 --- lib/gym/generators/package_command_generator_xcode7.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index 37e30ee..4f6d57d 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -70,8 +70,8 @@ def config_content if Gym.config[:export_method] == 'app-store' hash[:uploadSymbols] = (Gym.config[:include_symbols] ? true : false) hash[:uploadBitcode] = (Gym.config[:include_bitcode] ? true : false) - hash[:teamID] = Gym.config[:export_team_id] if Gym.config[:export_team_id] end + hash[:teamID] = Gym.config[:export_team_id] if Gym.config[:export_team_id] hash.to_plist end From b2a0f397ddce93741bffa7a80e71a1171510c307 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Tue, 27 Oct 2015 11:30:28 -0700 Subject: [PATCH 104/176] Updated travis config --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 86a16f3..37d9486 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,5 +6,4 @@ before_install: rvm: - 2.0.0 - 2.1.6 - - 2.2.2 script: bundle exec fastlane test From 2b7e2f87b5a397d81ae2f67dbbeb0d65f9103d4c Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Tue, 27 Oct 2015 14:24:49 -0700 Subject: [PATCH 105/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index 11caf06..79d6376 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "1.1.0" + VERSION = "1.1.1" DESCRIPTION = "Building your iOS apps has never been easier" end From f664a92225364e24d807a7954f0f799735bf5ab9 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 28 Oct 2015 10:15:02 -0700 Subject: [PATCH 106/176] Fixed exporting the binary to a ~ folder Fixes https://github.com/fastlane/gym/issues/131 --- lib/gym/runner.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb index 5ef65d1..388f1c8 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -124,9 +124,8 @@ def compress_and_move_dsym # Moves over the binary and dsym file to the output directory # @return (String) The path to the resulting ipa file def move_ipa - FileUtils.mv(PackageCommandGenerator.ipa_path, Gym.config[:output_directory], force: true) - - ipa_path = File.join(Gym.config[:output_directory], File.basename(PackageCommandGenerator.ipa_path)) + FileUtils.mv(PackageCommandGenerator.ipa_path, File.expand_path(Gym.config[:output_directory]), force: true) + ipa_path = File.expand_path(File.join(Gym.config[:output_directory], File.basename(PackageCommandGenerator.ipa_path))) Helper.log.info "Successfully exported and signed the ipa file:".green Helper.log.info ipa_path @@ -138,7 +137,7 @@ def move_mac_app app_path = Dir[File.join(BuildCommandGenerator.archive_path, "Products/Applications/*.app")].last raise "Couldn't find application in '#{BuildCommandGenerator.archive_path}'".red unless app_path - FileUtils.mv(app_path, Gym.config[:output_directory], force: true) + FileUtils.mv(app_path, File.expand_path(Gym.config[:output_directory]), force: true) app_path = File.join(Gym.config[:output_directory], File.basename(app_path)) Helper.log.info "Successfully exported the .app file:".green From 5eee386aeeb83d004c1d3e4654275e98343e90cc Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 28 Oct 2015 10:21:07 -0700 Subject: [PATCH 107/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index 79d6376..b0dd2f5 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "1.1.1" + VERSION = "1.1.2" DESCRIPTION = "Building your iOS apps has never been easier" end From dddca18cd9cb959f1b0bf20ff476ba586787536a Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 28 Oct 2015 10:52:12 -0700 Subject: [PATCH 108/176] Fixed "The generated archive is invalid" Fixes https://github.com/fastlane/gym/issues/115#issuecomment-151901326 --- lib/gym/runner.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb index 388f1c8..d466101 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -86,7 +86,8 @@ def build_app # Makes sure the archive is there and valid def verify_archive - if Dir[BuildCommandGenerator.archive_path + "/*"].count == 0 + # from https://github.com/fastlane/gym/issues/115 + if (Dir[BuildCommandGenerator.archive_path + "/*"] + Dir[BuildCommandGenerator.archive_path + ".xcarchive/*"]).count == 0 ErrorHandler.handle_empty_archive end end From 5a589894474ce637ba2aac852e6720d97d14a226 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 28 Oct 2015 10:52:22 -0700 Subject: [PATCH 109/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index b0dd2f5..19d2146 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "1.1.2" + VERSION = "1.1.3" DESCRIPTION = "Building your iOS apps has never been easier" end From ffcd7678e90618b7a92507aa2ca4fee9a791a8b6 Mon Sep 17 00:00:00 2001 From: frankenbot Date: Wed, 28 Oct 2015 15:44:35 -0700 Subject: [PATCH 110/176] Update redirects --- README.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index c7c1ee3..f6328fb 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@

- deliver • - snapshot • - frameit • - PEM • - sigh • - produce • - cert • + deliver • + snapshot • + frameit • + PEM • + sigh • + produce • + certspaceshippilotboarding • @@ -29,7 +29,7 @@ gym ============ [![Twitter: @KauseFx](https://img.shields.io/badge/contact-@KrauseFx-blue.svg?style=flat)](https://twitter.com/KrauseFx) -[![License](http://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/fastlane/gym/blob/master/LICENSE) +[![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/fastlane/gym/blob/master/LICENSE) [![Gem](https://img.shields.io/gem/v/gym.svg?style=flat)](http://rubygems.org/gems/gym) [![Build Status](https://img.shields.io/travis/fastlane/gym/master.svg?style=flat)](https://travis-ci.org/fastlane/gym) @@ -169,7 +169,7 @@ end You can then easily switch between the beta provider (e.g. `testflight`, `hockey`, `s3` and more). -For more information visit the [fastlane GitHub page](https://github.com/KrauseFx/fastlane). +For more information visit the [fastlane GitHub page](https://github.com/fastlane/fastlane). # How does it work? @@ -226,12 +226,12 @@ Afterwards the `ipa` file is moved to the output folder. The `dSYM` file is comp ## [`fastlane`](https://fastlane.tools) Toolchain - [`fastlane`](https://fastlane.tools): Connect all deployment tools into one streamlined workflow -- [`deliver`](https://github.com/KrauseFx/deliver): Upload screenshots, metadata and your app to the App Store -- [`snapshot`](https://github.com/KrauseFx/snapshot): Automate taking localized screenshots of your iOS app on every device -- [`frameit`](https://github.com/KrauseFx/frameit): Quickly put your screenshots into the right device frames -- [`PEM`](https://github.com/KrauseFx/pem): Automatically generate and renew your push notification profiles -- [`produce`](https://github.com/KrauseFx/produce): Create new iOS apps on iTunes Connect and Dev Portal using the command line -- [`cert`](https://github.com/KrauseFx/cert): Automatically create and maintain iOS code signing certificates +- [`deliver`](https://github.com/fastlane/deliver): Upload screenshots, metadata and your app to the App Store +- [`snapshot`](https://github.com/fastlane/snapshot): Automate taking localized screenshots of your iOS app on every device +- [`frameit`](https://github.com/fastlane/frameit): Quickly put your screenshots into the right device frames +- [`PEM`](https://github.com/fastlane/PEM): Automatically generate and renew your push notification profiles +- [`produce`](https://github.com/fastlane/produce): Create new iOS apps on iTunes Connect and Dev Portal using the command line +- [`cert`](https://github.com/fastlane/cert): Automatically create and maintain iOS code signing certificates - [`spaceship`](https://github.com/fastlane/spaceship): Ruby library to access the Apple Dev Center and iTunes Connect - [`pilot`](https://github.com/fastlane/pilot): The best way to manage your TestFlight testers and builds from your terminal - [`boarding`](https://github.com/fastlane/boarding): The easiest way to invite your TestFlight beta testers From 25813b641a114dd9264d50dfa49ad4a7ec656040 Mon Sep 17 00:00:00 2001 From: Niket Shah Date: Thu, 29 Oct 2015 09:33:23 +0000 Subject: [PATCH 111/176] Fix for issue #115, 'Generated archive is invalid' --- lib/gym/generators/build_command_generator.rb | 3 +++ lib/gym/runner.rb | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/gym/generators/build_command_generator.rb b/lib/gym/generators/build_command_generator.rb index 82f1c88..c0794c9 100644 --- a/lib/gym/generators/build_command_generator.rb +++ b/lib/gym/generators/build_command_generator.rb @@ -86,6 +86,9 @@ def archive_path file_name = [Gym.config[:output_name], Time.now.strftime("%F %H.%M.%S")] # e.g. 2015-08-07 14.49.12 Gym.cache[:archive_path] = File.join(build_path, file_name.join(" ") + ".xcarchive") end + if File.extname(Gym.cache[:archive_path]) != ".xcarchive" + Gym.cache[:archive_path] += ".xcarchive" + end return Gym.cache[:archive_path] end end diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb index d466101..fdefe8f 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -87,7 +87,7 @@ def build_app # Makes sure the archive is there and valid def verify_archive # from https://github.com/fastlane/gym/issues/115 - if (Dir[BuildCommandGenerator.archive_path + "/*"] + Dir[BuildCommandGenerator.archive_path + ".xcarchive/*"]).count == 0 + if (Dir[BuildCommandGenerator.archive_path + "/*"]).count == 0 ErrorHandler.handle_empty_archive end end From b60a7a586a838a612efa19a79933a871cfa64ce2 Mon Sep 17 00:00:00 2001 From: KrauseFx Date: Thu, 29 Oct 2015 22:40:37 -0700 Subject: [PATCH 112/176] Updated travis to Xcode 7.1 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 86a16f3..5167d1f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: objective-c -osx_image: xcode6.4 +osx_image: xcode7.1 before_install: - gem update --system - gem install bundler From 4785c6310ed43d779825411e85546749ae12231f Mon Sep 17 00:00:00 2001 From: KrauseFx Date: Thu, 29 Oct 2015 22:40:44 -0700 Subject: [PATCH 113/176] Removed xcpretty version restriction --- gym.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gym.gemspec b/gym.gemspec index 3d48009..7aacc96 100644 --- a/gym.gemspec +++ b/gym.gemspec @@ -22,7 +22,7 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.add_dependency 'fastlane_core', '>= 0.25.0', '< 1.0.0' # all shared code and dependencies - spec.add_dependency 'xcpretty', '~> 0.1' # pretty xcodebuild output + spec.add_dependency 'xcpretty' # pretty xcodebuild output spec.add_dependency 'terminal-table' # print out build information spec.add_dependency 'plist' # Generate the Xcode config plist file spec.add_dependency 'rubyzip', '>= 1.1.7' # fix swift/ipa From e4a450d6016532b65631aa31183804410781267a Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Fri, 30 Oct 2015 08:21:56 -0700 Subject: [PATCH 114/176] Updated xcpretty dependency --- gym.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gym.gemspec b/gym.gemspec index 7aacc96..a69c429 100644 --- a/gym.gemspec +++ b/gym.gemspec @@ -22,7 +22,7 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.add_dependency 'fastlane_core', '>= 0.25.0', '< 1.0.0' # all shared code and dependencies - spec.add_dependency 'xcpretty' # pretty xcodebuild output + spec.add_dependency 'xcpretty', '>= 0.2.1' # pretty xcodebuild output spec.add_dependency 'terminal-table' # print out build information spec.add_dependency 'plist' # Generate the Xcode config plist file spec.add_dependency 'rubyzip', '>= 1.1.7' # fix swift/ipa From bc6e78b6f84ffbc78bd6dae61a9730f1cb441b16 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Fri, 30 Oct 2015 08:22:08 -0700 Subject: [PATCH 115/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index 19d2146..a0fb14d 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "1.1.3" + VERSION = "1.1.4" DESCRIPTION = "Building your iOS apps has never been easier" end From 4bbbc7938cb48247e1e1767209bba826871db613 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Mon, 2 Nov 2015 15:24:19 -0800 Subject: [PATCH 116/176] Fixed encoding issue with special characters Fixes https://github.com/fastlane/gym/issues/110 --- lib/gym/xcodebuild_fixes/swift_fix.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/xcodebuild_fixes/swift_fix.rb b/lib/gym/xcodebuild_fixes/swift_fix.rb index def9377..e8d2c8a 100644 --- a/lib/gym/xcodebuild_fixes/swift_fix.rb +++ b/lib/gym/xcodebuild_fixes/swift_fix.rb @@ -61,7 +61,7 @@ def zip_entries_matching(zipfile, file_pattern) files = [] Zip::File.open(zipfile) do |zip_file| zip_file.each do |entry| - files << entry.name if entry.name.match file_pattern + files << entry.name if entry.name.force_encoding("utf-8").match file_pattern end end files From e5ff836e6e16ed2bdc113eec69d7ee30560e3137 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Mon, 2 Nov 2015 15:28:59 -0800 Subject: [PATCH 117/176] Switched to new option summary table --- lib/gym/manager.rb | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/lib/gym/manager.rb b/lib/gym/manager.rb index 2d6b3d5..4fa5da0 100644 --- a/lib/gym/manager.rb +++ b/lib/gym/manager.rb @@ -11,27 +11,7 @@ def work(options) private def print_summary - config = Gym.config - rows = [] - rows << ["Project", config[:project]] if config[:project] - rows << ["Workspace", config[:workspace]] if config[:workspace] - rows << ["Scheme", config[:scheme]] if config[:scheme] - rows << ["Configuration", config[:configuration]] if config[:configuration] - - if PackageCommandGenerator.generator == PackageCommandGeneratorXcode7 - rows << ["Export Method", config[:export_method]] - end - - rows << ["Archive Path", config[:archive_path]] if config[:archive_path] - rows << ["Platform", Gym.project.ios? ? "iOS" : "Mac"] - rows << ["Xcode Path", Xcode.xcode_path.gsub("/Contents/Developer", "")] - - puts "" - puts Terminal::Table.new( - title: "Building Application 🚀".green, - rows: rows - ) - puts "" + FastlaneCore::PrintTable.print_values(config: Gym.config, hide_keys: [], title: "Summary for gym #{Gym::VERSION}") end end end From d4eadaef51a1e037960cf7da386691ef91264b84 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Mon, 2 Nov 2015 15:29:11 -0800 Subject: [PATCH 118/176] Improved code style --- lib/gym/generators/build_command_generator.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/gym/generators/build_command_generator.rb b/lib/gym/generators/build_command_generator.rb index c0794c9..e5248aa 100644 --- a/lib/gym/generators/build_command_generator.rb +++ b/lib/gym/generators/build_command_generator.rb @@ -86,6 +86,7 @@ def archive_path file_name = [Gym.config[:output_name], Time.now.strftime("%F %H.%M.%S")] # e.g. 2015-08-07 14.49.12 Gym.cache[:archive_path] = File.join(build_path, file_name.join(" ") + ".xcarchive") end + if File.extname(Gym.cache[:archive_path]) != ".xcarchive" Gym.cache[:archive_path] += ".xcarchive" end From 43bd7f96dac6531523d427e79397d595a25bee32 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Mon, 2 Nov 2015 15:31:16 -0800 Subject: [PATCH 119/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index a0fb14d..5f0c9a7 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "1.1.4" + VERSION = "1.1.5" DESCRIPTION = "Building your iOS apps has never been easier" end From 0f218b59f5f91f7d92c4f8ed9689c6900bcffc70 Mon Sep 17 00:00:00 2001 From: Radek Pietruszewski Date: Tue, 3 Nov 2015 13:25:42 +0100 Subject: [PATCH 120/176] Update README.md Document that `--export_method` should be passed if you want an AdHoc (or any non-AppStore/TF) build --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index f6328fb..b336f39 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,10 @@ In general, if you run into issues while exporting the archive, try using: gym --use_legacy_build_api true +Set the right export method if you're not uploading to App Store or TestFlight: + + gym --export_method ad-hoc + To pass boolean parameters make sure to use `gym` like this: gym --include_bitcode true --include_symbols false From 32ed44c0d4172b9393cb2f0a06109d3b352acc36 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 4 Nov 2015 14:45:19 -0800 Subject: [PATCH 121/176] Added instructions to use the legacy build api when the export command fails --- lib/gym/error_handler.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/gym/error_handler.rb b/lib/gym/error_handler.rb index 0970722..adc5801 100644 --- a/lib/gym/error_handler.rb +++ b/lib/gym/error_handler.rb @@ -82,6 +82,10 @@ def handle_package_error(output) print "It seems like you ran into this radar" print "https://openradar.appspot.com/radar?id=4952000420642816" print "You can temporary use the :use_legacy_build_api option to get the build to work again" + when /IDEDistributionErrorDomain error 1/ + print "There was an error exporting your application" + print "Unfortunately the new Xcode export API is unstable and causes problems on some project" + print "You can temporary use the :use_legacy_build_api option to get the build to work again" end raise "Error packaging up the application".red end From a579721b581043f8cd0891bd9ba030ced86f2504 Mon Sep 17 00:00:00 2001 From: Mathias Aichinger Date: Fri, 20 Nov 2015 17:30:27 +0100 Subject: [PATCH 122/176] Added Fix for WatchKit 2.0 Support. (Apple Should fix their build chain) --- lib/gym.rb | 1 + lib/gym/runner.rb | 1 + lib/gym/xcodebuild_fixes/watchkit2_fix.rb | 35 +++++++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 lib/gym/xcodebuild_fixes/watchkit2_fix.rb diff --git a/lib/gym.rb b/lib/gym.rb index 27f6365..32ad8bc 100644 --- a/lib/gym.rb +++ b/lib/gym.rb @@ -34,6 +34,7 @@ def init_libs # Import all the fixes require 'gym/xcodebuild_fixes/swift_fix' require 'gym/xcodebuild_fixes/watchkit_fix' + require 'gym/xcodebuild_fixes/watchkit2_fix' require 'gym/xcodebuild_fixes/package_application_fix' end end diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb index fdefe8f..2a046b1 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -67,6 +67,7 @@ def fix_package return unless Gym.config[:use_legacy_build_api] Gym::XcodebuildFixes.swift_library_fix Gym::XcodebuildFixes.watchkit_fix + Gym::XcodebuildFixes.watchkit2_fix end # Builds the app and prepares the archive diff --git a/lib/gym/xcodebuild_fixes/watchkit2_fix.rb b/lib/gym/xcodebuild_fixes/watchkit2_fix.rb new file mode 100644 index 0000000..3f34046 --- /dev/null +++ b/lib/gym/xcodebuild_fixes/watchkit2_fix.rb @@ -0,0 +1,35 @@ +module Gym + class XcodebuildFixes + class << self + # Determine whether this app has WatchKit2 support and manually package up the WatchKit2 framework + def watchkit2_fix + return unless watchkit2? + + Helper.log.info "Adding WatchKit2 support" if $verbose + + Dir.mktmpdir do |tmpdir| + # Make watchkit support directory + watchkit_support = File.join(tmpdir, "WatchKitSupport2") + Dir.mkdir(watchkit_support) + + # Copy WK from Xcode into WatchKitSupport2 + FileUtils.copy_file("#{Xcode.xcode_path}/Platforms/WatchOS.platform/Developer/SDKs/WatchOS.sdk/Library/Application Support/WatchKit/WK", File.join(watchkit_support, "WK")) + + # Add "WatchKitSupport2" to the .ipa archive + Dir.chdir(tmpdir) do + abort unless system %(zip --recurse-paths "#{PackageCommandGenerator.ipa_path}" "WatchKitSupport2" > /dev/null) + end + + Helper.log.info "Successfully added WatchKit2 support" if $verbose + end + end + + # Does this application have a WatchKit target + def watchkit2? + Dir["#{PackageCommandGenerator.appfile_path}/**/*.plist"].any? do |plist_path| + `/usr/libexec/PlistBuddy -c 'Print DTSDKName' '#{plist_path}' 2>&1`.strip == 'watchos2.0' + end + end + end + end +end From 6be4fc36ddefe7dc68a92774f9d6bc86ee781c99 Mon Sep 17 00:00:00 2001 From: Mathias Aichinger Date: Fri, 20 Nov 2015 18:50:23 +0100 Subject: [PATCH 123/176] Exclude WatchKitSupport 1 folder when include WatchKitSupport2 --- lib/gym/xcodebuild_fixes/watchkit_fix.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/xcodebuild_fixes/watchkit_fix.rb b/lib/gym/xcodebuild_fixes/watchkit_fix.rb index dd37d20..e76c035 100644 --- a/lib/gym/xcodebuild_fixes/watchkit_fix.rb +++ b/lib/gym/xcodebuild_fixes/watchkit_fix.rb @@ -28,7 +28,7 @@ def watchkit_fix def watchkit? Dir["#{PackageCommandGenerator.appfile_path}/**/*.plist"].any? do |plist_path| `/usr/libexec/PlistBuddy -c 'Print WKWatchKitApp' '#{plist_path}' 2>&1`.strip == 'true' - end + end && !(Gym::XcodebuildFixes.watchkit2?) end end end From 8dd2e82b0b882601949e2c4e8f627302b48b504f Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Fri, 20 Nov 2015 15:11:46 -0800 Subject: [PATCH 124/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index 5f0c9a7..2dbd4d9 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "1.1.5" + VERSION = "1.1.6" DESCRIPTION = "Building your iOS apps has never been easier" end From e18d85f99c5efd6ee4abacd0120d6c2b06b2170c Mon Sep 17 00:00:00 2001 From: Mathias Aichinger Date: Sat, 28 Nov 2015 08:44:47 +0100 Subject: [PATCH 125/176] CRC --- lib/gym/xcodebuild_fixes/watchkit_fix.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/gym/xcodebuild_fixes/watchkit_fix.rb b/lib/gym/xcodebuild_fixes/watchkit_fix.rb index e76c035..70d2ff7 100644 --- a/lib/gym/xcodebuild_fixes/watchkit_fix.rb +++ b/lib/gym/xcodebuild_fixes/watchkit_fix.rb @@ -3,7 +3,7 @@ class XcodebuildFixes class << self # Determine whether this app has WatchKit support and manually package up the WatchKit framework def watchkit_fix - return unless watchkit? + return unless should_apply_watchkit1_fix? Helper.log.info "Adding WatchKit support" if $verbose @@ -28,7 +28,12 @@ def watchkit_fix def watchkit? Dir["#{PackageCommandGenerator.appfile_path}/**/*.plist"].any? do |plist_path| `/usr/libexec/PlistBuddy -c 'Print WKWatchKitApp' '#{plist_path}' 2>&1`.strip == 'true' - end && !(Gym::XcodebuildFixes.watchkit2?) + end + end + + # Should only be applied if watchkit app is not a watchkit2 app + def should_apply_watchkit1_fix? + watchkit? && !(Gym::XcodebuildFixes.watchkit2?) end end end From 164a6338b9c331141db5e0cd7ec2113a81192e21 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Mon, 30 Nov 2015 22:36:59 -0500 Subject: [PATCH 126/176] Updated travis config --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index fc3bf3c..0a800ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,10 @@ -language: objective-c +language: ruby osx_image: xcode7.1 +os: osx before_install: - gem update --system - gem install bundler rvm: - - 2.0.0 - 2.1.6 + - 2.2.2 script: bundle exec fastlane test From a59ec32659e5e28775828f1a11ceefaa598f30ea Mon Sep 17 00:00:00 2001 From: David Ohayon Date: Thu, 3 Dec 2015 11:32:59 -0500 Subject: [PATCH 127/176] Reference local gems for development --- Gemfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Gemfile b/Gemfile index 63b9871..232697e 100644 --- a/Gemfile +++ b/Gemfile @@ -2,3 +2,7 @@ source "https://rubygems.org" # Specify your gem's dependencies in .gemspec gemspec + +if `cd ..; git remote -v`.include?('countdown') + gem 'fastlane_core', path: '../fastlane_core' +end From 997473184e5df37ea762b18275283b2f8bd4b809 Mon Sep 17 00:00:00 2001 From: "John C. Bland II" Date: Fri, 4 Dec 2015 00:04:01 -0600 Subject: [PATCH 128/176] Fix misspelling in GymfileTemplate #quicktweak --- lib/assets/GymfileTemplate | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/assets/GymfileTemplate b/lib/assets/GymfileTemplate index 223f931..561d4e6 100644 --- a/lib/assets/GymfileTemplate +++ b/lib/assets/GymfileTemplate @@ -1,4 +1,4 @@ -# For more information about this configuation visit +# For more information about this configuration visit # https://github.com/fastlane/gym#gymfile # In general, you can use the options available From 026ca9c65098e52bddbe36d3c15fea8ca9342d12 Mon Sep 17 00:00:00 2001 From: KrauseFx Date: Sun, 6 Dec 2015 16:00:46 -0800 Subject: [PATCH 129/176] Renamed PEM to pem --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b336f39..e0e6f26 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ deliversnapshotframeit • - PEM • + pemsighproducecert • @@ -233,7 +233,7 @@ Afterwards the `ipa` file is moved to the output folder. The `dSYM` file is comp - [`deliver`](https://github.com/fastlane/deliver): Upload screenshots, metadata and your app to the App Store - [`snapshot`](https://github.com/fastlane/snapshot): Automate taking localized screenshots of your iOS app on every device - [`frameit`](https://github.com/fastlane/frameit): Quickly put your screenshots into the right device frames -- [`PEM`](https://github.com/fastlane/PEM): Automatically generate and renew your push notification profiles +- [`pem`](https://github.com/fastlane/pem): Automatically generate and renew your push notification profiles - [`produce`](https://github.com/fastlane/produce): Create new iOS apps on iTunes Connect and Dev Portal using the command line - [`cert`](https://github.com/fastlane/cert): Automatically create and maintain iOS code signing certificates - [`spaceship`](https://github.com/fastlane/spaceship): Ruby library to access the Apple Dev Center and iTunes Connect From 0f5148e4358716dc04aad6fd4f254dee34364939 Mon Sep 17 00:00:00 2001 From: joshrlesch Date: Tue, 8 Dec 2015 15:10:22 -0600 Subject: [PATCH 130/176] Adds suppress xcode output option and update pip def --- lib/gym/generators/build_command_generator.rb | 6 +++++- lib/gym/options.rb | 10 +++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/gym/generators/build_command_generator.rb b/lib/gym/generators/build_command_generator.rb index e5248aa..d55ee02 100644 --- a/lib/gym/generators/build_command_generator.rb +++ b/lib/gym/generators/build_command_generator.rb @@ -58,7 +58,11 @@ def suffix end def pipe - ["| tee '#{xcodebuild_log_path}' | xcpretty"] + pipe = [] + pipe << "| tee '#{xcodebuild_log_path}' | xcpretty" + pipe << "> /dev/null" if Gym.config[:suppress_xcode_output] + + pipe end def xcodebuild_log_path diff --git a/lib/gym/options.rb b/lib/gym/options.rb index f1524c2..99deda2 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -152,7 +152,15 @@ def self.plain_options optional: true, verify_block: proc do |value| raise "File not found at path '#{File.expand_path(value)}'".red unless File.exist?(value) - end) + end), + FastlaneCore::ConfigItem.new(key: :suppress_xcode_output, + short_option: "-r", + env_name: "SUPPRESS_OUTPUT", + description: "Suppress the output of xcodebuild to stdout. Output is still saved in buildlog_path", + optional: true, + default_value: false, + is_string: false + ) ] end end From 941f1bd73d85e7316b659ba2f819bec94f4f6946 Mon Sep 17 00:00:00 2001 From: joshrlesch Date: Tue, 8 Dec 2015 15:13:22 -0600 Subject: [PATCH 131/176] Don't need default value if its optional --- lib/gym/options.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/gym/options.rb b/lib/gym/options.rb index 99deda2..1951abf 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -158,7 +158,6 @@ def self.plain_options env_name: "SUPPRESS_OUTPUT", description: "Suppress the output of xcodebuild to stdout. Output is still saved in buildlog_path", optional: true, - default_value: false, is_string: false ) ] From 0d087c1633e37a7f4d78d53c31cb38800245a8f2 Mon Sep 17 00:00:00 2001 From: joshrlesch Date: Tue, 8 Dec 2015 15:23:08 -0600 Subject: [PATCH 132/176] hound violation --- lib/gym/options.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/options.rb b/lib/gym/options.rb index 1951abf..8989b34 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -159,7 +159,7 @@ def self.plain_options description: "Suppress the output of xcodebuild to stdout. Output is still saved in buildlog_path", optional: true, is_string: false - ) + ) ] end end From 9006f62c240642126b5625ddae82ced983ab3e51 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Thu, 10 Dec 2015 10:11:27 -0800 Subject: [PATCH 133/176] Added match --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e0e6f26..ff6a793 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,8 @@ pilotboardinggym • - scan + scan • + match

------- @@ -240,6 +241,7 @@ Afterwards the `ipa` file is moved to the output folder. The `dSYM` file is comp - [`pilot`](https://github.com/fastlane/pilot): The best way to manage your TestFlight testers and builds from your terminal - [`boarding`](https://github.com/fastlane/boarding): The easiest way to invite your TestFlight beta testers - [`scan`](https://github.com/fastlane/scan): The easiest way to run tests of your iOS and Mac app +- [`match`](https://github.com/fastlane/match): Easily sync your certificates and profiles across your team using git ##### [Like this tool? Be the first to know about updates and new fastlane tools](https://tinyletter.com/krausefx) From a71948fbed9020d3d7b57d1f517d31b965cfc9b6 Mon Sep 17 00:00:00 2001 From: KrauseFx Date: Thu, 10 Dec 2015 16:43:52 -0800 Subject: [PATCH 134/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index 2dbd4d9..3b01a7f 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "1.1.6" + VERSION = "1.2.0" DESCRIPTION = "Building your iOS apps has never been easier" end From ff99cab9245e882e5e750af11746cdd99b4988a1 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Thu, 17 Dec 2015 13:25:57 -0800 Subject: [PATCH 135/176] Enabled UI layer --- gym.gemspec | 2 +- lib/gym.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/gym.gemspec b/gym.gemspec index a69c429..9588dc6 100644 --- a/gym.gemspec +++ b/gym.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency 'fastlane_core', '>= 0.25.0', '< 1.0.0' # all shared code and dependencies + spec.add_dependency 'fastlane_core', '>= 0.30.0', '< 1.0.0' # all shared code and dependencies spec.add_dependency 'xcpretty', '>= 0.2.1' # pretty xcodebuild output spec.add_dependency 'terminal-table' # print out build information spec.add_dependency 'plist' # Generate the Xcode config plist file diff --git a/lib/gym.rb b/lib/gym.rb index 32ad8bc..279780a 100644 --- a/lib/gym.rb +++ b/lib/gym.rb @@ -40,6 +40,7 @@ def init_libs end Helper = FastlaneCore::Helper # you gotta love Ruby: Helper.* should use the Helper class contained in FastlaneCore + UI = FastlaneCore::UI Gym.init_libs end From f4f3b6f842767693248a6cf97654fee200da8422 Mon Sep 17 00:00:00 2001 From: KrauseFx Date: Fri, 18 Dec 2015 16:55:42 -0800 Subject: [PATCH 136/176] Updated gym to use the new UI class --- lib/gym/commands_generator.rb | 2 +- lib/gym/detect_values.rb | 2 +- lib/gym/error_handler.rb | 6 ++--- .../package_command_generator_xcode7.rb | 4 ++-- lib/gym/manager.rb | 10 +++----- lib/gym/options.rb | 2 +- lib/gym/runner.rb | 23 ++++++++----------- .../package_application_fix.rb | 2 +- lib/gym/xcodebuild_fixes/swift_fix.rb | 4 ++-- lib/gym/xcodebuild_fixes/watchkit2_fix.rb | 4 ++-- lib/gym/xcodebuild_fixes/watchkit_fix.rb | 4 ++-- 11 files changed, 28 insertions(+), 35 deletions(-) diff --git a/lib/gym/commands_generator.rb b/lib/gym/commands_generator.rb index d626b74..35c91d6 100644 --- a/lib/gym/commands_generator.rb +++ b/lib/gym/commands_generator.rb @@ -51,7 +51,7 @@ def run raise "Gymfile already exists".yellow if File.exist?(path) template = File.read("#{Helper.gem_path('gym')}/lib/assets/GymfileTemplate") File.write(path, template) - Helper.log.info "Successfully created '#{path}'. Open the file using a code editor.".green + UI.success "Successfully created '#{path}'. Open the file using a code editor." end end diff --git a/lib/gym/detect_values.rb b/lib/gym/detect_values.rb index a7eedab..44ec8d0 100644 --- a/lib/gym/detect_values.rb +++ b/lib/gym/detect_values.rb @@ -73,7 +73,7 @@ def self.detect_configuration if config[:configuration] # Verify the configuration is available unless configurations.include?(config[:configuration]) - Helper.log.error "Couldn't find specified configuration '#{config[:configuration]}'.".red + UI.error "Couldn't find specified configuration '#{config[:configuration]}'." config[:configuration] = nil end end diff --git a/lib/gym/error_handler.rb b/lib/gym/error_handler.rb index adc5801..f0539e6 100644 --- a/lib/gym/error_handler.rb +++ b/lib/gym/error_handler.rb @@ -49,7 +49,7 @@ def handle_build_error(output) print "For more information visit this stackoverflow answer:" print "https://stackoverflow.com/a/17031697/445598" end - raise "Error building the application - see the log above".red + UI.user_error!("Error building the application - see the log above") end # @param [Array] The output of the errored build (line by line) @@ -87,7 +87,7 @@ def handle_package_error(output) print "Unfortunately the new Xcode export API is unstable and causes problems on some project" print "You can temporary use the :use_legacy_build_api option to get the build to work again" end - raise "Error packaging up the application".red + UI.user_error!("Error packaging up the application") end def handle_empty_archive @@ -103,7 +103,7 @@ def handle_empty_archive # Just to make things easier def print(text) - Helper.log.error text.red + UI.error text end end end diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index 4f6d57d..0529221 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -78,8 +78,8 @@ def config_content def print_legacy_information if Gym.config[:provisioning_profile_path] - Helper.log.info "You're using Xcode 7, the `provisioning_profile_path` value will be ignored".yellow - Helper.log.info "Please follow the Code Signing Guide: https://github.com/KrauseFx/fastlane/blob/master/docs/CodeSigning.md".yellow + UI.important "You're using Xcode 7, the `provisioning_profile_path` value will be ignored" + UI.important "Please follow the Code Signing Guide: https://github.com/KrauseFx/fastlane/blob/master/docs/CodeSigning.md" end end end diff --git a/lib/gym/manager.rb b/lib/gym/manager.rb index 4fa5da0..4450574 100644 --- a/lib/gym/manager.rb +++ b/lib/gym/manager.rb @@ -3,15 +3,11 @@ class Manager def work(options) Gym.config = options - print_summary + FastlaneCore::PrintTable.print_values(config: Gym.config, + hide_keys: [], + title: "Summary for gym #{Gym::VERSION}") return Runner.new.run end - - private - - def print_summary - FastlaneCore::PrintTable.print_values(config: Gym.config, hide_keys: [], title: "Summary for gym #{Gym::VERSION}") - end end end diff --git a/lib/gym/options.rb b/lib/gym/options.rb index 8989b34..32c8c31 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -92,7 +92,7 @@ def self.plain_options is_string: false, verify_block: proc do |value| if value - Helper.log.info "Using legacy build system - waiting for radar to be fixed: https://openradar.appspot.com/radar?id=4952000420642816".red + UI.important "Using legacy build system - waiting for radar to be fixed: https://openradar.appspot.com/radar?id=4952000420642816" end end), FastlaneCore::ConfigItem.new(key: :export_method, diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb index c70ca46..9830171 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -81,8 +81,8 @@ def build_app ErrorHandler.handle_build_error(output) end) - Helper.log.info("Successfully stored the archive. You can find it in the Xcode Organizer.".green) - Helper.log.info("Stored the archive in: ".green + BuildCommandGenerator.archive_path) if $verbose + UI.success "Successfully stored the archive. You can find it in the Xcode Organizer." + UI.verbose("Stored the archive in: " + BuildCommandGenerator.archive_path) end # Makes sure the archive is there and valid @@ -112,18 +112,15 @@ def compress_and_move_dsym containing_directory = File.expand_path("..", PackageCommandGenerator.dsym_path) available_dsyms = Dir.glob("#{containing_directory}/*.dSYM") - - Helper.log.info "Compressing #{available_dsyms.count} dSYM(s)" + UI.message "Compressing #{available_dsyms.count} dSYM(s)" unless Gym.config[:silent] output_path = File.expand_path(File.join(Gym.config[:output_directory], Gym.config[:output_name] + ".app.dSYM.zip")) command = "cd '#{containing_directory}' && zip -r '#{output_path}' *.dSYM" - Helper.log.info command.yellow unless Gym.config[:silent] - command_result = `#{command}` - Helper.log.info command_result if $verbose + Helper.backticks(command, print: !Gym.config[:silent]) puts "" # new line - Helper.log.info "Successfully exported and compressed dSYM file".green + UI.success "Successfully exported and compressed dSYM file" end # Moves over the binary and dsym file to the output directory @@ -132,21 +129,21 @@ def move_ipa FileUtils.mv(PackageCommandGenerator.ipa_path, File.expand_path(Gym.config[:output_directory]), force: true) ipa_path = File.expand_path(File.join(Gym.config[:output_directory], File.basename(PackageCommandGenerator.ipa_path))) - Helper.log.info "Successfully exported and signed the ipa file:".green - Helper.log.info ipa_path + UI.success "Successfully exported and signed the ipa file:" + UI.message ipa_path ipa_path end # Move the .app from the archive into the output directory def move_mac_app app_path = Dir[File.join(BuildCommandGenerator.archive_path, "Products/Applications/*.app")].last - raise "Couldn't find application in '#{BuildCommandGenerator.archive_path}'".red unless app_path + UI.crash!("Couldn't find application in '#{BuildCommandGenerator.archive_path}'") unless app_path FileUtils.mv(app_path, File.expand_path(Gym.config[:output_directory]), force: true) app_path = File.join(Gym.config[:output_directory], File.basename(app_path)) - Helper.log.info "Successfully exported the .app file:".green - Helper.log.info app_path + UI.success "Successfully exported the .app file:" + UI.message app_path app_path end diff --git a/lib/gym/xcodebuild_fixes/package_application_fix.rb b/lib/gym/xcodebuild_fixes/package_application_fix.rb index 681a82e..dd54629 100644 --- a/lib/gym/xcodebuild_fixes/package_application_fix.rb +++ b/lib/gym/xcodebuild_fixes/package_application_fix.rb @@ -27,7 +27,7 @@ def patch_package_application # Apply patches to PackageApplication4Gym from patches folder Dir[File.join(Helper.gem_path("gym"), "lib/assets/package_application_patches/*.diff")].each do |patch| - Helper.log.info "Applying Package Application patch: #{File.basename(patch)}" if $verbose + UI.verbose "Applying Package Application patch: #{File.basename(patch)}" command = ["patch '#{@patched_package_application_path}' < '#{patch}'"] Runner.new.print_command(command, "Applying Package Application patch: #{File.basename(patch)}") if $verbose diff --git a/lib/gym/xcodebuild_fixes/swift_fix.rb b/lib/gym/xcodebuild_fixes/swift_fix.rb index e8d2c8a..cc60429 100644 --- a/lib/gym/xcodebuild_fixes/swift_fix.rb +++ b/lib/gym/xcodebuild_fixes/swift_fix.rb @@ -14,7 +14,7 @@ def swift_library_fix return if check_for_swift PackageCommandGenerator - Helper.log.info "Packaging up the Swift Framework as the current app is a Swift app" if $verbose + UI.verbose "Packaging up the Swift Framework as the current app is a Swift app" ipa_swift_frameworks = Dir["#{PackageCommandGenerator.appfile_path}/Frameworks/libswift*"] Dir.mktmpdir do |tmpdir| @@ -49,7 +49,7 @@ def swift_library_fix # @param the PackageCommandGenerator # @return true if swift def check_for_swift(pcg) - Helper.log.info "Checking for Swift framework" if $verbose + UI.verbose "Checking for Swift framework" default_swift_libs = "#{pcg.appfile_path}/Frameworks/libswift.*" # note the extra ., this is a string representation of a regexp zip_entries_matching(pcg.ipa_path, /#{default_swift_libs}/).count > 0 end diff --git a/lib/gym/xcodebuild_fixes/watchkit2_fix.rb b/lib/gym/xcodebuild_fixes/watchkit2_fix.rb index 3f34046..dd7afee 100644 --- a/lib/gym/xcodebuild_fixes/watchkit2_fix.rb +++ b/lib/gym/xcodebuild_fixes/watchkit2_fix.rb @@ -5,7 +5,7 @@ class << self def watchkit2_fix return unless watchkit2? - Helper.log.info "Adding WatchKit2 support" if $verbose + UI.verbose "Adding WatchKit2 support" Dir.mktmpdir do |tmpdir| # Make watchkit support directory @@ -20,7 +20,7 @@ def watchkit2_fix abort unless system %(zip --recurse-paths "#{PackageCommandGenerator.ipa_path}" "WatchKitSupport2" > /dev/null) end - Helper.log.info "Successfully added WatchKit2 support" if $verbose + UI.verbose "Successfully added WatchKit2 support" end end diff --git a/lib/gym/xcodebuild_fixes/watchkit_fix.rb b/lib/gym/xcodebuild_fixes/watchkit_fix.rb index 70d2ff7..392ccc7 100644 --- a/lib/gym/xcodebuild_fixes/watchkit_fix.rb +++ b/lib/gym/xcodebuild_fixes/watchkit_fix.rb @@ -5,7 +5,7 @@ class << self def watchkit_fix return unless should_apply_watchkit1_fix? - Helper.log.info "Adding WatchKit support" if $verbose + UI.verbose "Adding WatchKit support" Dir.mktmpdir do |tmpdir| # Make watchkit support directory @@ -20,7 +20,7 @@ def watchkit_fix abort unless system %(zip --recurse-paths "#{PackageCommandGenerator.ipa_path}" "WatchKitSupport" > /dev/null) end - Helper.log.info "Successfully added WatchKit support" if $verbose + UI.verbose "Successfully added WatchKit support" end end From a9c1cd38ae1ee3f563bab8db59bd260effd33fa5 Mon Sep 17 00:00:00 2001 From: Andrei Raifura Date: Sat, 19 Dec 2015 12:56:15 +0200 Subject: [PATCH 137/176] Disable colors in xcpretty when FASTLANE_DISABLE_COLORS is set --- lib/gym/generators/build_command_generator.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/gym/generators/build_command_generator.rb b/lib/gym/generators/build_command_generator.rb index d55ee02..da44c3f 100644 --- a/lib/gym/generators/build_command_generator.rb +++ b/lib/gym/generators/build_command_generator.rb @@ -60,6 +60,7 @@ def suffix def pipe pipe = [] pipe << "| tee '#{xcodebuild_log_path}' | xcpretty" + pipe << "--no-color" if ENV["FASTLANE_DISABLE_COLORS"] pipe << "> /dev/null" if Gym.config[:suppress_xcode_output] pipe From a5b8399e4c36afdf090f39512eae6407fdba0269 Mon Sep 17 00:00:00 2001 From: Alex Cristea Date: Thu, 24 Dec 2015 10:14:43 +0200 Subject: [PATCH 138/176] Try loading the Gymfile from the current directory --- lib/gym/detect_values.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/gym/detect_values.rb b/lib/gym/detect_values.rb index 44ec8d0..bc0658d 100644 --- a/lib/gym/detect_values.rb +++ b/lib/gym/detect_values.rb @@ -6,12 +6,15 @@ class DetectValues def self.set_additional_default_values config = Gym.config - FastlaneCore::Project.detect_projects(config) + # First, try loading the Gymfile from the current directory + config.load_configuration_file(Gym.gymfile_name) + # Detect the project + FastlaneCore::Project.detect_projects(config) Gym.project = FastlaneCore::Project.new(config) detect_provisioning_profile - # Go into the project's folder + # Go into the project's folder, as there might be a Gymfile there Dir.chdir(File.expand_path("..", Gym.project.path)) do config.load_configuration_file(Gym.gymfile_name) end From d7a43322eccacbd8afbf53c23b5e2899c8643bbb Mon Sep 17 00:00:00 2001 From: Jimmy Sambuo Date: Thu, 24 Dec 2015 02:50:15 -0800 Subject: [PATCH 139/176] Fixed issue with creating ipa and dsym for tvos --- lib/gym/runner.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb index 9830171..95f7be4 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -12,7 +12,7 @@ def run FileUtils.mkdir_p(Gym.config[:output_directory]) - if Gym.project.ios? + if Gym.project.ios? || Gym.project.tvos? package_app fix_package compress_and_move_dsym From f8e7ca39af80356ecfe0dbb212f82bfd4f1b0d33 Mon Sep 17 00:00:00 2001 From: KrauseFx Date: Wed, 30 Dec 2015 00:45:56 +0400 Subject: [PATCH 140/176] Added tvOS example project --- .../tvapp/tvapp.xcodeproj/project.pbxproj | 301 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/tvapp.xcscmblueprint | 30 ++ .../xcschemes/tvapp.xcscheme | 91 ++++++ .../xcschemes/xcschememanagement.plist | 22 ++ examples/tvapp/tvapp/AppDelegate.h | 17 + examples/tvapp/tvapp/AppDelegate.m | 45 +++ .../Content.imageset/Contents.json | 12 + .../Back.imagestacklayer/Contents.json | 6 + .../App Icon - Large.imagestack/Contents.json | 17 + .../Content.imageset/Contents.json | 12 + .../Front.imagestacklayer/Contents.json | 6 + .../Content.imageset/Contents.json | 12 + .../Middle.imagestacklayer/Contents.json | 6 + .../Content.imageset/Contents.json | 12 + .../Back.imagestacklayer/Contents.json | 6 + .../App Icon - Small.imagestack/Contents.json | 17 + .../Content.imageset/Contents.json | 12 + .../Front.imagestacklayer/Contents.json | 6 + .../Content.imageset/Contents.json | 12 + .../Middle.imagestacklayer/Contents.json | 6 + .../Contents.json | 26 ++ .../Top Shelf Image.imageset/Contents.json | 12 + .../tvapp/tvapp/Assets.xcassets/Contents.json | 6 + .../LaunchImage.launchimage/Contents.json | 15 + .../first.imageset/Contents.json | 12 + .../Assets.xcassets/first.imageset/first.pdf | Bin 0 -> 2465 bytes .../second.imageset/Contents.json | 12 + .../second.imageset/second.pdf | Bin 0 -> 2423 bytes .../tvapp/tvapp/Base.lproj/Main.storyboard | 111 +++++++ examples/tvapp/tvapp/FirstViewController.h | 15 + examples/tvapp/tvapp/FirstViewController.m | 27 ++ examples/tvapp/tvapp/Info.plist | 32 ++ examples/tvapp/tvapp/SecondViewController.h | 15 + examples/tvapp/tvapp/SecondViewController.m | 27 ++ examples/tvapp/tvapp/main.m | 16 + 36 files changed, 981 insertions(+) create mode 100644 examples/tvapp/tvapp.xcodeproj/project.pbxproj create mode 100644 examples/tvapp/tvapp.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 examples/tvapp/tvapp.xcodeproj/project.xcworkspace/xcshareddata/tvapp.xcscmblueprint create mode 100644 examples/tvapp/tvapp.xcodeproj/xcuserdata/felixkrause.xcuserdatad/xcschemes/tvapp.xcscheme create mode 100644 examples/tvapp/tvapp.xcodeproj/xcuserdata/felixkrause.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 examples/tvapp/tvapp/AppDelegate.h create mode 100644 examples/tvapp/tvapp/AppDelegate.m create mode 100644 examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json create mode 100644 examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json create mode 100644 examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Contents.json create mode 100644 examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json create mode 100644 examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json create mode 100644 examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json create mode 100644 examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json create mode 100644 examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json create mode 100644 examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json create mode 100644 examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Contents.json create mode 100644 examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json create mode 100644 examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json create mode 100644 examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json create mode 100644 examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json create mode 100644 examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json create mode 100644 examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json create mode 100644 examples/tvapp/tvapp/Assets.xcassets/Contents.json create mode 100644 examples/tvapp/tvapp/Assets.xcassets/LaunchImage.launchimage/Contents.json create mode 100644 examples/tvapp/tvapp/Assets.xcassets/first.imageset/Contents.json create mode 100644 examples/tvapp/tvapp/Assets.xcassets/first.imageset/first.pdf create mode 100644 examples/tvapp/tvapp/Assets.xcassets/second.imageset/Contents.json create mode 100644 examples/tvapp/tvapp/Assets.xcassets/second.imageset/second.pdf create mode 100644 examples/tvapp/tvapp/Base.lproj/Main.storyboard create mode 100644 examples/tvapp/tvapp/FirstViewController.h create mode 100644 examples/tvapp/tvapp/FirstViewController.m create mode 100644 examples/tvapp/tvapp/Info.plist create mode 100644 examples/tvapp/tvapp/SecondViewController.h create mode 100644 examples/tvapp/tvapp/SecondViewController.m create mode 100644 examples/tvapp/tvapp/main.m diff --git a/examples/tvapp/tvapp.xcodeproj/project.pbxproj b/examples/tvapp/tvapp.xcodeproj/project.pbxproj new file mode 100644 index 0000000..0581a01 --- /dev/null +++ b/examples/tvapp/tvapp.xcodeproj/project.pbxproj @@ -0,0 +1,301 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + CA7BF6BA1C33280500A161B3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = CA7BF6B91C33280500A161B3 /* main.m */; }; + CA7BF6BD1C33280500A161B3 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CA7BF6BC1C33280500A161B3 /* AppDelegate.m */; }; + CA7BF6C01C33280500A161B3 /* FirstViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CA7BF6BF1C33280500A161B3 /* FirstViewController.m */; }; + CA7BF6C31C33280500A161B3 /* SecondViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CA7BF6C21C33280500A161B3 /* SecondViewController.m */; }; + CA7BF6C61C33280500A161B3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CA7BF6C41C33280500A161B3 /* Main.storyboard */; }; + CA7BF6C81C33280500A161B3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CA7BF6C71C33280500A161B3 /* Assets.xcassets */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + CA7BF6B51C33280500A161B3 /* tvapp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = tvapp.app; sourceTree = BUILT_PRODUCTS_DIR; }; + CA7BF6B91C33280500A161B3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + CA7BF6BB1C33280500A161B3 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + CA7BF6BC1C33280500A161B3 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + CA7BF6BE1C33280500A161B3 /* FirstViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FirstViewController.h; sourceTree = ""; }; + CA7BF6BF1C33280500A161B3 /* FirstViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FirstViewController.m; sourceTree = ""; }; + CA7BF6C11C33280500A161B3 /* SecondViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SecondViewController.h; sourceTree = ""; }; + CA7BF6C21C33280500A161B3 /* SecondViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SecondViewController.m; sourceTree = ""; }; + CA7BF6C51C33280500A161B3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + CA7BF6C71C33280500A161B3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + CA7BF6C91C33280500A161B3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + CA7BF6B21C33280500A161B3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + CA7BF6AC1C33280500A161B3 = { + isa = PBXGroup; + children = ( + CA7BF6B71C33280500A161B3 /* tvapp */, + CA7BF6B61C33280500A161B3 /* Products */, + ); + sourceTree = ""; + }; + CA7BF6B61C33280500A161B3 /* Products */ = { + isa = PBXGroup; + children = ( + CA7BF6B51C33280500A161B3 /* tvapp.app */, + ); + name = Products; + sourceTree = ""; + }; + CA7BF6B71C33280500A161B3 /* tvapp */ = { + isa = PBXGroup; + children = ( + CA7BF6BB1C33280500A161B3 /* AppDelegate.h */, + CA7BF6BC1C33280500A161B3 /* AppDelegate.m */, + CA7BF6BE1C33280500A161B3 /* FirstViewController.h */, + CA7BF6BF1C33280500A161B3 /* FirstViewController.m */, + CA7BF6C11C33280500A161B3 /* SecondViewController.h */, + CA7BF6C21C33280500A161B3 /* SecondViewController.m */, + CA7BF6C41C33280500A161B3 /* Main.storyboard */, + CA7BF6C71C33280500A161B3 /* Assets.xcassets */, + CA7BF6C91C33280500A161B3 /* Info.plist */, + CA7BF6B81C33280500A161B3 /* Supporting Files */, + ); + path = tvapp; + sourceTree = ""; + }; + CA7BF6B81C33280500A161B3 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + CA7BF6B91C33280500A161B3 /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + CA7BF6B41C33280500A161B3 /* tvapp */ = { + isa = PBXNativeTarget; + buildConfigurationList = CA7BF6CC1C33280500A161B3 /* Build configuration list for PBXNativeTarget "tvapp" */; + buildPhases = ( + CA7BF6B11C33280500A161B3 /* Sources */, + CA7BF6B21C33280500A161B3 /* Frameworks */, + CA7BF6B31C33280500A161B3 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = tvapp; + productName = tvapp; + productReference = CA7BF6B51C33280500A161B3 /* tvapp.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + CA7BF6AD1C33280500A161B3 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0720; + ORGANIZATIONNAME = "Felix Krause"; + TargetAttributes = { + CA7BF6B41C33280500A161B3 = { + CreatedOnToolsVersion = 7.2; + }; + }; + }; + buildConfigurationList = CA7BF6B01C33280500A161B3 /* Build configuration list for PBXProject "tvapp" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = CA7BF6AC1C33280500A161B3; + productRefGroup = CA7BF6B61C33280500A161B3 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + CA7BF6B41C33280500A161B3 /* tvapp */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + CA7BF6B31C33280500A161B3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CA7BF6C81C33280500A161B3 /* Assets.xcassets in Resources */, + CA7BF6C61C33280500A161B3 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + CA7BF6B11C33280500A161B3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CA7BF6C31C33280500A161B3 /* SecondViewController.m in Sources */, + CA7BF6BD1C33280500A161B3 /* AppDelegate.m in Sources */, + CA7BF6C01C33280500A161B3 /* FirstViewController.m in Sources */, + CA7BF6BA1C33280500A161B3 /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + CA7BF6C41C33280500A161B3 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + CA7BF6C51C33280500A161B3 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + CA7BF6CA1C33280500A161B3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 9.1; + }; + name = Debug; + }; + CA7BF6CB1C33280500A161B3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 9.1; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + CA7BF6CD1C33280500A161B3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + INFOPLIST_FILE = tvapp/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.tvapp; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + CA7BF6CE1C33280500A161B3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + INFOPLIST_FILE = tvapp/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.tvapp; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + CA7BF6B01C33280500A161B3 /* Build configuration list for PBXProject "tvapp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CA7BF6CA1C33280500A161B3 /* Debug */, + CA7BF6CB1C33280500A161B3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CA7BF6CC1C33280500A161B3 /* Build configuration list for PBXNativeTarget "tvapp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CA7BF6CD1C33280500A161B3 /* Debug */, + CA7BF6CE1C33280500A161B3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; +/* End XCConfigurationList section */ + }; + rootObject = CA7BF6AD1C33280500A161B3 /* Project object */; +} diff --git a/examples/tvapp/tvapp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples/tvapp/tvapp.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..d1200ae --- /dev/null +++ b/examples/tvapp/tvapp.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/examples/tvapp/tvapp.xcodeproj/project.xcworkspace/xcshareddata/tvapp.xcscmblueprint b/examples/tvapp/tvapp.xcodeproj/project.xcworkspace/xcshareddata/tvapp.xcscmblueprint new file mode 100644 index 0000000..f784964 --- /dev/null +++ b/examples/tvapp/tvapp.xcodeproj/project.xcworkspace/xcshareddata/tvapp.xcscmblueprint @@ -0,0 +1,30 @@ +{ + "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D", + "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { + + }, + "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { + "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : 0, + "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : 0 + }, + "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "0C0BD7B7-BAC1-4D34-AD66-1835A6180719", + "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { + "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : "gym\/", + "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : "" + }, + "DVTSourceControlWorkspaceBlueprintNameKey" : "tvapp", + "DVTSourceControlWorkspaceBlueprintVersion" : 204, + "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "examples\/tvapp\/tvapp.xcodeproj", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/fastlane\/countdown", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" + }, + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/fastlane\/gym.git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" + } + ] +} \ No newline at end of file diff --git a/examples/tvapp/tvapp.xcodeproj/xcuserdata/felixkrause.xcuserdatad/xcschemes/tvapp.xcscheme b/examples/tvapp/tvapp.xcodeproj/xcuserdata/felixkrause.xcuserdatad/xcschemes/tvapp.xcscheme new file mode 100644 index 0000000..374b390 --- /dev/null +++ b/examples/tvapp/tvapp.xcodeproj/xcuserdata/felixkrause.xcuserdatad/xcschemes/tvapp.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/tvapp/tvapp.xcodeproj/xcuserdata/felixkrause.xcuserdatad/xcschemes/xcschememanagement.plist b/examples/tvapp/tvapp.xcodeproj/xcuserdata/felixkrause.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..f8cc1b7 --- /dev/null +++ b/examples/tvapp/tvapp.xcodeproj/xcuserdata/felixkrause.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + tvapp.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + CA7BF6B41C33280500A161B3 + + primary + + + + + diff --git a/examples/tvapp/tvapp/AppDelegate.h b/examples/tvapp/tvapp/AppDelegate.h new file mode 100644 index 0000000..5a8961a --- /dev/null +++ b/examples/tvapp/tvapp/AppDelegate.h @@ -0,0 +1,17 @@ +// +// AppDelegate.h +// tvapp +// +// Created by Felix Krause on 30/12/15. +// Copyright © 2015 Felix Krause. All rights reserved. +// + +#import + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + + +@end + diff --git a/examples/tvapp/tvapp/AppDelegate.m b/examples/tvapp/tvapp/AppDelegate.m new file mode 100644 index 0000000..5736b75 --- /dev/null +++ b/examples/tvapp/tvapp/AppDelegate.m @@ -0,0 +1,45 @@ +// +// AppDelegate.m +// tvapp +// +// Created by Felix Krause on 30/12/15. +// Copyright © 2015 Felix Krause. All rights reserved. +// + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. +} + +- (void)applicationDidEnterBackground:(UIApplication *)application { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. +} + +- (void)applicationWillEnterForeground:(UIApplication *)application { + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. +} + +- (void)applicationWillTerminate:(UIApplication *)application { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. +} + +@end diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..0564959 --- /dev/null +++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Contents.json new file mode 100644 index 0000000..8bf75d9 --- /dev/null +++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "layers" : [ + { + "filename" : "Front.imagestacklayer" + }, + { + "filename" : "Middle.imagestacklayer" + }, + { + "filename" : "Back.imagestacklayer" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..0564959 --- /dev/null +++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..0564959 --- /dev/null +++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..0564959 --- /dev/null +++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Contents.json new file mode 100644 index 0000000..8bf75d9 --- /dev/null +++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "layers" : [ + { + "filename" : "Front.imagestacklayer" + }, + { + "filename" : "Middle.imagestacklayer" + }, + { + "filename" : "Back.imagestacklayer" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..0564959 --- /dev/null +++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..0564959 --- /dev/null +++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json new file mode 100644 index 0000000..6a3dcfa --- /dev/null +++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json @@ -0,0 +1,26 @@ +{ + "assets" : [ + { + "size" : "1280x768", + "idiom" : "tv", + "filename" : "App Icon - Large.imagestack", + "role" : "primary-app-icon" + }, + { + "size" : "400x240", + "idiom" : "tv", + "filename" : "App Icon - Small.imagestack", + "role" : "primary-app-icon" + }, + { + "size" : "1920x720", + "idiom" : "tv", + "filename" : "Top Shelf Image.imageset", + "role" : "top-shelf-image" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json new file mode 100644 index 0000000..0564959 --- /dev/null +++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/tvapp/tvapp/Assets.xcassets/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/examples/tvapp/tvapp/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/tvapp/tvapp/Assets.xcassets/LaunchImage.launchimage/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000..29d94c7 --- /dev/null +++ b/examples/tvapp/tvapp/Assets.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "orientation" : "landscape", + "idiom" : "tv", + "extent" : "full-screen", + "minimum-system-version" : "9.0", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/tvapp/tvapp/Assets.xcassets/first.imageset/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/first.imageset/Contents.json new file mode 100644 index 0000000..33a7451 --- /dev/null +++ b/examples/tvapp/tvapp/Assets.xcassets/first.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "first.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/tvapp/tvapp/Assets.xcassets/first.imageset/first.pdf b/examples/tvapp/tvapp/Assets.xcassets/first.imageset/first.pdf new file mode 100644 index 0000000000000000000000000000000000000000..47d911dea647d55983671ead4d08b6f6b3600715 GIT binary patch literal 2465 zcmai03se(l7FLvisY+L#f-EBsK`11Xkc1=%MIujmB|xQ!>5vQ%APFP`Mlf5nSPOKE z2-^C>7DZkM!J;Da5{kfrC?Kvp3J41-3shFXvhuJqKo*oer*r0H{{P-P_x0cTzLzjD!bE z0qT;#Q7l4Gy%fMoXJaKT`@{5#R(MOqJPwQifv8iK6A%Ot9L14h2`38T!2s4PM=1!< zmL06}VYAA|ay#jZRs>HpA%X+eQW4rufWU%d1w5GTy!X#LeZsF_+~ccZmn3Fi)v^Z; zIG;?uU*yLLEYs61tjD>gXOFvSWsh{48xJvPNqKrIJtMdCz2cA2aC7TF?b@K`V!Lw- zE;zpH&ApqhoRjAHt}gK}>(qAc8dvrkD31*`<5<%C(&4_LqhQ3-GOf8~n^leG?+-@@^pjPa$J2gW@O)!b9hdTJ zTauyIJ&~rqeEZC1p9dWgx7{_WRc2=drMO=wcT7B{Zd58z`d)r_36r4V<6PGkCjpGfbnVMtm@;b}F!T?)Qav$_y7H5T;Mf!T}M zWxP9TNqrV?e5;b|pWd3^u2KAatk&3aDB@0 zZBaQNdCZ2#fblzYZnRCCjQ-GQWb-s8bX&<)?SxnUGdDYVFVk`xIf7@g3~$DX`H71_;r5OSZz1p`$_8y_9 z{;MJJ+n=>7Ewg;GnGHoz)&ID0z@F2!e$F7cWQ?d6s(!VY)_Gw})xCyMvsD={5i&H* zAIr_ACo8;Se6<*!-mm9Am79Iz^RVlc?%S5sg|E*SyIV{dd9{Mpf#d3cih5WKt=%ps zBEo)bt8EjmeCFYJRYU|b7d`p+-V|X2wOCYtyLP6t=!WH-kgdf0A};ytPfZiCwVPx{ z`g;zpe{8a4RQxQUwVU02<4X3w|9h;}XjhGWquqn{vVqq8g{*}BQF9=ybe_TM*!M=rFs1pN)yzctIXAoicsCe6>fit>wgZ#vp^hZOY0`J`rx zwdSK?GwR_xm9;5XjH|vf{O+Yg-)z;s*xt>;-vU`D-_SK<=~4eEVQkV~57{vyH6ebu>DqwE~Mx_A31Lg~$ zy`i52ew?GEluE%AN>ju*5z~dx2QQLP-G+E>o|En2+s&<^4*d zWD+<8PXs_Jp7_5Dpi(Gg3J?M;%Rn0{dcxQRh!ip+iHJ_e`!bM1MGxwI8Iedr+x>wI zq@&04fsADHfBccq^gpm8Q_u-~Uj|Z$YwSR(^_mzUb!}Xb2Cj`uqphg}v_Y%?Fs=;= zWq3s{hy)Tu!ji@RJsd$C+G{F6V2j1*Xk+7xo|LO7M2yb)(wc=%x-AclP9pLsbTF7m tf?*P!2EjJu5MD57P32Q4pdJ3tO_shCDT2=K;$ebx8ymcd$v!V9{2$sQiO>K5 literal 0 HcmV?d00001 diff --git a/examples/tvapp/tvapp/Assets.xcassets/second.imageset/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/second.imageset/Contents.json new file mode 100644 index 0000000..03bd9c9 --- /dev/null +++ b/examples/tvapp/tvapp/Assets.xcassets/second.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "second.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/tvapp/tvapp/Assets.xcassets/second.imageset/second.pdf b/examples/tvapp/tvapp/Assets.xcassets/second.imageset/second.pdf new file mode 100644 index 0000000000000000000000000000000000000000..401614e288b4b160471c2776bed6f09762af3e1c GIT binary patch literal 2423 zcmai02~-nz8dqq7rWU#)x2z)&iMWtV?gK$2a)?|BP!TbkV}J-Gl1wmy+3G9S0=uOY zwCjai6uBOPMMdNiN`VDYKwLQr2#W^`R93*Ua=0^)EGYY4=grIf|KIohziYnv{elnR zdN_hk6db<(+3RPcxs!iut8c(j0TLh=9mY931H^rhEE0|aXo!dh5Iv+ZFr+}9F+v#P zLSnfD0$f~hN>~92V{z)WJA&2)5rW!|I?;o?oI*%zejJiuY>hFLQwKlAa9O+PnPh#% zJp~JAfs-De6u@;{cREMs}D-sGib_4>50m8_|Uj-zC0m@Qx zJV+^5Da4Qxpf3sBtKZs<_aI$Cb1T+h<->yL^rfiGNelYkqgC3#SqVP_iy*_RPcbjH)xYf zvYdC-)D_t8c{=5%;$VJQxs_#61J*kuXMC?|&PbPo-7uSxoNr~&J94|gKk+03JGXXM zx4w~=^VUzWqQveu_ilRgPdgNNdnLZCH`=;w91}cP5ihdm*B_jI+rk=Pz2Lk!)XUu| zkls3P5&YHJ!t0kF+MUVBHOAx)a zy|JxHNd37z*~b5#%*0w_F|#4?erNP;pG`Fevoq2%?9Y>%Cmzv1(ks;aPIGaiy1mhU zr{7lB^Mem&`QK{b{uiDC zLD8muY(FNk{YdV|zO#o_VB%);8|~Bh(Z4p?GM}g-HN2;q7~YNl-lD~HZ$taYw$Fzbn6c?c5eo=@`i>()el#qwB)hI-gp0 ztsw@dh-I5NJkk)RCoBC`L8ffKfY)=oG=#4LA9TO6eA}9~@a6dupB+=S{%s&NY}^0GY`hAqod)uxXHitrN%bWOJohV>t?!&Z#d4y^7J@=Oh`HV zee**4j>xRUd)|D0^I-B#<`m1-5PO{)!v(q?ecFYqr_8Q{+z7#n0uFbq%_?hZ-Ck(6NRn=vDS7Nb1Xxx_)9`!i- zGR>av*PJ+P_AGO*a_$FbtCCISg5PK44p&W#t$)+HVcxpR9LAL%_Pv$p`0tO*sYZD0 zCG3-ipr4WoVbjpw%Nf^r^rnWcDcjumgBPEjO;NM6QtZ8wU#0rjHx5zs%CahF4$W*U zxu!q8kYP+>v!BNPVIRJUGIWU@7qVvx2VZDw4gV8%z%bjpF#aF(W|PmliwlakuRq>= zhaCRc^R!7T)#Twb0{!Tns=CxGcGX|leErg`f3|v0eBXxHU&6Sj-_>zOL!!d+j5Xt* zPS;rWd^zEF{5d5%)nNK=)zFJ8Uc=aQKD%UK)_TNes1Gh{SD*Mb_@O` z7*zs9Uq~Vq?v|^8FcM-yLGMKzR3YFB0z@CFM2X-dio%Gu6&MJj=I?uS7sA3AdE^p8 zE49$PjJzPIJwfn;jso7X2#jynr3dO@p5QAK1O9wqKLC=PkQjj~p#n|-5O`ihbTzzu zL{}2QEe28pJYfVZRW56FL`LvO&|C^1Th{0}hQMW$Ng$QPpnzlwnMnf=kVy8(y==L% zLMa>r0gELdT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/tvapp/tvapp/FirstViewController.h b/examples/tvapp/tvapp/FirstViewController.h new file mode 100644 index 0000000..eb65193 --- /dev/null +++ b/examples/tvapp/tvapp/FirstViewController.h @@ -0,0 +1,15 @@ +// +// FirstViewController.h +// tvapp +// +// Created by Felix Krause on 30/12/15. +// Copyright © 2015 Felix Krause. All rights reserved. +// + +#import + +@interface FirstViewController : UIViewController + + +@end + diff --git a/examples/tvapp/tvapp/FirstViewController.m b/examples/tvapp/tvapp/FirstViewController.m new file mode 100644 index 0000000..6179e1f --- /dev/null +++ b/examples/tvapp/tvapp/FirstViewController.m @@ -0,0 +1,27 @@ +// +// FirstViewController.m +// tvapp +// +// Created by Felix Krause on 30/12/15. +// Copyright © 2015 Felix Krause. All rights reserved. +// + +#import "FirstViewController.h" + +@interface FirstViewController () + +@end + +@implementation FirstViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view, typically from a nib. +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +@end diff --git a/examples/tvapp/tvapp/Info.plist b/examples/tvapp/tvapp/Info.plist new file mode 100644 index 0000000..4f33860 --- /dev/null +++ b/examples/tvapp/tvapp/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + + diff --git a/examples/tvapp/tvapp/SecondViewController.h b/examples/tvapp/tvapp/SecondViewController.h new file mode 100644 index 0000000..3d58a53 --- /dev/null +++ b/examples/tvapp/tvapp/SecondViewController.h @@ -0,0 +1,15 @@ +// +// SecondViewController.h +// tvapp +// +// Created by Felix Krause on 30/12/15. +// Copyright © 2015 Felix Krause. All rights reserved. +// + +#import + +@interface SecondViewController : UIViewController + + +@end + diff --git a/examples/tvapp/tvapp/SecondViewController.m b/examples/tvapp/tvapp/SecondViewController.m new file mode 100644 index 0000000..1d921ac --- /dev/null +++ b/examples/tvapp/tvapp/SecondViewController.m @@ -0,0 +1,27 @@ +// +// SecondViewController.m +// tvapp +// +// Created by Felix Krause on 30/12/15. +// Copyright © 2015 Felix Krause. All rights reserved. +// + +#import "SecondViewController.h" + +@interface SecondViewController () + +@end + +@implementation SecondViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view, typically from a nib. +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +@end diff --git a/examples/tvapp/tvapp/main.m b/examples/tvapp/tvapp/main.m new file mode 100644 index 0000000..37959e9 --- /dev/null +++ b/examples/tvapp/tvapp/main.m @@ -0,0 +1,16 @@ +// +// main.m +// tvapp +// +// Created by Felix Krause on 30/12/15. +// Copyright © 2015 Felix Krause. All rights reserved. +// + +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} From c4310f8e45922c6900abe41dbcc88d01c954465a Mon Sep 17 00:00:00 2001 From: KrauseFx Date: Wed, 30 Dec 2015 00:46:09 +0400 Subject: [PATCH 141/176] Added nice error message when trying to use gym on a tvOS project --- lib/gym/runner.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb index 9830171..c6d4e15 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -7,6 +7,11 @@ class Runner # @return (String) The path to the resulting ipa def run clear_old_files + + if Gym.project.tvos? + UI.user_error!("gym doesn't suppoort tvOS projects yet, we're working on adding this feature!") + end + build_app verify_archive From 7577fad25250ca2aa245a9f843389f521764af76 Mon Sep 17 00:00:00 2001 From: KrauseFx Date: Thu, 7 Jan 2016 15:28:26 +0700 Subject: [PATCH 142/176] Improved color disabled detection --- gym.gemspec | 2 +- lib/gym/generators/build_command_generator.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gym.gemspec b/gym.gemspec index 9588dc6..0482330 100644 --- a/gym.gemspec +++ b/gym.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency 'fastlane_core', '>= 0.30.0', '< 1.0.0' # all shared code and dependencies + spec.add_dependency 'fastlane_core', '>= 0.32.0', '< 1.0.0' # all shared code and dependencies spec.add_dependency 'xcpretty', '>= 0.2.1' # pretty xcodebuild output spec.add_dependency 'terminal-table' # print out build information spec.add_dependency 'plist' # Generate the Xcode config plist file diff --git a/lib/gym/generators/build_command_generator.rb b/lib/gym/generators/build_command_generator.rb index da44c3f..adb3333 100644 --- a/lib/gym/generators/build_command_generator.rb +++ b/lib/gym/generators/build_command_generator.rb @@ -60,7 +60,7 @@ def suffix def pipe pipe = [] pipe << "| tee '#{xcodebuild_log_path}' | xcpretty" - pipe << "--no-color" if ENV["FASTLANE_DISABLE_COLORS"] + pipe << "--no-color" if Helper.colors_disabled? pipe << "> /dev/null" if Gym.config[:suppress_xcode_output] pipe From 61b1533f703615ccc07aad7aa7513e494e0f1eb8 Mon Sep 17 00:00:00 2001 From: KrauseFx Date: Thu, 7 Jan 2016 15:28:32 +0700 Subject: [PATCH 143/176] Fixed code style --- lib/gym/manager.rb | 4 ++-- lib/gym/runner.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/gym/manager.rb b/lib/gym/manager.rb index 4450574..d0ed071 100644 --- a/lib/gym/manager.rb +++ b/lib/gym/manager.rb @@ -3,8 +3,8 @@ class Manager def work(options) Gym.config = options - FastlaneCore::PrintTable.print_values(config: Gym.config, - hide_keys: [], + FastlaneCore::PrintTable.print_values(config: Gym.config, + hide_keys: [], title: "Summary for gym #{Gym::VERSION}") return Runner.new.run diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb index c6d4e15..e5c036e 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -121,7 +121,7 @@ def compress_and_move_dsym output_path = File.expand_path(File.join(Gym.config[:output_directory], Gym.config[:output_name] + ".app.dSYM.zip")) command = "cd '#{containing_directory}' && zip -r '#{output_path}' *.dSYM" - Helper.backticks(command, print: !Gym.config[:silent]) + Helper.backticks(command, print: !Gym.config[:silent]) puts "" # new line From e6966dcd225c5928f37f0dba586bacb9b75144a7 Mon Sep 17 00:00:00 2001 From: Jerome Lacoste Date: Thu, 7 Jan 2016 11:45:54 +0100 Subject: [PATCH 144/176] #177 Concatenate LOG_DIR/IDEDistribution.standard.log to output upon xcodebuild failures --- lib/gym/error_handler.rb | 13 +++++++++++++ spec/error_handler.rb | 13 +++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 spec/error_handler.rb diff --git a/lib/gym/error_handler.rb b/lib/gym/error_handler.rb index f0539e6..00c03e4 100644 --- a/lib/gym/error_handler.rb +++ b/lib/gym/error_handler.rb @@ -83,6 +83,9 @@ def handle_package_error(output) print "https://openradar.appspot.com/radar?id=4952000420642816" print "You can temporary use the :use_legacy_build_api option to get the build to work again" when /IDEDistributionErrorDomain error 1/ + when /Error Domain=IDEDistributionErrorDomain Code=/ + standard_output = read_standard_output output + print standard_output if standard_output print "There was an error exporting your application" print "Unfortunately the new Xcode export API is unstable and causes problems on some project" print "You can temporary use the :use_legacy_build_api option to get the build to work again" @@ -99,8 +102,18 @@ def handle_empty_archive raise "Archive invalid" end + def find_standard_output_path(output) + m = /Created bundle at path '(.*)'/.match(output) + return File.join(m[1], 'IDEDistribution.standard.log') unless m.nil? + end + private + def read_standard_output(output) + path = find_standard_output_path output + return File.read(path) if File.exist?(path) + end + # Just to make things easier def print(text) UI.error text diff --git a/spec/error_handler.rb b/spec/error_handler.rb new file mode 100644 index 0000000..7a0324e --- /dev/null +++ b/spec/error_handler.rb @@ -0,0 +1,13 @@ +describe Gym do + describe Gym::ErrorHandler do + it "finds the standard output path" do + output = %( +2015-12-15 13:00:57.177 xcodebuild[81544:4350404] [MT] IDEDistribution: -[IDEDistributionLogging _createLoggingBundleAtPath:]: Created bundle at path '/var/folders/88/l77k840955j0x55fkb3m6cdr0000gn/T/EventLink_2015-12-15_13-00-57.177.xcdistributionlogs'. +2015-12-15 13:00:57.318 xcodebuild[81544:4350404] [MT] IDEDistribution: Failed to generate distribution items with error: Error Domain=DVTMachOErrorDomain Code=0 "Found an unexpected Mach-O header code: 0x72613c21" UserInfo={NSLocalizedDescription=Found an unexpected Mach-O header code: 0x72613c21, NSLocalizedRecoverySuggestion=} +2015-12-15 13:00:57.318 xcodebuild[81544:4350404] [MT] IDEDistribution: Step failed: : Error Domain=DVTMachOErrorDomain Code=0 "Found an unexpected Mach-O header code: 0x72613c21" UserInfo={NSLocalizedDescription=Found an unexpected Mach-O header code: 0x72613c21, NSLocalizedRecoverySuggestion=} +%) + expected = '/var/folders/88/l77k840955j0x55fkb3m6cdr0000gn/T/EventLink_2015-12-15_13-00-57.177.xcdistributionlogs/IDEDistribution.standard.log' + expect(Gym::ErrorHandler.find_standard_output_path output).to eq(expected) + end + end +end From c9b281ea7b848a1d7b0538f9953a898766a10804 Mon Sep 17 00:00:00 2001 From: Jeff Lingis Date: Tue, 5 Jan 2016 09:12:21 -0600 Subject: [PATCH 145/176] Shell escape product name in build command --- lib/gym/generators/build_command_generator.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/gym/generators/build_command_generator.rb b/lib/gym/generators/build_command_generator.rb index adb3333..94a8e77 100644 --- a/lib/gym/generators/build_command_generator.rb +++ b/lib/gym/generators/build_command_generator.rb @@ -1,3 +1,5 @@ +require 'shellwords' + module Gym # Responsible for building the fully working xcodebuild command class BuildCommandGenerator @@ -59,7 +61,7 @@ def suffix def pipe pipe = [] - pipe << "| tee '#{xcodebuild_log_path}' | xcpretty" + pipe << "| tee #{xcodebuild_log_path.shellescape} | xcpretty" pipe << "--no-color" if Helper.colors_disabled? pipe << "> /dev/null" if Gym.config[:suppress_xcode_output] From de553c51096f5f3d0b1a954112e024c9a365f395 Mon Sep 17 00:00:00 2001 From: Michael Furtak Date: Fri, 8 Jan 2016 13:47:23 -0500 Subject: [PATCH 146/176] Fix broken build_command_generator_spec example --- spec/build_command_generator_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/build_command_generator_spec.rb b/spec/build_command_generator_spec.rb index 59bc718..a10fff5 100644 --- a/spec/build_command_generator_spec.rb +++ b/spec/build_command_generator_spec.rb @@ -2,8 +2,8 @@ describe Gym::BuildCommandGenerator do it "raises an exception when project path wasn't found" do expect do - Gym.config = { project: "/notExistent" } - end.to raise_error "Could not find project at path '/notExistent'".red + Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, { project: "/notExistent" }) + end.to raise_error "Project file not found at path '/notExistent'".red end it "supports additional parameters" do From 3c788e1e7d2863040cf0bcee1a890c01a3dad102 Mon Sep 17 00:00:00 2001 From: KrauseFx Date: Sat, 9 Jan 2016 11:44:24 +0700 Subject: [PATCH 147/176] Updated fastlane_core dependency --- gym.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gym.gemspec b/gym.gemspec index 0482330..db069fc 100644 --- a/gym.gemspec +++ b/gym.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency 'fastlane_core', '>= 0.32.0', '< 1.0.0' # all shared code and dependencies + spec.add_dependency 'fastlane_core', '>= 0.32.1', '< 1.0.0' # all shared code and dependencies spec.add_dependency 'xcpretty', '>= 0.2.1' # pretty xcodebuild output spec.add_dependency 'terminal-table' # print out build information spec.add_dependency 'plist' # Generate the Xcode config plist file From 0d726078cb8dc87ab4ee2940e2d9c92189061622 Mon Sep 17 00:00:00 2001 From: KrauseFx Date: Sat, 9 Jan 2016 11:48:18 +0700 Subject: [PATCH 148/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index 3b01a7f..3bf84f0 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "1.2.0" + VERSION = "1.3.0" DESCRIPTION = "Building your iOS apps has never been easier" end From 45040cd5bd38bc8561092ba70d370a2ca96fce1e Mon Sep 17 00:00:00 2001 From: Jerome Lacoste Date: Thu, 7 Jan 2016 10:21:35 +0100 Subject: [PATCH 149/176] Issue #168 wrap xcodebuild calls within ruby system environment --- lib/assets/wrap_xcodebuild/xcbuild-safe.sh | 38 +++++++++++++++++++ .../package_command_generator_xcode7.rb | 2 +- .../package_application_fix.rb | 6 +++ spec/package_command_generator_xcode7_spec.rb | 2 +- 4 files changed, 46 insertions(+), 2 deletions(-) create mode 100755 lib/assets/wrap_xcodebuild/xcbuild-safe.sh diff --git a/lib/assets/wrap_xcodebuild/xcbuild-safe.sh b/lib/assets/wrap_xcodebuild/xcbuild-safe.sh new file mode 100755 index 0000000..90ce87e --- /dev/null +++ b/lib/assets/wrap_xcodebuild/xcbuild-safe.sh @@ -0,0 +1,38 @@ +#!/bin/bash --login + +# Cf. http://stackoverflow.com/questions/33041109 +# +# Xcode 7 (incl. 7.0.1) seems to have a dependency on the system ruby. +# xcodebuild is screwed up by using rvm to map to another non-system +# ruby†. This script is a fix that allows you call xcodebuild in a +# "safe" rvm environment, but will not (AFAIK) affect the "external" +# rvm setting. +# +# The script is a drop in replacement for your xcodebuild call. +# +# xcodebuild arg1 ... argn +# +# would become +# +# path/to/xcbuild-safe.sh arg1 ... argn +# +# ----- +# † Because, you know, that *never* happens when you are building +# Xcode projects, say with abstruse tools like Rake or CocoaPods. + +# This allows you to use rvm in a script. Otherwise you get a BS +# error along the lines of "cannot use rvm as function". Jeez. +[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm" + +# Cause rvm to use system ruby. AFAIK, this is effective only for +# the scope of this script. +which rvm && rvm use system +set -x # echoes commands +unset RUBYLIB +unset RUBYOPT +unset BUNDLE_BIN_PATH +unset _ORIGINAL_GEM_PATH +unset BUNDLE_GEMFILE +#env | sort > /tmp/env.wrapper +#rvm info >> /tmp/env.wrapper +xcodebuild "$@" # calls xcodebuild with all the arguments passed to this diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index 0529221..d7c117a 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -10,7 +10,7 @@ class << self def generate print_legacy_information unless Helper.fastlane_enabled? - parts = ["/usr/bin/xcrun xcodebuild -exportArchive"] + parts = ["/usr/bin/xcrun #{XcodebuildFixes.wrap_xcodebuild} -exportArchive"] parts += options parts += pipe diff --git a/lib/gym/xcodebuild_fixes/package_application_fix.rb b/lib/gym/xcodebuild_fixes/package_application_fix.rb index dd54629..d653420 100644 --- a/lib/gym/xcodebuild_fixes/package_application_fix.rb +++ b/lib/gym/xcodebuild_fixes/package_application_fix.rb @@ -42,6 +42,12 @@ def patch_package_application return @patched_package_application_path # Return path to the patched PackageApplication end + + # Wrap xcodebuild to work-around ipatool dependecy to system ruby + def wrap_xcodebuild + require 'fileutils' + @wrapped_xcodebuild_path ||= File.join(Helper.gem_path("gym"), "lib/assets/wrap_xcodebuild/xcbuild-safe.sh") + end end end end diff --git a/spec/package_command_generator_xcode7_spec.rb b/spec/package_command_generator_xcode7_spec.rb index c49aa9b..420dbf8 100644 --- a/spec/package_command_generator_xcode7_spec.rb +++ b/spec/package_command_generator_xcode7_spec.rb @@ -6,7 +6,7 @@ result = Gym::PackageCommandGeneratorXcode7.generate expect(result).to eq([ - "/usr/bin/xcrun xcodebuild -exportArchive", + "/usr/bin/xcrun #{Gym::XcodebuildFixes.wrap_xcodebuild} -exportArchive", "-exportOptionsPlist '#{Gym::PackageCommandGeneratorXcode7.config_path}'", "-archivePath '#{Gym::BuildCommandGenerator.archive_path}'", "-exportPath '#{Gym::PackageCommandGeneratorXcode7.temporary_output_path}'", From b87bd9c96a3b5a7f21dc7dd3f3150fec4cb7cb8e Mon Sep 17 00:00:00 2001 From: Jerome Lacoste Date: Fri, 8 Jan 2016 11:03:25 +0100 Subject: [PATCH 150/176] Modify wrapper to only work-around broken rvm environment. Tested under rbenv and system ruby --- lib/assets/wrap_xcodebuild/xcbuild-safe.sh | 41 ++++++++++++++-------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/lib/assets/wrap_xcodebuild/xcbuild-safe.sh b/lib/assets/wrap_xcodebuild/xcbuild-safe.sh index 90ce87e..8fc1a6c 100755 --- a/lib/assets/wrap_xcodebuild/xcbuild-safe.sh +++ b/lib/assets/wrap_xcodebuild/xcbuild-safe.sh @@ -1,6 +1,7 @@ #!/bin/bash --login -# Cf. http://stackoverflow.com/questions/33041109 +# Originally from, http://stackoverflow.com/questions/33041109 +# Modified to work in RVM and non RVM environments # # Xcode 7 (incl. 7.0.1) seems to have a dependency on the system ruby. # xcodebuild is screwed up by using rvm to map to another non-system @@ -20,19 +21,29 @@ # † Because, you know, that *never* happens when you are building # Xcode projects, say with abstruse tools like Rake or CocoaPods. -# This allows you to use rvm in a script. Otherwise you get a BS -# error along the lines of "cannot use rvm as function". Jeez. -[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm" +which rvm > /dev/null + +if [[ $? -eq 0 ]]; then + echo "RVM detected, forcing to use system ruby" + # This allows you to use rvm in a script. Otherwise you get a BS + # error along the lines of "cannot use rvm as function". Jeez. + [[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm" + + # Cause rvm to use system ruby. AFAIK, this is effective only for + # the scope of this script. + rvm use system + + # rvm doesn't unset itself properly without doing this + unset RUBYLIB + unset RUBYOPT + unset BUNDLE_BIN_PATH + unset _ORIGINAL_GEM_PATH + unset BUNDLE_GEMFILE +fi + +# to help troubleshooting +# env | sort > /tmp/env.wrapper +# rvm info >> /tmp/env.wrapper -# Cause rvm to use system ruby. AFAIK, this is effective only for -# the scope of this script. -which rvm && rvm use system set -x # echoes commands -unset RUBYLIB -unset RUBYOPT -unset BUNDLE_BIN_PATH -unset _ORIGINAL_GEM_PATH -unset BUNDLE_GEMFILE -#env | sort > /tmp/env.wrapper -#rvm info >> /tmp/env.wrapper -xcodebuild "$@" # calls xcodebuild with all the arguments passed to this +xcodebuild "$@" # calls xcodebuild with all the arguments passed to this \ No newline at end of file From 41dcf91a1fcf325d2ac747960603f52ed593dd44 Mon Sep 17 00:00:00 2001 From: Jerome Lacoste Date: Fri, 8 Jan 2016 12:33:49 +0100 Subject: [PATCH 151/176] Document --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ff6a793..fae76cd 100644 --- a/README.md +++ b/README.md @@ -204,7 +204,7 @@ After building the archive it is being checked by `gym`. If it's valid, it gets ### Xcode 7 and above ``` -/usr/bin/xcrun xcodebuild -exportArchive \ +/usr/bin/xcrun path/to/xcbuild-safe.sh -exportArchive \ -exportOptionsPlist '/tmp/gym_config_1442852529.plist' \ -archivePath '/Users/fkrause/Library/Developer/Xcode/Archives/2015-09-21/App 2015-09-21 09.21.56.xcarchive' \ -exportPath '/tmp/1442852529' @@ -214,6 +214,8 @@ After building the archive it is being checked by `gym`. If it's valid, it gets Using this method there are no workarounds for WatchKit or Swift required, as it uses the same technique Xcode uses when exporting your binary. +Note: the [xcbuild-safe.sh script](https://github.com/fastlane/gym/tree/master/lib/assets/wrap_xcodebuild/xcbuild-safe.sh) wraps around xcodebuild to workaround some incompatibilities. + ### Xcode 6 and below ``` From c0f7feff50536227ce823dd2a7da8651214abc94 Mon Sep 17 00:00:00 2001 From: Igor Makarov Date: Sun, 10 Jan 2016 09:48:13 +0200 Subject: [PATCH 152/176] changed the XCode 7 package command generator to use standard Ruby tempfile class instead of arbitrary /tmp/filename based on current epoch --- lib/gym/generators/package_command_generator_xcode7.rb | 6 ++++-- spec/package_command_generator_xcode7_spec.rb | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index 0529221..421454d 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -3,6 +3,8 @@ # because of # `incompatible encoding regexp match (UTF-8 regexp with ASCII-8BIT string) (Encoding::CompatibilityError)` +require 'tempfile' + module Gym # Responsible for building the fully working xcodebuild command class PackageCommandGeneratorXcode7 @@ -35,7 +37,7 @@ def pipe # We export the ipa into this directory, as we can't specify the ipa file directly def temporary_output_path - Gym.cache[:temporary_output_path] ||= File.join("/tmp", Time.now.to_i.to_s) + Gym.cache[:temporary_output_path] ||= "#{Tempfile.new('gym').path}.gym_output" end def ipa_path @@ -56,7 +58,7 @@ def dsym_path # The path the config file we use to sign our app def config_path - Gym.cache[:config_path] ||= "/tmp/gym_config_#{Time.now.to_i}.plist" + Gym.cache[:config_path] ||= "#{Tempfile.new('gym').path}_config.plist" return Gym.cache[:config_path] end diff --git a/spec/package_command_generator_xcode7_spec.rb b/spec/package_command_generator_xcode7_spec.rb index c49aa9b..5c1f730 100644 --- a/spec/package_command_generator_xcode7_spec.rb +++ b/spec/package_command_generator_xcode7_spec.rb @@ -47,7 +47,7 @@ Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) result = Gym::PackageCommandGeneratorXcode7.generate - expect(Gym::PackageCommandGeneratorXcode7.temporary_output_path).to match(%r{/tmp/\d+}) + expect(Gym::PackageCommandGeneratorXcode7.temporary_output_path).to match(%r{#{Dir.tmpdir}/gym.+\.gym_output}) end end end From ac42a959aac8ac63f1290f44f0756afbdb8d1d95 Mon Sep 17 00:00:00 2001 From: Atsushi Nagase Date: Mon, 11 Jan 2016 21:18:43 +0900 Subject: [PATCH 153/176] Update `Gym::XcodebuildFixes::watchkit2?` to match watchos2.1 --- lib/gym/xcodebuild_fixes/watchkit2_fix.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/xcodebuild_fixes/watchkit2_fix.rb b/lib/gym/xcodebuild_fixes/watchkit2_fix.rb index dd7afee..58e5ebc 100644 --- a/lib/gym/xcodebuild_fixes/watchkit2_fix.rb +++ b/lib/gym/xcodebuild_fixes/watchkit2_fix.rb @@ -27,7 +27,7 @@ def watchkit2_fix # Does this application have a WatchKit target def watchkit2? Dir["#{PackageCommandGenerator.appfile_path}/**/*.plist"].any? do |plist_path| - `/usr/libexec/PlistBuddy -c 'Print DTSDKName' '#{plist_path}' 2>&1`.strip == 'watchos2.0' + `/usr/libexec/PlistBuddy -c 'Print DTSDKName' '#{plist_path}' 2>&1`.match(/^\s*watchos2\.\d+\s*$/) end end end From bf767c0efd4948330962bf3c14546f6e2cbbb1ae Mon Sep 17 00:00:00 2001 From: Michael Furtak Date: Mon, 11 Jan 2016 14:38:30 -0500 Subject: [PATCH 154/176] Change KrauseFx references to fastlane --- README.md | 4 ++-- fastlane/Fastfile | 2 +- fastlane/README.md | 2 +- lib/gym/error_handler.rb | 18 +++++++++--------- .../package_command_generator_xcode7.rb | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index ff6a793..a18f569 100644 --- a/README.md +++ b/README.md @@ -29,14 +29,14 @@ gym ============ -[![Twitter: @KauseFx](https://img.shields.io/badge/contact-@KrauseFx-blue.svg?style=flat)](https://twitter.com/KrauseFx) +[![Twitter: @KauseFx](https://img.shields.io/badge/contact-@FastlaneTools-blue.svg?style=flat)](https://twitter.com/FastlaneTools) [![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/fastlane/gym/blob/master/LICENSE) [![Gem](https://img.shields.io/gem/v/gym.svg?style=flat)](http://rubygems.org/gems/gym) [![Build Status](https://img.shields.io/travis/fastlane/gym/master.svg?style=flat)](https://travis-ci.org/fastlane/gym) ###### Building your app has never been easier -Get in contact with the developer on Twitter: [@KrauseFx](https://twitter.com/KrauseFx) +Get in contact with the developer on Twitter: [@FastlaneTools](https://twitter.com/FastlaneTools) -------

diff --git a/fastlane/Fastfile b/fastlane/Fastfile index efe9ca0..df97c81 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -1,2 +1,2 @@ # Fetch and use the latest Fastfile from the fastlane main repository -import_from_git(url: "https://github.com/krausefx/fastlane/") +import_from_git(url: "https://github.com/fastlane/fastlane/") diff --git a/fastlane/README.md b/fastlane/README.md index f1ac2a9..3a94720 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -20,4 +20,4 @@ Does everything that's needed for a release, this includes running tests and ver Generate this documentation by running `fastlane docs` More information about fastlane can be found on [https://fastlane.tools](https://fastlane.tools). -The documentation of fastlane can be found on [GitHub](https://github.com/KrauseFx/fastlane) \ No newline at end of file +The documentation of fastlane can be found on [GitHub](https://github.com/fastlane/fastlane) \ No newline at end of file diff --git a/lib/gym/error_handler.rb b/lib/gym/error_handler.rb index 00c03e4..cc94ee6 100644 --- a/lib/gym/error_handler.rb +++ b/lib/gym/error_handler.rb @@ -10,31 +10,31 @@ def handle_build_error(output) when /Your build settings specify a provisioning profile with the UUID/ print "Invalid code signing settings" print "Your project defines a provisioning profile which doesn't exist on your local machine" - print "You can use sigh (https://github.com/KrauseFx/sigh) to download and install the provisioning profile" - print "Follow this guide: https://github.com/KrauseFx/fastlane/blob/master/docs/CodeSigning.md" + print "You can use sigh (https://github.com/fastlane/sigh) to download and install the provisioning profile" + print "Follow this guide: https://github.com/fastlane/fastlane/blob/master/docs/CodeSigning.md" when /Provisioning profile does not match bundle identifier/ print "Invalid code signing settings" print "Your project defines a provisioning profile that doesn't match the bundle identifier of your app" print "Make sure you use the correct provisioning profile for this app" print "Take a look at the ouptput above for more information" - print "You can follow this guide: https://github.com/KrauseFx/fastlane/blob/master/docs/CodeSigning.md" + print "You can follow this guide: https://github.com/fastlane/fastlane/blob/master/docs/CodeSigning.md" when /provisioning profiles matching the bundle identifier .(.*)./ # the . around the (.*) are for the strange " print "You don't have the provisioning profile for '#{$1}' installed on the local machine" print "Make sure you have the profile on this computer and it's properly installed" - print "You can use sigh (https://github.com/KrauseFx/sigh) to download and install the provisioning profile" - print "Follow this guide: https://github.com/KrauseFx/fastlane/blob/master/docs/CodeSigning.md" + print "You can use sigh (https://github.com/fastlane/sigh) to download and install the provisioning profile" + print "Follow this guide: https://github.com/fastlane/fastlane/blob/master/docs/CodeSigning.md" when /matching the bundle identifier .(.*). were found/ # the . around the (.*) are for the strange " print "You don't have a provisioning profile for the bundle identifier '#{$1}' installed on the local machine" print "Make sure you have the profile on this computer and it's properly installed" - print "You can use sigh (https://github.com/KrauseFx/sigh) to download and install the provisioning profile" - print "Follow this guide: https://github.com/KrauseFx/fastlane/blob/master/docs/CodeSigning.md" + print "You can use sigh (https://github.com/fastlane/sigh) to download and install the provisioning profile" + print "Follow this guide: https://github.com/fastlane/fastlane/blob/master/docs/CodeSigning.md" # Insert more code signing specific errors here when /code signing is required/ print "Your project settings define invalid code signing settings" print "To generate an ipa file you need to enable code signing for your project" print "Additionally make sure you have a code signing identity set" - print "Follow this guide: https://github.com/KrauseFx/fastlane/blob/master/docs/CodeSigning.md" + print "Follow this guide: https://github.com/fastlane/fastlane/blob/master/docs/CodeSigning.md" when /US\-ASCII/ print "Your shell environment is not correctly configured" print "Instead of UTF-8 your shell uses US-ASCII" @@ -98,7 +98,7 @@ def handle_empty_archive print "Usually it's caused by the `Skip Install` option in Xcode, set it to `NO`" print "For more information visit https://developer.apple.com/library/ios/technotes/tn2215/_index.html" print "Also, make sure to have a valid code signing identity and provisioning profile installed" - print "Follow this guide to setup code signing https://github.com/KrauseFx/fastlane/blob/master/docs/CodeSigning.md" + print "Follow this guide to setup code signing https://github.com/fastlane/fastlane/blob/master/docs/CodeSigning.md" raise "Archive invalid" end diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index 0529221..6183ec6 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -79,7 +79,7 @@ def config_content def print_legacy_information if Gym.config[:provisioning_profile_path] UI.important "You're using Xcode 7, the `provisioning_profile_path` value will be ignored" - UI.important "Please follow the Code Signing Guide: https://github.com/KrauseFx/fastlane/blob/master/docs/CodeSigning.md" + UI.important "Please follow the Code Signing Guide: https://github.com/fastlane/fastlane/blob/master/docs/CodeSigning.md" end end end From 44d509d900770a531406a3b24e3d582a11fdaaf8 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Tue, 12 Jan 2016 11:43:40 -0800 Subject: [PATCH 155/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index 3bf84f0..82a99a5 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "1.3.0" + VERSION = "1.4.0" DESCRIPTION = "Building your iOS apps has never been easier" end From f6ac19367e013ffbed72ede7cbf8bebf99ffeb6a Mon Sep 17 00:00:00 2001 From: Bartosz Janda Date: Mon, 28 Dec 2015 22:27:25 +0100 Subject: [PATCH 156/176] Added support for `-derivedDataPath` and `-resultBundlePath` options. --- lib/gym/generators/build_command_generator.rb | 9 ++++ lib/gym/options.rb | 11 ++++ spec/build_command_generator_spec.rb | 50 ++++++++++++++++++- 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/lib/gym/generators/build_command_generator.rb b/lib/gym/generators/build_command_generator.rb index 94a8e77..c140555 100644 --- a/lib/gym/generators/build_command_generator.rb +++ b/lib/gym/generators/build_command_generator.rb @@ -38,6 +38,8 @@ def options options << "-destination '#{config[:destination]}'" if config[:destination] options << "-xcconfig '#{config[:xcconfig]}'" if config[:xcconfig] options << "-archivePath '#{archive_path}'" + options << "-derivedDataPath '#{config[:derived_data_path]}'" if config[:derived_data_path] + options << "-resultBundlePath '#{result_bundle_path}'" if config[:result_bundle] options << config[:xcargs] if config[:xcargs] options @@ -99,6 +101,13 @@ def archive_path end return Gym.cache[:archive_path] end + + def result_bundle_path + unless Gym.cache[:result_bundle_path] + Gym.cache[:result_bundle_path] = File.join(Gym.config[:output_directory], Gym.config[:output_name]) + ".result" + end + return Gym.cache[:result_bundle_path] + end end end end diff --git a/lib/gym/options.rb b/lib/gym/options.rb index 32c8c31..1c6537a 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -112,6 +112,17 @@ def self.plain_options env_name: "GYM_ARCHIVE_PATH", description: "The directory in which the archive file should be stored in", optional: true), + FastlaneCore::ConfigItem.new(key: :derived_data_path, + short_option: "-f", + env_name: "GYM_DERIVED_DATA_PATH", + description: "The directory where build products and other derived data will go", + optional: true), + FastlaneCore::ConfigItem.new(key: :result_bundle, + short_option: "-u", + env_name: "GYM_RESULT_BUNDLE", + is_string: false, + description: "Produce the result bundle describing what occurred will be placed", + optional: true), FastlaneCore::ConfigItem.new(key: :buildlog_path, short_option: "-l", env_name: "GYM_BUILDLOG_PATH", diff --git a/spec/build_command_generator_spec.rb b/spec/build_command_generator_spec.rb index a10fff5..0012091 100644 --- a/spec/build_command_generator_spec.rb +++ b/spec/build_command_generator_spec.rb @@ -27,7 +27,7 @@ "-archivePath '#{Gym::BuildCommandGenerator.archive_path}'", "DEBUG=1 BUNDLE_NAME=Example\\ App", :archive, - "| tee '#{log_path}' | xcpretty" + "| tee #{log_path.shellescape} | xcpretty" ]) end @@ -49,7 +49,7 @@ "-destination 'generic/platform=iOS'", "-archivePath '#{Gym::BuildCommandGenerator.archive_path}'", :archive, - "| tee '#{log_path}' | xcpretty" + "| tee #{log_path.shellescape} | xcpretty" ]) end @@ -82,5 +82,51 @@ expect(result.to_s).to include("Library/Logs/gym") end end + + describe "Derived Data Example" do + before do + options = { project: "./examples/standard/Example.xcodeproj", derived_data_path: "/tmp/my/derived_data" } + Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) + end + + it "uses the correct build command with the example project" do + log_path = File.expand_path("~/Library/Logs/gym/ExampleProductName-Example.log") + + result = Gym::BuildCommandGenerator.generate + expect(result).to eq([ + "set -o pipefail &&", + "xcodebuild", + "-scheme 'Example'", + "-project './examples/standard/Example.xcodeproj'", + "-destination 'generic/platform=iOS'", + "-archivePath '#{Gym::BuildCommandGenerator.archive_path}'", + "-derivedDataPath '/tmp/my/derived_data'", + :archive, + "| tee #{log_path.shellescape} | xcpretty" + ]) + end + end + + describe "Result Bundle Example" do + it "uses the correct build command with the example project" do + log_path = File.expand_path("~/Library/Logs/gym/ExampleProductName-Example.log") + + options = { project: "./examples/standard/Example.xcodeproj", result_bundle: true } + Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) + + result = Gym::BuildCommandGenerator.generate + expect(result).to eq([ + "set -o pipefail &&", + "xcodebuild", + "-scheme 'Example'", + "-project './examples/standard/Example.xcodeproj'", + "-destination 'generic/platform=iOS'", + "-archivePath '#{Gym::BuildCommandGenerator.archive_path}'", + "-resultBundlePath './ExampleProductName.result'", + :archive, + "| tee #{log_path.shellescape} | xcpretty" + ]) + end + end end end From 3629bfea5eb1dd0ef81c6b72af4ae5e637771aa9 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Fri, 22 Jan 2016 12:09:59 -0800 Subject: [PATCH 157/176] Locked rubocop dependency --- gym.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gym.gemspec b/gym.gemspec index db069fc..8f0e97c 100644 --- a/gym.gemspec +++ b/gym.gemspec @@ -31,7 +31,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency "bundler" spec.add_development_dependency "fastlane", ">= 1.33.0" # yes, we use fastlane for testing spec.add_development_dependency "rake" - spec.add_development_dependency "rubocop" + spec.add_development_dependency "rubocop", '~> 0.35.1' spec.add_development_dependency "rspec", "~> 3.1.0" spec.add_development_dependency "pry" spec.add_development_dependency "yard", "~> 0.8.7.4" From 76497e1dbf74f2a822e3778d64b2d48890ccc266 Mon Sep 17 00:00:00 2001 From: Val Polouchkine Date: Thu, 21 Jan 2016 14:15:17 -0800 Subject: [PATCH 158/176] Update README.md Add link to Code of Conduct. Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index dd1e75a..e0a6ade 100644 --- a/README.md +++ b/README.md @@ -253,6 +253,9 @@ Download and install the [Provisioning Plugin](https://github.com/chockenberry/P # Need help? Please submit an issue on GitHub and provide information about your setup +# Code of Conduct +Help us keep `gym` open and inclusive. Please read and follow our [Code of Conduct](https://github.com/fastlane/code-of-conduct). + # License This project is licensed under the terms of the MIT license. See the LICENSE file. From 761acc520bf0827cb6700d30974b58d455dc6777 Mon Sep 17 00:00:00 2001 From: Aman Gupta Date: Tue, 26 Jan 2016 13:11:46 -0800 Subject: [PATCH 159/176] fix builds for tvOS --- lib/gym/detect_values.rb | 8 +++++++- lib/gym/runner.rb | 4 ---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/gym/detect_values.rb b/lib/gym/detect_values.rb index bc0658d..7494f76 100644 --- a/lib/gym/detect_values.rb +++ b/lib/gym/detect_values.rb @@ -62,7 +62,13 @@ def self.detect_scheme # Is it an iOS device or a Mac? def self.detect_platform return if Gym.config[:destination] - platform = Gym.project.mac? ? "OS X" : "iOS" # either `iOS` or `OS X` + platform = if Gym.project.mac? + "OS X" + elsif Gym.project.tvos? + "tvOS" + else + "iOS" + end Gym.config[:destination] = "generic/platform=#{platform}" end diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb index 98f4806..f00aa25 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -8,10 +8,6 @@ class Runner def run clear_old_files - if Gym.project.tvos? - UI.user_error!("gym doesn't suppoort tvOS projects yet, we're working on adding this feature!") - end - build_app verify_archive From fc38cadffd29badf7567781ae274ee5c17d1a2d2 Mon Sep 17 00:00:00 2001 From: Alexander Semenov Date: Thu, 28 Jan 2016 22:05:24 +0200 Subject: [PATCH 160/176] Replaced move of .app archive with copy for Mac projects --- lib/gym/runner.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb index 98f4806..e113dc2 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -24,7 +24,7 @@ def run move_ipa elsif Gym.project.mac? compress_and_move_dsym - move_mac_app + copy_mac_app end end @@ -139,12 +139,12 @@ def move_ipa ipa_path end - # Move the .app from the archive into the output directory - def move_mac_app + # Copies the .app from the archive into the output directory + def copy_mac_app app_path = Dir[File.join(BuildCommandGenerator.archive_path, "Products/Applications/*.app")].last UI.crash!("Couldn't find application in '#{BuildCommandGenerator.archive_path}'") unless app_path - FileUtils.mv(app_path, File.expand_path(Gym.config[:output_directory]), force: true) + FileUtils.cp_r(app_path, File.expand_path(Gym.config[:output_directory]), remove_destination: true) app_path = File.join(Gym.config[:output_directory], File.basename(app_path)) UI.success "Successfully exported the .app file:" From c1aace0fbd74acbe950869bdc486ce6cfc31d948 Mon Sep 17 00:00:00 2001 From: KrauseFx Date: Mon, 1 Feb 2016 17:26:21 -0800 Subject: [PATCH 161/176] Migrated gym to the new fastlane UI --- lib/gym/commands_generator.rb | 3 ++- lib/gym/detect_values.rb | 2 +- lib/gym/error_handler.rb | 2 +- lib/gym/generators/build_command_generator.rb | 2 +- lib/gym/options.rb | 18 +++++++++--------- .../package_application_fix.rb | 2 +- 6 files changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/gym/commands_generator.rb b/lib/gym/commands_generator.rb index 35c91d6..a92d5b7 100644 --- a/lib/gym/commands_generator.rb +++ b/lib/gym/commands_generator.rb @@ -6,6 +6,7 @@ module Gym class CommandsGenerator include Commander::Methods + UI = FastlaneCore::UI FastlaneCore::CommanderGenerator.new.generate(Gym::Options.available_options) @@ -48,7 +49,7 @@ def run c.action do |_args, options| containing = (File.directory?("fastlane") ? 'fastlane' : '.') path = File.join(containing, Gym.gymfile_name) - raise "Gymfile already exists".yellow if File.exist?(path) + UI.user_error! "Gymfile already exists" if File.exist?(path) template = File.read("#{Helper.gem_path('gym')}/lib/assets/GymfileTemplate") File.write(path, template) UI.success "Successfully created '#{path}'. Open the file using a code editor." diff --git a/lib/gym/detect_values.rb b/lib/gym/detect_values.rb index 7494f76..13a8c6f 100644 --- a/lib/gym/detect_values.rb +++ b/lib/gym/detect_values.rb @@ -42,7 +42,7 @@ def self.detect_provisioning_profile if profiles.count == 1 profile = File.expand_path(profiles.last) elsif profiles.count > 1 - puts "Found more than one provisioning profile in the project directory:" + UI.message "Found more than one provisioning profile in the project directory:" profile = choose(*(profiles)) end diff --git a/lib/gym/error_handler.rb b/lib/gym/error_handler.rb index cc94ee6..83d31a6 100644 --- a/lib/gym/error_handler.rb +++ b/lib/gym/error_handler.rb @@ -99,7 +99,7 @@ def handle_empty_archive print "For more information visit https://developer.apple.com/library/ios/technotes/tn2215/_index.html" print "Also, make sure to have a valid code signing identity and provisioning profile installed" print "Follow this guide to setup code signing https://github.com/fastlane/fastlane/blob/master/docs/CodeSigning.md" - raise "Archive invalid" + UI.user_error!("Archive invalid") end def find_standard_output_path(output) diff --git a/lib/gym/generators/build_command_generator.rb b/lib/gym/generators/build_command_generator.rb index c140555..4d22b87 100644 --- a/lib/gym/generators/build_command_generator.rb +++ b/lib/gym/generators/build_command_generator.rb @@ -25,7 +25,7 @@ def prefix def project_path_array proj = Gym.project.xcodebuild_parameters return proj if proj.count > 0 - raise "No project/workspace found" + UI.user_error!("No project/workspace found") end def options diff --git a/lib/gym/options.rb b/lib/gym/options.rb index 1c6537a..282f0e9 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -18,9 +18,9 @@ def self.plain_options description: "Path the workspace file", verify_block: proc do |value| v = File.expand_path(value.to_s) - raise "Workspace file not found at path '#{v}'".red unless File.exist?(v) - raise "Workspace file invalid".red unless File.directory?(v) - raise "Workspace file is not a workspace, must end with .xcworkspace".red unless v.include?(".xcworkspace") + UI.user_error!("Workspace file not found at path '#{v}'") unless File.exist?(v) + UI.user_error!("Workspace file invalid") unless File.directory?(v) + UI.user_error!("Workspace file is not a workspace, must end with .xcworkspace") unless v.include?(".xcworkspace") end), FastlaneCore::ConfigItem.new(key: :project, short_option: "-p", @@ -29,9 +29,9 @@ def self.plain_options description: "Path the project file", verify_block: proc do |value| v = File.expand_path(value.to_s) - raise "Project file not found at path '#{v}'".red unless File.exist?(v) - raise "Project file invalid".red unless File.directory?(v) - raise "Project file is not a project file, must end with .xcodeproj".red unless v.include?(".xcodeproj") + UI.user_error!("Project file not found at path '#{v}'") unless File.exist?(v) + UI.user_error!("Project file invalid") unless File.directory?(v) + UI.user_error!("Project file is not a project file, must end with .xcodeproj") unless v.include?(".xcodeproj") end), FastlaneCore::ConfigItem.new(key: :scheme, short_option: "-s", @@ -103,7 +103,7 @@ def self.plain_options optional: true, verify_block: proc do |value| av = %w(app-store ad-hoc package enterprise development developer-id) - raise "Unsupported export_method, must be: #{av}" unless av.include?(value) + UI.user_error!("Unsupported export_method, must be: #{av}") unless av.include?(value) end), # Very optional @@ -139,7 +139,7 @@ def self.plain_options description: "The path to the provisioning profile (optional)", optional: true, verify_block: proc do |value| - raise "Provisioning profile not found at path '#{File.expand_path(value)}'".red unless File.exist?(value) + UI.user_error!("Provisioning profile not found at path '#{File.expand_path(value)}'") unless File.exist?(value) end), FastlaneCore::ConfigItem.new(key: :destination, short_option: "-d", @@ -162,7 +162,7 @@ def self.plain_options description: "Use an extra XCCONFIG file to build your app", optional: true, verify_block: proc do |value| - raise "File not found at path '#{File.expand_path(value)}'".red unless File.exist?(value) + UI.user_error!("File not found at path '#{File.expand_path(value)}'") unless File.exist?(value) end), FastlaneCore::ConfigItem.new(key: :suppress_xcode_output, short_option: "-r", diff --git a/lib/gym/xcodebuild_fixes/package_application_fix.rb b/lib/gym/xcodebuild_fixes/package_application_fix.rb index d653420..d9a8347 100644 --- a/lib/gym/xcodebuild_fixes/package_application_fix.rb +++ b/lib/gym/xcodebuild_fixes/package_application_fix.rb @@ -20,7 +20,7 @@ def patch_package_application # If that location changes, search it using xcrun --sdk iphoneos -f PackageApplication package_application_path = "#{Xcode.xcode_path}/Platforms/iPhoneOS.platform/Developer/usr/bin/PackageApplication" - raise "Unable to patch the `PackageApplication` script bundled in XCode. This is not supported." unless expected_md5 == Digest::MD5.file(package_application_path).hexdigest + UI.crash!("Unable to patch the `PackageApplication` script bundled in Xcode. This is not supported.") unless expected_md5 == Digest::MD5.file(package_application_path).hexdigest # Duplicate PackageApplication script to PackageApplication4Gym FileUtils.copy_file(package_application_path, @patched_package_application_path) From f7fea1d8ce02b2d3e1fab6b58fe23dac67680819 Mon Sep 17 00:00:00 2001 From: KrauseFx Date: Mon, 1 Feb 2016 17:52:11 -0800 Subject: [PATCH 162/176] Updated fastlane_core dependency --- gym.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gym.gemspec b/gym.gemspec index 8f0e97c..310eefc 100644 --- a/gym.gemspec +++ b/gym.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency 'fastlane_core', '>= 0.32.1', '< 1.0.0' # all shared code and dependencies + spec.add_dependency 'fastlane_core', '>= 0.36.1', '< 1.0.0' # all shared code and dependencies spec.add_dependency 'xcpretty', '>= 0.2.1' # pretty xcodebuild output spec.add_dependency 'terminal-table' # print out build information spec.add_dependency 'plist' # Generate the Xcode config plist file From 4d5674cb0d356ecae6dadb14a70813b7cdc08270 Mon Sep 17 00:00:00 2001 From: KrauseFx Date: Mon, 1 Feb 2016 17:53:09 -0800 Subject: [PATCH 163/176] Fixed tests for new UI --- spec/build_command_generator_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/build_command_generator_spec.rb b/spec/build_command_generator_spec.rb index 0012091..814e2ac 100644 --- a/spec/build_command_generator_spec.rb +++ b/spec/build_command_generator_spec.rb @@ -3,7 +3,7 @@ it "raises an exception when project path wasn't found" do expect do Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, { project: "/notExistent" }) - end.to raise_error "Project file not found at path '/notExistent'".red + end.to raise_error "Project file not found at path '/notExistent'" end it "supports additional parameters" do From 5392c5c6e6a04a480891e447802d7a4f11231522 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Thu, 4 Feb 2016 15:30:15 -0800 Subject: [PATCH 164/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index 82a99a5..e8aedc5 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "1.4.0" + VERSION = "1.5.0" DESCRIPTION = "Building your iOS apps has never been easier" end From 12948c1aa4c52f26d510e391b528c03b320ad65e Mon Sep 17 00:00:00 2001 From: Jeremiah Njoroge Date: Sat, 6 Feb 2016 00:33:54 -0800 Subject: [PATCH 165/176] Fix typo in Contributing.md file --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 68a38a4..99f5ca2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,7 @@ To clone the [fastlane](https://fastlane.tools) repos, use the [countdown](https Before submitting a new issue, do the following: -- Verify you're runing the latest version by running `gym -v` and compare it with the [project page on GitHub](https://github.com/fastlane/gym). +- Verify you're running the latest version by running `gym -v` and compare it with the [project page on GitHub](https://github.com/fastlane/gym). - Verify you have Xcode tools installed by running `xcode-select --install`. - Make sure to read through the [README](https://github.com/fastlane/gym) of the project. From 34f30a9764fc77a9699e73c3b03a190e5e3dfcf2 Mon Sep 17 00:00:00 2001 From: olegoid Date: Mon, 8 Feb 2016 23:12:41 -0300 Subject: [PATCH 166/176] Set conflicting options: * Use conflicting options mechanism for workspace and project paths * Update specs --- lib/gym/options.rb | 8 ++++++++ spec/options_spec.rb | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/gym/options.rb b/lib/gym/options.rb index 282f0e9..096a757 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -21,6 +21,10 @@ def self.plain_options UI.user_error!("Workspace file not found at path '#{v}'") unless File.exist?(v) UI.user_error!("Workspace file invalid") unless File.directory?(v) UI.user_error!("Workspace file is not a workspace, must end with .xcworkspace") unless v.include?(".xcworkspace") + end, + conflicting_options: [:project], + conflict_block: proc do |value| + UI.user_error!("You can only pass either a 'workspace' or a '#{value.key}', not both") end), FastlaneCore::ConfigItem.new(key: :project, short_option: "-p", @@ -32,6 +36,10 @@ def self.plain_options UI.user_error!("Project file not found at path '#{v}'") unless File.exist?(v) UI.user_error!("Project file invalid") unless File.directory?(v) UI.user_error!("Project file is not a project file, must end with .xcodeproj") unless v.include?(".xcodeproj") + end, + conflicting_options: [:workspace], + conflict_block: proc do |value| + UI.user_error!("You can only pass either a 'project' or a '#{value.key}', not both") end), FastlaneCore::ConfigItem.new(key: :scheme, short_option: "-s", diff --git a/spec/options_spec.rb b/spec/options_spec.rb index df65f39..bb24da3 100644 --- a/spec/options_spec.rb +++ b/spec/options_spec.rb @@ -4,7 +4,7 @@ expect do options = { project: "./examples/standard/Example.xcodeproj", workspace: "./examples/cocoapods/Example.xcworkspace" } Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) - end.to raise_error "You can only pass either a workspace or a project path, not both".red + end.to raise_error "You can only pass either a 'project' or a 'workspace', not both" end it "removes the `ipa` from the output name if given" do From 0d78a2260c64acbc7d94dc297404f8221ce49bf3 Mon Sep 17 00:00:00 2001 From: Bartosz Janda Date: Sat, 23 Jan 2016 20:58:26 +0100 Subject: [PATCH 167/176] Added support for `export_options` and app thinning --- README.md | 22 +++++ examples/standard/ExampleExport.plist | 19 ++++ lib/gym/detect_values.rb | 3 - .../generators/package_command_generator.rb | 16 ++++ .../package_command_generator_legacy.rb | 16 ++++ .../package_command_generator_xcode7.rb | 95 ++++++++++++++++-- lib/gym/options.rb | 17 +++- lib/gym/runner.rb | 56 ++++++++++- spec/package_command_generator_xcode7_spec.rb | 96 +++++++++++++++++++ 9 files changed, 325 insertions(+), 15 deletions(-) create mode 100644 examples/standard/ExampleExport.plist diff --git a/README.md b/README.md index e0a6ade..7920bdb 100644 --- a/README.md +++ b/README.md @@ -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: "" +) +``` + +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. diff --git a/examples/standard/ExampleExport.plist b/examples/standard/ExampleExport.plist new file mode 100644 index 0000000..6c136c5 --- /dev/null +++ b/examples/standard/ExampleExport.plist @@ -0,0 +1,19 @@ + + + + + embedOnDemandResourcesAssetPacksInBundle + + manifest + + appURL + https://www.example.com/Example.ipa + displayImageURL + https://www.example.com/display.png + fullSizeImageURL + https://www.example.com/fullSize.png + + method + ad-hoc + + diff --git a/lib/gym/detect_values.rb b/lib/gym/detect_values.rb index 13a8c6f..71c5a96 100644 --- a/lib/gym/detect_values.rb +++ b/lib/gym/detect_values.rb @@ -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 diff --git a/lib/gym/generators/package_command_generator.rb b/lib/gym/generators/package_command_generator.rb index 30e03fe..20a636d 100644 --- a/lib/gym/generators/package_command_generator.rb +++ b/lib/gym/generators/package_command_generator.rb @@ -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] diff --git a/lib/gym/generators/package_command_generator_legacy.rb b/lib/gym/generators/package_command_generator_legacy.rb index 25b2a7e..624e3c1 100644 --- a/lib/gym/generators/package_command_generator_legacy.rb +++ b/lib/gym/generators/package_command_generator_legacy.rb @@ -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 diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb index fae7cb8..5c27725 100644 --- a/lib/gym/generators/package_command_generator_xcode7.rb +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -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 @@ -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] diff --git a/lib/gym/options.rb b/lib/gym/options.rb index 096a757..2bdad1a 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -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", @@ -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, diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb index b562bc1..931dc0e 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -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 @@ -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 diff --git a/spec/package_command_generator_xcode7_spec.rb b/spec/package_command_generator_xcode7_spec.rb index da3e74c..8486874 100644 --- a/spec/package_command_generator_xcode7_spec.rb +++ b/spec/package_command_generator_xcode7_spec.rb @@ -29,6 +29,98 @@ }) end + it "reads user export plist" do + options = { project: "./examples/standard/Example.xcodeproj", export_options: "./examples/standard/ExampleExport.plist" } + Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) + + result = Gym::PackageCommandGeneratorXcode7.generate + config_path = Gym::PackageCommandGeneratorXcode7.config_path + + require 'plist' + expect(Plist.parse_xml(config_path)).to eq({ + 'embedOnDemandResourcesAssetPacksInBundle' => true, + 'manifest' => { + 'appURL' => 'https://www.example.com/Example.ipa', + 'displayImageURL' => 'https://www.example.com/display.png', + 'fullSizeImageURL' => 'https://www.example.com/fullSize.png' + }, + 'method' => 'ad-hoc' + }) + expect(Gym.config[:export_method]).to eq("ad-hoc") + expect(Gym.config[:include_symbols]).to be_nil + expect(Gym.config[:include_bitcode]).to be_nil + expect(Gym.config[:export_team_id]).to be_nil + end + + it "reads user export plist and override some parameters" do + options = { + project: "./examples/standard/Example.xcodeproj", + export_options: "./examples/standard/ExampleExport.plist", + export_method: "app-store", + include_symbols: false, + include_bitcode: true, + export_team_id: "1234567890" + } + Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) + + result = Gym::PackageCommandGeneratorXcode7.generate + config_path = Gym::PackageCommandGeneratorXcode7.config_path + + require 'plist' + expect(Plist.parse_xml(config_path)).to eq({ + 'embedOnDemandResourcesAssetPacksInBundle' => true, + 'manifest' => { + 'appURL' => 'https://www.example.com/Example.ipa', + 'displayImageURL' => 'https://www.example.com/display.png', + 'fullSizeImageURL' => 'https://www.example.com/fullSize.png' + }, + 'method' => 'app-store', + 'uploadSymbols' => false, + 'uploadBitcode' => true, + 'teamID' => '1234567890' + }) + end + + it "reads export options from hash" do + options = { + project: "./examples/standard/Example.xcodeproj", + export_options: { + embedOnDemandResourcesAssetPacksInBundle: false, + manifest: { + appURL: "https://example.com/My App.ipa", + displayImageURL: "https://www.example.com/display image.png", + fullSizeImageURL: "https://www.example.com/fullSize image.png" + }, + method: "enterprise", + uploadSymbols: false, + uploadBitcode: true, + teamID: "1234567890" + }, + export_method: "app-store", + include_symbols: true, + include_bitcode: false, + export_team_id: "ASDFGHJK" + } + Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) + + result = Gym::PackageCommandGeneratorXcode7.generate + config_path = Gym::PackageCommandGeneratorXcode7.config_path + + require 'plist' + expect(Plist.parse_xml(config_path)).to eq({ + 'embedOnDemandResourcesAssetPacksInBundle' => false, + 'manifest' => { + 'appURL' => 'https://example.com/My%20App.ipa', + 'displayImageURL' => 'https://www.example.com/display%20image.png', + 'fullSizeImageURL' => 'https://www.example.com/fullSize%20image.png' + }, + 'method' => 'app-store', + 'uploadSymbols' => true, + 'uploadBitcode' => false, + 'teamID' => 'ASDFGHJK' + }) + end + it "doesn't store bitcode/symbols information for non app-store builds" do options = { project: "./examples/standard/Example.xcodeproj", export_method: 'ad-hoc' } Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) @@ -48,6 +140,10 @@ result = Gym::PackageCommandGeneratorXcode7.generate expect(Gym::PackageCommandGeneratorXcode7.temporary_output_path).to match(%r{#{Dir.tmpdir}/gym.+\.gym_output}) + expect(Gym::PackageCommandGeneratorXcode7.manifest_path).to match(%r{#{Dir.tmpdir}/gym.+\.gym_output/manifest.plist}) + expect(Gym::PackageCommandGeneratorXcode7.app_thinning_path).to match(%r{#{Dir.tmpdir}/gym.+\.gym_output/app-thinning.plist}) + expect(Gym::PackageCommandGeneratorXcode7.app_thinning_size_report_path).to match(%r{#{Dir.tmpdir}/gym.+\.gym_output/App Thinning Size Report.txt}) + expect(Gym::PackageCommandGeneratorXcode7.apps_path).to match(%r{#{Dir.tmpdir}/gym.+\.gym_output/Apps}) end end end From 6d1edf9c5eb332fda2648275a5abc1343eef0b84 Mon Sep 17 00:00:00 2001 From: Mark Pirri Date: Wed, 3 Feb 2016 16:48:31 -0500 Subject: [PATCH 168/176] Mark Xcode archives generated by gym with extended attribute --- lib/gym/runner.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb index b562bc1..e98879b 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -1,6 +1,7 @@ require 'pty' require 'open3' require 'fileutils' +require 'shellwords' module Gym class Runner @@ -71,6 +72,11 @@ def fix_package Gym::XcodebuildFixes.watchkit2_fix end + def mark_archive_as_built_by_gym(archive_path) + escaped_archive_path = archive_path.shellescape + system("xattr -w info.fastlane.generated_by_gym 1 #{escaped_archive_path}") + end + # Builds the app and prepares the archive def build_app command = BuildCommandGenerator.generate @@ -84,6 +90,7 @@ def build_app UI.success "Successfully stored the archive. You can find it in the Xcode Organizer." UI.verbose("Stored the archive in: " + BuildCommandGenerator.archive_path) + mark_archive_as_built_by_gym(BuildCommandGenerator.archive_path) end # Makes sure the archive is there and valid From 26b5428ac30b10fe86d79509973bcd1dbae9d1ef Mon Sep 17 00:00:00 2001 From: Mark Pirri Date: Tue, 9 Feb 2016 21:59:19 -0500 Subject: [PATCH 169/176] PR feedback: mark the archive as built by gym before logging success --- lib/gym/runner.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb index e98879b..27b790b 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -88,9 +88,9 @@ def build_app ErrorHandler.handle_build_error(output) end) + mark_archive_as_built_by_gym(BuildCommandGenerator.archive_path) UI.success "Successfully stored the archive. You can find it in the Xcode Organizer." UI.verbose("Stored the archive in: " + BuildCommandGenerator.archive_path) - mark_archive_as_built_by_gym(BuildCommandGenerator.archive_path) end # Makes sure the archive is there and valid From 43ac54f692c6e858e65e828595f925a2d0b52f7f Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 10 Feb 2016 16:35:47 -0800 Subject: [PATCH 170/176] Version bump --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index e8aedc5..cd17a54 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "1.5.0" + VERSION = "1.6.0" DESCRIPTION = "Building your iOS apps has never been easier" end From b2bc53aac3462d8d433111059c588967e5d07059 Mon Sep 17 00:00:00 2001 From: Bartosz Janda Date: Sat, 13 Feb 2016 13:01:48 +0100 Subject: [PATCH 171/176] Added `build_path` option --- lib/gym/generators/build_command_generator.rb | 3 ++- lib/gym/options.rb | 6 +++++- spec/build_command_generator_spec.rb | 9 ++++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/gym/generators/build_command_generator.rb b/lib/gym/generators/build_command_generator.rb index 4d22b87..8bb8674 100644 --- a/lib/gym/generators/build_command_generator.rb +++ b/lib/gym/generators/build_command_generator.rb @@ -78,8 +78,9 @@ def xcodebuild_log_path return File.join(containing, file_name) end - # The path to set the Derived Data to + # The path where archive will be created def build_path + Gym.cache[:build_path] ||= Gym.config[:build_path] unless Gym.cache[:build_path] day = Time.now.strftime("%F") # e.g. 2015-08-07 diff --git a/lib/gym/options.rb b/lib/gym/options.rb index 2bdad1a..ceba663 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -124,10 +124,14 @@ def self.plain_options end), # Very optional + FastlaneCore::ConfigItem.new(key: :build_path, + env_name: "GYM_BUILD_PATH", + description: "The directory in which the archive should be stored in", + optional: true), FastlaneCore::ConfigItem.new(key: :archive_path, short_option: "-b", env_name: "GYM_ARCHIVE_PATH", - description: "The directory in which the archive file should be stored in", + description: "The path to the created archive", optional: true), FastlaneCore::ConfigItem.new(key: :derived_data_path, short_option: "-f", diff --git a/spec/build_command_generator_spec.rb b/spec/build_command_generator_spec.rb index 814e2ac..5f53f4f 100644 --- a/spec/build_command_generator_spec.rb +++ b/spec/build_command_generator_spec.rb @@ -58,12 +58,19 @@ expect(result).to eq(["-scheme 'Example'", "-project './examples/standard/Example.xcodeproj'"]) end - it "#build_path" do + it "default #build_path" do result = Gym::BuildCommandGenerator.build_path regex = %r{Library/Developer/Xcode/Archives/\d\d\d\d\-\d\d\-\d\d} expect(result).to match(regex) end + it "user provided #build_path" do + options = { project: "./examples/standard/Example.xcodeproj", build_path: "/tmp/my/build_path" } + Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) + result = Gym::BuildCommandGenerator.build_path + expect(result).to eq("/tmp/my/build_path") + end + it "#archive_path" do result = Gym::BuildCommandGenerator.archive_path regex = %r{Library/Developer/Xcode/Archives/\d\d\d\d\-\d\d\-\d\d/ExampleProductName \d\d\d\d\-\d\d\-\d\d \d\d\.\d\d\.\d\d.xcarchive} From 345529d4429c3ee76410e410f8f05a1770858ca2 Mon Sep 17 00:00:00 2001 From: Bartosz Janda Date: Mon, 15 Feb 2016 19:38:30 +0100 Subject: [PATCH 172/176] Fixed `mkdir -p` for `build_path`. --- lib/gym/generators/build_command_generator.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/gym/generators/build_command_generator.rb b/lib/gym/generators/build_command_generator.rb index 8bb8674..54c43c8 100644 --- a/lib/gym/generators/build_command_generator.rb +++ b/lib/gym/generators/build_command_generator.rb @@ -80,11 +80,12 @@ def xcodebuild_log_path # The path where archive will be created def build_path - Gym.cache[:build_path] ||= Gym.config[:build_path] unless Gym.cache[:build_path] - day = Time.now.strftime("%F") # e.g. 2015-08-07 - - Gym.cache[:build_path] = File.expand_path("~/Library/Developer/Xcode/Archives/#{day}/") + Gym.cache[:build_path] = Gym.config[:build_path] + unless Gym.cache[:build_path] + day = Time.now.strftime("%F") # e.g. 2015-08-07 + Gym.cache[:build_path] = File.expand_path("~/Library/Developer/Xcode/Archives/#{day}/") + end FileUtils.mkdir_p Gym.cache[:build_path] end Gym.cache[:build_path] From 3cd82e79eb875f63957fdcaac84572ed750128a1 Mon Sep 17 00:00:00 2001 From: Mark Pirri Date: Wed, 17 Feb 2016 14:46:26 -0500 Subject: [PATCH 173/176] Version bump to 1.6.1 --- lib/gym/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gym/version.rb b/lib/gym/version.rb index cd17a54..37c81aa 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "1.6.0" + VERSION = "1.6.1" DESCRIPTION = "Building your iOS apps has never been easier" end From 7a4382dbbfb574cfe3daf1730a95d1a73e23b699 Mon Sep 17 00:00:00 2001 From: Mark Pirri Date: Wed, 17 Feb 2016 15:32:50 -0500 Subject: [PATCH 174/176] Add coverage directory to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 05ffc06..bdba1cf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .DS_Store Gemfile.lock +/coverage/ *UserInterfaceState.xcuserstate pkg **/Example.app.dSYM.zip From 96a639280dbf4676d566660e54ebd96dcabc1d11 Mon Sep 17 00:00:00 2001 From: Michael Furtak Date: Thu, 18 Feb 2016 10:26:37 -0500 Subject: [PATCH 175/176] Fix example use of --use_legacy_build_api flag Boolean command line flags do not take parameters, so removing it from this example will prevent users from learning a style that doesn't really work --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7920bdb..316271f 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ If you run into any issues, use the `verbose` mode to get more information In general, if you run into issues while exporting the archive, try using: - gym --use_legacy_build_api true + gym --use_legacy_build_api Set the right export method if you're not uploading to App Store or TestFlight: From e403cbfa1aa19b5e018be80bd0e6fdf89fc4621c Mon Sep 17 00:00:00 2001 From: Andrea Falcone Date: Mon, 22 Feb 2016 21:23:02 -0500 Subject: [PATCH 176/176] Add USE_SIMPLE_POPEN ENV var to CI to test hang fix --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 0a800ee..5e6f6f4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ language: ruby osx_image: xcode7.1 os: osx +env: + - USE_SIMPLE_POPEN=true before_install: - gem update --system - gem install bundler

- +
fastlane