Added custom plugins manifest URL option for self hosting - #4585
Added custom plugins manifest URL option for self hosting#4585mrmetrix4 wants to merge 11 commits into
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Included review availability: Your plan includes up to 10 reviews per rolling hour; 9 remain after this review. 📝 WalkthroughWalkthrough
ChangesPlugins manifest URL configuration
Estimated code review effort: 3 (Moderate) | ~20 minutes Merge Risk: ⚪ Minimal · up to This change adds an optional custom plugins manifest URL while preserving default fallback behavior; no actionable merge-blocking risk remains. Sequence Diagram(s)sequenceDiagram
participant SettingsUI
participant PublicAPIInstance
participant PluginsManifest
participant PluginStores
SettingsUI->>PublicAPIInstance: set PluginsManifestUrl
PublicAPIInstance->>PluginsManifest: UpdateManifestAsync(_settings, ...)
PluginsManifest->>PluginStores: construct or reuse configured or default store
PluginsManifest->>PluginStores: fetch and update manifest
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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)
Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs (1)
15-41: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy liftMake manifest state source-aware and synchronize store selection.
mainPluginStoreis process-wide, but Lines 24-41 overwrite it from each call’s settings before acquiringmanifestUpdateLock. Concurrent callers can therefore fetch through another caller’s store. Also,UserPluginsandlastFetchedAtare not keyed bycustomUrl, so changing the setting within the two-minute window can leave the previous manifest in use. Keep the selected store local or initialize it under the lock, and invalidate/key the cache by URL.🤖 Prompt for 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. In `@Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs` around lines 15 - 41, Make UpdateManifestAsync source-aware by deriving the CommunityPluginStore from the current customUrl inside the manifestUpdateLock or keeping it local, rather than overwriting shared mainPluginStore before synchronization. Key UserPlugins and lastFetchedAt by the effective manifest URL, and invalidate or bypass the cached manifest when customUrl changes so each settings source fetches and uses its own data.
🤖 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 `@Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs`:
- Around line 26-37: Validate PluginsManifestUrl before constructing
mainPluginStore: when customUrl is non-empty, require it to be a supported
absolute HTTPS URL and handle invalid input without allowing the constructor to
escape UpdateManifestAsync’s error path. Alternatively, move the new
plugin-store construction into the existing guarded section, preserving the
default mainUrls behavior.
---
Outside diff comments:
In `@Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs`:
- Around line 15-41: Make UpdateManifestAsync source-aware by deriving the
CommunityPluginStore from the current customUrl inside the manifestUpdateLock or
keeping it local, rather than overwriting shared mainPluginStore before
synchronization. Key UserPlugins and lastFetchedAt by the effective manifest
URL, and invalidate or bypass the cached manifest when customUrl changes so each
settings source fetches and uses its own data.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: d33357ed-31da-4f3e-8fa9-7a24de1f7e99
📒 Files selected for processing (6)
Flow.Launcher.Core/ExternalPlugins/PluginsManifest.csFlow.Launcher.Infrastructure/Constant.csFlow.Launcher.Infrastructure/UserSettings/PluginSettings.csFlow.Launcher/Languages/en.xamlFlow.Launcher/PublicAPIInstance.csFlow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml
There was a problem hiding this comment.
All reported issues were addressed across 6 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 3 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Ensures plugin data is refreshed by setting lastFetchedAt to DateTime.MinValue after lastCustomUrl changes, preventing skipped fetches due to stale timestamps.
Removed resetting customUrl to empty string when an invalid custom plugin manifest URL is detected. Now, the code logs a warning and initializes mainPluginStore with default URLs, preserving the original customUrl value.
|
@codex Review it |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6e2b8c80f5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
Adds a configurable plugin manifest URL for self-hosted or unofficial plugin catalogs.
Changes:
- Adds and persists a custom manifest URL setting.
- Selects custom or official manifest sources and refreshes changed sources.
- Exposes the setting in the General settings UI.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
SettingsPaneGeneral.xaml |
Adds the manifest URL input. |
PublicAPIInstance.cs |
Passes settings during manifest updates. |
en.xaml |
Adds the setting label. |
PluginSettings.cs |
Persists the custom URL. |
Constant.cs |
Contains a non-functional encoding change. |
PluginsManifest.cs |
Selects, validates, and refreshes manifest sources. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Refactored variable declarations to use var instead of explicit types for lockAcquired, defaultUrls, and customUrl. Moved customUrl initialization to after lock acquisition. Changed defaultUrls array declaration to use var and curly braces.
Now triggers RefreshExternalPluginsCommand when navigating to SettingsPanePluginStore, ensuring manifest URL changes are reflected immediately. Manifest service fetches new data only if cache is invalid or URL has changed.
The TextBox's Text property now updates the bound PluginsManifestUrl setting immediately as the user types, instead of only on focus loss, by setting UpdateSourceTrigger=PropertyChanged.
Added ManifestFileUrlForLogging to CommunityPluginSource, which returns a sanitized manifest URL for logging by removing credentials, query, and fragments. Updated all logging to use this property. Introduced SanitizeUrlForLogging helper method. Added CommunityPluginSourceTest to verify correct URL sanitization and ensure the original URL remains unchanged.
|
@codex Review it |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9f3ef81185
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (mainPluginStore == null || lastCustomUrl != customUrl) | ||
| { |
There was a problem hiding this comment.
Clear cached plugins when switching manifest sources
When the setting changes from the default or a previous custom source, this replaces mainPluginStore but leaves UserPlugins populated. If the new endpoint is unavailable, returns a non-success response, or serves an empty manifest, the fetch returns false while callers such as CheckForPluginUpdatesAsync ignore that result and continue consuming the old entries and download URLs through GetPluginManifest(). Clear or segregate the cached plugins when the source identity changes so selecting a private catalog cannot silently leave the previous catalog active.
Useful? React with 👍 / 👎.
| } | ||
| else | ||
| { | ||
| PublicApi.Instance.LogWarn(ClassName, $"Invalid custom plugins manifest URL: {customUrl}. Using default URLs."); |
There was a problem hiding this comment.
Sanitize the invalid manifest URL warning
When the user enters a rejected URL containing credentials or a token, such as ftp://user:password@example.com/plugins.json?token=secret, this warning writes the complete value to persistent diagnostic logs. Fresh evidence in this revision is that the source-fetch log paths are now sanitized, but this validation path still interpolates the raw setting; apply the same sanitization or omit the URL from this warning.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs:64
- Changing the configured source replaces the store but leaves
UserPluginsfrom the previous source intact. If the first fetch from the new/default source fails or returns no entries,UpdateManifestAsyncreturnsfalseand the view model does not notify the UI, so users can continue seeing and installing catalog entries from a source they have deselected. Cache entries must be associated with their source; clear/replace the old catalog on a source switch and notify the store UI even when the new fetch cannot populate it.
lastCustomUrl = customUrl;
lastFetchedAt = DateTime.MinValue;
| } | ||
| else | ||
| { | ||
| PublicApi.Instance.LogWarn(ClassName, $"Invalid custom plugins manifest URL: {customUrl}. Using default URLs."); |
There was a problem hiding this comment.
2 issues found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml">
<violation number="1" location="Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml:512">
P2: This binding only writes the typed URL into the settings object; nothing reacts to PluginsManifestUrl changing, so the plugin catalog is not refreshed when the user edits the URL (UpdatePluginManifestAsync is only called at startup, from the Plugin Store refresh button, and from the plugins settings pane). The user keeps seeing the stale catalog until a manual refresh or restart. Also, with UpdateSourceTrigger=PropertyChanged each keystroke commits a partial URL into the in-memory settings, which is then persisted when the settings window closes (SettingWindow.xaml.cs OnClosed calls _settings.Save()), so half-typed values can be written to disk.</violation>
</file>
<file name="Flow.Launcher.Test/CommunityPluginSourceTest.cs">
<violation number="1" location="Flow.Launcher.Test/CommunityPluginSourceTest.cs:18">
P1: This expected value will not match the implementation. `ManifestFileUrlForLogging` builds it with `UriComponents.SchemeAndServer | UriComponents.Path`, but `SchemeAndServer` (0xF) includes `UserInfo`, so `username:password@` is kept and the sanitized URL is `https://username:password@example.com:8443/private/plugins.json`. The assertion fails, and, worse, the commit's goal of protecting credentials in logs is not met. Fix `SanitizeUrlForLogging` to exclude `UserInfo` by using `UriComponents.Scheme | UriComponents.Host | UriComponents.Port | UriComponents.Path`, then this expected value becomes correct.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
|
|
||
| ClassicAssert.AreEqual(manifestUrl, source.ManifestFileUrl); | ||
| ClassicAssert.AreEqual( | ||
| "https://example.com:8443/private/plugins.json", |
There was a problem hiding this comment.
P1: This expected value will not match the implementation. ManifestFileUrlForLogging builds it with UriComponents.SchemeAndServer | UriComponents.Path, but SchemeAndServer (0xF) includes UserInfo, so username:password@ is kept and the sanitized URL is https://username:password@example.com:8443/private/plugins.json. The assertion fails, and, worse, the commit's goal of protecting credentials in logs is not met. Fix SanitizeUrlForLogging to exclude UserInfo by using UriComponents.Scheme | UriComponents.Host | UriComponents.Port | UriComponents.Path, then this expected value becomes correct.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Flow.Launcher.Test/CommunityPluginSourceTest.cs, line 18:
<comment>This expected value will not match the implementation. `ManifestFileUrlForLogging` builds it with `UriComponents.SchemeAndServer | UriComponents.Path`, but `SchemeAndServer` (0xF) includes `UserInfo`, so `username:password@` is kept and the sanitized URL is `https://username:password@example.com:8443/private/plugins.json`. The assertion fails, and, worse, the commit's goal of protecting credentials in logs is not met. Fix `SanitizeUrlForLogging` to exclude `UserInfo` by using `UriComponents.Scheme | UriComponents.Host | UriComponents.Port | UriComponents.Path`, then this expected value becomes correct.</comment>
<file context>
@@ -0,0 +1,21 @@
+
+ ClassicAssert.AreEqual(manifestUrl, source.ManifestFileUrl);
+ ClassicAssert.AreEqual(
+ "https://example.com:8443/private/plugins.json",
+ source.ManifestFileUrlForLogging);
+ }
</file context>
| <TextBox | ||
| Width="370" | ||
| Height="34" | ||
| Text="{Binding Settings.PluginSettings.PluginsManifestUrl, TargetNullValue='None', UpdateSourceTrigger=PropertyChanged}" /> |
There was a problem hiding this comment.
P2: This binding only writes the typed URL into the settings object; nothing reacts to PluginsManifestUrl changing, so the plugin catalog is not refreshed when the user edits the URL (UpdatePluginManifestAsync is only called at startup, from the Plugin Store refresh button, and from the plugins settings pane). The user keeps seeing the stale catalog until a manual refresh or restart. Also, with UpdateSourceTrigger=PropertyChanged each keystroke commits a partial URL into the in-memory settings, which is then persisted when the settings window closes (SettingWindow.xaml.cs OnClosed calls _settings.Save()), so half-typed values can be written to disk.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml, line 512:
<comment>This binding only writes the typed URL into the settings object; nothing reacts to PluginsManifestUrl changing, so the plugin catalog is not refreshed when the user edits the URL (UpdatePluginManifestAsync is only called at startup, from the Plugin Store refresh button, and from the plugins settings pane). The user keeps seeing the stale catalog until a manual refresh or restart. Also, with UpdateSourceTrigger=PropertyChanged each keystroke commits a partial URL into the in-memory settings, which is then persisted when the settings window closes (SettingWindow.xaml.cs OnClosed calls _settings.Save()), so half-typed values can be written to disk.</comment>
<file context>
@@ -509,7 +509,7 @@
Width="370"
Height="34"
- Text="{Binding Settings.PluginSettings.PluginsManifestUrl,TargetNullValue='None'}" />
+ Text="{Binding Settings.PluginSettings.PluginsManifestUrl, TargetNullValue='None', UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</ui:SettingsCard>
</file context>
Jack251970
left a comment
There was a problem hiding this comment.
Please resolve the comments from AI
As talked in a lot of issues
Fix #2178
Summary by cubic
Summary of changes
Adds a configurable custom plugins manifest URL for self-hosting and refreshes the catalog when the URL changes. Previously Flow Launcher used hard-coded mirrors only; now it accepts a user-provided URL, sanitizes URLs in logs, and refreshes the store on opening the Plugin Store page.
PluginsManifest.UpdateManifestAsyncnow requiresSettings;PublicAPIInstance.UpdatePluginManifestAsyncpasses_settings.CommunityPluginStoreonly whenPluginSettings.PluginsManifestUrlchanges; resetlastFetchedAttoDateTime.MinValueto force a refetch; reuse the instance otherwise; default manifest URLs are consolidated locally.RefreshExternalPluginsCommandso a new URL is picked up without restart; the General settings TextBox updatesPluginsManifestUrlon each keystroke.PluginSettings.PluginsManifestUrland XAML binding in General settings;pluginManifestUrllocale string.CommunityPluginSource.ManifestFileUrlForLoggingto omit credentials, query, and fragments.CommunityPluginSourceTestto verify URL sanitization; manual verification for default behavior, invalid URL fallback, and refresh on navigation/URL change.Release Note
You can now set your own plugins catalog URL; Flow Launcher refreshes the list when you open the Plugin Store and falls back to the official catalog if your URL is empty or invalid.
Written for commit 9f3ef81. Summary will update on new commits.