reviewr 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- # Requesting a code review
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
- ## Limitations
41
+ ## Accepting changes from a code review
42
+ reviewr accept <branch_name>
43
+ This will result in the following actions:
21
44
 
22
- * Email can only be sent from a Gmail (or Google Apps for my domain)
23
- address
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
- # TODO
53
+ # Contributing
54
+
55
+ ## Reporting Bugs
26
56
 
27
- ## Finish out the basic workflow
57
+ Bugs are being managed using Github's issue tracking
28
58
 
29
- General workflow goes something like this:
59
+ http://github.com/rhburrows/reviewr/issues
30
60
 
31
- Code, code, code. Decide changes are ready for review and run `reviewr
32
- request email@site.com`. This causes reviewr to:
61
+ ## Contributing Code
33
62
 
34
- * create a new branch with the name `review_0f38ef31` where `0f38ef31`
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
- The reviewer then looks over the code on github and comments as
44
- necessary. If the code is acceptable the reviewer runs `reviewr accept
45
- review_0f38ef31`. reviewr will then:
65
+ http://github.com/rhburrows/reviewr
66
+
67
+ # TODO
46
68
 
47
- * fetch the review branch
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
@@ -1,6 +1,8 @@
1
1
  require 'reviewr/cli/main'
2
+ require 'reviewr/cli/command'
2
3
  require 'reviewr/cli/help'
3
4
  require 'reviewr/cli/request'
5
+ require 'reviewr/cli/accept'
4
6
 
5
7
  module Reviewr
6
8
  module CLI
@@ -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
@@ -1,12 +1,8 @@
1
1
  module Reviewr
2
2
  module CLI
3
- class Help
4
- def call(command)
5
- if command.nil?
6
- usage
7
- else
8
- request_help
9
- end
3
+ class Help < Command
4
+ def execute
5
+ usage
10
6
  end
11
7
 
12
8
  def usage
@@ -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 :project
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
- @project = Project.new(args.shift)
11
+ @arguments = args
12
12
  @input, @output = input, output
13
13
  end
14
14
 
15
15
  def run
16
- case command
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 prompt_for_user(input = STDIN, output = STDOUT)
27
- output.print("Email (default #{project.user_email}): ")
28
- email = input.gets.chomp
29
- project.user_email = email unless email.empty?
30
-
31
- output.print("Email password: ")
32
- no_echo(input) do
33
- project.email_password = input.gets.chomp
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
- output.print("\n")
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
@@ -3,16 +3,12 @@ require 'forwardable'
3
3
 
4
4
  module Reviewr
5
5
  module CLI
6
- class Request
7
- attr_reader :project
8
-
9
- def initialize(project)
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
@@ -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 create_branch(branch_name)
24
- execute("git checkout -b #{branch_name}")
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
@@ -4,19 +4,32 @@ module Reviewr
4
4
  class Project
5
5
  extend Forwardable
6
6
 
7
- attr_reader :to, :git, :email_server
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(to, git = Git.instance)
15
- @to, @git = to, git
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
@@ -0,0 +1,6 @@
1
+ Hi,
2
+
3
+ I have reviewed your changes for branch <%= project.review_branch %> and decided to
4
+ merge them in.
5
+
6
+ Thanks!
@@ -1,3 +1,3 @@
1
1
  module Reviewr
2
- VERSION = "0.1.3"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 1
8
- - 3
9
- version: 0.1.3
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-13 00:00:00 -07:00
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