feat(update): address envs by their printed key; add scope flags (#166)#167
Conversation
`b update <arg>` resolved every arg through the binary parser first, which
splits on `@` and is not SSH-aware. So the exact key `b env status` prints
for an SSH-keyed env (e.g. git@github.com:org/repo#main) could never be
passed back — it parsed to "git" and failed as "unknown binary or env".
`--group` only filtered envs while still updating every binary, so there
was no way to refresh vendored env content without toolchain churn.
Resolution is now driven by config membership and an env marker instead of
guessing from shape:
- docker:// / oci:// / go:// stay binary ('#' there is a path/module char).
- A local path, or a '#' outside those protocols, is an env marker: resolve
as env (SSH/local-aware), error if no such env.
- Otherwise match envs by exact/canonical key before binary space, so the
printed key round-trips and an env ref isn't hijacked by the provider
convention. A trailing '#' forces env for keys that are also valid binary
refs (the dual-namespace escape hatch).
- Typing the https path of an SSH-keyed env still resolves to an ad-hoc
binary but prints a "did you mean env …" hint.
Scope flags:
- --group now implies env-only (groups are an env-only concept).
- add --envs-only / --binaries-only, validated as mutually exclusive and
rejected alongside explicit args.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes b update <arg> ambiguity between binaries and envs (notably SSH-keyed env refs) by resolving args based on config membership and explicit env markers, and adds namespace scoping flags to update only envs or only binaries.
Changes:
- Add
resolveUpdateArgto correctly round-trip env keys (includinggit@...#label) and provide “did you mean env …” hints when users type an https form that maps to an SSH-keyed env. - Introduce update scoping via
--envs-only/--binaries-only, and make--groupimply env-only when runningb updatewith no args. - Add docs + tests covering env address resolution, hinting, and scoping behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| pkg/cli/update.go | Implements new arg resolution and scope flag validation + run-all scoping. |
| pkg/cli/update_envaddr_test.go | Adds resolver and scoping tests for issue #166 regression coverage. |
| docs/b/subcommands/update.mdx | Documents env addressing and new scope flags/--group semantics. |
|
Review — solid fix; resolves the core of #166. Membership-based 1. Short-name addressing —
|
Copilot: - reject contradictory flag combos in Validate(): --plan-json with --binaries-only (binaries never appear in the JSON plan), and --group with explicit args (--group is env-only; a non-matching group would otherwise silently drop the named env to a no-op). The latter also covers Copilot's "--group still runs named binaries" point and the subagent's group no-op. - (refuted) "EnvList literals won't compile" — EnvList is []*EnvEntry and Go elides & in pointer-element composite literals; build + tests are green. Gemini review: - short env handles: matchEnv gains a third tier (only on the explicit '#'/path marker) that suffix-matches a repo basename or org/repo tail against env keys, so `lok8s#` / `org/lok8s#` reach `git@github.com:org/lok8s#main`. Ambiguous handles error with the candidate keys. Closes issue #166's short-handle gap and also makes a trailing '#' resolve a *labeled* env. Bare names (no marker) still never match an env, so they can't shadow a binary. - consolidate repoTail + the new matcher onto a shared cleanRepoPath helper. - docs: document short handles and the https-path → binary hint. - confirmed the config generator writes no `b env sync` comment (v4.13.0 only). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The env-sync guide showed SSH-keyed envs and --group but never how to update just one env — the exact gap behind #166. Add an 'Update a specific env' section (exact key / SSH / #label round-trip, short handles, --envs-only) and note that --group is env-only, cross-linking the b update reference. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…che dedup Copilot: - the https-vs-SSH guidance was wrong: appending '#' to an https arg (github.com/org/repo#) can't match an SSH-keyed env (the SSH identity has no host segment). The stderr note, the unknownArgError, and update.mdx now offer working forms only — the exact key or a short handle (org/repo#) — via a new envUpdateHint helper. - plan-json with binaries configured but no envs emitted no JSON at all (pre-existing); runAll now counts binaries as "nothing" under --plan-json so it reliably prints []. Regression test added. Subagent (round 2, fix-and-ship): - move transport-stripped repo-path extraction into gitcache.RepoPath (repo IDENTITY, contrasted with transport-preserving GitURL) and drop cli's duplicate cleanRepoPath; repoTail + matchEnvShort now reuse it. Also fixes the ssh:// explicit form (drops the leading user@host segment). gitcache test added. (round-1 repoTail dup finding resolved here too.) - tests: wrong-org short handle is rejected; short-handle matching is case-insensitive. Docs: README + env-sync addressing examples already added; update.mdx hint wording corrected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Subagent (round 3, ship): - envUpdateHint is now a method that appends the "(or short: b update <tail>#)" suggestion only when exactly one configured env has that org/repo tail (shortHandleUnique); otherwise the short form would itself fail to resolve. Both call sites (the ad-hoc-binary note and unknownArgError) updated. - strengthen TestRunAll_PlanJSON_BinariesOnlyConfig: assert binaries are NOT run under --plan-json (not just that output is []). - new TestEnvUpdateHint_OmitsAmbiguousShortHandle. Declined: RepoPath keeping a port as a path segment (host:22/...) — ports never appear in real env keys, and repoTail / right-anchored suffix matching are unaffected, so it can't cause a mismatch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot: suggestEnv returned the first env matching the repo tail, so when two envs share a tail (e.g. github + gitlab mirrors) the "did you mean env" hint could name the wrong key. Replaced with suggestEnvs (all matches) + an envHint helper: a single match names the key + copy-paste command; multiple matches list all candidates and omit the (ambiguous) short handle. Both the ad-hoc-binary note and unknownArgError use it. Test: HintsAllOnAmbiguousTail. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…// marker Three Copilot findings on the RepoPath work that an earlier round's timestamp filter missed (caught by the pre-merge thread-resolution check): - gitcache.RepoPath mistook a host:port for an scp "host:path" separator after an explicit scheme (ssh://host:2222/org/repo, https://host:443/org/repo), dropping the host; and trimmed .git before trailing slashes so "repo.git/" didn't normalize. Now the scp-colon strip runs only when there was no scheme, the leading user@host[:port] segment is dropped for ssh:// forms, and .git is stripped after slashes. Tests added for both port and trailing-slash forms. - the '#' env-marker heuristic also caught git://repo:<filepath> binary refs whose in-repo path legally contains '#' (forced env → "unknown env"). git:// is now exempt from the marker rule (added to hasBinaryProto); git:// envs stay addressable by exact/canonical key. Test: GitProtoPathHash_StaysBinary. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Review loop complete — 4 rounds (Copilot + subagent in parallel) + a final verification pass. Findings addressed across 5a5a043, ea84a9a, 47839b4, 1f5a05d, bf6f3b6: env-key round-trip incl. SSH keys, short handles, ambiguity-aware hints, --group/--envs-only/--binaries-only scope, gitcache.RepoPath (ports/.git/), git:// # exemption, --plan-json empty-array fix. 1 false positive refuted, 2 nits declined with rationale. All threads resolved; build/vet/fmt/test green. |
The squash-merge of #167 to main did not fire the push trigger (0 workflow runs registered for the merge commit), so the 4.18.0 release PR was never opened. Add a workflow_dispatch trigger as a manual fallback; this commit's push also re-triggers the workflow. Mirrors e8f917e for the docs deploy. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
🤖 I have created a release *beep* *boop* --- ## [4.18.0](v4.17.2...v4.18.0) (2026-06-24) ### Features * **update:** address envs by their printed key; add scope flags ([#166](#166)) ([#167](#167)) ([8d02543](8d02543)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
Fixes #166.
Problem
b update <arg>resolved every arg through the binary parser first (provider.ParseRef), which splits on@and is not SSH-aware. So:b env statusprints for an SSH-keyed env (git@github.com:org/repo#main) could never be passed back — it parsed togitand failed as "unknown binary or env: git".github.com/acme/frameworksilently fell into binary space → "release not found".--groupfiltered only envs while still updating every binary, so there was no way to refresh vendored env content without toolchain churn.Root cause: one flat positional addresses two namespaces (binaries + envs) with overlapping grammars, and we committed to the binary grammar before the env namespace (which has the SSH-aware parser) was ever consulted.
Fix — resolve by config membership + an env marker
resolveUpdateArgnow decides by what's actually inb.yaml, not by guessing from shape:docker:///oci:///go://→ always binary (#there is a path/module char, never an env label).#outside those protocols → env marker: resolve as env (SSH/local-aware), error if no such env (the user was explicit).github.com/org/repoprovider convention. A trailing#forces env for keys that are also valid binary refs (dual-namespace escape hatch).Scope flags
--groupnow implies env-only (groups are an env-only concept — binaries have no group). This is the direct fix for the--grouppart of b update can't scope to a single env — SSH-keyed envs are unaddressable, and --group doesn't gate binaries #166.--envs-only/--binaries-only, validated mutually-exclusive and rejected alongside explicit args.Notes
b.yamlheader saying "Runb env sync") is already gone from current source — nothing to change.b.yamlon save; the membership resolver makes it unnecessary, and silently rewriting user keys is the class of bug we fixed in fix(state): keep relative file paths on save; clearer install errors #159.Tests
pkg/cli/update_envaddr_test.go(19 cases): SSH-key round-trip, label-less SSH bare, trailing-#force, env-marker-no-match error, https-key-not-hijacked regression guard, claim-3 hint, docker-#-stays-binary,repoTailfolding, scope-flag validation, and threerunAllscoping tests.go build,go vet ./...,gofmt, fullgo test ./...all clean.🤖 Generated with Claude Code