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,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Mysigner
2
4
  class CLI < Thor
3
5
  module Concerns
@@ -6,141 +8,144 @@ module Mysigner
6
8
  def show_token_guidance(api_url)
7
9
  say "Don't have a token yet?", :yellow
8
10
  say " 1. Go to: #{api_url}", :cyan
9
- say " 2. Navigate to: Your Organization → API Tokens", :cyan
11
+ say ' 2. Navigate to: Your Organization → API Tokens', :cyan
10
12
  say " 3. Click 'Create Token'", :cyan
11
13
  say " 4. Copy the token (you'll only see it once!)", :cyan
12
- say ""
14
+ say ''
13
15
  say "💡 Or run 'mysigner onboard' for step-by-step guidance", :yellow
14
- say ""
16
+ say ''
15
17
  end
16
18
 
17
19
  # Handle connection failure
18
20
  def handle_connection_failure(api_url)
19
- say ""
20
- say "Possible issues:", :yellow
21
+ say ''
22
+ say 'Possible issues:', :yellow
21
23
  say " • API server is not running at #{api_url}"
22
- say " • Network connectivity problems"
23
- say " • Incorrect API URL"
24
- say ""
25
- say "💡 Try:", :cyan
26
- say " • Check the API URL is correct"
27
- say " • Verify the server is running"
24
+ say ' • Network connectivity problems'
25
+ say ' • Incorrect API URL'
26
+ say ''
27
+ say '💡 Try:', :cyan
28
+ say ' • Check the API URL is correct'
29
+ say ' • Verify the server is running'
28
30
  say " • Run 'mysigner onboard' for guided setup"
29
31
  end
30
32
 
31
33
  # Show guidance for creating an organization
32
34
  def show_create_org_guidance(api_url)
33
- say "To create an organization:", :cyan
35
+ say 'To create an organization:', :cyan
34
36
  say " 1. Go to: #{api_url}"
35
- say " 2. Sign in to your account"
37
+ say ' 2. Sign in to your account'
36
38
  say " 3. Click 'Create Organization'"
37
- say " 4. Then generate a new API token for that organization"
38
- say ""
39
+ say ' 4. Then generate a new API token for that organization'
40
+ say ''
39
41
  say "💡 Run 'mysigner onboard' for step-by-step guidance", :yellow
40
42
  end
41
43
 
42
44
  # Handle unauthorized error
43
45
  def handle_unauthorized_error(api_url)
44
- say ""
45
- say "=" * 80, :red
46
- say "✗ Authentication Failed", :red
47
- say "=" * 80, :red
48
- say ""
49
- say "Your API token is invalid or has been revoked.", :bold
50
- say ""
51
- say "Common reasons:", :yellow
52
- say " • Token was copied incorrectly (missing characters)"
53
- say " • Token was revoked in the web dashboard"
54
- say " • Token has expired"
46
+ say ''
47
+ say '=' * 80, :red
48
+ say '✗ Authentication Failed', :red
49
+ say '=' * 80, :red
50
+ say ''
51
+ say 'Your API token is invalid or has been revoked.', :bold
52
+ say ''
53
+ say 'Common reasons:', :yellow
54
+ say ' • Token was copied incorrectly (missing characters)'
55
+ say ' • Token was revoked in the web dashboard'
56
+ say ' • Token has expired'
55
57
  say " • You're using the wrong API URL"
56
- say ""
57
- say "To fix this:", :cyan
58
+ say ''
59
+ say 'To fix this:', :cyan
58
60
  say " 1. Go to: #{api_url}/organizations/YOUR_ORG/api_tokens"
59
- say " 2. Check if your token is still active"
60
- say " 3. If revoked or expired, create a new token"
61
- say " 4. Copy the NEW token carefully (entire string)"
61
+ say ' 2. Check if your token is still active'
62
+ say ' 3. If revoked or expired, create a new token'
63
+ say ' 4. Copy the NEW token carefully (entire string)'
62
64
  say " 5. Run 'mysigner login' again"
63
- say ""
65
+ say ''
64
66
  say "💡 Or run 'mysigner onboard' for guided setup", :yellow
65
- say ""
67
+ say ''
66
68
  end
67
69
 
68
70
  # Handle connection error
69
71
  def handle_connection_error(error, api_url)
70
- say ""
71
- say "=" * 80, :red
72
- say "✗ Connection Failed", :red
73
- say "=" * 80, :red
74
- say ""
72
+ say ''
73
+ say '=' * 80, :red
74
+ say '✗ Connection Failed', :red
75
+ say '=' * 80, :red
76
+ say ''
75
77
  say "Error: #{error.message}", :red
76
- say ""
77
- say "Possible causes:", :yellow
78
+ say ''
79
+ say 'Possible causes:', :yellow
78
80
  say " • My Signer API is not running at #{api_url}"
79
- say " • Network connectivity issues"
80
- say " • Firewall blocking the connection"
81
- say " • Incorrect API URL"
82
- say ""
83
- say "To fix this:", :cyan
84
- say ""
81
+ say ' • Network connectivity issues'
82
+ say ' • Firewall blocking the connection'
83
+ say ' • Incorrect API URL'
84
+ say ''
85
+ say 'To fix this:', :cyan
86
+ say ''
85
87
  if api_url.include?('localhost')
86
- say " For local development:", :bold
87
- say " 1. Make sure Rails server is running:"
88
- say " cd path/to/my-signer"
89
- say " bin/rails server"
90
- say ""
88
+ say ' For local development:', :bold
89
+ say ' 1. Make sure Rails server is running:'
90
+ say ' cd path/to/my-signer'
91
+ say ' bin/rails server'
92
+ say ''
91
93
  say " 2. Verify it's accessible:"
92
94
  say " curl #{api_url}/up"
93
- say ""
94
95
  else
95
- say " For production:", :bold
96
- say " 1. Check the API URL is correct"
97
- say " 2. Verify the service is running"
98
- say " 3. Check your internet connection"
99
- say ""
96
+ say ' For production:', :bold
97
+ say ' 1. Check the API URL is correct'
98
+ say ' 2. Verify the service is running'
99
+ say ' 3. Check your internet connection'
100
100
  end
101
- say " Or set a custom API URL:", :bold
102
- say " export MYSIGNER_API_URL=http://your-server.com"
103
- say ""
101
+ say ''
102
+ say ' Or set a custom API URL:', :bold
103
+ say ' export MYSIGNER_API_URL=http://your-server.com'
104
+ say ''
104
105
  say "💡 Run 'mysigner onboard' to reconfigure", :yellow
105
- say ""
106
+ say ''
106
107
  end
107
108
 
108
109
  # Handle unexpected error
109
110
  def handle_unexpected_error(error, api_url)
110
- say ""
111
- say "=" * 80, :red
112
- say "✗ Unexpected Error", :red
113
- say "=" * 80, :red
114
- say ""
111
+ say ''
112
+ say '=' * 80, :red
113
+ say '✗ Unexpected Error', :red
114
+ say '=' * 80, :red
115
+ say ''
115
116
  say "Error: #{error.message}", :red
116
117
  say "Type: #{error.class}", :red if ENV['DEBUG']
117
- say ""
118
- say "This is unexpected. Please try:", :yellow
118
+ say ''
119
+ say 'This is unexpected. Please try:', :yellow
119
120
  say " 1. Run 'mysigner onboard' to reconfigure"
120
121
  say " 2. Check #{api_url} is accessible"
121
122
  say " 3. Run 'mysigner doctor' to check your environment"
122
- say ""
123
+ say ''
123
124
  if ENV['DEBUG']
124
- say "Stack trace:", :red
125
+ say 'Stack trace:', :red
125
126
  say error.backtrace.first(5).join("\n"), :red
126
- say ""
127
127
  else
128
- say "💡 For more details, run with DEBUG=1", :yellow
129
- say ""
128
+ say '💡 For more details, run with DEBUG=1', :yellow
130
129
  end
130
+ say ''
131
131
  end
132
132
 
133
133
  # Handle Apple API errors with actionable suggestions
134
134
  def handle_apple_api_error(error, context: {})
135
135
  error_message = error.message.to_s
136
136
 
137
- say ""
138
- say "=" * 80, :red
137
+ say ''
138
+ say '=' * 80, :red
139
139
  say "✗ #{context[:title] || 'Apple API Error'}", :red
