182 lines
5.5 KiB
Bash
182 lines
5.5 KiB
Bash
#!/usr/bin/env bash
|
|
set -eo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
UPDATES_UC="$ROOT_DIR/tachyon/files/usr/lib/components/updates.uc"
|
|
REAL_LIB="$ROOT_DIR/tachyon/files/usr/lib"
|
|
WORK_DIR="$(mktemp -d)"
|
|
|
|
cleanup() {
|
|
rm -rf "$WORK_DIR"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
fail() {
|
|
printf 'FAIL: %s\n' "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
write_stub() {
|
|
local path="$1"
|
|
local body="$2"
|
|
|
|
mkdir -p "$(dirname "$path")"
|
|
printf '%s\n' "$body" >"$path"
|
|
}
|
|
|
|
stub_header='#!/usr/bin/env ucode
|
|
let fs = require("fs");
|
|
|
|
function as_string(value) {
|
|
return value == null ? "" : "" + value;
|
|
}
|
|
|
|
function shell_quote(value) {
|
|
return "'"'"'" + replace(as_string(value), /'"'"'/g, "'"'"'\\'"'"''"'"'") + "'"'"'";
|
|
}
|
|
|
|
function record(line) {
|
|
let path = getenv("FAKE_CALL_LOG") || "";
|
|
if (path != "")
|
|
system("printf '"'"'%s\\n'"'"' " + shell_quote(line) + " >> " + shell_quote(path));
|
|
}
|
|
'
|
|
|
|
FAKE_LIB="$WORK_DIR/lib"
|
|
|
|
write_stub "$FAKE_LIB/subscription/cache.uc" "$stub_header"'
|
|
let mode = as_string(ARGV[0]);
|
|
record("subscription/cache:" + mode);
|
|
if (mode == "ensure-runtime-dirs")
|
|
exit(0);
|
|
if (mode == "update-request") {
|
|
print(getenv("FAKE_SUBSCRIPTION_UPDATE_SUMMARY") || "1 0 0 0", "\n");
|
|
exit(0);
|
|
}
|
|
exit(64);
|
|
'
|
|
|
|
write_stub "$FAKE_LIB/service/state.uc" "$stub_header"'
|
|
let mode = as_string(ARGV[0]);
|
|
record("service/state:" + mode);
|
|
if (mode == "sing-box-service-runtime-pid") {
|
|
print("3285\n");
|
|
exit(0);
|
|
}
|
|
if (mode == "acquire-runtime-dir-lock" ||
|
|
mode == "acquire-runtime-dir-lock-wait" ||
|
|
mode == "release-runtime-dir-lock" ||
|
|
mode == "reload-sing-box-runtime" ||
|
|
mode == "write-current-reload-state-clean" ||
|
|
mode == "run-pending-reload-if-requested")
|
|
exit(0);
|
|
exit(64);
|
|
'
|
|
|
|
write_stub "$FAKE_LIB/server/service.uc" "$stub_header"'
|
|
record("server/service:" + as_string(ARGV[0]));
|
|
exit(ARGV[0] == "prepare-all-defaults" ? 0 : 64);
|
|
'
|
|
|
|
write_stub "$FAKE_LIB/config/validator.uc" "$stub_header"'
|
|
record("config/validator:" + as_string(ARGV[0]));
|
|
exit(ARGV[0] == "validate-runtime" ? 0 : 64);
|
|
'
|
|
|
|
write_stub "$FAKE_LIB/singbox/runtime.uc" "$stub_header"'
|
|
let mode = as_string(ARGV[0]);
|
|
if (mode == "init-config")
|
|
record("singbox/runtime:init-config:" + as_string(ARGV[1]) + ":" + as_string(ARGV[2]) + ":" + as_string(ARGV[3]));
|
|
else
|
|
record("singbox/runtime:" + mode);
|
|
if (mode == "configure-service" || mode == "init-config")
|
|
exit(0);
|
|
exit(64);
|
|
'
|
|
|
|
write_stub "$FAKE_LIB/singbox/priority.uc" "$stub_header"'
|
|
let mode = as_string(ARGV[0]);
|
|
record("singbox/priority:" + mode);
|
|
if (mode == "stop-runtime" || mode == "start-runtime")
|
|
exit(0);
|
|
exit(64);
|
|
'
|
|
|
|
write_stub "$FAKE_LIB/singbox/dns_failover.uc" "$stub_header"'
|
|
let mode = as_string(ARGV[0]);
|
|
record("singbox/dns_failover:" + mode);
|
|
if (mode == "stop-runtime" || mode == "start-runtime")
|
|
exit(0);
|
|
exit(64);
|
|
'
|
|
|
|
run_update() {
|
|
local summary="$1"
|
|
local log="$2"
|
|
|
|
: >"$log"
|
|
env \
|
|
TACHYON_LIB="$FAKE_LIB" \
|
|
TACHYON_RUNTIME_STATE_DIR="$WORK_DIR/run" \
|
|
TACHYON_SUBSCRIPTION_UPDATE_LOCK_DIR="$WORK_DIR/run/subscription-update.lock" \
|
|
TACHYON_RELOAD_LOCK_DIR="$WORK_DIR/run/reload.lock" \
|
|
TACHYON_SUBSCRIPTION_UPDATE_STATE_DIR="$WORK_DIR/run/subscription-update" \
|
|
TACHYON_SUBSCRIPTION_UPDATE_JOB_DIR="$WORK_DIR/run/subscription-update-jobs" \
|
|
TACHYON_SUBSCRIPTION_LINKS_DIR="$WORK_DIR/run/subscription-links" \
|
|
TACHYON_SUBSCRIPTION_METADATA_DIR="$WORK_DIR/run/subscription-metadata" \
|
|
TACHYON_OUTBOUND_METADATA_DIR="$WORK_DIR/run/outbound-metadata" \
|
|
TACHYON_SECTION_CACHE_DIR="$WORK_DIR/run/section-cache" \
|
|
TACHYON_RUNTIME_CACHE_FORMAT_FILE="$WORK_DIR/run/cache-format" \
|
|
TACHYON_PERSISTENT_SUBSCRIPTION_CACHE_DIR="$WORK_DIR/persistent/subscription-cache" \
|
|
TACHYON_PERSISTENT_SUBSCRIPTION_CACHE_FORMAT_FILE="$WORK_DIR/persistent/subscription-cache/cache-format" \
|
|
TACHYON_PENDING_RELOAD_FILE="$WORK_DIR/run/reload.pending" \
|
|
TACHYON_RELOAD_STATE_FILE="$WORK_DIR/run/reload-state" \
|
|
TACHYON_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
|
|
}
|
|
|
|
updated_log="$WORK_DIR/updated.log"
|
|
run_update "1 0 0 0" "$updated_log"
|
|
|
|
node - "$updated_log" <<'JS'
|
|
const fs = require("fs");
|
|
const calls = fs.readFileSync(process.argv[2], "utf8").trim().split(/\n+/);
|
|
const expected = [
|
|
"subscription/cache:update-request",
|
|
"server/service:prepare-all-defaults",
|
|
"config/validator:validate-runtime",
|
|
"singbox/runtime:configure-service",
|
|
"service/state:sing-box-service-runtime-pid",
|
|
"singbox/dns_failover:stop-runtime",
|
|
"singbox/runtime:init-config:0:1:1",
|
|
"singbox/priority:stop-runtime",
|
|
"service/state:reload-sing-box-runtime",
|
|
"singbox/priority:start-runtime",
|
|
"singbox/dns_failover:start-runtime",
|
|
"service/state:write-current-reload-state-clean",
|
|
"service/state:run-pending-reload-if-requested"
|
|
];
|
|
|
|
let position = -1;
|
|
for (const item of expected) {
|
|
const next = calls.indexOf(item, position + 1);
|
|
if (next === -1) {
|
|
console.error(`missing or out-of-order call: ${item}`);
|
|
console.error(calls.join("\n"));
|
|
process.exit(1);
|
|
}
|
|
position = next;
|
|
}
|
|
JS
|
|
|
|
unchanged_log="$WORK_DIR/unchanged.log"
|
|
run_update "0 0 1 0" "$unchanged_log"
|
|
|
|
if grep -Eq 'server/service|config/validator|singbox/runtime|singbox/priority|singbox/dns_failover|reload-sing-box-runtime|write-current-reload-state-clean' "$unchanged_log"; then
|
|
fail "unchanged subscription update must not rebuild or reload sing-box"
|
|
fi
|
|
|
|
printf 'subscription update reload checks passed\n'
|