jiragit 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c227df23b5b4a750360a0e4c071e33350fb9cce3
4
+ data.tar.gz: 5dbc5bebb53abb4fc137e2f6239528b2659a5e31
5
+ SHA512:
6
+ metadata.gz: 89dee5bc0243afa9887a7d5583c71325f101e078f2dda969d9c36a82cea3653db2acbd554e3044490e7f5c40cc07c877d0a35326b53f8ba2dd245ee7bcce61a0
7
+ data.tar.gz: 714bd81531c4661ea27d916feed9af42f053fb2b6fad1db955fd6d6a2a2724c59ce13f63cd1d320be5a513fe3209db530393b6a9062fbb28f2506954941bc582
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Derrick Parkhurst
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,112 @@
1
+ # Jiragit
2
+
3
+ A gem to smooth the integration of JIRA and Git in a common software development workflow. Jiragit automates the insertion of JIRA numbers and hyperlinks into Git commits, and provides a tool useful for accessing JIRA and Github while working on the command line. Pronounced 'drag it'.
4
+
5
+ ## Context
6
+
7
+ JIRA is an online service used by software development teams to track software issues as well as plan and track software development work. Each issue is assigned a number, for example PA-12345. Issues include a title, a description, user comments as well as a number of other customizable data fields. JIRA can integrate with Github. Github is an online service that provides Git repository hosting as well as a suite of software management tools, including code reviews and issue tracking. When integrated, JIRA can track pull requests and commits pushed to Github and relate them to JIRA issue numbers when the pull request description or the commit messages contain a JIRA issue number. This integration is useful in many ways, including for historical code analysis.
8
+
9
+ A frequently used workflow for automatically including JIRA issue numbers into commit messages is to take advantage of [Git repository hooks](http://git-scm.com/book/en/Customizing-Git-Git-Hooks). With the [appropriate hook installed](https://bitbucket.org/tpettersen/prepare-commit-msg-jira) into a repository, and a Git branch name that begins with a JIRA issue number, the JIRA number will be prepended to all commit messages made on that branch. JIRA will thus recognize the relation between the commit and the JIRA when the commit is pushed to Github, and the JIRA issue will be updated with a reference to the commit automatically.
10
+
11
+ While this workflow automates the insertion of JIRA numbers into commit messages, it has three negative side affects on Git usage. First, JIRA numbers, being serially determined, can be hard for a developer to remember, especially when multitasking on multiple issues or working in a repository shared by many developers. Beginning a branch name with a JIRA issue number requires developers to recall an arbitrary JIRA issue number while working and thus places an unnecessary cognitive burden on the developer. Second, JIRA numbers without links to the JIRA content, force the developer to take the extra steps of accessing the JIRA issue through an online search. Third, the JIRA issue numbers use up precious space in the title of the commit message and dilute the usefulness of many Git tools that only or preferentially report the first line of each commit message.
12
+
13
+ ## Solution
14
+
15
+ Install the jiragit gem (globally)
16
+ ```
17
+ $ gem install jiragit
18
+ ```
19
+ Configure your JIRA URL
20
+ ```
21
+ $ jiragit configure jira_url https://yourcompany.atlassian.net
22
+ ```
23
+ Use jiragit to install the custom Git hooks into your repository
24
+ ```
25
+ $ cd repository
26
+ $ jiragit install
27
+ ```
28
+ When creating a feature branch, choose a name that you will be able to easily remember, such as a short word phrase. It is always easier to remember branch names that you create yourself and that are meaningful:
29
+ ```
30
+ $ git checkout -b improve-one-click-purchase-form
31
+ Switched to a new branch 'improve-one-click-purchase-form'
32
+ What is the JIRA Number? []>
33
+ ```
34
+ Then enter the JIRA number that describes this work, for example PA-12345.
35
+ ```
36
+ $ git checkout -b improve-one-click-purchase-form
37
+ Switched to a new branch 'improve-one-click-purchase-form'
38
+ What is the JIRA Number? []> PA-12345
39
+ ```
40
+ Jiragit will remember the relationship between the Git branch name and the JIRA number. When you go to create a new commit Jiragit will automiatically insert
41
+ the JIRA issue number and a hyperlink to the end of the body of the commit message.
42
+ ```
43
+ $ git commit -m 'revised the form layout by updating the css'
44
+ [improve-one-click-purchase-form 3fde944] revised the form layout by updating the css
45
+ 1 file changed, 0 insertions(+), 0 deletions(-)
46
+ create mode 100644 form.css
47
+
48
+ $ git log -1
49
+ commit 3fde94467d55bff68e78d05324ead6687b4127ce
50
+ Author: Derrick Parkhurst <derrick.parkhurst@gmail.com>
51
+ Date: Mon Sep 8 17:49:14 2014 -0500
52
+
53
+ revised the form layout by updating the css
54
+
55
+ PA-12345: https://yourcompany.atlassian.net/browse/PA-12345
56
+
57
+ ```
58
+ Once you push this commit to Github, JIRA will recognize this commit as belonging to the JIRA issue because the JIRA/Github integration searches the entire body of the commit message for JIRA issue numbers.
59
+
60
+ Jiragit also provides a convenient way to navigate to JIRA issues via the command line. You can view the JIRA for the current branch in your browser:
61
+ ```
62
+ $ jiragit browse jira
63
+ ```
64
+ or view a specified JIRA in your browser
65
+ ```
66
+ $ jiragit PA-54321
67
+ ```
68
+
69
+ Jiragit also provides a convenient way to navigate to branches on Github via the command line. You can view the Github page for the current branch in your browser:
70
+ ```
71
+ $ jiragit browse
72
+ ```
73
+ or view the specified branch in your browser
74
+ ```
75
+ $ jiragit browse improve-one-click-purchase-form
76
+ ```
77
+
78
+ ## Requirements
79
+
80
+ * JIRA - http://www.atlassian.com/software/jira
81
+ * Github - http://github.com
82
+ * Git - http://git-scm.com/
83
+ * Ruby - https://www.ruby-lang.org/en/
84
+
85
+ ## Tested on
86
+
87
+ * Mac OS X 10.8 - Ruby MRI 2.1.0
88
+
89
+ ## License
90
+
91
+ Copyright (c) 2014 Derrick Parkhurst derrick.parkhurst@gmail.com
92
+
93
+ MIT License
94
+
95
+ Permission is hereby granted, free of charge, to any person obtaining
96
+ a copy of this software and associated documentation files (the
97
+ "Software"), to deal in the Software without restriction, including
98
+ without limitation the rights to use, copy, modify, merge, publish,
99
+ distribute, sublicense, and/or sell copies of the Software, and to
100
+ permit persons to whom the Software is furnished to do so, subject to
101
+ the following conditions:
102
+
103
+ The above copyright notice and this permission notice shall be
104
+ included in all copies or substantial portions of the Software.
105
+
106
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
107
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
108
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
109
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
110
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
111
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
112
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,18 @@
1
+ require 'rspec/core/rake_task'
2
+ require 'bundler/gem_tasks'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
8
+ namespace :gem do
9
+
10
+ task :build do
11
+ `gem build jiragit.gemspec`
12
+ end
13
+
14
+ task :install => [:build] do
15
+ `gem install pkg/jiragit-1.0.0.gem`
16
+ end
17
+
18
+ end
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'jiragit'
4
+
5
+ Jiragit::Cli.new(ARGV)
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'jiragit'
4
+
5
+ @log = Jiragit::Logger.new(".git/jiragit/hooks.log")
6
+ @log.hook = File.basename $0
7
+ @log.level = Logger::DEBUG
8
+
9
+ @log.debug "launched"
10
+
11
+ STDIN.reopen('/dev/tty')
12
+
13
+ def commit_message_filename
14
+ ARGV[0]
15
+ end
16
+
17
+ def commit_message
18
+ message = File.read(commit_message_filename)
19
+ @log.debug "#{__method__}\n#{message}"
20
+ message
21
+ end
22
+
23
+ def query(message, default=nil)
24
+ if default
25
+ print "#{message} [#{default}]> "
26
+ else
27
+ print "#{message} > "
28
+ end
29
+ response = STDIN.gets.chomp
30
+ if response.empty? && default
31
+ default
32
+ else
33
+ response
34
+ end
35
+ end
36
+
37
+ def message(message)
38
+ puts message
39
+ @log.debug message
40
+ end
41
+
42
+ def fail(message)
43
+ error_message = "Commit rejected: #{message}"
44
+ @log.debug error_message
45
+ $stderr.puts error_message
46
+
47
+ response = query("Edit commit message?", "yes")
48
+ if response[0].downcase == 'y'
49
+ root_dir = Jiragit::Git.repository_root
50
+ hook_dir = ".git/hooks"
51
+ exec("cd #{root_dir}; $GIT_EDITOR '#{commit_message_filename}'; #{hook_dir}/commit-msg '#{commit_message_filename}'")
52
+ else
53
+ $stderr.puts "Commit aborted"
54
+ exit 1
55
+ end
56
+
57
+ end
58
+
59
+ def enforce_a_title(line = "")
60
+ return unless line.length == 0
61
+ fail "The title of the commit message must be present"
62
+ end
63
+
64
+ def enforce_title_length(line)
65
+ return unless line.length > 50
66
+ fail "The title of the commit message must be no longer than 50 characters"
67
+ end
68
+
69
+ def enforce_body_length(line)
70
+ return unless line.length > 72
71
+ fail "Lines in the body of the commit message must be no longer than 72 characters"
72
+ end
73
+
74
+ def enforce_blank_line(line)
75
+ return unless line.length > 0
76
+ fail "The commit title and body must be separated by a single blank line"
77
+ end
78
+
79
+ def enforce_commit_message_length
80
+ lines = commit_message.split(/\n/)
81
+ title = lines.shift
82
+ enforce_a_title(title)
83
+ enforce_title_length(title)
84
+ puts lines
85
+ enforce_blank_line(lines.shift) if lines.any?
86
+ lines.each { |line| enforce_body_length(line) } if lines.any?
87
+ end
88
+
89
+ enforce_commit_message_length
90
+
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'jiragit'
4
+
5
+ @log = Jiragit::Logger.new(".git/jiragit/hooks.log")
6
+ @log.hook = File.basename $0
7
+ @log.level = Logger::DEBUG
8
+
9
+ @log.debug "launched"
10
+
11
+ STDIN.reopen('/dev/tty')
12
+
13
+ def previous_head
14
+ ARGV[0]
15
+ end
16
+
17
+ def new_head
18
+ ARGV[1]
19
+ end
20
+
21
+ def branch_switch?
22
+ ARGV[2] == '1'
23
+ end
24
+
25
+ def current_branch
26
+ Jiragit::Git.current_branch
27
+ end
28
+
29
+ def previous_branch
30
+ Jiragit::Git.previous_branch
31
+ end
32
+
33
+ def new_branch?
34
+ current_branch != previous_branch
35
+ end
36
+
37
+ def detached_head
38
+ current_branch==''
39
+ end
40
+
41
+ def master_branch
42
+ current_branch=='master'
43
+ end
44
+
45
+ def query(message, default=nil)
46
+ if default
47
+ print "#{message} [#{default}]> "
48
+ else
49
+ print "#{message} > "
50
+ end
51
+ response = STDIN.gets.chomp
52
+ if response.empty? && default
53
+ default
54
+ else
55
+ response
56
+ end
57
+ end
58
+
59
+ def message(message)
60
+ puts message
61
+ @log.debug message
62
+ end
63
+
64
+ def jira_store
65
+ @jira_store ||= Jiragit::JiraStore.new(".git/jiragit/jira_store")
66
+ end
67
+
68
+ def related_jiras(params)
69
+ jira_store
70
+ .relations(params)
71
+ .to_a
72
+ .select { |r| r.type == :jira }
73
+ .map(&:label)
74
+ end
75
+
76
+ def relate_jiras_to_current_branch(jiras)
77
+ return if jiras.empty?
78
+ jiras.gsub(/\s/,'').split(',').each do |jira|
79
+ jira_store.relate(jira: jira, branch: current_branch) unless jira.empty?
80
+ end
81
+ end
82
+
83
+ def message_jiras_related_to_current_branch
84
+ message("JIRA(s): #{related_jiras(branch: current_branch).join(', ')}")
85
+ end
86
+
87
+ def query_for_related_jiras
88
+ default = related_jiras(branch: previous_branch).join(', ') if new_branch?
89
+ query("What is the JIRA Number?", default)
90
+ end
91
+
92
+ exit(1) if detached_head || master_branch
93
+
94
+ if related_jiras(branch: current_branch).any?
95
+
96
+ message_jiras_related_to_current_branch
97
+
98
+ else
99
+
100
+ jiras = query_for_related_jiras
101
+ relate_jiras_to_current_branch(jiras)
102
+
103
+ end
104
+
105
+
106
+
107
+
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'jiragit'
4
+
5
+ @log = Jiragit::Logger.new(".git/jiragit/hooks.log")
6
+ @log.hook = File.basename $0
7
+ @log.level = Logger::DEBUG
8
+
9
+ @log.debug "launched"
10
+
11
+ def current_branch
12
+ @current_branch ||= begin
13
+ branch = Jiragit::Git.current_branch
14
+ @log.debug "#{__method__} #{branch}"
15
+ branch
16
+ end
17
+ end
18
+
19
+ def current_commit
20
+ @current_commit ||= begin
21
+ commit = Jiragit::Git.current_commit
22
+ @log.debug "#{__method__} #{commit}"
23
+ commit
24
+ end
25
+ end
26
+
27
+ def jira_store
28
+ @jira_store ||= Jiragit::JiraStore.new(".git/jiragit/jira_store")
29
+ end
30
+
31
+ def related_jiras(params)
32
+ jira_store
33
+ .relations(params)
34
+ .to_a
35
+ .select { |r| r.type == :jira }
36
+ .map(&:label)
37
+ end
38
+
39
+ def relate_commit_to_current_branch
40
+ jira_store.relate(commit: current_commit, branch: current_branch)
41
+ end
42
+
43
+ def relate_commit_to_jiras_for_current_branch
44
+ related_jiras(branch: current_branch).each do |jira|
45
+ jira_store.relate(jira: jira, commit: current_commit)
46
+ end
47
+ end
48
+
49
+ relate_commit_to_current_branch
50
+ relate_commit_to_jiras_for_current_branch
51
+
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'jiragit'
4
+
5
+ @log = Jiragit::Logger.new(".git/jiragit/hooks.log")
6
+ @log.hook = File.basename $0
7
+ @log.level = Logger::DEBUG
8
+
9
+ @log.debug "launched"
10
+
11
+ @config = Jiragit::Configuration.new("#{`echo ~`.chomp}/.jiragit")
12
+
13
+ STDIN.reopen('/dev/tty')
14
+
15
+ def commit_source
16
+ ARGV[1]
17
+ end
18
+
19
+ def commit_message
20
+ message = File.read(ARGV[0])
21
+ @log.debug "#{__method__}\n#{message}"
22
+ message
23
+ end
24
+
25
+ def write_commit_message(message)
26
+ @log.debug "#{__method__}\n#{message}"
27
+ File.write(ARGV[0], message)
28
+ end
29
+
30
+ def specified_commit_message_on_command_line?
31
+ @log.debug "#{__method__} #{commit_source == 'message'}"
32
+ commit_source == 'message'
33
+ end
34
+
35
+ def has_existing_commit_message?
36
+ @log.debug "#{__method__} #{commit_source == 'commit'}"
37
+ commit_source == 'commit'
38
+ end
39
+
40
+ def is_a_merge_commit?
41
+ @log.debug "#{__method__} #{commit_source == 'merge'}"
42
+ commit_source == 'merge'
43
+ end
44
+
45
+ def is_a_squash_commit?
46
+ @log.debug "#{__method__} #{commit_source == 'squash'}"
47
+ commit_source == 'squash'
48
+ end
49
+
50
+ def should_modify_commit_message?
51
+ modify = !(has_existing_commit_message? || is_a_squash_commit?)
52
+ @log.debug "#{__method__} #{modify}"
53
+ modify
54
+ end
55
+
56
+ def current_branch
57
+ branch = Jiragit::Git.current_branch
58
+ @log.debug "#{__method__} #{branch}"
59
+ branch
60
+ end
61
+
62
+ def merge_branch
63
+ commit_message.match(/Merge branch '(.*?)'/)
64
+ @log.debug "#{__method__} #{$1}"
65
+ $1
66
+ end
67
+
68
+ def jira_store
69
+ @jira_store ||= Jiragit::JiraStore.new(".git/jiragit/jira_store")
70
+ end
71
+
72
+ def related_jiras(params)
73
+ jira_store
74
+ .relations(params)
75
+ .to_a
76
+ .select { |r| r.type == :jira }
77
+ .map(&:label)
78
+ end
79
+
80
+ def add_to_commit_message(line="")
81
+ message = commit_message
82
+ message.insert(message.index('#') || -1,line+"\n")
83
+ write_commit_message(message)
84
+ end
85
+
86
+ def add_jiras_to_commit_message
87
+ jiras = related_jiras(branch: current_branch)
88
+ return if jiras.empty?
89
+
90
+ add_to_commit_message
91
+ jiras.each do |jira|
92
+ add_to_commit_message formatted_jira(jira)
93
+ end
94
+ add_to_commit_message
95
+ end
96
+
97
+ def add_merge_jiras_to_commit_message
98
+ jiras = related_jiras(branch: merge_branch)
99
+ return if jiras.empty?
100
+
101
+ add_to_commit_message "Merged JIRA(s)"
102
+ jiras.each do |jira|
103
+ add_to_commit_message formatted_jira(jira)
104
+ end
105
+ add_to_commit_message
106
+ end
107
+
108
+ def add_jiras_to_commit_message
109
+ jiras = related_jiras(branch: current_branch)
110
+ return if jiras.empty?
111
+
112
+ jiras.each do |jira|
113
+ add_to_commit_message formatted_jira(jira)
114
+ end
115
+ add_to_commit_message
116
+ end
117
+
118
+ def add_line_length_guide
119
+ add_to_commit_message "\n#------------------------------------------------#---------------------#"
120
+ end
121
+
122
+ def formatted_jira(jira)
123
+ if @config.include?(:jira_url) && !@config[:jira_url].empty?
124
+ "#{jira}: #{@config[:jira_url]}/browse/#{jira}"
125
+ else
126
+ jira
127
+ end
128
+ end
129
+
130
+ exit(0) unless should_modify_commit_message?
131
+
132
+ case
133
+ when specified_commit_message_on_command_line?
134
+ add_to_commit_message
135
+ add_jiras_to_commit_message
136
+
137
+ when is_a_merge_commit?
138
+ add_jiras_to_commit_message
139
+ add_merge_jiras_to_commit_message
140
+
141
+ else
142
+ add_line_length_guide
143
+ add_jiras_to_commit_message
144
+ end