Class: SashimiTanpopo::Provider::GitHub

Inherits:
Base
  • Object
show all
Defined in:
lib/sashimi_tanpopo/provider/github.rb,
sig/sashimi_tanpopo/provider/github.rbs

Overview

Apply recipe files and create Pull Request

Constant Summary collapse

DEFAULT_API_ENDPOINT =

Returns:

  • (String)
"https://api.github.com/"
DEFAULT_GITHUB_HOST =

Returns:

  • (String)
"github.com"

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

#apply_recipe_files, #cleanup_target_dir

Constructor Details

#initialize(recipe_paths:, target_dir:, params:, dry_run:, is_colored:, git_username:, git_email:, commit_message:, repository:, access_token:, api_endpoint: DEFAULT_API_ENDPOINT, pr_title:, pr_body:, pr_source_branch:, pr_target_branch:, pr_assignees: [], pr_reviewers: [], pr_labels: [], pr_auto_merge:, is_draft_pr:, summary_path:, only_changes_summary:) ⇒ GitHub

Returns a new instance of GitHub.

Parameters:

  • recipe_paths (Array<String>)
  • target_dir (String, nil)
  • params (Hash<Symbol, String>)
  • dry_run (Boolean)
  • is_colored (Boolean)

    Whether show color diff

  • git_username (String, nil)
  • git_email (String, nil)
  • commit_message (String)
  • repository (String)
  • access_token (String)
  • api_endpoint (String) (defaults to: DEFAULT_API_ENDPOINT)
  • pr_title (String)
  • pr_body (String)
  • pr_source_branch (String)

    Pull Request source branch (a.k.a. head branch)

  • pr_target_branch (String, nil)

    Pull Request target branch (a.k.a. base branch)

  • pr_assignees (Array<String>) (defaults to: [])
  • pr_reviewers (Array<String>) (defaults to: [])
  • pr_labels (Array<String>) (defaults to: [])
  • pr_auto_merge (Boolean)
  • is_draft_pr (Boolean)

    Whether create draft Pull Request

  • summary_path (String, nil)
  • only_changes_summary (Boolean)


33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/sashimi_tanpopo/provider/github.rb', line 33

def initialize(recipe_paths:, target_dir:, params:, dry_run:, is_colored:,
               git_username:, git_email:, commit_message:,
               repository:, access_token:, api_endpoint: DEFAULT_API_ENDPOINT,
               pr_title:, pr_body:, pr_source_branch:, pr_target_branch:,
               pr_assignees: [], pr_reviewers: [], pr_labels: [], pr_auto_merge:,
               is_draft_pr:, summary_path:, only_changes_summary:)
  super(
    recipe_paths:    recipe_paths,
    target_dir:      target_dir,
    params:          params,
    dry_run:         dry_run,
    is_colored:      is_colored,
    is_update_local: false,
  )

  @commit_message = commit_message
  @repository = repository
  @pr_title = pr_title
  @pr_body = pr_body
  @pr_source_branch = pr_source_branch
  @pr_target_branch = pr_target_branch
  @pr_assignees = pr_assignees
  @pr_reviewers = pr_reviewers
  @pr_labels = pr_labels
  @pr_auto_merge = pr_auto_merge
  @is_draft_pr = is_draft_pr
  @git_username = git_username
  @git_email = git_email
  @api_endpoint = api_endpoint
  @summary_path = summary_path || ""
  @only_changes_summary = only_changes_summary

  @octokit = Octokit::Client.new(api_endpoint: api_endpoint, access_token: access_token)
  @graphql = Graphlient::Client.new(
    "#{api_endpoint.delete_suffix("/")}/graphql",
    headers: {
      "Authorization" => "Bearer #{access_token}",
      "Content-Type" => 'application/json'
    },
  )
end

Class Method Details

.generate_summary(changed_files:, dry_run:) ⇒ String

Parameters:

  • changed_files (Hash<String, { before_content: String, after_content: String, mode: String }>)

    key: f path, value: Hash

  • dry_run (Boolean)
  • changed_files: (changed_files)
  • dry_run: (Boolean)

Returns:

  • (String)


