Skip to content

feat: improve npm authentication and plugin health checks during CLI updates#3547

Merged
eablack merged 9 commits intov11.0.0from
eb/better-messaging-when-failing-from-private-plugins
Mar 3, 2026
Merged

feat: improve npm authentication and plugin health checks during CLI updates#3547
eablack merged 9 commits intov11.0.0from
eb/better-messaging-when-failing-from-private-plugins

Conversation

@eablack
Copy link
Copy Markdown
Contributor

@eablack eablack commented Feb 27, 2026

Summary

This PR improves the user experience when updating the Heroku CLI by adding better handling for npm authentication and plugin installation issues. Users previously encountered cryptic npm errors when updating while logged out of npm with private plugins installed. This change proactively detects and handles these scenarios with clear messaging and interactive prompts.

Key Changes

1. Pre-update npm authentication check (hooks/preupdate/check-npm-auth.ts)

  • New preupdate hook that runs before the CLI update begins
  • Detects private npm plugins by checking package privacy via npm API
  • Parallelizes npm API calls in batches of 5 for better performance
  • Prompts users to authenticate with npm if private plugins are detected and user is not authenticated
  • Provides clear warning messages and recovery instructions if authentication is declined

2. Post-update plugin health check (hooks/update/check-plugin-health.ts)

  • New update hook that runs after plugin installation
  • Verifies all configured plugins exist in node_modules
  • Detects missing plugins that failed to install during update
  • Provides detailed recovery instructions with specific commands to reinstall or uninstall affected plugins

3. Reusable npm authentication library (lib/npm-auth.ts)

  • Centralized authentication logic with functions: isNpmAvailable(), isAuthenticated(), isPrivatePackage(), login()
  • Handles npm's 404 responses for private packages (to avoid leaking package existence)
  • Designed for testability with extracted exec/spawn methods

4. Cleanup

  • Removed deprecated v6 plugin migration code (hooks/update/plugin-migrate.ts)
  • Fixed missing await in hooks/init/terms-of-service.ts
  • Fixed resource leak in lib/repl.ts (missing historyStream.close())

Type of Change

Feature Additions (minor semver update)

  • feat: Introduces a new feature to the codebase

Testing

Notes:
The hooks integrate into the existing oclif update lifecycle. The check-npm-auth hook runs before the update starts (preupdate), while check-plugin-health runs after plugin installation (update). Both hooks are non-blocking - errors won't prevent updates from completing. Since this is part of the lifecycle, we'll test this once this gets into the beta release.

Steps:

  1. Install a private npm plugin: heroku plugins:install @private/some-plugin
  2. Log out of npm: npm logout
  3. Attempt to update CLI: heroku update
  4. Verify the check-npm-auth hook prompts for authentication
  5. Choose to authenticate and verify update proceeds successfully
  6. Alternatively, choose not to authenticate and verify clear warning message is displayed

To test plugin health check:

  1. Manually corrupt a plugin installation (delete from node_modules but leave in package.json)
  2. Run heroku update
  3. Verify warning message with specific recovery instructions for affected plugins

Related Issues

https://gus.lightning.force.com/lightning/r/ADM_Work__c/a07EE00002VgjNJYAZ/view

@eablack eablack requested a review from a team as a code owner February 27, 2026 23:33
Copy link
Copy Markdown
Contributor

@k80bowman k80bowman left a comment

Choose a reason for hiding this comment

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

I'm excited about using hooks for this, I think that's a great idea. And I love the idea of adding the plugin health check as well, that's awesome. I do have a few implementation comments, but this is great. I appreciate you figuring this out.

@eablack eablack temporarily deployed to AcceptanceTests March 2, 2026 20:52 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 2, 2026 20:52 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 2, 2026 20:52 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 2, 2026 20:52 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 2, 2026 22:28 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 2, 2026 22:28 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 2, 2026 22:28 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 2, 2026 22:28 — with GitHub Actions Inactive
eablack added 7 commits March 3, 2026 08:59
…updates

This change improves the user experience when updating the Heroku CLI by adding better handling for npm authentication and plugin installation issues.

Key changes:
- Add preupdate hook (check-npm-auth) that detects private plugins and prompts users to authenticate with npm before attempting the update
- Add post-update hook (check-plugin-health) that verifies all plugins installed correctly and provides recovery instructions for missing plugins
- Remove deprecated v6 plugin migration code from plugin-migrate hook
- Add comprehensive unit tests for both new hooks

The check-npm-auth hook:
- Reads installed plugins from package.json
- Checks which plugins are private (require authentication)
- Verifies npm authentication status
- Prompts user to login if needed before proceeding with update
- Handles user cancellation gracefully with clear messaging

The check-plugin-health hook:
- Runs after plugin installation during update
- Checks if all configured plugins exist in node_modules
- Warns users about missing plugins with recovery instructions
- Suggests both reinstall and uninstall options

This resolves issues where users would get cryptic npm errors during CLI updates when logged out of npm with private plugins installed.
Refactor check-npm-auth hook to check multiple plugins concurrently
instead of sequentially. This significantly reduces the time to check
plugin privacy when users have many plugins installed.

- Process plugins in batches of 5 using Promise.all
- Maintains the same behavior and debug logging
- Reduces check time from O(n) sequential to O(n/5) batches
- Extract NpmAuth class with testable wrapper methods (exec, spawn)
- Add isNpmAvailable() check to skip auth flow when npm is not installed
- Treat 404 errors as potentially private packages (npm returns 404 for private packages when not authenticated)
- Consolidate duplicate warning messages
- Simplify authentication flow to never block updates - always warn and continue
- Reduce check-npm-auth hook from 230 lines to 118 lines

Tests:
- Add 14 unit tests for NpmAuth class methods
- Add 9 comprehensive tests for hook orchestration logic
- Remove weak "doesn't throw" assertions in favor of specific behavioral checks
- All 23 tests passing
- Fix FileHandle deprecation warning by explicitly closing historyStream on REPL exit
- Fix terms-of-service hook to properly await async fs.createFile
- Simplify check-plugin-health error message by removing redundant text
The hook was registered as 'preupdate' in package.json but was located
in the hooks/update directory. Move it to hooks/preupdate to match its
actual hook type and improve code organization.
@eablack eablack force-pushed the eb/better-messaging-when-failing-from-private-plugins branch from 1e78521 to 3f367e5 Compare March 3, 2026 16:59
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 16:59 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 16:59 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 16:59 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 16:59 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 17:02 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 17:02 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 17:02 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 17:02 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 17:08 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 17:08 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 17:08 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 17:08 — with GitHub Actions Inactive
Copy link
Copy Markdown
Contributor

@k80bowman k80bowman left a comment

Choose a reason for hiding this comment

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

This looks awesome, thank you for all of the fixes!

@eablack eablack merged commit 7e6603d into v11.0.0 Mar 3, 2026
17 checks passed
@eablack eablack deleted the eb/better-messaging-when-failing-from-private-plugins branch March 3, 2026 18:19
Sign up for free to 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