diff --git a/cmd/oci/oci.go b/cmd/oci/oci.go index 3fe01965..57935533 100644 --- a/cmd/oci/oci.go +++ b/cmd/oci/oci.go @@ -36,7 +36,7 @@ func init() { OCICmd.Flags().BoolVarP(&loadPodman, "load-podman", "", false, "Load the image into podman") OCICmd.Flags().BoolVarP(&push, "push", "", false, "Push the image to the registry") OCICmd.Flags().BoolVarP(&devDeps, "dev", "", false, "Build base image for Dev Dependencies") - OCICmd.Flags().BoolVarP(&dfSwap, "df-swap", "", false, "Build base image for Dev Dependencies") + OCICmd.Flags().BoolVarP(&dfSwap, "df-swap", "", false, "Modify base images in Dockerfile") OCICmd.Flags().StringVarP(&tag, "tag", "t", "", "The tag that will be replaced with original tag in Dockerfile") OCICmd.Flags().StringVar(&path, "path", "", "The path to Dockerfile") } @@ -66,16 +66,14 @@ var OCICmd = &cobra.Command{ if dfSwap { if tag != "" { - if err := builddocker.ModifyDockerfile(path, tag, devDeps); err != nil { + if err = modifyDockerfileWithTag(path, tag, devDeps); err != nil { fmt.Println(styles.ErrorStyle.Render("error: ", err.Error())) - os.Exit(1) } fmt.Println(styles.SucessStyle.Render("dockerfile succesfully updated with tag:", tag)) - os.Exit(1) } else { fmt.Println(styles.HintStyle.Render("hint:", "use --tag flag to define a tag")) - os.Exit(1) } + os.Exit(1) } sc, fh, err := binit.GetBSFInitializers() @@ -254,6 +252,38 @@ func ProcessPlatformAndConfig(plat string, envName string) (hcl2nix.OCIArtifact, return artifact, plat, nil } +func modifyDockerfileWithTag(path, tag string, devDeps bool) error { + var dockerfilePath string + if path != "" { + dockerfilePath = path + "/Dockerfile" + } else { + dockerfilePath = "./Dockerfile" + } + + _, err := os.Stat(dockerfilePath) + if err != nil { + return err + } + + file, err := os.Open(dockerfilePath) + if err != nil { + return err + } + defer file.Close() + + resLines, err := builddocker.ModifyDockerfile(file, devDeps, tag) + if err != nil { + return err + } + + err = os.WriteFile(dockerfilePath, []byte(strings.Join(resLines, "\n")), 0644) + if err != nil { + return err + } + + return nil +} + func genOCIAttrName(env, platform string, artifact hcl2nix.OCIArtifact) string { var arch string diff --git a/pkg/builddocker/build.go b/pkg/builddocker/build.go index d2428967..82cb50da 100644 --- a/pkg/builddocker/build.go +++ b/pkg/builddocker/build.go @@ -50,80 +50,74 @@ func GenerateDockerfile(w io.Writer, env hcl2nix.OCIArtifact, platform string) e } // ModifyDockerfile modifies the Dockerfile with the specified tag -func ModifyDockerfile(path, tag string, dev bool) error { - var dockerfilePath string - if path != "" { - dockerfilePath = path + "/Dockerfile" - } else { - dockerfilePath = "./Dockerfile" - } - - if _, err := os.Stat(dockerfilePath); os.IsNotExist(err) { - return fmt.Errorf("dockerfile not found") +func ModifyDockerfile(file *os.File, dev bool, tag string) ([]string, error) { + lines, err := readDockerFile(file) + if err != nil { + return nil, err } - file, err := os.Open(dockerfilePath) + reslines, err := editDockerfile(lines, dev, tag) if err != nil { - return fmt.Errorf("error opening Dockerfile: %v", err) + return nil, err } - defer file.Close() + return reslines, nil +} + +func readDockerFile(file *os.File) ([]string, error) { scanner := bufio.NewScanner(file) lines := []string{} for scanner.Scan() { lines = append(lines, scanner.Text()) } if err := scanner.Err(); err != nil { - return fmt.Errorf("error reading Dockerfile: %v", err) - } - - var searchTag string - if dev { - searchTag = "# bsfimage:dev" - } else { - searchTag = "# bsfimage:runtime" + return nil, fmt.Errorf("error reading Dockerfile: %v", err) } + return lines, nil +} - var selectedFrom string - var selectedIndex int - for i, line := range lines { - if strings.Contains(line, searchTag) { - selectedFrom = line - selectedIndex = i - break + func editDockerfile(lines []string, dev bool, tag string) ([]string, error) { + var searchTag string + if dev { + searchTag = "# bsfimage:dev" + } else { + searchTag = "# bsfimage:runtime" } - } - if selectedFrom == "" { - return fmt.Errorf("no FROM command found with tag %s", searchTag) - } + var selectedFrom string + var selectedIndex int + for i, line := range lines { + if strings.Contains(line, searchTag) { + selectedFrom = line + selectedIndex = i + break + } + } - fromParts := strings.Fields(selectedFrom) - if len(fromParts) < 2 { - return fmt.Errorf("invalid FROM command format") - } + if selectedFrom == "" { + return nil, fmt.Errorf("no FROM command found with tag %s", searchTag) + } - var newFrom string - if strings.Contains(fromParts[1], ":") { - imageParts := strings.Split(fromParts[1], ":") - newFrom = fmt.Sprintf("FROM %s:%s", imageParts[0], tag) - } else { - newFrom = fmt.Sprintf("FROM %s:%s", fromParts[1], tag) - } - for _, part := range fromParts[2:] { - newFrom = fmt.Sprintf("%s %s", newFrom, part) - } + fromParts := strings.Fields(selectedFrom) + if len(fromParts) < 2 { + return nil, fmt.Errorf("invalid FROM command format") + } - lines[selectedIndex] = newFrom + var newFrom string + if strings.Contains(fromParts[1], ":") { + imageParts := strings.Split(fromParts[1], ":") + newFrom = fmt.Sprintf("FROM %s:%s", imageParts[0], tag) + } else { + newFrom = fmt.Sprintf("FROM %s:%s", fromParts[1], tag) + } + for _, part := range fromParts[2:] { + newFrom = fmt.Sprintf("%s %s", newFrom, part) + } - err = os.WriteFile(dockerfilePath, []byte(strings.Join(lines, "\n")), 0644) - if err != nil { - return fmt.Errorf("error writing to Dockerfile: %v", err) + lines[selectedIndex] = newFrom + return lines, nil } - return nil -} - func convertExportCfgToDockerfileCfg(env hcl2nix.OCIArtifact, platform string) dockerfileCfg { switch platform { case "linux/amd64": diff --git a/pkg/builddocker/build_test.go b/pkg/builddocker/build_test.go new file mode 100644 index 00000000..405a28d3 --- /dev/null +++ b/pkg/builddocker/build_test.go @@ -0,0 +1,80 @@ +package builddocker + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestEditDockerFile(t *testing.T) { + tests := []struct { + name string + lines []string + isDev bool + tag string + expectedRes []string + expectError bool + }{ + { + name: "Dev", + lines: []string{ + "FROM ubuntu:18.04 # bsfimage:dev", + "RUN apt-get update", + }, + isDev: true, + tag: "latest", + expectedRes: []string{ + "FROM ubuntu:latest # bsfimage:dev", + "RUN apt-get update", + }, + expectError: false, + }, + { + name: "Runtime", + lines: []string{ + "FROM ubuntu:18.04 # bsfimage:runtime", + "RUN apt-get update", + }, + isDev: false, + tag: "latest", + expectedRes: []string{ + "FROM ubuntu:latest # bsfimage:runtime", + "RUN apt-get update", + }, + expectError: false, + }, + { + name: "No FROM Command with bsf tag", + lines: []string{ + "FROM ubuntu:latest", + "RUN apt-get update", + }, + isDev: true, + tag: "latest", + expectedRes: nil, + expectError: true, + }, + { + name: "No FROM Command", + lines: []string{ + "RUN apt-get update", + }, + isDev: true, + tag: "latest", + expectedRes: nil, + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res, err := editDockerfile(tt.lines, tt.isDev, tt.tag) + if tt.expectError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedRes, res) + } + }) + } +}