feat: adds credential manager functionality to the CLI#3758
Open
k80bowman wants to merge 48 commits into
Open
Conversation
* update heroku-cli/command to v13.0.0-beta.1 * update test config
update CLI analytics to use heroku credential manager
* update analytics tests to use the credential manager * rename and move analytics.unit.test.ts
…tial manager (#3689) * refactor: introduce AccountEntry type and update list() to return structured data * feat: resolve current account via heroku client when using credential store * test: update accounts tests for AccountEntry shape and async list() * test: linting and test fixes * fix accounts list() tests by setting platform to 'darwin' * fix HerokuExec test * fix HerokuExec test again * test: make HerokuExec test more specific * test: fix git credentials test for windows * Revert "test: fix git credentials test for windows" This reverts commit 2d489a6. * fix: remove output from readline.createInterface in git:credentials * fix: use setImmediate with rl.close() in git:credentials
* fix: restore git configuration on logout * fix race condition in git:credentials tests * fix async function call without await
* update accounts:add to use the credential manager * check for useNetrc * revert
* feat: update accounts:remove to work with credential manager * test: update the stubbing method for the remove function
* feat: update accounts:set to allow for keychains * test: update tests for accounts:set and set function * test: update tests to work with Windows * test: fix git credentials tests * refactor: update logic to be more specific about keychain vs netrc accounts * test: update accounts:set tests * fix: revert changes to the accounts wrapper
* feat: remove cached netrc account on logout * fix: remove netrc cache on logout without env var * update package-lock * revert package-lock changes
#3733) * deps: update versions of heroku-cli/command and heroku/heroku-cli-util * fix: update package-lock for node 22
Adds private helper method to resolve alias names to email addresses by reading alias files. This enables alias-based lookup for keychain accounts. Also fixes broken references to removed removeNetrc method: - Updated logout command to use remove() instead - Updated tests to stub remove() instead of removeNetrc() - Removed obsolete removeNetrc() test block
Adds private helper method to build a Map of all alias files in the accounts directory. Maps alias names to email addresses by reading all files and filtering out invalid entries. Returns empty Map when directory doesn't exist or on errors.
Updates list() method to return aliased keychain accounts with both name and username properties. Creates reverse map (email → alias) from alias files and merges with keychain accounts.
Behavior:
- Aliased accounts: {name: "production", username: "prod@example.com"}
- Non-aliased accounts: {username: "user@example.com"}
- Multiple aliases to same email: uses first alias
- Orphaned alias files: ignored (only keychain accounts shown)
This enables keychain accounts to be identified by friendly alias names.
Updates set() method to handle keychain accounts with aliases by reading the email from the alias file and writing it to login.json. Behavior: - Aliased account: reads email from alias file, writes to login.json - Non-aliased account: writes username directly to login.json - Missing alias file: throws error Key: Always writes EMAIL (not alias name) to login.json since keychain entries are keyed by email.
Updates remove() method to handle both aliased and non-aliased keychain accounts: Behavior: - Aliased account: reads email from alias file, removes from keychain using email, deletes alias file - Non-aliased account: removes from keychain using email directly, no alias file to delete - Netrc mode: always removes alias file (existing behavior) Key: Keychain entries are keyed by email, so we must resolve alias → email before calling removeAuth().
Extracts repeated path.join(this.configDir(), 'accounts') calls into a single helper method. This reduces duplication across 6 locations and makes it easier to maintain the accounts directory path logic.
Extracts duplicated Ruby symbol key conversion logic into convertRubySymbols() helper method. Eliminates code duplication between account() and getAliasEmail() methods, making the Ruby CLI backward compatibility logic easier to maintain in one place.
Extracts duplicated file write + chmod logic into writeAccountFile() helper method. Reduces code duplication in add() method and makes the file creation logic consistent across credentialStore and useNetrc modes.
Removes outdated distinction between netrc and keychain accounts. Since aliased keychain accounts now have the name property (just like netrc accounts), we can use a single unified lookup: account.name === name || account.username === name. This handles: - Netrc accounts by name - Aliased keychain accounts by name - Non-aliased keychain accounts by username (email)
Fixed bug where both credentialStore and useNetrc conditions could execute, causing the password to be written to keychain alias files. Changed second if to else if to ensure mutual exclusivity. For credentialStore (keychain): only username is written to alias file For useNetrc: both username and password are written to alias file Added test to verify credentialStore mode only writes username without password.
Fixes issue where accounts:set fails when switching to netrc mode (HEROKU_NETRC_WRITE=true) with alias files created in keychain mode. These files only contain username (no password). Now uses empty string as password fallback, allowing the account switch to succeed. User will be prompted to login when running commands, which is the expected behavior.
Prevents data inconsistency when removing accounts in the wrong mode: - Keychain mode trying to remove netrc account: only deletes alias file, leaves netrc credentials - Netrc mode trying to remove keychain account: only deletes alias file, leaves keychain entry Now detects cross-mode scenarios by checking for password in alias file and provides clear error messages directing users to use the correct mode. Error messages use color.command() for better readability and explain the situation clearly.
Fixed test that was failing on Windows due to path separator differences. Changed regex matcher from /\.heroku.*production$/ to /production$/ to match just the filename, consistent with all other tests in the file. This makes the test work on both Unix (/) and Windows (\) path separators.
… accounts:current commands
…t from accounts:set
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
Adds credential manager functionality to the CLI. This includes:
git push heroku mainwill continue to work with Heroku tokens that are stored in the system keychainType of Change
Breaking Changes (major semver update)
!after your change type to denote a change that breaks current behaviorFeature Additions (minor semver update)
Patch Updates (patch semver update)
Testing
Notes:
We have done a lot of extensive testing already. I've included instructions below for some basic tests to run if you'd like.
Steps:
npm i && npm run build./bin/run loginand go through the login process. Confirm that your.netrcfile has been updated and an entry has been added to your system keychain with the nameheroku-cli../bin/run accounts:add account_1./bin/run accountsand confirm that it showsaccount_1./bin/run accounts:remove account_1and confirm that you get an error, since that is your current account./bin/run logoutand confirm that your.netrcfile no longer contains your Heroku token and theheroku-clientry has been removed from your system keychain.Screenshots (if applicable)
Related Issues
GUS work item: W-20867320