jiragit 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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