feat: 原生安装包 — deb/rpm/systemd + NSIS setup.exe + macOS pkg

此前交付的 tar.gz/zip 仅是解压直用包,补齐真正的平台安装包:
- Linux: .deb (dpkg-deb) 与 .rpm (rpmbuild),装 systemd 单元 + 专用
  系统账号 + /etc/webui4frpc 配置,非 root 运行 + 加固
- Windows: NSIS setup.exe,向导收集监听地址/凭据,注册卸载器 +
  防火墙放行(amd64/arm64)
- macOS: .pkg(在 Linux 上手工 xar+mkbom 组装 flat package),launchd
  开机自启 + env 配置文件(amd64/arm64)
- scripts/release.sh 纳入 build_installers.sh 阶段;README 增「安装」
  章节逐平台说明

已知限制:rpmbuild 无法在非 RHEL 主机交叉构建 aarch64(Debian rpm
只有 x86_64 构建 arch),aarch64 rpm 需在原生 ARM 机器构建;Linux/ARM
场景已由 deb-arm64 覆盖。
This commit is contained in:
JianFeeeee
2026-08-28 08:36:55 +08:00
parent 6ee901dc64
commit 15b1dcce8f
14 changed files with 974 additions and 0 deletions

187
packaging/macos/build_pkg.sh Executable file
View File