140
- say "=" * 80, :red
141
- say ""
140
+ say '=' * 80, :red
141
+ say ''
142
142
  say "Error: #{error_message}", :red
143
- say ""
143
+ say ''
144
+
145
+ if error.respond_to?(:suggestion) && error.suggestion
146
+ say "Suggestion: #{error.suggestion}", :yellow
147
+ say ''
148
+ end
144
149
 
145
150
  # Check for specific Apple error patterns
146
151
  if error_message =~ /no.*build.*found|build.*not.*found|no.*processed.*build/i
@@ -161,6 +166,8 @@ module Mysigner
161
166
  show_archive_not_found_suggestions
162
167
  elsif error_message =~ /ipa.*not.*found|no.*ipa.*file/i
163
168
  show_ipa_not_found_suggestions
169
+ elsif show_actionable_suggestions(error, platform: :ios)
170
+ # Suggestions already shown
164
171
  else
165
172
  show_generic_apple_suggestions
166
173
  end
@@ -173,13 +180,18 @@ module Mysigner
173
180
  def handle_android_api_error(error, context: {})
174
181
  error_message = error.message.to_s
175
182
 
176
- say ""
177
- say "=" * 80, :red
183
+ say ''
184
+ say '=' * 80, :red
178
185
  say "✗ #{context[:title] || 'Google Play API Error'}", :red
179
- say "=" * 80, :red
180
- say ""
186
+ say '=' * 80, :red
187
+ say ''
181
188
  say "Error: #{error_message}", :red
182
- say ""
189
+ say ''
190
+
191
+ if error.respond_to?(:suggestion) && error.suggestion
192
+ say "Suggestion: #{error.suggestion}", :yellow
193
+ say ''
194
+ end
183
195
 
184
196
  # Check for specific Google Play error patterns
185
197
  if error_message =~ /keystore.*not.*found|no.*keystore|missing.*keystore/i
@@ -198,6 +210,8 @@ module Mysigner
198
210
  show_track_not_setup_suggestions(context[:track])
199
211
  elsif error_message =~ /aab.*not.*found|no.*aab.*file/i
200
212
  show_aab_not_found_suggestions
213
+ elsif show_actionable_suggestions(error, platform: :android)
214
+ # Suggestions already shown
201
215
  else
202
216
  show_generic_android_suggestions
203
217
  end
@@ -209,236 +223,232 @@ module Mysigner
209
223
  private
210
224
 
211
225
  # iOS-specific suggestion helpers
212
- def show_build_not_found_suggestions(context)
213
- say "💡 Build Not Found: How to fix", :cyan
214
- say ""
215
- say " → Upload a build first: mysigner ship testflight", :yellow
216
- say " → If already uploaded, wait 5-15 minutes for Apple to process", :yellow
217
- say " → Use --wait flag to poll: mysigner ship appstore --wait", :yellow
218
- say " → Sync your builds: mysigner sync ios", :yellow
219
- say ""
226
+ def show_build_not_found_suggestions(_context)
227
+ say '💡 Build Not Found: How to fix', :cyan
228
+ say ''
229
+ say ' → Upload a build first: mysigner ship testflight', :yellow
230
+ say ' → If already uploaded, wait 5-15 minutes for Apple to process', :yellow
231
+ say ' → Use --wait flag to poll: mysigner ship appstore --wait', :yellow
232
+ say ' → Sync your builds: mysigner sync ios', :yellow
233
+ say ''
220
234
  end
221
235
 
222
236
  def show_build_processing_suggestions
223
- say "💡 Build Still Processing: How to fix", :cyan
224
- say ""
225
- say " → Apple typically takes 5-15 minutes to process builds", :yellow
226
- say " → Use --wait flag: mysigner ship appstore --wait", :yellow
227
- say " → Increase timeout: --asc-timeout-seconds 1800", :yellow
228
- say " → Check App Store Connect for processing status", :yellow
229
- say ""
237
+ say '💡 Build Still Processing: How to fix', :cyan
238
+ say ''
239
+ say ' → Apple typically takes 5-15 minutes to process builds', :yellow
240
+ say ' → Use --wait flag: mysigner ship appstore --wait', :yellow
241
+ say ' → Increase timeout: --asc-timeout-seconds 1800', :yellow
242
+ say ' → Check App Store Connect for processing status', :yellow
243
+ say ''
230
244
  end
231
245
 
232
246
  def show_expired_credential_suggestions
233
- say "💡 Expired Profile or Certificate: How to fix", :cyan
234
- say ""
235
- say " → List profiles with expiration dates: mysigner profiles", :yellow
236
- say " → Check status in My Signer dashboard", :yellow
237
- say " → Regenerate in Apple Developer Portal", :yellow
238
- say " → Download fresh profile: mysigner profile download <ID>", :yellow
239
- say ""
247
+ say '💡 Expired Profile or Certificate: How to fix', :cyan
248
+ say ''
249
+ say ' → List profiles with expiration dates: mysigner profiles', :yellow
250
+ say ' → Check status in My Signer dashboard', :yellow
251
+ say ' → Regenerate in Apple Developer Portal', :yellow
252
+ say ' → Download fresh profile: mysigner profile download <ID>', :yellow
253
+ say ''
240
254
  end
241
255
 
242
256
  def show_profile_not_found_suggestions
243
- say "💡 Provisioning Profile Not Found: How to fix", :cyan
244
- say ""
245
- say " → List available profiles: mysigner profiles", :yellow
246
- say " → Sync from Apple: mysigner sync ios", :yellow
247
- say " → Create profile in Apple Developer Portal", :yellow
248
- say " → Check if profile matches your Bundle ID", :yellow
249
- say ""
257
+ say '💡 Provisioning Profile Not Found: How to fix', :cyan
258
+ say ''
259
+ say ' → List available profiles: mysigner profiles', :yellow
260
+ say ' → Sync from Apple: mysigner sync ios', :yellow
261
+ say ' → Create profile in Apple Developer Portal', :yellow
262
+ say ' → Check if profile matches your Bundle ID', :yellow
263
+ say ''
250
264
  end
251
265
 
252
266
  def show_certificate_not_found_suggestions
253
- say "💡 Signing Certificate Not Found: How to fix", :cyan
254
- say ""
255
- say " → List certificates: mysigner certificates", :yellow
256
- say " → Download and install: mysigner certificate download <ID>", :yellow
257
- say " → Check Keychain Access for installed certificates", :yellow
258
- say " → Run: mysigner doctor (diagnose signing issues)", :yellow
259
- say ""
267
+ say '💡 Signing Certificate Not Found: How to fix', :cyan
268
+ say ''
269
+ say ' → List certificates: mysigner certificates', :yellow
270
+ say ' → Download and install: mysigner certificate download <ID>', :yellow
271
+ say ' → Check Keychain Access for installed certificates', :yellow
272
+ say ' → Run: mysigner doctor (diagnose signing issues)', :yellow
273
+ say ''
260
274
  end
261
275
 
262
276
  def show_app_not_found_suggestions(bundle_id = nil)
263
- say "💡 App Not Found: How to fix", :cyan
264
- say ""
265
- say " → Ensure app exists in App Store Connect", :yellow
266
- say " → Create app in App Store Connect first", :yellow
267
- say " → Verify Bundle ID matches your Xcode project", :yellow if bundle_id
268
- say " → Sync from App Store Connect: mysigner sync ios", :yellow
269
- say ""
277
+ say '💡 App Not Found: How to fix', :cyan
278
+ say ''
279
+ say ' → Ensure app exists in App Store Connect', :yellow
280
+ say ' → Create app in App Store Connect first', :yellow
281
+ say ' → Verify Bundle ID matches your Xcode project', :yellow if bundle_id
282
+ say ' → Sync from App Store Connect: mysigner sync ios', :yellow
283
+ say ''
270
284
  end
271
285
 
272
286
  def show_missing_metadata_suggestions
273
- say "💡 Missing App Store Metadata: How to fix", :cyan
274
- say ""
275
- say " → Configure release in My Signer dashboard", :yellow
287
+ say '💡 Missing App Store Metadata: How to fix', :cyan
288
+ say ''
289
+ say ' → Configure release in My Signer dashboard', :yellow
276
290
  say " → Provide What's New via CLI: --whats-new \"Your text\"", :yellow
