syncoku 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +80 -0
- data/Rakefile +2 -0
- data/bin/syncoku +8 -0
- data/lib/syncoku/capture_backup.rb +21 -0
- data/lib/syncoku/control.rb +52 -0
- data/lib/syncoku/local.rb +26 -0
- data/lib/syncoku/local_db.rb +112 -0
- data/lib/syncoku/remote.rb +35 -0
- data/lib/syncoku/remote_db.rb +31 -0
- data/lib/syncoku/runnable.rb +26 -0
- data/lib/syncoku/s3.rb +93 -0
- data/lib/syncoku/version.rb +3 -0
- data/lib/syncoku.rb +14 -0
- data/syncoku.gemspec +23 -0
- metadata +104 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f4fb8a94d545cfbd3ceca353015892221e9cb754
|
4
|
+
data.tar.gz: b2d8fd4509402e592ae9f70ce314402018b530c2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0e6aa7c4c8d0c6152069c95f517a954f7d5af33a7cf674220ab575998c82c6d285fd4e99b854bfdf54ed0e8d0db73cdca01082a35c5ec523e0b94abc59196896
|
7
|
+
data.tar.gz: e02b9ab5796c8b1abd3fcb8c03dc754b4d9b70677291cbf9c3854a2cdc56e69b4c00148142a3ecaf583589dc3902b2f5227e191195dc773db282b36f612511cc
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Bill Horsman
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# Syncoku
|
2
|
+
|
3
|
+
Copies a production Heroku Postgresql database to the local development database or a staging Heroku database. Optionally syncs the production S3 bucket with another bucket.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'syncoku'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install syncoku
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
To copy production to your local development database:
|
24
|
+
```
|
25
|
+
syncoku
|
26
|
+
```
|
27
|
+
|
28
|
+
If you have S3 configured it will do the database and S3. To choose just one of those:
|
29
|
+
```
|
30
|
+
syncoku db
|
31
|
+
syncoku s3
|
32
|
+
```
|
33
|
+
|
34
|
+
To target the staging environment:
|
35
|
+
```
|
36
|
+
syncoku staging
|
37
|
+
```
|
38
|
+
|
39
|
+
or more simply:
|
40
|
+
```
|
41
|
+
syncoku s
|
42
|
+
```
|
43
|
+
|
44
|
+
## Downloading the database (locally)
|
45
|
+
|
46
|
+
It will capture a backup of the database and download it to a local file called `.syncoku.dump`. If you run Syncoku a second time and it discovers this file then it will give you the option of reusing it or downloading a new one. Reusing the existing one comes in useful if you have messed around with the local database and want to clean it up.
|
47
|
+
|
48
|
+
## Hooks
|
49
|
+
|
50
|
+
If you define a rake task called `syncoku:after_sync` then it will automatically be run after the database has been restored and migrated. This is a good place to put anonymization tasks, for instance.
|
51
|
+
|
52
|
+
## S3
|
53
|
+
|
54
|
+
If you add a file called `syncoku.yml` with the following information, it can sync between S3 buckets too:
|
55
|
+
|
56
|
+
```
|
57
|
+
# syncoku.yml
|
58
|
+
s3:
|
59
|
+
access_key_id: "ABCDEFGH123456789"
|
60
|
+
secret_access_key: "a1secret2key3to4access5s3"
|
61
|
+
|
62
|
+
development:
|
63
|
+
bucket: "my-bucket-development"
|
64
|
+
|
65
|
+
staging:
|
66
|
+
bucket: "my-bucket-staging"
|
67
|
+
|
68
|
+
production:
|
69
|
+
bucket: "my-bucket-production"
|
70
|
+
```
|
71
|
+
|
72
|
+
*Note:* a limitation is that that the buckets must use the same credentials.
|
73
|
+
|
74
|
+
## Contributing
|
75
|
+
|
76
|
+
1. Fork it ( https://github.com/billhorsman/syncoku/fork )
|
77
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
78
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
79
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
80
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/syncoku
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Syncoku
|
2
|
+
module CaptureBackup
|
3
|
+
include Runnable
|
4
|
+
|
5
|
+
def capture
|
6
|
+
puts "Capturing #{production_app_name} backup..."
|
7
|
+
run_on_production("pgbackups:capture --expire")
|
8
|
+
run_on_production("pgbackups:url")
|
9
|
+
end
|
10
|
+
|
11
|
+
def run_on_production(command)
|
12
|
+
run_command "heroku #{command} --app #{production_app_name}"
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
def production_app_name
|
17
|
+
@production_app_name ||= run_command("git remote -v | grep production | grep push").match(/heroku[^:]*:(.*)\.git/)[1]
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Syncoku
|
2
|
+
|
3
|
+
module Control
|
4
|
+
extend Syncoku::Runnable
|
5
|
+
|
6
|
+
def self.run(args)
|
7
|
+
matching_remotes = remotes & args
|
8
|
+
if matching_remotes.size == 0 && remote_index_uniq?
|
9
|
+
if key = (remote_index.keys & args)[0]
|
10
|
+
matching_remotes = [remote_index[key]]
|
11
|
+
args.delete key
|
12
|
+
end
|
13
|
+
end
|
14
|
+
target = case matching_remotes.compact.size
|
15
|
+
when 0
|
16
|
+
Syncoku::Local.new
|
17
|
+
when 1
|
18
|
+
remote = matching_remotes[0]
|
19
|
+
args.delete remote
|
20
|
+
Syncoku::Remote.new(remote)
|
21
|
+
else
|
22
|
+
puts "Please choose just one remote out of #{remotes.join(" or ")}"
|
23
|
+
exit 1
|
24
|
+
end
|
25
|
+
commands = %w[both db s3 rebuild] & args
|
26
|
+
commands << "both" if commands.size == 0
|
27
|
+
if commands.size > 1
|
28
|
+
puts "Choose just one command"
|
29
|
+
exit 1
|
30
|
+
else
|
31
|
+
args.delete commands[0]
|
32
|
+
target.send(commands[0], args)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.remotes
|
37
|
+
@remotes ||= run_command("git remote -v | grep heroku | grep push").split("\n").map {|line|
|
38
|
+
line.match(/^(.*)\t/)[1]
|
39
|
+
}.reject {|r| r == "production" || r == "heroku" }
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.remote_index_uniq?
|
43
|
+
remote_index.size == remotes.size
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.remote_index
|
47
|
+
@remote_index ||= Hash[remotes.map{|r| [r.slice(0, 1), r] }]
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Syncoku
|
2
|
+
|
3
|
+
# Responsible for syncing to a local app
|
4
|
+
class Local
|
5
|
+
include Runnable
|
6
|
+
include CaptureBackup
|
7
|
+
|
8
|
+
def both(args)
|
9
|
+
db(args)
|
10
|
+
s3(args) if S3.config?
|
11
|
+
end
|
12
|
+
|
13
|
+
def db(args)
|
14
|
+
Syncoku::LocalDb.new.sync
|
15
|
+
end
|
16
|
+
|
17
|
+
def s3(args)
|
18
|
+
Syncoku::S3.new(:development).sync
|
19
|
+
end
|
20
|
+
|
21
|
+
def rebuild(args)
|
22
|
+
Syncoku::LocalDb.new.rebuild
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module Syncoku
|
2
|
+
|
3
|
+
class LocalDb
|
4
|
+
include Runnable
|
5
|
+
include CaptureBackup
|
6
|
+
|
7
|
+
def sync
|
8
|
+
if File.exist?("#{dump_filename}")
|
9
|
+
ask_to_download
|
10
|
+
else
|
11
|
+
download
|
12
|
+
end
|
13
|
+
drop_and_create
|
14
|
+
pg_restore
|
15
|
+
migrate
|
16
|
+
run_hook 'after_sync'
|
17
|
+
`touch tmp/restart.txt`
|
18
|
+
end
|
19
|
+
|
20
|
+
def rebuild
|
21
|
+
kill_connections
|
22
|
+
puts "Rebuilding database"
|
23
|
+
run_command "bundle exec rake db:drop db:create db:migrate"
|
24
|
+
puts "Seeding"
|
25
|
+
run_command "bundle exec rake db:seed"
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def run_hook(name)
|
31
|
+
if test_command "rake syncoku:#{name}"
|
32
|
+
puts "Running #{name} hook"
|
33
|
+
run_command "bundle exec rake syncoku:#{name}"
|
34
|
+
else
|
35
|
+
puts "Skipping #{name} hook. Define a Rake task called syncoku:#{name} to activate."
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def ask_to_download
|
40
|
+
print "Found existing #{dump_filename}. Choose:\n D = download new backup, or\n R = reuse existing backup\nPress D or R or anything else to abort: "
|
41
|
+
proceed = STDIN.getch.downcase
|
42
|
+
puts proceed
|
43
|
+
if proceed == 'd'
|
44
|
+
download
|
45
|
+
elsif proceed == 'r'
|
46
|
+
puts "OK, reusing backup"
|
47
|
+
else
|
48
|
+
exit 1
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def download
|
53
|
+
run_command "curl -o #{dump_filename} \"#{capture}\""
|
54
|
+
end
|
55
|
+
|
56
|
+
def kill_connections
|
57
|
+
pids = `ps x|grep postgres|grep #{database_config["database"]} | grep -v 'grep' | cut -b 1,2,3,4,5,6`.gsub(/[^0-9]/, ' ').split(' ')
|
58
|
+
if pids.any?
|
59
|
+
puts "Killing #{pids.size} Postgres connection(s) (#{pids.join(", ")})"
|
60
|
+
pids.each do |pid|
|
61
|
+
`kill #{pid}`
|
62
|
+
end
|
63
|
+
else
|
64
|
+
puts "No connections to kill"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def drop_and_create
|
69
|
+
kill_connections
|
70
|
+
puts "Dropping and recreating #{database_name} database"
|
71
|
+
run_command "bundle exec rake db:drop db:create"
|
72
|
+
end
|
73
|
+
|
74
|
+
def migrate
|
75
|
+
run_command "bundle exec rake db:migrate"
|
76
|
+
end
|
77
|
+
|
78
|
+
def pg_restore
|
79
|
+
puts "Restoring database from #{dump_filename}"
|
80
|
+
options = []
|
81
|
+
options << "--verbose"
|
82
|
+
options << "--clean"
|
83
|
+
options << "--no-acl"
|
84
|
+
options << "--no-owner"
|
85
|
+
options << "--username=#{database_config["user"]}" if database_config["user"]
|
86
|
+
options << "--password=#{database_config["password"]}" if database_config["password"]
|
87
|
+
options << "--dbname=#{database_name}"
|
88
|
+
options << "--port=#{database_config["port"] || "5432"}"
|
89
|
+
output = `pg_restore #{options.join(' ')} #{dump_filename} 2> /dev/null`
|
90
|
+
if output =~ /a transfer is currently in progress/
|
91
|
+
puts "It looks like a backup is already in progress (or possibly stuck):"
|
92
|
+
puts output
|
93
|
+
`heroku pgbackups --app #{production_app}`
|
94
|
+
puts "Use pgbackups:destroy to remove the offending backup (or wait a bit to see if it fixes itself)"
|
95
|
+
exit 1
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def database_name
|
100
|
+
database_config["database"]
|
101
|
+
end
|
102
|
+
|
103
|
+
def database_config
|
104
|
+
YAML.load(File.read("config/database.yml"))["development"]
|
105
|
+
end
|
106
|
+
|
107
|
+
def dump_filename
|
108
|
+
".syncoku.dump"
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Syncoku
|
2
|
+
|
3
|
+
# Responsible for syncing to a remote app
|
4
|
+
class Remote
|
5
|
+
include Runnable
|
6
|
+
|
7
|
+
attr_reader :remote
|
8
|
+
|
9
|
+
def initialize(remote)
|
10
|
+
@remote = remote
|
11
|
+
end
|
12
|
+
|
13
|
+
def both(args)
|
14
|
+
db(args)
|
15
|
+
s3(args) if S3.config?
|
16
|
+
end
|
17
|
+
|
18
|
+
def db(args)
|
19
|
+
Syncoku::RemoteDb.new(app_name).sync
|
20
|
+
end
|
21
|
+
|
22
|
+
def s3(args)
|
23
|
+
Syncoku::S3.new(remote).sync
|
24
|
+
end
|
25
|
+
|
26
|
+
def rebuild(args)
|
27
|
+
puts "Rebuild not implemented"
|
28
|
+
end
|
29
|
+
|
30
|
+
def app_name
|
31
|
+
@app_name ||= run_command("git remote -v | grep #{remote} | grep push").match(/heroku\.com:(.*)\.git/)[1]
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Syncoku
|
2
|
+
|
3
|
+
class RemoteDb
|
4
|
+
include Runnable
|
5
|
+
include CaptureBackup
|
6
|
+
|
7
|
+
attr_reader :app_name
|
8
|
+
|
9
|
+
def initialize(app_name)
|
10
|
+
@app_name = app_name
|
11
|
+
end
|
12
|
+
|
13
|
+
def sync
|
14
|
+
puts "Switch on maintenance mode"
|
15
|
+
run_remotely "maintenance:on"
|
16
|
+
puts "Restoring database"
|
17
|
+
run_remotely "pg:reset DATABASE_URL --confirm #{app_name}"
|
18
|
+
run_remotely "pgbackups:restore DATABASE_URL '#{capture}' --confirm #{app_name}"
|
19
|
+
run_remotely "run rake db:migrate"
|
20
|
+
run_remotely "run rake syncoku:after_sync"
|
21
|
+
run_remotely "restart"
|
22
|
+
puts "Switch off maintenance mode"
|
23
|
+
run_remotely "maintenance:off"
|
24
|
+
end
|
25
|
+
|
26
|
+
def run_remotely(command)
|
27
|
+
run_command "heroku #{command} --app #{app_name}"
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Syncoku
|
2
|
+
module Runnable
|
3
|
+
|
4
|
+
def run_command(command)
|
5
|
+
Bundler.with_clean_env {
|
6
|
+
out = `#{command}`
|
7
|
+
if $?.success?
|
8
|
+
out
|
9
|
+
else
|
10
|
+
puts "Error running command:"
|
11
|
+
puts command
|
12
|
+
puts out
|
13
|
+
exit $?.exitstatus
|
14
|
+
end
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_command(command)
|
19
|
+
Bundler.with_clean_env {
|
20
|
+
`#{command} 2> /dev/null`
|
21
|
+
$?.success?
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/lib/syncoku/s3.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
module Syncoku
|
2
|
+
|
3
|
+
class S3
|
4
|
+
|
5
|
+
attr_reader :to_env, :from_name, :to_name, :from_bucket, :to_bucket, :from_keys, :to_keys
|
6
|
+
|
7
|
+
def initialize(to_env)
|
8
|
+
@to_env = to_env
|
9
|
+
end
|
10
|
+
|
11
|
+
def sync
|
12
|
+
@missing = []
|
13
|
+
@from_name = config_value "production.bucket"
|
14
|
+
@to_name = config_value "#{to_env}.bucket"
|
15
|
+
access_key_id = config_value "access_key_id"
|
16
|
+
secret_access_key = config_value "secret_access_key"
|
17
|
+
if @missing.any?
|
18
|
+
puts "Missing syncoku.yml values prevented S3 sync"
|
19
|
+
@missing.each do |path|
|
20
|
+
puts " s3.#{path}"
|
21
|
+
end
|
22
|
+
return
|
23
|
+
end
|
24
|
+
puts "Syncing S3 from #{from_name} to #{to_name}..."
|
25
|
+
AWS.config access_key_id: access_key_id, secret_access_key: secret_access_key
|
26
|
+
@from_bucket = AWS::S3.new.buckets[from_name]
|
27
|
+
@to_bucket = AWS::S3.new.buckets[to_name]
|
28
|
+
return unless get_keys
|
29
|
+
if !simple_sync & !remove_spare
|
30
|
+
puts "S3 is in sync. Nothing to do :)"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_keys
|
35
|
+
@from_keys = from_bucket.objects.map(&:key)
|
36
|
+
@to_keys = to_bucket.objects.map(&:key)
|
37
|
+
true
|
38
|
+
rescue AWS::S3::Errors::SignatureDoesNotMatch => e
|
39
|
+
puts "Can't sync S3 because #{e.message.sub(/^T/, 't')}"
|
40
|
+
false
|
41
|
+
end
|
42
|
+
|
43
|
+
def simple_sync
|
44
|
+
missing = from_keys - to_keys
|
45
|
+
return false if missing.empty?
|
46
|
+
puts "On #{from_name} but not on #{to_name}: #{"%7d" % missing.size}"
|
47
|
+
puts "Copying to #{to_name}"
|
48
|
+
missing.each do |key|
|
49
|
+
to_bucket.objects[key].copy_from key, { bucket: from_bucket, acl: :public_read}
|
50
|
+
print "."
|
51
|
+
STDOUT.flush
|
52
|
+
end
|
53
|
+
puts " done"
|
54
|
+
true
|
55
|
+
end
|
56
|
+
|
57
|
+
def remove_spare
|
58
|
+
spare = to_keys - from_keys
|
59
|
+
return false if spare.empty?
|
60
|
+
puts "On #{to_name} but not on #{from_name}: #{"%7d" % spare.size}"
|
61
|
+
puts "Deleting from #{to_name}"
|
62
|
+
spare.each do |key|
|
63
|
+
to_bucket.objects[key].delete
|
64
|
+
print "."
|
65
|
+
STDOUT.flush
|
66
|
+
end
|
67
|
+
puts " done"
|
68
|
+
true
|
69
|
+
end
|
70
|
+
|
71
|
+
def config_value(path)
|
72
|
+
value = config.dup["s3"]
|
73
|
+
path.split(".").each do |name|
|
74
|
+
value = value[name]
|
75
|
+
if value.nil?
|
76
|
+
@missing << path
|
77
|
+
return nil
|
78
|
+
end
|
79
|
+
end
|
80
|
+
value
|
81
|
+
end
|
82
|
+
|
83
|
+
def config
|
84
|
+
@config ||= YAML.load(File.read("syncoku.yml"))
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.config?
|
88
|
+
File.exist?("syncoku.yml")
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
data/lib/syncoku.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'aws-sdk-v1'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'rake'
|
4
|
+
require 'yaml'
|
5
|
+
require 'io/console'
|
6
|
+
require "syncoku/runnable"
|
7
|
+
require "syncoku/capture_backup"
|
8
|
+
require "syncoku/control"
|
9
|
+
require "syncoku/local"
|
10
|
+
require "syncoku/local_db"
|
11
|
+
require "syncoku/remote"
|
12
|
+
require "syncoku/remote_db"
|
13
|
+
require "syncoku/s3"
|
14
|
+
require "syncoku/version"
|
data/syncoku.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'syncoku/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "syncoku"
|
8
|
+
spec.version = Syncoku::VERSION
|
9
|
+
spec.authors = ["Bill Horsman"]
|
10
|
+
spec.email = ["bill@logicalcobwebs.com"]
|
11
|
+
spec.summary = %q{Convenient way of syncing data from and to Heroku}
|
12
|
+
spec.homepage = "https://github.com/billhorsman/syncoku"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
21
|
+
spec.add_development_dependency "aws-sdk-v1", "~> 1.7"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: syncoku
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Bill Horsman
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-04-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: aws-sdk-v1
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.7'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.7'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
description:
|
56
|
+
email:
|
57
|
+
- bill@logicalcobwebs.com
|
58
|
+
executables:
|
59
|
+
- syncoku
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- ".gitignore"
|
64
|
+
- Gemfile
|
65
|
+
- LICENSE.txt
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- bin/syncoku
|
69
|
+
- lib/syncoku.rb
|
70
|
+
- lib/syncoku/capture_backup.rb
|
71
|
+
- lib/syncoku/control.rb
|
72
|
+
- lib/syncoku/local.rb
|
73
|
+
- lib/syncoku/local_db.rb
|
74
|
+
- lib/syncoku/remote.rb
|
75
|
+
- lib/syncoku/remote_db.rb
|
76
|
+
- lib/syncoku/runnable.rb
|
77
|
+
- lib/syncoku/s3.rb
|
78
|
+
- lib/syncoku/version.rb
|
79
|
+
- syncoku.gemspec
|
80
|
+
homepage: https://github.com/billhorsman/syncoku
|
81
|
+
licenses:
|
82
|
+
- MIT
|
83
|
+
metadata: {}
|
84
|
+
post_install_message:
|
85
|
+
rdoc_options: []
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
requirements: []
|
99
|
+
rubyforge_project:
|
100
|
+
rubygems_version: 2.4.5
|
101
|
+
signing_key:
|
102
|
+
specification_version: 4
|
103
|
+
summary: Convenient way of syncing data from and to Heroku
|
104
|
+
test_files: []
|