xcoder 0.0.17 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +2 -1
- data/bin/xcoder-build +0 -0
- data/lib/xcode/builder.rb +47 -29
- data/lib/xcode/configuration.rb +1 -1
- data/lib/xcode/shell.rb +1 -0
- data/lib/xcode/test/report_parser.rb +172 -0
- data/lib/xcode/testflight.rb +37 -0
- data/lib/xcode/version.rb +1 -1
- data/spec/test_report_spec.rb +85 -0
- data/xcoder.gemspec +2 -0
- metadata +34 -8
data/Gemfile
CHANGED
data/bin/xcoder-build
CHANGED
File without changes
|
data/lib/xcode/builder.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'xcode/shell'
|
2
2
|
require 'xcode/provisioning_profile'
|
3
|
+
require 'xcode/test/report_parser.rb'
|
4
|
+
require 'xcode/testflight'
|
3
5
|
|
4
6
|
module Xcode
|
5
7
|
class Builder
|
6
|
-
attr_accessor :profile, :identity, :build_path, :keychain
|
8
|
+
attr_accessor :profile, :identity, :build_path, :keychain, :sdk
|
7
9
|
|
8
10
|
def initialize(config)
|
9
11
|
if config.is_a? Xcode::Scheme
|
@@ -11,6 +13,7 @@ module Xcode
|
|
11
13
|
config = config.launch
|
12
14
|
end
|
13
15
|
@target = config.target
|
16
|
+
@sdk = @target.project.sdk
|
14
17
|
@config = config
|
15
18
|
@build_path = "#{File.dirname(@target.project.path)}/build/"
|
16
19
|
end
|
@@ -30,40 +33,33 @@ module Xcode
|
|
30
33
|
p
|
31
34
|
end
|
32
35
|
|
33
|
-
def build
|
34
|
-
|
35
|
-
|
36
|
-
cmd = []
|
37
|
-
cmd << "xcodebuild"
|
38
|
-
cmd << "-sdk #{@target.project.sdk}" unless @target.project.sdk.nil?
|
39
|
-
cmd << "-project \"#{@target.project.path}\""
|
40
|
-
|
41
|
-
cmd << "-scheme #{@scheme.name}" unless @scheme.nil?
|
42
|
-
cmd << "-target \"#{@target.name}\"" if @scheme.nil?
|
43
|
-
cmd << "-configuration \"#{@config.name}\"" if @scheme.nil?
|
44
|
-
|
45
|
-
cmd << "OTHER_CODE_SIGN_FLAGS=\"--keychain #{@keychain.path}\"" unless @keychain.nil?
|
46
|
-
cmd << "CODE_SIGN_IDENTITY=\"#{@identity}\"" unless @identity.nil?
|
47
|
-
cmd << "OBJROOT=\"#{@build_path}\""
|
48
|
-
cmd << "SYMROOT=\"#{@build_path}\""
|
49
|
-
cmd << "PROVISIONING_PROFILE=#{profile.uuid}" unless profile.nil?
|
50
|
-
yield(cmd) if block_given?
|
51
|
-
|
36
|
+
def build(sdk=@sdk)
|
37
|
+
cmd = build_command(@sdk)
|
52
38
|
Xcode::Shell.execute(cmd)
|
53
39
|
end
|
54
40
|
|
55
41
|
def test
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
42
|
+
cmd = build_command('iphonesimulator')
|
43
|
+
cmd << "TEST_AFTER_BUILD=YES"
|
44
|
+
cmd << "TEST_HOST=''"
|
45
|
+
|
46
|
+
parser = Xcode::Test::ReportParser.new
|
47
|
+
Xcode::Shell.execute(cmd, false) do |line|
|
48
|
+
puts line
|
49
|
+
parser << line
|
64
50
|
end
|
65
51
|
|
66
|
-
|
52
|
+
yield(parser) if block_given?
|
53
|
+
|
54
|
+
exit parser.exit_code if parser.exit_code!=0
|
55
|
+
|
56
|
+
parser
|
57
|
+
end
|
58
|
+
|
59
|
+
def upload(api_token, team_token)
|
60
|
+
testflight = Xcode::Testflight.new(api_token, team_token)
|
61
|
+
yield(testflight) if block_given?
|
62
|
+
testflight.upload(ipa_path, dsym_zip_path)
|
67
63
|
end
|
68
64
|
|
69
65
|
def clean
|
@@ -158,5 +154,27 @@ module Xcode
|
|
158
154
|
"#{product_version_basename}.dSYM.zip"
|
159
155
|
end
|
160
156
|
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
def build_command(sdk=@sdk)
|
161
|
+
profile = install_profile
|
162
|
+
cmd = []
|
163
|
+
cmd << "xcodebuild"
|
164
|
+
cmd << "-sdk #{sdk}" unless sdk.nil?
|
165
|
+
cmd << "-project \"#{@target.project.path}\""
|
166
|
+
|
167
|
+
cmd << "-scheme #{@scheme.name}" unless @scheme.nil?
|
168
|
+
cmd << "-target \"#{@target.name}\"" if @scheme.nil?
|
169
|
+
cmd << "-configuration \"#{@config.name}\"" if @scheme.nil?
|
170
|
+
|
171
|
+
cmd << "OTHER_CODE_SIGN_FLAGS=\"--keychain #{@keychain.path}\"" unless @keychain.nil?
|
172
|
+
cmd << "CODE_SIGN_IDENTITY=\"#{@identity}\"" unless @identity.nil?
|
173
|
+
cmd << "OBJROOT=\"#{@build_path}\""
|
174
|
+
cmd << "SYMROOT=\"#{@build_path}\""
|
175
|
+
cmd << "PROVISIONING_PROFILE=#{profile.uuid}" unless profile.nil?
|
176
|
+
cmd
|
177
|
+
end
|
178
|
+
|
161
179
|
end
|
162
180
|
end
|
data/lib/xcode/configuration.rb
CHANGED
data/lib/xcode/shell.rb
CHANGED
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'FileUtils'
|
3
|
+
require 'socket'
|
4
|
+
require 'builder'
|
5
|
+
|
6
|
+
module Xcode
|
7
|
+
module Test
|
8
|
+
module Formatters
|
9
|
+
class JunitFormatter
|
10
|
+
def initialize(dir)
|
11
|
+
@dir = dir
|
12
|
+
end
|
13
|
+
|
14
|
+
def string_to_xml(s)
|
15
|
+
s.gsub(/&/, '&').gsub(/'/, '"').gsub(/</, '<')
|
16
|
+
end
|
17
|
+
|
18
|
+
def write(report)
|
19
|
+
if report.end_time.nil?
|
20
|
+
raise "Report #{report} #{report.name} has a nil end time!?"
|
21
|
+
end
|
22
|
+
xml = ::Builder::XmlMarkup.new( :indent => 2 )
|
23
|
+
xml.instruct! :xml, :encoding => "UTF-8"
|
24
|
+
xml.testsuite(:errors => report.total_error_tests,
|
25
|
+
:failures => report.total_failed_tests,
|
26
|
+
:hostname => Socket.gethostname,
|
27
|
+
:name => report.name,
|
28
|
+
:tests => report.tests.count,
|
29
|
+
:time => (report.end_time - report.start_time),
|
30
|
+
:timestamp => report.end_time
|
31
|
+
) do |p|
|
32
|
+
|
33
|
+
report.tests.each do |t|
|
34
|
+
p.testcase(:classname => report.name,
|
35
|
+
:name => t.name,
|
36
|
+
:time => t.time
|
37
|
+
) do |e|
|
38
|
+
|
39
|
+
if t.error?
|
40
|
+
e.failure t.error_location, :message => t.error_message, :type => 'Failure'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
File.open("#{@dir}/TEST-#{report.name}.xml", 'w') do |current_file|
|
47
|
+
current_file.write xml.target!
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class SuiteReport
|
55
|
+
attr_accessor :tests, :name, :start_time, :end_time
|
56
|
+
|
57
|
+
def initialize(name, start_time)
|
58
|
+
@name = name
|
59
|
+
@start_time = start_time
|
60
|
+
@tests = []
|
61
|
+
end
|
62
|
+
|
63
|
+
def finish(time)
|
64
|
+
raise "Time is nil" if time.nil?
|
65
|
+
@end_time = time
|
66
|
+
end
|
67
|
+
|
68
|
+
def total_error_tests
|
69
|
+
@tests.select {|t| t.error? }.count
|
70
|
+
end
|
71
|
+
|
72
|
+
def total_passed_tests
|
73
|
+
@tests.select {|t| t.passed? }.count
|
74
|
+
end
|
75
|
+
|
76
|
+
def total_failed_tests
|
77
|
+
@tests.select {|t| t.failed? }.count
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
class CaseReport
|
83
|
+
attr_reader :name, :time, :error_message, :error_location
|
84
|
+
|
85
|
+
def initialize(name)
|
86
|
+
@name = name
|
87
|
+
end
|
88
|
+
|
89
|
+
def passed?
|
90
|
+
@passed
|
91
|
+
end
|
92
|
+
|
93
|
+
def failed?
|
94
|
+
error? or !@passed
|
95
|
+
end
|
96
|
+
|
97
|
+
def error?
|
98
|
+
!@error_message.nil?
|
99
|
+
end
|
100
|
+
|
101
|
+
def passed(time)
|
102
|
+
@passed = true
|
103
|
+
@time = time
|
104
|
+
end
|
105
|
+
|
106
|
+
def failed(time)
|
107
|
+
@passed = false
|
108
|
+
@time = time
|
109
|
+
end
|
110
|
+
|
111
|
+
def error(error_message,error_location)
|
112
|
+
@error_message = error_message
|
113
|
+
@error_location = error_location
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
class ReportParser
|
118
|
+
|
119
|
+
attr_reader :exit_code, :reports
|
120
|
+
|
121
|
+
def initialize
|
122
|
+
@exit_code = 0
|
123
|
+
@reports = []
|
124
|
+
end
|
125
|
+
|
126
|
+
def write(dir, format=:junit)
|
127
|
+
dir = File.expand_path(dir)
|
128
|
+
FileUtils.mkdir_p(dir)
|
129
|
+
|
130
|
+
formatter = Formatters.const_get("#{format.capitalize}Formatter").new(dir)
|
131
|
+
@reports.each do |r|
|
132
|
+
formatter.write(r)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def <<(piped_row)
|
137
|
+
case piped_row
|
138
|
+
|
139
|
+
when /Test Suite '(\S+)'.*started at\s+(.*)/
|
140
|
+
name = $1
|
141
|
+
time = Time.parse($2)
|
142
|
+
@reports << SuiteReport.new(name, time) unless name=~/\// # ignore if its a file path
|
143
|
+
|
144
|
+
when /Test Suite '(\S+)'.*finished at\s+(.*)./
|
145
|
+
@reports.last.finish(Time.parse($2))
|
146
|
+
|
147
|
+
when /Test Case '-\[\S+\s+(\S+)\]' started./
|
148
|
+
test = CaseReport.new($1)
|
149
|
+
@reports.last.tests << test
|
150
|
+
|
151
|
+
when /Test Case '-\[\S+\s+(\S+)\]' passed \((.*) seconds\)/
|
152
|
+
@reports.last.tests.last.passed($2.to_f)
|
153
|
+
|
154
|
+
when /(.*): error: -\[(\S+) (\S+)\] : (.*)/
|
155
|
+
@reports.last.tests.last.error(error_message,error_location)
|
156
|
+
@exit_code = 1 # should terminate
|
157
|
+
|
158
|
+
when /Test Case '-\[\S+ (\S+)\]' failed \((\S+) seconds\)/
|
159
|
+
@reports.last.tests.last.failed($2.to_f)
|
160
|
+
@exit_code = 1 # should terminate
|
161
|
+
|
162
|
+
when /failed with exit code (\d+)/
|
163
|
+
@exit_code = $1.to_i
|
164
|
+
|
165
|
+
when
|
166
|
+
/BUILD FAILED/
|
167
|
+
@exit_code = -1;
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rest-client'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Xcode
|
5
|
+
class Testflight
|
6
|
+
attr_accessor :api_token, :team_token, :notify, :proxy, :notes, :lists
|
7
|
+
|
8
|
+
def initialize(api_token, team_token)
|
9
|
+
@api_token = api_token
|
10
|
+
@team_token = team_token
|
11
|
+
@notify = true
|
12
|
+
@notes = nil
|
13
|
+
@lists = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def upload(ipa_path, dsymzip_path=nil)
|
17
|
+
# cmd = []
|
18
|
+
RestClient.proxy = @proxy || ENV['http_proxy'] || ENV['HTTP_PROXY']
|
19
|
+
RestClient.log = '/tmp/restclient.log'
|
20
|
+
|
21
|
+
puts "Uploading to Testflight..."
|
22
|
+
response = RestClient.post('http://testflightapp.com/api/builds.json',
|
23
|
+
:file => File.new(ipa_path),
|
24
|
+
:dsym => File.new(dsymzip_path),
|
25
|
+
:api_token => @api_token,
|
26
|
+
:team_token => @team_token,
|
27
|
+
:notes => @notes,
|
28
|
+
:notify => @notify ? 'True' : 'False',
|
29
|
+
:distribution_lists => @lists.join(',')
|
30
|
+
)
|
31
|
+
|
32
|
+
json = JSON.parse(response)
|
33
|
+
puts " + Done, got: #{json.inspect}"
|
34
|
+
json
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/xcode/version.rb
CHANGED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'xcoder'
|
3
|
+
|
4
|
+
describe Xcode::Test::ReportParser do
|
5
|
+
|
6
|
+
def example_report
|
7
|
+
t = Xcode::Test::ReportParser.new
|
8
|
+
t << "Run test suite AnExampleTestSuite"
|
9
|
+
t << "Test Suite 'AnExampleTestSuite' started at 2012-02-10 00:37:04 +0000"
|
10
|
+
|
11
|
+
t << "Run test case anExampleTest1"
|
12
|
+
t << "Test Case '-[AnExampleTestSuite anExampleTest1]' started."
|
13
|
+
t << "Test Case '-[AnExampleTestSuite anExampleTest1]' passed (0.003 seconds)."
|
14
|
+
|
15
|
+
t << "Run test case anExampleTest2"
|
16
|
+
t << "Test Case '-[AnExampleTestSuite anExampleTest2]' started."
|
17
|
+
t << "Test Case '-[AnExampleTestSuite anExampleTest2]' passed (0.003 seconds)."
|
18
|
+
|
19
|
+
yield(t) if block_given?
|
20
|
+
|
21
|
+
t << "Test Suite 'AnExampleTestSuite' finished at 2012-02-10 00:37:04 +0000."
|
22
|
+
t << "Executed 1 test, with 0 failures (0 unexpected) in 0.000 (0.000) seconds"
|
23
|
+
|
24
|
+
t
|
25
|
+
end
|
26
|
+
|
27
|
+
def example_failing_report
|
28
|
+
example_report do |t|
|
29
|
+
t << "Test Case '-[AnExampleTestSuite aFailingTest]' started."
|
30
|
+
t << "Test Case '-[AnExampleTestSuite aFailingTest]' failed (2 seconds)."
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should create a test case" do
|
35
|
+
t = example_report
|
36
|
+
t.reports.count.should==1
|
37
|
+
t.reports.first.name.should=="AnExampleTestSuite"
|
38
|
+
t.reports.first.start_time.should==Time.parse("2012-02-10 00:37:04 +0000")
|
39
|
+
t.reports.first.end_time.should==Time.parse("2012-02-10 00:37:04 +0000")
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should set the exist status to 0" do
|
43
|
+
t = example_report
|
44
|
+
t.exit_code.should==0
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should set the exit status to non 0" do
|
48
|
+
t = example_failing_report
|
49
|
+
t.exit_code.should_not==0
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should record a failure" do
|
53
|
+
t = example_failing_report
|
54
|
+
t.reports.first.total_failed_tests.should==1
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should create a test case with some tests" do
|
58
|
+
t = example_report
|
59
|
+
|
60
|
+
t.reports.count.should==1
|
61
|
+
t.reports.first.tests.count.should==2
|
62
|
+
t.reports.first.tests[0].name.should=='anExampleTest1'
|
63
|
+
t.reports.first.tests[0].time.should==0.003
|
64
|
+
t.reports.first.tests[0].passed?.should==true
|
65
|
+
|
66
|
+
t.reports.first.tests[1].name.should=='anExampleTest2'
|
67
|
+
t.reports.first.tests[1].time.should==0.003
|
68
|
+
t.reports.first.tests[1].passed?.should==true
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should write out reports in junit format" do
|
72
|
+
report_dir = "#{File.dirname(__FILE__)}/test-reports"
|
73
|
+
FileUtils.rm_rf report_dir
|
74
|
+
|
75
|
+
t = example_report
|
76
|
+
t.write(report_dir, :junit)
|
77
|
+
|
78
|
+
files = Dir["#{report_dir}/*.xml"]
|
79
|
+
files.count.should==1
|
80
|
+
files.first.should=~/TEST-AnExampleTestSuite.xml$/
|
81
|
+
|
82
|
+
# FIXME: parse the report
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
data/xcoder.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xcoder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.18
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-02-
|
12
|
+
date: 2012-02-10 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: json
|
16
|
-
requirement: &
|
16
|
+
requirement: &70260058216360 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70260058216360
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: plist
|
27
|
-
requirement: &
|
27
|
+
requirement: &70260058215940 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70260058215940
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: nokogiri
|
38
|
-
requirement: &
|
38
|
+
requirement: &70260058215520 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,7 +43,29 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70260058215520
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: builder
|
49
|
+
requirement: &70260058215100 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70260058215100
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: rest-client
|
60
|
+
requirement: &70260058214680 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :runtime
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70260058214680
|
47
69
|
description: Provides a ruby based object-model for parsing project structures and
|
48
70
|
invoking builds
|
49
71
|
email:
|
@@ -69,6 +91,8 @@ files:
|
|
69
91
|
- lib/xcode/scheme.rb
|
70
92
|
- lib/xcode/shell.rb
|
71
93
|
- lib/xcode/target.rb
|
94
|
+
- lib/xcode/test/report_parser.rb
|
95
|
+
- lib/xcode/testflight.rb
|
72
96
|
- lib/xcode/version.rb
|
73
97
|
- lib/xcode/workspace.rb
|
74
98
|
- lib/xcoder.rb
|
@@ -96,6 +120,7 @@ files:
|
|
96
120
|
- spec/project_spec.rb
|
97
121
|
- spec/scheme_spec.rb
|
98
122
|
- spec/target_spec.rb
|
123
|
+
- spec/test_report_spec.rb
|
99
124
|
- spec/workspace_spec.rb
|
100
125
|
- xcoder.gemspec
|
101
126
|
homepage: https://github.com/rayh/xcoder
|
@@ -147,4 +172,5 @@ test_files:
|
|
147
172
|
- spec/project_spec.rb
|
148
173
|
- spec/scheme_spec.rb
|
149
174
|
- spec/target_spec.rb
|
175
|
+
- spec/test_report_spec.rb
|
150
176
|
- spec/workspace_spec.rb
|