@ -0,0 +1,187 @@
#!/usr/bin/env bash
#
# 在 Linux 上手工组装 macOS .pkg (flat package)。
# macOS 的 pkgbuild/productbuild 不可用,因此用 xar + mkbom 手搓 flat package
#
# archive.xar
# ├── Distribution 安装器界面描述 (choices / title)
# └── webui4frpc.pkg/
# ├── PackageInfo 组件元数据 + postinstall 声明
# ├── Bom 文件清单 (mkbom 生成)
# ├── Payload 根文件系统 cpio.gz
# └── Scripts 安装脚本 cpio.gz
#
# 用法: build_pkg.sh <version> <arch:amd64|arm64> <binary> <outfile>
set -euo pipefail
VERSION="$1"; ARCH="$2"
# xar 与 cpio 都在临时目录里执行,输入输出必须先转成绝对路径
BINARY="$(cd "$(dirname "$3")" && pwd)/$(basename "$3")"
mkdir -p "$(dirname "$4")"
OUTFILE="$(cd "$(dirname "$4")" && pwd)/$(basename "$4")"
case "$ARCH" in
amd64) PKG_ARCH="x86_64" ;;
arm64) PKG_ARCH="arm64" ;;
*) echo "未知架构: $ARCH" >&2; exit 1 ;;
esac
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
ROOT="$WORK/root"
SCRIPTS="$WORK/scripts"
FLAT="$WORK/flat"
PKGDIR="$FLAT/webui4frpc.pkg"
mkdir -p "$ROOT/usr/local/bin" "$ROOT/usr/local/etc/webui4frpc" "$SCRIPTS" "$PKGDIR"
# ---- payload 根文件系统 ----
install -m 0755 "$BINARY" "$ROOT/usr/local/bin/webui4frpc"
cat > "$ROOT/usr/local/etc/webui4frpc/webui4frpc.env" <<'ENVEOF'
# webui4frpc runtime configuration (macOS)
# 请修改密码后再投入使用;改完执行:
# sudo launchctl kickstart -k system/com.jianfeeeee.webui4frpc
W4F_ADDR=0.0.0.0:7500
W4F_USER=admin
W4F_PASSWORD=admin
ENVEOF
chmod 0644 "$ROOT/usr/local/etc/webui4frpc/webui4frpc.env"
# ---- launchd plist ----
mkdir -p "$ROOT/Library/LaunchDaemons"
cat > "$ROOT/Library/LaunchDaemons/com.jianfeeeee.webui4frpc.plist" <<'PLISTEOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.jianfeeeee.webui4frpc</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/webui4frpc-launch</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/usr/local/var/log/webui4frpc.log</string>
<key>StandardErrorPath</key>
<string>/usr/local/var/log/webui4frpc.err.log</string>
<key>WorkingDirectory</key>
<string>/usr/local/var/lib/webui4frpc</string>
</dict>
</plist>
PLISTEOF
chmod 0644 "$ROOT/Library/LaunchDaemons/com.jianfeeeee.webui4frpc.plist"
# launchd 不读 EnvironmentFile用小包装脚本 source 配置再 exec
cat > "$ROOT/usr/local/bin/webui4frpc-launch" <<'LAUNCHEOF'
#!/bin/sh
# 读取 /usr/local/etc/webui4frpc/webui4frpc.env 后启动 webui4frpc
set -e
ENV_FILE=/usr/local/etc/webui4frpc/webui4frpc.env
W4F_ADDR=0.0.0.0:7500
W4F_USER=admin
W4F_PASSWORD=admin
[ -f "$ENV_FILE" ] && . "$ENV_FILE"
exec /usr/local/bin/webui4frpc \
-addr "$W4F_ADDR" \
-user "$W4F_USER" \
-password "$W4F_PASSWORD" \
-workdir /usr/local/var/lib/webui4frpc
LAUNCHEOF
chmod 0755 "$ROOT/usr/local/bin/webui4frpc-launch"
# ---- postinstall ----
cat > "$SCRIPTS/postinstall" <<'POSTEOF'
#!/bin/sh
set -e
mkdir -p /usr/local/var/lib/webui4frpc /usr/local/var/log
chmod 750 /usr/local/var/lib/webui4frpc
PLIST=/Library/LaunchDaemons/com.jianfeeeee.webui4frpc.plist
chown root:wheel "$PLIST"
chmod 644 "$PLIST"
# 覆盖安装时先卸载旧的
launchctl bootout system "$PLIST" 2>/dev/null || true
launchctl bootstrap system "$PLIST" 2>/dev/null || \
launchctl load -w "$PLIST" 2>/dev/null || true
exit 0
POSTEOF
chmod 0755 "$SCRIPTS/postinstall"
# ---- Payload / Scripts (cpio.gz) ----
( cd "$ROOT" && find . -print | cpio -o --format odc --owner 0:0 2>/dev/null ) \
| gzip -9 > "$PKGDIR/Payload"
( cd "$SCRIPTS" && find . -print | cpio -o --format odc --owner 0:0 2>/dev/null ) \
| gzip -9 > "$PKGDIR/Scripts"
# ---- Bom ----
mkbom -u 0 -g 80 "$ROOT" "$PKGDIR/Bom"
NFILES=$(find "$ROOT" | wc -l)
INSTALL_KB=$(du -sk "$ROOT" | cut -f1)
# ---- PackageInfo ----
cat > "$PKGDIR/PackageInfo" <<INFOEOF
<?xml version="1.0" encoding="utf-8"?>
<pkg-info format-version="2"
identifier="com.jianfeeeee.webui4frpc"
version="$VERSION"
install-location="/"
auth="root"
relocatable="false"
overwrite-permissions="true">
<payload installKBytes="$INSTALL_KB" numberOfFiles="$NFILES"/>
<scripts>
<postinstall file="./postinstall"/>
</scripts>
<bundle-version/>
</pkg-info>
INFOEOF
# ---- Distribution ----
cat > "$FLAT/Distribution" <<DISTEOF
<?xml version="1.0" encoding="utf-8"?>
<installer-gui-script minSpecVersion="1">
<title>webui4frpc $VERSION</title>
<organization>com.jianfeeeee</organization>
<options customize="never" require-scripts="false" hostArchitectures="$PKG_ARCH"/>
<domains enable_localSystem="true" enable_anywhere="false" enable_currentUserHome="false"/>
<volume-check>
<allowed-os-versions><os-version min="11.0"/></allowed-os-versions>
</volume-check>
<welcome language="zh_CN" mime-type="text/plain">webui4frpc — 可视化 frpc 控制器
安装内容:
/usr/local/bin/webui4frpc 主程序(内嵌 Web 界面)
/usr/local/etc/webui4frpc/webui4frpc.env 配置文件
/Library/LaunchDaemons/…webui4frpc.plist 开机自启服务
安装完成后服务自动启动,访问 http://127.0.0.1:7500
默认凭据 admin/admin —— 请立即修改 webui4frpc.env 中的密码。</welcome>
<choices-outline>
<line choice="default">
<line choice="com.jianfeeeee.webui4frpc"/>
</line>
</choices-outline>
<choice id="default"/>
<choice id="com.jianfeeeee.webui4frpc" visible="false">
<pkg-ref id="com.jianfeeeee.webui4frpc"/>
</choice>
<pkg-ref id="com.jianfeeeee.webui4frpc"
version="$VERSION"
onConclusion="none">#webui4frpc.pkg</pkg-ref>
</installer-gui-script>
DISTEOF
# ---- 打成 xar (Distribution 必须是第一个成员) ----
rm -f "$OUTFILE"
( cd "$FLAT" && xar --compression none -cf "$OUTFILE" Distribution webui4frpc.pkg )
echo "已生成: $OUTFILE ($(du -h "$OUTFILE" | cut -f1))"