pebblescape 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +23 -0
- data/Rakefile +2 -0
- data/bin/pebbles +14 -0
- data/lib/pebbles.rb +23 -0
- data/lib/pebbles/api.rb +122 -0
- data/lib/pebbles/api/apps.rb +71 -0
- data/lib/pebbles/api/config_vars.rb +33 -0
- data/lib/pebbles/api/errors.rb +27 -0
- data/lib/pebbles/api/login.rb +14 -0
- data/lib/pebbles/api/releases.rb +33 -0
- data/lib/pebbles/api/user.rb +14 -0
- data/lib/pebbles/auth.rb +302 -0
- data/lib/pebbles/cli.rb +35 -0
- data/lib/pebbles/command.rb +256 -0
- data/lib/pebbles/command/apps.rb +225 -0
- data/lib/pebbles/command/auth.rb +85 -0
- data/lib/pebbles/command/base.rb +231 -0
- data/lib/pebbles/command/config.rb +147 -0
- data/lib/pebbles/command/help.rb +124 -0
- data/lib/pebbles/git.rb +69 -0
- data/lib/pebbles/helpers.rb +284 -0
- data/lib/pebbles/version.rb +3 -0
- data/pebbles.gemspec +27 -0
- metadata +142 -0
| @@ -0,0 +1,147 @@ | |
| 1 | 
            +
            require "pebbles/command/base"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # manage app config vars
         | 
| 4 | 
            +
            #
         | 
| 5 | 
            +
            class Pebbles::Command::Config < Pebbles::Command::Base
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              # config
         | 
| 8 | 
            +
              #
         | 
| 9 | 
            +
              # display the config vars for an app
         | 
| 10 | 
            +
              #
         | 
| 11 | 
            +
              # -s, --shell  # output config vars in shell format
         | 
| 12 | 
            +
              #
         | 
| 13 | 
            +
              #Examples:
         | 
| 14 | 
            +
              #
         | 
| 15 | 
            +
              # $ pebbles config
         | 
| 16 | 
            +
              # A: one
         | 
| 17 | 
            +
              # B: two
         | 
| 18 | 
            +
              #
         | 
| 19 | 
            +
              # $ pebbles config --shell
         | 
| 20 | 
            +
              # A=one
         | 
| 21 | 
            +
              # B=two
         | 
| 22 | 
            +
              #
         | 
| 23 | 
            +
              def index
         | 
| 24 | 
            +
                validate_arguments!
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                vars = if options[:shell]
         | 
| 27 | 
            +
                         api.get_config_vars(app).body
         | 
| 28 | 
            +
                       else
         | 
| 29 | 
            +
                         api.request(
         | 
| 30 | 
            +
                           :expects  => 200,
         | 
| 31 | 
            +
                           :method   => :get,
         | 
| 32 | 
            +
                           :path     => "/apps/#{app}/config_vars",
         | 
| 33 | 
            +
                           :query    => { "symbolic" => true }
         | 
| 34 | 
            +
                         ).body
         | 
| 35 | 
            +
                       end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                if vars.empty?
         | 
| 38 | 
            +
                  display("#{app} has no config vars.")
         | 
| 39 | 
            +
                else
         | 
| 40 | 
            +
                  vars.each {|key, value| vars[key] = value.to_s}
         | 
| 41 | 
            +
                  if options[:shell]
         | 
| 42 | 
            +
                    vars.keys.sort.each do |key|
         | 
| 43 | 
            +
                      display(%{#{key}=#{vars[key]}})
         | 
| 44 | 
            +
                    end
         | 
| 45 | 
            +
                  else
         | 
| 46 | 
            +
                    styled_header("#{app} Config Vars")
         | 
| 47 | 
            +
                    styled_hash(vars)
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
              end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
              # config:set KEY1=VALUE1 [KEY2=VALUE2 ...]
         | 
| 53 | 
            +
              #
         | 
| 54 | 
            +
              # set one or more config vars
         | 
| 55 | 
            +
              #
         | 
| 56 | 
            +
              #Example:
         | 
| 57 | 
            +
              #
         | 
| 58 | 
            +
              # $ pebbles config:set A=one
         | 
| 59 | 
            +
              # Setting config vars and restarting example... done, v123
         | 
| 60 | 
            +
              # A: one
         | 
| 61 | 
            +
              #
         | 
| 62 | 
            +
              # $ pebbles config:set A=one B=two
         | 
| 63 | 
            +
              # Setting config vars and restarting example... done, v123
         | 
| 64 | 
            +
              # A: one
         | 
| 65 | 
            +
              # B: two
         | 
| 66 | 
            +
              #
         | 
| 67 | 
            +
              def set
         | 
| 68 | 
            +
                unless args.size > 0 and args.all? { |a| a.include?('=') }
         | 
| 69 | 
            +
                  error("Usage: pebbles config:set KEY1=VALUE1 [KEY2=VALUE2 ...]\nMust specify KEY and VALUE to set.")
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                vars = args.inject({}) do |vars, arg|
         | 
| 73 | 
            +
                  key, value = arg.split('=', 2)
         | 
| 74 | 
            +
                  vars[key] = value
         | 
| 75 | 
            +
                  vars
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                action("Setting config vars and restarting #{app}") do
         | 
| 79 | 
            +
                  api.put_config_vars(app, vars)
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                  @status = begin
         | 
| 82 | 
            +
                    if release = api.get_release(app, 'current').body
         | 
| 83 | 
            +
                      release['name']
         | 
| 84 | 
            +
                    end
         | 
| 85 | 
            +
                  rescue Pebbles::API::Errors::RequestFailed => e
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                vars.each {|key, value| vars[key] = value.to_s}
         | 
| 90 | 
            +
                styled_hash(vars)
         | 
| 91 | 
            +
              end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
              alias_command "config:add", "config:set"
         | 
| 94 | 
            +
             | 
| 95 | 
            +
              # config:get KEY
         | 
| 96 | 
            +
              #
         | 
| 97 | 
            +
              # display a config value for an app
         | 
| 98 | 
            +
              #
         | 
| 99 | 
            +
              #Examples:
         | 
| 100 | 
            +
              #
         | 
| 101 | 
            +
              # $ pebbles config:get A
         | 
| 102 | 
            +
              # one
         | 
| 103 | 
            +
              #
         | 
| 104 | 
            +
              def get
         | 
| 105 | 
            +
                unless key = shift_argument
         | 
| 106 | 
            +
                  error("Usage: pebbles config:get KEY\nMust specify KEY.")
         | 
| 107 | 
            +
                end
         | 
| 108 | 
            +
                validate_arguments!
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                vars = api.get_config_vars(app).body
         | 
| 111 | 
            +
                key, value = vars.detect {|k,v| k == key}
         | 
| 112 | 
            +
                display(value.to_s)
         | 
| 113 | 
            +
              end
         | 
| 114 | 
            +
             | 
| 115 | 
            +
              # config:unset KEY1 [KEY2 ...]
         | 
| 116 | 
            +
              #
         | 
| 117 | 
            +
              # unset one or more config vars
         | 
| 118 | 
            +
              #
         | 
| 119 | 
            +
              # $ pebbles config:unset A
         | 
| 120 | 
            +
              # Unsetting A and restarting example... done, v123
         | 
| 121 | 
            +
              #
         | 
| 122 | 
            +
              # $ pebbles config:unset A B
         | 
| 123 | 
            +
              # Unsetting A and restarting example... done, v123
         | 
| 124 | 
            +
              # Unsetting B and restarting example... done, v124
         | 
| 125 | 
            +
              #
         | 
| 126 | 
            +
              def unset
         | 
| 127 | 
            +
                if args.empty?
         | 
| 128 | 
            +
                  error("Usage: pebbles config:unset KEY1 [KEY2 ...]\nMust specify KEY to unset.")
         | 
| 129 | 
            +
                end
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                args.each do |key|
         | 
| 132 | 
            +
                  action("Unsetting #{key} and restarting #{app}") do
         | 
| 133 | 
            +
                    api.delete_config_var(app, key)
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                    @status = begin
         | 
| 136 | 
            +
                      if release = api.get_release(app, 'current').body
         | 
| 137 | 
            +
                        release['name']
         | 
| 138 | 
            +
                      end
         | 
| 139 | 
            +
                    rescue Pebbles::API::Errors::RequestFailed => e
         | 
| 140 | 
            +
                    end
         | 
| 141 | 
            +
                  end
         | 
| 142 | 
            +
                end
         | 
| 143 | 
            +
              end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
              alias_command "config:remove", "config:unset"
         | 
| 146 | 
            +
             | 
| 147 | 
            +
            end
         | 
| @@ -0,0 +1,124 @@ | |
| 1 | 
            +
            require "pebbles/command/base"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # list commands and display help
         | 
| 4 | 
            +
            #
         | 
| 5 | 
            +
            class Pebbles::Command::Help < Pebbles::Command::Base
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              PRIMARY_NAMESPACES = %w( auth apps ps run addons config releases domains logs sharing )
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              def index
         | 
| 10 | 
            +
                if command = args.shift
         | 
| 11 | 
            +
                  help_for_command(command)
         | 
| 12 | 
            +
                else
         | 
| 13 | 
            +
                  help_for_root
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              alias_command "-h", "help"
         | 
| 18 | 
            +
              alias_command "--help", "help"
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            private
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              def commands_for_namespace(name)
         | 
| 23 | 
            +
                Pebbles::Command.commands.values.select do |command|
         | 
| 24 | 
            +
                  command[:namespace] == name && command[:command] != name
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              def namespaces
         | 
| 29 | 
            +
                namespaces = Pebbles::Command.namespaces
         | 
| 30 | 
            +
                namespaces.delete("app")
         | 
| 31 | 
            +
                namespaces
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              def commands
         | 
| 35 | 
            +
                Pebbles::Command.commands
         | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              def skip_namespace?(ns)
         | 
| 39 | 
            +
                return true if ns[:description] =~ /DEPRECATED:/
         | 
| 40 | 
            +
                return true if ns[:description] =~ /HIDDEN:/
         | 
| 41 | 
            +
                false
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
              def skip_command?(command)
         | 
| 45 | 
            +
                return true if command[:help] =~ /DEPRECATED:/
         | 
| 46 | 
            +
                return true if command[:help] =~ /^ HIDDEN:/
         | 
| 47 | 
            +
                false
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
              def primary_namespaces
         | 
| 51 | 
            +
                PRIMARY_NAMESPACES.map { |name| namespaces[name] }.compact
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
              def additional_namespaces
         | 
| 55 | 
            +
                (namespaces.values - primary_namespaces)
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
              def summary_for_namespaces(namespaces)
         | 
| 59 | 
            +
                size = longest(namespaces.map { |n| n[:name] })
         | 
| 60 | 
            +
                namespaces.sort_by {|namespace| namespace[:name]}.each do |namespace|
         | 
| 61 | 
            +
                  next if skip_namespace?(namespace)
         | 
| 62 | 
            +
                  name = namespace[:name]
         | 
| 63 | 
            +
                  namespace[:description]
         | 
| 64 | 
            +
                  puts "  %-#{size}s  # %s" % [ name, namespace[:description] ]
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
              end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
              def help_for_root
         | 
| 69 | 
            +
                puts "Usage: pebbles COMMAND [--app APP] [command-specific-options]"
         | 
| 70 | 
            +
                puts
         | 
| 71 | 
            +
                puts "Primary help topics, type \"pebbles help TOPIC\" for more details:"
         | 
| 72 | 
            +
                puts
         | 
| 73 | 
            +
                summary_for_namespaces(primary_namespaces)
         | 
| 74 | 
            +
                puts
         | 
| 75 | 
            +
                puts "Additional topics:"
         | 
| 76 | 
            +
                puts
         | 
| 77 | 
            +
                summary_for_namespaces(additional_namespaces)
         | 
| 78 | 
            +
                puts
         | 
| 79 | 
            +
              end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
              def help_for_namespace(name)
         | 
| 82 | 
            +
                namespace_commands = commands_for_namespace(name)
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                unless namespace_commands.empty?
         | 
| 85 | 
            +
                  size = longest(namespace_commands.map { |c| c[:banner] })
         | 
| 86 | 
            +
                  namespace_commands.sort_by { |c| c[:banner].to_s }.each do |command|
         | 
| 87 | 
            +
                    next if skip_command?(command)
         | 
| 88 | 
            +
                    command[:summary]
         | 
| 89 | 
            +
                    puts "  %-#{size}s  # %s" % [ command[:banner], command[:summary] ]
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
              end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
              def help_for_command(name)
         | 
| 95 | 
            +
                if command_alias = Pebbles::Command.command_aliases[name]
         | 
| 96 | 
            +
                  display("Alias: #{name} redirects to #{command_alias}")
         | 
| 97 | 
            +
                  name = command_alias
         | 
| 98 | 
            +
                end
         | 
| 99 | 
            +
                if command = commands[name]
         | 
| 100 | 
            +
                  puts "Usage: pebbles #{command[:banner]}"
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                  if command[:help].strip.length > 0
         | 
| 103 | 
            +
                    help = command[:help].split("\n").reject do |line|
         | 
| 104 | 
            +
                      line =~ /HIDDEN/
         | 
| 105 | 
            +
                    end
         | 
| 106 | 
            +
                    puts help[1..-1].join("\n")
         | 
| 107 | 
            +
                  end
         | 
| 108 | 
            +
                  puts
         | 
| 109 | 
            +
                end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                namespace_commands = commands_for_namespace(name).reject do |command|
         | 
| 112 | 
            +
                  command[:help] =~ /DEPRECATED/
         | 
| 113 | 
            +
                end
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                if !namespace_commands.empty?
         | 
| 116 | 
            +
                  puts "Additional commands, type \"pebbles help COMMAND\" for more details:"
         | 
| 117 | 
            +
                  puts
         | 
| 118 | 
            +
                  help_for_namespace(name)
         | 
| 119 | 
            +
                  puts
         | 
| 120 | 
            +
                elsif command.nil?
         | 
| 121 | 
            +
                  error "#{name} is not a pebbles command. See `pebbles help`."
         | 
| 122 | 
            +
                end
         | 
| 123 | 
            +
              end
         | 
| 124 | 
            +
            end
         | 
    
        data/lib/pebbles/git.rb
    ADDED
    
    | @@ -0,0 +1,69 @@ | |
| 1 | 
            +
            require "pebbles/helpers"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Pebbles::Git
         | 
| 4 | 
            +
              extend Pebbles::Helpers
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              def self.check_git_version
         | 
| 7 | 
            +
                return unless running_on_windows? || running_on_a_mac?
         | 
| 8 | 
            +
                if git_is_insecure(git_version)
         | 
| 9 | 
            +
                  warn_about_insecure_git
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              def self.git_is_insecure(version)
         | 
| 14 | 
            +
                v = Version.parse(version)
         | 
| 15 | 
            +
                if v < Version.parse('1.8.5.6')
         | 
| 16 | 
            +
                  return true
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
                if v >= Version.parse('1.9') && v < Version.parse('1.9.5')
         | 
| 19 | 
            +
                  return true
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
                if v >= Version.parse('2.0') && v < Version.parse('2.0.5')
         | 
| 22 | 
            +
                  return true
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
                if v >= Version.parse('2.1') && v < Version.parse('2.1.4')
         | 
| 25 | 
            +
                  return true
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
                if v >= Version.parse('2.2') && v < Version.parse('2.2.1')
         | 
| 28 | 
            +
                  return true
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
                return false
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              def self.warn_about_insecure_git
         | 
| 34 | 
            +
                warn "Your version of git is #{git_version}. Which has serious security vulnerabilities."
         | 
| 35 | 
            +
                warn "More information here: https://blog.heroku.com/archives/2014/12/23/update_your_git_clients_on_windows_and_os_x"
         | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              private
         | 
| 39 | 
            +
             | 
| 40 | 
            +
              def self.git_version
         | 
| 41 | 
            +
                version = /git version ([\d\.]+)/.match(`git --version`)
         | 
| 42 | 
            +
                error("Git appears to be installed incorrectly\nEnsure that `git --version` outputs the version correctly.") unless version
         | 
| 43 | 
            +
                version[1]
         | 
| 44 | 
            +
              rescue Errno::ENOENT
         | 
| 45 | 
            +
                error("Git must be installed to use pebbles.\nSee instructions here: http://git-scm.com")
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
              class Version
         | 
| 49 | 
            +
                include Comparable
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                attr_accessor :major, :minor, :patch, :special
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                def initialize(major, minor=0, patch=0, special=0)
         | 
| 54 | 
            +
                  @major, @minor, @patch, @special = major, minor, patch, special
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                def self.parse(s)
         | 
| 58 | 
            +
                  digits = s.split('.').map { |i| i.to_i }
         | 
| 59 | 
            +
                  Version.new(*digits)
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                def <=>(other)
         | 
| 63 | 
            +
                  return major <=> other.major unless (major <=> other.major) == 0
         | 
| 64 | 
            +
                  return minor <=> other.minor unless (minor <=> other.minor) == 0
         | 
| 65 | 
            +
                  return patch <=> other.patch unless (patch <=> other.patch) == 0
         | 
| 66 | 
            +
                  return special <=> other.special
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
              end
         | 
| 69 | 
            +
            end
         | 
| @@ -0,0 +1,284 @@ | |
| 1 | 
            +
            module Pebbles
         | 
| 2 | 
            +
              module Helpers
         | 
| 3 | 
            +
                extend self
         | 
| 4 | 
            +
                
         | 
| 5 | 
            +
                def home_directory
         | 
| 6 | 
            +
                  return Dir.home if defined? Dir.home # Ruby 1.9+
         | 
| 7 | 
            +
                  running_on_windows? ? ENV['USERPROFILE'].gsub("\\","/") : ENV['HOME']
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def running_on_windows?
         | 
| 11 | 
            +
                  RUBY_PLATFORM =~ /mswin32|mingw32/
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def running_on_a_mac?
         | 
| 15 | 
            +
                  RUBY_PLATFORM =~ /-darwin\d/
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
                
         | 
| 18 | 
            +
                def launchy(message, url)
         | 
| 19 | 
            +
                  action(message) do
         | 
| 20 | 
            +
                    require("launchy")
         | 
| 21 | 
            +
                    launchy = Launchy.open(url)
         | 
| 22 | 
            +
                    if launchy.respond_to?(:join)
         | 
| 23 | 
            +
                      launchy.join
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
                
         | 
| 28 | 
            +
                def output_with_bang(message="", new_line=true)
         | 
| 29 | 
            +
                  return if message.to_s.strip == ""
         | 
| 30 | 
            +
                  display(format_with_bang(message), new_line)
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
                
         | 
| 33 | 
            +
                def format_with_bang(message)
         | 
| 34 | 
            +
                  return '' if message.to_s.strip == ""
         | 
| 35 | 
            +
                  " !    " + message.split("\n").join("\n !    ")
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
                
         | 
| 38 | 
            +
                def display(msg="", new_line=true)
         | 
| 39 | 
            +
                  if new_line
         | 
| 40 | 
            +
                    puts(msg)
         | 
| 41 | 
            +
                  else
         | 
| 42 | 
            +
                    print(msg)
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
                  $stdout.flush
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
                
         | 
| 47 | 
            +
                def debug(*args)
         | 
| 48 | 
            +
                  $stderr.puts(*args) if debugging?
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
                
         | 
| 51 | 
            +
                def debugging?
         | 
| 52 | 
            +
                  ENV['PEBBLES_DEBUG']
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
                
         | 
| 55 | 
            +
                def ask
         | 
| 56 | 
            +
                  $stdin.gets.to_s.strip
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
                
         | 
| 59 | 
            +
                def error(message, report=false)
         | 
| 60 | 
            +
                  if Pebbles::Helpers.error_with_failure
         | 
| 61 | 
            +
                    display("failed")
         | 
| 62 | 
            +
                    Pebbles::Helpers.error_with_failure = false
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
                  $stderr.puts(format_with_bang(message))
         | 
| 65 | 
            +
                  exit(1)
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
                
         | 
| 68 | 
            +
                def self.error_with_failure
         | 
| 69 | 
            +
                  @@error_with_failure ||= false
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                def self.error_with_failure=(new_error_with_failure)
         | 
| 73 | 
            +
                  @@error_with_failure = new_error_with_failure
         | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
                
         | 
| 76 | 
            +
                def hputs(string='')
         | 
| 77 | 
            +
                  Kernel.puts(string)
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
                
         | 
| 80 | 
            +
                def longest(items)
         | 
| 81 | 
            +
                  items.map { |i| i.to_s.length }.sort.last
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
                
         | 
| 84 | 
            +
                def has_git?
         | 
| 85 | 
            +
                  %x{ git --version }
         | 
| 86 | 
            +
                  $?.success?
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                def git(args)
         | 
| 90 | 
            +
                  return "" unless has_git?
         | 
| 91 | 
            +
                  flattened_args = [args].flatten.compact.join(" ")
         | 
| 92 | 
            +
                  %x{ git #{flattened_args} 2>&1 }.strip
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
                
         | 
| 95 | 
            +
                def has_git_remote?(remote)
         | 
| 96 | 
            +
                  git('remote').split("\n").include?(remote) && $?.success?
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                def create_git_remote(remote, url)
         | 
| 100 | 
            +
                  return if has_git_remote? remote
         | 
| 101 | 
            +
                  git "remote add #{remote} #{url}"
         | 
| 102 | 
            +
                  display "Git remote #{remote} added" if $?.success?
         | 
| 103 | 
            +
                end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                def update_git_remote(remote, url)
         | 
| 106 | 
            +
                  return unless has_git_remote? remote
         | 
| 107 | 
            +
                  git "remote set-url #{remote} #{url}"
         | 
| 108 | 
            +
                  display "Git remote #{remote} updated" if $?.success?
         | 
| 109 | 
            +
                end
         | 
| 110 | 
            +
                
         | 
| 111 | 
            +
                @@kb = 1024
         | 
| 112 | 
            +
                @@mb = 1024 * @@kb
         | 
| 113 | 
            +
                @@gb = 1024 * @@mb
         | 
| 114 | 
            +
                def format_bytes(amount)
         | 
| 115 | 
            +
                  amount = amount.to_i
         | 
| 116 | 
            +
                  return '(empty)' if amount == 0
         | 
| 117 | 
            +
                  return amount if amount < @@kb
         | 
| 118 | 
            +
                  return "#{(amount / @@kb).round}k" if amount < @@mb
         | 
| 119 | 
            +
                  return "#{(amount / @@mb).round}M" if amount < @@gb
         | 
| 120 | 
            +
                  return "#{(amount / @@gb).round}G"
         | 
| 121 | 
            +
                end
         | 
| 122 | 
            +
                
         | 
| 123 | 
            +
                def json_decode(json)
         | 
| 124 | 
            +
                  MultiJson.load(json)
         | 
| 125 | 
            +
                rescue MultiJson::ParseError
         | 
| 126 | 
            +
                  nil
         | 
| 127 | 
            +
                end
         | 
| 128 | 
            +
                
         | 
| 129 | 
            +
                def with_tty(&block)
         | 
| 130 | 
            +
                  return unless $stdin.isatty
         | 
| 131 | 
            +
                  begin
         | 
| 132 | 
            +
                    yield
         | 
| 133 | 
            +
                  rescue
         | 
| 134 | 
            +
                    # fails on windows
         | 
| 135 | 
            +
                  end
         | 
| 136 | 
            +
                end
         | 
| 137 | 
            +
                
         | 
| 138 | 
            +
                def action(message, options={})
         | 
| 139 | 
            +
                  message = "#{message} in organization #{org}" if options[:org]
         | 
| 140 | 
            +
                  display("#{message}... ", false)
         | 
| 141 | 
            +
                  Pebbles::Helpers.error_with_failure = true
         | 
| 142 | 
            +
                  ret = yield
         | 
| 143 | 
            +
                  Pebbles::Helpers.error_with_failure = false
         | 
| 144 | 
            +
                  display((options[:success] || "done"), false)
         | 
| 145 | 
            +
                  if @status
         | 
| 146 | 
            +
                    display(", #{@status}", false)
         | 
| 147 | 
            +
                    @status = nil
         | 
| 148 | 
            +
                  end
         | 
| 149 | 
            +
                  display
         | 
| 150 | 
            +
                  ret
         | 
| 151 | 
            +
                end
         | 
| 152 | 
            +
                
         | 
| 153 | 
            +
                def confirm_command(app_to_confirm = app, message=nil)
         | 
| 154 | 
            +
                  if confirmed_app = Pebbles::Command.current_options[:confirm]
         | 
| 155 | 
            +
                    unless confirmed_app == app_to_confirm
         | 
| 156 | 
            +
                      raise(Pebbles::Command::CommandFailed, "Confirmed app #{confirmed_app} did not match the selected app #{app_to_confirm}.")
         | 
| 157 | 
            +
                    end
         | 
| 158 | 
            +
                    return true
         | 
| 159 | 
            +
                  else
         | 
| 160 | 
            +
                    display
         | 
| 161 | 
            +
                    message ||= "WARNING: Destructive Action\nThis command will affect the app: #{app_to_confirm}"
         | 
| 162 | 
            +
                    message << "\nTo proceed, type \"#{app_to_confirm}\" or re-run this command with --confirm #{app_to_confirm}"
         | 
| 163 | 
            +
                    output_with_bang(message)
         | 
| 164 | 
            +
                    display
         | 
| 165 | 
            +
                    display "> ", false
         | 
| 166 | 
            +
                    if ask.downcase != app_to_confirm
         | 
| 167 | 
            +
                      error("Confirmation did not match #{app_to_confirm}. Aborted.")
         | 
| 168 | 
            +
                    else
         | 
| 169 | 
            +
                      true
         | 
| 170 | 
            +
                    end
         | 
| 171 | 
            +
                  end
         | 
| 172 | 
            +
                end
         | 
| 173 | 
            +
                
         | 
| 174 | 
            +
                def format_error(error, message='Pebblescape client internal error.')
         | 
| 175 | 
            +
                  formatted_error = []
         | 
| 176 | 
            +
                  formatted_error << " !    #{message}"
         | 
| 177 | 
            +
                  formatted_error << ''
         | 
| 178 | 
            +
                  formatted_error << "    Error:       #{error.message} (#{error.class})"
         | 
| 179 | 
            +
                  command = ARGV.map do |arg|
         | 
| 180 | 
            +
                    if arg.include?(' ')
         | 
| 181 | 
            +
                      arg = %{"#{arg}"}
         | 
| 182 | 
            +
                    else
         | 
| 183 | 
            +
                      arg
         | 
| 184 | 
            +
                    end
         | 
| 185 | 
            +
                  end.join(' ')
         | 
| 186 | 
            +
                  formatted_error << "    Command:     pebbles #{command}"
         | 
| 187 | 
            +
                  require 'pebbles/auth'
         | 
| 188 | 
            +
                  unless Pebbles::Auth.host == Pebbles::Auth.default_host
         | 
| 189 | 
            +
                    formatted_error << "    Host:        #{Pebbles::Auth.host}"
         | 
| 190 | 
            +
                  end
         | 
| 191 | 
            +
                  if http_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
         | 
| 192 | 
            +
                    formatted_error << "    HTTP Proxy:  #{http_proxy}"
         | 
| 193 | 
            +
                  end
         | 
| 194 | 
            +
                  if https_proxy = ENV['https_proxy'] || ENV['HTTPS_PROXY']
         | 
| 195 | 
            +
                    formatted_error << "    HTTPS Proxy: #{https_proxy}"
         | 
| 196 | 
            +
                  end
         | 
| 197 | 
            +
                  formatted_error << "    Version:     #{Pebbles.user_agent}"
         | 
| 198 | 
            +
                  formatted_error << "\n"
         | 
| 199 | 
            +
                  formatted_error.join("\n")
         | 
| 200 | 
            +
                end
         | 
| 201 | 
            +
                
         | 
| 202 | 
            +
                def styled_header(header)
         | 
| 203 | 
            +
                  display("=== #{header}")
         | 
| 204 | 
            +
                end
         | 
| 205 | 
            +
                
         | 
| 206 | 
            +
                # produces a printf formatter line for an array of items
         | 
| 207 | 
            +
                # if an individual line item is an array, it will create columns
         | 
| 208 | 
            +
                # that are lined-up
         | 
| 209 | 
            +
                #
         | 
| 210 | 
            +
                # line_formatter(["foo", "barbaz"])                 # => "%-6s"
         | 
| 211 | 
            +
                # line_formatter(["foo", "barbaz"], ["bar", "qux"]) # => "%-3s   %-6s"
         | 
| 212 | 
            +
                #
         | 
| 213 | 
            +
                def line_formatter(array)
         | 
| 214 | 
            +
                  if array.any? {|item| item.is_a?(Array)}
         | 
| 215 | 
            +
                    cols = []
         | 
| 216 | 
            +
                    array.each do |item|
         | 
| 217 | 
            +
                      if item.is_a?(Array)
         | 
| 218 | 
            +
                        item.each_with_index { |val,idx| cols[idx] = [cols[idx]||0, (val || '').length].max }
         | 
| 219 | 
            +
                      end
         | 
| 220 | 
            +
                    end
         | 
| 221 | 
            +
                    cols.map { |col| "%-#{col}s" }.join("  ")
         | 
| 222 | 
            +
                  else
         | 
| 223 | 
            +
                    "%s"
         | 
| 224 | 
            +
                  end
         | 
| 225 | 
            +
                end
         | 
| 226 | 
            +
             | 
| 227 | 
            +
                def styled_array(array, options={})
         | 
| 228 | 
            +
                  fmt = line_formatter(array)
         | 
| 229 | 
            +
                  array = array.sort unless options[:sort] == false
         | 
| 230 | 
            +
                  array.each do |element|
         | 
| 231 | 
            +
                    display((fmt % element).rstrip)
         | 
| 232 | 
            +
                  end
         | 
| 233 | 
            +
                  display
         | 
| 234 | 
            +
                end
         | 
| 235 | 
            +
                
         | 
| 236 | 
            +
                def styled_hash(hash, keys=nil)
         | 
| 237 | 
            +
                  max_key_length = hash.keys.map {|key| key.to_s.length}.max + 2
         | 
| 238 | 
            +
                  keys ||= hash.keys.sort {|x,y| x.to_s <=> y.to_s}
         | 
| 239 | 
            +
                  keys.each do |key|
         | 
| 240 | 
            +
                    case value = hash[key]
         | 
| 241 | 
            +
                    when Array
         | 
| 242 | 
            +
                      if value.empty?
         | 
| 243 | 
            +
                        next
         | 
| 244 | 
            +
                      else
         | 
| 245 | 
            +
                        elements = value.sort {|x,y| x.to_s <=> y.to_s}
         | 
| 246 | 
            +
                        display("#{key}: ".ljust(max_key_length), false)
         | 
| 247 | 
            +
                        display(elements[0])
         | 
| 248 | 
            +
                        elements[1..-1].each do |element|
         | 
| 249 | 
            +
                          display("#{' ' * max_key_length}#{element}")
         | 
| 250 | 
            +
                        end
         | 
| 251 | 
            +
                        if elements.length > 1
         | 
| 252 | 
            +
                          display
         | 
| 253 | 
            +
                        end
         | 
| 254 | 
            +
                      end
         | 
| 255 | 
            +
                    when nil
         | 
| 256 | 
            +
                      next
         | 
| 257 | 
            +
                    else
         | 
| 258 | 
            +
                      display("#{key}: ".ljust(max_key_length), false)
         | 
| 259 | 
            +
                      display(value)
         | 
| 260 | 
            +
                    end
         | 
| 261 | 
            +
                  end
         | 
| 262 | 
            +
                end
         | 
| 263 | 
            +
                
         | 
| 264 | 
            +
                def flatten_hash(hash, key)
         | 
| 265 | 
            +
                  hash[key].each do |k, v|
         | 
| 266 | 
            +
                    hash["#{key}_#{k}"] = v
         | 
| 267 | 
            +
                  end
         | 
| 268 | 
            +
                  
         | 
| 269 | 
            +
                  hash.delete(key)
         | 
| 270 | 
            +
                end
         | 
| 271 | 
            +
                
         | 
| 272 | 
            +
                def styled_error(error, message='Pebblescape client internal error.')
         | 
| 273 | 
            +
                  if Pebbles::Helpers.error_with_failure
         | 
| 274 | 
            +
                    display("failed")
         | 
| 275 | 
            +
                    Pebbles::Helpers.error_with_failure = false
         | 
| 276 | 
            +
                  end
         | 
| 277 | 
            +
                  $stderr.puts(format_error(error, message))
         | 
| 278 | 
            +
                end
         | 
| 279 | 
            +
                
         | 
| 280 | 
            +
                def has_http_git_entry_in_netrc
         | 
| 281 | 
            +
                  Auth.netrc && Auth.netrc[Auth.http_git_host]
         | 
| 282 | 
            +
                end
         | 
| 283 | 
            +
              end
         | 
| 284 | 
            +
            end
         |