Added support for .autopackage files (bzip2-encoded only at present).
[0publish.git] / 0publish
blob0b0882456ac6c66739a7b5ac32c65f5bc76f8397
1 #!/usr/bin/env python
2 from zeroinstall.injector import gpg
3 from optparse import OptionParser
4 import os, sys
5 import signing
6 from logging import info, debug
7 import edit, validator
9 version = '0.5'
11 parser = OptionParser(usage="usage: %prog [options] interface")
12 parser.add_option("--add-version", help="add a new implementation", action='store', metavar='VERSION')
13 parser.add_option("--archive-url", help="add archive at this URL", action='store', metavar='URL')
14 parser.add_option("--archive-file", help="local copy of archive-url", action='store', metavar='FILE')
15 parser.add_option("--archive-extract", help="subdirectory of archive to extract", action='store', metavar='DIR')
16 parser.add_option("-e", "--edit", help="edit with $EDITOR", action='store_true')
17 parser.add_option("-g", "--gpgsign", help="add a GPG signature block", action='store_true')
18 parser.add_option("-k", "--key", help="key to use for signing")
19 parser.add_option("-l", "--local", help="create feed from local interface")
20 parser.add_option("--manifest-algorithm", help="select algorithm for manifests", action='store', metavar='ALG')
21 parser.add_option("--set-interface-uri", help="set interface URI", action='store', metavar='URI')
22 parser.add_option("--set-id", help="set implementation ID", action='store', metavar='DIGEST')
23 parser.add_option("--set-main", help="set main executable", action='store', metavar='EXEC')
24 parser.add_option("--set-arch", help="set architecture", action='store', metavar='ARCH')
25 parser.add_option("--set-released", help="set release date", action='store', metavar='DATE')
26 parser.add_option("--set-stability", help="set stability", action='store', metavar='STABILITY')
27 parser.add_option("--set-version", help="set version number", action='store', metavar='VERSION')
28 parser.add_option("-s", "--stable", help="mark testing version stable", action='store_true')
29 parser.add_option("-x", "--xmlsign", help="add an XML signature block", action='store_true')
30 parser.add_option("-u", "--unsign", help="remove any signature", action='store_true')
31 parser.add_option("-v", "--verbose", help="more verbose output", action='count')
32 parser.add_option("-V", "--version", help="display version information", action='store_true')
34 (options, args) = parser.parse_args()
36 force_save = False
38 if options.version:
39 print "0publish (zero-install) " + version
40 print "Copyright (C) 2005 Thomas Leonard"
41 print "This program comes with ABSOLUTELY NO WARRANTY,"
42 print "to the extent permitted by law."
43 print "You may redistribute copies of this program"
44 print "under the terms of the GNU General Public License."
45 print "For more information about these matters, see the file named COPYING."
46 sys.exit(0)
48 if options.verbose:
49 import logging
50 logger = logging.getLogger()
51 if options.verbose == 1:
52 logger.setLevel(logging.INFO)
53 else:
54 logger.setLevel(logging.DEBUG)
56 if len(args) != 1:
57 parser.print_help()
58 sys.exit(1)
59 interface = args[0]
61 def confirm(q):
62 while True:
63 ans = raw_input(q + " [Y/N] ").lower()
64 if ans in ('y', 'yes'): return True
65 if ans in ('n', 'no'): return False
67 # Load or create the starting data...
69 if os.path.exists(interface):
70 contents = file(interface).read()
71 data, sign_fn, key = signing.check_signature(interface)
72 elif options.local:
73 import create
74 if os.path.exists(options.local):
75 data = create.create_from_local(options.local)
76 sign_fn = signing.sign_unsigned
77 key = None
78 force_save = True
79 options.local = False
80 else:
81 raise Exception("File '%s' does not exist." % options.local)
82 else:
83 if confirm("Interface file '%s' does not exist. Create it?" % interface):
84 from create import create
85 data = create(interface)
86 sign_fn = signing.sign_unsigned
87 key = None
88 options.edit = True
89 else:
90 sys.exit(1)
92 debug("Original data: %s", data)
93 info("Original signing method: %s", sign_fn.__name__)
94 info("Original key: %s", key)
96 old_data = data
97 old_sign_fn = sign_fn
98 old_key = key
100 while True:
101 # Validate the input...
102 try:
103 validator.check(data)
104 break
105 except validator.InvalidInterface, ex:
106 print "Invalid interface: " + str(ex)
108 while True:
109 ans = raw_input("Interface is invalid. (E)dit or (A)bort?").lower()
110 if ans in ('e', 'edit'):
111 data = edit.edit(data)
112 options.edit = False # Don't edit twice
113 break
114 if ans in ('a', 'abort'): sys.exit(1)
116 # Process it...
117 if options.xmlsign:
118 sign_fn = signing.sign_xml
119 if options.unsign:
120 sign_fn = signing.sign_unsigned
121 if options.gpgsign:
122 sign_fn = signing.sign_plain
123 if options.key:
124 print "Changing key from '%s' to '%s'" % (key, options.key)
125 key = options.key
126 if options.set_interface_uri:
127 import release
128 data = release.set_interface_uri(data, options.set_interface_uri)
129 if options.add_version:
130 import release
131 data = release.add_version(data, options.add_version)
132 if options.set_id or options.set_version or options.set_released or \
133 options.set_stability or options.set_arch or options.set_main:
134 import release
135 data = release.make_release(data, options.set_id,
136 options.set_version, options.set_released, options.set_stability,
137 options.set_main, options.set_arch)
138 if options.stable:
139 import stable
140 data = stable.mark_stable(data)
141 if options.archive_url:
142 import archive
143 data = archive.add_archive(data, options.archive_url, options.archive_file, options.archive_extract, options.manifest_algorithm)
144 elif options.archive_file or options.archive_extract:
145 raise Exception('Must use --archive-uri option')
146 if options.local:
147 import merge
148 data = merge.merge(data, options.local)
149 if options.edit:
150 data = edit.edit(data)
152 while True:
153 # Validate the result...
154 try:
155 validator.check(data)
156 break
157 except validator.InvalidInterface, ex:
158 print "Invalid interface: " + str(ex)
160 while True:
161 ans = raw_input("Interface is invalid. (E)dit or (A)bort?").lower()
162 if ans in ('e', 'edit'):
163 data = edit.edit(data)
164 break
165 if ans in ('a', 'abort'): sys.exit(1)
168 if (old_data == data and sign_fn == old_sign_fn and key == old_key) and not force_save:
169 print "Interface unchanged. Not writing."
170 sys.exit(1)
172 # Write it back out
173 if not data.endswith('\n'): data += '\n'
174 sign_fn(interface, data, key)
176 info("Wrote '%S'", interface)
178 if sign_fn != signing.sign_unsigned:
179 # Read it back in to find out what key we signed it with
180 # and ensure that the key has been exported
181 contents = file(interface).read()
182 saved_data, saved_sign_fn, saved_key = signing.check_signature(interface)
183 assert saved_data == data
184 assert saved_sign_fn == sign_fn
185 signing.export_key(os.path.dirname(interface), saved_key)