Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for optional depednencies #103

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions BUILD.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ packages:
- go.sum
deps:
- :helloworld
- :doesntExist?
argdeps:
- version
prep:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ description: A sentence describing what the script is good for.
# contains tools you want to use in a script.
deps:
- some/other:package
- some/other:optional?
# Env sets environment variables which are present during script execution.
env:
- MESSAGE=hello
Expand Down
77 changes: 62 additions & 15 deletions pkg/leeway/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,23 +124,67 @@ func (c *Component) Git() *GitInfo {

// PackageNotFoundErr is used when something references a package we don't know about
type PackageNotFoundErr struct {
Package string
Package PackageDependency
}

func (n PackageNotFoundErr) Error() string {
return fmt.Sprintf("package \"%s\" is unknown", n.Package)
}

type packageInternal struct {
Name string `yaml:"name"`
Type PackageType `yaml:"type"`
Sources []string `yaml:"srcs"`
Dependencies []string `yaml:"deps"`
Layout map[string]string `yaml:"layout"`
ArgumentDependencies []string `yaml:"argdeps"`
Environment []string `yaml:"env"`
Ephemeral bool `yaml:"ephemeral"`
PreparationCommands [][]string `yaml:"prep"`
Name string `yaml:"name"`
Type PackageType `yaml:"type"`
Sources []string `yaml:"srcs"`
Dependencies []PackageDependency `yaml:"deps"`
Layout map[string]string `yaml:"layout"`
ArgumentDependencies []string `yaml:"argdeps"`
Environment []string `yaml:"env"`
Ephemeral bool `yaml:"ephemeral"`
PreparationCommands [][]string `yaml:"prep"`
}

type PackageDependency string

func (dep PackageDependency) Valid() bool {
_, pkg := dep.segs()
return pkg != ""
}

func (dep PackageDependency) segs() (comp, pkg string) {
segs := strings.Split(dep.FullPackage(), ":")
if len(segs) != 2 {
return
}
return segs[0], segs[1]
}

// Component returns the component of the depdency
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Small typo

Suggested change
// Component returns the component of the depdency
// Component returns the component of the dependency

func (dep PackageDependency) Component() string {
comp, _ := dep.segs()
return comp
}

// FullPackage returns the name of the package this dependency refers to
func (dep PackageDependency) FullPackage() string {
return strings.TrimSuffix(string(dep), "?")
}

// Optional returns true if the dependency is optional
func (dep PackageDependency) Optional() bool {
return strings.HasSuffix(string(dep), "?")
}

// Relative returns true if the dependency is relative within its component
func (dep PackageDependency) Relative() bool {
return strings.HasPrefix(string(dep), ":")
}

func (dep PackageDependency) WithComponent(comp string) PackageDependency {
_, pkg := dep.segs()
if dep.Optional() {
pkg += "?"
}
return PackageDependency(comp + ":" + pkg)
}

// Package is a single buildable artifact within a component
Expand Down Expand Up @@ -168,17 +212,20 @@ func (p *Package) link(idx map[string]*Package) error {
return nil
}

p.dependencies = make([]*Package, len(p.Dependencies))
p.dependencies = make([]*Package, 0, len(p.Dependencies))
p.layout = make(map[*Package]string)
for i, dep := range p.Dependencies {
deppkg, ok := idx[dep]
for _, dep := range p.Dependencies {
deppkg, ok := idx[dep.FullPackage()]
if !ok {
if dep.Optional() {
continue
}
return PackageNotFoundErr{dep}
}
p.dependencies[i] = deppkg
p.dependencies = append(p.dependencies, deppkg)

// if the user hasn't specified a layout, tie it down at this point
p.layout[deppkg], ok = p.Layout[dep]
p.layout[deppkg], ok = p.Layout[dep.FullPackage()]
if !ok {
p.layout[deppkg] = deppkg.FilesystemSafeName()
}
Expand Down
102 changes: 102 additions & 0 deletions pkg/leeway/package_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,108 @@ import (
"testing"
)

func TestPackageDependencyValid(t *testing.T) {
tests := []struct {
Input string
Expectation bool
}{
{":foo", true},
{":foo?", true},
{"absolute/component:foo?", true},
{"missing/package", false},
{":", false},
{"", false},
}
for _, test := range tests {
t.Run(test.Input, func(t *testing.T) {
act := PackageDependency(test.Input).Valid()
if act != test.Expectation {
t.Fatalf("expected %v, got %v", test.Expectation, act)
}
})
}
}

func TestPackageDependencyOptional(t *testing.T) {
tests := []struct {
Input string
Expectation bool
}{
{":foo", false},
{":foo?", true},
{"absolute/component:foo", false},
{"absolute/component:foo?", true},
}
for _, test := range tests {
t.Run(test.Input, func(t *testing.T) {
act := PackageDependency(test.Input).Optional()
if act != test.Expectation {
t.Fatalf("expected %v, got %v", test.Expectation, act)
}
})
}
}

func TestPackageDependencyRelative(t *testing.T) {
tests := []struct {
Input string
Expectation bool
}{
{":foo", true},
{":foo?", true},
{"absolute/component:foo", false},
{"absolute/component:foo?", false},
}
for _, test := range tests {
t.Run(test.Input, func(t *testing.T) {
act := PackageDependency(test.Input).Relative()
if act != test.Expectation {
t.Fatalf("expected %v, got %v", test.Expectation, act)
}
})
}
}

func TestPackageDependencyComponent(t *testing.T) {
tests := []struct {
Input string
Expectation string
}{
{":foo", ""},
{":foo?", ""},
{"absolute/component:foo?", "absolute/component"},
{"", ""},
}
for _, test := range tests {
t.Run(test.Input, func(t *testing.T) {
act := PackageDependency(test.Input).Component()
if act != test.Expectation {
t.Fatalf("expected %v, got %v", test.Expectation, act)
}
})
}
}

func TestPackageDependencyFullPackage(t *testing.T) {
tests := []struct {
Input string
Expectation string
}{
{":foo", ":foo"},
{":foo?", ":foo"},
{"absolute/component:foo?", "absolute/component:foo"},
{"", ""},
}
for _, test := range tests {
t.Run(test.Input, func(t *testing.T) {
act := PackageDependency(test.Input).FullPackage()
if act != test.Expectation {
t.Fatalf("expected %v, got %v", test.Expectation, act)
}
})
}
}

func TestResolveBuiltinVariables(t *testing.T) {
tests := []struct {
PkgType PackageType
Expand Down
25 changes: 14 additions & 11 deletions pkg/leeway/scripts.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ const (
type Script struct {
C *Component

Name string `yaml:"name"`
Description string `yaml:"description"`
Dependencies []string `yaml:"deps"`
Environment []string `yaml:"env"`
WorkdirLayout WorkdirLayout `yaml:"workdir"`
Type ScriptType `yaml:"type"`
Script string `yaml:"script"`
Name string `yaml:"name"`
Description string `yaml:"description"`
Dependencies []PackageDependency `yaml:"deps"`
Environment []string `yaml:"env"`
WorkdirLayout WorkdirLayout `yaml:"workdir"`
Type ScriptType `yaml:"type"`
Script string `yaml:"script"`

dependencies []*Package
}
Expand All @@ -55,13 +55,16 @@ func (p *Script) FullName() string {

// link connects resolves the references to the dependencies
func (p *Script) link(idx map[string]*Package) error {
p.dependencies = make([]*Package, len(p.Dependencies))
for i, dep := range p.Dependencies {
var ok bool
p.dependencies[i], ok = idx[dep]
p.dependencies = make([]*Package, 0, len(p.Dependencies))
for _, dep := range p.Dependencies {
pkg, ok := idx[dep.FullPackage()]
if !ok {
if dep.Optional() {
continue
}
return PackageNotFoundErr{dep}
}
p.dependencies = append(p.dependencies, pkg)
}
return nil
}
Expand Down
14 changes: 7 additions & 7 deletions pkg/leeway/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -573,12 +573,12 @@ func filterExcludedComponents(variant *PackageVariant, c *Component) (ignoreComp

for _, p := range c.Packages {
for i, dep := range p.Dependencies {
segs := strings.Split(dep, ":")
if len(segs) != 2 {
comp := dep.Component()
if comp == "" {
continue
}

if variant.ExcludeComponent(segs[0]) {
if variant.ExcludeComponent(comp) {
p.Dependencies[i] = p.Dependencies[len(p.Dependencies)-1]
p.Dependencies = p.Dependencies[:len(p.Dependencies)-1]
}
Expand Down Expand Up @@ -759,11 +759,11 @@ func loadComponent(ctx context.Context, workspace *Workspace, path string, args

// make all dependencies fully qualified
for idx, dep := range pkg.Dependencies {
if !strings.HasPrefix(dep, ":") {
if !dep.Relative() {
continue
}

pkg.Dependencies[idx] = comp.Name + dep
pkg.Dependencies[idx] = dep.WithComponent(comp.Name)
}
// make all layout entries full qualified
if pkg.Layout == nil {
Expand Down Expand Up @@ -807,11 +807,11 @@ func loadComponent(ctx context.Context, workspace *Workspace, path string, args

// make all dependencies fully qualified
for idx, dep := range scr.Dependencies {
if !strings.HasPrefix(dep, ":") {
if !dep.Relative() {
continue
}

scr.Dependencies[idx] = comp.Name + dep
scr.Dependencies[idx] = dep.WithComponent(comp.Name)
}
}

Expand Down