Skip to content

fix(plugin-manager): treat a dangling plugin symlink as not installed#2696

Open
elibosley wants to merge 2 commits into
masterfrom
fix/plugin-manager-dangling-install-symlink
Open

fix(plugin-manager): treat a dangling plugin symlink as not installed#2696
elibosley wants to merge 2 commits into
masterfrom
fix/plugin-manager-dangling-install-symlink

Conversation

@elibosley

@elibosley elibosley commented Jul 15, 2026

Copy link
Copy Markdown
Member

Summary

Plugin Manager now treats dangling plugin tracking symlinks as not installed across both direct and bulk operations, removing the stale registration so the plugin can be checked or installed normally again.

Why This Exists

The manager tracks installed plugins with /var/log/plugins/<name>.plg symlinks pointing to /boot/config/plugins/<name>.plg. readlink() returns a path even when the target was removed or renamed out-of-band, so stale registrations were treated as installed and later failed with a misleading XML parse error. The Plugins page uses the bulk handlers, which also left these stale links behind.

Resolution

Add one installed_plugin() helper that resolves a tracking link, verifies its target is a regular file, and deletes a dangling link before reporting the plugin as not installed. Use the same helper in install, check, update, checkall, updateall, and checkos so direct CLI calls and WebGUI-triggered bulk actions share one installed-state contract.

Reviewer Considerations

  • Confirm healthy tracking symlinks retain the existing install, check, and update behavior; the helper returns their target unchanged.
  • Dangling links and links to non-files are cleaned, while an existing malformed XML target is preserved for the existing parse-error handling.
  • remove remains unchanged because it already unlinks the tracking entry before acting and self-heals this state.

Behavior Changes

  • Direct install, check, and update treat a dangling tracking link as not installed and remove it.
  • Bulk checkall, updateall, and built-in checkos now perform the same cleanup, covering the Plugins page Check For Updates path.
  • Healthy plugin operations are unchanged.

Implementation Summary

  • Add installed_plugin() as the canonical tracking-link resolver.
  • Replace direct readlink() checks in the three direct handlers and three bulk handlers.

Verification

  • Live QA confirmed direct dangling-link cleanup, fresh reinstall recovery, healthy install/check/update/remove behavior, directory-target cleanup, and malformed-target preservation.
  • Live QA isolated the remaining failure to checkall, updateall, and checkos; those exact call sites now use the already-validated helper.
  • php -l emhttp/plugins/dynamix.plugin.manager/scripts/plugin — passed.
  • git diff --check — passed.
  • GitHub build-plugin for commit 8a9d594 — passed.

Risk

Low; the follow-up changes only the resolver used by three bulk loops and reuses the helper already validated on the direct paths. Live bulk-path re-verification is still required before QA sign-off.

The plugin manager tracks installed plugins with a symlink under
/var/log/plugins/<name>.plg pointing at the real /boot/config/plugins/<name>.plg.
It used `@readlink()` alone to decide whether a plugin is installed, so a
*dangling* symlink — one whose target .plg was removed or renamed on the flash
out-of-band — was still read as "installed". The manager then tried to parse the
missing target and aborted with:

    plugin: XML file doesn't exist or xml parse error

For `install` this made the plugin permanently un-installable until the stale
symlink was removed by hand; `check` and `update` failed the same way.

Add an `installed_plugin()` helper that resolves the tracking symlink to its
target, treats a dangling link as not installed, and removes the stale link so a
subsequent install/update/check starts clean. Use it in the install, check, and
update handlers in place of the bare `@readlink()`. (`remove` already unlinks the
stale symlink before acting, so it self-heals.)

Verified on a live box: with a dangling tracking symlink, `check` now reports
"plugin: not installed" and clears the stale link, and a fresh install proceeds
normally instead of aborting on the parse error.
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The plugin script adds validated symlink resolution and stale-link removal. Install, check, update, checkall, updateall, and checkos now treat missing plugin targets as not installed.

Changes

Plugin symlink cleanup

Layer / File(s) Summary
Validated plugin resolution
emhttp/plugins/dynamix.plugin.manager/scripts/plugin
Adds installed_plugin() to resolve plugin symlinks, detect missing targets, and remove stale links.
Lifecycle command integration
emhttp/plugins/dynamix.plugin.manager/scripts/plugin
Install, check, update, checkall, updateall, and checkos use installed_plugin() instead of direct symlink reads.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

A rabbit hops past a symlink thread,
Finds the plugin file is gone instead.
It tidies the link with a gentle sweep,
So install and checks know what to keep.
Hop, hop—stale paths now sleep!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: dangling plugin symlinks are now treated as not installed.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/plugin-manager-dangling-install-symlink

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown

🔧 PR Test Plugin Available

A test plugin has been generated for this PR that includes the modified files.

Version: 2026.07.15.2107
Build: View Workflow Run

📥 Installation Instructions:

Install via Unraid Web UI:

  1. Go to Plugins → Install Plugin
  2. Copy and paste this URL:
https://preview.dl.unraid.net/pr-plugins/pr-2696/webgui-pr-2696.plg
  1. Click Install

Alternative: Direct Download

⚠️ Important Notes:

  • Testing only: This plugin is for testing PR changes
  • Backup included: Original files are automatically backed up
  • Easy removal: Files are restored when plugin is removed
  • Conflicts: Remove this plugin before installing production updates
  • Post-merge behavior: This preview stays available after merge until preview storage expires or it is manually cleaned up

📝 Modified Files:

Click to expand file list
emhttp/plugins/dynamix.plugin.manager/scripts/plugin

🔄 To Remove:

Navigate to Plugins → Installed Plugins and remove webgui-pr-2696, or run:

plugin remove webgui-pr-2696

🤖 This comment is automatically generated and will be updated with each new push to this PR.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@emhttp/plugins/dynamix.plugin.manager/scripts/plugin`:
- Around line 521-535: Update the checkall, updateall, and checkos bulk loops to
call installed_plugin() when resolving tracking symlinks instead of bypassing it
with direct checks. Preserve each loop’s existing handling for valid installed
plugins while ensuring dangling symlinks are removed and treated as not
installed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 615588bd-ed09-4084-9a2f-057611ff1eae

📥 Commits

Reviewing files that changed from the base of the PR and between 369f0b2 and dcb9acb.

📒 Files selected for processing (1)
  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin

Comment thread emhttp/plugins/dynamix.plugin.manager/scripts/plugin
@unraid-bot unraid-bot added the 7.4.0 Approved for release 7.4.0 (auto-managed by notification-worker) label Jul 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

7.4.0 Approved for release 7.4.0 (auto-managed by notification-worker)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants