mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
The fetch script tries git.hit.moe, then the repo.miawa.cn mirror, and only then Git LFS, but it bailed out of the mirror loop on the first file no mirror could serve and then pulled the whole case from GitHub. A case whose mirrors served every file but one paid GitHub's LFS bandwidth for all of them. Collect the files that survived every mirror and every retry instead, and scope the LFS fallback to just those, matching what the local macOS retrace helper already does.
247 lines
8.2 KiB
Bash
247 lines
8.2 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
if [ "$#" -lt 1 ] || [ "$#" -gt 2 ]; then
|
|
echo "usage: $0 <trace-case> [fixture-dir]" >&2
|
|
exit 2
|
|
fi
|
|
|
|
case_name="$1"
|
|
fixture_dir="${2:-tools/trace_replay/fixtures}"
|
|
python_bin="${PYTHON:-python3}"
|
|
# Fixture mirrors, tried in order before falling back to Git LFS. Override the
|
|
# whole list with MOBILEGL_TRACE_FIXTURE_MIRROR_BASES (whitespace separated);
|
|
# MOBILEGL_TRACE_FIXTURE_MIRROR_BASE still works and is tried first.
|
|
default_mirror_bases=(
|
|
"https://git.hit.moe/swung0x48/MobileGL/media/branch/dev/tools/trace_replay/fixtures"
|
|
"https://repo.miawa.cn/mgl/tools/trace_replay/fixtures"
|
|
)
|
|
if [ -n "${MOBILEGL_TRACE_FIXTURE_MIRROR_BASES:-}" ]; then
|
|
read -r -a mirror_bases <<< "${MOBILEGL_TRACE_FIXTURE_MIRROR_BASES}"
|
|
else
|
|
mirror_bases=("${default_mirror_bases[@]}")
|
|
fi
|
|
if [ -n "${MOBILEGL_TRACE_FIXTURE_MIRROR_BASE:-}" ]; then
|
|
mirror_bases=("${MOBILEGL_TRACE_FIXTURE_MIRROR_BASE}" "${mirror_bases[@]}")
|
|
fi
|
|
# Optional bearer token for mirrors that require authentication (private Gitea).
|
|
mirror_token="${MOBILEGL_TRACE_FIXTURE_MIRROR_TOKEN:-}"
|
|
download_attempts="${MOBILEGL_TRACE_FIXTURE_DOWNLOAD_ATTEMPTS:-5}"
|
|
retry_delay="${MOBILEGL_TRACE_FIXTURE_RETRY_DELAY:-2}"
|
|
|
|
if ! command -v "${python_bin}" >/dev/null 2>&1 && command -v python >/dev/null 2>&1; then
|
|
python_bin=python
|
|
fi
|
|
|
|
if ! [[ "${download_attempts}" =~ ^[1-9][0-9]*$ ]]; then
|
|
echo "MOBILEGL_TRACE_FIXTURE_DOWNLOAD_ATTEMPTS must be a positive integer: ${download_attempts}" >&2
|
|
exit 2
|
|
fi
|
|
if ! [[ "${retry_delay}" =~ ^[0-9]+$ ]]; then
|
|
echo "MOBILEGL_TRACE_FIXTURE_RETRY_DELAY must be a non-negative integer: ${retry_delay}" >&2
|
|
exit 2
|
|
fi
|
|
|
|
fixture_list="$("${python_bin}" tools/trace_replay/trace_cases.py \
|
|
--format fixture-files \
|
|
--case "${case_name}" \
|
|
--fixture-root "${fixture_dir}")"
|
|
# Strip CR so the script also works when python emits CRLF (Git Bash on Windows).
|
|
mapfile -t files < <(printf '%s\n' "${fixture_list}" | tr -d '\r')
|
|
|
|
include="$(IFS=,; echo "${files[*]}")"
|
|
if [ "${case_name}" = "OpenRA" ]; then
|
|
echo "Fixture files for ${case_name} are stored in Git: ${include}"
|
|
for file in "${files[@]}"; do
|
|
test -s "${file}"
|
|
if head -n 1 "${file}" | grep -q "version https://git-lfs.github.com/spec/v1"; then
|
|
echo "fixture should not be stored as an LFS pointer: ${file}" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
exit 0
|
|
fi
|
|
|
|
get_lfs_metadata() {
|
|
local file="$1"
|
|
local pointer
|
|
local expected_oid
|
|
local expected_size
|
|
|
|
if ! pointer="$(git show "HEAD:${file}" 2>/dev/null)"; then
|
|
echo "failed to read tracked fixture metadata: ${file}" >&2
|
|
return 1
|
|
fi
|
|
if ! grep -q '^version https://git-lfs.github.com/spec/v1$' <<< "${pointer}"; then
|
|
echo "tracked fixture is not a Git LFS pointer: ${file}" >&2
|
|
return 1
|
|
fi
|
|
|
|
expected_oid="$(awk '$1 == "oid" && $2 ~ /^sha256:/ { sub(/^sha256:/, "", $2); print $2 }' <<< "${pointer}")"
|
|
expected_size="$(awk '$1 == "size" { print $2 }' <<< "${pointer}")"
|
|
if ! [[ "${expected_oid}" =~ ^[0-9a-f]{64}$ ]] || ! [[ "${expected_size}" =~ ^[0-9]+$ ]]; then
|
|
echo "invalid Git LFS pointer metadata: ${file}" >&2
|
|
return 1
|
|
fi
|
|
|
|
printf '%s %s\n' "${expected_oid}" "${expected_size}"
|
|
}
|
|
|
|
verify_fixture_file() {
|
|
local downloaded_file="$1"
|
|
local display_name="$2"
|
|
local expected_oid="$3"
|
|
local expected_size="$4"
|
|
local actual_oid
|
|
local actual_size
|
|
|
|
if [ ! -f "${downloaded_file}" ]; then
|
|
echo "fixture file is missing: ${display_name}" >&2
|
|
return 1
|
|
fi
|
|
|
|
actual_size="$(wc -c < "${downloaded_file}" | tr -d '[:space:]')"
|
|
if [ "${actual_size}" != "${expected_size}" ]; then
|
|
echo "fixture size mismatch for ${display_name}: expected ${expected_size}, got ${actual_size}" >&2
|
|
return 1
|
|
fi
|
|
|
|
actual_oid="$(sha256sum "${downloaded_file}" | awk '{ print $1 }')"
|
|
if [ "${actual_oid}" != "${expected_oid}" ]; then
|
|
echo "fixture SHA-256 mismatch for ${display_name}: expected ${expected_oid}, got ${actual_oid}" >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
fetch_file_from_mirror() {
|
|
local file="$1"
|
|
local url="$2"
|
|
local metadata
|
|
local expected_oid
|
|
local expected_size
|
|
local tmp_file="${file}.tmp"
|
|
local attempt
|
|
local partial_size
|
|
local curl_status
|
|
local curl_auth
|
|
|
|
metadata="$(get_lfs_metadata "${file}")" || return 1
|
|
read -r expected_oid expected_size <<< "${metadata}"
|
|
|
|
if [ -f "${tmp_file}" ]; then
|
|
partial_size="$(wc -c < "${tmp_file}" | tr -d '[:space:]')"
|
|
if [ "${partial_size}" -gt "${expected_size}" ]; then
|
|
echo "Discarding oversized partial fixture ${tmp_file}: ${partial_size} > ${expected_size}" >&2
|
|
rm -f "${tmp_file}"
|
|
elif [ "${partial_size}" = "${expected_size}" ]; then
|
|
if verify_fixture_file "${tmp_file}" "${file}" "${expected_oid}" "${expected_size}"; then
|
|
mv "${tmp_file}" "${file}"
|
|
return 0
|
|
fi
|
|
rm -f "${tmp_file}"
|
|
fi
|
|
fi
|
|
|
|
for ((attempt = 1; attempt <= download_attempts; attempt++)); do
|
|
partial_size=0
|
|
if [ -f "${tmp_file}" ]; then
|
|
partial_size="$(wc -c < "${tmp_file}" | tr -d '[:space:]')"
|
|
fi
|
|
|
|
if [ "${partial_size}" -gt 0 ]; then
|
|
echo "Resuming mirror download for ${file} at byte ${partial_size} (attempt ${attempt}/${download_attempts})"
|
|
else
|
|
echo "Starting mirror download for ${file} (attempt ${attempt}/${download_attempts})"
|
|
fi
|
|
|
|
curl_auth=()
|
|
if [ -n "${mirror_token}" ]; then
|
|
curl_auth=(--header "Authorization: token ${mirror_token}")
|
|
fi
|
|
if curl -L --fail --show-error --continue-at - "${curl_auth[@]}" --output "${tmp_file}" "${url}"; then
|
|
if verify_fixture_file "${tmp_file}" "${file}" "${expected_oid}" "${expected_size}"; then
|
|
mv "${tmp_file}" "${file}"
|
|
return 0
|
|
fi
|
|
echo "Mirror download failed integrity verification; retrying from the beginning: ${file}" >&2
|
|
rm -f "${tmp_file}"
|
|
else
|
|
curl_status=$?
|
|
partial_size=0
|
|
if [ -f "${tmp_file}" ]; then
|
|
partial_size="$(wc -c < "${tmp_file}" | tr -d '[:space:]')"
|
|
fi
|
|
|
|
if [ "${partial_size}" = "${expected_size}" ]; then
|
|
if verify_fixture_file "${tmp_file}" "${file}" "${expected_oid}" "${expected_size}"; then
|
|
mv "${tmp_file}" "${file}"
|
|
return 0
|
|
fi
|
|
rm -f "${tmp_file}"
|
|
partial_size=0
|
|
elif [ "${partial_size}" -gt "${expected_size}" ]; then
|
|
echo "Discarding oversized partial fixture ${tmp_file}: ${partial_size} > ${expected_size}" >&2
|
|
rm -f "${tmp_file}"
|
|
partial_size=0
|
|
elif [ "${curl_status}" -eq 33 ]; then
|
|
echo "Mirror refused the resume request; retrying from the beginning: ${file}" >&2
|
|
rm -f "${tmp_file}"
|
|
partial_size=0
|
|
fi
|
|
|
|
echo "Mirror download attempt ${attempt}/${download_attempts} failed with curl exit ${curl_status}; retained ${partial_size} bytes for resume: ${file}" >&2
|
|
fi
|
|
|
|
if [ "${attempt}" -lt "${download_attempts}" ]; then
|
|
sleep "${retry_delay}"
|
|
fi
|
|
done
|
|
|
|
rm -f "${tmp_file}"
|
|
return 1
|
|
}
|
|
|
|
# Files no mirror could serve, even after retrying every mirror. Only these fall
|
|
# back to Git LFS, so a mirror that served the rest of the case still spares
|
|
# GitHub the bandwidth for those files.
|
|
mirror_failures=()
|
|
|
|
fetch_from_mirror() {
|
|
mkdir -p "${fixture_dir}"
|
|
for file in "${files[@]}"; do
|
|
local name
|
|
local url
|
|
local base
|
|
local fetched=0
|
|
name="$(basename "${file}")"
|
|
for base in "${mirror_bases[@]}"; do
|
|
url="${base%/}/${name}"
|
|
echo "Fetching trace fixture from mirror: ${url}"
|
|
if fetch_file_from_mirror "${file}" "${url}"; then
|
|
fetched=1
|
|
break
|
|
fi
|
|
echo "Mirror did not serve ${name}; trying the next mirror" >&2
|
|
done
|
|
if [ "${fetched}" -ne 1 ]; then
|
|
mirror_failures+=("${file}")
|
|
fi
|
|
done
|
|
[ "${#mirror_failures[@]}" -eq 0 ]
|
|
}
|
|
|
|
if fetch_from_mirror; then
|
|
echo "Fetched trace fixture files for ${case_name} from mirror: ${include}"
|
|
else
|
|
fallback_include="$(IFS=,; echo "${mirror_failures[*]}")"
|
|
echo "All mirrors failed for ${#mirror_failures[@]} of ${#files[@]} file(s) of ${case_name}; falling back to Git LFS: ${fallback_include}"
|
|
git lfs install --local
|
|
git lfs pull --include="${fallback_include}" --exclude=""
|
|
fi
|
|
|
|
for file in "${files[@]}"; do
|
|
metadata="$(get_lfs_metadata "${file}")"
|
|
read -r expected_oid expected_size <<< "${metadata}"
|
|
verify_fixture_file "${file}" "${file}" "${expected_oid}" "${expected_size}"
|
|
done
|