1 """Code for the B{0store} command-line interface."""
3 # Copyright (C) 2006, Thomas Leonard
4 # See the README file for details, or visit http://0install.net.
7 from zeroinstall
.zerostore
.manifest
import generate_manifest
, verify
, get_algorithm
, copy_tree_with_verify
8 from zeroinstall
import zerostore
, SafeException
, support
16 stores
= zerostore
.Stores()
18 class UsageError(SafeException
): pass
20 def do_manifest(args
):
21 """manifest DIRECTORY [ALGORITHM]"""
22 if len(args
) < 1 or len(args
) > 2: raise UsageError("Wrong number of arguments")
24 alg
= get_algorithm(args
[1])
26 # If no algorithm was given, guess from the directory name
27 name
= os
.path
.basename(args
[0])
29 alg
= get_algorithm(name
.split('=', 1)[0])
31 alg
= get_algorithm('sha1')
32 digest
= alg
.new_digest()
33 for line
in alg
.generate_manifest(args
[0]):
35 digest
.update(line
+ '\n')
36 print alg
.getID(digest
)
41 if len(args
) != 1: raise UsageError("Wrong number of arguments")
43 print stores
.lookup(args
[0])
45 except zerostore
.BadDigest
, ex
:
46 print >>sys
.stderr
, ex
47 except zerostore
.NotStored
, ex
:
48 print >>sys
.stderr
, ex
52 """add DIGEST (DIRECTORY | (ARCHIVE [EXTRACT]))"""
53 from zeroinstall
.zerostore
import unpack
54 if len(args
) < 2: raise UsageError("Missing arguments")
56 if os
.path
.isdir(args
[1]):
57 if len(args
) > 2: raise UsageError("Too many arguments")
58 stores
.add_dir_to_cache(digest
, args
[1])
59 elif os
.path
.isfile(args
[1]):
60 if len(args
) > 3: raise UsageError("Too many arguments")
66 type = unpack
.type_from_url(args
[1])
68 raise SafeException("Unknown extension in '%s' - can't guess MIME type" % args
[1])
69 unpack
.check_type_ok(type)
71 stores
.add_archive_to_cache(digest
, file(args
[1]), args
[1], extract
, type = type)
76 if ex
.errno
!= 2: # No such file or directory
77 raise UsageError(str(ex
)) # E.g. permission denied
78 raise UsageError("No such file or directory '%s'" % args
[1])
80 def do_optimise(args
):
81 """optimise [ CACHE ]"""
85 cache_dir
= stores
.stores
[0].dir
87 cache_dir
= os
.path
.realpath(cache_dir
)
90 info
= os
.stat(cache_dir
)
91 if not stat
.S_ISDIR(info
.st_mode
):
92 raise UsageError("Not a directory: '%s'" % cache_dir
)
94 impl_name
= os
.path
.basename(cache_dir
)
95 if impl_name
!= 'implementations':
96 raise UsageError("Cache directory should be named 'implementations', not\n"
97 "'%s' (in '%s')" % (impl_name
, cache_dir
))
99 print "Optimising", cache_dir
102 uniq_size
, dup_size
, already_linked
, man_size
= optimise
.optimise(cache_dir
)
103 print "Original size :", support
.pretty_size(uniq_size
+ dup_size
) + " (excluding the %s of manifests)" % support
.pretty_size(man_size
)
104 print "Already saved :", support
.pretty_size(already_linked
)
106 print "No duplicates found; no changes made."
108 print "Optimised size :", support
.pretty_size(uniq_size
)
109 perc
= (100 * float(dup_size
)) / (uniq_size
+ dup_size
)
110 print "Space freed up :", support
.pretty_size(dup_size
), "(%.2f%%)" % perc
111 print "Optimisation complete."
114 """verify (DIGEST | (DIRECTORY [DIGEST])"""
116 required_digest
= args
[1]
119 root
= get_stored(args
[0])
120 required_digest
= None # Get from name
122 raise UsageError("Missing DIGEST or DIRECTORY")
124 print "Verifying", root
126 verify(root
, required_digest
)
128 except zerostore
.BadDigest
, ex
:
135 def show_changes(actual
, saved
):
137 for line
in difflib
.unified_diff(saved
, actual
, 'Recorded', 'Actual'):
142 if args
: raise UsageError("List takes no arguments")
143 print "User store (writable) : " + stores
.stores
[0].dir
144 for s
in stores
.stores
[1:]:
145 print "System store : " + s
.dir
146 if len(stores
.stores
) < 2:
147 print "No system stores."
149 def get_stored(dir_or_digest
):
150 if os
.path
.isdir(dir_or_digest
):
154 return stores
.lookup(dir_or_digest
)
155 except zerostore
.NotStored
, ex
:
156 print >>sys
.stderr
, ex
160 """copy SOURCE [ TARGET ]"""
162 source
, target
= args
165 target
= stores
.stores
[0].dir
167 raise UsageError("Wrong number of arguments.")
169 if not os
.path
.isdir(source
):
170 raise UsageError("Source directory '%s' not found" % source
)
171 if not os
.path
.isdir(target
):
172 raise UsageError("Target directory '%s' not found" % target
)
173 manifest_path
= os
.path
.join(source
, '.manifest')
174 if not os
.path
.isfile(manifest_path
):
175 raise UsageError("Source manifest '%s' not found" % manifest_path
)
176 required_digest
= os
.path
.basename(source
)
177 manifest_data
= file(manifest_path
).read()
179 copy_tree_with_verify(source
, target
, manifest_data
, required_digest
)
181 commands
= [do_add
, do_copy
, do_find
, do_list
, do_manifest
, do_optimise
, do_verify
]