Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
c12f131
update: add build image to install codesphere, refactor installer, ad…
siherrmann Oct 28, 2025
b5d5fba
Merge branch 'main' into reference-base-image
siherrmann Oct 28, 2025
9009a5b
chore(docs): Auto-update docs and licenses
siherrmann Oct 28, 2025
0120619
fix: fix issues after main merge, small updates
siherrmann Oct 28, 2025
e74c5db
Merge branch 'reference-base-image' of https://github.com/codesphere-…
siherrmann Oct 28, 2025
889dc3a
chore(docs): Auto-update docs and licenses
siherrmann Oct 28, 2025
b2ecd0a
refac: refactor image file to not repeat docker podman if else
siherrmann Oct 30, 2025
cfbb6e9
update: clean up filewriter, add write function, add dockerfile updat…
siherrmann Oct 30, 2025
b0cacee
fix: fix lima-config, go did not install
siherrmann Oct 30, 2025
03cf176
feat: add build image command, add update dockerfile command, unify p…
siherrmann Oct 30, 2025
2169a41
Merge branch 'reference-base-image' of https://github.com/codesphere-…
siherrmann Oct 30, 2025
12b9f1e
update: update build image command description
siherrmann Oct 30, 2025
b8c09a0
update: update build image error on missing registry
siherrmann Oct 30, 2025
98fac5a
update: update docker from update to regexp, add tests
siherrmann Oct 31, 2025
3a7a00a
update: update global options to pointer
siherrmann Oct 31, 2025
3ec9e76
update: update dockerfile from update to only workspace agent images
siherrmann Oct 31, 2025
b12bde2
Merge branch 'main' into reference-base-image
siherrmann Oct 31, 2025
5d2a8ad
update: go mod tidy
siherrmann Oct 31, 2025
e6a4e25
update: regenerate docs
siherrmann Oct 31, 2025
12b8cac
chore(docs): Auto-update docs and licenses
siherrmann Oct 31, 2025
27bfd83
update: update image name handling
siherrmann Nov 3, 2025
460732f
update: remove required from description of non required flag
siherrmann Nov 3, 2025
8616407
Merge branch 'reference-base-image' of https://github.com/codesphere-…
siherrmann Nov 3, 2025
71f560f
Merge branch 'main' into reference-base-image
siherrmann Nov 3, 2025
30329b1
chore(docs): Auto-update docs and licenses
siherrmann Nov 3, 2025
425e547
fix: fix lint issues unhandled errors
siherrmann Nov 3, 2025
467965c
Merge branch 'reference-base-image' of https://github.com/codesphere-…
siherrmann Nov 3, 2025
fb45e37
fix: fix lint error unhandled error
siherrmann Nov 3, 2025
e0e3837
update: small PR updates
siherrmann Nov 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .mockery.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,18 @@ packages:
config:
all: true
interfaces:
github.com/codesphere-cloud/oms/internal/installer:
config:
all: true
interfaces:
github.com/codesphere-cloud/oms/internal/portal:
config:
all: true
interfaces:
github.com/codesphere-cloud/oms/internal/system:
config:
all: true
interfaces:
github.com/codesphere-cloud/oms/internal/util:
config:
all: true
Expand Down
30 changes: 18 additions & 12 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@ Version: v3.5.1
License: MIT
License URL: https://github.com/blang/semver/blob/v3.5.1/LICENSE

----------
Module: github.com/clipperhouse/stringish
Version: v0.1.1
License: MIT
License URL: https://github.com/clipperhouse/stringish/blob/v0.1.1/LICENSE

----------
Module: github.com/clipperhouse/uax29/v2
Version: v2.2.0
Version: v2.3.0
License: MIT
License URL: https://github.com/clipperhouse/uax29/blob/v2.2.0/LICENSE
License URL: https://github.com/clipperhouse/uax29/blob/v2.3.0/LICENSE

----------
Module: github.com/codesphere-cloud/cs-go/pkg/io
Expand Down Expand Up @@ -71,9 +77,9 @@ License URL: https://github.com/inconshreveable/go-update/blob/8152e7eb6ccf/inte

----------
Module: github.com/jedib0t/go-pretty/v6
Version: v6.6.8
Version: v6.6.9
License: MIT
License URL: https://github.com/jedib0t/go-pretty/blob/v6.6.8/LICENSE
License URL: https://github.com/jedib0t/go-pretty/blob/v6.6.9/LICENSE

----------
Module: github.com/mattn/go-runewidth
Expand Down Expand Up @@ -113,9 +119,9 @@ License URL: https://github.com/spf13/pflag/blob/v1.0.10/LICENSE

----------
Module: github.com/stretchr/objx
Version: v0.5.2
Version: v0.5.3
License: MIT
License URL: https://github.com/stretchr/objx/blob/v0.5.2/LICENSE
License URL: https://github.com/stretchr/objx/blob/v0.5.3/LICENSE

----------
Module: github.com/stretchr/testify
Expand All @@ -137,21 +143,21 @@ License URL: https://github.com/ulikunitz/xz/blob/v0.5.15/LICENSE

----------
Module: golang.org/x/crypto
Version: v0.42.0
Version: v0.43.0
License: BSD-3-Clause
License URL: https://cs.opensource.google/go/x/crypto/+/v0.42.0:LICENSE
License URL: https://cs.opensource.google/go/x/crypto/+/v0.43.0:LICENSE

----------
Module: golang.org/x/oauth2
Version: v0.30.0
Version: v0.32.0
License: BSD-3-Clause
License URL: https://cs.opensource.google/go/x/oauth2/+/v0.30.0:LICENSE
License URL: https://cs.opensource.google/go/x/oauth2/+/v0.32.0:LICENSE

----------
Module: golang.org/x/text
Version: v0.29.0
Version: v0.30.0
License: BSD-3-Clause
License URL: https://cs.opensource.google/go/x/text/+/v0.29.0:LICENSE
License URL: https://cs.opensource.google/go/x/text/+/v0.30.0:LICENSE

----------
Module: gopkg.in/yaml.v3
Expand Down
2 changes: 1 addition & 1 deletion cli/cmd/beta.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type BetaCmd struct {
cmd *cobra.Command
}

