reviewr 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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