From d0642c1b86fcd6a45a1c32b0dc34fac2a33cfad9 Mon Sep 17 00:00:00 2001 From: Shawn Bai Date: Sat, 10 Oct 2026 13:02:19 +1100 Subject: [PATCH 1/2] fix: improve upgrade error for missing release version --- cmd/ocm-backplane/upgrade/upgrade.go | 18 ++++++++++ .../upgrade/upgrade_suite_test.go | 13 +++++++ cmd/ocm-backplane/upgrade/upgrade_test.go | 36 +++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 cmd/ocm-backplane/upgrade/upgrade_suite_test.go create mode 100644 cmd/ocm-backplane/upgrade/upgrade_test.go diff --git a/cmd/ocm-backplane/upgrade/upgrade.go b/cmd/ocm-backplane/upgrade/upgrade.go index c44050ce6..c448c9975 100644 --- a/cmd/ocm-backplane/upgrade/upgrade.go +++ b/cmd/ocm-backplane/upgrade/upgrade.go @@ -31,6 +31,10 @@ var UpgradeCmd = &cobra.Command{ func runUpgrade(cmd *cobra.Command, _ []string) error { + if err := validateReleaseVersion(info.Version); err != nil { + return err + } + ctx, cancel := context.WithCancel(cmd.Context()) defer cancel() @@ -44,3 +48,17 @@ func runUpgrade(cmd *cobra.Command, _ []string) error { return upgrade.UpgradePlugin(ctx, info.Version) } + +// validateReleaseVersion ensures the binary carries the release version metadata +// that the native self-upgrade relies on for the SemVer comparison. This metadata +// is injected via ldflags only when building official release binaries, so a CLI +// built locally from source has an empty version and cannot self-upgrade. +func validateReleaseVersion(version string) error { + if version == "" { + return fmt.Errorf("release version metadata is missing; this usually happens when the CLI is built locally from source. " + + "Native self-upgrade requires release version metadata. " + + "Use 'backplane-tools upgrade backplane-cli' to install or update an official release") + } + + return nil +} diff --git a/cmd/ocm-backplane/upgrade/upgrade_suite_test.go b/cmd/ocm-backplane/upgrade/upgrade_suite_test.go new file mode 100644 index 000000000..d47c1869c --- /dev/null +++ b/cmd/ocm-backplane/upgrade/upgrade_suite_test.go @@ -0,0 +1,13 @@ +package upgrade + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestUpgrade(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Upgrade Command Test Suite") +} diff --git a/cmd/ocm-backplane/upgrade/upgrade_test.go b/cmd/ocm-backplane/upgrade/upgrade_test.go new file mode 100644 index 000000000..2ce2c0fe1 --- /dev/null +++ b/cmd/ocm-backplane/upgrade/upgrade_test.go @@ -0,0 +1,36 @@ +package upgrade + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("validateReleaseVersion", func() { + Context("when the release version metadata is missing", func() { + It("returns a clear, actionable error", func() { + err := validateReleaseVersion("") + + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("release version metadata is missing")) + Expect(err.Error()).To(ContainSubstring("built locally from source")) + Expect(err.Error()).To(ContainSubstring("backplane-tools upgrade backplane-cli")) + }) + + It("does not expose the raw SemVer parsing error", func() { + err := validateReleaseVersion("") + + Expect(err).To(HaveOccurred()) + Expect(err.Error()).ToNot(ContainSubstring("Invalid Semantic Version")) + }) + }) + + Context("when the release version metadata is present", func() { + It("does not trigger the missing-version validation", func() { + Expect(validateReleaseVersion("1.0.1")).To(Succeed()) + }) + + It("passes the version through unchanged regardless of its format", func() { + Expect(validateReleaseVersion("1.0.1-0.20261009184111-fc8c9e8dedd0")).To(Succeed()) + }) + }) +}) From 0a271a8b911759cd756abaf156fb2714a8e71442 Mon Sep 17 00:00:00 2001 From: Shawn Bai Date: Sun, 11 Oct 2026 11:20:09 +1100 Subject: [PATCH 2/2] fix: clarify native upgrade error guidance --- cmd/ocm-backplane/upgrade/upgrade.go | 7 ++++--- cmd/ocm-backplane/upgrade/upgrade_test.go | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cmd/ocm-backplane/upgrade/upgrade.go b/cmd/ocm-backplane/upgrade/upgrade.go index c448c9975..640e7ee59 100644 --- a/cmd/ocm-backplane/upgrade/upgrade.go +++ b/cmd/ocm-backplane/upgrade/upgrade.go @@ -52,12 +52,13 @@ func runUpgrade(cmd *cobra.Command, _ []string) error { // validateReleaseVersion ensures the binary carries the release version metadata // that the native self-upgrade relies on for the SemVer comparison. This metadata // is injected via ldflags only when building official release binaries, so a CLI -// built locally from source has an empty version and cannot self-upgrade. +// built without that injection (commonly a local source build) has an empty +// version and cannot self-upgrade. func validateReleaseVersion(version string) error { if version == "" { - return fmt.Errorf("release version metadata is missing; this usually happens when the CLI is built locally from source. " + + return fmt.Errorf("release version metadata is missing; this commonly happens when the CLI is built from source without release version injection. " + "Native self-upgrade requires release version metadata. " + - "Use 'backplane-tools upgrade backplane-cli' to install or update an official release") + "Install or update Backplane CLI through an official release channel") } return nil diff --git a/cmd/ocm-backplane/upgrade/upgrade_test.go b/cmd/ocm-backplane/upgrade/upgrade_test.go index 2ce2c0fe1..aad9b3db8 100644 --- a/cmd/ocm-backplane/upgrade/upgrade_test.go +++ b/cmd/ocm-backplane/upgrade/upgrade_test.go @@ -12,8 +12,8 @@ var _ = Describe("validateReleaseVersion", func() { Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("release version metadata is missing")) - Expect(err.Error()).To(ContainSubstring("built locally from source")) - Expect(err.Error()).To(ContainSubstring("backplane-tools upgrade backplane-cli")) + Expect(err.Error()).To(ContainSubstring("built from source")) + Expect(err.Error()).To(ContainSubstring("official release channel")) }) It("does not expose the raw SemVer parsing error", func() {