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.
- checksums.yaml +4 -4
- data/.githooks/pre-commit +15 -0
- data/.githooks/pre-push +21 -0
- data/.github/workflows/ci.yml +29 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +55 -0
- data/.rubocop_todo.yml +126 -0
- data/CHANGELOG.md +96 -0
- data/Gemfile +5 -3
- data/Gemfile.lock +38 -8
- data/README.md +14 -16
- data/Rakefile +5 -3
- data/bin/console +4 -3
- data/bin/setup +3 -0
- data/certificate_.cer +0 -0
- data/exe/mysigner +19 -2
- data/iOS_App_Store_Profile.mobileprovision +1 -0
- data/iOS_Distribution_Certificate.cer +1 -0
- data/lib/mysigner/build/android_executor.rb +83 -63
- data/lib/mysigner/build/android_parser.rb +33 -40
- data/lib/mysigner/build/configurator.rb +17 -16
- data/lib/mysigner/build/detector.rb +39 -50
- data/lib/mysigner/build/error_analyzer.rb +70 -68
- data/lib/mysigner/build/executor.rb +30 -37
- data/lib/mysigner/build/parser.rb +18 -18
- data/lib/mysigner/cleanup/private_keys_purger.rb +41 -0
- data/lib/mysigner/cli/auth_commands.rb +771 -764
- data/lib/mysigner/cli/build_commands.rb +962 -796
- data/lib/mysigner/cli/concerns/actionable_suggestions.rb +208 -154
- data/lib/mysigner/cli/concerns/api_helpers.rb +46 -54
- data/lib/mysigner/cli/concerns/error_handlers.rb +247 -237
- data/lib/mysigner/cli/concerns/helpers.rb +44 -1
- data/lib/mysigner/cli/diagnostic_commands.rb +667 -636
- data/lib/mysigner/cli/resource_commands.rb +1153 -985
- data/lib/mysigner/cli/validate_commands.rb +25 -25
- data/lib/mysigner/cli.rb +11 -1
- data/lib/mysigner/client.rb +27 -19
- data/lib/mysigner/config.rb +161 -60
- data/lib/mysigner/export/exporter.rb +38 -37
- data/lib/mysigner/signing/certificate_checker.rb +18 -23
- data/lib/mysigner/signing/gradle_signing_injector.rb +67 -0
- data/lib/mysigner/signing/keystore_manager.rb +81 -61
- data/lib/mysigner/signing/validator.rb +38 -40
- data/lib/mysigner/signing/wizard.rb +329 -342
- data/lib/mysigner/upload/app_store_automation.rb +96 -49
- data/lib/mysigner/upload/app_store_submission.rb +87 -92
- data/lib/mysigner/upload/asc_rest_uploader.rb +119 -0
- data/lib/mysigner/upload/play_store_uploader.rb +164 -144
- data/lib/mysigner/upload/uploader.rb +136 -115
- data/lib/mysigner/version.rb +3 -1
- data/lib/mysigner.rb +13 -11
- data/mysigner.gemspec +36 -33
- data/profile_.mobileprovision +0 -0
- data/test_manual.rb +37 -36
- metadata +44 -17
- data/.DS_Store +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Mysigner
|
|
2
4
|
module Build
|
|
3
5
|
class Detector
|
|
@@ -9,9 +11,7 @@ module Mysigner
|
|
|
9
11
|
# @param platform [Symbol, nil] Force detection for specific platform (:ios, :android, or nil for auto-detect iOS)
|
|
10
12
|
def self.detect(directory = Dir.pwd, platform: nil)
|
|
11
13
|
# If platform is explicitly android, detect android
|
|
12
|
-
if platform == :android
|
|
13
|
-
return detect_android(directory)
|
|
14
|
-
end
|
|
14
|
+
return detect_android(directory) if platform == :android
|
|
15
15
|
|
|
16
16
|
# Default behavior: detect iOS (backwards compatible)
|
|
17
17
|
detect_ios(directory)
|
|
@@ -21,7 +21,7 @@ module Mysigner
|
|
|
21
21
|
# Returns: { platform: :android, type: :gradle, path: String, framework: :capacitor/:react_native/:flutter/:native }
|
|
22
22
|
def self.detect_android(directory = Dir.pwd)
|
|
23
23
|
# 1. Check for Capacitor (most specific first)
|
|
24
|
-
if File.exist?("#{directory}/capacitor.config.json") ||
|
|
24
|
+
if File.exist?("#{directory}/capacitor.config.json") ||
|
|
25
25
|
File.exist?("#{directory}/capacitor.config.ts")
|
|
26
26
|
return detect_capacitor_android(directory)
|
|
27
27
|
end
|
|
@@ -34,21 +34,21 @@ module Mysigner
|
|
|
34
34
|
# Auto-run expo prebuild
|
|
35
35
|
puts "\nš¦ Expo managed workflow detected (no android/ folder)"
|
|
36
36
|
puts "š§ Running: npx expo prebuild --platform android\n\n"
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
result = system("cd #{directory} && npx expo prebuild --platform android")
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
unless result && Dir.exist?("#{directory}/android")
|
|
41
41
|
raise NoProjectError, <<~ERROR
|
|
42
42
|
Failed to generate Android project with expo prebuild.
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
Try running manually:
|
|
45
45
|
npx expo prebuild --platform android
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
Alternative: Use EAS Build (Expo's cloud service)
|
|
48
48
|
Learn more: https://docs.expo.dev/bare/overview/
|
|
49
49
|
ERROR
|
|
50
50
|
end
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
puts "\nā Android project generated successfully\n\n"
|
|
53
53
|
end
|
|
54
54
|
end
|
|
@@ -56,15 +56,11 @@ module Mysigner
|
|
|
56
56
|
# 3. Check for React Native
|
|
57
57
|
if File.exist?("#{directory}/package.json") && Dir.exist?("#{directory}/android")
|
|
58
58
|
content = File.read("#{directory}/package.json")
|
|
59
|
-
if content.include?('react-native')
|
|
60
|
-
return detect_react_native_android(directory)
|
|
61
|
-
end
|
|
59
|
+
return detect_react_native_android(directory) if content.include?('react-native')
|
|
62
60
|
end
|
|
63
61
|
|
|
64
62
|
# 3. Check for Flutter
|
|
65
|
-
if File.exist?("#{directory}/pubspec.yaml")
|
|
66
|
-
return detect_flutter_android(directory)
|
|
67
|
-
end
|
|
63
|
+
return detect_flutter_android(directory) if File.exist?("#{directory}/pubspec.yaml")
|
|
68
64
|
|
|
69
65
|
# 4. Check for .NET MAUI / Xamarin
|
|
70
66
|
maui_project = Dir.glob("#{directory}/*.csproj").first
|
|
@@ -81,8 +77,6 @@ module Mysigner
|
|
|
81
77
|
app_gradle_kts = "#{directory}/app/build.gradle.kts"
|
|
82
78
|
root_gradle = "#{directory}/build.gradle"
|
|
83
79
|
root_gradle_kts = "#{directory}/build.gradle.kts"
|
|
84
|
-
settings_gradle = "#{directory}/settings.gradle"
|
|
85
|
-
settings_gradle_kts = "#{directory}/settings.gradle.kts"
|
|
86
80
|
|
|
87
81
|
if File.exist?(app_gradle) || File.exist?(app_gradle_kts)
|
|
88
82
|
return {
|
|
@@ -119,7 +113,7 @@ module Mysigner
|
|
|
119
113
|
# Detect iOS project in directory (original detect behavior)
|
|
120
114
|
def self.detect_ios(directory = Dir.pwd)
|
|
121
115
|
# 1. Check for Capacitor (most specific first)
|
|
122
|
-
if File.exist?("#{directory}/capacitor.config.json") ||
|
|
116
|
+
if File.exist?("#{directory}/capacitor.config.json") ||
|
|
123
117
|
File.exist?("#{directory}/capacitor.config.ts")
|
|
124
118
|
return detect_capacitor(directory)
|
|
125
119
|
end
|
|
@@ -132,21 +126,21 @@ module Mysigner
|
|
|
132
126
|
# Auto-run expo prebuild
|
|
133
127
|
puts "\nš¦ Expo managed workflow detected (no ios/ folder)"
|
|
134
128
|
puts "š§ Running: npx expo prebuild --platform ios\n\n"
|
|
135
|
-
|
|
129
|
+
|
|
136
130
|
result = system("cd #{directory} && npx expo prebuild --platform ios")
|
|
137
|
-
|
|
131
|
+
|
|
138
132
|
unless result && Dir.exist?("#{directory}/ios")
|
|
139
133
|
raise NoProjectError, <<~ERROR
|
|
140
134
|
Failed to generate iOS project with expo prebuild.
|
|
141
|
-
|
|
135
|
+
|
|
142
136
|
Try running manually:
|
|
143
137
|
npx expo prebuild --platform ios
|
|
144
|
-
|
|
138
|
+
|
|
145
139
|
Alternative: Use EAS Build (Expo's cloud service)
|
|
146
140
|
Learn more: https://docs.expo.dev/bare/overview/
|
|
147
141
|
ERROR
|
|
148
142
|
end
|
|
149
|
-
|
|
143
|
+
|
|
150
144
|
puts "\nā iOS project generated successfully\n\n"
|
|
151
145
|
end
|
|
152
146
|
end
|
|
@@ -154,15 +148,11 @@ module Mysigner
|
|
|
154
148
|
# 3. Check for React Native
|
|
155
149
|
if File.exist?("#{directory}/package.json") && Dir.exist?("#{directory}/ios")
|
|
156
150
|
content = File.read("#{directory}/package.json")
|
|
157
|
-
if content.include?('react-native')
|
|
158
|
-
return detect_react_native(directory)
|
|
159
|
-
end
|
|
151
|
+
return detect_react_native(directory) if content.include?('react-native')
|
|
160
152
|
end
|
|
161
153
|
|
|
162
154
|
# 4. Check for Flutter
|
|
163
|
-
if File.exist?("#{directory}/pubspec.yaml")
|
|
164
|
-
return detect_flutter(directory)
|
|
165
|
-
end
|
|
155
|
+
return detect_flutter(directory) if File.exist?("#{directory}/pubspec.yaml")
|
|
166
156
|
|
|
167
157
|
# 5. Check for native iOS project (workspace first, then project)
|
|
168
158
|
workspace = Dir.glob("#{directory}/*.xcworkspace").first
|
|
@@ -187,18 +177,18 @@ module Mysigner
|
|
|
187
177
|
}
|
|
188
178
|
end
|
|
189
179
|
|
|
190
|
-
raise NoProjectError,
|
|
180
|
+
raise NoProjectError,
|
|
181
|
+
"No Xcode project found in #{directory}. Run in a project directory or try 'mysigner init' first."
|
|
191
182
|
end
|
|
192
183
|
|
|
193
|
-
private
|
|
194
|
-
|
|
195
184
|
# Android detection for cross-platform frameworks
|
|
196
185
|
|
|
197
186
|
def self.detect_capacitor_android(directory)
|
|
198
187
|
android_dir = "#{directory}/android"
|
|
199
|
-
|
|
188
|
+
|
|
200
189
|
unless Dir.exist?(android_dir)
|
|
201
|
-
raise NoProjectError,
|
|
190
|
+
raise NoProjectError,
|
|
191
|
+
"Capacitor project detected but no android/ folder found. Run 'npx cap add android' first."
|
|
202
192
|
end
|
|
203
193
|
|
|
204
194
|
app_gradle = "#{android_dir}/app/build.gradle"
|
|
@@ -216,7 +206,8 @@ module Mysigner
|
|
|
216
206
|
}
|
|
217
207
|
end
|
|
218
208
|
|
|
219
|
-
raise NoProjectError,
|
|
209
|
+
raise NoProjectError,
|
|
210
|
+
"Capacitor project detected but no Android build.gradle found. Run 'npx cap sync android' first."
|
|
220
211
|
end
|
|
221
212
|
|
|
222
213
|
def self.detect_react_native_android(directory)
|
|
@@ -237,15 +228,13 @@ module Mysigner
|
|
|
237
228
|
}
|
|
238
229
|
end
|
|
239
230
|
|
|
240
|
-
raise NoProjectError,
|
|
231
|
+
raise NoProjectError, 'React Native project detected but no Android build.gradle found in android/ directory.'
|
|
241
232
|
end
|
|
242
233
|
|
|
243
234
|
def self.detect_flutter_android(directory)
|
|
244
235
|
android_dir = "#{directory}/android"
|
|
245
236
|
|
|
246
|
-
unless Dir.exist?(android_dir)
|
|
247
|
-
raise NoProjectError, "Flutter project detected but no android/ folder found. Run 'flutter create .' first."
|
|
248
|
-
end
|
|
237
|
+
raise NoProjectError, "Flutter project detected but no android/ folder found. Run 'flutter create .' first." unless Dir.exist?(android_dir)
|
|
249
238
|
|
|
250
239
|
app_gradle = "#{android_dir}/app/build.gradle"
|
|
251
240
|
app_gradle_kts = "#{android_dir}/app/build.gradle.kts"
|
|
@@ -262,19 +251,20 @@ module Mysigner
|
|
|
262
251
|
}
|
|
263
252
|
end
|
|
264
253
|
|
|
265
|
-
raise NoProjectError,
|
|
254
|
+
raise NoProjectError,
|
|
255
|
+
"Flutter project detected but no Android build.gradle found. Run 'flutter build apk' first."
|
|
266
256
|
end
|
|
267
257
|
|
|
268
258
|
def self.detect_dotnet_android(directory, csproj_path)
|
|
269
259
|
content = File.read(csproj_path)
|
|
270
|
-
|
|
260
|
+
|
|
271
261
|
framework_type = if content.include?('Maui')
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
262
|
+
:maui
|
|
263
|
+
elsif content.include?('Xamarin.Forms')
|
|
264
|
+
:xamarin_forms
|
|
265
|
+
else
|
|
266
|
+
:xamarin
|
|
267
|
+
end
|
|
278
268
|
|
|
279
269
|
{
|
|
280
270
|
platform: :android,
|
|
@@ -291,7 +281,7 @@ module Mysigner
|
|
|
291
281
|
|
|
292
282
|
def self.detect_capacitor(directory)
|
|
293
283
|
ios_dir = "#{directory}/ios/App"
|
|
294
|
-
|
|
284
|
+
|
|
295
285
|
# Capacitor always creates App.xcworkspace
|
|
296
286
|
workspace = "#{ios_dir}/App.xcworkspace"
|
|
297
287
|
project = "#{ios_dir}/App.xcodeproj"
|
|
@@ -348,7 +338,7 @@ module Mysigner
|
|
|
348
338
|
}
|
|
349
339
|
end
|
|
350
340
|
|
|
351
|
-
raise NoProjectError,
|
|
341
|
+
raise NoProjectError, 'React Native project detected but no Xcode project found in ios/ directory.'
|
|
352
342
|
end
|
|
353
343
|
|
|
354
344
|
def self.detect_flutter(directory)
|
|
@@ -385,4 +375,3 @@ module Mysigner
|
|
|
385
375
|
end
|
|
386
376
|
end
|
|
387
377
|
end
|
|
388
|
-
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Mysigner
|
|
2
4
|
module Build
|
|
3
5
|
class ErrorAnalyzer
|
|
@@ -18,11 +20,11 @@ module Mysigner
|
|
|
18
20
|
return nil unless any_issues?
|
|
19
21
|
|
|
20
22
|
lines = []
|
|
21
|
-
lines <<
|
|
22
|
-
lines <<
|
|
23
|
-
lines <<
|
|
24
|
-
lines <<
|
|
25
|
-
lines <<
|
|
23
|
+
lines << ''
|
|
24
|
+
lines << ('=' * 70)
|
|
25
|
+
lines << ' š” SUGGESTIONS: How to fix these build errors'
|
|
26
|
+
lines << ('=' * 70)
|
|
27
|
+
lines << ''
|
|
26
28
|
|
|
27
29
|
# Group issues by type for cleaner output
|
|
28
30
|
profile_issues = @issues.select { |i| i[:type] == :profile_capability }
|
|
@@ -31,8 +33,8 @@ module Mysigner
|
|
|
31
33
|
|
|
32
34
|
# Profile capability issues
|
|
33
35
|
if profile_issues.any?
|
|
34
|
-
lines <<
|
|
35
|
-
lines <<
|
|
36
|
+
lines << ' š PROVISIONING PROFILE ISSUES'
|
|
37
|
+
lines << ''
|
|
36
38
|
|
|
37
39
|
# Group by profile name
|
|
38
40
|
by_profile = profile_issues.group_by { |i| i[:profile_name] }
|
|
@@ -40,70 +42,70 @@ module Mysigner
|
|
|
40
42
|
capabilities = issues.map { |i| i[:capability] }.compact.uniq
|
|
41
43
|
lines << " Profile: \"#{profile_name}\""
|
|
42
44
|
lines << " Missing capabilities: #{capabilities.join(', ')}"
|
|
43
|
-
lines <<
|
|
45
|
+
lines << ''
|
|
44
46
|
end
|
|
45
47
|
|
|
46
|
-
lines <<
|
|
47
|
-
lines <<
|
|
48
|
+
lines << ' How to fix:'
|
|
49
|
+
lines << ' 1. Go to Apple Developer Portal ā Certificates, Identifiers & Profiles'
|
|
48
50
|
lines << " 2. Select 'Identifiers' and find your Bundle ID"
|
|
49
|
-
lines <<
|
|
50
|
-
lines <<
|
|
51
|
+
lines << ' 3. Enable the missing capabilities (App Groups, Apple Pay, etc.)'
|
|
52
|
+
lines << ' 4. If adding App Groups or Merchant IDs, make sure to select the specific identifiers'
|
|
51
53
|
lines << " 5. Go to 'Profiles' and regenerate the affected provisioning profiles"
|
|
52
|
-
lines <<
|
|
53
|
-
lines <<
|
|
54
|
-
lines <<
|
|
54
|
+
lines << ' 6. Download new profiles: mysigner sync ios && mysigner profile download <ID>'
|
|
55
|
+
lines << ' 7. Install profiles to: ~/Library/MobileDevice/Provisioning Profiles/'
|
|
56
|
+
lines << ''
|
|
55
57
|
end
|
|
56
58
|
|
|
57
59
|
# Missing specific identifiers (App Group ID, Merchant ID)
|
|
58
60
|
if identifier_issues.any?
|
|
59
|
-
lines <<
|
|
60
|
-
lines <<
|
|
61
|
+
lines << ' š MISSING IDENTIFIERS'
|
|
62
|
+
lines << ''
|
|
61
63
|
|
|
62
64
|
identifier_issues.each do |issue|
|
|
63
65
|
lines << " Profile: \"#{issue[:profile_name]}\""
|
|
64
66
|
lines << " Missing: #{issue[:identifier_type]} - #{issue[:identifier]}"
|
|
65
|
-
lines <<
|
|
67
|
+
lines << ''
|
|
66
68
|
end
|
|
67
69
|
|
|
68
|
-
lines <<
|
|
69
|
-
lines <<
|
|
70
|
-
lines <<
|
|
71
|
-
lines <<
|
|
72
|
-
lines <<
|
|
73
|
-
lines <<
|
|
74
|
-
lines <<
|
|
75
|
-
lines <<
|
|
70
|
+
lines << ' How to fix:'
|
|
71
|
+
lines << ' 1. Go to Apple Developer Portal ā Identifiers'
|
|
72
|
+
lines << ' 2. Find your Bundle ID and edit it'
|
|
73
|
+
lines << ' 3. Under the capability, add/select the specific identifier:'
|
|
74
|
+
lines << ' ⢠For App Groups: select your group.* identifier'
|
|
75
|
+
lines << ' ⢠For Apple Pay: select your merchant.* identifier'
|
|
76
|
+
lines << ' 4. Regenerate the provisioning profile'
|
|
77
|
+
lines << ''
|
|
76
78
|
end
|
|
77
79
|
|
|
78
80
|
# Certificate mismatch
|
|
79
81
|
if cert_issues.any?
|
|
80
|
-
lines <<
|
|
81
|
-
lines <<
|
|
82
|
-
lines <<
|
|
83
|
-
lines <<
|
|
84
|
-
lines <<
|
|
85
|
-
lines <<
|
|
86
|
-
lines <<
|
|
87
|
-
lines <<
|
|
88
|
-
lines <<
|
|
82
|
+
lines << ' š CERTIFICATE MISMATCH'
|
|
83
|
+
lines << ''
|
|
84
|
+
lines << ' Your app and its extensions are signed with different certificates.'
|
|
85
|
+
lines << ''
|
|
86
|
+
lines << ' How to fix:'
|
|
87
|
+
lines << ' 1. Open your Xcode project'
|
|
88
|
+
lines << ' 2. For EACH target (main app AND extensions):'
|
|
89
|
+
lines << ' ⢠Select the target ā Signing & Capabilities'
|
|
90
|
+
lines << ' ⢠Ensure all targets use the same signing identity:'
|
|
89
91
|
lines << " - For App Store: 'Apple Distribution'"
|
|
90
92
|
lines << " - For Development: 'Apple Development'"
|
|
91
|
-
lines <<
|
|
92
|
-
lines <<
|
|
93
|
-
lines <<
|
|
94
|
-
lines <<
|
|
95
|
-
lines <<
|
|
96
|
-
lines <<
|
|
97
|
-
lines <<
|
|
98
|
-
lines <<
|
|
99
|
-
lines <<
|
|
93
|
+
lines << ' 3. Make sure all targets use matching profile types:'
|
|
94
|
+
lines << ' ⢠App Store profiles for App Store builds'
|
|
95
|
+
lines << ' ⢠Development profiles for development builds'
|
|
96
|
+
lines << ''
|
|
97
|
+
lines << ' Quick fix for App Store builds:'
|
|
98
|
+
lines << ' In project.pbxproj, ensure Release configuration has:'
|
|
99
|
+
lines << ' CODE_SIGN_IDENTITY = "Apple Distribution"'
|
|
100
|
+
lines << ' PROVISIONING_PROFILE_SPECIFIER = "YourApp App Store"'
|
|
101
|
+
lines << ''
|
|
100
102
|
end
|
|
101
103
|
|
|
102
|
-
lines <<
|
|
104
|
+
lines << ' š More help:'
|
|
103
105
|
lines << " ⢠Run 'mysigner doctor' to check your setup"
|
|
104
106
|
lines << " ⢠Run 'mysigner profiles' to list available profiles"
|
|
105
|
-
lines <<
|
|
106
|
-
lines <<
|
|
107
|
+
lines << ' ⢠Check My Signer dashboard for Bundle ID capabilities'
|
|
108
|
+
lines << ''
|
|
107
109
|
|
|
108
110
|
lines.join("\n")
|
|
109
111
|
end
|
|
@@ -118,10 +120,10 @@ module Mysigner
|
|
|
118
120
|
|
|
119
121
|
def analyze_error(error)
|
|
120
122
|
# Normalize curly quotes to straight quotes
|
|
121
|
-
error = error.gsub(
|
|
123
|
+
error = error.gsub('"', '"').gsub('\'', "'")
|
|
122
124
|
|
|
123
125
|
# Pattern: Provisioning profile "X" doesn't include the Y capability
|
|
124
|
-
if match = error.match(/Provisioning profile "([^"]+)".*(?:doesn't|does not) include the (.+?) capability/i)
|
|
126
|
+
if (match = error.match(/Provisioning profile "([^"]+)".*(?:doesn't|does not) include the (.+?) capability/i))
|
|
125
127
|
@issues << {
|
|
126
128
|
type: :profile_capability,
|
|
127
129
|
profile_name: match[1],
|
|
@@ -130,27 +132,27 @@ module Mysigner
|
|
|
130
132
|
end
|
|
131
133
|
|
|
132
134
|
# Pattern: Provisioning profile "X" doesn't support the Y App Group
|
|
133
|
-
if match = error.match(/Provisioning profile "([^"]+)".*(?:doesn't|does not) support the (.+?) App Group/i)
|
|
135
|
+
if (match = error.match(/Provisioning profile "([^"]+)".*(?:doesn't|does not) support the (.+?) App Group/i))
|
|
134
136
|
@issues << {
|
|
135
137
|
type: :missing_identifier,
|
|
136
138
|
profile_name: match[1],
|
|
137
|
-
identifier_type:
|
|
139
|
+
identifier_type: 'App Group',
|
|
138
140
|
identifier: match[2].strip
|
|
139
141
|
}
|
|
140
142
|
end
|
|
141
143
|
|
|
142
144
|
# Pattern: Provisioning profile "X" doesn't support the Y Merchant ID
|
|
143
|
-
if match = error.match(/Provisioning profile "([^"]+)".*(?:doesn't|does not) support the (.+?) Merchant ID/i)
|
|
145
|
+
if (match = error.match(/Provisioning profile "([^"]+)".*(?:doesn't|does not) support the (.+?) Merchant ID/i))
|
|
144
146
|
@issues << {
|
|
145
147
|
type: :missing_identifier,
|
|
146
148
|
profile_name: match[1],
|
|
147
|
-
identifier_type:
|
|
149
|
+
identifier_type: 'Merchant ID',
|
|
148
150
|
identifier: match[2].strip
|
|
149
151
|
}
|
|
150
152
|
end
|
|
151
153
|
|
|
152
154
|
# Pattern: Provisioning profile "X" doesn't match the entitlements file's value
|
|
153
|
-
if match = error.match(/Provisioning profile "([^"]+)".*(?:doesn't|does not) match.*entitlements.*?for the (.+?) entitlement/i)
|
|
155
|
+
if (match = error.match(/Provisioning profile "([^"]+)".*(?:doesn't|does not) match.*entitlements.*?for the (.+?) entitlement/i))
|
|
154
156
|
capability = entitlement_to_capability(match[2])
|
|
155
157
|
@issues << {
|
|
156
158
|
type: :profile_capability,
|
|
@@ -160,7 +162,7 @@ module Mysigner
|
|
|
160
162
|
end
|
|
161
163
|
|
|
162
164
|
# Pattern: Embedded binary is not signed with the same certificate
|
|
163
|
-
if error.include?(
|
|
165
|
+
if error.include?('Embedded binary is not signed with the same certificate')
|
|
164
166
|
@issues << {
|
|
165
167
|
type: :certificate_mismatch,
|
|
166
168
|
message: error
|
|
@@ -168,23 +170,23 @@ module Mysigner
|
|
|
168
170
|
end
|
|
169
171
|
|
|
170
172
|
# Pattern: Code Sign error
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
173
|
+
return unless error.include?('Code Sign error')
|
|
174
|
+
|
|
175
|
+
@issues << {
|
|
176
|
+
type: :code_sign_error,
|
|
177
|
+
message: error
|
|
178
|
+
}
|
|
177
179
|
end
|
|
178
180
|
|
|
179
181
|
def entitlement_to_capability(entitlement)
|
|
180
182
|
mappings = {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
183
|
+
'com.apple.security.application-groups' => 'App Groups',
|
|
184
|
+
'com.apple.developer.in-app-payments' => 'Apple Pay',
|
|
185
|
+
'aps-environment' => 'Push Notifications',
|
|
186
|
+
'com.apple.developer.associated-domains' => 'Associated Domains',
|
|
187
|
+
'com.apple.developer.applesignin' => 'Sign in with Apple',
|
|
188
|
+
'com.apple.developer.icloud-services' => 'iCloud',
|
|
189
|
+
'com.apple.developer.healthkit' => 'HealthKit'
|
|
188
190
|
}
|
|
189
191
|
mappings[entitlement] || entitlement
|
|
190
192
|
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'English'
|
|
3
4
|
module Mysigner
|
|
4
5
|
module Build
|
|
5
6
|
class Executor
|
|
@@ -20,7 +21,8 @@ module Mysigner
|
|
|
20
21
|
# - team_id: Development team ID to override project setting
|
|
21
22
|
# - bundle_id: Bundle ID to override project setting
|
|
22
23
|
# - skip_extensions: If true, disable code signing for extension targets
|
|
23
|
-
def build!(target_name = nil, configuration = 'Release', scheme: nil, signing_style: nil, team_id: nil,
|
|
24
|
+
def build!(target_name = nil, configuration = 'Release', scheme: nil, signing_style: nil, team_id: nil,
|
|
25
|
+
bundle_id: nil, skip_extensions: false)
|
|
24
26
|
target = target_name || @parser.main_target.name
|
|
25
27
|
scheme_name = scheme || target
|
|
26
28
|
@signing_style = signing_style
|
|
@@ -31,7 +33,7 @@ module Mysigner
|
|
|
31
33
|
# Use Xcode's default DerivedData location to keep project clean
|
|
32
34
|
# This matches Xcode's behavior and avoids polluting the project directory
|
|
33
35
|
output_dir = File.join(@project_info[:directory], 'build')
|
|
34
|
-
FileUtils.mkdir_p(output_dir)
|
|
36
|
+
FileUtils.mkdir_p(output_dir)
|
|
35
37
|
|
|
36
38
|
# Generate archive path with timestamp
|
|
37
39
|
timestamp = Time.now.strftime('%Y%m%d-%H%M%S')
|
|
@@ -44,14 +46,10 @@ module Mysigner
|
|
|
44
46
|
# Execute build
|
|
45
47
|
success = execute_with_output(cmd)
|
|
46
48
|
|
|
47
|
-
unless success
|
|
48
|
-
raise BuildError, "Build failed. Check output above for errors."
|
|
49
|
-
end
|
|
49
|
+
raise BuildError, 'Build failed. Check output above for errors.' unless success
|
|
50
50
|
|
|
51
51
|
# Verify archive was created
|
|
52
|
-
unless File.exist?(archive_path)
|
|
53
|
-
raise BuildError, "Build reported success but archive not found at: #{archive_path}"
|
|
54
|
-
end
|
|
52
|
+
raise BuildError, "Build reported success but archive not found at: #{archive_path}" unless File.exist?(archive_path)
|
|
55
53
|
|
|
56
54
|
archive_path
|
|
57
55
|
end
|
|
@@ -59,14 +57,14 @@ module Mysigner
|
|
|
59
57
|
private
|
|
60
58
|
|
|
61
59
|
def build_command(scheme, configuration, archive_path)
|
|
62
|
-
cmd = [
|
|
60
|
+
cmd = %w[xcodebuild archive]
|
|
63
61
|
|
|
64
62
|
# Workspace or project
|
|
65
|
-
if @project_info[:type] == :workspace
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
cmd += if @project_info[:type] == :workspace
|
|
64
|
+
['-workspace', @project_info[:path]]
|
|
65
|
+
else
|
|
66
|
+
['-project', @project_info[:path]]
|
|
67
|
+
end
|
|
70
68
|
|
|
71
69
|
# Scheme and configuration
|
|
72
70
|
cmd += [
|
|
@@ -78,26 +76,22 @@ module Mysigner
|
|
|
78
76
|
# SDK selection based on platform
|
|
79
77
|
platform = @parser.target_platform(scheme)
|
|
80
78
|
sdk = case platform
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
79
|
+
when :macos
|
|
80
|
+
'macosx'
|
|
81
|
+
when :tvos
|
|
82
|
+
'appletvos'
|
|
83
|
+
when :watchos
|
|
84
|
+
'watchos'
|
|
85
|
+
else
|
|
86
|
+
'iphoneos' # default to iOS
|
|
87
|
+
end
|
|
90
88
|
cmd += ['-sdk', sdk]
|
|
91
89
|
|
|
92
90
|
# Override team ID if provided
|
|
93
|
-
if @team_id
|
|
94
|
-
|
|
95
|
-
end
|
|
96
|
-
|
|
91
|
+
cmd += ["DEVELOPMENT_TEAM=#{@team_id}"] if @team_id
|
|
92
|
+
|
|
97
93
|
# Override bundle ID if provided
|
|
98
|
-
if @bundle_id
|
|
99
|
-
cmd += ["PRODUCT_BUNDLE_IDENTIFIER=#{@bundle_id}"]
|
|
100
|
-
end
|
|
94
|
+
cmd += ["PRODUCT_BUNDLE_IDENTIFIER=#{@bundle_id}"] if @bundle_id
|
|
101
95
|
|
|
102
96
|
# Handle signing based on style
|
|
103
97
|
case @signing_style
|
|
@@ -131,13 +125,13 @@ module Mysigner
|
|
|
131
125
|
end
|
|
132
126
|
|
|
133
127
|
def execute_with_output(cmd)
|
|
134
|
-
puts
|
|
135
|
-
puts
|
|
128
|
+
puts 'šļø Running: xcodebuild archive...'
|
|
129
|
+
puts ''
|
|
136
130
|
|
|
137
131
|
@build_errors = []
|
|
138
132
|
|
|
139
133
|
# Run command and capture output in real-time
|
|
140
|
-
IO.popen(cmd, err: [
|
|
134
|
+
IO.popen(cmd, err: %i[child out]) do |io|
|
|
141
135
|
io.each_line do |line|
|
|
142
136
|
# Filter output to show only important messages
|
|
143
137
|
next if line.strip.empty?
|
|
@@ -166,11 +160,10 @@ module Mysigner
|
|
|
166
160
|
end
|
|
167
161
|
end
|
|
168
162
|
|
|
169
|
-
puts
|
|
163
|
+
puts '' # New line after dots
|
|
170
164
|
|
|
171
|
-
|
|
165
|
+
$CHILD_STATUS.success?
|
|
172
166
|
end
|
|
173
167
|
end
|
|
174
168
|
end
|
|
175
169
|
end
|
|
176
|
-
|