122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/sashimi_tanpopo/provider/github.rb', line 122

def self.generate_summary(changed_files:, dry_run:)
  header = +"## :page_facing_up: sashimi_tanpopo report"
  header << " (dry run)" if dry_run

  lines = [header]

  if changed_files.empty?
    lines.push(
      "no changes",
      "",
    )
  else
    changed_files.each do |path, data|
      lines.push(
        "### :memo: #{path}",
        "```diff",
        SashimiTanpopo::DiffHelper.generate_diff(data[:before_content], data[:after_content], is_colored: false).rstrip,
        "```",
        "",
      )
    end
  end

  lines.join("\n")
end

.github_host(api_endpoint) ⇒ String

Get GitHub host from api_endpoint

Parameters:

  • api_endpoint (String)

Returns:

  • (String)


109
110
111
112
113
114
115
116
# File 'lib/sashimi_tanpopo/provider/github.rb', line 109

def self.github_host(api_endpoint)
  return DEFAULT_GITHUB_HOST if api_endpoint == DEFAULT_API_ENDPOINT

  matched = %r{^https?://(.+)/api}.match(api_endpoint)
  return matched[1] if matched # steep:ignore

  DEFAULT_GITHUB_HOST
end

Instance Method Details

#add_pr_assignees(pr_number)

This method returns an undefined value.



286
287
288
289
290
# File 'lib/sashimi_tanpopo/provider/github.rb', line 286

def add_pr_assignees(pr_number)
  return if @pr_assignees.empty?

  @octokit.add_assignees(@repository, pr_number, @pr_assignees)
end

#add_pr_labels(pr_number)

This method returns an undefined value.



277
278
279
280
281
# File 'lib/sashimi_tanpopo/provider/github.rb', line 277

def add_pr_labels(pr_number)
  return if @pr_labels.empty?

  @octokit.add_labels_to_an_issue(@repository, pr_number, @pr_labels)
end

#add_pr_reviewers(pr_number)

This method returns an undefined value.



295
296
297
298
299
# File 'lib/sashimi_tanpopo/provider/github.rb', line 295

def add_pr_reviewers(pr_number)
  return if @pr_reviewers.empty?

  @octokit.request_pull_request_review(@repository, pr_number, reviewers: @pr_reviewers)
end

#create_branch_and_push_changes(changed_files)

This method returns an undefined value.

Create branch on repository and push changes



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/sashimi_tanpopo/provider/github.rb', line 215

def create_branch_and_push_changes(changed_files)
  current_ref = @octokit.ref(@repository, "heads/#{pr_target_branch}")
  branch_ref = @octokit.create_ref(@repository, "heads/#{@pr_source_branch}", current_ref.object.sha) # steep:ignore

  branch_commit = @octokit.commit(@repository, branch_ref.object.sha) # steep:ignore

  tree_metas =
    changed_files.map do |path, data|
      create_tree_meta(path: path, body: data[:after_content], mode: data[:mode])
    end
  tree = @octokit.create_tree(@repository, tree_metas, base_tree: branch_commit.commit.tree.sha) # steep:ignore

  commit = @octokit.create_commit(
    @repository,
    @commit_message,
    tree.sha, # steep:ignore
    branch_ref.object.sha, # steep:ignore
    author: {
      name: git_username,
      email: git_email,
    }
  )

  @octokit.update_ref(@repository, "heads/#{@pr_source_branch}", commit.sha) # steep:ignore
end

#create_pull_requestHash{pr_number: Integer, html_url: String}

Returns Created Pull Request info.

Returns:

  • (Hash{pr_number: Integer, html_url: String})

    Created Pull Request info

See Also:



262
263
264
265
266
267
268
269
270
271
272
# File 'lib/sashimi_tanpopo/provider/github.rb', line 262

def create_pull_request
  pr = @octokit.create_pull_request(@repository, pr_target_branch, @pr_source_branch, @pr_title, @pr_body, draft: @is_draft_pr)

  SashimiTanpopo.logger.info "Pull Request is created: #{pr[:html_url]}"

  {
    number: pr[:number],
    html_url: pr[:html_url],
    node_id: pr[:node_id],
  }
end

#create_tree_meta(path:, body:, mode:) ⇒ Hash<{ path: String, mode: String, type: String, sha: String }>

Parameters:

  • path (String)
  • body (String)
  • mode (String)
  • path: (String)
  • body: (String)
  • mode: (String)

Returns:

  • (Hash<{ path: String, mode: String, type: String, sha: String }>)

See Also:



248
249
250
251
252
253
254
255
256
257
# File 'lib/sashimi_tanpopo/provider/github.rb', line 248

def create_tree_meta(path:, body:, mode:)
  file_body_sha = @octokit.create_blob(@repository, body)

  {
    path: path,
    mode: mode,
    type: "blob",
    sha:  file_body_sha,
  }
end

#current_user_nameString



168
169
170
# File 'lib/sashimi_tanpopo/provider/github.rb', line 168

def current_user_name
  @octokit.user[:login]
end

#exists_branch?(branch) ⇒ Boolean

Whether exists branch on repository

Parameters:

  • branch (String)

Returns:

  • (Boolean)


198
199
200
201
202
203
# File 'lib/sashimi_tanpopo/provider/github.rb', line 198

def exists_branch?(branch)
  @octokit.branch(@repository, branch)
  true
rescue Octokit::NotFound
  false
end

#get_default_branchString



175
176
177
178
# File 'lib/sashimi_tanpopo/provider/github.rb', line 175

def get_default_branch
  res = @octokit.repository(@repository)
  res[:default_branch]
end

#git_emailString

Returns:

  • (String)


161
162
163
# File 'lib/sashimi_tanpopo/provider/github.rb', line 161

def git_email
  @git_email ||= "#{git_username}@users.noreply.#{self.class.github_host(@api_endpoint)}"
end

#git_usernameString

Returns:

  • (String)


156
157
158
# File 'lib/sashimi_tanpopo/provider/github.rb', line 156

def git_username
  @git_username ||= current_user_name
end

#performString?

Apply recipe files

Returns:

  • (String)

    Created Pull Request URL

  • (nil)

    Pull Request isn't created



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/sashimi_tanpopo/provider/github.rb', line 79

def perform
  changed_files = apply_recipe_files

  write_summary_file(changed_files)

  return nil if changed_files.empty? || @dry_run

  if exists_branch?(@pr_source_branch)
    SashimiTanpopo.logger.info "Skipped because branch #{@pr_source_branch} already exists on #{@repository}"
    return nil
  end

  create_branch_and_push_changes(changed_files)

  pr = create_pull_request

  add_pr_labels(pr[:number])
  add_pr_assignees(pr[:number])
  add_pr_reviewers(pr[:number])

  set_auto_merge(pr[:node_id]) if @pr_auto_merge

  pr[:html_url]
end

#pr_target_branchString

Returns:

  • (String)


151
152
153
# File 'lib/sashimi_tanpopo/provider/github.rb', line 151

def pr_target_branch
  @pr_target_branch ||= get_default_branch
end

#set_auto_merge(pr_node_id)

This method returns an undefined value.



304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/sashimi_tanpopo/provider/github.rb', line 304

def set_auto_merge(pr_node_id)
  @graphql.query(<<~GRAPHQL, pullRequestId: pr_node_id)
    mutation($pullRequestId: ID!) {
      enablePullRequestAutoMerge(input: {
        pullRequestId: $pullRequestId
      }) {
        pullRequest {
          id
          autoMergeRequest {
            enabledAt
          }
        }
      }
    }
  GRAPHQL
end

#write_summary_file(changed_files)

This method returns an undefined value.

Parameters:

  • changed_files (Hash<String, { before_content: String, after_content: String, mode: String }>)

    key: f path, value: Hash



181
182
183
184
185
186
187
188
189
190
191
# File 'lib/sashimi_tanpopo/provider/github.rb', line 181

def write_summary_file(changed_files)
  return if @summary_path.empty?

  return if changed_files.empty? && @only_changes_summary

  summary = self.class.generate_summary(changed_files: changed_files, dry_run: @dry_run)

  File.open(@summary_path, "a") do |f|
    f.write(summary)
  end
end