mirror of
https://github.com/unsplash/comment-on-pr.git
synced 2024-11-10 09:02:32 +08:00
commit
bc79160dfc
12 changed files with 64 additions and 414 deletions
4
Gemfile
4
Gemfile
|
@ -1,4 +0,0 @@
|
|||
source "https://rubygems.org"
|
||||
|
||||
gem "octokit"
|
||||
gem "rspec", group: :test
|
57
Gemfile.lock
57
Gemfile.lock
|
@ -1,57 +0,0 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
addressable (2.8.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
diff-lcs (1.5.0)
|
||||
faraday (1.8.0)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
faraday-excon (~> 1.1)
|
||||
faraday-httpclient (~> 1.0.1)
|
||||
faraday-net_http (~> 1.0)
|
||||
faraday-net_http_persistent (~> 1.1)
|
||||
faraday-patron (~> 1.0)
|
||||
faraday-rack (~> 1.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-em_http (1.0.0)
|
||||
faraday-em_synchrony (1.0.0)
|
||||
faraday-excon (1.1.0)
|
||||
faraday-httpclient (1.0.1)
|
||||
faraday-net_http (1.0.1)
|
||||
faraday-net_http_persistent (1.2.0)
|
||||
faraday-patron (1.0.0)
|
||||
faraday-rack (1.0.0)
|
||||
multipart-post (2.1.1)
|
||||
octokit (4.21.0)
|
||||
faraday (>= 0.9)
|
||||
sawyer (~> 0.8.0, >= 0.5.3)
|
||||
public_suffix (4.0.6)
|
||||
rspec (3.10.0)
|
||||
rspec-core (~> 3.10.0)
|
||||
rspec-expectations (~> 3.10.0)
|
||||
rspec-mocks (~> 3.10.0)
|
||||
rspec-core (3.10.1)
|
||||
rspec-support (~> 3.10.0)
|
||||
rspec-expectations (3.10.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.10.0)
|
||||
rspec-mocks (3.10.2)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.10.0)
|
||||
rspec-support (3.10.3)
|
||||
ruby2_keywords (0.0.5)
|
||||
sawyer (0.8.2)
|
||||
addressable (>= 2.3.5)
|
||||
faraday (> 0.8, < 2.0)
|
||||
|
||||
PLATFORMS
|
||||
arm64-darwin-21
|
||||
|
||||
DEPENDENCIES
|
||||
octokit
|
||||
rspec
|
||||
|
||||
BUNDLED WITH
|
||||
2.2.14
|
|
@ -19,7 +19,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: comment PR
|
||||
uses: unsplash/comment-on-pr@v1.3.1
|
||||
uses: unsplash/comment-on-pr@v1.3.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
|
|
@ -2,8 +2,67 @@
|
|||
|
||||
require "json"
|
||||
require "octokit"
|
||||
require_relative "lib/github"
|
||||
require_relative "lib/commenter"
|
||||
require_relative "lib/action"
|
||||
|
||||
exit(run)
|
||||
json = File.read(ENV.fetch("GITHUB_EVENT_PATH"))
|
||||
event = JSON.parse(json)
|
||||
|
||||
if !ENV["GITHUB_TOKEN"]
|
||||
puts "Missing GITHUB_TOKEN"
|
||||
exit(1)
|
||||
end
|
||||
|
||||
github = Octokit::Client.new(access_token: ENV["GITHUB_TOKEN"])
|
||||
|
||||
if ARGV[0].empty?
|
||||
puts "Missing message argument."
|
||||
exit(1)
|
||||
end
|
||||
|
||||
message = ARGV[0]
|
||||
check_duplicate_msg = ARGV[1]
|
||||
delete_prev_regex_msg = ARGV[2]
|
||||
duplicate_msg_pattern = ARGV[3]
|
||||
|
||||
repo = event["repository"]["full_name"]
|
||||
|
||||
if ENV.fetch("GITHUB_EVENT_NAME") == "pull_request"
|
||||
pr_number = event["number"]
|
||||
else
|
||||
pulls = github.pull_requests(repo, state: "open")
|
||||
|
||||
push_head = event["after"]
|
||||
pr = pulls.find { |pr| pr["head"]["sha"] == push_head }
|
||||
|
||||
if !pr
|
||||
puts "Couldn't find an open pull request for branch with head at #{push_head}."
|
||||
exit(1)
|
||||
end
|
||||
pr_number = pr["number"]
|
||||
end
|
||||
|
||||
if !duplicate_msg_pattern.empty? || !delete_prev_regex_msg.empty?
|
||||
comments = github.issue_comments(repo, pr_number)
|
||||
|
||||
if check_duplicate_msg == "true"
|
||||
duplicate = if !duplicate_msg_pattern.empty?
|
||||
comments.find { |c| c["body"].match(/#{duplicate_msg_pattern}/) }
|
||||
else
|
||||
comments.find { |c| c["body"] == message }
|
||||
end
|
||||
|
||||
if duplicate
|
||||
puts "The PR already contains this message"
|
||||
exit(0)
|
||||
end
|
||||
end
|
||||
|
||||
if !delete_prev_regex_msg.empty?
|
||||
comments.each do |comment|
|
||||
if comment["body"].match(/#{delete_prev_regex_msg}/)
|
||||
github.delete_comment(repo, comment["id"])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
github.add_comment(repo, pr_number, message)
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
def run
|
||||
if !ENV["GITHUB_TOKEN"]
|
||||
puts "Missing GITHUB_TOKEN"
|
||||
return 1
|
||||
end
|
||||
|
||||
if ARGV[0].nil? || ARGV[0].empty?
|
||||
puts "Missing message argument."
|
||||
return 1
|
||||
end
|
||||
|
||||
commenter = Commenter.new(github: GitHub.new(env: ENV),
|
||||
message: ARGV[0],
|
||||
check_for_duplicates: ARGV[1],
|
||||
duplicate_pattern: ARGV[2],
|
||||
delete_previous_pattern: ARGV[3])
|
||||
|
||||
if commenter.block_duplicates? && commenter.existing_duplicates?
|
||||
puts "The PR already contains this message"
|
||||
return 0
|
||||
end
|
||||
|
||||
commenter.delete_matching_comments!
|
||||
commenter.comment!
|
||||
return 0
|
||||
end
|
|
@ -1,35 +0,0 @@
|
|||
class Commenter
|
||||
def initialize(github:, message:, check_for_duplicates: true, duplicate_pattern: nil, delete_previous_pattern: nil)
|
||||
@github = github
|
||||
@message = message
|
||||
@block_duplicates = check_for_duplicates.to_s.downcase.strip == "true"
|
||||
@duplicate_pattern = !duplicate_pattern.to_s.empty? && Regexp.new(duplicate_pattern)
|
||||
@delete_previous_pattern = !delete_previous_pattern.to_s.empty? && Regexp.new(delete_previous_pattern)
|
||||
end
|
||||
|
||||
def block_duplicates?
|
||||
@block_duplicates
|
||||
end
|
||||
|
||||
def existing_duplicates?
|
||||
if @duplicate_pattern
|
||||
@github.comments.any? { |c| c["body"].match(/#{@duplicate_pattern}/) }
|
||||
else
|
||||
@github.comments.any? { |c| c["body"] == @message }
|
||||
end
|
||||
end
|
||||
|
||||
def delete_matching_comments!
|
||||
return if !@delete_previous_pattern
|
||||
|
||||
@github.comments.each do |comment|
|
||||
if comment["body"].match(/#{@delete_previous_pattern}/)
|
||||
@github.delete_comment(@github.repo, comment["id"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def comment!
|
||||
@github.comment!(@message)
|
||||
end
|
||||
end
|
|
@ -1,53 +0,0 @@
|
|||
class GitHub
|
||||
class MissingPR < StandardError; end
|
||||
|
||||
attr_reader :client
|
||||
|
||||
def initialize(env:)
|
||||
@env = env
|
||||
@client = Octokit::Client.new(access_token: env["GITHUB_TOKEN"])
|
||||
end
|
||||
|
||||
def event
|
||||
@_event ||= JSON.parse(File.read(@env.fetch("GITHUB_EVENT_PATH")))
|
||||
end
|
||||
|
||||
def repo
|
||||
@_repo ||= event["repository"]["full_name"]
|
||||
end
|
||||
|
||||
def pr_number
|
||||
@_pr ||= begin
|
||||
|
||||
if @env.fetch("GITHUB_EVENT_NAME") == "pull_request"
|
||||
pr_number = event["number"]
|
||||
else
|
||||
pulls = client.pull_requests(repo, state: "open")
|
||||
|
||||
push_head = event["after"]
|
||||
pr = pulls.find { |pr| pr["head"]["sha"] == push_head }
|
||||
|
||||
if !pr
|
||||
raise MissingPR, "Couldn't find an open pull request for branch with head at #{push_head}"
|
||||
end
|
||||
|
||||
pr["number"]
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
def comments
|
||||
@_comments ||= client.issue_comments(repo, pr_number)
|
||||
rescue MissingPR => e
|
||||
puts e.message
|
||||
exit(1)
|
||||
end
|
||||
|
||||
def comment!(msg)
|
||||
client.add_comment(repo, pr_number, message)
|
||||
rescue MissingPR => e
|
||||
puts e.message
|
||||
exit(1)
|
||||
end
|
||||
end
|
|
@ -1,76 +0,0 @@
|
|||
require "spec_helper"
|
||||
|
||||
RSpec.describe "action.rb" do
|
||||
|
||||
let(:base_env) do
|
||||
{
|
||||
"GITHUB_TOKEN" => "secret",
|
||||
"GITHUB_EVENT_PATH" => File.join(__dir__, "event.json")
|
||||
}
|
||||
end
|
||||
|
||||
before :each do
|
||||
stub_const("ARGV", ["hello"])
|
||||
end
|
||||
|
||||
describe "required arguments" do
|
||||
it "fails without GITHUB_TOKEN" do
|
||||
expect { run }.to output(/Missing GITHUB_TOKEN/).to_stdout
|
||||
expect(run).to eq 1
|
||||
end
|
||||
|
||||
it "fails without message" do
|
||||
stub_const("ENV", base_env)
|
||||
stub_const("ARGV", [])
|
||||
expect { run }.to output(/Missing message argument/).to_stdout
|
||||
expect(run).to eq 1
|
||||
end
|
||||
end
|
||||
|
||||
describe "blocking duplicate comments" do
|
||||
|
||||
before :each do
|
||||
stub_const("ENV", base_env)
|
||||
end
|
||||
|
||||
context "we are blocking duplicates" do
|
||||
before :each do
|
||||
allow_any_instance_of(Commenter).to receive(:block_duplicates?).and_return(true)
|
||||
end
|
||||
|
||||
it "exits when there is already a matching comment" do
|
||||
allow_any_instance_of(Commenter).to receive(:existing_duplicates?).and_return(true)
|
||||
expect { run }.to output(/The PR already contains this message/).to_stdout
|
||||
expect(run).to eq 0
|
||||
end
|
||||
|
||||
it "comments when no match" do
|
||||
allow_any_instance_of(Commenter).to receive(:existing_duplicates?).and_return(false)
|
||||
allow_any_instance_of(Commenter).to receive(:comment!)
|
||||
expect { run }.to_not output.to_stdout
|
||||
expect(run).to eq 0
|
||||
end
|
||||
end
|
||||
|
||||
context "not blocking duplicates" do
|
||||
before :each do
|
||||
allow_any_instance_of(Commenter).to receive(:block_duplicates?).and_return(false)
|
||||
end
|
||||
|
||||
it "comments even with match" do
|
||||
allow_any_instance_of(Commenter).to receive(:existing_duplicates?).and_return(true)
|
||||
allow_any_instance_of(Commenter).to receive(:comment!)
|
||||
expect { run }.to_not output.to_stdout
|
||||
expect(run).to eq 0
|
||||
end
|
||||
|
||||
it "comments when no match" do
|
||||
allow_any_instance_of(Commenter).to receive(:existing_duplicates?).and_return(false)
|
||||
allow_any_instance_of(Commenter).to receive(:comment!)
|
||||
expect { run }.to_not output.to_stdout
|
||||
expect(run).to eq 0
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,85 +0,0 @@
|
|||
require "spec_helper"
|
||||
|
||||
RSpec.describe Commenter do
|
||||
let(:github) do
|
||||
spy("GitHub", comments: [
|
||||
{ "body" => "Oh, hi! I didn't see you there." },
|
||||
{ "body" => "Ah, that's just because of my invisibility powers." }
|
||||
],
|
||||
delete_comment: true)
|
||||
end
|
||||
|
||||
describe "#block_duplicates?" do
|
||||
it "default" do
|
||||
commenter = Commenter.new(github: github, message: "")
|
||||
expect(commenter.block_duplicates?).to eq true
|
||||
end
|
||||
|
||||
it "given true" do
|
||||
commenter = Commenter.new(github: github, message: "", check_for_duplicates: true)
|
||||
expect(commenter.block_duplicates?).to eq true
|
||||
end
|
||||
|
||||
it "given 'true'" do
|
||||
commenter = Commenter.new(github: github, message: "", check_for_duplicates: "true")
|
||||
expect(commenter.block_duplicates?).to eq true
|
||||
end
|
||||
|
||||
it "given 'false'" do
|
||||
commenter = Commenter.new(github: github, message: "", check_for_duplicates: false)
|
||||
expect(commenter.block_duplicates?).to eq false
|
||||
end
|
||||
|
||||
it "given nil" do
|
||||
commenter = Commenter.new(github: github, message: "", check_for_duplicates: nil)
|
||||
expect(commenter.block_duplicates?).to eq false
|
||||
end
|
||||
end
|
||||
|
||||
describe "#existing_duplicates?" do
|
||||
context "default/no pattern" do
|
||||
it "finds exact comment" do
|
||||
commenter = Commenter.new(github: github, message: "Ah, that's just because of my invisibility powers.")
|
||||
expect(commenter.existing_duplicates?).to eq true
|
||||
end
|
||||
|
||||
it "gives false when no matches" do
|
||||
commenter = Commenter.new(github: github, message: "Today I will say a sentence never been said.")
|
||||
expect(commenter.existing_duplicates?).to eq false
|
||||
end
|
||||
end
|
||||
|
||||
context "given pattern" do
|
||||
it "finds matching comment" do
|
||||
commenter = Commenter.new(github: github, message: "Is anyone listening?", duplicate_pattern: "invisibility powers")
|
||||
expect(commenter.existing_duplicates?).to eq true
|
||||
end
|
||||
|
||||
it "gives false when no matches" do
|
||||
commenter = Commenter.new(github: github, message: "Is anyone listening?", duplicate_pattern: "common idioms")
|
||||
expect(commenter.existing_duplicates?).to eq false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#delete_matching_comments!" do
|
||||
it "does nothing by default" do
|
||||
commenter = Commenter.new(github: github, message: "")
|
||||
expect(commenter.delete_matching_comments!).to eq nil
|
||||
expect(github).to have_received(:delete_comment).exactly(0).times
|
||||
end
|
||||
|
||||
it "does nothing given empty string" do
|
||||
commenter = Commenter.new(github: github, message: "", delete_previous_pattern: "")
|
||||
expect(commenter.delete_matching_comments!).to eq nil
|
||||
expect(github).to have_received(:delete_comment).exactly(0).times
|
||||
end
|
||||
|
||||
it "deletes matching comments given pattern" do
|
||||
commenter = Commenter.new(github: github, message: "", delete_previous_pattern: "invisibility")
|
||||
expect(commenter.delete_matching_comments!).to_not eq nil
|
||||
expect(github).to have_received(:delete_comment).once
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"respository": {
|
||||
"full_name": "foo/bar"
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
require "spec_helper"
|
||||
require "pry"
|
||||
|
||||
RSpec.describe GitHub do
|
||||
describe "#pr_number" do
|
||||
|
||||
let(:client) do
|
||||
double("GitHub client", pull_requests:
|
||||
[
|
||||
{
|
||||
"number" => 1234,
|
||||
"head" => {
|
||||
"sha" => "abc123"
|
||||
}
|
||||
},
|
||||
{
|
||||
"number" => 5678,
|
||||
"head" => {
|
||||
"sha" => "def456"
|
||||
}
|
||||
}
|
||||
])
|
||||
end
|
||||
|
||||
it "gives correct number for pull_request events" do
|
||||
github = GitHub.new(env: { "GITHUB_TOKEN" => "secret", "GITHUB_EVENT_NAME" => "pull_request" })
|
||||
allow(github).to receive(:event).and_return({ "number" => 1234 })
|
||||
|
||||
expect(github.pr_number).to eq 1234
|
||||
end
|
||||
|
||||
context "other events" do
|
||||
|
||||
before :each do
|
||||
@github = GitHub.new(env: { "GITHUB_TOKEN" => "secret", "GITHUB_EVENT_NAME" => "push" })
|
||||
allow(@github).to receive(:client).and_return(client)
|
||||
end
|
||||
|
||||
it "finds the existing PR" do
|
||||
allow(@github).to receive(:event).and_return({
|
||||
"after" => "def456",
|
||||
"repository" => {
|
||||
"full_name" => "foo/bar"
|
||||
}
|
||||
})
|
||||
expect(@github.pr_number).to eq 5678
|
||||
end
|
||||
|
||||
it "raises if no PR found" do
|
||||
@github = GitHub.new(env: { "GITHUB_TOKEN" => "secret", "GITHUB_EVENT_NAME" => "push" })
|
||||
allow(@github).to receive(:event).and_return({
|
||||
"after" => "ghi789",
|
||||
"repository" => {
|
||||
"full_name" => "foo/bar"
|
||||
}
|
||||
})
|
||||
allow(@github).to receive(:client).and_return(client)
|
||||
expect { @github.pr_number }.to raise_error(GitHub::MissingPR)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,5 +0,0 @@
|
|||
require "json"
|
||||
require "octokit"
|
||||
require_relative "../lib/github"
|
||||
require_relative "../lib/commenter"
|
||||
require_relative "../lib/action"
|
Loading…
Reference in a new issue