Skip to content

Replace hardcoded known-packages list with live Hex.pm fetch - #523

Merged
ddon merged 3 commits into
BeamLabEU:devfrom
timujinne:feature/known-packages-live-fetch
May 8, 2026
Merged

Replace hardcoded known-packages list with live Hex.pm fetch#523
ddon merged 3 commits into
BeamLabEU:devfrom
timujinne:feature/known-packages-live-fetch

Conversation

@timujinne

Copy link
Copy Markdown
Contributor

Summary

Replaces the hardcoded list of known external PhoenixKit packages in ModuleRegistry.known_external_packages/0 with a live Hex.pm fetch in a new PhoenixKit.KnownPackages module, cached in :persistent_term for 10 minutes. The list now reflects whatever is actually published under the phoenix_kit_* prefix on Hex.

Architecture

  • Live fetch from Hex.pm with Link-header pagination, filtered to phoenix_kit_* packages (skipping phoenix_kit itself).
  • 10-min in-memory cache via :persistent_term, hardcoded as a module attribute (no per-app retuning needed for an admin page).
  • On Hex unreachable: returns stale cached data when available (stale-while-revalidate), otherwise extras-only with Logger.warning.
  • No negative cache, no per-call config knobs, no validation of parent-app config: malformed extras crash loudly on use, which is louder and more honest than silently dropping entries with a log line.

Convention introduced

Module authors opt into a custom catalog icon by appending hex_docs_icon_name: hero-<name> to their package description on Hex. The marker is stripped from the displayed text and the icon name is used in the catalog UI. Default: hero-puzzle-piece.

Parent-app override

config:phoenix_kit,extra_known_packages: [%{key: "private_billing",package: "my_app_private_billing",name: "Private Billing",description: "Internal billing fork",icon: "hero-credit-card",hex_url: nil}]

Records get source: "config" automatically and win over Hex entries with the same package on dedup.

Catalog entry shape

%{key,package,name,description,icon,hex_url,source}

Removed from the previous shape: module: (atom — UI never used it), hex_package: (renamed to package:), github_url:, latest_version:. No in-repo callers of the removed fields remain.

Test plan

  • mix format --check-formatted clean
  • mix credo --strict clean
  • mix dialyzer clean
  • 11 tests in known_packages_test.exs (happy path, cache hit, cache clear, Hex 500, transport error, pagination, extras config valid + wins-over-Hex)
  • module_registry_test.exs regression suite for not_installed_packages/0
  • Live verified on a parent app: list/0 returns ~20 packages from Hex (more than the previous hardcoded 14); not_installed_packages/0 filters installed deps correctly

timujinneand others added 3 commits May 8, 2026 07:24
Replaces the hardcoded list of known external PhoenixKit packages in
ModuleRegistry.known_external_packages/0 with a live Hex.pm fetch in
a new PhoenixKit.KnownPackages module, cached in :persistent_term for
10 minutes. The list now reflects whatever is actually published
under the phoenix_kit_* prefix on Hex.
## Architecture
- Live fetch from Hex.pm with Link-header pagination, filtered to
phoenix_kit_* packages (skipping phoenix_kit itself).
- 10-min in-memory cache via :persistent_term, hardcoded as a module
attribute.
- On Hex unreachable: returns stale cached data when available
(stale-while-revalidate), otherwise extras-only with Logger.warning.
- No negative cache, no per-call config knobs, no validation of
parent-app config: malformed extras crash loudly on use, which is
louder and more honest than silently dropping entries with a log.
## Convention introduced
Module authors opt into a custom catalog icon by appending
hex_docs_icon_name: hero-<name> to their package description on Hex.
The marker is stripped from the displayed text and the icon name is
used in the catalog UI. Default: hero-puzzle-piece.
## Parent-app override
config :phoenix_kit,
extra_known_packages: [
%{key: "private_billing",
package: "my_app_private_billing",
name: "Private Billing",
description: "Internal billing fork",
icon: "hero-credit-card",
hex_url: nil}
]
Records get source: "config" automatically and win over Hex entries
with the same package on dedup.
## Catalog entry shape
%{key, package, name, description, icon, hex_url, source}
Removed from previous shape: module: (atom — UI never used it),
hex_package: (renamed to package:), github_url:, latest_version:.
## Test plan
- mix format / mix credo --strict / mix dialyzer all clean.
- 11 tests: happy path, cache hit, cache clear, Hex 500, transport
error, pagination, extras config (valid + wins-over-Hex).
- Live verified on a parent app: list/0 returns ~20 packages from
Hex; not_installed_packages/0 filters installed deps correctly.
…elds, UI guard
Independent Opus review of PR BeamLabEU#523 found critical issues; all addressed
except CHANGELOG (maintainer-owned).
- Restore API fields removed in earlier simplification: module (atom from
package-name heuristic), hex_package (alias for package), github_url
(BeamLabEU heuristic when Hex meta has no link), latest_version (from
Hex meta). Entry shape is now backward-compatible with the previous
hardcoded list — parent apps depending on these fields no longer break.
- Replace :persistent_term cache with ETS named_table (read_concurrency,
race-safe ensure_table). Avoids global-GC write amplification on
multi-node cold deploys (every persistent_term write triggers global
GC; ETS does not).
- Add @max_stale_age_ms cap (24h). On Hex failure with cached data:
serve stale only if within cap (warning log with age=Xm); otherwise
drop, log error with age=Xh, return extras-only. Prevents indefinite
stale serving when Hex stays down.
- Differentiate logging by failure mode: cache miss + Hex fail (warning,
no cache message), serving stale (warning + age), stale beyond cap
(error + age).
- Remove Application.get_env :_known_packages_req_opts seam: replace
with public list/1 opts arg (:req_options, :ttl_ms,
:max_stale_age_ms). Tests pass options directly; no global config
knob can be silently overridden by other libs in production.
- Fix /admin/modules card crash when pkg.hex_url is nil: wrap the
Hex.pm link with :if guard. Previously rendered <a href=""> for
config-source entries without hex_url.
- Tests: add stale-return-within-cap, stale-dropped-beyond-cap,
malformed JSON (200 + non-list), module/hex_package/github_url/
latest_version regression, all-11-keys-present.
- Tests: replace Application.put_env seam with opts-arg; setup just
clears cache and restores extras config.
@ddon
ddon merged commit 95b6218 into BeamLabEU:devMay 8, 2026
ddon added a commit that referenced this pull request May 8, 2026
…525
Code/doc fixes addressing one finding per PR (or several where
trivial). Each closes a NITPICK or IMPROVEMENT-LOW from the
matching CLAUDE_REVIEW.md; design-level / breaking / risky items
deferred per the FOLLOW_UP.md "Skipped" sections.
- #516: Drop dead `String.to_atom` fallback in OAuth interpolate_url
- #518: Delete stray 0-byte pages_html.ex
- #519: Fix stale `viewer={true}` template comment + login_path
trailing-slash self-loop guard
- #521: Resolution-order doc on permission_key_for_admin_view/1
- #522: Hot-reload safety pitfall in per-module-i18n.md
- #523: KnownPackages — max-pages cap, ensure_table race comment,
Logger-levels operational signals in moduledoc
- #524: __mix_recompile__?/0 note next to apply/3 explanation
- #525: LanguageSwitcher attr doc atom/string keys + DRY resolve_url
per-language + JS sortable:flash defensive status check
Plus FOLLOW_UP.md per PR enumerating closed vs deferred items.
PR #525's FOLLOW_UP also captures the bundled DnD audit trail
(table_default drag-handle scoping, sortable:flash, TR cell-width
preservation) absent from the original PR body.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ddon added a commit that referenced this pull request May 8, 2026
Three-day window of accumulated work since 1.7.105 (2026-05-05):
PRs #516, #518, #519, #521, #522, #523, #524, #525, plus the
review-doc suite and post-merge triage closing nitpicks across all
eight.
Headline changes — V111 PDF library tables, DB module extracted to
phoenix_kit_db, MediaBrowser modal viewer, sidebar gettext API, live
Hex.pm catalog, publishing routing-strategy shim closing the
/:locale/<literal>/... host-route shadowing bug, LanguageSwitcher
:per_translation_urls, and bundled DnD improvements (drag-handle
scoping, sortable:flash, TR cell-width preservation).
All changes are strictly additive / non-breaking; one transitional
extraction (DB → phoenix_kit_db) requires the paired Hex package
once it ships.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@timujinne
timujinne deleted the feature/known-packages-live-fetch branch June 25, 2026 08:42
Sign up for freeto 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.

2 participants

@timujinne@ddon