diff --git a/commands/abandon.go b/commands/abandon.go index b9ab6ebe..6f408e16 100644 --- a/commands/abandon.go +++ b/commands/abandon.go @@ -25,6 +25,7 @@ import ( "github.com/google/git-appraise/repository" "github.com/google/git-appraise/review" "github.com/google/git-appraise/review/comment" + "github.com/google/git-appraise/review/gpg" "github.com/google/git-appraise/review/request" ) @@ -33,6 +34,9 @@ var abandonFlagSet = flag.NewFlagSet("abandon", flag.ExitOnError) var ( abandonMessageFile = abandonFlagSet.String("F", "", "Take the comment from the given file. Use - to read the message from the standard input") abandonMessage = abandonFlagSet.String("m", "", "Message to attach to the review") + + abandonSign = abandonFlagSet.Bool("S", false, + "Sign the contents of the abandonment") ) // abandonReview adds an NMW comment to the current code review. @@ -88,14 +92,32 @@ func abandonReview(repo repository.Repo, args []string) error { c.Location = &location c.Resolved = &resolved - err = r.AddComment(c) + var key string + if *abandonSign { + key, err := repo.GetUserSigningKey() + if err != nil { + return err + } + err = gpg.Sign(key, &c) + if err != nil { + return err + } + } + err = r.AddComment(c) if err != nil { return err } // Empty target ref indicates that request was abandoned r.Request.TargetRef = "" + // (re)sign the request after clearing out `TargetRef'. + if *abandonSign { + err = gpg.Sign(key, &r.Request) + if err != nil { + return err + } + } note, err := r.Request.Write() if err != nil { diff --git a/commands/accept.go b/commands/accept.go index 6207b7b9..b50f424c 100644 --- a/commands/accept.go +++ b/commands/accept.go @@ -24,6 +24,7 @@ import ( "github.com/google/git-appraise/repository" "github.com/google/git-appraise/review" "github.com/google/git-appraise/review/comment" + "github.com/google/git-appraise/review/gpg" ) var acceptFlagSet = flag.NewFlagSet("accept", flag.ExitOnError) @@ -31,6 +32,9 @@ var acceptFlagSet = flag.NewFlagSet("accept", flag.ExitOnError) var ( acceptMessageFile = acceptFlagSet.String("F", "", "Take the comment from the given file. Use - to read the message from the standard input") acceptMessage = acceptFlagSet.String("m", "", "Message to attach to the review") + + acceptSign = acceptFlagSet.Bool("S", false, + "sign the contents of the acceptance") ) // acceptReview adds an LGTM comment to the current code review. @@ -80,6 +84,16 @@ func acceptReview(repo repository.Repo, args []string) error { c := comment.New(userEmail, *acceptMessage) c.Location = &location c.Resolved = &resolved + if *acceptSign { + key, err := repo.GetUserSigningKey() + if err != nil { + return err + } + err = gpg.Sign(key, &c) + if err != nil { + return err + } + } return r.AddComment(c) } diff --git a/commands/comment.go b/commands/comment.go index 28867499..cfb0cf6a 100644 --- a/commands/comment.go +++ b/commands/comment.go @@ -25,6 +25,7 @@ import ( "github.com/google/git-appraise/repository" "github.com/google/git-appraise/review" "github.com/google/git-appraise/review/comment" + "github.com/google/git-appraise/review/gpg" ) var commentFlagSet = flag.NewFlagSet("comment", flag.ExitOnError) @@ -37,6 +38,8 @@ var ( commentFile = commentFlagSet.String("f", "", "File being commented upon") commentLgtm = commentFlagSet.Bool("lgtm", false, "'Looks Good To Me'. Set this to express your approval. This cannot be combined with nmw") commentNmw = commentFlagSet.Bool("nmw", false, "'Needs More Work'. Set this to express your disapproval. This cannot be combined with lgtm") + commentSign = commentFlagSet.Bool("S", false, + "Sign the contents of the comment") ) func init() { @@ -129,6 +132,18 @@ func commentOnReview(repo repository.Repo, args []string) error { resolved := *commentLgtm c.Resolved = &resolved } + + if *commentSign { + key, err := repo.GetUserSigningKey() + if err != nil { + return err + } + err = gpg.Sign(key, &c) + if err != nil { + return err + } + } + return r.AddComment(c) } diff --git a/commands/pull.go b/commands/pull.go index 48dfe5ba..63fd2399 100644 --- a/commands/pull.go +++ b/commands/pull.go @@ -18,30 +18,73 @@ package commands import ( "errors" + "flag" "fmt" + "github.com/google/git-appraise/repository" + "github.com/google/git-appraise/review" +) + +var ( + pullFlagSet = flag.NewFlagSet("pull", flag.ExitOnError) + pullVerify = pullFlagSet.Bool("verify-signatures", false, + "verify the signatures of pulled reviews") ) -// pull updates the local git-notes used for reviews with those from a remote repo. +// pull updates the local git-notes used for reviews with those from a remote +// repo. func pull(repo repository.Repo, args []string) error { - if len(args) > 1 { - return errors.New("Only pulling from one remote at a time is supported.") + pullFlagSet.Parse(args) + pullArgs := pullFlagSet.Args() + + if len(pullArgs) > 1 { + return errors.New( + "Only pulling from one remote at a time is supported.") } remote := "origin" - if len(args) == 1 { - remote = args[0] + if len(pullArgs) == 1 { + remote = pullArgs[0] + } + // This is the easy case. We're not checking signatures so just go the + // normal route. + if !*pullVerify { + return repo.PullNotesAndArchive(remote, notesRefPattern, + archiveRefPattern) + } + + // Otherwise, we collect the fetched reviewed revisions (their hashes), get + // their reviews, and then one by one, verify them. If we make it through + // the set, _then_ we merge the remote reference into the local branch. + revisions, err := repo.FetchAndReturnNewReviewHashes(remote, + notesRefPattern, archiveRefPattern) + if err != nil { + return err + } + for _, revision := range revisions { + rvw, err := review.GetSummaryViaRefs(repo, + "refs/notes/"+remote+"/devtools/reviews", + "refs/notes/"+remote+"/devtools/discuss", revision) + if err != nil { + return err + } + err = rvw.Verify() + if err != nil { + return err + } + fmt.Println("verified review:", revision) } - if err := repo.PullNotesAndArchive(remote, notesRefPattern, archiveRefPattern); err != nil { + err = repo.MergeNotes(remote, notesRefPattern) + if err != nil { return err } - return nil + return repo.MergeArchives(remote, archiveRefPattern) } var pullCmd = &Command{ Usage: func(arg0 string) { - fmt.Printf("Usage: %s pull []\n", arg0) + fmt.Printf("Usage: %s pull [