jiragit 0.5.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.
@@ -0,0 +1,10 @@
1
+ require 'jiragit/version'
2
+ require 'jiragit/logger'
3
+ require 'jiragit/configuration'
4
+ require 'jiragit/cli'
5
+ require 'jiragit/git/repository'
6
+ require 'jiragit/git/commit_response'
7
+ require 'jiragit/git/branch'
8
+ require 'jiragit/tag'
9
+ require 'jiragit/vault'
10
+ require 'jiragit/jira_store'
@@ -0,0 +1,249 @@
1
+ module Jiragit
2
+
3
+ class Cli
4
+
5
+ COMMANDS = [
6
+ :install,
7
+ :uninstall,
8
+ :jira,
9
+ :branch,
10
+ :jira_branch,
11
+ :browse,
12
+ :remote,
13
+ :local,
14
+ :configure,
15
+ :configuration
16
+ ]
17
+
18
+ FLAGS = [
19
+ :silent
20
+ ]
21
+
22
+ def initialize(args)
23
+ exit_with_help and return if args.empty?
24
+ @args = args
25
+ command = args[0].to_sym
26
+ @params = args[1..-1]
27
+ exit_with_help and return unless COMMANDS.include?(command)
28
+
29
+ @config = Jiragit::Configuration.new("#{`echo ~`.chomp}/.jiragit")
30
+
31
+ begin
32
+ self.send(command)
33
+ rescue => error
34
+ $stderr.puts error.message
35
+ end
36
+ end
37
+
38
+ def exit_with_help
39
+ puts "jira {command}"
40
+ puts
41
+ puts "Commands"
42
+ puts "--------"
43
+ COMMANDS.each do |command|
44
+ puts command
45
+ end
46
+ end
47
+
48
+ def install
49
+ puts "Installing into #{Jiragit::Git.repository_root}" unless silent?
50
+ gem_hook_paths.each do |hook|
51
+ `cp #{hook} #{Jiragit::Git.repository_root}/.git/hooks/`
52
+ end
53
+ gem_hook_files.each do |hook|
54
+ `chmod a+x #{Jiragit::Git.repository_root}/.git/hooks/#{hook}`
55
+ end
56
+ end
57
+
58
+ def uninstall
59
+ puts "Uninstalling from #{Jiragit::Git.repository_root}"
60
+ gem_hook_files.each do |hook|
61
+ `rm #{Jiragit::Git.repository_root}/.git/hooks/#{hook}`
62
+ end
63
+ end
64
+
65
+ def jira
66
+ if @params.empty? || @params[0].empty?
67
+ warn "Please provide a jira"
68
+ return
69
+ end
70
+ puts "Listing all relations for jira #{@params[0]}"
71
+ js = JiraStore.new("#{Jiragit::Git.repository_root}/.git/jiragit/jira_store")
72
+ puts js.relations(jira: @params[0]).to_a
73
+ end
74
+
75
+ def branch
76
+ branch = @params[0] || Jiragit::Git.current_branch
77
+ puts "Listing all relations for branch #{branch}"
78
+ js = JiraStore.new("#{Jiragit::Git.repository_root}/.git/jiragit/jira_store")
79
+ puts js.relations(branch: branch).to_a
80
+ end
81
+
82
+ def jira_branch
83
+ puts "Relating jira #{@params[0]} and branch #{@params[1]}"
84
+ js = JiraStore.new("#{Jiragit::Git.repository_root}/.git/jiragit/jira_store")
85
+ js.relate(jira: @params[0], branch: @params[1])
86
+ end
87
+
88
+ def browse
89
+ type = :branch
90
+ type, object = classify(*@params) unless @params.empty?
91
+ unless object
92
+ object = case type
93
+ when :jira
94
+ related(:jira, {branch: Jiragit::Git.current_branch}).first
95
+ when :branch
96
+ Jiragit::Git.current_branch
97
+ when :commit
98
+ related(:commit, {branch: Jiragit::Git.current_branch}).first
99
+ end
100
+ end
101
+ self.send("browse_#{type}", object)
102
+ end
103
+
104
+ def remote
105
+ # remote branches
106
+ type = (@params[0] || 'branches').to_sym
107
+ case type
108
+ when :branches
109
+ list_remote_branches
110
+ end
111
+ end
112
+
113
+ def local
114
+ # local branches
115
+ type = (@params[0] || 'branches').to_sym
116
+ case type
117
+ when :branches
118
+ list_local_branches
119
+ end
120
+ end
121
+
122
+ def configure
123
+ if @params.size!=2 || @params.first.empty? || @params.last.empty?
124
+ warn "Please provide parameter and value"
125
+ return
126
+ end
127
+ @config[@params.first.to_sym] = @params.last
128
+ puts "Setting #{@params.first.to_sym} to #{@params.last}"
129
+ end
130
+
131
+ def configuration
132
+ puts "Configuration settings"
133
+ @config.each do |key,value|
134
+ puts "#{key} = #{value}"
135
+ end
136
+ end
137
+
138
+ private
139
+
140
+ def run(command)
141
+ `#{command}`
142
+ end
143
+
144
+ def silent?
145
+ !!@args.detect { |a| a == :silent }
146
+ end
147
+
148
+ def gem_hook_paths
149
+ spec = Gem::Specification.find_by_name('jiragit')
150
+ Dir.glob("#{spec.gem_dir}/hooks/*")
151
+ end
152
+
153
+ def gem_hook_files
154
+ gem_hook_paths.map { |hook| File.basename(hook) }
155
+ end
156
+
157
+ IS_JIRA = /\b([A-Z]{1,4}-[1-9][0-9]{0,6})\b/
158
+ IS_COMMIT = /\b([0-9a-f]{6,40})\b/
159
+ IS_BRANCH = /\b(?!\/)(?!.*\/\.)(?!.*\.\.)(?!.*\/\/)(?!.*\@\{)(?!.*\\)[^\040\177\s\~\^\:\?\*\[]+(?<!\.lock)(?<!\/)(?<!\.)\b/
160
+
161
+ def classify(*params)
162
+ case params.first
163
+ when /jira/
164
+ [ :jira, params.size>1 ? params.last[IS_JIRA] : nil ]
165
+ when /commit/
166
+ [ :commit, params.size>1 ? params.last[IS_COMMIT] : nil ]
167
+ when /branch/
168
+ [ :branch, params.size>1 ? params.last[IS_BRANCH] : nil ]
169
+ when IS_JIRA
170
+ [ :jira, params.first[IS_JIRA] ]
171
+ when IS_COMMIT
172
+ [ :commit, params.first[IS_COMMIT] ]
173
+ when IS_BRANCH
174
+ [ :branch, params.first[IS_BRANCH] ]
175
+ else
176
+ [ nil, nil ]
177
+ end
178
+ end
179
+
180
+ def browse_jira(jira)
181
+ if !jira || jira.empty?
182
+ warn "No jira available"
183
+ return
184
+ end
185
+ if !@config[:jira_url]
186
+ warn "No jira_url configured"
187
+ return
188
+ end
189
+ puts "Browsing jira #{jira}"
190
+ puts "Visiting #{@config[:jira_url]}/browse/#{jira}"
191
+ run "open #{@config[:jira_url]}/browse/#{jira}"
192
+ end
193
+
194
+ def related(type, relation)
195
+ js = JiraStore.new("#{Jiragit::Git.repository_root}/.git/jiragit/jira_store")
196
+ jiras = js
197
+ .relations(relation)
198
+ .to_a
199
+ .select { |r| r.type == type }
200
+ .map(&:label)
201
+ end
202
+
203
+ def browse_branch(branch)
204
+ if !branch || branch.empty?
205
+ warn "No branch available"
206
+ return
207
+ end
208
+ puts "Browsing branch #{branch}"
209
+ url = Jiragit::Git.github_branch_url(branch)
210
+ unless url
211
+ puts "No remote repository found on Github"
212
+ return
213
+ end
214
+ puts "Visiting #{url}"
215
+ run "open #{url}"
216
+ end
217
+
218
+ def browse_commit(commit)
219
+ if !commit || commit.empty?
220
+ warn "No commit available"
221
+ return
222
+ end
223
+ puts "Browsing commit #{commit}"
224
+ url = Jiragit::Git.github_commit_url(commit)
225
+ unless url
226
+ puts "No remote repository found on Github"
227
+ return
228
+ end
229
+ puts "Visiting #{url}"
230
+ run "open #{url}"
231
+ end
232
+
233
+ def list_remote_branches
234
+ puts "Fetching remote branch information"
235
+ Jiragit::Git.remote_branches.each do |branch|
236
+ puts "#{'%-10.10s'%branch.date}\s\s#{branch.short_commit}\s\s#{'%-10.10s'%branch.committer}\s\s#{branch.short_name}"
237
+ end
238
+ end
239
+
240
+ def list_local_branches
241
+ puts "Fetching local branch information"
242
+ Jiragit::Git.local_branches.each do |branch|
243
+ puts "#{'%-10.10s'%branch.date}\s\s#{branch.short_commit}\s\s#{'%-10.10s'%branch.committer}\s\s#{branch.short_name}"
244
+ end
245
+ end
246
+
247
+ end
248
+
249
+ end
@@ -0,0 +1,51 @@
1
+ require 'yaml'
2
+
3
+ module Jiragit
4
+
5
+ class Configuration
6
+
7
+ def initialize(location)
8
+ self.location = location
9
+ load_or_create
10
+ end
11
+
12
+ def load_or_create
13
+ self.config = load || create
14
+ end
15
+
16
+ def load
17
+ return false unless File.exists?(location)
18
+ self.config = YAML.load(File.read(location))
19
+ end
20
+
21
+ def create
22
+ self.config = {}
23
+ save
24
+ config
25
+ end
26
+
27
+ def save
28
+ File.open(location, 'w+') { |file| file.write YAML.dump(config) }
29
+ end
30
+
31
+ def [](key)
32
+ config[key]
33
+ end
34
+
35
+ def []=(key, value)
36
+ config[key] = value
37
+ save
38
+ end
39
+
40
+ def include?(key)
41
+ config.include?(key)
42
+ end
43
+
44
+ private
45
+
46
+ attr_accessor :config
47
+ attr_accessor :location
48
+
49
+ end
50
+
51
+ end
@@ -0,0 +1,29 @@
1
+ module Jiragit
2
+
3
+ module Git
4
+
5
+ class Branch
6
+
7
+ attr_accessor :name, :commit
8
+
9
+ def short_name
10
+ @name.gsub(/refs\/heads\//,'')
11
+ end
12
+
13
+ def date
14
+ @date ||= Jiragit::Git.timestamp(commit)
15
+ end
16
+
17
+ def committer
18
+ @committer ||= Jiragit::Git.committer(commit)
19
+ end
20
+
21
+ def short_commit
22
+ commit[0,6]
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,23 @@
1
+ class CommitResponse
2
+
3
+ def initialize(string)
4
+ @response = string
5
+ end
6
+
7
+ def commit_string
8
+ @response[/(?<=\[)(.*?)(?=\])/] || ""
9
+ end
10
+
11
+ def branch
12
+ commit_string.split(/ /).first
13
+ end
14
+
15
+ def sha
16
+ commit_string.split(/ /).last
17
+ end
18
+
19
+ def message
20
+ @response[/.*?(?=\n)/]
21
+ end
22
+
23
+ end
@@ -0,0 +1,222 @@
1
+ require 'pty'
2
+
3
+ module Jiragit
4
+
5
+ module Git
6
+
7
+ class NoRepositoryError < StandardError;
8
+ def initialize(msg = "No valid Git repository was found in current directory.")
9
+ super
10
+ end
11
+ end
12
+
13
+ def self.repository_root
14
+ value = `git rev-parse --show-toplevel 2>&1`.chomp
15
+ raise NoRepositoryError if value=~/Not a git repository/
16
+ value
17
+ end
18
+
19
+ def self.current_branch
20
+ value = `git symbolic-ref -q HEAD --short 2>&1`.chomp
21
+ raise NoRepositoryError if value=~/Not a git repository/
22
+ value
23
+ end
24
+
25
+ def self.current_commit
26
+ value = `git log -1 --format=%H 2>&1`.chomp
27
+ raise NoRepositoryError if value=~/Not a git repository/
28
+ value
29
+ end
30
+
31
+ def self.previous_branch
32
+ value = `git rev-parse --symbolic-full-name --abbrev-ref @{-1} 2>&1`.chomp
33
+ raise NoRepositoryError if value=~/Not a git repository/
34
+ value
35
+ end
36
+
37
+ def self.remote
38
+ value = `git config --get remote.origin.url 2>&1`.chomp
39
+ raise NoRepositoryError if value=~/Not a git repository/
40
+ value
41
+ end
42
+
43
+ def self.github_branch_url(branch = current_branch)
44
+ return unless remote =~ /github.com/
45
+ remote
46
+ .gsub(/git\@github.com\:/,'https://github.com/')
47
+ .gsub(/.git$/,"/tree/#{branch}")
48
+ end
49
+
50
+ def self.github_commit_url(commit)
51
+ return unless remote =~ /github.com/
52
+ remote
53
+ .gsub(/git\@github.com\:/,'https://github.com/')
54
+ .gsub(/.git$/,"/commit/#{commit}")
55
+ end
56
+
57
+ def self.remote_branches
58
+ value = `git fetch 2>&1`
59
+ raise NoRepositoryError if value=~/Not a git repository/
60
+
61
+ `git ls-remote --heads origin 2>&1`
62
+ .split(/\n/)
63
+ .map(&:chomp)
64
+ .map{ |line| line.split(/\t/) }
65
+ .map{ |entries|
66
+ Branch.new.tap do |branch|
67
+ branch.commit = entries.first
68
+ branch.name = entries.last
69
+ end
70
+ }
71
+ .sort_by{ |branch| branch.date }
72
+ .reverse
73
+ end
74
+
75
+ def self.local_branches
76
+ value = `git for-each-ref --sort=-committerdate refs/heads --format="%(objectname) %(refname)" 2>&1`
77
+ .split(/\n/)
78
+ .map(&:chomp)
79
+ .map{ |line| line.split(/ /) }
80
+ .map{ |entries|
81
+ Branch.new.tap do |branch|
82
+ branch.commit = entries.first
83
+ branch.name = entries.last
84
+ end
85
+ }
86
+ .sort_by{ |branch| branch.date }
87
+ .reverse
88
+ raise NoRepositoryError if value=~/Not a git repository/
89
+ value
90
+ end
91
+
92
+ def self.timestamp(reference)
93
+ value = `git log -1 --format=%ci #{reference} 2>&1`.chomp
94
+ raise NoRepositoryError if value=~/Not a git repository/
95
+ value
96
+ end
97
+
98
+ def self.committer(reference)
99
+ value = `git log -1 --format=%cN #{reference} 2>&1`.chomp
100
+ raise NoRepositoryError if value=~/Not a git repository/
101
+ value
102
+ end
103
+
104
+ class Repository
105
+
106
+ def self.create(path)
107
+ Dir.mkdir(path) unless Dir.exists?(path)
108
+ `cd #{path}; git init .` unless File.exists?("#{path}/.git")
109
+ repo = self.new(path)
110
+ end
111
+
112
+ def initialize(path)
113
+ self.path = path
114
+ end
115
+
116
+ def remove
117
+ `rm -rf #{path}` if Dir.exists?(path)
118
+ end
119
+
120
+ def checkout_new_branch(branch, &block)
121
+ run_command("git checkout -b #{branch}", &block)
122
+ end
123
+
124
+ def checkout_branch(branch, &block)
125
+ run_command("git checkout #{branch}", &block)
126
+ end
127
+
128
+ def create(filename, message)
129
+ run_command("echo '#{message}' > #{filename}")
130
+ end
131
+
132
+ def add(filename)
133
+ run_command("git add #{filename}")
134
+ end
135
+
136
+ def merge(branch, message="", &block)
137
+ number = rand(100)
138
+ run_command("echo '#{message}' > .git_commit_body")
139
+ output = run_command({env: "export GIT_EDITOR=$PWD/spec/git_editor.rb", command:"git merge #{branch}"}, &block)
140
+ run_command("rm .git_commit_body")
141
+ CommitResponse.new(output)
142
+ end
143
+
144
+ def commit(message, &block)
145
+ number = rand(100)
146
+ run_command("echo '#{message}' > .git_commit_body")
147
+ output = run_command({env: "export GIT_EDITOR=$PWD/spec/git_editor.rb", command:"git commit"}, &block)
148
+ run_command("rm .git_commit_body")
149
+ CommitResponse.new(output)
150
+ end
151
+
152
+ def make_a_commit(&block)
153
+ number = rand(100)
154
+ output = run_command({env: "export GIT_EDITOR=$PWD/spec/git_editor.rb", command:"echo \"#{number}\" > README.md; git add README.md; git commit"}, &block)
155
+ CommitResponse.new(output)
156
+ end
157
+
158
+ def make_a_command_line_commit(&block)
159
+ number = rand(100)
160
+ output = run_command("echo \"#{number}\" > README.md; git add README.md; git commit -m 'command line specified commit message #{number}'", &block)
161
+ CommitResponse.new(output)
162
+ end
163
+
164
+ def log(&block)
165
+ run_command("git log", &block)
166
+ end
167
+
168
+ def log_for_one_commit(sha, &block)
169
+ run_command("git log -n 1 #{sha}", &block)
170
+ end
171
+
172
+ def one_line_log(&block)
173
+ run_command("git log --format=oneline", &block)
174
+ end
175
+
176
+ def oneline_log_for_one_commit(sha, &block)
177
+ run_command("git log --format=oneline -n 1 #{sha}", &block)
178
+ end
179
+
180
+ def current_commit(&block)
181
+ run_command("git log -1 --format=%H", &block)
182
+ end
183
+
184
+ def root
185
+ @path
186
+ end
187
+
188
+ def origin
189
+ run_command("git remote -v")
190
+ end
191
+
192
+ def origin=(path)
193
+ run_command("git remote add origin #{path}")
194
+ end
195
+
196
+ private
197
+
198
+ attr_accessor :path
199
+
200
+ def run_command(command, &block)
201
+ if command.is_a? Hash
202
+ env = command[:env]
203
+ command = command[:command]
204
+ full_command = "#{env}; cd #{path}; #{command} 2>&1"
205
+ else
206
+ full_command = "cd #{path}; #{command} 2>&1"
207
+ end
208
+ if block_given?
209
+ PTY.spawn(full_command) do |output, input|
210
+ sleep(1) #avoid race conditions
211
+ yield output, input
212
+ end
213
+ else
214
+ `#{full_command}`.chomp
215
+ end
216
+ end
217
+
218
+ end
219
+
220
+ end
221
+
222
+ end