// populatePatch loads a patch into the project context, using patchId if provided. // If patchId is blank, will try to infer the patch ID from the version already loaded // into context, if available. func (pc *projectContext) populatePatch(patchId string) error { var err error if len(patchId) > 0 { // The patch is explicitly identified in the URL, so fetch it if !patch.IsValidId(patchId) { return fmt.Errorf("patch id '%v' is not an object id", patchId) } pc.Patch, err = patch.FindOne(patch.ById(patch.NewId(patchId)).Project(patch.ExcludePatchDiff)) } else if pc.Version != nil { // patch isn't in URL but the version in context has one, get it pc.Patch, err = patch.FindOne(patch.ByVersion(pc.Version.Id).Project(patch.ExcludePatchDiff)) } if err != nil { return err } // If there's a finalized patch loaded into context but not a version, load the version // associated with the patch as the context's version. if pc.Version == nil && pc.Patch != nil && pc.Patch.Version != "" { pc.Version, err = version.FindOne(version.ById(pc.Patch.Version)) if err != nil { return err } } return nil }
func (uis *UIServer) rawDiffPage(w http.ResponseWriter, r *http.Request) { projCtx := MustHaveProjectContext(r) if projCtx.Patch == nil { http.Error(w, "patch not found", http.StatusNotFound) return } fullPatch, err := patch.FindOne(patch.ById(projCtx.Patch.Id)) if err != nil { http.Error(w, fmt.Sprintf("error loading patch: %v", err.Error), http.StatusInternalServerError) return } fullPatch.FetchPatchFiles() patchNum, err := strconv.Atoi(r.FormValue("patch_number")) if err != nil { http.Error(w, fmt.Sprintf("error getting patch number: %v", err.Error), http.StatusInternalServerError) return } if patchNum < 0 || patchNum >= len(fullPatch.Patches) { http.Error(w, "patch number out of range", http.StatusInternalServerError) return } diff := fullPatch.Patches[patchNum].PatchSet.Patch w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusOK) w.Write([]byte(diff)) }
func (uis *UIServer) patchPage(w http.ResponseWriter, r *http.Request) { projCtx := MustHaveProjectContext(r) if projCtx.Patch == nil { http.Error(w, "not found", http.StatusNotFound) return } currentUser := MustHaveUser(r) var versionAsUI *uiVersion if projCtx.Version != nil { // Patch is already finalized versionAsUI = &uiVersion{ Version: *projCtx.Version, RepoOwner: projCtx.ProjectRef.Owner, Repo: projCtx.ProjectRef.Repo, } } // get the new patch document with the patched configuration var err error projCtx.Patch, err = patch.FindOne(patch.ById(projCtx.Patch.Id)) if err != nil { http.Error(w, fmt.Sprintf("error loading patch: %v", err), http.StatusInternalServerError) return } // Unmarshal the patch's project config so that it is always up to date with the configuration file in the project project := &model.Project{} if err := yaml.Unmarshal([]byte(projCtx.Patch.PatchedConfig), project); err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error unmarshaling project config: %v", err)) } projCtx.Project = project // retrieve tasks and variant mappings' names variantMappings := make(map[string]model.BuildVariant) for _, variant := range projCtx.Project.BuildVariants { variantMappings[variant.Name] = variant } tasksList := []interface{}{} for _, task := range projCtx.Project.Tasks { // add a task name to the list if it's patchable if !(task.Patchable != nil && *task.Patchable == false) { tasksList = append(tasksList, struct{ Name string }{task.Name}) } } uis.WriteHTML(w, http.StatusOK, struct { ProjectData projectContext User *user.DBUser Version *uiVersion Variants map[string]model.BuildVariant Tasks []interface{} CanEdit bool }{projCtx, currentUser, versionAsUI, variantMappings, tasksList, uis.canEditPatch(currentUser, projCtx.Patch)}, "base", "patch_version.html", "base_angular.html", "menu.html") }
// servePatch is the API hook for returning patch data as json func servePatch(w http.ResponseWriter, r *http.Request) { task := plugin.GetTask(r) patch, err := patch.FindOne(patch.ByVersion(task.Version)) if err != nil { msg := fmt.Sprintf("error fetching patch for task %v from db: %v", task.Id, err) evergreen.Logger.Logf(slogger.ERROR, msg) http.Error(w, msg, http.StatusInternalServerError) return } if patch == nil { msg := fmt.Sprintf("no patch found for task %v", task.Id) evergreen.Logger.Errorf(slogger.ERROR, msg) http.Error(w, msg, http.StatusNotFound) return } plugin.WriteJSON(w, http.StatusOK, patch) }
func (uis *UIServer) diffPage(w http.ResponseWriter, r *http.Request) { projCtx := MustHaveProjectContext(r) if projCtx.Patch == nil { http.Error(w, "patch not found", http.StatusNotFound) return } // We have to reload the patch outside of the project context, // since the raw diff is excluded by default. This redundancy is // worth the time savings this behavior offers other pages. fullPatch, err := patch.FindOne(patch.ById(projCtx.Patch.Id)) if err != nil { http.Error(w, fmt.Sprintf("error loading patch: %v", err.Error), http.StatusInternalServerError) return } uis.WriteHTML(w, http.StatusOK, fullPatch, "base", "diff.html") }
// construct the change information // struct from a given version struct func constructChangeInfo(v *version.Version, notification *NotificationKey) (changeInfo *ChangeInfo) { changeInfo = &ChangeInfo{} switch notification.NotificationRequester { case evergreen.RepotrackerVersionRequester: changeInfo.Project = v.Identifier changeInfo.Author = v.Author changeInfo.Message = v.Message changeInfo.Revision = v.Revision changeInfo.Email = v.AuthorEmail case evergreen.PatchVersionRequester: // get the author and description from the patch request patch, err := patch.FindOne(patch.ByVersion(v.Id)) if err != nil { evergreen.Logger.Errorf(slogger.ERROR, "Error finding patch for version %v: %v", v.Id, err) return } if patch == nil { evergreen.Logger.Errorf(slogger.ERROR, "%v notification was unable to locate patch with version: %v", notification, v.Id) return } // get the display name and email for this user dbUser, err := user.FindOne(user.ById(patch.Author)) if err != nil { evergreen.Logger.Errorf(slogger.ERROR, "Error finding user %v: %v", patch.Author, err) changeInfo.Author = patch.Author changeInfo.Email = patch.Author } else if dbUser == nil { evergreen.Logger.Errorf(slogger.ERROR, "User %v not found", patch.Author) changeInfo.Author = patch.Author changeInfo.Email = patch.Author } else { changeInfo.Email = dbUser.Email() changeInfo.Author = dbUser.DisplayName() } changeInfo.Project = patch.Project changeInfo.Message = patch.Description changeInfo.Revision = patch.Id.Hex() } return }
func (uis *UIServer) fileDiffPage(w http.ResponseWriter, r *http.Request) { projCtx := MustHaveProjectContext(r) if projCtx.Patch == nil { http.Error(w, "patch not found", http.StatusNotFound) return } fullPatch, err := patch.FindOne(patch.ById(projCtx.Patch.Id)) if err != nil { http.Error(w, fmt.Sprintf("error loading patch: %v", err.Error), http.StatusInternalServerError) return } uis.WriteHTML(w, http.StatusOK, struct { Data patch.Patch FileName string PatchNumber string }{*fullPatch, r.FormValue("file_name"), r.FormValue("patch_number")}, "base", "file_diff.html") }
// Get the patch with the specified request it func getPatchFromRequest(r *http.Request) (*patch.Patch, error) { // get id and secret from the request. vars := mux.Vars(r) patchIdStr := vars["patchId"] if len(patchIdStr) == 0 { return nil, fmt.Errorf("no patch id supplied") } if !patch.IsValidId(patchIdStr) { return nil, fmt.Errorf("patch id '%v' is not valid object id", patchIdStr) } // find the patch existingPatch, err := patch.FindOne(patch.ById(patch.NewId(patchIdStr))) if err != nil { return nil, err } if existingPatch == nil { return nil, fmt.Errorf("no existing request with id: %v", patchIdStr) } return existingPatch, nil }
// loadAlertContext fetches details from the database for all documents that are associated with the // AlertRequest. For example, it populates the task/build/version/project using the // task/build/version/project ids in the alert request document. func (qp *QueueProcessor) loadAlertContext(a *alert.AlertRequest) (*AlertContext, error) { aCtx := &AlertContext{AlertRequest: a} aCtx.Settings = qp.config taskId, projectId, buildId, versionId := a.TaskId, a.ProjectId, a.BuildId, a.VersionId patchId := a.PatchId var err error if len(a.HostId) > 0 { aCtx.Host, err = host.FindOne(host.ById(a.HostId)) if err != nil { return nil, err } } // Fetch task if there's a task ID present; if we find one, populate build/version IDs from it if len(taskId) > 0 { aCtx.Task, err = model.FindTask(taskId) if err != nil { return nil, err } if aCtx.Task != nil && aCtx.Task.Execution != a.Execution { oldTaskId := fmt.Sprintf("%s_%v", taskId, a.Execution) aCtx.Task, err = model.FindOneOldTask(bson.M{"_id": oldTaskId}, db.NoProjection, db.NoSort) if err != nil { return nil, err } } if aCtx.Task != nil { // override build and version ID with the ones this task belongs to buildId = aCtx.Task.BuildId versionId = aCtx.Task.Version projectId = aCtx.Task.Project aCtx.FailedTests = []model.TestResult{} for _, test := range aCtx.Task.TestResults { if test.Status == "fail" { aCtx.FailedTests = append(aCtx.FailedTests, test) } } } } // Fetch build if there's a build ID present; if we find one, populate version ID from it if len(buildId) > 0 { aCtx.Build, err = build.FindOne(build.ById(buildId)) if err != nil { return nil, err } if aCtx.Build != nil { versionId = aCtx.Build.Version projectId = aCtx.Build.Project } } if len(versionId) > 0 { aCtx.Version, err = version.FindOne(version.ById(versionId)) if err != nil { return nil, err } if aCtx.Version != nil { projectId = aCtx.Version.Identifier } } if len(patchId) > 0 { if !patch.IsValidId(patchId) { return nil, fmt.Errorf("patch id '%v' is not an object id", patchId) } aCtx.Patch, err = patch.FindOne(patch.ById(patch.NewId(patchId)).Project(patch.ExcludePatchDiff)) } else if aCtx.Version != nil { // patch isn't in URL but the version in context has one, get it aCtx.Patch, err = patch.FindOne(patch.ByVersion(aCtx.Version.Id).Project(patch.ExcludePatchDiff)) } // If there's a finalized patch loaded into context but not a version, load the version // associated with the patch as the context's version. if aCtx.Version == nil && aCtx.Patch != nil && aCtx.Patch.Version != "" { aCtx.Version, err = version.FindOne(version.ById(aCtx.Patch.Version).WithoutFields(version.ConfigKey)) if err != nil { return nil, err } } if len(projectId) > 0 { aCtx.ProjectRef, err = qp.findProject(projectId) if err != nil { return nil, err } } return aCtx, nil }
func (uis *UIServer) schedulePatch(w http.ResponseWriter, r *http.Request) { projCtx := MustHaveProjectContext(r) if projCtx.Patch == nil { http.Error(w, "patch not found", http.StatusNotFound) return } // grab patch again, as the diff was excluded var err error projCtx.Patch, err = patch.FindOne(patch.ById(projCtx.Patch.Id)) if err != nil { http.Error(w, fmt.Sprintf("error loading patch: %v", err), http.StatusInternalServerError) return } patchUpdateReq := struct { Variants []string `json:"variants"` Tasks []string `json:"tasks"` Description string `json:"description"` }{} err = util.ReadJSONInto(r.Body, &patchUpdateReq) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } if projCtx.Patch.Version != "" { // This patch has already been finalized, just add the new builds and tasks if projCtx.Version == nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Couldn't find patch for id %v", projCtx.Patch.Version)) return } // First add new tasks to existing builds, if necessary if len(patchUpdateReq.Tasks) > 0 { err = model.AddNewTasksForPatch(projCtx.Patch, projCtx.Version, patchUpdateReq.Tasks) if err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error creating new tasks: `%v` for version `%v`", err, projCtx.Version.Id)) return } } if len(patchUpdateReq.Variants) > 0 { _, err := model.AddNewBuildsForPatch(projCtx.Patch, projCtx.Version, patchUpdateReq.Variants) if err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error creating new builds: `%v` for version `%v`", err, projCtx.Version.Id)) return } } PushFlash(uis.CookieStore, r, w, NewSuccessFlash("Builds and tasks successfully added to patch.")) uis.WriteJSON(w, http.StatusOK, struct { VersionId string `json:"version"` }{projCtx.Version.Id}) } else { err = projCtx.Patch.SetVariantsAndTasks(patchUpdateReq.Variants, patchUpdateReq.Tasks) if err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error setting patch variants and tasks: %v", err)) return } if err = projCtx.Patch.SetDescription(patchUpdateReq.Description); err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error setting description: %v", err)) return } // Unmarshal the project config and set it in the project context project := model.Project{} if err := yaml.Unmarshal([]byte(projCtx.Patch.PatchedConfig), &project); err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error unmarshaling project config: %v", err)) } projCtx.Project = &project ver, err := model.FinalizePatch(projCtx.Patch, &uis.Settings) if err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error finalizing patch: %v", err)) return } PushFlash(uis.CookieStore, r, w, NewSuccessFlash("Patch builds are scheduled.")) uis.WriteJSON(w, http.StatusOK, struct { VersionId string `json:"version"` }{ver.Id}) } }
func (task *Task) FetchPatch() (*patch.Patch, error) { // find the patch associated with this version return patch.FindOne(patch.ByVersion(task.Version)) }
func (uis *UIServer) allTaskQueues(w http.ResponseWriter, r *http.Request) { projCtx := MustHaveProjectContext(r) taskQueues, err := model.FindAllTaskQueues() if err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error finding task queues: %v", err)) return } // cached map of version id to relevant patch cachedPatches := map[string]*patch.Patch{} // convert the task queues to the ui versions uiTaskQueues := []uiTaskQueue{} for _, tQ := range taskQueues { asUI := uiTaskQueue{ Distro: tQ.Distro, Queue: []uiTaskQueueItem{}, } if len(tQ.Queue) == 0 { uiTaskQueues = append(uiTaskQueues, asUI) continue } // convert the individual task queue items taskIds := []string{} for _, item := range tQ.Queue { // cache the ids, for fetching the tasks from the db taskIds = append(taskIds, item.Id) queueItemAsUI := uiTaskQueueItem{ Id: item.Id, DisplayName: item.DisplayName, BuildVariant: item.BuildVariant, RevisionOrderNumber: item.RevisionOrderNumber, Requester: item.Requester, Revision: item.Revision, Project: item.Project, } asUI.Queue = append(asUI.Queue, queueItemAsUI) } // find all the relevant tasks tasks, err := task.Find(task.ByIds(taskIds).WithFields(task.VersionKey, task.BuildIdKey)) if err != nil { msg := fmt.Sprintf("Error finding tasks: %v", err) evergreen.Logger.Errorf(slogger.ERROR, msg) http.Error(w, msg, http.StatusInternalServerError) return } // store all of the version and build ids in the relevant task queue // items for _, task := range tasks { // this sucks, but it's because we're not guaranteed the order out // of the db for idx, queueItemAsUI := range asUI.Queue { if queueItemAsUI.Id == task.Id { queueItemAsUI.Version = task.Version queueItemAsUI.Build = task.BuildId asUI.Queue[idx] = queueItemAsUI } } } // add all of the necessary patch info into the relevant task queue // items for idx, queueItemAsUI := range asUI.Queue { if queueItemAsUI.Requester == evergreen.PatchVersionRequester { // fetch the patch, if necessary var p *patch.Patch var ok bool if p, ok = cachedPatches[queueItemAsUI.Version]; ok { queueItemAsUI.User = p.Author asUI.Queue[idx] = queueItemAsUI } else { p, err = patch.FindOne( patch.ByVersion(queueItemAsUI.Version).WithFields(patch.AuthorKey), ) if err != nil { msg := fmt.Sprintf("Error finding patch: %v", err) evergreen.Logger.Errorf(slogger.ERROR, msg) http.Error(w, msg, http.StatusInternalServerError) return } if p == nil { msg := fmt.Sprintf("Couldn't find patch for version %v", queueItemAsUI.Version) evergreen.Logger.Errorf(slogger.ERROR, msg) http.Error(w, msg, http.StatusInternalServerError) return } cachedPatches[queueItemAsUI.Version] = p } queueItemAsUI.User = p.Author asUI.Queue[idx] = queueItemAsUI } } uiTaskQueues = append(uiTaskQueues, asUI) } // add other useful statistics to view alongside queue idleHosts, err := host.Find(host.IsIdle) if err != nil { msg := fmt.Sprintf("Error finding idle hosts: %v", err) evergreen.Logger.Errorf(slogger.ERROR, msg) http.Error(w, msg, http.StatusInternalServerError) return } activeHosts, err := host.Find(host.IsLive) if err != nil { msg := fmt.Sprintf("Error finding active hosts: %v", err) evergreen.Logger.Errorf(slogger.ERROR, msg) http.Error(w, msg, http.StatusInternalServerError) return } idleStaticHostsCount := 0 for _, host := range idleHosts { if host.Provider == evergreen.HostTypeStatic { idleStaticHostsCount++ } } activeStaticHostsCount := 0 for _, host := range activeHosts { if host.Provider == evergreen.HostTypeStatic { activeStaticHostsCount++ } } hostStats := uiHostStatistics{ ActiveHosts: len(activeHosts), ActiveStaticHosts: activeStaticHostsCount, IdleHosts: len(idleHosts), IdleStaticHosts: idleStaticHostsCount, } uis.WriteHTML(w, http.StatusOK, struct { ProjectData projectContext User *user.DBUser Flashes []interface{} Data uiResourceInfo }{projCtx, GetUser(r), []interface{}{}, uiResourceInfo{uiTaskQueues, hostStats}}, "base", "task_queues.html", "base_angular.html", "menu.html") }
func (uis *UIServer) schedulePatch(w http.ResponseWriter, r *http.Request) { projCtx := MustHaveProjectContext(r) if projCtx.Patch == nil { http.Error(w, "patch not found", http.StatusNotFound) return } curUser := GetUser(r) if !uis.canEditPatch(curUser, projCtx.Patch) { http.Error(w, "Not authorized to schedule patch", http.StatusUnauthorized) return } // grab patch again, as the diff was excluded var err error projCtx.Patch, err = patch.FindOne(patch.ById(projCtx.Patch.Id)) if err != nil { http.Error(w, fmt.Sprintf("error loading patch: %v", err), http.StatusInternalServerError) return } // Unmarshal the project config and set it in the project context project := &model.Project{} if err := yaml.Unmarshal([]byte(projCtx.Patch.PatchedConfig), project); err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error unmarshaling project config: %v", err)) } projCtx.Project = project patchUpdateReq := patchVariantsTasksRequest{} err = util.ReadJSONInto(r.Body, &patchUpdateReq) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } var pairs []model.TVPair if len(patchUpdateReq.VariantsTasks) > 0 { pairs = model.VariantTasksToTVPairs(patchUpdateReq.VariantsTasks) } else { for _, v := range patchUpdateReq.Variants { for _, t := range patchUpdateReq.Tasks { if project.FindTaskForVariant(t, v) != nil { pairs = append(pairs, model.TVPair{v, t}) } } } } pairs = model.IncludePatchDependencies(projCtx.Project, pairs) if err = model.ValidateTVPairs(projCtx.Project, pairs); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } // update the description for both reconfigured and new patches if err = projCtx.Patch.SetDescription(patchUpdateReq.Description); err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error setting description: %v", err)) return } // update the description for both reconfigured and new patches if err = projCtx.Patch.SetVariantsTasks(model.TVPairsToVariantTasks(pairs)); err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error setting description: %v", err)) return } if projCtx.Patch.Version != "" { projCtx.Patch.Activated = true // This patch has already been finalized, just add the new builds and tasks if projCtx.Version == nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Couldn't find patch for id %v", projCtx.Patch.Version)) return } // First add new tasks to existing builds, if necessary err = model.AddNewTasksForPatch(projCtx.Patch, projCtx.Version, projCtx.Project, pairs) if err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error creating new tasks: `%v` for version `%v`", err, projCtx.Version.Id)) return } err := model.AddNewBuildsForPatch(projCtx.Patch, projCtx.Version, projCtx.Project, pairs) if err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error creating new builds: `%v` for version `%v`", err, projCtx.Version.Id)) return } PushFlash(uis.CookieStore, r, w, NewSuccessFlash("Builds and tasks successfully added to patch.")) uis.WriteJSON(w, http.StatusOK, struct { VersionId string `json:"version"` }{projCtx.Version.Id}) } else { projCtx.Patch.Activated = true err = projCtx.Patch.SetVariantsTasks(model.TVPairsToVariantTasks(pairs)) if err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error setting patch variants and tasks: %v", err)) return } ver, err := model.FinalizePatch(projCtx.Patch, &uis.Settings) if err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error finalizing patch: %v", err)) return } PushFlash(uis.CookieStore, r, w, NewSuccessFlash("Patch builds are scheduled.")) uis.WriteJSON(w, http.StatusOK, struct { VersionId string `json:"version"` }{ver.Id}) } }
func (uis *UIServer) schedulePatch(w http.ResponseWriter, r *http.Request) { projCtx := MustHaveProjectContext(r) if projCtx.Patch == nil { http.Error(w, "patch not found", http.StatusNotFound) return } // grab patch again, as the diff was excluded var err error projCtx.Patch, err = patch.FindOne(patch.ById(projCtx.Patch.Id)) if err != nil { http.Error(w, fmt.Sprintf("error loading patch: %v", err), http.StatusInternalServerError) return } // Unmarshal the project config and set it in the project context project := &model.Project{} if err := yaml.Unmarshal([]byte(projCtx.Patch.PatchedConfig), project); err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error unmarshaling project config: %v", err)) } projCtx.Project = project patchUpdateReq := struct { Variants []string `json:"variants"` Tasks []string `json:"tasks"` Description string `json:"description"` }{} err = util.ReadJSONInto(r.Body, &patchUpdateReq) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } // Add all dependencies to patchUpdateReq.Tasks and add their variants to patchUpdateReq.Variants // Construct a set of variants to include in patchUpdateReq.Variants updateReqVariants := make(map[string]bool) for _, variant := range patchUpdateReq.Variants { updateReqVariants[variant] = true } // Construct a set of tasks to include in patchUpdateReq.Tasks // Add all dependencies, and add their variants to updateReqVariants updateReqTasks := make(map[string]bool) for _, v := range patchUpdateReq.Variants { for _, t := range projCtx.Project.FindTasksForVariant(v) { for _, task := range patchUpdateReq.Tasks { if t == task { deps, variants, err := getDeps(task, v, projCtx.Project) if err != nil { if err == TaskNotPatchableError { continue } else { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error getting dependencies for task: %v", err)) return } } updateReqTasks[task] = true for _, dep := range deps { updateReqTasks[dep] = true } for _, variant := range variants { updateReqVariants[variant] = true } } } } } // Reset patchUpdateReq.Tasks and patchUpdateReq.Variants patchUpdateReq.Tasks = make([]string, 0, len(updateReqTasks)) for task := range updateReqTasks { patchUpdateReq.Tasks = append(patchUpdateReq.Tasks, task) } patchUpdateReq.Variants = make([]string, 0, len(updateReqVariants)) for variant := range updateReqVariants { patchUpdateReq.Variants = append(patchUpdateReq.Variants, variant) } if projCtx.Patch.Version != "" { // This patch has already been finalized, just add the new builds and tasks if projCtx.Version == nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Couldn't find patch for id %v", projCtx.Patch.Version)) return } // First add new tasks to existing builds, if necessary if len(patchUpdateReq.Tasks) > 0 { err = model.AddNewTasksForPatch(projCtx.Patch, projCtx.Version, projCtx.Project, patchUpdateReq.Tasks) if err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error creating new tasks: `%v` for version `%v`", err, projCtx.Version.Id)) return } } if len(patchUpdateReq.Variants) > 0 { _, err := model.AddNewBuildsForPatch(projCtx.Patch, projCtx.Version, projCtx.Project, patchUpdateReq.Variants) if err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error creating new builds: `%v` for version `%v`", err, projCtx.Version.Id)) return } } PushFlash(uis.CookieStore, r, w, NewSuccessFlash("Builds and tasks successfully added to patch.")) uis.WriteJSON(w, http.StatusOK, struct { VersionId string `json:"version"` }{projCtx.Version.Id}) } else { err = projCtx.Patch.SetVariantsAndTasks(patchUpdateReq.Variants, patchUpdateReq.Tasks) if err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error setting patch variants and tasks: %v", err)) return } if err = projCtx.Patch.SetDescription(patchUpdateReq.Description); err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error setting description: %v", err)) return } ver, err := model.FinalizePatch(projCtx.Patch, &uis.Settings) if err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error finalizing patch: %v", err)) return } PushFlash(uis.CookieStore, r, w, NewSuccessFlash("Patch builds are scheduled.")) uis.WriteJSON(w, http.StatusOK, struct { VersionId string `json:"version"` }{ver.Id}) } }