toolshed 1.0.5 → 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 97f1edaf94718d33dd532dd486c88a9be34763ba
4
- data.tar.gz: f916422d538d77d894a4474735fbd729e46e8030
3
+ metadata.gz: 215029306670114b410a3c8e153734c8b6d9ce3d
4
+ data.tar.gz: 27c11d5351604e6dcb24c3bd5f3c00a98f69c6fd
5
5
  SHA512:
6
- metadata.gz: b31baee2db1a9feabfbd79455cd3946c91d2368ea0c53485f58c1bfb2d79e4e46bdf28188c90377695aa9f3ab3f2b8cace9b9954855a9e3ab0c0f2f323c45d20
7
- data.tar.gz: ee8fd8db4073acbe54ac95f04c14641c618d106e3c93e011e912c57728311113e5787367a9db4341c25fbc903a761958881c2a8ac780ab3b5a271fb8b19cc67a
6
+ metadata.gz: f8fa73f9315ebae7f0d46d0cdb17a526a0593abf6b871f6a8c24857ca336e372efb8ce525f1e13f45013c50abbc814a1c739923e094a5507de940b401d13ffc7
7
+ data.tar.gz: 9251f783275043a902ba2b889a0e11e1f982d9f72c18faf2976be8f65f470069651022efa9f478bd779ac479fcff1f0745e7b6bc0e4ea7c60f20eaf927a11dff
data/lib/toolshed/base.rb CHANGED
@@ -11,7 +11,7 @@ module Toolshed
11
11
  result = {}
12
12
  Open3.popen3(command) do |stdin, stdout, stderr, wait_thr|
13
13
  begin
14
- Timeout.timeout(seconds) do
14
+ ::Timeout.timeout(seconds) do
15
15
  stdin.close # make sure the subprocess is done
16
16
 
17
17
  a_stdout = []
@@ -27,7 +27,7 @@ module Toolshed
27
27
  exit_status = wait_thr.value # Process::Status object returned.
28
28
  result.merge!(stdout: a_stdout, stderr: a_stderr, all: all, process_status: exit_status) # rubocop:disable Metrics/LineLength
29
29
  end
30
- rescue Timeout::Error
30
+ rescue ::Timeout::Error
31
31
  Process.kill('KILL', wait_thr.pid)
32
32
  Toolshed.logger.fatal "Unable to perform the '#{command}' command in the allowed amount of time of #{seconds} seconds. Exiting." # rubocop:disable Metrics/LineLength
33
33
  Toolshed.die
data/lib/toolshed/cli.rb CHANGED
@@ -19,9 +19,8 @@ module Toolshed
19
19
  end
20
20
 
21
21
  def load_config(command_class)
22
- Toolshed::Client.load_credentials
23
22
  Toolshed.add_file_log_source(command_class.class.name)
24
- Toolshed.logger.info "Credentials loaded from #{File.absolute_path(Toolshed::Client.instance.credentials_loaded_from)}" # rubocop:disable Metrics/LineLength
23
+ Toolshed.logger.info "Credentials loaded from #{File.absolute_path(Toolshed::Client.instance.toolshedrc_path)}" # rubocop:disable Metrics/LineLength
25
24
  rescue => e
26
25
  Toolshed.logger.fatal "Error loading your credentials: #{e.message}"
27
26
  end
@@ -1,91 +1,55 @@
1
- require 'singleton'
1
+ require 'toolshed/error'
2
+ require 'toolshed/hash'
2
3
  require 'toolshed/version'
4
+
5
+ require 'erb'
6
+ require 'singleton'
3
7
  require 'yaml'
4
8
 
5
9
  module Toolshed
6
10
  # This is responsible for loading .toolshedrc file
7
- class Client
11
+ class Client < Hash
8
12
  include Singleton
9
13
 
10
- attr_accessor :debug,
11
- :git_quiet,
12
- :use_defaults,
13
- :github_username,
14
- :github_password,
15
- :github_token,
16
- :use_defaults,
17
- :pull_from_repository_user,
18
- :pull_from_repository_name,
19
- :pull_from_remote_name,
20
- :push_to_repository_user,
21
- :push_to_remote_name,
22
- :use_git_submodules,
23
- :git_tool,
24
- :pivotal_tracker_username,
25
- :pivotal_tracker_password,
26
- :default_pivotal_tracker_project_id,
27
- :ticket_tracker_username,
28
- :ticket_tracker_password,
29
- :default_ticket_tracker_project,
30
- :ticket_tracking_tool,
31
- :ticket_tracker_owner,
32
- :ticket_status_for_complete,
33
- :default_pull_request_title_format,
34
- :time_tracking_tool,
35
- :time_tracking_username,
36
- :time_tracking_password,
37
- :time_tracking_owner,
38
- :time_tracking_default_project_id,
39
- :load_credentials_if_necessary,
40
- :log_path,
41
- :credentials_loaded_from
42
-
43
14
  GITHUB_BASE_API_URL = 'https://api.github.com/'
44
15
  PIVOTAL_TRACKER_BASE_API_URL = 'https://www.pivotaltracker.com/services/v5/'
45
16
 
46
- class << self
47
- def config_path
48
- ENV['TOOLSHED_CONFIG'] || '~/.toolshedrc'
49
- end
17
+ attr_reader :struct
50
18
 
51
- def load_credentials # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
52
- credentials = Client.read_credenials
53
- instance.github_username ||= credentials['github_username']
54
- instance.github_password ||= credentials['github_password']
55
- instance.github_token ||= credentials['github_token']
56
- instance.pivotal_tracker_username ||= credentials['pivotal_tracker_username']
57
- instance.pivotal_tracker_password ||= credentials['pivotal_tracker_password']
58
- instance.default_pivotal_tracker_project_id ||= credentials['default_pivotal_tracker_project_id'] # rubocop:disable Metrics/LineLength
59
- instance.ticket_tracker_username ||= credentials['ticket_tracker_username']
60
- instance.ticket_tracker_password ||= credentials['ticket_tracker_password']
61
- instance.ticket_tracker_owner ||= credentials['ticket_tracker_owner']
62
- instance.ticket_status_for_complete ||= credentials['ticket_status_for_complete']
63
- instance.default_ticket_tracker_project ||= credentials['default_ticket_tracker_project'] # rubocop:disable Metrics/LineLength
64
- instance.pull_from_remote_name ||= credentials['pull_from_remote_name']
65
- instance.pull_from_repository_user ||= credentials['pull_from_repository_user']
66
- instance.pull_from_repository_name ||= credentials['pull_from_repository_name']
67
- instance.push_to_repository_user ||= credentials['push_to_repository_user']
68
- instance.push_to_remote_name ||= credentials['push_to_remote_name']
69
- instance.ticket_tracking_tool ||= credentials['ticket_tracking_tool'] # rubocop:disable Metrics/LineLength
70
- instance.use_git_submodules ||= credentials['use_git_submodules']
71
- instance.git_tool ||= credentials['git_tool']
72
- instance.time_tracking_username ||= credentials['time_tracking_username']
73
- instance.time_tracking_password ||= credentials['time_tracking_password']
74
- instance.time_tracking_owner ||= credentials['time_tracking_owner']
75
- instance.time_tracking_default_project_id ||= credentials['time_tracking_default_project_id'] # rubocop:disable Metrics/LineLength
76
- instance.time_tracking_tool ||= credentials['time_tracking_tool']
77
- instance.git_quiet ||= (credentials['git_quiet']) ? '&> /dev/null' : ''
78
- instance.use_defaults ||= credentials['use_defaults']
79
- instance.default_pull_request_title_format ||= credentials['default_pull_request_title_format'] # rubocop:disable Metrics/LineLength
80
- instance.log_path ||= credentials['log_path']
81
- instance.credentials_loaded_from ||= load_credentials_from
82
- end
19
+ def initialize
20
+ load_toolshedrc_configuration
21
+ @struct = to_ostruct
22
+ end
83
23
 
84
- def read_credenials
85
- YAML.load_file(File.expand_path(load_credentials_from))
24
+ def load_toolshedrc_configuration
25
+ toolshedrc_configurations = YAML.load(ERB.new(File.read(toolshedrc_path)).result)
26
+ raise CorruptFileException, 'Toolshedrc file is not configured properly.' unless toolshedrc_configurations.is_a?(Hash)
27
+ self.merge!(toolshedrc_configurations)
28
+ end
29
+
30
+ def method_missing(*args)
31
+ begin
32
+ if args.first.to_s.end_with?('=')
33
+ val = args.last
34
+ my_h = self
35
+ args.each_with_index do |arg, index|
36
+ arg = arg.to_s.gsub('=', '')
37
+ next_arg_val = args[index + 1].to_s.gsub('=', '')
38
+ my_h = my_h[arg] unless next_arg_val == val.to_s
39
+ my_h[arg] = val if next_arg_val == val.to_s && !my_h[arg].nil?
40
+ my_h.merge!(arg => val) if next_arg_val == val.to_s && my_h[arg].nil?
41
+ end
42
+ @struct = to_ostruct
43
+ else
44
+ struct.send(args.first)
45
+ end
46
+ rescue NoMethodError => e
47
+ Toolshed.die(e.message)
86
48
  end
49
+ end
87
50
 
88
- def load_credentials_from
51
+ def toolshedrc_path
52
+ @toolshedrc_path ||= begin
89
53
  dir = Dir.pwd
90
54
  while File.expand_path(dir) != '/'
91
55
  unless File.exist?("#{dir}/.toolshedrc")
@@ -95,7 +59,6 @@ module Toolshed
95
59
  credentials_loaded_from = "#{dir}/.toolshedrc"
96
60
  break
97
61
  end
98
-
99
62
  credentials_loaded_from
100
63
  end
101
64
  end
@@ -13,7 +13,7 @@ module Toolshed
13
13
  options_parser = OptionParser.new do |opts|
14
14
  opts.banner = cli_options[:banner] if cli_options[:banner]
15
15
  cli_options[:options].each do |option_name, option_variables|
16
- letter_map = ('a'..'z').map { |letter| letter }
16
+ letter_map = ('a'..'z').map { |letter| letter }.delete_if { |letter| letter == 'h' }
17
17
  short_on = (option_variables[:short_on]) ? option_variables[:short_on] : letter_map[rand(letter_map.length)] # rubocop:disable Metrics/LineLength
18
18
  on = (option_variables[:on]) ? option_variables[:on] : "--#{option_name.to_s.split('_').join('-')} [ARG]" # rubocop:disable Metrics/LineLength
19
19
  opts.on(short_on, on) do |opt|
@@ -21,6 +21,10 @@ module Toolshed
21
21
  options.merge!(option_name => value)
22
22
  end
23
23
  end
24
+ opts.on_tail("-h", "--help", "Show this message") do
25
+ puts opts
26
+ exit
27
+ end
24
28
  end
25
29
  options_parser.order! if options_parser
26
30
  begin
@@ -0,0 +1,56 @@
1
+ require 'toolshed/commands/base'
2
+ require 'toolshed/databases/mysql/backup'
3
+
4
+ module Toolshed
5
+ module Commands
6
+ module Mysql
7
+ # Responsible for handing backup of mysql database
8
+ class Backup < Toolshed::Commands::Base
9
+ def self.cli_options # rubocop:disable MethodLength
10
+ {
11
+ banner: 'Usage: mysql backup [options]',
12
+ options: {
13
+ local_host: {
14
+ short_on: '-l'
15
+ },
16
+ path: {
17
+ short_on: '-p'
18
+ },
19
+ username: {
20
+ short_on: '-u'
21
+ },
22
+ password: {
23
+ short_on: '-d'
24
+ },
25
+ name: {
26
+ short_on: '-n'
27
+ },
28
+ wait_time: {
29
+ short_on: '-w'
30
+ }
31
+ }
32
+ }
33
+ end
34
+
35
+ def execute(_args, options = nil)
36
+ options = options_with_defaults(options)
37
+ Toolshed.logger.info ''
38
+ Toolshed::Databases::Mysql::Backup.new(options).execute
39
+ Toolshed.logger.info ''
40
+ Toolshed.die
41
+ end
42
+
43
+ private
44
+
45
+ def options_with_defaults(options = nil)
46
+ options ||= {}
47
+ options[:local_host] ||= 'localhost'
48
+ options[:path] ||= read_user_input("Storage Path (/tmp/test/#{Time.now.utc.getlocal.strftime('%Y%m%d')}.sql) ?", required: true)
49
+ options[:username] ||= read_user_input('Username?', required: true)
50
+ options[:name] ||= read_user_input('Database Name?', required: true)
51
+ options
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -36,6 +36,9 @@ module Toolshed
36
36
  },
37
37
  verbose_output: {
38
38
  short_on: '-v'
39
+ },
40
+ timeout_period: {
41
+ short_on: '-t'
39
42
  }
40
43
  }
41
44
  }
@@ -8,17 +8,17 @@ module Toolshed
8
8
  end
9
9
 
10
10
  def execute(args, options = {})
11
- ticket_tracker_class = Object.const_get("Toolshed::TicketTracking::#{Toolshed::Client.ticket_tracking_tool.camel_case}")
11
+ ticket_tracker_class = Object.const_get("Toolshed::TicketTracking::#{Toolshed::Client.instance.ticket_tracking_tool.camel_case}")
12
12
 
13
13
  use_project_id = Object.const_get("#{ticket_tracker_class}::USE_PROJECT_ID") rescue false
14
14
  if use_project_id
15
- ticket_tracker_project_id = read_user_input_project("Project ID (Default: #{Toolshed::Client.default_pivotal_tracker_project_id}):", options.merge!({ default: Toolshed::Client.default_pivotal_tracker_project_id }))
15
+ ticket_tracker_project_id = read_user_input_project("Project ID (Default: #{Toolshed::Client.instance.default_pivotal_tracker_project_id}):", options.merge!({ default: Toolshed::Client.instance.default_pivotal_tracker_project_id }))
16
16
  options.merge!({ project_id: ticket_tracker_project_id })
17
17
  end
18
18
 
19
19
  use_project_name = Object.const_get("#{ticket_tracker_class}::USE_PROJECT_NAME") rescue false
20
20
  if use_project_name
21
- ticket_tracker_project_name = read_user_input_project("Project Name (Default: #{Toolshed::Client.default_ticket_tracker_project}):", options.merge!({ default: Toolshed::Client.default_ticket_tracker_project }))
21
+ ticket_tracker_project_name = read_user_input_project("Project Name (Default: #{Toolshed::Client.instance.default_ticket_tracker_project}):", options.merge!({ default: Toolshed::Client.instance.default_ticket_tracker_project }))
22
22
  options.merge!({ project: ticket_tracker_project_name })
23
23
  end
24
24
 
@@ -0,0 +1,39 @@
1
+ require 'toolshed/error'
2
+ require 'toolshed/password'
3
+
4
+ module Toolshed
5
+ module Databases
6
+ module Mysql
7
+ class Backup
8
+ include Toolshed::Password
9
+
10
+ attr_reader :local_host, :name, :path, :password, :username, :wait_time
11
+
12
+ def initialize(options = nil)
13
+ options ||= {}
14
+ @local_host = options[:local_host]
15
+ @name = options[:name]
16
+ @path = options[:path]
17
+ @password = options[:password]
18
+ @username = options[:username]
19
+ @wait_time = options[:wait_time] || 120
20
+ end
21
+
22
+ def execute
23
+ raise TypeError, "Wait time passed in is not a number #{wait_time}" unless wait_time.is_a?(Fixnum)
24
+ Toolshed.logger.info "Starting execution of mysqldump -h #{local_host} -u #{username} #{hidden_password_param} #{name} > #{path}."
25
+ Toolshed::Base.wait_for_command("mysqldump -h #{local_host} -u #{username} #{password_param} #{name} > #{path}", wait_time)
26
+ Toolshed.logger.info 'mysqldump has completed.'
27
+ end
28
+
29
+ def password_param
30
+ password.nil? || password.empty? ? '' : "-p #{password_from_config(password)}"
31
+ end
32
+
33
+ def hidden_password_param
34
+ password_param.empty? ? '' : '-p *******'
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -21,22 +21,22 @@ class EntryPoint
21
21
  # @TODO - clean this up as it should really be part of the command it's being used in not globally.
22
22
  global = OptionParser.new do |opts|
23
23
  opts.on("-u", "--github-username [ARG]") do |username|
24
- Toolshed::Client.github_username = username
24
+ Toolshed::Client.instance.github_username = username
25
25
  end
26
26
  opts.on("-p", "--github-password [ARG]") do |password|
27
- Toolshed::Client.github_password = password
27
+ Toolshed::Client.instance.github_password = password
28
28
  end
29
29
  opts.on("-t", "--github-token [ARG]") do |token|
30
- Toolshed::Client.github_token = token
30
+ Toolshed::Client.instance.github_token = token
31
31
  end
32
32
  opts.on("-u", "--pivotal-tracker-username [ARG]") do |username|
33
- Toolshed::Client.pivotal_tracker_username = username
33
+ Toolshed::Client.instance.pivotal_tracker_username = username
34
34
  end
35
35
  opts.on("-p", "--pivotal-tracker-password [ARG]") do |password|
36
- Toolshed::Client.pivotal_tracker_password = password
36
+ Toolshed::Client.instance.pivotal_tracker_password = password
37
37
  end
38
38
  opts.on("-d", "--debug [ARG]") do
39
- Toolshed::Client.debug = true
39
+ Toolshed::Client.instance.debug = true
40
40
  end
41
41
  opts.on('-v', '--version', 'Version') do
42
42
  Toolshed::Version.banner
@@ -108,9 +108,11 @@ Available Commands:
108
108
  toolshed delete_branch
109
109
  toolshed get_daily_time_update
110
110
  toolshed list_branches
111
+ toolshed mysql backup
111
112
  toolshed push_branch
112
113
  toolshed rename_branch
113
114
  toolshed scp download
115
+ toolshed scp upload
114
116
  toolshed ssh
115
117
  toolshed ticket_information
116
118
  toolshed update_pivotal_tracker_story_status
@@ -1,13 +1,8 @@
1
1
  module Toolshed
2
- class Error < StandardError
3
- end
4
-
5
- class RecordExists < Error
6
- end
7
-
8
- class RecordNotFoundError < Error
9
- end
10
-
11
- class AuthenticationFailed < Error
12
- end
2
+ class Error < StandardError; end
3
+ class RecordExists < Error; end
4
+ class RecordNotFoundError < Error; end
5
+ class AuthenticationFailed < Error; end
6
+ class SSHResponseException < Error; end
7
+ class CorruptFileException < StandardError; end
13
8
  end
@@ -0,0 +1,36 @@
1
+ require 'ostruct'
2
+
3
+ class Hash
4
+ # Recursively converts a <tt>Hash</tt> and all nested <tt>Hash</tt>es to
5
+ # <tt>OpenStruct</tt>s. Especially useful for parsing YAML.
6
+ # yaml=<<EOY
7
+ # subject: Programming Languages
8
+ # languages:
9
+ # - name : Ruby
10
+ # creator : Matz
11
+ # - name : Python
12
+ # creator : Guido van Rossum
13
+ # - name : Perl
14
+ # creator : Larry Wall
15
+ # EOY
16
+ # struct = YAML.load(yaml).to_ostruct
17
+ # struct.subject
18
+ # #=> "Programming Languages"
19
+ # struct.languages.first
20
+ # #=> #<OpenStruct name="Ruby", creator="Matz">
21
+ # struct.languages.first.creator
22
+ # #=> "Matz"
23
+ def to_ostruct
24
+ arr = map do |k, v|
25
+ case v
26
+ when Hash
27
+ [k, v.to_ostruct]
28
+ when Array
29
+ [k, v.map { |el| el.respond_to?(:to_ostruct) ? el.to_ostruct : el }]
30
+ else
31
+ [k, v]
32
+ end
33
+ end
34
+ OpenStruct.new(Hash[arr])
35
+ end
36
+ end
@@ -1,23 +1,23 @@
1
+ require 'toolshed'
1
2
  require 'toolshed/client'
2
3
 
3
4
  module Toolshed
5
+ # Password module looks up password from configuration file if its found
4
6
  module Password
5
7
  def password_from_config(password)
6
8
  return '' if password.nil? || password.empty?
7
9
 
8
- begin
9
- credentials = Toolshed::Client.read_credenials
10
- password_parts = password.split(':')
11
- password_parts.each do |password_part|
12
- return password if credentials[password_part].nil?
13
- credentials = credentials[password_part]
14
- end
15
- rescue => e
16
- Toolshed::Logger.instance.fatal e.message
17
- Toolshed::Logger.instance.fatal e.inspect
18
- return password
10
+ translated_password = Toolshed.configuration
11
+ password_parts = password.split(':')
12
+ password_parts.each do |password_part|
13
+ return password if translated_password[password_part].nil?
14
+ translated_password = translated_password[password_part]
19
15
  end
20
- credentials
16
+ return translated_password
17
+ rescue => e
18
+ Toolshed::Logger.instance.fatal e.message
19
+ Toolshed::Logger.instance.fatal e.inspect
20
+ return password
21
21
  end
22
22
  end
23
23
  end
@@ -1,4 +1,6 @@
1
+ require 'toolshed/error'
1
2
  require 'toolshed/password'
3
+ require 'toolshed/timeout'
2
4
 
3
5
  require 'net/ssh'
4
6
 
@@ -9,7 +11,7 @@ module Toolshed
9
11
  include Toolshed::Password
10
12
 
11
13
  attr_accessor :channel, :commands, :data, :host, :keys, :password, :ssh_options, :sudo_password, :user # rubocop:disable LineLength
12
- attr_reader :silent
14
+ attr_reader :silent, :timeout
13
15
 
14
16
  def initialize(options = nil) # rubocop:disable AbcSize, CyclomaticComplexity, PerceivedComplexity, LineLength
15
17
  options ||= {}
@@ -23,18 +25,27 @@ module Toolshed
23
25
  @password = options[:password] || ''
24
26
  @data = []
25
27
  @silent = options[:silent] || false
28
+ timeout_period = options[:timeout_period].to_i || 30
29
+ @timeout = Toolshed::Timeout.new(timeout_period: timeout_period)
26
30
 
27
31
  set_ssh_options
28
32
  end
29
33
 
30
34
  def execute
31
- Net::SSH.start(host, user, ssh_options) do |ssh|
32
- ssh.open_channel do |channel|
33
- self.channel = channel
34
- request_pty
35
- run_commands
35
+ begin
36
+ timeout.start do
37
+ Net::SSH.start(host, user, ssh_options) do |ssh|
38
+ ssh.open_channel do |channel|
39
+ self.channel = channel
40
+ request_pty
41
+ run_commands
42
+ end
43
+ ssh.loop
44
+ end
36
45
  end
37
- ssh.loop
46
+ rescue Toolshed::Timeout::Error => e
47
+ Toolshed.logger.fatal e.message
48
+ raise SSHResponseException, "Unable to handle response for #{data.last}"
38
49
  end
39
50
  data
40
51
  end
@@ -55,6 +66,7 @@ module Toolshed
55
66
 
56
67
  def request_pty
57
68
  channel.request_pty do |_ch, success|
69
+ timeout.reset_start_time
58
70
  unless silent
59
71
  message = (success) ? 'Successfully obtained pty' : 'Could not obtain pty'
60
72
  puts message
@@ -70,6 +82,7 @@ module Toolshed
70
82
 
71
83
  def on_extended_data
72
84
  channel.on_extended_data do |_ch, _type, data|
85
+ timeout.reset_start_time
73
86
  puts "stderr: #{data}" unless silent
74
87
  end
75
88
  end
@@ -78,13 +91,13 @@ module Toolshed
78
91
  channel.on_data do |_ch, data|
79
92
  puts "#{data}" unless silent
80
93
  self.data << data
94
+ timeout.reset_start_time
81
95
  send_data(data)
82
96
  end
83
97
  end
84
98
 
85
99
  def send_data(data)
86
100
  send_password_data if data =~ /password/
87
- # send_yes_no_data if data =~ /Do you want to continue \[Y\/n\]?/
88
101
  send_yes_no_data if data =~ %r{Do you want to continue [Y/n]?}
89
102
  end
90
103
 
@@ -24,7 +24,6 @@ module Toolshed
24
24
  end
25
25
 
26
26
  self.token = ::PivotalTracker::Client.token(username, password)
27
-
28
27
  self.project_id = (options[:project_id].nil?) ? Toolshed::Client.instance.default_pivotal_tracker_project_id : options[:project_id]
29
28
  @pt_project = ::PivotalTracker::Project.find(self.project_id)
30
29
  self.story = @pt_project.stories.find(options[:ticket_id])
@@ -0,0 +1,95 @@
1
+ require 'time'
2
+
3
+ module Toolshed
4
+ THIS_FILE = /\A#{Regexp.quote(__FILE__)}:/o
5
+ CALLER_OFFSET = ((c = caller[0]) && THIS_FILE =~ c) ? 1 : 0
6
+ private_constant :THIS_FILE, :CALLER_OFFSET
7
+
8
+ # https://github.com/ruby/ruby/blob/trunk/lib/timeout.rb
9
+ # This code had to be modified so the timeout could be extended instead of just being a fixnum.
10
+ # This is import for the SSH client as we want it to keep running for an unlimited time period as
11
+ # long as we are getting output from the client. When that stops then the timeout needs to kick in
12
+ # just in case the server is no longer responding or a connection got lost. Too bad ruby core doesn't
13
+ # already support something like this.
14
+ class Timeout
15
+ attr_accessor :start_time
16
+ attr_reader :timeout_period
17
+
18
+ def initialize(options = nil)
19
+ options ||= {}
20
+ @timeout_period = options[:timeout_period] || 30
21
+ @start_time = options[:start_time] || Time.now.utc.to_i
22
+ end
23
+
24
+ def reset_start_time
25
+ @start_time = Time.now.utc.to_i
26
+ end
27
+
28
+ def start(klass = nil) #:yield: +sec+
29
+ return yield(timeout_period) if timeout_period == nil or timeout_period.zero?
30
+ message = "execution expired in #{timeout_period} seconds".freeze
31
+ e = Error
32
+ bl = proc do |exception|
33
+ begin
34
+ x = Thread.current
35
+ y = Thread.start {
36
+ begin
37
+ sleep(1) until timed_out?
38
+ rescue => e
39
+ x.raise e
40
+ else
41
+ x.raise exception, message
42
+ end
43
+ }
44
+ return yield(timeout_period)
45
+ ensure
46
+ if y
47
+ y.kill
48
+ y.join # make sure y is dead.
49
+ end
50
+ end
51
+ end
52
+ if klass
53
+ begin
54
+ bl.call(klass)
55
+ rescue klass => e
56
+ bt = e.backtrace
57
+ end
58
+ else
59
+ bt = Error.catch(message, &bl)
60
+ end
61
+ level = -caller(CALLER_OFFSET).size-2
62
+ while THIS_FILE =~ bt[level]
63
+ bt.delete_at(level)
64
+ end
65
+ raise(e, message, bt)
66
+ end
67
+
68
+ def timed_out?
69
+ Time.now.utc.to_i - start_time > timeout_period
70
+ end
71
+
72
+ # Raised by Timeout#timeout when the block times out.
73
+ class Error < RuntimeError
74
+ attr_reader :thread
75
+
76
+ def self.catch(*args)
77
+ exc = new(*args)
78
+ exc.instance_variable_set(:@thread, Thread.current)
79
+ ::Kernel.catch(exc) {yield exc}
80
+ end
81
+
82
+ def exception(*)
83
+ # TODO: use Fiber.current to see if self can be thrown
84
+ if self.thread == Thread.current
85
+ bt = caller
86
+ begin
87
+ throw(self, bt)
88
+ rescue UncaughtThrowError
89
+ end
90
+ end
91
+ self
92
+ end
93
+ end
94
+ end
95
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Module for toolshed
4
4
  module Toolshed
5
- VERSION = '1.0.5'
5
+ VERSION = '1.0.6'
6
6
 
7
7
  # Display the version information with the toolshed banner
8
8
  class Version
data/lib/toolshed.rb CHANGED
@@ -15,7 +15,7 @@ module Toolshed
15
15
 
16
16
  class << self
17
17
  def add_file_log_source(command_name = '')
18
- log_path = Toolshed::Client.instance.log_path
18
+ log_path = Toolshed.configuration.log_path
19
19
  return if log_path.nil? || log_path.empty?
20
20
 
21
21
  FileUtils.mkdir_p(log_path)
@@ -27,6 +27,10 @@ module Toolshed
27
27
  logger.add_log_source(file)
28
28
  end
29
29
 
30
+ def configuration
31
+ Toolshed::Client.instance
32
+ end
33
+
30
34
  def deprecate(message = nil)
31
35
  message ||= 'You are using deprecated behavior which will be removed from the next major or minor release.' # rubocop:disable Metrics/LineLength
32
36
  warn("DEPRECATION WARNING: #{message}")
data/test/.toolshedrc CHANGED
@@ -1 +1,3 @@
1
1
  server_password: "tester1234"
2
+ use_git_submodules: false
3
+ git_quiet: ''
@@ -103,26 +103,18 @@ class CreatePullRequestTest < Test::Unit::TestCase
103
103
  pivotal_tracker_mock = mock('PivotalTracker::Client')
104
104
  pivotal_tracker_mock.stubs(:id => '1')
105
105
 
106
- PivotalTracker::Project.expects(:find).
107
- with(Toolshed::Client.instance.default_pivotal_tracker_project_id).
108
- returns(pivotal_tracker_mock)
106
+ PivotalTracker::Project.expects(:find).with(Toolshed::Client.instance.default_pivotal_tracker_project_id).returns(pivotal_tracker_mock)
109
107
 
110
108
  # mock up the story information
111
109
  pivotal_tracker_story_mock = mock('PivotalTracker::Story')
112
110
  pivotal_tracker_story_mock.stubs(:url => 'http://www.example.com', :id => '1', :name => "Test Title")
113
111
 
114
- pivotal_tracker_mock.expects(:stories).
115
- returns(pivotal_tracker_story_mock)
112
+ pivotal_tracker_mock.expects(:stories).returns(pivotal_tracker_story_mock)
116
113
 
117
- pivotal_tracker_story_mock.expects(:find).
118
- with('1').
119
- returns(pivotal_tracker_story_mock)
114
+ pivotal_tracker_story_mock.expects(:find).with('1').returns(pivotal_tracker_story_mock)
120
115
 
121
- Toolshed::TicketTracking::PivotalTracker.any_instance.expects(:title).
122
- returns("Sample")
123
-
124
- Toolshed::TicketTracking::PivotalTracker.any_instance.expects(:url).
125
- returns("github.com/pulls/1")
116
+ Toolshed::TicketTracking::PivotalTracker.any_instance.expects(:title).returns("Sample")
117
+ Toolshed::TicketTracking::PivotalTracker.any_instance.expects(:url).returns("github.com/pulls/1")
126
118
 
127
119
  # stub the possible input
128
120
  Toolshed::Commands::CreatePullRequest.any_instance.stubs(:read_user_input_ticket_tracker_ticket_id).returns('1')
@@ -0,0 +1,38 @@
1
+ require 'commands/commands_helper'
2
+ require 'toolshed/commands/mysql/backup'
3
+
4
+ module Test
5
+ module Commands
6
+ module Mysql
7
+ class BackupTest < Test::Unit::TestCase
8
+ def setup
9
+ Toolshed::Logger.instance.instance_variable_set(:@logs, { debug: [], fatal: [], info: [], warn: [] })
10
+ Toolshed.expects(:die).at_least(0).returns('Exiting')
11
+ end
12
+
13
+ def teardown
14
+ Toolshed::Logger.instance.instance_variable_set(:@logs, { debug: [], fatal: [], info: [], warn: [] })
15
+ end
16
+
17
+ def test_execute
18
+ Toolshed::Base.expects(:wait_for_command).returns(true)
19
+
20
+ path = '/tmp/testing/test.sql'
21
+ username = 'test'
22
+ password = 'test'
23
+ name = 'localdb'
24
+
25
+ mysql_backup_command = Toolshed::Commands::Mysql::Backup.new
26
+ mysql_backup_command.execute({}, path: path, username: username, password: password, name: name)
27
+
28
+ assert Toolshed::Logger.instance.logs[:info].include?("Starting execution of mysqldump -h localhost -u #{username} -p ******* #{name} > #{path}.")
29
+ assert Toolshed::Logger.instance.logs[:info].include?('mysqldump has completed.')
30
+ end
31
+
32
+ def test_cli_options
33
+ assert 'Usage: mysql backup [options]', Toolshed::Commands::Mysql::Backup.cli_options[:banner]
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,45 @@
1
+ require 'commands/commands_helper'
2
+ require 'toolshed/databases/mysql/backup'
3
+
4
+ module Test
5
+ module Databases
6
+ module Mysql
7
+ class BackupTest < Test::Unit::TestCase
8
+ def setup
9
+ Toolshed::Logger.instance.instance_variable_set(:@logs, { debug: [], fatal: [], info: [], warn: [] })
10
+ Toolshed.expects(:die).at_least(0).returns('Exiting')
11
+ end
12
+
13
+ def teardown
14
+ Toolshed::Logger.instance.instance_variable_set(:@logs, { debug: [], fatal: [], info: [], warn: [] })
15
+ end
16
+
17
+ def test_execute
18
+ Toolshed::Base.expects(:wait_for_command).returns(true)
19
+
20
+ path = '/tmp/testing/test.sql'
21
+ username = 'test'
22
+ password = 'test'
23
+ name = 'localdb'
24
+
25
+ Toolshed::Databases::Mysql::Backup.new(path: path, username: username, password: password, name: name, local_host: 'localhost').execute
26
+
27
+ assert Toolshed::Logger.instance.logs[:info].include?("Starting execution of mysqldump -h localhost -u #{username} -p ******* #{name} > #{path}.")
28
+ assert Toolshed::Logger.instance.logs[:info].include?('mysqldump has completed.')
29
+ end
30
+
31
+ def test_raises_type_error_if_wait_time_is_not_a_fixnum
32
+ path = '/tmp/testing/test.sql'
33
+ username = 'test'
34
+ password = 'test'
35
+ name = 'localdb'
36
+
37
+ ex = assert_raises TypeError do
38
+ Toolshed::Databases::Mysql::Backup.new(path: path, username: username, password: password, name: name, local_host: 'localhost', wait_time: 'bla').execute
39
+ end
40
+ assert_equal 'Wait time passed in is not a number bla', ex.message
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
data/test/helper.rb CHANGED
@@ -21,9 +21,7 @@ require 'toolshed'
21
21
  require 'toolshed/logger'
