Skill: zaneniu/uos-packager (version 2.0.0)

# UOS/deepin 应用打包规范 UOS/deepin 应用基于统信打包规范，使用 deb 格式，所有应用文件必须安装到 `/opt/apps/${appid}/` 目录下。 ## 核心规则 ### 1. 应用标识（appid） 必须使用**倒置域名**规则，支持小写字母和点号。**包名（Package）必须与 appid 完全一致，且仅支持小写字母。** ``` org.deepin.browser cn.i4.i4tools com.example.myapp ``` ### 2. 目录结构（重要） ``` ${appid}/ ← 直接作为打包根目录，不要有 data/opt 前缀 ├── DEBIAN/ # 打包根目录下的 DEBIAN/（不是 data/DEBIAN/） │ ├── control │ ├── postinst │ ├── prerm │ ├── postrm │ ├── preinst │ └── copyright # 版权文件不动 ├── opt/apps/${appid}/ ← 打包根目录下创建 opt/，不是 data/opt/ │ ├── entries/ # 资源映射到系统目录（DDE 自动处理） │ │ ├── applications/ # desktop → /usr/share/applications/ │ │ │ └── ${appid}.desktop │ │ ├── icons/ # 图标 → /usr/share/icons/ │ │ │ └── hicolor/scalable/apps/${appid}.svg │ │ └── ... │ ├── files/ # 应用主体文件（原包内容放这里） │ │ ├── bin/ │ │ ├── lib/ │ │ ├── run.sh │ │ └── resources/ │ └── info # 应用描述文件（JSON，DDE 解析后自动配置映射） ``` **⚠️ 关键教训：** - `dpkg-deb` 打包时，`DEBIAN/` 和 `opt/` 必须**直接位于打包根目录**，不能套一层 `data/` 再放 opt - 正确：`./opt/apps/${appid}/...`；错误：`./data/opt/apps/${appid}/...`（会导致文件装到 `/data/opt/...`） - 打包命令在 repack 目录的**父目录**执行：`dpkg-deb --build ${repack_dir} ${output}.deb` ### 3. info 文件（必需） info 文件是 UOS 打包的**核心文件**，由 DDE 桌面环境解析后**自动配置** entries 到系统目录的映射（无需手动写 postinst 创建 symlink）。 路径：`opt/apps/${appid}/info` ```json { "appid": "cn.i4.i4tools", "name": "i4Tools", "version": "3.09.002", "arch": ["amd64"], "permissions": { "autostart": false, "notification": true, "trayicon": true, "clipboard": true, "account": false, "bluetooth": false, "camera": false, "audio_record": false, "installed_apps": false }, "support-plugins": [], "plugins": [] } ``` **字段说明：** | 字段 | 说明 | 要求 | |------|------|------| | appid | 应用唯一标识 | 必填，必须与包名一致 | | name | 应用默认名称 | 必填 | | version | 版本号 | 必填，格式 `{MAJOR}.{MINOR}.{PATCH}`，纯数字 | | arch | 支持的架构 | 必填，支持：amd64, arm64, loongarch64, mips64el, sw_64 | | permissions | 沙箱权限 | 布尔值，默认 false | | support-plugins | 支持的插件类型 | 可选 | | plugins | 实现的插件类型 | 可选 | ### 4. Desktop Entry 文件 路径：`opt/apps/${appid}/entries/applications/${appid}.desktop` 编码：**必须 UTF-8**（其他编码会导致中文乱码） ```ini [Desktop Entry] Version=3.09.002 Type=Application Name=i4Tools Name[zh_CN]=爱思助手 Comment=苹果设备管理工具 Comment[zh_CN]=苹果设备管理工具 GenericName=i4Tools GenericName[zh_CN]=爱思助手 Exec=/opt/apps/cn.i4.i4tools/files/run.sh Icon=/opt/apps/cn.i4.i4tools/files/resources/logo.png Terminal=false Categories=utils Keywords=i4Tools;i4 StartupNotify=true ``` **必填字段：** `[Desktop Entry]`、`Name`、`Exec`、`Icon`、`Type`、`Terminal`、`StartupNotify` **Icon 路径注意：** 如果图标在 entries/ 下但 logo 资源在 files/ 下，应使用 `files/resources/logo.png` 的绝对路径，或将图标文件放入 `files/resources/` 后在 desktop 中引用。 **Categories 可选值：** | 值 | 启动器分类 | |---|-----------| | Network | 网络应用 | | Chat | 社交沟通 | | Audio | 音乐欣赏 | | AudioVideo | 视频播放 | | Graphics | 图形图像 | | Game | 游戏娱乐 | | Office | 办公学习 | | Reading | 阅读翻译 | | Development | 编程开发 | | System | 系统管理 | | utils | 工具类（未在上表时归为"其他应用"） | ### 5. 图标 矢量格式（推荐 SVG）： ``` opt/apps/${appid}/entries/icons/hicolor/scalable/apps/${appid}.svg ``` 非矢量格式（PNG，分辨率 16/24/32/48/128/256/512）： ``` opt/apps/${appid}/entries/icons/hicolor/24x24/apps/${appid}.png opt/apps/${appid}/entries/icons/hicolor/48x48/apps/${appid}.png opt/apps/${appid}/entries/icons/hicolor/128x128/apps/${appid}.png ``` ### 6. 文件系统权限 - **系统目录**：只读，不依赖其内容 - **应用数据目录**：使用 XDG 环境变量 | 环境变量 | 路径 | |----------|------| | `$XDG_DATA_HOME` | `~/.local/share` | | `$XDG_CONFIG_HOME` | `~/.config` | | `$XDG_CACHE_HOME` | `~/.cache` | 应用数据路径：`$XDG_DATA_HOME/${appid}`（例：`~/.local/share/org.deepin.browser`） **禁止直接写入 `$HOME`！** ### 7. DEBIAN 钩子脚本规范 #### 7.1 版权/归属信息不动 以下内容属于版权/归属信息，**不要修改**： - `DEBIAN/copyright` 文件 - `DEBIAN/control` 中的 `Maintainer` 字段 - 原包自带的业务逻辑（如 udev 规则内容） #### 7.2 可以修改的部分 - `DEBIAN/control` 中的 `Package`（包名，需与 appid 一致） - `DEBIAN/control` 中的 `Breaks`/`Replaces`（防冲突） - 所有维护脚本的功能逻辑（修复 bug、适配 UOS 环境） - desktop 文件、info 文件的内容 #### 7.3 rm -rf 命令规范 ```bash # ✅ 正确（带引号） rm -rf "$INSTALL_DIR/tmp" # ❌ 错误（未引用变量，重定向不受 sudo 影响） sudo echo xxx > /lib/udev/rules.d/xxx # 重定向不在 sudo 作用域 # 正确做法： printf '%s\n' "content" | sudo tee /lib/udev/rules.d/xxx > /dev/null ``` #### 7.4 sudo 与重定向 在脚本中，`sudo` **不影响重定向**（`>`、`>>`）。如果需要 sudo 写入文件，使用 `tee`： ```bash # 错误 sudo echo "rule" > /path/file # 正确 printf '%s\n' "rule" | sudo tee /path/file > /dev/null ``` #### 7.5 UOS 无 sudo 环境适配 UOS/统信环境下，普通用户可以直接写入 `/lib/udev/rules.d/` 等目录。可以通过 `/etc/os-release` 检测： ```bash if [ -f /etc/os-release ]; then . /etc/os-release case "$ID" in uos|uniontech|deepin) # UOS：直接写入，无需 sudo printf '%s\n' "$CONTENT" > /path/file ;; *) # 其他系统：使用 sudo printf '%s\n' "$CONTENT" | sudo tee /path/file > /dev/null ;; esac fi ``` #### 7.6 shellcheck 检查 ```bash sudo apt install shellcheck shellcheck DEBIAN/postinst DEBIAN/prerm DEBIAN/postrm DEBIAN/preinst ``` ## 完整打包流程 ### Step 1：分析原始 deb 包 ```bash dpkg-deb -I original.deb # 查看包信息 dpkg-deb -x original.deb /tmp/orig/ # 解压数据 dpkg-deb -e original.deb /tmp/orig/DEBIAN # 解压控制信息 ``` ### Step 2：建立 UOS 规范目录结构 ```bash APPID="cn.i4.i4tools" mkdir -p ${APPID}/opt/apps/${APPID}/{entries/applications,entries/icons/hicolor/scalable/apps,files} ``` ### Step 3：迁移文件 - **应用主体（原包 opt/ 下的内容）** → `opt/apps/${APPID}/files/` - **desktop 文件** → `opt/apps/${APPID}/entries/applications/${APPID}.desktop` - **图标** → `opt/apps/${APPID}/entries/icons/hicolor/scalable/apps/${APPID}.svg` - **创建 info 文件** → `opt/apps/${APPID}/info` ### Step 4：更新 desktop 文件 - `Exec` 路径改为 `files/` 下的实际路径 - `Icon` 路径指向 `files/resources/` 下的图标 - 确认 `StartupNotify=true` - 确保编码为 UTF-8 ### Step 5：编写/更新 DEBIAN 脚本 - `preinst`：安装前清理旧目录（如有）、写入 udev 规则（用 tee 不用 echo+重定向） - `postinst`：写入 udev 规则（UOS 检测）、触发 udev reload - `prerm`：移除 desktop 链接（如有） - `postrm`：卸载后清理 udev 规则 - **不要删除或修改 copyright 文件** ### Step 6：写 control 文件 ``` Package: ${APPID} Version: x.x.x Section: utils Priority: optional Architecture: amd64 Maintainer: <保持原样，不动> Breaks: ${APPID} Replaces: ${APPID} Description: 应用描述 ``` ### Step 7：打包 ```bash # 在 repack 目录的**父目录**执行 # 目录结构应为： # repack/ # ├── DEBIAN/ # ├── opt/ # └── usr/ （如需要） # 不要有 data/ 前缀 dpkg-deb --build repack/ output_${VERSION}_amd64.deb ``` ### Step 8：验证 ```bash dpkg-deb -I output.deb # 检查 control dpkg-deb -c output.deb | head -30 # 检查归档路径（应为 ./opt/...，不是 ./data/opt/...） sudo dpkg -i output.deb # 安装测试 dpkg -L ${APPID} | grep -E "(entries|info)" # 检查文件列表 ls /opt/apps/${APPID}/ # 确认目录存在 ls /usr/share/applications/ | grep ${APPID} # 确认 desktop 软链接 ls /usr/share/icons/.../${APPID}.svg # 确认 icon 软链接 cat /lib/udev/rules.d/38-${APPID}.rules # 确认 udev 规则 shellcheck DEBIAN/postinst DEBIAN/prerm DEBIAN/postrm DEBIAN/preinst ``` ## 支持的 CPU 架构 | 架构 | CPU 系列 | |------|---------| | amd64 | x86: 海光、兆芯、Intel、AMD | | arm64 | ARM64: 飞腾、鲲鹏、海思麒麟、瑞芯微 | | loongarch64 | 龙芯 3A5000/3B5000+ | | mips64el | 龙芯 3A4000/3A3000 及更早 | | sw_64 | 申威 CPU | ## 常见错误 | 错误 | 原因 | 解决 | |------|------|------| | 文件装到 `/data/opt/...` | 打包根目录内嵌套了 `data/` 层 | 打包时 `DEBIAN/` 和 `opt/` 直接在 repack 根目录下 | | 快捷方式不显示 | Exec 路径无效 / desktop 编码非 UTF-8 | 检查路径；保存为 UTF-8；确认 StartupNotify=true | | 图标不显示 | Icon 路径错误 | 使用 `files/resources/logo.png` 或确认 entries 下图标路径 | | DDE 未自动映射 entries | 缺少 info 文件 | 必须有 `opt/apps/${appid}/info` 文件 | | udev 规则无效 | sudo echo 重定向不生效 | 使用 `tee` 而非 `echo + 重定向` | | info 文件无效 | JSON 格式错误 | 严格 JSON 格式，注意字段名拼写 | | 中文乱码 | desktop 文件非 UTF-8 编码 | 保存为 UTF-8 编码 | | shellcheck 报错 | 脚本语法问题 | SC2024（sudo 重定向）、SC2129（多次重定向） | | 维护脚本执行失败 | 权限不够 / 路径问题 | UOS 下 `/etc/os-release` 检测判断是否需要 sudo |

