From 4f2fe27737a532f4c1cc40d5edd199cafaeb24e7 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Sun, 3 May 2026 23:31:10 -0500 Subject: [PATCH] feat(cmd): rename `upgrade` command to `self-update` Free the `upgrade` verb for devcontainer feature upgrades by renaming the CLI self-update command from `upgrade` to `self-update`. --- cmd/root.go | 2 +- cmd/{upgrade.go => self_update.go} | 26 ++++++++-------- cmd/self_update_test.go | 36 ++++++++++++++++++++++ e2e/e2e_suite_test.go | 2 +- e2e/tests/selfupdate/selfupdate.go | 48 ++++++++++++++++++++++++++++++ e2e/tests/upgrade/upgrade.go | 47 ----------------------------- 6 files changed, 99 insertions(+), 62 deletions(-) rename cmd/{upgrade.go => self_update.go} (51%) create mode 100644 cmd/self_update_test.go create mode 100644 e2e/tests/selfupdate/selfupdate.go delete mode 100644 e2e/tests/upgrade/upgrade.go diff --git a/cmd/root.go b/cmd/root.go index d97545ddf..bd4084dcc 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -125,7 +125,7 @@ func BuildRoot() *cobra.Command { rootCmd.AddCommand(NewExportCmd(globalFlags)) rootCmd.AddCommand(NewImportCmd(globalFlags)) rootCmd.AddCommand(NewLogsCmd(globalFlags)) - rootCmd.AddCommand(NewUpgradeCmd()) + rootCmd.AddCommand(NewSelfUpdateCmd()) rootCmd.AddCommand(NewTroubleshootCmd(globalFlags)) rootCmd.AddCommand(NewPingCmd(globalFlags)) rootCmd.AddCommand(NewReadConfigurationCmd(globalFlags)) diff --git a/cmd/upgrade.go b/cmd/self_update.go similarity index 51% rename from cmd/upgrade.go rename to cmd/self_update.go index 2604fd56a..5bb03136b 100644 --- a/cmd/upgrade.go +++ b/cmd/self_update.go @@ -7,32 +7,32 @@ import ( "github.com/spf13/cobra" ) -// UpgradeCmd is a struct that defines a command call for "upgrade". -type UpgradeCmd struct { +// SelfUpdateCmd is a struct that defines a command call for "self-update". +type SelfUpdateCmd struct { Version string DryRun bool } -// NewUpgradeCmd creates a new upgrade command. -func NewUpgradeCmd() *cobra.Command { - cmd := &UpgradeCmd{} - upgradeCmd := &cobra.Command{ - Use: "upgrade", - Short: "Upgrade the Devsy CLI to the newest version", +// NewSelfUpdateCmd creates a new self-update command. +func NewSelfUpdateCmd() *cobra.Command { + cmd := &SelfUpdateCmd{} + selfUpdateCmd := &cobra.Command{ + Use: "self-update", + Short: "Update the Devsy CLI to the newest version", Args: cobra.NoArgs, RunE: func(cobraCmd *cobra.Command, args []string) error { ctx := cobraCmd.Context() if err := upgrade.Upgrade(ctx, cmd.Version, cmd.DryRun); err != nil { - return fmt.Errorf("unable to upgrade: %w", err) + return fmt.Errorf("unable to update: %w", err) } return nil }, } - upgradeCmd.Flags(). + selfUpdateCmd.Flags(). StringVar(&cmd.Version, "version", "", "The version to update to. Defaults to the latest stable version available") - upgradeCmd.Flags(). - BoolVar(&cmd.DryRun, "dry-run", false, "Show which version would be downloaded without actually upgrading") - return upgradeCmd + selfUpdateCmd.Flags(). + BoolVar(&cmd.DryRun, "dry-run", false, "Show which version would be downloaded without actually updating") + return selfUpdateCmd } diff --git a/cmd/self_update_test.go b/cmd/self_update_test.go new file mode 100644 index 000000000..093bef243 --- /dev/null +++ b/cmd/self_update_test.go @@ -0,0 +1,36 @@ +package cmd + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNewSelfUpdateCmd_CommandName(t *testing.T) { + cmd := NewSelfUpdateCmd() + assert.Equal(t, "self-update", cmd.Use) +} + +func TestNewSelfUpdateCmd_HasVersionFlag(t *testing.T) { + cmd := NewSelfUpdateCmd() + f := cmd.Flags().Lookup("version") + require.NotNil(t, f, "--version flag must exist") + assert.Equal(t, "", f.DefValue) +} + +func TestNewSelfUpdateCmd_HasDryRunFlag(t *testing.T) { + cmd := NewSelfUpdateCmd() + f := cmd.Flags().Lookup("dry-run") + require.NotNil(t, f, "--dry-run flag must exist") + assert.Equal(t, "false", f.DefValue) +} + +func TestNewSelfUpdateCmd_AcceptsNoPositionalArgs(t *testing.T) { + cmd := NewSelfUpdateCmd() + err := cmd.Args(cmd, []string{"unexpected"}) + assert.Error(t, err, "should reject positional arguments") + + err = cmd.Args(cmd, []string{}) + assert.NoError(t, err, "should accept zero arguments") +} diff --git a/e2e/e2e_suite_test.go b/e2e/e2e_suite_test.go index a293207ef..e2c0763a5 100644 --- a/e2e/e2e_suite_test.go +++ b/e2e/e2e_suite_test.go @@ -22,11 +22,11 @@ import ( _ "github.com/devsy-org/devsy/e2e/tests/machineprovider" _ "github.com/devsy-org/devsy/e2e/tests/provider" _ "github.com/devsy-org/devsy/e2e/tests/readconfiguration" + _ "github.com/devsy-org/devsy/e2e/tests/selfupdate" _ "github.com/devsy-org/devsy/e2e/tests/ssh" _ "github.com/devsy-org/devsy/e2e/tests/tunnel" _ "github.com/devsy-org/devsy/e2e/tests/up" _ "github.com/devsy-org/devsy/e2e/tests/up-features" - _ "github.com/devsy-org/devsy/e2e/tests/upgrade" "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" ) diff --git a/e2e/tests/selfupdate/selfupdate.go b/e2e/tests/selfupdate/selfupdate.go new file mode 100644 index 000000000..d69ee6445 --- /dev/null +++ b/e2e/tests/selfupdate/selfupdate.go @@ -0,0 +1,48 @@ +package selfupdate + +import ( + "context" + "os" + "runtime" + "strings" + + "github.com/devsy-org/devsy/e2e/framework" + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" +) + +var _ = ginkgo.Describe( + "testing self-update command", ginkgo.Label("selfupdate"), ginkgo.Ordered, func() { + ginkgo.It( + "should detect correct binary for current OS and architecture using dry-run", + ginkgo.SpecTimeout(framework.TimeoutShort()), + func(ctx context.Context) { + initialDir, err := os.Getwd() + framework.ExpectNoError(err, "getting current working directory should not error") + + f := framework.NewDefaultFramework(initialDir + "/bin") + output, err := f.ExecCommandOutput(ctx, []string{"self-update", "--dry-run"}) + framework.ExpectNoError(err, "self-update --dry-run should not error") + + ginkgo.By("Parsing dry-run key=value output") + lines := strings.Split(strings.TrimSpace(output), "\n") + values := make(map[string]string) + for _, line := range lines { + parts := strings.SplitN(line, "=", 2) + if len(parts) == 2 { + values[parts[0]] = parts[1] + } + } + + expectedOS := runtime.GOOS + framework.ExpectEqual(values["os"], expectedOS, "OS should match runtime") + + expectedArch := runtime.GOARCH + framework.ExpectEqual(values["arch"], expectedArch, "Arch should match runtime") + + expectedAssetPattern := "devsy-" + expectedOS + "-" + expectedArch + gomega.Expect(values["asset_name"]). + To(gomega.ContainSubstring(expectedAssetPattern), "Asset name should contain OS and Arch") + }, + ) + }) diff --git a/e2e/tests/upgrade/upgrade.go b/e2e/tests/upgrade/upgrade.go deleted file mode 100644 index 2d2c0d3a5..000000000 --- a/e2e/tests/upgrade/upgrade.go +++ /dev/null @@ -1,47 +0,0 @@ -package upgrade - -import ( - "context" - "os" - "runtime" - "strings" - - "github.com/devsy-org/devsy/e2e/framework" - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" -) - -var _ = ginkgo.Describe("testing upgrade command", ginkgo.Label("upgrade"), ginkgo.Ordered, func() { - ginkgo.It( - "should detect correct binary for current OS and architecture using dry-run", - ginkgo.SpecTimeout(framework.TimeoutShort()), - func(ctx context.Context) { - initialDir, err := os.Getwd() - framework.ExpectNoError(err, "getting current working directory should not error") - - f := framework.NewDefaultFramework(initialDir + "/bin") - output, err := f.ExecCommandOutput(ctx, []string{"upgrade", "--dry-run"}) - framework.ExpectNoError(err, "upgrade --dry-run should not error") - - ginkgo.By("Parsing dry-run key=value output") - lines := strings.Split(strings.TrimSpace(output), "\n") - values := make(map[string]string) - for _, line := range lines { - parts := strings.SplitN(line, "=", 2) - if len(parts) == 2 { - values[parts[0]] = parts[1] - } - } - - expectedOS := runtime.GOOS - framework.ExpectEqual(values["os"], expectedOS, "OS should match runtime") - - expectedArch := runtime.GOARCH - framework.ExpectEqual(values["arch"], expectedArch, "Arch should match runtime") - - expectedAssetPattern := "devsy-" + expectedOS + "-" + expectedArch - gomega.Expect(values["asset_name"]). - To(gomega.ContainSubstring(expectedAssetPattern), "Asset name should contain OS and Arch") - }, - ) -})