277
- say " → Ensure support URL is set in App Store Connect", :yellow
278
- say " → Complete app information in App Store Connect", :yellow
279
- say ""
291
+ say ' → Ensure support URL is set in App Store Connect', :yellow
292
+ say ' → Complete app information in App Store Connect', :yellow
293
+ say ''
280
294
  end
281
295
 
282
296
  def show_archive_not_found_suggestions
283
- say "💡 Archive Not Found: How to fix", :cyan
284
- say ""
285
- say " → Build first: mysigner build", :yellow
286
- say " → Or use: mysigner ship testflight (handles build)", :yellow
287
- say " → Check if Xcode build succeeded", :yellow
288
- say ""
297
+ say '💡 Archive Not Found: How to fix', :cyan
298
+ say ''
299
+ say ' → Build first: mysigner build', :yellow
300
+ say ' → Or use: mysigner ship testflight (handles build)', :yellow
301
+ say ' → Check if Xcode build succeeded', :yellow
302
+ say ''
289
303
  end
290
304
 
291
305
  def show_ipa_not_found_suggestions
292
- say "💡 IPA File Not Found: How to fix", :cyan
293
- say ""
294
- say " → Export IPA: mysigner export <archive_path>", :yellow
295
- say " → Or use: mysigner ship testflight (handles export)", :yellow
296
- say " → Check export method matches profile type", :yellow
297
- say ""
306
+ say '💡 IPA File Not Found: How to fix', :cyan
307
+ say ''
308
+ say ' → Export IPA: mysigner export <archive_path>', :yellow
309
+ say ' → Or use: mysigner ship testflight (handles export)', :yellow
310
+ say ' → Check export method matches profile type', :yellow
311
+ say ''
298
312
  end
299
313
 
300
314
  def show_generic_apple_suggestions
301
- say "💡 General troubleshooting:", :cyan
302
- say ""
315
+ say '💡 General troubleshooting:', :cyan
316
+ say ''
303
317
  say " → Run 'mysigner doctor' to check your setup", :yellow
304
- say " → Sync from Apple: mysigner sync ios", :yellow
305
- say " → Check App Store Connect: https://appstoreconnect.apple.com", :yellow
306
- say ""
318
+ say ' → Sync from Apple: mysigner sync ios', :yellow
319
+ say ' → Check App Store Connect: https://appstoreconnect.apple.com', :yellow
320
+ say ''
307
321
  end
308
322
 
309
323
  # Android-specific suggestion helpers
310
324
  def show_keystore_not_found_suggestions
311
- say "💡 Keystore Not Found: How to fix", :cyan
312
- say ""
313
- say " → Upload keystore: mysigner keystore upload <path>", :yellow
314
- say " → List keystores: mysigner keystore list", :yellow
315
- say " → Download keystore: mysigner keystore download <ID>", :yellow
316
- say " → Check keystore path in build.gradle", :yellow
317
- say ""
325
+ say '💡 Keystore Not Found: How to fix', :cyan
326
+ say ''
327
+ say ' → Upload keystore: mysigner keystore upload <path>', :yellow
328
+ say ' → List keystores: mysigner keystore list', :yellow
329
+ say ' → Download keystore: mysigner keystore download <ID>', :yellow
330
+ say ' → Check keystore path in build.gradle', :yellow
331
+ say ''
318
332
  end
319
333
 
320
334
  def show_keystore_password_suggestions
321
- say "💡 Keystore Password Issue: How to fix", :cyan
322
- say ""
323
- say " → Verify keystore password is correct", :yellow
324
- say " → Check password in My Signer dashboard", :yellow
325
- say " → Update password: mysigner keystore update <ID>", :yellow
326
- say ""
335
+ say '💡 Keystore Password Issue: How to fix', :cyan
336
+ say ''
337
+ say ' → Verify keystore password is correct', :yellow
338
+ say ' → Check password in My Signer dashboard', :yellow
339
+ say ' → Update password: mysigner keystore update <ID>', :yellow
340
+ say ''
327
341
  end
328
342
 
