refactor: module-based strategy architecture

- Add sync_from_upstream.sh: clones Flowseal repo, merges lists/hosts, auto-detects new .bat strategies
- Add strategies/*.conf config files (extracted from .bat)
- Rewrite run_strategy.sh: loads .conf, substitutes %BIN%/%LISTS%/%GAME_*% using sed (no envsubst)
- Rewrite update.sh: pipeline is now sync→rebuild→test→install
- Add ensure_wrappers.sh: auto-creates general_*.sh wrappers from .conf
- Fix all 'local outside function' runtime bugs
- install.sh now calls sync_from_upstream.sh first
This commit is contained in:
2026-05-10 19:46:14 +04:00
parent b75d75931d
commit 7aeb16856d
6 changed files with 477 additions and 535 deletions
+128 -208
View File
@@ -1,15 +1,12 @@
#!/bin/bash
# update.sh - Auto-update, auto-build, auto-test, auto-install pipeline
# Full automation: download updates → rebuild nfqws → test strategies → install working one
# update.sh - Full auto-pipeline: sync upstream → update → build → test → install
# Version: 2.0
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/lib/functions.sh"
GITHUB_REPO="Flowseal/zapret-discord-youtube"
GITHUB_RAW="https://raw.githubusercontent.com/$GITHUB_REPO/main"
GITHUB_RELEASES="https://github.com/$GITHUB_REPO/releases"
LOCAL_VERSION_FILE="$SCRIPT_DIR/.service/version.txt"
UPDATE_LOG="$SCRIPT_DIR/.service/update.log"
@@ -19,100 +16,51 @@ log_msg() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$UPDATE_LOG"
}
# Check remote version
check_remote_version() {
local remote_ver
remote_ver=$(curl -sfL "$GITHUB_RAW/.service/version.txt" 2>/dev/null | head -n1 | tr -d '[:space:]')
if [[ -z "$remote_ver" ]]; then
log_msg "ERROR: Failed to fetch remote version"
# Check if nfqws binary is healthy
verify_nfqws() {
local bin="$BIN_DIR/nfqws"
if [[ ! -x "$bin" ]]; then
log_msg "[FAIL] nfqws binary missing or not executable: $bin"
return 1
fi
echo "$remote_ver"
}
# Update lists and scripts (non-destructive)
update_files() {
log_msg "[*] Updating lists and scripts from GitHub..."
for file in list-general.txt list-google.txt list-exclude.txt ipset-exclude.txt; do
local url="$GITHUB_RAW/lists/$file"
local dest="$SCRIPT_DIR/lists/$file"
if curl -sfL "$url" -o "$dest.tmp" 2>/dev/null; then
mv "$dest.tmp" "$dest"
log_msg " [OK] Updated lists/$file"
else
log_msg " [SKIP] lists/$file (unavailable)"
fi
done
if [[ -f "$SCRIPT_DIR/.service/ipset_filter_loaded" ]] || [[ -f "$SCRIPT_DIR/.service/ipset_filter_any" ]]; then
local url="$GITHUB_RAW/.service/ipset-all.txt"
local dest="$SCRIPT_DIR/lists/ipset-all.txt"
if curl -sfL "$url" -o "$dest.tmp" 2>/dev/null; then
mv "$dest.tmp" "$dest"
log_msg " [OK] Updated lists/ipset-all.txt"
fi
fi
for file in lib/functions.sh utils/test_zapret.sh utils/targets.txt; do
local url="$GITHUB_RAW/$file"
local dest="$SCRIPT_DIR/$file"
if curl -sfL "$url" -o "$dest.tmp" 2>/dev/null; then
mv "$dest.tmp" "$dest"
log_msg " [OK] Updated $file"
fi
done
log_msg "[*] File update complete."
}
# Rebuild nfqws with verification
rebuild_nfqws() {
log_msg "[*] Rebuilding nfqws..."
if ! bash "$SCRIPT_DIR/install_nfqws.sh"; then
log_msg "[FAIL] nfqws build script failed."
if ! "$bin" --help >/dev/null 2>&1; then
log_msg "[FAIL] nfqws binary does not run (segfault?)"
return 1
fi
# Verify binary exists
if [[ ! -x "$BIN_DIR/nfqws" ]]; then
log_msg "[FAIL] nfqws binary not found after build."
return 1
fi
# Verify binary can start (quick help check)
if ! "$BIN_DIR/nfqws" --help >/dev/null 2>&1; then
log_msg "[FAIL] nfqws binary built but does not execute."
return 1
fi
log_msg "[OK] nfqws rebuilt and verified."
log_msg "[OK] nfqws binary verified."
return 0
}
# Run autotest to find working strategy and install it
run_autotest_pipeline() {
log_msg "[*] Running auto-test pipeline to find working strategy..."
# Rebuild nfqws
rebuild_nfqws() {
log_msg "[*] Rebuilding nfqws..."
if bash "$SCRIPT_DIR/install_nfqws.sh"; then
verify_nfqws
else
log_msg "[FAIL] Build script failed."
return 1
fi
}
# Stop any existing service first
# Run autotest pipeline
run_pipeline_autotest() {
log_msg "[*] Running auto-test pipeline..."
if command -v systemctl >/dev/null 2>&1; then
systemctl stop zapret.service 2>/dev/null || true
fi
cleanup_firewall >/dev/null 2>&1 || true
sleep 2
# Run autotest in auto mode
if bash "$SCRIPT_DIR/autotest.sh" --auto; then
log_msg "[OK] Auto-test found working strategy and installed it."
bash "$SCRIPT_DIR/autotest.sh" --auto && {
log_msg "[OK] Auto-test: working strategy found and installed."
return 0
else
log_msg "[FAIL] Auto-test did not find any working strategy."
} || {
log_msg "[WARN] Auto-test: no working strategy found."
return 1
fi
}
}
# Restart service if installed
# Restart systemd service
restart_service() {
if command -v systemctl >/dev/null 2>&1; then
if systemctl is-enabled zapret.service >/dev/null 2>&1; then
@@ -123,120 +71,111 @@ restart_service() {
fi
}
# Full pipeline: update → build → test → install
# Step 0: Sync upstream
do_sync() {
log_msg "[*] Step 0: Syncing upstream strategies, lists, hosts..."
bash "$SCRIPT_DIR/sync_from_upstream.sh" 2>&1 | tee -a "$UPDATE_LOG"
local sync_status=${PIPESTATUS[0]}
case "$sync_status" in
0) log_msg "[OK] Sync complete, no rebuild needed."
SYNC_REBUILD=0
;;
2) log_msg "[OK] Sync complete, REBUILD FLAGGED (new version or lists changed)."
SYNC_REBUILD=1
;;
*) log_msg "[WARN] Sync encountered errors (exit $sync_status). Continue anyway."
SYNC_REBUILD=0
;;
esac
}
# Step 1-4: Main pipeline
full_pipeline() {
local mode="${1:-interactive}"
local force="${2:-no}"
if [[ "$mode" == "interactive" ]]; then
[[ "$mode" == "interactive" ]] && {
echo ""
echo "=============================================="
echo " Zapret Full Auto Pipeline"
echo " Zapret Full Auto Pipeline v2.0"
echo " Current: $LOCAL_VERSION"
echo "=============================================="
echo ""
fi
# Step 1: Check remote version
log_msg "[*] Step 1/4: Checking for updates..."
local remote_version
remote_version=$(check_remote_version) || {
log_msg "Could not check remote version."
if [[ "$mode" == "interactive" ]]; then
read -rp "Continue with local rebuild anyway? [y/N]: " ans
[[ "$ans" == [yY]* ]] || exit 1
else
exit 1
fi
remote_version="$LOCAL_VERSION"
}
if [[ "$mode" == "interactive" ]]; then
echo "Remote version: $remote_version"
echo "Local version: $LOCAL_VERSION"
echo ""
# Step 0: Always sync upstream first
do_sync
# Determine if we need to rebuild
local need_rebuild=0
if [[ "$force" == "yes" ]]; then
log_msg "[*] Force rebuild requested."
need_rebuild=1
elif [[ "${SYNC_REBUILD:-0}" -eq 1 ]]; then
log_msg "[*] Rebuild flagged by sync."
need_rebuild=1
fi
# Decide whether to update
local do_update=0
if [[ "$remote_version" != "$LOCAL_VERSION" ]]; then
do_update=1
log_msg "New version available: $remote_version"
elif [[ "$force" == "yes" ]]; then
log_msg "Force rebuild requested."
do_update=1
if [[ "$need_rebuild" -eq 0 ]] && [[ "$mode" == "interactive" ]]; then
read -rp "No upstream changes detected. Force rebuild + re-test? [y/N]: " ans
[[ "$ans" == [yY]* ]] && need_rebuild=1
fi
if [[ "$do_update" -eq 0 ]]; then
log_msg "No update needed."
if [[ "$mode" == "interactive" ]]; then
read -rp "Rebuild nfqws and re-test anyway? [y/N]: " ans
[[ "$ans" == [yY]* ]] || {
echo "[*] Nothing to do. Exiting."
exit 0
}
else
log_msg "Exiting (no update, no force)."
exit 0
fi
fi
# Step 2: Update files
if [[ "$remote_version" != "$LOCAL_VERSION" ]]; then
log_msg "[*] Step 2/4: Updating files from GitHub..."
update_files
echo "LOCAL_VERSION=\"$remote_version-linux\"" > "$LOCAL_VERSION_FILE"
else
log_msg "[*] Step 2/4: Skipping file update (same version, force mode)."
fi
# Step 3: Rebuild nfqws with verification
log_msg "[*] Step 3/4: Rebuilding nfqws..."
if ! rebuild_nfqws; then
log_msg "[CRITICAL] Build failed. Aborting."
if [[ "$mode" == "interactive" ]]; then
read -rp "Press Enter to exit..."
fi
exit 1
fi
# Step 4: Auto-test and install
log_msg "[*] Step 4/4: Auto-testing strategies..."
if run_autotest_pipeline; then
log_msg "[SUCCESS] Pipeline complete: updated, built, tested, installed."
else
log_msg "[WARNING] Build OK, but no working strategy found."
log_msg " You may need to adjust parameters manually."
fi
if [[ "$mode" == "interactive" ]]; then
echo ""
print_green "=============================================="
print_green " Full Pipeline Complete!"
print_green " Version: $(cat "$LOCAL_VERSION_FILE" 2>/dev/null || echo $LOCAL_VERSION)"
print_green "=============================================="
echo ""
systemctl status zapret --no-pager 2>/dev/null || true
fi
}
# Auto mode (for cron/systemd timers)
auto_mode() {
log_msg "Auto-update started..."
local remote_version
remote_version=$(check_remote_version) || {
log_msg "Update check failed. Exiting."
exit 1
}
if [[ "$remote_version" == "$LOCAL_VERSION" ]]; then
log_msg "No update needed."
if [[ "$need_rebuild" -eq 0 ]]; then
log_msg "[*] No rebuild needed. Exiting."
exit 0
fi
log_msg "New version found: $remote_version"
full_pipeline auto no
# Step 1: Update version marker
log_msg "[*] Step 1: Updating version marker..."
local upstream_ver
upstream_ver=$(cat "$LOCAL_VERSION_FILE" 2>/dev/null || echo "$LOCAL_VERSION")
echo "$upstream_ver" > "$LOCAL_VERSION_FILE"
# Step 2: Rebuild nfqws
log_msg "[*] Step 2: Rebuilding nfqws..."
if ! rebuild_nfqws; then
log_msg "[CRITICAL] Build failed. Aborting."
exit 1
fi
# Step 3: Auto-test
log_msg "[*] Step 3: Auto-testing strategies..."
run_pipeline_autotest || true
# Step 4: Restart
log_msg "[*] Step 4: Restarting service..."
restart_service
log_msg "[SUCCESS] Full pipeline complete."
[[ "$mode" == "interactive" ]] && {
echo ""
print_green "=============================================="
print_green " Pipeline Complete!"
print_green " Version: $(cat "$LOCAL_VERSION_FILE" 2>/dev/null || echo "$LOCAL_VERSION")"
print_green "=============================================="
systemctl status zapret --no-pager 2>/dev/null || true
}
}
# Non-interactive cron mode
auto_mode() {
log_msg "Auto-update started..."
log_msg "[*] Syncing upstream..."
bash "$SCRIPT_DIR/sync_from_upstream.sh" --auto 2>&1 | tee -a "$UPDATE_LOG"
local sync_status=${PIPESTATUS[0]}
if [[ "$sync_status" -ne 2 ]]; then
log_msg "No update needed or sync failed. Exiting."
exit 0
fi
log_msg "Update detected, running full pipeline..."
rebuild_nfqws || { log_msg "[FAIL] Rebuild failed."; exit 1; }
run_pipeline_autotest || true
restart_service
log_msg "Auto-update finished."
}
@@ -246,48 +185,29 @@ FORCE="no"
while [[ $# -gt 0 ]]; do
case "$1" in
--auto|-a)
MODE="auto"
shift
;;
--force|-f)
FORCE="yes"
shift
;;
--full-auto)
# Fully non-interactive: even if same version, rebuild and re-test
MODE="auto"
FORCE="yes"
shift
;;
--auto|-a) MODE="auto"; shift ;;
--force|-f) FORCE="yes"; shift ;;
--full-auto) MODE="auto"; FORCE="yes"; shift ;;
--help|-h)
cat <<EOF
Usage: $0 [OPTIONS]
Usage: sudo ./update.sh [OPTIONS]
Zapret Update Pipeline: checks version → updates files → rebuilds nfqws → auto-tests → installs.
Full pipeline: sync upstream lists/hosts/version → rebuild nfqws → test → install.
Options:
--auto, -a Non-interactive mode (for cron/systemd timers)
--auto, -a Non-interactive (for cron/systemd timers)
--force, -f Force rebuild even if versions match
--full-auto Same as --auto --force (rebuild + re-test always)
--full-auto Same as --auto --force (always rebuild + re-test)
--help, -h Show this help
Interactive mode (default):
sudo ./update.sh
Full automation (no questions, always rebuilds and re-tests):
sudo ./update.sh --full-auto
For systemd timer (daily auto-update):
sudo ./update.sh --auto
Examples:
sudo ./update.sh # interactive prompt
sudo ./update.sh --full-auto # headless, always rebuild
sudo ./update.sh --auto # headless, only if upstream changed
EOF
exit 0
;;
*)
echo "Unknown option: $1"
echo "Use --help for usage."
exit 1
;;
*) echo "Unknown: $1"; exit 1 ;;
esac
done