Class: GitHubLabeler

Inherits:
Object
  • Object
show all
Defined in:
lib/github_labeler.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(token, verbose = false, labels = nil) ⇒ GitHubLabeler

Returns a new instance of GitHubLabeler.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/github_labeler.rb', line 12

def initialize(token, verbose=false, labels=nil)
  @logger = Logger.new(STDERR)
  @logger.sev_threshold = verbose ? Logger::DEBUG : Logger::WARN

  @logger.debug "Creating new GitHubLabeler instance."

  @logger.debug "Creating a new Octokit client with token #{token[0..5]}"

  begin
    @client = Octokit::Client.new(:access_token => token)
    @client.rate_limit
  rescue Octokit::Unauthorized => exception
    @logger.error "Token #{token[0..5]} is not valid"
    raise ArgumentError.new("Token #{token[0..5]} is not valid")
  end

  @logger.debug "Token #{token[0..5]} is valid"

  @repo_labels = !labels.nil? ? labels : {}
end

Instance Attribute Details

#clientObject

Octokit client for acesing the API



8
9
10
# File 'lib/github_labeler.rb', line 8

def client
  @client
end

#loggerObject

Octokit client for acesing the API



8
9
10
# File 'lib/github_labeler.rb', line 8

def logger
  @logger
end

#repo_labelsObject

Octokit client for acesing the API



8
9
10
# File 'lib/github_labeler.rb', line 8

def repo_labels
  @repo_labels
end

Instance Method Details

#add_label_to_repo(repo, label, options = {}) ⇒ Object

Create a single change for adding a label to a repo



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/github_labeler.rb', line 203

def add_label_to_repo(repo, label, options = {})
  existing_label = @repo_labels[repo][label[:name].downcase]

  if existing_label
    if existing_label[:color] != label[:color] or existing_label[:name] != label[:name]
      @logger.warn "Label #{label[:name]} already exist, creating an update"
      return { :type => "update", :repo => repo, :label => label }
    end
  else
    return { :type => "add", :repo => repo, :label => label }
  end

  @logger.warn "Label #{label[:name]} already exist and is the same. No change created"
  return nil
end

#add_labels_to_repos(repos, labels, options = {}) ⇒ Object

Create changes for adding a list of labels to a list of repos



129
130
131
132
# File 'lib/github_labeler.rb', line 129

def add_labels_to_repos(repos, labels, options = {})
  @logger.debug "Adding labels to repositories"
  return process_labels_for_repos(repos, labels, method(:add_label_to_repo), options)
end

#change_string(change, options = {}) ⇒ Object

Get a human readable string representation of a change



295
296
297
# File 'lib/github_labeler.rb', line 295

def change_string(change, options = {})
  return "#{change[:repo]} - #{change[:type]} - #{change[:label][:name]} - color: #{change[:label][:color]} - new_name: #{change[:label][:new_name]}"
end

#delete_label_from_repo(repo, label, options = {}) ⇒ Object

Create a single change for deleting a label from a repo



222
223
224
225
226
227
228
229
230
231
# File 'lib/github_labeler.rb', line 222

def delete_label_from_repo(repo, label, options = {})
  existing_label = @repo_labels[repo][label[:name].downcase]

  if existing_label
    return { :type => "delete", :repo => repo, :label => label }
  end

  @logger.warn "Label #{label[:name]} doesn't exist. No change created"
  return nil
end

#delete_labels_from_repos(repos, labels, options = {}) ⇒ Object

Create changes for deleting a list of labels from a list of repos



137
138
139
140
# File 'lib/github_labeler.rb', line 137

def delete_labels_from_repos(repos, labels, options = {})
  @logger.debug "Deleting labels from repositories"
  return process_labels_for_repos(repos, labels, method(:delete_label_from_repo), options)
end

#duplicate_labels_from_repo(repo_source, repos_end, options = {}) ⇒ Object

Creates changes for duplicating labels from one repo to other repos



275
276
277
278
279
# File 'lib/github_labeler.rb', line 275

def duplicate_labels_from_repo(repo_source, repos_end, options = {})
  @logger.debug "Duplicating labels from repo #{repo_source}"
  source_repo_labels = export_labels_from_repo(repo_source, options)
  return add_labels_to_repos(repos_end, source_repo_labels, options)
end

#execute_changes(changes, options = {}) ⇒ Object

Executes a list of label changes. Each change has the following format:

:type => "update/create/delete",
:repo => "testrename/testing",
:label => {:color => "fc2929", :name => "design", :new_name => "ui"

}



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/github_labeler.rb', line 54

def execute_changes(changes, options = {})
  @logger.debug "Executing changes"

  if !is_remaining_rate_limit_sufficient?(changes.size)
    @logger.error "Remaining rate limit is not enough to make all changes. Wait for the limit to refresh and try again."
    return []
  end

  changes = validate_changes(changes, options)

  changes.each do |change|
    @logger.debug "Executing change: #{change_string(change)}"

    if change[:type] == "add"
      success = @client.add_label(change[:repo], change[:label][:name], change[:label][:color])

      if !@repo_labels[change[:repo]].nil?
        @repo_labels[change[:repo]][success[:name].downcase] = {:name => success[:name], :color => success[:color]}
      end

      @logger.debug "Change succeded"
      next
    end

    if change[:type] == "update"
      new_label = {:name => change[:label][:new_name] || change[:label][:name]}
      if !change[:label][:color].nil?
        new_label[:color] = change[:label][:color]
      end

      success = @client.update_label(change[:repo], change[:label][:name], new_label)

      if !@repo_labels[change[:repo]].nil?
        @repo_labels[change[:repo]][success[:name].downcase] = {:name => success[:name], :color => success[:color]}
      end

      @logger.debug "Change succeded"
      next
    end

    if change[:type] == "delete"
      success = @client.delete_label!(change[:repo], change[:label][:name])

      if !@repo_labels[change[:repo]].nil?
        @repo_labels[change[:repo]][change[:label][:name].downcase] = nil
      end

      @logger.debug "Change succeded"
      next
    end
  end

  @logger.debug "Done executing changes"

  return changes
