xcoder 0.1.1 → 0.1.2

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.
@@ -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