wfarr-github 0.4.1

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,17 @@
1
+ desc "Project issues tools - sub-commands : open [user], closed [user]"
2
+ flags :after => "Only show issues updated after a certain date"
3
+ flags :label => "Only show issues with a certain label"
4
+ command :issues do |command, user|
5
+ return if !helper.project
6
+ user ||= helper.owner
7
+
8
+ case command
9
+ when 'open', 'closed'
10
+ report = YAML.load(open(@helper.list_issues_for(user, command)))
11
+ @helper.print_issues(report['issues'], options)
12
+ when 'web'
13
+ helper.open helper.issues_page_for(user)
14
+ else
15
+ helper.print_issues_help
16
+ end
17
+ end
@@ -0,0 +1,110 @@
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
+ helper.network_members(user, options).each { |user| puts user }
24
+ when 'fetch'
25
+ # fetch each remote we don't have
26
+ data = helper.get_network_data(user, options)
27
+ data['users'].each do |hsh|
28
+ u = hsh['name']
29
+ GitHub.invoke(:track, u) unless helper.tracking?(u)
30
+ puts "fetching #{u}"
31
+ GitHub.invoke(:fetch_all, u)
32
+ end
33
+ when 'commits'
34
+ # show commits we don't have yet
35
+
36
+ $stderr.puts 'gathering heads'
37
+ cherry = []
38
+
39
+ if helper.cache_commits_data(options)
40
+ ids = []
41
+ data = helper.get_network_data(user, options)
42
+ data['users'].each do |hsh|
43
+ u = hsh['name']
44
+ if options[:thisbranch]
45
+ user_ids = hsh['heads'].map { |a| a['id'] if a['name'] == helper.current_branch }.compact
46
+ else
47
+ user_ids = hsh['heads'].map { |a| a['id'] }
48
+ end
49
+ user_ids.each do |id|
50
+ if !helper.has_commit?(id) && helper.cache_expired?
51
+ GitHub.invoke(:track, u) unless helper.tracking?(u)
52
+ puts "fetching #{u}"
53
+ GitHub.invoke(:fetch_all, u)
54
+ end
55
+ end
56
+ ids += user_ids
57
+ end
58
+ ids.uniq!
59
+
60
+ $stderr.puts 'has heads'
61
+
62
+ # check that we have all these shas locally
63
+ local_heads = helper.local_heads
64
+ local_heads_not = local_heads.map { |a| "^#{a}"}
65
+ looking_for = (ids - local_heads) + local_heads_not
66
+ commits = helper.get_commits(looking_for)
67
+
68
+ $stderr.puts 'ID SIZE:' + ids.size.to_s
69
+
70
+ ignores = helper.ignore_sha_array
71
+
72
+ ids.each do |id|
73
+ next if ignores[id] || !commits.assoc(id)
74
+ cherries = helper.get_cherry(id)
75
+ cherries = helper.remove_ignored(cherries, ignores)
76
+ cherry += cherries
77
+ helper.ignore_shas([id]) if cherries.size == 0
78
+ $stderr.puts "checking head #{id} : #{cherry.size.to_s}"
79
+ break if options[:limit] && cherry.size > options[:limit].to_i
80
+ end
81
+ end
82
+
83
+ if cherry.size > 0 || !helper.cache_commits_data(options)
84
+ helper.print_network_cherry_help if !options[:shas]
85
+
86
+ if helper.cache_commits_data(options)
87
+ $stderr.puts "caching..."
88
+ $stderr.puts "commits: " + cherry.size.to_s
89
+ our_commits = cherry.map { |item| c = commits.assoc(item[1]); [item, c] if c }
90
+ our_commits.delete_if { |item| item == nil }
91
+ helper.cache_commits(our_commits)
92
+ else
93
+ $stderr.puts "using cached..."
94
+ our_commits = helper.commits_cache
95
+ end
96
+
97
+ helper.print_commits(our_commits, options)
98
+ else
99
+ puts "no unapplied commits"
100
+ end
101
+ else
102
+ helper.print_network_help
103
+ end
104
+ end
105
+
106
+ desc "Ignore a SHA (from 'github network commits')"
107
+ command :ignore do |sha|
108
+ commits = helper.resolve_commits(sha)
109
+ helper.ignore_shas(commits) # add to .git/ignore-shas file
110
+ 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_line 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.empty?
103
+ replace @out = out if !out.empty?
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
@@ -0,0 +1,39 @@
1
+ # define #try
2
+ class Object
3
+ def try
4
+ self
5
+ end
6
+ end
7
+
8
+ class NilClass
9
+ klass = Class.new
10
+ klass.class_eval do
11
+ instance_methods.each { |meth| undef_method meth.to_sym unless meth =~ /^(__(id|send)__|object_id)$/ }
12
+ def method_missing(*args)
13
+ self
14
+ end
15
+ end
16
+ NilProxy = klass.new
17
+ def try
18
+ NilProxy
19
+ end
20
+ end
21
+
22
+ # define #tap
23
+ class Object
24
+ def tap(&block)
25
+ block.call(self)
26
+ self
27
+ end
28
+ end
29
+
30
+ # cute
31
+ module Color
32
+ COLORS = { :clear => 0, :red => 31, :green => 32, :yellow => 33 }
33
+ def self.method_missing(color_name, *args)
34
+ color(color_name) + args.first + color(:clear)
35
+ end
36
+ def self.color(color)
37
+ "\e[#{COLORS[color.to_sym]}m"
38
+ end
39
+ end