dbsync 0.3.0 → 1.0.0.beta2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4d8a3ddf36966a8c9aec5c43f62b383f6a01dac4
4
- data.tar.gz: 04dea7b42bca9c6492be1153947ca10975bca739
3
+ metadata.gz: 6361e83ca4c1d35147ad652e0be945ee8d198cce
4
+ data.tar.gz: a0c7d17fc882430f53c2309f14a9be2f379728c0
5
5
  SHA512:
6
- metadata.gz: 2a0119d2c7b4f67f5e7ec95ebcf29000a6692a7f8c0fd1523703da10efcb71b827c4d5debc347858a086e52a1261b5d5bdb372aae27aadd59a8bee9c9d9de7dc
7
- data.tar.gz: 7481b11c17c29b718c6b4f7addc1ae1345e7e468a012a10b23c0ff693e8a53b815f788fef3eb83312bb93e684b5557ae8ccf86fce23215e6624ddb53f5e974d7
6
+ metadata.gz: 126d7451021ddf112e89a6c753c185b470187d03fe26cd965b26b046a0fc3f652e21be03eb0e379a9871de85c7ada0a0dde5b69381c8ea0e233bfc708522cac0
7
+ data.tar.gz: 075b0e1041f00c29938133e08b3c4d59342fa45f8970b6350f0707263e12ec5c3acc377fd5d611e60653dac825be5e567dd2ebc32ed2aa342f97d52fc24df61e
@@ -1,4 +1,11 @@
1
- ## 0.3.0 (unreleased)
1
+ ## 1.0.0.beta2
2
+ * Remove old dependency
3
+
4
+ ## 1.0.0.beta1
5
+ * Added curl support
6
+ * Removed `clone_dump` task (use `fetch`)
7
+
8
+ ## 0.3.0
2
9
  * Removed all Rails dependencies. Rails is now optional.
3
10
  * Removed scp usage. Everything uses rsync now.
4
11
  * Added Dbsync.file_config and Dbsync.db_config to manually set configuration.
data/Gemfile CHANGED
@@ -1,4 +1,7 @@
1
1
  source "http://rubygems.org"
2
2
 
3
+ gem 'pry'
4
+ gem 'rspec'
5
+
3
6
  # Specify your gem's dependencies in dbsync.gemspec
4
7
  gemspec
data/README.md CHANGED
@@ -24,8 +24,8 @@ Add the following to your `config/environments/development.rb` file. Depending o
24
24
 
25
25
  ```ruby
26
26
  config.dbsync = {
27
- :remote => '66.123.4.567:~/dbsync/mydb.dump',
28
- :local => '../dbsync/mydb.dump'
27
+ :remote => 'dbuser@66.123.4.567:~/dbsync/mydb.dump',
28
+ :local => '../dbsync/dbsync-yourapp.dump'
29
29
  }
30
30
  ```
31
31
 
@@ -37,12 +37,12 @@ You can also specify the dbsync configuration with `Dbsync.file_config` and `Dbs
37
37
 
38
38
  ```ruby
39
39
  Dbsync.file_config = {
40
- :local => "../dbsync/dbsync.dump",
40
+ :local => "../dbsync/dbsync-yourapp.dump",
41
41
  :remote => "dbuser@100.0.100.100:~dbuser/dbsync.dump"
42
42
  }
43
43
 
