github_labeler 0.1.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.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/bin/github_labeler +288 -0
  3. data/lib/github_labeler.rb +298 -0
  4. metadata +88 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 21e8c495727bbb1dedeb2c52ece44b3def6c0290
4
+ data.tar.gz: d18b4d9bf033db40d62841716d26515e7a147655
5
+ SHA512:
6
+ metadata.gz: e60f2a3d8177b3ff85229decb3ddab4f72dd681991fb6b38235cc267390974576b47ce014ebf58c8466c2627618df03d022c823dc18f8b44dec3fe0aa2123ef7
7
+ data.tar.gz: 7b8a60e750fc95e5b233898c54e4baf2d674511c3d2c5ce1e5ea652dedccc5014ebfb8f42756ab9001342361df5eb4c026e8161bb5e23c0125cb572427cf7d2b
@@ -0,0 +1,288 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "commander"
4
+ require "json"
5
+ require "github_labeler"
6
+
7
+ ARGV.push('-h') if ARGV.empty?
8
+
9
+ cli = HighLine.new($stdin, $stderr)
10
+
11
+ Commander.configure do
12
+ program :name, 'GitHub Labeler'
13
+ program :version, '0.1.0'
14
+ program :description, 'Simple program for managing issue labels across GitHub repositories.'
15
+
16
+ global_option '--verbose', "Enable output of detailed debugging information to STDERR"
17
+ global_option '--execute', "Execute changes immediately, without asking for confirmation"
18
+ global_option '--token STRING', String, "GitHub OAuth token for making API calls. If not specified, the GITHUB_OAUTH_TOKEN environment variable is used. Create a token here: https://github.com/settings/token"
19
+
20
+ command :duplicate do |c|
21
+ c.syntax = 'github_labeler duplicate [options]'
22
+ c.description = 'Duplicate labels from one repository to another'
23
+
24
+ c.option '-s', '--source STRING', String, '(required) Source repository to copy labels from (USER/REPO)'
25
+ c.option '-d', '--destination STRING', String, '(required) Destination repository to copy labels to (USER/REPO), or name of JSON file with list of repositories'
26
+
27
+ c.example "Destination is repository name", "github_labeler duplicate -s github/linguist -d foo/bar"
28
+ c.example "Destinations are specified in JSON file", "github_labeler duplicate -s github/linguist -d dest.json"
29
+
30
+ c.action do |args, options|
31
+ options.default \
32
+ :verbose => false,
33
+ :execute => false,
34
+ :token => ENV["GITHUB_OAUTH_TOKEN"]
35
+
36
+ raise ArgumentError.new("Token is required") unless options.token
37
+ raise ArgumentError.new("Source is required") unless options.source
38
+ raise ArgumentError.new("Destination is required") unless options.destination
39
+
40
+ labeler = GitHubLabeler.new(options.token, options.verbose)
41
+ destination = File.file?(options.destination) ? JSON.parse(IO.read(options.destination), {:symbolize_names => true}) : [options.destination]
42
+ changes = labeler.duplicate_labels_from_repo(options.source, destination)
43
+
44
+ puts JSON.pretty_generate(changes)
45
+
46
+ if changes.size == 0
47
+ cli.say("No changes to execute.")
48
+ elsif options.execute or cli.agree("Execute #{changes.size} changes? [y/n]")
49
+ labeler.execute_changes(changes)
50
+ end
51
+
52
+ cli.say("Done!")
53
+ end
54
+ end
55
+
56
+ command :export do |c|
57
+ c.syntax = 'github_labeler export [options]'
58
+ c.description = 'Export a list of labels from a repository or list of repositories'
59
+
60
+ c.option '-r', '--repo STRING', String, '(required) Repository from which to extract labels (USER/REPO)'
61
+
62
+ c.example "Repository is repository name", "github_labeler export -r foo/bar"
63
+
64
+ c.action do |args, options|
65
+ options.default \
66
+ :verbose => false,
67
+ :execute => false,
68
+ :token => ENV["GITHUB_OAUTH_TOKEN"]
69
+
70
+ raise ArgumentError.new("Token is required") unless options.token
71
+ raise ArgumentError.new("Repo is required") unless options.repo
72
+ raise ArgumentError.new("Token is required") unless options.token
73
+
74
+ labeler = GitHubLabeler.new(options.token, options.verbose)
75
+ labels = labeler.export_labels_from_repo(options.repo)
76
+
77
+ puts JSON.pretty_generate(labels)
78
+
79
+ cli.say("Done!")
80
+ end
81
+ end
82
+
83
+ command :execute do |c|
84
+ c.syntax = 'github_labeler execute [options]'
85
+ c.description = 'Execute changes previously created by the program'
86
+
87
+ c.option '-c', '--changes STRING', String, '(required) Name of JSON file with list of changes'
88
+
89
+ c.example "Changes are specified in JSON file", "github_labeler export -c changes.json"
90
+
91
+ c.action do |args, options|
92
+ options.default \
93
+ :verbose => false,
94
+ :execute => false,
95
+ :token => ENV["GITHUB_OAUTH_TOKEN"]
96
+
97
+ raise ArgumentError.new("Token is required") unless options.token
98
+ raise ArgumentError.new("Changes are required") unless options.changes
99
+
100
+ labeler = GitHubLabeler.new(options.token, options.verbose)
101
+ changes = JSON.parse(IO.read(options.changes), {:symbolize_names => true})
102
+ result = labeler.execute_changes(changes)
103
+
104
+ puts JSON.pretty_generate(result)
105
+
106
+ cli.say("Done!")
107
+ end
108
+ end
109
+
110
+ command :add do |c|
111
+ c.syntax = 'github_labeler add [options]'
112
+ c.description = 'Add one or more label to one or more repositories'
113
+
114
+ c.option '-l', '--labels STRING', String, '(required) Label (NAME#COLOR) or name of JSON file with list of labels'
115
+ c.option '-r', '--repos STRING', String, '(required) Destination repository to add labels to (USER/REPO) or name of JSON file with list of repositories'
116
+
117
+ c.example "Label is a string and destination repo is repository name", "github_labeler add -r github/linguist -l api#c7def8"
118
+ c.example "Labels and repositories are specified in JSON files", "github_labeler add -r repos.json -l labels.json"
119
+
120
+ c.action do |args, options|
121
+ options.default \
122
+ :verbose => false,
123
+ :execute => false,
124
+ :token => ENV["GITHUB_OAUTH_TOKEN"]
125
+
126
+ raise ArgumentError.new("Token is required") unless options.token
127
+ raise ArgumentError.new("Labels are required") unless options.labels
128
+ raise ArgumentError.new("Repos are required") unless options.repos
129
+
130
+ labeler = GitHubLabeler.new(options.token, options.verbose)
131
+ repos = File.file?(options.repos) ? JSON.parse(IO.read(options.repos), {:symbolize_names => true}) : [options.repos]
132
+ labels = options.labels
133
+
134
+ if File.file?(options.labels)
135
+ labels = JSON.parse(IO.read(options.labels), {:symbolize_names => true})
136
+ else
137
+ label_name, label_color = labels.split("#")
138
+ labels = [{:name => label_name, :color => label_color}]
139
+ end
140
+
141
+ changes = labeler.add_labels_to_repos(repos, labels)
142
+
143
+ puts JSON.pretty_generate(changes)
144
+
145
+ if changes.size == 0
146
+ cli.say("No changes to execute.")
147
+ elsif options.execute or cli.agree("Execute #{changes.size} changes? [y/n]")
148
+ labeler.execute_changes(changes)
149
+ end
150
+
151
+ cli.say("Done!")
152
+ end
153
+ end
154
+
155
+ command :delete do |c|
156
+ c.syntax = 'github_labeler delete [options]'
157
+ c.description = 'Delete one or more label from one or more repositories'
158
+
159
+ c.option '-l', '--labels STRING', String, '(required) Label (NAME) or name of JSON file with list of labels'
160
+ c.option '-r', '--repos STRING', String, '(required) Destination repository to delete labels from (USER/REPO) or name of JSON file with list of repositories'
161
+
162
+ c.example "Label is a string and destination repo is repository name", "github_labeler delete -r github/linguist -l api"
163
+ c.example "Labels and repositories are specified in JSON files", "github_labeler delete -r repos.json -l labels.json"
164
+
165
+ c.action do |args, options|
166
+ options.default \
167
+ :verbose => false,
168
+ :execute => false,
169
+ :token => ENV["GITHUB_OAUTH_TOKEN"]
170
+
171
+ raise ArgumentError.new("Token is required") unless options.token
172
+ raise ArgumentError.new("Labels are required") unless options.labels
173
+ raise ArgumentError.new("Repos are required") unless options.repos
174
+
175
+ labeler = GitHubLabeler.new(options.token, options.verbose)
176
+ repos = File.file?(options.repos) ? JSON.parse(IO.read(options.repos), {:symbolize_names => true}) : [options.repos]
177
+ labels = options.labels
178
+
179
+ if File.file?(options.labels)
180
+ labels = JSON.parse(IO.read(options.labels), {:symbolize_names => true})
181
+ else
182
+ labels = [{:name => labels, :color => nil}]
183
+ end
184
+
185
+ changes = labeler.delete_labels_from_repos(repos, labels)
186
+
187
+ puts JSON.pretty_generate(changes)
188
+
189
+ if changes.size == 0
190
+ cli.say("No changes to execute.")
191
+ elsif options.execute or cli.agree("Execute #{changes.size} changes? [y/n]")
192
+ labeler.execute_changes(changes)
193
+ end
194
+
195
+ cli.say("Done!")
196
+ end
197
+ end
198
+
199
+ command :rename do |c|
200
+ c.syntax = 'github_labeler rename [options]'
201
+ c.description = 'Rename one or more labels in one or more repositories'
202
+
203
+ c.option '-l', '--labels STRING', String, '(required) Label (OLDNAME/NEWNAME) or name of JSON file with list of labels'
204
+ c.option '-r', '--repos STRING', String, '(required) Destination repository to rename labels in (USER/REPO) or name of JSON file with list of repositories'
205
+
206
+ c.example "Label is a string and destination repo is repository name", "github_labeler rename -r github/linguist -l design/ui"
207
+ c.example "Labels and repositories are specified in JSON files", "github_labeler rename -r repos.json -l labels.json"
208
+
209
+ c.action do |args, options|
210
+ options.default \
211
+ :verbose => false,
212
+ :execute => false,
213
+ :token => ENV["GITHUB_OAUTH_TOKEN"]
214
+
215
+ raise ArgumentError.new("Token is required") unless options.token
216
+ raise ArgumentError.new("Labels are required") unless options.labels
217
+ raise ArgumentError.new("Repos are required") unless options.repos
218
+
219
+ labeler = GitHubLabeler.new(options.token, options.verbose)
220
+ repos = File.file?(options.repos) ? JSON.parse(IO.read(options.repos), {:symbolize_names => true}) : [options.repos]
221
+ labels = options.labels
222
+
223
+ if File.file?(options.labels)
224
+ labels = JSON.parse(IO.read(options.labels), {:symbolize_names => true})
225
+ else
226
+ old_name, new_name = labels.split("/")
227
+ labels = [{:name => old_name, :new_name => new_name}]
228
+ end
229
+
230
+ changes = labeler.rename_labels_in_repos(repos, labels)
231
+
232
+ puts JSON.pretty_generate(changes)
233
+
234
+ if changes.size == 0
235
+ cli.say("No changes to execute.")
236
+ elsif options.execute or cli.agree("Execute #{changes.size} changes? [y/n]")
237
+ labeler.execute_changes(changes)
238
+ end
239
+
240
+ cli.say("Done!")
241
+ end
242
+ end
243
+
244
+ command :recolor do |c|
245
+ c.syntax = 'github_labeler recolor [options]'
246
+ c.description = 'Recolor one or more labels in one or more repositories'
247
+
248
+ c.option '-l', '--labels STRING', String, '(required) Label (NAME#COLOR) or name of JSON file with list of labels'
249
+ c.option '-r', '--repos STRING', String, '(required) Destination repository to recolor labels in (USER/REPO) or name of JSON file with list of repositories'
250
+
251
+ c.example "Label is a string and destination repo is repository name", "github_labeler recolor -r github/linguist -l api#c7def8"
252
+ c.example "Labels and repositories are specified in JSON files", "github_labeler recolor -r repos.json -l labels.json"
253
+
254
+ c.action do |args, options|
255
+ options.default \
256
+ :verbose => false,
257
+ :execute => false,
258
+ :token => ENV["GITHUB_OAUTH_TOKEN"]
259
+
260
+ raise ArgumentError.new("Token is required") unless options.token
261
+ raise ArgumentError.new("Labels are required") unless options.labels
262
+ raise ArgumentError.new("Repos are required") unless options.repos
263
+
264
+ labeler = GitHubLabeler.new(options.token, options.verbose)
265
+ repos = File.file?(options.repos) ? JSON.parse(IO.read(options.repos), {:symbolize_names => true}) : [options.repos]
266
+ labels = options.labels
267
+
268
+ if File.file?(options.labels)
269
+ labels = JSON.parse(IO.read(options.labels), {:symbolize_names => true})
270
+ else
271
+ label_name, label_color = labels.split("#")
272
+ labels = [{:name => label_name, :color => label_color}]
273
+ end
274
+
275
+ changes = labeler.recolor_labels_in_repos(repos, labels)
276
+
277
+ puts JSON.pretty_generate(changes)
278
+
279
+ if changes.size == 0
280
+ cli.say("No changes to execute.")
281
+ elsif options.execute or cli.agree("Execute #{changes.size} changes? [y/n]")
282
+ labeler.execute_changes(changes)
283
+ end
284
+
285
+ cli.say("Done!")
286
+ end
287
+ end
288
+ end
@@ -0,0 +1,298 @@
1
+ require "json"
2
+ require "logger"
3
+ require "Octokit"
4
+
5
+ Octokit.auto_paginate = true
6
+
7
+ class GitHubLabeler
8
+ attr_accessor :client, # Octokit client for acesing the API
9
+ :repo_labels, # Labels cache for repositories
10
+ :logger # Logger for writing debugging info
11
+
12
+ def initialize(token, verbose=false, labels=nil)
13
+ @logger = Logger.new(STDERR)
14
+ @logger.sev_threshold = verbose ? Logger::DEBUG : Logger::WARN
15
+
16
+ @logger.debug "Creating new GitHubLabeler instance."
17
+
18
+ @logger.debug "Creating a new Octokit client with token #{token[0..5]}"
19
+
20
+ begin
21
+ @client = Octokit::Client.new(:access_token => token)
22
+ @client.rate_limit
23
+ rescue Octokit::Unauthorized => exception
24
+ @logger.error "Token #{token[0..5]} is not valid"
25
+ raise ArgumentError.new("Token #{token[0..5]} is not valid")
26
+ end
27
+
28
+ @logger.debug "Token #{token[0..5]} is valid"
29
+
30
+ @repo_labels = !labels.nil? ? labels : {}
31
+ end
32
+
33
+ #
34
+ # Checks if the remaining rate limit allows us to make the specified number of
35
+ # changes
36
+ #
37
+ def is_remaining_rate_limit_sufficient?(expected_number_of_calls)
38
+ remaining_limit = @client.rate_limit.remaining
39
+
40
+ @logger.debug "There are #{expected_number_of_calls} API calls to be made, and the remaining API rate limit quota is #{remaining_limit}."
41
+
42
+ return expected_number_of_calls <= @client.rate_limit.remaining
43
+ end
44
+
45
+ #
46
+ # Executes a list of label changes. Each change has the following format:
47
+ #
48
+ # {
49
+ # :type => "update/create/delete",
50
+ # :repo => "testrename/testing",
51
+ # :label => {:color => "fc2929", :name => "design", :new_name => "ui"}
52
+ # }
53
+ #
54
+ def execute_changes(changes, options = {})
55
+ @logger.debug "Executing changes"
56
+
57
+ if !is_remaining_rate_limit_sufficient?(changes.size)
58
+ @logger.error "Remaining rate limit is not enough to make all changes. Wait for the limit to refresh and try again."
59
+ return []
60
+ end
61
+
62
+ changes = validate_changes(changes, options)
63
+
64
+ changes.each do |change|
65
+ @logger.debug "Executing change: #{change_string(change)}"
66
+
67
+ if change[:type] == "add"
68
+ success = @client.add_label(change[:repo], change[:label][:name], change[:label][:color])
69
+
70
+ if !@repo_labels[change[:repo]].nil?
71
+ @repo_labels[change[:repo]][success[:name].downcase] = {:name => success[:name], :color => success[:color]}
72
+ end
73
+
74
+ @logger.debug "Change succeded"
75
+ next
76
+ end
77
+
78
+ if change[:type] == "update"
79
+ new_label = {:name => change[:label][:new_name] || change[:label][:name]}
80
+ if !change[:label][:color].nil?
81
+ new_label[:color] = change[:label][:color]
82
+ end
83
+
84
+ success = @client.update_label(change[:repo], change[:label][:name], new_label)
85
+
86
+ if !@repo_labels[change[:repo]].nil?
87
+ @repo_labels[change[:repo]][success[:name].downcase] = {:name => success[:name], :color => success[:color]}
88
+ end
89
+
90
+ @logger.debug "Change succeded"
91
+ next
92
+ end
93
+
94
+ if change[:type] == "delete"
95
+ success = @client.delete_label!(change[:repo], change[:label][:name])
96
+
97
+ if !@repo_labels[change[:repo]].nil?
98
+ @repo_labels[change[:repo]][change[:label][:name].downcase] = nil
99
+ end
100
+
101
+ @logger.debug "Change succeded"
102
+ next
103
+ end
104
+ end
105
+
106
+ @logger.debug "Done executing changes"
107
+
108
+ return changes
109
+ end
110
+
111
+ #
112
+ # Updates repo labels cache for a specific label
113
+ #
114
+ def update_repos_labels_cache(repo, label)
115
+ @repo_labels[change[:repo]][change[:label][:name].downcase] = success
116
+ end
117
+
118
+ #
119
+ # Validates changes by merging multiple updates into one, removing
120
+ # duplicates, detecting conflicts, etc.
121
+ #
122
+ def validate_changes(changes, options = {})
123
+ return changes.sort_by { |hsh| [hsh["repo"], hsh["type"]] }
124
+ end
125
+
126
+ #
127
+ # Create changes for adding a list of labels to a list of repos
128
+ #
129
+ def add_labels_to_repos(repos, labels, options = {})
130
+ @logger.debug "Adding labels to repositories"
131
+ return process_labels_for_repos(repos, labels, method(:add_label_to_repo), options)
132
+ end
133
+
134
+ #
135
+ # Create changes for deleting a list of labels from a list of repos
136
+ #
137
+ def delete_labels_from_repos(repos, labels, options = {})
138
+ @logger.debug "Deleting labels from repositories"
139
+ return process_labels_for_repos(repos, labels, method(:delete_label_from_repo), options)
140
+ end
141
+
142
+ #
143
+ # Create changes for renaming a list of labels in a list of repos
144
+ #
145
+ def rename_labels_in_repos(repos, labels, options = {})
146
+ @logger.debug "Renaming labels in repositories"
147
+ return process_labels_for_repos(repos, labels, method(:rename_label_in_repo), options)
148
+ end
149
+
150
+ #
151
+ # Create changes for recoloring a list of labels in a list of repos
152
+ #
153
+ def recolor_labels_in_repos(repos, labels, options = {})
154
+ @logger.debug "Recoloring labels in repositories"
155
+ return process_labels_for_repos(repos, labels, method(:recolor_label_in_repo), options)
156
+ end
157
+
158
+ #
159
+ # Generic creation of changes for list of labels and list of repos
160
+ #
161
+ def process_labels_for_repos(repos, labels, change_creator, options = {})
162
+ if !is_remaining_rate_limit_sufficient?(repos.size)
163
+ @logger.error "Rate limit is not enough to process all labels in repositories"
164
+ return nil
165
+ end
166
+
167
+ changes = []
168
+
169
+ for repo in repos
170
+ repo_name = repo if repo.instance_of?(String)
171
+ repo_name = repo[:full_name] if repo.instance_of?(Hash)
172
+
173
+ @logger.debug "Processing labels for repository #{repo_name}"
174
+
175
+ refresh_repo_labels(repo_name, options)
176
+
177
+ for label in labels
178
+ @logger.debug "Processing label #{label[:name]}"
179
+ change = change_creator.call(repo_name, label, options)
180
+ changes << change if !change.nil?
181
+ end
182
+ end
183
+
184
+ return changes
185
+ end
186
+
187
+ #
188
+ # Fetches the list of labels in a repository and stores them
189
+ # in the labels cache
190
+ #
191
+ def refresh_repo_labels(repo, options = {})
192
+ @logger.debug "Fetching label information for #{repo}"
193
+
194
+ @repo_labels[repo] = {}
195
+ @client.labels(repo).each do |label|
196
+ @repo_labels[repo][label[:name].downcase] = label
197
+ end
198
+ end
199
+
200
+ #
201
+ # Create a single change for adding a label to a repo
202
+ #
203
+ def add_label_to_repo(repo, label, options = {})
204
+ existing_label = @repo_labels[repo][label[:name].downcase]
205
+
206
+ if existing_label
207
+ if existing_label[:color] != label[:color] or existing_label[:name] != label[:name]
208
+ @logger.warn "Label #{label[:name]} already exist, creating an update"
209
+ return { :type => "update", :repo => repo, :label => label }
210
+ end
211
+ else
212
+ return { :type => "add", :repo => repo, :label => label }
213
+ end
214
+
215
+ @logger.warn "Label #{label[:name]} already exist and is the same. No change created"
216
+ return nil
217
+ end
218
+
219
+ #
220
+ # Create a single change for deleting a label from a repo
221
+ #
222
+ def delete_label_from_repo(repo, label, options = {})
223
+ existing_label = @repo_labels[repo][label[:name].downcase]
224
+
225
+ if existing_label
226
+ return { :type => "delete", :repo => repo, :label => label }
227
+ end
228
+
229
+ @logger.warn "Label #{label[:name]} doesn't exist. No change created"
230
+ return nil
231
+ end
232
+
233
+ #
234
+ # Create a single change for renaming a label in a repo
235
+ #
236
+ def rename_label_in_repo(repo, label, options = {})
237
+ existing_label = @repo_labels[repo][label[:name].downcase]
238
+
239
+ if existing_label
240
+ if label[:new_name] and label[:new_name] != label[:name]
241
+ return { :type => "update", :repo => repo, :label => label }
242
+ end
243
+ else
244
+ @logger.warn "Label #{label[:name]} doesn't exist. Creating a create change"
245
+ newLabel = {:name => label[:new_name], :color => label[:color]}
246
+ return { :type => "add", :repo => repo, :label => newLabel }
247
+ end
248
+
249
+ @logger.warn "Label #{label[:name]} exist and is the same. No change created"
250
+ return nil
251
+ end
252
+
253
+ #
254
+ # Create a single change for recoloring a label in a repo
255
+ #
256
+ def recolor_label_in_repo(repo, label, options = {})
257
+ existing_label = @repo_labels[repo][label[:name].downcase]
258
+
259
+ if existing_label
260
+ if existing_label[:color] != label[:color]
261
+ return { :type => "update", :repo => repo, :label => label }
262
+ end
263
+ else
264
+ @logger.warn "Label #{label[:name]} doesn't exist. Creating a create change"
265
+ return { :type => "add", :repo => repo, :label => label }
266
+ end
267
+
268
+ @logger.warn "Label #{label[:name]} exist and is the same. No change created"
269
+ return nil
270
+ end
271
+
272
+ #
273
+ # Creates changes for duplicating labels from one repo to other repos
274
+ #
275
+ def duplicate_labels_from_repo(repo_source, repos_end, options = {})
276
+ @logger.debug "Duplicating labels from repo #{repo_source}"
277
+ source_repo_labels = export_labels_from_repo(repo_source, options)
278
+ return add_labels_to_repos(repos_end, source_repo_labels, options)
279
+ end
280
+
281
+ #
282
+ # Fetches a list of labels for a repository
283
+ #
284
+ def export_labels_from_repo(repo, options = {})
285
+ @logger.debug "Exporting labels from repo #{repo}"
286
+
287
+ @client.labels(repo).map do |label|
288
+ { :name => label[:name], :color => label[:color] }
289
+ end
290
+ end
291
+
292
+ #
293
+ # Get a human readable string representation of a change
294
+ #
295
+ def change_string(change, options = {})
296
+ return "#{change[:repo]} - #{change[:type]} - #{change[:label][:name]} - color: #{change[:label][:color]} - new_name: #{change[:label][:new_name]}"
297
+ end
298
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: github_labeler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ivan Zuzak
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: commander
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: octokit
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.3'
55
+ description:
56
+ email: izuzak@gmail.com
57
+ executables:
58
+ - github_labeler
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - bin/github_labeler
63
+ - lib/github_labeler.rb
64
+ homepage: https://github.com/izuzak/github_labeler
65
+ licenses:
66
+ - MIT
67
+ metadata: {}
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project:
84
+ rubygems_version: 2.2.3
85
+ signing_key:
86
+ specification_version: 4
87
+ summary: Utility for running actions on issue labels in groups of GitHub repositories
88
+ test_files: []