OLD | NEW |
(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 # This is expected to be invoked by the Pulse build step. It takes care of |
| 8 # doing the code signing and building the needed disk images. All needed |
| 9 # support files are assumed to live next to this script on disk. |
| 10 # |
| 11 # usage: generate_dmgs output_dir input_dir codesign_keychain codesign_id \ |
| 12 # omahaproxy_url [live_dmg_dir] |
| 13 # |
| 14 # Disk images are placed in output_dir. input_dir is the directory containing |
| 15 # unzipped packaging data. omahaproxy_url will be consulted to determine the |
| 16 # current live versions and URLs where they can be downloaded. live_dmg_dir |
| 17 # is no longer used but is accepted on the command line to allow the script |
| 18 # that calls this one to continue passing that argument to older versions of |
| 19 # this script. |
| 20 |
| 21 set -eu |
| 22 |
| 23 # Environment sanitization. Set a known-safe PATH. Clear environment variables |
| 24 # that might impact the interpreter's operation. The |bash -p| invocation |
| 25 # on the #! line takes the bite out of BASH_ENV, ENV, and SHELLOPTS (among |
| 26 # other features), but clearing them here ensures that they won't impact any |
| 27 # shell scripts used as utility programs. SHELLOPTS is read-only and can't be |
| 28 # unset, only unexported. |
| 29 export PATH="/usr/bin:/bin:/usr/sbin:/sbin" |
| 30 unset BASH_ENV CDPATH ENV GLOBIGNORE IFS POSIXLY_CORRECT |
| 31 export -n SHELLOPTS |
| 32 |
| 33 ME="$(basename "${0}")" |
| 34 readonly ME |
| 35 PACKAGING_DIR="$(dirname "${0}")" |
| 36 readonly PACKAGING_DIR |
| 37 |
| 38 readonly PRODUCT_NAME="Google Chrome" |
| 39 readonly APP_NAME="${PRODUCT_NAME}.app" |
| 40 DMG_PRODUCT_NAME="$(sed -e "s/ //" <<< "${PRODUCT_NAME}")" |
| 41 readonly DMG_PRODUCT_NAME |
| 42 |
| 43 readonly APP_ID_KEY="CFBundleIdentifier" |
| 44 readonly APP_VERSION_KEY="CFBundleShortVersionString" |
| 45 readonly KS_VERSION_KEY="KSVersion" |
| 46 readonly KS_CHANNEL_KEY="KSChannelID" |
| 47 readonly KS_BRAND_KEY="KSBrandID" |
| 48 readonly KS_ID_KEY="KSProductID" |
| 49 readonly BP_VERSION_KEY="BreakpadVersion" |
| 50 |
| 51 readonly RSYNC_FLAGS=("-Clprt" "--include" "*.so" "--copy-unsafe-links") |
| 52 readonly CURL_FLAGS=("--fail" "--location" "--silent" "--show-error") |
| 53 |
| 54 readonly OMAHAPROXY_PLATFORM="mac" |
| 55 readonly MIN_DIFF_VERSION="6.0.434.0" |
| 56 |
| 57 # CH, BR, and NF are parallel arrays. An element at the same index in any of |
| 58 # these arrays corresponds to the element at the same index in the others. |
| 59 # CH is used for Keystone channel codes; BR is used for Keystone brand codes; |
| 60 # NF is used for name fragments appended to the disk image name. |
| 61 declare -a CH BR NF |
| 62 |
| 63 # Stable channel, generic, with no brand code. |
| 64 CH+=(""); BR+=(""); NF+=("") |
| 65 |
| 66 # Stable channel with brand codes. |
| 67 # Pure organic installs. |
| 68 CH+=(""); BR+=("GGRO"); NF+=("Stable-PureOrganic") |
| 69 |
| 70 # google.xx marketing channels (house ads, toast promo, etc.) |
| 71 CH+=(""); BR+=("GGRM"); NF+=("Stable-GoogleMarketing") |
| 72 |
| 73 # Off-network marketing installs (not originating from google.xx marketing.) |
| 74 CH+=(""); BR+=("CHFA"); NF+=("Stable-OffNetworkMarketing") |
| 75 |
| 76 # Distribution deals. |
| 77 CH+=(""); BR+=("MACD"); NF+=("Stable-DistributionDeals") |
| 78 |
| 79 # Beta, Dev, and Canary channels, no brand codes. |
| 80 CH+=("beta"); BR+=(""); NF+=("Beta") |
| 81 CH+=("dev"); BR+=(""); NF+=("Dev") |
| 82 CH+=("canary"); BR+=(""); NF+=("Canary") |
| 83 |
| 84 readonly CH BR NF |
| 85 |
| 86 g_temp_dir= |
| 87 |
| 88 err() { |
| 89 local error="${1}" |
| 90 |
| 91 echo "${ME}: ${error}" >& 2 |
| 92 } |
| 93 |
| 94 declare -a g_cleanup |
| 95 cleanup() { |
| 96 local status=${?} |
| 97 |
| 98 trap - EXIT |
| 99 trap '' HUP INT QUIT TERM |
| 100 |
| 101 if [[ ${status} -ge 128 ]]; then |
| 102 err "Caught signal $((${status} - 128))" |
| 103 fi |
| 104 |
| 105 if [[ "${#g_cleanup[@]}" -gt 0 ]]; then |
| 106 rm -rf "${g_cleanup[@]}" |
| 107 fi |
| 108 |
| 109 exit ${status} |
| 110 } |
| 111 |
| 112 # Returns 0 if ${version1} and ${version2} are equal. Returns 1 if ${version1} |
| 113 # is less than ${version2}. Returns 2 if ${version1} is greater than |
| 114 # ${version2}. Returns 3 if ${version1} or ${version2} don't meet the expected |
| 115 # format. A piece-wise comparison is performed. |
| 116 readonly VERSION_RE="^([0-9]+)\\.([0-9]+)\\.([0-9]+)\\.([0-9]+)\$" |
| 117 version_compare() { |
| 118 local version1="${1}" |
| 119 local version2="${2}" |
| 120 |
| 121 if ! [[ "${version1}" =~ ${VERSION_RE} ]]; then |
| 122 return 3 |
| 123 fi |
| 124 |
| 125 local version1_components=("${BASH_REMATCH[1]}" |
| 126 "${BASH_REMATCH[2]}" |
| 127 "${BASH_REMATCH[3]}" |
| 128 "${BASH_REMATCH[4]}") |
| 129 |
| 130 if ! [[ "${version2}" =~ ${VERSION_RE} ]]; then |
| 131 return 3 |
| 132 fi |
| 133 |
| 134 local version2_components=("${BASH_REMATCH[1]}" |
| 135 "${BASH_REMATCH[2]}" |
| 136 "${BASH_REMATCH[3]}" |
| 137 "${BASH_REMATCH[4]}") |
| 138 |
| 139 local i |
| 140 for i in "${!version1_components[@]}"; do |
| 141 local version1_component="${version1_components[${i}]}" |
| 142 local version2_component="${version2_components[${i}]}" |
| 143 |
| 144 if [[ ${version1_component} -lt ${version2_component} ]]; then |
| 145 # version1 is less than version2. |
| 146 return 1 |
| 147 fi |
| 148 if [[ ${version1_component} -gt ${version2_component} ]]; then |
| 149 # version1 is greater than version2. |
| 150 return 2 |
| 151 fi |
| 152 done |
| 153 |
| 154 # The version numbers are equal. |
| 155 return 0 |
| 156 } |
| 157 |
| 158 is_version_gt() { |
| 159 local version1="${1}" |
| 160 local version2="${2}" |
| 161 |
| 162 if version_compare "${version1}" "${version2}"; then |
| 163 # They're equal. |
| 164 return 1 |
| 165 elif [[ ${?} -eq 2 ]]; then |
| 166 return 0 |
| 167 fi |
| 168 |
| 169 return 1 |
| 170 } |
| 171 |
| 172 is_version_ge() { |
| 173 local version1="${1}" |
| 174 local version2="${2}" |
| 175 |
| 176 if version_compare "${version1}" "${version2}"; then |
| 177 # They're equal. |
| 178 return 0 |
| 179 elif [[ ${?} -eq 2 ]]; then |
| 180 return 0 |
| 181 fi |
| 182 |
| 183 return 1 |
| 184 } |
| 185 |
| 186 build_dmg() { |
| 187 if [[ ${#} -ne 8 ]]; then |
| 188 err "build_dmg: wrong number of arguments" |
| 189 exit 1 |
| 190 fi |
| 191 |
| 192 local output_dir="${1}" |
| 193 local half_signed_app_path="${2}" |
| 194 local codesign_keychain="${3}" |
| 195 local codesign_id="${4}" |
| 196 local app_version="${5}" |
| 197 local channel_id="${6}" |
| 198 local brand_id="${7}" |
| 199 local name_fragment="${8}" |
| 200 |
| 201 # If the name fragment isn't empty, add a dash in front of it. |
| 202 local dash_fragment="${name_fragment}" |
| 203 if [[ -n "${dash_fragment}" ]]; then |
| 204 dash_fragment="-${dash_fragment}" |
| 205 fi |
| 206 |
| 207 local dmg_product_name="${DMG_PRODUCT_NAME}" |
| 208 local product_name="${PRODUCT_NAME}" |
| 209 |
| 210 # Canary: The disk image should use GoogleChromeCanary as the product name |
| 211 # instead of putting the fragment after the version number. |
| 212 if [[ "${channel_id}" = "canary" ]]; then |
| 213 product_name="${product_name} ${name_fragment}" |
| 214 dmg_product_name="${dmg_product_name}${name_fragment}" |
| 215 dash_fragment="" |
| 216 fi |
| 217 |
| 218 local dmg_name="${dmg_product_name}-${app_version}${dash_fragment}.dmg" |
| 219 |
| 220 # Even with --verbosity 0, and even when it succeeds, hdiutil (via pkg-dmg) |
| 221 # prints a newline to stderr. That looks pretty weird. Make it evident that |
| 222 # things are progressing by printing a message for each disk image. |
| 223 err "creating ${dmg_name}" |
| 224 |
| 225 local variant_dir="${g_temp_dir}/variant" |
| 226 mkdir "${variant_dir}" |
| 227 local variant_app_path="${variant_dir}/${APP_NAME}" |
| 228 |
| 229 # Copy the half-signed application to a variant-specific directory. |
| 230 rsync "${RSYNC_FLAGS[@]}" "${half_signed_app_path}/" "${variant_app_path}" |
| 231 |
| 232 local app_plist="${variant_app_path}/Contents/Info" |
| 233 local ks_plist="${app_plist}" |
| 234 |
| 235 # Apply the channel and brand code changes. |
| 236 if [[ -z "${channel_id}" ]]; then |
| 237 defaults delete "${ks_plist}" "${KS_CHANNEL_KEY}" 2> /dev/null || true |
| 238 else |
| 239 defaults write "${ks_plist}" "${KS_CHANNEL_KEY}" -string "${channel_id}" |
| 240 fi |
| 241 |
| 242 if [[ -z "${brand_id}" ]]; then |
| 243 defaults delete "${ks_plist}" "${KS_BRAND_KEY}" 2> /dev/null || true |
| 244 else |
| 245 defaults write "${ks_plist}" "${KS_BRAND_KEY}" -string "${brand_id}" |
| 246 fi |
| 247 |
| 248 # Canary: the outer application bundle needs a distinct bundle ID for the |
| 249 # system's bundle ID to disambiguate from a simultaneously-installed |
| 250 # non-canary, and for Keystone to support side-by-side auto-update. It must |
| 251 # keep the original Breakpad ID as this ID has been white-listed by the |
| 252 # crash server. The CrProductDirName key, normally absent, is set specially |
| 253 # in the canary build to force the default profile to an alternate |
| 254 # directory. The CFBundleSignature key and PkgInfo file are set to contain |
| 255 # the canary's distinct signature. The executable is renamed, and |
| 256 # CFBundleExecutable is changed to point to it. |
| 257 local app_bundle_id_old="$(defaults read "${app_plist}" "${APP_ID_KEY}")" |
| 258 local app_bundle_id_new="${app_bundle_id_old}" |
| 259 if [[ "${channel_id}" = "canary" ]]; then |
| 260 app_bundle_id_new="${app_bundle_id_old}.canary" |
| 261 defaults write "${app_plist}" "${APP_ID_KEY}" "${app_bundle_id_new}" |
| 262 local ks_bundle_id="$(defaults read "${ks_plist}" "${KS_ID_KEY}")" |
| 263 defaults write "${ks_plist}" "${KS_ID_KEY}" "${ks_bundle_id}.canary" |
| 264 defaults write "${app_plist}" CrProductDirName "Google/Chrome Canary" |
| 265 readonly CANARY_SIGNATURE="Pipi" |
| 266 defaults write "${app_plist}" CFBundleSignature "${CANARY_SIGNATURE}" |
| 267 echo -n "APPL${CANARY_SIGNATURE}" > "${variant_app_path}/Contents/PkgInfo" |
| 268 |
| 269 mv "${variant_app_path}/Contents/MacOS/Google Chrome" \ |
| 270 "${variant_app_path}/Contents/MacOS/Google Chrome Canary" |
| 271 defaults write "${app_plist}" CFBundleExecutable "Google Chrome Canary" |
| 272 fi |
| 273 |
| 274 # Info.plist will work perfectly well in any plist format, but traditionally |
| 275 # applications use xml1 for this, so convert it back after whatever defaults |
| 276 # might have done. |
| 277 plutil -convert xml1 "${app_plist}.plist" |
| 278 if [[ "${app_plist}" != "${ks_plist}" ]]; then |
| 279 plutil -convert xml1 "${ks_plist}.plist" |
| 280 fi |
| 281 |
| 282 # Canary: use different icons for the application and its documents. Adjust |
| 283 # the bundle ID used for the managed preferences manifest. |
| 284 if [[ "${channel}" = "canary" ]]; then |
| 285 rsync "${RSYNC_FLAGS[@]}" "${PACKAGING_DIR}/app_canary.icns" \ |
| 286 "${variant_app_path}/Contents/Resources/app.icns" |
| 287 rsync "${RSYNC_FLAGS[@]}" "${PACKAGING_DIR}/document_canary.icns" \ |
| 288 "${variant_app_path}/Contents/Resources/document.icns" |
| 289 |
| 290 local manifest_dir_new="\ |
| 291 ${variant_app_path}/Contents/Resources/${app_bundle_id_new}.manifest" |
| 292 mv "${variant_app_path}/Contents/Resources/${app_bundle_id_old}.manifest" \ |
| 293 "${manifest_dir_new}" |
| 294 local manifest_plist_tmp="${variant_dir}/manifest" |
| 295 mv "${manifest_dir_new}/Contents/Resources/${app_bundle_id_old}.manifest" \ |
| 296 "${manifest_plist_tmp}.plist" |
| 297 defaults write "${manifest_plist_tmp}" pfm_domain "${app_bundle_id_new}" |
| 298 plutil -convert xml1 "${manifest_plist_tmp}.plist" |
| 299 mv "${manifest_plist_tmp}.plist" \ |
| 300 "${manifest_dir_new}/Contents/Resources/${app_bundle_id_new}.manifest" |
| 301 fi |
| 302 |
| 303 # Sign the application - it's now fully signed. |
| 304 "${PACKAGING_DIR}/sign_app.sh" "${variant_app_path}" \ |
| 305 "${codesign_keychain}" "${codesign_id}" |
| 306 |
| 307 local app_name="${APP_NAME}" |
| 308 |
| 309 # Canary: the application's name in the disk image should be changed from |
| 310 # Google Chrome.app to Google Chrome Canary.app. The canary also uses a |
| 311 # different .DS_Store file and has a different volume icon. |
| 312 local dsstore_name="chrome_dmg_dsstore" |
| 313 local icon_name="chrome_dmg_icon.icns" |
| 314 if [[ "${channel_id}" = "canary" ]]; then |
| 315 app_name="$(sed -e "s/\\.app\$/ ${name_fragment}.app/" <<< "${app_name}")" |
| 316 dsstore_name="chrome_canary_dmg_dsstore" |
| 317 icon_name="chrome_canary_dmg_icon.icns" |
| 318 fi |
| 319 |
| 320 # A locally-created empty directory is more trustworthy than /var/empty. |
| 321 local empty_dir="${g_temp_dir}/empty" |
| 322 mkdir "${empty_dir}" |
| 323 |
| 324 local dmg_path="${output_dir}/${dmg_name}" |
| 325 |
| 326 # Make the disk image. Don't include ${name_fragment}, ${dash_fragment}, |
| 327 # or anything else in --volname because the .DS_Store expects the volume |
| 328 # name to be constant. Don't put a name on the /Applications symbolic link |
| 329 # because the same disk image is used for all languages. |
| 330 "${PACKAGING_DIR}/pkg-dmg" \ |
| 331 --verbosity 0 \ |
| 332 --tempdir "${g_temp_dir}" \ |
| 333 --source "${empty_dir}" \ |
| 334 --target "${dmg_path}" \ |
| 335 --format UDBZ \ |
| 336 --volname "${product_name}" \ |
| 337 --icon "${PACKAGING_DIR}/${icon_name}" \ |
| 338 --copy "${variant_app_path}/:/${app_name}" \ |
| 339 --copy "${PACKAGING_DIR}/keystone_install.sh:/.keystone_install" \ |
| 340 --mkdir "/.background" \ |
| 341 --copy \ |
| 342 "${PACKAGING_DIR}/chrome_dmg_background.png:/.background/background.png" \ |
| 343 --copy "${PACKAGING_DIR}/${dsstore_name}:/.DS_Store" \ |
| 344 --symlink "/Applications:/ " \ |
| 345 --mkdir "/.keychain_reauthorize" \ |
| 346 --copy \ |
| 347 "${PACKAGING_DIR}/.keychain_reauthorize/${app_bundle_id_new}:/.keychain_reauthor
ize/${app_bundle_id_new}" |
| 348 |
| 349 rmdir "${empty_dir}" |
| 350 rm -rf "${variant_dir}" |
| 351 } |
| 352 |
| 353 # shell_safe_path ensures that |path| is safe to pass to tools as a |
| 354 # command-line argument. If the first character in |path| is "-", "./" is |
| 355 # prepended to it. The possibily-modified |path| is output. |
| 356 shell_safe_path() { |
| 357 local path="${1}" |
| 358 if [[ "${path:0:1}" = "-" ]]; then |
| 359 echo "./${path}" |
| 360 else |
| 361 echo "${path}" |
| 362 fi |
| 363 } |
| 364 |
| 365 usage() { |
| 366 echo "usage: ${ME}: output_dir input_dir codesign_keychain codesign_id \ |
| 367 omahaproxy_url" >& 2 |
| 368 } |
| 369 |
| 370 main() { |
| 371 local output_dir input_dir codesign_keychain codesign_id omahaproxy_url |
| 372 output_dir="$(shell_safe_path "${1}")" |
| 373 input_dir="$(shell_safe_path "${2}")" |
| 374 codesign_keychain="$(shell_safe_path "${3}")" |
| 375 codesign_id="${4}" |
| 376 omahaproxy_url="${5}" |
| 377 |
| 378 trap cleanup EXIT HUP INT QUIT TERM |
| 379 |
| 380 err "output_dir=${output_dir}" |
| 381 err "input_dir=${input_dir}" |
| 382 err "codesign_keychain=${codesign_keychain}" |
| 383 err "codesign_id=${codesign_id}" |
| 384 err "omahaproxy_url=${omahaproxy_url}" |
| 385 |
| 386 if [[ -z "${input_dir}" ]] || |
| 387 [[ "${input_dir:0:1}" != "/" ]] || |
| 388 [[ ! -d "${input_dir}" ]]; then |
| 389 # input_dir needs to be an absolute path because that's what |defaults| |
| 390 # requires. |
| 391 err "input_dir must exist and be an absolute path to a directory" |
| 392 usage |
| 393 exit 1 |
| 394 fi |
| 395 if [[ ! -f "${codesign_keychain}" ]]; then |
| 396 err "codesign_keychain must exist and be a file" |
| 397 usage |
| 398 exit 1 |
| 399 fi |
| 400 |
| 401 local source_app_path="${input_dir}/${APP_NAME}" |
| 402 |
| 403 if [[ ! -d "${source_app_path}" ]]; then |
| 404 err "${source_app_path} not found" |
| 405 exit 1 |
| 406 fi |
| 407 |
| 408 # Make sure ${output_dir} doesn't already exist, or if it does, make sure |
| 409 # it's empty. |
| 410 if [[ ! -d "${output_dir}" ]]; then |
| 411 mkdir "${output_dir}" |
| 412 fi |
| 413 shopt -s nullglob dotglob |
| 414 local output_dir_contents=("${output_dir}"/*) |
| 415 shopt -u nullglob dotglob |
| 416 if [[ ${#output_dir_contents[@]} -ne 0 ]]; then |
| 417 err "output_dir must not exist or be empty" |
| 418 exit 1 |
| 419 fi |
| 420 |
| 421 # Application sanity checks. Make sure that it smells right and that the |
| 422 # version numbers in the various plists match. |
| 423 local app_plist="${source_app_path}/Contents/Info" |
| 424 local app_version |
| 425 app_version="$(defaults read "${app_plist}" "${APP_VERSION_KEY}")" |
| 426 local versioned_dir="${source_app_path}/Contents/Versions/${app_version}" |
| 427 local framework_path="${versioned_dir}/${PRODUCT_NAME} Framework.framework" |
| 428 local framework_plist="${framework_path}/Resources/Info" |
| 429 |
| 430 local ks_plist="${app_plist}" |
| 431 local ks_version |
| 432 if ! ks_version="$(defaults read "${ks_plist}" "${KS_VERSION_KEY}")"; then |
| 433 err "Keystone version not present" |
| 434 exit 1 |
| 435 fi |
| 436 if [[ "${ks_version}" != "${app_version}" ]]; then |
| 437 err "Keystone version does not match application version" |
| 438 exit 1 |
| 439 fi |
| 440 |
| 441 local bp_plist="${framework_plist}" |
| 442 local bp_version |
| 443 if ! bp_version="$(defaults read "${bp_plist}" "${BP_VERSION_KEY}")"; then |
| 444 err "Breakpad version not present" |
| 445 exit 1 |
| 446 fi |
| 447 if [[ "${bp_version}" != "${app_version}" ]]; then |
| 448 err "Breakpad version does not match application version" |
| 449 exit 1 |
| 450 fi |
| 451 |
| 452 # Consult omahaproxy to determine the current live versions on each channel. |
| 453 # Get omahaproxy output first in a separate variable, allowing a failure |
| 454 # to be caught directly. |
| 455 # |
| 456 # omahaproxy runs on appengine, which makes it the least reliable part of |
| 457 # the process. Loop around omahaproxy to retry in the event of a failure. |
| 458 # Do this early, so that the script can fail before performing major work |
| 459 # in the event that omahaproxy output can't be collected. |
| 460 local omahaproxy_full_url="\ |
| 461 ${omahaproxy_url}/dl_urls?os=${OMAHAPROXY_PLATFORM}" |
| 462 err "consulting ${omahaproxy_full_url}" |
| 463 local omahaproxy_output |
| 464 local i |
| 465 for i in {1..5}; do |
| 466 if ! omahaproxy_output="$(curl "${CURL_FLAGS[@]}" --max-time 15 \ |
| 467 "${omahaproxy_full_url}")" || |
| 468 [[ -z "${omahaproxy_output}" ]]; then |
| 469 if [[ ${i} -lt 5 ]]; then |
| 470 err "warning: could not get omahaproxy output, will retry" |
| 471 sleep 15 |
| 472 else |
| 473 err "could not get omahaproxy output" |
| 474 exit 1 |
| 475 fi |
| 476 else |
| 477 break |
| 478 fi |
| 479 done |
| 480 |
| 481 # Loop through the live versions reported by omahaproxy. |
| 482 local platform channel version url |
| 483 local live_versions=() |
| 484 local live_urls=() |
| 485 local line_number=0 |
| 486 while IFS="," read -r platform channel version url; do |
| 487 line_number=$((${line_number} + 1)) |
| 488 if [[ ${line_number} -eq 1 ]]; then |
| 489 # Skip the first line, it's a header. |
| 490 continue |
| 491 fi |
| 492 |
| 493 if [[ "${platform}" != "${OMAHAPROXY_PLATFORM}" ]]; then |
| 494 err "omahaproxy returned data for platform ${platform}" |
| 495 exit 1 |
| 496 fi |
| 497 |
| 498 # In CH and as used with Keystone, the stable channel is an empty/unset |
| 499 # string. omahaproxy reports it as "stable". Change "stable" to "" for |
| 500 # comparison with the elements of the CH array. |
| 501 if [[ "${channel}" = "stable" ]]; then |
| 502 channel="" |
| 503 fi |
| 504 |
| 505 # Loop through the known channels, and save the live version for each |
| 506 # known channel. |
| 507 for i in "${!CH[@]}"; do |
| 508 local channel_i="${CH[${i}]}" |
| 509 |
| 510 # Skip anything with a brand code. Every channel with a brand code |
| 511 # should also appear once without a brand code. |
| 512 if [[ -n "${BR[${i}]}" ]]; then |
| 513 continue |
| 514 fi |
| 515 |
| 516 if [[ "${channel_i}" = "${channel}" ]]; then |
| 517 live_versions[${i}]="${version}" |
| 518 live_urls[${i}]="${url}" |
| 519 fi |
| 520 done |
| 521 done <<< "${omahaproxy_output}" |
| 522 |
| 523 g_cleanup+=("${output_dir}") |
| 524 |
| 525 g_temp_dir="$(mktemp -d -t "${ME}")" |
| 526 g_cleanup+=("${g_temp_dir}") |
| 527 |
| 528 # A "half-signed" application has its inner components signed, but the |
| 529 # outermost browser application bundle is unsigned. The outer bundle can't |
| 530 # be signed until Keystone channel and branding values are added in |
| 531 # build_dmg. However, in order to facilitate differential updates, the inner |
| 532 # components need to be byte-for-byte identical regardless of the Keystone |
| 533 # values. The Keystone values are not placed in any inner component, and the |
| 534 # inner components are signed just once, here, in order to guarantee |
| 535 # that they'll be identical for each Keystone channel and branding setting. |
| 536 # A signature includes a signing timestamp, so there can be only one signing |
| 537 # operation of the inner components even if there are no other changes. |
| 538 |
| 539 local half_signed_dir="${g_temp_dir}/half_signed" |
| 540 mkdir "${half_signed_dir}" |
| 541 local half_signed_app_path="${half_signed_dir}/${APP_NAME}" |
| 542 |
| 543 rsync "${RSYNC_FLAGS[@]}" "${source_app_path}/" "${half_signed_app_path}" |
| 544 |
| 545 err "half-signing application" |
| 546 "${PACKAGING_DIR}/sign_versioned_dir.sh" "${half_signed_app_path}" \ |
| 547 "${codesign_keychain}" "${codesign_id}" |
| 548 |
| 549 if [[ ${#CH[@]} -ne ${#BR[@]} ]] || |
| 550 [[ ${#CH[@]} -ne ${#NF[@]} ]]; then |
| 551 err "there must be the same number of channels, brands, and name fragments" |
| 552 exit 1 |
| 553 fi |
| 554 |
| 555 # Loop through, setting the channels and brand codes, signing everything, |
| 556 # and building disk images. |
| 557 for i in "${!CH[@]}"; do |
| 558 local channel="${CH[${i}]}" |
| 559 local brand="${BR[${i}]}" |
| 560 local name_fragment="${NF[${i}]}" |
| 561 |
| 562 build_dmg "${output_dir}" "${half_signed_app_path}" \ |
| 563 "${codesign_keychain}" "${codesign_id}" \ |
| 564 "${app_version}" "${channel}" "${brand}" "${name_fragment}" |
| 565 done |
| 566 |
| 567 # Loop through everything with a live version. Note that bash arrays are |
| 568 # sparse, and ${!live_versions[@]} will only return indices that were set in |
| 569 # the ${live_versions} array. |
| 570 for i in "${!live_versions[@]}"; do |
| 571 local live_version="${live_versions[${i}]}" |
| 572 local live_url="${live_urls[${i}]}" |
| 573 local channel="${CH[${i}]}" |
| 574 local name_fragment="${NF[${i}]}" |
| 575 |
| 576 # If the name fragment isn't empty, add a dash in front of it. |
| 577 local dash_fragment="${name_fragment}" |
| 578 if [[ -n "${dash_fragment}" ]]; then |
| 579 dash_fragment="-${dash_fragment}" |
| 580 fi |
| 581 |
| 582 local product_name="${PRODUCT_NAME}" |
| 583 local dmg_product_name="${DMG_PRODUCT_NAME}" |
| 584 |
| 585 # Canary: The disk image should use GoogleChromeCanary as the product name |
| 586 # instead of putting the fragment after the version number. |
| 587 if [[ "${channel}" = "canary" ]]; then |
| 588 product_name="${product_name} ${name_fragment}" |
| 589 dmg_product_name="${dmg_product_name}${name_fragment}" |
| 590 dash_fragment="" |
| 591 fi |
| 592 |
| 593 local print_channel="${channel}" |
| 594 if [[ -z "${print_channel}" ]]; then |
| 595 print_channel="stable" |
| 596 fi |
| 597 |
| 598 if ! is_version_ge "${live_version}" "${MIN_DIFF_VERSION}"; then |
| 599 # The live version is too old to be updated with a differential |
| 600 # updater. Skip it. |
| 601 err "skipping ${print_channel} differential updater, \ |
| 602 live ${live_version} < minimum ${MIN_DIFF_VERSION}" |
| 603 continue |
| 604 fi |
| 605 |
| 606 if ! is_version_gt "${app_version}" "${live_version}"; then |
| 607 # The live version is newer than the app version; a differential updater |
| 608 # would actually be a downgrader. Skip it. |
| 609 err "skipping ${print_channel} differential updater, \ |
| 610 new ${app_version} <= live ${live_version}" |
| 611 continue |
| 612 fi |
| 613 |
| 614 local live_major=$(cut -d. -f1 <<< "${live_version}") |
| 615 local app_major=$(cut -d. -f1 <<< "${app_version}") |
| 616 if [[ ${app_major} -gt $((${live_major} + 1)) ]]; then |
| 617 err "skipping ${print_channel} differential updater, \ |
| 618 new ${app_version} is more than one major version ahead of live ${live_version}" |
| 619 continue |
| 620 fi |
| 621 |
| 622 err "downloading ${live_url}" |
| 623 local live_dmg="${g_temp_dir}/\ |
| 624 ${dmg_product_name}-${live_version}${dash_fragment}.dmg" |
| 625 for i in {1..5}; do |
| 626 if ! curl "${CURL_FLAGS[@]}" --max-time 120 "${live_url}" \ |
| 627 -o "${live_dmg}" || |
| 628 ! [[ -f "${live_dmg}" ]]; then |
| 629 if [[ ${i} -lt 5 ]]; then |
| 630 err "warning: could not get live image, will retry" |
| 631 sleep 15 |
| 632 else |
| 633 err "live_dmg file ${live_dmg} not found" |
| 634 exit 1 |
| 635 fi |
| 636 else |
| 637 break |
| 638 fi |
| 639 done |
| 640 |
| 641 local new_dmg="${output_dir}/\ |
| 642 ${dmg_product_name}-${app_version}${dash_fragment}.dmg" |
| 643 if ! [[ -f "${new_dmg}" ]]; then |
| 644 err "new_dmg file ${new_dmg} not found" |
| 645 exit 1 |
| 646 fi |
| 647 |
| 648 local patch_dmg_name=\ |
| 649 "${dmg_product_name}-${live_version}-${app_version}${dash_fragment}-Update.dmg" |
| 650 local patch_dmg="${output_dir}/${patch_dmg_name}" |
| 651 |
| 652 err "creating ${patch_dmg_name}" |
| 653 "${PACKAGING_DIR}/dmgdiffer.sh" \ |
| 654 "${product_name}" "${live_dmg}" "${new_dmg}" "${patch_dmg}" |
| 655 done |
| 656 |
| 657 rm -rf "${g_temp_dir}" |
| 658 unset g_cleanup[${#g_cleanup[@]}] # g_temp_dir |
| 659 |
| 660 unset g_cleanup[${#g_cleanup[@]}] # out_dir |
| 661 trap - EXIT |
| 662 } |
| 663 |
| 664 # Use -lt instead of -eq to tolerate this script's caller providing more |
| 665 # arguments. This script's caller may be upgraded to provide more arguments |
| 666 # to future versions of this script, but if this version runs, it should be |
| 667 # able to operate with only the arguments it needs. |
| 668 if [[ ${#} -lt 5 ]]; then |
| 669 usage |
| 670 exit 1 |
| 671 fi |
| 672 |
| 673 main "${@}" |
| 674 exit ${?} |
OLD | NEW |