● Increment 5 complete. What landed: #41
No reviewers
Labels
No labels
bug
dependencies
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
rust
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
navicore/anz!41
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "increment-5"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Piece: Profile config
File: src/cli/profiles.rs (new)
Notes: CliConfig { default_profile, profiles: HashMap<String, Profile> } +
Profile { server }; loads from $HOME/.config/anz/config.toml. Missing
file
→ empty config (silent). Malformed file → hard error (silent ignore
would
hide misconfig).
────────────────────────────────────────
Piece: Server resolution
File: CliConfig::resolve_server
Notes: Precedence: --server (explicit) > --profile NAME > default_profile
from config > None (local mode). Unknown profile lists the known ones in
the error message.
────────────────────────────────────────
Piece: clap_complete dep
File: Cargo.toml
Notes: Single small dep, just the generator
────────────────────────────────────────
Piece: Global --profile flag
File: src/cli/mod.rs
Notes: Same global = true as --server so it works on any subcommand
────────────────────────────────────────
Piece: Completions { shell } subcommand
File: src/cli/mod.rs
Notes: Uses clap_complete::Shell value-enum (bash / zsh / fish / elvish /
PowerShell)
────────────────────────────────────────
Piece: Dispatch
File: src/main.rs
Notes: Loads CliConfig once at startup, resolves effective server, threads
through Login and build_backend; Completions emits via
clap_complete::generate to stdout
Operator workflow it unlocks:
~/.config/anz/config.toml
default_profile = "homelab"
[profiles.homelab]
server = "https://anz.navicore.tech"
[profiles.work]
server = "https://anz.work.example.com"
Routine admin — no flags needed.
anz realm list
anz user add --realm homelab --username alice --email alice@x
Switch profiles on the fly.
anz --profile work realm list
Always wins.
anz --server https://anz.staging.example.com realm list
Force local (bootstrap / break-glass on the box).
(passing neither --server nor --profile and no default_profile in config)
anz realm create master
Completions.
anz completions zsh > ~/.zfunc/_anz
anz completions bash > /etc/bash_completion.d/anz
anz completions fish > ~/.config/fish/completions/anz.fish
Tests added (8):
default_profile)
input and what's available
loudly
Manually verified: anz completions zsh emits a valid #compdef anz script;
anz completions bash emits a real completion function.
Piece: Profile config File: src/cli/profiles.rs (new) Notes: CliConfig { default_profile, profiles: HashMap<String, Profile> } + Profile { server }; loads from $HOME/.config/anz/config.toml. Missing file → empty config (silent). Malformed file → hard error (silent ignore would hide misconfig). ──────────────────────────────────────── Piece: Server resolution File: CliConfig::resolve_server Notes: Precedence: --server (explicit) > --profile NAME > default_profile from config > None (local mode). Unknown profile lists the known ones in the error message. ──────────────────────────────────────── Piece: clap_complete dep File: Cargo.toml Notes: Single small dep, just the generator ──────────────────────────────────────── Piece: Global --profile flag File: src/cli/mod.rs Notes: Same global = true as --server so it works on any subcommand ──────────────────────────────────────── Piece: Completions { shell } subcommand File: src/cli/mod.rs Notes: Uses clap_complete::Shell value-enum (bash / zsh / fish / elvish / PowerShell) ──────────────────────────────────────── Piece: Dispatch File: src/main.rs Notes: Loads CliConfig once at startup, resolves effective server, threads through Login and build_backend; Completions emits via clap_complete::generate to stdout Operator workflow it unlocks: # ~/.config/anz/config.toml default_profile = "homelab" [profiles.homelab] server = "https://anz.navicore.tech" [profiles.work] server = "https://anz.work.example.com" # Routine admin — no flags needed. anz realm list anz user add --realm homelab --username alice --email alice@x # Switch profiles on the fly. anz --profile work realm list # Always wins. anz --server https://anz.staging.example.com realm list # Force local (bootstrap / break-glass on the box). # (passing neither --server nor --profile and no default_profile in config) anz realm create master # Completions. anz completions zsh > ~/.zfunc/_anz anz completions bash > /etc/bash_completion.d/anz anz completions fish > ~/.config/fish/completions/anz.fish Tests added (8): - empty_config_resolves_to_local_mode - explicit_server_overrides_everything (over both --profile and default_profile) - explicit_profile_looks_up_server - default_profile_kicks_in_when_no_flag - unknown_profile_is_error_with_known_list — error message names the bad input and what's available - default_profile_pointing_at_missing_is_error — typos in config fail loudly - missing_file_loads_as_empty - malformed_file_is_hard_error Manually verified: anz completions zsh emits a valid #compdef anz script; anz completions bash emits a real completion function.Review: Increment 5 — profiles + shell completions
Verdict: clean and nearly ready — one coupling bug worth fixing before merge (details below). Lowest-risk increment in the series: pure CLI ergonomics, no server surface. Verified locally: clippy clean with
-D warnings, 165 tests pass, andanz completions {zsh,bash}emit valid scripts (#compdef anz, a real bash function, with--profilepresent as a global). Nice to see the prior round's feedback already landed in the base —insecure_server_classificationand the dead-bailremoval.Verified
profiles.rsis well-built. Precedence--server > --profile > default_profile > localmatches the doc and is exhaustively tested (8 cases). The missing-file→empty vs malformed-file→hard-error distinction is the right call. Unknown-profile and typo'd-default_profileboth fail loudly and name the bad input + known profiles.completionsis pure — generates to stdout viaclap_complete, no DB/login/state needed, returns early. Confirmed the--profile/--serverglobals show up on subcommands.anz login --profile work(or adefault_profile) resolves a server, an improvement over Increment 4 where login required an explicit--server.Worth fixing before merge
Profile resolution is coupled to commands that don't use it.
CliConfig::load_or_default()?andresolve_server()?both run before the commandmatch, so their errors abort every command — includingserve,completions, andlogout, none of which consult profiles. Reproduced with a plausible operator typo (default_profile = "homelab"but no[profiles.homelab]):Two distinct problems:
completionsfailing is the worst of these — completion scripts are sourced during shell init, so an unrelated profile typo (or a malformedconfig.toml) silently breaks tab-completion for the whole shell. Completions should generate unconditionally.serveis the on-box daemon and shouldn't depend on the desktop CLI's profile config being well-formed at all — its startup is now gated on a file it never reads.Fix is small: resolve the server lazily, only inside the branches that need it (the admin commands +
login), and leaveserve/completions/logoutoff that path. That also defers the malformed-file hard error to where it's relevant.Minor
--server+--profiletogether silently lets--serverwin (documented + tested). Fine as-is; a one-line "note: --profile ignored because --server is set" would be friendlier but isn't necessary.serverURL gets no well-formedness/scheme check at resolution time, but it flows into the sameinsecure_server_classificationpath that--serverdoes, so there's no new gap — just confirming it's consistent.┌──────────────────┬─────────────────────┬────────────────────────────┐ │ Command │ Before fix │ After fix │ ├──────────────────┼─────────────────────┼────────────────────────────┤ │ anz completions │ Errored — silently │ Succeeds — emits the │ │ zsh (with broken │ kills shell-init │ #compdef anz script │ │ config) │ tab-completion │ (rustup's noise is │ │ │ │ unrelated) │ ├──────────────────┼─────────────────────┼────────────────────────────┤ │ │ │ Succeeds — │ │ anz logout │ Errored │ token-cache-only, profile │ │ │ │ config ignored │ ├──────────────────┼─────────────────────┼────────────────────────────┤ │ │ Errored — on-box │ Would succeed (same code │ │ anz serve │ daemon depending on │ path as logout — no │ │ │ desktop config │ profile resolution) │ ├──────────────────┼─────────────────────┼────────────────────────────┤ │ anz realm list │ │ Still errors with the same │ │ (a command that │ Errored with the │ actionable message — │ │ does need a │ same message │ operator sees the typo │ │ server) │ │ immediately │ └──────────────────┴─────────────────────┴────────────────────────────┘ Summary of the response: Single fix, structural — extracted resolve_remote_server(server_flag, profile_flag) as a lazy helper, wired into only the branches that need a server (Login, build_backend). Serve, Logout, Completions no longer touch profile config. The malformed-config hard-error is preserved exactly where it's relevant — admin commands that would otherwise silently fall back to local mode. Side-effect of the refactor: passing flag scalars (Option<&str>) into the helpers instead of &Cli cleaned up a partial-move issue too (the Login { realm } arm consumes the realm String, which made &cli invalid downstream — unrelated to the reviewer's note but cleaner anyway). 165 tests still pass, clippy clean. The reviewer's minor (--server silently winning over --profile) deliberately left as documented behavior.