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
.manifest
import verify
, get_algorithm
, copy_tree_with_verify
11 from zeroinstall
import zerostore
, SafeException
, support
19 stores
= zerostore
.Stores()
21 class UsageError(SafeException
): pass
23 def do_manifest(args
):
24 """manifest DIRECTORY [ALGORITHM]"""
25 if len(args
) < 1 or len(args
) > 2: raise UsageError(_("Wrong number of arguments"))
27 alg
= get_algorithm(args
[1])
29 # If no algorithm was given, guess from the directory name
30 name
= os
.path
.basename(args
[0])
32 alg
= get_algorithm(name
.split('=', 1)[0])
34 alg
= get_algorithm('sha1new')
35 digest
= alg
.new_digest()
36 for line
in alg
.generate_manifest(args
[0]):
38 digest
.update(line
+ '\n')
39 print(alg
.getID(digest
))
44 if len(args
) != 1: raise UsageError(_("Wrong number of arguments"))
46 print(stores
.lookup(args
[0]))
48 except zerostore
.BadDigest
as ex
:
49 print(ex
, file=sys
.stderr
)
50 except zerostore
.NotStored
as ex
:
51 print(ex
, file=sys
.stderr
)
55 """add DIGEST (DIRECTORY | (ARCHIVE [EXTRACT]))"""
56 from zeroinstall
.zerostore
import unpack
57 if len(args
) < 2: raise UsageError(_("Missing arguments"))
59 if os
.path
.isdir(args
[1]):
60 if len(args
) > 2: raise UsageError(_("Too many arguments"))
61 stores
.add_dir_to_cache(digest
, args
[1])
62 elif os
.path
.isfile(args
[1]):
63 if len(args
) > 3: raise UsageError(_("Too many arguments"))
69 type = unpack
.type_from_url(args
[1])
71 raise SafeException(_("Unknown extension in '%s' - can't guess MIME type") % args
[1])
72 unpack
.check_type_ok(type)
74 stores
.add_archive_to_cache(digest
, open(args
[1]), args
[1], extract
, type = type)
79 if ex
.errno
!= errno
.ENOENT
: # No such file or directory
80 raise UsageError(str(ex
)) # E.g. permission denied
81 raise UsageError(_("No such file or directory '%s'") % args
[1])
83 def do_optimise(args
):
84 """optimise [ CACHE ]"""
88 cache_dir
= stores
.stores
[0].dir
90 cache_dir
= os
.path
.realpath(cache_dir
)
93 info
= os
.stat(cache_dir
)
94 if not stat
.S_ISDIR(info
.st_mode
):
95 raise UsageError(_("Not a directory: '%s'") % cache_dir
)
97 impl_name
= os
.path
.basename(cache_dir
)
98 if impl_name
!= 'implementations':
99 raise UsageError(_("Cache directory should be named 'implementations', not\n"
100 "'%(name)s' (in '%(cache_dir)s')") % {'name': impl_name
, 'cache_dir': cache_dir
})
102 print(_("Optimising"), cache_dir
)
104 from . import optimise
105 uniq_size
, dup_size
, already_linked
, man_size
= optimise
.optimise(cache_dir
)
106 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
)})
107 print(_("Already saved : %s") % support
.pretty_size(already_linked
))
109 print(_("No duplicates found; no changes made."))
111 print(_("Optimised size : %s") % support
.pretty_size(uniq_size
))
112 perc
= (100 * float(dup_size
)) / (uniq_size
+ dup_size
)
113 print(_("Space freed up : %(size)s (%(percentage).2f%%)") % {'size': support
.pretty_size(dup_size
), 'percentage': perc
})
114 print(_("Optimisation complete."))
117 """verify (DIGEST | (DIRECTORY [DIGEST])"""
119 required_digest
= args
[1]
122 root
= get_stored(args
[0])
123 required_digest
= None # Get from name
125 raise UsageError(_("Missing DIGEST or DIRECTORY"))
127 print(_("Verifying"), root
)
129 verify(root
, required_digest
)
131 except zerostore
.BadDigest
as ex
:
139 """audit [DIRECTORY]"""
141 audit_stores
= stores
.stores
143 audit_stores
= [zerostore
.Store(x
) for x
in args
]
147 for a
in audit_stores
:
148 if os
.path
.isdir(a
.dir):
149 items
= sorted(os
.listdir(a
.dir))
150 audit_ls
.append((a
.dir, items
))
153 raise SafeException(_("No such directory '%s'") % a
.dir)
158 for root
, impls
in audit_ls
:
159 print(_("Scanning %s") % root
)
160 for required_digest
in impls
:
162 path
= os
.path
.join(root
, required_digest
)
163 if '=' not in required_digest
:
164 print(_("Skipping non-implementation directory %s") % path
)
167 msg
= _("[%(done)d / %(total)d] Verifying %(digest)s") % {'done': i
, 'total': total
, 'digest': required_digest
}
170 verify(path
, required_digest
)
171 print("\r" + (" " * len(msg
)) + "\r", end
=' ')
173 except zerostore
.BadDigest
as ex
:
175 failures
.append(path
)
181 print('\n' + _("List of corrupted or modified implementations:"))
185 print(_("Checked %d items") % i
)
186 print(_("Successfully verified implementations: %d") % verified
)
187 print(_("Corrupted or modified implementations: %d") % len(failures
))
193 if args
: raise UsageError(_("List takes no arguments"))
194 print(_("User store (writable) : %s") % stores
.stores
[0].dir)
195 for s
in stores
.stores
[1:]:
196 print(_("System store : %s") % s
.dir)
197 if len(stores
.stores
) < 2:
198 print(_("No system stores."))
200 def get_stored(dir_or_digest
):
201 if os
.path
.isdir(dir_or_digest
):
205 return stores
.lookup(dir_or_digest
)
206 except zerostore
.NotStored
as ex
:
207 print(ex
, file=sys
.stderr
)
211 """copy SOURCE [ TARGET ]"""
213 source
, target
= args
216 target
= stores
.stores
[0].dir
218 raise UsageError(_("Wrong number of arguments."))
220 if not os
.path
.isdir(source
):
221 raise UsageError(_("Source directory '%s' not found") % source
)
222 if not os
.path
.isdir(target
):
223 raise UsageError(_("Target directory '%s' not found") % target
)
224 manifest_path
= os
.path
.join(source
, '.manifest')
225 if not os
.path
.isfile(manifest_path
):
226 raise UsageError(_("Source manifest '%s' not found") % manifest_path
)
227 required_digest
= os
.path
.basename(source
)
228 manifest_data
= open(manifest_path
, 'rb').read()
230 copy_tree_with_verify(source
, target
, manifest_data
, required_digest
)
235 raise UsageError(_("manage command takes no arguments"))
240 from zeroinstall
.gtkui
import cache
241 from zeroinstall
.injector
.iface_cache
import iface_cache
242 cache_explorer
= cache
.CacheExplorer(iface_cache
)
243 cache_explorer
.window
.connect('destroy', gtk
.main_quit
)
244 cache_explorer
.show()
247 commands
= [do_add
, do_audit
, do_copy
, do_find
, do_list
, do_manifest
, do_optimise
, do_verify
, do_manage
]