When invoking the audit broadcast command, provide a few environment variables so that people can customize the format of the message if they want. We currently provide `MRSK_PERFORMER`, `MRSK_ROLE`, `MRSK_DESTINATION` and `MRSK_EVENT`. Also adds the destination to the default message, which we continue to send as the first argument as before.
135 lines
3.8 KiB
Ruby
135 lines
3.8 KiB
Ruby
require "thor"
|
|
require "dotenv"
|
|
require "mrsk/sshkit_with_ext"
|
|
|
|
module Mrsk::Cli
|
|
class Base < Thor
|
|
include SSHKit::DSL
|
|
|
|
def self.exit_on_failure?() true end
|
|
|
|
class_option :verbose, type: :boolean, aliases: "-v", desc: "Detailed logging"
|
|
class_option :quiet, type: :boolean, aliases: "-q", desc: "Minimal logging"
|
|
|
|
class_option :version, desc: "Run commands against a specific app version"
|
|
|
|
class_option :primary, type: :boolean, aliases: "-p", desc: "Run commands only on primary host instead of all"
|
|
class_option :hosts, aliases: "-h", desc: "Run commands on these hosts instead of all (separate by comma)"
|
|
class_option :roles, aliases: "-r", desc: "Run commands on these roles instead of all (separate by comma)"
|
|
|
|
class_option :config_file, aliases: "-c", default: "config/deploy.yml", desc: "Path to config file"
|
|
class_option :destination, aliases: "-d", desc: "Specify destination to be used for config file (staging -> deploy.staging.yml)"
|
|
|
|
class_option :skip_broadcast, aliases: "-B", type: :boolean, default: false, desc: "Skip audit broadcasts"
|
|
|
|
def initialize(*)
|
|
super
|
|
load_envs
|
|
initialize_commander(options_with_subcommand_class_options)
|
|
end
|
|
|
|
private
|
|
def load_envs
|
|
if destination = options[:destination]
|
|
Dotenv.load(".env.#{destination}", ".env")
|
|
else
|
|
Dotenv.load(".env")
|
|
end
|
|
end
|
|
|
|
def options_with_subcommand_class_options
|
|
options.merge(@_initializer.last[:class_options] || {})
|
|
end
|
|
|
|
def initialize_commander(options)
|
|
MRSK.tap do |commander|
|
|
if options[:verbose]
|
|
ENV["VERBOSE"] = "1" # For backtraces via cli/start
|
|
commander.verbosity = :debug
|
|
end
|
|
|
|
if options[:quiet]
|
|
commander.verbosity = :error
|
|
end
|
|
|
|
commander.configure \
|
|
config_file: Pathname.new(File.expand_path(options[:config_file])),
|
|
destination: options[:destination],
|
|
version: options[:version]
|
|
|
|
commander.specific_hosts = options[:hosts]&.split(",")
|
|
commander.specific_roles = options[:roles]&.split(",")
|
|
commander.specific_primary! if options[:primary]
|
|
end
|
|
end
|
|
|
|
def print_runtime
|
|
started_at = Time.now
|
|
yield
|
|
return Time.now - started_at
|
|
ensure
|
|
runtime = Time.now - started_at
|
|
puts " Finished all in #{sprintf("%.1f seconds", runtime)}"
|
|
end
|
|
|
|
def audit_broadcast(line)
|
|
if broadcast = MRSK.auditor.broadcast(line)
|
|
system(MRSK.auditor.broadcast_environment(line), broadcast)
|
|
end
|
|
end
|
|
|
|
def with_lock
|
|
if MRSK.holding_lock?
|
|
yield
|
|
else
|
|
acquire_lock
|
|
|
|
begin
|
|
yield
|
|
rescue
|
|
if MRSK.hold_lock_on_error?
|
|
error " \e[31mDeploy lock was not released\e[0m"
|
|
else
|
|
release_lock
|
|
end
|
|
|
|
raise
|
|
end
|
|
|
|
release_lock
|
|
end
|
|
end
|
|
|
|
def acquire_lock
|
|
say "Acquiring the deploy lock"
|
|
on(MRSK.primary_host) { execute *MRSK.lock.acquire("Automatic deploy lock", MRSK.config.version) }
|
|
|
|
MRSK.holding_lock = true
|
|
rescue SSHKit::Runner::ExecuteError => e
|
|
if e.message =~ /cannot create directory/
|
|
invoke "mrsk:cli:lock:status", []
|
|
raise LockError, "Deploy lock found"
|
|
else
|
|
raise e
|
|
end
|
|
end
|
|
|
|
def release_lock
|
|
say "Releasing the deploy lock"
|
|
on(MRSK.primary_host) { execute *MRSK.lock.release }
|
|
|
|
MRSK.holding_lock = false
|
|
end
|
|
|
|
def hold_lock_on_error
|
|
if MRSK.hold_lock_on_error?
|
|
yield
|
|
else
|
|
MRSK.hold_lock_on_error = true
|
|
yield
|
|
MRSK.hold_lock_on_error = false
|
|
end
|
|
end
|
|
end
|
|
end
|