Skip to content

Commit

Permalink
configurable project name (#44)
Browse files Browse the repository at this point in the history
* configurable bobfile project name, validate project name is unique
* validate project URL, cleanup
* add project to input hash
* remove builder and use project name
* show project name on inspection
* extract project regex
* allow duplicate tasknames

Co-authored-by: equanox <matthias@benchkram.de>
  • Loading branch information
rdnt and Equanox authored Apr 8, 2022
1 parent 49753d3 commit 9b86866
Show file tree
Hide file tree
Showing 28 changed files with 336 additions and 63 deletions.
48 changes: 42 additions & 6 deletions bob/aggregate.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import (
"github.com/hashicorp/go-version"
)

var (
ErrDuplicateProjectName = fmt.Errorf("duplicate project name")
)

// find bobfiles recursively.
func (b *B) find() (bobfiles []string, err error) {
defer errz.Recover(&err)
Expand Down Expand Up @@ -61,6 +65,10 @@ func (b *B) Aggregate() (aggregate *bobfile.Bobfile, err error) {
bobfiles, err := b.find()
errz.Fatal(err)

// FIXME: As we don't refer to a child task by projectname but by path
// it seems to be save to allow duplicate projectnames.
//projectNames := map[string]bool{}

// Read & Find Bobfiles
bobs := []*bobfile.Bobfile{}
for _, bf := range bobfiles {
Expand All @@ -71,6 +79,18 @@ func (b *B) Aggregate() (aggregate *bobfile.Bobfile, err error) {
aggregate = boblet
}

// FIXME: As we don't refer to a child task by projectname but by path
// it seems to be save to allow duplicate projectnames.
//
// Make sure project names are unique
// if boblet.Project != "" {
// if ok := projectNames[boblet.Project]; ok {
// return nil, usererror.Wrap(fmt.Errorf("%w found, [%s]", ErrDuplicateProjectName, boblet.Project))
// }
// projectNames[boblet.Project] = true
// }

// add env vars and build tasks
for variable, value := range boblet.Variables {
for key, task := range boblet.BTasks {
// TODO: Create and use envvar sanitizer
Expand All @@ -88,6 +108,24 @@ func (b *B) Aggregate() (aggregate *bobfile.Bobfile, err error) {
return nil, usererror.Wrap(ErrCouldNotFindTopLevelBobfile)
}

if aggregate.Project == "" {
// TODO: maybe don't leak absolute path of environment
aggregate.Project = aggregate.Dir()
}

// set project names for all bobfiles and build tasks
for _, bobfile := range bobs {
bobfile.Project = aggregate.Project

for taskname, task := range bobfile.BTasks {
// Should be the name of the umbrella-bobfile.
task.SetProject(aggregate.Project)

// Overwrite value in build map
bobfile.BTasks[taskname] = task
}
}

aggregate.SetBobfiles(bobs)

// Merge tasks into one Bobfile
Expand All @@ -104,8 +142,6 @@ func (b *B) Aggregate() (aggregate *bobfile.Bobfile, err error) {
prefix := strings.TrimPrefix(dir, b.dir)
taskname := addTaskPrefix(prefix, taskname)

// fmt.Printf("aggreagted [dir:%s, bdir:%s prefix:%s] taskname %s\n", prefix, dir, b.dir, taskname)

// Alter the taskname.
task.SetName(taskname)

Expand All @@ -132,10 +168,11 @@ func (b *B) Aggregate() (aggregate *bobfile.Bobfile, err error) {

// Use a relative path as task prefix.
prefix := strings.TrimPrefix(dir, b.dir)
name := addTaskPrefix(prefix, runname)

runname = addTaskPrefix(prefix, runname)

// Alter the runname.
run.SetName(name)
run.SetName(runname)

// Rewrite dependents to global scope.
dependsOn := []string{}
Expand All @@ -144,7 +181,7 @@ func (b *B) Aggregate() (aggregate *bobfile.Bobfile, err error) {
}
run.DependsOn = dependsOn

aggregate.RTasks[name] = run
aggregate.RTasks[runname] = run
}
}

Expand Down Expand Up @@ -197,7 +234,6 @@ func (b *B) Aggregate() (aggregate *bobfile.Bobfile, err error) {
for i, task := range aggregate.BTasks {
task.WithLocalstore(b.local)
task.WithBuildinfoStore(b.buildInfoStore)
task.SetBuilder(b.dir) // TODO: todoproject, use project name instead of dir

// a task must always-rebuild when caching is disabled
if !b.enableCaching {
Expand Down
182 changes: 180 additions & 2 deletions bob/aggregate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func BenchmarkAggregateOnPlayground(b *testing.B) {
testBob, err := Bob(WithDir(dir))
assert.Nil(b, err)

err = CreatePlayground(dir)
err = CreatePlayground(PlaygroundOptions{Dir: dir})
assert.Nil(b, err)

var bobfile *bobfile.Bobfile // to block compiler optimization
Expand Down Expand Up @@ -75,7 +75,7 @@ func benchmarkAggregate(b *testing.B, ignoredMultiplier int) {
testBob, err := Bob(WithDir(dir))
assert.Nil(b, err)

err = CreatePlayground(dir)
err = CreatePlayground(PlaygroundOptions{Dir: dir})
assert.Nil(b, err)

// Create a file structure which Aggregate will completly travers
Expand Down Expand Up @@ -178,3 +178,181 @@ func createIgnoreFileSturcture(dir string, multiplier int) error {

return nil
}

func TestEmptyProjectName(t *testing.T) {
// Create playground
dir, err := ioutil.TempDir("", "bob-test-aggregate-*")
assert.Nil(t, err)

defer os.RemoveAll(dir)

err = os.Chdir(dir)
assert.Nil(t, err)

testBob, err := Bob(WithDir(dir))
assert.Nil(t, err)

err = CreatePlayground(PlaygroundOptions{Dir: dir})
assert.Nil(t, err)

bobfile, err := testBob.Aggregate()
assert.Nil(t, err)

assert.Equal(t, dir, bobfile.Project)
}

func TestProjectName(t *testing.T) {
// Create playground
dir, err := ioutil.TempDir("", "bob-test-aggregate-*")
assert.Nil(t, err)

defer os.RemoveAll(dir)

err = os.Chdir(dir)
assert.Nil(t, err)

testBob, err := Bob(WithDir(dir))
assert.Nil(t, err)

projectName := "example.com/test-user/test-project"

err = CreatePlayground(PlaygroundOptions{
Dir: dir,
ProjectName: projectName,
})
assert.Nil(t, err)

bobfile, err := testBob.Aggregate()
assert.Nil(t, err)

assert.Equal(t, projectName, bobfile.Project)
}

func TestInvalidProjectName(t *testing.T) {
// Create playground
dir, err := ioutil.TempDir("", "bob-test-aggregate-*")
assert.Nil(t, err)

defer os.RemoveAll(dir)

err = os.Chdir(dir)
assert.Nil(t, err)

testBob, err := Bob(WithDir(dir))
assert.Nil(t, err)

projectName := "@"

err = CreatePlayground(PlaygroundOptions{
Dir: dir,
ProjectName: projectName,
})
assert.Nil(t, err)

_, err = testBob.Aggregate()
assert.ErrorIs(t, err, bobfile.ErrInvalidProjectName)
}

// FIXME: As we don't refer to a child task by projectname but by path
// it seems to be save to allow duplicate projectnames.
//
// func TestDuplicateProjectNameSimple(t *testing.T) {
// // Create playground
// dir, err := ioutil.TempDir("", "bob-test-aggregate-*")
// assert.Nil(t, err)

// defer os.RemoveAll(dir)

// err = os.Chdir(dir)
// assert.Nil(t, err)

// testBob, err := Bob(WithDir(dir))
// assert.Nil(t, err)

// projectName := "duplicated-name"
// projectNameSecondLevel := "duplicated-name"
// projectNameThirdLevel := "third-level"

// err = CreatePlayground(
// PlaygroundOptions{
// Dir: dir,
// ProjectName: projectName,
// ProjectNameSecondLevel: projectNameSecondLevel,
// ProjectNameThirdLevel: projectNameThirdLevel,
// },
// )
// assert.Nil(t, err)

// _, err = testBob.Aggregate()
// assert.ErrorIs(t, err, ErrDuplicateProjectName)
// }

// FIXME: As we don't refer to a child task by projectname but by path
// it seems to be save to allow duplicate projectnames.
//
// func TestDuplicateProjectNameComplex(t *testing.T) {
// // Create playground
// dir, err := ioutil.TempDir("", "bob-test-aggregate-*")
// assert.Nil(t, err)

// defer os.RemoveAll(dir)

// err = os.Chdir(dir)
// assert.Nil(t, err)

// testBob, err := Bob(WithDir(dir))
// assert.Nil(t, err)

// projectName := "bob.build/benchkram/duplicated-name"
// projectNameSecondLevel := "bob.build/benchkram/duplicated-name"
// projectNameThirdLevel := "bob.build/benchkram/third-level"

// err = CreatePlayground(
// PlaygroundOptions{
// Dir: dir,
// ProjectName: projectName,
// ProjectNameSecondLevel: projectNameSecondLevel,
// ProjectNameThirdLevel: projectNameThirdLevel,
// },
// )
// assert.Nil(t, err)

// _, err = testBob.Aggregate()
// assert.ErrorIs(t, err, ErrDuplicateProjectName)
// }

func TestMultiLevelBobfileSameProjectName(t *testing.T) {
// Create playground
dir, err := ioutil.TempDir("", "bob-test-aggregate-*")
assert.Nil(t, err)

defer os.RemoveAll(dir)

err = os.Chdir(dir)
assert.Nil(t, err)

testBob, err := Bob(WithDir(dir))
assert.Nil(t, err)

projectName := "first-level"
projectNameSecondLevel := "second-level"
projectNameThirdLevel := "third-level"

err = CreatePlayground(
PlaygroundOptions{
Dir: dir,

ProjectName: projectName,
ProjectNameSecondLevel: projectNameSecondLevel,
ProjectNameThirdLevel: projectNameThirdLevel,
},
)
assert.Nil(t, err)

aggregate, err := testBob.Aggregate()
assert.Nil(t, err)

for _, bobFile := range aggregate.Bobfiles() {
assert.Equal(t, bobFile.Project, projectName)
}
}
3 changes: 1 addition & 2 deletions bob/artifact.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (b *B) ArtifactList(ctx context.Context) (description string, err error) {
for _, m := range metadataAll {
var match bool

if m.Project == task.Dir() && m.Taskname == task.Name() {
if m.Project == task.Project() && m.Taskname == task.Name() {
match = true
}

Expand All @@ -75,7 +75,6 @@ func (b *B) ArtifactList(ctx context.Context) (description string, err error) {
}

func (b *B) ArtifactInspect(artifactID string) (ai bobtask.ArtifactInfo, err error) {

artifact, err := b.local.GetArtifact(context.TODO(), artifactID)
if err != nil {
_, ok := err.(*fs.PathError)
Expand Down
21 changes: 18 additions & 3 deletions bob/bobfile/bobfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

"github.com/benchkram/errz"

"github.com/benchkram/bob/bob/bobfile/project"
"github.com/benchkram/bob/bob/global"
"github.com/benchkram/bob/bobrun"
"github.com/benchkram/bob/bobtask"
Expand All @@ -37,14 +38,22 @@ var (
ErrBobfileExists = fmt.Errorf("Bobfile exists")
ErrTaskDoesNotExist = fmt.Errorf("Task does not exist")
ErrDuplicateTaskName = fmt.Errorf("duplicate task name")
ErrInvalidProjectName = fmt.Errorf("invalid project name")
ErrSelfReference = fmt.Errorf("self reference")

ErrInvalidRunType = fmt.Errorf("Invalid run type")
)

type Bobfile struct {
// Version is optional, and can be used to
Version string `yaml:"version,omitempty"`

// Project uniquely identifies the current project (optional). If supplied,
// aggregation makes sure the project does not depend on another instance
// of itself. If not provided, then the project name is set after the path
// of its bobfile.
Project string `yaml:"project,omitempty"`

Variables VariableMap

// BTasks build tasks
Expand Down Expand Up @@ -122,9 +131,6 @@ func bobfileRead(dir string) (_ *Bobfile, err error) {
task.SetEnv([]string{})
task.SetRebuildStrategy(bobtask.RebuildOnChange)

// TODO: todoproject
task.SetProject(dir)

// initialize docker registry for task
task.SetDockerRegistryClient()

Expand Down Expand Up @@ -165,6 +171,15 @@ func (b *Bobfile) Validate() (err error) {
}
}

// validate project name if set
if b.Project != "" {
if !project.RestrictedProjectNamePattern.MatchString(b.Project) {
return usererror.Wrap(errors.WithMessage(ErrInvalidProjectName,
"project name should be in the form 'project' or 'registry.com/user/project'",
))
}
}

// use for duplicate names validation
names := map[string]bool{}

Expand Down
Loading

0 comments on commit 9b86866

Please sign in to comment.