44
44
  Dbsync.db_config = {
45
- :adapter => "mysql2", # Not actually used yet
45
+ :adapter => "mysql2",
46
46
  :database => "yourdb",
47
47
  :username => "youruser",
48
48
  :password => "yourcoolpassword"
@@ -66,7 +66,6 @@ Run `rake -T dbsync` for all of the available tasks. The tasks are named after `
66
66
  ```
67
67
  rake dbsync # Alias for dbsync:pull
68
68
  rake dbsync:clone # Copy the remote dump file, reset the local database, and load in the dump file
69
- rake dbsync:clone_dump # Copy the remote dump file to a local destination
70
69
  rake dbsync:config # Show the dbsync configuration
71
70
  rake dbsync:fetch # Update the local dump file from the remote source
72
71
  rake dbsync:merge # Update the local database with the local dump file
@@ -75,8 +74,36 @@ rake dbsync:reset # Drop and Create the database, then load the dump file
75
74
  ```
76
75
 
77
76
 
78
- ### Caveats
77
+ ### Download strategies
78
+ `curl` and `rsync` are currently the only two supported options. You can pass a `strategy` option to the dbsync config (`:curl` or `:rsync`) to explicitly specify which strategy to use, or Dbsync will try to infer the strategy. Right now the strategy inference is a little dodgy, so it's best to just specify explicitly.
79
+
80
+ ```ruby
81
+ config.dbsync = {
82
+ :strategy => :rsync,
83
+ :remote => 'username@66.123.4.567:~/dbsync/mydb.dump',
84
+ :local => '../dbsync/mydb.dump'
85
+ }
86
+
87
+ config.dbsync = {
88
+ :strategy => :curl,
89
+ :bin_opts => "--netrc",
90
+ :remote => 'ftp://ftp.yourserver.com/dbsync/mydb.dump',
91
+ :local => '../dbsync/mydb.dump'
92
+ }
93
+ ```
94
+
95
+ `bin_opts` will be passed directly to the bin command.
96
+
97
+
98
+ ### Compressed files
99
+ Dbsync will attempt to determine if it needs to uncompress your file. `tar` and `gz` files are currently supported.
79
100
 
101
+
102
+ ### Database
103
+ Currently only MySQL is supported.
104
+
105
+
106
+ ### Caveats
80
107
  * The `merge` process doesn't clear out your database first. This is to improve performance. Therefore, any tables which you removed on the remote host won't be removed locally. To do a complete reset of your database, run `rake dbsync:reset`. This resets your database (`db:drop` and `db:create`), and then merges in the local file.
81
108
  * Rails: the test database isn't automatically updated when syncing to your development database. After a `dbsync` and before you run tests, you'll need to run `rake db:test:prepare` to setup your database.
82
109
  * Rails: your schema.rb isn't involed in `dbsync` at all. You need to manage it yourself.
@@ -19,5 +19,5 @@ Gem::Specification.new do |s|
19
19
 
20
20
  s.add_dependency "cocaine", [">= 0.5.0", "< 0.6"]
21
21
 
22
- s.add_development_dependency "rspec", '~> 0'
22
+ s.add_development_dependency "bundler", ['>= 1.6', "< 2"]
23
23
  end
@@ -1,7 +1,16 @@
1
+ require 'cocaine'
2
+ require 'fileutils'
3
+
1
4
  require "dbsync/version"
5
+ require 'dbsync/util'
6
+ require "dbsync/strategy"
7
+ require "dbsync/importer"
2
8
  require 'dbsync/sync'
3
9
 
4
10
  module Dbsync
11
+ class ConfigError < StandardError
12
+ end
13
+
5
14
  if defined?(Rails)
6
15
  class Railtie < Rails::Railtie
7
16
  rake_tasks do
@@ -0,0 +1,2 @@
1
+ require 'dbsync/importer/base'
2
+ require 'dbsync/importer/mysql'
@@ -0,0 +1,13 @@
1
+ module Dbsync
2
+ module Importer
3
+ class Base
4
+ def initialize(db_config, local)
5
+ @db_config = db_config
6
+ @local = local
7
+ end
8
+
9
+ def merge
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ module Dbsync
2
+ module Importer
3
+ class Mysql < Base
4
+ def merge
5
+ username = @db_config[:username]
6
+ password = @db_config[:password]
7
+ host = @db_config[:host]
8
+ database = @db_config[:database]
9
+
10
+ opts = ""
11
+ opts += "-u :username " if username
12
+ opts += "-p:password " if password
13
+ opts += "-h :host " if host
14
+
15
+ line = Cocaine::CommandLine.new('mysql', "#{opts} :database < :local")
16
+ line.run({
17
+ :username => username,
18
+ :password => password,
19
+ :host => host,
20
+ :database => database,
21
+ :local => @local
22
+ })
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ require 'dbsync/strategy/base'
2
+ require 'dbsync/strategy/rsync'
3
+ require 'dbsync/strategy/curl'
@@ -0,0 +1,20 @@
1
+ require 'cocaine'
2
+ require 'fileutils'
3
+
4
+ module Dbsync
5
+ module Strategy
6
+ class Base
7
+ def initialize(remote, local, bin_opts)
8
+ @remote = remote
9
+ @local = local
10
+ @bin_opts = bin_opts
11
+ end
12
+
13
+
14
+ # Strategy interface
15
+ # Retrieve the dump file.
16
+ def fetch
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ module Dbsync
2
+ module Strategy
3
+ class Curl < Base
4
+ BIN = "curl"
5
+
6
+ def fetch
7
+ line = Cocaine::CommandLine.new(BIN, ':remote :bin_opts > :local')
8
+ line.run({
9
+ :bin_opts => @bin_opts,
10
+ :remote => @remote,
11
+ :local => @local
12
+ })
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module Dbsync
2
+ module Strategy
3
+ class Rsync < Base
4
+ BIN = "rsync"
5
+
6
+ def fetch
7
+ line = Cocaine::CommandLine.new(BIN, ':bin_opts :remote :local')
8
+ line.run({
9
+ :bin_opts => @bin_opts,
10
+ :remote => @remote,
11
+ :local => @local
12
+ })
13
+ end
14
+ end
15
+ end
16
+ end
@@ -3,85 +3,48 @@ require 'fileutils'
3
3
 
4
4
  module Dbsync
5
5
  class Sync
6
- class << self
7
- def notify(message="")
8
- $stdout.puts "[#{Time.now.strftime('%T')}] [dbsync] #{message}"
9
- end
10
- end
6
+ STRATEGY = {
7
+ :rsync => Dbsync::Strategy::Rsync,
8
+ :curl => Dbsync::Strategy::Curl
9
+ }
10
+
11
+ IMPORTER = {
12
+ :mysql => Dbsync::Importer::Mysql
13
+ }
11
14
 
12
15
 
13
- def initialize(ssh_config, db_config, options={})
14
- ssh_config = symbolize_keys(ssh_config)
15
- db_config = symbolize_keys(db_config)
16
+ def initialize(file_config, db_config, options={})
17
+ @file_config = Dbsync::Util.symbolize_keys(file_config)
18
+ @db_config = Dbsync::Util.symbolize_keys(db_config)
16
19
 
17
20
  @verbose = !!options[:verbose]
18
21
 
19
- @db_username = db_config[:username]
20
- @db_password = db_config[:password]
21
- @db_host = db_config[:host]
22
- @db_database = db_config[:database]
23
-
24
- @remote = ssh_config[:remote]
25
- @local = File.expand_path(ssh_config[:local]) if ssh_config[:local]
26
-
27
- if !@remote
28
- $stdout.puts "DEPRECATED: The remote_host, remote_dir, and filename " \
29
- "options will be removed. " \
30
- "Instead, combine remote_host, remote_dir, and filename into a " \
31
- "single 'remote' configuration. Example: " \
32
- "'{ remote: \"dbuser@100.0.1.100:~/dbuser/yourdb.dump\" }'"
33
-
34
- remote_host = ssh_config[:remote_host]
35
- remote_dir = ssh_config[:remote_dir]
36
- filename = ssh_config[:filename]
37
- @remote = "#{remote_host}:#{File.join(remote_dir, filename)}"
38
- end
22
+ @remote = @file_config[:remote]
23
+ remote_filename = File.basename(@remote)
39
24
 
40
- if !@local
41
- $stdout.puts "DEPRECATED: The local_dir and filename " \
42
- "options will be removed. " \
43
- "Instead, combine local_dir and filename into a " \
44
- "single 'local' configuration. Example: " \
45
- "'{ local: \"../dbsync/yourdb.dump\" }'"
25
+ @local = File.expand_path(@file_config[:local])
26
+ local_dir = File.dirname(@local)
27
+ @download = File.join(local_dir, remote_filename)
46
28
 
47
- local_dir = ssh_config[:local_dir]
48
- filename = ssh_config[:filename]
49
- @local = File.expand_path(File.join(local_dir, filename))
50
- end
29
+ FileUtils.mkdir_p(local_dir)
30
+
31
+ @strategy = strategy.new(@remote, @download, @file_config[:bin_opts])
32
+ @importer = importer.new(@db_config, @local)
51
33
  end
52
34
 
53
35
 
54
36
  # Update the local dump file from the remote source (using rsync).
55
37
  def fetch
56
- notify "Updating '#{@local}' from '#{@remote}' via rsync..."
57
-
58
- FileUtils.mkdir_p(File.dirname(@local))
59
-
60
- line = Cocaine::CommandLine.new('rsync', '-v :remote :local')
61
- line.run({
62
- :remote => @remote,
63
- :local => @local
64
- })
38
+ notify "Downloading..."
39
+ @strategy.fetch
40
+ extract
65
41
  end
66
42
 
67
43
 
68
44
  # Update the local database with the local dump file.
69
45
  def merge
70
- notify "Dumping data from '#{@local}' into '#{@db_database}'..."
71
-
72
- options = ""
73
- options += "-u :username " if @db_username
74
- options += "-p:password " if @db_password
75
- options += "-h :host " if @db_host
76
-
77
- line = Cocaine::CommandLine.new('mysql', "#{options} :database < :local")
78
- line.run({
79
- :username => @db_username,
80
- :password => @db_password,
81
- :host => @db_host,
82
- :database => @db_database,
83
- :local => @local
84
- })
46
+ notify "Importing..."
47
+ @importer.merge
85
48
  end
86
49
 
87
50
 
@@ -92,36 +55,61 @@ module Dbsync
92
55
  end
93
56
 
94
57
 
95
- # Copy the remote dump file to a local destination.
96
- # Instead of requiring two different tools (rsync and scp) for this
97
- # library, instead we'll just remove the local file if it exists
98
- # then run rsync, which is essentially a full copy.
99
- def clone_dump
100
- notify "Copying '#{@remote}' into '#{@local}' via rsync..."
101
- FileUtils.rm_f(@local)
102
- fetch
58
+ private
59
+
60
+ # TODO: There is a ruby library called "Archive" which can
61
+ # extract these much better for us. The only problem is that
62
+ # we can't specify a *filename* to extract to, which is
63
+ # important for us.
64
+ def extract
65
+ case @download
66
+ when /\.tar/ then untar
67
+ when /\.gz\z/ then gunzip
68
+ end
103
69
  end
104
70
 
71
+ # We're overwriting files by default. Is this okay? Probably not.
72
+ def gunzip
73
+ line = Cocaine::CommandLine.new('gunzip', "-c :download > :local")
74
+ line.run(download: @download, local: @local)
75
+ end
105
76
 
106
- private
77
+ def untar
78
+ line = Cocaine::CommandLine.new('tar', "-C :local -xf :download")
79
+ line.run(download: @download, local: @local)
80
+ end
107
81
 
108
- def symbolize_keys(hash)
109
- return hash unless hash.keys.any? { |k| k.is_a?(String) }
110
82
 
111
- result = {}
112
- hash.each_key { |k| result[k.to_sym] = hash[k] }
113
- result
83
+ def importer
84
+ IMPORTER[@file_config[:importer]] ||
85
+ IMPORTER[infer_importer_key]
114
86
  end
115
87
 
88
+ def infer_importer_key
89
+ case @db_config[:adapter]
90
+ when /mysql/ then :mysql
91
+ else raise Dbsync::ConfigError, "Only MySQL supported right now."
92
+ end
93
+ end
116
94
 
117
- def raise_missing(config="")
118
- raise "Missing Configuration: '#{config}'. " \
119
- "See README for required config."
95
+
96
+ def strategy
97
+ STRATEGY[@file_config[:strategy]] ||
98
+ STRATEGY[infer_strategy_key]
120
99
  end
121
100
 
101
+ # These matches could be a lot better.
102
+ def infer_strategy_key
103
+ case @remote
104
+ when /\A.+?@.+?:.+?/ then :rsync
105
+ else :curl
106
+ end
107
+ end
108
+
109
+
122
110
  def notify(*args)
123
111
  if @verbose
124
- Sync.notify(*args)
112
+ Dbsync::Util.notify(*args)
125
113
  end
126
114
  end
127
115
  end
@@ -0,0 +1,17 @@
1
+ module Dbsync
2
+ module Util
3
+ class << self
4
+ def symbolize_keys(hash)
5
+ return hash unless hash.keys.any? { |k| k.is_a?(String) }
6
+
7
+ result = {}
8
+ hash.each_key { |k| result[k.to_sym] = hash[k] }
9
+ result
10
+ end
11
+
12
+ def notify(message="")
13
+ $stdout.puts "[#{Time.now.strftime('%T')}] [dbsync] #{message}"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,3 +1,3 @@
1
1
  module Dbsync
2
- VERSION = "0.3.0"
2
+ VERSION = "1.0.0.beta2"
3
3
  end
@@ -13,7 +13,7 @@ end
13
13
  namespace :dbsync do
14
14
  task :setup => :environment do
15
15
  if defined?(Rails)
16
- Dbsync::Sync.notify "Rails Environment: #{Rails.env}"
16
+ Dbsync::Util.notify "Rails Environment: #{Rails.env}"
17
17
 
18
18
  if Rails.env == 'production'
19
19
  raise "These tasks are destructive and shouldn't " \
@@ -29,7 +29,7 @@ namespace :dbsync do
29
29
  task :config => :setup do
30
30
  # We don't use Sync.notify here because we don't want or need
31
31
  # the extra output that comes with it.
32
- $stdout.puts file_config.to_yaml
32
+ Dbsync::Util.notify file_config
33
33
  end
34
34
 
35
35
 
@@ -42,7 +42,7 @@ namespace :dbsync do
42
42
  desc "Copy the remote dump file, reset the local database, " \
43
43
  "and load in the dump file"
44
44
  task :clone => :setup do
45
- @dbsync.clone_dump
45
+ @dbsync.fetch
46
46
  Rake::Task['dbsync:reset'].invoke
47
47
  end
48
48
 
@@ -53,12 +53,6 @@ namespace :dbsync do
53
53
  end
54
54
 
55
55
 
56
- desc "Copy the remote dump file to a local destination"
57
- task :clone_dump => :setup do
58
- @dbsync.clone_dump
59
- end
60
-
61
-
62
56
  desc "Update the local database with the local dump file."
63
57
  task :merge => :setup do
64
58
  @dbsync.merge
@@ -80,11 +74,11 @@ end
80
74
  def db_config
81
75
  return Dbsync.db_config if Dbsync.db_config
82
76
  return ActiveRecord::Base.configurations[Rails.env] if defined?(Rails)
83
- raise "No database configuration found."
77
+ raise Dbsync::ConfigError, "No database configuration found."
84
78
  end
85
79
 
86
80
  def file_config
87
81
  return Dbsync.file_config if Dbsync.file_config
88
82
  return Rails.application.config.dbsync if defined?(Rails)
89
- raise "No remote configuration found."
83
+ raise Dbsync::ConfigError, "No remote configuration found."
90
84
  end
@@ -46,19 +46,6 @@ describe Dbsync::Sync do
46
46
  describe '#merge' do
47
47
  end
48
48
 
49
- describe '#clone_dump' do
50
- it "removes the local file and rsyncs a fresh one" do
51
- # Put a dummy local file in place to make sure it gets removed
52
- File.open(local_path, "w") { |f| f.write "Hello." }
53
- File.read(local_path).should eq "Hello."
54
-
55
- sync = Dbsync::Sync.new(ssh_config, db_config)
56
- sync.clone_dump
57
-
58
- File.read(local_path).should eq File.read(remote_path)
59
- end
60
- end
61
-
62
49
  describe '#pull' do
63
50
  end
64
51
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dbsync
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 1.0.0.beta2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bryan Ricker
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-15 00:00:00.000000000 Z
11
+ date: 2014-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cocaine
@@ -31,19 +31,25 @@ dependencies:
31
31
  - !ruby/object:Gem::Version
32
32
  version: '0.6'
33
33
  - !ruby/object:Gem::Dependency
34
- name: rspec
34
+ name: bundler
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - "~>"
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '1.6'
40
+ - - "<"
38
41
  - !ruby/object:Gem::Version
39
- version: '0'
42
+ version: '2'
40
43
  type: :development
41
44
  prerelease: false
42
45
  version_requirements: !ruby/object:Gem::Requirement
43
46
  requirements:
44
- - - "~>"
47
+ - - ">="
45
48
  - !ruby/object:Gem::Version
46
- version: '0'
49
+ version: '1.6'
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '2'
47
53
  description: A set of rake tasks to help you sync your remote production data with
48
54
  your local database for development.
49
55
  email:
@@ -60,7 +66,15 @@ files:
60
66
  - Rakefile
61
67
  - dbsync.gemspec
62
68
  - lib/dbsync.rb
69
+ - lib/dbsync/importer.rb
70
+ - lib/dbsync/importer/base.rb
71
+ - lib/dbsync/importer/mysql.rb
72
+ - lib/dbsync/strategy.rb
73
+ - lib/dbsync/strategy/base.rb
74
+ - lib/dbsync/strategy/curl.rb
75
+ - lib/dbsync/strategy/rsync.rb
63
76
  - lib/dbsync/sync.rb
77
+ - lib/dbsync/util.rb
64
78
  - lib/dbsync/version.rb
65
79
  - lib/tasks/dbsync.rake
66
80
  - spec/local/.gitkeep
@@ -82,9 +96,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
82
96
  version: '0'
83
97
  required_rubygems_version: !ruby/object:Gem::Requirement
84
98
  requirements:
85
- - - ">="
99
+ - - ">"
86
100
  - !ruby/object:Gem::Version
87
- version: '0'
101
+ version: 1.3.1
88
102
  requirements: []
89
103
  rubyforge_project:
90
104
  rubygems_version: 2.2.2