110 lines
2.5 KiB
Plaintext
110 lines
2.5 KiB
Plaintext
|
#!/usr/bin/env ruby
|
||
|
|
||
|
# A sample pre-deploy hook
|
||
|
#
|
||
|
# Checks the Github status of the build, waiting for a pending build to complete for up to 720 seconds.
|
||
|
#
|
||
|
# Fails unless the combined status is "success"
|
||
|
#
|
||
|
# These environment variables are available:
|
||
|
# KAMAL_RECORDED_AT
|
||
|
# KAMAL_PERFORMER
|
||
|
# KAMAL_VERSION
|
||
|
# KAMAL_HOSTS
|
||
|
# KAMAL_COMMAND
|
||
|
# KAMAL_SUBCOMMAND
|
||
|
# KAMAL_ROLE (if set)
|
||
|
# KAMAL_DESTINATION (if set)
|
||
|
|
||
|
# Only check the build status for production deployments
|
||
|
if ENV["KAMAL_COMMAND"] == "rollback" || ENV["KAMAL_DESTINATION"] != "production"
|
||
|
exit 0
|
||
|
end
|
||
|
|
||
|
require "bundler/inline"
|
||
|
|
||
|
# true = install gems so this is fast on repeat invocations
|
||
|
gemfile(true, quiet: true) do
|
||
|
source "https://rubygems.org"
|
||
|
|
||
|
gem "octokit"
|
||
|
gem "faraday-retry"
|
||
|
end
|
||
|
|
||
|
MAX_ATTEMPTS = 72
|
||
|
ATTEMPTS_GAP = 10
|
||
|
|
||
|
def exit_with_error(message)
|
||
|
$stderr.puts message
|
||
|
exit 1
|
||
|
end
|
||
|
|
||
|
class GithubStatusChecks
|
||
|
attr_reader :remote_url, :git_sha, :github_client, :combined_status
|
||
|
|
||
|
def initialize
|
||
|
@remote_url = `git config --get remote.origin.url`.strip.delete_prefix("https://github.com/")
|
||
|
@git_sha = `git rev-parse HEAD`.strip
|
||
|
@github_client = Octokit::Client.new(access_token: ENV["GITHUB_TOKEN"])
|
||
|
refresh!
|
||
|
end
|
||
|
|
||
|
def refresh!
|
||
|
@combined_status = github_client.combined_status(remote_url, git_sha)
|
||
|
end
|
||
|
|
||
|
def state
|
||
|
combined_status[:state]
|
||
|
end
|
||
|
|
||
|
def first_status_url
|
||
|
first_status = combined_status[:statuses].find { |status| status[:state] == state }
|
||
|
first_status && first_status[:target_url]
|
||
|
end
|
||
|
|
||
|
def complete_count
|
||
|
combined_status[:statuses].count { |status| status[:state] != "pending"}
|
||
|
end
|
||
|
|
||
|
def total_count
|
||
|
combined_status[:statuses].count
|
||
|
end
|
||
|
|
||
|
def current_status
|
||
|
if total_count > 0
|
||
|
"Completed #{complete_count}/#{total_count} checks, see #{first_status_url} ..."
|
||
|
else
|
||
|
"Build not started..."
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
|
||
|
$stdout.sync = true
|
||
|
|
||
|
puts "Checking build status..."
|
||
|
attempts = 0
|
||
|
checks = GithubStatusChecks.new
|
||
|
|
||
|
begin
|
||
|
loop do
|
||
|
case checks.state
|
||
|
when "success"
|
||
|
puts "Checks passed, see #{checks.first_status_url}"
|
||
|
exit 0
|
||
|
when "failure"
|
||
|
exit_with_error "Checks failed, see #{checks.first_status_url}"
|
||
|
when "pending"
|
||
|
attempts += 1
|
||
|
end
|
||
|
|
||
|
exit_with_error "Checks are still pending, gave up after #{MAX_ATTEMPTS * ATTEMPTS_GAP} seconds" if attempts == MAX_ATTEMPTS
|
||
|
|
||
|
puts checks.current_status
|
||
|
sleep(ATTEMPTS_GAP)
|
||
|
checks.refresh!
|
||
|
end
|
||
|
rescue Octokit::NotFound
|
||
|
exit_with_error "Build status could not be found"
|
||
|
end
|