mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-07-22 01:15:14 +08:00
feat: add official full pull smoke
Emit runtime download progress and validation summaries for official sync. Add repeatable real-network full pull smoke runbook and script. Fixes #4 Fixes #6
This commit is contained in:
Executable
+184
@@ -0,0 +1,184 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
cd "${REPO_ROOT}"
|
||||
|
||||
timestamp="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
SMOKE_ROOT="${BAT_SMOKE_ROOT:-/tmp/bat-official-smoke-${timestamp}}"
|
||||
OUTPUT_DIR="${BAT_SMOKE_OUTPUT:-${SMOKE_ROOT}/resources}"
|
||||
STATE_DIR="${BAT_SMOKE_STATE_DIR:-${SMOKE_ROOT}/state}"
|
||||
REPORT_DIR="${BAT_SMOKE_REPORT_DIR:-${SMOKE_ROOT}/report}"
|
||||
BAT_BIN="${BAT_BIN:-${REPO_ROOT}/target/release/bat}"
|
||||
|
||||
fail() {
|
||||
printf 'error: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
real_output="$(realpath -m "${OUTPUT_DIR}")"
|
||||
case "${real_output}" in
|
||||
/tmp/*) ;;
|
||||
*)
|
||||
if [[ "${BAT_SMOKE_ALLOW_NON_TMP:-}" != "1" ]]; then
|
||||
fail "BAT_SMOKE_OUTPUT must be under /tmp by default; set BAT_SMOKE_ALLOW_NON_TMP=1 only for an isolated test path"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ -e "${OUTPUT_DIR}" ]] && [[ -n "$(find "${OUTPUT_DIR}" -mindepth 1 -maxdepth 1 -print -quit 2>/dev/null)" ]]; then
|
||||
fail "output directory is not empty: ${OUTPUT_DIR}"
|
||||
fi
|
||||
|
||||
mkdir -p "${OUTPUT_DIR}" "${STATE_DIR}" "${REPORT_DIR}"
|
||||
|
||||
if [[ "${BAT_SMOKE_SKIP_BUILD:-}" != "1" ]]; then
|
||||
cargo build --release -p bat-infrastructure --bin bat
|
||||
fi
|
||||
|
||||
[[ -x "${BAT_BIN}" ]] || fail "bat binary is not executable: ${BAT_BIN}"
|
||||
|
||||
write_command_file() {
|
||||
local name="$1"
|
||||
shift
|
||||
local command_file="${REPORT_DIR}/${name}.command.txt"
|
||||
: >"${command_file}"
|
||||
printf '$' >>"${command_file}"
|
||||
printf ' %q' "$@" >>"${command_file}"
|
||||
printf '\n' >>"${command_file}"
|
||||
}
|
||||
|
||||
run_step() {
|
||||
local name="$1"
|
||||
shift
|
||||
printf '==> %s\n' "${name}"
|
||||
write_command_file "${name}" "$@"
|
||||
"$@" >"${REPORT_DIR}/${name}.stdout.json" 2>"${REPORT_DIR}/${name}.stderr.log"
|
||||
}
|
||||
|
||||
require_contains() {
|
||||
local path="$1"
|
||||
local needle="$2"
|
||||
local description="$3"
|
||||
if ! grep -Fq "${needle}" "${path}"; then
|
||||
fail "${description}; expected '${needle}' in ${path}"
|
||||
fi
|
||||
}
|
||||
|
||||
run_step "01-dry-run-plan" \
|
||||
"${BAT_BIN}" \
|
||||
--auto-discover \
|
||||
--output "${OUTPUT_DIR}" \
|
||||
--state-dir "${STATE_DIR}" \
|
||||
--dry-run \
|
||||
--plan \
|
||||
--json \
|
||||
--no-progress
|
||||
|
||||
run_step "02-first-full-pull" \
|
||||
"${BAT_BIN}" \
|
||||
--auto-discover \
|
||||
--output "${OUTPUT_DIR}" \
|
||||
--state-dir "${STATE_DIR}" \
|
||||
--json \
|
||||
--progress \
|
||||
--no-banner
|
||||
|
||||
require_contains "${REPORT_DIR}/02-first-full-pull.stderr.log" "下载进度:总体" "first pull did not emit total download progress"
|
||||
require_contains "${REPORT_DIR}/02-first-full-pull.stderr.log" "单文件" "first pull did not emit single-file download progress"
|
||||
require_contains "${REPORT_DIR}/02-first-full-pull.stderr.log" "校验结果:" "first pull did not emit verification summary"
|
||||
|
||||
run_step "03-second-up-to-date" \
|
||||
"${BAT_BIN}" \
|
||||
--auto-discover \
|
||||
--output "${OUTPUT_DIR}" \
|
||||
--state-dir "${STATE_DIR}" \
|
||||
--json \
|
||||
--no-progress
|
||||
|
||||
require_contains "${REPORT_DIR}/03-second-up-to-date.stdout.json" '"update_status": "up_to_date"' "second run did not report up_to_date"
|
||||
|
||||
current_link="${OUTPUT_DIR}/current"
|
||||
[[ -e "${current_link}" ]] || fail "current release symlink/path was not created: ${current_link}"
|
||||
current_dir="$(realpath "${current_link}")"
|
||||
|
||||
damage_file="$(find "${current_dir}" -type f \
|
||||
! -name 'official-sync-snapshot.json' \
|
||||
! -name 'official-download-manifest.json' \
|
||||
-print -quit)"
|
||||
[[ -n "${damage_file}" ]] || fail "no resource file found to damage under ${current_dir}"
|
||||
|
||||
printf '%s\n' "${damage_file}" >"${REPORT_DIR}/damaged-file.txt"
|
||||
{
|
||||
printf 'path: %s\n' "${damage_file}"
|
||||
printf 'bytes: %s\n' "$(wc -c <"${damage_file}" | tr -d ' ')"
|
||||
printf 'sha256: %s\n' "$(sha256sum "${damage_file}" | awk '{print $1}')"
|
||||
} >"${REPORT_DIR}/damaged-file.before.txt"
|
||||
printf 'BAT official smoke intentional corruption\n' >"${damage_file}"
|
||||
|
||||
run_step "04-repair-after-damage" \
|
||||
"${BAT_BIN}" \
|
||||
repair \
|
||||
--auto-discover \
|
||||
--output "${OUTPUT_DIR}" \
|
||||
--state-dir "${STATE_DIR}" \
|
||||
--json \
|
||||
--progress \
|
||||
--no-banner
|
||||
|
||||
require_contains "${REPORT_DIR}/04-repair-after-damage.stdout.json" '"command": "repair"' "repair command report missing"
|
||||
require_contains "${REPORT_DIR}/04-repair-after-damage.stdout.json" '"status": "completed"' "repair command did not complete"
|
||||
require_contains "${REPORT_DIR}/04-repair-after-damage.stderr.log" "下载进度:总体" "repair did not emit total download progress"
|
||||
require_contains "${REPORT_DIR}/04-repair-after-damage.stderr.log" "单文件" "repair did not emit single-file download progress"
|
||||
require_contains "${REPORT_DIR}/04-repair-after-damage.stderr.log" "校验结果:" "repair did not emit verification summary"
|
||||
|
||||
run_step "05-verify-after-repair" \
|
||||
"${BAT_BIN}" \
|
||||
verify \
|
||||
--auto-discover \
|
||||
--output "${OUTPUT_DIR}" \
|
||||
--state-dir "${STATE_DIR}" \
|
||||
--json \
|
||||
--no-progress
|
||||
|
||||
require_contains "${REPORT_DIR}/05-verify-after-repair.stdout.json" '"healthy": true' "verify after repair did not report healthy=true"
|
||||
|
||||
file_count="$(find "${current_dir}" -type f | wc -l | tr -d ' ')"
|
||||
current_size="$(du -sh "${current_dir}" | awk '{print $1}')"
|
||||
report_file="${REPORT_DIR}/SMOKE_REPORT.md"
|
||||
|
||||
cat >"${report_file}" <<EOF
|
||||
# Official Full Pull Smoke Report
|
||||
|
||||
- generated_at_utc: ${timestamp}
|
||||
- repo: ${REPO_ROOT}
|
||||
- bat: ${BAT_BIN}
|
||||
- output: ${OUTPUT_DIR}
|
||||
- state_dir: ${STATE_DIR}
|
||||
- current_release: ${current_dir}
|
||||
- current_release_size: ${current_size}
|
||||
- current_release_file_count: ${file_count}
|
||||
- damaged_file: ${damage_file}
|
||||
|
||||
## Steps
|
||||
|
||||
1. dry-run with full URL plan: 01-dry-run-plan
|
||||
2. first full pull: 02-first-full-pull
|
||||
3. second up-to-date check: 03-second-up-to-date
|
||||
4. intentional local file damage and repair: 04-repair-after-damage
|
||||
5. verify after repair: 05-verify-after-repair
|
||||
|
||||
Each step has:
|
||||
|
||||
- <step>.command.txt
|
||||
- <step>.stdout.json
|
||||
- <step>.stderr.log
|
||||
|
||||
The large resource output is intentionally outside Git and remains under:
|
||||
|
||||
\`${OUTPUT_DIR}\`
|
||||
EOF
|
||||
|
||||
printf '\nSmoke report: %s\n' "${report_file}"
|
||||
printf 'Resource output: %s\n' "${OUTPUT_DIR}"
|
||||
Reference in New Issue
Block a user