func AddBetaCmd(rootCmd *cobra.Command, opts *GlobalOptions) {
func AddBetaCmd(rootCmd *cobra.Command, opts GlobalOptions) {
beta := BetaCmd{
cmd: &cobra.Command{
Use: "beta",
Expand Down
28 changes: 28 additions & 0 deletions cli/cmd/build.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) Codesphere Inc.
// SPDX-License-Identifier: Apache-2.0

package cmd

import (
"github.com/codesphere-cloud/cs-go/pkg/io"
"github.com/spf13/cobra"
)

// BuildCmd represents the build command
type BuildCmd struct {
cmd *cobra.Command
}

func AddBuildCmd(rootCmd *cobra.Command, opts GlobalOptions) {
build := BuildCmd{
cmd: &cobra.Command{
Use: "build",
Short: "Build and push images to a registry",
Long: io.Long(`Build and push container images to a registry using the provided configuration.`),
},
}
AddBuildImagesCmd(build.cmd, opts)
AddBuildImageCmd(build.cmd, opts)

rootCmd.AddCommand(build.cmd)
}
87 changes: 87 additions & 0 deletions cli/cmd/build_image.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package cmd

import (
"context"
"fmt"
"log"

"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/system"
"github.com/codesphere-cloud/oms/internal/util"
"github.com/spf13/cobra"
)

// BuildImageCmd represents the build image command
type BuildImageCmd struct {
cmd *cobra.Command
Opts BuildImageOpts
Env env.Env
}

type BuildImageOpts struct {
GlobalOptions
Dockerfile string
Package string
Registry string
}

func (c *BuildImageCmd) RunE(cmd *cobra.Command, args []string) error {
pm := installer.NewPackage(c.Env.GetOmsWorkdir(), c.Opts.Package)
im := system.NewImage(context.Background())

return c.BuildImage(pm, im)
}

func AddBuildImageCmd(parentCmd *cobra.Command, opts GlobalOptions) {
imageCmd := &BuildImageCmd{
cmd: &cobra.Command{
Use: "image",
Short: "Build and push Docker image using Dockerfile and Codesphere package version",
Long: `Build a Docker image from a Dockerfile and push it to a registry, tagged with the Codesphere version from the package.`,
Example: formatExamplesWithBinary("build image", []io.Example{
{Cmd: "--dockerfile baseimage/Dockerfile --package codesphere-v1.68.0.tar.gz --registry my-registry.com/my-image", Desc: "Build image for Codesphere version 1.68.0 and push to specified registry"},
}, "oms-cli"),
Args: cobra.ExactArgs(0),
},
Opts: BuildImageOpts{GlobalOptions: opts},
Env: env.NewEnv(),
}

imageCmd.cmd.Flags().StringVarP(&imageCmd.Opts.Dockerfile, "dockerfile", "d", "", "Path to the Dockerfile to build (required)")
imageCmd.cmd.Flags().StringVarP(&imageCmd.Opts.Package, "package", "p", "", "Path to the Codesphere package (required)")
imageCmd.cmd.Flags().StringVarP(&imageCmd.Opts.Registry, "registry", "r", "", "Registry URL to push to (e.g., my-registry.com/my-image) (required)")

util.MarkFlagRequired(imageCmd.cmd, "dockerfile")
util.MarkFlagRequired(imageCmd.cmd, "package")
util.MarkFlagRequired(imageCmd.cmd, "registry")

parentCmd.AddCommand(imageCmd.cmd)

imageCmd.cmd.RunE = imageCmd.RunE
}

// AddBuildImageCmd adds the build image command to the parent command
func (c *BuildImageCmd) BuildImage(pm installer.PackageManager, im system.ImageManager) error {
codesphereVersion, err := pm.GetCodesphereVersion()
if err != nil {
return fmt.Errorf("failed to get codesphere version from package: %w", err)
}

targetImage := fmt.Sprintf("%s:%s", c.Opts.Registry, codesphereVersion)

err = im.BuildImage(c.Opts.Dockerfile, targetImage, ".")
if err != nil {
return fmt.Errorf("failed to build image %s: %w", targetImage, err)
}

err = im.PushImage(targetImage)
if err != nil {
return fmt.Errorf("failed to push image %s: %w", targetImage, err)
}

log.Printf("Successfully built and pushed image: %s", targetImage)

return nil
}
170 changes: 170 additions & 0 deletions cli/cmd/build_image_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// Copyright (c) Codesphere Inc.
// SPDX-License-Identifier: Apache-2.0

package cmd_test

import (
"errors"

. "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/system"
)

var _ = Describe("BuildImageCmd", func() {
var (
c cmd.BuildImageCmd
opts cmd.BuildImageOpts
globalOpts cmd.GlobalOptions
mockEnv *env.MockEnv
)

BeforeEach(func() {
mockEnv = env.NewMockEnv(GinkgoT())
globalOpts = cmd.GlobalOptions{}
opts = cmd.BuildImageOpts{
GlobalOptions: globalOpts,
Dockerfile: "Dockerfile",
Package: "codesphere-vcodesphere-v1.66.0.tar.gz",
Registry: "my-registry.com/my-image",
}
c = cmd.BuildImageCmd{
Opts: opts,
Env: mockEnv,
}
})

AfterEach(func() {
mockEnv.AssertExpectations(GinkgoT())
})

Context("RunE method", func() {
It("calls BuildImage and fails when package manager fails", func() {
mockEnv.EXPECT().GetOmsWorkdir().Return("/test/workdir")

err := c.RunE(nil, []string{})
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("failed to get codesphere version from package"))
})

It("succeeds with valid options", func() {
mockEnv.EXPECT().GetOmsWorkdir().Return("/test/workdir")

// This will fail in real scenario because it tries to extract from real package
// But it should at least call the correct methods
err := c.RunE(nil, []string{})
Expect(err).To(HaveOccurred())
})
})

Context("BuildImage method", func() {
It("fails when package manager fails to get codesphere version", func() {
mockPackageManager := installer.NewMockPackageManager(GinkgoT())
mockImageManager := system.NewMockImageManager(GinkgoT())

mockPackageManager.EXPECT().GetCodesphereVersion().Return("", errors.New("failed to extract version"))

err := c.BuildImage(mockPackageManager, mockImageManager)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("failed to get codesphere version from package"))
})

It("fails when image build fails", func() {
mockPackageManager := installer.NewMockPackageManager(GinkgoT())
mockImageManager := system.NewMockImageManager(GinkgoT())

c.Opts.Dockerfile = "Dockerfile"
c.Opts.Registry = "my-registry.com/my-image"

mockPackageManager.EXPECT().GetCodesphereVersion().Return("codesphere-v1.66.0", nil)
mockImageManager.EXPECT().BuildImage("Dockerfile", "my-registry.com/my-image:codesphere-v1.66.0", ".").Return(errors.New("build failed"))

err := c.BuildImage(mockPackageManager, mockImageManager)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("failed to build image"))
})

It("fails when image push fails", func() {
mockPackageManager := installer.NewMockPackageManager(GinkgoT())
mockImageManager := system.NewMockImageManager(GinkgoT())

c.Opts.Dockerfile = "Dockerfile"
c.Opts.Registry = "my-registry.com/my-image"

mockPackageManager.EXPECT().GetCodesphereVersion().Return("codesphere-v1.66.0", nil)
mockImageManager.EXPECT().BuildImage("Dockerfile", "my-registry.com/my-image:codesphere-v1.66.0", ".").Return(nil)
mockImageManager.EXPECT().PushImage("my-registry.com/my-image:codesphere-v1.66.0").Return(errors.New("push failed"))

err := c.BuildImage(mockPackageManager, mockImageManager)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("failed to push image"))
})

It("successfully builds and pushes image", func() {
mockPackageManager := installer.NewMockPackageManager(GinkgoT())
mockImageManager := system.NewMockImageManager(GinkgoT())

c.Opts.Dockerfile = "Dockerfile"
c.Opts.Registry = "my-registry.com/my-image"

mockPackageManager.EXPECT().GetCodesphereVersion().Return("codesphere-v1.66.0", nil)
mockImageManager.EXPECT().BuildImage("Dockerfile", "my-registry.com/my-image:codesphere-v1.66.0", ".").Return(nil)
mockImageManager.EXPECT().PushImage("my-registry.com/my-image:codesphere-v1.66.0").Return(nil)

err := c.BuildImage(mockPackageManager, mockImageManager)
Expect(err).To(BeNil())
})
})
})

