iOS Ad-Hoc Beta publishing directly from Xcode


A couple of weeks ago I posted about automating Ad-Hoc publishing using some simple shell scripting and a modified version of the BetaBuilder utility by Hunter Hillegas. Based on a comment on his blog I’ve taken this a step further: I’ve fully integrated the publishing mechanism into Xcode. Here’s how…

Aggregate Targets

First step, if you don’t have one already, create a build configuration called something like “Ad Hoc” to differentiate a build from the normal Release and Debug variants. This is easily done: Go to the Project menu → Edit Project Settings (or, right-click or control-click on the project name at the top of Groups & Files or you can even double-click on the icon to open this). Go to the Configurations tab and “Duplicate” the Release configuration. I call this copy “Ad Hoc“. Then you can change the code signing identity of this new configuration to the Ad Hoc profile you generate on the Apple Provisioning Portal. Remember in future that if you change any settings in the Release configuration, you may also need to change them in the Ad Hoc one too. Note: Does it need to be called Ad Hoc?»

Xcode Aggregate Targets

Xcode Aggregate Targets

At first thought the approach to use to automate distribution appeared to be adding a build step at the end of the target configuration in Xcode – however this has a problem: Codesigning occurs after any configurable build steps and we need to be able to use the signed version of the package. No problem! Xcode has an aggregate target type for just this situation – a Shell Script Target whose description reads “Target that just executes a script rather than building a product”. Better yet, aggregate targets are designed to be dependent on other targets so that they get executed after they have been built. This is exactly what we need. You add a target by right-clicking (control-click) on Targets in Groups & Files, choosing Add → New Target… and in the Mac OS X → Other section there’s a selection of aggregate targets. I name this target “Package & Distribute Ad Hoc” but you can call it whatever you want.

Example Shell Script Target

Example Shell Script Target

You now have a new target in Groups & Files, expand it and you’ll see it has a single build step named “Run Script“. Right-click on this, select Get Info and you’re presented with a dialog with a basic “Script“. What I use here looks like:

Package and Distribute Ad Hoc → Run Script
1
2
${HOME}/bin/ipa-packager || exit 1
exit 0

Quite simply, ipa-packager is a shell script based on the one in a previous blog post that does the packaging for me. If it returns an error code, so do we – this is the signal to Xcode to stop the build process. All the other options to the Run Script dialog we leave at defaults.

Shell Script Configuration

Shell Script Configuration

Lastly, we need to make this new target depend on the target of our app. The easiest mechanism to do this is to simply drag-and-drop the app target, under Groups & Files, into the new target. Job done.

Packaging revisited

So, what next? If we try to build the project above it will complain because ipa-packager doesn’t exist. I reworked the script from my previous blog post to work either directly from the command line or using the environment variables that Xcode passes to scripts. The revised version appears below and is attached at the bottom, for download.

I also improved a number of items, particularly dependency checking so it does not re-do work that is unnecessary. Another area I paid attention to is being able to provide some of the settings in either the Xcode project or target build configuration – any user-defined values are also provided in the environment of the script. This is just as well since, because the script is called from its own target, the environment variable identifying the current target is that of the script, not our iOS app! Consequently, I have to provide at least one setting in the project: ADHOC_TARGET_NAME which I set to the name of the app.

One more major improvement is the provision of a local “configuration” file that allows a user of this script to set their own defaults without having to tweak the script itself. An example is given below. I will at some point add a command line tweak to allow for an arbitrary file to be used for this configuration.

Shell script: ipa-packager» Configuration file: ~/.adhoc-distribution.conf»

Naturally you will wish to alter the publishing URL to one that works for you or, as described in the next section, you can do this in the project configuration inside Xcode.

The observant reader will have noticed references to an ADHOC_TEMPLATE_HTML value. This is not yet used but it will allow a project to provide its own HTML template for the generated index.html file, the same way it does now for the README.txt file.

Configuring from Xcode

So, we have our Xcode target and we have the script that does the magic of packaging and distributing an Ad Hoc build. There’s one component left – the values that we need to pass from Xcode to ipa-packager. There’s more than one place you can do this (project or target level), and you can apply it to just Ad Hoc builds or to all – even though it won’t distribute code for non Ad Hoc builds, you may wish to archive development and release builds all the same. You should decide what functionality you want and where to keep the values. The suggested approach is to keep values unique to a target within that targets configuration and is what I shall demonstrate here.

