Skip to content
Permalink
Browse files

Add GitHub Actions. (#59190)

  • Loading branch information...
reitermarkus committed Feb 20, 2019
1 parent 937a00c commit bdf68d8090cad8c1bb36d6f09740b96661016c16
@@ -0,0 +1,12 @@
FROM linuxbrew/alpine

ENV HOMEBREW_DEVELOPER=1
ENV HOMEBREW_NO_AUTO_UPDATE=1

RUN brew update-reset
RUN brew ruby -e 'Homebrew.install_gem! "git_diff"'
ADD automerge.rb /automerge.rb
ADD git_diff_extensions.rb /git_diff_extensions.rb
ADD entrypoint.sh /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]
@@ -0,0 +1,128 @@
require "json"

Homebrew.install_gem! "git_diff"
require "git_diff"
require_relative "git_diff_extensions"
using GitDiffExtension

require "utils/github"

ENV["GITHUB_ACTION"] = ENV.delete("HOMEBREW_GITHUB_ACTION")
ENV["GITHUB_ACTOR"] = ENV.delete("HOMEBREW_GITHUB_ACTOR")
ENV["GITHUB_EVENT_NAME"] = ENV.delete("HOMEBREW_GITHUB_EVENT_NAME")
ENV["GITHUB_EVENT_PATH"] = ENV.delete("HOMEBREW_GITHUB_EVENT_PATH")
ENV["GITHUB_REPOSITORY"] = ENV.delete("HOMEBREW_GITHUB_REPOSITORY")
ENV["GITHUB_SHA"] = ENV.delete("HOMEBREW_GITHUB_SHA")
ENV["GITHUB_TOKEN"] = ENV.delete("HOMEBREW_GITHUB_TOKEN")
ENV["GITHUB_WORKFLOW"] = ENV.delete("HOMEBREW_GITHUB_WORKFLOW")
ENV["GITHUB_WORKSPACE"] = ENV.delete("HOMEBREW_GITHUB_WORKSPACE")

class Skip < StandardError; end

def skip(message)
raise Skip, message
end

event = JSON.parse(File.read(ENV.fetch("GITHUB_EVENT_PATH")))

def find_pull_request_for_status(event)
repo = event.fetch("repository").fetch("full_name")

branch = event.fetch("branches").find { |branch| branch.fetch("commit").fetch("sha") == event.fetch("commit").fetch("sha") }

/https:\/\/api.github.com\/repos\/(?<pr_author>[^\/]+)\// =~ branch.fetch("commit").fetch("url")

GitHub.pull_requests(
repo,
base: "#{event.fetch("repository").fetch("default_branch")}",
head: "#{pr_author}:#{branch.fetch("name")}",
state: "open",
sort: "updated",
direction: "desc",
).find { |pr| pr.fetch("head").fetch("sha") == event.fetch("commit").fetch("sha") }
end

def diff_for_pull_request(pr)
diff_url = pr.fetch("diff_url")

output, _, status = curl_output("--location", diff_url)

GitDiff.from_string(output) if status.success?
end

def merge_pull_request(pr, statuses = GitHub.open_api(pr.fetch("statuses_url")))
skip "CI status is not successful." unless passed_ci?(statuses)

diff = diff_for_pull_request(pr)
skip "Not a “simple” version bump pull request." unless diff.simple?

puts "Merging pull request #{pr.fetch("number")}"

repo = pr.fetch("base").fetch("repo").fetch("full_name")
number = pr.fetch("number")
sha = pr.fetch("head").fetch("sha")

begin
tries ||= 0

GitHub.merge_pull_request(
repo,
number: number, sha: sha,
merge_method: :squash,
)
puts "Pull request #{pr.fetch("number")} merged successfully."
rescue => e
$stderr.puts "Failed to merge pull request #{pr.fetch("number")}."
$stderr.puts e
raise if (tries += 1) > 3
sleep 5
retry
end
end

def passed_ci?(statuses = [])
statuses = Hash[
statuses.group_by { |status| status.fetch("context") }
.map { |(k, v)| [k, v.max_by { |status| Time.parse(status.fetch("updated_at")) }] }
]

statuses.dig("continuous-integration/travis-ci/pr", "state") == "success"
end

begin
case ENV["GITHUB_EVENT_NAME"]
when "status"
status = event

pr = find_pull_request_for_status(status)
merge_pull_request(pr, [status])
when "pull_request", "pull_request_review", "pull_request_review_comment"
pr = event.fetch("pull_request")
merge_pull_request(pr)
when "issue_comment"
issue = event.fetch("issue")

