mirror of
https://gitee.com/amber-ce/amber-pm
synced 2025-12-18 19:31:37 +08:00
413 lines
12 KiB
Bash
Executable File
413 lines
12 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
# APM软件包转换器 - 将DEB包转换为APM格式
|
||
log.warn() { echo -e "[\e[33mWARN\e[0m]: \e[1m$*\e[0m"; }
|
||
log.error() { echo -e "[\e[31mERROR\e[0m]: \e[1m$*\e[0m"; }
|
||
log.info() { echo -e "[\e[96mINFO\e[0m]: \e[1m$*\e[0m"; }
|
||
log.debug() { echo -e "[\e[32mDEBUG\e[0m]: \e[1m$*\e[0m"; }
|
||
SCRIPT_NAME=$(basename "$0")
|
||
|
||
if ! command -v dpkg > /dev/null ;then
|
||
log.error "若想使用APM软件包转换器,您需先安装dpkg"
|
||
exit 1
|
||
fi
|
||
# 显示用法信息
|
||
usage() {
|
||
echo "用法: $SCRIPT_NAME --base <basename> [--base <basename> ...] <deb文件路径> [--pkgname <包名>] [--version <版本号>]"
|
||
echo ""
|
||
echo "参数说明:"
|
||
echo " --basename 必填参数,指定基础环境名称,可多次使用指定多个基础环境"
|
||
echo " deb文件路径 必填参数,要转换的DEB文件路径"
|
||
echo " --pkgname 可选参数,指定新包的包名(默认使用原DEB包名)"
|
||
echo " --version 可选参数,指定新包的版本号(默认在原版本后追加'-apm')"
|
||
echo ""
|
||
echo "示例:"
|
||
echo " $SCRIPT_NAME --base amber-pm-trixie /path/to/package.deb"
|
||
echo " $SCRIPT_NAME --base amber-pm-bookworm-spark-wine /path/to/package.deb --pkgname new-pkg --version 1.0.0"
|
||
echo "最下层的base在最后面,从上到下写base"
|
||
}
|
||
|
||
# 检查参数数量
|
||
if [ $# -lt 3 ]; then
|
||
log.error "错误:参数不足"
|
||
usage
|
||
exit 1
|
||
fi
|
||
|
||
# 解析参数
|
||
BASENAMES=() # 改为数组存储多个base
|
||
DEB_PATH=""
|
||
PKGNAME=""
|
||
VERSION=""
|
||
|
||
# 参数解析
|
||
while [ $# -gt 0 ]; do
|
||
case $1 in
|
||
--base)
|
||
BASENAMES+=("$2")
|
||
BASENAMES_ORIG+=("$2")
|
||
shift 2
|
||
;;
|
||
--pkgname)
|
||
PKGNAME="$2"
|
||
shift 2
|
||
;;
|
||
--version)
|
||
VERSION="$2"
|
||
shift 2
|
||
;;
|
||
*)
|
||
if [ -z "$DEB_PATH" ] && [ -f "$1" ]; then
|
||
DEB_PATH="$1"
|
||
shift
|
||
else
|
||
log.error "错误:未知参数或无效的DEB文件路径: $1"
|
||
usage
|
||
exit 1
|
||
fi
|
||
;;
|
||
esac
|
||
done
|
||
|
||
# 检查必填参数
|
||
if [ ${#BASENAMES[@]} -eq 0 ] || [ -z "$DEB_PATH" ]; then
|
||
log.error "错误:至少需要一个--basename参数,且DEB文件路径为必填"
|
||
usage
|
||
exit 1
|
||
fi
|
||
|
||
# 检查DEB文件是否存在
|
||
if [ ! -f "$DEB_PATH" ]; then
|
||
log.error "错误:DEB文件不存在: $DEB_PATH"
|
||
exit 1
|
||
fi
|
||
|
||
log.info "开始转换DEB包: $DEB_PATH"
|
||
log.info "基础环境数量: ${#BASENAMES[@]}"
|
||
for i in "${!BASENAMES[@]}"; do
|
||
log.info " 基础环境 $((i+1)): ${BASENAMES[$i]}"
|
||
done
|
||
|
||
# 1. 创建临时工作目录
|
||
CRAFT_DIR="$HOME/apm-craft-$$"
|
||
log.info "创建临时工作目录: $CRAFT_DIR"
|
||
mkdir -p "$CRAFT_DIR"/{core,work,mergedir,modified_deb}
|
||
|
||
# 设置环境变量
|
||
export CRAFT_DIR
|
||
|
||
# 检查是否已挂载,避免重复挂载
|
||
cleanup_mount() {
|
||
if mountpoint -q "$CRAFT_DIR/mergedir"; then
|
||
log.info "解除挂载: $CRAFT_DIR/mergedir"
|
||
sudo umount "$CRAFT_DIR/mergedir" || true
|
||
fi
|
||
}
|
||
|
||
# 清理函数
|
||
cleanup() {
|
||
log.info "开始清理..."
|
||
cleanup_mount
|
||
if [ -d "$CRAFT_DIR" ]; then
|
||
log.info "删除临时目录: $CRAFT_DIR"
|
||
sudo rm -rf "$CRAFT_DIR"
|
||
fi
|
||
}
|
||
|
||
# 设置退出时清理
|
||
trap cleanup EXIT
|
||
|
||
# 递归获取info文件中的依赖
|
||
get_recursive_basenames() {
|
||
local basename="$1"
|
||
local base_dir="/var/lib/apm/apm/files/ace-env/var/lib/apm/$basename"
|
||
local info_file="$base_dir/info"
|
||
|
||
if [ -f "$info_file" ]; then
|
||
log.info "读取info文件: $info_file"
|
||
while IFS= read -r base; do
|
||
# 跳过空行
|
||
[[ -z "$base" ]] && continue
|
||
# 如果依赖的base没有被记录过,则递归添加
|
||
if [[ ! " ${BASENAMES[*]} " =~ " $base " ]]; then
|
||
BASENAMES+=("$base")
|
||
# 递归获取依赖
|
||
get_recursive_basenames "$base"
|
||
fi
|
||
done < "$info_file"
|
||
else
|
||
log.info "未找到info文件,跳过: $info_file"
|
||
fi
|
||
}
|
||
|
||
# 递归获取所有基础环境
|
||
for BASE in "${BASENAMES[@]}"; do
|
||
get_recursive_basenames "$BASE"
|
||
done
|
||
|
||
# 检查DEB文件
|
||
log.info "检查原DEB包信息..."
|
||
ORIG_PKGNAME=$(dpkg -f "$DEB_PATH" Package)
|
||
ORIG_VERSION=$(dpkg -f "$DEB_PATH" Version)
|
||
ORIG_ARCH=$(dpkg -f "$DEB_PATH" Architecture)
|
||
|
||
log.info "原包名: $ORIG_PKGNAME"
|
||
log.info "原版本: $ORIG_VERSION"
|
||
log.info "原架构: $ORIG_ARCH"
|
||
|
||
# 设置新包名和版本
|
||
NEW_PKGNAME="${PKGNAME:-$ORIG_PKGNAME}"
|
||
NEW_VERSION="${VERSION:-${ORIG_VERSION}-apm}"
|
||
|
||
log.info "新包名: $NEW_PKGNAME"
|
||
log.info "新版本: $NEW_VERSION"
|
||
log.info "新架构: $ORIG_ARCH"
|
||
|
||
# 2. 构建lowerdir路径(多个base按顺序叠放)
|
||
log.info "构建overlay lowerdir路径..."
|
||
LOWERDIRS=()
|
||
|
||
# 按顺序处理每个base(从第一个到最后一个,最后一个在最底层)
|
||
for BASENAME in "${BASENAMES[@]}"; do
|
||
ACE_ENV_PATH="/var/lib/apm/apm/files/ace-env/var/lib/apm/${BASENAME}/files/ace-env"
|
||
CORE_PATH="/var/lib/apm/apm/files/ace-env/var/lib/apm/${BASENAME}/files/core"
|
||
|
||
if [ -d "$ACE_ENV_PATH" ]; then
|
||
log.info "使用ace-env路径: $ACE_ENV_PATH"
|
||
LOWERDIRS+=("$ACE_ENV_PATH")
|
||
elif [ -d "$CORE_PATH" ]; then
|
||
log.info "使用core路径: $CORE_PATH"
|
||
LOWERDIRS+=("$CORE_PATH")
|
||
else
|
||
log.error "错误:基础环境路径不存在: $BASENAME"
|
||
log.error " 检查的路径: $ACE_ENV_PATH"
|
||
log.error " 检查的路径: $CORE_PATH"
|
||
exit 1
|
||
fi
|
||
done
|
||
|
||
# 将lowerdirs数组用冒号连接
|
||
LOWERDIR=$(IFS=:; echo "${LOWERDIRS[*]}")
|
||
log.debug "最终lowerdir: $LOWERDIR"
|
||
|
||
# 4. 进行融合挂载
|
||
log.info "正在进行融合挂载..."
|
||
|
||
sudo mount -t overlay overlay \
|
||
-o "lowerdir=$LOWERDIR,upperdir=$CRAFT_DIR/core/,workdir=$CRAFT_DIR/work/" \
|
||
"$CRAFT_DIR/mergedir"
|
||
|
||
log.info "挂载完成"
|
||
|
||
# 5. 在融合环境中安装修改后的DEB包
|
||
log.info "在融合环境中测试安装DEB包..."
|
||
|
||
# 更新包列表
|
||
export chrootEnvPath="$CRAFT_DIR/mergedir"
|
||
sudo -E /var/lib/apm/apm/files/ace-run-pkg aptss update
|
||
|
||
# 首先进行dry-run检查
|
||
log.info "进行安装前检查..."
|
||
if ! sudo -E /var/lib/apm/apm/files/ace-run-pkg aptss install "$DEB_PATH" --dry-run; then
|
||
log.error "错误:安装前检查失败,DEB包可能无法在基础环境中安装"
|
||
log.error "请检查依赖关系或基础环境是否兼容"
|
||
exit 1
|
||
fi
|
||
|
||
log.info "安装前检查通过,准备实际安装..."
|
||
|
||
# 6. 提取并修改DEB包
|
||
log.info "提取并修改原DEB包..."
|
||
EXTRACT_DIR="$CRAFT_DIR/extract"
|
||
MODIFIED_DEB_DIR="$CRAFT_DIR/modified_deb"
|
||
mkdir -p "$EXTRACT_DIR"
|
||
mkdir -p "$MODIFIED_DEB_DIR/DEBIAN"
|
||
|
||
# 解压DEB包
|
||
dpkg -x "$DEB_PATH" "$EXTRACT_DIR"
|
||
dpkg -e "$DEB_PATH" "$MODIFIED_DEB_DIR/DEBIAN"
|
||
|
||
# 处理.desktop文件
|
||
DESKTOP_MODIFIED=false
|
||
find "$EXTRACT_DIR" -name "*.desktop" | while read -r desktop_file; do
|
||
log.info "处理桌面文件: $desktop_file"
|
||
busybox dos2unix $desktop_file
|
||
DESKTOP_MODIFIED=true
|
||
|
||
# 在Exec和TryExec行前追加 "apm run $NEW_PKGNAME"
|
||
# 处理Exec行
|
||
if grep -q '^Exec=' "$desktop_file"; then
|
||
sed -i 's/^Exec=\(.*\)$/Exec=apm run '"$NEW_PKGNAME"' \1/' "$desktop_file"
|
||
fi
|
||
|
||
# 处理TryExec行 - 直接删除
|
||
if grep -q '^TryExec=' "$desktop_file"; then
|
||
sed -i '/^TryExec=/d' "$desktop_file"
|
||
log.info "已删除TryExec行"
|
||
fi
|
||
|
||
# 处理Icon路径
|
||
icon_line=$(grep "^Icon=" "$desktop_file")
|
||
if [[ "$icon_line" == "Icon=/"* ]]; then
|
||
sed -i 's|^Icon=/|Icon=/var/lib/apm/apm/files/ace-env/var/lib/apm/'"$NEW_PKGNAME"'/files/core/|' "$desktop_file"
|
||
fi
|
||
|
||
# 添加X-APM-APPID
|
||
if ! grep -q "X-APM-APPID" "$desktop_file"; then
|
||
echo "X-APM-APPID=$NEW_PKGNAME" >> "$desktop_file"
|
||
fi
|
||
|
||
# 检查修改结果
|
||
if grep -q "apm run $NEW_PKGNAME" "$desktop_file"; then
|
||
log.info "桌面文件修改成功"
|
||
log.debug "修改后的Exec行: $(grep '^Exec=' "$desktop_file" || echo "未找到")"
|
||
log.debug "修改后的TryExec行: $(grep '^TryExec=' "$desktop_file" || echo "未找到")"
|
||
else
|
||
log.warn "桌面文件可能未正确修改: $desktop_file"
|
||
fi
|
||
done
|
||
|
||
if [ "$DESKTOP_MODIFIED" = false ]; then
|
||
log.info "未找到需要修改的.desktop文件"
|
||
fi
|
||
|
||
# 重新打包修改后的DEB
|
||
MODIFIED_DEB_PATH="$CRAFT_DIR/modified_${ORIG_PKGNAME}.deb"
|
||
log.info "重新打包修改后的DEB: $MODIFIED_DEB_PATH"
|
||
|
||
# 复制修改后的文件结构到打包目录
|
||
mkdir -p "$MODIFIED_DEB_DIR/data"
|
||
cp -r "$EXTRACT_DIR"/* "$MODIFIED_DEB_DIR/" 2>/dev/null || true
|
||
|
||
# 使用fakeroot重新打包
|
||
cd "$MODIFIED_DEB_DIR" && fakeroot dpkg-deb --build -Z none . "$MODIFIED_DEB_PATH"
|
||
cd - > /dev/null
|
||
|
||
if [ ! -f "$MODIFIED_DEB_PATH" ]; then
|
||
log.error "错误:重新打包DEB失败"
|
||
exit 1
|
||
fi
|
||
|
||
log.info "DEB包修改完成,新包路径: $MODIFIED_DEB_PATH"
|
||
|
||
# 实际安装修改后的DEB包
|
||
if ! sudo -E /var/lib/apm/apm/files/ace-run-pkg ssaudit "$MODIFIED_DEB_PATH" --native --no-create-desktop-entry; then
|
||
log.error "错误:修改后的DEB包安装失败"
|
||
exit 1
|
||
fi
|
||
|
||
log.info "修改后的DEB包安装完成"
|
||
|
||
# 清理一些垃圾
|
||
sudo -E /var/lib/apm/apm/files/ace-run-pkg aptss clean
|
||
sudo -E /var/lib/apm/apm/files/ace-run-pkg rm -vfr /var/lib/apt/lists
|
||
sudo -E /var/lib/apm/apm/files/ace-run-pkg rm -vfr /var/lib/aptss/lists
|
||
|
||
# 7. 创建新的APM包结构
|
||
log.info "创建新的APM包结构..."
|
||
PKG_BUILD_DIR="$CRAFT_DIR/new-pkg"
|
||
mkdir -p "$PKG_BUILD_DIR/DEBIAN"
|
||
mkdir -p "$PKG_BUILD_DIR/var/lib/apm/$NEW_PKGNAME"/{entries,files}
|
||
|
||
# 创建info文件 - 写入所有base,每行一个
|
||
log.info "创建info文件,包含输入的基础环境:"
|
||
for BASENAME in "${BASENAMES_ORIG[@]}"; do
|
||
echo "$BASENAME" >> "$PKG_BUILD_DIR/var/lib/apm/$NEW_PKGNAME/info"
|
||
log.info " 写入: $BASENAME"
|
||
done
|
||
log.info "创建info_debug文件,包含所有基础环境:"
|
||
for BASENAME in "${BASENAMES[@]}"; do
|
||
echo "$BASENAME" >> "$PKG_BUILD_DIR/var/lib/apm/$NEW_PKGNAME/info_debug"
|
||
log.info " 写入: $BASENAME"
|
||
done
|
||
|
||
# 创建postrm脚本
|
||
cat > "$PKG_BUILD_DIR/DEBIAN/postrm" << 'EOF'
|
||
#!/bin/bash
|
||
PACKAGE_NAME="$DPKG_MAINTSCRIPT_PACKAGE"
|
||
|
||
if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then
|
||
echo "清理卸载残留"
|
||
rm -rf "/var/lib/apm/$PACKAGE_NAME"
|
||
for username in $(ls /home)
|
||
do
|
||
echo /home/$username
|
||
if [ -d "/home/$username/.apm/$PACKAGE_NAME" ]
|
||
then
|
||
rm -fr "/home/$username/.apm/$PACKAGE_NAME"
|
||
fi
|
||
done
|
||
else
|
||
echo "非卸载,跳过清理"
|
||
fi
|
||
EOF
|
||
|
||
chmod +x "$PKG_BUILD_DIR/DEBIAN/postrm"
|
||
|
||
# 8. 复制文件到新的APM包
|
||
log.info "复制文件到新的APM包..."
|
||
|
||
# 复制/usr/share/内容到entries
|
||
if [ -d "$EXTRACT_DIR/usr/share" ]; then
|
||
log.info "复制/usr/share/内容..."
|
||
cp -r "$EXTRACT_DIR/usr/share/"* "$PKG_BUILD_DIR/var/lib/apm/$NEW_PKGNAME/entries/"
|
||
fi
|
||
|
||
# 复制/opt/apps/内容(如果存在)
|
||
if [ -d "$EXTRACT_DIR/opt/apps/$ORIG_PKGNAME/entries" ]; then
|
||
log.info "复制/opt/apps/$ORIG_PKGNAME/entries内容..."
|
||
cp -r "$EXTRACT_DIR/opt/apps/$ORIG_PKGNAME/entries/"* "$PKG_BUILD_DIR/var/lib/apm/$NEW_PKGNAME/entries/"
|
||
fi
|
||
|
||
# 复制融合环境文件
|
||
log.info "复制融合环境文件..."
|
||
sudo cp -r "$CRAFT_DIR"/{core,work} "$PKG_BUILD_DIR/var/lib/apm/$NEW_PKGNAME/files/"
|
||
|
||
# 设置文件权限
|
||
sudo chmod -R 755 "$PKG_BUILD_DIR/var/lib/apm/$NEW_PKGNAME/files/"
|
||
|
||
# 9. 解除挂载
|
||
log.info "解除挂载..."
|
||
cleanup_mount
|
||
|
||
# 10. 打包新的APM包
|
||
log.info "打包新的APM包..."
|
||
calculate_directory_size() {
|
||
local dir="$1"
|
||
if [ -d "$dir" ]; then
|
||
du -sk "$dir" | cut -f1
|
||
else
|
||
echo "0"
|
||
fi
|
||
}
|
||
|
||
# 构建依赖字符串 - 包含所有base
|
||
DEPENDS_STR=$(IFS=,; echo "${BASENAMES_ORIG[*]}")
|
||
|
||
# 创建control文件
|
||
cat > "${PKG_BUILD_DIR}/DEBIAN/control" << EOF
|
||
Package: $NEW_PKGNAME
|
||
Version: $NEW_VERSION
|
||
Architecture: $ORIG_ARCH
|
||
Maintainer: APM Converter <apm-convert@spark-app.store>
|
||
Depends: $DEPENDS_STR
|
||
Installed-Size: $(calculate_directory_size $PKG_BUILD_DIR)
|
||
Description: APM converted package from $ORIG_PKGNAME
|
||
This package was automatically converted from the original deb package.
|
||
Based on: ${BASENAMES_ORIG[*]}
|
||
EOF
|
||
|
||
# 生成输出文件名
|
||
OUTPUT_DEB="${NEW_PKGNAME}_${NEW_VERSION}_${ORIG_ARCH}.deb"
|
||
|
||
# 打包
|
||
fakeroot dpkg-deb -Z xz --build "$PKG_BUILD_DIR" "$OUTPUT_DEB"
|
||
|
||
log.info "转换完成!"
|
||
log.info "生成的APM包: $OUTPUT_DEB"
|
||
log.info "包名: $NEW_PKGNAME"
|
||
log.info "版本: $NEW_VERSION"
|
||
log.info "架构: $ORIG_ARCH"
|
||
log.info "依赖: $DEPENDS_STR"
|
||
log.info "基础环境: ${BASENAMES[*]}"
|
||
log.info "注意:桌面文件已修改,添加了APM运行前缀和APPID" |