Rebrand project as Forkop
This commit is contained in:
@@ -2,12 +2,12 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_BIN="$ROOT_DIR/podkop/files/usr/bin/podkop"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
CLI_UC="$PODKOP_BIN"
|
||||
BYEDPI_RUNTIME_SH="$PODKOP_LIB/byedpi.sh"
|
||||
LIFECYCLE_UC="$PODKOP_LIB/service/lifecycle.uc"
|
||||
BYEDPI_RUNTIME_UC="$PODKOP_LIB/providers/byedpi/runtime.uc"
|
||||
FORKOP_BIN="$ROOT_DIR/forkop/files/usr/bin/forkop"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
CLI_UC="$FORKOP_BIN"
|
||||
BYEDPI_RUNTIME_SH="$FORKOP_LIB/byedpi.sh"
|
||||
LIFECYCLE_UC="$FORKOP_LIB/service/lifecycle.uc"
|
||||
BYEDPI_RUNTIME_UC="$FORKOP_LIB/providers/byedpi/runtime.uc"
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
@@ -17,15 +17,15 @@ fail() {
|
||||
[ ! -e "$BYEDPI_RUNTIME_SH" ] ||
|
||||
fail "byedpi.sh shell owner must be removed"
|
||||
|
||||
grep -Fq '#!/usr/bin/ucode' "$PODKOP_BIN" ||
|
||||
fail "podkop entrypoint must be a direct ucode executable"
|
||||
grep -Fq '#!/usr/bin/ucode' "$FORKOP_BIN" ||
|
||||
fail "forkop entrypoint must be a direct ucode executable"
|
||||
grep -Fq 'service/lifecycle.uc' "$CLI_UC" ||
|
||||
fail "service/cli.uc must dispatch lifecycle orchestration through service/lifecycle.uc"
|
||||
grep -Fq 'providers/byedpi/runtime.uc' "$LIFECYCLE_UC" ||
|
||||
fail "service/lifecycle.uc must call providers/byedpi/runtime.uc for ByeDPI runtime operations"
|
||||
|
||||
if grep -R -n -E 'start_byedpi_runtime|stop_byedpi_runtime|get_byedpi_status_json|check_byedpi_runtime_json|is_byedpi_installed|get_byedpi_package_version|get_byedpi_rule_|run_byedpi_supervisor' \
|
||||
"$PODKOP_BIN" "$PODKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
|
||||
"$FORKOP_BIN" "$FORKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
|
||||
fail "ByeDPI runtime shell symbols must not remain"
|
||||
fi
|
||||
if grep -n -E 'require\("uci"\)\.cursor|uci -q|"uci", "-q"' "$BYEDPI_RUNTIME_UC" >/dev/null 2>&1; then
|
||||
@@ -48,7 +48,7 @@ grep -Fq 'mode == "check"' "$BYEDPI_RUNTIME_UC" ||
|
||||
fail "providers/byedpi/runtime.uc must own ByeDPI check"
|
||||
|
||||
BYEDPI_BIN="$ROOT_DIR/tests/missing-ciadpi" \
|
||||
ucode -L "$PODKOP_LIB" "$BYEDPI_RUNTIME_UC" check |
|
||||
ucode -L "$FORKOP_LIB" "$BYEDPI_RUNTIME_UC" check |
|
||||
node -e '
|
||||
const fs = require("fs");
|
||||
const value = JSON.parse(fs.readFileSync(0, "utf8"));
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
VALIDATOR="$ROOT_DIR/podkop/files/usr/lib/providers/byedpi/validator.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
VALIDATOR="$ROOT_DIR/forkop/files/usr/lib/providers/byedpi/validator.uc"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -73,6 +73,6 @@ if (validator.strategy_or_default("", "--default\t1") != "--default 1")
|
||||
exit(1);
|
||||
UCODE
|
||||
|
||||
ucode -L "$PODKOP_LIB" "$WORK_DIR/require-byedpi-validator.uc"
|
||||
ucode -L "$FORKOP_LIB" "$WORK_DIR/require-byedpi-validator.uc"
|
||||
|
||||
printf 'ByeDPI validator checks passed\n'
|
||||
|
||||
+24
-24
@@ -2,8 +2,8 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_BIN="$ROOT_DIR/podkop/files/usr/bin/podkop"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
FORKOP_BIN="$ROOT_DIR/forkop/files/usr/bin/forkop"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -16,23 +16,23 @@ fail() {
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ ! -e "$PODKOP_LIB/service/cli.uc" ] ||
|
||||
[ ! -e "$FORKOP_LIB/service/cli.uc" ] ||
|
||||
fail "service/cli.uc must be removed after /usr/bin ucode entrypoint takeover"
|
||||
grep -Fq '#!/usr/bin/ucode' "$PODKOP_BIN" ||
|
||||
fail "podkop entrypoint must be a direct ucode executable"
|
||||
if grep -Fq '#!/bin/sh' "$PODKOP_BIN" ||
|
||||
grep -Fq '#!/bin/ash' "$PODKOP_BIN" ||
|
||||
grep -Fq 'exec ucode' "$PODKOP_BIN"; then
|
||||
fail "podkop entrypoint must not keep a shell loader"
|
||||
grep -Fq '#!/usr/bin/ucode' "$FORKOP_BIN" ||
|
||||
fail "forkop entrypoint must be a direct ucode executable"
|
||||
if grep -Fq '#!/bin/sh' "$FORKOP_BIN" ||
|
||||
grep -Fq '#!/bin/ash' "$FORKOP_BIN" ||
|
||||
grep -Fq 'exec ucode' "$FORKOP_BIN"; then
|
||||
fail "forkop entrypoint must not keep a shell loader"
|
||||
fi
|
||||
if grep -Fq 'PODKOP_COMMAND' "$PODKOP_BIN" ||
|
||||
grep -Fq 'run_module()' "$PODKOP_BIN"; then
|
||||
fail "podkop entrypoint must not keep legacy shell routing symbols"
|
||||
if grep -Fq 'FORKOP_COMMAND' "$FORKOP_BIN" ||
|
||||
grep -Fq 'run_module()' "$FORKOP_BIN"; then
|
||||
fail "forkop entrypoint must not keep legacy shell routing symbols"
|
||||
fi
|
||||
grep -Fq 'function command_spec(command)' "$PODKOP_BIN" ||
|
||||
fail "podkop ucode entrypoint must own command routing"
|
||||
grep -Fq 'function show_help()' "$PODKOP_BIN" ||
|
||||
fail "podkop ucode entrypoint must own help text"
|
||||
grep -Fq 'function command_spec(command)' "$FORKOP_BIN" ||
|
||||
fail "forkop ucode entrypoint must own command routing"
|
||||
grep -Fq 'function show_help()' "$FORKOP_BIN" ||
|
||||
fail "forkop ucode entrypoint must own help text"
|
||||
|
||||
fake_lib="$WORK_DIR/lib"
|
||||
mkdir -p "$fake_lib/service" "$fake_lib/diagnostics" "$fake_lib/components" "$fake_lib/dns"
|
||||
@@ -55,35 +55,35 @@ UCODE
|
||||
cat >"$fake_lib/dns/apply.uc" <<'UCODE'
|
||||
#!/usr/bin/env ucode
|
||||
let fs = require("fs");
|
||||
let marker = getenv("PODKOP_TEST_DNS_RESTORE_MARKER");
|
||||
let marker = getenv("FORKOP_TEST_DNS_RESTORE_MARKER");
|
||||
if (marker != null && marker != "")
|
||||
fs.writefile(marker, ARGV[0] || "");
|
||||
UCODE
|
||||
|
||||
show_version_out="$(PODKOP_LIB="$fake_lib" ucode "$PODKOP_BIN" show_version)"
|
||||
show_version_out="$(FORKOP_LIB="$fake_lib" ucode "$FORKOP_BIN" show_version)"
|
||||
[ "$show_version_out" = $'diagnostics\tshow-version\t\t\t' ] ||
|
||||
fail "show_version must dispatch through diagnostics/runtime.uc"
|
||||
|
||||
subscription_out="$(PODKOP_LIB="$fake_lib" ucode "$PODKOP_BIN" subscription_update proxy 2)"
|
||||
subscription_out="$(FORKOP_LIB="$fake_lib" ucode "$FORKOP_BIN" subscription_update proxy 2)"
|
||||
[ "$subscription_out" = $'updates\tsubscription-update\tproxy\t2' ] ||
|
||||
fail "subscription_update must dispatch through components/updates.uc with arguments"
|
||||
|
||||
component_out="$(PODKOP_LIB="$fake_lib" ucode "$PODKOP_BIN" component_action sing_box update)"
|
||||
component_out="$(FORKOP_LIB="$fake_lib" ucode "$FORKOP_BIN" component_action sing_box update)"
|
||||
[ "$component_out" = $'action\tcomponent-action\tsing_box\tupdate' ] ||
|
||||
fail "component_action must dispatch through components/action.uc with arguments"
|
||||
|
||||
component_check_cache_out="$(PODKOP_LIB="$fake_lib" ucode "$PODKOP_BIN" component_update_check_cache)"
|
||||
component_check_cache_out="$(FORKOP_LIB="$fake_lib" ucode "$FORKOP_BIN" component_update_check_cache)"
|
||||
[ "$component_check_cache_out" = $'updates\tcomponent-update-check-cache\t\t' ] ||
|
||||
fail "component_update_check_cache must dispatch through components/updates.uc"
|
||||
|
||||
component_updates_out="$(PODKOP_LIB="$fake_lib" ucode "$PODKOP_BIN" component_updates_if_due)"
|
||||
component_updates_out="$(FORKOP_LIB="$fake_lib" ucode "$FORKOP_BIN" component_updates_if_due)"
|
||||
[ "$component_updates_out" = $'updates\tcomponent-updates-if-due\t\t' ] ||
|
||||
fail "component_updates_if_due must dispatch through components/updates.uc"
|
||||
|
||||
rm -f "$fake_lib/service/lifecycle.uc"
|
||||
set +e
|
||||
PODKOP_TEST_DNS_RESTORE_MARKER="$WORK_DIR/dns-restore.marker" PODKOP_LIB="$fake_lib" \
|
||||
ucode "$PODKOP_BIN" start >/dev/null 2>"$WORK_DIR/missing.err"
|
||||
FORKOP_TEST_DNS_RESTORE_MARKER="$WORK_DIR/dns-restore.marker" FORKOP_LIB="$fake_lib" \
|
||||
ucode "$FORKOP_BIN" start >/dev/null 2>"$WORK_DIR/missing.err"
|
||||
status="$?"
|
||||
set -e
|
||||
[ "$status" -ne 0 ] ||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
UPDATES_UC="$PODKOP_LIB/components/updates.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
UPDATES_UC="$FORKOP_LIB/components/updates.uc"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -59,7 +59,7 @@ UCODE
|
||||
|
||||
cat >"$fake_lib/components/action.uc" <<'UCODE'
|
||||
#!/usr/bin/env ucode
|
||||
let component = ARGV[1] || "podkop";
|
||||
let component = ARGV[1] || "forkop";
|
||||
let action = ARGV[2] || "check_update";
|
||||
print(sprintf("%J\n", {
|
||||
success: true,
|
||||
@@ -89,11 +89,11 @@ UCODE
|
||||
updates_ucode() {
|
||||
TEST_COMPONENT_UPDATE_CHECK_ENABLED="${TEST_COMPONENT_UPDATE_CHECK_ENABLED:-1}" \
|
||||
TEST_COMPONENT_UPDATE_CHECK_INTERVAL="${TEST_COMPONENT_UPDATE_CHECK_INTERVAL:-1d}" \
|
||||
PODKOP_LIB="$fake_lib" \
|
||||
PODKOP_COMPONENT_UPDATE_CHECK_CACHE_DIR="$cache_dir" \
|
||||
PODKOP_COMPONENT_UPDATE_CHECK_STATE_FILE="$timestamp_file" \
|
||||
FORKOP_LIB="$fake_lib" \
|
||||
FORKOP_COMPONENT_UPDATE_CHECK_CACHE_DIR="$cache_dir" \
|
||||
FORKOP_COMPONENT_UPDATE_CHECK_STATE_FILE="$timestamp_file" \
|
||||
UPDATES_JOB_DIR="$job_dir" \
|
||||
ucode -L "$fake_lib" -L "$PODKOP_LIB" "$UPDATES_UC" "$@"
|
||||
ucode -L "$fake_lib" -L "$FORKOP_LIB" "$UPDATES_UC" "$@"
|
||||
}
|
||||
|
||||
updates_ucode component-action-worker "$state_file" "$output_file" zapret check_update
|
||||
@@ -117,8 +117,8 @@ TEST_COMPONENT_UPDATE_CHECK_ENABLED=0 updates_ucode \
|
||||
|
||||
rm -f "$state_file" "$timestamp_file"
|
||||
updates_ucode component-updates-if-due
|
||||
[ -s "$cache_dir/podkop.json" ] ||
|
||||
fail "automatic checks must cache the Podkop Plus result"
|
||||
[ -s "$cache_dir/forkop.json" ] ||
|
||||
fail "automatic checks must cache the Forkop result"
|
||||
[ -s "$timestamp_file" ] ||
|
||||
fail "automatic checks must record their last run"
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
PODKOP_BIN="$ROOT_DIR/podkop/files/usr/bin/podkop"
|
||||
CLI_UC="$PODKOP_BIN"
|
||||
UPDATER="$ROOT_DIR/podkop/files/usr/lib/components/updater.uc"
|
||||
UPDATES_UC="$ROOT_DIR/podkop/files/usr/lib/components/updates.uc"
|
||||
ACTION_UC="$ROOT_DIR/podkop/files/usr/lib/components/action.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
FORKOP_BIN="$ROOT_DIR/forkop/files/usr/bin/forkop"
|
||||
CLI_UC="$FORKOP_BIN"
|
||||
UPDATER="$ROOT_DIR/forkop/files/usr/lib/components/updater.uc"
|
||||
UPDATES_UC="$ROOT_DIR/forkop/files/usr/lib/components/updates.uc"
|
||||
ACTION_UC="$ROOT_DIR/forkop/files/usr/lib/components/action.uc"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -70,7 +70,7 @@ if ucode "$UPDATER" updates-check-result-row component 1.0 2.0 invalid >/dev/nul
|
||||
fail "invalid check result status should be rejected"
|
||||
fi
|
||||
|
||||
[ ! -e "$ROOT_DIR/podkop/files/usr/lib/updater.sh" ] ||
|
||||
[ ! -e "$ROOT_DIR/forkop/files/usr/lib/updater.sh" ] ||
|
||||
fail "updater.sh shell owner must be removed"
|
||||
grep -Fq 'component_action: [ "components/action.uc", "component-action", 2 ]' "$CLI_UC" ||
|
||||
fail "service/cli.uc must dispatch direct component_action through components/action.uc"
|
||||
@@ -97,15 +97,15 @@ grep -Fq 'require("core.uci")' "$ACTION_UC" ||
|
||||
if grep -n -E 'require\("uci"\)\.cursor|uci -q|uci", "-q"|command_exists\("uci"\)' "$ACTION_UC" >/dev/null 2>&1; then
|
||||
fail "components/action.uc must not own direct UCI cursor or CLI calls"
|
||||
fi
|
||||
grep -Fq 'podkop_status_running_with_timeout()' "$ACTION_UC" ||
|
||||
fail "components/action.uc must use bounded Podkop status checks for component actions"
|
||||
grep -Fq 'forkop_status_running_with_timeout()' "$ACTION_UC" ||
|
||||
fail "components/action.uc must use bounded Forkop status checks for component actions"
|
||||
if grep -Fq 'command_success_from_args([ SERVICE_INIT, "status" ])' "$ACTION_UC"; then
|
||||
fail "components/action.uc must not call init.d status without a timeout"
|
||||
fi
|
||||
status_timeout_line="$(grep -n '^function podkop_status_running_with_timeout()' "$ACTION_UC" | cut -d: -f1)"
|
||||
capture_state_line="$(grep -n '^function capture_podkop_running_state()' "$ACTION_UC" | cut -d: -f1)"
|
||||
status_timeout_line="$(grep -n '^function forkop_status_running_with_timeout()' "$ACTION_UC" | cut -d: -f1)"
|
||||
capture_state_line="$(grep -n '^function capture_forkop_running_state()' "$ACTION_UC" | cut -d: -f1)"
|
||||
[ -n "$status_timeout_line" ] && [ -n "$capture_state_line" ] && [ "$status_timeout_line" -lt "$capture_state_line" ] ||
|
||||
fail "components/action.uc must declare bounded status helper before capture_podkop_running_state for OpenWrt ucode"
|
||||
fail "components/action.uc must declare bounded status helper before capture_forkop_running_state for OpenWrt ucode"
|
||||
init_line="$(grep -n '^function init_tmp_dir()' "$ACTION_UC" | cut -d: -f1)"
|
||||
tmp_file_line="$(grep -n '^function make_tmp_file' "$ACTION_UC" | cut -d: -f1)"
|
||||
[ -n "$init_line" ] && [ -n "$tmp_file_line" ] && [ "$init_line" -lt "$tmp_file_line" ] ||
|
||||
@@ -183,10 +183,10 @@ chmod +x "$package_runtime_bin/opkg" "$package_runtime_bin/logger"
|
||||
|
||||
set +e
|
||||
PATH="$package_runtime_bin:$PATH" \
|
||||
PODKOP_LIB="$package_runtime_lib" \
|
||||
PODKOP_RUNTIME_STATE_DIR="$WORK_DIR/package-runtime" \
|
||||
PODKOP_BIN="$WORK_DIR/missing-podkop" \
|
||||
PODKOP_SERVICE_INIT="$WORK_DIR/missing-init" \
|
||||
FORKOP_LIB="$package_runtime_lib" \
|
||||
FORKOP_RUNTIME_STATE_DIR="$WORK_DIR/package-runtime" \
|
||||
FORKOP_BIN="$WORK_DIR/missing-forkop" \
|
||||
FORKOP_SERVICE_INIT="$WORK_DIR/missing-init" \
|
||||
FAKE_OPKG_LOG="$WORK_DIR/opkg.log" \
|
||||
FAKE_OPKG_UPDATED="$WORK_DIR/opkg.updated" \
|
||||
ucode -L "$package_runtime_lib" "$ACTION_UC" component-action sing_box install_stable >/dev/null
|
||||
@@ -210,48 +210,58 @@ NODE
|
||||
|
||||
release_json="$(cat <<'JSON'
|
||||
{
|
||||
"tag_name": "v1.2.3",
|
||||
"tag_name": "1.2.3",
|
||||
"html_url": "https://example.com/release",
|
||||
"assets": [
|
||||
{"name": "podkop-plus_1.2.3_all.ipk", "browser_download_url": "https://example.com/backend.ipk"},
|
||||
{"name": "luci-app-podkop-plus_1.2.3_all.ipk", "browser_download_url": "https://example.com/app.ipk"},
|
||||
{"name": "luci-i18n-podkop-plus-ru_1.2.3_all.ipk", "browser_download_url": "https://example.com/i18n.ipk"}
|
||||
{"name": "forkop_1.2.3.ipk", "browser_download_url": "https://example.com/backend.ipk"},
|
||||
{"name": "luci-app-forkop_1.2.3.ipk", "browser_download_url": "https://example.com/app.ipk"},
|
||||
{"name": "luci-i18n-forkop-ru_1.2.3.ipk", "browser_download_url": "https://example.com/i18n.ipk"}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
)"
|
||||
|
||||
assert_eq "$(printf 'https://example.com/release\tpodkop-plus_1.2.3_all.ipk\thttps://example.com/backend.ipk\tluci-app-podkop-plus_1.2.3_all.ipk\thttps://example.com/app.ipk\tluci-i18n-podkop-plus-ru_1.2.3_all.ipk\thttps://example.com/i18n.ipk')" \
|
||||
"$(printf '%s' "$release_json" | ucode "$UPDATER" podkop-plus-release-plan v1.2.3 ipk 1)" \
|
||||
"podkop release plan with i18n"
|
||||
assert_eq "$(printf 'https://example.com/release\tpodkop-plus_1.2.3_all.ipk\thttps://example.com/backend.ipk\tluci-app-podkop-plus_1.2.3_all.ipk\thttps://example.com/app.ipk\t\t')" \
|
||||
"$(printf '%s' "$release_json" | ucode "$UPDATER" podkop-plus-release-plan v1.2.3 ipk 0)" \
|
||||
"podkop release plan without i18n"
|
||||
if printf '%s' "$release_json" | ucode "$UPDATER" podkop-plus-release-plan v9.9.9 ipk 0 >/dev/null 2>&1; then
|
||||
fail "podkop release plan should reject tag mismatch"
|
||||
assert_eq "$(printf 'https://example.com/release\tforkop_1.2.3.ipk\thttps://example.com/backend.ipk\tluci-app-forkop_1.2.3.ipk\thttps://example.com/app.ipk\tluci-i18n-forkop-ru_1.2.3.ipk\thttps://example.com/i18n.ipk')" \
|
||||
"$(printf '%s' "$release_json" | ucode "$UPDATER" forkop-release-plan 1.2.3 ipk 1)" \
|
||||
"forkop release plan with i18n"
|
||||
assert_eq "$(printf 'https://example.com/release\tforkop_1.2.3.ipk\thttps://example.com/backend.ipk\tluci-app-forkop_1.2.3.ipk\thttps://example.com/app.ipk\t\t')" \
|
||||
"$(printf '%s' "$release_json" | ucode "$UPDATER" forkop-release-plan 1.2.3 ipk 0)" \
|
||||
"forkop release plan without i18n"
|
||||
if printf '%s' "$release_json" | ucode "$UPDATER" forkop-release-plan 9.9.9 ipk 0 >/dev/null 2>&1; then
|
||||
fail "forkop release plan should reject tag mismatch"
|
||||
fi
|
||||
missing_i18n_json="$(cat <<'JSON'
|
||||
{
|
||||
"tag_name": "v1.2.3",
|
||||
"tag_name": "1.2.3",
|
||||
"html_url": "https://example.com/release",
|
||||
"assets": [
|
||||
{"name": "podkop-plus_1.2.3_all.ipk", "browser_download_url": "https://example.com/backend.ipk"},
|
||||
{"name": "luci-app-podkop-plus_1.2.3_all.ipk", "browser_download_url": "https://example.com/app.ipk"}
|
||||
{"name": "forkop_1.2.3.ipk", "browser_download_url": "https://example.com/backend.ipk"},
|
||||
{"name": "luci-app-forkop_1.2.3.ipk", "browser_download_url": "https://example.com/app.ipk"}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
)"
|
||||
if printf '%s' "$missing_i18n_json" | ucode "$UPDATER" podkop-plus-release-plan v1.2.3 ipk 1 >/dev/null 2>&1; then
|
||||
fail "podkop release plan should require i18n asset when requested"
|
||||
if printf '%s' "$missing_i18n_json" | ucode "$UPDATER" forkop-release-plan 1.2.3 ipk 1 >/dev/null 2>&1; then
|
||||
fail "forkop release plan should require i18n asset when requested"
|
||||
fi
|
||||
|
||||
ucode "$UPDATER" forkop-release-version-valid 1.2.3 ||
|
||||
fail "three-part Forkop release version should be valid"
|
||||
for invalid_version in v1.2.3 1.2 1.2.3.4 1.2.3-1 1.2.3-r1; do
|
||||
if ucode "$UPDATER" forkop-release-version-valid "$invalid_version" >/dev/null 2>&1; then
|
||||
fail "invalid Forkop release version was accepted: $invalid_version"
|
||||
fi
|
||||
done
|
||||
assert_eq "-1" "$(ucode "$UPDATER" forkop-release-version-compare 1.2.3 1.2.4)" \
|
||||
"three-part Forkop release version comparison"
|
||||
|
||||
component_actions_dir="$WORK_DIR/component-actions"
|
||||
fake_lib="$WORK_DIR/lib"
|
||||
mkdir -p "$fake_lib/components" "$fake_lib/config" "$fake_lib/core"
|
||||
cp "$UPDATES_UC" "$fake_lib/components/updates.uc"
|
||||
cp "$PODKOP_LIB/config/connections.uc" "$fake_lib/config/connections.uc"
|
||||
cp "$PODKOP_LIB/core/uci.uc" "$fake_lib/core/uci.uc"
|
||||
cp "$PODKOP_LIB/core/common.uc" "$fake_lib/core/common.uc"
|
||||
cp "$FORKOP_LIB/config/connections.uc" "$fake_lib/config/connections.uc"
|
||||
cp "$FORKOP_LIB/core/uci.uc" "$fake_lib/core/uci.uc"
|
||||
cp "$FORKOP_LIB/core/common.uc" "$fake_lib/core/common.uc"
|
||||
cat >"$fake_lib/components/action.uc" <<'UCODE'
|
||||
#!/usr/bin/env ucode
|
||||
if ((ARGV[0] || "") == "component-action") {
|
||||
@@ -261,8 +271,8 @@ if ((ARGV[0] || "") == "component-action") {
|
||||
exit(1);
|
||||
UCODE
|
||||
|
||||
start_json="$(PODKOP_LIB="$fake_lib" UPDATES_JOB_DIR="$component_actions_dir" \
|
||||
ucode -L "$PODKOP_LIB" "$UPDATES_UC" component-action-async sing_box check_update)"
|
||||
start_json="$(FORKOP_LIB="$fake_lib" UPDATES_JOB_DIR="$component_actions_dir" \
|
||||
ucode -L "$FORKOP_LIB" "$UPDATES_UC" component-action-async sing_box check_update)"
|
||||
job_id="$(JSON_VALUE="$start_json" node - <<'NODE'
|
||||
const value = JSON.parse(process.env.JSON_VALUE);
|
||||
if (!value.success || !value.job_id) {
|
||||
@@ -275,8 +285,8 @@ NODE
|
||||
|
||||
status_json=""
|
||||
for _ in 1 2 3 4 5; do
|
||||
status_json="$(PODKOP_LIB="$fake_lib" UPDATES_JOB_DIR="$component_actions_dir" \
|
||||
ucode -L "$PODKOP_LIB" "$UPDATES_UC" component-action-status "$job_id")"
|
||||
status_json="$(FORKOP_LIB="$fake_lib" UPDATES_JOB_DIR="$component_actions_dir" \
|
||||
ucode -L "$FORKOP_LIB" "$UPDATES_UC" component-action-status "$job_id")"
|
||||
JSON_VALUE="$status_json" node - <<'NODE' && break || true
|
||||
const value = JSON.parse(process.env.JSON_VALUE);
|
||||
process.exit(value.running === false ? 0 : 1);
|
||||
@@ -293,8 +303,8 @@ if (value.running !== false || value.success !== true || value.component !== "si
|
||||
}
|
||||
NODE
|
||||
|
||||
start_json="$(PODKOP_LIB="$fake_lib" UPDATES_JOB_DIR="$component_actions_dir" \
|
||||
ucode -L "$PODKOP_LIB" "$UPDATES_UC" component-action-async sing-box check_update)"
|
||||
start_json="$(FORKOP_LIB="$fake_lib" UPDATES_JOB_DIR="$component_actions_dir" \
|
||||
ucode -L "$FORKOP_LIB" "$UPDATES_UC" component-action-async sing-box check_update)"
|
||||
job_id="$(JSON_VALUE="$start_json" node - <<'NODE'
|
||||
const value = JSON.parse(process.env.JSON_VALUE);
|
||||
if (!value.success || !value.job_id) {
|
||||
@@ -307,8 +317,8 @@ NODE
|
||||
|
||||
status_json=""
|
||||
for _ in 1 2 3 4 5; do
|
||||
status_json="$(PODKOP_LIB="$fake_lib" UPDATES_JOB_DIR="$component_actions_dir" \
|
||||
ucode -L "$PODKOP_LIB" "$UPDATES_UC" component-action-status "$job_id")"
|
||||
status_json="$(FORKOP_LIB="$fake_lib" UPDATES_JOB_DIR="$component_actions_dir" \
|
||||
ucode -L "$FORKOP_LIB" "$UPDATES_UC" component-action-status "$job_id")"
|
||||
JSON_VALUE="$status_json" node - <<'NODE' && break || true
|
||||
const value = JSON.parse(process.env.JSON_VALUE);
|
||||
process.exit(value.running === false ? 0 : 1);
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
STABLE_REF="${PODKOP_STABLE_REF:-0.7.19.9}"
|
||||
STABLE_REPO="${PODKOP_STABLE_REPO:-}"
|
||||
STABLE_REF="${FORKOP_STABLE_REF:-0.7.19.9}"
|
||||
STABLE_REPO="${FORKOP_STABLE_REPO:-}"
|
||||
MATRIX_SCRIPT="$ROOT_DIR/tests/helpers/config_contract_matrix.js"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
LEGACY_STEM="$(printf '\160\157\144\153\157\160')"
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$WORK_DIR"
|
||||
@@ -28,8 +29,10 @@ ensure_stable_ref() {
|
||||
|
||||
prepare_stable_repo() {
|
||||
if [ -n "$STABLE_REPO" ]; then
|
||||
[ -r "$STABLE_REPO/podkop/files/etc/config/podkop" ] ||
|
||||
fail "stable repo is missing Podkop config template: $STABLE_REPO"
|
||||
if [ ! -r "$STABLE_REPO/forkop/files/etc/config/forkop" ] &&
|
||||
[ ! -r "$STABLE_REPO/$LEGACY_STEM/files/etc/config/$LEGACY_STEM" ]; then
|
||||
fail "stable repo is missing the expected config template: $STABLE_REPO"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
|
||||
+56
-41
@@ -2,9 +2,11 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
MIGRATION="$PODKOP_LIB/config/migration.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
RUNTIME_MIGRATION="$FORKOP_LIB/config/migration.uc"
|
||||
INSTALLER="$ROOT_DIR/install.sh"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
MIGRATION="$WORK_DIR/config-migration.uc"
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$WORK_DIR"
|
||||
@@ -16,13 +18,26 @@ fail() {
|
||||
exit 1
|
||||
}
|
||||
|
||||
awk '
|
||||
/cat > "\$helper_path" <<'\''FORKOP_CONFIG_MIGRATION_EOF'\''/ { capture = 1; next }
|
||||
capture && /^FORKOP_CONFIG_MIGRATION_EOF$/ { exit }
|
||||
capture { print }
|
||||
' "$INSTALLER" > "$MIGRATION"
|
||||
[ -s "$MIGRATION" ] || fail "failed to extract config migration from install.sh"
|
||||
|
||||
grep -Fq 'Reserved for future Forkop configuration migrations.' "$RUNTIME_MIGRATION" ||
|
||||
fail "runtime config/migration.uc must remain a placeholder"
|
||||
if grep -n -E 'require\("core\.uci"\)|function migrate_|migrate_runtime|migrate-fixture' "$RUNTIME_MIGRATION" >/dev/null 2>&1; then
|
||||
fail "runtime config/migration.uc must not contain migration logic"
|
||||
fi
|
||||
|
||||
if grep -n -E 'require\("uci"\)\.cursor|uci -q|uci", "-q"' "$MIGRATION" >/dev/null 2>&1; then
|
||||
fail "config/migration.uc must use core.uci instead of direct UCI cursor or CLI access"
|
||||
fail "installer config migration must use core.uci instead of direct UCI cursor or CLI access"
|
||||
fi
|
||||
grep -Fq 'require("core.uci")' "$MIGRATION" ||
|
||||
fail "config/migration.uc must import core.uci"
|
||||
fail "installer config migration must import core.uci"
|
||||
|
||||
eval "$(ucode -L "$PODKOP_LIB" "$PODKOP_LIB/core/constants.uc" shell-env)"
|
||||
eval "$(ucode -L "$FORKOP_LIB" "$FORKOP_LIB/core/constants.uc" shell-env)"
|
||||
export ZAPRET_LEGACY_DEFAULT_NFQWS_OPT ZAPRET_DEFAULT_NFQWS_OPT
|
||||
|
||||
node >"$WORK_DIR/fixture.json" <<'NODE'
|
||||
@@ -133,7 +148,7 @@ const fixture = {
|
||||
process.stdout.write(`${JSON.stringify(fixture, null, 2)}\n`);
|
||||
NODE
|
||||
|
||||
PODKOP_LIB="$PODKOP_LIB" ucode -L "$PODKOP_LIB" "$MIGRATION" migrate-fixture "$WORK_DIR/fixture.json" >"$WORK_DIR/output.json"
|
||||
FORKOP_LIB="$FORKOP_LIB" ucode -L "$FORKOP_LIB" "$MIGRATION" migrate-fixture "$WORK_DIR/fixture.json" >"$WORK_DIR/output.json"
|
||||
|
||||
node - "$WORK_DIR/output.json" <<'NODE'
|
||||
const fs = require('fs');
|
||||
@@ -313,65 +328,65 @@ absent(legacyOutbound, 'outbound_detour_enabled', 'legacy-outbound');
|
||||
absent(legacyOutbound, 'outbound_detour_section', 'legacy-outbound');
|
||||
|
||||
assert(out.removed_caches.includes('/tmp/sing-box/subscriptions/legacy-sub.json'), 'subscription runtime cache removal');
|
||||
assert(out.removed_caches.includes('/var/run/podkop-plus/section-cache/legacy-sub.json'), 'section cache removal');
|
||||
assert(out.removed_caches.includes('/var/run/forkop/section-cache/legacy-sub.json'), 'section cache removal');
|
||||
NODE
|
||||
|
||||
cat >"$WORK_DIR/runtime-migrate.state" <<'EOF_UCI'
|
||||
podkop-plus.settings=settings
|
||||
podkop-plus.settings.routing_excluded_ips=192.0.2.0/24
|
||||
podkop-plus.legacy=rule
|
||||
podkop-plus.legacy.enabled=1
|
||||
podkop-plus.legacy.connection_type=proxy
|
||||
podkop-plus.legacy.proxy_config_type=url
|
||||
podkop-plus.legacy.proxy_string=vless://one
|
||||
podkop-plus.old_direct=section
|
||||
podkop-plus.old_direct.enabled=1
|
||||
podkop-plus.old_direct.action=direct
|
||||
forkop.settings=settings
|
||||
forkop.settings.routing_excluded_ips=192.0.2.0/24
|
||||
forkop.legacy=rule
|
||||
forkop.legacy.enabled=1
|
||||
forkop.legacy.connection_type=proxy
|
||||
forkop.legacy.proxy_config_type=url
|
||||
forkop.legacy.proxy_string=vless://one
|
||||
forkop.old_direct=section
|
||||
forkop.old_direct.enabled=1
|
||||
forkop.old_direct.action=direct
|
||||
EOF_UCI
|
||||
mkdir -p "$WORK_DIR/runtime" "$WORK_DIR/persistent-cache"
|
||||
: >"$WORK_DIR/runtime-migrate.log"
|
||||
PODKOP_UCI_STATE_FILE="$WORK_DIR/runtime-migrate.state" \
|
||||
PODKOP_UCI_LOG_FILE="$WORK_DIR/runtime-migrate.log" \
|
||||
PODKOP_CONFIG_NAME="podkop-plus" \
|
||||
FORKOP_UCI_STATE_FILE="$WORK_DIR/runtime-migrate.state" \
|
||||
FORKOP_UCI_LOG_FILE="$WORK_DIR/runtime-migrate.log" \
|
||||
FORKOP_CONFIG_NAME="forkop" \
|
||||
TMP_SUBSCRIPTION_FOLDER="$WORK_DIR/tmp-subscriptions" \
|
||||
PODKOP_RUNTIME_STATE_DIR="$WORK_DIR/runtime" \
|
||||
PODKOP_PERSISTENT_SUBSCRIPTION_CACHE_DIR="$WORK_DIR/persistent-cache" \
|
||||
PODKOP_INTERNAL_CONFIG_TRIGGER_GUARD="$WORK_DIR/internal-config-change" \
|
||||
ucode -L "$PODKOP_LIB" "$MIGRATION" migrate
|
||||
FORKOP_RUNTIME_STATE_DIR="$WORK_DIR/runtime" \
|
||||
FORKOP_PERSISTENT_SUBSCRIPTION_CACHE_DIR="$WORK_DIR/persistent-cache" \
|
||||
FORKOP_INTERNAL_CONFIG_TRIGGER_GUARD="$WORK_DIR/internal-config-change" \
|
||||
ucode -L "$FORKOP_LIB" "$MIGRATION" migrate
|
||||
|
||||
grep -Fxq 'podkop-plus.legacy=section' "$WORK_DIR/runtime-migrate.state" ||
|
||||
grep -Fxq 'forkop.legacy=section' "$WORK_DIR/runtime-migrate.state" ||
|
||||
fail "runtime migration must convert legacy rule type through core.uci"
|
||||
grep -Fxq 'podkop-plus.legacy.action=connection' "$WORK_DIR/runtime-migrate.state" ||
|
||||
grep -Fxq 'forkop.legacy.action=connection' "$WORK_DIR/runtime-migrate.state" ||
|
||||
fail "runtime migration must write migrated action through core.uci"
|
||||
grep -Fxq 'podkop-plus.old_direct.action=bypass' "$WORK_DIR/runtime-migrate.state" ||
|
||||
grep -Fxq 'forkop.old_direct.action=bypass' "$WORK_DIR/runtime-migrate.state" ||
|
||||
fail "runtime migration must convert direct action to bypass through core.uci"
|
||||
grep -Eq '^podkop-plus\\.cfg[0-9a-f]+=$' "$WORK_DIR/runtime-migrate.state" && fail "anonymous fixture section should include a type"
|
||||
grep -Fxq 'podkop-plus.legacy.selector_proxy_links=vless://one' "$WORK_DIR/runtime-migrate.state" ||
|
||||
grep -Eq '^forkop\\.cfg[0-9a-f]+=$' "$WORK_DIR/runtime-migrate.state" && fail "anonymous fixture section should include a type"
|
||||
grep -Fxq 'forkop.legacy.selector_proxy_links=vless://one' "$WORK_DIR/runtime-migrate.state" ||
|
||||
fail "runtime migration must keep connection URLs in the parent section"
|
||||
if grep -Fq '=connection_url' "$WORK_DIR/runtime-migrate.state"; then
|
||||
fail "runtime migration must not create connection_url child sections"
|
||||
fi
|
||||
if grep -Fq 'podkop-plus.legacy.urltest_enabled=' "$WORK_DIR/runtime-migrate.state"; then
|
||||
if grep -Fq 'forkop.legacy.urltest_enabled=' "$WORK_DIR/runtime-migrate.state"; then
|
||||
fail "runtime migration must delete migrated urltest_enabled through core.uci"
|
||||
fi
|
||||
if grep -Fq 'podkop-plus.settings.routing_excluded_ips=' "$WORK_DIR/runtime-migrate.state"; then
|
||||
if grep -Fq 'forkop.settings.routing_excluded_ips=' "$WORK_DIR/runtime-migrate.state"; then
|
||||
fail "runtime migration must delete removed routing_excluded_ips through core.uci"
|
||||
fi
|
||||
if grep -Fq 'podkop-plus.legacy.proxy_string=' "$WORK_DIR/runtime-migrate.state"; then
|
||||
if grep -Fq 'forkop.legacy.proxy_string=' "$WORK_DIR/runtime-migrate.state"; then
|
||||
fail "runtime migration must delete legacy proxy_string through core.uci"
|
||||
fi
|
||||
grep -Fxq 'commit podkop-plus' "$WORK_DIR/runtime-migrate.log" ||
|
||||
grep -Fxq 'commit forkop' "$WORK_DIR/runtime-migrate.log" ||
|
||||
fail "runtime migration must commit through core.uci"
|
||||
|
||||
: >"$WORK_DIR/uci-commit.log"
|
||||
: >"$WORK_DIR/uci-commit.state"
|
||||
PODKOP_UCI_STATE_FILE="$WORK_DIR/uci-commit.state" \
|
||||
PODKOP_UCI_LOG_FILE="$WORK_DIR/uci-commit.log" \
|
||||
PODKOP_CONFIG_NAME="podkop-plus" \
|
||||
PODKOP_INTERNAL_CONFIG_TRIGGER_GUARD="$WORK_DIR/internal-config-change" \
|
||||
ucode -L "$PODKOP_LIB" "$MIGRATION" commit
|
||||
FORKOP_UCI_STATE_FILE="$WORK_DIR/uci-commit.state" \
|
||||
FORKOP_UCI_LOG_FILE="$WORK_DIR/uci-commit.log" \
|
||||
FORKOP_CONFIG_NAME="forkop" \
|
||||
FORKOP_INTERNAL_CONFIG_TRIGGER_GUARD="$WORK_DIR/internal-config-change" \
|
||||
ucode -L "$FORKOP_LIB" "$MIGRATION" commit
|
||||
|
||||
grep -Fxq 'commit podkop-plus' "$WORK_DIR/uci-commit.log" ||
|
||||
fail "commit mode must commit podkop-plus through core.uci"
|
||||
grep -Fxq 'commit forkop' "$WORK_DIR/uci-commit.log" ||
|
||||
fail "commit mode must commit forkop through core.uci"
|
||||
|
||||
printf 'config migration checks passed\n'
|
||||
printf 'installer config migration checks passed\n'
|
||||
|
||||
@@ -2,27 +2,27 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_FILES="$ROOT_DIR/podkop/files"
|
||||
PODKOP_BIN="$PODKOP_FILES/usr/bin/podkop"
|
||||
PODKOP_LIB="$PODKOP_FILES/usr/lib"
|
||||
CLI_UC="$PODKOP_BIN"
|
||||
VALIDATOR="$PODKOP_LIB/config/validator.uc"
|
||||
LIFECYCLE="$PODKOP_LIB/service/lifecycle.uc"
|
||||
FORKOP_FILES="$ROOT_DIR/forkop/files"
|
||||
FORKOP_BIN="$FORKOP_FILES/usr/bin/forkop"
|
||||
FORKOP_LIB="$FORKOP_FILES/usr/lib"
|
||||
CLI_UC="$FORKOP_BIN"
|
||||
VALIDATOR="$FORKOP_LIB/config/validator.uc"
|
||||
LIFECYCLE="$FORKOP_LIB/service/lifecycle.uc"
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ ! -e "$PODKOP_LIB/config_validation.sh" ] ||
|
||||
[ ! -e "$FORKOP_LIB/config_validation.sh" ] ||
|
||||
fail "config_validation.sh shell owner must be removed"
|
||||
|
||||
if grep -R -n "config_validation.sh" "$PODKOP_FILES" >/dev/null 2>&1; then
|
||||
if grep -R -n "config_validation.sh" "$FORKOP_FILES" >/dev/null 2>&1; then
|
||||
fail "runtime files must not reference config_validation.sh"
|
||||
fi
|
||||
|
||||
legacy_symbols='(^|[^A-Za-z0-9_])(config_validate_runtime|check_requirements|commit_podkop_config|mwan3_is_active|get_inline_remote_ruleset_format|detect_inline_ruleset_reference_kind)([^A-Za-z0-9_]|$)'
|
||||
if grep -R -n -E "$legacy_symbols" "$PODKOP_BIN" "$PODKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
|
||||
legacy_symbols='(^|[^A-Za-z0-9_])(config_validate_runtime|check_requirements|commit_forkop_config|mwan3_is_active|get_inline_remote_ruleset_format|detect_inline_ruleset_reference_kind)([^A-Za-z0-9_]|$)'
|
||||
if grep -R -n -E "$legacy_symbols" "$FORKOP_BIN" "$FORKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
|
||||
fail "runtime shell must not keep config_validation.sh symbols"
|
||||
fi
|
||||
|
||||
@@ -35,13 +35,15 @@ grep -Fq 'require("core.uci")' "$VALIDATOR" ||
|
||||
if grep -n -E 'require\("uci"\)\.cursor|uci -q|uci", "-q"|command_output\("uci' "$VALIDATOR" >/dev/null 2>&1; then
|
||||
fail "config validator must not own direct UCI cursor or CLI access"
|
||||
fi
|
||||
grep -Fq '#!/usr/bin/ucode' "$PODKOP_BIN" ||
|
||||
fail "podkop entrypoint must be a direct ucode executable"
|
||||
grep -Fq '#!/usr/bin/ucode' "$FORKOP_BIN" ||
|
||||
fail "forkop entrypoint must be a direct ucode executable"
|
||||
grep -Fq 'service/lifecycle.uc' "$CLI_UC" ||
|
||||
fail "service/cli.uc must dispatch service lifecycle through service/lifecycle.uc"
|
||||
grep -Fq 'MIGRATION_UC' "$LIFECYCLE" &&
|
||||
grep -Fq '"migrate"' "$LIFECYCLE" ||
|
||||
fail "service/lifecycle.uc must run config migration directly through ucode"
|
||||
if grep -n -E 'MIGRATION_UC|config[./]migration|"migrate"' "$LIFECYCLE" >/dev/null 2>&1; then
|
||||
fail "service/lifecycle.uc must not run configuration migration"
|
||||
fi
|
||||
grep -Fq 'mark_internal_config_guard();' "$LIFECYCLE" ||
|
||||
fail "service/lifecycle.uc must preserve the internal config trigger guard"
|
||||
grep -Fq 'VALIDATOR_UC' "$LIFECYCLE" &&
|
||||
grep -Fq '"validate-runtime"' "$LIFECYCLE" ||
|
||||
fail "service/lifecycle.uc must run runtime validation directly through ucode"
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
VALIDATOR="$ROOT_DIR/podkop/files/usr/lib/config/validator.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
VALIDATOR="$ROOT_DIR/forkop/files/usr/lib/config/validator.uc"
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
@@ -20,7 +20,7 @@ assert_rejects() {
|
||||
shift 2
|
||||
local output
|
||||
|
||||
if output="$(rows "$@" | ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-outbound-detours 2>/dev/null)"; then
|
||||
if output="$(rows "$@" | ucode -L "$FORKOP_LIB" "$VALIDATOR" validate-outbound-detours 2>/dev/null)"; then
|
||||
fail "$label should be rejected"
|
||||
fi
|
||||
|
||||
@@ -31,7 +31,7 @@ assert_rejects() {
|
||||
rows \
|
||||
source 1 connection 1 target \
|
||||
target 1 connection 0 '' |
|
||||
ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-outbound-detours
|
||||
ucode -L "$FORKOP_LIB" "$VALIDATOR" validate-outbound-detours
|
||||
|
||||
assert_rejects "unsupported source action" "supported only for Connection rules" \
|
||||
source 1 zapret 1 target \
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
VALIDATOR="$ROOT_DIR/podkop/files/usr/lib/config/validator.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
VALIDATOR="$ROOT_DIR/forkop/files/usr/lib/config/validator.uc"
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
@@ -21,7 +21,7 @@ assert_accepts() {
|
||||
local zapret2="$4"
|
||||
shift 4
|
||||
|
||||
rows "$@" | ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-download-section "$target" "$byedpi" "$zapret" "$zapret2"
|
||||
rows "$@" | ucode -L "$FORKOP_LIB" "$VALIDATOR" validate-download-section "$target" "$byedpi" "$zapret" "$zapret2"
|
||||
}
|
||||
|
||||
assert_rejects() {
|
||||
@@ -34,7 +34,7 @@ assert_rejects() {
|
||||
shift 6
|
||||
local output
|
||||
|
||||
if output="$(rows "$@" | ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-download-section "$target" "$byedpi" "$zapret" "$zapret2" 2>/dev/null)"; then
|
||||
if output="$(rows "$@" | ucode -L "$FORKOP_LIB" "$VALIDATOR" validate-download-section "$target" "$byedpi" "$zapret" "$zapret2" 2>/dev/null)"; then
|
||||
fail "$label should be rejected"
|
||||
fi
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
VALIDATOR="$ROOT_DIR/podkop/files/usr/lib/config/validator.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
VALIDATOR="$ROOT_DIR/forkop/files/usr/lib/config/validator.uc"
|
||||
COMMUNITY_SERVICES="youtube twitter telegram"
|
||||
|
||||
fail() {
|
||||
@@ -15,7 +15,7 @@ assert_accepts() {
|
||||
local label="$1"
|
||||
shift
|
||||
|
||||
ucode -L "$PODKOP_LIB" "$VALIDATOR" "$@" >/dev/null ||
|
||||
ucode -L "$FORKOP_LIB" "$VALIDATOR" "$@" >/dev/null ||
|
||||
fail "$label should be accepted"
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ assert_rejects() {
|
||||
local label="$1"
|
||||
shift
|
||||
|
||||
if ucode -L "$PODKOP_LIB" "$VALIDATOR" "$@" >/dev/null 2>&1; then
|
||||
if ucode -L "$FORKOP_LIB" "$VALIDATOR" "$@" >/dev/null 2>&1; then
|
||||
fail "$label should be rejected"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
VALIDATOR="$PODKOP_LIB/config/validator.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
VALIDATOR="$FORKOP_LIB/config/validator.uc"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -33,7 +33,7 @@ if (input.settings.dns_server === undefined) input.settings.dns_server = ['77.88
|
||||
if (input.settings.bootstrap_dns_server === undefined) input.settings.bootstrap_dns_server = ['77.88.8.8'];
|
||||
fs.writeFileSync(process.argv[3], JSON.stringify(input));
|
||||
JS
|
||||
PODKOP_LIB="$PODKOP_LIB" ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-runtime-fixture "$normalized" "$context"
|
||||
FORKOP_LIB="$FORKOP_LIB" ucode -L "$FORKOP_LIB" "$VALIDATOR" validate-runtime-fixture "$normalized" "$context"
|
||||
}
|
||||
|
||||
assert_rejects() {
|
||||
@@ -354,7 +354,7 @@ cat >"$WORK_DIR/bad-byedpi.json" <<'JSON'
|
||||
]
|
||||
}
|
||||
JSON
|
||||
if output="$(PODKOP_LIB="$PODKOP_LIB" ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-runtime-fixture "$WORK_DIR/bad-byedpi.json" "$provider_context" 2>/dev/null)"; then
|
||||
if output="$(FORKOP_LIB="$FORKOP_LIB" ucode -L "$FORKOP_LIB" "$VALIDATOR" validate-runtime-fixture "$WORK_DIR/bad-byedpi.json" "$provider_context" 2>/dev/null)"; then
|
||||
fail "bad byedpi should be rejected"
|
||||
fi
|
||||
printf '%s\n' "$output" | grep -Fq "ByeDPI listen address and port are assigned" ||
|
||||
@@ -362,10 +362,10 @@ printf '%s\n' "$output" | grep -Fq "ByeDPI listen address and port are assigned"
|
||||
|
||||
runtime_lib="$WORK_DIR/runtime-lib"
|
||||
mkdir -p "$runtime_lib"
|
||||
ln -s "$PODKOP_LIB/core" "$runtime_lib/core"
|
||||
ln -s "$PODKOP_LIB/config" "$runtime_lib/config"
|
||||
ln -s "$PODKOP_LIB/subscription" "$runtime_lib/subscription"
|
||||
ln -s "$PODKOP_LIB/providers" "$runtime_lib/providers"
|
||||
ln -s "$FORKOP_LIB/core" "$runtime_lib/core"
|
||||
ln -s "$FORKOP_LIB/config" "$runtime_lib/config"
|
||||
ln -s "$FORKOP_LIB/subscription" "$runtime_lib/subscription"
|
||||
ln -s "$FORKOP_LIB/providers" "$runtime_lib/providers"
|
||||
touch "$WORK_DIR/ciadpi-provider"
|
||||
cat >"$WORK_DIR/bad-byedpi-runtime-state.json" <<'JSON'
|
||||
{
|
||||
@@ -391,7 +391,7 @@ env \
|
||||
ZAPRET2_QUEUE_RANGE_SIZE="16" \
|
||||
NFT_FAKEIP_MARK="0x00000800" \
|
||||
NFT_OUTBOUND_MARK="0x00000400" \
|
||||
PODKOP_LIB="$runtime_lib" \
|
||||
FORKOP_LIB="$runtime_lib" \
|
||||
ucode -L "$runtime_lib" "$VALIDATOR" validate-runtime-fixture "$WORK_DIR/bad-byedpi-runtime-state.json" "{}"
|
||||
chmod 755 "$WORK_DIR/ciadpi-provider"
|
||||
if output="$(env \
|
||||
@@ -409,7 +409,7 @@ if output="$(env \
|
||||
ZAPRET2_QUEUE_RANGE_SIZE="16" \
|
||||
NFT_FAKEIP_MARK="0x00000800" \
|
||||
NFT_OUTBOUND_MARK="0x00000400" \
|
||||
PODKOP_LIB="$runtime_lib" \
|
||||
FORKOP_LIB="$runtime_lib" \
|
||||
ucode -L "$runtime_lib" "$VALIDATOR" validate-runtime-fixture "$WORK_DIR/bad-byedpi-runtime-state.json" "{}" 2>/dev/null)"; then
|
||||
fail "executable byedpi provider should enable strategy validation"
|
||||
fi
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
GENERATOR="$PODKOP_LIB/singbox/generator.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
GENERATOR="$FORKOP_LIB/singbox/generator.uc"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -49,7 +49,7 @@ cat >"$WORK_DIR/subscriptions/source-subscription-1.json" <<'JSON'
|
||||
"outbounds": [ "nested-entry", "independent" ],
|
||||
"url": "https://www.gstatic.com/generate_204",
|
||||
"interval": "3m",
|
||||
"__podkop_allow_group": true
|
||||
"__forkop_allow_group": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -100,7 +100,7 @@ cat >"$WORK_DIR/fixture.json" <<'JSON'
|
||||
JSON
|
||||
|
||||
TMP_SUBSCRIPTION_FOLDER="$WORK_DIR/subscriptions" \
|
||||
ucode -L "$PODKOP_LIB" "$GENERATOR" generate-config-fixture \
|
||||
ucode -L "$FORKOP_LIB" "$GENERATOR" generate-config-fixture \
|
||||
"$WORK_DIR/fixture.json" "$WORK_DIR/output.json" "127.0.0.1"
|
||||
|
||||
ucode -e '
|
||||
|
||||
+20
-20
@@ -2,14 +2,14 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_BIN="$ROOT_DIR/podkop/files/usr/bin/podkop"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
CLI_UC="$PODKOP_BIN"
|
||||
PODKOP_MAKEFILE="$ROOT_DIR/podkop/Makefile"
|
||||
FORKOP_BIN="$ROOT_DIR/forkop/files/usr/bin/forkop"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
CLI_UC="$FORKOP_BIN"
|
||||
FORKOP_MAKEFILE="$ROOT_DIR/forkop/Makefile"
|
||||
BUILD_SCRIPT="$ROOT_DIR/build.sh"
|
||||
CONSTANTS_SH="$PODKOP_LIB/constants.sh"
|
||||
LIFECYCLE_UC="$PODKOP_LIB/service/lifecycle.uc"
|
||||
CONSTANTS_UC="$PODKOP_LIB/core/constants.uc"
|
||||
CONSTANTS_SH="$FORKOP_LIB/constants.sh"
|
||||
LIFECYCLE_UC="$FORKOP_LIB/service/lifecycle.uc"
|
||||
CONSTANTS_UC="$FORKOP_LIB/core/constants.uc"
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
@@ -19,36 +19,36 @@ fail() {
|
||||
[ ! -e "$CONSTANTS_SH" ] ||
|
||||
fail "constants.sh shell owner must be removed"
|
||||
|
||||
grep -Fq '#!/usr/bin/ucode' "$PODKOP_BIN" ||
|
||||
fail "podkop entrypoint must be a direct ucode executable"
|
||||
grep -Fq '#!/usr/bin/ucode' "$FORKOP_BIN" ||
|
||||
fail "forkop entrypoint must be a direct ucode executable"
|
||||
grep -Fq 'service/lifecycle.uc' "$CLI_UC" ||
|
||||
fail "service/cli.uc must dispatch lifecycle orchestration through service/lifecycle.uc"
|
||||
grep -Fq 'core.constants' "$LIFECYCLE_UC" ||
|
||||
fail "service/lifecycle.uc must load constants from core/constants.uc"
|
||||
|
||||
if grep -R -n -E 'constants\.sh|read_shell_constants|expand_shell_constants|unquote_shell_value' \
|
||||
"$PODKOP_BIN" "$PODKOP_LIB" --include='*.sh' --include='*.uc' >/dev/null 2>&1; then
|
||||
"$FORKOP_BIN" "$FORKOP_LIB" --include='*.sh' --include='*.uc' >/dev/null 2>&1; then
|
||||
fail "shell constants owner or parser references must not remain"
|
||||
fi
|
||||
|
||||
if grep -n 'constants\.sh' "$PODKOP_MAKEFILE" "$BUILD_SCRIPT" >/dev/null 2>&1; then
|
||||
if grep -n 'constants\.sh' "$FORKOP_MAKEFILE" "$BUILD_SCRIPT" >/dev/null 2>&1; then
|
||||
fail "package build must not patch removed constants.sh"
|
||||
fi
|
||||
grep -Fq 'core/constants.uc' "$PODKOP_MAKEFILE" ||
|
||||
fail "podkop/Makefile must patch core/constants.uc"
|
||||
grep -Fq 'core/constants.uc' "$FORKOP_MAKEFILE" ||
|
||||
fail "forkop/Makefile must patch core/constants.uc"
|
||||
grep -Fq 'core/constants.uc' "$BUILD_SCRIPT" ||
|
||||
fail "manual WSL build must patch core/constants.uc"
|
||||
|
||||
config_name="$(ucode -L "$PODKOP_LIB" "$CONSTANTS_UC" get PODKOP_CONFIG_NAME)"
|
||||
[ "$config_name" = "podkop-plus" ] ||
|
||||
fail "core/constants.uc get returned unexpected PODKOP_CONFIG_NAME"
|
||||
config_name="$(ucode -L "$FORKOP_LIB" "$CONSTANTS_UC" get FORKOP_CONFIG_NAME)"
|
||||
[ "$config_name" = "forkop" ] ||
|
||||
fail "core/constants.uc get returned unexpected FORKOP_CONFIG_NAME"
|
||||
|
||||
eval "$(ucode -L "$PODKOP_LIB" "$CONSTANTS_UC" shell-env)"
|
||||
[ "$PODKOP_CONFIG" = "/etc/config/podkop-plus" ] ||
|
||||
fail "core/constants.uc shell-env did not derive PODKOP_CONFIG"
|
||||
eval "$(ucode -L "$FORKOP_LIB" "$CONSTANTS_UC" shell-env)"
|
||||
[ "$FORKOP_CONFIG" = "/etc/config/forkop" ] ||
|
||||
fail "core/constants.uc shell-env did not derive FORKOP_CONFIG"
|
||||
[ "$TMP_RULESET_FOLDER" = "/tmp/sing-box/rulesets" ] ||
|
||||
fail "core/constants.uc shell-env did not derive TMP_RULESET_FOLDER"
|
||||
[ "$BYEDPI_PID_DIR" = "/var/run/podkop-plus/byedpi/pid" ] ||
|
||||
[ "$BYEDPI_PID_DIR" = "/var/run/forkop/byedpi/pid" ] ||
|
||||
fail "core/constants.uc shell-env did not derive BYEDPI_PID_DIR"
|
||||
|
||||
printf 'constants ownership checks passed\n'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
HELPERS_UC="$ROOT_DIR/podkop/files/usr/lib/core/helpers.uc"
|
||||
HELPERS_UC="$ROOT_DIR/forkop/files/usr/lib/core/helpers.uc"
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
UCODE_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
UCODE_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -95,9 +95,9 @@ UCODE
|
||||
|
||||
FAKE_COUNTRY_PAYLOAD="$WORK_DIR/payload.json" \
|
||||
FAKE_COUNTRY_RESOLVE="$WORK_DIR/resolve.txt" \
|
||||
PODKOP_COUNTRY_IS_URL="https://country.invalid/" \
|
||||
FORKOP_COUNTRY_IS_URL="https://country.invalid/" \
|
||||
PATH="$WORK_DIR/bin:$PATH" \
|
||||
ucode -L "$PODKOP_LIB" "$WORK_DIR/runner.uc"
|
||||
ucode -L "$FORKOP_LIB" "$WORK_DIR/runner.uc"
|
||||
|
||||
grep -Fq '8.8.8.8' "$WORK_DIR/payload.json" ||
|
||||
fail "country.is request did not contain the first resolved IP"
|
||||
|
||||
+24
-24
@@ -2,15 +2,15 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
DIAGNOSTICS="$ROOT_DIR/podkop/files/usr/lib/diagnostics/status.uc"
|
||||
DIAGNOSTICS_RUNTIME="$ROOT_DIR/podkop/files/usr/lib/diagnostics/runtime.uc"
|
||||
PODKOP_BIN="$ROOT_DIR/podkop/files/usr/bin/podkop"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
CLI_UC="$PODKOP_BIN"
|
||||
DIAGNOSTICS="$ROOT_DIR/forkop/files/usr/lib/diagnostics/status.uc"
|
||||
DIAGNOSTICS_RUNTIME="$ROOT_DIR/forkop/files/usr/lib/diagnostics/runtime.uc"
|
||||
FORKOP_BIN="$ROOT_DIR/forkop/files/usr/bin/forkop"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
CLI_UC="$FORKOP_BIN"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
status_ucode() {
|
||||
ucode -L "$PODKOP_LIB" "$DIAGNOSTICS" "$@"
|
||||
ucode -L "$FORKOP_LIB" "$DIAGNOSTICS" "$@"
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
@@ -47,17 +47,17 @@ assert_status 1 0 0 "running but disabled"
|
||||
assert_status 0 1 1 "stopped but enabled"
|
||||
assert_status 0 0 0 "stopped & disabled"
|
||||
|
||||
[ ! -e "$PODKOP_LIB/status_diagnostics.sh" ] ||
|
||||
[ ! -e "$FORKOP_LIB/status_diagnostics.sh" ] ||
|
||||
fail "status_diagnostics.sh shell owner must be removed"
|
||||
grep -Fq 'get_system_info: [ "diagnostics/runtime.uc", "get-system-info", 0 ]' "$CLI_UC" ||
|
||||
fail "service/cli.uc must dispatch get_system_info through diagnostics/runtime.uc"
|
||||
[ "$(PODKOP_VERSION=runtime-test ucode -L "$PODKOP_LIB" "$DIAGNOSTICS_RUNTIME" show-version)" = "runtime-test" ] ||
|
||||
[ "$(FORKOP_VERSION=runtime-test ucode -L "$FORKOP_LIB" "$DIAGNOSTICS_RUNTIME" show-version)" = "runtime-test" ] ||
|
||||
fail "diagnostics/runtime.uc show-version mode failed"
|
||||
if grep -n -E 'require\("uci"\)\.cursor|uci -q|uci", "show"|uci", "-q"' "$DIAGNOSTICS_RUNTIME" >/dev/null 2>&1; then
|
||||
fail "diagnostics/runtime.uc must use core.uci instead of owning direct UCI cursor or CLI calls"
|
||||
fi
|
||||
grep -Fq '"podkop-stably-running", RT_TABLE_NAME, NFT_TABLE_NAME, NFT_FAKEIP_MARK, RUNTIME_STABLE_MIN_AGE' "$DIAGNOSTICS_RUNTIME" ||
|
||||
fail "diagnostics Podkop status must use stable runtime state to avoid crash-loop flicker"
|
||||
grep -Fq '"forkop-stably-running", RT_TABLE_NAME, NFT_TABLE_NAME, NFT_FAKEIP_MARK, RUNTIME_STABLE_MIN_AGE' "$DIAGNOSTICS_RUNTIME" ||
|
||||
fail "diagnostics Forkop status must use stable runtime state to avoid crash-loop flicker"
|
||||
grep -Fq '"sing-box-service-stable",' "$DIAGNOSTICS_RUNTIME" ||
|
||||
fail "diagnostics sing-box status must use stable runtime state to avoid crash-loop flicker"
|
||||
|
||||
@@ -71,13 +71,13 @@ if (value.status !== "running but disabled" || value.dns_configured !== 1) {
|
||||
NODE
|
||||
|
||||
{
|
||||
printf 'Tue Jun 30 11:00:00 2026 user.notice podkop-plus: [info] Starting Podkop Plus\n'
|
||||
printf 'Tue Jun 30 11:00:00 2026 user.notice forkop: [info] Starting Forkop\n'
|
||||
for i in $(seq 1 4500); do
|
||||
printf 'Tue Jun 30 11:00:%02d 2026 daemon.info unrelated[%04d]: filler filler filler filler filler filler filler filler filler filler\n' "$((i % 60))" "$i"
|
||||
done
|
||||
printf 'Tue Jun 30 11:01:00 2026 user.notice podkop-plus: [info] large logread marker survived stdin transport\n'
|
||||
printf 'Tue Jun 30 11:01:00 2026 user.notice forkop: [info] large logread marker survived stdin transport\n'
|
||||
} >"$WORK_DIR/large-logread.txt"
|
||||
large_logs="$(PODKOP_LIB="$PODKOP_LIB" ucode -L "$PODKOP_LIB" "$DIAGNOSTICS_RUNTIME" podkop-logs-fixture <"$WORK_DIR/large-logread.txt")" ||
|
||||
large_logs="$(FORKOP_LIB="$FORKOP_LIB" ucode -L "$FORKOP_LIB" "$DIAGNOSTICS_RUNTIME" forkop-logs-fixture <"$WORK_DIR/large-logread.txt")" ||
|
||||
fail "diagnostics/runtime.uc must process large logread payloads through stdin without shell argument limits"
|
||||
case "$large_logs" in
|
||||
*"large logread marker survived stdin transport"*) ;;
|
||||
@@ -94,14 +94,14 @@ SH
|
||||
chmod +x "$fake_bin/curl"
|
||||
uci_state="$WORK_DIR/uci-state.txt"
|
||||
cat >"$uci_state" <<'EOF'
|
||||
podkop-plus.settings=settings
|
||||
podkop-plus.settings.latency_test_url=https://latency.example/generate_204
|
||||
forkop.settings=settings
|
||||
forkop.settings.latency_test_url=https://latency.example/generate_204
|
||||
EOF
|
||||
FAKE_CURL_LOG="$WORK_DIR/fake-curl.log" \
|
||||
PODKOP_UCI_STATE_FILE="$uci_state" \
|
||||
PODKOP_LIB="$PODKOP_LIB" \
|
||||
FORKOP_UCI_STATE_FILE="$uci_state" \
|
||||
FORKOP_LIB="$FORKOP_LIB" \
|
||||
PATH="$fake_bin:$PATH" \
|
||||
ucode -L "$PODKOP_LIB" "$DIAGNOSTICS_RUNTIME" clash-api get_proxy_latency proxy-out 5000 >/dev/null ||
|
||||
ucode -L "$FORKOP_LIB" "$DIAGNOSTICS_RUNTIME" clash-api get_proxy_latency proxy-out 5000 >/dev/null ||
|
||||
fail "clash-api get_proxy_latency should use fake curl successfully"
|
||||
grep -Fq "url=https://latency.example/generate_204" "$WORK_DIR/fake-curl.log" ||
|
||||
fail "clash-api latency check must use settings.latency_test_url"
|
||||
@@ -111,11 +111,11 @@ mkdir -p "$latency_action_dir"
|
||||
latency_state="$latency_action_dir/latency-1.json"
|
||||
printf '%s\n' '{"success":true,"running":true,"kind":"latency","latency_type":"proxy_list","section":"main","tag":"[]","started_at":100}' >"$latency_state"
|
||||
FAKE_CURL_LOG="$WORK_DIR/fake-curl-latencies.log" \
|
||||
PODKOP_UCI_STATE_FILE="$uci_state" \
|
||||
PODKOP_LIB="$PODKOP_LIB" \
|
||||
PODKOP_UI_LATENCY_ACTION_DIR="$latency_action_dir" \
|
||||
FORKOP_UCI_STATE_FILE="$uci_state" \
|
||||
FORKOP_LIB="$FORKOP_LIB" \
|
||||
FORKOP_UI_LATENCY_ACTION_DIR="$latency_action_dir" \
|
||||
PATH="$fake_bin:$PATH" \
|
||||
ucode -L "$PODKOP_LIB" "$DIAGNOSTICS_RUNTIME" clash-api get_proxy_latencies '["proxy-a","proxy-b"]' 5000 "$latency_state" >/dev/null ||
|
||||
ucode -L "$FORKOP_LIB" "$DIAGNOSTICS_RUNTIME" clash-api get_proxy_latencies '["proxy-a","proxy-b"]' 5000 "$latency_state" >/dev/null ||
|
||||
fail "clash-api get_proxy_latencies should update latency progress"
|
||||
JOB_STATE="$latency_state" node - <<'NODE'
|
||||
const fs = require("fs");
|
||||
@@ -226,10 +226,10 @@ EOF
|
||||
)"
|
||||
|
||||
printf '%s\n' "$sing_box_netstat" |
|
||||
PODKOP_LIB="$PODKOP_LIB" ucode -L "$PODKOP_LIB" "$DIAGNOSTICS_RUNTIME" sing-box-standard-ports-listening-fixture >/dev/null ||
|
||||
FORKOP_LIB="$FORKOP_LIB" ucode -L "$FORKOP_LIB" "$DIAGNOSTICS_RUNTIME" sing-box-standard-ports-listening-fixture >/dev/null ||
|
||||
fail "sing-box standard listeners should satisfy diagnostics"
|
||||
if printf '%s\n' "$sing_box_netstat" | sed '/0.0.0.0:1602/d' |
|
||||
PODKOP_LIB="$PODKOP_LIB" ucode -L "$PODKOP_LIB" "$DIAGNOSTICS_RUNTIME" sing-box-standard-ports-listening-fixture >/dev/null 2>&1; then
|
||||
FORKOP_LIB="$FORKOP_LIB" ucode -L "$FORKOP_LIB" "$DIAGNOSTICS_RUNTIME" sing-box-standard-ports-listening-fixture >/dev/null 2>&1; then
|
||||
fail "missing sing-box tproxy listener should fail diagnostics"
|
||||
fi
|
||||
|
||||
|
||||
+27
-27
@@ -2,8 +2,8 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
APPLY="$ROOT_DIR/podkop/files/usr/lib/dns/apply.uc"
|
||||
UCODE_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
APPLY="$ROOT_DIR/forkop/files/usr/lib/dns/apply.uc"
|
||||
UCODE_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
STATE="$WORK_DIR/uci.state"
|
||||
LOG="$WORK_DIR/uci.log"
|
||||
@@ -131,7 +131,7 @@ export UCI_STATE="$STATE"
|
||||
export UCI_LOG="$LOG"
|
||||
export DNSMASQ_LOG
|
||||
export DNSMASQ_INIT="$WORK_DIR/dnsmasq-init"
|
||||
export PODKOP_CONFIG_NAME="podkop-plus"
|
||||
export FORKOP_CONFIG_NAME="forkop"
|
||||
export SB_DNS_INBOUND_ADDRESS="127.0.0.42"
|
||||
|
||||
if grep -E 'uci -q|command -v uci' "$APPLY" >/dev/null; then
|
||||
@@ -182,18 +182,18 @@ cat >"$STATE" <<'EOF_STATE'
|
||||
dhcp.@dnsmasq[0].server=1.1.1.1 8.8.8.8
|
||||
dhcp.@dnsmasq[0].noresolv=0
|
||||
dhcp.@dnsmasq[0].cachesize=150
|
||||
podkop-plus.settings.shutdown_correctly=1
|
||||
forkop.settings.shutdown_correctly=1
|
||||
EOF_STATE
|
||||
|
||||
: > "$DNSMASQ_LOG"
|
||||
: > "$LOG"
|
||||
ucode -L "$UCODE_LIB" "$APPLY" configure force
|
||||
assert_value 'dhcp.@dnsmasq[0].server' '127.0.0.42'
|
||||
assert_value 'dhcp.@dnsmasq[0].podkop_server' '1.1.1.1 8.8.8.8'
|
||||
assert_value 'dhcp.@dnsmasq[0].forkop_server' '1.1.1.1 8.8.8.8'
|
||||
assert_value 'dhcp.@dnsmasq[0].noresolv' '1'
|
||||
assert_value 'dhcp.@dnsmasq[0].podkop_noresolv' '0'
|
||||
assert_value 'dhcp.@dnsmasq[0].forkop_noresolv' '0'
|
||||
assert_value 'dhcp.@dnsmasq[0].cachesize' '0'
|
||||
assert_value 'dhcp.@dnsmasq[0].podkop_cachesize' '150'
|
||||
assert_value 'dhcp.@dnsmasq[0].forkop_cachesize' '150'
|
||||
assert_log_contains 'commit dhcp'
|
||||
assert_dnsmasq_restarted
|
||||
|
||||
@@ -203,21 +203,21 @@ ucode -L "$UCODE_LIB" "$APPLY" restore force
|
||||
assert_value 'dhcp.@dnsmasq[0].server' '1.1.1.1 8.8.8.8'
|
||||
assert_value 'dhcp.@dnsmasq[0].noresolv' '0'
|
||||
assert_value 'dhcp.@dnsmasq[0].cachesize' '150'
|
||||
assert_absent 'dhcp.@dnsmasq[0].podkop_server'
|
||||
assert_absent 'dhcp.@dnsmasq[0].podkop_noresolv'
|
||||
assert_absent 'dhcp.@dnsmasq[0].podkop_cachesize'
|
||||
assert_absent 'dhcp.@dnsmasq[0].forkop_server'
|
||||
assert_absent 'dhcp.@dnsmasq[0].forkop_noresolv'
|
||||
assert_absent 'dhcp.@dnsmasq[0].forkop_cachesize'
|
||||
assert_log_contains 'commit dhcp'
|
||||
assert_dnsmasq_restarted
|
||||
|
||||
cat >"$STATE" <<'EOF_STATE'
|
||||
dhcp.@dnsmasq[0].server=127.0.0.42
|
||||
dhcp.@dnsmasq[0].notinterface=br-lan guest
|
||||
dhcp.@dnsmasq[0].podkop_server=1.1.1.1 8.8.8.8
|
||||
dhcp.@dnsmasq[0].podkop_notinterface=wan docker
|
||||
dhcp.@dnsmasq[0].podkop_noresolv=1
|
||||
dhcp.@dnsmasq[0].podkop_cachesize=0
|
||||
dhcp.podkop_plus.interface=br-lan guest
|
||||
podkop-plus.settings.dont_touch_dhcp=1
|
||||
dhcp.@dnsmasq[0].forkop_server=1.1.1.1 8.8.8.8
|
||||
dhcp.@dnsmasq[0].forkop_notinterface=wan docker
|
||||
dhcp.@dnsmasq[0].forkop_noresolv=1
|
||||
dhcp.@dnsmasq[0].forkop_cachesize=0
|
||||
dhcp.forkop.interface=br-lan guest
|
||||
forkop.settings.dont_touch_dhcp=1
|
||||
EOF_STATE
|
||||
|
||||
run_restore
|
||||
@@ -225,16 +225,16 @@ assert_value 'dhcp.@dnsmasq[0].server' '1.1.1.1 8.8.8.8'
|
||||
assert_value 'dhcp.@dnsmasq[0].notinterface' 'wan docker'
|
||||
assert_value 'dhcp.@dnsmasq[0].noresolv' '1'
|
||||
assert_value 'dhcp.@dnsmasq[0].cachesize' '0'
|
||||
assert_absent 'dhcp.@dnsmasq[0].podkop_server'
|
||||
assert_absent 'dhcp.@dnsmasq[0].podkop_notinterface'
|
||||
assert_absent 'dhcp.@dnsmasq[0].podkop_noresolv'
|
||||
assert_absent 'dhcp.@dnsmasq[0].podkop_cachesize'
|
||||
assert_absent 'dhcp.podkop_plus.interface'
|
||||
assert_absent 'dhcp.@dnsmasq[0].forkop_server'
|
||||
assert_absent 'dhcp.@dnsmasq[0].forkop_notinterface'
|
||||
assert_absent 'dhcp.@dnsmasq[0].forkop_noresolv'
|
||||
assert_absent 'dhcp.@dnsmasq[0].forkop_cachesize'
|
||||
assert_absent 'dhcp.forkop.interface'
|
||||
assert_log_contains 'commit dhcp'
|
||||
|
||||
cat >"$STATE" <<'EOF_STATE'
|
||||
dhcp.@dnsmasq[0].server=9.9.9.9
|
||||
podkop-plus.settings.dont_touch_dhcp=1
|
||||
forkop.settings.dont_touch_dhcp=1
|
||||
EOF_STATE
|
||||
|
||||
run_restore
|
||||
@@ -243,17 +243,17 @@ assert_log_empty
|
||||
|
||||
cat >"$STATE" <<'EOF_STATE'
|
||||
dhcp.@dnsmasq[0].server=127.0.0.42
|
||||
dhcp.@dnsmasq[0].podkop_noresolv=1
|
||||
dhcp.@dnsmasq[0].podkop_cachesize=0
|
||||
podkop-plus.settings.dont_touch_dhcp=0
|
||||
dhcp.@dnsmasq[0].forkop_noresolv=1
|
||||
dhcp.@dnsmasq[0].forkop_cachesize=0
|
||||
forkop.settings.dont_touch_dhcp=0
|
||||
EOF_STATE
|
||||
|
||||
run_restore
|
||||
assert_absent 'dhcp.@dnsmasq[0].server'
|
||||
assert_value 'dhcp.@dnsmasq[0].noresolv' '1'
|
||||
assert_value 'dhcp.@dnsmasq[0].cachesize' '0'
|
||||
assert_absent 'dhcp.@dnsmasq[0].podkop_noresolv'
|
||||
assert_absent 'dhcp.@dnsmasq[0].podkop_cachesize'
|
||||
assert_absent 'dhcp.@dnsmasq[0].forkop_noresolv'
|
||||
assert_absent 'dhcp.@dnsmasq[0].forkop_cachesize'
|
||||
assert_log_contains 'commit dhcp'
|
||||
|
||||
printf 'DNS apply checks passed\n'
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
GENERATOR="$PODKOP_LIB/singbox/generator.uc"
|
||||
FAILOVER="$PODKOP_LIB/singbox/dns_failover.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
GENERATOR="$FORKOP_LIB/singbox/generator.uc"
|
||||
FAILOVER="$FORKOP_LIB/singbox/dns_failover.uc"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -21,9 +21,9 @@ generate() {
|
||||
local fixture="$1"
|
||||
local output="$2"
|
||||
local state="${3:-$WORK_DIR/missing-state.json}"
|
||||
PODKOP_LIB="$PODKOP_LIB" \
|
||||
PODKOP_DNS_FAILOVER_STATE_FILE="$state" \
|
||||
ucode -L "$PODKOP_LIB" "$GENERATOR" generate-config-fixture "$fixture" "$output" 192.168.1.1 0
|
||||
FORKOP_LIB="$FORKOP_LIB" \
|
||||
FORKOP_DNS_FAILOVER_STATE_FILE="$state" \
|
||||
ucode -L "$FORKOP_LIB" "$GENERATOR" generate-config-fixture "$fixture" "$output" 192.168.1.1 0
|
||||
}
|
||||
|
||||
cat >"$WORK_DIR/single.json" <<'JSON'
|
||||
@@ -141,11 +141,11 @@ cat >"$WORK_DIR/alive-recovery.json" <<'JSON'
|
||||
{ "0": true, "1": true }
|
||||
JSON
|
||||
|
||||
selected="$(ucode -L "$PODKOP_LIB" "$FAILOVER" select-fixture "$WORK_DIR/select-state.json" "$WORK_DIR/alive-failover.json" main 0)"
|
||||
selected="$(ucode -L "$FORKOP_LIB" "$FAILOVER" select-fixture "$WORK_DIR/select-state.json" "$WORK_DIR/alive-failover.json" main 0)"
|
||||
printf '%s' "$selected" | grep -Eq '"index"[[:space:]]*:[[:space:]]*1' || fail "dead active DNS must select the first live server below"
|
||||
printf '%s' "$selected" | grep -Eq '"reason"[[:space:]]*:[[:space:]]*"active_dead"' || fail "failover reason"
|
||||
|
||||
selected="$(ucode -L "$PODKOP_LIB" "$FAILOVER" select-fixture "$WORK_DIR/select-state.json" "$WORK_DIR/alive-recovery.json" bootstrap 1)"
|
||||
selected="$(ucode -L "$FORKOP_LIB" "$FAILOVER" select-fixture "$WORK_DIR/select-state.json" "$WORK_DIR/alive-recovery.json" bootstrap 1)"
|
||||
printf '%s' "$selected" | grep -Eq '"index"[[:space:]]*:[[:space:]]*0' || fail "recovery must return to the highest live priority"
|
||||
printf '%s' "$selected" | grep -Eq '"reason"[[:space:]]*:[[:space:]]*"recovery"' || fail "recovery reason"
|
||||
|
||||
@@ -156,7 +156,7 @@ cat >"$WORK_DIR/verify-bootstrap.json" <<'JSON'
|
||||
{ "main_index": 0, "bootstrap_index": 1, "bootstrap_servers": [ "a", "b" ] }
|
||||
JSON
|
||||
|
||||
verification="$(ucode -L "$PODKOP_LIB" "$FAILOVER" verification-plan-fixture "$WORK_DIR/verify-previous.json" "$WORK_DIR/verify-bootstrap.json")"
|
||||
verification="$(ucode -L "$FORKOP_LIB" "$FAILOVER" verification-plan-fixture "$WORK_DIR/verify-previous.json" "$WORK_DIR/verify-bootstrap.json")"
|
||||
printf '%s' "$verification" | grep -Eq '"main"[[:space:]]*:[[:space:]]*false' || fail "bootstrap-only switch must not require a dead main DNS to recover"
|
||||
printf '%s' "$verification" | grep -Eq '"bootstrap"[[:space:]]*:[[:space:]]*true' || fail "bootstrap-only switch must verify the selected bootstrap DNS"
|
||||
|
||||
|
||||
@@ -5,6 +5,9 @@ const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { spawnSync } = require("child_process");
|
||||
|
||||
const legacyStem = String.fromCharCode(112, 111, 100, 107, 111, 112);
|
||||
const legacyAppDir = `luci-app-${legacyStem}-plus`;
|
||||
|
||||
function usage() {
|
||||
console.error("Usage: config_contract_matrix.js --current <repo> --stable <repo> [--check]");
|
||||
process.exit(2);
|
||||
@@ -145,15 +148,17 @@ function enrichUiValues(fileData, option, block, values) {
|
||||
}
|
||||
|
||||
function extractUi(repo, fields) {
|
||||
const viewDir = path.join(
|
||||
const currentViewDir = path.join(
|
||||
repo,
|
||||
"luci-app-podkop-plus",
|
||||
"luci-app-forkop",
|
||||
"htdocs",
|
||||
"luci-static",
|
||||
"resources",
|
||||
"view",
|
||||
"podkop",
|
||||
"forkop",
|
||||
);
|
||||
const legacyViewDir = path.join(repo, legacyAppDir, "htdocs", "luci-static", "resources", "view", legacyStem);
|
||||
const viewDir = fs.existsSync(currentViewDir) ? currentViewDir : legacyViewDir;
|
||||
const files = walkFiles(viewDir).filter((file) => file.endsWith(".js"));
|
||||
|
||||
for (const file of files) {
|
||||
@@ -200,7 +205,9 @@ function extractUi(repo, fields) {
|
||||
}
|
||||
|
||||
function extractConfigDefaults(repo, fields) {
|
||||
const file = path.join(repo, "podkop", "files", "etc", "config", "podkop");
|
||||
const currentFile = path.join(repo, "forkop", "files", "etc", "config", "forkop");
|
||||
const legacyFile = path.join(repo, legacyStem, "files", "etc", "config", legacyStem);
|
||||
const file = fs.existsSync(currentFile) ? currentFile : legacyFile;
|
||||
const data = readFileIfExists(file);
|
||||
for (const line of data.split(/\n/)) {
|
||||
const match = line.match(/^\s*(#\s*)?(option|list)\s+([A-Za-z0-9_]+)\s+['"]?([^'"]*)/);
|
||||
@@ -212,17 +219,22 @@ function extractConfigDefaults(repo, fields) {
|
||||
}
|
||||
|
||||
function extractBackend(repo, fields) {
|
||||
const backendRoot = fs.existsSync(path.join(repo, "forkop")) ? "forkop" : legacyStem;
|
||||
const roots = [
|
||||
path.join(repo, "podkop", "files", "usr", "lib"),
|
||||
path.join(repo, "podkop", "files", "usr", "bin"),
|
||||
path.join(repo, "podkop", "files", "etc", "init.d"),
|
||||
path.join(repo, backendRoot, "files", "usr", "lib"),
|
||||
path.join(repo, backendRoot, "files", "usr", "bin"),
|
||||
path.join(repo, backendRoot, "files", "etc", "init.d"),
|
||||
];
|
||||
const files = roots.flatMap((root) => walkFiles(root)).filter((file) => /\.(sh|uc)$/.test(file) || /[\\/]podkop$/.test(file));
|
||||
const files = roots
|
||||
.flatMap((root) => walkFiles(root))
|
||||
.filter((file) => /\.(sh|uc)$/.test(file) || ["forkop", legacyStem].includes(path.basename(file)));
|
||||
const installer = path.join(repo, "install.sh");
|
||||
if (fs.existsSync(installer)) files.push(installer);
|
||||
|
||||
const shellOptionRe = /\bconfig_(?:get|get_bool|list_foreach)\s+\S+\s+(?:"[^"]+"|'[^']+'|\$[A-Za-z_][A-Za-z0-9_]*|\$\{[^}]+\})\s+["']([A-Za-z0-9_]+)["']/g;
|
||||
const ucodeOptionRe = /\b(?:option|list_option|bool_option|int_option)\(\s*[^,\n]+,\s*["']([A-Za-z0-9_]+)["']/g;
|
||||
const ucodeStaticOptionKeyArrayRe = /^\s*\[\s*["']([A-Za-z0-9_]+)["']\s*,/gm;
|
||||
const migrationRe = /\bpodkop_uci_(?:set_option|set_option_if_missing|delete_option|add_list_unique)\s+["'$A-Za-z0-9_{}.-]+\s+["']([A-Za-z0-9_]+)["']/g;
|
||||
const migrationRe = new RegExp(`\\b(?:forkop|${legacyStem})_uci_(?:set_option|set_option_if_missing|delete_option|add_list_unique)\\s+["'$A-Za-z0-9_{}.-]+\\s+["']([A-Za-z0-9_]+)["']`, "g");
|
||||
const ucodeMigrationRe = /\b(?:set_option|set_option_if_missing|delete_option|add_list_unique)\(\s*[^,\n]+,\s*[^,\n]+,\s*["']([A-Za-z0-9_]+)["']/g;
|
||||
|
||||
for (const file of files) {
|
||||
|
||||
+36
-36
@@ -2,16 +2,16 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_BIN="$ROOT_DIR/podkop/files/usr/bin/podkop"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
CLI_UC="$PODKOP_BIN"
|
||||
HELPERS_SH="$PODKOP_LIB/helpers.sh"
|
||||
LIFECYCLE_UC="$PODKOP_LIB/service/lifecycle.uc"
|
||||
PACKAGES_UC="$PODKOP_LIB/core/packages.uc"
|
||||
RULES_UC="$PODKOP_LIB/providers/rules.uc"
|
||||
SINGBOX_RUNTIME_UC="$PODKOP_LIB/singbox/runtime.uc"
|
||||
COMPONENT_ACTION_UC="$PODKOP_LIB/components/action.uc"
|
||||
DIAGNOSTICS_RUNTIME_UC="$PODKOP_LIB/diagnostics/runtime.uc"
|
||||
FORKOP_BIN="$ROOT_DIR/forkop/files/usr/bin/forkop"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
CLI_UC="$FORKOP_BIN"
|
||||
HELPERS_SH="$FORKOP_LIB/helpers.sh"
|
||||
LIFECYCLE_UC="$FORKOP_LIB/service/lifecycle.uc"
|
||||
PACKAGES_UC="$FORKOP_LIB/core/packages.uc"
|
||||
RULES_UC="$FORKOP_LIB/providers/rules.uc"
|
||||
SINGBOX_RUNTIME_UC="$FORKOP_LIB/singbox/runtime.uc"
|
||||
COMPONENT_ACTION_UC="$FORKOP_LIB/components/action.uc"
|
||||
DIAGNOSTICS_RUNTIME_UC="$FORKOP_LIB/diagnostics/runtime.uc"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -26,16 +26,16 @@ fail() {
|
||||
|
||||
[ ! -e "$HELPERS_SH" ] ||
|
||||
fail "helpers.sh shell owner must be removed"
|
||||
if grep -R -n -F 'helpers.sh' "$PODKOP_BIN" "$PODKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
|
||||
if grep -R -n -F 'helpers.sh' "$FORKOP_BIN" "$FORKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
|
||||
fail "runtime shell must not reference helpers.sh"
|
||||
fi
|
||||
if grep -R -n -E 'helpers_ucode\(|get_(inbound|server_inbound|tailscale_dns_server|outbound)_tag_by_section\(|get_domain_resolver_tag\(|provider_status_ucode\(|is_ipv4\(|is_min_package_version\(|url_get_(scheme|userinfo|host|port|path|query_param)\(' \
|
||||
"$PODKOP_BIN" "$PODKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
|
||||
"$FORKOP_BIN" "$FORKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
|
||||
fail "helpers.sh wrapper symbols must not remain in runtime shell"
|
||||
fi
|
||||
|
||||
grep -Fq '#!/usr/bin/ucode' "$PODKOP_BIN" ||
|
||||
fail "podkop entrypoint must be a direct ucode executable"
|
||||
grep -Fq '#!/usr/bin/ucode' "$FORKOP_BIN" ||
|
||||
fail "forkop entrypoint must be a direct ucode executable"
|
||||
grep -Fq 'service/lifecycle.uc' "$CLI_UC" ||
|
||||
fail "service/cli.uc must dispatch lifecycle through service/lifecycle.uc"
|
||||
grep -Fq 'core/packages.uc' "$LIFECYCLE_UC" ||
|
||||
@@ -45,19 +45,19 @@ for shell_owner_pattern in \
|
||||
'config_get' \
|
||||
'uci_set'
|
||||
do
|
||||
if grep -Fq "$shell_owner_pattern" "$PODKOP_BIN"; then
|
||||
fail "podkop shell entrypoint must not own $shell_owner_pattern logic"
|
||||
if grep -Fq "$shell_owner_pattern" "$FORKOP_BIN"; then
|
||||
fail "forkop shell entrypoint must not own $shell_owner_pattern logic"
|
||||
fi
|
||||
done
|
||||
if grep -E -n '(^|[;&|[:space:]])nft[[:space:]]+(list|add|delete|flush)' "$PODKOP_BIN" >/dev/null 2>&1; then
|
||||
fail "podkop shell entrypoint must not own nft command logic"
|
||||
if grep -E -n '(^|[;&|[:space:]])nft[[:space:]]+(list|add|delete|flush)' "$FORKOP_BIN" >/dev/null 2>&1; then
|
||||
fail "forkop shell entrypoint must not own nft command logic"
|
||||
fi
|
||||
if grep -E -n '(^|[;&|[:space:]])ip[[:space:]]+(rule|route)' "$PODKOP_BIN" >/dev/null 2>&1; then
|
||||
fail "podkop shell entrypoint must not own ip rule/route logic"
|
||||
if grep -E -n '(^|[;&|[:space:]])ip[[:space:]]+(rule|route)' "$FORKOP_BIN" >/dev/null 2>&1; then
|
||||
fail "forkop shell entrypoint must not own ip rule/route logic"
|
||||
fi
|
||||
[ ! -e "$PODKOP_LIB/updater.sh" ] ||
|
||||
[ ! -e "$FORKOP_LIB/updater.sh" ] ||
|
||||
fail "updater.sh shell owner must be removed"
|
||||
[ ! -e "$PODKOP_LIB/status_diagnostics.sh" ] ||
|
||||
[ ! -e "$FORKOP_LIB/status_diagnostics.sh" ] ||
|
||||
fail "status_diagnostics.sh shell owner must be removed"
|
||||
grep -Fq 'diagnostics/runtime.uc' "$CLI_UC" ||
|
||||
fail "service/cli.uc must dispatch diagnostics through diagnostics/runtime.uc"
|
||||
@@ -86,52 +86,52 @@ do
|
||||
fail "singbox/runtime.uc missing $mode"
|
||||
done
|
||||
if grep -R -n -E 'get_sing_box_version\(|sing_box_version_from_output\(|sing_box_version_output\(|sing_box_output_has_build_tag\(|sing_box_has_build_tag\(|is_sing_box_extended\(|is_sing_box_tiny_package_installed\(|is_sing_box_full_package_installed\(|is_sing_box_compressed_marker_set\(|is_sing_box_extended_marker_set\(|read_sing_box_version_state\(|is_sing_box_tiny_marker_set\(|is_sing_box_tiny\(|sing_box_supports_tailscale\(|get_sing_box_variant\(|updates_(write|read|clear|restore)_sing_box_(variant_marker|version_state)\(' \
|
||||
"$PODKOP_BIN" "$PODKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
|
||||
"$FORKOP_BIN" "$FORKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
|
||||
fail "sing-box helper/state shell symbols must not remain"
|
||||
fi
|
||||
|
||||
if ucode -L "$PODKOP_LIB" "$PACKAGES_UC" installed podkop-plus-definitely-missing >/dev/null 2>&1; then
|
||||
if ucode -L "$FORKOP_LIB" "$PACKAGES_UC" installed forkop-definitely-missing >/dev/null 2>&1; then
|
||||
fail "missing package must not be reported installed"
|
||||
fi
|
||||
|
||||
mark_hex="$(ucode -L "$PODKOP_LIB" "$RULES_UC" mark-hex 0x01000000 2)"
|
||||
mark_hex="$(ucode -L "$FORKOP_LIB" "$RULES_UC" mark-hex 0x01000000 2)"
|
||||
[ "$mark_hex" = "0x01000002" ] ||
|
||||
fail "providers/rules.uc mark math changed"
|
||||
|
||||
version="$(printf 'sing-box version 1.12.4-extended\nEnvironment: test\n' |
|
||||
ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" version-from-output)"
|
||||
ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" version-from-output)"
|
||||
[ "$version" = "1.12.4-extended" ] ||
|
||||
fail "singbox/runtime.uc version-from-output changed"
|
||||
|
||||
ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" is-extended "1.12.4-extended" >/dev/null ||
|
||||
ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" is-extended "1.12.4-extended" >/dev/null ||
|
||||
fail "extended sing-box version should be detected"
|
||||
if ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" is-extended "1.12.4" >/dev/null 2>&1; then
|
||||
if ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" is-extended "1.12.4" >/dev/null 2>&1; then
|
||||
fail "stable sing-box version must not be detected as extended"
|
||||
fi
|
||||
ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" supports-tailscale "" "$(printf 'Tags: with_quic,with_tailscale\n')" >/dev/null ||
|
||||
ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" supports-tailscale "" "$(printf 'Tags: with_quic,with_tailscale\n')" >/dev/null ||
|
||||
fail "with_tailscale build tag should be detected"
|
||||
|
||||
SB_VERSION_STATE_FILE="$WORK_DIR/version" \
|
||||
ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" write-version-state 1.2.3 >/dev/null ||
|
||||
ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" write-version-state 1.2.3 >/dev/null ||
|
||||
fail "version state write failed"
|
||||
[ "$(SB_VERSION_STATE_FILE="$WORK_DIR/version" ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" read-version-state)" = "1.2.3" ] ||
|
||||
[ "$(SB_VERSION_STATE_FILE="$WORK_DIR/version" ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" read-version-state)" = "1.2.3" ] ||
|
||||
fail "version state read failed"
|
||||
SB_VERSION_STATE_FILE="$WORK_DIR/version" \
|
||||
ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" restore-version-state "" >/dev/null ||
|
||||
ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" restore-version-state "" >/dev/null ||
|
||||
fail "version state restore-clear failed"
|
||||
[ ! -e "$WORK_DIR/version" ] ||
|
||||
fail "empty version state restore must clear the state file"
|
||||
|
||||
SB_VARIANT_STATE_FILE="$WORK_DIR/variant" \
|
||||
ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" write-variant-marker extended-compressed >/dev/null ||
|
||||
ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" write-variant-marker extended-compressed >/dev/null ||
|
||||
fail "variant marker write failed"
|
||||
SB_VARIANT_STATE_FILE="$WORK_DIR/variant" \
|
||||
ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" marker-is extended-compressed >/dev/null ||
|
||||
ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" marker-is extended-compressed >/dev/null ||
|
||||
fail "variant marker check failed"
|
||||
[ "$(SB_VARIANT_STATE_FILE="$WORK_DIR/variant" ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" read-variant-marker)" = "extended-compressed" ] ||
|
||||
[ "$(SB_VARIANT_STATE_FILE="$WORK_DIR/variant" ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" read-variant-marker)" = "extended-compressed" ] ||
|
||||
fail "variant marker read failed"
|
||||
SB_VARIANT_STATE_FILE="$WORK_DIR/variant" \
|
||||
ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" restore-variant-marker "" >/dev/null ||
|
||||
ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" restore-variant-marker "" >/dev/null ||
|
||||
fail "variant marker restore-clear failed"
|
||||
[ ! -e "$WORK_DIR/variant" ] ||
|
||||
fail "empty variant marker restore must clear the marker file"
|
||||
|
||||
+11
-11
@@ -2,10 +2,10 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
INITD_UC="$PODKOP_LIB/service/initd.uc"
|
||||
STATE_UC="$PODKOP_LIB/service/state.uc"
|
||||
INITD="$ROOT_DIR/podkop/files/etc/init.d/podkop"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
INITD_UC="$FORKOP_LIB/service/initd.uc"
|
||||
STATE_UC="$FORKOP_LIB/service/state.uc"
|
||||
INITD="$ROOT_DIR/forkop/files/etc/init.d/forkop"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -19,10 +19,10 @@ fail() {
|
||||
}
|
||||
|
||||
initd_ucode() {
|
||||
ucode -L "$PODKOP_LIB" "$INITD_UC" "$@"
|
||||
ucode -L "$FORKOP_LIB" "$INITD_UC" "$@"
|
||||
}
|
||||
|
||||
config_file="$WORK_DIR/podkop-plus"
|
||||
config_file="$WORK_DIR/forkop"
|
||||
guard_file="$WORK_DIR/internal-config-change"
|
||||
sync_file="$WORK_DIR/service-triggers.sync"
|
||||
|
||||
@@ -89,7 +89,7 @@ if initd_ucode initd-should-queue-config-change-reload on_config_change on_confi
|
||||
fi
|
||||
|
||||
pending_file="$WORK_DIR/reload.pending"
|
||||
if PODKOP_PENDING_RELOAD_FILE="$pending_file" \
|
||||
if FORKOP_PENDING_RELOAD_FILE="$pending_file" \
|
||||
initd_ucode reload-begin-fixture on_config_change 123 0 1 start >/dev/null 2>&1; then
|
||||
fail "queued config-change reload should not run immediately while service is starting"
|
||||
fi
|
||||
@@ -99,7 +99,7 @@ grep -Fq 'reason=on_config_change' "$pending_file" ||
|
||||
fail "pending reload should preserve config-change reason"
|
||||
|
||||
rm -f "$pending_file"
|
||||
if PODKOP_PENDING_RELOAD_FILE="$pending_file" \
|
||||
if FORKOP_PENDING_RELOAD_FILE="$pending_file" \
|
||||
initd_ucode reload-begin-fixture pending 123 1 1 start >/dev/null 2>&1; then
|
||||
fail "pending reload should not run while another service action is active"
|
||||
fi
|
||||
@@ -229,7 +229,7 @@ fi
|
||||
if grep -Fq 'runtime_is_running' "$INITD"; then
|
||||
fail "init.d must not keep shell runtime status decisions"
|
||||
fi
|
||||
if grep -E -n 'PODKOP_(CONFIG_FILE|RELOAD_LOCK_DIR|RUNTIME_STATE_DIR|PENDING_RELOAD_FILE|SERVICE_TRIGGER_SYNC_FILE|INTERNAL_CONFIG_TRIGGER_GUARD|CONFIG_CHANGE_REASON|BIN|SERVICE_INIT)=' "$INITD" >/dev/null 2>&1; then
|
||||
if grep -E -n 'FORKOP_(CONFIG_FILE|RELOAD_LOCK_DIR|RUNTIME_STATE_DIR|PENDING_RELOAD_FILE|SERVICE_TRIGGER_SYNC_FILE|INTERNAL_CONFIG_TRIGGER_GUARD|CONFIG_CHANGE_REASON|BIN|SERVICE_INIT)=' "$INITD" >/dev/null 2>&1; then
|
||||
fail "init.d must not pass internal orchestration paths through shell env"
|
||||
fi
|
||||
if grep -n -E 'require\("uci"\)\.cursor|uci -q|uci", "-q"' "$INITD_UC" >/dev/null 2>&1; then
|
||||
@@ -238,10 +238,10 @@ fi
|
||||
if grep -E -n '(^|[[:space:]])config_(load|get|get_bool|set)[[:space:]]' "$INITD" >/dev/null 2>&1; then
|
||||
fail "init.d must not read UCI directly"
|
||||
fi
|
||||
if grep -Fq 'PODKOP_STATE_UC' "$INITD" || grep -Fq 'PODKOP_UI_UC' "$INITD" || grep -Fq 'PODKOP_STATUS_UC' "$INITD"; then
|
||||
if grep -Fq 'FORKOP_STATE_UC' "$INITD" || grep -Fq 'FORKOP_UI_UC' "$INITD" || grep -Fq 'FORKOP_STATUS_UC' "$INITD"; then
|
||||
fail "init.d must not orchestrate state/UI/status modules directly"
|
||||
fi
|
||||
if grep -Fq 'procd_set_param command "$PODKOP_BIN" start' "$INITD"; then
|
||||
if grep -Fq 'procd_set_param command "$FORKOP_BIN" start' "$INITD"; then
|
||||
fail "init.d must not hand the one-shot start operation to procd as a daemon"
|
||||
fi
|
||||
if grep -Fq 'initd-should-' "$STATE_UC"; then
|
||||
|
||||
+202
-66
@@ -4,6 +4,9 @@ set -euo pipefail
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
INSTALLER="$ROOT_DIR/install.sh"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
LEGACY_BRAND="$(printf '\160\157\144\153\157\160')"
|
||||
LEGACY_BACKEND="${LEGACY_BRAND}-plus"
|
||||
LEGACY_CONFIG_ALT="${LEGACY_BRAND}_plus"
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$WORK_DIR"
|
||||
@@ -21,7 +24,7 @@ if grep -n -E '(^|[;&|[:space:]])uci[[:space:]]+-q|command_exists[[:space:]]+uci
|
||||
fail "install.sh must not own UCI reads/writes through shell"
|
||||
fi
|
||||
|
||||
shell_sing_box_owner_symbols='REQUIRED_SING_BOX_VERSION|SING_BOX_EXTENDED_|SING_BOX_MANAGED_SERVICE_MARKER|remove_old_sing_box_if_needed|install_managed_sing_box_service_script|remove_managed_sing_box_service_script|disable_sing_box_service_config|prepare_sing_box_service_disabled|prepare_sing_box_package_service_install|stop_podkop_for_sing_box_install|pkg_install_sing_box_variant|pkg_install_name_downgrade|pkg_remove_sing_box_conflict|resolve_sing_box_extended_release|install_sing_box_extended_(package|binary)|restore_sing_box_after_failed|move_file_to_backup|validate_extended_sing_box_binary|archive-member-path|sing-box-extended-arch-suffix|sing-box-extended-asset-url|sing-box-extended-package-asset-url'
|
||||
shell_sing_box_owner_symbols='REQUIRED_SING_BOX_VERSION|SING_BOX_EXTENDED_|SING_BOX_MANAGED_SERVICE_MARKER|remove_old_sing_box_if_needed|install_managed_sing_box_service_script|remove_managed_sing_box_service_script|disable_sing_box_service_config|prepare_sing_box_service_disabled|prepare_sing_box_package_service_install|stop_forkop_for_sing_box_install|pkg_install_sing_box_variant|pkg_install_name_downgrade|pkg_remove_sing_box_conflict|resolve_sing_box_extended_release|install_sing_box_extended_(package|binary)|restore_sing_box_after_failed|move_file_to_backup|validate_extended_sing_box_binary|archive-member-path|sing-box-extended-arch-suffix|sing-box-extended-asset-url|sing-box-extended-package-asset-url'
|
||||
if grep -n -E "$shell_sing_box_owner_symbols" "$INSTALLER" >/dev/null; then
|
||||
fail "install.sh must not contain shell sing-box install/runtime ownership"
|
||||
fi
|
||||
@@ -31,47 +34,57 @@ grep -Fq 'ensure_bootstrap_package "ucode-mod-fs"' "$INSTALLER" ||
|
||||
grep -Fq 'ensure_bootstrap_package "ucode-mod-uci"' "$INSTALLER" ||
|
||||
fail "install.sh must bootstrap ucode-mod-uci before embedded UCI helper use"
|
||||
|
||||
grep -Fq '/usr/bin/podkop-plus component_action sing_box "$action"' "$INSTALLER" ||
|
||||
fail "selected sing-box install must delegate to podkop-plus component_action"
|
||||
grep -Fq '/usr/bin/forkop component_action sing_box "$action"' "$INSTALLER" ||
|
||||
fail "selected sing-box install must delegate to forkop component_action"
|
||||
for action in install_stable install_tiny install_extended install_extended_compressed; do
|
||||
grep -Fq "action=\"$action\"" "$INSTALLER" ||
|
||||
fail "selected sing-box installer is missing action mapping: $action"
|
||||
done
|
||||
|
||||
grep -Fq 'run_args([ INSTALLER_PODKOP_PLUS_BIN, "restore_dnsmasq" ])' "$INSTALLER" ||
|
||||
fail "installer dnsmasq restore must prefer installed podkop-plus ucode entrypoint"
|
||||
grep -Fq 'run_args([ bin_path, "restore_dnsmasq" ])' "$INSTALLER" ||
|
||||
fail "installer dnsmasq restore must prefer the active backend entrypoint"
|
||||
grep -Fq 'else if (mode == "dnsmasq-failsafe-restore")' "$INSTALLER" ||
|
||||
fail "dnsmasq restore fallback mode must remain available in embedded ucode helper"
|
||||
grep -Fq 'else if (mode == "installer-cleanup-legacy")' "$INSTALLER" ||
|
||||
fail "installer cleanup must be exposed as an embedded ucode mode"
|
||||
grep -Fq 'else if (mode == "installer-finalize-legacy")' "$INSTALLER" ||
|
||||
fail "installer legacy file cleanup must be exposed as an embedded ucode mode"
|
||||
grep -Fq 'else if (mode == "installer-post-install")' "$INSTALLER" ||
|
||||
fail "installer post-install must be exposed as an embedded ucode mode"
|
||||
grep -Fq 'install_json_ucode installer-cleanup-legacy' "$INSTALLER" ||
|
||||
fail "install.sh cleanup must delegate to embedded ucode"
|
||||
grep -Fq 'install_json_ucode installer-post-install' "$INSTALLER" ||
|
||||
fail "install.sh post-install must delegate to embedded ucode"
|
||||
grep -Fq 'installer_config_migration_path()' "$INSTALLER" ||
|
||||
fail "install.sh must embed the one-time configuration migration"
|
||||
grep -Fq 'ucode -L /usr/lib/forkop "$(installer_config_migration_path)" migrate' "$INSTALLER" ||
|
||||
fail "install.sh must execute the embedded migration only during the legacy transition"
|
||||
|
||||
if grep -n -E 'restore_podkop_dnsmasq_failsafe|remember_service_state|stop_conflicting_services|deactivate_original_podkop_if_present|remove_conflicting_dns_proxy|pkg_remove_if_installed|pkg_remove_matching_prefix|pkg_list_installed_names' "$INSTALLER" >/dev/null 2>&1; then
|
||||
if grep -n -E 'restore_forkop_dnsmasq_failsafe|remember_service_state|stop_conflicting_services|deactivate_original_forkop_if_present|remove_conflicting_dns_proxy|pkg_remove_if_installed|pkg_remove_matching_prefix|pkg_list_installed_names' "$INSTALLER" >/dev/null 2>&1; then
|
||||
fail "install.sh must not keep shell cleanup/remove service owners"
|
||||
fi
|
||||
if grep -n -E 'rm -f /var/luci-indexcache|rm -f /tmp/luci-indexcache|/etc/init\.d/rpcd[[:space:]]+reload|/etc/init\.d/podkop-plus[[:space:]]+(start|stop|disable|enable|restart)|/etc/init\.d/podkop[[:space:]]+(stop|disable)' "$INSTALLER" >/dev/null 2>&1; then
|
||||
if grep -n -E 'rm -f /var/luci-indexcache|rm -f /tmp/luci-indexcache|/etc/init\.d/rpcd[[:space:]]+reload|/etc/init\.d/forkop[[:space:]]+(start|stop|disable|enable|restart)|/etc/init\.d/forkop[[:space:]]+(stop|disable)' "$INSTALLER" >/dev/null 2>&1; then
|
||||
fail "install.sh shell must not own service/cache lifecycle actions"
|
||||
fi
|
||||
|
||||
awk '
|
||||
/^[[:space:]]*main\(\)[[:space:]]*\{/ { in_main = 1 }
|
||||
in_main && /ensure_bootstrap_ucode_runtime/ { ensure = NR }
|
||||
in_main && /detect_legacy_installation/ { detect = NR }
|
||||
in_main && /decide_i18n_installation/ { i18n = NR }
|
||||
in_main && /install_packages/ { install_packages = NR }
|
||||
in_main && /cleanup_legacy_installation/ { cleanup = NR }
|
||||
in_main && /install_backend_package/ { backend = NR }
|
||||
in_main && /migrate_legacy_configuration/ { migration = NR }
|
||||
in_main && /install_ui_packages/ { ui = NR }
|
||||
in_main && /install_selected_sing_box/ { sing_box = NR }
|
||||
in_main && /^[[:space:]]*\}/ { in_main = 0 }
|
||||
END {
|
||||
if (ensure > 0 && i18n > 0 && ensure < i18n &&
|
||||
install_packages > 0 && sing_box > 0 && install_packages < sing_box)
|
||||
if (ensure > 0 && detect > ensure && i18n > detect && cleanup > i18n &&
|
||||
backend > cleanup && migration > backend && ui > migration && sing_box > ui)
|
||||
exit 0
|
||||
exit 1
|
||||
}
|
||||
' "$INSTALLER" || fail "install.sh main order must bootstrap ucode before UCI reads and install backend before sing-box component_action"
|
||||
' "$INSTALLER" || fail "install.sh main order must remove the legacy packages, install the backend, migrate config, and then finish installation"
|
||||
|
||||
helper="$WORK_DIR/install-json.uc"
|
||||
awk '
|
||||
@@ -81,8 +94,15 @@ awk '
|
||||
' "$INSTALLER" > "$helper"
|
||||
[ -s "$helper" ] || fail "failed to extract embedded installer ucode helper"
|
||||
|
||||
printf '%s\n' '{"tag_name":"v-test"}' | ucode "$helper" release-tag | grep -Fxq 'v-test' ||
|
||||
printf '%s\n' '{"tag_name":"0.0.1"}' | ucode "$helper" release-tag | grep -Fxq '0.0.1' ||
|
||||
fail "embedded helper release-tag mode must parse release JSON"
|
||||
release_json='{"tag_name":"0.0.1","assets":[{"name":"forkop_0.0.1.ipk","browser_download_url":"https://example.com/forkop.ipk"}]}'
|
||||
printf '%s' "$release_json" | ucode "$helper" release-asset-url backend ipk | grep -Fxq 'https://example.com/forkop.ipk' ||
|
||||
fail "embedded helper must resolve the exact three-part Forkop package name"
|
||||
if printf '%s' '{"tag_name":"0.0.1","assets":[{"name":"forkop_0.0.1_all.ipk","browser_download_url":"https://example.com/old.ipk"}]}' |
|
||||
ucode "$helper" release-asset-url backend ipk | grep -q .; then
|
||||
fail "embedded helper must reject package names outside the Forkop release format"
|
||||
fi
|
||||
|
||||
cat >"$WORK_DIR/opkg" <<'SH'
|
||||
#!/usr/bin/env sh
|
||||
@@ -92,11 +112,18 @@ case "$1" in
|
||||
'https-dns-proxy - 1.0' \
|
||||
'luci-app-https-dns-proxy - 1.0' \
|
||||
'luci-i18n-https-dns-proxy-ru - 1.0'
|
||||
if [ -n "${FORKOP_INSTALLER_FAKE_LEGACY_BACKEND:-}" ]; then
|
||||
printf '%s\n' \
|
||||
"$FORKOP_INSTALLER_FAKE_LEGACY_BACKEND - 1.0" \
|
||||
"luci-app-$FORKOP_INSTALLER_FAKE_LEGACY_BACKEND - 1.0" \
|
||||
"luci-i18n-$FORKOP_INSTALLER_FAKE_LEGACY_BACKEND-ru - 1.0"
|
||||
fi
|
||||
;;
|
||||
remove)
|
||||
shift
|
||||
[ "$1" = "--force-depends" ] && shift
|
||||
printf '%s\n' "$1" >> "$PODKOP_INSTALLER_OPKG_LOG"
|
||||
printf '%s\n' "$1" >> "$FORKOP_INSTALLER_OPKG_LOG"
|
||||
[ "${FORKOP_INSTALLER_FAIL_REMOVE:-}" != "$1" ] || exit 1
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
@@ -106,20 +133,22 @@ chmod 0755 "$WORK_DIR/opkg"
|
||||
: >"$WORK_DIR/opkg.log"
|
||||
printf '%s\n' '1' |
|
||||
PATH="$WORK_DIR:$PATH" \
|
||||
PODKOP_INSTALLER_OPKG_LOG="$WORK_DIR/opkg.log" \
|
||||
PODKOP_INSTALLER_PODKOP_PLUS_INIT="$WORK_DIR/missing-podkop-plus-init" \
|
||||
PODKOP_INSTALLER_PODKOP_PLUS_BIN="$WORK_DIR/missing-podkop-bin" \
|
||||
PODKOP_INSTALLER_PODKOP_PLUS_LIB="$WORK_DIR/missing-podkop-lib" \
|
||||
PODKOP_INSTALLER_PODKOP_PLUS_UCI_DEFAULTS="$WORK_DIR/missing-uci-defaults" \
|
||||
PODKOP_INSTALLER_PODKOP_PLUS_LUCI_VIEW="$WORK_DIR/missing-luci-view" \
|
||||
PODKOP_INSTALLER_MENU_JSON="$WORK_DIR/missing-menu.json" \
|
||||
PODKOP_INSTALLER_ACL_JSON="$WORK_DIR/missing-acl.json" \
|
||||
PODKOP_INSTALLER_RU_LMO="$WORK_DIR/missing-ru.lmo" \
|
||||
PODKOP_INSTALLER_EN_LMO="$WORK_DIR/missing-en.lmo" \
|
||||
PODKOP_INSTALLER_RU_LUA="$WORK_DIR/missing-ru.lua" \
|
||||
PODKOP_INSTALLER_EN_LUA="$WORK_DIR/missing-en.lua" \
|
||||
PODKOP_INSTALLER_ORIGINAL_PODKOP_INIT="$WORK_DIR/missing-original-init" \
|
||||
PODKOP_UCI_STATE_FILE="$WORK_DIR/empty-uci.state" \
|
||||
FORKOP_INSTALLER_OPKG_LOG="$WORK_DIR/opkg.log" \
|
||||
FORKOP_INSTALLER_INIT="$WORK_DIR/missing-forkop-init" \
|
||||
FORKOP_INSTALLER_BIN="$WORK_DIR/missing-forkop-bin" \
|
||||
FORKOP_INSTALLER_LIB="$WORK_DIR/missing-forkop-lib" \
|
||||
FORKOP_INSTALLER_UCI_DEFAULTS="$WORK_DIR/missing-uci-defaults" \
|
||||
FORKOP_INSTALLER_LUCI_VIEW="$WORK_DIR/missing-luci-view" \
|
||||
FORKOP_INSTALLER_MENU_JSON="$WORK_DIR/missing-menu.json" \
|
||||
FORKOP_INSTALLER_ACL_JSON="$WORK_DIR/missing-acl.json" \
|
||||
FORKOP_INSTALLER_RU_LMO="$WORK_DIR/missing-ru.lmo" \
|
||||
FORKOP_INSTALLER_EN_LMO="$WORK_DIR/missing-en.lmo" \
|
||||
FORKOP_INSTALLER_RU_LUA="$WORK_DIR/missing-ru.lua" \
|
||||
FORKOP_INSTALLER_EN_LUA="$WORK_DIR/missing-en.lua" \
|
||||
FORKOP_INSTALLER_LEGACY_BASE_INIT="$WORK_DIR/missing-original-init" \
|
||||
FORKOP_INSTALLER_LEGACY_BRAND="$LEGACY_BRAND" \
|
||||
FORKOP_INSTALLER_LEGACY_BACKEND="$LEGACY_BACKEND" \
|
||||
FORKOP_UCI_STATE_FILE="$WORK_DIR/empty-uci.state" \
|
||||
ucode "$helper" installer-cleanup-legacy >"$WORK_DIR/conflict-state.env" 2>"$WORK_DIR/conflict.err"
|
||||
|
||||
grep -Fxq 'https-dns-proxy' "$WORK_DIR/opkg.log" ||
|
||||
@@ -129,76 +158,183 @@ grep -Fxq 'luci-app-https-dns-proxy' "$WORK_DIR/opkg.log" ||
|
||||
grep -Fxq 'luci-i18n-https-dns-proxy-ru' "$WORK_DIR/opkg.log" ||
|
||||
fail "installer cleanup must remove confirmed https-dns-proxy i18n packages"
|
||||
|
||||
write_fake_podkop_init() {
|
||||
cat >"$WORK_DIR/podkop-plus-init" <<'SH'
|
||||
write_fake_service_init() {
|
||||
service_path="$1"
|
||||
cat >"$service_path" <<'SH'
|
||||
#!/usr/bin/env sh
|
||||
case "$1" in
|
||||
enabled) exit 0 ;;
|
||||
status) printf '%s\n' running; exit 0 ;;
|
||||
stop|disable|enable|start|restart) printf '%s\n' "$1" >> "$PODKOP_INSTALLER_INIT_LOG"; exit 0 ;;
|
||||
stop|disable|enable|start|restart) printf '%s\n' "$1" >> "$FORKOP_INSTALLER_INIT_LOG"; exit 0 ;;
|
||||
esac
|
||||
exit 0
|
||||
SH
|
||||
chmod 0755 "$WORK_DIR/podkop-plus-init"
|
||||
chmod 0755 "$service_path"
|
||||
}
|
||||
|
||||
write_fake_podkop_init
|
||||
write_fake_service_init "$WORK_DIR/legacy-init"
|
||||
|
||||
cat >"$WORK_DIR/podkop-bin" <<'SH'
|
||||
cat >"$WORK_DIR/legacy-bin" <<'SH'
|
||||
#!/usr/bin/env sh
|
||||
printf '%s\n' "$*" >> "$PODKOP_INSTALLER_BIN_LOG"
|
||||
printf '%s\n' "$*" >> "$FORKOP_INSTALLER_BIN_LOG"
|
||||
case "$1" in
|
||||
get_status) printf '%s\n' '{"running":1}' ;;
|
||||
esac
|
||||
exit 0
|
||||
SH
|
||||
chmod 0755 "$WORK_DIR/podkop-bin"
|
||||
chmod 0755 "$WORK_DIR/legacy-bin"
|
||||
|
||||
: >"$WORK_DIR/init.log"
|
||||
: >"$WORK_DIR/bin.log"
|
||||
state="$WORK_DIR/state.env"
|
||||
PODKOP_INSTALLER_PODKOP_PLUS_INIT="$WORK_DIR/podkop-plus-init" \
|
||||
PODKOP_INSTALLER_PODKOP_PLUS_BIN="$WORK_DIR/podkop-bin" \
|
||||
PODKOP_INSTALLER_PODKOP_PLUS_LIB="$WORK_DIR/podkop-lib" \
|
||||
PODKOP_INSTALLER_PODKOP_PLUS_UCI_DEFAULTS="$WORK_DIR/uci-defaults" \
|
||||
PODKOP_INSTALLER_PODKOP_PLUS_LUCI_VIEW="$WORK_DIR/luci-view" \
|
||||
PODKOP_INSTALLER_MENU_JSON="$WORK_DIR/menu.json" \
|
||||
PODKOP_INSTALLER_ACL_JSON="$WORK_DIR/acl.json" \
|
||||
PODKOP_INSTALLER_RU_LMO="$WORK_DIR/ru.lmo" \
|
||||
PODKOP_INSTALLER_EN_LMO="$WORK_DIR/en.lmo" \
|
||||
PODKOP_INSTALLER_RU_LUA="$WORK_DIR/ru.lua" \
|
||||
PODKOP_INSTALLER_EN_LUA="$WORK_DIR/en.lua" \
|
||||
PODKOP_INSTALLER_ORIGINAL_PODKOP_INIT="$WORK_DIR/missing-original-init" \
|
||||
PODKOP_INSTALLER_INIT_LOG="$WORK_DIR/init.log" \
|
||||
PODKOP_INSTALLER_BIN_LOG="$WORK_DIR/bin.log" \
|
||||
PODKOP_UCI_STATE_FILE="$WORK_DIR/empty-uci.state" \
|
||||
PATH="$WORK_DIR:$PATH" \
|
||||
FORKOP_INSTALLER_INIT="$WORK_DIR/missing-forkop-init" \
|
||||
FORKOP_INSTALLER_BIN="$WORK_DIR/missing-forkop-bin" \
|
||||
FORKOP_INSTALLER_LIB="$WORK_DIR/missing-forkop-lib" \
|
||||
FORKOP_INSTALLER_UCI_DEFAULTS="$WORK_DIR/uci-defaults" \
|
||||
FORKOP_INSTALLER_LUCI_VIEW="$WORK_DIR/luci-view" \
|
||||
FORKOP_INSTALLER_MENU_JSON="$WORK_DIR/menu.json" \
|
||||
FORKOP_INSTALLER_ACL_JSON="$WORK_DIR/acl.json" \
|
||||
FORKOP_INSTALLER_RU_LMO="$WORK_DIR/ru.lmo" \
|
||||
FORKOP_INSTALLER_EN_LMO="$WORK_DIR/en.lmo" \
|
||||
FORKOP_INSTALLER_RU_LUA="$WORK_DIR/ru.lua" \
|
||||
FORKOP_INSTALLER_EN_LUA="$WORK_DIR/en.lua" \
|
||||
FORKOP_INSTALLER_LEGACY_BASE_INIT="$WORK_DIR/missing-original-init" \
|
||||
FORKOP_INSTALLER_LEGACY_BRAND="$LEGACY_BRAND" \
|
||||
FORKOP_INSTALLER_LEGACY_BACKEND="$LEGACY_BACKEND" \
|
||||
FORKOP_INSTALLER_LEGACY_CONFIG_ALT="$LEGACY_CONFIG_ALT" \
|
||||
FORKOP_INSTALLER_LEGACY_INIT="$WORK_DIR/legacy-init" \
|
||||
FORKOP_INSTALLER_LEGACY_BIN="$WORK_DIR/legacy-bin" \
|
||||
FORKOP_INSTALLER_LEGACY_LIB="$WORK_DIR/legacy-lib" \
|
||||
FORKOP_INSTALLER_FAKE_LEGACY_BACKEND="$LEGACY_BACKEND" \
|
||||
FORKOP_INSTALLER_OPKG_LOG="$WORK_DIR/opkg.log" \
|
||||
FORKOP_INSTALLER_INIT_LOG="$WORK_DIR/init.log" \
|
||||
FORKOP_INSTALLER_BIN_LOG="$WORK_DIR/bin.log" \
|
||||
FORKOP_UCI_STATE_FILE="$WORK_DIR/empty-uci.state" \
|
||||
ucode "$helper" installer-cleanup-legacy >"$state"
|
||||
|
||||
grep -Fxq 'PODKOP_WAS_ENABLED=1' "$state" ||
|
||||
grep -Fxq 'FORKOP_WAS_ENABLED=1' "$state" ||
|
||||
fail "installer cleanup must export previous enabled state"
|
||||
grep -Fxq 'PODKOP_WAS_RUNNING=1' "$state" ||
|
||||
grep -Fxq 'FORKOP_WAS_RUNNING=1' "$state" ||
|
||||
fail "installer cleanup must export previous running state"
|
||||
grep -Fxq 'FORKOP_LEGACY_DETECTED=1' "$state" ||
|
||||
fail "installer cleanup must report the legacy package transition"
|
||||
grep -Fxq 'stop' "$WORK_DIR/init.log" ||
|
||||
fail "installer cleanup must stop old Podkop Plus through ucode owner"
|
||||
fail "installer cleanup must stop the legacy service through ucode owner"
|
||||
grep -Fxq 'disable' "$WORK_DIR/init.log" ||
|
||||
fail "installer cleanup must disable old Podkop Plus through ucode owner"
|
||||
fail "installer cleanup must disable the legacy service through ucode owner"
|
||||
grep -Fxq 'restore_dnsmasq' "$WORK_DIR/bin.log" ||
|
||||
fail "installer cleanup must prefer backend restore_dnsmasq"
|
||||
grep -Fxq "$LEGACY_BACKEND" "$WORK_DIR/opkg.log" ||
|
||||
fail "installer cleanup must remove the legacy backend package"
|
||||
grep -Fxq "luci-app-$LEGACY_BACKEND" "$WORK_DIR/opkg.log" ||
|
||||
fail "installer cleanup must remove the legacy LuCI package"
|
||||
|
||||
write_fake_podkop_init
|
||||
if printf '%s\n' '1' |
|
||||
PATH="$WORK_DIR:$PATH" \
|
||||
FORKOP_INSTALLER_OPKG_LOG="$WORK_DIR/opkg.log" \
|
||||
FORKOP_INSTALLER_FAIL_REMOVE="$LEGACY_BACKEND" \
|
||||
FORKOP_INSTALLER_FAKE_LEGACY_BACKEND="$LEGACY_BACKEND" \
|
||||
FORKOP_INSTALLER_LEGACY_BRAND="$LEGACY_BRAND" \
|
||||
FORKOP_INSTALLER_LEGACY_BACKEND="$LEGACY_BACKEND" \
|
||||
FORKOP_UCI_STATE_FILE="$WORK_DIR/empty-uci.state" \
|
||||
ucode "$helper" installer-cleanup-legacy >/dev/null 2>&1; then
|
||||
fail "installer cleanup must stop when the package manager cannot remove the legacy backend"
|
||||
fi
|
||||
|
||||
legacy_config="$WORK_DIR/legacy-config"
|
||||
legacy_config_alt="$WORK_DIR/legacy-config-alt"
|
||||
legacy_persistent="$WORK_DIR/legacy-persistent"
|
||||
legacy_runtime="$WORK_DIR/legacy-runtime"
|
||||
legacy_tmp="$WORK_DIR/legacy-tmp"
|
||||
legacy_tmp_alt="$WORK_DIR/legacy-tmp-alt"
|
||||
legacy_base_config="$WORK_DIR/legacy-base-config"
|
||||
legacy_base_persistent="$WORK_DIR/legacy-base-persistent"
|
||||
legacy_base_runtime="$WORK_DIR/legacy-base-runtime"
|
||||
legacy_base_tmp="$WORK_DIR/legacy-base-tmp"
|
||||
legacy_base_init="$WORK_DIR/legacy-base-init"
|
||||
legacy_base_bin="$WORK_DIR/legacy-base-bin"
|
||||
legacy_base_lib="$WORK_DIR/legacy-base-lib"
|
||||
legacy_base_uci_defaults="$WORK_DIR/legacy-base-uci-defaults"
|
||||
legacy_base_luci_view="$WORK_DIR/legacy-base-luci-view"
|
||||
legacy_base_menu="$WORK_DIR/legacy-base-menu"
|
||||
legacy_base_acl="$WORK_DIR/legacy-base-acl"
|
||||
legacy_base_i18n="$WORK_DIR/legacy-base-i18n"
|
||||
legacy_tmp_package="$WORK_DIR/luci-app-${LEGACY_BRAND}.ipk"
|
||||
legacy_scan_root="$WORK_DIR/legacy-scan-root"
|
||||
legacy_nested_uci="$legacy_scan_root/.uci/${LEGACY_BACKEND}"
|
||||
legacy_nested_lock="$legacy_scan_root/lock/procd_${LEGACY_BACKEND}.lock"
|
||||
legacy_nested_backup="$legacy_scan_root/audit/${LEGACY_BACKEND}.config"
|
||||
mkdir -p "$legacy_persistent" "$legacy_runtime" "$legacy_tmp" "$legacy_tmp_alt"
|
||||
mkdir -p "$(dirname "$legacy_nested_uci")" "$(dirname "$legacy_nested_lock")" "$(dirname "$legacy_nested_backup")"
|
||||
touch "$legacy_config" "$legacy_config_alt" "$legacy_config-opkg"
|
||||
touch \
|
||||
"$legacy_base_config.backup" \
|
||||
"$legacy_base_persistent.cache" \
|
||||
"$legacy_base_runtime.internal" \
|
||||
"$legacy_base_tmp.log" \
|
||||
"$legacy_base_init.old" \
|
||||
"$legacy_base_bin.bak" \
|
||||
"$legacy_base_lib.prev" \
|
||||
"$legacy_base_uci_defaults.done" \
|
||||
"$legacy_base_luci_view.old" \
|
||||
"$legacy_base_menu.json" \
|
||||
"$legacy_base_acl.json" \
|
||||
"$legacy_base_i18n.ru.lmo" \
|
||||
"$legacy_tmp_package" \
|
||||
"$legacy_nested_uci" \
|
||||
"$legacy_nested_lock" \
|
||||
"$legacy_nested_backup"
|
||||
FORKOP_INSTALLER_LEGACY_BRAND="$LEGACY_BRAND" \
|
||||
FORKOP_INSTALLER_LEGACY_BACKEND="$LEGACY_BACKEND" \
|
||||
FORKOP_INSTALLER_LEGACY_CONFIG_ALT="$LEGACY_CONFIG_ALT" \
|
||||
FORKOP_INSTALLER_LEGACY_CONFIG="$legacy_config" \
|
||||
FORKOP_INSTALLER_LEGACY_CONFIG_FILE_ALT="$legacy_config_alt" \
|
||||
FORKOP_INSTALLER_LEGACY_PERSISTENT_DIR="$legacy_persistent" \
|
||||
FORKOP_INSTALLER_LEGACY_RUNTIME_DIR="$legacy_runtime" \
|
||||
FORKOP_INSTALLER_LEGACY_TMP_DIR="$legacy_tmp" \
|
||||
FORKOP_INSTALLER_LEGACY_TMP_ALT_DIR="$legacy_tmp_alt" \
|
||||
FORKOP_INSTALLER_LEGACY_BASE_CONFIG="$legacy_base_config" \
|
||||
FORKOP_INSTALLER_LEGACY_BASE_PERSISTENT_DIR="$legacy_base_persistent" \
|
||||
FORKOP_INSTALLER_LEGACY_BASE_RUNTIME_DIR="$legacy_base_runtime" \
|
||||
FORKOP_INSTALLER_LEGACY_BASE_TMP_DIR="$legacy_base_tmp" \
|
||||
FORKOP_INSTALLER_LEGACY_BASE_INIT="$legacy_base_init" \
|
||||
FORKOP_INSTALLER_LEGACY_BASE_BIN="$legacy_base_bin" \
|
||||
FORKOP_INSTALLER_LEGACY_BASE_LIB="$legacy_base_lib" \
|
||||
FORKOP_INSTALLER_LEGACY_BASE_UCI_DEFAULTS="$legacy_base_uci_defaults" \
|
||||
FORKOP_INSTALLER_LEGACY_BASE_LUCI_VIEW="$legacy_base_luci_view" \
|
||||
FORKOP_INSTALLER_LEGACY_BASE_MENU_JSON="$legacy_base_menu" \
|
||||
FORKOP_INSTALLER_LEGACY_BASE_ACL_JSON="$legacy_base_acl" \
|
||||
FORKOP_INSTALLER_LEGACY_BASE_I18N="$legacy_base_i18n" \
|
||||
FORKOP_INSTALLER_LEGACY_TMP_PACKAGE_GLOB="$WORK_DIR/*${LEGACY_BRAND}*" \
|
||||
FORKOP_INSTALLER_LEGACY_SCAN_ROOTS="$legacy_scan_root" \
|
||||
ucode "$helper" installer-finalize-legacy
|
||||
for path in "$legacy_config" "$legacy_config_alt" "$legacy_config-opkg" \
|
||||
"$legacy_persistent" "$legacy_runtime" "$legacy_tmp" "$legacy_tmp_alt" \
|
||||
"$legacy_base_config.backup" "$legacy_base_persistent.cache" \
|
||||
"$legacy_base_runtime.internal" "$legacy_base_tmp.log" \
|
||||
"$legacy_base_init.old" "$legacy_base_bin.bak" "$legacy_base_lib.prev" \
|
||||
"$legacy_base_uci_defaults.done" "$legacy_base_luci_view.old" \
|
||||
"$legacy_base_menu.json" "$legacy_base_acl.json" "$legacy_base_i18n.ru.lmo" \
|
||||
"$legacy_tmp_package" "$legacy_nested_uci" "$legacy_nested_lock" \
|
||||
"$legacy_nested_backup"; do
|
||||
[ ! -e "$path" ] || fail "installer finalization left a legacy path behind: $path"
|
||||
done
|
||||
|
||||
write_fake_service_init "$WORK_DIR/forkop-init"
|
||||
touch "$WORK_DIR/luci-indexcache.one" "$WORK_DIR/luci-indexcache.two"
|
||||
: >"$WORK_DIR/init.log"
|
||||
PODKOP_INSTALLER_PODKOP_PLUS_INIT="$WORK_DIR/podkop-plus-init" \
|
||||
PODKOP_INSTALLER_RPCD_INIT="$WORK_DIR/missing-rpcd" \
|
||||
PODKOP_INSTALLER_LUCI_CACHE_GLOBS="$WORK_DIR/luci-indexcache*" \
|
||||
PODKOP_INSTALLER_LATEST_VERSION_CACHE="$WORK_DIR/latest.cache" \
|
||||
PODKOP_INSTALLER_SYSTEM_INFO_CACHE="$WORK_DIR/system-info.json" \
|
||||
PODKOP_INSTALLER_SERVER_COUNTRY_CACHE="$WORK_DIR/server-country.json" \
|
||||
PODKOP_INSTALLER_SING_BOX_VERSION_CACHE="$WORK_DIR/sing-box-version" \
|
||||
PODKOP_INSTALLER_TMP_SYSTEM_INFO_CACHE="$WORK_DIR/tmp-system-info.json" \
|
||||
PODKOP_WAS_ENABLED=1 \
|
||||
PODKOP_WAS_RUNNING=1 \
|
||||
PODKOP_INSTALLER_INIT_LOG="$WORK_DIR/init.log" \
|
||||
FORKOP_INSTALLER_INIT="$WORK_DIR/forkop-init" \
|
||||
FORKOP_INSTALLER_RPCD_INIT="$WORK_DIR/missing-rpcd" \
|
||||
FORKOP_INSTALLER_LUCI_CACHE_GLOBS="$WORK_DIR/luci-indexcache*" \
|
||||
FORKOP_INSTALLER_LATEST_VERSION_CACHE="$WORK_DIR/latest.cache" \
|
||||
FORKOP_INSTALLER_SYSTEM_INFO_CACHE="$WORK_DIR/system-info.json" \
|
||||
FORKOP_INSTALLER_SERVER_COUNTRY_CACHE="$WORK_DIR/server-country.json" \
|
||||
FORKOP_INSTALLER_SING_BOX_VERSION_CACHE="$WORK_DIR/sing-box-version" \
|
||||
FORKOP_INSTALLER_TMP_SYSTEM_INFO_CACHE="$WORK_DIR/tmp-system-info.json" \
|
||||
FORKOP_WAS_ENABLED=1 \
|
||||
FORKOP_WAS_RUNNING=1 \
|
||||
FORKOP_INSTALLER_INIT_LOG="$WORK_DIR/init.log" \
|
||||
ucode "$helper" installer-post-install
|
||||
|
||||
if compgen -G "$WORK_DIR/luci-indexcache*" >/dev/null; then
|
||||
|
||||
@@ -12,8 +12,8 @@ reject_runtime_pattern() {
|
||||
local pattern="$1"
|
||||
local label="$2"
|
||||
|
||||
if rg -n --fixed-strings "$pattern" "$ROOT_DIR/podkop/files" >/dev/null 2>&1; then
|
||||
rg -n --fixed-strings "$pattern" "$ROOT_DIR/podkop/files" >&2
|
||||
if rg -n --fixed-strings "$pattern" "$ROOT_DIR/forkop/files" >/dev/null 2>&1; then
|
||||
rg -n --fixed-strings "$pattern" "$ROOT_DIR/forkop/files" >&2
|
||||
fail "$label"
|
||||
fi
|
||||
}
|
||||
@@ -22,8 +22,8 @@ reject_runtime_regex() {
|
||||
local pattern="$1"
|
||||
local label="$2"
|
||||
|
||||
if rg -n "$pattern" "$ROOT_DIR/podkop/files" >/dev/null 2>&1; then
|
||||
rg -n "$pattern" "$ROOT_DIR/podkop/files" >&2
|
||||
if rg -n "$pattern" "$ROOT_DIR/forkop/files" >/dev/null 2>&1; then
|
||||
rg -n "$pattern" "$ROOT_DIR/forkop/files" >&2
|
||||
fail "$label"
|
||||
fi
|
||||
}
|
||||
@@ -44,7 +44,7 @@ reject_runtime_pattern "Current sing-box config hash:" \
|
||||
"sing-box config logs must not emit duplicate hash lines"
|
||||
reject_runtime_pattern "Temporary sing-box config hash:" \
|
||||
"sing-box config logs must not emit duplicate hash lines"
|
||||
reject_runtime_pattern "Podkop Plus did not reach a stable running state after start" \
|
||||
reject_runtime_pattern "Forkop did not reach a stable running state after start" \
|
||||
"startup rollback logs must not be fatal secondary errors"
|
||||
reject_runtime_pattern "Flush nft" \
|
||||
"stop logs must not expose routine nft cleanup as user-facing info"
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
VALIDATOR="$PODKOP_LIB/config/validator.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
VALIDATOR="$FORKOP_LIB/config/validator.uc"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -42,7 +42,7 @@ assert_mark_range_no_overlap() {
|
||||
done
|
||||
}
|
||||
|
||||
eval "$(ucode -L "$PODKOP_LIB" "$PODKOP_LIB/core/constants.uc" shell-env)"
|
||||
eval "$(ucode -L "$FORKOP_LIB" "$FORKOP_LIB/core/constants.uc" shell-env)"
|
||||
|
||||
assert_mark_range_no_overlap "Zapret" "$((ZAPRET_ROUTE_MARK_BASE))" "$ZAPRET_QUEUE_RANGE_SIZE" "$((NFT_FAKEIP_MARK))" "FakeIP"
|
||||
assert_mark_range_no_overlap "Zapret" "$((ZAPRET_ROUTE_MARK_BASE))" "$ZAPRET_QUEUE_RANGE_SIZE" "$((NFT_OUTBOUND_MARK))" "outbound"
|
||||
@@ -78,9 +78,9 @@ context_json() {
|
||||
JSON
|
||||
}
|
||||
|
||||
PODKOP_LIB="$PODKOP_LIB" ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-runtime-fixture "$WORK_DIR/fixture.json" "$(context_json)"
|
||||
FORKOP_LIB="$FORKOP_LIB" ucode -L "$FORKOP_LIB" "$VALIDATOR" validate-runtime-fixture "$WORK_DIR/fixture.json" "$(context_json)"
|
||||
|
||||
if PODKOP_LIB="$PODKOP_LIB" ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-runtime-fixture "$WORK_DIR/fixture.json" "$(context_json "0x01100000")" >/dev/null 2>&1; then
|
||||
if FORKOP_LIB="$FORKOP_LIB" ucode -L "$FORKOP_LIB" "$VALIDATOR" validate-runtime-fixture "$WORK_DIR/fixture.json" "$(context_json "0x01100000")" >/dev/null 2>&1; then
|
||||
fail "legacy Zapret2 route mark base should overlap FakeIP mark"
|
||||
fi
|
||||
|
||||
|
||||
+117
-117
@@ -2,8 +2,8 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
NFT_RUNTIME="$ROOT_DIR/podkop/files/usr/lib/nft/apply.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
NFT_RUNTIME="$ROOT_DIR/forkop/files/usr/lib/nft/apply.uc"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
NFT_LOG="$WORK_DIR/nft.log"
|
||||
LOGGER_LOG="$WORK_DIR/logger.log"
|
||||
@@ -11,7 +11,7 @@ IP_LOG="$WORK_DIR/ip.log"
|
||||
SYSCTL_LOG="$WORK_DIR/sysctl.log"
|
||||
|
||||
nft_ucode() {
|
||||
ucode -L "$PODKOP_LIB" "$NFT_RUNTIME" "$@"
|
||||
ucode -L "$FORKOP_LIB" "$NFT_RUNTIME" "$@"
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
@@ -171,10 +171,10 @@ export LOGGER_LOG
|
||||
export IP_LOG
|
||||
export SYSCTL_LOG
|
||||
|
||||
assert_eq "/tmp/podkop-cache/condition_rule_1_domain_domains" \
|
||||
"$(nft_ucode cache-path 1 /tmp/podkop-cache condition rule_1 domain domains)" \
|
||||
assert_eq "/tmp/forkop-cache/condition_rule_1_domain_domains" \
|
||||
"$(nft_ucode cache-path 1 /tmp/forkop-cache condition rule_1 domain domains)" \
|
||||
"cache path"
|
||||
if nft_ucode cache-path 1 /tmp/podkop-cache condition 'bad-name' domain domains >/dev/null 2>&1; then
|
||||
if nft_ucode cache-path 1 /tmp/forkop-cache condition 'bad-name' domain domains >/dev/null 2>&1; then
|
||||
fail "unsafe cache key should fail"
|
||||
fi
|
||||
|
||||
@@ -206,55 +206,55 @@ assert_eq "^xn--80aswg[.]xn--p1ai$" \
|
||||
"$(nft_ucode rule-condition-csv domain_regex generic 0 0 '' '' 'сайт.рф full:пример.испытание keyword:пример regex:^сайт[.]рф$' '')" \
|
||||
"combined IDN regex is punycoded"
|
||||
|
||||
nft_ucode nft-create-runtime-base PodkopPlusTable localv4 podkop_plus_subnets podkop_plus_ports podkop_plus_ip_ports podkop_plus_interfaces "br-lan tun0" 0x00100000 0x00200000 198.18.0.0/15 1602 1
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\ttable\tinet\tPodkopPlusTable' "runtime table"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tPodkopPlusTable\tlocalv4\t{ type ipv4_addr; flags interval; auto-merge; }' "runtime localv4 set"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tPodkopPlusTable\tlocalv6\t{ type ipv6_addr; flags interval; auto-merge; }' "runtime localv6 set"
|
||||
nft_ucode nft-create-runtime-base ForkopTable localv4 forkop_subnets forkop_ports forkop_ip_ports forkop_interfaces "br-lan tun0" 0x00100000 0x00200000 198.18.0.0/15 1602 1
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\ttable\tinet\tForkopTable' "runtime table"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tForkopTable\tlocalv4\t{ type ipv4_addr; flags interval; auto-merge; }' "runtime localv4 set"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tForkopTable\tlocalv6\t{ type ipv6_addr; flags interval; auto-merge; }' "runtime localv6 set"
|
||||
assert_contains "$NFT_LOG" '0.0.0.0/8,10.0.0.0/8,127.0.0.0/8' "runtime localv4 elements"
|
||||
assert_contains "$NFT_LOG" '::/128,::1/128,64:ff9b::/96' "runtime localv6 elements"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tPodkopPlusTable\tpodkop_plus_interfaces\t{ type ifname; flags interval; }' "runtime interface set"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_interfaces\t{ br-lan }' "runtime br-lan interface"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_interfaces\t{ tun0 }' "runtime tun0 interface"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tchain\tinet\tPodkopPlusTable\tmangle\t{ type filter hook prerouting priority -150; policy accept; }' "runtime mangle chain"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tchain\tinet\tPodkopPlusTable\tpriority_rules\t{ }' "runtime priority chain"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tchain\tinet\tPodkopPlusTable\tpriority_output_rules\t{ }' "runtime priority output chain"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle\tjump\tpriority_rules' "runtime priority jump"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle\tiifname\t@podkop_plus_interfaces\tip\tdaddr\t@podkop_plus_subnets\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime common tcp rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle\tiifname\t@podkop_plus_interfaces\tip6\tdaddr\t@podkop_plus_subnets6\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime common6 tcp rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tproxy\tmeta\tmark\t&\t0x00100000\t==\t0x00100000\tmeta\tl4proto\ttcp\ttproxy\tip\tto\t:1602\tcounter' "runtime proxy tcp rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tproxy\tmeta\tmark\t&\t0x00100000\t==\t0x00100000\tmeta\tl4proto\ttcp\ttproxy\tip6\tto\t[::1]:1602\tcounter' "runtime proxy6 tcp rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\tmeta\tmark\t0x00200000\tcounter\treturn' "runtime outbound return"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\tjump\tpriority_output_rules' "runtime priority output jump"
|
||||
assert_contains "$NFT_LOG" $'nft\tinsert\trule\tinet\tPodkopPlusTable\tmangle\tudp\tdport\t123\treturn' "runtime ntp exclusion"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tForkopTable\tforkop_interfaces\t{ type ifname; flags interval; }' "runtime interface set"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_interfaces\t{ br-lan }' "runtime br-lan interface"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_interfaces\t{ tun0 }' "runtime tun0 interface"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tchain\tinet\tForkopTable\tmangle\t{ type filter hook prerouting priority -150; policy accept; }' "runtime mangle chain"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tchain\tinet\tForkopTable\tpriority_rules\t{ }' "runtime priority chain"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tchain\tinet\tForkopTable\tpriority_output_rules\t{ }' "runtime priority output chain"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle\tjump\tpriority_rules' "runtime priority jump"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle\tiifname\t@forkop_interfaces\tip\tdaddr\t@forkop_subnets\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime common tcp rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle\tiifname\t@forkop_interfaces\tip6\tdaddr\t@forkop_subnets6\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime common6 tcp rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tproxy\tmeta\tmark\t&\t0x00100000\t==\t0x00100000\tmeta\tl4proto\ttcp\ttproxy\tip\tto\t:1602\tcounter' "runtime proxy tcp rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tproxy\tmeta\tmark\t&\t0x00100000\t==\t0x00100000\tmeta\tl4proto\ttcp\ttproxy\tip6\tto\t[::1]:1602\tcounter' "runtime proxy6 tcp rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle_output\tmeta\tmark\t0x00200000\tcounter\treturn' "runtime outbound return"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle_output\tjump\tpriority_output_rules' "runtime priority output jump"
|
||||
assert_contains "$NFT_LOG" $'nft\tinsert\trule\tinet\tForkopTable\tmangle\tudp\tdport\t123\treturn' "runtime ntp exclusion"
|
||||
|
||||
cat >"$WORK_DIR/runtime-base-uci.state" <<'EOF_UCI'
|
||||
podkop-plus.settings=settings
|
||||
podkop-plus.settings.source_network_interfaces=br-lan tun0
|
||||
podkop-plus.settings.exclude_ntp=1
|
||||
forkop.settings=settings
|
||||
forkop.settings.source_network_interfaces=br-lan tun0
|
||||
forkop.settings.exclude_ntp=1
|
||||
EOF_UCI
|
||||
: > "$NFT_LOG"
|
||||
PODKOP_UCI_STATE_FILE="$WORK_DIR/runtime-base-uci.state" \
|
||||
nft_ucode nft-create-runtime-base-from-uci PodkopPlusTable localv4 podkop_plus_subnets podkop_plus_ports podkop_plus_ip_ports podkop_plus_interfaces 0x00100000 0x00200000 198.18.0.0/15 1602
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_interfaces\t{ br-lan }' "runtime base from UCI br-lan interface"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_interfaces\t{ tun0 }' "runtime base from UCI tun0 interface"
|
||||
assert_contains "$NFT_LOG" $'nft\tinsert\trule\tinet\tPodkopPlusTable\tmangle\tudp\tdport\t123\treturn' "runtime base from UCI ntp exclusion"
|
||||
FORKOP_UCI_STATE_FILE="$WORK_DIR/runtime-base-uci.state" \
|
||||
nft_ucode nft-create-runtime-base-from-uci ForkopTable localv4 forkop_subnets forkop_ports forkop_ip_ports forkop_interfaces 0x00100000 0x00200000 198.18.0.0/15 1602
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_interfaces\t{ br-lan }' "runtime base from UCI br-lan interface"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_interfaces\t{ tun0 }' "runtime base from UCI tun0 interface"
|
||||
assert_contains "$NFT_LOG" $'nft\tinsert\trule\tinet\tForkopTable\tmangle\tudp\tdport\t123\treturn' "runtime base from UCI ntp exclusion"
|
||||
|
||||
: > "$NFT_LOG"
|
||||
nft_ucode nft-create-runtime-output-rules PodkopPlusTable localv4 podkop_plus_subnets podkop_plus_ports podkop_plus_ip_ports 0x00100000 198.18.0.0/15
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\tip\tdaddr\t@podkop_plus_subnets\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output common tcp"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\tip6\tdaddr\t@podkop_plus_subnets6\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output common6 tcp"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\tip\tdaddr\t.\ttcp\tdport\t@podkop_plus_ip_ports\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output ip-port tcp"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\tip6\tdaddr\t.\ttcp\tdport\t@podkop_plus_ip6_ports\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output ip6-port tcp"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\ttcp\tdport\t@podkop_plus_ports\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output port tcp"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\tip\tdaddr\t198.18.0.0/15\tmeta\tl4proto\tudp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output fakeip udp"
|
||||
nft_ucode nft-create-runtime-output-rules ForkopTable localv4 forkop_subnets forkop_ports forkop_ip_ports 0x00100000 198.18.0.0/15
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle_output\tip\tdaddr\t@forkop_subnets\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output common tcp"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle_output\tip6\tdaddr\t@forkop_subnets6\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output common6 tcp"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle_output\tip\tdaddr\t.\ttcp\tdport\t@forkop_ip_ports\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output ip-port tcp"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle_output\tip6\tdaddr\t.\ttcp\tdport\t@forkop_ip6_ports\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output ip6-port tcp"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle_output\ttcp\tdport\t@forkop_ports\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output port tcp"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle_output\tip\tdaddr\t198.18.0.0/15\tmeta\tl4proto\tudp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output fakeip udp"
|
||||
|
||||
: > "$NFT_LOG"
|
||||
if NFT_LIST_TABLE_FAIL=1 nft_ucode nft-table-present-fixture PodkopPlusTable 2>"$WORK_DIR/nft-table-present.err"; then
|
||||
if NFT_LIST_TABLE_FAIL=1 nft_ucode nft-table-present-fixture ForkopTable 2>"$WORK_DIR/nft-table-present.err"; then
|
||||
fail "missing nft table should return false"
|
||||
fi
|
||||
[ ! -s "$WORK_DIR/nft-table-present.err" ] ||
|
||||
fail "missing nft table predicate must suppress nft stderr"
|
||||
assert_contains "$NFT_LOG" $'nft\tlist\ttable\tinet\tPodkopPlusTable' "nft table presence check"
|
||||
assert_contains "$NFT_LOG" $'nft\tlist\ttable\tinet\tForkopTable' "nft table presence check"
|
||||
|
||||
cat >"$WORK_DIR/provider-rules.json" <<'JSON'
|
||||
{
|
||||
@@ -272,16 +272,16 @@ printf '#!/usr/bin/env sh\nexit 0\n' >"$provider_bin"
|
||||
chmod 0755 "$provider_bin"
|
||||
|
||||
: > "$NFT_LOG"
|
||||
nft_ucode nft-create-provider-output-rules-fixture "$WORK_DIR/provider-rules.json" PodkopPlusTable zapret "$provider_bin" 0x01000000 4000 0x40000000 0x20000000
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\tmeta\tmark\t&\t0x40000000\t==\t0x40000000\treturn' "zapret desync return"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\tmeta\tmark\t0x01000001\tmeta\tl4proto\ttcp\tcounter\tqueue\tnum\t4000\tbypass' "zapret first tcp queue rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\tmeta\tmark\t0x01000002\tmeta\tl4proto\tudp\tcounter\tqueue\tnum\t4001\tbypass' "zapret second udp queue rule"
|
||||
nft_ucode nft-create-provider-output-rules-fixture "$WORK_DIR/provider-rules.json" ForkopTable zapret "$provider_bin" 0x01000000 4000 0x40000000 0x20000000
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle_output\tmeta\tmark\t&\t0x40000000\t==\t0x40000000\treturn' "zapret desync return"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle_output\tmeta\tmark\t0x01000001\tmeta\tl4proto\ttcp\tcounter\tqueue\tnum\t4000\tbypass' "zapret first tcp queue rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle_output\tmeta\tmark\t0x01000002\tmeta\tl4proto\tudp\tcounter\tqueue\tnum\t4001\tbypass' "zapret second udp queue rule"
|
||||
if grep -Fq 'zapret_disabled' "$NFT_LOG"; then
|
||||
fail "disabled provider section should not create nft rule"
|
||||
fi
|
||||
|
||||
: > "$NFT_LOG"
|
||||
nft_ucode nft-create-provider-output-rules-fixture "$WORK_DIR/provider-rules.json" PodkopPlusTable zapret "$WORK_DIR/bin/missing-provider" 0x01000000 4000 0x40000000 0x20000000
|
||||
nft_ucode nft-create-provider-output-rules-fixture "$WORK_DIR/provider-rules.json" ForkopTable zapret "$WORK_DIR/bin/missing-provider" 0x01000000 4000 0x40000000 0x20000000
|
||||
if [ -s "$NFT_LOG" ]; then
|
||||
fail "missing provider binary should skip provider nft output rules"
|
||||
fi
|
||||
@@ -315,26 +315,26 @@ cat >"$WORK_DIR/priority-rules.json" <<'JSON'
|
||||
JSON
|
||||
|
||||
: > "$NFT_LOG"
|
||||
nft_ucode nft-add-section-priority-rules-fixture "$WORK_DIR/priority-rules.json" PodkopPlusTable podkop_plus_interfaces localv4 localv6 0x00100000
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tPodkopPlusTable\tpodkop_plus_rule_bypass_first_subnets\t{ type ipv4_addr; flags interval; auto-merge; }' "bypass priority subnet set"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tPodkopPlusTable\tpodkop_plus_rule_wide_proxy_subnets\t{ type ipv4_addr; flags interval; auto-merge; }' "proxy priority subnet set"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tpriority_rules\tiifname\t@podkop_plus_interfaces\tip\tdaddr\t!=\t@localv4\tip\tdaddr\t@podkop_plus_rule_bypass_first_subnets\tcounter\taccept' "bypass priority accept rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tpriority_rules\tiifname\t@podkop_plus_interfaces\tip\tdaddr\t!=\t@localv4\tip\tdaddr\t@podkop_plus_rule_wide_proxy_subnets\tmeta\tmark\tset\t0x00100000\tcounter\taccept' "proxy priority capture rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tpriority_rules\tiifname\t@podkop_plus_interfaces\tip\tdaddr\t!=\t@localv4\ttcp\tdport\t@podkop_plus_rule_port_bypass_ports\tcounter\taccept' "port-only bypass priority rule"
|
||||
nft_ucode nft-add-section-priority-rules-fixture "$WORK_DIR/priority-rules.json" ForkopTable forkop_interfaces localv4 localv6 0x00100000
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tForkopTable\tforkop_rule_bypass_first_subnets\t{ type ipv4_addr; flags interval; auto-merge; }' "bypass priority subnet set"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tForkopTable\tforkop_rule_wide_proxy_subnets\t{ type ipv4_addr; flags interval; auto-merge; }' "proxy priority subnet set"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tpriority_rules\tiifname\t@forkop_interfaces\tip\tdaddr\t!=\t@localv4\tip\tdaddr\t@forkop_rule_bypass_first_subnets\tcounter\taccept' "bypass priority accept rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tpriority_rules\tiifname\t@forkop_interfaces\tip\tdaddr\t!=\t@localv4\tip\tdaddr\t@forkop_rule_wide_proxy_subnets\tmeta\tmark\tset\t0x00100000\tcounter\taccept' "proxy priority capture rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tpriority_rules\tiifname\t@forkop_interfaces\tip\tdaddr\t!=\t@localv4\ttcp\tdport\t@forkop_rule_port_bypass_ports\tcounter\taccept' "port-only bypass priority rule"
|
||||
assert_line_before "$NFT_LOG" \
|
||||
$'nft\tadd\trule\tinet\tPodkopPlusTable\tpriority_rules\tiifname\t@podkop_plus_interfaces\tip\tdaddr\t!=\t@localv4\tip\tdaddr\t@podkop_plus_rule_bypass_first_subnets\tcounter\taccept' \
|
||||
$'nft\tadd\trule\tinet\tPodkopPlusTable\tpriority_rules\tiifname\t@podkop_plus_interfaces\tip\tdaddr\t!=\t@localv4\tip\tdaddr\t@podkop_plus_rule_wide_proxy_subnets\tmeta\tmark\tset\t0x00100000\tcounter\taccept' \
|
||||
$'nft\tadd\trule\tinet\tForkopTable\tpriority_rules\tiifname\t@forkop_interfaces\tip\tdaddr\t!=\t@localv4\tip\tdaddr\t@forkop_rule_bypass_first_subnets\tcounter\taccept' \
|
||||
$'nft\tadd\trule\tinet\tForkopTable\tpriority_rules\tiifname\t@forkop_interfaces\tip\tdaddr\t!=\t@localv4\tip\tdaddr\t@forkop_rule_wide_proxy_subnets\tmeta\tmark\tset\t0x00100000\tcounter\taccept' \
|
||||
"bypass priority order"
|
||||
|
||||
: > "$IP_LOG"
|
||||
: > "$LOGGER_LOG"
|
||||
rt_tables="$WORK_DIR/rt_tables"
|
||||
IP_ROUTE_OUTPUT='' IP_ROUTE6_OUTPUT='' IP_RULE_OUTPUT='' IP_RULE6_OUTPUT='' nft_ucode ensure-tproxy-route-rule podkop 0x00100000 "$rt_tables"
|
||||
assert_contains "$rt_tables" "105 podkop" "tproxy route table registry"
|
||||
assert_contains "$IP_LOG" $'ip\troute\tadd\tlocal\t0.0.0.0/0\tdev\tlo\ttable\tpodkop' "tproxy route add"
|
||||
assert_contains "$IP_LOG" $'ip\t-6\troute\tadd\tlocal\t::/0\tdev\tlo\ttable\tpodkop' "tproxy route6 add"
|
||||
assert_contains "$IP_LOG" $'ip\t-4\trule\tadd\tfwmark\t0x00100000/0x00100000\ttable\tpodkop\tpriority\t105' "tproxy marking rule add"
|
||||
assert_contains "$IP_LOG" $'ip\t-6\trule\tadd\tfwmark\t0x00100000/0x00100000\ttable\tpodkop\tpriority\t105' "tproxy marking rule6 add"
|
||||
IP_ROUTE_OUTPUT='' IP_ROUTE6_OUTPUT='' IP_RULE_OUTPUT='' IP_RULE6_OUTPUT='' nft_ucode ensure-tproxy-route-rule forkop 0x00100000 "$rt_tables"
|
||||
assert_contains "$rt_tables" "105 forkop" "tproxy route table registry"
|
||||
assert_contains "$IP_LOG" $'ip\troute\tadd\tlocal\t0.0.0.0/0\tdev\tlo\ttable\tforkop' "tproxy route add"
|
||||
assert_contains "$IP_LOG" $'ip\t-6\troute\tadd\tlocal\t::/0\tdev\tlo\ttable\tforkop' "tproxy route6 add"
|
||||
assert_contains "$IP_LOG" $'ip\t-4\trule\tadd\tfwmark\t0x00100000/0x00100000\ttable\tforkop\tpriority\t105' "tproxy marking rule add"
|
||||
assert_contains "$IP_LOG" $'ip\t-6\trule\tadd\tfwmark\t0x00100000/0x00100000\ttable\tforkop\tpriority\t105' "tproxy marking rule6 add"
|
||||
assert_contains "$LOGGER_LOG" "[debug] Added IPv4 TPROXY route" "tproxy route creation log"
|
||||
assert_contains "$LOGGER_LOG" "[debug] Added IPv6 TPROXY route" "tproxy route6 creation log"
|
||||
assert_contains "$LOGGER_LOG" "[debug] Creating IPv4 TPROXY marking rule" "tproxy marking rule creation log"
|
||||
@@ -342,12 +342,12 @@ assert_contains "$LOGGER_LOG" "[debug] Creating IPv6 TPROXY marking rule" "tprox
|
||||
|
||||
: > "$IP_LOG"
|
||||
: > "$LOGGER_LOG"
|
||||
printf '%s\n' '105 podkop' >"$rt_tables"
|
||||
printf '%s\n' '105 forkop' >"$rt_tables"
|
||||
IP_ROUTE_OUTPUT='local default dev lo scope host' \
|
||||
IP_ROUTE6_OUTPUT='local default dev lo metric 1024 pref medium' \
|
||||
IP_RULE_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup podkop' \
|
||||
IP_RULE6_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup podkop' \
|
||||
nft_ucode ensure-tproxy-route-rule podkop 0x00100000 "$rt_tables"
|
||||
IP_RULE_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup forkop' \
|
||||
IP_RULE6_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup forkop' \
|
||||
nft_ucode ensure-tproxy-route-rule forkop 0x00100000 "$rt_tables"
|
||||
if grep -Fq $'\tadd\t' "$IP_LOG"; then
|
||||
fail "existing tproxy route/rule should not be added again"
|
||||
fi
|
||||
@@ -357,14 +357,14 @@ assert_contains "$LOGGER_LOG" "[debug] IPv4 TPROXY marking rule already exists"
|
||||
assert_contains "$LOGGER_LOG" "[debug] IPv6 TPROXY marking rule already exists" "existing tproxy marking rule6 log"
|
||||
IP_ROUTE_OUTPUT='local default dev lo scope host' \
|
||||
IP_ROUTE6_OUTPUT='local default dev lo metric 1024 pref medium' \
|
||||
IP_RULE_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup podkop' \
|
||||
IP_RULE6_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup podkop' \
|
||||
nft_ucode tproxy-route-rule-present podkop 0x00100000
|
||||
IP_RULE_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup forkop' \
|
||||
IP_RULE6_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup forkop' \
|
||||
nft_ucode tproxy-route-rule-present forkop 0x00100000
|
||||
if IP_ROUTE_OUTPUT='local default dev lo scope host' \
|
||||
IP_ROUTE6_OUTPUT='local default dev lo metric 1024 pref medium' \
|
||||
IP_RULE_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup other' \
|
||||
IP_RULE6_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup podkop' \
|
||||
nft_ucode tproxy-route-rule-present podkop 0x00100000 >/dev/null 2>&1; then
|
||||
IP_RULE6_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup forkop' \
|
||||
nft_ucode tproxy-route-rule-present forkop 0x00100000 >/dev/null 2>&1; then
|
||||
fail "tproxy route/rule presence should require matching lookup table"
|
||||
fi
|
||||
|
||||
@@ -389,10 +389,10 @@ bad-value
|
||||
203.0.113.0/24
|
||||
EOF_INPUT
|
||||
|
||||
nft_ucode nft-add-file-chunks-to-set "$input" PodkopPlusTable podkop_plus_subnets ips "" 2
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_subnets\t{ 198.51.100.1,203.0.113.0/24 }' "nft chunked ips"
|
||||
nft_ucode nft-add-file-chunks-to-set "$input" ForkopTable forkop_subnets ips "" 2
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_subnets\t{ 198.51.100.1,203.0.113.0/24 }' "nft chunked ips"
|
||||
assert_contains "$LOGGER_LOG" "[debug] 'bad-value' is not IP or CIDR" "invalid element log"
|
||||
assert_contains "$LOGGER_LOG" "[debug] Adding 2 elements to nft set podkop_plus_subnets" "chunk count log"
|
||||
assert_contains "$LOGGER_LOG" "[debug] Adding 2 elements to nft set forkop_subnets" "chunk count log"
|
||||
|
||||
: > "$NFT_LOG"
|
||||
ports_input="$WORK_DIR/ip-ports.txt"
|
||||
@@ -401,7 +401,7 @@ cat >"$ports_input" <<'EOF_INPUT'
|
||||
203.0.113.0/24
|
||||
EOF_INPUT
|
||||
|
||||
nft_ucode nft-add-file-chunks-to-set "$ports_input" PodkopPlusTable podkop_plus_ip_ports ip-port-from-ip "80,443-444" 3
|
||||
nft_ucode nft-add-file-chunks-to-set "$ports_input" ForkopTable forkop_ip_ports ip-port-from-ip "80,443-444" 3
|
||||
assert_contains "$NFT_LOG" $'198.51.100.1 . 80,198.51.100.1 . 443-444,203.0.113.0/24 . 80' "ip-port chunk"
|
||||
assert_contains "$NFT_LOG" $'203.0.113.0/24 . 443-444' "ip-port second chunk"
|
||||
|
||||
@@ -470,12 +470,12 @@ cat >"$plain_subnets" <<'EOF_INPUT'
|
||||
EOF_INPUT
|
||||
|
||||
: > "$NFT_LOG"
|
||||
nft_ucode nft-add-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" inline_no_ports "$plain_subnets" PodkopPlusTable podkop_plus_subnets podkop_plus_ip_ports 5000
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_subnets\t{ 198.51.100.210,203.0.113.0/24 }' "plain subnet import without ports"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_subnets6\t{ 2001:db8::210 }' "plain subnet6 import without ports"
|
||||
nft_ucode nft-add-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" inline_no_ports "$plain_subnets" ForkopTable forkop_subnets forkop_ip_ports 5000
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_inline_no_ports_subnets\t{ 198.51.100.210,203.0.113.0/24 }' "plain subnet import without ports"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_inline_no_ports_subnets6\t{ 2001:db8::210 }' "plain subnet6 import without ports"
|
||||
|
||||
: > "$NFT_LOG"
|
||||
nft_ucode nft-add-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" ports_only "$plain_subnets" PodkopPlusTable podkop_plus_subnets podkop_plus_ip_ports 3
|
||||
nft_ucode nft-add-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" ports_only "$plain_subnets" ForkopTable forkop_subnets forkop_ip_ports 3
|
||||
assert_contains "$NFT_LOG" $'198.51.100.210 . 53,198.51.100.210 . 853,198.51.100.210 . 5353' "plain subnet import scoped first chunk"
|
||||
assert_contains "$NFT_LOG" $'203.0.113.0/24 . 53,203.0.113.0/24 . 853,203.0.113.0/24 . 5353' "plain subnet import scoped second chunk"
|
||||
assert_contains "$NFT_LOG" $'2001:db8::210 . 53,2001:db8::210 . 853,2001:db8::210 . 5353' "plain subnet6 import scoped chunk"
|
||||
@@ -502,56 +502,56 @@ JSON
|
||||
: > "$NFT_LOG"
|
||||
unscoped_json="$WORK_DIR/unscoped-json.txt"
|
||||
scoped_json="$WORK_DIR/scoped-json.txt"
|
||||
nft_ucode nft-add-json-ruleset-subnets-for-section-fixture "$WORK_DIR/populate-fixture.json" inline_no_ports "$json_ruleset" "fixture json" PodkopPlusTable podkop_plus_subnets podkop_plus_ip_ports "$unscoped_json" "$scoped_json" 5000
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_subnets\t{ 198.51.100.220 }' "json ruleset unscoped import"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_subnets6\t{ 2001:db8::220 }' "json ruleset unscoped6 import"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_ip_ports\t{ 198.51.100.221 . 443,198.51.100.222 . 1000-1002 }' "json ruleset own port filters"
|
||||
nft_ucode nft-add-json-ruleset-subnets-for-section-fixture "$WORK_DIR/populate-fixture.json" inline_no_ports "$json_ruleset" "fixture json" ForkopTable forkop_subnets forkop_ip_ports "$unscoped_json" "$scoped_json" 5000
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_inline_no_ports_subnets\t{ 198.51.100.220 }' "json ruleset unscoped import"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_inline_no_ports_subnets6\t{ 2001:db8::220 }' "json ruleset unscoped6 import"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_inline_no_ports_ip_ports\t{ 198.51.100.221 . 443,198.51.100.222 . 1000-1002 }' "json ruleset own port filters"
|
||||
|
||||
: > "$NFT_LOG"
|
||||
unscoped_json="$WORK_DIR/unscoped-json-ports.txt"
|
||||
scoped_json="$WORK_DIR/scoped-json-ports.txt"
|
||||
nft_ucode nft-add-json-ruleset-subnets-for-section-fixture "$WORK_DIR/populate-fixture.json" ports_only "$json_ruleset" "fixture json ports" PodkopPlusTable podkop_plus_subnets podkop_plus_ip_ports "$unscoped_json" "$scoped_json" 4
|
||||
nft_ucode nft-add-json-ruleset-subnets-for-section-fixture "$WORK_DIR/populate-fixture.json" ports_only "$json_ruleset" "fixture json ports" ForkopTable forkop_subnets forkop_ip_ports "$unscoped_json" "$scoped_json" 4
|
||||
assert_contains "$NFT_LOG" $'198.51.100.220 . 53,198.51.100.220 . 5353,198.51.100.220 . 853' "json ruleset scoped import"
|
||||
if grep -Fq '198.51.100.221' "$NFT_LOG" || grep -Fq '198.51.100.222' "$NFT_LOG"; then
|
||||
fail "json ruleset section ports should intersect rule-owned port filters"
|
||||
fi
|
||||
|
||||
: > "$NFT_LOG"
|
||||
nft_ucode nft-add-community-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" inline_no_ports discord "$plain_subnets" PodkopPlusTable podkop_plus_subnets podkop_plus_ip_ports podkop_plus_interfaces podkop_plus_discord_subnets 0x00100000 5000
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_subnets\t{ 198.51.100.210,203.0.113.0/24 }' "discord community subnet import"
|
||||
nft_ucode nft-add-community-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" inline_no_ports discord "$plain_subnets" ForkopTable forkop_subnets forkop_ip_ports forkop_interfaces forkop_discord_subnets 0x00100000 5000
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_inline_no_ports_subnets\t{ 198.51.100.210,203.0.113.0/24 }' "discord community subnet import"
|
||||
|
||||
: > "$NFT_LOG"
|
||||
NFT_MANGLE_CHAIN_OUTPUT=$'iifname @podkop_plus_interfaces ip daddr @podkop_plus_discord_subnets udp dport { 19000-20000, 50000-65535 } meta mark set 0x00100000 counter\niifname @podkop_plus_interfaces ip6 daddr @podkop_plus_discord_subnets6 udp dport { 19000-20000, 50000-65535 } meta mark set 0x00100000 counter' \
|
||||
nft_ucode nft-add-community-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" inline_no_ports discord "$plain_subnets" PodkopPlusTable podkop_plus_subnets podkop_plus_ip_ports podkop_plus_interfaces podkop_plus_discord_subnets 0x00100000 5000
|
||||
NFT_MANGLE_CHAIN_OUTPUT=$'iifname @forkop_interfaces ip daddr @forkop_discord_subnets udp dport { 19000-20000, 50000-65535 } meta mark set 0x00100000 counter\niifname @forkop_interfaces ip6 daddr @forkop_discord_subnets6 udp dport { 19000-20000, 50000-65535 } meta mark set 0x00100000 counter' \
|
||||
nft_ucode nft-add-community-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" inline_no_ports discord "$plain_subnets" ForkopTable forkop_subnets forkop_ip_ports forkop_interfaces forkop_discord_subnets 0x00100000 5000
|
||||
if grep -Fq $'nft\tadd\trule' "$NFT_LOG"; then
|
||||
fail "discord community existing rule should not insert"
|
||||
fi
|
||||
|
||||
: > "$NFT_LOG"
|
||||
nft_ucode nft-add-community-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" ports_only discord "$plain_subnets" PodkopPlusTable podkop_plus_subnets podkop_plus_ip_ports podkop_plus_interfaces podkop_plus_discord_subnets 0x00100000 3
|
||||
if grep -Fq 'podkop_plus_discord_subnets' "$NFT_LOG"; then
|
||||
nft_ucode nft-add-community-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" ports_only discord "$plain_subnets" ForkopTable forkop_subnets forkop_ip_ports forkop_interfaces forkop_discord_subnets 0x00100000 3
|
||||
if grep -Fq 'forkop_discord_subnets' "$NFT_LOG"; then
|
||||
fail "discord community with section ports should not use the discord nft set"
|
||||
fi
|
||||
assert_contains "$NFT_LOG" $'198.51.100.210 . 53,198.51.100.210 . 853,198.51.100.210 . 5353' "discord community with ports uses scoped import"
|
||||
|
||||
: > "$NFT_LOG"
|
||||
nft_ucode nft-populate-runtime-sets-fixture "$WORK_DIR/populate-fixture.json" 1 "deferred" PodkopPlusTable podkop_plus_subnets podkop_plus_ports podkop_plus_ip_ports podkop_plus_interfaces localv4 0x00100000
|
||||
nft_ucode nft-populate-runtime-sets-fixture "$WORK_DIR/populate-fixture.json" 1 "deferred" ForkopTable forkop_subnets forkop_ports forkop_ip_ports forkop_interfaces localv4 0x00100000
|
||||
assert_contains "$NFT_LOG" $'198.51.100.1 . 80,198.51.100.1 . 443-444,203.0.113.0/24 . 80' "populate inline ip-port first chunk"
|
||||
assert_contains "$NFT_LOG" $'203.0.113.0/24 . 443-444' "populate inline ip-port second chunk"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_ip_ports\t{ 198.51.100.1 . 80,198.51.100.1 . 443-444,203.0.113.0/24 . 80,203.0.113.0/24 . 443-444 }' "populate inline priority ip-port set"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_ip6_ports\t{ 2001:db8::1 . 80,2001:db8::1 . 443-444 }' "populate inline priority ip6-port set"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_subnets\t{ 198.51.100.200 }' "populate inline ip without ports"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_subnets6\t{ 2001:db8::200 }' "populate inline ip6 without ports"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_ports_only_ports\t{ 53,853,5353 }' "populate ports-only set"
|
||||
assert_contains "$NFT_LOG" $'nft\tinsert\trule\tinet\tPodkopPlusTable\tmangle\tiifname\t@podkop_plus_interfaces\tip\tsaddr\t192.168.1.20/32\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "populate fully routed tcp"
|
||||
assert_contains "$NFT_LOG" $'nft\tinsert\trule\tinet\tPodkopPlusTable\tmangle\tiifname\t@podkop_plus_interfaces\tip6\tsaddr\t2001:db8::20/128\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "populate fully routed6 tcp"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_inline_ip_ports\t{ 198.51.100.1 . 80,198.51.100.1 . 443-444,203.0.113.0/24 . 80,203.0.113.0/24 . 443-444 }' "populate inline priority ip-port set"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_inline_ip6_ports\t{ 2001:db8::1 . 80,2001:db8::1 . 443-444 }' "populate inline priority ip6-port set"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_inline_no_ports_subnets\t{ 198.51.100.200 }' "populate inline ip without ports"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_inline_no_ports_subnets6\t{ 2001:db8::200 }' "populate inline ip6 without ports"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_ports_only_ports\t{ 53,853,5353 }' "populate ports-only set"
|
||||
assert_contains "$NFT_LOG" $'nft\tinsert\trule\tinet\tForkopTable\tmangle\tiifname\t@forkop_interfaces\tip\tsaddr\t192.168.1.20/32\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "populate fully routed tcp"
|
||||
assert_contains "$NFT_LOG" $'nft\tinsert\trule\tinet\tForkopTable\tmangle\tiifname\t@forkop_interfaces\tip6\tsaddr\t2001:db8::20/128\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "populate fully routed6 tcp"
|
||||
if grep -Fq '192.0.2.1' "$NFT_LOG"; then
|
||||
fail "disabled section should not populate nft"
|
||||
fi
|
||||
if grep -Fq '192.0.2.2' "$NFT_LOG"; then
|
||||
fail "deferred section should not populate nft"
|
||||
fi
|
||||
if grep -Fq $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_ports_with_domain_ports\t{ 8443 }' "$NFT_LOG"; then
|
||||
if grep -Fq $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_ports_with_domain_ports\t{ 8443 }' "$NFT_LOG"; then
|
||||
fail "ports with destination matchers should not populate global port set"
|
||||
fi
|
||||
if [ "$(grep -F $'ip\tsaddr\t192.168.1.20/32' "$NFT_LOG" | wc -l | tr -d ' ')" != "3" ]; then
|
||||
@@ -650,23 +650,23 @@ assert_eq "$expected_signature" \
|
||||
"nft runtime signature fixture"
|
||||
|
||||
cat >"$WORK_DIR/signature-uci.state" <<'EOF_UCI'
|
||||
podkop-plus.settings=settings
|
||||
podkop-plus.settings.source_network_interfaces=br-lan tun0
|
||||
podkop-plus.settings.exclude_ntp=1
|
||||
podkop-plus.disabled=section
|
||||
podkop-plus.disabled.enabled=0
|
||||
podkop-plus.disabled.ip_cidr=192.0.2.10
|
||||
podkop-plus.enabled=section
|
||||
podkop-plus.enabled.enabled=1
|
||||
podkop-plus.enabled.action=bypass
|
||||
podkop-plus.enabled.ip_cidr=10.0.0.0/8 192.0.2.1
|
||||
podkop-plus.enabled.ports=443 bad-value
|
||||
podkop-plus.enabled.ports_text=80 443-444 65536
|
||||
podkop-plus.enabled.fully_routed_ips=192.168.1.10/32
|
||||
podkop-plus.enabled.community_lists=geoblock meta telegram youtube discord
|
||||
podkop-plus.enabled.remote_subnet_lists=https://example.com/subnets.lst
|
||||
podkop-plus.enabled.rule_set_with_subnets=/tmp/local.json
|
||||
podkop-plus.enabled.domain_ip_lists=https://example.com/mixed.lst
|
||||
forkop.settings=settings
|
||||
forkop.settings.source_network_interfaces=br-lan tun0
|
||||
forkop.settings.exclude_ntp=1
|
||||
forkop.disabled=section
|
||||
forkop.disabled.enabled=0
|
||||
forkop.disabled.ip_cidr=192.0.2.10
|
||||
forkop.enabled=section
|
||||
forkop.enabled.enabled=1
|
||||
forkop.enabled.action=bypass
|
||||
forkop.enabled.ip_cidr=10.0.0.0/8 192.0.2.1
|
||||
forkop.enabled.ports=443 bad-value
|
||||
forkop.enabled.ports_text=80 443-444 65536
|
||||
forkop.enabled.fully_routed_ips=192.168.1.10/32
|
||||
forkop.enabled.community_lists=geoblock meta telegram youtube discord
|
||||
forkop.enabled.remote_subnet_lists=https://example.com/subnets.lst
|
||||
forkop.enabled.rule_set_with_subnets=/tmp/local.json
|
||||
forkop.enabled.domain_ip_lists=https://example.com/mixed.lst
|
||||
EOF_UCI
|
||||
cat >"$WORK_DIR/signature-uci-expected.txt" <<'EOF_EXPECTED'
|
||||
[settings.source_network_interfaces]
|
||||
@@ -694,7 +694,7 @@ https://example.com/mixed.lst
|
||||
EOF_EXPECTED
|
||||
expected_signature="$(md5sum "$WORK_DIR/signature-uci-expected.txt" | awk '{print $1}')"
|
||||
assert_eq "$expected_signature" \
|
||||
"$(PODKOP_UCI_STATE_FILE="$WORK_DIR/signature-uci.state" nft_ucode nft-runtime-signature)" \
|
||||
"$(FORKOP_UCI_STATE_FILE="$WORK_DIR/signature-uci.state" nft_ucode nft-runtime-signature)" \
|
||||
"nft runtime signature from UCI state"
|
||||
|
||||
cat >"$WORK_DIR/signature-defaults-fixture.json" <<'JSON'
|
||||
@@ -712,12 +712,12 @@ assert_eq "$expected_signature" \
|
||||
"nft runtime signature defaults"
|
||||
|
||||
: > "$NFT_LOG"
|
||||
nft_ucode nft-populate-runtime-sets-fixture "$WORK_DIR/populate-fixture.json" 0 "" PodkopPlusTable podkop_plus_subnets podkop_plus_ports podkop_plus_ip_ports podkop_plus_interfaces localv4 0x00100000
|
||||
nft_ucode nft-populate-runtime-sets-fixture "$WORK_DIR/populate-fixture.json" 0 "" ForkopTable forkop_subnets forkop_ports forkop_ip_ports forkop_interfaces localv4 0x00100000
|
||||
[ ! -s "$NFT_LOG" ] || fail "disabled nft population should not call nft"
|
||||
|
||||
: > "$NFT_LOG"
|
||||
NFT_MANGLE_CHAIN_OUTPUT='ip saddr 192.168.1.20 meta l4proto tcp meta mark set 0x00100000 counter' \
|
||||
nft_ucode nft-populate-runtime-sets-fixture "$WORK_DIR/populate-fixture.json" 1 "disabled deferred inline inline_no_ports ports_only ports_with_domain" PodkopPlusTable podkop_plus_subnets podkop_plus_ports podkop_plus_ip_ports podkop_plus_interfaces localv4 0x00100000
|
||||
nft_ucode nft-populate-runtime-sets-fixture "$WORK_DIR/populate-fixture.json" 1 "disabled deferred inline inline_no_ports ports_only ports_with_domain" ForkopTable forkop_subnets forkop_ports forkop_ip_ports forkop_interfaces localv4 0x00100000
|
||||
if grep -Fq $'nft\tinsert\trule' "$NFT_LOG"; then
|
||||
fail "existing nft-normalized /32 fully routed source should not insert"
|
||||
fi
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
GENERATOR_UC="$PODKOP_LIB/singbox/generator.uc"
|
||||
PARSER_UC="$PODKOP_LIB/subscription/parser.uc"
|
||||
VALIDATOR_UC="$PODKOP_LIB/config/validator.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
GENERATOR_UC="$FORKOP_LIB/singbox/generator.uc"
|
||||
PARSER_UC="$FORKOP_LIB/subscription/parser.uc"
|
||||
VALIDATOR_UC="$FORKOP_LIB/config/validator.uc"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -109,7 +109,7 @@ JSON
|
||||
runtime_config="$WORK_DIR/runtime-config.json"
|
||||
mkdir -p "$runtime_config.section-cache"
|
||||
TMP_SUBSCRIPTION_FOLDER="$WORK_DIR/subscriptions" \
|
||||
ucode -L "$PODKOP_LIB" "$GENERATOR_UC" generate-config-fixture \
|
||||
ucode -L "$FORKOP_LIB" "$GENERATOR_UC" generate-config-fixture \
|
||||
"$WORK_DIR/runtime-tags.json" "$runtime_config" "127.0.0.1" "0"
|
||||
|
||||
ucode -e '
|
||||
@@ -225,7 +225,7 @@ input.settings.dns_server = ['77.88.8.8'];
|
||||
input.settings.bootstrap_dns_server = ['77.88.8.8'];
|
||||
fs.writeFileSync(process.argv[3], JSON.stringify(input));
|
||||
JS
|
||||
if output="$(PODKOP_LIB="$PODKOP_LIB" ucode -L "$PODKOP_LIB" "$VALIDATOR_UC" \
|
||||
if output="$(FORKOP_LIB="$FORKOP_LIB" ucode -L "$FORKOP_LIB" "$VALIDATOR_UC" \
|
||||
validate-runtime-fixture "$normalized" '{}' 2>/dev/null)"; then
|
||||
fail "validator accepted $fixture"
|
||||
fi
|
||||
@@ -259,7 +259,7 @@ cat >"$WORK_DIR/xray-duplicates.json" <<'JSON'
|
||||
]
|
||||
JSON
|
||||
|
||||
ucode -L "$PODKOP_LIB" "$PARSER_UC" normalize-content \
|
||||
ucode -L "$FORKOP_LIB" "$PARSER_UC" normalize-content \
|
||||
"$WORK_DIR/xray-duplicates.json" "$WORK_DIR/xray-normalized.json"
|
||||
ucode -e '
|
||||
let fs = require("fs");
|
||||
|
||||
+17
-12
@@ -2,9 +2,9 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_MAKEFILE="$ROOT_DIR/podkop/Makefile"
|
||||
FORKOP_MAKEFILE="$ROOT_DIR/forkop/Makefile"
|
||||
BUILD_SCRIPT="$ROOT_DIR/build.sh"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
@@ -20,8 +20,8 @@ require_file() {
|
||||
require_make_dep() {
|
||||
local package="$1"
|
||||
|
||||
grep -Eq "DEPENDS:=.*(^|[[:space:]])\\+$package([[:space:]]|$)" "$PODKOP_MAKEFILE" ||
|
||||
fail "podkop/Makefile DEPENDS is missing +$package"
|
||||
grep -Eq "DEPENDS:=.*(^|[[:space:]])\\+$package([[:space:]]|$)" "$FORKOP_MAKEFILE" ||
|
||||
fail "forkop/Makefile DEPENDS is missing +$package"
|
||||
}
|
||||
|
||||
require_build_dep() {
|
||||
@@ -40,22 +40,27 @@ require_package_dependency() {
|
||||
require_build_dep "BACKEND_DEPENDS_APK" "$package"
|
||||
}
|
||||
|
||||
require_file "$PODKOP_MAKEFILE"
|
||||
require_file "$FORKOP_MAKEFILE"
|
||||
require_file "$BUILD_SCRIPT"
|
||||
require_file "$PODKOP_LIB"
|
||||
require_file "$FORKOP_LIB"
|
||||
|
||||
if grep -Rqs 'require("uci")' "$PODKOP_LIB"; then
|
||||
grep -Fq "must use x.y.z format" "$FORKOP_MAKEFILE" ||
|
||||
fail "forkop/Makefile must enforce the three-part release version contract"
|
||||
grep -Fq 'APK_INTERNAL_VERSION="$RELEASE_VERSION"' "$BUILD_SCRIPT" ||
|
||||
fail "build.sh must use the exact three-part release version for APK metadata"
|
||||
|
||||
if grep -Rqs 'require("uci")' "$FORKOP_LIB"; then
|
||||
require_package_dependency "ucode-mod-uci"
|
||||
fi
|
||||
|
||||
if grep -Rqs 'require("fs")' "$PODKOP_LIB"; then
|
||||
if grep -Rqs 'require("fs")' "$FORKOP_LIB"; then
|
||||
require_package_dependency "ucode-mod-fs"
|
||||
fi
|
||||
|
||||
if grep -Rqs 'podkop_dnsmasq_failsafe_restore_raw' \
|
||||
"$ROOT_DIR/podkop/files/usr/bin" \
|
||||
"$ROOT_DIR/podkop/files/usr/lib" \
|
||||
"$ROOT_DIR/podkop/files/etc/init.d"; then
|
||||
if grep -Rqs 'forkop_dnsmasq_failsafe_restore_raw' \
|
||||
"$ROOT_DIR/forkop/files/usr/bin" \
|
||||
"$ROOT_DIR/forkop/files/usr/lib" \
|
||||
"$ROOT_DIR/forkop/files/etc/init.d"; then
|
||||
fail "duplicated raw dnsmasq failsafe restore shell owner is present"
|
||||
fi
|
||||
|
||||
|
||||
+55
-65
@@ -2,11 +2,11 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_BIN="$ROOT_DIR/podkop/files/usr/bin/podkop"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
PACKAGE_UC="$PODKOP_LIB/service/package.uc"
|
||||
PODKOP_MAKEFILE="$ROOT_DIR/podkop/Makefile"
|
||||
LUCI_UCI_DEFAULTS="$ROOT_DIR/luci-app-podkop-plus/root/etc/uci-defaults/50_luci-podkop-plus"
|
||||
FORKOP_BIN="$ROOT_DIR/forkop/files/usr/bin/forkop"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
PACKAGE_UC="$FORKOP_LIB/service/package.uc"
|
||||
FORKOP_MAKEFILE="$ROOT_DIR/forkop/Makefile"
|
||||
LUCI_UCI_DEFAULTS="$ROOT_DIR/luci-app-forkop/root/etc/uci-defaults/50_luci-forkop"
|
||||
BUILD_SCRIPT="$ROOT_DIR/build.sh"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
@@ -27,102 +27,92 @@ if grep -n -E 'require\("uci"\)\.cursor|uci -q|uci", "-q"' "$PACKAGE_UC" >/dev/n
|
||||
fi
|
||||
grep -Fq 'require("core.uci")' "$PACKAGE_UC" ||
|
||||
fail "service/package.uc must import core.uci"
|
||||
grep -Fq 'package_prerm: [ "service/package.uc", "prerm", 0 ]' "$PODKOP_BIN" ||
|
||||
fail "podkop entrypoint must dispatch package prerm cleanup through service/package.uc"
|
||||
grep -Fq 'luci_postinst: [ "service/package.uc", "luci-postinst", 0 ]' "$PODKOP_BIN" ||
|
||||
fail "podkop entrypoint must dispatch LuCI postinstall cleanup through service/package.uc"
|
||||
grep -Fq 'package_prerm: [ "service/package.uc", "prerm", 0 ]' "$FORKOP_BIN" ||
|
||||
fail "forkop entrypoint must dispatch package prerm cleanup through service/package.uc"
|
||||
grep -Fq 'luci_postinst: [ "service/package.uc", "luci-postinst", 0 ]' "$FORKOP_BIN" ||
|
||||
fail "forkop entrypoint must dispatch LuCI postinstall cleanup through service/package.uc"
|
||||
grep -Fq '#!/bin/sh' "$LUCI_UCI_DEFAULTS" ||
|
||||
fail "LuCI uci-defaults must remain a shell script because OpenWrt default_postinst runs it through shell"
|
||||
grep -Fq '/usr/bin/podkop-plus luci_postinst' "$LUCI_UCI_DEFAULTS" ||
|
||||
grep -Fq '/usr/bin/forkop luci_postinst' "$LUCI_UCI_DEFAULTS" ||
|
||||
fail "LuCI uci-defaults must delegate cache/rpcd handling to ucode"
|
||||
if grep -E 'rm -f /var/luci-indexcache|rm -f /tmp/luci-indexcache|logger -t "podkop-plus"' "$LUCI_UCI_DEFAULTS" >/dev/null; then
|
||||
if grep -E 'rm -f /var/luci-indexcache|rm -f /tmp/luci-indexcache|logger -t "forkop"' "$LUCI_UCI_DEFAULTS" >/dev/null; then
|
||||
fail "LuCI uci-defaults must not own cache/logger shell logic"
|
||||
fi
|
||||
|
||||
if grep -n -E 'grep -q "105 podkopplus"|sed -i "/105 podkopplus|podkop_dont_touch_dhcp=.*uci|cp /etc/config/podkop_plus|rm -f /tmp/luci-indexcache|killall -HUP rpcd' "$PODKOP_MAKEFILE" "$BUILD_SCRIPT" >/dev/null; then
|
||||
if grep -n -E 'grep -q "105 forkop"|sed -i "/105 forkop|forkop_dont_touch_dhcp=.*uci|cp /etc/config/forkop|rm -f /tmp/luci-indexcache|killall -HUP rpcd' "$FORKOP_MAKEFILE" "$BUILD_SCRIPT" >/dev/null; then
|
||||
fail "package scripts must not keep backend/LuCI lifecycle business logic in shell"
|
||||
fi
|
||||
grep -Fq '#!/usr/bin/ucode' "$PODKOP_MAKEFILE" ||
|
||||
fail "podkop Makefile package hooks must use ucode entrypoints"
|
||||
grep -Fq '/usr/bin/podkop-plus package_prerm' "$PODKOP_MAKEFILE" ||
|
||||
fail "podkop Makefile prerm must delegate cleanup to package_prerm"
|
||||
grep -Fq '/usr/bin/podkop-plus luci_postinst' "$BUILD_SCRIPT" ||
|
||||
grep -Fq '#!/usr/bin/ucode' "$FORKOP_MAKEFILE" ||
|
||||
fail "forkop Makefile package hooks must use ucode entrypoints"
|
||||
grep -Fq '/usr/bin/forkop package_prerm' "$FORKOP_MAKEFILE" ||
|
||||
fail "forkop Makefile prerm must delegate cleanup to package_prerm"
|
||||
grep -Fq '/usr/bin/forkop luci_postinst' "$BUILD_SCRIPT" ||
|
||||
fail "manual package builder must delegate LuCI cache/rpcd handling to luci_postinst"
|
||||
|
||||
legacy_config="$WORK_DIR/podkop_plus"
|
||||
new_config="$WORK_DIR/podkop-plus"
|
||||
printf 'config podkop_plus legacy\n' >"$legacy_config"
|
||||
PODKOP_CONFIG="$new_config" PODKOP_LEGACY_CONFIG="$legacy_config" \
|
||||
ucode -L "$PODKOP_LIB" "$PACKAGE_UC" preinst
|
||||
[ "$(cat "$new_config")" = "config podkop_plus legacy" ] ||
|
||||
fail "package preinst must copy legacy config when new config is absent"
|
||||
|
||||
printf 'existing config\n' >"$new_config"
|
||||
PODKOP_CONFIG="$new_config" PODKOP_LEGACY_CONFIG="$legacy_config" \
|
||||
ucode -L "$PODKOP_LIB" "$PACKAGE_UC" preinst
|
||||
[ "$(cat "$new_config")" = "existing config" ] ||
|
||||
fail "package preinst must not overwrite existing config"
|
||||
if grep -n -E 'Package/forkop/preinst|copy_legacy_config|FORKOP_LEGACY_CONFIG|mode == "preinst"' \
|
||||
"$FORKOP_MAKEFILE" "$BUILD_SCRIPT" "$PACKAGE_UC" >/dev/null 2>&1; then
|
||||
fail "package hooks and runtime service must not own configuration migration"
|
||||
fi
|
||||
|
||||
rt_tables="$WORK_DIR/rt_tables"
|
||||
cat >"$rt_tables" <<'EOF'
|
||||
100 main
|
||||
105 podkopplus
|
||||
105 forkop
|
||||
200 custom
|
||||
EOF
|
||||
PODKOP_PACKAGE_TEST_MODE=1 PODKOP_RT_TABLES="$rt_tables" \
|
||||
ucode -L "$PODKOP_LIB" "$PACKAGE_UC" prerm
|
||||
if grep -Fq '105 podkopplus' "$rt_tables"; then
|
||||
fail "package prerm must remove the legacy podkopplus routing table entry"
|
||||
FORKOP_PACKAGE_TEST_MODE=1 FORKOP_RT_TABLES="$rt_tables" \
|
||||
ucode -L "$FORKOP_LIB" "$PACKAGE_UC" prerm
|
||||
if grep -Fq '105 forkop' "$rt_tables"; then
|
||||
fail "package prerm must remove the Forkop routing table entry"
|
||||
fi
|
||||
grep -Fq '200 custom' "$rt_tables" ||
|
||||
fail "package prerm must preserve unrelated rt_tables entries"
|
||||
|
||||
touch "$WORK_DIR/luci-indexcache.one" "$WORK_DIR/luci-indexcache.two"
|
||||
PODKOP_PACKAGE_TEST_MODE=1 PODKOP_LUCI_CACHE_GLOBS="$WORK_DIR/luci-indexcache*" \
|
||||
ucode -L "$PODKOP_LIB" "$PACKAGE_UC" luci-postinst
|
||||
FORKOP_PACKAGE_TEST_MODE=1 FORKOP_LUCI_CACHE_GLOBS="$WORK_DIR/luci-indexcache*" \
|
||||
ucode -L "$FORKOP_LIB" "$PACKAGE_UC" luci-postinst
|
||||
if compgen -G "$WORK_DIR/luci-indexcache*" >/dev/null; then
|
||||
fail "luci-postinst must remove LuCI index cache files"
|
||||
fi
|
||||
|
||||
cat >"$WORK_DIR/podkop-bin" <<'SH'
|
||||
cat >"$WORK_DIR/forkop-bin" <<'SH'
|
||||
#!/usr/bin/env bash
|
||||
printf '%s\n' "$*" >> "${PODKOP_RESTORE_LOG:?}"
|
||||
printf '%s\n' "$*" >> "${FORKOP_RESTORE_LOG:?}"
|
||||
SH
|
||||
chmod 0755 "$WORK_DIR/podkop-bin"
|
||||
chmod 0755 "$WORK_DIR/forkop-bin"
|
||||
|
||||
cat >"$WORK_DIR/dont-touch.state" <<'EOF_UCI'
|
||||
podkop-plus.settings=settings
|
||||
podkop-plus.settings.dont_touch_dhcp=1
|
||||
forkop.settings=settings
|
||||
forkop.settings.dont_touch_dhcp=1
|
||||
EOF_UCI
|
||||
printf '105 podkopplus\n' >"$WORK_DIR/rt_tables_dont_touch"
|
||||
printf '105 forkop\n' >"$WORK_DIR/rt_tables_dont_touch"
|
||||
: >"$WORK_DIR/restore-dont-touch.log"
|
||||
PODKOP_UCI_STATE_FILE="$WORK_DIR/dont-touch.state" \
|
||||
PODKOP_RESTORE_LOG="$WORK_DIR/restore-dont-touch.log" \
|
||||
PODKOP_BIN="$WORK_DIR/podkop-bin" \
|
||||
PODKOP_DNS_APPLY_UC="$WORK_DIR/missing-dns-apply.uc" \
|
||||
PODKOP_SING_BOX_INIT="$WORK_DIR/missing-sing-box-init" \
|
||||
PODKOP_SING_BOX_BIN="$WORK_DIR/missing-sing-box-bin" \
|
||||
PODKOP_SING_BOX_CRONET="$WORK_DIR/missing-cronet" \
|
||||
PODKOP_RT_TABLES="$WORK_DIR/rt_tables_dont_touch" \
|
||||
ucode -L "$PODKOP_LIB" "$PACKAGE_UC" prerm
|
||||
FORKOP_UCI_STATE_FILE="$WORK_DIR/dont-touch.state" \
|
||||
FORKOP_RESTORE_LOG="$WORK_DIR/restore-dont-touch.log" \
|
||||
FORKOP_BIN="$WORK_DIR/forkop-bin" \
|
||||
FORKOP_DNS_APPLY_UC="$WORK_DIR/missing-dns-apply.uc" \
|
||||
FORKOP_SING_BOX_INIT="$WORK_DIR/missing-sing-box-init" \
|
||||
FORKOP_SING_BOX_BIN="$WORK_DIR/missing-sing-box-bin" \
|
||||
FORKOP_SING_BOX_CRONET="$WORK_DIR/missing-cronet" \
|
||||
FORKOP_RT_TABLES="$WORK_DIR/rt_tables_dont_touch" \
|
||||
ucode -L "$FORKOP_LIB" "$PACKAGE_UC" prerm
|
||||
[ ! -s "$WORK_DIR/restore-dont-touch.log" ] ||
|
||||
fail "package prerm must skip dnsmasq restore when dont_touch_dhcp is enabled"
|
||||
|
||||
cat >"$WORK_DIR/restore.state" <<'EOF_UCI'
|
||||
podkop-plus.settings=settings
|
||||
podkop-plus.settings.dont_touch_dhcp=0
|
||||
forkop.settings=settings
|
||||
forkop.settings.dont_touch_dhcp=0
|
||||
EOF_UCI
|
||||
printf '105 podkopplus\n' >"$WORK_DIR/rt_tables_restore"
|
||||
printf '105 forkop\n' >"$WORK_DIR/rt_tables_restore"
|
||||
: >"$WORK_DIR/restore.log"
|
||||
PODKOP_UCI_STATE_FILE="$WORK_DIR/restore.state" \
|
||||
PODKOP_RESTORE_LOG="$WORK_DIR/restore.log" \
|
||||
PODKOP_BIN="$WORK_DIR/podkop-bin" \
|
||||
PODKOP_DNS_APPLY_UC="$WORK_DIR/missing-dns-apply.uc" \
|
||||
PODKOP_SING_BOX_INIT="$WORK_DIR/missing-sing-box-init" \
|
||||
PODKOP_SING_BOX_BIN="$WORK_DIR/missing-sing-box-bin" \
|
||||
PODKOP_SING_BOX_CRONET="$WORK_DIR/missing-cronet" \
|
||||
PODKOP_RT_TABLES="$WORK_DIR/rt_tables_restore" \
|
||||
ucode -L "$PODKOP_LIB" "$PACKAGE_UC" prerm
|
||||
FORKOP_UCI_STATE_FILE="$WORK_DIR/restore.state" \
|
||||
FORKOP_RESTORE_LOG="$WORK_DIR/restore.log" \
|
||||
FORKOP_BIN="$WORK_DIR/forkop-bin" \
|
||||
FORKOP_DNS_APPLY_UC="$WORK_DIR/missing-dns-apply.uc" \
|
||||
FORKOP_SING_BOX_INIT="$WORK_DIR/missing-sing-box-init" \
|
||||
FORKOP_SING_BOX_BIN="$WORK_DIR/missing-sing-box-bin" \
|
||||
FORKOP_SING_BOX_CRONET="$WORK_DIR/missing-cronet" \
|
||||
FORKOP_RT_TABLES="$WORK_DIR/rt_tables_restore" \
|
||||
ucode -L "$FORKOP_LIB" "$PACKAGE_UC" prerm
|
||||
grep -Fxq 'restore_dnsmasq' "$WORK_DIR/restore.log" ||
|
||||
fail "package prerm must restore dnsmasq when dont_touch_dhcp is disabled"
|
||||
|
||||
|
||||
+10
-10
@@ -2,10 +2,10 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
GENERATOR_UC="$PODKOP_LIB/singbox/generator.uc"
|
||||
VALIDATOR_UC="$PODKOP_LIB/config/validator.uc"
|
||||
PRIORITY_UC="$PODKOP_LIB/singbox/priority.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
GENERATOR_UC="$FORKOP_LIB/singbox/generator.uc"
|
||||
VALIDATOR_UC="$FORKOP_LIB/config/validator.uc"
|
||||
PRIORITY_UC="$FORKOP_LIB/singbox/priority.uc"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -23,7 +23,7 @@ generate_config() {
|
||||
local output="$2"
|
||||
|
||||
mkdir -p "${output}.section-cache"
|
||||
ucode -L "$PODKOP_LIB" "$GENERATOR_UC" generate-config-fixture \
|
||||
ucode -L "$FORKOP_LIB" "$GENERATOR_UC" generate-config-fixture \
|
||||
"$fixture" "$output" "127.0.0.1"
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ input.settings.dns_server ??= ['77.88.8.8'];
|
||||
input.settings.bootstrap_dns_server ??= ['77.88.8.8'];
|
||||
fs.writeFileSync(process.argv[3], JSON.stringify(input));
|
||||
JS
|
||||
PODKOP_LIB="$PODKOP_LIB" ucode -L "$PODKOP_LIB" "$VALIDATOR_UC" \
|
||||
FORKOP_LIB="$FORKOP_LIB" ucode -L "$FORKOP_LIB" "$VALIDATOR_UC" \
|
||||
validate-runtime-fixture "$normalized" "{}"
|
||||
}
|
||||
|
||||
@@ -366,7 +366,7 @@ JSON
|
||||
cat >"$WORK_DIR/select-first-live-latency.json" <<'JSON'
|
||||
{ "a": -1, "b": 200, "c": 50 }
|
||||
JSON
|
||||
first_live="$(ucode -L "$PODKOP_LIB" "$PRIORITY_UC" select-fixture \
|
||||
first_live="$(ucode -L "$FORKOP_LIB" "$PRIORITY_UC" select-fixture \
|
||||
"$WORK_DIR/select-first-live-group.json" "$WORK_DIR/select-first-live-latency.json" 0 1)"
|
||||
printf '%s\n' "$first_live" | grep -Fq '"tag": "b"' ||
|
||||
fail "priority selection should choose the first live outbound when pick_fastest=0"
|
||||
@@ -383,7 +383,7 @@ JSON
|
||||
cat >"$WORK_DIR/select-fastest-latency.json" <<'JSON'
|
||||
{ "a": 80, "b": 20, "c": 50 }
|
||||
JSON
|
||||
fastest="$(ucode -L "$PODKOP_LIB" "$PRIORITY_UC" select-fixture \
|
||||
fastest="$(ucode -L "$FORKOP_LIB" "$PRIORITY_UC" select-fixture \
|
||||
"$WORK_DIR/select-fastest-group.json" "$WORK_DIR/select-fastest-latency.json" 0 0)"
|
||||
printf '%s\n' "$fastest" | grep -Fq '"tag": "b"' ||
|
||||
fail "priority selection should choose the fastest live outbound when pick_fastest=1"
|
||||
@@ -652,7 +652,7 @@ cat >"$WORK_DIR/bad-filter-mode.json" <<'JSON'
|
||||
JSON
|
||||
assert_rejects "priority level bad filter mode" "$WORK_DIR/bad-filter-mode.json" "Invalid Priority filter mode"
|
||||
|
||||
skip_dead="$(ucode -L "$PODKOP_LIB" "$PRIORITY_UC" select-fixture \
|
||||
skip_dead="$(ucode -L "$FORKOP_LIB" "$PRIORITY_UC" select-fixture \
|
||||
"$WORK_DIR/select-first-live-group.json" "$WORK_DIR/select-fastest-latency.json" 0 1 a)"
|
||||
printf '%s\n' "$skip_dead" | grep -Fq '"tag": "b"' ||
|
||||
fail "replacement selection should skip the just-failed active outbound"
|
||||
@@ -660,7 +660,7 @@ printf '%s\n' "$skip_dead" | grep -Fq '"tag": "b"' ||
|
||||
cat >"$WORK_DIR/select-faster-current-latency.json" <<'JSON'
|
||||
{ "a": 100, "b": 50, "c": 80 }
|
||||
JSON
|
||||
same_level="$(ucode -L "$PODKOP_LIB" "$PRIORITY_UC" select-faster-fixture \
|
||||
same_level="$(ucode -L "$FORKOP_LIB" "$PRIORITY_UC" select-faster-fixture \
|
||||
"$WORK_DIR/select-fastest-group.json" "$WORK_DIR/select-faster-current-latency.json" 0 a)"
|
||||
printf '%s\n' "$same_level" | grep -Fq '"tag": "b"' ||
|
||||
fail "same-level switching should compare candidates with the active delay from the same pass"
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
RULES_UC="$ROOT_DIR/podkop/files/usr/lib/providers/rules.uc"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
RULES_UC="$ROOT_DIR/forkop/files/usr/lib/providers/rules.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
@@ -28,7 +28,7 @@ rows() {
|
||||
}
|
||||
|
||||
rules_ucode() {
|
||||
ucode -L "$PODKOP_LIB" "$RULES_UC" "$@"
|
||||
ucode -L "$FORKOP_LIB" "$RULES_UC" "$@"
|
||||
}
|
||||
|
||||
if grep -n -E 'require\("uci"\)\.cursor|cursor\.foreach' "$RULES_UC" >/dev/null 2>&1; then
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_BIN="$ROOT_DIR/podkop/files/usr/bin/podkop"
|
||||
PODKOP_INIT="$ROOT_DIR/podkop/files/etc/init.d/podkop"
|
||||
CLI_UC="$PODKOP_BIN"
|
||||
RUNTIME_STATE="$ROOT_DIR/podkop/files/usr/lib/runtime_state.sh"
|
||||
UPDATES_RUNTIME="$ROOT_DIR/podkop/files/usr/lib/updates_runtime.sh"
|
||||
LIFECYCLE_UC="$ROOT_DIR/podkop/files/usr/lib/service/lifecycle.uc"
|
||||
UPDATES_UC="$ROOT_DIR/podkop/files/usr/lib/components/updates.uc"
|
||||
STATE_UC="$ROOT_DIR/podkop/files/usr/lib/service/state.uc"
|
||||
NFT_UC="$ROOT_DIR/podkop/files/usr/lib/nft/apply.uc"
|
||||
FORKOP_BIN="$ROOT_DIR/forkop/files/usr/bin/forkop"
|
||||
FORKOP_INIT="$ROOT_DIR/forkop/files/etc/init.d/forkop"
|
||||
CLI_UC="$FORKOP_BIN"
|
||||
RUNTIME_STATE="$ROOT_DIR/forkop/files/usr/lib/runtime_state.sh"
|
||||
UPDATES_RUNTIME="$ROOT_DIR/forkop/files/usr/lib/updates_runtime.sh"
|
||||
LIFECYCLE_UC="$ROOT_DIR/forkop/files/usr/lib/service/lifecycle.uc"
|
||||
UPDATES_UC="$ROOT_DIR/forkop/files/usr/lib/components/updates.uc"
|
||||
STATE_UC="$ROOT_DIR/forkop/files/usr/lib/service/state.uc"
|
||||
NFT_UC="$ROOT_DIR/forkop/files/usr/lib/nft/apply.uc"
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
@@ -22,30 +22,30 @@ fail() {
|
||||
[ ! -e "$UPDATES_RUNTIME" ] ||
|
||||
fail "updates_runtime.sh shell owner must be removed"
|
||||
|
||||
grep -Fq 'runtime_state.sh' "$PODKOP_BIN" &&
|
||||
fail "podkop must not source runtime_state.sh"
|
||||
grep -Fq 'runtime_state.sh' "$FORKOP_BIN" &&
|
||||
fail "forkop must not source runtime_state.sh"
|
||||
|
||||
service_predicates='process_age_seconds|sing_box_service_pid|pid_is_sing_box|sing_box_service_is_running|sing_box_service_is_stable|podkop_runtime_network_is_configured|podkop_is_running|podkop_is_stably_running|wait_for_podkop_stable_start'
|
||||
if grep -R -n -E "$service_predicates" "$PODKOP_BIN" "$ROOT_DIR/podkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
|
||||
service_predicates='process_age_seconds|sing_box_service_pid|pid_is_sing_box|sing_box_service_is_running|sing_box_service_is_stable|forkop_runtime_network_is_configured|forkop_is_running|forkop_is_stably_running|wait_for_forkop_stable_start'
|
||||
if grep -R -n -E "$service_predicates" "$FORKOP_BIN" "$ROOT_DIR/forkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
|
||||
fail "service running/stability predicates must not remain in shell"
|
||||
fi
|
||||
|
||||
pending_state='mark_pending_reload|consume_pending_reload|run_pending_reload_if_requested|sync_time_if_needed'
|
||||
if grep -R -n -E "$pending_state" "$PODKOP_BIN" "$PODKOP_INIT" "$ROOT_DIR/podkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
|
||||
if grep -R -n -E "$pending_state" "$FORKOP_BIN" "$FORKOP_INIT" "$ROOT_DIR/forkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
|
||||
fail "pending reload/time-sync ownership must not remain in shell"
|
||||
fi
|
||||
|
||||
runtime_state_shell_symbols='runtime_state_ucode|acquire_runtime_dir_lock|release_runtime_dir_lock|capture_reload_state|write_reload_state_file|write_reload_state|clear_reload_state|populate_nft_runtime_sets|rebuild_nft_runtime|reload_sing_box_runtime|apply_pending_urltest_selector_switches|close_inherited_service_lock_fd|PODKOP_URLTEST_SELECTOR_SWITCHES'
|
||||
if grep -R -n -E "$runtime_state_shell_symbols" "$PODKOP_BIN" "$PODKOP_INIT" "$ROOT_DIR/podkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
|
||||
runtime_state_shell_symbols='runtime_state_ucode|acquire_runtime_dir_lock|release_runtime_dir_lock|capture_reload_state|write_reload_state_file|write_reload_state|clear_reload_state|populate_nft_runtime_sets|rebuild_nft_runtime|reload_sing_box_runtime|apply_pending_urltest_selector_switches|close_inherited_service_lock_fd|FORKOP_URLTEST_SELECTOR_SWITCHES'
|
||||
if grep -R -n -E "$runtime_state_shell_symbols" "$FORKOP_BIN" "$FORKOP_INIT" "$ROOT_DIR/forkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
|
||||
fail "runtime_state.sh symbols must not remain in shell"
|
||||
fi
|
||||
|
||||
if grep -Fq 'acquire_reload_lock' "$PODKOP_INIT" || grep -Fq 'release_reload_lock' "$PODKOP_INIT"; then
|
||||
if grep -Fq 'acquire_reload_lock' "$FORKOP_INIT" || grep -Fq 'release_reload_lock' "$FORKOP_INIT"; then
|
||||
fail "init.d must not own reload lock decisions"
|
||||
fi
|
||||
|
||||
grep -Fq '#!/usr/bin/ucode' "$PODKOP_BIN" ||
|
||||
fail "podkop entrypoint must be a direct ucode executable"
|
||||
grep -Fq '#!/usr/bin/ucode' "$FORKOP_BIN" ||
|
||||
fail "forkop entrypoint must be a direct ucode executable"
|
||||
grep -Fq 'service/lifecycle.uc' "$CLI_UC" ||
|
||||
fail "service/cli.uc must dispatch lifecycle orchestration through service/lifecycle.uc"
|
||||
grep -Fq 'refresh-cron-from-uci' "$LIFECYCLE_UC" ||
|
||||
@@ -56,7 +56,7 @@ grep -Fq '"list-update"' "$LIFECYCLE_UC" ||
|
||||
fail "service/lifecycle.uc start/reload must run list updates through components/updates.uc"
|
||||
grep -Fq 'subscription_update: [ "components/updates.uc", "subscription-update", 2 ]' "$CLI_UC" ||
|
||||
fail "service/cli.uc must run subscription updates through components/updates.uc"
|
||||
if grep -R -n -F 'updates_runtime.sh' "$PODKOP_BIN" "$ROOT_DIR/podkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
|
||||
if grep -R -n -F 'updates_runtime.sh' "$FORKOP_BIN" "$ROOT_DIR/forkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
|
||||
fail "runtime shell must not reference updates_runtime.sh"
|
||||
fi
|
||||
grep -Fq 'mode == "refresh-cron-from-uci"' "$UPDATES_UC" ||
|
||||
@@ -67,11 +67,11 @@ grep -Fq 'mode == "list-update"' "$UPDATES_UC" ||
|
||||
fail "components/updates.uc must own list update"
|
||||
grep -Fq 'mode == "subscription-update"' "$UPDATES_UC" ||
|
||||
fail "components/updates.uc must own subscription update"
|
||||
grep -Fq 'mode == "podkop-running"' "$STATE_UC" ||
|
||||
fail "service/state.uc must own Podkop running predicate"
|
||||
grep -Fq 'mode == "forkop-running"' "$STATE_UC" ||
|
||||
fail "service/state.uc must own Forkop running predicate"
|
||||
grep -Fq 'mode == "sing-box-service-stable"' "$STATE_UC" ||
|
||||
fail "service/state.uc must expose stable sing-box predicate"
|
||||
grep -Fq 'mode == "wait-podkop-stable-start"' "$STATE_UC" ||
|
||||
grep -Fq 'mode == "wait-forkop-stable-start"' "$STATE_UC" ||
|
||||
fail "service/state.uc must own stable-start waiting"
|
||||
grep -Fq 'mode == "mark-pending-reload"' "$STATE_UC" ||
|
||||
fail "service/state.uc must own pending reload writes"
|
||||
@@ -83,12 +83,12 @@ grep -Fq 'mode == "write-captured-reload-state"' "$STATE_UC" ||
|
||||
fail "service/state.uc must own captured reload state writes"
|
||||
grep -Fq 'mode == "reload-sing-box-runtime"' "$STATE_UC" ||
|
||||
fail "service/state.uc must own sing-box runtime reload"
|
||||
grep -Fq '"wait-podkop-stable-start"' "$LIFECYCLE_UC" ||
|
||||
grep -Fq '"wait-forkop-stable-start"' "$LIFECYCLE_UC" ||
|
||||
fail "service/lifecycle.uc must verify stable runtime after sing-box start/reload"
|
||||
grep -Fq 'SING_BOX_START_STABLE_MIN_AGE' "$LIFECYCLE_UC" ||
|
||||
fail "service/lifecycle.uc must use a dedicated sing-box start stability window"
|
||||
sing_box_start_line="$(grep -nF 'command_success_from_args([ "/etc/init.d/sing-box", "start" ])' "$LIFECYCLE_UC" | head -n1 | cut -d: -f1)"
|
||||
early_stable_line="$(awk -v start="$sing_box_start_line" 'NR > start && /"wait-podkop-stable-start"/ { print NR; exit }' "$LIFECYCLE_UC")"
|
||||
early_stable_line="$(awk -v start="$sing_box_start_line" 'NR > start && /"wait-forkop-stable-start"/ { print NR; exit }' "$LIFECYCLE_UC")"
|
||||
start_stable_min_age_line="$(awk -v start="$sing_box_start_line" 'NR > start && /SING_BOX_START_STABLE_MIN_AGE/ { print NR; exit }' "$LIFECYCLE_UC")"
|
||||
deferred_bootstrap_line="$(grep -nF '"run-deferred-bootstrap"' "$LIFECYCLE_UC" | head -n1 | cut -d: -f1)"
|
||||
[ -n "$sing_box_start_line" ] ||
|
||||
@@ -105,9 +105,9 @@ reload_stable_min_age_line="$(awk -v start="$sing_box_reload_line" 'NR > start &
|
||||
fail "service/lifecycle.uc must reload sing-box through service/state.uc"
|
||||
[ -n "$reload_stable_min_age_line" ] ||
|
||||
fail "service/lifecycle.uc must use the dedicated sing-box start stability window after sing-box reload"
|
||||
grep -Fq 'Reload verification failed after sing-box was reloaded; stopping Podkop Plus runtime' "$LIFECYCLE_UC" ||
|
||||
grep -Fq 'Reload verification failed after sing-box was reloaded; stopping Forkop runtime' "$LIFECYCLE_UC" ||
|
||||
fail "service/lifecycle.uc must fail reload when sing-box does not stay stable"
|
||||
grep -Fq 'Reload runtime restart verification failed after Podkop Plus was started; rolling back DNS changes' "$LIFECYCLE_UC" ||
|
||||
grep -Fq 'Reload runtime restart verification failed after Forkop was started; rolling back DNS changes' "$LIFECYCLE_UC" ||
|
||||
fail "service/lifecycle.uc must fail full runtime restart when sing-box does not stay stable"
|
||||
if grep -n -E 'require\("uci"\)\.cursor|uci -q|uci", "-q"|uci_get_cli|uci_exists_cli|mwan3_has_enabled_interface_from_uci_show' "$STATE_UC" >/dev/null 2>&1; then
|
||||
fail "service/state.uc must not own direct UCI CLI/cursor access"
|
||||
@@ -115,7 +115,7 @@ fi
|
||||
grep -Fq 'nft-rebuild-runtime-from-uci' "$LIFECYCLE_UC" ||
|
||||
fail "service/lifecycle.uc reload must call nft rebuild through ucode"
|
||||
if grep -Fq 'nft-create-full-runtime-from-uci' "$LIFECYCLE_UC"; then
|
||||
fail "podkop start/reload must rebuild nft runtime instead of appending to stale state"
|
||||
fail "forkop start/reload must rebuild nft runtime instead of appending to stale state"
|
||||
fi
|
||||
grep -Fq 'nft-populate-runtime-sets-from-uci' "$LIFECYCLE_UC" ||
|
||||
fail "service/lifecycle.uc reload must call nft populate through ucode"
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
STATE_UC="$ROOT_DIR/podkop/files/usr/lib/service/state.uc"
|
||||
NFT_UC="$ROOT_DIR/podkop/files/usr/lib/nft/apply.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
STATE_UC="$ROOT_DIR/forkop/files/usr/lib/service/state.uc"
|
||||
NFT_UC="$ROOT_DIR/forkop/files/usr/lib/nft/apply.uc"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
export ZAPRET_DEFAULT_NFQWS_OPT="--default-zapret"
|
||||
export ZAPRET2_DEFAULT_NFQWS2_OPT="--default-zapret2"
|
||||
export BYEDPI_DEFAULT_CMD_OPTS="--default-bye"
|
||||
export PODKOP_FAKE_INIT_CAPTURE="$WORK_DIR/pending-reload-init.args"
|
||||
export FORKOP_FAKE_INIT_CAPTURE="$WORK_DIR/pending-reload-init.args"
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$WORK_DIR"
|
||||
@@ -34,11 +34,11 @@ md5_file() {
|
||||
}
|
||||
|
||||
state_ucode() {
|
||||
ucode -L "$PODKOP_LIB" "$STATE_UC" "$@"
|
||||
ucode -L "$FORKOP_LIB" "$STATE_UC" "$@"
|
||||
}
|
||||
|
||||
nft_ucode() {
|
||||
ucode -L "$PODKOP_LIB" "$NFT_UC" "$@"
|
||||
ucode -L "$FORKOP_LIB" "$NFT_UC" "$@"
|
||||
}
|
||||
|
||||
state_ucode list-has-remote-references "local.lst https://example.com/list.txt" >/dev/null ||
|
||||
@@ -114,17 +114,17 @@ fi
|
||||
|
||||
cat >"$WORK_DIR/fake-init" <<'SH'
|
||||
#!/bin/sh
|
||||
printf '%s\n' "$1" >"$PODKOP_FAKE_INIT_CAPTURE"
|
||||
printf '%s\n' "$1" >"$FORKOP_FAKE_INIT_CAPTURE"
|
||||
SH
|
||||
chmod +x "$WORK_DIR/fake-init"
|
||||
state_ucode mark-pending-reload "$PENDING_RELOAD_FILE" "reload_busy"
|
||||
state_ucode run-pending-reload-if-requested "$PENDING_RELOAD_FILE" "$WORK_DIR/fake-init"
|
||||
for _ in $(seq 1 20); do
|
||||
[ -s "$PODKOP_FAKE_INIT_CAPTURE" ] && break
|
||||
[ -s "$FORKOP_FAKE_INIT_CAPTURE" ] && break
|
||||
sleep 0.1
|
||||
done
|
||||
assert_eq "reload" \
|
||||
"$(cat "$PODKOP_FAKE_INIT_CAPTURE")" \
|
||||
"$(cat "$FORKOP_FAKE_INIT_CAPTURE")" \
|
||||
"pending reload should invoke init.d reload"
|
||||
[ ! -e "$PENDING_RELOAD_FILE" ] ||
|
||||
fail "pending reload should be consumed when worker is started"
|
||||
@@ -191,9 +191,9 @@ cat >"$WORK_DIR/service-dns-state.json" <<'JSON'
|
||||
"server": [ "1.1.1.1#53", "8.8.8.8" ],
|
||||
"noresolv": "1",
|
||||
"cachesize": "0",
|
||||
"podkop_server": [ "127.0.0.42#53" ],
|
||||
"podkop_noresolv": "0",
|
||||
"podkop_cachesize": "1500"
|
||||
"forkop_server": [ "127.0.0.42#53" ],
|
||||
"forkop_noresolv": "0",
|
||||
"forkop_cachesize": "1500"
|
||||
},
|
||||
"legacy_dnsmasq_present": true
|
||||
}
|
||||
@@ -217,13 +217,13 @@ cat >"$WORK_DIR/dnsmasq-signature.expected" <<'EOF_DNSMASQ_SIG'
|
||||
1
|
||||
[dhcp.@dnsmasq[0].cachesize]
|
||||
0
|
||||
[dhcp.@dnsmasq[0].podkop_server]
|
||||
[dhcp.@dnsmasq[0].forkop_server]
|
||||
127.0.0.42#53
|
||||
[dhcp.@dnsmasq[0].podkop_noresolv]
|
||||
[dhcp.@dnsmasq[0].forkop_noresolv]
|
||||
0
|
||||
[dhcp.@dnsmasq[0].podkop_cachesize]
|
||||
[dhcp.@dnsmasq[0].forkop_cachesize]
|
||||
1500
|
||||
[dhcp.podkop_plus.present]
|
||||
[dhcp.forkop.present]
|
||||
1
|
||||
EOF_DNSMASQ_SIG
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
LIFECYCLE_UC="$PODKOP_LIB/service/lifecycle.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
LIFECYCLE_UC="$FORKOP_LIB/service/lifecycle.uc"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -17,8 +17,8 @@ fail() {
|
||||
}
|
||||
|
||||
lifecycle_ucode() {
|
||||
PODKOP_RUNTIME_STATE_DIR="$WORK_DIR/run" \
|
||||
ucode -L "$PODKOP_LIB" "$LIFECYCLE_UC" "$@"
|
||||
FORKOP_RUNTIME_STATE_DIR="$WORK_DIR/run" \
|
||||
ucode -L "$FORKOP_LIB" "$LIFECYCLE_UC" "$@"
|
||||
}
|
||||
|
||||
json_flatten() {
|
||||
|
||||
@@ -62,11 +62,11 @@ cat >"$WORK_DIR/different-array-order.json" <<'JSON'
|
||||
}
|
||||
JSON
|
||||
|
||||
if node "$COMPARE" "$WORK_DIR/expected.json" "$WORK_DIR/different-array-order.json" >/tmp/podkop-semantic-json.out 2>&1; then
|
||||
if node "$COMPARE" "$WORK_DIR/expected.json" "$WORK_DIR/different-array-order.json" >/tmp/forkop-semantic-json.out 2>&1; then
|
||||
fail "array order mismatch should be rejected"
|
||||
fi
|
||||
|
||||
grep -Fq '$.outbounds[0].tag: "first" != "second"' /tmp/podkop-semantic-json.out ||
|
||||
grep -Fq '$.outbounds[0].tag: "first" != "second"' /tmp/forkop-semantic-json.out ||
|
||||
fail "array order mismatch reported unexpected diff"
|
||||
|
||||
printf 'semantic JSON compare checks passed\n'
|
||||
|
||||
+33
-33
@@ -2,8 +2,8 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
SERVER_RUNTIME="$ROOT_DIR/podkop/files/usr/lib/server/service.uc"
|
||||
UCODE_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
SERVER_RUNTIME="$ROOT_DIR/forkop/files/usr/lib/server/service.uc"
|
||||
UCODE_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
STATE="$WORK_DIR/uci.state"
|
||||
LOG="$WORK_DIR/uci.log"
|
||||
@@ -59,26 +59,26 @@ LOGGER
|
||||
chmod 0755 "$WORK_DIR/bin/logger"
|
||||
|
||||
export PATH="$WORK_DIR/bin:$PATH"
|
||||
export PODKOP_UCI_STATE_FILE="$STATE"
|
||||
export PODKOP_UCI_LOG_FILE="$LOG"
|
||||
export PODKOP_CONFIG_NAME="podkop-plus"
|
||||
export PODKOP_SERVER_RUNTIME_UC="$SERVER_RUNTIME"
|
||||
export FORKOP_UCI_STATE_FILE="$STATE"
|
||||
export FORKOP_UCI_LOG_FILE="$LOG"
|
||||
export FORKOP_CONFIG_NAME="forkop"
|
||||
export FORKOP_SERVER_RUNTIME_UC="$SERVER_RUNTIME"
|
||||
|
||||
if grep -E 'uci -q|command -v uci' "$SERVER_RUNTIME" >/dev/null; then
|
||||
fail "server/service.uc must use ucode UCI access instead of shelling out to uci"
|
||||
fi
|
||||
|
||||
cat >"$STATE" <<'EOF_STATE'
|
||||
podkop-plus.vless=server
|
||||
podkop-plus.vless.protocol=vless
|
||||
podkop-plus.vless.server_users=client|22222222-2222-4222-8222-222222222222|xtls-rprx-vision
|
||||
podkop-plus.socks=server
|
||||
podkop-plus.socks.protocol=socks
|
||||
podkop-plus.socks.label=desk
|
||||
podkop-plus.tailscale=server
|
||||
podkop-plus.tailscale.protocol=tailscale
|
||||
podkop-plus.json=server
|
||||
podkop-plus.json.protocol=json_inbound
|
||||
forkop.vless=server
|
||||
forkop.vless.protocol=vless
|
||||
forkop.vless.server_users=client|22222222-2222-4222-8222-222222222222|xtls-rprx-vision
|
||||
forkop.socks=server
|
||||
forkop.socks.protocol=socks
|
||||
forkop.socks.label=desk
|
||||
forkop.tailscale=server
|
||||
forkop.tailscale.protocol=tailscale
|
||||
forkop.json=server
|
||||
forkop.json.protocol=json_inbound
|
||||
EOF_STATE
|
||||
|
||||
ucode -L "$UCODE_LIB" "$SERVER_RUNTIME" prepare-all-defaults
|
||||
@@ -93,22 +93,22 @@ assert_value() {
|
||||
[ "$actual" = "$expected" ] || fail "$path: expected '$expected', got '$actual'"
|
||||
}
|
||||
|
||||
assert_value podkop-plus.vless.security reality
|
||||
assert_value podkop-plus.vless.listen 0.0.0.0
|
||||
assert_value podkop-plus.vless.listen_port 443
|
||||
assert_value podkop-plus.vless.server_uuid 22222222-2222-4222-8222-222222222222
|
||||
assert_value podkop-plus.vless.vless_flow xtls-rprx-vision
|
||||
assert_value podkop-plus.vless.reality_short_id abcd1234
|
||||
assert_value podkop-plus.vless.reality_private_key private-key
|
||||
assert_value podkop-plus.vless.reality_public_key public-key
|
||||
assert_value podkop-plus.socks.security none
|
||||
assert_value podkop-plus.socks.server_username desk
|
||||
assert_value podkop-plus.socks.server_password generated-password
|
||||
assert_value podkop-plus.tailscale.security none
|
||||
assert_value podkop-plus.tailscale.tailscale_control_url https://controlplane.tailscale.com
|
||||
assert_value podkop-plus.tailscale.tailscale_hostname podkop-tailscale
|
||||
assert_value podkop-plus.tailscale.tailscale_advertise_exit_node 1
|
||||
assert_value podkop-plus.json.security none
|
||||
grep -Fxq 'commit podkop-plus' "$LOG" || fail 'expected config commit'
|
||||
assert_value forkop.vless.security reality
|
||||
assert_value forkop.vless.listen 0.0.0.0
|
||||
assert_value forkop.vless.listen_port 443
|
||||
assert_value forkop.vless.server_uuid 22222222-2222-4222-8222-222222222222
|
||||
assert_value forkop.vless.vless_flow xtls-rprx-vision
|
||||
assert_value forkop.vless.reality_short_id abcd1234
|
||||
assert_value forkop.vless.reality_private_key private-key
|
||||
assert_value forkop.vless.reality_public_key public-key
|
||||
assert_value forkop.socks.security none
|
||||
assert_value forkop.socks.server_username desk
|
||||
assert_value forkop.socks.server_password generated-password
|
||||
assert_value forkop.tailscale.security none
|
||||
assert_value forkop.tailscale.tailscale_control_url https://controlplane.tailscale.com
|
||||
assert_value forkop.tailscale.tailscale_hostname forkop-tailscale
|
||||
assert_value forkop.tailscale.tailscale_advertise_exit_node 1
|
||||
assert_value forkop.json.security none
|
||||
grep -Fxq 'commit forkop' "$LOG" || fail 'expected config commit'
|
||||
|
||||
printf 'server runtime checks passed\n'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
RELOAD_UC="$ROOT_DIR/podkop/files/usr/lib/service/reload.uc"
|
||||
RELOAD_UC="$ROOT_DIR/forkop/files/usr/lib/service/reload.uc"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
|
||||
+21
-21
@@ -2,18 +2,18 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_BIN="$ROOT_DIR/podkop/files/usr/bin/podkop"
|
||||
PODKOP_INIT="$ROOT_DIR/podkop/files/etc/init.d/podkop"
|
||||
CLI_UC="$PODKOP_BIN"
|
||||
LIFECYCLE_UC="$ROOT_DIR/podkop/files/usr/lib/service/lifecycle.uc"
|
||||
INITD_UC="$ROOT_DIR/podkop/files/usr/lib/service/initd.uc"
|
||||
STATE_UC="$ROOT_DIR/podkop/files/usr/lib/service/state.uc"
|
||||
UI_UC="$ROOT_DIR/podkop/files/usr/lib/service/ui.uc"
|
||||
UPDATES_UC="$ROOT_DIR/podkop/files/usr/lib/components/updates.uc"
|
||||
SUBSCRIPTION_CACHE_UC="$ROOT_DIR/podkop/files/usr/lib/subscription/cache.uc"
|
||||
NFQUEUE_RUNTIME_UC="$ROOT_DIR/podkop/files/usr/lib/providers/nfqueue/runtime.uc"
|
||||
BYEDPI_RUNTIME_UC="$ROOT_DIR/podkop/files/usr/lib/providers/byedpi/runtime.uc"
|
||||
PRIORITY_UC="$ROOT_DIR/podkop/files/usr/lib/singbox/priority.uc"
|
||||
FORKOP_BIN="$ROOT_DIR/forkop/files/usr/bin/forkop"
|
||||
FORKOP_INIT="$ROOT_DIR/forkop/files/etc/init.d/forkop"
|
||||
CLI_UC="$FORKOP_BIN"
|
||||
LIFECYCLE_UC="$ROOT_DIR/forkop/files/usr/lib/service/lifecycle.uc"
|
||||
INITD_UC="$ROOT_DIR/forkop/files/usr/lib/service/initd.uc"
|
||||
STATE_UC="$ROOT_DIR/forkop/files/usr/lib/service/state.uc"
|
||||
UI_UC="$ROOT_DIR/forkop/files/usr/lib/service/ui.uc"
|
||||
UPDATES_UC="$ROOT_DIR/forkop/files/usr/lib/components/updates.uc"
|
||||
SUBSCRIPTION_CACHE_UC="$ROOT_DIR/forkop/files/usr/lib/subscription/cache.uc"
|
||||
NFQUEUE_RUNTIME_UC="$ROOT_DIR/forkop/files/usr/lib/providers/nfqueue/runtime.uc"
|
||||
BYEDPI_RUNTIME_UC="$ROOT_DIR/forkop/files/usr/lib/providers/byedpi/runtime.uc"
|
||||
PRIORITY_UC="$ROOT_DIR/forkop/files/usr/lib/singbox/priority.uc"
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
@@ -31,7 +31,7 @@ reject_pattern() {
|
||||
local pattern="$1"
|
||||
local label="$2"
|
||||
|
||||
! grep -Fq "$pattern" "$PODKOP_BIN" || fail "$label"
|
||||
! grep -Fq "$pattern" "$FORKOP_BIN" || fail "$label"
|
||||
}
|
||||
|
||||
require_file_pattern() {
|
||||
@@ -42,18 +42,18 @@ require_file_pattern() {
|
||||
grep -Fq "$pattern" "$file" || fail "$label"
|
||||
}
|
||||
|
||||
[ -r "$PODKOP_BIN" ] || fail "podkop binary source is missing"
|
||||
[ -r "$PODKOP_BIN" ] || fail "podkop entrypoint is missing"
|
||||
[ -r "$FORKOP_BIN" ] || fail "forkop binary source is missing"
|
||||
[ -r "$FORKOP_BIN" ] || fail "forkop entrypoint is missing"
|
||||
[ -r "$LIFECYCLE_UC" ] || fail "service/lifecycle.uc is missing"
|
||||
|
||||
grep -Fq '#!/usr/bin/ucode' "$PODKOP_BIN" ||
|
||||
fail "podkop entrypoint must be a direct ucode executable"
|
||||
grep -Fq '#!/usr/bin/ucode' "$FORKOP_BIN" ||
|
||||
fail "forkop entrypoint must be a direct ucode executable"
|
||||
grep -Fq 'service/lifecycle.uc' "$CLI_UC" ||
|
||||
fail "service/cli.uc must dispatch startup through service/lifecycle.uc"
|
||||
reject_pattern "trap " \
|
||||
"podkop shell entrypoint must not own startup fail-safe traps"
|
||||
"forkop shell entrypoint must not own startup fail-safe traps"
|
||||
reject_pattern "clear_startup_failsafe_trap" \
|
||||
"podkop shell entrypoint must not keep old trap helper"
|
||||
"forkop shell entrypoint must not keep old trap helper"
|
||||
require_pattern 'module_background(UPDATES_UC, [ "list-update" ])' \
|
||||
"startup list_update background job must be owned by service/lifecycle.uc"
|
||||
require_pattern 'module_background(DIAGNOSTICS_UC, [ "get-system-info" ])' \
|
||||
@@ -91,9 +91,9 @@ require_file_pattern "$INITD_UC" 'reload pending >/dev/null 2>&1 1000>&- &' \
|
||||
"service/initd.uc pending reload worker must close inherited procd lock fd"
|
||||
require_file_pattern "$STATE_UC" 'reload pending >/dev/null 2>&1 1000>&- &' \
|
||||
"service/state.uc pending reload worker must close inherited procd lock fd"
|
||||
require_file_pattern "$PODKOP_INIT" 'PODKOP_LAST_START_STATUS="$?"' \
|
||||
require_file_pattern "$FORKOP_INIT" 'FORKOP_LAST_START_STATUS="$?"' \
|
||||
"init.d start_service must preserve backend start status for rc.common"
|
||||
require_file_pattern "$PODKOP_INIT" 'service_started()' \
|
||||
require_file_pattern "$FORKOP_INIT" 'service_started()' \
|
||||
"init.d must return preserved start status through rc.common service_started hook"
|
||||
require_file_pattern "$UI_UC" '>/dev/null 2>&1 1000>&- & echo $!' \
|
||||
"service/ui.uc service action workers must close inherited procd lock fd"
|
||||
|
||||
+31
-30
@@ -2,34 +2,34 @@
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_FILES="$ROOT_DIR/podkop/files"
|
||||
PODKOP_BIN="$PODKOP_FILES/usr/bin/podkop"
|
||||
PODKOP_LIB="$PODKOP_FILES/usr/lib"
|
||||
PODKOP_INIT="$PODKOP_FILES/etc/init.d/podkop"
|
||||
LUCI_ROOT="$ROOT_DIR/luci-app-podkop-plus/root"
|
||||
LUCI_UCI_DEFAULTS="$LUCI_ROOT/etc/uci-defaults/50_luci-podkop-plus"
|
||||
FORKOP_FILES="$ROOT_DIR/forkop/files"
|
||||
FORKOP_BIN="$FORKOP_FILES/usr/bin/forkop"
|
||||
FORKOP_LIB="$FORKOP_FILES/usr/lib"
|
||||
FORKOP_INIT="$FORKOP_FILES/etc/init.d/forkop"
|
||||
LUCI_ROOT="$ROOT_DIR/luci-app-forkop/root"
|
||||
LUCI_UCI_DEFAULTS="$LUCI_ROOT/etc/uci-defaults/50_luci-forkop"
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ -d "$PODKOP_LIB" ] || fail "runtime library directory is missing"
|
||||
[ -r "$PODKOP_BIN" ] || fail "podkop ucode entrypoint is missing"
|
||||
[ -r "$PODKOP_INIT" ] || fail "podkop init.d entrypoint is missing"
|
||||
[ -d "$FORKOP_LIB" ] || fail "runtime library directory is missing"
|
||||
[ -r "$FORKOP_BIN" ] || fail "forkop ucode entrypoint is missing"
|
||||
[ -r "$FORKOP_INIT" ] || fail "forkop init.d entrypoint is missing"
|
||||
[ -r "$LUCI_UCI_DEFAULTS" ] || fail "LuCI uci-defaults entrypoint is missing"
|
||||
|
||||
runtime_shell_files="$(find "$PODKOP_LIB" -type f -name '*.sh' -print)"
|
||||
runtime_shell_files="$(find "$FORKOP_LIB" -type f -name '*.sh' -print)"
|
||||
[ -z "$runtime_shell_files" ] ||
|
||||
fail "runtime library must not contain shell owners: $runtime_shell_files"
|
||||
|
||||
legacy_shell_owners='runtime_state\.sh|rules_nft_runtime\.sh|config_validation\.sh|sing_box_runtime\.sh|updates_runtime\.sh|updater\.sh|status_diagnostics\.sh|helpers\.sh|constants\.sh|subscription_runtime\.sh|byedpi\.sh|zapret\.sh|zapret2\.sh'
|
||||
if find "$PODKOP_FILES" -type f -print | grep -E "$legacy_shell_owners" >/dev/null 2>&1; then
|
||||
fail "legacy runtime shell owner file returned under podkop/files"
|
||||
if find "$FORKOP_FILES" -type f -print | grep -E "$legacy_shell_owners" >/dev/null 2>&1; then
|
||||
fail "legacy runtime shell owner file returned under forkop/files"
|
||||
fi
|
||||
|
||||
shell_scripts="$(
|
||||
find "$PODKOP_FILES" "$LUCI_ROOT" -type f -print |
|
||||
find "$FORKOP_FILES" "$LUCI_ROOT" -type f -print |
|
||||
while IFS= read -r file; do
|
||||
first_line="$(sed -n '1p' "$file")"
|
||||
case "$first_line" in
|
||||
@@ -38,13 +38,14 @@ shell_scripts="$(
|
||||
;;
|
||||
esac
|
||||
done |
|
||||
sort
|
||||
LC_ALL=C sort
|
||||
)"
|
||||
|
||||
expected_shell_scripts="$(
|
||||
printf '%s\n' \
|
||||
'luci-app-podkop-plus/root/etc/uci-defaults/50_luci-podkop-plus' \
|
||||
'podkop/files/etc/init.d/podkop'
|
||||
'luci-app-forkop/root/etc/uci-defaults/50_luci-forkop' \
|
||||
'forkop/files/etc/init.d/forkop' |
|
||||
LC_ALL=C sort
|
||||
)"
|
||||
|
||||
[ "$shell_scripts" = "$expected_shell_scripts" ] ||
|
||||
@@ -54,33 +55,33 @@ $expected_shell_scripts
|
||||
actual:
|
||||
$shell_scripts"
|
||||
|
||||
grep -Fq '#!/usr/bin/ucode' "$PODKOP_BIN" ||
|
||||
fail "/usr/bin/podkop must remain a direct ucode executable"
|
||||
grep -Fq 'function command_spec(command)' "$PODKOP_BIN" ||
|
||||
fail "/usr/bin/podkop must own command routing in ucode"
|
||||
if grep -n -E '#!/bin/(ba)?sh|exec[[:space:]]+ucode|run_module\(|PODKOP_COMMAND' "$PODKOP_BIN" >/dev/null 2>&1; then
|
||||
fail "/usr/bin/podkop must not regress to a shell loader or shell router"
|
||||
grep -Fq '#!/usr/bin/ucode' "$FORKOP_BIN" ||
|
||||
fail "/usr/bin/forkop must remain a direct ucode executable"
|
||||
grep -Fq 'function command_spec(command)' "$FORKOP_BIN" ||
|
||||
fail "/usr/bin/forkop must own command routing in ucode"
|
||||
if grep -n -E '#!/bin/(ba)?sh|exec[[:space:]]+ucode|run_module\(|FORKOP_COMMAND' "$FORKOP_BIN" >/dev/null 2>&1; then
|
||||
fail "/usr/bin/forkop must not regress to a shell loader or shell router"
|
||||
fi
|
||||
|
||||
grep -Fq 'PODKOP_INITD_UC="$PODKOP_LIB/service/initd.uc"' "$PODKOP_INIT" ||
|
||||
grep -Fq 'FORKOP_INITD_UC="$FORKOP_LIB/service/initd.uc"' "$FORKOP_INIT" ||
|
||||
fail "init.d must delegate service orchestration to service/initd.uc"
|
||||
grep -Fq 'initd_ucode start-service' "$PODKOP_INIT" ||
|
||||
grep -Fq 'initd_ucode start-service' "$FORKOP_INIT" ||
|
||||
fail "init.d start path must delegate to ucode"
|
||||
grep -Fq 'initd_ucode stop-service' "$PODKOP_INIT" ||
|
||||
grep -Fq 'initd_ucode stop-service' "$FORKOP_INIT" ||
|
||||
fail "init.d stop path must delegate to ucode"
|
||||
grep -Fq 'initd_ucode reload-service' "$PODKOP_INIT" ||
|
||||
grep -Fq 'initd_ucode reload-service' "$FORKOP_INIT" ||
|
||||
fail "init.d reload path must delegate to ucode"
|
||||
grep -Fq 'initd_ucode trigger-plan' "$PODKOP_INIT" ||
|
||||
grep -Fq 'initd_ucode trigger-plan' "$FORKOP_INIT" ||
|
||||
fail "init.d trigger decisions must be produced by ucode"
|
||||
|
||||
if grep -n -E '(^|[^[:alnum:]_])(uci|config_load|config_get|config_foreach|jsonfilter|nft|iptables|ip6?tables|sing-box|dnsmasq|curl|wget|opkg|apk)([[:space:]]|$)' "$PODKOP_INIT" >/dev/null 2>&1; then
|
||||
if grep -n -E '(^|[^[:alnum:]_])(uci|config_load|config_get|config_foreach|jsonfilter|nft|iptables|ip6?tables|sing-box|dnsmasq|curl|wget|opkg|apk)([[:space:]]|$)' "$FORKOP_INIT" >/dev/null 2>&1; then
|
||||
fail "init.d must not own UCI, routing, download, package, dnsmasq, nft, or sing-box decisions"
|
||||
fi
|
||||
if grep -n -E 'PODKOP_RELOAD_LOCK|PODKOP_URLTEST_SELECTOR_SWITCHES|capture_reload_state|populate_nft_runtime_sets|rebuild_nft_runtime|apply_pending_urltest_selector_switches' "$PODKOP_INIT" >/dev/null 2>&1; then
|
||||
if grep -n -E 'FORKOP_RELOAD_LOCK|FORKOP_URLTEST_SELECTOR_SWITCHES|capture_reload_state|populate_nft_runtime_sets|rebuild_nft_runtime|apply_pending_urltest_selector_switches' "$FORKOP_INIT" >/dev/null 2>&1; then
|
||||
fail "init.d must not own runtime state or reload decisions"
|
||||
fi
|
||||
|
||||
grep -Fq '/usr/bin/podkop-plus luci_postinst' "$LUCI_UCI_DEFAULTS" ||
|
||||
grep -Fq '/usr/bin/forkop luci_postinst' "$LUCI_UCI_DEFAULTS" ||
|
||||
fail "LuCI uci-defaults must delegate postinstall work to ucode"
|
||||
if grep -n -E '(^|[^[:alnum:]_])(uci|rm|logger|rpcd|killall|jsonfilter|config_load|config_get)([[:space:]]|$)' "$LUCI_UCI_DEFAULTS" >/dev/null 2>&1; then
|
||||
fail "LuCI uci-defaults must not own cache, rpcd, logging, or UCI logic"
|
||||
|
||||
+33
-33
@@ -2,13 +2,13 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
PODKOP_BIN="$ROOT_DIR/podkop/files/usr/bin/podkop"
|
||||
CLI_UC="$PODKOP_BIN"
|
||||
SING_BOX_RUNTIME_SH="$PODKOP_LIB/sing_box_runtime.sh"
|
||||
LIFECYCLE_UC="$PODKOP_LIB/service/lifecycle.uc"
|
||||
SINGBOX_RUNTIME_UC="$PODKOP_LIB/singbox/runtime.uc"
|
||||
SINGBOX_GENERATOR_UC="$PODKOP_LIB/singbox/generator.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
FORKOP_BIN="$ROOT_DIR/forkop/files/usr/bin/forkop"
|
||||
CLI_UC="$FORKOP_BIN"
|
||||
SING_BOX_RUNTIME_SH="$FORKOP_LIB/sing_box_runtime.sh"
|
||||
LIFECYCLE_UC="$FORKOP_LIB/service/lifecycle.uc"
|
||||
SINGBOX_RUNTIME_UC="$FORKOP_LIB/singbox/runtime.uc"
|
||||
SINGBOX_GENERATOR_UC="$FORKOP_LIB/singbox/generator.uc"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -23,13 +23,13 @@ fail() {
|
||||
|
||||
[ ! -e "$SING_BOX_RUNTIME_SH" ] ||
|
||||
fail "sing_box_runtime.sh shell owner must be removed"
|
||||
grep -Fq '#!/usr/bin/ucode' "$PODKOP_BIN" ||
|
||||
fail "podkop entrypoint must be a direct ucode executable"
|
||||
grep -Fq '#!/usr/bin/ucode' "$FORKOP_BIN" ||
|
||||
fail "forkop entrypoint must be a direct ucode executable"
|
||||
grep -Fq 'service/lifecycle.uc' "$CLI_UC" ||
|
||||
fail "service/cli.uc must dispatch lifecycle orchestration through service/lifecycle.uc"
|
||||
grep -Fq 'singbox/runtime.uc' "$LIFECYCLE_UC" ||
|
||||
fail "service/lifecycle.uc must call singbox/runtime.uc for sing-box runtime operations"
|
||||
if grep -R -n -E 'sing_box_runtime_ucode|rulesets_ucode|sing_box_configure_service|sing_box_init_config|get_service_listen_address|get_device_ipv4_address|get_download_detour_tag' "$PODKOP_BIN" "$PODKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
|
||||
if grep -R -n -E 'sing_box_runtime_ucode|rulesets_ucode|sing_box_configure_service|sing_box_init_config|get_service_listen_address|get_device_ipv4_address|get_download_detour_tag' "$FORKOP_BIN" "$FORKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
|
||||
fail "sing-box runtime shell symbols must not remain"
|
||||
fi
|
||||
grep -Fq 'mode == "configure-service"' "$SINGBOX_RUNTIME_UC" ||
|
||||
@@ -54,7 +54,7 @@ if grep -n -E 'require\("uci"\)\.cursor|uci -q|uci", "-q"' "$SINGBOX_GENERATOR_U
|
||||
fi
|
||||
grep -Fq 'require("core.uci")' "$SINGBOX_GENERATOR_UC" ||
|
||||
fail "singbox/generator.uc must import core.uci"
|
||||
grep -Fq 'PODKOP_RULE_CONDITION_CACHE_DIR' "$LIFECYCLE_UC" ||
|
||||
grep -Fq 'FORKOP_RULE_CONDITION_CACHE_DIR' "$LIFECYCLE_UC" ||
|
||||
fail "service/lifecycle.uc must pass rule-condition cache dir through module environment"
|
||||
if awk '
|
||||
/"write-current-reload-state-clean"|"write-captured-reload-state"/ {
|
||||
@@ -79,7 +79,7 @@ fi
|
||||
mkdir -p "$WORK_DIR/etc" "$WORK_DIR/tmp"
|
||||
printf 'old config\n' >"$WORK_DIR/etc/config.json"
|
||||
printf 'new config\n' >"$WORK_DIR/tmp/config.json"
|
||||
ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" save-config-file-fixture \
|
||||
ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" save-config-file-fixture \
|
||||
"$WORK_DIR/tmp/config.json" "$WORK_DIR/etc/config.json"
|
||||
[ "$(cat "$WORK_DIR/etc/config.json")" = "new config" ] ||
|
||||
fail "singbox/runtime.uc must replace an existing config from a temp file"
|
||||
@@ -92,7 +92,7 @@ generate_config() {
|
||||
local mwan3_active="${3:-0}"
|
||||
|
||||
mkdir -p "$output.section-cache" "$output.rulesets"
|
||||
ucode -L "$PODKOP_LIB" "$PODKOP_LIB/singbox/generator.uc" generate-config-fixture \
|
||||
ucode -L "$FORKOP_LIB" "$FORKOP_LIB/singbox/generator.uc" generate-config-fixture \
|
||||
"$fixture" "$output" "127.0.0.1" "$mwan3_active"
|
||||
}
|
||||
|
||||
@@ -102,8 +102,8 @@ generate_config_with_subscription_cache() {
|
||||
|
||||
mkdir -p "$output.section-cache" "$output.rulesets"
|
||||
TMP_SUBSCRIPTION_FOLDER="$WORK_DIR/subscriptions" \
|
||||
PODKOP_PERSISTENT_SUBSCRIPTION_CACHE_DIR="$WORK_DIR/persistent-subscription-cache" \
|
||||
ucode -L "$PODKOP_LIB" "$PODKOP_LIB/singbox/generator.uc" generate-config-fixture \
|
||||
FORKOP_PERSISTENT_SUBSCRIPTION_CACHE_DIR="$WORK_DIR/persistent-subscription-cache" \
|
||||
ucode -L "$FORKOP_LIB" "$FORKOP_LIB/singbox/generator.uc" generate-config-fixture \
|
||||
"$fixture" "$output" "127.0.0.1" "0"
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ cat >"$WORK_DIR/no-enabled-fixture.json" <<'JSON'
|
||||
}
|
||||
JSON
|
||||
|
||||
if ucode -L "$PODKOP_LIB" "$SINGBOX_GENERATOR_UC" generate-config-fixture \
|
||||
if ucode -L "$FORKOP_LIB" "$SINGBOX_GENERATOR_UC" generate-config-fixture \
|
||||
"$WORK_DIR/no-enabled-fixture.json" "$WORK_DIR/no-enabled.json" "127.0.0.1" "0" \
|
||||
>"$WORK_DIR/no-enabled.stdout" 2>"$WORK_DIR/no-enabled.stderr"; then
|
||||
fail "generator should reject a config without enabled sections"
|
||||
@@ -132,21 +132,21 @@ grep -Fxq 'no enabled sections' "$WORK_DIR/no-enabled.stderr" ||
|
||||
fail "generator failure reason should be concise"
|
||||
|
||||
cat >"$WORK_DIR/generator-uci.state" <<'EOF_UCI'
|
||||
podkop-plus.settings=settings
|
||||
podkop-plus.settings.dns_server=1.1.1.1
|
||||
podkop-plus.settings.bootstrap_dns_server=1.1.1.1
|
||||
podkop-plus.settings.config_path=/tmp/sing-box/config.json
|
||||
podkop-plus.settings.cache_path=/tmp/sing-box/cache.db
|
||||
podkop-plus.settings.log_level=warn
|
||||
podkop-plus.uci_proxy=section
|
||||
podkop-plus.uci_proxy.enabled=1
|
||||
podkop-plus.uci_proxy.action=connection
|
||||
podkop-plus.uci_proxy.outbound_jsons={"type":"direct"}
|
||||
podkop-plus.uci_proxy.domain_suffix=example.org
|
||||
forkop.settings=settings
|
||||
forkop.settings.dns_server=1.1.1.1
|
||||
forkop.settings.bootstrap_dns_server=1.1.1.1
|
||||
forkop.settings.config_path=/tmp/sing-box/config.json
|
||||
forkop.settings.cache_path=/tmp/sing-box/cache.db
|
||||
forkop.settings.log_level=warn
|
||||
forkop.uci_proxy=section
|
||||
forkop.uci_proxy.enabled=1
|
||||
forkop.uci_proxy.action=connection
|
||||
forkop.uci_proxy.outbound_jsons={"type":"direct"}
|
||||
forkop.uci_proxy.domain_suffix=example.org
|
||||
EOF_UCI
|
||||
PODKOP_UCI_STATE_FILE="$WORK_DIR/generator-uci.state" \
|
||||
PODKOP_SECTION_CACHE_DIR="$WORK_DIR/generated-from-uci.section-cache" \
|
||||
ucode -L "$PODKOP_LIB" "$SINGBOX_GENERATOR_UC" generate-config "$WORK_DIR/generated-from-uci.json" "127.0.0.1" "0"
|
||||
FORKOP_UCI_STATE_FILE="$WORK_DIR/generator-uci.state" \
|
||||
FORKOP_SECTION_CACHE_DIR="$WORK_DIR/generated-from-uci.section-cache" \
|
||||
ucode -L "$FORKOP_LIB" "$SINGBOX_GENERATOR_UC" generate-config "$WORK_DIR/generated-from-uci.json" "127.0.0.1" "0"
|
||||
grep -Fq '"example.org"' "$WORK_DIR/generated-from-uci.json" ||
|
||||
fail "singbox/generator.uc must read section matchers from core.uci"
|
||||
grep -Fq '"uci_proxy-out"' "$WORK_DIR/generated-from-uci.json" ||
|
||||
@@ -644,7 +644,7 @@ cat >"$WORK_DIR/subscriptions/grouped-subscription-1.json" <<'JSON'
|
||||
"interval": "10m",
|
||||
"tolerance": 50,
|
||||
"remark": "Provider Group",
|
||||
"__podkop_allow_group": true
|
||||
"__forkop_allow_group": true
|
||||
},
|
||||
{
|
||||
"type": "vless",
|
||||
@@ -653,7 +653,7 @@ cat >"$WORK_DIR/subscriptions/grouped-subscription-1.json" <<'JSON'
|
||||
"server_port": 443,
|
||||
"uuid": "00000000-0000-4000-8000-000000000001",
|
||||
"tls": { "enabled": true, "server_name": "example.com" },
|
||||
"__podkop_hidden": true
|
||||
"__forkop_hidden": true
|
||||
},
|
||||
{
|
||||
"type": "vless",
|
||||
@@ -662,7 +662,7 @@ cat >"$WORK_DIR/subscriptions/grouped-subscription-1.json" <<'JSON'
|
||||
"server_port": 443,
|
||||
"uuid": "00000000-0000-4000-8000-000000000002",
|
||||
"tls": { "enabled": true, "server_name": "example.org" },
|
||||
"__podkop_hidden": true
|
||||
"__forkop_hidden": true
|
||||
},
|
||||
{
|
||||
"type": "direct",
|
||||
|
||||
+12
-12
@@ -2,8 +2,8 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
RULESETS_UC="$ROOT_DIR/podkop/files/usr/lib/singbox/rulesets.uc"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
RULESETS_UC="$ROOT_DIR/forkop/files/usr/lib/singbox/rulesets.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
@@ -19,32 +19,32 @@ assert_eq() {
|
||||
}
|
||||
|
||||
assert_eq srs \
|
||||
"$(ucode -L "$PODKOP_LIB" "$RULESETS_UC" file-extension 'https://example.com/path/rule.srs?token=1#x')" \
|
||||
"$(ucode -L "$FORKOP_LIB" "$RULESETS_UC" file-extension 'https://example.com/path/rule.srs?token=1#x')" \
|
||||
"ruleset file extension"
|
||||
ucode -L "$PODKOP_LIB" "$RULESETS_UC" is-community youtube >/dev/null ||
|
||||
ucode -L "$FORKOP_LIB" "$RULESETS_UC" is-community youtube >/dev/null ||
|
||||
fail "youtube should be a community ruleset"
|
||||
if ucode -L "$PODKOP_LIB" "$RULESETS_UC" is-community unknown_service >/dev/null 2>&1; then
|
||||
if ucode -L "$FORKOP_LIB" "$RULESETS_UC" is-community unknown_service >/dev/null 2>&1; then
|
||||
fail "unknown service should not be a community ruleset"
|
||||
fi
|
||||
assert_eq domains \
|
||||
"$(ucode -L "$PODKOP_LIB" "$RULESETS_UC" kind-from-reference-hint 'https://example.com/geosite-custom.srs')" \
|
||||
"$(ucode -L "$FORKOP_LIB" "$RULESETS_UC" kind-from-reference-hint 'https://example.com/geosite-custom.srs')" \
|
||||
"domain ruleset hint"
|
||||
assert_eq subnets \
|
||||
"$(ucode -L "$PODKOP_LIB" "$RULESETS_UC" kind-from-reference-hint '/tmp/geoip-cidr.json')" \
|
||||
"$(ucode -L "$FORKOP_LIB" "$RULESETS_UC" kind-from-reference-hint '/tmp/geoip-cidr.json')" \
|
||||
"subnet ruleset hint"
|
||||
assert_eq unknown \
|
||||
"$(ucode -L "$PODKOP_LIB" "$RULESETS_UC" kind-from-reference-hint '/tmp/custom.srs')" \
|
||||
"$(ucode -L "$FORKOP_LIB" "$RULESETS_UC" kind-from-reference-hint '/tmp/custom.srs')" \
|
||||
"unknown ruleset hint"
|
||||
assert_eq source \
|
||||
"$(ucode -L "$PODKOP_LIB" "$RULESETS_UC" remote-format 'https://example.com/rules.json')" \
|
||||
"$(ucode -L "$FORKOP_LIB" "$RULESETS_UC" remote-format 'https://example.com/rules.json')" \
|
||||
"json remote ruleset format"
|
||||
assert_eq binary \
|
||||
"$(ucode -L "$PODKOP_LIB" "$RULESETS_UC" remote-format 'https://example.com/rules.srs')" \
|
||||
"$(ucode -L "$FORKOP_LIB" "$RULESETS_UC" remote-format 'https://example.com/rules.srs')" \
|
||||
"srs remote ruleset format"
|
||||
assert_eq binary \
|
||||
"$(ucode -L "$PODKOP_LIB" "$RULESETS_UC" remote-format 'https://example.com/rules.unknown')" \
|
||||
"$(ucode -L "$FORKOP_LIB" "$RULESETS_UC" remote-format 'https://example.com/rules.unknown')" \
|
||||
"unknown remote ruleset format"
|
||||
|
||||
ucode -L "$PODKOP_LIB" -e 'let rulesets = require("singbox.rulesets"); if (rulesets.kind_from_reference_hint("geoip") != "subnets") exit(1);'
|
||||
ucode -L "$FORKOP_LIB" -e 'let rulesets = require("singbox.rulesets"); if (rulesets.kind_from_reference_hint("geoip") != "subnets") exit(1);'
|
||||
|
||||
printf 'singbox rulesets checks passed\n'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PARSER="$ROOT_DIR/podkop/files/usr/lib/subscription/parser.uc"
|
||||
PARSER="$ROOT_DIR/forkop/files/usr/lib/subscription/parser.uc"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CACHE_UC="$ROOT_DIR/podkop/files/usr/lib/subscription/cache.uc"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
CACHE_UC="$ROOT_DIR/forkop/files/usr/lib/subscription/cache.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -25,7 +25,7 @@ assert_eq() {
|
||||
}
|
||||
|
||||
cache_ucode() {
|
||||
ucode -L "$PODKOP_LIB" "$CACHE_UC" "$@"
|
||||
ucode -L "$FORKOP_LIB" "$CACHE_UC" "$@"
|
||||
}
|
||||
|
||||
assert_eq "first second" \
|
||||
@@ -44,24 +44,24 @@ if cache_ucode state-list-contains "first second" third >/dev/null 2>&1; then
|
||||
fail "state list should not contain third"
|
||||
fi
|
||||
|
||||
SUBSCRIPTION_RUNTIME_SH="$ROOT_DIR/podkop/files/usr/lib/subscription_runtime.sh"
|
||||
UPDATES_RUNTIME_SH="$ROOT_DIR/podkop/files/usr/lib/updates_runtime.sh"
|
||||
SUBSCRIPTION_RUNTIME_SH="$ROOT_DIR/forkop/files/usr/lib/subscription_runtime.sh"
|
||||
UPDATES_RUNTIME_SH="$ROOT_DIR/forkop/files/usr/lib/updates_runtime.sh"
|
||||
|
||||
[ ! -e "$SUBSCRIPTION_RUNTIME_SH" ] ||
|
||||
fail "subscription_runtime.sh shell owner must be removed"
|
||||
[ ! -e "$UPDATES_RUNTIME_SH" ] ||
|
||||
fail "updates_runtime.sh shell owner must be removed"
|
||||
|
||||
if grep -R -n "subscription_runtime.sh" "$ROOT_DIR/podkop/files" >/dev/null 2>&1; then
|
||||
if grep -R -n "subscription_runtime.sh" "$ROOT_DIR/forkop/files" >/dev/null 2>&1; then
|
||||
fail "runtime files must not reference subscription_runtime.sh"
|
||||
fi
|
||||
|
||||
subscription_runtime_shell_symbols='subscription_runtime_|prepare_subscription_caches_for_startup|prepare_subscription_caches_for_runtime_generation|run_deferred_subscription_bootstrap|stop_deferred_subscription_bootstrap_retry_worker'
|
||||
if grep -R -n -E "$subscription_runtime_shell_symbols" "$ROOT_DIR/podkop/files/usr/bin/podkop" "$ROOT_DIR/podkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
|
||||
if grep -R -n -E "$subscription_runtime_shell_symbols" "$ROOT_DIR/forkop/files/usr/bin/forkop" "$ROOT_DIR/forkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
|
||||
fail "subscription_runtime shell symbols must not remain in runtime shell"
|
||||
fi
|
||||
|
||||
if grep -R -n -E 'get_subscription_metadata_path|get_outbound_metadata_path' "$ROOT_DIR/podkop/files/usr/bin/podkop" "$ROOT_DIR/podkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
|
||||
if grep -R -n -E 'get_subscription_metadata_path|get_outbound_metadata_path' "$ROOT_DIR/forkop/files/usr/bin/forkop" "$ROOT_DIR/forkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
|
||||
fail "subscription metadata path helpers must be owned by subscription/cache.uc"
|
||||
fi
|
||||
|
||||
@@ -94,10 +94,10 @@ grep -q 'mode == "stop-deferred-bootstrap-worker"' "$CACHE_UC" ||
|
||||
assert_eq "proxy-subscription-2" \
|
||||
"$(cache_ucode source-id proxy 2)" \
|
||||
"source id mode"
|
||||
assert_eq "/var/run/podkop-plus/subscription-metadata/proxy.json" \
|
||||
assert_eq "/var/run/forkop/subscription-metadata/proxy.json" \
|
||||
"$(cache_ucode subscription-metadata-path proxy)" \
|
||||
"subscription metadata path mode"
|
||||
assert_eq "/var/run/podkop-plus/outbound-metadata/proxy.json" \
|
||||
assert_eq "/var/run/forkop/outbound-metadata/proxy.json" \
|
||||
"$(cache_ucode outbound-metadata-path proxy)" \
|
||||
"outbound metadata path mode"
|
||||
if cache_ucode subscription-metadata-path '../bad' >/dev/null 2>&1; then
|
||||
@@ -129,16 +129,16 @@ runtime_env() {
|
||||
TMP_SING_BOX_FOLDER="$WORK_DIR/runtime/tmp-sing-box" \
|
||||
TMP_RULESET_FOLDER="$WORK_DIR/runtime/tmp-sing-box/rulesets" \
|
||||
TMP_SUBSCRIPTION_FOLDER="$WORK_DIR/runtime/tmp-sing-box/subscriptions" \
|
||||
PODKOP_RUNTIME_STATE_DIR="$WORK_DIR/runtime/run" \
|
||||
PODKOP_SUBSCRIPTION_UPDATE_STATE_DIR="$WORK_DIR/runtime/run/subscription-update" \
|
||||
PODKOP_SUBSCRIPTION_LINKS_DIR="$WORK_DIR/runtime/run/subscription-links" \
|
||||
PODKOP_SUBSCRIPTION_METADATA_DIR="$WORK_DIR/runtime/run/subscription-metadata" \
|
||||
PODKOP_OUTBOUND_METADATA_DIR="$WORK_DIR/runtime/run/outbound-metadata" \
|
||||
PODKOP_SECTION_CACHE_DIR="$WORK_DIR/runtime/run/section-cache" \
|
||||
PODKOP_RUNTIME_CACHE_FORMAT_FILE="$WORK_DIR/runtime/run/cache-format" \
|
||||
PODKOP_PERSISTENT_SUBSCRIPTION_CACHE_DIR="$WORK_DIR/runtime/persistent" \
|
||||
PODKOP_PERSISTENT_SUBSCRIPTION_CACHE_FORMAT_FILE="$WORK_DIR/runtime/persistent/cache-format" \
|
||||
PODKOP_SUBSCRIPTION_BOOTSTRAP_RETRY_PID_FILE="$WORK_DIR/runtime/run/bootstrap.pid" \
|
||||
FORKOP_RUNTIME_STATE_DIR="$WORK_DIR/runtime/run" \
|
||||
FORKOP_SUBSCRIPTION_UPDATE_STATE_DIR="$WORK_DIR/runtime/run/subscription-update" \
|
||||
FORKOP_SUBSCRIPTION_LINKS_DIR="$WORK_DIR/runtime/run/subscription-links" \
|
||||
FORKOP_SUBSCRIPTION_METADATA_DIR="$WORK_DIR/runtime/run/subscription-metadata" \
|
||||
FORKOP_OUTBOUND_METADATA_DIR="$WORK_DIR/runtime/run/outbound-metadata" \
|
||||
FORKOP_SECTION_CACHE_DIR="$WORK_DIR/runtime/run/section-cache" \
|
||||
FORKOP_RUNTIME_CACHE_FORMAT_FILE="$WORK_DIR/runtime/run/cache-format" \
|
||||
FORKOP_PERSISTENT_SUBSCRIPTION_CACHE_DIR="$WORK_DIR/runtime/persistent" \
|
||||
FORKOP_PERSISTENT_SUBSCRIPTION_CACHE_FORMAT_FILE="$WORK_DIR/runtime/persistent/cache-format" \
|
||||
FORKOP_SUBSCRIPTION_BOOTSTRAP_RETRY_PID_FILE="$WORK_DIR/runtime/run/bootstrap.pid" \
|
||||
"$@"
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
PARSER="$ROOT_DIR/podkop/files/usr/lib/subscription/parser.uc"
|
||||
GENERATOR="$ROOT_DIR/podkop/files/usr/lib/singbox/generator.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
PARSER="$ROOT_DIR/forkop/files/usr/lib/subscription/parser.uc"
|
||||
GENERATOR="$ROOT_DIR/forkop/files/usr/lib/singbox/generator.uc"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -119,7 +119,7 @@ cat >"$generator_fixture" <<'JSON'
|
||||
}
|
||||
JSON
|
||||
TMP_SUBSCRIPTION_FOLDER="$WORK_DIR/subscriptions" \
|
||||
ucode -L "$PODKOP_LIB" "$GENERATOR" generate-config-fixture \
|
||||
ucode -L "$FORKOP_LIB" "$GENERATOR" generate-config-fixture \
|
||||
"$generator_fixture" "$generator_output" "127.0.0.1" "0"
|
||||
assert_contains "$generator_output" '"type": "hysteria2"' "generated stale HY2 type"
|
||||
if grep -Fq '"utls"' "$generator_output"; then
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
PARSER="$ROOT_DIR/podkop/files/usr/lib/subscription/parser.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
PARSER="$ROOT_DIR/forkop/files/usr/lib/subscription/parser.uc"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -69,6 +69,6 @@ if (invalid.valid || invalid.error != "Subscription URL must start with http://
|
||||
exit(1);
|
||||
UCODE
|
||||
|
||||
ucode -L "$PODKOP_LIB" "$WORK_DIR/require-subscription-parser.uc"
|
||||
ucode -L "$FORKOP_LIB" "$WORK_DIR/require-subscription-parser.uc"
|
||||
|
||||
printf 'subscription source entry checks passed\n'
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
UPDATES_UC="$ROOT_DIR/podkop/files/usr/lib/components/updates.uc"
|
||||
UPDATES_RUNTIME="$ROOT_DIR/podkop/files/usr/lib/updates_runtime.sh"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
UPDATES_UC="$ROOT_DIR/forkop/files/usr/lib/components/updates.uc"
|
||||
UPDATES_RUNTIME="$ROOT_DIR/forkop/files/usr/lib/updates_runtime.sh"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -34,7 +34,7 @@ write_state() {
|
||||
}
|
||||
|
||||
updates_ucode() {
|
||||
ucode -L "$PODKOP_LIB" "$UPDATES_UC" "$@"
|
||||
ucode -L "$FORKOP_LIB" "$UPDATES_UC" "$@"
|
||||
}
|
||||
|
||||
[ ! -e "$UPDATES_RUNTIME" ] ||
|
||||
@@ -79,7 +79,7 @@ if (value.running !== false || value.success !== false || value.section !== "pro
|
||||
NODE
|
||||
|
||||
mkdir -p "$WORK_DIR/bin"
|
||||
cat >"$WORK_DIR/bin/podkop-plus" <<'SH'
|
||||
cat >"$WORK_DIR/bin/forkop" <<'SH'
|
||||
#!/usr/bin/env sh
|
||||
if [ "$1" != "subscription_update" ]; then
|
||||
exit 64
|
||||
@@ -87,27 +87,27 @@ fi
|
||||
printf 'fake update for %s/%s\n' "$2" "$3"
|
||||
printf 'Subscription update completed by fake worker\n'
|
||||
SH
|
||||
chmod +x "$WORK_DIR/bin/podkop-plus"
|
||||
chmod +x "$WORK_DIR/bin/forkop"
|
||||
|
||||
ASYNC_ENV=(
|
||||
"PODKOP_LIB=$ROOT_DIR/podkop/files/usr/lib"
|
||||
"PODKOP_BIN=$WORK_DIR/bin/podkop-plus"
|
||||
"FORKOP_LIB=$ROOT_DIR/forkop/files/usr/lib"
|
||||
"FORKOP_BIN=$WORK_DIR/bin/forkop"
|
||||
"TMP_SING_BOX_FOLDER=$WORK_DIR/tmp/sing-box"
|
||||
"TMP_RULESET_FOLDER=$WORK_DIR/tmp/sing-box/rulesets"
|
||||
"TMP_SUBSCRIPTION_FOLDER=$WORK_DIR/tmp/sing-box/subscriptions"
|
||||
"PODKOP_RUNTIME_STATE_DIR=$WORK_DIR/run"
|
||||
"PODKOP_SUBSCRIPTION_UPDATE_STATE_DIR=$WORK_DIR/run/subscription-update"
|
||||
"PODKOP_SUBSCRIPTION_UPDATE_JOB_DIR=$WORK_DIR/run/subscription-update-jobs"
|
||||
"PODKOP_SUBSCRIPTION_LINKS_DIR=$WORK_DIR/run/subscription-links"
|
||||
"PODKOP_SUBSCRIPTION_METADATA_DIR=$WORK_DIR/run/subscription-metadata"
|
||||
"PODKOP_OUTBOUND_METADATA_DIR=$WORK_DIR/run/outbound-metadata"
|
||||
"PODKOP_SECTION_CACHE_DIR=$WORK_DIR/run/section-cache"
|
||||
"PODKOP_RUNTIME_CACHE_FORMAT_FILE=$WORK_DIR/run/cache-format"
|
||||
"PODKOP_PERSISTENT_SUBSCRIPTION_CACHE_DIR=$WORK_DIR/persistent/subscription-cache"
|
||||
"PODKOP_PERSISTENT_SUBSCRIPTION_CACHE_FORMAT_FILE=$WORK_DIR/persistent/subscription-cache/cache-format"
|
||||
"FORKOP_RUNTIME_STATE_DIR=$WORK_DIR/run"
|
||||
"FORKOP_SUBSCRIPTION_UPDATE_STATE_DIR=$WORK_DIR/run/subscription-update"
|
||||
"FORKOP_SUBSCRIPTION_UPDATE_JOB_DIR=$WORK_DIR/run/subscription-update-jobs"
|
||||
"FORKOP_SUBSCRIPTION_LINKS_DIR=$WORK_DIR/run/subscription-links"
|
||||
"FORKOP_SUBSCRIPTION_METADATA_DIR=$WORK_DIR/run/subscription-metadata"
|
||||
"FORKOP_OUTBOUND_METADATA_DIR=$WORK_DIR/run/outbound-metadata"
|
||||
"FORKOP_SECTION_CACHE_DIR=$WORK_DIR/run/section-cache"
|
||||
"FORKOP_RUNTIME_CACHE_FORMAT_FILE=$WORK_DIR/run/cache-format"
|
||||
"FORKOP_PERSISTENT_SUBSCRIPTION_CACHE_DIR=$WORK_DIR/persistent/subscription-cache"
|
||||
"FORKOP_PERSISTENT_SUBSCRIPTION_CACHE_FORMAT_FILE=$WORK_DIR/persistent/subscription-cache/cache-format"
|
||||
)
|
||||
|
||||
async_response="$(env "${ASYNC_ENV[@]}" ucode -L "$ROOT_DIR/podkop/files/usr/lib" "$UPDATES_UC" subscription-update-async proxy 1)"
|
||||
async_response="$(env "${ASYNC_ENV[@]}" ucode -L "$ROOT_DIR/forkop/files/usr/lib" "$UPDATES_UC" subscription-update-async proxy 1)"
|
||||
job_id="$(JSON_VALUE="$async_response" node - <<'NODE'
|
||||
const value = JSON.parse(process.env.JSON_VALUE);
|
||||
if (value.success !== true || !value.job_id) {
|
||||
@@ -120,7 +120,7 @@ NODE
|
||||
|
||||
final_status=""
|
||||
for _ in $(seq 1 30); do
|
||||
final_status="$(env "${ASYNC_ENV[@]}" ucode -L "$ROOT_DIR/podkop/files/usr/lib" "$UPDATES_UC" subscription-update-status "$job_id")"
|
||||
final_status="$(env "${ASYNC_ENV[@]}" ucode -L "$ROOT_DIR/forkop/files/usr/lib" "$UPDATES_UC" subscription-update-status "$job_id")"
|
||||
if JSON_VALUE="$final_status" node -e 'process.exit(JSON.parse(process.env.JSON_VALUE).running ? 1 : 0)' >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
UPDATES_UC="$ROOT_DIR/podkop/files/usr/lib/components/updates.uc"
|
||||
REAL_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
UPDATES_UC="$ROOT_DIR/forkop/files/usr/lib/components/updates.uc"
|
||||
REAL_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -44,11 +44,6 @@ function record(line) {
|
||||
|
||||
FAKE_LIB="$WORK_DIR/lib"
|
||||
|
||||
write_stub "$FAKE_LIB/config/migration.uc" "$stub_header"'
|
||||
record("config/migration:" + as_string(ARGV[0]));
|
||||
exit(ARGV[0] == "migrate" ? 0 : 64);
|
||||
'
|
||||
|
||||
write_stub "$FAKE_LIB/subscription/cache.uc" "$stub_header"'
|
||||
let mode = as_string(ARGV[0]);
|
||||
record("subscription/cache:" + mode);
|
||||
@@ -117,22 +112,22 @@ run_update() {
|
||||
|
||||
: >"$log"
|
||||
env \
|
||||
PODKOP_LIB="$FAKE_LIB" \
|
||||
PODKOP_RUNTIME_STATE_DIR="$WORK_DIR/run" \
|
||||
PODKOP_SUBSCRIPTION_UPDATE_LOCK_DIR="$WORK_DIR/run/subscription-update.lock" \
|
||||
PODKOP_RELOAD_LOCK_DIR="$WORK_DIR/run/reload.lock" \
|
||||
PODKOP_SUBSCRIPTION_UPDATE_STATE_DIR="$WORK_DIR/run/subscription-update" \
|
||||
PODKOP_SUBSCRIPTION_UPDATE_JOB_DIR="$WORK_DIR/run/subscription-update-jobs" \
|
||||
PODKOP_SUBSCRIPTION_LINKS_DIR="$WORK_DIR/run/subscription-links" \
|
||||
PODKOP_SUBSCRIPTION_METADATA_DIR="$WORK_DIR/run/subscription-metadata" \
|
||||
PODKOP_OUTBOUND_METADATA_DIR="$WORK_DIR/run/outbound-metadata" \
|
||||
PODKOP_SECTION_CACHE_DIR="$WORK_DIR/run/section-cache" \
|
||||
PODKOP_RUNTIME_CACHE_FORMAT_FILE="$WORK_DIR/run/cache-format" \
|
||||
PODKOP_PERSISTENT_SUBSCRIPTION_CACHE_DIR="$WORK_DIR/persistent/subscription-cache" \
|
||||
PODKOP_PERSISTENT_SUBSCRIPTION_CACHE_FORMAT_FILE="$WORK_DIR/persistent/subscription-cache/cache-format" \
|
||||
PODKOP_PENDING_RELOAD_FILE="$WORK_DIR/run/reload.pending" \
|
||||
PODKOP_RELOAD_STATE_FILE="$WORK_DIR/run/reload-state" \
|
||||
PODKOP_RULE_CONDITION_CACHE_DIR="$WORK_DIR/run/rule-condition-cache" \
|
||||
FORKOP_LIB="$FAKE_LIB" \
|
||||
FORKOP_RUNTIME_STATE_DIR="$WORK_DIR/run" \
|
||||
FORKOP_SUBSCRIPTION_UPDATE_LOCK_DIR="$WORK_DIR/run/subscription-update.lock" \
|
||||
FORKOP_RELOAD_LOCK_DIR="$WORK_DIR/run/reload.lock" \
|
||||
FORKOP_SUBSCRIPTION_UPDATE_STATE_DIR="$WORK_DIR/run/subscription-update" \
|
||||
FORKOP_SUBSCRIPTION_UPDATE_JOB_DIR="$WORK_DIR/run/subscription-update-jobs" \
|
||||
FORKOP_SUBSCRIPTION_LINKS_DIR="$WORK_DIR/run/subscription-links" \
|
||||
FORKOP_SUBSCRIPTION_METADATA_DIR="$WORK_DIR/run/subscription-metadata" \
|
||||
FORKOP_OUTBOUND_METADATA_DIR="$WORK_DIR/run/outbound-metadata" \
|
||||
FORKOP_SECTION_CACHE_DIR="$WORK_DIR/run/section-cache" \
|
||||
FORKOP_RUNTIME_CACHE_FORMAT_FILE="$WORK_DIR/run/cache-format" \
|
||||
FORKOP_PERSISTENT_SUBSCRIPTION_CACHE_DIR="$WORK_DIR/persistent/subscription-cache" \
|
||||
FORKOP_PERSISTENT_SUBSCRIPTION_CACHE_FORMAT_FILE="$WORK_DIR/persistent/subscription-cache/cache-format" \
|
||||
FORKOP_PENDING_RELOAD_FILE="$WORK_DIR/run/reload.pending" \
|
||||
FORKOP_RELOAD_STATE_FILE="$WORK_DIR/run/reload-state" \
|
||||
FORKOP_RULE_CONDITION_CACHE_DIR="$WORK_DIR/run/rule-condition-cache" \
|
||||
FAKE_CALL_LOG="$log" \
|
||||
FAKE_SUBSCRIPTION_UPDATE_SUMMARY="$summary" \
|
||||
ucode -L "$REAL_LIB" "$UPDATES_UC" subscription-update-if-due
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CACHE_UC="$ROOT_DIR/podkop/files/usr/lib/subscription/cache.uc"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
CACHE_UC="$ROOT_DIR/forkop/files/usr/lib/subscription/cache.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -42,7 +42,7 @@ preferred_candidates="$(printf '%s\n%s\n%s\n%s\n%s\n%s' \
|
||||
Clash.Meta)"
|
||||
|
||||
cache_ucode() {
|
||||
ucode -L "$PODKOP_LIB" "$CACHE_UC" "$@"
|
||||
ucode -L "$FORKOP_LIB" "$CACHE_UC" "$@"
|
||||
}
|
||||
|
||||
cache_ucode write-user-agent-candidates "$WORK_DIR/default.txt" "" "" "$default_ua"
|
||||
|
||||
+30
-30
@@ -2,11 +2,11 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
UI_UC="$ROOT_DIR/podkop/files/usr/lib/service/ui.uc"
|
||||
PODKOP_FILES="$ROOT_DIR/podkop/files"
|
||||
PODKOP_BIN="$PODKOP_FILES/usr/bin/podkop"
|
||||
PODKOP_INIT="$PODKOP_FILES/etc/init.d/podkop"
|
||||
UI_RUNTIME_SH="$PODKOP_FILES/usr/lib/ui_runtime.sh"
|
||||
UI_UC="$ROOT_DIR/forkop/files/usr/lib/service/ui.uc"
|
||||
FORKOP_FILES="$ROOT_DIR/forkop/files"
|
||||
FORKOP_BIN="$FORKOP_FILES/usr/bin/forkop"
|
||||
FORKOP_INIT="$FORKOP_FILES/etc/init.d/forkop"
|
||||
UI_RUNTIME_SH="$FORKOP_FILES/usr/lib/ui_runtime.sh"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -20,7 +20,7 @@ fail() {
|
||||
}
|
||||
|
||||
ui_ucode() {
|
||||
ucode -L "$PODKOP_FILES/usr/lib" "$UI_UC" "$@"
|
||||
ucode -L "$FORKOP_FILES/usr/lib" "$UI_UC" "$@"
|
||||
}
|
||||
|
||||
assert_eq() {
|
||||
@@ -42,16 +42,16 @@ write_state() {
|
||||
[ ! -e "$UI_RUNTIME_SH" ] ||
|
||||
fail "ui_runtime.sh shell owner must be removed"
|
||||
|
||||
if grep -R -n "ui_runtime.sh" "$PODKOP_FILES" >/dev/null 2>&1; then
|
||||
if grep -R -n "ui_runtime.sh" "$FORKOP_FILES" >/dev/null 2>&1; then
|
||||
fail "runtime files must not reference ui_runtime.sh"
|
||||
fi
|
||||
|
||||
ui_runtime_shell_symbols='ui_runtime_|podkop_fast_get_ui_state|PODKOP_UI_RUNTIME|load_ui_runtime'
|
||||
if grep -R -n -E "$ui_runtime_shell_symbols" "$PODKOP_BIN" "$PODKOP_INIT" "$PODKOP_FILES/usr/lib" --include='*.sh' >/dev/null 2>&1; then
|
||||
ui_runtime_shell_symbols='ui_runtime_|forkop_fast_get_ui_state|FORKOP_UI_RUNTIME|load_ui_runtime'
|
||||
if grep -R -n -E "$ui_runtime_shell_symbols" "$FORKOP_BIN" "$FORKOP_INIT" "$FORKOP_FILES/usr/lib" --include='*.sh' >/dev/null 2>&1; then
|
||||
fail "ui_runtime shell symbols must not remain in runtime shell"
|
||||
fi
|
||||
|
||||
if grep -R -n '^get_ui_capabilities()' "$PODKOP_FILES/usr/lib" --include='*.sh' >/dev/null 2>&1; then
|
||||
if grep -R -n '^get_ui_capabilities()' "$FORKOP_FILES/usr/lib" --include='*.sh' >/dev/null 2>&1; then
|
||||
fail "UI capabilities must be owned by service/ui.uc, not shell"
|
||||
fi
|
||||
grep -Fq 'require("core.uci")' "$UI_UC" ||
|
||||
@@ -59,8 +59,8 @@ grep -Fq 'require("core.uci")' "$UI_UC" ||
|
||||
if grep -n -E 'require\("uci"\)\.cursor|uci -q|uci", "-q"' "$UI_UC" >/dev/null 2>&1; then
|
||||
fail "service/ui.uc must not own direct UCI cursor or CLI reads"
|
||||
fi
|
||||
grep -Fq '"podkop-stably-running"' "$UI_UC" ||
|
||||
fail "UI Podkop status must use stable runtime state to avoid crash-loop flicker"
|
||||
grep -Fq '"forkop-stably-running"' "$UI_UC" ||
|
||||
fail "UI Forkop status must use stable runtime state to avoid crash-loop flicker"
|
||||
grep -Fq '"sing-box-service-stable"' "$UI_UC" ||
|
||||
fail "UI sing-box status must use stable runtime state to avoid crash-loop flicker"
|
||||
grep -Fq 'run_pending_reload_after_service_action(action, success)' "$UI_UC" ||
|
||||
@@ -127,7 +127,7 @@ latency_action_dir="$WORK_DIR/latency-actions"
|
||||
mkdir -p "$latency_action_dir"
|
||||
latency_state="$latency_action_dir/latency-1.json"
|
||||
printf '%s\n' '{"success":true,"running":true,"kind":"latency","latency_type":"proxy_list","section":"main","tag":"[]","started_at":100}' >"$latency_state"
|
||||
PODKOP_UI_LATENCY_ACTION_DIR="$latency_action_dir" \
|
||||
FORKOP_UI_LATENCY_ACTION_DIR="$latency_action_dir" \
|
||||
ui_ucode latency-progress-state "$latency_state" 2 5 1 >/dev/null ||
|
||||
fail "latency-progress-state should update running latency jobs"
|
||||
JOB_STATE="$latency_state" node - <<'NODE'
|
||||
@@ -207,21 +207,21 @@ ui_ucode cleanup-action-dir-fixture "$cleanup_dir"
|
||||
[ ! -e "$cleanup_dir/done.out.json" ] ||
|
||||
fail "expired acknowledged action JSON sidecar should be cleaned"
|
||||
|
||||
export PODKOP_UI_STATE_DIR="$WORK_DIR/ui-state"
|
||||
export PODKOP_UI_SERVICE_ACTION_DIR="$PODKOP_UI_STATE_DIR/service-actions"
|
||||
export PODKOP_UI_SERVICE_ACTION_LOCK_DIR="$PODKOP_UI_STATE_DIR/service-actions.lock"
|
||||
export PODKOP_UI_LATENCY_ACTION_DIR="$PODKOP_UI_STATE_DIR/latency-actions"
|
||||
export PODKOP_UI_COMPONENT_ACTION_DIR="$PODKOP_UI_STATE_DIR/component-actions"
|
||||
export PODKOP_UI_SUBSCRIPTION_ACTION_DIR="$PODKOP_UI_STATE_DIR/subscription-actions"
|
||||
export FORKOP_UI_STATE_DIR="$WORK_DIR/ui-state"
|
||||
export FORKOP_UI_SERVICE_ACTION_DIR="$FORKOP_UI_STATE_DIR/service-actions"
|
||||
export FORKOP_UI_SERVICE_ACTION_LOCK_DIR="$FORKOP_UI_STATE_DIR/service-actions.lock"
|
||||
export FORKOP_UI_LATENCY_ACTION_DIR="$FORKOP_UI_STATE_DIR/latency-actions"
|
||||
export FORKOP_UI_COMPONENT_ACTION_DIR="$FORKOP_UI_STATE_DIR/component-actions"
|
||||
export FORKOP_UI_SUBSCRIPTION_ACTION_DIR="$FORKOP_UI_STATE_DIR/subscription-actions"
|
||||
|
||||
latency_start="$(
|
||||
PODKOP_BIN=/bin/true \
|
||||
PODKOP_LIB="$PODKOP_FILES/usr/lib" \
|
||||
FORKOP_BIN=/bin/true \
|
||||
FORKOP_LIB="$FORKOP_FILES/usr/lib" \
|
||||
ui_ucode latency-test-async proxy_list main '["proxy-a","proxy-b"]' 5000
|
||||
)"
|
||||
latency_job_id="$(printf '%s' "$latency_start" | sed -n 's/.*"job_id"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')"
|
||||
[ -n "$latency_job_id" ] || fail "latency-test-async should return a job id"
|
||||
latency_async_state="$PODKOP_UI_LATENCY_ACTION_DIR/$latency_job_id.json"
|
||||
latency_async_state="$FORKOP_UI_LATENCY_ACTION_DIR/$latency_job_id.json"
|
||||
[ -f "$latency_async_state" ] || fail "latency-test-async should create a state file"
|
||||
JOB_STATE="$latency_async_state" node - <<'NODE'
|
||||
const fs = require("fs");
|
||||
@@ -234,7 +234,7 @@ NODE
|
||||
|
||||
job_id="$(ui_ucode service-action-begin-if-idle reload test)"
|
||||
[ -n "$job_id" ] || fail "service-action-begin-if-idle should create a job"
|
||||
service_state="$PODKOP_UI_SERVICE_ACTION_DIR/$job_id.json"
|
||||
service_state="$FORKOP_UI_SERVICE_ACTION_DIR/$job_id.json"
|
||||
[ -f "$service_state" ] || fail "service action state file should be created"
|
||||
assert_eq "reload" \
|
||||
"$(ui_ucode active-service-action)" \
|
||||
@@ -247,11 +247,11 @@ fi
|
||||
ui_ucode service-action-update-pid "$job_id" "$$" >/dev/null ||
|
||||
fail "service-action-update-pid should update running job"
|
||||
|
||||
PODKOP_UI_SERVICE_ACTION_TIMEOUT_SECONDS=1 \
|
||||
PODKOP_UI_SERVICE_ACTION_SETTLE_SECONDS=0 \
|
||||
PODKOP_SERVICE_INIT=/bin/true \
|
||||
PODKOP_BIN=/bin/true \
|
||||
PODKOP_LIB="$PODKOP_FILES/usr/lib" \
|
||||
FORKOP_UI_SERVICE_ACTION_TIMEOUT_SECONDS=1 \
|
||||
FORKOP_UI_SERVICE_ACTION_SETTLE_SECONDS=0 \
|
||||
FORKOP_SERVICE_INIT=/bin/true \
|
||||
FORKOP_BIN=/bin/true \
|
||||
FORKOP_LIB="$FORKOP_FILES/usr/lib" \
|
||||
ui_ucode service-action-finish-after-command reload "$job_id" 0 >/dev/null ||
|
||||
fail "service-action-finish-after-command should spawn waiter without ucode declaration-order failure"
|
||||
|
||||
@@ -272,8 +272,8 @@ if (value.running !== false || value.success !== true || value.message !== "done
|
||||
}
|
||||
NODE
|
||||
|
||||
component_job="$PODKOP_UI_COMPONENT_ACTION_DIR/component-1.json"
|
||||
mkdir -p "$PODKOP_UI_COMPONENT_ACTION_DIR"
|
||||
component_job="$FORKOP_UI_COMPONENT_ACTION_DIR/component-1.json"
|
||||
mkdir -p "$FORKOP_UI_COMPONENT_ACTION_DIR"
|
||||
printf '%s\n' '{"running":true,"component":"sing_box","started_at":100}' >"$component_job"
|
||||
ui_ucode component-action-running-for sing_box >/dev/null ||
|
||||
fail "component-action-running-for should detect running sing-box component action"
|
||||
|
||||
+25
-25
@@ -2,8 +2,8 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
UPDATES_UC="$ROOT_DIR/podkop/files/usr/lib/components/updates.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
UPDATES_UC="$ROOT_DIR/forkop/files/usr/lib/components/updates.uc"
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
@@ -22,7 +22,7 @@ assert_status() {
|
||||
}
|
||||
|
||||
updates_ucode() {
|
||||
ucode -L "$PODKOP_LIB" "$UPDATES_UC" "$@"
|
||||
ucode -L "$FORKOP_LIB" "$UPDATES_UC" "$@"
|
||||
}
|
||||
|
||||
if grep -n -E 'require\("uci"\)\.cursor|uci -q' "$UPDATES_UC" >/dev/null 2>&1; then
|
||||
@@ -83,11 +83,11 @@ assert_eq "0 0 * * *" \
|
||||
"$(updates_ucode due-check-cron-schedule 86400)" \
|
||||
"daily cron schedule"
|
||||
|
||||
assert_eq "*/30 * * * * /usr/bin/podkop list_update_if_due # list" \
|
||||
"$(updates_ucode list-update-cron-job 30m /usr/bin/podkop '# list')" \
|
||||
assert_eq "*/30 * * * * /usr/bin/forkop list_update_if_due # list" \
|
||||
"$(updates_ucode list-update-cron-job 30m /usr/bin/forkop '# list')" \
|
||||
"list update cron job"
|
||||
assert_eq "0 */2 * * * /usr/bin/podkop subscription_update_if_due # subscription" \
|
||||
"$(updates_ucode subscription-update-cron-job 7200 /usr/bin/podkop '# subscription')" \
|
||||
assert_eq "0 */2 * * * /usr/bin/forkop subscription_update_if_due # subscription" \
|
||||
"$(updates_ucode subscription-update-cron-job 7200 /usr/bin/forkop '# subscription')" \
|
||||
"subscription update cron job"
|
||||
assert_eq $'error\trule3\tinvalid\nmin\t1800' \
|
||||
"$(printf 'rule1\t2h\nrule2\t30m\nrule3\tinvalid\nrule4\t\n' | updates_ucode subscription-update-interval-plan)" \
|
||||
@@ -95,10 +95,10 @@ assert_eq $'error\trule3\tinvalid\nmin\t1800' \
|
||||
assert_eq $'min\t0' \
|
||||
"$(printf 'rule1\t\n' | updates_ucode subscription-update-interval-plan)" \
|
||||
"empty subscription interval plan"
|
||||
if updates_ucode list-update-cron-job invalid /usr/bin/podkop '# list' >/dev/null 2>&1; then
|
||||
if updates_ucode list-update-cron-job invalid /usr/bin/forkop '# list' >/dev/null 2>&1; then
|
||||
fail "invalid list update interval should fail"
|
||||
fi
|
||||
if updates_ucode subscription-update-cron-job 0 /usr/bin/podkop '# subscription' >/dev/null 2>&1; then
|
||||
if updates_ucode subscription-update-cron-job 0 /usr/bin/forkop '# subscription' >/dev/null 2>&1; then
|
||||
fail "invalid subscription update interval should fail"
|
||||
fi
|
||||
|
||||
@@ -150,8 +150,8 @@ cat >"$WORK_DIR/cron-plan.json" <<'JSON'
|
||||
}
|
||||
JSON
|
||||
|
||||
assert_eq $'list\t*/30 * * * * /usr/bin/podkop list_update_if_due # list\nsubscription-error\tbad_sub\tbad\nsubscription\t*/45 * * * * /usr/bin/podkop subscription_update_if_due # subscription\ncomponent\t0 */2 * * * /usr/bin/podkop component_updates_if_due # component' \
|
||||
"$(updates_ucode cron-refresh-plan-fixture "$WORK_DIR/cron-plan.json" /usr/bin/podkop '# list' '# subscription' '# component')" \
|
||||
assert_eq $'list\t*/30 * * * * /usr/bin/forkop list_update_if_due # list\nsubscription-error\tbad_sub\tbad\nsubscription\t*/45 * * * * /usr/bin/forkop subscription_update_if_due # subscription\ncomponent\t0 */2 * * * /usr/bin/forkop component_updates_if_due # component' \
|
||||
"$(updates_ucode cron-refresh-plan-fixture "$WORK_DIR/cron-plan.json" /usr/bin/forkop '# list' '# subscription' '# component')" \
|
||||
"cron refresh plan"
|
||||
|
||||
grep -Fq 'fs.writefile(tmp, as_string(text)) == null' "$UPDATES_UC" ||
|
||||
@@ -175,7 +175,7 @@ cat >"$WORK_DIR/cron-plan-list-disabled.json" <<'JSON'
|
||||
JSON
|
||||
|
||||
assert_eq "list-disabled" \
|
||||
"$(updates_ucode cron-refresh-plan-fixture "$WORK_DIR/cron-plan-list-disabled.json" /usr/bin/podkop '# list' '# subscription' '# component')" \
|
||||
"$(updates_ucode cron-refresh-plan-fixture "$WORK_DIR/cron-plan-list-disabled.json" /usr/bin/forkop '# list' '# subscription' '# component')" \
|
||||
"disabled list cron plan"
|
||||
|
||||
cat >"$WORK_DIR/cron-plan-invalid-list.json" <<'JSON'
|
||||
@@ -196,26 +196,26 @@ cat >"$WORK_DIR/cron-plan-invalid-list.json" <<'JSON'
|
||||
JSON
|
||||
|
||||
status=0
|
||||
invalid_plan="$(updates_ucode cron-refresh-plan-fixture "$WORK_DIR/cron-plan-invalid-list.json" /usr/bin/podkop '# list' '# subscription' '# component')" || status="$?"
|
||||
invalid_plan="$(updates_ucode cron-refresh-plan-fixture "$WORK_DIR/cron-plan-invalid-list.json" /usr/bin/forkop '# list' '# subscription' '# component')" || status="$?"
|
||||
assert_eq 1 "$status" "invalid list cron plan status"
|
||||
assert_eq $'list-error\tbad' "$invalid_plan" "invalid list cron plan output"
|
||||
|
||||
cat >"$WORK_DIR/existing.cron" <<'CRON'
|
||||
0 1 * * * /bin/true # keep
|
||||
* * * * * /usr/bin/podkop list_update_if_due # list
|
||||
* * * * * /usr/bin/podkop subscription_update_if_due # subscription
|
||||
* * * * * /usr/bin/podkop component_updates_if_due # component
|
||||
* * * * * /usr/bin/forkop list_update_if_due # list
|
||||
* * * * * /usr/bin/forkop subscription_update_if_due # subscription
|
||||
* * * * * /usr/bin/forkop component_updates_if_due # component
|
||||
CRON
|
||||
|
||||
updates_ucode refresh-cron-fixture "$WORK_DIR/cron-plan.json" "$WORK_DIR/existing.cron" /usr/bin/podkop '# list' '# subscription' '# component' >"$WORK_DIR/cron-apply.json"
|
||||
updates_ucode refresh-cron-fixture "$WORK_DIR/cron-plan.json" "$WORK_DIR/existing.cron" /usr/bin/forkop '# list' '# subscription' '# component' >"$WORK_DIR/cron-apply.json"
|
||||
node - "$WORK_DIR/cron-apply.json" <<'JS'
|
||||
const fs = require("fs");
|
||||
const value = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
|
||||
const expected = [
|
||||
"0 1 * * * /bin/true # keep",
|
||||
"*/30 * * * * /usr/bin/podkop list_update_if_due # list",
|
||||
"*/45 * * * * /usr/bin/podkop subscription_update_if_due # subscription",
|
||||
"0 */2 * * * /usr/bin/podkop component_updates_if_due # component",
|
||||
"*/30 * * * * /usr/bin/forkop list_update_if_due # list",
|
||||
"*/45 * * * * /usr/bin/forkop subscription_update_if_due # subscription",
|
||||
"0 */2 * * * /usr/bin/forkop component_updates_if_due # component",
|
||||
""
|
||||
].join("\n");
|
||||
if (value.crontab !== expected) {
|
||||
@@ -224,22 +224,22 @@ if (value.crontab !== expected) {
|
||||
}
|
||||
const messages = value.logs.map(item => item.message);
|
||||
if (!messages.includes("The cron job removed") ||
|
||||
!messages.includes("The cron job has been created: */30 * * * * /usr/bin/podkop list_update_if_due # list") ||
|
||||
!messages.includes("The subscription cron job has been created: */45 * * * * /usr/bin/podkop subscription_update_if_due # subscription") ||
|
||||
!messages.includes("The component update check cron job has been created: 0 */2 * * * /usr/bin/podkop component_updates_if_due # component")) {
|
||||
!messages.includes("The cron job has been created: */30 * * * * /usr/bin/forkop list_update_if_due # list") ||
|
||||
!messages.includes("The subscription cron job has been created: */45 * * * * /usr/bin/forkop subscription_update_if_due # subscription") ||
|
||||
!messages.includes("The component update check cron job has been created: 0 */2 * * * /usr/bin/forkop component_updates_if_due # component")) {
|
||||
console.error("unexpected cron apply logs", JSON.stringify(value.logs));
|
||||
process.exit(1);
|
||||
}
|
||||
JS
|
||||
|
||||
status=0
|
||||
updates_ucode refresh-cron-fixture "$WORK_DIR/cron-plan-invalid-list.json" "$WORK_DIR/existing.cron" /usr/bin/podkop '# list' '# subscription' '# component' >"$WORK_DIR/cron-apply-invalid.json" || status="$?"
|
||||
updates_ucode refresh-cron-fixture "$WORK_DIR/cron-plan-invalid-list.json" "$WORK_DIR/existing.cron" /usr/bin/forkop '# list' '# subscription' '# component' >"$WORK_DIR/cron-apply-invalid.json" || status="$?"
|
||||
assert_eq 1 "$status" "invalid cron apply status"
|
||||
node - "$WORK_DIR/cron-apply-invalid.json" <<'JS'
|
||||
const fs = require("fs");
|
||||
const value = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
|
||||
if (value.crontab !== "0 1 * * * /bin/true # keep\n") {
|
||||
console.error("invalid plan should remove old podkop cron jobs only", JSON.stringify(value.crontab));
|
||||
console.error("invalid plan should remove old forkop cron jobs only", JSON.stringify(value.crontab));
|
||||
process.exit(1);
|
||||
}
|
||||
if (!value.logs.some(item => item.level === "error" && item.message === "Invalid update_interval value: bad")) {
|
||||
|
||||
+10
-10
@@ -2,10 +2,10 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
PARSER_UC="$PODKOP_LIB/subscription/parser.uc"
|
||||
GENERATOR_UC="$PODKOP_LIB/singbox/generator.uc"
|
||||
CACHE_UC="$PODKOP_LIB/subscription/cache.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
PARSER_UC="$FORKOP_LIB/subscription/parser.uc"
|
||||
GENERATOR_UC="$FORKOP_LIB/singbox/generator.uc"
|
||||
CACHE_UC="$FORKOP_LIB/subscription/cache.uc"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -21,7 +21,7 @@ fail() {
|
||||
normalize_subscription() {
|
||||
local input="$1"
|
||||
local output="$2"
|
||||
ucode -L "$PODKOP_LIB" "$PARSER_UC" normalize-content "$input" "$output"
|
||||
ucode -L "$FORKOP_LIB" "$PARSER_UC" normalize-content "$input" "$output"
|
||||
}
|
||||
|
||||
prepare_subscription_cache() {
|
||||
@@ -42,7 +42,7 @@ generate_config() {
|
||||
local output="$2"
|
||||
mkdir -p "${output}.section-cache"
|
||||
TMP_SUBSCRIPTION_FOLDER="$WORK_DIR/subscriptions" \
|
||||
ucode -L "$PODKOP_LIB" "$GENERATOR_UC" generate-config-fixture \
|
||||
ucode -L "$FORKOP_LIB" "$GENERATOR_UC" generate-config-fixture \
|
||||
"$fixture" "$output" "127.0.0.1"
|
||||
}
|
||||
|
||||
@@ -286,7 +286,7 @@ if (!reveal_selector || length(reveal_selector.outbounds || []) != 4)
|
||||
' "$xray_config" "$xray_config.section-cache/proxy.json" "$xray_reveal_urltest_config" "$xray_group_name_filter_config" || fail "xray generated URLTest behavior"
|
||||
|
||||
xray_metadata="$WORK_DIR/xray-ui-outbound-metadata.json"
|
||||
ucode -L "$PODKOP_LIB" "$CACHE_UC" get-outbound-metadata "$xray_config.section-cache" proxy "$WORK_DIR/missing-outbound-metadata.json" >"$xray_metadata"
|
||||
ucode -L "$FORKOP_LIB" "$CACHE_UC" get-outbound-metadata "$xray_config.section-cache" proxy "$WORK_DIR/missing-outbound-metadata.json" >"$xray_metadata"
|
||||
|
||||
ucode -e '
|
||||
let fs = require("fs");
|
||||
@@ -356,8 +356,8 @@ let value = json(fs.readfile(ARGV[0]));
|
||||
let flags = {};
|
||||
for (let outbound in value.outbounds || [])
|
||||
flags[outbound.tag] = {
|
||||
allow: outbound.__podkop_allow_group === true,
|
||||
hidden: outbound.__podkop_hidden === true
|
||||
allow: outbound.__forkop_allow_group === true,
|
||||
hidden: outbound.__forkop_hidden === true
|
||||
};
|
||||
if (!flags["Native Group"].allow)
|
||||
die("native sing-box URLTest group was not allowed\n");
|
||||
@@ -591,7 +591,7 @@ if (!contains(candidates, "Native A") || !contains(candidates, "Detour Only"))
|
||||
' "$singbox_config" "$singbox_config.section-cache/proxy.json" "$singbox_reveal_urltest_config" "$singbox_reveal_detour_config" || fail "native sing-box generated URLTest behavior"
|
||||
|
||||
singbox_metadata="$WORK_DIR/singbox-ui-outbound-metadata.json"
|
||||
ucode -L "$PODKOP_LIB" "$CACHE_UC" get-outbound-metadata "$singbox_config.section-cache" proxy "$WORK_DIR/missing-outbound-metadata.json" >"$singbox_metadata"
|
||||
ucode -L "$FORKOP_LIB" "$CACHE_UC" get-outbound-metadata "$singbox_config.section-cache" proxy "$WORK_DIR/missing-outbound-metadata.json" >"$singbox_metadata"
|
||||
|
||||
ucode -e '
|
||||
let fs = require("fs");
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -69,7 +69,7 @@ JSON
|
||||
|
||||
output="$WORK_DIR/config.json"
|
||||
mkdir -p "$output.section-cache"
|
||||
ucode -L "$PODKOP_LIB" "$PODKOP_LIB/singbox/generator.uc" generate-config-fixture \
|
||||
ucode -L "$FORKOP_LIB" "$FORKOP_LIB/singbox/generator.uc" generate-config-fixture \
|
||||
"$WORK_DIR/fixture.json" "$output" "127.0.0.1"
|
||||
|
||||
urltest_count="$(ucode -e '
|
||||
@@ -148,7 +148,7 @@ JSON
|
||||
|
||||
multi_output="$WORK_DIR/multi-config.json"
|
||||
mkdir -p "$multi_output.section-cache"
|
||||
ucode -L "$PODKOP_LIB" "$PODKOP_LIB/singbox/generator.uc" generate-config-fixture \
|
||||
ucode -L "$FORKOP_LIB" "$FORKOP_LIB/singbox/generator.uc" generate-config-fixture \
|
||||
"$WORK_DIR/multi-fixture.json" "$multi_output" "127.0.0.1"
|
||||
|
||||
ucode -e '
|
||||
@@ -222,7 +222,7 @@ JSON
|
||||
|
||||
hide_added_output="$WORK_DIR/hide-added-config.json"
|
||||
mkdir -p "$hide_added_output.section-cache"
|
||||
ucode -L "$PODKOP_LIB" "$PODKOP_LIB/singbox/generator.uc" generate-config-fixture \
|
||||
ucode -L "$FORKOP_LIB" "$FORKOP_LIB/singbox/generator.uc" generate-config-fixture \
|
||||
"$WORK_DIR/hide-added-fixture.json" "$hide_added_output" "127.0.0.1"
|
||||
|
||||
ucode -e '
|
||||
|
||||
@@ -2,32 +2,32 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_BIN="$ROOT_DIR/podkop/files/usr/bin/podkop"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
CLI_UC="$PODKOP_BIN"
|
||||
LIFECYCLE_UC="$PODKOP_LIB/service/lifecycle.uc"
|
||||
ZAPRET_RUNTIME="$PODKOP_LIB/providers/zapret/runtime.uc"
|
||||
ZAPRET2_RUNTIME="$PODKOP_LIB/providers/zapret2/runtime.uc"
|
||||
ZAPRET_COMMON="$PODKOP_LIB/providers/zapret/common.uc"
|
||||
ZAPRET2_COMMON="$PODKOP_LIB/providers/zapret2/common.uc"
|
||||
NFQUEUE_RUNTIME="$PODKOP_LIB/providers/nfqueue/runtime.uc"
|
||||
NFQUEUE_CHECK="$PODKOP_LIB/providers/nfqueue/check.uc"
|
||||
NFQUEUE_VALIDATOR="$PODKOP_LIB/providers/nfqueue/validator.uc"
|
||||
ZAPRET2_CHECK="$PODKOP_LIB/providers/zapret2/check.uc"
|
||||
ZAPRET2_VALIDATOR="$PODKOP_LIB/providers/zapret2/validator.uc"
|
||||
FORKOP_BIN="$ROOT_DIR/forkop/files/usr/bin/forkop"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
CLI_UC="$FORKOP_BIN"
|
||||
LIFECYCLE_UC="$FORKOP_LIB/service/lifecycle.uc"
|
||||
ZAPRET_RUNTIME="$FORKOP_LIB/providers/zapret/runtime.uc"
|
||||
ZAPRET2_RUNTIME="$FORKOP_LIB/providers/zapret2/runtime.uc"
|
||||
ZAPRET_COMMON="$FORKOP_LIB/providers/zapret/common.uc"
|
||||
ZAPRET2_COMMON="$FORKOP_LIB/providers/zapret2/common.uc"
|
||||
NFQUEUE_RUNTIME="$FORKOP_LIB/providers/nfqueue/runtime.uc"
|
||||
NFQUEUE_CHECK="$FORKOP_LIB/providers/nfqueue/check.uc"
|
||||
NFQUEUE_VALIDATOR="$FORKOP_LIB/providers/nfqueue/validator.uc"
|
||||
ZAPRET2_CHECK="$FORKOP_LIB/providers/zapret2/check.uc"
|
||||
ZAPRET2_VALIDATOR="$FORKOP_LIB/providers/zapret2/validator.uc"
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ ! -e "$PODKOP_LIB/zapret.sh" ] ||
|
||||
[ ! -e "$FORKOP_LIB/zapret.sh" ] ||
|
||||
fail "zapret.sh shell owner must be removed"
|
||||
[ ! -e "$PODKOP_LIB/zapret2.sh" ] ||
|
||||
[ ! -e "$FORKOP_LIB/zapret2.sh" ] ||
|
||||
fail "zapret2.sh shell owner must be removed"
|
||||
|
||||
grep -Fq '#!/usr/bin/ucode' "$PODKOP_BIN" ||
|
||||
fail "podkop entrypoint must be a direct ucode executable"
|
||||
grep -Fq '#!/usr/bin/ucode' "$FORKOP_BIN" ||
|
||||
fail "forkop entrypoint must be a direct ucode executable"
|
||||
grep -Fq 'service/lifecycle.uc' "$CLI_UC" ||
|
||||
fail "service/cli.uc must dispatch lifecycle orchestration through service/lifecycle.uc"
|
||||
grep -Fq 'providers/zapret/runtime.uc' "$LIFECYCLE_UC" ||
|
||||
@@ -64,21 +64,21 @@ if grep -n -E 'function (start_runtime|stop_runtime|status_json|create_nft_rules
|
||||
fail "provider common modules must stay thin config wrappers, not runtime engines"
|
||||
fi
|
||||
if grep -R -n -E 'providers/zapret/runtime\.uc" (start-runtime|stop-runtime|status|check|installed|package-installed|package-version|enabled-rule-count) zapret2' \
|
||||
"$PODKOP_BIN" "$PODKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
|
||||
"$FORKOP_BIN" "$FORKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
|
||||
fail "zapret2 must not be multiplexed through providers/zapret/runtime.uc"
|
||||
fi
|
||||
if grep -R -n -E 'providers\.zapret\.|providers/zapret/' "$PODKOP_LIB/providers/zapret2" >/dev/null 2>&1; then
|
||||
if grep -R -n -E 'providers\.zapret\.|providers/zapret/' "$FORKOP_LIB/providers/zapret2" >/dev/null 2>&1; then
|
||||
fail "zapret2 provider modules must not import or execute providers/zapret modules"
|
||||
fi
|
||||
if grep -n 'zapret2' "$ZAPRET_COMMON" >/dev/null 2>&1; then
|
||||
fail "providers/zapret/common.uc must not contain zapret2 ownership"
|
||||
fi
|
||||
if grep -R -n -E 'start_zapret2?_runtime|stop_zapret2?_runtime|get_zapret2?_status_json|check_zapret2?_runtime_json|create_zapret2?_nft_rules|validate_rule_nfqws2?_opt|has_enabled_zapret2?_rules|get_zapret2?_rule_' \
|
||||
"$PODKOP_BIN" "$PODKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
|
||||
"$FORKOP_BIN" "$FORKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
|
||||
fail "zapret runtime shell symbols must not remain"
|
||||
fi
|
||||
if grep -R -n -E 'is_zapret2?_installed|get_zapret2?_package_version' \
|
||||
"$PODKOP_BIN" "$PODKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
|
||||
"$FORKOP_BIN" "$FORKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
|
||||
fail "zapret installed/version shell predicates must not remain"
|
||||
fi
|
||||
if grep -n -E 'require\("uci"\)\.cursor|uci -q|"uci", "-q"|command_output\(command_from_args\(\[ "uci"' "$NFQUEUE_RUNTIME" "$ZAPRET_COMMON" "$ZAPRET2_COMMON" >/dev/null 2>&1; then
|
||||
@@ -119,7 +119,7 @@ grep -Fq 'providers/zapret2/check.uc' "$ZAPRET2_COMMON" ||
|
||||
grep -Fq 'providers.zapret2.validator' "$ZAPRET2_COMMON" ||
|
||||
fail "providers/zapret2/common.uc must use its own validator module"
|
||||
|
||||
status_json="$(PODKOP_CONFIG_NAME=podkop-plus-definitely-missing ucode -L "$PODKOP_LIB" "$ZAPRET_RUNTIME" status)"
|
||||
status_json="$(FORKOP_CONFIG_NAME=forkop-definitely-missing ucode -L "$FORKOP_LIB" "$ZAPRET_RUNTIME" status)"
|
||||
JSON_VALUE="$status_json" node - <<'NODE'
|
||||
const value = JSON.parse(process.env.JSON_VALUE);
|
||||
if (value.configured !== false || value.enabled_rule_count !== 0 || typeof value.provider_path !== 'string') {
|
||||
@@ -128,7 +128,7 @@ if (value.configured !== false || value.enabled_rule_count !== 0 || typeof value
|
||||
}
|
||||
NODE
|
||||
|
||||
check_json="$(ucode -L "$PODKOP_LIB" "$ZAPRET2_RUNTIME" check)"
|
||||
check_json="$(ucode -L "$FORKOP_LIB" "$ZAPRET2_RUNTIME" check)"
|
||||
JSON_VALUE="$check_json" node - <<'NODE'
|
||||
const value = JSON.parse(process.env.JSON_VALUE);
|
||||
if (!Object.prototype.hasOwnProperty.call(value, 'zapret2_installed') ||
|
||||
@@ -139,14 +139,14 @@ if (!Object.prototype.hasOwnProperty.call(value, 'zapret2_installed') ||
|
||||
}
|
||||
NODE
|
||||
|
||||
ucode -L "$PODKOP_LIB" "$ZAPRET2_VALIDATOR" validate-json nfqws2 '--name podkop --intercept=1' >/dev/null ||
|
||||
ucode -L "$FORKOP_LIB" "$ZAPRET2_VALIDATOR" validate-json nfqws2 '--name forkop --intercept=1' >/dev/null ||
|
||||
fail "providers/zapret2/validator.uc must validate nfqws2 strategies"
|
||||
if ucode -L "$PODKOP_LIB" "$ZAPRET2_VALIDATOR" validate-json nfqws '--dpi-desync=fake' >/dev/null 2>&1; then
|
||||
if ucode -L "$FORKOP_LIB" "$ZAPRET2_VALIDATOR" validate-json nfqws '--dpi-desync=fake' >/dev/null 2>&1; then
|
||||
fail "providers/zapret2/validator.uc must not validate nfqws strategies"
|
||||
fi
|
||||
|
||||
printf 'table inet x { chain y { queue num 4301 bypass } }\n' |
|
||||
ucode -L "$PODKOP_LIB" "$ZAPRET2_CHECK" nft-queue-overlap PodkopPlusTable 4300 4555 >/dev/null ||
|
||||
ucode -L "$FORKOP_LIB" "$ZAPRET2_CHECK" nft-queue-overlap ForkopTable 4300 4555 >/dev/null ||
|
||||
fail "providers/zapret2/check.uc must own zapret2 queue overlap checks"
|
||||
|
||||
printf 'zapret runtime ownership checks passed\n'
|
||||
|
||||
+16
-16
@@ -2,9 +2,9 @@
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
VALIDATOR="$ROOT_DIR/podkop/files/usr/lib/providers/zapret/validator.uc"
|
||||
ZAPRET2_VALIDATOR="$ROOT_DIR/podkop/files/usr/lib/providers/zapret2/validator.uc"
|
||||
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
|
||||
VALIDATOR="$ROOT_DIR/forkop/files/usr/lib/providers/zapret/validator.uc"
|
||||
ZAPRET2_VALIDATOR="$ROOT_DIR/forkop/files/usr/lib/providers/zapret2/validator.uc"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
@@ -43,42 +43,42 @@ assert_json_field() {
|
||||
[ "$actual" = "$expected" ] || fail "expected $field=$expected, got $actual"
|
||||
}
|
||||
|
||||
valid_nfqws="$(ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-json nfqws '--dpi-desync=fake --dpi-desync-repeats 2')"
|
||||
valid_nfqws="$(ucode -L "$FORKOP_LIB" "$VALIDATOR" validate-json nfqws '--dpi-desync=fake --dpi-desync-repeats 2')"
|
||||
assert_json_field "$valid_nfqws" valid true
|
||||
|
||||
configured="$(ucode -L "$PODKOP_LIB" "$VALIDATOR" strategy-or-default "$(printf -- '--dpi-desync=fake\t--dpi-desync-repeats 2')" '--default')"
|
||||
configured="$(ucode -L "$FORKOP_LIB" "$VALIDATOR" strategy-or-default "$(printf -- '--dpi-desync=fake\t--dpi-desync-repeats 2')" '--default')"
|
||||
[ "$configured" = "--dpi-desync=fake --dpi-desync-repeats 2" ] ||
|
||||
fail "configured zapret strategy should be normalized, got '$configured'"
|
||||
|
||||
defaulted="$(ucode -L "$PODKOP_LIB" "$VALIDATOR" strategy-or-default "" "$(printf -- '--default\t1')")"
|
||||
defaulted="$(ucode -L "$FORKOP_LIB" "$VALIDATOR" strategy-or-default "" "$(printf -- '--default\t1')")"
|
||||
[ "$defaulted" = "--default 1" ] ||
|
||||
fail "empty zapret strategy should use normalized default, got '$defaulted'"
|
||||
|
||||
if invalid_nfqws="$(ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-json nfqws '--hostlist domains.txt' 2>/dev/null)"; then
|
||||
if invalid_nfqws="$(ucode -L "$FORKOP_LIB" "$VALIDATOR" validate-json nfqws '--hostlist domains.txt' 2>/dev/null)"; then
|
||||
fail "hostlist selection should be rejected for nfqws"
|
||||
fi
|
||||
assert_json_field "$invalid_nfqws" valid false
|
||||
assert_json_field "$invalid_nfqws" needle --hostlist
|
||||
|
||||
if ucode -L "$PODKOP_LIB" "$VALIDATOR" validate nfqws '--qnum=200' >/tmp/zapret-validator.out 2>/dev/null; then
|
||||
if ucode -L "$FORKOP_LIB" "$VALIDATOR" validate nfqws '--qnum=200' >/tmp/zapret-validator.out 2>/dev/null; then
|
||||
fail "qnum override should be rejected for nfqws"
|
||||
fi
|
||||
grep -q 'NFQUEUE number is assigned by Podkop Plus' /tmp/zapret-validator.out ||
|
||||
grep -q 'NFQUEUE number is assigned by Forkop' /tmp/zapret-validator.out ||
|
||||
fail "qnum rejection should explain ownership"
|
||||
|
||||
valid_nfqws2="$(ucode -L "$PODKOP_LIB" "$ZAPRET2_VALIDATOR" validate-json nfqws2 '--name podkop --intercept=1')"
|
||||
valid_nfqws2="$(ucode -L "$FORKOP_LIB" "$ZAPRET2_VALIDATOR" validate-json nfqws2 '--name forkop --intercept=1')"
|
||||
assert_json_field "$valid_nfqws2" valid true
|
||||
|
||||
if invalid_nfqws2="$(ucode -L "$PODKOP_LIB" "$ZAPRET2_VALIDATOR" validate-json nfqws2 '--intercept=0' 2>/dev/null)"; then
|
||||
if invalid_nfqws2="$(ucode -L "$FORKOP_LIB" "$ZAPRET2_VALIDATOR" validate-json nfqws2 '--intercept=0' 2>/dev/null)"; then
|
||||
fail "disabled nfqws2 intercept should be rejected"
|
||||
fi
|
||||
assert_json_field "$invalid_nfqws2" valid false
|
||||
assert_json_field "$invalid_nfqws2" needle --intercept
|
||||
|
||||
if ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-json nfqws2 '--name podkop --intercept=1' >/dev/null 2>&1; then
|
||||
if ucode -L "$FORKOP_LIB" "$VALIDATOR" validate-json nfqws2 '--name forkop --intercept=1' >/dev/null 2>&1; then
|
||||
fail "zapret validator must not own nfqws2 validation"
|
||||
fi
|
||||
if ucode -L "$PODKOP_LIB" "$ZAPRET2_VALIDATOR" validate-json nfqws '--dpi-desync=fake' >/dev/null 2>&1; then
|
||||
if ucode -L "$FORKOP_LIB" "$ZAPRET2_VALIDATOR" validate-json nfqws '--dpi-desync=fake' >/dev/null 2>&1; then
|
||||
fail "zapret2 validator must not own nfqws validation"
|
||||
fi
|
||||
|
||||
@@ -94,11 +94,11 @@ let invalid_nfqws = validator.validate_strategy("nfqws", "--hostlist domains.txt
|
||||
if (invalid_nfqws.valid || invalid_nfqws.needle != "--hostlist")
|
||||
exit(1);
|
||||
|
||||
let valid_nfqws2 = zapret2_validator.validate_strategy("nfqws2", "--name podkop --intercept=1", "");
|
||||
let valid_nfqws2 = zapret2_validator.validate_strategy("nfqws2", "--name forkop --intercept=1", "");
|
||||
if (!valid_nfqws2.valid)
|
||||
exit(1);
|
||||
|
||||
if (validator.validate_strategy("nfqws2", "--name podkop --intercept=1", "").valid)
|
||||
if (validator.validate_strategy("nfqws2", "--name forkop --intercept=1", "").valid)
|
||||
exit(1);
|
||||
if (zapret2_validator.validate_strategy("nfqws", "--dpi-desync=fake", "").valid)
|
||||
exit(1);
|
||||
@@ -107,6 +107,6 @@ if (validator.strategy_or_default("", "--default\t1") != "--default 1")
|
||||
exit(1);
|
||||
UCODE
|
||||
|
||||
ucode -L "$PODKOP_LIB" "$WORK_DIR/require-zapret-validator.uc"
|
||||
ucode -L "$FORKOP_LIB" "$WORK_DIR/require-zapret-validator.uc"
|
||||
|
||||
printf 'Zapret validator checks passed\n'
|
||||
|
||||
Reference in New Issue
Block a user