Skip to content

feat(uninstall): Add support for APM package uninstallation#59

Open
shenmo7192 wants to merge 1 commit into
linuxdeepin:masterfrom
shenmo7192:master
Open

feat(uninstall): Add support for APM package uninstallation#59
shenmo7192 wants to merge 1 commit into
linuxdeepin:masterfrom
shenmo7192:master

Conversation

@shenmo7192

@shenmo7192 shenmo7192 commented Mar 22, 2026

Copy link
Copy Markdown

Add APM package uninstallation functionality, including:

  1. Add APM uninstallation permissions in polkit rules
  2. Add new APM uninstallation script
  3. Add APM type to PackageType enum
  4. Implement APM package uninstallation logic
  5. Update CMakeLists installation configuration

Summary by Sourcery

Add support for uninstalling APM-packaged applications through the application wizard.

New Features:

  • Introduce APM as a new package type in the uninstallation flow and handle APM package removal when X-APM-APPID is present in a desktop file.
  • Add a dedicated APM uninstallation script that parses the desktop file to obtain the APM app ID and calls apm autoremove with elevated privileges.

Enhancements:

  • Wire APM uninstallation into the existing D-Bus launcher compatibility layer and post-uninstall cleanup logic.
  • Install the new APM uninstaller script and update related polkit configuration to grant appropriate uninstallation permissions.

Build:

  • Update CMake installation rules to deploy the new APM uninstaller script alongside existing uninstaller helpers.

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: shenmo7192

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@deepin-ci-robot

Copy link
Copy Markdown

Hi @shenmo7192. Thanks for your PR. 😃

@deepin-ci-robot

Copy link
Copy Markdown

Hi @shenmo7192. Thanks for your PR.

I'm waiting for a linuxdeepin member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@sourcery-ai

sourcery-ai Bot commented Mar 22, 2026

Copy link
Copy Markdown

Reviewer's Guide

Implements APM package uninstallation support by wiring a new APM-specific uninstall path into the D-Bus launcher, adding an APM package type, installing a new privileged uninstall script, and updating polkit/CMake configuration accordingly.

Sequence diagram for APM package uninstallation flow

sequenceDiagram
    actor User
    participant LauncherUI
    participant Launcher1Compat
    participant pkexec
    participant dde_appwiz_apm_uninstaller_sh
    participant apm

    User->>LauncherUI: Click_uninstall_on_APM_app
    LauncherUI->>Launcher1Compat: RequestUninstall(desktop, skipPreinstallHook)
    Launcher1Compat->>Launcher1Compat: Parse_desktop_file
    Launcher1Compat->>Launcher1Compat: Detect_X_APM_APPID
    Launcher1Compat->>Launcher1Compat: m_packageDisplayName = ddeDisplayName()
    Launcher1Compat->>dde_appwiz_apm_uninstaller_sh: start_via_pkexec("/usr/libexec/dde-appwiz-apm-uninstaller.sh", desktopFilePath)
    activate pkexec
    pkexec->>dde_appwiz_apm_uninstaller_sh: Run_with_elevated_privileges
    deactivate pkexec

    activate dde_appwiz_apm_uninstaller_sh
    dde_appwiz_apm_uninstaller_sh->>dde_appwiz_apm_uninstaller_sh: Validate_desktop_file_and_extract_APPID
    dde_appwiz_apm_uninstaller_sh->>apm: apm_autoremove(APPID,_y)
    apm-->>dde_appwiz_apm_uninstaller_sh: Exit_status
    deactivate dde_appwiz_apm_uninstaller_sh

    dde_appwiz_apm_uninstaller_sh-->>Launcher1Compat: Exit_code_and_output
    alt Uninstall_success
        Launcher1Compat->>Launcher1Compat: sendNotification(m_packageDisplayName,true,m_base64Icon)
        Launcher1Compat->>Launcher1Compat: postUninstallCleanUp(fileName,PackageType_APM)
        Launcher1Compat-->>LauncherUI: UninstallSuccess(desktopFilePath)
    else Uninstall_failure
        Launcher1Compat->>Launcher1Compat: sendNotification(m_packageDisplayName,false,m_base64Icon)
        Launcher1Compat-->>LauncherUI: UninstallFailure(desktopFilePath)
    end
Loading

Updated class diagram for Launcher1Compat and PackageType with APM support

classDiagram
    class PackageType {
        <<enumeration>>
        Linglong
        Flatpak
        Deb
        DCM
        APM
    }

    class Launcher1Compat {
        +RequestUninstall(desktop, skipPreinstallHook) void
        -uninstallPackageByScript(pkgDisplayName, packageDesktopFilePath) void
        -uninstallAPMPackage(pkgDisplayName, packageDesktopFilePath) void
        -sendNotification(pkgDisplayName, success, base64Icon) void
        -postUninstallCleanUp(desktopId, packageType) void
        -m_packageDisplayName QString
        -m_base64Icon QString
        -m_desktopFilePath QString
    }

    Launcher1Compat --> PackageType : uses
Loading

File-Level Changes

Change Details Files
Add APM-specific uninstall flow in the D-Bus launcher
  • Introduce uninstallAPMPackage method in Launcher1Compat that invokes the APM uninstaller script via pkexec, logs output, and sends success/failure notifications
  • Update RequestUninstall to detect APM apps via X-APM-APPID desktop entry key and route them to the new uninstallAPMPackage flow
  • Set m_packageDisplayName earlier in RequestUninstall to reuse it for APM and other uninstall paths
dbus/launcher1compat.cpp
dbus/launcher1compat.h
Extend package type enum for APM support
  • Add APM entry to PackageType enum so post-uninstall cleanup can distinguish APM packages
dbus/launcher1compat.h
Install new APM uninstaller script and wire it into build/install system
  • Add installation rule for dde-appwiz-apm-uninstaller.sh to libexec directory in CMakeLists
  • Create dde-appwiz-apm-uninstaller.sh script to validate the desktop file, extract X-APM-APPID, and run apm autoremove with privilege escalation via pkexec when needed
  • Update polkit policy and rules files to grant permission for the new APM uninstall command (contents not shown in diff)
CMakeLists.txt
scripts/dde-appwiz-apm-uninstaller.sh
polkit-1/actions/org.deepin.dde.appwiz.uninstall.policy
polkit-1/rules.d/org.deepin.dde.application-wizard.rules

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues, and left some high level feedback:

  • The new uninstallAPMPackage path layers pkexec in C++ on top of a script that may also elevate, which is both redundant and makes control flow harder to reason about; consider consolidating privilege escalation to either the C++ caller or the script and passing arguments to pkexec without going through an extra shell where possible.
  • In uninstallAPMPackage you call postUninstallCleanUp(fi.fileName(), PackageType::APM) with a known // FIXME: THIS IS NOT DESKTOP ID comment; it would be better to resolve the correct desktop ID (or explicitly document why this is acceptable for APM) before relying on this in the new uninstall path.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `uninstallAPMPackage` path layers `pkexec` in C++ on top of a script that may also elevate, which is both redundant and makes control flow harder to reason about; consider consolidating privilege escalation to either the C++ caller or the script and passing arguments to `pkexec` without going through an extra shell where possible.
- In `uninstallAPMPackage` you call `postUninstallCleanUp(fi.fileName(), PackageType::APM)` with a known `// FIXME: THIS IS NOT DESKTOP ID` comment; it would be better to resolve the correct desktop ID (or explicitly document why this is acceptable for APM) before relying on this in the new uninstall path.

## Individual Comments

### Comment 1
<location path="dbus/launcher1compat.cpp" line_range="183-189" />
<code_context>
     }
 }

