match 0.1.0

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,94 @@
1
+ require 'fastlane_core'
2
+ require 'credentials_manager'
3
+
4
+ module Match
5
+ class Options
6
+ def self.available_options
7
+ user = CredentialsManager::AppfileConfig.try_fetch_value(:apple_dev_portal_id)
8
+ user ||= CredentialsManager::AppfileConfig.try_fetch_value(:apple_id)
9
+
10
+ [
11
+ FastlaneCore::ConfigItem.new(key: :git_url,
12
+ env_name: "MATCH_GIT_URl",
13
+ description: "URL to the git repo containing all the certificates",
14
+ optional: false,
15
+ short_option: "-r",
16
+ verify_block: proc do |value|
17
+ unless value.start_with?("https://") || value.start_with?("git")
18
+ raise "git_url must start with either https:// or git://".red
19
+ end
20
+ end),
21
+ FastlaneCore::ConfigItem.new(key: :type,
22
+ env_name: "MATCH_TYPE",
23
+ description: "Create a development certificate instead of a distribution one",
24
+ is_string: true,
25
+ short_option: "-y",
26
+ default_value: 'development',
27
+ verify_block: proc do |value|
28
+ unless Match.environments.include?(value)
29
+ raise "Unsupported environment #{value}, must be in #{Match.environments.join(', ')}".red
30
+ end
31
+ end),
32
+ FastlaneCore::ConfigItem.new(key: :app_identifier,
33
+ short_option: "-a",
34
+ env_name: "MATCH_APP_IDENTIFIER",
35
+ description: "The bundle identifier of your app",
36
+ default_value: CredentialsManager::AppfileConfig.try_fetch_value(:app_identifier)),
37
+ FastlaneCore::ConfigItem.new(key: :username,
38
+ short_option: "-u",
39
+ env_name: "MATCH_USERNAME",
40
+ description: "Your Apple ID Username",
41
+ default_value: user),
42
+ FastlaneCore::ConfigItem.new(key: :keychain_name,
43
+ short_option: "-s",
44
+ env_name: "MATCH_KEYCHAIN_NAME",
45
+ description: "Keychain the items should be imported to",
46
+ default_value: "login.keychain"),
47
+ FastlaneCore::ConfigItem.new(key: :readonly,
48
+ env_name: "MATCH_READONLY",
49
+ description: "Only fetch existing certificates and profiles, don't generate new ones",
50
+ is_string: false,
51
+ default_value: false),
52
+ FastlaneCore::ConfigItem.new(key: :team_id,
53
+ short_option: "-b",
54
+ env_name: "MATCH_TEAM_ID",
55
+ description: "The ID of your team if you're in multiple teams",
56
+ optional: true,
57
+ default_value: CredentialsManager::AppfileConfig.try_fetch_value(:team_id),
58
+ verify_block: proc do |value|
59
+ ENV["FASTLANE_TEAM_ID"] = value
60
+ end),
61
+ FastlaneCore::ConfigItem.new(key: :team_name,
62
+ short_option: "-l",
63
+ env_name: "MATCH_TEAM_NAME",
64
+ description: "The name of your team if you're in multiple teams",
65
+ optional: true,
66
+ default_value: CredentialsManager::AppfileConfig.try_fetch_value(:team_name),
67
+ verify_block: proc do |value|
68
+ ENV["FASTLANE_TEAM_NAME"] = value
69
+ end),
70
+ FastlaneCore::ConfigItem.new(key: :verbose,
71
+ env_name: "MATCH_VERBOSE",
72
+ description: "Print out extra information and all commands",
73
+ is_string: false,
74
+ default_value: false,
75
+ verify_block: proc do |value|
76
+ $verbose = true if value
77
+ end),
78
+ FastlaneCore::ConfigItem.new(key: :force,
79
+ env_name: "MATCH_FORCE",
80
+ description: "Renew the provisioning profiles every time you run match",
81
+ is_string: false,
82
+ default_value: false),
83
+ FastlaneCore::ConfigItem.new(key: :workspace,
84
+ description: nil,
85
+ verify_block: proc do |value|
86
+ unless Helper.test?
87
+ raise "Specify the `git_url` instead of the `path`".red unless value.start_with?("/var/folders")
88
+ end
89
+ end,
90
+ optional: true)
91
+ ]
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,93 @@
1
+ module Match
2
+ class Runner
3
+ attr_accessor :changes_to_commit
4
+
5
+ def run(params)
6
+ FastlaneCore::PrintTable.print_values(config: params,
7
+ hide_keys: [:workspace],
8
+ title: "Summary for match #{Match::VERSION}")
9
+
10
+ params[:workspace] = GitHelper.clone(params[:git_url])
11
+ spaceship = SpaceshipEnsure.new(params[:username]) unless params[:readonly]
12
+
13
+ # Verify the App ID (as we don't want 'match' to fail at a later point)
14
+ spaceship.bundle_identifier_exists(params) if spaceship
15
+
16
+ # Certificate
17
+ cert_id = certificate(params: params)
18
+ spaceship.certificate_exists(params, cert_id) if spaceship
19
+
20
+ # Provisioning Profile
21
+ uuid = profile(params: params,
22
+ certificate_id: cert_id)
23
+ spaceship.profile_exists(params, uuid) if spaceship
24
+
25
+ # Done
26
+ if self.changes_to_commit
27
+ message = GitHelper.generate_commit_message(params)
28
+ GitHelper.commit_changes(params[:workspace], message, params[:git_url])
29
+ else
30
+ GitHelper.clear_changes
31
+ end
32
+
33
+ TablePrinter.print_summary(params, uuid)
34
+
35
+ UI.success "All required keys, certificates and provisioning profiles are installed 🙌".green
36
+ end
37
+
38
+ def certificate(params: nil)
39
+ cert_type = :distribution
40
+ cert_type = :development if params[:type] == "development"
41
+
42
+ certs = Dir[File.join(params[:workspace], "certs", cert_type.to_s, "*.cer")]
43
+ keys = Dir[File.join(params[:workspace], "certs", cert_type.to_s, "*.p12")]
44
+
45
+ if certs.count == 0 or keys.count == 0
46
+ UI.important "Couldn't find a valid code signing identity in the git repo for #{cert_type}... creating one for you now"
47
+ UI.crash!("No code signing identity found and can not create a new one because you enabled `readonly`") if params[:readonly]
48
+ cert_path = Generator.generate_certificate(params, cert_type)
49
+ self.changes_to_commit = true
50
+ else
51
+ cert_path = certs.last
52
+ UI.message "Installing certificate..."
53
+
54
+ if FastlaneCore::CertChecker.installed?(cert_path)
55
+ UI.verbose "Certificate '#{File.basename(cert_path)}' is already installed on this machine"
56
+ else
57
+ Utils.import(cert_path, params[:keychain_name])
58
+ end
59
+
60
+ # Import the private key
61
+ # there seems to be no good way to check if it's already installed - so just install it
62
+ Utils.import(keys.last, params[:keychain_name])
63
+ end
64
+
65
+ return File.basename(cert_path).gsub(".cer", "") # Certificate ID
66
+ end
67
+
68
+ def profile(params: nil, certificate_id: nil)
69
+ prov_type = params[:type].to_sym
70
+
71
+ profile_name = [prov_type.to_s, params[:app_identifier]].join("_").gsub("*", '\*') # this is important, as it shouldn't be a wildcard
72
+ profiles = Dir[File.join(params[:workspace], "profiles", prov_type.to_s, "#{profile_name}.mobileprovision")]
73
+
74
+ # Install the provisioning profiles
75
+ profile = profiles.last
76
+ if profile.nil? or params[:force]
77
+ UI.crash!("No matching provisioning profiles found and can not create a new one because you enabled `readonly`") if params[:readonly]
78
+ profile = Generator.generate_provisioning_profile(params: params,
79
+ prov_type: prov_type,
80
+ certificate_id: certificate_id)
81
+ self.changes_to_commit = true
82
+ end
83
+
84
+ FastlaneCore::ProvisioningProfile.install(profile)
85
+
86
+ parsed = FastlaneCore::ProvisioningProfile.parse(profile)
87
+ uuid = parsed["UUID"]
88
+ Utils.fill_environment(params, uuid)
89
+
90
+ return uuid
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,20 @@
1
+ module Match
2
+ class Setup
3
+ def run(path)
4
+ template = File.read("#{Helper.gem_path('match')}/lib/assets/MatchfileTemplate")
5
+
6
+ UI.important "Please create a new, private git repository"
7
+ UI.important "to store the certificates and profiles there"
8
+ url = ask("URL of the Git Repo: ")
9
+
10
+ template.gsub!("[[GIT_URL]]", url)
11
+ File.write(path, template)
12
+ UI.success "Successfully created '#{path}'. You can open the file using a code editor."
13
+
14
+ UI.important "You can now run `match development`, `match adhoc` and `fastlane appstore`"
15
+ UI.message "On the first run for each environment it will create the provisioning profiles and"
16
+ UI.message "certificates for you. From then on, it will automatically import the existing profiles."
17
+ UI.message "For more information visit https://github.com/fastlane/match"
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,64 @@
1
+ module Match
2
+ # Ensures the certificate and profiles are also available on iTunes Connect
3
+ class SpaceshipEnsure
4
+ def initialize(user)
5
+ # We'll try to manually fetch the password
6
+ # to tell the user that a password is optional
7
+ require 'credentials_manager'
8
+
9
+ keychain_entry = CredentialsManager::AccountManager.new(user: user)
10
+
11
+ if keychain_entry.password(ask_if_missing: false).to_s.length == 0
12
+ UI.important("You can also run `match` in readonly mode to not require any access to the")
13
+ UI.important("Developer Portal. This way you only share the keys and credentials")
14
+ UI.command("match --readonly")
15
+ UI.important("More information https://github.com/fastlane/match#access-control")
16
+ end
17
+
18
+ UI.message("Verifying that the certificate and profile are still valid on the Dev Portal...")
19
+ Spaceship.login(user)
20
+ Spaceship.select_team
21
+ end
22
+
23
+ def bundle_identifier_exists(params)
24
+ found = Spaceship.app.find(params[:app_identifier])
25
+ return if found
26
+
27
+ require 'sigh'
28
+ Sigh::Runner.new.print_produce_command({
29
+ username: params[:username],
30
+ app_identifier: params[:app_identifier]
31
+ })
32
+ UI.error("An app with that bundle ID needs to exist in order to create a provisioning profile for it")
33
+ UI.error("================================================================")
34
+ UI.error("Make sure to run `match` with the same user and team every time.")
35
+ UI.user_error!("Couldn't find bundle identifier '#{params[:app_identifier]}' for the user '#{params[:username]}'")
36
+ end
37
+
38
+ def certificate_exists(params, certificate_id)
39
+ found = Spaceship.certificate.all.find do |cert|
40
+ cert.id == certificate_id
41
+ end
42
+ return if found
43
+
44
+ UI.error("Certificate '#{certificate_id}' (stored in your git repo) is not available on the Developer Portal")
45
+ UI.error("for the user #{params[:username]}")
46
+ UI.error("Make sure to use the same user and team every time you run 'match' for this")
47
+ UI.error("Git repository. This might be caused by revoking the certificate on the Dev Portal")
48
+ UI.user_error!("To reset the certificates of your Apple account, you can use the `match nuke` feature, more information on https://github.com/fastlane/match")
49
+ end
50
+
51
+ def profile_exists(params, uuid)
52
+ found = Spaceship.provisioning_profile.all.find do |profile|
53
+ profile.uuid == uuid
54
+ end
55
+ return if found
56
+
57
+ UI.error("Provisioning profile '#{uuid}' is not available on the Developer Portal")
58
+ UI.error("for the user #{params[:username]}")
59
+ UI.error("Make sure to use the same user and team every time you run 'match' for this")
60
+ UI.error("Git repository. This might be caused by deleting the provisioning profile on the Dev Portal")
61
+ UI.user_error!("To reset the provisioning profiles of your Apple account, you can use the `match nuke` feature, more information on https://github.com/fastlane/match")
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,20 @@
1
+ module Match
2
+ class TablePrinter
3
+ def self.print_summary(params, uuid)
4
+ rows = []
5
+
6
+ rows << ["App Identifier", params[:app_identifier]]
7
+ rows << ["Type", params[:type]]
8
+ rows << ["UUID", uuid]
9
+ rows << ["Environment Variable", Utils.environment_variable_name(params)]
10
+
11
+ params = {}
12
+ params[:rows] = rows
13
+ params[:title] = "Installed Provisioning Profile".green
14
+
15
+ puts ""
16
+ puts Terminal::Table.new(params)
17
+ puts ""
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,24 @@
1
+ module Match
2
+ class Utils
3
+ def self.import(item_path, keychain)
4
+ command = "security import #{item_path.shellescape} -k ~/Library/Keychains/#{keychain.shellescape}"
5
+ command << " -T /usr/bin/codesign" # to not be asked for permission when running a tool like `gym`
6
+ command << " -T /usr/bin/security"
7
+ command << "&> /dev/null" # we couldn't care less about the output
8
+
9
+ Helper.backticks(command, print: $verbose)
10
+ end
11
+
12
+ # Fill in the UUID of the profiles in environment variables, much recycling
13
+ def self.fill_environment(params, uuid)
14
+ # instead we specify the UUID of the profiles
15
+ key = environment_variable_name(params)
16
+ UI.important "Setting environment variable '#{key}' to '#{uuid}'" if $verbose
17
+ ENV[key] = uuid
18
+ end
19
+
20
+ def self.environment_variable_name(params)
21
+ ["sigh", params[:app_identifier], params[:type]].join("_")
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,4 @@
1
+ module Match
2
+ VERSION = "0.1.0"
3
+ DESCRIPTION = "Easily sync your certificates and profiles across your team using git"
4
+ end
metadata ADDED
@@ -0,0 +1,303 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: match
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Felix Krause
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-12-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: security
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: fastlane_core
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.29.0
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: 1.0.0
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.29.0
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: 1.0.0
47
+ - !ruby/object:Gem::Dependency
48
+ name: credentials_manager
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 0.13.0
54
+ - - "<"
55
+ - !ruby/object:Gem::Version
56
+ version: 1.0.0
57
+ type: :runtime
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 0.13.0
64
+ - - "<"
65
+ - !ruby/object:Gem::Version
66
+ version: 1.0.0
67
+ - !ruby/object:Gem::Dependency
68
+ name: spaceship
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 0.16.0
74
+ - - "<"
75
+ - !ruby/object:Gem::Version
76
+ version: 1.0.0
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: 0.16.0
84
+ - - "<"
85
+ - !ruby/object:Gem::Version
86
+ version: 1.0.0
87
+ - !ruby/object:Gem::Dependency
88
+ name: sigh
89
+ requirement: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: 1.2.1
94
+ - - "<"
95
+ - !ruby/object:Gem::Version
96
+ version: 2.0.0
97
+ type: :runtime
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: 1.2.1
104
+ - - "<"
105
+ - !ruby/object:Gem::Version
106
+ version: 2.0.0
107
+ - !ruby/object:Gem::Dependency
108
+ name: cert
109
+ requirement: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: 1.2.7
114
+ - - "<"
115
+ - !ruby/object:Gem::Version
116
+ version: 2.0.0
117
+ type: :runtime
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: 1.2.7
124
+ - - "<"
125
+ - !ruby/object:Gem::Version
126
+ version: 2.0.0
127
+ - !ruby/object:Gem::Dependency
128
+ name: bundler
129
+ requirement: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ - !ruby/object:Gem::Dependency
142
+ name: rake
143
+ requirement: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ type: :development
149
+ prerelease: false
150
+ version_requirements: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ - !ruby/object:Gem::Dependency
156
+ name: rspec
157
+ requirement: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - "~>"
160
+ - !ruby/object:Gem::Version
161
+ version: 3.1.0
162
+ type: :development
163
+ prerelease: false
164
+ version_requirements: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - "~>"
167
+ - !ruby/object:Gem::Version
168
+ version: 3.1.0
169
+ - !ruby/object:Gem::Dependency
170
+ name: pry
171
+ requirement: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - ">="
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
176
+ type: :development
177
+ prerelease: false
178
+ version_requirements: !ruby/object:Gem::Requirement
179
+ requirements:
180
+ - - ">="
181
+ - !ruby/object:Gem::Version
182
+ version: '0'
183
+ - !ruby/object:Gem::Dependency
184
+ name: yard
185
+ requirement: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - "~>"
188
+ - !ruby/object:Gem::Version
189
+ version: 0.8.7.4
190
+ type: :development
191
+ prerelease: false
192
+ version_requirements: !ruby/object:Gem::Requirement
193
+ requirements:
194
+ - - "~>"
195
+ - !ruby/object:Gem::Version
196
+ version: 0.8.7.4
197
+ - !ruby/object:Gem::Dependency
198
+ name: webmock
199
+ requirement: !ruby/object:Gem::Requirement
200
+ requirements:
201
+ - - "~>"
202
+ - !ruby/object:Gem::Version
203
+ version: 1.19.0
204
+ type: :development
205
+ prerelease: false
206
+ version_requirements: !ruby/object:Gem::Requirement
207
+ requirements:
208
+ - - "~>"
209
+ - !ruby/object:Gem::Version
210
+ version: 1.19.0
211
+ - !ruby/object:Gem::Dependency
212
+ name: coveralls
213
+ requirement: !ruby/object:Gem::Requirement
214
+ requirements:
215
+ - - ">="
216
+ - !ruby/object:Gem::Version
217
+ version: '0'
218
+ type: :development
219
+ prerelease: false
220
+ version_requirements: !ruby/object:Gem::Requirement
221
+ requirements:
222
+ - - ">="
223
+ - !ruby/object:Gem::Version
224
+ version: '0'
225
+ - !ruby/object:Gem::Dependency
226
+ name: fastlane
227
+ requirement: !ruby/object:Gem::Requirement
228
+ requirements:
229
+ - - ">="
230
+ - !ruby/object:Gem::Version
231
+ version: '0'
232
+ type: :development
233
+ prerelease: false
234
+ version_requirements: !ruby/object:Gem::Requirement
235
+ requirements:
236
+ - - ">="
237
+ - !ruby/object:Gem::Version
238
+ version: '0'
239
+ - !ruby/object:Gem::Dependency
240
+ name: rubocop
241
+ requirement: !ruby/object:Gem::Requirement
242
+ requirements:
243
+ - - "~>"
244
+ - !ruby/object:Gem::Version
245
+ version: '0.34'
246
+ type: :development
247
+ prerelease: false
248
+ version_requirements: !ruby/object:Gem::Requirement
249
+ requirements:
250
+ - - "~>"
251
+ - !ruby/object:Gem::Version
252
+ version: '0.34'
253
+ description: Easily sync your certificates and profiles across your team using git
254
+ email:
255
+ - match@krausefx.com
256
+ executables:
257
+ - match
258
+ extensions: []
259
+ extra_rdoc_files: []
260
+ files:
261
+ - LICENSE
262
+ - README.md
263
+ - bin/match
264
+ - lib/assets/MatchfileTemplate
265
+ - lib/assets/READMETemplate.md
266
+ - lib/match.rb
267
+ - lib/match/encrypt.rb
268
+ - lib/match/generator.rb
269
+ - lib/match/git_helper.rb
270
+ - lib/match/nuke.rb
271
+ - lib/match/options.rb
272
+ - lib/match/runner.rb
273
+ - lib/match/setup.rb
274
+ - lib/match/spaceship_ensure.rb
275
+ - lib/match/table_printer.rb
276
+ - lib/match/utils.rb
277
+ - lib/match/version.rb
278
+ homepage: https://fastlane.tools
279
+ licenses:
280
+ - MIT
281
+ metadata: {}
282
+ post_install_message:
283
+ rdoc_options: []
284
+ require_paths:
285
+ - lib
286
+ required_ruby_version: !ruby/object:Gem::Requirement
287
+ requirements:
288
+ - - ">="
289
+ - !ruby/object:Gem::Version
290
+ version: 2.0.0
291
+ required_rubygems_version: !ruby/object:Gem::Requirement
292
+ requirements:
293
+ - - ">="
294
+ - !ruby/object:Gem::Version
295
+ version: '0'
296
+ requirements: []
297
+ rubyforge_project:
298
+ rubygems_version: 2.4.0
299
+ signing_key:
300
+ specification_version: 4
301
+ summary: Easily sync your certificates and profiles across your team using git
302
+ test_files: []
303
+ has_rdoc: