Skip to content

Commit 32bbc27

Browse files
committed
Auto-merge ruby-builder-bot PRs
ref: https://bugs.ruby-lang.org/issues/21804 ref: #856 Introduces automerge-check.rb which checks changed files against a small allowlist. The script contains tests for itself which can be run by passing a `--test` flag on the commandline. Note that automerge-check.rb is run from the base branch (master) to avoid malicious PRs from changing the script. Also note that other checks are being done in test.yml (which this pipeline depends upon): - windows URLs are being checked in the validate-windows-versions job - dist/index.js is previously being checked in the check-dist-index job
1 parent 757ecf5 commit 32bbc27

File tree

2 files changed

+145
-0
lines changed

2 files changed

+145
-0
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Auto-merge bot PRs
2+
on:
3+
workflow_run:
4+
workflows: ["Test this action"]
5+
types: [completed]
6+
7+
jobs:
8+
check:
9+
runs-on: ubuntu-latest
10+
if: >
11+
github.event.workflow_run.conclusion == 'success' &&
12+
github.event.workflow_run.event == 'pull_request' &&
13+
github.event.workflow_run.actor.login == 'ruby-builder-bot' &&
14+
github.event.workflow_run.pull_requests[0].user.login == 'ruby-builder-bot' &&
15+
github.event.workflow_run.pull_requests[0].head.repo.full_name == 'ruby-builder-bot/setup-ruby'
16+
permissions:
17+
contents: read
18+
steps:
19+
- name: Checkout base branch
20+
uses: actions/checkout@v4
21+
with:
22+
fetch-depth: 0
23+
24+
- name: Set up Ruby
25+
uses: ruby/setup-ruby@8a836efbcebe5de0fe86b48a775b7a31b5c70c93
26+
with:
27+
ruby-version: ruby
28+
29+
- name: Fetch PR head commit
30+
run: |
31+
git fetch --no-tags origin \
32+
${{ github.event.workflow_run.pull_requests[0].head.sha }}
33+
34+
- name: Run automerge checks
35+
run: |
36+
./automerge-check.rb \
37+
${{ github.event.workflow_run.pull_requests[0].base.sha }} \
38+
${{ github.event.workflow_run.pull_requests[0].head.sha }}
39+
40+
merge:
41+
runs-on: ubuntu-latest
42+
needs: check
43+
permissions:
44+
contents: write
45+
pull-requests: write
46+
steps:
47+
- name: Merge PR
48+
env:
49+
GH_TOKEN: ${{ github.token }}
50+
run: |
51+
gh pr merge "${{ github.event.workflow_run.pull_requests[0].number }}" \
52+
--repo "${{ github.repository }}" \
53+
--squash \
54+
--delete-branch

automerge-check.rb

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#!/usr/bin/env ruby
2+
# frozen_string_literal: true
3+
4+
require "open3"
5+
6+
ALLOWED_FILES = [
7+
"README.md",
8+
"dist/index.js",
9+
"ruby-builder-versions.json",
10+
"windows-toolchain-versions.json",
11+
"windows-versions.json",
12+
].freeze
13+
14+
class AutomergeCheck
15+
attr_reader :errors
16+
17+
def initialize(base_ref, head_ref = "HEAD")
18+
@base_ref = base_ref
19+
@head_ref = head_ref
20+
@errors = []
21+
end
22+
23+
def run
24+
check_changed_files
25+
26+
if @errors.empty?
27+
puts "All checks passed."
28+
true
29+
else
30+
puts "Automerge check failed:"
31+
@errors.each { |e| puts " - #{e}" }
32+
false
33+
end
34+
end
35+
36+
def check_changed_files(changed_files = git_changed_files)
37+
disallowed = changed_files - ALLOWED_FILES
38+
39+
if disallowed.any?
40+
@errors << "Disallowed files changed: #{disallowed.join(', ')}"
41+
end
42+
end
43+
44+
private
45+
46+
def git_changed_files
47+
output, status = Open3.capture2("git", "diff", "--name-only", "#{@base_ref}...#{@head_ref}")
48+
unless status.success?
49+
raise "git diff failed: #{output}"
50+
end
51+
output.split("\n").map(&:strip).reject(&:empty?)
52+
end
53+
end
54+
55+
if __FILE__ == $0
56+
if ARGV[0] == "--test"
57+
ARGV.clear
58+
require "minitest/autorun"
59+
60+
class AutomergeCheckTest < Minitest::Test
61+
def setup
62+
@checker = AutomergeCheck.new("master")
63+
end
64+
65+
def test_allowed_files_not_flagged
66+
@checker.check_changed_files(["README.md", "dist/index.js"])
67+
assert_empty @checker.errors
68+
end
69+
70+
def test_disallowed_files_detected
71+
@checker.check_changed_files(["README.md", "evil.js", "dist/index.js"])
72+
assert_equal 1, @checker.errors.length
73+
assert_match(/evil\.js/, @checker.errors.first)
74+
end
75+
76+
def test_all_allowed_files_accepted
77+
@checker.check_changed_files(ALLOWED_FILES)
78+
assert_empty @checker.errors
79+
end
80+
end
81+
elsif ARGV.length < 1 || ARGV.length > 2
82+
puts "Usage: #{$0} <base-ref> [head-ref]"
83+
puts " #{$0} --test"
84+
exit 1
85+
else
86+
base_ref = ARGV[0]
87+
head_ref = ARGV[1] || "HEAD"
88+
checker = AutomergeCheck.new(base_ref, head_ref)
89+
exit(checker.run ? 0 : 1)
90+
end
91+
end

0 commit comments

Comments
 (0)