issue-beaver 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/enumerable/merge.rb +1 -1
- data/lib/issue_beaver/grammars.rb +3 -1
- data/lib/issue_beaver/grammars/github_issue_body.rb +11 -0
- data/lib/issue_beaver/grammars/github_issue_body.treetop +51 -0
- data/lib/issue_beaver/models/git.rb +11 -0
- data/lib/issue_beaver/models/github_issue.rb +28 -5
- data/lib/issue_beaver/models/merger.rb +19 -4
- data/lib/issue_beaver/models/todo_comments.rb +6 -4
- data/lib/issue_beaver/runner.rb +4 -3
- data/lib/issue_beaver/shared/attributes_model.rb +2 -0
- data/lib/issue_beaver/version.rb +1 -1
- data/spec/fixtures/github_issue.txt +4 -0
- data/spec/grammars/github_issue_body_spec.rb +34 -0
- data/spec/models/github_issue_spec.rb +19 -0
- metadata +9 -1
data/lib/enumerable/merge.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'treetop'
|
2
2
|
|
3
3
|
require 'issue_beaver/grammars/ruby_comments'
|
4
|
-
|
4
|
+
require 'issue_beaver/grammars/github_issue_body'
|
5
|
+
Treetop.load File.expand_path('../grammars/ruby_comments.treetop', __FILE__)
|
6
|
+
Treetop.load File.expand_path('../grammars/github_issue_body.treetop', __FILE__)
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module IssueBeaver
|
2
|
+
module Grammars
|
3
|
+
grammar GithubIssueBody
|
4
|
+
rule details
|
5
|
+
(mark / .)* {
|
6
|
+
def details
|
7
|
+
details = elements.find{|e| e.respond_to?(:is_mark?) }
|
8
|
+
return nil unless details
|
9
|
+
|
10
|
+
{
|
11
|
+
commit: details.commit_id.text_value.to_s,
|
12
|
+
begin: details.begin_line.text_value.to_i,
|
13
|
+
file: details.file.text_value.to_s
|
14
|
+
}
|
15
|
+
end
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
# Example for a mark:
|
20
|
+
# <!-- ISSUEBEAVER_ISSUE[fixtures/ruby.rb:2@879bee2f50] -->
|
21
|
+
# Update GithubIssueBody.build_mark when changing this!
|
22
|
+
rule mark
|
23
|
+
'<!--' white? magic_string '[' file ':' begin_line '@' commit_id ']' white? '-->' {
|
24
|
+
def is_mark?
|
25
|
+
true
|
26
|
+
end
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
rule magic_string
|
31
|
+
'ISSUEBEAVER_ISSUE'
|
32
|
+
end
|
33
|
+
|
34
|
+
rule file
|
35
|
+
[^:]+
|
36
|
+
end
|
37
|
+
|
38
|
+
rule begin_line
|
39
|
+
[0-9]+
|
40
|
+
end
|
41
|
+
|
42
|
+
rule commit_id
|
43
|
+
[0-9a-fA-F]+
|
44
|
+
end
|
45
|
+
|
46
|
+
rule white
|
47
|
+
[ \t]+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -45,6 +45,17 @@ module IssueBeaver
|
|
45
45
|
end
|
46
46
|
|
47
47
|
|
48
|
+
def head_commit
|
49
|
+
@head_commit ||= `git rev-parse HEAD`.chomp
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def is_ahead?(commit)
|
54
|
+
return false unless commit
|
55
|
+
!`git rev-list "#{head_commit}" | grep $(git rev-parse "#{commit}")`.chomp.empty?
|
56
|
+
end
|
57
|
+
|
58
|
+
|
48
59
|
private
|
49
60
|
|
50
61
|
def discover_root_dir(dir)
|
@@ -9,10 +9,14 @@ module IssueBeaver
|
|
9
9
|
@repository = repository
|
10
10
|
|
11
11
|
class << self
|
12
|
-
delegate :update, :create, :
|
12
|
+
delegate :update, :create, :first, to: :@repository
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
+
def self.default_attributes
|
17
|
+
@repository ? @repository.default_attributes : {}
|
18
|
+
end
|
19
|
+
|
16
20
|
|
17
21
|
def self.repo_name() @repository.repo if @repository end
|
18
22
|
|
@@ -23,8 +27,7 @@ module IssueBeaver
|
|
23
27
|
|
24
28
|
|
25
29
|
include Shared::AttributesModel
|
26
|
-
ATTRIBUTES = [:number, :state, :title, :body, :file, :begin_line, :created_at, :updated_at, :labels, :assignee]
|
27
|
-
|
30
|
+
ATTRIBUTES = [:number, :state, :title, :body, :file, :begin_line, :created_at, :updated_at, :labels, :assignee, :commit]
|
28
31
|
|
29
32
|
def closed?() state == "closed" end
|
30
33
|
|
@@ -41,6 +44,15 @@ module IssueBeaver
|
|
41
44
|
def must_update?() changed_attributes_for_update.any? end
|
42
45
|
|
43
46
|
|
47
|
+
def must_close?() !!@must_close end
|
48
|
+
|
49
|
+
|
50
|
+
def mark_for_closing(flag = true)
|
51
|
+
@must_close = flag
|
52
|
+
self.state = "closed"
|
53
|
+
end
|
54
|
+
|
55
|
+
|
44
56
|
def changed_attributes_for_update
|
45
57
|
Hashie::Mash.new(changed_attributes).only(:title, :body, :assignee)
|
46
58
|
end
|
@@ -71,6 +83,14 @@ module IssueBeaver
|
|
71
83
|
self.created_at = Time.parse(created_at) if created_at.kind_of?(String)
|
72
84
|
self.updated_at = Time.parse(updated_at) if updated_at.kind_of?(String)
|
73
85
|
self.assignee = assignee.login if assignee.respond_to?(:login)
|
86
|
+
self.body ||= ""
|
87
|
+
|
88
|
+
if details = Grammars::GithubIssueBodyParser.new.parse(self.body).details
|
89
|
+
self.commit = details[:commit]
|
90
|
+
self.begin_line = details[:begin]
|
91
|
+
self.file = details[:file]
|
92
|
+
end
|
93
|
+
|
74
94
|
@changed_attributes = nil
|
75
95
|
end
|
76
96
|
|
@@ -80,10 +100,13 @@ module IssueBeaver
|
|
80
100
|
self.title ||= ""
|
81
101
|
self.title = "#{self.title.capitalize} (#{basename}:#{self.begin_line})"
|
82
102
|
github_line_url = "https://github.com/#{self.class.repo_name}/blob/master/#{self.file}\#L#{self.begin_line}"
|
83
|
-
self.body = %Q{#{self.body}\n\n#{github_line_url}}
|
103
|
+
self.body = %Q{#{self.body}\n\n#{github_line_url}\n\n#{magic_mark}}
|
84
104
|
@changed_attributes = nil
|
85
105
|
end
|
86
106
|
|
107
|
+
def magic_mark
|
108
|
+
Grammars::GithubIssueBody.build_mark(file, begin_line, commit)
|
109
|
+
end
|
87
110
|
|
88
111
|
def update_attributes_with_limit(attrs)
|
89
112
|
update_attributes_without_limit(attrs.only(:title, :body, :updated_at, :assignee))
|
@@ -97,7 +120,7 @@ module IssueBeaver
|
|
97
120
|
elsif changed?
|
98
121
|
@attributes = self.class.update(number, attributes)
|
99
122
|
end
|
100
|
-
@changed_attributes.clear
|
123
|
+
@changed_attributes.clear if @changed_attributes
|
101
124
|
true
|
102
125
|
end
|
103
126
|
|
@@ -4,9 +4,10 @@ module IssueBeaver
|
|
4
4
|
module Models
|
5
5
|
class Merger
|
6
6
|
|
7
|
-
def initialize(issues, todos)
|
7
|
+
def initialize(issues, todos, repo)
|
8
8
|
@issues = issues
|
9
9
|
@matcher = Matcher.new(@issues, todos)
|
10
|
+
@repo = repo
|
10
11
|
end
|
11
12
|
|
12
13
|
|
@@ -21,12 +22,14 @@ module IssueBeaver
|
|
21
22
|
|
22
23
|
|
23
24
|
def changed
|
24
|
-
@changed ||=
|
25
|
+
@changed ||= added.merge_left(modified).merge_left(closed, :number)
|
25
26
|
end
|
26
27
|
|
27
28
|
|
28
|
-
|
29
|
-
|
29
|
+
def closed
|
30
|
+
@closed ||= unmatched_issues.select(&:must_close?)
|
31
|
+
end
|
32
|
+
|
30
33
|
|
31
34
|
def merged_issues
|
32
35
|
@merged_issues ||=
|
@@ -41,6 +44,18 @@ module IssueBeaver
|
|
41
44
|
issue
|
42
45
|
end
|
43
46
|
end
|
47
|
+
|
48
|
+
# TODO: Tidy up a bit
|
49
|
+
def unmatched_issues
|
50
|
+
@unmatched_issues ||=
|
51
|
+
Enumerator.new do |yielder|
|
52
|
+
@issues.to_a.each do |issue|
|
53
|
+
next if merged_issues.to_a.map(&:number).include? issue.number
|
54
|
+
issue.mark_for_closing if @repo.is_ahead?(issue.commit)
|
55
|
+
yielder << issue
|
56
|
+
end
|
57
|
+
end.memoizing.lazy
|
58
|
+
end
|
44
59
|
end
|
45
60
|
|
46
61
|
|
@@ -9,21 +9,22 @@ module IssueBeaver
|
|
9
9
|
module Models
|
10
10
|
class TodoComments
|
11
11
|
|
12
|
-
def initialize(root_dir, files)
|
12
|
+
def initialize(root_dir, files, head)
|
13
13
|
@root_dir = root_dir
|
14
14
|
@files = files
|
15
|
+
@head = head
|
15
16
|
end
|
16
17
|
|
17
18
|
|
18
19
|
def all
|
19
|
-
@todos ||= enum_scanned_files(@files).memoizing.lazy
|
20
|
+
@todos ||= enum_scanned_files(@files, @head).memoizing.lazy
|
20
21
|
end
|
21
22
|
|
22
23
|
|
23
24
|
private
|
24
25
|
|
25
26
|
# TODO: Allow individual TODOs to follow right after each other without newline in between
|
26
|
-
def enum_scanned_files(files)
|
27
|
+
def enum_scanned_files(files, head_commit)
|
27
28
|
Enumerator.new do |yielder|
|
28
29
|
todos = []
|
29
30
|
parser = Grammars::RubyCommentsParser.new
|
@@ -34,7 +35,8 @@ module IssueBeaver
|
|
34
35
|
yielder << new_todo(
|
35
36
|
comment.merge('file' => relative_path(file),
|
36
37
|
'created_at' => File.ctime(file),
|
37
|
-
'updated_at' => File.ctime(file)
|
38
|
+
'updated_at' => File.ctime(file),
|
39
|
+
'commit' => head_commit
|
38
40
|
))
|
39
41
|
}
|
40
42
|
end
|
data/lib/issue_beaver/runner.rb
CHANGED
@@ -119,7 +119,8 @@ module IssueBeaver
|
|
119
119
|
@todo_comments ||=
|
120
120
|
Models::TodoComments.new(
|
121
121
|
repo(config).root_dir,
|
122
|
-
repo(config).files(config['dir'])
|
122
|
+
repo(config).files(config['dir']),
|
123
|
+
repo(config).head_commit
|
123
124
|
)
|
124
125
|
end
|
125
126
|
|
@@ -139,8 +140,8 @@ module IssueBeaver
|
|
139
140
|
end
|
140
141
|
|
141
142
|
|
142
|
-
def merger(a, b)
|
143
|
-
@merger ||= Models::Merger.new(a, b)
|
143
|
+
def merger(a, b, repo = repo)
|
144
|
+
@merger ||= Models::Merger.new(a, b, repo)
|
144
145
|
end
|
145
146
|
|
146
147
|
|
data/lib/issue_beaver/version.rb
CHANGED
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe IssueBeaver::Grammars::RubyCommentsParser do
|
4
|
+
subject { IssueBeaver::Grammars::GithubIssueBodyParser.new.parse(body) }
|
5
|
+
let(:details) {subject.details}
|
6
|
+
|
7
|
+
context "github_issue.txt" do
|
8
|
+
let(:body) { File.read('./spec/fixtures/github_issue.txt') }
|
9
|
+
|
10
|
+
it "should find a magic mark" do
|
11
|
+
details.should_not == nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should find the file name" do
|
15
|
+
details[:file].should == "fixtures/ruby.rb"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should find the line number" do
|
19
|
+
details[:begin].should == 2
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should find the commit id" do
|
23
|
+
details[:commit].should == "879bee2f50"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "with empty body" do
|
28
|
+
let(:body){""}
|
29
|
+
|
30
|
+
it "should return nil" do
|
31
|
+
details.should == nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe IssueBeaver::Models::GithubIssue do
|
4
|
+
let(:body) { File.read('./spec/fixtures/github_issue.txt') }
|
5
|
+
subject { IssueBeaver::Models::GithubIssue.new_from_github(body: body, title: "Foo") }
|
6
|
+
let(:attrs) { subject.attributes }
|
7
|
+
|
8
|
+
it "should find the file name" do
|
9
|
+
attrs[:file].should == "fixtures/ruby.rb"
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should find the line number" do
|
13
|
+
attrs[:begin_line].should == 2
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should find the commit id" do
|
17
|
+
attrs[:commit].should == "879bee2f50"
|
18
|
+
end
|
19
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: issue-beaver
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -204,6 +204,8 @@ files:
|
|
204
204
|
- lib/enumerable/merge.rb
|
205
205
|
- lib/issue_beaver.rb
|
206
206
|
- lib/issue_beaver/grammars.rb
|
207
|
+
- lib/issue_beaver/grammars/github_issue_body.rb
|
208
|
+
- lib/issue_beaver/grammars/github_issue_body.treetop
|
207
209
|
- lib/issue_beaver/grammars/ruby_comments.rb
|
208
210
|
- lib/issue_beaver/grammars/ruby_comments.treetop
|
209
211
|
- lib/issue_beaver/models.rb
|
@@ -219,9 +221,12 @@ files:
|
|
219
221
|
- lib/issue_beaver/shared/model_collection.rb
|
220
222
|
- lib/issue_beaver/version.rb
|
221
223
|
- lib/password/password.rb
|
224
|
+
- spec/fixtures/github_issue.txt
|
222
225
|
- spec/fixtures/ruby.rb
|
223
226
|
- spec/fixtures/ruby2.rb
|
227
|
+
- spec/grammars/github_issue_body_spec.rb
|
224
228
|
- spec/grammars/ruby_comments_spec.rb
|
229
|
+
- spec/models/github_issue_spec.rb
|
225
230
|
- spec/spec_helper.rb
|
226
231
|
homepage: https://github.com/eckardt/issue-beaver
|
227
232
|
licenses: []
|
@@ -248,7 +253,10 @@ signing_key:
|
|
248
253
|
specification_version: 3
|
249
254
|
summary: Issue Beaver creates Github Issues for TODO comments in your source code
|
250
255
|
test_files:
|
256
|
+
- spec/fixtures/github_issue.txt
|
251
257
|
- spec/fixtures/ruby.rb
|
252
258
|
- spec/fixtures/ruby2.rb
|
259
|
+
- spec/grammars/github_issue_body_spec.rb
|
253
260
|
- spec/grammars/ruby_comments_spec.rb
|
261
|
+
- spec/models/github_issue_spec.rb
|
254
262
|
- spec/spec_helper.rb
|