dorkbox 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+ require 'dorkbox'
3
+
4
+ require 'optparse'
5
+ require 'pp'
6
+
7
+ # This hash will hold all of the options
8
+ # parsed from the command-line by
9
+ # OptionParser.
10
+ options = {}
11
+
12
+ optparse = OptionParser.new do |opts|
13
+ # TODO: Put command-line options here
14
+
15
+ # This displays the help screen, all programs are
16
+ # assumed to have this option.
17
+ opts.on('-h', '--help', 'Display this screen') do
18
+ puts opts
19
+ exit
20
+ end
21
+ end
22
+ optparse.parse!
23
+
24
+ command = ARGV[0]
25
+ ARGV.shift
26
+
27
+ VALID_COMMANDS = ['create', 'connect', 'sync', 'sync_tracked', 'cleanup_tracked']
28
+
29
+ VALID_COMMANDS.include?(command) or exit(1)
30
+ ## TODO: something like dorkbox clone with remote
31
+
32
+ Dorkbox::send(command.to_sym, *ARGV)
@@ -0,0 +1,7 @@
1
+ require 'minitest/unit'
2
+ require 'minitest/spec'
3
+ require 'minitest/mock'
4
+
5
+ require 'dorkbox_test'
6
+
7
+ MiniTest::Unit.autorun
@@ -0,0 +1,243 @@
1
+ #!/usr/bin/env ruby
2
+ require 'open3'
3
+ require 'io/wait'
4
+
5
+ module BashLike
6
+ ALWAYS_PRINT_STDOUT=false
7
+ ALWAYS_ECHO_CMDLINE=true
8
+ EXCEPTION_ON_NONZERO_STATUS=true
9
+ LOGFILE=nil
10
+ LOGFILE_MAX_LINES=5000
11
+
12
+ def `(cmdline)
13
+ c(cmdline, true)
14
+ return $exit == 0 ? true : false
15
+ end
16
+
17
+ def c(cmdline, print_each_line=ALWAYS_PRINT_STDOUT)
18
+ $stdout.puts "+ #{cmdline}" if ALWAYS_ECHO_CMDLINE
19
+ Open3.popen3(cmdline) { |stdin, stdout, stderr, wait_thr|
20
+ all_out = ''
21
+ all_err = ''
22
+ while wait_thr.alive?
23
+ if !(stderr.ready? || stdout.ready?)
24
+ sleep(0.1)
25
+ next
26
+ end
27
+ current_out = stdout.read(stdout.nread)
28
+ current_err = stderr.read(stderr.nread)
29
+ all_out = all_out + current_out
30
+ all_err = all_err + current_err
31
+ $stdout.puts current_out if (print_each_line && !current_out.empty?)
32
+ $stderr.puts current_err if !current_err.empty?
33
+ end
34
+ status = wait_thr.value
35
+ # do it once more to catch everything that was buffered
36
+ current_out = stdout.read()
37
+ current_err = stderr.read()
38
+ all_out = all_out + current_out
39
+ all_err = all_err + current_err
40
+ $stdout.puts current_out if (print_each_line && !current_out.empty?)
41
+ $stderr.puts current_err if !current_err.empty?
42
+ $out = all_out
43
+ $err = all_err
44
+ $exit = status.exitstatus
45
+ raise StandardError.new("ERROR: #{cmdline.split()[0]} -> exit status #{status.exitstatus}") if (status.exitstatus != 0 && EXCEPTION_ON_NONZERO_STATUS)
46
+ all_out
47
+ }
48
+ end
49
+
50
+ def log(message)
51
+ if $global_log.nil? and !LOGFILE.nil?
52
+ # remove the exceeding lines
53
+ if File.exists?(LOGFILE)
54
+ f = File.open(LOGFILE, "r")
55
+ lines = f.readlines()
56
+ retain_from = [lines.size, LOGFILE_MAX_LINES].min
57
+ retain_lines = lines[-retain_from..-1]
58
+ f.close()
59
+ else
60
+ retain_lines=[]
61
+ end
62
+ $global_log = File.open(LOGFILE, "w")
63
+ $global_log.write(retain_lines.join(''))
64
+ end
65
+
66
+ final_message = "[#{Time.now.to_s}] #{message}"
67
+ $global_log.puts(final_message) unless $global_log.nil?
68
+ $stdout.puts(final_message)
69
+ end
70
+
71
+ end
72
+
73
+ include BashLike
74
+ # BASHINGTHEBASH END - write your things down there
75
+
76
+ USAGE = '
77
+ create: create new repository with passed remote, that should exist and be empty.
78
+ connect: connect to existing repository
79
+ sync: sync current repository
80
+ sync_tracked: sync all tracked repositories
81
+ cleanup_tracked: cleanup tracked repositories that don''t exist anymore
82
+
83
+ MISSING:
84
+ notification on conflict
85
+ re-track repository (if moved)
86
+ refactoring for better architecture
87
+ '
88
+
89
+
90
+ require 'socket'
91
+ require 'securerandom'
92
+ require 'yaml'
93
+ require 'tempfile'
94
+
95
+ module Dorkbox
96
+
97
+ CONFLICT_STRING='CONFLICT_MUST_MANUALLY_MERGE'
98
+ GITIGNORE='.gitignore'
99
+ DORKBOX_CONFIG_PATH = File.join(Dir.home, ".dorkbox.yml")
100
+ DORKBOX_CRONTAB_COMMENT = '# dorkbox sync cronjob'
101
+
102
+ def create_new_client_id
103
+ 'dorkbox-' + Socket.gethostname() + "-" + SecureRandom.urlsafe_base64(5)
104
+ end
105
+
106
+ def align_client_ref_to_master(dorkbox_client_id)
107
+ `git update-ref refs/heads/#{dorkbox_client_id} master`
108
+ end
109
+
110
+ def configure_client_id(dorkbox_client_id)
111
+ `git config --local dorkbox.client-id #{dorkbox_client_id}`
112
+ end
113
+
114
+ def retrieve_client_id
115
+ dorkbox_client_id = c("git config --get dorkbox.client-id").strip()
116
+ end
117
+
118
+ def enable_dorkbox_cronjob(current_path=File.expand_path(__FILE__))
119
+ cron_start = "#{DORKBOX_CRONTAB_COMMENT} start\n"
120
+ cron_end = "#{DORKBOX_CRONTAB_COMMENT} end\n"
121
+ old_crontab = c('crontab -l 2>/dev/null || /bin/true')
122
+ old_crontab.sub!(/#{cron_start}.*?#{cron_end}/m, '')
123
+
124
+ tmp = Tempfile.new("dorkbox-temp")
125
+ if (old_crontab.size > 0) && (old_crontab[-1] != "\n")
126
+ old_crontab.concat("\n")
127
+ end
128
+
129
+ old_crontab.concat(cron_start).concat("*/5 * * * * #{current_path}\n").concat(cron_end)
130
+ tmp.puts(old_crontab)
131
+ tmp.flush()
132
+ `crontab #{tmp.path}`
133
+ tmp.close()
134
+ end
135
+
136
+ # we should lock the config file on this.
137
+ def track
138
+ begin
139
+ cfg = YAML.load_file(DORKBOX_CONFIG_PATH)
140
+ rescue Errno::ENOENT
141
+ cfg = {:track => []}
142
+ end
143
+ cfg[:track].push(File.expand_path(Dir.pwd))
144
+ cfg[:track].uniq!
145
+ File.open(DORKBOX_CONFIG_PATH, 'w') { |f| f.write(cfg.to_yaml) }
146
+ end
147
+
148
+ def cleanup_tracked
149
+ begin
150
+ cfg = YAML.load_file(DORKBOX_CONFIG_PATH)
151
+ rescue Errno::ENOENT
152
+ return
153
+ end
154
+ # TODO: check for dorkbox-enabled dir, e.g. try retrieving client id
155
+ cfg[:track].select! { |d| Dir.exists?(d) }
156
+ File.open(DORKBOX_CONFIG_PATH, 'w') { |f| f.write(cfg.to_yaml) }
157
+ end
158
+
159
+ def sync_tracked
160
+ begin
161
+ cfg = YAML.load_file(DORKBOX_CONFIG_PATH)
162
+ rescue Errno::ENOENT
163
+ return
164
+ end
165
+ cfg[:track].each { |d| Dir.chdir(d) { sync() } }
166
+ end
167
+
168
+
169
+ def create(dorkbox_remote)
170
+ log "Will create new dorkbox-enabled repository in local directory. Remote #{dorkbox_remote} should exist and be empty."
171
+ if Dir.exists?('.git')
172
+ raise StandardError.new("git repository found.")
173
+ end
174
+ `git init`
175
+ File.open(GITIGNORE, 'a') { |f|
176
+ f.puts(CONFLICT_STRING)
177
+ }
178
+ `git remote add dorkbox #{dorkbox_remote}`
179
+ `git add #{GITIGNORE}`
180
+ `git commit -m 'enabling dorkbox'`
181
+ dorkbox_client_id = create_new_client_id
182
+ configure_client_id(dorkbox_client_id)
183
+ align_client_ref_to_master(dorkbox_client_id)
184
+ `git push -u dorkbox master #{dorkbox_client_id}`
185
+ track()
186
+ log "New dorkbox enabled with remote #{dorkbox_remote}"
187
+ dorkbox_client_id
188
+ end
189
+
190
+ def connect(dorkbox_remote)
191
+ log "Will create new git repo in local directory and connect to remote existing dorkbox repository #{dorkbox_remote}"
192
+ if Dir.exists?('.git')
193
+ raise StandardError.new("git repository found.")
194
+ end
195
+ `git init`
196
+ `git remote add dorkbox #{dorkbox_remote}`
197
+ `git fetch --all`
198
+ `git checkout master`
199
+ # TODO: check for hostname duplication and/or autogenerate client ids
200
+ dorkbox_client_id = create_new_client_id
201
+ configure_client_id(dorkbox_client_id)
202
+ align_client_ref_to_master(dorkbox_client_id)
203
+ `git push -u dorkbox master #{dorkbox_client_id}`
204
+ track()
205
+ dorkbox_client_id
206
+ end
207
+
208
+
209
+ def sync
210
+ if File.exists?(CONFLICT_STRING)
211
+ log "Conflict found, not syncing."
212
+ raise StandardError.new("Conflict found, not syncing.")
213
+ end
214
+ `git fetch --all`
215
+ `git add -A`
216
+ any_change = c("git diff --staged").strip()
217
+ if !any_change.empty?
218
+ `git commit -m "Automatic dorkbox commit"`
219
+ end
220
+ begin
221
+ dorkbox_client_id = retrieve_client_id
222
+ `git merge --ff-only dorkbox/master`
223
+ align_client_ref_to_master(dorkbox_client_id)
224
+ `git push dorkbox master #{dorkbox_client_id}`
225
+ rescue
226
+ # TODO: check which error actually happens and think about
227
+ # a solving strategy.
228
+ log "Error while syncing, stopping until solved."
229
+ FileUtils.touch(CONFLICT_STRING)
230
+ raise StandardError.new("Conflict found, syncing stopped.")
231
+ end
232
+ log "sync succeeded"
233
+ end
234
+ end
235
+
236
+
237
+
238
+
239
+
240
+
241
+
242
+
243
+
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dorkbox
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Alan Franzoni
9
+ autorequire:
10
+ bindir: executables
11
+ cert_chain: []
12
+ date: 2015-05-25 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: dead simple personal file syncing
15
+ email: username@franzoni.eu
16
+ executables:
17
+ - dorkbox
18
+ - test
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - lib/dorkbox.rb
23
+ - executables/dorkbox
24
+ - executables/test
25
+ homepage: https://github.com/alanfranz/dorkbox
26
+ licenses:
27
+ - Apache-2.0
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ required_ruby_version: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ requirements: []
45
+ rubyforge_project:
46
+ rubygems_version: 1.8.23
47
+ signing_key:
48
+ specification_version: 3
49
+ summary: ! 'dorkbox: dead simple personal file syncing'
50
+ test_files: []