Skip to content

Adopt profile package for multi-account auth - #78

Merged
jeremy merged 7 commits into
masterfrom
multi-account-support
Mar 6, 2026
Merged

jeremy merged 7 commits into
masterfrom
multi-account-support

Conversation

@robzolkos

@robzolkos robzolkos commented Mar 6, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Replace custom account-scoped credential storage with the profile package from basecamp/cli. Each Fizzy account becomes a named profile backed by ~/.config/fizzy/config.json, with tokens stored under profile:<name> in the credential store.

  • --profile flag and FIZZY_PROFILE env var select the active profile (FIZZY_ACCOUNT accepted as deprecated alias)
  • Profile settings (BaseURL, board) outrank local/global YAML config but yield to env vars and flags
  • auth login/logout/status/list/switch operate on profiles
  • Legacy tokens (token, token:<account>) auto-migrate to profile:<name> on first run; logout --all cleans all formats

Config precedence

  1. CLI flags (--token, --profile, --api-url, --board)
  2. Environment variables (FIZZY_TOKEN, FIZZY_PROFILE, FIZZY_API_URL, FIZZY_BOARD)
  3. Named profile settings (BaseURL, board from config.json)
  4. Local project config (.fizzy.yaml)
  5. Global config (config.yaml)

Changed files

  • config.go — Remove Accounts/AddAccount/RemoveAccount; update Load() docs for 6-layer precedence
  • root.go — Profile store init, resolveProfile() with profile.Resolve(), profile-scoped credential helpers, ensureProfile() upsert, --profile flag
  • auth.go — All subcommands rewritten for profile CRUD; logout --all cleans three legacy key formats
  • setup.go / signup.go — Create profile on setup/signup via ensureProfile() + SetDefault()
  • auth_test.go — Rewritten with profile store injection; integration test for full precedence chain (config.Load → resolveProfile → resolveToken → flag)
  • README.md / AGENTS.md / SKILL.md / SURFACE.txt — Updated for --profile, 5-level precedence, path-agnostic config references

Test plan

  • make passes (fmt, vet, build, unit tests)
  • make test-e2e with credentials
  • Manual: fresh install → fizzy setup → profile created in config.json
  • Manual: existing install with legacy token → run command → verify migration to profile
  • Manual: fizzy auth login <token> --profile <name> → fizzy auth list → fizzy auth switch
  • Manual: FIZZY_PROFILE=staging fizzy auth status → verify env var resolution

Copilot AI review requested due to automatic review settings March 6, 2026 14:07
@github-actions github-actions Bot added enhancement New feature or request breaking labels Mar 6, 2026
@github-actions

github-actions Bot commented Mar 6, 2026 •

Copy link
Copy Markdown

Potential breaking changes detected:

  • Renamed the --account flag to --profile, which breaks existing scripts that use the --account flag.
  • Renamed the FIZZY_ACCOUNT environment variable to FIZZY_PROFILE, which will break setups that rely on the environment variable for configuration.
  • Changed the field name in the auth status output JSON object from account to profile, which could break scripts that parse this output.
  • Adjusted authLoginCmd and authLogoutCmd logic to become profile-oriented. Any existing behavior relying on the absence of profiles might no longer work as before.
  • Removed old APIs for single-token or account-scoped keys in the credential store with migration to profile-scoped keys. While fallback to legacy keys is present, older configurations might require migration scripts.

Review carefully before merging. Consider a major version bump.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

3 issues found across 9 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="internal/commands/root.go">

<violation number="1" location="internal/commands/root.go:639">
P1: Bug: legacy token is silently dropped when migration save fails. If `credsSaveAccountToken` returns an error, `cfg.Token` is never set and the function returns early, leaving the user unauthenticated despite having a valid token in the legacy store. The token should be used regardless of whether migration succeeds.</violation>
</file>

<file name="internal/commands/auth.go">

<violation number="1" location="internal/commands/auth.go:38">
P2: Missing `globalCfg.AddAccount(account)` call — switching to an account doesn't register it in the known accounts list, so `auth list` and `auth logout --all` won't see it.</violation>

<violation number="2" location="internal/commands/auth.go:43">
P2: Config save error silently discarded — if `globalCfg.Save()` fails here, the user sees "Token saved" but account/active-account config is lost. The YAML-only branch handles this error; this branch should too.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Add one-off context when rerunning by tagging @cubic-dev-ai with guidance or docs links (including llms.txt)
  • Ask questions if you need clarification on any suggestion

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread internal/commands/root.go Outdated
Comment thread internal/commands/auth.go Outdated
Comment thread internal/commands/auth.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces multi-account authentication support for the Fizzy CLI. Instead of storing a single credential, tokens are now stored per-account under token:<account> keys in the credential store. The PR also includes commands for listing, switching between, and logging out of multiple accounts, along with backward-compatible migration of existing single-key tokens.