path: _meta.json
content: ownerId: kn7a4eewgbq6zevf2bshnrfqrh837td4
slug: uos-packager
version: 1.0.0
publishedAt: 1773926345329

path: references/template_build.sh
content: #!/bin/bash
#
# UOS 应用创建脚手架
# 自动生成符合 UOS 打包规范的目录结构
#

set -e

APPID="${1:-}"
APPNAME="${2:-}"

if [[ -z "$APPID" || -z "$APPNAME" ]]; then
    echo "用法: $0 <appid> <appname> [version]"
    echo "示例: $0 org.example.myapp 我的应用 1.0.0"
    exit 1
fi

VERSION="${3:-1.0.0}"

# 校验 appid 格式（倒置域名）
if ! echo "$APPID" | grep -qE '^[a-z][a-z0-9]*(\.[a-z][a-z0-9]*)+$'; then
    echo "错误: appid 格式不正确，应使用倒置域名，如 org.deepin.browser"
    exit 1
fi

echo "=== 创建 UOS 应用脚手架: $APPID ==="

mkdir -p "$APPID/entries/applications"
mkdir -p "$APPID/entries/icons/hicolor/scalable/apps"
mkdir -p "$APPID/entries/icons/hicolor/128x128/apps"
mkdir -p "$APPID/entries/icons/hicolor/256x256/apps"
mkdir -p "$APPID/files/bin"
mkdir -p "$APPID/entries/doc"
mkdir -p "$APPID/entries/locale/zh_CN"
mkdir -p "$APPID/DEBIAN"

