// WriteNewReports takes a list of CI reports read from GitHub, and writes to the repo any that are new. // // The passed in logChan variable is used as our intermediary for logging, and allows us to // use the same logic for logging messages in either our CLI or our App Engine apps, even though // the two have different logging frameworks. func WriteNewReports(reportsMap map[string][]ci.Report, repo repository.Repo, logChan chan<- string) error { for commit, commitReports := range reportsMap { existingReports := ci.ParseAllValid(repo.GetNotes(ci.Ref, commit)) for _, report := range commitReports { bytes, err := json.Marshal(report) note := repository.Note(bytes) if err != nil { return err } missing := true for _, existing := range existingReports { if existing == report { missing = false } } if missing { logChan <- fmt.Sprintf("Found a new report for %.12s: %q", commit, string(bytes)) if err := repo.AppendNote(ci.Ref, commit, note); err != nil { return err } } } } return nil }
// WriteNewReviews takes a list of reviews read from GitHub, and writes to the repo any review // data that has not already been written to it. // // The passed in logChan variable is used as our intermediary for logging, and allows us to // use the same logic for logging messages in either our CLI or our App Engine apps, even though // the two have different logging frameworks. func WriteNewReviews(reviews []review.Review, repo repository.Repo, logChan chan<- string) error { existingReviews := review.ListAll(repo) for _, r := range reviews { requestNote, err := r.Request.Write() if err != nil { return err } if err != nil { return err } alreadyPresent := false if existing := findMatchingExistingReview(r, existingReviews); existing != nil { alreadyPresent = RequestsOverlap(existing.Request, r.Request) r.Revision = existing.Revision } if !alreadyPresent { requestJSON, err := r.GetJSON() if err != nil { return err } logChan <- fmt.Sprintf("Found a new review for %.12s:\n%s\n", r.Revision, requestJSON) if err := repo.AppendNote(request.Ref, r.Revision, requestNote); err != nil { return err } } if err := WriteNewComments(r, repo, logChan); err != nil { return err } } return nil }
// WriteNewComments takes a list of review comments read from GitHub, and writes to the repo any that are new. // // The passed in logChan variable is used as our intermediary for logging, and allows us to // use the same logic for logging messages in either our CLI or our App Engine apps, even though // the two have different logging frameworks. func WriteNewComments(r review.Review, repo repository.Repo, logChan chan<- string) error { existingComments := comment.ParseAllValid(repo.GetNotes(comment.Ref, r.Revision)) for _, commentThread := range r.Comments { commentNote, err := commentThread.Comment.Write() if err != nil { return err } missing := true for _, existing := range existingComments { if CommentsOverlap(existing, commentThread.Comment) { missing = false } } if missing { logChan <- fmt.Sprintf("Found a new comment: %q", string(commentNote)) if err := repo.AppendNote(comment.Ref, r.Revision, commentNote); err != nil { return err } } } return nil }
// Create a new code review request. // // The "args" parameter is all of the command line arguments that followed the subcommand. func requestReview(repo repository.Repo, args []string) error { requestFlagSet.Parse(args) if !*requestAllowUncommitted { // Requesting a code review with uncommited local changes is usually a mistake, so // we want to report that to the user instead of creating the request. if repo.HasUncommittedChanges() { return errors.New("You have uncommitted or untracked files. Use --allow-uncommitted to ignore those.") } } r := buildRequestFromFlags(repo.GetUserEmail()) if r.ReviewRef == "HEAD" { r.ReviewRef = repo.GetHeadRef() } repo.VerifyGitRefOrDie(r.TargetRef) repo.VerifyGitRefOrDie(r.ReviewRef) r.BaseCommit = repo.GetCommitHash(r.TargetRef) reviewCommits := repo.ListCommitsBetween(r.TargetRef, r.ReviewRef) if reviewCommits == nil { return errors.New("There are no commits included in the review request") } if r.Description == "" { r.Description = repo.GetCommitMessage(reviewCommits[0]) } note, err := r.Write() if err != nil { return err } repo.AppendNote(request.Ref, reviewCommits[0], note) if !*requestQuiet { fmt.Printf(requestSummaryTemplate, reviewCommits[0], r.TargetRef, r.ReviewRef, r.Description) } return nil }
// Create a new code review request. // // The "args" parameter is all of the command line arguments that followed the subcommand. func requestReview(repo repository.Repo, args []string) error { requestFlagSet.Parse(args) args = requestFlagSet.Args() if !*requestAllowUncommitted { // Requesting a code review with uncommited local changes is usually a mistake, so // we want to report that to the user instead of creating the request. hasUncommitted, err := repo.HasUncommittedChanges() if err != nil { return err } if hasUncommitted { return errors.New("You have uncommitted or untracked files. Use --allow-uncommitted to ignore those.") } } userEmail, err := repo.GetUserEmail() if err != nil { return err } r, err := buildRequestFromFlags(userEmail) if err != nil { return err } if r.ReviewRef == "HEAD" { headRef, err := repo.GetHeadRef() if err != nil { return err } r.ReviewRef = headRef } if err := repo.VerifyGitRef(r.TargetRef); err != nil { return err } if err := repo.VerifyGitRef(r.ReviewRef); err != nil { return err } reviewCommit, baseCommit, err := getReviewCommit(repo, r, args) if err != nil { return err } r.BaseCommit = baseCommit if r.Description == "" { description, err := repo.GetCommitMessage(reviewCommit) if err != nil { return err } r.Description = description } note, err := r.Write() if err != nil { return err } repo.AppendNote(request.Ref, reviewCommit, note) if !*requestQuiet { fmt.Printf(requestSummaryTemplate, reviewCommit, r.TargetRef, r.ReviewRef, r.Description) } return nil }
func mirrorRepoToReview(repo repository.Repo, tool review_utils.Tool, syncToRemote bool) { if syncToRemote { repo.PullNotes("origin", "refs/notes/devtools/*") } stateHash, err := repo.GetRepoStateHash() if err != nil { log.Fatal(err) } if processedStates[repo.GetPath()] != stateHash { log.Print("Mirroring repo: ", repo) for _, r := range review.ListAll(repo) { reviewJson, err := r.GetJSON() if err != nil { log.Fatal(err) } log.Println("Mirroring review: ", reviewJson) existingComments[r.Revision] = r.Comments reviewDetails, err := r.Details() if err == nil { tool.EnsureRequestExists(repo, *reviewDetails) } } openReviews[repo.GetPath()] = tool.ListOpenReviews(repo) processedStates[repo.GetPath()] = stateHash tool.Refresh(repo) } ReviewLoop: for _, phabricatorReview := range openReviews[repo.GetPath()] { if reviewCommit := phabricatorReview.GetFirstCommit(repo); reviewCommit != "" { log.Println("Processing review: ", reviewCommit) r, err := review.GetSummary(repo, reviewCommit) if err != nil { log.Fatal(err) } else if r == nil { log.Printf("Skipping unknown review %q", reviewCommit) continue ReviewLoop } revisionComments := existingComments[reviewCommit] log.Printf("Loaded %d comments for %v\n", len(revisionComments), reviewCommit) for _, c := range phabricatorReview.LoadComments() { if !hasOverlap(c, revisionComments) { // The comment is new. note, err := c.Write() if err != nil { log.Fatal(err) } log.Printf("Appending a comment: %s", string(note)) repo.AppendNote(comment.Ref, reviewCommit, note) } else { log.Printf("Skipping '%v', as it has already been written\n", c) } } } } if syncToRemote { if err := repo.PushNotes("origin", "refs/notes/devtools/*"); err != nil { log.Printf("Failed to push updates to the repo %v: %v\n", repo, err) } } }