#!/bin/bash set -euo pipefail # # Incrementally update a staged build in staging/ by zipping changed files from # the client build/ directory directly into app/omni.ja, based on a manifest # saved by dir_build after the last full build. # # Only files that are copied into omni.ja unmodified by build.sh can be updated # this way. If any other file changed (or a file was removed), a full rebuild # is required. # # Exit codes: # 0 -- staged build was updated (or nothing changed) # 2 -- a full rebuild is required # # This is normally run automatically by dir_build, not directly. # SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")" ROOT_DIR="$(dirname "$APP_ROOT_DIR")" . "$APP_ROOT_DIR/config.sh" . "$SCRIPT_DIR/utils.sh" function usage { cat >&2 <&2 exit 2 } if [ ! -d "$BUILD_SRC_DIR" ]; then fall_back "No build/ directory" fi if [ ! -f "$MANIFEST_FILE" ]; then fall_back "No manifest from previous build" fi if [[ "$(sed -n 's/^#format=//p' "$MANIFEST_FILE")" != "2" ]]; then fall_back "Unsupported manifest format" fi # Make sure a staged build exists for the requested platform case $platform in m) app_dir="$STAGE_DIR/Zotero.app/Contents/Resources" ;; w) app_dir="$STAGE_DIR/Zotero_$(get_canonical_arch w "$arch")" ;; l) app_dir="$STAGE_DIR/Zotero_linux-$(get_canonical_arch l "$arch")" ;; *) fall_back "Unknown platform '$platform'" ;; esac if [ ! -f "$app_dir/app/omni.ja" ]; then fall_back "No staged build for requested platform" fi # If the previous build didn't include tests or devtools and they're requested # now, files are missing from the staged build manifest_tests=$(sed -n 's/^#include_tests=//p' "$MANIFEST_FILE") manifest_tests=${manifest_tests:-0} manifest_devtools=$(sed -n 's/^#devtools=//p' "$MANIFEST_FILE") manifest_devtools=${manifest_devtools:-0} if [[ -z "${ZOTERO_TEST:-}" ]] || [[ "${ZOTERO_TEST:-}" == "0" ]]; then want_tests=0 else want_tests=1 fi if [[ $want_tests -eq 1 ]] && [[ $manifest_tests != "1" ]]; then fall_back "Tests requested but not included in staged build" fi if [[ $devtools -eq 1 ]] && [[ $manifest_devtools != "1" ]]; then fall_back "Devtools requested but not included in staged build" fi # If anything changed in app/ (build scripts, assets, bundled modules, Firefox # runtime), a full rebuild is required manifest_app_hash=$(sed -n 's/^#app_hash=//p' "$MANIFEST_FILE") app_hash=$(generate_app_hash "$APP_ROOT_DIR") if [[ -z "$manifest_app_hash" ]] || [[ "$app_hash" != "$manifest_app_hash" ]]; then fall_back "Files in app/ changed" fi file_list=$(mktemp) new_stats=$(mktemp) carried=$(mktemp) candidates=$(mktemp) removed=$(mktemp) cand_entries=$(mktemp) trap 'rm -f "$file_list" "$new_stats" "$carried" "$candidates" "$removed" "$cand_entries"' EXIT cd "$BUILD_SRC_DIR" # Quickly find files that may have changed based on size/mtime, so that only # those have to be hashed. Unmodified manifest entries are carried over, # new/modified files become candidates, and files no longer present are # considered removed. build_manifest_file_list > "$file_list" stat_file_list < "$file_list" > "$new_stats" awk -F'\t' -v carried="$carried" -v candidates="$candidates" -v removed="$removed" ' NR==FNR { if (/^#/) next hash[$3] = $1 stat[$3] = $2 next } { if (!($2 in stat)) { print > candidates } else { if ($1 == stat[$2]) { print hash[$2] "\t" $0 > carried } else { print > candidates } delete stat[$2] } } END { for (path in stat) print path > removed } ' "$MANIFEST_FILE" "$new_stats" # Hash the candidates and compare to the previous hashes to find real changes # -- A = added, C = changed, R = removed if [ -s "$candidates" ]; then paste \ <(cut -f2 "$candidates" | sed 's,^,./,' | tr '\n' '\0' | hash_file_list | cut -f1) \ "$candidates" \ > "$cand_entries" fi changes=$( if [ -s "$cand_entries" ]; then awk -F'\t' ' NR==FNR { if (/^#/) next old[$3] = $1 next } { if (!($3 in old)) print "A\t" $3 else if (old[$3] != $1) print "C\t" $3 } ' "$MANIFEST_FILE" "$cand_entries" fi if [ -s "$removed" ]; then awk '{print "R\t" $0}' "$removed" fi ) # Make sure all changed files can be zipped directly into omni.ja, and skip # ones that aren't included in the staged build zip_files=() ftl_files=() test_copy_files=() while IFS=$'\t' read -r status path; do if [ -z "$status" ]; then continue fi if [ "$status" == "R" ]; then case "$path" in test/*) # Ignore removed test files if tests weren't included if [[ $manifest_tests != "1" ]]; then continue fi ;; esac fall_back "$path was removed" fi case "$path" in # Copied into localization/ subdirectories by build.sh chrome/locale/*/zotero/mozilla/*) fall_back "Can't update $path in place" ;; # Pruned by prepare_build chrome/content/zotero/locale/csl/*) fall_back "Can't update $path in place" ;; # Copied to localization// in addition to the chrome path chrome/locale/*/zotero/*.ftl) zip_files+=("$path") ftl_files+=("$path") ;; chrome/*|components/*|resource/*) zip_files+=("$path") ;; # Appended to the main chrome.manifest by build.sh test/chrome.manifest) fall_back "Can't update $path in place" ;; test/*) # If tests weren't included in the staged build, ignore changes # to test files if [[ $manifest_tests == "1" ]]; then zip_files+=("$path") case "$path" in # Also copied to tests/ at the top level of the app test/tests/*) test_copy_files+=("$path") ;; esac fi ;; *) fall_back "Can't update $path in place" ;; esac done <<< "$changes" # Find all staged omni.ja files to update (multiple can exist -- e.g., all # Windows architectures) omni_files=() while IFS= read -r file; do omni_files+=("$file") done < <(find "$STAGE_DIR" -path '*/app/omni.ja') if [ ${#zip_files[@]} -gt 0 ]; then echo "Changed files:" for path in "${zip_files[@]}"; do echo " $path" done cd "$BUILD_SRC_DIR" for omni_file in "${omni_files[@]}"; do echo "Updating ${omni_file#"$STAGE_DIR"/}" zip -qX "$omni_file" "${zip_files[@]}" done # Copy .ftl files to their localization/ paths if [ ${#ftl_files[@]} -gt 0 ]; then l10n_dir=$(mktemp -d) for path in "${ftl_files[@]}"; do locale=${path#chrome/locale/} locale=${locale%%/*} mkdir -p "$l10n_dir/localization/$locale" cp -L "$path" "$l10n_dir/localization/$locale/$(basename "$path")" done pushd "$l10n_dir" > /dev/null for omni_file in "${omni_files[@]}"; do zip -qrX "$omni_file" localization done popd > /dev/null rm -rf "$l10n_dir" fi # Copy test files to tests/ directories if [ ${#test_copy_files[@]} -gt 0 ]; then for path in "${test_copy_files[@]}"; do for omni_file in "${omni_files[@]}"; do dest="${omni_file%/app/omni.ja}/tests/${path#test/tests/}" mkdir -p "$(dirname "$dest")" cp -L "$path" "$dest" done done fi # Bump BuildID so that startup caches are invalidated even without # -purgecaches build_id=$(date +%Y%m%d%H%M%S) for omni_file in "${omni_files[@]}"; do perl -pi -e "s/^BuildID=.*/BuildID=$build_id/" "$(dirname "$omni_file")/application.ini" done echo "Updated staged build with ${#zip_files[@]} changed file(s)" elif [ -n "$changes" ]; then echo "No staged files changed" else echo "No changes since last build" fi { echo "#format=2" echo "#include_tests=$manifest_tests" echo "#devtools=$manifest_devtools" echo "#app_hash=$app_hash" LC_ALL=C sort -t$'\t' -k3 "$carried" "$cand_entries" } > "$MANIFEST_FILE.tmp" mv "$MANIFEST_FILE.tmp" "$MANIFEST_FILE"