Allow 'today' as the date in --set-released.
[0publish.git] / 0publish
blob1018ad934b73868d96d1debbb8b120bfeeb7b8d4
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 options.local = False
76 else:
77 raise Exception("File '%s' does not exist." % options.local)
78 else:
79 if confirm("Interface file '%s' does not exist. Create it?" % interface):
80 from create import create
81 data = create(interface)
82 sign_fn = signing.sign_unsigned
83 key = None
84 options.edit = True
85 else:
86 sys.exit(1)
88 debug("Original data: %s", data)
89 info("Original signing method: %s", sign_fn.__name__)
90 info("Original key: %s", key)
92 old_data = data
93 old_sign_fn = sign_fn
94 old_key = key
96 while True:
97 # Validate the input...
98 try:
99 validator.check(data)
100 break
101 except validator.InvalidInterface, ex:
102 print "Invalid interface: " + str(ex)
104 while True:
105 ans = raw_input("Interface is invalid. (E)dit or (A)bort?").lower()
106 if ans in ('e', 'edit'):
107 data = edit.edit(data)
108 options.edit = False # Don't edit twice
109 break
110 if ans in ('a', 'abort'): sys.exit(1)
112 # Process it...
113 if options.xmlsign:
114 sign_fn = signing.sign_xml
115 if options.gpgsign:
116 sign_fn = signing.sign_plain
117 if options.key:
118 print "Changing key from '%s' to '%s'" % (key, options.key)
119 key = options.key
120 if options.set_id or options.set_version or options.set_released or \
121 options.set_stability or options.set_arch or options.set_main:
122 import release
123 data = release.make_release(data, options.set_id,
124 options.set_version, options.set_released, options.set_stability,
125 options.set_main, options.set_arch)
126 if options.stable:
127 import stable
128 data = stable.mark_stable(data)
129 if options.archive_url:
130 import archive
131 data = archive.add_archive(data, options.archive_url, options.archive_file, options.archive_extract)
132 elif options.archive_file or options.archive_extract:
133 raise Exception('Must use --archive-uri option')
134 if options.local:
135 import merge
136 data = merge.merge(data, options.local)
137 if options.edit:
138 data = edit.edit(data)
140 while True:
141 # Validate the result...
142 try:
143 validator.check(data)
144 break
145 except validator.InvalidInterface, ex:
146 print "Invalid interface: " + str(ex)
148 while True:
149 ans = raw_input("Interface is invalid. (E)dit or (A)bort?").lower()
150 if ans in ('e', 'edit'):
151 data = edit.edit(data)
152 break
153 if ans in ('a', 'abort'): sys.exit(1)
156 if (old_data == data and sign_fn == old_sign_fn and key == old_key) and not force_save:
157 print "Interface unchanged. Not writing."
158 sys.exit(1)
160 # Write it back out
161 if not data.endswith('\n'): data += '\n'
162 sign_fn(interface, data, key)
164 info("Wrote '%S'", interface)