# ── info 文件 ────────────────────────────────────────────────
cat > "$APPID/info" << EOF
{
    "appid": "${APPID}",
    "name": "${APPNAME}",
    "version": "${VERSION}",
    "arch": ["amd64"],
    "permissions": {
        "autostart": false,
        "notification": false,
        "trayicon": false,
        "clipboard": false,
        "account": false,
        "bluetooth": false,
        "camera": false,
        "audio_record": false,
        "installed_apps": false
    },
    "support-plugins": [],
    "plugins": []
}
EOF

# ── desktop 文件 ────────────────────────────────────────────
cat > "$APPID/entries/applications/${APPID}.desktop" << EOF
[Desktop Entry]
Name=${APPNAME}
Name[zh_CN]=${APPNAME}
Comment=UOS Application
Comment[zh_CN]=UOS 应用
Exec=/opt/apps/${APPID}/files/bin/${APPID##*.}
Icon=${APPID}
Type=Application
Terminal=false
StartupNotify=true
Categories=Development;
EOF

# ── SVG 图标模板（占位符）───────────────────────────────────
cat > "$APPID/entries/icons/hicolor/scalable/apps/${APPID}.svg" << 'SVGEOF'
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128">
  <rect width="128" height="128" rx="16" fill="#0081FF"/>
  <text x="64" y="80" font-size="64" text-anchor="middle" fill="white" font-family="sans-serif">APP</text>
</svg>
SVGEOF

# ── PNG 占位符说明 ──────────────────────────────────────────
touch "$APPID/entries/icons/hicolor/128x128/apps/${APPID}.png"
touch "$APPID/entries/icons/hicolor/256x256/apps/${APPID}.png"

# ── 可执行脚本模板 ──────────────────────────────────────────
cat > "$APPID/files/bin/${APPID##*.}" << 'BINEOF'
#!/bin/bash
# ${APPNAME} - ${APPID}
# UOS 应用入口脚本

echo "${APPNAME} 已启动"
# 在此替换为实际的应用启动命令
# exec /opt/apps/${APPID}/files/bin/real_app "$@"
BINEOF
chmod +x "$APPID/files/bin/${APPID##*.}"

# ── debian/control ──────────────────────────────────────────
cat > "$APPID/DEBIAN/control" << EOF
Package: ${APPID}
Version: ${VERSION}
Section: utils
Priority: optional
Architecture: amd64
Maintainer: $(whoami) <$(whoami)@localhost>
Description: ${APPNAME}
EOF

# ── README ─────────────────────────────────────────────────
cat > "$APPID/README.md" << EOF
# ${APPNAME} (${APPID})

## 打包说明

- **appid**: ${APPID}
- **version**: ${VERSION}
- **架构**: amd64（修改 info 和 DEBIAN/control 可添加其他架构）

## 待完成

1. 替换 \`entries/icons/hicolor/scalable/apps/${APPID}.svg\` 为实际 SVG 图标
2. 替换 \`files/bin/${APPID##*.}\` 为实际可执行文件
3. 填写 desktop 文件的 Comment、Categories 等字段
4. 检查并编写 DEBIAN/postinst（如需要，**禁止修改系统文件**）
5. 执行 \`./build_deb.sh ${APPID} ${VERSION}\` 生成 deb 包

## 目录结构

\`\`\`
${APPID}/
├── entries/
│   ├── applications/${APPID}.desktop
│   ├── icons/
│   │   └── hicolor/
│   │       ├── scalable/apps/${APPID}.svg   ← 替换为实际图标
│   │       ├── 128x128/apps/${APPID}.png
│   │       └── 256x256/apps/${APPID}.png
│   └── doc/
├── files/
│   └── bin/${APPID##*.}                     ← 替换为实际程序
├── info                                      ← 应用描述
├── DEBIAN/control
└── README.md
\`\`\`
EOF

echo ""
echo "✅ 脚手架创建完成: $APPID/"
echo ""
echo "下一步:"
echo "  1. 替换图标和可执行文件"
echo "  2. 编辑 $APPID/entries/applications/${APPID}.desktop"
echo "  3. 修改 $APPID/info 中的架构等字段"
echo "  4. 运行 ./build_deb.sh ${APPID} ${VERSION} 生成 deb"
echo ""
ls -la "$APPID/"


path: scripts/build_deb.sh
content: #!/bin/bash
#
# UOS 应用打包脚本
# 用法: ./build_deb.sh <appid> <version> [arch1,arch2]
# 示例: ./build_deb.sh org.example.myapp 1.0.0 amd64
#

set -e

APPID="${1:-}"
VERSION="${2:-}"
ARCHS="${3:-amd64}"

if [[ -z "$APPID" || -z "$VERSION" ]]; then
    echo "用法: $0 <appid> <version> [arch1,arch2...]"
    echo "示例: $0 org.example.myapp 1.0.0 amd64,arm64"
    exit 1
fi

BUILD_DIR="/tmp/uos_build_$$"
mkdir -p "$BUILD_DIR"

echo "=== UOS 应用打包工具 ==="
echo "  appid:   $APPID"
echo "  version: $VERSION"
echo "  archs:   $ARCHS"
echo "  输出:    $BUILD_DIR"
echo ""

# 解析多架构
IFS=',' read -ra ARCH_ARRAY <<< "$ARCHS"

for ARCH in "${ARCH_ARRAY[@]}"; do
    ARCH=$(echo "$ARCH" | tr -d ' ')
    PKG_DIR="$BUILD_DIR/${APPID}_${VERSION}_${ARCH}"
    
    echo "--- 打包 $ARCH ---"
    
    # 创建目录结构
    mkdir -p "$PKG_DIR/DEBIAN"
    mkdir -p "$PKG_DIR/opt/apps/${APPID}/entries/applications"
    mkdir -p "$PKG_DIR/opt/apps/${APPID}/entries/icons/hicolor/scalable/apps"
    mkdir -p "$PKG_DIR/opt/apps/${APPID}/files/bin"
    
    # 检查 info 文件
    if [[ ! -f "opt/apps/${APPID}/info" ]]; then
        echo "⚠ 警告: ${APPID}/opt/apps/${APPID}/info 不存在，跳过此架构"
        continue
    fi
    
    # 复制应用目录
    if [[ -d "$APPID/entries" ]]; then
        cp -r "$APPID/entries" "$PKG_DIR/opt/apps/${APPID}/"
    fi
    if [[ -d "$APPID/files" ]]; then
        cp -r "$APPID/files" "$PKG_DIR/opt/apps/${APPID}/"
    fi
    if [[ -f "$APPID/info" ]]; then
        cp "$APPID/info" "$PKG_DIR/opt/apps/${APPID}/"
    fi
    
    # 生成 debian/control
    cat > "$PKG_DIR/DEBIAN/control" << EOF
Package: ${APPID}
Version: ${VERSION}
Section: utils
Priority: optional
Architecture: ${ARCH}
Maintainer: $(whoami) <$(whoami)@localhost>
Description: UOS Application
EOF

    # 复制 desktop 文件（从 entries 或创建空的）
    if [[ -f "$APPID/entries/applications/${APPID}.desktop" ]]; then
        cp "$APPID/entries/applications/${APPID}.desktop" \
           "$PKG_DIR/DEBIAN/${APPID}.desktop"
    fi
    
    # 检查钩子脚本
    for hook in postinst postrm preinst prerm; do
        if [[ -f "$APPID/DEBIAN/$hook" ]]; then
            cp "$APPID/DEBIAN/$hook" "$PKG_DIR/DEBIAN/$hook"
            echo "⚠ 检测到 $hook，已复制（请确保符合规范）"
            # 检查 rm -rf 用法
            if grep -q 'rm -rf.*\$[A-Z_]' "$PKG_DIR/DEBIAN/$hook"; then
                echo "⚠ 警告: $hook 中 rm -rf 使用了变量，请确保加双引号"
            fi
        fi
    done
    
    # 打包
    DEB_FILE="${APPID}_${VERSION}_${ARCH}.deb"
    dpkg-deb --build "$PKG_DIR" "$DEB_FILE" 2>/dev/null || \
        fakeroot dpkg-deb --build "$PKG_DIR" "$DEB_FILE"
    
    echo "✅ 生成: $DEB_FILE"
    echo ""
done

echo "=== 打包完成 ==="
ls -lh "${APPID}"_*.deb 2>/dev/null || true
echo "输出目录: $BUILD_DIR"
echo ""
echo "提示: 安装测试"
echo "  sudo dpkg -i ${APPID}_${VERSION}_${ARCH_ARRAY[0]}.deb"
echo "  # 或在 UOS 桌面上双击 deb 文件安装"


path: scripts/introspect.sh
content: #!/bin/bash
#
# UOS deb 包检查脚本
# 检查 deb 包是否符合 UOS 打包规范
#

set -e

DEB_FILE="${1:-}"

if [[ -z "$DEB_FILE" ]]; then
    echo "用法: $0 <xxx.deb>"
    exit 1
fi

if [[ ! -f "$DEB_FILE" ]]; then
    echo "错误: 文件不存在: $DEB_FILE"
    exit 1
fi

WORK_DIR="/tmp/uos_inspect_$$"
mkdir -p "$WORK_DIR"

echo "=== UOS deb 包检查: $DEB_FILE ==="
echo ""

dpkg-deb -e "$DEB_FILE" "$WORK_DIR/DEBIAN" 2>/dev/null || true
dpkg-deb -x "$DEB_FILE" "$WORK_DIR/rootfs" 2>/dev/null || true

ERRORS=0
WARNINGS=0

# ── 1. 检查 appid 格式 ───────────────────────────────────
INFO_FILE="$WORK_DIR/rootfs/opt/apps/"*/info 2>/dev/null || INFO_FILE=""
if [[ -f "$INFO_FILE" ]]; then
    APPID=$(grep -oP '"appid"\s*:\s*"\K[^"]+' "$INFO_FILE" | head -1)
    echo "1. appid: $APPID"
    if ! echo "$APPID" | grep -qE '^[a-z][a-z0-9]*(\.[a-z][a-z0-9]*)+$'; then
        echo "   ❌ appid 不符合倒置域名规范"
        ((ERRORS++))
    else
        echo "   ✅ 格式正确"
    fi
    
    # ── 2. 检查 info 版本格式 ─────────────────────────────
    VERSION=$(grep -oP '"version"\s*:\s*"\K[^"]+' "$INFO_FILE" | head -1)
    echo "2. version: $VERSION"
    if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'; then
        echo "   ❌ version 格式错误，应为 {MAJOR}.{MINOR}.{PATCH}.{BUILD}（纯数字）"
        ((ERRORS++))
    else
        echo "   ✅ 格式正确"
    fi
    
    # ── 3. 检查 arch ────────────────────────────────────
    ARCH=$(grep -oP '"arch"\s*:\s*\[\K[^\]]+' "$INFO_FILE" | head -1)
    echo "3. arch: [$ARCH]"
    VALID_ARCHS="amd64 arm64 loongarch64 mips64el sw_64"
    for a in $(echo "$ARCH" | tr ',:' ' ' | tr -d '"'); do
        if echo "$VALID_ARCHS" | grep -qw "$a"; then
            echo "   ✅ $a 是支持的架构"
        else
            echo "   ❌ $a 是不支持的架构"
            ((ERRORS++))
        fi
    done
else
    echo "❌ 未找到 info 文件"
    ((ERRORS++))
fi

echo ""

# ── 4. 检查 desktop 文件 ──────────────────────────────────
DESKTOP_FILE="$WORK_DIR/rootfs/usr/share/applications/"*.desktop 2>/dev/null || DESKTOP_FILE=""
if [[ -f "$DESKTOP_FILE" ]]; then
    echo "4. desktop 文件: $(basename $DESKTOP_FILE)"
    
    # 检查编码
    ENCODING=$(file -b --mime-encoding "$DESKTOP_FILE" 2>/dev/null || echo "unknown")
    if [[ "$ENCODING" == "utf-8" ]]; then
        echo "   ✅ UTF-8 编码正确"
    else
        echo "   ❌ 编码是 $ENCODING，必须为 UTF-8"
        ((ERRORS++))
    fi
    
    # 检查必填字段
    for field in Name Exec Icon Type Terminal StartupNotify; do
        if grep -q "^$field=" "$DESKTOP_FILE"; then
            echo "   ✅ $field 已填写"
        else
            echo "   ❌ 缺少必填字段: $field"
            ((ERRORS++))
        fi
    done
else
    echo "4. desktop 文件: 未找到（不会显示在启动器中）"
    ((WARNINGS++))
fi

echo ""

# ── 5. 检查安装路径 ──────────────────────────────────────
OPT_APPS_DIR="$WORK_DIR/rootfs/opt/apps"
if [[ -d "$OPT_APPS_DIR" ]]; then
    echo "5. 安装路径: ✅ 符合 /opt/apps 规范"
    ls "$OPT_APPS_DIR/" | while read d; do
        echo "   - /opt/apps/$d"
    done
else
    echo "5. 安装路径: ❌ 未找到 /opt/apps/"
    ((ERRORS++))
fi

echo ""

# ── 6. 检查钩子脚本 rm -rf 规范 ──────────────────────────
for hook in postinst postrm preinst prerm; do
    HOOK_FILE="$WORK_DIR/DEBIAN/$hook"
    if [[ -f "$HOOK_FILE" ]]; then
        echo "6. 检测到 $hook:"
        
        # 检查危险的 rm -rf 用法
        if grep -n 'rm\s*-rf\s\+[$]' "$HOOK_FILE" | grep -v '".*\$' | grep -v '"\$[^"]*"' > /dev/null; then
            echo "   ⚠ 警告: $hook 中有未加引号的 rm -rf 变量"
            ((WARNINGS++))
        fi
        
        if grep -qE 'rm\s+-rf\s+/\s*\$' "$HOOK_FILE"; then
            echo "   ❌ 致命: $hook 中有递归删除根目录的操作"
            ((ERRORS++))
        fi
        
        if grep -qE 'rm\s+-rf\s+/\*' "$HOOK_FILE"; then
            echo "   ❌ 致命: $hook 中有删除系统目录的操作"
            ((ERRORS++))
        fi
        
        # shellcheck 检查
        if command -v shellcheck &>/dev/null; then
            if ! shellcheck "$HOOK_FILE" > /dev/null 2>&1; then
                echo "   ⚠ shellcheck 发现问题:"
                shellcheck "$HOOK_FILE" | head -3
                ((WARNINGS++))
            else
                echo "   ✅ shellcheck 通过"
            fi
        else
            echo "   ℹ 未安装 shellcheck，跳过语法检查（建议安装: sudo apt install shellcheck）"
        fi
    fi
done

echo ""

# ── 7. 检查文件权限 ───────────────────────────────────────
echo "7. 文件权限检查:"
BIN_FILE="$WORK_DIR/rootfs/opt/apps/"*/files/bin/* 2>/dev/null || true
if [[ -x "$BIN_FILE" ]]; then
    echo "   ✅ 可执行文件权限正确"
else
    echo "   ℹ 未找到可执行文件或未设置执行权限"
fi

echo ""
echo "=== 检查结果 ==="
echo "错误: $ERRORS"
echo "警告: $WARNINGS"

rm -rf "$WORK_DIR"

if [[ $ERRORS -gt 0 ]]; then
    echo ""
    echo "❌ 检查未通过，请修复上述错误"
    exit 1
elif [[ $WARNINGS -gt 0 ]]; then
    echo ""
    echo "⚠ 检查通过但有警告"
    exit 0
else
    echo ""
    echo "✅ 检查通过"
    exit 0
fi


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)