fruity_builder 1.0.1 → 1.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 16412bb7e55a1c0f1872c37d1a63609ca8efdcf0
4
- data.tar.gz: a63167548a0c1fecc2213a979f3b564568892b9f
3
+ metadata.gz: 8d8ae8d1b4b33450affa9d95e70c438bac84f250
4
+ data.tar.gz: 02aa4a49e613e1f7f330043e61dee0a09788ee45
5
5
  SHA512:
6
- metadata.gz: d9050248a0edf8da4341c705518246c5598a515f8664e0ecc69d47ae923ce6ec34812521de8459d8e574838515b246052568dfc4665a4790e0953dddd5b0b06b
7
- data.tar.gz: d57b536f65848d0aca184b6776435df42175a2b19a27da6aadaf80166f24e2e24bca64a9c4fde6c75e3d655d01a1497e49eb9e9f391c767e73cbbf7c412fdd3d
6
+ metadata.gz: cab3fe1d9d54fb4b81f2b694ff0bef41c24b0ea65aa5289669c173c4fe9c79185d1eba26e68b67a75076a9401dd81e92d841b1af3e84a04d07aa18a0d1dc656d
7
+ data.tar.gz: 2ca8656316a5268d2e216d3cee62a56dfe1fa8b5af58dbf14bfb7c26a197069f671274c7a17690a636db951add469f8b20232cefcc0bffca0a12355ada85d72a
@@ -1,6 +1,7 @@
1
1
  require 'fruity_builder/lib/sys_log'
2
2
  require 'fruity_builder/xcodebuild'
3
3
  require 'fruity_builder/build_properties'
4
+ require 'fruity_builder/signing'
4
5
 
5
6
 
6
7
  module FruityBuilder
@@ -1,50 +1,94 @@
1
1
  require 'fruity_builder/lib/execution'
2
+ require 'ox'
2
3
 
3
4
  module FruityBuilder
4
- module IOS
5
- class Plistutil < Execution
6
-
7
- def self.get_xml(options = {})
8
- if options.key?(:file)
9
- IO.read(options[:file])
10
- elsif options.key?(:xml)
11
- options[:xml]
12
- end
5
+ class Plistutil < Execution
6
+
7
+ def self.get_xml(options = {})
8
+ if options.key?(:file)
9
+ IO.read(options[:file])
10
+ elsif options.key?(:xml)
11
+ options[:xml]
13
12
  end
13
+ end
14
+
15
+ def self.get_bundle_id(options = {})
16
+ xml = get_xml(options)
14
17
 
15
- def self.get_bundle_id(options = {})
16
- xml = get_xml(options)
18
+ raise PlistutilCommandError.new('No XML was passed') unless xml
19
+ identifiers = xml.scan(/.*CFBundleIdentifier<\/key>\n\t<string>(.*?)<\/string>/)
20
+ identifiers << xml.scan(/.*CFBundleName<\/key>\n\t<string>(.*?)<\/string>/)
21
+ identifiers.flatten.uniq
22
+ end
23
+
24
+ def self.replace_bundle_id(options = {})
25
+ xml = get_xml(options)
17
26
 
18
- raise PlistutilCommandError.new('No XML was passed') unless xml
27
+ raise PlistutilCommandError.new('No XML was passed') unless xml
19
28
 
20
- identifiers = xml.scan(/.*CFBundleIdentifier<\/key>\n\t<string>(.*?)<\/string>/)
21
- identifiers << xml.scan(/.*CFBundleName<\/key>\n\t<string>(.*?)<\/string>/)
29
+ replacements = xml.scan(/.*CFBundleIdentifier<\/key>\n\t<string>(.*?)<\/string>/)
30
+ replacements << xml.scan(/.*CFBundleName<\/key>\n\t<string>(.*?)<\/string>/)
22
31
 
23
- identifiers.flatten.uniq
32
+ replacements.flatten.uniq.each do |replacement|
33
+ xml = xml.gsub(replacement, options[:new_id])
24
34
  end
25
35
 
26
- def self.replace_bundle_id(options = {})
27
- xml = get_xml(options)
36
+ IO.write(options[:file], xml) if options.key?(:file)
37
+ xml
38
+ end
28
39
 
