Skip to content

feat: adds credential manager functionality to the CLI#3758

Open
k80bowman wants to merge 48 commits into
mainfrom
feat/credential-mgr-integration
Open

feat: adds credential manager functionality to the CLI#3758
k80bowman wants to merge 48 commits into
mainfrom
feat/credential-mgr-integration

Conversation

@k80bowman

Copy link
Copy Markdown
Contributor

Summary

Adds credential manager functionality to the CLI. This includes:

  • Updates the CLI to use new versions of @heroku-cli/command and @heroku/heroku-cli-util which include the credential manager functionality
  • Adding a custom git credential helper so that git push heroku main will continue to work with Heroku tokens that are stored in the system keychain
  • Updates to the accounts commands to support keychain-based accounts
  • Updates to the CLI telemetry to use the credential manager

Type of Change

Breaking Changes (major semver update)

  • Add a ! after your change type to denote a change that breaks current behavior

Feature Additions (minor semver update)

  • feat: Introduces a new feature to the codebase

Patch Updates (patch semver update)

  • fix: Bug fix
  • deps: Dependency upgrade
  • revert: Revert a previous commit
  • chore: Change that does not affect production code
  • refactor: Refactoring existing code without changing behavior
  • test: Add/update/remove tests

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:

  1. Check out this branch and run npm i && npm run build
  2. Run ./bin/run login and go through the login process. Confirm that your .netrc file has been updated and an entry has been added to your system keychain with the name heroku-cli.
  3. Run ./bin/run accounts:add account_1
  4. Run ./bin/run accounts and confirm that it shows account_1
  5. Run ./bin/run accounts:remove account_1 and confirm that you get an error, since that is your current account
  6. Run ./bin/run logout and confirm that your .netrc file no longer contains your Heroku token and the heroku-cli entry has been removed from your system keychain.

Screenshots (if applicable)

Related Issues

GUS work item: W-20867320

erika-wallace and others added 30 commits April 15, 2026 10:05
* update heroku-cli/command to v13.0.0-beta.1

* update test config
)

* configure git:credentials as a git cred helper

* update/add tests for git cred helper

* delete HEROKU_API_KEY for git:credentials and login tests
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
)

* fix: update heroku-cli-command and heroku-cli-util

* fix: update whoami to correctly process 401 error

* update package-lock for node 22

* test: update resolve function unit test to use 503 error instead of 401
#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.
k80bowman added 11 commits June 5, 2026 15:34
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.
@k80bowman k80bowman requested a review from a team as a code owner June 9, 2026 14:36
@k80bowman k80bowman temporarily deployed to AcceptanceTests June 9, 2026 14:36 — with GitHub Actions Inactive
@k80bowman k80bowman temporarily deployed to AcceptanceTests June 9, 2026 14:36 — with GitHub Actions Inactive
@k80bowman k80bowman temporarily deployed to AcceptanceTests June 9, 2026 14:36 — with GitHub Actions Inactive
@k80bowman k80bowman temporarily deployed to AcceptanceTests June 9, 2026 14:36 — with GitHub Actions Inactive
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.

3 participants