xcoder 0.1.15 → 0.1.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.
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
@@ -1,9 +1,9 @@
1
1
  module Xcode
2
2
  module ConfigurationList
3
-
4
- #
3
+
4
+ #
5
5
  # @example configuration list
6
- #
6
+ #
7
7
  # 7165D47D146B4EA100DE2F0E /* Build configuration list for PBXNativeTarget "TestProject" */ = {
8
8
  # isa = XCConfigurationList;
9
9
  # buildConfigurations = (
@@ -13,73 +13,73 @@ module Xcode
13
13
  # defaultConfigurationIsVisible = 0;
14
14
  # defaultConfigurationName = Release;
15
15
  # };
16
- def self.configration_list
16
+ def self.configuration_list
17
17
  { 'isa' => 'XCConfigurationList',
18
18
  'buildConfigurations' => [],
19
19
  'defaultConfigurationIsVisible' => '0',
20
20
  'defaultConfigurationName' => '' }
21
21
  end
22
-
22
+
23
23
  #
24
24
  # @return [Hash] a hash of symbol names to configuration names.
25
- #
25
+ #
26
26
  def self.symbol_config_name_to_config_name
27
27
  { :debug => 'Debug', :release => 'Release' }
28
28
  end
29
-
29
+
30
30
  #
31
31
  # Create a configuration for this ConfigurationList. This configuration needs
32
32
  # to have a name.
33
- #
33
+ #
34
34
  # @note unique names are currently not enforced but likely necessary for the
35
35
  # the target to be build successfully.
36
- #
36
+ #
37
37
  # @param [Types] name Description
38
38
  #
39
39
  def create_config(name)
40
-
40
+
41
41
  name = ConfigurationList.symbol_config_name_to_config_name[name] if ConfigurationList.symbol_config_name_to_config_name[name]
42
-
43
- # @todo a configuration has additional fields that are ususally set with
42
+
43
+ # @todo a configuration has additional fields that are ususally set with
44
44
  # some target information for the title.
45
-
45
+
46
46
  new_config = @registry.add_object(Configuration.default_properties(name))
47
47
  @properties['buildConfigurations'] << new_config.identifier
48
-
48
+
49
49
  yield new_config if block_given?
50
-
50
+
51
51
  new_config.save!
52
52
  end
53
-
53
+
54
54
  #
55
55
  # @return [BuildConfiguration] the build configuration that is set to default;
56
56
  # nil if no configuration has been set as default.
57
- #
57
+ #
58
58
  def default_config
59
59
  build_configurations.find {|config| config.name == default_configuration_name }
60
60
  end
61
-
61
+
62
62
  #
63
63
  # @return [String] the name of the default build configuration; nil if no
64
64
  # configuration has been set as default.
65
- #
65
+ #
66
66
  def default_config_name
67
67
  default_configuration_name
68
68
  end
69
-
69
+
70
70
  #
71
71
  # @todo allow the ability for a configuration to set itself as default and/or
72
72
  # let a configuration be specified as a parameter here. Though we need
73
73
  # to check to see that the configuration is part of the this configuration
74
74
  # list.
75
- #
76
- # @param [String] name of the build configuration to set as the default
75
+ #
76
+ # @param [String] name of the build configuration to set as the default
77
77
  # configuration; specify nil if you want to remove any default configuration.
78
78
  #
79
79
  def set_default_config(name)
80
80
  # @todo ensure that the name specified is one of the available configurations
81
81
  @properties['defaultConfigurationName'] = name
82
82
  end
83
-
83
+
84
84
  end