29
- raise PlistutilCommandError.new('No XML was passed') unless xml
40
+ # Check to ensure that plistutil is available
41
+ # @return [Boolean] true if plistutil is available, false otherwise
42
+ def self.plistutil_available?
43
+ result = execute('which plistutil')
44
+ result.exit == 0
45
+ end
30
46
 
31
- replacements = xml.scan(/.*CFBundleIdentifier<\/key>\n\t<string>(.*?)<\/string>/)
32
- replacements << xml.scan(/.*CFBundleName<\/key>\n\t<string>(.*?)<\/string>/)
47
+ # Gets properties from the IPA and returns them in a hash
48
+ # @param [String] path path to the IPA/App
49
+ # @return [Hash] list of properties from the app
50
+ def self.get_bundle_id_from_app(path)
51
+ path = Signing.unpack_ipa(path) if Signing.is_ipa?(path)
52
+ get_bundle_id_from_plist("#{path}/Info.plist")
53
+ end
33
54
 
34
- replacements.flatten.uniq.each do |replacement|
35
- xml = xml.gsub(replacement, options[:new_id])
36
- end
55
+ # Gets properties from the IPA and returns them in a hash
56
+ # @param [String] plist path to the plist
57
+ # @return [Hash] list of properties from the app
58
+ def self.get_bundle_id_from_plist(plist)
59
+ raise PlistutilCommandError.new('plistutil not found') unless plistutil_available?
60
+ result = execute("plistutil -i #{plist}")
61
+ raise PlistutilCommandError.new(result.stderr) if result.exit != 0
62
+ parse_xml(result.stdout)
63
+ end
37
64
 
38
- IO.write(options[:file], xml) if options.key?(:file)
39
- xml
65
+ def self.parse_xml(xml)
66
+ info = Ox.parse(xml)
67
+ nodes = info.locate('*/dict')
68
+ values = {}
69
+ last_key = nil
70
+ nodes.each do |node|
71
+ node.nodes.each do |child|
72
+ if child.value == 'key'
73
+ if child.nodes.first == 'get-task-allow'
74
+ values['get-task-allow'] = nodes.first.nodes[nodes.first.nodes.index(child)+1].value
75
+ next
76
+ end
77
+ last_key = child.nodes.first
78
+ elsif child.value == 'string'
79
+ values[last_key] = child.nodes.first
80
+ end
81
+ end
40
82
  end
83
+ values
41
84
  end
85
+ end
42
86
 
43
- # plistutil error class
44
- class PlistutilCommandError < StandardError
45
- def initialize(msg)
46
- super(msg)
47
- end
87
+ # plistutil error class
88
+ class PlistutilCommandError < StandardError
89
+ def initialize(msg)
90
+ super(msg)
48
91
  end
49
92
  end
