feat(registry): support multiple plugin and theme registries via env vars - #2972
Open
louzt wants to merge 3 commits into
Open
feat(registry): support multiple plugin and theme registries via env vars#2972louzt wants to merge 3 commits into
louzt wants to merge 3 commits into
Conversation
Resolves AvengeMedia#2763 — both plugin and theme registries now read from a comma-separated list of git URLs (DMS_PLUGIN_REGISTRIES, DMS_THEME_REGISTRIES) instead of a single hardcoded repo. Each registry clones/pulls into its own subdir under the cache base; results aggregate in declaration order. Backwards compatible: when the env var is unset/empty/all-blank, defaults to the existing single official registry. Existing callers of NewRegistry() inherit the new behavior transparently. GetThemeSourcePath/GetThemeDir now search all registered registries and return the first match (falls back to first registry if none have the theme), so multi-registry themes resolve correctly. Tests: - TestParseRegistriesFromEnv covers unset/whitespace, multi-URL, trim, empty entries, all-empty fallback (both packages) - TestUpdate gains 'aggregates from multiple registries' subtest - loadPlugins/loadThemes split into per-dir + Update-loop aggregate so each registry's cache is read independently No push — local only, awaiting maintainer response on AvengeMedia#2763 before opening upstream PR.
Two follow-ups to the multi-registry refactor, addressing issues raised in
self-review before opening the upstream PR:
1. Declaration-order ID dedupe. Update() now keeps the first occurrence of
each plugin/theme ID and discards duplicates from later registries. List,
Search, Get all see one plugin per ID. Tests: TestUpdate/dedupes_by_ID
with declaration_order_priority (plugins).
2. Legacy cache migration. Update() now moves <base>/plugins/ to
<base>/official/plugins/ (and <base>/themes/ to <base>/official/themes/)
on first run, idempotent. Without this, every existing user re-clones
the official registry on first Update after upgrade. Tests:
TestUpdate/migrates_legacy_cache_directory + no-op_migration_when_legacy_cache_absent
(plugins).
Also wraps per-registry errors with the registry name ("registry <name>: %w")
so triage is one grep away.
Plugins only — themes inherit the same shape but tests cover the
migration in a follow-up to keep this commit focused.
No build, no test regressions. No push (stage-local-first).
Parallel coverage to the plugins registry migration test. Seed <base>/themes/example/theme.json as the pre-refactor layout, run Update(), and assert the theme resolves at <base>/official/themes/example/ after migration. Idempotent: fresh installs skip the migration entirely.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat(registry): support multiple plugin and theme registries via env vars
Closes #2763.
The plugin and theme registries currently hardcode a single git repo
(
registryRepoconst, cloned into a single cache dir). This blocks thecommon case of users who maintain a personal or community registry
alongside the official one. This PR adds multi-registry support driven by
two env vars and keeps the default behavior identical for users who don't
opt in.
What this PR does
core/internal/plugins/registry.goandcore/internal/themes/registry.gonow hold
[]RegistryConfig{Name, URL}instead of a single hardcoded URL.ParseRegistriesFromEnv()readsDMS_PLUGIN_REGISTRIES/DMS_THEME_REGISTRIES(comma-separated git URLs, trimmed, empty entriesskipped). Defaults to the official
AvengeMedia/dms-plugin-registry.gitwhen unset, whitespace-only, or all-empty.
Update()iterates over registries, clones/pulls each into its own<cacheBase>/<name>/subdir, and aggregates results in declaration order.First occurrence of each plugin/theme ID wins — duplicates from
later registries are silently dropped, so a personal registry cannot
shadow the official one by accident.
migrateLegacyCache()runs once perUpdate()to move the pre-refactorflat
<base>/plugins/(and<base>/themes/) into the new<base>/official/layout. Idempotent. Without it, every existinguser re-clones the official registry on first Update after upgrade.
loadPluginsFrom(dir)/loadThemesFrom(dir)extracted from themonolithic loader so each registry's cache is read independently.
GetThemeSourcePath/GetThemeDirnow search all registries (first hitwins, fallback to first registry) so multi-registry themes resolve to the
right path.
(
fmt.Errorf("registry %s: %w", cfg.Name, err)) so triage is one grep away.Decisions taken
DMS_*overridepattern (
DMS_SOCKET,DMS_DEBUG); avoids introducing a new on-diskformat for a feature that may not need persistence yet. A future PR
could add
~/.config/dms/registries.tomlfor users who want namedregistries (
louzt,nix-community) with the same semantics.r0/r1/...from index. Keeps the env var syntax to URLsonly. If the maintainer prefers explicit names, a one-line parser change
(
name|url) is enough — happy to ship that as a follow-up.Update()aggregation and
GetThemeSourcePathresolution. Nodisabled: boolfield yet — adding it later is additive (defaults to false).
officialis the only stable name. When users setDMS_PLUGIN_REGISTRIES=https://github.com/foo/bar.git, the cache dir is<base>/r0. Theofficialname only applies to the default fallback sousers can layer a personal registry on top without conflicting with the
default's stable name.
Scope boundary
This PR does NOT:
registries.toml) — see Decisions, follow-up.disabled/ per-registry priority flag — additive, follow-up.theme.json discovery) — only how the source list is composed.
separate concerns.
Validation
Themes have parallel coverage (
TestParseRegistriesFromEnvwith threesubtests: unset, comma-separated, all-empty fallback).
Existing tests for
TestUpdate,TestList, andTestLoadPluginswereupdated to the new method signatures; new subtests exercise the
multi-registry path end-to-end with a mock git client:
TestUpdate/aggregates_from_multiple_registries— official + louzt, both contributeTestUpdate/dedupes_by_ID_with_declaration_order_priority— first registry winsTestUpdate/migrates_legacy_cache_directory—<base>/plugins/→<base>/official/plugins/TestUpdate/no-op_migration_when_legacy_cache_absent— fresh install skips migrationTestUpdateMigration/migrates_legacy_themes_cache— themes parallel coverageDiffstat
Three logical commits on
feat/registry-list-config(master..HEAD):86f7550b— feat(registry): support multiple plugin/theme registries via envde79ae8f— feat(registry): dedupe by ID + migrate legacy cacheed7511ec— test(themes): cover legacy cache migration (parallel coverage)Two packages, one logical change. Plugins and themes share no code; this
PR keeps that property (no shared
registrieshelper package introducedyet — easy to extract later if a third caller appears).
Backwards compatibility
DMS_PLUGIN_REGISTRIESandDMS_THEME_REGISTRIESunset → singleofficial registry, identical to the previous behavior. Existing CLI
flags, config keys, and widget bindings are unchanged.
Cache dir moves from
<base>/plugins/to<base>/official/plugins/on first
Update()after this PR. The migration helper(
migrateLegacyCache) renames the existing flat layout in place; if nolegacy dir is present, it's a no-op. This is invisible for users on a
fresh install and recovers a fast-forward pull for users on an upgrade.
Why this is the right shape for #2763
Issue #2763 asks for "support custom plugin and theme registries". The
env-var approach:
DMS_PLUGIN_REGISTRIES=$WORK_REGISTRY dms).a
disabledflag is additive, named registries are a parser tweak.