sigh 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 22e89f083c78667b420dc18851b06214624b0a31
4
- data.tar.gz: 21fa51c43e3dbfb8b2c3d40c576b0dffe35e9973
3
+ metadata.gz: ef27c541a6d91c50a1a6230aa2dc3598456603b4
4
+ data.tar.gz: a9cb32dca3f10b03502ee41589d54db9d7d1d6bb
5
5
  SHA512:
6
- metadata.gz: 91b189b8dc6ff527e51751d3913ac951eab6e900bcdaf5ebde860f2a3ca816f2b92fca733b1e5a138fe47a85306ef017dd7790737f1b05ccd2da647bdf281693
7
- data.tar.gz: c88c9ea33d835863a2772ce79f4efd50d3aba88a7fc6b454b0c2ee9c8cf36a62d092928695b837200254349f116316f08c0a3612fd2dbe38a0d0bd6a6d728a24
6
+ metadata.gz: 2cfc39277183515956b30fcd2df642657f60ea084af41a3d8c248bc71f892440e6ffe8c62a1f787654f7ec24569f37fefcd9f68288c306af25697ec7ebafea53
7
+ data.tar.gz: f53445cff10af39c000800130eeeb139734ddc92f05feb0c2c326c034556eb704be201424ebebea0e12c27e49c74a1d35551a51175dce67a6f6f4f6451884474
data/README.md CHANGED
@@ -82,8 +82,6 @@ Make sure, you have the latest version of the Xcode command line tools installed
82
82
 
83
83
  xcode-select --install
84
84
 
85
- If you don't already have homebrew installed, [install it here](http://brew.sh/).
86
-
87
85
  # Usage
88
86
 
89
87
  sigh
@@ -121,16 +119,19 @@ If you need the provisioning profile to be renewed regardless of its state use t
121
119
 
122
120
  sigh --force -a com.krausefx.app -u username
123
121
 
124
- To renew a valid profile with a different certificate, look up the expiry date of the certificate you want to sign with in the Apple Developer Portal under Production Certificates. Copy the date string from there and use the following:
122
+ By default, ```sigh``` will include all certificates on development profiles, and first certificate on other types. If you need to specify which certificate to use you can either use the environment variable `SIGH_CERTIFICATE`, or pass the name or expiry date of the certificate as argument:
123
+
124
+ sigh -c "SunApps GmbH"
125
125
 
126
- sigh --force -a com.krausefx.app -u username -d "Nov 11, 2017"
127
126
 
127
+ sigh -d "Nov 11, 2017"
128
128
 
129
129
  ## Environment Variables
130
130
  In case you prefer environment variables:
131
131
 
132
132
  - ```SIGH_USERNAME```
133
133
  - ```SIGH_APP_IDENTIFIER```
134
+ - ```SIGH_CERTIFICATE``` (The name of the certificate to use)
134
135
  - ```SIGH_TEAM_ID``` (The Team ID, e.g. `Q2CBPK58CA`)
135
136
  - `SIGH_DISABLE_OPEN_ERROR` - in case of error, `sigh` won't open Preview with a screenshot of the error when this variable is set.
136
137
 
data/bin/sigh CHANGED
@@ -32,6 +32,7 @@ class SighApplication
32
32
  global_option '-n', '--cert_name STRING', String, 'The name of the generated certificate file.'
33
33
  global_option '-o', '--output STRING', String, 'The folder in which the file should be generated.'
34
34
  global_option '-d', '--cert_date STRING', String, 'The certificate with the given expiry date used to renew. (e.g. "Nov 11, 2017")'
35
+ global_option '-c', '--cert_owner STRING', String, 'The certificate name to use for new profiles, or to renew with. (e.g. "Felix Krause")'
35
36
  global_option '-f', '--filename STRING', String, 'Filename to use for the provisioning profile'
36
37
 
37
38
  command :renew do |c|
@@ -45,7 +46,9 @@ class SighApplication
45
46
  type = Sigh::DeveloperCenter::APPSTORE
46
47
  type = Sigh::DeveloperCenter::ADHOC if options.adhoc
47
48
  type = Sigh::DeveloperCenter::DEVELOPMENT if options.development
48
-
49
+
50
+ ENV['SIGH_CERTIFICATE'] = options.cert_owner if options.cert_owner
51
+
49
52
  path = Sigh::DeveloperCenter.new.run(app, type, options.cert_name, options.force, options.cert_date)
50
53
 
51
54
  if path
@@ -64,6 +67,16 @@ class SighApplication
64
67
  end
65
68
  end
66
69
 
70
+ command :resign do |c|
71
+ c.syntax = 'sigh resign'
72
+ c.description = 'Resigns an existing ipa file with the given provisioning profile'
73
+ c.option '-i', '--signing_identity STRING', String, 'The signing identity to use. Must match the one defined in the provisioning profile.'
74
+
75
+ c.action do |args, options|
76
+ Sigh::Resign.new.run(options)
77
+ end
78
+ end
79
+
67
80
  default_command :renew
68
81
 
69
82
  run!
