|
#!/usr/bin/env ruby |
|
|
|
require 'optparse' |
|
require 'yaml' |
|
|
|
def find_config |
|
git_root = `git rev-parse --show-toplevel 2>/dev/null`.strip |
|
if !git_root.empty? |
|
path = File.join(git_root, 'config', 'kucli.yml') |
|
return path if File.exist?(path) |
|
end |
|
path = File.join(Dir.pwd, 'config', 'kucli.yml') |
|
return path if File.exist?(path) |
|
nil |
|
end |
|
|
|
config_path = find_config |
|
abort "No config/kucli.yml found (tried git root and current directory)." unless config_path |
|
|
|
raw = YAML.load_file(config_path) |
|
ENVIRONMENTS = raw.fetch('environments').transform_values { |v| v.transform_keys(&:to_sym) } |
|
DEPLOY_URL_PATTERN = raw['deploy_url_pattern'] |
|
|
|
def find_default_env(command) |
|
ENVIRONMENTS.each do |key, env| |
|
d = env[:default] |
|
next unless d |
|
return key if d == true |
|
return key if Array(d).map(&:to_s).include?(command.to_s) |
|
end |
|
nil |
|
end |
|
|
|
def env_default_label(env) |
|
d = env[:default] |
|
return nil unless d |
|
return " [default]" if d == true |
|
" [default: #{Array(d).join(', ')}]" |
|
end |
|
|
|
def show_help |
|
first_env = find_default_env('status') || ENVIRONMENTS.keys.first |
|
puts <<~USAGE |
|
Usage: kucli [command] [options] |
|
|
|
Commands: |
|
status (default) Show deployment status for the default or all environments |
|
run Exec into a deployment pod |
|
deploy Show the deploy URL for an environment |
|
help Show this help message |
|
|
|
Options: |
|
-e <environment> Target a specific environment (overrides default) |
|
--all Show all environments (status only) |
|
|
|
Available environments: |
|
#{ENVIRONMENTS.map { |key, env| " #{key.ljust(20)} #{env[:name]}#{env_default_label(env)}" }.join("\n")} |
|
|
|
Examples: |
|
kucli |
|
kucli status --all |
|
kucli status -e #{first_env} |
|
kucli run -e #{first_env} |
|
kucli run -e #{first_env} -- rails console |
|
kucli deploy -e #{first_env} |
|
USAGE |
|
end |
|
|
|
def resolve_env!(env_key, command) |
|
key = env_key || find_default_env(command) |
|
abort "#{command} requires -e <environment> (no default configured)\nRun `kucli help` for available environments." unless key |
|
ENVIRONMENTS[key] || abort("Unknown environment: #{key}\nRun `kucli help` for available environments.") |
|
end |
|
|
|
def status(env_filter: nil, all: false) |
|
envs = if all |
|
ENVIRONMENTS |
|
elsif env_filter |
|
env = ENVIRONMENTS[env_filter] |
|
abort "Unknown environment: #{env_filter}\nRun `kucli help` for available environments." unless env |
|
{ env_filter => env } |
|
else |
|
default_key = find_default_env('status') |
|
default_key ? { default_key => ENVIRONMENTS[default_key] } : ENVIRONMENTS |
|
end |
|
|
|
system('git', 'fetch', out: File::NULL, err: File::NULL) |
|
|
|
envs.each do |_key, env| |
|
name, context, namespace, deployment = env.values_at(:name, :context, :namespace, :deployment) |
|
image = `kubectl --context #{context} --namespace #{namespace} get deployments.apps #{deployment} -o jsonpath='{.spec.template.spec.containers[0].image}'` |
|
sha = image.split('/').last |
|
|
|
commits_behind = `git rev-list #{sha.split(':').last}..origin/master --count 2>/dev/null`.strip |
|
behind_str = case commits_behind |
|
when "" then "unknown" |
|
when "0" then "up-to-date" |
|
else "#{commits_behind} commits behind" |
|
end |
|
|
|
tag = `git tag --points-at #{sha.split(':').last} 2>/dev/null`.split("\n").first&.strip |
|
tag_str = tag&.then { |t| " [#{t}]" } |
|
|
|
puts "#{name}: #{sha} (#{behind_str})#{tag_str}" |
|
end |
|
end |
|
|
|
def run_exec(env_key, extra_args) |
|
env = resolve_env!(env_key, 'run') |
|
context, namespace, deployment = env.values_at(:context, :namespace, :deployment) |
|
command = extra_args.empty? ? ['/bin/bash'] : extra_args |
|
exec('kubectl', '--context', context, '--namespace', namespace, |
|
'exec', '-it', "deploy/#{deployment}", '--', *command) |
|
end |
|
|
|
def deploy(env_key) |
|
abort "No deploy_url_pattern set in config/kucli.yml." unless DEPLOY_URL_PATTERN |
|
env = resolve_env!(env_key, 'deploy') |
|
name = env[:name] |
|
|
|
url = DEPLOY_URL_PATTERN.gsub(/:(\w+)/) do |
|
key = $1.to_sym |
|
env[key] || abort("deploy_url_pattern references :#{$1} but it is not set for environment '#{env_key}'") |
|
end |
|
|
|
puts "#{name}: #{url}" |
|
end |
|
|
|
options = {} |
|
parser = OptionParser.new do |opts| |
|
opts.on('-e ENVIRONMENT', 'Target a specific environment') { |e| options[:env] = e } |
|
opts.on('--all', 'Show all environments (status only)') { options[:all] = true } |
|
opts.on('-h', '--help') { show_help; exit } |
|
end |
|
|
|
begin |
|
parser.parse!(ARGV) |
|
rescue OptionParser::InvalidOption => e |
|
abort "#{e.message}\nRun `kucli help` for usage." |
|
end |
|
|
|
command = ARGV.shift || 'status' |
|
|
|
case command |
|
when 'status' |
|
status(env_filter: options[:env], all: options[:all]) |
|
when 'run' |
|
run_exec(options[:env], ARGV) |
|
when 'deploy' |
|
deploy(options[:env]) |
|
when 'help' |
|
show_help |
|
else |
|
abort "Unknown command: #{command}\nRun `kucli help` for usage." |
|
end |