Give user a chance to view a diff against the previous version
[0release.git] / support.py
blobd3008daadaad1888237339b43f0789961d71be2d
1 # Copyright (C) 2007, Thomas Leonard
2 # See the README file for details, or visit http://0install.net.
4 import os, subprocess, shutil
5 from zeroinstall import SafeException
6 from zeroinstall.injector import model
7 from logging import info
9 def check_call(*args, **kwargs):
10 exitstatus = subprocess.call(*args, **kwargs)
11 if exitstatus != 0:
12 raise SafeException("Command %s failed with exit code %d" % (' '.join(args), exitstatus))
14 def show_and_run(cmd, args):
15 print "Executing: %s %s" % (cmd, ' '.join("[%s]" % x for x in args))
16 check_call(['sh', '-c', cmd, '-'] + args)
18 def suggest_release_version(snapshot_version):
19 """Given a snapshot version, suggest a suitable release version.
20 >>> suggest_release_version('1.0-pre')
21 '1.0'
22 >>> suggest_release_version('0.9-post')
23 '0.10'
24 >>> suggest_release_version('3')
25 Traceback (most recent call last):
26 ...
27 SafeException: Version '3' is not a snapshot version (should end in -pre or -post)
28 """
29 version = model.parse_version(snapshot_version)
30 mod = version[-1]
31 if mod == 0:
32 raise SafeException("Version '%s' is not a snapshot version (should end in -pre or -post)" % snapshot_version)
33 if mod > 0:
34 # -post, so increment the number
35 version[-2][-1] += 1
36 version[-1] = 0 # Remove the modifier
37 return model.format_version(version)
39 def publish(iface, **kwargs):
40 args = [os.environ['0PUBLISH']]
41 for k in kwargs:
42 value = kwargs[k]
43 if value is True:
44 args += ['--' + k.replace('_', '-')]
45 elif value is not None:
46 args += ['--' + k.replace('_', '-'), value]
47 args.append(iface)
48 info("Executing %s", args)
49 check_call(args)
51 def get_singleton_impl(iface):
52 impls = iface.implementations
53 if len(impls) != 1:
54 raise SafeException("Local feed '%s' contains %d versions! I need exactly one!" % (iface.uri, len(impls)))
55 return impls.values()[0]
57 def backup_if_exists(name):
58 if not os.path.exists(name):
59 return
60 backup = name + '~'
61 if os.path.exists(backup):
62 print "(deleting old backup %s)" % backup
63 if os.path.isdir(backup):
64 shutil.rmtree(backup)
65 else:
66 os.unlink(backup)
67 os.rename(name, backup)
68 print "(renamed old %s as %s; will delete on next run)" % (name, backup)
70 def get_choice(options):
71 while True:
72 choice = raw_input('/'.join(options) + ': ').lower()
73 if not choice: continue
74 for o in options:
75 if o.lower().startswith(choice):
76 return o
78 def make_archive_name(feed_name, version):
79 return feed_name.lower().replace(' ', '-') + '-' + version
81 def in_PATH(prog):
82 for x in os.environ['PATH'].split(':'):
83 if os.path.isfile(os.path.join(x, prog)):
84 return True
85 return False
87 def show_diff(from_dir, to_dir):
88 for cmd in [['meld'], ['xxdiff'], ['diff', '-ur']]:
89 if in_PATH(cmd[0]):
90 code = os.spawnvp(os.P_WAIT, cmd[0], cmd + [from_dir, to_dir])
91 if code:
92 print "WARNING: command %s failed with exit code %d" % (cmd, code)
93 return
95 class Status(object):
96 __slots__ = ['old_snapshot_version', 'release_version', 'head_before_release', 'new_snapshot_version', 'head_at_release', 'created_archive', 'tagged']
97 def __init__(self):
98 for name in self.__slots__:
99 setattr(self, name, None)
101 if os.path.isfile(release_status_file):
102 for line in file(release_status_file):
103 assert line.endswith('\n')
104 line = line[:-1]
105 name, value = line.split('=')
106 setattr(self, name, value)
107 info("Loaded status %s=%s", name, value)
109 def save(self):
110 tmp_name = release_status_file + '.new'
111 tmp = file(tmp_name, 'w')
112 try:
113 lines = ["%s=%s\n" % (name, getattr(self, name)) for name in self.__slots__ if getattr(self, name)]
114 tmp.write(''.join(lines))
115 tmp.close()
116 os.rename(tmp_name, release_status_file)
117 info("Wrote status to %s", release_status_file)
118 except:
119 os.unlink(tmp_name)
120 raise