Skip to content

Add settings for custom gallery extension URLs - #14685

Merged
jstruzik merged 3 commits into
mainfrom
feature/custom-gallery-url
Jul 7, 2026
Merged

Add settings for custom gallery extension URLs#14685
jstruzik merged 3 commits into
mainfrom
feature/custom-gallery-url

Conversation

@jstruzik

@jstruzik jstruzik commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Reopening #14647 as a branch on the repo instead of a fork branch (now that I have write access). Original description below.


This PR does two things:

  • Adds support for pointing the extension gallery at a custom Open VSX-compatible server (for example a self-hosted Posit Package Manager), which previously was only possible via the undiscoverable EXTENSIONS_GALLERY environment variable (see EXTENSIONS_GALLERY is ignored when trying to set a custom gallery URL #13415)
    • A new Custom option activates a new Custom Gallery Url setting. Both the built-in presets and the custom URL now derive their full gallery endpoints from a single base URL through one deriveGalleryConfig() helper, so the Open VSX URL scheme lives in one place and presets and custom URLs cannot drift.
  • The second part fixes a pre-existing cross-gallery fallback: the resource ("unpkg") API in extensionGalleryService used the product-default gallery as a hard-coded fallback, so a request that failed against a non-default gallery would silently leak to the default gallery.

Here are some screenshots of the configuration and a request going through:
image
image

Release Notes

New Features

  • Add support for configuring a custom Open VSX-compatible extension gallery, such as a self-hosted Posit Package Manager, via the Extension Gallery Source and Custom Gallery Url settings.

Bug Fixes

  • Stop a non-default extension gallery (custom or Open VSX) from silently falling back to the default gallery when a resource request fails.

Validation Steps

  1. Open Settings and search for "Extension Gallery Source"; set it to Custom
  2. Set Custom Gallery Url to a valid Open VSX-compatible base URL, e.g. https://open-vsx.org/vscode. Restart, open the Extensions view, and confirm extensions load from the configured gallery
  3. Restart-prompt timing: with Source = Custom, edit the URL before selecting Custom (or select Custom with an empty URL) and confirm no premature restart prompt; the prompt appears only once the change resolves to a different gallery.
  4. Enter an unreachable URL (e.g. https://nope.invalid/vscode) and confirm a single warning notification (no spam while typing); enter https://user:pass@host/vscode and confirm it is rejected with no credentials shown.
  5. No cross-gallery leak: with Source = Custom pointed at an unreachable/erroring host, confirm requests do not fall back to p3m.dev (check the Network tab / Dev Tools); the failure stays within the configured gallery.
  6. If EXTENSIONS_GALLERY is set to a valid value, confirm changing the settings shows the "environment variable is overriding" notification and does not restart.
  7. Switch back to Posit Public Package Manager and confirm extensions still load.

jstruzik added 3 commits June 30, 2026 15:01
Add a Custom option to the extension gallery source setting plus a
Custom Gallery Url setting. Presets and custom URLs derive their full
gallery endpoints from one deriveGalleryConfig() (single source of
truth); base URLs are canonicalized (credentials, query, and fragment
are rejected). An advisory probe validates the configured gallery on
startup and on change (debounced, warn-once, credential-safe). The
restart prompt fires only when the resolved gallery actually changes.
extensionGalleryService's resource (unpkg) API used the product default
gallery (product.json, i.e. p3m.dev) as a hard-coded fallback, so a
request that failed against a non-default gallery selected via
positron.extensions.gallerySource -- a custom or Open VSX gallery --
silently leaked to the default gallery. For example, a custom gallery
returning 5xx would fall back to p3m.dev and serve its extensions
instead. Only use the product fallback when it targets the same gallery
host as the resolved resource (sameGalleryHost).
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

E2E Tests 🚀
This PR will run tests tagged with: @:critical

readme  valid tags

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

PETE's assessment 🧪

Verdict: 🟢 Adequate -- Every substantive new unit (deriveGalleryConfig, resolvePositronGalleryConfig's custom path, sameGalleryHost, the probe helpers, and the validation contribution) ships with focused Vitest coverage at the cheapest viable level.

What changed

  • Adds a Custom gallery source plus a positron.extensions.customGalleryUrl setting; all galleries (presets + custom) now derive their endpoints from one base URL via deriveGalleryConfig(), and resolvePositronGalleryConfig() gains a customGalleryUrl argument.
  • Fixes a cross-gallery leak in extensionGalleryService.getResources() -- the product-default resource ("unpkg") fallback is now gated by sameGalleryHost().
  • Adds an advisory reachability probe (positronCustomGalleryProbe.ts + PositronCustomGalleryValidation contribution) and restart-gating in both the browser and electron-browser manifest services (restart only when the resolved serviceUrl actually changes).

Tests in this PR

  • Unit (Vitest/Mocha) ✅ (added positronCustomGalleryProbe.vitest.ts, positronCustomGalleryValidation.vitest.ts; updated positronGalleryManifestService.vitest.ts for deriveGalleryConfig/sameGalleryHost/custom-source)
  • Extension host ✅ (not applicable -- no extension-host behavior)
  • E2E (Playwright) ✅ (not warranted -- logic is reachable at unit level; a real gallery switch requires restart and is covered by the manual Validation Steps)

Existing coverage

  • deriveGalleryConfig, resolvePositronGalleryConfig (incl. custom-valid/blank/invalid, env-wins, and the no-op-vs-real-change resolution property), and sameGalleryHost are directly asserted in src/vs/platform/extensionManagement/test/common/positronGalleryManifestService.vitest.ts.
  • The probe decision logic (interpretProbeResult, WarnOnceCache, redactUrlForDisplay, buildProbeQueryBody) and the contribution's debounce/collapse + not-custom-skips behavior are covered by the two new positronCustomGalleryProbe* test files.
  • Note (not a gap sufficient to fail): the getResources() unpkg-fallback wiring and the service-level restart gating (activeGalleryServiceUrl capture -> conditional requestRestart) in both browser/ and electron-browser/extensionGalleryManifestService.ts are only covered indirectly -- via the sameGalleryHost/resolvePositronGalleryConfig unit tests. The existing test/browser/extensionGalleryManifestService.vitest.ts was not extended to fire a customGalleryUrl change and assert the no-op path skips restart.

Suggested additions

None required. Optional hardening: extend src/vs/workbench/services/extensionManagement/test/browser/extensionGalleryManifestService.vitest.ts (Vitest -- the harness already stubs config/dialog/host services) with a case that fires a PositronCustomGalleryUrlConfigKey change while the source is a preset and asserts dialogService.confirm is not called, pinning the new activeGalleryServiceUrl restart-gating end-to-end rather than only at the resolver level.

Deployment note (optional)

The restart-gating logic is duplicated across the browser/ and electron-browser/ manifest services, and both were edited in parallel. The pure resolution/derivation logic they call is surface-agnostic and well covered, so this isn't a direct surface gap -- but the service-integration path (config change -> resolve -> conditional restart) is currently only unit-verified on the resolver, not per service. If either implementation drifts, the shared unit tests won't catch it; the optional browser-service test above would guard the web path, and an equivalent for the electron-browser service would guard desktop.


PETE (Positron Extreme Test Experiment) - LLM-based test-coverage advisor, in pilot. Triggers on PR open and on /recheck-tests comments. Wrong verdict? Comment /recheck-tests (or /rePETE) on this PR to re-run. Please share feedback on how PETE performed here.

@jstruzik

jstruzik commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

I have read the CLA Document and I hereby sign the CLA

@jstruzik
jstruzik requested a review from timtmok July 6, 2026 18:29
@jstruzik

jstruzik commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Requesting @timtmok for a review since I think you've worked in this area the most? Feel free to reassign as needed!

@timtmok timtmok left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@jstruzik
jstruzik merged commit baa6ce8 into main Jul 7, 2026
28 of 29 checks passed
@jstruzik
jstruzik deleted the feature/custom-gallery-url branch July 7, 2026 00:13
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 7, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants