toolshed 1.0.5 → 1.0.6
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 +4 -4
- data/lib/toolshed/base.rb +2 -2
- data/lib/toolshed/cli.rb +1 -2
- data/lib/toolshed/client.rb +38 -75
- data/lib/toolshed/commands/base.rb +5 -1
- data/lib/toolshed/commands/mysql/backup.rb +56 -0
- data/lib/toolshed/commands/ssh.rb +3 -0
- data/lib/toolshed/commands/update_ticket_status.rb +3 -3
- data/lib/toolshed/databases/mysql/backup.rb +39 -0
- data/lib/toolshed/entry_point.rb +8 -6
- data/lib/toolshed/error.rb +6 -11
- data/lib/toolshed/hash.rb +36 -0
- data/lib/toolshed/password.rb +12 -12
- data/lib/toolshed/server_administration/ssh.rb +21 -8
- data/lib/toolshed/ticket_tracking/pivotal_tracker.rb +0 -1
- data/lib/toolshed/timeout.rb +95 -0
- data/lib/toolshed/version.rb +1 -1
- data/lib/toolshed.rb +5 -1
- data/test/.toolshedrc +2 -0
- data/test/commands/create_pull_request_test.rb +5 -13
- data/test/commands/mysql/backup_test.rb +38 -0
- data/test/databases/mysql/backup_test.rb +45 -0
- data/test/helper.rb +0 -2
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 215029306670114b410a3c8e153734c8b6d9ce3d
|
4
|
+
data.tar.gz: 27c11d5351604e6dcb24c3bd5f3c00a98f69c6fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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
|
data/lib/toolshed/client.rb
CHANGED
@@ -1,91 +1,55 @@
|
|
1
|
-
require '
|
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
|
-
|
47
|
-
def config_path
|
48
|
-
ENV['TOOLSHED_CONFIG'] || '~/.toolshedrc'
|
49
|
-
end
|
17
|
+
attr_reader :struct
|
50
18
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
85
|
-
|
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
|
-
|
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
|
@@ -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
|
data/lib/toolshed/entry_point.rb
CHANGED
@@ -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
|
data/lib/toolshed/error.rb
CHANGED
@@ -1,13 +1,8 @@
|
|
1
1
|
module Toolshed
|
2
|
-
class Error < StandardError
|
3
|
-
end
|
4
|
-
|
5
|
-
class
|
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
|
data/lib/toolshed/password.rb
CHANGED
@@ -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
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
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
|
data/lib/toolshed/version.rb
CHANGED
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
|
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
@@ -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("
|
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.
|
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-
|
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
|