end

#export_labels_from_repo(repo, options = {}) ⇒ Object

Fetches a list of labels for a repository



284
285
286
287
288
289
290
# File 'lib/github_labeler.rb', line 284

def export_labels_from_repo(repo, options = {})
  @logger.debug "Exporting labels from repo #{repo}"

  @client.labels(repo).map do |label|
    { :name => label[:name], :color => label[:color] }
  end
end

#is_remaining_rate_limit_sufficient?(expected_number_of_calls) ⇒ Boolean

Checks if the remaining rate limit allows us to make the specified number of changes

Returns:



37
38
39
40
41
42
43
# File 'lib/github_labeler.rb', line 37

def is_remaining_rate_limit_sufficient?(expected_number_of_calls)
  remaining_limit = @client.rate_limit.remaining

  @logger.debug "There are #{expected_number_of_calls} API calls to be made, and the remaining API rate limit quota is #{remaining_limit}."

  return expected_number_of_calls <= @client.rate_limit.remaining
end

#process_labels_for_repos(repos, labels, change_creator, options = {}) ⇒ Object

Generic creation of changes for list of labels and list of repos



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/github_labeler.rb', line 161

def process_labels_for_repos(repos, labels, change_creator, options = {})
  if !is_remaining_rate_limit_sufficient?(repos.size)
    @logger.error "Rate limit is not enough to process all labels in repositories"
    return nil
  end

  changes = []

  for repo in repos
    repo_name = repo if repo.instance_of?(String)
    repo_name = repo[:full_name] if repo.instance_of?(Hash)

    @logger.debug "Processing labels for repository #{repo_name}"

    refresh_repo_labels(repo_name, options)

    for label in labels
      @logger.debug "Processing label #{label[:name]}"
      change = change_creator.call(repo_name, label, options)
      changes << change if !change.nil?
    end
  end

  return changes
end

#recolor_label_in_repo(repo, label, options = {}) ⇒ Object

Create a single change for recoloring a label in a repo



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/github_labeler.rb', line 256

def recolor_label_in_repo(repo, label, options = {})
  existing_label = @repo_labels[repo][label[:name].downcase]

  if existing_label
    if existing_label[:color] != label[:color]
      return { :type => "update", :repo => repo, :label => label }
    end
  else
    @logger.warn "Label #{label[:name]} doesn't exist. Creating a create change"
    return { :type => "add", :repo => repo, :label => label }
  end

  @logger.warn "Label #{label[:name]} exist and is the same. No change created"
  return nil
end

#recolor_labels_in_repos(repos, labels, options = {}) ⇒ Object

Create changes for recoloring a list of labels in a list of repos



153
154
155
156
# File 'lib/github_labeler.rb', line 153

def recolor_labels_in_repos(repos, labels, options = {})
  @logger.debug "Recoloring labels in repositories"
  return process_labels_for_repos(repos, labels, method(:recolor_label_in_repo), options)
end

#refresh_repo_labels(repo, options = {}) ⇒ Object

Fetches the list of labels in a repository and stores them in the labels cache



191
192
193
194
195
196
197
198
# File 'lib/github_labeler.rb', line 191

def refresh_repo_labels(repo, options = {})
  @logger.debug "Fetching label information for #{repo}"

  @repo_labels[repo] = {}
  @client.labels(repo).each do |label|
    @repo_labels[repo][label[:name].downcase] = label
  end
end

#rename_label_in_repo(repo, label, options = {}) ⇒ Object

Create a single change for renaming a label in a repo



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/github_labeler.rb', line 236

def rename_label_in_repo(repo, label, options = {})
  existing_label = @repo_labels[repo][label[:name].downcase]

  if existing_label
    if label[:new_name] and label[:new_name] != label[:name]
      return { :type => "update", :repo => repo, :label => label }
    end
  else
    @logger.warn "Label #{label[:name]} doesn't exist. Creating a create change"
    newLabel = {:name => label[:new_name], :color => label[:color]}
    return { :type => "add", :repo => repo, :label => newLabel }
  end

  @logger.warn "Label #{label[:name]} exist and is the same. No change created"
  return nil
end

#rename_labels_in_repos(repos, labels, options = {}) ⇒ Object

Create changes for renaming a list of labels in a list of repos



145
146
147
148
# File 'lib/github_labeler.rb', line 145

def rename_labels_in_repos(repos, labels, options = {})
  @logger.debug "Renaming labels in repositories"
  return process_labels_for_repos(repos, labels, method(:rename_label_in_repo), options)
end

#update_repos_labels_cache(repo, label) ⇒ Object

Updates repo labels cache for a specific label



114
115
116
# File 'lib/github_labeler.rb', line 114

def update_repos_labels_cache(repo, label)
  @repo_labels[change[:repo]][change[:label][:name].downcase] = success
end

#validate_changes(changes, options = {}) ⇒ Object

Validates changes by merging multiple updates into one, removing duplicates, detecting conflicts, etc.



122
123
124
# File 'lib/github_labeler.rb', line 122

def validate_changes(changes, options = {})
  return changes.sort_by { |hsh| [hsh["repo"], hsh["type"]] }
end