reviewr 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +3 -0
- data/README.md +50 -27
- data/lib/reviewr/cli.rb +2 -0
- data/lib/reviewr/cli/accept.rb +33 -0
- data/lib/reviewr/cli/command.rb +55 -0
- data/lib/reviewr/cli/help.rb +3 -7
- data/lib/reviewr/cli/main.rb +17 -38
- data/lib/reviewr/cli/request.rb +5 -20
- data/lib/reviewr/git.rb +27 -2
- data/lib/reviewr/project.rb +38 -7
- data/lib/reviewr/templates/accept_email.erb +6 -0
- data/lib/reviewr/version.rb +1 -1
- metadata +7 -4
data/Manifest.txt
CHANGED
@@ -9,8 +9,11 @@ lib/reviewr/git.rb
|
|
9
9
|
lib/reviewr/mailer.rb
|
10
10
|
lib/reviewr/project.rb
|
11
11
|
lib/reviewr/version.rb
|
12
|
+
lib/reviewr/cli/accept.rb
|
13
|
+
lib/reviewr/cli/command.rb
|
12
14
|
lib/reviewr/cli/help.rb
|
13
15
|
lib/reviewr/cli/main.rb
|
14
16
|
lib/reviewr/cli/request.rb
|
17
|
+
lib/reviewr/templates/accept_email.erb
|
15
18
|
lib/reviewr/templates/commit_msg.erb
|
16
19
|
lib/reviewr/templates/request_email.erb
|
data/README.md
CHANGED
@@ -6,7 +6,28 @@ git and github.com for version control.
|
|
6
6
|
# Installation
|
7
7
|
gem install reviewr
|
8
8
|
|
9
|
-
#
|
9
|
+
# Usage
|
10
|
+
|
11
|
+
Reviewr is designed to simplify code reviews for projects that revolve
|
12
|
+
around a single 'master' repository with multiple contributors (i.e. a
|
13
|
+
project that is hosted on github). It does this by providing a default
|
14
|
+
work-flow that all developers can use.
|
15
|
+
|
16
|
+
The general work-flow (at the moment) is:
|
17
|
+
|
18
|
+
1. The coder issues a code review request through the 'request'
|
19
|
+
command.
|
20
|
+
2. The reviewer(s) review the code and comment on it through github
|
21
|
+
3. If the code is good enough to merge it can be pulled into the
|
22
|
+
master branch through the 'accept' command
|
23
|
+
|
24
|
+
While this documentation is up to date (as of version 0.2.0) I am
|
25
|
+
still experimenting with an ideal work-flow for these type of code
|
26
|
+
reviews, so this code is subject to heavy change. If you have any
|
27
|
+
suggestions send me an email or comment in the issue tracker as I
|
28
|
+
would love further opinions.
|
29
|
+
|
30
|
+
## Requesting a code review
|
10
31
|
reviewr request <email>
|
11
32
|
This will result in the following actions:
|
12
33
|
|
@@ -15,41 +36,37 @@ This will result in the following actions:
|
|
15
36
|
* Push the code review branch to the remote repository
|
16
37
|
* Send an email to <email> requesting a code review of the branch
|
17
38
|
The email will include a link to Github's compare view for the
|
18
|
-
changes
|
39
|
+
changes
|
19
40
|
|
20
|
-
##
|
41
|
+
## Accepting changes from a code review
|
42
|
+
reviewr accept <branch_name>
|
43
|
+
This will result in the following actions:
|
21
44
|
|
22
|
-
*
|
23
|
-
|
45
|
+
* Create a branch for the reviewed code
|
46
|
+
* Rebase the reviewed code on the current branch
|
47
|
+
* Merge in the commits
|
48
|
+
* Push the merged branch
|
49
|
+
* Delete the code review branch from the remote repo
|
50
|
+
* Send an email to the requester of the review saying the changes have been
|
51
|
+
merged
|
24
52
|
|
25
|
-
#
|
53
|
+
# Contributing
|
54
|
+
|
55
|
+
## Reporting Bugs
|
26
56
|
|
27
|
-
|
57
|
+
Bugs are being managed using Github's issue tracking
|
28
58
|
|
29
|
-
|
59
|
+
http://github.com/rhburrows/reviewr/issues
|
30
60
|
|
31
|
-
|
32
|
-
request email@site.com`. This causes reviewr to:
|
61
|
+
## Contributing Code
|
33
62
|
|
34
|
-
|
35
|
-
is the SHA of the current commit
|
36
|
-
* Add a commit to the branch with metadata about the request
|
37
|
-
(requester name/email etc).
|
38
|
-
* push the branch to the origin repository
|
39
|
-
* generate a github review url from the current head to the pushed
|
40
|
-
review branch
|
41
|
-
* Send an email to `email@site.com` with a nice message and the url
|
63
|
+
Just fork the project on github and submit a pull request
|
42
64
|
|
43
|
-
|
44
|
-
|
45
|
-
|
65
|
+
http://github.com/rhburrows/reviewr
|
66
|
+
|
67
|
+
# TODO
|
46
68
|
|
47
|
-
|
48
|
-
* attempt to merge the reviewed branch into the master
|
49
|
-
* if it fails an error will be raised and execution will stop
|
50
|
-
* if is succeeds the merged master will be pushed
|
51
|
-
* the remote review branch will be deleted.
|
52
|
-
* an email will be sent to the review requester saying the code was accepted
|
69
|
+
## Add a reject code review command
|
53
70
|
|
54
71
|
If the code the reviewer is checking is not acceptable, upon finishing
|
55
72
|
comments on github the reviewer can run `reviewr reject
|
@@ -58,3 +75,9 @@ review_0f38ef31`. reviewr will:
|
|
58
75
|
* Re-generate the github url for comparing to the current master
|
59
76
|
* Send an email to the requester of the review saying the code has
|
60
77
|
been rejected and to please see the comments on the linked page
|
78
|
+
|
79
|
+
# Limitations
|
80
|
+
|
81
|
+
* Email can only be sent from a Gmail (or Google Apps for my domain)
|
82
|
+
address
|
83
|
+
* Only tested with git 1.7.0
|
data/lib/reviewr/cli.rb
CHANGED
@@ -0,0 +1,33 @@
|
|
1
|
+
module Reviewr
|
2
|
+
module CLI
|
3
|
+
class Accept < Command
|
4
|
+
def execute
|
5
|
+
merge_branch = project.current_branch
|
6
|
+
|
7
|
+
project.review_branch = arguments.first
|
8
|
+
prompt_for_user
|
9
|
+
project.fetch_review_branch
|
10
|
+
project.fetch_master
|
11
|
+
project.create_review_branch("origin/#{arguments.first}")
|
12
|
+
|
13
|
+
unless project.rebase_review
|
14
|
+
output.print "Branch '#{arguments.first}' won't merge cleanly"
|
15
|
+
else
|
16
|
+
# This must be run while on the review branch
|
17
|
+
project.to = project.requester_email
|
18
|
+
|
19
|
+
project.change_branch(merge_branch)
|
20
|
+
project.merge_commits
|
21
|
+
project.push_branch(merge_branch)
|
22
|
+
project.delete_remote_review_branch
|
23
|
+
|
24
|
+
Mailer.new(project).send(email_body)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def email_body
|
29
|
+
read_template('accept_email.erb')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Reviewr
|
2
|
+
module CLI
|
3
|
+
class Command
|
4
|
+
attr_reader :project
|
5
|
+
attr_accessor :input, :output, :arguments
|
6
|
+
|
7
|
+
def initialize(project, input = STDIN, output = STDOUT)
|
8
|
+
@project = project
|
9
|
+
@input, @output = input, output
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
execute
|
14
|
+
end
|
15
|
+
|
16
|
+
def prompt_for_user
|
17
|
+
output.print("Email (default #{project.user_email}): ")
|
18
|
+
email = input.gets.chomp
|
19
|
+
project.user_email = email unless email.empty?
|
20
|
+
|
21
|
+
output.print("Email password: ")
|
22
|
+
no_echo(input) do
|
23
|
+
project.email_password = input.gets.chomp
|
24
|
+
end
|
25
|
+
output.print("\n")
|
26
|
+
|
27
|
+
output.print("Remote repository (default origin): ")
|
28
|
+
repo = input.gets.chomp
|
29
|
+
project.remote_repo = repo unless repo.empty?
|
30
|
+
end
|
31
|
+
|
32
|
+
def read_template(name)
|
33
|
+
@templates ||= {}
|
34
|
+
@templates[name] ||= ERB.new(File.read(File.join(File.dirname(__FILE__),
|
35
|
+
'..',
|
36
|
+
'templates',
|
37
|
+
name)))
|
38
|
+
@templates[name].result(binding)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def no_echo(input)
|
44
|
+
oldt = Termios.tcgetattr(input)
|
45
|
+
newt = oldt.dup
|
46
|
+
newt.lflag &= ~Termios::ECHO
|
47
|
+
Termios.tcsetattr(input, Termios::TCSANOW, newt)
|
48
|
+
|
49
|
+
yield
|
50
|
+
|
51
|
+
Termios.tcsetattr(input, Termios::TCSANOW, oldt)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/reviewr/cli/help.rb
CHANGED
data/lib/reviewr/cli/main.rb
CHANGED
@@ -3,54 +3,33 @@ require 'termios'
|
|
3
3
|
module Reviewr
|
4
4
|
module CLI
|
5
5
|
class Main
|
6
|
-
attr_reader :command
|
7
|
-
attr_accessor :
|
6
|
+
attr_reader :command, :c
|
7
|
+
attr_accessor :arguments
|
8
8
|
|
9
9
|
def initialize(args, input = STDIN, output = STDOUT)
|
10
10
|
@command = args.shift
|
11
|
-
@
|
11
|
+
@arguments = args
|
12
12
|
@input, @output = input, output
|
13
13
|
end
|
14
14
|
|
15
15
|
def run
|
16
|
-
|
17
|
-
when "request"
|
18
|
-
prompt_for_user(@input, @output)
|
19
|
-
Request.new(project).call
|
20
|
-
else
|
21
|
-
#FIXME
|
22
|
-
Help.new.call(project.to)
|
23
|
-
end
|
16
|
+
build_command.call
|
24
17
|
end
|
25
18
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
19
|
+
def build_command
|
20
|
+
unless @c
|
21
|
+
case command
|
22
|
+
when "request"
|
23
|
+
@c = Request.new(Project.new, @input, @output)
|
24
|
+
when "accept"
|
25
|
+
@c = Accept.new(Project.new, @input, @output)
|
26
|
+
else
|
27
|
+
@c = Help.new(Project.new, @input, @output)
|
28
|
+
end
|
29
|
+
|
30
|
+
@c.arguments = arguments
|
34
31
|
end
|
35
|
-
|
36
|
-
|
37
|
-
output.print("Remote repository (default origin): ")
|
38
|
-
repo = input.gets.chomp
|
39
|
-
project.remote_repo = repo unless repo.empty?
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
#TODO, should this be somewhere better?
|
45
|
-
def no_echo(input)
|
46
|
-
oldt = Termios.tcgetattr(input)
|
47
|
-
newt = oldt.dup
|
48
|
-
newt.lflag &= ~Termios::ECHO
|
49
|
-
Termios.tcsetattr(input, Termios::TCSANOW, newt)
|
50
|
-
|
51
|
-
yield
|
52
|
-
|
53
|
-
Termios.tcsetattr(input, Termios::TCSANOW, oldt)
|
32
|
+
@c
|
54
33
|
end
|
55
34
|
end
|
56
35
|
end
|
data/lib/reviewr/cli/request.rb
CHANGED
@@ -3,16 +3,12 @@ require 'forwardable'
|
|
3
3
|
|
4
4
|
module Reviewr
|
5
5
|
module CLI
|
6
|
-
class Request
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
@project = project
|
11
|
-
end
|
12
|
-
|
13
|
-
def call
|
6
|
+
class Request < Command
|
7
|
+
def execute
|
8
|
+
project.to = arguments.first
|
9
|
+
prompt_for_user
|
14
10
|
original_branch = project.current_branch
|
15
|
-
project.create_review_branch
|
11
|
+
project.create_review_branch(original_branch)
|
16
12
|
project.create_review_commit(commit_msg)
|
17
13
|
project.push_review_branch
|
18
14
|
Mailer.new(project).send(email_body)
|
@@ -31,17 +27,6 @@ module Reviewr
|
|
31
27
|
def email_body
|
32
28
|
read_template('request_email.erb')
|
33
29
|
end
|
34
|
-
|
35
|
-
private
|
36
|
-
|
37
|
-
def read_template(name)
|
38
|
-
@templates ||= {}
|
39
|
-
@templates[name] ||= ERB.new(File.read(File.join(File.dirname(__FILE__),
|
40
|
-
'..',
|
41
|
-
'templates',
|
42
|
-
name)))
|
43
|
-
@templates[name].result(binding)
|
44
|
-
end
|
45
30
|
end
|
46
31
|
end
|
47
32
|
end
|
data/lib/reviewr/git.rb
CHANGED
@@ -20,8 +20,17 @@ module Reviewr
|
|
20
20
|
execute('git show --pretty=format:"%H" HEAD').split("\n")[0]
|
21
21
|
end
|
22
22
|
|
23
|
-
def
|
24
|
-
execute("git
|
23
|
+
def rebase(base, branch)
|
24
|
+
conflict = execute("git rebase #{base} #{branch}").to_s.
|
25
|
+
include?("CONFLICT")
|
26
|
+
if conflict
|
27
|
+
execute('git rebase --abort')
|
28
|
+
end
|
29
|
+
!conflict
|
30
|
+
end
|
31
|
+
|
32
|
+
def create_branch(branch_name, base)
|
33
|
+
execute("git branch #{branch_name} #{base}")
|
25
34
|
end
|
26
35
|
|
27
36
|
def commit(msg)
|
@@ -37,6 +46,10 @@ module Reviewr
|
|
37
46
|
execute("git checkout #{branch_name}")
|
38
47
|
end
|
39
48
|
|
49
|
+
def fetch(branch_name)
|
50
|
+
execute("git fetch #{remote_repo} #{branch_name}")
|
51
|
+
end
|
52
|
+
|
40
53
|
def user_email
|
41
54
|
email = execute('git config user.email')
|
42
55
|
email && email.chomp
|
@@ -56,6 +69,18 @@ module Reviewr
|
|
56
69
|
r && r.split(/\s+/)[0]
|
57
70
|
end
|
58
71
|
|
72
|
+
def cherry(from, to)
|
73
|
+
execute("git cherry #{from} #{to}")
|
74
|
+
end
|
75
|
+
|
76
|
+
def cherry_pick(commit)
|
77
|
+
execute("git cherry-pick -s #{commit}")
|
78
|
+
end
|
79
|
+
|
80
|
+
def log(n)
|
81
|
+
execute("git log -n #{n}")
|
82
|
+
end
|
83
|
+
|
59
84
|
def execute(cmd)
|
60
85
|
`#{cmd}`
|
61
86
|
end
|
data/lib/reviewr/project.rb
CHANGED
@@ -4,19 +4,32 @@ module Reviewr
|
|
4
4
|
class Project
|
5
5
|
extend Forwardable
|
6
6
|
|
7
|
-
attr_reader :
|
8
|
-
attr_accessor :email_password
|
9
|
-
attr_writer :user_email
|
7
|
+
attr_reader :git, :email_server
|
8
|
+
attr_accessor :email_password, :to
|
9
|
+
attr_writer :user_email, :review_branch
|
10
10
|
|
11
11
|
def_delegators :git, :push_branch, :origin_location, :remote_repo,
|
12
12
|
:remote_repo=, :current_branch, :change_branch
|
13
13
|
|
14
|
-
def initialize(
|
15
|
-
@
|
14
|
+
def initialize(git = Git.instance)
|
15
|
+
@git = git
|
16
16
|
end
|
17
17
|
|
18
|
-
def create_review_branch
|
19
|
-
git.create_branch(review_branch)
|
18
|
+
def create_review_branch(base = 'master')
|
19
|
+
git.create_branch(review_branch, base)
|
20
|
+
git.change_branch(review_branch)
|
21
|
+
end
|
22
|
+
|
23
|
+
def rebase_review
|
24
|
+
git.rebase(current_branch, review_branch)
|
25
|
+
end
|
26
|
+
|
27
|
+
def fetch_review_branch
|
28
|
+
git.fetch(review_branch)
|
29
|
+
end
|
30
|
+
|
31
|
+
def fetch_master
|
32
|
+
git.fetch('master')
|
20
33
|
end
|
21
34
|
|
22
35
|
def create_review_commit(msg)
|
@@ -46,5 +59,23 @@ module Reviewr
|
|
46
59
|
def email_server
|
47
60
|
@email_server ||= user_email.split('@')[1]
|
48
61
|
end
|
62
|
+
|
63
|
+
def merge_commits
|
64
|
+
git.cherry(current_branch, review_branch).split(/\n/).each do |line|
|
65
|
+
git.cherry_pick(line.split(/\s/)[1])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def delete_remote_review_branch
|
70
|
+
git.push_branch(":#{review_branch}")
|
71
|
+
end
|
72
|
+
|
73
|
+
def requester_email
|
74
|
+
msg = git.log(1)
|
75
|
+
if msg
|
76
|
+
m = msg.match(/requested_by: ([^\s]+)/)
|
77
|
+
m && m[1]
|
78
|
+
end
|
79
|
+
end
|
49
80
|
end
|
50
81
|
end
|
data/lib/reviewr/version.rb
CHANGED
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Ryan Burrows
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-04-
|
17
|
+
date: 2010-04-28 00:00:00 -07:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -85,9 +85,12 @@ files:
|
|
85
85
|
- lib/reviewr/mailer.rb
|
86
86
|
- lib/reviewr/project.rb
|
87
87
|
- lib/reviewr/version.rb
|
88
|
+
- lib/reviewr/cli/accept.rb
|
89
|
+
- lib/reviewr/cli/command.rb
|
88
90
|
- lib/reviewr/cli/help.rb
|
89
91
|
- lib/reviewr/cli/main.rb
|
90
92
|
- lib/reviewr/cli/request.rb
|
93
|
+
- lib/reviewr/templates/accept_email.erb
|
91
94
|
- lib/reviewr/templates/commit_msg.erb
|
92
95
|
- lib/reviewr/templates/request_email.erb
|
93
96
|
has_rdoc: true
|