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 15 # The file is copied over into:
16 16 # <homedir>/blimpStore/<originalfilename>-<md5sum>
17 17 #
18   -# Configuration is in:
  18 +# Configuration is in these locations in order:
  19 +# ./.blimp.conf
19 20 # <gitroot>/.blimp.conf
20 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 30 var
23   - blimpStore, uploadCommandFormat, downloadCommandFormat, deleteCommandFormat: string
24   - remoteBlimpStore: string = nil
  31 + blimpStore, remoteBlimpStore, uploadCommandFormat, downloadCommandFormat, deleteCommandFormat, rsyncPassword: string = nil
25 32 verbose: bool
26 33  
27 34 let
28 35 defaultConfig = """
29 36 [rsync]
30 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 41 # The following three formats should not need editing
34 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 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 50 proc parseConfFile(filename: string) =
43 51 var f = newFileStream(filename, fmRead)
44 52 if f != nil:
  53 + if verbose: echo "Reading config: " & filename
45 54 var p: CfgParser
  55 +
46 56 open(p, f, filename)
47 57 while true:
48 58 var e = next(p)
... ... @@ -53,14 +63,18 @@ proc parseConfFile(filename: string) =
53 63 continue # Ignore
54 64 of cfgKeyValuePair:
55 65 case e.key
  66 + of "blimpstore":
  67 + if blimpStore.isNil: blimpStore = e.value
56 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 72 of "upload":
59   - uploadCommandFormat = e.value
  73 + if uploadCommandFormat.isNil: uploadCommandFormat = e.value
60 74 of "download":
61   - downloadCommandFormat = e.value
  75 + if downloadCommandFormat.isNil: downloadCommandFormat = e.value
62 76 of "delete":
63   - deleteCommandFormat = e.value
  77 + if deleteCommandFormat.isNil: deleteCommandFormat = e.value
64 78 else:
65 79 quit("Unknown configuration: " & e.key)
66 80 of cfgOption:
... ... @@ -74,12 +88,19 @@ proc run(cmd: string): auto =
74 88 if verbose: echo(cmd)
75 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 98 # Upload a file to the remote master blimpStore
78 99 proc uploadFile(blimpFilename: string) =
79 100 if remoteBlimpStore.isNil:
80 101 echo("Remote blimpstore not set in configuration file, skipping uploading content:\n\t" & blimpFilename)
81 102 return
82   - let errorCode = run(format(uploadCommandFormat, blimpFilename, remoteBlimpStore, blimpStore))
  103 + let errorCode = rsyncRun(format(uploadCommandFormat, blimpFilename, remoteBlimpStore, blimpStore))
83 104 if errorCode != 0:
84 105 quit("Something went wrong uploading " & blimpFilename & " to " & remoteBlimpStore, 2)
85 106  
... ... @@ -87,7 +108,7 @@ proc uploadFile(blimpFilename: string) =
87 108 proc downloadFile(blimpFilename: string) =
88 109 if remoteBlimpStore.isNil:
89 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 112 if errorCode != 0:
92 113 quit("Something went wrong downloading " & blimpFilename & " from " & remoteBlimpStore, 3)
93 114  
... ... @@ -95,7 +116,7 @@ proc downloadFile(blimpFilename: string) =
95 116 proc remoteDeleteFile(blimpFilename: string) =
96 117 if remoteBlimpStore.isNil:
97 118 return
98   - let errorCode = run(format(deleteCommandFormat, blimpFilename, remoteBlimpStore, blimpStore))
  119 + let errorCode = rsyncRun(format(deleteCommandFormat, blimpFilename, remoteBlimpStore, blimpStore))
99 120 if errorCode != 0:
100 121 quit("Something went wrong deleting " & blimpFilename & " from " & remoteBlimpStore, 3)
101 122  
... ... @@ -130,36 +151,40 @@ proc blimpFileName(filename: string): string=
130 151  
131 152 # Get hash and compute blimpFilename
132 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 162 # Copy original file to blimpStore and replace with hash stub in git.
144 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 174 proc isInBlimpStore(filename: string): bool =
151 175 let blimpFilename = blimpFilename(filename)
152 176 if not blimpFilename.isNil:
153 177 return true
154   -
  178 +
155 179 # Parse out hash from hash stub and copy back original content from blimpStore.
156 180 proc inflate(filename: string) =
  181 + if verbose: echo "Inflating " & filename
157 182 let blimpFilename = blimpFilename(filename)
158 183 if blimpFilename.isNil:
159   - echo("\t" & filename & " is not deflated, skipping.", 5)
  184 + echo("\t" & filename & " is not deflated, skipping.")
160 185 else:
161 186 copyFromBlimpStore(blimpfilename, filename)
162   - echo("\t" & filename & " inflated.")
  187 + if verbose: echo("\t" & filename & " inflated.")
163 188  
164 189 # Inflates file first (if deflated) and then removes current content for it,
165 190 # both locally and in remote.
... ... @@ -172,68 +197,99 @@ proc remove(filename: string) =
172 197 deleteFromBlimpStore(blimpfilename, filename)
173 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 201 proc gitRoot(): string =
177 202 try:
178 203 let tup = execCmdEx("git rev-parse --show-toplevel")
179 204 if tup[1] == 0:
180   - result = tup[0]
  205 + result = strip(tup[0])
181 206 else:
182   - result = getCurrentDir()
  207 + result = nil
183 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 234 let help = """
187 235 blimp [options] <command> <filenames...>
  236 + -h,--help Show this
  237 + --version Show version of blimp
188 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 240 <filenames> (string...) One or more filepaths to inflate/deflate
191 241  
192 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 245 Deflate is run before you add the big file to the index for committing.
196 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 248 remote, using rsync.
199 249  
200 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 252 Use this whenever you need to work/edit the big file - in order to get
203 253 its real content.
204 254  
205 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 257 the current content version, not older versions. The file itself is first
208 258 inflated, if needed, and not deleted. This only "unblimps" the file.
209 259 """
210 260  
211 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 286 let command = args["command"].asString
235 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 294 # Do the deed
239 295 if command == "d" or command == "deflate":
... ...