#!/usr/bin/env bash set -euo pipefail NEW_WEB=${1:?new web root is required} OLD_WEB=${2:?old web root is required} MAX_COMPATIBILITY_GENERATIONS=${3:-3} case "$MAX_COMPATIBILITY_GENERATIONS" in ''|*[!0-9]*) printf 'invalid compatibility generation limit: %s\n' "$MAX_COMPATIBILITY_GENERATIONS" >&2; exit 1 ;; esac test "$MAX_COMPATIBILITY_GENERATIONS" -ge 1 || { printf 'compatibility generation limit must be positive\n' >&2; exit 1; } test -f "$NEW_WEB/.release-assets" || { printf 'new release manifest is missing\n' >&2; exit 1; } test -f "$OLD_WEB/.release-assets" || { printf 'old release manifest is missing\n' >&2; exit 1; } mkdir -p "$NEW_WEB/assets" "$NEW_WEB/.compatibility-manifests" validate_asset() { local asset=$1 case "$asset" in ''|/*|*/*|.|..) printf 'unsafe release asset: %s\n' "$asset" >&2; return 1 ;; esac } while IFS= read -r asset || test -n "$asset"; do asset=${asset%$'\r'} test -n "$asset" || continue validate_asset "$asset" test -f "$NEW_WEB/assets/$asset" || { printf 'current asset is missing: %s\n' "$NEW_WEB/assets/$asset" >&2; exit 1; } done < "$NEW_WEB/.release-assets" copy_manifest_assets() { local manifest=$1 local asset source destination while IFS= read -r asset || test -n "$asset"; do asset=${asset%$'\r'} test -n "$asset" || continue validate_asset "$asset" source="$OLD_WEB/assets/$asset" destination="$NEW_WEB/assets/$asset" test -f "$source" || { printf 'compatibility asset is missing: %s\n' "$source" >&2; return 1; } if test -f "$destination"; then cmp -s "$source" "$destination" || { printf 'asset hash collision has different content: %s\n' "$asset" >&2; return 1; } else cp "$source" "$destination" fi done < "$manifest" } cp "$OLD_WEB/.release-assets" "$NEW_WEB/.compatibility-manifests/1.assets" generation=2 while test "$generation" -le "$MAX_COMPATIBILITY_GENERATIONS"; do previous=$((generation - 1)) source_manifest="$OLD_WEB/.compatibility-manifests/$previous.assets" test -f "$source_manifest" || break cp "$source_manifest" "$NEW_WEB/.compatibility-manifests/$generation.assets" generation=$((generation + 1)) done compatibility_manifest="$NEW_WEB/.compatibility-assets" : > "$compatibility_manifest" for manifest in "$NEW_WEB"/.compatibility-manifests/*.assets; do test -f "$manifest" || continue copy_manifest_assets "$manifest" cat "$manifest" >> "$compatibility_manifest" done LC_ALL=C sort -u "$compatibility_manifest" -o "$compatibility_manifest" current_count=$(awk 'NF { count += 1 } END { print count + 0 }' "$NEW_WEB/.release-assets") compatibility_count=$(awk 'NF { count += 1 } END { print count + 0 }' "$compatibility_manifest") generation_count=$(find "$NEW_WEB/.compatibility-manifests" -maxdepth 1 -type f -name '*.assets' | wc -l | tr -d ' ') served_count=$(find "$NEW_WEB/assets" -maxdepth 1 -type f | wc -l | tr -d ' ') printf 'web_release_tree=ok current_assets=%s compatibility_assets=%s compatibility_generations=%s served_assets=%s\n' "$current_count" "$compatibility_count" "$generation_count" "$served_count"