Changes:

  • Account-scoped credential storage (token:<account> keys) with legacy migration and downgrade compatibility
  • New auth list, auth switch, and auth logout --all commands for multi-account management
  • Config Accounts field to track known accounts (workaround for keyring not supporting key enumeration)

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
internal/config/config.go Added Accounts field and AddAccount/RemoveAccount helpers to Config struct
internal/commands/root.go Account-scoped credential functions, updated resolveToken with account lookup and legacy migration
internal/commands/auth.go Updated login/logout for per-account storage, added list and switch commands, --all logout flag
internal/commands/signup.go Uses credsSaveAccountToken and registers account on signup
internal/commands/setup.go Uses credsSaveAccountToken and registers account on setup
internal/commands/auth_test.go Comprehensive tests for new key scheme, list, switch, migration, YAML-only mode, and --account flag
skills/fizzy/SKILL.md Documentation for new auth subcommands
internal/skills/SKILL.md Mirror documentation update
SURFACE.txt Surface manifest updated with new commands and flags

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/config/config.go Outdated
Comment thread internal/commands/auth.go Outdated
@github-actions github-actions Bot removed the breaking label Mar 6, 2026
@robzolkos
robzolkos requested a review from jeremy March 6, 2026 14:23
jeremy added 3 commits March 6, 2026 10:50
Replace custom Config.Accounts/AddAccount/RemoveAccount with profile.Store
backed by ~/.config/fizzy/config.json. Credential keys change from
"token:<account>" to "profile:<name>" via credstore.

Key changes in root.go:
- Add profile.Store initialization and --profile global flag
- resolveProfile() uses profile.Resolve() 6-step chain
- Profile settings (layer 3) outrank local/global YAML config (4-5)
  but yield to env vars (layer 2) and flags (layer 1)
- Credential helpers: credsSaveProfileToken, credsLoadProfileToken,
  credsDeleteProfileToken with legacy key migration
- ensureProfile() does delete-then-create for upsert semantics

Config precedence: flags > env vars > profiles > local config > global > defaults
All auth subcommands now operate on profiles instead of accounts:
- auth login: saves to "profile:<name>" credstore key, creates profile
- auth logout: deletes profile and "profile:<name>" key
- auth logout --all: cleans all three legacy key formats
  ("profile:<name>", "token:<account>", bare "token")
- auth list: reads from profile.Store.List()
- auth switch: calls profiles.SetDefault(), updates YAML config
- auth status: shows profile key in output

Tests rewritten with profile.Store injection via SetTestProfiles().
Covers: invalid --profile flag errors, env var precedence over profile
settings, ensureProfile upsert, logout --all legacy key cleanup.
setup: create profile via ensureProfile() + profiles.SetDefault()
signup: saveSignupConfig() creates profile instead of AddAccount()
Both use credsSaveProfileToken for credential storage.
Copilot AI review requested due to automatic review settings March 6, 2026 18:50
@jeremy
jeremy force-pushed the multi-account-support branch from 23adaf0 to 8b4246a Compare March 6, 2026 18:50
jeremy added 2 commits March 6, 2026 10:53
- README/AGENTS: 5-level precedence, --profile flag, FIZZY_ACCOUNT deprecation
- SKILL.md: --account → --profile, precedence includes profile layer
- SURFACE.txt: regenerated with --profile replacing --account globally
TestPrecedenceChainIntegration wires up real config files, credstore,
and profile store, then runs config.Load() → resolveProfile() →
resolveToken() → flag override — the same sequence as PersistentPreRunE.

Three cases lock the contract:
- profile outranks YAML config for APIURL and board
- env vars beat profile for all fields
- --api-url flag beats env and profile
@jeremy
jeremy force-pushed the multi-account-support branch from 8b4246a to f41b529 Compare March 6, 2026 18:54
@jeremy jeremy changed the title Add multi-account auth support Adopt profile package for multi-account auth Mar 6, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 7 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/skills/SKILL.md Outdated
Comment thread skills/fizzy/SKILL.md Outdated
Comment thread internal/skills/SKILL.md Outdated
Comment thread internal/commands/root.go Outdated
Comment thread internal/config/config.go
Comment thread internal/commands/auth.go
Comment thread skills/fizzy/SKILL.md Outdated
jeremy added 2 commits March 6, 2026 11:33
- ensureProfile: use json.Marshal for board value instead of manual quoting
- auth switch: fall back to credsLoadLegacyToken when profile key missing
Replace remaining "account" references in auth command examples
and troubleshooting sections with "profile" terminology.
@jeremy
jeremy merged commit cca24e1 into master Mar 6, 2026
16 checks passed
@jeremy
jeremy deleted the multi-account-support branch March 6, 2026 19:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants