RKNnoVPN/scripts/ci/check_local_first_runtime.sh
loop-uh 2ccfe8ce0a
Some checks failed
RKNnoVPN Linux and Android CI / policy-and-go (push) Successful in 14s
RKNnoVPN Linux and Android CI / android-debug (push) Successful in 2m5s
Full build and Forgejo release / Privacy Lint (push) Successful in 3s
Full build and Forgejo release / Local-first Runtime Guardrail (push) Successful in 2s
Full build and Forgejo release / Go Tests (push) Successful in 13s
Full build and Forgejo release / Resolve sing-box release (push) Successful in 1s
Full build and Forgejo release / Resolve Xray-core release (push) Successful in 1s
Full build and Forgejo release / Android Guardrails & Tests (push) Failing after 16s
Full build and Forgejo release / Build APK (push) Has been skipped
Full build and Forgejo release / Build Runtime CLI (arm64) (push) Successful in 11s
Full build and Forgejo release / Build Runtime CLI (armv7) (push) Successful in 11s
Full build and Forgejo release / Build sing-box (arm64) (push) Failing after 2s
Full build and Forgejo release / Build sing-box (armv7) (push) Failing after 2s
Full build and Forgejo release / Build Xray-core (arm64) (push) Failing after 3s
Full build and Forgejo release / Build Xray-core (armv7) (push) Failing after 2s
Full build and Forgejo release / Build Magisk Module (push) Has been skipped
Full build and Forgejo release / Create Release (push) Has been skipped
Перенести выпуск RKNnoVPN на Forgejo
2026-08-07 03:08:36 +03:00

245 lines
13 KiB
Shell

