Skip to content

Conversation

@elibosley
Copy link
Member

@elibosley elibosley commented Nov 24, 2025

Summary by CodeRabbit

  • New Features

    • Added a download-only mode and a "plugin download" command to fetch plugins/scripts without executing install Run actions; filesystem changes and staging still occur.
    • Added TARGET-VERSION support to allow downloads/updates against a specified target version for compatibility checks.
  • Bug Fixes / Improvements

    • Pre- and post-install hooks and Run actions are skipped in download-only mode and clearly reported.
    • Version checks now honor the specified target/version context and status messaging distinguishes downloaded vs installed/staged.
  • Documentation

    • Updated usage text and status messages to reflect download vs install semantics.

✏️ Tip: You can customize this high-level summary in your review settings.

@elibosley elibosley requested a review from ljm42 November 24, 2025 14:49
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 24, 2025

Walkthrough

Adds a download-only mode and plugin download flow that reuses the update/install path but skips Run, pre_hooks, and post_hooks; introduces global $download_only and $check_version (from /etc/unraid-version); supports optional TARGET-VERSION; stages files to /tmp/plugins and adjusts messages for download semantics.

Changes

Cohort / File(s) Summary
Plugin manager script
emhttp/plugins/dynamix.plugin.manager/scripts/plugin
Introduces global $download_only and $check_version; normalizes CLI into script, method, optional_args; implements plugin download by mapping to the update/install flow while skipping Run/pre_hooks/post_hooks when download-only; accepts optional TARGET-VERSION and uses $check_version for compatibility checks; stages FILE entries to /tmp/plugins; centralizes unknown-command errors to use $script; updates logging/messages to reflect "downloaded/staged" vs "installed/executed".

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Script as plugin (script)
    participant SysVer as /etc/unraid-version (check_version)
    participant Fetcher as Downloader (/tmp/plugins)
    participant Hooks
    participant Runner
    participant Logger

    User->>Script: plugin [download|install|update] <PLUGIN-FILE> [TARGET-VERSION]
    Script->>Script: parse argv → script, method, optional_args, set $download_only
    Script->>SysVer: read /etc/unraid-version -> $check_version
    Script->>Fetcher: fetch/stage FILE(s) -> /tmp/plugins
    alt TARGET-VERSION present
        Script->>SysVer: compare TARGET-VERSION vs $check_version
    end
    alt download-only
        Fetcher-->>Script: files staged
        Script->>Logger: log "downloaded / staged"
        Script->>Hooks: skip pre_hooks/post_hooks
        Script->>Runner: skip Run entries
        Script->>Logger: log completion (downloaded/staged)
    else normal install/update
        Fetcher-->>Script: files staged
        Script->>Hooks: execute pre_hooks
        Script->>Runner: execute Run entries
        Script->>Hooks: execute post_hooks
        Script->>Logger: log completion (installed/executed)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Verify CLI parsing normalization (script/optional_args) and unknown-command handling.
  • Confirm $download_only consistently bypasses pre_hooks, post_hooks, and Run entries.
  • Review TARGET-VERSION vs $check_version comparison and Min/Max gating logic.
  • Check messaging changes (downloaded/staged vs installed/executed) and staging to /tmp/plugins.

Poem

🐇 I fetched a plugin, neat and spry,
Stashed it softly where temp files lie.
Hooks curl up, Runs take a nap,
Downloaded, staged — no further yap.
A quiet burrow, ready to apply.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a 'download-only' option to the plugin installation command, which is the core feature introduced in this changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/download-only-plugin-installs

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

@github-actions
Copy link

github-actions bot commented Nov 24, 2025

🔧 PR Test Plugin Available

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

Version: 2025.11.25.1707
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-2464/webgui-pr-2464.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

📝 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-2464, or run:

plugin remove webgui-pr-2464

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
emhttp/plugins/dynamix.plugin.manager/scripts/plugin (1)

752-767: Reconsider plugin registration in download-only mode.

In download-only mode, the plugin is still registered as installed (symlink created in /var/log/plugins, copied to /boot/config/plugins, logged as "installed"). However, since Run commands were skipped, the plugin may not be functional. This creates an inconsistent state where:

  1. The plugin appears installed to the system and Plugin Manager
  2. Required services, dependencies, or configurations may not be set up
  3. At next boot, the plugin will attempt to install again from /boot/config/plugins

This could lead to user confusion and potential system issues.

Consider one of these approaches:

Option 1 (Recommended): Don't register the plugin as installed in download-only mode:

  // register successful install
  $target = "$boot/$plugin";
+ if ($download_only) {
+   write("plugin: $plugin files downloaded (not installed)\n");
+   my_logger("$plugin files downloaded only", $logger);
+ } else {
    if (!plugin('noInstall', $plugin_file, $error)) {
      if ($target != $plugin_file) copy($plugin_file, $target);
      symlink($target, $symlink);
      write("plugin: $plugin installed\n");
      my_logger("$plugin installed", $logger);
    } else {
      write("script: $plugin executed\n");
      my_logger("script: $plugin executed", $logger);
    }
+ }

Option 2: Store downloaded plugins in a separate location (e.g., /boot/config/plugins-downloaded) to distinguish them from installed plugins.

🧹 Nitpick comments (1)
emhttp/plugins/dynamix.plugin.manager/scripts/plugin (1)

17-28: Clarify usage text regarding plugin registration behavior.

The usage text indicates that download-only will "download and replace plugin files without executing any Run commands," but doesn't mention that the plugin will still be registered as installed (symlink created in /var/log/plugins). This could mislead users into thinking the plugin is not installed when it actually is.

Consider updating the usage text to clarify:

- download-only is optional and can be used to download and replace plugin files without executing any
- Run commands defined for the install method.
+ download-only is optional and can be used to download and replace plugin files without executing any
+ Run commands defined for the install method. The plugin will still be registered as installed.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b736f9c and c35627c.