+void Launcher1Compat::uninstallAPMPackage(const QString & pkgDisplayName, const QString & packageDesktopFilePath)
+{
+    // call `/usr/libexec/dde-appwiz-apm-uninstaller.sh <packageDesktopFilePath>` and check the return code.
+    qDebug() << "Calling dde-appwiz-apm-uninstaller.sh to uninstall" << pkgDisplayName << packageDesktopFilePath << "via script";
+    QProcess process;
+    process.start("pkexec", QStringList{"/usr/libexec/dde-appwiz-apm-uninstaller.sh", packageDesktopFilePath});
+    process.waitForFinished();
+
+    QString standardOutput = process.readAllStandardOutput();
</code_context>
<issue_to_address>
**issue (bug_risk):** Consider using a timeout and checking the return value of waitForFinished to avoid potential hangs.

Without a timeout or checking the return value, `waitForFinished()` can block this thread indefinitely if `pkexec` or the script hangs. Please either use `waitForFinished(ms)` and handle the `false` case, or at least check the boolean return and treat an unfinished process as a failure when determining which notification to send.
</issue_to_address>

### Comment 2
<location path="dbus/launcher1compat.cpp" line_range="199-200" />
<code_context>
+    if (process.exitCode() != 0) {
+        sendNotification(pkgDisplayName, false, m_base64Icon);
+    } else {
+        sendNotification(pkgDisplayName, true, m_base64Icon);
+        QFileInfo fi(m_desktopFilePath);
+        // FIXME: THIS IS NOT DESKTOP ID
+        postUninstallCleanUp(fi.fileName(), PackageType::APM);
</code_context>
<issue_to_address>
**issue (bug_risk):** Using m_desktopFilePath instead of the function parameter can cause incorrect cleanup.

In `uninstallAPMPackage`, `packageDesktopFilePath` is passed in but `QFileInfo` is built from `m_desktopFilePath`. If `m_desktopFilePath` is stale or differs from the argument, `postUninstallCleanUp` could operate on the wrong desktop file. Construct `QFileInfo` from `packageDesktopFilePath` instead so cleanup is correctly scoped to the function input.
</issue_to_address>

### Comment 3
<location path="scripts/dde-appwiz-apm-uninstaller.sh" line_range="39-47" />
<code_context>
+echo "找到 APM ID: $APPID"
+
+# 构建命令
+CMD="apm autoremove $APPID -y"
+
+# 如果不是root用户,则使用pkexec
+if [ "$EUID" -ne 0 ]; then
+    echo "当前不是root用户,使用 pkexec 提升权限..."
+    exec pkexec bash -c "$CMD"
+else
+    echo "以root用户身份执行命令..."
+    exec $CMD
+fi
\ No newline at end of file
</code_context>
<issue_to_address>
**🚨 suggestion (security):** Avoid building a single shell command string when APPID is user- or file-derived.

Building `CMD="apm autoremove $APPID -y"` and then calling `exec $CMD`/`exec pkexec bash -c "$CMD"` depends on word-splitting and lets unexpected characters in `APPID` alter the command. Instead, pass arguments directly to `exec` and quote `APPID`:

```bash
if [ "$EUID" -ne 0 ]; then
    exec pkexec apm autoremove "$APPID" -y
else
    exec apm autoremove "$APPID" -y
fi
```

This avoids `bash -c` and reduces the risk of command/argument injection.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread dbus/launcher1compat.cpp Outdated
Comment thread dbus/launcher1compat.cpp Outdated
Comment thread scripts/dde-appwiz-apm-uninstaller.sh Outdated
@shenmo7192
shenmo7192 force-pushed the master branch 3 times, most recently from 424225a to 65718b9 Compare March 22, 2026 13:22
- Add APM uninstallation permissions in polkit rules
- Add new APM uninstallation script
- Add APM type to PackageType enum
- Implement APM package uninstallation logic
- Update CMakeLists installation configuration

@BLumia BLumia left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这些 desktop 文件是完全由 APM 管理的吗?是否可以使用 X-Deepin-PreUninstall 字段?

这个字段的含义是:通常指向一个 shell script,卸载前会先执行这个字段的脚本或命令,如果返回 0 则会继续执行原有卸载行为,返回非 0 不会进行 DDE 的卸载行为。

如果这些 desktop 完全是 APM 管理的话,直接使用这个字段指向 APM 的卸载脚本就可以了,只需要确保能正常卸载并且返回非 0 值即可。

下面有一些预留的返回值含义,是兼容模式在使用的:

  • 0:正常执行了卸载前钩子,需要继续进行后续的常规卸载行为
  • 101:用户在卸载前钩子阶段取消了卸载(比如卸载前钩子脚本中弹对话框提示确认,用户点了取消)
  • 103:已有(由钩子一侧维护的)卸载行为正在进行,拒绝进行当前卸载

@shenmo7192

Copy link
Copy Markdown
Author

这些 desktop 文件是完全由 APM 管理的吗?是否可以使用 X-Deepin-PreUninstall 字段?

这个字段的含义是:通常指向一个 shell script,卸载前会先执行这个字段的脚本或命令,如果返回 0 则会继续执行原有卸载行为,返回非 0 不会进行 DDE 的卸载行为。

如果这些 desktop 完全是 APM 管理的话,直接使用这个字段指向 APM 的卸载脚本就可以了,只需要确保能正常卸载并且返回非 0 值即可。

下面有一些预留的返回值含义,是兼容模式在使用的:

* 0:正常执行了卸载前钩子,需要继续进行后续的常规卸载行为

* 101:用户在卸载前钩子阶段取消了卸载(比如卸载前钩子脚本中弹对话框提示确认,用户点了取消)

* 103:已有(由钩子一侧维护的)卸载行为正在进行,拒绝进行当前卸载

这些desktop是由APM维护的,在打包时就已经维护好了,目前 APM 并不支持主动修改软件包的desktop文件。

X-Deepin-PreUninstall 听起来像是由DDE维护的字段,由 APM 直接编辑不太合适吧....

APM本身只包含bash和一些挂载工具,并不提供对话框,如果要做额外的确认的话,估计要用zenity了,这可能会降低用户体验一致性

@BLumia

BLumia commented Mar 25, 2026

Copy link
Copy Markdown
Member

建议使用这个字段是单纯因为这个字段是 DDE 产品需求的一部分,不会轻易移除。APM 的卸载功能假定能合入也不会是产品需求的一部分,不会确保会不会哪天相关代码由别的组/团队维护后因为完全不清楚这个非需求文档描述的需求的事情而被移除。

X-Deepin-PreUninstall 听起来像是由DDE维护的字段,由 APM 直接编辑不太合适吧....

是表示由 DDE 提供支持的字段,或者说是 DDE 的扩展字段。值应当由有使用此扩展字段的一方主动提供。APM 是可以用的。

APM本身只包含bash和一些挂载工具,并不提供对话框,如果要做额外的确认的话,估计要用zenity了,这可能会降低用户体验一致性

和是否需要提供对话框没关系,上面提到只是因为兼容模式自己提供了。

@shenmo7192

Copy link
Copy Markdown
Author

建议使用这个字段是单纯因为这个字段是 DDE 产品需求的一部分,不会轻易移除。APM 的卸载功能假定能合入也不会是产品需求的一部分,不会确保会不会哪天相关代码由别的组/团队维护后因为完全不清楚这个非需求文档描述的需求的事情而被移除。

X-Deepin-PreUninstall 听起来像是由DDE维护的字段,由 APM 直接编辑不太合适吧....

是表示由 DDE 提供支持的字段,或者说是 DDE 的扩展字段。值应当由有使用此扩展字段的一方主动提供。APM 是可以用的。

APM本身只包含bash和一些挂载工具,并不提供对话框,如果要做额外的确认的话,估计要用zenity了,这可能会降低用户体验一致性

和是否需要提供对话框没关系,上面提到只是因为兼容模式自己提供了。

这个钩子的格式是什么?我直接写这个脚本的绝对路径,然后dde-app-wiz会把这个desktop的路径传过去这样?

@BLumia

BLumia commented Mar 25, 2026

Copy link
Copy Markdown
Member

这个钩子的格式是什么?我直接写这个脚本的绝对路径,然后dde-app-wiz会把这个desktop的路径传过去这样?

单纯调脚本,不会额外传任何参数。所以如果需要什么额外的信息的话,需要在这个字段里自行提供。

直接看代码里有 X-Deepin-PreUninstall 字样的段落就可以了解具体行为了。

@shenmo7192

Copy link
Copy Markdown
Author

这个钩子的格式是什么?我直接写这个脚本的绝对路径,然后dde-app-wiz会把这个desktop的路径传过去这样?

单纯调脚本,不会额外传任何参数。所以如果需要什么额外的信息的话,需要在这个字段里自行提供。

直接看代码里有 X-Deepin-PreUninstall 字样的段落就可以了解具体行为了。

https://bbs.deepin.org.cn/phone/zh/post/296557

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants