drnic-github 0.3.9

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,16 @@
1
+ desc "Project issues tools - sub-commands : open [user], closed [user]"
2
+ flags :user => "Show issues from a certain user's repository"
3
+ flags :after => "Only show issues updated after a certain date"
4
+ flags :label => "Only show issues with a certain label"
5
+ command :issues do |command|
6
+ return if !helper.project
7
+ options[:user] ||= helper.owner
8
+
9
+ case command
10
+ when 'open', 'closed'
11
+ report = YAML.load(Kernel.open(@helper.list_issues_for(options[:user], command)))
12
+ @helper.print_issues(report['issues'], options)
13
+ else
14
+ helper.print_issues_help
15
+ end
16
+ end
@@ -0,0 +1,113 @@
1
+ desc "Project network tools - sub-commands : web [user], list, fetch, commits"
2
+ flags :after => "Only show commits after a certain date"
3
+ flags :before => "Only show commits before a certain date"
4
+ flags :shas => "Only show shas"
5
+ flags :project => "Filter commits on a certain project"
6
+ flags :author => "Filter commits on a email address of author"
7
+ flags :applies => "Filter commits to patches that apply cleanly"
8
+ flags :noapply => "Filter commits to patches that do not apply cleanly"
9
+ flags :nocache => "Do not use the cached network data"
10
+ flags :cache => "Use the network data even if it's expired"
11
+ flags :sort => "How to sort : date(*), branch, author"
12
+ flags :common => "Show common branch point"
13
+ flags :thisbranch => "Look at branches that match the current one"
14
+ flags :limit => "Only look through the first X heads - useful for really large projects"
15
+ command :network do |command, user|
16
+ return if !helper.project
17
+ user ||= helper.owner
18
+
19
+ case command
20
+ when 'web'
21
+ helper.open helper.network_page_for(user)
22
+ when 'list'
23
+ members = helper.get_network_members(user, options)
24
+ members.each do |hsh|
25
+ puts hsh["owner"]["login"]
26
+ end
27
+ when 'fetch'
28
+ # fetch each remote we don't have
29
+ data = helper.get_network_data(user, options)
30
+ data['users'].each do |hsh|
31
+ u = hsh['name']
32
+ GitHub.invoke(:track, u) unless helper.tracking?(u)
33
+ puts "fetching #{u}"
34
+ GitHub.invoke(:fetch_all, u)
35
+ end
36
+ when 'commits'
37
+ # show commits we don't have yet
38
+
39
+ $stderr.puts 'gathering heads'
40
+ cherry = []
41
+
42
+ if helper.cache_commits_data(options)
43
+ ids = []
44
+ data = helper.get_network_data(user, options)
45
+ data['users'].each do |hsh|
46
+ u = hsh['name']
47
+ if options[:thisbranch]
48
+ user_ids = hsh['heads'].map { |a| a['id'] if a['name'] == helper.current_branch }.compact
49
+ else
50
+ user_ids = hsh['heads'].map { |a| a['id'] }
51
+ end
52
+ user_ids.each do |id|
53
+ if !helper.has_commit?(id) && helper.cache_expired?
54
+ GitHub.invoke(:track, u) unless helper.tracking?(u)
55
+ puts "fetching #{u}"
56
+ GitHub.invoke(:fetch_all, u)
57
+ end
58
+ end
59
+ ids += user_ids
60
+ end
61
+ ids.uniq!
62
+
63
+ $stderr.puts 'has heads'
64
+
65
+ # check that we have all these shas locally
66
+ local_heads = helper.local_heads
67
+ local_heads_not = local_heads.map { |a| "^#{a}"}
68
+ looking_for = (ids - local_heads) + local_heads_not
69
+ commits = helper.get_commits(looking_for)
70
+
71
+ $stderr.puts 'ID SIZE:' + ids.size.to_s
72
+
73
+ ignores = helper.ignore_sha_array
74
+
75
+ ids.each do |id|
76
+ next if ignores[id] || !commits.assoc(id)
77
+ cherries = helper.get_cherry(id)
78
+ cherries = helper.remove_ignored(cherries, ignores)
79
+ cherry += cherries
80
+ helper.ignore_shas([id]) if cherries.size == 0
81
+ $stderr.puts "checking head #{id} : #{cherry.size.to_s}"
82
+ break if options[:limit] && cherry.size > options[:limit].to_i
83
+ end
84
+ end
85
+
86
+ if cherry.size > 0 || !helper.cache_commits_data(options)
87
+ helper.print_network_cherry_help if !options[:shas]
88
+
89
+ if helper.cache_commits_data(options)
90
+ $stderr.puts "caching..."
91
+ $stderr.puts "commits: " + cherry.size.to_s
92
+ our_commits = cherry.map { |item| c = commits.assoc(item[1]); [item, c] if c }
93
+ our_commits.delete_if { |item| item == nil }
94
+ helper.cache_commits(our_commits)
95
+ else
96
+ $stderr.puts "using cached..."
97
+ our_commits = helper.commits_cache
98
+ end
99
+
100
+ helper.print_commits(our_commits, options)
101
+ else
102
+ puts "no unapplied commits"
103
+ end
104
+ else
105
+ helper.print_network_help
106
+ end
107
+ end
108
+
109
+ desc "Ignore a SHA (from 'github network commits')"
110
+ command :ignore do |sha|
111
+ commits = helper.resolve_commits(sha)
112
+ helper.ignore_shas(commits) # add to .git/ignore-shas file
113
+ end
data/lib/github.rb ADDED
@@ -0,0 +1,185 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+ require 'github/extensions'
3
+ require 'github/command'
4
+ require 'github/helper'
5
+ require 'github/ui'
6
+ require 'fileutils'
7
+ require 'rubygems'
8
+ require 'open-uri'
9
+ require 'json'
10
+ require 'yaml'
11
+ require 'text/format'
12
+
13
+ ##
14
+ # Starting simple.
15
+ #
16
+ # $ github <command> <args>
17
+ #
18
+ # GitHub.command <command> do |*args|
19
+ # whatever
20
+ # end
21
+ #
22
+
23
+ module GitHub
24
+ extend self
25
+
26
+ BasePath = File.expand_path(File.dirname(__FILE__))
27
+
28
+ def command(command, options = {}, &block)
29
+ command = command.to_s
30
+ debug "Registered `#{command}`"
31
+ descriptions[command] = @next_description if @next_description
32
+ @next_description = nil
33
+ flag_descriptions[command].update @next_flags if @next_flags
34
+ usage_descriptions[command] = @next_usage if @next_usage
35
+ @next_flags = nil
36
+ @next_usage = []
37
+ commands[command] = Command.new(block)
38
+ Array(options[:alias] || options[:aliases]).each do |command_alias|
39
+ commands[command_alias.to_s] = commands[command.to_s]
40
+ end
41
+ end
42
+
43
+ def desc(str)
44
+ @next_description = str
45
+ end
46
+
47
+ def flags(hash)
48
+ @next_flags ||= {}
49
+ @next_flags.update hash
50
+ end
51
+
52
+ def usage(string)
53
+ @next_usage ||= []
54
+ @next_usage << string
55
+ end
56
+
57
+ def helper(command, &block)
58
+ debug "Helper'd `#{command}`"
59
+ Helper.send :define_method, command, &block
60
+ end
61
+
62
+ def activate(args)
63
+ @@original_args = args.clone
64
+ @options = parse_options(args)
65
+ @debug = @options.delete(:debug)
66
+ @learn = @options.delete(:learn)
67
+ Dir[BasePath + '/commands/*.rb'].each do |command|
68
+ load command
69
+ end
70
+ invoke(args.shift, *args)
71
+ end
72
+
73
+ def invoke(command, *args)
74
+ block = find_command(command)
75
+ debug "Invoking `#{command}`"
76
+ block.call(*args)
77
+ end
78
+
79
+ def find_command(name)
80
+ name = name.to_s
81
+ commands[name] || (commands[name] = GitCommand.new(name)) || commands['default']
82
+ end
83
+
84
+ def commands
85
+ @commands ||= {}
86
+ end
87
+
88
+ def descriptions
89
+ @descriptions ||= {}
90
+ end
91
+
92
+ def flag_descriptions
93
+ @flagdescs ||= Hash.new { |h, k| h[k] = {} }
94
+ end
95
+
96
+ def usage_descriptions
97
+ @usage_descriptions ||= Hash.new { |h, k| h[k] = [] }
98
+ end
99
+
100
+ def options
101
+ @options
102
+ end
103
+
104
+ def original_args
105
+ @@original_args ||= []
106
+ end
107
+
108
+ def parse_options(args)
109
+ idx = 0
110
+ args.clone.inject({}) do |memo, arg|
111
+ case arg
112
+ when /^--(.+?)=(.*)/
113
+ args.delete_at(idx)
114
+ memo.merge($1.to_sym => $2)
115
+ when /^--(.+)/
116
+ args.delete_at(idx)
117
+ memo.merge($1.to_sym => true)
118
+ when "--"
119
+ args.delete_at(idx)
120
+ return memo
121
+ else
122
+ idx += 1
123
+ memo
124
+ end
125
+ end
126
+ end
127
+
128
+ def debug(*messages)
129
+ puts *messages.map { |m| "== #{m}" } if debug?
130
+ end
131
+
132
+ def learn(message)
133
+ if learn?
134
+ puts "== " + Color.yellow(message)
135
+ else
136
+ debug(message)
137
+ end
138
+ end
139
+
140
+ def learn?
141
+ !!@learn
142
+ end
143
+
144
+ def debug?
145
+ !!@debug
146
+ end
147
+
148
+ def load(file)
149
+ file[0] =~ /^\// ? path = file : path = BasePath + "/commands/#{File.basename(file)}"
150
+ data = File.read(path)
151
+ GitHub.module_eval data, path
152
+ end
153
+ end
154
+
155
+ GitHub.command :default, :aliases => ['', '-h', 'help', '-help', '--help'] do
156
+ message = []
157
+ message << "Usage: github command <space separated arguments>"
158
+ message << "Available commands:"
159
+ longest = GitHub.descriptions.map { |d,| d.to_s.size }.max
160
+ indent = longest + 6 # length of " " + " => "
161
+ fmt = Text::Format.new(
162
+ :first_indent => indent,
163
+ :body_indent => indent,
164
+ :columns => 79 # be a little more lenient than the default
165
+ )
166
+ GitHub.descriptions.sort {|a,b| a.to_s <=> b.to_s }.each do |command, desc|
167
+ cmdstr = "%-#{longest}s" % command
168
+ desc = fmt.format(desc).strip # strip to eat first "indent"
169
+ message << " #{cmdstr} => #{desc}"
170
+ flongest = GitHub.flag_descriptions[command].map { |d,| "--#{d}".size }.max
171
+ ffmt = fmt.clone
172
+ ffmt.body_indent += 2 # length of "% " and/or "--"
173
+ GitHub.usage_descriptions[command].each do |usage_descriptions|
174
+ usage_descriptions.each do |usage|
175
+ usage_str = "%% %-#{flongest}s" % usage
176
+ message << ffmt.format(usage_str)
177
+ end
178
+ end
179
+ GitHub.flag_descriptions[command].sort {|a,b| a.to_s <=> b.to_s }.each do |flag, fdesc|
180
+ flagstr = "#{" " * longest} %-#{flongest}s" % "--#{flag}"
181
+ message << ffmt.format(" #{flagstr}: #{fdesc}")
182
+ end
183
+ end
184
+ puts message.map { |m| m.gsub(/\n$/,'') }.join("\n") + "\n"
185
+ end
@@ -0,0 +1,131 @@
1
+ require 'fileutils'
2
+
3
+ if RUBY_PLATFORM =~ /mswin|mingw/
4
+ begin
5
+ require 'win32/open3'
6
+ rescue LoadError
7
+ warn "You must 'gem install win32-open3' to use the github command on Windows"
8
+ exit 1
9
+ end
10
+ else
11
+ require 'open3'
12
+ end
13
+
14
+ module GitHub
15
+ class Command
16
+ include FileUtils
17
+
18
+ def initialize(block)
19
+ (class << self;self end).send :define_method, :command, &block
20
+ end
21
+
22
+ def call(*args)
23
+ arity = method(:command).arity
24
+ args << nil while args.size < arity
25
+ send :command, *args
26
+ end
27
+
28
+ def helper
29
+ @helper ||= Helper.new
30
+ end
31
+
32
+ def options
33
+ GitHub.options
34
+ end
35
+
36
+ def pgit(*command)
37
+ puts git(*command)
38
+ end
39
+
40
+ def git(command)
41
+ run :sh, command
42
+ end
43
+
44
+ def git_exec(command)
45
+ run :exec, command
46
+ end
47
+
48
+ def run(method, command)
49
+ if command.is_a? Array
50
+ command = [ 'git', command ].flatten
51
+ GitHub.learn command.join(' ')
52
+ else
53
+ command = 'git ' + command
54
+ GitHub.learn command
55
+ end
56
+
57
+ send method, *command
58
+ end
59
+
60
+ def sh(*command)
61
+ Shell.new(*command).run
62
+ end
63
+
64
+ def die(message)
65
+ puts "=> #{message}"
66
+ exit!
67
+ end
68
+
69
+ def github_user
70
+ git("config --get github.user")
71
+ end
72
+
73
+ def github_token
74
+ git("config --get github.token")
75
+ end
76
+
77
+ def shell_user
78
+ ENV['USER']
79
+ end
80
+
81
+ def current_user?(user)
82
+ user == github_user || user == shell_user
83
+ end
84
+
85
+ class Shell < String
86
+ attr_reader :error
87
+ attr_reader :out
88
+
89
+ def initialize(*command)
90
+ @command = command
91
+ end
92
+
93
+ def run
94
+ GitHub.debug "sh: #{command}"
95
+
96
+ out = err = nil
97
+ Open3.popen3(*@command) do |_, pout, perr|
98
+ out = pout.read.strip
99
+ err = perr.read.strip
100
+ end
101
+
102
+ replace @error = err if err.any?
103
+ replace @out = out if out.any?
104
+
105
+ self
106
+ end
107
+
108
+ def command
109
+ @command.join(' ')
110
+ end
111
+
112
+ def error?
113
+ !!@error
114
+ end
115
+
116
+ def out?
117
+ !!@out
118
+ end
119
+ end
120
+ end
121
+
122
+ class GitCommand < Command
123
+ def initialize(name)
124
+ @name = name
125
+ end
126
+
127
+ def command(*args)
128
+ git_exec [ @name, args ]
129
+ end
130
+ end
131
+ end