Add mapbox auth profiles: list every stored credential profile - #38
Conversation
--profile <name> selects a named credential file, but nothing lists which profiles actually exist. Answering that meant `ls ~/.mapbox/credentials-*.json` by hand, and knowing the naming convention to tell the default profile apart from a named one. `mapbox auth profiles` reads the config directory rather than resolving --token/the environment/a single stored profile the way `whoami` does — it answers "what is stored" rather than "what will the next command use", which is a different question with a different audience: someone who has forgotten which named profiles they have logged into. Read-only, like whoami: it loads credentials without refreshing them, so listing profiles cannot itself spend a single-use refresh token. Reports each profile's account (the stored username, falling back to the token's own `u` claim) and expiry, decoded locally the same way whoami's fields are. profile_name_from_filename is credentials_filename's reverse, tested against credentials_filename's own output rather than only hand-written examples so the two cannot quietly drift apart, and checked against every other file a config directory can hold (config.json, update-check.json, a lock file) so none of them are mistaken for a profile. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
zmofei
left a comment
There was a problem hiding this comment.
Thanks for the changes. The feature looks good to me. A few things I'd like to flag before merging:
-
The human-readable text output could use clearer formatting. It's currently hard to scan.
-
Could we introduce a short, system-generated profile ID? As the screenshot shows, profile names can be long and cumbersome to read or reference. This could go into a follow-up PR; if so, please create a ticket before merging this one.
-
Bugs:
- Stray
credentials-default.jsoncreates a duplicatedefaultrow with wrong data. profilestriggers directory-create/chmod even though it's supposed to be read-only.- Expired (not clock-skewed) profiles wrongly show "check this machine's clock".
- Stray
-
Docs out of date:
docs/commands.mdstill says all commands take--profile/--output, contradicting the newprofilessection.tests/docs_contract.rscomment still says "four auth commands" (now five).README.mddoesn't listprofiles.--profileflag is silently ignored byprofiles, undocumented.
Bugs: - A stray credentials-default.json (never a spelling credentials_filename writes) no longer produces a second `default` row with data read from the wrong file. profile_name_from_filename rejects "default" from the credentials-<name>.json pattern, since the real default is always the bare credentials.json. - Listing profiles no longer creates or hardens the config directory as a side effect. load_credentials_readonly/credentials_path_readonly read through config_dir_path rather than config_dir, so asking what is stored is never why a directory starts to exist or its permissions change. - An expired (not clock-skewed) profile now reads as "expired" rather than "check this machine's clock" — that phrasing fit whoami's context (a token about to be used right now, where a small negative result is as likely to be skew as a real expiry) and was wrong far more often than right for a profile that may have sat untouched for weeks. Text output is now a padded table instead of a raw tab-separated line, which rendered misaligned across rows with differently sized account names. --profile is declared globally so it still parses on this command, but it selects a single profile to act on and this command's whole point is every one of them — so typing it now warns on stderr rather than silently doing nothing, the same shape completion::warn_output_ignored already uses for --output. Docs: the auth profiles section now calls out the --profile exception to the page-wide table, the docs_contract.rs comment counting auth subcommands is five now, and README lists the command. Filed mapbox-cli-private#179 for the reviewer's short-profile-ID suggestion, as requested, rather than folding it into this PR. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
# Conflicts: # CHANGELOG.md
|
Thanks for the thorough review. Addressed everything:
New tests for all of the above in |
# Conflicts: # CHANGELOG.md
# Conflicts: # CHANGELOG.md
|
Reviewed and tested locally —
Lower priority, fine to defer:
|
list_profile_names propagated a hard error via ? if a single read_dir
entry failed, contradicting this function's own stated design ("nothing to
read" rather than a failure) and inconsistent with load_credentials_readonly's
.ok() a few lines below — a transient filesystem error on one entry would
have made auth profiles fail for every profile instead of just skipping the
bad one. Fixed with the same let-else skip config_dir_path already uses
above it.
docs/commands.md had two references to "four" auth commands left over from
before this PR's fifth: the page's own opening count, and the Auth
section's "All five commands take" table, which now also says profiles
parses --profile without honoring it, matching the note its own section
already gives.
Deferred per reviewer's own priority call: the config.rs lock-file
inconsistency, a --schema contract test for config, deduplicating
warn_output_ignored, and the regular-file check on a matching directory
entry.
482 tests, fmt and clippy clean.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Addressed both must-fix items:
Deferring the four lower-priority items per your own call — happy to pick any of them up if you'd rather they land in this PR instead of a follow-up. |
Summary
--profile <name>selects a named credential file, but nothing lists which profiles actually exist — answering that meantls ~/.mapbox/credentials-*.jsonby hand.mapbox auth profilesreads the config directory and reports every stored profile's account and expiry, without resolving--token/the environment/a single profile the waywhoamidoes. Different question: "what is stored" vs. "what will the next command use."whoami— loads credentials without refreshing, so listing can't spend a single-use refresh token.profile_name_from_filename(the reverse ofcredentials_filename) is tested againstcredentials_filename's own output, and against every other file a config directory can hold (config.json,update-check.json, a lock file), so none of those are mistaken for a profile.Test plan
cargo build,cargo fmt,cargo clippy --all-targets -- -D warnings,cargo test— all cleansrc/auth.rsfor the filename round-triptests/auth_profiles.rs: empty state, multiple profiles sorted default-first, non-profile files correctly excluded, absent config directory creates nothingdocs/commands.md, README-adjacent CHANGELOG entry, and the two hardcoded command-surface test lists (the_auth_subcommands_that_write_offer_dry_run,the_hand_written_commands_describe_themselves) all updated