mysigner 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.
- checksums.yaml +7 -0
- data/.DS_Store +0 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +137 -0
- data/LICENSE +201 -0
- data/MANUAL_TEST.md +341 -0
- data/README.md +493 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/mysigner +5 -0
- data/lib/mysigner/build/android_executor.rb +367 -0
- data/lib/mysigner/build/android_parser.rb +293 -0
- data/lib/mysigner/build/configurator.rb +126 -0
- data/lib/mysigner/build/detector.rb +388 -0
- data/lib/mysigner/build/error_analyzer.rb +193 -0
- data/lib/mysigner/build/executor.rb +176 -0
- data/lib/mysigner/build/parser.rb +206 -0
- data/lib/mysigner/cli/auth_commands.rb +1381 -0
- data/lib/mysigner/cli/build_commands.rb +2095 -0
- data/lib/mysigner/cli/concerns/actionable_suggestions.rb +500 -0
- data/lib/mysigner/cli/concerns/api_helpers.rb +131 -0
- data/lib/mysigner/cli/concerns/error_handlers.rb +446 -0
- data/lib/mysigner/cli/concerns/helpers.rb +63 -0
- data/lib/mysigner/cli/diagnostic_commands.rb +1034 -0
- data/lib/mysigner/cli/resource_commands.rb +2670 -0
- data/lib/mysigner/cli.rb +43 -0
- data/lib/mysigner/client.rb +189 -0
- data/lib/mysigner/config.rb +311 -0
- data/lib/mysigner/export/exporter.rb +150 -0
- data/lib/mysigner/signing/certificate_checker.rb +148 -0
- data/lib/mysigner/signing/keystore_manager.rb +239 -0
- data/lib/mysigner/signing/validator.rb +150 -0
- data/lib/mysigner/signing/wizard.rb +784 -0
- data/lib/mysigner/upload/app_store_automation.rb +402 -0
- data/lib/mysigner/upload/app_store_submission.rb +312 -0
- data/lib/mysigner/upload/play_store_uploader.rb +378 -0
- data/lib/mysigner/upload/uploader.rb +373 -0
- data/lib/mysigner/version.rb +3 -0
- data/lib/mysigner.rb +15 -0
- data/mysigner.gemspec +78 -0
- data/test_manual.rb +102 -0
- metadata +286 -0
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
module Mysigner
|
|
2
|
+
class CLI < Thor
|
|
3
|
+
module Concerns
|
|
4
|
+
module ErrorHandlers
|
|
5
|
+
# Show guidance for getting a token
|
|
6
|
+
def show_token_guidance(api_url)
|
|
7
|
+
say "Don't have a token yet?", :yellow
|
|
8
|
+
say " 1. Go to: #{api_url}", :cyan
|
|
9
|
+
say " 2. Navigate to: Your Organization → API Tokens", :cyan
|
|
10
|
+
say " 3. Click 'Create Token'", :cyan
|
|
11
|
+
say " 4. Copy the token (you'll only see it once!)", :cyan
|
|
12
|
+
say ""
|
|
13
|
+
say "💡 Or run 'mysigner onboard' for step-by-step guidance", :yellow
|
|
14
|
+
say ""
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Handle connection failure
|
|
18
|
+
def handle_connection_failure(api_url)
|
|
19
|
+
say ""
|
|
20
|
+
say "Possible issues:", :yellow
|
|
21
|
+
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"
|
|
28
|
+
say " • Run 'mysigner onboard' for guided setup"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Show guidance for creating an organization
|
|
32
|
+
def show_create_org_guidance(api_url)
|
|
33
|
+
say "To create an organization:", :cyan
|
|
34
|
+
say " 1. Go to: #{api_url}"
|
|
35
|
+
say " 2. Sign in to your account"
|
|
36
|
+
say " 3. Click 'Create Organization'"
|
|
37
|
+
say " 4. Then generate a new API token for that organization"
|
|
38
|
+
say ""
|
|
39
|
+
say "💡 Run 'mysigner onboard' for step-by-step guidance", :yellow
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Handle unauthorized error
|
|
43
|
+
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"
|
|
55
|
+
say " • You're using the wrong API URL"
|
|
56
|
+
say ""
|
|
57
|
+
say "To fix this:", :cyan
|
|
58
|
+
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)"
|
|
62
|
+
say " 5. Run 'mysigner login' again"
|
|
63
|
+
say ""
|
|
64
|
+
say "💡 Or run 'mysigner onboard' for guided setup", :yellow
|
|
65
|
+
say ""
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Handle connection error
|
|
69
|
+
def handle_connection_error(error, api_url)
|
|
70
|
+
say ""
|
|
71
|
+
say "=" * 80, :red
|
|
72
|
+
say "✗ Connection Failed", :red
|
|
73
|
+
say "=" * 80, :red
|
|
74
|
+
say ""
|
|
75
|
+
say "Error: #{error.message}", :red
|
|
76
|
+
say ""
|
|
77
|
+
say "Possible causes:", :yellow
|
|
78
|
+
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 ""
|
|
85
|
+
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 ""
|
|
91
|
+
say " 2. Verify it's accessible:"
|
|
92
|
+
say " curl #{api_url}/up"
|
|
93
|
+
say ""
|
|
94
|
+
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 ""
|
|
100
|
+
end
|
|
101
|
+
say " Or set a custom API URL:", :bold
|
|
102
|
+
say " export MYSIGNER_API_URL=http://your-server.com"
|
|
103
|
+
say ""
|
|
104
|
+
say "💡 Run 'mysigner onboard' to reconfigure", :yellow
|
|
105
|
+
say ""
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Handle unexpected error
|
|
109
|
+
def handle_unexpected_error(error, api_url)
|
|
110
|
+
say ""
|
|
111
|
+
say "=" * 80, :red
|
|
112
|
+
say "✗ Unexpected Error", :red
|
|
113
|
+
say "=" * 80, :red
|
|
114
|
+
say ""
|
|
115
|
+
say "Error: #{error.message}", :red
|
|
116
|
+
say "Type: #{error.class}", :red if ENV['DEBUG']
|
|
117
|
+
say ""
|
|
118
|
+
say "This is unexpected. Please try:", :yellow
|
|
119
|
+
say " 1. Run 'mysigner onboard' to reconfigure"
|
|
120
|
+
say " 2. Check #{api_url} is accessible"
|
|
121
|
+
say " 3. Run 'mysigner doctor' to check your environment"
|
|
122
|
+
say ""
|
|
123
|
+
if ENV['DEBUG']
|
|
124
|
+
say "Stack trace:", :red
|
|
125
|
+
say error.backtrace.first(5).join("\n"), :red
|
|
126
|
+
say ""
|
|
127
|
+
else
|
|
128
|
+
say "💡 For more details, run with DEBUG=1", :yellow
|
|
129
|
+
say ""
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Handle Apple API errors with actionable suggestions
|
|
134
|
+
def handle_apple_api_error(error, context: {})
|
|
135
|
+
error_message = error.message.to_s
|
|
136
|
+
|
|
137
|
+
say ""
|
|
138
|
+
say "=" * 80, :red
|
|
139
|
+
say "✗ #{context[:title] || 'Apple API Error'}", :red
|
|
140
|
+
say "=" * 80, :red
|
|
141
|
+
say ""
|
|
142
|
+
say "Error: #{error_message}", :red
|
|
143
|
+
say ""
|
|
144
|
+
|
|
145
|
+
# Check for specific Apple error patterns
|
|
146
|
+
if error_message =~ /no.*build.*found|build.*not.*found|no.*processed.*build/i
|
|
147
|
+
show_build_not_found_suggestions(context)
|
|
148
|
+
elsif error_message =~ /still.*processing|processing.*build/i
|
|
149
|
+
show_build_processing_suggestions
|
|
150
|
+
elsif error_message =~ /profile.*expired|provisioning.*expired|certificate.*expired/i
|
|
151
|
+
show_expired_credential_suggestions
|
|
152
|
+
elsif error_message =~ /profile.*not.*found|no.*provisioning.*profile|missing.*profile/i
|
|
153
|
+
show_profile_not_found_suggestions
|
|
154
|
+
elsif error_message =~ /certificate.*not.*found|no.*signing.*certificate/i
|
|
155
|
+
show_certificate_not_found_suggestions
|
|
156
|
+
elsif error_message =~ /app.*with.*bundle.*id.*not.*found|app.*not.*found/i
|
|
157
|
+
show_app_not_found_suggestions(context[:bundle_id])
|
|
158
|
+
elsif error_message =~ /missing.*required.*field|what's.*new.*required|cannot.*submit.*missing/i
|
|
159
|
+
show_missing_metadata_suggestions
|
|
160
|
+
elsif error_message =~ /archive.*not.*found|no.*xcarchive/i
|
|
161
|
+
show_archive_not_found_suggestions
|
|
162
|
+
elsif error_message =~ /ipa.*not.*found|no.*ipa.*file/i
|
|
163
|
+
show_ipa_not_found_suggestions
|
|
164
|
+
else
|
|
165
|
+
show_generic_apple_suggestions
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
show_saved_files(context)
|
|
169
|
+
show_debug_info(error)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Handle Google Play API errors with actionable suggestions
|
|
173
|
+
def handle_android_api_error(error, context: {})
|
|
174
|
+
error_message = error.message.to_s
|
|
175
|
+
|
|
176
|
+
say ""
|
|
177
|
+
say "=" * 80, :red
|
|
178
|
+
say "✗ #{context[:title] || 'Google Play API Error'}", :red
|
|
179
|
+
say "=" * 80, :red
|
|
180
|
+
say ""
|
|
181
|
+
say "Error: #{error_message}", :red
|
|
182
|
+
say ""
|
|
183
|
+
|
|
184
|
+
# Check for specific Google Play error patterns
|
|
185
|
+
if error_message =~ /keystore.*not.*found|no.*keystore|missing.*keystore/i
|
|
186
|
+
show_keystore_not_found_suggestions
|
|
187
|
+
elsif error_message =~ /keystore.*password|wrong.*password/i
|
|
188
|
+
show_keystore_password_suggestions
|
|
189
|
+
elsif error_message =~ /package.*not.*found|first.*build.*uploaded.*manually/i
|
|
190
|
+
show_first_upload_suggestions(context[:package_name])
|
|
191
|
+
elsif error_message =~ /version.*code.*already|already.*used/i
|
|
192
|
+
show_version_code_conflict_suggestions
|
|
193
|
+
elsif error_message =~ /service.*account.*not.*found|no.*credentials/i
|
|
194
|
+
show_service_account_missing_suggestions
|
|
195
|
+
elsif error_message =~ /not.*authorized|permission.*denied|forbidden/i
|
|
196
|
+
show_permission_denied_suggestions
|
|
197
|
+
elsif error_message =~ /precondition.*failed|track.*not.*ready/i
|
|
198
|
+
show_track_not_setup_suggestions(context[:track])
|
|
199
|
+
elsif error_message =~ /aab.*not.*found|no.*aab.*file/i
|
|
200
|
+
show_aab_not_found_suggestions
|
|
201
|
+
else
|
|
202
|
+
show_generic_android_suggestions
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
show_saved_files(context)
|
|
206
|
+
show_debug_info(error)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
private
|
|
210
|
+
|
|
211
|
+
# 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 ""
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
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 ""
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
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 ""
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
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 ""
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
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 ""
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
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 ""
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
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
|
|
276
|
+
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 ""
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
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 ""
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
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 ""
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def show_generic_apple_suggestions
|
|
301
|
+
say "💡 General troubleshooting:", :cyan
|
|
302
|
+
say ""
|
|
303
|
+
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 ""
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# Android-specific suggestion helpers
|
|
310
|
+
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 keystores", :yellow
|
|
315
|
+
say " → Download keystore: mysigner keystore download <ID>", :yellow
|
|
316
|
+
say " → Check keystore path in build.gradle", :yellow
|
|
317
|
+
say ""
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
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 ""
|
|
327
|
+
end
|
|
328
|
+
|
|
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
|
|
336
|
+
say " 3. Click 'Create release' and upload the AAB", :yellow
|
|
337
|
+
say " 4. Save the release (no need to roll out)", :yellow
|
|
338
|
+
say ""
|
|
339
|
+
say " After that, 'mysigner ship' will work for future uploads.", :green
|
|
340
|
+
say ""
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
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 ""
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
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 ""
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
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
|
|
370
|
+
say " 2. Click 'Manage Play Console permissions'", :yellow
|
|
371
|
+
say " 3. Grant 'Admin' or 'Release manager' access", :yellow
|
|
372
|
+
say ""
|
|
373
|
+
say " Note: Permission changes take ~15 minutes to propagate", :green
|
|
374
|
+
say ""
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
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 ""
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
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 ""
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
def show_generic_android_suggestions
|
|
407
|
+
say "💡 General troubleshooting:", :cyan
|
|
408
|
+
say ""
|
|
409
|
+
say " → Run 'mysigner doctor' to check your setup", :yellow
|
|
410
|
+
say " → List keystores: mysigner keystores", :yellow
|
|
411
|
+
say " → Check Play Console: https://play.google.com/console", :yellow
|
|
412
|
+
say ""
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
# Helper to show saved file paths
|
|
416
|
+
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
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
# Helper to show debug info
|
|
429
|
+
def show_debug_info(error)
|
|
430
|
+
say ""
|
|
431
|
+
if ENV['DEBUG']
|
|
432
|
+
say "Debug info:", :yellow
|
|
433
|
+
say " Error class: #{error.class}", :yellow
|
|
434
|
+
say " Backtrace:", :yellow
|
|
435
|
+
error.backtrace&.first(5)&.each do |line|
|
|
436
|
+
say " #{line}", :yellow
|
|
437
|
+
end
|
|
438
|
+
else
|
|
439
|
+
say "💡 For more details, run with DEBUG=1", :yellow
|
|
440
|
+
end
|
|
441
|
+
say ""
|
|
442
|
+
end
|
|
443
|
+
end
|
|
444
|
+
end
|
|
445
|
+
end
|
|
446
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module Mysigner
|
|
2
|
+
class CLI < Thor
|
|
3
|
+
module Concerns
|
|
4
|
+
module Helpers
|
|
5
|
+
# Helper for timing operations
|
|
6
|
+
def with_timing(label)
|
|
7
|
+
start = Time.now
|
|
8
|
+
result = yield
|
|
9
|
+
duration = Time.now - start
|
|
10
|
+
[result, duration]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def format_duration(seconds)
|
|
14
|
+
if seconds < 60
|
|
15
|
+
"#{seconds.round}s"
|
|
16
|
+
elsif seconds < 3600
|
|
17
|
+
minutes = (seconds / 60).floor
|
|
18
|
+
secs = (seconds % 60).round
|
|
19
|
+
"#{minutes}m #{secs}s"
|
|
20
|
+
else
|
|
21
|
+
hours = (seconds / 3600).floor
|
|
22
|
+
minutes = ((seconds % 3600) / 60).floor
|
|
23
|
+
"#{hours}h #{minutes}m"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def format_bytes(bytes)
|
|
28
|
+
if bytes < 1024
|
|
29
|
+
"#{bytes} B"
|
|
30
|
+
elsif bytes < 1024 * 1024
|
|
31
|
+
"#{(bytes / 1024.0).round(1)} KB"
|
|
32
|
+
else
|
|
33
|
+
"#{(bytes / (1024.0 * 1024)).round(1)} MB"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def load_config
|
|
38
|
+
config = Config.new
|
|
39
|
+
|
|
40
|
+
unless config.exists?
|
|
41
|
+
error "Not logged in. Run 'mysigner login' first."
|
|
42
|
+
exit 1
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
config.load
|
|
46
|
+
config
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def create_client(config)
|
|
50
|
+
Client.new(
|
|
51
|
+
api_url: config.api_url,
|
|
52
|
+
api_token: config.api_token,
|
|
53
|
+
user_email: config.user_email
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def error(message)
|
|
58
|
+
say "✗ Error: #{message}", :red
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|