1 """Code for the B{0store} command-line interface."""
3 # Copyright (C) 2009, Thomas Leonard
4 # See the README file for details, or visit http://0install.net.
6 from __future__
import print_function
8 from zeroinstall
import _
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
20 stores
= zerostore
.Stores()
22 class UsageError(SafeException
): pass
24 def do_manifest(args
):
25 """manifest DIRECTORY [ALGORITHM]"""
26 if len(args
) < 1 or len(args
) > 2: raise UsageError(_("Wrong number of arguments"))
28 alg
= get_algorithm(args
[1])
30 # If no algorithm was given, guess from the directory name
31 name
= os
.path
.basename(args
[0])
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]):
39 digest
.update((line
+ '\n').encode('utf-8'))
40 print(alg
.getID(digest
))
45 if len(args
) != 1: raise UsageError(_("Wrong number of arguments"))
47 print(stores
.lookup(args
[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
)
56 """add DIGEST (DIRECTORY | (ARCHIVE [EXTRACT]))"""
57 from zeroinstall
.zerostore
import unpack
58 if len(args
) < 2: raise UsageError(_("Missing arguments"))
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"))
70 type = unpack
.type_from_url(args
[1])
72 raise SafeException(_("Unknown extension in '%s' - can't guess MIME type") % args
[1])
73 unpack
.check_type_ok(type)
75 with
open(args
[1], 'rb') as stream
:
76 stores
.add_archive_to_cache(digest
, stream
, args
[1], extract
, type = type)
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])
85 def do_optimise(args
):
86 """optimise [ CACHE ]"""
90 cache_dir
= stores
.stores
[0].dir
92 cache_dir
= os
.path
.realpath(cache_dir
)
95 info
= os
.stat(cache_dir
)
96 if not stat
.S_ISDIR(info
.st_mode
):
97 raise UsageError(_("Not a directory: '%s'") % cache_dir
)
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
})
104 print(_("Optimising"), cache_dir
)
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
))
111 print(_("No duplicates found; no changes made."))
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."))
119 """verify (DIGEST | (DIRECTORY [DIGEST])"""
121 required_digest
= args
[1]
124 root
= get_stored(args
[0])
125 required_digest
= None # Get from name
127 raise UsageError(_("Missing DIGEST or DIRECTORY"))
129 print(_("Verifying"), root
)
131 verify(root
, required_digest
)
133 except zerostore
.BadDigest
as ex
:
141 """audit [DIRECTORY]"""
143 audit_stores
= stores
.stores
145 audit_stores
= [zerostore
.Store(x
) for x
in args
]
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
))
155 raise SafeException(_("No such directory '%s'") % a
.dir)
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
)
165 (alg
, digest
) = zerostore
.parse_algorithm_digest_pair(required_digest
)
166 except zerostore
.BadDigest
:
167 print(_("Skipping non-implementation directory %s") % path
)
171 msg
= _("[%(done)d / %(total)d] Verifying %(digest)s") % {'done': i
, 'total': total
, 'digest': required_digest
}
174 verify(path
, required_digest
)
175 print("\r" + (" " * len(msg
)) + "\r", end
='')
177 except zerostore
.BadDigest
as ex
:
179 failures
.append(path
)
185 print('\n' + _("List of corrupted or modified implementations:"))
189 print(_("Checked %d items") % i
)
190 print(_("Successfully verified implementations: %d") % verified
)
191 print(_("Corrupted or modified implementations: %d") % len(failures
))
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."))
204 def get_stored(dir_or_digest
):
205 if os
.path
.isdir(dir_or_digest
):
209 return stores
.lookup(dir_or_digest
)
210 except zerostore
.NotStored
as ex
:
211 print(ex
, file=sys
.stderr
)
215 """copy SOURCE [ TARGET ]"""
217 source
, target
= args
220 target
= stores
.stores
[0].dir
222 raise UsageError(_("Wrong number of arguments."))
224 if not os
.path
.isdir(source
):
225 raise UsageError(_("Source directory '%s' not found") % source
)
226 if not os
.path
.isdir(target
):
227 raise UsageError(_("Target directory '%s' not found") % target
)
228 manifest_path
= os
.path
.join(source
, '.manifest')
229 if not os
.path
.isfile(manifest_path
):
230 raise UsageError(_("Source manifest '%s' not found") % manifest_path
)
231 required_digest
= os
.path
.basename(source
)
232 with
open(manifest_path
, 'rb') as stream
:
233 manifest_data
= stream
.read()
235 copy_tree_with_verify(source
, target
, manifest_data
, required_digest
)
240 raise UsageError(_("manage command takes no arguments"))
245 from zeroinstall
.gtkui
import cache
246 from zeroinstall
.injector
.iface_cache
import iface_cache
247 cache_explorer
= cache
.CacheExplorer(iface_cache
)
248 cache_explorer
.window
.connect('destroy', gtk
.main_quit
)
249 cache_explorer
.show()
252 commands
= [do_add
, do_audit
, do_copy
, do_find
, do_list
, do_manifest
, do_optimise
, do_verify
, do_manage
]