fix: accept IDs as well as names for --channel, --environment and --tenant - #702
Draft
NickJosevski wants to merge 1 commit into
Draft
fix: accept IDs as well as names for --channel, --environment and --tenant#702NickJosevski wants to merge 1 commit into
NickJosevski wants to merge 1 commit into
Conversation
…enant The executions API only matches channels, environments and tenants by name, so `release create`, `release deploy` and `runbook run` passed whatever the caller typed straight through and the server rejected IDs. `--project` already worked because the server accepts a project ID or name. Resolve those identifiers client side through the shared selectors package before handing them to the executor, preferring an ID match over a name match so it behaves the same way as `--project`. Fixes #250 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #250
The bug
The reporter's build tooling uses IDs everywhere (names change, IDs don't).
--projectaccepts an ID, but--channel,--environmentand--tenantdid not:Root cause: the executions API (
releases/create/v1,deployments/create/{un}tenanted/v1,runbook-runs/create/v1) takesprojectNameas an ID-or-name — the server resolves it — butchannelIDOrName,environmentName(s)andtenantsare matched by name only, server side. The CLI passed whatever the caller typed straight through, so IDs blew up.Name-vs-ID inventory (before this PR)
release create--projectselectors.FindProject→Projects.GetByIdentifier--channelselectors.FindChannel(interactive) / passed through (--no-prompt)--git-ref,--git-commit--package,--package-version,--git-resource--version,--release-notes,--ignore-*,--custom-fieldsrelease deploy--projectselectors.FindProject--environmentexecutionscommon.FindEnvironments(some paths) / passed through--tenant--tenant-tagRegions/us-east)--deployment-target/--exclude-deployment-targetSpecificMachineNames/ExcludedMachineNames--skip--deployment-freeze-nameDeploymentFreezeNames--version,--deploy-at,--variable, …Note on
--environment:executionscommon.FindEnvironmentsalready matched name-or-ID, but (a) only some code paths called it, (b) it never wrote the canonical name back into the options, so the raw ID still went to the server, and (c) the tenanted path usedselectors.FindEnvironment, which was name-only.runbook run--projectselectors.ResolveProject--snapshotrunbooks.GetSnapshot--name/--runbookrunbooks.GetByName--runbook-tag--environment--tenant--tenant-tag--run-target/--exclude-run-target--skipWhat changed
Everything goes through the shared
selectorspackage, so every caller benefits:selectors.FindEnvironments/FindEnvironment(pkg/question/selectors/environments.go) — the one environment resolver. OneGET /environments/all, matched client side by ID first, then name.executionscommon.FindEnvironmentsnow delegates to it, so its ~12 existing callers (target create commands,tenant connect) pick up the consistent precedence for free.selectors.FindChannel— now matches a channel's ID as well as its name, still scoped to the project so an ID from another project is never returned.selectors.FindTenant/FindTenants(newpkg/question/selectors/tenants.go) — wrapsTenants.GetByIdentifier(ID first, then name), turning "not found" into a clear message.release create— resolves--channelin the--no-promptpath and sends the canonical name.release deploy— resolves--tenantfor both paths and--environmentin the--no-promptpath; the interactive paths now write the resolved canonical name back into the options so what's sent (and what appears in the generated automation command) is the real name, not the raw input.runbook run— resolves--environmentand--tenantonce, right after the project is resolved, covering the db/git/by-tag and prompt/no-prompt paths.release deployfalls back to the ephemeral lookup when the regular one comes up empty (resolveEnvironmentNames).Precedence rule
Try an exact ID match first, fall back to an exact name match (case-insensitive).
This matches what
--projectalready does (Projects.GetByIdentifier=GetByID, thenGetByName) and whatchannel/shared.ResolveChannelalready did. So an entity that is genuinely namedEnvironments-201loses to the entity whose ID isEnvironments-201. It is documented in the resolver comments and covered by tests ("prefers an ID match over a name match").Note this flips the old precedence inside
executionscommon.FindEnvironments, which tried name first. That only matters when a name collides with a different entity's ID, and consistency with--projectseemed more valuable than preserving the old order.Error messages
Uniform and explicit about both forms being accepted:
cannot find an environment with the ID or name of 'Environments-404'cannot find a channel in project 'Fire Project' with the ID or name of 'Channels-404'cannot find a tenant with the ID or name of 'Tenants-404'The channel message previously read
no channel found with name of X; the two channel test files touched (channel/delete,channel/view) only update that expected string — no behaviour change to those commands.Test evidence
New tests:
pkg/question/selectors/find_test.go— table tests forFindEnvironments/FindEnvironment/FindChannel/FindTenants: by name, by name ignoring case, by ID, several at once, ID beats a colliding name, and the not-found error text.release create— "release creation specifying the project and channel by ID": asserts the POST body carriesChannelIDOrName: "BetaChannel"when--channel Channels-31was passed.release deploy— "release deploy specifying project, environment and tenant by ID": assertsEnvironmentName: "dev"/Tenants: ["Coke"]when--environment Environments-12 --tenant Tenants-29was passed.runbook run— "runbook run specifying project, environment and tenant by ID": same assertion forrunbook-runs/create/v1.Existing tests updated for the extra lookup requests (and one deploy test now asserts
"Ephemeral Environment"instead of the lowercase"ephemeral environment"the user typed — that normalisation is the fix working).go vet ./...reports only two pre-existingunreachable codewarnings inpkg/cmd/tenant/variables/list/list.go, untouched here.Open questions / options
Precedence: ID first, or name first? Chosen: ID first, for consistency with
--project. The alternative — name first — would mean an environment namedEnvironments-201shadows the environment with that ID, which is arguably the more surprising outcome and is inconsistent with--project. A third option is to error on ambiguity, which is the most correct but adds a failure mode to a hot path for a collision nobody has ever reported. Recommendation: keep ID first.Extra round trip.
runbook run --environment Xandrelease deploy --no-prompt --environment Xnow always do oneGET /environments/all(and one or two calls per--tenant) that they previously skipped. That's the price of resolving client side. The alternative is a "looks like an ID" regex (^Environments-\d+$) to skip the lookup for plain names — faster, but it bakes in an ID-format assumption and makes the collision case incoherent. Recommendation: keep the unconditional lookup; revisit only if someone measures a problem.executionscommon.FindEnvironmentsis now a one-line alias forselectors.FindEnvironments. I kept it so the ~12 callers in the target-create commands andtenant connectstay untouched and this PR's diff stays on-topic. Recommendation: delete it and update the call sites in a follow-up, in line with the recentselectors.ResolveProject/selectors.Channelconsolidation — happy to fold that in here instead if reviewers prefer.Flags deliberately left alone — all name-only, all out of scope, none reported:
--deployment-target/--exclude-deployment-target/--run-target/--exclude-run-target(machine names),--skip(step names),--deployment-freeze-name,--name/--runbook(runbook name), and the package/git-resource step specs. Machines and runbooks would be the natural next candidates if we want ID support everywhere; the server-side commands for those take names only, so each needs the same client-side resolve-then-send treatment. Recommendation: follow-up issue rather than growing this PR.--tenant-taghas no ID form at all — canonical tag paths are the only identifier.Should
release create --channelresolve in interactive mode too? It already did (viaselectors.FindChannel), and now picks up ID support for free. Worth a reviewer sanity check that echoing the resolved name back (rather than the ID the user typed) is the desired UX. I think it is — it confirms what the ID pointed at.🤖 Generated with Claude Code