git_trello_post_commit 0.1.1 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 471b1b00e71f8ff30557b7b4aafcfea9bff20e0b
4
- data.tar.gz: 5259e88d12923ffc55720fb1e22c9251685fe9dd
3
+ metadata.gz: 83151148c861df200b9f55ce1d2c435b06166ae8
4
+ data.tar.gz: 2a62f11e42798ed4a432a4cdbc398d554abd72c3
5
5
  SHA512:
6
- metadata.gz: e80dbf6367d3f4b5a829a3bbeb35f6176fb1a205b2ce795b21e61210e083b9d999b01c486b860552cffeccfd036a31330aab651fc5a8463ce3f2767c3f55f153
7
- data.tar.gz: 1c4433c99c886420ef7015b9d65771b01b4bc908f9b3885a104dbf41beaf345092dfa1cf57ec51b9e84f065c6dab6b8ec6be47b5667995f0ad7fc1f545c7e2d1
6
+ metadata.gz: ddcdb7dadd6bc97e2b15c7de0c25835a0cd7d2f9fb87fc7ddf570733a9e0642153020c3186a3854d2cdbe45ea0338b7746833dbea3a2cb0e394c4e43f064dc07
7
+ data.tar.gz: f615d024ba608c5c4286b21067c907cdc6d03863663d33e0d504abac1dfb5b42771ac6b97b8a94d73f4052a1e532148432b74970f8a63c630d81e9fd2e15a317
data/Gemfile CHANGED
@@ -5,4 +5,5 @@ gemspec
5
5
 
6
6
  group :development do
7
7
  gem 'pry'
8
+ gem 'pry-byebug'
8
9
  end
data/README.md CHANGED
@@ -62,15 +62,34 @@ If you omit the parameter, a link won't be added.
62
62
 
63
63
  ## Usage
64
64
 
65
- The gem uses the following regex to detect a reference to a card:
65
+ The commit-message parser looks for the following keywords:
66
66
 
67
- /((case|card|close|fix)e?s? \D?([0-9]+))/i
67
+ * DONE_KEYWORDS:
68
+ * close
69
+ * fix
70
+ * IN_PROGRESS_KEYWORDS:
71
+ * ref
72
+ * reference
73
+ * case
74
+ * issue
75
+ * card
68
76
 
69
- So an example commit message might be:
77
+ For DONE_KEYWORDS, the card is moved to the list with id `LIST_ID_DONE`, while for IN_PROGRESS_KEYWORDS it is moved to the list with id `LIST_ID_IN_PROGRESS`.
70
78
 
71
- Added that awesome feature closes #42
79
+ After it has found a keyword, the parser then looks for space, comma or semi-colon separated lists of (hash-prefixed) ids, e.g.:
80
+
81
+ closes #1
82
+ closes #1 #2 #3
83
+ closes #1, #2, #3
84
+ closes #1, #2 and #3
85
+ closes #1, #2, and #3
86
+ closes #1, #2 & #3
87
+ closes #1, #2, & #3
88
+
89
+ So a typical commit message might be:
90
+
91
+ Added that awesome feature, refs #42 and closes #1, #2 and #3.
72
92
 
73
- For the 'case' and 'card' keywords, the card is moved to the list with id `LIST_ID_IN_PROGRESS`, while for keywords 'close' and 'fix', the card is moved to the list with id `LIST_ID_DONE` .
74
93
 
75
94
  ## Contributing
76
95
 
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.3"
22
22
  spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
23
24
 
24
25
  spec.add_runtime_dependency 'json'
25
26
  spec.add_runtime_dependency "git", "~> 1.2"
@@ -1,3 +1,3 @@
1
1
  module GitTrelloPostCommit
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -5,6 +5,73 @@ require "git"
5
5
 
6
6
  module GitTrelloPostCommit
7
7
 
