1 """Code for the B{0store} command-line interface."""
2
3
4
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
21
23
42
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
84
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
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
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
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
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
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
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
259
260 commands = [do_add, do_audit, do_copy, do_find, do_list, do_manifest, do_optimise, do_verify, do_manage]
261