xcoder 0.1.15 → 0.1.18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +6 -0
  2. data/.rbenv-version +1 -0
  3. data/.rvmrc +1 -1
  4. data/Gemfile +10 -2
  5. data/README.md +110 -9
  6. data/Rakefile +2 -2
  7. data/bin/xcoder +74 -0
  8. data/lib/xcode/builder.rb +1 -2
  9. data/lib/xcode/builder/base_builder.rb +231 -102
  10. data/lib/xcode/builder/build_parser.rb +146 -0
  11. data/lib/xcode/builder/project_target_config_builder.rb +2 -2
  12. data/lib/xcode/builder/scheme_builder.rb +29 -12
  13. data/lib/xcode/buildspec.rb +286 -0
  14. data/lib/xcode/configuration_list.rb +24 -24
  15. data/lib/xcode/deploy/ftp.rb +56 -0
  16. data/lib/xcode/deploy/kickfolio.rb +18 -0
  17. data/lib/xcode/deploy/s3.rb +38 -0
  18. data/lib/xcode/deploy/ssh.rb +43 -0
  19. data/lib/xcode/deploy/templates/index.rhtml +22 -0
  20. data/lib/xcode/deploy/templates/manifest.rhtml +31 -0
  21. data/lib/xcode/deploy/testflight.rb +32 -27
  22. data/lib/xcode/deploy/web_assets.rb +39 -0
  23. data/lib/xcode/info_plist.rb +16 -0
  24. data/lib/xcode/keychain.rb +33 -10
  25. data/lib/xcode/platform.rb +65 -0
  26. data/lib/xcode/project.rb +7 -3
  27. data/lib/xcode/provisioning_profile.rb +38 -2
  28. data/lib/xcode/scheme.rb +44 -17
  29. data/lib/xcode/shell/command.rb +79 -5
  30. data/lib/xcode/terminal_output.rb +116 -0
  31. data/lib/xcode/test/formatters/junit_formatter.rb +7 -2
  32. data/lib/xcode/test/formatters/stdout_formatter.rb +34 -25
  33. data/lib/xcode/test/parsers/kif_parser.rb +87 -0
  34. data/lib/xcode/test/parsers/ocunit_parser.rb +3 -3
  35. data/lib/xcode/version.rb +1 -1
  36. data/lib/xcode/workspace.rb +13 -5
  37. data/lib/xcoder.rb +33 -31
  38. data/spec/TestProject/TestProject.xcodeproj/project.pbxproj +1627 -1015
  39. data/spec/TestWorkspace.xcworkspace/contents.xcworkspacedata +7 -0
  40. data/spec/builder_spec.rb +87 -71
  41. data/spec/deploy_spec.rb +63 -0
  42. data/spec/ocunit_parser_spec.rb +1 -1
  43. data/xcoder.gemspec +3 -1
  44. metadata +95 -19
  45. data/lib/xcode/buildfile.rb +0 -101
  46. data/lib/xcode/shell.rb +0 -26
  47. data/spec/deploy_testflight_spec.rb +0 -27
data/.gitignore CHANGED
@@ -23,3 +23,9 @@ spec/TestProject/TestProject.xcodeproj/project.xcworkspace/
23
23
  spec/TestProject/build
24
24
  spec/test-reports
25
25
 
