Blame view

blimp.nim 5.73 KB
b9ad52ff   Göran Krampe   Added default con...
1
  import md5, os, osproc, parseopt2, strutils, parsecfg, streams, lapp, subexes
74272cdb   Göran Krampe   First commit
2
  
5fc367ad   Göran Krampe   Renamed to blimp,...
3
  # blimp is a little utility program for handling large files
74272cdb   Göran Krampe   First commit
4
  # in git repositories. Its inspired by git-fat and s3annex
5fc367ad   Göran Krampe   Renamed to blimp,...
5
6
  # but doesn't rely on S3 for storage, is a single binary without
  # need for Python, and has less features than git-fat. So far.
74272cdb   Göran Krampe   First commit
7
  #
5fc367ad   Göran Krampe   Renamed to blimp,...
8
9
10
11
  # Manual use:
  #
  # Use "blimp d mybigfile" to deflate it before commit.
  # Use "blimp i mybigfile" to inflate it back to original size.
74272cdb   Göran Krampe   First commit
12
13
14
15
  #
  # When deflated the file only has an md5sum string inside it.
  #
  # The file is copied over into:
5fc367ad   Göran Krampe   Renamed to blimp,...
16
17
18
19
20
  #  <homedir>/blimpStore/<originalfilename>-<md5sum>
  #
  # Configuration is in:
  #   <gitroot>/.blimp.conf
  #   ~/blimpstore/.blimp.conf
74272cdb   Göran Krampe   First commit
21
  
b9ad52ff   Göran Krampe   Added default con...
22
23
24
25
  var
    blimpStore, uploadCommandFormat, downloadCommandFormat: string
    remoteBlimpStore: string = nil
    verbose: bool
74272cdb   Göran Krampe   First commit
26
  
b9ad52ff   Göran Krampe   Added default con...
27
28
29
30
31
32
33
34
  let
    defaultConfig = """
  [rsync]
  remote = "blimp@some-rsync-server.com::blimpstore"
  # $1 is filename, $2 is remote and $3 is the local blimpstore
  upload = "rsync --password-file ~/blimp.pass -avzP $3/$1 $2/"
  download = "rsync -avzP $2/$1 $3/"
  """
74272cdb   Göran Krampe   First commit
35
  
5fc367ad   Göran Krampe   Renamed to blimp,...
36
  # Load blimp.conf file, overkill for now but...
74272cdb   Göran Krampe   First commit
37
  proc parseConfFile(filename: string) =
74272cdb   Göran Krampe   First commit
38
39
40
41
42
43
44
45
46
47
48
49
    var f = newFileStream(filename, fmRead)
    if f != nil:
      var p: CfgParser
      open(p, f, filename)
      while true:
        var e = next(p)
        case e.kind
        of cfgEof: 
          break
        of cfgSectionStart:
          continue # Ignore
        of cfgKeyValuePair:
b9ad52ff   Göran Krampe   Added default con...
50
51
          case e.key
          of "remote":
5fc367ad   Göran Krampe   Renamed to blimp,...
52
            remoteBlimpStore = e.value
b9ad52ff   Göran Krampe   Added default con...
53
54
55
56
          of "upload":
            uploadCommandFormat = e.value
          of "download":
            downloadCommandFormat = e.value
74272cdb   Göran Krampe   First commit
57
58
59
60
61
62
63
64
          else:
            quit("Unknown configuration: " & e.key)
        of cfgOption:
          quit("Unknown configuration: " & e.key)
        of cfgError:
          quit("Parsing " & filename & ": " & e.msg)
      close(p)
  
db95b901   Göran Krampe   Various fixes, mo...
65
66
67
68
  # Trivial helper to enable verbose
  proc run(cmd: string): auto =
    if verbose: echo(cmd)
    execCmd(cmd)
74272cdb   Göran Krampe   First commit
69
  
5fc367ad   Göran Krampe   Renamed to blimp,...
70
71
72
73
  # Upload a file to the remote master blimpStore
  proc uploadFile(blimpFilename: string) =
    if remoteBlimpStore.isNil:
      echo("Remote blimpstore not set in configuration file, not uploading content:\n\t" & blimpFilename)
74272cdb   Göran Krampe   First commit
74
      return
b9ad52ff   Göran Krampe   Added default con...
75
76
77
    let errorCode = run(format(uploadCommandFormat, blimpFilename, remoteBlimpStore, blimpStore))
    if errorCode != 0:
      quit("Something went wrong uploading " & blimpFilename & " to " & remoteBlimpStore, 2)
74272cdb   Göran Krampe   First commit
78
    
5fc367ad   Göran Krampe   Renamed to blimp,...
79
80
81
82
  # Download a file to the remote master blimpStore
  proc downloadFile(blimpFilename: string) =
    if remoteBlimpStore.isNil:
      quit("Remote blimpstore not set in configuration file, can not download content:\n\t" & blimpFilename)
b9ad52ff   Göran Krampe   Added default con...
83
84
85
    let errorCode = run(format(downloadCommandFormat, blimpFilename, remoteBlimpStore, blimpStore))
    if errorCode != 0:
      quit("Something went wrong downloading " & blimpFilename & " from " & remoteBlimpStore, 3)
5fc367ad   Göran Krampe   Renamed to blimp,...
86
87
88
89
90
91
92
93
94
95
96
97
98
  
  
  # Copy content to blimpStore, no upload yet.
  proc copyToBlimpStore(filename, blimpFilename: string) =
    if not existsFile(blimpStore / blimpFilename):
      copyFile(filename, blimpStore / blimpFilename)
      uploadFile(blimpFilename)
  
  # Copy content from blimpStore, and downloading first if needed
  proc copyFromblimpStore(blimpFilename, filename: string) =
    if not existsFile(blimpStore / blimpFilename):
      downloadFile(blimpFilename)
    copyFile(blimpStore / blimpFilename, filename)
74272cdb   Göran Krampe   First commit
99
100
  
      
5fc367ad   Göran Krampe   Renamed to blimp,...
101
  # Copy original file to blimpStore and replace with hash stub in git.
74272cdb   Göran Krampe   First commit
102
  proc deflate(filename: string) =
db95b901   Göran Krampe   Various fixes, mo...
103
104
105
106
107
    var content: string
    try:
      content = readFile(filename)
    except:
      quit("Failed opening file: " & filename, 1)
74272cdb   Göran Krampe   First commit
108
109
110
    if content[0..4] == "hash:":
      quit("File is already deflated, ignored.", 5)
    let hash = getMD5(content)
5fc367ad   Göran Krampe   Renamed to blimp,...
111
112
113
    let blimpFilename = filename & "-" & hash
    copyToBlimpStore(filename, blimpFilename)
    writeFile(filename, "hash:" & blimpFilename)
db95b901   Göran Krampe   Various fixes, mo...
114
115
    echo("\t" & filename & " deflated.")
    
5fc367ad   Göran Krampe   Renamed to blimp,...
116
  # Parse out hash from hash stub and copy back original content from blimpStore.
74272cdb   Göran Krampe   First commit
117
118
119
  proc inflate(filename: string) =
    var hashfile: File
    if not open(hashfile, filename):
db95b901   Göran Krampe   Various fixes, mo...
120
      quit("Failed opening file: " & filename, 4)
74272cdb   Göran Krampe   First commit
121
122
    let hashline = split(string(readLine(hashfile)), {':'})
    if hashline[0] == "hash":
5fc367ad   Göran Krampe   Renamed to blimp,...
123
      let blimpfilename = hashline[1]
74272cdb   Göran Krampe   First commit
124
      #removeFile(filename)
5fc367ad   Göran Krampe   Renamed to blimp,...
125
      copyFromblimpStore(blimpfilename, filename)
74272cdb   Göran Krampe   First commit
126
    else:
db95b901   Göran Krampe   Various fixes, mo...
127
128
      quit("\t" & filename & " is not deflated.", 5)
    echo("\t" & filename & " inflated.")
5fc367ad   Göran Krampe   Renamed to blimp,...
129
130
131
132
133
134
135
136
137
138
139
  
  # Find git root dir or fall back on current dir
  proc gitRoot(): string =
    try:
      let tup = execCmdEx("git rev-parse --show-toplevel")
      if tup[1] == 0:
        result = tup[0]
      else:
        result = getCurrentDir()
    except:
      result = getCurrentDir()
74272cdb   Göran Krampe   First commit
140
  
db95b901   Göran Krampe   Various fixes, mo...
141
142
143
144
145
146
147
  let help = """
    blimp [options] <command> <filenames...>
      -v,--verbose             Verbosity
      <command>   (string)     (i)nflate or (d)eflate
      <filenames> (string...)  One or more filepaths to inflate/deflate
    """
  
74272cdb   Göran Krampe   First commit
148
149
  ################################ main #####################################
  
5fc367ad   Göran Krampe   Renamed to blimp,...
150
151
  # Hardwired to "blimpstore" directory in home dir.
  blimpStore = getHomeDir() / "blimpstore"
74272cdb   Göran Krampe   First commit
152
153
154
  
  # Make sure we have the dir, or create it.
  try:
b9ad52ff   Göran Krampe   Added default con...
155
156
157
158
    if not existsDir(blimpStore):
      createDir(blimpStore)
    if not existsFile(blimpStore / ".blimp.conf"):
      writeFile(blimpStore / ".blimp.conf", defaultConfig)
74272cdb   Göran Krampe   First commit
159
  except:
5fc367ad   Göran Krampe   Renamed to blimp,...
160
    quit("Could not create " & blimpStore & " directory.", 1)
74272cdb   Göran Krampe   First commit
161
162
  
  
5fc367ad   Göran Krampe   Renamed to blimp,...
163
164
165
  # Parse configuration files if they exist
  parseConfFile(gitRoot() / ".blimp.conf")
  parseConfFile(blimpStore / ".blimp.conf")
74272cdb   Göran Krampe   First commit
166
  
db95b901   Göran Krampe   Various fixes, mo...
167
168
169
170
171
  # Using lapp to get args
  let args = parse(help)
  let command = args["command"].asString
  let filenames = args["filenames"].asSeq
  verbose = args["verbose"].asBool
74272cdb   Göran Krampe   First commit
172
173
174
  
  # Do the deed
  if command == "d" or command == "deflate":
db95b901   Göran Krampe   Various fixes, mo...
175
176
    for fn in filenames:
      deflate(fn.asString)
74272cdb   Göran Krampe   First commit
177
  elif command == "i" or command == "inflate":
db95b901   Göran Krampe   Various fixes, mo...
178
179
    for fn in filenames:
      inflate(fn.asString)
74272cdb   Göran Krampe   First commit
180
181
182
183
184
  else:
    quit("Unknown command, only (d)eflate or (i)inflate are valid.", 6)
  
  # All good
  quit(0)