Xcode Target Build Configuration

Xcode Target Build Configuration

We need the Target Info page open for the aggregate “Run Shell Script” target we added earlier (right-click, control-click or double-click on the target under Groups & Files) and select the Build tab. The item “Configuration:” is where you will decide whether to apply the values to one specific configuration (such as Ad Hoc) or to all of them. Then quit simply use the cog-menu at bottom-left to Add User-Defined Setting. There is only value you must specify as barest minimum: ADHOC_TARGET_NAME which needs to refer to the name of the target in the project that contains your app. This should also be the same as the application name – that is, the name of the .app package.

You can also specify any of the values used by ipa-packager though note that the results may be undefined if you try to redefine a value provided by Xcode itself. Where I use a common URL scheme for all my projects, others may wish to specify a unique URL for each project and you can do that here by adding a value for ADHOC_PUBLISHING_URL to their project. See the script for the full list of values it will look for.

Replaceable values

Some of the environment variables that ipa-packager uses have a notion of replaceable values. These are components of a string that are replaced with values that are determined at runtime. The idea is to enable these variables to provide a template for the final value. For example, the default for ADHOC_PUBLISHING_URL is http://localhost/iosbeta/#SHORTBUNDLEID#-#BUILD#. Note: You keep using 'localhost'. Why?» #SHORTBUNDLEID# is replaced with “abcsea” (the last component of org.flirble.abcsea) and #BUILD# is replaced by the current build number which is discovered by reading the key CFBundleVersion from the targets Info.plist file.

Not all variables are checked for replaceable values since it is not a trivial process in a shell script. You can check in the script to see which ones are by looking at the contents of variable replacement_options. At the time of writing, this contained:

1
2
3
replacement_options="ADHOC_PUBLISHING_URL ADHOC_DELIVERY_NAME \
   ADHOC_TEMPLATE_HTML ADHOC_PUBLISHING_DIRECTORY \
   ADHOC_PUBLISHING_COMMAND"

The replaceable values are:

#PROJECT# Project name (spaces removed)
#APP# or #TARGET# The app/target name (spaces removed)
#CONFIG# The build configuration name (Release, Ad Hoc)
#VERSION# The version number (from CFBundleShortVersionString)
#BUILD# The build number (from CFBundleVersion)
#BUNDLEID# The bundle ID
#SHORTBUNDLEID# The last component of the bundle ID
#PLATFORM# The targetted platform, iphoneos or iphonesimulator

These values can appear anywhere within variables that are inspected, and any number of times.

The result

A picture says it all…

Xcode Build Results

Xcode Build Results with ipa-packager

Attachments

Note that some files may be downloaded with a .txt file extension to overcome WordPress security checks. Just rename the file if necessary once downloaded.

Related Reading

Learning iOS Programming: From Xcode to App Store
iOS Programming: The Big Nerd Ranch Guide (3rd Edition) (Big Nerd Ranch Guides)
iPad: The Missing Manual
How To Create An App Crash Course - Learn How To Create iPhone App, Create Android Apps And More
Beginning iOS 6 Development: Exploring the iOS SDK
Classic Shell Scripting
Did you like this? Share it:
You don’t need to call it Ad Hoc – the packaging script below contains a configurable value for this name, ADHOC_CONFIG_ADHOC which you are free to change.Powered by Hackadelic Sliding Notes 1.6.5

ipa-packager
1
2
3
4
5
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
#!/bin/sh
# Build an IPA package from an Ad Hoc build and then
# package it for Ad Hoc distribution.
# This version is designed for use as a Shell Script Target
# called directly by Xcode.
# (c) 2010 Chris Luke. Copying is fine, but please attribute to me!

# Useful environment variables Xcode provides:
# BUILD_DIR
# CONFIGURATION
# CONFIGURATION_BUILD_DIR
# CONFIGURATION_TEMP_DIR
# EFFECTIVE_PLATFORM_NAME
# ACTION (build|clean)
# CURRENT_PROJECT_VERSION
# PROJECT_DIR
# PROJECT_NAME
# PROJECT_TEMP_DIR
# TARGET_TEMP_DIR

# User settings we can place in the target settings:
# ADHOC_PUBLISHING_URL  with replaceables
# ADHOC_TARGET_NAME     the target we want to copy from
# ADHOC_BUNDLE_SOURCE   the build dir of the app we want to copy for
#                       distribution- built from TARGET_NAME if not set
# ADHOC_TEMPLATE_HTML   template for index.html
# ADHOC_TEMPLATE_README templatye for readme file
# ADHOC_DELIVERY_NAME   name of .zip/.ipa with replaceables
# ADHOC_DELIVERY_DIRECTORY directory to deliver a .zip archive of app to
#                       ie, releases, archived copies
# ADHOC_DELIVER_DSYM    If set and not 0 or no then we also copy the .dSYM
# ADHOC_PUBLISHING_DIRECTORY directory to deliver the pkg to with replaceables
# ADHOC_PUBLISHING_COMMAND command to execute to push materials out with
# ADHOC_CONFIG_ADHOC  the label used for ad hoc builds
# ADHOC_CONFIG_RELEASE  the label used for release builds
# ADHOC_AUTHOR

# Configuration can go into ~/.adhoc-distribution.conf . Defaults have the
# form DEFAULT_<VARIABLE_NAME> and are used only if the variable is not set.
# If you name a variable directly in the config, it overwrites any existing
# value.

# Replaceable values, where supported:
# #PROJECT#       Project name  (spaces removed)
# #APP# #TARGET#  The app/target name (spaces removed)
# #CONFIG#        The build configuration name (Release, Ad Hoc)
# #VERSION#       The version number  (from CFBundleShortVersionString)
# #BUILD#         The build number  (from CFBundleVersion)
# #BUNDLEID#      The bundle ID
# #SHORTBUNDLEID# The last component of the bundle ID
# #PLATFORM#      The targetted platform, iphoneos or iphonesimulator
#

pfx="Ad-Hoc IPA Packager:"
err="$pfx"
config_file="$HOME/.adhoc-distribution.conf"

# Some generic defaults. These can be overridden in a local config file
DEFAULT_BASEDIR=
DEFAULT_ADHOC_PUBLISHING_URL="http://localhost/iosbeta/#SHORTBUNDLEID#-#BUILD#"
DEFAULT_ADHOC_PUBLISHING_DIRECTORY="$HOME/Documents/iosbeta/#SHORTBUNDLEID#-#BUILD#"
DEFAULT_ADHOC_PUBLISHING_COMMAND=$HOME/bin/sync-iosbeta
DEFAULT_ADHOC_DELIVERY_NAME="#APP#-#VERSION#-#BUILD#-#CONFIG#.ipa"
DEFAULT_ADHOC_DELIVERY_DIRECTORY="$HOME/Documents/iOSArchives"
DEFAULT_ADHOC_DELIVER_DSYM=yes
DEFAULT_ADHOC_CONFIG_ADHOC="Ad Hoc"
DEFAULT_ADHOC_CONFIG_RELEASE="Release"
DEFAULT_ADHOC_AUTHOR=$(id -P | cut -d: -f8)
DEFAULT_verbose=0
DEFAULT_FORCEPKG=0

default_options="BASEDIR \
    ADHOC_PUBLISHING_URL ADHOC_TARGET_NAME ADHOC_BUNDLE_SOURCE \
    ADHOC_TEMPLATE_HTML ADHOC_TEMPLATE_README ADHOC_DELIVERY_NAME \
    ADHOC_DELIVERY_DIRECTORY ADHOC_PUBLISHING_DIRECTORY \
    ADHOC_PUBLISHING_COMMAND ADHOC_CONFIG_ADHOC ADHOC_CONFIG_RELEASE \
    ADHOC_DELIVER_DSYM ADHOC_AUTHOR verbose FORCEPKG"

mandatory_options="ADHOC_TARGET_NAME ADHOC_DELIVERY_NAME \
    ADHOC_DELIVERY_DIRECTORY ADHOC_CONFIG_ADHOC ADHOC_CONFIG_RELEASE \
    BUILD_DIR CONFIGURATION CONFIGURATION_BUILD_DIR \
    CONFIGURATION_TEMP_DIR ACTION PROJECT_DIR PROJECT_NAME \
    TARGET_TEMP_DIR"

mandatory_directories="PROJECT_DIR CONFIGURATION_BUILD_DIR \
    CONFIGURATION_TEMP_DIR TARGET_TEMP_DIR ADHOC_DELIVERY_DIRECTORY"

