Skip to content

Commit

Permalink
Support strong dependencies in manifests
Browse files Browse the repository at this point in the history
  • Loading branch information
charypar committed May 15, 2018
1 parent 2ad0ed8 commit 97ad88d
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 40 deletions.
111 changes: 84 additions & 27 deletions manifests/manifests.go
Expand Up @@ -8,62 +8,119 @@ import (
"strings"
)

func validDependency(components []string, dependency string) bool {
// Kind of the dependency (enum)
type Kind int

// Weak dependency is one, changes to which cause rebuild of its dependents,
// but dependents builds can run in parallel with its build
var Weak Kind = 1

// Strong dependency is one, changes to which cause rebuild of its dependentes,
// and the dependent builds can only be run when its build successfully finishes
var Strong Kind = 2

// Dependency holds information about the dependency relationship of one
// component with another. Dependencies hav a string name and a kind, which
// can be Weak or Strong
type Dependency struct {
name string
kind Kind
}

func validDependency(components []string, dependency Dependency) bool {
for _, c := range components {
if c == dependency {
if c == dependency.name {
return true
}
}

return false
}

func Read(manifestPaths []string, dependOnSelf bool) ([]string, map[string][]string, []error) {
dependencies := make(map[string][]string, len(manifestPaths))
func readDependency(line string) (Dependency, error) {
dep := strings.TrimRight(strings.TrimSpace(line), "/")

// blank line or comment
if len(dep) < 1 || dep[0] == '#' {
return Dependency{}, nil
}

// strong dependency
if dep[0] == '!' {
return Dependency{dep[1:], Strong}, nil
}

return Dependency{dep, Weak}, nil
}

func ReadManifest(path string) (string, []Dependency, []error) {
dependencies := make([]Dependency, 0)
errors := make([]error, 0)

file, err := os.Open(path)
if err != nil {
return "", nil, []error{fmt.Errorf("cannot open dependency manifest %s: %s", path, err)}
}

dir, _ := filepath.Split(path)
component := strings.TrimRight(dir, "/")

scanner := bufio.NewScanner(file)
for scanner.Scan() {
dep, err := readDependency(scanner.Text())

if err != nil {
errors = append(errors, err)
continue
}

// comment or blank line
if dep.name == "" {
continue
}

dependencies = append(dependencies, dep)
}

err = scanner.Err()
if err != nil {
return "", nil, []error{fmt.Errorf("cannot read dependency manifest %s: %s", path, err)}
}

return component, dependencies, nil
}

func Read(manifestPaths []string, dependOnSelf bool) ([]string, map[string][]Dependency, []error) {
dependencies := make(map[string][]Dependency, len(manifestPaths))
components := make([]string, 0)
errors := []error{}

for _, manifest := range manifestPaths {
file, err := os.Open(manifest)
component, deps, err := ReadManifest(manifest)
if err != nil {
return nil, nil, []error{fmt.Errorf("cannot open dependency manifest %s: %s", manifest, err)}
errors = append(errors, err...)
continue
}

dir, _ := filepath.Split(manifest)
component := strings.TrimRight(dir, "/")
components = append(components, component)

if dependOnSelf {
dependencies[component] = []string{component}
} else {
dependencies[component] = []string{}
deps = append([]Dependency{Dependency{component, Weak}}, deps...)
}

scanner := bufio.NewScanner(file)
for scanner.Scan() {
dep := strings.TrimRight(strings.TrimSpace(scanner.Text()), "/")
if len(dep) > 0 && dep[0] != '#' {
dependencies[component] = append(dependencies[component], dep)
}
}

err = scanner.Err()
if err != nil {
return nil, nil, []error{fmt.Errorf("cannot read dependency manifest %s: %s", manifest, err)}
}
components = append(components, component)
dependencies[component] = deps
}

// validate dependencies
for manifest, deps := range dependencies {
for _, dep := range deps {
if !validDependency(components, dep) {
errors = append(errors, fmt.Errorf("unknown dependency '%s' of '%s'", dep, manifest))
errors = append(errors, fmt.Errorf("unknown dependency '%s' of '%s'", dep.name, manifest))
}
}
}

if len(errors) > 0 {
return components, dependencies, errors
return nil, nil, errors
}

return components, dependencies, nil
Expand Down
20 changes: 10 additions & 10 deletions manifests/manifests_test.go
Expand Up @@ -27,7 +27,7 @@ func Test_Read(t *testing.T) {
cwd string
pattern string
want []string
want1 map[string][]string
want1 map[string][]Dependency
wantErr bool
}{
{
Expand All @@ -44,15 +44,15 @@ func Test_Read(t *testing.T) {
"libs/lib3",
"stack1",
},
map[string][]string{
"app1": []string{"app1", "libs/lib1", "libs/lib2"},
"app2": []string{"app2", "libs/lib2", "libs/lib3"},
"app3": []string{"app3", "libs/lib3"},
"app4": []string{"app4"},
"libs/lib1": []string{"libs/lib1", "libs/lib3"},
"libs/lib2": []string{"libs/lib2", "libs/lib3"},
"libs/lib3": []string{"libs/lib3"},
"stack1": []string{"stack1", "app1", "app2", "app3"},
map[string][]Dependency{
"app1": []Dependency{{"app1", Weak}, {"libs/lib1", Weak}, {"libs/lib2", Weak}},
"app2": []Dependency{{"app2", Weak}, {"libs/lib2", Weak}, {"libs/lib3", Weak}},
"app3": []Dependency{{"app3", Weak}, {"libs/lib3", Weak}},
"app4": []Dependency{{"app4", Weak}},
"libs/lib1": []Dependency{{"libs/lib1", Weak}, {"libs/lib3", Weak}},
"libs/lib2": []Dependency{{"libs/lib2", Weak}, {"libs/lib3", Weak}},
"libs/lib3": []Dependency{{"libs/lib3", Weak}},
"stack1": []Dependency{{"stack1", Weak}, {"app1", Strong}, {"app2", Strong}, {"app3", Strong}},
},
false,
},
Expand Down
6 changes: 3 additions & 3 deletions test/fixtures/manifests-test/stack1/Dependencies
@@ -1,6 +1,6 @@
# frontend
app1
!app1

# backend
app2
app3
!app2
!app3

0 comments on commit 97ad88d

Please sign in to comment.