// NewPatchTaskIdTable constructs a new TaskIdTable (map of [variant, task display name]->[task id]) func NewPatchTaskIdTable(proj *Project, v *version.Version, patchConfig TVPairSet) TaskIdTable { table := TaskIdTable{} processedVariants := map[string]bool{} for _, vt := range patchConfig { // don't hit the same variant more than once if _, ok := processedVariants[vt.Variant]; ok { continue } processedVariants[vt.Variant] = true // we must track the project's variants definitions as well, // so that we don't create Ids for variants that don't exist. projBV := proj.FindBuildVariant(vt.Variant) taskNamesForVariant := patchConfig.TaskNames(vt.Variant) for _, t := range projBV.Tasks { // create Ids for each task that can run on the variant and is requested by the patch. if util.SliceContains(taskNamesForVariant, t.Name) { taskId := util.CleanName( fmt.Sprintf("%v_%v_%v_%v_%v", proj.Identifier, projBV.Name, t.Name, v.Revision, v.CreateTime.Format(build.IdTimeLayout))) table[TVPair{vt.Variant, t.Name}] = taskId } } } return table }
// NewVersionFromRevision populates a new Version with metadata from a model.Revision. // Does not populate its config or store anything in the database. func NewVersionFromRevision(ref *model.ProjectRef, rev model.Revision) (*version.Version, error) { number, err := model.GetNewRevisionOrderNumber(ref.Identifier) if err != nil { return nil, err } v := &version.Version{ Author: rev.Author, AuthorEmail: rev.AuthorEmail, Branch: ref.Branch, CreateTime: rev.CreateTime, Id: util.CleanName(fmt.Sprintf("%v_%v", ref.String(), rev.Revision)), Identifier: ref.Identifier, Message: rev.RevisionMessage, Owner: ref.Owner, Project: ref.Identifier, RemotePath: ref.RemotePath, Repo: ref.Repo, RepoKind: ref.RepoKind, Requester: evergreen.RepotrackerVersionRequester, Revision: rev.Revision, Status: evergreen.VersionCreated, RevisionOrderNumber: number, } return v, nil }
// TaskIdTable builds a TaskIdTable for the given version and project func NewTaskIdTable(p *Project, v *version.Version) TaskIdTable { // init the variant map table := TaskIdTable{} for _, bv := range p.BuildVariants { for _, t := range bv.Tasks { // create a unique Id for each task taskId := util.CleanName( fmt.Sprintf("%v_%v_%v_%v_%v", p.Identifier, bv.Name, t.Name, v.Revision, v.CreateTime.Format(build.IdTimeLayout))) table[TVPair{bv.Name, t.Name}] = taskId } } return table }
// CreateBuildFromVersion creates a build given all of the necessary information // from the corresponding version and project and a list of tasks. func CreateBuildFromVersion(project *Project, v *version.Version, tt TaskIdTable, buildName string, activated bool, taskNames []string) (string, error) { evergreen.Logger.Logf(slogger.DEBUG, "Creating %v %v build, activated: %v", v.Requester, buildName, activated) // find the build variant for this project/build buildVariant := project.FindBuildVariant(buildName) if buildVariant == nil { return "", fmt.Errorf("could not find build %v in %v project file", buildName, project.Identifier) } // create a new build id buildId := util.CleanName( fmt.Sprintf("%v_%v_%v_%v", project.Identifier, buildName, v.Revision, v.CreateTime.Format(build.IdTimeLayout))) // create the build itself b := &build.Build{ Id: buildId, CreateTime: v.CreateTime, PushTime: v.CreateTime, Activated: activated, Project: project.Identifier, Revision: v.Revision, Status: evergreen.BuildCreated, BuildVariant: buildName, Version: v.Id, DisplayName: buildVariant.DisplayName, RevisionOrderNumber: v.RevisionOrderNumber, Requester: v.Requester, } // get a new build number for the build buildNumber, err := db.GetNewBuildVariantBuildNumber(buildName) if err != nil { return "", fmt.Errorf("could not get build number for build variant"+ " %v in %v project file", buildName, project.Identifier) } b.BuildNumber = strconv.FormatUint(buildNumber, 10) // create all of the necessary tasks for the build tasksForBuild, err := createTasksForBuild(project, buildVariant, b, v, tt, taskNames) if err != nil { return "", fmt.Errorf("error creating tasks for build %v: %v", b.Id, err) } // insert all of the build's tasks into the db for _, task := range tasksForBuild { if err := task.Insert(); err != nil { return "", fmt.Errorf("error inserting task %v: %v", task.Id, err) } } // create task caches for all of the tasks, and place them into the build tasks := make([]Task, 0, len(tasksForBuild)) for _, taskP := range tasksForBuild { tasks = append(tasks, *taskP) } b.Tasks = CreateTasksCache(tasks) // insert the build if err := b.Insert(); err != nil { return "", fmt.Errorf("error inserting build %v: %v", b.Id, err) } // success! return b.Id, nil }
// createTasksForBuild creates all of the necessary tasks for the build. Returns a // slice of all of the tasks created, as well as an error if any occurs. // The slice of tasks will be in the same order as the project's specified tasks // appear in the specified build variant. func createTasksForBuild(project *Project, buildVariant *BuildVariant, b *build.Build, v *version.Version, taskNames []string) ([]*Task, error) { // the list of tasks we should create. if tasks are passed in, then // use those, else use the default set tasksToCreate := []BuildVariantTask{} createAll := len(taskNames) == 0 for _, task := range buildVariant.Tasks { if task.Name == evergreen.PushStage && b.Requester == evergreen.PatchVersionRequester { continue } if createAll || util.SliceContains(taskNames, task.Name) { tasksToCreate = append(tasksToCreate, task) } } // create a map of display name -> task id for all of the tasks we are // going to create. we do this ahead of time so we can access it for the // dependency lists. taskIdsByDisplayName := map[string]string{} for _, task := range tasksToCreate { taskId := util.CleanName( fmt.Sprintf("%v_%v_%v_%v_%v", project.Identifier, b.BuildVariant, task.Name, v.Revision, v.CreateTime.Format(build.IdTimeLayout))) taskIdsByDisplayName[task.Name] = taskId } // if any tasks already exist in the build, add them to the map // so they can be used as dependencies for _, task := range b.Tasks { taskIdsByDisplayName[task.DisplayName] = task.Id } // create and insert all of the actual tasks tasks := make([]*Task, 0, len(tasksToCreate)) for _, task := range tasksToCreate { // get the task spec out of the project var taskSpec ProjectTask for _, projectTask := range project.Tasks { if projectTask.Name == task.Name { taskSpec = projectTask break } } // sanity check that the config isn't malformed if taskSpec.Name == "" { return nil, fmt.Errorf("config is malformed: variant '%v' runs "+ "task called '%v' but no such task exists for repo %v for "+ "version %v", buildVariant.Name, task.Name, project.Identifier, v.Id) } newTask := createOneTask(taskIdsByDisplayName[task.Name], task, project, buildVariant, b, v) // set the new task's dependencies if len(taskSpec.DependsOn) == 1 && taskSpec.DependsOn[0].Name == AllDependencies { // the task depends on all of the other tasks in the build newTask.DependsOn = make([]string, 0, len(tasksToCreate)-1) for _, dep := range tasksToCreate { if dep.Name != newTask.DisplayName { newTask.DependsOn = append(newTask.DependsOn, taskIdsByDisplayName[dep.Name]) } } } else { // the task has specific dependencies newTask.DependsOn = make([]string, 0, len(taskSpec.DependsOn)) for _, dep := range taskSpec.DependsOn { // only add as a dependency if the dependency is being created if taskIdsByDisplayName[dep.Name] != "" { newTask.DependsOn = append(newTask.DependsOn, taskIdsByDisplayName[dep.Name]) } } } // append the task to the list of the created tasks tasks = append(tasks, newTask) } // return all of the tasks created return tasks, nil }