85
- end
85
+ end
@@ -0,0 +1,56 @@
1
+ require 'net/ftp'
2
+ require 'xcode/deploy/web_assets'
3
+
4
+ module Xcode
5
+ module Deploy
6
+ class Ftp
7
+ attr_accessor :host, :username, :password, :dir
8
+
9
+ def initialize(builder, options = {})
10
+ @builder = builder
11
+ @username = options[:username]
12
+ @password = options[:password]
13
+ @dir = options[:dir]
14
+ @host = options[:host]
15
+ @base_url = options[:base_url]
16
+ end
17
+
18
+ # Support templating of member data.
19
+ def get_binding
20
+ binding
21
+ end
22
+
23
+ def remote_installation_path
24
+ File.join(@dir, @builder.product_name)
25
+ end
26
+
27
+ def deploy
28
+ WebAssets.generate @builder, @base_url do |dir|
29
+ puts "Connecting to #{@remote_host} with username #{@username}"
30
+ Net::FTP.open(@host, @username, @password) do |ftp|
31
+ begin
32
+ puts "Creating folder #{remote_installation_path}"
33
+ ftp.mkdir(remote_installation_path)
34
+ rescue Net::FTPError
35
+ puts "It looks like the folder is already there."
36
+ end
37
+
38
+ puts "Changing to remote folder #{remote_installation_path}"
39
+ files = ftp.chdir(remote_installation_path)
40
+
41
+ Dir["#{dir}/*"].each do |f|
42
+ filename = File.basename(f)
43
+ puts "Uploading #{filename}"
44
+ ftp.putbinaryfile(f, filename, 1024)
45
+ end
46
+
47
+ filename = File.basename("#{@builder.ipa_path}")
48
+ puts "Uploading #{filename}"
49
+ ftp.putbinaryfile("#{@builder.ipa_path}", filename, 1024)
50
+ end
51
+ end
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,18 @@
1
+ module Xcode
2
+ module Deploy
3
+ class Kickfolio
4
+
5
+ def initialize(bundler, options={})
6
+ @bundler = bundler
7
+ @options = options
8
+ end
9
+
10
+ def deploy
11
+ RestClient.post "https://kickfolio.com/api/apps/#{@options[:app_id]}",
12
+ {:bundle_url => @options[:url], :auth_token => @options[:api_key]},
13
+ :content_type => 'application/json'
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,38 @@
1
+ require 'xcode/deploy/web_assets'
2
+ require 'aws-sdk'
3
+
4
+ module Xcode
5
+ module Deploy
6
+ class S3
7
+ def initialize(builder, options)
8
+ @builder = builder
9
+ @options = options
10
+ end
11
+
12
+ def bucket
13
+ return @bucket unless @bucket.nil?
14
+ s3 = AWS::S3.new @options
15
+ @bucket = s3.buckets.create(@options[:bucket]) rescue s3.buckets[@options[:bucket]]
16
+ @bucket
17
+ end
18
+
19
+ def upload(path)
20
+ obj_path = File.join(@options[:dir]||'', File.basename(path))
21
+ puts "Uploading #{path} => #{bucket.name}/#{obj_path}"
22
+ bucket.objects[obj_path].
23
+ write(File.open(path), :acl => :public_read)
24
+ end
25
+
26
+ def deploy
27
+ remote_ipa = upload @builder.ipa_path
28
+ base_url = remote_ipa.public_url(:secure => false).to_s.split("/")[0..-2].join("/")
29
+
30
+ WebAssets.generate @builder, base_url do |dir|
31
+ Dir["#{dir}/*"].each do |path|
32
+ upload path
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,43 @@
1
+ require 'net/ssh'
2
+ require 'net/scp'
3
+ require 'xcode/deploy/web_assets'
4
+
5
+ module Xcode
6
+ module Deploy
7
+ class Ssh
8
+ attr_accessor :host, :username, :password, :dir
9
+
10
+ def initialize(builder, options)
11
+ @builder = builder
12
+ @username = options[:username]
13
+ @password = options[:password]
14
+ @dir = options[:dir]
15
+ @host = options[:host]
16
+ @base_url = options[:base_url]
17
+ end
18
+
19
+ def remote_installation_path
20
+ File.join(@dir, @builder.product_name)
21
+ end
22
+
23
+ def deploy
24
+ WebAssets.generate @builder, @base_url do |dist_path|
25
+ puts "Copying files to #{@remote_host}:#{remote_installation_path}"
26
+ Net::SSH.start(@host, @username, :password => @password) do |ssh|
27
+ puts "Creating folder with mkdir #{remote_installation_path}"
28
+ ssh.exec!("mkdir #{remote_installation_path}")
29
+ end
30
+ Net::SCP.start(@host, @username, :password => @password) do |scp|
31
+ puts "Copying files from folder #{dist_path}"
32
+ Dir["#{dist_path}/*"].each do |f|
33
+ puts "Copying #{f} to remote host in folder #{remote_installation_path}"
34
+ scp.upload! "#{f}", "#{remote_installation_path}"
35
+ end
36
+ scp.upload! "#{@options[:ipa_path]}", "#{remote_installation_path}"
37
+ end
38
+ end
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,22 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+ <html xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
6
+ <title>Beta Download</title>
7
+ <style type="text/css">
8
+ body {background:#fff;margin:0;padding:0;font-family:arial,helvetica,sans-serif;text-align:center;padding:10px;color:#333;font-size:16px;}
9
+ #container {width:300px;margin:0 auto;}
10
+ h1 {margin:0;padding:0;font-size:14px;}
11
+ p {font-size:13px;}
12
+ .link {background:#ecf5ff;border-top:1px solid #fff;border:1px solid #dfebf8;margin-top:.5em;padding:.3em;}
13
+ .link a {text-decoration:none;font-size:15px;display:block;color:#069;}
14
+ </style>
15
+ </head>
16
+ <body>
17
+ <div id="container">
18
+ <div class="link"><a href="itms-services://?action=download-manifest&url=<%= manifest_url %>">Tap Here to Install<br /><%= product_name %><br />On Your Device</a></div>
19
+ <p><strong>Link didn't work?</strong><br />
20
+ Make sure you're visiting this page on your device, not your computer.</p>
21
+ </body>
22
+ </html>
@@ -0,0 +1,31 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>items</key>
6
+ <array>
7
+ <dict>
8
+ <key>assets</key>
9
+ <array>
10
+ <dict>
11
+ <key>kind</key>
12
+ <string>software-package</string>
13
+ <key>url</key>
14
+ <string><%= deployment_url %></string>
15
+ </dict>
16
+ </array>
17
+ <key>metadata</key>
18
+ <dict>
19
+ <key>bundle-identifier</key>
20
+ <string><%= bundle_identifier %></string>
21
+ <key>bundle-version</key>
22
+ <string><%= bundle_version %></string>
23
+ <key>kind</key>
24
+ <string>software</string>
25
+ <key>title</key>
26
+ <string><%= product_name %></string>
27
+ </dict>
28
+ </dict>
29
+ </array>
30
+ </dict>
31
+ </plist>
@@ -1,60 +1,65 @@
1
1
  require 'rest-client'
2
- require 'json'
3
2
 
4
3
  module Xcode
5
4
  module Deploy
6
5
  class Testflight
7
- attr_accessor :api_token, :team_token, :notify, :proxy, :notes, :lists
8
-
9
- def initialize(api_token, team_token)
10
- @api_token = api_token
11
- @team_token = team_token
12
- @notify = true
13
- @notes = nil
14
- @lists = []
6
+ attr_accessor :api_token, :team_token, :notify, :proxy, :notes, :lists, :builder
7
+ @@defaults = {}
8
+
9
+ def self.defaults(defaults={})
10
+ @@defaults = defaults
11
+ end
12
+
13
+ def initialize(builder, options={})
14
+ @builder = builder
15
+ @api_token = options[:api_token]||@@defaults[:api_token]
16
+ @team_token = options[:team_token]||@@defaults[:team_token]
17
+ @notify = options.has_key?(:notify) ? options[:notify] : true
18
+ @notes = options[:notes]
19
+ @lists = options[:lists]||[]
15
20
  @proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
16
21
  end
17
-
18
- def upload(ipa_path, dsymzip_path=nil)
22
+
23
+ def deploy
19
24
  puts "Uploading to Testflight..."
20
-
25
+
21
26
  # RestClient.proxy = @proxy || ENV['http_proxy'] || ENV['HTTP_PROXY']
22
27
  # RestClient.log = '/tmp/restclient.log'
23
- #
28
+ #
24
29
  # response = RestClient.post('http://testflightapp.com/api/builds.json',
25
- # :file => File.new(ipa_path),
26
- # :dsym => File.new(dsymzip_path),
30
+ # :file => File.new(builder.ipa_path),
31
+ # :dsym => File.new(builder.dsym_zip_path),
27
32
  # :api_token => @api_token,
28
33
  # :team_token => @team_token,
29
34
  # :notes => @notes,
30
35
  # :notify => @notify ? 'True' : 'False',
31
36
  # :distribution_lists => @lists.join(',')
32
37
  # )
33
- #
38
+ #
34
39
  # json = JSON.parse(response)
35
40
  # puts " + Done, got: #{json.inspect}"
36
41
  # json
37
-
42
+
38
43
  cmd = Xcode::Shell::Command.new 'curl'
39
- cmd << "--proxy #{@proxy}" unless @proxy.nil? or @proxy==''
44
+ cmd << "--proxy #{@proxy}" unless @proxy.nil? or @proxy==''
40
45
  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?
46
+ cmd << "-F file=@\"#{@builder.ipa_path}\""
47
+ cmd << "-F dsym=@\"#{@builder.dsym_zip_path}\"" unless @builder.dsym_zip_path.nil?
43
48
  cmd << "-F api_token='#{@api_token}'"
44
49
  cmd << "-F team_token='#{@team_token}'"
45
50
  cmd << "-F notes=\"#{@notes}\"" unless @notes.nil?
46
51
  cmd << "-F notify=#{@notify ? 'True' : 'False'}"
47
52
  cmd << "-F distribution_lists='#{@lists.join(',')}'" unless @lists.count==0
48
-
49
- response = Xcode::Shell.execute(cmd)
50
-
51
- json = JSON.parse(response.join(''))
53
+
54
+ response = cmd.execute
55
+
56
+ json = MultiJson.load(response.join(''))
52
57
  puts " + Done, got: #{json.inspect}"
53
-
58
+
54
59
  yield(json) if block_given?
55
-
60
+
56
61
  json
57
62
  end
58
63
  end
59
64
  end
60
- end
65
+ end
@@ -0,0 +1,39 @@
1
+ require 'erb'
2
+ require 'ostruct'
3
+ require 'tmpdir'
4
+
5
+ module Xcode
6
+ module Deploy
7
+ module WebAssets
8
+ class BindingContext < OpenStruct
9
+ def get_binding
10
+ return binding()
11
+ end
12
+ end
13
+
14
+ def self.generate(builder, base_url, &block)
15
+ Dir.mktmpdir do |dist_path|
16
+
17
+ context = BindingContext.new
18
+ context.product_name = builder.product_name
19
+ context.manifest_url = "#{base_url}/manifest.plist"
20
+ context.deployment_url = "#{base_url}/#{builder.ipa_name}"
21
+ context.bundle_version = builder.bundle_version
22
+ context.bundle_identifier = builder.bundle_identifier
23
+
24
+ rhtml = ERB.new(File.read("#{File.dirname(__FILE__)}/templates/manifest.rhtml"))
25
+ File.open("#{dist_path}/manifest.plist", "w") do |io|
26
+ io.write(rhtml.result(context.get_binding))
27
+ end
28
+
29
+ rhtml = ERB.new(File.read("#{File.dirname(__FILE__)}/templates/index.rhtml"))
30
+ File.open("#{dist_path}/index.html", "w") do |io|
31
+ io.write(rhtml.result(context.get_binding))
32
+ end
33
+
34
+ yield dist_path
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end