@@ -0,0 +1,439 @@
1
+ # !/bin/bash
2
+
3
+ # Copyright (c) 2011 Float Mobile Learning
4
+ # http://www.floatlearning.com/
5
+ # Extension Copyright (c) 2013 Weptun Gmbh
6
+ # http://www.weptun.de
7
+ #
8
+ # Extended by Ronan O Ciosoig January 2012
9
+ #
10
+ # Extended by Patrick Blitz, April 2013
11
+ #
12
+ # Extended by John Turnipseed and Matthew Nespor, November 2014
13
+ # http://nanonation.net/
14
+ #
15
+ # Permission is hereby granted, free of charge, to any person obtaining
16
+ # a copy of this software and associated documentation files (the "Software"),
17
+ # to deal in the Software without restriction, including without limitation
18
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
19
+ # and/or sell copies of the Software, and to permit persons to whom the
20
+ # Software is furnished to do so, subject to the following conditions:
21
+ #
22
+ # The above copyright notice and this permission notice shall be included
23
+ # in all copies or substantial portions of the Software.
24
+ #
25
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
26
+ # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
28
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
29
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
30
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
31
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32
+ #
33
+ # Please let us know about any improvements you make to this script!
34
+ # ./floatsign source "iPhone Distribution: Name" -p "path/to/profile" [-d "display name"] [-e entitlements] [-k keychain] -b "BundleIdentifier" outputIpa
35
+ #
36
+ #
37
+ # Modifed 26th January 2012
38
+ #
39
+ # new features January 2012:
40
+ # 1. change the app display name
41
+ #
42
+ # new features April 2013
43
+ # 1. specify the target bundleId on the command line
44
+ # 2. correctly handles entitlements for keychain-enabled resigning
45
+ #
46
+ # new features November 2014
47
+ # 1. now re-signs embedded iOS frameworks, if present, prior to re-signing the application itself
48
+ # 2. extracts the team-identifier from provisioning profile and uses it to update previous entitlements
49
+ # 3. fixed bug in packaging if -e flag is used
50
+ # 4. renamed 'temp' directory and made it a variable so it can be easily modified
51
+ # 5. various code formatting and logging adjustments
52
+ #
53
+
54
+
55
+ function checkStatus {
56
+
57
+ if [ $? -ne 0 ];
58
+ then
59
+ echo "Encountered an error, aborting!" >&2
60
+ exit 1
61
+ fi
62
+ }
63
+
64
+ if [ $# -lt 3 ]; then
65
+ echo "usage: $0 source identity -p provisioning [-e entitlements] [-r adjustBetaReports] [-d displayName] [-n version] -b bundleId outputIpa" >&2
66
+ echo " -p and -b are optional, but their use is heavly recommended" >&2
67
+ echo " -r flag requires a value '-r yes'"
68
+ echo " -r flag is ignored if -e is also used" >&2
69
+ exit 1
70
+ fi
71
+
72
+ ORIGINAL_FILE="$1"
73
+ CERTIFICATE="$2"
74
+ NEW_PROVISION=
75
+ ENTITLEMENTS=
76
+ BUNDLE_IDENTIFIER=""
77
+ DISPLAY_NAME=""
78
+ APP_IDENTIFER_PREFIX=""
79
+ TEAM_IDENTIFIER=""
80
+ KEYCHAIN=""
81
+ VERSION_NUMBER=""
82
+ ADJUST_BETA_REPORTS_ACTIVE_FLAG="0"
83
+ TEMP_DIR="_floatsignTemp"
84
+
85
+ # options start index
86
+ OPTIND=3
87
+ while getopts p:d:e:k:b:r:n: opt; do
88
+ case $opt in
89
+ p)
90
+ NEW_PROVISION="$OPTARG"
91
+ echo "Specified provisioning profile: '$NEW_PROVISION'" >&2
92
+ ;;
93
+ d)
94
+ DISPLAY_NAME="$OPTARG"
95
+ echo "Specified display name: '$DISPLAY_NAME'" >&2
96
+ ;;
97
+ e)
98
+ ENTITLEMENTS="$OPTARG"
99
+ echo "Specified signing entitlements: '$ENTITLEMENTS'" >&2
100
+ ;;
101
+ b)
102
+ BUNDLE_IDENTIFIER="$OPTARG"
103
+ echo "Specified bundle identifier: '$BUNDLE_IDENTIFIER'" >&2
104
+ ;;
105
+ k)
106
+ KEYCHAIN="$OPTARG"
107
+ echo "Specified Keychain to use: '$KEYCHAIN'" >&2
108
+ ;;
109
+ n)
110
+ VERSION_NUMBER="$OPTARG"
111
+ echo "Specified version to use: '$VERSION_NUMBER'" >&2
112
+ ;;
113
+ r)
114
+ ADJUST_BETA_REPORTS_ACTIVE_FLAG="1"
115
+ echo "Enabled adjustment of beta-reports-active entitlements" >&2
116
+ ;;
117
+ \?)
118
+ echo "Invalid option: -$OPTARG" >&2
119
+ exit 1
120
+ ;;
121
+ :)
122
+ echo "Option -$OPTARG requires an argument." >&2
123
+ exit 1
124
+ ;;
125
+ esac
126
+ done
127
+
128
+ shift $((OPTIND-1))
129
+
130
+ NEW_FILE="$1"
131
+ if [ -z "$NEW_FILE" ];
132
+ then
133
+ echo "Output file name required" >&2
134
+ exit 1
135
+ fi
136
+
137
+
138
+ # Check for and remove the temporary directory if it already exists
139
+ if [ -d "$TEMP_DIR" ];
140
+ then
141
+ echo "Removing previous temporary directory: '$TEMP_DIR'" >&2
142
+ rm -Rf "$TEMP_DIR"
143
+ fi
144
+
145
+ filename=$(basename "$ORIGINAL_FILE")
146
+ extension="${filename##*.}"
147
+ filename="${filename%.*}"
148
+
149
+ # Check if the supplied file is an ipa or an app file
150
+ if [ "${extension}" = "ipa" ]
151
+ then
152
+ # Unzip the old ipa quietly
153
+ unzip -q "$ORIGINAL_FILE" -d $TEMP_DIR
154
+ checkStatus
155
+ elif [ "${extension}" = "app" ]
156
+ then
157
+ # Copy the app file into an ipa-like structure
158
+ mkdir -p "$TEMP_DIR/Payload"
159
+ cp -Rf "${ORIGINAL_FILE}" "$TEMP_DIR/Payload/${filename}.app"
160
+ checkStatus
161
+ else
162
+ echo "Error: Only can resign .app files and .ipa files." >&2
163
+ exit
164
+ fi
165
+
166
+ # check the keychain
167
+ if [ "${KEYCHAIN}" != "" ];
168
+ then
169
+ security list-keychains -s $KEYCHAIN
170
+ security unlock $KEYCHAIN
171
+ security default-keychain -s $KEYCHAIN
172
+ fi
173
+
174
+ # Set the app name
175
+ # The app name is the only file within the Payload directory
176
+ APP_NAME=$(ls "$TEMP_DIR/Payload/")
177
+
178
+ # Make sure that PATH includes the location of the PlistBuddy helper tool as its location is not standard
179
+ export PATH=$PATH:/usr/libexec
180
+
181
+ # Make sure that the Info.plist file is where we expect it
182
+ if [ ! -e "$TEMP_DIR/Payload/$APP_NAME/Info.plist" ];
183
+ then
184
+ echo "Expected file does not exist: '$TEMP_DIR/Payload/$APP_NAME/Info.plist'" >&2
185
+ exit 1;
186
+ fi
187
+
188
+ # Read in current values from the app
189
+ CURRENT_NAME=`PlistBuddy -c "Print :CFBundleDisplayName" "$TEMP_DIR/Payload/$APP_NAME/Info.plist"`
190
+ CURRENT_BUNDLE_IDENTIFIER=`PlistBuddy -c "Print :CFBundleIdentifier" "$TEMP_DIR/Payload/$APP_NAME/Info.plist"`
191
+ if [ "${BUNDLE_IDENTIFIER}" == "" ];
192
+ then
193
+ BUNDLE_IDENTIFIER=`egrep -a -A 2 application-identifier "${NEW_PROVISION}" | grep string | sed -e 's/<string>//' -e 's/<\/string>//' -e 's/ //' | awk '{split($0,a,"."); i = length(a); for(ix=2; ix <= i;ix++){ s=s a[ix]; if(i!=ix){s=s "."};} print s;}'`
194
+ if [[ "${BUNDLE_IDENTIFIER}" == *\** ]]; then
195
+ echo "Bundle Identifier contains a *, using the current bundle identifier" >&2
196
+ BUNDLE_IDENTIFIER=$CURRENT_BUNDLE_IDENTIFIER;
197
+ fi
198
+ checkStatus
199
+ fi
200
+
201
+ echo "Current bundle identifier is: '$CURRENT_BUNDLE_IDENTIFIER'" >&2
202
+ echo "New bundle identifier will be: '$BUNDLE_IDENTIFIER'" >&2
203
+
204
+ # Update the CFBundleDisplayName property in the Info.plist if a new name has been provided
205
+ if [ "${DISPLAY_NAME}" != "" ];
206
+ then
207
+ if [ "${DISPLAY_NAME}" != "${CURRENT_NAME}" ];
208
+ then
209
+ echo "Changing display name from '$CURRENT_NAME' to '$DISPLAY_NAME'" >&2
210
+ `PlistBuddy -c "Set :CFBundleDisplayName $DISPLAY_NAME" "$TEMP_DIR/Payload/$APP_NAME/Info.plist"`
211
+ fi
212
+ fi
213
+
214
+ # Replace the embedded mobile provisioning profile
215
+ if [ "$NEW_PROVISION" != "" ];
216
+ then
217
+ if [[ -e "$NEW_PROVISION" ]];
218
+ then
219
+ echo "Validating the new provisioning profile: $NEW_PROVISION" >&2
220
+ security cms -D -i "$NEW_PROVISION" > "$TEMP_DIR/profile.plist"
221
+ checkStatus
222
+
223
+ APP_IDENTIFER_PREFIX=`PlistBuddy -c "Print :Entitlements:application-identifier" "$TEMP_DIR/profile.plist" | grep -E '^[A-Z0-9]*' -o | tr -d '\n'`
224
+ if [ "$APP_IDENTIFER_PREFIX" == "" ];
225
+ then
226
+ APP_IDENTIFER_PREFIX=`PlistBuddy -c "Print :ApplicationIdentifierPrefix:0" "$TEMP_DIR/profile.plist"`
227
+ if [ "$APP_IDENTIFER_PREFIX" == "" ];
228
+ then
229
+ echo "Failed to extract any app identifier prefix from '$NEW_PROVISION'" >&2
230
+ exit 1;
231
+ else
232
+ echo "WARNING: extracted an app identifier prefix '$APP_IDENTIFER_PREFIX' from '$NEW_PROVISION', but it was not found in the profile's entitlements" >&2
233
+ fi
234
+ else
235
+ echo "Profile app identifier prefix is '$APP_IDENTIFER_PREFIX'" >&2
236
+ fi
237
+
238
+ TEAM_IDENTIFIER=`PlistBuddy -c "Print :Entitlements:com.apple.developer.team-identifier" "$TEMP_DIR/profile.plist" | tr -d '\n'`
239
+ if [ "$TEAM_IDENTIFIER" == "" ];
240
+ then
241
+ TEAM_IDENTIFIER=`PlistBuddy -c "Print :TeamIdentifier:0" "$TEMP_DIR/profile.plist"`
242
+ if [ "$TEAM_IDENTIFIER" == "" ];
243
+ then
244
+ echo "Failed to extract team identifier from '$NEW_PROVISION', resigned ipa may fail on iOS 8 and higher" >&2
245
+ else
246
+ echo "WARNING: extracted a team identifier '$TEAM_IDENTIFIER' from '$NEW_PROVISION', but it was not found in the profile's entitlements, resigned ipa may fail on iOS 8 and higher" >&2
247
+ fi
248
+ else
249
+ echo "Profile team identifier is '$TEAM_IDENTIFIER'" >&2
250
+ fi
251
+
252
+ cp "$NEW_PROVISION" "$TEMP_DIR/Payload/$APP_NAME/embedded.mobileprovision"
253
+ else
254
+ echo "Provisioning profile '$NEW_PROVISION' file does not exist" >&2
255
+ exit 1;
256
+ fi
257
+ else
258
+ echo "-p 'xxxx.mobileprovision' argument is required" >&2
259
+ exit 1;
260
+ fi
261
+
262
+
263
+ #if the current bundle identifier is different from the new one in the provisioning profile, then change it.
264
+ if [ "$CURRENT_BUNDLE_IDENTIFIER" != "$BUNDLE_IDENTIFIER" ];
265
+ then
266
+ echo "Updating the bundle identifier from '$CURRENT_BUNDLE_IDENTIFIER' to '$BUNDLE_IDENTIFIER'" >&2
267
+ `PlistBuddy -c "Set :CFBundleIdentifier $BUNDLE_IDENTIFIER" "$TEMP_DIR/Payload/$APP_NAME/Info.plist"`
268
+ checkStatus
269
+ fi
270
+
271
+ # Update the version number properties in the Info.plist if a version number has been provided
272
+ if [ "$VERSION_NUMBER" != "" ];
273
+ then
274
+ CURRENT_VERSION_NUMBER=`PlistBuddy -c "Print :CFBundleVersion" "$TEMP_DIR/Payload/$APP_NAME/Info.plist"`
275
+ if [ "$VERSION_NUMBER" != "$CURRENT_VERSION_NUMBER" ];
276
+ then
277
+ echo "Updating the version from '$CURRENT_VERSION_NUMBER' to '$VERSION_NUMBER'" >&2
278
+ `PlistBuddy -c "Set :CFBundleVersion $VERSION_NUMBER" "$TEMP_DIR/Payload/$APP_NAME/Info.plist"`
279
+ `PlistBuddy -c "Set :CFBundleShortVersionString $VERSION_NUMBER" "$TEMP_DIR/Payload/$APP_NAME/Info.plist"`
280
+ fi
281
+ fi
282
+
283
+ # Check for and resign any embedded frameworks (new feature for iOS 8 and above apps)
284
+ FRAMEWORKS_DIR="$TEMP_DIR/Payload/$APP_NAME/Frameworks"
285
+ if [ -d "$FRAMEWORKS_DIR" ];
286
+ then
287
+ if [ "$TEAM_IDENTIFIER" == "" ];
288
+ then
289
+ echo "ERROR: embedded frameworks detected, re-signing iOS 8 (or higher) applications wihout a team identifier in the certificate/profile does not work" >&2
290
+ exit 1;
291
+ fi
292
+
293
+ echo "Resigning embedded frameworks using certificate: '$CERTIFICATE'" >&2
294
+ for framework in "$FRAMEWORKS_DIR"/*
295
+ do
296
+ if [[ "$framework" == *.framework ]]
297
+ then
298
+ /usr/bin/codesign -f -s "$CERTIFICATE" "$framework"
299
+ checkStatus
300
+ else
301
+ echo "Ignoring non-framework: $framework" >&2
302
+ fi
303
+ done
304
+ fi
305
+
306
+
307
+ # Resign the application
308
+ if [ "$ENTITLEMENTS" != "" ];
309
+ then
310
+ if [ -n "$APP_IDENTIFER_PREFIX" ];
311
+ then
312
+ # sanity check the 'application-identifier' is present in the provided entitlements and matches the provisioning profile value
313
+ ENTITLEMENTS_APP_ID_PREFIX=`PlistBuddy -c "Print :application-identifier" "$ENTITLEMENTS" | grep -E '^[A-Z0-9]*' -o | tr -d '\n'`
314
+ if [ "$ENTITLEMENTS_APP_ID_PREFIX" == "" ];
315
+ then
316
+ echo "Provided entitlements file is missing a value for the required 'application-identifier' key" >&2
317
+ exit 1;
318
+ elif [ "$ENTITLEMENTS_APP_ID_PREFIX" != "$APP_IDENTIFER_PREFIX" ];
319
+ then
320
+ echo "Provided entitlements file's app identifier prefix value '$ENTITLEMENTS_APP_ID_PREFIX' does not match the provided provisioning profile's value '$APP_IDENTIFER_PREFIX'" >&2
321
+ exit 1;
322
+ fi
323
+ fi
324
+
325
+ if [ -n "$TEAM_IDENTIFIER" ];
326
+ then
327
+ # sanity check the 'com.apple.developer.team-identifier' is present in the provided entitlements and matches the provisioning profile value
328
+ ENTITLEMENTS_TEAM_IDENTIFIER=`PlistBuddy -c "Print :com.apple.developer.team-identifier" "$ENTITLEMENTS" | tr -d '\n'`
329
+ if [ "$ENTITLEMENTS_TEAM_IDENTIFIER" == "" ];
330
+ then
331
+ echo "Provided entitlements file is missing a value for the required 'com.apple.developer.team-identifier' key" >&2
332
+ exit 1;
333
+ elif [ "$ENTITLEMENTS_TEAM_IDENTIFIER" != "$TEAM_IDENTIFIER" ];
334
+ then
335
+ echo "Provided entitlements file's 'com.apple.developer.team-identifier' '$ENTITLEMENTS_TEAM_IDENTIFIER' does not match the provided provisioning profile's value '$TEAM_IDENTIFIER'" >&2
336
+ exit 1;
337
+ fi
338
+ fi
339
+
340
+ echo "Resigning application using certificate: '$CERTIFICATE'" >&2
341
+ echo "and entitlements: $ENTITLEMENTS" >&2
342
+ /usr/bin/codesign -f -s "$CERTIFICATE" --entitlements="$ENTITLEMENTS" "$TEMP_DIR/Payload/$APP_NAME"
343
+ checkStatus
344
+ else
345
+ echo "Extracting existing entitlements for updating" >&2
346
+ /usr/bin/codesign -d --entitlements - "$TEMP_DIR/Payload/$APP_NAME" > "$TEMP_DIR/newEntitlements" 2> /dev/null
347
+ if [ $? -eq 0 ];
348
+ then
349
+ ENTITLEMENTS_TEMP=`cat "$TEMP_DIR/newEntitlements" | sed -E -e '1d'`
350
+ if [ -n "$ENTITLEMENTS_TEMP" ]; then
351
+ echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>$ENTITLEMENTS_TEMP" > "$TEMP_DIR/newEntitlements"
352
+ if [ -s "$TEMP_DIR/newEntitlements" ];
353
+ then
354
+ if [ "$TEAM_IDENTIFIER" != "" ];
355
+ then
356
+ PlistBuddy -c "Set :com.apple.developer.team-identifier ${TEAM_IDENTIFIER}" "$TEMP_DIR/newEntitlements"
357
+ checkStatus
358
+ fi
359
+ PlistBuddy -c "Set :application-identifier ${APP_IDENTIFER_PREFIX}.${BUNDLE_IDENTIFIER}" "$TEMP_DIR/newEntitlements"
360
+ checkStatus
361
+ PlistBuddy -c "Set :keychain-access-groups:0 ${APP_IDENTIFER_PREFIX}.${BUNDLE_IDENTIFIER}" "$TEMP_DIR/newEntitlements"
362
+ # checkStatus -- if this fails it's likely because the keychain-access-groups key does not exist, so we have nothing to update
363
+ if [[ "$CERTIFICATE" == *Distribution* ]]; then
364
+ echo "Assuming Distribution Identity"
365
+ if [ "$ADJUST_BETA_REPORTS_ACTIVE_FLAG" == "1" ]; then
366
+ echo "Ensuring beta-reports-active is present and enabled"
367
+ # new beta key is only used for Distribution; might not exist yet, if we were building Development
368
+ PlistBuddy -c "Add :beta-reports-active bool true" "$TEMP_DIR/newEntitlements"
369
+ if [ $? -ne 0 ]; then
370
+ PlistBuddy -c "Set :beta-reports-active YES" "$TEMP_DIR/newEntitlements"
371
+ fi
372
+ checkStatus
373
+ fi
374
+ echo "Setting get-task-allow entitlement to NO"
375
+ PlistBuddy -c "Set :get-task-allow NO" "$TEMP_DIR/newEntitlements"
376
+ else
377
+ echo "Assuming Development Identity"
378
+ if [ "$ADJUST_BETA_REPORTS_ACTIVE_FLAG" == "1" ]; then
379
+ # if we were building with Distribution profile, we have to delete the beta key
380
+ echo "Ensuring beta-reports-active is not included"
381
+ PlistBuddy -c "Delete :beta-reports-active" "$TEMP_DIR/newEntitlements"
382
+ # do not check status here, just let it fail if entry does not exist
383
+ fi
384
+ echo "Setting get-task-allow entitlement to YES"
385
+ PlistBuddy -c "Set :get-task-allow YES" "$TEMP_DIR/newEntitlements"
386
+ fi
387
+ checkStatus
388
+ plutil -lint "$TEMP_DIR/newEntitlements" > /dev/null
389
+ checkStatus
390
+ echo "Resigning application using certificate: '$CERTIFICATE'" >&2
391
+ echo "using existing entitlements updated with bundle identifier: '$APP_IDENTIFER_PREFIX.$BUNDLE_IDENTIFIER'" >&2
392
+ if [ "$TEAM_IDENTIFIER" != "" ];
393
+ then
394
+ echo "and team identifier: '$TEAM_IDENTIFIER'" >&2
395
+ fi
396
+ /usr/bin/codesign -f -s "$CERTIFICATE" --entitlements="$TEMP_DIR/newEntitlements" "$TEMP_DIR/Payload/$APP_NAME"
397
+ checkStatus
398
+ else
399
+ echo "Failed to create required intermediate file" >&2
400
+ exit 1;
401
+ fi
402
+ else
403
+ echo "No entitlements found" >&2
404
+ echo "Resigning application using certificate: '$CERTIFICATE'" >&2
405
+ echo "without entitlements" >&2
406
+ /usr/bin/codesign -f -s "$CERTIFICATE" "$TEMP_DIR/Payload/$APP_NAME"
407
+ checkStatus
408
+ fi
409
+ else
410
+ echo "Failed to extract entitlements" >&2
411
+ echo "Resigning application using certificate: '$CERTIFICATE'" >&2
412
+ echo "without entitlements" >&2
413
+ /usr/bin/codesign -f -s "$CERTIFICATE" "$TEMP_DIR/Payload/$APP_NAME"
414
+ checkStatus
415
+ fi
416
+ fi
417
+
418
+ # Remove the temporary files if they were created before generating ipa
419
+ rm -f "$TEMP_DIR/newEntitlements"
420
+ rm -f "$TEMP_DIR/profile.plist"
421
+
422
+ # Repackage quietly
423
+ echo "Repackaging as $NEW_FILE" >&2
424
+
425
+ # Zip up the contents of the "$TEMP_DIR" folder
426
+ # Navigate to the temporary directory (sending the output to null)
427
+ # Zip all the contents, saving the zip file in the above directory
428
+ # Navigate back to the orignating directory (sending the output to null)
429
+ pushd "$TEMP_DIR" > /dev/null
430
+ zip -qr "../$TEMP_DIR.ipa" *
431
+ popd > /dev/null
432
+
433
+ # Move the resulting ipa to the target destination
434
+ mv "$TEMP_DIR.ipa" "$NEW_FILE"
435
+
436
+ # Remove the temp directory
437
+ rm -rf "$TEMP_DIR"
438
+
439
+ echo "Process complete" >&2
@@ -4,6 +4,7 @@ require 'sigh/helper'
4
4
  require 'sigh/dependency_checker'
5
5
  require 'sigh/developer_center'
6
6
  require 'sigh/update_checker'
7
+ require 'sigh/resign'
7
8
 
8
9
  # Third Party code
9
10
  require 'colored'
@@ -76,19 +76,15 @@ module Sigh
76
76
  result = visit PROFILES_URL
77
77
  raise "Could not open Developer Center" unless result['status'] == 'success'
78
78
 
79
- if page.has_content?"Member Center"
80
- # Already logged in
81
- return true
82
- end
79
+ # Already logged in
80
+ return true if page.has_content? "Member Center"
83
81
 
84
82
  (wait_for_elements(".button.blue").first.click rescue nil) # maybe already logged in
85
83
 
86
84
  (wait_for_elements('#accountpassword') rescue nil) # when the user is already logged in, this will raise an exception
87
85
 
88
- if page.has_content?"Member Center"
89
- # Already logged in
90
- return true
91
- end
86
+ # Already logged in
87
+ return true if page.has_content? "Member Center"
92
88
 
93
89
  fill_in "accountname", with: user
94
90
  fill_in "accountpassword", with: password
@@ -96,9 +92,8 @@ module Sigh
96
92
  all(".button.large.blue.signin-button").first.click
97
93
 
98
94
  begin
99
- if page.has_content?"Select Team" # If the user is not on multiple teams
100
- select_team
101
- end
95
+ # If the user is not on multiple teams
96
+ select_team if page.has_content? "Select Team"
102
97
  rescue => ex
103
98
  Helper.log.debug ex
104
99
  raise DeveloperCenterLoginError.new("Error loggin in user #{user}. User is on multiple teams and we were unable to correctly retrieve them.")
@@ -198,37 +193,27 @@ module Sigh
198
193
  certs = post_ajax(@list_certs_url)
199
194
 
200
195
  Helper.log.info "Checking if profile is available. (#{certs['provisioningProfiles'].count} profiles found)"
196
+ required_cert_types = type == DEVELOPMENT ? ['iOS Development'] : ['iOS Distribution', 'iOS UniversalDistribution']
201
197
  certs['provisioningProfiles'].each do |current_cert|
202
- if type == DEVELOPMENT
203
- if current_cert['type'] != "iOS Development"
204
- next
205
- end
206
- end
207
-
208
- if type != DEVELOPMENT
209
- if not ['iOS Distribution', 'iOS UniversalDistribution'].include?current_cert['type']
210
- next
211
- end
212
- end
198
+ next unless required_cert_types.include?(current_cert['type'])
213
199
 
214
200
  details = profile_details(current_cert['provisioningProfileId'])
215
201
 
216
202
  if details['provisioningProfile']['appId']['identifier'] == app_identifier
217
- if type == APPSTORE and details['provisioningProfile']['deviceCount'] > 0
218
- next # that's an Ad Hoc profile. I didn't find a better way to detect if it's one ... skipping it
219
- end
220
- if type != APPSTORE and details['provisioningProfile']['deviceCount'] == 0
221
- next # that's an App Store profile ... skipping it
222
- end
203
+ # that's an Ad Hoc profile. I didn't find a better way to detect if it's one ... skipping it
204
+ next if type == APPSTORE && details['provisioningProfile']['deviceCount'] > 0
205
+
206
+ # that's an App Store profile ... skipping it
207
+ next if type != APPSTORE && details['provisioningProfile']['deviceCount'] == 0
223
208
 
224
209
  # We found the correct certificate
225
- if force and type != DEVELOPMENT
210
+ if force && type != DEVELOPMENT
226
211
  provisioningProfileId = current_cert['provisioningProfileId']
227
212
  renew_profile(provisioningProfileId, type, cert_date) # This one needs to be forcefully renewed
228
213
  return maintain_app_certificate(app_identifier, type, false, cert_date) # recursive
229
214
  elsif current_cert['status'] == 'Active'
230
215
  return download_profile(details['provisioningProfile']['provisioningProfileId']) # this one is already finished. Just download it.
231
- elsif ['Expired', 'Invalid'].include?current_cert['status']
216
+ elsif ['Expired', 'Invalid'].include? current_cert['status']
232
217
  renew_profile(current_cert['provisioningProfileId'], type, cert_date) # This one needs to be renewed
233
218
  return maintain_app_certificate(app_identifier, type, false, cert_date) # recursive
234
219
  end
@@ -250,7 +235,7 @@ module Sigh
250
235
 
251
236
  def create_profile(app_identifier, type, cert_date)
252
237
  Helper.log.info "Creating new profile for app '#{app_identifier}' for type '#{type}'.".yellow
253
- certificate = code_signing_certificate(type, cert_date)
238
+ certificates = code_signing_certificates(type, cert_date)
254
239
 
255
240
  create_url = "https://developer.apple.com/account/ios/profile/profileCreate.action"
256
241
  visit create_url
@@ -265,7 +250,7 @@ module Sigh
265
250
  enterprise = true
266
251
  end
267
252
 
268
- value = (enterprise ? 'inhouse' : 'store')
253
+ value = enterprise ? 'inhouse' : 'store'
269
254
  value = 'limited' if type == DEVELOPMENT
270
255
  value = 'adhoc' if type == ADHOC
271
256
 
@@ -273,7 +258,7 @@ module Sigh
273
258
  click_next
274
259
 
275
260
  # 2) Select the App ID
276
- while not page.has_content?"Select App ID" do sleep 1 end
261
+ sleep 1 while !page.has_content? "Select App ID"
277
262
  # example: <option value="RGAWZGXSY4">ABP (5A997XSHK2.net.sunapps.34)</option>
278
263
  identifiers = all(:xpath, "//option[contains(text(), '.#{app_identifier})')]")
279
264
  if identifiers.count == 0
@@ -288,19 +273,32 @@ module Sigh
288
273
  click_next
289
274
 
290
275
  # 3) Select the certificate
291
- while not page.has_content?"Select certificates" do sleep 1 end
276
+ sleep 1 while !page.has_content? "Select certificates"
292
277
  sleep 3
293
- Helper.log.info "Using certificate ID '#{certificate['certificateId']}' from '#{certificate['ownerName']}'"
278
+ Helper.log.info "Using certificates: #{certificates.map { |c| "#{c['ownerName']} (#{c['certificateId']})" } }"
294
279
 
295
280
  # example: <input type="radio" name="certificateIds" class="validate" value="[XC5PH8D47H]"> (production)
296
- id = certificate["certificateId"]
297
- certs = all(:xpath, "//input[@type='radio' and @value='[#{id}]']") if type != DEVELOPMENT # production uses radio and has a [] around the value
298
- certs = all(:xpath, "//input[@type='checkbox' and @value='#{id}']") if type == DEVELOPMENT # development uses a checkbox and has no [] around the value
299
- if certs.count != 1
300
- Helper.log.info "Looking for certificate: #{certificate}. Found: #{certs.count}"
281
+
282
+ clicked = false
283
+ certificates.each do |cert|
284
+ cert_id = cert['certificateId']
285
+ input = if type == DEVELOPMENT
286
+ # development uses a checkbox and has no [] around the value
287
+ first(:xpath, "//input[@type='checkbox' and @value='#{cert_id}']")
288
+ else
289
+ break if clicked
290
+ # production uses radio and has a [] around the value
291
+ first(:xpath, "//input[@type='radio' and @value='[#{cert_id}]']")
292
+ end
293
+ if input
294
+ input.click
295
+ clicked = true
296
+ end
297
+ end
298
+
299
+ if !clicked
301
300
  raise "Could not find certificate in the list of available certificates."
302
301
  end
303
- certs.first.click
304
302
  click_next
305
303
 
306
304
  if type != APPSTORE
@@ -321,7 +319,7 @@ module Sigh
321
319
  end
322
320
 
323
321
  def renew_profile(profile_id, type, cert_date)
324
- certificate = code_signing_certificate(type, cert_date)
322
+ certificate = code_signing_certificates(type, cert_date).first
325
323
 
326
324
  details_url = "https://developer.apple.com/account/ios/profile/profileEdit.action?type=&provisioningProfileId=#{profile_id}"
327
325
  Helper.log.info "Renewing provisioning profile '#{profile_id}' using URL '#{details_url}'"
@@ -367,9 +365,9 @@ module Sigh
367
365
  end
368
366
  end
369
367
 
370
- # Returns a hash, that contains information about the iOS certificate
368
+ # Returns a array of hashs, that contains information about the iOS certificate
371
369
  # @example
372
- # {"certRequestId"=>"B23Q2P396B",
370
+ # [{"certRequestId"=>"B23Q2P396B",
373
371
  # "name"=>"SunApps GmbH",
374
372
  # "statusString"=>"Issued",
375
373
  # "expirationDate"=>"2015-11-25T22:45:50Z",
@@ -385,10 +383,10 @@ module Sigh
385
383
  # "certificateTypeDisplayId"=>"...",
386
384
  # "serialNum"=>"....",
387
385
  # "typeString"=>"iOS Distribution"},
388
- def code_signing_certificate(type, cert_date)
386
+ # {another sertificate...}]
387
+ def code_signing_certificates(type, cert_date)
389
388
  certs_url = "https://developer.apple.com/account/ios/certificate/certificateList.action?type="
390
- certs_url << "distribution" if type != DEVELOPMENT
391
- certs_url << "development" if type == DEVELOPMENT
389
+ certs_url << (type == DEVELOPMENT ? 'development' : 'distribution')
392
390
  visit certs_url
393
391
 
394
392
  certificateDataURL = wait_for_variable('certificateDataURL')
@@ -400,27 +398,44 @@ module Sigh
400
398
  # https://developer.apple.com/services-account/.../account/ios/certificate/listCertRequests.action?content-type=application/x-www-form-urlencoded&accept=application/json&requestId=...&userLocale=en_US&teamId=...&types=...&status=4&certificateStatus=0&type=distribution
401
399
 
402
400
  certs = post_ajax(url)['certRequests']
401
+
402
+ ret_certs = []
403
+ certificate_name = ENV['SIGH_CERTIFICATE']
404
+
405
+ # The other profiles are push profiles
406
+ certificate_type = type == DEVELOPMENT ? 'iOS Development' : 'iOS Distribution'
407
+
408
+ # New profiles first
409
+ certs.sort! do |a, b|
410
+ Time.parse(b['expirationDate']) <=> Time.parse(a['expirationDate'])
411
+ end
412
+
403
413
  certs.each do |current_cert|
404
- if type != DEVELOPMENT and current_cert['typeString'] == 'iOS Distribution'
405
- # The other profiles are push profiles
406
- # We only care about the distribution profile
407
- unless cert_date
408
- return current_cert # mostly we only care about the 'certificateId'
409
- else
410
- if current_cert['expirationDateString'] == cert_date
411
- Helper.log.info "Certificate ID '#{current_cert['certificateId']}' with expiry date '#{current_cert['expirationDateString']}' located"
412
- return current_cert
413
- end
414
+ next unless current_cert['typeString'] == certificate_type
415
+
416
+ if cert_date || certificate_name
417
+ if current_cert['expirationDateString'] == cert_date
418
+ Helper.log.info "Certificate ID '#{current_cert['certificateId']}' with expiry date '#{current_cert['expirationDateString']}' located"
419
+ ret_certs << current_cert
420
+ end
421
+ if current_cert['name'] == certificate_name
422
+ Helper.log.info "Certificate ID '#{current_cert['certificateId']}' with name '#{certificate_name}' located"
423
+ ret_certs << current_cert
414
424
  end
415
- elsif type == DEVELOPMENT and current_cert['typeString'] == 'iOS Development'
416
- return current_cert # mostly we only care about the 'certificateId'
425
+ else
426
+ ret_certs << current_cert
417
427
  end
418
428
  end
419
429
 
420
- error_message_no_cert_with_date = "Could not find a Certificate with expiry date '#{cert_date}'. Please open #{current_url} and make sure you have a signing profile created.".red
421
- error_message_no_cert = "Could not find a Certificate. Please open #{current_url} and make sure you have a signing profile created.".red
430
+ return ret_certs unless ret_certs.empty?
431
+
432
+ predicates = []
433
+ predicates << "name: #{certificate_name}" if certificate_name
434
+ predicates << "expiry date: #{cert_date}" if cert_date
435
+
436
+ predicates_str = " with #{predicates.join(' or ')}"
422
437
 
423
- raise cert_date ? error_message_no_cert_with_date : error_message_no_cert
438
+ raise "Could not find a Certificate#{predicates_str}. Please open #{current_url} and make sure you have a signing profile created.".red
424
439
  end
425
440
 
426
441
  # Download a file from the dev center, by using a HTTP client. This will return the content of the file
@@ -464,7 +479,7 @@ module Sigh
464
479
  def wait_for(method, parameter, success)
465
480
  counter = 0
466
481
  result = method.call(parameter)
467
- while !success.call(result)
482
+ while !success.call(result)
468
483
  sleep 0.2
469
484
 
470
485
  result = method.call(parameter)
@@ -45,5 +45,14 @@ module Sigh
45
45
  return "" if self.is_test? and not OS.mac?
46
46
  `xcode-select -p`.gsub("\n", '') + "/"
47
47
  end
48
+
49
+ # Path to the installed gem to load resources (e.g. resign.sh)
50
+ def self.gem_path
51
+ if not Helper.is_test? and Gem::Specification::find_all_by_name('sigh').any?
52
+ return Gem::Specification.find_by_name('sigh').gem_dir
53
+ else
54
+ return './'
55
+ end
56
+ end
48
57
  end
49
58
  end
@@ -0,0 +1,59 @@
1
+ module Sigh
2
+ # Resigns an existing ipa file
3
+ class Resign
4
+ def run(options)
5
+ get_inputs(options)
6
+
7
+ command = [
8
+ @resign_path,
9
+ "'#{@ipa}'",
10
+ "'#{@signing_identity}'",
11
+ "-p '#{@provisioning_profile}'",
12
+ "'#{@ipa}'"
13
+ ].join(' ')
14
+
15
+ puts command.magenta
16
+ puts `#{command}`
17
+ end
18
+
19
+ def get_inputs(options)
20
+ @resign_path = File.join(Helper.gem_path, 'lib', 'assets', 'resign.sh')
21
+ raise "Could not find resign.sh file. Please try re-installing the gem.".red unless File.exists?@resign_path
22
+
23
+ @ipa = options.ipa || find_ipa || ask("Path to ipa file: ")
24
+ @signing_identity = options.signing_identity || ask_for_signing_identity
25
+ validate_signing_identity
26
+ @provisioning_profile = options.provisioning_profile || find_provisioning_profile || ask("Path to provisioning file: ")
27
+ end
28
+
29
+ def find_ipa
30
+ Dir[File.join(Dir.pwd, "*.ipa")].sort { |a,b| File.mtime(a) <=> File.mtime(b) }.first
31
+ end
32
+
33
+ def find_provisioning_profile
34
+ Dir[File.join(Dir.pwd, "*.mobileprovision")].sort { |a,b| File.mtime(a) <=> File.mtime(b) }.first
35
+ end
36
+
37
+ def validate_signing_identity
38
+ while not installed_identies.include?@signing_identity
39
+ Helper.log.error "Couldn't find signing identity '#{@signing_identity}'. Available identities: \n\t#{installed_identies.join("\n\t")}\n"
40
+ @signing_identity = ask_for_signing_identity
41
+ end
42
+ end
43
+
44
+ def ask_for_signing_identity
45
+ ask("Signing Identity (e.g. 'iPhone Distribution: SunApps GmbH (5A997XAHK2)'): ")
46
+ end
47
+
48
+ # Array of available signing identities
49
+ def installed_identies
50
+ available = `security find-identity -v -p codesigning`
51
+ ids = []
52
+ available.split("\n").each do |current|
53
+ (ids << current.match(/.*\"(.*)\"/)[1]) rescue nil # the last line does not match
54
+ end
55
+
56
+ return ids
57
+ end
58
+ end
59
+ end
@@ -1,3 +1,3 @@
1
1
  module Sigh
2
- VERSION = "0.2.4"
2
+ VERSION = "0.3.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sigh
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felix Krause
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-05 00:00:00.000000000 Z
11
+ date: 2015-02-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - ~>
60
60
  - !ruby/object:Gem::Version
61
- version: 4.2.0
61
+ version: 4.3.0
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ~>
67
67
  - !ruby/object:Gem::Version
68
- version: 4.2.0
68
+ version: 4.3.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: credentials_manager
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -232,10 +232,12 @@ files:
232
232
  - LICENSE
233
233
  - README.md
234
234
  - bin/sigh
235
+ - lib/assets/resign.sh
235
236
  - lib/sigh.rb
236
237
  - lib/sigh/dependency_checker.rb
237
238
  - lib/sigh/developer_center.rb
238
239
  - lib/sigh/helper.rb
240
+ - lib/sigh/resign.rb
239
241
  - lib/sigh/update_checker.rb
240
242
  - lib/sigh/version.rb
241
243
  homepage: http://fastlane.tools