From 69d9c23cffa6e2e33123ff0543e011a4138d1249 Mon Sep 17 00:00:00 2001 From: shenmo Date: Thu, 19 Feb 2026 18:35:41 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90aptss=E5=AF=B9=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- electron/main/backend/install-manager.ts | 34 +++-- extras/apm-launcher | 102 -------------- extras/app-launcher | 164 +++++++++++++++++++++++ extras/check-is-installed | 45 +++++++ extras/shell-caller.sh | 75 +++++++++-- src/components/AppDetailModal.vue | 4 +- src/components/TopActions.vue | 6 +- 7 files changed, 297 insertions(+), 133 deletions(-) delete mode 100755 extras/apm-launcher create mode 100755 extras/app-launcher create mode 100755 extras/check-is-installed diff --git a/electron/main/backend/install-manager.ts b/electron/main/backend/install-manager.ts index bed11e29..d1b791ab 100644 --- a/electron/main/backend/install-manager.ts +++ b/electron/main/backend/install-manager.ts @@ -180,7 +180,7 @@ ipcMain.on("queue-install", async (event, download_json) => { } if (metalinkUrl && filename) { - execParams.push("aptss", "ssaudit", `${downloadDir}/${filename}`); + execParams.push("ssinstall", `${downloadDir}/${filename}` , "--delete-after-install"); } else { execParams.push("aptss", "install", "-y", pkgname); } @@ -395,36 +395,34 @@ ipcMain.handle("check-installed", async (_event, pkgname: string) => { logger.warn("check-installed missing pkgname"); return false; } - let isInstalled = false; logger.info(`检查应用是否已安装: ${pkgname}`); - const child = spawn( - SHELL_CALLER_PATH, - ["aptss", "list", "--installed", pkgname], - { - shell: true, - env: process.env, - }, - ); + const checkScript = "/opt/spark-store/extras/check-is-installed"; + let isInstalled = false; - let output = ""; - - child.stdout.on("data", (data) => { - output += data.toString(); + const child = spawn(checkScript, [pkgname], { + shell: false, + env: process.env, }); await new Promise((resolve) => { + child.on("error", (err) => { + logger.error(`check-installed 执行失败: ${err?.message || err}`); + resolve(); + }); + child.on("close", (code) => { - if (code === 0 && output.includes(pkgname)) { + if (code === 0) { isInstalled = true; logger.info(`应用已安装: ${pkgname}`); } else { - logger.info(`应用未安装: ${pkgname}`); + logger.info(`应用未安装: ${pkgname} (exit ${code})`); } resolve(); }); }); + return isInstalled; }); @@ -447,7 +445,7 @@ ipcMain.on("remove-installed", async (_event, pkgname: string) => { } const child = spawn( execCommand, - [...execParams, "aptss", "remove", "-y", pkgname], + [...execParams, "aptss", "remove", pkgname ], { shell: true, env: process.env, @@ -571,7 +569,7 @@ ipcMain.handle("launch-app", async (_event, pkgname: string) => { } const execCommand = "/opt/spark-store/extras/host-spawn"; - const execParams = ["/opt/spark-store/extras/apm-launcher", "launch", pkgname]; + const execParams = ["/opt/spark-store/extras/app-launcher", "launch", pkgname]; logger.info( `Launching app: ${pkgname} with command: ${execCommand} ${execParams.join(" ")}`, diff --git a/extras/apm-launcher b/extras/apm-launcher deleted file mode 100755 index d7aafd61..00000000 --- a/extras/apm-launcher +++ /dev/null @@ -1,102 +0,0 @@ -#!/bin/bash - -# ===== 日志函数(简化版)===== -log.info() { echo "INFO: $*"; } -log.warn() { echo "WARN: $*"; } -log.error() { echo "ERROR: $*"; } -log.debug() { :; } # APM 场景下可禁用 debug 日志 - -# ===== APM 专用桌面文件扫描(单文件)===== -function scan_apm_desktop_log() { - unset desktop_file_path - local pkg_name="$1" - local desktop_dir="/var/lib/apm/apm/files/ace-env/var/lib/apm/${pkg_name}/entries/applications" - - [ -d "$desktop_dir" ] || return 1 - - while IFS= read -r -d '' path; do - [ -f "$path" ] || continue - if ! grep -q 'NoDisplay=true' "$path" 2>/dev/null; then - log.info "Found valid APM desktop file: $path" - export desktop_file_path="$path" - return 0 - fi - done < <(find "$desktop_dir" -name "*.desktop" -type f -print0 2>/dev/null) - - return 1 -} - -# ===== APM 专用桌面文件扫描(多文件列表)===== -function scan_apm_desktop_list() { - local pkg_name="$1" - local desktop_dir="/var/lib/apm/apm/files/ace-env/var/lib/apm/${pkg_name}/entries/applications" - local result="" - - [ -d "$desktop_dir" ] || { echo ""; return; } - - while IFS= read -r -d '' path; do - [ -f "$path" ] || continue - if ! grep -q 'NoDisplay=true' "$path" 2>/dev/null; then - result+="${path}," - fi - done < <(find "$desktop_dir" -name "*.desktop" -type f -print0 2>/dev/null) - - echo "${result%,}" -} - -# ===== 启动应用 ===== -function launch_app() { - local desktop_path="${1#file://}" - local exec_cmd - - # 提取并清理 Exec 行(移除字段代码如 %f %u 等) - exec_cmd=$(grep -m1 '^Exec=' "$desktop_path" | cut -d= -f2- | sed 's/%[fFuUdDnNickvm]*//g; s/^[[:space:]]*//; s/[[:space:]]*$//') - [ -z "$exec_cmd" ] && return 1 - - log.info "Launching: $exec_cmd" - ${SHELL:-bash} -c "$exec_cmd" & -} - -# 导出函数供 ACE 环境调用 -export -f launch_app scan_apm_desktop_log scan_apm_desktop_list log.info log.error - -# ===== 主逻辑 ===== -[ $# -lt 2 ] && { - log.error "Usage: $0 {check|list|launch|start} " - exit 1 -} - -action="$1" -pkg_name="$2" - -case "$action" in - check) - if scan_apm_desktop_log "$pkg_name"; then - exit 0 - else - exit 1 - fi - ;; - - list) - if result=$(scan_apm_desktop_list "$pkg_name"); [ -n "$result" ]; then - echo "$result" - exit 0 - else - exit 1 - fi - ;; - - launch|start) - if scan_apm_desktop_log "$pkg_name" && launch_app "$desktop_file_path"; then - exit 0 - else - exit 1 - fi - ;; - - *) - log.error "Invalid command: $action (supported: check|list|launch|start)" - exit 2 - ;; -esac diff --git a/extras/app-launcher b/extras/app-launcher new file mode 100755 index 00000000..0460eaef --- /dev/null +++ b/extras/app-launcher @@ -0,0 +1,164 @@ +#!/bin/bash + +# ===== ACE环境配置 ===== +readonly ACE_ENVIRONMENTS=( + "bookworm-run:amber-ce-bookworm" + "trixie-run:amber-ce-trixie" + "deepin23-run:amber-ce-deepin23" + "sid-run:amber-ce-sid" +) + +# ===== 日志和函数 ===== +[ -f /opt/durapps/spark-store/bin/bashimport/log.amber ] && \ + source /opt/durapps/spark-store/bin/bashimport/log.amber || { + log.info() { echo "INFO: $*"; } + log.warn() { echo "WARN: $*"; } + log.error() { echo "ERROR: $*"; } + log.debug() { echo "DEBUG: $*"; } +} + +# ===== 功能函数 ===== +function scan_desktop_file_log() { + unset desktop_file_path + local package_name=$1 + # 标准desktop文件检测 + while IFS= read -r path; do + [ -z "$(grep 'NoDisplay=true' "$path")" ] && { + log.info "Found valid desktop file: $path" + export desktop_file_path="$path" + return 0 + } + done < <(dpkg -L "$package_name" 2>/dev/null | grep -E '/usr/share/applications/.*\.desktop$|/opt/apps/.*/entries/applications/.*\.desktop$') + + # 深度环境特殊处理 + while IFS= read -r path; do + [ -z "$(grep 'NoDisplay=true' "$path")" ] && { + log.info "Found deepin desktop file: $path" + export desktop_file_path="$path" + return 0 + } + done < <(find /opt/apps/$package_name -path '*/entries/applications/*.desktop' 2>/dev/null) + return 1 +} + +function scan_desktop_file() { + local package_name=$1 result="" + # 标准结果收集 + while IFS= read -r path; do + [ -z "$(grep 'NoDisplay=true' "$path")" ] && result+="$path," + done < <(dpkg -L "$package_name" 2>/dev/null | grep -E '/usr/share/applications/.*\.desktop$|/opt/apps/.*/entries/applications/.*\.desktop$') + + # 深度环境补充扫描 + while IFS= read -r path; do + [ -z "$(grep 'NoDisplay=true' "$path")" ] && result+="$path," + done < <(find /opt/apps/$package_name -path '*/entries/applications/*.desktop' 2>/dev/null) + + echo "${result%,}" +} + +function launch_app() { + local DESKTOP_FILE_PATH="${1#file://}" + # 提取并净化Exec命令 + exec_command=$(grep -m1 '^Exec=' "$DESKTOP_FILE_PATH" | cut -d= -f2- | sed 's/%.//g') + [ -z "$exec_command" ] && return 1 + [ ! -z "$IS_ACE_ENV" ] && HOST_PREFIX="host-spawn" + exec_command="${HOST_PREFIX} $exec_command" + log.info "Launching: $exec_command" + ${SHELL:-bash} -c " $exec_command" & + +} + +# 导出函数以便在ACE环境中使用 +export -f launch_app scan_desktop_file scan_desktop_file_log log.info log.warn log.debug log.error + +# ===== ACE环境执行器 ===== +function ace_runner() { + local action=$1 + local target=$2 + + for ace_entry in "${ACE_ENVIRONMENTS[@]}"; do + local ace_cmd=${ace_entry%%:*} + local ace_env=${ace_entry#*:} + + if ! command -v "$ace_cmd" >/dev/null; then + log.debug "$ace_cmd not found, skipping..." + continue + fi + + log.info "Attempting in $ace_env environment..." + + case "$action" in + check) + if "$ace_cmd" scan_desktop_file_log "$target"; then + log.info "Found desktop file in $ace_env" + return 0 + fi + ;; + list) + local result + if result=$("$ace_cmd" scan_desktop_file "$target"); then + echo "$result" + return 0 + fi + ;; + launch|start) +"$ace_cmd" scan_desktop_file_log "$target" + if desktop_path=$("$ace_cmd" scan_desktop_file_log "$target"); then + log.info "Launching from $ace_env..." + "$ace_cmd" launch_app $("$ace_cmd" scan_desktop_file "$target") + return 0 + fi + ;; + esac + + log.debug "Attempt in $ace_env failed" + done + + return 1 +} + +# ===== 主逻辑 ===== +[ $# -lt 2 ] && { + log.error "Usage: $0 {check|launch|list|start} package_name/desktop_file" + exit 1 +} + +case $1 in +check) + # 当前环境检查 + if scan_desktop_file_log "$2"; then + exit 0 + else + # 非ACE环境下执行ACE环境扫描 + [ -z "$IS_ACE_ENV" ] && ace_runner check "$2" + exit $? + fi + ;; + +list) + # 当前环境列表 + if result=$(scan_desktop_file "$2"); then + echo "$result" + exit 0 + else + # 非ACE环境下执行ACE环境扫描 + [ -z "$IS_ACE_ENV" ] && ace_runner list "$2" + exit $? + fi + ;; + +launch|start) + # 当前环境启动 + if scan_desktop_file_log "$2" && launch_app "$desktop_file_path"; then + exit 0 + else + # 非ACE环境下通过ACE环境启动 + [ -z "$IS_ACE_ENV" ] && ace_runner launch "$2" + exit $? + fi + ;; +*) + log.error "Invalid command: $1" + exit 2 + ;; +esac diff --git a/extras/check-is-installed b/extras/check-is-installed new file mode 100755 index 00000000..32518154 --- /dev/null +++ b/extras/check-is-installed @@ -0,0 +1,45 @@ +#!/bin/bash +readonly ACE_ENVIRONMENTS=( + "bookworm-run:amber-ce-bookworm" + "trixie-run:amber-ce-trixie" + "deepin23-run:amber-ce-deepin23" + "sid-run:amber-ce-sid" +) +dpkg -s "$1" > /dev/null +RET="$?" +if [[ "$RET" != "0" ]] &&[[ "$IS_ACE_ENV" == "" ]];then ## 如果未在ACE环境中 + + + +for ace_entry in "${ACE_ENVIRONMENTS[@]}"; do + ace_cmd=${ace_entry%%:*} + if command -v "$ace_cmd" >/dev/null 2>&1; then + echo "----------------------------------------" + echo "正在检查 $ace_cmd 环境的安装..." + echo "----------------------------------------" + + # 在ACE环境中执行安装检测 + $ace_cmd dpkg -l | grep "^ii $1 " > /dev/null + try_run_ret="$?" + + + # 最终检测结果处理 + if [ "$try_run_ret" -eq 0 ]; then + echo "----------------------------------------" + echo "在 $ace_cmd 环境中找到了安装" + echo "----------------------------------------" + exit $try_run_ret + else + echo "----------------------------------------" + echo "在 $ace_cmd 环境中未能找到安装,继续查找" + echo "----------------------------------------" + fi + fi + done + echo "----------------------------------------" + echo "所有已安装的 ACE 环境中未能找到安装,退出" + echo "----------------------------------------" + exit "$RET" + fi +## 如果在ACE环境中或者未出错 +exit "$RET" diff --git a/extras/shell-caller.sh b/extras/shell-caller.sh index fb6ef6ba..e086273f 100755 --- a/extras/shell-caller.sh +++ b/extras/shell-caller.sh @@ -2,18 +2,77 @@ # 检查是否提供了至少一个参数 if [[ $# -eq 0 ]]; then - echo "错误:未提供命令参数。用法: $0 aptss <子命令> [参数...]" + echo "错误:未提供命令参数。用法: $0 [aptss|ssinstall] <子命令> [参数...]" exit 1 fi -# 严格验证第一个参数必须是 "aptss" -if [[ "$1" != "aptss" ]]; then - echo "拒绝执行:仅允许执行 'aptss' 命令。收到的第一个参数: '$1'" +# 获取第一个参数 +first_arg="$1" + +# 根据第一个参数决定执行哪个命令 +if [[ "$first_arg" == "ssinstall" ]]; then + # 执行 ssinstall 命令(跳过第一个参数) + /usr/bin/ssinstall "${@:2}" 2>&1 + exit_code=$? +elif [[ "$first_arg" == "aptss" ]]; then + # 检查是否为 remove 子命令(第二个参数) + if [[ "$2" == "remove" ]]; then + # 获取要卸载的软件包名称(第三个参数及以后) + packages="${@:3}" + + + # 检查可用的对话框程序 + if command -v garma &> /dev/null; then + # 使用 garma 询问确认 + garma --question \ + --title="确认卸载" \ + --text="正在准备卸载: $packages\n若这是您下达的卸载指令,请选择确认继续卸载" \ + --ok-label="确认卸载" \ + --cancel-label="取消" \ + --width=400 + + if [[ $? -eq 0 ]]; then + # 用户确认,执行卸载 + /usr/bin/aptss "${@:2}" -y 2>&1 + exit_code=$? + else + # 用户取消 + echo "操作已取消" + exit 0 + fi + elif command -v zenity &> /dev/null; then + # 使用 zenity 询问确认 + zenity --question \ + --title="确认卸载" \ + --text="正在准备卸载: $packages\n\n若这是您下达的卸载指令,请选择确认继续卸载" \ + --ok-label="确认卸载" \ + --cancel-label="取消" \ + --width=400 + + if [[ $? -eq 0 ]]; then + # 用户确认,执行卸载 + /usr/bin/aptss "${@:2}" -y 2>&1 + exit_code=$? + else + # 用户取消 + echo "操作已取消" + exit 0 + fi + else + # 既没有 garma 也没有 zenity,拒绝卸载 + echo "错误:未找到 garma 或 zenity,无法显示确认对话框。卸载操作已拒绝。" + exit 1 + fi + + else + # 非 remove 命令,直接执行 + /usr/bin/aptss "${@:2}" 2>&1 + exit_code=$? + fi +else + # 其他情况,拒绝执行 + echo "拒绝执行:仅允许执行 'aptss' 或 'ssinstall' 命令。收到的第一个参数: '$first_arg'" exit 1 fi -# 执行 aptss 命令(跳过第一个参数 "aptss") -/usr/bin/aptss "${@:2}" 2>&1 -exit_code=$? - exit $exit_code \ No newline at end of file diff --git a/src/components/AppDetailModal.vue b/src/components/AppDetailModal.vue index 7f1cebc6..a910cd08 100644 --- a/src/components/AppDetailModal.vue +++ b/src/components/AppDetailModal.vue @@ -89,7 +89,7 @@ -
首次安装 APM 后需要重启系统以在启动器中看到应用入口。可前往 @@ -100,7 +100,7 @@ >APM Releases 获取 APM。 -
+ -->
软件更新 - + -->