xcoder 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,50 @@
1
+ require 'builder'
2
+ require 'socket'
3
+
4
+ module Xcode
5
+ module Test
6
+ module Formatters
7
+ class JunitFormatter
8
+ def initialize(dir)
9
+ @dir = dir
10
+ end
11
+
12
+ def write(report)
13
+ if report.end_time.nil?
14
+ raise "Report #{report} #{report.name} has a nil end time!?"
15
+ end
16
+ xml = ::Builder::XmlMarkup.new( :indent => 2 )
17
+ xml.instruct! :xml, :encoding => "UTF-8"
18
+ xml.testsuite(:errors => report.total_error_tests,
19
+ :failures => report.total_failed_tests,
20
+ :hostname => Socket.gethostname,
21
+ :name => report.name,
22
+ :tests => report.tests.count,
23
+ :time => (report.end_time - report.start_time),
24
+ :timestamp => report.end_time
25
+ ) do |p|
26
+
27
+ report.tests.each do |t|
28
+ p.testcase(:classname => report.name,
29
+ :name => t.name,
30
+ :time => t.time
31
+ ) do |e|
32
+
33
+ if t.error?
34
+ e.failure t.error_location, :message => t.error_message, :type => 'Failure'
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ File.open("#{@dir}/TEST-#{report.name}.xml", 'w') do |current_file|
41
+ current_file.write xml.target!
42
+ end
43
+
44
+ end # write
45
+
46
+ end # JUnitFormatter
47
+
48
+ end # Formatters
49
+ end # Test
50
+ end # Xcode
@@ -0,0 +1,68 @@
1
+ require 'time'
2
+ require 'fileutils'
3
+ require 'xcode/test/suite_result'
4
+ require 'xcode/test/test_result'
5
+
6
+ module Xcode
7
+ module Test
8
+
9
+ module Formatters
10
+ end
11
+
12
+ class OCUnitReportParser
13
+
14
+ attr_reader :exit_code, :reports
15
+
16
+ def initialize
17
+ @exit_code = 0
18
+ @reports = []
19
+ end
20
+
21
+ def write(dir, format=:junit)
22
+ dir = File.expand_path(dir)
23
+ FileUtils.mkdir_p(dir)
24
+
25
+ require "xcode/test/formatters/#{format.to_s}_formatter"
26
+ formatter = Formatters.const_get("#{format.to_s.capitalize}Formatter").new(dir)
27
+ @reports.each do |r|
28
+ formatter.write(r)
29
+ end
30
+ end
31
+
32
+ def <<(piped_row)
33
+ case piped_row
34
+
35
+ when /Test Suite '(\S+)'.*started at\s+(.*)/
36
+ name = $1
37
+ time = Time.parse($2)
38
+ @reports << SuiteResult.new(name, time) unless name=~/\// # ignore if its a file path
39
+
40
+ when /Test Suite '(\S+)'.*finished at\s+(.*)./
41
+ @reports.last.finish(Time.parse($2))
42
+
43
+ when /Test Case '-\[\S+\s+(\S+)\]' started./
44
+ test = TestResult.new($1)
45
+ @reports.last.tests << test
46
+
47
+ when /Test Case '-\[\S+\s+(\S+)\]' passed \((.*) seconds\)/
48
+ @reports.last.tests.last.passed($2.to_f)
49
+
50
+ when /(.*): error: -\[(\S+) (\S+)\] : (.*)/
51
+ @reports.last.tests.last.error(error_message,error_location)
52
+ @exit_code = 1 # should terminate
53
+
54
+ when /Test Case '-\[\S+ (\S+)\]' failed \((\S+) seconds\)/
55
+ @reports.last.tests.last.failed($2.to_f)
56
+ @exit_code = 1 # should terminate
57
+
58
+ when /failed with exit code (\d+)/
59
+ @exit_code = $1.to_i
60
+
61
+ when
62
+ /BUILD FAILED/
63
+ @exit_code = -1;
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,31 @@
1
+ module Xcode
2
+ module Test
3
+ class SuiteResult
4
+ attr_accessor :tests, :name, :start_time, :end_time
5
+
6
+ def initialize(name, start_time)
7
+ @name = name
8
+ @start_time = start_time
9
+ @tests = []
10
+ end
11
+
12
+ def finish(time)
13
+ raise "Time is nil" if time.nil?
14
+ @end_time = time
15
+ end
16
+
17
+ def total_error_tests
18
+ @tests.select {|t| t.error? }.count
19
+ end
20
+
21
+ def total_passed_tests
22
+ @tests.select {|t| t.passed? }.count
23
+ end
24
+
25
+ def total_failed_tests
26
+ @tests.select {|t| t.failed? }.count
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,38 @@
1
+ module Xcode
2
+ module Test
3
+ class TestResult
4
+ attr_reader :name, :time, :error_message, :error_location
5
+
6
+ def initialize(name)
7
+ @name = name
8
+ end
9
+
10
+ def passed?
11
+ @passed
12
+ end
13
+
14
+ def failed?
15
+ error? or !@passed
16
+ end
17
+
18
+ def error?
19
+ !@error_message.nil?
20
+ end
21
+
22
+ def passed(time)
23
+ @passed = true
24
+ @time = time
25
+ end
26
+
27
+ def failed(time)
28
+ @passed = false
29
+ @time = time
30
+ end
31
+
32
+ def error(error_message,error_location)
33
+ @error_message = error_message
34
+ @error_location = error_location
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,56 @@
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
+ @proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
15
+ end
16
+
17
+ def upload(ipa_path, dsymzip_path=nil)
18
+ puts "Uploading to Testflight..."
19
+
20
+ # RestClient.proxy = @proxy || ENV['http_proxy'] || ENV['HTTP_PROXY']
21
+ # RestClient.log = '/tmp/restclient.log'
22
+ #
23
+ # response = RestClient.post('http://testflightapp.com/api/builds.json',
24
+ # :file => File.new(ipa_path),
25
+ # :dsym => File.new(dsymzip_path),
26
+ # :api_token => @api_token,
27
+ # :team_token => @team_token,
28
+ # :notes => @notes,
29
+ # :notify => @notify ? 'True' : 'False',
30
+ # :distribution_lists => @lists.join(',')
31
+ # )
32
+ #
33
+ # json = JSON.parse(response)
34
+ # puts " + Done, got: #{json.inspect}"
35
+ # json
36
+
37
+ cmd = []
38
+ cmd << 'curl'
39
+ cmd << "--proxy #{@proxy}" unless @proxy.nil? or @proxy==''
40
+ cmd << "-X POST http://testflightapp.com/api/builds.json"
41
+ cmd << "-F file=@\"#{ipa_path}\""
42
+ cmd << "-F dsym=@\"#{dsymzip_path}\"" unless dsymzip_path.nil?
43
+ cmd << "-F api_token='#{@api_token}'"
44
+ cmd << "-F team_token='#{@team_token}'"
45
+ cmd << "-F notes=\"#{@notes}\"" unless @notes.nil?
46
+ cmd << "-F notify=#{@notify ? 'True' : 'False'}"
47
+ cmd << "-F distribution_lists='#{@lists.join(',')}'" unless @lists.count==0
48
+
49
+ response = Xcode::Shell.execute(cmd)
50
+
51
+ json = JSON.parse(response.join(''))
52
+ puts " + Done, got: #{json.inspect}"
53
+ json
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,28 @@
1
+ require_relative 'group'
2
+
3
+ module Xcode
4
+
5
+ module VariantGroup
6
+ include Group ; extend Group
7
+
8
+ #
9
+ # # E21EB9DE14E357CF0058122A /* InfoPlist.strings */ = {
10
+ # isa = PBXVariantGroup;
11
+ # children = (
12
+ # E21EB9DF14E357CF0058122A /* en */,
13
+ # );
14
+ # name = InfoPlist.strings;
15
+ # sourceTree = "<group>";
16
+ # };
17
+ #
18
+ def self.info_plist properties
19
+ default_properties = { 'isa' => 'PBXVariantGroup',
20
+ 'children' => [],
21
+ 'name' => name,
22
+ 'sourceTree' => '<group>' }
23
+
24
+ default_properties.merge(properties)
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ module Xcode
2
+ VERSION = "0.1.2"
3
+ end
@@ -0,0 +1,40 @@
1
+ require 'xcode/project'
2
+
3
+ module Xcode
4
+ class Workspace
5
+ attr_reader :projects, :name, :path
6
+ def initialize(path)
7
+ path = "#{path}.xcworkspace" unless path=~/\.xcworkspace/
8
+ path = "#{path}/contents.xcworkspacedata" unless path=~/xcworkspacedata$/
9
+
10
+ @name = File.basename(path.gsub(/\.xcworkspace\/contents\.xcworkspacedata/,''))
11
+ @projects = []
12
+ @path = File.expand_path path
13
+
14
+ File.open(@path).read.split(/<FileRef/).each do |line|
15
+ if line=~/location\s*=\s*\"group\:(.+?)\"/
16
+ project_path = "#{workspace_root}/#{$1}"
17
+ @projects << Xcode::Project.new(project_path)
18
+ end
19
+ end
20
+ end
21
+
22
+ def project(name)
23
+ project = @projects.select {|c| c.name == name.to_s}.first
24
+ raise "No such project #{name}, available projects are #{@projects.map {|c| c.name}.join(', ')}" if project.nil?
25
+ yield project if block_given?
26
+ project
27
+ end
28
+
29
+ def describe
30
+ puts "Workspace #{name} contains:"
31
+ projects.each do |p|
32
+ p.describe
33
+ end
34
+ end
35
+
36
+ def workspace_root
37
+ File.dirname(File.dirname(@path))
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,105 @@
1
+ require 'find'
2
+ require 'fileutils'
3
+ require "xcode/version"
4
+ require "xcode/project"
5
+ require "xcode/info_plist"
6
+ require "xcode/shell"
7
+ require 'plist'
8
+ require 'xcode/keychain'
9
+ require 'xcode/workspace'
10
+ require 'xcode/buildfile'
11
+
12
+ module Xcode
13
+ @@projects = nil
14
+ @@workspaces = nil
15
+ @@sdks = nil
16
+
17
+ def self.projects
18
+ @@projects = parse_projects if @@projects.nil?
19
+ @@projects
20
+ end
21
+
22
+ def self.workspaces
23
+ @@workspaces = parse_workspaces if @@workspaces.nil?
24
+ @@workspaces
25
+ end
26
+
27
+ def self.project(name)
28
+ name = name.to_s
29
+
30
+ return Xcode::Project.new(name) if name=~/\.xcodeproj/
31
+
32
+ self.projects.each do |p|
33
+ return p if p.name == name
34
+ end
35
+ raise "Unable to find a project named #{name}. However, I did find these projects: #{self.projects.map {|p| p.name}.join(', ') }"
36
+ end
37
+
38
+ def self.workspace(name)
39
+ name = name.to_s
40
+
41
+ return Xcode::Workspace.new(name) if name=~/\.xcworkspace/
42
+
43
+ self.workspaces.each do |p|
44
+ return p if p.name == name
45
+ end
46
+ raise "Unable to find a workspace named #{name}. However, I did find these workspaces: #{self.workspaces.map {|p| p.name}.join(', ') }"
47
+ end
48
+
49
+ def self.find_projects(dir='.')
50
+ parse_projects(dir)
51
+ end
52
+
53
+ def self.is_sdk_available?(sdk)
54
+ parse_sdks if @@sdks.nil?
55
+ @@sdks.values.include? sdk
56
+ end
57
+
58
+ def self.available_sdks
59
+ parse_sdks if @@sdks.nil?
60
+ @@sdks
61
+ end
62
+
63
+ private
64
+ def self.parse_sdks
65
+ @@sdks = {}
66
+ parsing = false
67
+ `xcodebuild -showsdks`.split("\n").each do |l|
68
+ l.strip!
69
+ if l=~/(.*)\s+SDKs:/
70
+ parsing = true
71
+ elsif l=~/^\s*$/
72
+ parsing = false
73
+ elsif parsing
74
+ l=~/([^\t]+)\t+\-sdk (.*)/
75
+ @@sdks[$1.strip] = $2.strip unless $1.nil? and $2.nil?
76
+ end
77
+ end
78
+ end
79
+
80
+ def self.parse_workspaces(dir='.')
81
+ projects = []
82
+ Find.find(dir) do |path|
83
+ if path=~/\.xcworkspace$/ and !(path=~/\.xcodeproj\//)
84
+ projects << Xcode::Workspace.new(path)
85
+ end
86
+ end
87
+ projects
88
+ end
89
+
90
+ def self.parse_projects(dir='.')
91
+ projects = []
92
+ Find.find(dir) do |path|
93
+ if path=~/(.*)\.xcodeproj$/
94
+ projects << Xcode::Project.new(path)
95
+ end
96
+ end
97
+ projects
98
+ end
99
+ end
100
+
101
+ require 'xcode/core_ext/hash'
102
+ require 'xcode/core_ext/array'
103
+ require 'xcode/core_ext/string'
104
+ require 'xcode/core_ext/boolean'
105
+ require 'xcode/core_ext/fixnum'
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "xcode/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "xcoder"
7
+ s.version = Xcode::VERSION
8
+ s.authors = ["Ray Hilton", "Frank Webber"]
9
+ s.email = ["ray@wirestorm.net", "franklin.webber@gmail.com"]
10
+ s.homepage = "https://github.com/rayh/xcoder"
11
+ s.summary = %q{Ruby wrapper around xcodebuild, xcrun, agvtool and pbxproj files}
12
+ s.description = %q{Provides a ruby based object-model for parsing project structures and invoking builds}
13
+
14
+ s.rubyforge_project = "xcoder"
15
+
16
+ s.files = `git ls-files`.split("\n").reject {|f| f=~/^(?:spec|examples)\//} # Ignore example files
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_runtime_dependency "json"
22
+ s.add_runtime_dependency "plist"
23
+ s.add_runtime_dependency "nokogiri"
24
+ s.add_runtime_dependency "builder"
25
+ s.add_runtime_dependency "rest-client"
26
+ end