Pin exact sing-box package on APK

This commit is contained in:
ushan0v
2026-07-12 15:09:46 +03:00
parent 3c0cc5fe7d
commit 6a3b0a459d
4 changed files with 95 additions and 14 deletions
+20 -13
View File
@@ -371,7 +371,7 @@ function opkg_package_version_from_list(package_name, output) {
function available_package_version(package_name) {
package_name = as_string(package_name);
if (is_apk())
return trim(helper_output_input(command_output_from_args([ "apk", "policy", package_name ]), "updates-apk-policy-version", []));
return trim(module_output([ LIB_DIR + "/core/packages.uc", "apk-available-version", package_name ]));
return opkg_package_version_from_list(package_name, command_output_from_args([ "opkg", "list", package_name ]));
}
@@ -384,12 +384,16 @@ function pkg_install_name_command(package_name) {
command_from_args([ "opkg", "install", package_name ]) + " </dev/null";
}
function pkg_install_name_downgrade(package_name) {
function pkg_install_name_downgrade(package_name, package_version) {
package_name = as_string(package_name);
if (is_apk()) {
package_version = as_string(package_version);
if (package_version == "")
return false;
let package_spec = package_name + "=" + package_version;
if (pkg_is_installed(package_name))
return command_success(command_from_args([ "apk", "fix", "--reinstall", "--upgrade", package_name ]) + " </dev/null");
return command_success(command_from_args([ "apk", "add", package_name ]) + " </dev/null");
return command_success(command_from_args([ "apk", "fix", "--reinstall", "--upgrade", package_spec ]) + " </dev/null");
return command_success(command_from_args([ "apk", "add", package_spec ]) + " </dev/null");
}
return command_success(command_from_args([ "opkg", "install", "--force-overwrite", "--force-reinstall", "--force-downgrade", package_name ]) + " </dev/null") ||
@@ -1150,7 +1154,8 @@ function restore_file_backup(target_path, backup_path) {
}
function restore_sing_box_service_from_marker(marker) {
if (as_string(marker) == "extended-compressed")
if (as_string(marker) == "extended-compressed" ||
(!file_exists("/etc/init.d/sing-box") && file_nonempty("/usr/bin/sing-box")))
return install_managed_sing_box_service_script();
remove_managed_sing_box_service_script();
return true;
@@ -1265,24 +1270,24 @@ function restore_sing_box_extended_package_variant() {
return true;
}
function replace_sing_box_package_variant(target_package, conflict_package) {
function replace_sing_box_package_variant(target_package, conflict_package, target_version) {
prepare_sing_box_package_service_install();
if ((as_string(conflict_package) == "" || !pkg_is_installed(conflict_package)) &&
(target_package == "sing-box-extended" || !pkg_is_installed("sing-box-extended")))
return pkg_install_name_downgrade(target_package);
return pkg_install_name_downgrade(target_package, target_version);
if (target_package != "sing-box-extended" && !pkg_remove_sing_box_conflict("sing-box-extended"))
return false;
if (as_string(conflict_package) != "" && !pkg_remove_sing_box_conflict(conflict_package))
return false;
return pkg_install_name_downgrade(target_package);
return pkg_install_name_downgrade(target_package, target_version);
}
function restore_sing_box_package_variant(previous_variant) {
if (previous_variant == "tiny")
return replace_sing_box_package_variant("sing-box-tiny", "sing-box");
return replace_sing_box_package_variant("sing-box-tiny", "sing-box", available_package_version("sing-box-tiny"));
if (previous_variant == "stable")
return replace_sing_box_package_variant("sing-box", "sing-box-tiny");
return replace_sing_box_package_variant("sing-box", "sing-box-tiny", available_package_version("sing-box"));
if (previous_variant == "extended")
return restore_sing_box_extended_package_variant();
if (previous_variant == "not-installed") {
@@ -1304,8 +1309,10 @@ function restore_sing_box_install_backup(previous_variant, backup_binary) {
if (sing_box_variant_is_package_managed(previous_variant)) {
if (restore_sing_box_package_variant(previous_variant))
return true;
if (as_string(backup_binary) != "")
restore_sing_box_backup(backup_binary);
if (as_string(backup_binary) != "" && restore_sing_box_backup(backup_binary)) {
updates_log("Package rollback failed; restored the previous sing-box binary backup", "warn");
return true;
}
return false;
}
@@ -1708,7 +1715,7 @@ function install_package_sing_box(action, tiny) {
}
if (!run_logged("Installing " + label + " package", "sh -c " + shell_quote("exit 0")) ||
!replace_sing_box_package_variant(package_name, conflict))
!replace_sing_box_package_variant(package_name, conflict, latest_version))
fail_package_sing_box_install(action, tiny, "package installation failed", current_version, latest_version,
package_name, previous_variant, backup_binary, backup_cronet, previous_marker, previous_version_state, cronet_touched);
+13 -1
View File
@@ -102,6 +102,16 @@ function apk_version(package_name) {
return apk_info_version(package_name, command_output([ "apk", "info", "-v", package_name ]));
}
function apk_available_version(package_name) {
package_name = as_string(package_name);
if (!command_exists("apk"))
return "";
return apk_manifest_version(
package_name,
command_output([ "apk", "list", "--available", "--manifest", package_name ])
);
}
function opkg_version(package_name) {
package_name = as_string(package_name);
if (!command_exists("opkg"))
@@ -133,9 +143,11 @@ else if (mode == "version")
print(version(ARGV[1]), "\n");
else if (mode == "apk-version")
print(apk_version(ARGV[1]), "\n");
else if (mode == "apk-available-version")
print(apk_available_version(ARGV[1]), "\n");
else if (mode == "opkg-version")
print(opkg_version(ARGV[1]), "\n");
else {
warn("Usage: core/packages.uc <installed|apk-installed|opkg-installed|version|apk-version|opkg-version> <package>\n");
warn("Usage: core/packages.uc <installed|apk-installed|opkg-installed|version|apk-version|apk-available-version|opkg-version> <package>\n");
exit(1);
}
+44
View File
@@ -101,6 +101,12 @@ grep -Fq 'forkop_status_running_with_timeout()' "$ACTION_UC" ||
fail "components/action.uc must use bounded Forkop status checks for component actions"
grep -Fq 'restore_sing_box_after_failed_package_install' "$ACTION_UC" ||
fail "stable and tiny sing-box package installs must restore the previous variant after validation failures"
grep -Fq 'let package_spec = package_name + "=" + package_version;' "$ACTION_UC" ||
fail "APK sing-box installs must pin the concrete package version instead of selecting a provider"
grep -Fq 'Package rollback failed; restored the previous sing-box binary backup' "$ACTION_UC" ||
fail "package rollback must accept a successfully restored binary backup"
grep -Fq '!file_exists("/etc/init.d/sing-box") && file_nonempty("/usr/bin/sing-box")' "$ACTION_UC" ||
fail "binary rollback must restore a managed sing-box service when the package script is missing"
grep -Fq 'fail_package_sing_box_install(action, tiny, "package was installed, but sing-box binary is not available"' "$ACTION_UC" ||
fail "stable and tiny sing-box binary validation failures must use transactional rollback"
grep -Fq 'fail_package_sing_box_install(action, tiny, "was installed, but Forkop did not start cleanly"' "$ACTION_UC" ||
@@ -184,6 +190,12 @@ if (mode == "opkg-version")
exit(0);
if (mode == "opkg-installed")
exit(1);
if (mode == "apk-available-version") {
print("1.2.3-r1\n");
exit(0);
}
if (mode == "apk-version")
exit(0);
exit(1);
UCODE
cat >"$package_runtime_lib/singbox/runtime.uc" <<'UCODE'
@@ -253,6 +265,38 @@ if (secondList <= update) fail('stable sing-box version must be resolved again a
if (install <= secondList) fail('stable sing-box install must happen after post-update version resolve');
NODE
cat >"$package_runtime_bin/apk" <<'SH'
#!/usr/bin/env sh
set -eu
printf 'apk %s\n' "$*" >>"${FAKE_APK_LOG:?}"
case "${1:-}" in
info)
exit 1
;;
update|add|fix|del)
exit 0
;;
*)
exit 1
;;
esac
SH
chmod +x "$package_runtime_bin/apk"
set +e
PATH="$package_runtime_bin:$PATH" \
FORKOP_LIB="$package_runtime_lib" \
FORKOP_RUNTIME_STATE_DIR="$WORK_DIR/apk-package-runtime" \
FORKOP_BIN="$WORK_DIR/missing-forkop" \
FORKOP_SERVICE_INIT="$WORK_DIR/missing-init" \
FAKE_APK_LOG="$WORK_DIR/apk.log" \
ucode -L "$package_runtime_lib" "$ACTION_UC" component-action sing_box install_stable >/dev/null
set -e
grep -Fxq 'apk add sing-box=1.2.3-r1' "$WORK_DIR/apk.log" ||
fail "APK stable action must request the exact sing-box package version"
if grep -Fxq 'apk add sing-box' "$WORK_DIR/apk.log"; then
fail "APK stable action must not use the provider-selecting unversioned package name"
fi
release_json="$(cat <<'JSON'
{
"tag_name": "1.2.3",
+18
View File
@@ -94,6 +94,24 @@ if ucode -L "$FORKOP_LIB" "$PACKAGES_UC" installed forkop-definitely-missing >/d
fail "missing package must not be reported installed"
fi
mkdir -p "$WORK_DIR/apk-bin"
cat >"$WORK_DIR/apk-bin/apk" <<'SH'
#!/usr/bin/env sh
if [ "$1 $2 $3 $4" = "list --available --manifest sing-box" ]; then
printf '%s\n' \
'P:sing-box-extended' \
'V:9.9.9-r1' \
'p:sing-box' \
'P:sing-box' \
'V:1.2.3-r1'
exit 0
fi
exit 1
SH
chmod +x "$WORK_DIR/apk-bin/apk"
[ "$(PATH="$WORK_DIR/apk-bin:$PATH" ucode -L "$FORKOP_LIB" "$PACKAGES_UC" apk-available-version sing-box)" = "1.2.3-r1" ] ||
fail "APK available version lookup must select the exact package name, not a higher-priority provider"
mark_hex="$(ucode -L "$FORKOP_LIB" "$RULES_UC" mark-hex 0x01000000 2)"
[ "$mark_hex" = "0x01000002" ] ||
fail "providers/rules.uc mark math changed"