Class: MultiRepo::Service::Github

Inherits:
Object
  • Object
show all
Defined in:
lib/multi_repo/service/github.rb

Constant Summary collapse

PR_REGEX =
%r{^([^/#]+/[^/#]+)#(\d+)$}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(dry_run: false) ⇒ Github

Returns a new instance of Github.



113
114
115
116
117
# File 'lib/multi_repo/service/github.rb', line 113

def initialize(dry_run: false)
  require "octokit"

  @dry_run = dry_run
end

Instance Attribute Details

#dry_runObject (readonly)

Returns the value of attribute dry_run.



111
112
113
# File 'lib/multi_repo/service/github.rb', line 111

def dry_run
  @dry_run
end

Class Method Details

.api_endpointObject



13
14
15
# File 'lib/multi_repo/service/github.rb', line 13

def self.api_endpoint
  @api_endpoint ||= ENV["GITHUB_API_ENDPOINT"]
end

.api_endpoint=(endpoint) ⇒ Object



17
18
19
# File 'lib/multi_repo/service/github.rb', line 17

def self.api_endpoint=(endpoint)
  @api_endpoint = endpoint
end

.api_tokenObject



5
6
7
# File 'lib/multi_repo/service/github.rb', line 5

def self.api_token
  @api_token ||= ENV["GITHUB_API_TOKEN"]
end

.api_token=(token) ⇒ Object



9
10
11
# File 'lib/multi_repo/service/github.rb', line 9

def self.api_token=(token)
  @api_token = token
end

.clientObject



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/multi_repo/service/github.rb', line 21

def self.client
  @client ||= begin
    raise "Missing GitHub API Token" if api_token.nil?

    params = {
      :api_endpoint  => api_endpoint,
      :access_token  => api_token,
      :auto_paginate => true
    }.compact

    require 'octokit'

    if ENV["DEBUG"]
      middleware = Octokit.middleware.dup
      middleware.response :logger
      Octokit.middleware = middleware
    end

    Octokit::Client.new(params)
  end
end

.disabled_workflows(repo_name) ⇒ Object



89
90
91
# File 'lib/multi_repo/service/github.rb', line 89

def self.disabled_workflows(repo_name)
  client.workflows(repo_name)[:workflows].select { |w| w.state == "disabled_inactivity" }
end

.find_milestone_by_title(repo_name, title) ⇒ Object



59
60
61
# File 'lib/multi_repo/service/github.rb', line 59

def self.find_milestone_by_title(repo_name, title)
  client.list_milestones(repo_name, :state => :all).detect { |m| m.title.casecmp?(title) }
end

.find_team_by_name(org, team) ⇒ Object



67
68
69
# File 'lib/multi_repo/service/github.rb', line 67

def self.find_team_by_name(org, team)
  client.org_teams(org).detect { |t| t.slug == team }
end

.org_member_names(org) ⇒ Object



63
64
65
# File 'lib/multi_repo/service/github.rb', line 63

def self.org_member_names(org)
  client.org_members(org).map(&:login).sort_by(&:downcase)
end

.org_repo_names(org, include_forks: false, include_archived: false) ⇒ Object



43
44
45
46
47
48
# File 'lib/multi_repo/service/github.rb', line 43

def self.org_repo_names(org, include_forks: false, include_archived: false)
  repos = client.list_repositories(org, :type => "sources")
  repos.reject!(&:fork?) unless include_forks
  repos.reject!(&:archived?) unless include_archived
  repos.map(&:full_name).sort
end

.parse_milestone_date(date) ⇒ Object



54
55
56
57
# File 'lib/multi_repo/service/github.rb', line 54

def self.parse_milestone_date(date)
  require "active_support/core_ext/time"
  ActiveSupport::TimeZone.new('Pacific Time (US & Canada)').parse(date) # LOL GitHub, TimeZones are hard
end

.parse_prs(*prs) ⇒ Object

Parse a list of PRs that are in URL or org/repo#pr format into a Array of

repo_name, pr_number

entries.



97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/multi_repo/service/github.rb', line 97

def self.parse_prs(*prs)
  prs.flatten.map do |pr|
    # Normalize to org/repo#pr
    normalized_pr = pr.sub("https://github.com/", "").sub("/pull/", "#")

    if (match = PR_REGEX.match(normalized_pr))
      repo_name, pr_number = match.captures
      [repo_name, pr_number.to_i]
    else
      raise ArgumentError, "Invalid PR '#{pr}'. PR must be a GitHub URL or in org/repo#pr format."
    end
  end
end

.team_ids_by_name(org) ⇒ Object



80
81
82
83
# File 'lib/multi_repo/service/github.rb', line 80

def self.team_ids_by_name(org)
  @team_ids_by_name ||= {}
  @team_ids_by_name[org] ||= client.org_teams(org).map { |t| [t.slug, t.id] }.sort.to_h
end

.team_member_names(org, team) ⇒ Object



76
77
78
# File 'lib/multi_repo/service/github.rb', line 76

def self.team_member_names(org, team)
  team_members(org, team).map(&:login).sort_by(&:downcase)
end

.team_members(org, team) ⇒ Object



71
72
73
74
# File 'lib/multi_repo/service/github.rb', line 71

def self.team_members(org, team)
  team_id = find_team_by_name(org, team)&.id
  team_id ? client.team_members(team_id) : []
end

.team_names(org) ⇒ Object



85
86
87
# File 'lib/multi_repo/service/github.rb', line 85

def self.team_names(org)
  team_ids_by_name(org).keys
end

.valid_milestone_date?(date) ⇒ Boolean

Returns:

  • (Boolean)


50
51
52
# File 'lib/multi_repo/service/github.rb', line 50

def self.valid_milestone_date?(date)
  !!parse_milestone_date(date)
end

Instance Method Details

#add_comment(repo_name, issue_number, body) ⇒ Object



187
188
189
190
191
192
193
# File 'lib/multi_repo/service/github.rb', line 187

def add_comment(repo_name, issue_number, body)
  if dry_run
    puts "** dry-run: github.add_comment(#{repo_name.inspect}, #{issue_number.inspect}, #{body.pretty_inspect.chomp})".light_black
  else
    client.add_comment(repo_name, issue_number, body)
  end
end

#add_labels_to_an_issue(repo_name, issue_number, labels) ⇒ Object



166
167
168
169
170
171
172
173
# File 'lib/multi_repo/service/github.rb', line 166

def add_labels_to_an_issue(repo_name, issue_number, labels)
  labels = Array(labels)
  if dry_run
    puts "** dry-run: github.add_labels_to_an_issue(#{repo_name.inspect}, #{issue_number.inspect}, #{labels.inspect})".light_black
  else
    client.add_labels_to_an_issue(repo_name, issue_number, labels)
  end
end

#add_team_membership(org, team, user) ⇒ Object



236
237
238
239
240
241
242
243
244
# File 'lib/multi_repo/service/github.rb', line 236

def add_team_membership(org, team, user)
  team_id = team_ids_by_name(org)[team]

  if dry_run
    puts "** dry-run: github.add_team_membership(#{team_id.inspect}, #{user.inspect})".light_black
  else
    client.add_team_membership(team_id, user)
  end
end

#assign_user(repo_name, issue_number, assignee) ⇒ Object



195
196
197
198
199
200
201
202
# File 'lib/multi_repo/service/github.rb', line 195

def assign_user(repo_name, issue_number, assignee)
  assignee = assignee[1..] if assignee.start_with?("@")
  if dry_run
    puts "** dry-run: github.update_issue(#{repo_name.inspect}, #{issue_number.inspect}, \"assignee\" => #{assignee.inspect})".light_black
  else
    client.update_issue(repo_name, issue_number, "assignee" => assignee)
  end
end

#close_milestone(repo_name, milestone_number) ⇒ Object



220
221
222
223
224
225
226
# File 'lib/multi_repo/service/github.rb', line 220

def close_milestone(repo_name, milestone_number)
  if dry_run
    puts "** dry-run: github.update_milestone(#{repo_name.inspect}, #{milestone_number}, :state => 'closed')".light_black
  else
    client.update_milestone(repo_name, milestone_number, :state => "closed")
  end
end

#create_label(repo_name, label, color) ⇒ Object



139
140
141
142
143
144
145
# File 'lib/multi_repo/service/github.rb', line 139

def create_label(repo_name, label, color)
  if dry_run
    puts "** dry-run: github.add_label(#{repo_name.inspect}, #{label.inspect}, #{color.inspect})".light_black
  else
    client.add_label(repo_name, label, color)
  end
end

#create_milestone(repo_name, title, due_on) ⇒ Object



204
205
206
207
208
209
210
# File 'lib/multi_repo/service/github.rb', line 204

def create_milestone(repo_name, title, due_on)
  if dry_run
    puts "** dry-run: github.create_milestone(#{repo_name.inspect}, #{title.inspect}, :due_on => #{due_on.strftime("%Y-%m-%d").inspect})".light_black
  else
    client.create_milestone(repo_name, title, :due_on => due_on)
  end
end

#create_or_update_repository_secret(repo_name, key, value) ⇒ Object



282
283
284
285
286
287
288
289
290
# File 'lib/multi_repo/service/github.rb', line 282

def create_or_update_repository_secret(repo_name, key, value)
  payload = encode_secret(repo_name, value)

  if dry_run
    puts "** dry-run: github.create_or_update_actions_secret(#{repo_name.inspect}, #{key.inspect}, #{payload.inspect})".light_black
  else
    client.create_or_update_actions_secret(repo_name, key, payload)
  end
end

#delete_label!(repo_name, label) ⇒ Object



158
159
160
161
162
163
164
# File 'lib/multi_repo/service/github.rb', line 158

def delete_label!(repo_name, label)
  if dry_run
    puts "** dry-run: github.delete_label!(#{repo_name.inspect}, #{label.inspect})".light_black
  else
    client.delete_label!(repo_name, label)
  end
end

#edit_repository(repo_name, settings) ⇒ Object



131
132
133
134
135
136
137
# File 'lib/multi_repo/service/github.rb', line 131

def edit_repository(repo_name, settings)
  if dry_run
    puts "** dry-run: github.edit_repository(#{repo_name.inspect}, #{settings.inspect[1..-2]})".light_black
  else
    client.edit_repository(repo_name, settings)
  end
end

#enable_workflow(repo_name, workflow_number) ⇒ Object



264
265
266
267
268
269
270
271
272
# File 'lib/multi_repo/service/github.rb', line 264

def enable_workflow(repo_name, workflow_number)
  command = "repos/#{repo_name}/actions/workflows/#{workflow_number}/enable"

  if dry_run
    puts "** dry-run: github.put(#{command.inspect})".light_black
  else
    client.put(command)
  end
end

#merge_pull_request(repo_name, pr_number) ⇒ Object



274
275
276
277
278
279
280
# File 'lib/multi_repo/service/github.rb', line 274

def merge_pull_request(repo_name, pr_number)
  if dry_run
    puts "** dry-run: github.merge_pull_request(#{repo_name.inspect}, #{pr_number.inspect})".light_black
  else
    client.merge_pull_request(repo_name, pr_number)
  end
end

#protect_branch(repo_name, branch, settings) ⇒ Object



228
229
230
231
232
233
234
# File 'lib/multi_repo/service/github.rb', line 228

def protect_branch(repo_name, branch, settings)
  if dry_run
    puts "** dry-run: github.protect_branch(#{repo_name.inspect}, #{branch.inspect}, #{settings.inspect[1..-2]})".light_black
  else
    client.protect_branch(repo_name, branch, settings)
  end
end

#remove_collaborator(repo_name, user) ⇒ Object



256
257
258
259
260
261
262
# File 'lib/multi_repo/service/github.rb', line 256

def remove_collaborator(repo_name, user)
  if dry_run
    puts "** dry-run: github.remove_collaborator(#{repo_name.inspect}, #{user.inspect})".light_black
  else
    client.remove_collaborator(repo_name, user)
  end
end

#remove_labels_from_an_issue(repo_name, issue_number, labels) ⇒ Object



175
176
177
178
179
180
181
182
183
184
185
# File 'lib/multi_repo/service/github.rb', line 175

def remove_labels_from_an_issue(repo_name, issue_number, labels)
  Array(labels).each do |label|
    if dry_run
      puts "** dry-run: github.remove_label(#{repo_name.inspect}, #{issue_number.inspect}, #{label.inspect})".light_black
    else
      client.remove_label(repo_name, issue_number, label)
    end
  rescue Octokit::NotFound
    # Ignore labels that are not found, because we want them removed anyway
  end
end

#remove_team_membership(org, team, user) ⇒ Object



246
247
248
249
250
251
252
253
254
# File 'lib/multi_repo/service/github.rb', line 246

def remove_team_membership(org, team, user)
  team_id = team_ids_by_name(org)[team]

  if dry_run
    puts "** dry-run: github.remove_team_membership(#{team_id.inspect}, #{user.inspect})".light_black
  else
    client.remove_team_membership(team_id, user)
  end
end

#update_label(repo_name, label, color: nil, name: nil) ⇒ Object

Raises:

  • (ArgumentError)


147
148
149
150
151
152
153
154
155
156
# File 'lib/multi_repo/service/github.rb', line 147

def update_label(repo_name, label, color: nil, name: nil)
  settings = {:color => color, :name => name}.compact
  raise ArgumentError, "one of color or name must be passed" if settings.empty?

  if dry_run
    puts "** dry-run: github.update_label(#{repo_name.inspect}, #{label.inspect}, #{settings.inspect[1..-2]})".light_black
  else
    client.update_label(repo_name, label, settings)
  end
end

#update_milestone(repo_name, milestone_number, due_on) ⇒ Object



212
213
214
215
216
217
218
# File 'lib/multi_repo/service/github.rb', line 212

def update_milestone(repo_name, milestone_number, due_on)
  if dry_run
    puts "** dry-run: github.update_milestone(#{repo_name.inspect}, #{milestone_number}, :due_on => #{due_on.strftime("%Y-%m-%d").inspect})".light_black
  else
    client.update_milestone(repo_name, milestone_number, :due_on => due_on)
  end
end