replacement_options="ADHOC_PUBLISHING_URL ADHOC_DELIVERY_NAME \
    ADHOC_TEMPLATE_HTML ADHOC_PUBLISHING_DIRECTORY \
    ADHOC_PUBLISHING_COMMAND"


# Load any local configuration
[ -f "${config_file}" ] && . "${config_file}"

# See if any defaults need to be loaded
for opt in ${default_options}; do
    val=
    eval "val=\"\$$opt\""
    if [ -z "$val" ]; then
        val=
        eval "val=\"\$DEFAULT_$opt\""
        [ ! -z "$val" ] && eval "$opt=\"$val\""
    fi
done

docmd=0
dohelp=0
[ -t 0 -a -z "${ACTION}" ] && dohelp=1
while [ "$1" ]; do
    opt=$1
    dohelp=0
    case "$opt" in
    --project|-p)
        PROJECT_NAME="$2"; shift
        docmd=1
        shift;;

    --target|--app|-t|-a)
        ADHOC_TARGET_NAME="$2"; shift
        docmd=1
        shift;;

    --config*|-c)
        CONFIGURATION="$2"; shift
        shift;;

    --basedir|-b)
        if [ -z "${PROJECT_NAME}" ]; then
            echo "$err Specify project name before base directory."
            exit 1
        fi
        if [ -z "${ADHOC_TARGET_NAME}" ]; then
            echo "$err Specify target name before base directory."
            exit 1
        fi
        BASEDIR="$2"; shift
        shift;;

    --force*|-f)
        FORCEPKG=1
        shift;;

    --verbose|-v)
        verbose=1
        shift;;

    --help|-h)
        dohelp=1
        shift;;
    *)
        dohelp=1
        echo "Unknown option \"$opt\"."
        shift;;
    esac
    [ "${dohelp}" = 1 ] && break
done
if [ "${dohelp}" = 1 ]; then
cat << EOT
$pfx Usage: $(basename $0) [options]
    --project|-p <name>     Project name
    --target|--app|-t|-a <name> Target name
    --config|-c <name>      Project configuration name
                    (Debug, Release, Ad Hoc)
    --basedir|-b <dir>              Parent directory of projects
    --force|-f          Force packaging, even if up-to-date

    --help|-h           This message
    --verbose|-v            Verbose mode

Configuration can also be loaded from "${config_file}"

EOT

    exit 1
fi

if [ ! -z "${BASEDIR}" -a "${docmd}" = 1 -a ! -z "${PROJECT_NAME}" -a \
        ! -z "${ADHOC_TARGET_NAME}" ]; then
    if [ ! -d "${BASEDIR}" ]; then
        echo "$err Basedir \"${BASEDIR}\" is not readable."
        exit 1
    fi

    [ -z "${CONFIGURATION}" ] && CONFIGURATION="${DEFAULT_ADHOC_CONFIG_ADHOC}"
    ACTION=build
    EFFECTIVE_PLATFORM_NAME=-iphoneos
    PROJECT_DIR="${BASEDIR}/${PROJECT_NAME}"
    BUILD_DIR="${PROJECT_DIR}/build"
    PROJECT_TEMP_DIR="${BUILD_DIR}/${PROJECT_NAME}.build"
    CONFIGURATION_BUILD_DIR="${BUILD_DIR}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}"
    CONFIGURATION_TEMP_DIR="${BUILD_DIR}/${PROJECT_NAME}.build/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/${PROJECT_NAME}.build"
    TARGET_BUILD_DIR="${CONFIGURATION_BUILD_DIR}"
    TARGET_TEMP_DIR="${BUILD_DIR}/${PROJECT_NAME}.build/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/${ADHOC_TARGET_NAME}.build"
fi

if [ "${ACTION}" != build ]; then
    if [ "${docmd}" = 1 ]; then
        echo "$err Incomplete parameters."
        echo "Need basedir, project name and target name, at minimum."
        exit 1
    fi
    [ "${verbose}" = 1 ] && echo "$pfx No packaging for action \"${ACTION}\"."
    exit 0  # we only work when building, not cleaning
fi

if [ -z "${BUILD_DIR}" -o ! -d "${BUILD_DIR}" ]; then
    echo "$err No valid BUILD_DIR!"
    exit 1
fi

