class Hu::Cli::Collab
Constants
- OP_COLORS
- OP_MAP
Public Instance Methods
collab()
click to toggle source
# File lib/hu/collab.rb, line 46 def collab; end
diff(_cmd, opts, _argv)
click to toggle source
# File lib/hu/collab.rb, line 56 def diff(_cmd, opts, _argv) parsed_state = parse_as_json_or_yaml(STDIN.read) show_plan(plan(HashDiff.diff(heroku_state['apps'], parsed_state['apps']), opts)) end
export(_cmd, opts, _argv)
click to toggle source
# File lib/hu/collab.rb, line 85 def export(_cmd, opts, _argv) puts heroku_state.send("to_#{opts[:format]}".to_sym) end
h()
click to toggle source
# File lib/hu/collab.rb, line 254 def h @h ||= PlatformAPI.connect_oauth(Hu::API_TOKEN) end
heroku_state(force_refresh = false)
click to toggle source
# File lib/hu/collab.rb, line 223 def heroku_state(force_refresh = false) return @heroku_state unless force_refresh || @heroku_state.nil? all_collaborators = Set.new data = { 'apps' => {} } app_names = h.app.list.map { |e| e['name'] }.reject { |e| ignored_app?(e) } threads = [] app_names.each_with_index do |app_name, _i| threads << Thread.new do d = data['apps'][app_name] = { 'collaborators' => [] } h.collaborator.list(app_name).map do |e| case e['role'] when 'owner' d['owner'] = e['user']['email'] when nil, 'member' d['collaborators'] << e['user']['email'] else raise "Unknown collaborator role: #{e['role']}" end all_collaborators << e['user']['email'] end end end threads.each_with_index do |t, i| t.join pb msg: app_names[i], total: app_names.length, done: i + 1 end pb :wipe data['collaborators'] = all_collaborators.to_a.sort @heroku_state = data end
ignored_app?(app_name)
click to toggle source
# File lib/hu/collab.rb, line 216 def ignored_app?(app_name) ENV.fetch('HU_IGNORE_APPS', '').split(' ').each do |p| return true if File.fnmatch(p, app_name) end false end
import(_cmd, opts, _argv)
click to toggle source
# File lib/hu/collab.rb, line 94 def import(_cmd, opts, _argv) parsed_state = parse_as_json_or_yaml(STDIN.read) validators = { op_add_collaborators: proc do |op| unless heroku_state['collaborators'].include?(op[:value]) || opts[:allow_create] raise InvalidOperation, "Use -c to allow creation of new collaborator '#{op[:value]}'" end end } begin plan(HashDiff.diff(heroku_state['apps'], parsed_state['apps']), opts, validators).each do |s| color = OP_COLORS[s[:op]] msg = '' icon = ' ' eol = "\e[1G" if s[:method].nil? color = "\e[0;41;33;1m" msg = 'Skipped.' icon = "\e[0;31;1m\u2718\e[0m" # X eol = "\n" end STDERR.printf "%s %s%6s %-30s %-15s %-30s %s\e[0m%s", icon, color, s[:op_name], s[:app_name], s[:role], s[:value], msg, eol next if s[:method].nil? begin send(s[:method], s) STDERR.puts "\e[0;32;1m\u2713\e[0m\n" # check rescue => e STDERR.puts "\e[0;31;1m\u2718\e[0m\n" # X puts e.inspect puts e.backtrace exit 1 end end # /plan() rescue InvalidPlan => e STDERR.puts "\e[0;31;1m#{e}:\e[0m\n\n" show_plan(e.invalid_plan) exit 1 end end
normalize(parsed)
click to toggle source
# File lib/hu/collab.rb, line 200 def normalize(parsed) unless parsed.include? 'apps' raise ArgumentError, "Malformed input, key 'apps' not found." end parsed['apps'].reject! { |e| ignored_app?(e) } parsed['apps'].each do |app_name, v| unless heroku_state['apps'].include? app_name raise ArgumentError, "Unknown application: #{app_name}" end next unless v['collaborators'].is_a? Array v['collaborators'].flatten! v['collaborators'].sort! end parsed end
op_add_collaborators(args)
click to toggle source
# File lib/hu/collab.rb, line 171 def op_add_collaborators(args) h.collaborator.create(args[:app_name], user: args[:value], silent: args[:env][:silent_create]) end
op_remove_collaborators(args)
click to toggle source
# File lib/hu/collab.rb, line 175 def op_remove_collaborators(args) h.collaborator.delete(args[:app_name], args[:value]) end
parse_as_json_or_yaml(input)
click to toggle source
# File lib/hu/collab.rb, line 179 def parse_as_json_or_yaml(input) begin parsed = JSON.load(input) rescue => jex begin parsed = YAML.load(input) if parsed.is_a? String raise ArgumentError, 'Input parsed as YAML yields a String' end rescue => yex STDERR.puts 'Error: Could neither parse stdin as YAML nor as JSON.' STDERR.puts '-- JSON Error --' STDERR.puts jex STDERR.puts '-- YAML Error --' STDERR.puts yex raise ArgumentError end end normalize(parsed) end
pb(show_opts)
click to toggle source
# File lib/hu/collab.rb, line 258 def pb(show_opts) return if $quiet @pb ||= PowerBar.new show_opts == :wipe ? @pb.wipe : @pb.show(show_opts) end
plan(diff, env = {}, validators = {})
click to toggle source
# File lib/hu/collab.rb, line 139 def plan(diff, env = {}, validators = {}) plan = [] last_error = nil diff.each do |op, target, lval, rval| value = rval || lval app_name, role = target.split('.') role = role.split('[')[0] unless role.nil? op_name = OP_MAP[op] method_name = "op_#{op_name}_#{role}".to_sym operation = { app_name: app_name, op: op, op_name: op_name, method: respond_to?(method_name) ? method_name : nil, value: value, role: role, env: env } if validators.include? method_name begin validators[method_name].call(operation) rescue InvalidOperation => e last_error = operation[:invalid] = e end end plan << operation end raise InvalidPlan.new('Plan did not pass validation', plan) unless last_error.nil? plan end
show_plan(plan)
click to toggle source
# File lib/hu/collab.rb, line 61 def show_plan(plan) plan.each do |s| color = OP_COLORS[s[:op]] msg = '' icon = ' ' if s[:method].nil? color = "\e[0m" msg = '<-- Can not resolve this (NO-OP)' icon = '⚠️' elsif s[:invalid] color = "\e[0m" icon = '⚠️' end STDERR.printf "%1s %s%6s %-30s %-15s %-30s %s\e[0m\n", icon, color, s[:op_name], s[:app_name], s[:role], s[:value], msg if s[:invalid] STDERR.puts "\e[31;1m Error: #{s[:invalid]}\e[0m\n\n" end end end