feat: GiteaHubSync v1.0.0 — fnOS 原生应用:Gitea→GitHub 镜像同步

- 每次推送到飞牛 Gitea(3000 端口)的代码 10 秒内自动镜像到 GitHub 7 个私有仓库
  (SMB_Android / DCU_APP / GD32E50X_BOOT / DCU_DesktopController / Motor_v2 /
  DCU_ECAT_Master / GD32_BOOT),真镜像语义(删分支/标签同步删)
- 标准 .fpk 原生应用:不进 Docker,依赖 python312,安装后进入应用中心已安装列表
  和桌面启动台,支持启动/停止/重启/卸载,NAS 重启自动恢复
- 控制面板(:8899):自动同步开关、立即同步、仓库状态、同步日志、打开 Gitea
- GitHub 推送走 SSH 22 端口直连(HTTPS 443 本地网络被断),部署私钥随应用本地分发
- 附 fnos-install.js(命令行自动安装)、build.sh(重新构建 fpk)、genicon.py(图标生成)

注意:GitHub 部署私钥(GiteaHubSync/app/ssh/id_ed25519)与打包产物 .fpk
按 .gitignore 规则不进仓库,仅保留在工作目录本地分发。
This commit is contained in:
cbb
2026-10-13 19:12:49 +08:00
commit 4d6f71af5b
24 changed files with 1459 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
#!/bin/bash
exit 0
+2
View File
@@ -0,0 +1,2 @@
#!/bin/bash
exit 0
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
# GiteaHubSync 安装后初始化:建数据目录、默认开启自动同步、落一份 SSH 密钥、清理旧面板
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
APPDEST="$(cd "$SCRIPT_DIR/.." && pwd)"
DATA_DIR="${TRIM_PKGVAR:-}"
if [ -n "$DATA_DIR" ] && [ -d "$DATA_DIR" ]; then
mkdir -p "${DATA_DIR}/repos" "${DATA_DIR}/logs"
[ -f "${DATA_DIR}/state.json" ] || printf '{"enabled":true}\n' > "${DATA_DIR}/state.json"
# 把内置 GitHub 密钥复制到数据目录,属主跟随数据目录(应用用户),保证守护进程可读
if [ -f "${APPDEST}/ssh/id_ed25519" ] && [ ! -f "${DATA_DIR}/id_ed25519" ]; then
cp "${APPDEST}/ssh/id_ed25519" "${DATA_DIR}/id_ed25519" 2>/dev/null
chmod 600 "${DATA_DIR}/id_ed25519" 2>/dev/null
chown --reference="${DATA_DIR}" "${DATA_DIR}/id_ed25519" 2>/dev/null
fi
fi
# 停掉占用 8899 端口的旧面板进程(早期原生版或本应用旧实例),避免端口冲突
pkill -f 'panel\.py$' 2>/dev/null
exit 0
+2
View File
@@ -0,0 +1,2 @@
#!/bin/bash
exit 0
+103
View File
@@ -0,0 +1,103 @@
#!/bin/bash
# GiteaHubSync service control(由 fnOS 应用中心在 启动/停止/查询状态 时调用)
# 守护进程以 setsid nohup 方式后台运行,pidfile 位于持久数据目录(@appdata/GiteaHubSync)
PYBIN=""
for candidate in \
/var/apps/python312/target/bin/python3 \
/vol1/@appcenter/python312/target/bin/python3 \
/var/apps/python311/target/bin/python3 \
/var/apps/python310/target/bin/python3 \
/usr/bin/python3 \
/usr/local/bin/python3; do
if [ -x "$candidate" ]; then PYBIN="$candidate"; break; fi
done
# TRIM_APPDEST 由 fnOS 注入;若缺失或指向其他应用,则回退到脚本自身位置
APPDEST="${TRIM_APPDEST:-}"
if [ -z "$APPDEST" ] || [ ! -d "$APPDEST/cmd" ]; then
APPDEST="$(cd "$(dirname "$0")/.." && pwd)"
fi
BACKEND_DIR="${APPDEST}/backend"
[ -d "$BACKEND_DIR" ] || BACKEND_DIR="${APPDEST}/target/backend"
[ -d "$BACKEND_DIR" ] || BACKEND_DIR="${APPDEST}/app/backend"
DAEMON="${BACKEND_DIR}/panel.py"
# 持久数据目录(TRIM_PKGVAR 由 fnOS 注入,通常为 /vol{n}/@appdata/GiteaHubSync)
if [ -n "${TRIM_PKGVAR}" ] && [ -d "${TRIM_PKGVAR}" ]; then
DATA_DIR="${TRIM_PKGVAR}"
else
DATA_DIR=""
for candidate in \
"${APPDEST}/../../@appdata/GiteaHubSync" \
"${APPDEST}/var" \
"/var/apps/GiteaHubSync/var" \
"/vol1/@appdata/GiteaHubSync" \
"/vol2/@appdata/GiteaHubSync"; do
if [ -d "$candidate" ]; then DATA_DIR="$candidate"; break; fi
done
if [ -z "$DATA_DIR" ]; then
DATA_DIR="${APPDEST}/../../@appdata/GiteaHubSync"
fi
fi
PIDFILE="${DATA_DIR}/daemon.pid"
daemon_alive() {
[ -f "$PIDFILE" ] || return 1
local pid
pid="$(cat "$PIDFILE" 2>/dev/null)"
[ -n "$pid" ] || return 1
kill -0 "$pid" 2>/dev/null
}
case "$1" in
start)
if daemon_alive; then
echo "GiteaHubSync daemon already running (pid $(cat "$PIDFILE"))"
exit 0
fi
if [ -z "$PYBIN" ]; then
echo "python3 runtime not found (install_dep_apps=python312)" > "${TRIM_TEMP_LOGFILE:-/dev/stderr}"
exit 1
fi
mkdir -p "$DATA_DIR"
# fnOS 解包可能放宽密钥权限,ssh 会拒绝;启动前收紧
[ -f "${APPDEST}/ssh/id_ed25519" ] && chmod 600 "${APPDEST}/ssh/id_ed25519" 2>/dev/null
[ -f "${DATA_DIR}/id_ed25519" ] && chmod 600 "${DATA_DIR}/id_ed25519" 2>/dev/null
GHS_DATA_DIR="$DATA_DIR" setsid nohup "$PYBIN" "$DAEMON" >> "${DATA_DIR}/daemon.out" 2>&1 &
for _ in 1 2 3 4 5 6 7 8 9 10; do
if daemon_alive; then
echo "GiteaHubSync daemon started (pid $(cat "$PIDFILE"))"
exit 0
fi
sleep 0.5
done
echo "GiteaHubSync daemon failed to start; see ${DATA_DIR}/daemon.out" > "${TRIM_TEMP_LOGFILE:-/dev/stderr}"
exit 1
;;
stop)
if daemon_alive; then
pid="$(cat "$PIDFILE")"
kill "$pid" 2>/dev/null
for _ in 1 2 3 4 5 6 7 8 9 10; do
kill -0 "$pid" 2>/dev/null || break
sleep 0.5
done
kill -9 "$pid" 2>/dev/null
echo "GiteaHubSync daemon stopped (pid $pid)"
else
echo "GiteaHubSync daemon not running"
fi
rm -f "$PIDFILE"
exit 0
;;
status)
if daemon_alive; then
exit 0
fi
exit 3
;;
*)
exit 1
;;
esac
+2
View File
@@ -0,0 +1,2 @@
#!/bin/bash
exit 0
+2
View File
@@ -0,0 +1,2 @@
#!/bin/bash
exit 0
+2
View File
@@ -0,0 +1,2 @@
#!/bin/bash
exit 0
+2
View File
@@ -0,0 +1,2 @@
#!/bin/bash
exit 0