refactor(fnos): move packaging workspace outside main repository
CI / verify (push) Canceled after 0s

This commit is contained in:
2026-10-01 18:58:28 +08:00
parent ac56394dce
commit ea5fd1b934
25 changed files with 8 additions and 410 deletions
-3
View File
@@ -15,9 +15,6 @@ dist/
output/
web/public/111favicon.png
web/src/assets/brand/logo00.png
fnos/*.fpk
fnos/.build/
fnos/dist/
# scripts/ 下的本地辅助脚本不入库,但随仓库分发的 agent 安装脚本需要跟踪
scripts/*
!scripts/agent-install.sh
+8 -3
View File
@@ -1033,13 +1033,18 @@ Havline 适合做飞牛 Docker 应用,不适合首发做成 CGI 或原生应
7. 构建 amd64 / arm64 镜像。
8. 用 `fnpack build` 打包并在飞牛测试设备安装验证。
当前已经在仓库内生成:
当前打包工程位于主仓库同级的飞牛工作目录中,不进入主仓库:
```text
fnos/havline/
F:\Fonu-main\fnos\
├── fnpack.exe
├── build.ps1
├── README.md
└── havline/
```
并补上了首发骨架所需的基础配置。
主程序仍位于 `F:\Fonu-main\havline`,打包工程通过主仓库的 `VERSION` 和
`fnos/build.ps1` 保持版本同步。
阶段 1 暂不实现:
-76
View File
@@ -1,76 +0,0 @@
# Havline fnOS 应用打包
本目录保存 Havline 的飞牛 fnOS 应用工程。
- 应用工程:[`havline/`](havline/)
- 官方工具:`fnpack`
- 当前工具版本:`1.2.3`
## 当前状态
已经完成:
- 使用 `fnpack create havline --template docker` 生成官方 Docker 应用骨架;
- 配置 `manifest`、应用入口、Docker Compose、权限和资源;
- 增加安装向导、配置向导和卸载向导;
- 增加安装、升级、配置和卸载生命周期脚本;
- 支持通过环境变量 `HAVLINE_INITIAL_ADMIN_PASSWORD` 初始化管理员。
仍需完成:
- 将 `app/docker/docker-compose.yaml` 中的 `ghcr.io/your-org/havline` 替换为实际多架构镜像地址;
- 将模板图标替换为 Havline 正式图标;
- 在飞牛测试设备验证安装、启动、入口、升级和卸载;
- 验证 amd64 与 arm64 镜像均可用。
## 打包
Windows PowerShell:
```powershell
cd F:\Fonu-main\havline
.\fnos\build.ps1
```
脚本会先复制一份不含 `.DS_Store` 的干净工程,再调用 `fnpack` 打包,输出到:
```text
fnos/dist/havline-<version>.fpk
```
如果 `fnpack` 已在 PATH 中,也可以传入路径:
```powershell
.\fnos\build.ps1 -FnpackPath fnpack
```
## 测试安装
```bash
appcenter-cli install-fpk havline.fpk
```
安装后检查:
1. 应用中心显示 Havline;
2. Docker 镜像可以拉取;
3. Havline 容器能够启动;
4. 桌面入口可以打开管理界面;
5. 数据写入 `${TRIM_PKGVAR}`;
6. 卸载默认保留数据。
## 当前默认端口
| 用途 | 安装向导默认值 | 容器端口 |
| --- | ---: | ---: |
| 管理界面 | `6893` | `6893` |
| HTTP 反向代理 | `18080` | `80` |
| HTTPS 反向代理 | `9443` | `443` |
## 首发边界
- 不挂载 Docker Socket。
- 不启动 Havline 自带 updater。
- 不使用 host 网络。
- 不共享数据库、证书、密钥、Nginx、FRP 和 cloudflared 目录。
- 应用中心负责安装、升级、启动、停止和卸载。
-41
View File
@@ -1,41 +0,0 @@
param(
[string]$FnpackPath = ""
)
$ErrorActionPreference = "Stop"
$root = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
$project = Join-Path $root "fnos\havline"
$version = (Get-Content -Raw (Join-Path $root "VERSION")).Trim()
if (-not (Test-Path -LiteralPath $project)) {
throw "找不到飞牛应用工程:$project"
}
if (-not $FnpackPath) {
$localTool = Join-Path (Split-Path $root -Parent) "fnos\fnpack.exe"
$FnpackPath = if (Test-Path -LiteralPath $localTool) { $localTool } else { "fnpack" }
}
$stamp = Get-Date -Format "yyyyMMddHHmmss"
$stage = Join-Path $PSScriptRoot ".build\havline-$stamp"
New-Item -ItemType Directory -Force $stage | Out-Null
robocopy $project $stage /E /XF .DS_Store /NFL /NDL /NJH /NJS /NP | Out-Null
if ($LASTEXITCODE -ge 8) {
throw "复制飞牛应用工程失败,robocopy 退出码:$LASTEXITCODE"
}
Push-Location $PSScriptRoot
try {
& $FnpackPath build -d $stage
if ($LASTEXITCODE -ne 0) {
throw "fnpack build 失败,退出码:$LASTEXITCODE"
}
$output = Join-Path $PSScriptRoot "havline.fpk"
$dist = Join-Path $PSScriptRoot "dist"
New-Item -ItemType Directory -Force $dist | Out-Null
Copy-Item -LiteralPath $output -Destination (Join-Path $dist "havline-$version.fpk") -Force
Write-Output "FPK: $(Join-Path $dist "havline-$version.fpk")"
}
finally {
Pop-Location
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

@@ -1,21 +0,0 @@
services:
havline:
# TODO: 发布多架构镜像后,把 your-org 替换为实际镜像命名空间。
image: ghcr.io/your-org/havline:${TRIM_APPVER}
container_name: havline
restart: unless-stopped
ports:
- "${wizard_service_port}:6893"
- "${wizard_nginx_http_port}:80"
- "${wizard_nginx_https_port}:443"
volumes:
- "${TRIM_PKGVAR}:/data"
environment:
HAVLINE_DATA_DIR: /data
HAVLINE_LISTEN: ":6893"
HAVLINE_NGINX_HTTP_PORT: "80"
HAVLINE_NGINX_HTTPS_PORT: "443"
HAVLINE_SESSION_SECRET: ""
HAVLINE_UPDATER_SOCKET: ""
HAVLINE_INITIAL_ADMIN_PASSWORD: "${wizard_initial_admin_password}"
TZ: "${wizard_timezone}"
-16
View File
@@ -1,16 +0,0 @@
{
".url": {
"havline.main": {
"title": "Havline",
"icon": "images/icon_{0}.png",
"type": "iframe",
"protocol": "http",
"port": "${wizard_service_port}",
"url": "/",
"allUsers": false,
"control": {
"accessPerm": "readonly"
}
}
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

-12
View File
@@ -1,12 +0,0 @@
#!/bin/bash
set -euo pipefail
for var in wizard_service_port wizard_nginx_http_port wizard_nginx_https_port; do
value="${!var:-}"
if ! [[ "$value" =~ ^[0-9]+$ ]]; then
echo "$var 必须是数字端口" > "$TRIM_TEMP_LOGFILE"
exit 1
fi
done
exit 0
-2
View File
@@ -1,2 +0,0 @@
#!/bin/bash
exit 0
-8
View File
@@ -1,8 +0,0 @@
#!/bin/bash
set -euo pipefail
mkdir -p "$TRIM_PKGVAR" "$TRIM_PKGVAR/backups"
chown -R "$TRIM_USERNAME:$TRIM_GROUPNAME" "$TRIM_PKGVAR"
echo "Havline 数据目录已准备:$TRIM_PKGVAR"
exit 0
-2
View File
@@ -1,2 +0,0 @@
#!/bin/bash
exit 0
-29
View File
@@ -1,29 +0,0 @@
#!/bin/bash
set -u
CONTAINER_NAME="havline"
LOG_FILE="${TRIM_TEMP_LOGFILE:-/dev/stderr}"
log_error() {
echo "$1" > "$LOG_FILE"
}
is_running() {
docker inspect "$CONTAINER_NAME" 2>/dev/null | grep -q '"Status": "running"'
}
case "${1:-}" in
start|stop)
exit 0
;;
status)
if is_running; then
exit 0
fi
exit 3
;;
*)
log_error "Unknown command: ${1:-}"
exit 1
;;
esac
-8
View File
@@ -1,8 +0,0 @@
#!/bin/bash
set -u
if [ "${wizard_uninstall_mode:-keep}" = "purge" ]; then
rm -rf "$TRIM_PKGVAR"
fi
exit 0
-2
View File
@@ -1,2 +0,0 @@
#!/bin/bash
exit 0
-14
View File
@@ -1,14 +0,0 @@
#!/bin/bash
set -euo pipefail
if [ -f "$TRIM_PKGVAR/havline.db" ] && [ ! -r "$TRIM_PKGVAR/havline.db" ]; then
echo "数据库不可读:$TRIM_PKGVAR/havline.db" > "$TRIM_TEMP_LOGFILE"
exit 1
fi
if [ -f "$TRIM_PKGVAR/session_secret" ] && [ ! -r "$TRIM_PKGVAR/session_secret" ]; then
echo "会话密钥不可读:$TRIM_PKGVAR/session_secret" > "$TRIM_TEMP_LOGFILE"
exit 1
fi
exit 0
-2
View File
@@ -1,2 +0,0 @@
#!/bin/bash
exit 0
-7
View File
@@ -1,7 +0,0 @@
{
"defaults": {
"run-as": "package"
},
"username": "havline",
"groupname": "havline"
}
-17
View File
@@ -1,17 +0,0 @@
{
"docker-project": {
"projects": [
{
"name": "havline",
"path": "docker"
}
]
},
"data-share": {
"shares": [
{
"name": "havline/backups"
}
]
}
}
-14
View File
@@ -1,14 +0,0 @@
appname = havline
version = 1.9.0
display_name = Havline
desc = NAS 公网访问管理:反向代理、DDNS、HTTPS 证书、FRP、Cloudflare Tunnel 与一键升级
platform = all
source = thirdparty
maintainer = Havline
maintainer_url = http://45.207.223.190:10082/mrlee/Havline
distributor = Havline
desktop_uidir = ui
desktop_applaunchname = havline.main
checkport = false
ctl_stop = true
changelog = 首发飞牛应用:支持安装向导、端口配置、数据持久化和应用中心升级
-42
View File
@@ -1,42 +0,0 @@
[
{
"stepTitle": "端口设置",
"items": [
{
"type": "text",
"field": "wizard_nginx_http_port",
"label": "HTTP 反向代理端口",
"initValue": "18080",
"rules": [
{ "required": true, "message": "请输入 HTTP 端口" },
{ "pattern": "^[0-9]+$", "message": "端口必须是数字" }
]
},
{
"type": "text",
"field": "wizard_nginx_https_port",
"label": "HTTPS 反向代理端口",
"initValue": "9443",
"rules": [
{ "required": true, "message": "请输入 HTTPS 端口" },
{ "pattern": "^[0-9]+$", "message": "端口必须是数字" }
]
}
]
},
{
"stepTitle": "基础设置",
"items": [
{
"type": "select",
"field": "wizard_timezone",
"label": "时区",
"initValue": "Asia/Shanghai",
"options": [
{ "label": "Asia/Shanghai", "value": "Asia/Shanghai" },
{ "label": "UTC", "value": "UTC" }
]
}
]
}
]
-70
View File
@@ -1,70 +0,0 @@
[
{
"stepTitle": "端口设置",
"items": [
{
"type": "text",
"field": "wizard_service_port",
"label": "Havline 管理端口",
"initValue": "6893",
"rules": [
{ "required": true, "message": "请输入管理端口" },
{ "pattern": "^[0-9]+$", "message": "端口必须是数字" }
]
},
{
"type": "text",
"field": "wizard_nginx_http_port",
"label": "HTTP 反向代理端口",
"initValue": "18080",
"rules": [
{ "required": true, "message": "请输入 HTTP 端口" },
{ "pattern": "^[0-9]+$", "message": "端口必须是数字" }
]
},
{
"type": "text",
"field": "wizard_nginx_https_port",
"label": "HTTPS 反向代理端口",
"initValue": "9443",
"rules": [
{ "required": true, "message": "请输入 HTTPS 端口" },
{ "pattern": "^[0-9]+$", "message": "端口必须是数字" }
]
}
]
},
{
"stepTitle": "管理员初始化",
"items": [
{
"type": "password",
"field": "wizard_initial_admin_password",
"label": "初始管理员密码",
"rules": [
{ "required": true, "message": "请输入初始管理员密码" },
{ "min": 8, "message": "密码至少 8 位" }
]
},
{
"type": "tips",
"helpText": "首次启动时使用该密码创建 admin 管理员。登录后请在系统设置中修改密码。"
}
]
},
{
"stepTitle": "基础设置",
"items": [
{
"type": "select",
"field": "wizard_timezone",
"label": "时区",
"initValue": "Asia/Shanghai",
"options": [
{ "label": "Asia/Shanghai", "value": "Asia/Shanghai" },
{ "label": "UTC", "value": "UTC" }
]
}
]
}
]
-21
View File
@@ -1,21 +0,0 @@
[
{
"stepTitle": "卸载选项",
"items": [
{
"type": "radio",
"field": "wizard_uninstall_mode",
"label": "数据处理",
"initValue": "keep",
"options": [
{ "label": "保留数据(推荐)", "value": "keep" },
{ "label": "删除全部数据", "value": "purge" }
]
},
{
"type": "tips",
"helpText": "删除数据将移除数据库、证书、Nginx 配置、FRP 与 Cloudflare 配置,且无法恢复。"
}
]
}
]