feat(admin): core update notice in the admin dashboard (WordPress-style version banner)#1939
feat(admin): core update notice in the admin dashboard (WordPress-style version banner)#1939swissky wants to merge 3 commits into
Conversation
WordPress-style "a new version is available" awareness (Discussion emdash-cms#1889). The server caches the latest published version from the npm registry in the options table — checked at most once per day, always deferred via after() so no request blocks on the network — and a new admin-only endpoint (GET /_emdash/api/admin/core-update) serves it to the dashboard, which shows a dismissible banner (per version) linking to the release notes. Opt out with updateCheck: false in config.
🦋 Changeset detectedLatest commit: dbe9979 The changes in this PR will be included in the next version bump. This PR includes changesets to release 16 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Scope checkThis PR changes 515 lines across 13 files. Large PRs are harder to review and more likely to be closed without review. If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs. See CONTRIBUTING.md for contribution guidelines. |
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/auth-atproto
@emdash-cms/blocks
@emdash-cms/cloudflare
@emdash-cms/contentful-to-portable-text
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/plugin-cli
@emdash-cms/plugin-types
@emdash-cms/registry-client
@emdash-cms/registry-lexicons
@emdash-cms/sandbox-workerd
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-field-kit
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
There was a problem hiding this comment.
The approach is sensible and matches the approved Discussion: a lightweight, deferred registry check with a purely informational admin banner is the right scope for core update awareness. The implementation generally follows EmDash conventions — admin-only route with requirePerm, after() for the background fetch, options-table caching, Lingui-wrapped strings, and RTL-safe layout classes.
I found two real issues that should be fixed before merge:
-
updateCheck: falsedoes nothing at runtime. The option is defined onEmDashConfigand documented, but it is not added toserializableConfig, which is what becomesvirtual:emdash/configand is exposed aslocals.emdash.config. The route therefore readsemdash.config.updateCheckasundefined, making the documented opt-out silently ignored. -
A malformed
checkedAtstring prevents the cache from ever refreshing.parseCacheonly verifies thatcheckedAtis a string, not that it parses to a valid date. If the stored value is garbage, the stale check becomesNaN >= CHECK_INTERVAL_MS(false), so no deferred refresh is scheduled and stale/corrupt data can persist indefinitely.
The unit tests cover the handler and the fetch logic well, but they exercise handleCoreUpdateStatus directly (with VERSION = "dev", so the deferred branch is never hit) and do not cover the integration-level serialization of updateCheck. Add a serialization-level check or route-level test for the opt-out once the config is wired through.
Findings
-
[needs fixing]
packages/core/src/astro/integration/index.ts:353updateCheckis added to theEmDashConfigtype and documented, but it is missing from theserializableConfigobject that is baked intovirtual:emdash/configand later attached aslocals.emdash.config. Because the route readsemdash.config.updateCheck !== false, the value is alwaysundefinedat runtime and the check is always treated as enabled — the documented opt-out is silently ignored.toolbar: resolvedConfig.toolbar, updateCheck: resolvedConfig.updateCheck, };
…able checkedAt Two bot findings: (1) updateCheck was on EmDashConfig but missing from serializableConfig, so locals.emdash.config.updateCheck was always undefined and the documented opt-out was silently ignored — extracted buildSerializableConfig() (unit-testable) and added the key. (2) a garbage checkedAt made the staleness math NaN >= interval (never stale), freezing a corrupt cache forever — parseCache now treats an unparsable date as no cache so the next request schedules a refresh.
|
Both findings addressed in 79abc28:
|
Overlapping PRsThis PR modifies files that are also changed by other open PRs:
This may cause merge conflicts or duplicated work. A maintainer will coordinate. |
…com network flake)
What does this PR do?
Implements the core update notice proposed (and maintainer-+1'd) in Discussion #1889: a WordPress-style "a new version is available" banner in the admin dashboard.
Coming from WordPress, the update notice is the single biggest "am I safe?" signal. EmDash already surfaces updates for marketplace plugins, but for the core there is nothing — a site owner who isn't the developer has no way to know they're running 0.22 while 0.24 is out with security fixes.
How it works:
VERSION). The latest published version comes from a plain GET tohttps://registry.npmjs.org/emdash/latest— checked at most once per day, always deferred viaafter()so it never blocks a request, result cached in the options table. The request carries no site data; sites without outbound internet simply never show the banner.GET /_emdash/api/admin/core-update(newupdates:readpermission, min role admin) serves the cached status — always a fast local read.updateCheck: falsein the EmDash config disables the check entirely (documented in the configuration reference).Deliberately not in scope (as discussed): an "update now" button. An EmDash update is an npm bump + rebuild + redeploy, which the admin cannot and should not trigger. The value is the awareness.
Discussion: #1889
Type of change
Checklist
pnpm typecheckpassespnpm lintpassespnpm testpasses (or targeted tests for my change)pnpm formathas been runmessages.pochanges except in translation PRs — a workflow extracts catalogs on merge tomain.AI-generated code disclosure
Screenshots / test output