Commit dab0c5b096c0c52ff8eaddd85c0f8b5df164e087

Authored by Göran Krampe
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":