6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
# File 'lib/mysigner/cli/validate_commands.rb', line 6
def self.included(base)
base.class_eval do
desc 'validate', 'Validate signing configuration on the server'
long_desc <<~DESC
Check if your bundle ID, certificate, and provisioning profile exist and
are valid on the My Signer server.
WHY VALIDATE?
The CLI does local keychain/certificate validation, but doesn't check if
your signing assets exist on the server. This catches "forgot to sync"
or "profile expired" errors before a build starts.
OPTIONS:
--bundle-id / -b Bundle identifier (e.g., com.example.app)
Auto-detected from Xcode project if not provided
--type / -t Signing type: development, appstore, adhoc, inhouse
EXAMPLES:
# Validate development signing for an app
mysigner validate --bundle-id com.example.app --type development
# Validate App Store signing
mysigner validate -b com.example.app -t appstore
# Auto-detect bundle ID from current project
mysigner validate --type development
DESC
method_option :bundle_id, type: :string, aliases: '-b', desc: 'Bundle identifier (e.g., com.example.app)'
method_option :type, type: :string, aliases: '-t', desc: 'Signing type: development, appstore, adhoc, inhouse'
def validate
config = load_config
client = create_client(config)
bundle_id = options[:bundle_id] || detect_bundle_id_from_project
signing_type = options[:type]
unless bundle_id
error 'Bundle ID is required. Use --bundle-id or run from an Xcode project directory.'
say ''
say 'Example: mysigner validate --bundle-id com.example.app --type development', :yellow
exit 1
end
unless signing_type
error 'Signing type is required. Use --type with one of: development, appstore, adhoc, inhouse'
say ''
say "Example: mysigner validate --bundle-id #{bundle_id} --type development", :yellow
exit 1
end
valid_types = %w[development appstore adhoc inhouse]
unless valid_types.include?(signing_type)
error "Invalid signing type: #{signing_type}"
say "Valid types: #{valid_types.join(', ')}", :yellow
exit 1
end
say '🔍 Validating signing configuration...', :cyan
say ''
say " Bundle ID: #{bundle_id}", :white
say " Type: #{signing_type}", :white
say ''
begin
response = client.post(
"/api/v1/organizations/#{config.current_organization_id}/validate",
body: {
bundle_id: bundle_id,
type: signing_type
}
)
result = response[:data]
checks = result['checks'] || {}
valid = result['valid']
%w[bundle_id certificate profile].each do |check_name|
check = checks[check_name]
next unless check
if check['status'] == 'pass'
say " ✓ #{check_name.tr('_', ' ').capitalize}: #{check['message']}", :green
else
say " ✗ #{check_name.tr('_', ' ').capitalize}: #{check['message']}", :red
end
end
say ''
if valid
say '✓ All checks passed! Signing configuration is valid.', :green
else
say '✗ Validation failed. Some checks did not pass.', :red
suggestions = result['suggestions'] || []
if suggestions.any?
say ''
say '💡 Suggestions:', :cyan
suggestions.each do |suggestion|
say " → #{suggestion}", :yellow
end
end
exit 1
end
rescue Mysigner::NotFoundError => e
error "Not found: #{e.message}"
say ''
say '💡 Make sure your bundle ID is synced:', :cyan
say " → Run 'mysigner sync ios' to sync from Apple Developer Portal", :yellow
say " → Run 'mysigner bundleid list' to list registered bundle IDs", :yellow
exit 1
rescue Mysigner::ValidationError => e
error "Validation error: #{e.message}"
e.details&.each do |field, errors|
errors_text = errors.is_a?(Array) ? errors.join(', ') : errors.to_s
say " #{field}: #{errors_text}", :red
end
exit 1
rescue Mysigner::ClientError => e
error "Validation request failed: #{e.message}"
say ''
say '💡 Try these steps:', :cyan
say ' → Check your network connection', :yellow
say ' → Verify API token: mysigner status', :yellow
exit 1
end
end
private
def detect_bundle_id_from_project
pbxproj_files = Dir.glob('**/*.pbxproj')
return nil if pbxproj_files.empty?
pbxproj_files.each do |file|
content = File.read(file)
match = content.match(/PRODUCT_BUNDLE_IDENTIFIER\s*=\s*"?([^;"]+)"?/)
return match[1].strip if match
end
nil
rescue StandardError
nil
end
end
end
|