This commit is contained in:
eric
2026-05-28 15:59:03 -05:00
parent 1d89aa29e0
commit 08155500fc
47 changed files with 1126 additions and 122 deletions

View File

@@ -12,10 +12,10 @@ except ImportError:
print("请先安装: pip install paramiko", file=sys.stderr)
sys.exit(1)
HOST = os.environ.get("LIVE_DEPLOY_HOST", "192.168.21.105")
USER = os.environ.get("LIVE_DEPLOY_USER", "live")
PASS = os.environ.get("LIVE_DEPLOY_PASS", "12345678")
REMOTE_BASE = os.environ.get("LIVE_DEPLOY_PATH", "/home/live/douyinyoutube").rstrip("/")
HOST = os.environ.get("LIVE_DEPLOY_HOST", "192.168.31.56")
USER = os.environ.get("LIVE_DEPLOY_USER", "orangepi")
PASS = os.environ.get("LIVE_DEPLOY_PASS", "orangepi")
REMOTE_BASE = os.environ.get("LIVE_DEPLOY_PATH", "/home/orangepi/d2ypp2-src").rstrip("/")
def main() -> int:

View File

@@ -17,22 +17,49 @@ except ImportError:
print("请先安装: pip install paramiko", file=sys.stderr)
sys.exit(1)
HOST = os.environ.get("LIVE_DEPLOY_HOST", "192.168.21.105")
USER = os.environ.get("LIVE_DEPLOY_USER", "live")
PASS = os.environ.get("LIVE_DEPLOY_PASS", "12345678")
REMOTE_BASE = os.environ.get("LIVE_DEPLOY_PATH", "/home/live/douyinyoutube").rstrip("/")
HOST = os.environ.get("LIVE_DEPLOY_HOST", "192.168.31.56")
USER = os.environ.get("LIVE_DEPLOY_USER", "orangepi")
PASS = os.environ.get("LIVE_DEPLOY_PASS", "orangepi")
REMOTE_BASE = os.environ.get("LIVE_DEPLOY_PATH", "/home/orangepi/d2ypp2-src").rstrip("/")
REPO_ROOT = Path(__file__).resolve().parent.parent
def _discover_repo_files() -> list[str]:
"""与 youtube.py 运行相关的源码,避免只同步白名单时缺模块。"""
extra: list[str] = []
backup_dir = REPO_ROOT / "backup"
if backup_dir.is_dir():
for p in sorted(backup_dir.glob("*.py")):
extra.append(str(p.relative_to(REPO_ROOT)).replace("\\", "/"))
return extra
"""Collect files needed for incremental board deployment without copying caches."""
extra: set[str] = {
".gitattributes",
"Dockerfile",
"docker-compose.yaml",
"docker-compose.yml",
"install.sh",
"package.json",
"package-lock.json",
"pyproject.toml",
"requirements.txt",
}
for pattern in (
"*.sh",
"backup/*.py",
"live-platform/scripts/**/*.sh",
"live-platform/tools/*.py",
"scripts/**/*.py",
"scripts/**/*.sh",
"services/**/*.sh",
"src/**/*.py",
"tiktok/**/*.sh",
"tools/*.py",
"web-console/*.json",
"web-console/*.mjs",
"web-console/*.ts",
"web-console/app/**/*",
"web-console/components/**/*",
"web-console/lib/**/*",
):
for candidate in REPO_ROOT.glob(pattern):
if candidate.is_file():
extra.add(str(candidate.relative_to(REPO_ROOT)).replace("\\", "/"))
return sorted(extra)
FILES = [
@@ -166,7 +193,7 @@ def mkdir_p(sftp: paramiko.SFTPClient, remote_dir: str) -> None:
pass
def _shell_bytes_lf(data: bytes) -> bytes:
def _text_bytes_lf(data: bytes) -> bytes:
"""Windows 工作区常见 CRLF;上传到 Linux 后 bash 会把 pipefail\\r 当成非法选项。"""
return data.replace(b"\r\n", b"\n").replace(b"\r", b"\n")
@@ -175,8 +202,14 @@ def sftp_put(sftp: paramiko.SFTPClient, local: Path, remote: str) -> None:
remote = remote.replace("\\", "/")
parent = posixpath.dirname(remote)
mkdir_p(sftp, parent)
if local.suffix.lower() == ".sh":
sftp.putfo(io.BytesIO(_shell_bytes_lf(local.read_bytes())), remote)
if local.suffix.lower() in {".sh", ".py"} or local.name in {
".gitattributes",
"Dockerfile",
"requirements.txt",
"docker-compose.yaml",
"docker-compose.yml",
}:
sftp.putfo(io.BytesIO(_text_bytes_lf(local.read_bytes())), remote)
else:
sftp.put(str(local), remote)

View File

@@ -1,10 +1,10 @@
#!/usr/bin/env python3
"""SSH 到板子:打印 ENABLE_NEKO 与 Docker 中与 srs/neko 相关的容器。
"""Probe ENABLE_NEKO and Docker service state on the ARM board.
环境变量与 deploy_live_paramiko.py 一致:
LIVE_DEPLOY_HOST(默认 192.168.21.105)
LIVE_DEPLOY_USER(默认 live)
LIVE_DEPLOY_PASS(默认 12345678)
Environment defaults match deploy_live_paramiko.py:
LIVE_DEPLOY_HOST: 192.168.31.56
LIVE_DEPLOY_USER: orangepi
LIVE_DEPLOY_PASS: orangepi
"""
from __future__ import annotations
@@ -17,13 +17,13 @@ except ImportError:
print("请先执行: pip install paramiko", file=sys.stderr)
sys.exit(1)
HOST = os.environ.get("LIVE_DEPLOY_HOST", "192.168.21.105")
USER = os.environ.get("LIVE_DEPLOY_USER", "live")
PASS = os.environ.get("LIVE_DEPLOY_PASS", "12345678")
HOST = os.environ.get("LIVE_DEPLOY_HOST", "192.168.31.56")
USER = os.environ.get("LIVE_DEPLOY_USER", "orangepi")
PASS = os.environ.get("LIVE_DEPLOY_PASS", "orangepi")
REMOTE_SH = r"""
echo "--- ENABLE_NEKO in known env files ---"
for f in /opt/live/config/system-stack.env /opt/live/generated/system-stack.env /home/live/douyinyoutube/config/system-stack.env; do
for f in /opt/live/config/system-stack.env /opt/live/generated/system-stack.env /home/orangepi/d2ypp2-src/config/system-stack.env; do
if [ -f "$f" ]; then
echo "=== $f ==="
grep -E '^ENABLE_NEKO=' "$f" 2>/dev/null || echo '(no ENABLE_NEKO line)'

View File

@@ -57,6 +57,7 @@ def render(count: int) -> str:
http_base = int(env("NEKO_HTTP_PORT", "9200"))
mux_base = int(env("NEKO_WEBRTC_TCPMUX", "52300"))
managed_policies = env_enabled("NEKO_MANAGED_POLICIES", True)
external_extensions = env_enabled("NEKO_EXTERNAL_EXTENSIONS", False)
policy_dir = "./policies" if managed_policies else "./no-managed-policies"
lines = [
@@ -109,15 +110,18 @@ def render(count: int) -> str:
f" - {q(f'{policy_dir}:/etc/chromium/policies/managed:ro')}",
f" - {q(f'{policy_dir}:/etc/chromium-browser/policies/managed:ro')}",
f" - {q(f'{policy_dir}:/etc/opt/chrome/policies/managed:ro')}",
' - "./external-extensions:/opt/google/chrome/extensions:ro"',
' - "./external-extensions:/usr/share/chromium/extensions:ro"',
' - "./external-extensions:/usr/share/chromium-browser/extensions:ro"',
' - "./tampermonkey-seed:/etc/neko/tampermonkey-seed:ro"',
' - "./provisioning:/var/www/provisioning:ro"',
' - "./browser-launch.sh:/etc/neko/browser-launch.sh:ro"',
f" - {q(f'./{supervisor_conf}:/etc/neko/supervisord/{supervisor_conf}:ro')}",
]
)
if external_extensions:
lines[-3:-3] = [
' - "./external-extensions:/opt/google/chrome/extensions:ro"',
' - "./external-extensions:/usr/share/chromium/extensions:ro"',
' - "./external-extensions:/usr/share/chromium-browser/extensions:ro"',
]
return "\n".join(lines) + "\n"

View File

@@ -7,10 +7,10 @@ import sys
import paramiko
HOST = os.environ.get("LIVE_DEPLOY_HOST", "192.168.21.105")
USER = os.environ.get("LIVE_DEPLOY_USER", "live")
PASS = os.environ.get("LIVE_DEPLOY_PASS", "12345678")
REMOTE_BASE = os.environ.get("LIVE_DEPLOY_PATH", "/home/live/douyinyoutube").rstrip("/")
HOST = os.environ.get("LIVE_DEPLOY_HOST", "192.168.31.56")
USER = os.environ.get("LIVE_DEPLOY_USER", "orangepi")
PASS = os.environ.get("LIVE_DEPLOY_PASS", "orangepi")
REMOTE_BASE = os.environ.get("LIVE_DEPLOY_PATH", "/home/orangepi/d2ypp2-src").rstrip("/")
CMD = (
f"export LC_ALL=C.UTF-8 LANG=C.UTF-8; "

View File

@@ -15,9 +15,9 @@ except ImportError:
print("请先安装: pip install paramiko", file=sys.stderr)
raise SystemExit(1)
HOST = os.environ.get("LIVE_DEPLOY_HOST", os.environ.get("LIVE_VERIFY_HOST", "192.168.21.105"))
USER = os.environ.get("LIVE_DEPLOY_USER", os.environ.get("LIVE_VERIFY_USER", "live"))
PASS = os.environ.get("LIVE_DEPLOY_PASS", os.environ.get("LIVE_VERIFY_PASS", "12345678"))
HOST = os.environ.get("LIVE_DEPLOY_HOST", os.environ.get("LIVE_VERIFY_HOST", "192.168.31.56"))
USER = os.environ.get("LIVE_DEPLOY_USER", os.environ.get("LIVE_VERIFY_USER", "orangepi"))
PASS = os.environ.get("LIVE_DEPLOY_PASS", os.environ.get("LIVE_VERIFY_PASS", "orangepi"))
PORT = os.environ.get("LIVE_VERIFY_PORT", os.environ.get("PORT", "8001"))
MDNS_HOST = os.environ.get("LIVE_VERIFY_DOMAIN", f"{os.environ.get('LIVE_VERIFY_ALIAS', 'live')}.local")
LOG = Path(__file__).resolve().parent.parent / ".deploy_logs" / "verify_live_console.log"

View File

@@ -490,7 +490,6 @@ def verify_neko(checks: list[dict[str, Any]], env: dict[str, str], *, timeout: f
"python3 - <<'PY'\n"
"import os\n"
"skip = {os.getpid(), os.getppid()}\n"
"bad = [b'--load-' + b'extension=', b'youtube-auto-' + b'dismiss-extension']\n"
"for name in os.listdir('/proc'):\n"
" if not name.isdigit() or int(name) in skip:\n"
" continue\n"
@@ -498,7 +497,10 @@ def verify_neko(checks: list[dict[str, Any]], env: dict[str, str], *, timeout: f
" cmdline = open(f'/proc/{name}/cmdline', 'rb').read()\n"
" except OSError:\n"
" continue\n"
" if any(marker in cmdline for marker in bad):\n"
" args = [arg for arg in cmdline.split(b'\\0') if arg]\n"
" has_unpacked_ext = any(arg.startswith(b'--load-extension=') and arg.split(b'=', 1)[1].strip() for arg in args)\n"
" has_legacy_youtube_ext = b'youtube-auto-' + b'dismiss-extension' in cmdline\n"
" if has_unpacked_ext or has_legacy_youtube_ext:\n"
" print(cmdline.replace(b'\\0', b' ').decode('utf-8', errors='replace'))\n"
" raise SystemExit(1)\n"
"print('no standalone youtube extension')\n"
@@ -527,7 +529,7 @@ def verify_neko(checks: list[dict[str, Any]], env: dict[str, str], *, timeout: f
f"neko_{idx}_tm_storage",
f"{name} Tampermonkey YouTube script imported",
code == 0 and bool(out.strip()),
required=True,
required=not managed_policies,
detail=out.strip() or err2.strip() or "not found",
hint=(
"Open Neko once or restart the browser so Chrome policy imports the provisioning payload."