22
22
 
23
23
  Test::Unit.at_start do
24
- Toolshed::Client.instance.use_git_submodules = false
25
24
  #Toolshed::Client.git_quiet = '&> /dev/null' unless ENV['RUNNING_ON_CI']
26
- Toolshed::Client.instance.git_quiet = ''
27
25
 
28
26
  I18n.config.enforce_available_locales = true
29
27
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: toolshed
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jake Waller
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-03 00:00:00.000000000 Z
11
+ date: 2015-10-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -349,6 +349,7 @@ files:
349
349
  - lib/toolshed/commands/delete_branch.rb
350
350
  - lib/toolshed/commands/get_daily_time_update.rb
351
351
  - lib/toolshed/commands/list_branches.rb
352
+ - lib/toolshed/commands/mysql/backup.rb
352
353
  - lib/toolshed/commands/push_branch.rb
353
354
  - lib/toolshed/commands/rename_branch.rb
354
355
  - lib/toolshed/commands/scp/download.rb
@@ -358,6 +359,7 @@ files:
358
359
  - lib/toolshed/commands/ticket_information.rb
359
360
  - lib/toolshed/commands/update_pivotal_tracker_story_status.rb
360
361
  - lib/toolshed/commands/update_ticket_status.rb
362
+ - lib/toolshed/databases/mysql/backup.rb
361
363
  - lib/toolshed/entry_point.rb
362
364
  - lib/toolshed/error.rb
363
365
  - lib/toolshed/exceptions.rb
@@ -365,6 +367,7 @@ files:
365
367
  - lib/toolshed/git/branch.rb
366
368
  - lib/toolshed/git/github.rb
367
369
  - lib/toolshed/git/validator.rb
370
+ - lib/toolshed/hash.rb
368
371
  - lib/toolshed/logger.rb
369
372
  - lib/toolshed/password.rb
370
373
  - lib/toolshed/server_administration/scp.rb
@@ -374,6 +377,7 @@ files:
374
377
  - lib/toolshed/ticket_tracking/ticket_tracking.rb
375
378
  - lib/toolshed/time_tracking/harvest.rb
376
379
  - lib/toolshed/time_tracking/time_tracking.rb
380
+ - lib/toolshed/timeout.rb
377
381
  - lib/toolshed/version.rb
378
382
  - test/.toolshedrc
379
383
  - test/commands/checkout_branch_test.rb
@@ -382,11 +386,13 @@ files:
382
386
  - test/commands/create_pull_request_test.rb
383
387
  - test/commands/delete_branch_test.rb
384
388
  - test/commands/get_daily_time_update_test.rb
389
+ - test/commands/mysql/backup_test.rb
385
390
  - test/commands/push_branch_test.rb
386
391
  - test/commands/rename_branch_test.rb
387
392
  - test/commands/scp/download_test.rb
388
393
  - test/commands/scp/upload_test.rb
389
394
  - test/config.rb
395
+ - test/databases/mysql/backup_test.rb
390
396
  - test/git/git_helper.rb
391
397
  - test/git/git_test.rb
392
398
  - test/git/github_test.rb
@@ -430,11 +436,13 @@ test_files:
430
436
  - test/commands/create_pull_request_test.rb
431
437
  - test/commands/delete_branch_test.rb
432
438
  - test/commands/get_daily_time_update_test.rb
439
+ - test/commands/mysql/backup_test.rb
433
440
  - test/commands/push_branch_test.rb
434
441
  - test/commands/rename_branch_test.rb
435
442
  - test/commands/scp/download_test.rb
436
443
  - test/commands/scp/upload_test.rb
437
444
  - test/config.rb
445
+ - test/databases/mysql/backup_test.rb
438
446
  - test/git/git_helper.rb
439
447
  - test/git/git_test.rb
440
448
  - test/git/github_test.rb