var _ = Describe("AddBuildImageCmd", func() {
var (
parentCmd *cobra.Command
globalOpts cmd.GlobalOptions
)

BeforeEach(func() {
parentCmd = &cobra.Command{Use: "build"}
globalOpts = cmd.GlobalOptions{}
})

It("adds the image command with correct properties and flags", func() {
cmd.AddBuildImageCmd(parentCmd, globalOpts)

var imageCmd *cobra.Command
for _, c := range parentCmd.Commands() {
if c.Use == "image" {
imageCmd = c
break
}
}

Expect(imageCmd).NotTo(BeNil())
Expect(imageCmd.Use).To(Equal("image"))
Expect(imageCmd.Short).To(Equal("Build and push Docker image using Dockerfile and Codesphere package version"))
Expect(imageCmd.Long).To(ContainSubstring("Build a Docker image from a Dockerfile and push it to a registry"))
Expect(imageCmd.Long).To(ContainSubstring("tagged with the Codesphere version"))
Expect(imageCmd.RunE).NotTo(BeNil())

// Check required flags
dockerfileFlag := imageCmd.Flags().Lookup("dockerfile")
Expect(dockerfileFlag).NotTo(BeNil())
Expect(dockerfileFlag.Shorthand).To(Equal("d"))
Expect(dockerfileFlag.Usage).To(ContainSubstring("Path to the Dockerfile to build"))

packageFlag := imageCmd.Flags().Lookup("package")
Expect(packageFlag).NotTo(BeNil())
Expect(packageFlag.Shorthand).To(Equal("p"))
Expect(packageFlag.Usage).To(ContainSubstring("Path to the Codesphere package"))

registryFlag := imageCmd.Flags().Lookup("registry")
Expect(registryFlag).NotTo(BeNil())
Expect(registryFlag.Shorthand).To(Equal("r"))
Expect(registryFlag.Usage).To(ContainSubstring("Registry URL to push to"))
})
})
Loading