synco 1.1.0 → 1.2.0
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 +5 -5
- data/.github/workflows/development.yml +40 -0
- data/.rspec +1 -1
- data/Gemfile +0 -3
- data/README.md +134 -116
- data/Rakefile +1 -3
- data/bin/synco +1 -2
- data/lib/synco/command.rb +4 -3
- data/lib/synco/command/disk.rb +2 -2
- data/lib/synco/command/prune.rb +1 -1
- data/lib/synco/command/rotate.rb +1 -1
- data/lib/synco/command/spawn.rb +1 -1
- data/lib/synco/scope.rb +5 -14
- data/lib/synco/script.rb +0 -2
- data/lib/synco/version.rb +1 -1
- data/spec/spec_helper.rb +34 -0
- data/spec/synco/directory_spec.rb +1 -1
- data/spec/synco/method_spec.rb +1 -1
- data/spec/synco/rsync_spec.rb +2 -2
- data/spec/synco/scp_spec.rb +1 -1
- data/spec/synco/script_spec.rb +1 -1
- data/spec/synco/shell_spec.rb +11 -13
- data/spec/synco/usb_spec.rb +1 -1
- data/spec/synco/zfs_spec.rb +1 -1
- data/synco.gemspec +9 -13
- metadata +20 -25
- data/.simplecov +0 -9
- data/.travis.yml +0 -14
- data/lib/synco/compact_formatter.rb +0 -115
- data/media/LSync Logo.artx/Preview/preview.png +0 -0
- data/media/LSync Logo.artx/QuickLook/Preview.pdf +0 -0
- data/media/LSync Logo.artx/doc.thread +0 -0
- data/media/LSync Logo.png +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5836163e56294236c873d3ed73f1f10ae20f164db3339118a3477831e9551f2d
|
4
|
+
data.tar.gz: 59f01018eaa9cd7bb703efc6f4f23c2c0b2eadc89d031482decf15398417d8cd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 42ec3e6db31d1d9c723509c200a36b41db10a95023374d9a6506704c344e8306090680c1524c08f400a78967f067085e24fb90aaa8dd4fe1014a97a4eb664d32
|
7
|
+
data.tar.gz: 92577fbd8cde9a481648fffeca5301401e19353fc5d85149e6b19eb8590811093f1f7ba9fd3ccb0c96fa960ab6af4457dd48d62269c53eb184366484985225f7
|
@@ -0,0 +1,40 @@
|
|
1
|
+
name: Development
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
test:
|
7
|
+
strategy:
|
8
|
+
fail-fast: false
|
9
|
+
matrix:
|
10
|
+
os: [ubuntu-latest, macos-latest]
|
11
|
+
ruby: [2.3, 2.4, 2.5, 2.6, 2.7]
|
12
|
+
runs-on: ${{matrix.os}}
|
13
|
+
steps:
|
14
|
+
- uses: actions/checkout@v2
|
15
|
+
|
16
|
+
- uses: ruby/setup-ruby@v1
|
17
|
+
with:
|
18
|
+
ruby-version: ${{matrix.ruby}}
|
19
|
+
|
20
|
+
- uses: actions/cache@v1
|
21
|
+
with:
|
22
|
+
path: vendor/bundle
|
23
|
+
key: bundle-use-ruby-${{matrix.os}}-${{matrix.ruby}}-${{hashFiles('**/Gemfile')}}
|
24
|
+
restore-keys: |
|
25
|
+
bundle-use-ruby-${{matrix.os}}-${{matrix.ruby}}-
|
26
|
+
|
27
|
+
- name: Installing packages (ubuntu)
|
28
|
+
if: matrix.os == 'ubuntu-latest'
|
29
|
+
run: sudo apt-get install rsync
|
30
|
+
|
31
|
+
- name: Installing packages (macos)
|
32
|
+
if: matrix.os == 'macos-latest'
|
33
|
+
run: brew install rsync
|
34
|
+
|
35
|
+
- name: Bundle install...
|
36
|
+
run: |
|
37
|
+
bundle config path vendor/bundle
|
38
|
+
bundle install
|
39
|
+
|
40
|
+
- run: bundle exec rake
|
data/.rspec
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Synco
|
1
|
+
# Synco 
|
2
2
|
|
3
3
|
Synco is a tool for scripted synchronization and backups. It provides a custom Ruby DSL for describing backup and synchronization tasks involving one more more system and disk. It is designed to provide flexibility while reducing the complexity multi-server backups.
|
4
4
|
|
@@ -8,10 +8,6 @@ Synco is a tool for scripted synchronization and backups. It provides a custom R
|
|
8
8
|
* Backup verification using [Fingerprint](https://github.com/ioquatix/fingerprint).
|
9
9
|
* Data backup redundancy controlled via DNS.
|
10
10
|
|
11
|
-
[](http://travis-ci.org/ioquatix/synco)
|
12
|
-
[](https://codeclimate.com/github/ioquatix/synco)
|
13
|
-
[](https://coveralls.io/r/ioquatix/synco)
|
14
|
-
|
15
11
|
## Installation
|
16
12
|
|
17
13
|
Add this line to your application's Gemfile:
|
@@ -32,28 +28,30 @@ Synco imposes a particular structure regarding the organisation of backup script
|
|
32
28
|
|
33
29
|
A simple backup script might look something like this:
|
34
30
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
backup('etc', 'var', 'srv', 'home',
|
54
|
-
arguments: %W{--exclude cache/ --exclude tmp/}
|
55
|
-
)
|
31
|
+
```ruby
|
32
|
+
#!/usr/bin/env ruby
|
33
|
+
|
34
|
+
require 'synco'
|
35
|
+
require 'synco/methods/rsync'
|
36
|
+
|
37
|
+
Synco::run_script do |script|
|
38
|
+
script.method = Synco::Methods::RSync.new(archive: true)
|
39
|
+
|
40
|
+
server(:master) do |server|
|
41
|
+
server.host = "server.example.com"
|
42
|
+
server.root = "/"
|
43
|
+
end
|
44
|
+
|
45
|
+
server(:backup) do |server|
|
46
|
+
server.host = "backup.example.com"
|
47
|
+
server.root = "/tank/backups/server.example.com"
|
56
48
|
end
|
49
|
+
|
50
|
+
backup('etc', 'var', 'srv', 'home',
|
51
|
+
arguments: %W{--exclude cache/ --exclude tmp/}
|
52
|
+
)
|
53
|
+
end
|
54
|
+
```
|
57
55
|
|
58
56
|
This will produce an identical copy using rsync of the specified directories. As an example `server.example.com:/etc` would be copied to `backup.example.com:/tank/backups/server.example.com/etc`.
|
59
57
|
|
@@ -61,61 +59,69 @@ This will produce an identical copy using rsync of the specified directories. As
|
|
61
59
|
|
62
60
|
Building on the above backup, you can use `Synco::Methods::RSyncSnapshot` which supports snapshot based backups. It creates a snapshot into a sub-directory called `latest.snapshot` and uses RSync's `--link-dest` to hard-link files when unchanged. Synco provides scripts to rotate and prune these backups as required, but you must invoke them as part of the script:
|
63
61
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
62
|
+
```ruby
|
63
|
+
server(:backup) do |server|
|
64
|
+
server.host = "backup.example.com"
|
65
|
+
server.root = "/"
|
66
|
+
|
67
|
+
server.on(:success) do
|
68
|
+
run "synco", "rotate", chdir: target_server.root
|
69
|
+
run "synco", "prune", chdir: target_server.root
|
72
70
|
end
|
71
|
+
end
|
72
|
+
```
|
73
73
|
|
74
74
|
These commands can also be run from the command line.
|
75
75
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
76
|
+
```
|
77
|
+
rotate [--format <name>] [--latest <name>] [--snapshot <name>]
|
78
|
+
Rotate a backup snapshot into a timestamped directory.
|
79
|
+
|
80
|
+
[--format <name>] Set the name of the backup rotations, including strftime expansions. Default: %Y.%m.%d-%H.%M.%S
|
81
|
+
[--latest <name>] The name of the latest backup symlink. Default: latest
|
82
|
+
[--snapshot <name>] The name of the in-progress backup snapshot. Default: latest.snapshot
|
83
|
+
|
84
|
+
prune [--hourly <count>] [--daily <count>] [--weekly <count>] [--monthly <count>] [--quarterly <count>] [--yearly <count>] [--format <name>] [--latest <name>] [--keep <new|old>] [--dry]
|
85
|
+
Prune old backups to reduce disk usage according to a given policy.
|
86
|
+
|
87
|
+
[--hourly <count>] Set the number of hourly backups to keep. Default: 24
|
88
|
+
[--daily <count>] Set the number of daily backups to keep. Default: 28
|
89
|
+
[--weekly <count>] Set the number of weekly backups to keep. Default: 52
|
90
|
+
[--monthly <count>] Set the number of monthly backups to keep. Default: 36
|
91
|
+
[--quarterly <count>] Set the number of quaterly backups to keep. Default: 40
|
92
|
+
[--yearly <count>] Set the number of yearly backups to keep. Default: 20
|
93
|
+
[--format <name>] Set the name of the backup rotations, including strftime expansions. Default: %Y.%m.%d-%H.%M.%S
|
94
|
+
[--latest <name>] The name of the latest backup symlink. Default: latest
|
95
|
+
[--keep <new|old>] Keep the younger or older backups within the same period division Default: old
|
96
|
+
[--dry] Print out what would be done rather than doing it.
|
97
|
+
```
|
96
98
|
|
97
99
|
### Mounting Disks
|
98
100
|
|
99
101
|
Synco supports mounting disks before the backup begins and unmounting them after done. The specifics of this process may require some adjustment based on your OS. For example on linux, `sudo` is used to invoke `mount` and `umount`.
|
100
102
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
103
|
+
```ruby
|
104
|
+
server(:destination) do |server|
|
105
|
+
self.mountpoint = '/mnt/backups'
|
106
|
+
self.root = File.join(server.mountpoint, 'laptop')
|
107
|
+
|
108
|
+
server.on(:prepare) do
|
109
|
+
# synco mount uses labels, e.g. the disk partition has LABEL=backups
|
110
|
+
target_server.run "synco", "mount", target_server.mountpoint, 'backups'
|
111
|
+
end
|
112
|
+
|
113
|
+
server.on(:finish) do
|
114
|
+
target_server.run "synco", "unmount", target_server.mountpoint
|
113
115
|
end
|
116
|
+
end
|
117
|
+
```
|
114
118
|
|
115
119
|
On Linux, you might want to create the file `/etc/sudoers.d/synco` with the following contents:
|
116
120
|
|
117
|
-
|
118
|
-
|
121
|
+
```
|
122
|
+
%wheel ALL=(root) NOPASSWD: /bin/mount
|
123
|
+
%wheel ALL=(root) NOPASSWD: /bin/umount
|
124
|
+
```
|
119
125
|
|
120
126
|
Please make sure you take the time to educate yourself on the security of such a setup.
|
121
127
|
|
@@ -123,47 +129,53 @@ Please make sure you take the time to educate yourself on the security of such a
|
|
123
129
|
|
124
130
|
If you'd like to dump data before running the backup, it's possible using the event handling mechanisms:
|
125
131
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
132
|
+
```ruby
|
133
|
+
server(:master) do |server|
|
134
|
+
server.host = "server.example.com"
|
135
|
+
server.root = "/"
|
136
|
+
|
137
|
+
server.on(:prepare) do
|
138
|
+
# Dump MySQL to /srv/mysql
|
139
|
+
run '/etc/lsync/mysql-backup.sh'
|
134
140
|
end
|
141
|
+
end
|
142
|
+
```
|
135
143
|
|
136
144
|
The exact contents of `mysql-backup.sh` will depend on your requirements, but here is an example:
|
137
145
|
|
138
|
-
|
146
|
+
```bash
|
147
|
+
#!/usr/bin/env bash
|
139
148
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
149
|
+
BACKUP_DIR=/srv/mysql
|
150
|
+
MYSQL=/usr/bin/mysql
|
151
|
+
MYSQLDUMP=/usr/bin/mysqldump
|
152
|
+
|
153
|
+
databases=`mysql --user=backup -e "SHOW DATABASES;" | grep -Ev "(Database|information_schema|performance_schema|_test|_restore)"`
|
154
|
+
|
155
|
+
# http://stackoverflow.com/questions/451404/how-to-obtain-a-correct-dump-using-mysqldump-and-single-transaction-when-ddl-is
|
156
|
+
MYSQLDUMP_OPTIONS="--force --skip-opt --single-transaction --add-drop-table --create-options --quick --extended-insert --set-charset --disable-keys"
|
157
|
+
|
158
|
+
for db in $databases; do
|
159
|
+
echo "Dumping database $db to $BACKUP_DIR/$db.sql.xz..."
|
160
|
+
mysqldump --user=backup $MYSQLDUMP_OPTIONS --databases $db | xz > "$BACKUP_DIR/$db.sql.xz"
|
161
|
+
done
|
162
|
+
```
|
153
163
|
|
154
164
|
### Fingerprint Integration
|
155
165
|
|
156
166
|
It is possible to make a [cryptographic checksum of the data](https://github.com/ioquatix/fingerprint). On a filesystem that support immutable snapshots, you can do this before the data is copied. For traditional filesystems, you generally need to do this afterwards.
|
157
167
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
168
|
+
```ruby
|
169
|
+
server(:master) do |server|
|
170
|
+
server.host = "server.example.com"
|
171
|
+
server.root = "/"
|
172
|
+
|
173
|
+
server.on(:success) do
|
174
|
+
# Run fingerprint on the backup data:
|
175
|
+
run 'fingerprint', '--root', target_server.root, 'analyze'
|
166
176
|
end
|
177
|
+
end
|
178
|
+
```
|
167
179
|
|
168
180
|
Fingerprint is used in many of the specs to verify file copies.
|
169
181
|
|
@@ -173,11 +185,15 @@ Fingerprint is used in many of the specs to verify file copies.
|
|
173
185
|
|
174
186
|
Synco can manage synchronization and backups of ZFS partitions. However, to use the standard tools, it is necessary to enable `zfs_admin_snapshot`, in `/etc/modprobe.d/zfs.conf`:
|
175
187
|
|
176
|
-
|
188
|
+
```
|
189
|
+
options zfs zfs_admin_snapshot=1
|
190
|
+
```
|
177
191
|
|
178
192
|
Propagate user permissions for the ZFS partition:
|
179
193
|
|
180
|
-
|
194
|
+
```bash
|
195
|
+
sudo zfs allow -ld -u `whoami` create,mount,send,receive,snapshot tank/test
|
196
|
+
```
|
181
197
|
|
182
198
|
### Backup staging
|
183
199
|
|
@@ -193,23 +209,25 @@ Firstly, a backup script defaults to the server with the name `:master` as the m
|
|
193
209
|
|
194
210
|
However, it is possible instead to specify a hostname, e.g. `primary.example.com`. Then, specify several servers, e.g. `s01.example.com`, `s02.example.com` and so on:
|
195
211
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
server("s02.example.com") do |server|
|
206
|
-
server.root = "/"
|
207
|
-
end
|
208
|
-
|
209
|
-
backup('srv/http',
|
210
|
-
arguments: %W{--exclude cache/ --exclude tmp/}
|
211
|
-
)
|
212
|
+
```ruby
|
213
|
+
Synco::run_script do |script|
|
214
|
+
script.method = Synco::Methods::RSync.new(archive: true)
|
215
|
+
|
216
|
+
script.master = "primary.example.com"
|
217
|
+
|
218
|
+
server("s01.example.com") do |server|
|
219
|
+
server.root = "/"
|
212
220
|
end
|
221
|
+
|
222
|
+
server("s02.example.com") do |server|
|
223
|
+
server.root = "/"
|
224
|
+
end
|
225
|
+
|
226
|
+
backup('srv/http',
|
227
|
+
arguments: %W{--exclude cache/ --exclude tmp/}
|
228
|
+
)
|
229
|
+
end
|
230
|
+
```
|
213
231
|
|
214
232
|
When you run the script, the behaviour will depend on whether `primary.example.com` points to `s01.example.com` or `s02.example.com`. The data will always be copied from the master server to the other servers.
|
215
233
|
|
data/Rakefile
CHANGED
data/bin/synco
CHANGED
data/lib/synco/command.rb
CHANGED
@@ -48,21 +48,22 @@ module Synco
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
-
nested
|
51
|
+
nested :command, {
|
52
52
|
'spawn' => Spawn,
|
53
53
|
'rotate' => Rotate,
|
54
54
|
'prune' => Prune,
|
55
55
|
'mount' => Mount,
|
56
56
|
'unmount' => Unmount
|
57
|
+
}
|
57
58
|
|
58
|
-
def
|
59
|
+
def call
|
59
60
|
if @options[:version]
|
60
61
|
puts "synco v#{Synco::VERSION}"
|
61
62
|
elsif @options[:help] or @command.nil?
|
62
63
|
print_usage(program_name)
|
63
64
|
else
|
64
65
|
chdir do
|
65
|
-
@command.
|
66
|
+
@command.call
|
66
67
|
end
|
67
68
|
end
|
68
69
|
end
|
data/lib/synco/command/disk.rb
CHANGED
@@ -34,7 +34,7 @@ module Synco
|
|
34
34
|
one :path, "The disk mount point."
|
35
35
|
one :name, "The symbolic name of the disk to mount, e.g. disk label."
|
36
36
|
|
37
|
-
def
|
37
|
+
def call
|
38
38
|
# We may not have permission to make this directory, but we should still try:
|
39
39
|
FileUtils.mkpath(@path) rescue nil
|
40
40
|
|
@@ -47,7 +47,7 @@ module Synco
|
|
47
47
|
|
48
48
|
one :path, "The disk mount point."
|
49
49
|
|
50
|
-
def
|
50
|
+
def call
|
51
51
|
Disk.unmount(@path)
|
52
52
|
end
|
53
53
|
end
|
data/lib/synco/command/prune.rb
CHANGED
data/lib/synco/command/rotate.rb
CHANGED
data/lib/synco/command/spawn.rb
CHANGED
data/lib/synco/scope.rb
CHANGED
@@ -22,7 +22,7 @@ require_relative 'script'
|
|
22
22
|
|
23
23
|
require 'process/group'
|
24
24
|
|
25
|
-
require '
|
25
|
+
require 'console'
|
26
26
|
require 'delegate'
|
27
27
|
|
28
28
|
module Synco
|
@@ -39,22 +39,13 @@ module Synco
|
|
39
39
|
end
|
40
40
|
|
41
41
|
class Runner
|
42
|
-
|
42
|
+
include Console
|
43
|
+
|
44
|
+
def initialize(*scripts)
|
43
45
|
@scripts = scripts
|
44
|
-
|
45
|
-
@logger = logger || Logger.new($stderr).tap do |logger|
|
46
|
-
logger.formatter = CompactFormatter.new
|
47
|
-
|
48
|
-
if verbose or ENV['SYNCO_VERBOSE']
|
49
|
-
logger.level = Logger::DEBUG
|
50
|
-
else
|
51
|
-
logger.level = Logger::INFO
|
52
|
-
end
|
53
|
-
end
|
54
46
|
end
|
55
47
|
|
56
48
|
attr :scripts
|
57
|
-
attr :logger
|
58
49
|
|
59
50
|
def call
|
60
51
|
start_time = Time.now
|
@@ -64,7 +55,7 @@ module Synco
|
|
64
55
|
Process::Group.wait do |group|
|
65
56
|
@scripts.each do |script|
|
66
57
|
Fiber.new do
|
67
|
-
ScriptScope.new(script,
|
58
|
+
ScriptScope.new(script, logger, group).call
|
68
59
|
end.resume
|
69
60
|
end
|
70
61
|
end
|
data/lib/synco/script.rb
CHANGED
data/lib/synco/version.rb
CHANGED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# Copyright, 2016, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require 'bundler/setup'
|
22
|
+
require 'covered/rspec'
|
23
|
+
|
24
|
+
RSpec.configure do |config|
|
25
|
+
# Enable flags like --only-failures and --next-failure
|
26
|
+
config.example_status_persistence_file_path = ".rspec_status"
|
27
|
+
|
28
|
+
# Disable RSpec exposing methods globally on `Module` and `main`
|
29
|
+
config.disable_monkey_patching!
|
30
|
+
|
31
|
+
config.expect_with :rspec do |c|
|
32
|
+
c.syntax = :expect
|
33
|
+
end
|
34
|
+
end
|
data/spec/synco/method_spec.rb
CHANGED
data/spec/synco/rsync_spec.rb
CHANGED
@@ -27,7 +27,7 @@ require 'synco/methods/rsync'
|
|
27
27
|
|
28
28
|
require_relative 'backup_script'
|
29
29
|
|
30
|
-
describe Synco::Methods::RSync do
|
30
|
+
RSpec.describe Synco::Methods::RSync do
|
31
31
|
include_context "backup script"
|
32
32
|
|
33
33
|
it 'should copy files using rsync' do
|
@@ -81,7 +81,7 @@ describe Synco::Methods::RSync do
|
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
|
-
describe Synco::Methods::RSyncSnapshot do
|
84
|
+
RSpec.describe Synco::Methods::RSyncSnapshot do
|
85
85
|
it 'should generate correct command for ssh shell' do
|
86
86
|
expect(subject.escape(['ssh', '-o', 'BatchMode=yes'])).to be == 'ssh -o BatchMode=yes'
|
87
87
|
end
|
data/spec/synco/scp_spec.rb
CHANGED
data/spec/synco/script_spec.rb
CHANGED
@@ -24,7 +24,7 @@ require 'logger'
|
|
24
24
|
require 'synco/script'
|
25
25
|
require 'synco/methods/scp'
|
26
26
|
|
27
|
-
describe Synco::Script do
|
27
|
+
RSpec.describe Synco::Script do
|
28
28
|
it 'should build a script with desired configuration' do
|
29
29
|
script = Synco::Script.build do |script|
|
30
30
|
script.server(:master) do |server|
|
data/spec/synco/shell_spec.rb
CHANGED
@@ -23,20 +23,18 @@
|
|
23
23
|
require 'synco/server'
|
24
24
|
require 'synco/shells/ssh'
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
expect(subject.connection_command(server)).to be == ['ssh', 'localhost']
|
32
|
-
end
|
26
|
+
RSpec.describe Synco::Shells::SSH do
|
27
|
+
let(:server) {Synco::Server.new('localhost')}
|
28
|
+
|
29
|
+
it 'should generate a connection command' do
|
30
|
+
expect(subject.connection_command(server)).to be == ['ssh', 'localhost']
|
33
31
|
end
|
32
|
+
end
|
33
|
+
|
34
|
+
RSpec.describe Synco::Shells::SSH.new(batch_mode: true) do
|
35
|
+
let(:server) {Synco::Server.new('localhost')}
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
it 'should generate a connection command with options' do
|
39
|
-
expect(subject.connection_command(server)).to be == ['ssh', '-o', 'BatchMode=yes', 'localhost']
|
40
|
-
end
|
37
|
+
it 'should generate a connection command with options' do
|
38
|
+
expect(subject.connection_command(server)).to be == ['ssh', '-o', 'BatchMode=yes', 'localhost']
|
41
39
|
end
|
42
40
|
end
|
data/spec/synco/usb_spec.rb
CHANGED
@@ -24,7 +24,7 @@ require 'synco/disk'
|
|
24
24
|
require 'synco/methods/rsync'
|
25
25
|
require_relative 'backup_script'
|
26
26
|
|
27
|
-
describe Synco::Methods::RSyncSnapshot, if: Synco::Disk.available?('TEST') do
|
27
|
+
RSpec.describe Synco::Methods::RSyncSnapshot, if: Synco::Disk.available?('TEST') do
|
28
28
|
include_context "backup script"
|
29
29
|
|
30
30
|
before(:each) do
|
data/spec/synco/zfs_spec.rb
CHANGED
@@ -25,7 +25,7 @@ require 'synco/script'
|
|
25
25
|
require 'synco/scope'
|
26
26
|
require 'synco/methods/zfs'
|
27
27
|
|
28
|
-
describe Synco::Methods::ZFS do
|
28
|
+
RSpec.describe Synco::Methods::ZFS do
|
29
29
|
xit 'should mirror two ZFS partitions' do
|
30
30
|
script = Synco::Script.build(master: :source) do |script|
|
31
31
|
script.method = Synco::Methods::ZFS.new
|
data/synco.gemspec
CHANGED
@@ -1,7 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'synco/version'
|
1
|
+
|
2
|
+
require_relative 'lib/synco/version'
|
5
3
|
|
6
4
|
Gem::Specification.new do |spec|
|
7
5
|
spec.name = "synco"
|
@@ -11,25 +9,23 @@ Gem::Specification.new do |spec|
|
|
11
9
|
spec.summary = %q{Synco is a tool for scripted synchronization and backups.}
|
12
10
|
spec.homepage = ""
|
13
11
|
spec.license = "MIT"
|
14
|
-
|
12
|
+
|
15
13
|
spec.files = `git ls-files -z`.split("\x0")
|
16
14
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
15
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
16
|
spec.require_paths = ["lib"]
|
19
|
-
|
17
|
+
|
20
18
|
spec.add_dependency("periodical", "~> 1.1")
|
21
|
-
spec.add_dependency("samovar", "~>
|
22
|
-
spec.add_dependency("fingerprint", "~>
|
19
|
+
spec.add_dependency("samovar", "~> 2.0")
|
20
|
+
spec.add_dependency("fingerprint", "~> 3.0")
|
23
21
|
spec.add_dependency("mapping", "~> 1.0")
|
24
22
|
spec.add_dependency("build-files", "~> 1.0")
|
25
23
|
spec.add_dependency("process-group", "~> 1.1")
|
26
24
|
|
27
25
|
spec.add_dependency("lockfile")
|
28
|
-
|
29
|
-
|
30
|
-
spec.
|
31
|
-
|
32
|
-
spec.add_development_dependency "bundler", "~> 1.11"
|
26
|
+
|
27
|
+
spec.add_development_dependency "covered"
|
28
|
+
spec.add_development_dependency "bundler"
|
33
29
|
spec.add_development_dependency "rspec", "~> 3.4"
|
34
30
|
spec.add_development_dependency "rake"
|
35
31
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: synco
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-02-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: periodical
|
@@ -30,28 +30,28 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '2.0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '2.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: fingerprint
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '3.0'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '3.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: mapping
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -109,33 +109,33 @@ dependencies:
|
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
112
|
+
name: covered
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- - "
|
115
|
+
- - ">="
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: '
|
118
|
-
type: :
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- - "
|
122
|
+
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: '
|
124
|
+
version: '0'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: bundler
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
|
-
- - "
|
129
|
+
- - ">="
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: '
|
131
|
+
version: '0'
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
|
-
- - "
|
136
|
+
- - ">="
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: '
|
138
|
+
version: '0'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: rspec
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -172,10 +172,9 @@ executables:
|
|
172
172
|
extensions: []
|
173
173
|
extra_rdoc_files: []
|
174
174
|
files:
|
175
|
+
- ".github/workflows/development.yml"
|
175
176
|
- ".gitignore"
|
176
177
|
- ".rspec"
|
177
|
-
- ".simplecov"
|
178
|
-
- ".travis.yml"
|
179
178
|
- Gemfile
|
180
179
|
- README.md
|
181
180
|
- Rakefile
|
@@ -186,7 +185,6 @@ files:
|
|
186
185
|
- lib/synco/command/prune.rb
|
187
186
|
- lib/synco/command/rotate.rb
|
188
187
|
- lib/synco/command/spawn.rb
|
189
|
-
- lib/synco/compact_formatter.rb
|
190
188
|
- lib/synco/controller.rb
|
191
189
|
- lib/synco/directory.rb
|
192
190
|
- lib/synco/disk.rb
|
@@ -200,10 +198,7 @@ files:
|
|
200
198
|
- lib/synco/shell.rb
|
201
199
|
- lib/synco/shells/ssh.rb
|
202
200
|
- lib/synco/version.rb
|
203
|
-
-
|
204
|
-
- media/LSync Logo.artx/QuickLook/Preview.pdf
|
205
|
-
- media/LSync Logo.artx/doc.thread
|
206
|
-
- media/LSync Logo.png
|
201
|
+
- spec/spec_helper.rb
|
207
202
|
- spec/synco/backup_script.rb
|
208
203
|
- spec/synco/directory_spec.rb
|
209
204
|
- spec/synco/local_backup.rb
|
@@ -235,12 +230,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
235
230
|
- !ruby/object:Gem::Version
|
236
231
|
version: '0'
|
237
232
|
requirements: []
|
238
|
-
|
239
|
-
rubygems_version: 2.5.1
|
233
|
+
rubygems_version: 3.0.6
|
240
234
|
signing_key:
|
241
235
|
specification_version: 4
|
242
236
|
summary: Synco is a tool for scripted synchronization and backups.
|
243
237
|
test_files:
|
238
|
+
- spec/spec_helper.rb
|
244
239
|
- spec/synco/backup_script.rb
|
245
240
|
- spec/synco/directory_spec.rb
|
246
241
|
- spec/synco/local_backup.rb
|
data/.simplecov
DELETED
data/.travis.yml
DELETED
@@ -1,115 +0,0 @@
|
|
1
|
-
# Copyright, 2016, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
-
#
|
3
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
-
# of this software and associated documentation files (the "Software"), to deal
|
5
|
-
# in the Software without restriction, including without limitation the rights
|
6
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
-
# copies of the Software, and to permit persons to whom the Software is
|
8
|
-
# furnished to do so, subject to the following conditions:
|
9
|
-
#
|
10
|
-
# The above copyright notice and this permission notice shall be included in
|
11
|
-
# all copies or substantial portions of the Software.
|
12
|
-
#
|
13
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
-
# THE SOFTWARE.
|
20
|
-
|
21
|
-
require 'rainbow'
|
22
|
-
require 'shellwords'
|
23
|
-
|
24
|
-
module Synco
|
25
|
-
class CompactFormatter
|
26
|
-
def initialize(verbose: true)
|
27
|
-
@start = Time.now
|
28
|
-
@verbose = verbose
|
29
|
-
end
|
30
|
-
|
31
|
-
def time_offset_prefix
|
32
|
-
offset = Time.now - @start
|
33
|
-
minutes = (offset/60).floor
|
34
|
-
seconds = (offset - (minutes*60))
|
35
|
-
|
36
|
-
if minutes > 0
|
37
|
-
"#{minutes}m#{seconds.floor}s"
|
38
|
-
else
|
39
|
-
"#{seconds.round(2)}s"
|
40
|
-
end.rjust(6)
|
41
|
-
end
|
42
|
-
|
43
|
-
def chdir_string(options)
|
44
|
-
if options[:chdir]
|
45
|
-
" in #{options[:chdir]}"
|
46
|
-
else
|
47
|
-
""
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def format_command(arguments, buffer)
|
52
|
-
arguments = arguments.dup
|
53
|
-
|
54
|
-
# environment = arguments.first.is_a?(Hash) ? arguments.shift : nil
|
55
|
-
options = arguments.last.is_a?(Hash) ? arguments.pop : nil
|
56
|
-
|
57
|
-
arguments = arguments.flatten.collect(&:to_s)
|
58
|
-
|
59
|
-
buffer << Rainbow(arguments.shelljoin).bright.blue
|
60
|
-
|
61
|
-
if options
|
62
|
-
buffer << chdir_string(options)
|
63
|
-
end
|
64
|
-
|
65
|
-
buffer << "\n"
|
66
|
-
|
67
|
-
# if environment
|
68
|
-
# environment.each do |key,value|
|
69
|
-
# buffer << "\texport #{key}=#{value.dump}\n"
|
70
|
-
# end
|
71
|
-
# end
|
72
|
-
end
|
73
|
-
|
74
|
-
def format_exception(exception, buffer)
|
75
|
-
buffer << Rainbow("#{exception.class}: #{exception}").bright.red << "\n"
|
76
|
-
exception.backtrace.each do |line|
|
77
|
-
buffer << "\t" << Rainbow(line).red << "\n"
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def format_line(message, severity)
|
82
|
-
if severity == 'ERROR'
|
83
|
-
Rainbow(message).red
|
84
|
-
else
|
85
|
-
message
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def call(severity, datetime, progname, message)
|
90
|
-
buffer = []
|
91
|
-
prefix = ""
|
92
|
-
|
93
|
-
if @verbose
|
94
|
-
prefix = time_offset_prefix
|
95
|
-
buffer << Rainbow(prefix).cyan + ": "
|
96
|
-
prefix = " " * (prefix.size) + "| "
|
97
|
-
end
|
98
|
-
|
99
|
-
if progname == 'shell' and message.kind_of? Array
|
100
|
-
format_command(message, buffer)
|
101
|
-
elsif message.kind_of? Exception
|
102
|
-
format_exception(message, buffer)
|
103
|
-
else
|
104
|
-
buffer << format_line(message, severity) << "\n"
|
105
|
-
end
|
106
|
-
|
107
|
-
result = buffer.join
|
108
|
-
|
109
|
-
# This fancy regex indents lines correctly depending on the prefix:
|
110
|
-
result.gsub!(/\n(?!$)/, "\n#{prefix}") unless prefix.empty?
|
111
|
-
|
112
|
-
return result
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
Binary file
|
Binary file
|
Binary file
|
data/media/LSync Logo.png
DELETED
Binary file
|