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

Unified Diff: chrome/installer/mac/keystone_install.sh

Issue 2832005: Binary differential (delta) updates: keystone_install dmgpatcher support (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 10 years, 6 months 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/installer/mac/keystone_install.sh
===================================================================
--- chrome/installer/mac/keystone_install.sh (revision 49726)
+++ chrome/installer/mac/keystone_install.sh (working copy)
@@ -23,12 +23,19 @@
# 3 Basic sanity check destination failure (e.g. ticket points to nothing)
# 4 Update driven by user ticket when a system ticket is also present
# 5 Could not prepare existing installed version to receive update
-# 6 rsync failed (could not assure presence of Versions directory)
+# 6 Patch sanity check failure
# 7 rsync failed (could not copy new versioned directory to Versions)
# 8 rsync failed (could not update outer .app bundle)
# 9 Could not get the version, update URL, or channel after update
# 10 Updated application does not have the version number from the update
# 11 ksadmin failure
+# 12 dirpatcher failed for versioned directory
+# 13 dirpatcher failed for outer .app bundle
+#
+# The following exit codes are not used by this script, but can be used to
+# convey special meaning to Keystone:
+# 66 (unused) success, request reboot
+# 77 (unused) try installation again later
set -eu
@@ -74,6 +81,41 @@
fi
}
+declare g_temp_dir
+cleanup() {
+ local status=${?}
+
+ trap - EXIT
+ trap '' HUP INT QUIT TERM
+
+ if [[ ${status} -ge 128 ]]; then
+ err "Caught signal $((${status} - 128))"
+ fi
+
+ if [[ -n "${g_temp_dir}" ]]; then
+ rm -rf "${g_temp_dir}"
+ fi
+
+ exit ${status}
+}
+
+ensure_temp_dir() {
+ if [[ -z "${g_temp_dir}" ]]; then
+ # Choose a template that won't be a dot directory. Make it safe by
+ # removing leading hyphens, too.
+ local template="${ME}"
+ if [[ "${template}" =~ ^[-.]+(.*)$ ]]; then
+ template="${BASH_REMATCH[1]}"
+ fi
+ if [[ -z "${template}" ]]; then
+ template="keystone_install"
+ fi
+
+ g_temp_dir="$(mktemp -d -t "${template}")"
+ note "g_temp_dir = ${g_temp_dir}"
+ fi
+}
+
# Returns 0 (true) if |symlink| exists, is a symbolic link, and appears
# writable on the basis of its POSIX permissions. This is used to determine
# writability like test's -w primary, but -w resolves symbolic links and this
@@ -184,6 +226,57 @@
return 0
}
+# ensure_writable_symlinks_recursive calls ensure_writable_symlink for every
+# symbolic link in |directory|, recursivley.
+#
+# In some very weird and rare cases, it is possible to wind up with a user
+# installation that contains symbolic links that the user does not have write
+# permission over. More on how that might happen later.
+#
+# If a weird and rare case like this is observed, rsync will exit with an
+# error when attempting to update the times on these symbolic links. rsync
+# may not be intelligent enough to try creating a new symbolic link in these
+# cases, but this script can be.
+#
+# The problem occurs when an administrative user first drag-installs the
+# application to /Applications, resulting in the program's user being set to
+# the user's own ID. If, subsequently, a .pkg package is installed over that,
+# the existing directory ownership will be preserved, but file ownership will
+# be changed to whateer is specified by the package, typically root. This
+# applies to symbolic links as well. On a subsequent update, rsync will be
+# able to copy the new files into place, because the user still has permission
+# to write to the directories. If the symbolic link targets are not changing,
+# though, rsync will not replace them, and they will remain owned by root.
+# The user will not have permission to update the time on the symbolic links,
+# resulting in an rsync error.
+ensure_writable_symlinks_recursive() {
+ local directory="${1}"
+
+ # This fix-up is not necessary when running as root, because root will
+ # always be able to write everything needed.
+ if [[ ${EUID} -eq 0 ]]; then
+ return 0
+ fi
+
+ # This step isn't critical.
+ local set_e=
+ if [[ "${-}" =~ e ]]; then
+ set_e="y"
+ set +e
+ fi
+
+ # Use find -print0 with read -d $'\0' to handle even the weirdest paths.
+ local symlink
+ while IFS= read -r -d $'\0' symlink; do
+ ensure_writable_symlink "${symlink}"
+ done < <(find "${directory}" -type l -print0)
+
+ # Go back to how things were.
+ if [[ -n "${set_e}" ]]; then
+ set -e
+ fi
+}
+
# Prints the version of ksadmin, as reported by ksadmin --ksadmin-version, to
# stdout. This function operates with "static" variables: it will only check
# the ksadmin version once per script run. If ksadmin is old enough to not
@@ -208,11 +301,11 @@
# Keystone version. |check_version| should be a string of the form
# "major.minor.micro.build". Returns 1 (false) if either |check_version| or
# the Keystone version do not match this format.
-readonly VER_RE="^([0-9]+)\\.([0-9]+)\\.([0-9]+)\\.([0-9]+)\$"
+readonly KSADMIN_VERSION_RE="^([0-9]+)\\.([0-9]+)\\.([0-9]+)\\.([0-9]+)\$"
is_ksadmin_version_ge() {
local check_version="${1}"
- if ! [[ "${check_version}" =~ ${VER_RE} ]]; then
+ if ! [[ "${check_version}" =~ ${KSADMIN_VERSION_RE} ]]; then
return 1
fi
@@ -224,7 +317,7 @@
local ksadmin_version
ksadmin_version="$(ksadmin_version)"
- if ! [[ "${ksadmin_version}" =~ ${VER_RE} ]]; then
+ if ! [[ "${ksadmin_version}" =~ ${KSADMIN_VERSION_RE} ]]; then
return 1
fi
@@ -294,10 +387,13 @@
# Early steps are critical. Don't continue past any failure.
set -e
+ trap cleanup EXIT HUP INT QUIT TERM
+
readonly PRODUCT_NAME="Google Chrome"
readonly APP_DIR="${PRODUCT_NAME}.app"
readonly FRAMEWORK_NAME="${PRODUCT_NAME} Framework"
readonly FRAMEWORK_DIR="${FRAMEWORK_NAME}.framework"
+ readonly PATCH_DIR=".patch"
readonly CONTENTS_DIR="Contents"
readonly APP_PLIST="${CONTENTS_DIR}/Info"
readonly VERSIONS_DIR="${CONTENTS_DIR}/Versions"
@@ -348,51 +444,144 @@
exit 2
fi
- # The update to install.
- local update_app="${update_dmg_mount_point}/${APP_DIR}"
- note "update_app = ${update_app}"
-
- # Make sure that there's something to copy from, and that it's an absolute
- # path.
- if [[ "${update_app:0:1}" != "/" ]] ||
- ! [[ -d "${update_app}" ]]; then
- err "update_app must be an absolute path to a directory"
+ local patch_dir="${update_dmg_mount_point}/${PATCH_DIR}"
+ if [[ "${patch_dir:0:1}" != "/" ]]; then
+ note "patch_dir = ${patch_dir}"
+ err "patch_dir must be an absolute path"
exit 2
fi
- # Get some information about the update.
- note "reading update values"
-
- local update_app_plist="${update_app}/${APP_PLIST}"
- note "update_app_plist = ${update_app_plist}"
- local update_version_app
- if ! update_version_app="$(defaults read "${update_app_plist}" \
- "${APP_VERSION_KEY}")" ||
- [[ -z "${update_version_app}" ]]; then
- err "couldn't determine update_version_app"
+ # Figure out if this is an ordinary installation disk image being used as a
+ # full update, or a patch. A patch will have a .patch directory at the root
+ # of the disk image containing information about the update, tools to apply
+ # it, and the update contents.
+ local is_patch=
+ local dirpatcher=
+ if [[ -d "${patch_dir}" ]]; then
+ # patch_dir exists and is a directory - this is a patch update.
+ is_patch="y"
+ dirpatcher="${patch_dir}/dirpatcher.sh"
+ if ! [[ -x "${dirpatcher}" ]]; then
+ err "couldn't locate dirpatcher"
+ exit 6
+ fi
+ elif [[ -e "${patch_dir}" ]]; then
+ # patch_dir exists, but is not a directory - what's that mean?
+ note "patch_dir = ${patch_dir}"
+ err "patch_dir must be a directory"
exit 2
+ else
+ # patch_dir does not exist - this is a full "installer."
+ patch_dir=
fi
- note "update_version_app = ${update_version_app}"
+ note "patch_dir = ${patch_dir}"
+ note "is_patch = ${is_patch}"
+ note "dirpatcher = ${dirpatcher}"
- local update_ks_plist="${update_app_plist}"
- note "update_ks_plist = ${update_ks_plist}"
- local update_version_ks
- if ! update_version_ks="$(defaults read "${update_ks_plist}" \
- "${KS_VERSION_KEY}")" ||
- [[ -z "${update_version_ks}" ]]; then
- err "couldn't determine update_version_ks"
- exit 2
- fi
- note "update_version_ks = ${update_version_ks}"
+ # The update to install.
- local product_id
- if ! product_id="$(defaults read "${update_ks_plist}" \
- "${KS_PRODUCT_KEY}")" ||
- [[ -z "${product_id}" ]]; then
- err "couldn't determine product_id"
- exit 2
+ # update_app is the path to the new version of the .app. It will only be
+ # set at this point for a non-patch update. It is not yet set for a patch
+ # update because no such directory exists yet; it will be set later when
+ # dirpatcher creates it.
+ local update_app=
+
+ # update_version_app_old, patch_app_dir, and patch_versioned_dir will only
+ # be set for patch updates.
+ local update_version_app_old=
+ local patch_app_dir=
+ local patch_versioned_dir=
+
+ local update_version_app update_version_ks product_id
+ if [[ -z "${is_patch}" ]]; then
+ update_app="${update_dmg_mount_point}/${APP_DIR}"
+ note "update_app = ${update_app}"
+
+ # Make sure that there's something to copy from, and that it's an absolute
+ # path.
+ if [[ "${update_app:0:1}" != "/" ]] ||
+ ! [[ -d "${update_app}" ]]; then
+ err "update_app must be an absolute path to a directory"
+ exit 2
+ fi
+
+ # Get some information about the update.
+ note "reading update values"
+
+ local update_app_plist="${update_app}/${APP_PLIST}"
+ note "update_app_plist = ${update_app_plist}"
+ if ! update_version_app="$(defaults read "${update_app_plist}" \
+ "${APP_VERSION_KEY}")" ||
+ [[ -z "${update_version_app}" ]]; then
+ err "couldn't determine update_version_app"
+ exit 2
+ fi
+ note "update_version_app = ${update_version_app}"
+
+ local update_ks_plist="${update_app_plist}"
+ note "update_ks_plist = ${update_ks_plist}"
+ if ! update_version_ks="$(defaults read "${update_ks_plist}" \
+ "${KS_VERSION_KEY}")" ||
+ [[ -z "${update_version_ks}" ]]; then
+ err "couldn't determine update_version_ks"
+ exit 2
+ fi
+ note "update_version_ks = ${update_version_ks}"
+
+ if ! product_id="$(defaults read "${update_ks_plist}" \
+ "${KS_PRODUCT_KEY}")" ||
+ [[ -z "${product_id}" ]]; then
+ err "couldn't determine product_id"
+ exit 2
+ fi
+ note "product_id = ${product_id}"
+ else # [[ -n "${is_patch}" ]]
+ # Get some information about the update.
+ note "reading update values"
+
+ if ! update_version_app_old=$(<"${patch_dir}/old_app_version") ||
+ [[ -z "${update_version_app_old}" ]]; then
+ err "couldn't determine update_version_app_old"
+ exit 2
+ fi
+ note "update_version_app_old = ${update_version_app_old}"
+
+ if ! update_version_app=$(<"${patch_dir}/new_app_version") ||
+ [[ -z "${update_version_app}" ]]; then
+ err "couldn't determine update_version_app"
+ exit 2
+ fi
+ note "update_version_app = ${update_version_app}"
+
+ if ! update_version_ks=$(<"${patch_dir}/new_ks_version") ||
+ [[ -z "${update_version_ks}" ]]; then
+ err "couldn't determine update_version_ks"
+ exit 2
+ fi
+ note "update_version_ks = ${update_version_ks}"
+
+ if ! product_id=$(<"${patch_dir}/ks_product") ||
+ [[ -z "${product_id}" ]]; then
+ err "couldn't determine product_id"
+ exit 2
+ fi
+ note "product_id = ${product_id}"
+
+ patch_app_dir="${patch_dir}/application.dirpatch"
+ if ! [[ -d "${patch_app_dir}" ]]; then
+ err "couldn't locate patch_app_dir"
+ exit 6
+ fi
+ note "patch_app_dir = ${patch_app_dir}"
+
+ patch_versioned_dir=\
+"${patch_dir}/version_${update_version_app_old}_${update_version_app}.dirpatch"
+ if ! [[ -d "${patch_versioned_dir}" ]]; then
+ err "couldn't locate patch_versioned_dir"
+ exit 6
+ fi
+ note "patch_versioned_dir = ${patch_versioned_dir}"
fi
- note "product_id = ${product_id}"
# ksadmin is required. Keystone should have set a ${PATH} that includes it.
# Check that here, so that more useful feedback can be offered in the
@@ -479,6 +668,22 @@
"${APP_VERSION_KEY}" || true)"
note "old_version_app = ${old_version_app}"
+ # old_version_app is not required, because it won't be present in skeleton
+ # bootstrap installations, which just have an empty .app directory. Only
+ # require it when doing a patch update, and use it to validate that the
+ # patch applies to the old installed version. By definition, skeleton
+ # bootstraps can't be installed with patch udpates. They require the full
+ # application on the disk image.
+ if [[ -n "${is_patch}" ]]; then
+ if [[ -z "${old_version_app}" ]]; then
+ err "old_version_app required for patch"
+ exit 6
+ elif [[ "${old_version_app}" != "${update_version_app_old}" ]]; then
+ err "this patch does not apply to the installed version"
+ exit 6
+ fi
+ fi
+
local installed_versions_dir="${installed_app}/${VERSIONS_DIR}"
note "installed_versions_dir = ${installed_versions_dir}"
@@ -500,119 +705,147 @@
true)"
note "old_brand = ${old_brand}"
- # See if the timestamp of what's currently on disk is newer than the
- # update's outer .app's timestamp. rsync will copy the update's timestamp
- # over, but if that timestamp isn't as recent as what's already on disk, the
- # .app will need to be touched.
- local needs_touch=
- if [[ "${installed_app}" -nt "${update_app}" ]]; then
- needs_touch="y"
- fi
- note "needs_touch = ${needs_touch}"
+ ensure_writable_symlinks_recursive "${installed_app}"
- # In some very weird and rare cases, it is possible to wind up with a user
- # installation that contains symbolic links that the user does not have
- # write permission over. More on how that might happen later.
- #
- # If a weird and rare case like this is observed, rsync will exit with an
- # error when attempting to update the times on these symbolic links. rsync
- # may not be intelligent enough to try creating a new symbolic link in these
- # cases, but this script can be.
- #
- # This fix-up is not necessary when running as root, because root will
- # always be able to write everything needed.
- #
- # The problem occurs when an administrative user first drag-installs the
- # application to /Applications, resulting in the program's user being set to
- # the user's own ID. If, subsequently, a .pkg package is installed over
- # that, the existing directory ownership will be preserved, but file
- # ownership will be changed to whateer is specified by the package,
- # typically root. This applies to symbolic links as well. On a subsequent
- # update, rsync will be able to copy the new files into place, because the
- # user still has permission to write to the directories. If the symbolic
- # link targets are not changing, though, rsync will not replace them, and
- # they will remain owned by root. The user will not have permission to
- # update the time on the symbolic links, resulting in an rsync error.
- if [[ ${EUID} -ne 0 ]]; then
- # This step isn't critical.
- set +e
- note "fixing installed symbolic links"
-
- # Only consider symbolic links in ${update_app}. If there are any other
- # links in ${installed_app} not present in ${update_app}, rsync will
- # delete them as needed later. Use find -print0 with read -d $'\0' to
- # handle even the weirdest paths.
- local update_link
- while IFS= read -r -d $'\0' update_link; do
- # ${update_link} is relative to ${update_app}. Prepending
- # ${installed_app} looks for the same link already on disk.
- local installed_link="${installed_app}/${update_link}"
- note "ensure_writable_symlink ${installed_link}"
- ensure_writable_symlink "${installed_link}"
- done < <(cd "${update_app}" && find . -type l -print0)
-
- # Go back to how things were.
- set -e
- fi
-
# By copying to ${installed_app}, the existing application name will be
# preserved, if the user has renamed the application on disk. Respecting
# the user's changes is friendly.
# Make sure that ${installed_versions_dir} exists, so that it can receive
# the versioned directory. It may not exist if updating from an older
- # version that did not use the versioned layout on disk. An rsync that
- # excludes all contents is used to bring the permissions over from
- # ${update_versions_dir}, otherwise, this directory would be the only one in
- # the entire update exempt from getting its permissions copied over. A
- # simple mkdir wouldn't copy mode bits. This is done even if
- # ${installed_versions_dir} already does exist to ensure that the mode bits
- # come from the update.
+ # version that did not use the versioned layout on disk. Later, during the
+ # rsync to copy the applciation directory, the mode bits and timestamp on
+ # ${installed_versions_dir} will be set to conform to whatever is present in
+ # the update.
#
# ${installed_app} is guaranteed to exist at this point, but
# ${installed_app}/${CONTENTS_DIR} may not if things are severely broken or
# if this update is actually an initial installation from a Keystone
# skeleton bootstrap. The mkdir creates ${installed_app}/${CONTENTS_DIR} if
# it doesn't exist; its mode bits will be fixed up in a subsequent rsync.
- note "creating CONTENTS_DIR"
- if ! mkdir -p "${installed_app}/${CONTENTS_DIR}"; then
- err "mkdir of CONTENTS_DIR failed"
+ note "creating installed_versions_dir"
+ if ! mkdir -p "${installed_versions_dir}"; then
+ err "mkdir of installed_versions_dir failed"
exit 5
fi
- local update_versions_dir="${update_app}/${VERSIONS_DIR}"
- note "update_versions_dir = ${update_versions_dir}"
+ local new_versioned_dir
+ new_versioned_dir="${installed_versions_dir}/${update_version_app}"
+ note "new_versioned_dir = ${new_versioned_dir}"
- note "rsyncing VERSIONS_DIR"
- if ! rsync ${RSYNC_FLAGS} --exclude "*" "${update_versions_dir}/" \
- "${installed_versions_dir}"; then
- err "rsync of VERSIONS_DIR failed, status ${PIPESTATUS[0]}"
- exit 6
+ # If there's an entry at ${new_versioned_dir} but it's not a directory
+ # (or it's a symbolic link, whether or not it points to a directory), rsync
+ # won't get rid of it. It's never correct to have a non-directory in place
+ # of the versioned directory, so toss out whatever's there. Don't treat
+ # this as a critical step: if removal fails, operation can still proceed to
+ # to the dirpatcher or rsync, which will likely fail.
+ if [[ -e "${new_versioned_dir}" ]] &&
+ ([[ -L "${new_versioned_dir}" ]] ||
+ ! [[ -d "${new_versioned_dir}" ]]); then
+ note "removing non-directory in place of versioned directory"
+ rm -f "${new_versioned_dir}" 2> /dev/null || true
fi
+ local update_versioned_dir
+ if [[ -z "${is_patch}" ]]; then
+ update_versioned_dir="${update_app}/${VERSIONS_DIR}/${update_version_app}"
+ note "update_versioned_dir = ${update_versioned_dir}"
+ else # [[ -n "${is_patch}" ]]
+ # dirpatcher won't patch into a directory that already exists. Doing so
+ # would be a bad idea, anyway. If ${new_versioned_dir} already exists,
+ # it may be something left over from a previous failed or incomplete
+ # update attempt, or it may be the live versioned directory if this is a
+ # same-version update intended only to change channels. Since there's no
+ # way to tell, this case is handled by having dirpatcher produce the new
+ # versioned directory in a temporary location and then having rsync copy
+ # it into place as an ${update_versioned_dir}, the same as in a non-patch
+ # update. If ${new_versioned_dir} doesn't exist, dirpatcher can place the
+ # new versioned directory at that location directly.
+ local versioned_dir_target
+ if ! [[ -e "${new_versioned_dir}" ]]; then
+ versioned_dir_target="${new_versioned_dir}"
+ note "versioned_dir_target = ${versioned_dir_target}"
+ else
+ ensure_temp_dir
+ versioned_dir_target="${g_temp_dir}/${update_version_app}"
+ note "versioned_dir_target = ${versioned_dir_target}"
+ update_versioned_dir="${versioned_dir_target}"
+ note "update_versioned_dir = ${update_versioned_dir}"
+ fi
+
+ note "dirpatching versioned directory"
+ if ! "${dirpatcher}" "${old_versioned_dir}" \
+ "${patch_versioned_dir}" \
+ "${versioned_dir_target}"; then
+ err "dirpatcher of versioned directory failed, status ${PIPESTATUS[0]}"
+ exit 12
+ fi
+ fi
+
# Copy the versioned directory. The new versioned directory should have a
# different name than any existing one, so this won't harm anything already
# present in ${installed_versions_dir}, including the versioned directory
# being used by any running processes. If this step is interrupted, there
# will be an incomplete versioned directory left behind, but it won't
# won't interfere with anything, and it will be replaced or removed during a
- # future update attempt. Note that in certain cases, same-version updates
- # are distributed to move users between channels; when this happens, the
- # contents of the versioned directories are identical and rsync will not
- # render the versioned directory unusable even for an instant.
- local update_versioned_dir new_versioned_dir
- update_versioned_dir="${update_versions_dir}/${update_version_app}"
- note "update_versioned_dir = ${update_versioned_dir}"
- new_versioned_dir="${installed_versions_dir}/${update_version_app}"
- note "new_versioned_dir = ${new_versioned_dir}"
+ # future update attempt.
+ #
+ # In certain cases, same-version updates are distributed to move users
+ # between channels; when this happens, the contents of the versioned
+ # directories are identical and rsync will not render the versioned
+ # directory unusable even for an instant.
+ #
+ # ${update_versioned_dir} may be empty during a patch update (${is_patch})
+ # if the dirpatcher above was able to write it into place directly. In
+ # that event, dirpatcher guarantees that ${new_versioned_dir} is already in
+ # place.
+ if [[ -n "${update_versioned_dir}" ]]; then
+ note "rsyncing versioned directory"
+ if ! rsync ${RSYNC_FLAGS} --delete-before "${update_versioned_dir}/" \
+ "${new_versioned_dir}"; then
+ err "rsync of versioned directory failed, status ${PIPESTATUS[0]}"
+ exit 7
+ fi
+ fi
- note "rsyncing versioned directory"
- if ! rsync ${RSYNC_FLAGS} --delete-before "${update_versioned_dir}/" \
- "${new_versioned_dir}"; then
- err "rsync of versioned directory failed, status ${PIPESTATUS[0]}"
- exit 7
+ if [[ -n "${is_patch}" ]]; then
+ # If the versioned directory was prepared in a temporary directory and
+ # then rsynced into place, remove the temporary copy now that it's no
+ # longer needed.
+ if [[ -n "${update_versioned_dir}" ]]; then
+ rm -rf "${update_versioned_dir}" 2> /dev/null || true
+ update_versioned_dir=
+ note "update_versioned_dir = ${update_versioned_dir}"
+ fi
+
+ # Prepare ${update_app}. This always needs to be done in a temporary
+ # location because dirpatcher won't write to a directory that already
+ # exists, and ${installed_app} needs to be used as input to dirpatcher
+ # in any event. The new application will be rsynced into place once
+ # dirpatcher creates it.
+ ensure_temp_dir
+ update_app="${g_temp_dir}/${APP_DIR}"
+ note "update_app = ${update_app}"
+
+ note "dirpatching app directory"
+ if ! "${dirpatcher}" "${installed_app}" \
+ "${patch_app_dir}" \
+ "${update_app}"; then
+ err "dirpatcher of app directory failed, status ${PIPESTATUS[0]}"
+ exit 13
+ fi
fi
+ # See if the timestamp of what's currently on disk is newer than the
+ # update's outer .app's timestamp. rsync will copy the update's timestamp
+ # over, but if that timestamp isn't as recent as what's already on disk, the
+ # .app will need to be touched.
+ local needs_touch=
+ if [[ "${installed_app}" -nt "${update_app}" ]]; then
+ needs_touch="y"
+ fi
+ note "needs_touch = ${needs_touch}"
+
# Copy the unversioned files into place, leaving everything in
# ${installed_versions_dir} alone. If this step is interrupted, the
# application will at least remain in a usable state, although it may not
@@ -621,9 +854,11 @@
# critical point is when the main executable is replaced. There isn't very
# much to copy in this step, because most of the application is in the
# versioned directory. This step only accounts for around 50 files, most of
- # which are small localized InfoPlist.strings files.
+ # which are small localized InfoPlist.strings files. Note that
+ # ${VERSIONS_DIR} is included to copy its mode bits and timestamp, but its
+ # contents are excluded, having already been installed above.
note "rsyncing app directory"
- if ! rsync ${RSYNC_FLAGS} --delete-after --exclude "/${VERSIONS_DIR}" \
+ if ! rsync ${RSYNC_FLAGS} --delete-after --exclude "/${VERSIONS_DIR}/*" \
"${update_app}/" "${installed_app}"; then
err "rsync of app directory failed, status ${PIPESTATUS[0]}"
exit 8
@@ -631,6 +866,20 @@
note "rsyncs complete"
+ if [[ -n "${is_patch}" ]]; then
+ # update_app has been rsynced into place and is no longer needed.
+ rm -rf "${update_app}" 2> /dev/null || true
+ update_app=
+ note "update_app = ${update_app}"
+ fi
+
+ if [[ -n "${g_temp_dir}" ]]; then
+ # The temporary directory, if any, is no longer needed.
+ rm -rf "${g_temp_dir}" 2> /dev/null || true
+ g_temp_dir=
+ note "g_temp_dir = ${g_temp_dir}"
+ fi
+
# If necessary, touch the outermost .app so that it appears to the outside
# world that something was done to the bundle. This will cause
# LaunchServices to invalidate the information it has cached about the
@@ -967,6 +1216,9 @@
# Great success!
note "done!"
+
+ trap - EXIT
+
return 0
}
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698