329
- def show_first_upload_suggestions(package_name = nil)
330
- say "💡 First Upload Required: How to fix", :cyan
331
- say ""
332
- say " Google Play requires the FIRST build to be uploaded manually:", :yellow
333
- say ""
334
- say " 1. Build AAB: mysigner android build", :yellow
335
- say " 2. Go to Play Console → Your App → Internal testing", :yellow
343
+ def show_first_upload_suggestions(_package_name = nil)
344
+ say '💡 First Upload Required: How to fix', :cyan
345
+ say ''
346
+ say ' Google Play requires the FIRST build to be uploaded manually:', :yellow
347
+ say ''
348
+ say ' 1. Build AAB: mysigner android build', :yellow
349
+ say ' 2. Go to Play Console → Your App → Internal testing', :yellow
336
350
  say " 3. Click 'Create release' and upload the AAB", :yellow
337
- say " 4. Save the release (no need to roll out)", :yellow
338
- say ""
351
+ say ' 4. Save the release (no need to roll out)', :yellow
352
+ say ''
339
353
  say " After that, 'mysigner ship' will work for future uploads.", :green
340
- say ""
354
+ say ''
341
355
  end
342
356
 
343
357
  def show_version_code_conflict_suggestions
344
- say "💡 Version Code Conflict: How to fix", :cyan
345
- say ""
346
- say " → Version code already exists on Google Play", :yellow
347
- say " → Run the command again - mysigner auto-increments", :yellow
348
- say " → Or manually increment versionCode in build.gradle", :yellow
349
- say ""
358
+ say '💡 Version Code Conflict: How to fix', :cyan
359
+ say ''
360
+ say ' → Version code already exists on Google Play', :yellow
361
+ say ' → Run the command again - mysigner auto-increments', :yellow
362
+ say ' → Or manually increment versionCode in build.gradle', :yellow
363
+ say ''
350
364
  end
351
365
 
352
366
  def show_service_account_missing_suggestions
353
- say "💡 Service Account Not Found: How to fix", :cyan
354
- say ""
355
- say " Set up Google Play credentials in My Signer dashboard:", :yellow
356
- say ""
357
- say " 1. Go to Play Console → API access → Service accounts", :yellow
358
- say " 2. Create a service account with Editor access", :yellow
359
- say " 3. Download the JSON key", :yellow
360
- say " 4. Upload to My Signer dashboard → Google Play Settings", :yellow
361
- say ""
367
+ say '💡 Service Account Not Found: How to fix', :cyan
368
+ say ''
369
+ say ' Set up Google Play credentials in My Signer dashboard:', :yellow
370
+ say ''
371
+ say ' 1. Go to Play Console → API access → Service accounts', :yellow
372
+ say ' 2. Create a service account with Editor access', :yellow
373
+ say ' 3. Download the JSON key', :yellow
374
+ say ' 4. Upload to My Signer dashboard → Google Play Settings', :yellow
375
+ say ''
362
376
  end
363
377
 
364
378
  def show_permission_denied_suggestions
365
- say "💡 Service Account Permission Denied: How to fix", :cyan
366
- say ""
367
- say " In Play Console → API access:", :yellow
368
- say ""
369
- say " 1. Find your service account", :yellow
379
+ say '💡 Service Account Permission Denied: How to fix', :cyan
380
+ say ''
381
+ say ' In Play Console → API access:', :yellow
382
+ say ''
383
+ say ' 1. Find your service account', :yellow
370
384
  say " 2. Click 'Manage Play Console permissions'", :yellow
371
385
  say " 3. Grant 'Admin' or 'Release manager' access", :yellow
372
- say ""
373
- say " Note: Permission changes take ~15 minutes to propagate", :green
374
- say ""
386
+ say ''
387
+ say ' Note: Permission changes take ~15 minutes to propagate', :green
388
+ say ''
375
389
  end
376
390
 
377
391
  def show_track_not_setup_suggestions(track = nil)
378
- track_name = track || "this track"
379
- say "💡 Track Not Set Up in Play Console: How to fix", :cyan
380
- say ""
381
- say " Complete track setup in Google Play Console:", :yellow
382
- say ""
383
- say " For PRODUCTION:", :yellow
384
- say " • Complete store listing, content rating, pricing", :yellow
385
- say ""
386
- say " For BETA/ALPHA:", :yellow
387
- say " • Create testing track and add testers", :yellow
388
- say ""
389
- say " For INTERNAL:", :yellow
390
- say " • Add internal testers", :yellow
391
- say ""
392
- say " ✓ Your AAB was uploaded successfully!", :green
393
- say " → Go to Play Console to finish track setup", :green
394
- say ""
392
+ track || 'this track'
393
+ say '💡 Track Not Set Up in Play Console: How to fix', :cyan
394
+ say ''
395
+ say ' Complete track setup in Google Play Console:', :yellow
396
+ say ''
397
+ say ' For PRODUCTION:', :yellow
398
+ say ' • Complete store listing, content rating, pricing', :yellow
399
+ say ''
400
+ say ' For BETA/ALPHA:', :yellow
401
+ say ' • Create testing track and add testers', :yellow
402
+ say ''
403
+ say ' For INTERNAL:', :yellow
404
+ say ' • Add internal testers', :yellow
405
+ say ''
406
+ say ' ✓ Your AAB was uploaded successfully!', :green
407
+ say ' → Go to Play Console to finish track setup', :green
408
+ say ''
395
409
  end
396
410
 
397
411
  def show_aab_not_found_suggestions
398
- say "💡 AAB File Not Found: How to fix", :cyan
399
- say ""
400
- say " → Build your app first: mysigner android build", :yellow
401
- say " → Or use: mysigner ship internal (handles build)", :yellow
402
- say " → Check if Gradle build succeeded", :yellow
403
- say ""
412
+ say '💡 AAB File Not Found: How to fix', :cyan
413
+ say ''
414
+ say ' → Build your app first: mysigner android build', :yellow
415
+ say ' → Or use: mysigner ship internal (handles build)', :yellow
416
+ say ' → Check if Gradle build succeeded', :yellow
417
+ say ''
404
418
  end
405
419
 
406
420
  def show_generic_android_suggestions
407
- say "💡 General troubleshooting:", :cyan
408
- say ""
421
+ say '💡 General troubleshooting:', :cyan
422
+ say ''
409
423
  say " → Run 'mysigner doctor' to check your setup", :yellow
410
- say " → List keystores: mysigner keystore list", :yellow
411
- say " → Check Play Console: https://play.google.com/console", :yellow
412
- say ""
424
+ say ' → List keystores: mysigner keystore list', :yellow
425
+ say ' → Check Play Console: https://play.google.com/console', :yellow
426
+ say ''
413
427
  end
414
428
 
415
429
  # Helper to show saved file paths
416
430
  def show_saved_files(context)
417
- if context[:archive_path] && File.exist?(context[:archive_path])
418
- say "📦 Archive saved at: #{context[:archive_path]}", :yellow
419
- end
420
- if context[:ipa_path] && File.exist?(context[:ipa_path])
421
- say "📦 IPA saved at: #{context[:ipa_path]}", :yellow
422
- end
423
- if context[:aab_path] && File.exist?(context[:aab_path])
424
- say "📦 AAB saved at: #{context[:aab_path]}", :yellow
425
- end
431
+ say "📦 Archive saved at: #{context[:archive_path]}", :yellow if context[:archive_path] && File.exist?(context[:archive_path])
432
+ say "📦 IPA saved at: #{context[:ipa_path]}", :yellow if context[:ipa_path] && File.exist?(context[:ipa_path])
433
+ return unless context[:aab_path] && File.exist?(context[:aab_path])
434
+
435
+ say "📦 AAB saved at: #{context[:aab_path]}", :yellow
426
436
  end
427
437
 
428
438
  # Helper to show debug info
429
439
  def show_debug_info(error)
430
- say ""
440
+ say ''
431
441
  if ENV['DEBUG']
432
- say "Debug info:", :yellow
442
+ say 'Debug info:', :yellow
433
443
  say " Error class: #{error.class}", :yellow
434
- say " Backtrace:", :yellow
444
+ say ' Backtrace:', :yellow
435
445
  error.backtrace&.first(5)&.each do |line|
436
446
  say " #{line}", :yellow
437
447
  end
438
448
  else
439
- say "💡 For more details, run with DEBUG=1", :yellow
449
+ say '💡 For more details, run with DEBUG=1', :yellow
440
450
  end
441
- say ""
451
+ say ''
442
452
  end
443
453
  end
444
454
  end