From a2c36b7c66f3efeb8288865f8dba0d844388df08 Mon Sep 17 00:00:00 2001 From: Vanshika Date: Tue, 13 Feb 2024 13:25:38 +0530 Subject: [PATCH 01/11] pretest Signed-off-by: Vanshika --- pkg/client/client.go | 30 ++++++++++++++++++++++ pkg/client/dependency_graph.go | 46 ++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 pkg/client/dependency_graph.go diff --git a/pkg/client/client.go b/pkg/client/client.go index 62833d0c..fe7c4c95 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "io" + "io/ioutil" "os" "path/filepath" "reflect" @@ -823,6 +824,35 @@ func (c *KpmClient) DownloadFromGit(dep *pkg.Git, localPath string) (string, err return localPath, err } +// Parse kcl.mod file and extract dependencies +func ParseKclModFile(c *KpmClient, kclPkg *pkg.KclPkg) (map[string][]string, error) { + // Get path to kcl.mod file + modFilePath := kclPkg.ModFile.GetModFilePath() + + // Parse kcl.mod file + modFileBytes, err := ioutil.ReadFile(modFilePath) + if err != nil { + return nil, err + } + + // Initialize map to store dependencies + dependencies := make(map[string][]string) + + // Parse each line in the mod file + lines := strings.Split(string(modFileBytes), "\n") + for _, line := range lines { + // Extract dependency name and version + parts := strings.Fields(line) + if len(parts) >= 2 { + dependency := parts[0] + version := parts[1] + dependencies[dependency] = append(dependencies[dependency], version) + } + } + + return dependencies, nil +} + // DownloadFromOci will download the dependency from the oci repository. func (c *KpmClient) DownloadFromOci(dep *pkg.Oci, localPath string) (string, error) { ociClient, err := oci.NewOciClient(dep.Reg, dep.Repo, &c.settings) diff --git a/pkg/client/dependency_graph.go b/pkg/client/dependency_graph.go new file mode 100644 index 00000000..6a4513b4 --- /dev/null +++ b/pkg/client/dependency_graph.go @@ -0,0 +1,46 @@ +package client + +// Construct dependency graph +type DependencyGraph map[string][]string + +// Function to construct dependency graph from dependency map +func ConstructDependencyGraph(dependencies map[string][]string) DependencyGraph { + graph := make(DependencyGraph) + for dependency, versions := range dependencies { + graph[dependency] = versions + } + return graph +} + +// Traverse dependency graph using depth-first search (DFS) +func DFS(graph DependencyGraph, dependency string, visited map[string]bool, result []string) []string { + // Mark current dependency as visited + visited[dependency] = true + + // Add current dependency to result + result = append(result, dependency) + + // Recursively traverse dependencies + for _, dep := range graph[dependency] { + if !visited[dep] { + result = DFS(graph, dep, visited, result) + } + } + + return result +} + +// Output dependencies in the same format as go mod graph +func OutputDependencies(graph DependencyGraph) []string { + result := make([]string, 0) + visited := make(map[string]bool) + + // Traverse each dependency using DFS + for dependency := range graph { + if !visited[dependency] { + result = DFS(graph, dependency, visited, result) + } + } + + return result +} From 3c0c3d050d117a8d4da4f6978c36871149c2cb1a Mon Sep 17 00:00:00 2001 From: Vanshika Date: Tue, 13 Feb 2024 16:43:04 +0530 Subject: [PATCH 02/11] pretest Signed-off-by: Vanshika --- pkg/client/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/client/client.go b/pkg/client/client.go index fe7c4c95..c6cbff1a 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -825,7 +825,7 @@ func (c *KpmClient) DownloadFromGit(dep *pkg.Git, localPath string) (string, err } // Parse kcl.mod file and extract dependencies -func ParseKclModFile(c *KpmClient, kclPkg *pkg.KclPkg) (map[string][]string, error) { +func (c *KpmClient) ParseKclModFile(kclPkg *pkg.KclPkg) (map[string][]string, error) { // Get path to kcl.mod file modFilePath := kclPkg.ModFile.GetModFilePath() From 7f1771c066571f79ea1a805c30187e0383ef0387 Mon Sep 17 00:00:00 2001 From: Vanshika Date: Wed, 14 Feb 2024 14:52:46 +0530 Subject: [PATCH 03/11] change Signed-off-by: Vanshika --- pkg/client/client.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/client/client.go b/pkg/client/client.go index c6cbff1a..17ac5076 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "os" "path/filepath" "reflect" @@ -830,7 +829,7 @@ func (c *KpmClient) ParseKclModFile(kclPkg *pkg.KclPkg) (map[string][]string, er modFilePath := kclPkg.ModFile.GetModFilePath() // Parse kcl.mod file - modFileBytes, err := ioutil.ReadFile(modFilePath) + modFileBytes, err := os.ReadFile(modFilePath) if err != nil { return nil, err } From f8c4fbd960917cc6c3be6af449e9ff12b054d892 Mon Sep 17 00:00:00 2001 From: Vanshika Date: Fri, 16 Feb 2024 00:09:34 +0530 Subject: [PATCH 04/11] changes Signed-off-by: Vanshika --- pkg/client/dependency_graph.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pkg/client/dependency_graph.go b/pkg/client/dependency_graph.go index 6a4513b4..c225b7b0 100644 --- a/pkg/client/dependency_graph.go +++ b/pkg/client/dependency_graph.go @@ -1,8 +1,19 @@ package client +import pkg "kcl-lang.io/kpm/pkg/package" + // Construct dependency graph type DependencyGraph map[string][]string +// Function to construct dependency graph by parsing kcl.mod file +func ConstructDependencyGraphFromModFile(kpmClient *KpmClient, kclPkg *pkg.KclPkg) (DependencyGraph, error) { + dependencies, err := kpmClient.ParseKclModFile(kclPkg) + if err != nil { + return nil, err + } + return ConstructDependencyGraph(dependencies), nil +} + // Function to construct dependency graph from dependency map func ConstructDependencyGraph(dependencies map[string][]string) DependencyGraph { graph := make(DependencyGraph) From 789940ea19c88af2362fbf8912f8fefcc3845753 Mon Sep 17 00:00:00 2001 From: Vanshika Date: Sun, 18 Feb 2024 12:13:58 +0530 Subject: [PATCH 05/11] ParseKclModFile Signed-off-by: Vanshika --- pkg/client/client.go | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/pkg/client/client.go b/pkg/client/client.go index e73d5f1c..9c7bba38 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -9,6 +9,7 @@ import ( "reflect" "strings" + "github.com/BurntSushi/toml" "github.com/dominikbraun/graph" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/otiai10/copy" @@ -852,29 +853,36 @@ func (c *KpmClient) DownloadFromGit(dep *pkg.Git, localPath string) (string, err return localPath, err } -// Parse kcl.mod file and extract dependencies func (c *KpmClient) ParseKclModFile(kclPkg *pkg.KclPkg) (map[string][]string, error) { // Get path to kcl.mod file modFilePath := kclPkg.ModFile.GetModFilePath() - // Parse kcl.mod file + // Read the content of the kcl.mod file modFileBytes, err := os.ReadFile(modFilePath) if err != nil { return nil, err } - // Initialize map to store dependencies - dependencies := make(map[string][]string) + // Normalize line endings for Windows systems + modFileContent := strings.ReplaceAll(string(modFileBytes), "\r\n", "\n") + + // Parse the TOML content + var modFileData map[string]interface{} + if err := toml.Unmarshal([]byte(modFileContent), &modFileData); err != nil { + return nil, err + } - // Parse each line in the mod file - lines := strings.Split(string(modFileBytes), "\n") - for _, line := range lines { - // Extract dependency name and version - parts := strings.Fields(line) - if len(parts) >= 2 { - dependency := parts[0] - version := parts[1] - dependencies[dependency] = append(dependencies[dependency], version) + // Extract dependency information + dependencies := make(map[string][]string) + if deps, ok := modFileData["dependencies"].(map[string]interface{}); ok { + for dep, versions := range deps { + if v, ok := versions.([]interface{}); ok { + for _, ver := range v { + if version, ok := ver.(string); ok { + dependencies[dep] = append(dependencies[dep], version) + } + } + } } } From 621a7653ccda866504769624ddf6bac166c022f1 Mon Sep 17 00:00:00 2001 From: Vanshika Date: Sun, 18 Feb 2024 12:45:17 +0530 Subject: [PATCH 06/11] fullPath Signed-off-by: Vanshika --- pkg/client/client.go | 23 +++++++++++++++-------- pkg/client/dependency_graph.go | 11 ++++++++--- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/pkg/client/client.go b/pkg/client/client.go index 9c7bba38..60b3d0e3 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -853,7 +853,7 @@ func (c *KpmClient) DownloadFromGit(dep *pkg.Git, localPath string) (string, err return localPath, err } -func (c *KpmClient) ParseKclModFile(kclPkg *pkg.KclPkg) (map[string][]string, error) { +func (c *KpmClient) ParseKclModFile(kclPkg *pkg.KclPkg) (map[string]map[string]string, error) { // Get path to kcl.mod file modFilePath := kclPkg.ModFile.GetModFilePath() @@ -873,16 +873,23 @@ func (c *KpmClient) ParseKclModFile(kclPkg *pkg.KclPkg) (map[string][]string, er } // Extract dependency information - dependencies := make(map[string][]string) + dependencies := make(map[string]map[string]string) if deps, ok := modFileData["dependencies"].(map[string]interface{}); ok { - for dep, versions := range deps { - if v, ok := versions.([]interface{}); ok { - for _, ver := range v { - if version, ok := ver.(string); ok { - dependencies[dep] = append(dependencies[dep], version) - } + for dep, details := range deps { + dependency := make(map[string]string) + switch d := details.(type) { + case string: + // For simple version strings + dependency["version"] = d + case map[string]interface{}: + // For dependencies with attributes + for key, value := range d { + dependency[key] = fmt.Sprintf("%v", value) } + default: + return nil, fmt.Errorf("unsupported dependency format") } + dependencies[dep] = dependency } } diff --git a/pkg/client/dependency_graph.go b/pkg/client/dependency_graph.go index c225b7b0..2c24759d 100644 --- a/pkg/client/dependency_graph.go +++ b/pkg/client/dependency_graph.go @@ -15,10 +15,15 @@ func ConstructDependencyGraphFromModFile(kpmClient *KpmClient, kclPkg *pkg.KclPk } // Function to construct dependency graph from dependency map -func ConstructDependencyGraph(dependencies map[string][]string) DependencyGraph { +func ConstructDependencyGraph(dependencies map[string]map[string]string) DependencyGraph { graph := make(DependencyGraph) - for dependency, versions := range dependencies { - graph[dependency] = versions + for dependency, details := range dependencies { + // Construct full module path including version or other attributes + fullPath := dependency + if version, ok := details["version"]; ok { + fullPath += "@" + version + } + graph[fullPath] = make([]string, 0) } return graph } From e4dc025f96fa55a6fb85256f44d5c52d23bb6ef0 Mon Sep 17 00:00:00 2001 From: Vanshika Date: Sun, 18 Feb 2024 13:47:07 +0530 Subject: [PATCH 07/11] unit-test Signed-off-by: Vanshika --- pkg/client/client_test.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index b9706ea3..735d3f4f 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -157,6 +157,39 @@ func TestDependencyGraph(t *testing.T) { ) } +func TestParseKclModFile(t *testing.T) { + testDir := getTestDir("test_parse_kcl_mod_file") + kpmcli, err := NewKpmClient() + assert.Nil(t, err, "error creating KpmClient") + + // Create a sample kcl.mod file for testing + modFileContent := ` + [dependencies] + teleport = "0.1.0" + rabbitmq = "0.0.1" + agent = { version = "0.1.0", someAttribute = "value" } + ` + modFilePath := filepath.Join(testDir, "kcl.mod") + err = os.WriteFile(modFilePath, []byte(modFileContent), 0644) + assert.Nil(t, err, "error writing mod file") + + // Create a mock KclPkg + mockKclPkg, err := kpmcli.LoadPkgFromPath(testDir) + assert.Nil(t, err, "error loading package from path") + + // Test the ParseKclModFile function + dependencies, err := kpmcli.ParseKclModFile(mockKclPkg) + assert.Nil(t, err, "error parsing kcl.mod file") + + expectedDependencies := map[string]map[string]string{ + "teleport": {"version": "0.1.0"}, + "rabbitmq": {"version": "0.0.1"}, + "agent": {"version": "0.1.0", "someAttribute": "value"}, + } + + assert.Equal(t, expectedDependencies, dependencies, "parsed dependencies do not match expected dependencies") +} + func TestInitEmptyPkg(t *testing.T) { testDir := initTestDir("test_init_empty_mod") kclPkg := pkg.NewKclPkg(&opt.InitOptions{Name: "test_name", InitPath: testDir}) From eed7b2e9cdb356f769892624e63b86494762c479 Mon Sep 17 00:00:00 2001 From: Vanshika Date: Sun, 18 Feb 2024 23:46:41 +0530 Subject: [PATCH 08/11] handling dependencies Signed-off-by: Vanshika --- pkg/client/client_test.go | 6 +++++- pkg/client/dependency_graph.go | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index 735d3f4f..0f8e2e39 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -162,12 +162,14 @@ func TestParseKclModFile(t *testing.T) { kpmcli, err := NewKpmClient() assert.Nil(t, err, "error creating KpmClient") - // Create a sample kcl.mod file for testing + // Create a sample kcl.mod file for testing with various dependency scenarios modFileContent := ` [dependencies] teleport = "0.1.0" rabbitmq = "0.0.1" agent = { version = "0.1.0", someAttribute = "value" } + gitdep = { git = "git://example.com/repo.git", tag = "v1.0.0" } + localdep = { path = "/path/to/local/dependency" } ` modFilePath := filepath.Join(testDir, "kcl.mod") err = os.WriteFile(modFilePath, []byte(modFileContent), 0644) @@ -185,6 +187,8 @@ func TestParseKclModFile(t *testing.T) { "teleport": {"version": "0.1.0"}, "rabbitmq": {"version": "0.0.1"}, "agent": {"version": "0.1.0", "someAttribute": "value"}, + "gitdep": {"git": "git://example.com/repo.git", "tag": "v1.0.0"}, + "localdep": {"path": "/path/to/local/dependency"}, } assert.Equal(t, expectedDependencies, dependencies, "parsed dependencies do not match expected dependencies") diff --git a/pkg/client/dependency_graph.go b/pkg/client/dependency_graph.go index 2c24759d..ae02955d 100644 --- a/pkg/client/dependency_graph.go +++ b/pkg/client/dependency_graph.go @@ -22,6 +22,15 @@ func ConstructDependencyGraph(dependencies map[string]map[string]string) Depende fullPath := dependency if version, ok := details["version"]; ok { fullPath += "@" + version + } else if gitURL, ok := details["git"]; ok { + fullPath += "@" + gitURL + if tag, ok := details["tag"]; ok { + fullPath += "#" + tag + } else if commit, ok := details["commit"]; ok { + fullPath += "@" + commit + } + } else if path, ok := details["path"]; ok { + fullPath += "@" + path } graph[fullPath] = make([]string, 0) } From 63c79b7df9c11232429e2540992b67cf039388e7 Mon Sep 17 00:00:00 2001 From: Vanshika Date: Mon, 19 Feb 2024 22:15:07 +0530 Subject: [PATCH 09/11] test cases Signed-off-by: Vanshika --- pkg/client/client_test.go | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index 0f8e2e39..464e9321 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -9,6 +9,7 @@ import ( "log" "os" "path/filepath" + "reflect" "strings" "testing" @@ -158,11 +159,22 @@ func TestDependencyGraph(t *testing.T) { } func TestParseKclModFile(t *testing.T) { - testDir := getTestDir("test_parse_kcl_mod_file") + // Create a temporary directory for testing + testDir, err := os.MkdirTemp("", "test_parse_kcl_mod_file") + if err != nil { + t.Fatalf("error creating temp directory: %v", err) + } + defer os.RemoveAll(testDir) // Clean up the temporary directory + kpmcli, err := NewKpmClient() - assert.Nil(t, err, "error creating KpmClient") + if err != nil { + t.Fatalf("error creating KpmClient: %v", err) + } + + // Construct the modFilePath using filepath.Join + modFilePath := filepath.Join(testDir, "kcl.mod") - // Create a sample kcl.mod file for testing with various dependency scenarios + // Write modFileContent to modFilePath modFileContent := ` [dependencies] teleport = "0.1.0" @@ -171,17 +183,21 @@ func TestParseKclModFile(t *testing.T) { gitdep = { git = "git://example.com/repo.git", tag = "v1.0.0" } localdep = { path = "/path/to/local/dependency" } ` - modFilePath := filepath.Join(testDir, "kcl.mod") - err = os.WriteFile(modFilePath, []byte(modFileContent), 0644) - assert.Nil(t, err, "error writing mod file") + if err := os.WriteFile(modFilePath, []byte(modFileContent), 0644); err != nil { + t.Fatalf("error writing mod file: %v", err) + } // Create a mock KclPkg mockKclPkg, err := kpmcli.LoadPkgFromPath(testDir) - assert.Nil(t, err, "error loading package from path") + if err != nil { + t.Fatalf("error loading package from path: %v", err) + } // Test the ParseKclModFile function dependencies, err := kpmcli.ParseKclModFile(mockKclPkg) - assert.Nil(t, err, "error parsing kcl.mod file") + if err != nil { + t.Fatalf("error parsing kcl.mod file: %v", err) + } expectedDependencies := map[string]map[string]string{ "teleport": {"version": "0.1.0"}, @@ -191,7 +207,9 @@ func TestParseKclModFile(t *testing.T) { "localdep": {"path": "/path/to/local/dependency"}, } - assert.Equal(t, expectedDependencies, dependencies, "parsed dependencies do not match expected dependencies") + if !reflect.DeepEqual(expectedDependencies, dependencies) { + t.Fatalf("parsed dependencies do not match expected dependencies") + } } func TestInitEmptyPkg(t *testing.T) { From d3bf2e83176968d58c5e022d05812bd998602202 Mon Sep 17 00:00:00 2001 From: Vanshika Date: Mon, 19 Feb 2024 23:29:02 +0530 Subject: [PATCH 10/11] formatting Signed-off-by: Vanshika --- pkg/client/client_test.go | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index 464e9321..b2bbe75c 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -9,7 +9,6 @@ import ( "log" "os" "path/filepath" - "reflect" "strings" "testing" @@ -160,16 +159,12 @@ func TestDependencyGraph(t *testing.T) { func TestParseKclModFile(t *testing.T) { // Create a temporary directory for testing - testDir, err := os.MkdirTemp("", "test_parse_kcl_mod_file") - if err != nil { - t.Fatalf("error creating temp directory: %v", err) - } - defer os.RemoveAll(testDir) // Clean up the temporary directory + testDir := initTestDir("test_parse_kcl_mod_file") + + assert.Equal(t, utils.DirExists(filepath.Join(testDir, "kcl.mod")), false) kpmcli, err := NewKpmClient() - if err != nil { - t.Fatalf("error creating KpmClient: %v", err) - } + assert.Nil(t, err, "error creating KpmClient") // Construct the modFilePath using filepath.Join modFilePath := filepath.Join(testDir, "kcl.mod") @@ -183,21 +178,18 @@ func TestParseKclModFile(t *testing.T) { gitdep = { git = "git://example.com/repo.git", tag = "v1.0.0" } localdep = { path = "/path/to/local/dependency" } ` - if err := os.WriteFile(modFilePath, []byte(modFileContent), 0644); err != nil { - t.Fatalf("error writing mod file: %v", err) - } + + err = os.WriteFile(modFilePath, []byte(modFileContent), 0644) + assert.Nil(t, err, "error writing mod file") // Create a mock KclPkg mockKclPkg, err := kpmcli.LoadPkgFromPath(testDir) - if err != nil { - t.Fatalf("error loading package from path: %v", err) - } + + assert.Nil(t, err, "error loading package from path") // Test the ParseKclModFile function dependencies, err := kpmcli.ParseKclModFile(mockKclPkg) - if err != nil { - t.Fatalf("error parsing kcl.mod file: %v", err) - } + assert.Nil(t, err, "error parsing kcl.mod file") expectedDependencies := map[string]map[string]string{ "teleport": {"version": "0.1.0"}, @@ -207,11 +199,8 @@ func TestParseKclModFile(t *testing.T) { "localdep": {"path": "/path/to/local/dependency"}, } - if !reflect.DeepEqual(expectedDependencies, dependencies) { - t.Fatalf("parsed dependencies do not match expected dependencies") - } + assert.Equal(t, expectedDependencies, dependencies, "parsed dependencies do not match expected dependencies") } - func TestInitEmptyPkg(t *testing.T) { testDir := initTestDir("test_init_empty_mod") kclPkg := pkg.NewKclPkg(&opt.InitOptions{Name: "test_name", InitPath: testDir}) From b69a434e03fe14564fac2c77b4edabee77cfe17d Mon Sep 17 00:00:00 2001 From: Vanshika Date: Tue, 20 Feb 2024 11:42:43 +0530 Subject: [PATCH 11/11] modFileContent Signed-off-by: Vanshika --- pkg/client/client_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index b2bbe75c..7997f78d 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -174,7 +174,6 @@ func TestParseKclModFile(t *testing.T) { [dependencies] teleport = "0.1.0" rabbitmq = "0.0.1" - agent = { version = "0.1.0", someAttribute = "value" } gitdep = { git = "git://example.com/repo.git", tag = "v1.0.0" } localdep = { path = "/path/to/local/dependency" } ` @@ -194,7 +193,6 @@ func TestParseKclModFile(t *testing.T) { expectedDependencies := map[string]map[string]string{ "teleport": {"version": "0.1.0"}, "rabbitmq": {"version": "0.0.1"}, - "agent": {"version": "0.1.0", "someAttribute": "value"}, "gitdep": {"git": "git://example.com/repo.git", "tag": "v1.0.0"}, "localdep": {"path": "/path/to/local/dependency"}, }