mysigner 0.1.1 → 0.1.2
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 +4 -4
- data/.gitignore +1 -0
- data/README.md +74 -2
- data/lib/mysigner/cli/concerns/actionable_suggestions.rb +5 -5
- data/lib/mysigner/cli/concerns/error_handlers.rb +2 -2
- data/lib/mysigner/cli/diagnostic_commands.rb +2 -2
- data/lib/mysigner/cli/resource_commands.rb +472 -6
- data/lib/mysigner/cli/validate_commands.rb +161 -0
- data/lib/mysigner/cli.rb +2 -0
- data/lib/mysigner/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 37696e7f0c5c14aa16dab283a20bf46b35f9b0fb18d3ff6f41930c3842fca963
|
|
4
|
+
data.tar.gz: ee6ad5f6063feaf786513352b95903c56c5a80f3b1eb244a18bf263b24578ffe
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1a347b8525f359d9974fc6d8846756dd82a73ae3872d2b30d539421f1939237d7a589cd128694830bb5fee48880f780c2ccfb771cc62094bf7d2089838a42ac7
|
|
7
|
+
data.tar.gz: efe0009decff514149523ff6c5177625496b4139aa2682f090cff5b54bab7ce51712d74886e7b92e99d6d0845f6ff228e7cbc28058bd3b1b3ccbe1832c89b35f
|
data/.gitignore
CHANGED
data/README.md
CHANGED
|
@@ -229,6 +229,76 @@ mysigner keystore activate ID # Set as active keystore
|
|
|
229
229
|
mysigner keystore delete ID # Delete keystore
|
|
230
230
|
```
|
|
231
231
|
|
|
232
|
+
### Google Play Credentials
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
mysigner gp-credential list # List all credentials
|
|
236
|
+
mysigner gp-credential activate ID # Set as active credential
|
|
237
|
+
mysigner gp-credential test ID # Test Google Play connection
|
|
238
|
+
mysigner gp-credential delete ID # Delete credential
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### App Store Releases
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
mysigner release list # List release configurations
|
|
245
|
+
mysigner release list --bundle-id com.app # Filter by bundle ID
|
|
246
|
+
mysigner release show ID # Show release details
|
|
247
|
+
mysigner release create --bundle-id-id 42 # Create release config
|
|
248
|
+
mysigner release update ID --auto-submit # Update release config
|
|
249
|
+
mysigner release update ID --whats-new "Bug fixes"
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Google Play Tracks
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
mysigner tracks com.example.app # List tracks for an app
|
|
256
|
+
mysigner tracks com.example.app --sort # Sort alphabetically
|
|
257
|
+
mysigner track com.example.app production # Show track details
|
|
258
|
+
mysigner track com.example.app beta # Show beta track details
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Android Apps
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
mysigner android init # Detect and register Android app
|
|
265
|
+
mysigner android add com.example.app # Register app manually
|
|
266
|
+
mysigner android build # Build AAB file
|
|
267
|
+
mysigner android list # List registered Android apps
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Apps
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
mysigner apps # List all apps (iOS + Android)
|
|
274
|
+
mysigner apps --platform ios # iOS apps only
|
|
275
|
+
mysigner apps --platform android # Android apps only
|
|
276
|
+
mysigner apps -q "myapp" # Search by name
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Merchant IDs
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
mysigner merchant-ids # List Apple Pay Merchant IDs
|
|
283
|
+
mysigner merchant-id create IDENTIFIER # Create a Merchant ID
|
|
284
|
+
mysigner merchant-id delete IDENTIFIER # Delete a Merchant ID
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### App Groups
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
mysigner app-groups # List App Groups
|
|
291
|
+
mysigner app-group register IDENTIFIER # Register an App Group
|
|
292
|
+
mysigner app-group delete IDENTIFIER # Delete an App Group
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Validate Signing
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
mysigner validate -b com.example.app -t development # Validate signing config
|
|
299
|
+
mysigner validate --type appstore # Auto-detect bundle ID
|
|
300
|
+
```
|
|
301
|
+
|
|
232
302
|
### Sync
|
|
233
303
|
|
|
234
304
|
```bash
|
|
@@ -280,11 +350,14 @@ mysigner config set KEY VAL # Update configuration value
|
|
|
280
350
|
- ✅ **iOS Build & Ship** (`mysigner ship testflight`, `mysigner ship appstore`)
|
|
281
351
|
- ✅ **Android Build & Ship** (`mysigner ship internal/alpha/beta/production`)
|
|
282
352
|
- ✅ Android keystore management (`mysigner keystore upload/download/activate`)
|
|
353
|
+
- ✅ Google Play credential management (`mysigner gp-credential list/activate/test/delete`)
|
|
283
354
|
- ✅ Automatic version code increment for Android
|
|
284
355
|
- ✅ App Store submission with release types (AFTER_APPROVAL, MANUAL, SCHEDULED)
|
|
356
|
+
- ✅ App Store release configuration (`mysigner release list/show/create/update`)
|
|
357
|
+
- ✅ Server-side signing validation (`mysigner validate`)
|
|
285
358
|
- ✅ Project detection (Native iOS/Android, React Native, Flutter, Capacitor/Ionic)
|
|
286
359
|
- ✅ `mysigner doctor` health check with auto-fix capabilities
|
|
287
|
-
- ✅
|
|
360
|
+
- ✅ 260+ RSpec tests
|
|
288
361
|
- ✅ Interactive prompts and wizards
|
|
289
362
|
|
|
290
363
|
📅 **Future**:
|
|
@@ -292,7 +365,6 @@ mysigner config set KEY VAL # Update configuration value
|
|
|
292
365
|
- Progress spinners (TTY::Spinner)
|
|
293
366
|
- `--json` flag for scripting
|
|
294
367
|
- CI/CD templates (GitHub Actions, GitLab CI)
|
|
295
|
-
- Phased release support
|
|
296
368
|
|
|
297
369
|
See the [main project roadmap](https://github.com/jurgenleka/my-signer/blob/main/ROADMAP.md) for detailed plans.
|
|
298
370
|
|
|
@@ -97,8 +97,8 @@ module Mysigner
|
|
|
97
97
|
title: "Bundle ID Issue",
|
|
98
98
|
suggestions: [
|
|
99
99
|
"Verify Bundle ID in Xcode matches Apple Developer Portal",
|
|
100
|
-
"List Bundle IDs: mysigner
|
|
101
|
-
"Register new Bundle ID: mysigner
|
|
100
|
+
"List Bundle IDs: mysigner bundleid list",
|
|
101
|
+
"Register new Bundle ID: mysigner bundleid register <ID>",
|
|
102
102
|
"Check My Signer dashboard for registered apps"
|
|
103
103
|
]
|
|
104
104
|
},
|
|
@@ -193,7 +193,7 @@ module Mysigner
|
|
|
193
193
|
title: "Keystore Not Found",
|
|
194
194
|
suggestions: [
|
|
195
195
|
"Upload keystore: mysigner keystore upload <path>",
|
|
196
|
-
"List keystores: mysigner
|
|
196
|
+
"List keystores: mysigner keystore list",
|
|
197
197
|
"Download keystore: mysigner keystore download <ID>",
|
|
198
198
|
"Check keystore path in build.gradle"
|
|
199
199
|
]
|
|
@@ -324,7 +324,7 @@ module Mysigner
|
|
|
324
324
|
suggestions: [
|
|
325
325
|
"AAB is signed with a different key than expected",
|
|
326
326
|
"Verify keystore matches what's registered in Play Console",
|
|
327
|
-
"Check active keystore: mysigner
|
|
327
|
+
"Check active keystore: mysigner keystore list --active",
|
|
328
328
|
"If using Google Play App Signing, upload the correct upload key"
|
|
329
329
|
]
|
|
330
330
|
}
|
|
@@ -482,7 +482,7 @@ module Mysigner
|
|
|
482
482
|
},
|
|
483
483
|
profiles: "mysigner profiles",
|
|
484
484
|
certificates: "mysigner certificates",
|
|
485
|
-
keystores: "mysigner
|
|
485
|
+
keystores: "mysigner keystore list",
|
|
486
486
|
doctor: "mysigner doctor",
|
|
487
487
|
onboard: "mysigner onboard",
|
|
488
488
|
login: "mysigner login"
|
|
@@ -311,7 +311,7 @@ module Mysigner
|
|
|
311
311
|
say "💡 Keystore Not Found: How to fix", :cyan
|
|
312
312
|
say ""
|
|
313
313
|
say " → Upload keystore: mysigner keystore upload <path>", :yellow
|
|
314
|
-
say " → List keystores: mysigner
|
|
314
|
+
say " → List keystores: mysigner keystore list", :yellow
|
|
315
315
|
say " → Download keystore: mysigner keystore download <ID>", :yellow
|
|
316
316
|
say " → Check keystore path in build.gradle", :yellow
|
|
317
317
|
say ""
|
|
@@ -407,7 +407,7 @@ module Mysigner
|
|
|
407
407
|
say "💡 General troubleshooting:", :cyan
|
|
408
408
|
say ""
|
|
409
409
|
say " → Run 'mysigner doctor' to check your setup", :yellow
|
|
410
|
-
say " → List keystores: mysigner
|
|
410
|
+
say " → List keystores: mysigner keystore list", :yellow
|
|
411
411
|
say " → Check Play Console: https://play.google.com/console", :yellow
|
|
412
412
|
say ""
|
|
413
413
|
end
|
|
@@ -823,7 +823,7 @@ module Mysigner
|
|
|
823
823
|
say ""
|
|
824
824
|
say " 📋 Quick fix:", :cyan
|
|
825
825
|
say " • Get UDID: Connect device → Finder → Click serial number", :green
|
|
826
|
-
say " • Run: mysigner device add <
|
|
826
|
+
say " • Run: mysigner device add <NAME> <UDID>", :green
|
|
827
827
|
say " • Or add in: #{client.api_url}/organizations/#{config.current_organization_id}", :green
|
|
828
828
|
say ""
|
|
829
829
|
else
|
|
@@ -931,7 +931,7 @@ module Mysigner
|
|
|
931
931
|
say "✓ Android sync started!", :green
|
|
932
932
|
say ""
|
|
933
933
|
say "Sync runs in the background. Check status with:", :cyan
|
|
934
|
-
say " mysigner android
|
|
934
|
+
say " mysigner apps --platform android", :green
|
|
935
935
|
say ""
|
|
936
936
|
|
|
937
937
|
# Optionally wait and show status
|
|
@@ -1017,7 +1017,7 @@ module Mysigner
|
|
|
1017
1017
|
invoke :help, ['certificate']
|
|
1018
1018
|
else
|
|
1019
1019
|
error "Unknown action: #{action}"
|
|
1020
|
-
say "Available actions: download, help", :yellow
|
|
1020
|
+
say "Available actions: check, download, help", :yellow
|
|
1021
1021
|
exit 1
|
|
1022
1022
|
end
|
|
1023
1023
|
end
|
|
@@ -1190,7 +1190,7 @@ module Mysigner
|
|
|
1190
1190
|
unless keystore_id
|
|
1191
1191
|
error "Usage: mysigner keystore download ID"
|
|
1192
1192
|
say ""
|
|
1193
|
-
say "Run 'mysigner
|
|
1193
|
+
say "Run 'mysigner keystore list' to see available IDs", :yellow
|
|
1194
1194
|
exit 1
|
|
1195
1195
|
end
|
|
1196
1196
|
|
|
@@ -1220,7 +1220,7 @@ module Mysigner
|
|
|
1220
1220
|
say ""
|
|
1221
1221
|
say "💡 Keystore Not Found: How to fix", :cyan
|
|
1222
1222
|
say ""
|
|
1223
|
-
say " → List available keystores: mysigner
|
|
1223
|
+
say " → List available keystores: mysigner keystore list", :yellow
|
|
1224
1224
|
say " → Upload a keystore: mysigner keystore upload <path>", :yellow
|
|
1225
1225
|
say " → Check the ID is correct (IDs are numeric)", :yellow
|
|
1226
1226
|
say ""
|
|
@@ -1254,7 +1254,7 @@ module Mysigner
|
|
|
1254
1254
|
say ""
|
|
1255
1255
|
say "💡 Keystore Not Found: How to fix", :cyan
|
|
1256
1256
|
say ""
|
|
1257
|
-
say " → List available keystores: mysigner
|
|
1257
|
+
say " → List available keystores: mysigner keystore list", :yellow
|
|
1258
1258
|
say " → Upload a keystore: mysigner keystore upload <path>", :yellow
|
|
1259
1259
|
say ""
|
|
1260
1260
|
exit 1
|
|
@@ -1298,7 +1298,7 @@ module Mysigner
|
|
|
1298
1298
|
say ""
|
|
1299
1299
|
say "💡 Keystore Not Found: How to fix", :cyan
|
|
1300
1300
|
say ""
|
|
1301
|
-
say " → List available keystores: mysigner
|
|
1301
|
+
say " → List available keystores: mysigner keystore list", :yellow
|
|
1302
1302
|
say " → Upload a keystore: mysigner keystore upload <path>", :yellow
|
|
1303
1303
|
say ""
|
|
1304
1304
|
exit 1
|
|
@@ -1307,7 +1307,7 @@ module Mysigner
|
|
|
1307
1307
|
say ""
|
|
1308
1308
|
say "💡 Activation Failed: Try these steps", :cyan
|
|
1309
1309
|
say ""
|
|
1310
|
-
say " → Verify keystore ID is correct: mysigner
|
|
1310
|
+
say " → Verify keystore ID is correct: mysigner keystore list", :yellow
|
|
1311
1311
|
say " → Check API token is valid: mysigner status", :yellow
|
|
1312
1312
|
say ""
|
|
1313
1313
|
exit 1
|
|
@@ -2847,6 +2847,472 @@ module Mysigner
|
|
|
2847
2847
|
exit 1
|
|
2848
2848
|
end
|
|
2849
2849
|
end
|
|
2850
|
+
|
|
2851
|
+
# ==================== GOOGLE PLAY CREDENTIALS ====================
|
|
2852
|
+
|
|
2853
|
+
desc "gp-credential SUBCOMMAND", "Manage Google Play credentials (list, delete, activate, test)"
|
|
2854
|
+
long_desc <<~DESC
|
|
2855
|
+
Manage Google Play API credentials for Android app distribution.
|
|
2856
|
+
|
|
2857
|
+
SUBCOMMANDS:
|
|
2858
|
+
|
|
2859
|
+
mysigner gp-credential list
|
|
2860
|
+
List all Google Play credentials
|
|
2861
|
+
|
|
2862
|
+
mysigner gp-credential delete ID
|
|
2863
|
+
Delete a credential
|
|
2864
|
+
|
|
2865
|
+
mysigner gp-credential activate ID
|
|
2866
|
+
Set a credential as the active one
|
|
2867
|
+
|
|
2868
|
+
mysigner gp-credential test ID
|
|
2869
|
+
Test connection to Google Play API
|
|
2870
|
+
|
|
2871
|
+
EXAMPLES:
|
|
2872
|
+
|
|
2873
|
+
# List all credentials
|
|
2874
|
+
mysigner gp-credential list
|
|
2875
|
+
|
|
2876
|
+
# Activate credential ID 2
|
|
2877
|
+
mysigner gp-credential activate 2
|
|
2878
|
+
|
|
2879
|
+
# Test if credential can connect to Google Play
|
|
2880
|
+
mysigner gp-credential test 1
|
|
2881
|
+
|
|
2882
|
+
# Delete old credential
|
|
2883
|
+
mysigner gp-credential delete 3
|
|
2884
|
+
DESC
|
|
2885
|
+
def gp_credential(action, *args)
|
|
2886
|
+
config = load_config
|
|
2887
|
+
client = create_client(config)
|
|
2888
|
+
|
|
2889
|
+
case action
|
|
2890
|
+
when 'list'
|
|
2891
|
+
say "🔑 Google Play Credentials", :cyan
|
|
2892
|
+
say ""
|
|
2893
|
+
|
|
2894
|
+
begin
|
|
2895
|
+
response = client.get("/api/v1/organizations/#{config.current_organization_id}/google_play_credentials")
|
|
2896
|
+
credentials = response[:data]['google_play_credentials'] || []
|
|
2897
|
+
|
|
2898
|
+
if credentials.empty?
|
|
2899
|
+
say "No Google Play credentials found", :yellow
|
|
2900
|
+
say ""
|
|
2901
|
+
say "Set up credentials with: mysigner onboard", :yellow
|
|
2902
|
+
return
|
|
2903
|
+
end
|
|
2904
|
+
|
|
2905
|
+
credentials.each do |cred|
|
|
2906
|
+
active_icon = cred['active'] ? '✓' : '○'
|
|
2907
|
+
active_color = cred['active'] ? :green : :white
|
|
2908
|
+
|
|
2909
|
+
say " #{active_icon} #{cred['name']} (ID: #{cred['id']})", active_color
|
|
2910
|
+
say " Developer Account: #{cred['developer_account_id'] || 'N/A'}"
|
|
2911
|
+
say " Active: #{cred['active'] ? 'Yes' : 'No'}"
|
|
2912
|
+
if cred['last_synced_at']
|
|
2913
|
+
synced = Time.parse(cred['last_synced_at']).strftime('%Y-%m-%d %H:%M')
|
|
2914
|
+
say " Last Synced: #{synced}"
|
|
2915
|
+
end
|
|
2916
|
+
if cred['last_sync_status']
|
|
2917
|
+
sync_color = cred['last_sync_status'] == 'success' ? :green : :red
|
|
2918
|
+
say " Sync Status: #{cred['last_sync_status']}", sync_color
|
|
2919
|
+
end
|
|
2920
|
+
say ""
|
|
2921
|
+
end
|
|
2922
|
+
|
|
2923
|
+
say "Total: #{credentials.count} credential(s)", :yellow
|
|
2924
|
+
rescue Mysigner::ClientError => e
|
|
2925
|
+
error "Failed to fetch credentials: #{e.message}"
|
|
2926
|
+
exit 1
|
|
2927
|
+
end
|
|
2928
|
+
|
|
2929
|
+
when 'delete'
|
|
2930
|
+
credential_id = args[0]
|
|
2931
|
+
|
|
2932
|
+
unless credential_id
|
|
2933
|
+
error "Usage: mysigner gp-credential delete ID"
|
|
2934
|
+
say ""
|
|
2935
|
+
say "Run 'mysigner gp-credential list' to see available IDs", :yellow
|
|
2936
|
+
exit 1
|
|
2937
|
+
end
|
|
2938
|
+
|
|
2939
|
+
say "⚠️ You are about to delete Google Play credential ID: #{credential_id}", :yellow
|
|
2940
|
+
say ""
|
|
2941
|
+
|
|
2942
|
+
if yes?("Are you sure? This cannot be undone. (y/n)")
|
|
2943
|
+
begin
|
|
2944
|
+
client.delete("/api/v1/organizations/#{config.current_organization_id}/google_play_credentials/#{credential_id}")
|
|
2945
|
+
say ""
|
|
2946
|
+
say "✓ Google Play credential deleted", :green
|
|
2947
|
+
rescue Mysigner::NotFoundError
|
|
2948
|
+
error "Credential not found with ID: #{credential_id}"
|
|
2949
|
+
say ""
|
|
2950
|
+
say "Run 'mysigner gp-credential list' to see available IDs", :yellow
|
|
2951
|
+
exit 1
|
|
2952
|
+
rescue Mysigner::ClientError => e
|
|
2953
|
+
error "Delete failed: #{e.message}"
|
|
2954
|
+
exit 1
|
|
2955
|
+
end
|
|
2956
|
+
else
|
|
2957
|
+
say "Deletion cancelled", :yellow
|
|
2958
|
+
end
|
|
2959
|
+
|
|
2960
|
+
when 'activate'
|
|
2961
|
+
credential_id = args[0]
|
|
2962
|
+
|
|
2963
|
+
unless credential_id
|
|
2964
|
+
error "Usage: mysigner gp-credential activate ID"
|
|
2965
|
+
say ""
|
|
2966
|
+
say "Run 'mysigner gp-credential list' to see available IDs", :yellow
|
|
2967
|
+
exit 1
|
|
2968
|
+
end
|
|
2969
|
+
|
|
2970
|
+
say "🔑 Activating credential...", :cyan
|
|
2971
|
+
|
|
2972
|
+
begin
|
|
2973
|
+
response = client.post("/api/v1/organizations/#{config.current_organization_id}/google_play_credentials/#{credential_id}/activate")
|
|
2974
|
+
credential = response[:data]['google_play_credential'] || response[:data]
|
|
2975
|
+
say "✓ Credential activated!", :green
|
|
2976
|
+
say ""
|
|
2977
|
+
say "#{credential['name']} is now the active Google Play credential", :cyan
|
|
2978
|
+
rescue Mysigner::NotFoundError
|
|
2979
|
+
error "Credential not found with ID: #{credential_id}"
|
|
2980
|
+
say ""
|
|
2981
|
+
say "Run 'mysigner gp-credential list' to see available IDs", :yellow
|
|
2982
|
+
exit 1
|
|
2983
|
+
rescue Mysigner::ClientError => e
|
|
2984
|
+
error "Activation failed: #{e.message}"
|
|
2985
|
+
exit 1
|
|
2986
|
+
end
|
|
2987
|
+
|
|
2988
|
+
when 'test'
|
|
2989
|
+
credential_id = args[0]
|
|
2990
|
+
|
|
2991
|
+
unless credential_id
|
|
2992
|
+
error "Usage: mysigner gp-credential test ID"
|
|
2993
|
+
say ""
|
|
2994
|
+
say "Run 'mysigner gp-credential list' to see available IDs", :yellow
|
|
2995
|
+
exit 1
|
|
2996
|
+
end
|
|
2997
|
+
|
|
2998
|
+
say "🔑 Testing credential connection...", :cyan
|
|
2999
|
+
say ""
|
|
3000
|
+
|
|
3001
|
+
begin
|
|
3002
|
+
response = client.post("/api/v1/organizations/#{config.current_organization_id}/google_play_credentials/#{credential_id}/test")
|
|
3003
|
+
result = response[:data]
|
|
3004
|
+
|
|
3005
|
+
if result['success']
|
|
3006
|
+
say "✓ Connection successful!", :green
|
|
3007
|
+
say ""
|
|
3008
|
+
say " Google Play API is reachable with this credential", :cyan
|
|
3009
|
+
else
|
|
3010
|
+
say "✗ Connection failed", :red
|
|
3011
|
+
say ""
|
|
3012
|
+
say " Error: #{result['error']}", :red if result['error']
|
|
3013
|
+
say ""
|
|
3014
|
+
say "💡 Check that:", :cyan
|
|
3015
|
+
say " → The service account JSON key is valid", :yellow
|
|
3016
|
+
say " → The service account has Google Play Console access", :yellow
|
|
3017
|
+
say " → API access is enabled in Google Play Console", :yellow
|
|
3018
|
+
exit 1
|
|
3019
|
+
end
|
|
3020
|
+
rescue Mysigner::NotFoundError
|
|
3021
|
+
error "Credential not found with ID: #{credential_id}"
|
|
3022
|
+
say ""
|
|
3023
|
+
say "Run 'mysigner gp-credential list' to see available IDs", :yellow
|
|
3024
|
+
exit 1
|
|
3025
|
+
rescue Mysigner::ClientError => e
|
|
3026
|
+
error "Test failed: #{e.message}"
|
|
3027
|
+
exit 1
|
|
3028
|
+
end
|
|
3029
|
+
|
|
3030
|
+
when 'help'
|
|
3031
|
+
invoke :help, ['gp-credential']
|
|
3032
|
+
else
|
|
3033
|
+
error "Unknown action: #{action}"
|
|
3034
|
+
say "Available actions: list, delete, activate, test, help", :yellow
|
|
3035
|
+
exit 1
|
|
3036
|
+
end
|
|
3037
|
+
end
|
|
3038
|
+
|
|
3039
|
+
# ==================== APP STORE RELEASES ====================
|
|
3040
|
+
|
|
3041
|
+
desc "release SUBCOMMAND", "Manage App Store release configurations (list, show, create, update)"
|
|
3042
|
+
long_desc <<~DESC
|
|
3043
|
+
Manage App Store release configurations for iOS app distribution.
|
|
3044
|
+
|
|
3045
|
+
Release configurations control how your app is submitted and released on the
|
|
3046
|
+
App Store, including auto-submit, phased release, and release notes.
|
|
3047
|
+
|
|
3048
|
+
SUBCOMMANDS:
|
|
3049
|
+
|
|
3050
|
+
mysigner release list [--bundle-id BUNDLE_ID]
|
|
3051
|
+
List all release configurations
|
|
3052
|
+
|
|
3053
|
+
mysigner release show ID
|
|
3054
|
+
Show details for a release configuration
|
|
3055
|
+
|
|
3056
|
+
mysigner release create --bundle-id-id ID [OPTIONS]
|
|
3057
|
+
Create a new release configuration
|
|
3058
|
+
|
|
3059
|
+
mysigner release update ID [OPTIONS]
|
|
3060
|
+
Update an existing release configuration
|
|
3061
|
+
|
|
3062
|
+
OPTIONS FOR CREATE/UPDATE:
|
|
3063
|
+
|
|
3064
|
+
--bundle-id-id ID Bundle ID record ID (required for create)
|
|
3065
|
+
--whats-new TEXT Release notes / What's New text
|
|
3066
|
+
--support-url URL Support URL
|
|
3067
|
+
--marketing-url URL Marketing URL
|
|
3068
|
+
--privacy-url URL Privacy policy URL
|
|
3069
|
+
--auto-submit Auto-submit for review after upload
|
|
3070
|
+
--phased-release Enable phased release (7-day rollout)
|
|
3071
|
+
--release-type TYPE Release type: manual, after_approval, scheduled
|
|
3072
|
+
--scheduled-date DATE Scheduled release date (ISO 8601)
|
|
3073
|
+
|
|
3074
|
+
EXAMPLES:
|
|
3075
|
+
|
|
3076
|
+
# List all release configs
|
|
3077
|
+
mysigner release list
|
|
3078
|
+
|
|
3079
|
+
# List releases for a specific bundle ID
|
|
3080
|
+
mysigner release list --bundle-id com.example.app
|
|
3081
|
+
|
|
3082
|
+
# Show details
|
|
3083
|
+
mysigner release show 1
|
|
3084
|
+
|
|
3085
|
+
# Create a release config
|
|
3086
|
+
mysigner release create --bundle-id-id 42 --auto-submit --phased-release
|
|
3087
|
+
|
|
3088
|
+
# Update release notes
|
|
3089
|
+
mysigner release update 1 --whats-new "Bug fixes and improvements"
|
|
3090
|
+
|
|
3091
|
+
# Set up scheduled release
|
|
3092
|
+
mysigner release update 1 --release-type scheduled --scheduled-date 2025-03-01T10:00:00Z
|
|
3093
|
+
DESC
|
|
3094
|
+
method_option :bundle_id, type: :string, aliases: '-b', desc: 'Filter by bundle identifier'
|
|
3095
|
+
method_option :bundle_id_id, type: :numeric, desc: 'Bundle ID record ID (for create)'
|
|
3096
|
+
method_option :whats_new, type: :string, desc: "What's New text"
|
|
3097
|
+
method_option :support_url, type: :string, desc: 'Support URL'
|
|
3098
|
+
method_option :marketing_url, type: :string, desc: 'Marketing URL'
|
|
3099
|
+
method_option :privacy_url, type: :string, desc: 'Privacy policy URL'
|
|
3100
|
+
method_option :auto_submit, type: :boolean, desc: 'Auto-submit for review'
|
|
3101
|
+
method_option :phased_release, type: :boolean, desc: 'Enable phased release'
|
|
3102
|
+
method_option :release_type, type: :string, desc: 'Release type: manual, after_approval, scheduled'
|
|
3103
|
+
method_option :scheduled_date, type: :string, desc: 'Scheduled release date (ISO 8601)'
|
|
3104
|
+
def release(action, *args)
|
|
3105
|
+
config = load_config
|
|
3106
|
+
client = create_client(config)
|
|
3107
|
+
|
|
3108
|
+
case action
|
|
3109
|
+
when 'list'
|
|
3110
|
+
say "🚀 App Store Releases", :cyan
|
|
3111
|
+
say ""
|
|
3112
|
+
|
|
3113
|
+
params = {}
|
|
3114
|
+
params[:bundle_id] = options[:bundle_id] if options[:bundle_id]
|
|
3115
|
+
|
|
3116
|
+
begin
|
|
3117
|
+
response = client.get(
|
|
3118
|
+
"/api/v1/organizations/#{config.current_organization_id}/app_store_releases",
|
|
3119
|
+
params: params
|
|
3120
|
+
)
|
|
3121
|
+
releases = response[:data]['app_store_releases'] || []
|
|
3122
|
+
|
|
3123
|
+
if releases.empty?
|
|
3124
|
+
say "No release configurations found", :yellow
|
|
3125
|
+
say ""
|
|
3126
|
+
say "Create one with: mysigner release create --bundle-id-id ID", :yellow
|
|
3127
|
+
return
|
|
3128
|
+
end
|
|
3129
|
+
|
|
3130
|
+
releases.each do |rel|
|
|
3131
|
+
say " • #{rel['app_name'] || rel['bundle_identifier']} (ID: #{rel['id']})", :green
|
|
3132
|
+
say " Bundle ID: #{rel['bundle_identifier']}" if rel['bundle_identifier']
|
|
3133
|
+
say " Release Type: #{rel['release_type'] || 'N/A'}"
|
|
3134
|
+
say " Auto Submit: #{rel['auto_submit'] ? 'Yes' : 'No'}"
|
|
3135
|
+
say " Phased Release: #{rel['phased_release'] ? 'Yes' : 'No'}"
|
|
3136
|
+
say " Version: #{rel['version_string']}" if rel['version_string']
|
|
3137
|
+
say ""
|
|
3138
|
+
end
|
|
3139
|
+
|
|
3140
|
+
say "Total: #{releases.count} release(s)", :yellow
|
|
3141
|
+
rescue Mysigner::ClientError => e
|
|
3142
|
+
error "Failed to fetch releases: #{e.message}"
|
|
3143
|
+
exit 1
|
|
3144
|
+
end
|
|
3145
|
+
|
|
3146
|
+
when 'show'
|
|
3147
|
+
release_id = args[0]
|
|
3148
|
+
|
|
3149
|
+
unless release_id
|
|
3150
|
+
error "Usage: mysigner release show ID"
|
|
3151
|
+
say ""
|
|
3152
|
+
say "Run 'mysigner release list' to see available IDs", :yellow
|
|
3153
|
+
exit 1
|
|
3154
|
+
end
|
|
3155
|
+
|
|
3156
|
+
begin
|
|
3157
|
+
response = client.get("/api/v1/organizations/#{config.current_organization_id}/app_store_releases/#{release_id}")
|
|
3158
|
+
rel = response[:data]['app_store_release'] || response[:data]
|
|
3159
|
+
|
|
3160
|
+
say "🚀 Release Configuration (ID: #{rel['id']})", :cyan
|
|
3161
|
+
say ""
|
|
3162
|
+
say "Details:", :bold
|
|
3163
|
+
say " App Name: #{rel['app_name'] || 'N/A'}"
|
|
3164
|
+
say " Bundle ID: #{rel['bundle_identifier'] || 'N/A'}"
|
|
3165
|
+
say " Version: #{rel['version_string'] || 'N/A'}"
|
|
3166
|
+
say " Release Type: #{rel['release_type'] || 'N/A'}"
|
|
3167
|
+
say " Auto Submit: #{rel['auto_submit'] ? 'Yes' : 'No'}"
|
|
3168
|
+
say " Phased Release: #{rel['phased_release'] ? 'Yes' : 'No'}"
|
|
3169
|
+
say ""
|
|
3170
|
+
if rel['whats_new'] && !rel['whats_new'].empty?
|
|
3171
|
+
say "What's New:", :bold
|
|
3172
|
+
say " #{rel['whats_new']}"
|
|
3173
|
+
say ""
|
|
3174
|
+
end
|
|
3175
|
+
say "URLs:", :bold
|
|
3176
|
+
say " Support: #{rel['support_url'] || 'N/A'}"
|
|
3177
|
+
say " Marketing: #{rel['marketing_url'] || 'N/A'}"
|
|
3178
|
+
say " Privacy: #{rel['privacy_url'] || 'N/A'}"
|
|
3179
|
+
if rel['scheduled_date']
|
|
3180
|
+
say ""
|
|
3181
|
+
say "Scheduled Date: #{rel['scheduled_date']}"
|
|
3182
|
+
end
|
|
3183
|
+
rescue Mysigner::NotFoundError
|
|
3184
|
+
error "Release not found with ID: #{release_id}"
|
|
3185
|
+
say ""
|
|
3186
|
+
say "Run 'mysigner release list' to see available IDs", :yellow
|
|
3187
|
+
exit 1
|
|
3188
|
+
rescue Mysigner::ClientError => e
|
|
3189
|
+
error "Failed to fetch release: #{e.message}"
|
|
3190
|
+
exit 1
|
|
3191
|
+
end
|
|
3192
|
+
|
|
3193
|
+
when 'create'
|
|
3194
|
+
say "🚀 Creating release configuration...", :cyan
|
|
3195
|
+
say ""
|
|
3196
|
+
|
|
3197
|
+
body = {}
|
|
3198
|
+
body[:bundle_id_id] = options[:bundle_id_id] if options[:bundle_id_id]
|
|
3199
|
+
body[:whats_new] = options[:whats_new] if options[:whats_new]
|
|
3200
|
+
body[:support_url] = options[:support_url] if options[:support_url]
|
|
3201
|
+
body[:marketing_url] = options[:marketing_url] if options[:marketing_url]
|
|
3202
|
+
body[:privacy_url] = options[:privacy_url] if options[:privacy_url]
|
|
3203
|
+
body[:auto_submit] = options[:auto_submit] unless options[:auto_submit].nil?
|
|
3204
|
+
body[:phased_release] = options[:phased_release] unless options[:phased_release].nil?
|
|
3205
|
+
body[:release_type] = options[:release_type] if options[:release_type]
|
|
3206
|
+
body[:scheduled_date] = options[:scheduled_date] if options[:scheduled_date]
|
|
3207
|
+
|
|
3208
|
+
begin
|
|
3209
|
+
response = client.post(
|
|
3210
|
+
"/api/v1/organizations/#{config.current_organization_id}/app_store_releases",
|
|
3211
|
+
body: body
|
|
3212
|
+
)
|
|
3213
|
+
rel = response[:data]['app_store_release'] || response[:data]
|
|
3214
|
+
|
|
3215
|
+
say "✓ Release configuration created!", :green
|
|
3216
|
+
say ""
|
|
3217
|
+
say "Details:", :bold
|
|
3218
|
+
say " ID: #{rel['id']}"
|
|
3219
|
+
say " Bundle ID: #{rel['bundle_identifier'] || 'N/A'}"
|
|
3220
|
+
say " Release Type: #{rel['release_type'] || 'N/A'}"
|
|
3221
|
+
say " Auto Submit: #{rel['auto_submit'] ? 'Yes' : 'No'}"
|
|
3222
|
+
say " Phased Release: #{rel['phased_release'] ? 'Yes' : 'No'}"
|
|
3223
|
+
rescue Mysigner::ValidationError => e
|
|
3224
|
+
if e.message.include?('already exists') || (e.error_code && e.error_code.to_s == '409')
|
|
3225
|
+
error "Release configuration already exists for this bundle ID"
|
|
3226
|
+
say ""
|
|
3227
|
+
say "💡 Use 'mysigner release update ID' to modify it", :cyan
|
|
3228
|
+
say " Run 'mysigner release list' to find the ID", :yellow
|
|
3229
|
+
else
|
|
3230
|
+
error "Validation failed: #{e.message}"
|
|
3231
|
+
if e.details
|
|
3232
|
+
e.details.each do |field, errors|
|
|
3233
|
+
errors_text = errors.is_a?(Array) ? errors.join(', ') : errors.to_s
|
|
3234
|
+
say " #{field}: #{errors_text}", :red
|
|
3235
|
+
end
|
|
3236
|
+
end
|
|
3237
|
+
end
|
|
3238
|
+
exit 1
|
|
3239
|
+
rescue Mysigner::ClientError => e
|
|
3240
|
+
if e.message.include?('409') || e.message.include?('already exists')
|
|
3241
|
+
error "Release configuration already exists for this bundle ID"
|
|
3242
|
+
say ""
|
|
3243
|
+
say "💡 Use 'mysigner release update ID' to modify it", :cyan
|
|
3244
|
+
say " Run 'mysigner release list' to find the ID", :yellow
|
|
3245
|
+
else
|
|
3246
|
+
error "Failed to create release: #{e.message}"
|
|
3247
|
+
end
|
|
3248
|
+
exit 1
|
|
3249
|
+
end
|
|
3250
|
+
|
|
3251
|
+
when 'update'
|
|
3252
|
+
release_id = args[0]
|
|
3253
|
+
|
|
3254
|
+
unless release_id
|
|
3255
|
+
error "Usage: mysigner release update ID [OPTIONS]"
|
|
3256
|
+
say ""
|
|
3257
|
+
say "Run 'mysigner release list' to see available IDs", :yellow
|
|
3258
|
+
exit 1
|
|
3259
|
+
end
|
|
3260
|
+
|
|
3261
|
+
body = {}
|
|
3262
|
+
body[:whats_new] = options[:whats_new] if options[:whats_new]
|
|
3263
|
+
body[:support_url] = options[:support_url] if options[:support_url]
|
|
3264
|
+
body[:marketing_url] = options[:marketing_url] if options[:marketing_url]
|
|
3265
|
+
body[:privacy_url] = options[:privacy_url] if options[:privacy_url]
|
|
3266
|
+
body[:auto_submit] = options[:auto_submit] unless options[:auto_submit].nil?
|
|
3267
|
+
body[:phased_release] = options[:phased_release] unless options[:phased_release].nil?
|
|
3268
|
+
body[:release_type] = options[:release_type] if options[:release_type]
|
|
3269
|
+
body[:scheduled_date] = options[:scheduled_date] if options[:scheduled_date]
|
|
3270
|
+
|
|
3271
|
+
say "🚀 Updating release configuration...", :cyan
|
|
3272
|
+
say ""
|
|
3273
|
+
|
|
3274
|
+
begin
|
|
3275
|
+
response = client.patch(
|
|
3276
|
+
"/api/v1/organizations/#{config.current_organization_id}/app_store_releases/#{release_id}",
|
|
3277
|
+
body: body
|
|
3278
|
+
)
|
|
3279
|
+
rel = response[:data]['app_store_release'] || response[:data]
|
|
3280
|
+
|
|
3281
|
+
say "✓ Release configuration updated!", :green
|
|
3282
|
+
say ""
|
|
3283
|
+
say "Details:", :bold
|
|
3284
|
+
say " ID: #{rel['id']}"
|
|
3285
|
+
say " Bundle ID: #{rel['bundle_identifier'] || 'N/A'}"
|
|
3286
|
+
say " Release Type: #{rel['release_type'] || 'N/A'}"
|
|
3287
|
+
say " Auto Submit: #{rel['auto_submit'] ? 'Yes' : 'No'}"
|
|
3288
|
+
say " Phased Release: #{rel['phased_release'] ? 'Yes' : 'No'}"
|
|
3289
|
+
rescue Mysigner::NotFoundError
|
|
3290
|
+
error "Release not found with ID: #{release_id}"
|
|
3291
|
+
say ""
|
|
3292
|
+
say "Run 'mysigner release list' to see available IDs", :yellow
|
|
3293
|
+
exit 1
|
|
3294
|
+
rescue Mysigner::ValidationError => e
|
|
3295
|
+
error "Validation failed: #{e.message}"
|
|
3296
|
+
if e.details
|
|
3297
|
+
e.details.each do |field, errors|
|
|
3298
|
+
errors_text = errors.is_a?(Array) ? errors.join(', ') : errors.to_s
|
|
3299
|
+
say " #{field}: #{errors_text}", :red
|
|
3300
|
+
end
|
|
3301
|
+
end
|
|
3302
|
+
exit 1
|
|
3303
|
+
rescue Mysigner::ClientError => e
|
|
3304
|
+
error "Failed to update release: #{e.message}"
|
|
3305
|
+
exit 1
|
|
3306
|
+
end
|
|
3307
|
+
|
|
3308
|
+
when 'help'
|
|
3309
|
+
invoke :help, ['release']
|
|
3310
|
+
else
|
|
3311
|
+
error "Unknown action: #{action}"
|
|
3312
|
+
say "Available actions: list, show, create, update, help", :yellow
|
|
3313
|
+
exit 1
|
|
3314
|
+
end
|
|
3315
|
+
end
|
|
2850
3316
|
end
|
|
2851
3317
|
end
|
|
2852
3318
|
end
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
module Mysigner
|
|
2
|
+
class CLI < Thor
|
|
3
|
+
module ValidateCommands
|
|
4
|
+
def self.included(base)
|
|
5
|
+
base.class_eval do
|
|
6
|
+
desc "validate", "Validate signing configuration on the server"
|
|
7
|
+
long_desc <<~DESC
|
|
8
|
+
Check if your bundle ID, certificate, and provisioning profile exist and
|
|
9
|
+
are valid on the My Signer server.
|
|
10
|
+
|
|
11
|
+
WHY VALIDATE?
|
|
12
|
+
|
|
13
|
+
The CLI does local keychain/certificate validation, but doesn't check if
|
|
14
|
+
your signing assets exist on the server. This catches "forgot to sync"
|
|
15
|
+
or "profile expired" errors before a build starts.
|
|
16
|
+
|
|
17
|
+
OPTIONS:
|
|
18
|
+
|
|
19
|
+
--bundle-id / -b Bundle identifier (e.g., com.example.app)
|
|
20
|
+
Auto-detected from Xcode project if not provided
|
|
21
|
+
|
|
22
|
+
--type / -t Signing type: development, appstore, adhoc, inhouse
|
|
23
|
+
|
|
24
|
+
EXAMPLES:
|
|
25
|
+
|
|
26
|
+
# Validate development signing for an app
|
|
27
|
+
mysigner validate --bundle-id com.example.app --type development
|
|
28
|
+
|
|
29
|
+
# Validate App Store signing
|
|
30
|
+
mysigner validate -b com.example.app -t appstore
|
|
31
|
+
|
|
32
|
+
# Auto-detect bundle ID from current project
|
|
33
|
+
mysigner validate --type development
|
|
34
|
+
DESC
|
|
35
|
+
method_option :bundle_id, type: :string, aliases: '-b', desc: 'Bundle identifier (e.g., com.example.app)'
|
|
36
|
+
method_option :type, type: :string, aliases: '-t', desc: 'Signing type: development, appstore, adhoc, inhouse'
|
|
37
|
+
def validate
|
|
38
|
+
config = load_config
|
|
39
|
+
client = create_client(config)
|
|
40
|
+
|
|
41
|
+
bundle_id = options[:bundle_id] || detect_bundle_id_from_project
|
|
42
|
+
signing_type = options[:type]
|
|
43
|
+
|
|
44
|
+
unless bundle_id
|
|
45
|
+
error "Bundle ID is required. Use --bundle-id or run from an Xcode project directory."
|
|
46
|
+
say ""
|
|
47
|
+
say "Example: mysigner validate --bundle-id com.example.app --type development", :yellow
|
|
48
|
+
exit 1
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
unless signing_type
|
|
52
|
+
error "Signing type is required. Use --type with one of: development, appstore, adhoc, inhouse"
|
|
53
|
+
say ""
|
|
54
|
+
say "Example: mysigner validate --bundle-id #{bundle_id} --type development", :yellow
|
|
55
|
+
exit 1
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
valid_types = %w[development appstore adhoc inhouse]
|
|
59
|
+
unless valid_types.include?(signing_type)
|
|
60
|
+
error "Invalid signing type: #{signing_type}"
|
|
61
|
+
say "Valid types: #{valid_types.join(', ')}", :yellow
|
|
62
|
+
exit 1
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
say "🔍 Validating signing configuration...", :cyan
|
|
66
|
+
say ""
|
|
67
|
+
say " Bundle ID: #{bundle_id}", :white
|
|
68
|
+
say " Type: #{signing_type}", :white
|
|
69
|
+
say ""
|
|
70
|
+
|
|
71
|
+
begin
|
|
72
|
+
response = client.post(
|
|
73
|
+
"/api/v1/organizations/#{config.current_organization_id}/validate",
|
|
74
|
+
body: {
|
|
75
|
+
bundle_id: bundle_id,
|
|
76
|
+
type: signing_type
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
result = response[:data]
|
|
81
|
+
checks = result['checks'] || {}
|
|
82
|
+
valid = result['valid']
|
|
83
|
+
|
|
84
|
+
# Display each check
|
|
85
|
+
%w[bundle_id certificate profile].each do |check_name|
|
|
86
|
+
check = checks[check_name]
|
|
87
|
+
next unless check
|
|
88
|
+
|
|
89
|
+
if check['status'] == 'pass'
|
|
90
|
+
say " ✓ #{check_name.tr('_', ' ').capitalize}: #{check['message']}", :green
|
|
91
|
+
else
|
|
92
|
+
say " ✗ #{check_name.tr('_', ' ').capitalize}: #{check['message']}", :red
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
say ""
|
|
97
|
+
|
|
98
|
+
if valid
|
|
99
|
+
say "✓ All checks passed! Signing configuration is valid.", :green
|
|
100
|
+
else
|
|
101
|
+
say "✗ Validation failed. Some checks did not pass.", :red
|
|
102
|
+
|
|
103
|
+
suggestions = result['suggestions'] || []
|
|
104
|
+
if suggestions.any?
|
|
105
|
+
say ""
|
|
106
|
+
say "💡 Suggestions:", :cyan
|
|
107
|
+
suggestions.each do |suggestion|
|
|
108
|
+
say " → #{suggestion}", :yellow
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
exit 1
|
|
113
|
+
end
|
|
114
|
+
rescue Mysigner::NotFoundError => e
|
|
115
|
+
error "Not found: #{e.message}"
|
|
116
|
+
say ""
|
|
117
|
+
say "💡 Make sure your bundle ID is synced:", :cyan
|
|
118
|
+
say " → Run 'mysigner sync ios' to sync from Apple Developer Portal", :yellow
|
|
119
|
+
say " → Run 'mysigner bundleid list' to list registered bundle IDs", :yellow
|
|
120
|
+
exit 1
|
|
121
|
+
rescue Mysigner::ValidationError => e
|
|
122
|
+
error "Validation error: #{e.message}"
|
|
123
|
+
if e.details
|
|
124
|
+
e.details.each do |field, errors|
|
|
125
|
+
errors_text = errors.is_a?(Array) ? errors.join(', ') : errors.to_s
|
|
126
|
+
say " #{field}: #{errors_text}", :red
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
exit 1
|
|
130
|
+
rescue Mysigner::ClientError => e
|
|
131
|
+
error "Validation request failed: #{e.message}"
|
|
132
|
+
say ""
|
|
133
|
+
say "💡 Try these steps:", :cyan
|
|
134
|
+
say " → Check your network connection", :yellow
|
|
135
|
+
say " → Verify API token: mysigner status", :yellow
|
|
136
|
+
exit 1
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
private
|
|
141
|
+
|
|
142
|
+
def detect_bundle_id_from_project
|
|
143
|
+
# Try to find bundle ID from Xcode project in current directory
|
|
144
|
+
pbxproj_files = Dir.glob('**/*.pbxproj')
|
|
145
|
+
return nil if pbxproj_files.empty?
|
|
146
|
+
|
|
147
|
+
pbxproj_files.each do |file|
|
|
148
|
+
content = File.read(file)
|
|
149
|
+
match = content.match(/PRODUCT_BUNDLE_IDENTIFIER\s*=\s*"?([^;"]+)"?/)
|
|
150
|
+
return match[1].strip if match
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
nil
|
|
154
|
+
rescue StandardError
|
|
155
|
+
nil
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
data/lib/mysigner/cli.rb
CHANGED
|
@@ -11,6 +11,7 @@ require_relative 'cli/auth_commands'
|
|
|
11
11
|
require_relative 'cli/diagnostic_commands'
|
|
12
12
|
require_relative 'cli/build_commands'
|
|
13
13
|
require_relative 'cli/resource_commands'
|
|
14
|
+
require_relative 'cli/validate_commands'
|
|
14
15
|
|
|
15
16
|
module Mysigner
|
|
16
17
|
class CLI < Thor
|
|
@@ -31,6 +32,7 @@ module Mysigner
|
|
|
31
32
|
include DiagnosticCommands
|
|
32
33
|
include BuildCommands
|
|
33
34
|
include ResourceCommands
|
|
35
|
+
include ValidateCommands
|
|
34
36
|
|
|
35
37
|
# Command aliases for power users
|
|
36
38
|
map 's' => :ship
|
data/lib/mysigner/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mysigner
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jurgen Leka
|
|
@@ -234,6 +234,7 @@ files:
|
|
|
234
234
|
- lib/mysigner/cli/concerns/helpers.rb
|
|
235
235
|
- lib/mysigner/cli/diagnostic_commands.rb
|
|
236
236
|
- lib/mysigner/cli/resource_commands.rb
|
|
237
|
+
- lib/mysigner/cli/validate_commands.rb
|
|
237
238
|
- lib/mysigner/client.rb
|
|
238
239
|
- lib/mysigner/config.rb
|
|
239
240
|
- lib/mysigner/export/exporter.rb
|