# Check for mandatory values
for opt in ${mandatory_options}; do
    val=
    eval "val=\"\$$opt\""
    if [ -z "$val" ]; then
        echo "$err Variable $opt is mandatory, yet empty."
        exit 1
    fi
done

# Check for mandatory directories
for opt in ${mandatory_directories}; do
    val=
    eval "val=\"\$$opt\""
    if [ ! -d "$val" ]; then
        echo "$err $opt is a missing mandatory directory \"$val\"."
        exit 1
    fi
done

if [ -z "${ADHOC_BUNDLE_SOURCE}" ]; then
    ADHOC_BUNDLE_SOURCE="${CONFIGURATION_BUILD_DIR}/${ADHOC_TARGET_NAME}.app"
fi

if [ ! -d "${ADHOC_BUNDLE_SOURCE}" ]; then
    echo "$err Can't find App \"${ADHOC_TARGET_NAME}\" at \"${ADHOC_BUNDLE_SOURCE}\""
    exit 1
fi

plist="${ADHOC_BUNDLE_SOURCE}/Info.plist"
if [ ! -f "${plist}" ]; then
    echo "$err Can't find \"Info.plist\" at \"${plist}\""
pwd
    exit 1
fi

# Get build and release versions
bundle_id=$(plutil -convert xml1 -o - - < "${plist}"  | \
    grep -A 1 CFBundleIdentifier | \
    sed -e 's/<[^>]*>//g' -e 's/[   ]//g'| tail -1)
bundle_short=$(echo "${bundle_id}" | sed -Ee 's/^.*\.(.*)$/\1/')
release_ver=$(plutil -convert xml1 -o - - < "${plist}"  | \
    grep -A 1 CFBundleShortVersionString | \
    sed -e 's/<[^>]*>//g' -e 's/[   ]//g'| tail -1)
build_ver=$(plutil -convert xml1 -o - - < "${plist}"  | \
    grep -A 1 CFBundleVersion | \
    sed -e 's/<[^>]*>//g' -e 's/[   ]//g'| tail -1)
config=$(echo "${CONFIGURATION}" | sed -e 's/  *//g')
ws_project_name=$(echo "${PROJECT_NAME}" | sed -e 's/  */_/g')
ws_app_name=$(echo "${ADHOC_TARGET_NAME}" | sed -e 's/  */_/g')
ws_platform=$(echo "${EFFECTIVE_PLATFORM_NAME}" | sed -e 's/^-//')

echo "Packaging project \"${PROJECT_NAME}\" target \"${ADHOC_TARGET_NAME}\" config \"${CONFIGURATION}\""
echo "* Bundle ID \"${bundle_id}\" (${bundle_short}) version ${release_ver} build ${build_ver}..."

# Do parameter replacement
for opt in ${replacement_options}; do
    val=
    eval "val=\"\$$opt\""
    val=$(echo "$val" | sed -e "s/#PROJECT#/${ws_project_name}/g" \
        -e "s/#APP#/${ws_app_name}/g" \
        -e "s/#TARGET#/${ws_app_name}/g" \
        -e "s/#CONFIG#/${config}/g" \
        -e "s/#BUILD#/${build_ver}/g" \
        -e "s/#VERSION#/${release_ver}/g" \
        -e "s/#BUNDLEID#/${bundle_id}/g" \
        -e "s/#SHORTBUNDLEID#/${bundle_short}/g" \
        -e "s/#PLATFORM#/${ws_platform}/g" )
    eval "$opt=\"$val\""
done

mkdir -p "${ADHOC_DELIVERY_DIRECTORY}" || exit 1
[ -z "${ADHOC_DELIVERY_NAME}" ] && \
    ADHOC_DELIVERY_NAME="${ADHOC_TARGET_NAME}-${release_ver}-${build_ver}-${config}.zip"
zip="${ADHOC_DELIVERY_DIRECTORY}/${ADHOC_DELIVERY_NAME}"
zip=$(echo "${zip}" | sed -e 's/  */_/g')
if [ "$(echo "${zip}" | grep -Ec '\.(zip|ipa)$')" = 0 ]; then
    echo "$err ADHOC_DELIVERY_NAME needs to end with .ipa or .zip"
    echo "The expanded name was: \"$zip\"."
    exit 1
fi
[ "$verbose" = 1 ] && echo "+ zip=$zip"
dsymin="${ADHOC_BUNDLE_SOURCE}.dSYM"
dsymout=$(echo "${zip}" | sed -Ee 's/\.(zip|ipa)$/.dSYM.zip/')
[ "$verbose" = 1 ] && echo "+ dsymin=$dsymin"
[ "$verbose" = 1 ] && echo "+ dsym=$dsymout"

dozip=yes
if [ -f "${zip}" ]; then
    # See if zip is older than the code signature or binary
    tf="${ADHOC_BUNDLE_SOURCE}/_CodeSignature/CodeResources"
    [ -f "${tf}" ] || tf="${ADHOC_BUNDLE_SOURCE}/${ADHOC_TARGET_NAME}"
    if [ -f "${tf}" -a "${zip}" -nt "${tf}" ]; then
        dozip=no
        [ "${verbose}" = 1 ] && echo "$pfx Existing archive is still current, no need to build."
    fi
fi

dodsym=no
if [ ! -z "${ADHOC_DELIVER_DSYM}" -a -d "${dsymin}" ]; then
    case "${ADHOC_DELIVER_DSYM}" in
    [Nn][Oo]|0)
        dodsym=no;;
    *)
        dodsym=yes;;
    esac
    if [ "${dsymin}" -ot "${dsymout}" ]; then
        [ "${verbose}" = 1 ] && echo "$pfx Existing dSYM is still current, no need to copy."
        dodsym=no
    fi
fi

if [ "${dozip}" = yes -o "${FORCEPKG}" = 1 ]; then
    # Make a copy of the build into the Payload directory
    tmpdir="${TARGET_TEMP_DIR}/adhoc-pkg"
    payloaddir="${tmpdir}/Payload/${ADHOC_TARGET_NAME}.app"
    payload="Payload"
    rm -rf "${tmpdir}"
    mkdir -p "${payloaddir}" || exit 1
    cp -rp "${ADHOC_BUNDLE_SOURCE}/" "${payloaddir}/" || exit 1

    oldpwd=$(pwd)
    cd "${payloaddir}/../../" || exit 1
    bindir="${payload}/${ADHOC_TARGET_NAME}.app"

    # Now zip it up
    year=$(date +%Y)
    rm -f "${zip}"
    opt_zip=
    [ "${verbose}" = 0 ] && opt_zip="-q"
    zip -Xyrz ${opt_zip} "${zip}" "${bindir}" << EOT | grep -v 'enter new zip file comment'
Project: ${PROJECT_NAME}
App: ${ADHOC_TARGET_NAME}
Build configuration: ${CONFIGURATION}
Release: ${release_ver}
Build: ${build_ver}
(c) ${year} ${ADHOC_AUTHOR}

.
EOT

    #zip -T "${zip}" || exit 1
    unzip -tq "${zip}" || exit 1
    echo "Packaged into ${zip}"
    cd "${oldpwd}"
    rm -rf "${tmpdir}"
fi

if [ "${dodsym}" = yes ]; then
    [ "${verbose}" = 1 ] && echo "$pfx Archiving dSYM..."
    oldpwd=$(pwd)
    cd "${dsymin}/../"

    year=$(date +%Y)
    rm -f "${dsymout}"
    opt_zip=
    [ "${verbose}" = 0 ] && opt_zip="-q"
    dsymname=$(basename "${dsymin}")
    zip -Xyrz ${opt_zip} "${dsymout}" "${dsymname}" << EOT | grep -v 'enter new zip file comment'
Project: ${PROJECT_NAME}
App: ${ADHOC_TARGET_NAME}
Build configuration: ${CONFIGURATION}
Release: ${release_ver}
Build: ${build_ver}
(c) ${year} ${ADHOC_AUTHOR}

.
EOT

    cd "${oldpwd}"
fi

# If it's Ad Hoc, the right options are set, it's iphoneos and it has been codesigned, we can publish it.
if [ "${CONFIGURATION}" = "${ADHOC_CONFIG_ADHOC}" -a \
    "${ADHOC_PUBLISHING_DIRECTORY}" -a "${ADHOC_PUBLISHING_URL}" \
    -a "${EFFECTIVE_PLATFORM_NAME}" = "-iphoneos" -a \
    -f "${ADHOC_BUNDLE_SOURCE}/_CodeSignature/CodeResources" ]; then
    # If it exists already, see if it is fresh.
    dopkg=yes
    pkg="${ADHOC_PUBLISHING_DIRECTORY}/"$(echo "${ADHOC_DELIVERY_NAME}" | \
        sed -e 's/\.ipa$/.zip/')
    if [ -f "${pkg}" ]; then
        if [ "${pkg}" -nt "${zip}" ]; then
            dopkg=no
            [ "${verbose}" = 1 ] && echo "$pfx Existing package is still current, no need to package and distribute."
        fi
    fi
    if [ "${dopkg}" = yes -o "${FORCEPKG}" = 1 ]; then
        # One more step - package it up for iosbeta distribution!
        echo "Distributing Ad Hoc release into ${ADHOC_PUBLISHING_DIRECTORY}..."
        mkdir -p "${ADHOC_PUBLISHING_DIRECTORY}" || exit 1
        opt_readme=
        [ ! -z "${ADHOC_TEMPLATE_README}" -a \
                -f "${ADHOC_TEMPLATE_README}" ] && \
            opt_readme="-r \"${ADHOC_TEMPLATE_README}\""
        opt_html=
        [ ! -z "${ADHOC_TEMPLATE_HTML}" -a \
                -f "${ADHOC_TEMPLATE_HTML}" ] && \
            opt_html="-r \"${ADHOC_TEMPLATE_HTML}\""
        cmd="open -n \"$HOME/Documents/MacOSX Projects/BetaBuilder TFO/build/Debug/BetaBuilder.app\" --args \
            -i \"${zip}\" \
            -o \"${ADHOC_PUBLISHING_DIRECTORY}\" \
            -u \"${ADHOC_PUBLISHING_URL}\" \
            ${opt_readme} ${opt_html}"

        if eval "${cmd}"; then
            if [ ! -z "${ADHOC_PUBLISHING_COMMAND}" -a \
                    -x "${ADHOC_PUBLISHING_COMMAND}" ]; then
                echo "Distributing Ad Hoc release to website..."
                "${ADHOC_PUBLISHING_COMMAND}"
            fi
        else
            echo "BetaBuilder encountered an error. Check system logs."
            exit 1
        fi
    fi
fi

exit 0

 
Powered by Hackadelic Sliding Notes 1.6.5

~/.adhoc-distribution.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# ~/.adhoc-distribution.conf
# Default values for our IPA packager and Ad Hoc distribution script
#
DEFAULT_BASEDIR="$HOME/Documents/iPhone Projects"

DEFAULT_ADHOC_PUBLISHING_URL="http://localhost/iosbeta/#SHORTBUNDLEID#-#BUILD#"
DEFAULT_ADHOC_PUBLISHING_DIRECTORY="$HOME/Documents/iosbeta/#SHORTBUNDLEID#-#BUILD#"
DEFAULT_ADHOC_PUBLISHING_COMMAND=$HOME/bin/sync-iosbeta

DEFAULT_ADHOC_DELIVERY_NAME="#APP#-#VERSION#-#BUILD#-#PLATFORM#-#CONFIG#.ipa"
DEFAULT_ADHOC_DELIVERY_DIRECTORY="$HOME/Documents/iOSArchives"

DEFAULT_ADHOC_CONFIG_ADHOC="Ad Hoc"
DEFAULT_ADHOC_CONFIG_RELEASE="Release"
DEFAULT_ADHOC_AUTHOR=$(id -P | cut -d: -f8)

DEFAULT_ADHOC_TEMPLATE_README=README.txt
DEFAULT_ADHOC_TEMPLATE_HTML=

 
Powered by Hackadelic Sliding Notes 1.6.5
I’m using localhost just as an example, and to avoid making the URL of my real beta site too public. You should set ADHOC_PUBLISHING_URL to use the hostname of your own website.Powered by Hackadelic Sliding Notes 1.6.5

Tags: automation, BetaBuilder, Hunter Hillegas, iOS, iPad, iPhone, shell script, Xcode
Posted in: Apple, iOS, Technology
Both comments and pings are currently closed.

One Comment

  1. [...] This post was mentioned on Twitter by Mugunth Kumar (), Cedric Chee. Cedric Chee said: iOS Ad-Hoc Beta publishing directly from Xcode.Automated with shell script target&BetaBuilder http://j.mp/erMGTl Similar to @testflightapp ? [...]