From f1ccd8ae7caced9d7b95e59ebc0349bbbc59f538 Mon Sep 17 00:00:00 2001 From: OliverTrautvetter <66372584+OliverTrautvetter@users.noreply.github.com> Date: Fri, 10 Oct 2025 12:24:59 +0200 Subject: [PATCH 1/2] feat: Refactor OMS updater to download latest release from github --- cli/cmd/mocks.go | 58 +++++++++++++-------- cli/cmd/update_oms.go | 86 ++++++++----------------------- cli/cmd/update_oms_test.go | 100 ++++++++++++------------------------- go.mod | 10 +++- go.sum | 35 +++++++++++++ internal/util/mocks.go | 3 +- 6 files changed, 134 insertions(+), 158 deletions(-) diff --git a/cli/cmd/mocks.go b/cli/cmd/mocks.go index ea7610c5..a1ac35ea 100644 --- a/cli/cmd/mocks.go +++ b/cli/cmd/mocks.go @@ -5,8 +5,8 @@ package cmd import ( + "github.com/blang/semver" mock "github.com/stretchr/testify/mock" - "io" ) // NewMockOMSUpdater creates a new instance of MockOMSUpdater. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. @@ -36,47 +36,63 @@ func (_m *MockOMSUpdater) EXPECT() *MockOMSUpdater_Expecter { return &MockOMSUpdater_Expecter{mock: &_m.Mock} } -// Apply provides a mock function for the type MockOMSUpdater -func (_mock *MockOMSUpdater) Apply(update io.Reader) error { - ret := _mock.Called(update) +// Update provides a mock function for the type MockOMSUpdater +func (_mock *MockOMSUpdater) Update(v semver.Version, repo string) (semver.Version, string, error) { + ret := _mock.Called(v, repo) if len(ret) == 0 { - panic("no return value specified for Apply") + panic("no return value specified for Update") } - var r0 error - if returnFunc, ok := ret.Get(0).(func(io.Reader) error); ok { - r0 = returnFunc(update) + var r0 semver.Version + var r1 string + var r2 error + if returnFunc, ok := ret.Get(0).(func(semver.Version, string) (semver.Version, string, error)); ok { + return returnFunc(v, repo) + } + if returnFunc, ok := ret.Get(0).(func(semver.Version, string) semver.Version); ok { + r0 = returnFunc(v, repo) + } else { + r0 = ret.Get(0).(semver.Version) + } + if returnFunc, ok := ret.Get(1).(func(semver.Version, string) string); ok { + r1 = returnFunc(v, repo) + } else { + r1 = ret.Get(1).(string) + } + if returnFunc, ok := ret.Get(2).(func(semver.Version, string) error); ok { + r2 = returnFunc(v, repo) } else { - r0 = ret.Error(0) + r2 = ret.Error(2) } - return r0 + return r0, r1, r2 } -// MockOMSUpdater_Apply_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Apply' -type MockOMSUpdater_Apply_Call struct { +// MockOMSUpdater_Update_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Update' +type MockOMSUpdater_Update_Call struct { *mock.Call } -// Apply is a helper method to define mock.On call -// - update -func (_e *MockOMSUpdater_Expecter) Apply(update interface{}) *MockOMSUpdater_Apply_Call { - return &MockOMSUpdater_Apply_Call{Call: _e.mock.On("Apply", update)} +// Update is a helper method to define mock.On call +// - v +// - repo +func (_e *MockOMSUpdater_Expecter) Update(v interface{}, repo interface{}) *MockOMSUpdater_Update_Call { + return &MockOMSUpdater_Update_Call{Call: _e.mock.On("Update", v, repo)} } -func (_c *MockOMSUpdater_Apply_Call) Run(run func(update io.Reader)) *MockOMSUpdater_Apply_Call { +func (_c *MockOMSUpdater_Update_Call) Run(run func(v semver.Version, repo string)) *MockOMSUpdater_Update_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(io.Reader)) + run(args[0].(semver.Version), args[1].(string)) }) return _c } -func (_c *MockOMSUpdater_Apply_Call) Return(err error) *MockOMSUpdater_Apply_Call { - _c.Call.Return(err) +func (_c *MockOMSUpdater_Update_Call) Return(version semver.Version, s string, err error) *MockOMSUpdater_Update_Call { + _c.Call.Return(version, s, err) return _c } -func (_c *MockOMSUpdater_Apply_Call) RunAndReturn(run func(update io.Reader) error) *MockOMSUpdater_Apply_Call { +func (_c *MockOMSUpdater_Update_Call) RunAndReturn(run func(v semver.Version, repo string) (semver.Version, string, error)) *MockOMSUpdater_Update_Call { _c.Call.Return(run) return _c } diff --git a/cli/cmd/update_oms.go b/cli/cmd/update_oms.go index 8d14b26a..362b7ca7 100644 --- a/cli/cmd/update_oms.go +++ b/cli/cmd/update_oms.go @@ -5,27 +5,25 @@ package cmd import ( "fmt" - "io" - "strings" "github.com/blang/semver" - "github.com/inconshreveable/go-update" + "github.com/rhysd/go-github-selfupdate/selfupdate" "github.com/spf13/cobra" - "golang.org/x/sync/errgroup" - "github.com/codesphere-cloud/oms/internal/portal" - "github.com/codesphere-cloud/oms/internal/util" "github.com/codesphere-cloud/oms/internal/version" ) -type OMSUpdater interface { - Apply(update io.Reader) error -} +const GitHubRepo = "codesphere-cloud/oms" + +type OMSUpdater func(v semver.Version, repo string) (semver.Version, string, error) -type OMSSelfUpdater struct{} +var OMSSelfUpdater OMSUpdater = func(v semver.Version, repo string) (semver.Version, string, error) { + latest, err := selfupdate.UpdateSelf(v, repo) + if err != nil { + return v, "", err + } -func (s *OMSSelfUpdater) Apply(r io.Reader) error { - return update.Apply(r, update.Options{}) + return latest.Version, latest.ReleaseNotes, nil } type UpdateOmsCmd struct { @@ -36,75 +34,33 @@ type UpdateOmsCmd struct { func AddOmsUpdateCmd(parentCmd *cobra.Command) { cmdState := &UpdateOmsCmd{ Version: &version.Build{}, - Updater: &OMSSelfUpdater{}, + Updater: OMSSelfUpdater, } omsCmd := &cobra.Command{ Use: "oms", Short: "Update the OMS CLI", - Long: `Updates the OMS CLI to the latest release from OMS Portal.`, + Long: `Updates the OMS CLI to the latest release from GitHub.`, RunE: func(_ *cobra.Command, args []string) error { - p := portal.NewPortalClient() - return cmdState.SelfUpdate(p) + return cmdState.SelfUpdate() }, } parentCmd.AddCommand(omsCmd) } - -func (c *UpdateOmsCmd) SelfUpdate(p portal.Portal) error { - currentVersion := semver.MustParse(c.Version.Version()) - - latest, err := p.GetBuild(portal.OmsProduct, "", "") +func (c *UpdateOmsCmd) SelfUpdate() error { + v := semver.MustParse(c.Version.Version()) + latestVersion, releaseNotes, err := c.Updater(v, GitHubRepo) if err != nil { - return fmt.Errorf("failed to query OMS Portal for latest version: %w", err) + return fmt.Errorf("update failed: %w", err) } - latestVersion := semver.MustParse(strings.TrimPrefix(latest.Version, "oms-v")) - fmt.Printf("current version: %v\n", currentVersion) - fmt.Printf("latest version: %v\n", latestVersion) - if latestVersion.Equals(currentVersion) { - fmt.Println("Current OMS CLI is already the latest version", c.Version.Version()) + if latestVersion.Equals(v) { + fmt.Println("Current OMS CLI is the latest version", c.Version.Version()) return nil } - // Need a build with a single artifact to download it - download, err := latest.GetBuildForDownload(fmt.Sprintf("%s_%s.tar.gz", c.Version.Os(), c.Version.Arch())) - if err != nil { - return fmt.Errorf("failed to find OMS CLI in package: %w", err) - } - - // Use a pipe to unzip the file while downloading without storing on the filesystem - reader, writer := io.Pipe() - defer func() { _ = reader.Close() }() - - eg := errgroup.Group{} - eg.Go(func() error { - defer func() { _ = writer.Close() }() - err = p.DownloadBuildArtifact(portal.OmsProduct, download, writer) - if err != nil { - return fmt.Errorf("failed to download latest OMS package: %w", err) - } - return nil - }) - - cliReader, err := util.StreamFileFromGzip(reader, "oms-cli") - if err != nil { - return fmt.Errorf("failed to extract binary from archive: %w", err) - } - - err = c.Updater.Apply(cliReader) - if err != nil { - return fmt.Errorf("failed to apply update: %w", err) - } - - _, _ = io.Copy(io.Discard, reader) - - // Wait for download to finish and handle any error from the go routine - err = eg.Wait() - if err != nil { - return err - } + fmt.Printf("Successfully updated from %s to %s\n", v.String(), latestVersion.String()) + fmt.Println("Release notes:\n", releaseNotes) - fmt.Println("Update finished successfully.") return nil } diff --git a/cli/cmd/update_oms_test.go b/cli/cmd/update_oms_test.go index 0a895b82..986fc2cd 100644 --- a/cli/cmd/update_oms_test.go +++ b/cli/cmd/update_oms_test.go @@ -4,98 +4,60 @@ package cmd_test import ( - "embed" - "io" + "github.com/blang/semver" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/stretchr/testify/mock" "github.com/codesphere-cloud/oms/cli/cmd" - "github.com/codesphere-cloud/oms/internal/portal" "github.com/codesphere-cloud/oms/internal/version" ) -// I didn't find a good way to do this in memory without just reversing the code under test. -// While this is not ideal, at least it doesn't read from file during the test run but during compilation. -// -//go:generate mkdir -p testdata -//go:generate sh -c "echo fake-cli > testdata/oms-cli" -//go:generate sh -c "cd testdata && tar cfz testcli.tar.gz oms-cli" -//go:embed testdata -var testdata embed.FS +type mockOMSUpdater struct{ mock.Mock } -var _ = Describe("Update", func() { +func (m *mockOMSUpdater) Update(v semver.Version, repo string) (semver.Version, string, error) { + args := m.Called(v, repo) + return args.Get(0).(semver.Version), args.String(1), args.Error(2) +} +var _ = Describe("Update", func() { var ( - mockPortal *portal.MockPortal - mockVersion *version.MockVersion - mockUpdater *cmd.MockOMSUpdater - latestBuild portal.Build - buildToDownload portal.Build - c cmd.UpdateOmsCmd + mockVersion *version.MockVersion + mockGit *mockOMSUpdater + c cmd.UpdateOmsCmd ) BeforeEach(func() { - mockPortal = portal.NewMockPortal(GinkgoT()) mockVersion = version.NewMockVersion(GinkgoT()) - mockUpdater = cmd.NewMockOMSUpdater(GinkgoT()) - - latestBuild = portal.Build{ - Version: "0.0.42", - Artifacts: []portal.Artifact{ - {Filename: "fakeos_fakearch.tar.gz"}, - {Filename: "fakeos2_fakearch2.tar.gz"}, - {Filename: "fakeos3_fakearch3.tar.gz"}, - }, - } - buildToDownload = portal.Build{ - Version: "0.0.42", - Artifacts: []portal.Artifact{ - {Filename: "fakeos_fakearch.tar.gz"}, - }, + mockGit = &mockOMSUpdater{} + // GitUpdate is a function type; forward calls to the testify mock. + gitFunc := func(v semver.Version, repo string) (semver.Version, string, error) { + return mockGit.Update(v, repo) } c = cmd.UpdateOmsCmd{ Version: mockVersion, - Updater: mockUpdater, + Updater: gitFunc, } }) - Describe("SelfUpdate", func() { - It("Extracts oms-cli from the downloaded archive", func() { - mockVersion.EXPECT().Arch().Return("fakearch") - mockVersion.EXPECT().Version().Return("0.0.0") - mockVersion.EXPECT().Os().Return("fakeos") - mockPortal.EXPECT().GetBuild(portal.OmsProduct, "", "").Return(latestBuild, nil) - mockPortal.EXPECT().DownloadBuildArtifact(portal.OmsProduct, buildToDownload, mock.Anything).RunAndReturn( - func(product portal.Product, build portal.Build, file io.Writer) error { - embeddedFile, err := testdata.Open("testdata/testcli.tar.gz") - if err != nil { - Expect(err).NotTo(HaveOccurred()) - } - defer func() { _ = embeddedFile.Close() }() + It("Detects when current version is latest version", func() { + v := "0.0.42" + mockVersion.EXPECT().Version().Return(v) - if _, err := io.Copy(file, embeddedFile); err != nil { - Expect(err).NotTo(HaveOccurred()) - } - return nil - }) - mockUpdater.EXPECT().Apply(mock.Anything).RunAndReturn(func(update io.Reader) error { - output, err := io.ReadAll(update) - Expect(err).NotTo(HaveOccurred()) - // file content written in go:generate - Expect(string(output)).To(Equal("fake-cli\n")) - return nil - }) - err := c.SelfUpdate(mockPortal) - Expect(err).NotTo(HaveOccurred()) - }) + mockGit.On("Update", semver.MustParse(v), cmd.GitHubRepo).Return(semver.MustParse(v), "", nil) + err := c.SelfUpdate() + Expect(err).NotTo(HaveOccurred()) + mockGit.AssertExpectations(GinkgoT()) + }) - It("Detects when current version is latest version", func() { - mockVersion.EXPECT().Version().Return(latestBuild.Version) - mockPortal.EXPECT().GetBuild(portal.OmsProduct, "", "").Return(latestBuild, nil) - err := c.SelfUpdate(mockPortal) - Expect(err).NotTo(HaveOccurred()) - }) + It("Updates when a newer version exists", func() { + current := "0.0.0" + latest := "0.0.42" + mockVersion.EXPECT().Version().Return(current) + mockGit.On("Update", semver.MustParse(current), cmd.GitHubRepo).Return(semver.MustParse(latest), "notes", nil) + err := c.SelfUpdate() + Expect(err).NotTo(HaveOccurred()) + mockGit.AssertExpectations(GinkgoT()) }) }) diff --git a/go.mod b/go.mod index a1628b4a..de050680 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.24.2 require ( github.com/blang/semver v3.5.1+incompatible - github.com/codesphere-cloud/cs-go v0.11.1 + github.com/codesphere-cloud/cs-go v0.6.1 github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf github.com/jedib0t/go-pretty/v6 v6.6.8 github.com/onsi/ginkgo/v2 v2.25.3 @@ -22,7 +22,10 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/golang/protobuf v1.2.0 // indirect github.com/google/go-cmp v0.7.0 // indirect + github.com/google/go-github v17.0.0+incompatible // indirect + github.com/google/go-querystring v1.0.0 // indirect github.com/google/pprof v0.0.0-20250923004556-9e5a51aed1e8 // indirect github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -30,17 +33,22 @@ require ( github.com/kr/text v0.2.0 // indirect github.com/mattn/go-runewidth v0.0.19 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rhysd/go-github-selfupdate v1.2.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/stretchr/objx v0.5.2 // indirect + github.com/tcnksm/go-gitconfig v0.1.2 // indirect + github.com/ulikunitz/xz v0.5.5 // indirect go.uber.org/automaxprocs v1.6.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/net v0.44.0 // indirect + golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/text v0.29.0 // indirect golang.org/x/tools v0.37.0 // indirect + google.golang.org/appengine v1.3.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 1996bbbe..fb8cea0a 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY= github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= +github.com/codesphere-cloud/cs-go v0.6.1 h1:foFlr6zZT6Sy3T4epSsyZwhAFU6H1OILtX6fiAlwkkg= +github.com/codesphere-cloud/cs-go v0.6.1/go.mod h1:g0v6uObonstXKSHGsko12N3aurN8ODXuicJlSAioYEs= github.com/codesphere-cloud/cs-go v0.10.1 h1:QhiBKYkBlAeephPKfl0AY18MLGNdcY2uAjvncu7ezq8= github.com/codesphere-cloud/cs-go v0.10.1/go.mod h1:lJENk49WB9ZIGeZdZdzERYlCxlpL1FNcN8oj/NjgUfw= github.com/codesphere-cloud/cs-go v0.11.1 h1:+1BzvrAZm/2ntffBDdGs7tkYEajiaGnS9az9HZISMBA= @@ -19,16 +21,24 @@ github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6N github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/pprof v0.0.0-20250923004556-9e5a51aed1e8 h1:ZI8gCoCjGzPsum4L21jHdQs8shFBIQih1TM9Rd/c+EQ= github.com/google/pprof v0.0.0-20250923004556-9e5a51aed1e8/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b h1:ogbOPx86mIhFy764gGkqnkFC8m5PJA7sPzlk9ppLVQA= github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf h1:WfD7VjIE6z8dIvMsI4/s+1qr5EL+zoIGev1BQj1eoJ8= @@ -39,6 +49,7 @@ github.com/jedib0t/go-pretty/v6 v6.6.7 h1:m+LbHpm0aIAPLzLbMfn8dc3Ht8MW7lsSO4MPIt github.com/jedib0t/go-pretty/v6 v6.6.7/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU= github.com/jedib0t/go-pretty/v6 v6.6.8 h1:JnnzQeRz2bACBobIaa/r+nqjvws4yEhcmaZ4n1QzsEc= github.com/jedib0t/go-pretty/v6 v6.6.8/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -50,10 +61,12 @@ github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6T github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= github.com/onsi/ginkgo/v2 v2.25.3 h1:Ty8+Yi/ayDAGtk4XxmmfUy4GabvM+MegeB4cDLRi6nw= github.com/onsi/ginkgo/v2 v2.25.3/go.mod h1:43uiyQC4Ed2tkOzLsEYm7hnrb7UJTWHYNsuy3bG/snE= +github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= @@ -63,6 +76,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= +github.com/rhysd/go-github-selfupdate v1.2.0 h1:3jivcC23qxbL5xwnf6S6oC9d/0IIjD5zQH1Kk5+y95M= +github.com/rhysd/go-github-selfupdate v1.2.0/go.mod h1:BZHw2H9JIH7wdyda/btlRRD+sgudn1RO1NbnvY27/Ng= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -88,23 +103,37 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tcnksm/go-gitconfig v0.1.2 h1:iiDhRitByXAEyjgBqsKi9QU4o2TNtv9kPP3RgPgXBPw= +github.com/tcnksm/go-gitconfig v0.1.2/go.mod h1:/8EhP4H7oJZdIPyT+/UIsG87kTzrzM4UsLGSItWYCpE= +github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= +github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288 h1:JIqe8uIcRBHXDQVvZtHwp80ai3Lw3IJAeJEs55Dc1W0= +golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= @@ -113,11 +142,17 @@ golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= +google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk= +google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/util/mocks.go b/internal/util/mocks.go index c9fcabf6..23487339 100644 --- a/internal/util/mocks.go +++ b/internal/util/mocks.go @@ -5,10 +5,9 @@ package util import ( - "os" - "github.com/jedib0t/go-pretty/v6/table" mock "github.com/stretchr/testify/mock" + "os" ) // NewMockFileIO creates a new instance of MockFileIO. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. From 2d01c41d214e4398aba763a137d32134e7e8ed3b Mon Sep 17 00:00:00 2001 From: OliverTrautvetter <66372584+OliverTrautvetter@users.noreply.github.com> Date: Tue, 14 Oct 2025 11:56:52 +0200 Subject: [PATCH 2/2] fix: Replace fmt.Printf with log.Printf, simplify oms updater --- cli/cmd/update.go | 4 ++-- cli/cmd/update_api_key.go | 3 ++- cli/cmd/update_oms.go | 19 ++++++++++++------- cli/cmd/update_oms_test.go | 6 +----- internal/portal/http.go | 2 +- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/cli/cmd/update.go b/cli/cmd/update.go index 7ab6500b..e41a8244 100644 --- a/cli/cmd/update.go +++ b/cli/cmd/update.go @@ -4,7 +4,7 @@ package cmd import ( - "fmt" + "log" "github.com/spf13/cobra" ) @@ -14,7 +14,7 @@ type UpdateCmd struct { } func (c *UpdateCmd) RunE(_ *cobra.Command, args []string) error { - fmt.Printf("running %s", c.cmd.Use) + log.Printf("running %s", c.cmd.Use) return nil } diff --git a/cli/cmd/update_api_key.go b/cli/cmd/update_api_key.go index dc2be180..6c96151e 100644 --- a/cli/cmd/update_api_key.go +++ b/cli/cmd/update_api_key.go @@ -5,6 +5,7 @@ package cmd import ( "fmt" + "log" "time" "github.com/codesphere-cloud/oms/internal/portal" @@ -58,6 +59,6 @@ func (c *UpdateAPIKeyCmd) UpdateAPIKey(p portal.Portal) error { return fmt.Errorf("failed to update API key: %w", err) } - fmt.Printf("Successfully updated API key '%s' with new expiration date %s.\n", c.Opts.APIKeyID, expiresAt.Format(time.RFC1123)) + log.Printf("Successfully updated API key '%s' with new expiration date %s.\n", c.Opts.APIKeyID, expiresAt.Format(time.RFC1123)) return nil } diff --git a/cli/cmd/update_oms.go b/cli/cmd/update_oms.go index 362b7ca7..abf6ad01 100644 --- a/cli/cmd/update_oms.go +++ b/cli/cmd/update_oms.go @@ -5,6 +5,7 @@ package cmd import ( "fmt" + "log" "github.com/blang/semver" "github.com/rhysd/go-github-selfupdate/selfupdate" @@ -15,9 +16,13 @@ import ( const GitHubRepo = "codesphere-cloud/oms" -type OMSUpdater func(v semver.Version, repo string) (semver.Version, string, error) +type OMSUpdater interface { + Update(v semver.Version, repo string) (semver.Version, string, error) +} + +type OMSSelfUpdater struct{} -var OMSSelfUpdater OMSUpdater = func(v semver.Version, repo string) (semver.Version, string, error) { +func (s *OMSSelfUpdater) Update(v semver.Version, repo string) (semver.Version, string, error) { latest, err := selfupdate.UpdateSelf(v, repo) if err != nil { return v, "", err @@ -34,7 +39,7 @@ type UpdateOmsCmd struct { func AddOmsUpdateCmd(parentCmd *cobra.Command) { cmdState := &UpdateOmsCmd{ Version: &version.Build{}, - Updater: OMSSelfUpdater, + Updater: &OMSSelfUpdater{}, } omsCmd := &cobra.Command{ @@ -49,18 +54,18 @@ func AddOmsUpdateCmd(parentCmd *cobra.Command) { } func (c *UpdateOmsCmd) SelfUpdate() error { v := semver.MustParse(c.Version.Version()) - latestVersion, releaseNotes, err := c.Updater(v, GitHubRepo) + latestVersion, releaseNotes, err := c.Updater.Update(v, GitHubRepo) if err != nil { return fmt.Errorf("update failed: %w", err) } if latestVersion.Equals(v) { - fmt.Println("Current OMS CLI is the latest version", c.Version.Version()) + log.Println("Current OMS CLI is the latest version", c.Version.Version()) return nil } - fmt.Printf("Successfully updated from %s to %s\n", v.String(), latestVersion.String()) - fmt.Println("Release notes:\n", releaseNotes) + log.Printf("Successfully updated from %s to %s\n", v.String(), latestVersion.String()) + log.Println("Release notes:\n", releaseNotes) return nil } diff --git a/cli/cmd/update_oms_test.go b/cli/cmd/update_oms_test.go index 986fc2cd..524f6bc1 100644 --- a/cli/cmd/update_oms_test.go +++ b/cli/cmd/update_oms_test.go @@ -31,13 +31,9 @@ var _ = Describe("Update", func() { BeforeEach(func() { mockVersion = version.NewMockVersion(GinkgoT()) mockGit = &mockOMSUpdater{} - // GitUpdate is a function type; forward calls to the testify mock. - gitFunc := func(v semver.Version, repo string) (semver.Version, string, error) { - return mockGit.Update(v, repo) - } c = cmd.UpdateOmsCmd{ Version: mockVersion, - Updater: gitFunc, + Updater: mockGit, } }) diff --git a/internal/portal/http.go b/internal/portal/http.go index 8f1a383f..e5affbcd 100644 --- a/internal/portal/http.go +++ b/internal/portal/http.go @@ -279,7 +279,7 @@ func (c *PortalClient) UpdateAPIKey(key string, expiresAt time.Time) error { } defer func() { _ = resp.Body.Close() }() - fmt.Println("API key updated successfully") + log.Println("API key updated successfully") return nil }