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 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/.travis.yml b/.travis.yml index 9167d41..5e6f6f4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,12 @@ -language: objective-c -osx_image: xcode6.4 -before_install: gem update --system +language: ruby +osx_image: xcode7.1 +os: osx +env: + - USE_SIMPLE_POPEN=true +before_install: + - gem update --system + - gem install bundler rvm: - - 2.0.0 + - 2.1.6 + - 2.2.2 script: bundle exec fastlane test 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. 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 diff --git a/README.md b/README.md index 03d2877..316271f 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,42 @@

- +
fastlane

- deliver • - snapshot • - frameit • - PEM • + deliver • + snapshot • + frameit • + pemsigh • - produce • - cert • - codes • + produce • + certspaceshippilotboarding • - gym + gym • + scan • + match

-------

- +

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) +[![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) -------

@@ -100,8 +101,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: @@ -116,15 +115,32 @@ 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 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 + +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 + +To access the raw `xcodebuild` output open `~/Library/Logs/gym` + # 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`. @@ -134,7 +150,7 @@ Run `gym init` to create a new configuration file. Example: ```ruby scheme "Example" -sdk "9.0" +sdk "iphoneos9.0" clean true @@ -142,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. @@ -158,7 +196,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? @@ -181,9 +219,27 @@ 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 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' +``` + +`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. + +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 + ``` /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 \ @@ -199,16 +255,17 @@ 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 -- [`codes`](https://github.com/KrauseFx/codes): Create promo codes for iOS Apps using the command line +- [`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 +- [`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) @@ -218,6 +275,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. 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 diff --git a/assets/gym.png b/assets/gym.png index 97d7532..8a3d16e 100644 Binary files a/assets/gym.png and b/assets/gym.png differ diff --git a/assets/gym.pxm b/assets/gym.pxm deleted file mode 100644 index 3bf7158..0000000 Binary files a/assets/gym.pxm and /dev/null differ diff --git a/assets/gymFullSize.png b/assets/gymFullSize.png deleted file mode 100644 index 73a7c58..0000000 Binary files a/assets/gymFullSize.png and /dev/null differ 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/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/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/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 0000000..47d911d Binary files /dev/null and b/examples/tvapp/tvapp/Assets.xcassets/first.imageset/first.pdf differ 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 0000000..401614e Binary files /dev/null and b/examples/tvapp/tvapp/Assets.xcassets/second.imageset/second.pdf differ diff --git a/examples/tvapp/tvapp/Base.lproj/Main.storyboard b/examples/tvapp/tvapp/Base.lproj/Main.storyboard new file mode 100644 index 0000000..1da21ff --- /dev/null +++ b/examples/tvapp/tvapp/Base.lproj/Main.storyboard @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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])); + } +} 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 + + + + + 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/gym.gemspec b/gym.gemspec index 77ccd0b..310eefc 100644 --- a/gym.gemspec +++ b/gym.gemspec @@ -21,16 +21,17 @@ 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 'xcpretty' # pretty xcodebuild output + 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 spec.add_dependency 'rubyzip', '>= 1.1.7' # fix swift/ipa # 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 "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" diff --git a/lib/assets/GymfileTemplate b/lib/assets/GymfileTemplate index b0fa0fe..561d4e6 100644 --- a/lib/assets/GymfileTemplate +++ b/lib/assets/GymfileTemplate @@ -1,14 +1,13 @@ -# 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 # 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" -# sdk "9.0" +# sdk "iphoneos9.0" output_directory "./" diff --git a/lib/assets/wrap_xcodebuild/xcbuild-safe.sh b/lib/assets/wrap_xcodebuild/xcbuild-safe.sh new file mode 100755 index 0000000..8fc1a6c --- /dev/null +++ b/lib/assets/wrap_xcodebuild/xcbuild-safe.sh @@ -0,0 +1,49 @@ +#!/bin/bash --login + +# 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 +# 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. + +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 + +set -x # echoes commands +xcodebuild "$@" # calls xcodebuild with all the arguments passed to this \ No newline at end of file diff --git a/lib/gym.rb b/lib/gym.rb index 7c1167a..279780a 100644 --- a/lib/gym.rb +++ b/lib/gym.rb @@ -1,18 +1,13 @@ require 'json' 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' - -# Import all the fixes -require 'gym/xcodebuild_fixes/swift_fix' -require 'gym/xcodebuild_fixes/watchkit_fix' -require 'gym/xcodebuild_fixes/package_application_fix' +require 'gym/xcode' require 'fastlane_core' require 'terminal-table' @@ -23,19 +18,29 @@ class << self attr_accessor :project + attr_accessor :cache + def config=(value) @config = value DetectValues.set_additional_default_values + @cache = {} end def gymfile_name "Gymfile" end - def xcode_path - @path ||= `xcode-select --print-path`.strip + 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 Helper = FastlaneCore::Helper # you gotta love Ruby: Helper.* should use the Helper class contained in FastlaneCore + UI = FastlaneCore::UI + + Gym.init_libs end diff --git a/lib/gym/build_command_generator.rb b/lib/gym/build_command_generator.rb deleted file mode 100644 index e1ef677..0000000 --- a/lib/gym/build_command_generator.rb +++ /dev/null @@ -1,88 +0,0 @@ -module Gym - # Responsible for building the fully working xcodebuild command - class BuildCommandGenerator - class << self - def generate - parts = prefix - parts << "xcodebuild" - parts += options - parts += actions - parts += suffix - parts += pipe - - parts - end - - def prefix - ["set -o pipefail &&"] - end - - # Path to the project or workspace as parameter - # 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] - - return proj if proj.count > 0 - raise "No project/workspace found" - end - - def options - config = Gym.config - - options = [] - options += project_path_array - options << "-configuration '#{config[:configuration]}'" if config[:configuration] - options << "-sdk '#{config[:sdk]}'" if config[:sdk] - options << "-destination '#{config[:destination]}'" if config[:destination] - options << "-xcconfig '#{config[:xcconfig]}'" if config[:xcconfig] - options << "-archivePath '#{archive_path}'" - options << config[:xcargs] if config[:xcargs] - - options - end - - def actions - config = Gym.config - - actions = [] - actions << :clean if config[:clean] - actions << :archive - - actions - end - - def suffix - [] - end - - def pipe - ["| xcpretty"] - end - - # The path to set the Derived Data to - def build_path - unless @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 - end - @build_path - end - - def archive_path - @archive_path ||= Gym.config[:archive_path] - unless @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") - end - return @archive_path - end - end - end -end diff --git a/lib/gym/commands_generator.rb b/lib/gym/commands_generator.rb index 425f49a..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) @@ -46,10 +47,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) + UI.user_error! "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) + 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 6fe2671..71c5a96 100644 --- a/lib/gym/detect_values.rb +++ b/lib/gym/detect_values.rb @@ -6,24 +6,21 @@ class DetectValues def self.set_additional_default_values config = Gym.config - detect_projects + # First, try loading the Gymfile from the current directory + config.load_configuration_file(Gym.gymfile_name) - 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 - - Gym.project = Project.new(config) + # 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 + 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 @@ -42,7 +39,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 @@ -55,84 +52,20 @@ def self.detect_provisioning_profile end end - def self.detect_projects - 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 - - 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 - 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? 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 = if Gym.project.mac? + "OS X" + elsif Gym.project.tvos? + "tvOS" + else + "iOS" + end Gym.config[:destination] = "generic/platform=#{platform}" end @@ -146,29 +79,10 @@ 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 - - # 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 diff --git a/lib/gym/error_handler.rb b/lib/gym/error_handler.rb index 767d977..83d31a6 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 @@ -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" @@ -47,9 +47,9 @@ 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 + UI.user_error!("Error building the application - see the log above") end # @param [Array] The output of the errored build (line by line) @@ -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" @@ -80,22 +78,45 @@ 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" + 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" end - raise "Error packaging up the application".red + UI.user_error!("Error packaging up the application") end 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" - raise "Archive invalid" + 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" + UI.user_error!("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) - Helper.log.error text.red + UI.error text end end 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/generators/build_command_generator.rb b/lib/gym/generators/build_command_generator.rb new file mode 100644 index 0000000..54c43c8 --- /dev/null +++ b/lib/gym/generators/build_command_generator.rb @@ -0,0 +1,115 @@ +require 'shellwords' + +module Gym + # Responsible for building the fully working xcodebuild command + class BuildCommandGenerator + class << self + def generate + parts = prefix + parts << "xcodebuild" + parts += options + parts += actions + parts += suffix + parts += pipe + + parts + end + + def prefix + ["set -o pipefail &&"] + end + + # Path to the project or workspace as parameter + # This will also include the scheme (if given) + # @return [Array] The array with all the components to join + def project_path_array + proj = Gym.project.xcodebuild_parameters + return proj if proj.count > 0 + UI.user_error!("No project/workspace found") + end + + def options + config = Gym.config + + options = [] + options += project_path_array + options << "-configuration '#{config[:configuration]}'" if config[:configuration] + options << "-sdk '#{config[:sdk]}'" if config[:sdk] + 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 + end + + def actions + config = Gym.config + + actions = [] + actions << :clean if config[:clean] + actions << :archive + + actions + end + + def suffix + suffix = [] + suffix << "CODE_SIGN_IDENTITY='#{Gym.config[:codesigning_identity]}'" if Gym.config[:codesigning_identity] + suffix + end + + def pipe + pipe = [] + pipe << "| tee #{xcodebuild_log_path.shellescape} | xcpretty" + pipe << "--no-color" if Helper.colors_disabled? + pipe << "> /dev/null" if Gym.config[:suppress_xcode_output] + + pipe + end + + def xcodebuild_log_path + file_name = "#{Gym.project.app_name}-#{Gym.config[:scheme]}.log" + containing = File.expand_path(Gym.config[:buildlog_path]) + FileUtils.mkdir_p(containing) + + return File.join(containing, file_name) + end + + # The path where archive will be created + def build_path + unless Gym.cache[: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}/") + end + FileUtils.mkdir_p Gym.cache[:build_path] + end + Gym.cache[:build_path] + end + + def 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 + 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 + + 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/generators/package_command_generator.rb b/lib/gym/generators/package_command_generator.rb new file mode 100644 index 0000000..20a636d --- /dev/null +++ b/lib/gym/generators/package_command_generator.rb @@ -0,0 +1,58 @@ +# 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 +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 + + # The path in which the ipa file will be available after executing the command + def ipa_path + generator.ipa_path + end + + 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] + PackageCommandGeneratorLegacy + else + PackageCommandGeneratorXcode7 + end + end + end + end +end diff --git a/lib/gym/package_command_generator.rb b/lib/gym/generators/package_command_generator_legacy.rb similarity index 78% rename from lib/gym/package_command_generator.rb rename to lib/gym/generators/package_command_generator_legacy.rb index ddcb9f4..624e3c1 100644 --- a/lib/gym/package_command_generator.rb +++ b/lib/gym/generators/package_command_generator_legacy.rb @@ -1,11 +1,14 @@ -require 'shellwords' +# 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 + # 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 PackageCommandGeneratorLegacy class << self def generate parts = ["/usr/bin/xcrun #{XcodebuildFixes.patch_package_application} -v"] @@ -53,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 new file mode 100644 index 0000000..5c27725 --- /dev/null +++ b/lib/gym/generators/package_command_generator_xcode7.rb @@ -0,0 +1,170 @@ +# 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 'tempfile' + +module Gym + # Responsible for building the fully working xcodebuild command + class PackageCommandGeneratorXcode7 + class << self + def generate + print_legacy_information unless Helper.fastlane_enabled? + + parts = ["/usr/bin/xcrun #{XcodebuildFixes.wrap_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 '#{temporary_output_path}'" + + options + end + + def pipe + [""] + end + + # 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] ||= "#{Tempfile.new('gym').path}.gym_output" + end + + def ipa_path + unless Gym.cache[:ipa_path] + path = Dir[File.join(temporary_output_path, "*.ipa")].last + 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 + + # The path the the dsym file for this app. Might be nil + def dsym_path + Dir[BuildCommandGenerator.archive_path + "/**/*.app.dSYM"].last + end + + # The path the config file we use to sign our app + def config_path + Gym.cache[:config_path] ||= "#{Tempfile.new('gym').path}_config.plist" + 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 = 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) 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] + + hash.to_plist + end + + 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/fastlane/fastlane/blob/master/docs/CodeSigning.md" + end + end + end + end +end diff --git a/lib/gym/manager.rb b/lib/gym/manager.rb index 9f4d499..d0ed071 100644 --- a/lib/gym/manager.rb +++ b/lib/gym/manager.rb @@ -3,30 +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 - 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] - 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", "")] - - puts "" - puts Terminal::Table.new( - title: "Building Application 🚀".green, - rows: rows - ) - puts "" - end end end diff --git a/lib/gym/options.rb b/lib/gym/options.rb index 1d9b406..ceba663 100644 --- a/lib/gym/options.rb +++ b/lib/gym/options.rb @@ -18,9 +18,13 @@ 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, + 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", @@ -29,9 +33,13 @@ 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, + 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", @@ -49,11 +57,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,11 +65,6 @@ def self.plain_options verify_block: proc do |value| value.gsub!(".ipa", "") end), - 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", @@ -83,11 +81,97 @@ 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: :include_symbols, + short_option: "-m", + env_name: "GYM_INCLUDE_SYMBOLS", + description: "Should the ipa file include symbols?", + 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?", + 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", + default_value: false, + is_string: false, + verify_block: proc do |value| + if value + 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, + short_option: "-j", + env_name: "GYM_EXPORT_METHOD", + description: "How should gym export the archive?", + is_string: true, + optional: true, + verify_block: proc do |value| + 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: :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 path to the created archive", + 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", + 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", + description: "The path to the provisioning profile (optional)", + optional: true, + verify_block: proc do |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", 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", @@ -99,17 +183,15 @@ 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: :provisioning_profile_path, - short_option: "-e", - env_name: "GYM_PROVISIONING_PROFILE_PATH", - description: "The path to the provisioning profile (optional)", + 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, - verify_block: proc do |value| - raise "Provisioning profile not found at path '#{File.expand_path(value)}'".red unless File.exist?(value) - end) - + is_string: false + ) ] end end 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/lib/gym/runner.rb b/lib/gym/runner.rb index 4a4fc9e..3f90519 100644 --- a/lib/gym/runner.rb +++ b/lib/gym/runner.rb @@ -1,28 +1,33 @@ require 'pty' require 'open3' require 'fileutils' +require 'shellwords' module Gym class Runner # @return (String) The path to the resulting ipa def run clear_old_files + build_app verify_archive FileUtils.mkdir_p(Gym.config[:output_directory]) - if Gym.project.ios? + if Gym.project.ios? || Gym.project.tvos? package_app - Gym::XcodebuildFixes.swift_library_fix - Gym::XcodebuildFixes.watchkit_fix - Gym::XcodebuildFixes.clear_patched_package_application - + 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 - move_mac_app + copy_mac_app end end @@ -60,11 +65,24 @@ def print_command(command, title) ##################################################### def clear_old_files - if File.exist? PackageCommandGenerator.ipa_path + 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 Gym.config[:use_legacy_build_api] + Gym::XcodebuildFixes.swift_library_fix + Gym::XcodebuildFixes.watchkit_fix + 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 @@ -76,13 +94,15 @@ 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 + 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) end # 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 + "/*"]).count == 0 ErrorHandler.handle_empty_archive end end @@ -104,42 +124,99 @@ 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") + 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}' '#{file_name}'" - Helper.log.info command.yellow unless Gym.config[:silent] - command_result = `#{command}` - Helper.log.info command_result if $verbose + command = "cd '#{containing_directory}' && zip -r '#{output_path}' *.dSYM" + 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 # @return (String) The path to the resulting ipa file def move_ipa - FileUtils.mv(PackageCommandGenerator.ipa_path, Gym.config[:output_directory], force: true) + 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))) - 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 + 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 + # 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 - 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, 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)) - 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 + + # 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 + if Gym.config[:use_legacy_build_api] + BuildCommandGenerator.archive_path + else + Dir.glob(File.join(BuildCommandGenerator.build_path, "*.ipa")).last + end + end end end diff --git a/lib/gym/version.rb b/lib/gym/version.rb index 62f772a..37c81aa 100644 --- a/lib/gym/version.rb +++ b/lib/gym/version.rb @@ -1,4 +1,4 @@ module Gym - VERSION = "0.4.6" + VERSION = "1.6.1" DESCRIPTION = "Building your iOS apps has never been easier" end diff --git a/lib/gym/xcode.rb b/lib/gym/xcode.rb new file mode 100644 index 0000000..ab7f7d4 --- /dev/null +++ b/lib/gym/xcode.rb @@ -0,0 +1,20 @@ +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 + is_pre = v.split('.')[0].to_i < 7 + is_pre + 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..d9a8347 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 @patched_package_application_path if File.exist?(@patched_package_application_path) Dir.mktmpdir do |tmpdir| # Check current PackageApplication MD5 @@ -18,16 +18,16 @@ 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 + 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) # 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 @@ -43,13 +43,10 @@ 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 + # 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 diff --git a/lib/gym/xcodebuild_fixes/swift_fix.rb b/lib/gym/xcodebuild_fixes/swift_fix.rb index eeffba4..cc60429 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 @@ -10,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| @@ -23,7 +27,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 @@ -45,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 @@ -57,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 diff --git a/lib/gym/xcodebuild_fixes/watchkit2_fix.rb b/lib/gym/xcodebuild_fixes/watchkit2_fix.rb new file mode 100644 index 0000000..58e5ebc --- /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? + + UI.verbose "Adding WatchKit2 support" + + 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 + + UI.verbose "Successfully added WatchKit2 support" + 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`.match(/^\s*watchos2\.\d+\s*$/) + end + end + end + end +end diff --git a/lib/gym/xcodebuild_fixes/watchkit_fix.rb b/lib/gym/xcodebuild_fixes/watchkit_fix.rb index b9100d7..392ccc7 100644 --- a/lib/gym/xcodebuild_fixes/watchkit_fix.rb +++ b/lib/gym/xcodebuild_fixes/watchkit_fix.rb @@ -3,9 +3,9 @@ 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 + UI.verbose "Adding WatchKit support" Dir.mktmpdir do |tmpdir| # Make watchkit support directory @@ -13,14 +13,14 @@ 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 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 @@ -30,6 +30,11 @@ def watchkit? `/usr/libexec/PlistBuddy -c 'Print WKWatchKitApp' '#{plist_path}' 2>&1`.strip == 'true' 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 end diff --git a/spec/build_command_generator_spec.rb b/spec/build_command_generator_spec.rb index dec9edf..5f53f4f 100644 --- a/spec/build_command_generator_spec.rb +++ b/spec/build_command_generator_spec.rb @@ -2,11 +2,13 @@ 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'" end it "supports additional parameters" do + 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| "#{k.to_s.shellescape}=#{v.shellescape}" @@ -20,13 +22,12 @@ "xcodebuild", "-scheme 'Example'", "-project './examples/standard/Example.xcodeproj'", - "-configuration 'Release'", "-sdk '9.0'", "-destination 'generic/platform=iOS'", "-archivePath '#{Gym::BuildCommandGenerator.archive_path}'", "DEBUG=1 BUNDLE_NAME=Example\\ App", :archive, - "| xcpretty" + "| tee #{log_path.shellescape} | xcpretty" ]) end @@ -37,17 +38,18 @@ end it "uses the correct build command with the example project with no additional parameters" 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'", - "-configuration 'Release'", "-destination 'generic/platform=iOS'", "-archivePath '#{Gym::BuildCommandGenerator.archive_path}'", :archive, - "| xcpretty" + "| tee #{log_path.shellescape} | xcpretty" ]) end @@ -56,17 +58,82 @@ 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} 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 + + 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 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 diff --git a/spec/fixtures/dummy.mobileprovision b/spec/fixtures/dummy.mobileprovision new file mode 100644 index 0000000..e69de29 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/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 diff --git a/spec/package_command_generator_legacy_spec.rb b/spec/package_command_generator_legacy_spec.rb new file mode 100644 index 0000000..0b6eab3 --- /dev/null +++ b/spec/package_command_generator_legacy_spec.rb @@ -0,0 +1,53 @@ +describe Gym 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) + + result = Gym::PackageCommandGeneratorLegacy.generate + expect(result).to eq([ + "/usr/bin/xcrun /tmp/PackageApplication4Gym -v", + "''", + "-o '#{Gym::PackageCommandGeneratorLegacy.ipa_path}'", + "exportFormat ipa", + "" + ]) + end + + it "works with the example project with no additional parameters and an apostrophe/single quote in the product name" do + options = { project: "./examples/standard/Example.xcodeproj" } + Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options) + + allow(Gym::PackageCommandGeneratorLegacy).to receive(:appfile_path).and_return("Krause's App") + + result = Gym::PackageCommandGeneratorLegacy.generate + expect(result).to eq([ + "/usr/bin/xcrun /tmp/PackageApplication4Gym -v", + "Krause\\'s\\ App", + "-o '#{Gym::PackageCommandGeneratorLegacy.ipa_path}'", + "exportFormat ipa", + "" + ]) + 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::PackageCommandGeneratorLegacy.generate + expect(result).to eq([ + "/usr/bin/xcrun /tmp/PackageApplication4Gym -v", + "''", + "-o '#{Gym::PackageCommandGeneratorLegacy.ipa_path}'", + "exportFormat ipa", + "--embed './spec/fixtures/dummy.mobileprovision'", + "" + ]) + end + end +end diff --git a/spec/package_command_generator_spec.rb b/spec/package_command_generator_spec.rb deleted file mode 100644 index 73a24ad..0000000 --- a/spec/package_command_generator_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -describe Gym do - describe Gym::PackageCommandGenerator 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 - expect(result).to eq([ - "/usr/bin/xcrun /tmp/PackageApplication4Gym -v", - "''", - "-o '#{Gym::PackageCommandGenerator.ipa_path}'", - "exportFormat ipa", - "" - ]) - end - - it "works with the example project with no additional parameters and an apostrophe/single quote in the product name" do - 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") - - result = Gym::PackageCommandGenerator.generate - expect(result).to eq([ - "/usr/bin/xcrun /tmp/PackageApplication4Gym -v", - "Krause\\'s\\ App", - "-o '#{Gym::PackageCommandGenerator.ipa_path}'", - "exportFormat ipa", - "" - ]) - end - end -end diff --git a/spec/package_command_generator_xcode7_spec.rb b/spec/package_command_generator_xcode7_spec.rb new file mode 100644 index 0000000..8486874 --- /dev/null +++ b/spec/package_command_generator_xcode7_spec.rb @@ -0,0 +1,149 @@ +describe Gym 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 #{Gym::XcodebuildFixes.wrap_xcodebuild} -exportArchive", + "-exportOptionsPlist '#{Gym::PackageCommandGeneratorXcode7.config_path}'", + "-archivePath '#{Gym::BuildCommandGenerator.archive_path}'", + "-exportPath '#{Gym::PackageCommandGeneratorXcode7.temporary_output_path}'", + "" + ]) + 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 + + 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) + + 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) + + 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 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 diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 40d7abd..29bfebe 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,8 +1,12 @@ 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 require 'gym' + +WebMock.disable_net_connect!(allow: 'coveralls.io') 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