Beispiel #1
0
// showReview prints the current code review.
func showReview(args []string) error {
	showFlagSet.Parse(args)
	args = showFlagSet.Args()

	var r *review.Review
	var err error
	if len(args) > 1 {
		return errors.New("Only showing a single review is supported.")
	}

	if len(args) == 1 {
		r = review.Get(args[0])
	} else {
		r, err = review.GetCurrent()
	}

	if err != nil {
		return fmt.Errorf("Failed to load the review: %v\n", err)
	}
	if r == nil {
		return errors.New("There is no matching review.")
	}
	if *showJsonOutput {
		return r.PrintJson()
	}
	return r.PrintDetails()
}
Beispiel #2
0
// PrintDiff prints the diff of the review.
func PrintDiff(r *review.Review, diffArgs ...string) error {
	diff, err := r.GetDiff(diffArgs...)
	if err != nil {
		return err
	}
	fmt.Println(diff)
	return nil
}
Beispiel #3
0
// PrintJson pretty prints the given review in JSON format.
func PrintJson(r *review.Review) error {
	json, err := r.GetJson()
	if err != nil {
		return err
	}
	fmt.Println(json)
	return nil
}
Beispiel #4
0
// PrintDetails prints a multi-line overview of a review, including all comments.
func PrintDetails(r *review.Review) error {
	PrintSummary(r.ReviewSummary)
	fmt.Printf(reviewDetailsTemplate, r.Request.ReviewRef, r.Request.TargetRef,
		strings.Join(r.Request.Reviewers, ", "),
		r.Request.Requester, r.GetBuildStatusMessage())
	printAnalyses(r)
	if err := printComments(r); err != nil {
		return err
	}
	return nil
}
Beispiel #5
0
// commentOnReview adds a comment to the current code review.
func commentOnReview(repo repository.Repo, args []string) error {
	commentFlagSet.Parse(args)
	args = commentFlagSet.Args()
	if *commentLgtm && *commentNmw {
		return errors.New("You cannot combine the flags -lgtm and -nmw.")
	}
	if *commentLine != 0 && *commentFile == "" {
		return errors.New("Specifying a line number with the -l flag requires that you also specify a file name with the -f flag.")
	}

	var r *review.Review
	var err error
	if len(args) > 1 {
		return errors.New("Only accepting a single review is supported.")
	}

	if len(args) == 1 {
		r = review.Get(repo, args[0])
	} else {
		r, err = review.GetCurrent(repo)
	}

	if err != nil {
		return fmt.Errorf("Failed to load the review: %v\n", err)
	}
	if r == nil {
		return errors.New("There is no matching review.")
	}

	commentedUponCommit, err := r.GetHeadCommit()
	if err != nil {
		return err
	}
	location := comment.Location{
		Commit: commentedUponCommit,
	}
	if *commentFile != "" {
		location.Path = *commentFile
		if *commentLine != 0 {
			location.Range = &comment.Range{
				StartLine: uint32(*commentLine),
			}
		}
	}

	c := comment.New(repo.GetUserEmail(), *commentMessage)
	c.Location = &location
	c.Parent = *commentParent
	if *commentLgtm || *commentNmw {
		resolved := *commentLgtm
		c.Resolved = &resolved
	}
	return r.AddComment(c)
}
Beispiel #6
0
// rejectReview adds an NMW comment to the current code review.
func rejectReview(repo repository.Repo, args []string) error {
	rejectFlagSet.Parse(args)
	args = rejectFlagSet.Args()

	var r *review.Review
	var err error
	if len(args) > 1 {
		return errors.New("Only rejecting a single review is supported.")
	}

	if len(args) == 1 {
		r, err = review.Get(repo, args[0])
	} else {
		r, err = review.GetCurrent(repo)
	}

	if err != nil {
		return fmt.Errorf("Failed to load the review: %v\n", err)
	}
	if r == nil {
		return errors.New("There is no matching review.")
	}

	if *rejectMessageFile != "" && *rejectMessage == "" {
		*rejectMessage, err = input.FromFile(*rejectMessageFile)
		if err != nil {
			return err
		}
	}
	if *rejectMessageFile == "" && *rejectMessage == "" {
		*rejectMessage, err = input.LaunchEditor(repo, commentFilename)
		if err != nil {
			return err
		}
	}

	rejectedCommit, err := r.GetHeadCommit()
	if err != nil {
		return err
	}
	location := comment.Location{
		Commit: rejectedCommit,
	}
	resolved := false
	userEmail, err := repo.GetUserEmail()
	if err != nil {
		return err
	}
	c := comment.New(userEmail, *rejectMessage)
	c.Location = &location
	c.Resolved = &resolved
	return r.AddComment(c)
}
Beispiel #7
0
// printAnalyses prints the static analysis results for the latest commit in the review.
func printAnalyses(r *review.Review) {
	analysesNotes, err := r.GetAnalysesNotes()
	if err != nil {
		fmt.Println("  analyses: ", err)
		return
	}
	if analysesNotes == nil {
		fmt.Println("  analyses: passed")
		return
	}
	fmt.Printf("  analyses: %d warnings\n", len(analysesNotes))
	// TODO(ojarjur): Print the actual notes
}
Beispiel #8
0
// acceptReview adds an LGTM comment to the current code review.
func acceptReview(repo repository.Repo, args []string) error {
	acceptFlagSet.Parse(args)
	args = acceptFlagSet.Args()

	var r *review.Review
	var err error
	if len(args) > 1 {
		return errors.New("Only accepting a single review is supported.")
	}

	if len(args) == 1 {
		r, err = review.Get(repo, args[0])
	} else {
		r, err = review.GetCurrent(repo)
	}

	if err != nil {
		return fmt.Errorf("Failed to load the review: %v\n", err)
	}
	if r == nil {
		return errors.New("There is no matching review.")
	}

	acceptedCommit, err := r.GetHeadCommit()
	if err != nil {
		return err
	}
	location := comment.Location{
		Commit: acceptedCommit,
	}
	resolved := true
	userEmail, err := repo.GetUserEmail()
	if err != nil {
		return err
	}
	c := comment.New(userEmail, *acceptMessage)
	c.Location = &location
	c.Resolved = &resolved
	return r.AddComment(c)
}
Beispiel #9
0
// showReview prints the current code review.
func showReview(repo repository.Repo, args []string) error {
	showFlagSet.Parse(args)
	args = showFlagSet.Args()
	if *showDiffOptions != "" && !*showDiffOutput {
		return errors.New("The --diff-opts flag can only be used if the --diff flag is set.")
	}

	var r *review.Review
	var err error
	if len(args) > 1 {
		return errors.New("Only showing a single review is supported.")
	}

	if len(args) == 1 {
		r = review.Get(repo, args[0])
	} else {
		r, err = review.GetCurrent(repo)
	}

	if err != nil {
		return fmt.Errorf("Failed to load the review: %v\n", err)
	}
	if r == nil {
		return errors.New("There is no matching review.")
	}
	if *showJsonOutput {
		return r.PrintJson()
	}
	if *showDiffOutput {
		var diffArgs []string
		if *showDiffOptions != "" {
			diffArgs = strings.Split(*showDiffOptions, ",")
		}
		return r.PrintDiff(diffArgs...)
	}
	return r.PrintDetails()
}
Beispiel #10
0
// Submit the current code review request.
//
// The "args" parameter contains all of the command line arguments that followed the subcommand.
func submitReview(repo repository.Repo, args []string) error {
	submitFlagSet.Parse(args)
	args = submitFlagSet.Args()

	if *submitMerge && *submitRebase {
		return errors.New("Only one of --merge or --rebase is allowed.")
	}

	var r *review.Review
	var err error
	if len(args) > 1 {
		return errors.New("Only accepting a single review is supported.")
	}
	if len(args) == 1 {
		r, err = review.Get(repo, args[0])
	} else {
		r, err = review.GetCurrent(repo)
	}

	if err != nil {
		return fmt.Errorf("Failed to load the review: %v\n", err)
	}
	if r == nil {
		return errors.New("There is no matching review.")
	}

	if r.Submitted {
		return errors.New("The review has already been submitted.")
	}

	if !*submitTBR && (r.Resolved == nil || !*r.Resolved) {
		return errors.New("Not submitting as the review has not yet been accepted.")
	}

	target := r.Request.TargetRef
	if err := repo.VerifyGitRef(target); err != nil {
		return err
	}
	source, err := r.GetHeadCommit()
	if err != nil {
		return err
	}

	isAncestor, err := repo.IsAncestor(target, source)
	if err != nil {
		return err
	}
	if !isAncestor {
		return errors.New("Refusing to submit a non-fast-forward review. First merge the target ref.")
	}

	if !(*submitRebase || *submitMerge || *submitFastForward) {
		submitStrategy, err := repo.GetSubmitStrategy()
		if err != nil {
			return err
		}
		if submitStrategy == "merge" && !*submitRebase && !*submitFastForward {
			*submitMerge = true
		}
		if submitStrategy == "rebase" && !*submitMerge && !*submitFastForward {
			*submitRebase = true
		}
		if submitStrategy == "fast-forward" && !*submitRebase && !*submitMerge {
			*submitFastForward = true
		}
	}

	if *submitRebase {
		if err := r.Rebase(*submitArchive); err != nil {
			return err
		}
		source, err = r.GetHeadCommit()
		if err != nil {
			return err
		}
	}

	if err := repo.SwitchToRef(target); err != nil {
		return err
	}
	if *submitMerge {
		submitMessage := fmt.Sprintf("Submitting review %.12s", r.Revision)
		return repo.MergeRef(source, false, submitMessage, r.Request.Description)
	} else {
		return repo.MergeRef(source, true)
	}
}
// EnsureRequestExists runs the "arcanist" command-line tool to create a Differential diff for the given request, if one does not already exist.
func (arc Arcanist) EnsureRequestExists(repo repository.Repo, review review.Review) {
	revision := review.Revision
	req := review.Request

	// If this revision has been previously closed shortcut all processing
	if closedRevisionsMap[revision] {
		return
	}
	existingReviews := arc.listDifferentialReviewsOrDie(revision)
	if review.Submitted {
		// The change has already been merged in, so we should simply close any open reviews.
		for _, differentialReview := range existingReviews {
			if !differentialReview.isClosed() {
				differentialReview.close()
			}
		}
		closedRevisionsMap[revision] = true
		return
	}

	base, err := review.GetBaseCommit()
	if err != nil {
		// There are lots of reasons that we might not be able to compute a base commit,
		// (e.g. the revision already being merged in, or being dropped and garbage collected),
		// but they all indicate that the review request is no longer valid.
		log.Printf("Ignoring review request '%v', because we could not compute a base commit", req)
		return
	}

	head, err := review.GetHeadCommit()
	if err != nil {
		// The given review ref has been deleted (or never existed), but the change wasn't merged.
		// TODO(ojarjur): We should mark the existing reviews as abandoned.
		log.Printf("Ignoring review because the review ref '%s' does not exist", req.ReviewRef)
		return
	}

	if len(existingReviews) > 0 {
		// The change is still pending, but we already have existing reviews, so we should just update those.
		for _, existing := range existingReviews {
			arc.updateReviewDiffs(repo, existing, head, req, review)
		}
		return
	}

	diff, err := arc.createDifferentialDiff(repo, base, revision, req, []string{})
	if err != nil {
		log.Fatal(err)
	}
	if diff == nil {
		// The revision is already merged in, ignore it.
		return
	}
	rev, err := arc.createDifferentialRevision(repo, revision, diff.ID, req)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Created diff %v and revision %v for the review of %s", diff, rev, revision)

	// If the review already contains multiple commits by the time we mirror it, then
	// we need to ensure that at least the first and last ones are added.
	existingReviews = arc.listDifferentialReviewsOrDie(revision)
	for _, existing := range existingReviews {
		arc.updateReviewDiffs(repo, existing, head, req, review)
	}
}
Beispiel #12
0
// commentOnReview adds a comment to the current code review.
func commentOnReview(repo repository.Repo, args []string) error {
	commentFlagSet.Parse(args)
	args = commentFlagSet.Args()

	var r *review.Review
	var err error
	if len(args) > 1 {
		return errors.New("Only accepting a single review is supported.")
	}

	if len(args) == 1 {
		r, err = review.Get(repo, args[0])
	} else {
		r, err = review.GetCurrent(repo)
	}

	if err != nil {
		return fmt.Errorf("Failed to load the review: %v\n", err)
	}
	if r == nil {
		return errors.New("There is no matching review.")
	}

	if *commentLgtm && *commentNmw {
		return errors.New("You cannot combine the flags -lgtm and -nmw.")
	}
	if *commentLine != 0 && *commentFile == "" {
		return errors.New("Specifying a line number with the -l flag requires that you also specify a file name with the -f flag.")
	}
	if *commentParent != "" && !commentHashExists(*commentParent, r.Comments) {
		return errors.New("There is no matching parent comment.")
	}

	if *commentMessage == "" {
		editor, err := repo.GetCoreEditor()
		if err != nil {
			return fmt.Errorf("Unable to detect default git editor: %v\n", err)
		}

		path := fmt.Sprintf("%s/.git/%s", repo.GetPath(), commentFilename)

		cmd := exec.Command(editor, path)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		err = cmd.Start()
		if err != nil {
			return fmt.Errorf("Unable to start editor: %v\n", err)
		}

		err = cmd.Wait()
		if err != nil {
			return fmt.Errorf("Editing finished with error: %v\n", err)
		}

		comment, err := ioutil.ReadFile(path)
		if err != nil {
			os.Remove(path)
			return fmt.Errorf("Error reading comment file: %v\n", err)
		}
		*commentMessage = string(comment)
		os.Remove(path)
	}

	commentedUponCommit, err := r.GetHeadCommit()
	if err != nil {
		return err
	}
	location := comment.Location{
		Commit: commentedUponCommit,
	}
	if *commentFile != "" {
		location.Path = *commentFile
		if *commentLine != 0 {
			location.Range = &comment.Range{
				StartLine: uint32(*commentLine),
			}
		}
	}

	userEmail, err := repo.GetUserEmail()
	if err != nil {
		return err
	}
	c := comment.New(userEmail, *commentMessage)
	c.Location = &location
	c.Parent = *commentParent
	if *commentLgtm || *commentNmw {
		resolved := *commentLgtm
		c.Resolved = &resolved
	}
	return r.AddComment(c)
}
Beispiel #13
0
// rejectReview adds an NMW comment to the current code review.
func rejectReview(repo repository.Repo, args []string) error {
	rejectFlagSet.Parse(args)
	args = rejectFlagSet.Args()

	var r *review.Review
	var err error
	if len(args) > 1 {
		return errors.New("Only rejecting a single review is supported.")
	}

	if len(args) == 1 {
		r, err = review.Get(repo, args[0])
	} else {
		r, err = review.GetCurrent(repo)
	}

	if err != nil {
		return fmt.Errorf("Failed to load the review: %v\n", err)
	}
	if r == nil {
		return errors.New("There is no matching review.")
	}

	if *rejectMessage == "" {
		editor, err := repo.GetCoreEditor()
		if err != nil {
			return fmt.Errorf("Unable to detect default git editor: %v\n", err)
		}

		path := fmt.Sprintf("%s/.git/%s", repo.GetPath(), rejectFilename)

		cmd := exec.Command(editor, path)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		err = cmd.Start()
		if err != nil {
			return fmt.Errorf("Unable to start editor: %v\n", err)
		}

		err = cmd.Wait()
		if err != nil {
			return fmt.Errorf("Editing finished with error: %v\n", err)
		}

		comment, err := ioutil.ReadFile(path)
		if err != nil {
			os.Remove(path)
			return fmt.Errorf("Error reading comment file: %v\n", err)
		}
		*rejectMessage = string(comment)
		os.Remove(path)
	}

	rejectedCommit, err := r.GetHeadCommit()
	if err != nil {
		return err
	}
	location := comment.Location{
		Commit: rejectedCommit,
	}
	resolved := false
	userEmail, err := repo.GetUserEmail()
	if err != nil {
		return err
	}
	c := comment.New(userEmail, *rejectMessage)
	c.Location = &location
	c.Resolved = &resolved
	return r.AddComment(c)
}
Beispiel #14
0
// printAnalyses prints the static analysis results for the latest commit in the review.
func printAnalyses(r *review.Review) {
	fmt.Println("  analyses: ", r.GetAnalysesMessage())
}