Repository navigation
Conversation
Previously the `_update-notifier-last-checked` sentinel was written as
soon as a check *started*, regardless of whether a notice was ever
shown to the user. This meant fast commands (e.g. `npm version`) would
silently consume the check window without the user ever seeing the
update notice, since the sentinel's mtime only ever meant "a check was
attempted", not "a notice was shown".
This redefines the sentinel's mtime to mean "last time a notice was
actually displayed":
- `updateCheck()` now returns `{ message, onShown }` instead of a bare
message string. The sentinel is written lazily via `onShown()`,
invoked from the exit handler synchronously (via `writeFileSync`,
required because `process.on('exit', ...)` can't await async work)
only after the notice has actually been logged.
- The weekly/daily split between GA and prerelease checks is removed
in favor of a single daily check for both, since the throttle is now
tied to actual visibility rather than check attempts.
- `npm.updateNotification` is now a single bundled object
(`{ message, onShown }`) instead of a bare string, removing the need
for a separate `updateNotificationShown` field and the duplication
that came with keeping two npm instance fields in sync.
- When silenced (`npm.silent`), the notice is not shown and the
sentinel is not written, so a later non-silent run will still get a
chance to show (and record) the notice.
- Node.js engine compatibility continues to be evaluated fresh on
every run (never cached), since the active Node version can change
between invocations (e.g. via nvm).
Ref: github/npm#15901 (comment)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…r path Full-suite coverage caught two branches the initial tests missed: - entry.js: the branch that actually assigns npm.updateNotification when the notifier resolves a real notification (previously only exercised the null/no-op path since update-notifier.js isn't mocked in entry.js's own tests). - exit-handler.js: the try/catch around onShown() when it throws, which only logs a verbose message and must not crash the exit path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.
Summary
The
update-notifiersentinel was written as soon as a check started, not when a notice was actually shown. Fast commands (e.g.npm version) wrote the sentinel and exited before the notice could ever print, so the throttle window was wasted and the user never saw the update message.This changes the sentinel's mtime to mean "last time a notice was shown", and retries the check on every eligible run until a notice is actually displayed.
Changes
updateCheck()returns{ message, onShown }instead of a bare string. The sentinel is only written whenonShown()runs, after the notice is logged.onShown()useswriteFileSyncsince it's called from theprocess.on('exit', ...)handler, where async work can't be awaited.npm.updateNotificationinto{ message, onShown }, removing the separateupdateNotificationShownfield.Tradeoffs
If a notice is never shown, every later eligible run retries the check. We preferred this over a more complex sentinel that caches a pending version/check state, since that would need to be environment-aware (Node version can change between runs) — not worth the added complexity.