Skip to content

Commit

Permalink
Merge branch 'main' into report-when-dependency-graph-contains-cycle
Browse files Browse the repository at this point in the history
Signed-off-by: Akash Kumar <meakash7902@gmail.com>
  • Loading branch information
AkashKumar7902 committed Feb 20, 2024
2 parents 6071084 + b6d5a18 commit fbbf056
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 0 deletions.
44 changes: 44 additions & 0 deletions pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -851,6 +852,49 @@ func (c *KpmClient) DownloadFromGit(dep *pkg.Git, localPath string) (string, err
return localPath, err
}

func (c *KpmClient) ParseKclModFile(kclPkg *pkg.KclPkg) (map[string]map[string]string, error) {
// Get path to kcl.mod file
modFilePath := kclPkg.ModFile.GetModFilePath()

// Read the content of the kcl.mod file
modFileBytes, err := os.ReadFile(modFilePath)
if err != nil {
return nil, err
}

// 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
}

// Extract dependency information
dependencies := make(map[string]map[string]string)
if deps, ok := modFileData["dependencies"].(map[string]interface{}); ok {
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
}
}

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)
Expand Down
43 changes: 43 additions & 0 deletions pkg/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,49 @@ func TestCyclicDependency(t *testing.T) {
assert.Equal(t, err, nil)
}

func TestParseKclModFile(t *testing.T) {
// Create a temporary directory for testing
testDir := initTestDir("test_parse_kcl_mod_file")

assert.Equal(t, utils.DirExists(filepath.Join(testDir, "kcl.mod")), false)

kpmcli, err := NewKpmClient()
assert.Nil(t, err, "error creating KpmClient")

// Construct the modFilePath using filepath.Join
modFilePath := filepath.Join(testDir, "kcl.mod")

// Write modFileContent to modFilePath
modFileContent := `
[dependencies]
teleport = "0.1.0"
rabbitmq = "0.0.1"
gitdep = { git = "git://example.com/repo.git", tag = "v1.0.0" }
localdep = { path = "/path/to/local/dependency" }
`

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"},
"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")
}

func TestInitEmptyPkg(t *testing.T) {
testDir := initTestDir("test_init_empty_mod")
kclPkg := pkg.NewKclPkg(&opt.InitOptions{Name: "test_name", InitPath: testDir})
Expand Down
71 changes: 71 additions & 0 deletions pkg/client/dependency_graph.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
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]map[string]string) DependencyGraph {
graph := make(DependencyGraph)
for dependency, details := range dependencies {
// Construct full module path including version or other attributes
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)
}
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
}

0 comments on commit fbbf056

Please sign in to comment.