Add Kamal deployment scripts
This commit is contained in:
		
							
								
								
									
										3
									
								
								.kamal/hooks/docker-setup.sample
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										3
									
								
								.kamal/hooks/docker-setup.sample
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
 | 
			
		||||
echo "Docker set up on $KAMAL_HOSTS..."
 | 
			
		||||
							
								
								
									
										3
									
								
								.kamal/hooks/post-app-boot.sample
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										3
									
								
								.kamal/hooks/post-app-boot.sample
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
 | 
			
		||||
echo "Booted app version $KAMAL_VERSION on $KAMAL_HOSTS..."
 | 
			
		||||
							
								
								
									
										14
									
								
								.kamal/hooks/post-deploy.sample
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										14
									
								
								.kamal/hooks/post-deploy.sample
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
 | 
			
		||||
# A sample post-deploy hook
 | 
			
		||||
#
 | 
			
		||||
# These environment variables are available:
 | 
			
		||||
# KAMAL_RECORDED_AT
 | 
			
		||||
# KAMAL_PERFORMER
 | 
			
		||||
# KAMAL_VERSION
 | 
			
		||||
# KAMAL_HOSTS
 | 
			
		||||
# KAMAL_ROLE (if set)
 | 
			
		||||
# KAMAL_DESTINATION (if set)
 | 
			
		||||
# KAMAL_RUNTIME
 | 
			
		||||
 | 
			
		||||
echo "$KAMAL_PERFORMER deployed $KAMAL_VERSION to $KAMAL_DESTINATION in $KAMAL_RUNTIME seconds"
 | 
			
		||||
							
								
								
									
										3
									
								
								.kamal/hooks/post-proxy-reboot.sample
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										3
									
								
								.kamal/hooks/post-proxy-reboot.sample
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
 | 
			
		||||
echo "Rebooted kamal-proxy on $KAMAL_HOSTS"
 | 
			
		||||
							
								
								
									
										3
									
								
								.kamal/hooks/pre-app-boot.sample
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										3
									
								
								.kamal/hooks/pre-app-boot.sample
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
 | 
			
		||||
echo "Booting app version $KAMAL_VERSION on $KAMAL_HOSTS..."
 | 
			
		||||
							
								
								
									
										51
									
								
								.kamal/hooks/pre-build.sample
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										51
									
								
								.kamal/hooks/pre-build.sample
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
 | 
			
		||||
# A sample pre-build hook
 | 
			
		||||
#
 | 
			
		||||
# Checks:
 | 
			
		||||
# 1. We have a clean checkout
 | 
			
		||||
# 2. A remote is configured
 | 
			
		||||
# 3. The branch has been pushed to the remote
 | 
			
		||||
# 4. The version we are deploying matches the remote
 | 
			
		||||
#
 | 
			
		||||
# These environment variables are available:
 | 
			
		||||
# KAMAL_RECORDED_AT
 | 
			
		||||
# KAMAL_PERFORMER
 | 
			
		||||
# KAMAL_VERSION
 | 
			
		||||
# KAMAL_HOSTS
 | 
			
		||||
# KAMAL_ROLE (if set)
 | 
			
		||||
# KAMAL_DESTINATION (if set)
 | 
			
		||||
 | 
			
		||||
if [ -n "$(git status --porcelain)" ]; then
 | 
			
		||||
  echo "Git checkout is not clean, aborting..." >&2
 | 
			
		||||
  git status --porcelain >&2
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
first_remote=$(git remote)
 | 
			
		||||
 | 
			
		||||
if [ -z "$first_remote" ]; then
 | 
			
		||||
  echo "No git remote set, aborting..." >&2
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
current_branch=$(git branch --show-current)
 | 
			
		||||
 | 
			
		||||
if [ -z "$current_branch" ]; then
 | 
			
		||||
  echo "Not on a git branch, aborting..." >&2
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
remote_head=$(git ls-remote $first_remote --tags $current_branch | cut -f1)
 | 
			
		||||
 | 
			
		||||
if [ -z "$remote_head" ]; then
 | 
			
		||||
  echo "Branch not pushed to remote, aborting..." >&2
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "$KAMAL_VERSION" != "$remote_head" ]; then
 | 
			
		||||
  echo "Version ($KAMAL_VERSION) does not match remote HEAD ($remote_head), aborting..." >&2
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
exit 0
 | 
			
		||||
							
								
								
									
										47
									
								
								.kamal/hooks/pre-connect.sample
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										47
									
								
								.kamal/hooks/pre-connect.sample
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
#!/usr/bin/env ruby
 | 
			
		||||
 | 
			
		||||
# A sample pre-connect check
 | 
			
		||||
#
 | 
			
		||||
# Warms DNS before connecting to hosts in parallel
 | 
			
		||||
#
 | 
			
		||||
# These environment variables are available:
 | 
			
		||||
# KAMAL_RECORDED_AT
 | 
			
		||||
# KAMAL_PERFORMER
 | 
			
		||||
# KAMAL_VERSION
 | 
			
		||||
# KAMAL_HOSTS
 | 
			
		||||
# KAMAL_ROLE (if set)
 | 
			
		||||
# KAMAL_DESTINATION (if set)
 | 
			
		||||
# KAMAL_RUNTIME
 | 
			
		||||
 | 
			
		||||
hosts = ENV["KAMAL_HOSTS"].split(",")
 | 
			
		||||
results = nil
 | 
			
		||||
max = 3
 | 
			
		||||
 | 
			
		||||
elapsed = Benchmark.realtime do
 | 
			
		||||
  results = hosts.map do |host|
 | 
			
		||||
    Thread.new do
 | 
			
		||||
      tries = 1
 | 
			
		||||
 | 
			
		||||
      begin
 | 
			
		||||
        Socket.getaddrinfo(host, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME)
 | 
			
		||||
      rescue SocketError
 | 
			
		||||
        if tries < max
 | 
			
		||||
          puts "Retrying DNS warmup: #{host}"
 | 
			
		||||
          tries += 1
 | 
			
		||||
          sleep rand
 | 
			
		||||
          retry
 | 
			
		||||
        else
 | 
			
		||||
          puts "DNS warmup failed: #{host}"
 | 
			
		||||
          host
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      tries
 | 
			
		||||
    end
 | 
			
		||||
  end.map(&:value)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
retries = results.sum - hosts.size
 | 
			
		||||
nopes = results.count { |r| r == max }
 | 
			
		||||
 | 
			
		||||
puts "Prewarmed %d DNS lookups in %.2f sec: %d retries, %d failures" % [ hosts.size, elapsed, retries, nopes ]
 | 
			
		||||
							
								
								
									
										109
									
								
								.kamal/hooks/pre-deploy.sample
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										109
									
								
								.kamal/hooks/pre-deploy.sample
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,109 @@
 | 
			
		||||
#!/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
 | 
			
		||||
							
								
								
									
										3
									
								
								.kamal/hooks/pre-proxy-reboot.sample
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										3
									
								
								.kamal/hooks/pre-proxy-reboot.sample
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
 | 
			
		||||
echo "Rebooting kamal-proxy on $KAMAL_HOSTS..."
 | 
			
		||||
							
								
								
									
										17
									
								
								.kamal/secrets
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								.kamal/secrets
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
# Secrets defined here are available for reference under registry/password, env/secret, builder/secrets,
 | 
			
		||||
# and accessories/*/env/secret in config/deploy.yml. All secrets should be pulled from either
 | 
			
		||||
# password manager, ENV, or a file. DO NOT ENTER RAW CREDENTIALS HERE! This file needs to be safe for git.
 | 
			
		||||
 | 
			
		||||
# Example of extracting secrets from 1password (or another compatible pw manager)
 | 
			
		||||
# SECRETS=$(kamal secrets fetch --adapter 1password --account your-account --from Vault/Item KAMAL_REGISTRY_PASSWORD RAILS_MASTER_KEY)
 | 
			
		||||
# KAMAL_REGISTRY_PASSWORD=$(kamal secrets extract KAMAL_REGISTRY_PASSWORD ${SECRETS})
 | 
			
		||||
# RAILS_MASTER_KEY=$(kamal secrets extract RAILS_MASTER_KEY ${SECRETS})
 | 
			
		||||
 | 
			
		||||
# Use a GITHUB_TOKEN if private repositories are needed for the image
 | 
			
		||||
# GITHUB_TOKEN=$(gh config get -h github.com oauth_token)
 | 
			
		||||
 | 
			
		||||
# Grab the registry password from ENV
 | 
			
		||||
KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
 | 
			
		||||
 | 
			
		||||
# Improve security by using a password manager. Never check config/master.key into git!
 | 
			
		||||
RAILS_MASTER_KEY=$(cat config/master.key)
 | 
			
		||||
		Reference in New Issue
	
	Block a user