app/registry/change.go (206 lines of code) (raw):

package registry import ( "context" "ddm-admin-console/router" "ddm-admin-console/service/gerrit" "encoding/json" "errors" "fmt" "net/url" "os" "os/exec" "path" "path/filepath" "strings" goGerrit "github.com/andygrunwald/go-gerrit" "github.com/gin-gonic/gin" ) const ( currentRevision = "current" mergeList = "/MERGE_LIST" updateMRRetryCount = 5 ) func (a *App) abandonChange(ctx *gin.Context) (response router.Response, retErr error) { changeID := ctx.Param("change") if _, _, err := a.Gerrit.GoGerritClient().Changes.AbandonChange(changeID, &goGerrit.AbandonInput{ Message: fmt.Sprintf("Abandoned by %s [%s]", ctx.GetString(router.UserNameSessionKey), ctx.GetString(router.UserEmailSessionKey)), }); err != nil { return nil, fmt.Errorf("unable to abandon change, %w", err) } mr, err := a.updateMRStatus(ctx, changeID, "ABANDONED") if err != nil { return nil, fmt.Errorf("unable to change MR status, %w", err) } if err := ClearRepoFiles(mr.Spec.ProjectName, a.Cache); err != nil { return nil, fmt.Errorf("unable to clear cached files") } return router.MakeHTMLResponse(200, "registry/change-abandoned.html", gin.H{}), nil } func (a *App) submitChange(ctx *gin.Context) (response router.Response, retErr error) { changeID := ctx.Param("change") if err := a.Gerrit.ApproveAndSubmitChange(changeID, ctx.GetString(router.UserNameSessionKey), ctx.GetString(router.UserEmailSessionKey)); err != nil { return nil, fmt.Errorf("unable to approve change, %w", err) } if _, err := a.updateMRStatus(ctx, changeID, "MERGED"); err != nil { return nil, fmt.Errorf("unable to change MR status, %w", err) } return router.MakeHTMLResponse(200, "registry/change-submitted.html", gin.H{}), nil } func (a *App) updateMRStatus(ctx context.Context, changeID, status string) (*gerrit.GerritMergeRequest, error) { var mr *gerrit.GerritMergeRequest for i := 0; i < updateMRRetryCount; i++ { var err error mr, err = a.Gerrit.GetMergeRequestByChangeID(ctx, changeID) if err != nil { return nil, fmt.Errorf("unable to get MR, %w", err) } mr.Status.Value = status if err := a.Gerrit.UpdateMergeRequestStatus(ctx, mr); err != nil { if strings.Contains(err.Error(), "modified") { continue } return nil, fmt.Errorf("unable to update MR status, %w", err) } break } return mr, nil } func (a *App) viewChange(ctx *gin.Context) (response router.Response, retErr error) { changeID := ctx.Param("change") changeInfo, _, err := a.Gerrit.GoGerritClient().Changes.GetChangeDetail(changeID, &goGerrit.ChangeOptions{}) if err != nil { return nil, fmt.Errorf("unable to get gerrit change details, %w", err) } changes, err := a.getChangeContents(ctx, changeInfo) if err != nil { return nil, fmt.Errorf("unable to get changes, %w", err) } rspParams := gin.H{ "changes": changes, "change": changeInfo, "changeID": changeID, } templateArgs, err := json.Marshal(rspParams) if err != nil { return nil, errors.New("unable to encode template arguments") } return router.MakeHTMLResponse(200, "registry/change.html", gin.H{ "page": "registry", "templateArgs": string(templateArgs), }), nil } func (a *App) getChangeContents(ctx context.Context, changeInfo *goGerrit.ChangeInfo) (string, error) { files, _, err := a.Gerrit.GoGerritClient().Changes.ListFiles(changeInfo.ID, currentRevision, &goGerrit.FilesOptions{ Parent: 1, }) if err != nil { return "", fmt.Errorf("unable to get change files, %w", err) } changes := make([]string, 0, len(files)-1) for fileName := range files { if fileName == "/COMMIT_MSG" || fileName == mergeList { continue } changesContent, err := a.getChangeFileChanges(changeInfo.ID, fileName, changeInfo.Project) if err != nil { return "", fmt.Errorf("unable to get file changes, %w", err) } changes = append(changes, changesContent) } out := strings.Join(changes, "") bts, err := json.Marshal(out) if err != nil { return "", fmt.Errorf("unable to encode changes, %w", err) } return string(bts), nil } func (a *App) getChangeContentData(changeID, fileName, projectName string) (string, error) { content, newHttpRsp, err := a.Gerrit.GoGerritClient().Changes.GetContent(changeID, currentRevision, fileName) if err != nil && newHttpRsp != nil && newHttpRsp.StatusCode != 404 { return "", errors.New("unable to get file content") } if newHttpRsp == nil || content == nil { return "", errors.New("empty response") } return *content, nil } // TODO: split function func (a *App) getChangeFileChanges(changeID, fileName, projectName string) (string, error) { commitInfo, _, err := a.Gerrit.GoGerritClient().Changes.GetCommit(changeID, currentRevision, &goGerrit.CommitOptions{}) if err != nil { return "", fmt.Errorf("unable to get change commit, %w", err) } if len(commitInfo.Parents) == 0 { return "", errors.New("no parent commit for change found") } originalContent, originalHttpRsp, err := a.Gerrit.GoGerritClient().Projects.GetCommitContent(projectName, commitInfo.Parents[0].Commit, url.PathEscape(fileName)) if err != nil && originalHttpRsp != nil && originalHttpRsp.StatusCode != 404 { return "", fmt.Errorf("unable to get file content, %w", err) } if originalHttpRsp == nil { return "", errors.New("empty http response") } originalFilePath := path.Join(a.Config.TempFolder, "original", fileName) if originalHttpRsp.StatusCode != 404 { originalFolder := filepath.Dir(originalFilePath) if err := os.MkdirAll(originalFolder, 0777); err != nil { return "", fmt.Errorf("unable to create folder, %w", err) } originalFile, err := os.Create(originalFilePath) if err != nil { return "", fmt.Errorf("unable to creaete file, %w", err) } if _, err := originalFile.WriteString(originalContent); err != nil { return "", fmt.Errorf("unable to write string, %w", err) } if err := originalFile.Close(); err != nil { return "", fmt.Errorf("unable to close file, %w", err) } defer os.RemoveAll(originalFilePath) } content, newHttpRsp, err := a.Gerrit.GoGerritClient().Changes.GetContent(changeID, currentRevision, fileName) if err != nil && newHttpRsp != nil && newHttpRsp.StatusCode != 404 { return "", fmt.Errorf("unable to get file content, %w", err) } if newHttpRsp == nil { return "", errors.New("empty response") } newFilePath := path.Join(a.Config.TempFolder, "new", fileName) if newHttpRsp.StatusCode != 404 && content != nil { newFolder := filepath.Dir(newFilePath) if err := os.MkdirAll(newFolder, 0777); err != nil { return "", fmt.Errorf("unable to create folder, %w", err) } newFile, err := os.Create(newFilePath) if err != nil { return "", fmt.Errorf("unable to create file, %w", err) } if _, err := newFile.WriteString(*content); err != nil { return "", fmt.Errorf("unable to write string, %w", err) } if err := newFile.Close(); err != nil { return "", fmt.Errorf("unable to close file, %w", err) } defer os.RemoveAll(newFilePath) } return createDiff(fileName, originalFilePath, originalHttpRsp.StatusCode == 404, newFilePath, newHttpRsp.StatusCode == 404), nil } func createDiff(fileName, originalFilePath string, newFileAdded bool, newFilePath string, fileDeleted bool) string { var outDiff string if newFileAdded { out, _ := exec.Command("diff", "-u", "/dev/null", newFilePath).CombinedOutput() //outDiff = string(out) + "new file mode 100644\n" outDiff = fmt.Sprintf("diff --git a/%s b/%s\n%s", fileName, fileName, string(out)) //outDiff = string(out) } else if fileDeleted { out, _ := exec.Command("diff", "-u", originalFilePath, "/dev/null").CombinedOutput() //outDiff = string(out) + "deleted file mode 100644\n" //outDiff = string(out) outDiff = fmt.Sprintf("diff --git a/%s b/%s\n%s", fileName, fileName, string(out)) } else { out, _ := exec.Command("diff", "-u", originalFilePath, newFilePath).CombinedOutput() //outDiff = string(out) outDiff = fmt.Sprintf("diff --git a/%s b/%s\n%s", fileName, fileName, string(out)) //outDiff = string(out) } outDiff = strings.ReplaceAll(outDiff, newFilePath, fileName) outDiff = strings.ReplaceAll(outDiff, originalFilePath, fileName) return outDiff }