|
1 | 1 | class GenerateSponsorsYamlFileJob < ApplicationJob |
2 | | - delegate :octokit, :base_branch, to: :github_installation |
3 | | - |
4 | 2 | def perform(conference, push: true) |
5 | 3 | @conference = conference |
6 | 4 |
|
@@ -126,97 +124,121 @@ def json_data |
126 | 124 | @json_data = data ? "#{data.to_json}\n" : nil |
127 | 125 | end |
128 | 126 |
|
129 | | - MAX_PUSH_RETRIES = 3 |
130 | | - |
131 | 127 | def push_to_github |
132 | 128 | return unless repo |
133 | | - return if yaml_data.nil? # to generate |
| 129 | + return if yaml_data.nil? |
| 130 | + |
134 | 131 | push_id = @last_id || "event-#{@last_event_editing_history_id}" |
135 | | - @branch_name = "sponsor-app/#{@conference.slug}" |
136 | | - @filepath = repo.path |
137 | | - pr_title = "Update sponsors.yml for #{@conference.slug} (#{push_id})" |
138 | | - |
139 | | - # Check if existing branch already has newer data |
140 | | - if branch_has_newer_data? |
141 | | - Rails.logger.info "GenerateSponsorsYamlFileJob: branch has newer data, skipping" |
142 | | - return |
143 | | - end |
| 132 | + GitHubPusher.new( |
| 133 | + conference: @conference, |
| 134 | + filepath: repo.path, |
| 135 | + content: yaml_data, |
| 136 | + last_editing_history_id: @last_id, |
| 137 | + push_id:, |
| 138 | + ).push |
| 139 | + end |
144 | 140 |
|
145 | | - # Delete and recreate branch from base HEAD |
146 | | - begin |
147 | | - octokit.delete_branch(repo.name, @branch_name) |
148 | | - rescue Octokit::UnprocessableEntity |
149 | | - end |
| 141 | + # For debugging |
| 142 | + def self.get_octokit(repo) |
| 143 | + GithubInstallation.new(repo).octokit |
| 144 | + end |
150 | 145 |
|
151 | | - head = octokit.branch(repo.name, base_branch) |
152 | | - octokit.create_ref(repo.name, "refs/heads/#{@branch_name}", head[:commit][:sha]) |
| 146 | + class GitHubPusher |
| 147 | + MAX_RETRIES = 3 |
153 | 148 |
|
154 | | - # Get blob_sha from base branch (acts as optimistic lock) |
155 | | - begin |
156 | | - blob_sha = octokit.contents(repo.name, path: @filepath)[:sha] |
157 | | - rescue Octokit::NotFound |
158 | | - blob_sha = nil |
| 149 | + delegate :octokit, :base_branch, to: :github_installation |
| 150 | + |
| 151 | + def initialize(conference:, filepath:, content:, last_editing_history_id:, push_id:) |
| 152 | + @conference = conference |
| 153 | + @repo = conference.github_repo |
| 154 | + @filepath = filepath |
| 155 | + @content = content |
| 156 | + @last_id = last_editing_history_id |
| 157 | + @branch_name = "sponsor-app/#{conference.slug}" |
| 158 | + @pr_title = "Update sponsors.yml for #{conference.slug} (#{push_id})" |
159 | 159 | end |
160 | 160 |
|
161 | | - push_retries = 0 |
162 | | - begin |
163 | | - octokit.update_contents(repo.name, @filepath, pr_title, blob_sha, yaml_data, branch: @branch_name) |
164 | | - rescue Octokit::Conflict, Octokit::UnprocessableEntity => e |
165 | | - # Conflict: another job committed first. Check if our data is newer. |
166 | | - if push_retries < MAX_PUSH_RETRIES && !branch_has_newer_data? |
167 | | - push_retries += 1 |
168 | | - # Re-read blob_sha from the branch (now updated by the other job) |
169 | | - begin |
170 | | - blob_sha = octokit.contents(repo.name, path: @filepath, ref: @branch_name)[:sha] |
171 | | - rescue Octokit::NotFound |
172 | | - blob_sha = nil |
173 | | - end |
174 | | - Rails.logger.info "GenerateSponsorsYamlFileJob: commit conflict, retrying (#{push_retries}/#{MAX_PUSH_RETRIES})" |
175 | | - retry |
176 | | - else |
177 | | - Rails.logger.info "GenerateSponsorsYamlFileJob: commit conflict and branch has newer data (or max retries), skipping" |
| 161 | + def push |
| 162 | + return unless @repo |
| 163 | + |
| 164 | + if branch_has_newer_data? |
| 165 | + Rails.logger.info "GenerateSponsorsYamlFileJob: branch has newer data, skipping" |
178 | 166 | return |
179 | 167 | end |
180 | | - end |
181 | 168 |
|
182 | | - create_or_update_pull_request(pr_title) |
183 | | - end |
| 169 | + reset_branch |
| 170 | + commit_content |
| 171 | + create_or_update_pull_request |
| 172 | + end |
184 | 173 |
|
185 | | - # For debugging |
186 | | - def self.get_octokit(repo) |
187 | | - GithubInstallation.new(repo).octokit |
188 | | - end |
| 174 | + private |
189 | 175 |
|
190 | | - private |
| 176 | + def reset_branch |
| 177 | + begin |
| 178 | + octokit.delete_branch(@repo.name, @branch_name) |
| 179 | + rescue Octokit::UnprocessableEntity |
| 180 | + end |
191 | 181 |
|
192 | | - def branch_has_newer_data? |
193 | | - existing = octokit.contents(repo.name, path: @filepath, ref: @branch_name) |
194 | | - existing_content = Base64.decode64(existing[:content]) |
195 | | - if existing_content =~ /last_editing_history: (\d+)/ |
196 | | - return @last_id && $1.to_i > @last_id |
| 182 | + head = octokit.branch(@repo.name, base_branch) |
| 183 | + octokit.create_ref(@repo.name, "refs/heads/#{@branch_name}", head[:commit][:sha]) |
197 | 184 | end |
198 | | - false |
199 | | - rescue Octokit::NotFound |
200 | | - false # Branch or file doesn't exist |
201 | | - end |
202 | 185 |
|
203 | | - def create_or_update_pull_request(pr_title) |
204 | | - owner = repo.name.split('/')[0] |
205 | | - existing_prs = octokit.pull_requests(repo.name, state: 'open', head: "#{owner}:#{@branch_name}") |
206 | | - if existing_prs.any? |
207 | | - octokit.update_pull_request(repo.name, existing_prs[0][:number], title: pr_title) |
208 | | - else |
| 186 | + def commit_content |
209 | 187 | begin |
210 | | - octokit.create_pull_request(repo.name, base_branch, @branch_name, pr_title, nil) |
211 | | - rescue Octokit::UnprocessableEntity |
212 | | - # Concurrent job already created PR |
213 | | - existing_prs = octokit.pull_requests(repo.name, state: 'open', head: "#{owner}:#{@branch_name}") |
214 | | - octokit.update_pull_request(repo.name, existing_prs[0][:number], title: pr_title) if existing_prs.any? |
| 188 | + blob_sha = octokit.contents(@repo.name, path: @filepath)[:sha] |
| 189 | + rescue Octokit::NotFound |
| 190 | + blob_sha = nil |
| 191 | + end |
| 192 | + |
| 193 | + retries = 0 |
| 194 | + begin |
| 195 | + octokit.update_contents(@repo.name, @filepath, @pr_title, blob_sha, @content, branch: @branch_name) |
| 196 | + rescue Octokit::Conflict, Octokit::UnprocessableEntity => e |
| 197 | + if retries < MAX_RETRIES && !branch_has_newer_data? |
| 198 | + retries += 1 |
| 199 | + begin |
| 200 | + blob_sha = octokit.contents(@repo.name, path: @filepath, ref: @branch_name)[:sha] |
| 201 | + rescue Octokit::NotFound |
| 202 | + blob_sha = nil |
| 203 | + end |
| 204 | + Rails.logger.info "GenerateSponsorsYamlFileJob: commit conflict, retrying (#{retries}/#{MAX_RETRIES})" |
| 205 | + retry |
| 206 | + else |
| 207 | + Rails.logger.info "GenerateSponsorsYamlFileJob: commit conflict and branch has newer data (or max retries), skipping" |
| 208 | + return |
| 209 | + end |
215 | 210 | end |
216 | 211 | end |
217 | | - end |
218 | 212 |
|
219 | | - def github_installation |
220 | | - @github_installation ||= GithubInstallation.new(repo.name, branch: repo.branch) |
| 213 | + def branch_has_newer_data? |
| 214 | + existing = octokit.contents(@repo.name, path: @filepath, ref: @branch_name) |
| 215 | + existing_content = Base64.decode64(existing[:content]) |
| 216 | + if existing_content =~ /last_editing_history: (\d+)/ |
| 217 | + return @last_id && $1.to_i > @last_id |
| 218 | + end |
| 219 | + false |
| 220 | + rescue Octokit::NotFound |
| 221 | + false # Branch or file doesn't exist |
| 222 | + end |
| 223 | + |
| 224 | + def create_or_update_pull_request |
| 225 | + owner = @repo.name.split('/')[0] |
| 226 | + existing_prs = octokit.pull_requests(@repo.name, state: 'open', head: "#{owner}:#{@branch_name}") |
| 227 | + if existing_prs.any? |
| 228 | + octokit.update_pull_request(@repo.name, existing_prs[0][:number], title: @pr_title) |
| 229 | + else |
| 230 | + begin |
| 231 | + octokit.create_pull_request(@repo.name, base_branch, @branch_name, @pr_title, nil) |
| 232 | + rescue Octokit::UnprocessableEntity |
| 233 | + # Concurrent job already created PR |
| 234 | + existing_prs = octokit.pull_requests(@repo.name, state: 'open', head: "#{owner}:#{@branch_name}") |
| 235 | + octokit.update_pull_request(@repo.name, existing_prs[0][:number], title: @pr_title) if existing_prs.any? |
| 236 | + end |
| 237 | + end |
| 238 | + end |
| 239 | + |
| 240 | + def github_installation |
| 241 | + @github_installation ||= GithubInstallation.new(@repo.name, branch: @repo.branch) |
| 242 | + end |
221 | 243 | end |
222 | 244 | end |
0 commit comments