Bugfix: Unset attributes return '', not None. This caused --stable to fail
[0publish.git] / 0publish
blob81b69eed9608d679cbbc78f496f08d623c1ad2c8
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.2'
11 parser = OptionParser(usage="usage: %prog [options] interface")
12 parser.add_option("--archive-url", help="add archive at this URL", action='store', metavar='URL')
13 parser.add_option("--archive-file", help="local copy of archive-url", action='store', metavar='FILE')
14 parser.add_option("--archive-extract", help="subdirectory of archive to extract", action='store', metavar='DIR')
15 parser.add_option("-e", "--edit", help="edit with $EDITOR", action='store_true')
16 parser.add_option("-g", "--gpgsign", help="add a GPG signature block", action='store_true')
17 parser.add_option("-k", "--key", help="key to use for signing")
18 parser.add_option("-l", "--local", help="create feed from local interface")
19 parser.add_option("--set-id", help="set implementation ID", action='store', metavar='DIGEST')
20 parser.add_option("--set-main", help="set main executable", action='store', metavar='EXEC')
21 parser.add_option("--set-arch", help="set architecture", action='store', metavar='ARCH')
22 parser.add_option("--set-released", help="set release date", action='store', metavar='DATE')
23 parser.add_option("--set-stability", help="set stability", action='store', metavar='STABILITY')
24 parser.add_option("--set-version", help="set version number", action='store', metavar='VERSION')
25 parser.add_option("-s", "--stable", help="mark testing version stable", action='store_true')
26 parser.add_option("-x", "--xmlsign", help="add an XML signature block", action='store_true')
27 parser.add_option("-v", "--verbose", help="more verbose output", action='count')
28 parser.add_option("-V", "--version", help="display version information", action='store_true')
30 (options, args) = parser.parse_args()
32 force_save = False
34 if options.version:
35 print "0publish (zero-install) " + version
36 print "Copyright (C) 2005 Thomas Leonard"
37 print "This program comes with ABSOLUTELY NO WARRANTY,"
38 print "to the extent permitted by law."
39 print "You may redistribute copies of this program"
40 print "under the terms of the GNU General Public License."
41 print "For more information about these matters, see the file named COPYING."
42 sys.exit(0)
44 if options.verbose:
45 import logging
46 logger = logging.getLogger()
47 if options.verbose == 1:
48 logger.setLevel(logging.INFO)
49 else:
50 logger.setLevel(logging.DEBUG)
52 if len(args) != 1:
53 parser.print_help()
54 sys.exit(1)
55 interface = args[0]
57 def confirm(q):
58 while True:
59 ans = raw_input(q + " [Y/N] ").lower()
60 if ans in ('y', 'yes'): return True
61 if ans in ('n', 'no'): return False
63 # Load or create the starting data...
65 if os.path.exists(interface):
66 contents = file(interface).read()
67 data, sign_fn, key = signing.check_signature(interface)
68 elif options.local:
69 import create
70 if os.path.exists(options.local):
71 data = create.create_from_local(options.local)
72 sign_fn = signing.sign_unsigned
73 key = None
74 force_save = True
75 else:
76 raise Exception("File '%s' does not exist." % options.local)
77 else:
78 if confirm("Interface file '%s' does not exist. Create it?" % interface):
79 from create import create
80 data = create(interface)
81 sign_fn = signing.sign_unsigned
82 key = None
83 options.edit = True
84 else:
85 sys.exit(1)
87 debug("Original data: %s", data)
88 info("Original signing method: %s", sign_fn.__name__)
89 info("Original key: %s", key)
91 old_data = data
92 old_sign_fn = sign_fn
93 old_key = key
95 while True:
96 # Validate the input...
97 try:
98 validator.check(data)
99 break
100 except validator.InvalidInterface, ex:
101 print "Invalid interface: " + str(ex)
103 while True:
104 ans = raw_input("Interface is invalid. (E)dit or (A)bort?").lower()
105 if ans in ('e', 'edit'):
106 data = edit.edit(data)
107 options.edit = False # Don't edit twice
108 break
109 if ans in ('a', 'abort'): sys.exit(1)
111 # Process it...
112 if options.xmlsign:
113 sign_fn = signing.sign_xml
114 if options.gpgsign:
115 sign_fn = signing.sign_plain
116 if options.key:
117 print "Changing key from '%s' to '%s'" % (key, options.key)
118 key = options.key
119 if options.set_id or options.set_version or options.set_released or \
120 options.set_stability or options.set_arch or options.set_main:
121 import release
122 data = release.make_release(data, options.set_id,
123 options.set_version, options.set_released, options.set_stability,
124 options.set_main, options.set_arch)
125 if options.stable:
126 import stable
127 data = stable.mark_stable(data)
128 if options.archive_url:
129 import archive
130 data = archive.add_archive(data, options.archive_url, options.archive_file, options.archive_extract)
131 elif options.archive_file or options.archive_extract:
132 raise Exception('Must use --archive-uri option')
133 if options.local:
134 import merge
135 data = merge.merge(data, options.local)
136 if options.edit:
137 data = edit.edit(data)
139 while True:
140 # Validate the result...
141 try:
142 validator.check(data)
143 break
144 except validator.InvalidInterface, ex:
145 print "Invalid interface: " + str(ex)
147 while True:
148 ans = raw_input("Interface is invalid. (E)dit or (A)bort?").lower()
149 if ans in ('e', 'edit'):
150 data = edit.edit(data)
151 break
152 if ans in ('a', 'abort'): sys.exit(1)
155 if (old_data == data and sign_fn == old_sign_fn and key == old_key) and not force_save:
156 print "Interface unchanged. Not writing."
157 sys.exit(1)
159 # Write it back out
160 if not data.endswith('\n'): data += '\n'
161 sign_fn(interface, data, key)
163 info("Wrote '%S'", interface)