skip "Not a pull request." unless pr_url = issue.dig("pull_request", "url")

pr = GitHub.open_api(pr_url)
merge_pull_request(pr)
when "push"
prs = GitHub.pull_requests(ENV["GITHUB_REPOSITORY"], state: :open, base: "master")

merged_prs = prs.select do |pr|
begin
merge_pull_request(pr)
true
rescue
false
end
end

skip "No “simple” version bump pull requests found." if merged_prs.empty?
else
skip "Unsupported GitHub Actions event."
end
rescue Skip => reason
$stderr.puts reason
exit 78
end
@@ -0,0 +1,17 @@
#!/bin/sh

set -e

export HOMEBREW_GITHUB_ACTION="$GITHUB_ACTION"
export HOMEBREW_GITHUB_ACTOR="$GITHUB_ACTOR"
export HOMEBREW_GITHUB_EVENT_NAME="$GITHUB_EVENT_NAME"
export HOMEBREW_GITHUB_EVENT_PATH="$GITHUB_EVENT_PATH"
export HOMEBREW_GITHUB_REPOSITORY="$GITHUB_REPOSITORY"
export HOMEBREW_GITHUB_SHA="$GITHUB_SHA"
export HOMEBREW_GITHUB_TOKEN="$GITHUB_TOKEN"
export HOMEBREW_GITHUB_WORKFLOW="$GITHUB_WORKFLOW"
export HOMEBREW_GITHUB_WORKSPACE="$GITHUB_WORKSPACE"

export HOMEBREW_GITHUB_API_TOKEN="$GITHUB_TOKEN"

brew ruby /automerge.rb
@@ -0,0 +1,39 @@
require "git_diff"

module GitDiffExtension
refine GitDiff::Diff do
def simple?
single_cask? && only_version_or_checksum?
end

def single_cask?
return false unless files.count == 1
file = files.first
return false unless file.a_path == file.b_path
file.a_path.match?(%r{\ACasks/[^/]+\.rb\Z})
end

def only_version_or_checksum?
lines = files.flat_map(&:hunks).flat_map(&:lines)

additions = lines.select(&:addition?)
deletions = lines.select(&:deletion?)
changed_lines = deletions + additions

return false if additions.count != deletions.count
return false if additions.count > 2

changed_lines.all? { |line| line.version? || line.sha256? }
end
end

refine GitDiff::Line::Context do
def version?
to_s.match?(/\A[+-]\s*version '[^']+'\Z/)
end

def sha256?
to_s.match?(/\A[+-]\s*sha256 '[0-9a-f]{64}'\Z/)
end
end
end
@@ -0,0 +1,34 @@
workflow "Trigger `automerge` on status update." {
on = "status"
resolves = ["automerge"]
}

workflow "Trigger `automerge` on pull request update." {
on = "pull_request"
resolves = ["automerge"]
}

workflow "Trigger `automerge` on pull request review." {
on = "pull_request_review"
resolves = ["automerge"]
}

workflow "Trigger `automerge` on pull request review comment." {
on = "pull_request_review_comment"
resolves = ["automerge"]
}

workflow "Trigger `automerge` on issue comment." {
on = "issue_comment"
resolves = ["automerge"]
}

workflow "Trigger `automerge` on push." {
on = "push"
resolves = ["automerge"]
}

action "automerge" {
uses = "./.github/actions/automerge"
secrets = ["GITHUB_TOKEN"]
}
@@ -22,7 +22,7 @@ def run
raise CaskError, "This command must be run from inside a tap directory."
end

ruby_files_in_wrong_directory = modified_ruby_files - (modified_cask_files + modified_command_files)
ruby_files_in_wrong_directory = modified_ruby_files - (modified_cask_files + modified_command_files + modified_github_files)

unless ruby_files_in_wrong_directory.empty?
raise CaskError, "Casks are in the wrong directory:\n" +
@@ -162,6 +162,10 @@ def modified_command_files
@modified_command_files ||= modified_files.select { |path| tap.command_file?(path) || path.ascend.to_a.last.to_s == "cmd" }
end

def modified_github_files
@modified_github_files ||= modified_files.select { |path| path.to_s.start_with?(".github/") }
end

def modified_cask_files
@modified_cask_files ||= modified_files.select { |path| tap.cask_file?(path) }
end

0 comments on commit bdf68d8

Please sign in to comment.
You can’t perform that action at this time.