Package zeroinstall :: Package zerostore :: Module cli
[frames] | no frames]

Source Code for Module zeroinstall.zerostore.cli

  1  """Code for the B{0store} command-line interface.""" 
  2   
  3  # Copyright (C) 2009, Thomas Leonard 
  4  # See the README file for details, or visit http://0install.net. 
  5   
  6  from __future__ import print_function 
  7   
  8  from zeroinstall import _ 
  9  import sys, os, errno 
 10  from zeroinstall.zerostore import manifest 
 11  from zeroinstall.zerostore.manifest import verify, get_algorithm, copy_tree_with_verify 
 12  from zeroinstall import zerostore, SafeException, support 
 13   
 14  stores = None 
 15   
16 -def init_stores():
17 global stores 18 assert stores is None 19 if stores is None: 20 stores = zerostore.Stores()
21
22 -class UsageError(SafeException): pass
23
24 -def do_manifest(args):
25 """manifest DIRECTORY [ALGORITHM]""" 26 if len(args) < 1 or len(args) > 2: raise UsageError(_("Wrong number of arguments")) 27 if len(args) == 2: 28 alg = get_algorithm(args[1]) 29 else: 30 # If no algorithm was given, guess from the directory name 31 name = os.path.basename(args[0]) 32 try: 33 alg, unused = manifest.splitID(name) 34 except zerostore.BadDigest: 35 alg = get_algorithm('sha1new') 36 digest = alg.new_digest() 37 for line in alg.generate_manifest(args[0]): 38 print(line) 39 digest.update((line + '\n').encode('utf-8')) 40 print(alg.getID(digest)) 41 sys.exit(0)
42
43 -def do_find(args):
44 """find DIGEST""" 45 if len(args) != 1: raise UsageError(_("Wrong number of arguments")) 46 try: 47 print(stores.lookup(args[0])) 48 sys.exit(0) 49 except zerostore.BadDigest as ex: 50 print(ex, file=sys.stderr) 51 except zerostore.NotStored as ex: 52 print(ex, file=sys.stderr) 53 sys.exit(1)
54
55 -def do_add(args):
56 """add DIGEST (DIRECTORY | (ARCHIVE [EXTRACT]))""" 57 from zeroinstall.zerostore import unpack 58 if len(args) < 2: raise UsageError(_("Missing arguments")) 59 digest = args[0] 60 if os.path.isdir(args[1]): 61 if len(args) > 2: raise UsageError(_("Too many arguments")) 62 stores.add_dir_to_cache(digest, args[1]) 63 elif os.path.isfile(args[1]): 64 if len(args) > 3: raise UsageError(_("Too many arguments")) 65 if len(args) > 2: 66 extract = args[2] 67 else: 68 extract = None 69 70 type = unpack.type_from_url(args[1]) 71 if not type: 72 raise SafeException(_("Unknown extension in '%s' - can't guess MIME type") % args[1]) 73 unpack.check_type_ok(type) 74 75 with open(args[1], 'rb') as stream: 76 stores.add_archive_to_cache(digest, stream, args[1], extract, type = type) 77 else: 78 try: 79 os.stat(args[1]) 80 except OSError as ex: 81 if ex.errno != errno.ENOENT: # No such file or directory 82 raise UsageError(str(ex)) # E.g. permission denied 83 raise UsageError(_("No such file or directory '%s'") % args[1])
84
85 -def do_optimise(args):
86 """optimise [ CACHE ]""" 87 if len(args) == 1: 88 cache_dir = args[0] 89 else: 90 cache_dir = stores.stores[0].dir 91 92 cache_dir = os.path.realpath(cache_dir) 93 94 import stat 95 info = os.stat(cache_dir) 96 if not stat.S_ISDIR(info.st_mode): 97 raise UsageError(_("Not a directory: '%s'") % cache_dir) 98 99 impl_name = os.path.basename(cache_dir) 100 if impl_name != 'implementations': 101 raise UsageError(_("Cache directory should be named 'implementations', not\n" 102 "'%(name)s' (in '%(cache_dir)s')") % {'name': impl_name, 'cache_dir': cache_dir}) 103 104 print(_("Optimising"), cache_dir) 105 106 from . import optimise 107 uniq_size, dup_size, already_linked, man_size = optimise.optimise(cache_dir) 108 print(_("Original size : %(size)s (excluding the %(manifest_size)s of manifests)") % {'size': support.pretty_size(uniq_size + dup_size), 'manifest_size': support.pretty_size(man_size)}) 109 print(_("Already saved : %s") % support.pretty_size(already_linked)) 110 if dup_size == 0: 111 print(_("No duplicates found; no changes made.")) 112 else: 113 print(_("Optimised size : %s") % support.pretty_size(uniq_size)) 114 perc = (100 * float(dup_size)) / (uniq_size + dup_size) 115 print(_("Space freed up : %(size)s (%(percentage).2f%%)") % {'size': support.pretty_size(dup_size), 'percentage': perc}) 116 print(_("Optimisation complete."))
117
118 -def do_verify(args):
119 """verify (DIGEST | (DIRECTORY [DIGEST])""" 120 if len(args) == 2: 121 required_digest = args[1] 122 root = args[0] 123 elif len(args) == 1: 124 root = get_stored(args[0]) 125 required_digest = None # Get from name 126 else: 127 raise UsageError(_("Missing DIGEST or DIRECTORY")) 128 129 print(_("Verifying"), root) 130 try: 131 verify(root, required_digest) 132 print(_("OK")) 133 except zerostore.BadDigest as ex: 134 print(str(ex)) 135 if ex.detail: 136 print() 137 print(ex.detail) 138 sys.exit(1)
139
140 -def do_audit(args):
141 """audit [DIRECTORY]""" 142 if len(args) == 0: 143 audit_stores = stores.stores 144 else: 145 audit_stores = [zerostore.Store(x) for x in args] 146 147 audit_ls = [] 148 total = 0 149 for a in audit_stores: 150 if os.path.isdir(a.dir): 151 items = sorted(os.listdir(a.dir)) 152 audit_ls.append((a.dir, items)) 153 total += len(items) 154 elif len(args): 155 raise SafeException(_("No such directory '%s'") % a.dir) 156 157 verified = 0 158 failures = [] 159 i = 0 160 for root, impls in audit_ls: 161 print(_("Scanning %s") % root) 162 for required_digest in impls: 163 path = os.path.join(root, required_digest) 164 try: 165 (alg, digest) = zerostore.parse_algorithm_digest_pair(required_digest) 166 except zerostore.BadDigest: 167 print(_("Skipping non-implementation directory %s") % path) 168 continue 169 i += 1 170 try: 171 msg = _("[%(done)d / %(total)d] Verifying %(digest)s") % {'done': i, 'total': total, 'digest': required_digest} 172 print(msg, end='') 173 sys.stdout.flush() 174 verify(path, required_digest) 175 print("\r" + (" " * len(msg)) + "\r", end='') 176 verified += 1 177 except zerostore.BadDigest as ex: 178 print() 179 failures.append(path) 180 print(str(ex)) 181 if ex.detail: 182 print() 183 print(ex.detail) 184 if failures: 185 print('\n' + _("List of corrupted or modified implementations:")) 186 for x in failures: 187 print(x) 188 print() 189 print(_("Checked %d items") % i) 190 print(_("Successfully verified implementations: %d") % verified) 191 print(_("Corrupted or modified implementations: %d") % len(failures)) 192 if failures: 193 sys.exit(1)
194
195 -def do_list(args):
196 """list""" 197 if args: raise UsageError(_("List takes no arguments")) 198 print(_("User store (writable) : %s") % stores.stores[0].dir) 199 for s in stores.stores[1:]: 200 print(_("System store : %s") % s.dir) 201 if len(stores.stores) < 2: 202 print(_("No system stores."))
203
204 -def get_stored(dir_or_digest):
205 """@type dir_or_digest: str 206 @rtype: str""" 207 if os.path.isdir(dir_or_digest): 208 return dir_or_digest 209 else: 210 try: 211 return stores.lookup(dir_or_digest) 212 except zerostore.NotStored as ex: 213 print(ex, file=sys.stderr) 214 sys.exit(1)
215
216 -def do_copy(args):
217 """copy SOURCE [ TARGET ]""" 218 if len(args) == 2: 219 source, target = args 220 elif len(args) == 1: 221 source = args[0] 222 target = stores.stores[0].dir 223 else: 224 raise UsageError(_("Wrong number of arguments.")) 225 226 if not os.path.isdir(source): 227 raise UsageError(_("Source directory '%s' not found") % source) 228 if not os.path.isdir(target): 229 raise UsageError(_("Target directory '%s' not found") % target) 230 manifest_path = os.path.join(source, '.manifest') 231 if not os.path.isfile(manifest_path): 232 raise UsageError(_("Source manifest '%s' not found") % manifest_path) 233 required_digest = os.path.basename(source) 234 with open(manifest_path, 'rb') as stream: 235 manifest_data = stream.read() 236 237 copy_tree_with_verify(source, target, manifest_data, required_digest)
238
239 -def do_manage(args):
240 """manage""" 241 if args: 242 raise UsageError(_("manage command takes no arguments")) 243 244 if sys.version_info[0] < 3: 245 import pygtk 246 pygtk.require('2.0') 247 else: 248 from zeroinstall.gtkui import pygtkcompat 249 pygtkcompat.enable() 250 pygtkcompat.enable_gtk(version = '3.0') 251 252 import gtk 253 from zeroinstall.gtkui import cache 254 from zeroinstall.injector.iface_cache import iface_cache 255 cache_explorer = cache.CacheExplorer(iface_cache) 256 cache_explorer.window.connect('destroy', gtk.main_quit) 257 cache_explorer.show() 258 gtk.main()
259 260 commands = [do_add, do_audit, do_copy, do_find, do_list, do_manifest, do_optimise, do_verify, do_manage] 261