Skip to content

Commit 14337b8

Browse files
committed
Run new Ruby version check workflow more frequently
Run it hourly instead of daily to ensure we catch new releases faster.
1 parent dc5f779 commit 14337b8

File tree

5 files changed

+168
-4
lines changed

5 files changed

+168
-4
lines changed

.github/workflows/check-ruby-versions.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: Check for New Ruby Versions
22

33
on:
44
schedule:
5-
- cron: '0 2 * * *' # Daily at 2 AM UTC
5+
- cron: '17 * * * *' # Hourly at minute 17
66
workflow_dispatch: # Allow manual triggers
77

88
permissions:

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
source "https://rubygems.org"
44

5+
gem "base64"
56
gem "minitest"
67
gem "mocha"
78
gem "octokit"

Gemfile.lock

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ GEM
33
specs:
44
addressable (2.8.8)
55
public_suffix (>= 2.0.2, < 8.0)
6+
base64 (0.3.0)
67
bigdecimal (4.0.1)
78
crack (1.0.1)
89
bigdecimal
@@ -44,6 +45,7 @@ PLATFORMS
4445
ruby
4546

4647
DEPENDENCIES
48+
base64
4749
minitest
4850
mocha
4951
octokit
@@ -52,6 +54,7 @@ DEPENDENCIES
5254

5355
CHECKSUMS
5456
addressable (2.8.8) sha256=7c13b8f9536cf6364c03b9d417c19986019e28f7c00ac8132da4eb0fe393b057
57+
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
5558
bigdecimal (4.0.1) sha256=8b07d3d065a9f921c80ceaea7c9d4ae596697295b584c296fe599dd0ad01c4a7
5659
crack (1.0.1) sha256=ff4a10390cd31d66440b7524eb1841874db86201d5b70032028553130b6d4c7e
5760
faraday (2.14.0) sha256=8699cfe5d97e55268f2596f9a9d5a43736808a943714e3d9a53e6110593941cd

lib/ruby_version_pr_creator.rb

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
require "octokit"
44
require "open3"
55
require "time"
6+
require "base64"
7+
require "json"
68
require_relative "console"
79

