postgres-clone 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6aa1bdbfbc49b007f45a70183cfcf64f67f74d59
4
+ data.tar.gz: 6b42f1372dec85cb75629a7dae0ff2991e71baf6
5
+ SHA512:
6
+ metadata.gz: 2bbf06fd4cf8f2b4ca18f443d434b3bbf5d8da59d78f228a83269abacf04c6131b41af26e4cba9a5a6f4a3b86bc6e84dc538c89ef8c36082637a5d84e0bf0622
7
+ data.tar.gz: 9a246fda241024a02fcea58c080389057d89a0da1f33ce7bc3a1281e2a6a4bb06bec112405f523b98284d7f7bb94ec19ab110f4d7a3704685b1dfe6d23e850fd
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --order random
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.4
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in postgres-clone.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Josh Rickard
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,73 @@
1
+ [![Build Status](https://travis-ci.org/joshrickard/postgres-clone.svg?branch=master)](https://travis-ci.org/joshrickard/postgres-clone)
2
+
3
+ # PostgresClone
4
+
5
+ A command line utility for cloning Postgres databases.
6
+
7
+
8
+ ## Installation
9
+
10
+ ```ruby
11
+ gem install postgres-clone
12
+ ```
13
+
14
+
15
+ ## Usage
16
+
17
+ After installing the `postgres-clone` gem, you will have access to the `pg-clone` command which facilitates copying Postgres databases.
18
+
19
+ `pg-clone` will ssh into the remote server, perform a `pg_dump`, copy the dump to the target server, and then perform a `pg_restore`.
20
+
21
+
22
+ ### Options
23
+
24
+ TODO:
25
+
26
+
27
+ ### Examples
28
+
29
+ **Remote to Local**
30
+
31
+ ```
32
+ # Dump remote database and restore to local Postgres server
33
+ pg-clone --src-host=db.example.com --src-db=database --dst-host=localhost
34
+ ```
35
+
36
+ **Remote to Remote**
37
+
38
+ ```
39
+ # Dump remote database and restore to a different remote Postgres server
40
+ pg-clone --src-host=db1.example.com --src-db=database --dst-host=db2.example.com
41
+ ```
42
+
43
+ **Local to Local**
44
+
45
+ ```
46
+ # Duplicate local Postgres database
47
+ pg-clone --src-host=local --src-db=database --dst-host=localhost
48
+ ```
49
+
50
+ **Local to Remote**
51
+
52
+ ```
53
+ # Dump local database and restore to a remote database server
54
+ pg-clone --src-host=local --src-db=database --dst-host=db.example.com
55
+ ```
56
+
57
+
58
+ ## TODOS
59
+
60
+ * Test keyless runs
61
+ * Clean up database dumps?
62
+ * Add checks for existing databases
63
+ * Add options for all prompts
64
+ * Check for existing dump on target machines before doing work
65
+
66
+ ## Contributing
67
+
68
+ Bug reports and pull requests are welcome on GitHub at https://github.com/joshrickard/postgres-clone.
69
+
70
+
71
+ ## License
72
+
73
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'postgres-clone'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require 'pry'
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'postgres-clone'
5
+
6
+ Postgres::Clone::Executable.start(ARGV)
@@ -0,0 +1,6 @@
1
+ require 'postgres/clone/version'
2
+ require 'postgres/clone/executable'
3
+
4
+ module Postgres
5
+
6
+ end
@@ -0,0 +1,21 @@
1
+ require 'postgres/clone/logger'
2
+
3
+ module Postgres
4
+ module Clone
5
+ module CommandLine
6
+ include Logger
7
+
8
+ def build_command(command, sudo: false, user: nil)
9
+ if sudo
10
+ "sudo#{user.nil? ? '' : " -u #{user}"} #{command}"
11
+ else
12
+ command
13
+ end
14
+ end
15
+
16
+ def log_command(host_name, command)
17
+ log_message(Rainbow("[#{host_name}] Executing: #{command}").darkgray)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ module Postgres
2
+ module Clone
3
+ class CommandResult
4
+ attr_reader :exit_code, :output
5
+
6
+ def initialize(exit_code:, output:)
7
+ @exit_code = exit_code
8
+ @output = output.strip
9
+ end
10
+
11
+ def failed?
12
+ !success?
13
+ end
14
+
15
+ def success?
16
+ exit_code == 0
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,211 @@
1
+ require 'etc'
2
+ require 'postgres/clone/command_result'
3
+ require 'postgres/clone/local_commands'
4
+ require 'postgres/clone/logger'
5
+ require 'postgres/clone/remote_commands'
6
+
7
+ module Postgres; module Clone;
8
+ module Commands
9
+ include Logger
10
+ include LocalCommands
11
+ include RemoteCommands
12
+
13
+ def copy_file(src_host, dst_host, file_path)
14
+ log_message("Copying #{file_path} from #{src_host} to #{dst_host}", header: '')
15
+
16
+ if src_host == dst_host
17
+ log_message(src_host, 'Source and destination host are the same, skipping file copy', color: :yellow, header: '')
18
+ return
19
+ end
20
+
21
+ result =
22
+ if local_host?(src_host)
23
+ if file_exists?(dst_host, file_path)
24
+ puts Rainbow("[#{dst_host}] File #{file_path} already exists!").green
25
+ return if %w(yes y).include?(
26
+ ask(Rainbow('Would you like to restore the existing dump (y/n)?').yellow).downcase
27
+ )
28
+ abort Rainbow("[#{dst_host}] File #{file_path} already exists!").red
29
+ else
30
+ run_local("scp #{file_path} #{dst_host}:#{file_path}")
31
+ end
32
+ elsif local_host?(dst_host)
33
+ if file_exists?('localhost', file_path)
34
+ puts Rainbow("[localhost] File #{file_path} already exists!").green
35
+ return if %w(yes y).include?(
36
+ ask(Rainbow('Would you like to restore the existing dump (y/n)?').yellow).downcase
37
+ )
38
+ abort Rainbow("[localhost] File #{file_path} already exists!").red
39
+ else
40
+ run_local("scp #{src_host}:#{file_path} #{file_path}")
41
+ end
42
+ else
43
+ if file_exists?(dst_host, file_path)
44
+ puts Rainbow("[#{dst_host}] File #{file_path} already exists!").green
45
+ return if %w(yes y).include?(
46
+ ask(Rainbow('Would you like to restore the existing dump (y/n)?').yellow).downcase
47
+ )
48
+ abort Rainbow("[#{dst_host}] File #{file_path} already exists!").red
49
+ else
50
+ run_remote(src_host, "scp #{file_path} #{dst_host}:#{file_path}")
51
+ end
52
+ end
53
+
54
+ abort Rainbow('The file copy failed, cancelling database clone').red if result.failed?
55
+ end
56
+
57
+ def current_user
58
+ # TODO: remote
59
+ Etc.getlogin
60
+ end
61
+
62
+ def disk_usage(host_name)
63
+ result =
64
+ if local_host?(host_name)
65
+ run_local(free_disk_space_command)
66
+ else
67
+ run_remote(host_name, free_disk_space_command)
68
+ end
69
+
70
+ columns = result.output.split(/\s+/)
71
+
72
+ abort Rainbow('Expected at least 5 columns of data from df -H').red unless columns.length >= 5
73
+
74
+ { available: columns[3], size: columns[1] }
75
+ end
76
+
77
+ def file_exists?(host_name, file_path)
78
+ log_message("Checking file existance: #{file_path}", host_name: host_name)
79
+
80
+ result =
81
+ if local_host?(host_name)
82
+ run_local(file_exists_command(file_path))
83
+ else
84
+ run_remote(host_name, file_exists_command(file_path))
85
+ end
86
+
87
+ result.success?
88
+ end
89
+
90
+ def file_exists_command(file_path)
91
+ "test -e #{file_path}"
92
+ end
93
+
94
+ def free_disk_space_command
95
+ 'df -H / | grep "/$"'
96
+ end
97
+
98
+ def local_host?(host_name)
99
+ host_name == 'localhost'
100
+ end
101
+
102
+ def postgres_create_database(host_name, database_name, template: nil)
103
+ log_message("Creating database #{database_name}", host_name: host_name)
104
+
105
+ result =
106
+ if local_host?(host_name)
107
+ run_local(
108
+ postgres_create_database_command(database_name, template: template)
109
+ )
110
+ else
111
+ sudo_remote(
112
+ host_name,
113
+ postgres_create_database_command(database_name, template: template),
114
+ user: 'postgres'
115
+ )
116
+ end
117
+
118
+ abort Rainbow('Failed to create database').red if result.failed?
119
+ end
120
+
121
+ def postgres_create_database_command(database_name, template: nil)
122
+ "psql -c \"create database #{database_name}#{template.nil? ? '' : " with template #{template}"}\""
123
+ end
124
+
125
+ def postgres_dump_database(host_name, database_name, file_path)
126
+ log_message("Dumping database #{database_name} to #{file_path}", header: '', host_name: host_name)
127
+
128
+ if file_exists?(host_name, file_path)
129
+ puts Rainbow("[#{host_name}] File #{file_path} already exists!").green
130
+ use_existing_file = %w(yes y).include?(
131
+ ask(Rainbow('Would you like to restore the existing dump (y/n)?').yellow).downcase
132
+ )
133
+ return if use_existing_file
134
+ abort Rainbow("Please delete #{host_name}:#{file_path} before continuing").red
135
+ end
136
+
137
+ result =
138
+ if local_host?(host_name)
139
+ run_local(
140
+ postgres_dump_database_command(file_path, database_name)
141
+ )
142
+ else
143
+ sudo_remote(
144
+ host_name,
145
+ postgres_dump_database_command(file_path, database_name),
146
+ user: 'postgres'
147
+ )
148
+ end
149
+
150
+ if result.failed?
151
+ puts Rainbow('The database dump failed, cleaning up partial dump file').red
152
+ if local_host?(host_name)
153
+ sudo_local("rm #{file_path}")
154
+ else
155
+ sudo_remote(host_name, "rm #{file_path}")
156
+ end
157
+ abort Rainbow('Cancelling database clone').red
158
+ end
159
+ end
160
+
161
+ def postgres_dump_database_command(file_path, source_database)
162
+ "pg_dump --file=#{file_path} --no-acl --no-owner --format=custom #{source_database}"
163
+ end
164
+
165
+ def postgres_restore_database(host_name, database_name, file_path)
166
+ log_message("Restoring database #{database_name} from #{file_path}", header: '', host_name: host_name)
167
+
168
+ abort Rainbow("File #{file_path} does not exist!").red unless file_exists?(host_name, file_path)
169
+
170
+ # TODO: check to see if target database exists
171
+
172
+ postgres_create_database(host_name, database_name)
173
+
174
+ result =
175
+ if local_host?(host_name)
176
+ run_local(
177
+ postgres_restore_database_command(file_path, database_name)
178
+ )
179
+ else
180
+ sudo_remote(
181
+ host_name,
182
+ postgres_restore_database_command(file_path, database_name),
183
+ user: 'postgres'
184
+ )
185
+ end
186
+
187
+ abort Rainbow('The database restore failed').red if result.failed?
188
+ end
189
+
190
+ def postgres_restore_database_command(file_path, destination_database)
191
+ "pg_restore --dbname=#{destination_database} --jobs=4 -O #{file_path}"
192
+ end
193
+
194
+ def print_host_disk_space(host_name)
195
+ log_message('Checking free disk space', header: '', host_name: host_name)
196
+
197
+ usage = disk_usage(host_name)
198
+
199
+ log_message("#{usage[:available]} free of #{usage[:size]} total", host_name: host_name)
200
+ end
201
+
202
+ def user_password(host_name, user)
203
+ @user_passwords ||= {}
204
+ @user_passwords["#{host_name}_#{user}"] ||= begin
205
+ ask(Rainbow("#{user}'s password for #{host_name}? ").yellow, echo: false).tap do |_|
206
+ puts
207
+ end
208
+ end
209
+ end
210
+ end
211
+ end; end;
@@ -0,0 +1,76 @@
1
+ require 'thor'
2
+
3
+ require 'postgres/clone/commands'
4
+
5
+ module Postgres; module Clone;
6
+ class Executable < Thor
7
+ include Commands
8
+
9
+ option :src_host, required: true
10
+ option :src_db, required: true
11
+ option :dst_host, required: true
12
+ option :dst_db
13
+ option :file_path
14
+ option :verbose, default: true
15
+ desc 'clone_database', 'Clones a Postgres database from one host to another'
16
+ def clone_database
17
+ print_hosts_disk_space
18
+
19
+ if options[:src_host] == options[:dst_host]
20
+ create_database_using_template
21
+ else
22
+ create_database_using_restore
23
+ end
24
+
25
+ print_hosts_disk_space
26
+
27
+ close_ssh_connections
28
+ end
29
+
30
+ default_task :clone_database
31
+
32
+ private
33
+
34
+ def create_database_using_restore
35
+ file_path = options[:file_path] || "/tmp/#{file_name}"
36
+
37
+ postgres_dump_database(options[:src_host], options[:src_db], file_path)
38
+
39
+ copy_file(options[:src_host], options[:dst_host], file_path)
40
+
41
+ postgres_restore_database(
42
+ options[:dst_host],
43
+ options[:dst_db] || file_name,
44
+ file_path
45
+ )
46
+
47
+ log_message('Finished restoring database!', header: '', color: :green)
48
+ end
49
+
50
+ def create_database_using_template
51
+ postgres_create_database(
52
+ options[:src_host],
53
+ options[:dst_db] || file_name,
54
+ template: options[:src_db]
55
+ )
56
+
57
+ log_message('Finished creating database!', header: '', color: :green)
58
+ end
59
+
60
+ def file_name
61
+ @file_name ||= begin
62
+ if options[:file_path].nil?
63
+ "#{options[:src_db]}_#{Time.now.strftime('%Y%m%d')}"
64
+ else
65
+ File.basename(options[:file_path])
66
+ end
67
+ end
68
+ end
69
+
70
+ def print_hosts_disk_space
71
+ [options[:src_host], options[:dst_host]].uniq.each do |host_name|
72
+ print_host_disk_space(host_name)
73
+ end
74
+ end
75
+ end
76
+ end; end;
@@ -0,0 +1,22 @@
1
+ require 'postgres/clone/command_line'
2
+ require 'postgres/clone/command_result'
3
+ require 'postgres/clone/logger'
4
+
5
+ module Postgres; module Clone;
6
+ module LocalCommands
7
+ include CommandLine
8
+
9
+ def run_local(command, sudo: false, user: nil)
10
+ actual_command = build_command(command, sudo: sudo, user: user)
11
+ log_command('localhost', actual_command)
12
+
13
+ output = `#{actual_command}`
14
+
15
+ CommandResult.new(exit_code: $?.exitstatus, output: output)
16
+ end
17
+
18
+ def sudo_local(command, user: nil)
19
+ run_local(command, sudo: true, user: user)
20
+ end
21
+ end
22
+ end; end;
@@ -0,0 +1,14 @@
1
+ require 'rainbow'
2
+
3
+ module Postgres
4
+ module Clone
5
+ module Logger
6
+ DEFAULT_COLOR = :blue
7
+
8
+ def log_message(message, color: DEFAULT_COLOR, header: nil, host_name: nil)
9
+ puts header unless header.nil?
10
+ puts Rainbow("#{host_name.nil? ? '' : "[#{host_name}] "}#{message}").color(color)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,74 @@
1
+ require 'net/ssh'
2
+ require 'postgres/clone/command_line'
3
+ require 'postgres/clone/command_result'
4
+ require 'postgres/clone/logger'
5
+
6
+ module Postgres; module Clone;
7
+ module RemoteCommands
8
+ include CommandLine
9
+ include Logger
10
+
11
+ def close_ssh_connections
12
+ (@ssh_connections || {}).values.each(&:close)
13
+ end
14
+
15
+ def open_ssh_connection(host_name, user)
16
+ @ssh_connections ||= {}
17
+ @ssh_connections[host_name] ||= begin
18
+ log_message("Opening ssh connection to #{host_name} as #{user}")
19
+ Net::SSH.start(host_name, user)
20
+ end
21
+ end
22
+
23
+ def run_remote(host_name, command, user: current_user, sudo: false)
24
+ result_attributes = { exit_code: nil, output: '' }
25
+
26
+ ssh = open_ssh_connection(host_name, user)
27
+ ssh.open_channel do |channel|
28
+ channel.request_pty { |_, success| abort('could not obtain pty') unless success }
29
+
30
+ actual_command = build_command(command, user: user, sudo: sudo)
31
+ log_message(actual_command, host_name: host_name, color: :gray)
32
+
33
+ channel.exec(actual_command) do |_, success|
34
+ abort('could not execute command') unless success
35
+
36
+ channel.on_data do |_, data|
37
+ puts data
38
+ case data
39
+ when /^\[sudo\] password for (.+):/i
40
+ password = user_password(host_name, $1)
41
+ log_message("Sending sudo password for #{user}", host_name: host_name, color: :gray)
42
+ channel.send_data("#{password}\n")
43
+ when /(.+)@(.+)'s password:/i
44
+ password = user_password($2, $1)
45
+ log_message("Sending user password for #{$1}", host_name: host_name, color: :gray)
46
+ channel.send_data("#{password}\n")
47
+ when /are you sure you want to continue connecting \(yes\/no\)\?/i
48
+ log_message('ignoring key warning', host_name: host_name, color: :yellow)
49
+ channel.send_data("yes\n")
50
+ else
51
+ result_attributes[:output] += data
52
+ end
53
+ end
54
+
55
+ channel.on_extended_data do |_, _, data|
56
+ log_message("stderr: #{data}", host_name: host_name, color: :red)
57
+ end
58
+
59
+ channel.on_request('exit-status') do |_, data|
60
+ result_attributes[:exit_code] = data.read_long
61
+ end
62
+ end
63
+ end
64
+
65
+ ssh.loop
66
+
67
+ CommandResult.new(result_attributes)
68
+ end
69
+
70
+ def sudo_remote(host_name, command, user: nil)
71
+ run_remote(host_name, command, user: user, sudo: true)
72
+ end
73
+ end
74
+ end; end;
@@ -0,0 +1,5 @@
1
+ module Postgres
2
+ module Clone
3
+ VERSION = '0.1.0'
4
+ end
5
+ end
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'postgres/clone/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'postgres-clone'
8
+ spec.version = Postgres::Clone::VERSION
9
+ spec.authors = ['Josh Rickard']
10
+ spec.email = ['josh.rickard@gmail.com']
11
+
12
+ spec.summary = 'A command line utility for copying postgres databases.'
13
+ spec.description = 'A command line utility for copying postgres databases. Wraps pg_backup and pg_restore in a Ruby wrapper.'
14
+ spec.homepage = 'https://github.com/joshrickard/postgres-clone'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = 'exe'
19
+ spec.executables = 'pg-clone'
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_dependency 'highline', '~> 1.7', '>= 1.7.8'
23
+ spec.add_dependency 'net-ssh', '~> 3.1', '>= 3.1.1'
24
+ spec.add_dependency 'rainbow', '~> 2.1', '>= 2.1.0'
25
+ spec.add_dependency 'thor', '~> 0.19', '>= 0.19.1'
26
+
27
+ spec.add_development_dependency 'bundler', '~> 1.11'
28
+ spec.add_development_dependency 'rake', '~> 10.0'
29
+ spec.add_development_dependency 'rspec', '~> 3.0'
30
+ end
metadata ADDED
@@ -0,0 +1,188 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: postgres-clone
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Josh Rickard
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-05-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: highline
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.7.8
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '1.7'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.7.8
33
+ - !ruby/object:Gem::Dependency
34
+ name: net-ssh
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.1'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 3.1.1
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '3.1'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 3.1.1
53
+ - !ruby/object:Gem::Dependency
54
+ name: rainbow
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '2.1'
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 2.1.0
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '2.1'
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 2.1.0
73
+ - !ruby/object:Gem::Dependency
74
+ name: thor
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: '0.19'
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 0.19.1
83
+ type: :runtime
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.19'
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 0.19.1
93
+ - !ruby/object:Gem::Dependency
94
+ name: bundler
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: '1.11'
100
+ type: :development
101
+ prerelease: false
102
+ version_requirements: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - "~>"
105
+ - !ruby/object:Gem::Version
106
+ version: '1.11'
107
+ - !ruby/object:Gem::Dependency
108
+ name: rake
109
+ requirement: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - "~>"
112
+ - !ruby/object:Gem::Version
113
+ version: '10.0'
114
+ type: :development
115
+ prerelease: false
116
+ version_requirements: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - "~>"
119
+ - !ruby/object:Gem::Version
120
+ version: '10.0'
121
+ - !ruby/object:Gem::Dependency
122
+ name: rspec
123
+ requirement: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - "~>"
126
+ - !ruby/object:Gem::Version
127
+ version: '3.0'
128
+ type: :development
129
+ prerelease: false
130
+ version_requirements: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - "~>"
133
+ - !ruby/object:Gem::Version
134
+ version: '3.0'
135
+ description: A command line utility for copying postgres databases. Wraps pg_backup
136
+ and pg_restore in a Ruby wrapper.
137
+ email:
138
+ - josh.rickard@gmail.com
139
+ executables:
140
+ - pg-clone
141
+ extensions: []
142
+ extra_rdoc_files: []
143
+ files:
144
+ - ".gitignore"
145
+ - ".rspec"
146
+ - ".travis.yml"
147
+ - Gemfile
148
+ - LICENSE.txt
149
+ - README.md
150
+ - Rakefile
151
+ - bin/console
152
+ - bin/setup
153
+ - exe/pg-clone
154
+ - lib/postgres-clone.rb
155
+ - lib/postgres/clone/command_line.rb
156
+ - lib/postgres/clone/command_result.rb
157
+ - lib/postgres/clone/commands.rb
158
+ - lib/postgres/clone/executable.rb
159
+ - lib/postgres/clone/local_commands.rb
160
+ - lib/postgres/clone/logger.rb
161
+ - lib/postgres/clone/remote_commands.rb
162
+ - lib/postgres/clone/version.rb
163
+ - postgres-clone.gemspec
164
+ homepage: https://github.com/joshrickard/postgres-clone
165
+ licenses:
166
+ - MIT
167
+ metadata: {}
168
+ post_install_message:
169
+ rdoc_options: []
170
+ require_paths:
171
+ - lib
172
+ required_ruby_version: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ version: '0'
177
+ required_rubygems_version: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ requirements: []
183
+ rubyforge_project:
184
+ rubygems_version: 2.5.1
185
+ signing_key:
186
+ specification_version: 4
187
+ summary: A command line utility for copying postgres databases.
188
+ test_files: []