26
+ # local vendor/bundle
27
+ vendor/bundle/*
28
+
29
+ # RubyMine project
30
+ .idea/*
31
+
@@ -0,0 +1 @@
1
+ 1.9.3-p392
data/.rvmrc CHANGED
@@ -1,4 +1,4 @@
1
- rvm use 1.9.2@xcoder
1
+ rvm use 1.9.3@xcoder
2
2
  rvm_install_on_use_flag=1
3
3
  rvm_project_rvmrc=1
4
4
  rvm_gemset_create_on_use_flag=1
data/Gemfile CHANGED
@@ -4,14 +4,22 @@ gemspec
4
4
  gem 'rake'
5
5
 
6
6
  gem 'builder'
7
- gem 'json'
7
+ gem 'multi_json'
8
8
  gem 'plist'
9
9
  gem 'rest-client'
10
+
11
+ # Documentation
10
12
  gem 'yard'
13
+ gem 'redcarpet'
14
+
15
+ # Deployment
16
+ gem 'net-ssh'
17
+ gem 'net-scp'
18
+ gem 'aws-sdk'
11
19
 
12
20
  group :test do
13
21
  gem 'rspec'
14
22
  gem 'guard'
15
23
  gem 'guard-rspec'
16
24
  gem 'ruby-debug19'
17
- end
25
+ end
data/README.md CHANGED
@@ -8,7 +8,7 @@ Full documentation can be found here: http://rayh.github.com/xcoder/
8
8
 
9
9
  ## Requirements
10
10
 
11
- Xcoder assumes you are using XCode 4.3 on Lion and ruby 1.9. You may have some degree of success with lesser versions, but they are not intentionally supported.
11
+ Xcoder assumes you are using XCode 4.6 on Mountain Lion and ruby 1.9. You may have some degree of success with lesser versions, but they are not intentionally supported.
12
12
 
13
13
  ## Example Usage
14
14
 
@@ -84,6 +84,23 @@ This will produce something like: MyProject-Debug-1.0.ipa and MyProject-Debug-1.
84
84
  info.save
85
85
  end
86
86
 
87
+ ### Managing build numbers more efficiently
88
+
89
+ Based on our experience we suggest [Versionomy](https://github.com/dazuma/versionomy) to manage version numbers.
90
+ Although the marketing version is usually set by hand on release,
91
+ there are situations where it could be nice to have an incremental build number in the marketing version number as well.
92
+ The following is an example that takes a marketing version number in the x.y.z format and increments the last part of it.
93
+
94
+ config.info_plist do |info|
95
+ info.version = info.version.to_i + 1
96
+ marketing_version = Versionomy.parse(info.marketing_version)
97
+ info.marketing_version = marketing_version.bump(:tiny).to_s
98
+ info.save
99
+ end
100
+
101
+ You can read more about Versionomy at their [site](https://github.com/dazuma/versionomy)
102
+
103
+
87
104
  ### Working with workspaces
88
105
 
89
106
  Loading workspaces can be done in a similar way to projects:
@@ -118,7 +135,8 @@ Note: Shared schemes and user (current logged in user) specific schemes are both
118
135
 
119
136
  The library provides a mechanism to install/uninstall a provisioning profile. This normally happens as part of a build (if a profile is provided to the builder, see above), but you can do this manually:
120
137
 
121
- Xcode::ProvisioningProfile.new("Myprofile.mobileprovision").install # installs profile into ~/Library
138
+ # installs profile into ~/Library
139
+ Xcode::ProvisioningProfile.new("Myprofile.mobileprovision").install
122
140
 
123
141
  Or enumerate installed profiles:
124
142
 
@@ -126,18 +144,59 @@ Or enumerate installed profiles:
126
144
  p.uninstall # Removes the profile from ~/Library/
127
145
  end
128
146
 
129
- ### [Testflight](http://testflightapp.com)
147
+ ### Deployment
148
+
149
+ #### [Testflight](http://testflightapp.com)
130
150
 
131
151
  The common output of this build/package process is to upload to Testflight. This is pretty simple with Xcoder:
132
152
 
133
- builder.testflight(API_TOKEN, TEAM_TOKEN) do |tf|
134
- tf.notes = "some release notes"
135
- tf.notify = true # Whether to send a notification to users, default is true
136
- tf.lists << "AList" # The lists to distribute the build to
137
- end
153
+ # Optionally do this, saves doing it again and again
154
+ Xcode::Deploy::Testflight.defaults :api_token => 'some api token', :team_token => 'team token'
155
+
156
+ builder.deploy :testflight,
157
+ :api_token => API_TOKEN,
158
+ :team_token => TEAM_TOKEN,
159
+ :notes => "some release notes",
160
+ :notify => true, # Whether to send a notification to users, default is true
161
+ :lists => ["AList"] # The lists to distribute the build to
162
+
163
+ You can also optionally set the HTTP_PROXY environment variable.
164
+
165
+ #### Deploying to Amazon S3
166
+
167
+ You can upload the output ipa to an arbitrary buckey on S3
168
+
169
+ builder.deploy :s3,
170
+ :bucket => "mybucket",
171
+ :access_key_id => "access id",
172
+ :secret_access_key => "access key",
173
+ :dir => "options/path/within/bucket"
174
+
175
+ #### Deploying to a web server (SSH)
176
+
177
+ The output of the build/package process can be deployed to a remote web server.
178
+ You can use SSH with the following syntax:
138
179
 
139
- You can also optionally set a .proxy= property or just set the HTTP_PROXY environment variable.
180
+ builder.deploy :ssh,
181
+ :host => "mywebserver.com",
182
+ :username => "myusername",
183
+ :password => "mypassword",
184
+ :dir => "/var/www/mywebserverpath",
185
+ :base_url => "http://mywebserver.com/"
140
186
 
187
+ #### Deploying to a web server (FTP)
188
+
189
+ The output of the build/package process can be deployed to a remote web server.
190
+ You can upload the files through FTP with the following syntax:
191
+
192
+ builder.deploy :ftp,
193
+ :host => "ftp.mywebserver.com",
194
+ :username => "myusername",
195
+ :password => "mypassword",
196
+ :dir => "/mywebserverpath",
197
+ :base_url => "http://mywebserver.com/"
198
+
199
+
141
200
  ### OCUnit to JUnit reports
142
201
 
143
202
  You can invoke your test target/bundle from the builder
@@ -308,6 +367,48 @@ There are some basic RSpec tests in the project which I suspect /wont/ work on m
308
367
 
309
368
  Currently these tests only assert the basic project file parsing and build code and do not perform file modification tests (e.g. for info plists) or provisioning profile/keychain importing
310
369
 
370
+ ## Automation and CI (BETA)
371
+
372
+ NOTE: This is only available in HEAD, you will need to install this gem from source to get this Buildspec support and the xcoder tool
373
+
374
+ This stuff is a work-in-progress and is subject to change.
375
+
376
+ Xcoder provides a simple mechanism to help with automating builds and CI. First, define a Buildspec file in the root of your project with contents like this:
377
+
378
+ # Assumes identity is first in keychain
379
+ group :adhoc do
380
+
381
+ # Which project/target/config, or workspace/scheme to use
382
+ use :MyProject, :target => :MyTarget, :config => :Release
383
+
384
+ # The mobile provision that should be used
385
+ profile 'Provisioning/MyProject_AdHoc.mobileprovision'
386
+
387
+ # Keychain is option, allows isolation of identities per-project without
388
+ # poluting global keychain
389
+ keychain 'Provisioning/build.keychain', 'build'
390
+
391
+ deploy :testflight,
392
+ :api_token => 'api token',
393
+ :team_token => 'team token',
394
+ :notify => true,
395
+ :lists => ['Internal'],
396
+ :notes => `git log -n 1`
397
+ end
398
+
399
+ You can then invoke the project using the xcoder command line:
400
+
401
+ # Invoke the default task (deploy)
402
+ xcoder -r
403
+
404
+ # Invoke a specific task
405
+ xcoder -r adhoc:package
406
+
407
+ # Get a list of tasks
408
+ xcoder -T
409
+
410
+ This is a bit of a work-in-progress and an attempt to allow projects to provide a minimal declaration of how thier artifacts should be built and where they should go. Integration with CI (Jenkins, for example) or just running locally from the command line should be simple.
411
+
311
412
  ## Feedback
312
413
 
313
414
  Please raise issues if you find defects or have a feature request.
data/Rakefile CHANGED
@@ -10,7 +10,7 @@ task :specs do
10
10
  end
11
11
 
12
12
  desc "Run integration tests"
13
- task :integration do
13
+ task :integration => :reset do
14
14
  system "rspec --color --format d --tag integration"
15
15
  end
16
16
 
@@ -28,7 +28,7 @@ namespace :test_project do
28
28
 
29
29
  task :reset do
30
30
  puts "Reseting the TestProject Project File"
31
- system "git co -- spec/TestProject"
31
+ system "git checkout -- spec/TestProject"
32
32
  puts "Removing any User schemes generated in the project"
33
33
  system "rm -rf spec/TestProject/TestProject.xcodeproj/xcuserdata"
34
34
  puts "Removing any installed files"
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+ require 'xcoder'
3
+ require 'optparse'
4
+
5
+ require 'xcode/buildspec'
6
+
7
+ options = {}
8
+ OptionParser.new do |opts|
9
+ opts.banner = "Usage: xcoder [options]"
10
+
11
+
12
+ opts.separator ""
13
+ opts.separator "Specific options:"
14
+
15
+ opts.on("-d", "--describe", "Dump the structure of the projects/workspaces in the working directory") do |v|
16
+ Xcode.workspaces.each do |w|
17
+ puts w.describe
18
+ end
19
+
20
+ Xcode.projects.each do |p|
21
+ puts p.describe
22
+ end
23
+ # options[:verbose] = v
24
+ end
25
+
26
+ opts.on("--install-profile [PROFILE]", "Install the given profile into ~/Library") do |profile|
27
+ Xcode::ProvisioningProfile.new(profile).install
28
+ end
29
+
30
+ opts.on("--show-sdks", "Show the available SDKs") do
31
+ Xcode::Platforms.supported.each do |p|
32
+ puts "#{p.name}: #{p.platform}, #{p.version}"
33
+ end
34
+ end
35
+
36
+ opts.separator ""
37
+ opts.separator "Buildspec options:"
38
+
39
+ opts.on("-r", "--run [task]", "Run the Buildspec with the given task, defaults to 'deploy'") do |task|
40
+ Xcode::Buildspec.parse
41
+ task = 'deploy' if task.nil?
42
+ Rake::Task[task].invoke
43
+ end
44
+
45
+ opts.on("-T", "--tasks", "List the available Buildspec tasks") do
46
+ Xcode::Buildspec.parse
47
+ puts Rake.application.tasks
48
+ end
49
+
50
+ opts.separator ""
51
+ opts.separator "Common options:"
52
+
53
+ opts.on_tail("-h", "--help", "Show this message") do
54
+ puts opts
55
+ exit
56
+ end
57
+
58
+ opts.on_tail("-l", "--loglevel LEVEL", "Show only output at LEVEL or below " + Xcode::TerminalOutput::LEVELS.join(", ")) do |level|
59
+ Xcode::TerminalOutput.log_level = level.to_sym
60
+ end
61
+
62
+ # opts.on_tail("-vv", "Show VERY verbose (debug level) output") do
63
+ # Xcode::TerminalOutput.log_level = :debug
64
+ # end
65
+
66
+ opts.on_tail("-q", "Hide all output except errors (equivilent to -l :error") do
67
+ Xcode::TerminalOutput.log_level = :error
68
+ end
69
+
70
+ opts.on_tail("--version", "Show version") do
71
+ puts "Xcoder #{Xcode::VERSION}"
72
+ exit
73
+ end
74
+ end.parse!
@@ -1,7 +1,6 @@
1
- require 'xcode/shell'
2
1
  require 'xcode/provisioning_profile'
3
2
  require 'xcode/test/parsers/ocunit_parser.rb'
4
- require 'xcode/deploy/testflight'
3
+ require 'xcode/test/parsers/kif_parser.rb'
5
4
  require 'xcode/builder/base_builder.rb'
6
5
  require 'xcode/builder/project_target_config_builder.rb'
7
6
  require 'xcode/builder/scheme_builder.rb'
@@ -1,83 +1,216 @@
1
+ require 'xcode/builder/build_parser'
2
+
1
3
  module Xcode
2
4
  module Builder
3
5
  #
4
- # This class tries to pull various bits of Xcoder together to provide a higher-level API for common
6
+ # This class tries to pull various bits of Xcoder together to provide a higher-level API for common
5
7
  # project build tasks.
6
8
  #
7
9
  class BaseBuilder
10
+ include Xcode::TerminalOutput
11
+
8
12
  attr_accessor :profile, :identity, :build_path, :keychain, :sdk, :objroot, :symroot
9
13
  attr_reader :config, :target
10
14
 
11
15
  def initialize(target, config)
12
16
  @target = target
13
17
  @config = config
14
-
18
+
15
19
  @sdk = @target.project.sdk
16
- @build_path = "#{File.dirname(@target.project.path)}/build/"
17
- @objroot = @build_path
18
- @symroot = @build_path
19
20
  end
20
-
21
- def common_environment
22
- env = {}
23
- env["OBJROOT"] = "\"#{@objroot}\""
24
- env["SYMROOT"] = "\"#{@symroot}\""
25
- env
21
+
22
+ def profile= (value)
23
+ if value.is_a?(ProvisioningProfile)
24
+ @profile = value
25
+ else
26
+ @profile = ProvisioningProfile.new(value)
27
+ end
28
+ end
29
+
30
+ def cocoapods_installed?
31
+ system("which pod > /dev/null 2>&1")
32
+ end
33
+
34
+ def has_dependencies?
35
+ podfile = File.join(File.dirname(@target.project.path), "Podfile")
36
+ File.exists? podfile
37
+ end
38
+
39
+ #
40
+ # If a Podfile exists, perform a pod install
41
+ #
42
+ def dependencies
43
+ if has_dependencies? and cocoapods_installed?
44
+ print_task :builder, "Fetch depencies", :notice
45
+ podfile = File.join(File.dirname(@target.project.path), "Podfile")
46
+
47
+ print_task :cocoapods, "pod setup", :info
48
+ with_command('pod setup').execute
49
+
50
+ print_task :cocoapods, "pod install", :info
51
+ with_command('pod install').execute
52
+ end
26
53
  end
54
+
55
+ def prepare_xcodebuild sdk=@sdk #:yield: Xcode::Shell::Command
56
+ with_command 'xcodebuild' do |cmd|
57
+
58
+ cmd.log_to_file = true
59
+ cmd.attach Xcode::Builder::XcodebuildParser.new
60
+
61
+ cmd.env["OBJROOT"] = "\"#{objroot}/\""
62
+ cmd.env["SYMROOT"] = "\"#{symroot}/\""
63
+
64
+ unless profile.nil?
65
+ profile.install
66
+ print_task "builder", "Using profile #{profile.install_path}", :debug
67
+ cmd.env["PROVISIONING_PROFILE"] = "#{profile.uuid}"
68
+ end
69
+
70
+ unless @keychain.nil?
71
+ print_task 'builder', "Using keychain #{@keychain.path}", :debug
72
+ cmd.env["OTHER_CODE_SIGN_FLAGS"] = "'--keychain #{@keychain.path}'"
73
+ end
74
+
75
+ unless @identity.nil?
76
+ print_task 'builder', "Using identity #{@identity}", :debug
77
+ cmd.env["CODE_SIGN_IDENTITY"] = "\"#{@identity}\""
78
+ end
79
+
80
+ cmd << "-sdk #{sdk}" unless sdk.nil?
81
+
82
+ yield cmd if block_given?
83
+ end
84
+ end
27
85
 
28
- def build_environment
29
- profile = install_profile
30
- env = common_environment
31
- env["OTHER_CODE_SIGN_FLAGS"] = "'--keychain #{@keychain.path}'" unless @keychain.nil?
32
- env["CODE_SIGN_IDENTITY"] = "\"#{@identity}\"" unless @identity.nil?
33
- env["PROVISIONING_PROFILE"] = "#{profile.uuid}" unless profile.nil?
34
- env
86
+ def with_command command_line
87
+ cmd = Xcode::Shell::Command.new command_line
88
+ cmd.output_dir = objroot
89
+ yield cmd if block_given?
90
+ cmd
91
+ end
92
+
93
+ def prepare_build_command sdk=@sdk
94
+ cmd = prepare_xcodebuild sdk
95
+ cmd
96
+ end
97
+
98
+ def prepare_test_command sdk=@sdk
99
+ cmd = prepare_xcodebuild sdk
100
+ cmd.env["OBJROOT"] = "\"#{objroot}/\""
101
+ cmd.env["SYMROOT"] = "\"#{symroot}/\""
102
+ cmd.env["TEST_AFTER_BUILD"]="YES"
103
+ cmd.env["ONLY_ACTIVE_ARCH"]="NO"
104
+ # cmd.env["TEST_HOST"]=0
105
+ # cmd << "-sdk #{sdk}" unless sdk.nil?
106
+ cmd
35
107
  end
36
108
 
109
+ def prepare_clean_command sdk=@sdk
110
+ cmd = prepare_xcodebuild sdk
111
+ cmd << "clean"
112
+ cmd
113
+ end
37
114
 
38
- def build(options = {:sdk => @sdk})
39
- cmd = xcodebuild
40
- cmd << "-sdk #{options[:sdk]}" unless options[:sdk].nil?
115
+ def prepare_package_command
116
+ #package IPA
117
+ with_command 'xcrun' do |cmd|
118
+ cmd << "-sdk #{@sdk}" unless @sdk.nil?
119
+ cmd << "PackageApplication"
120
+ cmd << "-v \"#{app_path}\""
121
+ cmd << "-o \"#{ipa_path}\""
122
+
123
+ unless @profile.nil?
124
+ cmd << "--embed \"#{@profile}\""
125
+ end
126
+ end
127
+ end
128
+
129
+ def prepare_dsym_command
130
+ # package dSYM
131
+ with_command 'zip' do |cmd|
132
+ cmd << "-r"
133
+ cmd << "-T"
134
+ cmd << "-y \"#{dsym_zip_path}\""
135
+ cmd << "\"#{dsym_path}\""
136
+ end
137
+ end
138
+
139
+ #
140
+ # Build the project
141
+ #
142
+ def build options = {}, &block
143
+ print_task :builder, "Building #{product_name}", :notice
144
+ cmd = prepare_build_command options[:sdk]||@sdk
41
145
 
42
146
  with_keychain do
43
147
  cmd.execute
44
148
  end
149
+
45
150
  self
46
151
  end
47
152
 
48
- #
153
+ #
49
154
  # Invoke the configuration's test target and parse the resulting output
50
155
  #
51
156
  # If a block is provided, the report is yielded for configuration before the test is run
52
157
  #
53
- def test(options = {:sdk => 'iphonesimulator'}) #, :parser => :OCUnit })
54
- cmd = xcodebuild
55
- cmd << "-sdk #{options[:sdk]}" unless options[:sdk].nil?
56
- cmd.env["TEST_AFTER_BUILD"]="YES"
57
-
158
+ # TODO: Move implementation to the Xcode::Test module
159
+ def test options = {:sdk => 'iphonesimulator', :show_output => false}
58
160
  report = Xcode::Test::Report.new
161
+ print_task :builder, "Testing #{product_name}", :notice
162
+
163
+ cmd = prepare_test_command options[:sdk]||@sdk
164
+
59
165
  if block_given?
60
166
  yield(report)
61
167
  else
62
- report.add_formatter :stdout
168
+ report.add_formatter :stdout, { :color_output => true }
63
169
  report.add_formatter :junit, 'test-reports'
64
170
  end
65
171
 
66
- parser = Xcode::Test::Parsers::OCUnitParser.new report
172
+ cmd.attach Xcode::Test::Parsers::OCUnitParser.new(report)
173
+ cmd.show_output = options[:show_output] # override it if user wants output
67
174
 
68
175
  begin
69
- cmd.execute(false) do |line|
70
- parser << line
71
- end
176
+ cmd.execute
72
177
  rescue Xcode::Shell::ExecutionError => e
73
- puts "Test platform exited: #{e.message}"
74
- ensure
75
- parser.flush
178
+ # FIXME: Perhaps we should always raise this?
179
+ raise e if report.suites.count==0
76
180
  end
77
181
 
78
182
  report
79
183
  end
80
184
 
185
+ #
186
+ # Deploy the package through the chosen method
187
+ #
188
+ # @param method the deployment method (web, ssh, testflight)
189
+ # @param options options specific for the chosen deployment method
190
+ #
191
+ # If a block is given, the deployer is yielded before deploy() is called
192
+ #
193
+ # TODO: move deployment to Xcode::Deploy module
194
+ def deploy method, options = {}
195
+ print_task :builder, "Deploy to #{method}", :notice
196
+ options = {
197
+ :ipa_path => ipa_path,
198
+ :dsym_zip_path => dsym_zip_path,
199
+ :ipa_name => ipa_name,
200
+ :app_path => app_path,
201
+ :configuration_build_path => configuration_build_path,
202
+ :product_name => @config.product_name,
203
+ :info_plist => @config.info_plist
204
+ }.merge options
205
+
206
+ require "xcode/deploy/#{method.to_s}.rb"
207
+ deployer = Xcode::Deploy.const_get("#{method.to_s.capitalize}").new(self, options)
208
+
209
+ yield(deployer) if block_given?
210
+
211
+ deployer.deploy
212
+ end
213
+
81
214
  #
82
215
  # Upload to testflight
83
216
  #
@@ -86,70 +219,49 @@ module Xcode
86
219
  # @param api_token the API token for your testflight account
87
220
  # @param team_token the token for the team you want to deploy to
88
221
  #
89
- def testflight(api_token, team_token)
90
- raise "Can't find #{ipa_path}, do you need to call builder.package?" unless File.exists? ipa_path
91
- raise "Can't find #{dsym_zip_path}, do you need to call builder.package?" unless File.exists? dsym_zip_path
222
+ # DEPRECATED, use deploy() instead
223
+ # def testflight(api_token, team_token)
224
+ # raise "Can't find #{ipa_path}, do you need to call builder.package?" unless File.exists? ipa_path
225
+ # raise "Can't find #{dsym_zip_path}, do you need to call builder.package?" unless File.exists? dsym_zip_path
92
226
 
93
- testflight = Xcode::Deploy::Testflight.new(api_token, team_token)
94
- yield(testflight) if block_given?
95
- testflight.upload(ipa_path, dsym_zip_path)
96
- end
227
+ # testflight = Xcode::Deploy::Testflight.new(api_token, team_token)
228
+ # yield(testflight) if block_given?
229
+ # testflight.upload(ipa_path, dsym_zip_path)
230
+ # end
97
231
 
98
- def clean
99
- cmd = xcodebuild
100
- cmd << "-sdk #{@sdk}" unless @sdk.nil?
101
- cmd << "clean"
102
- cmd.execute
232
+ def clean options = {:sdk=>@sdk}, &block
233
+ print_task :builder, "Cleaning #{product_name}", :notice
234
+ prepare_clean_command(options[:sdk]).execute
103
235
 
104
236
  @built = false
105
237
  @packaged = false
106
- self
107
- end
108
-
109
- def sign
110
- cmd = Xcode::Shell::Command.new 'codesign'
111
- cmd << "--force"
112
- cmd << "--sign \"#{@identity}\""
113
- cmd << "--resource-rules=\"#{app_path}/ResourceRules.plist\""
114
- cmd << "--entitlements \"#{entitlements_path}\""
115
- cmd << "\"#{ipa_path}\""
116
- cmd.execute
117
-
118
-
119
238
  self
120
239
  end
121
240
 
122
- def package
123
- raise "Can't find #{app_path}, do you need to call builder.build?" unless File.exists? app_path
241
+ def package options = {}, &block
242
+ options = {:show_output => false}.merge(options)
124
243
 
125
- #package IPA
126
- cmd = Xcode::Shell::Command.new 'xcrun'
127
- cmd << "-sdk #{@sdk}" unless @sdk.nil?
128
- cmd << "PackageApplication"
129
- cmd << "-v \"#{app_path}\""
130
- cmd << "-o \"#{ipa_path}\""
131
-
132
- unless @profile.nil?
133
- cmd << "--embed \"#{@profile}\""
134
- end
244
+ raise "Can't find #{app_path}, do you need to call builder.build?" unless File.exists? app_path or File.symlink? app_path
245
+
246
+ print_task 'builder', "Packaging #{product_name}", :notice
135
247
 
248
+ print_task :package, "generating IPA: #{ipa_path}", :info
136
249
  with_keychain do
137
- cmd.execute
250
+ prepare_package_command.execute
138
251
  end
139
252
 
140
- # package dSYM
141
- cmd = Xcode::Shell::Command.new 'zip'
142
- cmd << "-r"
143
- cmd << "-T"
144
- cmd << "-y \"#{dsym_zip_path}\""
145
- cmd << "\"#{dsym_path}\""
146
- cmd.execute
253
+ print_task :package, "creating dSYM zip: #{dsym_zip_path}", :info
254
+ prepare_dsym_command.execute
147
255
 
148
256
  self
149
257
  end
150
258
 
259
+ def project_root
260
+ @target.project.path
261
+ end
262
+
151
263
  def configuration_build_path
152
- "#{build_path}/#{@config.name}-#{@sdk}"
264
+ "#{symroot}/#{@config.name}-#{@sdk}"
153
265
  end
154
266
 
155
267
  def entitlements_path
@@ -166,6 +278,10 @@ module Xcode
166
278
  "#{configuration_build_path}/#{@config.product_name}-#{@config.name}-#{version}"
167
279
  end
168
280
 
281
+ def product_name
282
+ @config.product_name
283
+ end
284
+
169
285
  def ipa_path
170
286
  "#{product_version_basename}.ipa"
171
287
  end
@@ -178,35 +294,48 @@ module Xcode
178
294
  "#{product_version_basename}.dSYM.zip"
179
295
  end
180
296
 
181
- private
297
+ def ipa_name
298
+ File.basename(ipa_path)
299
+ end
182
300
 
183
- def with_keychain(&block)
184
- if @keychain.nil?
185
- yield
186
- else
187
- Xcode::Keychains.with_keychain_in_search_path @keychain, &block
188
- end
301
+ def bundle_identifier
302
+ @config.info_plist.identifier
189
303
  end
190
304
 
191
- def install_profile
192
- return nil if @profile.nil?
193
- # TODO: remove other profiles for the same app?
194
- p = ProvisioningProfile.new(@profile)
305
+ def bundle_version
306
+ @config.info_plist.version
307
+ end
195
308
 
196
- ProvisioningProfile.installed_profiles.each do |installed|
197
- if installed.identifiers==p.identifiers and installed.uuid==p.uuid
198
- installed.uninstall
199
- end
200
- end
309
+ def objroot
310
+ @objroot ||= build_path
311
+ end
201
312
 
202
- p.install
203
- p
313
+ def symroot
314
+ @symroot ||= File.join(build_path, 'Products')
204
315
  end
205
316
 
206
- def xcodebuild #:yield: Xcode::Shell::Command
207
- Xcode::Shell::Command.new 'xcodebuild', build_environment
317
+ def build_path=(path)
318
+ @build_path ||= path
319
+ FileUtils.mkdir_p @build_path
320
+ end
321
+
322
+ def build_path
323
+ return @build_path unless @build_path.nil?
324
+ @build_path = File.join File.dirname(@target.project.path), "Build"
325
+ FileUtils.mkdir_p @build_path
326
+ @build_path
327
+ end
328
+
329
+ private
330
+
331
+ def with_keychain(&block)
332
+ if @keychain.nil?
333
+ yield
334
+ else
335
+ Xcode::Keychains.with_keychain_in_search_path @keychain, &block
336
+ end
208
337
  end
209
338
 
210
339
  end
211
340
  end
212
- end
341
+ end