8
+ DONE_KEYWORDS = [:close, :fix]
9
+ IN_PROGRESS_KEYWORDS = [:ref, :case, :issue, :card]
10
+
11
+ KEYWORD_REGEX = /((ref)(?:erence)?s?|(case)s?|(issue)s?|(card)s?|(close)s?|(fix)(?:es)?)/i
12
+ ID_REGEX = /\#(?:\d+)/
13
+ ID_LIST_REGEX = /\s*((?:#{ID_REGEX}(?:[,;]?\s+(?:and|\&)|[,;])?\s*)+)/i
14
+ REF_REGEX = /#{KEYWORD_REGEX}\s+#{ID_LIST_REGEX}/
15
+
16
+ ID_CAPTURE_REGEX = /\#(\d+)/
17
+
18
+ class MessageParser
19
+
20
+ def initialize(message)
21
+ @message = message
22
+ end
23
+
24
+ def instructions
25
+ @instructions ||= parse_instructions
26
+ end
27
+
28
+ def parse_instructions
29
+ raw_matches = get_raw_matches
30
+ matches = extract_instructions(raw_matches)
31
+ normalize_matches(matches)
32
+ end
33
+
34
+ def get_raw_matches
35
+ @message.scan(REF_REGEX).map do |match|
36
+ match.compact[1..-1]
37
+ end
38
+ end
39
+
40
+ def categorize_keyword(keyword)
41
+ if DONE_KEYWORDS.include?(keyword) then :done
42
+ elsif IN_PROGRESS_KEYWORDS.include?(keyword) then :in_progress
43
+ end
44
+ end
45
+
46
+ def extract_instructions(raw_matches)
47
+ raw_matches.reduce(Hash.new { |hash, key| hash[key] = [] }) do |matches, raw_match|
48
+ keyword = raw_match.first.downcase.to_sym
49
+ id_list = raw_match.last
50
+ if keyword and id_list
51
+ ids = id_list.scan(ID_CAPTURE_REGEX).flatten.map(&:to_i).select { |n| n>0 }
52
+ ids.each do |id|
53
+ matches[id] << keyword
54
+ end
55
+ end
56
+ matches
57
+ end
58
+ end
59
+
60
+ def normalize_matches(matches)
61
+ matches.reduce({}) do |norm_matches, (card_id, keywords)|
62
+ keywords.uniq!
63
+ categories = keywords.map { |kw| categorize_keyword(kw) }.compact.uniq
64
+ if categories.length > 1 and categories.include?(:done)
65
+ norm_matches[card_id] = :done
66
+ elsif categories.length == 1
67
+ norm_matches[card_id] = categories.first
68
+ end
69
+ norm_matches
70
+ end
71
+ end
72
+
73
+ end
74
+
8
75
  class Hook
9
76
 
10
77
  def initialize(config)
@@ -26,41 +93,41 @@ module GitTrelloPostCommit
26
93
  commit = @repo.gcommit('HEAD')
27
94
  new_sha = commit.sha
28
95
 
29
- # Figure out the card short id
30
- match = commit.message.match(/((case|card|close|fix)e?s? \D?([0-9]+))/i)
31
- return unless match and match[3].to_i > 0
96
+ parser = MessageParser.new(commit.message)
32
97
 
33
- puts "Trello: Commenting on card ##{match[3].to_i}"
98
+ parser.instructions.each do |card_id, action|
34
99
 
35
- results = @http.get_card(@board_id, match[3].to_i)
36
- unless results
37
- puts "Trello: Cannot find card matching ID #{match[3]}"
38
- return
39
- end
40
- results = JSON.parse(results)
100
+ puts "Trello: Commenting on card ##{card_id}"
41
101
 
42
- # Determine the action to take
43
- target_list_id = ""
44
- target_list_id = case match[2].downcase
45
- when "case", "card" then @list_id_in_progress
46
- when "close", "fix" then @list_id_done
47
- end
48
-
49
- # Add the commit comment
50
- message = "#{commit.author.name}:\n#{commit.message}"
51
- message << "\n\n#{@commit_url_prefix}#{new_sha}" unless @commit_url_prefix.nil?
52
- message.gsub!(match[1], "")
53
- message.gsub!(/\(\)$/, "")
54
- message.gsub!(/Signed-off-by: (.*) <(.*)>/,"")
55
- @http.add_comment(results["id"], message)
56
-
57
- unless target_list_id == ""
58
- to_update = {}
59
- unless results["idList"] == target_list_id
60
- puts "Trello: Moving card ##{match[3].to_i} to list #{target_list_id}"
61
- to_update[:idList] = target_list_id
62
- @http.update_card(results["id"], to_update)
102
+ results = @http.get_card(@board_id, card_id)
103
+ unless results
104
+ puts "Trello: Cannot find card matching ID #{card_id}"
105
+ next
106
+ end
107
+ results = JSON.parse(results)
108
+
109
+ # Determine the action to take
110
+ target_list_id = case action
111
+ when :in_progress then @list_id_in_progress
112
+ when :done then @list_id_done
113
+ end
114
+
115
+ # Add the commit comment
116
+ message = "#{commit.author.name}:\n#{commit.message}"
117
+ message << "\n\n#{@commit_url_prefix}#{new_sha}" unless @commit_url_prefix.nil?
118
+ message.gsub!(/\(\)$/, "")
119
+ message.gsub!(/Signed-off-by: (.*) <(.*)>/,"")
120
+ @http.add_comment(results["id"], message)
121
+
122
+ if target_list_id
123
+ to_update = {}
124
+ unless results["idList"] == target_list_id
125
+ puts "Trello: Moving card ##{card_id} to list #{target_list_id}"
126
+ to_update[:idList] = target_list_id
127
+ @http.update_card(results["id"], to_update)
128
+ end
63
129
  end
130
+
64
131
  end
65
132
 
66
133
  end
@@ -0,0 +1,72 @@
1
+ require 'rspec'
2
+ require 'pry'
3
+ require 'git_trello_post_commit'
4
+
5
+ module GitTrelloPostCommit
6
+
7
+ describe MessageParser do
8
+ it "parses single instructions at the end of the message" do
9
+ parser = MessageParser.new("fixed that thing, closes #99")
10
+ expect(parser.instructions).to eq({99 => :done})
11
+ end
12
+
13
+ it "parses single instructions in the middle of a message" do
14
+ parser = MessageParser.new("fixed that thing, closes #99, blah, blah, blah")
15
+ expect(parser.instructions).to eq({99 => :done})
16
+ end
17
+
18
+ it "parses multiple distinct references" do
19
+ parser = MessageParser.new("fixed that thing, closes #99, refs #100")
20
+ expect(parser.instructions).to eq({99 => :done, 100 => :in_progress})
21
+ end
22
+
23
+ it "parses a space separated list of ids for a single action keyword" do
24
+ parser = MessageParser.new("fixed that thing, closes #99 #100 #101")
25
+ expect(parser.instructions).to eq({99 => :done, 100 => :done, 101 => :done})
26
+ end
27
+
28
+ it "parses a csv list of ids for a single action keyword" do
29
+ parser = MessageParser.new("fixed that thing, closes #99, #100, #101")
30
+ expect(parser.instructions).to eq({99 => :done, 100 => :done, 101 => :done})
31
+ end
32
+
33
+ it "parses a csv list of ids (ending with an 'and') for a single action keyword" do
34
+ parser = MessageParser.new("fixed that thing, closes #99, #100 and #101")
35
+ expect(parser.instructions).to eq({99 => :done, 100 => :done, 101 => :done})
36
+ end
37
+
38
+ it "parses a csv list of ids (ending with an '&') for a single action keyword" do
39
+ parser = MessageParser.new("fixed that thing, closes #99, #100 & #101")
40
+ expect(parser.instructions).to eq({99 => :done, 100 => :done, 101 => :done})
41
+ end
42
+
43
+ it "parses a csv list of ids (ending with an ', and') for a single action keyword" do
44
+ parser = MessageParser.new("fixed that thing, closes #99, #100, and #101")
45
+ expect(parser.instructions).to eq({99 => :done, 100 => :done, 101 => :done})
46
+ end
47
+
48
+ it "parses multiple lists of ids for a single action keyword" do
49
+ parser = MessageParser.new("fixed that thing, closes #99, #100 & #101 and refs #1 & #2")
50
+ expect(parser.instructions).to eq({
51
+ 99 => :done, 100 => :done, 101 => :done,
52
+ 1 => :in_progress, 2 => :in_progress
53
+ })
54
+ end
55
+
56
+ it "ignores duplicate instructions" do
57
+ parser = MessageParser.new("fixed that thing on card #99, refs #99")
58
+ expect(parser.instructions).to eq({99 => :in_progress})
59
+ end
60
+
61
+ it "ignores duplicate instructions give in a list" do
62
+ parser = MessageParser.new("fixed the things on cards #99 and #100, refs #99, #100")
63
+ expect(parser.instructions).to eq({99 => :in_progress, 100 => :in_progress})
64
+ end
65
+
66
+ it "ignores conflicting instructions, favouring :done over :in_progress" do
67
+ parser = MessageParser.new("fixed that thing on card #99, closes #99")
68
+ expect(parser.instructions).to eq({99 => :done})
69
+ end
70
+ end
71
+
72
+ end
metadata CHANGED
@@ -1,69 +1,83 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git_trello_post_commit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - jmchambers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-18 00:00:00.000000000 Z
11
+ date: 2014-03-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.3'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.3'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
39
53
  - !ruby/object:Gem::Version
40
54
  version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: json
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
- - - '>='
59
+ - - ">="
46
60
  - !ruby/object:Gem::Version
47
61
  version: '0'
48
62
  type: :runtime
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
- - - '>='
66
+ - - ">="
53
67
  - !ruby/object:Gem::Version
54
68
  version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: git
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
- - - ~>
73
+ - - "~>"
60
74
  - !ruby/object:Gem::Version
61
75
  version: '1.2'
62
76
  type: :runtime
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
- - - ~>
80
+ - - "~>"
67
81
  - !ruby/object:Gem::Version
68
82
  version: '1.2'
69
83
  description: 'This gem can be used in a post-commit hook in any Git repository to
@@ -76,7 +90,7 @@ executables: []
76
90
  extensions: []
77
91
  extra_rdoc_files: []
78
92
  files:
79
- - .gitignore
93
+ - ".gitignore"
80
94
  - Gemfile
81
95
  - LICENSE.txt
82
96
  - README.md
@@ -85,6 +99,7 @@ files:
85
99
  - lib/git_trello_post_commit.rb
86
100
  - lib/git_trello_post_commit/trello-http.rb
87
101
  - lib/git_trello_post_commit/version.rb
102
+ - spec/message_parser_spec.rb
88
103
  homepage: ''
89
104
  licenses:
90
105
  - MIT
@@ -95,18 +110,20 @@ require_paths:
95
110
  - lib
96
111
  required_ruby_version: !ruby/object:Gem::Requirement
97
112
  requirements:
98
- - - '>='
113
+ - - ">="
99
114
  - !ruby/object:Gem::Version
100
115
  version: '0'
101
116
  required_rubygems_version: !ruby/object:Gem::Requirement
102
117
  requirements:
103
- - - '>='
118
+ - - ">="
104
119
  - !ruby/object:Gem::Version
105
120
  version: '0'
106
121
  requirements: []
107
122
  rubyforge_project:
108
- rubygems_version: 2.1.10
123
+ rubygems_version: 2.2.2
109
124
  signing_key:
110
125
  specification_version: 4
111
126
  summary: Update Trello cards with git commit messages.
112
- test_files: []
127
+ test_files:
128
+ - spec/message_parser_spec.rb
129
+ has_rdoc: