Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(164)

Side by Side Diff: chrome_mac/Google Chrome Packaging/dmgdiffer.sh

Issue 85333005: Update reference builds to Chrome 32.0.1700.19 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/deps/reference_builds/
Patch Set: Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:executable
+ *
OLDNEW
(Empty)
1 #!/bin/bash -p
2
3 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
6
7 # usage: dmgdiffer.sh product_name old_dmg new_dmg patch_dmg
8 #
9 # dmgdiffer creates a disk image containing a binary update able to patch
10 # a product originally distributed in old_dmg to the version in new_dmg. Much
11 # of this script is generic, but the make_patch_fs function is specific to
12 # a product: in this case, Google Chrome.
13 #
14 # This script operates by mounting old_dmg and new_dmg, creating a new
15 # filesystem structure containing dirpatches generated by dirdiffer and
16 # goobsdiff (which should be located in the same directory as this script),
17 # and producing a disk image from that structure.
18 #
19 # The Chrome make_patch_fs function produces an disk image that is able to
20 # update a single old version on any Keystone channel to a new version on a
21 # specific Keystone channel (the Keystone channel associated with new_dmg).
22 # Chrome's updates are split into two dirpatches: one updates the old
23 # versioned directory to the new one, and the other updates the remainder of
24 # the application. The versioned directory is split out from the rest because
25 # it contains the bulk of the application and its name changes from version to
26 # version, and dirdiffer/dirpatcher do not directly handle name changes. This
27 # approach also allows the versioned directory dirpatch to be applied in-place
28 # in most cases during an update, rather than relying on a temporary
29 # directory. In order to allow a single update dmg to apply to an old version
30 # on any Keystone channel, several small files are never distributed as diffs,
31 # and only as full (possibly compressed) versions of the new files. These
32 # files include the outer application's Info.plist which contains Keystone
33 # channel information, and anything created or modified by code-signing the
34 # outer application.
35 #
36 # Application of update disk images produced by this script is
37 # product-specific. With updates managed by Keystone, the update disk images
38 # can contain a .keystone_install script that is able to locate and update
39 # the installed product.
40 #
41 # Exit codes:
42 # 0 OK
43 # 1 Unknown failure
44 # 2 Incorrect number of parameters
45 # 3 Input disk images do not exist
46 # 4 Output disk image already exists
47 # 5 Parent of output directory does not exist or is not a directory
48 # 6 Could not mount old_dmg
49 # 7 Could not mount new_dmg
50 # 8 Could not create temporary patch filesystem directory
51 # 9 Could not create disk image
52 # 10 Could not read old application data
53 # 11 Could not read new application data
54 # 12 Old or new application sanity check failure
55 # 13 Could not write the patch
56 #
57 # Exit codes in the range 21-40 are mapped to codes 1-20 as returned by the
58 # first dirdiffer invocation. Codes 41-60 are mapped to codes 1-20 as returned
59 # by the second.
60
61 set -eu
62
63 # Environment sanitization. Set a known-safe PATH. Clear environment variables
64 # that might impact the interpreter's operation. The |bash -p| invocation
65 # on the #! line takes the bite out of BASH_ENV, ENV, and SHELLOPTS (among
66 # other features), but clearing them here ensures that they won't impact any
67 # shell scripts used as utility programs. SHELLOPTS is read-only and can't be
68 # unset, only unexported.
69 export PATH="/usr/bin:/bin:/usr/sbin:/sbin"
70 unset BASH_ENV CDPATH ENV GLOBIGNORE IFS POSIXLY_CORRECT
71 export -n SHELLOPTS
72
73 ME="$(basename "${0}")"
74 readonly ME
75 SCRIPT_DIR="$(dirname "${0}")"
76 readonly SCRIPT_DIR
77 readonly DIRDIFFER="${SCRIPT_DIR}/dirdiffer.sh"
78 readonly PKG_DMG="${SCRIPT_DIR}/pkg-dmg"
79
80 err() {
81 local error="${1}"
82
83 echo "${ME}: ${error}" >& 2
84 }
85
86 declare -a g_cleanup g_cleanup_mount_points
87 cleanup() {
88 local status=${?}
89
90 trap - EXIT
91 trap '' HUP INT QUIT TERM
92
93 if [[ ${status} -ge 128 ]]; then
94 err "Caught signal $((${status} - 128))"
95 fi
96
97 if [[ "${#g_cleanup_mount_points[@]}" -gt 0 ]]; then
98 local mount_point
99 for mount_point in "${g_cleanup_mount_points[@]}"; do
100 hdiutil detach "${mount_point}" -force >& /dev/null || true
101 done
102 fi
103
104 if [[ "${#g_cleanup[@]}" -gt 0 ]]; then
105 rm -rf "${g_cleanup[@]}"
106 fi
107
108 exit ${status}
109 }
110
111 mount_dmg() {
112 local dmg="${1}"
113 local mount_point="${2}"
114
115 if ! hdiutil attach "${1}" -mountpoint "${2}" \
116 -nobrowse -owners off > /dev/null; then
117 # set -e is in effect. return ${?} so that the caller can check the return
118 # code if desired, perhaps to print a more useful error message or to exit
119 # with a more precise status than would be possible here.
120 return ${?}
121 fi
122 }
123
124 # make_patch_fs is responsible for comparing the old and new disk images
125 # mounted at old_fs and new_fs, respectively, and populating patch_fs with the
126 # contents of what will become a disk image able to update old_fs to new_fs.
127 # It then outputs a string which will be used as the volume name of the
128 # patch_dmg.
129 #
130 # The entire patch contents are placed into a .patch directory to hide them
131 # from ordinary view. The disk image will be given a volume name like
132 # "Google Chrome 5.0.375.55-5.0.375.70" as an identifying aid, although
133 # uniqueness is not important and users will never interact directly with
134 # them.
135 make_patch_fs() {
136 local product_name="${1}"
137 local old_fs="${2}"
138 local new_fs="${3}"
139 local patch_fs="${4}"
140
141 readonly APP_NAME="${product_name}.app"
142 readonly APP_NAME_RE="${product_name}\\.app"
143 readonly APP_PLIST="Contents/Info"
144 readonly APP_VERSION_KEY="CFBundleShortVersionString"
145 readonly APP_BUNDLEID_KEY="CFBundleIdentifier"
146 readonly KS_VERSION_KEY="KSVersion"
147 readonly KS_PRODUCT_KEY="KSProductID"
148 readonly KS_CHANNEL_KEY="KSChannelID"
149 readonly VERSIONS_DIR="Contents/Versions"
150 readonly BUILD_RE="^[0-9]+\\.[0-9]+\\.([0-9]+)\\.[0-9]+\$"
151 readonly MIN_BUILD=434
152
153 local product_url="http://www.google.com/chrome/"
154 if [[ "${product_name}" = "Google Chrome Canary" ]]; then
155 product_url="http://tools.google.com/dlpage/chromesxs"
156 fi
157
158 local old_app_path="${old_fs}/${APP_NAME}"
159 local old_app_plist="${old_app_path}/${APP_PLIST}"
160 local old_app_version
161 if ! old_app_version="$(defaults read "${old_app_plist}" \
162 "${APP_VERSION_KEY}")"; then
163 err "could not read old app version"
164 exit 10
165 fi
166 if ! [[ "${old_app_version}" =~ ${BUILD_RE} ]]; then
167 err "old app version not of expected format"
168 exit 10
169 fi
170 local old_app_version_build="${BASH_REMATCH[1]}"
171
172 local old_app_bundleid
173 if ! old_app_bundleid="$(defaults read "${old_app_plist}" \
174 "${APP_BUNDLEID_KEY}")"; then
175 err "could not read old app bundle ID"
176 exit 10
177 fi
178
179 local old_ks_plist="${old_app_plist}"
180 local old_ks_version
181 if ! old_ks_version="$(defaults read "${old_ks_plist}" \
182 "${KS_VERSION_KEY}")"; then
183 err "could not read old Keystone version"
184 exit 10
185 fi
186
187 local new_app_path="${new_fs}/${APP_NAME}"
188 local new_app_plist="${new_app_path}/${APP_PLIST}"
189 local new_app_version
190 if ! new_app_version="$(defaults read "${new_app_plist}" \
191 "${APP_VERSION_KEY}")"; then
192 err "could not read new app version"
193 exit 11
194 fi
195 if ! [[ "${new_app_version}" =~ ${BUILD_RE} ]]; then
196 err "new app version not of expected format"
197 exit 11
198 fi
199 local new_app_version_build="${BASH_REMATCH[1]}"
200
201 local new_ks_plist="${new_app_plist}"
202 local new_ks_version
203 if ! new_ks_version="$(defaults read "${new_ks_plist}" \
204 "${KS_VERSION_KEY}")"; then
205 err "could not read new Keystone version"
206 exit 11
207 fi
208
209 local new_ks_product
210 if ! new_ks_product="$(defaults read "${new_app_plist}" \
211 "${KS_PRODUCT_KEY}")"; then
212 err "could not read new Keystone product ID"
213 exit 11
214 fi
215
216 if [[ ${old_app_version_build} -lt ${MIN_BUILD} ]] ||
217 [[ ${new_app_version_build} -lt ${MIN_BUILD} ]]; then
218 err "old and new versions must be build ${MIN_BUILD} or newer"
219 exit 12
220 fi
221
222 local new_ks_channel
223 new_ks_channel="$(defaults read "${new_app_plist}" \
224 "${KS_CHANNEL_KEY}" 2> /dev/null || true)"
225
226 local name_extra
227 if [[ "${new_ks_channel}" = "beta" ]]; then
228 name_extra=" Beta"
229 elif [[ "${new_ks_channel}" = "dev" ]]; then
230 name_extra=" Dev"
231 elif [[ "${new_ks_channel}" = "canary" ]]; then
232 name_extra=
233 elif [[ -n "${new_ks_channel}" ]]; then
234 name_extra=" ${new_ks_channel}"
235 fi
236
237 local old_versioned_dir="${old_app_path}/${VERSIONS_DIR}/${old_app_version}"
238 local new_versioned_dir="${new_app_path}/${VERSIONS_DIR}/${new_app_version}"
239
240 if ! cp -p "${SCRIPT_DIR}/keystone_install.sh" \
241 "${patch_fs}/.keystone_install"; then
242 err "could not copy .keystone_install"
243 exit 13
244 fi
245
246 local patch_keychain_reauthorize_dir="${patch_fs}/.keychain_reauthorize"
247 if ! mkdir "${patch_keychain_reauthorize_dir}"; then
248 err "could not mkdir patch_keychain_reauthorize_dir"
249 exit 13
250 fi
251
252 if ! cp -p "${SCRIPT_DIR}/.keychain_reauthorize/${old_app_bundleid}" \
253 "${patch_keychain_reauthorize_dir}/${old_app_bundleid}"; then
254 err "could not copy keychain_reauthorize"
255 exit 13
256 fi
257
258 local patch_dotpatch_dir="${patch_fs}/.patch"
259 if ! mkdir "${patch_dotpatch_dir}"; then
260 err "could not mkdir patch_dotpatch_dir"
261 exit 13
262 fi
263
264 if ! cp -p "${SCRIPT_DIR}/dirpatcher.sh" \
265 "${SCRIPT_DIR}/goobspatch" \
266 "${SCRIPT_DIR}/liblzma_decompress.dylib" \
267 "${SCRIPT_DIR}/xzdec" \
268 "${patch_dotpatch_dir}/"; then
269 err "could not copy patching tools"
270 exit 13
271 fi
272
273 if ! echo "${new_ks_product}" > "${patch_dotpatch_dir}/ks_product" ||
274 ! echo "${old_app_version}" > "${patch_dotpatch_dir}/old_app_version" ||
275 ! echo "${new_app_version}" > "${patch_dotpatch_dir}/new_app_version" ||
276 ! echo "${old_ks_version}" > "${patch_dotpatch_dir}/old_ks_version" ||
277 ! echo "${new_ks_version}" > "${patch_dotpatch_dir}/new_ks_version"; then
278 err "could not write patch product or version information"
279 exit 13
280 fi
281 local patch_ks_channel_file="${patch_dotpatch_dir}/ks_channel"
282 if [[ -n "${new_ks_channel}" ]]; then
283 if ! echo "${new_ks_channel}" > "${patch_ks_channel_file}"; then
284 err "could not write Keystone channel information"
285 exit 13
286 fi
287 else
288 if ! touch "${patch_ks_channel_file}"; then
289 err "could not write empty Keystone channel information"
290 exit 13
291 fi
292 fi
293
294 # The only visible contents of the disk image will be a README file that
295 # explains the image's purpose.
296 local new_app_version_extra="${new_app_version}${name_extra}"
297 cat > "${patch_fs}/README.txt" << __EOF__ || \
298 (err "could not write README.txt" && exit 13)
299 This disk image contains a differential updater that can update
300 ${product_name} from version ${old_app_version} to ${new_app_version_extra}.
301
302 This image is part of the auto-update system and is not independently
303 useful.
304
305 To install ${product_name}, please visit
306 <${product_url}>.
307 __EOF__
308
309 local patch_versioned_dir="\
310 ${patch_dotpatch_dir}/version_${old_app_version}_${new_app_version}.dirpatch"
311
312 if ! "${DIRDIFFER}" "${old_versioned_dir}" \
313 "${new_versioned_dir}" \
314 "${patch_versioned_dir}"; then
315 local status=${?}
316 err "could not create a dirpatch for the versioned directory"
317 exit $((${status} + 20))
318 fi
319
320 # Set DIRDIFFER_EXCLUDE to exclude the contents of the Versions directory,
321 # but to include an empty Versions directory. The versioned directory was
322 # already addressed in the preceding dirpatch.
323 export DIRDIFFER_EXCLUDE="/${APP_NAME_RE}/Contents/Versions/"
324
325 # Set DIRDIFFER_NO_DIFF to exclude files introduced by or modified by
326 # Keystone channel and brand tagging and subsequent code signing.
327 export DIRDIFFER_NO_DIFF="\
328 /${APP_NAME_RE}/Contents/\
329 (CodeResources|Info\\.plist|MacOS/${product_name}|_CodeSignature/.*)$"
330
331 local patch_app_dir="${patch_dotpatch_dir}/application.dirpatch"
332
333 if ! "${DIRDIFFER}" "${old_app_path}" \
334 "${new_app_path}" \
335 "${patch_app_dir}"; then
336 local status=${?}
337 err "could not create a dirpatch for the application directory"
338 exit $((${status} + 40))
339 fi
340
341 unset DIRDIFFER_EXCLUDE DIRDIFFER_NO_DIFF
342
343 echo "${product_name} ${old_app_version}-${new_app_version_extra} Update"
344 }
345
346 # package_patch_dmg creates a disk image at patch_dmg with the contents of
347 # patch_fs. The disk image's volume name is taken from volume_name. temp_dir
348 # is a work directory such as /tmp for the packager's use.
349 package_patch_dmg() {
350 local patch_fs="${1}"
351 local patch_dmg="${2}"
352 local volume_name="${3}"
353 local temp_dir="${4}"
354
355 # Because most of the contents of ${patch_fs} are already compressed, the
356 # overall compression on the disk image is mostly used to minimize the sizes
357 # of the filesystem structures. In the presence of so much
358 # already-compressed data, zlib performs better than bzip2, so use UDZO.
359 if ! "${PKG_DMG}" \
360 --verbosity 0 \
361 --source "${patch_fs}" \
362 --target "${patch_dmg}" \
363 --tempdir "${temp_dir}" \
364 --format UDZO \
365 --volname "${volume_name}" \
366 --config "openfolder_bless=0"; then
367 err "disk image creation failed"
368 exit 9
369 fi
370 }
371
372 # make_patch_dmg mounts old_dmg and new_dmg, invokes make_patch_fs to prepare
373 # a patch filesystem, and then hands the patch filesystem to package_patch_dmg
374 # to create patch_dmg.
375 make_patch_dmg() {
376 local product_name="${1}"
377 local old_dmg="${2}"
378 local new_dmg="${3}"
379 local patch_dmg="${4}"
380
381 local temp_dir
382 temp_dir="$(mktemp -d -t "${ME}")"
383 g_cleanup+=("${temp_dir}")
384
385 local old_mount_point="${temp_dir}/old"
386 g_cleanup_mount_points+=("${old_mount_point}")
387 if ! mount_dmg "${old_dmg}" "${old_mount_point}"; then
388 err "could not mount old_dmg ${old_dmg}"
389 exit 6
390 fi
391
392 local new_mount_point="${temp_dir}/new"
393 g_cleanup_mount_points+=("${new_mount_point}")
394 if ! mount_dmg "${new_dmg}" "${new_mount_point}"; then
395 err "could not mount new_dmg ${new_dmg}"
396 exit 7
397 fi
398
399 local patch_fs="${temp_dir}/patch"
400 if ! mkdir "${patch_fs}"; then
401 err "could not mkdir patch_fs ${patch_fs}"
402 exit 8
403 fi
404
405 local volume_name
406 volume_name="$(make_patch_fs "${product_name}" \
407 "${old_mount_point}" \
408 "${new_mount_point}" \
409 "${patch_fs}")"
410
411 hdiutil detach "${new_mount_point}" > /dev/null
412 unset g_cleanup_mount_points[${#g_cleanup_mount_points[@]}]
413
414 hdiutil detach "${old_mount_point}" > /dev/null
415 unset g_cleanup_mount_points[${#g_cleanup_mount_points[@]}]
416
417 package_patch_dmg "${patch_fs}" "${patch_dmg}" "${volume_name}" "${temp_dir}"
418
419 rm -rf "${temp_dir}"
420 unset g_cleanup[${#g_cleanup[@]}]
421 }
422
423 # shell_safe_path ensures that |path| is safe to pass to tools as a
424 # command-line argument. If the first character in |path| is "-", "./" is
425 # prepended to it. The possibly-modified |path| is output.
426 shell_safe_path() {
427 local path="${1}"
428 if [[ "${path:0:1}" = "-" ]]; then
429 echo "./${path}"
430 else
431 echo "${path}"
432 fi
433 }
434
435 usage() {
436 echo "usage: ${ME} product_name old_dmg new_dmg patch_dmg" >& 2
437 }
438
439 main() {
440 local product_name old_dmg new_dmg patch_dmg
441 product_name="${1}"
442 old_dmg="$(shell_safe_path "${2}")"
443 new_dmg="$(shell_safe_path "${3}")"
444 patch_dmg="$(shell_safe_path "${4}")"
445
446 trap cleanup EXIT HUP INT QUIT TERM
447
448 if ! [[ -f "${old_dmg}" ]] || ! [[ -f "${new_dmg}" ]]; then
449 err "old_dmg and new_dmg must exist and be files"
450 usage
451 exit 3
452 fi
453
454 if [[ -e "${patch_dmg}" ]]; then
455 err "patch_dmg must not exist"
456 usage
457 exit 4
458 fi
459
460 local patch_dmg_parent
461 patch_dmg_parent="$(dirname "${patch_dmg}")"
462 if ! [[ -d "${patch_dmg_parent}" ]]; then
463 err "patch_dmg parent directory must exist and be a directory"
464 usage
465 exit 5
466 fi
467
468 make_patch_dmg "${product_name}" "${old_dmg}" "${new_dmg}" "${patch_dmg}"
469
470 trap - EXIT
471 }
472
473 if [[ ${#} -ne 4 ]]; then
474 usage
475 exit 2
476 fi
477
478 main "${@}"
479 exit ${?}
OLDNEW
« no previous file with comments | « chrome_mac/Google Chrome Packaging/dirpatcher.sh ('k') | chrome_mac/Google Chrome Packaging/document_canary.icns » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698