Report SafeExceptions without a stack trace, unless --verbose is used.
[0publish.git] / archive.py
blobdfcc01da5e4770a016c891896f5f7d76529b2ca0
1 from xml.dom import minidom
2 from zeroinstall.zerostore import Store, manifest
3 try:
4 from zeroinstall.zerostore import unpack
5 except ImportError:
6 # Older versions don't have it
7 import unpack
8 from zeroinstall.injector import namespaces
9 import os, time, re, shutil, tempfile
11 def manifest_for_dir(dir, alg):
12 if alg == 'sha1':
13 # (for older versions of the injector)
14 import sha
15 class SHA1:
16 def new_digest(self): return sha.new()
17 def generate_manifest(self, dir): return manifest.generate_manifest(dir)
18 def getID(self, digest): return 'sha1=' + digest.hexdigest()
19 algorithm = SHA1()
20 else:
21 algorithm = manifest.get_algorithm(alg)
23 digest = algorithm.new_digest()
24 for line in algorithm.generate_manifest(dir):
25 digest.update(line + '\n')
26 return algorithm.getID(digest)
28 def autopackage_get_start_offset(package):
29 for line in file(package):
30 if line.startswith('export dataSize='):
31 return os.path.getsize(package) - int(line.split('"', 2)[1])
32 raise Exception("Can't find payload in autopackage (missing 'dataSize')")
34 def add_archive(data, url, local_file, extract, alg):
35 if local_file is None:
36 local_file = os.path.abspath(os.path.basename(url))
37 if not os.path.exists(local_file):
38 raise Exception("Use --archive-file option to specify a local copy of the archive "
39 "(default file '%s' does not exist)" % local_file)
41 doc = minidom.parseString(data)
43 if alg is None:
44 if local_file.endswith('.deb') or local_file.endswith('.zip') or \
45 local_file.endswith('.package'):
46 # These require 0launch >= 0.20 anyway, so use the new hash to avoid
47 # problems with directory mtimes
48 alg = 'sha1new'
49 else:
50 alg = 'sha1'
52 if local_file.endswith('.package'):
53 start_offset = autopackage_get_start_offset(local_file)
54 type = 'application/x-bzip-compressed-tar'
55 else:
56 start_offset = 0
57 type = None
59 all_impls = doc.documentElement.getElementsByTagNameNS(namespaces.XMLNS_IFACE, 'implementation')
60 tmpdir = tempfile.mkdtemp('-0publish')
61 try:
62 if start_offset or type:
63 unpack.unpack_archive(url, file(local_file), tmpdir, extract, start_offset = start_offset, type = type)
64 else:
65 unpack.unpack_archive(url, file(local_file), tmpdir, extract)
66 if extract:
67 extracted = os.path.join(tmpdir, extract)
68 else:
69 extracted = tmpdir
71 archive_id = manifest_for_dir(extracted, alg)
72 finally:
73 shutil.rmtree(tmpdir)
75 local_ifaces = []
76 for impl in all_impls:
77 this_id = impl.getAttribute('id')
78 if this_id == archive_id:
79 break
80 if this_id.startswith('/') or this_id.startswith('.'):
81 local_ifaces.append(impl)
82 else:
83 if len(local_ifaces) == 0:
84 raise Exception('Nothing with id "%s", and no local implementations' % archive_id)
85 if len(local_ifaces) > 1:
86 raise Exception('Nothing with id "%s", and multiple local implementations!' % archive_id)
87 impl = local_ifaces[0]
88 impl.setAttribute('id', archive_id)
90 assert impl.getAttribute('id') == archive_id
92 nl = doc.createTextNode('\n ')
93 impl.appendChild(nl)
95 archive = doc.createElementNS(namespaces.XMLNS_IFACE, 'archive')
96 impl.appendChild(archive)
97 archive.setAttribute('href', url)
98 archive.setAttribute('size', str(os.stat(local_file).st_size - start_offset))
99 if extract is not None:
100 archive.setAttribute('extract', extract)
101 if start_offset:
102 archive.setAttribute('start-offset', str(start_offset))
103 if type:
104 archive.setAttribute('type', type)
106 nl = doc.createTextNode('\n ')
107 impl.appendChild(nl)
109 return doc.toxml()