50
- end
93
+ end
94
+
@@ -0,0 +1,118 @@
1
+ require 'fruity_builder/lib/execution'
2
+ require 'fruity_builder/plistutil'
3
+
4
+ module FruityBuilder
5
+ # Namespace for all methods encapsulating idevice calls
6
+ class Signing < Execution
7
+
8
+ # Check to see if the path is an IPA
9
+ def self.is_ipa?(path)
10
+ return true if (File.extname path).downcase == '.ipa'
11
+ false
12
+ end
13
+
14
+ def self.unpack_ipa(path)
15
+ folder = File.dirname(path)
16
+ target = (File.basename path, (File.extname path))
17
+
18
+ # Check to see if the target has already been unzipped
19
+ return Dir["#{folder}/#{target}/Payload/*.app"].first if File.exists? ("#{folder}/#{target}/Payload/")
20
+
21
+ result = execute("unzip '#{path}' -d '#{folder}/#{target}'")
22
+ raise SigningCommandError.new(result.stderr) if result.exit != 0
23
+
24
+ Dir["#{folder}/#{target}/Payload/*.app"].first
25
+ end
26
+
27
+ def self.is_app_signed?(app_path)
28
+ app_path = unpack_ipa(app_path) if is_ipa?(app_path)
29
+ result = execute("codesign -d -vvvv '#{app_path}'")
30
+
31
+ if result.exit != 0
32
+ return false if /is not signed/.match(result.stderr)
33
+ raise SigningCommandError.new(result.stderr)
34
+ end
35
+
36
+ true
37
+ end
38
+
39
+ def self.sign_app(options = {})
40
+ cert = options[:cert]
41
+ entitlements = options[:entitlements]
42
+ app = options[:app]
43
+ original_app = nil
44
+
45
+ if is_ipa?(app)
46
+ original_app = app
47
+ app = unpack_ipa(app)
48
+ end
49
+
50
+ # Check to see if the entitlements passed in is a file or the XML
51
+ unless File.exists?(entitlements)
52
+ file = Tempfile.new('entitlements')
53
+ file.write(entitlements)
54
+ file.close
55
+ entitlements = file.path
56
+ end
57
+
58
+ result = execute("codesign --force --sign '#{cert}' --entitlements #{entitlements} '#{app}'")
59
+
60
+ raise SigningCommandError.new(result.stderr) if result.exit != 0
61
+
62
+ zip_app(app, original_app) if original_app
63
+ end
64
+
65
+ def self.zip_app(payload_path, ipa_path)
66
+ result = execute("cd #{File.dirname(payload_path)}; zip -r #{ipa_path} ../Payload ")
67
+ raise SigningCommandError.new(result.stderr) if result.exit != 0
68
+
69
+ true
70
+ end
71
+
72
+ def self.get_signing_certs
73
+ result = execute('security find-identity -p codesigning -v')
74
+
75
+ raise SigningCommandError.new(result.stderr) if result.exit != 0
76
+
77
+ certs = []
78
+ result.stdout.split("\n").each do |line|
79
+ if /\)\s*(\S*)\s*"(.*)"/.match(line)
80
+ certs << { id: Regexp.last_match[1], name: Regexp.last_match[2] }
81
+ end
82
+ end
83
+ certs
84
+ end
85
+
86
+ def self.get_entitlements(app_path, raw = false)
87
+ app_path = unpack_ipa(app_path) if is_ipa?(app_path)
88
+ result = execute("codesign -d --entitlements - #{app_path}")
89
+
90
+ if result.exit != 0
91
+ raise SigningCommandError.new(result.stderr)
92
+ end
93
+
94
+ # Clean up the result as it occasionally contains invalid UTF-8 characters
95
+ xml = result.stdout.to_s.encode('UTF-8', 'UTF-8', invalid: :replace)
96
+ xml = xml[xml.index('<')..xml.length]
97
+
98
+ return xml if raw
99
+ entitlements = Plistutil.parse_xml(xml)
100
+ return entitlements
101
+ end
102
+
103
+ def self.enable_get_tasks(app_path)
104
+ entitlements = get_entitlements(app_path, raw: true)
105
+
106
+ return entitlements if entitlements.scan(/<key>get-task-allow<\/key>\\n.*<true\/>/).count > 0
107
+
108
+ entitlements.gsub('<false/>', '<true/>') if entitlements.scan(/<key>get-task-allow<\/key>\\n.*<false\/>/)
109
+ end
110
+ end
111
+
112
+ # Signing error class
113
+ class SigningCommandError < StandardError
114
+ def initialize(msg)
115
+ super(msg)
116
+ end
117
+ end
118
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fruity_builder
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - BBC
@@ -9,8 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-02-03 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2016-02-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ox
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: 2.1.0
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: 2.1.0
14
28
  description: iOS code signing utilities - used to replace bundle IDs, development
15
29
  teams and provisioning profiles programmatically
16
30
  email:
@@ -25,6 +39,7 @@ files:
25
39
  - lib/fruity_builder/lib/execution.rb
26
40
  - lib/fruity_builder/lib/sys_log.rb
27
41
  - lib/fruity_builder/plistutil.rb
42
+ - lib/fruity_builder/signing.rb
28
43
  - lib/fruity_builder/xcodebuild.rb
29
44
  homepage: https://github.com/bbc/fruity_builder
30
45
  licenses: