xcoder 0.1.15 → 0.1.18
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/.rbenv-version +1 -0
- data/.rvmrc +1 -1
- data/Gemfile +10 -2
- data/README.md +110 -9
- data/Rakefile +2 -2
- data/bin/xcoder +74 -0
- data/lib/xcode/builder.rb +1 -2
- data/lib/xcode/builder/base_builder.rb +231 -102
- data/lib/xcode/builder/build_parser.rb +146 -0
- data/lib/xcode/builder/project_target_config_builder.rb +2 -2
- data/lib/xcode/builder/scheme_builder.rb +29 -12
- data/lib/xcode/buildspec.rb +286 -0
- data/lib/xcode/configuration_list.rb +24 -24
- data/lib/xcode/deploy/ftp.rb +56 -0
- data/lib/xcode/deploy/kickfolio.rb +18 -0
- data/lib/xcode/deploy/s3.rb +38 -0
- data/lib/xcode/deploy/ssh.rb +43 -0
- data/lib/xcode/deploy/templates/index.rhtml +22 -0
- data/lib/xcode/deploy/templates/manifest.rhtml +31 -0
- data/lib/xcode/deploy/testflight.rb +32 -27
- data/lib/xcode/deploy/web_assets.rb +39 -0
- data/lib/xcode/info_plist.rb +16 -0
- data/lib/xcode/keychain.rb +33 -10
- data/lib/xcode/platform.rb +65 -0
- data/lib/xcode/project.rb +7 -3
- data/lib/xcode/provisioning_profile.rb +38 -2
- data/lib/xcode/scheme.rb +44 -17
- data/lib/xcode/shell/command.rb +79 -5
- data/lib/xcode/terminal_output.rb +116 -0
- data/lib/xcode/test/formatters/junit_formatter.rb +7 -2
- data/lib/xcode/test/formatters/stdout_formatter.rb +34 -25
- data/lib/xcode/test/parsers/kif_parser.rb +87 -0
- data/lib/xcode/test/parsers/ocunit_parser.rb +3 -3
- data/lib/xcode/version.rb +1 -1
- data/lib/xcode/workspace.rb +13 -5
- data/lib/xcoder.rb +33 -31
- data/spec/TestProject/TestProject.xcodeproj/project.pbxproj +1627 -1015
- data/spec/TestWorkspace.xcworkspace/contents.xcworkspacedata +7 -0
- data/spec/builder_spec.rb +87 -71
- data/spec/deploy_spec.rb +63 -0
- data/spec/ocunit_parser_spec.rb +1 -1
- data/xcoder.gemspec +3 -1
- metadata +95 -19
- data/lib/xcode/buildfile.rb +0 -101
- data/lib/xcode/shell.rb +0 -26
- data/spec/deploy_testflight_spec.rb +0 -27
data/.gitignore
CHANGED
data/.rbenv-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.9.3-p392
|
data/.rvmrc
CHANGED
data/Gemfile
CHANGED
@@ -4,14 +4,22 @@ gemspec
|
|
4
4
|
gem 'rake'
|
5
5
|
|
6
6
|
gem 'builder'
|
7
|
-
gem '
|
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.
|
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
|
-
|
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
|
-
###
|
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
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
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
|
-
|
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
|
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"
|
data/bin/xcoder
ADDED
@@ -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!
|
data/lib/xcode/builder.rb
CHANGED
@@ -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/
|
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
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
39
|
-
|
40
|
-
|
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
|
-
|
54
|
-
|
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
|
-
|
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
|
70
|
-
parser << line
|
71
|
-
end
|
176
|
+
cmd.execute
|
72
177
|
rescue Xcode::Shell::ExecutionError => e
|
73
|
-
|
74
|
-
|
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
|
-
|
90
|
-
|
91
|
-
|
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
|
-
|
94
|
-
|
95
|
-
|
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
|
-
|
100
|
-
|
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
|
-
|
241
|
+
def package options = {}, &block
|
242
|
+
options = {:show_output => false}.merge(options)
|
124
243
|
|
125
|
-
#
|
126
|
-
|
127
|
-
|
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
|
-
|
250
|
+
prepare_package_command.execute
|
138
251
|
end
|
139
252
|
|
140
|
-
|
141
|
-
|
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
|
-
"#{
|
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
|
-
|
297
|
+
def ipa_name
|
298
|
+
File.basename(ipa_path)
|
299
|
+
end
|
182
300
|
|
183
|
-
def
|
184
|
-
|
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
|
192
|
-
|
193
|
-
|
194
|
-
p = ProvisioningProfile.new(@profile)
|
305
|
+
def bundle_version
|
306
|
+
@config.info_plist.version
|
307
|
+
end
|
195
308
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
end
|
200
|
-
end
|
309
|
+
def objroot
|
310
|
+
@objroot ||= build_path
|
311
|
+
end
|
201
312
|
|
202
|
-
|
203
|
-
|
313
|
+
def symroot
|
314
|
+
@symroot ||= File.join(build_path, 'Products')
|
204
315
|
end
|
205
316
|
|
206
|
-
def
|
207
|
-
|
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
|