Commit dab0c5b096c0c52ff8eaddd85c0f8b5df164e087
1 parent
5e811526
Added --version, -v, --help and made configuration more flexible.
Can have .blimp.conf in more locations, overrides work. Location of blimpstore added to .blimp.conf. Added config dump, made verbosity work correctly. Fixes #4.
Showing
1 changed file
with
113 additions
and
57 deletions
blimp.nim
@@ -15,34 +15,44 @@ import md5, os, osproc, parseopt2, strutils, parsecfg, streams, lapp, subexes | @@ -15,34 +15,44 @@ import md5, os, osproc, parseopt2, strutils, parsecfg, streams, lapp, subexes | ||
15 | # The file is copied over into: | 15 | # The file is copied over into: |
16 | # <homedir>/blimpStore/<originalfilename>-<md5sum> | 16 | # <homedir>/blimpStore/<originalfilename>-<md5sum> |
17 | # | 17 | # |
18 | -# Configuration is in: | 18 | +# Configuration is in these locations in order: |
19 | +# ./.blimp.conf | ||
19 | # <gitroot>/.blimp.conf | 20 | # <gitroot>/.blimp.conf |
20 | # ~/blimpstore/.blimp.conf | 21 | # ~/blimpstore/.blimp.conf |
22 | +# ~/.blimp.conf | ||
23 | + | ||
24 | +const | ||
25 | + versionMajor* = 0 | ||
26 | + versionMinor* = 2 | ||
27 | + versionPatch* = 1 | ||
28 | + versionAsString* = $versionMajor & "." & $versionMinor & "." & $versionPatch | ||
21 | 29 | ||
22 | var | 30 | var |
23 | - blimpStore, uploadCommandFormat, downloadCommandFormat, deleteCommandFormat: string | ||
24 | - remoteBlimpStore: string = nil | 31 | + blimpStore, remoteBlimpStore, uploadCommandFormat, downloadCommandFormat, deleteCommandFormat, rsyncPassword: string = nil |
25 | verbose: bool | 32 | verbose: bool |
26 | 33 | ||
27 | let | 34 | let |
28 | defaultConfig = """ | 35 | defaultConfig = """ |
29 | [rsync] | 36 | [rsync] |
30 | # Set this to your remote rsync daemon area | 37 | # Set this to your remote rsync daemon area |
31 | -remote = "blimp@some-rsync-server.com::blimpstore" | 38 | +remote = "blimpuser@some-rsync-server.com::blimpstore" |
39 | +password = "some-good-rsync-password-for-blimpuser" | ||
32 | 40 | ||
33 | # The following three formats should not need editing | 41 | # The following three formats should not need editing |
34 | # $1 is filename, $2 is remote and $3 is the local blimpstore | 42 | # $1 is filename, $2 is remote and $3 is the local blimpstore |
35 | -upload = "rsync --password-file ~/blimp.pass -avzP $3/$1 $2/" | ||
36 | -download = "rsync --password-file ~/blimp.pass -avzP $2/$1 $3/" | 43 | +upload = "rsync --password-file $3/.blimp.pass -avzP $3/$1 $2/" |
44 | +download = "rsync --password-file $3/.blimp.pass -avzP $2/$1 $3/" | ||
37 | # This deletes a single file from destination, that is already deleted in source | 45 | # This deletes a single file from destination, that is already deleted in source |
38 | -delete = "rsync --password-file ~/blimp.pass -dv --delete --existing --ignore-existing --include '$1' --exclude '*' $3/ $2" | 46 | +delete = "rsync --password-file $3/.blimp.pass -dv --delete --existing --ignore-existing --include '$1' --exclude '*' $3/ $2" |
39 | """ | 47 | """ |
40 | 48 | ||
41 | -# Load blimp.conf file, overkill for now but... | 49 | +# Load a blimp.conf file |
42 | proc parseConfFile(filename: string) = | 50 | proc parseConfFile(filename: string) = |
43 | var f = newFileStream(filename, fmRead) | 51 | var f = newFileStream(filename, fmRead) |
44 | if f != nil: | 52 | if f != nil: |
53 | + if verbose: echo "Reading config: " & filename | ||
45 | var p: CfgParser | 54 | var p: CfgParser |
55 | + | ||
46 | open(p, f, filename) | 56 | open(p, f, filename) |
47 | while true: | 57 | while true: |
48 | var e = next(p) | 58 | var e = next(p) |
@@ -53,14 +63,18 @@ proc parseConfFile(filename: string) = | @@ -53,14 +63,18 @@ proc parseConfFile(filename: string) = | ||
53 | continue # Ignore | 63 | continue # Ignore |
54 | of cfgKeyValuePair: | 64 | of cfgKeyValuePair: |
55 | case e.key | 65 | case e.key |
66 | + of "blimpstore": | ||
67 | + if blimpStore.isNil: blimpStore = e.value | ||
56 | of "remote": | 68 | of "remote": |
57 | - remoteBlimpStore = e.value | 69 | + if remoteBlimpStore.isNil: remoteBlimpStore = e.value |
70 | + of "password": | ||
71 | + if rsyncPassword.isNil: rsyncPassword = e.value | ||
58 | of "upload": | 72 | of "upload": |
59 | - uploadCommandFormat = e.value | 73 | + if uploadCommandFormat.isNil: uploadCommandFormat = e.value |
60 | of "download": | 74 | of "download": |
61 | - downloadCommandFormat = e.value | 75 | + if downloadCommandFormat.isNil: downloadCommandFormat = e.value |
62 | of "delete": | 76 | of "delete": |
63 | - deleteCommandFormat = e.value | 77 | + if deleteCommandFormat.isNil: deleteCommandFormat = e.value |
64 | else: | 78 | else: |
65 | quit("Unknown configuration: " & e.key) | 79 | quit("Unknown configuration: " & e.key) |
66 | of cfgOption: | 80 | of cfgOption: |
@@ -74,12 +88,19 @@ proc run(cmd: string): auto = | @@ -74,12 +88,19 @@ proc run(cmd: string): auto = | ||
74 | if verbose: echo(cmd) | 88 | if verbose: echo(cmd) |
75 | execCmd(cmd) | 89 | execCmd(cmd) |
76 | 90 | ||
91 | +# Every rsync command, make sure we have a password file | ||
92 | +proc rsyncRun(cmd: string): auto = | ||
93 | + writeFile(blimpStore / ".blimp.pass", rsyncPassword) | ||
94 | + if execCmd("chmod 600 " & blimpStore / ".blimp.pass") != 0: | ||
95 | + quit("Failed to chmod 600 " & blimpStore / ".blimp.pass") | ||
96 | + run(cmd) | ||
97 | + | ||
77 | # Upload a file to the remote master blimpStore | 98 | # Upload a file to the remote master blimpStore |
78 | proc uploadFile(blimpFilename: string) = | 99 | proc uploadFile(blimpFilename: string) = |
79 | if remoteBlimpStore.isNil: | 100 | if remoteBlimpStore.isNil: |
80 | echo("Remote blimpstore not set in configuration file, skipping uploading content:\n\t" & blimpFilename) | 101 | echo("Remote blimpstore not set in configuration file, skipping uploading content:\n\t" & blimpFilename) |
81 | return | 102 | return |
82 | - let errorCode = run(format(uploadCommandFormat, blimpFilename, remoteBlimpStore, blimpStore)) | 103 | + let errorCode = rsyncRun(format(uploadCommandFormat, blimpFilename, remoteBlimpStore, blimpStore)) |
83 | if errorCode != 0: | 104 | if errorCode != 0: |
84 | quit("Something went wrong uploading " & blimpFilename & " to " & remoteBlimpStore, 2) | 105 | quit("Something went wrong uploading " & blimpFilename & " to " & remoteBlimpStore, 2) |
85 | 106 | ||
@@ -87,7 +108,7 @@ proc uploadFile(blimpFilename: string) = | @@ -87,7 +108,7 @@ proc uploadFile(blimpFilename: string) = | ||
87 | proc downloadFile(blimpFilename: string) = | 108 | proc downloadFile(blimpFilename: string) = |
88 | if remoteBlimpStore.isNil: | 109 | if remoteBlimpStore.isNil: |
89 | quit("Remote blimpstore not set in configuration file, can not download content:\n\t" & blimpFilename) | 110 | quit("Remote blimpstore not set in configuration file, can not download content:\n\t" & blimpFilename) |
90 | - let errorCode = run(format(downloadCommandFormat, blimpFilename, remoteBlimpStore, blimpStore)) | 111 | + let errorCode = rsyncRun(format(downloadCommandFormat, blimpFilename, remoteBlimpStore, blimpStore)) |
91 | if errorCode != 0: | 112 | if errorCode != 0: |
92 | quit("Something went wrong downloading " & blimpFilename & " from " & remoteBlimpStore, 3) | 113 | quit("Something went wrong downloading " & blimpFilename & " from " & remoteBlimpStore, 3) |
93 | 114 | ||
@@ -95,7 +116,7 @@ proc downloadFile(blimpFilename: string) = | @@ -95,7 +116,7 @@ proc downloadFile(blimpFilename: string) = | ||
95 | proc remoteDeleteFile(blimpFilename: string) = | 116 | proc remoteDeleteFile(blimpFilename: string) = |
96 | if remoteBlimpStore.isNil: | 117 | if remoteBlimpStore.isNil: |
97 | return | 118 | return |
98 | - let errorCode = run(format(deleteCommandFormat, blimpFilename, remoteBlimpStore, blimpStore)) | 119 | + let errorCode = rsyncRun(format(deleteCommandFormat, blimpFilename, remoteBlimpStore, blimpStore)) |
99 | if errorCode != 0: | 120 | if errorCode != 0: |
100 | quit("Something went wrong deleting " & blimpFilename & " from " & remoteBlimpStore, 3) | 121 | quit("Something went wrong deleting " & blimpFilename & " from " & remoteBlimpStore, 3) |
101 | 122 | ||
@@ -130,36 +151,40 @@ proc blimpFileName(filename: string): string= | @@ -130,36 +151,40 @@ proc blimpFileName(filename: string): string= | ||
130 | 151 | ||
131 | # Get hash and compute blimpFilename | 152 | # Get hash and compute blimpFilename |
132 | proc computeBlimpFilename(filename: string): string = | 153 | proc computeBlimpFilename(filename: string): string = |
133 | - result = blimpFilename(filename) | ||
134 | - if result.isNil: | ||
135 | - var content: string | ||
136 | - try: | ||
137 | - content = readFile(filename) | ||
138 | - except: | ||
139 | - quit("Failed opening file: " & filename, 1) | ||
140 | - let hash = getMD5(content) | ||
141 | - result = filename & "-" & hash | 154 | + var content: string |
155 | + try: | ||
156 | + content = readFile(filename) | ||
157 | + except: | ||
158 | + quit("Failed opening file: " & filename, 1) | ||
159 | + let hash = getMD5(content) | ||
160 | + result = filename & "-" & hash | ||
142 | 161 | ||
143 | # Copy original file to blimpStore and replace with hash stub in git. | 162 | # Copy original file to blimpStore and replace with hash stub in git. |
144 | proc deflate(filename: string) = | 163 | proc deflate(filename: string) = |
145 | - let blimpFilename = computeBlimpFilename(filename) | ||
146 | - copyToBlimpStore(filename, blimpFilename) | ||
147 | - writeFile(filename, "hash:" & blimpFilename) | ||
148 | - echo("\t" & filename & " deflated.") | 164 | + if verbose: echo "Deflating " & filename |
165 | + var blimpFilename = blimpFilename(filename) | ||
166 | + if not blimpFilename.isNil: | ||
167 | + echo("\t" & filename & " is already deflated, skipping.") | ||
168 | + else: | ||
169 | + blimpFilename = computeBlimpFilename(filename) | ||
170 | + copyToBlimpStore(filename, blimpFilename) | ||
171 | + writeFile(filename, "hash:" & blimpFilename) | ||
172 | + if verbose: echo("\t" & filename & " deflated.") | ||
149 | 173 | ||
150 | proc isInBlimpStore(filename: string): bool = | 174 | proc isInBlimpStore(filename: string): bool = |
151 | let blimpFilename = blimpFilename(filename) | 175 | let blimpFilename = blimpFilename(filename) |
152 | if not blimpFilename.isNil: | 176 | if not blimpFilename.isNil: |
153 | return true | 177 | return true |
154 | - | 178 | + |
155 | # Parse out hash from hash stub and copy back original content from blimpStore. | 179 | # Parse out hash from hash stub and copy back original content from blimpStore. |
156 | proc inflate(filename: string) = | 180 | proc inflate(filename: string) = |
181 | + if verbose: echo "Inflating " & filename | ||
157 | let blimpFilename = blimpFilename(filename) | 182 | let blimpFilename = blimpFilename(filename) |
158 | if blimpFilename.isNil: | 183 | if blimpFilename.isNil: |
159 | - echo("\t" & filename & " is not deflated, skipping.", 5) | 184 | + echo("\t" & filename & " is not deflated, skipping.") |
160 | else: | 185 | else: |
161 | copyFromBlimpStore(blimpfilename, filename) | 186 | copyFromBlimpStore(blimpfilename, filename) |
162 | - echo("\t" & filename & " inflated.") | 187 | + if verbose: echo("\t" & filename & " inflated.") |
163 | 188 | ||
164 | # Inflates file first (if deflated) and then removes current content for it, | 189 | # Inflates file first (if deflated) and then removes current content for it, |
165 | # both locally and in remote. | 190 | # both locally and in remote. |
@@ -172,68 +197,99 @@ proc remove(filename: string) = | @@ -172,68 +197,99 @@ proc remove(filename: string) = | ||
172 | deleteFromBlimpStore(blimpfilename, filename) | 197 | deleteFromBlimpStore(blimpfilename, filename) |
173 | echo("\t" & filename & " content removed from blimpstore locally and remotely.") | 198 | echo("\t" & filename & " content removed from blimpstore locally and remotely.") |
174 | 199 | ||
175 | -# Find git root dir or fall back on current dir | 200 | +# Find git root dir or nil |
176 | proc gitRoot(): string = | 201 | proc gitRoot(): string = |
177 | try: | 202 | try: |
178 | let tup = execCmdEx("git rev-parse --show-toplevel") | 203 | let tup = execCmdEx("git rev-parse --show-toplevel") |
179 | if tup[1] == 0: | 204 | if tup[1] == 0: |
180 | - result = tup[0] | 205 | + result = strip(tup[0]) |
181 | else: | 206 | else: |
182 | - result = getCurrentDir() | 207 | + result = nil |
183 | except: | 208 | except: |
184 | - result = getCurrentDir() | 209 | + result = nil |
210 | + | ||
211 | +proc setupBlimpStore() = | ||
212 | + try: | ||
213 | + if not existsDir(blimpStore): | ||
214 | + createDir(blimpStore) | ||
215 | + except: | ||
216 | + quit("Could not create " & blimpStore & " directory.", 1) | ||
217 | + | ||
218 | + try: | ||
219 | + if not existsFile(blimpStore / ".blimp.conf"): | ||
220 | + writeFile(blimpStore / ".blimp.conf", defaultConfig) | ||
221 | + except: | ||
222 | + quit("Could not create .blimp.conf config file in " & blimpStore & " directory.", 1) | ||
223 | + | ||
224 | +proc dumpConfig() = | ||
225 | + echo "\nDump of configuration:" | ||
226 | + echo "\tblimpStore: " & blimpStore | ||
227 | + echo "\tremoteBlimpStore: " & remoteBlimpStore | ||
228 | + echo "\tuploadCommandFormat: " & uploadCommandFormat | ||
229 | + echo "\tdownloadCommandFormat: " & downloadCommandFormat | ||
230 | + echo "\tdeleteCommandFormat: " & deleteCommandFormat | ||
231 | + echo "\trsyncPassword: " & rsyncPassword | ||
232 | + echo "\n" | ||
185 | 233 | ||
186 | let help = """ | 234 | let help = """ |
187 | blimp [options] <command> <filenames...> | 235 | blimp [options] <command> <filenames...> |
236 | + -h,--help Show this | ||
237 | + --version Show version of blimp | ||
188 | -v,--verbose Verbosity | 238 | -v,--verbose Verbosity |
189 | - <command> (string) (d)eflate, (i)nflate, delete, (c)heck, (r)ecover | 239 | + <command> (string) (d)eflate, (i)nflate, remove |
190 | <filenames> (string...) One or more filepaths to inflate/deflate | 240 | <filenames> (string...) One or more filepaths to inflate/deflate |
191 | 241 | ||
192 | Edit ~/blimpstore/.blimp.conf or <gitroot>/.blimp.conf and set a proper | 242 | Edit ~/blimpstore/.blimp.conf or <gitroot>/.blimp.conf and set a proper |
193 | - remote and also create ~/blimp.pass with the proper rsync password to use. | 243 | + remote and the proper rsync password to use. |
194 | 244 | ||
195 | Deflate is run before you add the big file to the index for committing. | 245 | Deflate is run before you add the big file to the index for committing. |
196 | Deflate will replace the file contents with a hash, and copy the | 246 | Deflate will replace the file contents with a hash, and copy the |
197 | - real content to ~/blimpstore, and if configured also upload it to | 247 | + real content to your local blimpstore, and if configured also upload it to |
198 | remote, using rsync. | 248 | remote, using rsync. |
199 | 249 | ||
200 | Inflate will bring back the original content by copying from | 250 | Inflate will bring back the original content by copying from |
201 | - ~/blimpstore, and if its not there, first downloading from the remote. | 251 | + your local blimpstore, and if its not there, first downloading from the remote. |
202 | Use this whenever you need to work/edit the big file - in order to get | 252 | Use this whenever you need to work/edit the big file - in order to get |
203 | its real content. | 253 | its real content. |
204 | 254 | ||
205 | Remove (no single character shortcut) will remove the file(s) content | 255 | Remove (no single character shortcut) will remove the file(s) content |
206 | - both from the local ~/blimpstore and from the remote. This only removes | 256 | + both from the local blimpstore and from the remote. This only removes |
207 | the current content version, not older versions. The file itself is first | 257 | the current content version, not older versions. The file itself is first |
208 | inflated, if needed, and not deleted. This only "unblimps" the file. | 258 | inflated, if needed, and not deleted. This only "unblimps" the file. |
209 | """ | 259 | """ |
210 | 260 | ||
211 | ################################ main ##################################### | 261 | ################################ main ##################################### |
212 | 262 | ||
213 | -# Hardwired to "blimpstore" directory in home dir. | ||
214 | -blimpStore = getHomeDir() / "blimpstore" | 263 | +# Using lapp to get args, on parsing failure this will show usage automatically |
264 | +var args = parse(help) | ||
265 | +verbose = args["verbose"].asBool | ||
266 | + | ||
267 | +# Parse configuration files, may shadow and override each other | ||
268 | +parseConfFile(getCurrentDir() / ".blimp.conf") | ||
269 | +if not gitRoot().isNil: | ||
270 | + parseConfFile(gitRoot() / ".blimp.conf") | ||
215 | 271 | ||
216 | -# Make sure we have the dir, or create it. | ||
217 | -try: | ||
218 | - if not existsDir(blimpStore): | ||
219 | - createDir(blimpStore) | ||
220 | - if not existsFile(blimpStore / ".blimp.conf"): | ||
221 | - writeFile(blimpStore / ".blimp.conf", defaultConfig) | ||
222 | -except: | ||
223 | - quit("Could not create " & blimpStore & " directory.", 1) | 272 | +# If we haven't gotten a blimpstore yet, we set a default one |
273 | +if blimpStore.isNil: | ||
274 | + blimpStore = getHomeDir() / "blimpstore" | ||
224 | 275 | ||
276 | +if existsDir(blimpStore): | ||
277 | + parseConfFile(blimpStore / ".blimp.conf") | ||
278 | +parseConfFile(getHomeDir() / ".blimp.conf") | ||
225 | 279 | ||
226 | -# Parse configuration files if they exist | ||
227 | -# The one in gitroot overrides settings from the store. | ||
228 | -parseConfFile(blimpStore / ".blimp.conf") | ||
229 | -parseConfFile(gitRoot() / ".blimp.conf") | 280 | +if verbose: dumpConfig() |
230 | 281 | ||
282 | +# These two are special, they short out | ||
283 | +if args.showHelp: quit(help) | ||
284 | +if args.showVersion: quit("blimp version: " & versionAsString) | ||
231 | 285 | ||
232 | -# Using lapp to get args | ||
233 | -let args = parse(help) | ||
234 | let command = args["command"].asString | 286 | let command = args["command"].asString |
235 | let filenames = args["filenames"].asSeq | 287 | let filenames = args["filenames"].asSeq |
236 | -verbose = args["verbose"].asBool | 288 | + |
289 | +# Make sure the local blimpstore is setup. | ||
290 | +setupBlimpStore() | ||
291 | + | ||
292 | + | ||
237 | 293 | ||
238 | # Do the deed | 294 | # Do the deed |
239 | if command == "d" or command == "deflate": | 295 | if command == "d" or command == "deflate": |