From 15752506331b1847e45b685f383a9c96abddc0ec Mon Sep 17 00:00:00 2001 From: Simon Herrmann Date: Mon, 13 Oct 2025 13:54:23 +0200 Subject: [PATCH 1/9] feat: add install codesphere command and tests --- cli/cmd/install.go | 3 +- cli/cmd/install_codesphere.go | 105 +++++++++++++- cli/cmd/install_codesphere_test.go | 224 +++++++++++++++++++++++++++++ cli/cmd/root.go | 6 +- internal/installer/package.go | 8 +- internal/util/filewriter.go | 5 + internal/util/mocks.go | 59 +++++++- 7 files changed, 400 insertions(+), 10 deletions(-) create mode 100644 cli/cmd/install_codesphere_test.go diff --git a/cli/cmd/install.go b/cli/cmd/install.go index fed8af0d..7402281d 100644 --- a/cli/cmd/install.go +++ b/cli/cmd/install.go @@ -13,7 +13,7 @@ type InstallCmd struct { cmd *cobra.Command } -func AddInstallCmd(rootCmd *cobra.Command) { +func AddInstallCmd(rootCmd *cobra.Command, opts *GlobalOptions) { install := InstallCmd{ cmd: &cobra.Command{ Use: "install", @@ -22,4 +22,5 @@ func AddInstallCmd(rootCmd *cobra.Command) { }, } rootCmd.AddCommand(install.cmd) + AddInstallCodesphereCmd(install.cmd, opts) } diff --git a/cli/cmd/install_codesphere.go b/cli/cmd/install_codesphere.go index c983c1b4..fb506593 100644 --- a/cli/cmd/install_codesphere.go +++ b/cli/cmd/install_codesphere.go @@ -4,22 +4,123 @@ package cmd import ( + "errors" + "fmt" + "log" + "os" + "os/exec" + "runtime" + "slices" + "github.com/codesphere-cloud/cs-go/pkg/io" + "github.com/codesphere-cloud/oms/internal/env" + "github.com/codesphere-cloud/oms/internal/installer" "github.com/spf13/cobra" ) // InstallCodesphereCmd represents the codesphere command type InstallCodesphereCmd struct { - cmd *cobra.Command + cmd *cobra.Command + Opts *InstallCodesphereOpts + Env env.Env +} + +type InstallCodesphereOpts struct { + *GlobalOptions + Package string + Force bool } -func AddInstallCodesphereCmd(install *cobra.Command) { +func (c *InstallCodesphereCmd) RunE(_ *cobra.Command, args []string) error { + if c.Opts.Package == "" { + return errors.New("required option package not set") + } + + workdir := c.Env.GetOmsWorkdir() + p := installer.NewPackage(workdir, c.Opts.Package) + + err := c.ExtractAndInstall(p, args) + if err != nil { + return fmt.Errorf("failed to extend baseimage: %w", err) + } + + return nil +} + +func AddInstallCodesphereCmd(install *cobra.Command, opts *GlobalOptions) { codesphere := InstallCodesphereCmd{ cmd: &cobra.Command{ Use: "codesphere", Short: "Coming soon: Install a Codesphere instance", Long: io.Long(`Coming soon: Install a Codesphere instance`), }, + Opts: &InstallCodesphereOpts{GlobalOptions: opts}, + Env: env.NewEnv(), } + codesphere.cmd.Flags().StringVarP(&codesphere.Opts.Package, "package", "p", "", "Package file (e.g. codesphere-v1.2.3-installer.tar.gz) to load base image from") + codesphere.cmd.Flags().BoolVarP(&codesphere.Opts.Force, "force", "f", false, "Enforce package extraction") install.AddCommand(codesphere.cmd) + codesphere.cmd.RunE = codesphere.RunE +} + +func (c *InstallCodesphereCmd) ExtractAndInstall(p *installer.Package, args []string) error { + if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { + return fmt.Errorf("codesphere installation is only supported on Linux amd64. Current platform: %s/%s", runtime.GOOS, runtime.GOARCH) + } + + err := p.Extract(c.Opts.Force) + if err != nil { + return fmt.Errorf("failed to extract package to workdir: %w", err) + } + + foundFiles, err := c.ListPackageContents(p) + if err != nil { + return fmt.Errorf("failed to list available files: %w", err) + } + + if !slices.Contains(foundFiles, "private-cloud-installer.js") { + return fmt.Errorf("private-cloud-installer.js not found in package") + } + if !slices.Contains(foundFiles, "node") { + return fmt.Errorf("node executable not found in package") + } + + nodeDir := "./" + p.GetWorkDir() + "/node" + err = os.Chmod(nodeDir, 0755) + if err != nil { + return fmt.Errorf("failed to make node executable: %w", err) + } + + log.Printf("Using Node.js executable: %s", nodeDir) + log.Println("Starting private cloud installer script...") + out, err := exec.Command(nodeDir, args...).Output() + if err != nil { + return fmt.Errorf("failed to run installer script: %w", err) + } + fmt.Println(string(out)) + fmt.Println("Private cloud installer script finished.") + + return nil +} + +func (c *InstallCodesphereCmd) ListPackageContents(p *installer.Package) ([]string, error) { + packageDir := p.GetWorkDir() + if !p.FileIO.Exists(packageDir) { + return nil, fmt.Errorf("work dir not found: %s", packageDir) + } + + entries, err := p.FileIO.ReadDir(packageDir) + if err != nil { + return nil, fmt.Errorf("failed to read directory contents: %w", err) + } + + log.Printf("Listing contents of %s", packageDir) + var foundFiles []string + for _, entry := range entries { + filename := entry.Name() + log.Printf("- %s", filename) + foundFiles = append(foundFiles, filename) + } + + return foundFiles, nil } diff --git a/cli/cmd/install_codesphere_test.go b/cli/cmd/install_codesphere_test.go new file mode 100644 index 00000000..3503ea32 --- /dev/null +++ b/cli/cmd/install_codesphere_test.go @@ -0,0 +1,224 @@ +// Copyright (c) Codesphere Inc. +// SPDX-License-Identifier: Apache-2.0 + +package cmd_test + +import ( + "fmt" + "os" + "runtime" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/spf13/cobra" + + "github.com/codesphere-cloud/oms/cli/cmd" + "github.com/codesphere-cloud/oms/internal/env" + "github.com/codesphere-cloud/oms/internal/installer" + "github.com/codesphere-cloud/oms/internal/util" +) + +var _ = Describe("InstallCodesphereCmd", func() { + var ( + c cmd.InstallCodesphereCmd + opts *cmd.InstallCodesphereOpts + globalOpts cmd.GlobalOptions + mockEnv *env.MockEnv + ) + + BeforeEach(func() { + mockEnv = env.NewMockEnv(GinkgoT()) + globalOpts = cmd.GlobalOptions{} + opts = &cmd.InstallCodesphereOpts{ + GlobalOptions: &globalOpts, + Package: "codesphere-v1.66.0-installer.tar.gz", + Force: false, + } + c = cmd.InstallCodesphereCmd{ + Opts: opts, + Env: mockEnv, + } + }) + + AfterEach(func() { + mockEnv.AssertExpectations(GinkgoT()) + }) + + Context("RunE method", func() { + It("fails when package is empty", func() { + c.Opts.Package = "" + err := c.RunE(nil, []string{}) + Expect(err).To(MatchError("required option package not set")) + }) + + It("calls GetOmsWorkdir and fails on non-linux platform", func() { + c.Opts.Package = "test-package.tar.gz" + mockEnv.EXPECT().GetOmsWorkdir().Return("/test/workdir") + + err := c.RunE(nil, []string{}) + + // On non-Linux platforms, should fail with platform error + if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("codesphere installation is only supported on Linux amd64")) + } + }) + }) + + Context("ExtractAndInstall method", func() { + It("fails on non-Linux amd64 platforms", func() { + pkg := &installer.Package{ + OmsWorkdir: "/test/workdir", + Filename: "test-package.tar.gz", + FileIO: &util.FilesystemWriter{}, + } + + err := c.ExtractAndInstall(pkg, []string{}) + + // Should always fail on non-Linux amd64 platforms + if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("codesphere installation is only supported on Linux amd64")) + Expect(err.Error()).To(ContainSubstring(fmt.Sprintf("Current platform: %s/%s", runtime.GOOS, runtime.GOARCH))) + } + }) + + Context("when on Linux amd64 (mocked)", func() { + BeforeEach(func() { + // Skip these tests if not on Linux amd64 since we can't easily mock runtime.GOOS + if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { + Skip("Skipping Linux-specific tests on non-Linux platform") + } + }) + + It("fails when package extraction fails", func() { + pkg := &installer.Package{ + OmsWorkdir: "/test/workdir", + Filename: "non-existent-package.tar.gz", + FileIO: &util.FilesystemWriter{}, + } + + err := c.ExtractAndInstall(pkg, []string{}) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("failed to extract package to workdir")) + }) + }) + }) + + Context("listPackageContents method", func() { + It("fails when work directory doesn't exist", func() { + mockFileIO := util.NewMockFileIO(GinkgoT()) + pkg := &installer.Package{ + OmsWorkdir: "/test/workdir", + Filename: "test-package.tar.gz", + FileIO: mockFileIO, + } + + mockFileIO.EXPECT().Exists("/test/workdir/test-package").Return(false) + + filenames, err := c.ListPackageContents(pkg) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("work dir not found")) + Expect(filenames).To(BeNil()) + mockFileIO.AssertExpectations(GinkgoT()) + }) + + It("fails when ReadDir fails", func() { + mockFileIO := util.NewMockFileIO(GinkgoT()) + pkg := &installer.Package{ + OmsWorkdir: "/test/workdir", + Filename: "test-package.tar.gz", + FileIO: mockFileIO, + } + + mockFileIO.EXPECT().Exists("/test/workdir/test-package").Return(true) + mockFileIO.EXPECT().ReadDir("/test/workdir/test-package").Return(nil, os.ErrPermission) + + filenames, err := c.ListPackageContents(pkg) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("failed to read directory contents")) + Expect(filenames).To(BeNil()) + mockFileIO.AssertExpectations(GinkgoT()) + }) + + It("successfully lists package contents", func() { + mockFileIO := util.NewMockFileIO(GinkgoT()) + pkg := &installer.Package{ + OmsWorkdir: "/test/workdir", + Filename: "test-package.tar.gz", + FileIO: mockFileIO, + } + + // Create mock directory entries + mockEntries := []os.DirEntry{ + &MockDirEntry{name: "deps.tar.gz", isDir: false}, + &MockDirEntry{name: "node", isDir: false}, + &MockDirEntry{name: "private-cloud-installer.js", isDir: false}, + &MockDirEntry{name: "kubectl", isDir: false}, + } + + mockFileIO.EXPECT().Exists("/test/workdir/test-package").Return(true) + mockFileIO.EXPECT().ReadDir("/test/workdir/test-package").Return(mockEntries, nil) + + filenames, err := c.ListPackageContents(pkg) + Expect(err).To(BeNil()) + Expect(filenames).To(HaveLen(4)) + Expect(filenames).To(ContainElement("deps.tar.gz")) + Expect(filenames).To(ContainElement("node")) + Expect(filenames).To(ContainElement("private-cloud-installer.js")) + Expect(filenames).To(ContainElement("kubectl")) + mockFileIO.AssertExpectations(GinkgoT()) + }) + }) +}) + +var _ = Describe("AddInstallCodesphereCmd", func() { + var ( + parentCmd *cobra.Command + globalOpts *cmd.GlobalOptions + ) + + BeforeEach(func() { + parentCmd = &cobra.Command{Use: "install"} + globalOpts = &cmd.GlobalOptions{} + }) + + It("adds the codesphere command with correct properties and flags", func() { + cmd.AddInstallCodesphereCmd(parentCmd, globalOpts) + + var codesphereCmd *cobra.Command + for _, c := range parentCmd.Commands() { + if c.Use == "codesphere" { + codesphereCmd = c + break + } + } + + Expect(codesphereCmd).NotTo(BeNil()) + Expect(codesphereCmd.Use).To(Equal("codesphere")) + Expect(codesphereCmd.Short).To(Equal("Coming soon: Install a Codesphere instance")) + Expect(codesphereCmd.Long).To(ContainSubstring("Coming soon: Install a Codesphere instance")) + Expect(codesphereCmd.RunE).NotTo(BeNil()) + + // Check flags + packageFlag := codesphereCmd.Flags().Lookup("package") + Expect(packageFlag).NotTo(BeNil()) + Expect(packageFlag.Shorthand).To(Equal("p")) + + forceFlag := codesphereCmd.Flags().Lookup("force") + Expect(forceFlag).NotTo(BeNil()) + Expect(forceFlag.Shorthand).To(Equal("f")) + Expect(forceFlag.DefValue).To(Equal("false")) + }) +}) + +// MockDirEntry implements os.DirEntry for testing +type MockDirEntry struct { + name string + isDir bool +} + +func (m *MockDirEntry) Name() string { return m.name } +func (m *MockDirEntry) IsDir() bool { return m.isDir } +func (m *MockDirEntry) Type() os.FileMode { return 0 } +func (m *MockDirEntry) Info() (os.FileInfo, error) { return nil, nil } diff --git a/cli/cmd/root.go b/cli/cmd/root.go index 7159a5e6..1392f856 100644 --- a/cli/cmd/root.go +++ b/cli/cmd/root.go @@ -26,11 +26,15 @@ func GetRootCmd() *cobra.Command { This command can be used to run common tasks related to managing codesphere installations, like downloading new versions.`), } + // General commands AddVersionCmd(rootCmd) + AddBetaCmd(rootCmd, &opts) AddUpdateCmd(rootCmd, opts) + + // Package commands AddListCmd(rootCmd, opts) AddDownloadCmd(rootCmd, opts) - AddBetaCmd(rootCmd, &opts) + AddInstallCmd(rootCmd, &opts) // OMS API key management commands AddRegisterCmd(rootCmd, opts) diff --git a/internal/installer/package.go b/internal/installer/package.go index 4426077b..c2a849cb 100644 --- a/internal/installer/package.go +++ b/internal/installer/package.go @@ -30,7 +30,7 @@ func NewPackage(omsWorkdir, filename string) *Package { } func (p *Package) Extract(force bool) error { - workDir := p.getWorkDir() + workDir := p.GetWorkDir() err := os.MkdirAll(p.OmsWorkdir, 0755) if err != nil { return fmt.Errorf("failed to ensure workdir exists: %w", err) @@ -58,7 +58,7 @@ func (p *Package) ExtractDependency(file string, force bool) error { if err != nil { return fmt.Errorf("failed to extract package: %w", err) } - workDir := p.getWorkDir() + workDir := p.GetWorkDir() if p.FileIO.Exists(p.GetDependencyPath(file)) && !force { log.Println("skipping extraction, dependency already unpacked. Use force option to overwrite.") @@ -84,11 +84,11 @@ func (p *Package) alreadyExtracted(dir string) (bool, error) { return isDir, nil } -func (p *Package) getWorkDir() string { +func (p *Package) GetWorkDir() string { return path.Join(p.OmsWorkdir, strings.ReplaceAll(p.Filename, ".tar.gz", "")) } func (p *Package) GetDependencyPath(filename string) string { - workDir := p.getWorkDir() + workDir := p.GetWorkDir() return path.Join(workDir, depsDir, filename) } diff --git a/internal/util/filewriter.go b/internal/util/filewriter.go index 3f58a695..b686039d 100644 --- a/internal/util/filewriter.go +++ b/internal/util/filewriter.go @@ -14,6 +14,7 @@ type FileIO interface { Create(filename string) (*os.File, error) IsDirectory(filename string) (bool, error) Exists(filename string) bool + ReadDir(dirname string) ([]os.DirEntry, error) } type FilesystemWriter struct{} @@ -55,6 +56,10 @@ func (fs *FilesystemWriter) OpenFile(name string, flag int, perm os.FileMode) (* return os.OpenFile(name, flag, perm) } +func (fs *FilesystemWriter) ReadDir(dirname string) ([]os.DirEntry, error) { + return os.ReadDir(dirname) +} + type ClosableFile interface { Close() error } diff --git a/internal/util/mocks.go b/internal/util/mocks.go index c9fcabf6..ae55b5ba 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. @@ -353,6 +352,62 @@ func (_c *MockFileIO_OpenFile_Call) RunAndReturn(run func(name string, flag int, return _c } +// ReadDir provides a mock function for the type MockFileIO +func (_mock *MockFileIO) ReadDir(dirname string) ([]os.DirEntry, error) { + ret := _mock.Called(dirname) + + if len(ret) == 0 { + panic("no return value specified for ReadDir") + } + + var r0 []os.DirEntry + var r1 error + if returnFunc, ok := ret.Get(0).(func(string) ([]os.DirEntry, error)); ok { + return returnFunc(dirname) + } + if returnFunc, ok := ret.Get(0).(func(string) []os.DirEntry); ok { + r0 = returnFunc(dirname) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]os.DirEntry) + } + } + if returnFunc, ok := ret.Get(1).(func(string) error); ok { + r1 = returnFunc(dirname) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MockFileIO_ReadDir_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ReadDir' +type MockFileIO_ReadDir_Call struct { + *mock.Call +} + +// ReadDir is a helper method to define mock.On call +// - dirname +func (_e *MockFileIO_Expecter) ReadDir(dirname interface{}) *MockFileIO_ReadDir_Call { + return &MockFileIO_ReadDir_Call{Call: _e.mock.On("ReadDir", dirname)} +} + +func (_c *MockFileIO_ReadDir_Call) Run(run func(dirname string)) *MockFileIO_ReadDir_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *MockFileIO_ReadDir_Call) Return(vs []os.DirEntry, err error) *MockFileIO_ReadDir_Call { + _c.Call.Return(vs, err) + return _c +} + +func (_c *MockFileIO_ReadDir_Call) RunAndReturn(run func(dirname string) ([]os.DirEntry, error)) *MockFileIO_ReadDir_Call { + _c.Call.Return(run) + return _c +} + // NewMockClosableFile creates a new instance of MockClosableFile. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewMockClosableFile(t interface { From 617513ec708c09b8a6d65418552f0ddfe4d1baaa Mon Sep 17 00:00:00 2001 From: Simon Herrmann Date: Mon, 13 Oct 2025 14:07:20 +0200 Subject: [PATCH 2/9] fix: small fixes --- cli/cmd/install_codesphere.go | 5 +++-- internal/util/mocks.go | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cli/cmd/install_codesphere.go b/cli/cmd/install_codesphere.go index fb506593..fac7cdcc 100644 --- a/cli/cmd/install_codesphere.go +++ b/cli/cmd/install_codesphere.go @@ -41,7 +41,7 @@ func (c *InstallCodesphereCmd) RunE(_ *cobra.Command, args []string) error { err := c.ExtractAndInstall(p, args) if err != nil { - return fmt.Errorf("failed to extend baseimage: %w", err) + return fmt.Errorf("failed to extract and install package: %w", err) } return nil @@ -93,7 +93,8 @@ func (c *InstallCodesphereCmd) ExtractAndInstall(p *installer.Package, args []st log.Printf("Using Node.js executable: %s", nodeDir) log.Println("Starting private cloud installer script...") - out, err := exec.Command(nodeDir, args...).Output() + installerScript := "./" + p.GetWorkDir() + "/private-cloud-installer.js" + out, err := exec.Command(nodeDir, append([]string{installerScript}, args...)...).Output() if err != nil { return fmt.Errorf("failed to run installer script: %w", err) } diff --git a/internal/util/mocks.go b/internal/util/mocks.go index ae55b5ba..7caf98d9 100644 --- a/internal/util/mocks.go +++ b/internal/util/mocks.go @@ -5,9 +5,10 @@ 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 3e38c817c5a2f4f7cfab03ee5d01dbb7f6c14460 Mon Sep 17 00:00:00 2001 From: Simon Herrmann Date: Wed, 15 Oct 2025 13:01:38 +0200 Subject: [PATCH 3/9] update: small fixes, added flags for private cloud installer to command --- cli/cmd/install_codesphere.go | 60 +++++--- cli/cmd/install_codesphere_test.go | 220 +++++++++++++++++++++++++---- cli/cmd/update.go | 4 +- cli/cmd/update_api_key.go | 3 +- cli/cmd/update_oms.go | 9 +- internal/portal/http.go | 2 +- 6 files changed, 245 insertions(+), 53 deletions(-) diff --git a/cli/cmd/install_codesphere.go b/cli/cmd/install_codesphere.go index fac7cdcc..7cea003e 100644 --- a/cli/cmd/install_codesphere.go +++ b/cli/cmd/install_codesphere.go @@ -4,7 +4,6 @@ package cmd import ( - "errors" "fmt" "log" "os" @@ -15,6 +14,7 @@ import ( "github.com/codesphere-cloud/cs-go/pkg/io" "github.com/codesphere-cloud/oms/internal/env" "github.com/codesphere-cloud/oms/internal/installer" + "github.com/codesphere-cloud/oms/internal/util" "github.com/spf13/cobra" ) @@ -27,19 +27,18 @@ type InstallCodesphereCmd struct { type InstallCodesphereOpts struct { *GlobalOptions - Package string - Force bool + Package string + Force bool + Config string + PrivKey string + SkipStep string } func (c *InstallCodesphereCmd) RunE(_ *cobra.Command, args []string) error { - if c.Opts.Package == "" { - return errors.New("required option package not set") - } - workdir := c.Env.GetOmsWorkdir() p := installer.NewPackage(workdir, c.Opts.Package) - err := c.ExtractAndInstall(p, args) + err := c.ExtractAndInstall(p, runtime.GOOS, runtime.GOARCH) if err != nil { return fmt.Errorf("failed to extract and install package: %w", err) } @@ -57,15 +56,23 @@ func AddInstallCodesphereCmd(install *cobra.Command, opts *GlobalOptions) { Opts: &InstallCodesphereOpts{GlobalOptions: opts}, Env: env.NewEnv(), } - codesphere.cmd.Flags().StringVarP(&codesphere.Opts.Package, "package", "p", "", "Package file (e.g. codesphere-v1.2.3-installer.tar.gz) to load base image from") + codesphere.cmd.Flags().StringVarP(&codesphere.Opts.Package, "package", "p", "", "Package file (e.g. codesphere-v1.2.3-installer.tar.gz) to load binaries, installer etc. from") codesphere.cmd.Flags().BoolVarP(&codesphere.Opts.Force, "force", "f", false, "Enforce package extraction") + codesphere.cmd.Flags().StringVarP(&codesphere.Opts.Config, "config", "c", "", "Configuration file for the private cloud installer") + codesphere.cmd.Flags().StringVarP(&codesphere.Opts.PrivKey, "priv-key", "k", "", "Private key file for the installation") + codesphere.cmd.Flags().StringVarP(&codesphere.Opts.SkipStep, "skip-step", "s", "", "Skip specific installation steps") + + util.MarkFlagRequired(codesphere.cmd, "package") + util.MarkFlagRequired(codesphere.cmd, "config") + util.MarkFlagRequired(codesphere.cmd, "priv-key") + install.AddCommand(codesphere.cmd) codesphere.cmd.RunE = codesphere.RunE } -func (c *InstallCodesphereCmd) ExtractAndInstall(p *installer.Package, args []string) error { - if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { - return fmt.Errorf("codesphere installation is only supported on Linux amd64. Current platform: %s/%s", runtime.GOOS, runtime.GOARCH) +func (c *InstallCodesphereCmd) ExtractAndInstall(p *installer.Package, goos string, goarch string) error { + if goos != "linux" || goarch != "amd64" { + return fmt.Errorf("codesphere installation is only supported on Linux amd64. Current platform: %s/%s", goos, goarch) } err := p.Extract(c.Opts.Force) @@ -78,6 +85,9 @@ func (c *InstallCodesphereCmd) ExtractAndInstall(p *installer.Package, args []st return fmt.Errorf("failed to list available files: %w", err) } + if !slices.Contains(foundFiles, "deps.tar.gz") { + return fmt.Errorf("deps.tar.gz not found in package") + } if !slices.Contains(foundFiles, "private-cloud-installer.js") { return fmt.Errorf("private-cloud-installer.js not found in package") } @@ -85,21 +95,33 @@ func (c *InstallCodesphereCmd) ExtractAndInstall(p *installer.Package, args []st return fmt.Errorf("node executable not found in package") } - nodeDir := "./" + p.GetWorkDir() + "/node" - err = os.Chmod(nodeDir, 0755) + nodePath := "./" + p.GetWorkDir() + "/node" + err = os.Chmod(nodePath, 0755) if err != nil { return fmt.Errorf("failed to make node executable: %w", err) } - log.Printf("Using Node.js executable: %s", nodeDir) + log.Printf("Using Node.js executable: %s", nodePath) log.Println("Starting private cloud installer script...") - installerScript := "./" + p.GetWorkDir() + "/private-cloud-installer.js" - out, err := exec.Command(nodeDir, append([]string{installerScript}, args...)...).Output() + installerPath := "./" + p.GetWorkDir() + "/private-cloud-installer.js" + archivePath := "./" + p.GetWorkDir() + "/deps.tar.gz" + + // Build command + cmdArgs := []string{installerPath, "--archive", archivePath, "--config", c.Opts.Config, "--privKey", c.Opts.PrivKey} + if c.Opts.SkipStep != "" { + cmdArgs = append(cmdArgs, "--skipStep", c.Opts.SkipStep) + } + + cmd := exec.Command(nodePath, cmdArgs...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + + err = cmd.Run() if err != nil { return fmt.Errorf("failed to run installer script: %w", err) } - fmt.Println(string(out)) - fmt.Println("Private cloud installer script finished.") + log.Println("Private cloud installer script finished.") return nil } diff --git a/cli/cmd/install_codesphere_test.go b/cli/cmd/install_codesphere_test.go index 3503ea32..b0bf1d02 100644 --- a/cli/cmd/install_codesphere_test.go +++ b/cli/cmd/install_codesphere_test.go @@ -4,7 +4,6 @@ package cmd_test import ( - "fmt" "os" "runtime" @@ -45,52 +44,45 @@ var _ = Describe("InstallCodesphereCmd", func() { }) Context("RunE method", func() { - It("fails when package is empty", func() { - c.Opts.Package = "" - err := c.RunE(nil, []string{}) - Expect(err).To(MatchError("required option package not set")) - }) - It("calls GetOmsWorkdir and fails on non-linux platform", func() { c.Opts.Package = "test-package.tar.gz" mockEnv.EXPECT().GetOmsWorkdir().Return("/test/workdir") err := c.RunE(nil, []string{}) - // On non-Linux platforms, should fail with platform error + Expect(err).To(HaveOccurred()) if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { - Expect(err).To(HaveOccurred()) + // Should fail with platform error on non-Linux platform Expect(err.Error()).To(ContainSubstring("codesphere installation is only supported on Linux amd64")) + } else { + // On Linux amd64, it should fail on package extraction since the package doesn't exist + Expect(err.Error()).To(ContainSubstring("failed to extract package to workdir")) } }) }) Context("ExtractAndInstall method", func() { - It("fails on non-Linux amd64 platforms", func() { + It("fails on non-linux amd64 platforms", func() { pkg := &installer.Package{ OmsWorkdir: "/test/workdir", Filename: "test-package.tar.gz", FileIO: &util.FilesystemWriter{}, } - err := c.ExtractAndInstall(pkg, []string{}) + // Test with Windows platform + err := c.ExtractAndInstall(pkg, "windows", "amd64") + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("codesphere installation is only supported on Linux amd64")) + Expect(err.Error()).To(ContainSubstring("windows/amd64")) - // Should always fail on non-Linux amd64 platforms - if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("codesphere installation is only supported on Linux amd64")) - Expect(err.Error()).To(ContainSubstring(fmt.Sprintf("Current platform: %s/%s", runtime.GOOS, runtime.GOARCH))) - } + // Test with ARM64 architecture + err = c.ExtractAndInstall(pkg, "linux", "arm64") + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("codesphere installation is only supported on Linux amd64")) + Expect(err.Error()).To(ContainSubstring("linux/arm64")) }) - Context("when on Linux amd64 (mocked)", func() { - BeforeEach(func() { - // Skip these tests if not on Linux amd64 since we can't easily mock runtime.GOOS - if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { - Skip("Skipping Linux-specific tests on non-Linux platform") - } - }) - + Context("when on Linux amd64", func() { It("fails when package extraction fails", func() { pkg := &installer.Package{ OmsWorkdir: "/test/workdir", @@ -98,10 +90,174 @@ var _ = Describe("InstallCodesphereCmd", func() { FileIO: &util.FilesystemWriter{}, } - err := c.ExtractAndInstall(pkg, []string{}) + err := c.ExtractAndInstall(pkg, "linux", "amd64") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("failed to extract package to workdir")) }) + + It("fails when deps.tar.gz is missing from package", func() { + tempDir, err := os.MkdirTemp("", "oms-test-*") + Expect(err).To(BeNil()) + defer func() { + err := os.RemoveAll(tempDir) + Expect(err).To(BeNil()) + }() + + origWd, err := os.Getwd() + Expect(err).To(BeNil()) + err = os.Chdir(tempDir) + Expect(err).To(BeNil()) + defer func() { + err := os.Chdir(origWd) + Expect(err).To(BeNil()) + }() + + // Create package without deps.tar.gz + testPackageFile := "test-package.tar.gz" + packageFiles := map[string][]byte{ + "node": []byte("fake node binary"), + "private-cloud-installer.js": []byte("console.log('installer');"), + "kubectl": []byte("fake kubectl binary"), + // deps.tar.gz missing + } + err = createTestTarGz(testPackageFile, packageFiles) + Expect(err).To(BeNil()) + + c.Opts.Force = true + pkg := &installer.Package{ + OmsWorkdir: tempDir, + Filename: testPackageFile, + FileIO: &util.FilesystemWriter{}, + } + + err = c.ExtractAndInstall(pkg, "linux", "amd64") + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("deps.tar.gz not found in package")) + }) + + It("fails when private-cloud-installer.js is missing from package", func() { + tempDir, err := os.MkdirTemp("", "oms-test-*") + Expect(err).To(BeNil()) + defer func() { + err := os.RemoveAll(tempDir) + Expect(err).To(BeNil()) + }() + + origWd, err := os.Getwd() + Expect(err).To(BeNil()) + err = os.Chdir(tempDir) + Expect(err).To(BeNil()) + defer func() { + err := os.Chdir(origWd) + Expect(err).To(BeNil()) + }() + + // Create package without private-cloud-installer.js + testPackageFile := "test-package.tar.gz" + packageFiles := map[string][]byte{ + "deps.tar.gz": []byte("fake deps archive"), + "node": []byte("fake node binary"), + "kubectl": []byte("fake kubectl binary"), + // private-cloud-installer.js missing + } + err = createTestTarGz(testPackageFile, packageFiles) + Expect(err).To(BeNil()) + + c.Opts.Force = true + pkg := &installer.Package{ + OmsWorkdir: tempDir, + Filename: testPackageFile, + FileIO: &util.FilesystemWriter{}, + } + + err = c.ExtractAndInstall(pkg, "linux", "amd64") + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("private-cloud-installer.js not found in package")) + }) + + It("fails when node executable is missing from package", func() { + tempDir, err := os.MkdirTemp("", "oms-test-*") + Expect(err).To(BeNil()) + defer func() { + err := os.RemoveAll(tempDir) + Expect(err).To(BeNil()) + }() + + origWd, err := os.Getwd() + Expect(err).To(BeNil()) + err = os.Chdir(tempDir) + Expect(err).To(BeNil()) + defer func() { + err := os.Chdir(origWd) + Expect(err).To(BeNil()) + }() + + // Create package without node executable + testPackageFile := "test-package.tar.gz" + packageFiles := map[string][]byte{ + "deps.tar.gz": []byte("fake deps archive"), + "private-cloud-installer.js": []byte("console.log('installer');"), + "kubectl": []byte("fake kubectl binary"), + // node missing + } + err = createTestTarGz(testPackageFile, packageFiles) + Expect(err).To(BeNil()) + + c.Opts.Force = true + pkg := &installer.Package{ + OmsWorkdir: tempDir, + Filename: testPackageFile, + FileIO: &util.FilesystemWriter{}, + } + + err = c.ExtractAndInstall(pkg, "linux", "amd64") + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("node executable not found in package")) + }) + + It("successfully extracts package with all required files but fails on execution", func() { + tempDir, err := os.MkdirTemp("", "oms-test-*") + Expect(err).To(BeNil()) + defer func() { + err := os.RemoveAll(tempDir) + Expect(err).To(BeNil()) + }() + + origWd, err := os.Getwd() + Expect(err).To(BeNil()) + err = os.Chdir(tempDir) + Expect(err).To(BeNil()) + defer func() { + err := os.Chdir(origWd) + Expect(err).To(BeNil()) + }() + + // Create complete package with all required files + testPackageFile := "test-package.tar.gz" + packageFiles := map[string][]byte{ + "deps.tar.gz": []byte("fake deps archive"), + "node": []byte("fake node binary that will fail to execute"), + "private-cloud-installer.js": []byte("console.log('installer');"), + "kubectl": []byte("fake kubectl binary"), + } + err = createTestTarGz(testPackageFile, packageFiles) + Expect(err).To(BeNil()) + + c.Opts.Force = true + pkg := &installer.Package{ + OmsWorkdir: tempDir, + Filename: testPackageFile, + FileIO: &util.FilesystemWriter{}, + } + + err = c.ExtractAndInstall(pkg, "linux", "amd64") + Expect(err).To(HaveOccurred()) + // Should fail when trying to chmod or execute the fake node binary + Expect(err.Error()).To(SatisfyAny( + ContainSubstring("failed to make node executable"), + ContainSubstring("failed to run installer script"), + )) + }) }) }) @@ -209,6 +365,18 @@ var _ = Describe("AddInstallCodesphereCmd", func() { Expect(forceFlag).NotTo(BeNil()) Expect(forceFlag.Shorthand).To(Equal("f")) Expect(forceFlag.DefValue).To(Equal("false")) + + configFlag := codesphereCmd.Flags().Lookup("config") + Expect(configFlag).NotTo(BeNil()) + Expect(configFlag.Shorthand).To(Equal("c")) + + privKeyFlag := codesphereCmd.Flags().Lookup("priv-key") + Expect(privKeyFlag).NotTo(BeNil()) + Expect(privKeyFlag.Shorthand).To(Equal("k")) + + skipStepFlag := codesphereCmd.Flags().Lookup("skip-step") + Expect(skipStepFlag).NotTo(BeNil()) + Expect(skipStepFlag.Shorthand).To(Equal("s")) }) }) 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 8d14b26a..bdb9c865 100644 --- a/cli/cmd/update_oms.go +++ b/cli/cmd/update_oms.go @@ -6,6 +6,7 @@ package cmd import ( "fmt" "io" + "log" "strings" "github.com/blang/semver" @@ -60,10 +61,10 @@ func (c *UpdateOmsCmd) SelfUpdate(p portal.Portal) error { } latestVersion := semver.MustParse(strings.TrimPrefix(latest.Version, "oms-v")) - fmt.Printf("current version: %v\n", currentVersion) - fmt.Printf("latest version: %v\n", latestVersion) + log.Printf("current version: %v\n", currentVersion) + log.Printf("latest version: %v\n", latestVersion) if latestVersion.Equals(currentVersion) { - fmt.Println("Current OMS CLI is already the latest version", c.Version.Version()) + log.Println("Current OMS CLI is already the latest version", c.Version.Version()) return nil } @@ -105,6 +106,6 @@ func (c *UpdateOmsCmd) SelfUpdate(p portal.Portal) error { return err } - fmt.Println("Update finished successfully.") + log.Println("Update finished successfully.") return nil } 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 } From db0c0c54053c699d055a020c06210c9c2604fcf5 Mon Sep 17 00:00:00 2001 From: Simon Herrmann Date: Wed, 15 Oct 2025 14:02:20 +0200 Subject: [PATCH 4/9] udate: add lima config and make commands for easy use --- Makefile | 7 ++++ go.mod | 4 -- go.sum | 44 +--------------------- hack/lima-oms.yaml | 91 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 47 deletions(-) create mode 100644 hack/lima-oms.yaml diff --git a/Makefile b/Makefile index 23663575..986ba051 100644 --- a/Makefile +++ b/Makefile @@ -55,3 +55,10 @@ docs: generate-license: generate go-licenses report --template .NOTICE.template ./... > NOTICE copywrite headers apply + +run-lima: + limactl start ./hack/lima-oms.yaml + +stop-lima: + limactl stop lima-oms + limactl delete lima-oms diff --git a/go.mod b/go.mod index a1628b4a..7dc1bdb4 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,6 @@ require ( require ( github.com/Masterminds/semver/v3 v3.4.0 // indirect - github.com/chzyer/readline v1.5.1 // indirect github.com/clipperhouse/uax29/v2 v2.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect @@ -24,13 +23,10 @@ require ( github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/google/go-cmp v0.7.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 github.com/kr/pretty v0.3.1 // indirect - 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/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 diff --git a/go.sum b/go.sum index 1996bbbe..51d2f6a8 100644 --- a/go.sum +++ b/go.sum @@ -2,17 +2,10 @@ github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1 github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= -github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= -github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= -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.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= github.com/codesphere-cloud/cs-go v0.11.1/go.mod h1:PzUvSxGKoonp4BjV3NNl7MPg1rqOrwVPwS3u8SeKsNQ= -github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= @@ -25,18 +18,12 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= 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/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/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= github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jedib0t/go-pretty/v6 v6.6.7 h1:m+LbHpm0aIAPLzLbMfn8dc3Ht8MW7lsSO4MPItz/Uuo= -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.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -46,16 +33,10 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= -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/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.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= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -63,59 +44,36 @@ 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/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= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= -github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -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= 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.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/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-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.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= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= -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/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= +google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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= diff --git a/hack/lima-oms.yaml b/hack/lima-oms.yaml new file mode 100644 index 00000000..d48532f4 --- /dev/null +++ b/hack/lima-oms.yaml @@ -0,0 +1,91 @@ +# Clean Lima configuration for OMS development +vmType: "qemu" +os: "Linux" +arch: "x86_64" + +# Ubuntu 24.04 LTS +images: +- location: "https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img" + arch: "x86_64" + +# Resources +cpus: 4 +memory: "8GiB" +disk: "60GiB" + +# Mount your OMS project +mounts: +- location: "." + mountPoint: "/home/user/oms" + writable: true + +# Ports and SSH +ssh: + localPort: 60022 + loadDotSSHPubKeys: true +portForwards: +- guestPort: 8080 + hostPort: 8080 + +# containerd +containerd: + system: false + user: false + +# Install Docker and Go +provision: +- mode: system + script: | + #!/bin/bash + set -eux -o pipefail + + # Update packages + export DEBIAN_FRONTEND=noninteractive + apt-get update + + # Install Docker + curl -fsSL https://get.docker.com | sh + systemctl disable --now docker + apt-get install -y uidmap dbus-user-session + +- mode: user + script: | + #!/bin/bash + set -eux -o pipefail + + # Install Docker in rootless mode + dockerd-rootless-setuptool.sh install + + # Install Go 1.24 + cd /tmp + wget -q "https://go.dev/dl/go1.24.0.linux-amd64.tar.gz" + sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf "go1.24.0.linux-amd64.tar.gz" + echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc + echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.profile + + # Install build tools + sudo apt-get update + sudo apt-get install -y make git + + # Set up the OMS project + cd /home/user/oms + export PATH=$PATH:/usr/local/go/bin + go mod download + cd cli && go build -a -buildvcs=false && mv cli ../oms-cli + +message: | + Your OMS development environment is ready! + + To access it: + ------ + limactl shell lima-oms + cd /home/user/oms + ./oms-cli --help + ------ + + To install Codesphere eg.: + ------ + ./oms-cli install codesphere --package codesphere-v1.66.0-installer --config config.yaml --priv-key ./path-to-private-key + ------ + + Go 1.24 and Docker are installed and ready to use. From 2f9f685d740208273752bedaf6d1335d49d811dc Mon Sep 17 00:00:00 2001 From: Simon Herrmann Date: Wed, 15 Oct 2025 16:01:48 +0200 Subject: [PATCH 5/9] update: add comments, update path concatenation to filepath.Join --- cli/cmd/install_codesphere.go | 7 ++++--- internal/installer/package.go | 6 ++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/cli/cmd/install_codesphere.go b/cli/cmd/install_codesphere.go index 7cea003e..005e055f 100644 --- a/cli/cmd/install_codesphere.go +++ b/cli/cmd/install_codesphere.go @@ -8,6 +8,7 @@ import ( "log" "os" "os/exec" + "path/filepath" "runtime" "slices" @@ -95,7 +96,7 @@ func (c *InstallCodesphereCmd) ExtractAndInstall(p *installer.Package, goos stri return fmt.Errorf("node executable not found in package") } - nodePath := "./" + p.GetWorkDir() + "/node" + nodePath := filepath.Join(".", p.GetWorkDir(), "node") err = os.Chmod(nodePath, 0755) if err != nil { return fmt.Errorf("failed to make node executable: %w", err) @@ -103,8 +104,8 @@ func (c *InstallCodesphereCmd) ExtractAndInstall(p *installer.Package, goos stri log.Printf("Using Node.js executable: %s", nodePath) log.Println("Starting private cloud installer script...") - installerPath := "./" + p.GetWorkDir() + "/private-cloud-installer.js" - archivePath := "./" + p.GetWorkDir() + "/deps.tar.gz" + installerPath := filepath.Join(".", p.GetWorkDir(), "private-cloud-installer.js") + archivePath := filepath.Join(".", p.GetWorkDir(), "deps.tar.gz") // Build command cmdArgs := []string{installerPath, "--archive", archivePath, "--config", c.Opts.Config, "--privKey", c.Opts.PrivKey} diff --git a/internal/installer/package.go b/internal/installer/package.go index c2a849cb..553e3dac 100644 --- a/internal/installer/package.go +++ b/internal/installer/package.go @@ -29,6 +29,8 @@ func NewPackage(omsWorkdir, filename string) *Package { } } +// Extract extracts the package tar.gz file into its working directory. +// If force is true, it will overwrite existing files. func (p *Package) Extract(force bool) error { workDir := p.GetWorkDir() err := os.MkdirAll(p.OmsWorkdir, 0755) @@ -53,6 +55,7 @@ func (p *Package) Extract(force bool) error { return nil } +// ExtractDependency extracts a specific dependency file from the deps.tar.gz archive within the package. func (p *Package) ExtractDependency(file string, force bool) error { err := p.Extract(force) if err != nil { @@ -84,10 +87,13 @@ func (p *Package) alreadyExtracted(dir string) (bool, error) { return isDir, nil } +// GetWorkDir returns the working directory path for the package +// by joining the OmsWorkdir and the filename (without the .tar.gz extension). func (p *Package) GetWorkDir() string { return path.Join(p.OmsWorkdir, strings.ReplaceAll(p.Filename, ".tar.gz", "")) } +// GetDependencyPath returns the full path to a dependency file within the package's deps directory. func (p *Package) GetDependencyPath(filename string) string { workDir := p.GetWorkDir() return path.Join(workDir, depsDir, filename) From e98e83bca373625029b3748e5af9fdd73e86c0d6 Mon Sep 17 00:00:00 2001 From: Simon Herrmann Date: Wed, 15 Oct 2025 17:11:33 +0200 Subject: [PATCH 6/9] update: update go installation to apt get --- hack/lima-oms.yaml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/hack/lima-oms.yaml b/hack/lima-oms.yaml index d48532f4..5ccff727 100644 --- a/hack/lima-oms.yaml +++ b/hack/lima-oms.yaml @@ -56,15 +56,9 @@ provision: # Install Docker in rootless mode dockerd-rootless-setuptool.sh install - # Install Go 1.24 - cd /tmp - wget -q "https://go.dev/dl/go1.24.0.linux-amd64.tar.gz" - sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf "go1.24.0.linux-amd64.tar.gz" - echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc - echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.profile - - # Install build tools - sudo apt-get update + # Install Go 1.24 and build tools + sudo apt update + sudo apt install golang-go sudo apt-get install -y make git # Set up the OMS project From d518f5246cb210470411b13d58a58b5b81d0d003 Mon Sep 17 00:00:00 2001 From: Simon Herrmann Date: Wed, 15 Oct 2025 17:15:56 +0200 Subject: [PATCH 7/9] update: update skip-steps to slice of string --- cli/cmd/install_codesphere.go | 18 ++++++++++-------- cli/cmd/install_codesphere_test.go | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/cli/cmd/install_codesphere.go b/cli/cmd/install_codesphere.go index 005e055f..5f2e35a1 100644 --- a/cli/cmd/install_codesphere.go +++ b/cli/cmd/install_codesphere.go @@ -28,11 +28,11 @@ type InstallCodesphereCmd struct { type InstallCodesphereOpts struct { *GlobalOptions - Package string - Force bool - Config string - PrivKey string - SkipStep string + Package string + Force bool + Config string + PrivKey string + SkipSteps []string } func (c *InstallCodesphereCmd) RunE(_ *cobra.Command, args []string) error { @@ -61,7 +61,7 @@ func AddInstallCodesphereCmd(install *cobra.Command, opts *GlobalOptions) { codesphere.cmd.Flags().BoolVarP(&codesphere.Opts.Force, "force", "f", false, "Enforce package extraction") codesphere.cmd.Flags().StringVarP(&codesphere.Opts.Config, "config", "c", "", "Configuration file for the private cloud installer") codesphere.cmd.Flags().StringVarP(&codesphere.Opts.PrivKey, "priv-key", "k", "", "Private key file for the installation") - codesphere.cmd.Flags().StringVarP(&codesphere.Opts.SkipStep, "skip-step", "s", "", "Skip specific installation steps") + codesphere.cmd.Flags().StringSliceVarP(&codesphere.Opts.SkipSteps, "skip-steps", "s", []string{}, "Skip specific installation steps") util.MarkFlagRequired(codesphere.cmd, "package") util.MarkFlagRequired(codesphere.cmd, "config") @@ -109,8 +109,10 @@ func (c *InstallCodesphereCmd) ExtractAndInstall(p *installer.Package, goos stri // Build command cmdArgs := []string{installerPath, "--archive", archivePath, "--config", c.Opts.Config, "--privKey", c.Opts.PrivKey} - if c.Opts.SkipStep != "" { - cmdArgs = append(cmdArgs, "--skipStep", c.Opts.SkipStep) + if len(c.Opts.SkipSteps) > 0 { + for _, step := range c.Opts.SkipSteps { + cmdArgs = append(cmdArgs, "--skipStep", step) + } } cmd := exec.Command(nodePath, cmdArgs...) diff --git a/cli/cmd/install_codesphere_test.go b/cli/cmd/install_codesphere_test.go index b0bf1d02..40743580 100644 --- a/cli/cmd/install_codesphere_test.go +++ b/cli/cmd/install_codesphere_test.go @@ -374,7 +374,7 @@ var _ = Describe("AddInstallCodesphereCmd", func() { Expect(privKeyFlag).NotTo(BeNil()) Expect(privKeyFlag.Shorthand).To(Equal("k")) - skipStepFlag := codesphereCmd.Flags().Lookup("skip-step") + skipStepFlag := codesphereCmd.Flags().Lookup("skip-steps") Expect(skipStepFlag).NotTo(BeNil()) Expect(skipStepFlag.Shorthand).To(Equal("s")) }) From e8afcba9d4fb7da94c63b6c5414f54c34995874a Mon Sep 17 00:00:00 2001 From: Simon Herrmann Date: Wed, 15 Oct 2025 17:18:13 +0200 Subject: [PATCH 8/9] update: update description of the install codesphere command --- cli/cmd/install_codesphere.go | 5 +++-- cli/cmd/install_codesphere_test.go | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cli/cmd/install_codesphere.go b/cli/cmd/install_codesphere.go index 5f2e35a1..301c80b8 100644 --- a/cli/cmd/install_codesphere.go +++ b/cli/cmd/install_codesphere.go @@ -51,8 +51,9 @@ func AddInstallCodesphereCmd(install *cobra.Command, opts *GlobalOptions) { codesphere := InstallCodesphereCmd{ cmd: &cobra.Command{ Use: "codesphere", - Short: "Coming soon: Install a Codesphere instance", - Long: io.Long(`Coming soon: Install a Codesphere instance`), + Short: "Install a Codesphere instance", + Long: io.Long(`Install a Codesphere instance with the provided package, configuration file, and private key. + Uses the private-cloud-installer.js script included in the package to perform the installation.`), }, Opts: &InstallCodesphereOpts{GlobalOptions: opts}, Env: env.NewEnv(), diff --git a/cli/cmd/install_codesphere_test.go b/cli/cmd/install_codesphere_test.go index 40743580..bcb04db7 100644 --- a/cli/cmd/install_codesphere_test.go +++ b/cli/cmd/install_codesphere_test.go @@ -352,8 +352,8 @@ var _ = Describe("AddInstallCodesphereCmd", func() { Expect(codesphereCmd).NotTo(BeNil()) Expect(codesphereCmd.Use).To(Equal("codesphere")) - Expect(codesphereCmd.Short).To(Equal("Coming soon: Install a Codesphere instance")) - Expect(codesphereCmd.Long).To(ContainSubstring("Coming soon: Install a Codesphere instance")) + Expect(codesphereCmd.Short).To(Equal("Install a Codesphere instance")) + Expect(codesphereCmd.Long).To(ContainSubstring("Uses the private-cloud-installer.js script included in the package to perform the installation.")) Expect(codesphereCmd.RunE).NotTo(BeNil()) // Check flags From e0dfb4e1f27caf23c8b7eb1b3c06349ac225f99c Mon Sep 17 00:00:00 2001 From: Simon Herrmann Date: Thu, 16 Oct 2025 11:20:08 +0200 Subject: [PATCH 9/9] update: updated flag descriptions for private cloud installer --- cli/cmd/install_codesphere.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/cmd/install_codesphere.go b/cli/cmd/install_codesphere.go index 301c80b8..59b65794 100644 --- a/cli/cmd/install_codesphere.go +++ b/cli/cmd/install_codesphere.go @@ -60,9 +60,9 @@ func AddInstallCodesphereCmd(install *cobra.Command, opts *GlobalOptions) { } codesphere.cmd.Flags().StringVarP(&codesphere.Opts.Package, "package", "p", "", "Package file (e.g. codesphere-v1.2.3-installer.tar.gz) to load binaries, installer etc. from") codesphere.cmd.Flags().BoolVarP(&codesphere.Opts.Force, "force", "f", false, "Enforce package extraction") - codesphere.cmd.Flags().StringVarP(&codesphere.Opts.Config, "config", "c", "", "Configuration file for the private cloud installer") - codesphere.cmd.Flags().StringVarP(&codesphere.Opts.PrivKey, "priv-key", "k", "", "Private key file for the installation") - codesphere.cmd.Flags().StringSliceVarP(&codesphere.Opts.SkipSteps, "skip-steps", "s", []string{}, "Skip specific installation steps") + codesphere.cmd.Flags().StringVarP(&codesphere.Opts.Config, "config", "c", "", "Path to the Codesphere Private Cloud configuration file (yaml)") + codesphere.cmd.Flags().StringVarP(&codesphere.Opts.PrivKey, "priv-key", "k", "", "Path to the private key to encrypt/decrypt secrets") + codesphere.cmd.Flags().StringSliceVarP(&codesphere.Opts.SkipSteps, "skip-steps", "s", []string{}, "Steps to be skipped. Must be one of: copy-dependencies, extract-dependencies, load-container-images, ceph, kubernetes") util.MarkFlagRequired(codesphere.cmd, "package") util.MarkFlagRequired(codesphere.cmd, "config")