#!/usr/bin/env bash
# Repository-independent Forgejo/local policy check.
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
removed_update_installer="app/app/src/main/kotlin/com/rknnovpn/panel/update/Update""Installer.kt"
obsolete_paths=(
"daemon"
"app/app/src/main/kotlin/com/rknnovpn/panel/ipc"
"app/app/src/main/kotlin/com/rknnovpn/panel/advisor"
"app/app/src/main/kotlin/com/rknnovpn/panel/boot"
"app/app/src/main/kotlin/com/rknnovpn/panel/model"
"app/app/src/main/kotlin/com/rknnovpn/panel/profile/AppProfileV1.kt"
"app/app/src/main/kotlin/com/rknnovpn/panel/repository/StatusRepository.kt"
"app/app/src/main/kotlin/com/rknnovpn/panel/runtime/RuntimeConfigRenderer.kt"
"${removed_update_installer}"
"app/app/src/main/kotlin/com/rknnovpn/panel/ui/audit"
"app/app/src/test/kotlin/com/rknnovpn/panel/ipc"
"scripts/ci/check_ipc_contract_codegen.py"
"tools/device_lab/check_diagnostics_report.py"
)
for obsolete_path in "${obsolete_paths[@]}"; do
if [ -e "${repo_root}/${obsolete_path}" ]; then
echo "::error file=${repo_root}/${obsolete_path},title=Local-first runtime::Obsolete daemon/IPC path must not exist" >&2
exit 1
fi
done
alternate_type_name='type''alias'
removed_model_package='com[.]rknnovpn[.]panel[.]mo''del[.]|package[[:space:]]+com[.]rknnovpn[.]panel[.]mo''del'
kotlin_import_alias='^import .* as '
if find "${repo_root}/app/app/src/main/kotlin" -type f -name '*.kt' -print0 |
xargs -0 -r grep -En -- "${alternate_type_name}|${removed_model_package}|${kotlin_import_alias}"; then
echo "::error title=Local-first runtime::APK production Kotlin must use direct domain types, not removed model package aliases or import aliases" >&2
exit 1
fi
compat_method='compat[.]check'
contract_method='ipc[.]contract'
generated_contract='Generated''Dae''monContract'
require_gate='require''Compatible'
compat_cache='compat''ibility''Cache'
cooldown_text='Repair ''cooldown'
stable_invalid='stable''InvalidResponse'
module_fix='module''Rep''air'
allow_module_fix='allow''Module''Rep''air'
old_socket_owner='Dae''mon'
old_client="${old_socket_owner}Client"
old_runtime_bridge="${old_socket_owner}RuntimeBridge"
oldctl='dae''mon''ctl'
old_socket='dae''mon[.]sock'
diagnostics_report='diagnostics[.-]report'
self_check='self''-check'
backend_method='backend[.](status|start|stop|reset)'
profile_method='profile[.](get|apply)'
subscription_method='subscription[.](preview|refresh)'
update_state='update''Install|update''Check|Cached''UpdateCheckState|Update''InstallState'
root_profile_state='RKNNOVPN_STATE_DIR|PROFILE_FILE|PROFILES_DIR'
old_status_shape='Backend''StatusV2|Runtime''V2|Applied''StateV2|Runtime''Readiness''Status|Node''Probe''ResultV2|backend''Status|to''Runtime''Status|applied''State|runtime_v2|backend_kind|tun''Active'
pattern="${compat_method}|${contract_method}|${generated_contract}|${require_gate}|${compat_cache}|${cooldown_text}|${stable_invalid}|${module_fix}|${allow_module_fix}|${old_client}|${old_runtime_bridge}|${oldctl}|${old_socket}|${diagnostics_report}|${self_check}|${backend_method}|${profile_method}|${subscription_method}|${update_state}|${root_profile_state}|${old_status_shape}"
if find "${repo_root}/app" "${repo_root}/runtime" "${repo_root}/module" "${repo_root}/tools" "${repo_root}/.forgejo" -type f \
! -path '*/.gradle/*' \
! -path '*/build/*' \
! -path '*/out/*' \
! -path '*/__pycache__/*' \
! -path '*/.git/*' \
! -path "${repo_root}/module/binaries/*" \
! -path "${repo_root}/scripts/ci/check_local_first_runtime.sh" -print0 |
xargs -0 -r grep -En -- "${pattern}"; then
echo "::error title=Local-first runtime::Forbidden removed runtime path detected" >&2
exit 1
fi
if find "${repo_root}/app/app/src/main/kotlin/com/rknnovpn/panel/runtime" -type f -name '*.kt' -print0 |
xargs -0 -r grep -En -- 'put[(]"subscriptions"|AppSubscription|profile[.]subscriptions'; then
echo "::error title=Local-first runtime::APK-owned subscriptions must not be rendered into runtime config" >&2
exit 1
fi
if find "${repo_root}/runtime" -type f -name '*.go' ! -name '*_test.go' -print0 |
xargs -0 -r grep -En -- 'ProfileSubscriptionConfig|Subscriptions[[:space:]]+\[\]json[.]RawMessage|profile[.]subscriptions'; then
echo "::error title=Local-first runtime::Runtime config must not own APK subscription registry" >&2
exit 1
fi
if find "${repo_root}/app/app/src/main/kotlin/com/rknnovpn/panel/runtime" -type f -name '*.kt' -print0 |
xargs -0 -r grep -En -- 'val link|link = link|\"link\"'; then
echo "::error title=Local-first runtime::Runtime projection must not carry share-link recovery data" >&2
exit 1
fi
if ! grep -q 'runtime response is not an envelope' \
"${repo_root}/app/app/src/main/kotlin/com/rknnovpn/panel/runtime/RuntimeController.kt"; then
echo "::error title=Local-first runtime::Runtime protocol must reject non-envelope responses instead of accepting removed bare JSON" >&2
exit 1
fi
if ! grep -q 'runtime response is missing ok' \
"${repo_root}/app/app/src/main/kotlin/com/rknnovpn/panel/runtime/RuntimeController.kt"; then
echo "::error title=Local-first runtime::Runtime protocol must require strict ok/result envelopes" >&2
exit 1
fi
if grep -En 'stageModule|stage-module|ModuleStageResult' \
"${repo_root}/app/app/src/main/kotlin/com/rknnovpn/panel/runtime/RuntimeController.kt" \
"${repo_root}/app/app/src/main/kotlin/com/rknnovpn/panel/repository/RuntimeSessionRepository.kt"; then
echo "::error title=Local-first runtime::Module staging must stay out of exact-match runtime session paths" >&2
exit 1
fi
if grep -En 'pending-module[.]zip|filepath[.]Join[(]moduleDir,[[:space:]]*"releases"' \
"${repo_root}/runtime/cmd/rknnovpn-runtime/main.go"; then
echo "::error title=Local-first runtime::Module staging must use root-manager modules_update layout, not legacy pending zip storage" >&2
exit 1
fi
if ! grep -q 'modules_update.*rknnovpn' \
"${repo_root}/runtime/cmd/rknnovpn-runtime/main.go"; then
echo "::error title=Local-first runtime::Module staging must target a root-manager modules_update/rknnovpn directory" >&2
exit 1
fi
if ! grep -q 'writeStagedReleaseCatalog' \
"${repo_root}/runtime/cmd/rknnovpn-runtime/main.go"; then
echo "::error title=Local-first runtime::Module staging must materialize the installed release catalog for root-manager activation" >&2
exit 1
fi
for required_staged_file in \
scripts/dns.sh \
scripts/iptables.sh \
scripts/privacy_guard.sh \
scripts/routing.sh \
scripts/lib/rknnovpn_netstack.sh \
scripts/lib/rknnovpn_iptables_rules.sh; do
if ! grep -q "\"${required_staged_file}\"" \
"${repo_root}/runtime/cmd/rknnovpn-runtime/main.go"; then
echo "::error title=Local-first runtime::Module staging must require ${required_staged_file}" >&2
exit 1
fi
done
if grep -En 'suspend fun (start|restart)[(][)]: RuntimeCommandResult|override suspend fun (start|restart)[(][)]: RuntimeCommandResult|runtime[.](start|restart)[(][)]' \
"${repo_root}/app/app/src/main/kotlin/com/rknnovpn/panel/runtime/RuntimeController.kt" \
"${repo_root}/app/app/src/main/kotlin/com/rknnovpn/panel/repository/RuntimeSessionRepository.kt"; then
echo "::error title=Local-first runtime::Runtime start/restart must take the current AppProfile and apply it before mutation" >&2
exit 1
fi
if find "${repo_root}/runtime/internal/config" -type f -name '*.go' ! -name '*_test.go' -print0 |
xargs -0 -r grep -En -- 'applyProfileLinkFallback|streamSettingsFromLinkQuery|parseShadowsocksLink|parseUserInfoProxyLink|Link[[:space:]]+string[[:space:]]+`json:"link"`'; then
echo "::error title=Local-first runtime::Runtime must not repair profile nodes from share links" >&2
exit 1
fi
if find "${repo_root}/runtime/internal/config" -type f -name '*.go' ! -name '*_test.go' -print0 |
xargs -0 -r grep -En -- 'normalizeProfileProjectionConfig|normalizeProfileNodes|delete[(]node,[[:space:]]*"(source|createdAt|latencyMs|responseMs|throughputBps|testStatus)"[)]'; then
echo "::error title=Local-first runtime::Runtime must reject stale APK node metadata instead of normalizing it" >&2
exit 1
fi
if grep -En 'ignoreUnknownKeys|isLenient|coerceInputValues|profile-v1[.]json[.]bak|profile[.]corrupt|quarantineLocked' \
"${repo_root}/app/app/src/main/kotlin/com/rknnovpn/panel/storage/FileAppProfileStore.kt"; then
echo "::error title=Local-first runtime::APK profile store must replace unsupported files cleanly instead of using removed repair paths" >&2
exit 1
fi
if grep -En '"(node|transport)"' \
"${repo_root}/module/defaults/config.json" \
"${repo_root}/app/app/src/main/kotlin/com/rknnovpn/panel/runtime/RuntimeProfileRenderer.kt"; then
echo "::error title=Local-first runtime::Runtime config must be APK profile projection, not removed node/transport state" >&2
exit 1
fi
if find "${repo_root}/runtime/cmd" "${repo_root}/runtime/internal/core" "${repo_root}/runtime/internal/config" \
-type f \( -name '*.go' ! -name '*_test.go' \) -print0 |
xargs -0 -r grep -En -- 'SyncFromProfileProjection|cfg[.]ResolveProfile[(][)]'; then
echo "::error title=Local-first runtime::Runtime actions must use APK-rendered active profile projection" >&2
exit 1
fi
if find "${repo_root}/runtime/internal/core" "${repo_root}/runtime/internal/netstack" \
-type f \( -name '*.go' ! -name '*_test.go' \) -print0 |
xargs -0 -r grep -En -- 'HotSwap|hot-swap|hotswap|singbox[.]hotswap|restorePreviousRuntimeAfterHotSwapFailure|RuntimePreserved|runtimePreservedError'; then
echo "::error title=Local-first runtime::Runtime must not keep removed hot-swap/rollback paths" >&2
exit 1
fi
if find "${repo_root}/runtime/cmd" -type f \( -name '*.go' ! -name '*_test.go' \) -print0 |
xargs -0 -r grep -En -- 'HotSwap|hot-swap|hotswap|singbox[.]hotswap'; then
echo "::error title=Local-first runtime::Runtime CLI must not keep removed hot-swap config paths" >&2
exit 1
fi
if find "${repo_root}/runtime/internal/core" -type f \( -name '*.go' ! -name '*_test.go' \) -print0 |
xargs -0 -r grep -En -- 'StatusInfo|func [(]m [*]CoreManager[)] Status[(]|SetState[(]|StateDegraded'; then
echo "::error title=Local-first runtime::Runtime CLI owns minimal status; core must not keep removed status snapshots" >&2
exit 1
fi
if find "${repo_root}/runtime/internal/config" -type f \( -name '*.go' ! -name '*_test.go' \) -print0 |
xargs -0 -r grep -En -- 'type[[:space:]]+(NodeConfig|TransportConfig)|ResolveProfile[(]'; then
echo "::error title=Local-first runtime::Runtime config must not keep removed node/transport recovery APIs" >&2
exit 1
fi
if find "${repo_root}/module" "${repo_root}/runtime/internal/modulecontract" -type f -print0 |
xargs -0 -r grep -En -- 'BACKUP_DIR|BackupDir(Name)?|current[.]pre-|bin config config/rendered scripts run logs backup|bin config config/rendered scripts run logs backup profiles|/profiles| profiles( |$)'; then
echo "::error title=Local-first runtime::Module layout must not recreate removed profile/backup carry-forward paths" >&2
exit 1
fi
if find "${repo_root}/module" -type f \
! -path "${repo_root}/module/binaries/*" -print0 |
xargs -0 -r grep -En -- 'restore_missing_binaries|Restoring missing or non-executable binary|binary restore|chmod[[:space:]]+0?750[[:space:]]+["$][^[:space:]]*bin_path'; then
echo "::error title=Local-first runtime::Module boot must fail clearly instead of running binary repair loops" >&2
exit 1
fi
if find "${repo_root}/module" -type f \
! -path "${repo_root}/module/binaries/*" -print0 |
xargs -0 -r grep -En -- 'rknnovpn-rollback|rollback-guard|rknnovpn-uninstall-cleanup|stage_deferred_cleanup|trigger_rollback_guard|ROLLBACK_(DIR|SERVICE_SCRIPT|SYSCTL)|DEFERRED_(DIR|SERVICE_SCRIPT)|sysctl-snapshot|restore_kernel_params|rknnovpn_restore_sysctl_snapshots'; then
echo "::error title=Local-first runtime::Module must not keep removed rollback/deferred cleanup hooks" >&2
exit 1
fi
if grep -En '/proc/sys/net/(ipv4/ip_forward|ipv6/conf/all/forwarding|ipv4/conf/.*/rp_filter)|rknnovpn_set_sysctl|set_sysctl_value' \
"${repo_root}/module/post-fs-data.sh" "${repo_root}/module/scripts/lib/rknnovpn_env.sh"; then
echo "::error title=Local-first runtime::Module boot must not change kernel networking before an explicit runtime action" >&2
exit 1
fi
if find "${repo_root}/module" "${repo_root}/runtime/internal/modulecontract" -type f -print0 |
xargs -0 -r grep -En -- 'boot-clean|--boot-clean|BootCleanupMarkers|has_boot_cleanup_markers|rknnovpn_has_boot_cleanup_markers|has_orphan_runtime_processes|Boot rescue cleanup|service boot cleanup|Running boot rescue cleanup'; then
echo "::error title=Local-first runtime::Module must not run boot-time root cleanup or stale-state repair" >&2
exit 1
fi
if find "${repo_root}/app/app/src/main/kotlin" -type f -name '*.kt' -print0 |
xargs -0 -r grep -En -- 'RuntimeStatusPoller|pollNow|lastPollError|RuntimeConnectionState[.]POLLING|^[[:space:]]*suspend fun probe[(][)]'; then
echo "::error title=Local-first runtime::APK must not poll optional runtime/root without an explicit user action" >&2
exit 1
fi