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}) } }
// submitPatch creates the Patch document, adds the patched project config to it, // and saves the patches to GridFS to be retrieved func (as *APIServer) submitPatch(w http.ResponseWriter, r *http.Request) { dbUser := MustHaveUser(r) var apiRequest PatchAPIRequest var finalize bool if r.Header.Get("Content-Type") == "application/x-www-form-urlencoded" { patchContent := r.FormValue("patch") if patchContent == "" { as.LoggedError(w, r, http.StatusBadRequest, fmt.Errorf("Error: Patch must not be empty")) return } apiRequest = PatchAPIRequest{ ProjectId: r.FormValue("project"), ModuleName: r.FormValue("module"), Githash: r.FormValue("githash"), PatchContent: r.FormValue("patch"), BuildVariants: strings.Split(r.FormValue("buildvariants"), ","), Description: r.FormValue("desc"), } finalize = strings.ToLower(r.FormValue("finalize")) == "true" } else { data := struct { Description string `json:"desc"` Project string `json:"project"` Patch string `json:"patch"` Githash string `json:"githash"` Variants string `json:"buildvariants"` Tasks []string `json:"tasks"` Finalize bool `json:"finalize"` }{} if err := util.ReadJSONInto(r.Body, &data); err != nil { as.LoggedError(w, r, http.StatusBadRequest, err) return } if len(data.Patch) > patch.SizeLimit { as.LoggedError(w, r, http.StatusBadRequest, fmt.Errorf("Patch is too large.")) } finalize = data.Finalize apiRequest = PatchAPIRequest{ ProjectId: data.Project, ModuleName: r.FormValue("module"), Githash: data.Githash, PatchContent: data.Patch, BuildVariants: strings.Split(data.Variants, ","), Tasks: data.Tasks, Description: data.Description, } } project, patchDoc, err := apiRequest.CreatePatch( finalize, as.Settings.Credentials["github"], dbUser, &as.Settings) if err != nil { as.LoggedError(w, r, http.StatusBadRequest, fmt.Errorf("Invalid patch: %v", err)) return } //expand tasks and build variants and include dependencies if len(patchDoc.BuildVariants) == 1 && patchDoc.BuildVariants[0] == "all" { patchDoc.BuildVariants = []string{} for _, buildVariant := range project.BuildVariants { if buildVariant.Disabled { continue } patchDoc.BuildVariants = append(patchDoc.BuildVariants, buildVariant.Name) } } if len(patchDoc.Tasks) == 1 && patchDoc.Tasks[0] == "all" { patchDoc.Tasks = []string{} for _, t := range project.Tasks { if t.Patchable != nil && !(*t.Patchable) { continue } patchDoc.Tasks = append(patchDoc.Tasks, t.Name) } } var pairs []model.TVPair for _, v := range patchDoc.BuildVariants { for _, t := range patchDoc.Tasks { if project.FindTaskForVariant(t, v) != nil { pairs = append(pairs, model.TVPair{v, t}) } } } // update variant and tasks to include dependencies pairs = model.IncludePatchDependencies(project, pairs) patchDoc.SyncVariantsTasks(model.TVPairsToVariantTasks(pairs)) if err = patchDoc.Insert(); err != nil { as.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("error inserting patch: %v", err)) return } if finalize { if _, err = model.FinalizePatch(patchDoc, &as.Settings); err != nil { as.LoggedError(w, r, http.StatusInternalServerError, err) return } } as.WriteJSON(w, http.StatusCreated, PatchAPIResponse{Patch: patchDoc}) }