📒 Files selected for processing (1)
  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin (14 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-05T23:34:04.847Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2355
File: emhttp/plugins/dynamix.plugin.manager/include/PluginHelpers.php:21-28
Timestamp: 2025-09-05T23:34:04.847Z
Learning: In the Unraid webGUI plugin system, the static $methods array in PluginHelpers.php contains only methods that should bypass caching and call the plugin script directly. Methods like 'checkall' are handled at the PHP wrapper level via recursive calls to the plugin() function, not as direct subcommands in the plugin script itself.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (5)
emhttp/plugins/dynamix.plugin.manager/scripts/plugin (5)

495-504: LGTM - Variable initialization looks good.

The initialization of global variables is correct. The new $script variable improves maintainability by storing $argv[0] once rather than referencing it multiple times.


612-614: LGTM - Clear user feedback.

The message appropriately informs users that download-only mode is active and install commands will be skipped.


461-465: LGTM - Run command skipping is correctly implemented.

The conditional logic properly skips Run command execution in download-only mode while still downloading/creating the files. The logging message clearly indicates why the command was skipped.


520-573: LGTM - Improved error message consistency.

Using the $script variable instead of directly referencing $argv[0] improves code maintainability and ensures consistent error reporting across checkall, updateall, and checkos commands.


620-622: LGTM - Consistent hook skipping in download-only mode.

The conditional execution of pre_hooks() and post_hooks() is correctly applied throughout all install flow paths (success, error, and early exit scenarios). This prevents hook scripts from running when the plugin is not fully installed.

Also applies to: 629-631, 645-647, 657-659, 669-671, 679-681, 688-690, 699-701, 707-709, 714-716, 727-729, 736-738, 746-748, 764-766

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
emhttp/plugins/dynamix.plugin.manager/scripts/plugin (1)

619-620: Fix incorrect forced install logic (duplicate issue).

The logic at line 620 incorrectly sets $forced = true whenever ANY option other than 'force' or 'forced' is present. This means the 'nchan' option (used for output control) would unintentionally bypass version checks.

Apply this diff to fix the logic:

- $non_flag_options = array_diff($options, ['force', 'forced']);
- $forced = in_array('force', $options, true) || in_array('forced', $options, true) || !empty($non_flag_options);
+ $forced = in_array('force', $options, true) || in_array('forced', $options, true);

Based on learnings, this issue was previously identified and remains unresolved.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c35627c and 5a49fb8.

📒 Files selected for processing (1)
  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin (13 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-03-27T22:04:00.594Z
Learnt from: zackspear
Repo: unraid/webgui PR: 2099
File: emhttp/plugins/dynamix.my.servers/include/activation-code-extractor.php:58-74
Timestamp: 2025-03-27T22:04:00.594Z
Learning: The file `emhttp/plugins/dynamix.my.servers/include/activation-code-extractor.php` is synced from a different repository, and modifications should not be suggested in this repository's context. Changes should be proposed in the source repository instead.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-09-05T23:34:04.847Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2355
File: emhttp/plugins/dynamix.plugin.manager/include/PluginHelpers.php:21-28
Timestamp: 2025-09-05T23:34:04.847Z
Learning: In the Unraid webGUI plugin system, the static $methods array in PluginHelpers.php contains only methods that should bypass caching and call the plugin script directly. Methods like 'checkall' are handled at the PHP wrapper level via recursive calls to the plugin() function, not as direct subcommands in the plugin script itself.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (7)
emhttp/plugins/dynamix.plugin.manager/scripts/plugin (7)

26-36: LGTM! Clear documentation for the new download command.

The usage documentation effectively explains the new download-only workflow and clarifies that FILE elements are processed similarly to install, minus the Run command execution.


297-297: LGTM! Proper global variable declaration.

The $download_only global variable is correctly added to support download-only mode checks within the plugin() function.


467-471: LGTM! Run command skip logic is correct.

The guard properly prevents Run command execution in download-only mode while providing clear logging of skipped actions.


612-618: Verify intentional breaking change for legacy download-only flags.

This code rejects the legacy download-only and download flags when used with plugin install, forcing users to use the new plugin download command instead. This is a breaking change from the previous behavior.

Please confirm this breaking change is intentional and that users will be notified in release notes or migration documentation. If backward compatibility is desired, consider accepting the legacy flags with a deprecation warning instead of an error.


634-636: LGTM! Hook guards are properly implemented.

All pre_hooks() and post_hooks() calls in the URL download and version validation paths are correctly guarded to skip execution in download-only mode.

Also applies to: 643-645, 659-662, 671-674


683-686: LGTM! Comprehensive hook guard coverage.

All pre_hooks() and post_hooks() calls throughout the install/download workflow are consistently guarded with !$download_only checks. This ensures hooks are not executed in download-only mode across all code paths including success, failure, and early exit scenarios.

Also applies to: 693-696, 702-705, 713-716, 721-724, 728-730, 741-743, 750-752, 760-762, 780-782


625-629: LGTM! Clear and accurate messaging.

The conditional messaging correctly differentiates between download-only and install operations, providing clear feedback to users about which action is being performed and the resulting status.

Also applies to: 771-777

@elibosley elibosley changed the title feat: Enhance plugin installation command to support 'download-only' option, allowing users to download plugin files without executing installation commands. Update usage instructions and adjust processing logic accordingly. feat: Enhance plugin installation command to support 'download-only' option Nov 24, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
emhttp/plugins/dynamix.plugin.manager/scripts/plugin (1)

503-512: Refine nchan detection to avoid treating plugin filename as an option

As previously noted, including PLUGIN-FILE in $optional_args means a plugin literally named nchan.plg will incorrectly enable nchan mode. You can avoid this while still supporting checkall/updateall/checkos by varying the slice offset based on whether the current method expects a plugin argument.

 $notify        = '/usr/local/emhttp/webGui/scripts/notify';
 $boot          = '/boot/config/plugins';
 $plugins       = '/var/log/plugins';
 $tmp           = '/tmp/plugins';
 $download_only = false;
 $script        = $argv[0];
 $method        = $argv[1];
-$optional_args = array_slice($argv, 2);
+$has_plugin_arg = !in_array($method, ['checkall', 'updateall', 'checkos'], true);
+// Methods with a PLUGIN/PLUGIN-FILE argument have it at $argv[2],
+// so option flags (including "nchan") begin at $argv[3].
+// Methods without a plugin argument (checkall/updateall/checkos)
+// take options starting at $argv[2].
+$optional_args = array_slice($argv, $has_plugin_arg ? 3 : 2);
 $builtin       = ['unRAIDServer','unRAIDServer-'];
 $nchan         = in_array('nchan', $optional_args, true); // console or nchan output

This preserves plugin checkall nchan (options from index 2) while ensuring plugin install myplugin.plg nchan and similar commands don’t treat the plugin filename itself as the nchan flag.

🧹 Nitpick comments (1)
emhttp/plugins/dynamix.plugin.manager/scripts/plugin (1)

608-623: Download-only parsing and forced behavior are coherent with existing conventions

The new install/download branch cleanly:

  • Normalizes the plugin argument,
  • Distinguishes options from the PLUGIN-FILE,
  • Rejects legacy plugin install ... download-only usage with a clear message, and
  • Computes $forced in a way that preserves the existing (intentional) behavior where any non-force/forced option implies a forced install/download.

The extra “download-only mode enabled” and $action messages should make CLI behavior much clearer for callers.

If you expect future readers to be surprised by the “any non-flag option implies forced” rule, a short inline comment next to the $forced assignment explaining that this is historical/intentional behavior would help avoid accidental “simplifications”.

Also applies to: 627-632

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3ac24e4 and 98909da.

📒 Files selected for processing (1)
  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin (11 hunks)
🧰 Additional context used
🧠 Learnings (8)
📚 Learning: 2025-11-24T15:12:41.007Z
Learnt from: elibosley
Repo: unraid/webgui PR: 2464
File: emhttp/plugins/dynamix.plugin.manager/scripts/plugin:0-0
Timestamp: 2025-11-24T15:12:41.007Z
Learning: In emhttp/plugins/dynamix.plugin.manager/scripts/plugin, the $forced variable logic intentionally treats ANY option argument (beyond the explicitly handled ones like 'download-only', 'download', 'force', 'forced') as triggering forced install mode. This means that options like 'nchan' will cause $forced to be true. This is by design, even though it's unusual behavior.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-03-27T22:04:00.594Z
Learnt from: zackspear
Repo: unraid/webgui PR: 2099
File: emhttp/plugins/dynamix.my.servers/include/activation-code-extractor.php:58-74
Timestamp: 2025-03-27T22:04:00.594Z
Learning: The file `emhttp/plugins/dynamix.my.servers/include/activation-code-extractor.php` is synced from a different repository, and modifications should not be suggested in this repository's context. Changes should be proposed in the source repository instead.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-09-05T23:34:04.847Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2355
File: emhttp/plugins/dynamix.plugin.manager/include/PluginHelpers.php:21-28
Timestamp: 2025-09-05T23:34:04.847Z
Learning: In the Unraid webGUI plugin system, the static $methods array in PluginHelpers.php contains only methods that should bypass caching and call the plugin script directly. Methods like 'checkall' are handled at the PHP wrapper level via recursive calls to the plugin() function, not as direct subcommands in the plugin script itself.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-08-13T03:23:31.900Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2333
File: emhttp/plugins/dynamix.docker.manager/DockerContainers.page:6-6
Timestamp: 2025-08-13T03:23:31.900Z
Learning: In unraid/webgui codebase, Squidly271 prefers to maintain existing naming conventions even when there are technical inconsistencies (like Nchan="docker_load" vs actual channel "dockerload") if they've been stable for a long time, to avoid causing confusion by changing long-standing patterns.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-08-10T20:48:52.086Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2326
File: emhttp/plugins/dynamix.vm.manager/nchan/vm_usage:93-93
Timestamp: 2025-08-10T20:48:52.086Z
Learning: In the unraid/webgui codebase, Squidly271 prefers the publish() function to internally handle parameter type overloading (e.g., accepting boolean true in place of buffer length 1) to avoid repetitive parameter specifications and maintain backwards compatibility. The publish() function in emhttp/plugins/dynamix/include/publish.php is designed to sort out these parameters internally rather than requiring explicit values at every call site.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-09-05T16:33:12.970Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2353
File: emhttp/plugins/dynamix/DashStats.page:2257-2257
Timestamp: 2025-09-05T16:33:12.970Z
Learning: In the Unraid webGUI Nchan system, publisher scripts (like "update_1") publish to normalized endpoint names (like "update1"). The Nchan header lists the script names to start, while JavaScript subscribes to the published endpoints. For example: Nchan="update_1" starts the script which calls publish_noDupe('update1', data), and JavaScript subscribes to '/sub/update1'. This is the intended design, not a mismatch.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-09-05T16:33:12.970Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2353
File: emhttp/plugins/dynamix/DashStats.page:2257-2257
Timestamp: 2025-09-05T16:33:12.970Z
Learning: In the Unraid webGUI Nchan system, the Nchan header declaration lists publisher scripts to start (e.g., "update_1"), while the JavaScript subscription endpoints use normalized names (e.g., "/sub/update1"). The publisher script "update_1" publishes to "/pub/update1" endpoint, creating a separation between script names and published endpoints. This is by design and not a mismatch.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-10-03T02:57:29.994Z
Learnt from: ljm42
Repo: unraid/webgui PR: 2414
File: etc/rc.d/rc.nginx:374-376
Timestamp: 2025-10-03T02:57:29.994Z
Learning: Repo unraid/webgui: In etc/rc.d/rc.nginx, maintainers prefer not to add explicit mv-failure checks or EXIT trap clearing around atomic writes in build_servers(), build_locations(), and build_ini(); treat mv failures (e.g., disk full/permissions) as non-recoverable and keep the implementation simple.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (5)
emhttp/plugins/dynamix.plugin.manager/scripts/plugin (5)

26-37: Download usage text matches implementation semantics

The new plugin download usage, including the note that both install and download process FILE elements with install (or no) method, correctly reflects the behavior in plugin('install', ...) with download_only gating only Run execution.


213-235: Hooks correctly suppressed in download-only mode

Short-circuiting pre_hooks() and post_hooks() when $download_only is true cleanly prevents hook side effects during plugin download while preserving behavior for all other methods.


299-299: Run commands are safely skipped in download-only mode

Importing $download_only into plugin() and bailing out before executing FILE Run commands achieves the intended “download without executing install commands” behavior, including inline scripts, while leaving normal install/update/remove flows unchanged.

Also applies to: 467-493


527-545: Consistent script path resolution via $script

Switching checkall, updateall, and checkos to resolve $cmd from $script centralizes how the executable path is derived and yields clearer error messages when the script path cannot be resolved, without changing the core behavior of these commands.

Also applies to: 550-573, 578-594


742-754: Confirm that download-only should still register the plugin as installed

In the registration block, plugin download for a normal plugin still:

  • Copies the .plg into /boot/config/plugins, and
  • Creates the /var/log/plugins symlink,

but reports the status as downloaded instead of installed. For noInstall scripts, it reports staged instead of executed.

This means download-only mode pre-stages the plugin and marks it as installed in the usual way, but defers FILE Run execution (thanks to $download_only and the Run guard in plugin()). If the intent was strictly “download artifacts without changing install state”, you’d need an additional guard here to skip the copy+symlink in download-only mode.

Would you like to keep this “pre-stage but not execute” behavior, or should plugin download avoid registering the plugin at all?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
emhttp/plugins/dynamix.plugin.manager/scripts/plugin (1)

503-513: Guard $argv access to avoid notices; minor nchan edge case remains

Right now $method = $argv[1]; and $optional_args = array_slice($argv, 2); are evaluated before the $argc < 2 check. If someone runs plugin with no subcommand, this will emit PHP notices for undefined $argv[1]. You can avoid that with a small defensive tweak:

 $notify        = '/usr/local/emhttp/webGui/scripts/notify';
 $boot          = '/boot/config/plugins';
 $plugins       = '/var/log/plugins';
 $tmp           = '/tmp/plugins';
-$download_only = false;
-$script        = $argv[0];
-$method        = $argv[1];
-$optional_args = array_slice($argv, 2);
-$builtin       = ['unRAIDServer','unRAIDServer-'];
-$nchan         = in_array('nchan', $optional_args, true); // console or nchan output
+$download_only = false;
+$script        = $argv[0];
+$method        = $argv[1] ?? null;
+$optional_args = $argc > 2 ? array_slice($argv, 2) : [];
+$builtin       = ['unRAIDServer','unRAIDServer-'];
+$nchan         = in_array('nchan', $optional_args, true); // console or nchan output

Note: as previously discussed, $optional_args still includes the plugin filename for commands that take a PLUGIN‑FILE, so a plugin literally named nchan would toggle $nchan. This is extremely unlikely in practice but still a tiny edge case; if you ever want to tighten it, you could strip the plugin filename from $optional_args only for methods that require one (install/download/check/update/remove).

Also applies to: 518-522

🧹 Nitpick comments (1)
emhttp/plugins/dynamix.plugin.manager/scripts/plugin (1)

602-607: Clarify intended semantics of plugin download vs registration

The current download flow still:

  • Runs plugin('install', $plugin_file, $error) with $download_only = true (so FILEs are processed but Run commands skipped), and
  • Registers the plugin in /boot/config/plugins and /var/log/plugins just like a normal install, only changing status/log messages to “downloaded”/“staged”.

This means a “downloaded” plugin is already treated as installed by the system (e.g., symlink exists, included in checkall/updateall), even though none of its Run steps or hooks have ever executed, and a later plugin install using the same PLUGIN‑FILE path will hit the “not re‑installing same plugin” short‑circuit.

If your goal is strictly “install all FILEs but skip Run and hooks” then this behavior is consistent. If instead the intent is more of an “offline fetch/stage without marking the plugin as installed yet”, you may want to consider:

  • Skipping the $target copy and $symlink creation when $download_only is true, or
  • Marking the resulting state in a way that allows a follow‑up real install (e.g., allowing re‑install from the same PLUGIN‑FILE when the previous run was download‑only).

Can you confirm which behavior you want for plugin download? If you’d like, I can sketch a diff for one of the above approaches.

Also applies to: 608-615, 619-623, 739-746

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 98909da and c12f031.

📒 Files selected for processing (1)
  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin (11 hunks)
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: elibosley
Repo: unraid/webgui PR: 2464
File: emhttp/plugins/dynamix.plugin.manager/scripts/plugin:0-0
Timestamp: 2025-11-24T15:12:41.007Z
Learning: In emhttp/plugins/dynamix.plugin.manager/scripts/plugin, the $forced variable logic intentionally treats ANY option argument (beyond the explicitly handled ones like 'download-only', 'download', 'force', 'forced') as triggering forced install mode. This means that options like 'nchan' will cause $forced to be true. This is by design, even though it's unusual behavior.
📚 Learning: 2025-11-24T15:12:41.007Z
Learnt from: elibosley
Repo: unraid/webgui PR: 2464
File: emhttp/plugins/dynamix.plugin.manager/scripts/plugin:0-0
Timestamp: 2025-11-24T15:12:41.007Z
Learning: In emhttp/plugins/dynamix.plugin.manager/scripts/plugin, the $forced variable logic intentionally treats ANY option argument (beyond the explicitly handled ones like 'download-only', 'download', 'force', 'forced') as triggering forced install mode. This means that options like 'nchan' will cause $forced to be true. This is by design, even though it's unusual behavior.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-03-27T22:04:00.594Z
Learnt from: zackspear
Repo: unraid/webgui PR: 2099
File: emhttp/plugins/dynamix.my.servers/include/activation-code-extractor.php:58-74
Timestamp: 2025-03-27T22:04:00.594Z
Learning: The file `emhttp/plugins/dynamix.my.servers/include/activation-code-extractor.php` is synced from a different repository, and modifications should not be suggested in this repository's context. Changes should be proposed in the source repository instead.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-09-05T23:34:04.847Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2355
File: emhttp/plugins/dynamix.plugin.manager/include/PluginHelpers.php:21-28
Timestamp: 2025-09-05T23:34:04.847Z
Learning: In the Unraid webGUI plugin system, the static $methods array in PluginHelpers.php contains only methods that should bypass caching and call the plugin script directly. Methods like 'checkall' are handled at the PHP wrapper level via recursive calls to the plugin() function, not as direct subcommands in the plugin script itself.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-08-13T03:23:31.900Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2333
File: emhttp/plugins/dynamix.docker.manager/DockerContainers.page:6-6
Timestamp: 2025-08-13T03:23:31.900Z
Learning: In unraid/webgui codebase, Squidly271 prefers to maintain existing naming conventions even when there are technical inconsistencies (like Nchan="docker_load" vs actual channel "dockerload") if they've been stable for a long time, to avoid causing confusion by changing long-standing patterns.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-08-10T20:48:52.086Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2326
File: emhttp/plugins/dynamix.vm.manager/nchan/vm_usage:93-93
Timestamp: 2025-08-10T20:48:52.086Z
Learning: In the unraid/webgui codebase, Squidly271 prefers the publish() function to internally handle parameter type overloading (e.g., accepting boolean true in place of buffer length 1) to avoid repetitive parameter specifications and maintain backwards compatibility. The publish() function in emhttp/plugins/dynamix/include/publish.php is designed to sort out these parameters internally rather than requiring explicit values at every call site.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-09-05T16:33:12.970Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2353
File: emhttp/plugins/dynamix/DashStats.page:2257-2257
Timestamp: 2025-09-05T16:33:12.970Z
Learning: In the Unraid webGUI Nchan system, publisher scripts (like "update_1") publish to normalized endpoint names (like "update1"). The Nchan header lists the script names to start, while JavaScript subscribes to the published endpoints. For example: Nchan="update_1" starts the script which calls publish_noDupe('update1', data), and JavaScript subscribes to '/sub/update1'. This is the intended design, not a mismatch.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-09-05T16:33:12.970Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2353
File: emhttp/plugins/dynamix/DashStats.page:2257-2257
Timestamp: 2025-09-05T16:33:12.970Z
Learning: In the Unraid webGUI Nchan system, the Nchan header declaration lists publisher scripts to start (e.g., "update_1"), while the JavaScript subscription endpoints use normalized names (e.g., "/sub/update1"). The publisher script "update_1" publishes to "/pub/update1" endpoint, creating a separation between script names and published endpoints. This is by design and not a mismatch.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-10-03T02:57:29.994Z
Learnt from: ljm42
Repo: unraid/webgui PR: 2414
File: etc/rc.d/rc.nginx:374-376
Timestamp: 2025-10-03T02:57:29.994Z
Learning: Repo unraid/webgui: In etc/rc.d/rc.nginx, maintainers prefer not to add explicit mv-failure checks or EXIT trap clearing around atomic writes in build_servers(), build_locations(), and build_ini(); treat mv failures (e.g., disk full/permissions) as non-recoverable and keep the implementation simple.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (4)
emhttp/plugins/dynamix.plugin.manager/scripts/plugin (4)

26-36: Download subcommand usage text looks consistent with behavior

The new plugin download usage block clearly documents that FILE elements for the install method are still processed while Run commands are skipped, which matches the implementation below. No issues here.


213-230: Skipping hooks in download-only mode matches intent

Conditioning pre_hooks()/post_hooks() on $download_only is a clean way to ensure download-only runs don't trigger external hook scripts, while other methods (check, update, remove, etc.) still execute hooks because $download_only remains false. Looks good.


298-300: Run skipping inside plugin() is correct and well-logged

Using the global $download_only in plugin() to short‑circuit Run blocks, with a clear log message and a reasonable $target fallback (Name, LOCAL, or 'inline script'), achieves “download without execution” semantics without affecting normal installs.

Also applies to: 467-473


528-529: Using realpath($script) improves consistency

Switching the helper commands (checkall, updateall, checkos) to use realpath($script) instead of inlining $argv[0] gives a single source of truth for the script path and aligns with the new initialization block. Behavior stays the same for normal invocations.

Also applies to: 551-552, 579-580

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
emhttp/plugins/dynamix.plugin.manager/scripts/plugin (1)

510-512: Consider extracting options consistently to avoid edge case.

$optional_args on line 510 includes the plugin filename (argv[2]), which means the nchan check on line 512 could incorrectly enable nchan mode if a plugin were literally named 'nchan'. Line 618 correctly extracts only options using array_slice($argv, 3). Consider using the same approach here for consistency.

-$optional_args = array_slice($argv, 2);
+$optional_args = array_slice($argv, 3);

Note: This issue was previously identified but remains unaddressed.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c12f031 and 09ee6ae.

📒 Files selected for processing (1)
  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin (12 hunks)
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: elibosley
Repo: unraid/webgui PR: 2464
File: emhttp/plugins/dynamix.plugin.manager/scripts/plugin:0-0
Timestamp: 2025-11-24T15:12:41.007Z
Learning: In emhttp/plugins/dynamix.plugin.manager/scripts/plugin, the $forced variable logic intentionally treats ANY option argument (beyond the explicitly handled ones like 'download-only', 'download', 'force', 'forced') as triggering forced install mode. This means that options like 'nchan' will cause $forced to be true. This is by design, even though it's unusual behavior.
📚 Learning: 2025-11-24T15:12:41.007Z
Learnt from: elibosley
Repo: unraid/webgui PR: 2464
File: emhttp/plugins/dynamix.plugin.manager/scripts/plugin:0-0
Timestamp: 2025-11-24T15:12:41.007Z
Learning: In emhttp/plugins/dynamix.plugin.manager/scripts/plugin, the $forced variable logic intentionally treats ANY option argument (beyond the explicitly handled ones like 'download-only', 'download', 'force', 'forced') as triggering forced install mode. This means that options like 'nchan' will cause $forced to be true. This is by design, even though it's unusual behavior.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-03-27T22:04:00.594Z
Learnt from: zackspear
Repo: unraid/webgui PR: 2099
File: emhttp/plugins/dynamix.my.servers/include/activation-code-extractor.php:58-74
Timestamp: 2025-03-27T22:04:00.594Z
Learning: The file `emhttp/plugins/dynamix.my.servers/include/activation-code-extractor.php` is synced from a different repository, and modifications should not be suggested in this repository's context. Changes should be proposed in the source repository instead.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-09-05T23:34:04.847Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2355
File: emhttp/plugins/dynamix.plugin.manager/include/PluginHelpers.php:21-28
Timestamp: 2025-09-05T23:34:04.847Z
Learning: In the Unraid webGUI plugin system, the static $methods array in PluginHelpers.php contains only methods that should bypass caching and call the plugin script directly. Methods like 'checkall' are handled at the PHP wrapper level via recursive calls to the plugin() function, not as direct subcommands in the plugin script itself.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-08-13T03:23:31.900Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2333
File: emhttp/plugins/dynamix.docker.manager/DockerContainers.page:6-6
Timestamp: 2025-08-13T03:23:31.900Z
Learning: In unraid/webgui codebase, Squidly271 prefers to maintain existing naming conventions even when there are technical inconsistencies (like Nchan="docker_load" vs actual channel "dockerload") if they've been stable for a long time, to avoid causing confusion by changing long-standing patterns.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-08-10T20:48:52.086Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2326
File: emhttp/plugins/dynamix.vm.manager/nchan/vm_usage:93-93
Timestamp: 2025-08-10T20:48:52.086Z
Learning: In the unraid/webgui codebase, Squidly271 prefers the publish() function to internally handle parameter type overloading (e.g., accepting boolean true in place of buffer length 1) to avoid repetitive parameter specifications and maintain backwards compatibility. The publish() function in emhttp/plugins/dynamix/include/publish.php is designed to sort out these parameters internally rather than requiring explicit values at every call site.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-09-05T16:33:12.970Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2353
File: emhttp/plugins/dynamix/DashStats.page:2257-2257
Timestamp: 2025-09-05T16:33:12.970Z
Learning: In the Unraid webGUI Nchan system, publisher scripts (like "update_1") publish to normalized endpoint names (like "update1"). The Nchan header lists the script names to start, while JavaScript subscribes to the published endpoints. For example: Nchan="update_1" starts the script which calls publish_noDupe('update1', data), and JavaScript subscribes to '/sub/update1'. This is the intended design, not a mismatch.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-09-05T16:33:12.970Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2353
File: emhttp/plugins/dynamix/DashStats.page:2257-2257
Timestamp: 2025-09-05T16:33:12.970Z
Learning: In the Unraid webGUI Nchan system, the Nchan header declaration lists publisher scripts to start (e.g., "update_1"), while the JavaScript subscription endpoints use normalized names (e.g., "/sub/update1"). The publisher script "update_1" publishes to "/pub/update1" endpoint, creating a separation between script names and published endpoints. This is by design and not a mismatch.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-10-03T02:57:29.994Z
Learnt from: ljm42
Repo: unraid/webgui PR: 2414
File: etc/rc.d/rc.nginx:374-376
Timestamp: 2025-10-03T02:57:29.994Z
Learning: Repo unraid/webgui: In etc/rc.d/rc.nginx, maintainers prefer not to add explicit mv-failure checks or EXIT trap clearing around atomic writes in build_servers(), build_locations(), and build_ini(); treat mv failures (e.g., disk full/permissions) as non-recoverable and keep the implementation simple.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (8)
emhttp/plugins/dynamix.plugin.manager/scripts/plugin (8)

26-36: LGTM! Clear documentation for the new download command.

The usage documentation accurately describes the download-only functionality and clarifies that both install and download commands process FILE elements but differ in Run command execution.


214-215: LGTM! Appropriate hook skipping in download-only mode.

Skipping pre/post hooks in download-only mode is correct since hooks are meant to run during actual install/update/remove operations, not during file staging.

Also applies to: 228-229


299-299: LGTM! Correctly adds global variable.

Adding $download_only to the global variables list is necessary for the download-only logic inside the plugin() function.


469-473: LGTM! Core download-only functionality implemented correctly.

The Run command skipping logic is well-implemented with clear logging. It correctly handles all cases (Name, LOCAL, inline script) and continues processing remaining FILE elements.


514-518: LGTM! Clean normalization approach.

The approach of normalizing the download command to reuse the install flow while setting the $download_only flag is elegant and avoids code duplication.


534-534: LGTM! Improved code clarity with $script variable.

Using the $script variable instead of $argv[0] improves readability and maintainability across the command resolution logic.

Also applies to: 557-557, 585-585


618-628: LGTM! Clear messaging for download-only mode.

The logic correctly distinguishes between download and install actions in user-facing messages. The $forced logic (line 619) follows the intentional design where any option triggers forced mode.

Based on learnings, the $forced variable behavior is by design.


744-750: LGTM! Appropriate success messaging for both modes.

The success messages correctly reflect the operation performed:

  • "downloaded" vs "installed" for regular plugins
  • "staged" vs "executed" for noInstall plugins

This provides clear feedback about what actually occurred in download-only mode.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
emhttp/plugins/dynamix.plugin.manager/scripts/plugin (1)

26-36: Good documentation for the new download command.

The usage documentation clearly describes the new download method and its behavior. The note about FILE element processing is helpful context.

Consider clarifying that FILE elements are not just "downloaded" but are actually created/placed at their target locations—only the Run commands are skipped. This would help users understand that download mode stages files in their final locations.

Example clarification:

-Usage: plugin download PLUGIN-FILE [forced]
-  download plugin files without executing any Run commands defined for the install method
+Usage: plugin download PLUGIN-FILE [forced]
+  stage plugin files at their target locations without executing any Run commands defined for the install method
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 09ee6ae and 62b7c68.

📒 Files selected for processing (1)
  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin (12 hunks)
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: elibosley
Repo: unraid/webgui PR: 2464
File: emhttp/plugins/dynamix.plugin.manager/scripts/plugin:0-0
Timestamp: 2025-11-24T15:12:41.007Z
Learning: In emhttp/plugins/dynamix.plugin.manager/scripts/plugin, the $forced variable logic intentionally treats ANY option argument (beyond the explicitly handled ones like 'download-only', 'download', 'force', 'forced') as triggering forced install mode. This means that options like 'nchan' will cause $forced to be true. This is by design, even though it's unusual behavior.
📚 Learning: 2025-11-24T15:12:41.007Z
Learnt from: elibosley
Repo: unraid/webgui PR: 2464
File: emhttp/plugins/dynamix.plugin.manager/scripts/plugin:0-0
Timestamp: 2025-11-24T15:12:41.007Z
Learning: In emhttp/plugins/dynamix.plugin.manager/scripts/plugin, the $forced variable logic intentionally treats ANY option argument (beyond the explicitly handled ones like 'download-only', 'download', 'force', 'forced') as triggering forced install mode. This means that options like 'nchan' will cause $forced to be true. This is by design, even though it's unusual behavior.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-03-27T22:04:00.594Z
Learnt from: zackspear
Repo: unraid/webgui PR: 2099
File: emhttp/plugins/dynamix.my.servers/include/activation-code-extractor.php:58-74
Timestamp: 2025-03-27T22:04:00.594Z
Learning: The file `emhttp/plugins/dynamix.my.servers/include/activation-code-extractor.php` is synced from a different repository, and modifications should not be suggested in this repository's context. Changes should be proposed in the source repository instead.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-09-05T23:34:04.847Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2355
File: emhttp/plugins/dynamix.plugin.manager/include/PluginHelpers.php:21-28
Timestamp: 2025-09-05T23:34:04.847Z
Learning: In the Unraid webGUI plugin system, the static $methods array in PluginHelpers.php contains only methods that should bypass caching and call the plugin script directly. Methods like 'checkall' are handled at the PHP wrapper level via recursive calls to the plugin() function, not as direct subcommands in the plugin script itself.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-08-13T03:23:31.900Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2333
File: emhttp/plugins/dynamix.docker.manager/DockerContainers.page:6-6
Timestamp: 2025-08-13T03:23:31.900Z
Learning: In unraid/webgui codebase, Squidly271 prefers to maintain existing naming conventions even when there are technical inconsistencies (like Nchan="docker_load" vs actual channel "dockerload") if they've been stable for a long time, to avoid causing confusion by changing long-standing patterns.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-08-10T20:48:52.086Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2326
File: emhttp/plugins/dynamix.vm.manager/nchan/vm_usage:93-93
Timestamp: 2025-08-10T20:48:52.086Z
Learning: In the unraid/webgui codebase, Squidly271 prefers the publish() function to internally handle parameter type overloading (e.g., accepting boolean true in place of buffer length 1) to avoid repetitive parameter specifications and maintain backwards compatibility. The publish() function in emhttp/plugins/dynamix/include/publish.php is designed to sort out these parameters internally rather than requiring explicit values at every call site.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-09-05T16:33:12.970Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2353
File: emhttp/plugins/dynamix/DashStats.page:2257-2257
Timestamp: 2025-09-05T16:33:12.970Z
Learning: In the Unraid webGUI Nchan system, publisher scripts (like "update_1") publish to normalized endpoint names (like "update1"). The Nchan header lists the script names to start, while JavaScript subscribes to the published endpoints. For example: Nchan="update_1" starts the script which calls publish_noDupe('update1', data), and JavaScript subscribes to '/sub/update1'. This is the intended design, not a mismatch.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-09-05T16:33:12.970Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2353
File: emhttp/plugins/dynamix/DashStats.page:2257-2257
Timestamp: 2025-09-05T16:33:12.970Z
Learning: In the Unraid webGUI Nchan system, the Nchan header declaration lists publisher scripts to start (e.g., "update_1"), while the JavaScript subscription endpoints use normalized names (e.g., "/sub/update1"). The publisher script "update_1" publishes to "/pub/update1" endpoint, creating a separation between script names and published endpoints. This is by design and not a mismatch.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-10-03T02:57:29.994Z
Learnt from: ljm42
Repo: unraid/webgui PR: 2414
File: etc/rc.d/rc.nginx:374-376
Timestamp: 2025-10-03T02:57:29.994Z
Learning: Repo unraid/webgui: In etc/rc.d/rc.nginx, maintainers prefer not to add explicit mv-failure checks or EXIT trap clearing around atomic writes in build_servers(), build_locations(), and build_ini(); treat mv failures (e.g., disk full/permissions) as non-recoverable and keep the implementation simple.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (7)
emhttp/plugins/dynamix.plugin.manager/scripts/plugin (7)

214-215: Verify that hooks should be skipped in download-only mode.

The logic skips both pre-hooks and post-hooks when $download_only is true. This makes sense since download-only is a staging operation rather than an active install/remove/update event. However, please confirm this is the intended behavior—some monitoring or logging hooks might benefit from download notifications.

Also applies to: 228-229


469-473: LGTM! Clean implementation of Run command skipping.

The logic correctly identifies download-only mode and skips Run command execution while providing clear logging of what was skipped. The target identification handles all three file sources (Name, LOCAL, INLINE) appropriately.


503-523: Clean normalization of download to install flow.

The variable initialization and argument parsing is well-structured:

  • Extracts $script for consistent use in checkall/updateall/checkos
  • Correctly parses $extra_args and handles the nchan flag removal
  • Normalizes the download method to reuse the install flow with a $download_only flag

This approach avoids code duplication while cleanly implementing the new download-only behavior.


539-540: Good refactoring for consistency.

Using the $script variable instead of directly accessing $argv[0] in checkall, updateall, and checkos methods improves code consistency and maintainability.

Also applies to: 562-563, 590-591


627-631: Clear messaging for download-only mode.

The conditional messaging appropriately informs users when download-only mode is active and uses consistent action verbs throughout the operation.


747-754: Good semantic status messaging.

The status messages appropriately distinguish between download and install operations:

  • "downloaded" vs "installed" for plugins
  • "staged" vs "executed" for scripts

Note that the plugin symlink is still created in download-only mode (line 746), meaning the plugin is registered as if installed. Please confirm this is intentional—it suggests that download-only mode stages files and registers the plugin but skips Run command execution.


299-299: LGTM!

Adding $download_only to the global variables in plugin() is necessary and follows the existing pattern.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
emhttp/plugins/dynamix.plugin.manager/scripts/plugin (1)

759-770: Critical: Prevent symlink creation in download-only mode.

Creating the symlink in download-only mode marks the plugin as "installed" even though Run commands never executed. This breaks the post-upgrade installation flow:

  1. User runs plugin download before OS upgrade
  2. Symlink is created at line 762
  3. After OS upgrade, boot-time install detects the symlink (line 689)
  4. Install logic sees same file already installed (line 690) and exits (line 691)
  5. Run commands never execute—plugin remains incompletely installed

Apply this diff to skip symlink creation in download-only mode:

 $target = "$boot/$plugin";
 if (!plugin('noInstall', $plugin_file, $error)) {
   if ($target != $plugin_file) copy($plugin_file, $target);
-  symlink($target, $symlink);
+  if (!$download_only) {
+    symlink($target, $symlink);
+  }
   $status = $download_only ? 'downloaded' : 'installed';
♻️ Duplicate comments (1)
emhttp/plugins/dynamix.plugin.manager/scripts/plugin (1)

882-888: Critical: Prevent symlink creation in download-only mode.

Same issue as the install method—the symlink at line 885 should not be created in download-only mode, otherwise the plugin won't properly install after OS upgrade.

Apply this diff:

 unlink($symlink);
 $target = "$boot/$plugin";
 copy($plugin_file, $target);
-symlink($target, $symlink);
+if (!$download_only) {
+  symlink($target, $symlink);
+}
 $status = $download_only ? 'downloaded' : 'updated';
🧹 Nitpick comments (2)
emhttp/plugins/dynamix.plugin.manager/scripts/plugin (2)

665-665: Remove redundant global declaration.

The $check_version variable is already in global scope (initialized at line 516), making this declaration unnecessary.

Apply this diff:

   }
-  global $check_version;
   $min = plugin('min', $plugin_file, $error);

67-77: Document TARGET-VERSION parameter for update command.

The code at line 525 supports the TARGET-VERSION parameter for both download and update methods, but the usage text only documents it for download (lines 79-87). Consider adding similar documentation here for consistency.

Example addition after line 68:

 Usage: plugin update PLUGIN
   update the plugin
 
+  TARGET-VERSION is optional and specifies the Unraid version to use for version compatibility
+  checks (Min/Max attributes). If omitted, the current Unraid version is used.
+
   We look for the new plugin in /tmp/plugins/ directory.  If found then we first execute the "install"
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 62b7c68 and efa0685.

📒 Files selected for processing (1)
  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin (17 hunks)
🧰 Additional context used
🧠 Learnings (9)
📚 Learning: 2025-11-24T15:12:41.028Z
Learnt from: elibosley
Repo: unraid/webgui PR: 2464
File: emhttp/plugins/dynamix.plugin.manager/scripts/plugin:0-0
Timestamp: 2025-11-24T15:12:41.028Z
Learning: In emhttp/plugins/dynamix.plugin.manager/scripts/plugin, the $forced variable logic intentionally treats ANY option argument (beyond the explicitly handled ones like 'download-only', 'download', 'force', 'forced') as triggering forced install mode. This means that options like 'nchan' will cause $forced to be true. This is by design, even though it's unusual behavior.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-03-27T22:04:00.594Z
Learnt from: zackspear
Repo: unraid/webgui PR: 2099
File: emhttp/plugins/dynamix.my.servers/include/activation-code-extractor.php:58-74
Timestamp: 2025-03-27T22:04:00.594Z
Learning: The file `emhttp/plugins/dynamix.my.servers/include/activation-code-extractor.php` is synced from a different repository, and modifications should not be suggested in this repository's context. Changes should be proposed in the source repository instead.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-09-05T23:34:04.847Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2355
File: emhttp/plugins/dynamix.plugin.manager/include/PluginHelpers.php:21-28
Timestamp: 2025-09-05T23:34:04.847Z
Learning: In the Unraid webGUI plugin system, the static $methods array in PluginHelpers.php contains only methods that should bypass caching and call the plugin script directly. Methods like 'checkall' are handled at the PHP wrapper level via recursive calls to the plugin() function, not as direct subcommands in the plugin script itself.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-08-13T03:23:31.900Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2333
File: emhttp/plugins/dynamix.docker.manager/DockerContainers.page:6-6
Timestamp: 2025-08-13T03:23:31.900Z
Learning: In unraid/webgui codebase, Squidly271 prefers to maintain existing naming conventions even when there are technical inconsistencies (like Nchan="docker_load" vs actual channel "dockerload") if they've been stable for a long time, to avoid causing confusion by changing long-standing patterns.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-08-10T20:48:52.086Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2326
File: emhttp/plugins/dynamix.vm.manager/nchan/vm_usage:93-93
Timestamp: 2025-08-10T20:48:52.086Z
Learning: In the unraid/webgui codebase, Squidly271 prefers the publish() function to internally handle parameter type overloading (e.g., accepting boolean true in place of buffer length 1) to avoid repetitive parameter specifications and maintain backwards compatibility. The publish() function in emhttp/plugins/dynamix/include/publish.php is designed to sort out these parameters internally rather than requiring explicit values at every call site.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-09-05T16:33:12.970Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2353
File: emhttp/plugins/dynamix/DashStats.page:2257-2257
Timestamp: 2025-09-05T16:33:12.970Z
Learning: In the Unraid webGUI Nchan system, publisher scripts (like "update_1") publish to normalized endpoint names (like "update1"). The Nchan header lists the script names to start, while JavaScript subscribes to the published endpoints. For example: Nchan="update_1" starts the script which calls publish_noDupe('update1', data), and JavaScript subscribes to '/sub/update1'. This is the intended design, not a mismatch.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-09-05T16:33:12.970Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2353
File: emhttp/plugins/dynamix/DashStats.page:2257-2257
Timestamp: 2025-09-05T16:33:12.970Z
Learning: In the Unraid webGUI Nchan system, the Nchan header declaration lists publisher scripts to start (e.g., "update_1"), while the JavaScript subscription endpoints use normalized names (e.g., "/sub/update1"). The publisher script "update_1" publishes to "/pub/update1" endpoint, creating a separation between script names and published endpoints. This is by design and not a mismatch.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-10-03T02:57:29.994Z
Learnt from: ljm42
Repo: unraid/webgui PR: 2414
File: etc/rc.d/rc.nginx:374-376
Timestamp: 2025-10-03T02:57:29.994Z
Learning: Repo unraid/webgui: In etc/rc.d/rc.nginx, maintainers prefer not to add explicit mv-failure checks or EXIT trap clearing around atomic writes in build_servers(), build_locations(), and build_ini(); treat mv failures (e.g., disk full/permissions) as non-recoverable and keep the implementation simple.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-09-03T08:33:04.940Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2352
File: emhttp/plugins/dynamix.plugin.manager/include/PluginHelpers.php:0-0
Timestamp: 2025-09-03T08:33:04.940Z
Learning: In the unraid/webgui plugin system, basename comparison for cache invalidation in dropPluginCache() is considered sufficient for their plugin naming conventions and use cases.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)

@elibosley elibosley marked this pull request as ready for review November 25, 2025 17:06
elibosley and others added 10 commits November 25, 2025 12:06
…option, allowing users to download plugin files without executing installation commands. Update usage instructions and adjust processing logic accordingly.
… a dedicated 'download' command. Adjust processing logic and improve logging for clarity during plugin installation and downloading.
…y' variable with 'download_mode'. Update related conditions for clarity and consistency in plugin installation and downloading behavior.
…load_only' variable with 'download_mode'. Update related conditions for clarity and consistency in plugin installation and downloading behavior."

This reverts commit 3ac24e4.
…load_only' variable. Ensure pre and post hook execution respects the download-only mode, improving clarity and functionality during plugin installation and downloading.
…gs. Update logic to determine if a plugin is forced based solely on the presence of options, enhancing clarity in plugin processing.
…. Streamline the handling of options to enhance clarity and maintainability in plugin processing.
…djusted method flow to reuse installation steps for download requests, enhancing clarity and maintainability in plugin processing.
…gument parsing. Streamline the logic for determining forced plugin installations and improve clarity in command processing.
@elibosley elibosley force-pushed the feat/download-only-plugin-installs branch from efa0685 to 1dd843d Compare November 25, 2025 17:06
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
emhttp/plugins/dynamix.plugin.manager/scripts/plugin (2)

363-375: Consider logging skipped files even in download-only mode.

When $download_only is true, files that don't meet Min/Max version requirements are silently skipped (no log output). This could make troubleshooting difficult if a user expects certain files to be downloaded but they're filtered out by version checks.

Consider always logging these skips, perhaps with a prefix like "download:" to maintain consistency with other download-only log messages.

Apply this diff to always log version-based skips:

   $min = $file->attributes()->Min;
-  if ($min && version_compare($check_version,$min,'<')) {
-    if (!$download_only) {
-      write("plugin: skipping: ".basename($name)." - Unraid version too low, requires at least version $min\n");
-    }
+  if ($min && version_compare($check_version,$min,'<')) {
+    write("plugin: skipping: ".basename($name)." - Unraid version too low, requires at least version $min\n");
     continue;
   }
   $max = $file->attributes()->Max;
-  if ($max && version_compare($check_version,$max,'>')) {
-    if (!$download_only) {
-      write("plugin: skipping: ".basename($name)." - Unraid version too high, requires at most version $max\n");
-    }
+  if ($max && version_compare($check_version,$max,'>')) {
+    write("plugin: skipping: ".basename($name)." - Unraid version too high, requires at most version $max\n");
     continue;
   }

665-665: Remove redundant global declaration.

The global $check_version; declaration on line 665 is unnecessary because $check_version is already defined at the script level (line 516) in the same scope. In PHP, variables defined at the script level are already global and don't need to be re-declared with the global keyword within other script-level code blocks.

The global keyword is only needed when accessing script-level variables from within functions.

Apply this diff to remove the redundant declaration:

   }
-  // bergware - check Unraid version dependency (if present)
-  global $check_version;
+  // bergware - check Unraid version dependency (if present)
   $min = plugin('min', $plugin_file, $error);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between efa0685 and 1dd843d.

📒 Files selected for processing (1)
  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin (17 hunks)
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
Learnt from: elibosley
Repo: unraid/webgui PR: 2464
File: emhttp/plugins/dynamix.plugin.manager/scripts/plugin:0-0
Timestamp: 2025-11-24T15:12:41.028Z
Learning: In emhttp/plugins/dynamix.plugin.manager/scripts/plugin, the $forced variable logic intentionally treats ANY option argument (beyond the explicitly handled ones like 'download-only', 'download', 'force', 'forced') as triggering forced install mode. This means that options like 'nchan' will cause $forced to be true. This is by design, even though it's unusual behavior.
📚 Learning: 2025-11-24T15:12:41.028Z
Learnt from: elibosley
Repo: unraid/webgui PR: 2464
File: emhttp/plugins/dynamix.plugin.manager/scripts/plugin:0-0
Timestamp: 2025-11-24T15:12:41.028Z
Learning: In emhttp/plugins/dynamix.plugin.manager/scripts/plugin, the $forced variable logic intentionally treats ANY option argument (beyond the explicitly handled ones like 'download-only', 'download', 'force', 'forced') as triggering forced install mode. This means that options like 'nchan' will cause $forced to be true. This is by design, even though it's unusual behavior.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-03-27T22:04:00.594Z
Learnt from: zackspear
Repo: unraid/webgui PR: 2099
File: emhttp/plugins/dynamix.my.servers/include/activation-code-extractor.php:58-74
Timestamp: 2025-03-27T22:04:00.594Z
Learning: The file `emhttp/plugins/dynamix.my.servers/include/activation-code-extractor.php` is synced from a different repository, and modifications should not be suggested in this repository's context. Changes should be proposed in the source repository instead.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-09-05T23:34:04.847Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2355
File: emhttp/plugins/dynamix.plugin.manager/include/PluginHelpers.php:21-28
Timestamp: 2025-09-05T23:34:04.847Z
Learning: In the Unraid webGUI plugin system, the static $methods array in PluginHelpers.php contains only methods that should bypass caching and call the plugin script directly. Methods like 'checkall' are handled at the PHP wrapper level via recursive calls to the plugin() function, not as direct subcommands in the plugin script itself.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-08-13T03:23:31.900Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2333
File: emhttp/plugins/dynamix.docker.manager/DockerContainers.page:6-6
Timestamp: 2025-08-13T03:23:31.900Z
Learning: In unraid/webgui codebase, Squidly271 prefers to maintain existing naming conventions even when there are technical inconsistencies (like Nchan="docker_load" vs actual channel "dockerload") if they've been stable for a long time, to avoid causing confusion by changing long-standing patterns.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-08-10T20:48:52.086Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2326
File: emhttp/plugins/dynamix.vm.manager/nchan/vm_usage:93-93
Timestamp: 2025-08-10T20:48:52.086Z
Learning: In the unraid/webgui codebase, Squidly271 prefers the publish() function to internally handle parameter type overloading (e.g., accepting boolean true in place of buffer length 1) to avoid repetitive parameter specifications and maintain backwards compatibility. The publish() function in emhttp/plugins/dynamix/include/publish.php is designed to sort out these parameters internally rather than requiring explicit values at every call site.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-09-05T16:33:12.970Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2353
File: emhttp/plugins/dynamix/DashStats.page:2257-2257
Timestamp: 2025-09-05T16:33:12.970Z
Learning: In the Unraid webGUI Nchan system, publisher scripts (like "update_1") publish to normalized endpoint names (like "update1"). The Nchan header lists the script names to start, while JavaScript subscribes to the published endpoints. For example: Nchan="update_1" starts the script which calls publish_noDupe('update1', data), and JavaScript subscribes to '/sub/update1'. This is the intended design, not a mismatch.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-09-05T16:33:12.970Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2353
File: emhttp/plugins/dynamix/DashStats.page:2257-2257
Timestamp: 2025-09-05T16:33:12.970Z
Learning: In the Unraid webGUI Nchan system, the Nchan header declaration lists publisher scripts to start (e.g., "update_1"), while the JavaScript subscription endpoints use normalized names (e.g., "/sub/update1"). The publisher script "update_1" publishes to "/pub/update1" endpoint, creating a separation between script names and published endpoints. This is by design and not a mismatch.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-10-03T02:57:29.994Z
Learnt from: ljm42
Repo: unraid/webgui PR: 2414
File: etc/rc.d/rc.nginx:374-376
Timestamp: 2025-10-03T02:57:29.994Z
Learning: Repo unraid/webgui: In etc/rc.d/rc.nginx, maintainers prefer not to add explicit mv-failure checks or EXIT trap clearing around atomic writes in build_servers(), build_locations(), and build_ini(); treat mv failures (e.g., disk full/permissions) as non-recoverable and keep the implementation simple.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
📚 Learning: 2025-09-03T08:33:04.940Z
Learnt from: Squidly271
Repo: unraid/webgui PR: 2352
File: emhttp/plugins/dynamix.plugin.manager/include/PluginHelpers.php:0-0
Timestamp: 2025-09-03T08:33:04.940Z
Learning: In the unraid/webgui plugin system, basename comparison for cache invalidation in dropPluginCache() is considered sufficient for their plugin naming conventions and use cases.

Applied to files:

  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (10)
emhttp/plugins/dynamix.plugin.manager/scripts/plugin (10)

79-87: LGTM! Clear documentation for the download command.

The usage documentation accurately describes the download-only mode, including the optional TARGET-VERSION parameter and its purpose for preparing plugin files before an OS upgrade.


210-212: LGTM! Hooks appropriately skipped in download-only mode.

Returning early when $download_only is true ensures that pre-install and post-install hooks don't run during file-download-only operations, which is the correct behavior.


296-296: LGTM! Global variables properly declared.

The $download_only and $check_version globals are correctly declared for use throughout the plugin() function.


470-474: LGTM! Run commands properly skipped in download-only mode.

The logic correctly bypasses script execution when $download_only is true, while still logging the skip for debugging. The fallback to 'inline script' for unnamed files is a nice touch.


554-621: LGTM! Consistent script path handling.

The refactoring to use the $script variable throughout checkall, updateall, and checkos methods improves consistency and maintainability. Error messages also properly reference {$script}.


642-646: LGTM! Clear messaging for download-only mode.

The conditional logging and dynamic $action variable provide clear feedback to users about the mode of operation, making the download-only behavior explicit and understandable.


763-770: LGTM! Status messaging accurately reflects download-only semantics.

The conditional status messages ('downloaded'/'installed' for plugins, 'staged'/'executed' for scripts) accurately convey what operations were performed, providing clear user feedback.


832-836: LGTM! Consistent download-only integration in update method.

The update method properly integrates download-only mode with the same messaging patterns as the install method, maintaining consistency across the codebase.


886-888: LGTM! Status messaging mirrors install method.

The final status messages in the update method use the same pattern as the install method, maintaining consistency in how download-only operations are reported.


523-531: The usage is properly documented and displayed to users.

The review comment expresses concern that argument order handling "might be acceptable if documented." Verification confirms it IS documented:

  • Line 79 explicitly specifies the usage: Usage: plugin download PLUGIN-FILE [TARGET-VERSION] [forced]
  • Lines 545-548 show the usage text is displayed to users when the script is called without arguments
  • The code correctly implements this documented order (checking only the first $extra_args element)

The behavior is intentional and matches the documented specification. No action is needed.

Likely an incorrect or invalid review comment.

@ljm42 ljm42 added 7.3 and removed 7.3 labels Nov 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants