mysigner 0.1.2 → 0.1.4

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/.githooks/pre-commit +15 -0
  3. data/.githooks/pre-push +21 -0
  4. data/.github/workflows/ci.yml +29 -0
  5. data/.gitignore +4 -0
  6. data/.rubocop.yml +55 -0
  7. data/.rubocop_todo.yml +126 -0
  8. data/CHANGELOG.md +96 -0
  9. data/Gemfile +5 -3
  10. data/Gemfile.lock +38 -8
  11. data/README.md +14 -16
  12. data/Rakefile +5 -3
  13. data/bin/console +4 -3
  14. data/bin/setup +3 -0
  15. data/certificate_.cer +0 -0
  16. data/exe/mysigner +19 -2
  17. data/iOS_App_Store_Profile.mobileprovision +1 -0
  18. data/iOS_Distribution_Certificate.cer +1 -0
  19. data/lib/mysigner/build/android_executor.rb +83 -63
  20. data/lib/mysigner/build/android_parser.rb +33 -40
  21. data/lib/mysigner/build/configurator.rb +17 -16
  22. data/lib/mysigner/build/detector.rb +39 -50
  23. data/lib/mysigner/build/error_analyzer.rb +70 -68
  24. data/lib/mysigner/build/executor.rb +30 -37
  25. data/lib/mysigner/build/parser.rb +18 -18
  26. data/lib/mysigner/cleanup/private_keys_purger.rb +41 -0
  27. data/lib/mysigner/cli/auth_commands.rb +771 -764
  28. data/lib/mysigner/cli/build_commands.rb +962 -796
  29. data/lib/mysigner/cli/concerns/actionable_suggestions.rb +208 -154
  30. data/lib/mysigner/cli/concerns/api_helpers.rb +46 -54
  31. data/lib/mysigner/cli/concerns/error_handlers.rb +247 -237
  32. data/lib/mysigner/cli/concerns/helpers.rb +44 -1
  33. data/lib/mysigner/cli/diagnostic_commands.rb +667 -636
  34. data/lib/mysigner/cli/resource_commands.rb +1153 -985
  35. data/lib/mysigner/cli/validate_commands.rb +25 -25
  36. data/lib/mysigner/cli.rb +11 -1
  37. data/lib/mysigner/client.rb +27 -19
  38. data/lib/mysigner/config.rb +161 -60
  39. data/lib/mysigner/export/exporter.rb +38 -37
  40. data/lib/mysigner/signing/certificate_checker.rb +18 -23
  41. data/lib/mysigner/signing/gradle_signing_injector.rb +67 -0
  42. data/lib/mysigner/signing/keystore_manager.rb +81 -61
  43. data/lib/mysigner/signing/validator.rb +38 -40
  44. data/lib/mysigner/signing/wizard.rb +329 -342
  45. data/lib/mysigner/upload/app_store_automation.rb +96 -49
  46. data/lib/mysigner/upload/app_store_submission.rb +87 -92
  47. data/lib/mysigner/upload/asc_rest_uploader.rb +119 -0
  48. data/lib/mysigner/upload/play_store_uploader.rb +164 -144
  49. data/lib/mysigner/upload/uploader.rb +136 -115
  50. data/lib/mysigner/version.rb +3 -1
  51. data/lib/mysigner.rb +13 -11
  52. data/mysigner.gemspec +36 -33
  53. data/profile_.mobileprovision +0 -0
  54. data/test_manual.rb +37 -36
  55. metadata +44 -17
  56. data/.DS_Store +0 -0
@@ -1,5 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
1
4
  require 'fileutils'
2
5
  require 'json'
6
+ require 'tmpdir'
3
7
 
4
8
  module Mysigner
5
9
  module Upload
@@ -11,15 +15,39 @@ module Mysigner
11
15
  TRANSPORTER_PATHS = [
12
16
  '/Applications/Xcode.app/Contents/Developer/usr/bin/iTMSTransporter', # Bundled with Xcode (primary)
13
17
  '/Applications/Transporter.app/Contents/itms/bin/iTMSTransporter', # Standalone Transporter app
14
- '/usr/local/itms/bin/iTMSTransporter' # Custom installation
18
+ '/usr/local/itms/bin/iTMSTransporter' # Custom installation
15
19
  ].freeze
16
20
 
21
+ # Parses CFBundleVersion + CFBundleShortVersionString from an .ipa
22
+ # (reads Info.plist from the Payload/*.app/ inside the zip).
23
+ # Used by the new ASC REST upload flow in build_commands.rb.
24
+ def self.extract_ipa_info(ipa_path)
25
+ require 'open3'
26
+ result = { cf_bundle_version: nil, cf_bundle_short_version_string: nil, bundle_id: nil }
27
+ Dir.mktmpdir('mysigner-ipa-inspect-') do |tmp|
28
+ plist_path = File.join(tmp, 'Info.plist')
29
+ zip_out, status = Open3.capture2e('unzip', '-p', ipa_path, 'Payload/*.app/Info.plist')
30
+ return result unless status.success? && !zip_out.empty?
31
+
32
+ File.binwrite(plist_path, zip_out)
33
+ xml_out, xml_status = Open3.capture2e('plutil', '-convert', 'xml1', '-o', '-', plist_path)
34
+ return result unless xml_status.success?
35
+
36
+ result[:cf_bundle_version] = xml_out[%r{<key>CFBundleVersion</key>\s*<string>([^<]+)</string>}, 1]
37
+ result[:cf_bundle_short_version_string] = xml_out[%r{<key>CFBundleShortVersionString</key>\s*<string>([^<]+)</string>}, 1]
38
+ result[:bundle_id] = xml_out[%r{<key>CFBundleIdentifier</key>\s*<string>([^<]+)</string>}, 1]
39
+ end
40
+ result
41
+ rescue StandardError
42
+ { cf_bundle_version: nil, cf_bundle_short_version_string: nil, bundle_id: nil }
43
+ end
44
+
17
45
  def initialize(ipa_path, api_key:, api_issuer:, private_key:)
18
46
  @ipa_path = File.expand_path(ipa_path)
19
47
  @api_key = api_key
20
48
  @api_issuer = api_issuer
21
49
  @private_key = private_key
22
-
50
+
23
51
  validate_ipa!
24
52
  detect_transporter!
25
53
  setup_private_key!
@@ -27,94 +55,90 @@ module Mysigner
27
55
 
28
56
  def upload!(wait_for_processing: false)
29
57
  say_uploading
30
-
58
+
31
59
  begin
32
60
  # Validate IPA first
33
61
  validate_result = validate_ipa
34
- unless validate_result[:success]
35
- raise UploadError, "IPA validation failed: #{validate_result[:error]}"
36
- end
37
-
62
+ raise UploadError, "IPA validation failed: #{validate_result[:error]}" unless validate_result[:success]
63
+
38
64
  # Upload to App Store Connect
39
65
  upload_result = upload_ipa
40
- unless upload_result[:success]
41
- raise UploadError, "Upload failed: #{upload_result[:error]}"
42
- end
43
-
66
+ raise UploadError, "Upload failed: #{upload_result[:error]}" unless upload_result[:success]
67
+
44
68
  say_success
45
-
69
+
46
70
  # Optionally wait for processing
47
71
  if wait_for_processing
48
72
  say_waiting_for_processing
49
- # Note: Build processing status is polled via the dashboard's sync API
73
+ # NOTE: Build processing status is polled via the dashboard's sync API
50
74
  # in build_commands.rb, not directly here. This flag is reserved for
51
75
  # future use or manual uploads outside the `ship` command flow.
52
76
  end
53
-
77
+
54
78
  {
55
79
  success: true,
56
- message: "Upload completed successfully"
80
+ message: 'Upload completed successfully'
57
81
  }
58
- rescue => e
82
+ rescue StandardError => e
59
83
  raise UploadError, "Upload failed: #{e.message}"
84
+ ensure
85
+ cleanup_private_key!
60
86
  end
61
87
  end
62
88
 
63
89
  private
64
90
 
65
91
  def validate_ipa!
66
- unless File.exist?(@ipa_path)
67
- raise UploadError, "IPA file not found: #{@ipa_path}"
68
- end
69
-
70
- unless @ipa_path.end_with?('.ipa')
71
- raise UploadError, "Invalid file type: #{@ipa_path} (must be .ipa)"
72
- end
73
-
92
+ raise UploadError, "IPA file not found: #{@ipa_path}" unless File.exist?(@ipa_path)
93
+
94
+ raise UploadError, "Invalid file type: #{@ipa_path} (must be .ipa)" unless @ipa_path.end_with?('.ipa')
95
+
74
96
  # Check file size (should be at least 10KB to catch truly corrupt files)
75
97
  file_size = File.size(@ipa_path)
76
- if file_size < 10_000
77
- raise UploadError, "IPA file seems too small: #{file_size} bytes (possible corruption)"
78
- end
98
+ return unless file_size < 10_000
99
+
100
+ raise UploadError, "IPA file seems too small: #{file_size} bytes (possible corruption)"
79
101
  end
80
102
 
81
103
  def detect_transporter!
82
104
  # We primarily use xcrun altool, but check for iTMSTransporter as fallback
83
105
  @transporter_path = TRANSPORTER_PATHS.find { |path| File.exist?(path) }
84
-
106
+
85
107
  # Even if iTMSTransporter is not found, xcrun altool should work
86
108
  # This is just for informational purposes
87
109
  @using_altool = true
88
110
  end
89
111
 
90
112
  def setup_private_key!
91
- # iTMSTransporter looks for .p8 files in these locations:
92
- # - ./private_keys/AuthKey_<KEY_ID>.p8
93
- # - ~/private_keys/AuthKey_<KEY_ID>.p8
94
- # - ~/.private_keys/AuthKey_<KEY_ID>.p8
95
- # - ~/.appstoreconnect/private_keys/AuthKey_<KEY_ID>.p8
96
-
97
- @private_keys_dir = File.expand_path("~/.private_keys")
98
- FileUtils.mkdir_p(@private_keys_dir)
99
-
113
+ # Phase 0: write the .p8 to a per-run tempdir (0600) and point altool
114
+ # there via API_PRIVATE_KEYS_DIR. #cleanup_private_key! in the upload!
115
+ # ensure block removes the tempdir, so the key never persists across
116
+ # invocations. Replaces the old ~/.private_keys/ persistent write.
117
+ @private_keys_dir = Dir.mktmpdir('mysigner-p8-')
100
118
  @private_key_path = File.join(@private_keys_dir, "AuthKey_#{@api_key}.p8")
101
-
102
- # Write the private key to disk
103
119
  File.write(@private_key_path, @private_key)
104
- File.chmod(0600, @private_key_path) # Secure permissions
120
+ File.chmod(0o600, @private_key_path)
121
+ ENV['API_PRIVATE_KEYS_DIR'] = @private_keys_dir
122
+ end
123
+
124
+ def cleanup_private_key!
125
+ FileUtils.rm_rf(@private_keys_dir) if @private_keys_dir && Dir.exist?(@private_keys_dir)
126
+ ENV.delete('API_PRIVATE_KEYS_DIR')
127
+ rescue StandardError
128
+ # Best-effort cleanup; never raise from ensure.
105
129
  end
106
130
 
107
131
  def validate_ipa
108
- puts "📋 Validating IPA..."
109
- puts ""
110
-
132
+ puts '📋 Validating IPA...'
133
+ puts ''
134
+
111
135
  # Try altool first (simpler, works today)
112
136
  if altool_available?
113
137
  validate_with_altool
114
138
  elsif @transporter_path
115
139
  validate_with_transporter
116
140
  else
117
- puts "⚠️ Skipping validation (no tools available)"
141
+ puts '⚠️ Skipping validation (no tools available)'
118
142
  { success: true } # Continue anyway
119
143
  end
120
144
  end
@@ -128,12 +152,12 @@ module Mysigner
128
152
  '--apiKey', @api_key,
129
153
  '--apiIssuer', @api_issuer
130
154
  ].join(' ')
131
-
155
+
132
156
  output = `#{cmd} 2>&1`
133
- success = $?.success?
134
-
157
+ success = $CHILD_STATUS.success?
158
+
135
159
  if success
136
- puts "✓ Validation passed (altool)"
160
+ puts '✓ Validation passed (altool)'
137
161
  { success: true }
138
162
  else
139
163
  error_message = extract_error_from_output(output)
@@ -151,14 +175,14 @@ module Mysigner
151
175
  '-f', @ipa_path,
152
176
  '-apiKey', @api_key,
153
177
  '-apiIssuer', @api_issuer,
154
- '-t', 'Signiant' # Transport mode
178
+ '-t', 'Signiant' # Transport mode
155
179
  ].join(' ')
156
-
180
+
157
181
  output = `#{cmd} 2>&1`
158
- success = $?.success?
159
-
182
+ success = $CHILD_STATUS.success?
183
+
160
184
  if success
161
- puts "✓ Validation passed (iTMSTransporter)"
185
+ puts '✓ Validation passed (iTMSTransporter)'
162
186
  { success: true }
163
187
  else
164
188
  error_message = extract_error_from_output(output)
@@ -168,24 +192,24 @@ module Mysigner
168
192
  end
169
193
 
170
194
  def upload_ipa
171
- puts ""
172
- puts "☁️ Uploading to App Store Connect..."
173
- puts ""
174
-
195
+ puts ''
196
+ puts '☁️ Uploading to App Store Connect...'
197
+ puts ''
198
+
175
199
  # Try altool first, fall back to iTMSTransporter
176
200
  if altool_available?
177
201
  upload_with_altool
178
202
  elsif @transporter_path
179
203
  upload_with_transporter
180
204
  else
181
- raise UploadError, "No upload tool available. Please ensure Xcode is installed."
205
+ raise UploadError, 'No upload tool available. Please ensure Xcode is installed.'
182
206
  end
183
207
  end
184
208
 
185
209
  def upload_with_altool
186
- puts "Using: xcrun altool"
187
- puts ""
188
-
210
+ puts 'Using: xcrun altool'
211
+ puts ''
212
+
189
213
  cmd = [
190
214
  'xcrun', 'altool',
191
215
  '--upload-app',
@@ -194,40 +218,38 @@ module Mysigner
194
218
  '--apiKey', @api_key,
195
219
  '--apiIssuer', @api_issuer
196
220
  ].join(' ')
197
-
221
+
198
222
  # Capture full output for error detection
199
223
  output = []
200
224
  has_errors = false
201
-
225
+
202
226
  # Run with live output
203
- IO.popen(cmd, err: [:child, :out]) do |io|
227
+ IO.popen(cmd, err: %i[child out]) do |io|
204
228
  io.each_line do |line|
205
229
  output << line
206
230
  next if line.strip.empty?
207
-
231
+
208
232
  # Detect errors
209
- if line.include?('ERROR') || line.include?('UPLOAD FAILED')
210
- has_errors = true
211
- end
212
-
233
+ has_errors = true if line.include?('ERROR') || line.include?('UPLOAD FAILED')
234
+
213
235
  # Show progress indicators
214
- if line.include?('Uploading') || line.include?('Processing') ||
236
+ if line.include?('Uploading') || line.include?('Processing') ||
215
237
  line.include?('Verifying') || line.include?('Package')
216
238
  print '.'
217
239
  elsif line.include?('error') || line.include?('ERROR')
218
- puts ""
240
+ puts ''
219
241
  puts line
220
242
  elsif line.include?('SUCCESS') || line.include?('No errors')
221
- puts ""
243
+ puts ''
222
244
  puts line
223
245
  end
224
246
  end
225
247
  end
226
-
227
- puts "" # New line after progress dots
228
-
248
+
249
+ puts '' # New line after progress dots
250
+
229
251
  # Check both exit code and output for errors
230
- if $?.success? && !has_errors
252
+ if $CHILD_STATUS.success? && !has_errors
231
253
  { success: true }
232
254
  else
233
255
  error_msg = extract_error_from_output(output.join("\n"))
@@ -236,9 +258,9 @@ module Mysigner
236
258
  end
237
259
 
238
260
  def upload_with_transporter
239
- puts "Using: iTMSTransporter (future-proof)"
240
- puts ""
241
-
261
+ puts 'Using: iTMSTransporter (future-proof)'
262
+ puts ''
263
+
242
264
  # iTMSTransporter upload mode
243
265
  # According to https://help.apple.com/itc/transporteruserguide/en.lproj/static.html
244
266
  cmd = [
@@ -247,34 +269,34 @@ module Mysigner
247
269
  '-f', @ipa_path,
248
270
  '-apiKey', @api_key,
249
271
  '-apiIssuer', @api_issuer,
250
- '-t', 'Signiant' # Transport mode (can also use 'Aspera' or 'DAV')
272
+ '-t', 'Signiant' # Transport mode (can also use 'Aspera' or 'DAV')
251
273
  ].join(' ')
252
-
274
+
253
275
  # Capture output
254
276
  output = []
255
-
256
- IO.popen(cmd, err: [:child, :out]) do |io|
277
+
278
+ IO.popen(cmd, err: %i[child out]) do |io|
257
279
  io.each_line do |line|
258
280
  output << line
259
281
  next if line.strip.empty?
260
-
282
+
261
283
  # Show progress
262
- if line.include?('Uploading') || line.include?('Processing') ||
284
+ if line.include?('Uploading') || line.include?('Processing') ||
263
285
  line.include?('Verifying')
264
286
  print '.'
265
287
  elsif line.include?('ERROR')
266
- puts ""
288
+ puts ''
267
289
  puts line
268
290
  elsif line.include?('SUCCESS')
269
- puts ""
291
+ puts ''
270
292
  puts line
271
293
  end
272
294
  end
273
295
  end
274
-
275
- puts ""
276
-
277
- if $?.success?
296
+
297
+ puts ''
298
+
299
+ if $CHILD_STATUS.success?
278
300
  { success: true }
279
301
  else
280
302
  error_msg = extract_error_from_output(output.join("\n"))
@@ -292,13 +314,13 @@ module Mysigner
292
314
  error_lines = output.lines.select do |l|
293
315
  l.include?('ERROR') || l.include?('error') || l.include?('Invalid')
294
316
  end
295
-
317
+
296
318
  if error_lines.any?
297
319
  # Clean up and join error messages
298
320
  cleaned_errors = error_lines.map(&:strip)
299
- .reject { |l| l.empty? || l.start_with?('code :') || l.start_with?('iris-code') }
300
- .join("\n")
301
-
321
+ .reject { |l| l.empty? || l.start_with?('code :') || l.start_with?('iris-code') }
322
+ .join("\n")
323
+
302
324
  # Add helpful context for common errors
303
325
  if output.include?('Cannot determine the Apple ID from Bundle ID')
304
326
  cleaned_errors += "\n\n💡 This usually means:\n"
@@ -318,44 +340,44 @@ module Mysigner
318
340
  cleaned_errors += " 3. Add icon images for all required sizes\n"
319
341
  cleaned_errors += " 4. Or use a tool like https://appicon.co to generate all sizes\n"
320
342
  end
321
-
343
+
322
344
  cleaned_errors
323
345
  else
324
- "Unknown error"
346
+ 'Unknown error'
325
347
  end
326
348
  end
327
349
 
328
350
  def say_uploading
329
- puts "☁️ Uploading to TestFlight..."
330
- puts ""
351
+ puts '☁️ Uploading to TestFlight...'
352
+ puts ''
331
353
  puts "IPA: #{File.basename(@ipa_path)}"
332
354
  puts "Size: #{format_bytes(File.size(@ipa_path))}"
333
-
355
+
334
356
  # Show which tool will be used
335
357
  if altool_available?
336
- puts "Tool: xcrun altool"
358
+ puts 'Tool: xcrun altool'
337
359
  elsif @transporter_path
