#!/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 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" # Startup config. The service reads this file via `-config`, so the admin # password never appears on the command line (visible in `ps` to every user). # Set the password in /usr/local/etc/webui4frpc/secret/password instead of # writing it here; the "file:" reference below reads its first line. mkdir -p "$ROOT/usr/local/etc/webui4frpc/secret" cat > "$ROOT/usr/local/etc/webui4frpc/config.json" <<'CFGEOF' { "_comment": "precedence: command-line flag > this file > built-in default", "addr": "0.0.0.0:7500", "user": "admin", "password": "file:/usr/local/etc/webui4frpc/secret/password", "workdir": "/usr/local/var/lib/webui4frpc" } CFGEOF chmod 0644 "$ROOT/usr/local/etc/webui4frpc/config.json" # Placeholder so the service starts out of the box (admin/admin). The # postinstall script removes it if the file was already customised. printf 'admin\n' > "$ROOT/usr/local/etc/webui4frpc/secret/password" chmod 0640 "$ROOT/usr/local/etc/webui4frpc/secret/password" # ---- launchd plist ---- mkdir -p "$ROOT/Library/LaunchDaemons" cat > "$ROOT/Library/LaunchDaemons/com.jianfeeeee.webui4frpc.plist" <<'PLISTEOF' Label com.jianfeeeee.webui4frpc ProgramArguments /usr/local/bin/webui4frpc-launch RunAtLoad KeepAlive StandardOutPath /usr/local/var/log/webui4frpc.log StandardErrorPath /usr/local/var/log/webui4frpc.err.log WorkingDirectory /usr/local/var/lib/webui4frpc PLISTEOF chmod 0644 "$ROOT/Library/LaunchDaemons/com.jianfeeeee.webui4frpc.plist" # launchd 不读 EnvironmentFile,用小包装脚本 source 配置再 exec cat > "$ROOT/usr/local/bin/webui4frpc-launch" <<'LAUNCHEOF' #!/bin/sh # Start webui4frpc from /usr/local/etc/webui4frpc/config.json. # launchd does not read an EnvironmentFile, so the optional env file is sourced # here first (config.json may reference ${W4F_*}), then the config file drives # everything. No credential is ever passed on this command line. set -e ENV_FILE=/usr/local/etc/webui4frpc/webui4frpc.env CONFIG_FILE=/usr/local/etc/webui4frpc/config.json [ -f "$ENV_FILE" ] && . "$ENV_FILE" exec /usr/local/bin/webui4frpc -config "$CONFIG_FILE" 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 # ---- Distribution ---- cat > "$FLAT/Distribution" < webui4frpc $VERSION com.jianfeeeee webui4frpc — 可视化 frpc 控制器 安装内容: /usr/local/bin/webui4frpc 主程序(内嵌 Web 界面) /usr/local/etc/webui4frpc/config.json 启动配置(监听地址/账号/对端) /usr/local/etc/webui4frpc/secret/password 口令文件(file: 引用,不进命令行) /usr/local/etc/webui4frpc/webui4frpc.env 可选环境变量(config.json 里可写 \${VAR}) /Library/LaunchDaemons/…webui4frpc.plist 开机自启服务 安装完成后服务自动启动,访问 http://127.0.0.1:7500 默认凭据 admin/admin —— 请立即修改 /usr/local/etc/webui4frpc/secret/password 后再重启 (sudo launchctl kickstart -k system/com.jianfeeeee.webui4frpc)。 #webui4frpc.pkg DISTEOF # ---- 打成 xar (Distribution 必须是第一个成员) ---- rm -f "$OUTFILE" ( cd "$FLAT" && xar --compression none -cf "$OUTFILE" Distribution webui4frpc.pkg ) echo "已生成: $OUTFILE ($(du -h "$OUTFILE" | cut -f1))"