810
# Ruby Version PR Creator
@@ -48,7 +50,15 @@ def initialize(new_versions, files_modified:, working_dir:, github_token: nil, o
4850
def call
4951
validate_prerequisites!
5052

51-
close_existing_automation_prs
53+
existing_pr = close_existing_automation_prs
54+
55+
if existing_pr
56+
log("", :blue)
57+
log("Reusing existing automation PR ##{existing_pr.number}", :blue, emoji: :info)
58+
log("URL: #{existing_pr.html_url}", emoji: :info)
59+
60+
return { success: true, pr_number: existing_pr.number, pr_url: existing_pr.html_url }
61+
end
5262

5363
branch_name = create_branch
5464
commit_changes
@@ -148,7 +158,15 @@ def close_existing_automation_prs
148158
labels.include?("automation") && labels.include?("ruby-versions")
149159
end
150160

151-
automation_prs.each do |pr|
161+
matching_pr = automation_prs.find { |pr| automation_pr_matches_versions?(pr) }
162+
163+
if matching_pr
164+
log("Found matching automation PR ##{matching_pr.number}; keeping it open.", :blue, emoji: :info)
165+
end
166+
167+
prs_to_close = matching_pr ? (automation_prs - [matching_pr]) : automation_prs
168+
169+
prs_to_close.each do |pr|
152170
log("Closing existing PR ##{pr.number}...", :yellow, emoji: :close)
153171
github_client.add_comment(
154172
repository,
@@ -157,9 +175,38 @@ def close_existing_automation_prs
157175
)
158176
github_client.close_pull_request(repository, pr.number)
159177
end
178+
179+
matching_pr
160180
rescue Octokit::Error => e
161181
# Non-fatal, just log and continue
162182
log("Could not check for existing PRs: #{e.message}", :yellow, emoji: :info)
183+
nil
184+
end
185+
end
186+
187+
def automation_pr_matches_versions?(pr)
188+
versions = ruby_versions_from_pr(pr)
189+
new_versions.all? { |version| versions.include?(version) }
190+
rescue Octokit::Error => e
191+
log("Could not inspect PR ##{pr.number} versions: #{e.message}", :yellow, emoji: :info)
192+
false
193+
rescue ArgumentError, JSON::ParserError => e
194+
log("Invalid versions file in PR ##{pr.number}: #{e.message}", :yellow, emoji: :info)
195+
false
196+
end
197+
198+
def ruby_versions_from_pr(pr)
199+
file = github_client.contents(repository, path: ".github/ruby-versions.json", ref: pr.head.ref)
200+
encoded_content = file.content.to_s.strip
201+
202+
return [] if encoded_content.empty?
203+
204+
parsed = JSON.parse(Base64.decode64(encoded_content))
205+
206+
if parsed.is_a?(Array)
207+
return parsed
208+
else
209+
raise ArgumentError, "Expected .github/ruby-versions.json to contain an array"
163210
end
164211
end
165212

test/ruby_version_pr_creator_test.rb

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
require "json"
77
require "stringio"
88
require "webmock/minitest"
9+
require "base64"
910
require_relative "../lib/ruby_version_pr_creator"
1011

1112
# Test suite for the RubyVersionPRCreator module
@@ -79,6 +80,34 @@ def test_closes_existing_automation_prs
7980
assert_match(/closing existing pr/i, @output.string)
8081
end
8182

83+
def test_reuses_existing_matching_automation_pr
84+
stub_github_api_with_matching_existing_pr
85+
creator = create_creator(["3.4.0"], [".github/ruby-versions.json"])
86+
stub_git_operations(creator)
87+
88+
result = creator.call
89+
90+
assert result[:success]
91+
assert_equal 100, result[:pr_number]
92+
assert_equal "https://github.com/rails/devcontainer/pull/100", result[:pr_url]
93+
assert_match(/reusing existing automation pr/i, @output.string)
94+
assert_not_requested(:post, "https://api.github.com/repos/rails/devcontainer/pulls")
95+
assert_not_requested(:patch, "https://api.github.com/repos/rails/devcontainer/pulls/100")
96+
end
97+
98+
def test_handles_empty_versions_file_content_as_non_matching
99+
stub_github_api_with_empty_versions_file_pr
100+
creator = create_creator(["3.4.0"], [".github/ruby-versions.json"])
101+
stub_git_operations(creator)
102+
103+
result = creator.call
104+
105+
assert result[:success]
106+
assert_equal 123, result[:pr_number]
107+
assert_requested(:patch, "https://api.github.com/repos/rails/devcontainer/pulls/100")
108+
assert_requested(:post, "https://api.github.com/repos/rails/devcontainer/pulls")
109+
end
110+
82111
def test_generates_correct_pr_title_single_version
83112
stub_github_api_success
84113
creator = create_creator(["3.4.0"], [".github/ruby-versions.json"])
@@ -190,7 +219,91 @@ def stub_github_api_with_existing_pr
190219
stub_request(:get, "https://api.github.com/repos/rails/devcontainer/pulls?state=open")
191220
.to_return(
192221
status: 200,
193-
body: JSON.generate([{ number: 100, labels: [{ name: "automation" }, { name: "ruby-versions" }] }]),
222+
body: JSON.generate([
223+
{
224+
number: 100,
225+
title: "Add Ruby version: 3.3.9",
226+
head: { ref: "automated/ruby-versions-old" },
227+
labels: [{ name: "automation" }, { name: "ruby-versions" }]
228+
}
229+
]),
230+
headers: json_headers
231+
)
232+
233+
stub_request(:get, "https://api.github.com/repos/rails/devcontainer/contents/.github/ruby-versions.json?ref=automated/ruby-versions-old")
234+
.to_return(
235+
status: 200,
236+
body: JSON.generate({ content: Base64.strict_encode64(JSON.generate(["3.3.9", "3.3.8"])) }),
237+
headers: json_headers
238+
)
239+
240+
stub_request(:post, "https://api.github.com/repos/rails/devcontainer/issues/100/comments")
241+
.to_return(status: 201, body: JSON.generate({ id: 1 }), headers: json_headers)
242+
243+
stub_request(:patch, "https://api.github.com/repos/rails/devcontainer/pulls/100")
244+
.to_return(status: 200, body: JSON.generate({ number: 100, state: "closed" }), headers: json_headers)
245+
246+
stub_request(:post, "https://api.github.com/repos/rails/devcontainer/pulls")
247+
.to_return(
248+
status: 201,
249+
body: JSON.generate({ number: 123, html_url: "https://github.com/rails/devcontainer/pull/123" }),
250+
headers: json_headers
251+
)
252+
253+
stub_request(:post, "https://api.github.com/repos/rails/devcontainer/issues/123/labels")
254+
.to_return(status: 200, body: JSON.generate([]), headers: json_headers)
255+
end
256+
257+
def stub_github_api_with_matching_existing_pr
258+
stub_request(:get, "https://api.github.com/repos/rails/devcontainer")
259+
.to_return(status: 200, body: JSON.generate({ full_name: "rails/devcontainer" }), headers: json_headers)
260+
261+
stub_request(:get, "https://api.github.com/repos/rails/devcontainer/pulls?state=open")
262+
.to_return(
263+
status: 200,
264+
body: JSON.generate([
265+
{
266+
number: 100,
267+
title: "Add Ruby version: 3.4.0",
268+
body: "Automated Ruby Version Update for 3.4.0",
269+
html_url: "https://github.com/rails/devcontainer/pull/100",
270+
head: { ref: "automated/ruby-versions-100" },
271+
labels: [{ name: "automation" }, { name: "ruby-versions" }]
272+
}
273+
]),
274+
headers: json_headers
275+
)
276+
277+
stub_request(:get, "https://api.github.com/repos/rails/devcontainer/contents/.github/ruby-versions.json?ref=automated/ruby-versions-100")
278+
.to_return(
279+
status: 200,
280+
body: JSON.generate({ content: Base64.strict_encode64(JSON.generate(["3.4.0", "3.3.10"])) }),
281+
headers: json_headers
282+
)
283+
end
284+
285+
def stub_github_api_with_empty_versions_file_pr
286+
stub_request(:get, "https://api.github.com/repos/rails/devcontainer")
287+
.to_return(status: 200, body: JSON.generate({ full_name: "rails/devcontainer" }), headers: json_headers)
288+
289+
stub_request(:get, "https://api.github.com/repos/rails/devcontainer/pulls?state=open")
290+
.to_return(
291+
status: 200,
292+
body: JSON.generate([
293+
{
294+
number: 100,
295+
title: "Add Ruby version: 3.4.0",
296+
head: { ref: "automated/ruby-versions-empty" },
297+
labels: [{ name: "automation" }, { name: "ruby-versions" }]
298+
}
299+
]),
300+
headers: json_headers
301+
)
302+
303+
stub_request(:get, "https://api.github.com/repos/rails/devcontainer/contents/.github/ruby-versions.json?ref=automated/ruby-versions-empty")
304+
.to_return(
305+
status: 200,
306+
body: JSON.generate({ content: "" }),
194307
headers: json_headers
195308
)
196309

0 commit comments

Comments
 (0)