338
- puts "Tool: iTMSTransporter (future-proof)"
360
+ puts 'Tool: iTMSTransporter (future-proof)'
339
361
  else
340
- puts "Tool: Auto-detect"
362
+ puts 'Tool: Auto-detect'
341
363
  end
342
- puts ""
364
+ puts ''
343
365
  end
344
366
 
345
367
  def say_success
346
- puts ""
347
- puts "=" * 80
348
- puts "✓ Upload succeeded!"
349
- puts "=" * 80
350
- puts ""
351
- puts "🎉 Your build is now processing in App Store Connect"
352
- puts ""
368
+ puts ''
369
+ puts '=' * 80
370
+ puts '✓ Upload succeeded!'
371
+ puts '=' * 80
372
+ puts ''
373
+ puts '🎉 Your build is now processing in App Store Connect'
374
+ puts ''
353
375
  end
354
376
 
355
377
  def say_waiting_for_processing
356
- puts "⏳ Waiting for App Store Connect to process the build..."
357
- puts "This may take 5-15 minutes..."
358
- puts ""
378
+ puts '⏳ Waiting for App Store Connect to process the build...'
379
+ puts 'This may take 5-15 minutes...'
380
+ puts ''
359
381
  end
360
382
 
361
383
  def format_bytes(bytes)
@@ -370,4 +392,3 @@ module Mysigner
370
392
  end
371
393
  end
372
394
  end
373
-
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Mysigner
2
- VERSION = "0.1.2"
4
+ VERSION = '0.1.4'
3
5
  end
data/lib/mysigner.rb CHANGED
@@ -1,15 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Mysigner
2
4
  class Error < StandardError; end
3
5
  end
4
6
 
5
- require "mysigner/version"
6
- require "mysigner/config"
7
- require "mysigner/client"
8
- require "mysigner/build/detector"
9
- require "mysigner/build/parser"
10
- require "mysigner/build/configurator"
11
- require "mysigner/build/executor"
12
- require "mysigner/signing/validator"
13
- require "mysigner/export/exporter"
14
- require "mysigner/upload/uploader"
15
- require "mysigner/cli"
7
+ require 'mysigner/version'
8
+ require 'mysigner/config'
9
+ require 'mysigner/client'
10
+ require 'mysigner/build/detector'
11
+ require 'mysigner/build/parser'
12
+ require 'mysigner/build/configurator'
13
+ require 'mysigner/build/executor'
14
+ require 'mysigner/signing/validator'
15
+ require 'mysigner/export/exporter'
16
+ require 'mysigner/upload/uploader'
17
+ require 'mysigner/cli'
data/mysigner.gemspec CHANGED
@@ -1,32 +1,34 @@
1
+ # frozen_string_literal: true
1
2
 
2
- lib = File.expand_path("../lib", __FILE__)
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "mysigner/version"
5
+ require 'mysigner/version'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = "mysigner"
8
+ spec.name = 'mysigner'
8
9
  spec.version = Mysigner::VERSION
9
- spec.authors = ["Jurgen Leka"]
10
- spec.email = ["lekacoding@gmail.com"]
10
+ spec.authors = ['Jurgen Leka']
11
+ spec.email = ['lekacoding@gmail.com']
11
12
 
12
- spec.summary = %q{CLI tool for iOS and Android code signing automation via My Signer API}
13
- spec.description = %q{Command-line interface for managing iOS certificates, devices, provisioning profiles, and Android keystores. Build, sign, and upload to App Store Connect and Google Play with simple commands like 'mysigner ship testflight' and 'mysigner ship internal --platform android'.}
14
- spec.homepage = "https://mysigner.dev"
15
- spec.license = "Apache-2.0"
13
+ spec.summary = 'CLI tool for iOS and Android code signing automation via My Signer API'
14
+ spec.description = "Command-line interface for managing iOS certificates, devices, provisioning profiles, and Android keystores. Build, sign, and upload to App Store Connect and Google Play with simple commands like 'mysigner ship testflight' and 'mysigner ship internal --platform android'."
15
+ spec.homepage = 'https://mysigner.dev'
16
+ spec.license = 'Apache-2.0'
16
17
 
