fastlane-plugin-branch 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,16 @@
1
+ module Fastlane
2
+ module Helper
3
+ class BranchOptions
4
+ attr_reader :params
5
+
6
+ # :param: params [FastlaneCore::Configuration] Params from an action
7
+ def initialize(params)
8
+ @params = params
9
+ end
10
+
11
+ def method_missing(method_sym, *arguments, &block)
12
+ return params[method_sym]
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module Branch
3
- VERSION = "0.5.1"
3
+ VERSION = "0.6.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-branch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Branch
@@ -9,10 +9,10 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-10-07 00:00:00.000000000 Z
12
+ date: 2017-11-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: fastlane-plugin-patch
15
+ name: branch_io_cli
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
18
  - - ">="
@@ -26,27 +26,13 @@ dependencies:
26
26
  - !ruby/object:Gem::Version
27
27
  version: '0'
28
28
  - !ruby/object:Gem::Dependency
29
- name: plist
30
- requirement: !ruby/object:Gem::Requirement
31
- requirements:
32
- - - ">="
33
- - !ruby/object:Gem::Version
34
- version: '0'
35
- type: :runtime
36
- prerelease: false
37
- version_requirements: !ruby/object:Gem::Requirement
38
- requirements:
39
- - - ">="
40
- - !ruby/object:Gem::Version
41
- version: '0'
42
- - !ruby/object:Gem::Dependency
43
- name: xcodeproj
29
+ name: pry
44
30
  requirement: !ruby/object:Gem::Requirement
45
31
  requirements:
46
32
  - - ">="
47
33
  - !ruby/object:Gem::Version
48
34
  version: '0'
49
- type: :runtime
35
+ type: :development
50
36
  prerelease: false
51
37
  version_requirements: !ruby/object:Gem::Requirement
52
38
  requirements:
@@ -54,7 +40,7 @@ dependencies:
54
40
  - !ruby/object:Gem::Version
55
41
  version: '0'
56
42
  - !ruby/object:Gem::Dependency
57
- name: pry
43
+ name: bundler
58
44
  requirement: !ruby/object:Gem::Requirement
59
45
  requirements:
60
46
  - - ">="
@@ -68,7 +54,7 @@ dependencies:
68
54
  - !ruby/object:Gem::Version
69
55
  version: '0'
70
56
  - !ruby/object:Gem::Dependency
71
- name: bundler
57
+ name: rspec
72
58
  requirement: !ruby/object:Gem::Requirement
73
59
  requirements:
74
60
  - - ">="
@@ -82,7 +68,7 @@ dependencies:
82
68
  - !ruby/object:Gem::Version
83
69
  version: '0'
84
70
  - !ruby/object:Gem::Dependency
85
- name: rspec
71
+ name: rake
86
72
  requirement: !ruby/object:Gem::Requirement
87
73
  requirements:
88
74
  - - ">="
@@ -96,7 +82,7 @@ dependencies:
96
82
  - !ruby/object:Gem::Version
97
83
  version: '0'
98
84
  - !ruby/object:Gem::Dependency
99
- name: rake
85
+ name: rspec_junit_formatter
100
86
  requirement: !ruby/object:Gem::Requirement
101
87
  requirements:
102
88
  - - ">="
@@ -127,16 +113,16 @@ dependencies:
127
113
  name: rubocop
128
114
  requirement: !ruby/object:Gem::Requirement
129
115
  requirements:
130
- - - ">="
116
+ - - "~>"
131
117
  - !ruby/object:Gem::Version
132
- version: '0'
118
+ version: 0.50.0
133
119
  type: :development
134
120
  prerelease: false
135
121
  version_requirements: !ruby/object:Gem::Requirement
136
122
  requirements:
137
- - - ">="
123
+ - - "~>"
138
124
  - !ruby/object:Gem::Version
139
- version: '0'
125
+ version: 0.50.0
140
126
  - !ruby/object:Gem::Dependency
141
127
  name: simplecov
142
128
  requirement: !ruby/object:Gem::Requirement
@@ -178,10 +164,7 @@ files:
178
164
  - lib/fastlane/plugin/branch.rb
179
165
  - lib/fastlane/plugin/branch/actions/setup_branch_action.rb
180
166
  - lib/fastlane/plugin/branch/actions/validate_universal_links_action.rb
181
- - lib/fastlane/plugin/branch/helper/android_helper.rb
182
- - lib/fastlane/plugin/branch/helper/branch_helper.rb
183
- - lib/fastlane/plugin/branch/helper/configuration_helper.rb
184
- - lib/fastlane/plugin/branch/helper/ios_helper.rb
167
+ - lib/fastlane/plugin/branch/helper/branch_options.rb
185
168
  - lib/fastlane/plugin/branch/version.rb
186
169
  homepage: https://github.com/BranchMetrics/fastlane-plugin-branch
187
170
  licenses:
@@ -203,7 +186,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
203
186
  version: '0'
204
187
  requirements: []
205
188
  rubyforge_project:
206
- rubygems_version: 2.6.13
189
+ rubygems_version: 2.6.14
207
190
  signing_key:
208
191
  specification_version: 4
209
192
  summary: Adds Branch keys, custom URI schemes and domains to iOS and Android projects.
@@ -1,86 +0,0 @@
1
- module Fastlane
2
- module Helper
3
- module AndroidHelper
4
- def add_keys_to_android_manifest(manifest, keys)
5
- add_metadata_to_manifest manifest, "io.branch.sdk.BranchKey", keys[:live] unless keys[:live].nil?
6
- add_metadata_to_manifest manifest, "io.branch.sdk.BranchKey.test", keys[:test] unless keys[:test].nil?
7
- end
8
-
9
- # TODO: Work on all XML/AndroidManifest formatting
10
-
11
- def add_metadata_to_manifest(manifest, key, value)
12
- element = manifest.elements["//manifest/application/meta-data[@android:name=\"#{key}\"]"]
13
- if element.nil?
14
- application = manifest.elements["//manifest/application"]
15
- application.add_element "meta-data", "android:name" => key, "android:value" => value
16
- else
17
- element.attributes["android:value"] = value
18
- end
19
- end
20
-
21
- def add_intent_filters_to_android_manifest(manifest, domains, uri_scheme, activity_name, remove_existing)
22
- if activity_name
23
- activity = manifest.elements["//manifest/application/activity[@android:name=\"#{activity_name}\""]
24
- else
25
- activity = find_activity manifest
26
- end
27
-
28
- raise "Failed to find an Activity in the Android manifest" if activity.nil?
29
-
30
- if remove_existing
31
- remove_existing_domains(activity)
32
- end
33
-
34
- add_intent_filter_to_activity activity, domains, uri_scheme
35
- end
36
-
37
- def find_activity(manifest)
38
- # try to infer the right activity
39
- # look for the first singleTask
40
- single_task_activity = manifest.elements["//manifest/application/activity[@android:launchMode=\"singleTask\"]"]
41
- return single_task_activity if single_task_activity
42
-
43
- # no singleTask activities. Take the first Activity
44
- # TODO: Add singleTask?
45
- manifest.elements["//manifest/application/activity"]
46
- end
47
-
48
- def add_intent_filter_to_activity(activity, domains, uri_scheme)
49
- # Add a single intent-filter with autoVerify and a data element for each domain and the optional uri_scheme
50
- intent_filter = REXML::Element.new "intent-filter"
51
- intent_filter.attributes["android:autoVerify"] = true
52
- intent_filter.add_element "action", "android:name" => "android.intent.action.VIEW"
53
- intent_filter.add_element "category", "android:name" => "android.intent.category.DEFAULT"
54
- intent_filter.add_element "category", "android:name" => "android.intent.category.BROWSABLE"
55
- intent_filter.elements << uri_scheme_data_element(uri_scheme) unless uri_scheme.nil?
56
- app_link_data_elements(domains).each { |e| intent_filter.elements << e }
57
-
58
- activity.add_element intent_filter
59
- end
60
-
61
- def remove_existing_domains(activity)
62
- # Find all intent-filters that include a data element with android:scheme
63
- # TODO: Can this be done with a single css/at_css call?
64
- activity.elements.each("//manifest//intent-filter") do |filter|
65
- filter.remove if filter.elements["data[@android:scheme]"]
66
- end
67
- end
68
-
69
- def app_link_data_elements(domains)
70
- domains.map do |domain|
71
- element = REXML::Element.new "data"
72
- element.attributes["android:scheme"] = "https"
73
- element.attributes["android:host"] = domain
74
- element
75
- end
76
- end
77
-
78
- def uri_scheme_data_element(uri_scheme)
79
- element = REXML::Element.new "data"
80
- element.attributes["android:scheme"] = uri_scheme
81
- element.attributes["android:host"] = "open"
82
- element
83
- end
84
- end
85
- end
86
- end
@@ -1,25 +0,0 @@
1
- require "fastlane/plugin/branch/helper/android_helper"
2
- require "fastlane/plugin/branch/helper/configuration_helper"
3
- require "fastlane/plugin/branch/helper/ios_helper"
4
-
5
- module Fastlane
6
- module Helper
7
- UI = FastlaneCore::UI
8
-
9
- class BranchHelper
10
- class << self
11
- attr_accessor :changes # An array of file paths (Strings) that were modified
12
- attr_accessor :errors # An array of error messages (Strings) from validation
13
-
14
- include AndroidHelper
15
- include ConfigurationHelper
16
- include IOSHelper
17
-
18
- def add_change(change)
19
- @changes ||= Set.new
20
- @changes << change.to_s
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,132 +0,0 @@
1
- module Fastlane
2
- module Helper
3
- module ConfigurationHelper
4
- def keys_from_params(params)
5
- live_key = params[:live_key]
6
- test_key = params[:test_key]
7
- keys = {}
8
- keys[:live] = live_key unless live_key.nil?
9
- keys[:test] = test_key unless test_key.nil?
10
- keys
11
- end
12
-
13
- def xcodeproj_path_from_params(params)
14
- return params[:xcodeproj] if params[:xcodeproj]
15
-
16
- # Adapted from commit_version_bump
17
- # https://github.com/fastlane/fastlane/blob/master/fastlane/lib/fastlane/actions/commit_version_bump.rb#L21
18
-
19
- # This may not be a git project. Search relative to the Gemfile.
20
- repo_path = Bundler.root
21
-
22
- all_xcodeproj_paths = Dir[File.expand_path(File.join(repo_path, '**/*.xcodeproj'))]
23
- # find an xcodeproj (ignoring the Cocoapods one)
24
- xcodeproj_paths = Fastlane::Actions.ignore_cocoapods_path(all_xcodeproj_paths)
25
-
26
- # no projects found: error
27
- UI.user_error!('Could not find a .xcodeproj in the current repository\'s working directory.') and return nil if xcodeproj_paths.count == 0
28
-
29
- # too many projects found: error
30
- if xcodeproj_paths.count > 1
31
- repo_pathname = Pathname.new repo_path
32
- relative_projects = xcodeproj_paths.map { |e| Pathname.new(e).relative_path_from(repo_pathname).to_s }.join("\n")
33
- UI.user_error!("Found multiple .xcodeproj projects in the current repository's working directory. Please specify your app's main project: \n#{relative_projects}")
34
- return nil
35
- end
36
-
37
- # one project found: great
38
- xcodeproj_paths.first
39
- end
40
-
41
- def domains_from_params(params)
42
- app_link_subdomains = app_link_subdomains_from_params params
43
- custom_domains = custom_domains_from_params params
44
- (app_link_subdomains + custom_domains).uniq
45
- end
46
-
47
- def app_link_subdomains_from_params(params)
48
- app_link_subdomain = params[:app_link_subdomain]
49
- live_key = params[:live_key]
50
- test_key = params[:test_key]
51
- return [] if live_key.nil? and test_key.nil?
52
- return [] if app_link_subdomain.nil?
53
-
54
- domains = []
55
- unless live_key.nil?
56
- domains += [
57
- "#{app_link_subdomain}.app.link",
58
- "#{app_link_subdomain}-alternate.app.link"
59
- ]
60
- end
61
- unless test_key.nil?
62
- domains += [
63
- "#{app_link_subdomain}.test-app.link",
64
- "#{app_link_subdomain}-alternate.test-app.link"
65
- ]
66
- end
67
- domains
68
- end
69
-
70
- def custom_domains_from_params(params)
71
- domains = params[:domains]
72
- return [] if domains.nil?
73
-
74
- if domains.kind_of? Array
75
- domains = domains.map(&:to_s)
76
- elsif domains.kind_of? String
77
- domains = domains.split(",")
78
- else
79
- raise ArgumentError, "Unsupported type #{domains.class.name} for :domains key"
80
- end
81
-
82
- domains
83
- end
84
-
85
- def podfile_path_from_params(params)
86
- # Disable Podfile update if add_sdk: false is present
87
- return nil unless add_sdk? params
88
-
89
- # Use the :podfile parameter if present
90
- if params[:podfile]
91
- UI.user_error! ":podfile argument must specify a path ending in '/Podfile'" unless params[:podfile] =~ %r{/Podfile$}
92
- podfile_path = File.expand_path params[:podfile], Bundler.root
93
- return podfile_path if File.exist? podfile_path
94
- UI.user_error! "#{podfile_path} not found"
95
- end
96
-
97
- # Look in the same directory as the project (typical setup)
98
- podfile_path = File.expand_path "../Podfile", params[:xcodeproj]
99
- return podfile_path if File.exist? podfile_path
100
- end
101
-
102
- def cartfile_path_from_params(params)
103
- # Disable Cartfile update if add_sdk: false is present
104
- return nil unless add_sdk? params
105
-
106
- # Use the :cartfile parameter if present
107
- if params[:cartfile]
108
- UI.user_error! ":cartfile argument must specify a path ending in '/Cartfile'" unless params[:cartfile] =~ %r{/Cartfile$}
109
- cartfile_path = File.expand_path params[:cartfile], Bundler.root
110
- return cartfile_path if File.exist? cartfile_path
111
- UI.user_error! "#{cartfile_path} not found"
112
- end
113
-
114
- # Look in the same directory as the project (typical setup)
115
- cartfile_path = File.expand_path "../Cartfile", params[:xcodeproj]
116
- return cartfile_path if File.exist? cartfile_path
117
- end
118
-
119
- def add_sdk?(params)
120
- add_sdk_param = params[:add_sdk]
121
- return false if add_sdk_param.nil?
122
-
123
- case add_sdk_param
124
- when String
125
- add_sdk_param.casecmp? "true"
126
- else
127
- add_sdk_param
128
- end
129
- end
130
- end
131
- end
132
- end
@@ -1,505 +0,0 @@
1
- require "fastlane/plugin/patch"
2
- require "plist"
3
-
4
- module Fastlane
5
- module Helper
6
- module IOSHelper
7
- APPLINKS = "applinks"
8
- ASSOCIATED_DOMAINS = "com.apple.developer.associated-domains"
9
- CODE_SIGN_ENTITLEMENTS = "CODE_SIGN_ENTITLEMENTS"
10
- DEVELOPMENT_TEAM = "DEVELOPMENT_TEAM"
11
- PRODUCT_BUNDLE_IDENTIFIER = "PRODUCT_BUNDLE_IDENTIFIER"
12
- RELEASE_CONFIGURATION = "Release"
13
-
14
- def add_keys_to_info_plist(project, target_name, keys, configuration = RELEASE_CONFIGURATION)
15
- update_info_plist_setting project, target_name, configuration do |info_plist|
16
- # add/overwrite Branch key(s)
17
- if keys.count > 1
18
- info_plist["branch_key"] = keys
19
- elsif keys[:live]
20
- info_plist["branch_key"] = keys[:live]
21
- else # no need to validate here, which was done by the action
22
- info_plist["branch_key"] = keys[:test]
23
- end
24
- end
25
- end
26
-
27
- def add_branch_universal_link_domains_to_info_plist(project, target_name, domains, configuration = RELEASE_CONFIGURATION)
28
- # Add all supplied domains unless all are app.link domains.
29
- return if domains.all? { |d| d =~ /app\.link$/ }
30
-
31
- update_info_plist_setting project, target_name, configuration do |info_plist|
32
- info_plist["branch_universal_link_domains"] = domains
33
- end
34
- end
35
-
36
- def update_info_plist_setting(project, target_name, configuration = RELEASE_CONFIGURATION, &b)
37
- # raises
38
- target = target_from_project project, target_name
39
-
40
- # find the Info.plist paths for this configuration
41
- info_plist_path = expanded_build_setting target, "INFOPLIST_FILE", configuration
42
-
43
- raise "Info.plist not found for configuration #{configuration}" if info_plist_path.nil?
44
-
45
- project_parent = File.dirname project.path
46
-
47
- info_plist_path = File.expand_path info_plist_path, project_parent
48
-
49
- # try to open and parse the Info.plist (raises)
50
- info_plist = File.open(info_plist_path) { |f| Plist.parse_xml f }
51
- raise "Failed to parse #{info_plist_path}" if info_plist.nil?
52
-
53
- yield info_plist
54
-
55
- Plist::Emit.save_plist info_plist, info_plist_path
56
- add_change info_plist_path
57
- end
58
-
59
- def add_universal_links_to_project(project, target_name, domains, remove_existing, configuration = RELEASE_CONFIGURATION)
60
- # raises
61
- target = target_from_project project, target_name
62
-
63
- relative_entitlements_path = expanded_build_setting target, CODE_SIGN_ENTITLEMENTS, configuration
64
- project_parent = File.dirname project.path
65
-
66
- if relative_entitlements_path.nil?
67
- relative_entitlements_path = File.join target.name, "#{target.name}.entitlements"
68
- entitlements_path = File.expand_path relative_entitlements_path, project_parent
69
-
70
- # Add CODE_SIGN_ENTITLEMENTS setting to each configuration
71
- target.build_configuration_list.set_setting CODE_SIGN_ENTITLEMENTS, relative_entitlements_path
72
-
73
- # Add the file to the project
74
- project.new_file relative_entitlements_path
75
-
76
- entitlements = {}
77
- current_domains = []
78
-
79
- add_change project.path
80
- new_path = entitlements_path
81
- else
82
- entitlements_path = File.expand_path relative_entitlements_path, project_parent
83
- # Raises
84
- entitlements = File.open(entitlements_path) { |f| Plist.parse_xml f }
85
- raise "Failed to parse entitlements file #{entitlements_path}" if entitlements.nil?
86
-
87
- if remove_existing
88
- current_domains = []
89
- else
90
- current_domains = entitlements[ASSOCIATED_DOMAINS]
91
- end
92
- end
93
-
94
- current_domains += domains.map { |d| "#{APPLINKS}:#{d}" }
95
- all_domains = current_domains.uniq
96
-
97
- entitlements[ASSOCIATED_DOMAINS] = all_domains
98
-
99
- Plist::Emit.save_plist entitlements, entitlements_path
100
- add_change entitlements_path
101
-
102
- new_path
103
- end
104
-
105
- def team_and_bundle_from_app_id(identifier)
106
- team = identifier.sub(/\..+$/, "")
107
- bundle = identifier.sub(/^[^.]+\./, "")
108
- [team, bundle]
109
- end
110
-
111
- def update_team_and_bundle_ids_from_aasa_file(project, target_name, domain)
112
- # raises
113
- identifiers = app_ids_from_aasa_file domain
114
- raise "Multiple appIDs found in AASA file" if identifiers.count > 1
115
-
116
- identifier = identifiers[0]
117
- team, bundle = team_and_bundle_from_app_id identifier
118
-
119
- update_team_and_bundle_ids project, target_name, team, bundle
120
- add_change project.path.expand_path
121
- end
122
-
123
- def validate_team_and_bundle_ids_from_aasa_files(project, target_name, domains = [], remove_existing = false, configuration = RELEASE_CONFIGURATION)
124
- @errors = []
125
- valid = true
126
-
127
- # Include any domains already in the project.
128
- # Raises. Returns a non-nil array of strings.
129
- if remove_existing
130
- # Don't validate domains to be removed (#16)
131
- all_domains = domains
132
- else
133
- all_domains = (domains + domains_from_project(project, target_name, configuration)).uniq
134
- end
135
-
136
- if all_domains.empty?
137
- # Cannot get here from SetupBranchAction, since the domains passed in will never be empty.
138
- # If called from ValidateUniversalLinksAction, this is a failure, possibly caused by
139
- # failure to add applinks:.
140
- @errors << "No Universal Link domains in project. Be sure each Universal Link domain is prefixed with applinks:."
141
- return false
142
- end
143
-
144
- all_domains.each do |domain|
145
- domain_valid = validate_team_and_bundle_ids project, target_name, domain, configuration
146
- valid &&= domain_valid
147
- UI.message "Valid Universal Link configuration for #{domain} ✅" if domain_valid
148
- end
149
- valid
150
- end
151
-
152
- def app_ids_from_aasa_file(domain)
153
- data = contents_of_aasa_file domain
154
- # errors reported in the method above
155
- return nil if data.nil?
156
-
157
- # raises
158
- file = JSON.parse data
159
-
160
- applinks = file[APPLINKS]
161
- @errors << "[#{domain}] No #{APPLINKS} found in AASA file" and return if applinks.nil?
162
-
163
- details = applinks["details"]
164
- @errors << "[#{domain}] No details found for #{APPLINKS} in AASA file" and return if details.nil?
165
-
166
- identifiers = details.map { |d| d["appID"] }.uniq
167
- @errors << "[#{domain}] No appID found in AASA file" and return if identifiers.count <= 0
168
- identifiers
169
- rescue JSON::ParserError => e
170
- @errors << "[#{domain}] Failed to parse AASA file: #{e.message}"
171
- nil
172
- end
173
-
174
- def contents_of_aasa_file(domain)
175
- uris = [
176
- URI("https://#{domain}/.well-known/apple-app-site-association"),
177
- URI("https://#{domain}/apple-app-site-association")
178
- # URI("http://#{domain}/.well-known/apple-app-site-association"),
179
- # URI("http://#{domain}/apple-app-site-association")
180
- ]
181
-
182
- data = nil
183
-
184
- uris.each do |uri|
185
- break unless data.nil?
186
-
187
- Net::HTTP.start uri.host, uri.port, use_ssl: uri.scheme == "https" do |http|
188
- request = Net::HTTP::Get.new uri
189
- response = http.request request
190
-
191
- # Better to use Net::HTTPRedirection and Net::HTTPSuccess here, but
192
- # having difficulty with the unit tests.
193
- if (300..399).cover?(response.code.to_i)
194
- UI.important "#{uri} cannot result in a redirect. Ignoring."
195
- next
196
- elsif response.code.to_i != 200
197
- # Try the next URI.
198
- UI.message "Could not retrieve #{uri}: #{response.code} #{response.message}. Ignoring."
199
- next
200
- end
201
-
202
- content_type = response["Content-type"]
203
- @errors << "[#{domain}] AASA Response does not contain a Content-type header" and next if content_type.nil?
204
-
205
- case content_type
206
- when %r{application/pkcs7-mime}
207
- # Verify/decrypt PKCS7 (non-Branch domains)
208
- cert_store = OpenSSL::X509::Store.new
209
- signature = OpenSSL::PKCS7.new response.body
210
- # raises
211
- signature.verify nil, cert_store, nil, OpenSSL::PKCS7::NOVERIFY
212
- data = signature.data
213
- else
214
- @error << "[#{domain}] Unsigned AASA files must be served via HTTPS" and next if uri.scheme == "http"
215
- data = response.body
216
- end
217
-
218
- UI.message "GET #{uri}: #{response.code} #{response.message} (Content-type:#{content_type}) ✅"
219
- end
220
- end
221
-
222
- @errors << "[#{domain}] Failed to retrieve AASA file" and return nil if data.nil?
223
-
224
- data
225
- rescue IOError, SocketError => e
226
- @errors << "[#{domain}] Socket error: #{e.message}"
227
- nil
228
- rescue OpenSSL::PKCS7::PKCS7Error => e
229
- @errors << "[#{domain}] Failed to verify signed AASA file: #{e.message}"
230
- nil
231
- end
232
-
233
- def validate_team_and_bundle_ids(project, target_name, domain, configuration)
234
- # raises
235
- target = target_from_project project, target_name
236
-
237
- product_bundle_identifier = expanded_build_setting target, PRODUCT_BUNDLE_IDENTIFIER, configuration
238
- development_team = expanded_build_setting target, DEVELOPMENT_TEAM, configuration
239
-
240
- identifiers = app_ids_from_aasa_file domain
241
- return false if identifiers.nil?
242
-
243
- app_id = "#{development_team}.#{product_bundle_identifier}"
244
- match_found = identifiers.include? app_id
245
-
246
- unless match_found
247
- @errors << "[#{domain}] appID mismatch. Project: #{app_id}. AASA: #{identifiers}"
248
- end
249
-
250
- match_found
251
- end
252
-
253
- def validate_project_domains(expected, project, target, configuration = RELEASE_CONFIGURATION)
254
- @errors = []
255
- project_domains = domains_from_project project, target, configuration
256
- valid = expected.count == project_domains.count
257
- if valid
258
- sorted = expected.sort
259
- project_domains.sort.each_with_index do |domain, index|
260
- valid = false and break unless sorted[index] == domain
261
- end
262
- end
263
-
264
- unless valid
265
- @errors << "Project domains do not match :domains parameter"
266
- @errors << "Project domains: #{project_domains}"
267
- @errors << ":domains parameter: #{expected}"
268
- end
269
-
270
- valid
271
- end
272
-
273
- def update_team_and_bundle_ids(project, target_name, team, bundle)
274
- # raises
275
- target = target_from_project project, target_name
276
-
277
- target.build_configuration_list.set_setting PRODUCT_BUNDLE_IDENTIFIER, bundle
278
- target.build_configuration_list.set_setting DEVELOPMENT_TEAM, team
279
-
280
- # also update the team in the first test target
281
- target = project.targets.find(&:test_target_type?)
282
- return if target.nil?
283
-
284
- target.build_configuration_list.set_setting DEVELOPMENT_TEAM, team
285
- end
286
-
287
- def target_from_project(project, target_name)
288
- if target_name
289
- target = project.targets.find { |t| t.name == target_name }
290
- raise "Target #{target} not found" if target.nil?
291
- else
292
- # find the first application target
293
- target = project.targets.find { |t| !t.extension_target_type? && !t.test_target_type? }
294
- raise "No application target found" if target.nil?
295
- end
296
- target
297
- end
298
-
299
- def domains_from_project(project, target_name, configuration = RELEASE_CONFIGURATION)
300
- # Raises. Does not return nil.
301
- target = target_from_project project, target_name
302
-
303
- relative_entitlements_path = expanded_build_setting target, CODE_SIGN_ENTITLEMENTS, configuration
304
- return [] if relative_entitlements_path.nil?
305
-
306
- project_parent = File.dirname project.path
307
- entitlements_path = File.expand_path relative_entitlements_path, project_parent
308
-
309
- # Raises
310
- entitlements = File.open(entitlements_path) { |f| Plist.parse_xml f }
311
- raise "Failed to parse entitlements file #{entitlements_path}" if entitlements.nil?
312
-
313
- entitlements[ASSOCIATED_DOMAINS].select { |d| d =~ /^applinks:/ }.map { |d| d.sub(/^applinks:/, "") }
314
- end
315
-
316
- def expanded_build_setting(target, setting_name, configuration)
317
- setting_value = target.resolved_build_setting(setting_name)[configuration]
318
- return if setting_value.nil?
319
-
320
- search_position = 0
321
- while (matches = /\$\(([^(){}]*)\)|\$\{([^(){}]*)\}/.match(setting_value, search_position))
322
- macro_name = matches[1] || matches[2]
323
- search_position = setting_value.index(macro_name) - 2
324
-
325
- expanded_macro = macro_name == "SRCROOT" ? "." : expanded_build_setting(target, macro_name, configuration)
326
- search_position += macro_name.length + 3 and next if expanded_macro.nil?
327
-
328
- setting_value.gsub!(/\$\(#{macro_name}\)|\$\{#{macro_name}\}/, expanded_macro)
329
- search_position += expanded_macro.length
330
- end
331
- setting_value
332
- end
333
-
334
- def add_system_frameworks(project, target_name, frameworks)
335
- target = target_from_project project, target_name
336
-
337
- target.add_system_framework frameworks
338
- end
339
-
340
- def patch_app_delegate_swift(project)
341
- app_delegate_swift = project.files.find { |f| f.path =~ /AppDelegate.swift$/ }
342
- return false if app_delegate_swift.nil?
343
-
344
- app_delegate_swift_path = app_delegate_swift.real_path.to_s
345
-
346
- app_delegate = File.open(app_delegate_swift_path, &:read)
347
- return false if app_delegate =~ /import\s+Branch/
348
-
349
- UI.message "Patching #{app_delegate_swift_path}"
350
-
351
- Actions::PatchAction.run(
352
- files: app_delegate_swift_path,
353
- regexp: /^\s*import .*$/,
354
- text: "\nimport Branch",
355
- mode: :prepend,
356
- offset: 0
357
- )
358
-
359
- # TODO: This is Swift 3. Support other versions, esp. 4.
360
- init_session_text = <<-EOF
361
- #if DEBUG
362
- Branch.setUseTestBranchKey(true)
363
- #endif
364
-
365
- Branch.getInstance().initSession(launchOptions: launchOptions) {
366
- universalObject, linkProperties, error in
367
-
368
- // TODO: Route Branch links
369
- }
370
- EOF
371
-
372
- Actions::PatchAction.run(
373
- files: app_delegate_swift_path,
374
- regexp: /didFinishLaunchingWithOptions.*?\{[^\n]*\n/m,
375
- text: init_session_text,
376
- mode: :append,
377
- offset: 0
378
- )
379
-
380
- unless app_delegate =~ /application:.*continueUserActivity:.*restorationHandler:/
381
- # Add the application:continueUserActivity:restorationHandler method if it does not exist
382
- continue_user_activity_text = <<-EOF
383
-
384
-
385
- func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
386
- return Branch.getInstance().continue(userActivity)
387
- }
388
- EOF
389
-
390
- Actions::PatchAction.run(
391
- files: app_delegate_swift_path,
392
- regexp: /\n\s*\}[^{}]*\Z/m,
393
- text: continue_user_activity_text,
394
- mode: :prepend,
395
- offset: 0
396
- )
397
- end
398
-
399
- add_change app_delegate_swift_path
400
- true
401
- end
402
-
403
- def patch_app_delegate_objc(project)
404
- app_delegate_objc = project.files.find { |f| f.path =~ /AppDelegate.m$/ }
405
- return false if app_delegate_objc.nil?
406
-
407
- app_delegate_objc_path = app_delegate_objc.real_path.to_s
408
-
409
- app_delegate = File.open(app_delegate_objc_path, &:read)
410
- return false if app_delegate =~ %r{^\s+#import\s+<Branch/Branch.h>|^\s+@import\s+Branch;}
411
-
412
- UI.message "Patching #{app_delegate_objc_path}"
413
-
414
- Actions::PatchAction.run(
415
- files: app_delegate_objc_path,
416
- regexp: /^\s+@import|^\s+#import.*$/,
417
- text: "\n#import <Branch/Branch.h>",
418
- mode: :prepend,
419
- offset: 0
420
- )
421
-
422
- init_session_text = <<-EOF
423
- #ifdef DEBUG
424
- [Branch setUseTestBranchKey:YES];
425
- #endif // DEBUG
426
-
427
- [[Branch getInstance] initSessionWithLaunchOptions:launchOptions
428
- andRegisterDeepLinkHandlerUsingBranchUniversalObject:^(BranchUniversalObject *universalObject, BranchLinkProperties *linkProperties, NSError *error){
429
- // TODO: Route Branch links
430
- }];
431
- EOF
432
-
433
- Actions::PatchAction.run(
434
- files: app_delegate_objc_path,
435
- regexp: /didFinishLaunchingWithOptions.*?\{[^\n]*\n/m,
436
- text: init_session_text,
437
- mode: :append,
438
- offset: 0
439
- )
440
-
441
- unless app_delegate =~ /application:.*continueUserActivity:.*restorationHandler:/
442
- # Add the application:continueUserActivity:restorationHandler method if it does not exist
443
- continue_user_activity_text = <<-EOF
444
-
445
-
446
- - (BOOL)application:(UIApplication *)app continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray * _Nullable))restorationHandler
447
- {
448
- return [[Branch getInstance] continueUserActivity:userActivity];
449
- }
450
- EOF
451
-
452
- Actions::PatchAction.run(
453
- files: app_delegate_objc_path,
454
- regexp: /\n\s*@end[^@]*\Z/m,
455
- text: continue_user_activity_text,
456
- mode: :prepend,
457
- offset: 0
458
- )
459
- end
460
-
461
- add_change app_delegate_objc_path
462
- true
463
- end
464
-
465
- def patch_podfile(podfile_path)
466
- podfile = File.open(podfile_path, &:read)
467
-
468
- # Podfile already contains the Branch pod
469
- return false if podfile =~ /pod\s+('Branch'|"Branch")/
470
-
471
- UI.message "Adding pod \"Branch\" to #{podfile_path}"
472
-
473
- # TODO: Improve this patch. Should work in the majority of cases for now.
474
- Actions::PatchAction.run(
475
- files: podfile_path,
476
- regexp: /^(\s*)pod\s*/,
477
- text: "\n\\1pod \"Branch\"\n",
478
- mode: :prepend,
479
- offset: 0
480
- )
481
-
482
- true
483
- end
484
-
485
- def patch_cartfile(cartfile_path)
486
- cartfile = File.open(cartfile_path, &:read)
487
-
488
- # Cartfile already contains the Branch framework
489
- return false if cartfile =~ /git.+Branch/
490
-
491
- UI.message "Adding \"Branch\" to #{cartfile_path}"
492
-
493
- Actions::PatchAction.run(
494
- files: cartfile_path,
495
- regexp: /\z/,
496
- text: "git \"https://github.com/BranchMetrics/ios-branch-deep-linking\"\n",
497
- mode: :append,
498
- offset: 0
499
- )
500
-
501
- true
502
- end
503
- end
504
- end
505
- end