feat(cli): avocado login as a shortcut for connect auth login - #242
mobileoverlord wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
LoginArgs::run awaits an async method that borrows a temporary ConnectAuthLoginCommand, which will fail to compile due to a temporary dropped while borrowed across .await.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds a top-level avocado login command as a shortcut for avocado connect auth login, implemented by sharing a single LoginArgs struct and dispatch path so the two spellings can’t drift.
Changes:
- Introduces
Commands::Login(LoginArgs)as a top-level CLI verb with help text explaining why login matters for feed access. - Refactors
connect auth loginto reuse the sameLoginArgsand execution path (LoginArgs::run()). - Adds a parse/routing regression test ensuring both spellings parse equivalently and neither triggers VM routing.
File summaries
| File | Description |
|---|---|
| src/main.rs | Adds the new top-level login command, shares args with connect auth login, and adds a regression test for parsing + VM routing. |
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
🟡 Changes recommended
The new regression test doesn’t fully assert equivalence for all parsed LoginArgs fields (e.g., url and output), weakening the stated guarantee that the two spellings cannot diverge.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 1
- Review effort level: Lite
There was a problem hiding this comment.
🟡 Changes recommended
The new regression test does not fully enforce “no drift” between the two spellings (it should compare all shared parsed fields), and there’s a small return-type consistency issue in LoginArgs::run.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
src/main.rs:5294
- This test aims to ensure
avocado loginandconnect auth logincannot drift, but it only validatestoken,profile, andorgagainst constants. To actually guard against drift, compare all shared parsed fields between the two (url,token,profile,org,output), and then assert the expected values on just one side.
for args in [a, b] {
assert_eq!(
(
args.token.as_deref(),
args.profile.as_deref(),
args.org.as_deref()
),
(Some("t"), Some("p"), Some("o"))
);
- Files reviewed: 1/1 changed files
- Comments generated: 1
- Review effort level: Lite
There was a problem hiding this comment.
🟢 Approval recommended
The change is a small, well-scoped CLI refactor with a targeted regression test ensuring the shortcut and nested command stay equivalent and remain non-routed.
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 0 new
- Review effort level: Lite
There was a problem hiding this comment.
🟡 Changes recommended
The new regression test does not yet assert all shared LoginArgs fields (e.g., url and output), so the “cannot diverge” guarantee described in the PR can still regress without failing tests.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
src/main.rs:5290
- This test claims to ensure
avocado loginandconnect auth logincannot diverge, but it only checkstoken/profile/org. It should also assert the remaining shared fields (urlandoutput) to match expectations, otherwise drift in those flags or defaults won’t be caught.
for args in [a, b] {
assert_eq!(
(
args.token.as_deref(),
args.profile.as_deref(),
- Files reviewed: 1/1 changed files
- Comments generated: 1
- Review effort level: Lite
There was a problem hiding this comment.
🟢 Approval recommended
The change is small, behavior-preserving (shared args + shared execution), and includes a focused regression test confirming equivalence and VM-routing expectations.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
src/main.rs:1449
- Use "Log in" (verb) rather than "Login" (noun/adjective) for consistency with the new top-level
loginhelp text and standard grammar.
- Files reviewed: 1/1 changed files
- Comments generated: 0 new
- Review effort level: Lite
There was a problem hiding this comment.
🟢 Approval recommended
The shortcut reuses a single args struct and execution path, and the added test covers both CLI spellings plus the VM-routing invariant.
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 0 new
- Review effort level: Lite
79ed664 to
2f8334d
Compare
There was a problem hiding this comment.
🟢 Approval recommended
The change is narrowly scoped, keeps behavior unified via a shared args struct + shared execution path, and includes a targeted regression test for equivalence and VM-routing invariants.
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 0 new
- Review effort level: Lite
2f8334d to
b197c86
Compare
There was a problem hiding this comment.
🟢 Approval recommended
The change is small and well-contained, reuses a shared args struct to prevent drift, and includes a targeted regression test for the key behavior and VM-routing constraint.
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 0 new
- Review effort level: Lite
bee387d to
f51e935
Compare
The merge-base changed after approval.
Logging in is what raises the package-feed rate limit and gives access to private feeds, so it deserves a top-level verb rather than being three words deep. Both spellings share one `LoginArgs` struct and run the same command, so flags, help text and behaviour cannot drift; the nested variant becomes a tuple variant over that struct. Neither routes to the avocado-vm: they are pure Connect API calls, and routing could auto-start a virtual machine for a login. The regression test asserts all five shared fields rather than a sample — the whole point is that the two cannot diverge, and a test covering three of five would not notice the other two diverging. Review also noted that this file aliases `anyhow::Result` as `Result` everywhere else including `main`, so the one `anyhow::Result` spelling invited the reader to look for a difference that was not there. One review finding was answered rather than changed: the claim that borrowing a temporary across the `.await` cannot compile. A temporary lives to the end of its enclosing statement, so it does; the build and the full suite confirm it.
f51e935 to
fd6ee04
Compare
|
Superseded by a fresh PR from this same branch (head fd6ee04, CI green): GitHub's stack handling blocks both the admin merge and the async merge once the parent landed and the approval was dismissed. |
Supersedes #242 (same branch, head fd6ee04, CI green on that sha); recreated because GitHub's stack handling blocked merging the original after #241 landed. > **Owner: the secure-feeds series** (241 → 242 → 243 → 244). Both sessions push as `mobileoverlord`, so GitHub cannot tell you which one wrote a PR — this line can. Adds `avocado login` as a top-level shortcut for `avocado connect auth login`. Logging in is what raises the package-feed rate limit and unlocks private feeds, so it deserves a top-level verb rather than being three words deep. Both spellings share one `LoginArgs` struct and run the same command, so flags, help text and behaviour cannot drift; the nested variant becomes a tuple variant over that struct. `-p` and the other short flags are untouched. Neither spelling routes to the avocado-vm: they are pure Connect API calls, and routing could auto-start a virtual machine for a login. ## How it was tested A parse test asserts that `avocado login` and `avocado connect auth login` produce the same arguments from the same flags, and that VM routing is off for both. That is the regression that matters, since the whole point is that the two cannot diverge. Stacked on #241, which it does not depend on functionally — it is based there only to keep the branch history linear. Review it on its own; the diff is one file.
#262) Supersedes #243 (same branch, GitHub-rebased head 5be014d); recreated because GitHub's stack handling blocked merging the original after its parent landed. > **Owner: the secure-feeds series** (241 → 242 → 243 → 244). Both sessions push as `mobileoverlord`, so GitHub cannot tell you which one wrote a PR — this line can. Makes `org:` feeds work, and stops re-resolving feeds for every container. ## Private organization feeds ```yaml repos: acme: org: acme # release/channel optional; branch defaults to main distro: feeds: [acme] ``` The CLI exchanges its Connect credential for a short-lived feed token, injects it into the generated repository file, and dnf reads a feed that refuses anonymous access. Previously `org:` parsed and was refused. **The token never becomes a build input.** Resolution records only what is stable — the organization as the credential identity, and a `connect://<org>/<path>` placeholder as the URL — and the recorded document is written before anything is minted. A per-build token in the stamp hash would invalidate every cached sysroot once per build, and a server-side host change should not either, because the organization is the input and the host is a detail of how it was served. The private tree mirrors the public one, so an organization feed is a distro-shaped feed whose release version is `<release>/orgs/<org>/<branch>`, and no new path construction was needed. The minted URL also goes through the loopback rewrite, which URL feeds get at resolution time and this had to get at mint time, or a locally hosted Connect resolves to nothing inside the container. Errors name the remedy: not logged in points at `avocado login`, 403 says the account is not entitled, 404 says the deployment does not serve feed tokens yet. ## Feeds are materialized once per invocation Previously every container run re-resolved, re-minted and wrote a fresh temporary directory. Two consequences: - A single `avocado build` minted **five** feed tokens. It is one per invocation now, and the end-to-end rig asserts it, so a regression fails a test rather than surfacing later as a rate limit refusing an ordinary build. - The mount list is part of a container's shape, so a per-run directory gave every step a unique shape and prevented sharing a container between steps. Each stage writes into its own subdirectory and `AVOCADO_FEEDS_DIR` selects it, so the mount is identical across steps while dnf still sees only its stage's feeds. Credentials now live as long as the invocation rather than one step; nothing reaches the SDK volume or the project directory, and the directory is removed when the process ends. The condition that would invalidate that trade, untrusted code running in the shared container, is named at the function. ## Test infrastructure included `scripts/local-feeds/edge.py` implements the observable contract of three pieces that do not exist yet — the token mint, the ES256 verifier, and the rate limiter — so this could be built and tested before any of them. `contract-tests.sh` asserts that contract against a base URL, naming no implementation, so the same file can later run against a real deployment. `org-feed-rig.sh` proves the whole path: mint, inject, authenticate, install, and no token in the recorded document. Two behaviours worth calling out, both found by review against the server design. A missing credential is answered with **401 and a Basic challenge**, not 403, because dnf authenticates only when challenged and only to Basic; 403 fails every fetch while working perfectly under `curl -u`. And the organization is a path **segment**, not a prefix, because the release precedes it — a prefix check would silently degrade into any authenticated organization being able to read any other. ## Status The server side of this is several phases out, so `org:` currently fails closed with a clear message rather than working. Merging it early is what lets the client be finished and tested; landing it later is also fine. Say which you prefer. Stacked on #242.
## What Cut `1.0.0-rc.4`: the `[Unreleased]` changelog section becomes `## [1.0.0-rc.4] - 2026-09-15`, and the crate version moves from `1.0.0-rc.3` to `1.0.0-rc.4` in `Cargo.toml` and `Cargo.lock`. ## Release notes The rc.4 section of `CHANGELOG.md` is the release notes. It covers everything merged since rc.3, grouped Added / Changed / Fixed / Security: - **Added:** named package feeds (`repos:` / `distro.feeds`, #241), private organization feeds (#243), feed set and package origin in the lock (#244), `avocado login` (#242), `kernel.cmdline` / `cmdline_extra` (#252), the managed `avocado hitl` NFS server (#258), the `avocado install` up-to-date skip (#260), per-runtime and per-extension SBOMs, and the `build` / `provision` var-image split (breaking: `build` no longer produces the var image). - **Changed:** installs no longer prompt and `--force` means reinstall only; `--output json` no longer implies `--force`; session containers (exec reuse, `atexit` teardown, Ctrl-C reaping, no async-runtime blocking); stamp coverage and output digests (`STAMP_VERSION` 2 → 3 → 4); `ext build` / `ext image` / rootfs / initramfs image skips; no more image copying and re-hashing in runtime builds. - **Fixed:** the `depends_on` closure is built (#253), package-sourced overlays ship (#254), the overlay staging dir is cleared every build (#255), the SDK entrypoint reads one `VERSION_CODENAME` (#240), kernel changes invalidate extension stamps and clean the sysroot (#267, `STAMP_VERSION` 4 → 5), plus the stamp-system hardening from the dogfood runs. - **Security:** `rustls` 0.23.45 for RUSTSEC-2026-0285. Every bullet that was under `[Unreleased]` on `main` is present; entries for the PRs that landed without a changelog line were added. An empty `[Unreleased]` heading remains for the next cycle. ## After merge Tag the squash commit `1.0.0-rc.4` to run the release workflow. The first `avocado build` after upgrading reports every stamp as "stamp format changed (v4 → v5)" and rebuilds once. ## Verification fmt, clippy (`-D warnings`) and the full test suite pass on this branch; the version guard test reads `1.0.0-rc.4`.
Adds
avocado loginas a top-level shortcut foravocado connect auth login.Logging in is what raises the package-feed rate limit and unlocks private feeds, so it deserves a top-level verb rather than being three words deep.
Both spellings share one
LoginArgsstruct and run the same command, so flags, help text and behaviour cannot drift; the nested variant becomes a tuple variant over that struct.-pand the other short flags are untouched.Neither spelling routes to the avocado-vm: they are pure Connect API calls, and routing could auto-start a virtual machine for a login.
How it was tested
A parse test asserts that
avocado loginandavocado connect auth loginproduce the same arguments from the same flags, and that VM routing is off for both. That is the regression that matters, since the whole point is that the two cannot diverge.Stacked on #241, which it does not depend on functionally — it is based there only to keep the branch history linear. Review it on its own; the diff is one file.