17
- spec.required_ruby_version = ">= 3.2.0"
18
+ spec.required_ruby_version = '>= 3.2.0'
18
19
 
19
20
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
20
21
  # to allow pushing to a single host or delete this section to allow pushing to any host.
21
22
  if spec.respond_to?(:metadata)
22
- spec.metadata["allowed_push_host"] = "https://rubygems.org"
23
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
23
24
 
24
- spec.metadata["homepage_uri"] = spec.homepage
25
- spec.metadata["changelog_uri"] = "https://mysigner.dev/docs/changelog"
26
- spec.metadata["documentation_uri"] = "https://mysigner.dev/docs/commands"
25
+ spec.metadata['homepage_uri'] = spec.homepage
26
+ spec.metadata['changelog_uri'] = 'https://mysigner.dev/docs/changelog'
27
+ spec.metadata['documentation_uri'] = 'https://mysigner.dev/docs/commands'
28
+ spec.metadata['rubygems_mfa_required'] = 'true'
27
29
  else
28
- raise "RubyGems 2.0 or newer is required to protect against " \
29
- "public gem pushes."
30
+ raise 'RubyGems 2.0 or newer is required to protect against ' \
31
+ 'public gem pushes.'
30
32
  end
31
33
 
32
34
  spec.post_install_message = <<~MSG
@@ -50,29 +52,30 @@ Gem::Specification.new do |spec|
50
52
 
51
53
  # Specify which files should be added to the gem when it is released.
52
54
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
53
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
55
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
54
56
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
55
57
  end
56
- spec.bindir = "exe"
58
+ spec.bindir = 'exe'
57
59
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
58
- spec.require_paths = ["lib"]
60
+ spec.require_paths = ['lib']
59
61
 
60
62
  # Runtime dependencies
61
- spec.add_runtime_dependency "thor", "~> 1.4"
62
- spec.add_runtime_dependency "faraday", "~> 2.14"
63
- spec.add_runtime_dependency "faraday-retry", "~> 2.2"
64
- spec.add_runtime_dependency "reline", "~> 0.5"
65
- spec.add_runtime_dependency "base64", "~> 0.2"
66
- spec.add_runtime_dependency "xcodeproj", "~> 1.27"
67
- spec.add_runtime_dependency "plist", "~> 3.7"
68
-
63
+ spec.add_dependency 'base64', '~> 0.2'
64
+ spec.add_dependency 'faraday', '~> 2.14'
65
+ spec.add_dependency 'faraday-retry', '~> 2.2'
66
+ spec.add_dependency 'plist', '~> 3.7'
67
+ spec.add_dependency 'reline', '~> 0.5'
68
+ spec.add_dependency 'thor', '~> 1.4'
69
+ spec.add_dependency 'xcodeproj', '~> 1.27'
70
+
69
71
  # Android/Google Play dependencies
70
- spec.add_runtime_dependency "google-apis-androidpublisher_v3", "~> 0.54"
71
- spec.add_runtime_dependency "googleauth", "~> 1.11"
72
+ spec.add_dependency 'google-apis-androidpublisher_v3', '~> 0.54'
73
+ spec.add_dependency 'googleauth', '~> 1.11'
72
74
 
73
75
  # Development dependencies
74
- spec.add_development_dependency "bundler", "~> 2.5"
75
- spec.add_development_dependency "rake", "~> 13.0"
76
- spec.add_development_dependency "rspec", "~> 3.0"
77
- spec.add_development_dependency "webmock", "~> 3.24"
76
+ spec.add_development_dependency 'bundler', '~> 2.5'
77
+ spec.add_development_dependency 'rake', '~> 13.0'
78
+ spec.add_development_dependency 'rspec', '~> 3.0'
79
+ spec.add_development_dependency 'rubocop', '~> 1.79'
80
+ spec.add_development_dependency 'webmock', '~> 3.24'
78
81
  end
File without changes