dbsync 0.3.0 → 1.0.0.beta2

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: 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