From 302c961f7541f12ad282116542ba5c4201e9cd9e Mon Sep 17 00:00:00 2001 From: Artur Reznikov Date: Wed, 9 Oct 2024 11:25:17 -0700 Subject: [PATCH 1/5] parse go mod to read golang version --- pkg/docker/image.go | 9 ++++++- pkg/utils/file.go | 17 +++++++++++++ pkg/utils/file_test.go | 54 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/pkg/docker/image.go b/pkg/docker/image.go index c5627be1f..46fc9f656 100644 --- a/pkg/docker/image.go +++ b/pkg/docker/image.go @@ -5,10 +5,12 @@ package docker import ( "fmt" + "path/filepath" "strings" "github.com/ava-labs/avalanche-cli/pkg/constants" "github.com/ava-labs/avalanche-cli/pkg/models" + "github.com/ava-labs/avalanche-cli/pkg/utils" "github.com/ava-labs/avalanche-cli/pkg/ux" ) @@ -40,7 +42,12 @@ func parseDockerImageListOutput(output []byte) []string { // BuildDockerImage builds a docker image on a remote host. func BuildDockerImage(host *models.Host, image string, path string, dockerfile string) error { - _, err := host.Command(fmt.Sprintf("cd %s && docker build -q --build-arg GO_VERSION=%s -t %s -f %s .", path, constants.BuildEnvGolangVersion, image, dockerfile), nil, constants.SSHLongRunningScriptTimeout) + goVersion, err := utils.ReadGoVersion(filepath.Join(path, "go.mod")) + if err != nil { + //fall back to default + goVersion = constants.BuildEnvGolangVersion + } + _, err = host.Command(fmt.Sprintf("cd %s && docker build -q --build-arg GO_VERSION=%s -t %s -f %s .", path, goVersion, image, dockerfile), nil, constants.SSHLongRunningScriptTimeout) return err } diff --git a/pkg/utils/file.go b/pkg/utils/file.go index 3898b3d00..bceb025ca 100644 --- a/pkg/utils/file.go +++ b/pkg/utils/file.go @@ -8,6 +8,7 @@ import ( "path/filepath" "github.com/ava-labs/avalanche-cli/pkg/constants" + "golang.org/x/mod/modfile" ) func NonEmptyDirectory(dirName string) (bool, error) { @@ -127,3 +128,19 @@ func GetRemoteComposeServicePath(serviceName string, dirs ...string) string { servicePrefix := filepath.Join(constants.CloudNodeCLIConfigBasePath, "services", serviceName) return filepath.Join(append([]string{servicePrefix}, dirs...)...) } + +// ReadGoVersion reads the Go version from the go.mod file +func ReadGoVersion(filePath string) (string, error) { + data, err := os.ReadFile(filePath) + if err != nil { + return "", err + } + modFile, err := modfile.Parse("go.mod", data, nil) + if err != nil { + return "", err + } + if modFile.Go != nil { + return modFile.Go.Version, nil + } + return "", fmt.Errorf("go version not found in %s", filePath) +} diff --git a/pkg/utils/file_test.go b/pkg/utils/file_test.go index de9c42ccb..f641861aa 100644 --- a/pkg/utils/file_test.go +++ b/pkg/utils/file_test.go @@ -44,3 +44,57 @@ func TestExpandHome(t *testing.T) { t.Errorf("ExpandHome failed for empty path: expected %s, got %s", expectedEmptyPath, expandedEmptyPath) } } + +// createTempGoMod creates a temporary go.mod file with the provided content. +func createTempGoMod(t *testing.T, content string) string { + t.Helper() + file, err := os.CreateTemp("", "go.mod") + if err != nil { + t.Fatal(err) + } + + if _, err := file.Write([]byte(content)); err != nil { + t.Fatal(err) + } + + if err := file.Close(); err != nil { + t.Fatal(err) + } + + return file.Name() +} + +// TestReadGoVersion tests all scenarios in one function using sub-tests. +func TestReadGoVersion(t *testing.T) { + t.Run("Success", func(t *testing.T) { + tempFile := createTempGoMod(t, "module example.com/test\n\ngo 1.18\n") + defer os.Remove(tempFile) // Clean up the temp file + + version, err := ReadGoVersion(tempFile) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + expectedVersion := "1.18" + if version != expectedVersion { + t.Errorf("expected version %s, got %s", expectedVersion, version) + } + }) + + t.Run("NoVersion", func(t *testing.T) { + tempFile := createTempGoMod(t, "module example.com/test\n") + defer os.Remove(tempFile) + + _, err := ReadGoVersion(tempFile) + if err == nil { + t.Fatalf("expected an error, but got none") + } + }) + + t.Run("InvalidFile", func(t *testing.T) { + _, err := ReadGoVersion("nonexistent-go.mod") + if err == nil { + t.Fatalf("expected an error for nonexistent file, but got none") + } + }) +} From 627e5cfe9b809d864c1b153c2fe46d7efd98850b Mon Sep 17 00:00:00 2001 From: Artur Reznikov Date: Wed, 9 Oct 2024 12:04:01 -0700 Subject: [PATCH 2/5] forgot --- pkg/docker/image.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/docker/image.go b/pkg/docker/image.go index 46fc9f656..2010b93a5 100644 --- a/pkg/docker/image.go +++ b/pkg/docker/image.go @@ -42,9 +42,11 @@ func parseDockerImageListOutput(output []byte) []string { // BuildDockerImage builds a docker image on a remote host. func BuildDockerImage(host *models.Host, image string, path string, dockerfile string) error { - goVersion, err := utils.ReadGoVersion(filepath.Join(path, "go.mod")) + // expectation is to have go.mod next to Dockerfile + goVersion, err := utils.ReadGoVersion(filepath.Join(filepath.Dir(dockerfile), "go.mod")) if err != nil { //fall back to default + //return err goVersion = constants.BuildEnvGolangVersion } _, err = host.Command(fmt.Sprintf("cd %s && docker build -q --build-arg GO_VERSION=%s -t %s -f %s .", path, goVersion, image, dockerfile), nil, constants.SSHLongRunningScriptTimeout) From 5ec3ed520fc0c878079ad16037e6bb6d4866e839 Mon Sep 17 00:00:00 2001 From: Artur Reznikov Date: Wed, 9 Oct 2024 13:41:45 -0700 Subject: [PATCH 3/5] r4r --- pkg/docker/image.go | 29 ++++++++++++++++++++++------- pkg/utils/file.go | 2 +- pkg/utils/file_test.go | 4 ++-- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/pkg/docker/image.go b/pkg/docker/image.go index 2010b93a5..fdf761ec1 100644 --- a/pkg/docker/image.go +++ b/pkg/docker/image.go @@ -5,6 +5,7 @@ package docker import ( "fmt" + "os" "path/filepath" "strings" @@ -40,17 +41,31 @@ func parseDockerImageListOutput(output []byte) []string { return strings.Split(string(output), "\n") } +func parseRemoteGoModFile(path string, host *models.Host) (string, error) { + goMod := filepath.Join(path, "go.mod") + // download and parse go.mod + tmpFile, err := os.CreateTemp("", "go-mod-*.txt") + if err != nil { + return "", err + } + defer os.Remove(tmpFile.Name()) + if err := host.Download(goMod, tmpFile.Name(), constants.SSHFileOpsTimeout); err != nil { + return "", err + } + return utils.ReadGoVersion(tmpFile.Name()) +} + // BuildDockerImage builds a docker image on a remote host. func BuildDockerImage(host *models.Host, image string, path string, dockerfile string) error { - // expectation is to have go.mod next to Dockerfile - goVersion, err := utils.ReadGoVersion(filepath.Join(filepath.Dir(dockerfile), "go.mod")) + goVersion, err := parseRemoteGoModFile(path, host) if err != nil { - //fall back to default - //return err + // fall back to default + ux.Logger.Info("failed to read go version from go.mod: %s. falling back to default", err) goVersion = constants.BuildEnvGolangVersion } - _, err = host.Command(fmt.Sprintf("cd %s && docker build -q --build-arg GO_VERSION=%s -t %s -f %s .", path, goVersion, image, dockerfile), nil, constants.SSHLongRunningScriptTimeout) - return err + cmd := fmt.Sprintf("cd %s && docker build -q --build-arg GO_VERSION=%s -t %s -f %s .", path, goVersion, image, dockerfile) + _, err = host.Command(cmd, nil, constants.SSHLongRunningScriptTimeout) + return fmt.Errorf("failed building docker image: %s %w", cmd, err) } // BuildDockerImageFromGitRepo builds a docker image from a git repo on a remote host. @@ -68,7 +83,7 @@ func BuildDockerImageFromGitRepo(host *models.Host, image string, gitRepo string } }() // clone the repo and checkout commit - if _, err := host.Command(fmt.Sprintf("git clone %s %s && cd %s && git checkout %s ", gitRepo, tmpDir, tmpDir, commit), nil, constants.SSHLongRunningScriptTimeout); err != nil { + if _, err := host.Command(fmt.Sprintf("git clone %s %s && cd %s && git checkout %s && sleep 2", gitRepo, tmpDir, tmpDir, commit), nil, constants.SSHLongRunningScriptTimeout); err != nil { return err } // build the image diff --git a/pkg/utils/file.go b/pkg/utils/file.go index bceb025ca..c276f71bd 100644 --- a/pkg/utils/file.go +++ b/pkg/utils/file.go @@ -135,7 +135,7 @@ func ReadGoVersion(filePath string) (string, error) { if err != nil { return "", err } - modFile, err := modfile.Parse("go.mod", data, nil) + modFile, err := modfile.Parse(filePath, data, nil) if err != nil { return "", err } diff --git a/pkg/utils/file_test.go b/pkg/utils/file_test.go index f641861aa..f500cd82d 100644 --- a/pkg/utils/file_test.go +++ b/pkg/utils/file_test.go @@ -67,7 +67,7 @@ func createTempGoMod(t *testing.T, content string) string { // TestReadGoVersion tests all scenarios in one function using sub-tests. func TestReadGoVersion(t *testing.T) { t.Run("Success", func(t *testing.T) { - tempFile := createTempGoMod(t, "module example.com/test\n\ngo 1.18\n") + tempFile := createTempGoMod(t, "module example.com/test\n\ngo 1.23\n") defer os.Remove(tempFile) // Clean up the temp file version, err := ReadGoVersion(tempFile) @@ -75,7 +75,7 @@ func TestReadGoVersion(t *testing.T) { t.Fatalf("expected no error, got %v", err) } - expectedVersion := "1.18" + expectedVersion := "1.23" if version != expectedVersion { t.Errorf("expected version %s, got %s", expectedVersion, version) } From 77186bbad55826bedcbb0d8acfde968ab663ebff Mon Sep 17 00:00:00 2001 From: Artur Reznikov Date: Wed, 9 Oct 2024 13:44:36 -0700 Subject: [PATCH 4/5] fix return err --- pkg/docker/image.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/docker/image.go b/pkg/docker/image.go index fdf761ec1..cffb1e215 100644 --- a/pkg/docker/image.go +++ b/pkg/docker/image.go @@ -65,7 +65,10 @@ func BuildDockerImage(host *models.Host, image string, path string, dockerfile s } cmd := fmt.Sprintf("cd %s && docker build -q --build-arg GO_VERSION=%s -t %s -f %s .", path, goVersion, image, dockerfile) _, err = host.Command(cmd, nil, constants.SSHLongRunningScriptTimeout) - return fmt.Errorf("failed building docker image: %s %w", cmd, err) + if err != nil { + return fmt.Errorf("failed to build docker image %s: %w", image, err) + } + return nil } // BuildDockerImageFromGitRepo builds a docker image from a git repo on a remote host. From 5113200b71283b62835219445de5cd2dbb5974ed Mon Sep 17 00:00:00 2001 From: Artur Reznikov Date: Mon, 14 Oct 2024 12:40:36 -0700 Subject: [PATCH 5/5] rm not needed sleep2 --- pkg/docker/image.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/docker/image.go b/pkg/docker/image.go index cffb1e215..105346083 100644 --- a/pkg/docker/image.go +++ b/pkg/docker/image.go @@ -86,7 +86,7 @@ func BuildDockerImageFromGitRepo(host *models.Host, image string, gitRepo string } }() // clone the repo and checkout commit - if _, err := host.Command(fmt.Sprintf("git clone %s %s && cd %s && git checkout %s && sleep 2", gitRepo, tmpDir, tmpDir, commit), nil, constants.SSHLongRunningScriptTimeout); err != nil { + if _, err := host.Command(fmt.Sprintf("git clone %s %s && cd %s && git checkout %s", gitRepo, tmpDir, tmpDir, commit), nil, constants.SSHLongRunningScriptTimeout); err != nil { return err } // build the image