Skip to content

fix: report unknown release versions instead of a server null reference - #696

Draft
NickJosevski wants to merge 1 commit into
mainfrom
nj/issue-294
Draft

fix: report unknown release versions instead of a server null reference#696
NickJosevski wants to merge 1 commit into
mainfrom
nj/issue-294

Conversation

@NickJosevski

Copy link
Copy Markdown
Contributor

Refs #294

Root cause

octopus release deploy --version latest (or any version that doesn't exist) sends the literal string straight through to the executions API:

deployRunexecutor.releaseDeployPOST /api/{space}/deployments/create/untenanted/v1 with ReleaseVersion: "latest".

In automation mode (--no-prompt) nothing on the client ever looks the release up, so the server is left to resolve a release named latest, finds none, and NREs. The CLI faithfully prints what it got back:

Octopus API error: Object reference not set to an instance of an object. []

That's the bug benPearce1 confirmed in the issue. This PR fixes that half. The latest feature itself is not implemented here — see Open questions below.

What changed

New selectors.FindRelease (pkg/question/selectors/releases.go) — sits alongside the existing FindProject / FindChannel / FindRunbook helpers and wraps releases.GetReleaseInProject, turning "no such release" into a message that names what was asked for:

cannot find a release with version '9.9' in project 'Fire Project'

and, for the literal latest, one that says why and what to do instead:

cannot find a release with version 'latest' in project 'Fire Project'; 'latest' is not a
supported alias, specify an exact version. Run 'octopus release list --project "Fire Project"'
to see the available versions

Two not-found shapes are handled: a 404 carrying an APIError body, and a 404 with an empty body — the latter doesn't reach go-octopusdeploy's error path at all, it decodes into a zero-valued Release, so the empty ID is checked as well. Any other error (403, 500, transport) is returned untouched, so the server stays the authority on things like permissions.

release deploy automation path now resolves the release after resolving the project, before building the deployment command. Side benefit: options.ReleaseID is populated up front, so the two extra lookups that used to happen after deploying just to print the "View this release" link are gone. Net API calls: unchanged for table output, +1 for --output-format basic|json.

release deploy interactive path now goes through the same helper, so a bad --version gets the same message instead of dereferencing an empty release and failing later on a channel lookup.

release progression allow/preventprogression/shared.FindRelease now delegates to the new selector rather than duplicating the lookup with a different message.

Test evidence

Added two cases to TestDeployCreate_AutomationMode, one per not-found shape:

  • release deploy reports a release version that doesn't exist — 404 with an APIError body
  • release deploy explains that 'latest' is not a supported release version — 404 with an empty body

Existing automation tests were updated for the new pre-flight lookup (added expectation) and the removed post-deploy lookups.

$ go build ./...
(clean)

$ go test ./pkg/... -timeout 300s
ok  github.com/OctopusDeploy/cli/pkg/cmd/release/deploy               5.294s
ok  github.com/OctopusDeploy/cli/pkg/cmd/release/progression/allow    4.870s
ok  github.com/OctopusDeploy/cli/pkg/cmd/release/progression/prevent  4.931s
ok  github.com/OctopusDeploy/cli/pkg/question/selectors               5.737s
... 60 packages, all ok, exit 0

go vet ./pkg/... reports only the four pre-existing unreachable code findings in worker/shared, workerpool/shared, and tenant/variables/list.

Open questions / options — needs a maintainer call

benPearce1 called dropping latest intentional ("could mean latest by time or latest by semver"); DamienDaco wants the old octo behaviour back to deploy the newest release. That's a product decision, so this PR deliberately stops at the error message and leaves latest unimplemented. The three ways forward:

(a) --version latest = most recently created release (by Assembled date). Matches old octo, zero new flags, drops straight into existing CI scripts. Costs: re-introduces exactly the ambiguity that motivated the removal, and quietly disagrees with a user who assumed semver — for a release with a version prefix scheme or an out-of-order rebuild, "newest by time" and "highest by semver" are different releases, and the command that deploys the wrong one gives no hint. Also collides with a real release literally named latest (rare, but the alias would win). Currently the error message tells users this alias doesn't exist; implementing (a) makes that message wrong, so it has to land as one change.

(b) An explicit modifier, e.g. --latest-by time|semver (mutually exclusive with --version). No ambiguity, self-documenting in --help, and the two meanings stay separately addressable. Costs: new flag surface on an already wide command; needs a decision on whether it also applies to runbook run, and on whether --channel should scope it (deploying "the latest release" usually means "latest in this channel" — GetReleasesInProjectChannel already exists for that).

(c) Ship nothing beyond this error fix. The message now names the project and points at octopus release list, so the failure is self-service. Scripts that want the newest release can do it explicitly today with octopus release list -p MyProject -f json.

Recommendation: (b), scoped to release deploy first, with --latest-by semver and --latest-by time both spelled out, optionally narrowed by --channel. It gives DamienDaco the capability without re-introducing the ambiguity that got latest removed, and it degrades honestly — a user who picks the wrong ordering picked it explicitly. If parity with octo matters more than precision, (a) is a much smaller change and I'm happy to swap. Either way the latest branch of releaseNotFoundError gets deleted as part of implementing it.

Happy to implement whichever you pick in this PR.

🤖 Generated with Claude Code

`release deploy` passed --version straight to the executions API, which
answers an unknown version with "Object reference not set to an instance
of an object". Resolve the release before deploying so a version that
doesn't exist is reported by name, and call out `latest` explicitly since
it is not a supported alias.

Refs #294

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant